From 6fa24363ac4db37ee4485535d6f7b57bec6d0069 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 12 Oct 2024 01:59:03 +0000 Subject: [PATCH 001/374] chore(deps): weekly `cargo update` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v0.8.6 -> v0.8.7 Updating alloy-json-abi v0.8.6 -> v0.8.7 Updating alloy-primitives v0.8.6 -> v0.8.7 Updating alloy-sol-macro v0.8.6 -> v0.8.7 Updating alloy-sol-macro-expander v0.8.6 -> v0.8.7 Updating alloy-sol-macro-input v0.8.6 -> v0.8.7 Updating alloy-sol-type-parser v0.8.6 -> v0.8.7 Updating alloy-sol-types v0.8.6 -> v0.8.7 Updating async-compression v0.4.13 -> v0.4.14 Updating aws-sdk-kms v1.46.0 -> v1.47.0 Updating aws-sdk-sso v1.45.0 -> v1.46.0 Updating aws-sdk-ssooidc v1.46.0 -> v1.47.0 Updating aws-sdk-sts v1.45.0 -> v1.46.0 Updating aws-smithy-runtime v1.7.1 -> v1.7.2 Updating cc v1.1.28 -> v1.1.30 Updating clap v4.5.19 -> v4.5.20 Updating clap_builder v4.5.19 -> v4.5.20 Updating clap_complete v4.5.32 -> v4.5.33 Updating derive_builder v0.20.1 -> v0.20.2 Updating derive_builder_core v0.20.1 -> v0.20.2 Updating derive_builder_macro v0.20.1 -> v0.20.2 Updating js-sys v0.3.70 -> v0.3.72 Updating lru v0.12.4 -> v0.12.5 Updating newtype-uuid v1.1.0 -> v1.1.2 Updating proc-macro2 v1.0.86 -> v1.0.87 Updating scc v2.2.0 -> v2.2.1 Updating sdd v3.0.3 -> v3.0.4 Updating syn-solidity v0.8.6 -> v0.8.7 Updating wasm-bindgen v0.2.93 -> v0.2.95 Updating wasm-bindgen-backend v0.2.93 -> v0.2.95 Updating wasm-bindgen-futures v0.4.43 -> v0.4.45 Updating wasm-bindgen-macro v0.2.93 -> v0.2.95 Updating wasm-bindgen-macro-support v0.2.93 -> v0.2.95 Updating wasm-bindgen-shared v0.2.93 -> v0.2.95 Updating web-sys v0.3.70 -> v0.3.72 note: pass `--verbose` to see 10 unchanged dependencies behind latest --- Cargo.lock | 148 ++++++++++++++++++++++++++--------------------------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0aec92430c11f..e597a7a91fd0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1109c57718022ac84c194f775977a534e1b3969b405e55693a61c42187cc0612" +checksum = "f95d76a38cae906fd394a5afb0736aaceee5432efe76addfd71048e623e208af" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cc0e59c803dd44d14fc0cfa9fea1f74cfa8fd9fb60ca303ced390c58c28d4e" +checksum = "03c66eec1acdd96b39b995b8f5ee5239bc0c871d62c527ae1ac9fd1d7fecd455" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -260,9 +260,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a289ffd7448036f2f436b377f981c79ce0b2090877bad938d43387dc09931877" +checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" dependencies = [ "alloy-rlp", "arbitrary", @@ -606,9 +606,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0409e3ba5d1de409997a7db8b8e9d679d52088c1dee042a85033affd3cadeab4" +checksum = "661c516eb1fa3294cc7f2fb8955b3b609d639c282ac81a4eedb14d3046db503a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -620,9 +620,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18372ef450d59f74c7a64a738f546ba82c92f816597fed1802ef559304c81f1" +checksum = "ecbabb8fc3d75a0c2cea5215be22e7a267e3efde835b0f2a8922f5e3f5d47683" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7bad89dd0d5f109e8feeaf787a9ed7a05a91a9a0efc6687d147a70ebca8eff7" +checksum = "16517f2af03064485150d89746b8ffdcdbc9b6eeb3d536fb66efd7c2846fbc75" dependencies = [ "alloy-json-abi", "const-hex", @@ -656,9 +656,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd3548d5262867c2c4be6223fe4f2583e21ade0ca1c307fd23bc7f28fca479e" +checksum = "c07ebb0c1674ff8cbb08378d7c2e0e27919d2a2dae07ad3bca26174deda8d389" dependencies = [ "serde", "winnow", @@ -666,9 +666,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa666f1036341b46625e72bd36878bf45ad0185f1b88601223e1ec6ed4b72b1" +checksum = "8e448d879903624863f608c552d10efb0e0905ddbee98b0049412799911eb062" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -1129,9 +1129,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" +checksum = "998282f8f49ccd6116b0ed8a4de0fbd3151697920e7c7533416d6e25e76434a7" dependencies = [ "flate2", "futures-core", @@ -1321,9 +1321,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.46.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33590e8d45206fdc4273ded8a1f292bcceaadd513037aa790fc67b237bc30ee" +checksum = "564a597a3c71a957d60a2e4c62c93d78ee5a0d636531e15b760acad983a5c18e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1343,9 +1343,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.45.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33ae899566f3d395cbf42858e433930682cc9c1889fa89318896082fef45efb" +checksum = "0dc2faec3205d496c7e57eff685dd944203df7ce16a4116d0281c44021788a7b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1365,9 +1365,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.46.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39c09e199ebd96b9f860b0fce4b6625f211e064ad7c8693b72ecf7ef03881e0" +checksum = "c93c241f52bc5e0476e259c953234dab7e2a35ee207ee202e86c0095ec4951dc" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1387,9 +1387,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.45.0" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d95f93a98130389eb6233b9d615249e543f6c24a68ca1f109af9ca5164a8765" +checksum = "b259429be94a3459fa1b00c5684faee118d74f9577cc50aebadc36e507c63b5f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1483,9 +1483,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" +checksum = "a065c0fe6fdbdf9f11817eb68582b2ab4aff9e9c39e986ae48f7ec576c6322db" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1991,9 +1991,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.28" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "shlex", ] @@ -2105,9 +2105,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -2115,9 +2115,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -2130,9 +2130,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.32" +version = "4.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a01f4f9ee6c066d42a1c8dedf0dcddad16c72a8981a309d6398de3a75b0c39" +checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb" dependencies = [ "clap", ] @@ -2663,18 +2663,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -2684,9 +2684,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn 2.0.79", @@ -4673,10 +4673,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "hashbrown" @@ -4684,6 +4680,8 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", "serde", ] @@ -5280,9 +5278,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -5493,11 +5491,11 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -5753,9 +5751,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "newtype-uuid" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526cb7c660872e401beaf3297f95f548ce3b4b4bdd8121b7c0713771d7c4a6e" +checksum = "4f4933943834e236c864a48aefdc2da43885dbd5eb77bff3ab20f31e0c3146f5" dependencies = [ "uuid 1.10.0", ] @@ -6661,9 +6659,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -7602,9 +7600,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836f1e0f4963ef5288b539b643b35e043e76a32d0f4e47e67febf69576527f50" +checksum = "553f8299af7450cda9a52d3a370199904e7a46b5ffd1bef187c4a6af3bb6db69" dependencies = [ "sdd", ] @@ -7678,9 +7676,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" +checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" [[package]] name = "sec1" @@ -8343,9 +8341,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a850d65181df41b83c6be01a7d91f5e9377c43d48faa5af7d95816f437f5a3" +checksum = "20e7b52ad118b2153644eea95c6fc740b6c1555b2344fdab763fc9de4075f665" dependencies = [ "paste", "proc-macro2", @@ -9252,9 +9250,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -9263,9 +9261,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -9278,9 +9276,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -9290,9 +9288,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9300,9 +9298,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -9313,9 +9311,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" @@ -9395,9 +9393,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", From c46fd9289e8a3d21a95db83163675fce7d7521d8 Mon Sep 17 00:00:00 2001 From: Legion's <64915515+Dargon789@users.noreply.github.com> Date: Fri, 11 Oct 2024 19:09:52 -0700 Subject: [PATCH 002/374] Create codeql.yml Signed-off-by: Legion's <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/codeql.yml | 92 ++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" From f4c98067e8f66d0b4cc8634971412e7f065d79d5 Mon Sep 17 00:00:00 2001 From: Legion's <64915515+Dargon789@users.noreply.github.com> Date: Fri, 11 Oct 2024 22:53:00 -0700 Subject: [PATCH 003/374] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/custom.md | 10 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..dd84ea7824f11 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 4e543f759a28b7d944ca88d71982a23ace15c58d Mon Sep 17 00:00:00 2001 From: Legion's <64915515+Dargon789@users.noreply.github.com> Date: Fri, 11 Oct 2024 22:54:56 -0700 Subject: [PATCH 004/374] Create apisec-scan.yml Signed-off-by: Legion's <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/apisec-scan.yml | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/apisec-scan.yml diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..d719efe66e0e2 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,71 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# APIsec addresses the critical need to secure APIs before they reach production. +# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. +# Clients rely on APIsec to evaluate every update and release, ensuring that no APIs go to production with vulnerabilities. + +# How to Get Started with APIsec.ai +# 1. Schedule a demo at https://www.apisec.ai/request-a-demo . +# +# 2. Register your account at https://cloud.apisec.ai/#/signup . +# +# 3. Register your API . See the video (https://www.youtube.com/watch?v=MK3Xo9Dbvac) to get up and running with APIsec quickly. +# +# 4. Get GitHub Actions scan attributes from APIsec Project -> Configurations -> Integrations -> CI-CD -> GitHub Actions +# +# apisec-run-scan +# +# This action triggers the on-demand scans for projects registered in APIsec. +# If your GitHub account allows code scanning alerts, you can then upload the sarif file generated by this action to show the scan findings. +# Else you can view the scan results from the project home page in APIsec Platform. +# The link to view the scan results is also displayed on the console on successful completion of action. + +# This is a starter workflow to help you get started with APIsec-Scan Actions + +name: APIsec + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + # Customize trigger events based on your DevSecOps processes. + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '42 12 * * 4' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + + +permissions: + contents: read + +jobs: + + Trigger_APIsec_scan: + permissions: + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + + steps: + - name: APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + # The APIsec username with which the scans will be executed + apisec-username: ${{ secrets.apisec_username }} + # The Password of the APIsec user with which the scans will be executed + apisec-password: ${{ secrets.apisec_password}} + # The name of the project for security scan + apisec-project: "VAmPI" + # The name of the sarif format result file The file is written only if this property is provided. + sarif-result-file: "apisec-results.sarif" + - name: Import results + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ./apisec-results.sarif From 7843e74f82f95ebe60768b2bfa5a05adf9f27faa Mon Sep 17 00:00:00 2001 From: Dargon789 Date: Mon, 28 Oct 2024 03:57:33 +0700 Subject: [PATCH 005/374] add remix_tests project --- .deps/remix-tests/remix_accounts.sol | 39 +++++ .deps/remix-tests/remix_tests.sol | 225 +++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} From fb47f35432aabfcf203fa910709cc09597f16dab Mon Sep 17 00:00:00 2001 From: Legion's <64915515+Dargon789@users.noreply.github.com> Date: Tue, 19 Nov 2024 05:01:53 +0700 Subject: [PATCH 006/374] Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Legion's <64915515+Dargon789@users.noreply.github.com> --- crates/config/src/compilation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/config/src/compilation.rs b/crates/config/src/compilation.rs index b4f00b91b0d92..cf5195a4bbf32 100644 --- a/crates/config/src/compilation.rs +++ b/crates/config/src/compilation.rs @@ -54,7 +54,7 @@ impl SettingsOverrides { #[derive(Debug, thiserror::Error)] pub enum RestrictionsError { - #[error("specified both exact and relative restrictions for {0}")] + #[error("invalid configuration: cannot specify both exact and relative restrictions for '{0}' - please choose only one type of restriction")] BothExactAndRelative(&'static str), } From 682c4f0966365d1cc3405eb415e4bf7fa4a41528 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 4 Mar 2025 05:16:42 +0700 Subject: [PATCH 007/374] Delete .github/workflows/apisec-scan.yml (#13) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/apisec-scan.yml | 71 ------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 .github/workflows/apisec-scan.yml diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml deleted file mode 100644 index d719efe66e0e2..0000000000000 --- a/.github/workflows/apisec-scan.yml +++ /dev/null @@ -1,71 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# APIsec addresses the critical need to secure APIs before they reach production. -# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. -# Clients rely on APIsec to evaluate every update and release, ensuring that no APIs go to production with vulnerabilities. - -# How to Get Started with APIsec.ai -# 1. Schedule a demo at https://www.apisec.ai/request-a-demo . -# -# 2. Register your account at https://cloud.apisec.ai/#/signup . -# -# 3. Register your API . See the video (https://www.youtube.com/watch?v=MK3Xo9Dbvac) to get up and running with APIsec quickly. -# -# 4. Get GitHub Actions scan attributes from APIsec Project -> Configurations -> Integrations -> CI-CD -> GitHub Actions -# -# apisec-run-scan -# -# This action triggers the on-demand scans for projects registered in APIsec. -# If your GitHub account allows code scanning alerts, you can then upload the sarif file generated by this action to show the scan findings. -# Else you can view the scan results from the project home page in APIsec Platform. -# The link to view the scan results is also displayed on the console on successful completion of action. - -# This is a starter workflow to help you get started with APIsec-Scan Actions - -name: APIsec - -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the "master" branch - # Customize trigger events based on your DevSecOps processes. - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - schedule: - - cron: '42 12 * * 4' - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - - -permissions: - contents: read - -jobs: - - Trigger_APIsec_scan: - permissions: - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - runs-on: ubuntu-latest - - steps: - - name: APIsec scan - uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea - with: - # The APIsec username with which the scans will be executed - apisec-username: ${{ secrets.apisec_username }} - # The Password of the APIsec user with which the scans will be executed - apisec-password: ${{ secrets.apisec_password}} - # The name of the project for security scan - apisec-project: "VAmPI" - # The name of the sarif format result file The file is written only if this property is provided. - sarif-result-file: "apisec-results.sarif" - - name: Import results - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ./apisec-results.sarif From 2a2b63d00c75aa243d19348de6515c475dd80b2c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 4 Mar 2025 19:38:16 +0700 Subject: [PATCH 008/374] Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7824f11..43a59e29b9c29 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,7 +25,7 @@ If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] + - Browser [e.g. Chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** From cfc4503bce8cac7d5a22ee3f1a235895e1a927c8 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:13:23 +0700 Subject: [PATCH 009/374] Add .circleci/config.yml (#15) --- .circleci/config.yml | 31 ++++++++++++++ .github/workflows/apisec-scan.yml | 71 +++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .github/workflows/apisec-scan.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..62291703e26a7 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello \ No newline at end of file diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..d719efe66e0e2 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,71 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# APIsec addresses the critical need to secure APIs before they reach production. +# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. +# Clients rely on APIsec to evaluate every update and release, ensuring that no APIs go to production with vulnerabilities. + +# How to Get Started with APIsec.ai +# 1. Schedule a demo at https://www.apisec.ai/request-a-demo . +# +# 2. Register your account at https://cloud.apisec.ai/#/signup . +# +# 3. Register your API . See the video (https://www.youtube.com/watch?v=MK3Xo9Dbvac) to get up and running with APIsec quickly. +# +# 4. Get GitHub Actions scan attributes from APIsec Project -> Configurations -> Integrations -> CI-CD -> GitHub Actions +# +# apisec-run-scan +# +# This action triggers the on-demand scans for projects registered in APIsec. +# If your GitHub account allows code scanning alerts, you can then upload the sarif file generated by this action to show the scan findings. +# Else you can view the scan results from the project home page in APIsec Platform. +# The link to view the scan results is also displayed on the console on successful completion of action. + +# This is a starter workflow to help you get started with APIsec-Scan Actions + +name: APIsec + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + # Customize trigger events based on your DevSecOps processes. + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '42 12 * * 4' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + + +permissions: + contents: read + +jobs: + + Trigger_APIsec_scan: + permissions: + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + + steps: + - name: APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + # The APIsec username with which the scans will be executed + apisec-username: ${{ secrets.apisec_username }} + # The Password of the APIsec user with which the scans will be executed + apisec-password: ${{ secrets.apisec_password}} + # The name of the project for security scan + apisec-project: "VAmPI" + # The name of the sarif format result file The file is written only if this property is provided. + sarif-result-file: "apisec-results.sarif" + - name: Import results + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ./apisec-results.sarif From 0b98e332b6f3bb02671adc4ffa4afcb47f7b7966 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 4 Mar 2025 22:48:54 +0700 Subject: [PATCH 010/374] Update apisec-scan.yml (#16) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/apisec-scan.yml | 79 +++++++------------------------ 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml index d719efe66e0e2..728b4819e7213 100644 --- a/.github/workflows/apisec-scan.yml +++ b/.github/workflows/apisec-scan.yml @@ -1,71 +1,24 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# APIsec addresses the critical need to secure APIs before they reach production. -# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. -# Clients rely on APIsec to evaluate every update and release, ensuring that no APIs go to production with vulnerabilities. - -# How to Get Started with APIsec.ai -# 1. Schedule a demo at https://www.apisec.ai/request-a-demo . -# -# 2. Register your account at https://cloud.apisec.ai/#/signup . -# -# 3. Register your API . See the video (https://www.youtube.com/watch?v=MK3Xo9Dbvac) to get up and running with APIsec quickly. -# -# 4. Get GitHub Actions scan attributes from APIsec Project -> Configurations -> Integrations -> CI-CD -> GitHub Actions -# -# apisec-run-scan -# -# This action triggers the on-demand scans for projects registered in APIsec. -# If your GitHub account allows code scanning alerts, you can then upload the sarif file generated by this action to show the scan findings. -# Else you can view the scan results from the project home page in APIsec Platform. -# The link to view the scan results is also displayed on the console on successful completion of action. - # This is a starter workflow to help you get started with APIsec-Scan Actions - name: APIsec -# Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the "master" branch - # Customize trigger events based on your DevSecOps processes. - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - schedule: - - cron: '42 12 * * 4' - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - - -permissions: - contents: read +on: [push, pull_request] jobs: - Trigger_APIsec_scan: - permissions: - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status runs-on: ubuntu-latest - steps: - - name: APIsec scan - uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea - with: - # The APIsec username with which the scans will be executed - apisec-username: ${{ secrets.apisec_username }} - # The Password of the APIsec user with which the scans will be executed - apisec-password: ${{ secrets.apisec_password}} - # The name of the project for security scan - apisec-project: "VAmPI" - # The name of the sarif format result file The file is written only if this property is provided. - sarif-result-file: "apisec-results.sarif" - - name: Import results - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ./apisec-results.sarif + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-project: VAmPI + sarif-result-file: apisec-results.sarif + apisec-profile: Master + apisec-oas: false + # Ensure you provide valid credentials and host details + username: ${{ secrets.APISEC_USERNAME }} + password: ${{ secrets.APISEC_PASSWORD }} + host: ${{ secrets.APISEC_HOST }} + scanner: "default-scanner" From 1e98af9cec50931c37d0e04379f4481b4cb45d1b Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:03:21 +0700 Subject: [PATCH 011/374] fix(forge): use float total cmp instead partial (#10005) (#17) fix(forge): use total cmp instead partial Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 3 +-- crates/common/src/contracts.rs | 2 +- crates/forge/bin/cmd/snapshot.rs | 6 ++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..a675ccae963c9 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,5 +1,4 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. -use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -17,7 +16,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 1e78751fde964..f9da7638fd422 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -147,7 +147,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap()) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index 872a53138c8ee..aa0f0d94d95ee 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -373,9 +373,7 @@ fn diff(tests: Vec, snaps: Vec) -> Result<()> let mut overall_gas_change = 0i128; let mut overall_gas_used = 0i128; - diffs.sort_by(|a, b| { - a.gas_diff().abs().partial_cmp(&b.gas_diff().abs()).unwrap_or(Ordering::Equal) - }); + diffs.sort_by(|a, b| a.gas_diff().abs().total_cmp(&b.gas_diff().abs())); for diff in diffs { let gas_change = diff.gas_change(); @@ -401,7 +399,7 @@ 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) { + match change.total_cmp(&0.0) { Ordering::Less => format!("{change_pct:.3}%").green().to_string(), Ordering::Equal => { format!("{change_pct:.3}%") From e45fdec11dd4bfea05b446395ca1a0ce90835160 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 5 Mar 2025 05:15:58 +0700 Subject: [PATCH 012/374] Update apisec-scan.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/apisec-scan.yml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml index 728b4819e7213..5875cc28ae724 100644 --- a/.github/workflows/apisec-scan.yml +++ b/.github/workflows/apisec-scan.yml @@ -1,24 +1,27 @@ -# This is a starter workflow to help you get started with APIsec-Scan Actions name: APIsec -on: [push, pull_request] +on: + pull_request: + branches: + - main jobs: - Trigger_APIsec_scan: + scan: runs-on: ubuntu-latest steps: - - name: Checkout repository + - name: Checkout code uses: actions/checkout@v2 - name: Run APIsec scan uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} apisec-project: VAmPI - sarif-result-file: apisec-results.sarif apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical apisec-oas: false - # Ensure you provide valid credentials and host details - username: ${{ secrets.APISEC_USERNAME }} - password: ${{ secrets.APISEC_PASSWORD }} - host: ${{ secrets.APISEC_HOST }} - scanner: "default-scanner" + apisec-openapi-spec-url: "https://example.com/openapi.json" From 02e28abdca0bf8c9acd833ab33b7a405198a7dea Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 5 Mar 2025 06:20:17 +0700 Subject: [PATCH 013/374] Revert "fix(forge): use float total cmp instead partial (#10005) (#17)" (#18) This reverts commit 1e98af9cec50931c37d0e04379f4481b4cb45d1b. --- crates/cli/src/utils/suggestions.rs | 3 ++- crates/common/src/contracts.rs | 2 +- crates/forge/bin/cmd/snapshot.rs | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..8f6d7f3cde092 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -16,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index f9da7638fd422..1e78751fde964 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -147,7 +147,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap()) .map(|(_, data)| data) } diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index aa0f0d94d95ee..872a53138c8ee 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -373,7 +373,9 @@ fn diff(tests: Vec, snaps: Vec) -> Result<()> let mut overall_gas_change = 0i128; let mut overall_gas_used = 0i128; - diffs.sort_by(|a, b| a.gas_diff().abs().total_cmp(&b.gas_diff().abs())); + diffs.sort_by(|a, b| { + a.gas_diff().abs().partial_cmp(&b.gas_diff().abs()).unwrap_or(Ordering::Equal) + }); for diff in diffs { let gas_change = diff.gas_change(); @@ -399,7 +401,7 @@ fn diff(tests: Vec, snaps: Vec) -> Result<()> fn fmt_pct_change(change: f64) -> String { let change_pct = change * 100.0; - match change.total_cmp(&0.0) { + match change.partial_cmp(&0.0).unwrap_or(Ordering::Equal) { Ordering::Less => format!("{change_pct:.3}%").green().to_string(), Ordering::Equal => { format!("{change_pct:.3}%") From 5931c87ca0200dcc7b7cd7dea7670086f9cb4d35 Mon Sep 17 00:00:00 2001 From: Legion's <64915515+Dargon789@users.noreply.github.com> Date: Wed, 5 Mar 2025 22:10:23 +0000 Subject: [PATCH 014/374] forge test openz --- .codesandbox/tasks.json | 7 ++++ .gitmodules | 6 +++ counter/.github/workflows/test.yml | 43 +++++++++++++++++++ counter/.gitignore | 14 +++++++ counter/README.md | 66 ++++++++++++++++++++++++++++++ counter/foundry.toml | 6 +++ counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 +++++++++ counter/src/Counter.sol | 14 +++++++ counter/test/Counter.t.sol | 24 +++++++++++ 11 files changed, 201 insertions(+) create mode 100644 .codesandbox/tasks.json create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..9265b4558406a --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..acd4ff74de833 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit acd4ff74de833399287ed6b31b4debf6b2b35527 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} From fdfd0e77852f21551fee8185091a2ba3be6dcb30 Mon Sep 17 00:00:00 2001 From: Legion's <64915515+Dargon789@users.noreply.github.com> Date: Wed, 5 Mar 2025 22:11:02 +0000 Subject: [PATCH 015/374] diff sig --- counter/lib/openzeppelin-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts index acd4ff74de833..4458d32fb643a 160000 --- a/counter/lib/openzeppelin-contracts +++ b/counter/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit acd4ff74de833399287ed6b31b4debf6b2b35527 +Subproject commit 4458d32fb643a22ac81698c5ce3ca3ca9fed5a50 From a0a8bde78ec08ddd85eafe773cde166203b0cf83 Mon Sep 17 00:00:00 2001 From: samooyo Date: Thu, 6 Mar 2025 14:14:51 +0100 Subject: [PATCH 016/374] feat(forge): add params natspec for enums --- crates/doc/src/parser/comment.rs | 12 +++++-- crates/doc/src/writer/as_doc.rs | 11 ++++++- crates/doc/src/writer/buf_writer.rs | 51 +++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index bf2b0ad7b4f0d..cba7f32df2767 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -46,7 +46,7 @@ impl CommentTag { } _ => { warn!(target: "forge::doc", tag=trimmed, "unknown comment tag. custom tags must be preceded by `custom:`"); - return None + return None; } }; Some(tag) @@ -157,6 +157,12 @@ impl From> for Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); @@ -184,8 +190,8 @@ impl<'a> CommentsRef<'a> { self.iter().any(|c| match (&c.tag, &target.tag) { (CommentTag::Inheritdoc, CommentTag::Inheritdoc) => c.value == target.value, (CommentTag::Param, CommentTag::Param) | (CommentTag::Return, CommentTag::Return) => { - c.split_first_word().map(|(name, _)| name) == - target.split_first_word().map(|(name, _)| name) + c.split_first_word().map(|(name, _)| name) + == target.split_first_word().map(|(name, _)| name) } (tag1, tag2) => tag1 == tag2, }) diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 56a0a4026c504..98fe8f668eca0 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -229,7 +229,16 @@ impl AsDoc for Document { writer.write_subtitle("Enums")?; enums.into_iter().try_for_each(|(item, comments, code)| { writer.write_heading(&item.name.safe_unwrap().name)?; - writer.write_section(comments, code) + + let filtered_comments: Comments = (*comments) + .iter() + .cloned() + .filter(|c| c.tag != CommentTag::Custom("variant".to_string())) + .collect::>() + .into(); + + writer.write_section(&filtered_comments, code)?; + writer.try_write_variant_table(&item, comments) })?; } } diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index e6109c338c03f..152f29dc61345 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,6 +1,8 @@ use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; -use solang_parser::pt::{ErrorParameter, EventParameter, Parameter, VariableDeclaration}; +use solang_parser::pt::{ + EnumDefinition, ErrorParameter, EventParameter, Parameter, VariableDeclaration, +}; use std::{ fmt::{self, Display, Write}, sync::LazyLock, @@ -19,6 +21,11 @@ const DEPLOYMENTS_TABLE_HEADERS: &[&str] = &["Network", "Address"]; static DEPLOYMENTS_TABLE_SEPARATOR: LazyLock = LazyLock::new(|| DEPLOYMENTS_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); +/// Headers and separator for rendering the variants table. +const VARIANTS_TABLE_HEADERS: &[&str] = &["Name", "Description"]; +const VARIANTS_TABLE_SEPARATOR: LazyLock = + LazyLock::new(|| VARIANTS_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); + /// The buffered writer. /// Writes various display items into the internal buffer. #[derive(Debug, Default)] @@ -132,7 +139,7 @@ impl BufWriter { // There is nothing to write. if params.is_empty() || comments.is_empty() { - return Ok(()) + return Ok(()); } self.write_bold(heading)?; @@ -177,6 +184,46 @@ impl BufWriter { self.try_write_table(CommentTag::Param, params, comments, "Properties") } + /// Tries to write the variant table to the buffer. + /// Doesn't write anything if either params or comments are empty. + pub fn try_write_variant_table( + &mut self, + params: &EnumDefinition, + comments: &Comments, + ) -> fmt::Result { + let comments = + comments.include_tags(&[CommentTag::Param, CommentTag::Custom("variant".to_string())]); + + // There is nothing to write. + if comments.is_empty() { + return Ok(()); + } + + self.write_bold("Variants")?; + self.writeln()?; + + self.write_piped(&VARIANTS_TABLE_HEADERS.join("|"))?; + self.write_piped(&VARIANTS_TABLE_SEPARATOR)?; + + for value in params.values.iter() { + let param_name = value.as_ref().map(|v| v.name.clone()); + + let comment = param_name.as_ref().and_then(|name| { + comments.iter().find_map(|comment| comment.match_first_word(name)) + }); + + let row = [ + Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + comment.unwrap_or_default().replace('\n', " "), + ]; + self.write_piped(&row.join("|"))?; + } + + self.writeln()?; + + Ok(()) + } + /// Tries to write the parameters table to the buffer. /// Doesn't write anything if either params or comments are empty. pub fn try_write_events_table( From c0b572530bad0202ee38f95f05988273b6ffda02 Mon Sep 17 00:00:00 2001 From: samooyo Date: Thu, 6 Mar 2025 14:15:45 +0100 Subject: [PATCH 017/374] chore: cargo fmt --- crates/doc/src/parser/comment.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index cba7f32df2767..5dc6c17f98890 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -190,8 +190,8 @@ impl<'a> CommentsRef<'a> { self.iter().any(|c| match (&c.tag, &target.tag) { (CommentTag::Inheritdoc, CommentTag::Inheritdoc) => c.value == target.value, (CommentTag::Param, CommentTag::Param) | (CommentTag::Return, CommentTag::Return) => { - c.split_first_word().map(|(name, _)| name) - == target.split_first_word().map(|(name, _)| name) + c.split_first_word().map(|(name, _)| name) == + target.split_first_word().map(|(name, _)| name) } (tag1, tag2) => tag1 == tag2, }) From d6033484b6f75de82b51eec87e1d0f36745898ac Mon Sep 17 00:00:00 2001 From: samooyo Date: Thu, 6 Mar 2025 14:23:16 +0100 Subject: [PATCH 018/374] fix: clippy errors --- crates/doc/src/writer/as_doc.rs | 4 ++-- crates/doc/src/writer/buf_writer.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 98fe8f668eca0..d38f880ed8693 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -232,13 +232,13 @@ impl AsDoc for Document { let filtered_comments: Comments = (*comments) .iter() - .cloned() .filter(|c| c.tag != CommentTag::Custom("variant".to_string())) + .cloned() .collect::>() .into(); writer.write_section(&filtered_comments, code)?; - writer.try_write_variant_table(&item, comments) + writer.try_write_variant_table(item, comments) })?; } } diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index 152f29dc61345..7fb657ba1d7a4 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -23,7 +23,7 @@ static DEPLOYMENTS_TABLE_SEPARATOR: LazyLock = /// Headers and separator for rendering the variants table. const VARIANTS_TABLE_HEADERS: &[&str] = &["Name", "Description"]; -const VARIANTS_TABLE_SEPARATOR: LazyLock = +static VARIANTS_TABLE_SEPARATOR: LazyLock = LazyLock::new(|| VARIANTS_TABLE_HEADERS.iter().map(|h| "-".repeat(h.len())).join("|")); /// The buffered writer. From 4cae0e34a3da13bbd8f6f4ce5236624b8f59d11d Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 7 Mar 2025 16:33:09 +0100 Subject: [PATCH 019/374] add @gregorsternat as co-author given their earlier work on https://github.com/foundry-rs/foundry/pull/9905 Co-authored-by: gregorsternat From ffefcd22688e53b429034b088442c51427c140c6 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 8 Mar 2025 20:05:26 +0700 Subject: [PATCH 020/374] Update openzeppelin-contracts --- counter/lib/openzeppelin-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts index 4458d32fb643a..ca7a4e39de086 160000 --- a/counter/lib/openzeppelin-contracts +++ b/counter/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 4458d32fb643a22ac81698c5ce3ca3ca9fed5a50 +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 From 30ccc2c75b877cfcbae31435d42bb8d0ff616be7 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:58:29 +0700 Subject: [PATCH 021/374] chore(deps): weekly `cargo update` (#10039) (#22) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- Cargo.lock | 160 +++++++++++++++++++++++++---------------------------- 1 file changed, 76 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fc0e255c012d..1cea57209a9b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,9 +86,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc96bae4737f5a8c8fae0db8fe9f000ebf3dd9894db755ba178fedbbab244d6" +checksum = "1715ed2a977d3ca4b39ffe0fc69f9f5b0e81382b348bdb5172abaa77a10f0b6d" dependencies = [ "alloy-eips", "alloy-primitives", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f02faf3f83ddd877925a46bc206eb70f718e0c438b3159cd153751cde7ade7a" +checksum = "660705969af143897d83937d73f53c741c1587f49c27c2cfce594e188fcbc1e4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47250b3cd43cf7a89aab508a81c11a1b92acd7f13b2200df21aed83cc3e1da7" +checksum = "5362637b25ba5282a921ca139a10f188fa34e1248a7c83c907a21de54d36dce1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b48f915ccad936effcfb6f3711225f3e00e1db0effd6faf7da57518da325c88" +checksum = "d13734f722326c846e7690ce732c9864f5ae82f52c7fb60c871f56654f348d4c" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -227,9 +227,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f88ea3da00e9f890ea8c6913dba52025454c34a57df893dab1d7b0e3c53615" +checksum = "738b6d7da21955cfdebeb7bcf300040b79e51c58a22e5f029ae989a8d834a3f3" dependencies = [ "alloy-eips", "alloy-primitives", @@ -252,9 +252,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba0e8a8d1bf498da62affe8472e1ee2fd064144d5826a1088a49416f2f448a3" +checksum = "e6fbb61c4dfe5def9a065438162faf39503b3e8d90f36d01563418a75f0ef016" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fc810a9967c3a6c69f9e9b2bb68bb13bd919aa741a533bdd5bf074f4a6709b" +checksum = "f10b0bc0657b018ee4f3758f889d066af6b1f20f57cd825b540182029090c151" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3dddaef8fc70307e8775be36a853e6118a628842d013f77f2c73ee48497aaf6" +checksum = "6cac4aeeabbbc16623d0745ae3b5a515d727ce8ef4ec4b6a886c3634d8b298fe" dependencies = [ "alloy-consensus", "alloy-eips", @@ -305,9 +305,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fc757d22ea387b404036301201e54f24e2f4f6a9632b6d8bd58d06d0abfa3b" +checksum = "d9151507742ac142201c3a56f74fd94c2ceda96115eed60a3dabaeef85e6b64a" dependencies = [ "alloy-genesis", "alloy-network", @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57dc6935c8b91fc742c6f2b6f100ea04060475b312c245b42a63729f4299951f" +checksum = "d06ffafc44e68c8244feb51919895c679c153a0b143c182e1ffe8cce998abf15" dependencies = [ "alloy-chains", "alloy-consensus", @@ -399,9 +399,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7538324684444a59fde89469f2dc097bca7dc2ee024e214f3a48c21917978f" +checksum = "6abe9f9e6be75dc8532bb2bf3f4c700c9e7bce8a3b05ec702a7324abdb118016" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -440,9 +440,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7604f6c3438bf48d5d183fa9dd02a0a0151a631eb15c1444ea4630c7f75b4271" +checksum = "c9ae316fdb92a4546f0dba4919ea4c1c0edb89a876536520c248fada0febac5d" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0957527935118dfb52427d220f326a80f6b99e9d21b94fa16d884d150ddc814" +checksum = "61e50cc5a693dfbef452e3dbcea3cd3342840d10eb3ffa018b0a5676967d8b6b" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -482,9 +482,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94cd833d43007286d1a3ff1246a8cd8f1d9a5854248e766e29f84b7d8a5bb86" +checksum = "2852d7350760c3fbfc60ee3396b95a66ea57afe3aeecee72bf1171ac6b1d5d18" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -494,9 +494,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84ae770c31d4aae0ce056ff594dffc8bb51cc246d1854ddae5083fbd978ef58" +checksum = "f726ebb03d5918a946d0a6e17829cabd90ffe928664dc3f7fdbba1be511760de" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed0352a0e8c55f6a53e82509342b5c3ff6a449601cb393569ce597d09ee816d" +checksum = "ab1fe2c636a14190fe3c6caf6a20d2fb8691a5824c1789ee495324a14295df82" dependencies = [ "alloy-primitives", "serde", @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d5c16b174beb31b50cbfc754b879dc9473bce12d0f2c4675d79419ed7381f5" +checksum = "b05bfe640e4708c5a83dfcc65b5e4a0deb6ddcb18897dd49862ddc3964e06ff8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -533,9 +533,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171bf00513b8cbfb338a9028f9151cac787c2fca5fc923c2c2201b059444df44" +checksum = "c24a3b6c552b74c4abdbaa45fd467a230f5564f62c6adae16972dd90b6b4dca5" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -553,9 +553,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658eb579d6499139da033c0b7e5a302dad90bb57c4e722250dd82636de035936" +checksum = "e25f16f6bfe65c23d873741aa343830de270db42c982822e23689d11f2f4d812" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -567,9 +567,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.12.0" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51305494072ed029790cdfe322a1a30d7d8a0baf47ad8f61b5981354e298741" +checksum = "9415e7e3f32a93a38ecb83aa449f7326081b5b362964291463f8f2060b4b8a31" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -579,9 +579,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de933b08fd186491d7171b4075c54ccf37d3f4fe17178bc3a8ba7cd1e77fb387" +checksum = "3aebca035ca670bd7de8165a9494c0d502625e26129dd95d17fdfd70d5521c02" dependencies = [ "alloy-primitives", "serde", @@ -590,9 +590,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55818175303de00576c5eb27ffa22ca31efe481e81cac7b05cc87e5f0ba2153" +checksum = "7abfef2a155c7d6a9f54861159a3fdd29abe1f67f0865b081bce4c2fdc9e83cc" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -607,9 +607,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57980f25e09e42aaa0eb9267951f54de06a59f66b8b12c74d47b2128a4456a5b" +checksum = "f25d4994d79911d1fdf64945c8fc08df46e0ba828abd5c7505264b7012318443" dependencies = [ "alloy-consensus", "alloy-network", @@ -625,9 +625,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369ce741e4c0cbdb44db9761f5767befc03b8c63f469c57a821d101197d65fc" +checksum = "4316e51a4131884e42a5d8b3228a508a60d280add3742bc1196b81f758b7c204" dependencies = [ "alloy-consensus", "alloy-network", @@ -643,9 +643,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54fef49330880f268f3cab430f3d69665436b2e651dd9ba2cbfdd1cef3100d8e" +checksum = "4097dcfbbef9ca5bc3c02901af815c82db89a1a429520acad1c34a0b136acbba" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -663,9 +663,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b1094d0e467f8bea128fc7a4d853a1300c53d20c9b2fddd76d59cc3b25d787" +checksum = "326033310c939b0d00b03fdbe2c243cb45add25c4e195d97b1792883c93a4c4c" dependencies = [ "alloy-consensus", "alloy-network", @@ -682,9 +682,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0586b1a20d7559d21f7e33318fa5881bf850f94c58b37e74a5ba245c34d8b80c" +checksum = "c6da910dc4386ab642a58a5a98cec80ecb47855b28b340a997ba7fa019f1cec0" dependencies = [ "alloy-consensus", "alloy-network", @@ -772,9 +772,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9f400ac2a4da066cd5c8beb4efc6b97111ac43e205928534768671b6cf2b7b" +checksum = "463f6cb5234c7420e7e77c248c0460a8e2dea933f2bb4e8f169d5f12510b38e0" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -791,9 +791,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b582d46a163af722ce965e014933c19994c5161979929ce1e486067cf4503a0" +checksum = "1eacd1c195c2a706bfbc92113d4bd3481b0dbd1742923a232dbe8a7910ac0fe5" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -806,9 +806,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c7b142bd57d2416453c762c6216315562f1b9ed5db2019539d5b34032f655f" +checksum = "6c5d3531e65eed82f14f93bb668fb06797c3754d0141c5da042bb63c5c19f13c" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -827,9 +827,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2dec526ed36192bfb9f02e6b7e4575a5135c358a96d786bfb42d691a3ca898" +checksum = "218e64c375edd8fe8d00d9aa983fb2d85f7cfeebb91f707fe4c7ba52410803f5" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -2606,12 +2606,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - [[package]] name = "crossbeam-channel" version = "0.5.14" @@ -5463,9 +5457,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" dependencies = [ "doctest-file", "futures-core", @@ -5884,9 +5878,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.45" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07d36d96ffe1b5b16ddf2bc80b3b26bb7a498b2a6591061250bf0af8e8095ad" +checksum = "85c54109598152e19add1933b223b1913bcd16e27cbd7c2783269fe2f9a6cbdc" dependencies = [ "ammonia", "anyhow", @@ -5896,6 +5890,7 @@ dependencies = [ "elasticlunr-rs", "env_logger", "handlebars", + "hex", "log", "memchr", "once_cell", @@ -5904,6 +5899,7 @@ dependencies = [ "regex", "serde", "serde_json", + "sha2", "shlex", "tempfile", "toml 0.5.11", @@ -6344,10 +6340,6 @@ name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" -dependencies = [ - "critical-section", - "portable-atomic", -] [[package]] name = "op-alloy-consensus" @@ -7681,9 +7673,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8dcd64f141950290e45c99f7710ede1b600297c91818bb30b3667c0f45dc0" +checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" dependencies = [ "bitflags 2.9.0", "errno", @@ -8840,7 +8832,7 @@ dependencies = [ "fastrand", "getrandom 0.3.1", "once_cell", - "rustix 1.0.0", + "rustix 1.0.1", "windows-sys 0.59.0", ] @@ -8868,11 +8860,11 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 0.38.44", + "rustix 1.0.1", "windows-sys 0.59.0", ] @@ -9053,9 +9045,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" dependencies = [ "backtrace", "bytes", From c235592917161f2eba6ec443739e84e524de9174 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 12 Mar 2025 02:59:45 +0700 Subject: [PATCH 022/374] python3-compile-vyper-contract (#27) * fix deny.toml, ignore RUSTSEC-2025-0014 (#10052) * fix deny.toml, ignore RUSTSEC-2025-0014 * roll back allow-git * update derive_more to 2.0 (#9987) * chore: fix ci, bump python setup version (#10054) Bump gh python setup version * feat(forge): match chain id with token symbol (#10043) * feat: match chain id with token symbol * fix: change symbol from matic to pol * fix: use NameChain instead of manual mapping * Fix tests, fmt and clippy --------- Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: grandizzy * feat(cast): storage add --proxy to manually specify a proxy contract when Etherscan fails to find it (#10033) * Adding a proxy parameter to the cast storage command, allowing manual selection of a proxy address to retrieve the full storage layout. * Adding tests for the cast storage proxy optional argument. * Using if let some else pattern. * Improved documentation. --------- Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(forge): use etherscan verifier if key provided (#10058) * fix(forge): use etherscan if key provided * Changes after review: expose is_sourcify and is_etherscan fns --------- Co-authored-by: gerald <3949379+getong@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: supamongkonR <73258014+supamongkonR@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Cizeon <110527347+Cizeon@users.noreply.github.com> --- .github/workflows/nextest.yml | 4 +- Cargo.lock | 347 +++++++++++++++++------------ Cargo.toml | 2 +- crates/cast/bin/cmd/storage.rs | 10 +- crates/cast/tests/cli/main.rs | 42 ++++ crates/forge/tests/cli/script.rs | 2 +- crates/script/src/simulate.rs | 10 +- crates/test-utils/src/util.rs | 5 +- crates/verify/src/etherscan/mod.rs | 9 +- crates/verify/src/provider.rs | 19 +- deny.toml | 3 +- 11 files changed, 295 insertions(+), 158 deletions(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index c8c0e0961eb0c..5f8a9561af7d4 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -68,11 +68,11 @@ jobs: with: bun-version: latest - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.11 - name: Install Vyper - run: pip install vyper==0.4.0 + run: pip --version && pip install vyper==0.4.0 - name: Forge RPC cache uses: actions/cache@v3 diff --git a/Cargo.lock b/Cargo.lock index aed90b9330cf4..f02aec99ed670 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,7 +339,7 @@ dependencies = [ "foldhash", "getrandom 0.2.15", "hashbrown 0.15.2", - "indexmap 2.7.1", + "indexmap 2.8.0", "itoa", "k256", "keccak-asm", @@ -435,7 +435,7 @@ checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -708,7 +708,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -721,11 +721,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.7.1", + "indexmap 2.8.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "syn-solidity", "tiny-keccak", ] @@ -743,7 +743,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.99", + "syn 2.0.100", "syn-solidity", ] @@ -1289,7 +1289,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1311,7 +1311,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1322,7 +1322,7 @@ checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1375,7 +1375,7 @@ checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -1565,9 +1565,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59d1327d8b5053c54bf2eaae63bf629ba9e904434d0835a28ed3c0ed0a614e" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -1614,11 +1614,52 @@ dependencies = [ "tracing", ] +[[package]] +name = "aws-smithy-http" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5949124d11e538ca21142d1fba61ab0a2a2c1bc3ed323cdb3e4b878bfb83166" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.2.0", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0497ef5d53065b7cd6a35e9c1654bd1fefeae5c52900d91d1b188b0af0f29324" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.4.8", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "pin-project-lite", + "rustls 0.21.12", + "tokio", + "tracing", +] + [[package]] name = "aws-smithy-json" -version = "0.61.2" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623a51127f24c30776c8b374295f2df78d92517386f77ba30773f15a30ce1422" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] @@ -1635,36 +1676,33 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.8" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" +checksum = "f6328865e36c6fd970094ead6b05efd047d3a80ec5fc3be5e743910da9f2ebf8" dependencies = [ "aws-smithy-async", - "aws-smithy-http 0.60.12", + "aws-smithy-http 0.62.0", + "aws-smithy-http-client", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", - "h2 0.3.26", "http 0.2.12", + "http 1.2.0", "http-body 0.4.6", "http-body 1.0.1", - "httparse", - "hyper 0.14.32", - "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", "pin-utils", - "rustls 0.21.12", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.3" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +checksum = "3da37cf5d57011cb1753456518ec76e31691f1f474b73934a284eb2a1c76510f" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1679,9 +1717,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.13" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b8a53819e42f10d0821f56da995e1470b199686a1809168db6ca485665f042" +checksum = "836155caafba616c0ff9b07944324785de2ab016141c3550bd1c07882f8cee8f" dependencies = [ "base64-simd", "bytes", @@ -1826,9 +1864,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d" [[package]] name = "bech32" @@ -1944,7 +1982,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2233,9 +2271,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -2243,9 +2281,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -2277,14 +2315,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2540,9 +2578,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" dependencies = [ "unicode-segmentation", ] @@ -2733,7 +2771,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2744,7 +2782,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2818,7 +2856,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2839,7 +2877,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2849,7 +2887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -2876,10 +2914,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "convert_case", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "unicode-xid", ] @@ -2889,9 +2926,10 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "unicode-xid", ] @@ -3000,7 +3038,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -3025,7 +3063,7 @@ checksum = "8dc51d98e636f5e3b0759a39257458b22619cac7e96d932da6eeb052891bb67c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -3157,7 +3195,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -3178,14 +3216,14 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff 0.2.4", "log", ] @@ -3263,7 +3301,7 @@ dependencies = [ "ahash", "alloy-dyn-abi", "alloy-primitives", - "indexmap 2.7.1", + "indexmap 2.8.0", ] [[package]] @@ -3315,12 +3353,12 @@ dependencies = [ [[package]] name = "fd-lock" -version = "4.0.3" +version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c44818c96aec5cadc9dacfb97bbcbcfc19a0de75b218412d56f57fbaab94e439" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 0.38.44", + "rustix 1.0.2", "windows-sys 0.59.0", ] @@ -3336,9 +3374,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core 0.6.4", "subtle", @@ -3507,7 +3545,7 @@ name = "forge-doc" version = "1.0.0" dependencies = [ "alloy-primitives", - "derive_more 1.0.0", + "derive_more 2.0.1", "eyre", "forge-fmt", "foundry-common", @@ -3616,7 +3654,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4047,7 +4085,7 @@ version = "1.0.0" dependencies = [ "alloy-primitives", "alloy-sol-types", - "derive_more 1.0.0", + "derive_more 2.0.1", "foundry-common-fmt", "foundry-macros", "foundry-test-utils", @@ -4194,7 +4232,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4354,7 +4392,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -4532,7 +4570,7 @@ checksum = "c57c477b645ee248b173bb1176b52dd528872f12c50375801a58aaf5ae91113f" dependencies = [ "bstr", "itoa", - "jiff", + "jiff 0.1.29", "thiserror 2.0.12", ] @@ -4754,7 +4792,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.7.1", + "indexmap 2.8.0", "slab", "tokio", "tokio-util", @@ -4773,7 +4811,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.2.0", - "indexmap 2.7.1", + "indexmap 2.8.0", "slab", "tokio", "tokio-util", @@ -4911,7 +4949,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5254,7 +5292,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5336,7 +5374,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5364,9 +5402,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "arbitrary", "equivalent", @@ -5454,7 +5492,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -5551,6 +5589,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "jiff" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "jiff-tzdb" version = "0.1.3" @@ -5880,9 +5942,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.46" +version = "0.4.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c54109598152e19add1933b223b1913bcd16e27cbd7c2783269fe2f9a6cbdc" +checksum = "7e1a8fe3a4a01f28dab245c474cb7b95ccb4d3d2f17a5419a3d949f474c45e84" dependencies = [ "ammonia", "anyhow", @@ -5963,7 +6025,7 @@ checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6053,7 +6115,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6297,7 +6359,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6339,9 +6401,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" [[package]] name = "op-alloy-consensus" @@ -6456,7 +6518,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6539,7 +6601,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6598,7 +6660,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6619,7 +6681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.1", + "indexmap 2.8.0", ] [[package]] @@ -6672,7 +6734,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6721,7 +6783,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6775,11 +6837,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy 0.8.23", ] [[package]] @@ -6821,7 +6883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" dependencies = [ "proc-macro2", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6896,7 +6958,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -6916,7 +6978,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "version_check", "yansi", ] @@ -6928,7 +6990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d35f4dc9988d1326b065b4def5e950c3ed727aa03e3151b86cc9e2aec6b03f54" dependencies = [ "futures", - "indexmap 2.7.1", + "indexmap 2.8.0", "nix 0.29.0", "tokio", "tracing", @@ -6984,7 +7046,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -7007,7 +7069,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -7070,7 +7132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed1a693391a16317257103ad06a88c6529ac640846021da7c435a06fffdacd7" dependencies = [ "chrono", - "indexmap 2.7.1", + "indexmap 2.8.0", "newtype-uuid", "quick-xml 0.37.2", "strip-ansi-escapes", @@ -7423,9 +7485,9 @@ dependencies = [ [[package]] name = "revm" -version = "19.5.0" +version = "19.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc5bef3c95fadf3b6a24a253600348380c169ef285f9780a793bb7090c8990d" +checksum = "7b906766b7ba049b515848952b5ae74f363d456e98de2021048a513e442b4f42" dependencies = [ "auto_impl", "cfg-if", @@ -7675,9 +7737,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" +checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" dependencies = [ "bitflags 2.9.0", "errno", @@ -7898,7 +7960,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -7937,9 +7999,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.7" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07779b9b918cc05650cb30f404d4d7835d26df37c235eded8a6832e2fb82cca" +checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" [[package]] name = "sec1" @@ -8059,22 +8121,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8085,7 +8147,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8094,7 +8156,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.8.0", "itoa", "memchr", "ryu", @@ -8129,7 +8191,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8163,7 +8225,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_derive", "serde_json", @@ -8180,7 +8242,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8215,7 +8277,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8481,7 +8543,7 @@ checksum = "71d07263243b313296eca18f18eda3a190902dc3284bf67ceff29b8b54dac3e6" dependencies = [ "bumpalo", "index_vec", - "indexmap 2.7.1", + "indexmap 2.8.0", "parking_lot", "rayon", "rustc-hash", @@ -8524,7 +8586,7 @@ checksum = "970d7c774741f786d62cab78290e47d845b0b9c0c9d094a1642aced1d7946036" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8696,7 +8758,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8709,7 +8771,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8777,9 +8839,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.99" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -8795,7 +8857,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8815,7 +8877,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8834,7 +8896,7 @@ dependencies = [ "fastrand", "getrandom 0.3.1", "once_cell", - "rustix 1.0.1", + "rustix 1.0.2", "windows-sys 0.59.0", ] @@ -8866,7 +8928,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 1.0.1", + "rustix 1.0.2", "windows-sys 0.59.0", ] @@ -8925,7 +8987,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -8936,7 +8998,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -9071,7 +9133,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -9174,7 +9236,7 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", @@ -9196,7 +9258,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.8.0", "serde", "serde_spanned", "toml_datetime", @@ -9347,7 +9409,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -9784,7 +9846,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -9819,7 +9881,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10064,7 +10126,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10075,7 +10137,7 @@ checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10086,7 +10148,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10097,7 +10159,7 @@ checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10474,7 +10536,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "synstructure", ] @@ -10484,7 +10546,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive 0.7.35", ] @@ -10505,7 +10566,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10516,7 +10577,7 @@ checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10536,7 +10597,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", "synstructure", ] @@ -10557,7 +10618,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10579,7 +10640,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.100", ] [[package]] @@ -10594,7 +10655,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.7.1", + "indexmap 2.8.0", "memchr", "thiserror 2.0.12", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 0811a200fec91..eae5e6f6e266c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -255,7 +255,7 @@ proc-macro2 = "1.0" quote = "1.0" syn = "2.0" async-trait = "0.1" -derive_more = { version = "1.0", features = ["full"] } +derive_more = { version = "2.0", features = ["full"] } thiserror = "2" # bench diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index c7a90ad2f06b5..c51511c423487 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -51,6 +51,10 @@ pub struct StorageArgs { #[arg(value_parser = parse_slot)] slot: Option, + /// The known proxy address. If provided, the storage layout is retrieved from this address. + #[arg(long,value_parser = NameOrAddress::from_str)] + proxy: Option, + /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. @@ -134,7 +138,11 @@ 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 source = find_source(client, address).await?; + let source = if let Some(proxy) = self.proxy { + find_source(client, proxy.resolve(&provider).await?).await? + } else { + 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") diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index e88ade5f28489..509caf0efd468 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1295,6 +1295,48 @@ casttest!(storage_layout_complex, |_prj, cmd| { ╰-------------------------------+--------------------------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+---------------------------------╯ +"#]]); +}); + +casttest!(storage_layout_complex_proxy, |_prj, cmd| { + cmd.args([ + "storage", + "--rpc-url", + next_rpc_endpoint(NamedChain::Sepolia).as_str(), + "--block", + "7857852", + "--etherscan-api-key", + next_mainnet_etherscan_api_key().as_str(), + "0xE2588A9CAb7Ea877206E35f615a39f84a64A7A3b", + "--proxy", + "0x29fcb43b46531bca003ddc8fcb67ffe91900c762" + ]) + .assert_success() + .stdout_eq(str![[r#" + +╭----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------╮ +| Name | Type | Slot | Offset | Bytes | Value | Hex Value | Contract | ++============================================================================================================================================================================================================================================================+ +| singleton | address | 0 | 0 | 20 | 239704109775411986678417050956533140837380441954 | 0x00000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c762 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| modules | mapping(address => address) | 1 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| owners | mapping(address => address) | 2 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| ownerCount | uint256 | 3 | 0 | 32 | 1 | 0x0000000000000000000000000000000000000000000000000000000000000001 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| threshold | uint256 | 4 | 0 | 32 | 1 | 0x0000000000000000000000000000000000000000000000000000000000000001 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| nonce | uint256 | 5 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| _deprecatedDomainSeparator | bytes32 | 6 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| signedMessages | mapping(bytes32 => uint256) | 7 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | +|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| +| approvedHashes | mapping(address => mapping(bytes32 => uint256)) | 8 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | +╰----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------╯ + + "#]]); }); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index aaf0658c65ac3..465d3db337590 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1953,7 +1953,7 @@ contract SimpleScript is Script { .assert_success() .stdout_eq(str![[r#" {"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"[..]","output":"[..]","gas_used":"{...}","gas_limit":"{...}","status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":"{...}","gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":"{...}","gas_limit":1056940994,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":"{...}","gas_limit":1056940645,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":"{...}","labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} -{"chain":31337,"estimated_gas_price":"{...}","estimated_total_gas_used":"{...}","estimated_amount_required":"{...}"} +{"chain":31337,"estimated_gas_price":"{...}","estimated_total_gas_used":"{...}","estimated_amount_required":"{...}","token_symbol":"ETH"} {"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":"{...}","gas_price":"{...}"} {"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index cf6aeb349436b..a58b1058ebc2e 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,6 +9,7 @@ use crate::{ sequence::get_commit_hash, ScriptArgs, ScriptConfig, ScriptResult, }; +use alloy_chains::NamedChain; use alloy_network::TransactionBuilder; use alloy_primitives::{map::HashMap, utils::format_units, Address, Bytes, TxKind, U256}; use dialoguer::Confirm; @@ -346,6 +347,12 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); + // Get the native token symbol for the chain using NamedChain + let token_symbol = NamedChain::try_from(provider_info.chain) + .unwrap_or_default() + .native_currency_symbol() + .unwrap_or("ETH"); + // 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 { @@ -369,7 +376,7 @@ impl FilledTransactionsState { sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; + sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; sh_println!("\n==========================")?; } else { sh_println!( @@ -379,6 +386,7 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, + "token_symbol": token_symbol, }) )?; } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index f2b71064f1f56..75f39bf1d2e2e 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1017,7 +1017,10 @@ fn test_redactions() -> snapbox::Redactions { ("[SAVED_SENSITIVE_VALUES]", r"Sensitive values saved to: .*\.json"), ("[ESTIMATED_GAS_PRICE]", r"Estimated gas price:\s*(\d+(\.\d+)?)\s*gwei"), ("[ESTIMATED_TOTAL_GAS_USED]", r"Estimated total gas used for script: \d+"), - ("[ESTIMATED_AMOUNT_REQUIRED]", r"Estimated amount required:\s*(\d+(\.\d+)?)\s*ETH"), + ( + "[ESTIMATED_AMOUNT_REQUIRED]", + r"Estimated amount required:\s*(\d+(\.\d+)?)\s*[A-Z]{3}", + ), ]; for (placeholder, re) in redactions { r.insert(placeholder, Regex::new(re).expect(re)).expect(re); diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index eece0c928064d..e61d5e1b07f5c 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -278,8 +278,13 @@ 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('/'); - let base_url = if *verifier_type != VerificationProviderType::Etherscan { - // If verifier is not Etherscan then set base url as api url without trialing /api. + + // Verifier is etherscan if explicitly set or if no verifier set (default sourcify) but + // API key passed. + let is_etherscan = verifier_type.is_etherscan() || + (verifier_type.is_sourcify() && etherscan_key.is_some()); + let base_url = if !is_etherscan { + // If verifier is not Etherscan then set base url as api url without /api suffix. api_url.strip_prefix("/api").unwrap_or(api_url) } else { base_url.unwrap_or(api_url) diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 07f1e09ea6aa3..01a06332a36ce 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -170,8 +170,9 @@ pub enum VerificationProviderType { impl VerificationProviderType { /// Returns the corresponding `VerificationProvider` for the key pub fn client(&self, key: Option<&str>) -> Result> { - // 1. If `--verifier sourcify` is set, always use Sourcify. - if matches!(self, Self::Sourcify) { + let has_key = key.as_ref().is_some_and(|k| !k.is_empty()); + // 1. If no verifier or `--verifier sourcify` is set and no API key provided, use Sourcify. + if !has_key && self.is_sourcify() { sh_println!( "Attempting to verify on Sourcify. Pass the --etherscan-api-key to verify on Etherscan, \ or use the --verifier flag to verify on another provider." @@ -180,8 +181,8 @@ impl VerificationProviderType { } // 2. If `--verifier etherscan` is explicitly set, enforce the API key requirement. - if matches!(self, Self::Etherscan) { - if key.as_ref().is_none_or(|key| key.is_empty()) { + if self.is_etherscan() { + if !has_key { eyre::bail!("ETHERSCAN_API_KEY must be set to use Etherscan as a verifier") } return Ok(Box::::default()); @@ -194,11 +195,19 @@ impl VerificationProviderType { } // 4. If no `--verifier` is specified but `ETHERSCAN_API_KEY` is set, default to Etherscan. - if key.as_ref().is_some_and(|k| !k.is_empty()) { + if has_key { return Ok(Box::::default()); } // 5. If no valid provider is specified, bail. eyre::bail!("No valid verification provider specified. Pass the --verifier flag to specify a provider or set the ETHERSCAN_API_KEY environment variable to use Etherscan as a verifier.") } + + pub fn is_sourcify(&self) -> bool { + matches!(self, Self::Sourcify) + } + + pub fn is_etherscan(&self) -> bool { + matches!(self, Self::Etherscan) + } } diff --git a/deny.toml b/deny.toml index ac92742909a18..33c5a4233e55d 100644 --- a/deny.toml +++ b/deny.toml @@ -11,6 +11,8 @@ ignore = [ "RUSTSEC-2024-0436", # https://rustsec.org/advisories/RUSTSEC-2024-0437 protobuf! Crash due to uncontrolled recursion in protobuf crate. "RUSTSEC-2024-0437", + # humantime is unmaintained + "RUSTSEC-2025-0014", ] # This section is considered when running `cargo deny check bans`. @@ -47,7 +49,6 @@ allow = [ "BSD-3-Clause", "ISC", "Unicode-3.0", - "OpenSSL", "Unlicense", "WTFPL", "BSL-1.0", From 2802d43da3811c20c0fcec2454fd326921fb47d9 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 15 Mar 2025 12:59:39 +0700 Subject: [PATCH 023/374] const LATEST_SOLC: Version = Version::new(0, 8, 29); (#28) * feat(forge): allow path in forge selectors upload (#10073) * feat(forge): allow path in forge selectors upload * Changes after review: reuse PathOrContractInfo * feat: add `x86_64-musl` and `aarch64-musl` release targets (#9984) * feat: add `x86_64` and `aarch64` musl targets * feat: update nextest matrices * try with aarch64 targets * feat: define granular linux targets and restore `aarch64` one * revert `matrices.py` file * chore: reenable impersonate test (#10076) * feat: solc 0.8.29 (#10078) * feat: solc 0.8.29 * update match --------- Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Giovanni Napoli Co-authored-by: Matthias Seitz --- .github/workflows/release.yml | 10 ++++ Cargo.lock | 59 ++++++------------- Cargo.toml | 2 +- crates/cast/tests/cli/main.rs | 69 ++++++++++------------ crates/config/src/utils.rs | 3 +- crates/forge/bin/cmd/selectors.rs | 27 +++++++-- crates/forge/tests/cli/odyssey.rs | 6 ++ crates/forge/tests/cli/svm.rs | 8 ++- crates/forge/tests/cli/test_cmd.rs | 95 ++++++++++++++++++++++++++++++ crates/test-utils/src/util.rs | 4 +- 10 files changed, 196 insertions(+), 87 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e0b5739e8ce7..b798ea6fd68fa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,11 +94,21 @@ jobs: svm_target_platform: linux-amd64 platform: linux arch: amd64 + - runner: Linux-20.04 + target: x86_64-unknown-linux-musl + 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: Linux-20.04 + target: aarch64-unknown-linux-musl + svm_target_platform: linux-aarch64 + platform: linux + arch: arm64 # This is pinned to `macos-13-large` to support old SDK versions. # If the runner is deprecated it should be pinned to the oldest available version of the runner. - runner: macos-13-large diff --git a/Cargo.lock b/Cargo.lock index 48d28ddf548d1..5224c76930bac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2191,7 +2191,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "clap", - "dirs 6.0.0", + "dirs", "eyre", "forge-fmt", "foundry-cli", @@ -2958,22 +2958,13 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.5.0", + "dirs-sys", ] [[package]] @@ -2986,18 +2977,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys" version = "0.5.0" @@ -3882,15 +3861,15 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8f0bab060fd7c1764c4be2563e6933d39ec8c2b8a8d6c08aaf45ab29d08310" +checksum = "21089dce1284b88283a6dc1684b22347debb517e86f7e0033e1cf277fda3ea7e" dependencies = [ "alloy-json-abi", "alloy-primitives", "auto_impl", "derive_more 1.0.0", - "dirs 6.0.0", + "dirs", "dyn-clone", "foundry-compilers-artifacts", "foundry-compilers-core", @@ -3919,9 +3898,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102dd131e939d80cc5c85214d2f0f6ba20ed75cf098019c4d995791b4ebae05" +checksum = "c6cc7a5aec89a3e0f271ec522aac586f97d9104c3b31563716f9ca20113e7f5c" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3929,9 +3908,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9ef02de4fda04ae3ed098afb6e16edd742d1073f972197a4566836b453bdcd" +checksum = "c0c1058532f7a062f9bba3cd807f74e034b2f37f8a27d8bcccabdc104102f847" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3953,9 +3932,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd9d33cbeda448af917105920fefdc3ac42f9ffdaa2d840d58207db7807ea29" +checksum = "33f5f4621a0fad72868c5174c01bbfdfb16a6a650edd2f4a41cf5e5dc611a15e" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3968,9 +3947,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "746d121f7b86b84b20e582a27a56c49435768ad3b8005e9afeaf68b53a77fb5c" +checksum = "3de4acbffff75510c230626f2c3071efa1d6c031afc821a8ed410b67ee6958dd" dependencies = [ "alloy-primitives", "cfg-if", @@ -3995,7 +3974,7 @@ dependencies = [ "Inflector", "alloy-chains", "alloy-primitives", - "dirs 6.0.0", + "dirs", "dunce", "eyre", "figment", @@ -8780,12 +8759,12 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svm-rs" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4197826bb07b996788b9860a95a1fe2c1307b2404a8c66f5ba825c42532b7c3c" +checksum = "9a30a58b94c1ddc5f07a428d16e58e8f4c7cee9e67130cda12ed148f5ef98fff" dependencies = [ "const-hex", - "dirs 5.0.1", + "dirs", "fs4", "reqwest", "semver 1.0.26", @@ -8800,9 +8779,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074faea21171905847a96135b3896e2e0b74373758ca07b96a41c646cc04a8e5" +checksum = "ebddc0ea9fd9fb61d0de9585a99c58f87e8c14b8811726a57cc5f5b1a029585e" dependencies = [ "build_const", "const-hex", diff --git a/Cargo.toml b/Cargo.toml index eae5e6f6e266c..b55f708db90cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -188,7 +188,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.11.0", default-features = false } -foundry-compilers = { version = "0.13.3", default-features = false } +foundry-compilers = { version = "0.13.5", default-features = false } foundry-fork-db = "0.12" solang-parser = "=0.3.3" solar-parse = { version = "=0.1.1", default-features = false } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 06c5e61167f16..2d1ad75854999 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2032,43 +2032,38 @@ forgetest_async!(cast_call_custom_chain_id, |_prj, cmd| { }); // https://github.com/foundry-rs/foundry/issues/9541 -forgetest_async!( - // ignore for now, fails with "json: unsupported value: NaN" - #[ignore] - cast_run_impersonated_tx, - |_prj, cmd| { - let (_api, handle) = anvil::spawn( - NodeConfig::test() - .with_auto_impersonate(true) - .with_eth_rpc_url(Some("https://sepolia.base.org")), - ) - .await; - - let http_endpoint = handle.http_endpoint(); - - let provider = ProviderBuilder::new().on_http(http_endpoint.parse().unwrap()); - - // send impersonated tx - let tx = TransactionRequest::default() - .with_from(address!("041563c07028Fc89106788185763Fc73028e8511")) - .with_to(address!("F38aA5909D89F5d98fCeA857e708F6a6033f6CF8")) - .with_input( - Bytes::from_str( - "0x60fe47b1000000000000000000000000000000000000000000000000000000000000000c", - ) - .unwrap(), - ); - - let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - - assert!(receipt.status()); - - // run impersonated tx - cmd.cast_fuse() - .args(["run", &receipt.transaction_hash.to_string(), "--rpc-url", &http_endpoint]) - .assert_success(); - } -); +forgetest_async!(cast_run_impersonated_tx, |_prj, cmd| { + let (_api, handle) = anvil::spawn( + NodeConfig::test() + .with_auto_impersonate(true) + .with_eth_rpc_url(Some("https://sepolia.base.org")), + ) + .await; + + let http_endpoint = handle.http_endpoint(); + + let provider = ProviderBuilder::new().on_http(http_endpoint.parse().unwrap()); + + // send impersonated tx + let tx = TransactionRequest::default() + .with_from(address!("041563c07028Fc89106788185763Fc73028e8511")) + .with_to(address!("F38aA5909D89F5d98fCeA857e708F6a6033f6CF8")) + .with_input( + Bytes::from_str( + "0x60fe47b1000000000000000000000000000000000000000000000000000000000000000c", + ) + .unwrap(), + ); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert!(receipt.status()); + + // run impersonated tx + cmd.cast_fuse() + .args(["run", &receipt.transaction_hash.to_string(), "--rpc-url", &http_endpoint]) + .assert_success(); +}); // casttest!(fetch_src_blockscout, |_prj, cmd| { diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index 94f4823dc7cb5..86b25d1043797 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -263,6 +263,7 @@ pub fn evm_spec_id(evm_version: EvmVersion, odyssey: bool) -> SpecId { EvmVersion::Paris => SpecId::MERGE, EvmVersion::Shanghai => SpecId::SHANGHAI, EvmVersion::Cancun => SpecId::CANCUN, - EvmVersion::Prague => SpecId::OSAKA, // Osaka enables EOF + EvmVersion::Prague => SpecId::PRAGUE, + EvmVersion::Osaka => SpecId::OSAKA, } } diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index 56c25cc003cd6..9c2db2be8d8ec 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -7,7 +7,7 @@ use foundry_cli::{ utils::{cache_local_signatures, FoundryPathExt}, }; use foundry_common::{ - compile::{compile_target, ProjectCompiler}, + compile::{compile_target, PathOrContractInfo, ProjectCompiler}, selectors::{import_selectors, SelectorImportData}, }; use foundry_compilers::{artifacts::output_selection::ContractOutputSelection, info::ContractInfo}; @@ -36,8 +36,9 @@ pub enum SelectorsSubcommands { #[command(visible_alias = "up")] Upload { /// The name of the contract to upload selectors for. + /// Can also be in form of `path:contract name`. #[arg(required_unless_present = "all")] - contract: Option, + contract: Option, /// Upload selectors for all contracts in the project. #[arg(long, required_unless_present = "contract")] @@ -107,8 +108,15 @@ impl SelectorsSubcommands { }; let project = build_args.project()?; - let output = if let Some(name) = &contract { - let target_path = project.find_contract_path(name)?; + let output = if let Some(contract_info) = &contract { + let Some(contract_name) = contract_info.name() else { + eyre::bail!("No contract name provided.") + }; + + let target_path = contract_info + .path() + .map(Ok) + .unwrap_or_else(|| project.find_contract_path(contract_name))?; compile_target(&target_path, &project, false)? } else { ProjectCompiler::new().compile(&project)? @@ -125,8 +133,15 @@ impl SelectorsSubcommands { .map(|(_, contract, artifact)| (contract, artifact)) .collect() } else { - let contract = contract.unwrap(); - let found_artifact = output.find_first(&contract); + let contract_info = contract.unwrap(); + let contract = contract_info.name().unwrap().to_string(); + + let found_artifact = if let Some(path) = contract_info.path() { + output.find(project.root().join(path).as_path(), &contract) + } else { + output.find_first(&contract) + }; + let artifact = found_artifact .ok_or_else(|| { eyre::eyre!( diff --git a/crates/forge/tests/cli/odyssey.rs b/crates/forge/tests/cli/odyssey.rs index 7d98e79fcf082..f465c032bd134 100644 --- a/crates/forge/tests/cli/odyssey.rs +++ b/crates/forge/tests/cli/odyssey.rs @@ -1,3 +1,5 @@ +use foundry_compilers::artifacts::EvmVersion; + // Ensure we can run basic counter tests with EOF support. forgetest_init!(test_eof_flag, |prj, cmd| { if !has_docker() { @@ -5,6 +7,10 @@ forgetest_init!(test_eof_flag, |prj, cmd| { return; } + prj.update_config(|config| { + config.evm_version = EvmVersion::Osaka; + }); + cmd.forge_fuse().args(["test", "--eof"]).assert_success().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index e0c10a052e936..d585529da8e40 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, 27); +const LATEST_SOLC: Version = Version::new(0, 8, 29); macro_rules! ensure_svm_releases { ($($test:ident => $platform:ident),* $(,)?) => {$( @@ -57,6 +57,12 @@ contract CounterTest is Test {{ "# ); prj.add_test("Counter", &src).unwrap(); + + // we need to remove the pinned solc version for this + prj.update_config(|c| { + c.solc.take(); + }); + cmd.arg("test").assert_success().stdout_eq(str![[r#" ... Ran 1 test for test/Counter.sol:CounterTest diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index ce0f4b13f89d1..a13f8c51ba81a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -3316,3 +3316,98 @@ Traces: "#]]); }); + +// +forgetest_init!(can_upload_selectors_with_path, |prj, cmd| { + prj.add_source( + "CounterV1.sol", + r#" +contract Counter { + uint256 public number; + + function setNumberV1(uint256 newNumber) public { + number = newNumber; + } + + function incrementV1() public { + number++; + } +} + "#, + ) + .unwrap(); + + prj.add_source( + "CounterV2.sol", + r#" +contract CounterV2 { + uint256 public number; + + function setNumberV2(uint256 newNumber) public { + number = newNumber; + } + + function incrementV2() public { + number++; + } +} + "#, + ) + .unwrap(); + + // Upload Counter without path fails as there are multiple contracts with same name. + cmd.args(["selectors", "upload", "Counter"]).assert_failure().stderr_eq(str![[r#" +... +Error: Multiple contracts found with the name `Counter` +... + +"#]]); + + // Upload without contract name should fail. + cmd.forge_fuse().args(["selectors", "upload", "src/Counter.sol"]).assert_failure().stderr_eq( + str![[r#" +... +Error: No contract name provided. +... + +"#]], + ); + + // Upload single CounterV2. + cmd.forge_fuse().args(["selectors", "upload", "CounterV2"]).assert_success().stdout_eq(str![[ + r#" +... +Uploading selectors for CounterV2... +... +Selectors successfully uploaded to OpenChain +... + +"# + ]]); + + // Upload CounterV1 with path. + cmd.forge_fuse() + .args(["selectors", "upload", "src/CounterV1.sol:Counter"]) + .assert_success() + .stdout_eq(str![[r#" +... +Uploading selectors for Counter... +... +Selectors successfully uploaded to OpenChain +... + +"#]]); + + // Upload Counter with path. + cmd.forge_fuse() + .args(["selectors", "upload", "src/Counter.sol:Counter"]) + .assert_success() + .stdout_eq(str![[r#" +... +Uploading selectors for Counter... +... +Selectors successfully uploaded to OpenChain +... + +"#]]); +}); diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 75f39bf1d2e2e..3c2b0546f7061 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -213,7 +213,7 @@ impl ExtTester { } } -/// Initializes a project with `forge init` at the given path. +/// Initializes a project with `forge init` at the given path from a template directory. /// /// This should be called after an empty project is created like in /// [some of this crate's macros](crate::forgetest_init). @@ -226,6 +226,8 @@ impl ExtTester { /// This used to use a `static` `Lazy`, but this approach does not with `cargo-nextest` because it /// runs each test in a separate process. Instead, we use a global lock file to ensure that only one /// test can initialize the template at a time. +/// +/// This sets the project's solc version to the [`SOLC_VERSION`]. #[allow(clippy::disallowed_macros)] pub fn initialize(target: &Path) { println!("initializing {}", target.display()); From eca7fc8c53535001016249581985038e1de0f7a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:39:47 +0700 Subject: [PATCH 024/374] chore(deps): bump zip in the cargo group across 1 directory (#31) Bumps the cargo group with 1 update in the / directory: [zip](https://github.com/zip-rs/zip2). Updates `zip` from 2.2.3 to 2.4.1 - [Release notes](https://github.com/zip-rs/zip2/releases) - [Changelog](https://github.com/zip-rs/zip2/blob/master/CHANGELOG.md) - [Commits](https://github.com/zip-rs/zip2/compare/v2.2.3...v2.4.1) --- updated-dependencies: - dependency-name: zip dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5224c76930bac..58a3082ed6709 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8537,7 +8537,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.12", + "thiserror 1.0.69", "tracing", "unicode-width 0.2.0", ] @@ -8772,7 +8772,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror 2.0.12", + "thiserror 1.0.69", "url", "zip", ] @@ -10609,9 +10609,9 @@ dependencies = [ [[package]] name = "zip" -version = "2.2.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b280484c454e74e5fff658bbf7df8fdbe7a07c6b2de4a53def232c15ef138f3a" +checksum = "938cc23ac49778ac8340e366ddc422b2227ea176edb447e23fc0627608dddadd" dependencies = [ "arbitrary", "bzip2", From e24df49cb7a20c65b66ae095613761b8cf9d60b6 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 30 Mar 2025 20:12:34 +0700 Subject: [PATCH 025/374] Add .circleci/config.yml (#33) From f62dc844821b4f224f90969790ced81a87dab791 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 30 Mar 2025 20:46:08 +0700 Subject: [PATCH 026/374] Revert "python3-compile-vyper-contract (#27)" (#34) This reverts commit c235592917161f2eba6ec443739e84e524de9174. --- .github/workflows/nextest.yml | 4 +- Cargo.lock | 347 ++++++++++++----------------- Cargo.toml | 2 +- crates/cast/bin/cmd/storage.rs | 10 +- crates/cast/tests/cli/main.rs | 42 ---- crates/forge/tests/cli/script.rs | 2 +- crates/script/src/simulate.rs | 10 +- crates/test-utils/src/util.rs | 5 +- crates/verify/src/etherscan/mod.rs | 9 +- crates/verify/src/provider.rs | 19 +- deny.toml | 3 +- 11 files changed, 158 insertions(+), 295 deletions(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index 5f8a9561af7d4..c8c0e0961eb0c 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -68,11 +68,11 @@ jobs: with: bun-version: latest - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v4 with: python-version: 3.11 - name: Install Vyper - run: pip --version && pip install vyper==0.4.0 + run: pip install vyper==0.4.0 - name: Forge RPC cache uses: actions/cache@v3 diff --git a/Cargo.lock b/Cargo.lock index 58a3082ed6709..b4532ce77227a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,7 +339,7 @@ dependencies = [ "foldhash", "getrandom 0.2.15", "hashbrown 0.15.2", - "indexmap 2.8.0", + "indexmap 2.7.1", "itoa", "k256", "keccak-asm", @@ -435,7 +435,7 @@ checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -708,7 +708,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -721,11 +721,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.8.0", + "indexmap 2.7.1", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "syn-solidity", "tiny-keccak", ] @@ -743,7 +743,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.100", + "syn 2.0.99", "syn-solidity", ] @@ -1289,7 +1289,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -1311,7 +1311,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -1322,7 +1322,7 @@ checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -1375,7 +1375,7 @@ checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -1565,9 +1565,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.5" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" +checksum = "fa59d1327d8b5053c54bf2eaae63bf629ba9e904434d0835a28ed3c0ed0a614e" dependencies = [ "futures-util", "pin-project-lite", @@ -1614,52 +1614,11 @@ dependencies = [ "tracing", ] -[[package]] -name = "aws-smithy-http" -version = "0.62.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5949124d11e538ca21142d1fba61ab0a2a2c1bc3ed323cdb3e4b878bfb83166" -dependencies = [ - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", - "http 0.2.12", - "http 1.2.0", - "http-body 0.4.6", - "once_cell", - "percent-encoding", - "pin-project-lite", - "pin-utils", - "tracing", -] - -[[package]] -name = "aws-smithy-http-client" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0497ef5d53065b7cd6a35e9c1654bd1fefeae5c52900d91d1b188b0af0f29324" -dependencies = [ - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "h2 0.4.8", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper-rustls 0.24.2", - "pin-project-lite", - "rustls 0.21.12", - "tokio", - "tracing", -] - [[package]] name = "aws-smithy-json" -version = "0.61.3" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" +checksum = "623a51127f24c30776c8b374295f2df78d92517386f77ba30773f15a30ce1422" dependencies = [ "aws-smithy-types", ] @@ -1676,33 +1635,36 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.8.0" +version = "1.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6328865e36c6fd970094ead6b05efd047d3a80ec5fc3be5e743910da9f2ebf8" +checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" dependencies = [ "aws-smithy-async", - "aws-smithy-http 0.62.0", - "aws-smithy-http-client", + "aws-smithy-http 0.60.12", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", + "h2 0.3.26", "http 0.2.12", - "http 1.2.0", "http-body 0.4.6", "http-body 1.0.1", + "httparse", + "hyper 0.14.32", + "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", "pin-utils", + "rustls 0.21.12", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.4" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da37cf5d57011cb1753456518ec76e31691f1f474b73934a284eb2a1c76510f" +checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1717,9 +1679,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.0" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836155caafba616c0ff9b07944324785de2ab016141c3550bd1c07882f8cee8f" +checksum = "c7b8a53819e42f10d0821f56da995e1470b199686a1809168db6ca485665f042" dependencies = [ "base64-simd", "bytes", @@ -1864,9 +1826,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.7.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bech32" @@ -1982,7 +1944,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2271,9 +2233,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -2281,9 +2243,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -2315,14 +2277,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2578,9 +2540,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ "unicode-segmentation", ] @@ -2762,7 +2724,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2773,7 +2735,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2847,7 +2809,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2868,7 +2830,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2878,7 +2840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -2905,9 +2867,10 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "unicode-xid", ] @@ -2917,10 +2880,9 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "convert_case", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "unicode-xid", ] @@ -3008,7 +2970,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -3033,7 +2995,7 @@ checksum = "8dc51d98e636f5e3b0759a39257458b22619cac7e96d932da6eeb052891bb67c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -3165,7 +3127,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -3186,14 +3148,14 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", "env_filter", - "jiff 0.2.4", + "humantime", "log", ] @@ -3271,7 +3233,7 @@ dependencies = [ "ahash", "alloy-dyn-abi", "alloy-primitives", - "indexmap 2.8.0", + "indexmap 2.7.1", ] [[package]] @@ -3323,12 +3285,12 @@ dependencies = [ [[package]] name = "fd-lock" -version = "4.0.4" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +checksum = "c44818c96aec5cadc9dacfb97bbcbcfc19a0de75b218412d56f57fbaab94e439" dependencies = [ "cfg-if", - "rustix 1.0.2", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -3344,9 +3306,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", "subtle", @@ -3515,7 +3477,7 @@ name = "forge-doc" version = "1.0.0" dependencies = [ "alloy-primitives", - "derive_more 2.0.1", + "derive_more 1.0.0", "eyre", "forge-fmt", "foundry-common", @@ -3624,7 +3586,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -4055,7 +4017,7 @@ version = "1.0.0" dependencies = [ "alloy-primitives", "alloy-sol-types", - "derive_more 2.0.1", + "derive_more 1.0.0", "foundry-common-fmt", "foundry-macros", "foundry-test-utils", @@ -4202,7 +4164,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -4362,7 +4324,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -4540,7 +4502,7 @@ checksum = "c57c477b645ee248b173bb1176b52dd528872f12c50375801a58aaf5ae91113f" dependencies = [ "bstr", "itoa", - "jiff 0.1.29", + "jiff", "thiserror 2.0.12", ] @@ -4762,7 +4724,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.8.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -4781,7 +4743,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.2.0", - "indexmap 2.8.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -4919,7 +4881,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -5262,7 +5224,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -5344,7 +5306,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -5372,9 +5334,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "arbitrary", "equivalent", @@ -5462,7 +5424,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -5559,30 +5521,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "jiff" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde", -] - -[[package]] -name = "jiff-static" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "jiff-tzdb" version = "0.1.3" @@ -5912,9 +5850,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.47" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e1a8fe3a4a01f28dab245c474cb7b95ccb4d3d2f17a5419a3d949f474c45e84" +checksum = "85c54109598152e19add1933b223b1913bcd16e27cbd7c2783269fe2f9a6cbdc" dependencies = [ "ammonia", "anyhow", @@ -5995,7 +5933,7 @@ checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6073,7 +6011,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6323,7 +6261,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6365,9 +6303,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.0" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "op-alloy-consensus" @@ -6482,7 +6420,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6565,7 +6503,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6624,7 +6562,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6645,7 +6583,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.8.0", + "indexmap 2.7.1", ] [[package]] @@ -6698,7 +6636,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6747,7 +6685,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6801,11 +6739,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.21" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.8.23", + "zerocopy 0.7.35", ] [[package]] @@ -6847,7 +6785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6922,7 +6860,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -6942,7 +6880,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "version_check", "yansi", ] @@ -6954,7 +6892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d35f4dc9988d1326b065b4def5e950c3ed727aa03e3151b86cc9e2aec6b03f54" dependencies = [ "futures", - "indexmap 2.8.0", + "indexmap 2.7.1", "nix 0.29.0", "tokio", "tracing", @@ -7010,7 +6948,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -7033,7 +6971,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -7096,7 +7034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed1a693391a16317257103ad06a88c6529ac640846021da7c435a06fffdacd7" dependencies = [ "chrono", - "indexmap 2.8.0", + "indexmap 2.7.1", "newtype-uuid", "quick-xml 0.37.2", "strip-ansi-escapes", @@ -7449,9 +7387,9 @@ dependencies = [ [[package]] name = "revm" -version = "19.6.0" +version = "19.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b906766b7ba049b515848952b5ae74f363d456e98de2021048a513e442b4f42" +checksum = "dfc5bef3c95fadf3b6a24a253600348380c169ef285f9780a793bb7090c8990d" dependencies = [ "auto_impl", "cfg-if", @@ -7701,9 +7639,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" dependencies = [ "bitflags 2.9.0", "errno", @@ -7924,7 +7862,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -7963,9 +7901,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.8" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" +checksum = "b07779b9b918cc05650cb30f404d4d7835d26df37c235eded8a6832e2fb82cca" [[package]] name = "sec1" @@ -8085,22 +8023,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8111,7 +8049,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8120,7 +8058,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.7.1", "itoa", "memchr", "ryu", @@ -8155,7 +8093,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8189,7 +8127,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.8.0", + "indexmap 2.7.1", "serde", "serde_derive", "serde_json", @@ -8206,7 +8144,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8241,7 +8179,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8507,7 +8445,7 @@ checksum = "71d07263243b313296eca18f18eda3a190902dc3284bf67ceff29b8b54dac3e6" dependencies = [ "bumpalo", "index_vec", - "indexmap 2.8.0", + "indexmap 2.7.1", "parking_lot", "rayon", "rustc-hash", @@ -8550,7 +8488,7 @@ checksum = "970d7c774741f786d62cab78290e47d845b0b9c0c9d094a1642aced1d7946036" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8722,7 +8660,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8735,7 +8673,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8803,9 +8741,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -8821,7 +8759,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8841,7 +8779,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8860,7 +8798,7 @@ dependencies = [ "fastrand", "getrandom 0.3.1", "once_cell", - "rustix 1.0.2", + "rustix 1.0.1", "windows-sys 0.59.0", ] @@ -8892,7 +8830,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 1.0.2", + "rustix 1.0.1", "windows-sys 0.59.0", ] @@ -8951,7 +8889,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -8962,7 +8900,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -9097,7 +9035,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -9200,7 +9138,7 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", @@ -9222,7 +9160,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", @@ -9373,7 +9311,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -9810,7 +9748,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "wasm-bindgen-shared", ] @@ -9845,7 +9783,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10090,7 +10028,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10101,7 +10039,7 @@ checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10112,7 +10050,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10123,7 +10061,7 @@ checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10500,7 +10438,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "synstructure", ] @@ -10510,6 +10448,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive 0.7.35", ] @@ -10530,7 +10469,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10541,7 +10480,7 @@ checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10561,7 +10500,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", "synstructure", ] @@ -10582,7 +10521,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10604,7 +10543,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.99", ] [[package]] @@ -10619,7 +10558,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap 2.8.0", + "indexmap 2.7.1", "memchr", "thiserror 2.0.12", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index b55f708db90cc..f6cb1e06f6c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -255,7 +255,7 @@ proc-macro2 = "1.0" quote = "1.0" syn = "2.0" async-trait = "0.1" -derive_more = { version = "2.0", features = ["full"] } +derive_more = { version = "1.0", features = ["full"] } thiserror = "2" # bench diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index c51511c423487..c7a90ad2f06b5 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -51,10 +51,6 @@ pub struct StorageArgs { #[arg(value_parser = parse_slot)] slot: Option, - /// The known proxy address. If provided, the storage layout is retrieved from this address. - #[arg(long,value_parser = NameOrAddress::from_str)] - proxy: Option, - /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. @@ -138,11 +134,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 source = if let Some(proxy) = self.proxy { - find_source(client, proxy.resolve(&provider).await?).await? - } else { - find_source(client, address).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") diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 2d1ad75854999..fedcb7d7d2a70 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1295,48 +1295,6 @@ casttest!(storage_layout_complex, |_prj, cmd| { ╰-------------------------------+--------------------------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+---------------------------------╯ -"#]]); -}); - -casttest!(storage_layout_complex_proxy, |_prj, cmd| { - cmd.args([ - "storage", - "--rpc-url", - next_rpc_endpoint(NamedChain::Sepolia).as_str(), - "--block", - "7857852", - "--etherscan-api-key", - next_mainnet_etherscan_api_key().as_str(), - "0xE2588A9CAb7Ea877206E35f615a39f84a64A7A3b", - "--proxy", - "0x29fcb43b46531bca003ddc8fcb67ffe91900c762" - ]) - .assert_success() - .stdout_eq(str![[r#" - -╭----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------╮ -| Name | Type | Slot | Offset | Bytes | Value | Hex Value | Contract | -+============================================================================================================================================================================================================================================================+ -| singleton | address | 0 | 0 | 20 | 239704109775411986678417050956533140837380441954 | 0x00000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c762 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| modules | mapping(address => address) | 1 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| owners | mapping(address => address) | 2 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| ownerCount | uint256 | 3 | 0 | 32 | 1 | 0x0000000000000000000000000000000000000000000000000000000000000001 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| threshold | uint256 | 4 | 0 | 32 | 1 | 0x0000000000000000000000000000000000000000000000000000000000000001 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| nonce | uint256 | 5 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| _deprecatedDomainSeparator | bytes32 | 6 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| signedMessages | mapping(bytes32 => uint256) | 7 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | -|----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------| -| approvedHashes | mapping(address => mapping(bytes32 => uint256)) | 8 | 0 | 32 | 0 | 0x0000000000000000000000000000000000000000000000000000000000000000 | contracts/SafeL2.sol:SafeL2 | -╰----------------------------+-------------------------------------------------+------+--------+-------+--------------------------------------------------+--------------------------------------------------------------------+-----------------------------╯ - - "#]]); }); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 465d3db337590..aaf0658c65ac3 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1953,7 +1953,7 @@ contract SimpleScript is Script { .assert_success() .stdout_eq(str![[r#" {"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"[..]","output":"[..]","gas_used":"{...}","gas_limit":"{...}","status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":"{...}","gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":"{...}","gas_limit":1056940994,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":"{...}","gas_limit":1056940645,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":"{...}","labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} -{"chain":31337,"estimated_gas_price":"{...}","estimated_total_gas_used":"{...}","estimated_amount_required":"{...}","token_symbol":"ETH"} +{"chain":31337,"estimated_gas_price":"{...}","estimated_total_gas_used":"{...}","estimated_amount_required":"{...}"} {"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":"{...}","gas_price":"{...}"} {"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index a58b1058ebc2e..cf6aeb349436b 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,7 +9,6 @@ use crate::{ sequence::get_commit_hash, ScriptArgs, ScriptConfig, ScriptResult, }; -use alloy_chains::NamedChain; use alloy_network::TransactionBuilder; use alloy_primitives::{map::HashMap, utils::format_units, Address, Bytes, TxKind, U256}; use dialoguer::Confirm; @@ -347,12 +346,6 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); - // Get the native token symbol for the chain using NamedChain - let token_symbol = NamedChain::try_from(provider_info.chain) - .unwrap_or_default() - .native_currency_symbol() - .unwrap_or("ETH"); - // 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 { @@ -376,7 +369,7 @@ impl FilledTransactionsState { sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; + sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; sh_println!("\n==========================")?; } else { sh_println!( @@ -386,7 +379,6 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, - "token_symbol": token_symbol, }) )?; } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 3c2b0546f7061..123bda33dbb9d 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1019,10 +1019,7 @@ fn test_redactions() -> snapbox::Redactions { ("[SAVED_SENSITIVE_VALUES]", r"Sensitive values saved to: .*\.json"), ("[ESTIMATED_GAS_PRICE]", r"Estimated gas price:\s*(\d+(\.\d+)?)\s*gwei"), ("[ESTIMATED_TOTAL_GAS_USED]", r"Estimated total gas used for script: \d+"), - ( - "[ESTIMATED_AMOUNT_REQUIRED]", - r"Estimated amount required:\s*(\d+(\.\d+)?)\s*[A-Z]{3}", - ), + ("[ESTIMATED_AMOUNT_REQUIRED]", r"Estimated amount required:\s*(\d+(\.\d+)?)\s*ETH"), ]; for (placeholder, re) in redactions { r.insert(placeholder, Regex::new(re).expect(re)).expect(re); diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index e61d5e1b07f5c..eece0c928064d 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -278,13 +278,8 @@ 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('/'); - - // Verifier is etherscan if explicitly set or if no verifier set (default sourcify) but - // API key passed. - let is_etherscan = verifier_type.is_etherscan() || - (verifier_type.is_sourcify() && etherscan_key.is_some()); - let base_url = if !is_etherscan { - // If verifier is not Etherscan then set base url as api url without /api suffix. + let base_url = if *verifier_type != VerificationProviderType::Etherscan { + // If verifier is not Etherscan then set base url as api url without trialing /api. api_url.strip_prefix("/api").unwrap_or(api_url) } else { base_url.unwrap_or(api_url) diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 01a06332a36ce..07f1e09ea6aa3 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -170,9 +170,8 @@ pub enum VerificationProviderType { impl VerificationProviderType { /// Returns the corresponding `VerificationProvider` for the key pub fn client(&self, key: Option<&str>) -> Result> { - let has_key = key.as_ref().is_some_and(|k| !k.is_empty()); - // 1. If no verifier or `--verifier sourcify` is set and no API key provided, use Sourcify. - if !has_key && self.is_sourcify() { + // 1. If `--verifier sourcify` is set, always use Sourcify. + if matches!(self, Self::Sourcify) { sh_println!( "Attempting to verify on Sourcify. Pass the --etherscan-api-key to verify on Etherscan, \ or use the --verifier flag to verify on another provider." @@ -181,8 +180,8 @@ impl VerificationProviderType { } // 2. If `--verifier etherscan` is explicitly set, enforce the API key requirement. - if self.is_etherscan() { - if !has_key { + if matches!(self, Self::Etherscan) { + if key.as_ref().is_none_or(|key| key.is_empty()) { eyre::bail!("ETHERSCAN_API_KEY must be set to use Etherscan as a verifier") } return Ok(Box::::default()); @@ -195,19 +194,11 @@ impl VerificationProviderType { } // 4. If no `--verifier` is specified but `ETHERSCAN_API_KEY` is set, default to Etherscan. - if has_key { + if key.as_ref().is_some_and(|k| !k.is_empty()) { return Ok(Box::::default()); } // 5. If no valid provider is specified, bail. eyre::bail!("No valid verification provider specified. Pass the --verifier flag to specify a provider or set the ETHERSCAN_API_KEY environment variable to use Etherscan as a verifier.") } - - pub fn is_sourcify(&self) -> bool { - matches!(self, Self::Sourcify) - } - - pub fn is_etherscan(&self) -> bool { - matches!(self, Self::Etherscan) - } } diff --git a/deny.toml b/deny.toml index 33c5a4233e55d..ac92742909a18 100644 --- a/deny.toml +++ b/deny.toml @@ -11,8 +11,6 @@ ignore = [ "RUSTSEC-2024-0436", # https://rustsec.org/advisories/RUSTSEC-2024-0437 protobuf! Crash due to uncontrolled recursion in protobuf crate. "RUSTSEC-2024-0437", - # humantime is unmaintained - "RUSTSEC-2025-0014", ] # This section is considered when running `cargo deny check bans`. @@ -49,6 +47,7 @@ allow = [ "BSD-3-Clause", "ISC", "Unicode-3.0", + "OpenSSL", "Unlicense", "WTFPL", "BSL-1.0", From 90896e02a97e4846707f923d85f0e6d20e113792 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 27 Apr 2025 05:39:27 +0700 Subject: [PATCH 027/374] Add .circleci/config.yml --- .circleci/config.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..62291703e26a7 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello \ No newline at end of file From 9b9f087ed2f71981b133d29e06fe3f8e95397ff0 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:23:52 +0700 Subject: [PATCH 028/374] Updated config.yml --- .circleci/config.yml | 120 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 62291703e26a7..5121ad4351f4b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,31 +1,109 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + build: docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current + - image: ubuntu:23.04 + + - image: mongo:6.0.14 + command: [mongod, --smallfiles] + + - image: postgres:14.12 + # some containers require setting environment variables + environment: + POSTGRES_USER: user + + - image: redis@sha256:54057dd7e125ca41afe526a877e8bd35ec2cdd33b9217e022ed37bdcf7d09673 + + - image: rabbitmq:3.12.12 + + environment: + TEST_REPORTS: /tmp/test-reports + + working_directory: ~/my-project - # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps steps: - # Checkout the code as the first step. - checkout + + - run: + command: echo 127.0.0.1 devhost | sudo tee -a /etc/hosts + + # Create Postgres users and database + # Note the YAML heredoc '|' for nicer formatting + - run: | + sudo -u root createuser -h localhost --superuser ubuntu && + sudo createdb -h localhost test_db + + - restore_cache: + keys: + - v1-my-project-{{ checksum "project.clj" }} + - v1-my-project- + + - run: + environment: + SSH_TARGET: "localhost" + TEST_ENV: "linux" + command: | + set -xu + mkdir -p ${TEST_REPORTS} + run-tests.sh + cp out/tests/*.xml ${TEST_REPORTS} + + - run: | + set -xu + mkdir -p /tmp/artifacts + create_jars.sh << pipeline.number >> + cp *.jar /tmp/artifacts + + - save_cache: + key: v1-my-project-{{ checksum "project.clj" }} + paths: + - ~/.m2 + + # Save artifacts + - store_artifacts: + path: /tmp/artifacts + destination: build + + # Upload test results + - store_test_results: + path: /tmp/test-reports + + deploy-stage: + docker: + - image: ubuntu:23.04 + working_directory: /tmp/my-project + steps: + - run: + name: Deploy if tests pass and branch is Staging + command: ansible-playbook site.yml -i staging + + deploy-prod: + docker: + - image: ubuntu:23.04 + working_directory: /tmp/my-project + steps: - run: - name: "Say hello" - command: "echo Hello, World!" + name: Deploy if tests pass and branch is Main + command: ansible-playbook site.yml -i production -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. + build-deploy: jobs: - - say-hello \ No newline at end of file + - build: + filters: + branches: + ignore: + - develop + - /feature-.*/ + - deploy-stage: + requires: + - build + filters: + branches: + only: staging + - deploy-prod: + requires: + - build + filters: + branches: + only: main \ No newline at end of file From ad4415ad276513863df50b5e4e8bace30bfc05f0 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:28:14 +0700 Subject: [PATCH 029/374] Updated config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5121ad4351f4b..29513b62d13e3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: - image: ubuntu:23.04 - image: mongo:6.0.14 - command: [mongod, --smallfiles] + command: [mongod --dbpath /path/to/data] - image: postgres:14.12 # some containers require setting environment variables From 37b29ad897addfcbd4f01c01bec55283a27bd947 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 27 Apr 2025 09:31:08 +0700 Subject: [PATCH 030/374] Updated config.yml --- .circleci/config.yml | 120 ++++++++----------------------------------- 1 file changed, 21 insertions(+), 99 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 29513b62d13e3..62291703e26a7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,109 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs jobs: - build: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job docker: - - image: ubuntu:23.04 - - - image: mongo:6.0.14 - command: [mongod --dbpath /path/to/data] - - - image: postgres:14.12 - # some containers require setting environment variables - environment: - POSTGRES_USER: user - - - image: redis@sha256:54057dd7e125ca41afe526a877e8bd35ec2cdd33b9217e022ed37bdcf7d09673 - - - image: rabbitmq:3.12.12 - - environment: - TEST_REPORTS: /tmp/test-reports - - working_directory: ~/my-project + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps steps: + # Checkout the code as the first step. - checkout - - - run: - command: echo 127.0.0.1 devhost | sudo tee -a /etc/hosts - - # Create Postgres users and database - # Note the YAML heredoc '|' for nicer formatting - - run: | - sudo -u root createuser -h localhost --superuser ubuntu && - sudo createdb -h localhost test_db - - - restore_cache: - keys: - - v1-my-project-{{ checksum "project.clj" }} - - v1-my-project- - - - run: - environment: - SSH_TARGET: "localhost" - TEST_ENV: "linux" - command: | - set -xu - mkdir -p ${TEST_REPORTS} - run-tests.sh - cp out/tests/*.xml ${TEST_REPORTS} - - - run: | - set -xu - mkdir -p /tmp/artifacts - create_jars.sh << pipeline.number >> - cp *.jar /tmp/artifacts - - - save_cache: - key: v1-my-project-{{ checksum "project.clj" }} - paths: - - ~/.m2 - - # Save artifacts - - store_artifacts: - path: /tmp/artifacts - destination: build - - # Upload test results - - store_test_results: - path: /tmp/test-reports - - deploy-stage: - docker: - - image: ubuntu:23.04 - working_directory: /tmp/my-project - steps: - - run: - name: Deploy if tests pass and branch is Staging - command: ansible-playbook site.yml -i staging - - deploy-prod: - docker: - - image: ubuntu:23.04 - working_directory: /tmp/my-project - steps: - run: - name: Deploy if tests pass and branch is Main - command: ansible-playbook site.yml -i production + name: "Say hello" + command: "echo Hello, World!" +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows workflows: - build-deploy: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. jobs: - - build: - filters: - branches: - ignore: - - develop - - /feature-.*/ - - deploy-stage: - requires: - - build - filters: - branches: - only: staging - - deploy-prod: - requires: - - build - filters: - branches: - only: main \ No newline at end of file + - say-hello \ No newline at end of file From 975ca47b8c27518fe24014a3455a46ff950b1e94 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 9 May 2025 22:38:42 +0700 Subject: [PATCH 031/374] Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/jekyll.yml | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/jekyll.yml diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml new file mode 100644 index 0000000000000..501686bcc9563 --- /dev/null +++ b/.github/workflows/jekyll.yml @@ -0,0 +1,65 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Ruby + # https://github.com/ruby/setup-ruby/releases/tag/v1.207.0 + uses: ruby/setup-ruby@4a9ddd6f338a97768b8006bf671dfbad383215f4 + with: + ruby-version: '3.1' # Not needed with a .ruby-version file + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + cache-version: 0 # Increment this number if you need to re-download cached gems + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + - name: Build with Jekyll + # Outputs to the './_site' directory by default + run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" + env: + JEKYLL_ENV: production + - name: Upload artifact + # Automatically uploads an artifact from the './_site' directory by default + uses: actions/upload-pages-artifact@v3 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 3f90e930ddf16aa53b2fe971d7afbf55e982be7f Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 9 May 2025 23:02:24 +0700 Subject: [PATCH 032/374] Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000000000..793d8e0e39e39 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) From 79528db656d98ec2c59e33f37e2e3e5980e21189 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 9 May 2025 23:18:47 +0700 Subject: [PATCH 033/374] Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 793d8e0e39e39..fe65b8b969f4d 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [ "master" ] +permissions: + contents: read + jobs: build: From 4d200a767a10e925ceb4185fa4765d102984173e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 9 May 2025 23:19:20 +0700 Subject: [PATCH 034/374] Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index fe65b8b969f4d..b37794b4a7f85 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -17,5 +17,5 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) +- name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From a585df630d075684256e8901eff9af4ef93006d3 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 10 May 2025 04:02:45 +0700 Subject: [PATCH 035/374] Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index b37794b4a7f85..e0c9c518e8748 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -17,5 +17,5 @@ jobs: steps: - uses: actions/checkout@v4 -- name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From 3fb94af1686be281d6ea3e86fb6977bdd7e27bfd Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 27 Apr 2025 06:45:56 +0700 Subject: [PATCH 036/374] Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. --- crates/forge/tests/cli/test_optimizer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index 6c286c52793ec..944abed57ff30 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1347,7 +1347,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.update_config(|config| { config.dynamic_test_linking = true; From 6949f7f91c979f391192d9aa6877d31257b3d518 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 25 May 2025 06:07:02 +0700 Subject: [PATCH 037/374] Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/jekyll.yml | 65 ------------------------------------ 1 file changed, 65 deletions(-) delete mode 100644 .github/workflows/jekyll.yml diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml deleted file mode 100644 index 501686bcc9563..0000000000000 --- a/.github/workflows/jekyll.yml +++ /dev/null @@ -1,65 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: Deploy Jekyll site to Pages - -on: - # Runs on pushes targeting the default branch - push: - branches: ["master"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Ruby - # https://github.com/ruby/setup-ruby/releases/tag/v1.207.0 - uses: ruby/setup-ruby@4a9ddd6f338a97768b8006bf671dfbad383215f4 - with: - ruby-version: '3.1' # Not needed with a .ruby-version file - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - cache-version: 0 # Increment this number if you need to re-download cached gems - - name: Setup Pages - id: pages - uses: actions/configure-pages@v5 - - name: Build with Jekyll - # Outputs to the './_site' directory by default - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" - env: - JEKYLL_ENV: production - - name: Upload artifact - # Automatically uploads an artifact from the './_site' directory by default - uses: actions/upload-pages-artifact@v3 - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 From 868fd5c064c707621925567db21692a766861ec6 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 25 May 2025 23:08:28 +0700 Subject: [PATCH 038/374] Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bf0543fb7482..1b18d4ad1dd2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -295,6 +295,9 @@ jobs: runs-on: ubuntu-latest needs: [prepare, release-docker, release, cleanup] if: failure() + permissions: + issues: write + contents: read steps: - uses: actions/checkout@v4 - uses: JasonEtco/create-an-issue@v2 From 7aa139ecd096ab7ebd551e5196b497a3f354c682 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 25 May 2025 23:26:49 +0700 Subject: [PATCH 039/374] Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0486f2237f10b..d3fe0f910e5b4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,9 +38,10 @@ jobs: uses: peaceiris/actions-gh-pages@v3 if: github.event_name == 'push' && github.ref == 'refs/heads/master' with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: target/doc - force_orphan: true + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: target/doc + force_orphan: true + commit_message: "Deploy documentation [skip ci]" doctest: runs-on: ubuntu-latest From f0897aa843fdcc48871abde95b9597cdffe263d1 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 25 May 2025 23:26:49 +0700 Subject: [PATCH 040/374] Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0486f2237f10b..d3fe0f910e5b4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,9 +38,10 @@ jobs: uses: peaceiris/actions-gh-pages@v3 if: github.event_name == 'push' && github.ref == 'refs/heads/master' with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: target/doc - force_orphan: true + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: target/doc + force_orphan: true + commit_message: "Deploy documentation [skip ci]" doctest: runs-on: ubuntu-latest From 7bdfb1b6ad2c5221781bed53f4a470828c5570cc Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 25 May 2025 23:08:28 +0700 Subject: [PATCH 041/374] Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bf0543fb7482..1b18d4ad1dd2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -295,6 +295,9 @@ jobs: runs-on: ubuntu-latest needs: [prepare, release-docker, release, cleanup] if: failure() + permissions: + issues: write + contents: read steps: - uses: actions/checkout@v4 - uses: JasonEtco/create-an-issue@v2 From 3be9159c06ac637f76a65ef1e9e7299b7257aa8f Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 26 May 2025 01:43:49 +0700 Subject: [PATCH 042/374] Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d3fe0f910e5b4..f4d5d2d361f5a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,15 @@ jobs: publish_dir: target/doc force_orphan: true commit_message: "Deploy documentation [skip ci]" - + - name: Push changes + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + to pass before a push is allowed. + git commit -S -m "Auto-update gh-pages" + git push origin --force gh-pages + +### Solution **Review Repository Rules:** + doctest: runs-on: ubuntu-latest timeout-minutes: 30 From 428d049479cdd617336544f38a0646b60c5f8c68 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 26 May 2025 12:46:14 +0200 Subject: [PATCH 043/374] chore(deps): bump revm to 24.0.0 (#10601) --- Cargo.lock | 208 +++++++++---------- Cargo.toml | 68 +++--- crates/anvil/core/src/eth/transaction/mod.rs | 4 +- crates/anvil/src/eth/backend/mem/mod.rs | 4 +- crates/anvil/tests/it/optimism.rs | 2 +- crates/cheatcodes/src/inspector.rs | 9 +- crates/debugger/src/tui/draw.rs | 1 - 7 files changed, 139 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e972d3906b5dc..a8c4a15e7bc6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,9 +58,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5848366a4f08dca1caca0a6151294a4799fe2e59ba25df100491d92e0b921b1c" +checksum = "517e5acbd38b6d4c59da380e8bbadc6d365bf001903ce46cf5521c53c647e07b" dependencies = [ "alloy-primitives", "num_enum", @@ -74,10 +74,10 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7329eb72d95576dfb8813175bcf671198fb24266b0b3e520052a513e30c284df" dependencies = [ - "alloy-eips 1.0.7", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-trie", "auto_impl", "c-kzg", @@ -99,10 +99,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31b286aeef04a32720c10defd21c3aa6c626154ac442b55f6d472caeb1c6741" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 1.0.7", + "alloy-serde", "serde", ] @@ -185,26 +185,6 @@ dependencies = [ "thiserror 2.0.12", ] -[[package]] -name = "alloy-eips" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609515c1955b33af3d78d26357540f68c5551a90ef58fd53def04f2aa074ec43" -dependencies = [ - "alloy-eip2124", - "alloy-eip2930", - "alloy-eip7702", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.14.0", - "auto_impl", - "c-kzg", - "derive_more 2.0.1", - "either", - "serde", - "sha2 0.10.9", -] - [[package]] name = "alloy-eips" version = "1.0.7" @@ -216,7 +196,7 @@ dependencies = [ "alloy-eip7702", "alloy-primitives", "alloy-rlp", - "alloy-serde 1.0.7", + "alloy-serde", "auto_impl", "c-kzg", "derive_more 2.0.1", @@ -241,12 +221,12 @@ dependencies = [ [[package]] name = "alloy-evm" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c5b34c78c42525917b236e4135b1951ca183ede4004b594db0effee8bed169" +checksum = "394b09cf3a32773eedf11828987f9c72dfa74545040be0422e3f5f09a2a3fab9" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-hardforks", "alloy-primitives", "alloy-sol-types", @@ -264,9 +244,9 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b81b2dfd278d58af8bfde8753fa4685407ba8fbad8bc88a2bb0e065eed48478" dependencies = [ - "alloy-eips 1.0.7", + "alloy-eips", "alloy-primitives", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-trie", "serde", ] @@ -318,13 +298,13 @@ checksum = "f0ed07e76fbc72790a911ea100cdfbe85b1f12a097c91b948042e854959d140e" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-any", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-signer", "alloy-sol-types", "async-trait", @@ -343,20 +323,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f05aa52713c376f797b3c7077708585f22a5c3053a7b1b2b355ea98edeb2052d" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-primitives", - "alloy-serde 1.0.7", + "alloy-serde", "serde", ] [[package]] name = "alloy-op-evm" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4fda8b1920a38a5adc607d6ff7be1e8991e16ffcf97bb12765644b87331c598" +checksum = "9f32538cc243ec5d4603da9845cc2f5254c6a3a78e82475beb1a2a1de6c0d36c" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -415,7 +395,7 @@ checksum = "05a3f7a59c276c6e410267e77a166f9297dbe74e4605f1abf625e29d85c53144" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-json-rpc", "alloy-network", "alloy-network-primitives", @@ -535,7 +515,7 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 1.0.7", + "alloy-serde", "serde", ] @@ -547,7 +527,7 @@ checksum = "51e15bd6456742d6dcadacf3cd238a90a8a7aa9f00bc7cc641ae272f5d3f5d4f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", "serde", ] @@ -559,7 +539,7 @@ checksum = "67971a228100ac65bd86e90439028853435f21796330ef08f00a70a918a84126" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", ] [[package]] @@ -579,10 +559,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bcf49fe91b3d621440dcc5bb067afaeba5ca4b07f59e42fb7af42944146a8c0" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 1.0.7", + "alloy-serde", "derive_more 2.0.1", "jsonwebtoken", "rand 0.8.5", @@ -598,11 +578,11 @@ checksum = "89d9b4293dfd4721781d33ee40de060376932d4a55d421cf6618ad66ff97cc52" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-sol-types", "itertools 0.14.0", "serde", @@ -618,7 +598,7 @@ checksum = "7f68f020452c0d570b4eee22d4ffda9e4eda68ebcf67e1199d6dff48097f442b" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", "serde", "serde_json", "thiserror 2.0.12", @@ -632,21 +612,10 @@ checksum = "62a82f15f296c2c83c55519d21ca07801fb58b118878b0f4777250968e49f4fe" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", "serde", ] -[[package]] -name = "alloy-serde" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4dba6ff08916bc0a9cbba121ce21f67c0b554c39cf174bc7b9df6c651bd3c3b" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", -] - [[package]] name = "alloy-serde" version = "1.0.7" @@ -1051,7 +1020,7 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-evm", "alloy-genesis", "alloy-network", @@ -1061,7 +1030,7 @@ dependencies = [ "alloy-pubsub", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -1113,12 +1082,12 @@ version = "1.2.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "bytes", "foundry-common", "foundry-evm", @@ -2389,7 +2358,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -3814,7 +3783,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -3947,13 +3916,13 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-signer", "clap", "dialoguer", @@ -4145,7 +4114,7 @@ version = "1.2.1" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-ens", "alloy-json-abi", "alloy-primitives", @@ -4191,7 +4160,7 @@ version = "1.2.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -4200,7 +4169,7 @@ dependencies = [ "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -4249,7 +4218,7 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-rpc-types", - "alloy-serde 1.0.7", + "alloy-serde", "chrono", "foundry-macros", "revm", @@ -4570,9 +4539,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99831be91edd8a025fa60443b5404ad407708bc337d2e20440d498b5806fab8" +checksum = "b02fb598e4a8ae7b7af7c256081a419b071eacf5e03537806b339b3151409403" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -6169,9 +6138,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "newtype-uuid" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3224f0e8be7c2a1ebc77ef9c3eecb90f55c6594399ee825de964526b3c9056" +checksum = "a8ba303c7a8f8fdee1fe1513cfd918f50f1c69bf65c91b39217bfc2b2af5c081" dependencies = [ "uuid 1.17.0", ] @@ -6487,17 +6456,17 @@ dependencies = [ [[package]] name = "op-alloy-consensus" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f318b09e24148f07392c5e011bae047a0043851f9041145df5f3b01e4fedd1e" +checksum = "bb35d16e5420e43e400a235783e3d18b6ba564917139b668b48e9ac42cb3d35a" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", "derive_more 2.0.1", "serde", "thiserror 2.0.12", @@ -6511,16 +6480,16 @@ checksum = "4ef71f23a8caf6f2a2d5cafbdc44956d44e6014dcb9aa58abf7e4e6481c6ec34" [[package]] name = "op-alloy-rpc-types" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15ede8322c10c21249de4fced204e2af4978972e715afee34b6fe684d73880cf" +checksum = "7534a0ec6b8409edc511acbe77abe7805aa63129b98e9a915bb4eb8555eaa6ff" dependencies = [ "alloy-consensus", - "alloy-eips 1.0.7", + "alloy-eips", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 1.0.7", + "alloy-serde", "derive_more 2.0.1", "op-alloy-consensus", "serde", @@ -6530,8 +6499,9 @@ dependencies = [ [[package]] name = "op-revm" -version = "4.0.2" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47296d449fbe2d5cc74ab6e1213dee88cae3e2fd238343bec605c3c687bbcfab" dependencies = [ "auto_impl", "once_cell", @@ -7585,8 +7555,9 @@ dependencies = [ [[package]] name = "revm" -version = "23.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d3ae9d1b08303eb5150dcf820a29e14235cf3f24f6c09024458a4dcbffe6695" dependencies = [ "revm-bytecode", "revm-context", @@ -7603,8 +7574,9 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91f9b90b3bab18942252de2d970ee8559794c49ca7452b2cc1774456040f8fb" dependencies = [ "bitvec", "once_cell", @@ -7615,8 +7587,9 @@ dependencies = [ [[package]] name = "revm-context" -version = "4.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b181214eb2bbb76ee9d6195acba19857d991d2cdb9a65b7cb6939c30250a3966" dependencies = [ "cfg-if", "derive-where", @@ -7630,8 +7603,9 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "4.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b844f48a411e62c7dde0f757bf5cce49c85b86d6fc1d3b2722c07f2bec4c3ce" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -7645,10 +7619,11 @@ dependencies = [ [[package]] name = "revm-database" -version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad3fbe34f6bb00a9c3155723b3718b9cb9f17066ba38f9eb101b678cd3626775" dependencies = [ - "alloy-eips 0.14.0", + "alloy-eips", "revm-bytecode", "revm-database-interface", "revm-primitives", @@ -7658,8 +7633,9 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8acd36784a6d95d5b9e1b7be3ce014f1e759abb59df1fa08396b30f71adc2a" dependencies = [ "auto_impl", "revm-primitives", @@ -7669,8 +7645,9 @@ dependencies = [ [[package]] name = "revm-handler" -version = "4.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9204e3ac1a8edb850cc441a6a1d0f2251c0089e5fffdaba11566429e6c64e" dependencies = [ "auto_impl", "revm-bytecode", @@ -7686,8 +7663,9 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "4.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae4881eeae6ff35417c8569bc7cc03b6c0969869ee2c9b3945a39b4f9fa58bc5" dependencies = [ "auto_impl", "revm-context", @@ -7702,9 +7680,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.22.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f847f5e88a09ac84b36529fbe2fee80b3d8bbf91e9a7ae3ea856c4125d0d232" +checksum = "4b50ef375dbacefecfdacf8f02afc31df98acc5d8859a6f2b24d121ff2a740a8" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -7720,8 +7698,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "19.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5ee65e57375c6639b0f50555e92a4f1b2434349dd32f52e2176f5c711171697" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -7731,8 +7710,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "20.1.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9311e735123d8d53a02af2aa81877bba185be7c141be7f931bb3d2f3af449c" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -7755,8 +7735,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "19.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "19.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18ea2ea0134568ee1e14281ce52f60e2710d42be316888d464c53e37ff184fd8" dependencies = [ "alloy-primitives", "num_enum", @@ -7765,8 +7746,9 @@ dependencies = [ [[package]] name = "revm-state" -version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=b5808253#b580825320708f0c47d3734ceab03d90c0b11ba1" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0040c61c30319254b34507383ba33d85f92949933adf6525a2cede05d165e1fa" dependencies = [ "bitflags 2.9.1", "revm-bytecode", diff --git a/Cargo.toml b/Cargo.toml index 76f51d5337151..8686f046c2c14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -200,7 +200,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.17.0", default-features = false } foundry-compilers = { version = "0.16.1", default-features = false } -foundry-fork-db = "0.14" +foundry-fork-db = "0.15" solang-parser = { version = "=0.3.8", package = "foundry-solang-parser" } solar-ast = { version = "=0.1.3", default-features = false } solar-parse = { version = "=0.1.3", default-features = false } @@ -208,28 +208,28 @@ solar-interface = { version = "=0.1.3", default-features = false } solar-sema = { version = "=0.1.3", default-features = false } ## alloy -alloy-consensus = { version = "1.0.5", default-features = false } -alloy-contract = { version = "1.0.5", default-features = false } -alloy-eips = { version = "1.0.5", default-features = false } -alloy-ens = { version = "1.0.5", default-features = false } -alloy-genesis = { version = "1.0.5", default-features = false } -alloy-json-rpc = { version = "1.0.5", default-features = false } -alloy-network = { version = "1.0.5", default-features = false } -alloy-provider = { version = "1.0.5", default-features = false } -alloy-pubsub = { version = "1.0.5", default-features = false } -alloy-rpc-client = { version = "1.0.5", default-features = false } -alloy-rpc-types = { version = "1.0.5", default-features = true } -alloy-serde = { version = "1.0.5", default-features = false } -alloy-signer = { version = "1.0.5", default-features = false } -alloy-signer-aws = { version = "1.0.5", default-features = false } -alloy-signer-gcp = { version = "1.0.5", default-features = false } -alloy-signer-ledger = { version = "1.0.5", default-features = false } -alloy-signer-local = { version = "1.0.5", default-features = false } -alloy-signer-trezor = { version = "1.0.5", default-features = false } -alloy-transport = { version = "1.0.5", default-features = false } -alloy-transport-http = { version = "1.0.5", default-features = false } -alloy-transport-ipc = { version = "1.0.5", default-features = false } -alloy-transport-ws = { version = "1.0.5", default-features = false } +alloy-consensus = { version = "1.0.7", default-features = false } +alloy-contract = { version = "1.0.7", default-features = false } +alloy-eips = { version = "1.0.7", default-features = false } +alloy-ens = { version = "1.0.7", default-features = false } +alloy-genesis = { version = "1.0.7", default-features = false } +alloy-json-rpc = { version = "1.0.7", default-features = false } +alloy-network = { version = "1.0.7", default-features = false } +alloy-provider = { version = "1.0.7", default-features = false } +alloy-pubsub = { version = "1.0.7", default-features = false } +alloy-rpc-client = { version = "1.0.7", default-features = false } +alloy-rpc-types = { version = "1.0.7", default-features = true } +alloy-serde = { version = "1.0.7", default-features = false } +alloy-signer = { version = "1.0.7", default-features = false } +alloy-signer-aws = { version = "1.0.7", default-features = false } +alloy-signer-gcp = { version = "1.0.7", default-features = false } +alloy-signer-ledger = { version = "1.0.7", default-features = false } +alloy-signer-local = { version = "1.0.7", default-features = false } +alloy-signer-trezor = { version = "1.0.7", default-features = false } +alloy-transport = { version = "1.0.7", default-features = false } +alloy-transport-http = { version = "1.0.7", default-features = false } +alloy-transport-ipc = { version = "1.0.7", default-features = false } +alloy-transport-ws = { version = "1.0.7", default-features = false } ## alloy-core alloy-dyn-abi = "1.0" @@ -249,18 +249,18 @@ alloy-rlp = "0.3" alloy-trie = "0.8.1" ## op-alloy -op-alloy-consensus = "0.16.0" -op-alloy-rpc-types = "0.16.0" +op-alloy-consensus = "0.17.1" +op-alloy-rpc-types = "0.17.1" op-alloy-flz = "0.13.0" ## revm -revm = { version = "23.1.0", default-features = false } -revm-inspectors = { version = "0.22.3", features = ["serde"] } -op-revm = { version = "4.0.2", default-features = false } +revm = { version = "24.0.0", default-features = false } +revm-inspectors = { version = "0.23.0", features = ["serde"] } +op-revm = { version = "5.0.0", default-features = false } ## alloy-evm -alloy-evm = "0.9.1" -alloy-op-evm = "0.9.1" +alloy-evm = "0.10.0" +alloy-op-evm = "0.10.0" ## cli anstream = "0.6" @@ -390,12 +390,12 @@ zip-extract = "=0.2.1" # alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } ## alloy-evm -# alloy-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "95f6a8a" } -# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "95f6a8a" } +# alloy-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "8076e12" } +# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "8076e12" } ## revm -revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" } -op-revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" } +# revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" } +# op-revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" } # revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors.git", rev = "a625c04" } ## foundry diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index f6122b2893207..ae6a7262565ca 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -60,7 +60,7 @@ pub fn transaction_request_to_typed( from: from.unwrap_or_default(), source_hash: other.get_deserialized::("sourceHash")?.ok()?, to: to.unwrap_or_default(), - mint: Some(mint), + mint, value: value.unwrap_or_default(), gas_limit: gas.unwrap_or_default(), is_system_transaction: other.get_deserialized::("isSystemTx")?.ok()?, @@ -574,7 +574,7 @@ impl PendingTransaction { let deposit = DepositTransactionParts { source_hash: *source_hash, - mint: *mint, + mint: Some(*mint), is_system_transaction: *is_system_transaction, }; diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index a7b02ff39c059..321ed41ea3925 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -3178,8 +3178,8 @@ impl TransactionValidator for Backend { // 1. no gas cost check required since already have prepaid gas from L1 // 2. increment account balance by deposited amount before checking for sufficient // funds `tx.value <= existing account value + deposited value` - if value > account.balance + U256::from(deposit_tx.mint.unwrap_or_default()) { - warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint.unwrap_or_default()), value, *pending.sender()); + if value > account.balance + U256::from(deposit_tx.mint) { + warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender()); return Err(InvalidTransactionError::InsufficientFunds); } } diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index e8bfe4d6560a5..f871cca724a97 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -200,7 +200,7 @@ async fn test_deposit_tx_checks_sufficient_funds_after_applying_deposited_value( source_hash: b256!("0x0000000000000000000000000000000000000000000000000000000000000000"), from: sender, to: TxKind::Call(recipient), - mint: Some(send_value), + mint: send_value, value: U256::from(send_value), gas_limit: 21_000, is_system_transaction: false, diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 9677806893d3f..4d9b5cd5ef5ed 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -54,7 +54,7 @@ use revm::{ context_interface::{transaction::SignedAuthorization, CreateScheme}, handler::FrameResult, interpreter::{ - interpreter_types::{Jumps, LoopControl, MemoryTr}, + interpreter_types::{Jumps, MemoryTr}, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, @@ -1845,9 +1845,10 @@ impl Cheatcodes { if let Some(paused_gas) = self.gas_metering.paused_frames.last() { // Keep gas constant if paused. // Make sure we record the memory changes so that memory expansion is not paused. - let memory = interpreter.control.gas.memory; + let memory = *interpreter.control.gas.memory(); interpreter.control.gas = *paused_gas; - interpreter.control.gas.memory = memory; + interpreter.control.gas.memory_mut().words_num = memory.words_num; + interpreter.control.gas.memory_mut().expansion_cost = memory.expansion_cost; } else { // Record frame paused gas. self.gas_metering.paused_frames.push(interpreter.control.gas); @@ -1889,7 +1890,7 @@ impl Cheatcodes { #[cold] fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) { - interpreter.control.gas = Gas::new(interpreter.control.gas().limit()); + interpreter.control.gas = Gas::new(interpreter.control.gas.limit()); self.gas_metering.reset = false; } diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index a918bc89ea733..580d6f958ac44 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -199,7 +199,6 @@ impl TUIContext<'_> { CallKind::CallCode => "Contract callcode", CallKind::DelegateCall => "Contract delegatecall", CallKind::AuthCall => "Contract authcall", - CallKind::EOFCreate => "EOF contract creation", }; let title = format!( "{} {} ", From 7ff1d3b2d4ae18d07495a85b1626be7cdf291a5e Mon Sep 17 00:00:00 2001 From: pistomat Date: Mon, 26 May 2025 16:26:49 +0200 Subject: [PATCH 044/374] feat: implement add_balance endpoint (#10636) --- crates/anvil/core/src/eth/mod.rs | 14 +++++++++++++- crates/anvil/src/eth/api.rs | 13 +++++++++++++ crates/anvil/tests/it/fork.rs | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index df07f62ee730c..ac7db74892003 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -361,9 +361,21 @@ pub enum EthRequest { SetRpcUrl(String), /// Modifies the balance of an account. - #[serde(rename = "anvil_setBalance", alias = "hardhat_setBalance")] + #[serde( + rename = "anvil_setBalance", + alias = "hardhat_setBalance", + alias = "tenderly_setBalance" + )] SetBalance(Address, #[serde(deserialize_with = "deserialize_number")] U256), + /// Increases the balance of an account. + #[serde( + rename = "anvil_addBalance", + alias = "hardhat_addBalance", + alias = "tenderly_addBalance" + )] + AddBalance(Address, #[serde(deserialize_with = "deserialize_number")] U256), + /// Modifies the ERC20 balance of an account. #[serde( rename = "anvil_dealERC20", diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 243ae88440e8d..ec6fbeb4227a1 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -356,6 +356,9 @@ impl EthApi { EthRequest::SetBalance(addr, val) => { self.anvil_set_balance(addr, val).await.to_rpc_result() } + EthRequest::AddBalance(addr, val) => { + self.anvil_add_balance(addr, val).await.to_rpc_result() + } EthRequest::DealERC20(addr, token_addr, val) => { self.anvil_deal_erc20(addr, token_addr, val).await.to_rpc_result() } @@ -1856,6 +1859,16 @@ impl EthApi { Ok(()) } + /// Increases the balance of an account. + /// + /// Handler for RPC call: `anvil_addBalance` + pub async fn anvil_add_balance(&self, address: Address, balance: U256) -> Result<()> { + node_info!("anvil_addBalance"); + let current_balance = self.backend.get_balance(address, None).await?; + self.backend.set_balance(address, current_balance + balance).await?; + Ok(()) + } + /// Deals ERC20 tokens to a address /// /// Handler for RPC call: `anvil_dealERC20` diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 54bae673a6bd0..e4e1cda1a31d3 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1490,6 +1490,22 @@ async fn test_set_erc20_balance() { assert_eq!(new_balance, value); } +#[tokio::test(flavor = "multi_thread")] +async fn test_add_balance() { + let config: NodeConfig = fork_config(); + let address = config.genesis_accounts[0].address(); + let (api, _handle) = spawn(config).await; + + let start_balance = U256::from(100_000_u64); + api.anvil_set_balance(address, start_balance).await.unwrap(); + + let balance_increase = U256::from(50_000_u64); + api.anvil_add_balance(address, balance_increase).await.unwrap(); + + let new_balance = api.balance(address, None).await.unwrap(); + assert_eq!(new_balance, start_balance + balance_increase); +} + #[tokio::test(flavor = "multi_thread")] async fn test_reset_updates_cache_path_when_rpc_url_not_provided() { let config: NodeConfig = fork_config(); From 15d07f0431bf217aa292e3ffe3dbffd4a48dae48 Mon Sep 17 00:00:00 2001 From: zark <77061323+zarkk01@users.noreply.github.com> Date: Mon, 26 May 2025 23:21:59 +0300 Subject: [PATCH 045/374] fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- Cargo.lock | 1 + crates/sol-macro-gen/Cargo.toml | 2 ++ crates/sol-macro-gen/src/sol_macro_gen.rs | 8 +++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8c4a15e7bc6d..c05a8b99f5d05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3978,6 +3978,7 @@ dependencies = [ "alloy-sol-macro-input", "eyre", "foundry-common", + "heck", "prettyplease", "proc-macro2", "quote", diff --git a/crates/sol-macro-gen/Cargo.toml b/crates/sol-macro-gen/Cargo.toml index 79b47880f33f9..b983dd6a762b8 100644 --- a/crates/sol-macro-gen/Cargo.toml +++ b/crates/sol-macro-gen/Cargo.toml @@ -26,3 +26,5 @@ prettyplease.workspace = true serde_json.workspace = true eyre.workspace = true + +heck.workspace = true diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs index 657dd4cbb8458..1e40d51169695 100644 --- a/crates/sol-macro-gen/src/sol_macro_gen.rs +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -21,6 +21,8 @@ use std::{ str::FromStr, }; +use heck::ToSnakeCase; + pub struct SolMacroGen { pub path: PathBuf, pub name: String, @@ -209,7 +211,7 @@ edition = "2021" for instance in &self.instances { let contents = instance.expansion.as_ref().unwrap(); - let name = instance.name.to_lowercase(); + let name = instance.name.to_snake_case(); let path = src.join(format!("{name}.rs")); let file = syn::parse2(contents.clone()) .wrap_err_with(|| parse_error(&format!("{}:{}", path.display(), name)))?; @@ -266,7 +268,7 @@ edition = "2021" .to_string(); for instance in &self.instances { - let name = instance.name.to_lowercase(); + let name = instance.name.to_snake_case(); if !single_file { // Module write_mod_name(&mut mod_contents, &name)?; @@ -328,7 +330,7 @@ edition = "2021" )?; if !single_file { for instance in &self.instances { - let name = instance.name.to_lowercase(); + let name = instance.name.to_snake_case(); let path = if is_mod { crate_path.join(format!("{name}.rs")) } else { From 1e705a697ff01d4267a54b2052de1f7204baf7fb Mon Sep 17 00:00:00 2001 From: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Date: Mon, 26 May 2025 16:04:58 -0500 Subject: [PATCH 046/374] chore: standardize lint help + validate docs existance (#10639) --- crates/forge/tests/cli/lint.rs | 53 +++++++++++++++++++ crates/lint/src/linter.rs | 13 ++--- crates/lint/src/sol/gas/keccak.rs | 3 +- crates/lint/src/sol/high/incorrect_shift.rs | 3 +- crates/lint/src/sol/info/mixed_case.rs | 3 +- crates/lint/src/sol/info/pascal_case.rs | 3 +- .../lint/src/sol/info/screaming_snake_case.rs | 3 +- crates/lint/src/sol/macros.rs | 10 ++-- crates/lint/src/sol/med/div_mul.rs | 3 +- crates/lint/src/sol/mod.rs | 4 +- .../lint/testdata/DivideBeforeMultiply.stderr | 6 +++ crates/lint/testdata/IncorrectShift.stderr | 5 ++ crates/lint/testdata/Keccak256.stderr | 2 + crates/lint/testdata/MixedCase.stderr | 18 ++++--- .../lint/testdata/ScreamingSnakeCase.stderr | 12 +++-- crates/lint/testdata/StructPascalCase.stderr | 12 ++--- 16 files changed, 110 insertions(+), 43 deletions(-) diff --git a/crates/forge/tests/cli/lint.rs b/crates/forge/tests/cli/lint.rs index 94599eb059b17..2223d1cf11fce 100644 --- a/crates/forge/tests/cli/lint.rs +++ b/crates/forge/tests/cli/lint.rs @@ -1,3 +1,4 @@ +use forge_lint::{linter::Lint, sol::med::REGISTERED_LINTS}; use foundry_config::{LintSeverity, LinterConfig}; const CONTRACT: &str = r#" @@ -53,6 +54,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 | (1 / 2) * 3; | ----------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply "#]]); @@ -75,6 +77,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 6 | uint256 VARIABLE_MIXED_CASE_INFO; | ------------------------ | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable "#]]); @@ -109,6 +112,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 6 | uint256 VARIABLE_MIXED_CASE_INFO; | ------------------------ | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable "#]]); @@ -134,6 +138,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 | (1 / 2) * 3; | ----------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply "#]]); @@ -160,8 +165,56 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 13 | result = 8 >> localValue; | --------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift "# ]]); }); + +#[tokio::test] +async fn ensure_lint_rule_docs() { + const FOUNDRY_BOOK_LINT_PAGE_URL: &str = + "https://book.getfoundry.sh/reference/forge/forge-lint"; + + // Fetch the content of the lint reference + let content = match reqwest::get(FOUNDRY_BOOK_LINT_PAGE_URL).await { + Ok(resp) => { + if !resp.status().is_success() { + panic!( + "Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}). Status: {status}", + status = resp.status() + ); + } + match resp.text().await { + Ok(text) => text, + Err(e) => { + panic!("Failed to read response text: {e}"); + } + } + } + Err(e) => { + panic!("Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}): {e}",); + } + }; + + // Ensure no missing lints + let mut missing_lints = Vec::new(); + for lint in REGISTERED_LINTS { + let selector = format!("#{}", lint.id()); + if !content.contains(&selector) { + missing_lints.push(lint.id()); + } + } + + if !missing_lints.is_empty() { + let mut msg = String::from( + "Foundry Book lint validation failed. The following lints must be added to the docs:\n", + ); + for lint in missing_lints { + msg.push_str(&format!(" - {lint}\n")); + } + msg.push_str("Please open a PR: https://github.com/foundry-rs/book"); + panic!("{msg}"); + } +} diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 6c188ff86497f..2c11e0222a286 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -31,9 +31,7 @@ pub trait Lint { fn id(&self) -> &'static str; fn severity(&self) -> Severity; fn description(&self) -> &'static str; - fn help(&self) -> Option<&'static str> { - None - } + fn help(&self) -> &'static str; } pub struct LintContext<'s> { @@ -48,19 +46,14 @@ impl<'s> LintContext<'s> { // Helper method to emit diagnostics easily from passes pub fn emit(&self, lint: &'static L, span: Span) { - let (desc, help) = match (self.desc, lint.help()) { - (true, Some(help)) => (lint.description(), help), - (true, None) => (lint.description(), ""), - (false, _) => ("", ""), - }; - + let desc = if self.desc { lint.description() } else { "" }; let diag: DiagBuilder<'_, ()> = self .sess .dcx .diag(lint.severity().into(), desc) .code(DiagId::new_str(lint.id())) .span(MultiSpan::from_span(span)) - .help(help); + .help(lint.help()); diag.emit(); } diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index f4031554b90ec..7316f4c4239b7 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -11,8 +11,7 @@ declare_forge_lint!( ASM_KECCAK256, Severity::Gas, "asm-keccak256", - "hash using inline assembly to save gas", - "" + "hash using inline assembly to save gas" ); impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 { diff --git a/crates/lint/src/sol/high/incorrect_shift.rs b/crates/lint/src/sol/high/incorrect_shift.rs index 6d038d86382cd..8d2326c5cc9f9 100644 --- a/crates/lint/src/sol/high/incorrect_shift.rs +++ b/crates/lint/src/sol/high/incorrect_shift.rs @@ -10,8 +10,7 @@ declare_forge_lint!( INCORRECT_SHIFT, Severity::High, "incorrect-shift", - "the order of args in a shift operation is incorrect", - "" + "the order of args in a shift operation is incorrect" ); impl<'ast> EarlyLintPass<'ast> for IncorrectShift { diff --git a/crates/lint/src/sol/info/mixed_case.rs b/crates/lint/src/sol/info/mixed_case.rs index 035c21ef592c1..5e839e9f313cf 100644 --- a/crates/lint/src/sol/info/mixed_case.rs +++ b/crates/lint/src/sol/info/mixed_case.rs @@ -10,8 +10,7 @@ declare_forge_lint!( MIXED_CASE_FUNCTION, Severity::Info, "mixed-case-function", - "function names should use mixedCase", - "https://docs.soliditylang.org/en/latest/style-guide.html#function-names" + "function names should use mixedCase" ); impl<'ast> EarlyLintPass<'ast> for MixedCaseFunction { diff --git a/crates/lint/src/sol/info/pascal_case.rs b/crates/lint/src/sol/info/pascal_case.rs index 435debd66ac3c..60cafbc8365a3 100644 --- a/crates/lint/src/sol/info/pascal_case.rs +++ b/crates/lint/src/sol/info/pascal_case.rs @@ -10,8 +10,7 @@ declare_forge_lint!( PASCAL_CASE_STRUCT, Severity::Info, "pascal-case-struct", - "structs should use PascalCase", - "https://docs.soliditylang.org/en/latest/style-guide.html#struct-names" + "structs should use PascalCase" ); impl<'ast> EarlyLintPass<'ast> for PascalCaseStruct { diff --git a/crates/lint/src/sol/info/screaming_snake_case.rs b/crates/lint/src/sol/info/screaming_snake_case.rs index 528638181b126..ccc978029b8a4 100644 --- a/crates/lint/src/sol/info/screaming_snake_case.rs +++ b/crates/lint/src/sol/info/screaming_snake_case.rs @@ -10,8 +10,7 @@ declare_forge_lint!( SCREAMING_SNAKE_CASE_CONSTANT, Severity::Info, "screaming-snake-case-const", - "constants should use SCREAMING_SNAKE_CASE", - "https://docs.soliditylang.org/en/latest/style-guide.html#constants" + "constants should use SCREAMING_SNAKE_CASE" ); declare_forge_lint!( diff --git a/crates/lint/src/sol/macros.rs b/crates/lint/src/sol/macros.rs index 093b96a7e4a70..357f742aa297c 100644 --- a/crates/lint/src/sol/macros.rs +++ b/crates/lint/src/sol/macros.rs @@ -7,16 +7,20 @@ /// - `$severity`: The `Severity` of the lint (e.g. `High`, `Med`, `Low`, `Info`, `Gas`). /// - `$str_id`: A unique identifier used to reference a specific lint during configuration. /// - `$desc`: A short description of the lint. -/// - `$help` (optional): Link to additional information about the lint or best practices. +/// +/// # Note +/// Each lint must have a `help` section in the foundry book. This help field is auto-generated by +/// the macro. Because of that, to ensure that new lint rules have their corresponding docs in the +/// book, the existence of the lint rule's help section is validated with a unit test. #[macro_export] macro_rules! declare_forge_lint { - ($id:ident, $severity:expr, $str_id:expr, $desc:expr, $help:expr) => { + ($id:ident, $severity:expr, $str_id:expr, $desc:expr) => { // Declare the static `Lint` metadata pub static $id: SolLint = SolLint { id: $str_id, severity: $severity, description: $desc, - help: if $help.is_empty() { None } else { Some($help) }, + help: concat!("https://book.getfoundry.sh/reference/forge/forge-lint#", $str_id), }; }; diff --git a/crates/lint/src/sol/med/div_mul.rs b/crates/lint/src/sol/med/div_mul.rs index b513468be6b83..b154971e55434 100644 --- a/crates/lint/src/sol/med/div_mul.rs +++ b/crates/lint/src/sol/med/div_mul.rs @@ -10,8 +10,7 @@ declare_forge_lint!( DIVIDE_BEFORE_MULTIPLY, Severity::Med, "divide-before-multiply", - "multiplication should occur before division to avoid loss of precision", - "" + "multiplication should occur before division to avoid loss of precision" ); impl<'ast> EarlyLintPass<'ast> for DivideBeforeMultiply { diff --git a/crates/lint/src/sol/mod.rs b/crates/lint/src/sol/mod.rs index 6bb2ffabef39a..463777d5b194a 100644 --- a/crates/lint/src/sol/mod.rs +++ b/crates/lint/src/sol/mod.rs @@ -159,7 +159,7 @@ pub enum SolLintError { pub struct SolLint { id: &'static str, description: &'static str, - help: Option<&'static str>, + help: &'static str, severity: Severity, } @@ -173,7 +173,7 @@ impl Lint for SolLint { fn description(&self) -> &'static str { self.description } - fn help(&self) -> Option<&'static str> { + fn help(&self) -> &'static str { self.help } } diff --git a/crates/lint/testdata/DivideBeforeMultiply.stderr b/crates/lint/testdata/DivideBeforeMultiply.stderr index 08d8ebe25d1c9..1c98f4b13d194 100644 --- a/crates/lint/testdata/DivideBeforeMultiply.stderr +++ b/crates/lint/testdata/DivideBeforeMultiply.stderr @@ -4,6 +4,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 3 | (1 / 2) * 3; | ----------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -11,6 +12,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 5 | ((1 / 2) * 3) * 4; | ----------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -18,6 +20,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 6 | ((1 * 2) / 3) * 4; | ----------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -25,6 +28,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 7 | (1 / 2 / 3) * 4; | --------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -32,6 +36,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 8 | (1 / (2 + 3)) * 4; | ----------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -39,4 +44,5 @@ warning[divide-before-multiply]: multiplication should occur before division to 15 | 1 / ((2 / 3) * 3); | ----------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply diff --git a/crates/lint/testdata/IncorrectShift.stderr b/crates/lint/testdata/IncorrectShift.stderr index 16fbda627cd9b..46276262da4bd 100644 --- a/crates/lint/testdata/IncorrectShift.stderr +++ b/crates/lint/testdata/IncorrectShift.stderr @@ -4,6 +4,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 21 | result = 2 << stateValue; | --------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect --> ROOT/testdata/IncorrectShift.sol:LL:CC @@ -11,6 +12,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 22 | result = 8 >> localValue; | --------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect --> ROOT/testdata/IncorrectShift.sol:LL:CC @@ -18,6 +20,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 23 | result = 16 << (stateValue + 1); | ---------------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect --> ROOT/testdata/IncorrectShift.sol:LL:CC @@ -25,6 +28,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 24 | result = 32 >> getAmount(); | ----------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect --> ROOT/testdata/IncorrectShift.sol:LL:CC @@ -32,4 +36,5 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 25 | ... result = 1 << (localValue > 10 ? localShiftAmount : stateShiftAmount); | ------------------------------------------------------------ | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift diff --git a/crates/lint/testdata/Keccak256.stderr b/crates/lint/testdata/Keccak256.stderr index e589bec00eb77..8011afa89a112 100644 --- a/crates/lint/testdata/Keccak256.stderr +++ b/crates/lint/testdata/Keccak256.stderr @@ -4,6 +4,7 @@ note[asm-keccak256]: hash using inline assembly to save gas 3 | keccak256(abi.encodePacked(a, b)); | --------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 note[asm-keccak256]: hash using inline assembly to save gas --> ROOT/testdata/Keccak256.sol:LL:CC @@ -11,4 +12,5 @@ note[asm-keccak256]: hash using inline assembly to save gas 7 | keccak256(abi.encodePacked(a, b)); | --------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 diff --git a/crates/lint/testdata/MixedCase.stderr b/crates/lint/testdata/MixedCase.stderr index 1245d53caf5cc..0976dced8716c 100644 --- a/crates/lint/testdata/MixedCase.stderr +++ b/crates/lint/testdata/MixedCase.stderr @@ -4,6 +4,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 9 | uint256 Variablemixedcase; | ----------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -11,6 +12,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 10 | uint256 VARIABLE_MIXED_CASE; | ------------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -18,6 +20,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 11 | uint256 VariableMixedCase; | ----------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -25,6 +28,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 17 | uint256 testVAL; | ------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -32,6 +36,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 18 | uint256 TestVal; | ------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -39,6 +44,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 19 | uint256 TESTVAL; | ------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable note[mixed-case-function]: function names should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -46,7 +52,7 @@ note[mixed-case-function]: function names should use mixedCase 26 | function Functionmixedcase() public {} | ----------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#function-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function note[mixed-case-function]: function names should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -54,7 +60,7 @@ note[mixed-case-function]: function names should use mixedCase 27 | function FUNCTION_MIXED_CASE() public {} | ------------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#function-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function note[mixed-case-function]: function names should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -62,7 +68,7 @@ note[mixed-case-function]: function names should use mixedCase 28 | function FunctionMixedCase() public {} | ----------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#function-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function note[mixed-case-function]: function names should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -70,7 +76,7 @@ note[mixed-case-function]: function names should use mixedCase 29 | function function_mixed_case() public {} | ------------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#function-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function note[mixed-case-function]: function names should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -78,7 +84,7 @@ note[mixed-case-function]: function names should use mixedCase 53 | function invariantBalance_MixedCase_Enabled() public {} | ---------------------------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#function-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function note[mixed-case-function]: function names should use mixedCase --> ROOT/testdata/MixedCase.sol:LL:CC @@ -86,5 +92,5 @@ note[mixed-case-function]: function names should use mixedCase 54 | function invariantbalance_mixedcase_enabled() public {} | ---------------------------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#function-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function diff --git a/crates/lint/testdata/ScreamingSnakeCase.stderr b/crates/lint/testdata/ScreamingSnakeCase.stderr index 96c2cb34405b3..b511f09d254f3 100644 --- a/crates/lint/testdata/ScreamingSnakeCase.stderr +++ b/crates/lint/testdata/ScreamingSnakeCase.stderr @@ -4,7 +4,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE 9 | uint256 constant screamingSnakeCase = 0; | ------------------ | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#constants + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -12,7 +12,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE 10 | uint256 constant screaming_snake_case = 0; | -------------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#constants + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -20,7 +20,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE 11 | uint256 constant ScreamingSnakeCase = 0; | ------------------ | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#constants + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -28,7 +28,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE 12 | uint256 constant SCREAMING_snake_case = 0; | -------------------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#constants + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -36,6 +36,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE 19 | uint256 immutable screamingSnakeCase0 = 0; | ------------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -43,6 +44,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE 20 | uint256 immutable screaming_snake_case0 = 0; | --------------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -50,6 +52,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE 21 | uint256 immutable ScreamingSnakeCase0 = 0; | ------------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE --> ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -57,4 +60,5 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE 22 | uint256 immutable SCREAMING_snake_case_0 = 0; | ---------------------- | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable diff --git a/crates/lint/testdata/StructPascalCase.stderr b/crates/lint/testdata/StructPascalCase.stderr index 2ef869c7ccc21..7858c18cbf33d 100644 --- a/crates/lint/testdata/StructPascalCase.stderr +++ b/crates/lint/testdata/StructPascalCase.stderr @@ -4,7 +4,7 @@ note[pascal-case-struct]: structs should use PascalCase 13 | struct _PascalCase { | ----------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#struct-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct note[pascal-case-struct]: structs should use PascalCase --> ROOT/testdata/StructPascalCase.sol:LL:CC @@ -12,7 +12,7 @@ note[pascal-case-struct]: structs should use PascalCase 17 | struct pascalCase { | ---------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#struct-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct note[pascal-case-struct]: structs should use PascalCase --> ROOT/testdata/StructPascalCase.sol:LL:CC @@ -20,7 +20,7 @@ note[pascal-case-struct]: structs should use PascalCase 21 | struct pascalcase { | ---------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#struct-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct note[pascal-case-struct]: structs should use PascalCase --> ROOT/testdata/StructPascalCase.sol:LL:CC @@ -28,7 +28,7 @@ note[pascal-case-struct]: structs should use PascalCase 25 | struct pascal_case { | ----------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#struct-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct note[pascal-case-struct]: structs should use PascalCase --> ROOT/testdata/StructPascalCase.sol:LL:CC @@ -36,7 +36,7 @@ note[pascal-case-struct]: structs should use PascalCase 29 | struct PASCAL_CASE { | ----------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#struct-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct note[pascal-case-struct]: structs should use PascalCase --> ROOT/testdata/StructPascalCase.sol:LL:CC @@ -44,5 +44,5 @@ note[pascal-case-struct]: structs should use PascalCase 33 | struct PASCALCASE { | ---------- | - = help: https://docs.soliditylang.org/en/latest/style-guide.html#struct-names + = help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct From ca03f8ffe74d56f559ad861e2eea911f19d5b906 Mon Sep 17 00:00:00 2001 From: Mablr <59505383+mablr@users.noreply.github.com> Date: Tue, 27 May 2025 00:33:22 +0200 Subject: [PATCH 047/374] feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. --- crates/cast/src/cmd/mktx.rs | 22 +++++++++++++++++++--- crates/cast/tests/cli/main.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/crates/cast/src/cmd/mktx.rs b/crates/cast/src/cmd/mktx.rs index af06d556c23ff..ebfb5afeadd47 100644 --- a/crates/cast/src/cmd/mktx.rs +++ b/crates/cast/src/cmd/mktx.rs @@ -2,6 +2,7 @@ use crate::tx::{self, CastTxBuilder}; use alloy_ens::NameOrAddress; use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}; use alloy_primitives::hex; +use alloy_provider::Provider; use alloy_signer::Signer; use clap::Parser; use eyre::{OptionExt, Result}; @@ -50,6 +51,10 @@ pub struct MakeTxArgs { /// Relaxes the wallet requirement. #[arg(long, requires = "from")] raw_unsigned: bool, + + /// Call `eth_signTransaction` using the `--from` argument or $ETH_FROM as sender + #[arg(long, requires = "from", conflicts_with = "raw_unsigned")] + ethsign: bool, } #[derive(Debug, Parser)] @@ -70,7 +75,7 @@ pub enum MakeTxSubcommands { impl MakeTxArgs { pub async fn run(self) -> Result<()> { - let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned } = self; + let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self; let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; @@ -91,7 +96,7 @@ impl MakeTxArgs { let provider = get_provider(&config)?; - let tx_builder = CastTxBuilder::new(provider, tx, &config) + let tx_builder = CastTxBuilder::new(&provider, tx, &config) .await? .with_to(to) .await? @@ -108,7 +113,18 @@ impl MakeTxArgs { return Ok(()); } - // Retrieve the signer, and bail if it can't be constructed. + if ethsign { + // Use "eth_signTransaction" to sign the transaction only works if the node/RPC has + // unlocked accounts. + let (tx, _) = tx_builder.build(config.sender).await?; + let signed_tx = provider.sign_transaction(tx).await?; + + sh_println!("{signed_tx}")?; + return Ok(()); + } + + // Default to using the local signer. + // Get the signer from the wallet, and fail if it can't be constructed. let signer = eth.wallet.signer().await?; let from = signer.address(); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 4735a8ce1abbc..1a0c0bf958cf8 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1296,6 +1296,37 @@ casttest!(mktx_raw_unsigned, |_prj, cmd| { ]]); }); +casttest!(mktx_ethsign, async |_prj, cmd| { + let (_api, handle) = anvil::spawn(NodeConfig::test()).await; + let rpc = handle.http_endpoint(); + cmd.args([ + "mktx", + "--from", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "--chain", + "31337", + "--nonce", + "0", + "--gas-limit", + "21000", + "--gas-price", + "10000000000", + "--priority-gas-price", + "1000000000", + "0x0000000000000000000000000000000000000001", + "--ethsign", + "--rpc-url", + rpc.as_str(), + ]) + .assert_success() + .stdout_eq(str![[ + r#" +0x02f86d827a6980843b9aca008502540be4008252089400000000000000000000000000000000000000018080c001a0b8eeb1ded87b085859c510c5692bed231e3ee8b068ccf71142bbf28da0e95987a07813b676a248ae8055f28495021d78dee6695479d339a6ad9d260d9eaf20674c + +"# + ]]); +}); + // tests that the raw encoded transaction is returned casttest!(tx_raw, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); From fbb793c5383503b7aa0c037e510690ae1d17f5e2 Mon Sep 17 00:00:00 2001 From: Mablr <59505383+mablr@users.noreply.github.com> Date: Tue, 27 May 2025 16:28:39 +0200 Subject: [PATCH 048/374] chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure --- crates/wallets/src/wallet.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index 29ce54438586d..8e3a4dafb332a 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -128,12 +128,14 @@ impl WalletOpts { eyre::bail!( "\ 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 +Run the command with --help flag for more information or use the corresponding CLI flag to set your key via: --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." +Alternatively, when using the `cast send` or `cast mktx` commands with a local node +or RPC that has unlocked accounts, the --unlocked or --ethsign flags can be used, +respectively. The sender address can be specified by setting the `ETH_FROM` environment +variable to the desired unlocked account address, or by providing the address directly +using the --from flag." ) }; From fbdec00058201cf794b5e148de8d6807a033af21 Mon Sep 17 00:00:00 2001 From: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Date: Tue, 27 May 2025 20:19:41 +0530 Subject: [PATCH 049/374] chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- Cargo.lock | 12 +- Cargo.toml | 2 + crates/anvil/Cargo.toml | 2 + crates/anvil/src/cmd.rs | 7 +- crates/anvil/src/config.rs | 15 +- crates/anvil/src/hardfork.rs | 274 ++++++++------------------- crates/anvil/src/lib.rs | 3 +- crates/anvil/tests/it/anvil.rs | 3 +- crates/anvil/tests/it/anvil_api.rs | 3 +- crates/anvil/tests/it/eip4844.rs | 3 +- crates/anvil/tests/it/eip7702.rs | 3 +- crates/anvil/tests/it/otterscan.rs | 3 +- crates/anvil/tests/it/traces.rs | 3 +- crates/anvil/tests/it/transaction.rs | 3 +- crates/cast/Cargo.toml | 1 + crates/cast/tests/cli/main.rs | 3 +- crates/forge/Cargo.toml | 1 + crates/forge/tests/cli/script.rs | 3 +- 18 files changed, 126 insertions(+), 218 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05a8b99f5d05..1fa7e6a150282 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,9 +253,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6b8067561eb8f884b215ace4c962313c5467e47bde6b457c8c51e268fb5d99" +checksum = "fbff8445282ec080c2673692062bd4930d7a0d6bda257caf138cfc650c503000" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -348,9 +348,9 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08043c9284e597f9b5cf741cc6d906fdb26c195a01d88423c84c00ffda835713" +checksum = "9ddfbb5cc9f614efa5d56e0d7226214bb67b29271d44b6ddfcbbe25eb0ff898b" dependencies = [ "alloy-hardforks", "auto_impl", @@ -1023,8 +1023,10 @@ dependencies = [ "alloy-eips", "alloy-evm", "alloy-genesis", + "alloy-hardforks", "alloy-network", "alloy-op-evm", + "alloy-op-hardforks", "alloy-primitives", "alloy-provider", "alloy-pubsub", @@ -2351,6 +2353,7 @@ dependencies = [ "alloy-contract", "alloy-dyn-abi", "alloy-ens", + "alloy-hardforks", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -3778,6 +3781,7 @@ version = "1.2.1" dependencies = [ "alloy-chains", "alloy-dyn-abi", + "alloy-hardforks", "alloy-json-abi", "alloy-network", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 8686f046c2c14..3baefd5dca836 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -230,6 +230,8 @@ alloy-transport = { version = "1.0.7", default-features = false } alloy-transport-http = { version = "1.0.7", default-features = false } alloy-transport-ipc = { version = "1.0.7", default-features = false } alloy-transport-ws = { version = "1.0.7", default-features = false } +alloy-hardforks = { version = "0.2.6", default-features = false } +alloy-op-hardforks = { version = "0.2.6", default-features = false } ## alloy-core alloy-dyn-abi = "1.0" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index e45c95e49f85e..23ff53b271895 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -55,6 +55,8 @@ alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true alloy-trie.workspace = true +alloy-hardforks.workspace = true +alloy-op-hardforks.workspace = true op-alloy-consensus = { workspace = true, features = ["serde"] } # revm diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index d4b91e14f20fd..c8223a76ed959 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -1,9 +1,10 @@ use crate::{ config::{ForkChoice, DEFAULT_MNEMONIC}, eth::{backend::db::SerializableState, pool::transactions::TransactionOrder, EthApi}, - AccountGenerator, EthereumHardfork, NodeConfig, OptimismHardfork, CHAIN_ID, + AccountGenerator, EthereumHardfork, NodeConfig, CHAIN_ID, }; use alloy_genesis::Genesis; +use alloy_op_hardforks::OpHardfork; use alloy_primitives::{utils::Unit, B256, U256}; use alloy_signer_local::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; @@ -218,7 +219,7 @@ impl NodeArgs { let hardfork = match &self.hardfork { Some(hf) => { if self.evm.optimism { - Some(OptimismHardfork::from_str(hf)?.into()) + Some(OpHardfork::from_str(hf)?.into()) } else { Some(EthereumHardfork::from_str(hf)?.into()) } @@ -836,7 +837,7 @@ mod tests { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--optimism", "--hardfork", "Regolith"]); let config = args.into_node_config().unwrap(); - assert_eq!(config.hardfork, Some(OptimismHardfork::Regolith.into())); + assert_eq!(config.hardfork, Some(OpHardfork::Regolith.into())); } #[test] diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 722408c72a9c4..1cde743c3e952 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -11,13 +11,14 @@ use crate::{ fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE}, pool::transactions::{PoolTransaction, TransactionOrder}, }, - hardfork::ChainHardfork, + hardfork::{ethereum_hardfork_from_block_tag, spec_id_from_ethereum_hardfork, ChainHardfork}, mem::{self, in_memory_db::MemDb}, - EthereumHardfork, FeeManager, OptimismHardfork, PrecompileFactory, + EthereumHardfork, FeeManager, PrecompileFactory, }; use alloy_consensus::BlockHeader; use alloy_genesis::Genesis; use alloy_network::{AnyNetwork, TransactionResponse}; +use alloy_op_hardforks::OpHardfork; use alloy_primitives::{hex, map::HashMap, utils::Unit, BlockNumber, TxHash, U256}; use alloy_provider::Provider; use alloy_rpc_types::{Block, BlockNumberOrTag}; @@ -530,9 +531,9 @@ impl NodeConfig { return hardfork; } if self.enable_optimism { - return OptimismHardfork::default().into(); + return OpHardfork::Isthmus.into(); } - EthereumHardfork::default().into() + EthereumHardfork::Cancun.into() } /// Sets a custom code size limit @@ -1186,8 +1187,10 @@ impl NodeConfig { let chain_id = provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?; if alloy_chains::NamedChain::Mainnet == chain_id { - let hardfork: EthereumHardfork = fork_block_number.into(); - env.evm_env.cfg_env.spec = hardfork.into(); + let hardfork: EthereumHardfork = + ethereum_hardfork_from_block_tag(fork_block_number); + + env.evm_env.cfg_env.spec = spec_id_from_ethereum_hardfork(hardfork); self.hardfork = Some(ChainHardfork::Ethereum(hardfork)); } Some(U256::from(chain_id)) diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index 34fe97fa1de41..f5e063aafc625 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -1,14 +1,14 @@ -use std::str::FromStr; - +use alloy_hardforks::EthereumHardfork; +use alloy_op_hardforks::OpHardfork::{self}; use alloy_rpc_types::BlockNumberOrTag; -use eyre::bail; + use op_revm::OpSpecId; use revm::primitives::hardfork::SpecId; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ChainHardfork { Ethereum(EthereumHardfork), - Optimism(OptimismHardfork), + Optimism(OpHardfork), } impl From for ChainHardfork { @@ -17,8 +17,8 @@ impl From for ChainHardfork { } } -impl From for ChainHardfork { - fn from(value: OptimismHardfork) -> Self { +impl From for ChainHardfork { + fn from(value: OpHardfork) -> Self { Self::Optimism(value) } } @@ -26,213 +26,99 @@ impl From for ChainHardfork { impl From for SpecId { fn from(fork: ChainHardfork) -> Self { match fork { - ChainHardfork::Ethereum(hardfork) => hardfork.into(), - ChainHardfork::Optimism(hardfork) => hardfork.into_eth_spec(), - } - } -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum EthereumHardfork { - Frontier, - Homestead, - Dao, - Tangerine, - SpuriousDragon, - Byzantium, - Constantinople, - Petersburg, - Istanbul, - Muirglacier, - Berlin, - London, - ArrowGlacier, - GrayGlacier, - Paris, - Shanghai, - Cancun, - Prague, - #[default] - Latest, -} - -impl EthereumHardfork { - /// Get the first block number of the hardfork. - pub fn fork_block(&self) -> u64 { - match *self { - 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, - Self::Prague => 22431084, + ChainHardfork::Ethereum(hardfork) => spec_id_from_ethereum_hardfork(hardfork), + ChainHardfork::Optimism(hardfork) => spec_id_from_optimism_hardfork(hardfork).into(), } } } -impl FromStr for EthereumHardfork { - type Err = eyre::Report; - - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - let hardfork = match s.as_str() { - "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, - "prague" | "18" => Self::Prague, - "latest" => Self::Latest, - _ => bail!("Unknown hardfork {s}"), - }; - Ok(hardfork) +/// Map an EthereumHardfork enum into its corresponding SpecId. +pub fn spec_id_from_ethereum_hardfork(hardfork: EthereumHardfork) -> SpecId { + match hardfork { + EthereumHardfork::Frontier => SpecId::FRONTIER, + EthereumHardfork::Homestead => SpecId::HOMESTEAD, + EthereumHardfork::Dao => SpecId::DAO_FORK, + EthereumHardfork::Tangerine => SpecId::TANGERINE, + EthereumHardfork::SpuriousDragon => SpecId::SPURIOUS_DRAGON, + EthereumHardfork::Byzantium => SpecId::BYZANTIUM, + EthereumHardfork::Constantinople => SpecId::CONSTANTINOPLE, + EthereumHardfork::Petersburg => SpecId::PETERSBURG, + EthereumHardfork::Istanbul => SpecId::ISTANBUL, + EthereumHardfork::MuirGlacier => SpecId::MUIR_GLACIER, + EthereumHardfork::Berlin => SpecId::BERLIN, + EthereumHardfork::London => SpecId::LONDON, + EthereumHardfork::ArrowGlacier => SpecId::ARROW_GLACIER, + EthereumHardfork::GrayGlacier => SpecId::GRAY_GLACIER, + EthereumHardfork::Paris => SpecId::MERGE, + EthereumHardfork::Shanghai => SpecId::SHANGHAI, + EthereumHardfork::Cancun => SpecId::CANCUN, + EthereumHardfork::Prague => SpecId::PRAGUE, + EthereumHardfork::Osaka => SpecId::OSAKA, } } -impl From for SpecId { - fn from(fork: EthereumHardfork) -> Self { - match fork { - EthereumHardfork::Frontier => Self::FRONTIER, - EthereumHardfork::Homestead => Self::HOMESTEAD, - EthereumHardfork::Dao => Self::HOMESTEAD, - EthereumHardfork::Tangerine => Self::TANGERINE, - EthereumHardfork::SpuriousDragon => Self::SPURIOUS_DRAGON, - EthereumHardfork::Byzantium => Self::BYZANTIUM, - EthereumHardfork::Constantinople => Self::CONSTANTINOPLE, - EthereumHardfork::Petersburg => Self::PETERSBURG, - EthereumHardfork::Istanbul => Self::ISTANBUL, - EthereumHardfork::Muirglacier => Self::MUIR_GLACIER, - EthereumHardfork::Berlin => Self::BERLIN, - EthereumHardfork::London => Self::LONDON, - EthereumHardfork::ArrowGlacier => Self::LONDON, - EthereumHardfork::GrayGlacier => Self::GRAY_GLACIER, - EthereumHardfork::Paris => Self::MERGE, - EthereumHardfork::Shanghai => Self::SHANGHAI, - EthereumHardfork::Cancun | EthereumHardfork::Latest => Self::CANCUN, - EthereumHardfork::Prague => Self::PRAGUE, - } +/// Map an OptimismHardfork enum into its corresponding OpSpecId. +pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId { + match hardfork { + OpHardfork::Bedrock => OpSpecId::BEDROCK, + OpHardfork::Regolith => OpSpecId::REGOLITH, + OpHardfork::Canyon => OpSpecId::CANYON, + OpHardfork::Ecotone => OpSpecId::ECOTONE, + OpHardfork::Fjord => OpSpecId::FJORD, + OpHardfork::Granite => OpSpecId::GRANITE, + OpHardfork::Holocene => OpSpecId::HOLOCENE, + OpHardfork::Isthmus => OpSpecId::ISTHMUS, + OpHardfork::Interop => OpSpecId::INTEROP, } } -impl> From for EthereumHardfork { - fn from(block: T) -> Self { - let num = match block.into() { - BlockNumberOrTag::Earliest => 0, - BlockNumberOrTag::Number(num) => num, - _ => u64::MAX, - }; - - match num { - _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, - } - } -} +/// Convert a `BlockNumberOrTag` into an `EthereumHardfork`. +pub fn ethereum_hardfork_from_block_tag(block: impl Into) -> EthereumHardfork { + let num = match block.into() { + BlockNumberOrTag::Earliest => 0, + BlockNumberOrTag::Number(num) => num, + _ => u64::MAX, + }; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum OptimismHardfork { - Bedrock, - Regolith, - Canyon, - Ecotone, - Fjord, - Granite, - Holocene, - #[default] - Isthmus, + EthereumHardfork::from_mainnet_block_number(num) } -impl OptimismHardfork { - pub fn into_eth_spec(self) -> SpecId { - let op_spec: OpSpecId = self.into(); - op_spec.into_eth_spec() - } -} +#[cfg(test)] +mod tests { + use super::*; + use alloy_hardforks::ethereum::mainnet::*; + #[allow(unused_imports)] + use alloy_rpc_types::BlockNumberOrTag; -impl FromStr for OptimismHardfork { - type Err = eyre::Report; + #[test] + fn test_ethereum_spec_id_mapping() { + assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Frontier), SpecId::FRONTIER); + assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Homestead), SpecId::HOMESTEAD); - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - let hardfork = match s.as_str() { - "bedrock" => Self::Bedrock, - "regolith" => Self::Regolith, - "canyon" => Self::Canyon, - "ecotone" => Self::Ecotone, - "fjord" => Self::Fjord, - "granite" => Self::Granite, - "holocene" => Self::Holocene, - "isthmus" => Self::Isthmus, - _ => bail!("Unknown hardfork {s}"), - }; - Ok(hardfork) + // Test latest hardforks + assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Cancun), SpecId::CANCUN); + assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Prague), SpecId::PRAGUE); } -} - -impl From for OpSpecId { - fn from(fork: OptimismHardfork) -> Self { - match fork { - OptimismHardfork::Bedrock => Self::BEDROCK, - OptimismHardfork::Regolith => Self::REGOLITH, - OptimismHardfork::Canyon => Self::CANYON, - OptimismHardfork::Ecotone => Self::ECOTONE, - OptimismHardfork::Fjord => Self::FJORD, - OptimismHardfork::Granite => Self::GRANITE, - OptimismHardfork::Holocene => Self::HOLOCENE, - OptimismHardfork::Isthmus => Self::ISTHMUS, - } - } -} - -#[cfg(test)] -mod tests { - use crate::EthereumHardfork; #[test] - fn test_hardfork_blocks() { - let hf: EthereumHardfork = 12_965_000u64.into(); - assert_eq!(hf, EthereumHardfork::London); + fn test_optimism_spec_id_mapping() { + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK); + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH); - let hf: EthereumHardfork = 4370000u64.into(); - assert_eq!(hf, EthereumHardfork::Byzantium); + // Test latest hardforks + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE); + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP); + } - let hf: EthereumHardfork = 12244000u64.into(); - assert_eq!(hf, EthereumHardfork::Berlin); + #[test] + fn test_hardfork_from_block_tag_numbers() { + assert_eq!( + ethereum_hardfork_from_block_tag(MAINNET_HOMESTEAD_BLOCK - 1), + EthereumHardfork::Frontier + ); + assert_eq!( + ethereum_hardfork_from_block_tag(MAINNET_LONDON_BLOCK + 1), + EthereumHardfork::London + ); } } diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 77e072e0aa1f2..5ee4fead559b2 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -47,8 +47,7 @@ pub use config::{ }; mod hardfork; -pub use hardfork::{EthereumHardfork, OptimismHardfork}; - +pub use alloy_hardforks::EthereumHardfork; /// ethereum related implementations pub mod eth; /// Evm related abstractions diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index 329caea844b45..721aa404b36e1 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -2,9 +2,10 @@ use alloy_consensus::EMPTY_ROOT_HASH; use alloy_eips::BlockNumberOrTag; +use alloy_hardforks::EthereumHardfork; use alloy_primitives::Address; use alloy_provider::Provider; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn test_can_change_mining_mode() { diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index ce337714587b9..d4af986814ed6 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -6,6 +6,7 @@ use crate::{ utils::http_provider_with_signer, }; use alloy_consensus::{SignableTransaction, TxEip1559}; +use alloy_hardforks::EthereumHardfork; use alloy_network::{EthereumWallet, TransactionBuilder, TxSignerSync}; use alloy_primitives::{address, fixed_bytes, utils::Unit, Address, Bytes, TxKind, U256}; use alloy_provider::{ext::TxPoolApi, Provider}; @@ -21,7 +22,7 @@ use anvil::{ api::CLIENT_VERSION, backend::mem::{EXECUTOR, P256_DELEGATION_CONTRACT, P256_DELEGATION_RUNTIME_CODE}, }, - spawn, EthereumHardfork, NodeConfig, + spawn, NodeConfig, }; use anvil_core::{ eth::{ diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 633c8a01bdef0..2d713c3d955e3 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -4,12 +4,13 @@ use alloy_eips::{ eip4844::{BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK_DENCUN}, Typed2718, }; +use alloy_hardforks::EthereumHardfork; use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder, TransactionBuilder4844}; use alloy_primitives::{b256, Address, U256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn can_send_eip4844_transaction() { diff --git a/crates/anvil/tests/it/eip7702.rs b/crates/anvil/tests/it/eip7702.rs index c09412c85e306..9424360119e4e 100644 --- a/crates/anvil/tests/it/eip7702.rs +++ b/crates/anvil/tests/it/eip7702.rs @@ -1,12 +1,13 @@ use crate::utils::http_provider; use alloy_consensus::{transaction::TxEip7702, SignableTransaction}; +use alloy_hardforks::EthereumHardfork; use alloy_network::{ReceiptResponse, TransactionBuilder, TxSignerSync}; use alloy_primitives::{bytes, U256}; use alloy_provider::{PendingTransactionConfig, Provider}; use alloy_rpc_types::{Authorization, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_signer::SignerSync; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn can_send_eip7702_tx() { diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 3809e3183e1a7..ffd70d1349ddb 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,6 +1,7 @@ //! Tests for otterscan endpoints. use crate::abi::Multicall; +use alloy_hardforks::EthereumHardfork; use alloy_network::TransactionResponse; use alloy_primitives::{address, Address, Bytes, U256}; use alloy_provider::Provider; @@ -10,7 +11,7 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use alloy_sol_types::{sol, SolCall, SolError, SolValue}; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; use std::collections::VecDeque; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 849956cabe5dd..2e073ca9c8735 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -4,6 +4,7 @@ use crate::{ utils::http_provider_with_signer, }; use alloy_eips::BlockId; +use alloy_hardforks::EthereumHardfork; use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{ hex::{self, FromHex}, @@ -27,7 +28,7 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use alloy_sol_types::sol; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn test_get_transfer_parity_traces() { diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 953c30515d3ef..7cb681af85a14 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -2,6 +2,7 @@ use crate::{ abi::{Greeter, Multicall, SimpleStorage}, utils::{connect_pubsub, http_provider_with_signer}, }; +use alloy_hardforks::EthereumHardfork; use alloy_network::{EthereumWallet, TransactionBuilder, TransactionResponse}; use alloy_primitives::{address, hex, map::B256HashSet, Address, Bytes, FixedBytes, U256}; use alloy_provider::{Provider, WsConnect}; @@ -12,7 +13,7 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use alloy_sol_types::SolValue; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; use eyre::Ok; use futures::{future::join_all, FutureExt, StreamExt}; use std::{str::FromStr, time::Duration}; diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 26f001132baff..2dda3ba341872 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -53,6 +53,7 @@ alloy-signer.workspace = true alloy-sol-types.workspace = true alloy-transport.workspace = true alloy-ens = { workspace = true, features = ["provider"] } +alloy-hardforks.workspace = true op-alloy-flz.workspace = true op-alloy-consensus = { workspace = true, features = ["alloy-compat"] } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 1a0c0bf958cf8..eca950b2f89d4 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,11 +1,12 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; +use alloy_hardforks::EthereumHardfork; use alloy_network::{TransactionBuilder, TransactionResponse}; use alloy_primitives::{address, b256, Bytes, B256}; use alloy_provider::{Provider, ProviderBuilder}; use alloy_rpc_types::{BlockNumberOrTag, Index, TransactionRequest}; -use anvil::{EthereumHardfork, NodeConfig}; +use anvil::NodeConfig; use foundry_test_utils::{ rpc::{ next_etherscan_api_key, next_http_archive_rpc_url, next_http_rpc_endpoint, diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index c3251ed76c9eb..d823ca3285140 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -63,6 +63,7 @@ alloy-rpc-types.workspace = true alloy-serde.workspace = true alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true revm.workspace = true diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index d72c21fce60ff..1d6f415aef1d4 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1,8 +1,9 @@ //! Contains various tests related to `forge script`. use crate::constants::TEMPLATE_CONTRACT; +use alloy_hardforks::EthereumHardfork; use alloy_primitives::{address, hex, Address, Bytes}; -use anvil::{spawn, EthereumHardfork, NodeConfig}; +use anvil::{spawn, NodeConfig}; use forge_script_sequence::ScriptSequence; use foundry_test_utils::{ rpc::{self, next_http_archive_rpc_url}, From 07dee8164299a22f4826242b35e81387b4a55005 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 28 May 2025 13:03:08 +0100 Subject: [PATCH 050/374] fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit --- crates/anvil/src/cmd.rs | 2 +- crates/anvil/src/config.rs | 6 +++--- crates/anvil/tests/it/anvil_api.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index c8223a76ed959..4ca9624d57f6e 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -79,7 +79,7 @@ pub struct NodeArgs { /// The EVM hardfork to use. /// - /// Choose the hardfork by name, e.g. `cancun`, `shanghai`, `paris`, `london`, etc... + /// Choose the hardfork by name, e.g. `prague`, `cancun`, `shanghai`, `paris`, `london`, etc... /// [default: latest] #[arg(long)] pub hardfork: Option, diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 1cde743c3e952..d86515f9f8fba 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -525,15 +525,15 @@ impl NodeConfig { /// Returns the hardfork to use pub fn get_hardfork(&self) -> ChainHardfork { if self.odyssey { - return ChainHardfork::Ethereum(EthereumHardfork::Prague); + return ChainHardfork::Ethereum(EthereumHardfork::default()); } if let Some(hardfork) = self.hardfork { return hardfork; } if self.enable_optimism { - return OpHardfork::Isthmus.into(); + return OpHardfork::default().into(); } - EthereumHardfork::Cancun.into() + EthereumHardfork::default().into() } /// Sets a custom code size limit diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index d4af986814ed6..5b4bcd78d2059 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -449,7 +449,7 @@ async fn can_get_node_info() { let block_number = provider.get_block_number().await.unwrap(); let block = provider.get_block(BlockId::from(block_number)).await.unwrap().unwrap(); - let hard_fork: &str = SpecId::CANCUN.into(); + let hard_fork: &str = SpecId::PRAGUE.into(); let expected_node_info = NodeInfo { current_block_number: 0_u64, From 7837b82a765a843bdfb97e1e7aea939de6937f9b Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Wed, 6 Aug 2025 11:28:25 -0700 Subject: [PATCH 051/374] deprecate forge config --basic --- crates/config/src/lib.rs | 132 --------------------------------- crates/forge/src/cmd/config.rs | 13 +--- crates/forge/tests/cli/cmd.rs | 10 +-- 3 files changed, 6 insertions(+), 149 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 2aa4cc7b61a41..b69d3116f9dfc 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1700,24 +1700,6 @@ impl Config { } } - /// Extracts a basic subset of the config, used for initialisations. - /// - /// # Example - /// - /// ```rust - /// use foundry_config::Config; - /// let my_config = Config::with_root(".").into_basic(); - /// ``` - pub fn into_basic(self) -> BasicConfig { - BasicConfig { - profile: self.profile, - src: self.src, - out: self.out, - libs: self.libs, - remappings: self.remappings, - } - } - /// Updates the `foundry.toml` file for the given `root` based on the provided closure. /// /// **Note:** the closure will only be invoked if the `foundry.toml` file exists, See @@ -2523,49 +2505,6 @@ impl> From for SolcReq { } } -/// A subset of the foundry `Config` -/// used to initialize a `foundry.toml` file -/// -/// # Example -/// -/// ```rust -/// use foundry_config::{BasicConfig, Config}; -/// use serde::Deserialize; -/// -/// let my_config = Config::figment().extract::(); -/// ``` -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct BasicConfig { - /// the profile tag: `[profile.default]` - #[serde(skip)] - pub profile: Profile, - /// path of the source contracts dir, like `src` or `contracts` - pub src: PathBuf, - /// path to where artifacts shut be written to - pub out: PathBuf, - /// all library folders to include, `lib`, `node_modules` - pub libs: Vec, - /// `Remappings` to use for this repo - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub remappings: Vec, -} - -impl BasicConfig { - /// Serialize the config as a String of TOML. - /// - /// This serializes to a table with the name of the profile - pub fn to_string_pretty(&self) -> Result { - let s = toml::to_string_pretty(self)?; - Ok(format!( - "\ -[profile.{}] -{s} -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options\n", - self.profile - )) - } -} - pub(crate) mod from_str_lowercase { use serde::{Deserialize, Deserializer, Serializer}; use std::str::FromStr; @@ -4021,52 +3960,6 @@ mod tests { }); } - #[test] - fn test_extract_basic() { - figment::Jail::expect_with(|jail| { - jail.create_file( - "foundry.toml", - r#" - [profile.default] - src = "mysrc" - out = "myout" - verbosity = 3 - evm_version = 'berlin' - - [profile.other] - src = "other-src" - "#, - )?; - let loaded = Config::load().unwrap(); - assert_eq!(loaded.evm_version, EvmVersion::Berlin); - let base = loaded.into_basic(); - let default = Config::default(); - assert_eq!( - base, - BasicConfig { - profile: Config::DEFAULT_PROFILE, - src: "mysrc".into(), - out: "myout".into(), - libs: default.libs.clone(), - remappings: default.remappings.clone(), - } - ); - jail.set_env("FOUNDRY_PROFILE", r"other"); - let base = Config::figment().extract::().unwrap(); - assert_eq!( - base, - BasicConfig { - profile: Config::DEFAULT_PROFILE, - src: "other-src".into(), - out: "myout".into(), - libs: default.libs.clone(), - remappings: default.remappings, - } - ); - Ok(()) - }); - } - #[test] #[should_panic] fn test_parse_invalid_fuzz_weight() { @@ -4622,31 +4515,6 @@ mod tests { }); } - #[test] - fn test_parse_with_profile() { - let foundry_str = 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 - "; - assert_eq!( - parse_with_profile::(foundry_str).unwrap().unwrap(), - ( - Config::DEFAULT_PROFILE, - BasicConfig { - profile: Config::DEFAULT_PROFILE, - src: "src".into(), - out: "out".into(), - libs: vec!["lib".into()], - remappings: vec![] - } - ) - ); - } - #[test] fn test_implicit_profile_loads() { figment::Jail::expect_with(|jail| { diff --git a/crates/forge/src/cmd/config.rs b/crates/forge/src/cmd/config.rs index c337e1fd4facc..d006c43525b2d 100644 --- a/crates/forge/src/cmd/config.rs +++ b/crates/forge/src/cmd/config.rs @@ -10,10 +10,6 @@ foundry_config::impl_figment_convert!(ConfigArgs, build, evm); /// CLI arguments for `forge config`. #[derive(Clone, Debug, Parser)] pub struct ConfigArgs { - /// Print only a basic set of the currently set config values. - #[arg(long)] - basic: bool, - /// Attempt to fix any configuration warnings. #[arg(long)] fix: bool, @@ -41,14 +37,7 @@ impl ConfigArgs { // 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(); - if shell::is_json() { - serde_json::to_string_pretty(&config)? - } else { - config.to_string_pretty()? - } - } else if shell::is_json() { + if shell::is_json() { serde_json::to_string_pretty(&config)? } else { config.to_string_pretty()? diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 0be5e77956fc5..4147839902a33 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::{ConfigurableContractArtifact, Metadata, remappings::Remapping}; use foundry_config::{ - BasicConfig, Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, parse_with_profile, + Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, parse_with_profile, }; use foundry_test_utils::{ foundry_compilers::PathStyle, @@ -278,7 +278,7 @@ Warning: Target directory is not empty, but `--force` was specified "#]]); let s = read_string(&foundry_toml); - let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; + let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; }); // Checks that a forge project fails to initialise if dir is already git repo and dirty @@ -638,7 +638,7 @@ Compiler run successful! "#]]); let s = read_string(&foundry_toml); - let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; + let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; }); // Checks that quiet mode does not print anything for clone @@ -686,7 +686,7 @@ Compiler run successful! "#]]); let s = read_string(&foundry_toml); - let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; + let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; }); // checks that clone works with --keep-directory-structure @@ -725,7 +725,7 @@ forgetest!(can_clone_keep_directory_structure, |prj, cmd| { } let s = read_string(&foundry_toml); - let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; + let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; }); // checks that clone works with raw src containing `node_modules` From 9e084ce8789a5f591ec8ce8168f405bfb8d9bc6f Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Wed, 6 Aug 2025 11:42:03 -0700 Subject: [PATCH 052/374] fix --- crates/config/assets/config.schema.json | 0 crates/forge/src/cmd/config.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 crates/config/assets/config.schema.json diff --git a/crates/config/assets/config.schema.json b/crates/config/assets/config.schema.json new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/forge/src/cmd/config.rs b/crates/forge/src/cmd/config.rs index d006c43525b2d..a7b65fe949194 100644 --- a/crates/forge/src/cmd/config.rs +++ b/crates/forge/src/cmd/config.rs @@ -37,13 +37,13 @@ impl ConfigArgs { // we explicitly normalize the version, so mimic the behavior when invoking solc .normalized_evm_version(); - if shell::is_json() { + let s = if shell::is_json() { serde_json::to_string_pretty(&config)? } else { config.to_string_pretty()? }; - sh_println!("{s}")?; + Ok(()) } } From 076ad02fd2aa8656769f5e90ac037344bd9de2a3 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Wed, 6 Aug 2025 12:45:03 -0700 Subject: [PATCH 053/374] start adding schema generation for config --- .cargo/config.toml | 6 +- Cargo.lock | 9 +++ Cargo.toml | 2 + .../cheatcodes/assets/cheatcodes.schema.json | 2 +- crates/cheatcodes/spec/src/lib.rs | 2 +- crates/config/assets/config.schema.json | 6 ++ crates/config/spec/Cargo.toml | 27 +++++++++ crates/config/spec/src/lib.rs | 60 +++++++++++++++++++ 8 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 crates/config/spec/Cargo.toml create mode 100644 crates/config/spec/src/lib.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 76cf725f9e2e2..d5ea9679ea410 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,11 @@ [alias] -cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-config = "test -p foundry-config-spec --features schema tests::" test-debugger = "test -p forge --test cli manual_debug_setup -- --include-ignored --nocapture" +# Backwards compatibility alias for `spec-cheats` +cheats = "spec-cheats" + # Increase the stack size to 10MB for Windows targets, which is in line with Linux # (whereas default for Windows is 1MB). [target.x86_64-pc-windows-msvc] diff --git a/Cargo.lock b/Cargo.lock index 2392a73ff20ac..f9e96e513c5ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4616,6 +4616,15 @@ dependencies = [ "yansi", ] +[[package]] +name = "foundry-config-spec" +version = "1.3.1" +dependencies = [ + "schemars 1.0.4", + "serde", + "serde_json", +] + [[package]] name = "foundry-debugger" version = "1.3.1" diff --git a/Cargo.toml b/Cargo.toml index ab299c998cec6..7364b02709bc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/cli/", "crates/common/", "crates/config/", + "crates/config/spec/", "crates/debugger/", "crates/doc/", "crates/evm/core/", @@ -190,6 +191,7 @@ foundry-cli = { path = "crates/cli" } foundry-common = { path = "crates/common" } foundry-common-fmt = { path = "crates/common/fmt" } foundry-config = { path = "crates/config" } +foundry-config-spec = { path = "crates/config/spec" } foundry-debugger = { path = "crates/debugger" } foundry-evm = { path = "crates/evm/evm" } foundry-evm-abi = { path = "crates/evm/abi" } diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index c98cfb69357bd..af1a09c38d109 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Cheatcodes", - "description": "Foundry cheatcodes. Learn more: ", + "description": "Foundry cheatcodes. Learn more: ", "type": "object", "properties": { "cheatcodes": { diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index b6d4e9e75aa2e..e35d0b9ded12f 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -19,7 +19,7 @@ mod vm; pub use vm::Vm; // The `cheatcodes.json` schema. -/// Foundry cheatcodes. Learn more: +/// Foundry cheatcodes. Learn more: #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] diff --git a/crates/config/assets/config.schema.json b/crates/config/assets/config.schema.json index e69de29bb2d1d..00a381eaf66ff 100644 --- a/crates/config/assets/config.schema.json +++ b/crates/config/assets/config.schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Config", + "description": "Foundry configuration. Learn more: ", + "type": "object" +} \ No newline at end of file diff --git a/crates/config/spec/Cargo.toml b/crates/config/spec/Cargo.toml new file mode 100644 index 0000000000000..ef9fb39a3f9be --- /dev/null +++ b/crates/config/spec/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "foundry-config-spec" +description = "Foundry configuration specification" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +serde.workspace = true + +# schema +schemars = { version = "1.0", optional = true } + +[dev-dependencies] +serde_json.workspace = true + +[features] +schema = ["dep:schemars"] diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs new file mode 100644 index 0000000000000..c04c20c79d428 --- /dev/null +++ b/crates/config/spec/src/lib.rs @@ -0,0 +1,60 @@ +//! Config specification for Foundry. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use serde::{Deserialize, Serialize}; + +// The `config.json` schema. +/// Foundry configuration. Learn more: +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct Config {} + +#[cfg(test)] +#[expect(clippy::disallowed_macros)] +mod tests { + use super::*; + use std::{fs, path::Path}; + + #[cfg(feature = "schema")] + const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/config.schema.json"); + + /// Generates the configuration JSON schema. + #[cfg(feature = "schema")] + fn json_schema() -> String { + serde_json::to_string_pretty(&schemars::schema_for!(Config)).unwrap() + } + + #[test] + #[cfg(feature = "schema")] + fn schema_up_to_date() { + ensure_file_contents(Path::new(SCHEMA_PATH), &json_schema()); + } + + /// Checks that the `file` has the specified `contents`. If that is not the + /// case, updates the file and then fails the test. + fn ensure_file_contents(file: &Path, contents: &str) { + if let Ok(old_contents) = fs::read_to_string(file) + && normalize_newlines(&old_contents) == normalize_newlines(contents) + { + // File is already up to date. + return; + } + + eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); + if std::env::var("CI").is_ok() { + eprintln!(" NOTE: run `cargo cheats` locally and commit the updated files\n"); + } + if let Some(parent) = file.parent() { + let _ = fs::create_dir_all(parent); + } + fs::write(file, contents).unwrap(); + panic!("some file was not up to date and has been updated, simply re-run the tests"); + } + + fn normalize_newlines(s: &str) -> String { + s.replace("\r\n", "\n") + } +} From 602d68d6d1e7f1b56acc1d27eb6ddfb583656e55 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Wed, 6 Aug 2025 13:17:19 -0700 Subject: [PATCH 054/374] naive implementation, restore basic config - relevant for use in forge init --- Cargo.lock | 2 + crates/cheatcodes/spec/src/lib.rs | 2 +- crates/config/Cargo.toml | 4 + crates/config/spec/Cargo.toml | 3 +- crates/config/spec/src/lib.rs | 12 ++- crates/config/src/lib.rs | 152 ++++++++++++++++++++++++++++++ crates/forge/src/cmd/config.rs | 13 ++- crates/forge/tests/cli/cmd.rs | 10 +- 8 files changed, 186 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9e96e513c5ac..4eba64171dd1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4600,6 +4600,7 @@ dependencies = [ "regex", "reqwest", "revm", + "schemars 1.0.4", "semver 1.0.26", "serde", "serde_json", @@ -4620,6 +4621,7 @@ dependencies = [ name = "foundry-config-spec" version = "1.3.1" dependencies = [ + "foundry-config", "schemars 1.0.4", "serde", "serde_json", diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index e35d0b9ded12f..ad2c2619b53d0 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -179,7 +179,7 @@ interface Vm {{ eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); if std::env::var("CI").is_ok() { - eprintln!(" NOTE: run `cargo cheats` locally and commit the updated files\n"); + eprintln!(" NOTE: run `cargo spec-cheats` locally and commit the updated files\n"); } if let Some(parent) = file.parent() { let _ = fs::create_dir_all(parent); diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 459dc0d9a75ec..892982a966960 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -49,6 +49,9 @@ walkdir.workspace = true yansi.workspace = true clap = { version = "4", features = ["derive"] } +# schema +schemars = { version = "1.0", optional = true } + [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2" @@ -59,3 +62,4 @@ tempfile.workspace = true [features] isolate-by-default = [] +schema = ["dep:schemars"] diff --git a/crates/config/spec/Cargo.toml b/crates/config/spec/Cargo.toml index ef9fb39a3f9be..89d40cf5606ce 100644 --- a/crates/config/spec/Cargo.toml +++ b/crates/config/spec/Cargo.toml @@ -15,6 +15,7 @@ exclude.workspace = true workspace = true [dependencies] +foundry-config.workspace = true serde.workspace = true # schema @@ -24,4 +25,4 @@ schemars = { version = "1.0", optional = true } serde_json.workspace = true [features] -schema = ["dep:schemars"] +schema = ["dep:schemars", "foundry-config/schema"] diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs index c04c20c79d428..5a362e963d956 100644 --- a/crates/config/spec/src/lib.rs +++ b/crates/config/spec/src/lib.rs @@ -3,14 +3,18 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +use foundry_config::Config; use serde::{Deserialize, Serialize}; // The `config.json` schema. /// Foundry configuration. Learn more: -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] -pub struct Config {} +pub struct ConfigSchema { + #[serde(flatten)] + pub config: Config, +} #[cfg(test)] #[expect(clippy::disallowed_macros)] @@ -24,7 +28,7 @@ mod tests { /// Generates the configuration JSON schema. #[cfg(feature = "schema")] fn json_schema() -> String { - serde_json::to_string_pretty(&schemars::schema_for!(Config)).unwrap() + serde_json::to_string_pretty(&schemars::schema_for!(ConfigSchema)).unwrap() } #[test] @@ -45,7 +49,7 @@ mod tests { eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); if std::env::var("CI").is_ok() { - eprintln!(" NOTE: run `cargo cheats` locally and commit the updated files\n"); + eprintln!(" NOTE: run `cargo spec-config` locally and commit the updated files\n"); } if let Some(parent) = file.parent() { let _ = fs::create_dir_all(parent); diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index b69d3116f9dfc..f36bd14bcda73 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -159,6 +159,7 @@ pub use compilation::{CompilationRestrictions, SettingsOverrides}; /// /// Note that these behaviors differ from those of [`Config::figment()`]. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Config { /// The selected profile. **(default: _default_ `default`)** /// @@ -1700,6 +1701,24 @@ impl Config { } } + /// Extracts a basic subset of the config, used for initialisations. + /// + /// # Example + /// + /// ```rust + /// use foundry_config::Config; + /// let my_config = Config::with_root(".").into_basic(); + /// ``` + pub fn into_basic(self) -> BasicConfig { + BasicConfig { + profile: self.profile, + src: self.src, + out: self.out, + libs: self.libs, + remappings: self.remappings, + } + } + /// Updates the `foundry.toml` file for the given `root` based on the provided closure. /// /// **Note:** the closure will only be invoked if the `foundry.toml` file exists, See @@ -2505,6 +2524,49 @@ impl> From for SolcReq { } } +/// A subset of the foundry `Config` +/// used to initialize a `foundry.toml` file +/// +/// # Example +/// +/// ```rust +/// use foundry_config::{BasicConfig, Config}; +/// use serde::Deserialize; +/// +/// let my_config = Config::figment().extract::(); +/// ``` +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BasicConfig { + /// the profile tag: `[profile.default]` + #[serde(skip)] + pub profile: Profile, + /// path of the source contracts dir, like `src` or `contracts` + pub src: PathBuf, + /// path to where artifacts shut be written to + pub out: PathBuf, + /// all library folders to include, `lib`, `node_modules` + pub libs: Vec, + /// `Remappings` to use for this repo + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub remappings: Vec, +} + +impl BasicConfig { + /// Serialize the config as a String of TOML. + /// + /// This serializes to a table with the name of the profile + pub fn to_string_pretty(&self) -> Result { + let s = toml::to_string_pretty(self)?; + Ok(format!( + "\ +[profile.{}] +{s} +# See more config options: https://getfoundry.sh/config/reference/default-config\n", + self.profile + )) + } +} + pub(crate) mod from_str_lowercase { use serde::{Deserialize, Deserializer, Serializer}; use std::str::FromStr; @@ -3960,6 +4022,51 @@ mod tests { }); } + #[test] + fn test_extract_basic() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "mysrc" + out = "myout" + verbosity = 3 + evm_version = 'berlin' + [profile.other] + src = "other-src" + "#, + )?; + let loaded = Config::load().unwrap(); + assert_eq!(loaded.evm_version, EvmVersion::Berlin); + let base = loaded.into_basic(); + let default = Config::default(); + assert_eq!( + base, + BasicConfig { + profile: Config::DEFAULT_PROFILE, + src: "mysrc".into(), + out: "myout".into(), + libs: default.libs.clone(), + remappings: default.remappings.clone(), + } + ); + jail.set_env("FOUNDRY_PROFILE", r"other"); + let base = Config::figment().extract::().unwrap(); + assert_eq!( + base, + BasicConfig { + profile: Config::DEFAULT_PROFILE, + src: "other-src".into(), + out: "myout".into(), + libs: default.libs.clone(), + remappings: default.remappings, + } + ); + Ok(()) + }); + } + #[test] #[should_panic] fn test_parse_invalid_fuzz_weight() { @@ -4515,6 +4622,51 @@ mod tests { }); } + #[test] + fn test_extract_basic() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "mysrc" + out = "myout" + verbosity = 3 + evm_version = 'berlin' + [profile.other] + src = "other-src" + "#, + )?; + let loaded = Config::load().unwrap(); + assert_eq!(loaded.evm_version, EvmVersion::Berlin); + let base = loaded.into_basic(); + let default = Config::default(); + assert_eq!( + base, + BasicConfig { + profile: Config::DEFAULT_PROFILE, + src: "mysrc".into(), + out: "myout".into(), + libs: default.libs.clone(), + remappings: default.remappings.clone(), + } + ); + jail.set_env("FOUNDRY_PROFILE", r"other"); + let base = Config::figment().extract::().unwrap(); + assert_eq!( + base, + BasicConfig { + profile: Config::DEFAULT_PROFILE, + src: "other-src".into(), + out: "myout".into(), + libs: default.libs.clone(), + remappings: default.remappings, + } + ); + Ok(()) + }); + } + #[test] fn test_implicit_profile_loads() { figment::Jail::expect_with(|jail| { diff --git a/crates/forge/src/cmd/config.rs b/crates/forge/src/cmd/config.rs index a7b65fe949194..89a08bff361a0 100644 --- a/crates/forge/src/cmd/config.rs +++ b/crates/forge/src/cmd/config.rs @@ -10,6 +10,10 @@ foundry_config::impl_figment_convert!(ConfigArgs, build, evm); /// CLI arguments for `forge config`. #[derive(Clone, Debug, Parser)] pub struct ConfigArgs { + /// Print only a basic set of the currently set config values. + #[arg(long)] + basic: bool, + /// Attempt to fix any configuration warnings. #[arg(long)] fix: bool, @@ -37,7 +41,14 @@ impl ConfigArgs { // we explicitly normalize the version, so mimic the behavior when invoking solc .normalized_evm_version(); - let s = if shell::is_json() { + let s = if self.basic { + let config = config.into_basic(); + if shell::is_json() { + serde_json::to_string_pretty(&config)? + } else { + config.to_string_pretty()? + } + } else if shell::is_json() { serde_json::to_string_pretty(&config)? } else { config.to_string_pretty()? diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 4147839902a33..0be5e77956fc5 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::{ConfigurableContractArtifact, Metadata, remappings::Remapping}; use foundry_config::{ - Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, parse_with_profile, + BasicConfig, Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, parse_with_profile, }; use foundry_test_utils::{ foundry_compilers::PathStyle, @@ -278,7 +278,7 @@ Warning: Target directory is not empty, but `--force` was specified "#]]); let s = read_string(&foundry_toml); - let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; }); // Checks that a forge project fails to initialise if dir is already git repo and dirty @@ -638,7 +638,7 @@ Compiler run successful! "#]]); let s = read_string(&foundry_toml); - let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; }); // Checks that quiet mode does not print anything for clone @@ -686,7 +686,7 @@ Compiler run successful! "#]]); let s = read_string(&foundry_toml); - let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; }); // checks that clone works with --keep-directory-structure @@ -725,7 +725,7 @@ forgetest!(can_clone_keep_directory_structure, |prj, cmd| { } let s = read_string(&foundry_toml); - let _config: Config = parse_with_profile(&s).unwrap().unwrap().1; + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; }); // checks that clone works with raw src containing `node_modules` From 17b50b4242c36fa902d2c31f8d1b53b17bed0b31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 02:00:01 +0700 Subject: [PATCH 055/374] chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 76 +++++++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fa7e6a150282..c6cae03ce7321 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1074,7 +1074,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", "yansi", ] @@ -2225,7 +2225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", - "regex-automata 0.4.9", + "regex-automata", "serde", ] @@ -2475,7 +2475,7 @@ dependencies = [ "time", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", "walkdir", "yansi", ] @@ -3896,7 +3896,7 @@ dependencies = [ "thiserror 2.0.12", "toml 0.8.22", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", ] [[package]] @@ -4153,7 +4153,7 @@ dependencies = [ "tikv-jemallocator", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", "tracing-tracy", "tracy-client", "yansi", @@ -4619,7 +4619,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", "ui_test", "zip-extract", ] @@ -4898,8 +4898,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] @@ -5290,7 +5290,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.9", + "regex-automata", "same-file", "walkdir", "winapi-util", @@ -5668,7 +5668,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.5", + "regex-syntax", "sha3", "string_cache", "term", @@ -5682,7 +5682,7 @@ version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" dependencies = [ - "regex-automata 0.4.9", + "regex-automata", "rustversion", ] @@ -5866,7 +5866,7 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", ] [[package]] @@ -5967,11 +5967,11 @@ dependencies = [ [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -6254,12 +6254,11 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -6561,12 +6560,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owo-colors" version = "3.5.0" @@ -7105,7 +7098,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.5", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -7463,17 +7456,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -7484,7 +7468,7 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] @@ -7493,12 +7477,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -9598,7 +9576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", ] [[package]] @@ -9635,14 +9613,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -9658,7 +9636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba" dependencies = [ "tracing-core", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.20", "tracy-client", ] From fcfa587cbfce70b8eb32ac87207cd8a2e2c4f726 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 7 Sep 2025 23:17:57 +0700 Subject: [PATCH 056/374] Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4d5d2d361f5a..716abb3648f13 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,8 +4,10 @@ on: push: branches: - master + - main + - dev + pull_request: - concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true From 5973becffce4febb02046b125adb408c5c4d5f3c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 8 Sep 2025 23:12:20 +0700 Subject: [PATCH 057/374] Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index e0c9c518e8748..9c2e6a62fd947 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,21 +1,15 @@ name: Docker Image CI - on: push: branches: [ "master" ] pull_request: branches: [ "master" ] - permissions: - contents: read - + contents: read jobs: - build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} + run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From 70c73b4b5afaafeb5c587e690df25f6f93f2e94c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 13 Sep 2025 04:33:10 +0700 Subject: [PATCH 058/374] Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .circleci/ci.yml diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..d5d401c51893c --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello From 913e6393fab483e6ddd9906fe97faab4939577fb Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 13 Sep 2025 16:55:29 +0700 Subject: [PATCH 059/374] Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_cargo.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .circleci/ci_cargo.yml diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test From 8575916b7675f246b54daf70cfddccb3f5b97fb0 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:39:46 +0700 Subject: [PATCH 060/374] Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/web3_defi_gamefi.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .circleci/web3_defi_gamefi.yml diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- From a07134ed485f776c739b0809a93a8c3c81add390 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:51:21 +0700 Subject: [PATCH 061/374] Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/.circleci/ci.yml b/.circleci/ci.yml index d5d401c51893c..b908ef7ada8fb 100644 --- a/.circleci/ci.yml +++ b/.circleci/ci.yml @@ -1,31 +1,14 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + build_and_test: docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + - image: cimg/rust:1.75.0 steps: - # Checkout the code as the first step. - checkout - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello + name: "Check formatting" + command: "cargo fmt --all -- --check" + - run: + name: "Run Clippy" + command: "cargo clippy --all-targets -- -D warnings" + - run: + name: "Run tests" + command: "cargo test --all-features" From bb76ae4cbcc85360612363a7934d07c1422331a8 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 16 Sep 2025 03:39:32 +0700 Subject: [PATCH 062/374] Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..76b2889f1c4b2 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + From 9c4a6353ac372fd499792e48fbfc01ec247f1a11 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 22 Sep 2025 01:41:52 +0700 Subject: [PATCH 063/374] Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/dependencies.yml | 50 +++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index e452a68c80967..53e2ca70b23b0 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -7,14 +7,48 @@ on: # Run weekly - cron: "0 0 * * SUN" workflow_dispatch: -# Needed so we can run it manually - -permissions: - contents: write - pull-requests: write + # 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. +
cargo update log +

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

+
jobs: update: - uses: ithacaxyz/ci/.github/workflows/cargo-update-pr.yml@main - secrets: - token: ${{ secrets.GITHUB_TOKEN }} + 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 }} From a2eb55138f3a3eb17f33e363c608d99f67a6bffd Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 22 Sep 2025 01:43:43 +0700 Subject: [PATCH 064/374] Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/dependencies.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 53e2ca70b23b0..184e5d13f2c73 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -26,6 +26,9 @@ jobs: update: name: Update runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly From 58a14551db4bf60eaf2dfca242e6e9a062e5e3e8 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 16 Sep 2025 05:14:25 +0700 Subject: [PATCH 065/374] Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_cargo.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .circleci/ci_cargo.yml diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test From 82923a7714a069007d6cc2770e5fa908276e258c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:46:59 +0700 Subject: [PATCH 066/374] Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..f967cfaa30db5 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello From f0b1a99ac7c7d91ee2e6421f63a8666b2ab42ec5 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:00:39 +0700 Subject: [PATCH 067/374] Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/apisec-scan.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml index 5875cc28ae724..166d2f8ee0235 100644 --- a/.github/workflows/apisec-scan.yml +++ b/.github/workflows/apisec-scan.yml @@ -1,4 +1,6 @@ name: APIsec +permissions: + contents: read on: pull_request: From ab7fffd8da41fed1a84a7173198f107490548ed8 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:03:11 +0700 Subject: [PATCH 068/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index d66f340d8c6fc..7bd478a210b3e 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -163,7 +163,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap()) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) .map(|(_, data)| data) } From 33594a198c8f0aeee0a28da299af89b275b745fd Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 02:46:12 +0700 Subject: [PATCH 069/374] Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/nix.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 5e9659e777e54..69c51935102c7 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -1,5 +1,8 @@ name: nix +permissions: + contents: read + on: schedule: # Run weekly @@ -14,6 +17,9 @@ concurrency: jobs: # Opens a PR with an updated flake.lock file update: + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest steps: - uses: DeterminateSystems/determinate-nix-action@v3 From 12807f145aad8c6e3821f99196b054865fbd9e59 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 02:46:33 +0700 Subject: [PATCH 070/374] Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/npm.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 6d377de185359..bef8b7bedd0af 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -66,7 +66,7 @@ jobs: merge-multiple: true # Download all foundry artifacts from the triggering release run pattern: "foundry_*" - path: foundry_artifacts + path: ${{ runner.temp }}/foundry_artifacts github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id || inputs.run_id }} @@ -103,11 +103,11 @@ jobs: set -euo pipefail echo "Artifacts in foundry_artifacts:" - ls -la ../foundry_artifacts || true + ls -la ${{ runner.temp }}/foundry_artifacts || true # Derive RELEASE_VERSION from any foundry artifact we downloaded # Expected names: foundry___.{tar.gz,zip} - first_file=$(ls ../foundry_artifacts/foundry_* 2>/dev/null | head -n1 || true) + first_file=$(ls "${{ runner.temp }}/foundry_artifacts/foundry_"* 2>/dev/null | head -n1 || true) if [[ -z "${first_file}" ]]; then echo "No foundry artifacts found to publish" >&2 exit 1 From 6ff615c85092301c5e9478f372caa5cc54869fee Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Oct 2025 23:40:41 +0000 Subject: [PATCH 071/374] Create config.yml (#105) --- .circleci/config.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..d5d401c51893c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello From 251a2b4fce0c50e3426ffb2022d9abef5b948fa9 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Oct 2025 23:51:17 +0000 Subject: [PATCH 072/374] Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test From 6afa4a42b716ac74d0c107f15b9ff887413877b4 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 00:17:22 +0000 Subject: [PATCH 073/374] Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/.circleci/ci.yml b/.circleci/ci.yml index b908ef7ada8fb..7293433a50f2d 100644 --- a/.circleci/ci.yml +++ b/.circleci/ci.yml @@ -1,14 +1,32 @@ - build_and_test: +version: 2.1 + +jobs: + build-and-test: docker: - - image: cimg/rust:1.75.0 + - image: cimg/rust:1.88.0 steps: - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- - run: name: "Check formatting" - command: "cargo fmt --all -- --check" + command: cargo fmt -- --check - run: - name: "Run Clippy" - command: "cargo clippy --all-targets -- -D warnings" + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check - run: name: "Run tests" - command: "cargo test --all-features" + command: cargo test From 85cbe70b54d9df09e27033011ad799e6a28c7310 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 07:30:55 +0700 Subject: [PATCH 074/374] Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index e0c9c518e8748..0000000000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -permissions: - contents: read - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From bf979a354ebcd03740d081461860793c38d2244c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 02:28:17 +0000 Subject: [PATCH 075/374] Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..d5d401c51893c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello From b95fcbe71c234ad4f7fb16ea331f32e420fb0dbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:20:11 +0700 Subject: [PATCH 076/374] chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 94526a89f54d4..25e86624d1c8c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -189,12 +189,12 @@ jobs: with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" From 857e8adf9d575274aa269cfbc31c79490d35914c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:23:20 +0700 Subject: [PATCH 077/374] chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 09810ac02b208..1667f1096deed 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,7 +19,7 @@ jobs: contents: write pull-requests: write steps: - - uses: DeterminateSystems/determinate-nix-action@dbda91f6efef3ee627f56175120aa9543687d830 # v3 + - uses: DeterminateSystems/determinate-nix-action@762d7fdba79d046449732c729c1d3aaad021baa2 # v3 - uses: actions/checkout@v5 with: persist-credentials: false @@ -38,7 +38,7 @@ jobs: permissions: contents: read steps: - - uses: DeterminateSystems/determinate-nix-action@dbda91f6efef3ee627f56175120aa9543687d830 # v3 + - uses: DeterminateSystems/determinate-nix-action@762d7fdba79d046449732c729c1d3aaad021baa2 # v3 - uses: actions/checkout@v5 with: persist-credentials: false From 3fa1ae4f94a2bc4067d3dc27e2c561079b59f97e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:25:41 +0700 Subject: [PATCH 078/374] chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25e86624d1c8c..f7e8d00e8cda2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,7 +92,7 @@ jobs: - uses: actions/checkout@v5 with: persist-credentials: false - - uses: crate-ci/typos@83157de2df0fa7c7ae20f73f9dbed44c41f2bb64 # v1 + - uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1 # v1 clippy: runs-on: depot-ubuntu-latest From 35729e26795c6161c162b1b0dd59db9b07e0f635 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:43:48 +0700 Subject: [PATCH 079/374] chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 589d38e8014c5..c4d553c80a428 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -267,7 +267,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4 + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -282,7 +282,7 @@ jobs: # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release if: ${{ env.IS_NIGHTLY == 'true' }} - uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4 + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: name: "Nightly" tag_name: "nightly" From ee8bebac3a817ad0722e61ac79c727193ec5acd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:45:19 +0700 Subject: [PATCH 080/374] chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nextest.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index 5d986107519d9..a5513e9398fdc 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -67,7 +67,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2 + - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 with: tool: nextest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7e8d00e8cda2..e0424c485b067 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -159,7 +159,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2 + - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 From f96ff7748a4adf71e1082b02e716da5c4157ecd4 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:25:07 +0000 Subject: [PATCH 081/374] Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e0424c485b067..94526a89f54d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,7 +92,7 @@ jobs: - uses: actions/checkout@v5 with: persist-credentials: false - - uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1 # v1 + - uses: crate-ci/typos@83157de2df0fa7c7ae20f73f9dbed44c41f2bb64 # v1 clippy: runs-on: depot-ubuntu-latest @@ -159,7 +159,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 + - uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 @@ -189,12 +189,12 @@ jobs: with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" From 9c9343ad7b53f359ec32c65a31bf69c3f4b208e9 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 19:31:57 +0700 Subject: [PATCH 082/374] Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- crates/doc/src/writer/buf_writer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index d1e51dfeca6e0..e5dfbbaadce25 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -213,7 +213,7 @@ impl BufWriter { }); let row = [ - Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + Markdown::Code(param_name.as_deref().unwrap_or("")).as_doc()?, comment.unwrap_or_default().replace('\n', " "), ]; self.write_piped(&row.join("|"))?; From fb852fce06ecb0cea856cb1d238697cc6dc74abf Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 00:25:54 +0700 Subject: [PATCH 083/374] Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d5d401c51893c..790c879119cd6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,31 +1,17 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference version: 2.1 -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs jobs: say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base + docker: - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: - # Checkout the code as the first step. - checkout - run: name: "Say hello" command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows + workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. + say-hello-workflow: jobs: - say-hello From 4a7067157f3813c366b07106e0bbe2534efab823 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:28:54 +0700 Subject: [PATCH 084/374] Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/{ci_cargo.yml => cargo.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{ci_cargo.yml => cargo.yml} (100%) diff --git a/.circleci/ci_cargo.yml b/.circleci/cargo.yml similarity index 100% rename from .circleci/ci_cargo.yml rename to .circleci/cargo.yml From 2b20d9d4ccd0e79ba9d3660e172ee455084a76fb Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:31:48 +0700 Subject: [PATCH 085/374] Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/{config.yml => ci.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{config.yml => ci.yml} (100%) diff --git a/.circleci/config.yml b/.circleci/ci.yml similarity index 100% rename from .circleci/config.yml rename to .circleci/ci.yml From e2b8aa9d6184931920d28f0cdfc7dfb6f4131871 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:36:47 +0700 Subject: [PATCH 086/374] Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/{ci_cargo.yml => ci_v1.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{ci_cargo.yml => ci_v1.yml} (100%) diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_v1.yml similarity index 100% rename from .circleci/ci_cargo.yml rename to .circleci/ci_v1.yml From d4b621fc308e595e484eab989b403c38f36540d8 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:55:28 +0700 Subject: [PATCH 087/374] Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_v1.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml index 46a18d45a5fca..94bf46b3bb04f 100644 --- a/.circleci/ci_v1.yml +++ b/.circleci/ci_v1.yml @@ -24,12 +24,6 @@ jobs: - "~/.cargo/registry/cache" - "~/.cargo/git/db" - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test workflows: ci: From b5585aebcd03672a0efd9054ce5502d750097c90 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 21:18:46 +0000 Subject: [PATCH 088/374] Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .circleci/ci.yml | 31 +++++++++++++++++++++++++++++++ .circleci/ci_v1.yml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 .circleci/ci.yml create mode 100644 .circleci/ci_v1.yml diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..f967cfaa30db5 --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..94bf46b3bb04f --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test From b67d5891aa6dee2a15637794483188a656d1852c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Oct 2025 21:34:13 +0000 Subject: [PATCH 089/374] Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ++++++++++++++++++++++++++++++++++ .circleci/config.yml | 17 ----------------- 2 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 .circleci/ci_deploy.yml delete mode 100644 .circleci/config.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 790c879119cd6..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello From d39b7a5e51705fe2b696f6794e0adfc047309402 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:54:08 +0700 Subject: [PATCH 090/374] Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/snyk-container.yml | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/snyk-container.yml diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..0db31480beb38 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@14818c4695ecc4045f33c9cee9e795a788711ca4 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: snyk.sarif From a767f09584ca1598d02e8e93dedea07129f88523 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:14:19 +0700 Subject: [PATCH 091/374] Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/{ci.yml => ci-say-hello.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{ci.yml => ci-say-hello.yml} (100%) diff --git a/.circleci/ci.yml b/.circleci/ci-say-hello.yml similarity index 100% rename from .circleci/ci.yml rename to .circleci/ci-say-hello.yml From 93f1ac838589770bd4a6826b5807c75cf0961806 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:18:58 +0700 Subject: [PATCH 092/374] Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 94526a89f54d4..76b4367363adb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: docs: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -55,7 +55,7 @@ jobs: if: github.ref_name == 'master' && github.event_name == 'push' needs: [docs] runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: pages: write id-token: write @@ -69,7 +69,7 @@ jobs: doctest: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -85,7 +85,7 @@ jobs: typos: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -96,7 +96,7 @@ jobs: clippy: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -115,7 +115,7 @@ jobs: rustfmt: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -130,7 +130,7 @@ jobs: forge-fmt: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -148,7 +148,7 @@ jobs: crate-checks: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -213,7 +213,7 @@ jobs: - crate-checks - deny - codeql - timeout-minutes: 30 + timeout-minutes: 5 steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1 From cac7793c527951b86839c24d55156f60c27b695c Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 15 Oct 2025 22:14:25 +0000 Subject: [PATCH 093/374] Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.ym | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .circleci/config.ym diff --git a/.circleci/config.ym b/.circleci/config.ym new file mode 100644 index 0000000000000..709c9a7474d04 --- /dev/null +++ b/.circleci/config.ym @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/hardhat-project/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- From 95d4a0b5ac6da5fefadd287ef1e07f3a07e8104f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 06:11:14 +0700 Subject: [PATCH 094/374] chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- Cargo.lock | 36 ++++++++++++++++++------------------ Cargo.toml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fb563560bbbc..3a48463ff8597 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6c2905bafc2df7ccd32ca3af13f0b0d82f2e2ff9dfbeb12196c0d978d5c0deb" +checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2acb6637a9c0e1cdf8971e0ced8f3fa34c04c5e9dccf6bb184f6a64fe0e37d8" +checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -383,9 +383,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b77f7d5e60ad8ae6bd2200b8097919712a07a6db622a4b201e7ead6166f02e5" +checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" dependencies = [ "alloy-rlp", "arbitrary", @@ -784,9 +784,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c84c3637bee9b5c4a4d2b93360ee16553d299c3b932712353caf1cea76d0e6" +checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -798,9 +798,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a882aa4e1790063362434b9b40d358942b188477ac1c44cfb8a52816ffc0cc17" +checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -817,9 +817,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5772107f9bb265d8d8c86e0733937bb20d0857ea5425b1b6ddf51a9804042" +checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ "alloy-json-abi", "const-hex", @@ -835,9 +835,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e188b939aa4793edfaaa099cb1be4e620036a775b4bdf24fdc56f1cd6fd45890" +checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" dependencies = [ "serde", "winnow", @@ -845,9 +845,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c8a9a909872097caffc05df134e5ef2253a1cdb56d3a9cf0052a042ac763f9" +checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -9674,9 +9674,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2375c17f6067adc651d8c2c51658019cef32edfff4a982adaf1d7fd1c039f08b" +checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index a69d750b6eb2a..6c29441366881 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,7 +259,7 @@ alloy-hardforks = { version = "0.3.2", default-features = false } alloy-op-hardforks = { version = "0.3.2", default-features = false } ## alloy-core -alloy-dyn-abi = "1.3.1" +alloy-dyn-abi = "1.4.1" alloy-json-abi = "1.3.0" alloy-primitives = { version = "1.3.1", features = [ "getrandom", From 0a0a62a2f9db40b779cbf77b8c074e464c916a06 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 00:25:27 +0000 Subject: [PATCH 095/374] Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..d7a82b5c93b6e --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test From 343afb6fad0635fff7f09f7f6580276f608bdff5 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:01:53 +0700 Subject: [PATCH 096/374] Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/{config.ym => config.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{config.ym => config.yml} (100%) diff --git a/.circleci/config.ym b/.circleci/config.yml similarity index 100% rename from .circleci/config.ym rename to .circleci/config.yml From 9f64c477f81e02b10dc2bc39f62a86f20de8e72f Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:13:58 +0700 Subject: [PATCH 097/374] Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 709c9a7474d04..3b59d59e57c17 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,6 +17,7 @@ jobs: executor: my-custom-executor steps: - checkout + - run: echo "Build started" - run: | # echo Hello, World! From 1c5dcafbe1b4931c8a891067874d22a5c16caec9 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 07:49:33 +0000 Subject: [PATCH 098/374] fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .circleci/cargo.yml | 37 +++++++++++++++++++ .circleci/ci-say-hello.yml | 31 ++++++++++++++++ .circleci/ci_deploy.yml | 34 +++++++++++++++++ .circleci/ci_v1.yml | 31 ++++++++++++++++ .circleci/config.yml | 27 ++++++++++++++ .github/workflows/release.yml | 4 +- .github/workflows/snyk-container.yml | 55 ++++++++++++++++++++++++++++ .github/workflows/test.yml | 26 ++++++------- 8 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 .circleci/cargo.yml create mode 100644 .circleci/ci-say-hello.yml create mode 100644 .circleci/ci_deploy.yml create mode 100644 .circleci/ci_v1.yml create mode 100644 .circleci/config.yml create mode 100644 .github/workflows/snyk-container.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..d7a82b5c93b6e --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci-say-hello.yml b/.circleci/ci-say-hello.yml new file mode 100644 index 0000000000000..f967cfaa30db5 --- /dev/null +++ b/.circleci/ci-say-hello.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..94bf46b3bb04f --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..3b59d59e57c17 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,27 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/hardhat-project/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: echo "Build started" + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d03c82751e72b..06061ff6c7508 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -267,7 +267,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4 + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -282,7 +282,7 @@ jobs: # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release if: ${{ env.IS_NIGHTLY == 'true' }} - uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4 + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: name: "Nightly" tag_name: "nightly" diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..0db31480beb38 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@14818c4695ecc4045f33c9cee9e795a788711ca4 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: snyk.sarif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e0424c485b067..76b4367363adb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: docs: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -55,7 +55,7 @@ jobs: if: github.ref_name == 'master' && github.event_name == 'push' needs: [docs] runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: pages: write id-token: write @@ -69,7 +69,7 @@ jobs: doctest: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -85,18 +85,18 @@ jobs: typos: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: - uses: actions/checkout@v5 with: persist-credentials: false - - uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1 # v1 + - uses: crate-ci/typos@83157de2df0fa7c7ae20f73f9dbed44c41f2bb64 # v1 clippy: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -115,7 +115,7 @@ jobs: rustfmt: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -130,7 +130,7 @@ jobs: forge-fmt: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -148,7 +148,7 @@ jobs: crate-checks: runs-on: depot-ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 permissions: contents: read steps: @@ -159,7 +159,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 + - uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 @@ -189,12 +189,12 @@ jobs: with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" @@ -213,7 +213,7 @@ jobs: - crate-checks - deny - codeql - timeout-minutes: 30 + timeout-minutes: 5 steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1 From 5b4a4997ef80f7f75dda19032c4f879f4905ead8 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:58:49 +0700 Subject: [PATCH 099/374] Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> From 8d1f66a47982e14d54d63b48e762ecd9b6f7cf8e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 17:00:26 +0700 Subject: [PATCH 100/374] Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> From 93a4079269bcc9e83abb5c4da5d2b3460e8a8075 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 17:00:48 +0700 Subject: [PATCH 101/374] Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> From b8b5fbb83fa2436063cebc34ddf900abc972b11d Mon Sep 17 00:00:00 2001 From: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Date: Thu, 16 Oct 2025 12:57:08 +0200 Subject: [PATCH 102/374] fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance --- crates/fmt/src/state/sol.rs | 34 +++++++++++++------ .../bracket-spacing.fmt.sol | 12 +++++++ .../contract-new-lines.fmt.sol | 12 +++++++ .../fmt/testdata/ContractDefinition/fmt.sol | 12 +++++++ .../testdata/ContractDefinition/original.sol | 14 +++++++- 5 files changed, 72 insertions(+), 12 deletions(-) diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index f5c01bb85b619..21664445ee1c0 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -288,6 +288,14 @@ impl<'ast> State<'_, 'ast> { self.print_ident(name); self.nbsp(); + if let Some(layout) = layout + && !self.handle_span(layout.span, false) + { + self.word("layout at "); + self.print_expr(layout.slot); + self.print_sep(Separator::Space); + } + if let Some(first) = bases.first().map(|base| base.span()) && let Some(last) = bases.last().map(|base| base.span()) && self.inline_config.is_disabled(first.to(last)) @@ -296,26 +304,30 @@ impl<'ast> State<'_, 'ast> { } else if !bases.is_empty() { self.word("is"); self.space(); - for (pos, base) in bases.iter().delimited() { + let last = bases.len() - 1; + for (i, base) in bases.iter().enumerate() { if !self.handle_span(base.span(), false) { self.print_modifier_call(base, false); - if !pos.is_last { + if i != last { self.word(","); - self.space(); + if self + .print_comments( + bases[i + 1].span().lo(), + CommentConfig::skip_ws().mixed_prev_space().mixed_post_nbsp(), + ) + .is_none() + { + self.space(); + } } } } - self.space(); + if !self.print_trailing_comment(bases.last().unwrap().span().hi(), None) { + self.space(); + } self.s.offset(-self.ind); } self.end(); - if let Some(layout) = layout - && !self.handle_span(layout.span, false) - { - self.word("layout at "); - self.print_expr(layout.slot); - self.print_sep(Separator::Space); - } self.print_word("{"); self.end(); diff --git a/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol index 20894a47a3262..25074229db558 100644 --- a/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol @@ -39,3 +39,15 @@ contract ERC20DecimalsMock is ERC20 { _decimals = decimals_; } } + +contract SomeContract is + ERC165Upgradeable, // 1 inherited component + ISomeContract // 4 inherited components +{ } + +contract AnotherContract is + Adminable, /* 1 inherited components */ + UUPSUpgradeable /* 1 inherited component */ +{ } + +contract WithLayoutAndBase layout at 69 is Base { } diff --git a/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol b/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol index 2e9661f956dcf..990845d3d1349 100644 --- a/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol @@ -50,3 +50,15 @@ contract ERC20DecimalsMock is ERC20 { } } + +contract SomeContract is + ERC165Upgradeable, // 1 inherited component + ISomeContract // 4 inherited components +{} + +contract AnotherContract is + Adminable, /* 1 inherited components */ + UUPSUpgradeable /* 1 inherited component */ +{} + +contract WithLayoutAndBase layout at 69 is Base {} diff --git a/crates/fmt/testdata/ContractDefinition/fmt.sol b/crates/fmt/testdata/ContractDefinition/fmt.sol index 551e84decfc5b..93cddcd2c6a20 100644 --- a/crates/fmt/testdata/ContractDefinition/fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/fmt.sol @@ -45,3 +45,15 @@ contract ERC20DecimalsMock is ERC20 { _decimals = decimals_; } } + +contract SomeContract is + ERC165Upgradeable, // 1 inherited component + ISomeContract // 4 inherited components +{} + +contract AnotherContract is + Adminable, /* 1 inherited components */ + UUPSUpgradeable /* 1 inherited component */ +{} + +contract WithLayoutAndBase layout at 69 is Base {} diff --git a/crates/fmt/testdata/ContractDefinition/original.sol b/crates/fmt/testdata/ContractDefinition/original.sol index 4c671985bda7b..c0aa88a7d6d17 100644 --- a/crates/fmt/testdata/ContractDefinition/original.sol +++ b/crates/fmt/testdata/ContractDefinition/original.sol @@ -17,7 +17,7 @@ contract SampleContract { // comment 16 external /* comment 17 */ pure - returns(uint256) + returns(uint256) // comment 18 { // comment 19 return arg1 > arg2 ? arg1 : arg2; @@ -38,3 +38,15 @@ contract ERC20DecimalsMock is ERC20 { _decimals = decimals_; } } + +contract SomeContract is + ERC165Upgradeable, // 1 inherited component + ISomeContract // 4 inherited components +{ } + +contract AnotherContract is + Adminable, /* 1 inherited components */ + UUPSUpgradeable /* 1 inherited component */ +{ } + +contract WithLayoutAndBase layout at 69 is Base {} From c35961b39146e60ad4f76f912cc383e5852842c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 20:07:12 +0700 Subject: [PATCH 103/374] chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nextest.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index a5513e9398fdc..d39b597f7613e 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -67,7 +67,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 + - uses: taiki-e/install-action@0005e0116e92d8489d8d96fbff83f061c79ba95a # v2 with: tool: nextest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 76b4367363adb..aa505a05a8382 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -159,7 +159,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2 + - uses: taiki-e/install-action@0005e0116e92d8489d8d96fbff83f061c79ba95a # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 From 79ead878042ea6f1cb9ba2cefb02d25d8ac8601e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 20:12:28 +0700 Subject: [PATCH 104/374] Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. --- crates/fmt/src/state/sol.rs | 34 ++++++------------- .../bracket-spacing.fmt.sol | 12 ------- .../contract-new-lines.fmt.sol | 12 ------- .../fmt/testdata/ContractDefinition/fmt.sol | 12 ------- .../testdata/ContractDefinition/original.sol | 14 +------- 5 files changed, 12 insertions(+), 72 deletions(-) diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index 21664445ee1c0..f5c01bb85b619 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -288,14 +288,6 @@ impl<'ast> State<'_, 'ast> { self.print_ident(name); self.nbsp(); - if let Some(layout) = layout - && !self.handle_span(layout.span, false) - { - self.word("layout at "); - self.print_expr(layout.slot); - self.print_sep(Separator::Space); - } - if let Some(first) = bases.first().map(|base| base.span()) && let Some(last) = bases.last().map(|base| base.span()) && self.inline_config.is_disabled(first.to(last)) @@ -304,30 +296,26 @@ impl<'ast> State<'_, 'ast> { } else if !bases.is_empty() { self.word("is"); self.space(); - let last = bases.len() - 1; - for (i, base) in bases.iter().enumerate() { + for (pos, base) in bases.iter().delimited() { if !self.handle_span(base.span(), false) { self.print_modifier_call(base, false); - if i != last { + if !pos.is_last { self.word(","); - if self - .print_comments( - bases[i + 1].span().lo(), - CommentConfig::skip_ws().mixed_prev_space().mixed_post_nbsp(), - ) - .is_none() - { - self.space(); - } + self.space(); } } } - if !self.print_trailing_comment(bases.last().unwrap().span().hi(), None) { - self.space(); - } + self.space(); self.s.offset(-self.ind); } self.end(); + if let Some(layout) = layout + && !self.handle_span(layout.span, false) + { + self.word("layout at "); + self.print_expr(layout.slot); + self.print_sep(Separator::Space); + } self.print_word("{"); self.end(); diff --git a/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol index 25074229db558..20894a47a3262 100644 --- a/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol @@ -39,15 +39,3 @@ contract ERC20DecimalsMock is ERC20 { _decimals = decimals_; } } - -contract SomeContract is - ERC165Upgradeable, // 1 inherited component - ISomeContract // 4 inherited components -{ } - -contract AnotherContract is - Adminable, /* 1 inherited components */ - UUPSUpgradeable /* 1 inherited component */ -{ } - -contract WithLayoutAndBase layout at 69 is Base { } diff --git a/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol b/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol index 990845d3d1349..2e9661f956dcf 100644 --- a/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol @@ -50,15 +50,3 @@ contract ERC20DecimalsMock is ERC20 { } } - -contract SomeContract is - ERC165Upgradeable, // 1 inherited component - ISomeContract // 4 inherited components -{} - -contract AnotherContract is - Adminable, /* 1 inherited components */ - UUPSUpgradeable /* 1 inherited component */ -{} - -contract WithLayoutAndBase layout at 69 is Base {} diff --git a/crates/fmt/testdata/ContractDefinition/fmt.sol b/crates/fmt/testdata/ContractDefinition/fmt.sol index 93cddcd2c6a20..551e84decfc5b 100644 --- a/crates/fmt/testdata/ContractDefinition/fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/fmt.sol @@ -45,15 +45,3 @@ contract ERC20DecimalsMock is ERC20 { _decimals = decimals_; } } - -contract SomeContract is - ERC165Upgradeable, // 1 inherited component - ISomeContract // 4 inherited components -{} - -contract AnotherContract is - Adminable, /* 1 inherited components */ - UUPSUpgradeable /* 1 inherited component */ -{} - -contract WithLayoutAndBase layout at 69 is Base {} diff --git a/crates/fmt/testdata/ContractDefinition/original.sol b/crates/fmt/testdata/ContractDefinition/original.sol index c0aa88a7d6d17..4c671985bda7b 100644 --- a/crates/fmt/testdata/ContractDefinition/original.sol +++ b/crates/fmt/testdata/ContractDefinition/original.sol @@ -17,7 +17,7 @@ contract SampleContract { // comment 16 external /* comment 17 */ pure - returns(uint256) + returns(uint256) // comment 18 { // comment 19 return arg1 > arg2 ? arg1 : arg2; @@ -38,15 +38,3 @@ contract ERC20DecimalsMock is ERC20 { _decimals = decimals_; } } - -contract SomeContract is - ERC165Upgradeable, // 1 inherited component - ISomeContract // 4 inherited components -{ } - -contract AnotherContract is - Adminable, /* 1 inherited components */ - UUPSUpgradeable /* 1 inherited component */ -{ } - -contract WithLayoutAndBase layout at 69 is Base {} From 4a977cd7b1d2aebd2762d9670975d40e1c9e0fe1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 20:24:58 +0700 Subject: [PATCH 105/374] chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/snyk-container.yml | 2 +- .github/workflows/test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml index 0db31480beb38..171f2aabfbfb1 100644 --- a/.github/workflows/snyk-container.yml +++ b/.github/workflows/snyk-container.yml @@ -50,6 +50,6 @@ jobs: image: your/image-to-test args: --file=Dockerfile - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: snyk.sarif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aa505a05a8382..1f16e1d9964b1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -189,12 +189,12 @@ jobs: with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" From 1c660a0d0c1f75f90876e4d55f3f2228bb2a37aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:47:02 +0000 Subject: [PATCH 106/374] chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/snyk-container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml index 171f2aabfbfb1..3c3a0f30b84e3 100644 --- a/.github/workflows/snyk-container.yml +++ b/.github/workflows/snyk-container.yml @@ -40,7 +40,7 @@ jobs: # Snyk can be used to break the build when it detects vulnerabilities. # In this case we want to upload the issues to GitHub Code Scanning continue-on-error: true - uses: snyk/actions/docker@14818c4695ecc4045f33c9cee9e795a788711ca4 + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 env: # In order to use the Snyk Action you will need to have a Snyk API token. # More details in https://github.com/snyk/actions#getting-your-snyk-token From d287039817a741b53e724d4dacd0a0d4ba496eb7 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Oct 2025 21:02:39 +0700 Subject: [PATCH 107/374] Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 790c879119cd6..d5d401c51893c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,17 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference version: 2.1 +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs jobs: say-hello: - docker: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base - image: cimg/base:current - + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps steps: + # Checkout the code as the first step. - checkout - run: name: "Say hello" command: "echo Hello, World!" - + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows workflows: - say-hello-workflow: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. jobs: - say-hello From 4934b0283692720e702a258ee5300a62bd25005e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 00:17:59 +0700 Subject: [PATCH 108/374] Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b59d59e57c17..f967cfaa30db5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,27 +1,31 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - +# See: https://circleci.com/docs/reference/configuration-reference version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/hardhat-project/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs jobs: - web3-defi-game-project-: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current - executor: my-custom-executor + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps steps: + # Checkout the code as the first step. - checkout - - run: echo "Build started" - - run: | - # echo Hello, World! + - run: + name: "Say hello" + command: "echo Hello, World!" +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows workflows: - my-custom-workflow: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. jobs: - - web3-defi-game-project- + - say-hello From 0749dc831e2a63a259928cd773a389371457e671 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 06:32:48 +0700 Subject: [PATCH 109/374] Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b59d59e57c17..04f996a346ab7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,8 +18,6 @@ jobs: steps: - checkout - run: echo "Build started" - - run: | - # echo Hello, World! workflows: my-custom-workflow: From 880e28435b0ef38d85f57279df2d33839dfabfef Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 00:49:41 +0000 Subject: [PATCH 110/374] Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci-say-hello.yml | 31 ------------------------------- .circleci/ci-web3-defi-gamefi.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 31 deletions(-) delete mode 100644 .circleci/ci-say-hello.yml create mode 100644 .circleci/ci-web3-defi-gamefi.yml diff --git a/.circleci/ci-say-hello.yml b/.circleci/ci-say-hello.yml deleted file mode 100644 index f967cfaa30db5..0000000000000 --- a/.circleci/ci-say-hello.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello diff --git a/.circleci/ci-web3-defi-gamefi.yml b/.circleci/ci-web3-defi-gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/ci-web3-defi-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- From 78de4afcd8c43817a445010eca9d771e3e87f07b Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 04:30:24 +0000 Subject: [PATCH 111/374] Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci-web3-defi-gamefi.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .circleci/ci-web3-defi-gamefi.yml diff --git a/.circleci/ci-web3-defi-gamefi.yml b/.circleci/ci-web3-defi-gamefi.yml deleted file mode 100644 index edb6605e3f101..0000000000000 --- a/.circleci/ci-web3-defi-gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- From 23811a7b17a1358e75fa611d4af534e7cddeb603 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 07:19:43 +0000 Subject: [PATCH 112/374] Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail From cc3940779716d5a91105941c16faea922f1d0b0e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 07:22:09 +0000 Subject: [PATCH 113/374] Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index d7a82b5c93b6e..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test From aa9ac06c9c3303de0af9c42c3bd399f6714be098 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 18:43:53 +0700 Subject: [PATCH 114/374] chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nextest.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index d39b597f7613e..3d7ef42941deb 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -67,7 +67,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@0005e0116e92d8489d8d96fbff83f061c79ba95a # v2 + - uses: taiki-e/install-action@e43a5023a747770bfcb71ae048541a681714b951 # v2 with: tool: nextest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1f16e1d9964b1..e71c1d2f993e3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -159,7 +159,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@0005e0116e92d8489d8d96fbff83f061c79ba95a # v2 + - uses: taiki-e/install-action@e43a5023a747770bfcb71ae048541a681714b951 # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 From 93e0c5525c67f81ee0ca78f773b5d22e24a9b117 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 18:46:43 +0700 Subject: [PATCH 115/374] chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/snyk-container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml index 3c3a0f30b84e3..10ac023cf51fc 100644 --- a/.github/workflows/snyk-container.yml +++ b/.github/workflows/snyk-container.yml @@ -33,7 +33,7 @@ jobs: actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build a Docker image run: docker build -t your/image-to-test . - name: Run Snyk to check Docker image for vulnerabilities From 07b64156d78ae32bf392635e282bc71e2d3d347e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:50:52 +0000 Subject: [PATCH 116/374] Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .circleci/cargo.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test From 9f4f6848a6f83c0e785573833b89471bcc08e048 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 15:56:21 +0000 Subject: [PATCH 117/374] fix(anvil): always disable nonce check (foundry-rs#12144) (#165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude --- .github/workflows/nextest.yml | 5 +- Cargo.lock | 150 +- Cargo.toml | 48 +- crates/anvil/src/eth/backend/mem/mod.rs | 13 +- crates/cheatcodes/spec/src/lib.rs | 3 +- crates/chisel/src/source.rs | 2 +- crates/common/src/fs.rs | 43 +- crates/forge/tests/cli/test_cmd/core.rs | 142 ++ crates/forge/tests/cli/test_cmd/fuzz.rs | 735 +++++++++ .../tests/cli/test_cmd/invariant/common.rs | 1424 +++++++++++++++++ .../test_cmd/invariant/mod.rs} | 708 +------- .../tests/cli/test_cmd/invariant/storage.rs | 19 +- .../tests/cli/test_cmd/invariant/target.rs | 766 +++++++++ crates/forge/tests/cli/test_cmd/logs.rs | 756 +++++++++ .../cli/{test_cmd.rs => test_cmd/mod.rs} | 62 +- .../forge/tests/cli/test_cmd/repros.rs | 447 +++++- .../forge/tests/{it => cli/test_cmd}/spec.rs | 13 +- .../forge/tests/{it => cli/test_cmd}/table.rs | 0 crates/forge/tests/cli/test_cmd/trace.rs | 397 +++++ crates/forge/tests/it/cheats.rs | 112 -- crates/forge/tests/it/config.rs | 170 -- crates/forge/tests/it/core.rs | 829 ---------- crates/forge/tests/it/fork.rs | 129 -- crates/forge/tests/it/fs.rs | 23 - crates/forge/tests/it/fuzz.rs | 496 ------ crates/forge/tests/it/inline.rs | 70 - crates/forge/tests/it/main.rs | 14 - crates/forge/tests/it/repros.rs | 411 ----- crates/forge/tests/it/test_helpers.rs | 283 ---- crates/forge/tests/it/vyper.rs | 10 - crates/linking/src/lib.rs | 15 +- crates/test-utils/src/rpc.rs | 20 +- crates/test-utils/src/script.rs | 25 +- crates/test-utils/src/util.rs | 71 +- testdata/default/cheats/AccessList.t.sol | 8 +- testdata/default/cheats/Addr.t.sol | 7 +- .../default/cheats/ArbitraryStorage.t.sol | 22 +- testdata/default/cheats/Assert.t.sol | 7 +- testdata/default/cheats/Assume.t.sol | 7 +- testdata/default/cheats/AssumeNoRevert.t.sol | 18 +- testdata/default/cheats/AttachBlob.t.sol | 6 +- .../default/cheats/AttachDelegation.t.sol | 6 +- testdata/default/cheats/Bank.t.sol | 7 +- testdata/default/cheats/Base64.t.sol | 8 +- testdata/default/cheats/BlobBaseFee.t.sol | 7 +- testdata/default/cheats/Blobhashes.t.sol | 7 +- testdata/default/cheats/Broadcast.t.sol | 56 +- .../cheats/BroadcastRawTransaction.t.sol | 11 +- testdata/default/cheats/ChainId.t.sol | 7 +- testdata/default/cheats/CloneAccount.t.sol | 7 +- testdata/default/cheats/Cool.t.sol | 6 +- testdata/default/cheats/CopyStorage.t.sol | 10 +- testdata/default/cheats/Deal.t.sol | 7 +- testdata/default/cheats/DeployCode.t.sol | 7 +- testdata/default/cheats/Derive.t.sol | 7 +- testdata/default/cheats/EnsNamehash.t.sol | 7 +- testdata/default/cheats/Env.t.sol | 7 +- testdata/default/cheats/Etch.t.sol | 7 +- testdata/default/cheats/ExpectCall.t.sol | 15 +- testdata/default/cheats/ExpectCreate.t.sol | 6 +- testdata/default/cheats/ExpectEmit.t.sol | 9 +- testdata/default/cheats/ExpectRevert.t.sol | 19 +- testdata/default/cheats/Fee.t.sol | 7 +- testdata/default/cheats/Ffi.t.sol | 7 +- testdata/default/cheats/Fork.t.sol | 6 +- testdata/default/cheats/Fork2.t.sol | 15 +- testdata/default/cheats/Fs.t.sol | 14 +- testdata/default/cheats/GasMetering.t.sol | 8 +- testdata/default/cheats/GetArtifactPath.t.sol | 12 +- .../default/cheats/GetBlockTimestamp.t.sol | 7 +- testdata/default/cheats/GetChain.t.sol | 7 +- testdata/default/cheats/GetCode.t.sol | 8 +- testdata/default/cheats/GetDeployedCode.t.sol | 7 +- .../default/cheats/GetFoundryVersion.t.sol | 7 +- testdata/default/cheats/GetLabel.t.sol | 7 +- testdata/default/cheats/GetNonce.t.sol | 7 +- .../default/cheats/GetRawBlockHeader.t.sol | 7 +- testdata/default/cheats/GetStorageSlots.t.sol | 6 +- testdata/default/cheats/Json.t.sol | 11 +- testdata/default/cheats/Label.t.sol | 7 +- testdata/default/cheats/Load.t.sol | 6 +- testdata/default/cheats/Mapping.t.sol | 7 +- testdata/default/cheats/MemSafety.t.sol | 7 +- testdata/default/cheats/MockCall.t.sol | 11 +- testdata/default/cheats/MockCalls.t.sol | 7 +- testdata/default/cheats/MockFunction.t.sol | 6 +- testdata/default/cheats/Nonce.t.sol | 8 +- testdata/default/cheats/Parse.t.sol | 7 +- testdata/default/cheats/Prank.t.sol | 15 +- testdata/default/cheats/Prevrandao.t.sol | 7 +- testdata/default/cheats/ProjectRoot.t.sol | 20 +- testdata/default/cheats/Prompt.t.sol | 23 +- testdata/default/cheats/RandomAddress.t.sol | 7 +- testdata/default/cheats/RandomBytes.t.sol | 7 +- .../default/cheats/RandomCheatcodes.t.sol | 11 +- testdata/default/cheats/RandomUint.t.sol | 7 +- testdata/default/cheats/ReadCallers.t.sol | 7 +- testdata/default/cheats/Record.t.sol | 7 +- .../cheats/RecordAccountAccesses.t.sol | 108 +- .../default/cheats/RecordDebugTrace.t.sol | 19 +- testdata/default/cheats/RecordLogs.t.sol | 6 +- testdata/default/cheats/Remember.t.sol | 7 +- testdata/default/cheats/ResetNonce.t.sol | 6 +- testdata/default/cheats/Rlp.t.sol | 7 +- testdata/default/cheats/Roll.t.sol | 7 +- testdata/default/cheats/RpcUrls.t.sol | 7 +- testdata/default/cheats/Seed.t.sol | 7 +- testdata/default/cheats/SetBlockhash.t.sol | 7 +- testdata/default/cheats/SetNonce.t.sol | 6 +- testdata/default/cheats/SetNonceUnsafe.t.sol | 6 +- testdata/default/cheats/Setup.t.sol | 6 +- testdata/default/cheats/Shuffle.t.sol | 7 +- testdata/default/cheats/Sign.t.sol | 7 +- testdata/default/cheats/SignP256.t.sol | 7 +- testdata/default/cheats/Skip.t.sol | 7 +- testdata/default/cheats/Sleep.t.sol | 7 +- testdata/default/cheats/Sort.t.sol | 7 +- .../default/cheats/StateDiffBytesString.t.sol | 6 +- .../default/cheats/StateDiffMappings.t.sol | 6 +- .../cheats/StateDiffStorageLayout.t.sol | 6 +- .../default/cheats/StateDiffStructTest.t.sol | 6 +- testdata/default/cheats/StateSnapshots.t.sol | 11 +- .../default/cheats/StorageSlotState.t.sol | 7 +- testdata/default/cheats/Store.t.sol | 6 +- testdata/default/cheats/StringUtils.t.sol | 7 +- testdata/default/cheats/ToString.t.sol | 7 +- testdata/default/cheats/Toml.t.sol | 11 +- testdata/default/cheats/Travel.t.sol | 7 +- testdata/default/cheats/TryFfi.sol | 7 +- testdata/default/cheats/UnixTime.t.sol | 7 +- testdata/default/cheats/Wallet.t.sol | 7 +- testdata/default/cheats/Warp.t.sol | 19 +- testdata/default/cheats/dumpState.t.sol | 7 +- testdata/default/cheats/getBlockNumber.t.sol | 7 +- testdata/default/cheats/loadAllocs.t.sol | 6 +- .../default/core/BadSigAfterInvariant.t.sol | 4 +- .../default/core/ContractEnvironment.t.sol | 4 +- .../core/FailingTestAfterFailedSetup.t.sol | 18 - testdata/default/core/LegacyAssertions.t.sol | 24 - testdata/default/core/PaymentFailure.t.sol | 19 - testdata/default/core/Reverting.t.sol | 7 +- testdata/default/core/SetupConsistency.t.sol | 4 +- testdata/default/fork/ForkSame_1.t.sol | 6 +- testdata/default/fork/ForkSame_2.t.sol | 6 +- testdata/default/fork/LaunchFork.t.sol | 21 +- testdata/default/fs/Disabled.t.sol | 23 +- .../fs/{Default.t.sol => ReadOnly.sol} | 14 +- testdata/default/fuzz/Fuzz.t.sol | 31 - testdata/default/fuzz/FuzzCollection.t.sol | 70 - .../default/fuzz/FuzzFailurePersist.t.sol | 29 - testdata/default/fuzz/FuzzInt.t.sol | 58 - testdata/default/fuzz/FuzzPositive.t.sol | 18 - testdata/default/fuzz/FuzzUint.t.sol | 46 - .../common/InvariantAfterInvariant.t.sol | 55 - .../invariant/common/InvariantAssume.t.sol | 23 - .../common/InvariantCalldataDictionary.t.sol | 95 -- .../common/InvariantCustomError.t.sol | 35 - .../common/InvariantExcludedSenders.t.sol | 22 - .../invariant/common/InvariantFixtures.t.sol | 77 - .../common/InvariantHandlerFailure.t.sol | 35 - .../common/InvariantInnerContract.t.sol | 50 - .../common/InvariantPreserveState.t.sol | 49 - .../common/InvariantReentrancy.t.sol | 55 - .../invariant/common/InvariantRollFork.t.sol | 50 - .../common/InvariantScrapeValues.t.sol | 69 - .../common/InvariantSequenceNoReverts.t.sol | 25 - .../common/InvariantShrinkBigSequence.t.sol | 31 - .../common/InvariantShrinkFailOnRevert.t.sol | 26 - .../common/InvariantShrinkWithAssert.t.sol | 32 - .../invariant/common/InvariantTest1.t.sol | 39 - .../invariant/target/ExcludeContracts.t.sol | 31 - .../invariant/target/ExcludeSelectors.t.sol | 41 - .../invariant/target/ExcludeSenders.t.sol | 45 - .../target/FuzzedTargetContracts.t.sol | 66 - .../invariant/target/TargetContracts.t.sol | 32 - .../invariant/target/TargetInterfaces.t.sol | 72 - .../invariant/target/TargetSelectors.t.sol | 41 - .../fuzz/invariant/target/TargetSenders.t.sol | 31 - .../targetAbi/ExcludeArtifacts.t.sol | 45 - .../targetAbi/TargetArtifactSelectors.t.sol | 42 - .../targetAbi/TargetArtifactSelectors2.t.sol | 72 - .../invariant/targetAbi/TargetArtifacts.t.sol | 44 - testdata/default/inline/FuzzInlineConf.t.sol | 6 +- .../default/inline/InvariantInlineConf.t.sol | 6 +- .../default/linking/duplicate/Duplicate.t.sol | 4 +- testdata/default/linking/nested/Nested.t.sol | 4 +- testdata/default/linking/simple/Simple.t.sol | 4 +- testdata/default/logs/DebugLogs.t.sol | 105 -- testdata/default/logs/HardhatLogs.t.sol | 238 --- testdata/default/repros/Issue10302.t.sol | 7 +- testdata/default/repros/Issue10477.t.sol | 7 +- testdata/default/repros/Issue10527.t.sol | 7 +- testdata/default/repros/Issue10552.t.sol | 7 +- testdata/default/repros/Issue10586.t.sol | 11 +- testdata/default/repros/Issue10957.t.sol | 7 +- testdata/default/repros/Issue11353.t.sol | 7 +- testdata/default/repros/Issue11616.t.sol | 6 +- testdata/default/repros/Issue2623.t.sol | 7 +- testdata/default/repros/Issue2629.t.sol | 7 +- testdata/default/repros/Issue2723.t.sol | 7 +- testdata/default/repros/Issue2851.t.sol | 29 - testdata/default/repros/Issue2898.t.sol | 7 +- testdata/default/repros/Issue2956.t.sol | 6 +- testdata/default/repros/Issue2984.t.sol | 6 +- testdata/default/repros/Issue3055.t.sol | 36 - testdata/default/repros/Issue3077.t.sol | 7 +- testdata/default/repros/Issue3110.t.sol | 7 +- testdata/default/repros/Issue3119.t.sol | 7 +- testdata/default/repros/Issue3189.t.sol | 32 - testdata/default/repros/Issue3190.t.sol | 8 +- testdata/default/repros/Issue3192.t.sol | 6 +- testdata/default/repros/Issue3220.t.sol | 6 +- testdata/default/repros/Issue3221.t.sol | 6 +- testdata/default/repros/Issue3223.t.sol | 7 +- testdata/default/repros/Issue3347.t.sol | 14 - testdata/default/repros/Issue3596.t.sol | 31 - testdata/default/repros/Issue3653.t.sol | 6 +- testdata/default/repros/Issue3661.t.sol | 4 +- testdata/default/repros/Issue3674.t.sol | 10 +- testdata/default/repros/Issue3685.t.sol | 7 +- testdata/default/repros/Issue3703.t.sol | 9 +- testdata/default/repros/Issue3708.t.sol | 6 +- testdata/default/repros/Issue3753.t.sol | 7 +- testdata/default/repros/Issue3792.t.sol | 4 +- testdata/default/repros/Issue4232.t.sol | 7 +- testdata/default/repros/Issue4402.t.sol | 7 +- testdata/default/repros/Issue4586.t.sol | 10 +- testdata/default/repros/Issue4630.t.sol | 7 +- testdata/default/repros/Issue4640.t.sol | 7 +- testdata/default/repros/Issue5038.t.sol | 7 +- testdata/default/repros/Issue5529.t.sol | 16 +- testdata/default/repros/Issue5739.t.sol | 6 +- testdata/default/repros/Issue5808.t.sol | 7 +- testdata/default/repros/Issue5929.t.sol | 7 +- testdata/default/repros/Issue5935.t.sol | 7 +- testdata/default/repros/Issue5948.t.sol | 7 +- testdata/default/repros/Issue6006.t.sol | 7 +- testdata/default/repros/Issue6032.t.sol | 7 +- testdata/default/repros/Issue6070.t.sol | 7 +- testdata/default/repros/Issue6115.t.sol | 4 +- testdata/default/repros/Issue6170.t.sol | 28 - testdata/default/repros/Issue6180.t.sol | 7 +- testdata/default/repros/Issue6293.t.sol | 7 +- testdata/default/repros/Issue6355.t.sol | 39 - testdata/default/repros/Issue6437.t.sol | 7 +- testdata/default/repros/Issue6501.t.sol | 14 - testdata/default/repros/Issue6538.t.sol | 7 +- testdata/default/repros/Issue6554.t.sol | 11 +- testdata/default/repros/Issue6616.t.sol | 7 +- testdata/default/repros/Issue6634.t.sol | 9 +- testdata/default/repros/Issue6643.t.sol | 6 +- testdata/default/repros/Issue6759.t.sol | 7 +- testdata/default/repros/Issue6966.t.sol | 4 +- testdata/default/repros/Issue7238.t.sol | 7 +- testdata/default/repros/Issue7457.t.sol | 7 +- testdata/default/repros/Issue7481.t.sol | 7 +- testdata/default/repros/Issue8004.t.sol | 12 +- testdata/default/repros/Issue8006.t.sol | 6 +- testdata/default/repros/Issue8168.t.sol | 7 +- testdata/default/repros/Issue8277.t.sol | 7 +- testdata/default/repros/Issue8287.t.sol | 7 +- testdata/default/repros/Issue8566.t.sol | 7 +- testdata/default/repros/Issue8639.t.sol | 10 +- testdata/default/repros/Issue8971.t.sol | 6 +- testdata/default/repros/Issue9643.t.sol | 9 +- testdata/default/spec/ShanghaiCompat.t.sol | 7 +- .../default/trace/ConflictingSignatures.t.sol | 41 - testdata/default/trace/Trace.t.sol | 98 -- testdata/default/vyper/CounterTest.vy | 4 +- testdata/fixtures/File/ignored/.gitignore | 1 + testdata/foundry.toml | 56 +- testdata/multi-version/Counter.sol | 2 + testdata/multi-version/cheats/GetCode.t.sol | 7 +- testdata/multi-version/cheats/GetCode17.t.sol | 7 +- testdata/paris/cheats/Fork.t.sol | 6 +- testdata/paris/cheats/GasSnapshots.t.sol | 23 +- testdata/paris/cheats/LastCallGas.t.sol | 7 +- testdata/paris/core/BeforeTest.t.sol | 22 +- testdata/paris/fork/Transact.t.sol | 8 +- testdata/paris/spec/ShanghaiCompat.t.sol | 7 +- testdata/{default/vyper => src}/Counter.vy | 0 testdata/{default/vyper => src}/ICounter.vyi | 0 .../ds-test/src/test.sol => utils/DSTest.sol} | 0 testdata/utils/Test.sol | 11 + testdata/{cheats => utils}/Vm.sol | 0 testdata/{default/logs => utils}/console.sol | 0 286 files changed, 5645 insertions(+), 6884 deletions(-) create mode 100644 crates/forge/tests/cli/test_cmd/core.rs create mode 100644 crates/forge/tests/cli/test_cmd/fuzz.rs create mode 100644 crates/forge/tests/cli/test_cmd/invariant/common.rs rename crates/forge/tests/{it/invariant.rs => cli/test_cmd/invariant/mod.rs} (54%) rename testdata/default/fuzz/invariant/storage/InvariantStorageTest.t.sol => crates/forge/tests/cli/test_cmd/invariant/storage.rs (79%) create mode 100644 crates/forge/tests/cli/test_cmd/invariant/target.rs create mode 100644 crates/forge/tests/cli/test_cmd/logs.rs rename crates/forge/tests/cli/{test_cmd.rs => test_cmd/mod.rs} (98%) rename testdata/default/repros/Issue8383.t.sol => crates/forge/tests/cli/test_cmd/repros.rs (57%) rename crates/forge/tests/{it => cli/test_cmd}/spec.rs (93%) rename crates/forge/tests/{it => cli/test_cmd}/table.rs (100%) create mode 100644 crates/forge/tests/cli/test_cmd/trace.rs delete mode 100644 crates/forge/tests/it/cheats.rs delete mode 100644 crates/forge/tests/it/config.rs delete mode 100644 crates/forge/tests/it/core.rs delete mode 100644 crates/forge/tests/it/fork.rs delete mode 100644 crates/forge/tests/it/fs.rs delete mode 100644 crates/forge/tests/it/fuzz.rs delete mode 100644 crates/forge/tests/it/inline.rs delete mode 100644 crates/forge/tests/it/main.rs delete mode 100644 crates/forge/tests/it/repros.rs delete mode 100644 crates/forge/tests/it/test_helpers.rs delete mode 100644 crates/forge/tests/it/vyper.rs delete mode 100644 testdata/default/core/FailingTestAfterFailedSetup.t.sol delete mode 100644 testdata/default/core/LegacyAssertions.t.sol delete mode 100644 testdata/default/core/PaymentFailure.t.sol rename testdata/default/fs/{Default.t.sol => ReadOnly.sol} (68%) delete mode 100644 testdata/default/fuzz/Fuzz.t.sol delete mode 100644 testdata/default/fuzz/FuzzCollection.t.sol delete mode 100644 testdata/default/fuzz/FuzzFailurePersist.t.sol delete mode 100644 testdata/default/fuzz/FuzzInt.t.sol delete mode 100644 testdata/default/fuzz/FuzzPositive.t.sol delete mode 100644 testdata/default/fuzz/FuzzUint.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantAssume.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantHandlerFailure.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantSequenceNoReverts.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol delete mode 100644 testdata/default/fuzz/invariant/common/InvariantTest1.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/TargetContracts.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/TargetSelectors.t.sol delete mode 100644 testdata/default/fuzz/invariant/target/TargetSenders.t.sol delete mode 100644 testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol delete mode 100644 testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol delete mode 100644 testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol delete mode 100644 testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol delete mode 100644 testdata/default/logs/DebugLogs.t.sol delete mode 100644 testdata/default/logs/HardhatLogs.t.sol delete mode 100644 testdata/default/repros/Issue2851.t.sol delete mode 100644 testdata/default/repros/Issue3055.t.sol delete mode 100644 testdata/default/repros/Issue3189.t.sol delete mode 100644 testdata/default/repros/Issue3347.t.sol delete mode 100644 testdata/default/repros/Issue3596.t.sol delete mode 100644 testdata/default/repros/Issue6170.t.sol delete mode 100644 testdata/default/repros/Issue6355.t.sol delete mode 100644 testdata/default/repros/Issue6501.t.sol delete mode 100644 testdata/default/trace/ConflictingSignatures.t.sol delete mode 100644 testdata/default/trace/Trace.t.sol create mode 100644 testdata/fixtures/File/ignored/.gitignore rename testdata/{default/vyper => src}/Counter.vy (100%) rename testdata/{default/vyper => src}/ICounter.vyi (100%) rename testdata/{lib/ds-test/src/test.sol => utils/DSTest.sol} (100%) create mode 100644 testdata/utils/Test.sol rename testdata/{cheats => utils}/Vm.sol (100%) rename testdata/{default/logs => utils}/console.sol (100%) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index 3d7ef42941deb..272e1c7fcb8b4 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -98,7 +98,10 @@ jobs: ~/.config/.foundry/cache testdata/cache testdata/out - key: ${{ runner.os }}-foundry-${{ matrix.name }} + # Use a unique key for each run to always update the cache. + key: foundry-${{ matrix.name }}-${{ github.run_id }} + restore-keys: | + foundry-${{ matrix.name }}- - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - name: Setup Git config run: | diff --git a/Cargo.lock b/Cargo.lock index 61d92544e9846..b6f44ac1c26a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,9 +70,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a0dd3ed764953a6b20458b2b7abbfdc93d20d14b38babe1a70fe631a443a9f1" +checksum = "b9b151e38e42f1586a01369ec52a6934702731d07e8509a7307331b09f6c46dc" dependencies = [ "alloy-eips", "alloy-primitives", @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9556182afa73cddffa91e64a5aa9508d5e8c912b3a15f26998d2388a824d2c7b" +checksum = "6e2d5e8668ef6215efdb7dcca6f22277b4e483a5650e05f5de22b2350971f4b8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19d7092c96defc3d132ee0d8969ca1b79ef512b5eda5c66e3065266b253adf2" +checksum = "630288cf4f3a34a8c6bc75c03dce1dbd47833138f65f37d53a1661eafc96b83f" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e21b782483672d41c62e27973102276060f9526badf3a260adea8cdf4adc049e" +checksum = "15a5ec61206c5b2113bd79b0690395a456ef542d63b596c661b6aaf402f4a34d" dependencies = [ "alloy-primitives", "alloy-serde", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fa99b538ca7006b0c03cfed24ec6d82beda67aac857ef4714be24231d15e6" +checksum = "e5434834adaf64fa20a6fb90877bc1d33214c41b055cc49f82189c98614368cc" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a00efb95ebca2feb59b53930b295f853aef4e0642e865610f3d824d2727ca74" +checksum = "23e7b71e8963a7920dff8c1d4380ea275b3b37c5abde1fc8ea501cd2bffb159b" dependencies = [ "alloy-contract", "alloy-primitives", @@ -262,9 +262,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a272533715aefc900f89d51db00c96e6fd4f517ea081a12fea482a352c8c815c" +checksum = "919a8471cfbed7bcd8cf1197a57dda583ce0e10c6385f6ff4e8b41304b223392" dependencies = [ "alloy-eips", "alloy-primitives", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91676d242c0ced99c0dd6d0096d7337babe9457cc43407d26aa6367fcf90553" +checksum = "d7c69f6c9c68a1287c9d5ff903d0010726934de0dac10989be37b75a29190d55" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -315,9 +315,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f82150116b30ba92f588b87f08fa97a46a1bd5ffc0d0597efdf0843d36bfda" +checksum = "8eaf2ae05219e73e0979cb2cf55612aafbab191d130f203079805eaf881cca58" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -341,9 +341,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223612259a080160ce839a4e5df0125ca403a1d5e7206cc911cea54af5d769aa" +checksum = "e58f4f345cef483eab7374f2b6056973c7419ffe8ad35e994b7a7f5d8e0c7ba4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7283b81b6f136100b152e699171bc7ed8184a58802accbc91a7df4ebb944445" +checksum = "de2597751539b1cc8fe4204e5325f9a9ed83fcacfb212018dfcfa7877e76de21" dependencies = [ "alloy-chains", "alloy-consensus", @@ -458,9 +458,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee7e3d343814ec0dfea69bd1820042a133a9d0b9ac5faf1e6eb133b43366315" +checksum = "06e45a68423e732900a0c824b8e22237db461b79d2e472dd68b7547c16104427" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -502,9 +502,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1154b12d470bef59951c62676e106f4ce5de73b987d86b9faa935acebb138ded" +checksum = "edf8eb8be597cfa8c312934d2566ec4516f066d69164f9212d7a148979fdcfd8" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -528,9 +528,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ab76bf97648a1c6ad8fb00f0d594618942b5a9e008afbfb5c8a8fca800d574" +checksum = "339af7336571dd39ae3a15bde08ae6a647e62f75350bd415832640268af92c06" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -544,9 +544,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456cfc2c1677260edbd7ce3eddb7de419cb46de0e9826c43401f42b0286a779a" +checksum = "83d98fb386a462e143f5efa64350860af39950c49e7c0cbdba419c16793116ef" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -556,9 +556,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cc57ee0c1ac9fb14854195fc249494da7416591dc4a4d981ddfd5dd93b9bce" +checksum = "fbde0801a32d21c5f111f037bee7e22874836fba7add34ed4a6919932dd7cf23" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -567,9 +567,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa4edd92c3124ec19b9d572dc7923d070fe5c2efb677519214affd6156a4463" +checksum = "55c8d51ebb7c5fa8be8ea739a3933c5bfea08777d2d662b30b2109ac5ca71e6b" dependencies = [ "alloy-eips", "alloy-primitives", @@ -583,9 +583,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ac29dd005c33e3f7e09087accc80843315303685c3f7a1b888002cd27785b" +checksum = "388cf910e66bd4f309a81ef746dcf8f9bca2226e3577890a8d56c5839225cf46" dependencies = [ "alloy-primitives", "derive_more", @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9d173854879bcf26c7d71c1c3911972a3314df526f4349ffe488e676af577d" +checksum = "605ec375d91073851f566a3082548af69a28dca831b27a8be7c1b4c49f5c6ca2" dependencies = [ "alloy-consensus", "alloy-eips", @@ -615,9 +615,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7d47bca1a2a1541e4404aa38b7e262bb4dffd9ac23b4f178729a4ddc5a5caa" +checksum = "361cd87ead4ba7659bda8127902eda92d17fa7ceb18aba1676f7be10f7222487" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -636,9 +636,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c331c8e48665607682e8a9549a2347c13674d4fbcbdc342e7032834eba2424f4" +checksum = "de4e95fb0572b97b17751d0fdf5cdc42b0050f9dd9459eddd1bf2e2fbfed0a33" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -650,9 +650,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2f66afe1e76ca4485e593980056f061b2bdae2055486a062fca050ff111a52" +checksum = "cddde1bbd4feeb0d363ae7882af1e2e7955ef77c17f933f31402aad9343b57c5" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -662,9 +662,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8468f1a7f9ee3bae73c24eead0239abea720dbf7779384b9c7e20d51bfb6b0" +checksum = "64600fc6c312b7e0ba76f73a381059af044f4f21f43e07f51f1fa76c868fe302" dependencies = [ "alloy-primitives", "serde", @@ -673,9 +673,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33387c90b0a5021f45a5a77c2ce6c49b8f6980e66a318181468fb24cea771670" +checksum = "5772858492b26f780468ae693405f895d6a27dea6e3eab2c36b6217de47c2647" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -690,9 +690,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bf90f2355769ad93f790b930434b8d3d2948317f3e484de458010409024462" +checksum = "66acf5f8745dd935e94855aada39d83b555112872321d9293748424de144897e" dependencies = [ "alloy-consensus", "alloy-network", @@ -709,9 +709,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c768277bfc541a7aab3c3a079d838b3925b6c2f367e29be943f002ecde2712" +checksum = "0ccf849fbbdbd3657c0fd07e5654b8880f25bdcb325424edde5e1a4a77b48816" dependencies = [ "alloy-consensus", "alloy-network", @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccf703581d2c0b2dd2d5bd235de2b5ccfd6bdc43e750ac767327fe0fb0b4ea1" +checksum = "4599a95670313c028b1f69c425deee72c26f2c4911713eb49a4d5faf9eb67c29" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -747,9 +747,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55d9e795c85e36dcea08786d2e7ae9b73cb554b6bea6ac4c212def24e1b4d03" +checksum = "f4195b803d0a992d8dbaab2ca1986fc86533d4bc80967c0cce7668b26ad99ef9" dependencies = [ "alloy-consensus", "alloy-network", @@ -767,9 +767,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675184c6682378f32ce8fc8429f91e93f72098b8a73af6a9447549b6606241ea" +checksum = "f9985b3afacb904655814a47816cc3e1dc8819753b7896ee7bef7e8c66f8e697" dependencies = [ "alloy-consensus", "alloy-network", @@ -857,9 +857,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702002659778d89a94cd4ff2044f6b505460df6c162e2f47d1857573845b0ace" +checksum = "025a940182bddaeb594c26fe3728525ae262d0806fe6a4befdf5d7bc13d54bce" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -881,9 +881,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6bdc0830e5e8f08a4c70a4c791d400a86679c694a3b4b986caf26fad680438" +checksum = "e3b5064d1e1e1aabc918b5954e7fb8154c39e77ec6903a581b973198b26628fa" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -896,9 +896,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ce41d99a32346f354725fe62eadd271cdbae45fe6b3cc40cb054e0bf763112" +checksum = "d47962f3f1d9276646485458dc842b4e35675f42111c9d814ae4711c664c8300" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -916,9 +916,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686219dcef201655763bd3d4eabe42388d9368bfbf6f1c8016d14e739ec53aac" +checksum = "9476a36a34e2fb51b6746d009c53d309a186a825aa95435407f0e07149f4ad2d" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -950,9 +950,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bf39928a5e70c9755d6811a2928131b53ba785ad37c8bf85c90175b5d43b818" +checksum = "f8e52276fdb553d3c11563afad2898f4085165e4093604afe3d78b69afbf408f" dependencies = [ "alloy-primitives", "darling 0.21.3", @@ -3573,7 +3573,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3832,7 +3832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6731,7 +6731,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8505,7 +8505,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9742,7 +9742,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.1.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9762,7 +9762,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2111ef44dae28680ae9752bb89409e7310ca33a8c621ebe7b106cf5c928b3ac0" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11035,7 +11035,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bda08cf270a9d..269c294f25b83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -236,30 +236,30 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features ] } ## alloy -alloy-consensus = { version = "1.0.38", default-features = false } -alloy-contract = { version = "1.0.38", default-features = false } -alloy-eips = { version = "1.0.38", default-features = false } -alloy-eip5792 = { version = "1.0.38", default-features = false } -alloy-ens = { version = "1.0.38", default-features = false } -alloy-genesis = { version = "1.0.38", default-features = false } -alloy-json-rpc = { version = "1.0.38", default-features = false } -alloy-network = { version = "1.0.38", default-features = false } -alloy-provider = { version = "1.0.38", default-features = false } -alloy-pubsub = { version = "1.0.38", default-features = false } -alloy-rpc-client = { version = "1.0.38", default-features = false } -alloy-rpc-types = { version = "1.0.38", default-features = true } -alloy-rpc-types-beacon = { version = "1.0.38", default-features = true } -alloy-serde = { version = "1.0.38", default-features = false } -alloy-signer = { version = "1.0.38", default-features = false } -alloy-signer-aws = { version = "1.0.38", default-features = false } -alloy-signer-gcp = { version = "1.0.38", default-features = false } -alloy-signer-ledger = { version = "1.0.38", default-features = false } -alloy-signer-local = { version = "1.0.38", default-features = false } -alloy-signer-trezor = { version = "1.0.38", default-features = false } -alloy-transport = { version = "1.0.38", default-features = false } -alloy-transport-http = { version = "1.0.38", default-features = false } -alloy-transport-ipc = { version = "1.0.38", default-features = false } -alloy-transport-ws = { version = "1.0.38", default-features = false } +alloy-consensus = { version = "1.0.41", default-features = false } +alloy-contract = { version = "1.0.41", default-features = false } +alloy-eips = { version = "1.0.41", default-features = false } +alloy-eip5792 = { version = "1.0.41", default-features = false } +alloy-ens = { version = "1.0.41", default-features = false } +alloy-genesis = { version = "1.0.41", default-features = false } +alloy-json-rpc = { version = "1.0.41", default-features = false } +alloy-network = { version = "1.0.41", default-features = false } +alloy-provider = { version = "1.0.41", default-features = false } +alloy-pubsub = { version = "1.0.41", default-features = false } +alloy-rpc-client = { version = "1.0.41", default-features = false } +alloy-rpc-types = { version = "1.0.41", default-features = true } +alloy-rpc-types-beacon = { version = "1.0.41", default-features = true } +alloy-serde = { version = "1.0.41", default-features = false } +alloy-signer = { version = "1.0.41", default-features = false } +alloy-signer-aws = { version = "1.0.41", default-features = false } +alloy-signer-gcp = { version = "1.0.41", default-features = false } +alloy-signer-ledger = { version = "1.0.41", default-features = false } +alloy-signer-local = { version = "1.0.41", default-features = false } +alloy-signer-trezor = { version = "1.0.41", default-features = false } +alloy-transport = { version = "1.0.41", default-features = false } +alloy-transport-http = { version = "1.0.41", default-features = false } +alloy-transport-ipc = { version = "1.0.41", default-features = false } +alloy-transport-ws = { version = "1.0.41", default-features = false } alloy-hardforks = { version = "0.3.2", default-features = false } alloy-op-hardforks = { version = "0.3.2", default-features = false } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 64ca56809e0d5..72e3724ce1d5e 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1520,7 +1520,7 @@ impl Backend { /// - `disable_eip3607` is set to `true` /// - `disable_base_fee` is set to `true` /// - `tx_gas_limit_cap` is set to `Some(u64::MAX)` indicating no gas limit cap - /// - `nonce` check is skipped if `request.nonce` is None + /// - `nonce` check is skipped fn build_call_env( &self, request: WithOtherFields, @@ -1571,6 +1571,9 @@ impl Backend { // - tracing env.evm_env.cfg_env.disable_base_fee = true; + // Disable nonce check in revm + env.evm_env.cfg_env.disable_nonce_check = true; + let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| { self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE) }); @@ -1608,9 +1611,6 @@ impl Backend { if let Some(nonce) = nonce { env.tx.base.nonce = nonce; - } else { - // Disable nonce check in revm - env.evm_env.cfg_env.disable_nonce_check = true; } if env.evm_env.block_env.basefee == 0 { @@ -1909,8 +1909,9 @@ impl Backend { block_request: Option, opts: GethDebugTracingCallOptions, ) -> Result { - let GethDebugTracingCallOptions { tracing_options, block_overrides, state_overrides } = - opts; + let GethDebugTracingCallOptions { + tracing_options, block_overrides, state_overrides, .. + } = opts; let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options; self.with_database_at(block_request, |state, mut block| { diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 5ee5c03dea2b6..29e968aeb5bc6 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -117,8 +117,7 @@ mod tests { #[cfg(feature = "schema")] const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/cheatcodes.schema.json"); - const IFACE_PATH: &str = - concat!(env!("CARGO_MANIFEST_DIR"), "/../../../testdata/cheats/Vm.sol"); + const IFACE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../testdata/utils/Vm.sol"); /// Generates the `cheatcodes.json` file contents. fn json_cheatcodes() -> String { diff --git a/crates/chisel/src/source.rs b/crates/chisel/src/source.rs index 8fce772a4ef69..576f36eab02f3 100644 --- a/crates/chisel/src/source.rs +++ b/crates/chisel/src/source.rs @@ -26,7 +26,7 @@ use walkdir::WalkDir; pub const MIN_VM_VERSION: Version = Version::new(0, 6, 2); /// Solidity source for the `Vm` interface in [forge-std](https://github.com/foundry-rs/forge-std) -static VM_SOURCE: &str = include_str!("../../../testdata/cheats/Vm.sol"); +static VM_SOURCE: &str = include_str!("../../../testdata/utils/Vm.sol"); /// [`SessionSource`] build output. pub struct GeneratedOutput { diff --git a/crates/common/src/fs.rs b/crates/common/src/fs.rs index 090f885e708a3..4cf3358f24400 100644 --- a/crates/common/src/fs.rs +++ b/crates/common/src/fs.rs @@ -5,7 +5,7 @@ use flate2::{Compression, read::GzDecoder, write::GzEncoder}; use serde::{Serialize, de::DeserializeOwned}; use std::{ fs::{self, File}, - io::{BufReader, BufWriter, Read, Write}, + io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}, path::{Component, Path, PathBuf}, }; @@ -62,25 +62,29 @@ pub fn read_json_gzip_file(path: &Path) -> Result { /// Reads the entire contents of a locked shared file into a string. pub fn locked_read_to_string(path: impl AsRef) -> Result { let path = path.as_ref(); - let file = + let mut file = fs::OpenOptions::new().read(true).open(path).map_err(|err| FsPathError::open(err, path))?; file.lock_shared().map_err(|err| FsPathError::lock(err, path))?; - let mut contents = String::new(); - (&file).read_to_string(&mut contents).map_err(|err| FsPathError::read(err, path))?; + let contents = read_inner(path, &mut file)?; file.unlock().map_err(|err| FsPathError::unlock(err, path))?; - Ok(contents) + String::from_utf8(contents).map_err(|err| FsPathError::read(std::io::Error::other(err), path)) } /// Reads the entire contents of a locked shared file into a bytes vector. pub fn locked_read(path: impl AsRef) -> Result> { let path = path.as_ref(); - let file = + let mut file = fs::OpenOptions::new().read(true).open(path).map_err(|err| FsPathError::open(err, path))?; file.lock_shared().map_err(|err| FsPathError::lock(err, path))?; + let contents = read_inner(path, &mut file)?; + file.unlock().map_err(|err| FsPathError::unlock(err, path))?; + Ok(contents) +} + +fn read_inner(path: &Path, file: &mut File) -> Result> { let file_len = file.metadata().map_err(|err| FsPathError::open(err, path))?.len() as usize; let mut buffer = Vec::with_capacity(file_len); - (&file).read_to_end(&mut buffer).map_err(|err| FsPathError::read(err, path))?; - file.unlock().map_err(|err| FsPathError::unlock(err, path))?; + file.read_to_end(&mut buffer).map_err(|err| FsPathError::read(err, path))?; Ok(buffer) } @@ -136,18 +140,39 @@ pub fn locked_write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Resul } /// Writes a line in an exclusive locked file. -pub fn locked_write_line(path: impl AsRef, line: &String) -> Result<()> { +pub fn locked_write_line(path: impl AsRef, line: &str) -> Result<()> { let path = path.as_ref(); + if cfg!(windows) { + return locked_write_line_windows(path, line); + } + let mut file = std::fs::OpenOptions::new() .append(true) .create(true) .open(path) .map_err(|err| FsPathError::open(err, path))?; + file.lock().map_err(|err| FsPathError::lock(err, path))?; writeln!(file, "{line}").map_err(|err| FsPathError::write(err, path))?; file.unlock().map_err(|err| FsPathError::unlock(err, path)) } +// Locking fails on Windows if the file is opened in append mode. +fn locked_write_line_windows(path: &Path, line: &str) -> Result<()> { + let mut file = std::fs::OpenOptions::new() + .write(true) + .truncate(false) + .create(true) + .open(path) + .map_err(|err| FsPathError::open(err, path))?; + file.lock().map_err(|err| FsPathError::lock(err, path))?; + + file.seek(SeekFrom::End(0)).map_err(|err| FsPathError::write(err, path))?; + writeln!(file, "{line}").map_err(|err| FsPathError::write(err, path))?; + + file.unlock().map_err(|err| FsPathError::unlock(err, path)) +} + /// Wrapper for `std::fs::copy` pub fn copy(from: impl AsRef, to: impl AsRef) -> Result { let from = from.as_ref(); diff --git a/crates/forge/tests/cli/test_cmd/core.rs b/crates/forge/tests/cli/test_cmd/core.rs new file mode 100644 index 0000000000000..fffb6394e4779 --- /dev/null +++ b/crates/forge/tests/cli/test_cmd/core.rs @@ -0,0 +1,142 @@ +//! Core test functionality tests + +use foundry_test_utils::str; + +forgetest_init!(failing_test_after_failed_setup, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "FailingTestAfterFailedSetup.t.sol", + r#" +import "forge-std/Test.sol"; + +contract FailingTestAfterFailedSetupTest is Test { + function setUp() public { + assertTrue(false); + } + + function testAssertSuccess() public { + assertTrue(true); + } + + function testAssertFailure() public { + assertTrue(false); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest +[FAIL: assertion failed] setUp() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest +[FAIL: assertion failed] setUp() ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +forgetest_init!(legacy_assertions, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "LegacyAssertions.t.sol", + r#" +import "forge-std/Test.sol"; + +contract NoAssertionsRevertTest is Test { + function testMultipleAssertFailures() public { + vm.assertEq(uint256(1), uint256(2)); + vm.assertLt(uint256(5), uint256(4)); + } +} + +/// forge-config: default.legacy_assertions = true +contract LegacyAssertionsTest { + bool public failed; + + function testFlagNotSetSuccess() public {} + + function testFlagSetFailure() public { + failed = true; + } +} +"#, + ); + + cmd.args(["test", "-j1"]).assert_failure().stdout_eq(str![[r#" +... +Ran 2 tests for test/LegacyAssertions.t.sol:LegacyAssertionsTest +[PASS] testFlagNotSetSuccess() ([GAS]) +[FAIL] testFlagSetFailure() ([GAS]) +Suite result: FAILED. 1 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/LegacyAssertions.t.sol:NoAssertionsRevertTest +[FAIL: assertion failed: 1 != 2] testMultipleAssertFailures() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 2 test suites [ELAPSED]: 1 tests passed, 2 failed, 0 skipped (3 total tests) + +Failing tests: +Encountered 1 failing test in test/LegacyAssertions.t.sol:LegacyAssertionsTest +[FAIL] testFlagSetFailure() ([GAS]) + +Encountered 1 failing test in test/LegacyAssertions.t.sol:NoAssertionsRevertTest +[FAIL: assertion failed: 1 != 2] testMultipleAssertFailures() ([GAS]) + +Encountered a total of 2 failing tests, 1 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 2 failed tests + +"#]]); +}); + +forgetest_init!(payment_failure, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "PaymentFailure.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Payable { + function pay() public payable {} +} + +contract PaymentFailureTest is Test { + function testCantPay() public { + Payable target = new Payable(); + vm.prank(address(1)); + target.pay{value: 1}(); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/PaymentFailure.t.sol:PaymentFailureTest +[FAIL: EvmError: Revert] testCantPay() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/PaymentFailure.t.sol:PaymentFailureTest +[FAIL: EvmError: Revert] testCantPay() ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd/fuzz.rs b/crates/forge/tests/cli/test_cmd/fuzz.rs new file mode 100644 index 0000000000000..aaa8fe687d369 --- /dev/null +++ b/crates/forge/tests/cli/test_cmd/fuzz.rs @@ -0,0 +1,735 @@ +use alloy_primitives::U256; +use foundry_test_utils::{TestCommand, forgetest_init, str}; +use regex::Regex; + +forgetest_init!(test_can_scrape_bytecode, |prj, cmd| { + prj.update_config(|config| config.optimizer = Some(true)); + prj.add_source( + "FuzzerDict.sol", + r#" +// 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; + } +} + "#, + ); + + prj.add_test( + "FuzzerDictTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import "src/FuzzerDict.sol"; + +contract FuzzerDictTest is Test { + FuzzerDict fuzzerDict; + + function setUp() public { + fuzzerDict = new FuzzerDict(address(100), address(200)); + } + + /// forge-config: default.fuzz.runs = 2000 + function testImmutableOwner(address who) public { + assertTrue(who != fuzzerDict.immutableOwner()); + } + + /// forge-config: default.fuzz.runs = 2000 + function testStorageOwner(address who) public { + assertTrue(who != fuzzerDict.storageOwner()); + } +} + "#, + ); + + // Test that immutable address is used as fuzzed input, causing test to fail. + cmd.args(["test", "--fuzz-seed", "119", "--mt", "testImmutableOwner"]).assert_failure(); + // Test that storage address is used as fuzzed input, causing test to fail. + cmd.forge_fuse() + .args(["test", "--fuzz-seed", "119", "--mt", "testStorageOwner"]) + .assert_failure(); +}); + +// tests that inline max-test-rejects config is properly applied +forgetest_init!(test_inline_max_test_rejects, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "Contract.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract InlineMaxRejectsTest is Test { + /// forge-config: default.fuzz.max-test-rejects = 1 + function test_fuzz_bound(uint256 a) public { + vm.assume(false); + } +} + "#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL: `vm.assume` rejected too many inputs (1 allowed)] test_fuzz_bound(uint256) (runs: 0, [AVG_GAS]) +... +"#]]); +}); + +// Tests that test timeout config is properly applied. +// If test doesn't timeout after one second, then test will fail with `rejected too many inputs`. +forgetest_init!(test_fuzz_timeout, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "Contract.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract FuzzTimeoutTest is Test { + /// forge-config: default.fuzz.max-test-rejects = 50000 + /// forge-config: default.fuzz.timeout = 1 + function test_fuzz_bound(uint256 a) public pure { + vm.assume(a == 0); + } +} + "#, + ); + + cmd.args(["test"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Contract.t.sol:FuzzTimeoutTest +[PASS] test_fuzz_bound(uint256) (runs: [..], [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + +forgetest_init!(test_fuzz_fail_on_revert, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| config.fuzz.fail_on_revert = false); + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + require(number > 10000000000, "low number"); + number = newNumber; + } +} + "#, + ); + + prj.add_test( + "CounterTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; +import "src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + } + + function testFuzz_SetNumberRequire(uint256 x) public { + counter.setNumber(x); + require(counter.number() == 1); + } + + function testFuzz_SetNumberAssert(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), 1); + } +} + "#, + ); + + // Tests should not fail as revert happens in Counter contract. + cmd.args(["test", "--mc", "CounterTest"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 2 tests for test/CounterTest.t.sol:CounterTest +[PASS] testFuzz_SetNumberAssert(uint256) (runs: 256, [AVG_GAS]) +[PASS] testFuzz_SetNumberRequire(uint256) (runs: 256, [AVG_GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); + + // Tested contract does not revert. + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } +} + "#, + ); + + // Tests should fail as revert happens in cheatcode (assert) and test (require) contract. + cmd.assert_failure().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 2 tests for test/CounterTest.t.sol:CounterTest +[FAIL: assertion failed: [..]] testFuzz_SetNumberAssert(uint256) (runs: 0, [AVG_GAS]) +[FAIL: EvmError: Revert; [..]] testFuzz_SetNumberRequire(uint256) (runs: 0, [AVG_GAS]) +Suite result: FAILED. 0 passed; 2 failed; 0 skipped; [ELAPSED] +... + +"#]]); +}); + +// Test 256 runs regardless number of test rejects. +// +forgetest_init!(test_fuzz_runs_with_rejects, |prj, cmd| { + prj.add_test( + "FuzzWithRejectsTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract FuzzWithRejectsTest is Test { + function testFuzzWithRejects(uint256 x) public pure { + vm.assume(x < 1_000_000); + } +} + "#, + ); + + // Tests should not fail as revert happens in Counter contract. + cmd.args(["test", "--mc", "FuzzWithRejectsTest"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/FuzzWithRejectsTest.t.sol:FuzzWithRejectsTest +[PASS] testFuzzWithRejects(uint256) (runs: 256, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + +// Test that counterexample is not replayed if test changes. +// +forgetest_init!(test_fuzz_replay_with_changed_test, |prj, cmd| { + prj.update_config(|config| config.fuzz.seed = Some(U256::from(100u32))); + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterTest is Test { + function testFuzz_SetNumber(uint256 x) public pure { + require(x > 200); + } +} + "#, + ); + // Tests should fail and record counterexample with value 2. + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Failing tests: +Encountered 1 failing test in test/Counter.t.sol:CounterTest +[FAIL: EvmError: Revert; counterexample: calldata=0x5c7f60d70000000000000000000000000000000000000000000000000000000000000002 args=[2]] testFuzz_SetNumber(uint256) (runs: 19, [AVG_GAS]) +... + +"#]]); + + // Change test to assume counterexample 2 is discarded. + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterTest is Test { + function testFuzz_SetNumber(uint256 x) public pure { + vm.assume(x != 2); + } +} + "#, + ); + // Test should pass when replay failure with changed assume logic. + cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint256) (runs: 256, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); + + // Change test signature. + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterTest is Test { + function testFuzz_SetNumber(uint8 x) public pure { + } +} + "#, + ); + // Test should pass when replay failure with changed function signature. + cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Counter.t.sol:CounterTest +[PASS] testFuzz_SetNumber(uint8) (runs: 256, [AVG_GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); + + // Change test back to the original one that produced the counterexample. + prj.add_test( + "Counter.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterTest is Test { + function testFuzz_SetNumber(uint256 x) public pure { + require(x > 200); + } +} + "#, + ); + // Test should fail with replayed counterexample 2 (0 runs). + cmd.forge_fuse().args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Failing tests: +Encountered 1 failing test in test/Counter.t.sol:CounterTest +[FAIL: EvmError: Revert; counterexample: calldata=0x5c7f60d70000000000000000000000000000000000000000000000000000000000000002 args=[2]] testFuzz_SetNumber(uint256) (runs: 0, [AVG_GAS]) +... + +"#]]); +}); + +forgetest_init!(fuzz_basic, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Fuzz.t.sol", + r#" +import "forge-std/Test.sol"; + +contract FuzzTest is Test { + constructor() { + emit log("constructor"); + } + + function setUp() public { + emit log("setUp"); + } + + function testShouldFailFuzz(uint8 x) public { + emit log("testFailFuzz"); + require(x > 128, "should revert"); + } + + function testSuccessfulFuzz(uint128 a, uint128 b) public { + emit log("testSuccessfulFuzz"); + assertEq(uint256(a) + uint256(b), uint256(a) + uint256(b)); + } + + function testToStringFuzz(bytes32 data) public { + vm.toString(data); + } +} + "#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 3 tests for test/Fuzz.t.sol:FuzzTest +[FAIL: should revert; counterexample: calldata=[..] args=[..]] testShouldFailFuzz(uint8) (runs: [..], [AVG_GAS]) +[PASS] testSuccessfulFuzz(uint128,uint128) (runs: 256, [AVG_GAS]) +[PASS] testToStringFuzz(bytes32) (runs: 256, [AVG_GAS]) +Suite result: FAILED. 2 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 1 failed, 0 skipped (3 total tests) + +Failing tests: +Encountered 1 failing test in test/Fuzz.t.sol:FuzzTest +[FAIL: should revert; counterexample: calldata=[..] args=[..]] testShouldFailFuzz(uint8) (runs: [..], [AVG_GAS]) + +Encountered a total of 1 failing tests, 2 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// Test that showcases PUSH collection on normal fuzzing. +// Ignored until we collect them in a smarter way. +forgetest_init!( + #[ignore] + fuzz_collection, + |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 100; + config.invariant.runs = 1000; + config.fuzz.runs = 1000; + config.fuzz.seed = Some(U256::from(6u32)); + }); + prj.add_test( + "FuzzCollection.t.sol", + r#" +import "forge-std/Test.sol"; + +contract SampleContract { + uint256 public counter; + uint256 public counterX2; + address public owner = address(0xBEEF); + bool public found_needle; + + event Incremented(uint256 counter); + + modifier onlyOwner() { + require(msg.sender == owner, "ONLY_OWNER"); + _; + } + + function compare(uint256 val) public { + if (val == 0x4446) { + found_needle = true; + } + } + + function incrementBy(uint256 numToIncrement) public onlyOwner { + counter += numToIncrement; + counterX2 += numToIncrement * 2; + + emit Incremented(counter); + } + + function breakTheInvariant(uint256 x) public { + if (x == 0x5556) { + counterX2 = 0; + } + } +} + +contract SampleContractTest is Test { + event Incremented(uint256 counter); + + SampleContract public sample; + + function setUp() public { + sample = new SampleContract(); + } + + function testIncrement(address caller) public { + vm.startPrank(address(caller)); + + vm.expectRevert("ONLY_OWNER"); + sample.incrementBy(1); + } + + function testNeedle(uint256 needle) public { + sample.compare(needle); + require(!sample.found_needle(), "needle found."); + } + + function invariantCounter() public { + require(sample.counter() * 2 == sample.counterX2(), "broken counter."); + } +} + "#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#""#]]); + } +); + +forgetest_init!(fuzz_failure_persist, |prj, cmd| { + prj.wipe_contracts(); + + let persist_dir = prj.cache().parent().unwrap().join("persist"); + assert!(!persist_dir.exists()); + prj.update_config(|config| { + config.fuzz.failure_persist_dir = Some(persist_dir.clone()); + }); + + prj.add_test( + "FuzzFailurePersist.t.sol", + r#" +import "forge-std/Test.sol"; + +struct TestTuple { + address user; + uint256 amount; +} + +contract FuzzFailurePersistTest is Test { + 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); + } +} + "#, + ); + + let mut calldata = None; + let expected = str![[r#" +... +Ran 1 test for test/FuzzFailurePersist.t.sol:FuzzFailurePersistTest +[FAIL: EvmError: Revert; counterexample: calldata=[..] args=[..]] test_persist_fuzzed_failure(uint256,int256,address,bool,string,(address,uint256),address[]) (runs: 0, [AVG_GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"#]]; + let mut check = |cmd: &mut TestCommand, same: bool| { + let assert = cmd.assert_failure(); + let output = assert.get_output(); + let stdout = String::from_utf8_lossy(&output.stdout); + let calldata = calldata.get_or_insert_with(|| { + let re = Regex::new(r"calldata=(0x[0-9a-fA-F]+)").unwrap(); + re.captures(&stdout).unwrap().get(1).unwrap().as_str().to_string() + }); + assert_eq!(stdout.contains(calldata.as_str()), same, "\n{stdout}"); + assert.stdout_eq(expected.clone()); + }; + + cmd.arg("test"); + + // Run several times, asserting that the failure persists and is the same. + for _ in 0..3 { + check(&mut cmd, true); + assert!(persist_dir.exists()); + } + + // Change dir and run again, asserting that the failure persists. It should be a new failure. + let new_persist_dir = prj.cache().parent().unwrap().join("persist2"); + assert!(!new_persist_dir.exists()); + prj.update_config(|config| { + config.fuzz.failure_persist_dir = Some(new_persist_dir.clone()); + }); + check(&mut cmd, false); + assert!(new_persist_dir.exists()); +}); + +// 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 +forgetest_init!(fuzz_int, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "FuzzInt.t.sol", + r#" +import "forge-std/Test.sol"; + +contract FuzzNumbersTest is Test { + function testPositive(int256) public { + assertTrue(true); + } + + function testNegativeHalf(int256 val) public { + assertTrue(val < 2 ** 128 - 1); + } + + function testNegative0(int256 val) public { + assertTrue(val == 0); + } + + function testNegative1(int256 val) public { + assertTrue(val == -1); + } + + function testNegative2(int128 val) public { + assertTrue(val == 1); + } + + function testNegativeMax0(int256 val) public { + assertTrue(val == type(int256).max); + } + + function testNegativeMax1(int256 val) public { + assertTrue(val == type(int256).max - 2); + } + + function testNegativeMin0(int256 val) public { + assertTrue(val == type(int256).min); + } + + function testNegativeMin1(int256 val) public { + assertTrue(val == type(int256).min + 2); + } + + function testEquality(int256 x, int256 y) public { + int256 xy; + + unchecked { + xy = x * y; + } + + if ((x != 0 && xy / x != y)) { + return; + } + + assertEq(((xy - 1) / 1e18) + 1, (xy - 1) / (1e18 + 1)); + } +} + "#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 10 tests for test/FuzzInt.t.sol:FuzzNumbersTest +[FAIL: assertion failed[..]] testEquality(int256,int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegative0(int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegative1(int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegative2(int128) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeHalf(int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeMax0(int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeMax1(int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeMin0(int256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeMin1(int256) (runs: [..], [AVG_GAS]) +[PASS] testPositive(int256) (runs: 256, [AVG_GAS]) +Suite result: FAILED. 1 passed; 9 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 9 failed, 0 skipped (10 total tests) +... +"#]]); +}); + +forgetest_init!(fuzz_positive, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "FuzzPositive.t.sol", + r#" +import "forge-std/Test.sol"; + +contract FuzzPositive is Test { + function testSuccessChecker(uint256 val) public { + assertTrue(true); + } + + function testSuccessChecker2(int256 val) public { + assert(val == val); + } + + function testSuccessChecker3(uint32 val) public { + assert(val + 0 == val); + } +} + "#, + ); + + cmd.args(["test"]).assert_success().stdout_eq(str![[r#" +... +Ran 3 tests for test/FuzzPositive.t.sol:FuzzPositive +[PASS] testSuccessChecker(uint256) (runs: 256, [AVG_GAS]) +[PASS] testSuccessChecker2(int256) (runs: 256, [AVG_GAS]) +[PASS] testSuccessChecker3(uint32) (runs: 256, [AVG_GAS]) +Suite result: ok. 3 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) + +"#]]); +}); + +// 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 +forgetest_init!(fuzz_uint, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "FuzzUint.t.sol", + r#" +import "forge-std/Test.sol"; + +contract FuzzNumbersTest is Test { + function testPositive(uint256) public { + assertTrue(true); + } + + function testNegativeHalf(uint256 val) public { + assertTrue(val < 2 ** 128 - 1); + } + + function testNegative0(uint256 val) public { + assertTrue(val == 0); + } + + function testNegative2(uint256 val) public { + assertTrue(val == 2); + } + + function testNegative2Max(uint256 val) public { + assertTrue(val == type(uint256).max - 2); + } + + function testNegativeMax(uint256 val) public { + assertTrue(val == type(uint256).max); + } + + function testEquality(uint256 x, uint256 y) public { + uint256 xy; + + unchecked { + xy = x * y; + } + + if ((x != 0 && xy / x != y)) { + return; + } + + assertEq(((xy - 1) / 1e18) + 1, (xy - 1) / (1e18 + 1)); + } +} + "#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 7 tests for test/FuzzUint.t.sol:FuzzNumbersTest +[FAIL: assertion failed[..]] testEquality(uint256,uint256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegative0(uint256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegative2(uint256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegative2Max(uint256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeHalf(uint256) (runs: [..], [AVG_GAS]) +[FAIL: assertion failed[..]] testNegativeMax(uint256) (runs: [..], [AVG_GAS]) +[PASS] testPositive(uint256) (runs: 256, [AVG_GAS]) +Suite result: FAILED. 1 passed; 6 failed; 0 skipped; [ELAPSED] +... +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd/invariant/common.rs b/crates/forge/tests/cli/test_cmd/invariant/common.rs new file mode 100644 index 0000000000000..4e32bf143fb58 --- /dev/null +++ b/crates/forge/tests/cli/test_cmd/invariant/common.rs @@ -0,0 +1,1424 @@ +use super::*; + +forgetest!(invariant_after_invariant, |prj, cmd| { + prj.insert_vm(); + prj.insert_ds_test(); + + prj.add_test( + "InvariantAfterInvariant.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract AfterInvariantHandler { + uint256 public count; + + function inc() external { + count += 1; + } +} + +contract InvariantAfterInvariantTest is Test { + 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"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 3 tests for test/InvariantAfterInvariant.t.sol:InvariantAfterInvariantTest +[FAIL: afterInvariant failure] + [SEQUENCE] + invariant_after_invariant_failure() ([RUNS]) + +[STATS] + +[FAIL: invariant failure] + [SEQUENCE] + invariant_failure() ([RUNS]) + +[STATS] + +[PASS] invariant_success() ([RUNS]) + +[STATS] + +Suite result: FAILED. 1 passed; 2 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 2 failed, 0 skipped (3 total tests) + +Failing tests: +Encountered 2 failing tests in test/InvariantAfterInvariant.t.sol:InvariantAfterInvariantTest +[FAIL: afterInvariant failure] + [SEQUENCE] + invariant_after_invariant_failure() ([RUNS]) +[FAIL: invariant failure] + [SEQUENCE] + invariant_failure() ([RUNS]) + +Encountered a total of 2 failing tests, 1 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 2 failed tests + +"#]]); +}); + +forgetest_init!(invariant_assume, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.runs = 1; + config.invariant.depth = 10; + // Should not treat vm.assume as revert. + config.invariant.fail_on_revert = true; + }); + + prj.add_test( + "InvariantAssume.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Handler is Test { + function doSomething(uint256 param) public { + vm.assume(param == 0); + } +} + +contract InvariantAssume is Test { + Handler handler; + + function setUp() public { + handler = new Handler(); + } + + function invariant_dummy() public {} +} +"#, + ); + + assert_invariant(cmd.args(["test"])).success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (2018): Function state mutability can be restricted to pure + [FILE]:7:5: + | +7 | function doSomething(uint256 param) public { + | ^ (Relevant source part starts here and spans across multiple lines). + + +Ran 1 test for test/InvariantAssume.t.sol:InvariantAssume +[PASS] invariant_dummy() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); + + // Test that max_assume_rejects is respected. + prj.update_config(|config| { + config.invariant.max_assume_rejects = 1; + }); + + assert_invariant(&mut cmd).failure().stdout_eq(str![[r#" +No files changed, compilation skipped + +Ran 1 test for test/InvariantAssume.t.sol:InvariantAssume +[FAIL: `vm.assume` rejected too many inputs (1 allowed)] invariant_dummy() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantAssume.t.sol:InvariantAssume +[FAIL: `vm.assume` rejected too many inputs (1 allowed)] invariant_dummy() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/5868 +forgetest!(invariant_calldata_dictionary, |prj, cmd| { + prj.wipe_contracts(); + prj.insert_utils(); + prj.update_config(|config| { + config.invariant.depth = 10; + }); + + prj.add_test( + "InvariantCalldataDictionary.t.sol", + r#" +import "./utils/Test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +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 Test { + 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 Test { + 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) { + 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 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); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary +[FAIL: ] + [SEQUENCE] + invariant_owner_never_changes() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary +[FAIL: ] + [SEQUENCE] + invariant_owner_never_changes() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +forgetest_init!(invariant_custom_error, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 10; + config.invariant.fail_on_revert = true; + }); + + prj.add_test( + "InvariantCustomError.t.sol", + r#" +import "forge-std/Test.sol"; + +contract ContractWithCustomError { + error InvariantCustomError(uint256, string); + + function revertWithInvariantCustomError() external { + revert InvariantCustomError(111, "custom"); + } +} + +contract Handler is Test { + ContractWithCustomError target; + + constructor() { + target = new ContractWithCustomError(); + } + + function revertTarget() external { + target.revertWithInvariantCustomError(); + } +} + +contract InvariantCustomError is Test { + Handler handler; + + function setUp() external { + handler = new Handler(); + } + + function invariant_decode_error() public {} +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantCustomError.t.sol:InvariantCustomError +[FAIL: InvariantCustomError(111, "custom")] + [SEQUENCE] + invariant_decode_error() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantCustomError.t.sol:InvariantCustomError +[FAIL: InvariantCustomError(111, "custom")] + [SEQUENCE] + invariant_decode_error() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +forgetest_init!(invariant_excluded_senders, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 10; + config.invariant.fail_on_revert = true; + }); + + prj.add_test( + "InvariantExcludedSenders.t.sol", + r#" +import "forge-std/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 Test { + InvariantSenders target; + + function setUp() public { + target = new InvariantSenders(); + } + + function invariant_check_sender() public view {} +} +"#, + ); + + assert_invariant(cmd.args(["test"])).success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful with warnings: +Warning (2018): Function state mutability can be restricted to view + [FILE]:7:5: + | +7 | function checkSender() external { + | ^ (Relevant source part starts here and spans across multiple lines). + + +Ran 1 test for test/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest +[PASS] invariant_check_sender() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + +forgetest_init!(invariant_fixtures, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.runs = 1; + config.invariant.depth = 100; + }); + + prj.add_test( + "InvariantFixtures.t.sol", + r#" +import "forge-std/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 Test { + 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); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantFixtures.t.sol:InvariantFixtures +[FAIL: assertion failed: true != false] + [SEQUENCE] + invariant_target_not_compromised() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantFixtures.t.sol:InvariantFixtures +[FAIL: assertion failed: true != false] + [SEQUENCE] + invariant_target_not_compromised() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +forgetest!(invariant_handler_failure, |prj, cmd| { + prj.insert_utils(); + prj.update_config(|config| { + config.invariant.fail_on_revert = true; + config.invariant.runs = 1; + config.invariant.depth = 10; + }); + + prj.add_test( + "InvariantHandlerFailure.t.sol", + r#" +import "./utils/Test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract Handler is Test { + function doSomething() public { + require(false, "failed on revert"); + } +} + +contract InvariantHandlerFailure is Test { + bytes4[] internal selectors; + + Handler handler; + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = handler.doSomething.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + function setUp() public { + handler = new Handler(); + } + + function statefulFuzz_BrokenInvariant() public {} +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantHandlerFailure.t.sol:InvariantHandlerFailure +[FAIL: failed on revert] + [SEQUENCE] + statefulFuzz_BrokenInvariant() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantHandlerFailure.t.sol:InvariantHandlerFailure +[FAIL: failed on revert] + [SEQUENCE] + statefulFuzz_BrokenInvariant() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// Here we test that the fuzz engine can include a contract created during the fuzz +// in its fuzz dictionary and eventually break the invariant. +// Specifically, can Judas, a created contract from Jesus, break Jesus contract +// by revealing his identity. +forgetest_init!( + #[cfg_attr(windows, ignore = "for some reason there's different rng")] + invariant_inner_contract, + |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 10; + }); + + prj.add_test( + "InvariantInnerContract.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Jesus { + address fren; + bool public identity_revealed; + + function create_fren() public { + fren = address(new Judas()); + } + + function kiss() public { + require(msg.sender == fren); + identity_revealed = true; + } +} + +contract Judas { + Jesus jesus; + + constructor() { + jesus = Jesus(msg.sender); + } + + function betray() public { + jesus.kiss(); + } +} + +contract InvariantInnerContract is Test { + Jesus jesus; + + function setUp() public { + jesus = new Jesus(); + } + + function invariantHideJesus() public { + require(jesus.identity_revealed() == false, "jesus betrayed"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantInnerContract.t.sol:InvariantInnerContract +[FAIL: jesus betrayed] + [SEQUENCE] + invariantHideJesus() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantInnerContract.t.sol:InvariantInnerContract +[FAIL: jesus betrayed] + [SEQUENCE] + invariantHideJesus() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); + + // `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2. + prj.update_config(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + // Disable persisted failures for rerunning the test. + config.invariant.failure_persist_dir = Some( + config + .invariant + .failure_persist_dir + .as_ref() + .unwrap() + .parent() + .unwrap() + .join("persistence2"), + ); + }); + cmd.assert_failure().stdout_eq(str![[r#" +No files changed, compilation skipped + +Ran 1 test for test/InvariantInnerContract.t.sol:InvariantInnerContract +[FAIL: jesus betrayed] + [Sequence] (original: 2, shrunk: 2) + sender=[..] addr=[test/InvariantInnerContract.t.sol:Jesus][..] calldata=create_fren() args=[] + sender=[..] addr=[test/InvariantInnerContract.t.sol:Judas][..] calldata=betray() args=[] + invariantHideJesus() (runs: 0, calls: 0, reverts: 1) +... +"#]]); + } +); + +// https://github.com/foundry-rs/foundry/issues/7219 +forgetest!(invariant_preserve_state, |prj, cmd| { + prj.insert_utils(); + prj.update_config(|config| { + config.invariant.depth = 10; + config.invariant.fail_on_revert = true; + }); + + prj.add_test( + "InvariantPreserveState.t.sol", + r#" +import "./utils/Test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract Handler is Test { + 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 Test { + 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); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantPreserveState.t.sol:InvariantPreserveState +[FAIL: EvmError: Revert] + [SEQUENCE] + invariant_preserve_state() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantPreserveState.t.sol:InvariantPreserveState +[FAIL: EvmError: Revert] + [SEQUENCE] + invariant_preserve_state() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// add code so contract is accounted as valid sender +// see https://github.com/foundry-rs/foundry/issues/4245 +forgetest!(invariant_reentrancy, |prj, cmd| { + prj.insert_utils(); + prj.update_config(|config| { + config.invariant.depth = 10; + config.invariant.fail_on_revert = false; + config.invariant.call_override = true; + }); + + prj.add_test( + "InvariantReentrancy.t.sol", + r#" +import "./utils/Test.sol"; + +contract Malicious { + function world() public { + payable(msg.sender).call(""); + } +} + +contract Vulnerable { + bool public open_door = false; + bool public stolen = false; + Malicious mal; + + constructor(address _mal) { + mal = Malicious(_mal); + } + + function hello() public { + open_door = true; + mal.world(); + open_door = false; + } + + function backdoor() public { + require(open_door, ""); + stolen = true; + } +} + +contract InvariantReentrancy is Test { + Vulnerable vuln; + Malicious mal; + + function setUp() public { + mal = new Malicious(); + 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"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantReentrancy.t.sol:InvariantReentrancy +[FAIL: stolen] + [SEQUENCE] + invariantNotStolen() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantReentrancy.t.sol:InvariantReentrancy +[FAIL: stolen] + [SEQUENCE] + invariantNotStolen() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +forgetest_init!(invariant_roll_fork, |prj, cmd| { + prj.wipe_contracts(); + prj.add_rpc_endpoints(); + prj.update_config(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + }); + + prj.add_test( + "InvariantRollFork.t.sol", + r#" +import "forge-std/Test.sol"; + +interface IERC20 { + function totalSupply() external view returns (uint256 supply); +} + +contract RollForkHandler is Test { + uint256 public totalSupply; + + function work() external { + vm.rollFork(block.number + 1); + totalSupply = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F).totalSupply(); + } +} + +contract InvariantRollForkBlockTest is Test { + RollForkHandler forkHandler; + + function setUp() public { + vm.createSelectFork("mainnet", 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"); + } +} + +contract InvariantRollForkStateTest is Test { + RollForkHandler forkHandler; + + function setUp() public { + vm.createSelectFork("mainnet", 19812632); + forkHandler = new RollForkHandler(); + } + + /// forge-config: default.invariant.runs = 1 + function invariant_fork_handler_state() public { + require(forkHandler.totalSupply() < 3254378807384273078310283461, "wrong supply"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test", "-j1"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantRollFork.t.sol:InvariantRollForkBlockTest +[FAIL: too many blocks mined] + [SEQUENCE] + invariant_fork_handler_block() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/InvariantRollFork.t.sol:InvariantRollForkStateTest +[FAIL: wrong supply] + [SEQUENCE] + invariant_fork_handler_state() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 2 test suites [ELAPSED]: 0 tests passed, 2 failed, 0 skipped (2 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantRollFork.t.sol:InvariantRollForkBlockTest +[FAIL: too many blocks mined] + [SEQUENCE] + invariant_fork_handler_block() ([RUNS]) + +Encountered 1 failing test in test/InvariantRollFork.t.sol:InvariantRollForkStateTest +[FAIL: wrong supply] + [SEQUENCE] + invariant_fork_handler_state() ([RUNS]) + +Encountered a total of 2 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 2 failed tests + +"#]]); +}); + +forgetest_init!(invariant_scrape_values, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 10; + }); + + prj.add_test( + "InvariantScrapeValues.t.sol", + r#" +import "forge-std/Test.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 Test { + 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 Test { + 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"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test", "-j1"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantScrapeValues.t.sol:FindFromLogValueTest +[FAIL: value from logs found] + [SEQUENCE] + invariant_value_not_found() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/InvariantScrapeValues.t.sol:FindFromReturnValueTest +[FAIL: value from return found] + [SEQUENCE] + invariant_value_not_found() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 2 test suites [ELAPSED]: 0 tests passed, 2 failed, 0 skipped (2 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantScrapeValues.t.sol:FindFromLogValueTest +[FAIL: value from logs found] + [SEQUENCE] + invariant_value_not_found() ([RUNS]) + +Encountered 1 failing test in test/InvariantScrapeValues.t.sol:FindFromReturnValueTest +[FAIL: value from return found] + [SEQUENCE] + invariant_value_not_found() ([RUNS]) + +Encountered a total of 2 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 2 failed tests + +"#]]); +}); + +forgetest_init!(invariant_sequence_no_reverts, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 15; + config.invariant.fail_on_revert = false; + // Use original counterexample to test sequence len. + config.invariant.shrink_run_limit = 0; + }); + + prj.add_test( + "InvariantSequenceNoReverts.t.sol", + r#" +import "forge-std/Test.sol"; + +contract SequenceNoReverts { + uint256 public count; + + function work(uint256 x) public { + require(x % 2 != 0); + count++; + } +} + +contract SequenceNoRevertsTest is Test { + SequenceNoReverts target; + + function setUp() public { + target = new SequenceNoReverts(); + } + + function invariant_no_reverts() public view { + require(target.count() < 10, "condition met"); + } +} +"#, + ); + + // ensure original counterexample len is 10 (even without shrinking) + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantSequenceNoReverts.t.sol:SequenceNoRevertsTest +[FAIL: condition met] + [Sequence] (original: 10, shrunk: 10) +... + invariant_no_reverts() ([..]) +... +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) +... +"#]]); +}); + +forgetest_init!( + #[cfg_attr(windows, ignore = "for some reason there's different rng")] + invariant_shrink_big_sequence, + |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + config.invariant.runs = 1; + config.invariant.depth = 1000; + }); + + prj.add_test( + "InvariantShrinkBigSequence.t.sol", + r#" +import "forge-std/Test.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 Test { + ShrinkBigSequence target; + + function setUp() public { + target = new ShrinkBigSequence(); + } + + function invariant_shrink_big_sequence() public view { + target.checkCond(); + } +} +"#, + ); + + // ensure shrinks to same sequence of 77 + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantShrinkBigSequence.t.sol:ShrinkBigSequenceTest +[FAIL: condition met] + [Sequence] (original: [..], shrunk: 77) +... +"#]]); + cmd.assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantShrinkBigSequence.t.sol:ShrinkBigSequenceTest +[FAIL: invariant_shrink_big_sequence replay failure] + [Sequence] (original: [..], shrunk: 77) +... +"#]]); + } +); + +forgetest_init!(invariant_shrink_fail_on_revert, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.fuzz.seed = Some(U256::from(119u32)); + config.invariant.fail_on_revert = true; + config.invariant.runs = 1; + config.invariant.depth = 200; + }); + + prj.add_test( + "InvariantShrinkFailOnRevert.t.sol", + r#" +import "forge-std/Test.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 Test { + ShrinkFailOnRevert target; + + function setUp() public { + target = new ShrinkFailOnRevert(); + } + + function invariant_shrink_fail_on_revert() public view {} +} +"#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/InvariantShrinkFailOnRevert.t.sol:ShrinkFailOnRevertTest +[FAIL: condition met] + [Sequence] (original: [..], shrunk: 10) +... +"#]]); +}); + +forgetest_init!(invariant_shrink_with_assert, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.fuzz.seed = Some(U256::from(100u32)); + config.invariant.runs = 1; + config.invariant.depth = 15; + }); + + prj.add_test( + "InvariantShrinkWithAssert.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Counter { + uint256 public number; + + function increment() public { + number++; + } + + function decrement() public { + number--; + } +} + +contract InvariantShrinkWithAssert is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + } + + function invariant_with_assert() public { + assertTrue(counter.number() < 2, "wrong counter assert"); + } + + function invariant_with_require() public { + require(counter.number() < 2, "wrong counter require"); + } +} +"#, + ); + + cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" +... +Ran 2 tests for test/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithAssert +[FAIL: wrong counter assert] + [Sequence] (original: 2, shrunk: 2) +... + invariant_with_assert() ([..]) +... +[FAIL: wrong counter require] + [Sequence] (original: 2, shrunk: 2) +... + invariant_with_require() ([..]) +... +"#]]); +}); + +forgetest_init!(invariant_test1, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.invariant.depth = 10; + }); + + prj.add_test( + "InvariantTest1.t.sol", + r#" +import "forge-std/Test.sol"; + +contract InvariantBreaker { + bool public flag0 = true; + bool public flag1 = true; + + function set0(int256 val) public returns (bool) { + if (val % 100 == 0) { + flag0 = false; + } + return flag0; + } + + function set1(int256 val) public returns (bool) { + if (val % 10 == 0 && !flag0) { + flag1 = false; + } + return flag1; + } +} + +contract InvariantTest is Test { + InvariantBreaker inv; + + function setUp() public { + inv = new InvariantBreaker(); + } + + function invariant_neverFalse() public { + require(inv.flag1(), "false"); + } + + function statefulFuzz_neverFalseWithInvariantAlias() public { + require(inv.flag1(), "false"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +... +Ran 2 tests for test/InvariantTest1.t.sol:InvariantTest +[FAIL: false] + [SEQUENCE] + invariant_neverFalse() ([RUNS]) + +[STATS] + +[FAIL: false] + [SEQUENCE] + statefulFuzz_neverFalseWithInvariantAlias() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 2 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 2 failed, 0 skipped (2 total tests) + +Failing tests: +Encountered 2 failing tests in test/InvariantTest1.t.sol:InvariantTest +[FAIL: false] + [SEQUENCE] + invariant_neverFalse() ([RUNS]) +[FAIL: false] + [SEQUENCE] + statefulFuzz_neverFalseWithInvariantAlias() ([RUNS]) + +Encountered a total of 2 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 2 failed tests + +"#]]); +}); diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/cli/test_cmd/invariant/mod.rs similarity index 54% rename from crates/forge/tests/it/invariant.rs rename to crates/forge/tests/cli/test_cmd/invariant/mod.rs index 83ba8660b4457..4e93f00b404b1 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/mod.rs @@ -1,700 +1,16 @@ -//! Invariant tests. - -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use alloy_primitives::U256; -use forge::fuzz::CounterExample; -use foundry_test_utils::{Filter, forgetest_init, str}; -use std::collections::BTreeMap; - -macro_rules! get_counterexample { - ($runner:ident, $filter:expr) => { - $runner - .test_collect($filter) - .unwrap() - .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_with_alias() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantTest1.t.sol"); - let results = TEST_DATA_DEFAULT.runner().test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantTest1.t.sol:InvariantTest", - vec![ - ("invariant_neverFalse()", false, Some("false".into()), None, None), - ( - "statefulFuzz_neverFalseWithInvariantAlias()", - false, - Some("false".into()), - None, - None, - ), - ], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_filters() { - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.runs = 10; - }); - - // Contracts filter tests. - assert_multiple( - &runner - .test_collect(&Filter::new( - ".*", - ".*", - ".*fuzz/invariant/target/(ExcludeContracts|TargetContracts).t.sol", - )) - .unwrap(), - BTreeMap::from([ - ( - "default/fuzz/invariant/target/ExcludeContracts.t.sol:ExcludeContracts", - vec![("invariantTrueWorld()", true, None, None, None)], - ), - ( - "default/fuzz/invariant/target/TargetContracts.t.sol:TargetContracts", - vec![("invariantTrueWorld()", true, None, None, None)], - ), - ]), - ); - - // Senders filter tests. - assert_multiple( - &runner - .test_collect(&Filter::new( - ".*", - ".*", - ".*fuzz/invariant/target/(ExcludeSenders|TargetSenders).t.sol", - )) - .unwrap(), - BTreeMap::from([ - ( - "default/fuzz/invariant/target/ExcludeSenders.t.sol:ExcludeSenders", - vec![("invariantTrueWorld()", true, None, None, None)], - ), - ( - "default/fuzz/invariant/target/TargetSenders.t.sol:TargetSenders", - vec![("invariantTrueWorld()", false, Some("false world".into()), None, None)], - ), - ]), - ); - - // Interfaces filter tests. - assert_multiple( - &runner - .test_collect(&Filter::new( - ".*", - ".*", - ".*fuzz/invariant/target/TargetInterfaces.t.sol", - )) - .unwrap(), - BTreeMap::from([( - "default/fuzz/invariant/target/TargetInterfaces.t.sol:TargetWorldInterfaces", - vec![("invariantTrueWorld()", false, Some("false world".into()), None, None)], - )]), - ); - - // Selectors filter tests. - assert_multiple( - &runner - .test_collect(&Filter::new( - ".*", - ".*", - ".*fuzz/invariant/target/(ExcludeSelectors|TargetSelectors).t.sol", - )) - .unwrap(), - BTreeMap::from([ - ( - "default/fuzz/invariant/target/ExcludeSelectors.t.sol:ExcludeSelectors", - vec![("invariantFalseWorld()", true, None, None, None)], - ), - ( - "default/fuzz/invariant/target/TargetSelectors.t.sol:TargetSelectors", - vec![("invariantTrueWorld()", true, None, None, None)], - ), - ]), - ); - - // Artifacts filter tests. - assert_multiple( - &runner.test_collect(&Filter::new( - ".*", - ".*", - ".*fuzz/invariant/targetAbi/(ExcludeArtifacts|TargetArtifacts|TargetArtifactSelectors|TargetArtifactSelectors2).t.sol", - )).unwrap(), - BTreeMap::from([ - ( - "default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:ExcludeArtifacts", - vec![("invariantShouldPass()", true, None, None, None)], - ), - ( - "default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol:TargetArtifacts", - vec![ - ("invariantShouldPass()", true, None, None, None), - ( - "invariantShouldFail()", - false, - Some("false world".into()), - None, - None, - ), - ], - ), - ( - "default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:TargetArtifactSelectors", - vec![("invariantShouldPass()", true, None, None, None)], - ), - ( - "default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:TargetArtifactSelectors2", - vec![( - "invariantShouldFail()", - false, - Some("it's false".into()), - None, - None, - )], - ), - ]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_override() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.fail_on_revert = false; - config.invariant.call_override = true; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", - vec![("invariantNotStolen()", false, Some("stolen".into()), None, None)], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_fail_on_revert() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantHandlerFailure.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.fail_on_revert = true; - config.invariant.runs = 1; - config.invariant.depth = 10; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantHandlerFailure.t.sol:InvariantHandlerFailure", - vec![( - "statefulFuzz_BrokenInvariant()", - false, - Some("failed on revert".into()), - None, - None, - )], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_invariant_storage() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/storage/InvariantStorageTest.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.depth = 100; - if cfg!(windows) { - config.invariant.depth += 50; - } - config.fuzz.seed = Some(U256::from(6u32)); - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "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), - ("invariantChangeUint()", false, Some("changedUint".to_string()), None, None), - ("invariantPush()", false, Some("pushUint".to_string()), None, None), - ], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_inner_contract() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantInnerContract.t.sol"); - let results = TEST_DATA_DEFAULT.runner().test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantInnerContract.t.sol:InvariantInnerContract", - vec![("invariantHideJesus()", false, Some("jesus betrayed".into()), None, None)], - )]), - ); -} - -#[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 = - TEST_DATA_DEFAULT.runner_with(|config| config.fuzz.seed = Some(U256::from(119u32))); - - match get_counterexample!(runner, &filter) { - CounterExample::Single(_) => panic!("CounterExample should be a sequence."), - // `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2. - CounterExample::Sequence(_, sequence) => { - assert!(sequence.len() <= 3); - - 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(), - "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(), - "default/fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" - ); - assert_eq!(betray_sequence.signature.unwrap(), "betray()"); - } - } - }; -} - -#[tokio::test(flavor = "multi_thread")] -#[cfg_attr(windows, ignore = "for some reason there's different rng")] -async fn test_invariant_assert_shrink() { - // ensure assert shrinks to same sequence of 2 as require - check_shrink_sequence("invariant_with_assert", 2).await; -} - -#[tokio::test(flavor = "multi_thread")] -#[cfg_attr(windows, ignore = "for some reason there's different rng")] -async fn test_invariant_require_shrink() { - // ensure require shrinks to same sequence of 2 as assert - check_shrink_sequence("invariant_with_require", 2).await; -} - -async fn check_shrink_sequence(test_pattern: &str, expected_len: usize) { - let filter = - Filter::new(test_pattern, ".*", ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fuzz.seed = Some(U256::from(100u32)); - config.invariant.runs = 1; - config.invariant.depth = 15; - }); - - match get_counterexample!(runner, &filter) { - CounterExample::Single(_) => panic!("CounterExample should be a sequence."), - CounterExample::Sequence(_, sequence) => { - assert_eq!(sequence.len(), expected_len); - } - }; -} - -#[tokio::test(flavor = "multi_thread")] -#[cfg_attr(windows, ignore = "for some reason there's different rng")] -async fn test_shrink_big_sequence() { - let filter = - Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantShrinkBigSequence.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fuzz.seed = Some(U256::from(119u32)); - config.invariant.runs = 1; - config.invariant.depth = 1000; - }); - - let initial_counterexample = runner - .test_collect(&filter) - .unwrap() - .values() - .last() - .expect("Invariant contract should be testable.") - .test_results - .values() - .last() - .expect("Invariant contract should be testable.") - .counterexample - .clone() - .unwrap(); - - 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); - - // test failure persistence - let results = runner.test_collect(&filter).unwrap(); - 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 - .clone() - .unwrap() - { - CounterExample::Single(_) => panic!("CounterExample should be a sequence."), - 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")] -#[cfg_attr(windows, ignore = "for some reason there's different rng")] -async fn test_shrink_fail_on_revert() { - let filter = - Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fuzz.seed = Some(U256::from(119u32)); - config.invariant.fail_on_revert = true; - config.invariant.runs = 1; - config.invariant.depth = 200; - }); - - match get_counterexample!(runner, &filter) { - CounterExample::Single(_) => panic!("CounterExample should be a sequence."), - 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"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.fail_on_revert = true; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", - vec![( - "invariant_preserve_state()", - false, - Some("EvmError: Revert".into()), - None, - None, - )], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -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", - )) - .unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", - vec![( - "invariant_owner_never_changes()", - false, - Some("".into()), - None, - None, - )], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_assume_does_not_revert() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantAssume.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - // Should not treat vm.assume as revert. - config.invariant.fail_on_revert = true; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/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 = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.runs = 1; - config.invariant.depth = 10; - config.invariant.max_assume_rejects = 1; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", - vec![( - "invariant_dummy()", - false, - Some("`vm.assume` rejected too many inputs (1 allowed)".into()), - None, - None, - )], - )]), - ); -} - -#[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_with(|config| { - config.invariant.fail_on_revert = true; - }); - let results = runner.test_collect(&filter).unwrap(); - 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, - )], - )]), - ); -} - -#[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_with(|config| { - config.invariant.fail_on_revert = true; - }); - let results = runner.test_collect(&filter).unwrap(); - 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("wrong target selector called".into()), - None, - None, - )], - ), - ]), - ); -} - -#[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_with(|config| { - config.invariant.runs = 1; - config.invariant.depth = 100; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantFixtures.t.sol:InvariantFixtures", - vec![( - "invariant_target_not_compromised()", - false, - Some("".into()), - None, - None, - )], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_scrape_values() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantScrapeValues.t.sol"); - let results = TEST_DATA_DEFAULT.runner().test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([ - ( - "default/fuzz/invariant/common/InvariantScrapeValues.t.sol:FindFromReturnValueTest", - vec![( - "invariant_value_not_found()", - false, - Some("value from return found".into()), - None, - None, - )], - ), - ( - "default/fuzz/invariant/common/InvariantScrapeValues.t.sol:FindFromLogValueTest", - vec![( - "invariant_value_not_found()", - false, - Some("value from logs found".into()), - None, - None, - )], - ), - ]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_invariant_roll_fork_handler() { - let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantRollFork.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fuzz.seed = Some(U256::from(119u32)); - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([ - ( - "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkBlockTest", - vec![( - "invariant_fork_handler_block()", - false, - Some("too many blocks mined".into()), - None, - None, - )], - ), - ( - "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkStateTest", - vec![( - "invariant_fork_handler_state()", - false, - Some("wrong supply".into()), - None, - None, - )], - ), - ]), - ); -} - -#[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_with(|config| { - config.invariant.fail_on_revert = true; - }); - let results = runner.test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest", - vec![("invariant_check_sender()", true, None, None, None)], - )]), - ); -} - -#[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 results = TEST_DATA_DEFAULT.runner().test_collect(&filter).unwrap(); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantAfterInvariant.t.sol:InvariantAfterInvariantTest", - vec![ - ( - "invariant_after_invariant_failure()", - false, - Some("afterInvariant failure".into()), - None, - None, - ), - ("invariant_failure()", false, Some("invariant failure".into()), None, None), - ("invariant_success()", true, None, None, None), - ], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_no_reverts_in_counterexample() { - let filter = - Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantSequenceNoReverts.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.fail_on_revert = false; - // Use original counterexample to test sequence len. - config.invariant.shrink_run_limit = 0; - }); - - match get_counterexample!(runner, &filter) { - CounterExample::Single(_) => panic!("CounterExample should be a sequence."), - CounterExample::Sequence(_, sequence) => { - // ensure original counterexample len is 10 (even without shrinking) - assert_eq!(sequence.len(), 10); - } - }; +use foundry_test_utils::{TestCommand, forgetest_init, snapbox::cmd::OutputAssert, str}; + +mod common; +mod storage; +mod target; + +fn assert_invariant(cmd: &mut TestCommand) -> OutputAssert { + cmd.assert_with(&[ + ("[RUNS]", r"runs: \d+, calls: \d+, reverts: \d+"), + ("[SEQUENCE]", r"\[Sequence\].*(\n\t\t.*)*"), + ("[STATS]", r"╭[\s\S]*?╰.*"), + ]) } // Tests that a persisted failure doesn't fail due to assume revert if test driver is changed. diff --git a/testdata/default/fuzz/invariant/storage/InvariantStorageTest.t.sol b/crates/forge/tests/cli/test_cmd/invariant/storage.rs similarity index 79% rename from testdata/default/fuzz/invariant/storage/InvariantStorageTest.t.sol rename to crates/forge/tests/cli/test_cmd/invariant/storage.rs index 890c495c310bb..83c4f72bca93d 100644 --- a/testdata/default/fuzz/invariant/storage/InvariantStorageTest.t.sol +++ b/crates/forge/tests/cli/test_cmd/invariant/storage.rs @@ -1,6 +1,13 @@ -pragma solidity >0.8.13; +use super::*; -import "ds-test/test.sol"; +forgetest_init!( + #[ignore = "slow"] + storage, + |prj, cmd| { + prj.add_test( + "name", + r#" +import "forge-std/Test.sol"; contract Contract { address public addr = address(0xbeef); @@ -33,7 +40,7 @@ contract Contract { } } -contract InvariantStorageTest is DSTest { +contract InvariantStorageTest is Test { Contract c; function setUp() public { @@ -56,3 +63,9 @@ contract InvariantStorageTest is DSTest { require(c.pushNum() == 0, "pushUint"); } } +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#""#]]); + } +); diff --git a/crates/forge/tests/cli/test_cmd/invariant/target.rs b/crates/forge/tests/cli/test_cmd/invariant/target.rs new file mode 100644 index 0000000000000..31e684d4f307d --- /dev/null +++ b/crates/forge/tests/cli/test_cmd/invariant/target.rs @@ -0,0 +1,766 @@ +use super::*; + +forgetest!(filters, |prj, cmd| { + prj.insert_vm(); + prj.insert_ds_test(); + prj.update_config(|config| { + config.invariant.runs = 50; + config.invariant.depth = 10; + }); + + prj.add_test( + "ExcludeContracts.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +contract Hello { + bool public world = true; + + function change() public { + world = false; + } +} + +contract ExcludeContracts is Test { + Hello hello; + + function setUp() public { + hello = new Hello(); + new Hello(); + } + + function excludeContracts() public view returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(hello); + return addrs; + } + + function invariantTrueWorld() public { + require(hello.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "ExcludeSelectors.t.sol", + r#" +import { DSTest as Test } from "src/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 Test { + Hello hello; + + function setUp() public { + hello = new Hello(); + } + + function excludeSelectors() public view 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"); + } +} +"#, + ); + + prj.add_test( + "ExcludeSenders.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +contract Hello { + address seed_address = address(0xdeadbeef); + bool public world = true; + + function changeBeef() public { + require(msg.sender == address(0xdeadbeef)); + world = false; + } + + // address(0) should be automatically excluded + function change0() public { + require(msg.sender == address(0)); + world = false; + } +} + +contract ExcludeSenders is Test { + Hello hello; + + function setUp() public { + hello = new Hello(); + } + + function excludeSenders() public view returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(0xdeadbeef); + return addrs; + } + + // Tests clashing. Exclusion takes priority. + function targetSenders() public view returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(0xdeadbeef); + return addrs; + } + + function invariantTrueWorld() public { + require(hello.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "TargetContracts.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +contract Hello { + bool public world = true; + + function change() public { + world = false; + } +} + +contract TargetContracts is Test { + Hello hello1; + Hello hello2; + + function setUp() public { + hello1 = new Hello(); + hello2 = new Hello(); + } + + function targetContracts() public view returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(hello1); + return addrs; + } + + function invariantTrueWorld() public { + require(hello2.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "TargetInterfaces.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzInterface { + address target; + string[] artifacts; +} + +contract Hello { + bool public world; + + function changeWorld() external { + world = true; + } +} + +interface IHello { + function world() external view returns (bool); + function changeWorld() external; +} + +contract HelloProxy { + address internal immutable _implementation; + + constructor(address implementation_) { + _implementation = implementation_; + } + + function _delegate(address implementation) internal { + assembly { + calldatacopy(0, 0, calldatasize()) + + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + returndatacopy(0, 0, returndatasize()) + + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + } + + fallback() external payable { + _delegate(_implementation); + } +} + +contract TargetWorldInterfaces is Test { + IHello proxy; + + function setUp() public { + Hello hello = new Hello(); + proxy = IHello(address(new HelloProxy(address(hello)))); + } + + function targetInterfaces() public view returns (FuzzInterface[] memory) { + FuzzInterface[] memory targets = new FuzzInterface[](1); + + string[] memory artifacts = new string[](1); + artifacts[0] = "IHello"; + + targets[0] = FuzzInterface(address(proxy), artifacts); + + return targets; + } + + function invariantTrueWorld() public { + require(proxy.world() == false, "false world"); + } +} +"#, + ); + + prj.add_test( + "TargetSelectors.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract Hello { + bool public world = true; + + function change() public { + world = true; + } + + function real_change() public { + world = false; + } +} + +contract TargetSelectors is Test { + Hello hello; + + function setUp() public { + hello = new Hello(); + } + + function targetSelectors() public view 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 invariantTrueWorld() public { + require(hello.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "TargetSenders.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +contract Hello { + bool public world = true; + + function change() public { + require(msg.sender == address(0xdeadbeef)); + world = false; + } +} + +contract TargetSenders is Test { + Hello hello; + + function setUp() public { + hello = new Hello(); + } + + function targetSenders() public view returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(0xdeadbeef); + return addrs; + } + + function invariantTrueWorld() public { + require(hello.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "ExcludeArtifacts.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +// Will get automatically excluded. Otherwise it would throw error. +contract NoMutFunctions { + function no_change() public pure {} +} + +contract Excluded { + bool public world = true; + + function change() public { + world = false; + } +} + +contract Hello { + bool public world = true; + + function change() public { + world = false; + } +} + +contract ExcludeArtifacts is Test { + Excluded excluded; + + function setUp() public { + excluded = new Excluded(); + new Hello(); + new NoMutFunctions(); + } + + function excludeArtifacts() public returns (string[] memory) { + string[] memory abis = new string[](1); + abis[0] = "test/ExcludeArtifacts.t.sol:Excluded"; + return abis; + } + + function invariantShouldPass() public { + require(excluded.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "TargetArtifactSelectors2.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzArtifactSelector { + string artifact; + bytes4[] selectors; +} + +contract Parent { + bool public should_be_true = true; + address public child; + + function change() public { + child = msg.sender; + should_be_true = false; + } + + function create() public { + new Child(); + } +} + +contract Child { + Parent parent; + bool public changed = false; + + constructor() { + parent = Parent(msg.sender); + } + + function change_parent() public { + parent.change(); + } + + function tracked_change_parent() public { + parent.change(); + } +} + +contract TargetArtifactSelectors2 is Test { + Parent parent; + + function setUp() public { + parent = new Parent(); + } + + 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] = FuzzArtifactSelector( + "test/TargetArtifactSelectors2.t.sol:Child", selectors_child + ); + + bytes4[] memory selectors_parent = new bytes4[](1); + selectors_parent[0] = Parent.create.selector; + targets[1] = FuzzArtifactSelector( + "test/TargetArtifactSelectors2.t.sol:Parent", selectors_parent + ); + return targets; + } + + function invariantShouldFail() public { + if (!parent.should_be_true()) { + require(!Child(address(parent.child())).changed(), "should have not happened"); + } + require(parent.should_be_true() == true, "it's false"); + } +} +"#, + ); + + prj.add_test( + "TargetArtifactSelectors.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzArtifactSelector { + string artifact; + bytes4[] selectors; +} + +contract Hi { + bool public world = true; + + function no_change() public { + world = true; + } + + function change() public { + world = false; + } +} + +contract TargetArtifactSelectors is Test { + Hi hello; + + function setUp() public { + hello = new Hi(); + } + + 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] = + FuzzArtifactSelector("test/TargetArtifactSelectors.t.sol:Hi", selectors); + return targets; + } + + function invariantShouldPass() public { + require(hello.world() == true, "false world"); + } +} +"#, + ); + + prj.add_test( + "TargetArtifacts.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +contract Targeted { + bool public world = true; + + function change() public { + world = false; + } +} + +contract Hello { + bool public world = true; + + function no_change() public {} +} + +contract TargetArtifacts is Test { + Targeted target1; + Targeted target2; + Hello hello; + + function setUp() public { + target1 = new Targeted(); + target2 = new Targeted(); + hello = new Hello(); + } + + function targetArtifacts() public returns (string[] memory) { + string[] memory abis = new string[](1); + abis[0] = "test/TargetArtifacts.t.sol:Targeted"; + return abis; + } + + function invariantShouldPass() public { + require(target2.world() == true || target1.world() == true || hello.world() == true, "false world"); + } + + function invariantShouldFail() public { + require(target2.world() == true || target1.world() == true, "false world"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test", "-j1"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/ExcludeArtifacts.t.sol:ExcludeArtifacts +[PASS] invariantShouldPass() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/ExcludeContracts.t.sol:ExcludeContracts +[PASS] invariantTrueWorld() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/ExcludeSelectors.t.sol:ExcludeSelectors +[PASS] invariantFalseWorld() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/ExcludeSenders.t.sol:ExcludeSenders +[PASS] invariantTrueWorld() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/TargetArtifactSelectors.t.sol:TargetArtifactSelectors +[PASS] invariantShouldPass() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/TargetArtifactSelectors2.t.sol:TargetArtifactSelectors2 +[FAIL: it's false] + [SEQUENCE] + invariantShouldFail() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 2 tests for test/TargetArtifacts.t.sol:TargetArtifacts +[FAIL: false world] + [SEQUENCE] + invariantShouldFail() ([RUNS]) + +[STATS] + +[PASS] invariantShouldPass() ([RUNS]) + +[STATS] + +Suite result: FAILED. 1 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/TargetContracts.t.sol:TargetContracts +[PASS] invariantTrueWorld() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/TargetInterfaces.t.sol:TargetWorldInterfaces +[FAIL: false world] + [SEQUENCE] + invariantTrueWorld() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/TargetSelectors.t.sol:TargetSelectors +[PASS] invariantTrueWorld() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/TargetSenders.t.sol:TargetSenders +[FAIL: false world] + [SEQUENCE] + invariantTrueWorld() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 11 test suites [ELAPSED]: 8 tests passed, 4 failed, 0 skipped (12 total tests) + +Failing tests: +Encountered 1 failing test in test/TargetArtifactSelectors2.t.sol:TargetArtifactSelectors2 +[FAIL: it's false] + [SEQUENCE] + invariantShouldFail() ([RUNS]) + +Encountered 1 failing test in test/TargetArtifacts.t.sol:TargetArtifacts +[FAIL: false world] + [SEQUENCE] + invariantShouldFail() ([RUNS]) + +Encountered 1 failing test in test/TargetInterfaces.t.sol:TargetWorldInterfaces +[FAIL: false world] + [SEQUENCE] + invariantTrueWorld() ([RUNS]) + +Encountered 1 failing test in test/TargetSenders.t.sol:TargetSenders +[FAIL: false world] + [SEQUENCE] + invariantTrueWorld() ([RUNS]) + +Encountered a total of 4 failing tests, 8 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 4 failed tests + +"#]]); +}); + +// 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 +forgetest!(fuzzed_selected_targets, |prj, cmd| { + prj.insert_vm(); + prj.insert_ds_test(); + prj.update_config(|config| { + config.invariant.depth = 10; + config.invariant.fail_on_revert = true; + }); + + prj.add_test( + "FuzzedTargetContracts.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; +import "src/Vm.sol"; + +contract Target { + uint256 count; + + function wrongSelector() external { + revert("wrong target selector called"); + } + + function goodSelector() external { + count++; + } +} + +contract Handler is Test { + function increment() public { + Target(0x6B175474E89094C44Da98b954EedeAC495271d0F).goodSelector(); + } +} + +contract ExplicitTargetContract is Test { + Vm constant vm = Vm(HEVM_ADDRESS); + 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 view returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(handler); + return addrs; + } + + function invariant_explicit_target() public {} +} + +contract DynamicTargetContract is Test { + Vm constant vm = Vm(HEVM_ADDRESS); + 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 {} +} +"#, + ); + + assert_invariant(cmd.args(["test", "-j1"])).failure().stdout_eq(str![[r#" +... +Ran 1 test for test/FuzzedTargetContracts.t.sol:DynamicTargetContract +[FAIL: wrong target selector called] + [SEQUENCE] + invariant_dynamic_targets() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/FuzzedTargetContracts.t.sol:ExplicitTargetContract +[PASS] invariant_explicit_target() ([RUNS]) + +[STATS] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 2 test suites [ELAPSED]: 1 tests passed, 1 failed, 0 skipped (2 total tests) + +Failing tests: +Encountered 1 failing test in test/FuzzedTargetContracts.t.sol:DynamicTargetContract +[FAIL: wrong target selector called] + [SEQUENCE] + invariant_dynamic_targets() ([RUNS]) + +Encountered a total of 1 failing tests, 1 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd/logs.rs b/crates/forge/tests/cli/test_cmd/logs.rs new file mode 100644 index 0000000000000..7aeafc2065ac8 --- /dev/null +++ b/crates/forge/tests/cli/test_cmd/logs.rs @@ -0,0 +1,756 @@ +//! Tests for various logging functionality + +use foundry_test_utils::str; + +forgetest_init!(debug_logs, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "DebugLogs.t.sol", + r#" +import "forge-std/Test.sol"; + +contract DebugLogsTest is Test { + constructor() { + emit log_uint(0); + } + + function setUp() public { + emit log_uint(1); + } + + function test1() public { + emit log_uint(2); + } + + function test2() public { + emit log_uint(3); + } + + function testRevertIfWithRevert() public { + Fails fails = new Fails(); + emit log_uint(4); + vm.expectRevert(); + fails.failure(); + } + + /// forge-config: default.allow_internal_expect_revert = true + function testRevertIfWithRequire() public { + emit log_uint(5); + vm.expectRevert(); + require(false); + } + + function testLog() public { + emit log("Error: Assertion Failed"); + } + + function testLogs() public { + emit logs(bytes("abcd")); + } + + function testLogAddress() public { + emit log_address(address(1)); + } + + function testLogBytes32() public { + emit log_bytes32(bytes32("abcd")); + } + + function testLogInt() public { + emit log_int(int256(-31337)); + } + + function testLogBytes() public { + emit log_bytes(bytes("abcd")); + } + + function testLogString() public { + emit log_string("here"); + } + + function testLogNamedAddress() public { + emit log_named_address("address", address(1)); + } + + function testLogNamedBytes32() public { + emit log_named_bytes32("abcd", bytes32("abcd")); + } + + function testLogNamedDecimalInt() public { + emit log_named_decimal_int("amount", int256(-31337), uint256(18)); + } + + function testLogNamedDecimalUint() public { + emit log_named_decimal_uint("amount", uint256(1 ether), uint256(18)); + } + + function testLogNamedInt() public { + emit log_named_int("amount", int256(-31337)); + } + + function testLogNamedUint() public { + emit log_named_uint("amount", uint256(1 ether)); + } + + function testLogNamedBytes() public { + emit log_named_bytes("abcd", bytes("abcd")); + } + + function testLogNamedString() public { + emit log_named_string("key", "val"); + } +} + +contract Fails is Test { + function failure() public { + emit log_uint(100); + revert(); + } +} +"#, + ); + + cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 19 tests for test/DebugLogs.t.sol:DebugLogsTest +[PASS] test1() ([GAS]) +Logs: + 0 + 1 + 2 + +[PASS] test2() ([GAS]) +Logs: + 0 + 1 + 3 + +[PASS] testLog() ([GAS]) +Logs: + 0 + 1 + Error: Assertion Failed + +[PASS] testLogAddress() ([GAS]) +Logs: + 0 + 1 + 0x0000000000000000000000000000000000000001 + +[PASS] testLogBytes() ([GAS]) +Logs: + 0 + 1 + 0x61626364 + +[PASS] testLogBytes32() ([GAS]) +Logs: + 0 + 1 + 0x6162636400000000000000000000000000000000000000000000000000000000 + +[PASS] testLogInt() ([GAS]) +Logs: + 0 + 1 + -31337 + +[PASS] testLogNamedAddress() ([GAS]) +Logs: + 0 + 1 + address: 0x0000000000000000000000000000000000000001 + +[PASS] testLogNamedBytes() ([GAS]) +Logs: + 0 + 1 + abcd: 0x61626364 + +[PASS] testLogNamedBytes32() ([GAS]) +Logs: + 0 + 1 + abcd: 0x6162636400000000000000000000000000000000000000000000000000000000 + +[PASS] testLogNamedDecimalInt() ([GAS]) +Logs: + 0 + 1 + amount: -0.000000000000031337 + +[PASS] testLogNamedDecimalUint() ([GAS]) +Logs: + 0 + 1 + amount: 1.000000000000000000 + +[PASS] testLogNamedInt() ([GAS]) +Logs: + 0 + 1 + amount: -31337 + +[PASS] testLogNamedString() ([GAS]) +Logs: + 0 + 1 + key: val + +[PASS] testLogNamedUint() ([GAS]) +Logs: + 0 + 1 + amount: 1000000000000000000 + +[PASS] testLogString() ([GAS]) +Logs: + 0 + 1 + here + +[PASS] testLogs() ([GAS]) +Logs: + 0 + 1 + 0x61626364 + +[PASS] testRevertIfWithRequire() ([GAS]) +Logs: + 0 + 1 + 5 + +[PASS] testRevertIfWithRevert() ([GAS]) +Logs: + 0 + 1 + 4 + 100 + +Suite result: ok. 19 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 19 tests passed, 0 failed, 0 skipped (19 total tests) + +"#]]); +}); + +forgetest_init!(hardhat_logs, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "HardhatLogs.t.sol", + r#" +import "forge-std/console.sol"; + +contract HardhatLogsTest { + constructor() { + console.log("constructor"); + } + + string testStr; + int256 testInt; + uint256 testUint; + bool testBool; + address testAddr; + bytes testBytes; + + function setUp() public { + testStr = "test"; + testInt = -31337; + testUint = 1; + testBool = false; + testAddr = 0x0000000000000000000000000000000000000001; + testBytes = "a"; + } + + function testInts() public view { + console.log(uint256(0)); + console.log(uint256(1)); + console.log(uint256(2)); + console.log(uint256(3)); + } + + function testStrings() public view { + console.log("testStrings"); + } + + function testMisc() public view { + console.log("testMisc", address(1)); + console.log("testMisc", uint256(42)); + } + + function testConsoleLog() public view { + console.log(testStr); + } + + function testLogInt() public view { + console.logInt(testInt); + } + + function testLogUint() public view { + console.logUint(testUint); + } + + function testLogString() public view { + console.logString(testStr); + } + + function testLogBool() public view { + console.logBool(testBool); + } + + function testLogAddress() public view { + console.logAddress(testAddr); + } + + function testLogBytes() public view { + console.logBytes(testBytes); + } + + function testLogBytes1() public view { + console.logBytes1(bytes1(testBytes)); + } + + function testLogBytes2() public view { + console.logBytes2(bytes2(testBytes)); + } + + function testLogBytes3() public view { + console.logBytes3(bytes3(testBytes)); + } + + function testLogBytes4() public view { + console.logBytes4(bytes4(testBytes)); + } + + function testLogBytes5() public view { + console.logBytes5(bytes5(testBytes)); + } + + function testLogBytes6() public view { + console.logBytes6(bytes6(testBytes)); + } + + function testLogBytes7() public view { + console.logBytes7(bytes7(testBytes)); + } + + function testLogBytes8() public view { + console.logBytes8(bytes8(testBytes)); + } + + function testLogBytes9() public view { + console.logBytes9(bytes9(testBytes)); + } + + function testLogBytes10() public view { + console.logBytes10(bytes10(testBytes)); + } + + function testLogBytes11() public view { + console.logBytes11(bytes11(testBytes)); + } + + function testLogBytes12() public view { + console.logBytes12(bytes12(testBytes)); + } + + function testLogBytes13() public view { + console.logBytes13(bytes13(testBytes)); + } + + function testLogBytes14() public view { + console.logBytes14(bytes14(testBytes)); + } + + function testLogBytes15() public view { + console.logBytes15(bytes15(testBytes)); + } + + function testLogBytes16() public view { + console.logBytes16(bytes16(testBytes)); + } + + function testLogBytes17() public view { + console.logBytes17(bytes17(testBytes)); + } + + function testLogBytes18() public view { + console.logBytes18(bytes18(testBytes)); + } + + function testLogBytes19() public view { + console.logBytes19(bytes19(testBytes)); + } + + function testLogBytes20() public view { + console.logBytes20(bytes20(testBytes)); + } + + function testLogBytes21() public view { + console.logBytes21(bytes21(testBytes)); + } + + function testLogBytes22() public view { + console.logBytes22(bytes22(testBytes)); + } + + function testLogBytes23() public view { + console.logBytes23(bytes23(testBytes)); + } + + function testLogBytes24() public view { + console.logBytes24(bytes24(testBytes)); + } + + function testLogBytes25() public view { + console.logBytes25(bytes25(testBytes)); + } + + function testLogBytes26() public view { + console.logBytes26(bytes26(testBytes)); + } + + function testLogBytes27() public view { + console.logBytes27(bytes27(testBytes)); + } + + function testLogBytes28() public view { + console.logBytes28(bytes28(testBytes)); + } + + function testLogBytes29() public view { + console.logBytes29(bytes29(testBytes)); + } + + function testLogBytes30() public view { + console.logBytes30(bytes30(testBytes)); + } + + function testLogBytes31() public view { + console.logBytes31(bytes31(testBytes)); + } + + function testLogBytes32() public view { + console.logBytes32(bytes32(testBytes)); + } + + function testConsoleLogUint() public view { + console.log(testUint); + } + + function testConsoleLogString() public view { + console.log(testStr); + } + + function testConsoleLogBool() public view { + console.log(testBool); + } + + function testConsoleLogAddress() public view { + console.log(testAddr); + } + + function testConsoleLogFormatString() public view { + console.log("formatted log str=%s", testStr); + } + + function testConsoleLogFormatUint() public view { + console.log("formatted log uint=%s", testUint); + } + + function testConsoleLogFormatAddress() public view { + console.log("formatted log addr=%s", testAddr); + } + + function testConsoleLogFormatMulti() public view { + console.log("formatted log str=%s uint=%d", testStr, testUint); + } + + function testConsoleLogFormatEscape() public view { + console.log("formatted log %% %s", testStr); + } + + function testConsoleLogFormatSpill() public view { + console.log("formatted log %s", testStr, testUint); + } +} +"#, + ); + + cmd.args(["test", "-vv"]).assert_success().stdout_eq(str![[r#" +... +Ran 52 tests for test/HardhatLogs.t.sol:HardhatLogsTest +[PASS] testConsoleLog() ([GAS]) +Logs: + constructor + test + +[PASS] testConsoleLogAddress() ([GAS]) +Logs: + constructor + 0x0000000000000000000000000000000000000001 + +[PASS] testConsoleLogBool() ([GAS]) +Logs: + constructor + false + +[PASS] testConsoleLogFormatAddress() ([GAS]) +Logs: + constructor + formatted log addr=0x0000000000000000000000000000000000000001 + +[PASS] testConsoleLogFormatEscape() ([GAS]) +Logs: + constructor + formatted log % test + +[PASS] testConsoleLogFormatMulti() ([GAS]) +Logs: + constructor + formatted log str=test uint=1 + +[PASS] testConsoleLogFormatSpill() ([GAS]) +Logs: + constructor + formatted log test 1 + +[PASS] testConsoleLogFormatString() ([GAS]) +Logs: + constructor + formatted log str=test + +[PASS] testConsoleLogFormatUint() ([GAS]) +Logs: + constructor + formatted log uint=1 + +[PASS] testConsoleLogString() ([GAS]) +Logs: + constructor + test + +[PASS] testConsoleLogUint() ([GAS]) +Logs: + constructor + 1 + +[PASS] testInts() ([GAS]) +Logs: + constructor + 0 + 1 + 2 + 3 + +[PASS] testLogAddress() ([GAS]) +Logs: + constructor + 0x0000000000000000000000000000000000000001 + +[PASS] testLogBool() ([GAS]) +Logs: + constructor + false + +[PASS] testLogBytes() ([GAS]) +Logs: + constructor + 0x61 + +[PASS] testLogBytes1() ([GAS]) +Logs: + constructor + 0x61 + +[PASS] testLogBytes10() ([GAS]) +Logs: + constructor + 0x61000000000000000000 + +[PASS] testLogBytes11() ([GAS]) +Logs: + constructor + 0x6100000000000000000000 + +[PASS] testLogBytes12() ([GAS]) +Logs: + constructor + 0x610000000000000000000000 + +[PASS] testLogBytes13() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000 + +[PASS] testLogBytes14() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000 + +[PASS] testLogBytes15() ([GAS]) +Logs: + constructor + 0x610000000000000000000000000000 + +[PASS] testLogBytes16() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000000000 + +[PASS] testLogBytes17() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000000000 + +[PASS] testLogBytes18() ([GAS]) +Logs: + constructor + 0x610000000000000000000000000000000000 + +[PASS] testLogBytes19() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000000000000000 + +[PASS] testLogBytes2() ([GAS]) +Logs: + constructor + 0x6100 + +[PASS] testLogBytes20() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000000000000000 + +[PASS] testLogBytes21() ([GAS]) +Logs: + constructor + 0x610000000000000000000000000000000000000000 + +[PASS] testLogBytes22() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000000000000000000000 + +[PASS] testLogBytes23() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000000000000000000000 + +[PASS] testLogBytes24() ([GAS]) +Logs: + constructor + 0x610000000000000000000000000000000000000000000000 + +[PASS] testLogBytes25() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes26() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes27() ([GAS]) +Logs: + constructor + 0x610000000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes28() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes29() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes3() ([GAS]) +Logs: + constructor + 0x610000 + +[PASS] testLogBytes30() ([GAS]) +Logs: + constructor + 0x610000000000000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes31() ([GAS]) +Logs: + constructor + 0x61000000000000000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes32() ([GAS]) +Logs: + constructor + 0x6100000000000000000000000000000000000000000000000000000000000000 + +[PASS] testLogBytes4() ([GAS]) +Logs: + constructor + 0x61000000 + +[PASS] testLogBytes5() ([GAS]) +Logs: + constructor + 0x6100000000 + +[PASS] testLogBytes6() ([GAS]) +Logs: + constructor + 0x610000000000 + +[PASS] testLogBytes7() ([GAS]) +Logs: + constructor + 0x61000000000000 + +[PASS] testLogBytes8() ([GAS]) +Logs: + constructor + 0x6100000000000000 + +[PASS] testLogBytes9() ([GAS]) +Logs: + constructor + 0x610000000000000000 + +[PASS] testLogInt() ([GAS]) +Logs: + constructor + -31337 + +[PASS] testLogString() ([GAS]) +Logs: + constructor + test + +[PASS] testLogUint() ([GAS]) +Logs: + constructor + 1 + +[PASS] testMisc() ([GAS]) +Logs: + constructor + testMisc 0x0000000000000000000000000000000000000001 + testMisc 42 + +[PASS] testStrings() ([GAS]) +Logs: + constructor + testStrings + +Suite result: ok. 52 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 52 tests passed, 0 failed, 0 skipped (52 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd/mod.rs similarity index 98% rename from crates/forge/tests/cli/test_cmd.rs rename to crates/forge/tests/cli/test_cmd/mod.rs index 11bbc782af2b6..d708cbf4123a7 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd/mod.rs @@ -3,11 +3,63 @@ use alloy_primitives::U256; use anvil::{NodeConfig, spawn}; use foundry_test_utils::{ - TestCommand, rpc, str, + TestCommand, + rpc::{self, rpc_endpoints}, + str, util::{OTHER_SOLC_VERSION, OutputExt, SOLC_VERSION}, }; use similar_asserts::assert_eq; -use std::{path::PathBuf, str::FromStr}; +use std::{io::Write, path::PathBuf, str::FromStr}; + +mod core; +mod fuzz; +mod invariant; +mod logs; +mod repros; +mod spec; +mod trace; + +// Run `forge test` on `/testdata`. +forgetest!(testdata, |_prj, cmd| { + let testdata = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../testdata").canonicalize().unwrap(); + cmd.current_dir(&testdata); + + let mut dotenv = std::fs::File::create(testdata.join(".env")).unwrap(); + for (name, endpoint) in rpc_endpoints().iter() { + if let Some(url) = endpoint.endpoint.as_url() { + let key = format!("RPC_{}", name.to_uppercase()); + // cmd.env(&key, url); + writeln!(dotenv, "{key}={url}").unwrap(); + } + } + drop(dotenv); + + let mut args = vec!["test"]; + if cfg!(feature = "isolate-by-default") { + args.push( + "--nmc=(LastCallGasDefaultTest|MockFunctionTest|WithSeed|StateDiff|GetStorageSlotsTest|RecordAccount)", + ); + } + + let orig_assert = cmd.args(args).assert(); + if orig_assert.get_output().status.success() { + return; + } + + // Retry failed tests. + cmd.args(["--rerun"]); + let n = 3; + for i in 1..=n { + test_debug!("retrying failed tests... ({i}/{n})"); + let assert = cmd.assert(); + if assert.get_output().status.success() { + return; + } + } + + orig_assert.success(); +}); // tests that test filters are handled correctly forgetest!(can_set_filter_values, |prj, cmd| { @@ -272,7 +324,7 @@ forgetest!(can_run_test_with_json_output_verbose, |prj, cmd| { // Assert that with verbose output the json output includes the traces cmd.args(["test", "-vvv", "--json"]) .assert_success() - .stdout_eq(file!["../fixtures/SimpleContractTestVerbose.json": Json]); + .stdout_eq(file!["../../fixtures/SimpleContractTestVerbose.json": Json]); }); forgetest!(can_run_test_with_json_output_non_verbose, |prj, cmd| { @@ -284,7 +336,7 @@ forgetest!(can_run_test_with_json_output_non_verbose, |prj, cmd| { // Assert that without verbose output the json output does not include the traces cmd.args(["test", "--json"]) .assert_success() - .stdout_eq(file!["../fixtures/SimpleContractTestNonVerbose.json": Json]); + .stdout_eq(file!["../../fixtures/SimpleContractTestNonVerbose.json": Json]); }); // tests that `forge test` will pick up tests that are stored in the `test = ` config value @@ -2931,7 +2983,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) forgetest_init!(colored_traces, |prj, cmd| { cmd.args(["test", "--mt", "test_Increment", "--color", "always", "-vvvvv"]) .assert_success() - .stdout_eq(file!["../fixtures/colored_traces.svg": TermSvg]); + .stdout_eq(file!["../../fixtures/colored_traces.svg": TermSvg]); }); // Tests that traces for successful tests can be suppressed by using `-s` flag. diff --git a/testdata/default/repros/Issue8383.t.sol b/crates/forge/tests/cli/test_cmd/repros.rs similarity index 57% rename from testdata/default/repros/Issue8383.t.sol rename to crates/forge/tests/cli/test_cmd/repros.rs index 3c40e44475fcc..bb663c051fa63 100644 --- a/testdata/default/repros/Issue8383.t.sol +++ b/crates/forge/tests/cli/test_cmd/repros.rs @@ -1,13 +1,438 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; +//! Regression tests for specific GitHub issues -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +use foundry_test_utils::str; + +// https://github.com/foundry-rs/foundry/issues/3055 +forgetest_init!(issue_3055, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue3055.t.sol", + r#" +import "forge-std/Test.sol"; + +/// forge-config: default.assertions_revert = false +contract Issue3055Test is Test { + function test_snapshot() external { + uint256 snapshotId = vm.snapshotState(); + assertEq(uint256(0), uint256(1)); + vm.revertToState(snapshotId); + } + + function test_snapshot2() public { + uint256 snapshotId = vm.snapshotState(); + assertTrue(false); + vm.revertToState(snapshotId); + assertTrue(true); + } + + function test_snapshot3(uint256) public { + vm.expectRevert(); + // Call exposed_snapshot3() using this to perform an external call, + // so we can properly test for reverts. + this.exposed_snapshot3(); + } + + function exposed_snapshot3() public { + uint256 snapshotId = vm.snapshotState(); + assertTrue(false); + vm.revertToState(snapshotId); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 3 tests for test/Issue3055.t.sol:Issue3055Test +[FAIL] test_snapshot() ([GAS]) +[FAIL] test_snapshot2() ([GAS]) +[FAIL: next call did not revert as expected; counterexample: calldata=[..] args=[..] test_snapshot3(uint256) (runs: 0, [AVG_GAS]) +Suite result: FAILED. 0 passed; 3 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 3 failed, 0 skipped (3 total tests) + +Failing tests: +Encountered 3 failing tests in test/Issue3055.t.sol:Issue3055Test +[FAIL] test_snapshot() ([GAS]) +[FAIL] test_snapshot2() ([GAS]) +[FAIL: next call did not revert as expected; counterexample: calldata=[..] args=[..] test_snapshot3(uint256) (runs: 0, [AVG_GAS]) + +Encountered a total of 3 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 3 failed tests + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/3189 +forgetest_init!(issue_3189, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue3189.t.sol", + r#" +import "forge-std/Test.sol"; + +contract MyContract { + function foo(uint256 arg) public returns (uint256) { + return arg + 2; + } +} + +contract MyContractUser is Test { + MyContract immutable myContract; + + constructor() { + myContract = new MyContract(); + } + + function foo(uint256 arg) public returns (uint256 ret) { + ret = myContract.foo(arg); + assertEq(ret, arg + 1, "Invariant failed"); + } +} + +contract Issue3189Test is Test { + function testFoo() public { + MyContractUser user = new MyContractUser(); + user.foo(123); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/Issue3189.t.sol:Issue3189Test +[FAIL: Invariant failed: 125 != 124] testFoo() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/Issue3189.t.sol:Issue3189Test +[FAIL: Invariant failed: 125 != 124] testFoo() ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/3596 +forgetest_init!(issue_3596, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue3596.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Issue3596Test is Test { + function testDealTransfer() public { + address addr = vm.addr(1337); + vm.startPrank(addr); + vm.deal(addr, 20000001 ether); + payable(address(this)).transfer(20000000 ether); + + Nested nested = new Nested(); + nested.doStuff(); + vm.stopPrank(); + } +} + +contract Nested { + function doStuff() public { + doRevert(); + } + + function doRevert() public { + revert("This fails"); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/Issue3596.t.sol:Issue3596Test +[FAIL: EvmError: Revert] testDealTransfer() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/Issue3596.t.sol:Issue3596Test +[FAIL: EvmError: Revert] testDealTransfer() ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/2851 +forgetest_init!(issue_2851, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue2851.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Backdoor { + uint256 public number = 1; + + function backdoor(uint256 newNumber) public payable { + uint256 x = newNumber - 1; + if (x == 6912213124124531) { + number = 0; + } + } +} + +contract Issue2851Test is Test { + Backdoor back; + + function setUp() public { + back = new Backdoor(); + } + + /// forge-config: default.fuzz.seed = "111" + function invariantNotZero() public { + assertEq(back.number(), 1); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 1 test for test/Issue2851.t.sol:Issue2851Test +[FAIL: assertion failed: 0 != 1] +... + invariantNotZero() ([..]) +... +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) +... +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/6170 +forgetest_init!(issue_6170, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue6170.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Emitter { + event Values(uint256 indexed a, uint256 indexed b); + + function plsEmit(uint256 a, uint256 b) external { + emit Values(a, b); + } +} + +contract Issue6170Test is Test { + event Values(uint256 indexed a, uint256 b); + + Emitter e = new Emitter(); + + function test() public { + vm.expectEmit(true, true, false, true); + emit Values(69, 420); + e.plsEmit(69, 420); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Issue6170.t.sol:Issue6170Test +[FAIL: log != expected log] test() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/Issue6170.t.sol:Issue6170Test +[FAIL: log != expected log] test() ([GAS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/6355 +forgetest_init!(issue_6355, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue6355.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Issue6355Test is Test { + uint256 snapshotId; + Target targ; + + function setUp() public { + snapshotId = vm.snapshotState(); + targ = new Target(); + } + + // this non-deterministically fails sometimes and passes sometimes + function test_shouldPass() public { + assertEq(2, targ.num()); + } + + // always fails + function test_shouldFailWithRevertToState() public { + assertEq(3, targ.num()); + vm.revertToState(snapshotId); + } + + // always fails + function test_shouldFail() public { + assertEq(3, targ.num()); + } +} + +contract Target { + function num() public pure returns (uint256) { + return 2; + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 3 tests for test/Issue6355.t.sol:Issue6355Test +[FAIL: assertion failed: 3 != 2] test_shouldFail() ([GAS]) +[FAIL: assertion failed: 3 != 2] test_shouldFailWithRevertToState() ([GAS]) +[PASS] test_shouldPass() ([GAS]) +Suite result: FAILED. 1 passed; 2 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 2 failed, 0 skipped (3 total tests) + +Failing tests: +Encountered 2 failing tests in test/Issue6355.t.sol:Issue6355Test +[FAIL: assertion failed: 3 != 2] test_shouldFail() ([GAS]) +[FAIL: assertion failed: 3 != 2] test_shouldFailWithRevertToState() ([GAS]) + +Encountered a total of 2 failing tests, 1 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 2 failed tests + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/3347 +forgetest_init!(issue_3347, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue3347.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Issue3347Test is Test { + event log2(uint256, uint256); + + function test() public { + emit log2(1, 2); + } +} +"#, + ); + + cmd.args(["test", "-vvvv"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/Issue3347.t.sol:Issue3347Test +[PASS] test() ([GAS]) +Traces: + [..] Issue3347Test::test() + ├─ emit log2(: 1, : 2) + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/6501 +// Make sure we decode Hardhat-style `console.log`s correctly, in both logs and traces. +forgetest_init!(issue_6501, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Issue6501.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Issue6501Test is Test { + function test_hhLogs() public { + console.log("a"); + console.log(uint256(1)); + console.log("b", uint256(2)); + } +} +"#, + ); + + cmd.args(["test", "-vvvv"]).assert_success().stdout_eq(str![[r#" +... +Ran 1 test for test/Issue6501.t.sol:Issue6501Test +[PASS] test_hhLogs() ([GAS]) +Logs: + a + 1 + b 2 + +Traces: + [..] Issue6501Test::test_hhLogs() + ├─ [0] console::log("a") [staticcall] + │ └─ ← [Stop] + ├─ [0] console::log(1) [staticcall] + │ └─ ← [Stop] + ├─ [0] console::log("b", 2) [staticcall] + │ └─ ← [Stop] + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); // https://github.com/foundry-rs/foundry/issues/8383 -contract Issue8383Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +forgetest_init!(issue_8383, |prj, cmd| { + prj.wipe_contracts(); + prj.update_config(|config| { + config.optimizer = Some(true); + config.optimizer_runs = Some(200); + }); + prj.add_test( + "Issue8383.t.sol", + r#" +import "forge-std/Test.sol"; +contract Issue8383Test is Test { address internal _verifier; mapping(bytes32 => bool) internal _vectorTested; @@ -326,3 +751,13 @@ contract P256Verifier { } } } +"#, + ); + + cmd.arg("test").with_no_redact().assert_success().stdout_eq(str![[r#" +... +Ran 1 test for test/Issue8383.t.sol:Issue8383Test +[PASS] testP256VerifyOutOfBounds() (gas: 3139) +... +"#]]); +}); diff --git a/crates/forge/tests/it/spec.rs b/crates/forge/tests/cli/test_cmd/spec.rs similarity index 93% rename from crates/forge/tests/it/spec.rs rename to crates/forge/tests/cli/test_cmd/spec.rs index bcd16ce08028a..d8eabfad548e6 100644 --- a/crates/forge/tests/it/spec.rs +++ b/crates/forge/tests/cli/test_cmd/spec.rs @@ -1,19 +1,10 @@ -//! Integration tests for EVM specifications. - -use crate::{config::*, test_helpers::TEST_DATA_PARIS}; -use foundry_test_utils::{Filter, forgetest_init, rpc, str}; -use revm::primitives::hardfork::SpecId; - -#[tokio::test(flavor = "multi_thread")] -async fn test_shanghai_compat() { - let filter = Filter::new("", "ShanghaiCompat", ".*spec"); - TestConfig::with_filter(TEST_DATA_PARIS.runner(), filter).spec_id(SpecId::SHANGHAI).run().await; -} +use foundry_test_utils::rpc; // Test evm version switch during tests / scripts. // // forgetest_init!(test_set_evm_version, |prj, cmd| { + prj.wipe_contracts(); let endpoint = rpc::next_http_archive_rpc_url(); prj.add_test( "TestEvmVersion.t.sol", diff --git a/crates/forge/tests/it/table.rs b/crates/forge/tests/cli/test_cmd/table.rs similarity index 100% rename from crates/forge/tests/it/table.rs rename to crates/forge/tests/cli/test_cmd/table.rs diff --git a/crates/forge/tests/cli/test_cmd/trace.rs b/crates/forge/tests/cli/test_cmd/trace.rs new file mode 100644 index 0000000000000..093afbbdb8fe5 --- /dev/null +++ b/crates/forge/tests/cli/test_cmd/trace.rs @@ -0,0 +1,397 @@ +//! Tests for tracing functionality + +use foundry_test_utils::str; + +forgetest_init!(conflicting_signatures, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "ConflictingSignatures.t.sol", + r#" +pragma solidity ^0.8.18; + +import "forge-std/Test.sol"; + +contract ReturnsNothing { + function func() public pure {} +} + +contract ReturnsString { + function func() public pure returns (string memory) { + return "string"; + } +} + +contract ReturnsUint { + function func() public pure returns (uint256) { + return 1; + } +} + +contract ConflictingSignaturesTest is Test { + ReturnsNothing retsNothing; + ReturnsString retsString; + ReturnsUint retsUint; + + function setUp() public { + retsNothing = new ReturnsNothing(); + retsString = new ReturnsString(); + retsUint = new ReturnsUint(); + } + + /// Tests that traces are decoded properly when multiple + /// functions have the same 4byte signature, but different + /// return values. + function testTraceWithConflictingSignatures() public { + retsNothing.func(); + retsString.func(); + retsUint.func(); + } +} +"#, + ); + + cmd.args(["test", "-vvvvv"]).assert_success().stdout_eq(str![[r#" +... +Ran 1 test for test/ConflictingSignatures.t.sol:ConflictingSignaturesTest +[PASS] testTraceWithConflictingSignatures() ([GAS]) +Traces: + [..] ConflictingSignaturesTest::setUp() + ├─ [..] → new ReturnsNothing@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] 106 bytes of code + ├─ [..] → new ReturnsString@0x2e234DAe75C793f67A35089C9d99245E1C58470b + │ └─ ← [Return] 334 bytes of code + ├─ [..] → new ReturnsUint@0xF62849F9A0B5Bf2913b396098F7c7019b51A820a + │ └─ ← [Return] 175 bytes of code + └─ ← [Stop] + + [..] ConflictingSignaturesTest::testTraceWithConflictingSignatures() + ├─ [..] ReturnsNothing::func() [staticcall] + │ └─ ← [Stop] + ├─ [..] ReturnsString::func() [staticcall] + │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006737472696e670000000000000000000000000000000000000000000000000000 + ├─ [..] ReturnsUint::func() [staticcall] + │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001 + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + +#[cfg(not(feature = "isolate-by-default"))] +forgetest_init!(trace_test, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "Trace.t.sol", + r#" +pragma solidity ^0.8.18; + +import "forge-std/Test.sol"; + +contract RecursiveCall { + TraceTest factory; + + event Depth(uint256 depth); + event ChildDepth(uint256 childDepth); + event CreatedChild(uint256 childDepth); + + constructor(address _factory) { + factory = TraceTest(_factory); + } + + function recurseCall(uint256 neededDepth, uint256 depth) public returns (uint256) { + if (depth == neededDepth) { + this.negativeNum(); + return neededDepth; + } + + uint256 childDepth = this.recurseCall(neededDepth, depth + 1); + emit ChildDepth(childDepth); + + this.someCall(); + emit Depth(depth); + + return depth; + } + + function recurseCreate(uint256 neededDepth, uint256 depth) public returns (uint256) { + if (depth == neededDepth) { + return neededDepth; + } + + RecursiveCall child = factory.create(); + emit CreatedChild(depth + 1); + + uint256 childDepth = child.recurseCreate(neededDepth, depth + 1); + emit ChildDepth(childDepth); + emit Depth(depth); + + return depth; + } + + function someCall() public pure {} + + function negativeNum() public pure returns (int256) { + return -1000000000; + } +} + +contract TraceTest is Test { + uint256 nodeId = 0; + RecursiveCall first; + + function setUp() public { + first = this.create(); + } + + function create() public returns (RecursiveCall) { + RecursiveCall node = new RecursiveCall(address(this)); + vm.label(address(node), string(abi.encodePacked("Node ", uintToString(nodeId++)))); + + return node; + } + + function testRecurseCall() public { + first.recurseCall(8, 0); + } + + function testRecurseCreate() public { + first.recurseCreate(8, 0); + } +} + +function uintToString(uint256 value) pure returns (string memory) { + // Taken from OpenZeppelin + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); +} +"#, + ); + + cmd.args(["test", "-vvvvv"]).assert_success().stdout_eq(str![[r#" +... +Ran 2 tests for test/Trace.t.sol:TraceTest +[PASS] testRecurseCall() ([GAS]) +Traces: + [..] TraceTest::setUp() + ├─ [..] TraceTest::create() + │ ├─ [..] → new Node 0@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ │ └─ ← [Return] 1911 bytes of code + │ ├─ [0] VM::label(Node 0: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], "Node 0") + │ │ └─ ← [Return] + │ └─ ← [Return] Node 0: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f] + └─ ← [Stop] + + [..] TraceTest::testRecurseCall() + ├─ [..] Node 0::recurseCall(8, 0) + │ ├─ [..] Node 0::recurseCall(8, 1) + │ │ ├─ [..] Node 0::recurseCall(8, 2) + │ │ │ ├─ [..] Node 0::recurseCall(8, 3) + │ │ │ │ ├─ [..] Node 0::recurseCall(8, 4) + │ │ │ │ │ ├─ [..] Node 0::recurseCall(8, 5) + │ │ │ │ │ │ ├─ [..] Node 0::recurseCall(8, 6) + │ │ │ │ │ │ │ ├─ [..] Node 0::recurseCall(8, 7) + │ │ │ │ │ │ │ │ ├─ [..] Node 0::recurseCall(8, 8) + │ │ │ │ │ │ │ │ │ ├─ [..] Node 0::negativeNum() [staticcall] + │ │ │ │ │ │ │ │ │ │ └─ ← [Return] -1000000000 [-1e9] + │ │ │ │ │ │ │ │ │ └─ ← [Return] 8 + │ │ │ │ │ │ │ │ ├─ emit ChildDepth(childDepth: 8) + │ │ │ │ │ │ │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ │ │ │ │ │ │ └─ ← [Stop] + │ │ │ │ │ │ │ │ ├─ emit Depth(depth: 7) + │ │ │ │ │ │ │ │ └─ ← [Return] 7 + │ │ │ │ │ │ │ ├─ emit ChildDepth(childDepth: 7) + │ │ │ │ │ │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ │ │ │ │ │ └─ ← [Stop] + │ │ │ │ │ │ │ ├─ emit Depth(depth: 6) + │ │ │ │ │ │ │ └─ ← [Return] 6 + │ │ │ │ │ │ ├─ emit ChildDepth(childDepth: 6) + │ │ │ │ │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ │ │ │ │ └─ ← [Stop] + │ │ │ │ │ │ ├─ emit Depth(depth: 5) + │ │ │ │ │ │ └─ ← [Return] 5 + │ │ │ │ │ ├─ emit ChildDepth(childDepth: 5) + │ │ │ │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ │ │ │ └─ ← [Stop] + │ │ │ │ │ ├─ emit Depth(depth: 4) + │ │ │ │ │ └─ ← [Return] 4 + │ │ │ │ ├─ emit ChildDepth(childDepth: 4) + │ │ │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ │ │ └─ ← [Stop] + │ │ │ │ ├─ emit Depth(depth: 3) + │ │ │ │ └─ ← [Return] 3 + │ │ │ ├─ emit ChildDepth(childDepth: 3) + │ │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ │ └─ ← [Stop] + │ │ │ ├─ emit Depth(depth: 2) + │ │ │ └─ ← [Return] 2 + │ │ ├─ emit ChildDepth(childDepth: 2) + │ │ ├─ [..] Node 0::someCall() [staticcall] + │ │ │ └─ ← [Stop] + │ │ ├─ emit Depth(depth: 1) + │ │ └─ ← [Return] 1 + │ ├─ emit ChildDepth(childDepth: 1) + │ ├─ [..] Node 0::someCall() [staticcall] + │ │ └─ ← [Stop] + │ ├─ emit Depth(depth: 0) + │ └─ ← [Return] 0 + └─ ← [Stop] + +[PASS] testRecurseCreate() ([GAS]) +Traces: + [..] TraceTest::setUp() + ├─ [..] TraceTest::create() + │ ├─ [..] → new Node 0@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ │ └─ ← [Return] 1911 bytes of code + │ ├─ [0] VM::label(Node 0: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], "Node 0") + │ │ └─ ← [Return] + │ └─ ← [Return] Node 0: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f] + └─ ← [Stop] + + [..] TraceTest::testRecurseCreate() + ├─ [..] Node 0::recurseCreate(8, 0) + │ ├─ [..] TraceTest::create() + │ │ ├─ [..] → new Node 1@0x2e234DAe75C793f67A35089C9d99245E1C58470b + │ │ │ ├─ storage changes: + │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ └─ ← [Return] 1911 bytes of code + │ │ ├─ [0] VM::label(Node 1: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], "Node 1") + │ │ │ └─ ← [Return] + │ │ ├─ storage changes: + │ │ │ @ 32: 1 → 2 + │ │ └─ ← [Return] Node 1: [0x2e234DAe75C793f67A35089C9d99245E1C58470b] + │ ├─ emit CreatedChild(childDepth: 1) + │ ├─ [..] Node 1::recurseCreate(8, 1) + │ │ ├─ [..] TraceTest::create() + │ │ │ ├─ [..] → new Node 2@0xF62849F9A0B5Bf2913b396098F7c7019b51A820a + │ │ │ │ ├─ storage changes: + │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ ├─ [0] VM::label(Node 2: [0xF62849F9A0B5Bf2913b396098F7c7019b51A820a], "Node 2") + │ │ │ │ └─ ← [Return] + │ │ │ ├─ storage changes: + │ │ │ │ @ 32: 2 → 3 + │ │ │ └─ ← [Return] Node 2: [0xF62849F9A0B5Bf2913b396098F7c7019b51A820a] + │ │ ├─ emit CreatedChild(childDepth: 2) + │ │ ├─ [..] Node 2::recurseCreate(8, 2) + │ │ │ ├─ [..] TraceTest::create() + │ │ │ │ ├─ [..] → new Node 3@0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9 + │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ │ ├─ [0] VM::label(Node 3: [0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9], "Node 3") + │ │ │ │ │ └─ ← [Return] + │ │ │ │ ├─ storage changes: + │ │ │ │ │ @ 32: 3 → 4 + │ │ │ │ └─ ← [Return] Node 3: [0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9] + │ │ │ ├─ emit CreatedChild(childDepth: 3) + │ │ │ ├─ [..] Node 3::recurseCreate(8, 3) + │ │ │ │ ├─ [..] TraceTest::create() + │ │ │ │ │ ├─ [..] → new Node 4@0xc7183455a4C133Ae270771860664b6B7ec320bB1 + │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ │ │ ├─ [0] VM::label(Node 4: [0xc7183455a4C133Ae270771860664b6B7ec320bB1], "Node 4") + │ │ │ │ │ │ └─ ← [Return] + │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ @ 32: 4 → 5 + │ │ │ │ │ └─ ← [Return] Node 4: [0xc7183455a4C133Ae270771860664b6B7ec320bB1] + │ │ │ │ ├─ emit CreatedChild(childDepth: 4) + │ │ │ │ ├─ [..] Node 4::recurseCreate(8, 4) + │ │ │ │ │ ├─ [..] TraceTest::create() + │ │ │ │ │ │ ├─ [..] → new Node 5@0xa0Cb889707d426A7A386870A03bc70d1b0697598 + │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ │ │ │ ├─ [0] VM::label(Node 5: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], "Node 5") + │ │ │ │ │ │ │ └─ ← [Return] + │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ @ 32: 5 → 6 + │ │ │ │ │ │ └─ ← [Return] Node 5: [0xa0Cb889707d426A7A386870A03bc70d1b0697598] + │ │ │ │ │ ├─ emit CreatedChild(childDepth: 5) + │ │ │ │ │ ├─ [..] Node 5::recurseCreate(8, 5) + │ │ │ │ │ │ ├─ [..] TraceTest::create() + │ │ │ │ │ │ │ ├─ [..] → new Node 6@0x1d1499e622D69689cdf9004d05Ec547d650Ff211 + │ │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ │ │ │ │ ├─ [0] VM::label(Node 6: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211], "Node 6") + │ │ │ │ │ │ │ │ └─ ← [Return] + │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ @ 32: 6 → 7 + │ │ │ │ │ │ │ └─ ← [Return] Node 6: [0x1d1499e622D69689cdf9004d05Ec547d650Ff211] + │ │ │ │ │ │ ├─ emit CreatedChild(childDepth: 6) + │ │ │ │ │ │ ├─ [..] Node 6::recurseCreate(8, 6) + │ │ │ │ │ │ │ ├─ [..] TraceTest::create() + │ │ │ │ │ │ │ │ ├─ [..] → new Node 7@0xA4AD4f68d0b91CFD19687c881e50f3A00242828c + │ │ │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ │ │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ │ │ │ │ │ ├─ [0] VM::label(Node 7: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c], "Node 7") + │ │ │ │ │ │ │ │ │ └─ ← [Return] + │ │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ │ @ 32: 7 → 8 + │ │ │ │ │ │ │ │ └─ ← [Return] Node 7: [0xA4AD4f68d0b91CFD19687c881e50f3A00242828c] + │ │ │ │ │ │ │ ├─ emit CreatedChild(childDepth: 7) + │ │ │ │ │ │ │ ├─ [..] Node 7::recurseCreate(8, 7) + │ │ │ │ │ │ │ │ ├─ [..] TraceTest::create() + │ │ │ │ │ │ │ │ │ ├─ [..] → new Node 8@0x03A6a84cD762D9707A21605b548aaaB891562aAb + │ │ │ │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ │ │ │ @ 0: 0 → 0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496 + │ │ │ │ │ │ │ │ │ │ └─ ← [Return] 1911 bytes of code + │ │ │ │ │ │ │ │ │ ├─ [0] VM::label(Node 8: [0x03A6a84cD762D9707A21605b548aaaB891562aAb], "Node 8") + │ │ │ │ │ │ │ │ │ │ └─ ← [Return] + │ │ │ │ │ │ │ │ │ ├─ storage changes: + │ │ │ │ │ │ │ │ │ │ @ 32: 8 → 9 + │ │ │ │ │ │ │ │ │ └─ ← [Return] Node 8: [0x03A6a84cD762D9707A21605b548aaaB891562aAb] + │ │ │ │ │ │ │ │ ├─ emit CreatedChild(childDepth: 8) + │ │ │ │ │ │ │ │ ├─ [..] Node 8::recurseCreate(8, 8) + │ │ │ │ │ │ │ │ │ └─ ← [Return] 8 + │ │ │ │ │ │ │ │ ├─ emit ChildDepth(childDepth: 8) + │ │ │ │ │ │ │ │ ├─ emit Depth(depth: 7) + │ │ │ │ │ │ │ │ └─ ← [Return] 7 + │ │ │ │ │ │ │ ├─ emit ChildDepth(childDepth: 7) + │ │ │ │ │ │ │ ├─ emit Depth(depth: 6) + │ │ │ │ │ │ │ └─ ← [Return] 6 + │ │ │ │ │ │ ├─ emit ChildDepth(childDepth: 6) + │ │ │ │ │ │ ├─ emit Depth(depth: 5) + │ │ │ │ │ │ └─ ← [Return] 5 + │ │ │ │ │ ├─ emit ChildDepth(childDepth: 5) + │ │ │ │ │ ├─ emit Depth(depth: 4) + │ │ │ │ │ └─ ← [Return] 4 + │ │ │ │ ├─ emit ChildDepth(childDepth: 4) + │ │ │ │ ├─ emit Depth(depth: 3) + │ │ │ │ └─ ← [Return] 3 + │ │ │ ├─ emit ChildDepth(childDepth: 3) + │ │ │ ├─ emit Depth(depth: 2) + │ │ │ └─ ← [Return] 2 + │ │ ├─ emit ChildDepth(childDepth: 2) + │ │ ├─ emit Depth(depth: 1) + │ │ └─ ← [Return] 1 + │ ├─ emit ChildDepth(childDepth: 1) + │ ├─ emit Depth(depth: 0) + │ └─ ← [Return] 0 + └─ ← [Stop] + +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs deleted file mode 100644 index 1271f534cdc5d..0000000000000 --- a/crates/forge/tests/it/cheats.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Forge tests for cheatcodes. -use crate::{ - config::*, - test_helpers::{ - ForgeTestData, ForgeTestProfile, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, - TEST_DATA_MULTI_VERSION, TEST_DATA_PARIS, - }, -}; -use alloy_primitives::U256; -use foundry_cli::utils::install_crypto_provider; -use foundry_compilers::artifacts::output_selection::ContractOutputSelection; -use foundry_config::{FsPermissions, fs_permissions::PathPermission}; -use foundry_test_utils::{Filter, init_tracing, util::get_compiled}; - -/// Executes all cheat code tests but not fork cheat codes or tests that require isolation mode or -/// specific seed. -async fn test_cheats_local(test_data: &ForgeTestData) { - let mut filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")) - .exclude_paths("Fork") - .exclude_contracts("(Isolated|WithSeed|StateDiff|GetStorageSlotsTest)"); - - // 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)"); - } - - if cfg!(feature = "isolate-by-default") { - filter = filter.exclude_contracts( - "(LastCallGasDefaultTest|MockFunctionTest|WithSeed|StateDiff|GetStorageSlotsTest|RecordAccount)", - ); - } - - let runner = test_data.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - }); - - 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}*")) - .exclude_contracts("(StateDiff|GetStorageSlotsTest)"); - - let runner = test_data.runner_with(|config| { - config.isolate = true; - }); - - TestConfig::with_filter(runner, filter).run().await; -} - -/// Executes subset of all cheat code tests using a specific seed. -async fn test_cheats_local_with_seed(test_data: &ForgeTestData) { - let filter = Filter::new(".*", ".*(WithSeed)", &format!(".*cheats{RE_PATH_SEPARATOR}*")); - - let runner = test_data.runner_with(|config| { - config.fuzz.seed = Some(U256::from(100)); - }); - - 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_state_diff_storage_layout() { - let test_data = { - let profile = ForgeTestProfile::Default; - install_crypto_provider(); - init_tracing(); - let mut config = profile.config(); - config.extra_output = vec![ContractOutputSelection::StorageLayout]; - let mut project = config.project().unwrap(); - // Compile with StorageLayout - let output = get_compiled(&mut project); - ForgeTestData { project, output, config: config.into(), profile } - }; - let filter = Filter::new( - ".*", - "(StateDiff|GetStorageSlotsTest)", - &format!(".*cheats{RE_PATH_SEPARATOR}*"), - ); - - let runner = test_data.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - }); - - TestConfig::with_filter(runner, filter).run().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_default_with_seed() { - test_cheats_local_with_seed(&TEST_DATA_DEFAULT).await -} - -#[tokio::test(flavor = "multi_thread")] -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_paris() { - test_cheats_local(&TEST_DATA_PARIS).await -} diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs deleted file mode 100644 index b028b45005b8d..0000000000000 --- a/crates/forge/tests/it/config.rs +++ /dev/null @@ -1,170 +0,0 @@ -//! Test config. - -use forge::{ - MultiContractRunner, - result::{SuiteResult, TestStatus}, -}; -use foundry_evm::{ - decode::decode_console_logs, - traces::{CallTraceDecoderBuilder, decode_trace_arena, render_trace_arena}, -}; -use foundry_test_utils::{Filter, init_tracing}; -use futures::future::join_all; -use itertools::Itertools; -use revm::primitives::hardfork::SpecId; -use std::collections::BTreeMap; - -/// How to execute a test run. -pub struct TestConfig { - pub runner: MultiContractRunner, - pub should_fail: bool, - pub filter: Filter, -} - -impl TestConfig { - pub fn new(runner: MultiContractRunner) -> Self { - Self::with_filter(runner, Filter::matches_all()) - } - - pub fn with_filter(runner: MultiContractRunner, filter: Filter) -> Self { - init_tracing(); - Self { runner, should_fail: false, filter } - } - - pub fn spec_id(mut self, spec: SpecId) -> Self { - self.runner.spec_id = spec; - self - } - - pub fn should_fail(self) -> Self { - self.set_should_fail(true) - } - - pub fn set_should_fail(mut self, should_fail: bool) -> Self { - self.should_fail = should_fail; - self - } - - /// Executes the test runner - pub fn test(&mut self) -> eyre::Result> { - self.runner.test_collect(&self.filter) - } - - pub async fn run(&mut self) { - self.try_run().await.unwrap() - } - - /// Executes the test case - /// - /// Returns an error if - /// * 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()?; - if suite_result.is_empty() { - eyre::bail!("empty test result"); - } - for (_, SuiteResult { test_results, .. }) in suite_result { - for (test_name, mut result) in test_results { - if self.should_fail && (result.status == TestStatus::Success) - || !self.should_fail && (result.status == TestStatus::Failure) - { - let logs = decode_console_logs(&result.logs); - let outcome = if self.should_fail { "fail" } else { "pass" }; - let call_trace_decoder = CallTraceDecoderBuilder::default() - .with_known_contracts(&self.runner.known_contracts) - .build(); - let decoded_traces = join_all(result.traces.iter_mut().map(|(_, arena)| { - let decoder = &call_trace_decoder; - async move { - decode_trace_arena(arena, decoder).await; - render_trace_arena(arena) - } - })) - .await - .into_iter() - .collect::>(); - eyre::bail!( - "Test {} did not {} as expected.\nReason: {:?}\nLogs:\n{}\n\nTraces:\n{}", - test_name, - outcome, - result.reason, - logs.join("\n"), - decoded_traces.into_iter().format("\n"), - ) - } - } - } - - Ok(()) - } -} - -/// A helper to assert the outcome of multiple tests with helpful assert messages -#[track_caller] -#[expect(clippy::type_complexity)] -pub fn assert_multiple( - actuals: &BTreeMap, - expecteds: BTreeMap< - &str, - Vec<(&str, bool, Option, Option>, Option)>, - >, -) { - assert_eq!(actuals.len(), expecteds.len(), "We did not run as many contracts as we expected"); - for (contract_name, tests) in &expecteds { - assert!( - actuals.contains_key(*contract_name), - "We did not run the contract {contract_name}" - ); - - assert_eq!( - actuals[*contract_name].len(), - expecteds[contract_name].len(), - "We did not run as many test functions as we expected for {contract_name}" - ); - for (test_name, should_pass, reason, expected_logs, expected_warning_count) in tests { - let logs = &decode_console_logs(&actuals[*contract_name].test_results[*test_name].logs); - - let warnings_count = &actuals[*contract_name].warnings.len(); - - if *should_pass { - assert!( - actuals[*contract_name].test_results[*test_name].status == TestStatus::Success, - "Test {} did not pass as expected.\nReason: {:?}\nLogs:\n{}", - test_name, - actuals[*contract_name].test_results[*test_name].reason, - logs.join("\n") - ); - } else { - assert!( - actuals[*contract_name].test_results[*test_name].status == TestStatus::Failure, - "Test {} did not fail as expected.\nLogs:\n{}", - test_name, - logs.join("\n") - ); - assert_eq!( - actuals[*contract_name].test_results[*test_name].reason, *reason, - "Failure reason for test {test_name} did not match what we expected." - ); - } - - if let Some(expected_logs) = expected_logs { - assert_eq!( - logs, - expected_logs, - "Logs did not match for test {}.\nExpected:\n{}\n\nGot:\n{}", - test_name, - expected_logs.join("\n"), - logs.join("\n") - ); - } - - if let Some(expected_warning_count) = expected_warning_count { - assert_eq!( - warnings_count, expected_warning_count, - "Test {test_name} did not pass as expected. Expected:\n{expected_warning_count}Got:\n{warnings_count}" - ); - } - } - } -} diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs deleted file mode 100644 index 161f4db363c20..0000000000000 --- a/crates/forge/tests/it/core.rs +++ /dev/null @@ -1,829 +0,0 @@ -//! Forge tests for core functionality. - -use crate::{ - config::*, - test_helpers::{TEST_DATA_DEFAULT, TEST_DATA_PARIS}, -}; -use forge::result::SuiteResult; -use foundry_evm::traces::TraceKind; -use foundry_test_utils::Filter; -use std::{collections::BTreeMap, env}; - -#[tokio::test(flavor = "multi_thread")] -async fn test_core() { - let filter = Filter::new(".*", ".*", ".*core"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let results = runner.test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([ - ( - "default/core/Reverting.t.sol:RevertingTest", - vec![("testRevert()", true, None, None, None)], - ), - ( - "default/core/SetupConsistency.t.sol:SetupConsistencyCheck", - vec![ - ("testAdd()", true, None, None, None), - ("testMultiply()", true, None, None, None), - ], - ), - ( - "default/core/ContractEnvironment.t.sol:ContractEnvironmentTest", - vec![ - ("testAddresses()", true, None, None, None), - ("testEnvironment()", true, None, None, None), - ], - ), - ( - "default/core/PaymentFailure.t.sol:PaymentFailureTest", - vec![("testCantPay()", false, Some("EvmError: Revert".to_string()), None, None)], - ), - ( - "default/core/Abstract.t.sol:AbstractTest", - vec![("testSomething()", true, None, None, None)], - ), - ( - "default/core/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest", - vec![("setUp()", false, Some("execution error".to_string()), None, None)], - ), - ( - "default/core/BadSigAfterInvariant.t.sol:BadSigAfterInvariant", - vec![("testShouldPassWithWarning()", true, None, None, None)], - ), - ( - "default/core/LegacyAssertions.t.sol:NoAssertionsRevertTest", - vec![( - "testMultipleAssertFailures()", - false, - Some("assertion failed: 1 != 2".to_string()), - None, - None, - )], - ), - ( - "default/core/LegacyAssertions.t.sol:LegacyAssertionsTest", - vec![ - ("testFlagNotSetSuccess()", true, None, None, None), - ("testFlagSetFailure()", true, None, None, None), - ], - ), - ]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_linking() { - let filter = Filter::new(".*", ".*", ".*linking"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let results = runner.test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([ - ( - "default/linking/simple/Simple.t.sol:SimpleLibraryLinkingTest", - vec![("testCall()", true, None, None, None)], - ), - ( - "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest", - vec![ - ("testDirect()", true, None, None, None), - ("testNested()", true, None, None, None), - ], - ), - ( - "default/linking/duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest", - vec![ - ("testA()", true, None, None, None), - ("testB()", true, None, None, None), - ("testC()", true, None, None, None), - ("testD()", true, None, None, None), - ("testE()", true, None, None, None), - ], - ), - ]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_logs() { - let filter = Filter::new(".*", ".*", ".*logs"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let results = runner.test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([ - ( - "default/logs/DebugLogs.t.sol:DebugLogsTest", - vec![ - ("test1()", true, None, Some(vec!["0".into(), "1".into(), "2".into()]), None), - ("test2()", true, None, Some(vec!["0".into(), "1".into(), "3".into()]), None), - ( - "testRevertIfWithRequire()", - true, - None, - Some(vec!["0".into(), "1".into(), "5".into()]), - None, - ), - ( - "testRevertIfWithRevert()", - true, - None, - Some(vec!["0".into(), "1".into(), "4".into(), "100".into()]), - None, - ), - ( - "testLog()", - true, - None, - Some(vec!["0".into(), "1".into(), "Error: Assertion Failed".into()]), - None, - ), - ( - "testLogs()", - true, - None, - Some(vec!["0".into(), "1".into(), "0x61626364".into()]), - None, - ), - ( - "testLogAddress()", - true, - None, - Some(vec![ - "0".into(), - "1".into(), - "0x0000000000000000000000000000000000000001".into(), - ]), - None, - ), - ( - "testLogBytes32()", - true, - None, - Some(vec![ - "0".into(), - "1".into(), - "0x6162636400000000000000000000000000000000000000000000000000000000" - .into(), - ]), - None, - ), - ( - "testLogInt()", - true, - None, - Some(vec!["0".into(), "1".into(), "-31337".into()]), - None, - ), - ( - "testLogBytes()", - true, - None, - Some(vec!["0".into(), "1".into(), "0x61626364".into()]), - None, - ), - ( - "testLogString()", - true, - None, - Some(vec!["0".into(), "1".into(), "here".into()]), - None, - ), - ( - "testLogNamedAddress()", - true, - None, - Some(vec![ - "0".into(), - "1".into(), - "address: 0x0000000000000000000000000000000000000001".into(), - ]), - None, - ), - ( - "testLogNamedBytes32()", - true, - None, - Some(vec![ - "0".into(), - "1".into(), - "abcd: 0x6162636400000000000000000000000000000000000000000000000000000000" - .into(), - ]), - None, - ), - ( - "testLogNamedDecimalInt()", - true, - None, - Some(vec!["0".into(), "1".into(), "amount: -0.000000000000031337".into()]), - None, - ), - ( - "testLogNamedDecimalUint()", - true, - None, - Some(vec!["0".into(), "1".into(), "amount: 1.000000000000000000".into()]), - None, - ), - ( - "testLogNamedInt()", - true, - None, - Some(vec!["0".into(), "1".into(), "amount: -31337".into()]), - None, - ), - ( - "testLogNamedUint()", - true, - None, - Some(vec!["0".into(), "1".into(), "amount: 1000000000000000000".into()]), - None, - ), - ( - "testLogNamedBytes()", - true, - None, - Some(vec!["0".into(), "1".into(), "abcd: 0x61626364".into()]), - None, - ), - ( - "testLogNamedString()", - true, - None, - Some(vec!["0".into(), "1".into(), "key: val".into()]), - None, - ), - ], - ), - ( - "default/logs/HardhatLogs.t.sol:HardhatLogsTest", - vec![ - ( - "testInts()", - true, - None, - Some(vec![ - "constructor".into(), - "0".into(), - "1".into(), - "2".into(), - "3".into(), - ]), - None, - ), - ( - "testMisc()", - true, - None, - Some(vec![ - "constructor".into(), - "testMisc 0x0000000000000000000000000000000000000001".into(), - "testMisc 42".into(), - ]), - None, - ), - ( - "testStrings()", - true, - None, - Some(vec!["constructor".into(), "testStrings".into()]), - None, - ), - ( - "testConsoleLog()", - true, - None, - Some(vec!["constructor".into(), "test".into()]), - None, - ), - ( - "testLogInt()", - true, - None, - Some(vec!["constructor".into(), "-31337".into()]), - None, - ), - ( - "testLogUint()", - true, - None, - Some(vec!["constructor".into(), "1".into()]), - None, - ), - ( - "testLogString()", - true, - None, - Some(vec!["constructor".into(), "test".into()]), - None, - ), - ( - "testLogBool()", - true, - None, - Some(vec!["constructor".into(), "false".into()]), - None, - ), - ( - "testLogAddress()", - true, - None, - Some(vec![ - "constructor".into(), - "0x0000000000000000000000000000000000000001".into(), - ]), - None, - ), - ( - "testLogBytes()", - true, - None, - Some(vec!["constructor".into(), "0x61".into()]), - None, - ), - ( - "testLogBytes1()", - true, - None, - Some(vec!["constructor".into(), "0x61".into()]), - None, - ), - ( - "testLogBytes2()", - true, - None, - Some(vec!["constructor".into(), "0x6100".into()]), - None, - ), - ( - "testLogBytes3()", - true, - None, - Some(vec!["constructor".into(), "0x610000".into()]), - None, - ), - ( - "testLogBytes4()", - true, - None, - Some(vec!["constructor".into(), "0x61000000".into()]), - None, - ), - ( - "testLogBytes5()", - true, - None, - Some(vec!["constructor".into(), "0x6100000000".into()]), - None, - ), - ( - "testLogBytes6()", - true, - None, - Some(vec!["constructor".into(), "0x610000000000".into()]), - None, - ), - ( - "testLogBytes7()", - true, - None, - Some(vec!["constructor".into(), "0x61000000000000".into()]), - None, - ), - ( - "testLogBytes8()", - true, - None, - Some(vec!["constructor".into(), "0x6100000000000000".into()]), - None, - ), - ( - "testLogBytes9()", - true, - None, - Some(vec!["constructor".into(), "0x610000000000000000".into()]), - None, - ), - ( - "testLogBytes10()", - true, - None, - Some(vec!["constructor".into(), "0x61000000000000000000".into()]), - None, - ), - ( - "testLogBytes11()", - true, - None, - Some(vec!["constructor".into(), "0x6100000000000000000000".into()]), - None, - ), - ( - "testLogBytes12()", - true, - None, - Some(vec!["constructor".into(), "0x610000000000000000000000".into()]), - None, - ), - ( - "testLogBytes13()", - true, - None, - Some(vec!["constructor".into(), "0x61000000000000000000000000".into()]), - None, - ), - ( - "testLogBytes14()", - true, - None, - Some(vec!["constructor".into(), "0x6100000000000000000000000000".into()]), - None, - ), - ( - "testLogBytes15()", - true, - None, - Some(vec!["constructor".into(), "0x610000000000000000000000000000".into()]), - None, - ), - ( - "testLogBytes16()", - true, - None, - Some(vec![ - "constructor".into(), - "0x61000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes17()", - true, - None, - Some(vec![ - "constructor".into(), - "0x6100000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes18()", - true, - None, - Some(vec![ - "constructor".into(), - "0x610000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes19()", - true, - None, - Some(vec![ - "constructor".into(), - "0x61000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes20()", - true, - None, - Some(vec![ - "constructor".into(), - "0x6100000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes21()", - true, - None, - Some(vec![ - "constructor".into(), - "0x610000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes22()", - true, - None, - Some(vec![ - "constructor".into(), - "0x61000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes23()", - true, - None, - Some(vec![ - "constructor".into(), - "0x6100000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes24()", - true, - None, - Some(vec![ - "constructor".into(), - "0x610000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes25()", - true, - None, - Some(vec![ - "constructor".into(), - "0x61000000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes26()", - true, - None, - Some(vec![ - "constructor".into(), - "0x6100000000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes27()", - true, - None, - Some(vec![ - "constructor".into(), - "0x610000000000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes28()", - true, - None, - Some(vec![ - "constructor".into(), - "0x61000000000000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes29()", - true, - None, - Some(vec![ - "constructor".into(), - "0x6100000000000000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes30()", - true, - None, - Some(vec![ - "constructor".into(), - "0x610000000000000000000000000000000000000000000000000000000000".into(), - ]), - None, - ), - ( - "testLogBytes31()", - true, - None, - Some(vec![ - "constructor".into(), - "0x61000000000000000000000000000000000000000000000000000000000000" - .into(), - ]), - None, - ), - ( - "testLogBytes32()", - true, - None, - Some(vec![ - "constructor".into(), - "0x6100000000000000000000000000000000000000000000000000000000000000" - .into(), - ]), - None, - ), - ( - "testConsoleLogUint()", - true, - None, - Some(vec!["constructor".into(), "1".into()]), - None, - ), - ( - "testConsoleLogString()", - true, - None, - Some(vec!["constructor".into(), "test".into()]), - None, - ), - ( - "testConsoleLogBool()", - true, - None, - Some(vec!["constructor".into(), "false".into()]), - None, - ), - ( - "testConsoleLogAddress()", - true, - None, - Some(vec![ - "constructor".into(), - "0x0000000000000000000000000000000000000001".into(), - ]), - None, - ), - ( - "testConsoleLogFormatString()", - true, - None, - Some(vec!["constructor".into(), "formatted log str=test".into()]), - None, - ), - ( - "testConsoleLogFormatUint()", - true, - None, - Some(vec!["constructor".into(), "formatted log uint=1".into()]), - None, - ), - ( - "testConsoleLogFormatAddress()", - true, - None, - Some(vec![ - "constructor".into(), - "formatted log addr=0x0000000000000000000000000000000000000001".into(), - ]), - None, - ), - ( - "testConsoleLogFormatMulti()", - true, - None, - Some(vec!["constructor".into(), "formatted log str=test uint=1".into()]), - None, - ), - ( - "testConsoleLogFormatEscape()", - true, - None, - Some(vec!["constructor".into(), "formatted log % test".into()]), - None, - ), - ( - "testConsoleLogFormatSpill()", - true, - None, - Some(vec!["constructor".into(), "formatted log test 1".into()]), - None, - ), - ], - ), - ]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_env_vars() { - let env_var_key = "_foundryCheatcodeSetEnvTestKey"; - let env_var_val = "_foundryCheatcodeSetEnvTestVal"; - unsafe { - env::remove_var(env_var_key); - } - - let filter = Filter::new("testSetEnv", ".*", ".*"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let _ = runner.test_collect(&filter); - - 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 = TEST_DATA_DEFAULT.runner(); - let results = runner.test_collect(&filter).unwrap(); - 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 = TEST_DATA_DEFAULT.tracing_runner(); - let suite_result = runner.test_collect(&filter).unwrap(); - - // TODO: This trace test is very basic - it is probably a good candidate for snapshot - // testing. - for (_, SuiteResult { test_results, .. }) in suite_result { - for (test_name, result) in test_results { - let deployment_traces = - 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::Execution); - - assert_eq!( - deployment_traces.count(), - 14, - "Test {test_name} did not have exactly 14 deployment traces." - ); - assert!(setup_traces.count() <= 1, "Test {test_name} had more than 1 setup trace."); - assert_eq!( - execution_traces.count(), - 1, - "Test {test_name} did not have exactly 1 execution trace." - ); - } - } -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_assertions_revert_false() { - let filter = Filter::new(".*", ".*NoAssertionsRevertTest", ".*"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.assertions_revert = false; - }); - let results = runner.test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([( - "default/core/LegacyAssertions.t.sol:NoAssertionsRevertTest", - vec![( - "testMultipleAssertFailures()", - false, - None, - Some(vec![ - "assertion failed: 1 != 2".to_string(), - "assertion failed: 5 >= 4".to_string(), - ]), - None, - )], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_legacy_assertions() { - let filter = Filter::new(".*", ".*LegacyAssertions", ".*"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.legacy_assertions = true; - }); - let results = runner.test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([( - "default/core/LegacyAssertions.t.sol:LegacyAssertionsTest", - vec![ - ("testFlagNotSetSuccess()", true, None, None, None), - ("testFlagSetFailure()", false, None, None, None), - ], - )]), - ); -} - -/// Test `beforeTest` functionality and `selfdestruct`. -/// See -#[tokio::test(flavor = "multi_thread")] -async fn test_before_setup_with_selfdestruct() { - let filter = Filter::new(".*", ".*BeforeTestSelfDestructTest", ".*"); - let results = TEST_DATA_PARIS.runner().test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([( - "paris/core/BeforeTest.t.sol:BeforeTestSelfDestructTest", - vec![ - ("testKill()", true, None, None, None), - ("testA()", true, None, None, None), - ("testSimpleA()", true, None, None, None), - ("testB()", true, None, None, None), - ("testC(uint256)", true, None, None, None), - ], - )]), - ); -} diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs deleted file mode 100644 index 92038ead6df9f..0000000000000 --- a/crates/forge/tests/it/fork.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Forge forking tests. - -use crate::{ - config::*, - test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_PARIS}, -}; -use alloy_chains::Chain; -use forge::result::SuiteResult; -use foundry_config::{Config, FsPermissions, fs_permissions::PathPermission}; -use foundry_test_utils::Filter; -use std::fs; - -/// 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 = TEST_DATA_DEFAULT.runner(); - let suite_result = runner.test_collect(&filter).unwrap(); - assert_eq!(suite_result.len(), 1); - - for (_, SuiteResult { test_results, .. }) in suite_result { - for (_, result) in test_results { - assert_eq!( - result.reason.unwrap(), - "Contract 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f does not exist on active fork with id `1`\n But exists on non active forks: `[0]`" - ); - } - } -} - -/// Executes all non-reverting fork cheatcodes -#[tokio::test(flavor = "multi_thread")] -async fn test_cheats_fork() { - let runner = TEST_DATA_PARIS.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - }); - let filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) - .exclude_tests(".*Revert"); - TestConfig::with_filter(runner, filter).run().await; -} - -/// Executes eth_getLogs cheatcode -#[tokio::test(flavor = "multi_thread")] -async fn test_get_logs_fork() { - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - }); - let filter = Filter::new("testEthGetLogs", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) - .exclude_tests(".*Revert"); - TestConfig::with_filter(runner, filter).run().await; -} - -/// Executes rpc cheatcode -#[tokio::test(flavor = "multi_thread")] -async fn test_rpc_fork() { - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - }); - let filter = Filter::new("testRpc", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) - .exclude_tests(".*Revert"); - TestConfig::with_filter(runner, filter).run().await; -} - -/// Tests that we can launch in forking mode -#[tokio::test(flavor = "multi_thread")] -async fn test_launch_fork() { - let rpc_url = foundry_test_utils::rpc::next_http_archive_rpc_url(); - 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; -} - -/// Smoke test that forking workings with websockets -#[tokio::test(flavor = "multi_thread")] -async fn test_launch_fork_ws() { - let rpc_url = foundry_test_utils::rpc::next_ws_archive_rpc_url(); - 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; -} - -/// Tests that we can transact transactions in forking mode -#[tokio::test(flavor = "multi_thread")] -async fn test_transact_fork() { - let runner = TEST_DATA_PARIS.runner(); - let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Transact")); - TestConfig::with_filter(runner, filter).run().await; -} - -/// 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(); - 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() { - let filter = - Filter::new("testStorageCaching", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) - .exclude_tests(".*Revert"); - - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.no_storage_caching = true; - }); - - // no_storage_caching set to true: storage should not be cached - TestConfig::with_filter(runner, filter.clone()).run().await; - let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap(); - let _ = fs::remove_file(cache_dir); - - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.no_storage_caching = false; - }); - TestConfig::with_filter(runner, filter).run().await; - - // no_storage_caching set to false: storage should be cached - let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap(); - assert!(cache_dir.exists()); - - // cleanup cached storage so subsequent tests does not fail - let _ = fs::remove_file(cache_dir); -} diff --git a/crates/forge/tests/it/fs.rs b/crates/forge/tests/it/fs.rs deleted file mode 100644 index 3e9fb1f4dad82..0000000000000 --- a/crates/forge/tests/it/fs.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Filesystem tests. - -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; -use foundry_config::{FsPermissions, fs_permissions::PathPermission}; -use foundry_test_utils::Filter; - -#[tokio::test(flavor = "multi_thread")] -async fn test_fs_disabled() { - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::none("./")]); - }); - let filter = Filter::new(".*", ".*", ".*fs/Disabled"); - TestConfig::with_filter(runner, filter).run().await; -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_fs_default() { - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - }); - let filter = Filter::new(".*", ".*", ".*fs/Default"); - TestConfig::with_filter(runner, filter).run().await; -} diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs deleted file mode 100644 index 616b19a17f805..0000000000000 --- a/crates/forge/tests/it/fuzz.rs +++ /dev/null @@ -1,496 +0,0 @@ -//! Fuzz tests. - -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; -use alloy_primitives::{Bytes, U256}; -use forge::{ - decode::decode_console_logs, - fuzz::CounterExample, - result::{SuiteResult, TestStatus}, -}; -use foundry_test_utils::{Filter, forgetest_init, str}; -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\)|testStorageOwner\(address\)|testImmutableOwner\(address\)") - .exclude_paths("invariant"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let suite_result = runner.test_collect(&filter).unwrap(); - - assert!(!suite_result.is_empty()); - - for (_, SuiteResult { test_results, .. }) in suite_result { - for (test_name, result) in test_results { - match test_name.as_str() { - "testPositive(uint256)" - | "testPositive(int256)" - | "testSuccessfulFuzz(uint128,uint128)" - | "testToStringFuzz(bytes32)" => assert_eq!( - result.status, - TestStatus::Success, - "Test {} did not pass as expected.\nReason: {:?}\nLogs:\n{}", - test_name, - result.reason, - decode_console_logs(&result.logs).join("\n") - ), - _ => assert_eq!( - result.status, - TestStatus::Failure, - "Test {} did not fail as expected.\nReason: {:?}\nLogs:\n{}", - test_name, - result.reason, - decode_console_logs(&result.logs).join("\n") - ), - } - } - } -} - -#[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 = TEST_DATA_DEFAULT.runner(); - let suite_result = runner.test_collect(&filter).unwrap(); - - assert!(!suite_result.is_empty()); - - for (_, SuiteResult { test_results, .. }) in suite_result { - for (test_name, result) in test_results { - match test_name.as_str() { - "testSuccessChecker(uint256)" - | "testSuccessChecker2(int256)" - | "testSuccessChecker3(uint32)" => assert_eq!( - result.status, - TestStatus::Success, - "Test {} did not pass as expected.\nReason: {:?}\nLogs:\n{}", - test_name, - result.reason, - decode_console_logs(&result.logs).join("\n") - ), - _ => {} - } - } - } -} - -/// Test that showcases PUSH collection on normal fuzzing. Ignored until we collect them in a -/// smarter way. -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_fuzz_collection() { - let filter = Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.invariant.depth = 100; - config.invariant.runs = 1000; - config.fuzz.runs = 1000; - config.fuzz.seed = Some(U256::from(6u32)); - }); - let results = runner.test_collect(&filter).unwrap(); - - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/FuzzCollection.t.sol:SampleContractTest", - vec![ - ("invariantCounter", false, Some("broken counter.".into()), None, None), - ( - "testIncrement(address)", - false, - Some("Call did not revert as expected".into()), - None, - None, - ), - ("testNeedle(uint256)", false, Some("needle found.".into()), None, None), - ], - )]), - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_persist_fuzz_failure() { - let filter = Filter::new(".*", ".*", ".*fuzz/FuzzFailurePersist.t.sol"); - - macro_rules! run_fail { - () => { run_fail!(|config| {}) }; - (|$config:ident| $e:expr) => {{ - let mut runner = TEST_DATA_DEFAULT.runner_with(|$config| { - $config.fuzz.runs = 1000; - $e - }); - runner - .test_collect(&filter) - .unwrap() - .get("default/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 initial_counterexample = run_fail!(); - let initial_calldata = match initial_counterexample { - Some(CounterExample::Single(counterexample)) => counterexample.calldata, - _ => Bytes::new(), - }; - - // run several times and compare counterexamples calldata - for i in 0..10 { - let new_calldata = match run_fail!() { - Some(CounterExample::Single(counterexample)) => counterexample.calldata, - _ => Bytes::new(), - }; - // calldata should be the same with the initial one - assert_eq!(initial_calldata, new_calldata, "run {i}"); - } - - // write new failure in different dir. - let persist_dir = tempfile::tempdir().unwrap().keep(); - let new_calldata = match run_fail!(|config| config.fuzz.failure_persist_dir = Some(persist_dir)) - { - 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); -} - -forgetest_init!(test_can_scrape_bytecode, |prj, cmd| { - prj.update_config(|config| config.optimizer = Some(true)); - prj.add_source( - "FuzzerDict.sol", - r#" -// 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; - } -} - "#, - ); - - prj.add_test( - "FuzzerDictTest.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; -import "src/FuzzerDict.sol"; - -contract FuzzerDictTest is Test { - FuzzerDict fuzzerDict; - - function setUp() public { - fuzzerDict = new FuzzerDict(address(100), address(200)); - } - - /// forge-config: default.fuzz.runs = 2000 - function testImmutableOwner(address who) public { - assertTrue(who != fuzzerDict.immutableOwner()); - } - - /// forge-config: default.fuzz.runs = 2000 - function testStorageOwner(address who) public { - assertTrue(who != fuzzerDict.storageOwner()); - } -} - "#, - ); - - // Test that immutable address is used as fuzzed input, causing test to fail. - cmd.args(["test", "--fuzz-seed", "119", "--mt", "testImmutableOwner"]).assert_failure(); - // Test that storage address is used as fuzzed input, causing test to fail. - cmd.forge_fuse() - .args(["test", "--fuzz-seed", "119", "--mt", "testStorageOwner"]) - .assert_failure(); -}); - -// tests that inline max-test-rejects config is properly applied -forgetest_init!(test_inline_max_test_rejects, |prj, cmd| { - prj.wipe_contracts(); - - prj.add_test( - "Contract.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract InlineMaxRejectsTest is Test { - /// forge-config: default.fuzz.max-test-rejects = 1 - function test_fuzz_bound(uint256 a) public { - vm.assume(false); - } -} - "#, - ); - - cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" -... -[FAIL: `vm.assume` rejected too many inputs (1 allowed)] test_fuzz_bound(uint256) (runs: 0, [AVG_GAS]) -... -"#]]); -}); - -// Tests that test timeout config is properly applied. -// If test doesn't timeout after one second, then test will fail with `rejected too many inputs`. -forgetest_init!(test_fuzz_timeout, |prj, cmd| { - prj.wipe_contracts(); - - prj.add_test( - "Contract.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract FuzzTimeoutTest is Test { - /// forge-config: default.fuzz.max-test-rejects = 50000 - /// forge-config: default.fuzz.timeout = 1 - function test_fuzz_bound(uint256 a) public pure { - vm.assume(a == 0); - } -} - "#, - ); - - cmd.args(["test"]).assert_success().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 1 test for test/Contract.t.sol:FuzzTimeoutTest -[PASS] test_fuzz_bound(uint256) (runs: [..], [AVG_GAS]) -Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] - -Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) - -"#]]); -}); - -forgetest_init!(test_fuzz_fail_on_revert, |prj, cmd| { - prj.wipe_contracts(); - prj.update_config(|config| config.fuzz.fail_on_revert = false); - prj.add_source( - "Counter.sol", - r#" -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - require(number > 10000000000, "low number"); - number = newNumber; - } -} - "#, - ); - - prj.add_test( - "CounterTest.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; -import "src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - } - - function testFuzz_SetNumberRequire(uint256 x) public { - counter.setNumber(x); - require(counter.number() == 1); - } - - function testFuzz_SetNumberAssert(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), 1); - } -} - "#, - ); - - // Tests should not fail as revert happens in Counter contract. - cmd.args(["test", "--mc", "CounterTest"]).assert_success().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 2 tests for test/CounterTest.t.sol:CounterTest -[PASS] testFuzz_SetNumberAssert(uint256) (runs: 256, [AVG_GAS]) -[PASS] testFuzz_SetNumberRequire(uint256) (runs: 256, [AVG_GAS]) -Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] - -Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) - -"#]]); - - // Tested contract does not revert. - prj.add_source( - "Counter.sol", - r#" -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } -} - "#, - ); - - // Tests should fail as revert happens in cheatcode (assert) and test (require) contract. - cmd.assert_failure().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 2 tests for test/CounterTest.t.sol:CounterTest -[FAIL: assertion failed: [..]] testFuzz_SetNumberAssert(uint256) (runs: 0, [AVG_GAS]) -[FAIL: EvmError: Revert; [..]] testFuzz_SetNumberRequire(uint256) (runs: 0, [AVG_GAS]) -Suite result: FAILED. 0 passed; 2 failed; 0 skipped; [ELAPSED] -... - -"#]]); -}); - -// Test 256 runs regardless number of test rejects. -// -forgetest_init!(test_fuzz_runs_with_rejects, |prj, cmd| { - prj.add_test( - "FuzzWithRejectsTest.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract FuzzWithRejectsTest is Test { - function testFuzzWithRejects(uint256 x) public pure { - vm.assume(x < 1_000_000); - } -} - "#, - ); - - // Tests should not fail as revert happens in Counter contract. - cmd.args(["test", "--mc", "FuzzWithRejectsTest"]).assert_success().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 1 test for test/FuzzWithRejectsTest.t.sol:FuzzWithRejectsTest -[PASS] testFuzzWithRejects(uint256) (runs: 256, [AVG_GAS]) -Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] - -Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) - -"#]]); -}); - -// Test that counterexample is not replayed if test changes. -// -forgetest_init!(test_fuzz_replay_with_changed_test, |prj, cmd| { - prj.update_config(|config| config.fuzz.seed = Some(U256::from(100u32))); - prj.add_test( - "Counter.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract CounterTest is Test { - function testFuzz_SetNumber(uint256 x) public pure { - require(x > 200); - } -} - "#, - ); - // Tests should fail and record counterexample with value 2. - cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" -... -Failing tests: -Encountered 1 failing test in test/Counter.t.sol:CounterTest -[FAIL: EvmError: Revert; counterexample: calldata=0x5c7f60d70000000000000000000000000000000000000000000000000000000000000002 args=[2]] testFuzz_SetNumber(uint256) (runs: 19, [AVG_GAS]) -... - -"#]]); - - // Change test to assume counterexample 2 is discarded. - prj.add_test( - "Counter.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract CounterTest is Test { - function testFuzz_SetNumber(uint256 x) public pure { - vm.assume(x != 2); - } -} - "#, - ); - // Test should pass when replay failure with changed assume logic. - cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 1 test for test/Counter.t.sol:CounterTest -[PASS] testFuzz_SetNumber(uint256) (runs: 256, [AVG_GAS]) -Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] - -Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) - -"#]]); - - // Change test signature. - prj.add_test( - "Counter.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract CounterTest is Test { - function testFuzz_SetNumber(uint8 x) public pure { - } -} - "#, - ); - // Test should pass when replay failure with changed function signature. - cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 1 test for test/Counter.t.sol:CounterTest -[PASS] testFuzz_SetNumber(uint8) (runs: 256, [AVG_GAS]) -Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] - -Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) - -"#]]); - - // Change test back to the original one that produced the counterexample. - prj.add_test( - "Counter.t.sol", - r#" -import {Test} from "forge-std/Test.sol"; - -contract CounterTest is Test { - function testFuzz_SetNumber(uint256 x) public pure { - require(x > 200); - } -} - "#, - ); - // Test should fail with replayed counterexample 2 (0 runs). - cmd.forge_fuse().args(["test"]).assert_failure().stdout_eq(str![[r#" -... -Failing tests: -Encountered 1 failing test in test/Counter.t.sol:CounterTest -[FAIL: EvmError: Revert; counterexample: calldata=0x5c7f60d70000000000000000000000000000000000000000000000000000000000000002 args=[2]] testFuzz_SetNumber(uint256) (runs: 0, [AVG_GAS]) -... - -"#]]); -}); diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs deleted file mode 100644 index 475570a102d67..0000000000000 --- a/crates/forge/tests/it/inline.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Inline configuration tests. - -use crate::test_helpers::TEST_DATA_DEFAULT; -use forge::result::TestKind; -use foundry_test_utils::Filter; - -#[tokio::test(flavor = "multi_thread")] -async fn inline_config_run_fuzz() { - let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let result = runner.test_collect(&filter).unwrap(); - let results = result - .into_iter() - .flat_map(|(path, r)| { - r.test_results.into_iter().map(move |(name, t)| { - let runs = match t.kind { - TestKind::Fuzz { runs, .. } => runs, - _ => unreachable!(), - }; - (path.clone(), name, runs) - }) - }) - .collect::>(); - - assert_eq!( - results, - vec![ - ( - "default/inline/FuzzInlineConf.t.sol:FuzzInlineConf".to_string(), - "testInlineConfFuzz(uint8)".to_string(), - 1024 - ), - ( - "default/inline/FuzzInlineConf.t.sol:FuzzInlineConf2".to_string(), - "testInlineConfFuzz1(uint8)".to_string(), - 1 - ), - ( - "default/inline/FuzzInlineConf.t.sol:FuzzInlineConf2".to_string(), - "testInlineConfFuzz2(uint8)".to_string(), - 10 - ), - ] - ); -} - -#[tokio::test(flavor = "multi_thread")] -async fn inline_config_run_invariant() { - const ROOT: &str = "default/inline/InvariantInlineConf.t.sol"; - - let filter = Filter::new(".*", ".*", ".*inline/InvariantInlineConf.t.sol"); - let mut runner = TEST_DATA_DEFAULT.runner(); - let result = runner.test_collect(&filter).unwrap(); - - let suite_result_1 = result.get(&format!("{ROOT}:InvariantInlineConf")).expect("Result exists"); - let suite_result_2 = - result.get(&format!("{ROOT}:InvariantInlineConf2")).expect("Result exists"); - - let test_result_1 = suite_result_1.test_results.get("invariant_neverFalse()").unwrap(); - match test_result_1.kind { - TestKind::Invariant { runs, .. } => assert_eq!(runs, 333), - _ => 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!(), - } -} diff --git a/crates/forge/tests/it/main.rs b/crates/forge/tests/it/main.rs deleted file mode 100644 index c8890af3d4614..0000000000000 --- a/crates/forge/tests/it/main.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub mod config; -pub mod test_helpers; - -mod cheats; -mod core; -mod fork; -mod fs; -mod fuzz; -mod inline; -mod invariant; -mod repros; -mod spec; -mod table; -mod vyper; diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs deleted file mode 100644 index e8b1228ad2a5d..0000000000000 --- a/crates/forge/tests/it/repros.rs +++ /dev/null @@ -1,411 +0,0 @@ -//! Regression tests for previous issues. - -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; -use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt}; -use alloy_json_abi::Event; -use alloy_primitives::{Address, U256, address, b256}; -use forge::{ - decode::decode_console_logs, - result::{TestKind, TestStatus}, -}; -use foundry_config::{Config, FsPermissions, fs_permissions::PathPermission}; -use foundry_evm::{ - constants::HARDHAT_CONSOLE_ADDRESS, - traces::{CallKind, CallTraceDecoder, DecodedCallData, TraceKind}, -}; -use foundry_test_utils::Filter; -use std::sync::Arc; - -/// Creates a test that runs `testdata/repros/Issue{issue}.t.sol`. -macro_rules! test_repro { - ($(#[$attr:meta])* $issue_number:literal $(,)?) => { - test_repro!($(#[$attr])* $issue_number, false, None); - }; - ($(#[$attr:meta])* $issue_number:literal, $should_fail:expr $(,)?) => { - test_repro!($(#[$attr])* $issue_number, $should_fail, None); - }; - ($(#[$attr:meta])* $issue_number:literal, $should_fail:expr, $sender:expr $(,)?) => { - paste::paste! { - #[tokio::test(flavor = "multi_thread")] - $(#[$attr])* - async fn [< issue_ $issue_number >]() { - repro_config($issue_number, $should_fail, $sender.into()).await.run().await; - } - } - }; - ($(#[$attr:meta])* $issue_number:literal, $should_fail:expr, $sender:expr, |$res:ident| $e:expr $(,)?) => { - paste::paste! { - #[tokio::test(flavor = "multi_thread")] - $(#[$attr])* - async fn [< issue_ $issue_number >]() { - let mut $res = repro_config($issue_number, $should_fail, $sender.into()).await.test().unwrap(); - $e - } - } - }; - ($(#[$attr:meta])* $issue_number:literal; |$config:ident| $e:expr $(,)?) => { - paste::paste! { - #[tokio::test(flavor = "multi_thread")] - $(#[$attr])* - async fn [< issue_ $issue_number >]() { - let mut $config = repro_config($issue_number, false, None).await; - $e - $config.run().await; - } - } - }; -} - -async fn repro_config(issue: usize, should_fail: bool, sender: Option
) -> TestConfig { - foundry_test_utils::init_tracing(); - let filter = Filter::path(&format!(".*repros/Issue{issue}.t.sol")); - - let runner = TEST_DATA_DEFAULT.runner_with(|config| { - config.fs_permissions = FsPermissions::new(vec![ - PathPermission::read("./fixtures"), - PathPermission::read("out"), - ]); - if let Some(sender) = sender { - config.sender = sender; - } - }); - TestConfig::with_filter(runner, filter).set_should_fail(should_fail) -} - -// https://github.com/foundry-rs/foundry/issues/2623 -test_repro!(2623); - -// https://github.com/foundry-rs/foundry/issues/2629 -test_repro!(2629); - -// https://github.com/foundry-rs/foundry/issues/2723 -test_repro!(2723); - -// https://github.com/foundry-rs/foundry/issues/2898 -test_repro!(2898); - -// https://github.com/foundry-rs/foundry/issues/2956 -test_repro!(2956); - -// https://github.com/foundry-rs/foundry/issues/2984 -test_repro!(2984); - -// https://github.com/foundry-rs/foundry/issues/3055 -test_repro!(3055, true); - -// https://github.com/foundry-rs/foundry/issues/3077 -test_repro!(3077); - -// https://github.com/foundry-rs/foundry/issues/3110 -test_repro!(3110); - -// https://github.com/foundry-rs/foundry/issues/3119 -test_repro!(3119); - -// https://github.com/foundry-rs/foundry/issues/3189 -test_repro!(3189, true); - -// https://github.com/foundry-rs/foundry/issues/3190 -test_repro!(3190); - -// https://github.com/foundry-rs/foundry/issues/3192 -test_repro!(3192); - -// https://github.com/foundry-rs/foundry/issues/3220 -test_repro!(3220); - -// https://github.com/foundry-rs/foundry/issues/3221 -test_repro!(3221); - -// https://github.com/foundry-rs/foundry/issues/3223 -test_repro!(3223, false, address!("0xF0959944122fb1ed4CfaBA645eA06EED30427BAA")); - -// https://github.com/foundry-rs/foundry/issues/3347 -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::parse("event log2(uint256, uint256)").unwrap(); - let decoded = event.decode_log(&test.logs[0].data).unwrap(); - assert_eq!( - decoded, - DecodedEvent { - selector: Some(b256!( - "0x78b9a1f3b55d6797ab2c4537e83ee04ff0c65a1ca1bb39d79a62e0a78d5a8a57" - )), - indexed: vec![], - body: vec![ - DynSolValue::Uint(U256::from(1), 256), - DynSolValue::Uint(U256::from(2), 256) - ] - } - ); -}); - -// https://github.com/foundry-rs/foundry/issues/3596 -test_repro!(3596, true, None); - -// https://github.com/foundry-rs/foundry/issues/3653 -test_repro!(3653); - -// https://github.com/foundry-rs/foundry/issues/3661 -test_repro!(3661); - -// https://github.com/foundry-rs/foundry/issues/3674 -test_repro!(3674, false, address!("0xF0959944122fb1ed4CfaBA645eA06EED30427BAA")); - -// https://github.com/foundry-rs/foundry/issues/3685 -test_repro!(3685); - -// https://github.com/foundry-rs/foundry/issues/3703 -test_repro!( - #[ignore = "flaky polygon RPCs"] - 3703 -); - -// https://github.com/foundry-rs/foundry/issues/3708 -test_repro!(3708); - -// https://github.com/foundry-rs/foundry/issues/3753 -test_repro!(3753); - -// https://github.com/foundry-rs/foundry/issues/3792 -test_repro!(3792); - -// https://github.com/foundry-rs/foundry/issues/4232 -test_repro!(4232); - -// https://github.com/foundry-rs/foundry/issues/4402 -test_repro!(4402); - -// https://github.com/foundry-rs/foundry/issues/4586 -test_repro!(4586); - -// https://github.com/foundry-rs/foundry/issues/4630 -test_repro!(4630); - -// https://github.com/foundry-rs/foundry/issues/4640 -test_repro!(4640); - -// https://github.com/foundry-rs/foundry/issues/5038 -test_repro!(5038); - -// https://github.com/foundry-rs/foundry/issues/5808 -test_repro!(5808); - -// -test_repro!(5929); - -// -test_repro!(5935); - -// -test_repro!(5948); - -// https://github.com/foundry-rs/foundry/issues/6006 -test_repro!(6006); - -// https://github.com/foundry-rs/foundry/issues/6032 -test_repro!(6032); - -// https://github.com/foundry-rs/foundry/issues/6070 -test_repro!(6070); - -// https://github.com/foundry-rs/foundry/issues/6115 -test_repro!(6115); - -// https://github.com/foundry-rs/foundry/issues/6170 -test_repro!(6170, false, None, |res| { - 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())); -}); - -// -test_repro!(6293); - -// https://github.com/foundry-rs/foundry/issues/6180 -test_repro!(6180); - -// https://github.com/foundry-rs/foundry/issues/6355 -test_repro!(6355, false, None, |res| { - 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); - - let test = res.test_results.remove("test_shouldFailWithRevertToState()").unwrap(); - assert_eq!(test.status, TestStatus::Failure); -}); - -// https://github.com/foundry-rs/foundry/issues/6437 -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("default/repros/Issue6501.t.sol:Issue6501Test").unwrap(); - let test = res.test_results.remove("test_hhLogs()").unwrap(); - assert_eq!(test.status, TestStatus::Success); - assert_eq!( - decode_console_logs(&test.logs), - ["a".to_string(), "1".to_string(), "b 2".to_string()] - ); - - let (kind, traces) = test.traces.last().unwrap().clone(); - let nodes = traces.arena.into_nodes(); - assert_eq!(kind, TraceKind::Execution); - - let test_call = nodes.first().unwrap(); - assert_eq!(test_call.idx, 0); - assert_eq!(test_call.children, [1, 2, 3]); - assert_eq!(test_call.trace.depth, 0); - assert!(!test_call.trace.is_error()); - - let expected = [ - ("log(string)", vec!["\"a\""]), - ("log(uint256)", vec!["1"]), - ("log(string,uint256)", vec!["\"b\"", "2"]), - ]; - for (node, expected) in nodes[1..=3].iter().zip(expected) { - let trace = &node.trace; - let decoded = CallTraceDecoder::new().decode_function(trace).await; - assert_eq!(trace.kind, CallKind::StaticCall); - assert_eq!(trace.address, HARDHAT_CONSOLE_ADDRESS); - assert_eq!(decoded.label, Some("console".into())); - assert_eq!(trace.depth, 1); - assert!(!trace.is_error()); - assert_eq!( - decoded.call_data, - Some(DecodedCallData { - signature: expected.0.into(), - args: expected.1.into_iter().map(ToOwned::to_owned).collect(), - }) - ); - } -}); - -// https://github.com/foundry-rs/foundry/issues/6538 -test_repro!(6538); - -// https://github.com/foundry-rs/foundry/issues/6554 -test_repro!(6554; |config| { - let path = config.runner.config.root.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 -test_repro!(6759); - -// https://github.com/foundry-rs/foundry/issues/6966 -test_repro!(6966); - -// https://github.com/foundry-rs/foundry/issues/6616 -test_repro!(6616); - -// https://github.com/foundry-rs/foundry/issues/5529 -test_repro!(5529; |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 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/7457 -test_repro!(7457); - -// https://github.com/foundry-rs/foundry/issues/7481 -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); - -// https://github.com/foundry-rs/foundry/issues/2851 -test_repro!(2851, false, None, |res| { - let mut suite_result = res.remove("default/repros/Issue2851.t.sol:Issue2851Test").unwrap(); - let tr = suite_result.test_results.remove("invariantNotZero()").unwrap(); - assert_eq!(tr.status, TestStatus::Failure, "{tr}"); -}); - -// https://github.com/foundry-rs/foundry/issues/8006 -test_repro!(8006); - -// https://github.com/foundry-rs/foundry/issues/8277 -test_repro!(8277); - -// https://github.com/foundry-rs/foundry/issues/8287 -test_repro!(8287); - -// https://github.com/foundry-rs/foundry/issues/8168 -test_repro!(8168); - -// https://github.com/foundry-rs/foundry/issues/8383 -test_repro!(8383, false, None, |res| { - let mut res = res.remove("default/repros/Issue8383.t.sol:Issue8383Test").unwrap(); - let test = res.test_results.remove("testP256VerifyOutOfBounds()").unwrap(); - assert_eq!(test.status, TestStatus::Success); - match test.kind { - TestKind::Unit { gas } => assert_eq!(gas, 3101), - _ => panic!("not a unit test kind"), - } -}); - -// https://github.com/foundry-rs/foundry/issues/6643 -test_repro!(6643); - -// https://github.com/foundry-rs/foundry/issues/8971 -test_repro!(8971; |config| { - let mut prj_config = Config::clone(&config.runner.config); - prj_config.isolate = true; - config.runner.config = Arc::new(prj_config); -}); - -// https://github.com/foundry-rs/foundry/issues/8639 -test_repro!(8639); - -// https://github.com/foundry-rs/foundry/issues/8566 -test_repro!(8566); - -// https://github.com/foundry-rs/foundry/issues/9643 -test_repro!(9643); - -// https://github.com/foundry-rs/foundry/issues/7238 -test_repro!(7238); - -// https://github.com/foundry-rs/foundry/issues/10302 -test_repro!(10302); - -// https://github.com/foundry-rs/foundry/issues/10477 -test_repro!(10477); - -// https://github.com/foundry-rs/foundry/issues/10527 -test_repro!(10527); - -// https://github.com/foundry-rs/foundry/issues/10552 -test_repro!(10552); - -// https://github.com/foundry-rs/foundry/issues/10586 -test_repro!(10586); - -// https://github.com/foundry-rs/foundry/issues/10957 -test_repro!(10957); - -// https://github.com/foundry-rs/foundry/issues/11353 -test_repro!(11353); - -// https://github.com/foundry-rs/foundry/issues/11616 -test_repro!(11616); diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs deleted file mode 100644 index 280e6f483abb5..0000000000000 --- a/crates/forge/tests/it/test_helpers.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! Test helpers for Forge integration tests. - -use alloy_chains::NamedChain; -use alloy_primitives::U256; -use forge::{MultiContractRunner, MultiContractRunnerBuilder}; -use foundry_cli::utils::install_crypto_provider; -use foundry_compilers::{ - Project, ProjectCompileOutput, SolcConfig, - artifacts::{EvmVersion, Settings}, - compilers::multi::MultiCompiler, -}; -use foundry_config::{ - Config, FsPermissions, FuzzConfig, FuzzCorpusConfig, FuzzDictionaryConfig, InvariantConfig, - RpcEndpointUrl, RpcEndpoints, fs_permissions::PathPermission, -}; -use foundry_evm::{constants::CALLER, opts::EvmOpts}; -use foundry_test_utils::{ - init_tracing, - rpc::{next_http_archive_rpc_url, next_rpc_endpoint}, - util::get_compiled, -}; -use revm::primitives::hardfork::SpecId; -use std::{ - env, fmt, - path::{Path, PathBuf}, - sync::{Arc, LazyLock}, -}; - -pub const RE_PATH_SEPARATOR: &str = "/"; -const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); - -/// Profile for the tests group. Used to configure separate configurations for test runs. -pub enum ForgeTestProfile { - Default, - Paris, - MultiVersion, -} - -impl fmt::Display for ForgeTestProfile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Default => write!(f, "default"), - Self::Paris => write!(f, "paris"), - Self::MultiVersion => write!(f, "multi-version"), - } - } -} - -impl ForgeTestProfile { - /// Returns true if the profile is Paris. - pub fn is_paris(&self) -> bool { - matches!(self, Self::Paris) - } - - pub fn root(&self) -> PathBuf { - PathBuf::from(TESTDATA) - } - - /// Configures the solc settings for the test profile. - pub fn solc_config(&self) -> SolcConfig { - let mut settings = Settings::default(); - - if matches!(self, Self::Paris) { - settings.evm_version = Some(EvmVersion::Paris); - } - - let settings = SolcConfig::builder().settings(settings).build(); - SolcConfig { settings } - } - - /// 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.prompt_timeout = 0; - - config.optimizer = Some(true); - config.optimizer_runs = Some(200); - - config.gas_limit = u64::MAX.into(); - config.chain = None; - config.tx_origin = CALLER; - config.block_number = U256::from(1); - config.block_timestamp = U256::from(1); - - config.sender = CALLER; - config.initial_balance = U256::MAX; - config.ffi = true; - config.verbosity = 3; - config.memory_limit = 1 << 26; - - if self.is_paris() { - config.evm_version = EvmVersion::Paris; - } - - config.fuzz = FuzzConfig { - runs: 256, - fail_on_revert: true, - max_test_rejects: 65536, - seed: None, - dictionary: FuzzDictionaryConfig { - include_storage: true, - include_push_bytes: true, - dictionary_weight: 40, - max_fuzz_dictionary_addresses: 10_000, - max_fuzz_dictionary_values: 10_000, - }, - gas_report_samples: 256, - corpus: FuzzCorpusConfig::default(), - failure_persist_dir: Some(tempfile::tempdir().unwrap().keep()), - show_logs: false, - timeout: None, - }; - config.invariant = InvariantConfig { - runs: 256, - depth: 15, - fail_on_revert: false, - call_override: false, - dictionary: FuzzDictionaryConfig { - dictionary_weight: 80, - include_storage: true, - include_push_bytes: true, - max_fuzz_dictionary_addresses: 10_000, - max_fuzz_dictionary_values: 10_000, - }, - shrink_run_limit: 5000, - max_assume_rejects: 65536, - gas_report_samples: 256, - corpus: FuzzCorpusConfig::default(), - failure_persist_dir: Some( - tempfile::Builder::new() - .prefix(&format!("foundry-{self}")) - .tempdir() - .unwrap() - .keep(), - ), - show_metrics: true, - timeout: None, - show_solidity: false, - }; - - config.sanitized() - } -} - -/// Container for test data for a specific test profile. -pub struct ForgeTestData { - pub project: Project, - pub output: ProjectCompileOutput, - pub config: Arc, - pub profile: ForgeTestProfile, -} - -impl ForgeTestData { - /// Builds [ForgeTestData] for the given [ForgeTestProfile]. - /// - /// Uses [get_compiled] to lazily compile the project. - pub fn new(profile: ForgeTestProfile) -> Self { - install_crypto_provider(); - init_tracing(); - let config = Arc::new(profile.config()); - let mut project = config.project().unwrap(); - let output = get_compiled(&mut project); - Self { project, output, config, profile } - } - - /// Builds a base runner - pub fn base_runner(&self) -> MultiContractRunnerBuilder { - init_tracing(); - let config = self.config.clone(); - let mut runner = MultiContractRunnerBuilder::new(config).sender(self.config.sender); - if self.profile.is_paris() { - runner = runner.evm_spec(SpecId::MERGE); - } - runner - } - - /// Builds a non-tracing runner - pub fn runner(&self) -> MultiContractRunner { - self.runner_with(|_| {}) - } - - /// Builds a non-tracing runner - pub fn runner_with(&self, modify: impl FnOnce(&mut Config)) -> MultiContractRunner { - let mut config = (*self.config).clone(); - modify(&mut config); - self.runner_with_config(config) - } - - fn runner_with_config(&self, mut config: Config) -> MultiContractRunner { - config.rpc_endpoints = rpc_endpoints(); - config.allow_paths.push(manifest_root().to_path_buf()); - - if config.fs_permissions.is_empty() { - config.fs_permissions = - FsPermissions::new(vec![PathPermission::read_write(manifest_root())]); - } - - let opts = config_evm_opts(&config); - - let mut builder = self.base_runner(); - let config = Arc::new(config); - builder.config = config.clone(); - builder - .enable_isolation(opts.isolate) - .sender(config.sender) - .build::(&self.output, opts.local_evm_env(), opts) - .unwrap() - } - - /// Builds a tracing runner - pub fn tracing_runner(&self) -> MultiContractRunner { - let mut opts = config_evm_opts(&self.config); - opts.verbosity = 5; - self.base_runner().build::(&self.output, 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 = config_evm_opts(&self.config); - - opts.env.chain_id = None; // clear chain id so the correct one gets fetched from the RPC - opts.fork_url = Some(rpc.to_string()); - - 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.output, env, opts).unwrap() - } -} - -/// Default data for the tests group. -pub static TEST_DATA_DEFAULT: LazyLock = - LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default)); - -/// Data for tests requiring Paris support on Solc and EVM level. -pub static TEST_DATA_PARIS: LazyLock = - LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Paris)); - -/// Data for tests requiring no specific version on Solc and EVM level. -pub static TEST_DATA_MULTI_VERSION: LazyLock = - LazyLock::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 - // `testdata` - if root.ends_with("forge") { - root = root.parent().unwrap(); - } - root -} - -/// the RPC endpoints used during tests -pub fn rpc_endpoints() -> RpcEndpoints { - RpcEndpoints::new([ - ("mainnet", RpcEndpointUrl::Url(next_http_archive_rpc_url())), - ("mainnet2", RpcEndpointUrl::Url(next_http_archive_rpc_url())), - ("sepolia", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Sepolia))), - ("optimism", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Optimism))), - ("arbitrum", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Arbitrum))), - ("polygon", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Polygon))), - ("bsc", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::BinanceSmartChain))), - ("avaxTestnet", RpcEndpointUrl::Url("https://api.avax-test.network/ext/bc/C/rpc".into())), - ("moonbeam", RpcEndpointUrl::Url("https://moonbeam-rpc.publicnode.com".into())), - ("rpcEnvAlias", RpcEndpointUrl::Env("${RPC_ENV_ALIAS}".into())), - ]) -} - -fn config_evm_opts(config: &Config) -> EvmOpts { - config.to_figment(foundry_config::FigmentProviders::None).extract().unwrap() -} diff --git a/crates/forge/tests/it/vyper.rs b/crates/forge/tests/it/vyper.rs deleted file mode 100644 index c40b87541bfb9..0000000000000 --- a/crates/forge/tests/it/vyper.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Integration tests for EVM specifications. - -use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; -use foundry_test_utils::Filter; - -#[tokio::test(flavor = "multi_thread")] -async fn test_basic_vyper_test() { - let filter = Filter::new("", "CounterTest", ".*vyper"); - TestConfig::with_filter(TEST_DATA_DEFAULT.runner(), filter).run().await; -} diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 43acb20cd24bf..33fad7418ac2f 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -409,9 +409,8 @@ mod tests { .to_string_lossy(); let identifier = format!("{source}:{}", id.name); - // Skip ds-test as it always has no dependencies etc. (and the path is outside root - // so is not sanitized) - if identifier.contains("DSTest") { + // Skip test utils as they always have no dependencies. + if identifier.contains("utils/") { return None; } @@ -701,7 +700,7 @@ mod tests { "default/linking/nested/Nested.t.sol:NestedLib", &[( "default/linking/nested/Nested.t.sol:Lib", - address!("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74"), + address!("0x773253227cce756e50c3993ec6366b3ec27786f9"), )], ) .assert_dependencies( @@ -711,12 +710,12 @@ mod tests { // have the same address and nonce. ( "default/linking/nested/Nested.t.sol:Lib", - Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74") + Address::from_str("0x773253227cce756e50c3993ec6366b3ec27786f9") .unwrap(), ), ( "default/linking/nested/Nested.t.sol:NestedLib", - Address::from_str("0xfebE2F30641170642f317Ff6F644Cee60E7Ac369") + Address::from_str("0xac231df03403867b05d092c26fc91b6b83f4bebe") .unwrap(), ), ], @@ -726,12 +725,12 @@ mod tests { &[ ( "default/linking/nested/Nested.t.sol:Lib", - Address::from_str("0xddb1Cd2497000DAeA687CEa3dc34Af44084BEa74") + Address::from_str("0x773253227cce756e50c3993ec6366b3ec27786f9") .unwrap(), ), ( "default/linking/nested/Nested.t.sol:NestedLib", - Address::from_str("0xfebE2F30641170642f317Ff6F644Cee60E7Ac369") + Address::from_str("0xac231df03403867b05d092c26fc91b6b83f4bebe") .unwrap(), ), ], diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index cf0844429b0ee..52b5446ebdb73 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -1,10 +1,10 @@ //! RPC API keys utilities. use foundry_config::{ - NamedChain, NamedChain::{ - Arbitrum, Base, BinanceSmartChainTestnet, Celo, Mainnet, Optimism, Polygon, Sepolia, + self, Arbitrum, Base, BinanceSmartChainTestnet, Celo, Mainnet, Optimism, Polygon, Sepolia, }, + RpcEndpointUrl, RpcEndpoints, }; use rand::seq::SliceRandom; use std::sync::{ @@ -90,6 +90,22 @@ fn next(list: &[T]) -> &T { &list[next_idx() % list.len()] } +/// the RPC endpoints used during tests +pub fn rpc_endpoints() -> RpcEndpoints { + RpcEndpoints::new([ + ("mainnet", RpcEndpointUrl::Url(next_http_archive_rpc_url())), + ("mainnet2", RpcEndpointUrl::Url(next_http_archive_rpc_url())), + ("sepolia", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Sepolia))), + ("optimism", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Optimism))), + ("arbitrum", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Arbitrum))), + ("polygon", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Polygon))), + ("bsc", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::BinanceSmartChain))), + ("avaxTestnet", RpcEndpointUrl::Url("https://api.avax-test.network/ext/bc/C/rpc".into())), + ("moonbeam", RpcEndpointUrl::Url("https://moonbeam-rpc.publicnode.com".into())), + ("rpcEnvAlias", RpcEndpointUrl::Env("${RPC_ENV_ALIAS}".into())), + ]) +} + /// Returns the next _mainnet_ rpc URL in inline /// /// This will rotate all available rpc endpoints diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 2dfd11986d13c..839a3bfe8b224 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -21,17 +21,7 @@ fn init_script_cmd( 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", - ]); + cmd.args(["script", target_contract, "--root", project_root.to_str().unwrap(), "-vvvvv"]); if let Some(rpc_url) = endpoint { cmd.args(["--fork-url", rpc_url]); @@ -125,11 +115,16 @@ impl ScriptTester { } /// Initialises the test contracts by copying them into the workspace - fn copy_testdata(current_dir: &Path) -> Result<()> { + fn copy_testdata(root: &Path) -> Result<()> { let testdata = Self::testdata_path(); - 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"))?; + let from_dir = testdata.join("utils"); + let to_dir = root.join("utils"); + fs::create_dir_all(&to_dir)?; + for entry in fs::read_dir(&from_dir)? { + let file = &entry?.path(); + let name = file.file_name().unwrap(); + fs::copy(file, to_dir.join(name))?; + } Ok(()) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index a8d33e615fede..1f58821a574a6 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1,4 +1,4 @@ -use crate::init_tracing; +use crate::{init_tracing, rpc::rpc_endpoints}; use eyre::{Result, WrapErr}; use foundry_compilers::{ ArtifactOutput, ConfigurableArtifacts, PathStyle, Project, ProjectCompileOutput, @@ -642,6 +642,13 @@ impl TestProject { pretty_err(&file, fs::write(&file, config.to_string_pretty().unwrap())); } + /// Writes [`rpc_endpoints`] to the project's config. + pub fn add_rpc_endpoints(&self) { + self.update_config(|config| { + config.rpc_endpoints = rpc_endpoints(); + }); + } + /// Adds a source file to the project. pub fn add_source(&self, name: &str, contents: &str) -> PathBuf { self.inner.add_source(name, Self::add_source_prelude(contents)).unwrap() @@ -740,19 +747,26 @@ impl TestProject { /// Adds DSTest as a source under "test.sol" pub fn insert_ds_test(&self) -> PathBuf { - let s = include_str!("../../../testdata/lib/ds-test/src/test.sol"); - self.add_source("test.sol", s) + self.add_source("test.sol", include_str!("../../../testdata/utils/DSTest.sol")) + } + + /// Adds custom test utils under the "test/utils" directory. + pub fn insert_utils(&self) { + self.add_test("utils/DSTest.sol", include_str!("../../../testdata/utils/DSTest.sol")); + self.add_test("utils/Test.sol", include_str!("../../../testdata/utils/Test.sol")); + self.add_test("utils/Vm.sol", include_str!("../../../testdata/utils/Vm.sol")); + self.add_test("utils/console.sol", include_str!("../../../testdata/utils/console.sol")); } /// Adds `console.sol` as a source under "console.sol" pub fn insert_console(&self) -> PathBuf { - let s = include_str!("../../../testdata/default/logs/console.sol"); + let s = include_str!("../../../testdata/utils/console.sol"); self.add_source("console.sol", s) } /// Adds `Vm.sol` as a source under "Vm.sol" pub fn insert_vm(&self) -> PathBuf { - let s = include_str!("../../../testdata/cheats/Vm.sol"); + let s = include_str!("../../../testdata/utils/Vm.sol"); self.add_source("Vm.sol", s) } @@ -1039,14 +1053,26 @@ impl TestCommand { /// Runs the command, returning a [`snapbox`] object to assert the command output. #[track_caller] - pub fn assert(&mut self) -> OutputAssert { + pub fn assert_with(&mut self, f: &[RegexRedaction]) -> OutputAssert { let assert = OutputAssert::new(self.execute()); if self.redact_output { - return assert.with_assert(test_assert()); + let mut redactions = test_redactions(); + insert_redactions(f, &mut redactions); + return assert.with_assert( + snapbox::Assert::new() + .action_env(snapbox::assert::DEFAULT_ACTION_ENV) + .redact_with(redactions), + ); } assert } + /// Runs the command, returning a [`snapbox`] object to assert the command output. + #[track_caller] + pub fn assert(&mut self) -> OutputAssert { + self.assert_with(&[]) + } + /// Runs the command and asserts that it resulted in success. #[track_caller] pub fn assert_success(&mut self) -> OutputAssert { @@ -1142,16 +1168,9 @@ impl TestCommand { } } -fn test_assert() -> snapbox::Assert { - snapbox::Assert::new() - .action_env(snapbox::assert::DEFAULT_ACTION_ENV) - .redact_with(test_redactions()) -} - fn test_redactions() -> snapbox::Redactions { static REDACTIONS: LazyLock = LazyLock::new(|| { - let mut r = snapbox::Redactions::new(); - let redactions = [ + make_redactions(&[ ("[SOLC_VERSION]", r"Solc( version)? \d+.\d+.\d+"), ("[ELAPSED]", r"(finished )?in \d+(\.\d+)?\w?s( \(.*?s CPU time\))?"), ("[GAS]", r"[Gg]as( used)?: \d+"), @@ -1174,15 +1193,27 @@ fn test_redactions() -> snapbox::Redactions { "[ESTIMATED_AMOUNT_REQUIRED]", r"Estimated amount required:\s*(\d+(\.\d+)?)\s*[A-Z]{3}", ), - ]; - for (placeholder, re) in redactions { - r.insert(placeholder, Regex::new(re).expect(re)).expect(re); - } - r + ]) }); REDACTIONS.clone() } +/// A tuple of a placeholder and a regex replacement string. +type RegexRedaction = (&'static str, &'static str); + +/// Creates a [`snapbox`] redactions object from a list of regex redactions. +fn make_redactions(redactions: &[RegexRedaction]) -> snapbox::Redactions { + let mut r = snapbox::Redactions::new(); + insert_redactions(redactions, &mut r); + r +} + +fn insert_redactions(redactions: &[RegexRedaction], r: &mut snapbox::Redactions) { + for &(placeholder, re) in redactions { + r.insert(placeholder, Regex::new(re).expect(re)).expect(re); + } +} + /// Extension trait for [`Output`]. pub trait OutputExt { /// Returns the stdout as lossy string diff --git a/testdata/default/cheats/AccessList.t.sol b/testdata/default/cheats/AccessList.t.sol index 4615ab588adba..f59e8cdb439c3 100644 --- a/testdata/default/cheats/AccessList.t.sol +++ b/testdata/default/cheats/AccessList.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract AccessListIsolatedTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +/// forge-config: default.isolate = true +contract AccessListIsolatedTest is Test { function test_access_list() public { Write anotherWrite = new Write(); Write write = new Write(); diff --git a/testdata/default/cheats/Addr.t.sol b/testdata/default/cheats/Addr.t.sol index b0b3fefbdba79..82b8cf7d277f0 100644 --- a/testdata/default/cheats/Addr.t.sol +++ b/testdata/default/cheats/Addr.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract AddrTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract AddrTest is Test { /// forge-config: default.allow_internal_expect_revert = true function testRevertIfPkZero() public { vm.expectRevert("vm.addr: private key cannot be 0"); diff --git a/testdata/default/cheats/ArbitraryStorage.t.sol b/testdata/default/cheats/ArbitraryStorage.t.sol index 1b5585b681b3d..407d14448a996 100644 --- a/testdata/default/cheats/ArbitraryStorage.t.sol +++ b/testdata/default/cheats/ArbitraryStorage.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public a; @@ -27,9 +26,8 @@ contract Counter { } } -contract CounterArbitraryStorageWithSeedTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.fuzz.seed = "100" +contract CounterArbitraryStorageWithSeedTest is Test { function test_fresh_storage() public { uint256 index = 55; Counter counter = new Counter(); @@ -77,9 +75,8 @@ contract AContract { bytes32[] public d; } -contract AContractArbitraryStorageWithSeedTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.fuzz.seed = "100" +contract AContractArbitraryStorageWithSeedTest is Test { function test_arbitrary_storage_with_seed() public { AContract target = new AContract(); vm.setArbitraryStorage(address(target)); @@ -96,9 +93,8 @@ contract SymbolicStore { constructor() {} } -contract SymbolicStorageWithSeedTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.fuzz.seed = "100" +contract SymbolicStorageWithSeedTest is Test { function test_SymbolicStorage() public { uint256 slot = vm.randomUint(0, 100); address addr = 0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8; @@ -125,8 +121,8 @@ contract SymbolicStorageWithSeedTest is DSTest { } // -contract ArbitraryStorageOverwriteWithSeedTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); +/// forge-config: default.fuzz.seed = "100" +contract ArbitraryStorageOverwriteWithSeedTest is Test { uint256 _value; function testArbitraryStorageFalse(uint256 value) public { diff --git a/testdata/default/cheats/Assert.t.sol b/testdata/default/cheats/Assert.t.sol index ea093efc4fcb2..d29a7dba3e604 100644 --- a/testdata/default/cheats/Assert.t.sol +++ b/testdata/default/cheats/Assert.t.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract AssertionsTest is DSTest { +contract AssertionsTest is Test { string constant errorMessage = "User provided message"; uint256 constant maxDecimals = 77; - Vm constant vm = Vm(HEVM_ADDRESS); - function _abs(int256 a) internal pure returns (uint256) { // Required or it will fail when `a = type(int256).min` if (a == type(int256).min) { diff --git a/testdata/default/cheats/Assume.t.sol b/testdata/default/cheats/Assume.t.sol index 14ed341c9970e..91de77f246e3b 100644 --- a/testdata/default/cheats/Assume.t.sol +++ b/testdata/default/cheats/Assume.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract AssumeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract AssumeTest is Test { function testAssume(uint8 x) public { vm.assume(x < 2 ** 7); assertTrue(x < 2 ** 7, "did not discard inputs"); diff --git a/testdata/default/cheats/AssumeNoRevert.t.sol b/testdata/default/cheats/AssumeNoRevert.t.sol index ea6d2d9747bdd..f6a017efec212 100644 --- a/testdata/default/cheats/AssumeNoRevert.t.sol +++ b/testdata/default/cheats/AssumeNoRevert.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import {DSTest as Test} from "ds-test/test.sol"; -import {Vm} from "cheats/Vm.sol"; +import "utils/Test.sol"; contract ReverterB { /// @notice has same error selectors as contract below to test the `reverter` param @@ -62,7 +61,6 @@ contract Reverter { contract ReverterTest is Test { Reverter reverter; - Vm _vm = Vm(HEVM_ADDRESS); function setUp() public { reverter = new Reverter(); @@ -70,7 +68,7 @@ contract ReverterTest is Test { /// @dev Test that `assumeNoRevert` anticipates and correctly rejects a specific error selector function testAssumeSelector(uint256 x) public view { - _vm.assumeNoRevert( + vm.assumeNoRevert( Vm.PotentialRevert({ revertData: abi.encodeWithSelector(Reverter.MyRevert.selector), partialMatch: false, @@ -82,7 +80,7 @@ contract ReverterTest is Test { /// @dev Test that `assumeNoRevert` anticipates and correctly rejects a specific error selector and data function testAssumeWithDataSingle(uint256 x) public view { - _vm.assumeNoRevert( + vm.assumeNoRevert( Vm.PotentialRevert({ revertData: abi.encodeWithSelector(Reverter.RevertWithData.selector, 2), partialMatch: false, @@ -94,7 +92,7 @@ contract ReverterTest is Test { /// @dev Test that `assumeNoRevert` anticipates and correctly rejects a specific error selector with any extra data (ie providing selector allows for arbitrary extra data) function testAssumeWithDataPartial(uint256 x) public view { - _vm.assumeNoRevert( + vm.assumeNoRevert( Vm.PotentialRevert({ revertData: abi.encodeWithSelector(Reverter.RevertWithData.selector), partialMatch: true, @@ -106,14 +104,14 @@ contract ReverterTest is Test { /// @dev Test that `assumeNoRevert` assumptions are not cleared after a cheatcode call function testAssumeNotClearedAfterCheatcodeCall(uint256 x) public { - _vm.assumeNoRevert( + vm.assumeNoRevert( Vm.PotentialRevert({ revertData: abi.encodeWithSelector(Reverter.MyRevert.selector), partialMatch: false, reverter: address(0) }) ); - _vm.warp(block.timestamp + 1000); + vm.warp(block.timestamp + 1000); reverter.revertIf2(x); } @@ -130,7 +128,7 @@ contract ReverterTest is Test { partialMatch: false, reverter: address(reverter) }); - _vm.assumeNoRevert(revertData); + vm.assumeNoRevert(revertData); reverter.twoPossibleReverts(x); } @@ -147,7 +145,7 @@ contract ReverterTest is Test { partialMatch: false, reverter: address(reverter) }); - _vm.assumeNoRevert(revertData); + vm.assumeNoRevert(revertData); reverter.twoPossibleReverts(x); } } diff --git a/testdata/default/cheats/AttachBlob.t.sol b/testdata/default/cheats/AttachBlob.t.sol index db17c1fae5f4c..1be49ceef2e5c 100644 --- a/testdata/default/cheats/AttachBlob.t.sol +++ b/testdata/default/cheats/AttachBlob.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.25; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public counter; @@ -12,8 +11,7 @@ contract Counter { } } -contract AttachBlobTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract AttachBlobTest is Test { uint256 bobPk = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d; address bob = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; diff --git a/testdata/default/cheats/AttachDelegation.t.sol b/testdata/default/cheats/AttachDelegation.t.sol index a4e870d4cee9e..63e00462d85ba 100644 --- a/testdata/default/cheats/AttachDelegation.t.sol +++ b/testdata/default/cheats/AttachDelegation.t.sol @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract AttachDelegationTest is DSTest { +contract AttachDelegationTest is Test { event ExecutedBy(uint256 id); - Vm constant vm = Vm(HEVM_ADDRESS); uint256 alice_pk = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d; address payable alice = payable(0x70997970C51812dc3A010C7d01b50e0d17dc79C8); uint256 bob_pk = 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a; diff --git a/testdata/default/cheats/Bank.t.sol b/testdata/default/cheats/Bank.t.sol index 166fbb16ac8ea..bcda62aa6d676 100644 --- a/testdata/default/cheats/Bank.t.sol +++ b/testdata/default/cheats/Bank.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract CoinbaseTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract CoinbaseTest is Test { function testCoinbase() public { vm.coinbase(0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8); assertEq(block.coinbase, 0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8, "coinbase failed"); diff --git a/testdata/default/cheats/Base64.t.sol b/testdata/default/cheats/Base64.t.sol index fad7bbf4f297c..c96ca6b4b3a79 100644 --- a/testdata/default/cheats/Base64.t.sol +++ b/testdata/default/cheats/Base64.t.sol @@ -1,13 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; - -contract Base64Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract Base64Test is Test { function test_toBase64() public { bytes memory input = hex"00112233445566778899aabbccddeeff"; string memory expected = "ABEiM0RVZneImaq7zN3u/w=="; diff --git a/testdata/default/cheats/BlobBaseFee.t.sol b/testdata/default/cheats/BlobBaseFee.t.sol index 54fbc8f7f0616..6ce979d046eda 100644 --- a/testdata/default/cheats/BlobBaseFee.t.sol +++ b/testdata/default/cheats/BlobBaseFee.t.sol @@ -1,12 +1,9 @@ // 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); +import "utils/Test.sol"; +contract BlobBaseFeeTest is Test { function test_blob_base_fee() public { vm.blobBaseFee(6969); assertEq(vm.getBlobBaseFee(), 6969); diff --git a/testdata/default/cheats/Blobhashes.t.sol b/testdata/default/cheats/Blobhashes.t.sol index 4a589b45a38ff..6cf672c540d29 100644 --- a/testdata/default/cheats/Blobhashes.t.sol +++ b/testdata/default/cheats/Blobhashes.t.sol @@ -1,12 +1,9 @@ // 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); +import "utils/Test.sol"; +contract BlobhashesTest is Test { function testSetAndGetBlobhashes() public { bytes32[] memory blobhashes = new bytes32[](2); blobhashes[0] = bytes32(0x0000000000000000000000000000000000000000000000000000000000000001); diff --git a/testdata/default/cheats/Broadcast.t.sol b/testdata/default/cheats/Broadcast.t.sol index 9916ca0efc799..3912ea1756d82 100644 --- a/testdata/default/cheats/Broadcast.t.sol +++ b/testdata/default/cheats/Broadcast.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import {Test as ForgeTest} from "utils/Test.sol"; library F { function t2() public pure returns (uint256) { @@ -10,7 +9,7 @@ library F { } } -contract Test is DSTest { +contract Test is ForgeTest { uint256 public changed = 0; function t(uint256 a) public returns (uint256) { @@ -33,9 +32,7 @@ contract Test is DSTest { } } -contract BroadcastTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract BroadcastTest is ForgeTest { // 1st anvil account address public ACCOUNT_A = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; // 2nd anvil account @@ -160,7 +157,7 @@ contract BroadcastTest is DSTest { // } } -contract NoLink is DSTest { +contract NoLink is ForgeTest { function t(uint256 a) public returns (uint256) { uint256 b = 0; for (uint256 i; i < a; i++) { @@ -179,9 +176,7 @@ interface INoLink { function t(uint256 a) external returns (uint256); } -contract BroadcastTestNoLinking is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract BroadcastTestNoLinking is ForgeTest { // ganache-cli -d 1st address public ACCOUNT_A = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; @@ -254,9 +249,7 @@ contract BroadcastTestNoLinking is DSTest { } } -contract BroadcastMix is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract BroadcastMix is ForgeTest { // ganache-cli -d 1st address public ACCOUNT_A = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; @@ -312,9 +305,7 @@ contract BroadcastMix is DSTest { } } -contract BroadcastTestSetup is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract BroadcastTestSetup is ForgeTest { function setUp() public { // It predeployed a library first assert(vm.getNonce(msg.sender) == 1); @@ -338,9 +329,7 @@ contract BroadcastTestSetup is DSTest { } } -contract BroadcastTestLog is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract BroadcastTestLog is ForgeTest { function run() public { uint256[] memory arr = new uint256[](2); arr[0] = 3; @@ -361,14 +350,12 @@ contract BroadcastTestLog is DSTest { } } -contract TestInitialBalance is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract TestInitialBalance is ForgeTest { function runCustomSender() public { // Make sure we're testing a different caller than the default one. assert(msg.sender != address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); - // NodeConfig::test() sets the balance of the address used in this test to 100 ether. + // NodeConfig::test() sets the balance of the address used in this ForgeTest to 100 ether. assert(msg.sender.balance == 100 ether); vm.broadcast(); @@ -386,9 +373,7 @@ contract TestInitialBalance is DSTest { } } -contract MultiChainBroadcastNoLink is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract MultiChainBroadcastNoLink is ForgeTest { // ganache-cli -d 1st address public ACCOUNT_A = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; @@ -431,9 +416,7 @@ contract MultiChainBroadcastNoLink is DSTest { } } -contract MultiChainBroadcastLink is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract MultiChainBroadcastLink is ForgeTest { // ganache-cli -d 1st address public ACCOUNT_A = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; @@ -454,9 +437,7 @@ contract MultiChainBroadcastLink is DSTest { } } -contract BroadcastEmptySetUp is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract BroadcastEmptySetUp is ForgeTest { function setUp() public {} function run() public { @@ -495,9 +476,7 @@ contract ContractB { } } -contract CheckOverrides is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract CheckOverrides is ForgeTest { function run() external { // `script_caller` can be set by `--private-key ...` or `--sender ...` // Otherwise it will take the default value of 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38 @@ -546,9 +525,7 @@ contract Parent { } } -contract ScriptAdditionalContracts is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ScriptAdditionalContracts is ForgeTest { function run() external { vm.startBroadcast(); new Parent(); @@ -567,8 +544,7 @@ contract SignatureTester { } } -contract ScriptSign is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract ScriptSign is ForgeTest { bytes32 digest = keccak256("something"); function run() external { diff --git a/testdata/default/cheats/BroadcastRawTransaction.t.sol b/testdata/default/cheats/BroadcastRawTransaction.t.sol index 3806580281f73..9cf6c4950e37a 100644 --- a/testdata/default/cheats/BroadcastRawTransaction.t.sol +++ b/testdata/default/cheats/BroadcastRawTransaction.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract BroadcastRawTransactionTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract BroadcastRawTransactionTest is Test { function test_revert_not_a_tx() public { vm._expectCheatcodeRevert("failed to decode RLP-encoded transaction: unexpected string"); vm.broadcastRawTransaction(hex"0102"); @@ -277,9 +274,7 @@ contract MyERC20 { } } -contract ScriptBroadcastRawTransactionBroadcast is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ScriptBroadcastRawTransactionBroadcast is Test { function runSignedTxBroadcast() public { uint256 pk_to = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; vm.startBroadcast(pk_to); diff --git a/testdata/default/cheats/ChainId.t.sol b/testdata/default/cheats/ChainId.t.sol index ef0108e7e208e..bc2c69b798942 100644 --- a/testdata/default/cheats/ChainId.t.sol +++ b/testdata/default/cheats/ChainId.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ChainIdTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ChainIdTest is Test { function testChainId() public { uint256 newChainId = 99; vm.chainId(newChainId); diff --git a/testdata/default/cheats/CloneAccount.t.sol b/testdata/default/cheats/CloneAccount.t.sol index d584c747cb9b9..95eccd843ee09 100644 --- a/testdata/default/cheats/CloneAccount.t.sol +++ b/testdata/default/cheats/CloneAccount.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Source { uint256 public a; @@ -20,9 +19,7 @@ contract Source { } } -contract CloneAccountTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); - +contract CloneAccountTest is Test { address clone = address(777); function setUp() public { diff --git a/testdata/default/cheats/Cool.t.sol b/testdata/default/cheats/Cool.t.sol index d0750bebfa18e..71e19e4f8fd88 100644 --- a/testdata/default/cheats/Cool.t.sol +++ b/testdata/default/cheats/Cool.t.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "lib/ds-test/src/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract CoolTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract CoolTest is Test { uint256 public slot0 = 1; function testCool_SLOAD_normal() public { diff --git a/testdata/default/cheats/CopyStorage.t.sol b/testdata/default/cheats/CopyStorage.t.sol index e9195949e49c9..881a0c0aacf57 100644 --- a/testdata/default/cheats/CopyStorage.t.sol +++ b/testdata/default/cheats/CopyStorage.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public a; @@ -18,10 +17,10 @@ contract Counter { } } -contract CounterWithSeedTest is DSTest { +/// forge-config: default.fuzz.seed = "100" +contract CounterWithSeedTest is Test { Counter public counter; Counter public counter1; - Vm vm = Vm(HEVM_ADDRESS); function test_copy_storage() public { counter = new Counter(); @@ -67,11 +66,10 @@ contract CopyStorageContract { uint256 public x; } -contract CopyStorageTest is DSTest { +contract CopyStorageTest is Test { CopyStorageContract csc_1; CopyStorageContract csc_2; CopyStorageContract csc_3; - Vm vm = Vm(HEVM_ADDRESS); function _storeUInt256(address contractAddress, uint256 slot, uint256 value) internal { vm.store(contractAddress, bytes32(slot), bytes32(value)); diff --git a/testdata/default/cheats/Deal.t.sol b/testdata/default/cheats/Deal.t.sol index a46d9e7140e3e..4fcb37249aa67 100644 --- a/testdata/default/cheats/Deal.t.sol +++ b/testdata/default/cheats/Deal.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract DealTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract DealTest is Test { function testDeal(uint256 amount) public { address target = address(10); assertEq(target.balance, 0, "initial balance incorrect"); diff --git a/testdata/default/cheats/DeployCode.t.sol b/testdata/default/cheats/DeployCode.t.sol index 3978cfc335f5f..9499a9ff7f6fc 100644 --- a/testdata/default/cheats/DeployCode.t.sol +++ b/testdata/default/cheats/DeployCode.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract TestContract {} @@ -36,9 +35,7 @@ contract TestPayableContractWithArgs { } } -contract DeployCodeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract DeployCodeTest is Test { address public constant overrideAddress = 0x0000000000000000000000000000000000000064; event Payload(address sender, address target, bytes data); diff --git a/testdata/default/cheats/Derive.t.sol b/testdata/default/cheats/Derive.t.sol index c27456c6ec487..5ed5bd709c5e3 100644 --- a/testdata/default/cheats/Derive.t.sol +++ b/testdata/default/cheats/Derive.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract DeriveTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract DeriveTest is Test { function testDerive() public { string memory mnemonic = "test test test test test test test test test test test junk"; diff --git a/testdata/default/cheats/EnsNamehash.t.sol b/testdata/default/cheats/EnsNamehash.t.sol index 965d505006060..94af39a8eb784 100644 --- a/testdata/default/cheats/EnsNamehash.t.sol +++ b/testdata/default/cheats/EnsNamehash.t.sol @@ -1,12 +1,9 @@ // 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); +import "utils/Test.sol"; +contract EnsNamehashTest is Test { function testEnsNamehash() public { assertEq(vm.ensNamehash(""), 0x0000000000000000000000000000000000000000000000000000000000000000); assertEq(vm.ensNamehash("eth"), 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae); diff --git a/testdata/default/cheats/Env.t.sol b/testdata/default/cheats/Env.t.sol index 7edb35dff13ff..641167f3dfb14 100644 --- a/testdata/default/cheats/Env.t.sol +++ b/testdata/default/cheats/Env.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract EnvTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract EnvTest is Test { function testSetEnv() public { string memory key = "_foundryCheatcodeSetEnvTestKey"; string memory val = "_foundryCheatcodeSetEnvTestVal"; diff --git a/testdata/default/cheats/Etch.t.sol b/testdata/default/cheats/Etch.t.sol index 5e93dfb81213b..6bd4c28914052 100644 --- a/testdata/default/cheats/Etch.t.sol +++ b/testdata/default/cheats/Etch.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract EtchTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract EtchTest is Test { function testEtch() public { address target = address(7070707); bytes memory code = hex"1010"; diff --git a/testdata/default/cheats/ExpectCall.t.sol b/testdata/default/cheats/ExpectCall.t.sol index 01a95b4277e01..d4f2a3fb593fa 100644 --- a/testdata/default/cheats/ExpectCall.t.sol +++ b/testdata/default/cheats/ExpectCall.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Contract { function numberA() public pure returns (uint256) { @@ -60,9 +59,7 @@ contract ProxyWithDelegateCall { } } -contract ExpectCallTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectCallTest is Test { function exposed_callTargetNTimes(Contract target, uint256 a, uint256 b, uint256 times) public { for (uint256 i = 0; i < times; i++) { target.add(a, b); @@ -206,9 +203,7 @@ contract ExpectCallTest is DSTest { } } -contract ExpectCallCountTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectCallCountTest is Test { function testExpectCallCountWithData() public { Contract target = new Contract(); vm.expectCall(address(target), abi.encodeWithSelector(Contract.add.selector, 1, 2), 3); @@ -332,9 +327,7 @@ contract ExpectCallCountTest is DSTest { } } -contract ExpectCallMixedTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectCallMixedTest is Test { function exposed_callTargetNTimes(Contract target, uint256 a, uint256 b, uint256 times) public { for (uint256 i = 0; i < times; i++) { target.add(1, 2); diff --git a/testdata/default/cheats/ExpectCreate.t.sol b/testdata/default/cheats/ExpectCreate.t.sol index a922d01b92bac..f416c645b3424 100644 --- a/testdata/default/cheats/ExpectCreate.t.sol +++ b/testdata/default/cheats/ExpectCreate.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Contract { function add(uint256 a, uint256 b) public pure returns (uint256) { @@ -20,8 +19,7 @@ contract ContractDeployer { } } -contract ExpectCreateTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract ExpectCreateTest is Test { bytes bytecode = vm.getDeployedCode("cheats/ExpectCreate.t.sol:Contract"); function testExpectCreate() public { diff --git a/testdata/default/cheats/ExpectEmit.t.sol b/testdata/default/cheats/ExpectEmit.t.sol index ca91513443d21..516ac1079476c 100644 --- a/testdata/default/cheats/ExpectEmit.t.sol +++ b/testdata/default/cheats/ExpectEmit.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Emitter { uint256 public thing; @@ -115,8 +114,7 @@ contract LowLevelCaller { function g() public {} } -contract ExpectEmitTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract ExpectEmitTest is Test { Emitter emitter; event Something(uint256 indexed topic1, uint256 indexed topic2, uint256 indexed topic3, uint256 data); @@ -416,8 +414,7 @@ contract ExpectEmitTest is DSTest { // } } -contract ExpectEmitCountTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract ExpectEmitCountTest is Test { Emitter emitter; event Something(uint256 indexed topic1, uint256 indexed topic2, uint256 indexed topic3, uint256 data); diff --git a/testdata/default/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol index 1b70af1188765..f8fcf3d621318 100644 --- a/testdata/default/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Reverter { error CustomError(); @@ -71,9 +70,7 @@ contract Dummy { } } -contract ExpectRevertTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectRevertTest is Test { function shouldRevert() internal { revert(); } @@ -263,9 +260,7 @@ contract DContract { } } -contract ExpectRevertWithReverterTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectRevertWithReverterTest is Test { error CContractError(string reason); AContract aContract; @@ -312,9 +307,7 @@ contract ExpectRevertWithReverterTest is DSTest { } } -contract ExpectRevertCount is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectRevertCount is Test { function testRevertCountAny() public { uint64 count = 3; Reverter reverter = new Reverter(); @@ -395,9 +388,7 @@ contract ExpectRevertCount is DSTest { } } -contract ExpectRevertCountWithReverter is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ExpectRevertCountWithReverter is Test { function testRevertCountWithReverter() public { uint64 count = 2; Reverter reverter = new Reverter(); diff --git a/testdata/default/cheats/Fee.t.sol b/testdata/default/cheats/Fee.t.sol index 120627c0004e9..d96fa6f9b6af7 100644 --- a/testdata/default/cheats/Fee.t.sol +++ b/testdata/default/cheats/Fee.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract FeeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract FeeTest is Test { function testFee() public { vm.fee(10); assertEq(block.basefee, 10, "fee failed"); diff --git a/testdata/default/cheats/Ffi.t.sol b/testdata/default/cheats/Ffi.t.sol index 23ac54e6ace12..6c84bdf893c29 100644 --- a/testdata/default/cheats/Ffi.t.sol +++ b/testdata/default/cheats/Ffi.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract FfiTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract FfiTest is Test { function testFfi() public { string[] memory inputs = new string[](3); inputs[0] = "bash"; diff --git a/testdata/default/cheats/Fork.t.sol b/testdata/default/cheats/Fork.t.sol index 2f2e627de131a..281a27a0b868c 100644 --- a/testdata/default/cheats/Fork.t.sol +++ b/testdata/default/cheats/Fork.t.sol @@ -1,19 +1,17 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; interface IWETH { function deposit() external payable; function balanceOf(address) external view returns (uint256); } -contract ForkTest is DSTest { +contract ForkTest is Test { address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; uint256 constant mainblock = 14_608_400; - Vm constant vm = Vm(HEVM_ADDRESS); IWETH WETH = IWETH(WETH_TOKEN_ADDR); uint256 forkA; diff --git a/testdata/default/cheats/Fork2.t.sol b/testdata/default/cheats/Fork2.t.sol index d0703ce7fa6ce..037d4e0fa9528 100644 --- a/testdata/default/cheats/Fork2.t.sol +++ b/testdata/default/cheats/Fork2.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "../logs/console.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; struct MyStruct { uint256 value; @@ -27,9 +25,7 @@ contract MyContract { } } -contract ForkTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ForkTest is Test { uint256 mainnetFork; uint256 optimismFork; @@ -150,7 +146,7 @@ contract ForkTest is DSTest { assertEq(dummy.val(), expectedValue); } - // checks diagnostic + /// forge-config: default.allow_internal_expect_revert = true function testNonExistingContractRevert() public { vm.selectFork(mainnetFork); DummyContract dummy = new DummyContract(); @@ -164,7 +160,8 @@ contract ForkTest is DSTest { assertEq(dummyAddress, address(dummy)); // this will revert since `dummy` does not exists on the currently active fork - string memory msg2 = dummy.hello(); + vm.expectRevert(); + dummy.noop(); } struct EthGetLogsJsonParseable { @@ -245,6 +242,8 @@ contract ForkTest is DSTest { contract DummyContract { uint256 public val; + function noop() external pure {} + function hello() external view returns (string memory) { return "hello"; } diff --git a/testdata/default/cheats/Fs.t.sol b/testdata/default/cheats/Fs.t.sol index b4882525944cf..80b89d7d69931 100644 --- a/testdata/default/cheats/Fs.t.sol +++ b/testdata/default/cheats/Fs.t.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract FsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract FsTest is Test { bytes constant FOUNDRY_TOML_ACCESS_ERR = "access to foundry.toml is not allowed"; bytes constant FOUNDRY_READ_ERR = "the path /etc/hosts is not allowed to be accessed for read operations"; bytes constant FOUNDRY_READ_DIR_ERR = "the path /etc is not allowed to be accessed for read operations"; @@ -42,7 +40,7 @@ contract FsTest is DSTest { } function testWriteFile() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; string memory data = "hello writable world"; vm.writeFile(path, data); @@ -58,7 +56,7 @@ contract FsTest is DSTest { function testCopyFile() public { string memory from = "fixtures/File/read.txt"; - string memory to = "fixtures/File/copy.txt"; + string memory to = "fixtures/File/ignored/copy.txt"; uint64 copied = vm.copyFile(from, to); assertEq(vm.fsMetadata(to).length, uint256(copied)); assertEq(vm.readFile(from), vm.readFile(to)); @@ -66,7 +64,7 @@ contract FsTest is DSTest { } function testWriteLine() public { - string memory path = "fixtures/File/write_line.txt"; + string memory path = "fixtures/File/ignored/write_line.txt"; string memory line1 = "first line"; vm.writeLine(path, line1); @@ -91,7 +89,7 @@ contract FsTest is DSTest { } function testRemoveFile() public { - string memory path = "fixtures/File/remove_file.txt"; + string memory path = "fixtures/File/ignored/remove_file.txt"; string memory data = "hello writable world"; vm.writeFile(path, data); diff --git a/testdata/default/cheats/GasMetering.t.sol b/testdata/default/cheats/GasMetering.t.sol index 3cb105d236f02..fbde54741c624 100644 --- a/testdata/default/cheats/GasMetering.t.sol +++ b/testdata/default/cheats/GasMetering.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract B { function a() public returns (uint256) { @@ -10,9 +9,7 @@ contract B { } } -contract GasMeteringTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GasMeteringTest is Test { function testGasMetering() public { uint256 gas_start = gasleft(); @@ -40,6 +37,7 @@ contract GasMeteringTest is DSTest { function testGasMeteringExternal() public { B b = new B(); + uint256 gas_start = gasleft(); b.a(); diff --git a/testdata/default/cheats/GetArtifactPath.t.sol b/testdata/default/cheats/GetArtifactPath.t.sol index 4b0df4ba6e6ec..7a2e1335d0c04 100644 --- a/testdata/default/cheats/GetArtifactPath.t.sol +++ b/testdata/default/cheats/GetArtifactPath.t.sol @@ -1,20 +1,16 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity =0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract DummyForGetArtifactPath {} -contract GetArtifactPathTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GetArtifactPathTest is Test { function testGetArtifactPathByCode() public { - DummyForGetArtifactPath dummy = new DummyForGetArtifactPath(); bytes memory dummyCreationCode = type(DummyForGetArtifactPath).creationCode; string memory path = vm.getArtifactPathByCode(dummyCreationCode); - assertTrue(vm.contains(path, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json")); + assertTrue(vm.contains(path, "/out/GetArtifactPath.t.sol/DummyForGetArtifactPath.json")); } function testGetArtifactPathByDeployedCode() public { @@ -22,6 +18,6 @@ contract GetArtifactPathTest is DSTest { bytes memory dummyRuntimeCode = address(dummy).code; string memory path = vm.getArtifactPathByDeployedCode(dummyRuntimeCode); - assertTrue(vm.contains(path, "/out/default/GetArtifactPath.t.sol/DummyForGetArtifactPath.json")); + assertTrue(vm.contains(path, "/out/GetArtifactPath.t.sol/DummyForGetArtifactPath.json")); } } diff --git a/testdata/default/cheats/GetBlockTimestamp.t.sol b/testdata/default/cheats/GetBlockTimestamp.t.sol index 816bc0d1ef89e..65bda02052764 100644 --- a/testdata/default/cheats/GetBlockTimestamp.t.sol +++ b/testdata/default/cheats/GetBlockTimestamp.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GetBlockTimestampTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GetBlockTimestampTest is Test { function testGetTimestamp() public { uint256 timestamp = vm.getBlockTimestamp(); assertEq(timestamp, 1, "timestamp should be 1"); diff --git a/testdata/default/cheats/GetChain.t.sol b/testdata/default/cheats/GetChain.t.sol index 15923612cfc60..ca6e1a5c284cf 100644 --- a/testdata/default/cheats/GetChain.t.sol +++ b/testdata/default/cheats/GetChain.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GetChainTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GetChainTest is Test { function testGetMainnet() public { // Test mainnet Vm.Chain memory mainnet = vm.getChain("mainnet"); diff --git a/testdata/default/cheats/GetCode.t.sol b/testdata/default/cheats/GetCode.t.sol index 6020e4f1fd5bb..514ac66b9d0b3 100644 --- a/testdata/default/cheats/GetCode.t.sol +++ b/testdata/default/cheats/GetCode.t.sol @@ -1,16 +1,13 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity =0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract TestContract {} contract TestContractGetCode {} -contract GetCodeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GetCodeTest is Test { function testGetCode() public { bytes memory fullPath = vm.getCode("fixtures/GetCode/WorkingContract.json"); //bytes memory fileOnly = vm.getCode("WorkingContract.sol"); @@ -73,6 +70,7 @@ contract GetCodeTest is DSTest { /// forge-config: default.allow_internal_expect_revert = true function testRevertIfGetUnlinked() public { + vm.skip(true, "artifacts are always linked now"); vm.expectRevert("vm.getCode: no matching artifact found"); vm.getCode("UnlinkedContract.sol"); } diff --git a/testdata/default/cheats/GetDeployedCode.t.sol b/testdata/default/cheats/GetDeployedCode.t.sol index 295d2ae8f3f38..a33a13d47805c 100644 --- a/testdata/default/cheats/GetDeployedCode.t.sol +++ b/testdata/default/cheats/GetDeployedCode.t.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity =0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract TestContract {} -contract GetDeployedCodeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GetDeployedCodeTest is Test { address public constant overrideAddress = 0x0000000000000000000000000000000000000064; event Payload(address sender, address target, bytes data); diff --git a/testdata/default/cheats/GetFoundryVersion.t.sol b/testdata/default/cheats/GetFoundryVersion.t.sol index ac30bb8491874..6139b8b6b6a5e 100644 --- a/testdata/default/cheats/GetFoundryVersion.t.sol +++ b/testdata/default/cheats/GetFoundryVersion.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GetFoundryVersionTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GetFoundryVersionTest is Test { function testGetFoundryVersion() public view { // (e.g. 0.3.0-nightly+3cb96bde9b.1737036656.debug) string memory fullVersionString = vm.getFoundryVersion(); diff --git a/testdata/default/cheats/GetLabel.t.sol b/testdata/default/cheats/GetLabel.t.sol index c5a5638d36752..b5ccc64533862 100644 --- a/testdata/default/cheats/GetLabel.t.sol +++ b/testdata/default/cheats/GetLabel.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GetLabelTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GetLabelTest is Test { function testGetLabel() public { // Label an address. vm.label(address(1), "Sir Address the 1st"); diff --git a/testdata/default/cheats/GetNonce.t.sol b/testdata/default/cheats/GetNonce.t.sol index d4043a59992a8..a786fd496ff94 100644 --- a/testdata/default/cheats/GetNonce.t.sol +++ b/testdata/default/cheats/GetNonce.t.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Foo {} -contract GetNonceTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GetNonceTest is Test { function testGetNonce() public { uint64 nonce1 = vm.getNonce(address(this)); new Foo(); diff --git a/testdata/default/cheats/GetRawBlockHeader.t.sol b/testdata/default/cheats/GetRawBlockHeader.t.sol index 9dcd2fc0d39c4..54600149863a0 100644 --- a/testdata/default/cheats/GetRawBlockHeader.t.sol +++ b/testdata/default/cheats/GetRawBlockHeader.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GetRawBlockHeaderTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GetRawBlockHeaderTest is Test { function testGetRawBlockHeaderWithFork() public { vm.createSelectFork("mainnet"); assertEq( diff --git a/testdata/default/cheats/GetStorageSlots.t.sol b/testdata/default/cheats/GetStorageSlots.t.sol index 4ac6f77d462fd..f61638ac51ea1 100644 --- a/testdata/default/cheats/GetStorageSlots.t.sol +++ b/testdata/default/cheats/GetStorageSlots.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract StorageContract { // Simple variables - 1 slot each @@ -44,8 +43,7 @@ contract StorageContract { } } -contract GetStorageSlotsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract GetStorageSlotsTest is Test { StorageContract storageContract; function setUp() public { diff --git a/testdata/default/cheats/Json.t.sol b/testdata/default/cheats/Json.t.sol index b7aefae736e9a..c187e07465bb1 100644 --- a/testdata/default/cheats/Json.t.sol +++ b/testdata/default/cheats/Json.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; library JsonStructs { address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); @@ -66,7 +64,7 @@ library JsonStructs { } } -contract ParseJsonTest is DSTest { +contract ParseJsonTest is Test { using JsonStructs for *; struct FlatJson { @@ -88,7 +86,6 @@ contract ParseJsonTest is DSTest { string name; } - Vm constant vm = Vm(HEVM_ADDRESS); string json; function setUp() public { @@ -328,9 +325,7 @@ contract ParseJsonTest is DSTest { } } -contract WriteJsonTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract WriteJsonTest is Test { string json1; string json2; diff --git a/testdata/default/cheats/Label.t.sol b/testdata/default/cheats/Label.t.sol index 4ff5d3860bed0..7c1ebd02d2443 100644 --- a/testdata/default/cheats/Label.t.sol +++ b/testdata/default/cheats/Label.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract LabelTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract LabelTest is Test { function testLabel() public { vm.label(address(1), "Sir Address the 1st"); } diff --git a/testdata/default/cheats/Load.t.sol b/testdata/default/cheats/Load.t.sol index 06f4b5bd52718..dcdd145c7c8f7 100644 --- a/testdata/default/cheats/Load.t.sol +++ b/testdata/default/cheats/Load.t.sol @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Storage { uint256 slot0 = 10; } -contract LoadTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract LoadTest is Test { uint256 slot0 = 20; Storage store; diff --git a/testdata/default/cheats/Mapping.t.sol b/testdata/default/cheats/Mapping.t.sol index 82477150ae9ca..7b0eb11ccb32d 100644 --- a/testdata/default/cheats/Mapping.t.sol +++ b/testdata/default/cheats/Mapping.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract RecordMapping { int256 length; @@ -18,9 +17,7 @@ contract RecordMapping { } } -contract RecordMappingTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract RecordMappingTest is Test { function testRecordMapping() public { RecordMapping target = new RecordMapping(); diff --git a/testdata/default/cheats/MemSafety.t.sol b/testdata/default/cheats/MemSafety.t.sol index 9bb282538ddfe..953ca07e85e03 100644 --- a/testdata/default/cheats/MemSafety.t.sol +++ b/testdata/default/cheats/MemSafety.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract MemSafetyTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract MemSafetyTest is Test { //////////////////////////////////////////////////////////////// // MSTORE // //////////////////////////////////////////////////////////////// diff --git a/testdata/default/cheats/MockCall.t.sol b/testdata/default/cheats/MockCall.t.sol index f11fd20984571..e2ac74d6f70fa 100644 --- a/testdata/default/cheats/MockCall.t.sol +++ b/testdata/default/cheats/MockCall.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Mock { uint256 state = 0; @@ -56,9 +55,7 @@ contract NestedMockDelegateCall { } } -contract MockCallTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract MockCallTest is Test { function testMockGetters() public { Mock target = new Mock(); @@ -183,9 +180,7 @@ contract MockCallTest is DSTest { } } -contract MockCallRevertTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract MockCallRevertTest is Test { error TestError(bytes msg); bytes constant ERROR_MESSAGE = "ERROR_MESSAGE"; diff --git a/testdata/default/cheats/MockCalls.t.sol b/testdata/default/cheats/MockCalls.t.sol index 2bd4d8bd9ea2e..e0f5eef151db6 100644 --- a/testdata/default/cheats/MockCalls.t.sol +++ b/testdata/default/cheats/MockCalls.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract MockCallsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract MockCallsTest is Test { function testMockCallsLastShouldPersist() public { address mockUser = vm.addr(vm.randomUint()); address mockErc20 = vm.addr(vm.randomUint()); diff --git a/testdata/default/cheats/MockFunction.t.sol b/testdata/default/cheats/MockFunction.t.sol index 6d670024b2053..3defa8cfd3580 100644 --- a/testdata/default/cheats/MockFunction.t.sol +++ b/testdata/default/cheats/MockFunction.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract MockFunctionContract { uint256 public a; @@ -28,10 +27,9 @@ contract ModelMockFunctionContract { } } -contract MockFunctionTest is DSTest { +contract MockFunctionTest is Test { MockFunctionContract my_contract; ModelMockFunctionContract model_contract; - Vm vm = Vm(HEVM_ADDRESS); function setUp() public { my_contract = new MockFunctionContract(); diff --git a/testdata/default/cheats/Nonce.t.sol b/testdata/default/cheats/Nonce.t.sol index 312c2b4d7edcc..7b7f7553e7854 100644 --- a/testdata/default/cheats/Nonce.t.sol +++ b/testdata/default/cheats/Nonce.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public count; @@ -12,9 +11,8 @@ contract Counter { } } -contract NonceIsolatedTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.isolate = true +contract NonceIsolatedTest is Test { function testIncrementNonce() public { address bob = address(14); vm.startPrank(bob); diff --git a/testdata/default/cheats/Parse.t.sol b/testdata/default/cheats/Parse.t.sol index 65e7561d104de..2736aa8ade7b6 100644 --- a/testdata/default/cheats/Parse.t.sol +++ b/testdata/default/cheats/Parse.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ParseTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ParseTest is Test { function testParseBytes() public { bytes memory testBytes = hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"; diff --git a/testdata/default/cheats/Prank.t.sol b/testdata/default/cheats/Prank.t.sol index 151f0d8306776..e8124b2475e16 100644 --- a/testdata/default/cheats/Prank.t.sol +++ b/testdata/default/cheats/Prank.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Victim { function assertCallerAndOrigin( @@ -107,9 +106,7 @@ contract ProxyTest { address public sender; } -contract PrankTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract PrankTest is Test { function testPrankDelegateCallPrank2() public { ProxyTest proxy = new ProxyTest(); ImplementationTest impl = new ImplementationTest(); @@ -551,9 +548,7 @@ contract PrankTest is DSTest { } } -contract Issue9990 is DSTest { - Vm constant vm = Vm(address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))); - +contract Issue9990 is Test { function testDelegatePrank() external { A a = new A(); vm.etch(address(0x11111), hex"11"); @@ -600,9 +595,7 @@ contract Counter { } } -contract Issue10528 is DSTest { - Vm constant vm = Vm(address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))); - +contract Issue10528 is Test { function testStartPrankOnContractCreation() external { vm.startPrank(address(0x22222)); Counter counter = new Counter(); diff --git a/testdata/default/cheats/Prevrandao.t.sol b/testdata/default/cheats/Prevrandao.t.sol index aab8e326c43ce..ff4d8b48b8c8d 100644 --- a/testdata/default/cheats/Prevrandao.t.sol +++ b/testdata/default/cheats/Prevrandao.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract PrevrandaoTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract PrevrandaoTest is Test { function testPrevrandao() public { assertEq(block.prevrandao, 0); vm.prevrandao(uint256(10)); diff --git a/testdata/default/cheats/ProjectRoot.t.sol b/testdata/default/cheats/ProjectRoot.t.sol index cff3d83751d66..8835d2192cbfc 100644 --- a/testdata/default/cheats/ProjectRoot.t.sol +++ b/testdata/default/cheats/ProjectRoot.t.sol @@ -1,15 +1,19 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract ProjectRootTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract ProjectRootTest is Test { bytes public manifestDirBytes; function testProjectRoot() public { - manifestDirBytes = bytes(vm.envString("CARGO_MANIFEST_DIR")); + // .../crates/forge + string memory manifestDir = vm.envOr("CARGO_MANIFEST_DIR", string("")); + if (bytes(manifestDir).length == 0) { + vm.skip(true, "CARGO_MANIFEST_DIR environment variable is not set"); + } + manifestDirBytes = bytes(manifestDir); + for (uint256 i = 0; i < 7; i++) { manifestDirBytes.pop(); } @@ -20,6 +24,10 @@ contract ProjectRootTest is DSTest { } bytes memory expectedRootDir = abi.encodePacked(manifestDirBytes, "ata"); - assertEq(vm.projectRoot(), string(expectedRootDir)); + assertEq(normalizePath(vm.projectRoot()), normalizePath(string(expectedRootDir))); + } + + function normalizePath(string memory path) internal pure returns (string memory) { + return vm.replace(path, "\\", "/"); } } diff --git a/testdata/default/cheats/Prompt.t.sol b/testdata/default/cheats/Prompt.t.sol index fa60f6740698e..5505629da15ba 100644 --- a/testdata/default/cheats/Prompt.t.sol +++ b/testdata/default/cheats/Prompt.t.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 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); +import "utils/Test.sol"; +// All `prompt` functions should revert in CI and testing environments either +// with a timeout or because no terminal is available. +contract PromptTest is Test { function testPrompt_revertNotATerminal() public { - // should revert in CI and testing environments either with timeout or because no terminal is available + checkTty(); + vm._expectCheatcodeRevert(); vm.prompt("test"); @@ -21,12 +20,22 @@ contract PromptTest is DSTest { } function testPrompt_Address() public { + checkTty(); + vm._expectCheatcodeRevert(); address test = vm.promptAddress("test"); } function testPrompt_Uint() public { + checkTty(); + vm._expectCheatcodeRevert(); uint256 test = vm.promptUint("test"); } + + function checkTty() internal { + if (!vm.envOr("CI", false)) { + vm.skip(true, "min timeout is 1s, don't test it"); + } + } } diff --git a/testdata/default/cheats/RandomAddress.t.sol b/testdata/default/cheats/RandomAddress.t.sol index 61510ed4eaaac..043884ed0a014 100644 --- a/testdata/default/cheats/RandomAddress.t.sol +++ b/testdata/default/cheats/RandomAddress.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RandomAddress is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RandomAddress is Test { function testRandomAddress() public { vm.randomAddress(); } diff --git a/testdata/default/cheats/RandomBytes.t.sol b/testdata/default/cheats/RandomBytes.t.sol index dbc03a6ccfb8f..4cdc08a61604c 100644 --- a/testdata/default/cheats/RandomBytes.t.sol +++ b/testdata/default/cheats/RandomBytes.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RandomBytes is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RandomBytes is Test { function testRandomBytes4() public { vm.randomBytes4(); } diff --git a/testdata/default/cheats/RandomCheatcodes.t.sol b/testdata/default/cheats/RandomCheatcodes.t.sol index c42b4310012f1..bd1968000eab7 100644 --- a/testdata/default/cheats/RandomCheatcodes.t.sol +++ b/testdata/default/cheats/RandomCheatcodes.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RandomCheatcodesTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RandomCheatcodesTest is Test { int128 constant min = -170141183460469231731687303715884105728; int128 constant max = 170141183460469231731687303715884105727; @@ -57,9 +54,7 @@ contract RandomCheatcodesTest is DSTest { } } -contract RandomBytesTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); - +contract RandomBytesTest is Test { bytes1 local_byte; bytes local_bytes; diff --git a/testdata/default/cheats/RandomUint.t.sol b/testdata/default/cheats/RandomUint.t.sol index c0021030d0d1e..8e0b402386133 100644 --- a/testdata/default/cheats/RandomUint.t.sol +++ b/testdata/default/cheats/RandomUint.t.sol @@ -1,12 +1,9 @@ // 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); +import "utils/Test.sol"; +contract RandomUint is Test { function testRandomUint() public { vm.randomUint(); } diff --git a/testdata/default/cheats/ReadCallers.t.sol b/testdata/default/cheats/ReadCallers.t.sol index dbd198a2d93e8..7bef8c502a091 100644 --- a/testdata/default/cheats/ReadCallers.t.sol +++ b/testdata/default/cheats/ReadCallers.t.sol @@ -1,16 +1,13 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Target { function consumeNewCaller() external {} } -contract ReadCallersTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract ReadCallersTest is Test { function testReadCallersWithNoActivePrankOrBroadcast() public { address expectedSender = msg.sender; address expectedTxOrigin = tx.origin; diff --git a/testdata/default/cheats/Record.t.sol b/testdata/default/cheats/Record.t.sol index c3029d5f54205..6525092803303 100644 --- a/testdata/default/cheats/Record.t.sol +++ b/testdata/default/cheats/Record.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract RecordAccess { function record() public returns (NestedRecordAccess) { @@ -25,9 +24,7 @@ contract NestedRecordAccess { } } -contract RecordTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract RecordTest is Test { function testRecordAccess() public { RecordAccess target = new RecordAccess(); diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index 88f795fafef99..fee8ebbc361c8 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; /// @notice Helper contract with a construction that makes a call to itself then /// optionally reverts if zero-length data is passed @@ -124,13 +122,13 @@ contract NestedRunner { /// Helper contract that uses all three EXT* opcodes on a given address contract ExtChecker { - function checkExts(address a) external { + function checkExts(address a) external returns (bytes memory out) { assembly { - let x := extcodesize(a) - let y := extcodehash(a) - extcodecopy(a, x, y, 0) - // sstore to check that storage accesses are correctly stored in a new access with a "resume" context - sstore(0, balance(a)) + mstore(out, mul(0x20, 4)) + mstore(add(out, 0x20), extcodesize(a)) + mstore(add(out, 0x40), extcodehash(a)) + extcodecopy(a, 0, 0x60, 0x20) + mstore(add(out, 0x80), balance(a)) } } } @@ -203,8 +201,7 @@ contract Proxy { } /// @notice Test that the cheatcode correctly records account accesses -contract RecordAccountAccessesTest is DSTest { - Vm constant cheats = Vm(HEVM_ADDRESS); +contract RecordAccountAccessesTest is Test { NestedRunner runner; NestedStorer nestedStorer; Create2or create2or; @@ -225,9 +222,9 @@ contract RecordAccountAccessesTest is DSTest { StorageAccessor one = test1; Proxy proxy = new Proxy(address(one)); - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); address(proxy).call(abi.encodeCall(StorageAccessor.read, bytes32(uint256(1234)))); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 2, "incorrect length"); @@ -255,24 +252,24 @@ contract RecordAccountAccessesTest is DSTest { function testStorageAccesses() public { StorageAccessor one = test1; StorageAccessor two = test2; - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); one.read(bytes32(uint256(1234))); one.write(bytes32(uint256(1235)), bytes32(uint256(5678))); two.write(bytes32(uint256(5678)), bytes32(uint256(123469))); two.write(bytes32(uint256(5678)), bytes32(uint256(1234))); - string memory diffs = cheats.getStateDiff(); + string memory diffs = vm.getStateDiff(); assertEq( "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9\ncontract: default/cheats/RecordAccountAccesses.t.sol:StorageAccessor\n- state diff:\n@ 0x00000000000000000000000000000000000000000000000000000000000004d3: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x000000000000000000000000000000000000000000000000000000000000162e\n\n0xc7183455a4C133Ae270771860664b6B7ec320bB1\ncontract: default/cheats/RecordAccountAccesses.t.sol:StorageAccessor\n- state diff:\n@ 0x000000000000000000000000000000000000000000000000000000000000162e: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x00000000000000000000000000000000000000000000000000000000000004d2\n\n", diffs ); - string memory diffsJson = cheats.getStateDiffJson(); + string memory diffsJson = vm.getStateDiffJson(); assertEq( '{"0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9":{"label":null,"contract":"default/cheats/RecordAccountAccesses.t.sol:StorageAccessor","balanceDiff":null,"nonceDiff":null,"stateDiff":{"0x00000000000000000000000000000000000000000000000000000000000004d3":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x000000000000000000000000000000000000000000000000000000000000162e"}}},"0xc7183455a4c133ae270771860664b6b7ec320bb1":{"label":null,"contract":"default/cheats/RecordAccountAccesses.t.sol:StorageAccessor","balanceDiff":null,"nonceDiff":null,"stateDiff":{"0x000000000000000000000000000000000000000000000000000000000000162e":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x00000000000000000000000000000000000000000000000000000000000004d2"}}}}', diffsJson ); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 4, "incorrect length"); assertEq(called[0].storageAccesses.length, 1, "incorrect storage length"); @@ -334,7 +331,7 @@ contract RecordAccountAccessesTest is DSTest { /// @notice Test that basic account accesses are correctly recorded function testRecordAccountAccesses() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); (bool succ,) = address(1234).call(""); (succ,) = address(5678).call{value: 1 ether}(""); @@ -343,7 +340,7 @@ contract RecordAccountAccessesTest is DSTest { // contract calls to self in constructor SelfCaller caller = new SelfCaller{value: 2 ether}("hello2 world2"); - string memory callerAddress = cheats.toString(address(caller)); + string memory callerAddress = vm.toString(address(caller)); string memory expectedStateDiff = "0x000000000000000000000000000000000000162e\n- balance diff: 0 \xE2\x86\x92 1000000000000000000\n\n"; expectedStateDiff = string.concat(expectedStateDiff, callerAddress); @@ -353,9 +350,9 @@ contract RecordAccountAccessesTest is DSTest { expectedStateDiff, "\n- balance diff: 0 \xE2\x86\x92 2000000000000000000\n- nonce diff: 0 \xE2\x86\x92 1\n\n" ); - assertEq(expectedStateDiff, cheats.getStateDiff()); + assertEq(expectedStateDiff, vm.getStateDiff()); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 6); assertEq( called[0], @@ -484,17 +481,17 @@ contract RecordAccountAccessesTest is DSTest { /// reverts function testRevertingCall() public { uint256 initBalance = address(this).balance; - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); try this.revertingCall{value: 1 ether}(address(1234), "") {} catch {} assertEq( "0x00000000000000000000000000000000000004d2\n- balance diff: 0 \xE2\x86\x92 100000000000000000\n\n", - cheats.getStateDiff() + vm.getStateDiff() ); assertEq( '{"0x00000000000000000000000000000000000004d2":{"label":null,"contract":null,"balanceDiff":{"previousValue":"0x0","newValue":"0x16345785d8a0000"},"nonceDiff":null,"stateDiff":{}}}', - cheats.getStateDiffJson() + vm.getStateDiffJson() ); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 2); assertEq( called[0], @@ -540,14 +537,14 @@ contract RecordAccountAccessesTest is DSTest { /// @notice Test that nested account accesses are correctly recorded function testNested() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); runNested(false, false); } /// @notice Test that nested account accesses are correctly recorded when /// the first call reverts function testNested_Revert() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); runNested(true, false); } @@ -555,7 +552,7 @@ contract RecordAccountAccessesTest is DSTest { /// @param shouldRevert Whether the first call should revert function runNested(bool shouldRevert, bool expectFirstCall) public { try runner.run{value: 1 ether}(shouldRevert) {} catch {} - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 7 + toUint(expectFirstCall), "incorrect length"); uint64 startingIndex = uint64(toUint(expectFirstCall)); @@ -829,18 +826,18 @@ contract RecordAccountAccessesTest is DSTest { } function testNestedStorage() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); nestedStorer.run(); - cheats.label(address(nestedStorer), "NestedStorer"); + vm.label(address(nestedStorer), "NestedStorer"); assertEq( "0x2e234DAe75C793f67A35089C9d99245E1C58470b\nlabel: NestedStorer\ncontract: default/cheats/RecordAccountAccesses.t.sol:NestedStorer\n- state diff:\n@ 0x4566fa0cd03218c55bba914d793f5e6b9113172c1f684bb5f464c08c867e8977: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 0xbf57896b60daefa2c41de2feffecfc11debd98ea8c913a5170f60e53959ac00a: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 0xc664893a982d78bbeab379feef216ff517b7ea73626b280723be1ace370364cd: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n@ 0xdc5330afa9872081253545dca3f448752688ff1b098b38c1abe4c4cdff4b0b0e: 0x0000000000000000000000000000000000000000000000000000000000000000 \xE2\x86\x92 0x0000000000000000000000000000000000000000000000000000000000000001\n\n", - cheats.getStateDiff() + vm.getStateDiff() ); assertEq( '{"0x2e234dae75c793f67a35089c9d99245e1c58470b":{"label":"NestedStorer","contract":"default/cheats/RecordAccountAccesses.t.sol:NestedStorer","balanceDiff":null,"nonceDiff":null,"stateDiff":{"0x4566fa0cd03218c55bba914d793f5e6b9113172c1f684bb5f464c08c867e8977":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x0000000000000000000000000000000000000000000000000000000000000001"},"0xbf57896b60daefa2c41de2feffecfc11debd98ea8c913a5170f60e53959ac00a":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x0000000000000000000000000000000000000000000000000000000000000001"},"0xc664893a982d78bbeab379feef216ff517b7ea73626b280723be1ace370364cd":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x0000000000000000000000000000000000000000000000000000000000000001"},"0xdc5330afa9872081253545dca3f448752688ff1b098b38c1abe4c4cdff4b0b0e":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x0000000000000000000000000000000000000000000000000000000000000001"}}}}', - cheats.getStateDiffJson() + vm.getStateDiffJson() ); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 3, "incorrect account access length"); assertEq(called[0].storageAccesses.length, 2, "incorrect run storage length"); @@ -963,14 +960,14 @@ contract RecordAccountAccessesTest is DSTest { /// @notice Test that constructor account and storage accesses are recorded, including reverts function testConstructorStorage() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); address storer = address(new ConstructorStorer(false)); try create2or.create2(bytes32(0), abi.encodePacked(type(ConstructorStorer).creationCode, abi.encode(true))) {} catch {} bytes memory creationCode = abi.encodePacked(type(ConstructorStorer).creationCode, abi.encode(true)); address hypotheticalStorer = deriveCreate2Address(address(create2or), bytes32(0), keccak256(creationCode)); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 3, "incorrect account access length"); assertEq(toUint(called[0].kind), toUint(Vm.AccountAccessKind.Create), "incorrect kind"); assertEq(toUint(called[1].kind), toUint(Vm.AccountAccessKind.Call), "incorrect kind"); @@ -1083,12 +1080,12 @@ contract RecordAccountAccessesTest is DSTest { /// @notice Test that constructor calls and calls made within a constructor /// are correctly recorded, even if it reverts function testCreateRevert() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); bytes memory creationCode = abi.encodePacked(type(SelfCaller).creationCode, abi.encode("")); try create2or.create2(bytes32(0), creationCode) {} catch {} address hypotheticalAddress = deriveCreate2Address(address(create2or), bytes32(0), keccak256(creationCode)); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 3, "incorrect length"); assertEq( called[1], @@ -1140,7 +1137,7 @@ contract RecordAccountAccessesTest is DSTest { this.startRecordingFromLowerDepth(); address a = address(new SelfDestructor{value: 1 ether}(address(this))); address b = address(new SelfDestructor{value: 1 ether}(address(bytes20("doesn't exist yet")))); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 5, "incorrect length"); assertEq( called[1], @@ -1228,13 +1225,13 @@ contract RecordAccountAccessesTest is DSTest { /// @notice Asserts interaction between broadcast and recording cheatcodes function testIssue6514() public { - cheats.startStateDiffRecording(); - cheats.startBroadcast(); + vm.startStateDiffRecording(); + vm.startBroadcast(); StorageAccessor a = new StorageAccessor(); - cheats.stopBroadcast(); - Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(cheats.stopAndReturnStateDiff()); + vm.stopBroadcast(); + Vm.AccountAccess[] memory called = filterExtcodesizeForLegacyTests(vm.stopAndReturnStateDiff()); assertEq(called.length, 1, "incorrect length"); assertEq(toUint(called[0].kind), toUint(Vm.AccountAccessKind.Create)); assertEq(called[0].account, address(a)); @@ -1242,22 +1239,17 @@ contract RecordAccountAccessesTest is DSTest { /// @notice Test that EXT* opcodes are recorded as account accesses function testExtOpcodes() public { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); extChecker.checkExts(address(1234)); - Vm.AccountAccess[] memory called = cheats.stopAndReturnStateDiff(); - assertEq(called.length, 7, "incorrect length"); - // initial solidity extcodesize check for calling extChecker - assertEq(toUint(called[0].kind), toUint(Vm.AccountAccessKind.Extcodesize)); + Vm.AccountAccess[] memory called = vm.stopAndReturnStateDiff(); + assertEq(called.length, 5, "incorrect length"); // call to extChecker - assertEq(toUint(called[1].kind), toUint(Vm.AccountAccessKind.Call)); + assertEq(toUint(called[0].kind), toUint(Vm.AccountAccessKind.Call)); // extChecker checks - assertEq(toUint(called[2].kind), toUint(Vm.AccountAccessKind.Extcodesize)); - assertEq(toUint(called[3].kind), toUint(Vm.AccountAccessKind.Extcodehash)); - assertEq(toUint(called[4].kind), toUint(Vm.AccountAccessKind.Extcodecopy)); - assertEq(toUint(called[5].kind), toUint(Vm.AccountAccessKind.Balance)); - // resume of extChecker to hold SSTORE access - assertEq(toUint(called[6].kind), toUint(Vm.AccountAccessKind.Resume)); - assertEq(called[6].storageAccesses.length, 1, "incorrect length"); + assertEq(toUint(called[1].kind), toUint(Vm.AccountAccessKind.Extcodesize)); + assertEq(toUint(called[2].kind), toUint(Vm.AccountAccessKind.Extcodehash)); + assertEq(toUint(called[3].kind), toUint(Vm.AccountAccessKind.Extcodecopy)); + assertEq(toUint(called[4].kind), toUint(Vm.AccountAccessKind.Balance)); } /** @@ -1287,7 +1279,7 @@ contract RecordAccountAccessesTest is DSTest { } function startRecordingFromLowerDepth() external { - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); assembly { pop(call(gas(), 1234, 0, 0, 0, 0, 0)) } @@ -1411,7 +1403,7 @@ contract RecordAccountAccessesTest is DSTest { StorageAccessor accessor = test1; // Start recording to enable storage access tracking - cheats.startStateDiffRecording(); + vm.startStateDiffRecording(); // Perform a read operation accessor.read(bytes32(uint256(789))); @@ -1423,7 +1415,7 @@ contract RecordAccountAccessesTest is DSTest { accessor.read(bytes32(uint256(123))); // Get all storage accesses - Vm.StorageAccess[] memory accesses = cheats.getStorageAccesses(); + Vm.StorageAccess[] memory accesses = vm.getStorageAccesses(); // Check we have 3 storage accesses (2 reads + 1 write) assertEq(accesses.length, 3, "should have 3 storage accesses"); diff --git a/testdata/default/cheats/RecordDebugTrace.t.sol b/testdata/default/cheats/RecordDebugTrace.t.sol index f66f701cc7ab4..f7c0b895761e4 100644 --- a/testdata/default/cheats/RecordDebugTrace.t.sol +++ b/testdata/default/cheats/RecordDebugTrace.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity 0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract MStoreAndMLoadCaller { uint256 public constant expectedValueInMemory = 999; @@ -64,23 +63,21 @@ contract OutOfGas { } } -contract RecordDebugTraceTest is DSTest { - Vm constant cheats = Vm(HEVM_ADDRESS); +contract RecordDebugTraceTest is Test { /** * The goal of this test is to ensure the debug steps provide the correct OPCODE with its stack * and memory input used. The test checke MSTORE and MLOAD and ensure it records the expected * stack and memory inputs. */ - function testDebugTraceCanRecordOpcodeWithStackAndMemoryData() public { MStoreAndMLoadCaller testContract = new MStoreAndMLoadCaller(); - cheats.startDebugTraceRecording(); + vm.startDebugTraceRecording(); uint256 val = testContract.storeAndLoadValueFromMemory(); assertTrue(val == testContract.expectedValueInMemory()); - Vm.DebugStep[] memory steps = cheats.stopAndReturnDebugTraceRecording(); + Vm.DebugStep[] memory steps = vm.stopAndReturnDebugTraceRecording(); bool mstoreCalled = false; bool mloadCalled = false; @@ -118,11 +115,11 @@ contract RecordDebugTraceTest is DSTest { SecondLayer second = new SecondLayer(); FirstLayer first = new FirstLayer(second); - cheats.startDebugTraceRecording(); + vm.startDebugTraceRecording(); first.callSecondLayer(); - Vm.DebugStep[] memory steps = cheats.stopAndReturnDebugTraceRecording(); + Vm.DebugStep[] memory steps = vm.stopAndReturnDebugTraceRecording(); bool goToDepthTwo = false; bool goToDepthThree = false; @@ -149,11 +146,11 @@ contract RecordDebugTraceTest is DSTest { function testDebugTraceCanRecordOutOfGas() public { OutOfGas testContract = new OutOfGas(); - cheats.startDebugTraceRecording(); + vm.startDebugTraceRecording(); testContract.triggerOOG(); - Vm.DebugStep[] memory steps = cheats.stopAndReturnDebugTraceRecording(); + Vm.DebugStep[] memory steps = vm.stopAndReturnDebugTraceRecording(); bool isOOG = false; for (uint256 i = 0; i < steps.length; i++) { diff --git a/testdata/default/cheats/RecordLogs.t.sol b/testdata/default/cheats/RecordLogs.t.sol index 14ca8dde35e99..5927d5af23866 100644 --- a/testdata/default/cheats/RecordLogs.t.sol +++ b/testdata/default/cheats/RecordLogs.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Emitter { event LogAnonymous(bytes data) anonymous; @@ -48,8 +47,7 @@ contract Emitterv2 { } } -contract RecordLogsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract RecordLogsTest is Test { Emitter emitter; bytes32 internal seedTestData = keccak256(abi.encodePacked("Some data")); diff --git a/testdata/default/cheats/Remember.t.sol b/testdata/default/cheats/Remember.t.sol index b8dbe7e38007c..fdf00e3839f72 100644 --- a/testdata/default/cheats/Remember.t.sol +++ b/testdata/default/cheats/Remember.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RememberTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RememberTest is Test { function testRememberKey() public { string memory mnemonic = "test test test test test test test test test test test junk"; diff --git a/testdata/default/cheats/ResetNonce.t.sol b/testdata/default/cheats/ResetNonce.t.sol index d8c911587095c..53c8ec54b09c0 100644 --- a/testdata/default/cheats/ResetNonce.t.sol +++ b/testdata/default/cheats/ResetNonce.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Foo { function f() external view returns (uint256) { @@ -10,8 +9,7 @@ contract Foo { } } -contract ResetNonce is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract ResetNonce is Test { Foo public fooContract; address barEOA; diff --git a/testdata/default/cheats/Rlp.t.sol b/testdata/default/cheats/Rlp.t.sol index c0f10f362458d..24f2ec833de7a 100644 --- a/testdata/default/cheats/Rlp.t.sol +++ b/testdata/default/cheats/Rlp.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract Rlp is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract Rlp is Test { function testToRlp() public { bytes[] memory data = new bytes[](2); data[0] = hex"01"; diff --git a/testdata/default/cheats/Roll.t.sol b/testdata/default/cheats/Roll.t.sol index 0f26e3a431d7a..38e94b2bea267 100644 --- a/testdata/default/cheats/Roll.t.sol +++ b/testdata/default/cheats/Roll.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RollTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RollTest is Test { function testRoll() public { vm.roll(10); assertEq(block.number, 10, "roll failed"); diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index 86f4d33b1d41c..321b5b3cbbeed 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RpcUrlTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RpcUrlTest is Test { // returns the correct url function testCanGetRpcUrl() public { string memory url = vm.rpcUrl("mainnet"); diff --git a/testdata/default/cheats/Seed.t.sol b/testdata/default/cheats/Seed.t.sol index 6db1b41da0ef3..0909c38a26f72 100644 --- a/testdata/default/cheats/Seed.t.sol +++ b/testdata/default/cheats/Seed.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SeedTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SeedTest is Test { function testSeedAffectsRandom() public { // Use a known seed uint256 seed = 123456789; diff --git a/testdata/default/cheats/SetBlockhash.t.sol b/testdata/default/cheats/SetBlockhash.t.sol index 1274620df41a6..2897976d11e5e 100644 --- a/testdata/default/cheats/SetBlockhash.t.sol +++ b/testdata/default/cheats/SetBlockhash.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SetBlockhash is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SetBlockhash is Test { function testSetBlockhash() public { bytes32 blockHash = 0x1234567890123456789012345678901234567890123456789012345678901234; vm.setBlockhash(block.number - 1, blockHash); diff --git a/testdata/default/cheats/SetNonce.t.sol b/testdata/default/cheats/SetNonce.t.sol index e0fda6aaec688..19e8a366b92ca 100644 --- a/testdata/default/cheats/SetNonce.t.sol +++ b/testdata/default/cheats/SetNonce.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Foo { function f() external view returns (uint256) { @@ -10,8 +9,7 @@ contract Foo { } } -contract SetNonceTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract SetNonceTest is Test { Foo public foo; function setUp() public { diff --git a/testdata/default/cheats/SetNonceUnsafe.t.sol b/testdata/default/cheats/SetNonceUnsafe.t.sol index 0caf2b4ce7421..9f799f0829acc 100644 --- a/testdata/default/cheats/SetNonceUnsafe.t.sol +++ b/testdata/default/cheats/SetNonceUnsafe.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Foo { function f() external view returns (uint256) { @@ -10,8 +9,7 @@ contract Foo { } } -contract SetNonceTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract SetNonceTest is Test { Foo public foo; function setUp() public { diff --git a/testdata/default/cheats/Setup.t.sol b/testdata/default/cheats/Setup.t.sol index 4d6e5954b5fe1..9ce1682ab7a3c 100644 --- a/testdata/default/cheats/Setup.t.sol +++ b/testdata/default/cheats/Setup.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Victim { function assertSender(address sender) external { @@ -10,8 +9,7 @@ contract Victim { } } -contract VmSetupTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract VmSetupTest is Test { Victim victim; function setUp() public { diff --git a/testdata/default/cheats/Shuffle.t.sol b/testdata/default/cheats/Shuffle.t.sol index 565f596a6dcb5..5701fa7ab72c2 100644 --- a/testdata/default/cheats/Shuffle.t.sol +++ b/testdata/default/cheats/Shuffle.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ShuffleTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ShuffleTest is Test { function testDeterministicShuffle() public { // Use a known seed uint256 seed = 123456789; diff --git a/testdata/default/cheats/Sign.t.sol b/testdata/default/cheats/Sign.t.sol index b21a6050b76c1..ed0a9aa2e93de 100644 --- a/testdata/default/cheats/Sign.t.sol +++ b/testdata/default/cheats/Sign.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SignTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SignTest is Test { function testSignDigest(uint248 pk, bytes32 digest) public { vm.assume(pk != 0); diff --git a/testdata/default/cheats/SignP256.t.sol b/testdata/default/cheats/SignP256.t.sol index b92588ce9f823..d8d07acbf67b7 100644 --- a/testdata/default/cheats/SignP256.t.sol +++ b/testdata/default/cheats/SignP256.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SignTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SignTest is Test { function testSignP256() public { bytes32 pk = hex"A8568B74282DCC66FF70F10B4CE5CC7B391282F5381BBB4F4D8DD96974B16E6B"; bytes32 digest = hex"54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad"; diff --git a/testdata/default/cheats/Skip.t.sol b/testdata/default/cheats/Skip.t.sol index d7e75fa0f51af..0eff55b95db30 100644 --- a/testdata/default/cheats/Skip.t.sol +++ b/testdata/default/cheats/Skip.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SkipTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SkipTest is Test { function testSkip() public { vm.skip(true); revert("Should not reach this revert"); diff --git a/testdata/default/cheats/Sleep.t.sol b/testdata/default/cheats/Sleep.t.sol index 7af548e742573..b3268d41d3672 100644 --- a/testdata/default/cheats/Sleep.t.sol +++ b/testdata/default/cheats/Sleep.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SleepTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SleepTest is Test { function testSleep() public { uint256 milliseconds = 1234; diff --git a/testdata/default/cheats/Sort.t.sol b/testdata/default/cheats/Sort.t.sol index 2f557d54c2bbc..91991b572a081 100644 --- a/testdata/default/cheats/Sort.t.sol +++ b/testdata/default/cheats/Sort.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract SortTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract SortTest is Test { function testSortCheatcode() public { uint256[] memory numbers = new uint256[](3); numbers[0] = 3; diff --git a/testdata/default/cheats/StateDiffBytesString.t.sol b/testdata/default/cheats/StateDiffBytesString.t.sol index 31d37e9bd5b74..a61e3ee37f60d 100644 --- a/testdata/default/cheats/StateDiffBytesString.t.sol +++ b/testdata/default/cheats/StateDiffBytesString.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract BytesStringStorage { // Short string (less than 32 bytes) @@ -48,8 +47,7 @@ contract BytesStringStorage { } } -contract StateDiffBytesStringTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract StateDiffBytesStringTest is Test { BytesStringStorage bytesStringStorage; function setUp() public { diff --git a/testdata/default/cheats/StateDiffMappings.t.sol b/testdata/default/cheats/StateDiffMappings.t.sol index b4ed985791cde..45b53e0dfc5f0 100644 --- a/testdata/default/cheats/StateDiffMappings.t.sol +++ b/testdata/default/cheats/StateDiffMappings.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract MappingStorage { // Simple mappings only @@ -29,8 +28,7 @@ contract MappingStorage { } } -contract StateDiffMappingsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract StateDiffMappingsTest is Test { MappingStorage public mappingStorage; function setUp() public { diff --git a/testdata/default/cheats/StateDiffStorageLayout.t.sol b/testdata/default/cheats/StateDiffStorageLayout.t.sol index 027e3d8567098..298a8eb8bf236 100644 --- a/testdata/default/cheats/StateDiffStorageLayout.t.sol +++ b/testdata/default/cheats/StateDiffStorageLayout.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract SimpleStorage { uint256 public value; // Slot 0 @@ -97,8 +96,7 @@ contract TwoDArrayStorage { } } -contract StateDiffStorageLayoutTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract StateDiffStorageLayoutTest is Test { SimpleStorage simpleStorage; VariousArrays variousArrays; TwoDArrayStorage twoDArrayStorage; diff --git a/testdata/default/cheats/StateDiffStructTest.t.sol b/testdata/default/cheats/StateDiffStructTest.t.sol index a0b29f156c673..54e5403411dd4 100644 --- a/testdata/default/cheats/StateDiffStructTest.t.sol +++ b/testdata/default/cheats/StateDiffStructTest.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract DiffTest { // slot 0 @@ -49,8 +48,7 @@ contract DiffTest { } } -contract StateDiffStructTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract StateDiffStructTest is Test { DiffTest internal test; function setUp() public { diff --git a/testdata/default/cheats/StateSnapshots.t.sol b/testdata/default/cheats/StateSnapshots.t.sol index 8751a04094129..4a72639ce8c8d 100644 --- a/testdata/default/cheats/StateSnapshots.t.sol +++ b/testdata/default/cheats/StateSnapshots.t.sol @@ -1,17 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; struct Storage { uint256 slot0; uint256 slot1; } -contract StateSnapshotTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract StateSnapshotTest is Test { Storage store; function setUp() public { @@ -105,9 +102,7 @@ contract StateSnapshotTest is DSTest { } // TODO: remove this test suite once `snapshot*` has been deprecated in favor of `snapshotState*`. -contract DeprecatedStateSnapshotTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract DeprecatedStateSnapshotTest is Test { Storage store; function setUp() public { diff --git a/testdata/default/cheats/StorageSlotState.t.sol b/testdata/default/cheats/StorageSlotState.t.sol index 7c2971f22a14f..6c80900038558 100644 --- a/testdata/default/cheats/StorageSlotState.t.sol +++ b/testdata/default/cheats/StorageSlotState.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract StorageSlotStateTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract StorageSlotStateTest is Test { function test_gas_two_reads() public { Read read = new Read(); read.number(); diff --git a/testdata/default/cheats/Store.t.sol b/testdata/default/cheats/Store.t.sol index 9a1ce6101c1b0..e096dc3f50341 100644 --- a/testdata/default/cheats/Store.t.sol +++ b/testdata/default/cheats/Store.t.sol @@ -1,16 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Storage { uint256 public slot0 = 10; uint256 public slot1 = 20; } -contract StoreTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract StoreTest is Test { Storage store; function setUp() public { diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index 256d65302a445..fbe8718d99cc6 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract StringManipulationTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract StringManipulationTest is Test { function testToLowercase() public { string memory original = "Hello World"; string memory lowercased = vm.toLowercase(original); diff --git a/testdata/default/cheats/ToString.t.sol b/testdata/default/cheats/ToString.t.sol index f19110e3e8655..3fda38c4e5f1b 100644 --- a/testdata/default/cheats/ToString.t.sol +++ b/testdata/default/cheats/ToString.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ToStringTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ToStringTest is Test { function testAddressToString() public { address testAddress = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; string memory stringAddress = vm.toString(testAddress); diff --git a/testdata/default/cheats/Toml.t.sol b/testdata/default/cheats/Toml.t.sol index 9802389877ed2..613ec7c4046fb 100644 --- a/testdata/default/cheats/Toml.t.sol +++ b/testdata/default/cheats/Toml.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; library TomlStructs { address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); @@ -58,7 +56,7 @@ library TomlStructs { } } -contract ParseTomlTest is DSTest { +contract ParseTomlTest is Test { using TomlStructs for *; struct FlatToml { @@ -80,7 +78,6 @@ contract ParseTomlTest is DSTest { string name; } - Vm constant vm = Vm(HEVM_ADDRESS); string toml; function setUp() public { @@ -349,9 +346,7 @@ contract ParseTomlTest is DSTest { } } -contract WriteTomlTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract WriteTomlTest is Test { string json1; string json2; diff --git a/testdata/default/cheats/Travel.t.sol b/testdata/default/cheats/Travel.t.sol index b46d2e7ad7041..4937b679dc413 100644 --- a/testdata/default/cheats/Travel.t.sol +++ b/testdata/default/cheats/Travel.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ChainIdTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ChainIdTest is Test { function testChainId() public { vm.chainId(10); assertEq(block.chainid, 10, "chainId switch failed"); diff --git a/testdata/default/cheats/TryFfi.sol b/testdata/default/cheats/TryFfi.sol index 58d93a48b4fea..916377b6cc590 100644 --- a/testdata/default/cheats/TryFfi.sol +++ b/testdata/default/cheats/TryFfi.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract TryFfiTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract TryFfiTest is Test { function testTryFfi() public { string[] memory inputs = new string[](3); inputs[0] = "bash"; diff --git a/testdata/default/cheats/UnixTime.t.sol b/testdata/default/cheats/UnixTime.t.sol index 29d86699f64d1..6d11cdb3da02f 100644 --- a/testdata/default/cheats/UnixTime.t.sol +++ b/testdata/default/cheats/UnixTime.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract UnixTimeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract UnixTimeTest is Test { // This is really wide because CI sucks. uint256 constant errMargin = 1000; diff --git a/testdata/default/cheats/Wallet.t.sol b/testdata/default/cheats/Wallet.t.sol index d061b55ae45c5..e25ed4a9d071e 100644 --- a/testdata/default/cheats/Wallet.t.sol +++ b/testdata/default/cheats/Wallet.t.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Foo {} -contract WalletTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract WalletTest is Test { uint256 internal constant Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; uint256 private constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935; diff --git a/testdata/default/cheats/Warp.t.sol b/testdata/default/cheats/Warp.t.sol index 7ba53f6e5eda4..f3512944d6797 100644 --- a/testdata/default/cheats/Warp.t.sol +++ b/testdata/default/cheats/Warp.t.sol @@ -1,26 +1,23 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract WarpTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract WarpTest is Test { function testWarp() public { vm.warp(10); - assertEq(block.timestamp, 10, "warp failed"); + assertEq(vm.getBlockTimestamp(), 10, "warp failed"); } function testWarpFuzzed(uint32 jump) public { - uint256 pre = block.timestamp; - vm.warp(block.timestamp + jump); - assertEq(block.timestamp, pre + jump, "warp failed"); + uint256 pre = vm.getBlockTimestamp(); + vm.warp(vm.getBlockTimestamp() + jump); + assertEq(vm.getBlockTimestamp(), pre + jump, "warp failed"); } function testWarp2() public { - assertEq(block.timestamp, 1); + assertEq(vm.getBlockTimestamp(), 1); vm.warp(100); - assertEq(block.timestamp, 100); + assertEq(vm.getBlockTimestamp(), 100); } } diff --git a/testdata/default/cheats/dumpState.t.sol b/testdata/default/cheats/dumpState.t.sol index 8a8675ca5eace..887de131e5888 100644 --- a/testdata/default/cheats/dumpState.t.sol +++ b/testdata/default/cheats/dumpState.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract SimpleContract { constructor() { @@ -12,9 +11,7 @@ contract SimpleContract { } } -contract DumpStateTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract DumpStateTest is Test { function testDumpStateCheatAccount() public { // Path to temporary file that is deleted after the test string memory path = string.concat(vm.projectRoot(), "/fixtures/Json/test_dump_state_cheat.json"); diff --git a/testdata/default/cheats/getBlockNumber.t.sol b/testdata/default/cheats/getBlockNumber.t.sol index 18e2a163f3b86..491a726781b99 100644 --- a/testdata/default/cheats/getBlockNumber.t.sol +++ b/testdata/default/cheats/getBlockNumber.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GetBlockNumberTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GetBlockNumberTest is Test { function testGetBlockNumber() public { uint256 height = vm.getBlockNumber(); assertEq(height, uint256(block.number), "height should be equal to block.number"); diff --git a/testdata/default/cheats/loadAllocs.t.sol b/testdata/default/cheats/loadAllocs.t.sol index 94ce6804c1260..fa660e75fb775 100644 --- a/testdata/default/cheats/loadAllocs.t.sol +++ b/testdata/default/cheats/loadAllocs.t.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract LoadAllocsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract LoadAllocsTest is Test { address constant ALLOCD = address(0x420); address constant ALLOCD_B = address(0x421); diff --git a/testdata/default/core/BadSigAfterInvariant.t.sol b/testdata/default/core/BadSigAfterInvariant.t.sol index 7b485e24f4a04..1e2ce2f9a9448 100644 --- a/testdata/default/core/BadSigAfterInvariant.t.sol +++ b/testdata/default/core/BadSigAfterInvariant.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; -contract BadSigAfterInvariant is DSTest { +contract BadSigAfterInvariant is Test { function afterinvariant() public {} function testShouldPassWithWarning() public { diff --git a/testdata/default/core/ContractEnvironment.t.sol b/testdata/default/core/ContractEnvironment.t.sol index 452fa88022557..ddb03b41a2556 100644 --- a/testdata/default/core/ContractEnvironment.t.sol +++ b/testdata/default/core/ContractEnvironment.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; -contract ContractEnvironmentTest is DSTest { +contract ContractEnvironmentTest is Test { function chainId() internal view returns (uint256 id) { assembly { id := chainid() diff --git a/testdata/default/core/FailingTestAfterFailedSetup.t.sol b/testdata/default/core/FailingTestAfterFailedSetup.t.sol deleted file mode 100644 index c56f4ba5de605..0000000000000 --- a/testdata/default/core/FailingTestAfterFailedSetup.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract FailingTestAfterFailedSetupTest is DSTest { - function setUp() public { - assertTrue(false); - } - - function testAssertSuccess() public { - assertTrue(true); - } - - function testAssertFailure() public { - assertTrue(false); - } -} diff --git a/testdata/default/core/LegacyAssertions.t.sol b/testdata/default/core/LegacyAssertions.t.sol deleted file mode 100644 index c35a63417efc3..0000000000000 --- a/testdata/default/core/LegacyAssertions.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract NoAssertionsRevertTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - function testMultipleAssertFailures() public { - vm.assertEq(uint256(1), uint256(2)); - vm.assertLt(uint256(5), uint256(4)); - } -} - -contract LegacyAssertionsTest { - bool public failed; - - function testFlagNotSetSuccess() public {} - - function testFlagSetFailure() public { - failed = true; - } -} diff --git a/testdata/default/core/PaymentFailure.t.sol b/testdata/default/core/PaymentFailure.t.sol deleted file mode 100644 index 52c42fd376052..0000000000000 --- a/testdata/default/core/PaymentFailure.t.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract Payable { - function pay() public payable {} -} - -contract PaymentFailureTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - function testCantPay() public { - Payable target = new Payable(); - vm.prank(address(1)); - target.pay{value: 1}(); - } -} diff --git a/testdata/default/core/Reverting.t.sol b/testdata/default/core/Reverting.t.sol index 73877cab0b542..699b2bf1123d3 100644 --- a/testdata/default/core/Reverting.t.sol +++ b/testdata/default/core/Reverting.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RevertingTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract RevertingTest is Test { /// forge-config: default.allow_internal_expect_revert = true function testRevert() public { vm.expectRevert("should revert here"); diff --git a/testdata/default/core/SetupConsistency.t.sol b/testdata/default/core/SetupConsistency.t.sol index 08d766f0f9242..bf5a2e31935c1 100644 --- a/testdata/default/core/SetupConsistency.t.sol +++ b/testdata/default/core/SetupConsistency.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; -contract SetupConsistencyCheck is DSTest { +contract SetupConsistencyCheck is Test { uint256 two; uint256 four; uint256 result; diff --git a/testdata/default/fork/ForkSame_1.t.sol b/testdata/default/fork/ForkSame_1.t.sol index 949c7ea9ec17d..f8c0234f5baa6 100644 --- a/testdata/default/fork/ForkSame_1.t.sol +++ b/testdata/default/fork/ForkSame_1.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract ForkTest is DSTest { +contract ForkTest is Test { address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - Vm constant vm = Vm(HEVM_ADDRESS); uint256 forkA; // this will create two _different_ forks during setup diff --git a/testdata/default/fork/ForkSame_2.t.sol b/testdata/default/fork/ForkSame_2.t.sol index 949c7ea9ec17d..f8c0234f5baa6 100644 --- a/testdata/default/fork/ForkSame_2.t.sol +++ b/testdata/default/fork/ForkSame_2.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract ForkTest is DSTest { +contract ForkTest is Test { address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - Vm constant vm = Vm(HEVM_ADDRESS); uint256 forkA; // this will create two _different_ forks during setup diff --git a/testdata/default/fork/LaunchFork.t.sol b/testdata/default/fork/LaunchFork.t.sol index 710ac97f51d97..8784b84b8e7e1 100644 --- a/testdata/default/fork/LaunchFork.t.sol +++ b/testdata/default/fork/LaunchFork.t.sol @@ -1,14 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.6.12; -import "ds-test/test.sol"; +import "utils/Test.sol"; import "./DssExecLib.sol"; -interface Vm { - function store(address account, bytes32 slot, bytes32 value) external; - function activeFork() external returns (uint256); -} - interface IWETH { function deposit() external payable; function balanceOf(address) external view returns (uint256); @@ -23,14 +18,13 @@ contract DummyContract { } } -contract ForkTest is DSTest { +abstract contract ForkTest is Test { address constant DAI_TOKEN_ADDR = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // checks that we can retrieve the fork we launched with function testActiveFork() public { - Vm cheatvm = Vm(HEVM_ADDRESS); - uint256 activeFork = cheatvm.activeFork(); + uint256 activeFork = vm.activeFork(); // launch fork has id `0` assertEq(activeFork, 0); } @@ -52,13 +46,12 @@ contract ForkTest is DSTest { } function testCheatcode() public { - Vm cheatvm = Vm(HEVM_ADDRESS); IWETH WETH = IWETH(WETH_TOKEN_ADDR); bytes32 value = bytes32(uint256(1)); // "0x3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff" is the slot storing the balance of zero address for the weth contract // `cast index address uint 0x0000000000000000000000000000000000000000 3` bytes32 zero_address_balance_slot = 0x3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff; - cheatvm.store(WETH_TOKEN_ADDR, zero_address_balance_slot, value); + vm.store(WETH_TOKEN_ADDR, zero_address_balance_slot, value); assertEq( WETH.balanceOf(0x0000000000000000000000000000000000000000), 1, @@ -77,3 +70,9 @@ contract ForkTest is DSTest { assertEq(WETH.balanceOf(address(this)) - current, 1000, "WETH balance is not equal to deposited amount."); } } + +contract ForkTestHttp is ForkTest { + function setUp() public { + vm.createSelectFork("mainnet"); + } +} diff --git a/testdata/default/fs/Disabled.t.sol b/testdata/default/fs/Disabled.t.sol index 36f05c211fcad..19fb755c4f425 100644 --- a/testdata/default/fs/Disabled.t.sol +++ b/testdata/default/fs/Disabled.t.sol @@ -1,16 +1,19 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract DisabledTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +// No permissions: all FS operations should revert. +/// forge-config: default.fs_permissions = [] +contract DisabledAccessTest is Test { function testReadFile() public { string memory path = "fixtures/File/read.txt"; vm._expectCheatcodeRevert(); vm.readFile(path); + + vm._expectCheatcodeRevert(); + vm.readFileBinary(path); } function testReadLine() public { @@ -20,21 +23,27 @@ contract DisabledTest is DSTest { } function testWriteFile() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; string memory data = "hello writable world"; + vm._expectCheatcodeRevert(); vm.writeFile(path, data); + + vm._expectCheatcodeRevert(); + vm.writeFileBinary(path, bytes(data)); } function testWriteLine() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; string memory data = "hello writable world"; + vm._expectCheatcodeRevert(); vm.writeLine(path, data); } function testRemoveFile() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; + vm._expectCheatcodeRevert(); vm.removeFile(path); } diff --git a/testdata/default/fs/Default.t.sol b/testdata/default/fs/ReadOnly.sol similarity index 68% rename from testdata/default/fs/Default.t.sol rename to testdata/default/fs/ReadOnly.sol index e1524963f6c3e..a738ae20e61c4 100644 --- a/testdata/default/fs/Default.t.sol +++ b/testdata/default/fs/ReadOnly.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract DefaultAccessTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +// Default permissions: only read FS operations should succeed. +/// forge-config: default.fs_permissions = [{ access = "read", path = "./fixtures"}] +contract ReadOnlyAccessTest is Test { function testReadFile() public { string memory path = "fixtures/File/read.txt"; vm.readFile(path); @@ -20,7 +20,7 @@ contract DefaultAccessTest is DSTest { } function testWriteFile() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; string memory data = "hello writable world"; vm._expectCheatcodeRevert(); @@ -31,7 +31,7 @@ contract DefaultAccessTest is DSTest { } function testWriteLine() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; string memory data = "hello writable world"; vm._expectCheatcodeRevert(); @@ -39,7 +39,7 @@ contract DefaultAccessTest is DSTest { } function testRemoveFile() public { - string memory path = "fixtures/File/write_file.txt"; + string memory path = "fixtures/File/ignored/write_file.txt"; vm._expectCheatcodeRevert(); vm.removeFile(path); diff --git a/testdata/default/fuzz/Fuzz.t.sol b/testdata/default/fuzz/Fuzz.t.sol deleted file mode 100644 index b1cf54716be93..0000000000000 --- a/testdata/default/fuzz/Fuzz.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract FuzzTest is DSTest { - constructor() { - emit log("constructor"); - } - - Vm constant vm = Vm(HEVM_ADDRESS); - - function setUp() public { - emit log("setUp"); - } - - function testShouldFailFuzz(uint8 x) public { - emit log("testFailFuzz"); - require(x > 128, "should revert"); - } - - function testSuccessfulFuzz(uint128 a, uint128 b) public { - emit log("testSuccessfulFuzz"); - assertEq(uint256(a) + uint256(b), uint256(a) + uint256(b)); - } - - function testToStringFuzz(bytes32 data) public { - vm.toString(data); - } -} diff --git a/testdata/default/fuzz/FuzzCollection.t.sol b/testdata/default/fuzz/FuzzCollection.t.sol deleted file mode 100644 index 0c98ddc66b6b2..0000000000000 --- a/testdata/default/fuzz/FuzzCollection.t.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract SampleContract { - uint256 public counter; - uint256 public counterX2; - address public owner = address(0xBEEF); - bool public found_needle; - - event Incremented(uint256 counter); - - modifier onlyOwner() { - require(msg.sender == owner, "ONLY_OWNER"); - _; - } - - function compare(uint256 val) public { - if (val == 0x4446) { - found_needle = true; - } - } - - function incrementBy(uint256 numToIncrement) public onlyOwner { - counter += numToIncrement; - counterX2 += numToIncrement * 2; - - emit Incremented(counter); - } - - function breakTheInvariant(uint256 x) public { - if (x == 0x5556) { - counterX2 = 0; - } - } -} - -interface Vm { - function startPrank(address) external; - function expectRevert(bytes calldata msg) external; -} - -contract SampleContractTest is DSTest { - Vm hevm = Vm(HEVM_ADDRESS); - - event Incremented(uint256 counter); - - SampleContract public sample; - - function setUp() public { - sample = new SampleContract(); - } - - function testIncrement(address caller) public { - hevm.startPrank(address(caller)); - - hevm.expectRevert("ONLY_OWNER"); - sample.incrementBy(1); - } - - function testNeedle(uint256 needle) public { - sample.compare(needle); - require(!sample.found_needle(), "needle found."); - } - - function invariantCounter() public { - require(sample.counter() * 2 == sample.counterX2(), "broken counter."); - } -} diff --git a/testdata/default/fuzz/FuzzFailurePersist.t.sol b/testdata/default/fuzz/FuzzFailurePersist.t.sol deleted file mode 100644 index 3787060411e5d..0000000000000 --- a/testdata/default/fuzz/FuzzFailurePersist.t.sol +++ /dev/null @@ -1,29 +0,0 @@ -// 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); - } -} diff --git a/testdata/default/fuzz/FuzzInt.t.sol b/testdata/default/fuzz/FuzzInt.t.sol deleted file mode 100644 index a47ff2953331f..0000000000000 --- a/testdata/default/fuzz/FuzzInt.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -// 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); - } - - function testNegativeHalf(int256 val) public { - assertTrue(val < 2 ** 128 - 1); - } - - function testNegative0(int256 val) public { - assertTrue(val == 0); - } - - function testNegative1(int256 val) public { - assertTrue(val == -1); - } - - function testNegative2(int128 val) public { - assertTrue(val == 1); - } - - function testNegativeMax0(int256 val) public { - assertTrue(val == type(int256).max); - } - - function testNegativeMax1(int256 val) public { - assertTrue(val == type(int256).max - 2); - } - - function testNegativeMin0(int256 val) public { - assertTrue(val == type(int256).min); - } - - function testNegativeMin1(int256 val) public { - assertTrue(val == type(int256).min + 2); - } - - function testEquality(int256 x, int256 y) public { - int256 xy; - - unchecked { - xy = x * y; - } - - if ((x != 0 && xy / x != y)) { - return; - } - - assertEq(((xy - 1) / 1e18) + 1, (xy - 1) / (1e18 + 1)); - } -} diff --git a/testdata/default/fuzz/FuzzPositive.t.sol b/testdata/default/fuzz/FuzzPositive.t.sol deleted file mode 100644 index 7d3639dfe5ec2..0000000000000 --- a/testdata/default/fuzz/FuzzPositive.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract FuzzPositive is DSTest { - function testSuccessChecker(uint256 val) public { - assertTrue(true); - } - - function testSuccessChecker2(int256 val) public { - assert(val == val); - } - - function testSuccessChecker3(uint32 val) public { - assert(val + 0 == val); - } -} diff --git a/testdata/default/fuzz/FuzzUint.t.sol b/testdata/default/fuzz/FuzzUint.t.sol deleted file mode 100644 index c0cbf6466c31b..0000000000000 --- a/testdata/default/fuzz/FuzzUint.t.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -// 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); - } - - function testNegativeHalf(uint256 val) public { - assertTrue(val < 2 ** 128 - 1); - } - - function testNegative0(uint256 val) public { - assertTrue(val == 0); - } - - function testNegative2(uint256 val) public { - assertTrue(val == 2); - } - - function testNegative2Max(uint256 val) public { - assertTrue(val == type(uint256).max - 2); - } - - function testNegativeMax(uint256 val) public { - assertTrue(val == type(uint256).max); - } - - function testEquality(uint256 x, uint256 y) public { - uint256 xy; - - unchecked { - xy = x * y; - } - - if ((x != 0 && xy / x != y)) { - return; - } - - assertEq(((xy - 1) / 1e18) + 1, (xy - 1) / (1e18 + 1)); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol b/testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol deleted file mode 100644 index 3030b43e077cc..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol +++ /dev/null @@ -1,55 +0,0 @@ -// 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"); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol b/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol deleted file mode 100644 index 9808a870f7228..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol +++ /dev/null @@ -1,23 +0,0 @@ -// 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 {} -} diff --git a/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol deleted file mode 100644 index 3d4c51eac41e5..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol +++ /dev/null @@ -1,95 +0,0 @@ -// 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; - 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) { - 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 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/InvariantCustomError.t.sol b/testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol deleted file mode 100644 index 737cf5ba9dd05..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// 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 {} -} diff --git a/testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol b/testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol deleted file mode 100644 index 8fe0bed2c6e7c..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol +++ /dev/null @@ -1,22 +0,0 @@ -// 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 {} -} diff --git a/testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol b/testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol deleted file mode 100644 index b3f1e17cb2497..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol +++ /dev/null @@ -1,77 +0,0 @@ -// 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); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantHandlerFailure.t.sol b/testdata/default/fuzz/invariant/common/InvariantHandlerFailure.t.sol deleted file mode 100644 index 5ff50e782ac55..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantHandlerFailure.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.0; - -import "ds-test/test.sol"; - -struct FuzzSelector { - address addr; - bytes4[] selectors; -} - -contract Handler is DSTest { - function doSomething() public { - require(false, "failed on revert"); - } -} - -contract InvariantHandlerFailure is DSTest { - bytes4[] internal selectors; - - Handler handler; - - function targetSelectors() public returns (FuzzSelector[] memory) { - FuzzSelector[] memory targets = new FuzzSelector[](1); - bytes4[] memory selectors = new bytes4[](1); - selectors[0] = handler.doSomething.selector; - targets[0] = FuzzSelector(address(handler), selectors); - return targets; - } - - function setUp() public { - handler = new Handler(); - } - - function statefulFuzz_BrokenInvariant() public {} -} diff --git a/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol b/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol deleted file mode 100644 index 817e65d179f81..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -/*////////////////////////////////////////////////////////////// - Here we test that the fuzz engine can include a contract created during the fuzz - in its fuzz dictionary and eventually break the invariant. - Specifically, can Judas, a created contract from Jesus, break Jesus contract - by revealing his identity. -/*/ -///////////////////////////////////////////////////////////// - -contract Jesus { - address fren; - bool public identity_revealed; - - function create_fren() public { - fren = address(new Judas()); - } - - function kiss() public { - require(msg.sender == fren); - identity_revealed = true; - } -} - -contract Judas { - Jesus jesus; - - constructor() { - jesus = Jesus(msg.sender); - } - - function betray() public { - jesus.kiss(); - } -} - -contract InvariantInnerContract is DSTest { - Jesus jesus; - - function setUp() public { - jesus = new Jesus(); - } - - function invariantHideJesus() public { - require(jesus.identity_revealed() == false, "jesus betrayed"); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol deleted file mode 100644 index bd70dd3aeafba..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol +++ /dev/null @@ -1,49 +0,0 @@ -// 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); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol b/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol deleted file mode 100644 index 74a01f1805de6..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract Malicious { - function world() public { - // add code so contract is accounted as valid sender - // see https://github.com/foundry-rs/foundry/issues/4245 - payable(msg.sender).call(""); - } -} - -contract Vulnerable { - bool public open_door = false; - bool public stolen = false; - Malicious mal; - - constructor(address _mal) { - mal = Malicious(_mal); - } - - function hello() public { - open_door = true; - mal.world(); - open_door = false; - } - - function backdoor() public { - require(open_door, ""); - stolen = true; - } -} - -contract InvariantReentrancy is DSTest { - Vulnerable vuln; - Malicious mal; - - function setUp() public { - mal = new Malicious(); - 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"); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol deleted file mode 100644 index d15619b635f18..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -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 InvariantRollForkBlockTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - RollForkHandler forkHandler; - - function setUp() public { - vm.createSelectFork("mainnet", 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"); - } -} - -contract InvariantRollForkStateTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - RollForkHandler forkHandler; - - function setUp() public { - vm.createSelectFork("mainnet", 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/fuzz/invariant/common/InvariantScrapeValues.t.sol b/testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol deleted file mode 100644 index 40824a2602e77..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol +++ /dev/null @@ -1,69 +0,0 @@ -// 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"); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantSequenceNoReverts.t.sol b/testdata/default/fuzz/invariant/common/InvariantSequenceNoReverts.t.sol deleted file mode 100644 index 993d806f81b38..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantSequenceNoReverts.t.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "ds-test/test.sol"; - -contract SequenceNoReverts { - uint256 public count; - - function work(uint256 x) public { - require(x % 2 != 0); - count++; - } -} - -contract SequenceNoRevertsTest is DSTest { - SequenceNoReverts target; - - function setUp() public { - target = new SequenceNoReverts(); - } - - function invariant_no_reverts() public view { - require(target.count() < 10, "condition met"); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol deleted file mode 100644 index 5699d9c455e89..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// 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(); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol deleted file mode 100644 index d971367b69988..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol +++ /dev/null @@ -1,26 +0,0 @@ -// 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 {} -} diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol deleted file mode 100644 index fa4a6e945804e..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "ds-test/test.sol"; - -contract Counter { - uint256 public number; - - function increment() public { - number++; - } - - function decrement() public { - number--; - } -} - -contract InvariantShrinkWithAssert is DSTest { - Counter public counter; - - function setUp() public { - counter = new Counter(); - } - - function invariant_with_assert() public { - assertTrue(counter.number() < 2, "wrong counter"); - } - - function invariant_with_require() public { - require(counter.number() < 2, "wrong counter"); - } -} diff --git a/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol b/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol deleted file mode 100644 index bb62f34c6a965..0000000000000 --- a/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract InvariantBreaker { - bool public flag0 = true; - bool public flag1 = true; - - function set0(int256 val) public returns (bool) { - if (val % 100 == 0) { - flag0 = false; - } - return flag0; - } - - function set1(int256 val) public returns (bool) { - if (val % 10 == 0 && !flag0) { - flag1 = false; - } - return flag1; - } -} - -contract InvariantTest is DSTest { - InvariantBreaker inv; - - function setUp() public { - inv = new InvariantBreaker(); - } - - function invariant_neverFalse() public { - require(inv.flag1(), "false"); - } - - function statefulFuzz_neverFalseWithInvariantAlias() public { - require(inv.flag1(), "false"); - } -} diff --git a/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol b/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol deleted file mode 100644 index e2e850e316d1e..0000000000000 --- a/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract Hello { - bool public world = true; - - function change() public { - world = false; - } -} - -contract ExcludeContracts is DSTest { - Hello hello; - - function setUp() public { - hello = new Hello(); - new Hello(); - } - - function excludeContracts() public returns (address[] memory) { - address[] memory addrs = new address[](1); - addrs[0] = address(hello); - return addrs; - } - - function invariantTrueWorld() public { - require(hello.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol b/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol deleted file mode 100644 index e2251f42c8126..0000000000000 --- a/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol +++ /dev/null @@ -1,41 +0,0 @@ -// 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"); - } -} diff --git a/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol b/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol deleted file mode 100644 index dda07074d18c7..0000000000000 --- a/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract Hello { - address seed_address = address(0xdeadbeef); - bool public world = true; - - function changeBeef() public { - require(msg.sender == address(0xdeadbeef)); - world = false; - } - - // address(0) should be automatically excluded - function change0() public { - require(msg.sender == address(0)); - world = false; - } -} - -contract ExcludeSenders is DSTest { - Hello hello; - - function setUp() public { - hello = new Hello(); - } - - function excludeSenders() public returns (address[] memory) { - address[] memory addrs = new address[](1); - addrs[0] = address(0xdeadbeef); - return addrs; - } - - // Tests clashing. Exclusion takes priority. - function targetSenders() public returns (address[] memory) { - address[] memory addrs = new address[](1); - addrs[0] = address(0xdeadbeef); - return addrs; - } - - function invariantTrueWorld() public { - require(hello.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol b/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol deleted file mode 100644 index 759810611e95b..0000000000000 --- a/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol +++ /dev/null @@ -1,66 +0,0 @@ -// 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 {} -} diff --git a/testdata/default/fuzz/invariant/target/TargetContracts.t.sol b/testdata/default/fuzz/invariant/target/TargetContracts.t.sol deleted file mode 100644 index d24c7eb5282a4..0000000000000 --- a/testdata/default/fuzz/invariant/target/TargetContracts.t.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract Hello { - bool public world = true; - - function change() public { - world = false; - } -} - -contract TargetContracts is DSTest { - Hello hello1; - Hello hello2; - - function setUp() public { - hello1 = new Hello(); - hello2 = new Hello(); - } - - function targetContracts() public returns (address[] memory) { - address[] memory addrs = new address[](1); - addrs[0] = address(hello1); - return addrs; - } - - function invariantTrueWorld() public { - require(hello2.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol b/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol deleted file mode 100644 index 30b4a05e3eaf9..0000000000000 --- a/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -struct FuzzInterface { - address target; - string[] artifacts; -} - -contract Hello { - bool public world; - - function changeWorld() external { - world = true; - } -} - -interface IHello { - function world() external view returns (bool); - function changeWorld() external; -} - -contract HelloProxy { - address internal immutable _implementation; - - constructor(address implementation_) { - _implementation = implementation_; - } - - function _delegate(address implementation) internal { - assembly { - calldatacopy(0, 0, calldatasize()) - - let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) - - returndatacopy(0, 0, returndatasize()) - - switch result - case 0 { revert(0, returndatasize()) } - default { return(0, returndatasize()) } - } - } - - fallback() external payable { - _delegate(_implementation); - } -} - -contract TargetWorldInterfaces is DSTest { - IHello proxy; - - function setUp() public { - Hello hello = new Hello(); - proxy = IHello(address(new HelloProxy(address(hello)))); - } - - function targetInterfaces() public returns (FuzzInterface[] memory) { - FuzzInterface[] memory targets = new FuzzInterface[](1); - - string[] memory artifacts = new string[](1); - artifacts[0] = "IHello"; - - targets[0] = FuzzInterface(address(proxy), artifacts); - - return targets; - } - - function invariantTrueWorld() public { - require(proxy.world() == false, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol b/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol deleted file mode 100644 index c74ac7fa18114..0000000000000 --- a/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol +++ /dev/null @@ -1,41 +0,0 @@ -// 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 = true; - - function change() public { - world = true; - } - - function real_change() public { - world = false; - } -} - -contract TargetSelectors is DSTest { - Hello hello; - - function setUp() public { - hello = new Hello(); - } - - function targetSelectors() 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 invariantTrueWorld() public { - require(hello.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/target/TargetSenders.t.sol b/testdata/default/fuzz/invariant/target/TargetSenders.t.sol deleted file mode 100644 index 6fa4c9a6387d5..0000000000000 --- a/testdata/default/fuzz/invariant/target/TargetSenders.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract Hello { - bool public world = true; - - function change() public { - require(msg.sender == address(0xdeadbeef)); - world = false; - } -} - -contract TargetSenders is DSTest { - Hello hello; - - function setUp() public { - hello = new Hello(); - } - - function targetSenders() public returns (address[] memory) { - address[] memory addrs = new address[](1); - addrs[0] = address(0xdeadbeef); - return addrs; - } - - function invariantTrueWorld() public { - require(hello.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol b/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol deleted file mode 100644 index 86ca6d5439b7a..0000000000000 --- a/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -// Will get automatically excluded. Otherwise it would throw error. -contract NoMutFunctions { - function no_change() public pure {} -} - -contract Excluded { - bool public world = true; - - function change() public { - world = false; - } -} - -contract Hello { - bool public world = true; - - function change() public { - world = false; - } -} - -contract ExcludeArtifacts is DSTest { - Excluded excluded; - - function setUp() public { - excluded = new Excluded(); - new Hello(); - new NoMutFunctions(); - } - - function excludeArtifacts() public returns (string[] memory) { - string[] memory abis = new string[](1); - abis[0] = "default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:Excluded"; - return abis; - } - - function invariantShouldPass() public { - require(excluded.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol deleted file mode 100644 index a8d9c2799486d..0000000000000 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -struct FuzzArtifactSelector { - string artifact; - bytes4[] selectors; -} - -contract Hi { - bool public world = true; - - function no_change() public { - world = true; - } - - function change() public { - world = false; - } -} - -contract TargetArtifactSelectors is DSTest { - Hi hello; - - function setUp() public { - hello = new Hi(); - } - - 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] = - FuzzArtifactSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:Hi", selectors); - return targets; - } - - function invariantShouldPass() public { - require(hello.world() == true, "false world"); - } -} diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol deleted file mode 100644 index 162d9cc2e1106..0000000000000 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -struct FuzzArtifactSelector { - string artifact; - bytes4[] selectors; -} - -contract Parent { - bool public should_be_true = true; - address public child; - - function change() public { - child = msg.sender; - should_be_true = false; - } - - function create() public { - new Child(); - } -} - -contract Child { - Parent parent; - bool public changed = false; - - constructor() { - parent = Parent(msg.sender); - } - - function change_parent() public { - parent.change(); - } - - function tracked_change_parent() public { - parent.change(); - } -} - -contract TargetArtifactSelectors2 is DSTest { - Parent parent; - - function setUp() public { - parent = new Parent(); - } - - 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] = 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] = FuzzArtifactSelector( - "default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Parent", selectors_parent - ); - return targets; - } - - function invariantShouldFail() public { - if (!parent.should_be_true()) { - require(!Child(address(parent.child())).changed(), "should have not happened"); - } - require(parent.should_be_true() == true, "it's false"); - } -} diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol deleted file mode 100644 index 28fa146059f92..0000000000000 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; - -contract Targeted { - bool public world = true; - - function change() public { - world = false; - } -} - -contract Hello { - bool public world = true; - - function no_change() public {} -} - -contract TargetArtifacts is DSTest { - Targeted target1; - Targeted target2; - Hello hello; - - function setUp() public { - target1 = new Targeted(); - target2 = new Targeted(); - hello = new Hello(); - } - - function targetArtifacts() public returns (string[] memory) { - string[] memory abis = new string[](1); - abis[0] = "default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol:Targeted"; - return abis; - } - - function invariantShouldPass() public { - require(target2.world() == true || target1.world() == true || hello.world() == true, "false world"); - } - - function invariantShouldFail() public { - require(target2.world() == true || target1.world() == true, "false world"); - } -} diff --git a/testdata/default/inline/FuzzInlineConf.t.sol b/testdata/default/inline/FuzzInlineConf.t.sol index 73d2de2fc4ed1..24c059e935be7 100644 --- a/testdata/default/inline/FuzzInlineConf.t.sol +++ b/testdata/default/inline/FuzzInlineConf.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; -contract FuzzInlineConf is DSTest { +contract FuzzInlineConf is Test { /** * forge-config: default.fuzz.runs = 1024 * forge-config: default.fuzz.max-test-rejects = 500 @@ -14,7 +14,7 @@ contract FuzzInlineConf is DSTest { } /// forge-config: default.fuzz.runs = 10 -contract FuzzInlineConf2 is DSTest { +contract FuzzInlineConf2 is Test { /// forge-config: default.fuzz.runs = 1 function testInlineConfFuzz1(uint8 x) public { require(true, "this is not going to revert"); diff --git a/testdata/default/inline/InvariantInlineConf.t.sol b/testdata/default/inline/InvariantInlineConf.t.sol index 5ac81755e998d..6699008a343ac 100644 --- a/testdata/default/inline/InvariantInlineConf.t.sol +++ b/testdata/default/inline/InvariantInlineConf.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; contract InvariantBreaker { bool public flag0 = true; @@ -22,7 +22,7 @@ contract InvariantBreaker { } } -contract InvariantInlineConf is DSTest { +contract InvariantInlineConf is Test { InvariantBreaker inv; function setUp() public { @@ -38,7 +38,7 @@ contract InvariantInlineConf is DSTest { } } -contract InvariantInlineConf2 is DSTest { +contract InvariantInlineConf2 is Test { InvariantBreaker inv; function setUp() public { diff --git a/testdata/default/linking/duplicate/Duplicate.t.sol b/testdata/default/linking/duplicate/Duplicate.t.sol index d1d0f32789238..2ad1d9f7fbe5b 100644 --- a/testdata/default/linking/duplicate/Duplicate.t.sol +++ b/testdata/default/linking/duplicate/Duplicate.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; // Linking scenario: contract has many dependencies, some of which appear to the linker // more than once. @@ -93,7 +93,7 @@ contract LibraryConsumer { } } -contract DuplicateLibraryLinkingTest is DSTest { +contract DuplicateLibraryLinkingTest is Test { LibraryConsumer consumer; function setUp() public { diff --git a/testdata/default/linking/nested/Nested.t.sol b/testdata/default/linking/nested/Nested.t.sol index 136cb36479cbf..709bf0143857d 100644 --- a/testdata/default/linking/nested/Nested.t.sol +++ b/testdata/default/linking/nested/Nested.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; // Linking scenario: contract with a library that depends on a library @@ -27,7 +27,7 @@ contract LibraryConsumer { } } -contract NestedLibraryLinkingTest is DSTest { +contract NestedLibraryLinkingTest is Test { LibraryConsumer consumer; function setUp() public { diff --git a/testdata/default/linking/simple/Simple.t.sol b/testdata/default/linking/simple/Simple.t.sol index 85be791fd2489..ded3efb1694c8 100644 --- a/testdata/default/linking/simple/Simple.t.sol +++ b/testdata/default/linking/simple/Simple.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; // Linking scenario: contract with one library @@ -17,7 +17,7 @@ contract LibraryConsumer { } } -contract SimpleLibraryLinkingTest is DSTest { +contract SimpleLibraryLinkingTest is Test { LibraryConsumer consumer; function setUp() public { diff --git a/testdata/default/logs/DebugLogs.t.sol b/testdata/default/logs/DebugLogs.t.sol deleted file mode 100644 index b560fd2bfb9ca..0000000000000 --- a/testdata/default/logs/DebugLogs.t.sol +++ /dev/null @@ -1,105 +0,0 @@ -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract DebugLogsTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - constructor() { - emit log_uint(0); - } - - function setUp() public { - emit log_uint(1); - } - - function test1() public { - emit log_uint(2); - } - - function test2() public { - emit log_uint(3); - } - - function testRevertIfWithRevert() public { - Fails fails = new Fails(); - emit log_uint(4); - vm.expectRevert(); - fails.failure(); - } - - /// forge-config: default.allow_internal_expect_revert = true - function testRevertIfWithRequire() public { - emit log_uint(5); - vm.expectRevert(); - require(false); - } - - function testLog() public { - emit log("Error: Assertion Failed"); - } - - function testLogs() public { - emit logs(bytes("abcd")); - } - - function testLogAddress() public { - emit log_address(address(1)); - } - - function testLogBytes32() public { - emit log_bytes32(bytes32("abcd")); - } - - function testLogInt() public { - emit log_int(int256(-31337)); - } - - function testLogBytes() public { - emit log_bytes(bytes("abcd")); - } - - function testLogString() public { - emit log_string("here"); - } - - function testLogNamedAddress() public { - emit log_named_address("address", address(1)); - } - - function testLogNamedBytes32() public { - emit log_named_bytes32("abcd", bytes32("abcd")); - } - - function testLogNamedDecimalInt() public { - emit log_named_decimal_int("amount", int256(-31337), uint256(18)); - } - - function testLogNamedDecimalUint() public { - emit log_named_decimal_uint("amount", uint256(1 ether), uint256(18)); - } - - function testLogNamedInt() public { - emit log_named_int("amount", int256(-31337)); - } - - function testLogNamedUint() public { - emit log_named_uint("amount", uint256(1 ether)); - } - - function testLogNamedBytes() public { - emit log_named_bytes("abcd", bytes("abcd")); - } - - function testLogNamedString() public { - emit log_named_string("key", "val"); - } -} - -contract Fails is DSTest { - function failure() public { - emit log_uint(100); - revert(); - } -} diff --git a/testdata/default/logs/HardhatLogs.t.sol b/testdata/default/logs/HardhatLogs.t.sol deleted file mode 100644 index a6226bbd665fe..0000000000000 --- a/testdata/default/logs/HardhatLogs.t.sol +++ /dev/null @@ -1,238 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "./console.sol"; - -contract HardhatLogsTest { - constructor() { - console.log("constructor"); - } - - string testStr; - int256 testInt; - uint256 testUint; - bool testBool; - address testAddr; - bytes testBytes; - - function setUp() public { - testStr = "test"; - testInt = -31337; - testUint = 1; - testBool = false; - testAddr = 0x0000000000000000000000000000000000000001; - testBytes = "a"; - } - - function testInts() public view { - console.log(uint256(0)); - console.log(uint256(1)); - console.log(uint256(2)); - console.log(uint256(3)); - } - - function testStrings() public view { - console.log("testStrings"); - } - - function testMisc() public view { - console.log("testMisc", address(1)); - console.log("testMisc", uint256(42)); - } - - function testConsoleLog() public view { - console.log(testStr); - } - - function testLogInt() public view { - console.logInt(testInt); - } - - function testLogUint() public view { - console.logUint(testUint); - } - - function testLogString() public view { - console.logString(testStr); - } - - function testLogBool() public view { - console.logBool(testBool); - } - - function testLogAddress() public view { - console.logAddress(testAddr); - } - - function testLogBytes() public view { - console.logBytes(testBytes); - } - - function testLogBytes1() public view { - console.logBytes1(bytes1(testBytes)); - } - - function testLogBytes2() public view { - console.logBytes2(bytes2(testBytes)); - } - - function testLogBytes3() public view { - console.logBytes3(bytes3(testBytes)); - } - - function testLogBytes4() public view { - console.logBytes4(bytes4(testBytes)); - } - - function testLogBytes5() public view { - console.logBytes5(bytes5(testBytes)); - } - - function testLogBytes6() public view { - console.logBytes6(bytes6(testBytes)); - } - - function testLogBytes7() public view { - console.logBytes7(bytes7(testBytes)); - } - - function testLogBytes8() public view { - console.logBytes8(bytes8(testBytes)); - } - - function testLogBytes9() public view { - console.logBytes9(bytes9(testBytes)); - } - - function testLogBytes10() public view { - console.logBytes10(bytes10(testBytes)); - } - - function testLogBytes11() public view { - console.logBytes11(bytes11(testBytes)); - } - - function testLogBytes12() public view { - console.logBytes12(bytes12(testBytes)); - } - - function testLogBytes13() public view { - console.logBytes13(bytes13(testBytes)); - } - - function testLogBytes14() public view { - console.logBytes14(bytes14(testBytes)); - } - - function testLogBytes15() public view { - console.logBytes15(bytes15(testBytes)); - } - - function testLogBytes16() public view { - console.logBytes16(bytes16(testBytes)); - } - - function testLogBytes17() public view { - console.logBytes17(bytes17(testBytes)); - } - - function testLogBytes18() public view { - console.logBytes18(bytes18(testBytes)); - } - - function testLogBytes19() public view { - console.logBytes19(bytes19(testBytes)); - } - - function testLogBytes20() public view { - console.logBytes20(bytes20(testBytes)); - } - - function testLogBytes21() public view { - console.logBytes21(bytes21(testBytes)); - } - - function testLogBytes22() public view { - console.logBytes22(bytes22(testBytes)); - } - - function testLogBytes23() public view { - console.logBytes23(bytes23(testBytes)); - } - - function testLogBytes24() public view { - console.logBytes24(bytes24(testBytes)); - } - - function testLogBytes25() public view { - console.logBytes25(bytes25(testBytes)); - } - - function testLogBytes26() public view { - console.logBytes26(bytes26(testBytes)); - } - - function testLogBytes27() public view { - console.logBytes27(bytes27(testBytes)); - } - - function testLogBytes28() public view { - console.logBytes28(bytes28(testBytes)); - } - - function testLogBytes29() public view { - console.logBytes29(bytes29(testBytes)); - } - - function testLogBytes30() public view { - console.logBytes30(bytes30(testBytes)); - } - - function testLogBytes31() public view { - console.logBytes31(bytes31(testBytes)); - } - - function testLogBytes32() public view { - console.logBytes32(bytes32(testBytes)); - } - - function testConsoleLogUint() public view { - console.log(testUint); - } - - function testConsoleLogString() public view { - console.log(testStr); - } - - function testConsoleLogBool() public view { - console.log(testBool); - } - - function testConsoleLogAddress() public view { - console.log(testAddr); - } - - function testConsoleLogFormatString() public view { - console.log("formatted log str=%s", testStr); - } - - function testConsoleLogFormatUint() public view { - console.log("formatted log uint=%s", testUint); - } - - function testConsoleLogFormatAddress() public view { - console.log("formatted log addr=%s", testAddr); - } - - function testConsoleLogFormatMulti() public view { - console.log("formatted log str=%s uint=%d", testStr, testUint); - } - - function testConsoleLogFormatEscape() public view { - console.log("formatted log %% %s", testStr); - } - - function testConsoleLogFormatSpill() public view { - console.log("formatted log %s", testStr, testUint); - } -} diff --git a/testdata/default/repros/Issue10302.t.sol b/testdata/default/repros/Issue10302.t.sol index c47332cb2872d..c5516804ccd1c 100644 --- a/testdata/default/repros/Issue10302.t.sol +++ b/testdata/default/repros/Issue10302.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract A { function foo() public pure returns (bool) { @@ -10,9 +9,7 @@ contract A { } } -contract Issue10302Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue10302Test is Test { function testDelegateFails() external { vm.createSelectFork("sepolia"); A a = new A(); diff --git a/testdata/default/repros/Issue10477.t.sol b/testdata/default/repros/Issue10477.t.sol index 86d36fc741ef4..29bfcb37ced29 100644 --- a/testdata/default/repros/Issue10477.t.sol +++ b/testdata/default/repros/Issue10477.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract SimpleDelegate { function call(address target, bytes memory data) external returns (bool callResult, bytes memory callData) { @@ -18,9 +17,7 @@ contract Counter { } } -contract Issue10477Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue10477Test is Test { address payable ALICE_ADDRESS = payable(0x70997970C51812dc3A010C7d01b50e0d17dc79C8); uint256 constant ALICE_PK = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d; diff --git a/testdata/default/repros/Issue10527.t.sol b/testdata/default/repros/Issue10527.t.sol index fdf62b22db35c..ba92317672f3c 100644 --- a/testdata/default/repros/Issue10527.t.sol +++ b/testdata/default/repros/Issue10527.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract A { event Event1(); @@ -17,12 +16,10 @@ contract A { } } -contract Issue10527Test is DSTest { +contract Issue10527Test is Test { event Event1(); event Event2(); - Vm constant vm = Vm(HEVM_ADDRESS); - A a; function setUp() public { diff --git a/testdata/default/repros/Issue10552.t.sol b/testdata/default/repros/Issue10552.t.sol index e77168f150f4d..d0f6a7713a9b7 100644 --- a/testdata/default/repros/Issue10552.t.sol +++ b/testdata/default/repros/Issue10552.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public number; @@ -21,9 +20,7 @@ contract Counter { } } -contract Issue10552Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue10552Test is Test { Counter public counter; uint256 mainnetId; uint256 opId; diff --git a/testdata/default/repros/Issue10586.t.sol b/testdata/default/repros/Issue10586.t.sol index 3d7eefbf3ac03..24c355abef7ee 100644 --- a/testdata/default/repros/Issue10586.t.sol +++ b/testdata/default/repros/Issue10586.t.sol @@ -1,20 +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 Target is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract Target is Test { function setChainId() public { vm.chainId(123); } } -contract Issue10586Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue10586Test is Test { Target public target; function setUp() public { diff --git a/testdata/default/repros/Issue10957.t.sol b/testdata/default/repros/Issue10957.t.sol index b41e116da44fa..a8efd20f5cda6 100644 --- a/testdata/default/repros/Issue10957.t.sol +++ b/testdata/default/repros/Issue10957.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/10957 -contract Issue10957Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue10957Test is Test { function testCreateSelectForkBlockNumber() public { // Transaction hash from mainnet bytes32 txHash = 0x2e175897d19307c664815129720c8ac3581da6cb92e4cce923996dd59fbb6ffc; diff --git a/testdata/default/repros/Issue11353.t.sol b/testdata/default/repros/Issue11353.t.sol index e5e863a762903..cb89de17a0ada 100644 --- a/testdata/default/repros/Issue11353.t.sol +++ b/testdata/default/repros/Issue11353.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.24; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Blobhash { function getIndices(uint256[] calldata blobIndices) public view returns (bytes32[] memory) { @@ -18,9 +17,7 @@ contract Blobhash { } // https://github.com/foundry-rs/foundry/issues/11353 -contract Issue11353Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue11353Test is Test { Blobhash public blobhashContract; function setUp() public { diff --git a/testdata/default/repros/Issue11616.t.sol b/testdata/default/repros/Issue11616.t.sol index 9d82dbbdc4ec3..cf2cbb1900faa 100644 --- a/testdata/default/repros/Issue11616.t.sol +++ b/testdata/default/repros/Issue11616.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.24; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Emit { event A(); @@ -13,8 +12,7 @@ contract Emit { } } -contract Issue11616Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue11616Test is Test { Emit public e; function setUp() public { diff --git a/testdata/default/repros/Issue2623.t.sol b/testdata/default/repros/Issue2623.t.sol index 31252cae36c3b..486db1963b190 100644 --- a/testdata/default/repros/Issue2623.t.sol +++ b/testdata/default/repros/Issue2623.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/2623 -contract Issue2623Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue2623Test is Test { function testRollFork() public { uint256 fork = vm.createFork("mainnet", 10); vm.selectFork(fork); diff --git a/testdata/default/repros/Issue2629.t.sol b/testdata/default/repros/Issue2629.t.sol index d46868903a60e..b7e1d9ebdb208 100644 --- a/testdata/default/repros/Issue2629.t.sol +++ b/testdata/default/repros/Issue2629.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/2629 -contract Issue2629Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue2629Test is Test { function testSelectFork() public { address coinbase = 0x0193d941b50d91BE6567c7eE1C0Fe7AF498b4137; diff --git a/testdata/default/repros/Issue2723.t.sol b/testdata/default/repros/Issue2723.t.sol index b7678df450cb8..58e9ba43f64d8 100644 --- a/testdata/default/repros/Issue2723.t.sol +++ b/testdata/default/repros/Issue2723.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/2723 -contract Issue2723Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue2723Test is Test { function testRollFork() public { address coinbase = 0x0193d941b50d91BE6567c7eE1C0Fe7AF498b4137; diff --git a/testdata/default/repros/Issue2851.t.sol b/testdata/default/repros/Issue2851.t.sol deleted file mode 100644 index 7742af4b708e0..0000000000000 --- a/testdata/default/repros/Issue2851.t.sol +++ /dev/null @@ -1,29 +0,0 @@ -// 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(); - } - - /// forge-config: default.fuzz.seed = '111' - function invariantNotZero() public { - assertEq(back.number(), 1); - } -} diff --git a/testdata/default/repros/Issue2898.t.sol b/testdata/default/repros/Issue2898.t.sol index a16adf5a350ff..564ef4fa2ef78 100644 --- a/testdata/default/repros/Issue2898.t.sol +++ b/testdata/default/repros/Issue2898.t.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/2898 -contract Issue2898Test is DSTest { +contract Issue2898Test is Test { address private constant BRIDGE = address(10); address private constant BENEFICIARY = address(11); - Vm constant vm = Vm(HEVM_ADDRESS); function setUp() public { vm.deal(BRIDGE, 100); diff --git a/testdata/default/repros/Issue2956.t.sol b/testdata/default/repros/Issue2956.t.sol index c57b46cc1f4bc..3295df41dbf36 100644 --- a/testdata/default/repros/Issue2956.t.sol +++ b/testdata/default/repros/Issue2956.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/2956 -contract Issue2956Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue2956Test is Test { uint256 fork1; uint256 fork2; diff --git a/testdata/default/repros/Issue2984.t.sol b/testdata/default/repros/Issue2984.t.sol index fbcd1ab8c3c4a..52a3c52d06a84 100644 --- a/testdata/default/repros/Issue2984.t.sol +++ b/testdata/default/repros/Issue2984.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/2984 -contract Issue2984Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue2984Test is Test { uint256 fork; uint256 snapshot; diff --git a/testdata/default/repros/Issue3055.t.sol b/testdata/default/repros/Issue3055.t.sol deleted file mode 100644 index 90ac8c3b08afd..0000000000000 --- a/testdata/default/repros/Issue3055.t.sol +++ /dev/null @@ -1,36 +0,0 @@ -// 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/3055 -contract Issue3055Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - function test_snapshot() external { - uint256 snapshotId = vm.snapshotState(); - assertEq(uint256(0), uint256(1)); - vm.revertToState(snapshotId); - } - - function test_snapshot2() public { - uint256 snapshotId = vm.snapshotState(); - assertTrue(false); - vm.revertToState(snapshotId); - assertTrue(true); - } - - function test_snapshot3(uint256) public { - vm.expectRevert(); - // Call exposed_snapshot3() using this to perform an external call, - // so we can properly test for reverts. - this.exposed_snapshot3(); - } - - function exposed_snapshot3() public { - uint256 snapshotId = vm.snapshotState(); - assertTrue(false); - vm.revertToState(snapshotId); - } -} diff --git a/testdata/default/repros/Issue3077.t.sol b/testdata/default/repros/Issue3077.t.sol index 3b5e4257a3ad4..ec68bc3f4bf63 100644 --- a/testdata/default/repros/Issue3077.t.sol +++ b/testdata/default/repros/Issue3077.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3077 -abstract contract ZeroState is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +abstract contract ZeroState is Test { // deployer and users address public deployer = vm.addr(1); Token aaveToken; diff --git a/testdata/default/repros/Issue3110.t.sol b/testdata/default/repros/Issue3110.t.sol index 9f1da8d032ec3..4c4f3d257d5a5 100644 --- a/testdata/default/repros/Issue3110.t.sol +++ b/testdata/default/repros/Issue3110.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3110 -abstract contract ZeroState is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +abstract contract ZeroState is Test { // deployer and users address public deployer = vm.addr(1); Token aaveToken; diff --git a/testdata/default/repros/Issue3119.t.sol b/testdata/default/repros/Issue3119.t.sol index 6c0ceb429d6d6..99de3f7aab7fd 100644 --- a/testdata/default/repros/Issue3119.t.sol +++ b/testdata/default/repros/Issue3119.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3119 -contract Issue3119Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue3119Test is Test { address public owner = vm.addr(1); address public alice = vm.addr(2); diff --git a/testdata/default/repros/Issue3189.t.sol b/testdata/default/repros/Issue3189.t.sol deleted file mode 100644 index 0bcf5ddce8d76..0000000000000 --- a/testdata/default/repros/Issue3189.t.sol +++ /dev/null @@ -1,32 +0,0 @@ -// 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/3189 -contract MyContract { - function foo(uint256 arg) public returns (uint256) { - return arg + 2; - } -} - -contract MyContractUser is DSTest { - MyContract immutable myContract; - - constructor() { - myContract = new MyContract(); - } - - function foo(uint256 arg) public returns (uint256 ret) { - ret = myContract.foo(arg); - assertEq(ret, arg + 1, "Invariant failed"); - } -} - -contract Issue3189Test is DSTest { - function testFoo() public { - MyContractUser user = new MyContractUser(); - uint256 fooRet = user.foo(123); - } -} diff --git a/testdata/default/repros/Issue3190.t.sol b/testdata/default/repros/Issue3190.t.sol index ede3e50e2e3ec..30a6bb623b8a1 100644 --- a/testdata/default/repros/Issue3190.t.sol +++ b/testdata/default/repros/Issue3190.t.sol @@ -1,14 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3190 -contract Issue3190Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue3190Test is Test { function setUp() public { vm.chainId(99); assertEq(99, block.chainid); diff --git a/testdata/default/repros/Issue3192.t.sol b/testdata/default/repros/Issue3192.t.sol index 9c5be8d89f3d6..6fa3e8dfd442c 100644 --- a/testdata/default/repros/Issue3192.t.sol +++ b/testdata/default/repros/Issue3192.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3192 -contract Issue3192Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue3192Test is Test { uint256 fork1; uint256 fork2; diff --git a/testdata/default/repros/Issue3220.t.sol b/testdata/default/repros/Issue3220.t.sol index 5235e44c79c60..366c079619d84 100644 --- a/testdata/default/repros/Issue3220.t.sol +++ b/testdata/default/repros/Issue3220.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3220 -contract Issue3220Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue3220Test is Test { IssueRepro repro; uint256 fork1; diff --git a/testdata/default/repros/Issue3221.t.sol b/testdata/default/repros/Issue3221.t.sol index 81398c41fc290..9526987ddd87b 100644 --- a/testdata/default/repros/Issue3221.t.sol +++ b/testdata/default/repros/Issue3221.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3221 -contract Issue3221Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue3221Test is Test { uint256 fork1; uint256 fork2; diff --git a/testdata/default/repros/Issue3223.t.sol b/testdata/default/repros/Issue3223.t.sol index 6c21b7b3d60b1..ede3042ba732f 100644 --- a/testdata/default/repros/Issue3223.t.sol +++ b/testdata/default/repros/Issue3223.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3223 -contract Issue3223Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +/// forge-config: default.sender = "0xF0959944122fb1ed4CfaBA645eA06EED30427BAA" +contract Issue3223Test is Test { uint256 fork1; uint256 fork2; diff --git a/testdata/default/repros/Issue3347.t.sol b/testdata/default/repros/Issue3347.t.sol deleted file mode 100644 index e48c1305db426..0000000000000 --- a/testdata/default/repros/Issue3347.t.sol +++ /dev/null @@ -1,14 +0,0 @@ -// 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/3347 -contract Issue3347Test is DSTest { - event log2(uint256, uint256); - - function test() public { - emit log2(1, 2); - } -} diff --git a/testdata/default/repros/Issue3596.t.sol b/testdata/default/repros/Issue3596.t.sol deleted file mode 100644 index b0c6785874375..0000000000000 --- a/testdata/default/repros/Issue3596.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// 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/3596 -contract Issue3596Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - function testDealTransfer() public { - address addr = vm.addr(1337); - vm.startPrank(addr); - vm.deal(addr, 20000001 ether); - payable(address(this)).transfer(20000000 ether); - - Nested nested = new Nested(); - nested.doStuff(); - vm.stopPrank(); - } -} - -contract Nested { - function doStuff() public { - doRevert(); - } - - function doRevert() public { - revert("This fails"); - } -} diff --git a/testdata/default/repros/Issue3653.t.sol b/testdata/default/repros/Issue3653.t.sol index 26eb38e4a29f1..0657e9908d306 100644 --- a/testdata/default/repros/Issue3653.t.sol +++ b/testdata/default/repros/Issue3653.t.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3653 -contract Issue3653Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue3653Test is Test { uint256 fork; Token token; diff --git a/testdata/default/repros/Issue3661.t.sol b/testdata/default/repros/Issue3661.t.sol index 76b55a222ca00..8491857074e07 100644 --- a/testdata/default/repros/Issue3661.t.sol +++ b/testdata/default/repros/Issue3661.t.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3661 -contract Issue3661Test is DSTest { +contract Issue3661Test is Test { address sender; function setUp() public { diff --git a/testdata/default/repros/Issue3674.t.sol b/testdata/default/repros/Issue3674.t.sol index de5a960059f52..39d8d9c24f582 100644 --- a/testdata/default/repros/Issue3674.t.sol +++ b/testdata/default/repros/Issue3674.t.sol @@ -1,17 +1,15 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3674 -contract Issue3674Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.sender = "0xF0959944122fb1ed4CfaBA645eA06EED30427BAA" +contract Issue3674Test is Test { function testNonceCreateSelect() public { vm.createSelectFork("sepolia"); vm.createSelectFork("avaxTestnet"); - assert(vm.getNonce(msg.sender) > 0x17); + assertTrue(vm.getNonce(msg.sender) > 0x17); } } diff --git a/testdata/default/repros/Issue3685.t.sol b/testdata/default/repros/Issue3685.t.sol index f1da5bf6997bd..e7ff00264e52c 100644 --- a/testdata/default/repros/Issue3685.t.sol +++ b/testdata/default/repros/Issue3685.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3685 -contract Issue3685Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue3685Test is Test { Actor a; Actor b; diff --git a/testdata/default/repros/Issue3703.t.sol b/testdata/default/repros/Issue3703.t.sol index 48651be24c669..3ac50765f1a93 100644 --- a/testdata/default/repros/Issue3703.t.sol +++ b/testdata/default/repros/Issue3703.t.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3703 -contract Issue3703Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue3703Test is Test { function setUp() public { + vm.skip(true, "flaky polygon RPCs"); + uint256 fork = vm.createSelectFork("polygon", bytes32(0xbed0c8c1b9ff8bf0452979d170c52893bb8954f18a904aa5bcbd0f709be050b9)); } diff --git a/testdata/default/repros/Issue3708.t.sol b/testdata/default/repros/Issue3708.t.sol index 53a7c461f873f..c78304a1cbf02 100644 --- a/testdata/default/repros/Issue3708.t.sol +++ b/testdata/default/repros/Issue3708.t.sol @@ -1,14 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3708 -contract Issue3708Test is DSTest { +contract Issue3708Test is Test { // https://optimistic.etherscan.io/address/0x4e59b44847b379578588920ca78fbf26c0b4956c#code address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; - Vm constant vm = Vm(HEVM_ADDRESS); function setUp() public { uint256 forkId = vm.createSelectFork("optimism"); diff --git a/testdata/default/repros/Issue3753.t.sol b/testdata/default/repros/Issue3753.t.sol index 2c927c823dbb6..1fe6040f2a44a 100644 --- a/testdata/default/repros/Issue3753.t.sol +++ b/testdata/default/repros/Issue3753.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/3753 -contract Issue3753Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue3753Test is Test { function test_repro() public { bool res; assembly { diff --git a/testdata/default/repros/Issue3792.t.sol b/testdata/default/repros/Issue3792.t.sol index 37f62bc61fabe..39d3f77bc28a4 100644 --- a/testdata/default/repros/Issue3792.t.sol +++ b/testdata/default/repros/Issue3792.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/DSTest.sol"; +import "utils/Vm.sol"; contract Config { address public test = 0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa; diff --git a/testdata/default/repros/Issue4232.t.sol b/testdata/default/repros/Issue4232.t.sol index 0ac6a77c77076..44f038bfbf28e 100644 --- a/testdata/default/repros/Issue4232.t.sol +++ b/testdata/default/repros/Issue4232.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/4232 -contract Issue4232Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue4232Test is Test { function testFork() public { // Smoke test, worked previously as well vm.createSelectFork("sepolia", 7215400); diff --git a/testdata/default/repros/Issue4402.t.sol b/testdata/default/repros/Issue4402.t.sol index 830b2926ef269..19072638e74da 100644 --- a/testdata/default/repros/Issue4402.t.sol +++ b/testdata/default/repros/Issue4402.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/4402 -contract Issue4402Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue4402Test is Test { function testReadNonEmptyArray() public { string memory path = "fixtures/Json/Issue4402.json"; string memory json = vm.readFile(path); diff --git a/testdata/default/repros/Issue4586.t.sol b/testdata/default/repros/Issue4586.t.sol index c904af1e46abb..57f1dd3052d04 100644 --- a/testdata/default/repros/Issue4586.t.sol +++ b/testdata/default/repros/Issue4586.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/4586 -contract Issue4586Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue4586Test is Test { uint256 constant initialBlock = 16730733; InvariantHandler handler; @@ -31,8 +28,7 @@ contract Issue4586Test is DSTest { } contract InvariantHandler { - address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); - Vm constant vm = Vm(HEVM_ADDRESS); + Vm constant vm = Vm(address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))); uint256 public calledRollFork; diff --git a/testdata/default/repros/Issue4630.t.sol b/testdata/default/repros/Issue4630.t.sol index 01eb626505cd1..cfc0ebde43089 100644 --- a/testdata/default/repros/Issue4630.t.sol +++ b/testdata/default/repros/Issue4630.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/4630 -contract Issue4630Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue4630Test is Test { function testExistingValue() public { string memory path = "fixtures/Json/Issue4630.json"; string memory json = vm.readFile(path); diff --git a/testdata/default/repros/Issue4640.t.sol b/testdata/default/repros/Issue4640.t.sol index 1e7d887a9b57d..3a20eaa0f8d8c 100644 --- a/testdata/default/repros/Issue4640.t.sol +++ b/testdata/default/repros/Issue4640.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/4640 -contract Issue4640Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue4640Test is Test { function testArbitrumBlockNumber() public { // vm.createSelectFork("arbitrum", 75219831); diff --git a/testdata/default/repros/Issue5038.t.sol b/testdata/default/repros/Issue5038.t.sol index 51a90bca10d4c..2139639cc56ac 100644 --- a/testdata/default/repros/Issue5038.t.sol +++ b/testdata/default/repros/Issue5038.t.sol @@ -1,17 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; struct Value { uint256 value; } // https://github.com/foundry-rs/foundry/issues/5038 -contract Issue5038Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue5038Test is Test { function testParseMaxUint64() public { string memory json = '{"value": 18446744073709551615}'; bytes memory parsed = vm.parseJson(json); diff --git a/testdata/default/repros/Issue5529.t.sol b/testdata/default/repros/Issue5529.t.sol index 14ec7cfdbce60..2f7afbbc2d524 100644 --- a/testdata/default/repros/Issue5529.t.sol +++ b/testdata/default/repros/Issue5529.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public number; @@ -16,20 +15,21 @@ contract Counter { } } -contract Issue5529Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.always_use_create_2_factory = true +contract Issue5529Test is Test { Counter public counter; address public constant default_create2_factory = 0x4e59b44847b379578588920cA78FbF26c0B4956C; function testCreate2FactoryUsedInTests() public { - address a = vm.computeCreate2Address(0, keccak256(type(Counter).creationCode), address(default_create2_factory)); - address b = address(new Counter{salt: 0}()); - require(a == b, "create2 address mismatch"); + run(); } function testCreate2FactoryUsedWhenPranking() public { vm.startPrank(address(1234)); + run(); + } + + function run() private { address a = vm.computeCreate2Address(0, keccak256(type(Counter).creationCode), address(default_create2_factory)); address b = address(new Counter{salt: 0}()); require(a == b, "create2 address mismatch"); diff --git a/testdata/default/repros/Issue5739.t.sol b/testdata/default/repros/Issue5739.t.sol index 6f3494b7e6e7d..bbde1b73e7c20 100644 --- a/testdata/default/repros/Issue5739.t.sol +++ b/testdata/default/repros/Issue5739.t.sol @@ -1,16 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.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); +contract Issue5739Test is Test { IERC20 dai; function setUp() public { diff --git a/testdata/default/repros/Issue5808.t.sol b/testdata/default/repros/Issue5808.t.sol index 40efe65a9ce69..fc76fe1e4a3cf 100644 --- a/testdata/default/repros/Issue5808.t.sol +++ b/testdata/default/repros/Issue5808.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/5808 -contract Issue5808Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue5808Test is Test { function testReadInt() public { string memory str1 = '["ffffffff","00000010"]'; vm._expectCheatcodeRevert(); diff --git a/testdata/default/repros/Issue5929.t.sol b/testdata/default/repros/Issue5929.t.sol index ced9d6d9b4a39..6dac42653e3ed 100644 --- a/testdata/default/repros/Issue5929.t.sol +++ b/testdata/default/repros/Issue5929.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/5929 -contract Issue5929Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue5929Test is Test { function test_transact_not_working() public { vm.createSelectFork("mainnet", 21134547); // https://etherscan.io/tx/0x96a129768ec66fd7d65114bf182f4e173bf0b73a44219adaf71f01381a3d0143 diff --git a/testdata/default/repros/Issue5935.t.sol b/testdata/default/repros/Issue5935.t.sol index 8ef724412ce31..220681cab55fb 100644 --- a/testdata/default/repros/Issue5935.t.sol +++ b/testdata/default/repros/Issue5935.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract SimpleStorage { uint256 public value; @@ -12,9 +11,7 @@ contract SimpleStorage { } } -contract Issue5935Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue5935Test is Test { function testFork() public { uint256 forkId1 = vm.createFork("mainnet", 18234083); uint256 forkId2 = vm.createFork("mainnet", 18234083); diff --git a/testdata/default/repros/Issue5948.t.sol b/testdata/default/repros/Issue5948.t.sol index ae6ee9d50d8ba..b7a5080721baf 100644 --- a/testdata/default/repros/Issue5948.t.sol +++ b/testdata/default/repros/Issue5948.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/5948 -contract Issue5948Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue5948Test is Test { /// forge-config: default.fuzz.runs = 2 function testSleepFuzzed(uint256 _milliseconds) public { // Limit sleep time to 2 seconds to decrease test time diff --git a/testdata/default/repros/Issue6006.t.sol b/testdata/default/repros/Issue6006.t.sol index 54f0d11376d79..30dcbd00a74a2 100644 --- a/testdata/default/repros/Issue6006.t.sol +++ b/testdata/default/repros/Issue6006.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6006 -contract Issue6066Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6066Test is Test { function test_parse_11e20_sci() public { string memory json = '{"value": 1.1e20}'; bytes memory parsed = vm.parseJson(json); diff --git a/testdata/default/repros/Issue6032.t.sol b/testdata/default/repros/Issue6032.t.sol index 2fa05222d5a54..149097975ad30 100644 --- a/testdata/default/repros/Issue6032.t.sol +++ b/testdata/default/repros/Issue6032.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6032 -contract Issue6032Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6032Test is Test { function testEtchFork() public { // Deploy initial contract Counter counter = new Counter(); diff --git a/testdata/default/repros/Issue6070.t.sol b/testdata/default/repros/Issue6070.t.sol index ebf3c7ab15c7b..5188a877e8d90 100644 --- a/testdata/default/repros/Issue6070.t.sol +++ b/testdata/default/repros/Issue6070.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6070 -contract Issue6066Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6066Test is Test { function testNonPrefixed() public { vm.setEnv("__FOUNDRY_ISSUE_6066", "abcd"); vm._expectCheatcodeRevert( diff --git a/testdata/default/repros/Issue6115.t.sol b/testdata/default/repros/Issue6115.t.sol index ae65a7dae8647..878b584a31afb 100644 --- a/testdata/default/repros/Issue6115.t.sol +++ b/testdata/default/repros/Issue6115.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; contract Counter { uint256 public number; @@ -16,7 +16,7 @@ contract Counter { } // https://github.com/foundry-rs/foundry/issues/6115 -contract Issue6115Test is DSTest { +contract Issue6115Test is Test { Counter public counter; function setUp() public { diff --git a/testdata/default/repros/Issue6170.t.sol b/testdata/default/repros/Issue6170.t.sol deleted file mode 100644 index 78511f4a2dc91..0000000000000 --- a/testdata/default/repros/Issue6170.t.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract Emitter { - event Values(uint256 indexed a, uint256 indexed b); - - function plsEmit(uint256 a, uint256 b) external { - emit Values(a, b); - } -} - -// https://github.com/foundry-rs/foundry/issues/6170 -contract Issue6170Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - event Values(uint256 indexed a, uint256 b); - - Emitter e = new Emitter(); - - function test() public { - vm.expectEmit(true, true, false, true); - emit Values(69, 420); - e.plsEmit(69, 420); - } -} diff --git a/testdata/default/repros/Issue6180.t.sol b/testdata/default/repros/Issue6180.t.sol index 3d08ccbebac5d..5d6b82988e139 100644 --- a/testdata/default/repros/Issue6180.t.sol +++ b/testdata/default/repros/Issue6180.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6180 -contract Issue6180Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6180Test is Test { function test_timebug() external { uint256 start = block.timestamp; uint256 count = 4; diff --git a/testdata/default/repros/Issue6293.t.sol b/testdata/default/repros/Issue6293.t.sol index 6d57d13850950..78fe026c3dbf3 100644 --- a/testdata/default/repros/Issue6293.t.sol +++ b/testdata/default/repros/Issue6293.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6293 -contract Issue6293Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6293Test is Test { constructor() { require(address(this).balance > 0); payable(address(1)).call{value: 1}(""); diff --git a/testdata/default/repros/Issue6355.t.sol b/testdata/default/repros/Issue6355.t.sol deleted file mode 100644 index bbc3a4a98d412..0000000000000 --- a/testdata/default/repros/Issue6355.t.sol +++ /dev/null @@ -1,39 +0,0 @@ -// 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/6355 -contract Issue6355Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - uint256 snapshotId; - Target targ; - - function setUp() public { - snapshotId = vm.snapshotState(); - targ = new Target(); - } - - // this non-deterministically fails sometimes and passes sometimes - function test_shouldPass() public { - assertEq(2, targ.num()); - } - - // always fails - function test_shouldFailWithRevertToState() public { - assertEq(3, targ.num()); - vm.revertToState(snapshotId); - } - - // always fails - function test_shouldFail() public { - assertEq(3, targ.num()); - } -} - -contract Target { - function num() public pure returns (uint256) { - return 2; - } -} diff --git a/testdata/default/repros/Issue6437.t.sol b/testdata/default/repros/Issue6437.t.sol index 4cf27be7b233e..e2fceaa660f2a 100644 --- a/testdata/default/repros/Issue6437.t.sol +++ b/testdata/default/repros/Issue6437.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6437 -contract Issue6437Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6437Test is Test { function test0() public { string memory json = "[]"; address[] memory arr = vm.parseJsonAddressArray(json, "$"); diff --git a/testdata/default/repros/Issue6501.t.sol b/testdata/default/repros/Issue6501.t.sol deleted file mode 100644 index 5d631cbe3e0a8..0000000000000 --- a/testdata/default/repros/Issue6501.t.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "../logs/console.sol"; - -// https://github.com/foundry-rs/foundry/issues/6501 -contract Issue6501Test is DSTest { - function test_hhLogs() public { - console.log("a"); - console.log(uint256(1)); - console.log("b", uint256(2)); - } -} diff --git a/testdata/default/repros/Issue6538.t.sol b/testdata/default/repros/Issue6538.t.sol index 34c4e2253a68b..c870c668e6816 100644 --- a/testdata/default/repros/Issue6538.t.sol +++ b/testdata/default/repros/Issue6538.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6538 -contract Issue6538Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6538Test is Test { function test_transact() public { bytes32 lastHash = 0x4b70ca8c5a0990b43df3064372d424d46efa41dfaab961754b86c5afb2df4f61; vm.createSelectFork("mainnet", lastHash); diff --git a/testdata/default/repros/Issue6554.t.sol b/testdata/default/repros/Issue6554.t.sol index 7a5fe7879c655..a6b086653a96a 100644 --- a/testdata/default/repros/Issue6554.t.sol +++ b/testdata/default/repros/Issue6554.t.sol @@ -1,16 +1,13 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6554 -contract Issue6554Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6554Test is Test { function testPermissions() public { - vm.writeFile("./out/default/Issue6554.t.sol/cachedFile.txt", "cached data"); - string memory content = vm.readFile("./out/default/Issue6554.t.sol/cachedFile.txt"); + vm.writeFile("./out/Issue6554.t.sol/cachedFile.txt", "cached data"); + string memory content = vm.readFile("./out/Issue6554.t.sol/cachedFile.txt"); assertEq(content, "cached data"); } } diff --git a/testdata/default/repros/Issue6616.t.sol b/testdata/default/repros/Issue6616.t.sol index 262721d86d118..587c4703e3d6c 100644 --- a/testdata/default/repros/Issue6616.t.sol +++ b/testdata/default/repros/Issue6616.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6616 -contract Issue6616Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6616Test is Test { function testCreateForkRollLatestBlock() public { vm.createSelectFork("mainnet"); uint256 startBlock = block.number; diff --git a/testdata/default/repros/Issue6634.t.sol b/testdata/default/repros/Issue6634.t.sol index aa94922dd1ccb..5426b3f2404ba 100644 --- a/testdata/default/repros/Issue6634.t.sol +++ b/testdata/default/repros/Issue6634.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../logs/console.sol"; +import "utils/Test.sol"; contract Box { uint256 public number; @@ -14,9 +12,8 @@ contract Box { } // https://github.com/foundry-rs/foundry/issues/6634 -contract Issue6634Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +/// forge-config: default.always_use_create_2_factory = true +contract Issue6634Test is Test { function test_Create2FactoryCallRecordedInStandardTest() public { address CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; diff --git a/testdata/default/repros/Issue6643.t.sol b/testdata/default/repros/Issue6643.t.sol index 5c7e1c483a03a..0f8b45fa2864d 100644 --- a/testdata/default/repros/Issue6643.t.sol +++ b/testdata/default/repros/Issue6643.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { event TestEvent(uint256 n); @@ -24,8 +23,7 @@ contract Counter { } // https://github.com/foundry-rs/foundry/issues/6643 -contract Issue6643Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue6643Test is Test { Counter public counter; event TestEvent(uint256 n); diff --git a/testdata/default/repros/Issue6759.t.sol b/testdata/default/repros/Issue6759.t.sol index ffdcb88935a92..436d7922fe3d2 100644 --- a/testdata/default/repros/Issue6759.t.sol +++ b/testdata/default/repros/Issue6759.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6759 -contract Issue6759Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue6759Test is Test { function testCreateMulti() public { uint256 fork1 = vm.createFork("mainnet", 10); uint256 fork2 = vm.createFork("mainnet", 10); diff --git a/testdata/default/repros/Issue6966.t.sol b/testdata/default/repros/Issue6966.t.sol index 7e35a869ed0fb..9032a51dd63f7 100644 --- a/testdata/default/repros/Issue6966.t.sol +++ b/testdata/default/repros/Issue6966.t.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/6966 // See also https://github.com/RustCrypto/elliptic-curves/issues/988#issuecomment-1817681013 -contract Issue6966Test is DSTest { +contract Issue6966Test is Test { function testEcrecover() public { bytes32 h = 0x0000000000000000000000000000000000000000000000000000000000000000; uint8 v = 27; diff --git a/testdata/default/repros/Issue7238.t.sol b/testdata/default/repros/Issue7238.t.sol index 5291993525009..989b8d5799fb6 100644 --- a/testdata/default/repros/Issue7238.t.sol +++ b/testdata/default/repros/Issue7238.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Reverter { function doNotRevert() public {} @@ -13,9 +12,7 @@ contract Reverter { } // https://github.com/foundry-rs/foundry/issues/7238 -contract Issue7238Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue7238Test is Test { function testExpectRevertString() public { Reverter reverter = new Reverter(); vm.expectRevert("revert"); diff --git a/testdata/default/repros/Issue7457.t.sol b/testdata/default/repros/Issue7457.t.sol index 13cd033afac9a..c6413ec8cbe4b 100644 --- a/testdata/default/repros/Issue7457.t.sol +++ b/testdata/default/repros/Issue7457.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; interface ITarget { event AnonymousEventEmpty() anonymous; @@ -46,9 +45,7 @@ contract Target is ITarget { } // https://github.com/foundry-rs/foundry/issues/7457 -contract Issue7457Test is DSTest, ITarget { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue7457Test is Test, ITarget { Target public target; function setUp() external { diff --git a/testdata/default/repros/Issue7481.t.sol b/testdata/default/repros/Issue7481.t.sol index c8116b8095aeb..7fc01fde6523a 100644 --- a/testdata/default/repros/Issue7481.t.sol +++ b/testdata/default/repros/Issue7481.t.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.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); - +contract Issue7481Test is Test { /// forge-config: default.allow_internal_expect_revert = true function testRevertTransact() public { vm.expectRevert("vm.createSelectFork: invalid rpc url: mainnet"); diff --git a/testdata/default/repros/Issue8004.t.sol b/testdata/default/repros/Issue8004.t.sol index 278aa12125ff3..b7dd82af36cec 100644 --- a/testdata/default/repros/Issue8004.t.sol +++ b/testdata/default/repros/Issue8004.t.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; -contract NonPersistentHelper is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract NonPersistentHelper is Test { uint256 public curState; function createSelectFork() external { @@ -42,8 +40,7 @@ contract NonPersistentHelper is DSTest { } // https://github.com/foundry-rs/foundry/issues/8004 -contract Issue8004CreateSelectForkTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue8004CreateSelectForkTest is Test { NonPersistentHelper helper; function setUp() public { @@ -72,8 +69,7 @@ contract Issue8004CreateSelectForkTest is DSTest { } } -contract Issue8004RollForkTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue8004RollForkTest is Test { NonPersistentHelper helper; uint256 forkId; diff --git a/testdata/default/repros/Issue8006.t.sol b/testdata/default/repros/Issue8006.t.sol index efe339d9fef2b..706703eecf8ba 100644 --- a/testdata/default/repros/Issue8006.t.sol +++ b/testdata/default/repros/Issue8006.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; interface IERC20 { function totalSupply() external view returns (uint256 supply); @@ -15,8 +14,7 @@ contract Mock { } // https://github.com/foundry-rs/foundry/issues/8006 -contract Issue8006Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +contract Issue8006Test is Test { IERC20 dai; bytes32 transaction = 0xb23f389b26eb6f95c08e275ec2c360ab3990169492ff0d3e7b7233a3f81d299f; diff --git a/testdata/default/repros/Issue8168.t.sol b/testdata/default/repros/Issue8168.t.sol index 9a072ce4bbf8f..f7871b4b76f71 100644 --- a/testdata/default/repros/Issue8168.t.sol +++ b/testdata/default/repros/Issue8168.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/8168 -contract Issue8168Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue8168Test is Test { function testForkWarpRollPreserved() public { uint256 fork1 = vm.createFork("mainnet"); uint256 fork2 = vm.createFork("mainnet"); diff --git a/testdata/default/repros/Issue8277.t.sol b/testdata/default/repros/Issue8277.t.sol index 48a089575b402..8dca9cd7ea832 100644 --- a/testdata/default/repros/Issue8277.t.sol +++ b/testdata/default/repros/Issue8277.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/8277 -contract Issue8277Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue8277Test is Test { struct MyJson { string s; } diff --git a/testdata/default/repros/Issue8287.t.sol b/testdata/default/repros/Issue8287.t.sol index d1e372bda91d8..e1a67f3538e46 100644 --- a/testdata/default/repros/Issue8287.t.sol +++ b/testdata/default/repros/Issue8287.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/8287 -contract Issue8287Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue8287Test is Test { function testRpcBalance() public { uint256 f2 = vm.createSelectFork("mainnet", 10); bytes memory data = vm.rpc("eth_getBalance", "[\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\",\"0x0\"]"); diff --git a/testdata/default/repros/Issue8566.t.sol b/testdata/default/repros/Issue8566.t.sol index f300d096f7a22..2dad2fc1f8371 100644 --- a/testdata/default/repros/Issue8566.t.sol +++ b/testdata/default/repros/Issue8566.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; // https://github.com/foundry-rs/foundry/issues/8566 -contract Issue8566Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue8566Test is Test { function testParseJsonUint() public { string memory json = "{ \"1284\": { \"addRewardInfo\": { \"amount\": 74258.225772486694040708e18, \"rewardPerSec\": 0.03069536448928848133e20 } } }"; diff --git a/testdata/default/repros/Issue8639.t.sol b/testdata/default/repros/Issue8639.t.sol index 6f0a7b526336f..96b2fa3621522 100644 --- a/testdata/default/repros/Issue8639.t.sol +++ b/testdata/default/repros/Issue8639.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; library ExternalLibrary { function doWork(uint256 a) public returns (uint256) { @@ -20,7 +20,7 @@ contract Counter { } // https://github.com/foundry-rs/foundry/issues/8639 -contract Issue8639Test is DSTest { +contract Issue8639Test is Test { Counter counter; function setUp() public { @@ -28,15 +28,15 @@ contract Issue8639Test is DSTest { } /// forge-config: default.fuzz.runs = 1000 - /// forge-config: default.fuzz.seed = '100' + /// forge-config: default.fuzz.seed = "100" function test_external_library_address(address test) public { require(test != address(ExternalLibrary)); } } -contract Issue8639AnotherTest is DSTest { +contract Issue8639AnotherTest is Test { /// forge-config: default.fuzz.runs = 1000 - /// forge-config: default.fuzz.seed = '100' + /// forge-config: default.fuzz.seed = "100" function test_another_external_library_address(address test) public { require(test != address(ExternalLibrary)); } diff --git a/testdata/default/repros/Issue8971.t.sol b/testdata/default/repros/Issue8971.t.sol index 37861b483ec5d..4d235be214679 100644 --- a/testdata/default/repros/Issue8971.t.sol +++ b/testdata/default/repros/Issue8971.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Counter { uint256 public number; @@ -31,7 +30,8 @@ contract Handler { } } -contract Invariant is DSTest { +/// forge-config: default.isolate = true +contract Invariant is Test { Handler h; function setUp() public { diff --git a/testdata/default/repros/Issue9643.t.sol b/testdata/default/repros/Issue9643.t.sol index 5429219e5863d..fead6ebcbb0c4 100644 --- a/testdata/default/repros/Issue9643.t.sol +++ b/testdata/default/repros/Issue9643.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Mock { uint256 private counter; @@ -33,16 +32,14 @@ contract DelegateProxy { } } -contract Issue9643Test is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract Issue9643Test is Test { function test_storage_json_diff() public { vm.startStateDiffRecording(); Mock proxied = Mock(address(new DelegateProxy(address(new Mock())))); proxied.setCounter(42); string memory rawDiff = vm.getStateDiffJson(); assertEq( - "{\"0x2e234dae75c793f67a35089c9d99245e1c58470b\":{\"label\":null,\"contract\":\"default/repros/Issue9643.t.sol:DelegateProxy\",\"balanceDiff\":null,\"nonceDiff\":{\"previousValue\":0,\"newValue\":1},\"stateDiff\":{\"0x0000000000000000000000000000000000000000000000000000000000000000\":{\"previousValue\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"newValue\":\"0x000000000000000000000000000000000000000000000000000000000000002a\"}}},\"0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f\":{\"label\":null,\"contract\":\"default/repros/Issue9643.t.sol:Mock\",\"balanceDiff\":null,\"nonceDiff\":{\"previousValue\":0,\"newValue\":1},\"stateDiff\":{}}}", + '{"0x2e234dae75c793f67a35089c9d99245e1c58470b":{"label":null,"contract":"default/repros/Issue9643.t.sol:DelegateProxy","balanceDiff":null,"nonceDiff":{"previousValue":0,"newValue":1},"stateDiff":{"0x0000000000000000000000000000000000000000000000000000000000000000":{"previousValue":"0x0000000000000000000000000000000000000000000000000000000000000000","newValue":"0x000000000000000000000000000000000000000000000000000000000000002a","label":"implementation","type":"address","offset":0,"slot":"0","decoded":{"previousValue":"0x0000000000000000000000000000000000000000","newValue":"0x000000000000000000000000000000000000002A"}}}},"0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f":{"label":null,"contract":"default/repros/Issue9643.t.sol:Mock","balanceDiff":null,"nonceDiff":{"previousValue":0,"newValue":1},"stateDiff":{}}}', rawDiff ); } diff --git a/testdata/default/spec/ShanghaiCompat.t.sol b/testdata/default/spec/ShanghaiCompat.t.sol index fd7213b3d0702..7ff6405d16ccf 100644 --- a/testdata/default/spec/ShanghaiCompat.t.sol +++ b/testdata/default/spec/ShanghaiCompat.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ShanghaiCompat is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ShanghaiCompat is Test { function testPush0() public { address target = address(uint160(uint256(0xc4f3))); diff --git a/testdata/default/trace/ConflictingSignatures.t.sol b/testdata/default/trace/ConflictingSignatures.t.sol deleted file mode 100644 index c8b7066c7a2f1..0000000000000 --- a/testdata/default/trace/ConflictingSignatures.t.sol +++ /dev/null @@ -1,41 +0,0 @@ -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ReturnsNothing { - function func() public pure {} -} - -contract ReturnsString { - function func() public pure returns (string memory) { - return "string"; - } -} - -contract ReturnsUint { - function func() public pure returns (uint256) { - return 1; - } -} - -contract ConflictingSignaturesTest is DSTest { - ReturnsNothing retsNothing; - ReturnsString retsString; - ReturnsUint retsUint; - - function setUp() public { - retsNothing = new ReturnsNothing(); - retsString = new ReturnsString(); - retsUint = new ReturnsUint(); - } - - /// Tests that traces are decoded properly when multiple - /// functions have the same 4byte signature, but different - /// return values. - function testTraceWithConflictingSignatures() public { - retsNothing.func(); - retsString.func(); - retsUint.func(); - } -} diff --git a/testdata/default/trace/Trace.t.sol b/testdata/default/trace/Trace.t.sol deleted file mode 100644 index 19af6dd7c9fe7..0000000000000 --- a/testdata/default/trace/Trace.t.sol +++ /dev/null @@ -1,98 +0,0 @@ -pragma solidity ^0.8.18; - -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract RecursiveCall { - TraceTest factory; - - event Depth(uint256 depth); - event ChildDepth(uint256 childDepth); - event CreatedChild(uint256 childDepth); - - constructor(address _factory) { - factory = TraceTest(_factory); - } - - function recurseCall(uint256 neededDepth, uint256 depth) public returns (uint256) { - if (depth == neededDepth) { - this.negativeNum(); - return neededDepth; - } - - uint256 childDepth = this.recurseCall(neededDepth, depth + 1); - emit ChildDepth(childDepth); - - this.someCall(); - emit Depth(depth); - - return depth; - } - - function recurseCreate(uint256 neededDepth, uint256 depth) public returns (uint256) { - if (depth == neededDepth) { - return neededDepth; - } - - RecursiveCall child = factory.create(); - emit CreatedChild(depth + 1); - - uint256 childDepth = child.recurseCreate(neededDepth, depth + 1); - emit ChildDepth(childDepth); - emit Depth(depth); - - return depth; - } - - function someCall() public pure {} - - function negativeNum() public pure returns (int256) { - return -1000000000; - } -} - -contract TraceTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - - uint256 nodeId = 0; - RecursiveCall first; - - function setUp() public { - first = this.create(); - } - - function create() public returns (RecursiveCall) { - RecursiveCall node = new RecursiveCall(address(this)); - vm.label(address(node), string(abi.encodePacked("Node ", uintToString(nodeId++)))); - - return node; - } - - function testRecurseCall() public { - first.recurseCall(8, 0); - } - - function testRecurseCreate() public { - first.recurseCreate(8, 0); - } -} - -function uintToString(uint256 value) pure returns (string memory) { - // Taken from OpenZeppelin - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); -} diff --git a/testdata/default/vyper/CounterTest.vy b/testdata/default/vyper/CounterTest.vy index b6cc517d25dd6..960ec5cbaa830 100644 --- a/testdata/default/vyper/CounterTest.vy +++ b/testdata/default/vyper/CounterTest.vy @@ -1,4 +1,4 @@ -from . import ICounter +from src import ICounter interface Vm: def deployCode(artifact_name: String[1024], args: Bytes[1024] = b"") -> address: nonpayable @@ -8,7 +8,7 @@ counter: ICounter @external def setUp(): - self.counter = ICounter(extcall vm.deployCode("vyper/Counter.vy")) + self.counter = ICounter(extcall vm.deployCode("src/Counter.vy")) @external def test_increment(): diff --git a/testdata/fixtures/File/ignored/.gitignore b/testdata/fixtures/File/ignored/.gitignore new file mode 100644 index 0000000000000..9c558e357c416 --- /dev/null +++ b/testdata/fixtures/File/ignored/.gitignore @@ -0,0 +1 @@ +. diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 2360d009c7a09..86700b2584c2f 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -1,14 +1,60 @@ [profile.default] -src = "./" +src = "src" test = "./" -solc = "0.8.18" -evm_version = "paris" -ffi = false -fs_permissions = [{ access = "read-write", path = "./" }] +optimizer = true +optimizer_runs = 200 +via_ir = false +ignored_error_codes = [ + 1878, # SPDX license identifier not provided + 2018, # Function state mutability can be restricted + 2072, # Unused local variable + 2319, # This declaration shadows a builtin symbol + 2519, # This declaration shadows an existing declaration + 3860, # Contract init code size exceeds limit + 5574, # Contract code size exceeds limit + 5667, # Unused function parameter +] +extra_output = ["storageLayout"] + +additional_compiler_profiles = [ + # paris + { name = "paris", evm_version = "paris" }, +] +compilation_restrictions = [ + # paris + { paths = "paris/**", evm_version = "paris" }, +] + +ffi = true +fs_permissions = [ + { access = "read-write", path = "out/" }, + { access = "read-write", path = "fixtures/" }, +] +verbosity = 3 +prompt_timeout = 0 [profile.default.rpc_storage_caching] chains = "all" endpoints = "all" +[profile.default.invariant] +depth = 15 + [fmt] ignore = ["cheats/Vm.sol"] + +[lint] +lint_on_build = false + +# These are set in .env, which is populated when running through `cargo (nex)test`. +[rpc_endpoints] +mainnet = "${RPC_MAINNET}" +mainnet2 = "${RPC_MAINNET2}" +sepolia = "${RPC_SEPOLIA}" +optimism = "${RPC_OPTIMISM}" +arbitrum = "${RPC_ARBITRUM}" +polygon = "${RPC_POLYGON}" +bsc = "${RPC_BSC}" +avaxTestnet = "https://api.avax-test.network/ext/bc/C/rpc" +moonbeam = "https://moonbeam-rpc.publicnode.com" +rpcEnvAlias = "${RPC_ENV_ALIAS}" diff --git a/testdata/multi-version/Counter.sol b/testdata/multi-version/Counter.sol index 4f0c350335f8e..b2d9774d4b9a8 100644 --- a/testdata/multi-version/Counter.sol +++ b/testdata/multi-version/Counter.sol @@ -1,3 +1,5 @@ +pragma solidity ^0.8.4; + contract Counter { uint256 public number; diff --git a/testdata/multi-version/cheats/GetCode.t.sol b/testdata/multi-version/cheats/GetCode.t.sol index 72dae24e676af..ecb78876cb053 100644 --- a/testdata/multi-version/cheats/GetCode.t.sol +++ b/testdata/multi-version/cheats/GetCode.t.sol @@ -1,13 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity =0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; import "../Counter.sol"; -contract GetCodeTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GetCodeTest is Test { function testGetCodeMultiVersion() public { assertEq(vm.getCode("Counter.sol"), type(Counter).creationCode); require( diff --git a/testdata/multi-version/cheats/GetCode17.t.sol b/testdata/multi-version/cheats/GetCode17.t.sol index f8bf4bb2aee28..3cdd42ef3c78a 100644 --- a/testdata/multi-version/cheats/GetCode17.t.sol +++ b/testdata/multi-version/cheats/GetCode17.t.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity =0.8.17; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.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); - +contract GetCodeTest17 is Test { function testGetCodeMultiVersion() public { assertEq(vm.getCode("Counter.sol"), type(Counter).creationCode); require( diff --git a/testdata/paris/cheats/Fork.t.sol b/testdata/paris/cheats/Fork.t.sol index 2f2e627de131a..281a27a0b868c 100644 --- a/testdata/paris/cheats/Fork.t.sol +++ b/testdata/paris/cheats/Fork.t.sol @@ -1,19 +1,17 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; interface IWETH { function deposit() external payable; function balanceOf(address) external view returns (uint256); } -contract ForkTest is DSTest { +contract ForkTest is Test { address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; uint256 constant mainblock = 14_608_400; - Vm constant vm = Vm(HEVM_ADDRESS); IWETH WETH = IWETH(WETH_TOKEN_ADDR); uint256 forkA; diff --git a/testdata/paris/cheats/GasSnapshots.t.sol b/testdata/paris/cheats/GasSnapshots.t.sol index d4f471fdeaf4d..a3f1c9921a4b5 100644 --- a/testdata/paris/cheats/GasSnapshots.t.sol +++ b/testdata/paris/cheats/GasSnapshots.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract GasSnapshotTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract GasSnapshotTest is Test { uint256 public slot0; Flare public flare; @@ -94,7 +91,7 @@ contract GasSnapshotTest is DSTest { function testSnapshotGasSectionDefaultGroupStop() public { vm.startSnapshotGas("testSnapshotGasSection"); - flare.run(256); + flare.run(8); // vm.stopSnapshotGas() will use the last snapshot name. uint256 gasUsed = vm.stopSnapshotGas(); @@ -105,7 +102,7 @@ contract GasSnapshotTest is DSTest { function testSnapshotGasSectionCustomGroupStop() public { vm.startSnapshotGas("CustomGroup", "testSnapshotGasSection"); - flare.run(256); + flare.run(8); // vm.stopSnapshotGas() will use the last snapshot name, even with custom group. uint256 gasUsed = vm.stopSnapshotGas(); @@ -116,7 +113,7 @@ contract GasSnapshotTest is DSTest { function testSnapshotGasSectionName() public { vm.startSnapshotGas("testSnapshotGasSectionName"); - flare.run(256); + flare.run(8); uint256 gasUsed = vm.stopSnapshotGas("testSnapshotGasSectionName"); assertGt(gasUsed, 0); @@ -126,7 +123,7 @@ contract GasSnapshotTest is DSTest { function testSnapshotGasSectionGroupName() public { vm.startSnapshotGas("CustomGroup", "testSnapshotGasSectionGroupName"); - flare.run(256); + flare.run(8); uint256 gasUsed = vm.stopSnapshotGas("CustomGroup", "testSnapshotGasSectionGroupName"); assertGt(gasUsed, 0); @@ -149,9 +146,7 @@ contract GasSnapshotTest is DSTest { } } -contract GasComparisonTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract GasComparisonTest is Test { uint256 public slot0; uint256 public slot1; @@ -263,12 +258,12 @@ contract GasComparisonTest is DSTest { // Start a cheatcode snapshot. vm.startSnapshotGas("ComparisonGroup", "testGasComparisonFlareA"); - flare.run(256); + flare.run(8); uint256 a = vm.stopSnapshotGas(); // Start a comparative Solidity snapshot. _snapStart(); - flare.run(256); + flare.run(8); uint256 b = _snapEnd(); vm.snapshotValue("ComparisonGroup", "testGasComparisonFlareB", b); diff --git a/testdata/paris/cheats/LastCallGas.t.sol b/testdata/paris/cheats/LastCallGas.t.sol index 23f6df224963f..8c2c20ee8c41e 100644 --- a/testdata/paris/cheats/LastCallGas.t.sol +++ b/testdata/paris/cheats/LastCallGas.t.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; +import "utils/Test.sol"; contract Target { uint256 public slot0; @@ -28,8 +27,7 @@ contract Target { fallback() external {} } -abstract contract LastCallGasFixture is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +abstract contract LastCallGasFixture is Test { Target public target; struct Gas { @@ -67,6 +65,7 @@ abstract contract LastCallGasFixture is DSTest { } } +/// forge-config: default.isolate = true contract LastCallGasIsolatedTest is LastCallGasFixture { function testRecordLastCallGas() public { _setup(); diff --git a/testdata/paris/core/BeforeTest.t.sol b/testdata/paris/core/BeforeTest.t.sol index 2b14bcad1d2ef..10acfb53ee88f 100644 --- a/testdata/paris/core/BeforeTest.t.sol +++ b/testdata/paris/core/BeforeTest.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; +import "utils/Test.sol"; contract SelfDestructor { function kill() external { @@ -10,7 +10,7 @@ contract SelfDestructor { } // https://github.com/foundry-rs/foundry/issues/1543 -contract BeforeTestSelfDestructTest is DSTest { +contract BeforeTestSelfDestructTest is Test { SelfDestructor killer; uint256 a; uint256 b; @@ -46,16 +46,18 @@ contract BeforeTestSelfDestructTest is DSTest { function kill_contract() external { uint256 killer_size = getSize(address(killer)); - require(killer_size == 106); + assertEq(killer_size, 106); killer.kill(); + assertEq(killer_size, 106); } - function testKill() public view { + /// forge-config: default.evm_version = "paris" + function testKill() public { uint256 killer_size = getSize(address(killer)); - require(killer_size == 0); + assertEq(killer_size, 0); } - function getSize(address c) public view returns (uint32) { + function getSize(address c) internal view returns (uint32) { uint32 size; assembly { size := extcodesize(c) @@ -64,12 +66,12 @@ contract BeforeTestSelfDestructTest is DSTest { } function testA() public { - require(a <= 3); + assertLe(a, 3); a += 1; } - function testSimpleA() public view { - require(a == 0); + function testSimpleA() public { + assertEq(a, 0); } function setB() public { @@ -77,7 +79,7 @@ contract BeforeTestSelfDestructTest is DSTest { } function testB() public { - require(b == 100); + assertEq(b, 100); } function setBWithValue(uint256 value) public { diff --git a/testdata/paris/fork/Transact.t.sol b/testdata/paris/fork/Transact.t.sol index 92d595f98c516..c543590d6b1be 100644 --- a/testdata/paris/fork/Transact.t.sol +++ b/testdata/paris/fork/Transact.t.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; -import "../../default/logs/console.sol"; +import "utils/Test.sol"; interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); @@ -11,9 +9,7 @@ interface IERC20 { function balanceOf(address account) external view returns (uint256); } -contract TransactOnForkTest is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); - +contract TransactOnForkTest is Test { IERC20 constant USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); event Transfer(address indexed from, address indexed to, uint256 value); diff --git a/testdata/paris/spec/ShanghaiCompat.t.sol b/testdata/paris/spec/ShanghaiCompat.t.sol index fd7213b3d0702..7ff6405d16ccf 100644 --- a/testdata/paris/spec/ShanghaiCompat.t.sol +++ b/testdata/paris/spec/ShanghaiCompat.t.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; -import "ds-test/test.sol"; -import "cheats/Vm.sol"; - -contract ShanghaiCompat is DSTest { - Vm constant vm = Vm(HEVM_ADDRESS); +import "utils/Test.sol"; +contract ShanghaiCompat is Test { function testPush0() public { address target = address(uint160(uint256(0xc4f3))); diff --git a/testdata/default/vyper/Counter.vy b/testdata/src/Counter.vy similarity index 100% rename from testdata/default/vyper/Counter.vy rename to testdata/src/Counter.vy diff --git a/testdata/default/vyper/ICounter.vyi b/testdata/src/ICounter.vyi similarity index 100% rename from testdata/default/vyper/ICounter.vyi rename to testdata/src/ICounter.vyi diff --git a/testdata/lib/ds-test/src/test.sol b/testdata/utils/DSTest.sol similarity index 100% rename from testdata/lib/ds-test/src/test.sol rename to testdata/utils/DSTest.sol diff --git a/testdata/utils/Test.sol b/testdata/utils/Test.sol new file mode 100644 index 0000000000000..6c7930bc3088e --- /dev/null +++ b/testdata/utils/Test.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.6.2 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./DSTest.sol"; +import "./Vm.sol"; +import "./console.sol"; + +contract Test is DSTest { + Vm public constant vm = Vm(HEVM_ADDRESS); +} diff --git a/testdata/cheats/Vm.sol b/testdata/utils/Vm.sol similarity index 100% rename from testdata/cheats/Vm.sol rename to testdata/utils/Vm.sol diff --git a/testdata/default/logs/console.sol b/testdata/utils/console.sol similarity index 100% rename from testdata/default/logs/console.sol rename to testdata/utils/console.sol From 9a02aa8bab55a637472a9d2e1fe57a233f00d66e Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 23:21:36 +0700 Subject: [PATCH 118/374] Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 151 ++++++++++--------------------------- 1 file changed, 39 insertions(+), 112 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e71c1d2f993e3..fc858c4bd6f85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,5 @@ name: test -permissions: {} - on: push: branches: @@ -15,31 +13,23 @@ concurrency: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full - RUSTC_WRAPPER: "sccache" jobs: nextest: uses: ./.github/workflows/nextest.yml - permissions: - contents: read with: profile: default secrets: inherit docs: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@nightly + - uses: Swatinem/rust-cache@v2 with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master - with: - toolchain: nightly - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + cache-on-failure: true - name: Build documentation run: cargo doc --workspace --all-features --no-deps --document-private-items env: @@ -47,161 +37,99 @@ jobs: - name: Setup Pages uses: actions/configure-pages@v5 - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v3 with: path: ./target/doc deploy-docs: if: github.ref_name == 'master' && github.event_name == 'push' + runs-on: ubuntu-latest needs: [docs] - runs-on: depot-ubuntu-latest - timeout-minutes: 5 + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} + timeout-minutes: 30 steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 doctest: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master - with: - toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + cache-on-failure: true - run: cargo test --workspace --doc typos: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 - with: - persist-credentials: false - - uses: crate-ci/typos@83157de2df0fa7c7ae20f73f9dbed44c41f2bb64 # v1 + - uses: crate-ci/typos@v1 clippy: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@clippy + - uses: Swatinem/rust-cache@v2 with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master - with: - toolchain: nightly - components: clippy - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + cache-on-failure: true - run: cargo clippy --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings rustfmt: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@nightly with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master - with: - toolchain: nightly components: rustfmt - run: cargo fmt --all --check forge-fmt: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master - with: - toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + cache-on-failure: true - name: forge fmt shell: bash run: ./.github/scripts/format.sh --check crate-checks: - runs-on: depot-ubuntu-latest - timeout-minutes: 5 - permissions: - contents: read + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master - with: - toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@e43a5023a747770bfcb71ae048541a681714b951 # v2 - with: - tool: cargo-hack - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + cache-on-failure: true - run: cargo hack check deny: - uses: ithacaxyz/ci/.github/workflows/deny.yml@9c8d0dc20e7ad02455d3fdab2378a05f29907630 # main - permissions: - contents: read - - codeql: - name: Analyze (${{ matrix.language }}) - runs-on: ubuntu-latest - permissions: - security-events: write - actions: read - contents: read - strategy: - fail-fast: false - matrix: - include: - - language: actions - build-mode: none - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - persist-credentials: false - - name: Initialize CodeQL - uses: github/codeql-action/init@v4 - with: - languages: ${{ matrix.language }} - build-mode: ${{ matrix.build-mode }} - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 - with: - category: "/language:${{matrix.language}}" + uses: ithacaxyz/ci/.github/workflows/deny.yml@main ci-success: runs-on: ubuntu-latest if: always() - permissions: {} needs: - nextest - docs @@ -212,10 +140,9 @@ jobs: - forge-fmt - crate-checks - deny - - codeql - timeout-minutes: 5 + timeout-minutes: 30 steps: - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1 + uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} From e787d818e5d56fdc15062c697fa57652d1b992f0 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:32:03 +0000 Subject: [PATCH 119/374] Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fc858c4bd6f85..14265f1325db0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -146,3 +146,4 @@ jobs: uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} + From 99056c7fe5ad4e7eea5cc7fbf999c3353b1ca573 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 17 Oct 2025 18:31:21 +0000 Subject: [PATCH 120/374] Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8af345fe28ff..6ce03503a8858 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: docs: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: @@ -56,7 +56,7 @@ jobs: if: github.ref_name == 'master' && github.event_name == 'push' needs: [docs] runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: pages: write id-token: write @@ -70,7 +70,7 @@ jobs: doctest: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: @@ -86,18 +86,18 @@ jobs: typos: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: - uses: actions/checkout@v5 with: persist-credentials: false - - uses: crate-ci/typos@83157de2df0fa7c7ae20f73f9dbed44c41f2bb64 # v1 + - uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1 # v1 clippy: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: @@ -116,7 +116,7 @@ jobs: rustfmt: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: @@ -131,7 +131,7 @@ jobs: forge-fmt: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: @@ -149,7 +149,7 @@ jobs: crate-checks: runs-on: depot-ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 30 permissions: contents: read steps: @@ -160,7 +160,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@e43a5023a747770bfcb71ae048541a681714b951 # v2 + - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 @@ -214,7 +214,7 @@ jobs: - crate-checks - deny - codeql - timeout-minutes: 5 + timeout-minutes: 30 steps: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1 From 243077e8a2bbc64b90416152d1ac221d05f19203 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 19 Oct 2025 01:51:58 +0700 Subject: [PATCH 121/374] Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/ci.yml diff --git a/.circleci/ci.yml b/.circleci/ci.yml deleted file mode 100644 index f967cfaa30db5..0000000000000 --- a/.circleci/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello From ac329187c3edb9299d681d9fd64e3df0a8e65580 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 18 Oct 2025 22:05:55 +0000 Subject: [PATCH 122/374] Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml index 46a18d45a5fca..d7a82b5c93b6e 100644 --- a/.circleci/cargo.yml +++ b/.circleci/cargo.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.89.0 steps: - checkout - restore_cache: From 081909d7ec8c854adf4a34a5d7d1d18b294a93c6 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 19 Oct 2025 01:28:24 +0000 Subject: [PATCH 123/374] Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml index 46a18d45a5fca..d7a82b5c93b6e 100644 --- a/.circleci/cargo.yml +++ b/.circleci/cargo.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.89.0 steps: - checkout - restore_cache: From 2c1f1f9ee1e9e894dc2df4ab4d18b8fa7b810596 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:42:58 +0000 Subject: [PATCH 124/374] Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_v1.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/ci_v1.yml diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 94bf46b3bb04f..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test From b1d84b66b81a035c156dfd555df2e50cd4c4d8d6 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:06:59 +0000 Subject: [PATCH 125/374] Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> From 7de78fb9fc7cead569c81669e74d36941b6bbd2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 21:42:52 +0700 Subject: [PATCH 126/374] chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 447e61f3a0afa..8a1ab60296d24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -166,7 +166,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@e7ef886cf8f69c25ecef6bbc2858a42e273496ec # v2 + - uses: taiki-e/install-action@e43a5023a747770bfcb71ae048541a681714b951 # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 From a737afa7975a5942ac7da09ddabeb80247bc5038 Mon Sep 17 00:00:00 2001 From: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:05:18 +0700 Subject: [PATCH 127/374] Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. --- .circleci/cargo.yml | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index 46a18d45a5fca..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test From c56008ed7b73e17973d518036a2fb7117b0d9230 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:17:06 +0000 Subject: [PATCH 128/374] Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index d7a82b5c93b6e..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test From 75c3b25338c8c161703893b627d08fe90bb4b8ad Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:47:06 +0000 Subject: [PATCH 129/374] Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index d7a82b5c93b6e..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test From e3ae5bef386108acd6f65ec292468016c38213dc Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:27:46 +0000 Subject: [PATCH 130/374] Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_v1.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/ci_v1.yml diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 94bf46b3bb04f..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test From 956581c9997b1f362083c74c91cb5d5d5de8fe44 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:29:35 +0000 Subject: [PATCH 131/374] Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f967cfaa30db5..df6b1216062af 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,6 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/reference/configuration-reference version: 2.1 - # Define a job to be invoked later in a workflow. # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs jobs: From 0ba70d2cd676df26ef1bb1da45cdb0dbc22e05f0 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:24:31 +0000 Subject: [PATCH 132/374] Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index df6b1216062af..1b131002534c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,5 +1,6 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/reference/configuration-reference + version: 2.1 # Define a job to be invoked later in a workflow. # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs From f6a1abeffd51ef2dd63204cfdba28c93c583cce4 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:49:42 +0000 Subject: [PATCH 133/374] Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index f967cfaa30db5..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello From 58865b9bc8090e4aa448653074372d36c36889ee Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Oct 2025 19:50:32 +0700 Subject: [PATCH 134/374] Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 1b131002534c9..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference - -version: 2.1 -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello From e060526516fb805f9d3be6c38bb99811c0c80447 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:42:40 +0000 Subject: [PATCH 135/374] Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> From 6ff6e5954ec7ed348c745f2f4ab19d3bec7e2371 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:43:12 +0000 Subject: [PATCH 136/374] Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_v1.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml index 94bf46b3bb04f..82c6de5b42b73 100644 --- a/.circleci/ci_v1.yml +++ b/.circleci/ci_v1.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.89.0 steps: - checkout - restore_cache: From 1b37bfc8cc836fb459cf8a4dfa30ee9f2ec25a84 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:04:59 +0700 Subject: [PATCH 137/374] Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/npm.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 79f9c45604119..b410a80d7d11f 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -162,10 +162,11 @@ jobs: PLATFORM_NAME=${{ matrix.os }} ARCH=${{ matrix.arch }} FORGE_BIN_PATH="$BIN" bun ./scripts/prepublish.ts - name: Sanity Check Binary - working-directory: ./npm + env: + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} run: | set -euo pipefail - PKG_DIR="./@foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }}" + PKG_DIR="$ARTIFACT_DIR/@foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }}" BIN="$PKG_DIR/bin/forge" if [[ "${{ matrix.os }}" == "win32" ]]; then BIN="$PKG_DIR/bin/forge.exe" @@ -185,7 +186,7 @@ jobs: fi - name: Publish ${{ matrix.os }}-${{ matrix.arch }} Binary - working-directory: ./npm + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} env: PROVENANCE: true VERSION_NAME: ${{ steps.release-version.outputs.RELEASE_VERSION }} @@ -195,9 +196,9 @@ jobs: run: | set -euo pipefail - ls -la ./@foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }} + ls -la "$ARTIFACT_DIR/@foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }}" - bun ./scripts/publish.ts ./@foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }} + bun ./scripts/publish.ts "$ARTIFACT_DIR/@foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }}" echo "Published @foundry-rs/forge-${{ matrix.os }}-${{ matrix.arch }}" From 0b4d4dfb29a34884bd6ab7d651a3e5342293da0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:42:21 +0000 Subject: [PATCH 138/374] chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] --- Cargo.lock | 88 ++++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 2 +- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e972d3906b5dc..830ba11d18833 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,16 +130,15 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cc14d832bc3331ca22a1c7819de1ede99f58f61a7d123952af7dde8de124a6" +checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-type-parser", "alloy-sol-types", "arbitrary", - "derive_arbitrary", "derive_more 2.0.1", "itoa", "proptest", @@ -286,9 +285,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ccaa79753d7bf15f06399ea76922afbfaf8d18bebed9e8fc452984b4a90dcc9" +checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -378,20 +377,19 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c35fc4b03ace65001676358ffbbaefe2a2b27ee50fe777c345082c7c888be8" +checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" dependencies = [ "alloy-rlp", "arbitrary", "bytes", "cfg-if", "const-hex", - "derive_arbitrary", "derive_more 2.0.1", - "foldhash", + "foldhash 0.2.0", "getrandom 0.3.3", - "hashbrown 0.15.3", + "hashbrown 0.16.0", "indexmap 2.9.0", "itoa", "k256", @@ -769,9 +767,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8612e0658964d616344f199ab251a49d48113992d81b92dab93ed855faa66383" +checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -783,9 +781,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a384edac7283bc4c010a355fb648082860c04b826bb7a814c45263c8f304c74" +checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -802,9 +800,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd588c2d516da7deb421b8c166dc60b7ae31bca5beea29ab6621fcfa53d6ca5" +checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ "alloy-json-abi", "const-hex", @@ -820,9 +818,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86ddeb70792c7ceaad23e57d52250107ebbb86733e52f4a25d8dc1abc931837" +checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" dependencies = [ "serde", "winnow", @@ -830,9 +828,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584cb97bfc5746cb9dcc4def77da11694b5d6d7339be91b7480a6a68dc129387" +checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3803,6 +3801,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "forge" version = "1.2.1" @@ -5008,7 +5012,16 @@ checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "foldhash 0.2.0", "serde", ] @@ -7138,9 +7151,9 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", @@ -7849,14 +7862,15 @@ dependencies = [ [[package]] name = "ruint" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "alloy-rlp", "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", + "ark-ff 0.5.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -7870,7 +7884,7 @@ dependencies = [ "rand 0.9.1", "rlp", "ruint-macro", - "serde", + "serde_core", "valuable", "zeroize", ] @@ -8270,18 +8284,28 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -9068,9 +9092,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d879005cc1b5ba4e18665be9e9501d9da3a9b95f625497c4cb7ee082b532e" +checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 76f51d5337151..68e6e2881ff7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -232,7 +232,7 @@ alloy-transport-ipc = { version = "1.0.5", default-features = false } alloy-transport-ws = { version = "1.0.5", default-features = false } ## alloy-core -alloy-dyn-abi = "1.0" +alloy-dyn-abi = "1.4" alloy-json-abi = "1.0" alloy-primitives = { version = "1.0", features = [ "getrandom", From 635d4360f35cecb84e4b3d72036fa63513c5b44a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:42:21 +0000 Subject: [PATCH 139/374] chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] --- Cargo.lock | 88 ++++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 2 +- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e972d3906b5dc..830ba11d18833 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,16 +130,15 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cc14d832bc3331ca22a1c7819de1ede99f58f61a7d123952af7dde8de124a6" +checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-type-parser", "alloy-sol-types", "arbitrary", - "derive_arbitrary", "derive_more 2.0.1", "itoa", "proptest", @@ -286,9 +285,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ccaa79753d7bf15f06399ea76922afbfaf8d18bebed9e8fc452984b4a90dcc9" +checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -378,20 +377,19 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c35fc4b03ace65001676358ffbbaefe2a2b27ee50fe777c345082c7c888be8" +checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" dependencies = [ "alloy-rlp", "arbitrary", "bytes", "cfg-if", "const-hex", - "derive_arbitrary", "derive_more 2.0.1", - "foldhash", + "foldhash 0.2.0", "getrandom 0.3.3", - "hashbrown 0.15.3", + "hashbrown 0.16.0", "indexmap 2.9.0", "itoa", "k256", @@ -769,9 +767,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8612e0658964d616344f199ab251a49d48113992d81b92dab93ed855faa66383" +checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -783,9 +781,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a384edac7283bc4c010a355fb648082860c04b826bb7a814c45263c8f304c74" +checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -802,9 +800,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd588c2d516da7deb421b8c166dc60b7ae31bca5beea29ab6621fcfa53d6ca5" +checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ "alloy-json-abi", "const-hex", @@ -820,9 +818,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86ddeb70792c7ceaad23e57d52250107ebbb86733e52f4a25d8dc1abc931837" +checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" dependencies = [ "serde", "winnow", @@ -830,9 +828,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584cb97bfc5746cb9dcc4def77da11694b5d6d7339be91b7480a6a68dc129387" +checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3803,6 +3801,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "forge" version = "1.2.1" @@ -5008,7 +5012,16 @@ checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "foldhash 0.2.0", "serde", ] @@ -7138,9 +7151,9 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", @@ -7849,14 +7862,15 @@ dependencies = [ [[package]] name = "ruint" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "alloy-rlp", "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", + "ark-ff 0.5.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -7870,7 +7884,7 @@ dependencies = [ "rand 0.9.1", "rlp", "ruint-macro", - "serde", + "serde_core", "valuable", "zeroize", ] @@ -8270,18 +8284,28 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -9068,9 +9092,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d879005cc1b5ba4e18665be9e9501d9da3a9b95f625497c4cb7ee082b532e" +checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 76f51d5337151..68e6e2881ff7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -232,7 +232,7 @@ alloy-transport-ipc = { version = "1.0.5", default-features = false } alloy-transport-ws = { version = "1.0.5", default-features = false } ## alloy-core -alloy-dyn-abi = "1.0" +alloy-dyn-abi = "1.4" alloy-json-abi = "1.0" alloy-primitives = { version = "1.0", features = [ "getrandom", From 14f26f52dbe20e3ad1e33723e6a492a7adf2fc76 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Oct 2025 23:44:17 +0700 Subject: [PATCH 140/374] Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci-web3-gamefi.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .circleci/ci-web3-gamefi.yml diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- From 445d5cf19414cf6cf0ba026aba5a946ba5377ae1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 00:31:56 +0700 Subject: [PATCH 141/374] Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/ci.yml b/.circleci/ci.yml index 7293433a50f2d..9a32db6188818 100644 --- a/.circleci/ci.yml +++ b/.circleci/ci.yml @@ -1,5 +1,4 @@ version: 2.1 - jobs: build-and-test: docker: From b3bbd1ef5c91bbe27b1f3ba1c3f0886afd3316a8 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:35:25 +0700 Subject: [PATCH 142/374] Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/npm.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 79f9c45604119..60c1fe01c64e1 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -98,6 +98,17 @@ jobs: node-version: "24" registry-url: "https://registry.npmjs.org" + - name: Validate npm directory for artifact poisoning + run: | + set -euo pipefail + # Disallow dangerous files likely to be poisoned by artifacts + for f in "package.json" "bun.lockb" "package-lock.json" "yarn.lock" ".npmrc"; do + if [ -f "./npm/$f" ]; then + echo "ERROR: Untrusted '$f' present in ./npm – aborting to prevent artifact poisoning." + exit 1 + fi + done + - name: Install Dependencies working-directory: ./npm run: bun install --frozen-lockfile From e101ade3d5caa414dc80ef96f69740c17bf08790 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:35:55 +0700 Subject: [PATCH 143/374] Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- benches/src/lib.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/benches/src/lib.rs b/benches/src/lib.rs index e123988861691..500eef320deef 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -130,10 +130,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } From d96b67de4b62a28833f79ae0211420616c8ed44d Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:38:59 +0700 Subject: [PATCH 144/374] Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/evm/evm/src/executors/corpus.rs | 33 ++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/crates/evm/evm/src/executors/corpus.rs b/crates/evm/evm/src/executors/corpus.rs index 4577c9788d708..e4de7932f55f4 100644 --- a/crates/evm/evm/src/executors/corpus.rs +++ b/crates/evm/evm/src/executors/corpus.rs @@ -190,6 +190,15 @@ impl CorpusManager { foundry_common::fs::create_dir_all(corpus_dir)?; } + // Canonicalize the corpus_dir to a trusted absolute path + let canonical_corpus_dir = match corpus_dir.canonicalize() { + Ok(dir) => dir, + Err(e) => { + trace!(target: "corpus", "failed to canonicalize corpus_dir {}: {e}", corpus_dir.display()); + return Err(e.into()); + } + }; + let can_replay_tx = |tx: &BasicTxDetails| -> bool { fuzzed_contracts.is_some_and(|contracts| contracts.targets.lock().can_replay(tx)) || fuzzed_function.is_some_and(|function| { @@ -202,21 +211,31 @@ impl CorpusManager { 'corpus_replay: for entry in std::fs::read_dir(corpus_dir)? { let path = entry?.path(); - if path.is_file() - && let Some(name) = path.file_name().and_then(|s| s.to_str()) + // Canonicalize the candidate path, skip if it cannot be canonicalized (e.g. broken symlink) + let canonical_path = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, + }; + // Ensure file is inside the corpus directory (prevents path traversal/symlink escape) + if !canonical_path.starts_with(&canonical_corpus_dir) { + trace!(target: "corpus", "Skipping file outside corpus_dir: {}", path.display()); + continue; + } + if canonical_path.is_file() + && let Some(name) = canonical_path.file_name().and_then(|s| s.to_str()) && name.contains(METADATA_SUFFIX) { // Ignore metadata files continue; } - let read_corpus_result = match path.extension().and_then(|ext| ext.to_str()) { - Some("gz") => foundry_common::fs::read_json_gzip_file::>(&path), - _ => foundry_common::fs::read_json_file::>(&path), + let read_corpus_result = match canonical_path.extension().and_then(|ext| ext.to_str()) { + Some("gz") => foundry_common::fs::read_json_gzip_file::>(&canonical_path), + _ => foundry_common::fs::read_json_file::>(&canonical_path), }; let Ok(tx_seq) = read_corpus_result else { - trace!(target: "corpus", "failed to load corpus from {}", path.display()); + trace!(target: "corpus", "failed to load corpus from {}", canonical_path.display()); continue; }; @@ -265,7 +284,7 @@ impl CorpusManager { ); // Populate in memory corpus with the sequence from corpus file. - in_memory_corpus.push(CorpusEntry::new(tx_seq, path)?); + in_memory_corpus.push(CorpusEntry::new(tx_seq, canonical_path.clone())?); } } From 3011ba593af52c4ce3ae1090d04d44d67b9d5ff9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:40:44 +0700 Subject: [PATCH 145/374] Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/npm.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 60c1fe01c64e1..d20eb24129462 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -87,6 +87,37 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id || inputs.run_id }} + - name: Validate and Copy Artifacts + env: + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} + run: | + set -euo pipefail + # List expected artifacts and their paths + EXPECTED_PATHS=( + "@foundry-rs/forge-linux-amd64" + "@foundry-rs/forge-linux-arm64" + "@foundry-rs/forge-darwin-amd64" + "@foundry-rs/forge-darwin-arm64" + "@foundry-rs/forge-win32-amd64" + ) + DEST_DIR="./npm" + mkdir -p "$DEST_DIR" + for PKG in "${EXPECTED_PATHS[@]}"; do + SRC="$ARTIFACT_DIR/$PKG" + if [ -d "$SRC" ]; then + echo "Validating and copying $SRC to $DEST_DIR" + # Optionally: validate files inside $SRC (e.g., hashes, signatures, expected file names) + # Basic check: ensure correct file structure + find "$SRC" -type f | grep -E '/bin/(forge|forge.exe)$' || { + echo "ERROR: Expected binary not found in $SRC" >&2 + exit 1 + } + cp -a "$SRC" "$DEST_DIR/" + else + echo "WARNING: Expected package $SRC not found in artifacts." + fi + done + ls -l "$DEST_DIR" - name: Setup Bun uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 with: @@ -196,6 +227,7 @@ jobs: fi - name: Publish ${{ matrix.os }}-${{ matrix.arch }} Binary + # Only use ./npm, which now contains validated/copies of artifact files working-directory: ./npm env: PROVENANCE: true From 321c2023ff954e2f0a99ad9d832ac2780dfaf872 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:41:19 +0700 Subject: [PATCH 146/374] Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 839a3bfe8b224..e1f5d83e46408 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -123,6 +123,18 @@ impl ScriptTester { for entry in fs::read_dir(&from_dir)? { let file = &entry?.path(); let name = file.file_name().unwrap(); + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Optionally verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } fs::copy(file, to_dir.join(name))?; } Ok(()) From 1bc0b5d5dd1ecfe05681f3b06e8faadc0c0a3ab5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:41:54 +0700 Subject: [PATCH 147/374] Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- npm/src/const.ts | 25 ++++++++++++++++++++++++- npm/src/install.ts | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/npm/src/const.ts b/npm/src/const.ts index 6762a149de196..1f0e32ab5d4fb 100644 --- a/npm/src/const.ts +++ b/npm/src/const.ts @@ -1,13 +1,36 @@ import type * as Process from 'node:process' + +// Allow-list of recognized/approved registry hostnames +export const ALLOWED_REGISTRY_HOSTS = [ + 'registry.npmjs.org', + 'localhost', + '127.0.0.1', + '::1', + // Add any additional trusted registry hosts below as needed +] + export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const rawUrl = ( process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' ) + let hostname + try { + hostname = new URL(rawUrl).hostname + } catch { + throw new Error(`Invalid registry URL: ${rawUrl}`) + } + if (!ALLOWED_REGISTRY_HOSTS.includes(hostname)) { + throw new Error( + `Registry URL host "${hostname}" is not in the allow-list (${ALLOWED_REGISTRY_HOSTS.join(', ')}). ` + + 'Set your registry to a trusted host.' + ) + } + return rawUrl } export type Architecture = Extract<(typeof Process)['arch'], 'arm64' | 'x64'> diff --git a/npm/src/install.ts b/npm/src/install.ts index a89fe5403571c..f637106a3e452 100644 --- a/npm/src/install.ts +++ b/npm/src/install.ts @@ -1,4 +1,4 @@ -import { BINARY_NAME, colors, getRegistryUrl, PLATFORM_SPECIFIC_PACKAGE_NAME } from '#const.ts' +import { BINARY_NAME, colors, getRegistryUrl, PLATFORM_SPECIFIC_PACKAGE_NAME, ALLOWED_REGISTRY_HOSTS } from '#const.ts' import * as NodeCrypto from 'node:crypto' import * as NodeFS from 'node:fs' import * as NodeHttp from 'node:http' From a970332c912f39c25246465f75ae4fb81a5e9bed Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:42:45 +0700 Subject: [PATCH 148/374] Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- npm/src/const.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/npm/src/const.ts b/npm/src/const.ts index 6762a149de196..42a352712c6ba 100644 --- a/npm/src/const.ts +++ b/npm/src/const.ts @@ -1,13 +1,34 @@ import type * as Process from 'node:process' +const ALLOWED_REGISTRY_HOSTS = [ + 'registry.npmjs.org', + 'registry.yarnpkg.com', + // Add any additional trusted registry domains here +] + +function isAllowedRegistryHostname(urlString: string): boolean { + try { + const url = new URL(urlString) + // Compare against allow-list. Consider only exact domain match for safety. + return ALLOWED_REGISTRY_HOSTS.includes(url.hostname) + } catch { + return false + } +} + export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const candidate = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + if (!isAllowedRegistryHostname(candidate)) { + throw new Error( + `Refusing to use registry URL '${candidate}' not in allowed list: ${ALLOWED_REGISTRY_HOSTS.join(', ')}` + ) + } + return candidate } export type Architecture = Extract<(typeof Process)['arch'], 'arm64' | 'x64'> From 1cf6991bb1b82fd10018efbe4ddbb31c001b9010 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 09:24:29 +0000 Subject: [PATCH 149/374] Create codeql.yml (#208) --- .github/workflows/codeql.yml | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..63c774765c2b6 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,105 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '35 4 * * 6' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none + - language: rust + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" From 51d92e156cc3e15a605ebc08307d607f1b122fbe Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:16:27 +0000 Subject: [PATCH 150/374] Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. --- .circleci/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/ci.yml b/.circleci/ci.yml index 7293433a50f2d..e5e4c033b1e82 100644 --- a/.circleci/ci.yml +++ b/.circleci/ci.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.89.0 steps: - checkout - restore_cache: From ce554bee23a64865b45c5321dd4afef99d10e1d6 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:58:37 +0000 Subject: [PATCH 151/374] Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml index 46a18d45a5fca..a8a81c7bbd986 100644 --- a/.circleci/cargo.yml +++ b/.circleci/cargo.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.87.0 steps: - checkout - restore_cache: From ae8b96f84fc30556907a5f9d210ec43845f0f599 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 11:37:24 +0000 Subject: [PATCH 152/374] Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/ci.yml b/.circleci/ci.yml index 9a32db6188818..1b5df6d6e668e 100644 --- a/.circleci/ci.yml +++ b/.circleci/ci.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.89.0 steps: - checkout - restore_cache: From f68ca66b074031df1f82aada59917b8b71349e9e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Oct 2025 11:39:16 +0000 Subject: [PATCH 153/374] Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 04f996a346ab7..ce463545d7b10 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,11 +8,11 @@ executors: - image: cimg/base:stable auth: # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/hardhat-project/environment-variables + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables username: $DOCKER_HUB_USER password: $DOCKER_HUB_PASSWORD jobs: - web3-defi-game-project-: + web3-defi-game-project: executor: my-custom-executor steps: @@ -22,4 +22,4 @@ jobs: workflows: my-custom-workflow: jobs: - - web3-defi-game-project- + - web3-defi-game-project From 11e6a75587787c0d6d00539b209dc98c378146e1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Oct 2025 05:16:46 +0000 Subject: [PATCH 154/374] Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .circleci/cargo.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test From 647b198634605b36717e132d68c8c4ecdfc4fa9b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 26 Oct 2025 04:55:38 +0000 Subject: [PATCH 155/374] Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 145 +++++++++++++--------------------- 1 file changed, 55 insertions(+), 90 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06061ff6c7508..1b18d4ad1dd2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,5 @@ name: release -permissions: {} - on: push: tags: @@ -17,35 +15,31 @@ env: CARGO_TERM_COLOR: always IS_NIGHTLY: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} PROFILE: maxperf - STABLE_VERSION: "v1.3.6" + STABLE_VERSION: "v1.1.0" jobs: prepare: name: Prepare release runs-on: ubuntu-latest timeout-minutes: 30 - permissions: - contents: write - pull-requests: read 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@v5 + - uses: actions/checkout@v4 with: - persist-credentials: false fetch-depth: 0 - name: Compute release name and tag id: release_info run: | if [[ ${IS_NIGHTLY} == 'true' ]]; then - printf 'tag_name=%s\n' "nightly-${GITHUB_SHA}" >> "$GITHUB_OUTPUT" - printf 'release_name=%s\n' "Nightly ($(date '+%Y-%m-%d'))" >> "$GITHUB_OUTPUT" + echo "tag_name=nightly-${GITHUB_SHA}" >> $GITHUB_OUTPUT + echo "release_name=Nightly ($(date '+%Y-%m-%d'))" >> $GITHUB_OUTPUT else - printf 'tag_name=%s\n' "$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT" - printf 'release_name=%s\n' "$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT" + 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 @@ -54,7 +48,7 @@ jobs: # the changelog. - name: Create build-specific nightly tag if: ${{ env.IS_NIGHTLY == 'true' }} - uses: actions/github-script@v8 + uses: actions/github-script@v7 env: TAG_NAME: ${{ steps.release_info.outputs.tag_name }} with: @@ -64,7 +58,7 @@ jobs: - name: Build changelog id: build_changelog - uses: mikepenz/release-changelog-builder-action@d702b5bb7c23735c8afc130dac9c4c8b8eb669e8 # v6 + uses: mikepenz/release-changelog-builder-action@v4 with: configuration: "./.github/changelog.json" fromTag: ${{ env.IS_NIGHTLY == 'true' && 'nightly' || env.STABLE_VERSION }} @@ -76,10 +70,6 @@ jobs: name: Release Docker needs: prepare uses: ./.github/workflows/docker-publish.yml - permissions: - contents: read - id-token: write - packages: write with: tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -100,27 +90,28 @@ jobs: # `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 - # These are pinned to the oldest runner versions to support old libc/SDK versions. - - runner: depot-ubuntu-22.04-16 + - runner: Linux-22.04 target: x86_64-unknown-linux-gnu svm_target_platform: linux-amd64 platform: linux arch: amd64 - - runner: depot-ubuntu-22.04-16 + - runner: Linux-22.04 target: x86_64-unknown-linux-musl svm_target_platform: linux-amd64 platform: alpine arch: amd64 - - runner: depot-ubuntu-22.04-arm-16 + - runner: Linux-22.04 target: aarch64-unknown-linux-gnu svm_target_platform: linux-aarch64 platform: linux arch: arm64 - - runner: depot-ubuntu-22.04-16 + - runner: Linux-22.04 target: aarch64-unknown-linux-musl svm_target_platform: linux-aarch64 platform: alpine arch: arm64 + # This is pinned to `macos-13-large` to support old SDK versions. + # If the runner is deprecated it should be pinned to the oldest available version of the runner. - runner: macos-13-large target: x86_64-apple-darwin svm_target_platform: macosx-amd64 @@ -131,31 +122,31 @@ jobs: svm_target_platform: macosx-aarch64 platform: darwin arch: arm64 - - runner: depot-windows-latest-16 + - runner: Windows target: x86_64-pc-windows-msvc svm_target_platform: windows-amd64 platform: win32 arch: amd64 steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable targets: ${{ matrix.target }} - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + cache-on-failure: true - name: Apple M1 setup if: matrix.target == 'aarch64-apple-darwin' run: | - printf 'SDKROOT=%s\n' "$(xcrun -sdk macosx --show-sdk-path)" >> "$GITHUB_ENV" - printf 'MACOSX_DEPLOYMENT_TARGET=%s\n' "$(xcrun -sdk macosx --show-sdk-platform-version)" >> "$GITHUB_ENV" + echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)" >> $GITHUB_ENV + echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> $GITHUB_ENV - name: cross setup - if: contains(matrix.target, 'musl') + if: matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-gnu' run: | - cargo install cross --git https://github.com/cross-rs/cross --rev baf457efc2555225af47963475bd70e8d2f5993f + cargo install cross - name: Build binaries env: @@ -167,8 +158,8 @@ jobs: shell: bash run: | set -eo pipefail - flags=(--target $TARGET --profile $PROFILE --bins - --no-default-features --features aws-kms,gcp-kms,cli,asm-keccak,js-tracer) + flags=(--target $TARGET --profile $PROFILE --bins + --no-default-features --features aws-kms,gcp-kms,cli,asm-keccak) # `jemalloc` is not fully supported on MSVC or aarch64 Linux. if [[ "$TARGET" != *msvc* && "$TARGET" != "aarch64-unknown-linux-gnu" ]]; then @@ -177,7 +168,7 @@ jobs: [[ "$TARGET" == *windows* ]] && ext=".exe" - if [[ "$TARGET" == *-musl ]]; then + if [[ "$TARGET" == *-musl || "$TARGET" == "aarch64-unknown-linux-gnu" ]]; then cross build "${flags[@]}" else cargo build "${flags[@]}" @@ -185,13 +176,13 @@ jobs: bins=(anvil cast chisel forge) for name in "${bins[@]}"; do - bin="$OUT_DIR/$name$ext" - printf '\n' + bin=$OUT_DIR/$name$ext + echo "" file "$bin" || true du -h "$bin" || true ldd "$bin" || true $bin --version || true - printf '%s_bin_path=%s\n' "$name" "$bin" >> "$GITHUB_ENV" + echo "${name}_bin_path=${bin}" >> $GITHUB_ENV done - name: Archive binaries @@ -204,27 +195,19 @@ jobs: shell: bash run: | if [[ "$PLATFORM_NAME" == "linux" || "$PLATFORM_NAME" == "alpine" ]]; then - tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C "$OUT_DIR" forge cast anvil chisel - printf "file_name=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> "$GITHUB_OUTPUT" + tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C $OUT_DIR 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 "$OUT_DIR" forge cast anvil chisel - printf "file_name=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> "$GITHUB_OUTPUT" + gtar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C $OUT_DIR forge cast anvil chisel + echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT else - cd "$OUT_DIR" + cd $OUT_DIR 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" ../../../ - printf "file_name=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> "$GITHUB_OUTPUT" + echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> $GITHUB_OUTPUT fi - printf "foundry_attestation=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.attestation.txt" >> "$GITHUB_OUTPUT" - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - retention-days: 1 - name: ${{ steps.artifacts.outputs.file_name }} - path: ${{ steps.artifacts.outputs.file_name }} - name: Build man page id: man @@ -244,30 +227,11 @@ jobs: 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 - printf 'foundry_man=%s\n' "foundry_man_${VERSION_NAME}.tar.gz" >> "$GITHUB_OUTPUT" - - - name: Binaries attestation - id: attestation - uses: actions/attest-build-provenance@v3 - with: - subject-path: | - ${{ env.anvil_bin_path }} - ${{ env.cast_bin_path }} - ${{ env.chisel_bin_path }} - ${{ env.forge_bin_path }} - - - name: Record attestation URL - env: - ATTESTATION_URL: ${{ steps.attestation.outputs.attestation-url }} - FOUNDRY_ATTESTATION: ${{ steps.artifacts.outputs.foundry_attestation }} - shell: bash - run: | - set -euo pipefail - printf '%s\n' "$ATTESTATION_URL" > "$FOUNDRY_ATTESTATION" + 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@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 + uses: softprops/action-gh-release@v2 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -275,14 +239,22 @@ jobs: body: ${{ needs.prepare.outputs.changelog }} files: | ${{ steps.artifacts.outputs.file_name }} - ${{ steps.artifacts.outputs.foundry_attestation }} ${{ steps.man.outputs.foundry_man }} + - name: Binaries attestation + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + ${{ env.anvil_bin_path }} + ${{ env.cast_bin_path }} + ${{ env.chisel_bin_path }} + ${{ env.forge_bin_path }} + # 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 == 'true' }} - uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 + uses: softprops/action-gh-release@v2 with: name: "Nightly" tag_name: "nightly" @@ -290,33 +262,28 @@ jobs: body: ${{ needs.prepare.outputs.changelog }} files: | ${{ steps.artifacts.outputs.file_name }} - ${{ steps.artifacts.outputs.foundry_attestation }} ${{ steps.man.outputs.foundry_man }} cleanup: name: Release cleanup runs-on: ubuntu-latest timeout-minutes: 30 - permissions: - contents: write needs: release if: always() steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false + - uses: actions/checkout@v4 # Moves the `nightly` tag to `HEAD` - name: Move nightly tag if: ${{ env.IS_NIGHTLY == 'true' }} - uses: actions/github-script@v8 + 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@v8 + uses: actions/github-script@v7 with: script: | const prunePrereleases = require('./.github/scripts/prune-prereleases.js') @@ -329,13 +296,11 @@ jobs: needs: [prepare, release-docker, release, cleanup] if: failure() permissions: - contents: read issues: write + contents: read steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2 + - uses: actions/checkout@v4 + - uses: JasonEtco/create-an-issue@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} WORKFLOW_URL: | From 99d05916a91fc18c65bd20a60e585ace627d72c9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 26 Oct 2025 15:23:58 +0000 Subject: [PATCH 156/374] Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 21 ------ .github/workflows/docker.yml | 100 +++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 21 deletions(-) delete mode 100644 .github/workflows/docker-image.yml create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index e0c9c518e8748..0000000000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -permissions: - contents: read - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..e994f94e7085c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,100 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '21 12 * * *' + push: + branches: [ "master" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "master" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + - name: Build the Docker image + run: docker build . --file path/to/Dockerfile --tag my-image-name:$(date +%s) + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v5.0.0 + with: + context: ./ + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From 130352fbd0745d9ce6fded0910407b079ee0a0a3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 27 Oct 2025 05:08:28 +0000 Subject: [PATCH 157/374] Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci-web3-gamefi.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .circleci/ci-web3-gamefi.yml diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- From c89ea92d11c3401bdb2f40c2a06d7f1d749bcf94 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:59:11 +0700 Subject: [PATCH 158/374] Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker.yml | 100 +++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..e994f94e7085c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,100 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '21 12 * * *' + push: + branches: [ "master" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "master" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + - name: Build the Docker image + run: docker build . --file path/to/Dockerfile --tag my-image-name:$(date +%s) + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v5.0.0 + with: + context: ./ + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From ee6ba459e054c412b1aa90cf1975c33284b2476e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 29 Oct 2025 21:29:14 +0700 Subject: [PATCH 159/374] Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/ci.yml b/.circleci/ci.yml index 7293433a50f2d..0d184dfcdec13 100644 --- a/.circleci/ci.yml +++ b/.circleci/ci.yml @@ -1,5 +1,5 @@ version: 2.1 - +# jobs: build-and-test: docker: From 4a51626d2317289955d6bbd82bed064074a87991 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 31 Oct 2025 07:50:14 +0700 Subject: [PATCH 160/374] Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/docker-image.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000000000..e0c9c518e8748 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,21 @@ +name: Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From f4ae42a12d4f5119710877ea3c5830578e6d744b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:37:07 +0700 Subject: [PATCH 161/374] Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 76b2889f1c4b2..a622410e8211b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/configuration-reference version: 2.1 - +# # Define a job to be invoked later in a workflow. # See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs jobs: @@ -12,7 +12,7 @@ jobs: # Specify the version you desire here # See: https://circleci.com/developer/images/image/cimg/base - image: cimg/base:current - + # # Add steps to the job # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps steps: @@ -21,7 +21,7 @@ jobs: - run: name: "Say hello" command: "echo Hello, World!" - +# # Orchestrate jobs using workflows # See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows workflows: From 64546a91d35ddaeac83810303e006cd18928d21a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:39:16 +0700 Subject: [PATCH 162/374] Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/script-sequence/src/sequence.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index 5759e7cf15208..143f6b3e9e8a1 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -12,7 +12,7 @@ use std::{ path::PathBuf, time::{Duration, SystemTime, UNIX_EPOCH}, }; - +# pub const DRY_RUN_DIR: &str = "dry-run"; #[derive(Clone, Serialize, Deserialize)] @@ -20,7 +20,7 @@ pub struct NestedValue { pub internal_type: String, pub value: String, } - +# /// Helper that saves the transactions sequence and its state on which transactions have been /// broadcasted #[derive(Clone, Default, Serialize, Deserialize)] @@ -148,7 +148,7 @@ impl ScriptSequence { 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_by_key(|r| (r.block_number, r.transaction_index)); @@ -164,7 +164,7 @@ impl ScriptSequence { pub fn remove_pending(&mut self, tx_hash: TxHash) { self.pending.retain(|element| element != &tx_hash); } - + # /// Gets paths in the formats /// `./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json` and /// `./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json`. @@ -218,7 +218,7 @@ impl ScriptSequence { .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); } } - +# /// Converts the `sig` argument into the corresponding file path. /// /// This accepts either the signature of the function or the raw calldata. From ecfc99bf2353dfdcfb24d68a39631b1601ed69f9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 31 Oct 2025 12:28:08 +0700 Subject: [PATCH 163/374] Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .github/workflows/dependencies.yml | 52 ++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index d3ec66f9d214d..0e51587b94623 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -2,18 +2,56 @@ name: dependencies -permissions: {} - on: schedule: - - cron: "0 0 * * SUN" # Run weekly on Sundays at midnight UTC - workflow_dispatch: # Needed so we can run it manually + # 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. +
cargo update log +

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

+
jobs: update: - uses: ithacaxyz/ci/.github/workflows/cargo-update-pr.yml@9c8d0dc20e7ad02455d3fdab2378a05f29907630 # main + name: Update + runs-on: ubuntu-latest permissions: contents: write pull-requests: write - secrets: - token: ${{ secrets.GITHUB_TOKEN }} + 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 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 }} From 1b91245ea6ebda033002225a7abffeb6109280d3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 31 Oct 2025 14:29:55 +0700 Subject: [PATCH 164/374] Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/npm.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index b410a80d7d11f..1b2437288eb64 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -184,7 +184,8 @@ jobs: exit 1 fi fi - + + # Run automatically after a successful 'release' workflow, or manually if a run_id is provided - name: Publish ${{ matrix.os }}-${{ matrix.arch }} Binary ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} env: From f652ebab934253f0b1ca21a1131ad7f3e0523067 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 1 Nov 2025 00:25:46 +0700 Subject: [PATCH 165/374] Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/snyk-container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml index 10ac023cf51fc..f07df9c75c8d1 100644 --- a/.github/workflows/snyk-container.yml +++ b/.github/workflows/snyk-container.yml @@ -2,7 +2,7 @@ # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. - +# # A sample workflow which checks out the code, builds a container # image using Docker and scans that image for vulnerabilities using # Snyk. The results are then uploaded to GitHub Security Code Scanning From a63cc1a5e96d2ded9140ed74eaccf6f41da3dc47 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 1 Nov 2025 01:22:41 +0700 Subject: [PATCH 166/374] Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/nextest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nextest.yml b/.github/workflows/nextest.yml index edd230a05a5d9..c1343dcc6d64c 100644 --- a/.github/workflows/nextest.yml +++ b/.github/workflows/nextest.yml @@ -1,5 +1,4 @@ # Reusable workflow for running tests via `cargo nextest` - name: nextest permissions: {} @@ -85,6 +84,7 @@ jobs: with: python-version: "3.14" - name: Install Vyper + # Also update vyper version in .devcontainer/Dockerfile.dev run: pip --version && pip install vyper==0.4.3 From 02e3a1b758bf51e3bff13150632731ad18ce3830 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 2 Nov 2025 02:34:56 +0700 Subject: [PATCH 167/374] Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- npm/src/const.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/npm/src/const.ts b/npm/src/const.ts index 42a352712c6ba..890dc5d384767 100644 --- a/npm/src/const.ts +++ b/npm/src/const.ts @@ -1,5 +1,4 @@ import type * as Process from 'node:process' - const ALLOWED_REGISTRY_HOSTS = [ 'registry.npmjs.org', 'registry.yarnpkg.com', From 297291cb60a6fb32893366d6bf500709b9519ca5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:26:12 +0700 Subject: [PATCH 168/374] Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. From 5f89e0e224f7c3cdeeb56dad7af71e156e7e037f Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:30:48 +0700 Subject: [PATCH 169/374] Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/deploy.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs From b2aaca372dc4880c4f4b32f0717d7e597f26c0a6 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 9 Nov 2025 00:16:20 +0700 Subject: [PATCH 170/374] Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/dependencies.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 184e5d13f2c73..be3aab19c4eca 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -1,5 +1,4 @@ # Runs `cargo update` periodically. - name: dependencies on: From daa9b24b45532964b73a4140d494b239138dd626 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 9 Nov 2025 05:07:08 +0700 Subject: [PATCH 171/374] Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/dependencies.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index be3aab19c4eca..2d7e331e5fffc 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -1,10 +1,12 @@ # Runs `cargo update` periodically. + name: dependencies on: schedule: # Run weekly - cron: "0 0 * * SUN" + workflow_dispatch: # Needed so we can run it manually From 732ee8b2223e8121bfc468c08fc6ffe294cc42a9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 9 Nov 2025 05:30:19 +0700 Subject: [PATCH 172/374] Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/dependencies.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 2d7e331e5fffc..184e5d13f2c73 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -6,7 +6,6 @@ on: schedule: # Run weekly - cron: "0 0 * * SUN" - workflow_dispatch: # Needed so we can run it manually From fdba942106ad15725e4c9da90d1e5c34d6766b39 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 9 Nov 2025 05:57:11 +0700 Subject: [PATCH 173/374] Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 716abb3648f13..38ddd73f42e6e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,6 @@ on: branches: - master - main - - dev pull_request: concurrency: From 3010559e30ce64cc93670d794b4af61b72013f77 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 9 Nov 2025 07:19:33 +0700 Subject: [PATCH 174/374] Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d3fe0f910e5b4..33846b945a7fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - main pull_request: concurrency: From d8c5c3cba74b2e69628e875c8f255d955a4964ff Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 11 Nov 2025 01:49:26 +0700 Subject: [PATCH 175/374] Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> From 9d2402a5506f5ec29cf8bf6b86272df8d3fd35f2 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 11 Nov 2025 06:19:32 +0700 Subject: [PATCH 176/374] Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- Cargo.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 830ba11d18833..72f58c7188cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 - [[package]] name = "addr2line" version = "0.24.2" @@ -10847,3 +10846,5 @@ dependencies = [ "cc", "pkg-config", ] + + From dd9886e8d0adf479abf73df9cc7ab693a1bf877a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 11 Nov 2025 06:47:08 +0700 Subject: [PATCH 177/374] Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .circleci/config.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..e0476fddba507 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 +# +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:2024.06 + # + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" +# +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + From 724151fc36e84a1bbf337b4421d7ceb3d0a0cbe3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 12 Nov 2025 12:13:25 +0700 Subject: [PATCH 178/374] Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 76b2889f1c4b2..4168efef0971f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,20 +1,20 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference +# See: https://circleci.com/docs/reference/configuration-reference version: 2.1 # Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs jobs: say-hello: # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job docker: # Specify the version you desire here # See: https://circleci.com/developer/images/image/cimg/base - image: cimg/base:current # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps steps: # Checkout the code as the first step. - checkout @@ -23,7 +23,7 @@ jobs: command: "echo Hello, World!" # Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows workflows: say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. # Inside the workflow, you define the jobs you want to run. From 810d81ce1c61ca91a9d2f55a70053cb3b8e22457 Mon Sep 17 00:00:00 2001 From: "snyk-io[bot]" <141718529+snyk-io[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 15:21:45 +0000 Subject: [PATCH 179/374] fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr --- npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/package.json b/npm/package.json index 23d091b1cec0c..8575d52837b43 100644 --- a/npm/package.json +++ b/npm/package.json @@ -10,7 +10,7 @@ "clean": "rm -rf bin dist && rm -rf ./@foundry-rs/forge*/bin ./@foundry-rs/forge*/dist ./@foundry-rs/forge*/*.tgz" }, "dependencies": { - "tsdown": "^0.15.1", + "tsdown": "^0.16.1", "typescript": "^5.9.2", "@types/bun": "^1.2.22", "@types/node": "^24.4.0" From bfd383fb0bbef4b366538ec4bce88a85e73339ef Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:49:46 +0700 Subject: [PATCH 180/374] Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/google.yml | 117 +++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 .github/workflows/google.yml diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide From 76989a6f07049f9ca5eb89306f60d4d090ec521d Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 13 Dec 2025 05:51:39 +0700 Subject: [PATCH 181/374] Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/{ci.yml => cargo.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .circleci/{ci.yml => cargo.yml} (95%) diff --git a/.circleci/ci.yml b/.circleci/cargo.yml similarity index 95% rename from .circleci/ci.yml rename to .circleci/cargo.yml index 7293433a50f2d..e89694e7d2a6e 100644 --- a/.circleci/ci.yml +++ b/.circleci/cargo.yml @@ -3,7 +3,7 @@ version: 2.1 jobs: build-and-test: docker: - - image: cimg/rust:1.88.0 + - image: cimg/rust:1.78.0 steps: - checkout - restore_cache: From 2c467d94ca1398ba1a6d2f3542a92d2d16f911af Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:40:34 +0700 Subject: [PATCH 182/374] Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index e02008bf849af..af5e606fbc1de 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1760856120, - "narHash": "sha256-yH1K/WDJpwIIw7e3wKdRgwHAZ38LXgcGE2Ecvk3I6GU=", + "lastModified": 1757745213, + "narHash": "sha256-P9VX/P2mN96MkFN8hwCYUQ+LV1bfH57UJ/pGwjd0Olc=", "owner": "nix-community", "repo": "fenix", - "rev": "b435bfccee71c6591dbce2fcfabe3e17e98c09fa", + "rev": "1458349a1bd55105f917e962dca4b328ac0a55e8", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1760872779, - "narHash": "sha256-c5C907Raf9eY8f1NUXYeju9aUDlm227s/V0OptEbypA=", + "lastModified": 1757746433, + "narHash": "sha256-fEvTiU4s9lWgW7mYEU/1QUPirgkn+odUBTaindgiziY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "63bdb5d90fa2fa11c42f9716ad1e23565613b07c", + "rev": "6d7ec06d6868ac6d94c371458fc2391ded9ff13d", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1760714286, - "narHash": "sha256-WOt9KquZ1BXjMcVyHpMeliqNRL6BfRvBHFGfRDriDx4=", + "lastModified": 1757362324, + "narHash": "sha256-/PAhxheUq4WBrW5i/JHzcCqK5fGWwLKdH6/Lu1tyS18=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "1e20331e42449dfc0b44bce84147a06772d045d7", + "rev": "9edc9cbe5d8e832b5864e09854fa94861697d2fd", "type": "github" }, "original": { From ea542a2f2b35e931c9b4b20eb8a806125e2b1e1a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 14 Dec 2025 19:23:06 +0700 Subject: [PATCH 183/374] Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- flake.nix | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 5caaab9934aec..8cf41ed16d3a3 100644 --- a/flake.nix +++ b/flake.nix @@ -19,8 +19,7 @@ lib = pkgs.lib; toolchain = fenix.packages.${system}.stable.toolchain; - in - { + in { default = pkgs.mkShell { nativeBuildInputs = with pkgs; [ pkg-config @@ -29,16 +28,13 @@ # test dependencies solc vyper - dprint nodejs ]; + buildInputs = lib.optionals pkgs.stdenv.isDarwin + [ pkgs.darwin.apple_sdk.frameworks.AppKit ]; packages = with pkgs; [ rust-analyzer-unwrapped ]; - # Remove the hardening added by nix to fix jmalloc compilation error. - # More info: https://github.com/tikv/jemallocator/issues/108 - hardeningDisable = [ "fortify" ]; - # Environment variables RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library"; LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.libusb1 ]; From e45aa66f3dd2ad5d28bf4f9910cb6d99b09c1822 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sun, 14 Dec 2025 20:18:30 +0700 Subject: [PATCH 184/374] Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 68e6e2881ff7d..9d2f94aad00cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ resolver = "2" version = "1.2.1" edition = "2021" # Remember to update clippy.toml as well -rust-version = "1.86" +rust-version = "1.89.0" authors = ["Foundry Contributors"] license = "MIT OR Apache-2.0" homepage = "https://github.com/foundry-rs/foundry" From c0d08b1943203e4b3bc3fe31805f49ddca6173f0 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:11:32 +0700 Subject: [PATCH 185/374] Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .config/nextest.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 2a9a4acd42460..70616d4e45e6b 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,14 +1,14 @@ [test-groups] +chisel-serial = { max-threads = 1 } [profile.default] retries = { backoff = "exponential", count = 2, delay = "5s", jitter = true } -slow-timeout = { period = "30s", terminate-after = 3 } +slow-timeout = { period = "1m", terminate-after = 3 } [[profile.default.overrides]] -filter = "test(/ext_integration/)" +filter = "test(/ext_integration|can_test_forge_std/)" slow-timeout = { period = "5m", terminate-after = 4 } -# Do not re-run so that `cargo cheats` is ran locally. [[profile.default.overrides]] filter = "package(foundry-cheatcodes-spec)" retries = 0 From 562ffb33383724e4723b3ce71eb330fe06b41033 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 17 Dec 2025 02:32:57 +0700 Subject: [PATCH 186/374] Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- dprint.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dprint.json b/dprint.json index f665622619dd4..bf3ea43b41a11 100644 --- a/dprint.json +++ b/dprint.json @@ -3,10 +3,10 @@ "indentWidth": 2, "useTabs": false, "excludes": [ + "!dprint.json", "!npm/**/*.{json,md,toml}", "!npm/**/*.{js,cjs,mjs,d.ts,d.cts,d.mts,ts,tsx,jsx}", "**/_", - "dprint.json", "**/abi", "**/build", "**/target", @@ -25,8 +25,8 @@ ], "plugins": [ "https://plugins.dprint.dev/toml-0.7.0.wasm", - "https://plugins.dprint.dev/json-0.20.0.wasm", - "https://plugins.dprint.dev/markdown-0.19.0.wasm", + "https://plugins.dprint.dev/json-0.21.0.wasm", + "https://plugins.dprint.dev/markdown-0.20.0.wasm", "https://plugins.dprint.dev/dockerfile-0.3.3.wasm", "https://plugins.dprint.dev/typescript-0.95.11.wasm", "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm" @@ -57,4 +57,4 @@ "exportDeclaration.sortTypeOnlyExports": "none", "importDeclaration.sortTypeOnlyImports": "none" } -} \ No newline at end of file +} From e8b5cb61e3110121163ea24933796caca5a98299 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:17:24 +0700 Subject: [PATCH 187/374] Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/apisec-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml index 166d2f8ee0235..e716760284792 100644 --- a/.github/workflows/apisec-scan.yml +++ b/.github/workflows/apisec-scan.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Run APIsec scan uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea From d72eca45cf6f58fd395089c9b3759b4364fc56c6 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:19:05 +0700 Subject: [PATCH 188/374] Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- counter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counter/README.md b/counter/README.md index 9265b4558406a..679a7f4518035 100644 --- a/counter/README.md +++ b/counter/README.md @@ -7,7 +7,7 @@ Foundry consists of: - **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). - **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. - **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. ## Documentation From f4ebaa7ee344d1bb6aeefade0e9b1512bc4869b9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:19:58 +0700 Subject: [PATCH 189/374] Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 43a59e29b9c29..53a505774ac88 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -31,6 +31,7 @@ If applicable, add screenshots to help explain your problem. **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] - Browser [e.g. stock browser, safari] - Version [e.g. 22] From d784ebfa40bbae32aaaf7e212c88209b3b83bfc9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 24 Dec 2025 20:24:44 +0700 Subject: [PATCH 190/374] Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .circleci/{ci.yml => cargo.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .circleci/{ci.yml => cargo.yml} (100%) diff --git a/.circleci/ci.yml b/.circleci/cargo.yml similarity index 100% rename from .circleci/ci.yml rename to .circleci/cargo.yml From a531ef8fa32491e1b4ca0571cd7095db5fb40b30 Mon Sep 17 00:00:00 2001 From: Gengar Date: Wed, 24 Dec 2025 16:21:10 +0200 Subject: [PATCH 191/374] Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --- crates/verify/src/etherscan/standard_json.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index 8a38485e48922..0471e3e42888e 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -56,7 +56,7 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { let sources = Source::read_all_from(path, &["vy", "vyi"])?; let input = VyperInput::new( sources, - context.clone().compiler_settings.vyper, + context.compiler_settings.vyper.clone(), &context.compiler_version, ); From d1d564ff1c9fbe1d5cb27c5ee0fcfd33b09650e5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 25 Dec 2025 00:52:27 +0700 Subject: [PATCH 192/374] Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e0476fddba507..ad53a8e498202 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,32 +1,26 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/configuration-reference + version: 2.1 -# -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job +executors: + my-custom-executor: docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:2024.06 - # - # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor steps: - # Checkout the code as the first step. - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" -# -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows + - run: | + # echo Hello, World! + workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. + my-custom-workflow: jobs: - - say-hello - + - web3-defi-game-project- From cb8ddcd20e9ba21fcca10bc9458eab382437ad42 Mon Sep 17 00:00:00 2001 From: tskoyo Date: Thu, 25 Dec 2025 21:18:00 +0100 Subject: [PATCH 193/374] EIP-4788 implementation --- crates/cast/src/cmd/run.rs | 23 ++++++++++++++++++++-- crates/evm/evm/src/executors/trace.rs | 28 ++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 54894fc10b7ea..51a08274ee7cb 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -2,7 +2,7 @@ use crate::{debug::handle_traces, utils::apply_chain_and_block_specific_env_chan use alloy_consensus::Transaction; use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{ - Address, Bytes, U256, + Address, Bytes, FixedBytes, U256, map::{AddressSet, HashMap}, }; use alloy_provider::Provider; @@ -31,7 +31,7 @@ use foundry_evm::{ utils::configure_tx_env, }; use futures::TryFutureExt; -use revm::DatabaseRef; +use revm::{DatabaseRef, primitives::hardfork::SpecId}; /// CLI arguments for `cast run`. #[derive(Clone, Debug, Parser)] @@ -183,6 +183,8 @@ impl RunArgs { env.evm_env.cfg_env.limit_contract_code_size = None; env.evm_env.block_env.number = U256::from(tx_block_number); + let mut parent_beacon_block_root: FixedBytes<32> = FixedBytes::default(); + if let Some(block) = &block { env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); env.evm_env.block_env.beneficiary = block.header.beneficiary; @@ -191,6 +193,16 @@ impl RunArgs { env.evm_env.block_env.basefee = block.header.base_fee_per_gas.unwrap_or_default(); env.evm_env.block_env.gas_limit = block.header.gas_limit; + if env.evm_env.cfg_env.spec >= SpecId::CANCUN { + if let Some(beacon_root) = block.header.parent_beacon_block_root { + parent_beacon_block_root = beacon_root; + } else { + return Err(eyre::eyre!( + "ParentBeaconBlockRoot is missing for Cancun or later blocks" + )); + } + } + // TODO: we need a smarter way to map the block to the corresponding evm_version for // commonly used chains if evm_version.is_none() { @@ -214,6 +226,7 @@ impl RunArgs { InternalTraceMode::None }) .with_state_changes(shell::verbosity() > 4); + let mut executor = TracingExecutor::new( env.clone(), fork, @@ -223,6 +236,12 @@ impl RunArgs { create2_deployer, None, )?; + + if parent_beacon_block_root != FixedBytes::default() { + let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().unwrap_or(0); + executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; + } + let mut env = Env::new_with_spec_id( env.evm_env.cfg_env.clone(), env.evm_env.block_env.clone(), diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index d951dde0d6663..319e2ab495ca6 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -2,7 +2,7 @@ use crate::{ Env, executors::{Executor, ExecutorBuilder}, }; -use alloy_primitives::{Address, U256, map::HashMap}; +use alloy_primitives::{Address, FixedBytes, U256, address, map::HashMap}; use alloy_rpc_types::state::StateOverride; use eyre::Context; use foundry_compilers::artifacts::EvmVersion; @@ -92,6 +92,32 @@ impl TracingExecutor { let chain = env.tx.chain_id.unwrap().into(); Ok((env, fork, chain, networks)) } + + /// Processes the beacon block root by storing it in the appropriate storage slots. + pub fn process_beacon_block_root( + &mut self, + block_timestamp: u64, + beacon_root: FixedBytes<32>, + ) -> eyre::Result<()> { + const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); + const HISTORY_BUFFER_LENGTH: u64 = 8191; + + let timestamp_index = block_timestamp % HISTORY_BUFFER_LENGTH; + let root_index = timestamp_index + HISTORY_BUFFER_LENGTH; + + let timestamp_slot = U256::from(timestamp_index); + let root_slot = U256::from(root_index); + + self.set_storage_slot(BEACON_ROOTS_ADDRESS, timestamp_slot, U256::from(block_timestamp))?; + + self.set_storage_slot( + BEACON_ROOTS_ADDRESS, + root_slot, + U256::from_be_bytes(beacon_root.into()), + )?; + + Ok(()) + } } impl Deref for TracingExecutor { From ee13505c8ca6dc6399c082c07cc075c3330c03a0 Mon Sep 17 00:00:00 2001 From: tskoyo Date: Sat, 27 Dec 2025 14:55:45 +0100 Subject: [PATCH 194/374] formatting --- crates/cast/src/cmd/run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 51a08274ee7cb..6152f5b6978a6 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -226,7 +226,6 @@ impl RunArgs { InternalTraceMode::None }) .with_state_changes(shell::verbosity() > 4); - let mut executor = TracingExecutor::new( env.clone(), fork, From fdad166d8ca338d6a891d42c6c7dc153bf1d9c77 Mon Sep 17 00:00:00 2001 From: Aganis Date: Tue, 30 Dec 2025 05:33:33 +0800 Subject: [PATCH 195/374] Remove duplicate logic in TxSigner::address() implementations --- crates/wallets/src/signer.rs | 2 +- crates/wallets/src/wallet_browser/signer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wallets/src/signer.rs b/crates/wallets/src/signer.rs index 5a5c77c676587..8d50c03d1a90b 100644 --- a/crates/wallets/src/signer.rs +++ b/crates/wallets/src/signer.rs @@ -322,7 +322,7 @@ impl Signer for WalletSigner { #[async_trait] impl TxSigner for WalletSigner { fn address(&self) -> Address { - delegate!(self, inner => alloy_signer::Signer::address(inner)) + Signer::address(self) } async fn sign_transaction( diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs index 1e3df775e2598..78cf166a35ad0 100644 --- a/crates/wallets/src/wallet_browser/signer.rs +++ b/crates/wallets/src/wallet_browser/signer.rs @@ -177,7 +177,7 @@ impl Signer for BrowserSigner { #[async_trait] impl TxSigner for BrowserSigner { fn address(&self) -> Address { - self.address + Signer::address(self) } async fn sign_transaction( From 7214be116217e999bddb05a6163a0b255c279a68 Mon Sep 17 00:00:00 2001 From: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Date: Sun, 28 Dec 2025 20:33:12 +0700 Subject: [PATCH 196/374] fix: use network-specific BaseFeeParams for Optimism in Anvil --- Cargo.lock | 2 + crates/anvil/src/config.rs | 6 +- crates/anvil/src/eth/backend/mem/mod.rs | 3 +- crates/anvil/src/eth/fees.rs | 25 +++++--- crates/anvil/tests/it/optimism.rs | 80 ++++++++++++++++++++++++- crates/evm/networks/Cargo.toml | 2 + crates/evm/networks/src/lib.rs | 19 ++++++ 7 files changed, 125 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f043ac4bd3b5..2a36076e9880b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4974,7 +4974,9 @@ name = "foundry-evm-networks" version = "1.5.1" dependencies = [ "alloy-chains", + "alloy-eips", "alloy-evm", + "alloy-op-hardforks", "alloy-primitives", "clap", "revm", diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index c0ef7c922ec9d..59e79bfe3a989 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -16,7 +16,7 @@ use crate::{ }; use alloy_chains::Chain; use alloy_consensus::BlockHeader; -use alloy_eips::eip7840::BlobParams; +use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams}; use alloy_evm::EvmEnv; use alloy_genesis::Genesis; use alloy_network::{AnyNetwork, TransactionResponse}; @@ -1094,6 +1094,9 @@ impl NodeConfig { self.networks, ); + let base_fee_params: BaseFeeParams = + self.networks.base_fee_params(self.get_genesis_timestamp()); + let fees = FeeManager::new( spec_id, self.get_base_fee(), @@ -1101,6 +1104,7 @@ impl NodeConfig { self.get_gas_price(), self.get_blob_excess_gas_and_price(), self.get_blob_params(), + base_fee_params, ); let (db, fork): (Arc>>, Option) = diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index adc56fe33a711..c46669e439df7 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -41,7 +41,6 @@ use alloy_consensus::{ use alloy_eip5792::{Capabilities, DelegationCapability}; use alloy_eips::{ BlockNumHash, Encodable2718, - eip1559::BaseFeeParams, eip4844::{BlobTransactionSidecar, kzg_to_versioned_hash}, eip7840::BlobParams, eip7910::SystemContract, @@ -1857,7 +1856,7 @@ impl Backend { block_env.basefee = simulated_block .inner .header - .next_block_base_fee(BaseFeeParams::ethereum()) + .next_block_base_fee(self.fees.base_fee_params()) .unwrap_or_default(); block_res.push(simulated_block); diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index e34b7c6eb6a24..0e4c6dffef57f 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -36,10 +36,6 @@ 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 -} - /// Stores the fee related information #[derive(Clone, Debug)] pub struct FeeManager { @@ -62,6 +58,8 @@ pub struct FeeManager { /// This will be constant value unless changed manually gas_price: Arc>, elasticity: Arc>, + /// Network-specific base fee params for EIP-1559 calculations + base_fee_params: BaseFeeParams, } impl FeeManager { @@ -72,7 +70,9 @@ impl FeeManager { gas_price: u128, blob_excess_gas_and_price: BlobExcessGasAndPrice, blob_params: BlobParams, + base_fee_params: BaseFeeParams, ) -> Self { + let elasticity = 1f64 / base_fee_params.elasticity_multiplier as f64; Self { spec_id, blob_params: Arc::new(RwLock::new(blob_params)), @@ -80,10 +80,16 @@ impl FeeManager { is_min_priority_fee_enforced, 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())), + elasticity: Arc::new(RwLock::new(elasticity)), + base_fee_params, } } + /// Returns the base fee params used for EIP-1559 calculations + pub fn base_fee_params(&self) -> BaseFeeParams { + self.base_fee_params + } + pub fn elasticity(&self) -> f64 { *self.elasticity.read() } @@ -156,7 +162,7 @@ impl FeeManager { if self.base_fee() == 0 { return 0; } - calculate_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas) + calc_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas, self.base_fee_params) } /// Calculates the next block blob base fee. @@ -185,7 +191,12 @@ impl FeeManager { } } -/// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec +/// Calculate base fee for next block using Ethereum mainnet parameters. +/// +/// See [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec. +/// +/// Note: This uses Ethereum's base fee params. For network-specific calculations +/// (e.g., Optimism), use [`FeeManager::get_next_block_base_fee_per_gas`] instead. pub fn calculate_next_block_base_fee(gas_used: u64, gas_limit: u64, base_fee: u64) -> u64 { calc_next_block_base_fee(gas_used, gas_limit, base_fee, BaseFeeParams::ethereum()) } diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 1dbef5eb1e5fc..3eb3fa341a30f 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -5,9 +5,9 @@ use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, Bloom, TxHash, TxKind, U256, b256}; use alloy_provider::Provider; -use alloy_rpc_types::TransactionRequest; +use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; -use anvil::{NodeConfig, spawn}; +use anvil::{NodeConfig, eth::fees::INITIAL_BASE_FEE, spawn}; use foundry_evm_networks::NetworkConfigs; use op_alloy_consensus::TxDeposit; use op_alloy_rpc_types::OpTransactionFields; @@ -270,3 +270,79 @@ fn preserves_op_fields_in_convert_to_anvil_receipt() { assert_eq!(got, Some(expected), "field `{key}` mismatch"); } } + +const GAS_TRANSFER: u64 = 21_000; + +/// Test that Optimism uses Canyon base fee params instead of Ethereum params. +/// +/// Optimism Canyon uses different EIP-1559 parameters: +/// - elasticity_multiplier: 6 (vs Ethereum's 2) +/// - base_fee_max_change_denominator: 250 (vs Ethereum's 8) +/// +/// This means with a full block: +/// - Ethereum: base_fee increases by base_fee * 1 / 8 = 12.5% +/// - Optimism: base_fee increases by base_fee * 5 / 250 = 2% +#[tokio::test(flavor = "multi_thread")] +async fn test_optimism_base_fee_params() { + // Spawn an Optimism node with a gas limit equal to one transfer (full block scenario) + let (_api, handle) = spawn( + NodeConfig::test() + .with_networks(NetworkConfigs::with_optimism()) + .with_base_fee(Some(INITIAL_BASE_FEE)) + .with_gas_limit(Some(GAS_TRANSFER)), + ) + .await; + + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumWallet = 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); + + // Send first transaction to fill the block + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let base_fee = provider + .get_block(BlockId::latest()) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); + + // Send second transaction to fill the next block + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let next_base_fee = provider + .get_block(BlockId::latest()) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); + + assert!(next_base_fee > base_fee, "base fee should increase with full block"); + + // Optimism Canyon formula: base_fee * (elasticity - 1) / denominator = base_fee * 5 / 250 + // = INITIAL_BASE_FEE * 5 / 250 = 1_000_000_000 * 5 / 250 = 20_000_000 + // + // Note: Ethereum would be INITIAL_BASE_FEE + 125_000_000 (12.5% increase) + let expected_op_increase = INITIAL_BASE_FEE * 5 / 250; // 2% increase = 20_000_000 + assert_eq!( + next_base_fee, + INITIAL_BASE_FEE + expected_op_increase, + "Optimism should use Canyon base fee params (2% max increase), not Ethereum's (12.5%)" + ); + + // Explicitly verify it's NOT using Ethereum params (which would give 12.5% increase) + let ethereum_increase = INITIAL_BASE_FEE / 8; // 125_000_000 + assert_ne!( + next_base_fee, + INITIAL_BASE_FEE + ethereum_increase, + "Should not be using Ethereum base fee params" + ); +} diff --git a/crates/evm/networks/Cargo.toml b/crates/evm/networks/Cargo.toml index b3bc2701ed3bb..934458a42165b 100644 --- a/crates/evm/networks/Cargo.toml +++ b/crates/evm/networks/Cargo.toml @@ -15,7 +15,9 @@ workspace = true [dependencies] alloy-chains.workspace = true +alloy-eips.workspace = true alloy-evm.workspace = true +alloy-op-hardforks.workspace = true alloy-primitives = { workspace = true, features = [ "serde", "getrandom", diff --git a/crates/evm/networks/src/lib.rs b/crates/evm/networks/src/lib.rs index 3f327e15617a5..8e64589bb71ad 100644 --- a/crates/evm/networks/src/lib.rs +++ b/crates/evm/networks/src/lib.rs @@ -9,7 +9,9 @@ use alloy_chains::{ NamedChain, NamedChain::{Chiado, Gnosis, Moonbase, Moonbeam, MoonbeamDev, Moonriver, Rsk, RskTestnet}, }; +use alloy_eips::eip1559::BaseFeeParams; use alloy_evm::precompiles::PrecompilesMap; +use alloy_op_hardforks::{OpChainHardforks, OpHardforks}; use alloy_primitives::{Address, map::AddressHashMap}; use clap::Parser; use serde::{Deserialize, Serialize}; @@ -47,6 +49,23 @@ impl NetworkConfigs { self.optimism } + /// Returns the base fee parameters for the configured network. + /// + /// For Optimism networks, returns Canyon parameters if the Canyon hardfork is active + /// at the given timestamp, otherwise returns pre-Canyon parameters. + pub fn base_fee_params(&self, timestamp: u64) -> BaseFeeParams { + if self.is_optimism() { + let op_hardforks = OpChainHardforks::op_mainnet(); + if op_hardforks.is_canyon_active_at_timestamp(timestamp) { + BaseFeeParams::optimism_canyon() + } else { + BaseFeeParams::optimism() + } + } else { + BaseFeeParams::ethereum() + } + } + pub fn bypass_prevrandao(&self, chain_id: u64) -> bool { if let Ok( Moonbeam | Moonbase | Moonriver | MoonbeamDev | Rsk | RskTestnet | Gnosis | Chiado, From 17250c474507d3a960742dcec147d8ff212a379b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 20:48:07 +0700 Subject: [PATCH 197/374] Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar --- .circleci/config.yml | 32 ------------------- benches/src/lib.rs | 16 ++++++++-- crates/evm/evm/src/executors/corpus.rs | 33 +++++++++++++++----- crates/test-utils/src/script.rs | 12 +++++++ crates/verify/src/etherscan/standard_json.rs | 2 +- 5 files changed, 52 insertions(+), 43 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index d02c693397345..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 -# -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - # - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" -# -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello - diff --git a/benches/src/lib.rs b/benches/src/lib.rs index 89629f6898628..eeb3cbeeafe11 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -132,10 +132,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/crates/evm/evm/src/executors/corpus.rs b/crates/evm/evm/src/executors/corpus.rs index eb5431e3ba57a..92eb4452ce802 100644 --- a/crates/evm/evm/src/executors/corpus.rs +++ b/crates/evm/evm/src/executors/corpus.rs @@ -190,6 +190,15 @@ impl CorpusManager { foundry_common::fs::create_dir_all(corpus_dir)?; } + // Canonicalize the corpus_dir to a trusted absolute path + let canonical_corpus_dir = match corpus_dir.canonicalize() { + Ok(dir) => dir, + Err(e) => { + trace!(target: "corpus", "failed to canonicalize corpus_dir {}: {e}", corpus_dir.display()); + return Err(e.into()); + } + }; + let can_replay_tx = |tx: &BasicTxDetails| -> bool { fuzzed_contracts.is_some_and(|contracts| contracts.targets.lock().can_replay(tx)) || fuzzed_function.is_some_and(|function| { @@ -202,21 +211,31 @@ impl CorpusManager { 'corpus_replay: for entry in std::fs::read_dir(corpus_dir)? { let path = entry?.path(); - if path.is_file() - && let Some(name) = path.file_name().and_then(|s| s.to_str()) + // Canonicalize the candidate path, skip if it cannot be canonicalized (e.g. broken symlink) + let canonical_path = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, + }; + // Ensure file is inside the corpus directory (prevents path traversal/symlink escape) + if !canonical_path.starts_with(&canonical_corpus_dir) { + trace!(target: "corpus", "Skipping file outside corpus_dir: {}", path.display()); + continue; + } + if canonical_path.is_file() + && let Some(name) = canonical_path.file_name().and_then(|s| s.to_str()) && name.contains(METADATA_SUFFIX) { // Ignore metadata files continue; } - let read_corpus_result = match path.extension().and_then(|ext| ext.to_str()) { - Some("gz") => foundry_common::fs::read_json_gzip_file::>(&path), - _ => foundry_common::fs::read_json_file::>(&path), + let read_corpus_result = match canonical_path.extension().and_then(|ext| ext.to_str()) { + Some("gz") => foundry_common::fs::read_json_gzip_file::>(&canonical_path), + _ => foundry_common::fs::read_json_file::>(&canonical_path), }; let Ok(tx_seq) = read_corpus_result else { - trace!(target: "corpus", "failed to load corpus from {}", path.display()); + trace!(target: "corpus", "failed to load corpus from {}", canonical_path.display()); continue; }; @@ -257,7 +276,7 @@ impl CorpusManager { ); // Populate in memory corpus with the sequence from corpus file. - in_memory_corpus.push(CorpusEntry::new(tx_seq, path)?); + in_memory_corpus.push(CorpusEntry::new(tx_seq, canonical_path.clone())?); } } diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 45dbbbebdd73a..626ce94448a4a 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -123,6 +123,18 @@ impl ScriptTester { for entry in fs::read_dir(&from_dir)? { let file = &entry?.path(); let name = file.file_name().unwrap(); + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Optionally verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } fs::copy(file, to_dir.join(name))?; } Ok(()) diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index 8a38485e48922..0471e3e42888e 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -56,7 +56,7 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { let sources = Source::read_all_from(path, &["vy", "vyi"])?; let input = VyperInput::new( sources, - context.clone().compiler_settings.vyper, + context.compiler_settings.vyper.clone(), &context.compiler_version, ); From 8b29d48133545a0f5c32da2fdc48bb54ac2eb309 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 21:08:43 +0700 Subject: [PATCH 198/374] merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .circleci/config.yml | 31 +++++++++++ Cargo.lock | 8 +-- Cargo.toml | 4 +- crates/anvil/tests/it/anvil_api.rs | 2 +- crates/config/src/compilation.rs | 33 +++++++++++- crates/config/src/lib.rs | 75 +++++++++++++++++--------- crates/evm/core/src/backend/mod.rs | 4 +- crates/forge/tests/cli/test_cmd/mod.rs | 27 ++++++++++ 8 files changed, 148 insertions(+), 36 deletions(-) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..d5d401c51893c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello diff --git a/Cargo.lock b/Cargo.lock index 2f043ac4bd3b5..f19b33e2859ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9a33550fc21fd77a3f8b63e99969d17660eec8dcc50a95a80f7c9964f7680b" +checksum = "83ba208044232d14d4adbfa77e57d6329f51bc1acc21f5667bb7db72d88a0831" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -388,9 +388,9 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f96fb2fce4024ada5b2c11d4076acf778a0d3e4f011c6dfd2ffce6d0fcf84ee9" +checksum = "6472c610150c4c4c15be9e1b964c9b78068f933bda25fb9cdf09b9ac2bb66f36" dependencies = [ "alloy-chains", "alloy-hardforks", diff --git a/Cargo.toml b/Cargo.toml index dbeac987c0358..9f9fdc0498bdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -268,8 +268,8 @@ alloy-transport = { version = "1.1.3", default-features = false } alloy-transport-http = { version = "1.1.3", default-features = false } alloy-transport-ipc = { version = "1.1.3", default-features = false } alloy-transport-ws = { version = "1.1.3", default-features = false } -alloy-hardforks = { version = "0.4.5", default-features = false } -alloy-op-hardforks = { version = "0.4.5", default-features = false } +alloy-hardforks = { version = "0.4.7", default-features = false } +alloy-op-hardforks = { version = "0.4.7", default-features = false } ## alloy-core alloy-dyn-abi = "1.5.1" diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index fc89206db81fd..e675001d7a480 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -440,7 +440,7 @@ async fn can_get_node_info() { let block_number = provider.get_block_number().await.unwrap(); let block = provider.get_block(BlockId::from(block_number)).await.unwrap().unwrap(); - let hard_fork: &str = SpecId::PRAGUE.into(); + let hard_fork: &str = SpecId::OSAKA.into(); let expected_node_info = NodeInfo { current_block_number: 0_u64, diff --git a/crates/config/src/compilation.rs b/crates/config/src/compilation.rs index 94335dd26a8ef..46f0f2158a0f1 100644 --- a/crates/config/src/compilation.rs +++ b/crates/config/src/compilation.rs @@ -7,7 +7,7 @@ use foundry_compilers::{ solc::{Restriction, SolcRestrictions}, }; use semver::VersionReq; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; /// Keeps possible overrides for default settings which users may configure to construct additional /// settings profile. @@ -69,6 +69,7 @@ pub enum RestrictionsError { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct CompilationRestrictions { pub paths: GlobMatcher, + #[serde(default, deserialize_with = "deserialize_version_req")] pub version: Option, pub via_ir: Option, pub bytecode_hash: Option, @@ -85,6 +86,36 @@ pub struct CompilationRestrictions { pub max_evm_version: Option, } +/// Custom deserializer for version field that rejects ambiguous bare version numbers. +fn deserialize_version_req<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let opt_string: Option = Option::deserialize(deserializer)?; + let Some(opt_string) = opt_string else { + return Ok(None); + }; + + let version = opt_string.trim(); + // Reject bare versions like "0.8.11" that lack an operator prefix + if version.chars().next().is_some_and(|c| c.is_ascii_digit()) { + return Err(serde::de::Error::custom(format!( + "Invalid version format '{version}' in compilation_restrictions. \ + Bare version numbers are ambiguous and default to caret requirements (e.g. '^{version}'). \ + Use an explicit constraint such as '={version}' for an exact version or '>={version}' for a minimum version." + ))); + } + + let req = VersionReq::parse(version).map_err(|e| { + serde::de::Error::custom(format!( + "Invalid version requirement '{version}': {e}. \ + Examples: '=0.8.11' (exact), '>=0.8.11' (minimum), '>=0.8.11 <0.9.0' (range)." + )) + })?; + + Ok(Some(req)) +} + impl TryFrom for RestrictionsWithVersion { type Error = RestrictionsError; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 50faf02cbfcb0..d54cb0aac63f5 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -774,40 +774,28 @@ impl Config { } fn from_figment(figment: Figment) -> Result { - // Helper to add a profile only if it's not already present - fn add_profile(profiles: &mut Vec, profile: &Profile) { - if !profiles.contains(profile) { - profiles.push(profile.clone()); - } - } - let mut config = figment.extract::().map_err(ExtractConfigError::new)?; - let active_profile = figment.profile().clone(); + config.profile = figment.profile().clone(); - // Collect profiles from the profile section. // The `"profile"` profile contains all the profiles as keys. + let mut add_profile = |profile: &Profile| { + if !config.profiles.contains(profile) { + config.profiles.push(profile.clone()); + } + }; let figment = figment.select(Self::PROFILE_SECTION); if let Ok(data) = figment.data() && let Some(profiles) = data.get(&Profile::new(Self::PROFILE_SECTION)) { for profile in profiles.keys() { - add_profile(&mut config.profiles, &Profile::new(profile)); + add_profile(&Profile::new(profile)); } } - - // Always include the default profile - add_profile(&mut config.profiles, &Self::DEFAULT_PROFILE); - - // Ensure the active profile exists. - if config.profiles.contains(&active_profile) { - config.profile = active_profile; - } else { - return Err(ExtractConfigError::new(Error::from(format!( - "Profile {active_profile} does not exist" - )))); - } + add_profile(&Self::DEFAULT_PROFILE); + add_profile(&config.profile); config.normalize_optimizer_settings(); + Ok(config) } @@ -6427,18 +6415,53 @@ mod tests { } #[test] - fn fails_on_unknown_profile() { + fn fails_on_ambiguous_version_in_compilation_restrictions() { figment::Jail::expect_with(|jail| { jail.create_file( "foundry.toml", r#" [profile.default] + src = "src" + + [[profile.default.compilation_restrictions]] + paths = "src/*.sol" + version = "0.8.11" "#, )?; - jail.set_env("FOUNDRY_PROFILE", "foo"); - let err = Config::load().expect_err("expected unknown profile to fail"); - assert!(err.to_string().contains("Profile foo does not exist")); + let err = Config::load().expect_err("expected bare version to fail"); + let err_msg = err.to_string(); + assert!( + err_msg.contains("Invalid version format '0.8.11'") + && err_msg.contains("Bare version numbers are ambiguous"), + "Expected error about ambiguous version, got: {err_msg}" + ); + + Ok(()) + }); + } + + #[test] + fn accepts_explicit_version_requirements() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [[profile.default.compilation_restrictions]] + paths = "src/*.sol" + version = "=0.8.11" + + [[profile.default.compilation_restrictions]] + paths = "test/*.sol" + version = ">=0.8.11" + "#, + )?; + + let config = Config::load().expect("should accept explicit version requirements"); + assert_eq!(config.compilation_restrictions.len(), 2); Ok(()) }); diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 26e56e7bf883f..6429ceb3db23f 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -6,7 +6,7 @@ use crate::{ evm::new_evm_with_inspector, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, - utils::{configure_tx_env, configure_tx_req_env, get_blob_base_fee_update_fraction_by_spec_id}, + utils::{configure_tx_env, configure_tx_req_env, get_blob_base_fee_update_fraction}, }; use alloy_consensus::Typed2718; use alloy_evm::Evm; @@ -1954,7 +1954,7 @@ fn update_env_block(env: &mut EnvMut<'_>, block: &AnyRpcBlock) { if let Some(excess_blob_gas) = block.header.excess_blob_gas { env.block.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( excess_blob_gas, - get_blob_base_fee_update_fraction_by_spec_id(env.cfg.spec), + get_blob_base_fee_update_fraction(env.cfg.chain_id, block.header.timestamp), )); } } diff --git a/crates/forge/tests/cli/test_cmd/mod.rs b/crates/forge/tests/cli/test_cmd/mod.rs index 9666cf4d7e69d..0abffff57c4a8 100644 --- a/crates/forge/tests/cli/test_cmd/mod.rs +++ b/crates/forge/tests/cli/test_cmd/mod.rs @@ -678,6 +678,33 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); }); +// Validates BPO1 blob gas price calculation during fork transaction replay. +// Block 24127158 has a blob tx at index 0, target tx at index 1. +// Forking at the target tx replays the blob tx with correct BPO1 blob base fee calculation. +forgetest_init!(fork_tx_replay_bpo1_blob_base_fee, |prj, cmd| { + let endpoint = rpc::next_http_archive_rpc_url(); + + prj.add_test( + "BlobFork.t.sol", + &r#" +import {Test} from "forge-std/Test.sol"; + +contract BlobForkTest is Test { + function test_fork_with_blob_replay() public { + // Fork at tx index 1 in block 24127158, which replays blob tx at index 0 + bytes32 txHash = 0xa0f349b16e0f338ee760a9954ff5dbf2a402cff3320f3fe2c3755aee8babc335; + vm.createSelectFork("", txHash); + // If we get here, blob tx replay succeeded + assertTrue(true); + } +} + "# + .replace("", &endpoint), + ); + + cmd.args(["test", "-vvvv"]).assert_success(); +}); + // https://github.com/foundry-rs/foundry/issues/6579 forgetest_init!(include_custom_types_in_traces, |prj, cmd| { prj.add_test( From 49754a8131b5ac24511cd478acb414ef279f085e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 21:22:25 +0700 Subject: [PATCH 199/374] Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/docker-image.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index e0c9c518e8748..0000000000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -permissions: - contents: read - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From a859609cb8e0b2112adedec72aa758b1c3183fcf Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 21:42:42 +0700 Subject: [PATCH 200/374] Gamefi defi (#288) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .github/workflows/bump-forge-std.yml | 2 +- .github/workflows/ci.yml | 12 +- .github/workflows/docs.yml | 2 +- .github/workflows/nix.yml | 4 +- .github/workflows/test.yml | 5 +- Cargo.lock | 312 ++++++++++++----------- Cargo.toml | 4 +- crates/anvil/core/src/eth/mod.rs | 4 +- crates/anvil/src/eth/api.rs | 4 +- crates/anvil/src/eth/backend/fork.rs | 4 +- crates/anvil/src/eth/backend/mem/mod.rs | 12 +- crates/anvil/src/tasks/mod.rs | 4 +- crates/anvil/tests/it/anvil_api.rs | 2 +- crates/anvil/tests/it/api.rs | 4 +- crates/cheatcodes/assets/cheatcodes.json | 8 +- crates/cheatcodes/spec/src/vm.rs | 4 +- crates/chisel/src/dispatcher.rs | 2 - crates/chisel/src/executor.rs | 6 +- crates/chisel/tests/it/repl/mod.rs | 62 +++-- crates/config/src/compilation.rs | 33 ++- crates/config/src/lib.rs | 53 ++++ crates/evm/core/src/backend/mod.rs | 4 +- crates/forge/tests/cli/test_cmd/mod.rs | 27 ++ crates/linking/src/lib.rs | 1 + crates/test-utils/src/rpc.rs | 7 + deny.toml | 2 + flake.lock | 18 +- testdata/default/repros/Issue2851.t.sol | 30 --- testdata/utils/Vm.sol | 4 +- 29 files changed, 372 insertions(+), 264 deletions(-) delete mode 100644 testdata/default/repros/Issue2851.t.sol diff --git a/.github/workflows/bump-forge-std.yml b/.github/workflows/bump-forge-std.yml index 2fb3e8a9a536f..00109defa89b1 100644 --- a/.github/workflows/bump-forge-std.yml +++ b/.github/workflows/bump-forge-std.yml @@ -23,7 +23,7 @@ jobs: - name: Fetch and update forge-std tag run: curl 'https://api.github.com/repos/foundry-rs/forge-std/tags' | jq '.[0].commit.sha' -jr > testdata/forge-std-rev - name: Create pull request - uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 + uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v7 with: commit-message: "chore: bump forge-std version used for tests" title: "chore(tests): bump forge-std version" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 578a95c4738cc..ac58f9560d0f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - run: cargo test --workspace --doc typos: @@ -58,7 +58,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: crate-ci/typos@2d0ce569feab1f8752f1dde43cc2f2aa53236e06 # v1 + - uses: crate-ci/typos@1a319b54cc9e3b333fed6a5c88ba1a90324da514 # v1 shellcheck: runs-on: depot-ubuntu-latest @@ -88,7 +88,7 @@ jobs: components: clippy - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - run: cargo clippy --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings @@ -122,7 +122,7 @@ jobs: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - name: forge fmt shell: bash run: ./.github/scripts/format.sh --check @@ -140,11 +140,11 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2 + - uses: taiki-e/install-action@4c6723ec9c638cccae824b8957c5085b695c8085 # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - run: cargo hack check deny: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 04a99b63626ee..2ace7a6c6fcf1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,7 +32,7 @@ jobs: toolchain: nightly - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - name: Build documentation run: cargo doc --workspace --all-features --no-deps --document-private-items env: diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 2c51ee2d99fbd..7082dc5dff403 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,7 +19,7 @@ jobs: contents: write pull-requests: write steps: - - uses: DeterminateSystems/determinate-nix-action@95732e95d70db3ba1e0adc26a63c5e0375aba78c # v3 + - uses: DeterminateSystems/determinate-nix-action@1d699fc25db3f9e079cd2f168ca007a4183389be # v3 - uses: actions/checkout@v6 with: persist-credentials: false @@ -38,7 +38,7 @@ jobs: permissions: contents: read steps: - - uses: DeterminateSystems/determinate-nix-action@95732e95d70db3ba1e0adc26a63c5e0375aba78c # v3 + - uses: DeterminateSystems/determinate-nix-action@1d699fc25db3f9e079cd2f168ca007a4183389be # v3 - uses: actions/checkout@v6 with: persist-credentials: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d2e9ce09250e..79f4dfc4431db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2 + - uses: taiki-e/install-action@4c6723ec9c638cccae824b8957c5085b695c8085 # v2 with: tool: nextest @@ -102,7 +102,7 @@ jobs: restore-keys: | ${{ runner.os }}-foundry-${{ matrix.name }}- - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2 + - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - name: Setup Git config run: | git config --global user.name "GitHub Actions Bot" @@ -115,4 +115,5 @@ jobs: WS_ARCHIVE_URLS: ${{ secrets.WS_ARCHIVE_URLS }} ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} + ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} run: cargo nextest run ${{ matrix.flags }} diff --git a/Cargo.lock b/Cargo.lock index 0db46ec2c09e8..f19b33e2859ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35d744058a9daa51a8cf22a3009607498fcf82d3cf4c5444dd8056cdf651f471" +checksum = "b163ff4acf0eac29af05a911397cc418a76e153467b859398adc26cb9335a611" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e318e25fb719e747a7e8db1654170fc185024f3ed5b10f86c08d448a912f6e2" +checksum = "f3dcd2b4e208ce5477de90ccdcbd4bde2c8fb06af49a443974e92bb8f2c5e93f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364380a845193a317bcb7a5398fc86cdb66c47ebe010771dde05f6869bf9e64a" +checksum = "ee5655f234985f5ab1e31bef7e02ed11f0a899468cf3300e061e1b96e9e11de0" dependencies = [ "alloy-consensus", "alloy-eips", @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d39c80ffc806f27a76ed42f3351a455f3dc4f81d6ff92c8aad2cf36b7d3a34" +checksum = "7f01b6d8e5b4f3222aaf7f18613a7292e2fbc9163fe120649cd1b078ca534349" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d48a9101f4a67c22fae57489f1ddf3057b8ab4a368d8eac3be088b6e9d9c9d9" +checksum = "369f5707b958927176265e8a58627fc6195e5dfa5c55689396e68b241b3a72e6" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb56700a502913fa9d40367e8f8c77720b043447c7763305f4ab582d713e36d" +checksum = "cdb31eba85ca1ac2f9df9170ffda56b470816b899c8be577a5b4c6a830368682" dependencies = [ "alloy-primitives", "alloy-serde", @@ -215,9 +215,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4d7c5839d9f3a467900c625416b24328450c65702eb3d8caff8813e4d1d33" +checksum = "6847d641141b92a1557094aa6c236cbe49c06fb24144d4a21fe6acb970c15888" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -240,9 +240,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a171486ee60b6f98e12451b1eddd1458bc21571f3829f75fcf12bb16ae48e59" +checksum = "9ddbb0d31df814ea588fc61034c3f3d21dc3678e65e26a50fff21fd35b4d0d3b" dependencies = [ "alloy-contract", "alloy-primitives", @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba4b1be0988c11f0095a2380aa596e35533276b8fa6c9e06961bbfe0aebcac5" +checksum = "fe3192fca2eb0b0c4b122b3c2d8254496b88a4e810558dddd3ea2f30ad9469df" dependencies = [ "alloy-eips", "alloy-primitives", @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9a33550fc21fd77a3f8b63e99969d17660eec8dcc50a95a80f7c9964f7680b" +checksum = "83ba208044232d14d4adbfa77e57d6329f51bc1acc21f5667bb7db72d88a0831" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -304,9 +304,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9914c147bb9b25f440eca68a31dc29f5c22298bfa7754aa802965695384122b0" +checksum = "84e3cf01219c966f95a460c95f1d4c30e12f6c18150c21a30b768af2a2a29142" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -316,9 +316,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72cf87cda808e593381fb9f005ffa4d2475552b7a6c5ac33d087bf77d82abd0" +checksum = "d4ab3330e491053e9608b2a315f147357bb8acb9377a988c1203f2e8e2b296c9" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12aeb37b6f2e61b93b1c3d34d01ee720207c76fe447e2a2c217e433ac75b17f5" +checksum = "c1e22ff194b1e34b4defd1e257e3fe4dce0eee37451c7757a1510d6b23e7379a" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -357,9 +357,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd29ace62872083e30929cd9b282d82723196d196db589f3ceda67edcc05552" +checksum = "b8a6cbb9f431bdad294eebb5af9b293d6979e633bfe5468d1e87c1421a858265" dependencies = [ "alloy-consensus", "alloy-eips", @@ -388,9 +388,9 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f96fb2fce4024ada5b2c11d4076acf778a0d3e4f011c6dfd2ffce6d0fcf84ee9" +checksum = "6472c610150c4c4c15be9e1b964c9b78068f933bda25fb9cdf09b9ac2bb66f36" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -400,9 +400,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db950a29746be9e2f2c6288c8bd7a6202a81f999ce109a2933d2379970ec0fa" +checksum = "f6a0fb18dd5fb43ec5f0f6a20be1ce0287c79825827de5744afaa6c957737c33" dependencies = [ "alloy-rlp", "arbitrary", @@ -431,9 +431,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b710636d7126e08003b8217e24c09f0cca0b46d62f650a841736891b1ed1fc1" +checksum = "3f5dde1abc3d582e53d139904fcdd8b2103f0bd03e8f2acb4292edbbaeaa7e6e" dependencies = [ "alloy-chains", "alloy-consensus", @@ -476,9 +476,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd4c64eb250a18101d22ae622357c6b505e158e9165d4c7974d59082a600c5e" +checksum = "acbfe0a3c553a027f722185fb574124d205147fffb309cae52d0a2094f076887" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -520,9 +520,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0882e72d2c1c0c79dcf4ab60a67472d3f009a949f774d4c17d0bdb669cfde05" +checksum = "5a94bdef2710322c6770be08689fee0878c2ad75615b8fc40e05d7f3c9618c0b" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -546,9 +546,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cf1398cb33aacb139a960fa3d8cf8b1202079f320e77e952a0b95967bf7a9f" +checksum = "811a573c8080e1b492d488e6a240ec5dd7677d7167e91ce9cb4d0ec1fcac8027" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -562,9 +562,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ce4c24e416bd0f17fceeb2f26cd8668df08fe19e1dc02f9d41c3b8ed1e93e0" +checksum = "838ca94be532a929f27961851000ec8bbbaeb06e2a2bcca44fac7855a2fe0f6f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -574,9 +574,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a63fb40ed24e4c92505f488f9dd256e2afaed17faa1b7a221086ebba74f4122" +checksum = "12df0b34551ca2eab8ec83b56cb709ee5da991737282180d354a659b907f00dc" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -585,9 +585,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16633087e23d8d75161c3a59aa183203637b817a5a8d2f662f612ccb6d129af0" +checksum = "32598a2443750a2e884c1b48efccaeeaae75e7eb4e0f13df9146b78107b4c301" dependencies = [ "alloy-eips", "alloy-primitives", @@ -601,9 +601,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4936f579d9d10eae01772b2ab3497f9d568684f05f26f8175e12f9a1a2babc33" +checksum = "6c49a3a168a5bf18f1cf7ed5723a650aebe714edf7665b53dacf5707716733d0" dependencies = [ "alloy-primitives", "derive_more", @@ -613,9 +613,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c60bdce3be295924122732b7ecd0b2495ce4790bedc5370ca7019c08ad3f26e" +checksum = "ffe16cd1dea6089902ec609e04261a9ae6d11ec66005ba24c1f97f0eefbc0fa9" dependencies = [ "alloy-consensus", "alloy-eips", @@ -633,9 +633,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eae0c7c40da20684548cbc8577b6b7447f7bf4ddbac363df95e3da220e41e72" +checksum = "b7f9f130511b8632686dfe6f9909b38d7ae4c68de3ce17d28991400646a39b25" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -654,9 +654,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef206a4b8d436fbb7cf2e6a61c692d11df78f9382becc3c9a283bd58e64f0583" +checksum = "cafe859944638c5d57d1a3a0034cdb5d07c98c37de8adce5508f28834acf958f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -668,9 +668,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb5a795264a02222f9534435b8f40dcbd88de8e9d586647884aae24f389ebf2" +checksum = "afaa06544e36f223b99b1415a12911230fd527994f020736c3c7950d5080208e" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -680,9 +680,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0df1987ed0ff2d0159d76b52e7ddfc4e4fbddacc54d2fbee765e0d14d7c01b5" +checksum = "067b718d2e6ac1bb889341fcc7a250cfa49bcd3ba4f23923f1c1eb1f2b10cb7c" dependencies = [ "alloy-primitives", "serde", @@ -691,9 +691,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff69deedee7232d7ce5330259025b868c5e6a52fa8dffda2c861fb3a5889b24" +checksum = "acff6b251740ef473932386d3b71657d3825daebf2217fb41a7ef676229225d4" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc8784b7567d5cfdad7450c5c71ddbdc12b288b82ea559be7a5363c28f774210" +checksum = "a4111255269e2d96b7e064ffa98f94ebc51c8e18d43501a10808c316e6d5a4d6" dependencies = [ "alloy-consensus", "alloy-network", @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70059cb6f08c225be3840f8104573fd9342ca29117a735ea09ed8ee1be09fb9f" +checksum = "b3fe5d26c2e1144aa89d65a0f1d1faec4dbf4c3ea1f7375888274c6609de2234" dependencies = [ "alloy-consensus", "alloy-network", @@ -745,9 +745,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54e01fb2228c993c9ef7735043bb22c88a84502628fae820d6469c3bebddb84" +checksum = "018fe424516f3bad2aefca5ee556a9c75301d68ab5377e296f85d33d6dc446c0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -765,9 +765,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72cfe0be3ec5a8c1a46b2e5a7047ed41121d360d97f4405bb7c1c784880c86cb" +checksum = "c9129ef31975d987114c27c9930ee817cf3952355834d47f2fdf4596404507e8" dependencies = [ "alloy-consensus", "alloy-network", @@ -785,9 +785,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119c750dd3d2158c57ab14711dca06cd6dc5db657bd16e6ab46ed11112187958" +checksum = "af231a5e9b07e54cd3142ad51bd07cbb95b4ccb4db991897a0ab9e7ed052286f" dependencies = [ "alloy-consensus", "alloy-network", @@ -802,9 +802,9 @@ dependencies = [ [[package]] name = "alloy-signer-turnkey" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd65beb3a838bff2e528e2ac4f0cf14f55893c4f0bc67c8339f2752b3d144022" +checksum = "1dae31a1578b3dedbc7c07aa5376d399adcf883caca7fdd56151b7618df72634" dependencies = [ "alloy-consensus", "alloy-network", @@ -818,9 +818,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b96d5f5890605ba9907ce1e2158e2701587631dc005bfa582cf92dd6f21147" +checksum = "09eb18ce0df92b4277291bbaa0ed70545d78b02948df756bbd3d6214bf39a218" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -832,9 +832,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8247b7cca5cde556e93f8b3882b01dbd272f527836049083d240c57bf7b4c15" +checksum = "95d9fa2daf21f59aa546d549943f10b5cce1ae59986774019fbedae834ffe01b" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -851,9 +851,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd54f38512ac7bae10bbc38480eefb1b9b398ca2ce25db9cc0c048c6411c4f1" +checksum = "9396007fe69c26ee118a19f4dee1f5d1d6be186ea75b3881adf16d87f8444686" dependencies = [ "alloy-json-abi", "const-hex", @@ -869,9 +869,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b09815b44899564566d4d56613d14fa9a274b1043a021f00468568752f449" +checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" dependencies = [ "serde", "winnow", @@ -879,9 +879,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1038284171df8bfd48befc0c7b78f667a7e2be162f45f07bd1c378078ebe58" +checksum = "09aeea64f09a7483bdcd4193634c7e5cf9fd7775ee767585270cd8ce2d69dc95" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -891,9 +891,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be98b07210d24acf5b793c99b759e9a696e4a2e67593aec0487ae3b3e1a2478c" +checksum = "bec1fb08ee484e615f24867c0b154fff5722bb00176102a16868c6532b7c3623" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -914,9 +914,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4198a1ee82e562cab85e7f3d5921aab725d9bd154b6ad5017f82df1695877c97" +checksum = "64b722073c76f2de7e118d546ee1921c50710f97feb32aed50db94cfa5b663e1" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -929,9 +929,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8db249779ebc20dc265920c7e706ed0d31dbde8627818d1cbde60919b875bb0" +checksum = "bdedcf401aab4b96d8b5e6638b79d04a6afb96c0bfcb50a2324fbadfe65c47b3" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -949,15 +949,14 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad2344a12398d7105e3722c9b7a7044ea837128e11d453604dec6e3731a86e2" +checksum = "942210908f0c56941097f5653a5f334546940e6fd9073495b257e52216469feb" dependencies = [ "alloy-pubsub", "alloy-transport", "futures", "http 1.4.0", - "rustls", "serde_json", "tokio", "tokio-tungstenite 0.26.2", @@ -967,9 +966,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" +checksum = "2b77b56af09ead281337d06b1d036c88e2dc8a2e45da512a532476dbee94912b" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -983,9 +982,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333544408503f42d7d3792bfc0f7218b643d968a03d2c0ed383ae558fb4a76d0" +checksum = "04950a13cc4209d8e9b78f306e87782466bad8538c94324702d061ff03e211c9" dependencies = [ "darling 0.21.3", "proc-macro2", @@ -1957,9 +1956,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.9.6" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fda37911905ea4d3141a01364bc5509a0f32ae3f3b22d6e330c0abfb62d247" +checksum = "a392db6c583ea4a912538afb86b7be7c5d8887d91604f50eb55c262ee1b4a5f5" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -2080,9 +2079,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", @@ -2652,9 +2651,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.50" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "jobserver", @@ -3292,7 +3291,7 @@ dependencies = [ "document-features", "mio", "parking_lot", - "rustix 1.1.2", + "rustix 1.1.3", "signal-hook", "signal-hook-mio", "winapi", @@ -3556,18 +3555,18 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", @@ -3688,9 +3687,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dtoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" [[package]] name = "dtoa-short" @@ -4107,7 +4106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.59.0", ] @@ -4149,9 +4148,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "fixed-hash" @@ -4554,7 +4553,7 @@ version = "1.5.1" dependencies = [ "alloy-sol-types", "foundry-macros", - "schemars 1.1.0", + "schemars 1.2.0", "serde", "serde_json", ] @@ -6189,15 +6188,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8" dependencies = [ "jiff-static", "log", @@ -6208,9 +6207,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" dependencies = [ "proc-macro2", "quote", @@ -6418,9 +6417,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", @@ -6440,9 +6439,9 @@ dependencies = [ [[package]] name = "libz-rs-sys" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15413ef615ad868d4d65dce091cb233b229419c7c0c4bcaa746c0901c49ff39c" +checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" dependencies = [ "zlib-rs", ] @@ -7670,9 +7669,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59e70c4aef1e55797c2e8fd94a4f2a973fc972cfde0e0b05f683667b0cd39dd" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -7812,9 +7811,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" dependencies = [ "unicode-ident", ] @@ -8350,9 +8349,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.26" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -9049,9 +9048,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" +checksum = "7f5befb5191be3584a4edaf63435e8ff92ffff622e711ca7e77f8f8f365a9df8" dependencies = [ "alloy-rlp", "arbitrary", @@ -9167,9 +9166,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", @@ -9270,9 +9269,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "ryu-js" @@ -9330,9 +9329,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -9343,9 +9342,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633" +checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" dependencies = [ "proc-macro2", "quote", @@ -9570,16 +9569,16 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" dependencies = [ "indexmap 2.12.1", "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -9635,7 +9634,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.1.0", + "schemars 1.2.0", "serde_core", "serde_json", "serde_with_macros", @@ -9750,10 +9749,11 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -10349,9 +10349,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b1d2e2059056b66fec4a6bb2b79511d5e8d76196ef49c38996f4b48db7662f" +checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" dependencies = [ "paste", "proc-macro2", @@ -10414,14 +10414,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -10476,7 +10476,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.3", "windows-sys 0.60.2", ] @@ -11747,7 +11747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ "env_home", - "rustix 1.1.2", + "rustix 1.1.3", "winsafe", ] @@ -12411,9 +12411,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" + +[[package]] +name = "zmij" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f936044d677be1a1168fae1d03b583a285a5dd9d8cbf7b24c23aa1fc775235" +checksum = "e6d6085d62852e35540689d1f97ad663e3971fc19cf5eceab364d62c646ea167" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index dbeac987c0358..9f9fdc0498bdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -268,8 +268,8 @@ alloy-transport = { version = "1.1.3", default-features = false } alloy-transport-http = { version = "1.1.3", default-features = false } alloy-transport-ipc = { version = "1.1.3", default-features = false } alloy-transport-ws = { version = "1.1.3", default-features = false } -alloy-hardforks = { version = "0.4.5", default-features = false } -alloy-op-hardforks = { version = "0.4.5", default-features = false } +alloy-hardforks = { version = "0.4.7", default-features = false } +alloy-op-hardforks = { version = "0.4.7", default-features = false } ## alloy-core alloy-dyn-abi = "1.5.1" diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 34f44453998d7..43e0e8ecc7745 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -210,7 +210,7 @@ pub enum EthRequest { GetGenesisTime(()), #[serde(rename = "eth_getTransactionByBlockHashAndIndex")] - EthGetTransactionByBlockHashAndIndex(TxHash, Index), + EthGetTransactionByBlockHashAndIndex(B256, Index), #[serde(rename = "eth_getTransactionByBlockNumberAndIndex")] EthGetTransactionByBlockNumberAndIndex(BlockNumber, Index), @@ -219,7 +219,7 @@ pub enum EthRequest { EthGetRawTransactionByHash(TxHash), #[serde(rename = "eth_getRawTransactionByBlockHashAndIndex")] - EthGetRawTransactionByBlockHashAndIndex(TxHash, Index), + EthGetRawTransactionByBlockHashAndIndex(B256, Index), #[serde(rename = "eth_getRawTransactionByBlockNumberAndIndex")] EthGetRawTransactionByBlockNumberAndIndex(BlockNumber, Index), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 095d1337a3076..b8c193fca084e 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -29,7 +29,7 @@ use crate::{ filter::{EthFilter, Filters, LogsFilter}, mem::transaction_build, }; -use alloy_consensus::{Account, Blob, Transaction, TxEip4844Variant, transaction::Recovered}; +use alloy_consensus::{Blob, Transaction, TrieAccount, TxEip4844Variant, transaction::Recovered}; use alloy_dyn_abi::TypedData; use alloy_eips::{ eip2718::Encodable2718, @@ -751,7 +751,7 @@ impl EthApi { &self, address: Address, block_number: Option, - ) -> Result { + ) -> Result { node_info!("eth_getAccount"); let block_request = self.block_request(block_number).await?; diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index e251d8f11de81..995206fbfcc89 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -1,7 +1,7 @@ //! Support for forking off another client use crate::eth::{backend::db::Db, error::BlockchainError, pool::transactions::PoolTransaction}; -use alloy_consensus::Account; +use alloy_consensus::TrieAccount; use alloy_eips::eip2930::AccessListResult; use alloy_network::{AnyRpcBlock, AnyRpcTransaction, BlockResponse, TransactionResponse}; use alloy_primitives::{ @@ -297,7 +297,7 @@ impl ClientFork { &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_account={:?}", address); self.provider().get_account(address).block_id(blocknumber.into()).await } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 6cf5b085f4837..adc56fe33a711 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -33,8 +33,8 @@ use crate::{ }; use alloy_chains::NamedChain; use alloy_consensus::{ - Account, Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait, - TxEnvelope, Typed2718, + Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait, + TrieAccount, TxEnvelope, Typed2718, proofs::{calculate_receipt_root, calculate_transaction_root}, transaction::Recovered, }; @@ -1981,7 +1981,8 @@ impl Backend { GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()), GethDebugBuiltInTracerType::FourByteTracer | GethDebugBuiltInTracerType::MuxTracer - | GethDebugBuiltInTracerType::FlatCallTracer => { + | GethDebugBuiltInTracerType::FlatCallTracer + | GethDebugBuiltInTracerType::Erc7562Tracer => { Err(RpcError::invalid_params("unsupported tracer type").into()) } }, @@ -2569,7 +2570,7 @@ impl Backend { &self, address: Address, block_request: Option, - ) -> Result { + ) -> Result { self.with_database_at(block_request, |block_db, _| { let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; let account = db.get(&address).cloned().unwrap_or_default(); @@ -2577,7 +2578,7 @@ impl Backend { let code_hash = account.info.code_hash; let balance = account.info.balance; let nonce = account.info.nonce; - Ok(Account { balance, nonce, code_hash, storage_root }) + Ok(TrieAccount { balance, nonce, code_hash, storage_root }) }) .await? } @@ -2951,6 +2952,7 @@ impl Backend { } GethDebugBuiltInTracerType::NoopTracer | GethDebugBuiltInTracerType::MuxTracer + | GethDebugBuiltInTracerType::Erc7562Tracer | GethDebugBuiltInTracerType::FlatCallTracer => {} }, GethDebugTracerType::JsTracer(_code) => {} diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index deab3b29e38f3..2d73fc99d8364 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -63,7 +63,7 @@ impl TaskManager { /// let endpoint = "http://...."; /// let (api, handle) = spawn(NodeConfig::default().with_eth_rpc_url(Some(endpoint))).await; /// - /// let provider = RootProvider::connect_builtin(endpoint).await.unwrap(); + /// let provider = RootProvider::connect(endpoint).await.unwrap(); /// /// handle.task_manager().spawn_reset_on_new_polled_blocks(provider, api); /// # } @@ -120,7 +120,7 @@ impl TaskManager { /// # async fn t() { /// let (api, handle) = spawn(NodeConfig::default().with_eth_rpc_url(Some("http://...."))).await; /// - /// let provider = RootProvider::connect_builtin("ws://...").await.unwrap(); + /// let provider = RootProvider::connect("ws://...").await.unwrap(); /// /// handle.task_manager().spawn_reset_on_subscribed_blocks(provider, api); /// diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index fc89206db81fd..e675001d7a480 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -440,7 +440,7 @@ async fn can_get_node_info() { let block_number = provider.get_block_number().await.unwrap(); let block = provider.get_block(BlockId::from(block_number)).await.unwrap().unwrap(); - let hard_fork: &str = SpecId::PRAGUE.into(); + let hard_fork: &str = SpecId::OSAKA.into(); let expected_node_info = NodeInfo { current_block_number: 0_u64, diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 979b40c86f0f8..41df263fec070 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -571,7 +571,7 @@ async fn test_fill_transaction_eip4844_blob_fee() { // EIP-4844 blob transaction with sidecar but no blob fee let mut tx_req = TransactionRequest::default().with_from(from).with_to(Address::random()); - tx_req.sidecar = Some(sidecar); + tx_req.sidecar = Some(sidecar.into()); tx_req.transaction_type = Some(3); // EIP-4844 let filled = api.fill_transaction(WithOtherFields::new(tx_req)).await.unwrap(); @@ -602,7 +602,7 @@ async fn test_fill_transaction_eip4844_preserves_blob_fee() { .with_from(from) .with_to(Address::random()) .with_max_fee_per_blob_gas(provided_blob_fee); - tx_req.sidecar = Some(sidecar); + tx_req.sidecar = Some(sidecar.into()); tx_req.transaction_type = Some(3); // EIP-4844 let filled = api.fill_transaction(WithOtherFields::new(tx_req)).await.unwrap(); diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 8b6eeb049c9fd..2134b95ac571d 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -9994,9 +9994,9 @@ "func": { "id": "signCompact_0", "description": "Signs data with a `Wallet`.\nReturns a compact signature (`r`, `vs`) as per EIP-2098, where `vs` encodes both the\nsignature's `s` value, and the recovery id `v` in a single bytes32.\nThis format reduces the signature size from 65 to 64 bytes.", - "declaration": "function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs);", + "declaration": "function signCompact(Wallet calldata wallet, bytes32 digest) external pure returns (bytes32 r, bytes32 vs);", "visibility": "external", - "mutability": "", + "mutability": "pure", "signature": "signCompact((address,uint256,uint256,uint256),bytes32)", "selector": "0x3d0e292f", "selectorBytes": [ @@ -10174,9 +10174,9 @@ "func": { "id": "sign_0", "description": "Signs data with a `Wallet`.", - "declaration": "function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);", + "declaration": "function sign(Wallet calldata wallet, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s);", "visibility": "external", - "mutability": "", + "mutability": "pure", "signature": "sign((address,uint256,uint256,uint256),bytes32)", "selector": "0xb25c5a25", "selectorBytes": [ diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 87d938db5691e..59b6d96eec2f1 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2704,7 +2704,7 @@ interface Vm { /// Signs data with a `Wallet`. #[cheatcode(group = Crypto)] - function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); + function sign(Wallet calldata wallet, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); /// Signs data with a `Wallet`. /// @@ -2712,7 +2712,7 @@ interface Vm { /// signature's `s` value, and the recovery id `v` in a single bytes32. /// This format reduces the signature size from 65 to 64 bytes. #[cheatcode(group = Crypto)] - function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); + function signCompact(Wallet calldata wallet, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); /// Signs `digest` with `privateKey` using the secp256k1 curve. #[cheatcode(group = Crypto)] diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 6b611e795f3ce..a1497677aee18 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -132,8 +132,6 @@ impl ChiselDispatcher { // Create new source with exact input appended and parse let (new_source, do_execute) = source.clone_with_new_line(input.to_string())?; - // TODO: Cloning / parsing the session source twice on non-inspected inputs kinda sucks. - // Should change up how this works. let (cf, res) = source.inspect(input).await?; if let Some(res) = &res { let _ = sh_println!("{res}"); diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index f9cd04af13f6a..abb6be8693585 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -36,9 +36,7 @@ impl SessionSource { .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?; Ok((bytecode.into_owned(), output.final_pc(contract)?)) })?; - - let Some(final_pc) = final_pc else { return Ok(Default::default()) }; - + let final_pc = final_pc.unwrap_or_default(); let mut runner = self.build_runner(final_pc).await?; runner.run(bytecode) } @@ -59,7 +57,7 @@ impl SessionSource { let mut source = match self.clone_with_new_line(line) { Ok((source, _)) => source, Err(err) => { - debug!(%err, "failed to build new source"); + debug!(%err, "failed to build new source for inspection"); return Ok((ControlFlow::Continue(()), None)); } }; diff --git a/crates/chisel/tests/it/repl/mod.rs b/crates/chisel/tests/it/repl/mod.rs index 9a24801752b4a..6fb38b6ca1805 100644 --- a/crates/chisel/tests/it/repl/mod.rs +++ b/crates/chisel/tests/it/repl/mod.rs @@ -19,14 +19,14 @@ macro_rules! repl_test { }; } -repl_test!(test_repl_help, |repl| { +repl_test!(repl_help, |repl| { repl.sendln_raw("!h"); repl.expect("Chisel help"); repl.expect_prompt(); }); // Test abi encode/decode. -repl_test!(test_abi_encode_decode, |repl| { +repl_test!(abi_encode_decode, |repl| { repl.sendln("bytes memory encoded = abi.encode(42, \"hello\")"); repl.sendln("(uint num, string memory str) = abi.decode(encoded, (uint, string))"); repl.sendln("num"); @@ -36,7 +36,7 @@ repl_test!(test_abi_encode_decode, |repl| { }); // Test 0x prefixed strings. -repl_test!(test_hex_string_interpretation, |repl| { +repl_test!(hex_string_interpretation, |repl| { repl.sendln("string memory s = \"0x1234\""); repl.sendln("s"); // Should be treated as string, not hex literal. @@ -44,7 +44,7 @@ repl_test!(test_hex_string_interpretation, |repl| { }); // Test cheatcodes availability. -repl_test!(test_cheatcodes_available, "", init = true, |repl| { +repl_test!(cheatcodes_available, "", init = true, |repl| { repl.sendln("address alice = address(0x1)"); repl.sendln("alice.balance"); @@ -60,12 +60,12 @@ repl_test!(test_cheatcodes_available, "", init = true, |repl| { }); // Test empty inputs. -repl_test!(test_empty_input, |repl| { +repl_test!(empty_input, |repl| { repl.sendln(" \n \n\n \t \t \n \n\t\t\t\t \n \n"); }); // Issue #4130: Test type(intN).min correctness. -repl_test!(test_int_min_values, |repl| { +repl_test!(int_min_values, |repl| { repl.sendln("type(int8).min"); repl.expect("-128"); repl.sendln("type(int256).min"); @@ -74,7 +74,7 @@ repl_test!(test_int_min_values, |repl| { // Issue #4393: Test edit command with traces. // TODO: test `!edit` -// repl_test!(test_edit_with_traces, |repl| { +// repl_test!(edit_with_traces, |repl| { // repl.sendln("!traces"); // repl.sendln("uint x = 42"); // repl.sendln("!edit"); @@ -83,7 +83,7 @@ repl_test!(test_int_min_values, |repl| { // }); // Test tuple support. -repl_test!(test_tuples, |repl| { +repl_test!(tuples, |repl| { repl.sendln("(uint a, uint b) = (1, 2)"); repl.sendln("a"); repl.expect("Decimal: 1"); @@ -92,7 +92,7 @@ repl_test!(test_tuples, |repl| { }); // Issue #4467: Test import. -repl_test!(test_import, "", init = true, |repl| { +repl_test!(import, "", init = true, |repl| { repl.sendln("import {Counter} from \"src/Counter.sol\""); repl.sendln("Counter c = new Counter()"); // TODO: pre-existing inspection failure. @@ -106,7 +106,7 @@ repl_test!(test_import, "", init = true, |repl| { }); // Issue #4617: Test code after assembly return. -repl_test!(test_assembly_return, |repl| { +repl_test!(assembly_return, |repl| { repl.sendln("uint x = 1;"); repl.sendln("assembly { mstore(0x0, 0x1337) return(0x0, 0x20) }"); repl.sendln("x = 2;"); @@ -116,25 +116,25 @@ repl_test!(test_assembly_return, |repl| { }); // Issue #4652: Test commands with trailing whitespace. -repl_test!(test_trailing_whitespace, |repl| { +repl_test!(trailing_whitespace, |repl| { repl.sendln("uint x = 42 "); repl.sendln("x"); repl.expect("Decimal: 42"); }); // Issue #4652: Test that solc flags are respected. -repl_test!(test_solc_flags, "--use 0.8.23", |repl| { +repl_test!(solc_flags, "--use 0.8.23", |repl| { repl.sendln("pragma solidity 0.8.24;"); repl.expect("invalid solc version"); }); // Issue #4915: `chisel eval` -repl_test!(test_eval_subcommand, "eval type(uint8).max", |repl| { +repl_test!(eval_subcommand, "eval type(uint8).max", |repl| { repl.expect("Decimal: 255"); }); // Issue #4938: Test memory/stack dumps with assembly. -repl_test!(test_assembly_memory_dump, |repl| { +repl_test!(assembly_memory_dump, |repl| { let input = r#" uint256 value = 12345; string memory str; @@ -154,34 +154,34 @@ assembly { }); // Issue #5051, #8978: Test EVM version normalization. -repl_test!(test_evm_version_normalization, "--use 0.7.6 --evm-version london", |repl| { +repl_test!(evm_version_normalization, "--use 0.7.6 --evm-version london", |repl| { repl.sendln("uint x;\nx"); repl.expect("Decimal: 0"); }); // Issue #5481: Test function return values are displayed. -repl_test!(test_function_return_display, |repl| { +repl_test!(function_return_display, |repl| { repl.sendln("function add(uint a, uint b) public pure returns (uint) { return a + b; }"); repl.sendln("add(2, 3)"); repl.expect("Decimal: 5"); }); // Issue #5737: Test bytesN return types. -repl_test!(test_bytes_length_type, |repl| { +repl_test!(bytes_length_type, |repl| { repl.sendln("bytes10 b = bytes10(0)"); repl.sendln("b.length"); repl.expect("Decimal: 10"); }); // Issue #5737: Test bytesN indexing return type. -repl_test!(test_bytes_index_type, |repl| { +repl_test!(bytes_index_type, |repl| { repl.sendln("bytes32 b = bytes32(uint256(0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20))"); repl.sendln("b[3]"); repl.expect("Data: 0x0400000000000000000000000000000000000000000000000000000000000000"); }); // Issue #6618: Test fetching interface with structs. -repl_test!(test_fetch_interface_with_structs, |repl| { +repl_test!(fetch_interface_with_structs, |repl| { repl.sendln_raw("!fe 0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789 IEntryPoint"); repl.expect( "Added 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789's interface to source as `IEntryPoint`", @@ -192,7 +192,7 @@ repl_test!(test_fetch_interface_with_structs, |repl| { }); // Issue #7035: Test that hex strings aren't checksummed as addresses. -repl_test!(test_hex_string_no_checksum, |repl| { +repl_test!(hex_string_no_checksum, |repl| { repl.sendln("function test(string memory s) public pure returns (string memory) { return s; }"); repl.sendln("test(\"0xe5f3af50fe5d0bf402a3c6f55ccc47d4307922d4\")"); // Should return the exact string, not checksummed. @@ -200,7 +200,7 @@ repl_test!(test_hex_string_no_checksum, |repl| { }); // Issue #7050: Test enum min/max operations. -repl_test!(test_enum_min_max, |repl| { +repl_test!(enum_min_max, |repl| { repl.sendln("enum Color { Red, Green, Blue }"); repl.sendln("type(Color).min"); repl.expect("Decimal: 0"); @@ -209,7 +209,7 @@ repl_test!(test_enum_min_max, |repl| { }); // Issue #9377: Test correct hex formatting for uint256. -repl_test!(test_uint256_hex_formatting, |repl| { +repl_test!(uint256_hex_formatting, |repl| { repl.sendln("uint256 x = 42"); // Full word hex should be 64 chars (256 bits). repl.sendln("x"); @@ -217,7 +217,7 @@ repl_test!(test_uint256_hex_formatting, |repl| { }); // Issue #9377: Test that full words are printed correctly. -repl_test!(test_full_word_hex_formatting, |repl| { +repl_test!(full_word_hex_formatting, |repl| { repl.sendln(r#"keccak256(abi.encode(uint256(keccak256("AgoraStableSwapStorage.OracleStorage")) - 1)) & ~bytes32(uint256(0xff))"#); repl.expect( "Hex (full word): 0x0a6b316b47a0cd26c1b582ae3dcffbd175283c221c3cb3d1c614e3e47f62a700", @@ -225,7 +225,7 @@ repl_test!(test_full_word_hex_formatting, |repl| { }); // Test that uint is printed properly with any size. -repl_test!(test_uint_formatting, |repl| { +repl_test!(uint_formatting, |repl| { for size in (8..=256).step_by(8) { repl.sendln(&format!("type(uint{size}).max")); repl.expect(&format!("Hex: 0x{}", "f".repeat(size / 4))); @@ -236,7 +236,7 @@ repl_test!(test_uint_formatting, |repl| { }); // Test that int is printed properly with any size. -repl_test!(test_int_formatting, |repl| { +repl_test!(int_formatting, |repl| { for size in (8..=256).step_by(8) { let size_minus_1: usize = size / 4 - 1; repl.sendln(&format!("type(int{size}).max")); @@ -252,3 +252,15 @@ repl_test!(test_int_formatting, |repl| { repl.expect(&format!("Hex: 0x{}e", "f".repeat(size_minus_1))); } }); + +repl_test!(uninitialized_variables, |repl| { + repl.sendln("uint256 x;"); + repl.sendln("address y;"); + repl.sendln("assembly { y := not(x) }"); + + repl.sendln("x"); + repl.expect("Hex: 0x0"); + + repl.sendln("y"); + repl.expect("Data: 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF"); +}); diff --git a/crates/config/src/compilation.rs b/crates/config/src/compilation.rs index bdd222348d055..492a18a0a96d6 100644 --- a/crates/config/src/compilation.rs +++ b/crates/config/src/compilation.rs @@ -7,7 +7,7 @@ use foundry_compilers::{ solc::{Restriction, SolcRestrictions}, }; use semver::VersionReq; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; /// Keeps possible overrides for default settings which users may configure to construct additional /// settings profile. @@ -69,6 +69,7 @@ pub enum RestrictionsError { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct CompilationRestrictions { pub paths: GlobMatcher, + #[serde(default, deserialize_with = "deserialize_version_req")] pub version: Option, pub via_ir: Option, pub bytecode_hash: Option, @@ -85,6 +86,36 @@ pub struct CompilationRestrictions { pub max_evm_version: Option, } +/// Custom deserializer for version field that rejects ambiguous bare version numbers. +fn deserialize_version_req<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let opt_string: Option = Option::deserialize(deserializer)?; + let Some(opt_string) = opt_string else { + return Ok(None); + }; + + let version = opt_string.trim(); + // Reject bare versions like "0.8.11" that lack an operator prefix + if version.chars().next().is_some_and(|c| c.is_ascii_digit()) { + return Err(serde::de::Error::custom(format!( + "Invalid version format '{opt_string}' in compilation_restrictions. \ + Bare version numbers are ambiguous because Cargo interprets them as caret requirements (e.g. '^{version}'). \ + Use an explicit constraint such as '={version}' for an exact version or '>={version}' for a minimum version." + ))); + } + + let req = VersionReq::parse(&opt_string).map_err(|e| { + serde::de::Error::custom(format!( + "Invalid version requirement '{opt_string}': {e}. \ + Examples: '=0.8.11' (exact), '>=0.8.11' (minimum), '>=0.8.11 <0.9.0' (range)." + )) + })?; + + Ok(Some(req)) +} + impl TryFrom for RestrictionsWithVersion { type Error = RestrictionsError; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 17f13a3921fe5..d54cb0aac63f5 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -6413,4 +6413,57 @@ mod tests { Ok(()) }); } + + #[test] + fn fails_on_ambiguous_version_in_compilation_restrictions() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [[profile.default.compilation_restrictions]] + paths = "src/*.sol" + version = "0.8.11" + "#, + )?; + + let err = Config::load().expect_err("expected bare version to fail"); + let err_msg = err.to_string(); + assert!( + err_msg.contains("Invalid version format '0.8.11'") + && err_msg.contains("Bare version numbers are ambiguous"), + "Expected error about ambiguous version, got: {err_msg}" + ); + + Ok(()) + }); + } + + #[test] + fn accepts_explicit_version_requirements() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [[profile.default.compilation_restrictions]] + paths = "src/*.sol" + version = "=0.8.11" + + [[profile.default.compilation_restrictions]] + paths = "test/*.sol" + version = ">=0.8.11" + "#, + )?; + + let config = Config::load().expect("should accept explicit version requirements"); + assert_eq!(config.compilation_restrictions.len(), 2); + + Ok(()) + }); + } } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 26e56e7bf883f..6429ceb3db23f 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -6,7 +6,7 @@ use crate::{ evm::new_evm_with_inspector, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, - utils::{configure_tx_env, configure_tx_req_env, get_blob_base_fee_update_fraction_by_spec_id}, + utils::{configure_tx_env, configure_tx_req_env, get_blob_base_fee_update_fraction}, }; use alloy_consensus::Typed2718; use alloy_evm::Evm; @@ -1954,7 +1954,7 @@ fn update_env_block(env: &mut EnvMut<'_>, block: &AnyRpcBlock) { if let Some(excess_blob_gas) = block.header.excess_blob_gas { env.block.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( excess_blob_gas, - get_blob_base_fee_update_fraction_by_spec_id(env.cfg.spec), + get_blob_base_fee_update_fraction(env.cfg.chain_id, block.header.timestamp), )); } } diff --git a/crates/forge/tests/cli/test_cmd/mod.rs b/crates/forge/tests/cli/test_cmd/mod.rs index 9666cf4d7e69d..0abffff57c4a8 100644 --- a/crates/forge/tests/cli/test_cmd/mod.rs +++ b/crates/forge/tests/cli/test_cmd/mod.rs @@ -678,6 +678,33 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); }); +// Validates BPO1 blob gas price calculation during fork transaction replay. +// Block 24127158 has a blob tx at index 0, target tx at index 1. +// Forking at the target tx replays the blob tx with correct BPO1 blob base fee calculation. +forgetest_init!(fork_tx_replay_bpo1_blob_base_fee, |prj, cmd| { + let endpoint = rpc::next_http_archive_rpc_url(); + + prj.add_test( + "BlobFork.t.sol", + &r#" +import {Test} from "forge-std/Test.sol"; + +contract BlobForkTest is Test { + function test_fork_with_blob_replay() public { + // Fork at tx index 1 in block 24127158, which replays blob tx at index 0 + bytes32 txHash = 0xa0f349b16e0f338ee760a9954ff5dbf2a402cff3320f3fe2c3755aee8babc335; + vm.createSelectFork("", txHash); + // If we get here, blob tx replay succeeded + assertTrue(true); + } +} + "# + .replace("", &endpoint), + ); + + cmd.args(["test", "-vvvv"]).assert_success(); +}); + // https://github.com/foundry-rs/foundry/issues/6579 forgetest_init!(include_custom_types_in_traces, |prj, cmd| { prj.add_test( diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 0066ab7c83843..ebf33383c9491 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -697,6 +697,7 @@ mod tests { } #[test] + #[ignore = "addresses depend on testdata utils internals for some reason"] fn link_create2_nested() { link_test(testdata().join("default/linking/nested"), |linker| { linker diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 336ea499fff41..cccde297ef967 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -187,6 +187,13 @@ fn next_url_inner(is_ws: bool, chain: NamedChain) -> String { return "https://celo.drpc.org".to_string(); } + if matches!(chain, Sepolia) { + let rpc_url = env::var("ETH_SEPOLIA_RPC").unwrap_or_default(); + if !rpc_url.is_empty() { + return rpc_url; + } + } + if matches!(chain, Arbitrum) { let rpc_url = env::var("ARBITRUM_RPC").unwrap_or_default(); if !rpc_url.is_empty() { diff --git a/deny.toml b/deny.toml index dfe4f0128f313..854f33033ed2c 100644 --- a/deny.toml +++ b/deny.toml @@ -9,6 +9,8 @@ ignore = [ "RUSTSEC-2024-0436", # https://rustsec.org/advisories/RUSTSEC-2024-0437 protobuf! Crash due to uncontrolled recursion in protobuf crate. "RUSTSEC-2024-0437", + # https://rustsec.org/advisories/RUSTSEC-2025-0137 `reciprocal_mg10` OOB, unused + "RUSTSEC-2025-0137", ] # This section is considered when running `cargo deny check bans`. diff --git a/flake.lock b/flake.lock index bd08c4fc369a7..48f8b3851e80f 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1766267710, - "narHash": "sha256-2icDwxnuPUl+vA5YwxbiFdRGlO/IrBH5Jllu8aswCkc=", + "lastModified": 1766818130, + "narHash": "sha256-FeVBDk9H5OPynbG+4wx0j04Mz/F+/LUfAPr4bnSeu/8=", "owner": "nix-community", "repo": "fenix", - "rev": "16642c5ae9941c36e4e3acb4ea02fd4959012010", + "rev": "3479aaf375090ede15fb623a84892f8597e00a25", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1766125104, - "narHash": "sha256-l/YGrEpLromL4viUo5GmFH3K5M1j0Mb9O+LiaeCPWEM=", + "lastModified": 1766840161, + "narHash": "sha256-Ss/LHpJJsng8vz1Pe33RSGIWUOcqM1fjrehjUkdrWio=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7d853e518814cca2a657b72eeba67ae20ebf7059", + "rev": "3edc4a30ed3903fdf6f90c837f961fa6b49582d1", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1766021917, - "narHash": "sha256-UfHpZWcVQouCRf64yAJGletSAuiAOmHnlmhHrsKTQtk=", + "lastModified": 1766755908, + "narHash": "sha256-1SBtDgvqG/tazoe3nyIfqhFgEI6RFSGBnh1Lnv0/Y6U=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "ea1d2998f2b3c6f6a494f72580550d521548e3d9", + "rev": "8c5a68e214578d4ccbb65fa8c48a4efd5c82697f", "type": "github" }, "original": { diff --git a/testdata/default/repros/Issue2851.t.sol b/testdata/default/repros/Issue2851.t.sol deleted file mode 100644 index bafa7c0ed0d07..0000000000000 --- a/testdata/default/repros/Issue2851.t.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.1; - -import "utils/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 Test { - Backdoor back; - - function setUp() public { - back = new Backdoor(); - } - - /// forge-config: default.fuzz.dictionary.max_fuzz_dictionary_literals = 0 - /// forge-config: default.fuzz.seed = '111' - function invariantNotZero() public { - assertEq(back.number(), 1); - } -} diff --git a/testdata/utils/Vm.sol b/testdata/utils/Vm.sol index cf5930928cf8e..8cced251493aa 100644 --- a/testdata/utils/Vm.sol +++ b/testdata/utils/Vm.sol @@ -492,7 +492,7 @@ interface Vm { function signAndAttachDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation); function signAndAttachDelegation(address implementation, uint256 privateKey, uint64 nonce) external returns (SignedDelegation memory signedDelegation); function signAndAttachDelegation(address implementation, uint256 privateKey, bool crossChain) external returns (SignedDelegation memory signedDelegation); - function signCompact(Wallet calldata wallet, bytes32 digest) external returns (bytes32 r, bytes32 vs); + function signCompact(Wallet calldata wallet, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(bytes32 digest) external pure returns (bytes32 r, bytes32 vs); function signCompact(address signer, bytes32 digest) external pure returns (bytes32 r, bytes32 vs); @@ -501,7 +501,7 @@ interface Vm { function signDelegation(address implementation, uint256 privateKey, bool crossChain) external returns (SignedDelegation memory signedDelegation); function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); function signWithNonceUnsafe(uint256 privateKey, bytes32 digest, uint256 nonce) 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 sign(Wallet calldata wallet, bytes32 digest) external pure returns (uint8 v, 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); From 818f43b6a4f5e94ba5e9d6b1af4d03699a883065 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 21:58:07 +0700 Subject: [PATCH 201/374] Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> From 3478be83972d1822a47bec3a0980894acc71e534 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:13:06 +0700 Subject: [PATCH 202/374] Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .circleci/ci_cargo.yml | 37 +++++ .circleci/web3_defi_gamefi.yml | 26 ++++ crates/cast/src/cmd/mktx.rs | 26 +--- crates/forge/Cargo.toml | 1 + crates/lint/src/linter.rs | 129 ++++++++++++++++++ crates/lint/src/sol/gas/keccak.rs | 98 ++----------- .../lint/testdata/DivideBeforeMultiply.stderr | 64 ++++----- 7 files changed, 243 insertions(+), 138 deletions(-) create mode 100644 .circleci/ci_cargo.yml create mode 100644 .circleci/web3_defi_gamefi.yml create mode 100644 crates/lint/src/linter.rs diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/crates/cast/src/cmd/mktx.rs b/crates/cast/src/cmd/mktx.rs index b4a9c0fb681bd..ebfb5afeadd47 100644 --- a/crates/cast/src/cmd/mktx.rs +++ b/crates/cast/src/cmd/mktx.rs @@ -1,14 +1,14 @@ use crate::tx::{self, CastTxBuilder}; use alloy_ens::NameOrAddress; -use alloy_network::{EthereumWallet, TransactionBuilder, eip2718::Encodable2718}; -use alloy_primitives::{Address, hex}; +use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}; +use alloy_primitives::hex; use alloy_provider::Provider; use alloy_signer::Signer; use clap::Parser; -use eyre::Result; +use eyre::{OptionExt, Result}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{LoadConfig, get_provider}, + utils::{get_provider, LoadConfig}, }; use std::{path::PathBuf, str::FromStr}; @@ -25,7 +25,6 @@ pub struct MakeTxArgs { sig: Option, /// The arguments of the function to call. - #[arg(allow_negative_numbers = true)] args: Vec, #[command(subcommand)] @@ -50,7 +49,7 @@ pub struct MakeTxArgs { /// Generate a raw RLP-encoded unsigned transaction. /// /// Relaxes the wallet requirement. - #[arg(long)] + #[arg(long, requires = "from")] raw_unsigned: bool, /// Call `eth_signTransaction` using the `--from` argument or $ETH_FROM as sender @@ -70,7 +69,6 @@ pub enum MakeTxSubcommands { sig: Option, /// The constructor arguments. - #[arg(allow_negative_numbers = true)] args: Vec, }, } @@ -98,7 +96,7 @@ impl MakeTxArgs { let provider = get_provider(&config)?; - let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config) + let tx_builder = CastTxBuilder::new(&provider, tx, &config) .await? .with_to(to) .await? @@ -108,17 +106,7 @@ impl MakeTxArgs { if raw_unsigned { // Build unsigned raw tx - // Check if nonce is provided when --from is not specified - // See: - if eth.wallet.from.is_none() && tx.nonce.is_none() { - eyre::bail!( - "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce" - ); - } - - // Use zero address as placeholder for unsigned transactions - let from = eth.wallet.from.unwrap_or(Address::ZERO); - + let from = eth.wallet.from.ok_or_eyre("missing `--from` address")?; let raw_tx = tx_builder.build_unsigned_raw(from).await?; sh_println!("{raw_tx}")?; diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 53b5243d54b38..515983876019c 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,6 +62,7 @@ alloy-rpc-types.workspace = true alloy-serde.workspace = true alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true revm.workspace = true diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index cb942510bbb49..7316f4c4239b7 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -1,103 +1,27 @@ use super::AsmKeccak256; use crate::{ - linter::{LateLintPass, LintContext}, + declare_forge_lint, + linter::EarlyLintPass, sol::{Severity, SolLint}, }; -use solar::{ - ast::{self as ast, Span}, - interface::kw, - sema::hir::{self}, -}; +use solar_ast::{Expr, ExprKind}; +use solar_interface::kw; declare_forge_lint!( ASM_KECCAK256, Severity::Gas, "asm-keccak256", - "use of inefficient hashing mechanism; consider using inline assembly" + "hash using inline assembly to save gas" ); -impl<'hir> LateLintPass<'hir> for AsmKeccak256 { - fn check_stmt( - &mut self, - ctx: &LintContext, - hir: &'hir hir::Hir<'hir>, - stmt: &'hir hir::Stmt<'hir>, - ) { - let check_expr_and_emit_lint = - |expr: &'hir hir::Expr<'hir>, assign: Option, is_return: bool| { - if let Some(hash_arg) = extract_keccak256_arg(expr) { - self.emit_lint( - ctx, - hir, - stmt.span, - expr, - hash_arg, - AsmContext { _assign: assign, _is_return: is_return }, - ); - } - }; - - match stmt.kind { - hir::StmtKind::DeclSingle(var_id) => { - let var = hir.variable(var_id); - if let Some(init) = var.initializer { - // Constants should be optimized by the compiler, so no gas savings apply. - if !matches!(var.mutability, Some(hir::VarMut::Constant)) { - check_expr_and_emit_lint(init, var.name, false); - } +impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 { + fn check_expr(&mut self, ctx: &crate::linter::LintContext<'_>, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(expr, _) = &expr.kind { + if let ExprKind::Ident(ident) = &expr.kind { + if ident.name == kw::Keccak256 { + ctx.emit(&ASM_KECCAK256, expr.span); } } - // Expressions that don't (directly) assign to a variable - hir::StmtKind::Expr(expr) - | hir::StmtKind::Emit(expr) - | hir::StmtKind::Revert(expr) - | hir::StmtKind::DeclMulti(_, expr) - | hir::StmtKind::If(expr, ..) => check_expr_and_emit_lint(expr, None, false), - hir::StmtKind::Return(Some(expr)) => check_expr_and_emit_lint(expr, None, true), - _ => (), } } } - -impl AsmKeccak256 { - /// Emits lints (when possible with fix suggestions) for inefficient `keccak256` calls. - fn emit_lint( - &self, - ctx: &LintContext, - _hir: &hir::Hir<'_>, - _stmt_span: Span, - call: &hir::Expr<'_>, - _hash: &hir::Expr<'_>, - _asm_ctx: AsmContext, - ) { - ctx.emit(&ASM_KECCAK256, call.span); - } -} - -/// If the expression is a call to `keccak256` with one argument, returns that argument. -fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Expr<'hir>> { - let hir::ExprKind::Call( - callee, - hir::CallArgs { kind: hir::CallArgsKind::Unnamed(args), .. }, - .., - ) = &expr.kind - else { - return None; - }; - - let is_keccak = if let hir::ExprKind::Ident([hir::Res::Builtin(builtin)]) = callee.kind { - matches!(builtin.name(), kw::Keccak256) - } else { - return None; - }; - - if is_keccak && args.len() == 1 { Some(&args[0]) } else { None } -} - -// -- HELPER FUNCTIONS AND STRUCTS ---------------------------------------------------------------- - -#[derive(Debug, Clone, Copy)] -struct AsmContext { - _assign: Option, - _is_return: bool, -} diff --git a/crates/lint/testdata/DivideBeforeMultiply.stderr b/crates/lint/testdata/DivideBeforeMultiply.stderr index 7677d97452527..1c98f4b13d194 100644 --- a/crates/lint/testdata/DivideBeforeMultiply.stderr +++ b/crates/lint/testdata/DivideBeforeMultiply.stderr @@ -1,48 +1,48 @@ warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC - | -LL | (1 / 2) * 3; - | ^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC + | +3 | (1 / 2) * 3; + | ----------- + | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC - | -LL | ((1 / 2) * 3) * 4; - | ^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC + | +5 | ((1 / 2) * 3) * 4; + | ----------- + | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC - | -LL | ((1 * 2) / 3) * 4; - | ^^^^^^^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC + | +6 | ((1 * 2) / 3) * 4; + | ----------------- + | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC - | -LL | (1 / 2 / 3) * 4; - | ^^^^^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC + | +7 | (1 / 2 / 3) * 4; + | --------------- + | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC - | -LL | (1 / (2 + 3)) * 4; - | ^^^^^^^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC + | +8 | (1 / (2 + 3)) * 4; + | ----------------- + | + = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision --> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC | -LL | 1 / ((2 / 3) * 3); - | ^^^^^^^^^^^ +15 | 1 / ((2 / 3) * 3); + | ----------- | = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply From 4cd968ba7f3d847f42f4c243a550b7b22bd32885 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 31 Dec 2025 23:32:30 +0700 Subject: [PATCH 203/374] Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/evm/evm/src/executors/corpus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/executors/corpus.rs b/crates/evm/evm/src/executors/corpus.rs index 92eb4452ce802..f8333418caec3 100644 --- a/crates/evm/evm/src/executors/corpus.rs +++ b/crates/evm/evm/src/executors/corpus.rs @@ -209,7 +209,7 @@ impl CorpusManager { }) }; - 'corpus_replay: for entry in std::fs::read_dir(corpus_dir)? { + 'corpus_replay: for entry in std::fs::read_dir(&canonical_corpus_dir)? { let path = entry?.path(); // Canonicalize the candidate path, skip if it cannot be canonicalized (e.g. broken symlink) let canonical_path = match path.canonicalize() { From e614a6319ddbb974c94e478fc657b0f2fe9b4da9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 1 Jan 2026 00:41:48 +0700 Subject: [PATCH 204/374] Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci.yml | 31 +++++++++++++++++++++++++++++ .circleci/config.yml | 32 ++++++++++++++++++++++++++++++ .github/workflows/docker-image.yml | 21 ++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 .circleci/ci.yml create mode 100644 .circleci/config.yml create mode 100644 .github/workflows/docker-image.yml diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..76b2889f1c4b2 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000000000..e0c9c518e8748 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,21 @@ +name: Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From 3532bf0d43137fe7637f425747841bd71bd27407 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 1 Jan 2026 01:26:50 +0700 Subject: [PATCH 205/374] fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --- .circleci/config.yml | 10 +++++----- crates/wallets/src/signer.rs | 2 +- crates/wallets/src/wallet_browser/signer.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d5d401c51893c..f967cfaa30db5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,20 +1,20 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference +# See: https://circleci.com/docs/reference/configuration-reference version: 2.1 # Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs jobs: say-hello: # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job docker: # Specify the version you desire here # See: https://circleci.com/developer/images/image/cimg/base - image: cimg/base:current # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps steps: # Checkout the code as the first step. - checkout @@ -23,7 +23,7 @@ jobs: command: "echo Hello, World!" # Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows workflows: say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. # Inside the workflow, you define the jobs you want to run. diff --git a/crates/wallets/src/signer.rs b/crates/wallets/src/signer.rs index 5a5c77c676587..8d50c03d1a90b 100644 --- a/crates/wallets/src/signer.rs +++ b/crates/wallets/src/signer.rs @@ -322,7 +322,7 @@ impl Signer for WalletSigner { #[async_trait] impl TxSigner for WalletSigner { fn address(&self) -> Address { - delegate!(self, inner => alloy_signer::Signer::address(inner)) + Signer::address(self) } async fn sign_transaction( diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs index 1e3df775e2598..78cf166a35ad0 100644 --- a/crates/wallets/src/wallet_browser/signer.rs +++ b/crates/wallets/src/wallet_browser/signer.rs @@ -177,7 +177,7 @@ impl Signer for BrowserSigner { #[async_trait] impl TxSigner for BrowserSigner { fn address(&self) -> Address { - self.address + Signer::address(self) } async fn sign_transaction( From 4f1dfb9fc6b58ab9b248af34e359f5600a743381 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 1 Jan 2026 01:40:10 +0700 Subject: [PATCH 206/374] fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --- .circleci/config.yml | 10 +++++----- crates/wallets/src/signer.rs | 2 +- crates/wallets/src/wallet_browser/signer.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 76b2889f1c4b2..4168efef0971f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,20 +1,20 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference +# See: https://circleci.com/docs/reference/configuration-reference version: 2.1 # Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs jobs: say-hello: # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job docker: # Specify the version you desire here # See: https://circleci.com/developer/images/image/cimg/base - image: cimg/base:current # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps steps: # Checkout the code as the first step. - checkout @@ -23,7 +23,7 @@ jobs: command: "echo Hello, World!" # Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows workflows: say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. # Inside the workflow, you define the jobs you want to run. diff --git a/crates/wallets/src/signer.rs b/crates/wallets/src/signer.rs index 5a5c77c676587..8d50c03d1a90b 100644 --- a/crates/wallets/src/signer.rs +++ b/crates/wallets/src/signer.rs @@ -322,7 +322,7 @@ impl Signer for WalletSigner { #[async_trait] impl TxSigner for WalletSigner { fn address(&self) -> Address { - delegate!(self, inner => alloy_signer::Signer::address(inner)) + Signer::address(self) } async fn sign_transaction( diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs index 1e3df775e2598..78cf166a35ad0 100644 --- a/crates/wallets/src/wallet_browser/signer.rs +++ b/crates/wallets/src/wallet_browser/signer.rs @@ -177,7 +177,7 @@ impl Signer for BrowserSigner { #[async_trait] impl TxSigner for BrowserSigner { fn address(&self) -> Address { - self.address + Signer::address(self) } async fn sign_transaction( From 20435ca66ec2f373542d4bed52ef33834a175198 Mon Sep 17 00:00:00 2001 From: tskoyo Date: Thu, 1 Jan 2026 07:29:18 +0100 Subject: [PATCH 207/374] add beacon block root tests --- crates/cast/src/cmd/run.rs | 14 ++++---------- crates/cast/tests/cli/main.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 6152f5b6978a6..8336714a66bb2 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -2,7 +2,7 @@ use crate::{debug::handle_traces, utils::apply_chain_and_block_specific_env_chan use alloy_consensus::Transaction; use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{ - Address, Bytes, FixedBytes, U256, + Address, Bytes, U256, map::{AddressSet, HashMap}, }; use alloy_provider::Provider; @@ -183,7 +183,7 @@ impl RunArgs { env.evm_env.cfg_env.limit_contract_code_size = None; env.evm_env.block_env.number = U256::from(tx_block_number); - let mut parent_beacon_block_root: FixedBytes<32> = FixedBytes::default(); + let mut parent_beacon_block_root = None; if let Some(block) = &block { env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); @@ -194,13 +194,7 @@ impl RunArgs { env.evm_env.block_env.gas_limit = block.header.gas_limit; if env.evm_env.cfg_env.spec >= SpecId::CANCUN { - if let Some(beacon_root) = block.header.parent_beacon_block_root { - parent_beacon_block_root = beacon_root; - } else { - return Err(eyre::eyre!( - "ParentBeaconBlockRoot is missing for Cancun or later blocks" - )); - } + parent_beacon_block_root = block.header.parent_beacon_block_root; } // TODO: we need a smarter way to map the block to the corresponding evm_version for @@ -236,7 +230,7 @@ impl RunArgs { None, )?; - if parent_beacon_block_root != FixedBytes::default() { + if let Some(parent_beacon_block_root) = parent_beacon_block_root { let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().unwrap_or(0); executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index b776370d2ac4d..c4a809f6c065c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2954,6 +2954,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(fetch_artifact_from_etherscan, |_prj, cmd| { From 77200b02e604ea5c67643d6cc3c5931b58777b74 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 2 Jan 2026 01:54:09 +0700 Subject: [PATCH 208/374] Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/dev_stage.yml | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .circleci/dev_stage.yml diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..03e77d78fcc3c --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,50 @@ +# ~/.circleci/config.yml +version: 2.1 + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ From a0c703a3761df9e10fbaa622c2b9d52239c630eb Mon Sep 17 00:00:00 2001 From: tskoyo Date: Thu, 25 Dec 2025 21:18:00 +0100 Subject: [PATCH 209/374] EIP-4788 implementation --- crates/cast/src/cmd/run.rs | 23 ++++++++++++++++++++-- crates/evm/evm/src/executors/trace.rs | 28 ++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 54894fc10b7ea..51a08274ee7cb 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -2,7 +2,7 @@ use crate::{debug::handle_traces, utils::apply_chain_and_block_specific_env_chan use alloy_consensus::Transaction; use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{ - Address, Bytes, U256, + Address, Bytes, FixedBytes, U256, map::{AddressSet, HashMap}, }; use alloy_provider::Provider; @@ -31,7 +31,7 @@ use foundry_evm::{ utils::configure_tx_env, }; use futures::TryFutureExt; -use revm::DatabaseRef; +use revm::{DatabaseRef, primitives::hardfork::SpecId}; /// CLI arguments for `cast run`. #[derive(Clone, Debug, Parser)] @@ -183,6 +183,8 @@ impl RunArgs { env.evm_env.cfg_env.limit_contract_code_size = None; env.evm_env.block_env.number = U256::from(tx_block_number); + let mut parent_beacon_block_root: FixedBytes<32> = FixedBytes::default(); + if let Some(block) = &block { env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); env.evm_env.block_env.beneficiary = block.header.beneficiary; @@ -191,6 +193,16 @@ impl RunArgs { env.evm_env.block_env.basefee = block.header.base_fee_per_gas.unwrap_or_default(); env.evm_env.block_env.gas_limit = block.header.gas_limit; + if env.evm_env.cfg_env.spec >= SpecId::CANCUN { + if let Some(beacon_root) = block.header.parent_beacon_block_root { + parent_beacon_block_root = beacon_root; + } else { + return Err(eyre::eyre!( + "ParentBeaconBlockRoot is missing for Cancun or later blocks" + )); + } + } + // TODO: we need a smarter way to map the block to the corresponding evm_version for // commonly used chains if evm_version.is_none() { @@ -214,6 +226,7 @@ impl RunArgs { InternalTraceMode::None }) .with_state_changes(shell::verbosity() > 4); + let mut executor = TracingExecutor::new( env.clone(), fork, @@ -223,6 +236,12 @@ impl RunArgs { create2_deployer, None, )?; + + if parent_beacon_block_root != FixedBytes::default() { + let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().unwrap_or(0); + executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; + } + let mut env = Env::new_with_spec_id( env.evm_env.cfg_env.clone(), env.evm_env.block_env.clone(), diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index d951dde0d6663..319e2ab495ca6 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -2,7 +2,7 @@ use crate::{ Env, executors::{Executor, ExecutorBuilder}, }; -use alloy_primitives::{Address, U256, map::HashMap}; +use alloy_primitives::{Address, FixedBytes, U256, address, map::HashMap}; use alloy_rpc_types::state::StateOverride; use eyre::Context; use foundry_compilers::artifacts::EvmVersion; @@ -92,6 +92,32 @@ impl TracingExecutor { let chain = env.tx.chain_id.unwrap().into(); Ok((env, fork, chain, networks)) } + + /// Processes the beacon block root by storing it in the appropriate storage slots. + pub fn process_beacon_block_root( + &mut self, + block_timestamp: u64, + beacon_root: FixedBytes<32>, + ) -> eyre::Result<()> { + const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); + const HISTORY_BUFFER_LENGTH: u64 = 8191; + + let timestamp_index = block_timestamp % HISTORY_BUFFER_LENGTH; + let root_index = timestamp_index + HISTORY_BUFFER_LENGTH; + + let timestamp_slot = U256::from(timestamp_index); + let root_slot = U256::from(root_index); + + self.set_storage_slot(BEACON_ROOTS_ADDRESS, timestamp_slot, U256::from(block_timestamp))?; + + self.set_storage_slot( + BEACON_ROOTS_ADDRESS, + root_slot, + U256::from_be_bytes(beacon_root.into()), + )?; + + Ok(()) + } } impl Deref for TracingExecutor { From 61646b8242c176937abf134364180a44951ae4a2 Mon Sep 17 00:00:00 2001 From: tskoyo Date: Sat, 27 Dec 2025 14:55:45 +0100 Subject: [PATCH 210/374] formatting --- crates/cast/src/cmd/run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 51a08274ee7cb..6152f5b6978a6 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -226,7 +226,6 @@ impl RunArgs { InternalTraceMode::None }) .with_state_changes(shell::verbosity() > 4); - let mut executor = TracingExecutor::new( env.clone(), fork, From 242e19c664797b1b9a0de672120b9eebe4287dbf Mon Sep 17 00:00:00 2001 From: tskoyo Date: Thu, 1 Jan 2026 07:29:18 +0100 Subject: [PATCH 211/374] add beacon block root tests --- crates/cast/src/cmd/run.rs | 14 ++++---------- crates/cast/tests/cli/main.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 6152f5b6978a6..8336714a66bb2 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -2,7 +2,7 @@ use crate::{debug::handle_traces, utils::apply_chain_and_block_specific_env_chan use alloy_consensus::Transaction; use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{ - Address, Bytes, FixedBytes, U256, + Address, Bytes, U256, map::{AddressSet, HashMap}, }; use alloy_provider::Provider; @@ -183,7 +183,7 @@ impl RunArgs { env.evm_env.cfg_env.limit_contract_code_size = None; env.evm_env.block_env.number = U256::from(tx_block_number); - let mut parent_beacon_block_root: FixedBytes<32> = FixedBytes::default(); + let mut parent_beacon_block_root = None; if let Some(block) = &block { env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); @@ -194,13 +194,7 @@ impl RunArgs { env.evm_env.block_env.gas_limit = block.header.gas_limit; if env.evm_env.cfg_env.spec >= SpecId::CANCUN { - if let Some(beacon_root) = block.header.parent_beacon_block_root { - parent_beacon_block_root = beacon_root; - } else { - return Err(eyre::eyre!( - "ParentBeaconBlockRoot is missing for Cancun or later blocks" - )); - } + parent_beacon_block_root = block.header.parent_beacon_block_root; } // TODO: we need a smarter way to map the block to the corresponding evm_version for @@ -236,7 +230,7 @@ impl RunArgs { None, )?; - if parent_beacon_block_root != FixedBytes::default() { + if let Some(parent_beacon_block_root) = parent_beacon_block_root { let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().unwrap_or(0); executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index b776370d2ac4d..c4a809f6c065c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2954,6 +2954,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(fetch_artifact_from_etherscan, |_prj, cmd| { From 4e7adcda7260affcadf032000bf3a9a05781c48e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 3 Jan 2026 01:31:13 +0700 Subject: [PATCH 212/374] Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/evm/evm/src/executors/trace.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 319e2ab495ca6..7b63c019e848c 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -100,7 +100,7 @@ impl TracingExecutor { beacon_root: FixedBytes<32>, ) -> eyre::Result<()> { const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); - const HISTORY_BUFFER_LENGTH: u64 = 8191; + const HISTORY_BUFFER_LENGTH: u64 = 8192; let timestamp_index = block_timestamp % HISTORY_BUFFER_LENGTH; let root_index = timestamp_index + HISTORY_BUFFER_LENGTH; From 87051b31120d54da1bb40aba4ab5bf4f8a26b217 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 3 Jan 2026 01:31:54 +0700 Subject: [PATCH 213/374] Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cast/src/cmd/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 8336714a66bb2..b65101f5db80d 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -231,7 +231,7 @@ impl RunArgs { )?; if let Some(parent_beacon_block_root) = parent_beacon_block_root { - let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().unwrap_or(0); + let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().wrap_err("failed to convert block timestamp to u64")?; executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; } From cda69962ef7eff908d5d6eb7c244c3e486aa02da Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 3 Jan 2026 07:03:13 +0700 Subject: [PATCH 214/374] Update crates/common/fmt/src/ui.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/fmt/src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index a7e583b6696e1..002439fb93152 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -891,7 +891,7 @@ blobGasUsed {}", receipt.effective_gas_price.pretty(), receipt.from.pretty(), receipt.gas_used.pretty(), - serde_json::to_string(receipt.inner.logs()).unwrap(), + serde_json::to_string(receipt.inner.logs()).unwrap_or_else(|e| e.to_string()), receipt.inner.logs_bloom().pretty(), self.state_root().pretty(), receipt.inner.status().pretty(), From 0303c65d2e2c0735b4c24da45c2038f43372108f Mon Sep 17 00:00:00 2001 From: "snyk-io[bot]" <141718529+snyk-io[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:58:06 +0000 Subject: [PATCH 215/374] feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr --- npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/package.json b/npm/package.json index 2362816053f8c..d596a7930b609 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.0.2", "bun": "^1.3.1", "typescript": "^5.9.3" }, From 43cd8245ddb3c6f14707ea5b80dfc71d6333e69b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:20:33 +0700 Subject: [PATCH 216/374] fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --- Cargo.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f5f65ef4645d..7b4472f3e5388 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3665,7 +3665,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3970,7 +3970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5409,8 +5409,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.2.1", - "windows-result 0.4.1", + "windows-link 0.1.3", + "windows-result 0.3.4", ] [[package]] @@ -5801,7 +5801,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -6178,7 +6178,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6924,7 +6924,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -8074,7 +8074,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -8111,9 +8111,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -8920,7 +8920,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -9716,7 +9716,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.17", + "thiserror 1.0.69", "tracing", "unicode-width 0.2.0", ] @@ -10042,9 +10042,9 @@ dependencies = [ [[package]] name = "svm-rs" -version = "0.5.22" +version = "0.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909e8ff825120cd2b34ceb236ab72e2a7f74b1d3a86c247936c8ff7a80c5d408" +checksum = "415b159b54c22d9810087f0991371fd6242a912673e982a7c4ca8ea122f7e00a" dependencies = [ "const-hex", "dirs", @@ -10054,7 +10054,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror 2.0.17", + "thiserror 1.0.69", "url", "zip", ] @@ -10168,7 +10168,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -10208,7 +10208,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -11520,7 +11520,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] From 6067ae2b1f3cf645627443274bae23e1d0f5a935 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:22:30 +0700 Subject: [PATCH 217/374] Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar --- .github/workflows/docker-image.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index e0c9c518e8748..0000000000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -permissions: - contents: read - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} From 784a3857a84dfa2a1bd8604dafc4a783d05e0c64 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:54:11 +0700 Subject: [PATCH 218/374] Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar From f61d8e8595b855634491413defc8508e84e0dede Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 8 Jan 2026 02:55:41 +0700 Subject: [PATCH 219/374] Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .circleci/dev_stage.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml index 03e77d78fcc3c..5ba351727d22b 100644 --- a/.circleci/dev_stage.yml +++ b/.circleci/dev_stage.yml @@ -1,5 +1,25 @@ -# ~/.circleci/config.yml +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + jobs: my-job: steps: From c97d4fa09efa5908766756c157ab34b6aee9d81f Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 8 Jan 2026 03:53:35 +0700 Subject: [PATCH 220/374] Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> From cc27594b6fbab226fdd556eb67fbdd6b19976577 Mon Sep 17 00:00:00 2001 From: Matt D Date: Thu, 8 Jan 2026 17:16:11 +0800 Subject: [PATCH 221/374] chore: ignore RUSTSEC (#13011) * update deny for CI * Update more --- deny.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deny.toml b/deny.toml index 854f33033ed2c..6b041e529e801 100644 --- a/deny.toml +++ b/deny.toml @@ -9,8 +9,10 @@ ignore = [ "RUSTSEC-2024-0436", # https://rustsec.org/advisories/RUSTSEC-2024-0437 protobuf! Crash due to uncontrolled recursion in protobuf crate. "RUSTSEC-2024-0437", - # https://rustsec.org/advisories/RUSTSEC-2025-0137 `reciprocal_mg10` OOB, unused - "RUSTSEC-2025-0137", + # https://rustsec.org/advisories/RUSTSEC-2025-0141 bincode is unmaintained, need to transition all deps to wincode first + "RUSTSEC-2025-0141", + # https://rustsec.org/advisories/RUSTSEC-2026-0002 lru unused directly: + "RUSTSEC-2026-0002", ] # This section is considered when running `cargo deny check bans`. From 39fb603d5a427c51393a91f36f447d4ff1413a8f Mon Sep 17 00:00:00 2001 From: onbjerg Date: Thu, 8 Jan 2026 10:50:39 +0100 Subject: [PATCH 222/374] chore(chisel): rm dead code (#13014) --- crates/chisel/src/source.rs | 47 ------------------------------------- 1 file changed, 47 deletions(-) diff --git a/crates/chisel/src/source.rs b/crates/chisel/src/source.rs index 576f36eab02f3..7eedb31923439 100644 --- a/crates/chisel/src/source.rs +++ b/crates/chisel/src/source.rs @@ -567,20 +567,6 @@ impl SessionSource { self.clear_output(); } - /// Clear the global-level code . - pub fn clear_global(&mut self) -> &mut Self { - String::clear(&mut self.global_code); - self.clear_output(); - self - } - - /// Clear the contract-level code . - pub fn clear_contract(&mut self) -> &mut Self { - String::clear(&mut self.contract_code); - self.clear_output(); - self - } - /// Clear the `run()` function code. pub fn clear_run(&mut self) -> &mut Self { String::clear(&mut self.run_code); @@ -694,39 +680,6 @@ impl SessionSource { Ok(intermediate_output) } - /// Construct the source as a valid Forge script. - pub fn to_script_source(&self) -> String { - let Self { - contract_name, - global_code, - contract_code: top_level_code, - run_code, - config, - .. - } = self; - - let script_import = - if !config.no_vm { "import {Script} from \"forge-std/Script.sol\";\n" } else { "" }; - - format!( - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0; - -{script_import} -{global_code} - -contract {contract_name} is Script {{ - {top_level_code} - - /// @notice Script entry point - function run() public {{ - {run_code} - }} -}}"#, - ) - } - /// Construct the REPL source. pub fn to_repl_source(&self) -> String { let Self { From a1a18dfb7cfbd5201373093e16cd79386acabfaf Mon Sep 17 00:00:00 2001 From: onbjerg Date: Thu, 8 Jan 2026 10:50:54 +0100 Subject: [PATCH 223/374] chore(cli): rm dead code (#13015) --- crates/cli/src/opts/evm.rs | 8 ------- crates/cli/src/utils/cmd.rs | 46 +------------------------------------ 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/crates/cli/src/opts/evm.rs b/crates/cli/src/opts/evm.rs index b1a55d5cbae03..6499d693ddf40 100644 --- a/crates/cli/src/opts/evm.rs +++ b/crates/cli/src/opts/evm.rs @@ -2,7 +2,6 @@ use alloy_primitives::{Address, B256, U256}; use clap::Parser; -use eyre::ContextCompat; use foundry_config::{ Chain, Config, figment::{ @@ -260,13 +259,6 @@ pub struct EnvArgs { pub enable_tx_gas_limit: bool, } -impl EvmArgs { - /// Ensures that fork url exists and returns its reference. - pub fn ensure_fork_url(&self) -> eyre::Result<&String> { - self.fork_url.as_ref().wrap_err("Missing `--fork-url` field.") - } -} - /// We have to serialize chain IDs and not names because when extracting an EVM `Env`, it expects /// `chain_id` to be `u64`. fn id(chain: &Option, s: S) -> Result { diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index ec483bca88359..b0f3661881843 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -2,10 +2,7 @@ use alloy_json_abi::JsonAbi; use eyre::{Result, WrapErr}; use foundry_common::{TestFunctionExt, fs, fs::json_files, selectors::SelectorKind, shell}; use foundry_compilers::{ - Artifact, ArtifactId, ProjectCompileOutput, - artifacts::{CompactBytecode, Settings}, - cache::{CacheEntry, CompilerCache}, - utils::read_json_file, + Artifact, ArtifactId, ProjectCompileOutput, artifacts::CompactBytecode, utils::read_json_file, }; use foundry_config::{Chain, Config, NamedChain, error::ExtractConfigError, figment::Figment}; use foundry_evm::{ @@ -65,47 +62,6 @@ pub fn find_contract_artifacts( Ok((abi, bin, id)) } -/// Helper function for finding a contract by ContractName -// 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: &CompilerCache, - name: &str, -) -> Result<(PathBuf, CacheEntry)> { - let mut cached_entry = None; - let mut alternatives = Vec::new(); - - for (abs_path, entry) in &cache.files { - for artifact_name in entry.artifacts.keys() { - if artifact_name == name { - if cached_entry.is_some() { - eyre::bail!( - "contract with duplicate name `{}`. please pass the path instead", - name - ) - } - cached_entry = Some((abs_path.to_owned(), entry.to_owned())); - } else { - alternatives.push(artifact_name); - } - } - } - - if let Some(entry) = cached_entry { - return Ok(entry); - } - - let mut err = format!("could not find artifact: `{name}`"); - if let Some(suggestion) = super::did_you_mean(name, &alternatives).pop() { - err = format!( - r#"{err} - - Did you mean `{suggestion}`?"# - ); - } - eyre::bail!(err) -} - /// Returns error if constructor has arguments. pub fn ensure_clean_constructor(abi: &JsonAbi) -> Result<()> { if let Some(constructor) = &abi.constructor From 709daa895b3e5f9ba5778be572cbbe098ed3468b Mon Sep 17 00:00:00 2001 From: onbjerg Date: Thu, 8 Jan 2026 10:51:01 +0100 Subject: [PATCH 224/374] chore(cheatcodes): rm dead code (#13016) --- crates/cheatcodes/src/script.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index ca9e9e8ccef86..80bd907198a95 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -320,12 +320,6 @@ impl Wallets { .unwrap_or_else(|| panic!("not all instances were dropped")) } - /// Locks inner Mutex and adds a signer to the [MultiWallet]. - pub fn add_private_key(&self, private_key: &B256) -> Result<()> { - 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: PrivateKeySigner) { self.inner.lock().multi_wallet.add_signer(WalletSigner::Local(wallet)); From 397cc8b8e5a1a1a4b8813f8cb5deb38c0ebcd0da Mon Sep 17 00:00:00 2001 From: onbjerg Date: Thu, 8 Jan 2026 10:52:27 +0100 Subject: [PATCH 225/374] chore(common): rm dead code (#13018) --- crates/common/src/contracts.rs | 9 --------- crates/common/src/serde_helpers.rs | 12 ------------ 2 files changed, 21 deletions(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 5bed2124aee62..f079d0f4fbf3d 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -395,15 +395,6 @@ impl ContractsByArtifact { Ok(contracts.first().copied()) } - /// Finds abi for contract which has the same contract name or identifier as `id`. - pub fn find_abi_by_name_or_identifier(&self, id: &str) -> Option { - self.iter() - .find(|(artifact, _)| { - artifact.name.split(".").next().unwrap() == id || artifact.identifier() == id - }) - .map(|(_, contract)| contract.abi.clone()) - } - /// Finds abi by name or source path /// /// Returns the abi and the contract name. diff --git a/crates/common/src/serde_helpers.rs b/crates/common/src/serde_helpers.rs index fc04bfbbdb3d5..59c2b7063ca23 100644 --- a/crates/common/src/serde_helpers.rs +++ b/crates/common/src/serde_helpers.rs @@ -37,18 +37,6 @@ impl FromStr for Numeric { } } -/// Deserializes the input into an `Option`, using [`from_int_or_hex`] to deserialize the -/// inner value. -pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - match Option::::deserialize(deserializer)? { - Some(val) => val.try_into_u256().map(Some), - None => Ok(None), - } -} - /// An enum that represents either a [serde_json::Number] integer, or a hex [U256]. #[derive(Debug, Deserialize)] #[serde(untagged)] From 703ffe40dd2a8f04c3aeaee9b9bb052e43f14117 Mon Sep 17 00:00:00 2001 From: onbjerg Date: Thu, 8 Jan 2026 10:52:36 +0100 Subject: [PATCH 226/374] chore(bench): rm dead code (#13017) --- benches/src/lib.rs | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/benches/src/lib.rs b/benches/src/lib.rs index eeb3cbeeafe11..55bbd9762b1d9 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -6,9 +6,7 @@ use foundry_common::{sh_eprintln, sh_println}; use foundry_compilers::project_util::TempProject; use foundry_test_utils::util::clone_remote; use once_cell::sync::Lazy; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::{ - env, path::{Path, PathBuf}, process::Command, str::FromStr, @@ -506,44 +504,3 @@ pub fn get_forge_version_details() -> Result { Ok(lines.first().unwrap_or(&"unknown").to_string()) } } - -/// Get Foundry versions to benchmark from environment variable or default -/// -/// Reads from FOUNDRY_BENCH_VERSIONS environment variable if set, -/// otherwise returns the default versions from FOUNDRY_VERSIONS constant. -/// -/// The environment variable should be a comma-separated list of versions, -/// e.g., "stable,nightly,v1.2.0" -pub fn get_benchmark_versions() -> Vec { - if let Ok(versions_env) = env::var("FOUNDRY_BENCH_VERSIONS") { - versions_env.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() - } else { - FOUNDRY_VERSIONS.iter().map(|&s| s.to_string()).collect() - } -} - -/// Setup Repositories for benchmarking -pub fn setup_benchmark_repos() -> Vec<(RepoConfig, BenchmarkProject)> { - // Check for FOUNDRY_BENCH_REPOS environment variable - let repos = if let Ok(repos_env) = env::var("FOUNDRY_BENCH_REPOS") { - // Parse repo specs from the environment variable - // Format should be: "org1/repo1,org2/repo2" - repos_env - .split(',') - .map(|s| s.trim()) - .filter(|s| !s.is_empty()) - .map(|s| s.parse::()) - .collect::>>() - .expect("Failed to parse FOUNDRY_BENCH_REPOS") - } else { - BENCHMARK_REPOS.clone() - }; - - repos - .par_iter() - .map(|repo_config| { - let project = BenchmarkProject::setup(repo_config).expect("Failed to setup project"); - (repo_config.clone(), project) - }) - .collect() -} From 933901d916096150b84eac3b7240304bd8bc3478 Mon Sep 17 00:00:00 2001 From: Theodore Solis Date: Thu, 8 Jan 2026 10:07:36 +0000 Subject: [PATCH 227/374] fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol --- crates/cli/src/opts/build/utils.rs | 16 ++++++++++++++-- crates/forge/src/cmd/build.rs | 2 +- crates/forge/src/cmd/lint.rs | 3 ++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/opts/build/utils.rs b/crates/cli/src/opts/build/utils.rs index e204130528946..1952886f36ee0 100644 --- a/crates/cli/src/opts/build/utils.rs +++ b/crates/cli/src/opts/build/utils.rs @@ -91,11 +91,20 @@ pub fn get_solar_sources_from_compile_output( config: &Config, output: &ProjectCompileOutput, target_paths: Option<&[PathBuf]>, + ignored_paths: Option<&[PathBuf]>, ) -> Result { let is_solidity_file = |path: &Path| -> bool { path.extension().and_then(|s| s.to_str()).is_some_and(|ext| SOLC_EXTENSIONS.contains(&ext)) }; + let is_ignored = |path: &Path| -> bool { + if let Some(ignored) = ignored_paths { + ignored.iter().any(|ignored_path| path == ignored_path) + } else { + false + } + }; + // Collect source path targets let mut source_paths: HashSet = if let Some(targets) = target_paths && !targets.is_empty() @@ -111,7 +120,10 @@ pub fn get_solar_sources_from_compile_output( while let Some(path) = queue.pop_front() { if source_paths.insert(path.clone()) { for import in output.graph().imports(path.as_path()) { - queue.push_back(import.to_path_buf()); + // Skip ignored imports to prevent solar from trying to compile them + if !is_ignored(import) { + queue.push_back(import.to_path_buf()); + } } } } @@ -166,7 +178,7 @@ pub fn configure_pcx_from_compile_output( output: &ProjectCompileOutput, target_paths: Option<&[PathBuf]>, ) -> Result<()> { - let solc = get_solar_sources_from_compile_output(config, output, target_paths)?; + let solc = get_solar_sources_from_compile_output(config, output, target_paths, None)?; configure_pcx_from_solc(pcx, &config.project_paths(), &solc, true); Ok(()) } diff --git a/crates/forge/src/cmd/build.rs b/crates/forge/src/cmd/build.rs index d37c73fd68cbb..a98fdc8d7eeb0 100644 --- a/crates/forge/src/cmd/build.rs +++ b/crates/forge/src/cmd/build.rs @@ -175,7 +175,7 @@ impl BuildArgs { .collect::>(); let solar_sources = - get_solar_sources_from_compile_output(config, output, Some(&input_files))?; + get_solar_sources_from_compile_output(config, output, Some(&input_files), None)?; if solar_sources.input.sources.is_empty() { if !input_files.is_empty() { sh_warn!( diff --git a/crates/forge/src/cmd/lint.rs b/crates/forge/src/cmd/lint.rs index c62320b003780..1d5e5857ee594 100644 --- a/crates/forge/src/cmd/lint.rs +++ b/crates/forge/src/cmd/lint.rs @@ -108,7 +108,8 @@ impl LintArgs { .with_mixed_case_exceptions(&config.lint.mixed_case_exceptions); let output = ProjectCompiler::new().files(input.iter().cloned()).compile(&project)?; - let solar_sources = get_solar_sources_from_compile_output(&config, &output, Some(&input))?; + let solar_sources = + get_solar_sources_from_compile_output(&config, &output, Some(&input), Some(&ignored))?; if solar_sources.input.sources.is_empty() { return Err(eyre!( "unable to lint. Solar only supports Solidity versions prior to 0.8.0" From 74d697cb5ef17b5f6a8c68e1531e04065f3bbbeb Mon Sep 17 00:00:00 2001 From: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:10:43 +0200 Subject: [PATCH 228/374] fix: deduplicate submodule status check logic (#13010) Update mod.rs --- crates/cli/src/utils/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 78e0f87d93e5d..924a717fed3af 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -699,10 +699,9 @@ ignore them in the `.gitignore` file." /// /// Ref: pub fn submodules_uninitialized(self) -> Result { - self.cmd() - .args(["submodule", "status"]) - .get_stdout_lossy() - .map(|stdout| stdout.lines().any(|line| line.starts_with('-'))) + // keep behavior consistent with `has_missing_dependencies`, but avoid duplicating the + // "submodule status has '-' prefix" logic. + self.has_missing_dependencies(std::iter::empty::<&OsStr>()) } /// Initializes the git submodules. From 04fae2756664825e6d416a94b93132597c08da5a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:45:22 +0700 Subject: [PATCH 229/374] Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar From a0042474d19f9aa331802e44b934704262d053ed Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:39:45 +0000 Subject: [PATCH 230/374] Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci-say-hello.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/ci-say-hello.yml diff --git a/.circleci/ci-say-hello.yml b/.circleci/ci-say-hello.yml deleted file mode 100644 index f967cfaa30db5..0000000000000 --- a/.circleci/ci-say-hello.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello From 8e4075aefdefe0d5a4d1e46e6c9c182d3ed0bf4c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:41:11 +0000 Subject: [PATCH 231/374] Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_v1.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .circleci/ci_v1.yml diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 94bf46b3bb04f..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test From 87dd517cf50fef686c8ef39431d06b16d4744d88 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:17:44 +0000 Subject: [PATCH 232/374] Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail From 99b70710b6ea82570ca82585b9c86deb0c0ac62b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:01:45 +0000 Subject: [PATCH 233/374] Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/snyk-container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml index 0db31480beb38..3809ed9cf966f 100644 --- a/.github/workflows/snyk-container.yml +++ b/.github/workflows/snyk-container.yml @@ -15,7 +15,7 @@ name: Snyk Container on: push: - branches: [ "master" ] + branches: [ "main" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] From aac85e54f87bbf19254a199908d44ab6fd8038cf Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:05:22 +0700 Subject: [PATCH 234/374] Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 626ce94448a4a..07413fef5495c 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,21 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; // Validate file name to avoid path traversal and absolute paths let name_str = name.to_string_lossy(); if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { // Skip invalid (potentially dangerous) file names continue; } - // Optionally verify canonicalized file is in from_dir to avoid symlink traversal + // Verify canonicalized file is in from_dir to avoid symlink traversal if let Ok(canonical_file) = file.canonicalize() { if !canonical_file.starts_with(&from_dir) { continue; } + } else { + continue; } - fs::copy(file, to_dir.join(name))?; + fs::copy(&file, to_dir.join(name))?; } Ok(()) } From ecaa54b2f0987d02109f0600785705744bba78e5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:06:30 +0700 Subject: [PATCH 235/374] Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- npm/scripts/stage-from-artifact.mjs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix From e5f8f49f4a400837c6f888369d86ecc3daedd812 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:07:08 +0700 Subject: [PATCH 236/374] Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- npm/src/const.mjs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/npm/src/const.mjs b/npm/src/const.mjs index a37e8dfd7ae30..d07978918c4eb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** From 04b8cfd9640a40b1d8e921481c4bf969ad418ab3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:17:39 +0700 Subject: [PATCH 237/374] fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol --- .github/workflows/docker-publish.yml | 2 +- .github/workflows/release.yml | 2 +- Cargo.lock | 148 ++----- Cargo.toml | 72 ++- crates/anvil/src/eth/api.rs | 3 +- crates/anvil/tests/it/traces.rs | 2 +- crates/cast/tests/cli/main.rs | 2 +- crates/cheatcodes/assets/cheatcodes.json | 20 + crates/cheatcodes/spec/src/vm.rs | 4 + crates/cheatcodes/src/evm.rs | 28 ++ crates/config/src/error.rs | 4 +- crates/evm/core/src/precompiles.rs | 32 ++ crates/evm/evm/src/inspectors/stack.rs | 8 +- crates/evm/traces/src/decoder/mod.rs | 13 +- crates/evm/traces/src/decoder/precompiles.rs | 183 +++++++- crates/evm/traces/src/lib.rs | 2 +- crates/fmt/src/state/mod.rs | 9 - crates/fmt/src/state/sol.rs | 11 - crates/forge/Cargo.toml | 2 + crates/forge/src/cmd/clone.rs | 435 +++++++++++++++++-- crates/forge/tests/cli/cmd.rs | 27 ++ crates/forge/tests/cli/precompiles.rs | 172 ++++++++ crates/primitives/src/network/receipt.rs | 2 + crates/verify/src/lib.rs | 2 +- npm/env.d.ts | 4 +- npm/src/const.mjs | 2 +- testdata/default/cheats/RecordLogs.t.sol | 35 ++ testdata/utils/Vm.sol | 1 + 28 files changed, 1022 insertions(+), 205 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 369875f9373b3..614aa6a508f67 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -24,7 +24,7 @@ env: IMAGE_NAME: ${{ github.repository }} # Keep in sync with `release.yml`. - RUST_PROFILE: maxperf + RUST_PROFILE: dist RUST_FEATURES: aws-kms,gcp-kms,turnkey,cli,asm-keccak,js-tracer jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5049cd617a0c1..85cc3e6f5859d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ env: IS_NIGHTLY: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} # Keep in sync with `docker-publish.yml`. - RUST_PROFILE: maxperf + RUST_PROFILE: dist RUST_FEATURES: aws-kms,gcp-kms,turnkey,cli,asm-keccak,js-tracer LAST_STABLE_VERSION: "v1.5.0" diff --git a/Cargo.lock b/Cargo.lock index ba6c0b99014d2..a27d890bc918a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1065,7 +1065,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1089,7 +1089,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3606,7 +3606,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3902,7 +3902,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4096,17 +4096,16 @@ dependencies = [ ] [[package]] -name = "figment" -version = "0.10.19" +name = "figment2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +checksum = "4380ce44915a6227efbb61e3885bc1c8e99fb9820f5db612abfac2c5cfc46871" dependencies = [ "atomic", "parking_lot", - "pear", "serde", "tempfile", - "toml 0.8.23", + "toml_edit 0.23.10+spec-1.0.0", "uncased", "version_check", ] @@ -4247,6 +4246,7 @@ dependencies = [ "toml_edit 0.24.0+spec-1.1.0", "tower-http", "tracing", + "url", "watchexec", "watchexec-events", "watchexec-signals", @@ -4273,7 +4273,7 @@ dependencies = [ "serde_json", "solar-compiler", "thiserror 2.0.17", - "toml 0.9.11+spec-1.1.0", + "toml", "tracing", ] @@ -4288,7 +4288,7 @@ dependencies = [ "similar", "snapbox", "solar-compiler", - "toml 0.9.11+spec-1.1.0", + "toml", ] [[package]] @@ -4507,7 +4507,7 @@ dependencies = [ "serde_json", "solar-compiler", "thiserror 2.0.17", - "toml 0.9.11+spec-1.1.0", + "toml", "tracing", "walkdir", ] @@ -4759,7 +4759,7 @@ dependencies = [ "dirs", "dunce", "eyre", - "figment", + "figment2", "foundry-block-explorers", "foundry-compilers", "foundry-evm-networks", @@ -4782,7 +4782,7 @@ dependencies = [ "soldeer-core", "tempfile", "thiserror 2.0.17", - "toml 0.9.11+spec-1.1.0", + "toml", "toml_edit 0.24.0+spec-1.1.0", "tracing", "unit-prefix", @@ -5341,7 +5341,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.1.3", + "windows-link 0.2.1", "windows-result 0.4.1", ] @@ -5998,12 +5998,6 @@ dependencies = [ "str_stack", ] -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - [[package]] name = "inotify" version = "0.11.0" @@ -6107,7 +6101,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6537,7 +6531,7 @@ dependencies = [ "regex", "serde", "serde_json", - "toml 0.9.11+spec-1.1.0", + "toml", "tracing", ] @@ -6560,7 +6554,7 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "toml 0.9.11+spec-1.1.0", + "toml", "topological-sort", "tracing", ] @@ -6924,7 +6918,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7406,29 +7400,6 @@ dependencies = [ "hmac", ] -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.114", -] - [[package]] name = "pem" version = "3.0.6" @@ -7841,19 +7812,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", - "version_check", - "yansi", -] - [[package]] name = "process-wrap" version = "8.2.1" @@ -8959,7 +8917,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9377,15 +9335,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - [[package]] name = "serde_spanned" version = "1.0.4" @@ -10196,7 +10145,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -10236,7 +10185,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10522,18 +10471,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - [[package]] name = "toml" version = "0.9.11+spec-1.1.0" @@ -10542,22 +10479,13 @@ checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap 2.13.0", "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", + "serde_spanned", + "toml_datetime", "toml_parser", "toml_writer", "winnow", ] -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -10567,20 +10495,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.13.0", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - [[package]] name = "toml_edit" version = "0.23.10+spec-1.0.0" @@ -10589,8 +10503,8 @@ checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap 2.13.0", "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", + "serde_spanned", + "toml_datetime", "toml_parser", "toml_writer", "winnow", @@ -10603,7 +10517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c740b185920170a6d9191122cafef7010bd6270a3824594bff6784c04d7f09e" dependencies = [ "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime", "toml_parser", "toml_writer", "winnow", @@ -10618,12 +10532,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - [[package]] name = "toml_writer" version = "1.0.6+spec-1.1.0" @@ -11539,7 +11447,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0c43b235223fa..6da5c1359a06c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ strip = "none" [profile.bench] inherits = "profiling" -[profile.maxperf] +[profile.dist] inherits = "release" lto = "fat" codegen-units = 1 @@ -182,18 +182,62 @@ walkdir.opt-level = 3 # Override packages which aren't perf-sensitive for faster compilation speed and smaller binary size. [profile.release.package] -alloy-sol-macro-expander.opt-level = "z" -figment.opt-level = "z" -foundry-compilers-artifacts-solc.opt-level = "z" -foundry-config.opt-level = "z" -html5ever.opt-level = "z" -mdbook-driver.opt-level = "z" -prettyplease.opt-level = "z" -protobuf.opt-level = "z" -pulldown-cmark.opt-level = "z" -syn-solidity.opt-level = "z" -syn.opt-level = "z" -trezor-client.opt-level = "z" +alloy-sol-macro-expander.opt-level = "s" +figment2.opt-level = "s" +foundry-compilers-artifacts-solc.opt-level = "s" +foundry-config.opt-level = "s" +html5ever.opt-level = "s" +mdbook-driver.opt-level = "s" +prettyplease.opt-level = "s" +protobuf.opt-level = "s" +pulldown-cmark.opt-level = "s" +syn-solidity.opt-level = "s" +syn.opt-level = "s" +trezor-client.opt-level = "s" + +# Networking stack (not perf-critical for CLI tools). +reqwest.opt-level = "s" +hyper.opt-level = "s" +hyper-util.opt-level = "s" +h2.opt-level = "s" +rustls.opt-level = "s" +tower.opt-level = "s" +tower-http.opt-level = "s" + +# Doc generation / templating. +mdbook-core.opt-level = "s" +mdbook-html.opt-level = "s" +font-awesome-as-a-crate.opt-level = "s" +handlebars.opt-level = "s" + +# Watch mode. +watchexec.opt-level = "s" +watchexec-events.opt-level = "s" +watchexec-signals.opt-level = "s" +watchexec-supervisor.opt-level = "s" + +# Soldeer package manager. +soldeer-commands.opt-level = "s" +soldeer-core.opt-level = "s" + +# Parsing / editing (not perf-sensitive in CLI). +toml_edit.opt-level = "s" +toml.opt-level = "s" + +# Block explorers / verification. +foundry-block-explorers.opt-level = "s" + +# Misc CLI dependencies. +clap_builder.opt-level = "s" +clap_complete.opt-level = "s" +comfy-table.opt-level = "s" +indicatif.opt-level = "s" +dialoguer.opt-level = "s" +ratatui.opt-level = "s" +crossterm.opt-level = "s" + +# Alloy JSON (parsing, not hot path). +alloy-json-abi.opt-level = "s" [workspace.dependencies] anvil = { path = "crates/anvil" } @@ -342,7 +386,7 @@ dunce = "1" evm-disassembler = "0.6" evmole = "0.8" eyre = "0.6" -figment = "0.10" +figment = { package = "figment2", version = "0.11" } futures = { version = "0.3", default-features = false } hyper = "1.8" indicatif = "0.18" diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 53db1bc9670c4..3e783b7dbafaa 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -3354,11 +3354,10 @@ impl EthApi { .max_fee_per_gas() .is_none() .then(|| request.set_max_fee_per_gas(self.gas_price())); - // TODO: use suggested tip instead of 0 request .max_priority_fee_per_gas() .is_none() - .then(|| request.set_max_priority_fee_per_gas(Default::default())); + .then(|| request.set_max_priority_fee_per_gas(MIN_SUGGESTED_PRIORITY_FEE)); } if tx_type == FoundryTxType::Eip4844 { request.as_ref().max_fee_per_blob_gas().is_none().then(|| { diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index dd4a196831cea..5bf7e1afb5457 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1237,7 +1237,7 @@ async fn test_debug_trace_transaction_pre_state_tracer() { let expected = r#" { "0x0000000000000000000000000000000000000000": { - "balance": "0x0" + "balance": "1206031000000000" }, "0x5fbdb2315678afecb367f032d93f642f64180aa3": { "balance": "0x0", diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index c4a809f6c065c..5946d87986190 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -3749,7 +3749,7 @@ Traces: │ │ │ │ └─ ← [Return] 0xc13089327d3c20c0ce35f2f058c423de29977e6950e406c095e366a8fabd463f │ │ │ ├─ [96] PRECOMPILES::sha256(0x424242424242424242424242424242424242424242424242424242424242424201000000c13089327d3c20c0ce35f2f058c423de29977e6950e406c095e366a8fabd463f) [staticcall] │ │ │ │ └─ ← [Return] 0xc544bd9a4ea526dda3a008f43c21b6f0be3031b1ff71832b9876915dc91deea0 - │ │ │ ├─ [6900] 0x0000000000000000000000000000000000000100::c544bd9a(4ea526dda3a008f43c21b6f0be3031b1ff71832b9876915dc91deea0dd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f925bf54fa13f88658092efa36c51b1e3c4db31d3afb92812fb852dac7cf9614bc479bf5da7241d9c4ab1b431b57ec3369587b4c831d7a564438990da053708c3289) [staticcall] + │ │ │ ├─ [6900] P256VERIFY::c544bd9a(4ea526dda3a008f43c21b6f0be3031b1ff71832b9876915dc91deea0dd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f925bf54fa13f88658092efa36c51b1e3c4db31d3afb92812fb852dac7cf9614bc479bf5da7241d9c4ab1b431b57ec3369587b4c831d7a564438990da053708c3289) [staticcall] │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001 │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000011bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000011bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 2134b95ac571d..67820b2030c23 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6524,6 +6524,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getRecordedLogsJson", + "description": "Gets all the recorded logs, in JSON format.", + "declaration": "function getRecordedLogsJson() external view returns (string memory logsJson);", + "visibility": "external", + "mutability": "view", + "signature": "getRecordedLogsJson()", + "selector": "0x3b171111", + "selectorBytes": [ + 59, + 23, + 17, + 17 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "getStateDiff", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 59b6d96eec2f1..640c97e3108fb 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -951,6 +951,10 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function getRecordedLogs() external view returns (Log[] memory logs); + /// Gets all the recorded logs, in JSON format. + #[cheatcode(group = Evm, safety = Safe)] + function getRecordedLogsJson() external view returns (string memory logsJson); + // -------- Gas Metering -------- // It's recommend to use the `noGasMetering` modifier included with forge-std, instead of diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 70e5f66785999..b5e76e1148694 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -55,6 +55,18 @@ pub(crate) mod mapping; pub(crate) mod mock; pub(crate) mod prank; +/// JSON-serializable log entry for `getRecordedLogsJson`. +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct LogJson { + /// The topics of the log, including the signature, if any. + topics: Vec, + /// The raw data of the log, hex-encoded with 0x prefix. + data: String, + /// The address of the log's emitter. + emitter: String, +} + /// Records storage slots reads and writes. #[derive(Clone, Debug, Default)] pub struct RecordAccess { @@ -403,6 +415,22 @@ impl Cheatcode for getRecordedLogsCall { } } +impl Cheatcode for getRecordedLogsJsonCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self {} = self; + let logs = state.recorded_logs.replace(Default::default()).unwrap_or_default(); + let json_logs: Vec<_> = logs + .into_iter() + .map(|log| LogJson { + topics: log.topics.iter().map(|t| format!("{t}")).collect(), + data: hex::encode_prefixed(&log.data), + emitter: format!("{}", log.emitter), + }) + .collect(); + Ok(serde_json::to_string(&json_logs)?.abi_encode()) + } +} + impl Cheatcode for pauseGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 61d954ef73956..67cc8d7e04762 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -12,7 +12,7 @@ pub struct ExtractConfigError { } impl ExtractConfigError { - /// Wraps the figment error + /// Wraps the figment error. pub fn new(error: figment::Error) -> Self { Self { error } } @@ -63,7 +63,7 @@ impl Error for ExtractConfigError { pub enum FoundryConfigError { /// An error thrown during toml parsing Toml(figment::Error), - /// Any other error thrown when constructing the config's figment + /// Any other error thrown when constructing the config's figment. Other(figment::Error), } diff --git a/crates/evm/core/src/precompiles.rs b/crates/evm/core/src/precompiles.rs index 62f7397a12bf9..fd569dc30dc07 100644 --- a/crates/evm/core/src/precompiles.rs +++ b/crates/evm/core/src/precompiles.rs @@ -30,6 +30,30 @@ pub const BLAKE_2F: Address = address!("0x00000000000000000000000000000000000000 /// The PointEvaluation precompile address. pub const POINT_EVALUATION: Address = address!("0x000000000000000000000000000000000000000a"); +/// The BLS12-381 G1ADD precompile address. +pub const BLS12_G1ADD: Address = address!("0x000000000000000000000000000000000000000b"); + +/// The BLS12-381 G1MSM precompile address. +pub const BLS12_G1MSM: Address = address!("0x000000000000000000000000000000000000000c"); + +/// The BLS12-381 G2ADD precompile address. +pub const BLS12_G2ADD: Address = address!("0x000000000000000000000000000000000000000d"); + +/// The BLS12-381 G2MSM precompile address. +pub const BLS12_G2MSM: Address = address!("0x000000000000000000000000000000000000000e"); + +/// The BLS12-381 pairing check precompile address. +pub const BLS12_PAIRING_CHECK: Address = address!("0x000000000000000000000000000000000000000f"); + +/// The BLS12-381 map Fp to G1 precompile address. +pub const BLS12_MAP_FP_TO_G1: Address = address!("0x0000000000000000000000000000000000000010"); + +/// The BLS12-381 map Fp2 to G2 precompile address. +pub const BLS12_MAP_FP2_TO_G2: Address = address!("0x0000000000000000000000000000000000000011"); + +/// The P256VERIFY precompile address. +pub const P256_VERIFY: Address = address!("0x0000000000000000000000000000000000000100"); + /// Precompile addresses. pub const PRECOMPILES: &[Address] = &[ EC_RECOVER, @@ -42,4 +66,12 @@ pub const PRECOMPILES: &[Address] = &[ EC_PAIRING, BLAKE_2F, POINT_EVALUATION, + BLS12_G1ADD, + BLS12_G1MSM, + BLS12_G2ADD, + BLS12_G2MSM, + BLS12_PAIRING_CHECK, + BLS12_MAP_FP_TO_G1, + BLS12_MAP_FP2_TO_G2, + P256_VERIFY, ]; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 8d889eca9a329..a76409308a0b3 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -964,11 +964,13 @@ impl Inspector> for InspectorStackRefMut<'_> if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() { // Handle mocked functions, replace bytecode address with mock if matched. if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) { + let input_bytes = call.input.bytes(ecx); // Check if any mock function set for call data or if catch-all mock function set // for selector. - if let Some(target) = mocks.get(&call.input.bytes(ecx)).or_else(|| { - call.input.bytes(ecx).get(..4).and_then(|selector| mocks.get(selector)) - }) { + if let Some(target) = mocks + .get(&input_bytes) + .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector))) + { call.bytecode_address = *target; call.known_bytecode = None; } diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 4f377fe03c510..38f40b244c042 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -18,8 +18,9 @@ use foundry_evm_core::{ constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS}, decode::RevertDecoder, precompiles::{ - BLAKE_2F, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, MOD_EXP, POINT_EVALUATION, - RIPEMD_160, SHA_256, + BLAKE_2F, BLS12_G1ADD, BLS12_G1MSM, BLS12_G2ADD, BLS12_G2MSM, BLS12_MAP_FP_TO_G1, + BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, + MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256, }, }; use itertools::Itertools; @@ -183,6 +184,14 @@ impl CallTraceDecoder { (EC_PAIRING, "ECPairing".to_string()), (BLAKE_2F, "Blake2F".to_string()), (POINT_EVALUATION, "PointEvaluation".to_string()), + (BLS12_G1ADD, "BLS12_G1ADD".to_string()), + (BLS12_G1MSM, "BLS12_G1MSM".to_string()), + (BLS12_G2ADD, "BLS12_G2ADD".to_string()), + (BLS12_G2MSM, "BLS12_G2MSM".to_string()), + (BLS12_PAIRING_CHECK, "BLS12_PAIRING_CHECK".to_string()), + (BLS12_MAP_FP_TO_G1, "BLS12_MAP_FP_TO_G1".to_string()), + (BLS12_MAP_FP2_TO_G2, "BLS12_MAP_FP2_TO_G2".to_string()), + (P256_VERIFY, "P256VERIFY".to_string()), ]), receive_contracts: Default::default(), fallback_contracts: Default::default(), diff --git a/crates/evm/traces/src/decoder/precompiles.rs b/crates/evm/traces/src/decoder/precompiles.rs index 7437c4eb1a3a1..fdbe744641cd7 100644 --- a/crates/evm/traces/src/decoder/precompiles.rs +++ b/crates/evm/traces/src/decoder/precompiles.rs @@ -2,8 +2,9 @@ use crate::{CallTrace, DecodedCallData}; use alloy_primitives::{Address, B256, U256, hex}; use alloy_sol_types::{SolCall, abi, sol}; use foundry_evm_core::precompiles::{ - BLAKE_2F, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, MOD_EXP, POINT_EVALUATION, - RIPEMD_160, SHA_256, + BLAKE_2F, BLS12_G1ADD, BLS12_G1MSM, BLS12_G2ADD, BLS12_G2MSM, BLS12_MAP_FP_TO_G1, + BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, + MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256, }; use itertools::Itertools; use revm_inspectors::tracing::types::DecodedCallTrace; @@ -33,6 +34,18 @@ interface Precompiles { /* 0x08 */ function ecpairing(EcPairingInput[] input) returns (bool success); /* 0x09 */ function blake2f(uint32 rounds, uint64[8] h, uint64[16] m, uint64[2] t, bool f) returns (uint64[8] h); /* 0x0a */ function pointEvaluation(bytes32 versionedHash, bytes32 z, bytes32 y, bytes1[48] commitment, bytes1[48] proof) returns (bytes value); + + // Prague BLS12-381 precompiles (EIP-2537) + /* 0x0b */ function bls12G1Add(bytes p1, bytes p2) returns (bytes result); + /* 0x0c */ function bls12G1Msm(bytes[] scalarsAndPoints) returns (bytes result); + /* 0x0d */ function bls12G2Add(bytes p1, bytes p2) returns (bytes result); + /* 0x0e */ function bls12G2Msm(bytes[] scalarsAndPoints) returns (bytes result); + /* 0x0f */ function bls12PairingCheck(bytes[] pairs) returns (bool success); + /* 0x10 */ function bls12MapFpToG1(bytes fp) returns (bytes result); + /* 0x11 */ function bls12MapFp2ToG2(bytes fp2) returns (bytes result); + + // Osaka precompiles (EIP-7212) + /* 0x100 */ function p256Verify(bytes32 hash, uint256 r, uint256 s, uint256 qx, uint256 qy) returns (bool success); } } use Precompiles::*; @@ -51,6 +64,14 @@ pub(super) fn is_known_precompile(address: Address, _chain_id: u64) -> bool { | EC_PAIRING | BLAKE_2F | POINT_EVALUATION + | BLS12_G1ADD + | BLS12_G1MSM + | BLS12_G2ADD + | BLS12_G2MSM + | BLS12_PAIRING_CHECK + | BLS12_MAP_FP_TO_G1 + | BLS12_MAP_FP2_TO_G2 + | P256_VERIFY ) } @@ -115,6 +136,14 @@ const PRECOMPILES: &[&dyn Precompile] = &[ &Ecpairing, &Blake2f, &PointEvaluation, + &Bls12G1Add, + &Bls12G1Msm, + &Bls12G2Add, + &Bls12G2Msm, + &Bls12PairingCheck, + &Bls12MapFpToG1, + &Bls12MapFp2ToG2, + &P256Verify, ]; struct Ecrecover; @@ -346,6 +375,156 @@ fn iter_to_string, T: std::fmt::Display>(iter: I) -> Strin format!("[{}]", iter.format(", ")) } +const G1_POINT_SIZE: usize = 128; +const G2_POINT_SIZE: usize = 256; +const SCALAR_SIZE: usize = 32; +const FP_SIZE: usize = 64; + +struct Bls12G1Add; +impl Precompile for Bls12G1Add { + fn address(&self) -> Address { + BLS12_G1ADD + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12G1AddCall::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let (p1, rest) = take_at_most(data, G1_POINT_SIZE); + let (p2, _) = take_at_most(rest, G1_POINT_SIZE); + Ok(vec![hex::encode_prefixed(p1), hex::encode_prefixed(p2)]) + } +} + +struct Bls12G1Msm; +impl Precompile for Bls12G1Msm { + fn address(&self) -> Address { + BLS12_G1MSM + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12G1MsmCall::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let pair_size = G1_POINT_SIZE + SCALAR_SIZE; + Ok(data.chunks(pair_size).map(hex::encode_prefixed).collect()) + } +} + +struct Bls12G2Add; +impl Precompile for Bls12G2Add { + fn address(&self) -> Address { + BLS12_G2ADD + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12G2AddCall::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let (p1, rest) = take_at_most(data, G2_POINT_SIZE); + let (p2, _) = take_at_most(rest, G2_POINT_SIZE); + Ok(vec![hex::encode_prefixed(p1), hex::encode_prefixed(p2)]) + } +} + +struct Bls12G2Msm; +impl Precompile for Bls12G2Msm { + fn address(&self) -> Address { + BLS12_G2MSM + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12G2MsmCall::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let pair_size = G2_POINT_SIZE + SCALAR_SIZE; + Ok(data.chunks(pair_size).map(hex::encode_prefixed).collect()) + } +} + +struct Bls12PairingCheck; +impl Precompile for Bls12PairingCheck { + fn address(&self) -> Address { + BLS12_PAIRING_CHECK + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12PairingCheckCall::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let pair_size = G1_POINT_SIZE + G2_POINT_SIZE; + Ok(data.chunks(pair_size).map(hex::encode_prefixed).collect()) + } + + fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result> { + let ret = bls12PairingCheckCall::abi_decode_returns(data)?; + Ok(vec![ret.to_string()]) + } +} + +struct Bls12MapFpToG1; +impl Precompile for Bls12MapFpToG1 { + fn address(&self) -> Address { + BLS12_MAP_FP_TO_G1 + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12MapFpToG1Call::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let (fp, _) = take_at_most(data, FP_SIZE); + Ok(vec![hex::encode_prefixed(fp)]) + } +} + +struct Bls12MapFp2ToG2; +impl Precompile for Bls12MapFp2ToG2 { + fn address(&self) -> Address { + BLS12_MAP_FP2_TO_G2 + } + + fn signature(&self, _: &[u8]) -> &'static str { + bls12MapFp2ToG2Call::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let (fp2, _) = take_at_most(data, G1_POINT_SIZE); + Ok(vec![hex::encode_prefixed(fp2)]) + } +} + +struct P256Verify; +impl Precompile for P256Verify { + fn address(&self) -> Address { + P256_VERIFY + } + + fn signature(&self, _: &[u8]) -> &'static str { + p256VerifyCall::SIGNATURE + } + + fn decode_call(&self, data: &[u8]) -> alloy_sol_types::Result> { + let p256VerifyCall { hash, r, s, qx, qy } = p256VerifyCall::abi_decode_raw(data)?; + Ok(vec![hash.to_string(), r.to_string(), s.to_string(), qx.to_string(), qy.to_string()]) + } + + fn decode_return(&self, data: &[u8]) -> alloy_sol_types::Result> { + let ret = p256VerifyCall::abi_decode_returns(data)?; + Ok(vec![ret.to_string()]) + } +} + +fn take_at_most(data: &[u8], n: usize) -> (&[u8], &[u8]) { + let n = n.min(data.len()); + data.split_at(n) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 301454d43eeab..5e8c452c8695a 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -204,7 +204,7 @@ pub fn render_trace_arena_inner( with_storage_changes: bool, ) -> String { if shell::is_json() { - return serde_json::to_string(&arena.resolve_arena()).expect("Failed to write traces"); + return serde_json::to_string(&arena.resolve_arena()).expect("Failed to serialize traces"); } let mut w = TraceWriter::new(Vec::::new()) diff --git a/crates/fmt/src/state/mod.rs b/crates/fmt/src/state/mod.rs index ada5eadd55bf0..762ecdb4ce645 100644 --- a/crates/fmt/src/state/mod.rs +++ b/crates/fmt/src/state/mod.rs @@ -61,7 +61,6 @@ impl CallContext { #[derive(Debug, Default)] pub(super) struct CallStack { stack: Vec, - precall_size: usize, } impl Deref for CallStack { @@ -80,14 +79,6 @@ impl CallStack { self.stack.pop() } - pub(crate) fn add_precall(&mut self, size: usize) { - self.precall_size += size; - } - - pub(crate) fn reset_precall(&mut self) { - self.precall_size = 0; - } - pub(crate) fn is_nested(&self) -> bool { self.last().is_some_and(|call| call.is_nested()) } diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index 52fb7fe0172f1..a8c9bd6647de1 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -795,12 +795,8 @@ impl<'ast> State<'_, 'ast> { fits_alone && !self.has_comment_between(rhs.span.lo(), rhs.span.hi()); let force_break = overflows && fits_alone_no_cmnts; - // Set up precall size tracking if lhs_size <= space_left { self.neverbreak(); - self.call_stack.add_precall(lhs_size + 1); - } else { - self.call_stack.add_precall(space_left + self.config.tab_width); } // Handle comments before the RHS expression @@ -932,7 +928,6 @@ impl<'ast> State<'_, 'ast> { } self.var_init = cache; - self.call_stack.reset_precall(); } fn print_var(&mut self, var: &'ast ast::VariableDefinition<'ast>, is_var_def: bool) { @@ -1955,12 +1950,6 @@ impl<'ast> State<'_, 'ast> { self.end(); self.word(" ="); - // '(' + var + ', ' + var + ')' + ' =' - let vars_size = vars.iter().fold(0, |acc, var| { - acc + var.as_ref().unspan().map_or(0, |v| self.estimate_size(v.span)) + 2 - }) + 2; - self.call_stack.add_precall(vars_size); - if self.estimate_size(init_expr.span) + self.config.tab_width <= std::cmp::max(space_left, self.space_left()) { diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 515983876019c..8169750642c6c 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -88,6 +88,8 @@ watchexec-signals = "5.0" clearscreen = "4.0" evm-disassembler.workspace = true path-slash.workspace = true +reqwest = { workspace = true, features = ["json"] } +url.workspace = true # doc server axum = { workspace = true, features = ["ws"] } diff --git a/crates/forge/src/cmd/clone.rs b/crates/forge/src/cmd/clone.rs index 6cf3db9ff7ae9..97337907ac201 100644 --- a/crates/forge/src/cmd/clone.rs +++ b/crates/forge/src/cmd/clone.rs @@ -1,10 +1,13 @@ use super::{init::InitArgs, install::DependencyInstallOpts}; use alloy_primitives::{Address, Bytes, ChainId, TxHash}; -use clap::{Parser, ValueHint}; +use clap::{Parser, ValueEnum, ValueHint}; use eyre::Result; +use forge_verify::sourcify::SOURCIFY_URL; use foundry_block_explorers::{ Client, - contract::{ContractCreationData, ContractMetadata, Metadata}, + contract::{ + ContractCreationData, ContractMetadata, Metadata, SourceCodeEntry, SourceCodeMetadata, + }, errors::EtherscanError, }; use foundry_cli::{ @@ -22,11 +25,16 @@ use foundry_compilers::{ compilers::solc::Solc, }; use foundry_config::{Chain, Config}; +use reqwest::StatusCode; +use serde::Deserialize; use std::{ + collections::{BTreeMap, HashMap}, fs::read_dir, path::{Path, PathBuf}, time::Duration, }; +use tracing::trace; +use url::Url; /// 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. @@ -52,14 +60,25 @@ pub struct CloneMetadata { pub storage_layout: StorageLayout, } +/// Source explorer type for `forge clone`. +#[derive(Clone, Copy, Debug, ValueEnum, Default)] +pub enum SourceExplorer { + /// Use Etherscan API (default). + #[default] + Etherscan, + /// Use Sourcify API. + Sourcify, +} + /// CLI arguments for `forge clone`. /// -/// `forge clone` clones an on-chain contract from block explorers (e.g., Etherscan) in the -/// following steps: +/// `forge clone` clones an on-chain contract from block explorers (e.g., Etherscan, Sourcify) 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. +/// 4. Update the `foundry.toml` configuration file with the compiler settings from the block +/// explorer. /// 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 @@ -78,7 +97,7 @@ pub struct CloneArgs { #[arg(long)] pub no_remappings_txt: bool, - /// Keep the original directory structure collected from Etherscan. + /// Keep the original directory structure collected from the block explorer. /// /// 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 @@ -86,6 +105,18 @@ pub struct CloneArgs { #[arg(long)] pub keep_directory_structure: bool, + /// Source explorer to use for fetching contract data. + /// + /// Can be either "etherscan" (default) or "sourcify". + #[arg(long, default_value = "etherscan", value_name = "EXPLORER")] + pub source: SourceExplorer, + + /// Custom Sourcify API URL. + /// + /// Implies `--source sourcify`. + #[arg(long, value_name = "URL")] + pub sourcify_url: Option, + #[command(flatten)] pub etherscan: EtherscanOpts, @@ -95,19 +126,41 @@ pub struct CloneArgs { impl CloneArgs { pub async fn run(self) -> Result<()> { - let Self { address, root, install, etherscan, no_remappings_txt, keep_directory_structure } = - self; + let Self { + address, + root, + install, + etherscan, + no_remappings_txt, + keep_directory_structure, + source, + sourcify_url, + } = self; // step 0. get the chain and api key from the config let config = etherscan.load_config()?; 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 - sh_println!("Downloading the source code of {address} from Etherscan...")?; - let meta = Self::collect_metadata_from_client(address, &client).await?; + // If sourcify_url is specified, use Sourcify as the source + let source = if sourcify_url.is_some() { SourceExplorer::Sourcify } else { source }; + + // step 1. get the metadata from client based on source type + let (meta, explorer_name, sourcify_client) = match source { + SourceExplorer::Etherscan => { + let etherscan_api_key = + config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let client = Client::new(chain, etherscan_api_key.clone())?; + sh_println!("Downloading the source code of {address} from Etherscan...")?; + let meta = Self::collect_metadata_from_client(address, &client).await?; + (meta, "Etherscan", None) + } + SourceExplorer::Sourcify => { + let client = SourcifyClient::with_url(chain, sourcify_url.as_deref()); + sh_println!("Downloading the source code of {address} from Sourcify...")?; + let meta = Self::collect_metadata_from_client(address, &client).await?; + (meta, "Sourcify", Some(client)) + } + }; // step 2. initialize an empty project Self::init_an_empty_project(&root, install).await?; @@ -120,20 +173,31 @@ impl CloneArgs { .await?; // step 4. collect the compilation metadata - // if the etherscan api key is not set, we need to wait for 3 seconds between calls - sh_println!("Collecting the creation information of {address} from Etherscan...")?; - - if etherscan_api_key.is_empty() { - sh_warn!("Waiting for 5 seconds to avoid rate limit...")?; - tokio::time::sleep(Duration::from_secs(5)).await; + sh_println!("Collecting the creation information of {address} from {explorer_name}...")?; + + match source { + SourceExplorer::Etherscan => { + let etherscan_api_key = + config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let client = Client::new(chain, etherscan_api_key.clone())?; + if etherscan_api_key.is_empty() { + sh_warn!("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).await?; + } + SourceExplorer::Sourcify => { + // Reuse the client from step 1 to benefit from cached creation data + let client = sourcify_client.expect("Sourcify client should exist"); + Self::collect_compilation_metadata(&meta, chain, address, &root, &client).await?; + } } - Self::collect_compilation_metadata(&meta, chain, address, &root, &client).await?; // step 5. git add and commit the changes if needed if install.commit { let git = Git::new(&root); git.add(Some("--all"))?; - let msg = format!("chore: forge clone {address}"); + let msg = format!("chore: forge clone {address} from {explorer_name}"); git.commit(&msg)?; } @@ -144,7 +208,7 @@ impl CloneArgs { /// /// * `address` - the address of the contract to be cloned. /// * `client` - the client of the block explorer. - pub(crate) async fn collect_metadata_from_client( + pub(crate) async fn collect_metadata_from_client( address: Address, client: &C, ) -> Result { @@ -175,12 +239,12 @@ 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 Etherscan). + /// * `meta` - the metadata of the contract (from block explorer). /// * `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( + pub(crate) async fn collect_compilation_metadata( meta: &Metadata, chain: Chain, address: Address, @@ -588,10 +652,10 @@ pub fn find_main_contract<'a>( rv.ok_or_else(|| eyre::eyre!("contract not found")) } -/// EtherscanClient is a trait that defines the methods to interact with Etherscan. +/// ExplorerClient is a trait that defines the methods to interact with block explorers. /// It is defined as a wrapper of the `foundry_block_explorers::Client` to allow mocking. #[cfg_attr(test, mockall::automock)] -pub(crate) trait EtherscanClient { +pub(crate) trait ExplorerClient { async fn contract_source_code( &self, address: Address, @@ -602,7 +666,7 @@ pub(crate) trait EtherscanClient { ) -> std::result::Result; } -impl EtherscanClient for Client { +impl ExplorerClient for Client { async fn contract_source_code( &self, address: Address, @@ -618,13 +682,322 @@ impl EtherscanClient for Client { } } +/// SourcifyClient is a client for interacting with Sourcify API. +pub(crate) struct SourcifyClient { + client: reqwest::Client, + chain: Chain, + base_url: String, + /// Whether the base_url already contains the full path (v2/contract/chain) + is_full_path: bool, + /// Cached creation data from the first API call + cached_creation_data: std::sync::Arc>>, +} + +impl SourcifyClient { + pub fn with_url(chain: Chain, verifier_url: Option<&str>) -> Self { + let (base_url, is_full_path) = match verifier_url { + Some(url) => ( + Url::parse(url).unwrap_or_else(|_| Url::parse(SOURCIFY_URL).unwrap()), + true, // Custom URL contains full path + ), + None => (Url::parse(SOURCIFY_URL).unwrap(), false), + }; + Self { + client: reqwest::Client::new(), + chain, + base_url: base_url.to_string().trim_end_matches('/').to_string(), + is_full_path, + cached_creation_data: std::sync::Arc::new(std::sync::Mutex::new(None)), + } + } + + fn get_contract_url(&self, address: Address, fields: &str) -> String { + if self.is_full_path { + // Custom URL already contains v2/contract/chain, just append address and fields + format!("{}/{}?fields={}", self.base_url, address, fields) + } else { + // Default URL, need to build full path + format!( + "{}/v2/contract/{}/{}?fields={}", + self.base_url, + self.chain.id(), + address, + fields + ) + } + } +} + +/// Sourcify API response for contract files. +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +#[allow(dead_code, clippy::large_enum_variant)] +enum SourcifyContractResponse { + Success(SourcifyContractData), + Error(SourcifyErrorResponse), +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourcifyContractData { + #[serde(default)] + sources: Option>, + #[serde(default)] + abi: Option, + #[serde(default)] + compilation: Option, + #[serde(default)] + #[allow(dead_code)] + creation_code: Option, + #[serde(default)] + #[allow(dead_code)] + deployed_bytecode: Option, + #[serde(default)] + #[allow(dead_code)] + runtime_bytecode: Option, + #[serde(default)] + deployment: Option, + // Additional fields that may be present in the response + #[serde(default)] + #[allow(dead_code)] + match_id: Option, + #[serde(default)] + #[allow(dead_code)] + creation_match: Option, + #[serde(default)] + #[allow(dead_code)] + runtime_match: Option, + #[serde(default)] + #[allow(dead_code)] + verified_at: Option, + #[serde(default)] + #[allow(dead_code)] + r#match: Option, + #[serde(default)] + #[allow(dead_code)] + chain_id: Option, + #[serde(default)] + #[allow(dead_code)] + address: Option, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourcifySourceFile { + #[serde(default)] + content: String, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourcifyCompilation { + #[serde(default)] + compiler_version: String, + #[serde(default)] + name: String, + #[serde(default)] + compiler_settings: Option, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourcifyDeployment { + #[serde(default)] + transaction_hash: Option, + #[serde(default)] + deployer: Option, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SourcifyErrorResponse { + #[serde(default)] + custom_code: String, + #[serde(default)] + message: String, + #[serde(default)] + #[allow(dead_code)] + error_id: String, + // Error responses should not have sources field + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + #[allow(dead_code)] + sources: Option<()>, +} + +impl ExplorerClient for SourcifyClient { + async fn contract_source_code( + &self, + address: Address, + ) -> std::result::Result { + // Request all fields including creation data to cache them + let url = self.get_contract_url(address, "sources,abi,compilation,deployment"); + let response = self.client.get(&url).send().await?; + + let status = response.status(); + trace!("Sourcify API response: status={:?}, url={}", status, url); + + match status { + StatusCode::NOT_FOUND => return Err(EtherscanError::ContractCodeNotVerified(address)), + StatusCode::TOO_MANY_REQUESTS => return Err(EtherscanError::RateLimitExceeded), + _ => {} + } + + // Read response body once + let response_text = response.text().await?; + trace!("Sourcify API response body: {}", response_text); + + if !status.is_success() { + return Err(EtherscanError::Unknown(format!( + "Sourcify API error (status {status}): {response_text}" + ))); + } + + // Use the untagged enum to properly handle both success and error responses + let response: SourcifyContractResponse = + serde_json::from_str(&response_text).map_err(|e| { + // Truncate response for error message to avoid huge output + let truncated = if response_text.len() > 500 { + format!("{}... (truncated)", &response_text[..500]) + } else { + response_text.clone() + }; + EtherscanError::Unknown(format!( + "Failed to parse Sourcify response: {e}. Response: {truncated}" + )) + })?; + + let data = match response { + SourcifyContractResponse::Success(data) => data, + SourcifyContractResponse::Error(error) => { + let error_msg = if error.custom_code.is_empty() && error.message.is_empty() { + "Unknown Sourcify API error".to_string() + } else { + format!("Sourcify API error: {} - {}", error.custom_code, error.message) + }; + return Err(EtherscanError::Unknown(error_msg)); + } + }; + + let sources_map = data.sources.ok_or_else(|| { + EtherscanError::Unknown("Sourcify response missing sources field".to_string()) + })?; + + // Convert sources map to SourceCodeMetadata::Sources format + let sources: HashMap = sources_map + .into_iter() + .map(|(path, source_file)| (path, SourceCodeEntry { content: source_file.content })) + .collect(); + + let source_code = SourceCodeMetadata::Sources(sources); + + let contract_name = data + .compilation + .as_ref() + .map(|c| c.name.clone()) + .unwrap_or_else(|| "Contract".to_string()); + + let compiler_version = + data.compilation.as_ref().map(|c| c.compiler_version.clone()).unwrap_or_default(); + + let abi = data.abi.map(|a| a.to_string()).unwrap_or_default(); + + // Cache creation data for later use in contract_creation_data + let tx_hash = data + .deployment + .as_ref() + .and_then(|d| d.transaction_hash.as_ref()) + .and_then(|h| h.parse().ok()) + .unwrap_or(TxHash::ZERO); + let creator = data + .deployment + .as_ref() + .and_then(|d| d.deployer.as_ref()) + .and_then(|a| a.parse().ok()) + .unwrap_or(Address::ZERO); + let creation_data = ContractCreationData { + contract_address: address, + contract_creator: creator, + transaction_hash: tx_hash, + }; + if let Ok(mut cache) = self.cached_creation_data.lock() { + *cache = Some(creation_data); + } + + // Extract compiler_settings from compilation if available + let constructor_arguments = Bytes::default(); + let optimization_used = data + .compilation + .as_ref() + .and_then(|c| c.compiler_settings.as_ref()) + .and_then(|s| s.get("optimizer")) + .and_then(|o| o.get("enabled")) + .and_then(|e| e.as_bool()) + .map(|b| if b { 1 } else { 0 }) + .unwrap_or(0); + + let runs = data + .compilation + .as_ref() + .and_then(|c| c.compiler_settings.as_ref()) + .and_then(|s| s.get("optimizer")) + .and_then(|o| o.get("runs")) + .and_then(|r| r.as_u64()) + .unwrap_or(0); + + Ok(ContractMetadata { + items: vec![Metadata { + source_code, + abi, + contract_name, + compiler_version, + optimization_used, + runs, + constructor_arguments, + evm_version: String::new(), + library: String::new(), + license_type: String::new(), + proxy: 0, + implementation: None, + swarm_source: String::new(), + }], + }) + } + + async fn contract_creation_data( + &self, + address: Address, + ) -> std::result::Result { + // Check cache first + if let Ok(cache) = self.cached_creation_data.lock() + && let Some(ref cached_data) = *cache + && cached_data.contract_address == address + { + return Ok(*cached_data); + } + + // If cache is empty or address doesn't match, use fallback values + // This should rarely happen since we cache in contract_source_code + trace!("Creation data not in cache for address {address}, using fallback values"); + let creation_data = ContractCreationData { + contract_address: address, + contract_creator: Address::ZERO, + transaction_hash: TxHash::ZERO, + }; + if let Ok(mut cache) = self.cached_creation_data.lock() { + *cache = Some(creation_data); + } + + Ok(creation_data) + } +} + #[cfg(test)] mod tests { use super::*; use alloy_primitives::hex; use foundry_compilers::CompilerContract; use foundry_test_utils::rpc::next_etherscan_api_key; - use std::collections::BTreeMap; #[expect(clippy::disallowed_macros)] fn assert_successful_compilation(root: &PathBuf) -> ProjectCompileOutput { @@ -652,7 +1025,7 @@ mod tests { }); } - fn mock_etherscan(address: Address) -> impl super::EtherscanClient { + fn mock_etherscan(address: Address) -> impl super::ExplorerClient { // load mock data let mut mocked_data = BTreeMap::new(); let data_folder = @@ -679,7 +1052,7 @@ mod tests { 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(); + let mut mocked_client = super::MockExplorerClient::new(); mocked_client .expect_contract_source_code() .times(1) diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 844caaa5f15a0..e0b2c43c499ac 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -742,6 +742,33 @@ forgetest!(can_clone_quiet, |prj, cmd| { .assert_empty_stdout(); }); +// checks that clone works with sourcify +forgetest!(can_clone_sourcify, |prj, cmd| { + prj.wipe(); + + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(!foundry_toml.exists()); + + cmd.args(["clone", "--source", "sourcify", "0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec"]) + .arg(prj.root()) + .assert_success() + .stdout_eq(str![[r#" +Downloading the source code of 0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec from Sourcify... +Initializing [..]... +Installing forge-std in [..] (url: https://github.com/foundry-rs/forge-std, tag: None) + Installed forge-std[..] + Initialized forge project +Collecting the creation information of 0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec from Sourcify... +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +"#]]); + + let s = read_string(&foundry_toml); + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; +}); + // checks that clone works with --no-remappings-txt forgetest!(can_clone_no_remappings_txt, |prj, cmd| { prj.wipe(); diff --git a/crates/forge/tests/cli/precompiles.rs b/crates/forge/tests/cli/precompiles.rs index 3ffda2d5ce414..505a6f7b04800 100644 --- a/crates/forge/tests/cli/precompiles.rs +++ b/crates/forge/tests/cli/precompiles.rs @@ -3,6 +3,178 @@ use foundry_evm_networks::NetworkConfigs; use foundry_test_utils::str; +forgetest_init!(precompile_trace_decoding, |prj, cmd| { + prj.add_test( + "PrecompileTrace.t.sol", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; + +contract PrecompileCaller { + constructor() { + // 0x01 - ECRECOVER + { + bytes32 hash = keccak256("test message"); + uint8 v = 27; + bytes32 r = bytes32(uint256(1)); + bytes32 s = bytes32(uint256(2)); + address(0x01).staticcall(abi.encode(hash, v, r, s)); + } + + // 0x02 - SHA256 + address(0x02).staticcall(abi.encodePacked("hello")); + + // 0x03 - RIPEMD160 + address(0x03).staticcall(abi.encodePacked("hello")); + + // 0x04 - IDENTITY (datacopy) + address(0x04).staticcall(abi.encodePacked("hello")); + + // 0x05 - MODEXP: compute 2^3 mod 5 = 3 + { + bytes memory modexpInput = abi.encodePacked( + uint256(1), // base length + uint256(1), // exponent length + uint256(1), // modulus length + uint8(2), // base = 2 + uint8(3), // exponent = 3 + uint8(5) // modulus = 5 + ); + address(0x05).staticcall(modexpInput); + } + + // 0x06 - BN254 ADD (ecadd): P + O = P + { + uint256 g1x = 1; + uint256 g1y = 2; + uint256 zerox = 0; + uint256 zeroy = 0; + address(0x06).staticcall(abi.encode(g1x, g1y, zerox, zeroy)); + } + + // 0x07 - BN254 MUL (ecmul): 1 * G = G + { + uint256 g1x = 1; + uint256 g1y = 2; + uint256 scalar = 1; + address(0x07).staticcall(abi.encode(g1x, g1y, scalar)); + } + + // 0x08 - BN254 PAIRING: empty input returns success (1) + address(0x08).staticcall(""); + + // 0x09 - BLAKE2F + { + bytes memory blake2fInput = new bytes(213); + blake2fInput[3] = 0x0c; // 12 rounds + bytes8[8] memory iv = [ + bytes8(0x6a09e667f3bcc908), + bytes8(0xbb67ae8584caa73b), + bytes8(0x3c6ef372fe94f82b), + bytes8(0xa54ff53a5f1d36f1), + bytes8(0x510e527fade682d1), + bytes8(0x9b05688c2b3e6c1f), + bytes8(0x1f83d9abfb41bd6b), + bytes8(0x5be0cd19137e2179) + ]; + for (uint256 i = 0; i < 8; i++) { + for (uint256 j = 0; j < 8; j++) { + blake2fInput[4 + i * 8 + j] = iv[i][j]; + } + } + blake2fInput[212] = 0x01; + address(0x09).staticcall(blake2fInput); + } + + // 0x0B - BLS12-381 G1 ADD (two points at infinity) + address(0x0B).staticcall(new bytes(256)); + + // 0x0C - BLS12-381 G1 MSM + address(0x0C).staticcall(new bytes(160)); + + // 0x0D - BLS12-381 G2 ADD (two points at infinity) + address(0x0D).staticcall(new bytes(512)); + + // 0x0E - BLS12-381 G2 MSM + address(0x0E).staticcall(new bytes(288)); + + // 0x0F - BLS12-381 PAIRING (G1 + G2 infinity points) + address(0x0F).staticcall(new bytes(384)); + + // 0x10 - BLS12-381 MAP FP TO G1 + address(0x10).staticcall(new bytes(64)); + + // 0x11 - BLS12-381 MAP FP2 TO G2 + address(0x11).staticcall(new bytes(128)); + + // 0x100 - P256VERIFY (secp256r1) + address(0x100).staticcall(new bytes(160)); + } +} + +contract PrecompileTraceTest is Test { + function test_precompile_traces() public { + new PrecompileCaller(); + } +} + "#, + ); + + cmd.args(["test", "--mt", "test_precompile_traces", "-vvvv", "--evm-version", "osaka"]) + .assert_success() + .stdout_eq(str![[r#" +... +Ran 1 test for test/PrecompileTrace.t.sol:PrecompileTraceTest +[PASS] test_precompile_traces() ([GAS]) +Traces: + [..] PrecompileTraceTest::test_precompile_traces() + ├─ [..] → new PrecompileCaller@[..] + │ ├─ [..] PRECOMPILES::ecrecover(0xea83cdcdd06bf61e414054115a551e23133711d0507dcbc07a4bab7dc4581935, 27, 1, 2) [staticcall] + │ │ └─ ← [Return] 0xBe038042508C42Df7b2A529cd4Cc0a9447c7D2b6 + │ ├─ [..] PRECOMPILES::sha256(0x68656c6c6f) [staticcall] + │ │ └─ ← [Return] 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 + │ ├─ [..] PRECOMPILES::ripemd(0x68656c6c6f) [staticcall] + │ │ └─ ← [Return] 0x000000000000000000000000108f07b838241261 + │ ├─ [..] PRECOMPILES::identity(0x68656c6c6f) [staticcall] + │ │ └─ ← [Return] 0x68656c6c6f + │ ├─ [..] PRECOMPILES::modexp(1, 1, 1, 0x02, 0x03, 0x05) [staticcall] + │ │ └─ ← [Return] 0x03 + │ ├─ [..] PRECOMPILES::ecadd(1, 2, 0, 0) [staticcall] + │ │ └─ ← [Return] (1, 2) + │ ├─ [..] PRECOMPILES::ecmul(1, 2, 1) [staticcall] + │ │ └─ ← [Return] (1, 2) + │ ├─ [..] PRECOMPILES::ecpairing() [staticcall] + │ │ └─ ← [Return] true + │ ├─ [..] PRECOMPILES::blake2f(12, [633244976228469098, 4298627039875721147, 3168446158426304060, 17381112106731261861, 15096882533739138641, 2264253069420660123, 7763433881832358687, 8728396173323133019], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0], 1) [staticcall] + │ │ └─ ← [Return] 0x1a48bfec594a1b13bb024be345656b8af895d662ccbc3f39fb5ecf2ef05942b5acace594cb81cdff6044b5bfaabfea105168676ce5753f6bb559ce3f92ad4850 + │ ├─ [..] PRECOMPILES::bls12G1Add(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + │ ├─ [..] PRECOMPILES::bls12G1Msm(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + │ ├─ [..] PRECOMPILES::bls12G2Add(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + │ ├─ [..] PRECOMPILES::bls12G2Msm(0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + │ ├─ [..] PRECOMPILES::bls12PairingCheck(0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] true + │ ├─ [..] PRECOMPILES::bls12MapFpToG1(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] 0x0000000000000000000000000000000011a9a0372b8f332d5c30de9ad14e50372a73fa4c45d5f2fa5097f2d6fb93bcac592f2e1711ac43db0519870c7d0ea41500000000000000000000000000000000092c0f994164a0719f51c24ba3788de240ff926b55f58c445116e8bc6a47cd63392fd4e8e22bdf9feaa96ee773222133 + │ ├─ [..] PRECOMPILES::bls12MapFp2ToG2(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [staticcall] + │ │ └─ ← [Return] 0x00000000000000000000000000000000018320896ec9eef9d5e619848dc29ce266f413d02dd31d9b9d44ec0c79cd61f18b075ddba6d7bd20b7ff27a4b324bfce000000000000000000000000000000000a67d12118b5a35bb02d2e86b3ebfa7e23410db93de39fb06d7025fa95e96ffa428a7a27c3ae4dd4b40bd251ac658892000000000000000000000000000000000260e03644d1a2c321256b3246bad2b895cad13890cbe6f85df55106a0d334604fb143c7a042d878006271865bc359410000000000000000000000000000000004c69777a43f0bda07679d5805e63f18cf4e0e7c6112ac7f70266d199b4f76ae27c6269a3ceebdae30806e9a76aadf5c + │ ├─ [..] P256VERIFY::fulfillBasicOrder_efficient_6GL6yc() [staticcall] + │ │ └─ ← [Return] + │ └─ ← [Return] 62 bytes of code + └─ ← [Stop] + +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + // tests transfer using celo precompile. // forgetest_init!(celo_transfer, |prj, cmd| { diff --git a/crates/primitives/src/network/receipt.rs b/crates/primitives/src/network/receipt.rs index d671dbb3d5535..9facb404b8c87 100644 --- a/crates/primitives/src/network/receipt.rs +++ b/crates/primitives/src/network/receipt.rs @@ -6,6 +6,7 @@ use alloy_serde::WithOtherFields; use derive_more::AsRef; use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom}; use serde::{Deserialize, Serialize}; +use tempo_primitives::TEMPO_TX_TYPE_ID; use crate::FoundryReceiptEnvelope; @@ -118,6 +119,7 @@ impl TryFrom for FoundryTxReceipt { 0x02 => FoundryReceiptEnvelope::Eip1559(receipt_with_bloom), 0x03 => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom), 0x04 => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom), + TEMPO_TX_TYPE_ID => FoundryReceiptEnvelope::Tempo(receipt_with_bloom), 0x7E => { // Construct the deposit receipt, extracting optional deposit fields // These fields may not be present in all receipts, so missing/invalid diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index 85f8a7a1c830e..8e78c1bffde5f 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -19,7 +19,7 @@ pub use bytecode::VerifyBytecodeArgs; pub mod retry; pub use retry::RetryArgs; -mod sourcify; +pub mod sourcify; pub mod verify; pub use verify::{VerifierArgs, VerifyArgs, VerifyCheckArgs}; diff --git a/npm/env.d.ts b/npm/env.d.ts index 6df532a23210a..828acd8e5a17b 100644 --- a/npm/env.d.ts +++ b/npm/env.d.ts @@ -25,8 +25,8 @@ interface ImportMetaEnv { readonly VERSION_NAME: string // release.yml#jobs:release:strategy:matrix:include:-|platform readonly PLATFORM_NAME: 'linux' | 'darwin' | 'win32' - // `debug` / `release` / `maxperf` # <- always `maxperf` - readonly PROFILE: 'debug' | 'release' | 'maxperf' + // `debug` / `release` / `maxperf` / `dist` + readonly PROFILE: 'debug' | 'release' | 'maxperf' | 'dist' // Used for local testing/development only readonly REGISTRY_URL: string diff --git a/npm/src/const.mjs b/npm/src/const.mjs index d07978918c4eb..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -5,7 +5,7 @@ import { URL } from 'node:url' * @typedef {'amd64' | 'arm64'} Arch * @typedef {'linux' | 'darwin' | 'win32'} Platform * @typedef {'forge' | 'cast' | 'anvil' | 'chisel'} Tool - * @typedef {'debug' | 'release' | 'maxperf'} Profile + * @typedef {'debug' | 'release' | 'maxperf' | 'dist'} Profile */ /** @type {readonly Tool[]} */ diff --git a/testdata/default/cheats/RecordLogs.t.sol b/testdata/default/cheats/RecordLogs.t.sol index 5927d5af23866..a21e7f0a06304 100644 --- a/testdata/default/cheats/RecordLogs.t.sol +++ b/testdata/default/cheats/RecordLogs.t.sol @@ -188,6 +188,41 @@ contract RecordLogsTest is Test { assertEq(entries[2].emitter, emitter2.getEmitterAddr()); } + function testRecordedLogsJson() public { + bytes memory testData = "Event Data in String"; + + vm.recordLogs(); + emitter.emitEvent(1, 2, 3, testData); + string memory logsJson = vm.getRecordedLogsJson(); + + // Verify JSON structure and values + assertGt(bytes(logsJson).length, 0); + + // Verify emitter address + string memory emitterAddr = vm.parseJsonString(logsJson, "[0].emitter"); + assertEq(vm.parseAddress(emitterAddr), address(emitter)); + + // Verify topics - first topic is event signature + string[] memory topics = vm.parseJsonStringArray(logsJson, "[0].topics"); + assertEq(topics.length, 4); + assertEq(vm.parseBytes32(topics[0]), keccak256("LogTopic123(uint256,uint256,uint256,bytes)")); + assertEq(vm.parseBytes32(topics[1]), bytes32(uint256(1))); + assertEq(vm.parseBytes32(topics[2]), bytes32(uint256(2))); + assertEq(vm.parseBytes32(topics[3]), bytes32(uint256(3))); + + // Verify data is hex-encoded + string memory data = vm.parseJsonString(logsJson, "[0].data"); + assertGt(bytes(data).length, 2); // At least "0x" + } + + function testRecordedLogsJsonEmpty() public { + vm.recordLogs(); + string memory logsJson = vm.getRecordedLogsJson(); + + // Empty array + assertEq(logsJson, "[]"); + } + function testRecordsConsumednAsRead() public { Vm.Log[] memory entries; diff --git a/testdata/utils/Vm.sol b/testdata/utils/Vm.sol index 8cced251493aa..e06ed8facda10 100644 --- a/testdata/utils/Vm.sol +++ b/testdata/utils/Vm.sol @@ -319,6 +319,7 @@ interface Vm { function getNonce(Wallet calldata wallet) external view returns (uint64 nonce); function getRawBlockHeader(uint256 blockNumber) external view returns (bytes memory rlpHeader); function getRecordedLogs() external view returns (Log[] memory logs); + function getRecordedLogsJson() external view returns (string memory logsJson); function getStateDiff() external view returns (string memory diff); function getStateDiffJson() external view returns (string memory diff); function getStorageAccesses() external view returns (StorageAccess[] memory storageAccesses); From 9c5e4e72529aa6115bad2be1ca80aaaaf4b0b024 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:09:24 +0700 Subject: [PATCH 238/374] Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/npm.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index ae5642a6e1baf..0ab853e0520d0 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -233,9 +233,46 @@ jobs: PLATFORM='${{ matrix.os }}' ARCH='${{ matrix.arch }}' + # Basic validation of matrix-derived values to avoid path manipulation + case "$TOOL" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid TOOL value: $TOOL" >&2; exit 1 ;; + esac + case "$PLATFORM" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid PLATFORM value: $PLATFORM" >&2; exit 1 ;; + esac + case "$ARCH" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid ARCH value: $ARCH" >&2; exit 1 ;; + esac + PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" + echo "Preparing to publish package from: $PACKAGE_DIR" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 + exit 1 + fi + + # Resolve to an absolute path and ensure it stays within ./@foundry-rs + ABS_PACKAGE_DIR="$(realpath "$PACKAGE_DIR")" + ABS_EXPECTED_ROOT="$(realpath "./@foundry-rs")" + case "$ABS_PACKAGE_DIR" in + "$ABS_EXPECTED_ROOT"/*) ;; + *) + echo "ERROR: Resolved package directory is outside expected root:" >&2 + echo " ABS_PACKAGE_DIR=$ABS_PACKAGE_DIR" >&2 + echo " ABS_EXPECTED_ROOT=$ABS_EXPECTED_ROOT" >&2 + exit 1 + ;; + esac + ls -la "$PACKAGE_DIR" + # Minimal sanity check: require a package.json before publishing + if [[ ! -f "$PACKAGE_DIR/package.json" ]]; then + echo "ERROR: package.json not found in $PACKAGE_DIR; refusing to publish." >&2 + exit 1 + fi + bun ./scripts/publish.mjs "$PACKAGE_DIR" echo "Published @foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" From 2ad832aab599625b84a446e3c6b041a920086dd8 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 23 Jan 2026 08:11:49 +0700 Subject: [PATCH 239/374] Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/Docker.yml | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/Docker.yml diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/Docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From e3d4029ef71342a1344f9b5b84cd112f4631927b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:23:49 +0700 Subject: [PATCH 240/374] Potential fix for code scanning alert no. 108: Artifact poisoning (#345) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/npm.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 0ab853e0520d0..6ef9898e62587 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -226,6 +226,7 @@ jobs: RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} run: | set -euo pipefail @@ -245,7 +246,21 @@ jobs: esac PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" - echo "Preparing to publish package from: $PACKAGE_DIR" + EXPECTED_PKG_NAME="${TOOL}-${PLATFORM}-${ARCH}" + + # Ensure corresponding artifact directory exists in the isolated artifact dir + if [[ -z "${ARTIFACT_DIR:-}" ]]; then + echo "ERROR: ARTIFACT_DIR is not set; refusing to publish." >&2 + exit 1 + fi + + EXPECTED_ARTIFACT_PATH="${ARTIFACT_DIR}/${EXPECTED_PKG_NAME}" + if [[ ! -d "$EXPECTED_ARTIFACT_PATH" ]]; then + echo "ERROR: Expected artifact directory not found at: $EXPECTED_ARTIFACT_PATH" >&2 + exit 1 + fi + + echo "Preparing to publish package from: $PACKAGE_DIR (artifact: $EXPECTED_ARTIFACT_PATH)" if [[ ! -d "$PACKAGE_DIR" ]]; then echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 From b8eb87b1129ee13407177b2500dc93e00be182a4 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:46:11 +0700 Subject: [PATCH 241/374] Potential fix for code scanning alert no. 110: Uncontrolled data used in path expression (#347) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- crates/test-utils/src/script.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 07413fef5495c..f20dae571cef3 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -118,6 +118,7 @@ impl ScriptTester { fn copy_testdata(root: &Path) -> Result<()> { let testdata = Self::testdata_path(); let from_dir = testdata.join("utils"); + let canonical_from_dir = from_dir.canonicalize()?; let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { @@ -138,9 +139,9 @@ impl ScriptTester { // Skip invalid (potentially dangerous) file names continue; } - // Verify canonicalized file is in from_dir to avoid symlink traversal + // Verify canonicalized file is in canonical_from_dir to avoid symlink traversal if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&from_dir) { + if !canonical_file.starts_with(&canonical_from_dir) { continue; } } else { From c97ebb32ec4b5f84897f9048488a94b2959e9cab Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 00:07:31 +0700 Subject: [PATCH 242/374] benches\LATEST.md --- benches/LATEST.md | 71 +--- sleep.json | 955 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 968 insertions(+), 58 deletions(-) create mode 100644 sleep.json diff --git a/benches/LATEST.md b/benches/LATEST.md index 7ea1049a2ac41..2ddf3b8c922f1 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,73 +1,28 @@ # Foundry Benchmark Results -**Date**: 2025-10-02 12:14:23 +**Date**: 2026-01-27 03:38:34 -## Repositories Tested +## Summary -1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -2. [Vectorized/solady](https://github.com/Vectorized/solady) -3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) -4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) - -## Foundry Versions - -- **v1.3.6**: forge Version: 1.3.6-v1.3.6 (d241588 2025-09-16) -- **v1.4.0-rc1**: forge Version: 1.4.0-v1.4.0-rc1 (bd0e4a7 2025-10-01) - -## Forge Test - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 3.17 s | 2.94 s | -| solady | 2.28 s | 2.10 s | -| Uniswap-v4-core | 7.27 s | 6.13 s | -| sparkdotfi-spark-psm | 43.04 s | 44.08 s | +Benchmarked 2 Foundry versions across 1 repositories. -## Forge Fuzz Test +### Repositories Tested -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------ | ---------- | -| ithacaxyz-account | 3.18 s | 3.02 s | -| solady | 2.39 s | 2.24 s | -| Uniswap-v4-core | 6.84 s | 6.20 s | -| sparkdotfi-spark-psm | 3.07 s | 2.72 s | - -## Forge Test (Isolated) - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| solady | 2.26 s | 2.41 s | -| Uniswap-v4-core | 7.22 s | 7.71 s | -| sparkdotfi-spark-psm | 45.53 s | 50.49 s | +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -## Forge Build (No Cache) +### Foundry Versions -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 9.16 s | 9.08 s | -| solady | 14.62 s | 14.69 s | -| Uniswap-v4-core | 2m 3.8s | 2m 5.3s | -| sparkdotfi-spark-psm | 13.17 s | 13.14 s | +- **stable**: forge Version: 1.5.0-dev (6e718be 2025-12-07) +- **nightly**: forge Version: 1.5.0-dev (6e718be 2025-12-07) ## Forge Build (With Cache) -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 0.156 s | 0.113 s | -| solady | 0.089 s | 0.094 s | -| Uniswap-v4-core | 0.133 s | 0.127 s | -| sparkdotfi-spark-psm | 0.173 s | 0.131 s | - -## Forge Coverage - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | -------- | ---------- | -| ithacaxyz-account | 14.91 s | 13.34 s | -| Uniswap-v4-core | 1m 34.8s | 1m 30.3s | -| sparkdotfi-spark-psm | 3m 49.3s | 3m 40.2s | +| Repository | stable | nightly | +|------------|----------|----------| +| ithacaxyz-account | 0.345 s | 0.279 s | ## System Information -- **OS**: macos +- **OS**: linux - **CPU**: 8 -- **Rustc**: rustc 1.90.0-nightly (3014e79f9 2025-07-15) +- **Rustc**: rustc 1.93.0 (254b59607 2026-01-19) diff --git a/sleep.json b/sleep.json new file mode 100644 index 0000000000000..5b430e1e663f6 --- /dev/null +++ b/sleep.json @@ -0,0 +1,955 @@ +{ + "results": [ + { + "command": "sleep 0.020", + "mean": 0.023726515413333333, + "stddev": 0.004602014051751124, + "median": 0.02267755758, + "user": 0.0013185473333333334, + "system": 0.0020899164444444446, + "min": 0.02109890308, + "max": 0.05602819808, + "times": [ + 0.02856005608, + 0.02346135008, + 0.02202502208, + 0.02139558708, + 0.02265920408, + 0.02121691608, + 0.02272505608, + 0.02114247908, + 0.02157142808, + 0.021514666079999998, + 0.02161920108, + 0.02335035008, + 0.02224331408, + 0.02228639708, + 0.02152537208, + 0.021732302079999998, + 0.02273370308, + 0.02115513608, + 0.02268494308, + 0.02244547308, + 0.023943647079999998, + 0.02324528508, + 0.02152617908, + 0.023991903079999998, + 0.02250884108, + 0.02342551708, + 0.02113216608, + 0.02168223108, + 0.02222267508, + 0.02273532108, + 0.02273995308, + 0.05602819808, + 0.02501500608, + 0.03121396008, + 0.02424400108, + 0.02459129108, + 0.02633760708, + 0.02377406808, + 0.02365474708, + 0.02406064008, + 0.02300910408, + 0.02437339208, + 0.02317403908, + 0.02257532008, + 0.02267017208, + 0.02356714508, + 0.02367204808, + 0.02258227108, + 0.02330384008, + 0.02225645108, + 0.02478414908, + 0.02484724308, + 0.02270765708, + 0.02339114708, + 0.02450795908, + 0.02348840008, + 0.044674490080000004, + 0.028041754080000002, + 0.022940745079999998, + 0.02259975308, + 0.022112378079999998, + 0.02271348408, + 0.02320266708, + 0.02284982108, + 0.02244050908, + 0.02238655808, + 0.022084648079999998, + 0.02241669808, + 0.02523103408, + 0.02256237908, + 0.03532525108, + 0.02232798408, + 0.02173793008, + 0.021903001079999998, + 0.02288046308, + 0.02368652508, + 0.02211418708, + 0.02265551308, + 0.02187778308, + 0.02191395108, + 0.02182523808, + 0.02185612208, + 0.02109890308, + 0.02294132008, + 0.02191512608, + 0.02264461208, + 0.02227651108, + 0.02307147508, + 0.02227169708, + 0.02177434208 + ], + "memory_usage_byte": [ + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.021", + "mean": 0.022889189941111117, + "stddev": 0.0007161191938371117, + "median": 0.02280623708, + "user": 0.0009166992592592593, + "system": 0.0016941181481481477, + "min": 0.02132554808, + "max": 0.02453766808, + "times": [ + 0.02311599608, + 0.02274468508, + 0.02193879008, + 0.02158843608, + 0.02329398008, + 0.02379494508, + 0.02260801308, + 0.02439507908, + 0.02448522508, + 0.02403379508, + 0.02298143008, + 0.02263027308, + 0.02229235308, + 0.02335063508, + 0.02377098008, + 0.02269184108, + 0.023631199079999998, + 0.02338021508, + 0.02198521708, + 0.02251586208, + 0.022295963079999998, + 0.02226397608, + 0.02453766808, + 0.02184453408, + 0.02289659908, + 0.02382663208, + 0.02347397108, + 0.02225926308, + 0.02207640608, + 0.02243237108, + 0.02278192608, + 0.02270514808, + 0.02245069008, + 0.023018867079999998, + 0.02399866208, + 0.02236840708, + 0.02366382208, + 0.02294188908, + 0.02155127708, + 0.02294999808, + 0.02132554808, + 0.02242025908, + 0.02202766108, + 0.02182175108, + 0.02272186608, + 0.02211805308, + 0.02319764908, + 0.022308045079999998, + 0.02345400908, + 0.022437877079999998, + 0.02273417808, + 0.02217370908, + 0.02254318408, + 0.023269922079999998, + 0.02384951108, + 0.02419476108, + 0.02439866908, + 0.02354840508, + 0.02304219108, + 0.02354960608, + 0.02382648708, + 0.02345751208, + 0.02367913708, + 0.02253067208, + 0.02215132608, + 0.022603942079999998, + 0.02284062808, + 0.02252907808, + 0.02220393508, + 0.023291509079999998, + 0.02399456908, + 0.02407123208, + 0.02279175108, + 0.02300624708, + 0.02309500408, + 0.023036532079999998, + 0.02303833108, + 0.02316846908, + 0.02228349608, + 0.02247140608, + 0.022482600079999998, + 0.02370720808, + 0.02220123708, + 0.02230588608, + 0.02333678708, + 0.02153336008, + 0.02203071908, + 0.02279195108, + 0.02353659108, + 0.02267460708, + 0.022536274079999998, + 0.022769262079999998, + 0.02314857808, + 0.02194885908, + 0.02355038408, + 0.02320035308, + 0.02307451408, + 0.02379926408, + 0.02330480208, + 0.02257055708, + 0.02330320308, + 0.02303003208, + 0.02327859908, + 0.02171311608, + 0.02282052308, + 0.02170123708, + 0.02254831308, + 0.02235855408 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.022", + "mean": 0.02415569324504855, + "stddev": 0.0009830972994273135, + "median": 0.02409406108, + "user": 0.001165289514563107, + "system": 0.001767603883495146, + "min": 0.02243173808, + "max": 0.02755932908, + "times": [ + 0.02456728108, + 0.02650439708, + 0.02480475408, + 0.02452974808, + 0.02300978308, + 0.02521451608, + 0.02543841408, + 0.02538411108, + 0.02475773908, + 0.02403843308, + 0.02426362708, + 0.02326921708, + 0.02447185308, + 0.02361749008, + 0.02410661008, + 0.02371481508, + 0.02327300908, + 0.02430165908, + 0.02328269108, + 0.02315262608, + 0.02380195808, + 0.02283639508, + 0.02491355808, + 0.02401717008, + 0.02556049408, + 0.02350359508, + 0.02400529208, + 0.02533555808, + 0.02467923308, + 0.02478442308, + 0.02422068708, + 0.02352175108, + 0.02481882108, + 0.02456148108, + 0.02314905108, + 0.024188183079999998, + 0.02483985908, + 0.02289141308, + 0.02364977308, + 0.02354907008, + 0.02379135508, + 0.026812933079999997, + 0.023360627079999998, + 0.02331436308, + 0.02504176308, + 0.02358805508, + 0.02409406108, + 0.02350689508, + 0.02303628508, + 0.02430972408, + 0.02516170908, + 0.02352843108, + 0.02274564308, + 0.02345165808, + 0.02429327308, + 0.02252948108, + 0.02445868508, + 0.02755932908, + 0.02522621808, + 0.02491753008, + 0.022858510079999998, + 0.02401968108, + 0.02409596908, + 0.02390450108, + 0.02373108808, + 0.027211489079999998, + 0.02537487108, + 0.02319182608, + 0.02390569508, + 0.02490164708, + 0.02384732708, + 0.02243173808, + 0.02367003008, + 0.02494288308, + 0.02436298308, + 0.02390639308, + 0.02423030808, + 0.02430082908, + 0.02320845908, + 0.02421546708, + 0.02530823508, + 0.02368935308, + 0.02306283708, + 0.023536658079999998, + 0.02359881208, + 0.02438320308, + 0.02477724008, + 0.02362231908, + 0.02419465008, + 0.02596891608, + 0.02307578608, + 0.02459456508, + 0.02384055408, + 0.02421387408, + 0.02510733208, + 0.02473580508, + 0.02243970708, + 0.02253156008, + 0.02550018108, + 0.02440877608, + 0.02281331608, + 0.02354148408, + 0.02352098308 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] +} From e0b64ca9f339c880a88c1c7e0a4b49597c67a77e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:16:04 +0700 Subject: [PATCH 243/374] Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- benches/LATEST.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/LATEST.md b/benches/LATEST.md index 2ddf3b8c922f1..00776abc94003 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -4,7 +4,7 @@ ## Summary -Benchmarked 2 Foundry versions across 1 repositories. +Benchmarked 2 Foundry versions across 1 repository. ### Repositories Tested From 4d4b5fc510a0631104062ca0efe18630a0851558 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:02:39 +0700 Subject: [PATCH 244/374] Potential fix for code scanning alert no. 102: Artifact poisoning (#351) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/npm.yml | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 6ef9898e62587..3bfadac860785 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -164,15 +164,29 @@ jobs: # Derive RELEASE_VERSION from any foundry artifact we downloaded # Expected names: foundry___.{tar.gz,zip} - first_file=$(ls "$ARTIFACT_DIR"/foundry_* 2>/dev/null | head -n1 || true) - if [[ -z "${first_file}" ]]; then - echo "No foundry artifacts found to publish" >&2 + shopt -s nullglob + foundry_files=("$ARTIFACT_DIR"/foundry_*) + shopt -u nullglob + + if [[ ${#foundry_files[@]} -eq 0 ]]; then + echo "No foundry artifacts found to publish in $ARTIFACT_DIR" >&2 exit 1 fi + + first_file="${foundry_files[0]}" version_part=$(basename "$first_file") version_part=${version_part#foundry_} - export RELEASE_VERSION=${version_part%%_*} - echo "Detected RELEASE_VERSION=$RELEASE_VERSION" + RELEASE_VERSION=${version_part%%_*} + + # Validate derived RELEASE_VERSION to mitigate artifact poisoning + # Require a sane version format, e.g. 1.2.3 or 1.2.3-beta.1 + if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+(\.[0-9]+)*(-[0-9A-Za-z.-]+)?$ ]]; then + echo "ERROR: Derived RELEASE_VERSION '$RELEASE_VERSION' from artifact '$first_file' is not a valid version" >&2 + exit 1 + fi + + export RELEASE_VERSION + echo "Detected RELEASE_VERSION=$RELEASE_VERSION from artifact $first_file" printf 'RELEASE_VERSION=%s\n' "$RELEASE_VERSION" >>"$GITHUB_OUTPUT" From 71a26eea6cce80052b3a82da9aa7c9aa0d987b09 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:04:35 +0700 Subject: [PATCH 245/374] benches\LATEST.md (#350) * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- benches/LATEST.md | 71 +--- sleep.json | 955 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 968 insertions(+), 58 deletions(-) create mode 100644 sleep.json diff --git a/benches/LATEST.md b/benches/LATEST.md index 7ea1049a2ac41..00776abc94003 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,73 +1,28 @@ # Foundry Benchmark Results -**Date**: 2025-10-02 12:14:23 +**Date**: 2026-01-27 03:38:34 -## Repositories Tested +## Summary -1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -2. [Vectorized/solady](https://github.com/Vectorized/solady) -3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) -4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) - -## Foundry Versions - -- **v1.3.6**: forge Version: 1.3.6-v1.3.6 (d241588 2025-09-16) -- **v1.4.0-rc1**: forge Version: 1.4.0-v1.4.0-rc1 (bd0e4a7 2025-10-01) - -## Forge Test - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 3.17 s | 2.94 s | -| solady | 2.28 s | 2.10 s | -| Uniswap-v4-core | 7.27 s | 6.13 s | -| sparkdotfi-spark-psm | 43.04 s | 44.08 s | +Benchmarked 2 Foundry versions across 1 repository. -## Forge Fuzz Test +### Repositories Tested -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------ | ---------- | -| ithacaxyz-account | 3.18 s | 3.02 s | -| solady | 2.39 s | 2.24 s | -| Uniswap-v4-core | 6.84 s | 6.20 s | -| sparkdotfi-spark-psm | 3.07 s | 2.72 s | - -## Forge Test (Isolated) - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| solady | 2.26 s | 2.41 s | -| Uniswap-v4-core | 7.22 s | 7.71 s | -| sparkdotfi-spark-psm | 45.53 s | 50.49 s | +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -## Forge Build (No Cache) +### Foundry Versions -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 9.16 s | 9.08 s | -| solady | 14.62 s | 14.69 s | -| Uniswap-v4-core | 2m 3.8s | 2m 5.3s | -| sparkdotfi-spark-psm | 13.17 s | 13.14 s | +- **stable**: forge Version: 1.5.0-dev (6e718be 2025-12-07) +- **nightly**: forge Version: 1.5.0-dev (6e718be 2025-12-07) ## Forge Build (With Cache) -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 0.156 s | 0.113 s | -| solady | 0.089 s | 0.094 s | -| Uniswap-v4-core | 0.133 s | 0.127 s | -| sparkdotfi-spark-psm | 0.173 s | 0.131 s | - -## Forge Coverage - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | -------- | ---------- | -| ithacaxyz-account | 14.91 s | 13.34 s | -| Uniswap-v4-core | 1m 34.8s | 1m 30.3s | -| sparkdotfi-spark-psm | 3m 49.3s | 3m 40.2s | +| Repository | stable | nightly | +|------------|----------|----------| +| ithacaxyz-account | 0.345 s | 0.279 s | ## System Information -- **OS**: macos +- **OS**: linux - **CPU**: 8 -- **Rustc**: rustc 1.90.0-nightly (3014e79f9 2025-07-15) +- **Rustc**: rustc 1.93.0 (254b59607 2026-01-19) diff --git a/sleep.json b/sleep.json new file mode 100644 index 0000000000000..5b430e1e663f6 --- /dev/null +++ b/sleep.json @@ -0,0 +1,955 @@ +{ + "results": [ + { + "command": "sleep 0.020", + "mean": 0.023726515413333333, + "stddev": 0.004602014051751124, + "median": 0.02267755758, + "user": 0.0013185473333333334, + "system": 0.0020899164444444446, + "min": 0.02109890308, + "max": 0.05602819808, + "times": [ + 0.02856005608, + 0.02346135008, + 0.02202502208, + 0.02139558708, + 0.02265920408, + 0.02121691608, + 0.02272505608, + 0.02114247908, + 0.02157142808, + 0.021514666079999998, + 0.02161920108, + 0.02335035008, + 0.02224331408, + 0.02228639708, + 0.02152537208, + 0.021732302079999998, + 0.02273370308, + 0.02115513608, + 0.02268494308, + 0.02244547308, + 0.023943647079999998, + 0.02324528508, + 0.02152617908, + 0.023991903079999998, + 0.02250884108, + 0.02342551708, + 0.02113216608, + 0.02168223108, + 0.02222267508, + 0.02273532108, + 0.02273995308, + 0.05602819808, + 0.02501500608, + 0.03121396008, + 0.02424400108, + 0.02459129108, + 0.02633760708, + 0.02377406808, + 0.02365474708, + 0.02406064008, + 0.02300910408, + 0.02437339208, + 0.02317403908, + 0.02257532008, + 0.02267017208, + 0.02356714508, + 0.02367204808, + 0.02258227108, + 0.02330384008, + 0.02225645108, + 0.02478414908, + 0.02484724308, + 0.02270765708, + 0.02339114708, + 0.02450795908, + 0.02348840008, + 0.044674490080000004, + 0.028041754080000002, + 0.022940745079999998, + 0.02259975308, + 0.022112378079999998, + 0.02271348408, + 0.02320266708, + 0.02284982108, + 0.02244050908, + 0.02238655808, + 0.022084648079999998, + 0.02241669808, + 0.02523103408, + 0.02256237908, + 0.03532525108, + 0.02232798408, + 0.02173793008, + 0.021903001079999998, + 0.02288046308, + 0.02368652508, + 0.02211418708, + 0.02265551308, + 0.02187778308, + 0.02191395108, + 0.02182523808, + 0.02185612208, + 0.02109890308, + 0.02294132008, + 0.02191512608, + 0.02264461208, + 0.02227651108, + 0.02307147508, + 0.02227169708, + 0.02177434208 + ], + "memory_usage_byte": [ + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.021", + "mean": 0.022889189941111117, + "stddev": 0.0007161191938371117, + "median": 0.02280623708, + "user": 0.0009166992592592593, + "system": 0.0016941181481481477, + "min": 0.02132554808, + "max": 0.02453766808, + "times": [ + 0.02311599608, + 0.02274468508, + 0.02193879008, + 0.02158843608, + 0.02329398008, + 0.02379494508, + 0.02260801308, + 0.02439507908, + 0.02448522508, + 0.02403379508, + 0.02298143008, + 0.02263027308, + 0.02229235308, + 0.02335063508, + 0.02377098008, + 0.02269184108, + 0.023631199079999998, + 0.02338021508, + 0.02198521708, + 0.02251586208, + 0.022295963079999998, + 0.02226397608, + 0.02453766808, + 0.02184453408, + 0.02289659908, + 0.02382663208, + 0.02347397108, + 0.02225926308, + 0.02207640608, + 0.02243237108, + 0.02278192608, + 0.02270514808, + 0.02245069008, + 0.023018867079999998, + 0.02399866208, + 0.02236840708, + 0.02366382208, + 0.02294188908, + 0.02155127708, + 0.02294999808, + 0.02132554808, + 0.02242025908, + 0.02202766108, + 0.02182175108, + 0.02272186608, + 0.02211805308, + 0.02319764908, + 0.022308045079999998, + 0.02345400908, + 0.022437877079999998, + 0.02273417808, + 0.02217370908, + 0.02254318408, + 0.023269922079999998, + 0.02384951108, + 0.02419476108, + 0.02439866908, + 0.02354840508, + 0.02304219108, + 0.02354960608, + 0.02382648708, + 0.02345751208, + 0.02367913708, + 0.02253067208, + 0.02215132608, + 0.022603942079999998, + 0.02284062808, + 0.02252907808, + 0.02220393508, + 0.023291509079999998, + 0.02399456908, + 0.02407123208, + 0.02279175108, + 0.02300624708, + 0.02309500408, + 0.023036532079999998, + 0.02303833108, + 0.02316846908, + 0.02228349608, + 0.02247140608, + 0.022482600079999998, + 0.02370720808, + 0.02220123708, + 0.02230588608, + 0.02333678708, + 0.02153336008, + 0.02203071908, + 0.02279195108, + 0.02353659108, + 0.02267460708, + 0.022536274079999998, + 0.022769262079999998, + 0.02314857808, + 0.02194885908, + 0.02355038408, + 0.02320035308, + 0.02307451408, + 0.02379926408, + 0.02330480208, + 0.02257055708, + 0.02330320308, + 0.02303003208, + 0.02327859908, + 0.02171311608, + 0.02282052308, + 0.02170123708, + 0.02254831308, + 0.02235855408 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.022", + "mean": 0.02415569324504855, + "stddev": 0.0009830972994273135, + "median": 0.02409406108, + "user": 0.001165289514563107, + "system": 0.001767603883495146, + "min": 0.02243173808, + "max": 0.02755932908, + "times": [ + 0.02456728108, + 0.02650439708, + 0.02480475408, + 0.02452974808, + 0.02300978308, + 0.02521451608, + 0.02543841408, + 0.02538411108, + 0.02475773908, + 0.02403843308, + 0.02426362708, + 0.02326921708, + 0.02447185308, + 0.02361749008, + 0.02410661008, + 0.02371481508, + 0.02327300908, + 0.02430165908, + 0.02328269108, + 0.02315262608, + 0.02380195808, + 0.02283639508, + 0.02491355808, + 0.02401717008, + 0.02556049408, + 0.02350359508, + 0.02400529208, + 0.02533555808, + 0.02467923308, + 0.02478442308, + 0.02422068708, + 0.02352175108, + 0.02481882108, + 0.02456148108, + 0.02314905108, + 0.024188183079999998, + 0.02483985908, + 0.02289141308, + 0.02364977308, + 0.02354907008, + 0.02379135508, + 0.026812933079999997, + 0.023360627079999998, + 0.02331436308, + 0.02504176308, + 0.02358805508, + 0.02409406108, + 0.02350689508, + 0.02303628508, + 0.02430972408, + 0.02516170908, + 0.02352843108, + 0.02274564308, + 0.02345165808, + 0.02429327308, + 0.02252948108, + 0.02445868508, + 0.02755932908, + 0.02522621808, + 0.02491753008, + 0.022858510079999998, + 0.02401968108, + 0.02409596908, + 0.02390450108, + 0.02373108808, + 0.027211489079999998, + 0.02537487108, + 0.02319182608, + 0.02390569508, + 0.02490164708, + 0.02384732708, + 0.02243173808, + 0.02367003008, + 0.02494288308, + 0.02436298308, + 0.02390639308, + 0.02423030808, + 0.02430082908, + 0.02320845908, + 0.02421546708, + 0.02530823508, + 0.02368935308, + 0.02306283708, + 0.023536658079999998, + 0.02359881208, + 0.02438320308, + 0.02477724008, + 0.02362231908, + 0.02419465008, + 0.02596891608, + 0.02307578608, + 0.02459456508, + 0.02384055408, + 0.02421387408, + 0.02510733208, + 0.02473580508, + 0.02243970708, + 0.02253156008, + 0.02550018108, + 0.02440877608, + 0.02281331608, + 0.02354148408, + 0.02352098308 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] +} From cc693d32a187dc57a2d405601cdcf7c9586fe23f Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:35:41 +0700 Subject: [PATCH 246/374] Potential fix for code scanning alert no. 109: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index f20dae571cef3..f3adc9e7bb420 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -123,13 +123,21 @@ impl ScriptTester { fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); + // Canonicalize the file path and ensure it stays within canonical_from_dir + let canonical_file = match file.canonicalize() { + Ok(path) => path, + Err(_) => continue, + }; + if !canonical_file.starts_with(&canonical_from_dir) { + continue; + } // Only operate on regular files to avoid following symlinks or directories - let metadata = fs::symlink_metadata(&file)?; + let metadata = fs::symlink_metadata(&canonical_file)?; let ftype = metadata.file_type(); if !ftype.is_file() { continue; } - let name = match file.file_name() { + let name = match canonical_file.file_name() { Some(name) => name, None => continue, }; @@ -139,15 +147,7 @@ impl ScriptTester { // Skip invalid (potentially dangerous) file names continue; } - // Verify canonicalized file is in canonical_from_dir to avoid symlink traversal - if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&canonical_from_dir) { - continue; - } - } else { - continue; - } - fs::copy(&file, to_dir.join(name))?; + fs::copy(&canonical_file, to_dir.join(name))?; } Ok(()) } From 33588d6bdb3664a3ccea49f6ff4efe1a7f5bc6ce Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:38:35 +0700 Subject: [PATCH 247/374] Update docker.yml --- .github/workflows/docker.yml | 104 +++++++++++------------------------ 1 file changed, 33 insertions(+), 71 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index e994f94e7085c..5a2330e7d5d62 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,100 +1,62 @@ name: Docker -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - on: - schedule: - - cron: '21 12 * * *' push: - branches: [ "master" ] - # Publish semver tags as releases. - tags: [ 'v*.*.*' ] + tags: ["*"] + branches: + - "main" pull_request: - branches: [ "master" ] + branches: ["**"] env: - # Use docker.io for Docker Hub if empty - REGISTRY: ghcr.io - # github.repository as / + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag IMAGE_NAME: ${{ github.repository }} - + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} jobs: build: - - name: Build the Docker image - run: docker build . --file path/to/Dockerfile --tag my-image-name:$(date +%s) - - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. - id-token: write + runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Install the cosign tool except on PR - # https://github.com/sigstore/cosign-installer - - name: Install cosign - if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 - with: - cosign-release: 'v2.2.4' - - # Set up BuildKit Docker container builder to be able to build - # multi-platform images and export cache - # https://github.com/docker/setup-buildx-action - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - - # Login against a Docker registry except on PR - # https://github.com/docker/login-action - - name: Log into registry ${{ env.REGISTRY }} - if: github.event_name != 'pull_request' - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta - uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) - name: Build and push Docker image id: build-and-push - uses: docker/build-push-action@v5.0.0 + uses: docker/build-push-action@v6 with: - context: ./ + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - - # Sign the resulting Docker image digest except on PRs. - # This will only write to the public Rekor transparency log when the Docker - # repository is public to avoid leaking data. If you would like to publish - # transparency data even for private images, pass --force to cosign below. - # https://github.com/sigstore/cosign - - name: Sign the published Docker image - if: ${{ github.event_name != 'pull_request' }} - env: - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - TAGS: ${{ steps.meta.outputs.tags }} - DIGEST: ${{ steps.build-and-push.outputs.digest }} - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From 76bba522f0409013e1e868a7c13b7dd4a533156d Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:43:24 +0700 Subject: [PATCH 248/374] Wagmi (e604566) (#344) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol * Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update docker.yml --------- Signed-off-by: dependabot[bot] Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Desant pivo Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory --- .circleci/cargo.yml | 32 +++ .circleci/ci-web3-gamefi.yml | 26 +++ .circleci/ci.yml | 31 +++ .circleci/ci_cargo.yml | 37 +++ .circleci/ci_v1.yml | 31 +++ .circleci/config.yml | 32 +++ .circleci/dev_stage.yml | 70 ++++++ .circleci/web3_defi_gamefi.yml | 26 +++ .codesandbox/tasks.json | 7 + .deps/remix-tests/remix_accounts.sol | 39 ++++ .deps/remix-tests/remix_tests.sol | 225 +++++++++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 39 ++++ .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 ++ .github/workflows/apisec-scan.yml | 29 +++ .github/workflows/codeql.yml | 92 ++++++++ .github/workflows/deploy.yml | 27 +++ .github/workflows/docker.yml | 62 +++++ .github/workflows/google.yml | 117 ++++++++++ .github/workflows/npm.yml | 37 +++ .github/workflows/snyk-container.yml | 55 +++++ .gitmodules | 6 + benches/src/lib.rs | 16 +- counter/.github/workflows/test.yml | 43 ++++ counter/.gitignore | 14 ++ counter/README.md | 66 ++++++ counter/foundry.toml | 6 + counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 ++ counter/src/Counter.sol | 14 ++ counter/test/Counter.t.sol | 24 ++ crates/cast/src/cmd/run.rs | 14 +- crates/cast/tests/cli/main.rs | 28 +++ crates/cli/src/utils/suggestions.rs | 3 +- crates/common/src/contracts.rs | 2 +- crates/doc/src/parser/comment.rs | 6 + crates/doc/src/writer/as_doc.rs | 35 ++- crates/doc/src/writer/buf_writer.rs | 20 +- crates/evm/evm/src/executors/trace.rs | 28 ++- crates/forge/Cargo.toml | 1 + crates/forge/tests/cli/test_optimizer.rs | 1 - crates/lint/src/linter.rs | 129 +++++++++++ crates/lint/src/sol/gas/keccak.rs | 98 +------- crates/script-sequence/src/sequence.rs | 10 +- crates/script/src/simulate.rs | 10 +- crates/test-utils/src/script.rs | 29 ++- crates/verify/src/etherscan/standard_json.rs | 2 +- flake.nix | 10 +- npm/package.json | 2 +- npm/scripts/stage-from-artifact.mjs | 28 ++- npm/src/const.mjs | 30 ++- 52 files changed, 1588 insertions(+), 152 deletions(-) create mode 100644 .circleci/cargo.yml create mode 100644 .circleci/ci-web3-gamefi.yml create mode 100644 .circleci/ci.yml create mode 100644 .circleci/ci_cargo.yml create mode 100644 .circleci/ci_v1.yml create mode 100644 .circleci/config.yml create mode 100644 .circleci/dev_stage.yml create mode 100644 .circleci/web3_defi_gamefi.yml create mode 100644 .codesandbox/tasks.json create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/google.yml create mode 100644 .github/workflows/snyk-container.yml create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol create mode 100644 crates/lint/src/linter.rs diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..32b65e6a23cc5 --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,32 @@ +version: 2.1 +# +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..82c6de5b42b73 --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..4168efef0971f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..5ba351727d22b --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,70 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..53a505774ac88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 7bb1ad13a42db..6347fe0fd59d2 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -233,9 +233,46 @@ jobs: PLATFORM='${{ matrix.os }}' ARCH='${{ matrix.arch }}' + # Basic validation of matrix-derived values to avoid path manipulation + case "$TOOL" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid TOOL value: $TOOL" >&2; exit 1 ;; + esac + case "$PLATFORM" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid PLATFORM value: $PLATFORM" >&2; exit 1 ;; + esac + case "$ARCH" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid ARCH value: $ARCH" >&2; exit 1 ;; + esac + PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" + echo "Preparing to publish package from: $PACKAGE_DIR" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 + exit 1 + fi + + # Resolve to an absolute path and ensure it stays within ./@foundry-rs + ABS_PACKAGE_DIR="$(realpath "$PACKAGE_DIR")" + ABS_EXPECTED_ROOT="$(realpath "./@foundry-rs")" + case "$ABS_PACKAGE_DIR" in + "$ABS_EXPECTED_ROOT"/*) ;; + *) + echo "ERROR: Resolved package directory is outside expected root:" >&2 + echo " ABS_PACKAGE_DIR=$ABS_PACKAGE_DIR" >&2 + echo " ABS_EXPECTED_ROOT=$ABS_EXPECTED_ROOT" >&2 + exit 1 + ;; + esac + ls -la "$PACKAGE_DIR" + # Minimal sanity check: require a package.json before publishing + if [[ ! -f "$PACKAGE_DIR/package.json" ]]; then + echo "ERROR: package.json not found in $PACKAGE_DIR; refusing to publish." >&2 + exit 1 + fi + bun ./scripts/publish.mjs "$PACKAGE_DIR" echo "Published @foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/benches/src/lib.rs b/benches/src/lib.rs index d50f88ae2bd63..55bbd9762b1d9 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -130,10 +130,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 492ce360e7c2e..0999aa55ec495 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -31,7 +31,7 @@ use foundry_evm::{ utils::configure_tx_env, }; use futures::TryFutureExt; -use revm::DatabaseRef; +use revm::{DatabaseRef, primitives::hardfork::SpecId}; /// CLI arguments for `cast run`. #[derive(Clone, Debug, Parser)] @@ -183,6 +183,8 @@ impl RunArgs { env.evm_env.cfg_env.limit_contract_code_size = None; env.evm_env.block_env.number = U256::from(tx_block_number); + let mut parent_beacon_block_root = None; + if let Some(block) = &block { env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); env.evm_env.block_env.beneficiary = block.header.beneficiary; @@ -191,6 +193,10 @@ impl RunArgs { env.evm_env.block_env.basefee = block.header.base_fee_per_gas.unwrap_or_default(); env.evm_env.block_env.gas_limit = block.header.gas_limit; + if env.evm_env.cfg_env.spec >= SpecId::CANCUN { + parent_beacon_block_root = block.header.parent_beacon_block_root; + } + // TODO: we need a smarter way to map the block to the corresponding evm_version for // commonly used chains if evm_version.is_none() { @@ -223,6 +229,12 @@ impl RunArgs { create2_deployer, None, )?; + + if let Some(parent_beacon_block_root) = parent_beacon_block_root { + let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().wrap_err("failed to convert block timestamp to u64")?; + executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; + } + let mut env = Env::new_with_spec_id( env.evm_env.cfg_env.clone(), env.evm_env.block_env.clone(), diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index a9147e2e284ff..7aafdceb8a402 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2998,6 +2998,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..8f6d7f3cde092 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -16,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 06b2c29945417..f079d0f4fbf3d 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) .map(|(_, data)| data) } diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 431bf70df88f5..69f529c3eb6a2 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -155,6 +155,12 @@ impl From> for Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index a215a95b581c7..455cd4082bb61 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -1,12 +1,11 @@ use crate::{ - CONTRACT_INHERITANCE_ID, CommentTag, Comments, CommentsRef, DEPLOYMENTS_ID, Document, - GIT_SOURCE_ID, INHERITDOC_ID, Markdown, PreprocessorOutput, - document::{DocumentContent, read_context}, - helpers::function_signature, + document::{read_context, DocumentContent}, parser::ParseSource, - solang_ext::SafeUnwrap, writer::BufWriter, + CommentTag, Comments, CommentsRef, Document, Markdown, PreprocessorOutput, + CONTRACT_INHERITANCE_ID, DEPLOYMENTS_ID, GIT_SOURCE_ID, INHERITDOC_ID, }; +use forge_fmt::solang_ext::SafeUnwrap; use itertools::Itertools; use solang_parser::pt::{Base, FunctionDefinition}; use std::path::Path; @@ -63,7 +62,7 @@ impl AsDoc for CommentsRef<'_> { // Write dev tags let devs = self.include_tag(CommentTag::Dev); for d in devs.iter() { - writer.write_dev_content(&d.value)?; + writer.write_italic(&d.value)?; writer.writeln()?; } @@ -107,7 +106,16 @@ impl AsDoc for Document { for item in items { let func = item.as_function().unwrap(); - let heading = function_signature(func).replace(',', ", "); + let mut heading = item.source.ident(); + if !func.params.is_empty() { + heading.push_str(&format!( + "({})", + func.params + .iter() + .map(|p| p.1.as_ref().map(|p| p.ty.to_string()).unwrap_or_default()) + .join(", ") + )); + } writer.write_heading(&heading)?; writer.write_section(&item.comments, &item.code)?; } @@ -226,7 +234,15 @@ impl AsDoc for Document { writer.write_subtitle("Enums")?; enums.into_iter().try_for_each(|(item, comments, code)| { writer.write_heading(&item.name.safe_unwrap().name)?; - writer.write_section(comments, code)?; + + let filtered_comments: Comments = (*comments) + .iter() + .filter(|c| c.tag != CommentTag::Custom("variant".to_string())) + .cloned() + .collect::>() + .into(); + + writer.write_section(&filtered_comments, code)?; writer.try_write_variant_table(item, comments) })?; } @@ -292,10 +308,9 @@ impl Document { comments: &Comments, code: &str, ) -> Result<(), std::fmt::Error> { - let func_sign = function_signature(func); let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned()); let comments = - comments.merge_inheritdoc(&func_sign, read_context!(self, INHERITDOC_ID, Inheritdoc)); + comments.merge_inheritdoc(&func_name, read_context!(self, INHERITDOC_ID, Inheritdoc)); // Write function name writer.write_heading(&func_name)?; diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index a62dff3f21582..e5dfbbaadce25 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,4 +1,4 @@ -use crate::{AsDoc, CommentTag, Comments, Deployment, Markdown, writer::traits::ParamLike}; +use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; use solang_parser::pt::{ EnumDefinition, ErrorParameter, EventParameter, Parameter, VariableDeclaration, @@ -89,17 +89,6 @@ impl BufWriter { writeln!(self.buf, "{}", Markdown::Italic(text)) } - /// Writes dev content to the buffer, handling markdown lists properly. - /// If the content contains markdown lists, it formats them correctly. - /// Otherwise, it writes the content in italics. - pub fn write_dev_content(&mut self, text: &str) -> fmt::Result { - for line in text.lines() { - writeln!(self.buf, "{line}")?; - } - - Ok(()) - } - /// Writes bold text to the buffer formatted as [Markdown::Bold]. pub fn write_bold(&mut self, text: &str) -> fmt::Result { writeln!(self.buf, "{}", Markdown::Bold(text)) @@ -202,7 +191,8 @@ impl BufWriter { params: &EnumDefinition, comments: &Comments, ) -> fmt::Result { - let comments = comments.include_tags(&[CommentTag::Param]); + let comments = + comments.include_tags(&[CommentTag::Param, CommentTag::Custom("variant".to_string())]); // There is nothing to write. if comments.is_empty() { @@ -215,7 +205,7 @@ impl BufWriter { self.write_piped(&VARIANTS_TABLE_HEADERS.join("|"))?; self.write_piped(&VARIANTS_TABLE_SEPARATOR)?; - for value in ¶ms.values { + for value in params.values.iter() { let param_name = value.as_ref().map(|v| v.name.clone()); let comment = param_name.as_ref().and_then(|name| { @@ -223,7 +213,7 @@ impl BufWriter { }); let row = [ - Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + Markdown::Code(param_name.as_deref().unwrap_or("")).as_doc()?, comment.unwrap_or_default().replace('\n', " "), ]; self.write_piped(&row.join("|"))?; diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index d951dde0d6663..7b63c019e848c 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -2,7 +2,7 @@ use crate::{ Env, executors::{Executor, ExecutorBuilder}, }; -use alloy_primitives::{Address, U256, map::HashMap}; +use alloy_primitives::{Address, FixedBytes, U256, address, map::HashMap}; use alloy_rpc_types::state::StateOverride; use eyre::Context; use foundry_compilers::artifacts::EvmVersion; @@ -92,6 +92,32 @@ impl TracingExecutor { let chain = env.tx.chain_id.unwrap().into(); Ok((env, fork, chain, networks)) } + + /// Processes the beacon block root by storing it in the appropriate storage slots. + pub fn process_beacon_block_root( + &mut self, + block_timestamp: u64, + beacon_root: FixedBytes<32>, + ) -> eyre::Result<()> { + const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); + const HISTORY_BUFFER_LENGTH: u64 = 8192; + + let timestamp_index = block_timestamp % HISTORY_BUFFER_LENGTH; + let root_index = timestamp_index + HISTORY_BUFFER_LENGTH; + + let timestamp_slot = U256::from(timestamp_index); + let root_slot = U256::from(root_index); + + self.set_storage_slot(BEACON_ROOTS_ADDRESS, timestamp_slot, U256::from(block_timestamp))?; + + self.set_storage_slot( + BEACON_ROOTS_ADDRESS, + root_slot, + U256::from_be_bytes(beacon_root.into()), + )?; + + Ok(()) + } } impl Deref for TracingExecutor { diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index cd552853cfcff..2142a87c5cbd4 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,6 +62,7 @@ alloy-rpc-types.workspace = true alloy-serde.workspace = true alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true revm.workspace = true diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index cb942510bbb49..7316f4c4239b7 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -1,103 +1,27 @@ use super::AsmKeccak256; use crate::{ - linter::{LateLintPass, LintContext}, + declare_forge_lint, + linter::EarlyLintPass, sol::{Severity, SolLint}, }; -use solar::{ - ast::{self as ast, Span}, - interface::kw, - sema::hir::{self}, -}; +use solar_ast::{Expr, ExprKind}; +use solar_interface::kw; declare_forge_lint!( ASM_KECCAK256, Severity::Gas, "asm-keccak256", - "use of inefficient hashing mechanism; consider using inline assembly" + "hash using inline assembly to save gas" ); -impl<'hir> LateLintPass<'hir> for AsmKeccak256 { - fn check_stmt( - &mut self, - ctx: &LintContext, - hir: &'hir hir::Hir<'hir>, - stmt: &'hir hir::Stmt<'hir>, - ) { - let check_expr_and_emit_lint = - |expr: &'hir hir::Expr<'hir>, assign: Option, is_return: bool| { - if let Some(hash_arg) = extract_keccak256_arg(expr) { - self.emit_lint( - ctx, - hir, - stmt.span, - expr, - hash_arg, - AsmContext { _assign: assign, _is_return: is_return }, - ); - } - }; - - match stmt.kind { - hir::StmtKind::DeclSingle(var_id) => { - let var = hir.variable(var_id); - if let Some(init) = var.initializer { - // Constants should be optimized by the compiler, so no gas savings apply. - if !matches!(var.mutability, Some(hir::VarMut::Constant)) { - check_expr_and_emit_lint(init, var.name, false); - } +impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 { + fn check_expr(&mut self, ctx: &crate::linter::LintContext<'_>, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(expr, _) = &expr.kind { + if let ExprKind::Ident(ident) = &expr.kind { + if ident.name == kw::Keccak256 { + ctx.emit(&ASM_KECCAK256, expr.span); } } - // Expressions that don't (directly) assign to a variable - hir::StmtKind::Expr(expr) - | hir::StmtKind::Emit(expr) - | hir::StmtKind::Revert(expr) - | hir::StmtKind::DeclMulti(_, expr) - | hir::StmtKind::If(expr, ..) => check_expr_and_emit_lint(expr, None, false), - hir::StmtKind::Return(Some(expr)) => check_expr_and_emit_lint(expr, None, true), - _ => (), } } } - -impl AsmKeccak256 { - /// Emits lints (when possible with fix suggestions) for inefficient `keccak256` calls. - fn emit_lint( - &self, - ctx: &LintContext, - _hir: &hir::Hir<'_>, - _stmt_span: Span, - call: &hir::Expr<'_>, - _hash: &hir::Expr<'_>, - _asm_ctx: AsmContext, - ) { - ctx.emit(&ASM_KECCAK256, call.span); - } -} - -/// If the expression is a call to `keccak256` with one argument, returns that argument. -fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Expr<'hir>> { - let hir::ExprKind::Call( - callee, - hir::CallArgs { kind: hir::CallArgsKind::Unnamed(args), .. }, - .., - ) = &expr.kind - else { - return None; - }; - - let is_keccak = if let hir::ExprKind::Ident([hir::Res::Builtin(builtin)]) = callee.kind { - matches!(builtin.name(), kw::Keccak256) - } else { - return None; - }; - - if is_keccak && args.len() == 1 { Some(&args[0]) } else { None } -} - -// -- HELPER FUNCTIONS AND STRUCTS ---------------------------------------------------------------- - -#[derive(Debug, Clone, Copy)] -struct AsmContext { - _assign: Option, - _is_return: bool, -} diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index 5da88735a70ec..56b7c6b36f79c 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -12,7 +12,7 @@ use std::{ path::PathBuf, time::{Duration, SystemTime, UNIX_EPOCH}, }; - +# pub const DRY_RUN_DIR: &str = "dry-run"; #[derive(Clone, Serialize, Deserialize)] @@ -20,7 +20,7 @@ pub struct NestedValue { pub internal_type: String, pub value: String, } - +# /// Helper that saves the transactions sequence and its state on which transactions have been /// broadcasted #[derive(Clone, Default, Serialize, Deserialize)] @@ -148,7 +148,7 @@ impl ScriptSequence { 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_by_key(|r| (r.block_number, r.transaction_index)); @@ -164,7 +164,7 @@ impl ScriptSequence { pub fn remove_pending(&mut self, tx_hash: TxHash) { self.pending.retain(|element| element != &tx_hash); } - + # /// Gets paths in the formats /// `./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json` and /// `./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json`. @@ -219,7 +219,7 @@ impl ScriptSequence { .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); } } - +# /// 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 index ab57786398428..0b2be58ca30d2 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,7 +9,6 @@ use crate::{ execute::{ExecutionArtifacts, ExecutionData}, sequence::get_commit_hash, }; -use alloy_chains::NamedChain; use alloy_network::TransactionBuilder; use alloy_primitives::{Address, TxKind, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; @@ -352,12 +351,6 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); - // Get the native token symbol for the chain using NamedChain - let token_symbol = NamedChain::try_from(provider_info.chain) - .unwrap_or_default() - .native_currency_symbol() - .unwrap_or("ETH"); - // 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 { @@ -381,7 +374,7 @@ impl FilledTransactionsState { sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; + sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; sh_println!("\n==========================")?; } else { sh_println!( @@ -391,7 +384,6 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, - "token_symbol": token_symbol, }) )?; } diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 45dbbbebdd73a..07413fef5495c 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,9 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); - fs::copy(file, to_dir.join(name))?; + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } else { + continue; + } + fs::copy(&file, to_dir.join(name))?; } Ok(()) } diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index 8a38485e48922..0471e3e42888e 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -56,7 +56,7 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { let sources = Source::read_all_from(path, &["vy", "vyi"])?; let input = VyperInput::new( sources, - context.clone().compiler_settings.vyper, + context.compiler_settings.vyper.clone(), &context.compiler_version, ); diff --git a/flake.nix b/flake.nix index 5caaab9934aec..8cf41ed16d3a3 100644 --- a/flake.nix +++ b/flake.nix @@ -19,8 +19,7 @@ lib = pkgs.lib; toolchain = fenix.packages.${system}.stable.toolchain; - in - { + in { default = pkgs.mkShell { nativeBuildInputs = with pkgs; [ pkg-config @@ -29,16 +28,13 @@ # test dependencies solc vyper - dprint nodejs ]; + buildInputs = lib.optionals pkgs.stdenv.isDarwin + [ pkgs.darwin.apple_sdk.frameworks.AppKit ]; packages = with pkgs; [ rust-analyzer-unwrapped ]; - # Remove the hardening added by nix to fix jmalloc compilation error. - # More info: https://github.com/tikv/jemallocator/issues/108 - hardeningDisable = [ "fortify" ]; - # Environment variables RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library"; LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.libusb1 ]; diff --git a/npm/package.json b/npm/package.json index 2362816053f8c..d596a7930b609 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.0.2", "bun": "^1.3.1", "typescript": "^5.9.3" }, diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix diff --git a/npm/src/const.mjs b/npm/src/const.mjs index 6b3dcf3f9fbed..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** From eaf1abe653e58669070007dbd2a3a11f4c44c67e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:00:43 +0700 Subject: [PATCH 249/374] Potential fix for code scanning alert no. 102: Artifact poisoning (#354) * Potential fix for code scanning alert no. 102: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- .github/workflows/npm.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index df45e41a50295..741590095fec5 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -136,6 +136,36 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id || inputs.run_id }} + - name: Validate Downloaded Artifacts + env: + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} + run: | + set -euo pipefail + + echo "Validating artifacts in: $ARTIFACT_DIR" + + if [[ ! -d "$ARTIFACT_DIR" ]]; then + echo "ERROR: Artifact directory does not exist: $ARTIFACT_DIR" >&2 + exit 1 + fi + + if ! find "$ARTIFACT_DIR" -mindepth 1 -print -quit | grep -q .; then + echo "ERROR: Artifact directory is empty: $ARTIFACT_DIR" >&2 + exit 1 + fi + + # Reject files with suspicious paths (absolute paths or parent directory traversals) + # Use null-delimited paths to safely handle filenames with newlines or whitespace + while IFS= read -r -d '' path; do + rel="${path#"$ARTIFACT_DIR"/}" + if [[ "$rel" == /* ]] || [[ "$rel" == *".."* ]]; then + echo "ERROR: Suspicious artifact path detected: $rel" >&2 + exit 1 + fi + done < <(find "$ARTIFACT_DIR" -type f -print0) + + echo "Artifact validation completed successfully." + - name: Setup Bun uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 with: From 3ff465c909d0ac9f933f3f5abe6e6491cbcafce9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:32:33 +0700 Subject: [PATCH 250/374] fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#357) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> --- crates/config/src/etherscan.rs | 63 ++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/crates/config/src/etherscan.rs b/crates/config/src/etherscan.rs index 5999825e94681..e24f92b7d931e 100644 --- a/crates/config/src/etherscan.rs +++ b/crates/config/src/etherscan.rs @@ -319,10 +319,17 @@ impl ResolvedEtherscanConfig { .with_client(client) .with_api_key(api_key) .with_cache(cache, Duration::from_secs(24 * 60 * 60)); - if let Some(browser_url) = browser_url { + if let Some(ref browser_url) = browser_url { client_builder = client_builder.with_url(browser_url)?; } - client_builder.chain(chain)?.build() + + // Use the provided URL (either custom from foundry.toml or chain's default from resolve()) + client_builder = client_builder.with_api_url(api_url.clone())?; + // Fallback: Use api_url as browser URL if browser_url is not set + if browser_url.is_none() { + client_builder = client_builder.with_url(api_url)?; + } + client_builder.build() } } @@ -478,10 +485,8 @@ mod tests { let config = resolved.remove("mainnet").unwrap().unwrap(); assert_eq!(config.key, "ABCDEFG"); let client = config.into_client().unwrap(); - assert_eq!( - client.etherscan_api_url().as_str(), - "https://api.etherscan.io/v2/api?chainid=1" - ); + // Custom URL should be used even when chain has a default URL + assert_eq!(client.etherscan_api_url().as_str(), "https://api.etherscan.io/api"); unsafe { std::env::remove_var(env); @@ -518,4 +523,50 @@ mod tests { let resolved = config.resolve(Some("base-sepolia")).unwrap(); assert_eq!(resolved.chain, Some(Chain::base_sepolia())); } + + #[test] + fn can_create_client_with_custom_url_for_chain_without_default_url() { + // Chains without default Etherscan URLs (e.g., Dev, AnvilHardhat networks) + // should work if a custom URL is provided in foundry.toml. + let mut configs = EtherscanConfigs::default(); + configs.insert( + "dev".to_string(), + EtherscanConfig { + chain: Some(Chain::dev()), + url: Some("https://custom.api.url/verify/etherscan".to_string()), + key: EtherscanApiKey::Key("test_key".to_string()), + }, + ); + + let mut resolved = configs.resolved(); + let config = resolved.remove("dev").unwrap().unwrap(); + let result = config.into_client(); + assert!( + result.is_ok(), + "Should succeed with custom URL even for chains without default Etherscan URLs" + ); + } + + #[test] + fn fails_without_custom_url_for_chain_without_default_url() { + // Chains without default Etherscan URLs (e.g., Dev, AnvilHardhat networks) + // should fail if no custom URL is provided in foundry.toml. + let mut configs = EtherscanConfigs::default(); + configs.insert( + "dev".to_string(), + EtherscanConfig { + chain: Some(Chain::dev()), + url: None, + key: EtherscanApiKey::Key("test_key".to_string()), + }, + ); + + let mut resolved = configs.resolved(); + let config = resolved.remove("dev").unwrap(); + + assert!( + config.is_err(), + "Should fail: chains without default Etherscan URLs require custom URL" + ); + } } From 1528ab2395a035c59cde2334575860763df6b802 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 28 Jan 2026 17:25:47 +0700 Subject: [PATCH 251/374] Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. --- .circleci/ci_deploy.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail From a737f7f3a7a0ca7e92f06370e92f6155d7fcc2cc Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:02:20 +0000 Subject: [PATCH 252/374] refactor(common): make ProviderBuilder generic over Network #13250 (#361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(common): make `ProviderBuilder` generic over `Network` (#13250) * refactor(common): make `ProviderBuilder` generic over `Network` - Updated `ProviderBuilder` helper to be generic, which will facilate `FoundryNetwork` rollout - Adjusted the instantiation of `ProviderBuilder` in various locations to use `AnyNetwork`. - Enhanced the `build` and `build_with_wallet` methods to accommodate the new generic structure. * fix: relax trait bound on `N::TransactionRequest` * fix: comment * fix: comment * fix(anvil): return error instead of empty vec for out-of-range log queries (#13251) * fix(config): respect user-configured etherscan URL over chain defaults (#13239) fix(config): Respect user-configured etherscan URL over chain defaults (#13238) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` (#13218) * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` * fix: after `TransactionBuilder4844` impl * feat(common): introduce generic `ProviderBuilder::from_config` method (#13268) - keep the existing helpers that erase generic to avoid breaking change * fix(cast): remove redundant chain() call in explorer_client (#13272) Co-authored-by: tefyosL-sol * fix(verify): respect user-configured etherscan URL over chain defaults (#13275) Co-authored-by: tefyosL-sol * Update flake.lock (#13279) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/93523fa' (2026-01-24) → 'github:nix-community/fenix/b2344f3' (2026-01-31) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) → 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) → 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) Co-authored-by: github-actions[bot] * fix(invariant): remove unused cloned calldata (#12893) * fix(invariant): prune calldata to bound memory usage in long runs * style: fix formatting in invariant executor * chore: remove vyper files from testdata to fix CI * chore: trigger ci update * fix(invariant): remove unused FuzzCase.calldata field to prevent OOM The calldata field in FuzzCase was stored but never read after construction. Removing it entirely eliminates memory accumulation during long invariant runs. Changes: - Remove FuzzCase.calldata field (unused after construction) - Remove prune_calldata() methods (no longer needed) - Restore vyper test files that were incorrectly deleted Fixes #12397 Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(debugger): display actual gas usage alongside refund counter (#13271) * feat(debugger): display actual gas usage alongside refund counter * fix(forge-test): fix flamegraph gas inaccuracy issues * reverse `--decode-internal` not default with `--flamechart` * make gas unsigned as `inferno` doesn't support anyway * replace revm-inspectors patch * fix clippy * update tests * update tests * fmt * fix(config): handle decimal string in U256 deserialization (#13284) * fix: adjust numerical cells in gas report to be right aligned (#12883) * Adjust Cells to be Right Aligned in Gas Report * fix test and fmt --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): weekly `cargo update` (#13280) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v1.5.2 -> v1.5.4 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-json-abi v1.5.2 -> v1.5.4 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-primitives v1.5.2 -> v1.5.4 Updating alloy-sol-macro v1.5.2 -> v1.5.4 Updating alloy-sol-macro-expander v1.5.2 -> v1.5.4 Updating alloy-sol-macro-input v1.5.2 -> v1.5.4 Updating alloy-sol-type-parser v1.5.2 -> v1.5.4 Updating alloy-sol-types v1.5.2 -> v1.5.4 Updating annotate-snippets v0.12.10 -> v0.12.11 Updating aws-smithy-async v1.2.7 -> v1.2.10 Updating aws-smithy-http-client v1.1.5 -> v1.1.8 Updating aws-smithy-observability v0.2.0 -> v0.2.3 Updating aws-smithy-query v0.60.9 -> v0.60.12 Updating aws-smithy-runtime-api v1.10.0 -> v1.11.2 Updating aws-smithy-types v1.3.6 -> v1.4.2 Updating bytemuck v1.24.0 -> v1.25.0 Updating cc v1.2.54 -> v1.2.55 Updating clap v4.5.54 -> v4.5.56 Updating clap_builder v4.5.54 -> v4.5.56 Updating clap_derive v4.5.49 -> v4.5.55 Updating cliclack v0.3.7 -> v0.3.8 Updating find-msvc-tools v0.1.8 -> v0.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating iana-time-zone v0.1.64 -> v0.1.65 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating keccak-asm v0.1.4 -> v0.1.5 Unchanged matchit v0.8.4 (available: v0.8.6) Updating notify-types v2.0.0 -> v2.1.0 Updating portable-atomic v1.13.0 -> v1.13.1 Updating portable-atomic-util v0.2.4 -> v0.2.5 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating revm-inspectors v0.34.0 -> v0.34.2 Updating sha3-asm v0.1.4 -> v0.1.5 Updating siphasher v1.0.1 -> v1.0.2 Updating slab v0.4.11 -> v0.4.12 Updating syn-solidity v1.5.2 -> v1.5.4 Removing tiny-keccak v2.0.2 Updating zerocopy v0.8.33 -> v0.8.37 Updating zerocopy-derive v0.8.33 -> v0.8.37 Updating zmij v1.0.16 -> v1.0.18 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: update RPC URLs from ithaca.xyz to reth.rs (#13261) * chore: update RPC URLs from ithaca.xyz to reth.rs Co-authored-by: Tim Beiko Amp-Thread-ID: https://ampcode.com/threads/T-019c0a51-3f0a-76eb-ba4a-bfb6a697d9ba Co-authored-by: Amp * fix * fmt * Update rpc.rs * Update rpc.rs * test: update test --------- Co-authored-by: Tim Beiko Co-authored-by: Amp Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg * ci(bench): use depot runner for benchmarks (#13288) * feat(cli): cli markdown docs (#13291) * feat: add foundry-cli-markdown crate Add a new crate for generating Markdown documentation from clap CLIs. This is a fork of clap-markdown with the following enhancements: - Support for grouped options by help heading (PR #48) - Show environment variable names for arguments (PR #50) - Add version information to generated Markdown (PR #52) * feat(cli): add hidden --markdown-help flag Add a hidden --markdown-help flag to forge, cast, anvil, and chisel that prints CLI reference documentation as Markdown and exits. This uses the new foundry-cli-markdown crate to generate the output. * chore(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#13298) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/5e57cd118135c172c3672efd75eb46360885c0ef...c94ce9fb468520275223c153574b00df6fe4bcc9) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.13 to 2.67.18 (#13297) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.13 to 2.67.18. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/710817a1645ef40daad5bcde7431ceccf6cc3528...650c5ca14212efbbf3e580844b04bdccf68dac31) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 (#13299) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/727cc5b0b19bc265bd5ef28fc66bccb284473b5d...5adeaaaf36f64df54f62adb34aa5fbfdb0109d34) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.0.1 to 6.1.0 (#13300) chore(deps): bump mikepenz/release-changelog-builder-action Bumps [mikepenz/release-changelog-builder-action](https://github.com/mikepenz/release-changelog-builder-action) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/mikepenz/release-changelog-builder-action/releases) - [Commits](https://github.com/mikepenz/release-changelog-builder-action/compare/439f79b5b5428107c7688c1d2b0e8bacc9b8792c...6faf020194b7c8853f9e55c4fd92e40b02122a04) --- updated-dependencies: - dependency-name: mikepenz/release-changelog-builder-action dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: fix typos CI (#13303) - Add consts to typos ignore list (valid Rust std::env::consts) - Fix LintCotext -> LintContext typos in linter docs * chore(deps): bump crate-ci/typos from 1.42.2 to 1.43.0 (#13296) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.2 to 1.43.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/a1d64977b4aa1709d6328d518aa753f4899352d8...93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * sec: bump to `bytes` `^1.11.1` for `RUSTSEC-2026-0007` (#13306) bump to 1.11.1 for patch: https://rustsec.org/advisories/RUSTSEC-2026-0007 * chore(anvil,cast): remove unnecessary `populate_blob_hashes()` (#13308) Both `set_blob_sidecar`/`set_blob_sidecar_7594` implement a call to `populate_blob_hashes()` * feat(forge): generate random fuzz seed if none provided (#13309) * feat(forge): generate random fuzz seed if none provided Generate a random seed for fuzz/invariant tests when no seed is explicitly configured. This ensures reproducibility by always having a seed available that can be passed via --fuzz-seed to reproduce test runs. * feat(forge): print fuzz seed on fuzz failure (#13310) * feat(forge): print fuzz seed on test failure When a fuzz or invariant test fails, print the seed used so users can reproduce the failure with --fuzz-seed. * test: update snapshots for fuzz seed output Add [SEED] redaction pattern to match 'Fuzz seed: 0x...' output. Update all test snapshots that have fuzz/invariant failures to include the new seed line. * fix: use [SEED] placeholder in issue_3055 test snapshot Amp-Thread-ID: https://ampcode.com/threads/T-019c26cb-9d21-74f9-9e49-7ea59885e827 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(anvil): cache block timestamp in mined receipts (#13311) * fix: use shared `display_chain` helper in CLI error handler (#13314) * Update handler.rs * Update handler.rs * Add --enable-tx-gas-limit CLI flag for EIP-7825 support (#13307) add --enable-tx-gas-limit * fix(anvil): use consistent chain_id fallback in fork setup (#13276) Co-authored-by: tefyosL-sol * fix(test-utils): skip build artifacts when copying to temp workspace (#13266) * feat(lint): add common uppercase abbreviations to mixedCase exceptions (#13305) Co-authored-by: onbjerg * fix(fmt): correct indentation for closing brace in empty contracts with comments (#13319) Co-authored-by: onbjerg * feat(anvil): add `trace_replayBlockTransactions` endpoint for block txs tracing (#13098) Co-authored-by: onbjerg * fix(eip712): write diagnostics to stderr instead of stdout (#13293) Co-authored-by: onbjerg --------- Signed-off-by: dependabot[bot] Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Matt D Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Tim Beiko Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> --- .github/workflows/benchmarks.yml | 2 +- .github/workflows/ci.yml | 4 +- .github/workflows/docker-publish.yml | 2 +- .github/workflows/nix.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 2 +- Cargo.lock | 188 +++--- Cargo.toml | 4 +- crates/anvil/core/src/eth/mod.rs | 10 +- crates/anvil/src/args.rs | 2 + crates/anvil/src/cmd.rs | 15 + crates/anvil/src/config.rs | 40 +- crates/anvil/src/eth/api.rs | 17 +- crates/anvil/src/eth/backend/fork.rs | 14 +- crates/anvil/src/eth/backend/mem/mod.rs | 118 +++- crates/anvil/src/eth/otterscan/api.rs | 10 +- crates/anvil/test-data/state-dump.json | 315 +++++++++- crates/anvil/tests/it/beacon_api.rs | 3 +- crates/anvil/tests/it/eip4844.rs | 47 +- crates/anvil/tests/it/state.rs | 1 + crates/anvil/tests/it/traces.rs | 80 ++- crates/cast/src/args.rs | 2 + crates/cast/src/lib.rs | 2 +- crates/cast/src/tx.rs | 2 - crates/cheatcodes/src/evm/fork.rs | 7 +- crates/chisel/src/args.rs | 2 + crates/cli-markdown/Cargo.toml | 20 + crates/cli-markdown/src/lib.rs | 582 ++++++++++++++++++ crates/cli/Cargo.toml | 1 + crates/cli/src/handler.rs | 4 +- crates/cli/src/opts/global.rs | 11 + crates/cli/src/utils/mod.rs | 27 +- crates/common/Cargo.toml | 1 + crates/common/src/provider/mod.rs | 86 ++- crates/config/src/lint.rs | 17 +- crates/config/src/utils.rs | 7 +- crates/debugger/src/node.rs | 7 +- crates/debugger/src/tui/draw.rs | 5 +- crates/evm/evm/src/executors/fuzz/mod.rs | 2 +- crates/evm/evm/src/executors/invariant/mod.rs | 8 +- crates/evm/fuzz/src/lib.rs | 2 - crates/evm/traces/src/folded_stack_trace.rs | 32 +- crates/fmt/src/state/sol.rs | 4 +- crates/fmt/testdata/CommentEmptyLine/fmt.sol | 5 + .../testdata/CommentEmptyLine/original.sol | 6 + crates/fmt/tests/formatter.rs | 26 + crates/forge/Cargo.toml | 1 + crates/forge/src/args.rs | 2 + crates/forge/src/cmd/eip712.rs | 2 +- crates/forge/src/cmd/test/mod.rs | 15 +- crates/forge/src/gas_report.rs | 26 +- crates/forge/src/result.rs | 35 +- crates/forge/tests/cli/cmd.rs | 152 ++--- crates/forge/tests/cli/script.rs | 4 +- crates/forge/tests/cli/test_cmd/fuzz.rs | 2 + .../tests/cli/test_cmd/invariant/common.rs | 28 + .../forge/tests/cli/test_cmd/invariant/mod.rs | 6 + .../tests/cli/test_cmd/invariant/target.rs | 8 + crates/forge/tests/cli/test_cmd/mod.rs | 4 + crates/forge/tests/cli/test_cmd/repros.rs | 2 + .../fixtures/SimpleContractTestVerbose.json | 5 + .../forge/tests/fixtures/invariant_traces.svg | 8 +- crates/lint/src/linter/early.rs | 2 +- crates/lint/src/linter/late.rs | 2 +- crates/lint/testdata/MixedCase.sol | 13 + crates/primitives/src/network/receipt.rs | 18 + crates/primitives/src/transaction/request.rs | 9 +- crates/test-utils/src/prj.rs | 14 +- crates/test-utils/src/rpc.rs | 10 +- crates/test-utils/src/util.rs | 48 +- crates/verify/src/etherscan/mod.rs | 17 +- flake.lock | 18 +- testdata/default/cheats/RpcUrls.t.sol | 2 +- typos.toml | 1 + 76 files changed, 1825 insertions(+), 379 deletions(-) create mode 100644 crates/cli-markdown/Cargo.toml create mode 100644 crates/cli-markdown/src/lib.rs create mode 100644 crates/fmt/testdata/CommentEmptyLine/fmt.sol create mode 100644 crates/fmt/testdata/CommentEmptyLine/original.sol diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 34622ca7979a5..53e29ecd33691 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -29,7 +29,7 @@ env: jobs: run-benchmarks: name: Run All Benchmarks - runs-on: foundry-runner + runs-on: depot-ubuntu-24.04-32 permissions: contents: write steps: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0ca528491f95..bdb7848f55f29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: crate-ci/typos@a1d64977b4aa1709d6328d518aa753f4899352d8 # v1 + - uses: crate-ci/typos@93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c # v1 shellcheck: runs-on: depot-ubuntu-latest @@ -140,7 +140,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@710817a1645ef40daad5bcde7431ceccf6cc3528 # v2 + - uses: taiki-e/install-action@650c5ca14212efbbf3e580844b04bdccf68dac31 # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 566844094730d..51e625d0d6d28 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -44,7 +44,7 @@ jobs: # Login against a Docker registry except on PR # https://github.com/docker/login-action - name: Login into registry ${{ env.REGISTRY }} - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 7c348a9726f90..8d39e71112494 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: DeterminateSystems/update-flake-lock@727cc5b0b19bc265bd5ef28fc66bccb284473b5d # main + - uses: DeterminateSystems/update-flake-lock@5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 # main with: pr-title: "Update flake.lock" pr-labels: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b18435be45d22..a267433f27ec2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,7 @@ jobs: - name: Build changelog id: build_changelog - uses: mikepenz/release-changelog-builder-action@439f79b5b5428107c7688c1d2b0e8bacc9b8792c # v6 + uses: mikepenz/release-changelog-builder-action@6faf020194b7c8853f9e55c4fd92e40b02122a04 # v6 with: configuration: "./.github/changelog.json" fromTag: ${{ env.IS_NIGHTLY == 'true' && 'nightly' || env.STABLE_VERSION }} diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index 84029e775c2b4..858d1c8c43f2c 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -33,7 +33,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@710817a1645ef40daad5bcde7431ceccf6cc3528 # v2 + - uses: taiki-e/install-action@650c5ca14212efbbf3e580844b04bdccf68dac31 # v2 with: tool: nextest - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index ee17ace188413..d24fbfca3d1a0 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -37,7 +37,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@710817a1645ef40daad5bcde7431ceccf6cc3528 # v2 + - uses: taiki-e/install-action@650c5ca14212efbbf3e580844b04bdccf68dac31 # v2 with: tool: nextest - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25e32aac98b5e..abb8b8363eece 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@710817a1645ef40daad5bcde7431ceccf6cc3528 # v2 + - uses: taiki-e/install-action@650c5ca14212efbbf3e580844b04bdccf68dac31 # v2 with: tool: nextest diff --git a/Cargo.lock b/Cargo.lock index d9b1edda73916..7b638f17ed1a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369f5707b958927176265e8a58627fc6195e5dfa5c55689396e68b241b3a72e6" +checksum = "14ff5ee5f27aa305bda825c735f686ad71bb65508158f059f513895abe69b8c3" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -315,9 +315,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e3cf01219c966f95a460c95f1d4c30e12f6c18150c21a30b768af2a2a29142" +checksum = "8708475665cc00e081c085886e68eada2f64cfa08fc668213a9231655093d4de" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -411,9 +411,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a0fb18dd5fb43ec5f0f6a20be1ce0287c79825827de5744afaa6c957737c33" +checksum = "3b88cf92ed20685979ed1d8472422f0c6c2d010cec77caf63aaa7669cc1a7bc2" dependencies = [ "alloy-rlp", "arbitrary", @@ -437,7 +437,6 @@ dependencies = [ "rustc-hash", "serde", "sha3", - "tiny-keccak", ] [[package]] @@ -829,9 +828,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09eb18ce0df92b4277291bbaa0ed70545d78b02948df756bbd3d6214bf39a218" +checksum = "f5fa1ca7e617c634d2bd9fa71f9ec8e47c07106e248b9fcbd3eaddc13cabd625" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -843,9 +842,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d9fa2daf21f59aa546d549943f10b5cce1ae59986774019fbedae834ffe01b" +checksum = "27c00c0c3a75150a9dc7c8c679ca21853a137888b4e1c5569f92d7e2b15b5102" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -855,16 +854,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", + "sha3", "syn 2.0.114", "syn-solidity", - "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9396007fe69c26ee118a19f4dee1f5d1d6be186ea75b3881adf16d87f8444686" +checksum = "297db260eb4d67c105f68d6ba11b8874eec681caec5505eab8fbebee97f790bc" dependencies = [ "alloy-json-abi", "const-hex", @@ -880,9 +879,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" +checksum = "94b91b13181d3bcd23680fd29d7bc861d1f33fbe90fdd0af67162434aeba902d" dependencies = [ "serde", "winnow", @@ -890,9 +889,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09aeea64f09a7483bdcd4193634c7e5cf9fd7775ee767585270cd8ce2d69dc95" +checksum = "fc442cc2a75207b708d481314098a0f8b6f7b58e3148dd8d8cc7407b0d6f9385" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -1024,9 +1023,9 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.12.10" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15580ece6ea97cbf832d60ba19c021113469480852c6a2a6beb0db28f097bf1f" +checksum = "16e4850548ff4a25a77ce3bda7241874e17fb702ea551f0cc62a2dbe052f1272" dependencies = [ "anstyle", "memchr", @@ -1874,9 +1873,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.7" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee19095c7c4dda59f1697d028ce704c24b2d33c6718790c7f1d5a3015b4107c" +checksum = "e39f47abe8641e434de98e047e85ced629862e7ab719b6914a846796ceb289e2" dependencies = [ "futures-util", "pin-project-lite", @@ -1906,9 +1905,9 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e62db736db19c488966c8d787f52e6270be565727236fd5579eaa301e7bc4a" +checksum = "a395c914b1ff95db3cb7003ea4fd19432343af698a9b5028a7d35f8e712240a1" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1939,18 +1938,18 @@ dependencies = [ [[package]] name = "aws-smithy-observability" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1fcbefc7ece1d70dcce29e490f269695dfca2d2bacdeaf9e5c3f799e4e6a42" +checksum = "7764bc1dfdb71157bc481528a649e617ed8c9c8aa93c0e8b01087133677cfc8e" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-query" -version = "0.60.9" +version = "0.60.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae5d689cf437eae90460e944a58b5668530d433b4ff85789e69d2f2a556e057d" +checksum = "786bbf4434ed3e3413c5a1741abf53a0372dd48124ddb2091e6bff1b1b2582b5" dependencies = [ "aws-smithy-types", "urlencoding", @@ -1982,9 +1981,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efce7aaaf59ad53c5412f14fc19b2d5c6ab2c3ec688d272fd31f76ec12f44fb0" +checksum = "8b743e9aab0b8d50a9a40eebedf974fcfe3621032e07c6388d1c7821b155b7b0" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1999,9 +1998,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.6" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f172bcb02424eb94425db8aed1b6d583b5104d4d5ddddf22402c661a320048" +checksum = "0828575b70da70406b4cdb5d4afe0afe725f72245f04d34f02e0fb5ebd6fc872" dependencies = [ "base64-simd", "bytes", @@ -2478,9 +2477,9 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] @@ -2504,9 +2503,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -2641,9 +2640,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.54" +version = "1.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ "find-msvc-tools", "jobserver", @@ -2757,9 +2756,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" dependencies = [ "clap_builder", "clap_derive", @@ -2777,9 +2776,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" dependencies = [ "anstream", "anstyle", @@ -2811,9 +2810,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", @@ -2842,9 +2841,9 @@ dependencies = [ [[package]] name = "cliclack" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2381872509dfa50d8b92b92a5da8367ba68458ab9494be4134b57ad6ca26295f" +checksum = "aa510b739c618c679375ea9c5af44ce9f591289546e874ad5910e7ce7df79844" dependencies = [ "console 0.15.11", "indicatif", @@ -3562,6 +3561,12 @@ 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" @@ -4107,9 +4112,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed-hash" @@ -4222,6 +4227,7 @@ dependencies = [ "path-slash", "proptest", "quick-junit", + "rand 0.9.2", "rayon", "regex", "reqwest", @@ -4540,6 +4546,7 @@ dependencies = [ "dunce", "eyre", "foundry-block-explorers", + "foundry-cli-markdown", "foundry-common", "foundry-compilers", "foundry-config", @@ -4567,6 +4574,14 @@ dependencies = [ "yansi", ] +[[package]] +name = "foundry-cli-markdown" +version = "1.6.0" +dependencies = [ + "clap", + "pretty_assertions", +] + [[package]] name = "foundry-common" version = "1.6.0" @@ -4602,6 +4617,7 @@ dependencies = [ "foundry-block-explorers", "foundry-common-fmt", "foundry-compilers", + "foundry-config", "itertools 0.14.0", "jiff", "num-format", @@ -5728,9 +5744,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -6269,9 +6285,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -6875,9 +6891,12 @@ dependencies = [ [[package]] name = "notify-types" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.10.0", +] [[package]] name = "nu-ansi-term" @@ -7630,15 +7649,15 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] @@ -7699,6 +7718,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "prettydiff" version = "0.9.0" @@ -8605,9 +8634,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.34.0" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1ce3f52a052d78cc251714d57bf05dc8bc75e269677de11805d3153300a2cd" +checksum = "6e435414e9de50a1b930da602067c76365fea2fea11e80ceb50783c94ddd127f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -9423,9 +9452,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" dependencies = [ "cc", "cfg-if", @@ -9533,15 +9562,15 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "small_btree" @@ -9678,7 +9707,7 @@ name = "solar-interface" version = "0.1.8" source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ - "annotate-snippets 0.12.10", + "annotate-snippets 0.12.11", "anstream", "anstyle", "derive_more", @@ -10058,9 +10087,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" +checksum = "2379beea9476b89d0237078be761cf8e012d92d5ae4ae0c9a329f974838870fc" dependencies = [ "paste", "proc-macro2", @@ -10332,15 +10361,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -11937,18 +11957,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0" dependencies = [ "proc-macro2", "quote", @@ -12063,9 +12083,9 @@ checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "1966f8ac2c1f76987d69a74d0e0f929241c10e78136434e3be70ff7f58f64214" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index 870d36bc0787d..06a320a24b358 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "crates/primitives/", "crates/script-sequence/", "crates/test-utils/", + "crates/cli-markdown/", ] resolver = "2" @@ -255,6 +256,7 @@ forge-script-sequence = { path = "crates/script-sequence" } foundry-cheatcodes = { path = "crates/cheatcodes" } foundry-cheatcodes-spec = { path = "crates/cheatcodes/spec" } foundry-cli = { path = "crates/cli" } +foundry-cli-markdown = { path = "crates/cli-markdown" } foundry-common = { path = "crates/common" } foundry-common-fmt = { path = "crates/common/fmt" } foundry-config = { path = "crates/config" } @@ -343,7 +345,7 @@ alloy-op-evm = "0.26.3" # revm revm = { version = "34.0.0", default-features = false } -revm-inspectors = { version = "0.34.0", features = ["serde"] } +revm-inspectors = { version = "0.34.2", features = ["serde"] } op-revm = { version = "15.0.0", default-features = false } ## cli diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index fb4b774f775f9..ee28f59e67c9b 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -1,5 +1,5 @@ use crate::{eth::subscription::SubscriptionId, types::ReorgOptions}; -use alloy_primitives::{Address, B64, B256, Bytes, TxHash, U256}; +use alloy_primitives::{Address, B64, B256, Bytes, TxHash, U256, map::HashSet}; use alloy_rpc_types::{ BlockId, BlockNumberOrTag as BlockNumber, BlockOverrides, Filter, Index, anvil::{Forking, MineOptions}, @@ -10,6 +10,7 @@ use alloy_rpc_types::{ trace::{ filter::TraceFilter, geth::{GethDebugTracingCallOptions, GethDebugTracingOptions}, + parity::TraceType, }, }; use alloy_serde::WithOtherFields; @@ -325,6 +326,13 @@ pub enum EthRequest { #[serde(rename = "trace_filter", with = "sequence")] TraceFilter(TraceFilter), + /// Trace transaction endpoint for parity's `trace_replayBlockTransactions` + #[serde(rename = "trace_replayBlockTransactions")] + TraceReplayBlockTransactions( + #[serde(deserialize_with = "lenient_block_number::lenient_block_number")] BlockNumber, + HashSet, + ), + // Custom endpoints, they're not extracted to a separate type out of serde convenience /// send transactions impersonating specific account and contract addresses. #[serde( diff --git a/crates/anvil/src/args.rs b/crates/anvil/src/args.rs index 806c8bc992155..4dee7f830fa15 100644 --- a/crates/anvil/src/args.rs +++ b/crates/anvil/src/args.rs @@ -7,6 +7,8 @@ use foundry_cli::utils; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let mut args = Anvil::parse(); args.global.init()?; args.node.evm.resolve_rpc_alias(); diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 31759962111b1..017cc1ceff3c3 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -234,6 +234,7 @@ impl NodeArgs { Ok(NodeConfig::default() .with_gas_limit(self.evm.gas_limit) .disable_block_gas_limit(self.evm.disable_block_gas_limit) + .enable_tx_gas_limit(self.evm.enable_tx_gas_limit) .with_gas_price(self.evm.gas_price) .with_hardfork(hardfork) .with_blocktime(self.block_time) @@ -544,6 +545,10 @@ pub struct AnvilEvmArgs { )] pub disable_block_gas_limit: bool, + /// Enable the transaction gas limit check as imposed by EIP-7825 (Osaka hardfork). + #[arg(long, visible_alias = "tx-gas-limit", help_heading = "Environment config")] + pub enable_tx_gas_limit: bool, + /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. To /// disable entirely, use `--disable-code-size-limit`. By default, it is 0x6000 (~25kb). #[arg(long, value_name = "CODE_SIZE", help_heading = "Environment config")] @@ -895,6 +900,16 @@ mod tests { assert!(args.is_err()); } + #[test] + fn can_parse_enable_tx_gas_limit() { + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--enable-tx-gas-limit"]); + assert!(args.evm.enable_tx_gas_limit); + + // Also test the alias + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--tx-gas-limit"]); + assert!(args.evm.enable_tx_gas_limit); + } + #[test] fn can_parse_disable_code_size_limit() { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--disable-code-size-limit"]); diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 648cdeed0256b..57e08dd54fc87 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -14,7 +14,6 @@ use crate::{ }, mem::{self, in_memory_db::MemDb}, }; -use alloy_chains::Chain; use alloy_consensus::BlockHeader; use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams}; use alloy_evm::EvmEnv; @@ -1300,6 +1299,23 @@ latest block number: {latest_block}" ..Default::default() }; + // Determine chain_id early so we can use it consistently + let chain_id = if let Some(chain_id) = self.chain_id { + chain_id + } else { + let chain_id = if let Some(fork_chain_id) = fork_chain_id { + fork_chain_id.to() + } else { + provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")? + }; + + // need to update the dev signers and env with the chain id + self.set_chain_id(Some(chain_id)); + env.evm_env.cfg_env.chain_id = chain_id; + env.tx.base.chain_id = chain_id.into(); + chain_id + }; + // 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 { @@ -1320,12 +1336,7 @@ latest block number: {latest_block}" (block.header.excess_blob_gas, block.header.blob_gas_used) { // derive the blobparams that are active at this timestamp - let blob_params = get_blob_params( - fork_chain_id - .unwrap_or_else(|| U256::from(Chain::mainnet().id())) - .saturating_to(), - block.header.timestamp, - ); + let blob_params = get_blob_params(chain_id, block.header.timestamp); env.evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( blob_excess_gas, @@ -1353,21 +1364,6 @@ latest block number: {latest_block}" let block_hash = block.header.hash; - let chain_id = if let Some(chain_id) = self.chain_id { - chain_id - } else { - let chain_id = if let Some(fork_chain_id) = fork_chain_id { - fork_chain_id.to() - } else { - provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")? - }; - - // need to update the dev signers and env with the chain id - self.set_chain_id(Some(chain_id)); - env.evm_env.cfg_env.chain_id = chain_id; - env.tx.base.chain_id = chain_id.into(); - 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::( diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 7e623ea2bd986..a66f4f8d7d183 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -56,7 +56,7 @@ use alloy_rpc_types::{ trace::{ filter::TraceFilter, geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace, + parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType}, }, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, }; @@ -344,6 +344,9 @@ impl EthApi { EthRequest::TraceTransaction(tx) => self.trace_transaction(tx).await.to_rpc_result(), EthRequest::TraceBlock(block) => self.trace_block(block).await.to_rpc_result(), EthRequest::TraceFilter(filter) => self.trace_filter(filter).await.to_rpc_result(), + EthRequest::TraceReplayBlockTransactions(block, trace_types) => { + self.trace_replay_block_transactions(block, trace_types).await.to_rpc_result() + } EthRequest::ImpersonateAccount(addr) => { self.anvil_impersonate_account(addr).await.to_rpc_result() } @@ -1990,6 +1993,18 @@ impl EthApi { node_info!("trace_filter"); self.backend.trace_filter(filter).await } + + /// Replays all transactions in a block returning the requested traces for each transaction + /// + /// Handler for RPC call: `trace_replayBlockTransactions` + pub async fn trace_replay_block_transactions( + &self, + block: BlockNumber, + trace_types: HashSet, + ) -> Result> { + node_info!("trace_replayBlockTransactions"); + self.backend.trace_replay_block_transactions(block, trace_types).await + } } // == impl EthApi anvil endpoints == diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 995206fbfcc89..b7732446c8358 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -6,7 +6,7 @@ use alloy_eips::eip2930::AccessListResult; use alloy_network::{AnyRpcBlock, AnyRpcTransaction, BlockResponse, TransactionResponse}; use alloy_primitives::{ Address, B256, Bytes, StorageValue, U256, - map::{FbHashMap, HashMap}, + map::{FbHashMap, HashMap, HashSet}, }; use alloy_provider::{ Provider, @@ -19,7 +19,7 @@ use alloy_rpc_types::{ simulate::{SimulatePayload, SimulatedBlock}, trace::{ geth::{GethDebugTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace as Trace, + parity::{LocalizedTransactionTrace as Trace, TraceResultsWithTransactionHash, TraceType}, }, }; use alloy_serde::WithOtherFields; @@ -419,6 +419,16 @@ impl ClientFork { Ok(traces) } + pub async fn trace_replay_block_transactions( + &self, + number: u64, + trace_types: HashSet, + ) -> Result, TransportError> { + // Forward to upstream provider for historical blocks + let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::>()); + self.provider().raw_request("trace_replayBlockTransactions".into(), params).await + } + pub async fn transaction_receipt( &self, hash: B256, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 68de83c20bb13..78718fda4f72c 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -54,7 +54,7 @@ use alloy_network::{ }; use alloy_primitives::{ Address, B256, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom, - map::{AddressMap, HashMap}, + map::{AddressMap, HashMap, HashSet}, }; use alloy_rpc_types::{ AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, @@ -71,7 +71,7 @@ use alloy_rpc_types::{ FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame, }, - parity::LocalizedTransactionTrace, + parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType}, }, }; use alloy_serde::{OtherFields, WithOtherFields}; @@ -2141,8 +2141,7 @@ impl Backend { let from_block = self.convert_block_number(filter.block_option.get_from_block().copied()); if from_block > best { - // requested log range does not exist yet - return Ok(vec![]); + return Err(BlockchainError::BlockOutOfRange(best, from_block)); } self.logs_for_range(&filter, from_block, to_block).await @@ -2944,6 +2943,114 @@ impl Backend { Ok(vec![]) } + /// Replays all transactions in a block and returns the requested traces for each transaction + pub async fn trace_replay_block_transactions( + &self, + block: BlockNumber, + trace_types: HashSet, + ) -> Result, BlockchainError> { + let block_number = self.convert_block_number(Some(block)); + + // Try mined blocks first + if let Some(results) = + self.mined_parity_trace_replay_block_transactions(block_number, &trace_types) + { + return Ok(results); + } + + // Fallback to fork if block predates fork + if let Some(fork) = self.get_fork() + && fork.predates_fork(block_number) + { + return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?); + } + + Ok(vec![]) + } + + /// Returns the trace results for all transactions in a mined block by replaying them + fn mined_parity_trace_replay_block_transactions( + &self, + block_number: u64, + trace_types: &HashSet, + ) -> Option> { + let block = self.get_block(block_number)?; + + // Execute this in the context of the parent state + let parent_hash = block.header.parent_hash; + let trace_config = TracingInspectorConfig::from_parity_config(trace_types); + + let read_guard = self.states.upgradable_read(); + if let Some(state) = read_guard.get_state(&parent_hash) { + self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types) + } else { + let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard); + let state = write_guard.get_on_disk_state(&parent_hash)?; + self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types) + } + } + + /// Replays all transactions in a block with the tracing inspector to generate TraceResults + fn replay_block_transactions_with_inspector( + &self, + block: &Block, + parent_state: &StateDb, + trace_config: TracingInspectorConfig, + trace_types: &HashSet, + ) -> Option> { + let mut cache_db = CacheDB::new(Box::new(parent_state)); + let mut results = Vec::new(); + + // Configure the block environment + let mut env = self.env.read().clone(); + env.evm_env.block_env = BlockEnv { + number: U256::from(block.header.number), + beneficiary: block.header.beneficiary, + 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(), + gas_limit: block.header.gas_limit, + ..Default::default() + }; + + // Execute each transaction in the block with tracing + for tx_envelope in &block.body.transactions { + let tx_hash = tx_envelope.hash(); + + // Create a fresh inspector for this transaction + let mut inspector = TracingInspector::new(trace_config); + + // Prepare transaction environment + let pending_tx = + PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?; + let mut tx_env: OpTransaction = FromRecoveredTx::from_recovered_tx( + pending_tx.transaction.as_ref(), + *pending_tx.sender(), + ); + if env.networks.is_optimism() { + tx_env.enveloped_tx = Some(pending_tx.transaction.encoded_2718().into()); + } + + // Execute the transaction with the inspector + let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); + let result = evm.transact(tx_env.clone()).ok()?; + + // Build TraceResults from the inspector and execution result + let full_trace = inspector + .into_parity_builder() + .into_trace_results_with_state(&result, trace_types, &cache_db) + .ok()?; + + results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace }); + + // Commit the state changes for the next transaction + cache_db.commit(result.state); + } + + Some(results) + } + pub async fn transaction_receipt( &self, hash: B256, @@ -3083,7 +3190,8 @@ impl Backend { blob_gas_used, }; - let inner = FoundryTxReceipt::new(receipt); + // Include timestamp in receipt to avoid extra block lookups (e.g., in Otterscan API) + let inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp); Some(MinedTransactionReceipt { inner, out: info.out }) } diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 2e6fed286eb0f..3f4be8158f457 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -419,8 +419,14 @@ impl EthApi { let receipts = join_all(receipt_futs.map(|r| async { if let Ok(Some(r)) = r.await { - let block = self.block_by_number(r.block_number().unwrap().into()).await?; - let timestamp = block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp; + // Try to get timestamp from receipt's other fields first (set by mined receipts), + // fallback to block lookup for fork receipts that may not have it + let timestamp = if let Some(ts) = r.block_timestamp() { + ts + } else { + let block = self.block_by_number(r.block_number().unwrap().into()).await?; + block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp + }; let receipt = r.as_ref().inner.clone().map_inner(OtsReceipt::from); Ok(OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }) } else { diff --git a/crates/anvil/test-data/state-dump.json b/crates/anvil/test-data/state-dump.json index a60e3c861c107..e7a1e37f62809 100644 --- a/crates/anvil/test-data/state-dump.json +++ b/crates/anvil/test-data/state-dump.json @@ -1 +1,314 @@ -{"block":{"number":2,"beneficiary":"0x0000000000000000000000000000000000000000","timestamp":1724763179,"gas_limit":30000000,"basefee":875175000,"difficulty":"0x0","prevrandao":"0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":2,"blocks":[{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdcc25","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc29","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","type":"0x2"},"impersonated_sender":null}],"ommers":[]},{"header":{"parentHash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc2b","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","type":"0x2"},"impersonated_sender":null}],"ommers":[]}],"transactions":[{"info":{"transaction_hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","transaction_index":0,"from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","block_number":1},{"info":{"transaction_hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","transaction_index":0,"from":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0","block_number":2}]} \ No newline at end of file +{ + "block": { + "number": 2, + "beneficiary": "0x0000000000000000000000000000000000000000", + "timestamp": 1724763179, + "gas_limit": 30000000, + "basefee": 875175000, + "difficulty": "0x0", + "prevrandao": "0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a", + "blob_excess_gas_and_price": { + "excess_blob_gas": 0, + "blob_gasprice": 1 + } + }, + "accounts": { + "0x0000000000000000000000000000000000000000": { + "nonce": 0, + "balance": "0xa410", + "code": "0x", + "storage": {} + }, + "0x14dc79964da2c08b23698b3d3cc7ca32193d9955": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x4e59b44847b379578588920ca78fbf26c0b4956c": { + "nonce": 0, + "balance": "0x0", + "code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3", + "storage": {} + }, + "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { + "nonce": 1, + "balance": "0x21e19e0b90393da9b38", + "code": "0x", + "storage": {} + }, + "0x90f79bf6eb2c4f870365e785982e1f101e93b906": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x976ea74026e726554db657fa54763abd0c3a0aa9": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "nonce": 1, + "balance": "0x21e19e0b6a140b55df8", + "code": "0x", + "storage": {} + } + }, + "best_block_number": 2, + "blocks": [ + { + "header": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x0", + "number": "0x0", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x66cdcc25", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x3b9aca00", + "blobGasUsed": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x" + }, + "transactions": [], + "ommers": [] + }, + { + "header": { + "parentHash": "0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175", + "transactionsRoot": "0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x0", + "number": "0x1", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "timestamp": "0x66cdcc29", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x3b9aca00", + "blobGasUsed": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x" + }, + "transactions": [ + { + "transaction": { + "chainId": "0x7a69", + "nonce": "0x0", + "gas": "0x5209", + "maxFeePerGas": "0x77359401", + "maxPriorityFeePerGas": "0x1", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "value": "0x0", + "accessList": [], + "input": "0x", + "r": "0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853", + "s": "0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0", + "yParity": "0x0", + "hash": "0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3", + "type": "0x2" + }, + "impersonated_sender": null + } + ], + "ommers": [] + }, + { + "header": { + "parentHash": "0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1", + "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x0", + "number": "0x2", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "timestamp": "0x66cdcc2b", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x342a1c58", + "blobGasUsed": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x" + }, + "transactions": [ + { + "transaction": { + "chainId": "0x7a69", + "nonce": "0x0", + "gas": "0x5209", + "maxFeePerGas": "0x77359401", + "maxPriorityFeePerGas": "0x1", + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "value": "0x0", + "accessList": [], + "input": "0x", + "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0", + "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd", + "yParity": "0x0", + "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515", + "type": "0x2" + }, + "impersonated_sender": null + } + ], + "ommers": [] + } + ], + "transactions": [ + { + "info": { + "transaction_hash": "0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3", + "transaction_index": 0, + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "contract_address": null, + "traces": [ + { + "parent": null, + "children": [], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "address": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "maybe_precompile": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0x", + "output": "0x", + "gas_used": 0, + "gas_limit": 1, + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + }, + "gas_refund_counter": 0 + }, + "logs": [], + "ordering": [] + } + ], + "exit": "Stop", + "out": "0x", + "nonce": 0, + "gas_used": 21000 + }, + "receipt": { + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "block_hash": "0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70", + "block_number": 1 + }, + { + "info": { + "transaction_hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515", + "transaction_index": 0, + "from": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "contract_address": null, + "traces": [ + { + "parent": null, + "children": [], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "address": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "maybe_precompile": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0x", + "output": "0x", + "gas_used": 0, + "gas_limit": 1, + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + }, + "gas_refund_counter": 0 + }, + "logs": [], + "ordering": [] + } + ], + "exit": "Stop", + "out": "0x", + "nonce": 0, + "gas_used": 21000 + }, + "receipt": { + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "block_hash": "0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0", + "block_number": 2 + } + ] +} diff --git a/crates/anvil/tests/it/beacon_api.rs b/crates/anvil/tests/it/beacon_api.rs index 5c6c9b2486fa8..ec0f13c02e7c0 100644 --- a/crates/anvil/tests/it/beacon_api.rs +++ b/crates/anvil/tests/it/beacon_api.rs @@ -66,8 +66,7 @@ async fn test_beacon_api_get_blobs() { .with_blob_sidecar(sidecar) .value(U256::from(100)); - let mut tx = WithOtherFields::new(tx); - tx.populate_blob_hashes(); + let tx = WithOtherFields::new(tx); let pending = provider.send_transaction(tx).await.unwrap(); pending_txs.push(pending); diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 11200e5667206..ddc8c91eefda7 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -39,9 +39,7 @@ async fn can_send_eip4844_transaction() { .with_blob_sidecar(sidecar) .value(U256::from(5)); - let mut tx = WithOtherFields::new(tx); - - tx.populate_blob_hashes(); + let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -103,6 +101,29 @@ async fn can_send_eip4844_transaction_eth_send_transaction() { let _blobs = api.anvil_get_blob_by_tx_hash(tx_hash).unwrap().unwrap(); } +// +#[tokio::test(flavor = "multi_thread")] +async fn can_send_eip4844_transaction_with_eip7594_sidecar_format() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Osaka.into())); + let (api, handle) = spawn(node_config).await; + let provider = ProviderBuilder::new().connect(handle.http_endpoint().as_str()).await.unwrap(); + let accounts = provider.get_accounts().await.unwrap(); + let alice = accounts[0]; + let bob = accounts[1]; + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Blobs are fun!"); + let sidecar = sidecar.build_7594().unwrap(); + + let mut tx = TransactionRequest::default().with_from(alice).with_to(bob); + alloy_network::TransactionBuilder7594::set_blob_sidecar_7594(&mut tx, sidecar); + + let pending_tx = provider.send_transaction(tx).await.unwrap(); + let receipt = pending_tx.get_receipt().await.unwrap(); + let tx_hash = receipt.transaction_hash; + + let _blobs = api.anvil_get_blob_by_tx_hash(tx_hash).unwrap().unwrap(); +} + #[tokio::test(flavor = "multi_thread")] async fn can_send_multiple_blobs_in_one_tx() { let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); @@ -131,9 +152,7 @@ async fn can_send_multiple_blobs_in_one_tx() { .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 tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -169,9 +188,7 @@ async fn cannot_exceed_six_blobs() { .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 tx = WithOtherFields::new(tx); let err = provider.send_transaction(tx).await.unwrap_err(); @@ -211,8 +228,6 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { .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]; @@ -224,7 +239,6 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { 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; @@ -428,9 +442,7 @@ async fn can_get_blobs_by_versioned_hash() { .with_blob_sidecar(sidecar.clone()) .value(U256::from(5)); - let mut tx = WithOtherFields::new(tx); - - tx.populate_blob_hashes(); + let tx = WithOtherFields::new(tx); let _receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -466,10 +478,7 @@ async fn can_get_blobs_by_tx_hash() { .with_blob_sidecar(sidecar.clone()) .value(U256::from(5)); - let mut tx = WithOtherFields::new(tx); - - tx.populate_blob_hashes(); - + let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let hash = receipt.transaction_hash; api.anvil_set_auto_mine(true).await.unwrap(); diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index 9d35644f24949..2c677231e07ad 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -725,6 +725,7 @@ async fn test_backward_compatibility_state_dump_deserialization_v1_2() { "output": "0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80633fb5c1cb146100435780638381f58a1461005f578063d09de08a1461007d575b5f5ffd5b61005d600480360381019061005891906100e4565b610087565b005b610067610090565b604051610074919061011e565b60405180910390f35b610085610095565b005b805f8190555050565b5f5481565b5f5f8154809291906100a690610164565b9190505550565b5f5ffd5b5f819050919050565b6100c3816100b1565b81146100cd575f5ffd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101a05761019f610137565b5b60018201905091905056fea264697066735822122040b6a3cd3ec8f890002f39a8719ebee029ba9bac3d7fa9d581d4712cfe9ffec264736f6c634300081e0033", "gas_used": 96345, "gas_limit": 143385, + "gas_refund_counter": 0, "status": "Return", "steps": [], "decoded": null diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index ba6c7ba887246..fb1777aa4f9f0 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -25,7 +25,7 @@ use alloy_rpc_types::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, PreStateConfig, PreStateFrame, }, - parity::{Action, LocalizedTransactionTrace}, + parity::{Action, ChangedType, LocalizedTransactionTrace, TraceType}, }, }; use alloy_serde::WithOtherFields; @@ -1273,3 +1273,81 @@ async fn test_debug_trace_transaction_pre_state_tracer() { _ => unreachable!(), } } + +#[tokio::test(flavor = "multi_thread")] +async fn test_trace_replay_block_transactions_local() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + api.anvil_set_auto_mine(false).await.unwrap(); + + let accounts = handle.dev_wallets().collect::>(); + let from = accounts[0].address(); + let to = accounts[1].address(); + let amount = U256::from(1000000u64); + + // Send first transaction + let tx1 = TransactionRequest::default().to(to).value(amount).from(from); + let tx1 = WithOtherFields::new(tx1); + let pending_tx1 = provider.send_transaction(tx1).await.unwrap(); + + // Send second transaction with different value + let tx2 = TransactionRequest::default().to(to).value(amount).from(from); + let tx2 = WithOtherFields::new(tx2); + let pending_tx2 = provider.send_transaction(tx2).await.unwrap(); + + api.mine_one().await; + let receipt1 = pending_tx1.get_receipt().await.unwrap(); + let receipt2 = pending_tx2.get_receipt().await.unwrap(); + + let block_number = receipt2.block_number.unwrap(); + + // Replay the block transactions with call trace type + // Pass block number as hex string as per Ethereum RPC spec + let results = api + .trace_replay_block_transactions( + block_number.into(), + vec![TraceType::Trace, TraceType::VmTrace, TraceType::StateDiff].into_iter().collect(), + ) + .await + .unwrap(); + + // Verify we have traces for both transactions + assert_eq!(results.len(), 2, "Should have traces for 2 transactions"); + + // Verify first transaction hash matches + assert_eq!(results[0].transaction_hash, receipt1.transaction_hash); + + // Verify second transaction hash matches + assert_eq!(results[1].transaction_hash, receipt2.transaction_hash); + + // Verify trace types are present and accurate + for result in results { + let full_trace = &result.full_trace; + + // Verify Trace (call trace) is present and accurate + assert!(!full_trace.trace.is_empty(), "Trace should not be empty"); + let first_trace = &full_trace.trace[0]; + match &first_trace.action { + Action::Call(call) => { + assert_eq!(call.from, from, "Call from address should match"); + assert_eq!(call.to, to, "Call to address should match"); + } + _ => panic!("Expected Call action, got {:?}", first_trace.action), + } + + // Verify VmTrace is present + assert!(full_trace.vm_trace.is_some(), "VmTrace should be present when requested"); + + // Verify StateDiff is present + assert!(full_trace.state_diff.is_some(), "StateDiff should be present when requested"); + // Verify balance change is correct in state diff + let ChangedType:: { from, to } = + full_trace.state_diff.as_ref().unwrap().get(&to).unwrap().balance.as_changed().unwrap(); + assert_eq!( + to.checked_sub(*from).unwrap(), + amount, + "Incorrect balance change in state diff" + ); + } +} diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index cf4966a86c15b..2f30b89e07216 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -33,6 +33,8 @@ use std::time::Instant; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let args = CastArgs::parse(); args.global.init()?; args.global.tokio_runtime().block_on(run_command(args)) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index cf6c585bd1bdd..b24af1db60e84 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -2336,7 +2336,7 @@ fn explorer_client( api_url: Option, explorer_url: Option, ) -> Result { - let mut builder = Client::builder().chain(chain)?; + let mut builder = Client::builder(); let deduced = chain.etherscan_urls(); diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 64ff8da391200..d38d27fec63dc 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -682,8 +682,6 @@ where self.tx.set_blob_sidecar_7594(sidecar); } - self.tx.populate_blob_hashes(); - Ok(self) } } diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index ea3a003a7ffc4..d32712219000e 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -3,6 +3,7 @@ use crate::{ json::json_value_to_token, }; use alloy_dyn_abi::DynSolValue; +use alloy_network::AnyNetwork; use alloy_primitives::{B256, U256}; use alloy_provider::Provider; use alloy_rpc_types::Filter; @@ -238,7 +239,7 @@ impl Cheatcode for eth_getLogsCall { .database .active_fork_url() .ok_or_else(|| fmt_err!("no active fork URL found"))?; - let provider = ProviderBuilder::new(&url).build()?; + 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() { filter.topics[i] = topic.into(); @@ -275,7 +276,7 @@ impl Cheatcode for getRawBlockHeaderCall { .database .active_fork_url() .ok_or_else(|| fmt_err!("no active fork"))?; - let provider = ProviderBuilder::new(&url).build()?; + let provider = ProviderBuilder::::new(&url).build()?; let block_number = u64::try_from(blockNumber) .map_err(|_| fmt_err!("block number must be less than 2^64"))?; let block = @@ -397,7 +398,7 @@ fn persist_caller(ccx: &mut CheatsCtxt) { /// Performs an Ethereum JSON-RPC request to the given endpoint. fn rpc_call(url: &str, method: &str, params: &str) -> Result { - let provider = ProviderBuilder::new(url).build()?; + let provider = ProviderBuilder::::new(url).build()?; let params_json: serde_json::Value = serde_json::from_str(params)?; let result = foundry_common::block_on(provider.raw_request(method.to_string().into(), params_json)) diff --git a/crates/chisel/src/args.rs b/crates/chisel/src/args.rs index e3af6d5c91203..8200f4fc44f63 100644 --- a/crates/chisel/src/args.rs +++ b/crates/chisel/src/args.rs @@ -14,6 +14,8 @@ use yansi::Paint; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let args = Chisel::parse(); args.global.init()?; args.global.tokio_runtime().block_on(run_command(args)) diff --git a/crates/cli-markdown/Cargo.toml b/crates/cli-markdown/Cargo.toml new file mode 100644 index 0000000000000..7f039685d2cf1 --- /dev/null +++ b/crates/cli-markdown/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "foundry-cli-markdown" +description = "Generate Markdown documentation for clap CLIs" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +clap = { version = "4", features = ["env"] } + +[dev-dependencies] +clap = { version = "4", features = ["derive"] } +pretty_assertions = "1" diff --git a/crates/cli-markdown/src/lib.rs b/crates/cli-markdown/src/lib.rs new file mode 100644 index 0000000000000..732e22bfd193d --- /dev/null +++ b/crates/cli-markdown/src/lib.rs @@ -0,0 +1,582 @@ +//! Generate Markdown documentation for clap command-line tools. +//! +//! This is a fork of [`clap-markdown`](https://crates.io/crates/clap-markdown) with the following +//! enhancements: +//! - Support for grouped options by help heading ([PR #48](https://github.com/ConnorGray/clap-markdown/pull/48)) +//! - Show environment variable names for arguments ([PR #50](https://github.com/ConnorGray/clap-markdown/pull/50)) +//! - Add version information to generated Markdown ([PR #52](https://github.com/ConnorGray/clap-markdown/pull/52)) + +use std::{ + collections::BTreeMap, + fmt::{self, Write}, +}; + +use clap::builder::PossibleValue; + +/// Options to customize the structure of the output Markdown document. +#[non_exhaustive] +pub struct MarkdownOptions { + title: Option, + show_footer: bool, + show_table_of_contents: bool, + show_aliases: bool, +} + +impl MarkdownOptions { + /// Construct a default instance of `MarkdownOptions`. + pub fn new() -> Self { + Self { title: None, show_footer: true, show_table_of_contents: true, show_aliases: true } + } + + /// Set a custom title to use in the generated document. + pub fn title(mut self, title: String) -> Self { + self.title = Some(title); + self + } + + /// Whether to show the default footer advertising `clap-markdown`. + pub fn show_footer(mut self, show: bool) -> Self { + self.show_footer = show; + self + } + + /// Whether to show the default table of contents. + pub fn show_table_of_contents(mut self, show: bool) -> Self { + self.show_table_of_contents = show; + self + } + + /// Whether to show aliases for arguments and commands. + pub fn show_aliases(mut self, show: bool) -> Self { + self.show_aliases = show; + self + } +} + +impl Default for MarkdownOptions { + fn default() -> Self { + Self::new() + } +} + +/// Format the help information for `command` as Markdown. +pub fn help_markdown() -> String { + let command = C::command(); + help_markdown_command(&command) +} + +/// Format the help information for `command` as Markdown, with custom options. +pub fn help_markdown_custom(options: &MarkdownOptions) -> String { + let command = C::command(); + help_markdown_command_custom(&command, options) +} + +/// Format the help information for `command` as Markdown. +pub fn help_markdown_command(command: &clap::Command) -> String { + help_markdown_command_custom(command, &Default::default()) +} + +/// Format the help information for `command` as Markdown, with custom options. +pub fn help_markdown_command_custom(command: &clap::Command, options: &MarkdownOptions) -> String { + let mut buffer = String::with_capacity(100); + write_help_markdown(&mut buffer, command, options); + buffer +} + +/// Format the help information for `command` as Markdown and print it. +/// +/// Output is printed to the standard output. +#[allow(clippy::disallowed_macros)] +pub fn print_help_markdown() { + let command = C::command(); + let mut buffer = String::with_capacity(100); + write_help_markdown(&mut buffer, &command, &Default::default()); + println!("{buffer}"); +} + +fn write_help_markdown(buffer: &mut String, command: &clap::Command, options: &MarkdownOptions) { + let title_name = get_canonical_name(command); + + let title = match options.title { + Some(ref title) => title.to_owned(), + None => format!("Command-Line Help for `{title_name}`"), + }; + writeln!(buffer, "# {title}\n",).unwrap(); + + writeln!( + buffer, + "This document contains the help content for the `{title_name}` command-line program.\n", + ) + .unwrap(); + + // Write the version if available (PR #52) + if let Some(version) = command.get_version() { + let version_str = version.to_string(); + + if version_str.contains('\n') { + // Multi-line version: use a code block + writeln!(buffer, "**Version:**\n\n```\n{}\n```\n", version_str.trim()).unwrap(); + } else { + // Single-line version: use inline code + writeln!(buffer, "**Version:** `{version_str}`\n").unwrap(); + } + } + + // Write the table of contents + if options.show_table_of_contents { + writeln!(buffer, "**Command Overview:**\n").unwrap(); + build_table_of_contents_markdown(buffer, Vec::new(), command, 0).unwrap(); + writeln!(buffer).unwrap(); + } + + // Write the commands/subcommands sections + build_command_markdown(buffer, Vec::new(), command, 0, options).unwrap(); + + // Write the footer + if options.show_footer { + write!( + buffer, + r#"
+ + + This document was generated automatically by + clap-markdown. + +"# + ) + .unwrap(); + } +} + +fn build_table_of_contents_markdown( + buffer: &mut String, + parent_command_path: Vec, + command: &clap::Command, + _depth: usize, +) -> std::fmt::Result { + // Don't document commands marked with `clap(hide = true)` + if command.is_hide_set() { + return Ok(()); + } + + let title_name = get_canonical_name(command); + + let command_path = { + let mut command_path = parent_command_path; + command_path.push(title_name); + command_path + }; + + writeln!(buffer, "* [`{}`↴](#{})", command_path.join(" "), command_path.join("-"),)?; + + for subcommand in command.get_subcommands() { + build_table_of_contents_markdown(buffer, command_path.clone(), subcommand, _depth + 1)?; + } + + Ok(()) +} + +fn build_command_markdown( + buffer: &mut String, + parent_command_path: Vec, + command: &clap::Command, + _depth: usize, + options: &MarkdownOptions, +) -> std::fmt::Result { + // Don't document commands marked with `clap(hide = true)` + if command.is_hide_set() { + return Ok(()); + } + + let title_name = get_canonical_name(command); + + let command_path = { + let mut command_path = parent_command_path.clone(); + command_path.push(title_name); + command_path + }; + + // Write the markdown heading + writeln!(buffer, "## `{}`\n", command_path.join(" "))?; + + if let Some(long_about) = command.get_long_about() { + writeln!(buffer, "{long_about}\n")?; + } else if let Some(about) = command.get_about() { + writeln!(buffer, "{about}\n")?; + } + + if let Some(help) = command.get_before_long_help() { + writeln!(buffer, "{help}\n")?; + } else if let Some(help) = command.get_before_help() { + writeln!(buffer, "{help}\n")?; + } + + writeln!( + buffer, + "**Usage:** `{}{}`\n", + if parent_command_path.is_empty() { + String::new() + } else { + let mut s = parent_command_path.join(" "); + s.push(' '); + s + }, + command.clone().render_usage().to_string().replace("Usage: ", "") + )?; + + if options.show_aliases { + let aliases = command.get_visible_aliases().collect::>(); + if let Some(aliases_str) = get_alias_string(&aliases) { + writeln!( + buffer, + "**{}:** {aliases_str}\n", + pluralize(aliases.len(), "Command Alias", "Command Aliases") + )?; + } + } + + if let Some(help) = command.get_after_long_help() { + writeln!(buffer, "{help}\n")?; + } else if let Some(help) = command.get_after_help() { + writeln!(buffer, "{help}\n")?; + } + + // Subcommands + if command.get_subcommands().next().is_some() { + writeln!(buffer, "###### **Subcommands:**\n")?; + + for subcommand in command.get_subcommands() { + if subcommand.is_hide_set() { + continue; + } + + let title_name = get_canonical_name(subcommand); + let about = match subcommand.get_about() { + Some(about) => about.to_string(), + None => String::new(), + }; + + writeln!(buffer, "* `{title_name}` — {about}",)?; + } + + writeln!(buffer)?; + } + + // Arguments (positional) + if command.get_positionals().next().is_some() { + writeln!(buffer, "###### **Arguments:**\n")?; + + for pos_arg in command.get_positionals() { + write_arg_markdown(buffer, pos_arg)?; + } + + writeln!(buffer)?; + } + + // Options (grouped by help heading) - PR #48 + let non_pos: Vec<_> = + command.get_arguments().filter(|arg| !arg.is_positional() && !arg.is_hide_set()).collect(); + + if !non_pos.is_empty() { + // Group arguments by help heading + let mut grouped_args: BTreeMap<&str, Vec<&clap::Arg>> = BTreeMap::new(); + + for arg in non_pos { + let heading = arg.get_help_heading().unwrap_or("Options"); + grouped_args.entry(heading).or_default().push(arg); + } + + // Write each group with its heading + for (heading, args) in grouped_args { + writeln!(buffer, "###### **{heading}:**\n")?; + + for arg in args { + write_arg_markdown(buffer, arg)?; + } + + writeln!(buffer)?; + } + } + + // Include extra space between commands + write!(buffer, "\n\n")?; + + for subcommand in command.get_subcommands() { + build_command_markdown(buffer, command_path.clone(), subcommand, _depth + 1, options)?; + } + + Ok(()) +} + +fn write_arg_markdown(buffer: &mut String, arg: &clap::Arg) -> fmt::Result { + // Markdown list item + write!(buffer, "* ")?; + + let value_name: String = match arg.get_value_names() { + Some([name, ..]) => name.as_str().to_owned(), + Some([]) => unreachable!("clap Arg::get_value_names() returned Some(..) of empty list"), + None => arg.get_id().to_string().to_ascii_uppercase(), + }; + + match (arg.get_short(), arg.get_long()) { + (Some(short), Some(long)) => { + if arg.get_action().takes_values() { + write!(buffer, "`-{short}`, `--{long} <{value_name}>`")? + } else { + write!(buffer, "`-{short}`, `--{long}`")? + } + } + (Some(short), None) => { + if arg.get_action().takes_values() { + write!(buffer, "`-{short} <{value_name}>`")? + } else { + write!(buffer, "`-{short}`")? + } + } + (None, Some(long)) => { + if arg.get_action().takes_values() { + write!(buffer, "`--{long} <{value_name}>`")? + } else { + write!(buffer, "`--{long}`")? + } + } + (None, None) => { + debug_assert!( + arg.is_positional(), + "unexpected non-positional Arg with neither short nor long name: {arg:?}" + ); + write!(buffer, "`<{value_name}>`",)?; + } + } + + if let Some(aliases) = arg.get_visible_aliases().as_deref() + && let Some(aliases_str) = get_alias_string(aliases) + { + write!(buffer, " [{}: {aliases_str}]", pluralize(aliases.len(), "alias", "aliases"))?; + } + + if let Some(help) = arg.get_long_help() { + buffer.push_str(&indent(&help.to_string(), " — ", " ")) + } else if let Some(short_help) = arg.get_help() { + writeln!(buffer, " — {short_help}")?; + } else { + writeln!(buffer)?; + } + + // Arg default values + if !arg.get_default_values().is_empty() { + let default_values: String = arg + .get_default_values() + .iter() + .map(|value| format!("`{}`", value.to_string_lossy())) + .collect::>() + .join(", "); + + if arg.get_default_values().len() > 1 { + writeln!(buffer, "\n Default values: {default_values}")?; + } else { + writeln!(buffer, "\n Default value: {default_values}")?; + } + } + + // Arg possible values + let possible_values: Vec = + arg.get_possible_values().into_iter().filter(|pv| !pv.is_hide_set()).collect(); + + if !possible_values.is_empty() && !matches!(arg.get_action(), clap::ArgAction::SetTrue) { + let any_have_help: bool = possible_values.iter().any(|pv| pv.get_help().is_some()); + + if any_have_help { + let text: String = possible_values + .iter() + .map(|pv| match pv.get_help() { + Some(help) => { + format!(" - `{}`:\n {}\n", pv.get_name(), help) + } + None => format!(" - `{}`\n", pv.get_name()), + }) + .collect::>() + .join(""); + + writeln!(buffer, "\n Possible values:\n{text}")?; + } else { + let text: String = possible_values + .iter() + .map(|pv| format!("`{}`", pv.get_name())) + .collect::>() + .join(", "); + + writeln!(buffer, "\n Possible values: {text}\n")?; + } + } + + // Arg environment variable (PR #50) + if !arg.is_hide_env_set() + && let Some(env) = arg.get_env() + { + writeln!(buffer, "\n Environment variable: `{}`", env.to_string_lossy())?; + } + + Ok(()) +} + +/// Utility function to get the canonical name of a command. +fn get_canonical_name(command: &clap::Command) -> String { + command + .get_display_name() + .or_else(|| command.get_bin_name()) + .map(|name| name.to_owned()) + .unwrap_or_else(|| command.get_name().to_owned()) +} + +/// Indents non-empty lines. The output always ends with a newline. +fn indent(s: &str, first: &str, rest: &str) -> String { + if s.is_empty() { + return "\n".to_string(); + } + let mut result = String::new(); + let mut first_line = true; + + for line in s.lines() { + if !line.is_empty() { + result.push_str(if first_line { first } else { rest }); + result.push_str(line); + first_line = false; + } + result.push('\n'); + } + result +} + +fn get_alias_string(aliases: &[&str]) -> Option { + if aliases.is_empty() { + return None; + } + + Some(aliases.iter().map(|alias| format!("`{alias}`")).collect::>().join(", ")) +} + +fn pluralize<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str { + if count == 1 { singular } else { plural } +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::{Arg, Command}; + use pretty_assertions::assert_eq; + + #[test] + fn test_indent() { + assert_eq!(&indent("Header\n\nMore info", "___", "~~~~"), "___Header\n\n~~~~More info\n"); + assert_eq!( + &indent("Header\n\nMore info\n", "___", "~~~~"), + &indent("Header\n\nMore info", "___", "~~~~"), + ); + assert_eq!(&indent("", "___", "~~~~"), "\n"); + assert_eq!(&indent("\n", "___", "~~~~"), "\n"); + } + + #[test] + fn test_version_output() { + let app = Command::new("test-app").version("1.2.3").about("A test application"); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("**Version:** `1.2.3`"), "Should contain version"); + } + + #[test] + fn test_multiline_version() { + let multi_line_version = "my-cli 1.2.3 (abc123)\nmy-lib 2.0.0 (789xyz)"; + + let app = Command::new("my-cli").version(multi_line_version).about("Multi-version CLI"); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("**Version:**\n\n```"), "Should use code block for multi-line"); + } + + #[test] + fn test_env_var_output() { + let app = Command::new("env-test").about("Test env var output").arg( + Arg::new("config") + .short('c') + .long("config") + .env("CONFIG_PATH") + .help("Path to config file"), + ); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!( + markdown.contains("Environment variable: `CONFIG_PATH`"), + "Should show env var. Output: {markdown}" + ); + } + + #[test] + fn test_grouped_options() { + let app = Command::new("grouped-app") + .about("Test app with grouped options") + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .help("Enable verbose output") + .help_heading("General Options") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("input") + .short('i') + .long("input") + .help("Input file") + .help_heading("File Options") + .value_name("FILE"), + ) + .arg( + Arg::new("format") + .short('f') + .long("format") + .help("Output format") + .value_name("FORMAT"), + ); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("###### **File Options:**"), "Should have File Options heading"); + assert!( + markdown.contains("###### **General Options:**"), + "Should have General Options heading" + ); + assert!(markdown.contains("###### **Options:**"), "Should have default Options heading"); + } + + #[test] + fn test_no_grouped_options_backward_compatibility() { + let app = Command::new("simple-app") + .about("Test app without grouped options") + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .help("Enable verbose output") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("output").short('o').long("output").help("Output file").value_name("FILE"), + ); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("###### **Options:**"), "Should have default Options heading"); + assert!(markdown.contains("`-v`, `--verbose`"), "Should have verbose option"); + assert!(markdown.contains("`-o`, `--output `"), "Should have output option"); + } +} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index d9acf1de83d09..e1ec43c33df19 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] foundry-block-explorers.workspace = true +foundry-cli-markdown.workspace = true foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true diff --git a/crates/cli/src/handler.rs b/crates/cli/src/handler.rs index 020b7e953068f..763466d21185d 100644 --- a/crates/cli/src/handler.rs +++ b/crates/cli/src/handler.rs @@ -1,5 +1,4 @@ use eyre::EyreHandler; -use itertools::Itertools; use std::{error::Error, fmt}; /// A custom context type for Foundry specific error reporting via `eyre`. @@ -23,8 +22,7 @@ impl Handler { impl EyreHandler for Handler { fn display(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { - use fmt::Display; - foundry_common::errors::dedup_chain(error).into_iter().format("; ").fmt(f) + f.write_str(&foundry_common::errors::display_chain(error)) } fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/crates/cli/src/opts/global.rs b/crates/cli/src/opts/global.rs index 8bfc13c00001f..fee75a5d34e87 100644 --- a/crates/cli/src/opts/global.rs +++ b/crates/cli/src/opts/global.rs @@ -51,6 +51,17 @@ pub struct GlobalArgs { } impl GlobalArgs { + /// Check if `--markdown-help` was passed and print CLI reference as Markdown, then exit. + /// + /// This must be called **before** parsing arguments, since commands with required + /// subcommands would fail parsing before the flag is checked. + pub fn check_markdown_help() { + if std::env::args().any(|arg| arg == "--markdown-help") { + foundry_cli_markdown::print_help_markdown::(); + std::process::exit(0); + } + } + /// Initialize the global options. pub fn init(&self) -> eyre::Result<()> { // Set the global shell. diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f127010674aaf..dff85f9457fa7 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -115,33 +115,8 @@ pub fn get_provider_with_curl(config: &Config, curl_mode: bool) -> Result Result { - let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = ProviderBuilder::new(url.as_ref()); - - builder = builder.accept_invalid_certs(config.eth_rpc_accept_invalid_certs); - builder = builder.curl_mode(curl_mode); - - if let Ok(chain) = config.chain.unwrap_or_default().try_into() { - builder = builder.chain(chain); - } - - if let Some(jwt) = config.get_rpc_jwt_secret()? { - builder = builder.jwt(jwt.as_ref()); - } - - if let Some(rpc_timeout) = config.eth_rpc_timeout { - builder = builder.timeout(Duration::from_secs(rpc_timeout)); - } - - if let Some(rpc_headers) = config.eth_rpc_headers.clone() { - builder = builder.headers(rpc_headers); - } - - Ok(builder) + ProviderBuilder::from_config(config).map(|builder| builder.curl_mode(curl_mode)) } pub async fn get_chain

(chain: Option, provider: P) -> Result diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 154bbc5cc53ca..75776743d1132 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -16,6 +16,7 @@ workspace = true foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-common-fmt.workspace = true foundry-compilers.workspace = true +foundry-config.workspace = true alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 5d4c4657362e4..a098c33a34827 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -8,16 +8,19 @@ use crate::{ provider::{curl_transport::CurlTransport, runtime_transport::RuntimeTransportBuilder}, }; use alloy_chains::NamedChain; +use alloy_network::{Network, NetworkWallet}; use alloy_provider::{ Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, - fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, + fillers::{FillProvider, JoinFill, RecommendedFillers, WalletFiller}, network::{AnyNetwork, EthereumWallet}, }; use alloy_rpc_client::ClientBuilder; use alloy_transport::{layers::RetryBackoffLayer, utils::guess_local_url}; use eyre::{Result, WrapErr}; +use foundry_config::Config; use reqwest::Url; use std::{ + marker::PhantomData, net::SocketAddr, path::{Path, PathBuf}, str::FromStr, @@ -36,20 +39,8 @@ const POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR: f32 = 0.6; pub type RetryProvider = RootProvider; /// Helper type alias for a retry provider with a signer -pub type RetryProviderWithSigner = FillProvider< - JoinFill< - JoinFill< - Identity, - JoinFill< - GasFiller, - JoinFill< - alloy_provider::fillers::BlobGasFiller, - JoinFill, - >, - >, - >, - WalletFiller, - >, +pub type RetryProviderWithSigner = FillProvider< + JoinFill::RecommendedFillers>, WalletFiller>, RootProvider, N, >; @@ -84,8 +75,10 @@ pub fn try_get_http_provider(builder: impl AsRef) -> Result } /// Helper type to construct a `RetryProvider` +/// +/// This builder is generic over the network type `N`, defaulting to `AnyNetwork`. #[derive(Debug)] -pub struct ProviderBuilder { +pub struct ProviderBuilder { // Note: this is a result, so we can easily chain builder calls url: Result, chain: NamedChain, @@ -104,10 +97,12 @@ pub struct ProviderBuilder { no_proxy: bool, /// Whether to output curl commands instead of making requests. curl_mode: bool, + /// Phantom data for the network type. + _network: PhantomData, } -impl ProviderBuilder { - /// Creates a new builder instance +impl ProviderBuilder { + /// Creates a new ProviderBuilder helper instance. pub fn new(url_str: &str) -> Self { // a copy is needed for the next lines to work let mut url_str = url_str; @@ -156,9 +151,38 @@ impl ProviderBuilder { accept_invalid_certs: false, no_proxy: false, curl_mode: false, + _network: PhantomData, } } + /// Constructs a [ProviderBuilder] instantiated using [Config] values. + /// + /// Defaults to `http://localhost:8545` and `Mainnet`. + pub fn from_config(config: &Config) -> Result { + let url = config.get_rpc_url_or_localhost_http()?; + let mut builder = Self::new(url.as_ref()); + + builder = builder.accept_invalid_certs(config.eth_rpc_accept_invalid_certs); + + if let Ok(chain) = config.chain.unwrap_or_default().try_into() { + builder = builder.chain(chain); + } + + if let Some(jwt) = config.get_rpc_jwt_secret()? { + builder = builder.jwt(jwt.as_ref()); + } + + if let Some(rpc_timeout) = config.eth_rpc_timeout { + builder = builder.timeout(Duration::from_secs(rpc_timeout)); + } + + if let Some(rpc_headers) = config.eth_rpc_headers.clone() { + builder = builder.headers(rpc_headers); + } + + Ok(builder) + } + /// Enables a request timeout. /// /// The timeout is applied from when the request starts connecting until the @@ -278,7 +302,7 @@ impl ProviderBuilder { } /// Constructs the `RetryProvider` taking all configs into account. - pub fn build(self) -> Result { + pub fn build(self) -> Result> { let Self { url, chain, @@ -292,6 +316,7 @@ impl ProviderBuilder { accept_invalid_certs, no_proxy, curl_mode, + .. } = self; let url = url?; @@ -303,7 +328,7 @@ impl ProviderBuilder { let transport = CurlTransport::new(url).with_headers(headers).with_jwt(jwt); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + let provider = AlloyProviderBuilder::<_, _, N>::default() .connect_provider(RootProvider::new(client)); return Ok(provider); @@ -330,14 +355,22 @@ impl ProviderBuilder { ); } - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() - .connect_provider(RootProvider::new(client)); + let provider = + AlloyProviderBuilder::<_, _, N>::default().connect_provider(RootProvider::new(client)); Ok(provider) } +} +impl ProviderBuilder { /// Constructs the `RetryProvider` with a wallet. - pub fn build_with_wallet(self, wallet: EthereumWallet) -> Result { + pub fn build_with_wallet + Clone>( + self, + wallet: W, + ) -> Result> + where + N: RecommendedFillers, + { let Self { url, chain, @@ -351,6 +384,7 @@ impl ProviderBuilder { accept_invalid_certs, no_proxy, curl_mode, + .. } = self; let url = url?; @@ -362,7 +396,7 @@ impl ProviderBuilder { let transport = CurlTransport::new(url).with_headers(headers).with_jwt(jwt); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + let provider = AlloyProviderBuilder::<_, _, N>::default() .with_recommended_fillers() .wallet(wallet) .connect_provider(RootProvider::new(client)); @@ -392,7 +426,7 @@ impl ProviderBuilder { ); } - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + let provider = AlloyProviderBuilder::<_, _, N>::default() .with_recommended_fillers() .wallet(wallet) .connect_provider(RootProvider::new(client)); @@ -426,7 +460,7 @@ mod tests { #[test] fn can_auto_correct_missing_prefix() { - let builder = ProviderBuilder::new("localhost:8545"); + let builder = ProviderBuilder::::new("localhost:8545"); assert!(builder.url.is_ok()); let url = builder.url.unwrap(); diff --git a/crates/config/src/lint.rs b/crates/config/src/lint.rs index c726e3ecc8f5b..25094ead98cf8 100644 --- a/crates/config/src/lint.rs +++ b/crates/config/src/lint.rs @@ -28,8 +28,8 @@ pub struct LinterConfig { /// Configurable patterns that should be excluded when performing `mixedCase` lint checks. /// - /// Default's to ["ERC", "URI"] to allow common names like `rescueERC20`, `ERC721TokenReceiver` - /// or `tokenURI`. + /// Defaults to common abbreviations: `ERC`, `URI`, `ID`, `URL`, `API`, `JSON`, `XML`, `HTML`, + /// `HTTP`, `HTTPS`. This allows names like `marketID`, `tokenURI`, `apiURL`, `parseJSON`, etc. pub mixed_case_exceptions: Vec, } @@ -40,7 +40,18 @@ impl Default for LinterConfig { severity: vec![Severity::High, Severity::Med, Severity::Low], exclude_lints: Vec::new(), ignore: Vec::new(), - mixed_case_exceptions: vec!["ERC".to_string(), "URI".to_string()], + mixed_case_exceptions: vec![ + "ERC".to_string(), + "URI".to_string(), + "ID".to_string(), + "URL".to_string(), + "API".to_string(), + "JSON".to_string(), + "XML".to_string(), + "HTML".to_string(), + "HTTP".to_string(), + "HTTPS".to_string(), + ], } } } diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index 52e751cd2ca03..624dd5a35f81b 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -213,7 +213,7 @@ where deserialize_u64_or_max(deserializer)?.try_into().map_err(D::Error::custom) } -/// Deserialize into `U256` from either a `u64` or a `U256` hex string. +/// Deserialize into `U256` from either a `u64`, a `U256` hex string, or a decimal string. pub fn deserialize_u64_to_u256<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -223,11 +223,16 @@ where enum NumericValue { U256(U256), U64(u64), + String(String), } match NumericValue::deserialize(deserializer)? { NumericValue::U64(n) => Ok(U256::from(n)), NumericValue::U256(n) => Ok(n), + NumericValue::String(s) => { + // Handle decimal strings (e.g., "18446744073709551615") + U256::from_str(&s).map_err(D::Error::custom) + } } } diff --git a/crates/debugger/src/node.rs b/crates/debugger/src/node.rs index f7b18a978a4f4..e74ae58d66323 100644 --- a/crates/debugger/src/node.rs +++ b/crates/debugger/src/node.rs @@ -14,6 +14,8 @@ pub struct DebugNode { pub kind: CallKind, /// Calldata of the call. pub calldata: Bytes, + /// The gas limit of the call. + pub gas_limit: u64, /// The debug steps. pub steps: Vec, } @@ -25,8 +27,9 @@ impl DebugNode { kind: CallKind, steps: Vec, calldata: Bytes, + gas_limit: u64, ) -> Self { - Self { address, kind, steps, calldata } + Self { address, kind, steps, calldata, gas_limit } } } @@ -78,7 +81,7 @@ pub fn flatten_call_trace(arena: CallTraceArena, out: &mut Vec) { let call = &arena_nodes[pending.node_idx].trace; let calldata = if call.kind.is_any_create() { Bytes::new() } else { call.data.clone() }; - let node = DebugNode::new(call.address, call.kind, steps, calldata); + let node = DebugNode::new(call.address, call.kind, steps, calldata, call.gas_limit); out.push(node); } diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 6badd441fdb94..daf2d01d1da84 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -372,10 +372,11 @@ impl TUIContext<'_> { .collect::>(); let title = format!( - "Address: {} | PC: {} | Gas used in call: {}", + "Address: {} | PC: {} | Gas used: {} | Gas refund: {}", self.address(), self.current_step().pc, - self.current_step().gas_used, + self.debug_call().gas_limit - self.current_step().gas_remaining, + self.current_step().gas_refund_counter ); let block = Block::default().title(title).borders(Borders::ALL); let list = List::new(items) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 1d6933ee5e981..5a6cc4e91bafb 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -280,7 +280,7 @@ impl FuzzedExecutor { if success { Ok(FuzzOutcome::Case(CaseOutcome { - case: FuzzCase { calldata, gas: call.gas_used, stipend: call.stipend }, + case: FuzzCase { gas: call.gas_used, stipend: call.stipend }, traces: call.traces, coverage: call.line_coverage, breakpoints, diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index db992516850a1..0aa09f7fc406c 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -470,11 +470,9 @@ impl<'a> InvariantExecutor<'a> { { warn!(target: "forge::test", "{error}"); } - current_run.fuzz_runs.push(FuzzCase { - calldata: tx.call_details.calldata.clone(), - gas: call_result.gas_used, - stipend: call_result.stipend, - }); + current_run + .fuzz_runs + .push(FuzzCase { gas: call_result.gas_used, stipend: call_result.stipend }); // Determine if test can continue or should exit. // Check invariants based on check_interval to improve deep run performance. diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 1308be9b40241..44d71fb6deee3 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -317,8 +317,6 @@ impl FuzzTestResult { /// Data of a single fuzz test case #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct FuzzCase { - /// The calldata used for this fuzz test - pub calldata: Bytes, /// Consumed gas pub gas: u64, /// The initial gas stipend for the transaction diff --git a/crates/evm/traces/src/folded_stack_trace.rs b/crates/evm/traces/src/folded_stack_trace.rs index 80ac8a8dcb620..2b4e58b8665a7 100644 --- a/crates/evm/traces/src/folded_stack_trace.rs +++ b/crates/evm/traces/src/folded_stack_trace.rs @@ -5,20 +5,26 @@ use revm_inspectors::tracing::{ }; /// Builds a folded stack trace from a call trace arena. -pub fn build(arena: &CallTraceArena) -> Vec { - let mut fst = EvmFoldedStackTraceBuilder::default(); +pub fn build(arena: &CallTraceArena, isolate: bool) -> Vec { + let mut fst = EvmFoldedStackTraceBuilder::new(isolate); fst.process_call_node(arena.nodes(), 0); fst.build() } /// Wrapper for building a folded stack trace using EVM call trace node. -#[derive(Default)] pub struct EvmFoldedStackTraceBuilder { + /// Trace produced in isolate mode, meaning refund needs to be reversed at the depth=1 + /// frame for consistent gas values. + isolate: bool, /// Raw folded stack trace builder. fst: FoldedStackTraceBuilder, } impl EvmFoldedStackTraceBuilder { + pub fn new(isolate: bool) -> Self { + Self { isolate, fst: FoldedStackTraceBuilder::default() } + } + /// Returns the folded stack trace. pub fn build(self) -> Vec { self.fst.build() @@ -57,7 +63,13 @@ impl EvmFoldedStackTraceBuilder { } }; - self.fst.enter(func_name, node.trace.gas_used as i64); + let mut gas_used = node.trace.gas_used; + let max_refund_adjust_depth = if self.isolate { 1 } else { 0 }; + if node.trace.depth <= max_refund_adjust_depth { + gas_used += node.trace.gas_refund_counter; + } + + self.fst.enter(func_name, gas_used); // Track internal function step exits to do in this call context. let mut step_exits = vec![]; @@ -99,8 +111,8 @@ impl EvmFoldedStackTraceBuilder { if let Some(decoded_step) = &step.decoded { match decoded_step.as_ref() { DecodedTraceStep::InternalCall(decoded_internal_call, step_end_idx) => { - let gas_used = steps[*step_end_idx].gas_used.saturating_sub(step.gas_used); - self.fst.enter(decoded_internal_call.func_name.clone(), gas_used as i64); + let gas_used = step.gas_remaining - steps[*step_end_idx].gas_remaining; + self.fst.enter(decoded_internal_call.func_name.clone(), gas_used); step_exits.push(*step_end_idx); } DecodedTraceStep::Line(_) => {} @@ -158,13 +170,13 @@ pub struct FoldedStackTraceBuilder { struct TraceEntry { /// Names of all functions in the call stack of this trace. names: Vec, - /// Gas consumed by this function, allowed to be negative due to refunds. - gas: i64, + /// Gas consumed by this function, not including refunds. + gas: u64, } impl FoldedStackTraceBuilder { /// Enter execution of a function call that consumes `gas`. - pub fn enter(&mut self, label: String, gas: i64) { + pub fn enter(&mut self, label: String, gas: u64) { let mut names = self.traces.last().map(|entry| entry.names.clone()).unwrap_or_default(); while self.exits > 0 { @@ -189,7 +201,7 @@ impl FoldedStackTraceBuilder { /// Internal method to build the folded stack trace without subtracting gas consumed by /// the children function calls. - fn build_without_subtraction(&mut self) -> Vec { + pub fn build_without_subtraction(&mut self) -> Vec { let mut lines = Vec::new(); for TraceEntry { names, gas } in &self.traces { lines.push(format!("{} {}", names.join(";"), gas)); diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index fd0e5a9e54c0a..540949f51c071 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -407,7 +407,9 @@ impl<'ast> State<'_, 'ast> { self.block_depth -= 1; } else { if self.print_comments(span.hi(), CommentConfig::skip_ws()).is_some() { - self.zerobreak(); + // Adjust the offset of the trailing break from comment printing + // so the closing brace is not indented + self.s.offset(-self.ind); } else if self.config.bracket_spacing { self.nbsp(); }; diff --git a/crates/fmt/testdata/CommentEmptyLine/fmt.sol b/crates/fmt/testdata/CommentEmptyLine/fmt.sol new file mode 100644 index 0000000000000..a2407017f4be9 --- /dev/null +++ b/crates/fmt/testdata/CommentEmptyLine/fmt.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment +} diff --git a/crates/fmt/testdata/CommentEmptyLine/original.sol b/crates/fmt/testdata/CommentEmptyLine/original.sol new file mode 100644 index 0000000000000..9c7f35770e9f3 --- /dev/null +++ b/crates/fmt/testdata/CommentEmptyLine/original.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment + +} diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index b0d4ed3bdcd7f..f337b5b3657fa 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -169,6 +169,7 @@ fmt_tests! { ArrayExpressions, BlockComments, BlockCommentsFunction, + CommentEmptyLine, ConditionalOperatorExpression, ConstructorDefinition, ConstructorModifierStyle, @@ -224,3 +225,28 @@ fmt_tests! { Yul, YulStrings, } + +#[test] +fn test_comment_empty_line_bug() { + init_tracing(); + let source = r#"pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment + +} +"#; + + let expected = r#"pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment +} +"#; + + let fmt_config = Arc::new(FormatterConfig::default()); + let path = Path::new("test.sol"); + let formatted = format(source, path, fmt_config); + + assert_eq!(formatted, expected, "Formatting mismatch"); +} diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 2142a87c5cbd4..c059fb00e8f0a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -35,6 +35,7 @@ foundry-linking.workspace = true comfy-table.workspace = true eyre.workspace = true proptest.workspace = true +rand.workspace = true rayon.workspace = true serde.workspace = true tracing.workspace = true diff --git a/crates/forge/src/args.rs b/crates/forge/src/args.rs index aada974c91b9a..4f55f028a3690 100644 --- a/crates/forge/src/args.rs +++ b/crates/forge/src/args.rs @@ -13,6 +13,8 @@ use foundry_evm::inspectors::cheatcodes::{ForgeContext, set_execution_context}; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let args = Forge::parse(); args.global.init()?; diff --git a/crates/forge/src/cmd/eip712.rs b/crates/forge/src/cmd/eip712.rs index 00a872a1df7a4..5d6b1bace69ca 100644 --- a/crates/forge/src/cmd/eip712.rs +++ b/crates/forge/src/cmd/eip712.rs @@ -84,7 +84,7 @@ impl Eip712Args { if compiler.sess().dcx.has_errors().is_err() { eyre::bail!("{diags}"); } else { - let _ = sh_print!("{diags}"); + let _ = sh_eprint!("{diags}"); } Ok(()) diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index a31f227d4d675..a026a80b5231b 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -43,6 +43,7 @@ use foundry_evm::{ opts::EvmOpts, traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth}, }; +use rand::Rng; use regex::Regex; use std::{ collections::{BTreeMap, BTreeSet}, @@ -304,6 +305,12 @@ impl TestArgs { config.invariant.gas_report_samples = 0; } + // Generate a random fuzz seed if none provided, for reproducibility. + config.fuzz.seed = config + .fuzz + .seed + .or_else(|| Some(U256::from_be_bytes(rand::rng().random::<[u8; 32]>()))); + // Create test options from general project settings and compiler output. let should_debug = self.debug; let should_draw = self.flamegraph || self.flamechart; @@ -361,7 +368,7 @@ impl TestArgs { // Decode traces. let decoder = outcome.last_run_decoder.as_ref().unwrap(); decode_trace_arena(arena, decoder).await; - let mut fst = folded_stack_trace::build(arena); + let mut fst = folded_stack_trace::build(arena, self.evm.isolate); let label = if self.flamegraph { "flamegraph" } else { "flamechart" }; let contract = suite_name.split(':').next_back().unwrap(); @@ -429,6 +436,7 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, output: &ProjectCompileOutput, ) -> eyre::Result { + let fuzz_seed = config.fuzz.seed; if self.list { return list(runner, filter); } @@ -504,13 +512,13 @@ impl TestArgs { } }); sh_println!("{}", serde_json::to_string(&results)?)?; - return Ok(TestOutcome::new(Some(runner), results, self.allow_failure)); + return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed)); } if self.junit { let results = runner.test_collect(filter)?; sh_println!("{}", junit_xml_report(&results, verbosity).to_string()?)?; - return Ok(TestOutcome::new(Some(runner), results, self.allow_failure)); + return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed)); } let remote_chain = @@ -567,6 +575,7 @@ impl TestArgs { let mut gas_snapshots = BTreeMap::>::new(); let mut outcome = TestOutcome::empty(None, self.allow_failure); + outcome.fuzz_seed = fuzz_seed; let mut any_test_failed = false; let mut backtrace_builder = None; diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 985febef2b964..a8a1b5bbc10cf 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -5,7 +5,9 @@ use crate::{ traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData}, }; use alloy_primitives::map::HashSet; -use comfy_table::{Cell, Color, Table, modifiers::UTF8_ROUND_CORNERS, presets::ASCII_MARKDOWN}; +use comfy_table::{ + Cell, CellAlignment, Color, Table, modifiers::UTF8_ROUND_CORNERS, presets::ASCII_MARKDOWN, +}; use foundry_common::{TestFunctionExt, calc, shell}; use foundry_evm::traces::CallKind; @@ -213,8 +215,8 @@ impl GasReport { Cell::new("Deployment Size").fg(Color::Cyan), ]); table.add_row(vec![ - Cell::new(contract.gas.to_string()), - Cell::new(contract.size.to_string()), + Cell::new(contract.gas.to_string()).set_alignment(CellAlignment::Right), + Cell::new(contract.size.to_string()).set_alignment(CellAlignment::Right), ]); // Add a blank row to separate deployment info from function info. @@ -237,11 +239,19 @@ impl GasReport { table.add_row(vec![ Cell::new(display_name), - Cell::new(gas_info.min.to_string()).fg(Color::Green), - Cell::new(gas_info.mean.to_string()).fg(Color::Yellow), - Cell::new(gas_info.median.to_string()).fg(Color::Yellow), - Cell::new(gas_info.max.to_string()).fg(Color::Red), - Cell::new(gas_info.calls.to_string()), + Cell::new(gas_info.min.to_string()) + .fg(Color::Green) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.mean.to_string()) + .fg(Color::Yellow) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.median.to_string()) + .fg(Color::Yellow) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.max.to_string()) + .fg(Color::Red) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.calls.to_string()).set_alignment(CellAlignment::Right), ]); }) }); diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index c8ccdc5bc995d..693bea6f1eeb7 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -6,7 +6,7 @@ use crate::{ gas_report::GasReport, }; use alloy_primitives::{ - Address, I256, Log, + Address, I256, Log, U256, map::{AddressHashMap, HashMap}, }; use eyre::Report; @@ -46,6 +46,8 @@ pub struct TestOutcome { pub gas_report: Option, /// The runner used to execute the tests. pub runner: Option, + /// The fuzz seed used for the test run. + pub fuzz_seed: Option, } impl TestOutcome { @@ -54,13 +56,14 @@ impl TestOutcome { runner: Option, results: BTreeMap, allow_failure: bool, + fuzz_seed: Option, ) -> Self { - Self { results, allow_failure, last_run_decoder: None, gas_report: None, runner } + Self { results, allow_failure, last_run_decoder: None, gas_report: None, runner, fuzz_seed } } /// Creates a new empty test outcome. pub fn empty(runner: Option, allow_failure: bool) -> Self { - Self::new(runner, BTreeMap::new(), allow_failure) + Self::new(runner, BTreeMap::new(), allow_failure, None) } /// Returns an iterator over all individual succeeding tests and their names. @@ -130,6 +133,11 @@ impl TestOutcome { self.failures().count() } + /// Returns `true` if any fuzz or invariant test failed. + pub fn has_fuzz_failures(&self) -> bool { + self.failures().any(|(_, t)| t.kind.is_fuzz() || t.kind.is_invariant()) + } + /// Sums up all the durations of all individual test suites. /// /// Note that this is not necessarily the wall clock time of the entire test run. @@ -200,6 +208,17 @@ impl TestOutcome { test_word )?; + // Print seed for fuzz/invariant test failures to enable reproduction. + if let Some(seed) = self.fuzz_seed + && outcome.has_fuzz_failures() + { + sh_println!( + "\nFuzz seed: {} (use {} to reproduce)", + format!("{seed:#x}").cyan(), + "`--fuzz-seed`".cyan() + )?; + } + std::process::exit(1); } @@ -895,6 +914,16 @@ impl Default for TestKind { } impl TestKind { + /// Returns `true` if this is a fuzz test. + pub fn is_fuzz(&self) -> bool { + matches!(self, Self::Fuzz { .. }) + } + + /// Returns `true` if this is an invariant test. + pub fn is_invariant(&self) -> bool { + matches!(self, Self::Invariant { .. }) + } + /// The gas consumed by this test pub fn report(&self) -> TestKindReport { match self { diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 4594004e7a8d0..0ab26ee567deb 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1630,13 +1630,13 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1644,13 +1644,13 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1658,13 +1658,13 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -1735,13 +1735,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1749,13 +1749,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1763,13 +1763,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -1840,13 +1840,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1854,13 +1854,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1868,13 +1868,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -1948,13 +1948,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1962,13 +1962,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1976,13 +1976,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2060,13 +2060,13 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2107,13 +2107,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2154,13 +2154,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -2209,13 +2209,13 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -2223,13 +2223,13 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2290,13 +2290,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -2304,13 +2304,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -2379,13 +2379,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -2393,13 +2393,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -2407,13 +2407,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2542,19 +2542,19 @@ contract CounterTest is DSTest { +=======================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| -| 172107 | 578 | | | | | +| 172107 | 578 | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------+-----------------+-------+--------+-------+---------| -| a | 2402 | 2402 | 2402 | 2402 | 1 | +| a | 2402 | 2402 | 2402 | 2402 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| b | 2447 | 2447 | 2447 | 2447 | 1 | +| b | 2447 | 2447 | 2447 | 2447 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| setNumber(int256) | 23851 | 33807 | 33807 | 43763 | 2 | +| setNumber(int256) | 23851 | 33807 | 33807 | 43763 | 2 | |----------------------------------+-----------------+-------+--------+-------+---------| -| setNumber(uint256) | 23806 | 33762 | 33762 | 43718 | 2 | +| setNumber(uint256) | 23806 | 33762 | 33762 | 43718 | 2 | ╰----------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2669,15 +2669,15 @@ contract GasReportFallbackTest is Test { +========================================================================================================+ | Deployment Cost | Deployment Size | | | | | |---------------------------------------------------+-----------------+-------+--------+-------+---------| -| 117171 | 471 | | | | | +| 117171 | 471 | | | | | |---------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |---------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |---------------------------------------------------+-----------------+-------+--------+-------+---------| -| deposit | 21185 | 21185 | 21185 | 21185 | 1 | +| deposit | 21185 | 21185 | 21185 | 21185 | 1 | |---------------------------------------------------+-----------------+-------+--------+-------+---------| -| fallback | 29758 | 29758 | 29758 | 29758 | 1 | +| fallback | 29758 | 29758 | 29758 | 29758 | 1 | ╰---------------------------------------------------+-----------------+-------+--------+-------+---------╯ ╭-----------------------------------------------------+-----------------+------+--------+------+---------╮ @@ -2685,13 +2685,13 @@ contract GasReportFallbackTest is Test { +========================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-----------------------------------------------------+-----------------+------+--------+------+---------| -| 153531 | 494 | | | | | +| 153531 | 494 | | | | | |-----------------------------------------------------+-----------------+------+--------+------+---------| | | | | | | | |-----------------------------------------------------+-----------------+------+--------+------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------------------+-----------------+------+--------+------+---------| -| deposit | 3661 | 3661 | 3661 | 3661 | 1 | +| deposit | 3661 | 3661 | 3661 | 3661 | 1 | ╰-----------------------------------------------------+-----------------+------+--------+------+---------╯ @@ -2807,13 +2807,13 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] +=====================================================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| 132471 | 396 | | | | | +| 132471 | 396 | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| fallback | 43461 | 43461 | 43461 | 43461 | 1 | +| fallback | 43461 | 43461 | 43461 | 43461 | 1 | ╰----------------------------------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2892,13 +2892,13 @@ contract NestedDeploy is Test { +======================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-------------------------------------------------+-----------------+-------+--------+-------+---------| -| 0 | 132 | | | | | +| 0 | 132 | | | | | |-------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |-------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-------------------------------------------------+-----------------+-------+--------+-------+---------| -| w | 21185 | 21185 | 21185 | 21185 | 1 | +| w | 21185 | 21185 | 21185 | 21185 | 1 | ╰-------------------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+------+--------+------+---------╮ @@ -2906,13 +2906,13 @@ contract NestedDeploy is Test { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+------+--------+------+---------| -| 0 | 731 | | | | | +| 0 | 731 | | | | | |------------------------------------------+-----------------+------+--------+------+---------| | | | | | | | |------------------------------------------+-----------------+------+--------+------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+------+--------+------+---------| -| child | 2681 | 2681 | 2681 | 2681 | 1 | +| child | 2681 | 2681 | 2681 | 2681 | 1 | ╰------------------------------------------+-----------------+------+--------+------+---------╯ ╭-------------------------------------------+-----------------+-----+--------+-----+---------╮ @@ -2920,13 +2920,13 @@ contract NestedDeploy is Test { +============================================================================================+ | Deployment Cost | Deployment Size | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| -| 328961 | 1163 | | | | | +| 328961 | 1163 | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| | | | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-------------------------------------------+-----------------+-----+--------+-----+---------| -| child | 525 | 525 | 525 | 525 | 1 | +| child | 525 | 525 | 525 | 525 | 1 | ╰-------------------------------------------+-----------------+-----+--------+-----+---------╯ @@ -3732,17 +3732,17 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { +=======================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| -| 156813 | 509 | | | | | +| 156813 | 509 | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------+-----------------+-------+--------+-------+---------| -| increment | 43482 | 43482 | 43482 | 43482 | 1 | +| increment | 43482 | 43482 | 43482 | 43482 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| number | 2424 | 2424 | 2424 | 2424 | 1 | +| number | 2424 | 2424 | 2424 | 2424 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | +| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | ╰----------------------------------+-----------------+-------+--------+-------+---------╯ ╭-----------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -3750,15 +3750,15 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { +================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| 1544498 | 7573 | | | | | +| 1544498 | 7573 | | | | | |-----------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |-----------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| setUp | 218902 | 218902 | 218902 | 218902 | 1 | +| setUp | 218902 | 218902 | 218902 | 218902 | 1 | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | +| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | ╰-----------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -3774,21 +3774,21 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) | src/Counter.sol:Counter Contract | | | | | | |----------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 156813 | 509 | | | | | +| 156813 | 509 | | | | | | | | | | | | | Function Name | Min | Avg | Median | Max | # Calls | -| increment | 43482 | 43482 | 43482 | 43482 | 1 | -| number | 2424 | 2424 | 2424 | 2424 | 1 | -| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | +| increment | 43482 | 43482 | 43482 | 43482 | 1 | +| number | 2424 | 2424 | 2424 | 2424 | 1 | +| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | | test/Counter.t.sol:CounterTest Contract | | | | | | |-----------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 1544498 | 7573 | | | | | +| 1544498 | 7573 | | | | | | | | | | | | | Function Name | Min | Avg | Median | Max | # Calls | -| setUp | 218902 | 218902 | 218902 | 218902 | 1 | -| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | +| setUp | 218902 | 218902 | 218902 | 218902 | 1 | +| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 14217dfaba4de..4a1243051dd88 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1945,7 +1945,7 @@ contract SimpleScript is Script { ]) .assert_success() .stdout_eq(str![[r#" -{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"[..]","output":"[..]","gas_used":"{...}","gas_limit":"{...}","status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":"{...}","gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":"true","call_data":{"signature":"run()","args":[]}}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":"{...}","gas_limit":1056940999,"status":"Return","steps":[],"decoded":{"label":"VM","return_data":null,"call_data":{"signature":"startBroadcast()","args":[]}}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":"{...}","gas_limit":1056940650,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":"{...}","labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} +{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"[..]","output":"[..]","gas_used":"{...}","gas_limit":"{...}","gas_refund_counter":0,"status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":"{...}","gas_limit":1073720760,"gas_refund_counter":0,"status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":"true","call_data":{"signature":"run()","args":[]}}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":"{...}","gas_limit":1056940999,"gas_refund_counter":0,"status":"Return","steps":[],"decoded":{"label":"VM","return_data":null,"call_data":{"signature":"startBroadcast()","args":[]}}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":"{...}","gas_limit":1056940650,"gas_refund_counter":0,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":"{...}","labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} {"chain":31337,"estimated_gas_price":"{...}","estimated_total_gas_used":"{...}","estimated_amount_required":"{...}","token_symbol":"ETH"} {"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":"{...}","gas_price":"{...}"} {"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} @@ -3144,7 +3144,7 @@ contract CounterScript is Script { Compiler run successful! Traces: [..] → new CounterScript@[..] - └─ ← [Return] 2200 bytes of code + └─ ← [Return] 2162 bytes of code [..] CounterScript::setUp() └─ ← [Stop] diff --git a/crates/forge/tests/cli/test_cmd/fuzz.rs b/crates/forge/tests/cli/test_cmd/fuzz.rs index 7874712a7d21d..fefefb30d9b15 100644 --- a/crates/forge/tests/cli/test_cmd/fuzz.rs +++ b/crates/forge/tests/cli/test_cmd/fuzz.rs @@ -384,6 +384,8 @@ Encountered a total of 1 failing tests, 2 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd/invariant/common.rs b/crates/forge/tests/cli/test_cmd/invariant/common.rs index 0c765515437a6..eb28ca8c15ca0 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/common.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/common.rs @@ -98,6 +98,8 @@ Encountered a total of 2 failing tests, 1 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -175,6 +177,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -304,6 +308,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -373,6 +379,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -538,6 +546,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -624,6 +634,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -696,6 +708,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -779,6 +793,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); // `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2. @@ -888,6 +904,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -980,6 +998,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1092,6 +1112,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1191,6 +1213,8 @@ Encountered a total of 2 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1290,6 +1314,8 @@ Encountered a total of 2 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1589,6 +1615,8 @@ Encountered a total of 2 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd/invariant/mod.rs b/crates/forge/tests/cli/test_cmd/invariant/mod.rs index db3c8247f175e..3cbeeb4a46db7 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/mod.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/mod.rs @@ -431,6 +431,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); @@ -458,6 +460,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); @@ -481,6 +485,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); }); diff --git a/crates/forge/tests/cli/test_cmd/invariant/target.rs b/crates/forge/tests/cli/test_cmd/invariant/target.rs index 41fadd3cb59d3..a2b766926bb09 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/target.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/target.rs @@ -627,6 +627,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); // Test TargetSelectors @@ -670,6 +672,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); @@ -715,6 +719,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); // Test TargetArtifactSelectors @@ -763,6 +769,8 @@ Encountered a total of 1 failing tests, 1 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd/mod.rs b/crates/forge/tests/cli/test_cmd/mod.rs index 1c02fd40e001f..8a2f0e2e7fafe 100644 --- a/crates/forge/tests/cli/test_cmd/mod.rs +++ b/crates/forge/tests/cli/test_cmd/mod.rs @@ -915,6 +915,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -966,6 +968,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd/repros.rs b/crates/forge/tests/cli/test_cmd/repros.rs index 2ac4279a7c0e5..82ec6744a6172 100644 --- a/crates/forge/tests/cli/test_cmd/repros.rs +++ b/crates/forge/tests/cli/test_cmd/repros.rs @@ -63,6 +63,8 @@ Encountered a total of 3 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 3 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); diff --git a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json index eff3ecf321bc8..244a2d94fc7eb 100644 --- a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json +++ b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json @@ -45,6 +45,7 @@ "output": "{...}", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Return", "steps": [], "decoded": null @@ -78,6 +79,7 @@ "output": "0x", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Stop", "steps": [ { @@ -10196,6 +10198,7 @@ "output": "{...}", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Return", "steps": [ { @@ -10564,6 +10567,7 @@ "output": "0x", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Stop", "steps": [ { @@ -13836,6 +13840,7 @@ "output": "0x", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Stop", "steps": [], "decoded": null diff --git a/crates/forge/tests/fixtures/invariant_traces.svg b/crates/forge/tests/fixtures/invariant_traces.svg index 37c7bc8ebcf84..8844062c2553c 100644 --- a/crates/forge/tests/fixtures/invariant_traces.svg +++ b/crates/forge/tests/fixtures/invariant_traces.svg @@ -1,4 +1,4 @@ - +

- - - - - - - - - - -  - -
- -**Takeaway: Forge compilation is consistently faster than Hardhat by a factor of `2.1x` to `5.2x`, depending on the amount of caching involved.** - -## Forge +## Getting Started -Forge helps you build, test, fuzz, debug and deploy Solidity contracts. - -The best way to understand Forge is to simply try it (in less than 30 seconds!). - -First, let's initialize a new `counter` example repository: - -```sh -forge init counter -``` - -Next `cd` into `counter` and build: +Initialize a new project, build and test: ```sh +forge init counter && cd counter forge build -``` - -```console -[⠊] Compiling... -[⠔] Compiling 27 files with Solc 0.8.28 -[⠒] Solc 0.8.28 finished in 452.13ms -Compiler run successful! -``` - -Let's [test](https://getfoundry.sh/forge/tests#tests) our contracts: - -```sh forge test ``` -```console -[⠊] Compiling... -No files changed, compilation skipped - -Ran 2 tests for test/Counter.t.sol:CounterTest -[PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 31121, ~: 31277) -[PASS] test_Increment() (gas: 31293) -Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.35ms (4.86ms CPU time) - -Ran 1 test suite in 5.91ms (5.35ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests) -``` - -Finally, let's run our deployment script: - -```sh -forge script script/Counter.s.sol -``` - -```console -[⠊] Compiling... -No files changed, compilation skipped -Script ran successfully. -Gas used: 109037 - -If you wish to simulate on-chain transactions pass a RPC URL. -``` - -Run `forge --help` to explore the full list of available subcommands and their usage. - -More documentation can be found in the [forge](https://getfoundry.sh/forge/overview) section of the Foundry Docs. - -## Cast - -Cast is a Swiss Army knife for interacting with Ethereum applications from the command line. - -Here are a few examples of what you can do: - -**Check the latest block on Ethereum Mainnet**: +Interact with a live network: ```sh cast block-number --rpc-url https://eth.merkle.io -``` - -**Check the Ether balance of `vitalik.eth`** - -```sh cast balance vitalik.eth --ether --rpc-url https://eth.merkle.io ``` -**Replay and trace a transaction** - -```sh -cast run 0x9c32042f5e997e27e67f82583839548eb19dc78c4769ad6218657c17f2a5ed31 --rpc-url https://eth.merkle.io -``` - -Optionally, pass `--etherscan-api-key ` to decode transaction traces using verified source maps, providing more detailed and human-readable information. - ---- - -Run `cast --help` to explore the full list of available subcommands and their usage. - -More documentation can be found in the [cast](https://getfoundry.sh/cast/overview) section of the Foundry Docs. - -## Anvil - -Anvil is a fast local Ethereum development node. - -Let's fork Ethereum mainnet at the latest block: +Fork mainnet locally: ```sh anvil --fork-url https://eth.merkle.io ``` -You can use those same `cast` subcommands against your `anvil` instance: - -```sh -cast block-number -``` - ---- - -Run `anvil --help` to explore the full list of available features and their usage. - -More documentation can be found in the [anvil](https://getfoundry.sh/anvil/overview) section of the Foundry Docs. - -## Chisel - -Chisel is a fast, utilitarian, and verbose Solidity REPL. - -To use Chisel, simply type `chisel`. - -```sh -chisel -``` - -From here, start writing Solidity code! Chisel will offer verbose feedback on each input. - -Create a variable `a` and query it: - -```console -➜ uint256 a = 123; -➜ a -Type: uint256 -├ Hex: 0x7b -├ Hex (full word): 0x000000000000000000000000000000000000000000000000000000000000007b -└ Decimal: 123 -``` - -Finally, run `!source` to see `a` was applied: - -```solidity -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -import {Vm} from "forge-std/Vm.sol"; - -contract REPL { - Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); - - /// @notice REPL contract entry point - function run() public { - uint256 a = 123; - } -} -``` - ---- - -Run `chisel --help` to explore the full list of available features and their usage. - -More documentation can be found in the [chisel](https://getfoundry.sh/chisel/overview) section of the Foundry Docs. - -## Configuration - -Foundry is highly configurable, allowing you to tailor it to your needs. Configuration is managed via a file called [`foundry.toml`](./crates/config) located in the root of your project or any parent directory. For a full list of configuration options, refer to the [config package documentation](./crates/config/README.md#all-options). - -You can find additional [setup and configurations guides](https://getfoundry.sh/config/overview) in the [Foundry Docs][foundry-docs] and in the [config crate](./crates/config/README.md): - -- [Configuring with `foundry.toml`](https://getfoundry.sh/config/overview) -- [Setting up VSCode][vscode-setup] -- [Shell autocompletions][shell-setup] - -**Profiles and Namespaces** - -- Configuration can be organized into **profiles**, which are arbitrarily namespaced for flexibility. -- The default profile is named `default`. Learn more in the [Default Profile section](./crates/config/README.md#default-profile). -- To select a different profile, set the `FOUNDRY_PROFILE` environment variable. -- Override specific settings using environment variables prefixed with `FOUNDRY_` (e.g., `FOUNDRY_SRC`). - ---- +Read the [Foundry Docs][foundry-docs] to learn more. ## Contributing Contributions are welcome and highly appreciated. To get started, check out the [contributing guidelines](./CONTRIBUTING.md). -If you want to contribute, or follow along with contributor discussion, you can use our [main telegram](https://t.me/foundry_rs) to chat with us about the development of Foundry! +Join our [Telegram][tg-url] to chat about the development of Foundry. ## Support -Having trouble? See if the answer to your question can be found in the [Foundry Docs][foundry-docs]. - -If the answer is not there: -- Join the [support Telegram][tg-support-url] to get help, or -- Open an issue with [the bug](https://github.com/foundry-rs/foundry/issues/new) +Having trouble? Check the [Foundry Docs][foundry-docs], join the [support Telegram][tg-support-url], or [open an issue](https://github.com/foundry-rs/foundry/issues/new). #### License @@ -338,30 +91,4 @@ for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -## Acknowledgements - -- Foundry is a clean-room rewrite of the testing framework [DappTools][dapptools]. None of this would have been possible without the DappHub team's work over the years. -- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] (now [foundry-compilers]) which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. -- [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. -- Thank you to [Depot](https://depot.dev) for sponsoring us with their fast GitHub runners and sccache, which we use in CI to reduce build and test times significantly. -- All the other [contributors](https://github.com/foundry-rs/foundry/graphs/contributors) to the [ethers-rs](https://github.com/gakonst/ethers-rs), [alloy][alloy] & [foundry](https://github.com/foundry-rs/foundry) repositories and chatrooms. - -[solidity]: https://soliditylang.org/ [foundry-docs]: https://getfoundry.sh -[foundry-gha]: https://github.com/foundry-rs/foundry-toolchain -[foundry-compilers]: https://github.com/foundry-rs/compilers -[ethers-solc]: https://github.com/gakonst/ethers-rs/tree/master/ethers-solc/ -[solady]: https://github.com/Vectorized/solady -[openzeppelin]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/release-v5.1 -[morpho-blue]: https://github.com/morpho-org/morpho-blue -[solmate]: https://github.com/transmissions11/solmate/ -[geb]: https://github.com/reflexer-labs/geb -[benchmark-post]: https://www.paradigm.xyz/2022/03/foundry-02#blazing-fast-compilation--testing -[convex]: https://github.com/mds1/convex-shutdown-simulation -[vscode-setup]: https://getfoundry.sh/config/vscode.html -[shell-setup]: https://getfoundry.sh/config/shell-autocompletion.html -[foundry-0.2]: https://github.com/foundry-rs/foundry/releases/tag/nightly-5b7e4cb3c882b28f3c32ba580de27ce7381f415a -[foundry-1.0]: https://github.com/foundry-rs/foundry/releases/tag/nightly-59f354c179f4e7f6d7292acb3d068815c79286d1 -[dapptools]: https://github.com/dapphub/dapptools -[alloy]: https://github.com/alloy-rs/alloy diff --git a/benches/Cargo.toml b/benches/Cargo.toml index a0012900facf2..0b7b6c4c19039 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -29,6 +29,3 @@ chrono = { version = "0.4", features = ["serde"] } rayon.workspace = true clap = { version = "4", features = ["derive"] } once_cell = "1.21" - -[dev-dependencies] -foundry-test-utils.workspace = true diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index adeb988cd4fca..c10e7c3cedb51 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -39,7 +39,6 @@ alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-contract = { workspace = true, features = ["pubsub"] } alloy-network.workspace = true alloy-eips.workspace = true -alloy-eip5792.workspace = true alloy-rlp.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } alloy-signer-local = { workspace = true, features = ["mnemonic"] } @@ -112,7 +111,6 @@ fdlimit = { version = "0.3", optional = true } [dev-dependencies] alloy-provider = { workspace = true, features = ["txpool-api"] } alloy-pubsub.workspace = true -alloy-eip5792.workspace = true rand.workspace = true reqwest.workspace = true foundry-test-utils.workspace = true diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 67354bcf50b34..17ca572e040c2 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -1,5 +1,5 @@ use crate::{eth::subscription::SubscriptionId, types::ReorgOptions}; -use alloy_primitives::{Address, B64, B256, Bytes, TxHash, U256}; +use alloy_primitives::{Address, B64, B256, Bytes, TxHash, U256, map::HashSet}; use alloy_rpc_types::{ BlockId, BlockNumberOrTag as BlockNumber, BlockOverrides, Filter, Index, anvil::{Forking, MineOptions}, @@ -10,6 +10,7 @@ use alloy_rpc_types::{ trace::{ filter::TraceFilter, geth::{GethDebugTracingCallOptions, GethDebugTracingOptions}, + parity::TraceType, }, }; use alloy_serde::WithOtherFields; @@ -325,6 +326,13 @@ pub enum EthRequest { #[serde(rename = "trace_filter", with = "sequence")] TraceFilter(TraceFilter), + /// Trace transaction endpoint for parity's `trace_replayBlockTransactions` + #[serde(rename = "trace_replayBlockTransactions")] + TraceReplayBlockTransactions( + #[serde(deserialize_with = "lenient_block_number::lenient_block_number")] BlockNumber, + HashSet, + ), + // Custom endpoints, they're not extracted to a separate type out of serde convenience /// send transactions impersonating specific account and contract addresses. #[serde( @@ -578,11 +586,6 @@ pub enum EthRequest { #[serde(rename = "eth_sendUnsignedTransaction", with = "sequence")] EthSendUnsignedTransaction(Box>), - /// Turn on call traces for transactions that are returned to the user when they execute a - /// transaction (instead of just txhash/receipt) - #[serde(rename = "anvil_enableTraces", with = "empty_params")] - EnableTraces(()), - /// 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: @@ -706,18 +709,6 @@ pub enum EthRequest { /// Rollback the chain #[serde(rename = "anvil_rollback", with = "sequence")] Rollback(Option), - - /// Wallet - #[serde(rename = "wallet_getCapabilities", with = "empty_params")] - WalletGetCapabilities(()), - - /// Add an address to the delegation capability of the wallet - #[serde(rename = "anvil_addCapability", with = "sequence")] - AnvilAddCapability(Address), - - /// Set the executor (sponsor) wallet - #[serde(rename = "anvil_setExecutor", with = "sequence")] - AnvilSetExecutor(String), } /// Represents ethereum JSON-RPC API diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 86cb813fe456a..b3146a7b14d58 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1,6 +1,6 @@ //! Transaction related types use alloy_consensus::{ - Signed, Transaction, TxEnvelope, Typed2718, crypto::RecoveryError, transaction::Recovered, + Signed, Transaction, Typed2718, crypto::RecoveryError, transaction::Recovered, }; use alloy_eips::eip2718::Encodable2718; @@ -70,28 +70,8 @@ impl MaybeImpersonatedTransaction { // NOTE: we must update the hash because the tx can be impersonated, this requires forcing // the hash - let inner_envelope = match envelope { - TxEnvelope::Legacy(t) => { - let (tx, sig, _) = t.into_parts(); - TxEnvelope::Legacy(Signed::new_unchecked(tx, sig, hash)) - } - TxEnvelope::Eip2930(t) => { - let (tx, sig, _) = t.into_parts(); - TxEnvelope::Eip2930(Signed::new_unchecked(tx, sig, hash)) - } - TxEnvelope::Eip1559(t) => { - let (tx, sig, _) = t.into_parts(); - TxEnvelope::Eip1559(Signed::new_unchecked(tx, sig, hash)) - } - TxEnvelope::Eip4844(t) => { - let (tx, sig, _) = t.into_parts(); - TxEnvelope::Eip4844(Signed::new_unchecked(tx, sig, hash)) - } - TxEnvelope::Eip7702(t) => { - let (tx, sig, _) = t.into_parts(); - TxEnvelope::Eip7702(Signed::new_unchecked(tx, sig, hash)) - } - }; + let (typed_tx, signature, _) = Into::>::into(envelope).into_parts(); + let inner_envelope = Signed::new_unchecked(typed_tx, signature, hash).into(); RpcTransaction { block_hash: None, diff --git a/crates/anvil/server/src/pubsub.rs b/crates/anvil/server/src/pubsub.rs index 26d4126ea5389..ef02ceefb2d61 100644 --- a/crates/anvil/server/src/pubsub.rs +++ b/crates/anvil/server/src/pubsub.rs @@ -221,6 +221,7 @@ where let mut progress = false; for n in (0..pin.processing.len()).rev() { let mut req = pin.processing.swap_remove(n); + #[allow(clippy::collapsible_match)] match req.poll_unpin(cx) { Poll::Ready(resp) => { if let Ok(text) = serde_json::to_string(&resp) { @@ -238,6 +239,7 @@ where 'outer: for n in (0..subscriptions.len()).rev() { let (id, mut sub) = subscriptions.swap_remove(n); 'inner: loop { + #[allow(clippy::collapsible_match)] match sub.poll_next_unpin(cx) { Poll::Ready(Some(res)) => { if let Ok(text) = serde_json::to_string(&res) { diff --git a/crates/anvil/src/args.rs b/crates/anvil/src/args.rs index 806c8bc992155..4dee7f830fa15 100644 --- a/crates/anvil/src/args.rs +++ b/crates/anvil/src/args.rs @@ -7,6 +7,8 @@ use foundry_cli::utils; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let mut args = Anvil::parse(); args.global.init()?; args.node.evm.resolve_rpc_alias(); diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 9194580f8d7b4..9dac717e1f8a2 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -188,13 +188,21 @@ pub struct NodeArgs { #[arg(long)] pub transaction_block_keeper: Option, + /// Maximum number of transactions in a block. + #[arg(long)] + pub max_transactions: Option, + #[command(flatten)] pub evm: AnvilEvmArgs, #[command(flatten)] pub server_config: ServerConfig, - /// Path to the cache directory where states are stored. + /// Path to the cache directory where persisted states are stored (see + /// `--max-persisted-states`). + /// + /// Note: This does not affect the fork RPC cache location (`storage.json`), which is stored in + /// `~/.foundry/cache/rpc///`. #[arg(long, value_name = "PATH")] pub cache_path: Option, } @@ -230,6 +238,7 @@ impl NodeArgs { Ok(NodeConfig::default() .with_gas_limit(self.evm.gas_limit) .disable_block_gas_limit(self.evm.disable_block_gas_limit) + .enable_tx_gas_limit(self.evm.enable_tx_gas_limit) .with_gas_price(self.evm.gas_price) .with_hardfork(hardfork) .with_blocktime(self.block_time) @@ -259,7 +268,7 @@ impl NodeArgs { .with_eth_rpc_url(self.evm.fork_url.map(|fork| fork.url)) .with_base_fee(self.evm.block_base_fee_per_gas) .disable_min_priority_fee(self.evm.disable_min_priority_fee) - .with_storage_caching(self.evm.no_storage_caching) + .with_no_storage_caching(self.evm.no_storage_caching) .with_server_config(self.server_config) .with_host(self.host) .set_silent(shell::is_quiet()) @@ -277,6 +286,7 @@ impl NodeArgs { .set_pruned_history(self.prune_history) .with_init_state(self.load_state.or_else(|| self.state.and_then(|s| s.state))) .with_transaction_block_keeper(self.transaction_block_keeper) + .with_max_transactions(self.max_transactions) .with_max_persisted_states(self.max_persisted_states) .with_networks(self.evm.networks) .with_disable_default_create2_deployer(self.evm.disable_default_create2_deployer) @@ -540,6 +550,10 @@ pub struct AnvilEvmArgs { )] pub disable_block_gas_limit: bool, + /// Enable the transaction gas limit check as imposed by EIP-7825 (Osaka hardfork). + #[arg(long, visible_alias = "tx-gas-limit", help_heading = "Environment config")] + pub enable_tx_gas_limit: bool, + /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. To /// disable entirely, use `--disable-code-size-limit`. By default, it is 0x6000 (~25kb). #[arg(long, value_name = "CODE_SIZE", help_heading = "Environment config")] @@ -891,6 +905,16 @@ mod tests { assert!(args.is_err()); } + #[test] + fn can_parse_enable_tx_gas_limit() { + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--enable-tx-gas-limit"]); + assert!(args.evm.enable_tx_gas_limit); + + // Also test the alias + let args: NodeArgs = NodeArgs::parse_from(["anvil", "--tx-gas-limit"]); + assert!(args.evm.enable_tx_gas_limit); + } + #[test] fn can_parse_disable_code_size_limit() { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--disable-code-size-limit"]); diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 59e79bfe3a989..8e4be0a329387 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -14,7 +14,6 @@ use crate::{ }, mem::{self, in_memory_db::MemDb}, }; -use alloy_chains::Chain; use alloy_consensus::BlockHeader; use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams}; use alloy_evm::EvmEnv; @@ -58,8 +57,6 @@ use revm::{ use serde_json::{Value, json}; use std::{ fmt::Write as FmtWrite, - fs::File, - io, net::{IpAddr, Ipv4Addr}, path::PathBuf, sync::Arc, @@ -206,7 +203,8 @@ pub struct NodeConfig { pub networks: NetworkConfigs, /// Do not print log messages. pub silent: bool, - /// The path where states are cached. + /// The path where persisted states are cached (used with `max_persisted_states`). + /// This does not affect the fork RPC cache location. pub cache_path: Option, } @@ -533,7 +531,7 @@ impl NodeConfig { BlobExcessGasAndPrice::new( excess_blob_gas, get_blob_base_fee_update_fraction( - self.chain_id.unwrap_or(Chain::mainnet().id()), + self.get_chain_id(), self.get_genesis_timestamp(), ), ) @@ -542,10 +540,7 @@ impl NodeConfig { /// Returns the [`BlobParams`] that should be used. pub fn get_blob_params(&self) -> BlobParams { - get_blob_params( - self.chain_id.unwrap_or(Chain::mainnet().id()), - self.get_genesis_timestamp(), - ) + get_blob_params(self.get_chain_id(), self.get_genesis_timestamp()) } /// Returns the hardfork to use @@ -665,6 +660,15 @@ impl NodeConfig { self } + /// Sets the max number of transactions in a block + #[must_use] + pub fn with_max_transactions(mut self, max_transactions: Option) -> Self { + if let Some(max_transactions) = max_transactions { + self.max_transactions = max_transactions; + } + self + } + /// Sets max number of blocks with transactions to keep in memory #[must_use] pub fn with_transaction_block_keeper>( @@ -822,15 +826,9 @@ impl NodeConfig { self } - /// Disables storage caching #[must_use] - pub fn no_storage_caching(self) -> Self { - self.with_storage_caching(true) - } - - #[must_use] - pub fn with_storage_caching(mut self, storage_caching: bool) -> Self { - self.no_storage_caching = storage_caching; + pub fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { + self.no_storage_caching = no_storage_caching; self } @@ -980,11 +978,8 @@ impl NodeConfig { /// Prints the config info pub fn print(&self, fork: Option<&ClientFork>) -> Result<()> { if let Some(path) = &self.config_out { - let file = io::BufWriter::new( - File::create(path).wrap_err("unable to create anvil config description file")?, - ); let value = self.as_json(fork); - serde_json::to_writer(file, &value).wrap_err("failed writing JSON")?; + foundry_common::fs::write_json_file(path, &value).wrap_err("failed writing JSON")?; } if !self.silent { sh_println!("{}", self.as_string(fork))?; @@ -1044,7 +1039,10 @@ impl NodeConfig { self } - /// Sets the path where states are cached + /// Sets the path where persisted states are cached (used with `max_persisted_states`). + /// + /// Note: This does not control the fork RPC cache location (`storage.json`), which uses + /// `~/.foundry/cache/rpc///` via [`Config::foundry_block_cache_file`]. #[must_use] pub fn with_cache_path(mut self, cache_path: Option) -> Self { self.cache_path = cache_path; @@ -1212,7 +1210,7 @@ impl NodeConfig { eth_rpc_url: String, env: &mut Env, fees: &FeeManager, - ) -> Result<(ForkedDatabase, ClientForkConfig)> { + ) -> Result<(ForkedDatabase, ClientForkConfig)> { debug!(target: "node", ?eth_rpc_url, "setting up fork db"); let provider = Arc::new( ProviderBuilder::new(ð_rpc_url) @@ -1299,6 +1297,23 @@ latest block number: {latest_block}" ..Default::default() }; + // Determine chain_id early so we can use it consistently + let chain_id = if let Some(chain_id) = self.chain_id { + chain_id + } else { + let chain_id = if let Some(fork_chain_id) = fork_chain_id { + fork_chain_id.to() + } else { + provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")? + }; + + // need to update the dev signers and env with the chain id + self.set_chain_id(Some(chain_id)); + env.evm_env.cfg_env.chain_id = chain_id; + env.tx.base.chain_id = chain_id.into(); + chain_id + }; + // 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 { @@ -1319,12 +1334,7 @@ latest block number: {latest_block}" (block.header.excess_blob_gas, block.header.blob_gas_used) { // derive the blobparams that are active at this timestamp - let blob_params = get_blob_params( - fork_chain_id - .unwrap_or_else(|| U256::from(Chain::mainnet().id())) - .saturating_to(), - block.header.timestamp, - ); + let blob_params = get_blob_params(chain_id, block.header.timestamp); env.evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( blob_excess_gas, @@ -1352,21 +1362,6 @@ latest block number: {latest_block}" let block_hash = block.header.hash; - let chain_id = if let Some(chain_id) = self.chain_id { - chain_id - } else { - let chain_id = if let Some(fork_chain_id) = fork_chain_id { - fork_chain_id.to() - } else { - provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")? - }; - - // need to update the dev signers and env with the chain id - self.set_chain_id(Some(chain_id)); - env.evm_env.cfg_env.chain_id = chain_id; - env.tx.base.chain_id = chain_id.into(); - 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::( diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 3e783b7dbafaa..de572546de349 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1,6 +1,6 @@ use super::{ backend::mem::{BlockRequest, DatabaseRef, State}, - sign::build_typed_transaction, + sign::build_impersonated, }; use crate::{ ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, @@ -41,7 +41,7 @@ use alloy_network::{ TransactionBuilder4844, TransactionResponse, eip2718::Decodable2718, }; use alloy_primitives::{ - Address, B64, B256, Bytes, Signature, TxHash, TxKind, U64, U256, + Address, B64, B256, Bytes, TxHash, TxKind, U64, U256, map::{HashMap, HashSet}, }; use alloy_rpc_types::{ @@ -56,7 +56,7 @@ use alloy_rpc_types::{ trace::{ filter::TraceFilter, geth::{GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace, + parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType}, }, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, }; @@ -69,7 +69,6 @@ use anvil_core::{ EthRequest, block::BlockInfo, transaction::{MaybeImpersonatedTransaction, PendingTransaction}, - wallet::WalletCapabilities, }, types::{ReorgOptions, TransactionData}, }; @@ -77,7 +76,8 @@ use anvil_rpc::{error::RpcError, response::ResponseResult}; use foundry_common::provider::ProviderBuilder; use foundry_evm::decode::RevertDecoder; use foundry_primitives::{ - FoundryTransactionRequest, FoundryTxEnvelope, FoundryTxReceipt, FoundryTxType, FoundryTypedTx, + FoundryNetwork, FoundryTransactionRequest, FoundryTxEnvelope, FoundryTxReceipt, FoundryTxType, + FoundryTypedTx, }; use futures::{ StreamExt, TryFutureExt, @@ -113,7 +113,7 @@ pub struct EthApi { /// Whether this node is mining is_mining: bool, /// available signers - signers: Arc>>, + signers: Arc>>>, /// data required for `eth_feeHistory` fee_history_cache: FeeHistoryCache, /// max number of items kept in fee cache @@ -141,7 +141,7 @@ impl EthApi { pub fn new( pool: Arc, backend: Arc, - signers: Arc>>, + signers: Arc>>>, fee_history_cache: FeeHistoryCache, fee_history_limit: u64, miner: Miner, @@ -345,6 +345,9 @@ impl EthApi { EthRequest::TraceTransaction(tx) => self.trace_transaction(tx).await.to_rpc_result(), EthRequest::TraceBlock(block) => self.trace_block(block).await.to_rpc_result(), EthRequest::TraceFilter(filter) => self.trace_filter(filter).await.to_rpc_result(), + EthRequest::TraceReplayBlockTransactions(block, trace_types) => { + self.trace_replay_block_transactions(block, trace_types).await.to_rpc_result() + } EthRequest::ImpersonateAccount(addr) => { self.anvil_impersonate_account(addr).await.to_rpc_result() } @@ -455,7 +458,6 @@ impl EthApi { EthRequest::EthSendUnsignedTransaction(tx) => { self.eth_send_unsigned_transaction(*tx).await.to_rpc_result() } - EthRequest::EnableTraces(_) => self.anvil_enable_traces().await.to_rpc_result(), EthRequest::EthNewFilter(filter) => self.new_filter(filter).await.to_rpc_result(), EthRequest::EthGetFilterChanges(id) => self.get_filter_changes(&id).await, EthRequest::EthNewBlockFilter(_) => self.new_block_filter().await.to_rpc_result(), @@ -512,11 +514,6 @@ impl EthApi { self.anvil_reorg(reorg_options).await.to_rpc_result() } EthRequest::Rollback(depth) => self.anvil_rollback(depth).await.to_rpc_result(), - EthRequest::WalletGetCapabilities(()) => self.get_capabilities().to_rpc_result(), - EthRequest::AnvilAddCapability(addr) => self.anvil_add_capability(addr).to_rpc_result(), - EthRequest::AnvilSetExecutor(executor_pk) => { - self.anvil_set_executor(executor_pk).to_rpc_result() - } }; if let ResponseResult::Error(err) = &response { @@ -528,21 +525,13 @@ impl EthApi { response } - fn sign_request(&self, from: &Address, request: FoundryTypedTx) -> Result { - match request { - FoundryTypedTx::Deposit(_) => { - let nil_signature = Signature::from_scalars_and_parity( - B256::with_last_byte(1), - B256::with_last_byte(1), - false, - ); - return build_typed_transaction(request, nil_signature); - } + fn sign_request(&self, from: &Address, typed_tx: FoundryTypedTx) -> Result { + match typed_tx { + FoundryTypedTx::Deposit(_) => return Ok(build_impersonated(typed_tx)), _ => { for signer in self.signers.iter() { if signer.accounts().contains(from) { - let signature = signer.sign_transaction(request.clone(), from)?; - return build_typed_transaction(request, signature); + return signer.sign_transaction_from(from, typed_tx); } } } @@ -568,7 +557,7 @@ impl EthApi { match self.pool.get_transaction(hash) { Some(tx) => Ok(Some(tx.transaction.encoded_2718().into())), None => match self.backend.transaction_by_hash(hash).await? { - Some(tx) => Ok(Some(tx.inner.inner.encoded_2718().into())), + Some(tx) => Ok(Some(tx.as_ref().encoded_2718().into())), None => Ok(None), }, } @@ -1070,17 +1059,16 @@ impl EthApi { })?; let (nonce, on_chain_nonce) = self.request_nonce(&request, from).await?; - let request = self.build_tx_request(request, nonce).await?; + let typed_tx = self.build_tx_request(request, nonce).await?; // if the sender is currently impersonated we need to "bypass" signing let pending_transaction = if self.is_impersonated(from) { - let bypass_signature = self.impersonated_signature(&request); - let transaction = sign::build_typed_transaction(request, bypass_signature)?; + let transaction = sign::build_impersonated(typed_tx); self.ensure_typed_transaction_supported(&transaction)?; trace!(target : "node", ?from, "eth_sendTransaction: impersonating"); PendingTransaction::with_impersonated(transaction, from) } else { - let transaction = self.sign_request(&from, request)?; + let transaction = self.sign_request(&from, typed_tx)?; self.ensure_typed_transaction_supported(&transaction)?; PendingTransaction::new(transaction)? }; @@ -1371,10 +1359,7 @@ impl EthApi { } let typed_tx = self.build_tx_request(request, nonce).await?; - let tx = build_typed_transaction( - typed_tx, - Signature::new(Default::default(), Default::default(), false), - )?; + let tx = build_impersonated(typed_tx); let raw = tx.encoded_2718().to_vec().into(); @@ -1865,7 +1850,7 @@ impl EthApi { if let Some(filter) = self.filters.get_log_filter(id).await { self.backend.logs(filter).await } else { - Ok(Vec::new()) + Err(BlockchainError::FilterNotFound) } } @@ -1996,6 +1981,18 @@ impl EthApi { node_info!("trace_filter"); self.backend.trace_filter(filter).await } + + /// Replays all transactions in a block returning the requested traces for each transaction + /// + /// Handler for RPC call: `trace_replayBlockTransactions` + pub async fn trace_replay_block_transactions( + &self, + block: BlockNumber, + trace_types: HashSet, + ) -> Result> { + node_info!("trace_replayBlockTransactions"); + self.backend.trace_replay_block_transactions(block, trace_types).await + } } // == impl EthApi anvil endpoints == @@ -2146,12 +2143,14 @@ impl EthApi { node_info!("anvil_reset"); if let Some(forking) = forking { // if we're resetting the fork we need to reset the instance id - self.backend.reset_fork(forking).await + self.backend.reset_fork(forking).await?; } else { // Reset to a fresh in-memory state - - self.backend.reset_to_in_mem().await + self.backend.reset_to_in_mem().await?; } + // Clear pending transactions since they reference the old chain state. + self.pool.clear(); + Ok(()) } pub async fn anvil_set_chain_id(&self, chain_id: u64) -> Result<()> { @@ -2175,7 +2174,7 @@ impl EthApi { pub async fn anvil_add_balance(&self, address: Address, balance: U256) -> Result<()> { node_info!("anvil_addBalance"); let current_balance = self.backend.get_balance(address, None).await?; - self.backend.set_balance(address, current_balance + balance).await?; + self.backend.set_balance(address, current_balance.saturating_add(balance)).await?; Ok(()) } @@ -2580,20 +2579,18 @@ impl EthApi { ); // Build typed transaction request - let typed = self.build_tx_request(request.into(), *curr_nonce).await?; + let typed_tx = self.build_tx_request(request.into(), *curr_nonce).await?; // Increment nonce *curr_nonce += 1; // Handle signer and convert to pending transaction if self.is_impersonated(from) { - let bypass_signature = self.impersonated_signature(&typed); - let transaction = - sign::build_typed_transaction(typed, bypass_signature)?; + let transaction = sign::build_impersonated(typed_tx); self.ensure_typed_transaction_supported(&transaction)?; PendingTransaction::with_impersonated(transaction, from) } else { - let transaction = self.sign_request(&from, typed)?; + let transaction = self.sign_request(&from, typed_tx)?; self.ensure_typed_transaction_supported(&transaction)?; PendingTransaction::new(transaction)? } @@ -2806,15 +2803,6 @@ impl EthApi { Ok(()) } - /// Turn on call traces for transactions that are returned to the user when they execute a - /// transaction (instead of just txhash/receipt) - /// - /// Handler for ETH RPC call: `anvil_enableTraces` - pub async fn anvil_enable_traces(&self) -> Result<()> { - node_info!("anvil_enableTraces"); - Err(BlockchainError::RpcUnimplemented) - } - /// Execute a transaction regardless of signature status /// /// Handler for ETH RPC call: `eth_sendUnsignedTransaction` @@ -2828,10 +2816,9 @@ impl EthApi { let (nonce, on_chain_nonce) = self.request_nonce(&request, from).await?; - let request = self.build_tx_request(request, nonce).await?; + let typed_tx = self.build_tx_request(request, nonce).await?; - let bypass_signature = self.impersonated_signature(&request); - let transaction = sign::build_typed_transaction(request, bypass_signature)?; + let transaction = sign::build_impersonated(typed_tx); self.ensure_typed_transaction_supported(&transaction)?; @@ -2939,33 +2926,6 @@ impl EthApi { } } -// ===== impl Wallet endpoints ===== -impl EthApi { - /// Get the capabilities of the wallet. - /// - /// See also [EIP-5792][eip-5792]. - /// - /// [eip-5792]: https://eips.ethereum.org/EIPS/eip-5792 - pub fn get_capabilities(&self) -> Result { - node_info!("wallet_getCapabilities"); - Ok(self.backend.get_capabilities()) - } - - /// Add an address to the delegation capability of wallet. - /// - /// This entails that the executor will now be able to sponsor transactions to this address. - pub fn anvil_add_capability(&self, address: Address) -> Result<()> { - node_info!("anvil_addCapability"); - self.backend.add_capability(address); - Ok(()) - } - - pub fn anvil_set_executor(&self, executor_pk: String) -> Result
{ - node_info!("anvil_setExecutor"); - self.backend.set_executor(executor_pk) - } -} - impl EthApi { /// Executes the future on a new blocking task. async fn on_blocking_task(&self, c: C) -> Result @@ -3223,7 +3183,7 @@ impl EthApi { /// Returns the first signer that can sign for the given address #[expect(clippy::borrowed_box)] - pub fn get_signer(&self, address: Address) -> Option<&Box> { + pub fn get_signer(&self, address: Address) -> Option<&Box>> { self.signers.iter().find(|signer| signer.is_signer_for(address)) } @@ -3328,10 +3288,14 @@ impl EthApi { request.kind().is_none().then(|| request.set_kind(TxKind::default())); if request.gas_limit().is_none() { request.set_gas_limit( - self.do_estimate_gas(request.as_ref().clone(), None, EvmOverrides::default()) - .await - .map(|v| v as u64) - .unwrap_or(self.backend.gas_limit()), + self.do_estimate_gas( + request.as_ref().clone().into(), + None, + EvmOverrides::default(), + ) + .await + .map(|v| v as u64) + .unwrap_or(self.backend.gas_limit()), ); } @@ -3387,30 +3351,6 @@ impl EthApi { self.backend.cheats().is_impersonated(addr) } - /// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC - fn impersonated_signature(&self, request: &FoundryTypedTx) -> Signature { - match request { - // Only the legacy transaction type requires v to be in {27, 28}, thus - // requiring the use of Parity::NonEip155 - FoundryTypedTx::Legacy(_) => Signature::from_scalars_and_parity( - B256::with_last_byte(1), - B256::with_last_byte(1), - false, - ), - FoundryTypedTx::Eip2930(_) - | FoundryTypedTx::Eip1559(_) - | FoundryTypedTx::Eip7702(_) - | FoundryTypedTx::Eip4844(_) - | FoundryTypedTx::Deposit(_) => Signature::from_scalars_and_parity( - B256::with_last_byte(1), - B256::with_last_byte(1), - false, - ), - // TODO(onbjerg): we should impl support for Tempo transactions - FoundryTypedTx::Tempo(_) => todo!(), - } - } - /// Returns the nonce of the `address` depending on the `block_number` async fn get_transaction_count( &self, diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 6ad560787adcc..e162fd89e67c2 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -7,7 +7,11 @@ use std::{ }; use alloy_consensus::{BlockBody, Header}; -use alloy_primitives::{Address, B256, Bytes, U256, keccak256, map::HashMap}; +use alloy_eips::eip4895::Withdrawals; +use alloy_primitives::{ + Address, B256, Bytes, U256, keccak256, + map::{AddressMap, HashMap}, +}; use alloy_rpc_types::BlockId; use anvil_core::eth::{ block::Block, @@ -37,7 +41,7 @@ use crate::mem::storage::MinedTransaction; /// Helper trait get access to the full state data of the database pub trait MaybeFullDatabase: DatabaseRef + Debug { - fn maybe_as_full_db(&self) -> Option<&HashMap> { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { None } @@ -60,7 +64,7 @@ impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T where &'a T: DatabaseRef, { - fn maybe_as_full_db(&self) -> Option<&HashMap> { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { T::maybe_as_full_db(self) } @@ -168,6 +172,7 @@ pub trait Db: Some(Bytecode::new_raw(alloy_primitives::Bytes(account.code.0))) }, nonce, + account_id: None, }, ); @@ -237,7 +242,7 @@ impl + Send + Sync + Clone + fmt::Debug> D } impl + Debug> MaybeFullDatabase for CacheDB { - fn maybe_as_full_db(&self) -> Option<&HashMap> { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { Some(&self.cache.accounts) } @@ -345,7 +350,7 @@ impl DatabaseRef for StateDb { } impl MaybeFullDatabase for StateDb { - fn maybe_as_full_db(&self) -> Option<&HashMap> { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { self.0.maybe_as_full_db() } @@ -579,6 +584,8 @@ pub struct SerializableBlock { pub header: Header, pub transactions: Vec, pub ommers: Vec
, + #[serde(default)] + pub withdrawals: Option, } impl From for SerializableBlock { @@ -587,6 +594,7 @@ impl From for SerializableBlock { header: block.header, transactions: block.body.transactions.into_iter().map(Into::into).collect(), ommers: block.body.ommers.into_iter().collect(), + withdrawals: block.body.withdrawals, } } } @@ -595,7 +603,7 @@ impl From for Block { fn from(block: SerializableBlock) -> Self { let transactions = block.transactions.into_iter().map(Into::into).collect(); let ommers = block.ommers; - let body = BlockBody { transactions, ommers, withdrawals: None }; + let body = BlockBody { transactions, ommers, withdrawals: block.withdrawals }; Self::new(block.header, body) } } @@ -714,4 +722,36 @@ mod test { let _block: SerializableBlock = serde_json::from_str(block).unwrap(); } + + #[test] + fn test_block_withdrawals_preserved() { + use alloy_eips::eip4895::Withdrawal; + + // create a block with withdrawals (like post-Shanghai blocks) + let withdrawal = Withdrawal { + index: 42, + validator_index: 123, + address: Address::repeat_byte(1), + amount: 1000, + }; + + let header = Header::default(); + let body = BlockBody { + transactions: vec![], + ommers: vec![], + withdrawals: Some(vec![withdrawal].into()), + }; + let block = Block::new(header, body); + + // convert to SerializableBlock and back + let serializable = SerializableBlock::from(block); + let restored = Block::from(serializable); + + // withdrawals should be preserved + assert!(restored.body.withdrawals.is_some()); + let withdrawals = restored.body.withdrawals.unwrap(); + assert_eq!(withdrawals.len(), 1); + assert_eq!(withdrawals[0].index, 42); + assert_eq!(withdrawals[0].validator_index, 123); + } } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index aac713c9a3ef4..f102df45f656c 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -18,6 +18,7 @@ use alloy_consensus::{ proofs::calculate_receipt_root, transaction::Either, }; use alloy_eips::{ + Encodable2718, eip2935, eip4788, eip7685::EMPTY_REQUESTS_HASH, eip7702::{RecoveredAuthority, RecoveredAuthorization}, eip7840::BlobParams, @@ -28,7 +29,7 @@ use alloy_evm::{ precompiles::{DynPrecompile, Precompile, PrecompilesMap}, }; use alloy_op_evm::OpEvmFactory; -use alloy_primitives::{B256, Bloom, BloomInput, Log}; +use alloy_primitives::{B256, Bloom, BloomInput, Bytes, Log}; use anvil_core::eth::{ block::{BlockInfo, create_block}, transaction::{PendingTransaction, TransactionInfo}, @@ -71,7 +72,7 @@ impl ExecutedTransaction { *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 status_code = u8::from(self.exit_reason.is_ok()); let receipt_with_bloom: ReceiptWithBloom = Receipt { status: (status_code == 1).into(), cumulative_gas_used: *cumulative_gas_used, @@ -169,6 +170,26 @@ impl TransactionExecutor<'_, DB, V> { if is_cancun { self.evm_env.block_env().blob_excess_gas() } else { None }; let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None }; + // EIP-2935: store parent block hash in history storage contract. + if is_prague && !block_number.is_zero() { + let env = Env::new(self.evm_env.clone(), Default::default(), self.networks); + let mut inspector = AnvilInspector::default(); + let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector); + // SYSTEM_ADDRESS is defined in EIP-4788 and reused by EIP-2935. + match evm.transact_system_call( + eip4788::SYSTEM_ADDRESS, + eip2935::HISTORY_STORAGE_ADDRESS, + Bytes::copy_from_slice(parent_hash.as_slice()), + ) { + Ok(result) => { + self.db.commit(result.state); + } + Err(err) => { + warn!(target: "backend", "EIP-2935 system call failed: {:?}", err); + } + } + } + for tx in self.into_iter() { let tx = match tx { TransactionExecutionOutcome::Executed(tx) => { @@ -210,14 +231,16 @@ impl TransactionExecutor<'_, DB, V> { let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx; build_logs_bloom(&logs, &mut bloom); - 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 - } - }); + // For contract creation transactions, compute the contract address from sender + nonce. + // This should be set even if the transaction reverted, matching geth's behavior. + let sender = *transaction.pending_transaction.sender(); + let contract_address = if transaction.pending_transaction.transaction.to().is_none() { + let addr = sender.create(tx.nonce); + trace!(target: "backend", "Contract creation tx: computed address {:?}", addr); + Some(addr) + } else { + None + }; let transaction_index = transaction_infos.len() as u64; let info = TransactionInfo { @@ -303,7 +326,7 @@ impl TransactionExecutor<'_, DB, V> { } if self.networks.is_optimism() { - tx_env.enveloped_tx = Some(alloy_rlp::encode(tx.transaction.as_ref()).into()); + tx_env.enveloped_tx = Some(tx.transaction.encoded_2718().into()); } Env::new(self.evm_env.clone(), tx_env, self.networks) @@ -502,7 +525,10 @@ where { if env.networks.is_optimism() { let evm_env = EvmEnv::new( - env.evm_env.cfg_env.clone().with_spec(op_revm::OpSpecId::ISTHMUS), + env.evm_env + .cfg_env + .clone() + .with_spec_and_mainnet_gas_params(op_revm::OpSpecId::ISTHMUS), env.evm_env.block_env.clone(), ); EitherEvm::Op(OpEvmFactory::default().create_evm_with_inspector(db, evm_env, inspector)) diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 995206fbfcc89..362a9906fd418 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -6,7 +6,7 @@ use alloy_eips::eip2930::AccessListResult; use alloy_network::{AnyRpcBlock, AnyRpcTransaction, BlockResponse, TransactionResponse}; use alloy_primitives::{ Address, B256, Bytes, StorageValue, U256, - map::{FbHashMap, HashMap}, + map::{FbHashMap, HashMap, HashSet}, }; use alloy_provider::{ Provider, @@ -19,7 +19,7 @@ use alloy_rpc_types::{ simulate::{SimulatePayload, SimulatedBlock}, trace::{ geth::{GethDebugTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace as Trace, + parity::{LocalizedTransactionTrace as Trace, TraceResultsWithTransactionHash, TraceType}, }, }; use alloy_serde::WithOtherFields; @@ -308,6 +308,7 @@ impl ClientFork { index: usize, ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { + #[allow(clippy::collapsible_match)] match block.transactions() { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { @@ -332,6 +333,7 @@ impl ClientFork { index: usize, ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { + #[allow(clippy::collapsible_match)] match block.transactions() { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { @@ -419,6 +421,16 @@ impl ClientFork { Ok(traces) } + pub async fn trace_replay_block_transactions( + &self, + number: u64, + trace_types: HashSet, + ) -> Result, TransportError> { + // Forward to upstream provider for historical blocks + let params = (number, trace_types.iter().map(|t| format!("{t:?}")).collect::>()); + self.provider().raw_request("trace_replayBlockTransactions".into(), params).await + } + pub async fn transaction_receipt( &self, hash: B256, @@ -675,7 +687,7 @@ impl ClientForkConfig { .initial_backoff(self.backoff.as_millis() as u64) .compute_units_per_second(self.compute_units_per_second) .build() - .map_err(|_| BlockchainError::InvalidUrl(url.clone()))?, // .interval(interval), + .map_err(|e| BlockchainError::InvalidUrl(format!("{url}: {e}")))?, /* .interval(interval), */ ); trace!(target: "fork", "Updated rpc url {}", url); self.eth_rpc_url = url; diff --git a/crates/anvil/src/eth/backend/genesis.rs b/crates/anvil/src/eth/backend/genesis.rs index 597dd1ee2d179..a2db1c1d66e80 100644 --- a/crates/anvil/src/eth/backend/genesis.rs +++ b/crates/anvil/src/eth/backend/genesis.rs @@ -32,6 +32,7 @@ impl GenesisConfig { // we set this to empty so `Database::code_by_hash` doesn't get called code: Some(Default::default()), nonce: 0, + account_id: None, }; (address, info) }) @@ -65,6 +66,7 @@ impl GenesisConfig { nonce: nonce.unwrap_or_default(), code_hash: code.as_ref().map(|code| code.hash_slow()).unwrap_or(KECCAK_EMPTY), code, + account_id: None, } } } diff --git a/crates/anvil/src/eth/backend/mem/cache.rs b/crates/anvil/src/eth/backend/mem/cache.rs index b7c7d634a3d6c..524ef9cbd74bb 100644 --- a/crates/anvil/src/eth/backend/mem/cache.rs +++ b/crates/anvil/src/eth/backend/mem/cache.rs @@ -55,24 +55,21 @@ impl DiskStateCache { } } - /// Stores the snapshot for the given hash + /// Stores the snapshot for the given hash synchronously. /// - /// Note: this writes the state on a new spawned task - /// - /// Caution: this requires a running tokio Runtime. - pub fn write(&mut self, hash: B256, state: StateSnapshot) { - self.with_cache_file(hash, |file| { - tokio::task::spawn_blocking(move || { - match foundry_common::fs::write_json_file(&file, &state) { - Ok(_) => { - trace!(target: "backend", ?hash, "wrote state json file"); - } - Err(err) => { - error!(target: "backend", %err, ?hash, "Failed to write state snapshot"); - } - }; - }); - }); + /// Returns `true` if the write was successful, `false` otherwise. + pub fn write(&mut self, hash: B256, state: &StateSnapshot) -> bool { + self.with_cache_file(hash, |file| match foundry_common::fs::write_json_file(&file, state) { + Ok(_) => { + trace!(target: "backend", ?hash, "wrote state json file"); + true + } + Err(err) => { + error!(target: "backend", %err, ?hash, "Failed to write state snapshot"); + false + } + }) + .unwrap_or(false) } /// Loads the snapshot file for the given hash diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 306c60cfb62fa..308cdd2e4334c 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -2,7 +2,8 @@ use crate::eth::backend::db::{ Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, SerializableHistoricalStates, SerializableState, SerializableTransaction, StateDb, }; -use alloy_primitives::{Address, B256, U256, map::HashMap}; +use alloy_network::Network; +use alloy_primitives::{Address, B256, U256, map::AddressMap}; use alloy_rpc_types::BlockId; use foundry_evm::{ backend::{BlockchainDb, DatabaseResult, RevertStateSnapshotAction, StateSnapshot}, @@ -16,7 +17,7 @@ use revm::{ pub use foundry_evm::fork::database::ForkedDatabase; -impl Db for ForkedDatabase { +impl Db for ForkedDatabase { fn insert_account(&mut self, address: Address, account: AccountInfo) { self.database_mut().insert_account(address, account) } @@ -86,8 +87,8 @@ impl Db for ForkedDatabase { } } -impl MaybeFullDatabase for ForkedDatabase { - fn maybe_as_full_db(&self) -> Option<&HashMap> { +impl MaybeFullDatabase for ForkedDatabase { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { Some(&self.database().cache.accounts) } @@ -121,8 +122,8 @@ impl MaybeFullDatabase for ForkedDatabase { } } -impl MaybeFullDatabase for ForkDbStateSnapshot { - fn maybe_as_full_db(&self) -> Option<&HashMap> { +impl MaybeFullDatabase for ForkDbStateSnapshot { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { Some(&self.local.cache.accounts) } @@ -154,7 +155,7 @@ impl MaybeFullDatabase for ForkDbStateSnapshot { } } -impl MaybeForkedDatabase for ForkedDatabase { +impl MaybeForkedDatabase for ForkedDatabase { fn maybe_reset(&mut self, url: Option, block_number: BlockId) -> Result<(), String> { self.reset(url, block_number) } 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 d395259bad901..766805ee7ae9d 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -7,7 +7,7 @@ use crate::{ }, mem::state::state_root, }; -use alloy_primitives::{Address, B256, U256, map::HashMap}; +use alloy_primitives::{Address, B256, U256, map::AddressMap}; use alloy_rpc_types::BlockId; use foundry_evm::backend::{BlockchainDb, DatabaseResult, StateSnapshot}; use revm::{ @@ -106,7 +106,7 @@ impl Db for MemDb { } impl MaybeFullDatabase for MemDb { - fn maybe_as_full_db(&self) -> Option<&HashMap> { + fn maybe_as_full_db(&self) -> Option<&AddressMap> { Some(&self.inner.cache.accounts) } @@ -164,6 +164,7 @@ mod tests { code_hash: KECCAK_EMPTY, code: Some(contract_code.clone()), nonce: 1234, + account_id: None, }, ); dump_db @@ -206,6 +207,7 @@ mod tests { code_hash: KECCAK_EMPTY, code: Some(contract_code.clone()), nonce: 1234, + account_id: None, }, ); diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 3efaa1db98ec1..e50f2115f80c8 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -38,8 +38,8 @@ impl AnvilInspector { /// /// This will log all `console.sol` logs pub fn print_logs(&self) { - if let Some(collector) = &self.log_collector { - print_logs(&collector.logs); + if let Some(LogCollector::Capture { logs }) = &self.log_collector { + print_logs(logs); } } @@ -78,7 +78,7 @@ impl AnvilInspector { /// Configures the `Tracer` [`revm::Inspector`] with a log collector pub fn with_log_collector(mut self) -> Self { - self.log_collector = Some(Default::default()); + self.log_collector = Some(LogCollector::Capture { logs: Vec::new() }); self } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index b442843ccc4f7..cd1e2a03d5cd5 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -24,7 +24,7 @@ use crate::{ fees::{FeeDetails, FeeManager, MIN_SUGGESTED_PRIORITY_FEE}, macros::node_info, pool::transactions::PoolTransaction, - sign::build_typed_transaction, + sign::build_impersonated, }, mem::{ inspector::AnvilInspector, @@ -38,9 +38,8 @@ use alloy_consensus::{ proofs::{calculate_receipt_root, calculate_transaction_root}, transaction::Recovered, }; -use alloy_eip5792::{Capabilities, DelegationCapability}; use alloy_eips::{ - BlockNumHash, Encodable2718, eip4844::kzg_to_versioned_hash, eip7840::BlobParams, + BlockNumHash, Encodable2718, eip2935, eip4844::kzg_to_versioned_hash, eip7840::BlobParams, eip7910::SystemContract, }; use alloy_evm::{ @@ -51,12 +50,11 @@ use alloy_evm::{ }; use alloy_network::{ AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, - EthereumWallet, ReceiptResponse, TransactionBuilder, UnknownTxEnvelope, - UnknownTypedTransaction, + ReceiptResponse, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction, }; use alloy_primitives::{ - Address, B256, Bytes, TxHash, TxKind, U64, U256, address, hex, keccak256, logs_bloom, - map::HashMap, + Address, B256, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom, + map::{AddressMap, HashMap, HashSet}, }; use alloy_rpc_types::{ AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, @@ -73,17 +71,14 @@ use alloy_rpc_types::{ FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame, }, - parity::LocalizedTransactionTrace, + parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType}, }, }; use alloy_serde::{OtherFields, WithOtherFields}; -use alloy_signer::Signature; -use alloy_signer_local::PrivateKeySigner; use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer}; use anvil_core::eth::{ block::{Block, BlockInfo}, transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo}, - wallet::WalletCapabilities, }; use anvil_rpc::error::RpcError; use chrono::Datelike; @@ -152,15 +147,6 @@ impl DatabaseRef for dyn crate::eth::backend::db::Db {} pub const MIN_TRANSACTION_GAS: u128 = 21000; // Gas per transaction creating a contract. pub const MIN_CREATE_GAS: u128 = 53000; -// Executor -pub const EXECUTOR: Address = address!("0x6634F723546eCc92277e8a2F93d4f248bf1189ea"); -pub const EXECUTOR_PK: &str = "0x502d47e1421cb9abef497096728e69f07543232b93ef24de4998e18b5fd9ba0f"; -// Experimental ERC20 -pub const EXP_ERC20_CONTRACT: Address = address!("0x238c8CD93ee9F8c7Edf395548eF60c0d2e46665E"); -// Runtime code of the experimental ERC20 contract -pub const EXP_ERC20_RUNTIME_CODE: &[u8] = &hex!( - "60806040526004361015610010575b005b5f3560e01c806306fdde03146106f7578063095ea7b31461068c57806318160ddd1461066757806323b872dd146105a15780632bb7c5951461050e578063313ce567146104f35780633644e5151461045557806340c10f191461043057806370a08231146103fe5780637ecebe00146103cc57806395d89b4114610366578063a9059cbb146102ea578063ad0c8fdd146102ad578063d505accf146100fb5763dd62ed3e0361000e57346100f75760403660031901126100f7576100d261075c565b6100da610772565b602052637f5e9f20600c525f5260206034600c2054604051908152f35b5f80fd5b346100f75760e03660031901126100f75761011461075c565b61011c610772565b6084359160643560443560ff851685036100f757610138610788565b60208101906e04578706572696d656e74455243323608c1b8252519020908242116102a0576040519360018060a01b03169460018060a01b03169565383775081901600e52855f5260c06020600c20958654957f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252602082019586528660408301967fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc688528b6060850198468a528c608087019330855260a08820602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9885252528688525260a082015220604e526042602c205f5260ff1660205260a43560405260c43560605260208060805f60015afa93853d5103610293577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92594602094019055856303faf4f960a51b176040526034602c2055a3005b63ddafbaef5f526004601cfd5b631a15a3cc5f526004601cfd5b5f3660031901126100f7576103e834023481046103e814341517156102d65761000e90336107ac565b634e487b7160e01b5f52601160045260245ffd5b346100f75760403660031901126100f75761030361075c565b602435906387a211a2600c52335f526020600c2080548084116103595783900390555f526020600c20818154019055602052600c5160601c335f51602061080d5f395f51905f52602080a3602060405160018152f35b63f4d678b85f526004601cfd5b346100f7575f3660031901126100f757604051604081019080821067ffffffffffffffff8311176103b8576103b491604052600381526204558560ec1b602082015260405191829182610732565b0390f35b634e487b7160e01b5f52604160045260245ffd5b346100f75760203660031901126100f7576103e561075c565b6338377508600c525f52602080600c2054604051908152f35b346100f75760203660031901126100f75761041761075c565b6387a211a2600c525f52602080600c2054604051908152f35b346100f75760403660031901126100f75761000e61044c61075c565b602435906107ac565b346100f7575f3660031901126100f757602060a0610471610788565b828101906e04578706572696d656e74455243323608c1b8252519020604051907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252838201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015220604051908152f35b346100f7575f3660031901126100f757602060405160128152f35b346100f75760203660031901126100f7576004356387a211a2600c52335f526020600c2090815490818111610359575f80806103e88487839688039055806805345cdf77eb68f44c54036805345cdf77eb68f44c5580835282335f51602061080d5f395f51905f52602083a304818115610598575b3390f11561058d57005b6040513d5f823e3d90fd5b506108fc610583565b346100f75760603660031901126100f7576105ba61075c565b6105c2610772565b604435908260601b33602052637f5e9f208117600c526034600c20908154918219610643575b506387a211a2915017600c526020600c2080548084116103595783900390555f526020600c20818154019055602052600c5160601c9060018060a01b03165f51602061080d5f395f51905f52602080a3602060405160018152f35b82851161065a57846387a211a293039055856105e8565b6313be252b5f526004601cfd5b346100f7575f3660031901126100f75760206805345cdf77eb68f44c54604051908152f35b346100f75760403660031901126100f7576106a561075c565b60243590602052637f5e9f20600c52335f52806034600c20555f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa3602060405160018152f35b346100f7575f3660031901126100f7576103b4610712610788565b6e04578706572696d656e74455243323608c1b6020820152604051918291825b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b03821682036100f757565b602435906001600160a01b03821682036100f757565b604051906040820182811067ffffffffffffffff8211176103b857604052600f8252565b6805345cdf77eb68f44c548281019081106107ff576805345cdf77eb68f44c556387a211a2600c525f526020600c20818154019055602052600c5160601c5f5f51602061080d5f395f51905f52602080a3565b63e5cfe9575f526004601cfdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220fbe302881d9891005ba1448ba48547cc1cb17dea1a5c4011dfcb035de325bb1d64736f6c634300081b0033" -); pub type State = foundry_evm::utils::StateChangeset; @@ -239,9 +225,6 @@ pub struct Backend { precompile_factory: Option>, /// Prevent race conditions during mining mining: Arc>, - // === wallet === // - capabilities: Arc>, - executor_wallet: Arc>>, /// Disable pool balance checks disable_pool_balance_checks: bool, } @@ -340,8 +323,6 @@ impl Backend { slots_in_an_epoch, precompile_factory, mining: Arc::new(tokio::sync::Mutex::new(())), - capabilities: Arc::new(RwLock::new(WalletCapabilities(Default::default()))), - executor_wallet: Arc::new(RwLock::new(None)), disable_pool_balance_checks, }; @@ -361,44 +342,11 @@ impl Backend { Ok(()) } - /// Get the capabilities of the wallet. - /// - /// Currently the only capability is delegation. - /// - /// See `anvil_core::eth::wallet::Capabilities` for construction helpers. - pub(crate) fn get_capabilities(&self) -> WalletCapabilities { - self.capabilities.read().clone() - } - /// Updates memory limits that should be more strict when auto-mine is enabled pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) { self.states.write().update_interval_mine_block_time(block_time) } - /// Adds an address to the wallet's delegation capability. - pub(crate) fn add_capability(&self, address: Address) { - let chain_id = self.env.read().evm_env.cfg_env.chain_id; - let mut capabilities = self.capabilities.write(); - let mut capability = capabilities - .get(chain_id) - .cloned() - .unwrap_or(Capabilities { delegation: DelegationCapability { addresses: vec![] } }); - capability.delegation.addresses.push(address); - capabilities.0.insert(chain_id, capability); - } - - pub(crate) fn set_executor(&self, executor_pk: String) -> Result { - let signer: PrivateKeySigner = - executor_pk.parse().map_err(|_| RpcError::invalid_params("Invalid private key"))?; - - let executor = signer.address(); - let wallet = EthereumWallet::new(signer); - - *self.executor_wallet.write() = Some(wallet); - - Ok(executor) - } - /// Applies the configured genesis settings /// /// This will fund, create the genesis accounts @@ -438,6 +386,14 @@ impl Backend { // 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()); + + // Deploy EIP-2935 blockhash history storage contract if Prague is active. + if self.spec_id() >= SpecId::PRAGUE { + db.set_code( + eip2935::HISTORY_STORAGE_ADDRESS, + eip2935::HISTORY_STORAGE_CODE.clone(), + )?; + } } let db = self.db.write().await; @@ -1218,8 +1174,7 @@ impl Backend { ); if env.networks.is_optimism() { - env.tx.enveloped_tx = - Some(alloy_rlp::encode(tx.pending_transaction.transaction.as_ref()).into()); + env.tx.enveloped_tx = Some(tx.pending_transaction.transaction.encoded_2718().into()); } let db = self.db.read().await; @@ -1747,10 +1702,7 @@ impl Backend { let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?; - let tx = build_typed_transaction( - typed_tx, - Signature::new(Default::default(), Default::default(), false), - )?; + let tx = build_impersonated(typed_tx); let tx_hash = tx.hash(); let rpc_tx = transaction_build( None, @@ -1789,7 +1741,7 @@ impl Backend { }) .collect(), }; - logs.extend(sim_res.logs.clone().iter().map(|log| log.inner.clone())); + logs.extend(sim_res.logs.iter().map(|log| log.inner.clone())); log_index += sim_res.logs.len(); call_res.push(sim_res); } @@ -2032,7 +1984,7 @@ impl Backend { drop(evm); let tracing_inspector = inspector.tracer.expect("tracer disappeared"); - let return_value = out.as_ref().map(|o| o.data().clone()).unwrap_or_default(); + let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default(); trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call"); @@ -2192,8 +2144,7 @@ impl Backend { let from_block = self.convert_block_number(filter.block_option.get_from_block().copied()); if from_block > best { - // requested log range does not exist yet - return Ok(vec![]); + return Err(BlockchainError::BlockOutOfRange(best, from_block)); } self.logs_for_range(&filter, from_block, to_block).await @@ -2476,8 +2427,14 @@ impl Backend { None => None, }; let block_number = self.convert_block_number(block_number); + let current_number = self.best_number(); + + // Reject requests for future blocks that don't exist yet + if block_number > current_number { + return Err(BlockchainError::BlockOutOfRange(current_number, block_number)); + } - if block_number < self.env.read().evm_env.block_env.number.saturating_to() { + if block_number < current_number { if let Some((block_hash, block)) = self .block_by_number(BlockNumber::Number(block_number)) .await? @@ -2495,10 +2452,7 @@ impl Backend { } warn!(target: "backend", "Not historic state found for block={}", block_number); - return Err(BlockchainError::BlockOutOfRange( - self.env.read().evm_env.block_env.number.saturating_to(), - block_number, - )); + return Err(BlockchainError::BlockOutOfRange(current_number, block_number)); } let db = self.db.read().await; @@ -2995,6 +2949,114 @@ impl Backend { Ok(vec![]) } + /// Replays all transactions in a block and returns the requested traces for each transaction + pub async fn trace_replay_block_transactions( + &self, + block: BlockNumber, + trace_types: HashSet, + ) -> Result, BlockchainError> { + let block_number = self.convert_block_number(Some(block)); + + // Try mined blocks first + if let Some(results) = + self.mined_parity_trace_replay_block_transactions(block_number, &trace_types) + { + return Ok(results); + } + + // Fallback to fork if block predates fork + if let Some(fork) = self.get_fork() + && fork.predates_fork(block_number) + { + return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?); + } + + Ok(vec![]) + } + + /// Returns the trace results for all transactions in a mined block by replaying them + fn mined_parity_trace_replay_block_transactions( + &self, + block_number: u64, + trace_types: &HashSet, + ) -> Option> { + let block = self.get_block(block_number)?; + + // Execute this in the context of the parent state + let parent_hash = block.header.parent_hash; + let trace_config = TracingInspectorConfig::from_parity_config(trace_types); + + let read_guard = self.states.upgradable_read(); + if let Some(state) = read_guard.get_state(&parent_hash) { + self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types) + } else { + let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard); + let state = write_guard.get_on_disk_state(&parent_hash)?; + self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types) + } + } + + /// Replays all transactions in a block with the tracing inspector to generate TraceResults + fn replay_block_transactions_with_inspector( + &self, + block: &Block, + parent_state: &StateDb, + trace_config: TracingInspectorConfig, + trace_types: &HashSet, + ) -> Option> { + let mut cache_db = CacheDB::new(Box::new(parent_state)); + let mut results = Vec::new(); + + // Configure the block environment + let mut env = self.env.read().clone(); + env.evm_env.block_env = BlockEnv { + number: U256::from(block.header.number), + beneficiary: block.header.beneficiary, + 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(), + gas_limit: block.header.gas_limit, + ..Default::default() + }; + + // Execute each transaction in the block with tracing + for tx_envelope in &block.body.transactions { + let tx_hash = tx_envelope.hash(); + + // Create a fresh inspector for this transaction + let mut inspector = TracingInspector::new(trace_config); + + // Prepare transaction environment + let pending_tx = + PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?; + let mut tx_env: OpTransaction = FromRecoveredTx::from_recovered_tx( + pending_tx.transaction.as_ref(), + *pending_tx.sender(), + ); + if env.networks.is_optimism() { + tx_env.enveloped_tx = Some(pending_tx.transaction.encoded_2718().into()); + } + + // Execute the transaction with the inspector + let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); + let result = evm.transact(tx_env.clone()).ok()?; + + // Build TraceResults from the inspector and execution result + let full_trace = inspector + .into_parity_builder() + .into_trace_results_with_state(&result, trace_types, &cache_db) + .ok()?; + + results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace }); + + // Commit the state changes for the next transaction + cache_db.commit(result.state); + } + + Some(results) + } + pub async fn transaction_receipt( &self, hash: B256, @@ -3134,7 +3196,8 @@ impl Backend { blob_gas_used, }; - let inner = FoundryTxReceipt::new(receipt); + // Include timestamp in receipt to avoid extra block lookups (e.g., in Otterscan API) + let inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp); Some(MinedTransactionReceipt { inner, out: info.out }) } @@ -3361,14 +3424,14 @@ impl Backend { .into_iter() .map(|(_, v)| v) .collect(); - let storage_proofs = prove_storage(&account.storage, &keys); + let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys); let account_proof = AccountProof { address, balance: account.info.balance, nonce: account.info.nonce, code_hash: account.info.code_hash, - storage_hash: storage_root(&account.storage), + storage_hash, account_proof: proof, storage_proof: keys .into_iter() @@ -3443,7 +3506,7 @@ impl Backend { // Get the database at the common block let common_state = { let return_state_or_throw_err = - |db: Option<&StateDb>| -> Result, BlockchainError> { + |db: Option<&StateDb>| -> Result, BlockchainError> { let state_db = db.ok_or(BlockchainError::DataUnavailable)?; let db_full = state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; @@ -3461,18 +3524,7 @@ impl Backend { }; { - // Set state to common state - self.db.write().await.clear(); - for (address, acc) in common_state { - for (key, value) in acc.storage { - self.db.write().await.set_storage_at(address, key.into(), value.into())?; - } - self.db.write().await.insert_account(address, acc.info); - } - } - - { - // Unwind the storage back to the common ancestor + // Unwind the storage back to the common ancestor first self.blockchain .storage .write() @@ -3488,6 +3540,41 @@ impl Backend { self.time.reset(env.evm_env.block_env.timestamp.saturating_to()); } + + { + // Collect block hashes before acquiring db lock to avoid holding blockchain storage + // lock across await. Only collect the last 256 blocks since that's all BLOCKHASH can + // access. + let block_hashes: Vec<_> = { + let storage = self.blockchain.storage.read(); + let min_block = common_block.header.number.saturating_sub(256); + storage + .hashes + .iter() + .filter(|(num, _)| **num >= min_block) + .map(|(&num, &hash)| (num, hash)) + .collect() + }; + + // Acquire db lock once for the entire restore operation to reduce lock churn. + let mut db = self.db.write().await; + db.clear(); + + // Insert account info before storage to prevent fork-mode RPC fetches after clear. + for (address, acc) in common_state { + db.insert_account(address, acc.info); + for (key, value) in acc.storage { + db.set_storage_at(address, key.into(), value.into())?; + } + } + + // Restore block hashes from blockchain storage (now unwound, contains only valid + // blocks). + for (block_num, hash) in block_hashes { + db.insert_block_hash(U256::from(block_num), hash); + } + } + Ok(()) } } @@ -3601,6 +3688,19 @@ impl TransactionValidator for Backend { } } + // EIP-3860 initcode size validation, respects --code-size-limit / --disable-code-size-limit + if env.evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create { + let max_initcode_size = env + .evm_env + .cfg_env + .limit_contract_code_size + .map(|limit| limit.saturating_mul(2)) + .unwrap_or(revm::primitives::eip3860::MAX_INITCODE_SIZE); + if tx.input().len() > max_initcode_size { + return Err(InvalidTransactionError::MaxInitCodeSizeExceeded); + } + } + // Balance and fee related checks if !self.disable_pool_balance_checks { // Gas limit validation @@ -3819,7 +3919,7 @@ 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(storage: &HashMap, keys: &[B256]) -> Vec> { +pub fn prove_storage(storage: &HashMap, keys: &[B256]) -> (B256, Vec>) { let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect(); let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone())); @@ -3828,7 +3928,7 @@ pub fn prove_storage(storage: &HashMap, keys: &[B256]) -> Vec, keys: &[B256]) -> Vec bool { diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index e055cbfef5487..e577db9ff3b3a 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -1,6 +1,9 @@ //! Support for generating the state root for memdb storage -use alloy_primitives::{Address, B256, U256, keccak256, map::HashMap}; +use alloy_primitives::{ + B256, U256, keccak256, + map::{AddressMap, HashMap}, +}; use alloy_rlp::Encodable; use alloy_trie::{HashBuilder, Nibbles}; use revm::{database::DbAccount, state::AccountInfo}; @@ -14,7 +17,7 @@ pub fn build_root(values: impl IntoIterator)>) -> B256 } /// Builds state root from the given accounts -pub fn state_root(accounts: &HashMap) -> B256 { +pub fn state_root(accounts: &AddressMap) -> B256 { build_root(trie_accounts(accounts)) } @@ -32,21 +35,21 @@ pub fn trie_storage(storage: &HashMap) -> Vec<(Nibbles, Vec)> { (Nibbles::unpack(keccak256(key.to_be_bytes::<32>())), data) }) .collect::>(); - storage.sort_by(|(key1, _), (key2, _)| key1.cmp(key2)); + storage.sort_by_key(|(key, _)| *key); storage } /// 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 +pub fn trie_accounts(accounts: &AddressMap) -> Vec<(Nibbles, Vec)> { + let mut accounts: Vec<(Nibbles, Vec)> = accounts .iter() .map(|(address, account)| { let data = trie_account_rlp(&account.info, &account.storage); (Nibbles::unpack(keccak256(*address)), data) }) - .collect::>(); - accounts.sort_by(|(key1, _), (key2, _)| key1.cmp(key2)); + .collect(); + accounts.sort_by_key(|(key, _)| *key); accounts } diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index af99852a53032..4d73f19ebdaca 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -149,9 +149,19 @@ impl InMemoryBlockStates { // only write to disk if supported if !self.is_memory_only() { let state_snapshot = state.0.clear_into_state_snapshot(); - self.disk_cache.write(hash, state_snapshot); - self.on_disk_states.insert(hash, state); - self.oldest_on_disk.push_back(hash); + if self.disk_cache.write(hash, &state_snapshot) { + // Write succeeded, move state to on-disk tracking + self.on_disk_states.insert(hash, state); + self.oldest_on_disk.push_back(hash); + } else { + // Write failed, restore state to memory to avoid data loss + state.init_from_state_snapshot(state_snapshot); + self.states.insert(hash, state); + self.present.push_front(hash); + // Increase limit temporarily to prevent infinite retry loop + self.in_memory_limit = self.in_memory_limit.saturating_add(1); + break; + } } } } @@ -426,6 +436,13 @@ impl BlockchainStorage { let block_number = block.header.number; self.blocks.insert(block_hash, block); self.hashes.insert(block_number, block_hash); + + // Update genesis_hash if we are loading block 0, so that Finalized/Safe/Earliest + // block tag lookups return the correct hash. + // See: https://github.com/foundry-rs/foundry/issues/12645 + if block_number == 0 { + self.genesis_hash = block_hash; + } } } diff --git a/crates/anvil/src/eth/backend/time.rs b/crates/anvil/src/eth/backend/time.rs index 175f2792430b3..056941942c419 100644 --- a/crates/anvil/src/eth/backend/time.rs +++ b/crates/anvil/src/eth/backend/time.rs @@ -7,7 +7,7 @@ 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_timestamp(secs as i64, 0).unwrap() + DateTime::from_timestamp(secs as i64, 0).unwrap_or(DateTime::::MAX_UTC) } /// Manages block time diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 2db3afbae8060..d3844f67312f2 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -123,6 +123,8 @@ pub enum BlockchainError { }, #[error("Invalid transaction request: {0}")] InvalidTransactionRequest(String), + #[error("filter not found")] + FilterNotFound, } impl From for BlockchainError { @@ -577,6 +579,11 @@ impl ToRpcResponseResult for Result { err @ BlockchainError::RecoveryError(_) => { RpcError::invalid_params(err.to_string()) } + BlockchainError::FilterNotFound => RpcError { + code: ErrorCode::ServerError(-32000), + message: "filter not found".into(), + data: None, + }, } .into(), } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 28ad0999e3eba..1d4c718a42f02 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -7,10 +7,7 @@ use std::{ }; use alloy_consensus::{Header, Transaction}; -use alloy_eips::{ - calc_next_block_base_fee, eip1559::BaseFeeParams, eip7691::MAX_BLOBS_PER_BLOCK_ELECTRA, - eip7840::BlobParams, -}; +use alloy_eips::{calc_next_block_base_fee, eip1559::BaseFeeParams, eip7840::BlobParams}; use alloy_primitives::B256; use futures::StreamExt; use parking_lot::{Mutex, RwLock}; @@ -276,15 +273,22 @@ impl FeeHistoryService { 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_BLOBS_PER_BLOCK_ELECTRA as f64).unwrap_or(0 as f64); + item.blob_gas_used_ratio = blob_gas_used + .map(|g| { + let max = self.blob_params.max_blob_gas_per_block() as f64; + if max == 0.0 { 0.0 } else { g / max } + }) + .unwrap_or(0.0); // extract useful tx info (gas_used, effective_reward) let mut transactions: Vec<(_, _)> = receipts .iter() .enumerate() .map(|(i, receipt)| { - let gas_used = receipt.cumulative_gas_used(); + let cumulative = receipt.cumulative_gas_used(); + let prev_cumulative = + if i > 0 { receipts[i - 1].cumulative_gas_used() } else { 0 }; + let gas_used = cumulative - prev_cumulative; let effective_reward = block .body .transactions @@ -297,7 +301,7 @@ impl FeeHistoryService { .collect(); // sort by effective reward asc - transactions.sort_by(|(_, a), (_, b)| a.cmp(b)); + transactions.sort_by_key(|(_, reward)| *reward); // calculate percentile rewards item.rewards = reward_percentiles diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 16151219786cb..3f4be8158f457 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -375,10 +375,11 @@ impl EthApi { let receipt_futs = block.transactions.hashes().map(|hash| self.transaction_receipt(hash)); - let receipts = join_all(receipt_futs.map(|r| async { + // Reuse timestamp from the block we already have + let timestamp = block.header.timestamp; + + let receipts = join_all(receipt_futs.map(|r| async move { if let Ok(Some(r)) = r.await { - let block = self.block_by_number(r.block_number().unwrap().into()).await?; - let timestamp = block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp; let receipt = r.as_ref().inner.clone().map_inner(OtsReceipt::from); Ok(OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }) } else { @@ -418,8 +419,14 @@ impl EthApi { let receipts = join_all(receipt_futs.map(|r| async { if let Ok(Some(r)) = r.await { - let block = self.block_by_number(r.block_number().unwrap().into()).await?; - let timestamp = block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp; + // Try to get timestamp from receipt's other fields first (set by mined receipts), + // fallback to block lookup for fork receipts that may not have it + let timestamp = if let Some(ts) = r.block_timestamp() { + ts + } else { + let block = self.block_by_number(r.block_number().unwrap().into()).await?; + block.ok_or(BlockchainError::BlockNotFound)?.header.timestamp + }; let receipt = r.as_ref().inner.clone().map_inner(OtsReceipt::from); Ok(OtsTransactionReceipt { receipt, timestamp: Some(timestamp) }) } else { diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 25b5e673db812..8231315ea7e31 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -106,19 +106,17 @@ impl Pool { markers: impl IntoIterator, ) -> PruneResult { debug!(target: "txpool", ?block_number, "pruning transactions"); - self.inner.write().prune_markers(markers) + let res = self.inner.write().prune_markers(markers); + for tx in &res.promoted { + self.notify_ready(tx); + } + res } /// Adds a new transaction to the pool pub fn add_transaction(&self, tx: PoolTransaction) -> Result { let added = self.inner.write().add_transaction(tx)?; - if let AddedTransaction::Ready(ref ready) = added { - self.notify_listener(ready.hash); - // also notify promoted transactions - for promoted in ready.promoted.iter().copied() { - self.notify_listener(promoted); - } - } + self.notify_ready(&added); Ok(added) } @@ -172,6 +170,16 @@ impl Pool { pool.clear(); } + /// Notifies listeners if the transaction was added to the ready queue. + fn notify_ready(&self, tx: &AddedTransaction) { + if let AddedTransaction::Ready(ready) = tx { + self.notify_listener(ready.hash); + for promoted in ready.promoted.iter().copied() { + self.notify_listener(promoted); + } + } + } + /// notifies all listeners about the transaction fn notify_listener(&self, hash: TxHash) { let mut listener = self.transaction_listener.lock(); diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index e549009d7a5c0..e044bf041132c 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -653,11 +653,11 @@ impl ReadyTransactions { // remove from unlocks for mark in &tx.transaction.transaction.requires { - if let Some(hash) = self.provided_markers.get(mark) - && let Some(tx) = ready.get_mut(hash) - && let Some(idx) = tx.unlocks.iter().position(|i| i == hash) + if let Some(provider_hash) = self.provided_markers.get(mark) + && let Some(provider_tx) = ready.get_mut(provider_hash) + && let Some(idx) = provider_tx.unlocks.iter().position(|i| i == &hash) { - tx.unlocks.swap_remove(idx); + provider_tx.unlocks.swap_remove(idx); } } diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index c29fbd02f2541..57af250a36869 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,16 +1,20 @@ use crate::eth::error::BlockchainError; use alloy_consensus::{Sealed, SignableTransaction}; use alloy_dyn_abi::TypedData; -use alloy_network::TxSignerSync; +use alloy_network::{Network, TxSignerSync}; use alloy_primitives::{Address, B256, Signature, map::AddressHashMap}; use alloy_signer::Signer as AlloySigner; use alloy_signer_local::PrivateKeySigner; use foundry_primitives::{FoundryTxEnvelope, FoundryTypedTx}; use tempo_primitives::TempoSignature; -/// A transaction signer +/// A transaction signer, generic over the network. +/// +/// Modelled after alloy's `NetworkWallet`: the +/// [`sign_transaction_from`](Signer::sign_transaction_from) method takes an +/// unsigned transaction and returns the fully-signed envelope in one step. #[async_trait::async_trait] -pub trait Signer: Send + Sync { +pub trait Signer: Send + Sync { /// returns the available accounts for this signer fn accounts(&self) -> Vec
; @@ -33,12 +37,14 @@ pub trait Signer: Send + Sync { /// Signs the given hash. async fn sign_hash(&self, address: Address, hash: B256) -> Result; - /// signs a transaction request using the given account in request - fn sign_transaction( + /// Signs an unsigned transaction and returns the signed envelope. + /// + /// Mirrors `NetworkWallet::sign_transaction_from`. + fn sign_transaction_from( &self, - request: FoundryTypedTx, - address: &Address, - ) -> Result; + sender: &Address, + tx: N::UnsignedTx, + ) -> Result; } /// Maintains developer keys @@ -56,7 +62,7 @@ impl DevSigner { } #[async_trait::async_trait] -impl Signer for DevSigner { +impl Signer for DevSigner { fn accounts(&self) -> Vec
{ self.addresses.clone() } @@ -92,36 +98,51 @@ impl Signer for DevSigner { Ok(signer.sign_hash(&hash).await?) } - fn sign_transaction( + fn sign_transaction_from( &self, - request: FoundryTypedTx, - address: &Address, - ) -> Result { - let signer = self.accounts.get(address).ok_or(BlockchainError::NoSignerAvailable)?; - match request { - FoundryTypedTx::Legacy(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), - FoundryTypedTx::Eip2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), - FoundryTypedTx::Eip1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), - FoundryTypedTx::Eip7702(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), - FoundryTypedTx::Eip4844(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + sender: &Address, + tx: FoundryTypedTx, + ) -> Result { + let signer = self.accounts.get(sender).ok_or(BlockchainError::NoSignerAvailable)?; + let envelope = match tx { + FoundryTypedTx::Legacy(mut t) => { + let sig = signer.sign_transaction_sync(&mut t)?; + FoundryTxEnvelope::Legacy(t.into_signed(sig)) + } + FoundryTypedTx::Eip2930(mut t) => { + let sig = signer.sign_transaction_sync(&mut t)?; + FoundryTxEnvelope::Eip2930(t.into_signed(sig)) + } + FoundryTypedTx::Eip1559(mut t) => { + let sig = signer.sign_transaction_sync(&mut t)?; + FoundryTxEnvelope::Eip1559(t.into_signed(sig)) + } + FoundryTypedTx::Eip7702(mut t) => { + let sig = signer.sign_transaction_sync(&mut t)?; + FoundryTxEnvelope::Eip7702(t.into_signed(sig)) + } + FoundryTypedTx::Eip4844(mut t) => { + let sig = signer.sign_transaction_sync(&mut t)?; + FoundryTxEnvelope::Eip4844(t.into_signed(sig)) + } FoundryTypedTx::Deposit(_) => { unreachable!("op deposit txs should not be signed") } - FoundryTypedTx::Tempo(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), - } + FoundryTypedTx::Tempo(mut t) => { + let sig = signer.sign_transaction_sync(&mut t)?; + FoundryTxEnvelope::Tempo(t.into_signed(sig.into())) + } + }; + Ok(envelope) } } -/// converts the `request` into a [`FoundryTypedTx`] with the given signature +/// Builds a TxEnvelope from UnsignedTx with a zeroed signature. /// -/// # Errors -/// -/// This will fail if the `signature` contains an erroneous recovery id. -pub fn build_typed_transaction( - request: FoundryTypedTx, - signature: Signature, -) -> Result { - let tx = match request { +/// Used for impersonated accounts, where transactions are accepted without a valid signature. +pub fn build_impersonated(typed_tx: FoundryTypedTx) -> FoundryTxEnvelope { + let signature = Signature::new(Default::default(), Default::default(), false); + match typed_tx { FoundryTypedTx::Legacy(tx) => FoundryTxEnvelope::Legacy(tx.into_signed(signature)), FoundryTypedTx::Eip2930(tx) => FoundryTxEnvelope::Eip2930(tx.into_signed(signature)), FoundryTypedTx::Eip1559(tx) => FoundryTxEnvelope::Eip1559(tx.into_signed(signature)), @@ -132,7 +153,5 @@ pub fn build_typed_transaction( let tempo_sig: TempoSignature = signature.into(); FoundryTxEnvelope::Tempo(tx.into_signed(tempo_sig)) } - }; - - Ok(tx) + } } diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm.rs index 295a3ef0d1fe2..65b718e4a767a 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm.rs @@ -36,9 +36,12 @@ mod tests { primitives::hardfork::SpecId, }; - // A precompile activated in the `Prague` spec. + // A precompile activated in the `Prague` spec (BLS12-381 G2 map). const ETH_PRAGUE_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000011"); + // A precompile activated in the `Osaka` spec (EIP-7951). + const ETH_OSAKA_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); + // A precompile activated in the `Isthmus` spec. const OP_ISTHMUS_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); @@ -136,7 +139,7 @@ mod tests { chain.operator_fee_scalar = Some(U256::from(0)); } - let op_cfg = op_env.evm_env.cfg_env.clone().with_spec(op_spec); + let op_cfg: CfgEnv = CfgEnv::new_with_spec(op_spec); let op_evm_context = OpContext { journaled_state: { let mut journal = Journal::new(EmptyDB::default()); @@ -167,10 +170,13 @@ mod tests { } #[test] - fn build_eth_evm_with_extra_precompiles_default_spec() { - let (env, mut evm) = create_eth_evm(SpecId::default()); + fn build_eth_evm_with_extra_precompiles_osaka_spec() { + let (env, mut evm) = create_eth_evm(SpecId::OSAKA); + + // Check that the Osaka precompile IS present when using the Osaka spec. + assert!(evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE)); - // Check that the Prague precompile IS present when using the default spec. + // Check that the Prague precompile IS present when using the Osaka spec. assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); @@ -192,6 +198,9 @@ mod tests { fn build_eth_evm_with_extra_precompiles_london_spec() { let (env, mut evm) = create_eth_evm(SpecId::LONDON); + // Check that the Osaka precompile IS NOT present when using the London spec. + assert!(!evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE)); + // Check that the Prague precompile IS NOT present when using the London spec. assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); @@ -211,13 +220,38 @@ mod tests { } #[test] - fn build_op_evm_with_extra_precompiles_default_spec() { - let (env, mut evm) = create_op_evm(SpecId::default(), OpSpecId::default()); + fn build_eth_evm_with_extra_precompiles_prague_spec() { + let (env, mut evm) = create_eth_evm(SpecId::PRAGUE); + + // Check that the Osaka precompile IS NOT present when using the Prague spec. + assert!(!evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE)); + + // Check that the Prague precompile IS present when using the Prague spec. + assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); + + assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); + + assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + let result = match &mut evm { + EitherEvm::Eth(eth_evm) => eth_evm.transact(env.tx).unwrap(), + _ => unreachable!(), + }; + + assert!(result.result.is_success()); + assert_eq!(result.result.output(), Some(&PAYLOAD.into())); + } + + #[test] + fn build_op_evm_with_extra_precompiles_isthmus_spec() { + let (env, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::ISTHMUS); - // Check that the Isthmus precompile IS present when using the default spec. + // Check that the Isthmus precompile IS present when using the Isthmus spec. assert!(evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); - // Check that the Prague precompile IS present when using the default spec. + // Check that the Prague precompile IS present when using the Isthmus spec. assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); @@ -237,7 +271,7 @@ mod tests { #[test] fn build_op_evm_with_extra_precompiles_bedrock_spec() { - let (env, mut evm) = create_op_evm(SpecId::default(), OpSpecId::BEDROCK); + let (env, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::BEDROCK); // Check that the Isthmus precompile IS NOT present when using the `OpSpecId::BEDROCK` spec. assert!(!evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 9d1e0958e99fe..a23be64cf89be 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -7,7 +7,10 @@ use crate::{ use alloy_primitives::{TxHash, map::HashMap}; use alloy_rpc_types::{Filter, FilteredParams, Log}; use anvil_core::eth::subscription::SubscriptionId; -use anvil_rpc::response::ResponseResult; +use anvil_rpc::{ + error::{ErrorCode, RpcError}, + response::ResponseResult, +}; use futures::{Stream, StreamExt, channel::mpsc::Receiver}; use std::{ pin::Pin, @@ -55,7 +58,11 @@ impl Filters { } } warn!(target: "node::filter", "No filter found for {}", id); - ResponseResult::success(Vec::<()>::new()) + ResponseResult::error(RpcError { + code: ErrorCode::ServerError(-32000), + message: "filter not found".into(), + data: None, + }) } /// Returns the original `Filter` of an `eth_newFilter` diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 82b1f6f356d25..af0d05657e476 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -184,7 +184,8 @@ pub async fn try_spawn(mut config: NodeConfig) -> Result<(EthApi, NodeHandle)> { _ => Miner::new(mode), }; - let dev_signer: Box = Box::new(DevSigner::new(signer_accounts)); + let dev_signer: Box> = + Box::new(DevSigner::new(signer_accounts)); let mut signers = vec![dev_signer]; if let Some(genesis) = genesis { let genesis_signers = genesis diff --git a/crates/anvil/test-data/state-dump.json b/crates/anvil/test-data/state-dump.json index a60e3c861c107..e7a1e37f62809 100644 --- a/crates/anvil/test-data/state-dump.json +++ b/crates/anvil/test-data/state-dump.json @@ -1 +1,314 @@ -{"block":{"number":2,"beneficiary":"0x0000000000000000000000000000000000000000","timestamp":1724763179,"gas_limit":30000000,"basefee":875175000,"difficulty":"0x0","prevrandao":"0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a","blob_excess_gas_and_price":{"excess_blob_gas":0,"blob_gasprice":1}},"accounts":{"0x0000000000000000000000000000000000000000":{"nonce":0,"balance":"0xa410","code":"0x","storage":{}},"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x4e59b44847b379578588920ca78fbf26c0b4956c":{"nonce":0,"balance":"0x0","code":"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"nonce":1,"balance":"0x21e19e0b90393da9b38","code":"0x","storage":{}},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"nonce":0,"balance":"0x21e19e0c9bab2400000","code":"0x","storage":{}},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"nonce":1,"balance":"0x21e19e0b6a140b55df8","code":"0x","storage":{}}},"best_block_number":2,"blocks":[{"header":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x1c9c380","gasUsed":"0x0","timestamp":"0x66cdcc25","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[],"ommers":[]},{"header":{"parentHash":"0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175","transactionsRoot":"0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x1","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc29","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x3b9aca00","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","value":"0x0","accessList":[],"input":"0x","r":"0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853","s":"0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0","yParity":"0x0","hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","type":"0x2"},"impersonated_sender":null}],"ommers":[]},{"header":{"parentHash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1","transactionsRoot":"0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988","receiptsRoot":"0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x2","gasLimit":"0x1c9c380","gasUsed":"0x5208","timestamp":"0x66cdcc2b","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x342a1c58","blobGasUsed":"0x0","excessBlobGas":"0x0","extraData":"0x"},"transactions":[{"transaction":{"chainId":"0x7a69","nonce":"0x0","gas":"0x5209","maxFeePerGas":"0x77359401","maxPriorityFeePerGas":"0x1","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","value":"0x0","accessList":[],"input":"0x","r":"0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0","s":"0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd","yParity":"0x0","hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","type":"0x2"},"impersonated_sender":null}],"ommers":[]}],"transactions":[{"info":{"transaction_hash":"0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3","transaction_index":0,"from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70","block_number":1},{"info":{"transaction_hash":"0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515","transaction_index":0,"from":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","to":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","contract_address":null,"traces":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","maybe_precompile":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}],"exit":"Stop","out":"0x","nonce":0,"gas_used":21000},"receipt":{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"block_hash":"0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0","block_number":2}]} \ No newline at end of file +{ + "block": { + "number": 2, + "beneficiary": "0x0000000000000000000000000000000000000000", + "timestamp": 1724763179, + "gas_limit": 30000000, + "basefee": 875175000, + "difficulty": "0x0", + "prevrandao": "0xdb639d7f8af4f0ff2aa9cc49861820e72f5f8bfeeed677d1e3569f6b1625df4a", + "blob_excess_gas_and_price": { + "excess_blob_gas": 0, + "blob_gasprice": 1 + } + }, + "accounts": { + "0x0000000000000000000000000000000000000000": { + "nonce": 0, + "balance": "0xa410", + "code": "0x", + "storage": {} + }, + "0x14dc79964da2c08b23698b3d3cc7ca32193d9955": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x4e59b44847b379578588920ca78fbf26c0b4956c": { + "nonce": 0, + "balance": "0x0", + "code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3", + "storage": {} + }, + "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { + "nonce": 1, + "balance": "0x21e19e0b90393da9b38", + "code": "0x", + "storage": {} + }, + "0x90f79bf6eb2c4f870365e785982e1f101e93b906": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x976ea74026e726554db657fa54763abd0c3a0aa9": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { + "nonce": 0, + "balance": "0x21e19e0c9bab2400000", + "code": "0x", + "storage": {} + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "nonce": 1, + "balance": "0x21e19e0b6a140b55df8", + "code": "0x", + "storage": {} + } + }, + "best_block_number": 2, + "blocks": [ + { + "header": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x0", + "number": "0x0", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x66cdcc25", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x3b9aca00", + "blobGasUsed": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x" + }, + "transactions": [], + "ommers": [] + }, + { + "header": { + "parentHash": "0x3a52101c98a4319c419681131d3585d70a6cf13a9af25136be20d451eed5480a", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x6e5f60b37eeaece7dedfc42cc394731a0ae3ed3d3be93c402780b2e23e141175", + "transactionsRoot": "0x9ceaeb1b16b924afbf4bf4df4c2c49dc9cfbe23ac7a40bf26a704158ea2d352f", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x0", + "number": "0x1", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "timestamp": "0x66cdcc29", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x3b9aca00", + "blobGasUsed": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x" + }, + "transactions": [ + { + "transaction": { + "chainId": "0x7a69", + "nonce": "0x0", + "gas": "0x5209", + "maxFeePerGas": "0x77359401", + "maxPriorityFeePerGas": "0x1", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "value": "0x0", + "accessList": [], + "input": "0x", + "r": "0x703a4b4d6dbff2fa2345df73263df2098faa7214863b5ec82c4c07162d87b853", + "s": "0x17dea762c4ce600ad1d9d2c9ae6dd35b9e526d03c875f868ad0792fd4fad72e0", + "yParity": "0x0", + "hash": "0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3", + "type": "0x2" + }, + "impersonated_sender": null + } + ], + "ommers": [] + }, + { + "header": { + "parentHash": "0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0xe1423fd180478ab4fd05a7103277d64496b15eb914ecafe71eeec871b552efd1", + "transactionsRoot": "0x2b5598ef261e5f88e4303bb2b3986b3d5c0ebf4cd9977daebccae82a6469b988", + "receiptsRoot": "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x0", + "number": "0x2", + "gasLimit": "0x1c9c380", + "gasUsed": "0x5208", + "timestamp": "0x66cdcc2b", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x342a1c58", + "blobGasUsed": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x" + }, + "transactions": [ + { + "transaction": { + "chainId": "0x7a69", + "nonce": "0x0", + "gas": "0x5209", + "maxFeePerGas": "0x77359401", + "maxPriorityFeePerGas": "0x1", + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "value": "0x0", + "accessList": [], + "input": "0x", + "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0", + "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd", + "yParity": "0x0", + "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515", + "type": "0x2" + }, + "impersonated_sender": null + } + ], + "ommers": [] + } + ], + "transactions": [ + { + "info": { + "transaction_hash": "0xf8d5fb22350f52ae8c30cd7f6969eb73de849c8dc010f4215d4c5c24824fe2b3", + "transaction_index": 0, + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "contract_address": null, + "traces": [ + { + "parent": null, + "children": [], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "address": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "maybe_precompile": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0x", + "output": "0x", + "gas_used": 0, + "gas_limit": 1, + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + }, + "gas_refund_counter": 0 + }, + "logs": [], + "ordering": [] + } + ], + "exit": "Stop", + "out": "0x", + "nonce": 0, + "gas_used": 21000 + }, + "receipt": { + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "block_hash": "0x0d575f9ca968cd483549172245483a12343afc3cabef80f0fa39855b10b98c70", + "block_number": 1 + }, + { + "info": { + "transaction_hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515", + "transaction_index": 0, + "from": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "contract_address": null, + "traces": [ + { + "parent": null, + "children": [], + "idx": 0, + "trace": { + "depth": 0, + "success": true, + "caller": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "address": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "maybe_precompile": null, + "selfdestruct_refund_target": null, + "selfdestruct_transferred_value": null, + "kind": "CALL", + "value": "0x0", + "data": "0x", + "output": "0x", + "gas_used": 0, + "gas_limit": 1, + "status": "Stop", + "steps": [], + "decoded": { + "label": null, + "return_data": null, + "call_data": null + }, + "gas_refund_counter": 0 + }, + "logs": [], + "ordering": [] + } + ], + "exit": "Stop", + "out": "0x", + "nonce": 0, + "gas_used": 21000 + }, + "receipt": { + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "block_hash": "0x1f435a603c1bf6d544a90156b572b96d7a1730028422d800839bae78bb3506d0", + "block_number": 2 + } + ] +} diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index e675001d7a480..c5fb4688bf1d5 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -658,8 +658,7 @@ async fn can_remove_pool_transactions() { } #[tokio::test(flavor = "multi_thread")] -#[ignore = "flaky"] -async fn test_reorg() { +async fn flaky_test_reorg() { let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.http_provider(); @@ -709,7 +708,8 @@ async fn test_reorg() { // Verify that historic blocks are still accessible for num in (0..14).rev() { - let _ = provider.get_block_by_number(num.into()).full().await.unwrap(); + let block = provider.get_block_by_number(num.into()).full().await.unwrap(); + assert!(block.is_some(), "Historic block {num} should be accessible after reorg"); } // Send a few more transaction to verify the chain can still progress @@ -787,6 +787,126 @@ async fn test_reorg() { assert!(res.is_err()); } +#[tokio::test(flavor = "multi_thread")] +async fn test_reorg_blockhash_opcode_consistency() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let multicall = Multicall::deploy(&provider).await.unwrap(); + + api.evm_mine(Some(MineOptions::Options { timestamp: None, blocks: Some(200) })).await.unwrap(); + + let tip_before_reorg = api.block_number().unwrap().to::(); + + let mut cached_hashes: Vec<(u64, alloy_primitives::B256, alloy_primitives::B256)> = Vec::new(); + for i in 1..=10 { + let block_num = tip_before_reorg - i; + let rpc_hash = + provider.get_block_by_number(block_num.into()).await.unwrap().unwrap().header.hash; + let opcode_hash = multicall.getBlockHash(U256::from(block_num)).call().await.unwrap(); + assert_eq!(rpc_hash, opcode_hash, "RPC and BLOCKHASH opcode should match before reorg"); + cached_hashes.push((block_num, rpc_hash, opcode_hash)); + } + + let tx = TransactionRequest::default(); + api.anvil_reorg(ReorgOptions { + depth: 5, + tx_block_pairs: vec![(TransactionData::JSON(tx), 0)], + }) + .await + .unwrap(); + + api.mine_one().await; + + for (block_num, rpc_before, opcode_before) in &cached_hashes { + let rpc_after = + provider.get_block_by_number((*block_num).into()).await.unwrap().unwrap().header.hash; + let opcode_after = multicall.getBlockHash(U256::from(*block_num)).call().await.unwrap(); + if *block_num <= tip_before_reorg.saturating_sub(5) { + assert_eq!( + rpc_after, *rpc_before, + "Block {block_num}: hash should not change for non-reorged blocks" + ); + assert_eq!( + opcode_after, *opcode_before, + "Block {block_num}: BLOCKHASH should not change for non-reorged blocks" + ); + } + assert_eq!( + rpc_after, opcode_after, + "Block {block_num}: RPC ({rpc_after}) and BLOCKHASH opcode ({opcode_after}) should match after reorg" + ); + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_reorg_deep_blockhash_consistency() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let multicall = Multicall::deploy(&provider).await.unwrap(); + + // Mine 300 blocks (more than BLOCKHASH limit of 256) + api.evm_mine(Some(MineOptions::Options { timestamp: None, blocks: Some(300) })).await.unwrap(); + + let tip_before_reorg = api.block_number().unwrap().to::(); + assert!(tip_before_reorg > 256, "Need more than 256 blocks for this test"); + + // Check blocks within the 256 window before reorg + let mut cached_hashes: Vec<(u64, alloy_primitives::B256)> = Vec::new(); + for i in [1, 10, 100, 200, 255] { + let block_num = tip_before_reorg - i; + let rpc_hash = + provider.get_block_by_number(block_num.into()).await.unwrap().unwrap().header.hash; + let opcode_hash = multicall.getBlockHash(U256::from(block_num)).call().await.unwrap(); + assert_eq!(rpc_hash, opcode_hash, "RPC and BLOCKHASH opcode should match before reorg"); + cached_hashes.push((block_num, rpc_hash)); + } + + // Perform a deep reorg (50 blocks) + let tx = TransactionRequest::default(); + api.anvil_reorg(ReorgOptions { + depth: 50, + tx_block_pairs: vec![(TransactionData::JSON(tx), 0)], + }) + .await + .unwrap(); + + api.mine_one().await; + + let tip_after_reorg = api.block_number().unwrap().to::(); + + // Verify blocks still in the 256 window have consistent hashes + for (block_num, rpc_before) in &cached_hashes { + // Skip blocks that were reorged + if *block_num > tip_after_reorg - 50 { + continue; + } + let rpc_after = + provider.get_block_by_number((*block_num).into()).await.unwrap().unwrap().header.hash; + let opcode_after = multicall.getBlockHash(U256::from(*block_num)).call().await.unwrap(); + assert_eq!( + rpc_after, *rpc_before, + "Block {block_num}: hash should not change for non-reorged blocks" + ); + assert_eq!( + rpc_after, opcode_after, + "Block {block_num}: RPC and BLOCKHASH should match after deep reorg" + ); + } + + // Verify BLOCKHASH returns 0 for blocks outside the 256 window + let old_block = tip_after_reorg.saturating_sub(257); + if old_block > 0 { + let opcode_hash = multicall.getBlockHash(U256::from(old_block)).call().await.unwrap(); + assert_eq!( + opcode_hash, + alloy_primitives::B256::ZERO, + "BLOCKHASH should return 0 for blocks outside 256 window" + ); + } +} + #[tokio::test(flavor = "multi_thread")] async fn test_rollback() { let (api, handle) = spawn(NodeConfig::test()).await; diff --git a/crates/anvil/tests/it/beacon_api.rs b/crates/anvil/tests/it/beacon_api.rs index 5c6c9b2486fa8..ec0f13c02e7c0 100644 --- a/crates/anvil/tests/it/beacon_api.rs +++ b/crates/anvil/tests/it/beacon_api.rs @@ -66,8 +66,7 @@ async fn test_beacon_api_get_blobs() { .with_blob_sidecar(sidecar) .value(U256::from(100)); - let mut tx = WithOtherFields::new(tx); - tx.populate_blob_hashes(); + let tx = WithOtherFields::new(tx); let pending = provider.send_transaction(tx).await.unwrap(); pending_txs.push(pending); diff --git a/crates/anvil/tests/it/eip2935.rs b/crates/anvil/tests/it/eip2935.rs new file mode 100644 index 0000000000000..8bb2beef9605b --- /dev/null +++ b/crates/anvil/tests/it/eip2935.rs @@ -0,0 +1,70 @@ +use crate::utils::http_provider; +use alloy_eips::{BlockNumberOrTag, eip2935::HISTORY_STORAGE_ADDRESS}; +use alloy_network::TransactionBuilder; +use alloy_primitives::U256; +use alloy_provider::Provider; +use alloy_rpc_types::TransactionRequest; +use anvil::{NodeConfig, spawn}; +use foundry_evm::hardfork::EthereumHardfork; + +#[tokio::test(flavor = "multi_thread")] +async fn eip2935_contract_deployed_at_genesis() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); + let (_api, handle) = spawn(node_config).await; + let provider = http_provider(&handle.http_endpoint()); + + let code = provider.get_code_at(HISTORY_STORAGE_ADDRESS).await.unwrap(); + assert!(!code.is_empty(), "EIP-2935 history storage contract should be deployed at genesis"); +} + +#[tokio::test(flavor = "multi_thread")] +async fn eip2935_stores_parent_block_hash() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); + let (api, handle) = spawn(node_config).await; + let provider = http_provider(&handle.http_endpoint()); + + // Mine a few blocks so there are parent hashes to store + api.mine_one().await; + api.mine_one().await; + api.mine_one().await; + + // Block 1's hash should be stored when block 2 was mined + let block1 = provider + .get_block_by_number(BlockNumberOrTag::from(1)) + .await + .unwrap() + .expect("block 1 should exist"); + let block1_hash = block1.header.hash; + + // Query the history storage contract for block 1's hash. + // The EIP-2935 contract uses raw calldata (not ABI-encoded): pass the block number + // as a 32-byte big-endian word directly. + let call_data: [u8; 32] = U256::from(1).to_be_bytes(); + let tx = TransactionRequest::default().with_to(HISTORY_STORAGE_ADDRESS).with_input(call_data); + let result = provider.call(tx.into()).await.unwrap(); + + let stored_hash = alloy_primitives::B256::from_slice(&result); + assert_eq!(stored_hash, block1_hash, "EIP-2935 contract should store parent block hash"); +} + +#[tokio::test(flavor = "multi_thread")] +async fn eip2935_no_system_call_on_genesis() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); + let (_api, handle) = spawn(node_config).await; + let provider = http_provider(&handle.http_endpoint()); + + // At genesis (block 0), the contract should exist but no system call should have + // written any parent hash into its storage. Check raw storage slot 0 directly. + let slot = provider.get_storage_at(HISTORY_STORAGE_ADDRESS, U256::from(0)).await.unwrap(); + assert_eq!(slot, U256::ZERO, "No hash should be stored in the contract at genesis"); +} + +#[tokio::test(flavor = "multi_thread")] +async fn eip2935_not_deployed_before_prague() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); + let (_api, handle) = spawn(node_config).await; + let provider = http_provider(&handle.http_endpoint()); + + let code = provider.get_code_at(HISTORY_STORAGE_ADDRESS).await.unwrap(); + assert!(code.is_empty(), "EIP-2935 contract should NOT be deployed before Prague"); +} diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 11200e5667206..ddc8c91eefda7 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -39,9 +39,7 @@ async fn can_send_eip4844_transaction() { .with_blob_sidecar(sidecar) .value(U256::from(5)); - let mut tx = WithOtherFields::new(tx); - - tx.populate_blob_hashes(); + let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -103,6 +101,29 @@ async fn can_send_eip4844_transaction_eth_send_transaction() { let _blobs = api.anvil_get_blob_by_tx_hash(tx_hash).unwrap().unwrap(); } +// +#[tokio::test(flavor = "multi_thread")] +async fn can_send_eip4844_transaction_with_eip7594_sidecar_format() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Osaka.into())); + let (api, handle) = spawn(node_config).await; + let provider = ProviderBuilder::new().connect(handle.http_endpoint().as_str()).await.unwrap(); + let accounts = provider.get_accounts().await.unwrap(); + let alice = accounts[0]; + let bob = accounts[1]; + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Blobs are fun!"); + let sidecar = sidecar.build_7594().unwrap(); + + let mut tx = TransactionRequest::default().with_from(alice).with_to(bob); + alloy_network::TransactionBuilder7594::set_blob_sidecar_7594(&mut tx, sidecar); + + let pending_tx = provider.send_transaction(tx).await.unwrap(); + let receipt = pending_tx.get_receipt().await.unwrap(); + let tx_hash = receipt.transaction_hash; + + let _blobs = api.anvil_get_blob_by_tx_hash(tx_hash).unwrap().unwrap(); +} + #[tokio::test(flavor = "multi_thread")] async fn can_send_multiple_blobs_in_one_tx() { let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into())); @@ -131,9 +152,7 @@ async fn can_send_multiple_blobs_in_one_tx() { .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 tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -169,9 +188,7 @@ async fn cannot_exceed_six_blobs() { .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 tx = WithOtherFields::new(tx); let err = provider.send_transaction(tx).await.unwrap_err(); @@ -211,8 +228,6 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { .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]; @@ -224,7 +239,6 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { 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; @@ -428,9 +442,7 @@ async fn can_get_blobs_by_versioned_hash() { .with_blob_sidecar(sidecar.clone()) .value(U256::from(5)); - let mut tx = WithOtherFields::new(tx); - - tx.populate_blob_hashes(); + let tx = WithOtherFields::new(tx); let _receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -466,10 +478,7 @@ async fn can_get_blobs_by_tx_hash() { .with_blob_sidecar(sidecar.clone()) .value(U256::from(5)); - let mut tx = WithOtherFields::new(tx); - - tx.populate_blob_hashes(); - + let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let hash = receipt.transaction_hash; api.anvil_set_auto_mine(true).await.unwrap(); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index d5df1ca93382a..139e9e6b9bdbd 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -830,7 +830,7 @@ async fn test_fork_init_base_fee() { } #[tokio::test(flavor = "multi_thread")] -async fn test_reset_fork_on_new_blocks() { +async fn flaky_test_reset_fork_on_new_blocks() { let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(rpc::next_http_archive_rpc_url()))).await; @@ -1202,7 +1202,7 @@ async fn test_fork_reset_basefee() { // #[tokio::test(flavor = "multi_thread")] -async fn test_arbitrum_fork_dev_balance() { +async fn flaky_test_arbitrum_fork_dev_balance() { let (api, handle) = spawn( fork_config() .with_fork_block_number(None::) @@ -1240,7 +1240,7 @@ async fn test_arb_fork_mining() { // #[tokio::test(flavor = "multi_thread")] -async fn test_arbitrum_fork_block_number() { +async fn flaky_test_arbitrum_fork_block_number() { // fork to get initial block for test let (_, handle) = spawn( fork_config() diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index 4a52391f776c2..e208093cfe62b 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -3,6 +3,7 @@ mod anvil; mod anvil_api; mod api; mod beacon_api; +mod eip2935; mod eip4844; mod eip7702; mod fork; diff --git a/crates/anvil/tests/it/proof.rs b/crates/anvil/tests/it/proof.rs index abf0fc0d077f7..52c633b058790 100644 --- a/crates/anvil/tests/it/proof.rs +++ b/crates/anvil/tests/it/proof.rs @@ -55,14 +55,17 @@ async fn test_account_proof() { .await .unwrap(); + // Note: proof values account for EIP-2935 history storage contract deployed at genesis. verify_account_proof(&api, address!("0x2031f89b3ea8014eb51a78c316e42af3e0d7695f"), [ - "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf851808080808080a0347892f3eeb6af4d4574ad0b89706247951bbbece83ef27efc46eb96436598b5808080a07e35bed15a14b4072a0929310da6a26e34d7017a82cca3589d7d0badb53de2e3808080808080", + "0xe217a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", - "0xf8719f31355ec1c8f7e26bb3ccbcb0b75d870d15846c0b98e5cc452db46c37faea40b84ff84d80890270801d946c940000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + "0xf8719f31355ec1c8f7e26bb3ccbcb0b75d870d15846c0b98e5cc452db46c37faea40b84ff84d80890270801d946c940000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ]).await; verify_account_proof(&api, address!("0x33f0fc440b8477fcfbe9d0bf8649e7dea9baedb2"), [ - "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf851808080808080a0347892f3eeb6af4d4574ad0b89706247951bbbece83ef27efc46eb96436598b5808080a07e35bed15a14b4072a0929310da6a26e34d7017a82cca3589d7d0badb53de2e3808080808080", + "0xe217a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", "0xe48200d3a0ef957210bca5b9b402d614eb8408c88cfbf4913eb6ab83ca233c8b8f0e626b54", "0xf851808080a02743a5addaf4cf9b8c0c073e1eaa555deaaf8c41cb2b41958e88624fa45c2d908080808080a0bfbf6937911dfb88113fecdaa6bde822e4e99dae62489fcf61a91cb2f36793d680808080808080", @@ -70,17 +73,19 @@ async fn test_account_proof() { ]).await; verify_account_proof(&api, address!("0x62b0dd4aab2b1a0a04e279e2b828791a10755528"), [ - "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf851808080808080a0347892f3eeb6af4d4574ad0b89706247951bbbece83ef27efc46eb96436598b5808080a07e35bed15a14b4072a0929310da6a26e34d7017a82cca3589d7d0badb53de2e3808080808080", + "0xe217a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", "0xf8709f3936599f93b769acf90c7178fd2ddcac1b5b4bc9949ee5a04b7e0823c2446eb84ef84c80880f43fc2c04ee0000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ]).await; verify_account_proof(&api, address!("0x1ed9b1dd266b607ee278726d324b855a093394a6"), [ - "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf851808080808080a0347892f3eeb6af4d4574ad0b89706247951bbbece83ef27efc46eb96436598b5808080a07e35bed15a14b4072a0929310da6a26e34d7017a82cca3589d7d0badb53de2e3808080808080", + "0xe217a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", "0xe48200d3a0ef957210bca5b9b402d614eb8408c88cfbf4913eb6ab83ca233c8b8f0e626b54", "0xf851808080a02743a5addaf4cf9b8c0c073e1eaa555deaaf8c41cb2b41958e88624fa45c2d908080808080a0bfbf6937911dfb88113fecdaa6bde822e4e99dae62489fcf61a91cb2f36793d680808080808080", - "0xf86f9e207a32b8ab5eb4b043c65b1f00c93f517bc8883c5cd31baf8e8a279475e3b84ef84c808801aa535d3d0c0000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + "0xf86f9e207a32b8ab5eb4b043c65b1f00c93f517bc8883c5cd31baf8e8a279475e3b84ef84c808801aa535d3d0c0000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ]).await; } diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index ad542d73019db..2c677231e07ad 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -48,6 +48,65 @@ async fn can_load_state() { assert_eq!(num, U256::from(num_from_tag)); } +// +#[tokio::test(flavor = "multi_thread")] +async fn finalized_block_hash_consistent_after_load_state() { + use alloy_eips::BlockNumberOrTag; + + let tmp = tempfile::tempdir().unwrap(); + let state_file = tmp.path().join("state.json"); + + let (api, _handle) = spawn(NodeConfig::test()).await; + + api.mine_one().await; + + // Get the original genesis block hash + let original_genesis = api.block_by_number(BlockNumberOrTag::Number(0)).await.unwrap().unwrap(); + let original_genesis_hash = original_genesis.header.hash; + + let state = api.serialized_state(false).await.unwrap(); + foundry_common::fs::write_json_file(&state_file, &state).unwrap(); + + // Load state with a different genesis timestamp. + // The new instance will create its own genesis block with a different timestamp, + // but then load_state should overwrite it. The bug is that genesis_hash field isn't updated. + let (api, _handle) = spawn( + NodeConfig::test() + .with_genesis_timestamp(Some(original_genesis.header.timestamp + 1000)) + .with_init_state_path(state_file), + ) + .await; + + // Query finalized block - should return genesis (block 0) since best_number is small + let finalized_block = api.block_by_number(BlockNumberOrTag::Finalized).await.unwrap().unwrap(); + let finalized_hash = finalized_block.header.hash; + let finalized_number = finalized_block.header.number; + + // Query block by the finalized block's number directly + let block_by_number = + api.block_by_number(BlockNumberOrTag::Number(finalized_number)).await.unwrap().unwrap(); + let block_by_number_hash = block_by_number.header.hash; + + // Verify the loaded genesis matches the original + assert_eq!( + block_by_number_hash, original_genesis_hash, + "Loaded genesis should match original genesis hash" + ); + + // Both finalized and block 0 should return the same hash + assert_eq!( + finalized_hash, block_by_number_hash, + "Finalized block hash should match block queried by number" + ); + + // Also verify Earliest block tag returns consistent hash + let earliest_block = api.block_by_number(BlockNumberOrTag::Earliest).await.unwrap().unwrap(); + assert_eq!( + earliest_block.header.hash, original_genesis_hash, + "Earliest block hash should match original genesis hash" + ); +} + #[tokio::test(flavor = "multi_thread")] async fn can_load_existing_state_legacy() { let state_file = "test-data/state-dump-legacy.json"; @@ -666,6 +725,7 @@ async fn test_backward_compatibility_state_dump_deserialization_v1_2() { "output": "0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80633fb5c1cb146100435780638381f58a1461005f578063d09de08a1461007d575b5f5ffd5b61005d600480360381019061005891906100e4565b610087565b005b610067610090565b604051610074919061011e565b60405180910390f35b610085610095565b005b805f8190555050565b5f5481565b5f5f8154809291906100a690610164565b9190505550565b5f5ffd5b5f819050919050565b6100c3816100b1565b81146100cd575f5ffd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101a05761019f610137565b5b60018201905091905056fea264697066735822122040b6a3cd3ec8f890002f39a8719ebee029ba9bac3d7fa9d581d4712cfe9ffec264736f6c634300081e0033", "gas_used": 96345, "gas_limit": 143385, + "gas_refund_counter": 0, "status": "Return", "steps": [], "decoded": null diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 5bf7e1afb5457..916dcbb728f63 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -25,7 +25,7 @@ use alloy_rpc_types::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, PreStateConfig, PreStateFrame, }, - parity::{Action, LocalizedTransactionTrace}, + parity::{Action, ChangedType, LocalizedTransactionTrace, TraceType}, }, }; use alloy_serde::WithOtherFields; @@ -777,8 +777,7 @@ async fn test_trace_address_fork2() { } #[tokio::test(flavor = "multi_thread")] -#[ignore = "flaky"] -async fn test_trace_filter() { +async fn flaky_test_trace_filter() { let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.ws_provider(); @@ -1244,7 +1243,7 @@ async fn test_debug_trace_transaction_pre_state_tracer() { "nonce": 1 }, "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { - "balance": "0x56bc75e2d630fffff" + "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512": { "balance": "0x0", @@ -1274,3 +1273,81 @@ async fn test_debug_trace_transaction_pre_state_tracer() { _ => unreachable!(), } } + +#[tokio::test(flavor = "multi_thread")] +async fn test_trace_replay_block_transactions_local() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + api.anvil_set_auto_mine(false).await.unwrap(); + + let accounts = handle.dev_wallets().collect::>(); + let from = accounts[0].address(); + let to = accounts[1].address(); + let amount = U256::from(1000000u64); + + // Send first transaction + let tx1 = TransactionRequest::default().to(to).value(amount).from(from); + let tx1 = WithOtherFields::new(tx1); + let pending_tx1 = provider.send_transaction(tx1).await.unwrap(); + + // Send second transaction with different value + let tx2 = TransactionRequest::default().to(to).value(amount).from(from); + let tx2 = WithOtherFields::new(tx2); + let pending_tx2 = provider.send_transaction(tx2).await.unwrap(); + + api.mine_one().await; + let receipt1 = pending_tx1.get_receipt().await.unwrap(); + let receipt2 = pending_tx2.get_receipt().await.unwrap(); + + let block_number = receipt2.block_number.unwrap(); + + // Replay the block transactions with call trace type + // Pass block number as hex string as per Ethereum RPC spec + let results = api + .trace_replay_block_transactions( + block_number.into(), + vec![TraceType::Trace, TraceType::VmTrace, TraceType::StateDiff].into_iter().collect(), + ) + .await + .unwrap(); + + // Verify we have traces for both transactions + assert_eq!(results.len(), 2, "Should have traces for 2 transactions"); + + // Verify first transaction hash matches + assert_eq!(results[0].transaction_hash, receipt1.transaction_hash); + + // Verify second transaction hash matches + assert_eq!(results[1].transaction_hash, receipt2.transaction_hash); + + // Verify trace types are present and accurate + for result in results { + let full_trace = &result.full_trace; + + // Verify Trace (call trace) is present and accurate + assert!(!full_trace.trace.is_empty(), "Trace should not be empty"); + let first_trace = &full_trace.trace[0]; + match &first_trace.action { + Action::Call(call) => { + assert_eq!(call.from, from, "Call from address should match"); + assert_eq!(call.to, to, "Call to address should match"); + } + _ => panic!("Expected Call action, got {:?}", first_trace.action), + } + + // Verify VmTrace is present + assert!(full_trace.vm_trace.is_some(), "VmTrace should be present when requested"); + + // Verify StateDiff is present + assert!(full_trace.state_diff.is_some(), "StateDiff should be present when requested"); + // Verify balance change is correct in state diff + let ChangedType:: { from, to } = + full_trace.state_diff.as_ref().unwrap().get(&to).unwrap().balance.as_changed().unwrap(); + assert_eq!( + to.checked_sub(*from).unwrap(), + amount, + "Incorrect balance change in state diff" + ); + } +} diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index fcbf2647e2cf0..86836de97cae6 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -3,7 +3,7 @@ use crate::{ utils::{connect_pubsub, http_provider_with_signer}, }; use alloy_consensus::Transaction; -use alloy_network::{EthereumWallet, TransactionBuilder, TransactionResponse}; +use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder, TransactionResponse}; use alloy_primitives::{Address, Bytes, FixedBytes, U256, address, hex, map::B256HashSet}; use alloy_provider::{Provider, WsConnect}; use alloy_rpc_types::{ @@ -899,6 +899,42 @@ async fn test_tx_receipt() { assert!(tx.contract_address.is_some()); } +// +#[tokio::test(flavor = "multi_thread")] +async fn test_reverted_contract_creation_has_contract_address() { + let (_api, handle) = spawn(NodeConfig::test()).await; + + let provider = handle.http_provider(); + let wallet = handle.dev_wallets().next().unwrap(); + + // Init code that immediately reverts: PUSH1 0x00 PUSH1 0x00 REVERT (0x60006000fd) + let reverting_init_code = hex!("60006000fd"); + + let tx = TransactionRequest::default() + .from(wallet.address()) + .with_input(reverting_init_code.to_vec()); + + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + // Transaction should have reverted + assert!(!receipt.status()); + + // `to` field should be none (contract creation) + assert!(receipt.to.is_none()); + + // `contractAddress` should still be set even though the transaction reverted + // This matches geth's behavior: https://github.com/ethereum/go-ethereum/issues/27937 + assert!( + receipt.contract_address.is_some(), + "contractAddress should be set for reverted contract creation" + ); + + // Verify the computed address is correct (sender.create(nonce)) + let expected_addr = wallet.address().create(0); + assert_eq!(receipt.contract_address, Some(expected_addr)); +} + #[tokio::test(flavor = "multi_thread")] async fn can_stream_pending_transactions() { let (_api, handle) = diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index cf4966a86c15b..3188ade007e3c 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -33,6 +33,8 @@ use std::time::Instant; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let args = CastArgs::parse(); args.global.init()?; args.global.tokio_runtime().block_on(run_command(args)) @@ -354,7 +356,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { } CastSubcommand::BlockNumber { rpc, block } => { let config = rpc.load_config()?; - let provider = utils::get_provider_with_curl(&config, rpc.curl)?; + let provider = utils::get_provider(&config)?; let number = match block { Some(id) => { provider @@ -375,7 +377,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { } CastSubcommand::ChainId { rpc } => { let config = rpc.load_config()?; - let provider = utils::get_provider_with_curl(&config, rpc.curl)?; + let provider = utils::get_provider(&config)?; sh_println!("{}", Cast::new(provider).chain_id().await?)? } CastSubcommand::Client { rpc } => { @@ -447,7 +449,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { CastSubcommand::FindBlock(cmd) => cmd.run().await?, CastSubcommand::GasPrice { rpc } => { let config = rpc.load_config()?; - let provider = utils::get_provider_with_curl(&config, rpc.curl)?; + let provider = utils::get_provider(&config)?; sh_println!("{}", Cast::new(provider).gas_price().await?)?; } CastSubcommand::Index { key_type, key, slot_number } => { diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index 6359682ae717e..fef2288334b95 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -16,7 +16,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{ opts::{ChainValueParser, RpcOpts, TransactionOpts}, - utils::{LoadConfig, TraceResult, get_provider_with_curl, parse_ether_value}, + utils::{LoadConfig, TraceResult, get_provider, parse_ether_value}, }; use foundry_common::{ abi::{encode_function_args, get_func}, @@ -247,7 +247,7 @@ impl CallArgs { sig = Some(data); } - let provider = get_provider_with_curl(&config, false)?; + let provider = get_provider(&config)?; let sender = SenderKind::from_wallet_opts(wallet).await?; let from = sender.address(); @@ -524,13 +524,12 @@ impl CallArgs { // Parse and apply state overrides for (addr, entries) in parse_state_overrides(&self.state_overrides)? { - state_overrides_builder = state_overrides_builder.with_state(addr, entries.into_iter()); + state_overrides_builder = state_overrides_builder.with_state(addr, entries); } // Parse and apply state diff overrides for (addr, entries) in parse_state_overrides(&self.state_diff_overrides)? { - state_overrides_builder = - state_overrides_builder.with_state_diff(addr, entries.into_iter()) + state_overrides_builder = state_overrides_builder.with_state_diff(addr, entries) } Ok(Some(state_overrides_builder.build())) diff --git a/crates/cast/src/cmd/create2.rs b/crates/cast/src/cmd/create2.rs index a07e3fd8e4378..a531c95856666 100644 --- a/crates/cast/src/cmd/create2.rs +++ b/crates/cast/src/cmd/create2.rs @@ -218,7 +218,10 @@ impl Create2Args { // Important: add the thread index to the salt to avoid duplicate results. *salt_word = salt_word.wrapping_add(i); - let mut checksum = [0; 42]; + // Use checksum format only when case_sensitive is enabled. + // This avoids an extra keccak256 call per iteration when not needed. + let mut checksum_buf = [0u8; 42]; + let mut hex_buf = [0u8; 40]; loop { // Stop if a result was found in another thread. if found.load(Ordering::Relaxed) { @@ -229,11 +232,19 @@ impl Create2Args { #[expect(clippy::needless_borrows_for_generic_args)] 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); - // SAFETY: stripping 2 ASCII bytes ("0x") off of an already valid UTF-8 string - // is safe. - let s = unsafe { std::str::from_utf8_unchecked(checksum.get_unchecked(2..)) }; + // Check if the regex matches the calculated address. + // When case_sensitive is true, use EIP-55 checksum format (requires keccak256). + // Otherwise, use lowercase hex to avoid the extra hash computation. + let s = if case_sensitive { + let _ = addr.to_checksum_raw(&mut checksum_buf, None); + // SAFETY: stripping 2 ASCII bytes ("0x") off of an already valid UTF-8 + // string is safe. + unsafe { std::str::from_utf8_unchecked(checksum_buf.get_unchecked(2..)) } + } else { + // SAFETY: hex::encode_to_slice always produces valid UTF-8 (hex digits). + let _ = hex::encode_to_slice(addr.as_slice(), &mut hex_buf); + unsafe { std::str::from_utf8_unchecked(&hex_buf) } + }; if regex.matches(s).into_iter().count() == regex_len { // Notify other threads that we found a result. found.store(true, Ordering::Relaxed); diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index 4806fc9489eb2..55ec3951ee6b0 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -3,11 +3,11 @@ use std::str::FromStr; use crate::{ cmd::send::cast_send, format_uint_exp, - tx::{CastTxSender, SendTxOpts, signing_provider_with_curl}, + tx::{CastTxSender, SendTxOpts, get_provider_with_wallet}, }; use alloy_eips::{BlockId, Encodable2718}; use alloy_ens::NameOrAddress; -use alloy_network::{AnyNetwork, NetworkWallet, TransactionBuilder}; +use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy_primitives::{U64, U256}; use alloy_provider::Provider; use alloy_rpc_types::TransactionRequest; @@ -16,8 +16,9 @@ use alloy_sol_types::sol; use clap::{Args, Parser}; use foundry_cli::{ opts::{RpcOpts, TempoOpts}, - utils::{LoadConfig, get_chain, get_provider_with_curl}, + utils::{LoadConfig, get_chain, get_provider}, }; +use foundry_common::shell; #[doc(hidden)] pub use foundry_config::{Chain, utils::*}; use foundry_primitives::FoundryTransactionRequest; @@ -118,8 +119,7 @@ async fn send_erc20_tx>( ftx.set_chain_id(provider.get_chain_id().await?); } - // Sign using NetworkWallet - let signed_tx = signer.sign_request(ftx).await?; + let signed_tx = ftx.build(&EthereumWallet::new(signer)).await?; // Encode and send raw let mut raw_tx = Vec::with_capacity(signed_tx.encode_2718_len()); @@ -352,8 +352,8 @@ impl Erc20Subcommand { match self { // Read-only - Self::Allowance { token, owner, spender, block, rpc, .. } => { - let provider = get_provider_with_curl(&config, rpc.curl)?; + Self::Allowance { token, owner, spender, block, .. } => { + let provider = get_provider(&config)?; let token = token.resolve(&provider).await?; let owner = owner.resolve(&provider).await?; let spender = spender.resolve(&provider).await?; @@ -364,10 +364,14 @@ impl Erc20Subcommand { .call() .await?; - sh_println!("{}", format_uint_exp(allowance))? + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&allowance.to_string())?)? + } else { + sh_println!("{}", format_uint_exp(allowance))? + } } - Self::Balance { token, owner, block, rpc, .. } => { - let provider = get_provider_with_curl(&config, rpc.curl)?; + Self::Balance { token, owner, block, .. } => { + let provider = get_provider(&config)?; let token = token.resolve(&provider).await?; let owner = owner.resolve(&provider).await?; @@ -376,10 +380,15 @@ impl Erc20Subcommand { .block(block.unwrap_or_default()) .call() .await?; - sh_println!("{}", format_uint_exp(balance))? + + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&balance.to_string())?)? + } else { + sh_println!("{}", format_uint_exp(balance))? + } } - Self::Name { token, block, rpc, .. } => { - let provider = get_provider_with_curl(&config, rpc.curl)?; + Self::Name { token, block, .. } => { + let provider = get_provider(&config)?; let token = token.resolve(&provider).await?; let name = IERC20::new(token, &provider) @@ -387,10 +396,15 @@ impl Erc20Subcommand { .block(block.unwrap_or_default()) .call() .await?; - sh_println!("{}", name)? + + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&name)?)? + } else { + sh_println!("{}", name)? + } } - Self::Symbol { token, block, rpc, .. } => { - let provider = get_provider_with_curl(&config, rpc.curl)?; + Self::Symbol { token, block, .. } => { + let provider = get_provider(&config)?; let token = token.resolve(&provider).await?; let symbol = IERC20::new(token, &provider) @@ -398,10 +412,15 @@ impl Erc20Subcommand { .block(block.unwrap_or_default()) .call() .await?; - sh_println!("{}", symbol)? + + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&symbol)?)? + } else { + sh_println!("{}", symbol)? + } } - Self::Decimals { token, block, rpc, .. } => { - let provider = get_provider_with_curl(&config, rpc.curl)?; + Self::Decimals { token, block, .. } => { + let provider = get_provider(&config)?; let token = token.resolve(&provider).await?; let decimals = IERC20::new(token, &provider) @@ -409,10 +428,14 @@ impl Erc20Subcommand { .block(block.unwrap_or_default()) .call() .await?; - sh_println!("{}", decimals)? + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&decimals)?)? + } else { + sh_println!("{}", decimals)? + } } - Self::TotalSupply { token, block, rpc, .. } => { - let provider = get_provider_with_curl(&config, rpc.curl)?; + Self::TotalSupply { token, block, .. } => { + let provider = get_provider(&config)?; let token = token.resolve(&provider).await?; let total_supply = IERC20::new(token, &provider) @@ -420,11 +443,16 @@ impl Erc20Subcommand { .block(block.unwrap_or_default()) .call() .await?; - sh_println!("{}", format_uint_exp(total_supply))? + + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&total_supply.to_string())?)? + } else { + sh_println!("{}", format_uint_exp(total_supply))? + } } // State-changing Self::Transfer { token, to, amount, send_tx, tx: tx_opts, .. } => { - let provider = signing_provider_with_curl(&send_tx, send_tx.eth.rpc.curl).await?; + let provider = get_provider_with_wallet(&send_tx).await?; let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) .transfer(to.resolve(&provider).await?, U256::from_str(&amount)?) .into_transaction_request(); @@ -445,7 +473,7 @@ impl Erc20Subcommand { .await? } Self::Approve { token, spender, amount, send_tx, tx: tx_opts, .. } => { - let provider = signing_provider_with_curl(&send_tx, send_tx.eth.rpc.curl).await?; + let provider = get_provider_with_wallet(&send_tx).await?; let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) .approve(spender.resolve(&provider).await?, U256::from_str(&amount)?) .into_transaction_request(); @@ -466,7 +494,7 @@ impl Erc20Subcommand { .await? } Self::Mint { token, to, amount, send_tx, tx: tx_opts, .. } => { - let provider = signing_provider_with_curl(&send_tx, send_tx.eth.rpc.curl).await?; + let provider = get_provider_with_wallet(&send_tx).await?; let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) .mint(to.resolve(&provider).await?, U256::from_str(&amount)?) .into_transaction_request(); @@ -487,7 +515,7 @@ impl Erc20Subcommand { .await? } Self::Burn { token, amount, send_tx, tx: tx_opts, .. } => { - let provider = signing_provider_with_curl(&send_tx, send_tx.eth.rpc.curl).await?; + let provider = get_provider_with_wallet(&send_tx).await?; let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) .burn(U256::from_str(&amount)?) .into_transaction_request(); diff --git a/crates/cast/src/cmd/interface.rs b/crates/cast/src/cmd/interface.rs index caae11ae8892a..634fc96d83c31 100644 --- a/crates/cast/src/cmd/interface.rs +++ b/crates/cast/src/cmd/interface.rs @@ -1,4 +1,4 @@ -use alloy_json_abi::{ContractObject, JsonAbi}; +use alloy_json_abi::{ContractObject, JsonAbi, ToSolConfig}; use alloy_primitives::Address; use clap::Parser; use eyre::{Context, Result}; @@ -50,13 +50,19 @@ pub struct InterfaceArgs { )] output: Option, + /// If set, generate all types in a single interface, inlining any inherited or library types. + /// + /// This can fail if there are structs with the same name in different interfaces. + #[arg(long)] + flatten: bool, + #[command(flatten)] etherscan: EtherscanOpts, } impl InterfaceArgs { pub async fn run(self) -> Result<()> { - let Self { contract, name, pragma, output: output_location, etherscan } = self; + let Self { contract, name, pragma, output: output_location, flatten, etherscan } = self; // Determine if the target contract is an ABI file, a local contract or an Ethereum address. let abis = if Path::new(&contract).is_file() @@ -73,8 +79,11 @@ impl InterfaceArgs { } }; + // Build config for to_sol conversion. + let config = if flatten { Some(ToSolConfig::new().one_contract(true)) } else { None }; + // Retrieve interfaces from the array of ABIs. - let interfaces = get_interfaces(abis)?; + let interfaces = get_interfaces(abis, config)?; // Print result or write to file. let res = if shell::is_json() { @@ -142,11 +151,14 @@ fn load_abi_from_artifact(path_or_contract: &str) -> Result) -> Result> { +fn get_interfaces( + abis: Vec<(JsonAbi, String)>, + config: Option, +) -> Result> { abis.into_iter() .map(|(contract_abi, name)| { let source = match forge_fmt::format( - &contract_abi.to_sol(&name, None), + &contract_abi.to_sol(&name, config.clone()), FormatterConfig::default(), ) .into_result() @@ -154,7 +166,7 @@ fn get_interfaces(abis: Vec<(JsonAbi, String)>) -> Result> Ok(generated_source) => generated_source, Err(e) => { sh_warn!("Failed to format interface for {name}: {e}")?; - contract_abi.to_sol(&name, None) + contract_abi.to_sol(&name, config.clone()) } }; diff --git a/crates/cast/src/cmd/mktx.rs b/crates/cast/src/cmd/mktx.rs index 541789bbc94c6..da11ea3e47353 100644 --- a/crates/cast/src/cmd/mktx.rs +++ b/crates/cast/src/cmd/mktx.rs @@ -1,7 +1,7 @@ use crate::tx::{self, CastTxBuilder}; use alloy_eips::Encodable2718; use alloy_ens::NameOrAddress; -use alloy_network::{EthereumWallet, NetworkWallet, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, hex}; use alloy_provider::Provider; use alloy_signer::Signer; @@ -132,7 +132,7 @@ impl MakeTxArgs { // Use "eth_signTransaction" to sign the transaction only works if the node/RPC has // unlocked accounts. let (tx, _) = tx_builder.build(config.sender).await?; - let signed_tx = provider.sign_transaction(tx.into_inner()).await?; + let signed_tx = provider.sign_transaction(tx.into_inner().into()).await?; sh_println!("{signed_tx}")?; return Ok(()); @@ -152,8 +152,7 @@ impl MakeTxArgs { if is_tempo { let (ftx, _) = tx_builder.build(&signer).await?; - // Sign using NetworkWallet - let signed_tx = signer.sign_request(ftx).await?; + let signed_tx = ftx.build(&EthereumWallet::new(signer)).await?; // Encode as 2718 let mut raw_tx = Vec::with_capacity(signed_tx.encode_2718_len()); diff --git a/crates/cast/src/cmd/rpc.rs b/crates/cast/src/cmd/rpc.rs index d31202eb136b5..8883c3fbb5be2 100644 --- a/crates/cast/src/cmd/rpc.rs +++ b/crates/cast/src/cmd/rpc.rs @@ -53,7 +53,7 @@ impl RpcArgs { serde_json::Value::Array(params.into_iter().map(value_or_string).collect()) }; - let provider = utils::get_provider_with_curl(&config, rpc.curl)?; + let provider = utils::get_provider(&config)?; let result = Cast::new(provider).rpc(&method, params).await?; if shell::is_json() { let result: serde_json::Value = serde_json::from_str(&result)?; diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 0999aa55ec495..c6d316d571009 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -135,7 +135,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_cli::utils::get_provider_builder(&config, false)? + let provider = foundry_cli::utils::get_provider_builder(&config)? .compute_units_per_second_opt(compute_units_per_second) .build()?; @@ -271,7 +271,7 @@ impl RunArgs { break; } - configure_tx_env(&mut env.as_env_mut(), &tx.inner); + configure_tx_env(&mut env.as_env_mut(), tx); env.evm_env.cfg_env.disable_balance_check = true; @@ -312,8 +312,8 @@ impl RunArgs { let result = { executor.set_trace_printer(self.trace_printer); - configure_tx_env(&mut env.as_env_mut(), &tx.inner); - if is_impersonated_tx(tx.inner.inner.inner()) { + configure_tx_env(&mut env.as_env_mut(), &tx); + if is_impersonated_tx(tx.as_ref()) { env.evm_env.cfg_env.disable_balance_check = true; } diff --git a/crates/cast/src/cmd/send.rs b/crates/cast/src/cmd/send.rs index 3e50ed27ed799..d747c975ed63e 100644 --- a/crates/cast/src/cmd/send.rs +++ b/crates/cast/src/cmd/send.rs @@ -2,7 +2,7 @@ use std::{path::PathBuf, str::FromStr, time::Duration}; use alloy_eips::Encodable2718; use alloy_ens::NameOrAddress; -use alloy_network::{AnyNetwork, EthereumWallet, NetworkWallet}; +use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy_provider::{Provider, ProviderBuilder}; use alloy_rpc_types::TransactionRequest; use alloy_serde::WithOtherFields; @@ -11,9 +11,8 @@ use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::TransactionOpts, - utils::{LoadConfig, get_provider_with_curl}, + utils::{LoadConfig, get_provider}, }; -use foundry_wallets::WalletSigner; use crate::tx::{self, CastTxBuilder, CastTxSender, SendTxOpts}; @@ -120,7 +119,7 @@ impl SendTxArgs { }; let config = send_tx.eth.load_config()?; - let provider = get_provider_with_curl(&config, send_tx.eth.rpc.curl)?; + let provider = get_provider(&config)?; if let Some(interval) = send_tx.poll_interval { provider.client().set_poll_interval(Duration::from_secs(interval)) @@ -139,8 +138,11 @@ impl SendTxArgs { // Check if this is a Tempo transaction - requires special handling for local signing let is_tempo = builder.is_tempo(); - // Tempo transactions with browser wallets are not supported - if is_tempo && send_tx.eth.wallet.browser { + // Launch browser signer if `--browser` flag is set + let browser = send_tx.browser.run().await?; + + // Tempo transactions with browser signer are not supported + if is_tempo && browser.is_some() { return Err(eyre!("Tempo transactions are not supported with browser wallets.")); } @@ -148,7 +150,7 @@ impl SendTxArgs { // 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 // or remote RPC with unlocked accounts. - if unlocked && !send_tx.eth.wallet.browser { + if unlocked && browser.is_none() { // 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_chain_id().await?; @@ -172,7 +174,7 @@ impl SendTxArgs { cast_send( provider, - tx.into_inner(), + tx.into_inner().into(), send_tx.cast_async, send_tx.sync, send_tx.confirmations, @@ -184,20 +186,10 @@ impl SendTxArgs { // If we cannot successfully instantiate a local signer, then we will assume we don't have // enough information to sign and we must bail. } else { - // Retrieve the signer, and bail if it can't be constructed. - let signer = send_tx.eth.wallet.signer().await?; - let from = signer.address(); - - tx::validate_from_address(send_tx.eth.wallet.from, from)?; - - // Browser wallets work differently as they sign and send the transaction in one step. - if send_tx.eth.wallet.browser - && let WalletSigner::Browser(ref browser_signer) = signer - { - let (tx_request, _) = builder.build(from).await?; - let tx_hash = browser_signer - .send_transaction_via_browser(tx_request.into_inner().inner) - .await?; + // Browser wallet work differently as it sign and send the transaction in one step. + if let Some(browser) = browser { + let (tx_request, _) = builder.build(browser.address()).await?; + let tx_hash = browser.send_transaction_via_browser(tx_request.into_inner()).await?; if send_tx.cast_async { sh_println!("{tx_hash:#x}")?; @@ -217,6 +209,11 @@ impl SendTxArgs { return Ok(()); } + // Retrieve the signer, and bail if it can't be constructed. + let signer = send_tx.eth.wallet.signer().await?; + let from = signer.address(); + + tx::validate_from_address(send_tx.eth.wallet.from, from)?; // Tempo transactions need to be signed locally and sent as raw transactions // because EthereumWallet doesn't understand type 0x76 // TODO(onbjerg): All of this is a side effect of a few things, most notably that we do @@ -225,8 +222,7 @@ impl SendTxArgs { if is_tempo { let (ftx, _) = builder.build(&signer).await?; - // Sign using NetworkWallet - let signed_tx = signer.sign_request(ftx).await?; + let signed_tx = ftx.build(&EthereumWallet::new(signer)).await?; // Encode and send raw let mut raw_tx = Vec::with_capacity(signed_tx.encode_2718_len()); @@ -238,18 +234,6 @@ impl SendTxArgs { if send_tx.cast_async { sh_println!("{tx_hash:#x}")?; - } else if send_tx.sync { - // For sync mode, we already have the hash, just wait for receipt - let receipt = cast - .receipt( - format!("{tx_hash:#x}"), - None, - send_tx.confirmations, - Some(timeout), - false, - ) - .await?; - sh_println!("{receipt}")?; } else { let receipt = cast .receipt( @@ -275,7 +259,7 @@ impl SendTxArgs { cast_send( provider, - tx_request.into_inner(), + tx_request.into_inner().into(), send_tx.cast_async, send_tx.sync, send_tx.confirmations, diff --git a/crates/cast/src/cmd/storage.rs b/crates/cast/src/cmd/storage.rs index d5a74c0954624..b2a2c32251f6f 100644 --- a/crates/cast/src/cmd/storage.rs +++ b/crates/cast/src/cmd/storage.rs @@ -156,6 +156,7 @@ impl StorageArgs { } // Create or reuse a persistent cache for Etherscan sources; fall back to a temp dir + let mut temp_dir = None; let root_path = if let Some(cache_root) = foundry_config::Config::foundry_etherscan_chain_cache_dir(chain) { @@ -163,12 +164,18 @@ impl StorageArgs { let contract_root = sources_root.join(format!("{address}")); if let Err(err) = std::fs::create_dir_all(&contract_root) { sh_warn!("Could not create etherscan cache dir, falling back to temp: {err}")?; - tempfile::tempdir()?.path().to_path_buf() + let tmp = tempfile::tempdir()?; + let path = tmp.path().to_path_buf(); + temp_dir = Some(tmp); + path } else { contract_root } } else { - tempfile::tempdir()?.keep() + let tmp = tempfile::tempdir()?; + let path = tmp.path().to_path_buf(); + temp_dir = Some(tmp); + path }; let mut project = etherscan_project(metadata, &root_path)?; add_storage_layout_output(&mut project); @@ -221,6 +228,7 @@ impl StorageArgs { artifact }; + drop(temp_dir); fetch_and_print_storage(provider, address, block, artifact).await } } diff --git a/crates/cast/src/cmd/trace.rs b/crates/cast/src/cmd/trace.rs index 6005dc952f086..bc7a4a78b96ad 100644 --- a/crates/cast/src/cmd/trace.rs +++ b/crates/cast/src/cmd/trace.rs @@ -54,7 +54,7 @@ impl TraceArgs { hex::decode(trimmed.strip_prefix("0x").unwrap_or(trimmed))? } else if is_json { let tx: AnyRpcTransaction = serde_json::from_str(trimmed)?; - tx.inner.inner.encoded_2718().to_vec() + tx.as_ref().encoded_2718().to_vec() } else { hex::decode(trimmed)? }; diff --git a/crates/cast/src/cmd/wallet/list.rs b/crates/cast/src/cmd/wallet/list.rs index 6a4da6a0f18f2..9555dbe4a4f93 100644 --- a/crates/cast/src/cmd/wallet/list.rs +++ b/crates/cast/src/cmd/wallet/list.rs @@ -60,11 +60,10 @@ impl ListArgs { { match self.list_local_senders() { Ok(()) => {} - Err(e) => { - if !self.all { - sh_err!("{}", e)?; - } + Err(e) if !self.all => { + sh_err!("{}", e)?; } + _ => {} } } @@ -78,6 +77,7 @@ impl ListArgs { .turnkey(self.turnkey || self.all) .interactives(0) .interactive(false) + .browser(Default::default()) .build() .expect("build multi wallet"); @@ -96,11 +96,10 @@ impl ListArgs { }) } } - Err(e) => { - if !self.all { - sh_err!("{}", e)?; - } + Err(e) if !self.all => { + sh_err!("{}", e)?; } + _ => {} } }; } @@ -131,7 +130,10 @@ impl ListArgs { && let Some(file_name) = path.file_name() && let Some(name) = file_name.to_str() { - sh_println!("{name} (Local)")?; + // Extract address from keystore filename format: UTC--{timestamp}--{address} + if let Some(address) = name.split("--").last() { + sh_println!("0x{} (Local)", address)?; + } } } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index cf6c585bd1bdd..5f9b288017851 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -325,7 +325,7 @@ impl + Clone + Unpin> Cast

{ let mut result = String::new(); for field in fields { result.push_str( - &get_pretty_block_attr(&block, &field) + &get_pretty_block_attr::(&block, &field) .unwrap_or_else(|| format!("{field} is not a valid block field")), ); @@ -756,7 +756,7 @@ impl + Clone + Unpin> Cast

{ let encoded = foundry_tx.encoded_2718(); format!("0x{}", hex::encode(encoded)) } else if let Some(ref field) = field { - get_pretty_tx_attr(&tx.inner, field.as_str()) + get_pretty_tx_attr::(&tx, field.as_str()) .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? } else if shell::is_json() { // to_value first to sort json object keys @@ -1158,7 +1158,7 @@ impl SimpleCast { DynSolType::Uint(n) => { if MAX { let mut max = U256::MAX; - if n < 255 { + if n < 256 { max &= U256::from(1).wrapping_shl(n).wrapping_sub(U256::from(1)); } Ok(max.to_string()) @@ -1874,7 +1874,7 @@ impl SimpleCast { let mut topics = vec![event.selector()]; let mut data_tokens: Vec = Vec::new(); - for (input, token) in event.inputs.iter().zip(tokens.into_iter()) { + for (input, token) in event.inputs.iter().zip(tokens) { if input.indexed { let ty = DynSolType::parse(&input.ty)?; if matches!( @@ -2336,7 +2336,7 @@ fn explorer_client( api_url: Option, explorer_url: Option, ) -> Result { - let mut builder = Client::builder().chain(chain)?; + let mut builder = Client::builder(); let deduced = chain.etherscan_urls(); diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 64ff8da391200..1fceefaa66edb 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -19,12 +19,12 @@ use foundry_cli::{ utils::{self, LoadConfig, get_provider_builder, parse_function_args}, }; use foundry_common::{ - TransactionReceiptWithRevertReason, fmt::*, get_pretty_tx_receipt_attr, + TransactionReceiptWithRevertReason, fmt::*, get_pretty_receipt_w_reason_attr, provider::RetryProviderWithSigner, shell, }; use foundry_config::{Chain, Config}; use foundry_primitives::{FoundryTransactionRequest, FoundryTypedTx}; -use foundry_wallets::{WalletOpts, WalletSigner}; +use foundry_wallets::{BrowserWalletOpts, WalletOpts, WalletSigner}; use itertools::Itertools; use serde_json::value::RawValue; use std::{fmt::Write, str::FromStr, time::Duration}; @@ -55,6 +55,10 @@ pub struct SendTxOpts { /// Ethereum options #[command(flatten)] pub eth: EthereumOpts, + + /// Browser wallet options + #[command(flatten)] + pub browser: BrowserWalletOpts, } /// Different sender kinds used by [`CastTxBuilder`]. @@ -172,7 +176,7 @@ impl> CastTxSender

{ /// Sends a transaction and waits for receipt synchronously pub async fn send_sync(&self, tx: WithOtherFields) -> Result { - let mut receipt: TransactionReceiptWithRevertReason = + let mut receipt: TransactionReceiptWithRevertReason = self.provider.send_transaction_sync(tx).await?.into(); // Allow to fail silently @@ -259,7 +263,7 @@ impl> CastTxSender

{ ) -> Result { let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; - let mut receipt: TransactionReceiptWithRevertReason = + let mut receipt: TransactionReceiptWithRevertReason = match self.provider.get_transaction_receipt(tx_hash).await? { Some(r) => r, None => { @@ -287,11 +291,11 @@ impl> CastTxSender

{ /// Helper method to format transaction receipts consistently fn format_receipt( &self, - receipt: TransactionReceiptWithRevertReason, + receipt: TransactionReceiptWithRevertReason, field: Option, ) -> Result { Ok(if let Some(ref field) = field { - get_pretty_tx_receipt_attr(&receipt, field) + get_pretty_receipt_w_reason_attr(&receipt, field) .ok_or_else(|| eyre::eyre!("invalid receipt field: {}", field))? } else if shell::is_json() { // to_value first to sort json object keys @@ -682,8 +686,6 @@ where self.tx.set_blob_sidecar_7594(sidecar); } - self.tx.populate_blob_hashes(); - Ok(self) } } @@ -707,17 +709,13 @@ async fn decode_execution_revert(data: &RawValue) -> Result> { } /// Creates a provider with wallet for signing transactions locally. -/// -/// If `curl_mode` is true, the provider will print equivalent curl commands to stdout -/// instead of executing RPC requests. -pub(crate) async fn signing_provider_with_curl( +pub(crate) async fn get_provider_with_wallet( tx_opts: &SendTxOpts, - curl_mode: bool, ) -> eyre::Result { let config = tx_opts.eth.load_config()?; let signer = tx_opts.eth.wallet.signer().await?; let wallet = alloy_network::EthereumWallet::from(signer); - let provider = get_provider_builder(&config, curl_mode)?.build_with_wallet(wallet)?; + let provider = get_provider_builder(&config)?.build_with_wallet(wallet)?; if let Some(interval) = tx_opts.poll_interval { provider.client().set_poll_interval(Duration::from_secs(interval)) } diff --git a/crates/cast/tests/cli/erc20.rs b/crates/cast/tests/cli/erc20.rs index 012844fb07730..3256e6f8bc167 100644 --- a/crates/cast/tests/cli/erc20.rs +++ b/crates/cast/tests/cli/erc20.rs @@ -501,3 +501,108 @@ casttest!(erc20_curl_total_supply, |_prj, cmd| { assert!(output.contains("eth_call")); assert!(output.contains(rpc)); }); + +// tests that `balance` command works correctly with --json flag +forgetest_async!(erc20_balance_json, |prj, cmd| { + let (rpc, token) = setup_token_test(&prj, &mut cmd).await; + + let output = cmd + .cast_fuse() + .args(["--json", "erc20", "balance", &token, anvil_const::ADDR1, "--rpc-url", &rpc]) + .assert_success() + .get_output() + .stdout_lossy(); + + let balance_str: String = serde_json::from_str(&output).expect("valid json string"); + let balance: U256 = balance_str.parse().unwrap(); + assert_eq!(balance, U256::from(1_000_000_000_000_000_000_000u128)); +}); + +// tests that `allowance` command works correctly with --json flag +forgetest_async!(erc20_allowance_json, |prj, cmd| { + let (rpc, token) = setup_token_test(&prj, &mut cmd).await; + + // First approve some tokens + let approve_amount = U256::from(50_000_000_000_000_000_000u128); + cmd.cast_fuse() + .args([ + "erc20", + "approve", + &token, + anvil_const::ADDR2, + &approve_amount.to_string(), + "--rpc-url", + &rpc, + "--private-key", + anvil_const::PK1, + ]) + .assert_success(); + + // Check allowance with JSON flag + let output = cmd + .cast_fuse() + .args([ + "--json", + "erc20", + "allowance", + &token, + anvil_const::ADDR1, + anvil_const::ADDR2, + "--rpc-url", + &rpc, + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let allowance_str: String = serde_json::from_str(&output).expect("valid json string"); + let allowance: U256 = allowance_str.parse().unwrap(); + assert_eq!(allowance, approve_amount); +}); + +// tests that `name`, `symbol`, `decimals`, and `totalSupply` commands work correctly with --json +// flag +forgetest_async!(erc20_metadata_json, |prj, cmd| { + let (rpc, token) = setup_token_test(&prj, &mut cmd).await; + + // Test name with --json + let output = cmd + .cast_fuse() + .args(["--json", "erc20", "name", &token, "--rpc-url", &rpc]) + .assert_success() + .get_output() + .stdout_lossy(); + let name: String = serde_json::from_str(&output).expect("valid json string"); + assert_eq!(name, "Test Token"); + + // Test symbol with --json + let output = cmd + .cast_fuse() + .args(["--json", "erc20", "symbol", &token, "--rpc-url", &rpc]) + .assert_success() + .get_output() + .stdout_lossy(); + let symbol: String = serde_json::from_str(&output).expect("valid json string"); + assert_eq!(symbol, "TEST"); + + // Test decimals with --json + let output = cmd + .cast_fuse() + .args(["--json", "erc20", "decimals", &token, "--rpc-url", &rpc]) + .assert_success() + .get_output() + .stdout_lossy(); + let decimals: u8 = output.trim().parse().expect("valid number"); + assert_eq!(decimals, 18); + + // Test totalSupply with --json + let output = cmd + .cast_fuse() + .args(["--json", "erc20", "total-supply", &token, "--rpc-url", &rpc]) + .assert_success() + .get_output() + .stdout_lossy(); + let total_supply_str: String = serde_json::from_str(&output).expect("valid json string"); + let total_supply: U256 = total_supply_str.parse().unwrap(); + assert_eq!(total_supply, U256::from(1_000_000_000_000_000_000_000u128)); +}); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 5bfaee843d41c..27c156031e0df 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -2025,19 +2025,19 @@ blockNumber 22287055 from 0x4648451b5F87FF8F0F7D622bD40574bb97E25980 transactionIndex 230 effectiveGasPrice 363392048 -accessList [] +hash 0x5bcd22734cca2385dc25b2d38a3d33a640c5961bd46d390dff184c894204b594 +type 2 chainId 1 +nonce 113642 gasLimit 350000 -hash 0x5bcd22734cca2385dc25b2d38a3d33a640c5961bd46d390dff184c894204b594 -input 0xa9059cbb000000000000000000000000568766d218d82333dd4dae933ddfcda5da26625000000000000000000000000000000000000000000000000000000000cc3ed109 maxFeePerGas 675979146 maxPriorityFeePerGas 1337 -nonce 113642 -r 0x1e92d3e1ca69109a1743fc4b3cf9dff58630bc9f429cea3c3fe311506264e36c -s 0x793947d4bbdce56a1a5b2b3525c46f01569414a22355f4883b5429668ab0f51a to 0xdAC17F958D2ee523a2206206994597C13D831ec7 -type 2 value 0 +accessList [] +input 0xa9059cbb000000000000000000000000568766d218d82333dd4dae933ddfcda5da26625000000000000000000000000000000000000000000000000000000000cc3ed109 +r 0x1e92d3e1ca69109a1743fc4b3cf9dff58630bc9f429cea3c3fe311506264e36c +s 0x793947d4bbdce56a1a5b2b3525c46f01569414a22355f4883b5429668ab0f51a yParity 1 ... "#]]); @@ -2154,7 +2154,7 @@ casttest!(storage, |_prj, cmd| { "#]]); }); -casttest!(storage_with_valid_solc_version_1, |_prj, cmd| { +casttest!(flaky_storage_with_valid_solc_version_1, |_prj, cmd| { cmd.args([ "storage", "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2", @@ -2168,7 +2168,7 @@ casttest!(storage_with_valid_solc_version_1, |_prj, cmd| { .assert_success(); }); -casttest!(storage_with_valid_solc_version_2, |_prj, cmd| { +casttest!(flaky_storage_with_valid_solc_version_2, |_prj, cmd| { cmd.args([ "storage", "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2", @@ -2182,7 +2182,7 @@ casttest!(storage_with_valid_solc_version_2, |_prj, cmd| { .assert_success(); }); -casttest!(storage_with_invalid_solc_version_1, |_prj, cmd| { +casttest!(flaky_storage_with_invalid_solc_version_1, |_prj, cmd| { let output = cmd .args([ "storage", @@ -2207,7 +2207,7 @@ casttest!(storage_with_invalid_solc_version_1, |_prj, cmd| { ); }); -casttest!(storage_with_invalid_solc_version_2, |_prj, cmd| { +casttest!(flaky_storage_with_invalid_solc_version_2, |_prj, cmd| { cmd.args([ "storage", "0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2", @@ -2226,7 +2226,7 @@ Error: Encountered invalid solc version in contracts/Create2Deployer.sol: No sol }); // -casttest!(storage_layout_simple, |_prj, cmd| { +casttest!(flaky_storage_layout_simple, |_prj, cmd| { cmd.args([ "storage", "--rpc-url", @@ -2253,7 +2253,7 @@ casttest!(storage_layout_simple, |_prj, cmd| { }); // -casttest!(storage_layout_simple_json, |_prj, cmd| { +casttest!(flaky_storage_layout_simple_json, |_prj, cmd| { cmd.args([ "storage", "--rpc-url", @@ -2270,7 +2270,7 @@ casttest!(storage_layout_simple_json, |_prj, cmd| { }); // -casttest!(storage_layout_complex, |_prj, cmd| { +casttest!(flaky_storage_layout_complex, |_prj, cmd| { cmd.args([ "storage", "--rpc-url", @@ -2318,7 +2318,7 @@ casttest!(storage_layout_complex, |_prj, cmd| { "#]]); }); -casttest!(storage_layout_complex_md, |_prj, cmd| { +casttest!(flaky_storage_layout_complex_md, |_prj, cmd| { cmd.args([ "storage", "--rpc-url", @@ -2353,7 +2353,7 @@ casttest!(storage_layout_complex_md, |_prj, cmd| { "#]]); }); -casttest!(storage_layout_complex_proxy, |_prj, cmd| { +casttest!(flaky_storage_layout_complex_proxy, |_prj, cmd| { cmd.args([ "storage", "--rpc-url", @@ -2395,7 +2395,7 @@ casttest!(storage_layout_complex_proxy, |_prj, cmd| { "#]]); }); -casttest!(storage_layout_complex_json, |_prj, cmd| { +casttest!(flaky_storage_layout_complex_json, |_prj, cmd| { cmd.args([ "storage", "--rpc-url", @@ -2486,9 +2486,53 @@ interface Interface { ]]); }); +// tests that `cast interface --flatten` inlines inherited struct types into the interface +// +casttest!(interface_flatten, |prj, cmd| { + let interface = include_str!("../fixtures/interface_inherited_struct.json"); + + let path = prj.root().join("interface_inherited_struct.json"); + fs::write(&path, interface).unwrap(); + + // Without --flatten, a separate library is generated for the struct + cmd.arg("interface").arg(&path).assert_success().stdout_eq(str![[ + r#"// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +library IBase { + struct TestStruct { + address asset; + } +} + +interface Interface { + function test(IBase.TestStruct memory param) external; +} + +"# + ]]); + + // With --flatten, the struct is inlined into the interface + cmd.cast_fuse().arg("interface").arg("--flatten").arg(&path).assert_success().stdout_eq(str![ + [r#"// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface Interface { + // Types from `IBase` + struct TestStruct { + address asset; + } + + function test(TestStruct memory param) external; +} + +"#] + ]); +}); + // tests that fetches WETH interface from etherscan // -casttest!(fetch_weth_interface_from_etherscan, |_prj, cmd| { +casttest!(flaky_fetch_weth_interface_from_etherscan, |_prj, cmd| { cmd.args([ "interface", "--etherscan-api-key", @@ -2873,7 +2917,7 @@ casttest!(format_units, |_prj, cmd| { // tests that fetches a sample contract creation code // -casttest!(fetch_creation_code_from_etherscan, |_prj, cmd| { +casttest!(flaky_fetch_creation_code_from_etherscan, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); cmd.args([ "creation-code", @@ -2892,7 +2936,7 @@ casttest!(fetch_creation_code_from_etherscan, |_prj, cmd| { // tests that fetches a sample contract creation args bytes // -casttest!(fetch_creation_code_only_args_from_etherscan, |_prj, cmd| { +casttest!(flaky_fetch_creation_code_only_args_from_etherscan, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); cmd.args([ "creation-code", @@ -2912,7 +2956,7 @@ casttest!(fetch_creation_code_only_args_from_etherscan, |_prj, cmd| { // tests that displays a sample contract creation args // -casttest!(fetch_constructor_args_from_etherscan, |_prj, cmd| { +casttest!(flaky_fetch_constructor_args_from_etherscan, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); cmd.args([ "constructor-args", @@ -2930,7 +2974,7 @@ casttest!(fetch_constructor_args_from_etherscan, |_prj, cmd| { }); // -casttest!(test_non_mainnet_traces, |prj, cmd| { +casttest!(flaky_test_non_mainnet_traces, |prj, cmd| { prj.clear(); cmd.args([ "run", @@ -2984,7 +3028,7 @@ Transaction successfully executed. // tests that displays a sample contract artifact // -casttest!(fetch_artifact_from_etherscan, |_prj, cmd| { +casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); cmd.args([ "artifact", @@ -3175,7 +3219,7 @@ Transaction successfully executed. }); // tests cast can decode external libraries traces with project cached selectors -forgetest_async!(decode_external_libraries_with_cached_selectors, |prj, cmd| { +forgetest_async!(flaky_decode_external_libraries_with_cached_selectors, |prj, cmd| { let (api, handle) = anvil::spawn(NodeConfig::test()).await; foundry_test_utils::util::initialize(prj.root()); @@ -3249,9 +3293,9 @@ contract CounterInExternalLibScript is Script { ... Traces: [..] → new @0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - ├─ [..] 0x6fD8bf6770F4bEe578348D24028000cE9c4D2bB9::updateCounterInExternalLib(0, 100) [delegatecall] + ├─ [..] [..]::updateCounterInExternalLib(0, 100) [delegatecall] │ └─ ← [Stop] - └─ ← [Return] 62 bytes of code + └─ ← [Return] [..] bytes of code Transaction successfully executed. @@ -3631,7 +3675,7 @@ Transaction successfully executed. }); // https://github.com/foundry-rs/foundry/issues/9541 -forgetest_async!(cast_run_impersonated_tx, |_prj, cmd| { +forgetest_async!(flaky_cast_run_impersonated_tx, |_prj, cmd| { let (_api, handle) = anvil::spawn( NodeConfig::test() .with_auto_impersonate(true) @@ -3665,48 +3709,35 @@ forgetest_async!(cast_run_impersonated_tx, |_prj, cmd| { }); // -casttest!( - #[cfg_attr(all(target_os = "linux", target_arch = "aarch64"), ignore = "no 0.4 solc")] - fetch_src_blockscout, - |_prj, cmd| { - let url = "https://eth.blockscout.com/api"; +casttest!(flaky_fetch_src_blockscout, |_prj, cmd| { + let url = "https://eth.blockscout.com/api"; - let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); + let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); - cmd.args([ - "source", - &weth.to_string(), - "--chain-id", - "1", - "--explorer-api-url", - url, - "--flatten", - ]) - .assert_success() - .stdout_eq(str![[r#" + cmd.args([ + "source", + &weth.to_string(), + "--chain-id", + "1", + "--explorer-api-url", + url, + "--flatten", + ]) + .assert_success() + .stdout_eq(str![[r#" ... contract WETH9 { string public name = "Wrapped Ether"; string public symbol = "WETH"; uint8 public decimals = 18; ..."#]]); - } -); +}); -casttest!( - #[cfg_attr(all(target_os = "linux", target_arch = "aarch64"), ignore = "no 0.4 solc")] - fetch_src_default, - |_prj, cmd| { - let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); - let etherscan_api_key = next_etherscan_api_key(); +casttest!(flaky_fetch_src_default, |_prj, cmd| { + let weth = address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); + let etherscan_api_key = next_etherscan_api_key(); - cmd.args([ - "source", - &weth.to_string(), - "--flatten", - "--etherscan-api-key", - ðerscan_api_key, - ]) + cmd.args(["source", &weth.to_string(), "--flatten", "--etherscan-api-key", ðerscan_api_key]) .assert_success() .stdout_eq(str![[r#" ... @@ -3715,12 +3746,11 @@ contract WETH9 { string public symbol = "WETH"; uint8 public decimals = 18; ..."#]]); - } -); +}); // // -casttest!(osaka_can_run_p256_precompile, |_prj, cmd| { +casttest!(flaky_osaka_can_run_p256_precompile, |_prj, cmd| { cmd.args([ "run", "0x17b2de59ebd7dfd2452a3638a16737b6b65ae816c1c5571631dc0d80b63c41de", @@ -3862,7 +3892,7 @@ contract SimpleStorageScript is Script { &handle.http_endpoint(), ]) .assert_failure().stderr_eq(str![[r#" -Error: Failed to estimate gas: server returned an error response: error code 3: execution reverted: custom error 0x6786ad34: 000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000003e8, data: "0x6786ad34000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000003e8": AddressInsufficientBalance(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 1000) +Error: Failed to estimate gas: server returned an error response: error code 3: execution reverted: custom error 0x6786ad34: 000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000003e8, data: "0x6786ad34000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000003e8"[..] "#]]); }); @@ -4588,19 +4618,19 @@ casttest!(abi_encode_event_dynamic_indexed, |_prj, cmd| { // Test cast run Celo transfer with precompiles. casttest!( - #[ignore = "flaky celo rpc url"] - run_celo_with_precompiles, + #[ignore = "requires debug_traceTransaction, which most free Celo RPC endpoints no longer support"] + flaky_run_celo_with_precompiles, |_prj, cmd| { let rpc = next_rpc_endpoint(NamedChain::Celo); cmd.args([ - "run", - "0xa652b9f41bb1a617ea6b2835b3316e79f0f21b8264e7bcd20e57c4092a70a0f6", - "--quick", - "--rpc-url", - rpc.as_str(), - ]) - .assert_success() - .stdout_eq(str![[r#" + "run", + "0xa652b9f41bb1a617ea6b2835b3316e79f0f21b8264e7bcd20e57c4092a70a0f6", + "--quick", + "--rpc-url", + rpc.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" Traces: [17776] 0x471EcE3750Da237f93B8E339c536989b8978a438::transfer(0xD2eB2d37d238Caeff39CFA36A013299C6DbAC56A, 138000000000000000 [1.38e17]) ├─ [12370] 0xFeA1B35f1D5f2A58532a70e7A32e6F2D3Bc4F7B1::transfer(0xD2eB2d37d238Caeff39CFA36A013299C6DbAC56A, 138000000000000000 [1.38e17]) [delegatecall] @@ -4781,3 +4811,56 @@ casttest!(curl_call, |_prj, cmd| { assert!(output.contains("eth_call")); assert!(output.contains(rpc)); }); + +// https://github.com/foundry-rs/foundry/issues/11584 +// Tests that invalid hex calldata (odd length) produces a clear error message +casttest!(cast_call_invalid_hex_calldata_error, |_prj, cmd| { + let rpc = next_rpc_endpoint(NamedChain::Mainnet); + cmd.args([ + "call", + "0xdead000000000000000000000000000000000000", + "--data", + "0x0", // Invalid: odd length hex + "--rpc-url", + rpc.as_str(), + ]) + .assert_failure() + .stderr_eq(str![[r#" +Error: Invalid hex calldata '0x0': odd number of digits + +"#]]); +}); + +// https://github.com/foundry-rs/foundry/issues/11584 +// Tests that valid hex calldata works correctly +casttest!(cast_call_valid_hex_calldata, |_prj, cmd| { + let rpc = next_rpc_endpoint(NamedChain::Mainnet); + cmd.args([ + "call", + "0xdead000000000000000000000000000000000000", + "--data", + "0x00", // Valid: even length hex + "--rpc-url", + rpc.as_str(), + ]) + .assert_success(); +}); + +// https://github.com/foundry-rs/foundry/issues/11584 +// Tests that invalid hex with uppercase 0X prefix also produces clear error +casttest!(cast_call_invalid_hex_uppercase_prefix, |_prj, cmd| { + let rpc = next_rpc_endpoint(NamedChain::Mainnet); + cmd.args([ + "call", + "0xdead000000000000000000000000000000000000", + "--data", + "0X1", // Invalid: odd length hex with uppercase prefix + "--rpc-url", + rpc.as_str(), + ]) + .assert_failure() + .stderr_eq(str![[r#" +Error: Invalid hex calldata '0X1': odd number of digits + +"#]]); +}); diff --git a/crates/cast/tests/cli/selectors.rs b/crates/cast/tests/cli/selectors.rs index 7be4f4fa61898..09fb2c95384ce 100644 --- a/crates/cast/tests/cli/selectors.rs +++ b/crates/cast/tests/cli/selectors.rs @@ -1,7 +1,7 @@ use foundry_test_utils::util::OutputExt; use std::path::Path; -casttest!(error_decode_with_openchain, |prj, cmd| { +casttest!(flaky_error_decode_with_openchain, |prj, cmd| { prj.clear_cache(); cmd.args(["decode-error", "0x7a0e198500000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064"]).assert_success().stdout_eq(str![[r#" ValueTooHigh(uint256,uint256) @@ -11,7 +11,7 @@ ValueTooHigh(uint256,uint256) "#]]); }); -casttest!(fourbyte, |_prj, cmd| { +casttest!(flaky_fourbyte, |_prj, cmd| { cmd.args(["4byte", "0xa9059cbb"]).assert_success().stdout_eq(str![[r#" transfer(address,uint256) @@ -27,7 +27,7 @@ For more information, try '--help'. "#]]); }); -casttest!(fourbyte_calldata, |_prj, cmd| { +casttest!(flaky_fourbyte_calldata, |_prj, cmd| { cmd.args(["4byte-calldata", "0xa9059cbb0000000000000000000000000a2ac0c368dc8ec680a0c98c907656bd970675950000000000000000000000000000000000000000000000000000000767954a79"]).assert_success().stdout_eq(str![[r#" 1) "transfer(address,uint256)" 0x0A2AC0c368Dc8eC680a0c98C907656BD97067595 @@ -36,14 +36,14 @@ casttest!(fourbyte_calldata, |_prj, cmd| { "#]]); }); -casttest!(fourbyte_calldata_only_selector, |_prj, cmd| { +casttest!(flaky_fourbyte_calldata_only_selector, |_prj, cmd| { cmd.args(["4byte-calldata", "0xa9059cbb"]).assert_success().stdout_eq(str![[r#" transfer(address,uint256) "#]]); }); -casttest!(fourbyte_calldata_alias, |_prj, cmd| { +casttest!(flaky_fourbyte_calldata_alias, |_prj, cmd| { cmd.args(["4byte-decode", "0xa9059cbb0000000000000000000000000a2ac0c368dc8ec680a0c98c907656bd970675950000000000000000000000000000000000000000000000000000000767954a79"]).assert_success().stdout_eq(str![[r#" 1) "transfer(address,uint256)" 0x0A2AC0c368Dc8eC680a0c98C907656BD97067595 @@ -52,7 +52,7 @@ casttest!(fourbyte_calldata_alias, |_prj, cmd| { "#]]); }); -casttest!(fourbyte_event, |_prj, cmd| { +casttest!(flaky_fourbyte_event, |_prj, cmd| { cmd.args(["4byte-event", "0x7e1db2a1cd12f0506ecd806dba508035b290666b84b096a87af2fd2a1516ede6"]) .assert_success() .stdout_eq(str![[r#" @@ -61,7 +61,7 @@ updateAuthority(address,uint8) "#]]); }); -casttest!(fourbyte_event_2, |_prj, cmd| { +casttest!(flaky_fourbyte_event_2, |_prj, cmd| { cmd.args(["4byte-event", "0xb7009613e63fb13fd59a2fa4c206a992c1f090a44e5d530be255aa17fed0b3dd"]) .assert_success() .stdout_eq(str![[r#" @@ -70,7 +70,7 @@ canCall(address,address,bytes4) "#]]); }); -casttest!(upload_signatures, |_prj, cmd| { +casttest!(flaky_upload_signatures, |_prj, cmd| { // test no prefix is accepted as function let output = cmd .args(["upload-signature", "transfer(address,uint256)"]) @@ -148,7 +148,7 @@ casttest!(event_decode_with_sig, |_prj, cmd| { }); // tests cast can decode event with Openchain API -casttest!(event_decode_with_openchain, |prj, cmd| { +casttest!(flaky_event_decode_with_openchain, |prj, cmd| { prj.clear_cache(); cmd.args(["decode-event", "0xe27c4c1372396a3d15a9922f74f9dfc7c72b1ad6d63868470787249c356454c1000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000dd00000004e"]).assert_success().stdout_eq(str![[r#" BaseCurrencySet(address,uint256) @@ -176,7 +176,7 @@ casttest!(error_decode_with_sig, |_prj, cmd| { }); // tests cast can decode error and event when using local sig identifiers cache -forgetest_init!(error_event_decode_with_cache, |prj, cmd| { +forgetest_init!(flaky_error_event_decode_with_cache, |prj, cmd| { prj.add_source( "LocalProjectContract", r#" @@ -212,7 +212,7 @@ MyUniqueEventWithinLocalProject(uint256,address) "#]]); }); -forgetest!(cache_selectors_from_extra_abis, |prj, cmd| { +forgetest!(flaky_cache_selectors_from_extra_abis, |prj, cmd| { // Create folder with ABI JSON files containing a unique error let abis_dir = prj.root().join("external_abis"); std::fs::create_dir(&abis_dir).unwrap(); diff --git a/crates/cast/tests/fixtures/interface_inherited_struct.json b/crates/cast/tests/fixtures/interface_inherited_struct.json new file mode 100644 index 0000000000000..b82c0829b5ac7 --- /dev/null +++ b/crates/cast/tests/fixtures/interface_inherited_struct.json @@ -0,0 +1 @@ +[{"type":"function","name":"test","inputs":[{"name":"param","type":"tuple","internalType":"struct IBase.TestStruct","components":[{"name":"asset","type":"address","internalType":"address"}]}],"outputs":[],"stateMutability":"nonpayable"}] diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 799945a663161..5b8724e559257 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -20,6 +20,7 @@ foundry-common.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true foundry-evm-core.workspace = true +foundry-primitives.workspace = true foundry-evm-fuzz.workspace = true foundry-evm-traces.workspace = true foundry-wallets.workspace = true @@ -55,6 +56,7 @@ jsonpath_lib.workspace = true k256.workspace = true memchr.workspace = true p256 = "0.13" +ed25519-consensus = "2.1" ecdsa = "0.16" rand.workspace = true revm.workspace = true diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 67820b2030c23..7a865cc348386 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3780,6 +3780,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "createEd25519Key", + "description": "Generates an Ed25519 key pair from a deterministic salt.\nReturns (publicKey, privateKey) as 32-byte values.", + "declaration": "function createEd25519Key(bytes32 salt) external pure returns (bytes32 publicKey, bytes32 privateKey);", + "visibility": "external", + "mutability": "pure", + "signature": "createEd25519Key(bytes32)", + "selector": "0x1ef3f27a", + "selectorBytes": [ + 30, + 243, + 242, + 122 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "createFork_0", @@ -5084,6 +5104,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "executeTransaction", + "description": "Executes an RLP-encoded signed transaction with full EVM semantics (like `--isolate` mode).\nThe transaction is decoded from EIP-2718 format (type byte prefix + RLP payload) or legacy RLP.\nReturns the execution output bytes.\nThis cheatcode is not allowed in `forge script` contexts.", + "declaration": "function executeTransaction(bytes calldata rawTx) external returns (bytes memory);", + "visibility": "external", + "mutability": "", + "signature": "executeTransaction(bytes)", + "selector": "0x943d7209", + "selectorBytes": [ + 148, + 61, + 114, + 9 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "exists", @@ -8426,6 +8466,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "publicKeyEd25519", + "description": "Derives the Ed25519 public key from a private key.", + "declaration": "function publicKeyEd25519(bytes32 privateKey) external pure returns (bytes32 publicKey);", + "visibility": "external", + "mutability": "pure", + "signature": "publicKeyEd25519(bytes32)", + "selector": "0x27f44236", + "selectorBytes": [ + 39, + 244, + 66, + 54 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "publicKeyP256", @@ -10150,6 +10210,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "signEd25519", + "description": "Signs a message with namespace using Ed25519.\nThe signature covers namespace || message for domain separation.\nReturns a 64-byte Ed25519 signature.", + "declaration": "function signEd25519(bytes calldata namespace, bytes calldata message, bytes32 privateKey) external pure returns (bytes memory signature);", + "visibility": "external", + "mutability": "pure", + "signature": "signEd25519(bytes,bytes,bytes32)", + "selector": "0xef609c65", + "selectorBytes": [ + 239, + 96, + 156, + 101 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "signP256", @@ -11332,6 +11412,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "verifyEd25519", + "description": "Verifies an Ed25519 signature over namespace || message.\nReturns true if signature is valid, false otherwise.", + "declaration": "function verifyEd25519(bytes calldata signature, bytes calldata namespace, bytes calldata message, bytes32 publicKey) external pure returns (bool valid);", + "visibility": "external", + "mutability": "pure", + "signature": "verifyEd25519(bytes,bytes,bytes,bytes32)", + "selector": "0xd08c2888", + "selectorBytes": [ + 208, + 140, + 40, + 136 + ] + }, + "group": "crypto", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "warmSlot", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 640c97e3108fb..c703c2fc5fd9a 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -563,6 +563,14 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function setBlockhash(uint256 blockNumber, bytes32 blockHash) external; + /// Executes an RLP-encoded signed transaction with full EVM semantics (like `--isolate` mode). + /// The transaction is decoded from EIP-2718 format (type byte prefix + RLP payload) or legacy RLP. + /// Returns the execution output bytes. + /// + /// This cheatcode is not allowed in `forge script` contexts. + #[cheatcode(group = Evm, safety = Unsafe)] + function executeTransaction(bytes calldata rawTx) external returns (bytes memory); + // -------- Account State -------- /// Sets an address' balance. @@ -2783,6 +2791,34 @@ interface Vm { #[cheatcode(group = Crypto)] function publicKeyP256(uint256 privateKey) external pure returns (uint256 publicKeyX, uint256 publicKeyY); + /// Generates an Ed25519 key pair from a deterministic salt. + /// Returns (publicKey, privateKey) as 32-byte values. + #[cheatcode(group = Crypto, safety = Safe)] + function createEd25519Key(bytes32 salt) external pure returns (bytes32 publicKey, bytes32 privateKey); + + /// Derives the Ed25519 public key from a private key. + #[cheatcode(group = Crypto, safety = Safe)] + function publicKeyEd25519(bytes32 privateKey) external pure returns (bytes32 publicKey); + + /// Signs a message with namespace using Ed25519. + /// The signature covers namespace || message for domain separation. + /// Returns a 64-byte Ed25519 signature. + #[cheatcode(group = Crypto, safety = Safe)] + function signEd25519(bytes calldata namespace, bytes calldata message, bytes32 privateKey) + external + pure + returns (bytes memory signature); + + /// Verifies an Ed25519 signature over namespace || message. + /// Returns true if signature is valid, false otherwise. + #[cheatcode(group = Crypto, safety = Safe)] + function verifyEd25519( + bytes calldata signature, + bytes calldata namespace, + bytes calldata message, + bytes32 publicKey + ) external pure returns (bool valid); + /// Derive a private key from a provided mnemonic string (or mnemonic file path) /// at the derivation path `m/44'/60'/0'/0/{index}`. #[cheatcode(group = Crypto)] diff --git a/crates/cheatcodes/src/base64.rs b/crates/cheatcodes/src/base64.rs index 4aa4ba74a0e4b..8c03a8b63ef6e 100644 --- a/crates/cheatcodes/src/base64.rs +++ b/crates/cheatcodes/src/base64.rs @@ -2,28 +2,28 @@ use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_sol_types::SolValue; use base64::prelude::*; -impl Cheatcode for toBase64_0Call { +impl Cheatcode for toBase64_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { data } = self; Ok(BASE64_STANDARD.encode(data).abi_encode()) } } -impl Cheatcode for toBase64_1Call { +impl Cheatcode for toBase64_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { data } = self; Ok(BASE64_STANDARD.encode(data).abi_encode()) } } -impl Cheatcode for toBase64URL_0Call { +impl Cheatcode for toBase64URL_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { data } = self; Ok(BASE64_URL_SAFE.encode(data).abi_encode()) } } -impl Cheatcode for toBase64URL_1Call { +impl Cheatcode for toBase64URL_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { data } = self; Ok(BASE64_URL_SAFE.encode(data).abi_encode()) diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index 924a44ef7ce32..e897ee2779203 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -21,31 +21,36 @@ use p256::ecdsa::{ Signature as P256Signature, SigningKey as P256SigningKey, signature::hazmat::PrehashSigner, }; +use ed25519_consensus::{ + Signature as Ed25519Signature, SigningKey as Ed25519SigningKey, + VerificationKey as Ed25519VerificationKey, +}; + /// The BIP32 default derivation path prefix. const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; -impl Cheatcode for createWallet_0Call { +impl Cheatcode for createWallet_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { walletLabel } = self; create_wallet(&U256::from_be_bytes(keccak256(walletLabel).0), Some(walletLabel), state) } } -impl Cheatcode for createWallet_1Call { +impl Cheatcode for createWallet_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { privateKey } = self; create_wallet(privateKey, None, state) } } -impl Cheatcode for createWallet_2Call { +impl Cheatcode for createWallet_2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { privateKey, walletLabel } = self; create_wallet(privateKey, Some(walletLabel), state) } } -impl Cheatcode for sign_0Call { +impl Cheatcode for sign_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { wallet, digest } = self; let sig = sign(&wallet.privateKey, digest)?; @@ -53,7 +58,7 @@ impl Cheatcode for sign_0Call { } } -impl Cheatcode for signWithNonceUnsafeCall { +impl Cheatcode for signWithNonceUnsafeCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let pk: U256 = self.privateKey; let digest: B256 = self.digest; @@ -63,7 +68,7 @@ impl Cheatcode for signWithNonceUnsafeCall { } } -impl Cheatcode for signCompact_0Call { +impl Cheatcode for signCompact_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { wallet, digest } = self; let sig = sign(&wallet.privateKey, digest)?; @@ -71,35 +76,35 @@ impl Cheatcode for signCompact_0Call { } } -impl Cheatcode for deriveKey_0Call { +impl Cheatcode for deriveKey_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { mnemonic, index } = self; derive_key::(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index) } } -impl Cheatcode for deriveKey_1Call { +impl Cheatcode for deriveKey_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { mnemonic, derivationPath, index } = self; derive_key::(mnemonic, derivationPath, *index) } } -impl Cheatcode for deriveKey_2Call { +impl Cheatcode for deriveKey_2Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { mnemonic, index, language } = self; derive_key_str(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index, language) } } -impl Cheatcode for deriveKey_3Call { +impl Cheatcode for deriveKey_3Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { mnemonic, derivationPath, index, language } = self; derive_key_str(mnemonic, derivationPath, *index, language) } } -impl Cheatcode for rememberKeyCall { +impl Cheatcode for rememberKeyCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { privateKey } = self; let wallet = parse_wallet(privateKey)?; @@ -108,7 +113,7 @@ impl Cheatcode for rememberKeyCall { } } -impl Cheatcode for rememberKeys_0Call { +impl Cheatcode for rememberKeys_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { mnemonic, derivationPath, count } = self; let wallets = derive_wallets::(mnemonic, derivationPath, *count)?; @@ -122,7 +127,7 @@ impl Cheatcode for rememberKeys_0Call { } } -impl Cheatcode for rememberKeys_1Call { +impl Cheatcode for rememberKeys_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { mnemonic, derivationPath, language, count } = self; let wallets = derive_wallets_str(mnemonic, derivationPath, language, *count)?; @@ -142,7 +147,7 @@ fn inject_wallet(state: &mut Cheatcodes, wallet: LocalSigner) -> Add address } -impl Cheatcode for sign_1Call { +impl Cheatcode for sign_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey, digest } = self; let sig = sign(privateKey, digest)?; @@ -150,7 +155,7 @@ impl Cheatcode for sign_1Call { } } -impl Cheatcode for signCompact_1Call { +impl Cheatcode for signCompact_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey, digest } = self; let sig = sign(privateKey, digest)?; @@ -158,7 +163,7 @@ impl Cheatcode for signCompact_1Call { } } -impl Cheatcode for sign_2Call { +impl Cheatcode for sign_2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { digest } = self; let sig = sign_with_wallet(state, None, digest)?; @@ -166,7 +171,7 @@ impl Cheatcode for sign_2Call { } } -impl Cheatcode for signCompact_2Call { +impl Cheatcode for signCompact_2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { digest } = self; let sig = sign_with_wallet(state, None, digest)?; @@ -174,7 +179,7 @@ impl Cheatcode for signCompact_2Call { } } -impl Cheatcode for sign_3Call { +impl Cheatcode for sign_3Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { signer, digest } = self; let sig = sign_with_wallet(state, Some(*signer), digest)?; @@ -182,7 +187,7 @@ impl Cheatcode for sign_3Call { } } -impl Cheatcode for signCompact_3Call { +impl Cheatcode for signCompact_3Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { signer, digest } = self; let sig = sign_with_wallet(state, Some(*signer), digest)?; @@ -190,14 +195,14 @@ impl Cheatcode for signCompact_3Call { } } -impl Cheatcode for signP256Call { +impl Cheatcode for signP256Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey, digest } = self; sign_p256(privateKey, digest) } } -impl Cheatcode for publicKeyP256Call { +impl Cheatcode for publicKeyP256Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey } = self; let pub_key = @@ -209,6 +214,34 @@ impl Cheatcode for publicKeyP256Call { } } +impl Cheatcode for createEd25519KeyCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { salt } = self; + create_ed25519_key(salt) + } +} + +impl Cheatcode for publicKeyEd25519Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { privateKey } = self; + public_key_ed25519(privateKey) + } +} + +impl Cheatcode for signEd25519Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { namespace, message, privateKey } = self; + sign_ed25519(namespace, message, privateKey) + } +} + +impl Cheatcode for verifyEd25519Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { signature, namespace, message, publicKey } = self; + verify_ed25519(signature, namespace, message, publicKey) + } +} + /// 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) /// @@ -344,7 +377,7 @@ fn sign_with_wallet( let mut wallets = state.wallets().inner.lock(); let maybe_provided_sender = wallets.provided_sender; - let signers = wallets.multi_wallet.signers()?; + let (signers, _) = wallets.multi_wallet.signers()?; let signer = if let Some(signer) = signer { signer @@ -399,6 +432,47 @@ fn parse_private_key_p256(private_key: &U256) -> Result { Ok(P256SigningKey::from_bytes((&private_key.to_be_bytes()).into())?) } +fn parse_signing_key_ed25519(private_key: &B256) -> Result { + Ed25519SigningKey::try_from(private_key.as_slice()) + .map_err(|e| fmt_err!("invalid Ed25519 private key: {e}")) +} + +fn create_ed25519_key(salt: &B256) -> Result { + let signing_key = parse_signing_key_ed25519(salt)?; + let public_key = B256::from_slice(signing_key.verification_key().as_ref()); + Ok((public_key, *salt).abi_encode()) +} + +fn public_key_ed25519(private_key: &B256) -> Result { + let signing_key = parse_signing_key_ed25519(private_key)?; + Ok(B256::from_slice(signing_key.verification_key().as_ref()).abi_encode()) +} + +fn sign_ed25519(namespace: &[u8], message: &[u8], private_key: &B256) -> Result { + let signing_key = parse_signing_key_ed25519(private_key)?; + let combined = [namespace, message].concat(); + let signature: [u8; 64] = signing_key.sign(&combined).into(); + Ok(signature.to_vec().abi_encode()) +} + +fn verify_ed25519(signature: &[u8], namespace: &[u8], message: &[u8], public_key: &B256) -> Result { + if signature.len() != 64 { + return Ok(false.abi_encode()); + } + + let Ok(verification_key) = Ed25519VerificationKey::try_from(public_key.as_slice()) else { + return Ok(false.abi_encode()); + }; + + let Ok(sig_bytes): Result<[u8; 64], _> = signature.try_into() else { + return Ok(false.abi_encode()); + }; + + let combined = [namespace, message].concat(); + let valid = verification_key.verify(&Ed25519Signature::from(sig_bytes), &combined).is_ok(); + Ok(valid.abi_encode()) +} + pub(super) fn parse_wallet(private_key: &U256) -> Result { parse_private_key(private_key).map(PrivateKeySigner::from) } @@ -596,4 +670,105 @@ mod tests { let msg = err.to_string(); assert!(msg.contains("invalid nonce scalar"), "unexpected error: {msg}"); } + + #[test] + fn test_create_ed25519_key_determinism() { + let salt = B256::from([1u8; 32]); + let result1 = create_ed25519_key(&salt).unwrap(); + let result2 = create_ed25519_key(&salt).unwrap(); + assert_eq!(result1, result2, "same salt should produce same keys"); + } + + #[test] + fn test_create_ed25519_key_different_salts() { + let salt1 = B256::from([1u8; 32]); + let salt2 = B256::from([2u8; 32]); + let result1 = create_ed25519_key(&salt1).unwrap(); + let result2 = create_ed25519_key(&salt2).unwrap(); + assert_ne!(result1, result2, "different salts should produce different keys"); + } + + #[test] + fn test_public_key_ed25519_consistency() { + let salt = B256::from([42u8; 32]); + let create_result = create_ed25519_key(&salt).unwrap(); + let (expected_public, private): (B256, B256) = + <(B256, B256)>::abi_decode(&create_result).unwrap(); + + let derived_public_result = public_key_ed25519(&private).unwrap(); + let derived_public = B256::abi_decode(&derived_public_result).unwrap(); + + assert_eq!(expected_public, derived_public, "derived public key should match"); + } + + #[test] + fn test_sign_and_verify_ed25519_valid() { + let salt = B256::from([123u8; 32]); + let create_result = create_ed25519_key(&salt).unwrap(); + let (public_key, private_key): (B256, B256) = + <(B256, B256)>::abi_decode(&create_result).unwrap(); + + let namespace = b"test.namespace"; + let message = b"hello world"; + let sig_result = sign_ed25519(namespace, message, &private_key).unwrap(); + let sig_bytes: Vec = Vec::abi_decode(&sig_result).unwrap(); + + let verify_result = verify_ed25519(&sig_bytes, namespace, message, &public_key).unwrap(); + let valid = bool::abi_decode(&verify_result).unwrap(); + + assert!(valid, "signature should be valid"); + } + + #[test] + fn test_verify_ed25519_invalid_signature() { + let salt = B256::from([123u8; 32]); + let create_result = create_ed25519_key(&salt).unwrap(); + let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap(); + + let invalid_sig = [0u8; 64]; + let namespace = b"test.namespace"; + let message = b"hello world"; + + let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap(); + let valid = bool::abi_decode(&verify_result).unwrap(); + + assert!(!valid, "invalid signature should not verify"); + } + + #[test] + fn test_verify_ed25519_namespace_separation() { + let salt = B256::from([123u8; 32]); + let create_result = create_ed25519_key(&salt).unwrap(); + let (public_key, private_key): (B256, B256) = + <(B256, B256)>::abi_decode(&create_result).unwrap(); + + let namespace_a = b"namespace.a"; + let message = b"message"; + let sig_result = sign_ed25519(namespace_a, message, &private_key).unwrap(); + let sig_bytes: Vec = Vec::abi_decode(&sig_result).unwrap(); + + let namespace_b = b"namespace.b"; + let verify_result = verify_ed25519(&sig_bytes, namespace_b, message, &public_key).unwrap(); + let valid = bool::abi_decode(&verify_result).unwrap(); + assert!(!valid, "signature with namespace A should not verify with namespace B"); + + let verify_result = verify_ed25519(&sig_bytes, namespace_a, message, &public_key).unwrap(); + let valid = bool::abi_decode(&verify_result).unwrap(); + assert!(valid, "signature should verify with correct namespace"); + } + + #[test] + fn test_verify_ed25519_invalid_signature_length() { + let salt = B256::from([123u8; 32]); + let create_result = create_ed25519_key(&salt).unwrap(); + let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap(); + + let invalid_sig = [0u8; 32]; + let namespace = b"test"; + let message = b"message"; + + let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap(); + let valid = bool::abi_decode(&verify_result).unwrap(); + assert!(!valid, "signature with wrong length should not verify"); + } } diff --git a/crates/cheatcodes/src/env.rs b/crates/cheatcodes/src/env.rs index 63f9bd00ade3a..b4f623a741e2d 100644 --- a/crates/cheatcodes/src/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -8,7 +8,7 @@ use std::{env, sync::OnceLock}; /// Stores the forge execution context for the duration of the program. pub static FORGE_CONTEXT: OnceLock = OnceLock::new(); -impl Cheatcode for setEnvCall { +impl Cheatcode for setEnvCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name: key, value } = self; if key.is_empty() { @@ -28,7 +28,7 @@ impl Cheatcode for setEnvCall { } } -impl Cheatcode for resolveEnvCall { +impl Cheatcode for resolveEnvCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; let resolved = foundry_config::resolve::interpolate(input) @@ -37,105 +37,105 @@ impl Cheatcode for resolveEnvCall { } } -impl Cheatcode for envExistsCall { +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 { +impl Cheatcode for envBool_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::Bool) } } -impl Cheatcode for envUint_0Call { +impl Cheatcode for envUint_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::Uint(256)) } } -impl Cheatcode for envInt_0Call { +impl Cheatcode for envInt_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::Int(256)) } } -impl Cheatcode for envAddress_0Call { +impl Cheatcode for envAddress_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::Address) } } -impl Cheatcode for envBytes32_0Call { +impl Cheatcode for envBytes32_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::FixedBytes(32)) } } -impl Cheatcode for envString_0Call { +impl Cheatcode for envString_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::String) } } -impl Cheatcode for envBytes_0Call { +impl Cheatcode for envBytes_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; env(name, &DynSolType::Bytes) } } -impl Cheatcode for envBool_1Call { +impl Cheatcode for envBool_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::Bool) } } -impl Cheatcode for envUint_1Call { +impl Cheatcode for envUint_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::Uint(256)) } } -impl Cheatcode for envInt_1Call { +impl Cheatcode for envInt_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::Int(256)) } } -impl Cheatcode for envAddress_1Call { +impl Cheatcode for envAddress_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::Address) } } -impl Cheatcode for envBytes32_1Call { +impl Cheatcode for envBytes32_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::FixedBytes(32)) } } -impl Cheatcode for envString_1Call { +impl Cheatcode for envString_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::String) } } -impl Cheatcode for envBytes_1Call { +impl Cheatcode for envBytes_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim } = self; env_array(name, delim, &DynSolType::Bytes) @@ -143,7 +143,7 @@ impl Cheatcode for envBytes_1Call { } // bool -impl Cheatcode for envOr_0Call { +impl Cheatcode for envOr_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::Bool) @@ -151,7 +151,7 @@ impl Cheatcode for envOr_0Call { } // uint256 -impl Cheatcode for envOr_1Call { +impl Cheatcode for envOr_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::Uint(256)) @@ -159,7 +159,7 @@ impl Cheatcode for envOr_1Call { } // int256 -impl Cheatcode for envOr_2Call { +impl Cheatcode for envOr_2Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::Int(256)) @@ -167,7 +167,7 @@ impl Cheatcode for envOr_2Call { } // address -impl Cheatcode for envOr_3Call { +impl Cheatcode for envOr_3Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::Address) @@ -175,7 +175,7 @@ impl Cheatcode for envOr_3Call { } // bytes32 -impl Cheatcode for envOr_4Call { +impl Cheatcode for envOr_4Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::FixedBytes(32)) @@ -183,7 +183,7 @@ impl Cheatcode for envOr_4Call { } // string -impl Cheatcode for envOr_5Call { +impl Cheatcode for envOr_5Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::String) @@ -191,7 +191,7 @@ impl Cheatcode for envOr_5Call { } // bytes -impl Cheatcode for envOr_6Call { +impl Cheatcode for envOr_6Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, defaultValue } = self; env_default(name, defaultValue, &DynSolType::Bytes) @@ -199,7 +199,7 @@ impl Cheatcode for envOr_6Call { } // bool[] -impl Cheatcode for envOr_7Call { +impl Cheatcode for envOr_7Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; env_array_default(name, delim, defaultValue, &DynSolType::Bool) @@ -207,7 +207,7 @@ impl Cheatcode for envOr_7Call { } // uint256[] -impl Cheatcode for envOr_8Call { +impl Cheatcode for envOr_8Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; env_array_default(name, delim, defaultValue, &DynSolType::Uint(256)) @@ -215,7 +215,7 @@ impl Cheatcode for envOr_8Call { } // int256[] -impl Cheatcode for envOr_9Call { +impl Cheatcode for envOr_9Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; env_array_default(name, delim, defaultValue, &DynSolType::Int(256)) @@ -223,7 +223,7 @@ impl Cheatcode for envOr_9Call { } // address[] -impl Cheatcode for envOr_10Call { +impl Cheatcode for envOr_10Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; env_array_default(name, delim, defaultValue, &DynSolType::Address) @@ -231,7 +231,7 @@ impl Cheatcode for envOr_10Call { } // bytes32[] -impl Cheatcode for envOr_11Call { +impl Cheatcode for envOr_11Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; env_array_default(name, delim, defaultValue, &DynSolType::FixedBytes(32)) @@ -239,7 +239,7 @@ impl Cheatcode for envOr_11Call { } // string[] -impl Cheatcode for envOr_12Call { +impl Cheatcode for envOr_12Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; env_array_default(name, delim, defaultValue, &DynSolType::String) @@ -247,7 +247,7 @@ impl Cheatcode for envOr_12Call { } // bytes[] -impl Cheatcode for envOr_13Call { +impl Cheatcode for envOr_13Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; let default = defaultValue.to_vec(); @@ -255,7 +255,7 @@ impl Cheatcode for envOr_13Call { } } -impl Cheatcode for isContextCall { +impl Cheatcode for isContextCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { context } = self; Ok((FORGE_CONTEXT.get() == Some(context)).abi_encode()) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index b5e76e1148694..d82998d58f543 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -2,10 +2,10 @@ use crate::{ BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Error, Result, - Vm::*, - inspector::{Ecx, RecordDebugStepInfo}, + Vm::*, inspector::RecordDebugStepInfo, }; use alloy_consensus::TxEnvelope; +use alloy_evm::FromRecoveredTx; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_network::eip2718::EIP4844_TX_TYPE_ID; use alloy_primitives::{ @@ -23,19 +23,22 @@ use foundry_common::{ }; use foundry_compilers::artifacts::EvmVersion; use foundry_evm_core::{ - ContextExt, - backend::{DatabaseExt, RevertStateSnapshotAction}, + backend::{DatabaseExt, FoundryJournalExt, RevertStateSnapshotAction}, constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, + env::FoundryContextExt, + evm::NestedEvmExt, utils::get_blob_base_fee_update_fraction_by_spec_id, }; use foundry_evm_traces::TraceMode; +use foundry_primitives::FoundryTxEnvelope; use itertools::Itertools; use rand::Rng; use revm::{ bytecode::Bytecode, - context::{Block, JournalTr}, + context::{Block, Cfg, ContextTr, JournalTr, Transaction, TxEnv, result::ExecutionResult}, + inspector::JournalExt, primitives::{KECCAK_EMPTY, hardfork::SpecId}, - state::Account, + state::{Account, AccountStatus}, }; use std::{ collections::{BTreeMap, HashSet, btree_map::Entry}, @@ -241,7 +244,7 @@ impl Display for AccountStateDiffs { } } -impl Cheatcode for addrCall { +impl Cheatcode for addrCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { privateKey } = self; let wallet = super::crypto::parse_wallet(privateKey)?; @@ -249,29 +252,30 @@ impl Cheatcode for addrCall { } } -impl Cheatcode for getNonce_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for getNonce_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account } = self; get_nonce(ccx, account) } } -impl Cheatcode for getNonce_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for getNonce_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { wallet } = self; get_nonce(ccx, &wallet.addr) } } -impl Cheatcode for loadCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for loadCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, slot } = *self; ccx.ensure_not_precompile(&target)?; - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - journal.load_account(db, target)?; - let mut val = journal - .sload(db, target, slot.into(), false) + ccx.ecx.journal_mut().load_account(target)?; + let mut val = ccx + .ecx + .journal_mut() + .sload(target, slot.into()) .map_err(|e| fmt_err!("failed to load storage slot: {:?}", e))?; if val.is_cold && val.data.is_zero() { @@ -304,8 +308,8 @@ impl Cheatcode for loadCall { } } -impl Cheatcode for loadAllocsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for loadAllocsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { pathToAllocsJson } = self; let path = Path::new(pathToAllocsJson); @@ -322,29 +326,31 @@ impl Cheatcode for loadAllocsCall { }; // Then, load the allocs into the database. - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - db.load_allocs(&allocs, journal) + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.load_allocs(&allocs, inner) .map(|()| Vec::default()) .map_err(|e| fmt_err!("failed to load allocs: {e}")) } } -impl Cheatcode for cloneAccountCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for cloneAccountCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { source, target } = self; - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - let account = journal.load_account(db, *source)?; - let genesis = &genesis_account(account.data); - db.clone_account(genesis, target, journal)?; + let account = ccx.ecx.journal_mut().load_account(*source)?; + let genesis = genesis_account(account.data); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.clone_account(&genesis, target, inner)?; // Cloned account should persist in forked envs. - ccx.ecx.journaled_state.database.add_persistent_account(*target); + ccx.ecx.db_mut().add_persistent_account(*target); Ok(Default::default()) } } -impl Cheatcode for dumpStateCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for dumpStateCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { pathToStateJson } = self; let path = Path::new(pathToStateJson); @@ -361,8 +367,8 @@ impl Cheatcode for dumpStateCall { let alloc = ccx .ecx - .journaled_state - .state() + .journal_mut() + .evm_state_mut() .iter_mut() .filter(|(key, val)| !skip(key, val)) .map(|(key, val)| (key, genesis_account(val))) @@ -373,7 +379,7 @@ impl Cheatcode for dumpStateCall { } } -impl Cheatcode for recordCall { +impl Cheatcode for recordCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.recording_accesses = true; @@ -382,14 +388,14 @@ impl Cheatcode for recordCall { } } -impl Cheatcode for stopRecordCall { +impl Cheatcode for stopRecordCall { fn apply(&self, state: &mut Cheatcodes) -> Result { state.recording_accesses = false; Ok(Default::default()) } } -impl Cheatcode for accessesCall { +impl Cheatcode for accessesCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { target } = *self; let result = ( @@ -400,7 +406,7 @@ impl Cheatcode for accessesCall { } } -impl Cheatcode for recordLogsCall { +impl Cheatcode for recordLogsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.recorded_logs = Some(Default::default()); @@ -408,14 +414,14 @@ impl Cheatcode for recordLogsCall { } } -impl Cheatcode for getRecordedLogsCall { +impl Cheatcode for getRecordedLogsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; Ok(state.recorded_logs.replace(Default::default()).unwrap_or_default().abi_encode()) } } -impl Cheatcode for getRecordedLogsJsonCall { +impl Cheatcode for getRecordedLogsJsonCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; let logs = state.recorded_logs.replace(Default::default()).unwrap_or_default(); @@ -431,7 +437,7 @@ impl Cheatcode for getRecordedLogsJsonCall { } } -impl Cheatcode for pauseGasMeteringCall { +impl Cheatcode for pauseGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.gas_metering.paused = true; @@ -439,7 +445,7 @@ impl Cheatcode for pauseGasMeteringCall { } } -impl Cheatcode for resumeGasMeteringCall { +impl Cheatcode for resumeGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.gas_metering.resume(); @@ -447,7 +453,7 @@ impl Cheatcode for resumeGasMeteringCall { } } -impl Cheatcode for resetGasMeteringCall { +impl Cheatcode for resetGasMeteringCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.gas_metering.reset(); @@ -455,7 +461,7 @@ impl Cheatcode for resetGasMeteringCall { } } -impl Cheatcode for lastCallGasCall { +impl Cheatcode for lastCallGasCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; let Some(last_call_gas) = &state.gas_metering.last_call_gas else { @@ -465,170 +471,171 @@ impl Cheatcode for lastCallGasCall { } } -impl Cheatcode for getChainIdCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getChainIdCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - Ok(U256::from(ccx.ecx.cfg.chain_id).abi_encode()) + Ok(U256::from(ccx.ecx.cfg().chain_id()).abi_encode()) } } -impl Cheatcode for chainIdCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for chainIdCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newChainId } = self; ensure!(*newChainId <= U256::from(u64::MAX), "chain ID must be less than 2^64"); - ccx.ecx.cfg.chain_id = newChainId.to(); + ccx.ecx.cfg_mut().chain_id = newChainId.to(); Ok(Default::default()) } } -impl Cheatcode for coinbaseCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for coinbaseCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newCoinbase } = self; - ccx.ecx.block.beneficiary = *newCoinbase; + ccx.ecx.block_mut().beneficiary = *newCoinbase; Ok(Default::default()) } } -impl Cheatcode for difficultyCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for difficultyCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newDifficulty } = self; ensure!( - ccx.ecx.cfg.spec < SpecId::MERGE, + ccx.ecx.cfg().spec().into() < 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.ecx.block.difficulty = *newDifficulty; + ccx.ecx.block_mut().difficulty = *newDifficulty; Ok(Default::default()) } } -impl Cheatcode for feeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for feeCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newBasefee } = self; ensure!(*newBasefee <= U256::from(u64::MAX), "base fee must be less than 2^64"); - ccx.ecx.block.basefee = newBasefee.saturating_to(); + ccx.ecx.block_mut().basefee = newBasefee.saturating_to(); Ok(Default::default()) } } -impl Cheatcode for prevrandao_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for prevrandao_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newPrevrandao } = self; ensure!( - ccx.ecx.cfg.spec >= SpecId::MERGE, + ccx.ecx.cfg().spec().into() >= 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.block.prevrandao = Some(*newPrevrandao); + ccx.ecx.block_mut().prevrandao = Some(*newPrevrandao); Ok(Default::default()) } } -impl Cheatcode for prevrandao_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for prevrandao_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newPrevrandao } = self; ensure!( - ccx.ecx.cfg.spec >= SpecId::MERGE, + ccx.ecx.cfg().spec().into() >= 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.block.prevrandao = Some((*newPrevrandao).into()); + ccx.ecx.block_mut().prevrandao = Some((*newPrevrandao).into()); Ok(Default::default()) } } -impl Cheatcode for blobhashesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for blobhashesCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { hashes } = self; ensure!( - ccx.ecx.cfg.spec >= SpecId::CANCUN, + ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, "`blobhashes` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - ccx.ecx.tx.blob_hashes.clone_from(hashes); + ccx.ecx.tx_mut().blob_hashes.clone_from(hashes); // force this as 4844 txtype - ccx.ecx.tx.tx_type = EIP4844_TX_TYPE_ID; + ccx.ecx.tx_mut().tx_type = EIP4844_TX_TYPE_ID; Ok(Default::default()) } } -impl Cheatcode for getBlobhashesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getBlobhashesCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; ensure!( - ccx.ecx.cfg.spec >= SpecId::CANCUN, + ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, "`getBlobhashes` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - Ok(ccx.ecx.tx.blob_hashes.clone().abi_encode()) + Ok(ccx.ecx.tx().blob_versioned_hashes().to_vec().abi_encode()) } } -impl Cheatcode for rollCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for rollCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newHeight } = self; - ccx.ecx.block.number = *newHeight; + ccx.ecx.block_mut().number = *newHeight; Ok(Default::default()) } } -impl Cheatcode for getBlockNumberCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getBlockNumberCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - Ok(ccx.ecx.block.number.abi_encode()) + Ok(ccx.ecx.block().number().abi_encode()) } } -impl Cheatcode for txGasPriceCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for txGasPriceCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newGasPrice } = self; ensure!(*newGasPrice <= U256::from(u64::MAX), "gas price must be less than 2^64"); - ccx.ecx.tx.gas_price = newGasPrice.saturating_to(); + ccx.ecx.tx_mut().gas_price = newGasPrice.saturating_to(); Ok(Default::default()) } } -impl Cheatcode for warpCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for warpCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newTimestamp } = self; - ccx.ecx.block.timestamp = *newTimestamp; + ccx.ecx.block_mut().timestamp = *newTimestamp; Ok(Default::default()) } } -impl Cheatcode for getBlockTimestampCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getBlockTimestampCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - Ok(ccx.ecx.block.timestamp.abi_encode()) + Ok(ccx.ecx.block().timestamp().abi_encode()) } } -impl Cheatcode for blobBaseFeeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for blobBaseFeeCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newBlobBaseFee } = self; ensure!( - ccx.ecx.cfg.spec >= SpecId::CANCUN, + ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, "`blobBaseFee` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - ccx.ecx.block.set_blob_excess_gas_and_price( + let spec: SpecId = ccx.ecx.cfg().spec().into(); + ccx.ecx.block_mut().set_blob_excess_gas_and_price( (*newBlobBaseFee).to(), - get_blob_base_fee_update_fraction_by_spec_id(ccx.ecx.cfg.spec), + get_blob_base_fee_update_fraction_by_spec_id(spec), ); Ok(Default::default()) } } -impl Cheatcode for getBlobBaseFeeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getBlobBaseFeeCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - Ok(ccx.ecx.block.blob_excess_gas().unwrap_or(0).abi_encode()) + Ok(ccx.ecx.block().blob_excess_gas().unwrap_or(0).abi_encode()) } } -impl Cheatcode for dealCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for dealCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account: address, newBalance: new_balance } = *self; let account = journaled_account(ccx.ecx, address)?; let old_balance = std::mem::replace(&mut account.info.balance, new_balance); @@ -638,21 +645,20 @@ impl Cheatcode for dealCall { } } -impl Cheatcode for etchCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for etchCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, newRuntimeBytecode } = self; ccx.ensure_not_precompile(target)?; - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - journal.load_account(db, *target)?; + ccx.ecx.journal_mut().load_account(*target)?; let bytecode = Bytecode::new_raw_checked(newRuntimeBytecode.clone()) .map_err(|e| fmt_err!("failed to create bytecode: {e}"))?; - journal.set_code(*target, bytecode); + ccx.ecx.journal_mut().set_code(*target, bytecode); Ok(Default::default()) } } -impl Cheatcode for resetNonceCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for resetNonceCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account } = self; let account = journaled_account(ccx.ecx, *account)?; // Per EIP-161, EOA nonces start at 0, but contract nonces @@ -666,8 +672,8 @@ impl Cheatcode for resetNonceCall { } } -impl Cheatcode for setNonceCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for setNonceCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account, newNonce } = *self; let account = journaled_account(ccx.ecx, account)?; // nonce must increment only @@ -682,8 +688,8 @@ impl Cheatcode for setNonceCall { } } -impl Cheatcode for setNonceUnsafeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for setNonceUnsafeCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account, newNonce } = *self; let account = journaled_account(ccx.ecx, account)?; account.info.nonce = newNonce; @@ -691,23 +697,23 @@ impl Cheatcode for setNonceUnsafeCall { } } -impl Cheatcode for storeCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for storeCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, slot, value } = *self; ccx.ensure_not_precompile(&target)?; ensure_loaded_account(ccx.ecx, target)?; - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - journal - .sstore(db, target, slot.into(), value.into(), false) + ccx.ecx + .journal_mut() + .sstore(target, slot.into(), value.into()) .map_err(|e| fmt_err!("failed to store storage slot: {:?}", e))?; Ok(Default::default()) } } -impl Cheatcode for coolCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for coolCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target } = self; - if let Some(account) = ccx.ecx.journaled_state.state.get_mut(target) { + if let Some(account) = ccx.ecx.journal_mut().evm_state_mut().get_mut(target) { account.unmark_touch(); account.storage.values_mut().for_each(|slot| slot.mark_cold()); } @@ -715,7 +721,7 @@ impl Cheatcode for coolCall { } } -impl Cheatcode for accessListCall { +impl Cheatcode for accessListCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { access } = self; let access_list = access @@ -730,7 +736,7 @@ impl Cheatcode for accessListCall { } } -impl Cheatcode for noAccessListCall { +impl Cheatcode for noAccessListCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; // Set to empty option in order to override previous applied access list. @@ -741,45 +747,45 @@ impl Cheatcode for noAccessListCall { } } -impl Cheatcode for warmSlotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for warmSlotCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, slot } = *self; set_cold_slot(ccx, target, slot.into(), false); Ok(Default::default()) } } -impl Cheatcode for coolSlotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for coolSlotCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, slot } = *self; set_cold_slot(ccx, target, slot.into(), true); Ok(Default::default()) } } -impl Cheatcode for readCallersCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for readCallersCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - read_callers(ccx.state, &ccx.ecx.tx.caller, ccx.ecx.journaled_state.depth()) + read_callers(ccx.state, &ccx.ecx.tx().caller(), ccx.ecx.journal().depth()) } } -impl Cheatcode for snapshotValue_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for snapshotValue_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { name, value } = self; inner_value_snapshot(ccx, None, Some(name.clone()), value.to_string()) } } -impl Cheatcode for snapshotValue_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for snapshotValue_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { group, name, value } = self; inner_value_snapshot(ccx, Some(group.clone()), Some(name.clone()), value.to_string()) } } -impl Cheatcode for snapshotGasLastCall_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for snapshotGasLastCall_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { name } = self; let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else { bail!("no external call was made yet"); @@ -788,8 +794,8 @@ impl Cheatcode for snapshotGasLastCall_0Call { } } -impl Cheatcode for snapshotGasLastCall_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for snapshotGasLastCall_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { name, group } = self; let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else { bail!("no external call was made yet"); @@ -803,117 +809,129 @@ impl Cheatcode for snapshotGasLastCall_1Call { } } -impl Cheatcode for startSnapshotGas_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for startSnapshotGas_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { name } = self; inner_start_gas_snapshot(ccx, None, Some(name.clone())) } } -impl Cheatcode for startSnapshotGas_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for startSnapshotGas_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { group, name } = self; inner_start_gas_snapshot(ccx, Some(group.clone()), Some(name.clone())) } } -impl Cheatcode for stopSnapshotGas_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for stopSnapshotGas_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_stop_gas_snapshot(ccx, None, None) } } -impl Cheatcode for stopSnapshotGas_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for stopSnapshotGas_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { name } = self; inner_stop_gas_snapshot(ccx, None, Some(name.clone())) } } -impl Cheatcode for stopSnapshotGas_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for stopSnapshotGas_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { group, name } = self; inner_stop_gas_snapshot(ccx, Some(group.clone()), Some(name.clone())) } } // Deprecated in favor of `snapshotStateCall` -impl Cheatcode for snapshotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for snapshotCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_snapshot_state(ccx) } } -impl Cheatcode for snapshotStateCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for snapshotStateCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_snapshot_state(ccx) } } // Deprecated in favor of `revertToStateCall` -impl Cheatcode for revertToCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for revertToCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state(ccx, *snapshotId) } } -impl Cheatcode for revertToStateCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for revertToStateCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state(ccx, *snapshotId) } } // Deprecated in favor of `revertToStateAndDeleteCall` -impl Cheatcode for revertToAndDeleteCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for revertToAndDeleteCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state_and_delete(ccx, *snapshotId) } } -impl Cheatcode for revertToStateAndDeleteCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for revertToStateAndDeleteCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state_and_delete(ccx, *snapshotId) } } // Deprecated in favor of `deleteStateSnapshotCall` -impl Cheatcode for deleteSnapshotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for deleteSnapshotCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_delete_state_snapshot(ccx, *snapshotId) } } -impl Cheatcode for deleteStateSnapshotCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for deleteStateSnapshotCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_delete_state_snapshot(ccx, *snapshotId) } } // Deprecated in favor of `deleteStateSnapshotsCall` -impl Cheatcode for deleteSnapshotsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for deleteSnapshotsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_delete_state_snapshots(ccx) } } -impl Cheatcode for deleteStateSnapshotsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for deleteStateSnapshotsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_delete_state_snapshots(ccx) } } -impl Cheatcode for startStateDiffRecordingCall { +impl Cheatcode for startStateDiffRecordingCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.recorded_account_diffs_stack = Some(Default::default()); @@ -923,15 +941,15 @@ impl Cheatcode for startStateDiffRecordingCall { } } -impl Cheatcode for stopAndReturnStateDiffCall { +impl Cheatcode for stopAndReturnStateDiffCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; get_state_diff(state) } } -impl Cheatcode for getStateDiffCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for getStateDiffCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let mut diffs = String::new(); let state_diffs = get_recorded_state_diffs(ccx); for (address, state_diffs) in state_diffs { @@ -942,15 +960,15 @@ impl Cheatcode for getStateDiffCall { } } -impl Cheatcode for getStateDiffJsonCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for getStateDiffJsonCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let state_diffs = get_recorded_state_diffs(ccx); Ok(serde_json::to_string(&state_diffs)?.abi_encode()) } } -impl Cheatcode for getStorageSlotsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for getStorageSlotsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, variableName } = self; let storage_layout = get_contract_data(ccx, *target) @@ -959,10 +977,11 @@ impl Cheatcode for getStorageSlotsCall { trace!(storage = ?storage_layout.storage, "fetched storage"); + let variable_name_lower = variableName.to_lowercase(); let storage = storage_layout .storage .iter() - .find(|s| s.label.to_lowercase() == *variableName.to_lowercase()) + .find(|s| s.label.to_lowercase() == variable_name_lower) .ok_or_else(|| fmt_err!("variable '{variableName}' not found in storage layout"))?; let storage_type = storage_layout @@ -1005,8 +1024,7 @@ impl Cheatcode for getStorageSlotsCall { if storage_type.encoding == ENCODING_BYTES { // Try to check if it's a long bytes/string by reading the current storage // value - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - if let Ok(value) = journal.sload(db, *target, slot, false) { + if let Ok(value) = ccx.ecx.journal_mut().sload(*target, slot) { let value_bytes = value.data.to_be_bytes::<32>(); let length_byte = value_bytes[31]; // Check if it's a long bytes/string (LSB is 1) @@ -1027,7 +1045,7 @@ impl Cheatcode for getStorageSlotsCall { } } -impl Cheatcode for getStorageAccessesCall { +impl Cheatcode for getStorageAccessesCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let mut storage_accesses = Vec::new(); @@ -1041,22 +1059,26 @@ impl Cheatcode for getStorageAccessesCall { } } -impl Cheatcode for broadcastRawTransactionCall { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for broadcastRawTransactionCall +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let tx = TxEnvelope::decode(&mut self.data.as_ref()) .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?; - let (db, journal, env) = ccx.ecx.as_db_env_and_journal(); - db.transact_from_tx( - &tx.clone().into(), - env.to_owned(), - journal, - &mut *executor.get_inspector(ccx.state), - )?; + let env = ccx.ecx.to_env(); + let mut inspector = executor.get_inspector(ccx.state); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.transact_from_tx(&tx.clone().into(), env, inner, &mut *inspector)?; + drop(inspector); if ccx.state.broadcast.is_some() { ccx.state.broadcastable_transactions.push_back(BroadcastableTransaction { - rpc: ccx.ecx.journaled_state.database.active_fork_url(), + rpc: ccx.ecx.db().active_fork_url(), transaction: tx.try_into()?, }); } @@ -1065,23 +1087,174 @@ impl Cheatcode for broadcastRawTransactionCall { } } -impl Cheatcode for setBlockhashCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for setBlockhashCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { blockNumber, blockHash } = *self; ensure!(blockNumber <= U256::from(u64::MAX), "blockNumber must be less than 2^64"); ensure!( - blockNumber <= U256::from(ccx.ecx.block.number), + blockNumber <= U256::from(ccx.ecx.block().number()), "block number must be less than or equal to the current block number" ); - ccx.ecx.journaled_state.database.set_blockhash(blockNumber, blockHash); + ccx.ecx.db_mut().set_blockhash(blockNumber, blockHash); Ok(Default::default()) } } -impl Cheatcode for startDebugTraceRecordingCall { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for executeTransactionCall +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { + use crate::env::FORGE_CONTEXT; + + // Block in script contexts. + if let Some(ctx) = FORGE_CONTEXT.get() + && *ctx == ForgeContext::ScriptGroup + { + return Err(fmt_err!("executeTransaction is not allowed in forge script")); + } + + // Decode the RLP-encoded signed transaction. + let tx = FoundryTxEnvelope::decode(&mut self.rawTx.as_ref()) + .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?; + + // Reject unsupported transaction types. + // TODO: add support for OP deposit transactions. + if matches!(tx, FoundryTxEnvelope::Deposit(_)) { + return Err(fmt_err!( + "OP deposit transactions are not yet supported by executeTransaction" + )); + } + // TODO: add support for Tempo AA transactions. + if matches!(tx, FoundryTxEnvelope::Tempo(_)) { + return Err(fmt_err!("Tempo transactions are not yet supported by executeTransaction")); + } + + // Recover signer from the transaction signature. + let sender = tx.recover().map_err(|err| fmt_err!("failed to recover signer: {err}"))?; + + // Build TxEnv from the recovered transaction. + let tx_env = >::from_recovered_tx(&tx, sender); + + // Save current env for restoration after execution. + let cached_env = ccx.ecx.to_env(); + + // Override env for isolated execution. + ccx.ecx.block_mut().basefee = 0; + *ccx.ecx.tx_mut() = tx_env; + ccx.ecx.tx_mut().gas_price = 0; + ccx.ecx.tx_mut().gas_priority_fee = None; + + // Enable nonce checks for realistic simulation. + ccx.ecx.cfg_mut().disable_nonce_check = false; + + // EIP-3860: enforce initcode size limit. + ccx.ecx.cfg_mut().limit_contract_initcode_size = + Some(revm::primitives::eip3860::MAX_INITCODE_SIZE); + + // Snapshot the modified env for EVM construction. + let modified_env = ccx.ecx.to_env(); + + // Mark as inner context so isolation mode doesn't trigger a nested transact_inner + // when the inner EVM executes calls at depth == 1. + executor.set_in_inner_context(true, Some(sender)); + + let res = { + let mut inspector = executor.get_inspector(ccx.state); + + let res = { + let (db, journal) = ccx.ecx.journal_mut().as_db_and_inner(); + + // Create a new EVM instance with the inspector. + let mut evm = CTX::new_nested_evm(db, modified_env.clone(), &mut *inspector); + + // Clone journaled state and mark all accounts/slots cold. + evm.journal_inner_mut().state = { + let mut state = journal.state.clone(); + for (addr, acc_mut) in &mut state { + if journal.warm_addresses.is_cold(addr) { + acc_mut.mark_cold(); + } + for slot_mut in acc_mut.storage.values_mut() { + slot_mut.is_cold = true; + slot_mut.original_value = slot_mut.present_value; + } + } + state + }; + + // Set depth to 1 for proper trace collection. + evm.journal_inner_mut().depth = 1; + + evm.transact(modified_env.tx) + }; + + // Inspector must be dropped before we can call set_in_inner_context again. + drop(inspector); + res + }; + + // Restore the original environment. + ccx.ecx.apply_env(cached_env); + + // Reset inner context flag. + executor.set_in_inner_context(false, None); + + let res = res.map_err(|e| fmt_err!("transaction execution failed: {e}"))?; + + // Merge state changes back into the parent journaled state. + for (addr, mut acc) in res.state { + let Some(acc_mut) = ccx.ecx.journal_mut().evm_state_mut().get_mut(&addr) else { + ccx.ecx.journal_mut().evm_state_mut().insert(addr, acc); + continue; + }; + + // Preserve warm account status from parent context. + if acc.status.contains(AccountStatus::Cold) + && !acc_mut.status.contains(AccountStatus::Cold) + { + acc.status -= AccountStatus::Cold; + } + acc_mut.info = acc.info; + acc_mut.status |= acc.status; + + // Merge storage changes. + for (key, val) in acc.storage { + let Some(slot_mut) = acc_mut.storage.get_mut(&key) else { + acc_mut.storage.insert(key, val); + continue; + }; + slot_mut.present_value = val.present_value; + slot_mut.is_cold &= val.is_cold; + } + } + + // Return output bytes. + let output = match res.result { + ExecutionResult::Success { output, .. } => output.into_data(), + ExecutionResult::Halt { reason, .. } => { + return Err(fmt_err!("transaction halted: {reason:?}")); + } + ExecutionResult::Revert { output, .. } => { + return Err(fmt_err!("transaction reverted: {}", hex::encode_prefixed(&output))); + } + }; + + Ok(output.abi_encode()) + } +} + +impl Cheatcode for startDebugTraceRecordingCall { + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Some(tracer) = executor.tracing_inspector() else { return Err(Error::from("no tracer initiated, consider adding -vvv flag")); }; @@ -1106,8 +1279,12 @@ impl Cheatcode for startDebugTraceRecordingCall { } } -impl Cheatcode for stopAndReturnDebugTraceRecordingCall { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl Cheatcode for stopAndReturnDebugTraceRecordingCall { + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Some(tracer) = executor.tracing_inspector() else { return Err(Error::from("no tracer initiated, consider adding -vvv flag")); }; @@ -1141,8 +1318,8 @@ impl Cheatcode for stopAndReturnDebugTraceRecordingCall { } } -impl Cheatcode for setEvmVersionCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for setEvmVersionCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { evm } = self; let spec_id = evm_spec_id( EvmVersion::from_str(evm) @@ -1153,64 +1330,80 @@ impl Cheatcode for setEvmVersionCall { } } -impl Cheatcode for getEvmVersionCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - Ok(ccx.ecx.cfg.spec.to_string().to_lowercase().abi_encode()) +impl Cheatcode for getEvmVersionCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + let spec: SpecId = ccx.ecx.cfg().spec().into(); + Ok(spec.to_string().to_lowercase().abi_encode()) } } -pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - let account = journal.load_account(db, *address)?; - Ok(account.info.nonce.abi_encode()) +pub(super) fn get_nonce>( + ccx: &mut CheatsCtxt<'_, CTX>, + address: &Address, +) -> Result { + let account = ccx.ecx.journal_mut().load_account(*address)?; + Ok(account.data.info.nonce.abi_encode()) } -fn inner_snapshot_state(ccx: &mut CheatsCtxt) -> Result { - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - Ok(db.snapshot_state(journal, &mut env).abi_encode()) +fn inner_snapshot_state>( + ccx: &mut CheatsCtxt<'_, CTX>, +) -> Result { + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + Ok(db.snapshot_state(inner, &mut env).abi_encode()) } -fn inner_revert_to_state(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - let result = if let Some(journaled_state) = - db.revert_state(snapshot_id, &*journal, &mut env, RevertStateSnapshotAction::RevertKeep) +fn inner_revert_to_state>( + ccx: &mut CheatsCtxt<'_, CTX>, + snapshot_id: U256, +) -> Result { + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + if let Some(restored) = + db.revert_state(snapshot_id, inner, &mut env, RevertStateSnapshotAction::RevertKeep) { - // we reset the evm's journaled_state to the state of the snapshot previous state - ccx.ecx.journaled_state.inner = journaled_state; - true + *inner = restored; + Ok(true.abi_encode()) } else { - false - }; - Ok(result.abi_encode()) + Ok(false.abi_encode()) + } } -fn inner_revert_to_state_and_delete(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - - let result = if let Some(journaled_state) = - db.revert_state(snapshot_id, &*journal, &mut env, RevertStateSnapshotAction::RevertRemove) +fn inner_revert_to_state_and_delete< + CTX: FoundryContextExt + ContextTr, +>( + ccx: &mut CheatsCtxt<'_, CTX>, + snapshot_id: U256, +) -> Result { + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + if let Some(restored) = + db.revert_state(snapshot_id, inner, &mut env, RevertStateSnapshotAction::RevertRemove) { - // we reset the evm's journaled_state to the state of the snapshot previous state - ccx.ecx.journaled_state.inner = journaled_state; - true + *inner = restored; + Ok(true.abi_encode()) } else { - false - }; - Ok(result.abi_encode()) + Ok(false.abi_encode()) + } } -fn inner_delete_state_snapshot(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result { - let result = ccx.ecx.journaled_state.database.delete_state_snapshot(snapshot_id); +fn inner_delete_state_snapshot>( + ccx: &mut CheatsCtxt<'_, CTX>, + snapshot_id: U256, +) -> Result { + let result = ccx.ecx.db_mut().delete_state_snapshot(snapshot_id); Ok(result.abi_encode()) } -fn inner_delete_state_snapshots(ccx: &mut CheatsCtxt) -> Result { - ccx.ecx.journaled_state.database.delete_state_snapshots(); +fn inner_delete_state_snapshots>( + ccx: &mut CheatsCtxt<'_, CTX>, +) -> Result { + ccx.ecx.db_mut().delete_state_snapshots(); Ok(Default::default()) } -fn inner_value_snapshot( - ccx: &mut CheatsCtxt, +fn inner_value_snapshot( + ccx: &mut CheatsCtxt<'_, CTX>, group: Option, name: Option, value: String, @@ -1222,8 +1415,8 @@ fn inner_value_snapshot( Ok(Default::default()) } -fn inner_last_gas_snapshot( - ccx: &mut CheatsCtxt, +fn inner_last_gas_snapshot( + ccx: &mut CheatsCtxt<'_, CTX>, group: Option, name: Option, value: u64, @@ -1235,8 +1428,8 @@ fn inner_last_gas_snapshot( Ok(value.abi_encode()) } -fn inner_start_gas_snapshot( - ccx: &mut CheatsCtxt, +fn inner_start_gas_snapshot( + ccx: &mut CheatsCtxt<'_, CTX>, group: Option, name: Option, ) -> Result { @@ -1251,7 +1444,7 @@ fn inner_start_gas_snapshot( group: group.clone(), name: name.clone(), gas_used: 0, - depth: ccx.ecx.journaled_state.depth(), + depth: ccx.ecx.journal().depth(), }); ccx.state.gas_metering.active_gas_snapshot = Some((group, name)); @@ -1261,8 +1454,8 @@ fn inner_start_gas_snapshot( Ok(Default::default()) } -fn inner_stop_gas_snapshot( - ccx: &mut CheatsCtxt, +fn inner_stop_gas_snapshot( + ccx: &mut CheatsCtxt<'_, CTX>, group: Option, name: Option, ) -> Result { @@ -1312,8 +1505,8 @@ fn inner_stop_gas_snapshot( } // Derives the snapshot group and name from the provided group and name or the running contract. -fn derive_snapshot_name( - ccx: &CheatsCtxt, +fn derive_snapshot_name( + ccx: &CheatsCtxt<'_, CTX>, group: Option, name: Option, ) -> (String, String) { @@ -1371,18 +1564,20 @@ fn read_callers(state: &Cheatcodes, default_sender: &Address, call_depth: usize) } /// Ensures the `Account` is loaded and touched. -pub(super) fn journaled_account<'a>( - ecx: Ecx<'a, '_, '_>, +pub(super) fn journaled_account>( + ecx: &mut CTX, addr: Address, -) -> Result<&'a mut Account> { +) -> Result<&mut Account> { ensure_loaded_account(ecx, addr)?; - Ok(ecx.journaled_state.state.get_mut(&addr).expect("account is loaded")) + Ok(ecx.journal_mut().evm_state_mut().get_mut(&addr).expect("account is loaded")) } -pub(super) fn ensure_loaded_account(ecx: Ecx, addr: Address) -> Result<()> { - let (db, journal, _) = ecx.as_db_env_and_journal(); - journal.load_account(db, addr)?; - journal.touch(addr); +pub(super) fn ensure_loaded_account>( + ecx: &mut CTX, + addr: Address, +) -> Result<()> { + ecx.journal_mut().load_account(addr)?; + ecx.journal_mut().touch_account(addr); Ok(()) } @@ -1422,7 +1617,9 @@ fn genesis_account(account: &Account) -> GenesisAccount { } /// Helper function to returns state diffs recorded for each changed account. -fn get_recorded_state_diffs(ccx: &mut CheatsCtxt) -> BTreeMap { +fn get_recorded_state_diffs>( + ccx: &mut CheatsCtxt<'_, CTX>, +) -> BTreeMap { let mut state_diffs: BTreeMap = BTreeMap::default(); // First, collect all unique addresses we need to look up @@ -1599,17 +1796,16 @@ const EIP1822_PROXIABLE_SLOT: &str = "c5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"; /// Helper function to get the contract data from the deployed code at an address. -fn get_contract_data<'a>( - ccx: &'a mut CheatsCtxt, +fn get_contract_data<'a, CTX: ContextTr>( + ccx: &'a mut CheatsCtxt<'_, CTX>, address: Address, ) -> Option<(&'a foundry_compilers::ArtifactId, &'a foundry_common::contracts::ContractData)> { // Check if we have available artifacts to match against let artifacts = ccx.state.config.available_artifacts.as_ref()?; // Try to load the account and get its code - let (db, journal, _) = ccx.ecx.as_db_env_and_journal(); - let account = journal.load_account(db, address).ok()?; - let code = account.info.code.as_ref()?; + let account = ccx.ecx.journal_mut().load_account(address).ok()?; + let code = account.data.info.code.as_ref()?; // Skip if code is empty if code.is_empty() { @@ -1643,8 +1839,13 @@ fn get_contract_data<'a>( } /// Helper function to set / unset cold storage slot of the target address. -fn set_cold_slot(ccx: &mut CheatsCtxt, target: Address, slot: U256, cold: bool) { - if let Some(account) = ccx.ecx.journaled_state.state.get_mut(&target) +fn set_cold_slot>( + ccx: &mut CheatsCtxt<'_, CTX>, + target: Address, + slot: U256, + cold: bool, +) { + if let Some(account) = ccx.ecx.journal_mut().evm_state_mut().get_mut(&target) && let Some(storage_slot) = account.storage.get_mut(&slot) { storage_slot.is_cold = cold; diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index ea3a003a7ffc4..b631a9a2a6f3e 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -3,216 +3,246 @@ use crate::{ json::json_value_to_token, }; use alloy_dyn_abi::DynSolValue; +use alloy_network::AnyNetwork; use alloy_primitives::{B256, U256}; use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use foundry_common::provider::ProviderBuilder; -use foundry_evm_core::{AsEnvMut, ContextExt, fork::CreateFork}; +use foundry_evm_core::{FoundryContextExt, backend::FoundryJournalExt, fork::CreateFork}; +use revm::context::ContextTr; -impl Cheatcode for activeForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for activeForkCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; ccx.ecx - .journaled_state - .database + .db() .active_fork_id() .map(|id| id.abi_encode()) .ok_or_else(|| fmt_err!("no active fork")) } } -impl Cheatcode for createFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for createFork_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias } = self; create_fork(ccx, urlOrAlias, None) } } -impl Cheatcode for createFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for createFork_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, blockNumber } = self; create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } -impl Cheatcode for createFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for createFork_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, txHash } = self; create_fork_at_transaction(ccx, urlOrAlias, txHash) } } -impl Cheatcode for createSelectFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for createSelectFork_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias } = self; create_select_fork(ccx, urlOrAlias, None) } } -impl Cheatcode for createSelectFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for createSelectFork_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, blockNumber } = self; create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } -impl Cheatcode for createSelectFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for createSelectFork_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, txHash } = self; create_select_fork_at_transaction(ccx, urlOrAlias, txHash) } } -impl Cheatcode for rollFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for rollFork_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { blockNumber } = self; persist_caller(ccx); - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - db.roll_fork(None, (*blockNumber).to(), &mut env, journal)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + db.roll_fork(None, (*blockNumber).to(), &mut env, inner)?; Ok(Default::default()) } } -impl Cheatcode for rollFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for rollFork_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { txHash } = self; persist_caller(ccx); - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - db.roll_fork_to_transaction(None, *txHash, &mut env, journal)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + db.roll_fork_to_transaction(None, *txHash, &mut env, inner)?; Ok(Default::default()) } } -impl Cheatcode for rollFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for rollFork_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId, blockNumber } = self; persist_caller(ccx); - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - db.roll_fork(Some(*forkId), (*blockNumber).to(), &mut env, journal)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + db.roll_fork(Some(*forkId), (*blockNumber).to(), &mut env, inner)?; Ok(Default::default()) } } -impl Cheatcode for rollFork_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for rollFork_3Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId, txHash } = self; persist_caller(ccx); - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - db.roll_fork_to_transaction(Some(*forkId), *txHash, &mut env, journal)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + db.roll_fork_to_transaction(Some(*forkId), *txHash, &mut env, inner)?; Ok(Default::default()) } } -impl Cheatcode for selectForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for selectForkCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId } = self; persist_caller(ccx); check_broadcast(ccx.state)?; - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - db.select_fork(*forkId, &mut env, journal)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + db.select_fork(*forkId, &mut env, inner)?; Ok(Default::default()) } } -impl Cheatcode for transact_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for transact_0Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { txHash } = *self; transact(ccx, executor, txHash, None) } } -impl Cheatcode for transact_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for transact_1Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { forkId, txHash } = *self; transact(ccx, executor, txHash, Some(forkId)) } } -impl Cheatcode for allowCheatcodesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for allowCheatcodesCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account } = self; - ccx.ecx.journaled_state.database.allow_cheatcode_access(*account); + ccx.ecx.db_mut().allow_cheatcode_access(*account); Ok(Default::default()) } } -impl Cheatcode for makePersistent_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for makePersistent_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account } = self; - ccx.ecx.journaled_state.database.add_persistent_account(*account); + ccx.ecx.db_mut().add_persistent_account(*account); Ok(Default::default()) } } -impl Cheatcode for makePersistent_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for makePersistent_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account0, account1 } = self; - ccx.ecx.journaled_state.database.add_persistent_account(*account0); - ccx.ecx.journaled_state.database.add_persistent_account(*account1); + ccx.ecx.db_mut().add_persistent_account(*account0); + ccx.ecx.db_mut().add_persistent_account(*account1); Ok(Default::default()) } } -impl Cheatcode for makePersistent_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for makePersistent_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account0, account1, account2 } = self; - ccx.ecx.journaled_state.database.add_persistent_account(*account0); - ccx.ecx.journaled_state.database.add_persistent_account(*account1); - ccx.ecx.journaled_state.database.add_persistent_account(*account2); + ccx.ecx.db_mut().add_persistent_account(*account0); + ccx.ecx.db_mut().add_persistent_account(*account1); + ccx.ecx.db_mut().add_persistent_account(*account2); Ok(Default::default()) } } -impl Cheatcode for makePersistent_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for makePersistent_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { accounts } = self; for account in accounts { - ccx.ecx.journaled_state.database.add_persistent_account(*account); + ccx.ecx.db_mut().add_persistent_account(*account); } Ok(Default::default()) } } -impl Cheatcode for revokePersistent_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for revokePersistent_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account } = self; - ccx.ecx.journaled_state.database.remove_persistent_account(account); + ccx.ecx.db_mut().remove_persistent_account(account); Ok(Default::default()) } } -impl Cheatcode for revokePersistent_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for revokePersistent_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { accounts } = self; for account in accounts { - ccx.ecx.journaled_state.database.remove_persistent_account(account); + ccx.ecx.db_mut().remove_persistent_account(account); } Ok(Default::default()) } } -impl Cheatcode for isPersistentCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for isPersistentCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { account } = self; - Ok(ccx.ecx.journaled_state.database.is_persistent(account).abi_encode()) + Ok(ccx.ecx.db().is_persistent(account).abi_encode()) } } -impl Cheatcode for rpc_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for rpc_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { method, params } = self; - let url = ccx - .ecx - .journaled_state - .database - .active_fork_url() - .ok_or_else(|| fmt_err!("no active fork URL found"))?; + let url = + ccx.ecx.db().active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; rpc_call(&url, method, params) } } -impl Cheatcode for rpc_1Call { +impl Cheatcode for rpc_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { urlOrAlias, method, params } = self; let url = state.config.rpc_endpoint(urlOrAlias)?.url()?; @@ -220,8 +250,8 @@ impl Cheatcode for rpc_1Call { } } -impl Cheatcode for eth_getLogsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for eth_getLogsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { fromBlock, toBlock, target, topics } = self; let (Ok(from_block), Ok(to_block)) = (u64::try_from(fromBlock), u64::try_from(toBlock)) else { @@ -232,13 +262,9 @@ impl Cheatcode for eth_getLogsCall { bail!("topics array must contain at most 4 elements") } - let url = ccx - .ecx - .journaled_state - .database - .active_fork_url() - .ok_or_else(|| fmt_err!("no active fork URL found"))?; - let provider = ProviderBuilder::new(&url).build()?; + 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 mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block); for (i, &topic) in topics.iter().enumerate() { filter.topics[i] = topic.into(); @@ -266,16 +292,11 @@ impl Cheatcode for eth_getLogsCall { } } -impl Cheatcode for getRawBlockHeaderCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for getRawBlockHeaderCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { blockNumber } = self; - let url = ccx - .ecx - .journaled_state - .database - .active_fork_url() - .ok_or_else(|| fmt_err!("no active fork"))?; - let provider = ProviderBuilder::new(&url).build()?; + let url = ccx.ecx.db().active_fork_url().ok_or_else(|| fmt_err!("no active fork"))?; + let provider = ProviderBuilder::::new(&url).build()?; let block_number = u64::try_from(blockNumber) .map_err(|_| fmt_err!("block number must be less than 2^64"))?; let block = @@ -294,50 +315,64 @@ impl Cheatcode for getRawBlockHeaderCall { } /// Creates and then also selects the new fork -fn create_select_fork(ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option) -> Result { +fn create_select_fork< + CTX: FoundryContextExt + ContextTr, +>( + ccx: &mut CheatsCtxt<'_, CTX>, + url_or_alias: &str, + block: Option, +) -> Result { check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, block)?; - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - let id = db.create_select_fork(fork, &mut env, journal)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + let id = db.create_select_fork(fork, &mut env, inner)?; Ok(id.abi_encode()) } /// Creates a new fork -fn create_fork(ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option) -> Result { +fn create_fork>( + ccx: &mut CheatsCtxt<'_, CTX>, + url_or_alias: &str, + block: Option, +) -> Result { let fork = create_fork_request(ccx, url_or_alias, block)?; - let id = ccx.ecx.journaled_state.database.create_fork(fork)?; + let id = ccx.ecx.db_mut().create_fork(fork)?; Ok(id.abi_encode()) } /// Creates and then also selects the new fork at the given transaction -fn create_select_fork_at_transaction( - ccx: &mut CheatsCtxt, +fn create_select_fork_at_transaction< + CTX: FoundryContextExt + ContextTr, +>( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, transaction: &B256, ) -> Result { check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, None)?; - let (db, journal, mut env) = ccx.ecx.as_db_env_and_journal(); - let id = db.create_select_fork_at_transaction(fork, &mut env, journal, *transaction)?; + let (journal, mut env) = ccx.ecx.journal_and_env_mut(); + let (db, inner) = journal.as_db_and_inner(); + let id = db.create_select_fork_at_transaction(fork, &mut env, inner, *transaction)?; Ok(id.abi_encode()) } /// Creates a new fork at the given transaction -fn create_fork_at_transaction( - ccx: &mut CheatsCtxt, +fn create_fork_at_transaction>( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, transaction: &B256, ) -> Result { let fork = create_fork_request(ccx, url_or_alias, None)?; - let id = ccx.ecx.journaled_state.database.create_fork_at_transaction(fork, *transaction)?; + let id = ccx.ecx.db_mut().create_fork_at_transaction(fork, *transaction)?; Ok(id.abi_encode()) } /// Creates the request object for a new fork request -fn create_fork_request( - ccx: &mut CheatsCtxt, +fn create_fork_request>( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, ) -> Result { @@ -356,7 +391,7 @@ fn create_fork_request( enable_caching: !ccx.state.config.no_storage_caching && ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), url, - env: ccx.ecx.as_env_mut().to_owned(), + env: ccx.ecx.to_env(), evm_opts, }; Ok(fork) @@ -370,20 +405,16 @@ fn check_broadcast(state: &Cheatcodes) -> Result<()> { } } -fn transact( - ccx: &mut CheatsCtxt, +fn transact>( + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, transaction: B256, fork_id: Option, ) -> Result { - let (db, journal, env) = ccx.ecx.as_db_env_and_journal(); - db.transact( - fork_id, - transaction, - env.to_owned(), - journal, - &mut *executor.get_inspector(ccx.state), - )?; + let env = ccx.ecx.to_env(); + let mut inspector = executor.get_inspector(ccx.state); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.transact(fork_id, transaction, env, inner, &mut *inspector)?; Ok(Default::default()) } @@ -391,13 +422,13 @@ fn transact( // 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 -fn persist_caller(ccx: &mut CheatsCtxt) { - ccx.ecx.journaled_state.database.add_persistent_account(ccx.caller); +fn persist_caller>(ccx: &mut CheatsCtxt<'_, CTX>) { + ccx.ecx.db_mut().add_persistent_account(ccx.caller); } /// Performs an Ethereum JSON-RPC request to the given endpoint. fn rpc_call(url: &str, method: &str, params: &str) -> Result { - let provider = ProviderBuilder::new(url).build()?; + let provider = ProviderBuilder::::new(url).build()?; let params_json: serde_json::Value = serde_json::from_str(params)?; let result = foundry_common::block_on(provider.raw_request(method.to_string().into(), params_json)) diff --git a/crates/cheatcodes/src/evm/mapping.rs b/crates/cheatcodes/src/evm/mapping.rs index dab6e6aababe8..3fba5ce012de4 100644 --- a/crates/cheatcodes/src/evm/mapping.rs +++ b/crates/cheatcodes/src/evm/mapping.rs @@ -3,7 +3,7 @@ use alloy_primitives::{Address, B256}; use alloy_sol_types::SolValue; use foundry_common::mapping_slots::MappingSlots; -impl Cheatcode for startMappingRecordingCall { +impl Cheatcode for startMappingRecordingCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.mapping_slots.get_or_insert_default(); @@ -11,7 +11,7 @@ impl Cheatcode for startMappingRecordingCall { } } -impl Cheatcode for stopMappingRecordingCall { +impl Cheatcode for stopMappingRecordingCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.mapping_slots = None; @@ -19,7 +19,7 @@ impl Cheatcode for stopMappingRecordingCall { } } -impl Cheatcode for getMappingLengthCall { +impl Cheatcode for getMappingLengthCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { target, mappingSlot } = self; let result = slot_child(state, target, mappingSlot).map(Vec::len).unwrap_or(0); @@ -27,7 +27,7 @@ impl Cheatcode for getMappingLengthCall { } } -impl Cheatcode for getMappingSlotAtCall { +impl Cheatcode for getMappingSlotAtCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { target, mappingSlot, idx } = self; let result = slot_child(state, target, mappingSlot) @@ -38,7 +38,7 @@ impl Cheatcode for getMappingSlotAtCall { } } -impl Cheatcode for getMappingKeyAndParentOfCall { +impl Cheatcode for getMappingKeyAndParentOfCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { target, elementSlot: slot } = self; let mut found = false; diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 3277ca338ec50..981782ea8a030 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -1,6 +1,11 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, Bytes, U256}; -use revm::{bytecode::Bytecode, context::JournalTr, interpreter::InstructionResult}; +use foundry_evm_core::backend::DatabaseExt; +use revm::{ + bytecode::Bytecode, + context::{ContextTr, JournalTr}, + interpreter::InstructionResult, +}; use std::{cmp::Ordering, collections::VecDeque}; /// Mocked call data. @@ -38,7 +43,7 @@ impl Ord for MockCallDataContext { } } -impl Cheatcode for clearMockedCallsCall { +impl Cheatcode for clearMockedCallsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.mocked_calls = Default::default(); @@ -46,8 +51,8 @@ impl Cheatcode for clearMockedCallsCall { } } -impl Cheatcode for mockCall_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCall_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -56,8 +61,8 @@ impl Cheatcode for mockCall_0Call { } } -impl Cheatcode for mockCall_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCall_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, msgValue, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -66,8 +71,8 @@ impl Cheatcode for mockCall_1Call { } } -impl Cheatcode for mockCall_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCall_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -83,8 +88,8 @@ impl Cheatcode for mockCall_2Call { } } -impl Cheatcode for mockCall_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCall_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, msgValue, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -100,8 +105,8 @@ impl Cheatcode for mockCall_3Call { } } -impl Cheatcode for mockCalls_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCalls_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -110,8 +115,8 @@ impl Cheatcode for mockCalls_0Call { } } -impl Cheatcode for mockCalls_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCalls_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, msgValue, data, returnData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -120,8 +125,8 @@ impl Cheatcode for mockCalls_1Call { } } -impl Cheatcode for mockCallRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCallRevert_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, data, revertData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -130,8 +135,8 @@ impl Cheatcode for mockCallRevert_0Call { } } -impl Cheatcode for mockCallRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCallRevert_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, msgValue, data, revertData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -140,8 +145,8 @@ impl Cheatcode for mockCallRevert_1Call { } } -impl Cheatcode for mockCallRevert_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCallRevert_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, data, revertData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -157,8 +162,8 @@ impl Cheatcode for mockCallRevert_2Call { } } -impl Cheatcode for mockCallRevert_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for mockCallRevert_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { callee, msgValue, data, revertData } = self; let _ = make_acc_non_empty(callee, ccx)?; @@ -174,7 +179,7 @@ impl Cheatcode for mockCallRevert_3Call { } } -impl Cheatcode for mockFunctionCall { +impl Cheatcode for mockFunctionCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, target, data } = self; state.mocked_functions.entry(*callee).or_default().insert(data.clone(), *target); @@ -213,13 +218,17 @@ fn mock_calls( // Etches a single byte onto the account if it is empty to circumvent the `extcodesize` // check Solidity might perform. -fn make_acc_non_empty(callee: &Address, ecx: &mut CheatsCtxt) -> Result { - let acc = ecx.journaled_state.load_account(*callee)?; - - let empty_bytecode = acc.info.code.as_ref().is_none_or(Bytecode::is_empty); +fn make_acc_non_empty>( + callee: &Address, + ccx: &mut CheatsCtxt<'_, CTX>, +) -> Result { + let empty_bytecode = { + let acc = ccx.ecx.journal_mut().load_account(*callee)?; + acc.info.code.as_ref().is_none_or(Bytecode::is_empty) + }; if empty_bytecode { let code = Bytecode::new_raw(Bytes::from_static(&[0u8])); - ecx.journaled_state.set_code(*callee, code); + ccx.ecx.journal_mut().set_code(*callee, code); } Ok(Default::default()) diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index d4619d9368e36..70a489cdc508a 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -1,6 +1,10 @@ use crate::{Cheatcode, CheatsCtxt, Result, Vm::*, evm::journaled_account}; use alloy_primitives::Address; -use revm::context::JournalTr; +use foundry_evm_core::{backend::DatabaseExt, env::FoundryContextExt}; +use revm::{ + context::{ContextTr, JournalTr, Transaction}, + inspector::JournalExt, +}; /// Prank information. #[derive(Clone, Copy, Debug, Default)] @@ -53,72 +57,88 @@ impl Prank { } } -impl Cheatcode for prank_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for prank_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender } = self; prank(ccx, msgSender, None, true, false) } } -impl Cheatcode for startPrank_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startPrank_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender } = self; prank(ccx, msgSender, None, false, false) } } -impl Cheatcode for prank_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for prank_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender, txOrigin } = self; prank(ccx, msgSender, Some(txOrigin), true, false) } } -impl Cheatcode for startPrank_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startPrank_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender, txOrigin } = self; prank(ccx, msgSender, Some(txOrigin), false, false) } } -impl Cheatcode for prank_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for prank_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender, delegateCall } = self; prank(ccx, msgSender, None, true, *delegateCall) } } -impl Cheatcode for startPrank_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startPrank_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender, delegateCall } = self; prank(ccx, msgSender, None, false, *delegateCall) } } -impl Cheatcode for prank_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for prank_3Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender, txOrigin, delegateCall } = self; prank(ccx, msgSender, Some(txOrigin), true, *delegateCall) } } -impl Cheatcode for startPrank_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startPrank_3Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { msgSender, txOrigin, delegateCall } = self; prank(ccx, msgSender, Some(txOrigin), false, *delegateCall) } } -impl Cheatcode for stopPrankCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for stopPrankCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - ccx.state.pranks.remove(&ccx.ecx.journaled_state.depth()); + ccx.state.pranks.remove(&ccx.ecx.journal().depth()); Ok(Default::default()) } } -fn prank( - ccx: &mut CheatsCtxt, +fn prank>( + ccx: &mut CheatsCtxt<'_, CTX>, new_caller: &Address, new_origin: Option<&Address>, single_call: bool, @@ -137,7 +157,7 @@ fn prank( ); } - let depth = ccx.ecx.journaled_state.depth(); + let depth = ccx.ecx.journal().depth(); if let Some(Prank { used, single_call: current_single_call, .. }) = ccx.state.get_prank(depth) { ensure!(used, "cannot overwrite a prank until it is applied at least once"); // This case can only fail if the user calls `vm.startPrank` and then `vm.prank` later on. @@ -151,7 +171,7 @@ fn prank( let prank = Prank::new( ccx.caller, - ccx.ecx.tx.caller, + ccx.ecx.tx().caller(), *new_caller, new_origin.copied(), depth, diff --git a/crates/cheatcodes/src/evm/record_debug_step.rs b/crates/cheatcodes/src/evm/record_debug_step.rs index d1cd2b52f111b..3691ceb52ad00 100644 --- a/crates/cheatcodes/src/evm/record_debug_step.rs +++ b/crates/cheatcodes/src/evm/record_debug_step.rs @@ -51,11 +51,9 @@ fn recursive_flatten_call_trace<'a>( for order in &node.ordering { match order { - TraceMemberOrder::Step(step_idx) => { - if *record_started { - let step = &node.trace.steps[*step_idx]; - flatten_steps.push(CallTraceCtx { node, step }); - } + TraceMemberOrder::Step(step_idx) if *record_started => { + let step = &node.trace.steps[*step_idx]; + flatten_steps.push(CallTraceCtx { node, step }); } TraceMemberOrder::Call(call_idx) => { let child_node_idx = node.children[*call_idx]; diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index c90c1937d0bb3..c63352e171377 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1,7 +1,9 @@ //! Implementations of [`Filesystem`](spec::Group::Filesystem) cheatcodes. use super::string::parse; -use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; +use crate::{ + Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*, inspector::exec_create, +}; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; use alloy_network::AnyTransactionReceipt; @@ -12,8 +14,9 @@ use dialoguer::{Input, Password}; use forge_script_sequence::{BroadcastReader, TransactionWithMetadata}; use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; +use foundry_evm_core::{backend::FoundryJournalExt, env::FoundryContextExt, evm::NestedEvmExt}; use revm::{ - context::{CreateScheme, JournalTr}, + context::{Cfg, ContextTr, CreateScheme, JournalTr}, interpreter::CreateInputs, }; use revm_inspectors::tracing::types::CallKind; @@ -28,7 +31,7 @@ use std::{ }; use walkdir::WalkDir; -impl Cheatcode for existsCall { +impl Cheatcode for existsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -36,7 +39,7 @@ impl Cheatcode for existsCall { } } -impl Cheatcode for fsMetadataCall { +impl Cheatcode for fsMetadataCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -62,7 +65,7 @@ impl Cheatcode for fsMetadataCall { } } -impl Cheatcode for isDirCall { +impl Cheatcode for isDirCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -70,7 +73,7 @@ impl Cheatcode for isDirCall { } } -impl Cheatcode for isFileCall { +impl Cheatcode for isFileCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -78,14 +81,14 @@ impl Cheatcode for isFileCall { } } -impl Cheatcode for projectRootCall { +impl Cheatcode for projectRootCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; Ok(state.config.root.display().to_string().abi_encode()) } } -impl Cheatcode for unixTimeCall { +impl Cheatcode for unixTimeCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self {} = self; let difference = SystemTime::now() @@ -95,7 +98,7 @@ impl Cheatcode for unixTimeCall { } } -impl Cheatcode for closeFileCall { +impl Cheatcode for closeFileCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -106,7 +109,7 @@ impl Cheatcode for closeFileCall { } } -impl Cheatcode for copyFileCall { +impl Cheatcode for copyFileCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { from, to } = self; let from = state.config.ensure_path_allowed(from, FsAccessKind::Read)?; @@ -118,7 +121,7 @@ impl Cheatcode for copyFileCall { } } -impl Cheatcode for createDirCall { +impl Cheatcode for createDirCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, recursive } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Write)?; @@ -127,28 +130,28 @@ impl Cheatcode for createDirCall { } } -impl Cheatcode for readDir_0Call { +impl Cheatcode for readDir_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; read_dir(state, path.as_ref(), 1, false) } } -impl Cheatcode for readDir_1Call { +impl Cheatcode for readDir_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, maxDepth } = self; read_dir(state, path.as_ref(), *maxDepth, false) } } -impl Cheatcode for readDir_2Call { +impl Cheatcode for readDir_2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, maxDepth, followLinks } = self; read_dir(state, path.as_ref(), *maxDepth, *followLinks) } } -impl Cheatcode for readFileCall { +impl Cheatcode for readFileCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -156,7 +159,7 @@ impl Cheatcode for readFileCall { } } -impl Cheatcode for readFileBinaryCall { +impl Cheatcode for readFileBinaryCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -164,7 +167,7 @@ impl Cheatcode for readFileBinaryCall { } } -impl Cheatcode for readLineCall { +impl Cheatcode for readLineCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -190,7 +193,7 @@ impl Cheatcode for readLineCall { } } -impl Cheatcode for readLinkCall { +impl Cheatcode for readLinkCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { linkPath: path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; @@ -199,7 +202,7 @@ impl Cheatcode for readLinkCall { } } -impl Cheatcode for removeDirCall { +impl Cheatcode for removeDirCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, recursive } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Write)?; @@ -208,7 +211,7 @@ impl Cheatcode for removeDirCall { } } -impl Cheatcode for removeFileCall { +impl Cheatcode for removeFileCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Write)?; @@ -225,21 +228,21 @@ impl Cheatcode for removeFileCall { } } -impl Cheatcode for writeFileCall { +impl Cheatcode for writeFileCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, data } = self; write_file(state, path.as_ref(), data.as_bytes()) } } -impl Cheatcode for writeFileBinaryCall { +impl Cheatcode for writeFileBinaryCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, data } = self; write_file(state, path.as_ref(), data) } } -impl Cheatcode for writeLineCall { +impl Cheatcode for writeLineCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { path, data: line } = self; let path = state.config.ensure_path_allowed(path, FsAccessKind::Write)?; @@ -253,7 +256,7 @@ impl Cheatcode for writeLineCall { } } -impl Cheatcode for getArtifactPathByCodeCall { +impl Cheatcode for getArtifactPathByCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { code } = self; let (artifact_id, _) = state @@ -267,7 +270,7 @@ impl Cheatcode for getArtifactPathByCodeCall { } } -impl Cheatcode for getArtifactPathByDeployedCodeCall { +impl Cheatcode for getArtifactPathByDeployedCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { deployedCode } = self; let (artifact_id, _) = state @@ -281,71 +284,119 @@ impl Cheatcode for getArtifactPathByDeployedCodeCall { } } -impl Cheatcode for getCodeCall { +impl Cheatcode for getCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { artifactPath: path } = self; Ok(get_artifact_code(state, path, false)?.abi_encode()) } } -impl Cheatcode for getDeployedCodeCall { +impl Cheatcode for getDeployedCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { artifactPath: path } = self; Ok(get_artifact_code(state, path, true)?.abi_encode()) } } -impl Cheatcode for deployCode_0Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_0Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path } = self; deploy_code(ccx, executor, path, None, None, None) } } -impl Cheatcode for deployCode_1Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_1Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, constructorArgs: args } = self; deploy_code(ccx, executor, path, Some(args), None, None) } } -impl Cheatcode for deployCode_2Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_2Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, value } = self; deploy_code(ccx, executor, path, None, Some(*value), None) } } -impl Cheatcode for deployCode_3Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_3Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, constructorArgs: args, value } = self; deploy_code(ccx, executor, path, Some(args), Some(*value), None) } } -impl Cheatcode for deployCode_4Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_4Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, salt } = self; deploy_code(ccx, executor, path, None, None, Some((*salt).into())) } } -impl Cheatcode for deployCode_5Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_5Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, constructorArgs: args, salt } = self; deploy_code(ccx, executor, path, Some(args), None, Some((*salt).into())) } } -impl Cheatcode for deployCode_6Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_6Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, value, salt } = self; deploy_code(ccx, executor, path, None, Some(*value), Some((*salt).into())) } } -impl Cheatcode for deployCode_7Call { - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { +impl> Cheatcode + for deployCode_7Call +{ + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let Self { artifactPath: path, constructorArgs: args, value, salt } = self; deploy_code(ccx, executor, path, Some(args), Some(*value), Some((*salt).into())) } @@ -353,8 +404,8 @@ impl Cheatcode for deployCode_7Call { /// Helper function to deploy contract from artifact code. /// Uses CREATE2 scheme if salt specified. -fn deploy_code( - ccx: &mut CheatsCtxt, +fn deploy_code>( + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, path: &str, constructor_args: Option<&Bytes>, @@ -376,19 +427,18 @@ fn deploy_code( if let Some(salt) = salt { CreateScheme::Create2 { salt } } else { CreateScheme::Create }; // If prank active at current depth, then use it as caller for create input. - let caller = ccx - .state - .get_prank(ccx.ecx.journaled_state.depth()) - .map_or(ccx.caller, |prank| prank.new_caller); + let caller = + ccx.state.get_prank(ccx.ecx.journal().depth()).map_or(ccx.caller, |prank| prank.new_caller); - let outcome = executor.exec_create( - CreateInputs { + let outcome = exec_create( + executor, + CreateInputs::new( caller, scheme, - value: value.unwrap_or(U256::ZERO), - init_code: bytecode.into(), - gas_limit: ccx.gas_limit, - }, + value.unwrap_or(U256::ZERO), + bytecode.into(), + ccx.gas_limit, + ), ccx, )?; @@ -552,7 +602,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result Cheatcode for ffiCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { commandInput: input } = self; @@ -580,42 +630,42 @@ impl Cheatcode for ffiCall { } } -impl Cheatcode for tryFfiCall { +impl Cheatcode for tryFfiCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { commandInput: input } = self; ffi(state, input).map(|res| res.abi_encode()) } } -impl Cheatcode for promptCall { +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 { +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()) } } -impl Cheatcode for promptSecretUintCall { +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 { +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 { +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)) @@ -729,7 +779,7 @@ fn prompt( } } -impl Cheatcode for getBroadcastCall { +impl Cheatcode for getBroadcastCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId, txType } = self; @@ -744,7 +794,7 @@ impl Cheatcode for getBroadcastCall { } } -impl Cheatcode for getBroadcasts_0Call { +impl Cheatcode for getBroadcasts_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId, txType } = self; @@ -765,7 +815,7 @@ impl Cheatcode for getBroadcasts_0Call { } } -impl Cheatcode for getBroadcasts_1Call { +impl Cheatcode for getBroadcasts_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId } = self; @@ -785,10 +835,10 @@ impl Cheatcode for getBroadcasts_1Call { } } -impl Cheatcode for getDeployment_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getDeployment_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { contractName } = self; - let chain_id = ccx.ecx.cfg.chain_id; + let chain_id = ccx.ecx.cfg().chain_id(); let latest_broadcast = latest_broadcast( contractName, @@ -801,7 +851,7 @@ impl Cheatcode for getDeployment_0Call { } } -impl Cheatcode for getDeployment_1Call { +impl Cheatcode for getDeployment_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId } = self; @@ -816,7 +866,7 @@ impl Cheatcode for getDeployment_1Call { } } -impl Cheatcode for getDeploymentsCall { +impl Cheatcode for getDeploymentsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId } = self; @@ -897,7 +947,7 @@ fn latest_broadcast( #[cfg(test)] mod tests { use super::*; - use crate::CheatsConfig; + use crate::{AnyCtx, CheatsConfig}; use std::sync::Arc; fn cheats() -> Cheatcodes { @@ -937,7 +987,7 @@ mod tests { #[cfg(windows)] let args = vec!["cmd".to_string(), "/c".to_string(), "exit 1".to_string()]; - let result = ffiCall { commandInput: args }.apply(&mut cheats); + let result = Cheatcode::::apply(&ffiCall { commandInput: args }, &mut cheats); // Assert that the cheatcode returned an error. assert!(result.is_err(), "Expected ffi cheatcode to fail, but it succeeded"); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 7dfad36322c6a..6047da22b260d 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1,7 +1,7 @@ //! Cheatcode EVM inspector. use crate::{ - CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, + Cheatcode, CheatsConfig, CheatsCtxt, Error, Result, Vm::{self, AccountAccess}, evm::{ DealRecord, GasRecord, RecordAccess, journaled_account, @@ -21,7 +21,6 @@ use crate::{ utils::IgnoredTraces, }; use alloy_consensus::BlobTransactionSidecarVariant; -use alloy_evm::eth::EthEvmContext; use alloy_network::{TransactionBuilder4844, TransactionBuilder7594}; use alloy_primitives::{ Address, B256, Bytes, Log, TxKind, U256, hex, @@ -37,11 +36,12 @@ use foundry_common::{ mapping_slots::{MappingSlots, step as mapping_step}, }; use foundry_evm_core::{ - Breakpoints, ContextExt, InspectorExt, + Breakpoints, FoundryInspectorExt, InspectorExt, abi::Vm::stopExpectSafeMemoryCall, - backend::{DatabaseError, DatabaseExt, RevertDiagnostic}, + backend::{DatabaseError, DatabaseExt, FoundryJournalExt, RevertDiagnostic}, constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, - evm::{FoundryEvm, new_evm_with_existing_context}, + env::FoundryContextExt, + evm::NestedEvmExt, }; use foundry_evm_traces::{ TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier, @@ -51,13 +51,16 @@ use itertools::Itertools; use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; use rand::Rng; use revm::{ - Inspector, Journal, + Inspector, bytecode::opcode as op, - context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError}, + context::{ + BlockEnv, Cfg, ContextTr, JournalTr, Transaction, TransactionType, result::EVMError, + }, context_interface::{CreateScheme, transaction::SignedAuthorization}, handler::FrameResult, + inspector::JournalExt, interpreter::{ - CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host, + CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, interpreter_types::{Jumps, LoopControl, MemoryTr}, }, @@ -79,7 +82,19 @@ mod utils; pub mod analysis; pub use analysis::CheatcodeAnalysis; -pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>; +/// Bounds for the generic `Inspector` impl on `Cheatcodes`. +/// +/// Shorthand used internally to avoid repeating the full where-clause. +/// Any `EthEvmContext<&mut dyn DatabaseExt>` satisfies these bounds, so all +/// existing call-sites (e.g. `InspectorStackRefMut`) keep working unchanged. +pub trait CheatsCtxExt: + FoundryContextExt + NestedEvmExt +{ +} +impl CheatsCtxExt for CTX where + CTX: FoundryContextExt + NestedEvmExt +{ +} /// Helper trait for obtaining complete [revm::Inspector] instance from mutable reference to /// [Cheatcodes]. @@ -91,78 +106,45 @@ pub trait CheatcodesExecutor { /// [revm::Inspector]. fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box; - /// Obtains [FoundryEvm] instance and executes the given CREATE frame. - fn exec_create( - &mut self, - inputs: CreateInputs, - ccx: &mut CheatsCtxt, - ) -> Result> { - with_evm(self, ccx, |evm| { - evm.journaled_state.depth += 1; - - let frame = FrameInput::Create(Box::new(inputs)); - - let outcome = match evm.run_execution(frame)? { - FrameResult::Call(_) => unreachable!(), - FrameResult::Create(create) => create, - }; - - evm.journaled_state.depth -= 1; - - Ok(outcome) - }) - } - - fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) { - self.get_inspector(ccx.state).console_log(msg); + fn console_log(&mut self, cheats: &mut Cheatcodes, msg: &str) { + self.get_inspector(cheats).console_log(msg); } /// Returns a mutable reference to the tracing inspector if it is available. fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> { None } + + /// Marks that the next EVM frame is an "inner context" so that isolation mode does not + /// trigger a nested `transact_inner`. `original_origin` is stored for the existing + /// inner-context adjustment logic that restores `tx.origin`. + fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option

) {} } -/// Constructs [FoundryEvm] and runs a given closure with it. -fn with_evm( - executor: &mut E, - ccx: &mut CheatsCtxt, - f: F, -) -> Result> +/// Builds a sub-EVM from the current context and executes the given CREATE frame. +pub(crate) fn exec_create( + executor: &mut dyn CheatcodesExecutor, + inputs: CreateInputs, + ccx: &mut CheatsCtxt<'_, CTX>, +) -> std::result::Result> where - E: CheatcodesExecutor + ?Sized, - F: for<'a, 'b> FnOnce( - &mut FoundryEvm<'a, &'b mut dyn InspectorExt>, - ) -> Result>, + CTX::Journal: FoundryJournalExt, { let mut inspector = executor.get_inspector(ccx.state); - let error = std::mem::replace(&mut ccx.ecx.error, Ok(())); - - let ctx = EthEvmContext { - block: ccx.ecx.block.clone(), - cfg: ccx.ecx.cfg.clone(), - tx: ccx.ecx.tx.clone(), - journaled_state: Journal { - inner: ccx.ecx.journaled_state.inner.clone(), - database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt, - }, - local: LocalContext::default(), - chain: (), - error, - }; + ccx.ecx.with_nested_evm(&mut *inspector, |evm| { + evm.journal_inner_mut().depth += 1; - let mut evm = new_evm_with_existing_context(ctx, &mut *inspector); + let frame = FrameInput::Create(Box::new(inputs)); - let res = f(&mut evm)?; + let outcome = match evm.run_execution(frame)? { + FrameResult::Call(_) => unreachable!(), + FrameResult::Create(create) => create, + }; - let ctx = evm.into_context(); - ccx.ecx.journaled_state.inner = ctx.journaled_state.inner; - ccx.ecx.block = ctx.block; - ccx.ecx.tx = ctx.tx; - ccx.ecx.cfg = ctx.cfg; - ccx.ecx.error = ctx.error; + evm.journal_inner_mut().depth -= 1; - Ok(res) + Ok(outcome) + }) } /// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an @@ -314,12 +296,17 @@ impl ArbitraryStorage { /// Saves arbitrary storage value for a given address: /// - store value in changed values cache. /// - update account's storage with given value. - pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) { + pub fn save>( + &mut self, + ecx: &mut CTX, + address: Address, + slot: U256, + data: U256, + ) { self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data); - let (db, journal, _) = ecx.as_db_env_and_journal(); - if journal.load_account(db, address).is_ok() { - journal - .sstore(db, address, slot, data, false) + if ecx.journal_mut().load_account(address).is_ok() { + ecx.journal_mut() + .sstore(address, slot, data) .expect("could not set arbitrary storage value"); } } @@ -329,7 +316,13 @@ impl ArbitraryStorage { /// existing value. /// - if no value was yet generated for given slot, then save new value in cache and update both /// source and target storages. - pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 { + pub fn copy>( + &mut self, + ecx: &mut CTX, + target: Address, + slot: U256, + new_value: U256, + ) -> U256 { let source = self.copies.get(&target).expect("missing arbitrary copy target entry"); let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage"); let value = match storage_cache.get(&slot) { @@ -337,19 +330,17 @@ impl ArbitraryStorage { None => { storage_cache.insert(slot, new_value); // Update source storage with new value. - let (db, journal, _) = ecx.as_db_env_and_journal(); - if journal.load_account(db, *source).is_ok() { - journal - .sstore(db, *source, slot, new_value, false) + if ecx.journal_mut().load_account(*source).is_ok() { + ecx.journal_mut() + .sstore(*source, slot, new_value) .expect("could not copy arbitrary storage value"); } new_value } }; // Update target storage with new value. - let (db, journal, _) = ecx.as_db_env_and_journal(); - if journal.load_account(db, target).is_ok() { - journal.sstore(db, target, slot, value, false).expect("could not set storage"); + if ecx.journal_mut().load_account(target).is_ok() { + ecx.journal_mut().sstore(target, slot, value).expect("could not set storage"); } value } @@ -610,9 +601,9 @@ impl Cheatcodes { } /// Decodes the input data and applies the cheatcode. - fn apply_cheatcode( + fn apply_cheatcode( &mut self, - ecx: Ecx, + ecx: &mut CTX, call: &CallInputs, executor: &mut dyn CheatcodesExecutor, ) -> Result { @@ -633,7 +624,7 @@ impl Cheatcodes { // ensure the caller is allowed to execute cheatcodes, // but only if the backend is in forking mode - ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?; + ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?; apply_dispatch( &decoded, @@ -647,11 +638,14 @@ impl Cheatcodes { /// /// There may be cheatcodes in the constructor of the new contract, in order to allow them /// automatically we need to determine the new address. - fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) { - if ecx.journaled_state.depth <= 1 - || ecx.journaled_state.database.has_cheatcode_access(&caller) - { - ecx.journaled_state.database.allow_cheatcode_access(created_address); + fn allow_cheatcodes_on_create>( + &self, + ecx: &mut CTX, + caller: Address, + created_address: Address, + ) { + if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) { + ecx.db_mut().allow_cheatcode_access(created_address); } } @@ -660,12 +654,12 @@ impl Cheatcodes { /// If the transaction type is [TransactionType::Legacy] we need to upgrade it to /// [TransactionType::Eip2930] in order to use access lists. Other transaction types support /// access lists themselves. - fn apply_accesslist(&mut self, ecx: Ecx) { + fn apply_accesslist(&mut self, ecx: &mut CTX) { if let Some(access_list) = &self.access_list { - ecx.tx.access_list = access_list.clone(); + ecx.tx_mut().access_list = access_list.clone(); - if ecx.tx.tx_type == TransactionType::Legacy as u8 { - ecx.tx.tx_type = TransactionType::Eip2930 as u8; + if ecx.tx().tx_type() == TransactionType::Legacy as u8 { + ecx.tx_mut().tx_type = TransactionType::Eip2930 as u8; } } } @@ -674,7 +668,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, ecx: Ecx) { + pub fn on_revert>(&mut self, ecx: &mut CTX) { trace!(deals=?self.eth_deals.len(), "rolling back deals"); // Delay revert clean up until expected revert is handled, if set. @@ -683,7 +677,7 @@ impl Cheatcodes { } // we only want to apply cleanup top level - if ecx.journaled_state.depth() > 0 { + if ecx.journal().depth() > 0 { return; } @@ -691,31 +685,31 @@ 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) = ecx.journaled_state.state.get_mut(&record.address) { + if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) { acc.info.balance = record.old_balance; } } } - pub fn call_with_executor( + pub fn call_with_executor( &mut self, - ecx: Ecx, + ecx: &mut CTX, call: &mut CallInputs, executor: &mut dyn CheatcodesExecutor, ) -> Option { // Apply custom execution evm version. if let Some(spec_id) = self.execution_evm_version { - ecx.cfg.spec = spec_id; + ecx.cfg_mut().spec = spec_id; } let gas = Gas::new(call.gas_limit); - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); // 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 curr_depth == 0 { - let sender = ecx.tx.caller; + let sender = ecx.tx().caller(); let account = match super::evm::journaled_account(ecx, sender) { Ok(account) => account, Err(err) => { @@ -766,6 +760,13 @@ impl Cheatcodes { return None; } + // `expectRevert`: track max call depth. This is also done in `initialize_interp`, but + // precompile calls don't create an interpreter frame so we must also track it here. + // The callee executes at `curr_depth + 1`. + if let Some(expected) = &mut self.expected_revert { + expected.max_depth = max(curr_depth + 1, expected.max_depth); + } + // Handle expected calls // Grab the different calldatas expected. @@ -837,7 +838,7 @@ impl Cheatcodes { call.target_address = prank.new_caller; call.caller = prank.new_caller; if let Some(new_origin) = prank.new_origin { - ecx.tx.caller = new_origin; + ecx.tx_mut().caller = new_origin; } } @@ -854,7 +855,7 @@ impl Cheatcodes { // At the target depth, or deeper, we set `tx.origin` if let Some(new_origin) = prank.new_origin { - ecx.tx.caller = new_origin; + ecx.tx_mut().caller = new_origin; prank_applied = true; } @@ -883,7 +884,7 @@ impl Cheatcodes { // 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.tx.caller = broadcast.new_origin; + ecx.tx_mut().caller = broadcast.new_origin; call.caller = broadcast.new_origin; // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here @@ -891,8 +892,7 @@ impl Cheatcodes { // into 1559, in the cli package, relatively easily once we // know the target chain supports EIP-1559. if !call.is_static { - let (db, journal, _) = ecx.as_db_env_and_journal(); - if let Err(err) = journal.load_account(db, broadcast.new_origin) { + if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) { return Some(CallOutcome { result: InterpreterResult { result: InstructionResult::Revert, @@ -906,9 +906,10 @@ impl Cheatcodes { } let input = TransactionInput::new(call.input.bytes(ecx)); - + let chain_id = ecx.cfg().chain_id(); + let rpc = ecx.db().active_fork_url(); let account = - ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap(); + ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap(); let mut tx_req = TransactionRequest { from: Some(broadcast.new_origin), @@ -916,7 +917,7 @@ impl Cheatcodes { value: call.transfer_value(), input, nonce: Some(account.info.nonce), - chain_id: Some(ecx.cfg.chain_id), + chain_id: Some(chain_id), gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None }, ..Default::default() }; @@ -960,10 +961,8 @@ impl Cheatcodes { tx_req.authorization_list = Some(active_delegations); } - self.broadcastable_transactions.push_back(BroadcastableTransaction { - rpc: ecx.journaled_state.database.active_fork_url(), - transaction: tx_req.into(), - }); + self.broadcastable_transactions + .push_back(BroadcastableTransaction { rpc, transaction: tx_req.into() }); debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call"); // Explicitly increment nonce if calls are not isolated. @@ -996,11 +995,10 @@ impl Cheatcodes { let old_balance; let old_nonce; - let (db, journal, _) = ecx.as_db_env_and_journal(); - if let Ok(acc) = journal.load_account(db, call.target_address) { - initialized = acc.info.exists(); - old_balance = acc.info.balance; - old_nonce = acc.info.nonce; + if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) { + initialized = acc.data.info.exists(); + old_balance = acc.data.info.balance; + old_nonce = acc.data.info.nonce; } else { initialized = false; old_balance = U256::ZERO; @@ -1021,8 +1019,8 @@ impl 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: ecx.journaled_state.db().active_fork_id().unwrap_or_default(), - chainId: U256::from(ecx.cfg.chain_id), + forkId: ecx.db().active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.cfg().chain_id()), }, accessor: call.caller, account: call.bytecode_address, @@ -1037,11 +1035,7 @@ impl Cheatcodes { reverted: false, deployedCode: Bytes::new(), storageAccesses: vec![], // updated on step - depth: ecx - .journaled_state - .depth() - .try_into() - .expect("journaled state depth exceeds u64"), + depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"), }]); } @@ -1118,15 +1112,15 @@ impl Cheatcodes { } } -impl Inspector> for Cheatcodes { - fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { +impl Inspector for Cheatcodes { + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { // 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() { - ecx.block = block; + *ecx.block_mut() = block; } if let Some(gas_price) = self.gas_price.take() { - ecx.tx.gas_price = gas_price; + ecx.tx_mut().gas_price = gas_price; } // Record gas for current frame. @@ -1136,11 +1130,11 @@ impl Inspector> for Cheatcodes { // `expectRevert`: track the max call depth during `expectRevert` if let Some(expected) = &mut self.expected_revert { - expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth); + expected.max_depth = max(ecx.journal().depth(), expected.max_depth); } } - fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { self.pc = interpreter.bytecode.pc(); if self.broadcast.is_some() { @@ -1171,7 +1165,7 @@ impl Inspector> for Cheatcodes { if !self.allowed_mem_writes.is_empty() { self.check_mem_opcodes( interpreter, - ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"), + ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"), ); } @@ -1186,7 +1180,7 @@ impl Inspector> for Cheatcodes { } } - fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { if self.gas_metering.paused { self.meter_gas_end(interpreter); } @@ -1201,7 +1195,7 @@ impl Inspector> for Cheatcodes { } } - fn log(&mut self, _ecx: Ecx, log: Log) { + fn log(&mut self, _ecx: &mut CTX, log: Log) { if !self.expected_emits.is_empty() && let Some(err) = expect::handle_expect_emit(self, &log, None) { @@ -1215,7 +1209,7 @@ impl Inspector> for Cheatcodes { record_logs(&mut self.recorded_logs, &log); } - fn log_full(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) { + fn log_full(&mut self, interpreter: &mut Interpreter, _ecx: &mut CTX, log: Log) { if !self.expected_emits.is_empty() { expect::handle_expect_emit(self, &log, Some(interpreter)); } @@ -1224,11 +1218,11 @@ impl Inspector> for Cheatcodes { record_logs(&mut self.recorded_logs, &log); } - fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option { + fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option { Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor) } - fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) { + fn call_end(&mut self, ecx: &mut CTX, call: &CallInputs, outcome: &mut CallOutcome) { let cheatcode_call = call.target_address == CHEATCODE_ADDRESS || call.target_address == HARDHAT_CONSOLE_ADDRESS; @@ -1237,11 +1231,11 @@ impl Inspector> for Cheatcodes { // This should be placed before the revert handling, because we might exit early there if !cheatcode_call { // Clean up pranks - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); if let Some(prank) = &self.get_prank(curr_depth) && curr_depth == prank.depth { - ecx.tx.caller = prank.prank_origin; + ecx.tx_mut().caller = prank.prank_origin; // Clean single-call prank once we have returned to the original depth if prank.single_call { @@ -1253,7 +1247,7 @@ impl Inspector> for Cheatcodes { if let Some(broadcast) = &self.broadcast && curr_depth == broadcast.depth { - ecx.tx.caller = broadcast.original_origin; + ecx.tx_mut().caller = broadcast.original_origin; // Clean single-call broadcast once we have returned to the original depth if broadcast.single_call { @@ -1271,7 +1265,7 @@ impl Inspector> for Cheatcodes { } // allow multiple cheatcode calls at the same depth - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); if curr_depth <= assume_no_revert.depth && !cheatcode_call { // Discard run if we're at the same depth as cheatcode, call reverted, and no // specific reason was supplied @@ -1318,7 +1312,7 @@ impl Inspector> for Cheatcodes { } } - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); if curr_depth <= expected_revert.depth { let needs_processing = match expected_revert.kind { ExpectedRevertKind::Default => !cheatcode_call, @@ -1387,7 +1381,7 @@ 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 ecx.journaled_state.depth() > 0 + if ecx.journal().depth() > 0 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop() { // Update the reverted status of all deeper calls if this call reverted, in @@ -1407,14 +1401,13 @@ impl Inspector> for Cheatcodes { // 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. - let (db, journal, _) = ecx.as_db_env_and_journal(); - let curr_depth = journal.depth; + let curr_depth = ecx.journal().depth(); if call_access.depth == curr_depth as u64 - && let Ok(acc) = journal.load_account(db, call.target_address) + && let Ok(acc) = ecx.journal_mut().load_account(call.target_address) { debug_assert!(access_is_call(call_access.kind)); - call_access.newBalance = acc.info.balance; - call_access.newNonce = acc.info.nonce; + call_access.newBalance = acc.data.info.balance; + call_access.newNonce = acc.data.info.nonce; } // Merge the last depth's AccountAccesses into the AccountAccesses at the // current depth, or push them back onto the pending @@ -1444,7 +1437,7 @@ impl Inspector> for Cheatcodes { .expected_emits .iter() .any(|(expected, _)| { - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); expected.depth == curr_depth }) && // Ignore staticcalls @@ -1521,21 +1514,20 @@ impl Inspector> for Cheatcodes { // try to diagnose reverts in multi-fork mode where a call is made to an address that does // not exist - if let TxKind::Call(test_contract) = ecx.tx.kind { + if let TxKind::Call(test_contract) = ecx.tx().kind() { // 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 ecx.journaled_state.db().is_forked_mode() + if ecx.db().is_forked_mode() && outcome.result.result == InstructionResult::Stop && call.target_address != test_contract { - let journaled_state = ecx.journaled_state.clone(); self.fork_revert_diagnostic = - ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state); + ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state()); } } // If the depth is 0, then this is the root call terminating - if ecx.journaled_state.depth() == 0 { + if ecx.journal().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. @@ -1632,10 +1624,10 @@ impl Inspector> for Cheatcodes { } } - fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option { + fn create(&mut self, ecx: &mut CTX, mut input: &mut CreateInputs) -> Option { // Apply custom execution evm version. if let Some(spec_id) = self.execution_evm_version { - ecx.cfg.spec = spec_id; + ecx.cfg_mut().spec = spec_id; } let gas = Gas::new(input.gas_limit()); @@ -1654,7 +1646,7 @@ impl Inspector> for Cheatcodes { }); } - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); // Apply our prank if let Some(prank) = &self.get_prank(curr_depth) @@ -1673,7 +1665,7 @@ impl Inspector> for Cheatcodes { // At the target depth, or deeper, we set `tx.origin` if let Some(new_origin) = prank.new_origin { - ecx.tx.caller = new_origin; + ecx.tx_mut().caller = new_origin; prank_applied = true; } @@ -1691,8 +1683,7 @@ impl Inspector> for Cheatcodes { && curr_depth >= broadcast.depth && input.caller() == broadcast.original_caller { - let (db, journal, _) = ecx.as_db_env_and_journal(); - if let Err(err) = journal.load_account(db, broadcast.new_origin) { + if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) { return Some(CreateOutcome { result: InterpreterResult { result: InstructionResult::Revert, @@ -1703,7 +1694,7 @@ impl Inspector> for Cheatcodes { }); } - ecx.tx.caller = broadcast.new_origin; + ecx.tx_mut().caller = broadcast.new_origin; if curr_depth == broadcast.depth || broadcast.deploy_from_code { // Reset deploy from code flag for upcoming calls; @@ -1711,9 +1702,10 @@ impl Inspector> for Cheatcodes { input.set_caller(broadcast.new_origin); - let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin]; + let rpc = ecx.db().active_fork_url(); + let account = &ecx.journal().evm_state()[&broadcast.new_origin]; self.broadcastable_transactions.push_back(BroadcastableTransaction { - rpc: ecx.journaled_state.database.active_fork_url(), + rpc, transaction: TransactionRequest { from: Some(broadcast.new_origin), to: None, @@ -1736,8 +1728,8 @@ impl Inspector> for Cheatcodes { if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { recorded_account_diffs_stack.push(vec![AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(), - chainId: U256::from(ecx.cfg.chain_id), + forkId: ecx.db().active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.cfg().chain_id()), }, accessor: input.caller(), account: address, @@ -1759,15 +1751,15 @@ impl Inspector> for Cheatcodes { None } - fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) { + fn create_end(&mut self, ecx: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome) { let call = Some(call); - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); // Clean up pranks if let Some(prank) = &self.get_prank(curr_depth) && curr_depth == prank.depth { - ecx.tx.caller = prank.prank_origin; + ecx.tx_mut().caller = prank.prank_origin; // Clean single-call prank once we have returned to the original depth if prank.single_call { @@ -1779,7 +1771,7 @@ impl Inspector> for Cheatcodes { if let Some(broadcast) = &self.broadcast && curr_depth == broadcast.depth { - ecx.tx.caller = broadcast.original_origin; + ecx.tx_mut().caller = broadcast.original_origin; // Clean single-call broadcast once we have returned to the original depth if broadcast.single_call { @@ -1843,20 +1835,24 @@ 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. - let depth = ecx.journaled_state.depth(); + let depth = ecx.journal().depth(); if create_access.depth == depth as u64 { debug_assert_eq!( create_access.kind as u8, crate::Vm::AccountAccessKind::Create as u8 ); - let (db, journal, _) = ecx.as_db_env_and_journal(); if let Some(address) = outcome.address - && let Ok(created_acc) = journal.load_account(db, address) + && let Ok(created_acc) = ecx.journal_mut().load_account(address) { - create_access.newBalance = created_acc.info.balance; - create_access.newNonce = created_acc.info.nonce; - create_access.deployedCode = - created_acc.info.code.clone().unwrap_or_default().original_bytes(); + create_access.newBalance = created_acc.data.info.balance; + create_access.newNonce = created_acc.data.info.nonce; + create_access.deployedCode = created_acc + .data + .info + .code + .clone() + .unwrap_or_default() + .original_bytes(); } } // Merge the last depth's AccountAccesses into the AccountAccesses at the @@ -1873,16 +1869,15 @@ impl Inspector> for Cheatcodes { } // Match the create against expected_creates - let (db, journal, _) = ecx.as_db_env_and_journal(); if !self.expected_creates.is_empty() && let (Some(address), Some(call)) = (outcome.address, call) - && let Ok(created_acc) = journal.load_account(db, address) + && let Ok(created_acc) = ecx.journal_mut().load_account(address) { - let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes(); + let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes(); if let Some((index, _)) = self.expected_creates.iter().find_position(|expected_create| { - expected_create.deployer == call.caller - && expected_create.create_scheme.eq(call.scheme.into()) + expected_create.deployer == call.caller() + && expected_create.create_scheme.eq(call.scheme().into()) && expected_create.bytecode == bytecode }) { @@ -1892,10 +1887,9 @@ impl Inspector> for Cheatcodes { } } -impl InspectorExt for Cheatcodes { - fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool { - if let CreateScheme::Create2 { .. } = inputs.scheme { - let depth = ecx.journaled_state.depth(); +impl FoundryInspectorExt for Cheatcodes { + fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool { + if let CreateScheme::Create2 { .. } = inputs.scheme() { let target_depth = if let Some(prank) = &self.get_prank(depth) { prank.depth } else if let Some(broadcast) = &self.broadcast { @@ -1933,10 +1927,14 @@ impl Cheatcodes { } #[cold] - fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { + fn meter_gas_record( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut CTX, + ) { if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() { self.gas_metering.gas_records.iter_mut().for_each(|record| { - let curr_depth = ecx.journaled_state.depth(); + let curr_depth = ecx.journal().depth(); if curr_depth == record.depth { // Skip the first opcode of the first call frame as it includes the gas cost of // creating the snapshot. @@ -1997,7 +1995,11 @@ impl Cheatcodes { /// cache) from mapped source address to the target address. /// - generates arbitrary value and saves it in target address storage. #[cold] - fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { + fn arbitrary_storage_end( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut CTX, + ) { let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD { (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address) } else { @@ -2049,7 +2051,11 @@ impl Cheatcodes { } #[cold] - fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { + fn record_state_diffs( + &mut self, + interpreter: &mut Interpreter, + ecx: &mut CTX, + ) { let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return }; match interpreter.bytecode.opcode() { op::SELFDESTRUCT => { @@ -2059,11 +2065,15 @@ impl Cheatcodes { // get previous balance, nonce and initialized status of the target account let target = try_or_return!(interpreter.stack.peek(0)); let target = Address::from_word(B256::from(target)); - let (db, journal, _) = ecx.as_db_env_and_journal(); - let (initialized, old_balance, old_nonce) = journal - .load_account(db, target) + let (initialized, old_balance, old_nonce) = ecx + .journal_mut() + .load_account(target) .map(|account| { - (account.info.exists(), account.info.balance, account.info.nonce) + ( + account.data.info.exists(), + account.data.info.balance, + account.data.info.nonce, + ) }) .unwrap_or_default(); @@ -2076,8 +2086,8 @@ impl Cheatcodes { // register access for the target account last.push(crate::Vm::AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(), - chainId: U256::from(ecx.cfg.chain_id), + forkId: ecx.db().active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.cfg().chain_id()), }, accessor: interpreter.input.target_address, account: target, @@ -2093,7 +2103,7 @@ impl Cheatcodes { deployedCode: Bytes::new(), storageAccesses: vec![], depth: ecx - .journaled_state + .journal() .depth() .try_into() .expect("journaled state depth exceeds u64"), @@ -2110,8 +2120,7 @@ impl Cheatcodes { // it's not set (zero value) let mut present_value = U256::ZERO; // Try to load the account and the slot's present value - let (db, journal, _) = ecx.as_db_env_and_journal(); - if journal.load_account(db, address).is_ok() + if ecx.journal_mut().load_account(address).is_ok() && let Some(previous) = ecx.sload(address, key) { present_value = previous.data; @@ -2124,11 +2133,8 @@ impl Cheatcodes { newValue: present_value.into(), reverted: false, }; - let curr_depth = ecx - .journaled_state - .depth() - .try_into() - .expect("journaled state depth exceeds u64"); + let curr_depth = + ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"); append_storage_access(last, access, curr_depth); } op::SSTORE => { @@ -2140,8 +2146,7 @@ impl 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; - let (db, journal, _) = ecx.as_db_env_and_journal(); - if journal.load_account(db, address).is_ok() + if ecx.journal_mut().load_account(address).is_ok() && let Some(previous) = ecx.sload(address, key) { previous_value = previous.data; @@ -2155,11 +2160,8 @@ impl Cheatcodes { newValue: value.into(), reverted: false, }; - let curr_depth = ecx - .journaled_state - .depth() - .try_into() - .expect("journaled state depth exceeds u64"); + let curr_depth = + ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"); append_storage_access(last, access, curr_depth); } @@ -2177,25 +2179,21 @@ impl Cheatcodes { let initialized; let balance; let nonce; - let (db, journal, _) = ecx.as_db_env_and_journal(); - if let Ok(acc) = journal.load_account(db, address) { - initialized = acc.info.exists(); - balance = acc.info.balance; - nonce = acc.info.nonce; + if let Ok(acc) = ecx.journal_mut().load_account(address) { + initialized = acc.data.info.exists(); + balance = acc.data.info.balance; + nonce = acc.data.info.nonce; } else { initialized = false; balance = U256::ZERO; nonce = 0; } - let curr_depth = ecx - .journaled_state - .depth() - .try_into() - .expect("journaled state depth exceeds u64"); + let curr_depth = + ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"); let account_access = crate::Vm::AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(), - chainId: U256::from(ecx.cfg.chain_id), + forkId: ecx.db().active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.cfg().chain_id()), }, accessor: interpreter.input.target_address, account: address, @@ -2507,29 +2505,61 @@ fn append_storage_access( } } +/// Returns the [`spec::Cheatcode`] definition for a given [`spec::CheatcodeDef`] implementor. +fn cheatcode_of(_: &T) -> &'static spec::Cheatcode<'static> { + T::CHEATCODE +} + +fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str { + cheat.func.signature.split('(').next().unwrap() +} + +fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str { + cheat.func.id +} + +fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str { + cheat.func.signature +} + /// Dispatches the cheatcode call to the appropriate function. -fn apply_dispatch( +fn apply_dispatch( calls: &Vm::VmCalls, - ccx: &mut CheatsCtxt, + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, ) -> Result { - let cheat = calls_as_dyn_cheatcode(calls); + // Extract metadata for logging/deprecation via CheatcodeDef. + macro_rules! get_cheatcode { + ($($variant:ident),*) => { + match calls { + $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)* + } + }; + } + let cheat = vm_calls!(get_cheatcode); - let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered(); - trace!(target: "cheatcodes", ?cheat, "applying"); + let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered(); + trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying"); - if let spec::Status::Deprecated(replacement) = *cheat.status() { - ccx.state.deprecated.insert(cheat.signature(), replacement); + if let spec::Status::Deprecated(replacement) = cheat.status { + ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement); } - // Apply the cheatcode. - let mut result = cheat.dyn_apply(ccx, executor); + // Monomorphized dispatch: calls apply_full directly, no trait objects. + macro_rules! dispatch { + ($($variant:ident),*) => { + match calls { + $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)* + } + }; + } + let mut result = vm_calls!(dispatch); // Format the error message to include the cheatcode name. if let Err(e) = &mut result && e.is_str() { - let name = cheat.name(); + let name = cheatcode_name(cheat); // Skip showing the cheatcode name for: // - assertions: too verbose, and can already be inferred from the error message // - `rpcUrl`: forge-std relies on it in `getChainWithUpdatedRpcUrl` @@ -2549,17 +2579,6 @@ fn apply_dispatch( result } -fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode { - macro_rules! as_dyn { - ($($variant:ident),*) => { - match calls { - $(Vm::VmCalls::$variant(cheat) => cheat,)* - } - }; - } - vm_calls!(as_dyn) -} - /// Helper function to check if frame execution will exit. fn will_exit(action: &InterpreterAction) -> bool { match action { diff --git a/crates/cheatcodes/src/inspector/utils.rs b/crates/cheatcodes/src/inspector/utils.rs index 58d1f2f90d7b7..b66eb31291933 100644 --- a/crates/cheatcodes/src/inspector/utils.rs +++ b/crates/cheatcodes/src/inspector/utils.rs @@ -1,7 +1,11 @@ -use super::Ecx; +use super::CheatsCtxExt; use crate::inspector::Cheatcodes; use alloy_primitives::{Address, Bytes, U256}; -use revm::interpreter::{CreateInputs, CreateScheme}; +use revm::{ + context::ContextTr, + inspector::JournalExt, + interpreter::{CreateInputs, CreateScheme}, +}; /// Common behaviour of legacy and EOF create inputs. pub(crate) trait CommonCreateInput { @@ -12,27 +16,31 @@ pub(crate) trait CommonCreateInput { fn scheme(&self) -> Option; fn set_caller(&mut self, caller: Address); fn log_debug(&self, cheatcode: &mut Cheatcodes, scheme: &CreateScheme); - fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: Ecx) -> Address; + fn allow_cheatcodes( + &self, + cheatcodes: &mut Cheatcodes, + ecx: &mut CTX, + ) -> Address; } impl CommonCreateInput for &mut CreateInputs { fn caller(&self) -> Address { - self.caller + CreateInputs::caller(self) } fn gas_limit(&self) -> u64 { - self.gas_limit + CreateInputs::gas_limit(self) } fn value(&self) -> U256 { - self.value + CreateInputs::value(self) } fn init_code(&self) -> Bytes { - self.init_code.clone() + CreateInputs::init_code(self).clone() } fn scheme(&self) -> Option { - Some(self.scheme) + Some(CreateInputs::scheme(self)) } fn set_caller(&mut self, caller: Address) { - self.caller = caller; + CreateInputs::set_call(self, caller); } fn log_debug(&self, cheatcode: &mut Cheatcodes, scheme: &CreateScheme) { let kind = match scheme { @@ -42,15 +50,16 @@ impl CommonCreateInput for &mut CreateInputs { }; debug!(target: "cheatcodes", tx=?cheatcode.broadcastable_transactions.back().unwrap(), "broadcastable {kind}"); } - fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: Ecx) -> Address { - let old_nonce = ecx - .journaled_state - .state - .get(&self.caller) - .map(|acc| acc.info.nonce) - .unwrap_or_default(); + fn allow_cheatcodes( + &self, + cheatcodes: &mut Cheatcodes, + ecx: &mut CTX, + ) -> Address { + let caller = CreateInputs::caller(self); + let old_nonce = + ecx.journal().evm_state().get(&caller).map(|acc| acc.info.nonce).unwrap_or_default(); let created_address = self.created_address(old_nonce); - cheatcodes.allow_cheatcodes_on_create(ecx, self.caller, created_address); + cheatcodes.allow_cheatcodes_on_create(ecx, caller, created_address); created_address } } diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 02fa0777def82..7605359ace98a 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -12,133 +12,133 @@ use std::{ collections::{BTreeMap, BTreeSet}, }; -impl Cheatcode for keyExistsCall { +impl Cheatcode for keyExistsCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; check_json_key_exists(json, key) } } -impl Cheatcode for keyExistsJsonCall { +impl Cheatcode for keyExistsJsonCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; check_json_key_exists(json, key) } } -impl Cheatcode for parseJson_0Call { +impl Cheatcode for parseJson_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json } = self; parse_json(json, "$", state.struct_defs()) } } -impl Cheatcode for parseJson_1Call { +impl Cheatcode for parseJson_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json(json, key, state.struct_defs()) } } -impl Cheatcode for parseJsonUintCall { +impl Cheatcode for parseJsonUintCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Uint(256)) } } -impl Cheatcode for parseJsonUintArrayCall { +impl Cheatcode for parseJsonUintArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Uint(256)))) } } -impl Cheatcode for parseJsonIntCall { +impl Cheatcode for parseJsonIntCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Int(256)) } } -impl Cheatcode for parseJsonIntArrayCall { +impl Cheatcode for parseJsonIntArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Int(256)))) } } -impl Cheatcode for parseJsonBoolCall { +impl Cheatcode for parseJsonBoolCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Bool) } } -impl Cheatcode for parseJsonBoolArrayCall { +impl Cheatcode for parseJsonBoolArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bool))) } } -impl Cheatcode for parseJsonAddressCall { +impl Cheatcode for parseJsonAddressCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Address) } } -impl Cheatcode for parseJsonAddressArrayCall { +impl Cheatcode for parseJsonAddressArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Address))) } } -impl Cheatcode for parseJsonStringCall { +impl Cheatcode for parseJsonStringCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::String) } } -impl Cheatcode for parseJsonStringArrayCall { +impl Cheatcode for parseJsonStringArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::String))) } } -impl Cheatcode for parseJsonBytesCall { +impl Cheatcode for parseJsonBytesCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Bytes) } } -impl Cheatcode for parseJsonBytesArrayCall { +impl Cheatcode for parseJsonBytesArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::Bytes))) } } -impl Cheatcode for parseJsonBytes32Call { +impl Cheatcode for parseJsonBytes32Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::FixedBytes(32)) } } -impl Cheatcode for parseJsonBytes32ArrayCall { +impl Cheatcode for parseJsonBytes32ArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_coerce(json, key, &DynSolType::Array(Box::new(DynSolType::FixedBytes(32)))) } } -impl Cheatcode for parseJsonType_0Call { +impl Cheatcode for parseJsonType_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, typeDescription } = self; parse_json_coerce(json, "$", &resolve_type(typeDescription, state.struct_defs())?) @@ -146,7 +146,7 @@ impl Cheatcode for parseJsonType_0Call { } } -impl Cheatcode for parseJsonType_1Call { +impl Cheatcode for parseJsonType_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, key, typeDescription } = self; parse_json_coerce(json, key, &resolve_type(typeDescription, state.struct_defs())?) @@ -154,7 +154,7 @@ impl Cheatcode for parseJsonType_1Call { } } -impl Cheatcode for parseJsonTypeArrayCall { +impl Cheatcode for parseJsonTypeArrayCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, key, typeDescription } = self; let ty = resolve_type(typeDescription, state.struct_defs())?; @@ -162,14 +162,14 @@ impl Cheatcode for parseJsonTypeArrayCall { } } -impl Cheatcode for parseJsonKeysCall { +impl Cheatcode for parseJsonKeysCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; parse_json_keys(json, key) } } -impl Cheatcode for serializeJsonCall { +impl Cheatcode for serializeJsonCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, value } = self; *state.serialized_jsons.entry(objectKey.into()).or_default() = serde_json::from_str(value)?; @@ -177,56 +177,56 @@ impl Cheatcode for serializeJsonCall { } } -impl Cheatcode for serializeBool_0Call { +impl Cheatcode for serializeBool_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, (*value).into()) } } -impl Cheatcode for serializeUint_0Call { +impl Cheatcode for serializeUint_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, (*value).into()) } } -impl Cheatcode for serializeInt_0Call { +impl Cheatcode for serializeInt_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, (*value).into()) } } -impl Cheatcode for serializeAddress_0Call { +impl Cheatcode for serializeAddress_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, (*value).into()) } } -impl Cheatcode for serializeBytes32_0Call { +impl Cheatcode for serializeBytes32_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, DynSolValue::FixedBytes(*value, 32)) } } -impl Cheatcode for serializeString_0Call { +impl Cheatcode for serializeString_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, value.clone().into()) } } -impl Cheatcode for serializeBytes_0Call { +impl Cheatcode for serializeBytes_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; serialize_json(state, objectKey, valueKey, value.to_vec().into()) } } -impl Cheatcode for serializeBool_1Call { +impl Cheatcode for serializeBool_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -238,7 +238,7 @@ impl Cheatcode for serializeBool_1Call { } } -impl Cheatcode for serializeUint_1Call { +impl Cheatcode for serializeUint_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -250,7 +250,7 @@ impl Cheatcode for serializeUint_1Call { } } -impl Cheatcode for serializeInt_1Call { +impl Cheatcode for serializeInt_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -262,7 +262,7 @@ impl Cheatcode for serializeInt_1Call { } } -impl Cheatcode for serializeAddress_1Call { +impl Cheatcode for serializeAddress_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -274,7 +274,7 @@ impl Cheatcode for serializeAddress_1Call { } } -impl Cheatcode for serializeBytes32_1Call { +impl Cheatcode for serializeBytes32_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -286,7 +286,7 @@ impl Cheatcode for serializeBytes32_1Call { } } -impl Cheatcode for serializeString_1Call { +impl Cheatcode for serializeString_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -298,7 +298,7 @@ impl Cheatcode for serializeString_1Call { } } -impl Cheatcode for serializeBytes_1Call { +impl Cheatcode for serializeBytes_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, values } = self; serialize_json( @@ -312,7 +312,7 @@ impl Cheatcode for serializeBytes_1Call { } } -impl Cheatcode for serializeJsonType_0Call { +impl Cheatcode for serializeJsonType_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { typeDescription, value } = self; let ty = resolve_type(typeDescription, state.struct_defs())?; @@ -322,7 +322,7 @@ impl Cheatcode for serializeJsonType_0Call { } } -impl Cheatcode for serializeJsonType_1Call { +impl Cheatcode for serializeJsonType_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, typeDescription, value } = self; let ty = resolve_type(typeDescription, state.struct_defs())?; @@ -331,7 +331,7 @@ impl Cheatcode for serializeJsonType_1Call { } } -impl Cheatcode for serializeUintToHexCall { +impl Cheatcode for serializeUintToHexCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; let hex = format!("0x{value:x}"); @@ -339,7 +339,7 @@ impl Cheatcode for serializeUintToHexCall { } } -impl Cheatcode for writeJson_0Call { +impl Cheatcode for writeJson_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, path } = self; let json = serde_json::from_str(json).unwrap_or_else(|_| Value::String(json.to_owned())); @@ -348,7 +348,7 @@ impl Cheatcode for writeJson_0Call { } } -impl Cheatcode for writeJson_1Call { +impl Cheatcode for writeJson_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json: value, path, valueKey } = self; diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 1124f65dda9c4..18e2d0ba3b016 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -15,16 +15,16 @@ pub extern crate foundry_cheatcodes_spec as spec; #[macro_use] extern crate tracing; -use alloy_evm::eth::EthEvmContext; use alloy_primitives::Address; use foundry_evm_core::backend::DatabaseExt; -use spec::Status; +use revm::context::{ContextTr, JournalTr}; pub use Vm::ForgeContext; pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; pub use inspector::{ BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, CheatcodesExecutor, + CheatsCtxExt, }; pub use spec::{CheatcodeDef, Vm}; @@ -64,7 +64,7 @@ mod toml; mod utils; /// Cheatcode implementation. -pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { +pub(crate) trait Cheatcode: CheatcodeDef { /// Applies this cheatcode to the given state. /// /// Implement this function if you don't need access to the EVM data. @@ -77,7 +77,7 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// /// Implement this function if you need access to the EVM data. #[inline(always)] - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { self.apply(ccx.state) } @@ -85,60 +85,35 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { /// /// Implement this function if you need access to the executor. #[inline(always)] - fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { + fn apply_full( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, + ) -> Result { let _ = executor; self.apply_stateful(ccx) } } -pub(crate) trait DynCheatcode: 'static + std::fmt::Debug { - fn cheatcode(&self) -> &'static spec::Cheatcode<'static>; - - fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result; -} - -impl DynCheatcode for T { - fn cheatcode(&self) -> &'static spec::Cheatcode<'static> { - Self::CHEATCODE - } - - fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result { - self.apply_full(ccx, executor) - } -} - -impl dyn DynCheatcode { - pub(crate) fn name(&self) -> &'static str { - self.cheatcode().func.signature.split('(').next().unwrap() - } - - pub(crate) fn id(&self) -> &'static str { - self.cheatcode().func.id - } - - pub(crate) fn signature(&self) -> &'static str { - self.cheatcode().func.signature - } - - pub(crate) fn status(&self) -> &Status<'static> { - &self.cheatcode().status - } -} - -/// The cheatcode context, used in `Cheatcode`. -pub struct CheatsCtxt<'cheats, 'evm, 'db, 'db2> { +/// The cheatcode context. +pub struct CheatsCtxt<'a, CTX> { /// The cheatcodes inspector state. - pub(crate) state: &'cheats mut Cheatcodes, - /// The EVM data. - pub(crate) ecx: &'evm mut EthEvmContext<&'db mut (dyn DatabaseExt + 'db2)>, + pub(crate) state: &'a mut Cheatcodes, + /// The EVM context. + pub(crate) ecx: &'a mut CTX, /// The original `msg.sender`. pub(crate) caller: Address, /// Gas limit of the current cheatcode call. pub(crate) gas_limit: u64, } -impl<'db, 'db2> std::ops::Deref for CheatsCtxt<'_, '_, 'db, 'db2> { - type Target = EthEvmContext<&'db mut (dyn DatabaseExt + 'db2)>; +/// Placeholder context type for cheatcodes that don't need EVM context access +/// (i.e., they only use `apply`, not `apply_stateful` or `apply_full`). +#[cfg(test)] +pub(crate) type AnyCtx = (); + +impl std::ops::Deref for CheatsCtxt<'_, CTX> { + type Target = CTX; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -146,20 +121,20 @@ impl<'db, 'db2> std::ops::Deref for CheatsCtxt<'_, '_, 'db, 'db2> { } } -impl std::ops::DerefMut for CheatsCtxt<'_, '_, '_, '_> { +impl std::ops::DerefMut for CheatsCtxt<'_, CTX> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { - &mut *self.ecx + self.ecx } } -impl CheatsCtxt<'_, '_, '_, '_> { +impl CheatsCtxt<'_, CTX> { pub(crate) fn ensure_not_precompile(&self, address: &Address) -> Result<()> { if self.is_precompile(address) { Err(precompile_error(address)) } else { Ok(()) } } pub(crate) fn is_precompile(&self, address: &Address) -> bool { - self.ecx.journaled_state.warm_addresses.precompiles().contains(address) + self.ecx.journal().precompile_addresses().contains(address) } } diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 5375630e996bb..4ce408760f7a7 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -4,106 +4,130 @@ use crate::{Cheatcode, CheatsCtxt, Result, Vm::*, evm::journaled_account}; use alloy_consensus::{SidecarBuilder, SimpleCoder}; use alloy_primitives::{Address, B256, U256, Uint}; use alloy_rpc_types::Authorization; -use alloy_signer::SignerSync; +use alloy_signer::{Signer, SignerSync}; use alloy_signer_local::PrivateKeySigner; use alloy_sol_types::SolValue; +use foundry_evm_core::{backend::DatabaseExt, env::FoundryContextExt}; use foundry_wallets::{WalletSigner, wallet_multi::MultiWallet}; use parking_lot::Mutex; use revm::{ bytecode::Bytecode, - context::JournalTr, + context::{Cfg, ContextTr, JournalTr, Transaction}, context_interface::transaction::SignedAuthorization, + inspector::JournalExt, primitives::{KECCAK_EMPTY, hardfork::SpecId}, }; use std::sync::Arc; -impl Cheatcode for broadcast_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for broadcast_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; broadcast(ccx, None, true) } } -impl Cheatcode for broadcast_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for broadcast_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { signer } = self; broadcast(ccx, Some(signer), true) } } -impl Cheatcode for broadcast_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for broadcast_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { privateKey } = self; broadcast_key(ccx, privateKey, true) } } -impl Cheatcode for attachDelegation_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for attachDelegation_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { signedDelegation } = self; attach_delegation(ccx, signedDelegation, false) } } -impl Cheatcode for attachDelegation_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for attachDelegation_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { signedDelegation, crossChain } = self; attach_delegation(ccx, signedDelegation, *crossChain) } } -impl Cheatcode for signDelegation_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for signDelegation_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { implementation, privateKey } = *self; sign_delegation(ccx, privateKey, implementation, None, false, false) } } -impl Cheatcode for signDelegation_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for signDelegation_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { implementation, privateKey, nonce } = *self; sign_delegation(ccx, privateKey, implementation, Some(nonce), false, false) } } -impl Cheatcode for signDelegation_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for signDelegation_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { implementation, privateKey, crossChain } = *self; sign_delegation(ccx, privateKey, implementation, None, crossChain, false) } } -impl Cheatcode for signAndAttachDelegation_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for signAndAttachDelegation_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { implementation, privateKey } = *self; sign_delegation(ccx, privateKey, implementation, None, false, true) } } -impl Cheatcode for signAndAttachDelegation_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for signAndAttachDelegation_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { implementation, privateKey, nonce } = *self; sign_delegation(ccx, privateKey, implementation, Some(nonce), false, true) } } -impl Cheatcode for signAndAttachDelegation_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for signAndAttachDelegation_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { implementation, privateKey, crossChain } = *self; sign_delegation(ccx, privateKey, implementation, None, crossChain, true) } } /// Helper function to attach an EIP-7702 delegation. -fn attach_delegation( - ccx: &mut CheatsCtxt, +fn attach_delegation>( + ccx: &mut CheatsCtxt<'_, CTX>, delegation: &SignedDelegation, cross_chain: bool, ) -> Result { let SignedDelegation { v, r, s, nonce, implementation } = delegation; // Set chain id to 0 if universal deployment is preferred. // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md#protection-from-malleability-cross-chain - let chain_id = if cross_chain { U256::from(0) } else { U256::from(ccx.ecx.cfg.chain_id) }; + let chain_id = if cross_chain { U256::from(0) } else { U256::from(ccx.ecx.cfg().chain_id()) }; let auth = Authorization { address: *implementation, nonce: *nonce, chain_id }; let signed_auth = SignedAuthorization::new_unchecked( @@ -119,8 +143,8 @@ fn attach_delegation( /// Helper function to sign and attach (if needed) an EIP-7702 delegation. /// Uses the provided nonce, otherwise retrieves and increments the nonce of the EOA. -fn sign_delegation( - ccx: &mut CheatsCtxt, +fn sign_delegation>( + ccx: &mut CheatsCtxt<'_, CTX>, private_key: Uint<256, 4>, implementation: Address, nonce: Option, @@ -131,16 +155,19 @@ fn sign_delegation( let nonce = if let Some(nonce) = nonce { nonce } else { - let authority_acc = ccx.ecx.journaled_state.load_account(signer.address())?; + let account_nonce = { + let authority_acc = ccx.ecx.journal_mut().load_account(signer.address())?; + authority_acc.info.nonce + }; // Calculate next nonce considering existing active delegations next_delegation_nonce( &ccx.state.active_delegations, signer.address(), &ccx.state.broadcast, - authority_acc.data.info.nonce, + account_nonce, ) }; - let chain_id = if cross_chain { U256::from(0) } else { U256::from(ccx.ecx.cfg.chain_id) }; + let chain_id = if cross_chain { U256::from(0) } else { U256::from(ccx.ecx.cfg().chain_id()) }; let auth = Authorization { address: implementation, nonce, chain_id }; let sig = signer.sign_hash_sync(&auth.signature_hash())?; @@ -189,15 +216,21 @@ fn next_delegation_nonce( } } -fn write_delegation(ccx: &mut CheatsCtxt, auth: SignedAuthorization) -> Result<()> { +fn write_delegation>( + ccx: &mut CheatsCtxt<'_, CTX>, + auth: SignedAuthorization, +) -> Result<()> { let authority = auth.recover_authority().map_err(|e| format!("{e}"))?; - let authority_acc = ccx.ecx.journaled_state.load_account(authority)?; + let account_nonce = { + let authority_acc = ccx.ecx.journal_mut().load_account(authority)?; + authority_acc.info.nonce + }; let expected_nonce = next_delegation_nonce( &ccx.state.active_delegations, authority, &ccx.state.broadcast, - authority_acc.data.info.nonce, + account_nonce, ); if expected_nonce != auth.nonce { @@ -211,24 +244,26 @@ fn write_delegation(ccx: &mut CheatsCtxt, auth: SignedAuthorization) -> Result<( if auth.address.is_zero() { // Set empty code if the delegation address of authority is 0x. // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md#behavior. - ccx.ecx.journaled_state.set_code_with_hash(authority, Bytecode::default(), KECCAK_EMPTY); + ccx.ecx.journal_mut().set_code_with_hash(authority, Bytecode::default(), KECCAK_EMPTY); } else { let bytecode = Bytecode::new_eip7702(*auth.address()); - ccx.ecx.journaled_state.set_code(authority, bytecode); + ccx.ecx.journal_mut().set_code(authority, bytecode); } Ok(()) } -impl Cheatcode for attachBlobCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for attachBlobCall +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { blob } = self; ensure!( - ccx.ecx.cfg.spec >= SpecId::CANCUN, + ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, "`attachBlob` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); let sidecar: SidecarBuilder = SidecarBuilder::from_slice(blob); - let sidecar_variant = if ccx.ecx.cfg.spec < SpecId::OSAKA { + let sidecar_variant = if ccx.ecx.cfg().spec().into() < SpecId::OSAKA { sidecar.build_4844().map_err(|e| format!("{e}"))?.into() } else { sidecar.build_7594().map_err(|e| format!("{e}"))?.into() @@ -238,29 +273,35 @@ impl Cheatcode for attachBlobCall { } } -impl Cheatcode for startBroadcast_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startBroadcast_0Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; broadcast(ccx, None, false) } } -impl Cheatcode for startBroadcast_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startBroadcast_1Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { signer } = self; broadcast(ccx, Some(signer), false) } } -impl Cheatcode for startBroadcast_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode + for startBroadcast_2Call +{ + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { privateKey } = self; broadcast_key(ccx, privateKey, false) } } -impl Cheatcode for stopBroadcastCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for stopBroadcastCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; let Some(broadcast) = ccx.state.broadcast.take() else { bail!("no broadcast in progress to stop"); @@ -270,8 +311,8 @@ impl Cheatcode for stopBroadcastCall { } } -impl Cheatcode for getWalletsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for getWalletsCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let wallets = ccx.state.wallets().signers().unwrap_or_default(); Ok(wallets.abi_encode()) } @@ -331,17 +372,18 @@ impl Wallets { /// Locks inner Mutex and returns all signer addresses in the [MultiWallet]. pub fn signers(&self) -> Result> { - Ok(self.inner.lock().multi_wallet.signers()?.keys().copied().collect()) + let mut wallets = self.inner.lock(); + let (signers, browser) = wallets.multi_wallet.signers()?; + Ok(signers.keys().copied().chain(browser.map(|b| b.address())).collect()) } /// Number of signers in the [MultiWallet]. pub fn len(&self) -> usize { let mut inner = self.inner.lock(); - let signers = inner.multi_wallet.signers(); - if signers.is_err() { - return 0; + match inner.multi_wallet.signers() { + Ok((signers, browser)) => signers.len() + usize::from(browser.is_some()), + Err(_) => 0, } - signers.unwrap().len() } /// Whether the [MultiWallet] is empty. @@ -351,8 +393,12 @@ impl Wallets { } /// Sets up broadcasting from a script using `new_origin` as the sender. -fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bool) -> Result { - let depth = ccx.ecx.journaled_state.depth(); +fn broadcast>( + ccx: &mut CheatsCtxt<'_, CTX>, + new_origin: Option<&Address>, + single_call: bool, +) -> Result { + let depth = ccx.ecx.journal().depth(); ensure!( ccx.state.get_prank(depth).is_none(), "you have an active prank; broadcasting and pranks are not compatible" @@ -366,21 +412,21 @@ fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bo if let Some(provided_sender) = wallets.provided_sender { new_origin = Some(provided_sender); } else { - let signers = wallets.multi_wallet.signers()?; + let (signers, _) = wallets.multi_wallet.signers()?; if signers.len() == 1 { let address = signers.keys().next().unwrap(); new_origin = Some(*address); } } } - let new_origin = new_origin.unwrap_or(ccx.ecx.tx.caller); + let new_origin = new_origin.unwrap_or(ccx.ecx.tx().caller()); // Ensure new origin is loaded and touched. let _ = journaled_account(ccx.ecx, new_origin)?; let broadcast = Broadcast { new_origin, original_caller: ccx.caller, - original_origin: ccx.ecx.tx.caller, + original_origin: ccx.ecx.tx().caller(), depth, single_call, deploy_from_code: false, @@ -393,7 +439,11 @@ fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bo /// Sets up broadcasting from a script with the sender derived from `private_key`. /// Adds this private key to `state`'s `wallets` vector to later be used for signing /// if broadcast is successful. -fn broadcast_key(ccx: &mut CheatsCtxt, private_key: &U256, single_call: bool) -> Result { +fn broadcast_key>( + ccx: &mut CheatsCtxt<'_, CTX>, + private_key: &U256, + single_call: bool, +) -> Result { let wallet = super::crypto::parse_wallet(private_key)?; let new_origin = wallet.address(); diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index ca3c1a88b7fee..e96cb6ea071b1 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -6,7 +6,7 @@ use alloy_primitives::{U256, hex}; use alloy_sol_types::SolValue; // address -impl Cheatcode for toString_0Call { +impl Cheatcode for toString_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; Ok(value.to_string().abi_encode()) @@ -14,7 +14,7 @@ impl Cheatcode for toString_0Call { } // bytes -impl Cheatcode for toString_1Call { +impl Cheatcode for toString_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; Ok(value.to_string().abi_encode()) @@ -22,7 +22,7 @@ impl Cheatcode for toString_1Call { } // bytes32 -impl Cheatcode for toString_2Call { +impl Cheatcode for toString_2Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; Ok(value.to_string().abi_encode()) @@ -30,7 +30,7 @@ impl Cheatcode for toString_2Call { } // bool -impl Cheatcode for toString_3Call { +impl Cheatcode for toString_3Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; Ok(value.to_string().abi_encode()) @@ -38,7 +38,7 @@ impl Cheatcode for toString_3Call { } // uint256 -impl Cheatcode for toString_4Call { +impl Cheatcode for toString_4Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; Ok(value.to_string().abi_encode()) @@ -46,84 +46,84 @@ impl Cheatcode for toString_4Call { } // int256 -impl Cheatcode for toString_5Call { +impl Cheatcode for toString_5Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; Ok(value.to_string().abi_encode()) } } -impl Cheatcode for parseBytesCall { +impl Cheatcode for parseBytesCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { stringifiedValue } = self; parse(stringifiedValue, &DynSolType::Bytes) } } -impl Cheatcode for parseAddressCall { +impl Cheatcode for parseAddressCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { stringifiedValue } = self; parse(stringifiedValue, &DynSolType::Address) } } -impl Cheatcode for parseUintCall { +impl Cheatcode for parseUintCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { stringifiedValue } = self; parse(stringifiedValue, &DynSolType::Uint(256)) } } -impl Cheatcode for parseIntCall { +impl Cheatcode for parseIntCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { stringifiedValue } = self; parse(stringifiedValue, &DynSolType::Int(256)) } } -impl Cheatcode for parseBytes32Call { +impl Cheatcode for parseBytes32Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { stringifiedValue } = self; parse(stringifiedValue, &DynSolType::FixedBytes(32)) } } -impl Cheatcode for parseBoolCall { +impl Cheatcode for parseBoolCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { stringifiedValue } = self; parse(stringifiedValue, &DynSolType::Bool) } } -impl Cheatcode for toLowercaseCall { +impl Cheatcode for toLowercaseCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; Ok(input.to_lowercase().abi_encode()) } } -impl Cheatcode for toUppercaseCall { +impl Cheatcode for toUppercaseCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; Ok(input.to_uppercase().abi_encode()) } } -impl Cheatcode for trimCall { +impl Cheatcode for trimCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; Ok(input.trim().abi_encode()) } } -impl Cheatcode for replaceCall { +impl Cheatcode for replaceCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, from, to } = self; Ok(input.replace(from, to).abi_encode()) } } -impl Cheatcode for splitCall { +impl Cheatcode for splitCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, delimiter } = self; let parts: Vec<&str> = input.split(delimiter).collect(); @@ -131,14 +131,14 @@ impl Cheatcode for splitCall { } } -impl Cheatcode for indexOfCall { +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()) } } -impl Cheatcode for containsCall { +impl Cheatcode for containsCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { subject, search } = self; Ok(subject.contains(search).abi_encode()) @@ -193,10 +193,10 @@ fn parse_value_fallback(s: &str, ty: &DynSolType) -> Option { - if !s.starts_with("0x") && hex::check_raw(s) { - return Some(Err("missing hex prefix (\"0x\") for hex string")); - } + | DynSolType::Bytes + if !s.starts_with("0x") && hex::check_raw(s) => + { + return Some(Err("missing hex prefix (\"0x\") for hex string")); } _ => {} } diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 683c5131bcddf..cae52f829e446 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -6,6 +6,7 @@ use alloy_primitives::{Address, U256}; use alloy_sol_types::SolValue; use foundry_common::version::SEMVER_VERSION; use foundry_evm_core::constants::MAGIC_SKIP; +use revm::context::{ContextTr, JournalTr}; use std::str::FromStr; pub(crate) mod assert; @@ -13,28 +14,28 @@ pub(crate) mod assume; pub(crate) mod expect; pub(crate) mod revert_handlers; -impl Cheatcode for breakpoint_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for breakpoint_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { char } = self; breakpoint(ccx.state, &ccx.caller, char, true) } } -impl Cheatcode for breakpoint_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for breakpoint_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { char, value } = self; breakpoint(ccx.state, &ccx.caller, char, *value) } } -impl Cheatcode for getFoundryVersionCall { +impl Cheatcode for getFoundryVersionCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self {} = self; Ok(SEMVER_VERSION.abi_encode()) } } -impl Cheatcode for rpcUrlCall { +impl Cheatcode for rpcUrlCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { rpcAlias } = self; let url = state.config.rpc_endpoint(rpcAlias)?.url()?.abi_encode(); @@ -42,21 +43,21 @@ impl Cheatcode for rpcUrlCall { } } -impl Cheatcode for rpcUrlsCall { +impl Cheatcode for rpcUrlsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.config.rpc_urls().map(|urls| urls.abi_encode()) } } -impl Cheatcode for rpcUrlStructsCall { +impl Cheatcode for rpcUrlStructsCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.config.rpc_urls().map(|urls| urls.abi_encode()) } } -impl Cheatcode for sleepCall { +impl Cheatcode for sleepCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { duration } = self; let sleep_duration = std::time::Duration::from_millis(duration.saturating_to()); @@ -65,20 +66,20 @@ impl Cheatcode for sleepCall { } } -impl Cheatcode for skip_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for skip_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { skipTest } = *self; skip_1Call { skipTest, reason: String::new() }.apply_stateful(ccx) } } -impl Cheatcode for skip_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for skip_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { skipTest, reason } = self; 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.ecx.journaled_state.depth <= 1, "`skip` can only be used at test level"); + ensure!(ccx.ecx.journal().depth() <= 1, "`skip` can only be used at test level"); Err([MAGIC_SKIP, reason.as_bytes()].concat().into()) } else { Ok(Default::default()) @@ -86,14 +87,14 @@ impl Cheatcode for skip_1Call { } } -impl Cheatcode for getChain_0Call { +impl Cheatcode for getChain_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { chainAlias } = self; get_chain(state, chainAlias) } } -impl Cheatcode for getChain_1Call { +impl Cheatcode for getChain_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { chainId } = self; // Convert the chainId to a string and use the existing get_chain function diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index e1483644b3f40..f74737674832f 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -2,11 +2,11 @@ use crate::{CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{I256, U256, U512}; use foundry_evm_core::{ abi::console::{format_units_int, format_units_uint}, - backend::GLOBAL_FAIL_SLOT, + backend::{DatabaseExt, GLOBAL_FAIL_SLOT}, constants::CHEATCODE_ADDRESS, }; use itertools::Itertools; -use revm::context::JournalTr; +use revm::context::{ContextTr, JournalTr}; use std::{borrow::Cow, fmt}; const EQ_REL_DELTA_RESOLUTION: U256 = U256::from_limbs([18, 0, 0, 0]); @@ -187,8 +187,8 @@ impl EqRelAssertionError { type ComparisonResult<'a, T> = Result<(), ComparisonAssertionError<'a, T>>; #[cold] -fn handle_assertion_result( - ccx: &mut CheatsCtxt, +fn handle_assertion_result, E>( + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, err: E, error_formatter: Option<&dyn Fn(&E) -> String>, @@ -203,16 +203,16 @@ fn handle_assertion_result( handle_assertion_result_mono(ccx, executor, msg) } -fn handle_assertion_result_mono( - ccx: &mut CheatsCtxt, +fn handle_assertion_result_mono>( + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, msg: Cow<'_, str>, ) -> Result { if ccx.state.config.assertions_revert { Err(msg.into_owned().into()) } else { - executor.console_log(ccx, &msg); - ccx.ecx.journaled_state.sstore(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT, U256::from(1))?; + executor.console_log(ccx.state, &msg); + ccx.ecx.journal_mut().sstore(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT, U256::from(1))?; Ok(Default::default()) } } @@ -245,10 +245,10 @@ macro_rules! impl_assertions { }; (@impl $no_error:ident, $with_error:ident, ($($arg:ident),*), $body:expr, $error_formatter:expr) => { - impl crate::Cheatcode for $no_error { + impl> crate::Cheatcode for $no_error { fn apply_full( &self, - ccx: &mut CheatsCtxt, + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, ) -> Result { let Self { $($arg),* } = self; @@ -259,10 +259,10 @@ macro_rules! impl_assertions { } } - impl crate::Cheatcode for $with_error { + impl> crate::Cheatcode for $with_error { fn apply_full( &self, - ccx: &mut CheatsCtxt, + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, ) -> Result { let Self { $($arg,)* error } = self; diff --git a/crates/cheatcodes/src/test/assume.rs b/crates/cheatcodes/src/test/assume.rs index 98126bcee7621..1febc9a66fd03 100644 --- a/crates/cheatcodes/src/test/assume.rs +++ b/crates/cheatcodes/src/test/assume.rs @@ -1,6 +1,7 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result}; use alloy_primitives::Address; use foundry_evm_core::constants::MAGIC_ASSUME; +use revm::context::{ContextTr, JournalTr}; use spec::Vm::{ PotentialRevert, assumeCall, assumeNoRevert_0Call, assumeNoRevert_1Call, assumeNoRevert_2Call, }; @@ -43,36 +44,36 @@ impl AcceptableRevertParameters { } } -impl Cheatcode for assumeCall { +impl Cheatcode for assumeCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { condition } = self; if *condition { Ok(Default::default()) } else { Err(Error::from(MAGIC_ASSUME)) } } } -impl Cheatcode for assumeNoRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - assume_no_revert(ccx.state, ccx.ecx.journaled_state.depth, vec![]) +impl Cheatcode for assumeNoRevert_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + assume_no_revert(ccx.state, ccx.ecx.journal().depth(), vec![]) } } -impl Cheatcode for assumeNoRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for assumeNoRevert_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { potentialRevert } = self; assume_no_revert( ccx.state, - ccx.ecx.journaled_state.depth, + ccx.ecx.journal().depth(), vec![AcceptableRevertParameters::from(potentialRevert)], ) } } -impl Cheatcode for assumeNoRevert_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for assumeNoRevert_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { potentialReverts } = self; assume_no_revert( ccx.state, - ccx.ecx.journaled_state.depth, + ccx.ecx.journal().depth(), potentialReverts.iter().map(AcceptableRevertParameters::from).collect(), ) } diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index bdbfc583b5d04..a12979765d8f3 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -13,7 +13,7 @@ use alloy_primitives::{ use foundry_common::{abi::get_indexed_event, fmt::format_token}; use foundry_evm_traces::DecodedCallLog; use revm::{ - context::JournalTr, + context::{ContextTr, JournalTr}, interpreter::{ InstructionResult, Interpreter, InterpreterAction, interpreter_types::LoopControl, }, @@ -162,28 +162,28 @@ impl CreateScheme { } } -impl Cheatcode for expectCall_0Call { +impl Cheatcode for expectCall_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, data } = self; expect_call(state, callee, data, None, None, None, 1, ExpectedCallType::NonCount) } } -impl Cheatcode for expectCall_1Call { +impl Cheatcode for expectCall_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, data, count } = self; expect_call(state, callee, data, None, None, None, *count, ExpectedCallType::Count) } } -impl Cheatcode for expectCall_2Call { +impl Cheatcode for expectCall_2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, msgValue, data } = self; expect_call(state, callee, data, Some(msgValue), None, None, 1, ExpectedCallType::NonCount) } } -impl Cheatcode for expectCall_3Call { +impl Cheatcode for expectCall_3Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, msgValue, data, count } = self; expect_call( @@ -199,7 +199,7 @@ impl Cheatcode for expectCall_3Call { } } -impl Cheatcode for expectCall_4Call { +impl Cheatcode for expectCall_4Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, msgValue, gas, data } = self; expect_call( @@ -215,7 +215,7 @@ impl Cheatcode for expectCall_4Call { } } -impl Cheatcode for expectCall_5Call { +impl Cheatcode for expectCall_5Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, msgValue, gas, data, count } = self; expect_call( @@ -231,7 +231,7 @@ impl Cheatcode for expectCall_5Call { } } -impl Cheatcode for expectCallMinGas_0Call { +impl Cheatcode for expectCallMinGas_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, msgValue, minGas, data } = self; expect_call( @@ -247,7 +247,7 @@ impl Cheatcode for expectCallMinGas_0Call { } } -impl Cheatcode for expectCallMinGas_1Call { +impl Cheatcode for expectCallMinGas_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { callee, msgValue, minGas, data, count } = self; expect_call( @@ -263,12 +263,12 @@ impl Cheatcode for expectCallMinGas_1Call { } } -impl Cheatcode for expectEmit_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self; expect_emit( ccx.state, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), [true, checkTopic1, checkTopic2, checkTopic3, checkData], None, false, @@ -277,12 +277,12 @@ impl Cheatcode for expectEmit_0Call { } } -impl Cheatcode for expectEmit_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self; expect_emit( ccx.state, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), [true, checkTopic1, checkTopic2, checkTopic3, checkData], Some(emitter), false, @@ -291,26 +291,26 @@ impl Cheatcode for expectEmit_1Call { } } -impl Cheatcode for expectEmit_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, 1) + expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, 1) } } -impl Cheatcode for expectEmit_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { emitter } = *self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false, 1) + expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, 1) } } -impl Cheatcode for expectEmit_4Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_4Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self; expect_emit( ccx.state, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), [true, checkTopic1, checkTopic2, checkTopic3, checkData], None, false, @@ -319,12 +319,12 @@ impl Cheatcode for expectEmit_4Call { } } -impl Cheatcode for expectEmit_5Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_5Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self; expect_emit( ccx.state, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), [true, checkTopic1, checkTopic2, checkTopic3, checkData], Some(emitter), false, @@ -333,33 +333,26 @@ impl Cheatcode for expectEmit_5Call { } } -impl Cheatcode for expectEmit_6Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_6Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { count } = *self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, count) + expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, false, count) } } -impl Cheatcode for expectEmit_7Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmit_7Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { emitter, count } = *self; - expect_emit( - ccx.state, - ccx.ecx.journaled_state.depth(), - [true; 5], - Some(emitter), - false, - count, - ) + expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), false, count) } } -impl Cheatcode for expectEmitAnonymous_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmitAnonymous_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self; expect_emit( ccx.state, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData], None, true, @@ -368,12 +361,12 @@ impl Cheatcode for expectEmitAnonymous_0Call { } } -impl Cheatcode for expectEmitAnonymous_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmitAnonymous_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self; expect_emit( ccx.state, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData], Some(emitter), true, @@ -382,48 +375,48 @@ impl Cheatcode for expectEmitAnonymous_1Call { } } -impl Cheatcode for expectEmitAnonymous_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmitAnonymous_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true, 1) + expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], None, true, 1) } } -impl Cheatcode for expectEmitAnonymous_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectEmitAnonymous_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { emitter } = *self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true, 1) + expect_emit(ccx.state, ccx.ecx.journal().depth(), [true; 5], Some(emitter), true, 1) } } -impl Cheatcode for expectCreateCall { +impl Cheatcode for expectCreateCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bytecode, deployer } = self; expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create) } } -impl Cheatcode for expectCreate2Call { +impl Cheatcode for expectCreate2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bytecode, deployer } = self; expect_create(state, bytecode.clone(), *deployer, CreateScheme::Create2) } } -impl Cheatcode for expectRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, 1) + expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, 1) } } -impl Cheatcode for expectRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, None, @@ -432,43 +425,27 @@ impl Cheatcode for expectRevert_1Call { } } -impl Cheatcode for expectRevert_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData } = self; - expect_revert( - ccx.state, - Some(revertData), - ccx.ecx.journaled_state.depth(), - false, - false, - None, - 1, - ) + expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), false, false, None, 1) } } -impl Cheatcode for expectRevert_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_3Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { reverter } = self; - expect_revert( - ccx.state, - None, - ccx.ecx.journaled_state.depth(), - false, - false, - Some(*reverter), - 1, - ) + expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, Some(*reverter), 1) } } -impl Cheatcode for expectRevert_4Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_4Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, reverter } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, Some(*reverter), @@ -477,13 +454,13 @@ impl Cheatcode for expectRevert_4Call { } } -impl Cheatcode for expectRevert_5Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_5Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, reverter } = self; expect_revert( ccx.state, Some(revertData), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, Some(*reverter), @@ -492,20 +469,20 @@ impl Cheatcode for expectRevert_5Call { } } -impl Cheatcode for expectRevert_6Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_6Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { count } = self; - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None, *count) + expect_revert(ccx.state, None, ccx.ecx.journal().depth(), false, false, None, *count) } } -impl Cheatcode for expectRevert_7Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_7Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, count } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, None, @@ -514,13 +491,13 @@ impl Cheatcode for expectRevert_7Call { } } -impl Cheatcode for expectRevert_8Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_8Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, count } = self; expect_revert( ccx.state, Some(revertData), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, None, @@ -529,13 +506,13 @@ impl Cheatcode for expectRevert_8Call { } } -impl Cheatcode for expectRevert_9Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_9Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { reverter, count } = self; expect_revert( ccx.state, None, - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, Some(*reverter), @@ -544,13 +521,13 @@ impl Cheatcode for expectRevert_9Call { } } -impl Cheatcode for expectRevert_10Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_10Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, reverter, count } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, Some(*reverter), @@ -559,13 +536,13 @@ impl Cheatcode for expectRevert_10Call { } } -impl Cheatcode for expectRevert_11Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectRevert_11Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, reverter, count } = self; expect_revert( ccx.state, Some(revertData), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, false, Some(*reverter), @@ -574,13 +551,13 @@ impl Cheatcode for expectRevert_11Call { } } -impl Cheatcode for expectPartialRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectPartialRevert_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, true, None, @@ -589,13 +566,13 @@ impl Cheatcode for expectPartialRevert_0Call { } } -impl Cheatcode for expectPartialRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectPartialRevert_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData, reverter } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), false, true, Some(*reverter), @@ -604,19 +581,19 @@ impl Cheatcode for expectPartialRevert_1Call { } } -impl Cheatcode for _expectCheatcodeRevert_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { - expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None, 1) +impl Cheatcode for _expectCheatcodeRevert_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + expect_revert(ccx.state, None, ccx.ecx.journal().depth(), true, false, None, 1) } } -impl Cheatcode for _expectCheatcodeRevert_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for _expectCheatcodeRevert_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData } = self; expect_revert( ccx.state, Some(revertData.as_ref()), - ccx.ecx.journaled_state.depth(), + ccx.ecx.journal().depth(), true, false, None, @@ -625,40 +602,32 @@ impl Cheatcode for _expectCheatcodeRevert_1Call { } } -impl Cheatcode for _expectCheatcodeRevert_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for _expectCheatcodeRevert_2Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { revertData } = self; - expect_revert( - ccx.state, - Some(revertData), - ccx.ecx.journaled_state.depth(), - true, - false, - None, - 1, - ) + expect_revert(ccx.state, Some(revertData), ccx.ecx.journal().depth(), true, false, None, 1) } } -impl Cheatcode for expectSafeMemoryCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectSafeMemoryCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { min, max } = *self; - expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth().try_into()?) + expect_safe_memory(ccx.state, min, max, ccx.ecx.journal().depth().try_into()?) } } -impl Cheatcode for stopExpectSafeMemoryCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for stopExpectSafeMemoryCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; - ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth().try_into()?); + ccx.state.allowed_mem_writes.remove(&ccx.ecx.journal().depth().try_into()?); Ok(Default::default()) } } -impl Cheatcode for expectSafeMemoryCallCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for expectSafeMemoryCallCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { min, max } = *self; - expect_safe_memory(ccx.state, min, max, (ccx.ecx.journaled_state.depth() + 1).try_into()?) + expect_safe_memory(ccx.state, min, max, (ccx.ecx.journal().depth() + 1).try_into()?) } } @@ -932,10 +901,7 @@ pub(crate) fn handle_expect_emit( } // Maybe match source address. - if event_to_fill_or_check - .address - .is_some_and(|addr| addr.to_checksum(None) != log.address.to_checksum(None)) - { + if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) { event_to_fill_or_check.mismatch_error = Some(format!( "log emitter mismatch: expected={:#x}, got={:#x}", event_to_fill_or_check.address.unwrap(), diff --git a/crates/cheatcodes/src/toml.rs b/crates/cheatcodes/src/toml.rs index 7fb8210c88d66..53c8911287185 100644 --- a/crates/cheatcodes/src/toml.rs +++ b/crates/cheatcodes/src/toml.rs @@ -15,14 +15,14 @@ use foundry_config::fs_permissions::FsAccessKind; use serde_json::Value as JsonValue; use toml::Value as TomlValue; -impl Cheatcode for keyExistsTomlCall { +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 { +impl Cheatcode for parseToml_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { toml } = self; parse_toml( @@ -33,7 +33,7 @@ impl Cheatcode for parseToml_0Call { } } -impl Cheatcode for parseToml_1Call { +impl Cheatcode for parseToml_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml( @@ -44,105 +44,105 @@ impl Cheatcode for parseToml_1Call { } } -impl Cheatcode for parseTomlUintCall { +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 { +impl Cheatcode for parseTomlUintArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Uint(256)))) } } -impl Cheatcode for parseTomlIntCall { +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 { +impl Cheatcode for parseTomlIntArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Int(256)))) } } -impl Cheatcode for parseTomlBoolCall { +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 { +impl Cheatcode for parseTomlBoolArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Bool))) } } -impl Cheatcode for parseTomlAddressCall { +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 { +impl Cheatcode for parseTomlAddressArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Address))) } } -impl Cheatcode for parseTomlStringCall { +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 { +impl Cheatcode for parseTomlStringArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::String))) } } -impl Cheatcode for parseTomlBytesCall { +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 { +impl Cheatcode for parseTomlBytesArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::Bytes))) } } -impl Cheatcode for parseTomlBytes32Call { +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 { +impl Cheatcode for parseTomlBytes32ArrayCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { toml, key } = self; parse_toml_coerce(toml, key, &DynSolType::Array(Box::new(DynSolType::FixedBytes(32)))) } } -impl Cheatcode for parseTomlType_0Call { +impl Cheatcode for parseTomlType_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { toml, typeDescription } = self; parse_toml_coerce( @@ -157,7 +157,7 @@ impl Cheatcode for parseTomlType_0Call { } } -impl Cheatcode for parseTomlType_1Call { +impl Cheatcode for parseTomlType_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { toml, key, typeDescription } = self; parse_toml_coerce( @@ -172,7 +172,7 @@ impl Cheatcode for parseTomlType_1Call { } } -impl Cheatcode for parseTomlTypeArrayCall { +impl Cheatcode for parseTomlTypeArrayCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { toml, key, typeDescription } = self; let ty = resolve_type( @@ -183,14 +183,14 @@ impl Cheatcode for parseTomlTypeArrayCall { } } -impl Cheatcode for parseTomlKeysCall { +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 { +impl Cheatcode for writeToml_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, path } = self; let value = @@ -201,7 +201,7 @@ impl Cheatcode for writeToml_0Call { } } -impl Cheatcode for writeToml_1Call { +impl Cheatcode for writeToml_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json: value, path, valueKey } = self; diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 2c59b58b1a083..1b19833102ed5 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -12,7 +12,10 @@ use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; use foundry_evm_fuzz::strategies::BoundMutator; use proptest::prelude::Strategy; use rand::{Rng, RngCore, seq::SliceRandom}; -use revm::context::JournalTr; +use revm::{ + context::{ContextTr, JournalTr}, + inspector::JournalExt, +}; use std::path::PathBuf; /// Contains locations of traces ignored via cheatcodes. @@ -29,7 +32,7 @@ pub struct IgnoredTraces { pub last_pause_call: Option<(usize, usize)>, } -impl Cheatcode for labelCall { +impl Cheatcode for labelCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { account, newLabel } = self; state.labels.insert(*account, newLabel.clone()); @@ -37,7 +40,7 @@ impl Cheatcode for labelCall { } } -impl Cheatcode for getLabelCall { +impl Cheatcode for getLabelCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { account } = self; Ok(match state.labels.get(account) { @@ -47,7 +50,7 @@ impl Cheatcode for getLabelCall { } } -impl Cheatcode for computeCreateAddressCall { +impl Cheatcode for computeCreateAddressCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { nonce, deployer } = self; ensure!(*nonce <= U256::from(u64::MAX), "nonce must be less than 2^64"); @@ -55,28 +58,28 @@ impl Cheatcode for computeCreateAddressCall { } } -impl Cheatcode for computeCreate2Address_0Call { +impl Cheatcode for computeCreate2Address_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { salt, initCodeHash, deployer } = self; Ok(deployer.create2(salt, initCodeHash).abi_encode()) } } -impl Cheatcode for computeCreate2Address_1Call { +impl Cheatcode for computeCreate2Address_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { salt, initCodeHash } = self; Ok(DEFAULT_CREATE2_DEPLOYER.create2(salt, initCodeHash).abi_encode()) } } -impl Cheatcode for ensNamehashCall { +impl Cheatcode for ensNamehashCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; Ok(namehash(name).abi_encode()) } } -impl Cheatcode for bound_0Call { +impl Cheatcode for bound_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { current, min, max } = *self; let Some(mutated) = U256::bound(current, min, max, state.test_runner()) else { @@ -86,7 +89,7 @@ impl Cheatcode for bound_0Call { } } -impl Cheatcode for bound_1Call { +impl Cheatcode for bound_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { current, min, max } = *self; let Some(mutated) = I256::bound(current, min, max, state.test_runner()) else { @@ -96,27 +99,27 @@ impl Cheatcode for bound_1Call { } } -impl Cheatcode for randomUint_0Call { +impl Cheatcode for randomUint_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { random_uint(state, None, None) } } -impl Cheatcode for randomUint_1Call { +impl Cheatcode for randomUint_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { min, max } = *self; random_uint(state, None, Some((min, max))) } } -impl Cheatcode for randomUint_2Call { +impl Cheatcode for randomUint_2Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bits } = *self; random_uint(state, Some(bits), None) } } -impl Cheatcode for randomAddressCall { +impl Cheatcode for randomAddressCall { fn apply(&self, state: &mut Cheatcodes) -> Result { Ok(DynSolValue::type_strategy(&DynSolType::Address) .new_tree(state.test_runner()) @@ -126,27 +129,27 @@ impl Cheatcode for randomAddressCall { } } -impl Cheatcode for randomInt_0Call { +impl Cheatcode for randomInt_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { random_int(state, None) } } -impl Cheatcode for randomInt_1Call { +impl Cheatcode for randomInt_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bits } = *self; random_int(state, Some(bits)) } } -impl Cheatcode for randomBoolCall { +impl Cheatcode for randomBoolCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let rand_bool: bool = state.rng().random(); Ok(rand_bool.abi_encode()) } } -impl Cheatcode for randomBytesCall { +impl Cheatcode for randomBytesCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { len } = *self; ensure!( @@ -159,24 +162,24 @@ impl Cheatcode for randomBytesCall { } } -impl Cheatcode for randomBytes4Call { +impl Cheatcode for randomBytes4Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let rand_u32 = state.rng().next_u32(); Ok(B32::from(rand_u32).abi_encode()) } } -impl Cheatcode for randomBytes8Call { +impl Cheatcode for randomBytes8Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let rand_u64 = state.rng().next_u64(); Ok(B64::from(rand_u64).abi_encode()) } } -impl Cheatcode for pauseTracingCall { +impl Cheatcode for pauseTracingCall { fn apply_full( &self, - ccx: &mut crate::CheatsCtxt, + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, ) -> Result { let Some(tracer) = executor.tracing_inspector() else { @@ -196,10 +199,10 @@ impl Cheatcode for pauseTracingCall { } } -impl Cheatcode for resumeTracingCall { +impl Cheatcode for resumeTracingCall { fn apply_full( &self, - ccx: &mut crate::CheatsCtxt, + ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, ) -> Result { let Some(tracer) = executor.tracing_inspector() else { @@ -219,7 +222,7 @@ impl Cheatcode for resumeTracingCall { } } -impl Cheatcode for interceptInitcodeCall { +impl Cheatcode for interceptInitcodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; if !state.intercept_next_create_call { @@ -231,8 +234,8 @@ impl Cheatcode for interceptInitcodeCall { } } -impl Cheatcode for setArbitraryStorage_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for setArbitraryStorage_0Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target } = self; ccx.state.arbitrary_storage().mark_arbitrary(target, false); @@ -240,8 +243,8 @@ impl Cheatcode for setArbitraryStorage_0Call { } } -impl Cheatcode for setArbitraryStorage_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for setArbitraryStorage_1Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, overwrite } = self; ccx.state.arbitrary_storage().mark_arbitrary(target, *overwrite); @@ -249,8 +252,8 @@ impl Cheatcode for setArbitraryStorage_1Call { } } -impl Cheatcode for copyStorageCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl> Cheatcode for copyStorageCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { from, to } = self; ensure!( @@ -258,11 +261,11 @@ impl Cheatcode for copyStorageCall { "target address cannot have arbitrary storage" ); - if let Ok(from_account) = ccx.ecx.journaled_state.load_account(*from) { + if let Ok(from_account) = ccx.ecx.journal_mut().load_account(*from) { let from_storage = from_account.storage.clone(); - if ccx.ecx.journaled_state.load_account(*to).is_ok() { + if ccx.ecx.journal_mut().load_account(*to).is_ok() { // SAFETY: We ensured the account was already loaded. - ccx.ecx.journaled_state.state.get_mut(to).unwrap().storage = from_storage; + ccx.ecx.journal_mut().evm_state_mut().get_mut(to).unwrap().storage = from_storage; if let Some(arbitrary_storage) = &mut ccx.state.arbitrary_storage { arbitrary_storage.mark_copy(from, to); } @@ -273,7 +276,7 @@ impl Cheatcode for copyStorageCall { } } -impl Cheatcode for sortCall { +impl Cheatcode for sortCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { array } = self; @@ -284,7 +287,7 @@ impl Cheatcode for sortCall { } } -impl Cheatcode for shuffleCall { +impl Cheatcode for shuffleCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { array } = self; @@ -296,8 +299,8 @@ impl Cheatcode for shuffleCall { } } -impl Cheatcode for setSeedCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { +impl Cheatcode for setSeedCall { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { seed } = self; ccx.state.set_seed(*seed); Ok(Default::default()) @@ -349,7 +352,7 @@ fn random_int(state: &mut Cheatcodes, bits: Option) -> Result { .abi_encode()) } -impl Cheatcode for eip712HashType_0Call { +impl Cheatcode for eip712HashType_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { typeNameOrDefinition } = self; @@ -359,7 +362,7 @@ impl Cheatcode for eip712HashType_0Call { } } -impl Cheatcode for eip712HashType_1Call { +impl Cheatcode for eip712HashType_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bindingsPath, typeName } = self; @@ -370,7 +373,7 @@ impl Cheatcode for eip712HashType_1Call { } } -impl Cheatcode for eip712HashStruct_0Call { +impl Cheatcode for eip712HashStruct_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { typeNameOrDefinition, abiEncodedData } = self; @@ -381,7 +384,7 @@ impl Cheatcode for eip712HashStruct_0Call { } } -impl Cheatcode for eip712HashStruct_1Call { +impl Cheatcode for eip712HashStruct_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bindingsPath, typeName, abiEncodedData } = self; @@ -392,7 +395,7 @@ impl Cheatcode for eip712HashStruct_1Call { } } -impl Cheatcode for eip712HashTypedDataCall { +impl Cheatcode for eip712HashTypedDataCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { jsonData } = self; let typed_data: TypedData = serde_json::from_str(jsonData)?; @@ -494,7 +497,7 @@ fn get_struct_hash(primary: &str, type_def: &String, abi_encoded_data: &Bytes) - Ok(keccak256(&bytes_to_hash).to_vec()) } -impl Cheatcode for toRlpCall { +impl Cheatcode for toRlpCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { data } = self; @@ -505,7 +508,7 @@ impl Cheatcode for toRlpCall { } } -impl Cheatcode for fromRlpCall { +impl Cheatcode for fromRlpCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { rlp } = self; diff --git a/crates/cheatcodes/src/version.rs b/crates/cheatcodes/src/version.rs index 84eace8f0017e..07652b3395f19 100644 --- a/crates/cheatcodes/src/version.rs +++ b/crates/cheatcodes/src/version.rs @@ -4,14 +4,14 @@ use foundry_common::version::SEMVER_VERSION; use semver::Version; use std::cmp::Ordering; -impl Cheatcode for foundryVersionCmpCall { +impl Cheatcode for foundryVersionCmpCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { version } = self; foundry_version_cmp(version).map(|cmp| (cmp as i8).abi_encode()) } } -impl Cheatcode for foundryVersionAtLeastCall { +impl Cheatcode for foundryVersionAtLeastCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { version } = self; foundry_version_cmp(version).map(|cmp| cmp.is_ge().abi_encode()) diff --git a/crates/chisel/src/args.rs b/crates/chisel/src/args.rs index e3af6d5c91203..8200f4fc44f63 100644 --- a/crates/chisel/src/args.rs +++ b/crates/chisel/src/args.rs @@ -14,6 +14,8 @@ use yansi::Paint; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let args = Chisel::parse(); args.global.init()?; args.global.tokio_runtime().block_on(run_command(args)) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index abb6be8693585..6ea9859643ed4 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -213,15 +213,19 @@ impl SessionSource { let executor = ExecutorBuilder::new() .inspectors(|stack| { - stack.chisel_state(final_pc).trace_mode(TraceMode::Call).cheatcodes( - CheatsConfig::new( - &self.config.foundry_config, - self.config.evm_opts.clone(), - None, - None, + stack + .logs(self.config.foundry_config.live_logs) + .chisel_state(final_pc) + .trace_mode(TraceMode::Call) + .cheatcodes( + CheatsConfig::new( + &self.config.foundry_config, + self.config.evm_opts.clone(), + None, + None, + ) + .into(), ) - .into(), - ) }) .gas_limit(self.config.evm_opts.gas_limit()) .spec_id(self.config.foundry_config.evm_spec_id()) @@ -521,16 +525,10 @@ impl Type { pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)), pt::Expression::HexNumberLiteral(_, s, _) => { match s.parse::
() { - Ok(addr) => { - if *s == addr.to_checksum(None) { - Some(Self::Builtin(DynSolType::Address)) - } else { - Some(Self::Builtin(DynSolType::Uint(256))) - } - }, - _ => { - Some(Self::Builtin(DynSolType::Uint(256))) + Ok(addr) if *s == addr.to_checksum(None) => { + Some(Self::Builtin(DynSolType::Address)) } + _ => Some(Self::Builtin(DynSolType::Uint(256))), } } @@ -667,6 +665,7 @@ impl Type { // Type members, like array, bytes etc #[expect(clippy::single_match)] + #[allow(clippy::collapsible_match)] match &self { Self::Access(inner, access) => { if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) { diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index a23a927261172..1002b5533f116 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -169,16 +169,12 @@ impl SolidityHelper { } }, - Literal { kind: Str { terminated, .. } } => { - if !terminated { - return ValidationResult::Incomplete; - } + Literal { kind: Str { terminated, .. } } if !terminated => { + return ValidationResult::Incomplete; } - BlockComment { terminated, .. } => { - if !terminated { - return ValidationResult::Incomplete; - } + BlockComment { terminated, .. } if !terminated => { + return ValidationResult::Incomplete; } _ => {} diff --git a/crates/chisel/tests/it/repl/mod.rs b/crates/chisel/tests/it/repl/mod.rs index 6fb38b6ca1805..aa9c00f471aa7 100644 --- a/crates/chisel/tests/it/repl/mod.rs +++ b/crates/chisel/tests/it/repl/mod.rs @@ -264,3 +264,13 @@ repl_test!(uninitialized_variables, |repl| { repl.sendln("y"); repl.expect("Data: 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF"); }); + +repl_test!(chisel_can_run_with_live_logs_flag, "--live-logs", init = true, |repl| { + repl.sendln("import {console} from 'forge-std/Script.sol';"); + repl.sendln("console.log('Hello, World!');"); + repl.expect("Hello, World!"); + + repl.sendln("console.log('Goodbye, World!');"); + repl.expect("Hello, World!"); // old log is also printed + repl.expect("Goodbye, World!"); +}); diff --git a/crates/cli-markdown/Cargo.toml b/crates/cli-markdown/Cargo.toml new file mode 100644 index 0000000000000..7f039685d2cf1 --- /dev/null +++ b/crates/cli-markdown/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "foundry-cli-markdown" +description = "Generate Markdown documentation for clap CLIs" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +clap = { version = "4", features = ["env"] } + +[dev-dependencies] +clap = { version = "4", features = ["derive"] } +pretty_assertions = "1" diff --git a/crates/cli-markdown/src/lib.rs b/crates/cli-markdown/src/lib.rs new file mode 100644 index 0000000000000..732e22bfd193d --- /dev/null +++ b/crates/cli-markdown/src/lib.rs @@ -0,0 +1,582 @@ +//! Generate Markdown documentation for clap command-line tools. +//! +//! This is a fork of [`clap-markdown`](https://crates.io/crates/clap-markdown) with the following +//! enhancements: +//! - Support for grouped options by help heading ([PR #48](https://github.com/ConnorGray/clap-markdown/pull/48)) +//! - Show environment variable names for arguments ([PR #50](https://github.com/ConnorGray/clap-markdown/pull/50)) +//! - Add version information to generated Markdown ([PR #52](https://github.com/ConnorGray/clap-markdown/pull/52)) + +use std::{ + collections::BTreeMap, + fmt::{self, Write}, +}; + +use clap::builder::PossibleValue; + +/// Options to customize the structure of the output Markdown document. +#[non_exhaustive] +pub struct MarkdownOptions { + title: Option, + show_footer: bool, + show_table_of_contents: bool, + show_aliases: bool, +} + +impl MarkdownOptions { + /// Construct a default instance of `MarkdownOptions`. + pub fn new() -> Self { + Self { title: None, show_footer: true, show_table_of_contents: true, show_aliases: true } + } + + /// Set a custom title to use in the generated document. + pub fn title(mut self, title: String) -> Self { + self.title = Some(title); + self + } + + /// Whether to show the default footer advertising `clap-markdown`. + pub fn show_footer(mut self, show: bool) -> Self { + self.show_footer = show; + self + } + + /// Whether to show the default table of contents. + pub fn show_table_of_contents(mut self, show: bool) -> Self { + self.show_table_of_contents = show; + self + } + + /// Whether to show aliases for arguments and commands. + pub fn show_aliases(mut self, show: bool) -> Self { + self.show_aliases = show; + self + } +} + +impl Default for MarkdownOptions { + fn default() -> Self { + Self::new() + } +} + +/// Format the help information for `command` as Markdown. +pub fn help_markdown() -> String { + let command = C::command(); + help_markdown_command(&command) +} + +/// Format the help information for `command` as Markdown, with custom options. +pub fn help_markdown_custom(options: &MarkdownOptions) -> String { + let command = C::command(); + help_markdown_command_custom(&command, options) +} + +/// Format the help information for `command` as Markdown. +pub fn help_markdown_command(command: &clap::Command) -> String { + help_markdown_command_custom(command, &Default::default()) +} + +/// Format the help information for `command` as Markdown, with custom options. +pub fn help_markdown_command_custom(command: &clap::Command, options: &MarkdownOptions) -> String { + let mut buffer = String::with_capacity(100); + write_help_markdown(&mut buffer, command, options); + buffer +} + +/// Format the help information for `command` as Markdown and print it. +/// +/// Output is printed to the standard output. +#[allow(clippy::disallowed_macros)] +pub fn print_help_markdown() { + let command = C::command(); + let mut buffer = String::with_capacity(100); + write_help_markdown(&mut buffer, &command, &Default::default()); + println!("{buffer}"); +} + +fn write_help_markdown(buffer: &mut String, command: &clap::Command, options: &MarkdownOptions) { + let title_name = get_canonical_name(command); + + let title = match options.title { + Some(ref title) => title.to_owned(), + None => format!("Command-Line Help for `{title_name}`"), + }; + writeln!(buffer, "# {title}\n",).unwrap(); + + writeln!( + buffer, + "This document contains the help content for the `{title_name}` command-line program.\n", + ) + .unwrap(); + + // Write the version if available (PR #52) + if let Some(version) = command.get_version() { + let version_str = version.to_string(); + + if version_str.contains('\n') { + // Multi-line version: use a code block + writeln!(buffer, "**Version:**\n\n```\n{}\n```\n", version_str.trim()).unwrap(); + } else { + // Single-line version: use inline code + writeln!(buffer, "**Version:** `{version_str}`\n").unwrap(); + } + } + + // Write the table of contents + if options.show_table_of_contents { + writeln!(buffer, "**Command Overview:**\n").unwrap(); + build_table_of_contents_markdown(buffer, Vec::new(), command, 0).unwrap(); + writeln!(buffer).unwrap(); + } + + // Write the commands/subcommands sections + build_command_markdown(buffer, Vec::new(), command, 0, options).unwrap(); + + // Write the footer + if options.show_footer { + write!( + buffer, + r#"
+ + + This document was generated automatically by + clap-markdown. + +"# + ) + .unwrap(); + } +} + +fn build_table_of_contents_markdown( + buffer: &mut String, + parent_command_path: Vec, + command: &clap::Command, + _depth: usize, +) -> std::fmt::Result { + // Don't document commands marked with `clap(hide = true)` + if command.is_hide_set() { + return Ok(()); + } + + let title_name = get_canonical_name(command); + + let command_path = { + let mut command_path = parent_command_path; + command_path.push(title_name); + command_path + }; + + writeln!(buffer, "* [`{}`↴](#{})", command_path.join(" "), command_path.join("-"),)?; + + for subcommand in command.get_subcommands() { + build_table_of_contents_markdown(buffer, command_path.clone(), subcommand, _depth + 1)?; + } + + Ok(()) +} + +fn build_command_markdown( + buffer: &mut String, + parent_command_path: Vec, + command: &clap::Command, + _depth: usize, + options: &MarkdownOptions, +) -> std::fmt::Result { + // Don't document commands marked with `clap(hide = true)` + if command.is_hide_set() { + return Ok(()); + } + + let title_name = get_canonical_name(command); + + let command_path = { + let mut command_path = parent_command_path.clone(); + command_path.push(title_name); + command_path + }; + + // Write the markdown heading + writeln!(buffer, "## `{}`\n", command_path.join(" "))?; + + if let Some(long_about) = command.get_long_about() { + writeln!(buffer, "{long_about}\n")?; + } else if let Some(about) = command.get_about() { + writeln!(buffer, "{about}\n")?; + } + + if let Some(help) = command.get_before_long_help() { + writeln!(buffer, "{help}\n")?; + } else if let Some(help) = command.get_before_help() { + writeln!(buffer, "{help}\n")?; + } + + writeln!( + buffer, + "**Usage:** `{}{}`\n", + if parent_command_path.is_empty() { + String::new() + } else { + let mut s = parent_command_path.join(" "); + s.push(' '); + s + }, + command.clone().render_usage().to_string().replace("Usage: ", "") + )?; + + if options.show_aliases { + let aliases = command.get_visible_aliases().collect::>(); + if let Some(aliases_str) = get_alias_string(&aliases) { + writeln!( + buffer, + "**{}:** {aliases_str}\n", + pluralize(aliases.len(), "Command Alias", "Command Aliases") + )?; + } + } + + if let Some(help) = command.get_after_long_help() { + writeln!(buffer, "{help}\n")?; + } else if let Some(help) = command.get_after_help() { + writeln!(buffer, "{help}\n")?; + } + + // Subcommands + if command.get_subcommands().next().is_some() { + writeln!(buffer, "###### **Subcommands:**\n")?; + + for subcommand in command.get_subcommands() { + if subcommand.is_hide_set() { + continue; + } + + let title_name = get_canonical_name(subcommand); + let about = match subcommand.get_about() { + Some(about) => about.to_string(), + None => String::new(), + }; + + writeln!(buffer, "* `{title_name}` — {about}",)?; + } + + writeln!(buffer)?; + } + + // Arguments (positional) + if command.get_positionals().next().is_some() { + writeln!(buffer, "###### **Arguments:**\n")?; + + for pos_arg in command.get_positionals() { + write_arg_markdown(buffer, pos_arg)?; + } + + writeln!(buffer)?; + } + + // Options (grouped by help heading) - PR #48 + let non_pos: Vec<_> = + command.get_arguments().filter(|arg| !arg.is_positional() && !arg.is_hide_set()).collect(); + + if !non_pos.is_empty() { + // Group arguments by help heading + let mut grouped_args: BTreeMap<&str, Vec<&clap::Arg>> = BTreeMap::new(); + + for arg in non_pos { + let heading = arg.get_help_heading().unwrap_or("Options"); + grouped_args.entry(heading).or_default().push(arg); + } + + // Write each group with its heading + for (heading, args) in grouped_args { + writeln!(buffer, "###### **{heading}:**\n")?; + + for arg in args { + write_arg_markdown(buffer, arg)?; + } + + writeln!(buffer)?; + } + } + + // Include extra space between commands + write!(buffer, "\n\n")?; + + for subcommand in command.get_subcommands() { + build_command_markdown(buffer, command_path.clone(), subcommand, _depth + 1, options)?; + } + + Ok(()) +} + +fn write_arg_markdown(buffer: &mut String, arg: &clap::Arg) -> fmt::Result { + // Markdown list item + write!(buffer, "* ")?; + + let value_name: String = match arg.get_value_names() { + Some([name, ..]) => name.as_str().to_owned(), + Some([]) => unreachable!("clap Arg::get_value_names() returned Some(..) of empty list"), + None => arg.get_id().to_string().to_ascii_uppercase(), + }; + + match (arg.get_short(), arg.get_long()) { + (Some(short), Some(long)) => { + if arg.get_action().takes_values() { + write!(buffer, "`-{short}`, `--{long} <{value_name}>`")? + } else { + write!(buffer, "`-{short}`, `--{long}`")? + } + } + (Some(short), None) => { + if arg.get_action().takes_values() { + write!(buffer, "`-{short} <{value_name}>`")? + } else { + write!(buffer, "`-{short}`")? + } + } + (None, Some(long)) => { + if arg.get_action().takes_values() { + write!(buffer, "`--{long} <{value_name}>`")? + } else { + write!(buffer, "`--{long}`")? + } + } + (None, None) => { + debug_assert!( + arg.is_positional(), + "unexpected non-positional Arg with neither short nor long name: {arg:?}" + ); + write!(buffer, "`<{value_name}>`",)?; + } + } + + if let Some(aliases) = arg.get_visible_aliases().as_deref() + && let Some(aliases_str) = get_alias_string(aliases) + { + write!(buffer, " [{}: {aliases_str}]", pluralize(aliases.len(), "alias", "aliases"))?; + } + + if let Some(help) = arg.get_long_help() { + buffer.push_str(&indent(&help.to_string(), " — ", " ")) + } else if let Some(short_help) = arg.get_help() { + writeln!(buffer, " — {short_help}")?; + } else { + writeln!(buffer)?; + } + + // Arg default values + if !arg.get_default_values().is_empty() { + let default_values: String = arg + .get_default_values() + .iter() + .map(|value| format!("`{}`", value.to_string_lossy())) + .collect::>() + .join(", "); + + if arg.get_default_values().len() > 1 { + writeln!(buffer, "\n Default values: {default_values}")?; + } else { + writeln!(buffer, "\n Default value: {default_values}")?; + } + } + + // Arg possible values + let possible_values: Vec = + arg.get_possible_values().into_iter().filter(|pv| !pv.is_hide_set()).collect(); + + if !possible_values.is_empty() && !matches!(arg.get_action(), clap::ArgAction::SetTrue) { + let any_have_help: bool = possible_values.iter().any(|pv| pv.get_help().is_some()); + + if any_have_help { + let text: String = possible_values + .iter() + .map(|pv| match pv.get_help() { + Some(help) => { + format!(" - `{}`:\n {}\n", pv.get_name(), help) + } + None => format!(" - `{}`\n", pv.get_name()), + }) + .collect::>() + .join(""); + + writeln!(buffer, "\n Possible values:\n{text}")?; + } else { + let text: String = possible_values + .iter() + .map(|pv| format!("`{}`", pv.get_name())) + .collect::>() + .join(", "); + + writeln!(buffer, "\n Possible values: {text}\n")?; + } + } + + // Arg environment variable (PR #50) + if !arg.is_hide_env_set() + && let Some(env) = arg.get_env() + { + writeln!(buffer, "\n Environment variable: `{}`", env.to_string_lossy())?; + } + + Ok(()) +} + +/// Utility function to get the canonical name of a command. +fn get_canonical_name(command: &clap::Command) -> String { + command + .get_display_name() + .or_else(|| command.get_bin_name()) + .map(|name| name.to_owned()) + .unwrap_or_else(|| command.get_name().to_owned()) +} + +/// Indents non-empty lines. The output always ends with a newline. +fn indent(s: &str, first: &str, rest: &str) -> String { + if s.is_empty() { + return "\n".to_string(); + } + let mut result = String::new(); + let mut first_line = true; + + for line in s.lines() { + if !line.is_empty() { + result.push_str(if first_line { first } else { rest }); + result.push_str(line); + first_line = false; + } + result.push('\n'); + } + result +} + +fn get_alias_string(aliases: &[&str]) -> Option { + if aliases.is_empty() { + return None; + } + + Some(aliases.iter().map(|alias| format!("`{alias}`")).collect::>().join(", ")) +} + +fn pluralize<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str { + if count == 1 { singular } else { plural } +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::{Arg, Command}; + use pretty_assertions::assert_eq; + + #[test] + fn test_indent() { + assert_eq!(&indent("Header\n\nMore info", "___", "~~~~"), "___Header\n\n~~~~More info\n"); + assert_eq!( + &indent("Header\n\nMore info\n", "___", "~~~~"), + &indent("Header\n\nMore info", "___", "~~~~"), + ); + assert_eq!(&indent("", "___", "~~~~"), "\n"); + assert_eq!(&indent("\n", "___", "~~~~"), "\n"); + } + + #[test] + fn test_version_output() { + let app = Command::new("test-app").version("1.2.3").about("A test application"); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("**Version:** `1.2.3`"), "Should contain version"); + } + + #[test] + fn test_multiline_version() { + let multi_line_version = "my-cli 1.2.3 (abc123)\nmy-lib 2.0.0 (789xyz)"; + + let app = Command::new("my-cli").version(multi_line_version).about("Multi-version CLI"); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("**Version:**\n\n```"), "Should use code block for multi-line"); + } + + #[test] + fn test_env_var_output() { + let app = Command::new("env-test").about("Test env var output").arg( + Arg::new("config") + .short('c') + .long("config") + .env("CONFIG_PATH") + .help("Path to config file"), + ); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!( + markdown.contains("Environment variable: `CONFIG_PATH`"), + "Should show env var. Output: {markdown}" + ); + } + + #[test] + fn test_grouped_options() { + let app = Command::new("grouped-app") + .about("Test app with grouped options") + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .help("Enable verbose output") + .help_heading("General Options") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("input") + .short('i') + .long("input") + .help("Input file") + .help_heading("File Options") + .value_name("FILE"), + ) + .arg( + Arg::new("format") + .short('f') + .long("format") + .help("Output format") + .value_name("FORMAT"), + ); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("###### **File Options:**"), "Should have File Options heading"); + assert!( + markdown.contains("###### **General Options:**"), + "Should have General Options heading" + ); + assert!(markdown.contains("###### **Options:**"), "Should have default Options heading"); + } + + #[test] + fn test_no_grouped_options_backward_compatibility() { + let app = Command::new("simple-app") + .about("Test app without grouped options") + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .help("Enable verbose output") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("output").short('o').long("output").help("Output file").value_name("FILE"), + ); + + let markdown = + help_markdown_command_custom(&app, &MarkdownOptions::new().show_footer(false)); + + assert!(markdown.contains("###### **Options:**"), "Should have default Options heading"); + assert!(markdown.contains("`-v`, `--verbose`"), "Should have verbose option"); + assert!(markdown.contains("`-o`, `--output `"), "Should have output option"); + } +} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index d9acf1de83d09..e1ec43c33df19 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] foundry-block-explorers.workspace = true +foundry-cli-markdown.workspace = true foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true diff --git a/crates/cli/src/handler.rs b/crates/cli/src/handler.rs index 020b7e953068f..763466d21185d 100644 --- a/crates/cli/src/handler.rs +++ b/crates/cli/src/handler.rs @@ -1,5 +1,4 @@ use eyre::EyreHandler; -use itertools::Itertools; use std::{error::Error, fmt}; /// A custom context type for Foundry specific error reporting via `eyre`. @@ -23,8 +22,7 @@ impl Handler { impl EyreHandler for Handler { fn display(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { - use fmt::Display; - foundry_common::errors::dedup_chain(error).into_iter().format("; ").fmt(f) + f.write_str(&foundry_common::errors::display_chain(error)) } fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/crates/cli/src/opts/evm.rs b/crates/cli/src/opts/evm.rs index 6499d693ddf40..7af7c20fb021a 100644 --- a/crates/cli/src/opts/evm.rs +++ b/crates/cli/src/opts/evm.rs @@ -93,6 +93,11 @@ pub struct EvmArgs { #[serde(skip)] pub ffi: bool, + /// Whether to show `console.log` outputs in realtime during script/test execution + #[arg(long)] + #[serde(skip)] + pub live_logs: bool, + /// Use the create 2 factory in all cases including tests and non-broadcasting scripts. #[arg(long)] #[serde(skip)] @@ -157,6 +162,10 @@ impl Provider for EvmArgs { dict.insert("ffi".to_string(), self.ffi.into()); } + if self.live_logs { + dict.insert("live_logs".to_string(), self.live_logs.into()); + } + if self.isolate { dict.insert("isolate".to_string(), self.isolate.into()); } diff --git a/crates/cli/src/opts/global.rs b/crates/cli/src/opts/global.rs index 8bfc13c00001f..fee75a5d34e87 100644 --- a/crates/cli/src/opts/global.rs +++ b/crates/cli/src/opts/global.rs @@ -51,6 +51,17 @@ pub struct GlobalArgs { } impl GlobalArgs { + /// Check if `--markdown-help` was passed and print CLI reference as Markdown, then exit. + /// + /// This must be called **before** parsing arguments, since commands with required + /// subcommands would fail parsing before the flag is checked. + pub fn check_markdown_help() { + if std::env::args().any(|arg| arg == "--markdown-help") { + foundry_cli_markdown::print_help_markdown::(); + std::process::exit(0); + } + } + /// Initialize the global options. pub fn init(&self) -> eyre::Result<()> { // Set the global shell. diff --git a/crates/cli/src/opts/rpc.rs b/crates/cli/src/opts/rpc.rs index 4016c86f2a427..15a5de678272a 100644 --- a/crates/cli/src/opts/rpc.rs +++ b/crates/cli/src/opts/rpc.rs @@ -30,6 +30,14 @@ pub struct RpcOpts { #[arg(short = 'k', long = "insecure", default_value = "false")] pub accept_invalid_certs: bool, + /// Disable automatic proxy detection. + /// + /// Use this in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) where + /// system proxy detection causes crashes. When enabled, HTTP_PROXY/HTTPS_PROXY environment + /// variables and system proxy settings will be ignored. + #[arg(long = "no-proxy", alias = "disable-proxy", default_value = "false")] + pub no_proxy: bool, + /// Use the Flashbots RPC URL with fast mode (). /// /// This shares the transaction privately with all registered builders. @@ -118,6 +126,12 @@ impl RpcOpts { if self.accept_invalid_certs { dict.insert("eth_rpc_accept_invalid_certs".into(), true.into()); } + if self.no_proxy { + dict.insert("eth_rpc_no_proxy".into(), true.into()); + } + if self.curl { + dict.insert("eth_rpc_curl".into(), true.into()); + } dict } diff --git a/crates/cli/src/utils/abi.rs b/crates/cli/src/utils/abi.rs index a33b2b5ee7e25..cbcd96a268166 100644 --- a/crates/cli/src/utils/abi.rs +++ b/crates/cli/src/utils/abi.rs @@ -38,8 +38,12 @@ pub async fn parse_function_args>( let args = resolve_name_args(&args, provider).await; + // Try to decode as hex calldata first, otherwise treat as function signature if let Ok(data) = hex::decode(sig) { return Ok((data, None)); + } else if sig.starts_with("0x") || sig.starts_with("0X") { + let e = hex::decode(sig).unwrap_err(); + eyre::bail!("Invalid hex calldata '{}': {e}", sig); } let func = if sig.contains('(') { diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index b0f3661881843..722a80fbe2d3c 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -125,13 +125,16 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { | NamedChain::KaruraTestnet | NamedChain::Mantle | NamedChain::MantleSepolia + | NamedChain::Metis | NamedChain::Monad | NamedChain::MonadTestnet | NamedChain::Moonbase | NamedChain::Moonbeam | NamedChain::MoonbeamDev | NamedChain::Moonriver - | NamedChain::Metis + | NamedChain::PolkadotTestnet + | NamedChain::Kusama + | NamedChain::Polkadot ); } false diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 5bac965e11c92..92fa39343a761 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -87,7 +87,7 @@ pub fn subscriber() { let registry = tracing_subscriber::Registry::default().with(env_filter()); #[cfg(feature = "tracy")] let registry = registry.with(tracing_tracy::TracyLayer::default()); - registry.with(tracing_subscriber::fmt::layer()).init() + registry.with(tracing_subscriber::fmt::layer().with_writer(std::io::stderr)).init() } fn env_filter() -> tracing_subscriber::EnvFilter { @@ -101,47 +101,14 @@ fn env_filter() -> tracing_subscriber::EnvFilter { /// Returns a [RetryProvider] instantiated using [Config]'s RPC settings. pub fn get_provider(config: &Config) -> Result { - get_provider_builder(config, false)?.build() -} - -/// Returns a [RetryProvider] with curl mode option. -/// -/// When `curl_mode` is true, the provider will print equivalent curl commands -/// to stdout instead of executing RPC requests. -pub fn get_provider_with_curl(config: &Config, curl_mode: bool) -> Result { - get_provider_builder(config, curl_mode)?.build() + get_provider_builder(config)?.build() } /// Returns a [ProviderBuilder] instantiated using [Config] values. /// /// Defaults to `http://localhost:8545` and `Mainnet`. -/// -/// When `curl_mode` is true, the provider will print equivalent curl commands -/// to stdout instead of executing RPC requests. -pub fn get_provider_builder(config: &Config, curl_mode: bool) -> Result { - let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = ProviderBuilder::new(url.as_ref()); - - builder = builder.accept_invalid_certs(config.eth_rpc_accept_invalid_certs); - builder = builder.curl_mode(curl_mode); - - if let Ok(chain) = config.chain.unwrap_or_default().try_into() { - builder = builder.chain(chain); - } - - if let Some(jwt) = config.get_rpc_jwt_secret()? { - builder = builder.jwt(jwt.as_ref()); - } - - if let Some(rpc_timeout) = config.eth_rpc_timeout { - builder = builder.timeout(Duration::from_secs(rpc_timeout)); - } - - if let Some(rpc_headers) = config.eth_rpc_headers.clone() { - builder = builder.headers(rpc_headers); - } - - Ok(builder) +pub fn get_provider_builder(config: &Config) -> Result { + ProviderBuilder::from_config(config) } pub async fn get_chain

(chain: Option, provider: P) -> Result @@ -314,7 +281,7 @@ impl CommandUtils for Command { }; if !msg.is_empty() { err.push(':'); - err.push(if msg.lines().count() == 0 { ' ' } else { '\n' }); + err.push(if msg.lines().count() == 1 { ' ' } else { '\n' }); err.push_str(&msg); } Err(eyre::eyre!(err)) @@ -509,7 +476,7 @@ impl<'a> Git<'a> { } pub fn is_repo_root(self) -> Result { - self.cmd().args(["rev-parse", "--show-cdup"]).exec().map(|out| out.stdout.is_empty()) + self.cmd().args(["rev-parse", "--show-cdup"]).get_stdout_lossy().map(|s| s.is_empty()) } pub fn is_clean(self) -> Result { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 0152f353bc8b3..75776743d1132 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -16,6 +16,7 @@ workspace = true foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-common-fmt.workspace = true foundry-compilers.workspace = true +foundry-config.workspace = true alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } @@ -79,7 +80,6 @@ flate2.workspace = true [build-dependencies] chrono.workspace = true vergen = { workspace = true, features = ["build", "emit_and_set"] } -vergen-git2 = { workspace = true } [dev-dependencies] tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/crates/common/build.rs b/crates/common/build.rs index df57e9fe81888..7d1ba0f085309 100644 --- a/crates/common/build.rs +++ b/crates/common/build.rs @@ -2,16 +2,14 @@ use chrono::DateTime; use std::{error::Error, path::PathBuf}; -use vergen::{BuildBuilder, Emitter}; -use vergen_git2::Git2Builder; fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed=build.rs"); - let build = BuildBuilder::default().build_date(true).build_timestamp(true).build()?; - let git2 = Git2Builder::default().describe(false, true, None).sha(false).build()?; + let build = vergen::Build::builder().build_date(true).build_timestamp(true).build(); + let git = vergen::Gitcl::builder().describe(false, true, None).sha(false).build(); - Emitter::default().add_instructions(&build)?.add_instructions(&git2)?.emit_and_set()?; + vergen::Emitter::new().add_instructions(&build)?.add_instructions(&git)?.emit_and_set()?; let sha = env_var("VERGEN_GIT_SHA"); let sha_short = &sha[..10]; diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 5adfea559f6fe..0c213d77de8e8 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -20,6 +20,7 @@ eyre.workspace = true # ui alloy-consensus.workspace = true +op-alloy-consensus.workspace = true alloy-network.workspace = true alloy-rpc-types = { workspace = true, features = ["eth"] } alloy-serde.workspace = true @@ -32,6 +33,9 @@ yansi.workspace = true # foundry primitives foundry-primitives.workspace = true +# Tempo +tempo-alloy.workspace = true + [dev-dependencies] foundry-macros.workspace = true similar-asserts.workspace = true diff --git a/crates/common/fmt/src/lib.rs b/crates/common/fmt/src/lib.rs index 6affaeec0438a..1d0336c7c7e78 100644 --- a/crates/common/fmt/src/lib.rs +++ b/crates/common/fmt/src/lib.rs @@ -15,4 +15,7 @@ mod exp; pub use exp::{format_int_exp, format_uint_exp, to_exp_notation}; mod ui; -pub use ui::{EthValue, UIfmt, get_pretty_block_attr, get_pretty_tx_attr}; +pub use ui::{ + EthValue, UIfmt, UIfmtReceiptExt, get_pretty_block_attr, get_pretty_receipt_attr, + get_pretty_tx_attr, +}; diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index a7e583b6696e1..2cd999adca536 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -1,20 +1,32 @@ //! Helper trait and functions to format Ethereum types. use alloy_consensus::{ - Eip658Value, Receipt, ReceiptWithBloom, Transaction as TxTrait, TxEnvelope, TxType, Typed2718, + BlockHeader, Eip658Value, Receipt, ReceiptWithBloom, Signed, Transaction as TxTrait, TxEip1559, + TxEip2930, TxEip4844Variant, TxEip7702, TxEnvelope, TxLegacy, TxReceipt, Typed2718, + transaction::TxHashRef, }; use alloy_network::{ - AnyHeader, AnyReceiptEnvelope, AnyRpcBlock, AnyRpcTransaction, AnyTransactionReceipt, - AnyTxEnvelope, ReceiptResponse, + AnyReceiptEnvelope, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTransactionReceipt, + AnyTxEnvelope, BlockResponse, Network, ReceiptResponse, primitives::HeaderResponse, +}; +use alloy_primitives::{ + Address, Bloom, Bytes, FixedBytes, I256, Signature, U8, U64, U256, Uint, hex, }; -use alloy_primitives::{Address, Bloom, Bytes, FixedBytes, I256, U8, U64, U256, Uint, hex}; use alloy_rpc_types::{ AccessListItem, Block, BlockTransactions, Header, Log, Transaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; -use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxReceipt}; +use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope, FoundryTxReceipt}; +use op_alloy_consensus::TxDeposit; use revm::context_interface::transaction::SignedAuthorization; use serde::Deserialize; +use tempo_alloy::{ + primitives::{ + AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope, + transaction::{Call, PrimitiveSignature}, + }, + rpc::{TempoHeaderResponse, TempoTransactionReceipt}, +}; /// length of the name column for pretty formatting `{:>20}{value}` const NAME_COLUMN_LEN: usize = 20usize; @@ -114,12 +126,6 @@ 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() @@ -159,6 +165,12 @@ impl UIfmt for Eip658Value { } } +impl UIfmt for Signature { + fn pretty(&self) -> String { + format!("[r: {}, s: {}, y_parity: {}]", self.r(), self.s(), self.v()) + } +} + impl UIfmt for AnyTransactionReceipt { fn pretty(&self) -> String { let Self { @@ -261,13 +273,13 @@ transactionIndex: {}", } } -impl UIfmt for Block> { +impl UIfmt for Block { fn pretty(&self) -> String { format!( " {} transactions: {}", - pretty_block_basics(self), + pretty_generic_header_response(&self.header), self.transactions.pretty() ) } @@ -311,184 +323,261 @@ impl UIfmt for AccessListItem { } } -impl UIfmt for TxEnvelope { +impl UIfmt for TxLegacy { fn pretty(&self) -> String { - match &self { - Self::Eip2930(tx) => format!( - " -accessList {} + format!( + " chainId {} -gasLimit {} +nonce {} gasPrice {} -hash {} -input {} +gasLimit {} +to {} +value {} +input {}", + self.chain_id.pretty(), + self.nonce.pretty(), + self.gas_price.pretty(), + self.gas_limit.pretty(), + self.to().pretty(), + self.value.pretty(), + self.input.pretty(), + ) + } +} + +impl UIfmt for TxEip2930 { + fn pretty(&self) -> String { + format!( + " +chainId {} nonce {} -r {} -s {} +gasPrice {} +gasLimit {} to {} -type {} value {} -yParity {}", - self.access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.chain_id().pretty(), - self.gas_limit().pretty(), - self.gas_price().pretty(), - self.tx_hash().pretty(), - self.input().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - Self::Eip1559(tx) => format!( - " accessList {} +input {}", + self.chain_id.pretty(), + self.nonce.pretty(), + self.gas_price.pretty(), + self.gas_limit.pretty(), + self.to().pretty(), + self.value.pretty(), + self.access_list.pretty(), + self.input.pretty(), + ) + } +} + +impl UIfmt for TxEip1559 { + fn pretty(&self) -> String { + format!( + " chainId {} +nonce {} gasLimit {} -hash {} -input {} maxFeePerGas {} maxPriorityFeePerGas {} +to {} +value {} +accessList {} +input {}", + self.chain_id.pretty(), + self.nonce.pretty(), + self.gas_limit.pretty(), + self.max_fee_per_gas.pretty(), + self.max_priority_fee_per_gas.pretty(), + self.to().pretty(), + self.value.pretty(), + self.access_list.pretty(), + self.input.pretty(), + ) + } +} + +impl UIfmt for TxEip4844Variant { + fn pretty(&self) -> String { + use alloy_consensus::TxEip4844; + let tx: &TxEip4844 = match self { + Self::TxEip4844(tx) => tx, + Self::TxEip4844WithSidecar(tx) => tx.tx(), + }; + format!( + " +chainId {} nonce {} -r {} -s {} +gasLimit {} +maxFeePerGas {} +maxPriorityFeePerGas {} to {} -type {} value {} -yParity {}", - self.access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.chain_id().pretty(), - self.gas_limit().pretty(), - self.tx_hash().pretty(), - self.input().pretty(), - self.max_fee_per_gas().pretty(), - self.max_priority_fee_per_gas().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - Self::Eip4844(tx) => format!( - " accessList {} blobVersionedHashes {} +maxFeePerBlobGas {} +input {}", + tx.chain_id.pretty(), + tx.nonce.pretty(), + tx.gas_limit.pretty(), + tx.max_fee_per_gas.pretty(), + tx.max_priority_fee_per_gas.pretty(), + tx.to.pretty(), + tx.value.pretty(), + tx.access_list.pretty(), + tx.blob_versioned_hashes.pretty(), + tx.max_fee_per_blob_gas.pretty(), + tx.input.pretty(), + ) + } +} + +impl UIfmt for TxEip7702 { + fn pretty(&self) -> String { + format!( + " chainId {} +nonce {} gasLimit {} -hash {} -input {} -maxFeePerBlobGas {} maxFeePerGas {} maxPriorityFeePerGas {} -nonce {} -r {} -s {} to {} -type {} value {} -yParity {}", - self.access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.blob_versioned_hashes().unwrap_or(&[]).pretty(), - self.chain_id().pretty(), - self.gas_limit().pretty(), - self.tx_hash().pretty(), - self.input().pretty(), - self.max_fee_per_blob_gas().pretty(), - self.max_fee_per_gas().pretty(), - self.max_priority_fee_per_gas().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - Self::Eip7702(tx) => format!( - " accessList {} authorizationList {} -chainId {} +input {}", + self.chain_id.pretty(), + self.nonce.pretty(), + self.gas_limit.pretty(), + self.max_fee_per_gas.pretty(), + self.max_priority_fee_per_gas.pretty(), + self.to.pretty(), + self.value.pretty(), + self.access_list.pretty(), + self.authorization_list.pretty(), + self.input.pretty(), + ) + } +} + +impl UIfmt for TxDeposit { + fn pretty(&self) -> String { + format!( + " +sourceHash {} +from {} +to {} +mint {} +value {} gasLimit {} -hash {} -input {} -maxFeePerGas {} +isSystemTransaction {} +input {}", + self.source_hash.pretty(), + self.from.pretty(), + self.to().pretty(), + self.mint.pretty(), + self.value.pretty(), + self.gas_limit.pretty(), + self.is_system_transaction, + self.input.pretty(), + ) + } +} + +impl UIfmt for Call { + fn pretty(&self) -> String { + format!( + "to: {}, value: {}, input: {}", + self.to.into_to().pretty(), + self.value.pretty(), + self.input.pretty(), + ) + } +} + +impl UIfmt for TempoTransaction { + fn pretty(&self) -> String { + format!( + " +chainId {} +feeToken {} maxPriorityFeePerGas {} +maxFeePerGas {} +gasLimit {} +calls {} +accessList {} +nonceKey {} nonce {} -r {} -s {} -to {} +feePayerSignature {} +validBefore {} +validAfter {}", + self.chain_id.pretty(), + self.fee_token.pretty(), + self.max_priority_fee_per_gas.pretty(), + self.max_fee_per_gas.pretty(), + self.gas_limit.pretty(), + self.calls.pretty(), + self.access_list.pretty(), + self.nonce_key.pretty(), + self.nonce.pretty(), + self.fee_payer_signature.pretty(), + self.valid_after.pretty(), + self.valid_before.pretty(), + ) + } +} + +impl UIfmt for TempoSignature { + fn pretty(&self) -> String { + serde_json::to_string(self).unwrap_or_default() + } +} + +impl UIfmt for AASigned { + fn pretty(&self) -> String { + format!( + " +hash {} type {} -value {} -yParity {}", - self.access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.authorization_list() - .as_ref() - .map(|l| l.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.chain_id().pretty(), - self.gas_limit().pretty(), - self.tx_hash().pretty(), - self.input().pretty(), - self.max_fee_per_gas().pretty(), - self.max_priority_fee_per_gas().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - _ => format!( - " -gas {} -gasPrice {} +{} +tempoSignature {}", + self.hash().pretty(), + self.tx().ty(), + self.tx().pretty().trim_start(), + self.signature().pretty(), + ) + } +} + +impl UIfmt for Signed +where + Self: TxHashRef, +{ + fn pretty(&self) -> String { + format!( + " hash {} -input {} -nonce {} +type {} +{} r {} s {} -to {} -type {} -v {} -value {}", - self.gas_limit().pretty(), - self.gas_price().pretty(), - self.tx_hash().pretty(), - self.input().pretty(), - self.nonce().pretty(), - self.as_legacy() - .map(|tx| FixedBytes::from(tx.signature().r()).pretty()) - .unwrap_or_default(), - self.as_legacy() - .map(|tx| FixedBytes::from(tx.signature().s()).pretty()) - .unwrap_or_default(), - self.to().pretty(), - self.ty(), - self.as_legacy() - .map(|tx| (if tx.signature().v() { 1u64 } else { 0 }).pretty()) - .unwrap_or_default(), - self.value().pretty(), - ), +yParity {}", + self.tx_hash().pretty(), + self.ty(), + self.tx().pretty().trim_start(), + FixedBytes::from(self.signature().r()).pretty(), + FixedBytes::from(self.signature().s()).pretty(), + (if self.signature().v() { 1u64 } else { 0 }).pretty(), + ) + } +} + +impl UIfmt for TxEnvelope { + fn pretty(&self) -> String { + match self { + Self::Legacy(tx) => tx.pretty(), + Self::Eip2930(tx) => tx.pretty(), + Self::Eip1559(tx) => tx.pretty(), + Self::Eip4844(tx) => tx.pretty(), + Self::Eip7702(tx) => tx.pretty(), } } } @@ -501,7 +590,7 @@ impl UIfmt for AnyTxEnvelope { format!( " hash {} -type {} +type {:#x} {} ", tx.hash.pretty(), @@ -512,234 +601,20 @@ type {} } } } -impl UIfmt for Transaction { + +impl UIfmt for TempoTxEnvelope { fn pretty(&self) -> String { - match &self.inner.inner() { - TxEnvelope::Eip2930(tx) => format!( - " -accessList {} -blockHash {} -blockNumber {} -chainId {} -from {} -gasLimit {} -gasPrice {} -hash {} -input {} -nonce {} -r {} -s {} -to {} -transactionIndex {} -type {} -value {} -yParity {}", - self.inner - .access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.block_hash.pretty(), - self.block_number.pretty(), - self.chain_id().pretty(), - self.inner.signer().pretty(), - self.gas_limit().pretty(), - self.gas_price().pretty(), - self.inner.tx_hash().pretty(), - self.input().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.transaction_index.pretty(), - self.inner.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - TxEnvelope::Eip1559(tx) => format!( - " -accessList {} -blockHash {} -blockNumber {} -chainId {} -from {} -gasLimit {} -hash {} -input {} -maxFeePerGas {} -maxPriorityFeePerGas {} -nonce {} -r {} -s {} -to {} -transactionIndex {} -type {} -value {} -yParity {}", - self.inner - .access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.block_hash.pretty(), - self.block_number.pretty(), - self.chain_id().pretty(), - self.inner.signer().pretty(), - self.gas_limit().pretty(), - tx.hash().pretty(), - self.input().pretty(), - self.max_fee_per_gas().pretty(), - self.max_priority_fee_per_gas().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.transaction_index.pretty(), - self.inner.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - TxEnvelope::Eip4844(tx) => format!( - " -accessList {} -blobVersionedHashes {} -blockHash {} -blockNumber {} -chainId {} -from {} -gasLimit {} -hash {} -input {} -maxFeePerBlobGas {} -maxFeePerGas {} -maxPriorityFeePerGas {} -nonce {} -r {} -s {} -to {} -transactionIndex {} -type {} -value {} -yParity {}", - self.inner - .access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.blob_versioned_hashes().unwrap_or(&[]).pretty(), - self.block_hash.pretty(), - self.block_number.pretty(), - self.chain_id().pretty(), - self.inner.signer().pretty(), - self.gas_limit().pretty(), - tx.hash().pretty(), - self.input().pretty(), - self.max_fee_per_blob_gas().pretty(), - self.max_fee_per_gas().pretty(), - self.max_priority_fee_per_gas().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.transaction_index.pretty(), - self.inner.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - TxEnvelope::Eip7702(tx) => format!( - " -accessList {} -authorizationList {} -blockHash {} -blockNumber {} -chainId {} -from {} -gasLimit {} -hash {} -input {} -maxFeePerGas {} -maxPriorityFeePerGas {} -nonce {} -r {} -s {} -to {} -transactionIndex {} -type {} -value {} -yParity {}", - self.inner - .access_list() - .map(|a| a.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.authorization_list() - .as_ref() - .map(|l| l.iter().collect::>()) - .unwrap_or_default() - .pretty(), - self.block_hash.pretty(), - self.block_number.pretty(), - self.chain_id().pretty(), - self.inner.signer().pretty(), - self.gas_limit().pretty(), - tx.hash().pretty(), - self.input().pretty(), - self.max_fee_per_gas().pretty(), - self.max_priority_fee_per_gas().pretty(), - self.nonce().pretty(), - FixedBytes::from(tx.signature().r()).pretty(), - FixedBytes::from(tx.signature().s()).pretty(), - self.to().pretty(), - self.transaction_index.pretty(), - self.inner.ty(), - self.value().pretty(), - (if tx.signature().v() { 1u64 } else { 0 }).pretty(), - ), - _ => format!( - " -blockHash {} -blockNumber {} -from {} -gas {} -gasPrice {} -hash {} -input {} -nonce {} -r {} -s {} -to {} -transactionIndex {} -v {} -value {}", - self.block_hash.pretty(), - self.block_number.pretty(), - self.inner.signer().pretty(), - self.gas_limit().pretty(), - self.gas_price().pretty(), - self.inner.tx_hash().pretty(), - self.input().pretty(), - self.nonce().pretty(), - self.inner - .as_legacy() - .map(|tx| FixedBytes::from(tx.signature().r()).pretty()) - .unwrap_or_default(), - self.inner - .as_legacy() - .map(|tx| FixedBytes::from(tx.signature().s()).pretty()) - .unwrap_or_default(), - self.to().pretty(), - self.transaction_index.pretty(), - self.inner - .as_legacy() - .map(|tx| (if tx.signature().v() { 1u64 } else { 0 }).pretty()) - .unwrap_or_default(), - self.value().pretty(), - ), + match self { + Self::Legacy(tx) => tx.pretty(), + Self::Eip2930(tx) => tx.pretty(), + Self::Eip1559(tx) => tx.pretty(), + Self::Eip7702(tx) => tx.pretty(), + Self::AA(tx) => tx.pretty(), } } } -impl UIfmt for Transaction { +impl UIfmt for Transaction { fn pretty(&self) -> String { format!( " @@ -748,14 +623,13 @@ blockNumber {} from {} transactionIndex {} effectiveGasPrice {} -{} - ", +{}", self.block_hash.pretty(), self.block_number.pretty(), self.inner.signer().pretty(), self.transaction_index.pretty(), self.effective_gas_price.pretty(), - self.inner.pretty().trim_start(), + self.inner.inner().pretty().trim_start(), ) } } @@ -810,61 +684,220 @@ impl UIfmt for EthValue { } } -impl UIfmt for SignedAuthorization { - fn pretty(&self) -> String { - let signed_authorization = serde_json::to_string(self).unwrap_or("".to_string()); +impl UIfmt for SignedAuthorization { + fn pretty(&self) -> String { + let signed_authorization = serde_json::to_string(self).unwrap_or("".to_string()); + + match self.recover_authority() { + Ok(authority) => format!( + "{{recoveredAuthority: {authority}, signedAuthority: {signed_authorization}}}", + ), + Err(e) => format!( + "{{recoveredAuthority: , signedAuthority: {signed_authorization}}}", + ), + } + } +} + +impl UIfmt for FoundryReceiptEnvelope +where + T: UIfmt + Clone + core::fmt::Debug + PartialEq + Eq, +{ + fn pretty(&self) -> String { + let receipt = self.as_receipt(); + let deposit_info = match self { + Self::Deposit(d) => { + format!( + " +depositNonce {} +depositReceiptVersion {}", + d.receipt.deposit_nonce.pretty(), + d.receipt.deposit_receipt_version.pretty() + ) + } + _ => String::new(), + }; + + format!( + " +status {} +cumulativeGasUsed {} +logs {} +logsBloom {} +type {}{}", + receipt.status.pretty(), + receipt.cumulative_gas_used.pretty(), + receipt.logs.pretty(), + self.logs_bloom().pretty(), + self.tx_type() as u8, + deposit_info + ) + } +} + +impl UIfmt for FoundryTxReceipt { + fn pretty(&self) -> String { + let receipt = &self.0.inner; + let other = &self.0.other; + + let mut pretty = format!( + " +blockHash {} +blockNumber {} +contractAddress {} +cumulativeGasUsed {} +effectiveGasPrice {} +from {} +gasUsed {} +logs {} +logsBloom {} +root {} +status {} +transactionHash {} +transactionIndex {} +type {} +blobGasPrice {} +blobGasUsed {}", + receipt.block_hash.pretty(), + receipt.block_number.pretty(), + receipt.contract_address.pretty(), + receipt.inner.cumulative_gas_used().pretty(), + receipt.effective_gas_price.pretty(), + receipt.from.pretty(), + receipt.gas_used.pretty(), + serde_json::to_string(receipt.inner.logs()).unwrap(), + receipt.inner.logs_bloom().pretty(), + self.state_root().pretty(), + receipt.inner.status().pretty(), + receipt.transaction_hash.pretty(), + receipt.transaction_index.pretty(), + receipt.inner.tx_type() as u8, + receipt.blob_gas_price.pretty(), + receipt.blob_gas_used.pretty() + ); + + if let Some(to) = receipt.to { + pretty.push_str(&format!("\nto {}", to.pretty())); + } + + // additional captured fields + pretty.push_str(&other.pretty()); + + pretty + } +} + +pub trait UIfmtHeaderExt { + fn size_pretty(&self) -> String; +} + +impl UIfmtHeaderExt for Header { + fn size_pretty(&self) -> String { + self.size.pretty() + } +} + +impl UIfmtHeaderExt for AnyRpcHeader { + fn size_pretty(&self) -> String { + self.size.pretty() + } +} + +impl UIfmtHeaderExt for TempoHeaderResponse { + fn size_pretty(&self) -> String { + self.inner.size.pretty() + } +} + +pub trait UIfmtSignatureExt { + fn signature_pretty(&self) -> Option<(String, String, String)>; +} + +impl UIfmtSignatureExt for TxEnvelope { + fn signature_pretty(&self) -> Option<(String, String, String)> { + let sig = self.signature(); + Some(( + FixedBytes::from(sig.r()).pretty(), + FixedBytes::from(sig.s()).pretty(), + U8::from_le_slice(&sig.as_bytes()[64..]).pretty(), + )) + } +} + +impl UIfmtSignatureExt for AnyTxEnvelope { + fn signature_pretty(&self) -> Option<(String, String, String)> { + self.as_envelope().and_then(|envelope| envelope.signature_pretty()) + } +} + +impl UIfmtSignatureExt for FoundryTxEnvelope { + fn signature_pretty(&self) -> Option<(String, String, String)> { + self.clone().try_into_eth().ok().and_then(|envelope| envelope.signature_pretty()) + } +} + +impl UIfmtSignatureExt for TempoTxEnvelope { + fn signature_pretty(&self) -> Option<(String, String, String)> { + let sig = match self { + Self::Legacy(tx) => Some(tx.signature()), + Self::Eip2930(tx) => Some(tx.signature()), + Self::Eip1559(tx) => Some(tx.signature()), + Self::Eip7702(tx) => Some(tx.signature()), + Self::AA(tempo_tx) => { + if let TempoSignature::Primitive(PrimitiveSignature::Secp256k1(sig)) = + tempo_tx.signature() + { + Some(sig) + } else { + None + } + } + }?; + Some(( + FixedBytes::from(sig.r()).pretty(), + FixedBytes::from(sig.s()).pretty(), + U8::from_le_slice(&sig.as_bytes()[64..]).pretty(), + )) + } +} + +pub trait UIfmtReceiptExt { + fn logs_pretty(&self) -> String; + fn logs_bloom_pretty(&self) -> String; + fn tx_type_pretty(&self) -> String; +} - match self.recover_authority() { - Ok(authority) => format!( - "{{recoveredAuthority: {authority}, signedAuthority: {signed_authorization}}}", - ), - Err(e) => format!( - "{{recoveredAuthority: , signedAuthority: {signed_authorization}}}", - ), - } +impl UIfmtReceiptExt for AnyTransactionReceipt { + fn logs_pretty(&self) -> String { + serde_json::to_string(&self.inner.inner.inner.receipt.logs).unwrap_or_default() + } + + fn logs_bloom_pretty(&self) -> String { + self.inner.inner.inner.logs_bloom.pretty() + } + + fn tx_type_pretty(&self) -> String { + self.inner.inner.r#type.to_string() } } -impl UIfmt for FoundryReceiptEnvelope -where - T: UIfmt + Clone + core::fmt::Debug + PartialEq + Eq, -{ - fn pretty(&self) -> String { - let receipt = self.as_receipt(); - let deposit_info = match self { - Self::Deposit(d) => { - format!( - " -depositNonce {} -depositReceiptVersion {}", - d.receipt.deposit_nonce.pretty(), - d.receipt.deposit_receipt_version.pretty() - ) - } - _ => String::new(), - }; +impl UIfmtReceiptExt for FoundryTxReceipt { + fn logs_pretty(&self) -> String { + serde_json::to_string(self.0.inner.inner.logs()).unwrap_or_default() + } - format!( - " -status {} -cumulativeGasUsed {} -logs {} -logsBloom {} -type {}{}", - receipt.status.pretty(), - receipt.cumulative_gas_used.pretty(), - receipt.logs.pretty(), - self.logs_bloom().pretty(), - self.tx_type() as u8, - deposit_info - ) + fn logs_bloom_pretty(&self) -> String { + self.0.inner.inner.logs_bloom().pretty() + } + + fn tx_type_pretty(&self) -> String { + (self.0.inner.inner.tx_type() as u8).to_string() } } -impl UIfmt for FoundryTxReceipt { +impl UIfmt for TempoTransactionReceipt { fn pretty(&self) -> String { - let receipt = &self.0.inner; - let other = &self.0.other; + let receipt = &self.inner; let mut pretty = format!( " @@ -882,8 +915,8 @@ status {} transactionHash {} transactionIndex {} type {} -blobGasPrice {} -blobGasUsed {}", +feePayer {} +feeToken {}", receipt.block_hash.pretty(), receipt.block_number.pretty(), receipt.contract_address.pretty(), @@ -892,85 +925,103 @@ blobGasUsed {}", receipt.from.pretty(), receipt.gas_used.pretty(), serde_json::to_string(receipt.inner.logs()).unwrap(), - receipt.inner.logs_bloom().pretty(), + receipt.inner.logs_bloom.pretty(), self.state_root().pretty(), receipt.inner.status().pretty(), receipt.transaction_hash.pretty(), receipt.transaction_index.pretty(), - receipt.inner.tx_type() as u8, - receipt.blob_gas_price.pretty(), - receipt.blob_gas_used.pretty() + receipt.inner.receipt.tx_type as u8, + self.fee_payer.pretty(), + self.fee_token.pretty(), ); if let Some(to) = receipt.to { pretty.push_str(&format!("\nto {}", to.pretty())); } - // additional captured fields - pretty.push_str(&other.pretty()); - pretty } } +impl UIfmtReceiptExt for TempoTransactionReceipt { + fn logs_pretty(&self) -> String { + serde_json::to_string(self.inner.inner.logs()).unwrap_or_default() + } + + fn logs_bloom_pretty(&self) -> String { + self.inner.inner.logs_bloom.pretty() + } + + fn tx_type_pretty(&self) -> String { + (self.inner.inner.receipt.tx_type as u8).to_string() + } +} + /// Returns the `UiFmt::pretty()` formatted attribute of the transactions -pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option { - let sig = match &transaction.inner.inner() { - AnyTxEnvelope::Ethereum(envelope) => match &envelope { - TxEnvelope::Eip2930(tx) => Some(tx.signature()), - TxEnvelope::Eip1559(tx) => Some(tx.signature()), - TxEnvelope::Eip4844(tx) => Some(tx.signature()), - TxEnvelope::Eip7702(tx) => Some(tx.signature()), - TxEnvelope::Legacy(tx) => Some(tx.signature()), - }, - _ => None, - }; +pub fn get_pretty_tx_attr(transaction: &N::TransactionResponse, attr: &str) -> Option +where + N: Network, + N::TxEnvelope: UIfmtSignatureExt, +{ + let (r, s, v) = transaction.as_ref().signature_pretty().unwrap_or_default(); match attr { - "blockHash" | "block_hash" => Some(transaction.block_hash.pretty()), - "blockNumber" | "block_number" => Some(transaction.block_number.pretty()), - "from" => Some(transaction.inner.signer().pretty()), - "gas" => Some(transaction.gas_limit().pretty()), - "gasPrice" | "gas_price" => Some(Transaction::gas_price(transaction).pretty()), + "blockHash" | "block_hash" => { + Some(alloy_network::TransactionResponse::block_hash(transaction).pretty()) + } + "blockNumber" | "block_number" => { + Some(alloy_network::TransactionResponse::block_number(transaction).pretty()) + } + "from" => Some(alloy_network::TransactionResponse::from(transaction).pretty()), + "gas" => Some(TxTrait::gas_limit(transaction).pretty()), + "gasPrice" | "gas_price" => Some(TxTrait::max_fee_per_gas(transaction).pretty()), "hash" => Some(alloy_network::TransactionResponse::tx_hash(transaction).pretty()), - "input" => Some(transaction.input().pretty()), - "nonce" => Some(transaction.nonce().to_string()), - "s" => sig.map(|s| FixedBytes::from(s.s()).pretty()), - "r" => sig.map(|s| FixedBytes::from(s.r()).pretty()), - "to" => Some(transaction.to().pretty()), - "transactionIndex" | "transaction_index" => Some(transaction.transaction_index.pretty()), - "v" => sig.map(|s| U8::from_be_slice(&s.as_bytes()[64..]).pretty()), - "value" => Some(transaction.value().pretty()), + "input" => Some(TxTrait::input(transaction).pretty()), + "nonce" => Some(TxTrait::nonce(transaction).to_string()), + "s" => Some(s), + "r" => Some(r), + "to" => Some(TxTrait::to(transaction).pretty()), + "transactionIndex" | "transaction_index" => { + Some(alloy_network::TransactionResponse::transaction_index(transaction).pretty()) + } + "v" => Some(v), + "value" => Some(TxTrait::value(transaction).pretty()), _ => None, } } -/// Returns the `UiFmt::pretty()` formatted attribute of the given block -pub fn get_pretty_block_attr(block: &AnyRpcBlock, attr: &str) -> Option { +pub fn get_pretty_block_attr(block: &N::BlockResponse, attr: &str) -> Option +where + N: Network, + N::BlockResponse: BlockResponse

, + N::HeaderResponse: UIfmtHeaderExt, +{ match attr { - "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.inner.beneficiary.pretty()), - "mixHash" | "mix_hash" => Some(block.header.mix_hash.pretty()), - "nonce" => Some(block.header.nonce.pretty()), - "number" => Some(block.header.number.pretty()), - "parentHash" | "parent_hash" => Some(block.header.parent_hash.pretty()), - "transactionsRoot" | "transactions_root" => Some(block.header.transactions_root.pretty()), - "receiptsRoot" | "receipts_root" => Some(block.header.receipts_root.pretty()), - "sha3Uncles" | "sha_3_uncles" => Some(block.header.ommers_hash.pretty()), - "size" => Some(block.header.size.pretty()), - "stateRoot" | "state_root" => Some(block.header.state_root.pretty()), - "timestamp" => Some(block.header.timestamp.pretty()), - "totalDifficulty" | "total_difficult" => Some(block.header.total_difficulty.pretty()), - "blobGasUsed" | "blob_gas_used" => Some(block.header.blob_gas_used.pretty()), - "excessBlobGas" | "excess_blob_gas" => Some(block.header.excess_blob_gas.pretty()), - "requestsHash" | "requests_hash" => Some(block.header.requests_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().beneficiary().pretty()), + "mixHash" | "mix_hash" => Some(block.header().mix_hash().pretty()), + "nonce" => Some(block.header().nonce().pretty()), + "number" => Some(block.header().number().pretty()), + "parentHash" | "parent_hash" => Some(block.header().parent_hash().pretty()), + "transactionsRoot" | "transactions_root" => { + Some(block.header().transactions_root().pretty()) + } + "receiptsRoot" | "receipts_root" => Some(block.header().receipts_root().pretty()), + "sha3Uncles" | "sha_3_uncles" => Some(block.header().ommers_hash().pretty()), + "size" => Some(block.header().size_pretty()), + "stateRoot" | "state_root" => Some(block.header().state_root().pretty()), + "timestamp" => Some(block.header().timestamp().pretty()), + "totalDifficulty" | "total_difficulty" => Some(block.header().difficulty().pretty()), + "blobGasUsed" | "blob_gas_used" => Some(block.header().blob_gas_used().pretty()), + "excessBlobGas" | "excess_blob_gas" => Some(block.header().excess_blob_gas().pretty()), + "requestsHash" | "requests_hash" => Some(block.header().requests_hash().pretty()), other => { - if let Some(value) = block.other.get(other) { + if let Some(value) = block.other_fields().and_then(|fields| fields.get(other)) { let val = EthValue::from(value.clone()); return Some(val.pretty()); } @@ -979,42 +1030,34 @@ pub fn get_pretty_block_attr(block: &AnyRpcBlock, attr: &str) -> Option } } -fn pretty_block_basics(block: &Block>) -> String { - let Block { - header: - Header { - hash, - size, - total_difficulty, - inner: - AnyHeader { - parent_hash, - ommers_hash, - beneficiary, - state_root, - transactions_root, - receipts_root, - logs_bloom, - difficulty, - number, - gas_limit, - gas_used, - timestamp, - extra_data, - mix_hash, - nonce, - base_fee_per_gas, - withdrawals_root, - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root, - requests_hash, - }, - }, - uncles: _, - transactions: _, - withdrawals: _, - } = block; +pub fn get_pretty_receipt_attr(receipt: &N::ReceiptResponse, attr: &str) -> Option +where + N: Network, + N::ReceiptResponse: ReceiptResponse + UIfmtReceiptExt, +{ + match attr { + "blockHash" | "block_hash" => Some(receipt.block_hash().pretty()), + "blockNumber" | "block_number" => Some(receipt.block_number().pretty()), + "contractAddress" | "contract_address" => Some(receipt.contract_address().pretty()), + "cumulativeGasUsed" | "cumulative_gas_used" => Some(receipt.cumulative_gas_used().pretty()), + "effectiveGasPrice" | "effective_gas_price" => Some(receipt.effective_gas_price().pretty()), + "from" => Some(receipt.from().pretty()), + "gasUsed" | "gas_used" => Some(receipt.gas_used().pretty()), + "logs" => Some(receipt.logs_pretty()), + "logsBloom" | "logs_bloom" => Some(receipt.logs_bloom_pretty()), + "root" | "stateRoot" | "state_root" => Some(receipt.state_root().pretty()), + "status" | "statusCode" | "status_code" => Some(receipt.status().pretty()), + "transactionHash" | "transaction_hash" => Some(receipt.transaction_hash().pretty()), + "transactionIndex" | "transaction_index" => Some(receipt.transaction_index().pretty()), + "to" => Some(receipt.to().pretty()), + "type" | "transaction_type" => Some(receipt.tx_type_pretty()), + "blobGasPrice" | "blob_gas_price" => Some(receipt.blob_gas_price().pretty()), + "blobGasUsed" | "blob_gas_used" => Some(receipt.blob_gas_used().pretty()), + _ => None, + } +} + +fn pretty_generic_header_response(header: &H) -> String { format!( " baseFeePerGas {} @@ -1041,31 +1084,31 @@ totalDifficulty {} blobGasUsed {} excessBlobGas {} requestsHash {}", - base_fee_per_gas.pretty(), - difficulty.pretty(), - extra_data.pretty(), - gas_limit.pretty(), - gas_used.pretty(), - hash.pretty(), - logs_bloom.pretty(), - beneficiary.pretty(), - mix_hash.pretty(), - nonce.pretty(), - number.pretty(), - parent_hash.pretty(), - parent_beacon_block_root.pretty(), - transactions_root.pretty(), - receipts_root.pretty(), - ommers_hash.pretty(), - size.pretty(), - state_root.pretty(), - timestamp.pretty(), - fmt_timestamp(*timestamp), - withdrawals_root.pretty(), - total_difficulty.pretty(), - blob_gas_used.pretty(), - excess_blob_gas.pretty(), - requests_hash.pretty(), + header.base_fee_per_gas().pretty(), + header.difficulty().pretty(), + header.extra_data().pretty(), + header.gas_limit().pretty(), + header.gas_used().pretty(), + header.hash().pretty(), + header.logs_bloom().pretty(), + header.beneficiary().pretty(), + header.mix_hash().pretty(), + header.nonce().pretty(), + header.number().pretty(), + header.parent_hash().pretty(), + header.parent_beacon_block_root().pretty(), + header.transactions_root().pretty(), + header.receipts_root().pretty(), + header.ommers_hash().pretty(), + header.size_pretty(), + header.state_root().pretty(), + header.timestamp().pretty(), + fmt_timestamp(header.timestamp()), + header.withdrawals_root().pretty(), + header.difficulty().pretty(), + header.blob_gas_used().pretty(), + header.excess_blob_gas().pretty(), + header.requests_hash().pretty(), ) } @@ -1092,6 +1135,7 @@ mod tests { use super::*; use alloy_primitives::B256; use alloy_rpc_types::Authorization; + use foundry_primitives::FoundryNetwork; use similar_asserts::assert_eq; use std::str::FromStr; @@ -1150,23 +1194,26 @@ mod tests { } "#; - let tx: WithOtherFields = serde_json::from_str(s).unwrap(); + let tx: WithOtherFields> = serde_json::from_str(s).unwrap(); assert_eq!(tx.pretty().trim(), r" blockHash 0x02b853cf50bc1c335b70790f93d5a390a35a166bea9c895e685cc866e4961cae blockNumber 436 from 0x3b179DcfC5fAa677044c27dCe958e4BC0ad696A6 -gas 18660316 -gasPrice 0 +transactionIndex 0 +effectiveGasPrice 0 hash 0x2642e960d3150244e298d52b5b0f024782253e6d0b2c9a01dd4858f7b4665a3f -input 0xd294f093 +type 0 +chainId 10 nonce 162 -r 0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2bee -s 0x0e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583 +gasPrice 0 +gasLimit 18660316 to 0x4a16A42407AA491564643E1dfc1fd50af29794eF -transactionIndex 0 -v 1 value 0 +input 0xd294f093 +r 0x6fca94073a0cf3381978662d46cf890602d3e9ccf6a31e4b69e8ecbd995e2bee +s 0x0e804161a2b56a37ca1f6f4c4b8bce926587afa0d9b1acc5165e6556c959d583 +yParity 1 index 435 l1BlockNumber 12691036 l1Timestamp 1624460128 @@ -1197,17 +1244,29 @@ txType 0 "v": "0x1", "r": "0x2a98c51c2782f664d3ce571fef0491b48f5ebbc5845fa513192e6e6b24ecdaa1", "s": "0x29b8e0c67aa9c11327e16556c591dc84a7aac2f6fc57c7f93901be8ee867aebc", - "chainId": "0x66a", - "accessList": [ - { "address": "0x2b371c0262ceab27face32fbb5270ddc6aa01ba4", "storageKeys": ["0x1122334455667788990011223344556677889900112233445566778899001122", "0x0000000000000000000000000000000000000000000000000000000000000000"] }, - { "address": "0x8e730df7c70d33118d9e5f79ab81aed0be6f6635", "storageKeys": [] } - ] + "chainId": "0x66a", + "accessList": [ + { "address": "0x2b371c0262ceab27face32fbb5270ddc6aa01ba4", "storageKeys": ["0x1122334455667788990011223344556677889900112233445566778899001122", "0x0000000000000000000000000000000000000000000000000000000000000000"] }, + { "address": "0x8e730df7c70d33118d9e5f79ab81aed0be6f6635", "storageKeys": [] } + ] } "#; - let tx: Transaction = serde_json::from_str(s).unwrap(); assert_eq!(tx.pretty().trim(), r" +blockHash 0x2b27fe2bbc8ce01ac7ae8bf74f793a197cf7edbe82727588811fa9a2c4776f81 +blockNumber 76573 +from 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4 +transactionIndex 2 +effectiveGasPrice 1000000000 +hash 0xbddbb685774d8a3df036ed9fb920b48f876090a57e9e90ee60921e0510ef7090 +type 1 +chainId 1642 +nonce 28 +gasPrice 1000000000 +gasLimit 27615 +to 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635 +value 0 accessList [ 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4 => [ 0x1122334455667788990011223344556677889900112233445566778899001122 @@ -1215,21 +1274,9 @@ accessList [ ] 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635 => [] ] -blockHash 0x2b27fe2bbc8ce01ac7ae8bf74f793a197cf7edbe82727588811fa9a2c4776f81 -blockNumber 76573 -chainId 1642 -from 0x2b371c0262CEAb27fAcE32FBB5270dDc6Aa01ba4 -gasLimit 27615 -gasPrice 1000000000 -hash 0xbddbb685774d8a3df036ed9fb920b48f876090a57e9e90ee60921e0510ef7090 input 0x9c0e3f7a0000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000002a -nonce 28 r 0x2a98c51c2782f664d3ce571fef0491b48f5ebbc5845fa513192e6e6b24ecdaa1 s 0x29b8e0c67aa9c11327e16556c591dc84a7aac2f6fc57c7f93901be8ee867aebc -to 0x8E730Df7C70D33118D9e5F79ab81aEd0bE6F6635 -transactionIndex 2 -type 1 -value 0 yParity 1 ".trim() ); @@ -1271,27 +1318,28 @@ yParity 1 assert_eq!( tx.pretty().trim(), r" -accessList [ - 0xC141a9A7463e6C4716d9FC0C056C054F46Bb2993 => [ - 0x0000000000000000000000000000000000000000000000000000000000000000 - ] -] blockHash 0x61abbe5e22738de0462046f5a5d6c4cd6bc1f3a6398e4457d5e293590e721125 blockNumber 30279 -chainId 1642 from 0xBaaDF00d42264eEb3FAFe6799d0b56cf55DF0F00 -gasLimit 100000 +transactionIndex 65 +effectiveGasPrice 20000000000 hash 0xa7231d4da0576fade5d3b9481f4cd52459ec59b9bbdbf4f60d6cd726b2a3a244 -input 0x48600055323160015500 +type 2 +chainId 1642 +nonce 300 +gasLimit 100000 maxFeePerGas 20000000000 maxPriorityFeePerGas 20000000000 -nonce 300 -r 0x396864e5f9132327defdb1449504252e1fa6bce73feb8cd6f348a342b198af34 -s 0x44dbba72e6d3304104848277143252ee43627c82f02d1ef8e404e1bf97c70158 to -transactionIndex 65 -type 2 value 0 +accessList [ + 0xC141a9A7463e6C4716d9FC0C056C054F46Bb2993 => [ + 0x0000000000000000000000000000000000000000000000000000000000000000 + ] +] +input 0x48600055323160015500 +r 0x396864e5f9132327defdb1449504252e1fa6bce73feb8cd6f348a342b198af34 +s 0x44dbba72e6d3304104848277143252ee43627c82f02d1ef8e404e1bf97c70158 yParity 1 " .trim() @@ -1301,57 +1349,58 @@ yParity 1 #[test] fn can_pretty_print_eip4884() { let s = r#"{ - "blockHash": "0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052", - "blockNumber": "0x2a1cb", - "from": "0xad01b55d7c3448b8899862eb335fbb17075d8de2", - "gas": "0x5208", - "gasPrice": "0x1d1a94a201c", - "maxFeePerGas": "0x1d1a94a201c", - "maxPriorityFeePerGas": "0x1d1a94a201c", - "maxFeePerBlobGas": "0x3e8", - "hash": "0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00", - "input": "0x", - "nonce": "0x1b483", - "to": "0x000000000000000000000000000000000000f1c1", - "transactionIndex": "0x0", - "value": "0x0", - "type": "0x3", - "accessList": [], - "chainId": "0x1a1f0ff42", - "blobVersionedHashes": [ - "0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76" - ], - "v": "0x0", - "r": "0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1", - "s": "0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b", - "yParity": "0x0" - } + "blockHash": "0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052", + "blockNumber": "0x2a1cb", + "from": "0xad01b55d7c3448b8899862eb335fbb17075d8de2", + "gas": "0x5208", + "gasPrice": "0x1d1a94a201c", + "maxFeePerGas": "0x1d1a94a201c", + "maxPriorityFeePerGas": "0x1d1a94a201c", + "maxFeePerBlobGas": "0x3e8", + "hash": "0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00", + "input": "0x", + "nonce": "0x1b483", + "to": "0x000000000000000000000000000000000000f1c1", + "transactionIndex": "0x0", + "value": "0x0", + "type": "0x3", + "accessList": [], + "chainId": "0x1a1f0ff42", + "blobVersionedHashes": [ + "0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76" + ], + "v": "0x0", + "r": "0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1", + "s": "0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b", + "yParity": "0x0" + } "#; let tx: Transaction = serde_json::from_str(s).unwrap(); assert_eq!( tx.pretty().trim(), r" -accessList [] -blobVersionedHashes [ - 0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76 -] blockHash 0xfc2715ff196e23ae613ed6f837abd9035329a720a1f4e8dce3b0694c867ba052 blockNumber 172491 -chainId 7011893058 from 0xAD01b55d7c3448B8899862eb335FBb17075d8DE2 -gasLimit 21000 +transactionIndex 0 +effectiveGasPrice 2000000000028 hash 0x5ceec39b631763ae0b45a8fb55c373f38b8fab308336ca1dc90ecd2b3cf06d00 -input 0x -maxFeePerBlobGas 1000 +type 3 +chainId 7011893058 +nonce 111747 +gasLimit 21000 maxFeePerGas 2000000000028 maxPriorityFeePerGas 2000000000028 -nonce 111747 -r 0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1 -s 0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b to 0x000000000000000000000000000000000000f1C1 -transactionIndex 0 -type 3 value 0 +accessList [] +blobVersionedHashes [ + 0x01a128c46fc61395706686d6284f83c6c86dfc15769b9363171ea9d8566e6e76 +] +maxFeePerBlobGas 1000 +input 0x +r 0x343c6239323a81ef61293cb4a4d37b6df47fbf68114adb5dd41581151a077da1 +s 0x48c21f6872feaf181d37cc4f9bbb356d3f10b352ceb38d1c3b190d749f95a11b yParity 0 " .trim() @@ -1362,21 +1411,26 @@ yParity 0 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 output ="\nblockHash 0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972 + let output = " +blockHash 0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972 blockNumber 3 from 0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a -gas 90000 -gasPrice 20000000000 +transactionIndex 0 +effectiveGasPrice 20000000000 hash 0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067 -input 0x +type 0 +chainId 1 nonce 2 +gasPrice 20000000000 +gasLimit 90000 +to 0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e +value 0 +input 0x r 0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88 s 0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e -to 0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e -transactionIndex 0 -v 0 -value 0".to_string(); - let txs = match block.transactions { +yParity 0" + .to_string(); + let txs = match block.transactions() { BlockTransactions::Full(txs) => txs, _ => panic!("not full transactions"), }; @@ -1428,41 +1482,51 @@ value 0".to_string(); #[test] fn test_pretty_tx_attr() { let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#; - let block: Block> = serde_json::from_str(block).unwrap(); - let txs = match block.transactions { + let block: ::BlockResponse = + 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!(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(&txs[0], "from") + 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("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(&txs[0], "hash") + get_pretty_tx_attr::(&txs[0], "hash") ); - 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("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(&txs[0], "r") + get_pretty_tx_attr::(&txs[0], "r") ); assert_eq!( Some("0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e".to_string()), - get_pretty_tx_attr(&txs[0], "s") + get_pretty_tx_attr::(&txs[0], "s") ); assert_eq!( Some("0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e".into()), - get_pretty_tx_attr(&txs[0], "to") + get_pretty_tx_attr::(&txs[0], "to") + ); + assert_eq!( + Some("0".to_string()), + get_pretty_tx_attr::(&txs[0], "transactionIndex") ); - assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&txs[0], "transactionIndex")); - assert_eq!(Some("27".to_string()), get_pretty_tx_attr(&txs[0], "v")); - assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&txs[0], "value")); + assert_eq!(Some("27".to_string()), get_pretty_tx_attr::(&txs[0], "v")); + assert_eq!(Some("0".to_string()), get_pretty_tx_attr::(&txs[0], "value")); } #[test] @@ -1494,59 +1558,87 @@ value 0".to_string(); "gasUsed": "0x9f759", "timestamp": "0x54e34e8e", "transactions": [], - "uncles": [] + "uncles": [], } ); - let block: AnyRpcBlock = serde_json::from_value(json).unwrap(); + let block: ::BlockResponse = + 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")); - assert_eq!(Some("163591".to_string()), get_pretty_block_attr(&block, "difficulty")); + assert_eq!(None, get_pretty_block_attr::(&block, "")); + assert_eq!( + Some("7".to_string()), + get_pretty_block_attr::(&block, "baseFeePerGas") + ); + assert_eq!( + Some("163591".to_string()), + get_pretty_block_attr::(&block, "difficulty") + ); assert_eq!( Some("0x0000000000000000000000000000000000000000000000000000000000000000".to_string()), - get_pretty_block_attr(&block, "extraData") + get_pretty_block_attr::(&block, "extraData") + ); + assert_eq!( + Some("653145".to_string()), + get_pretty_block_attr::(&block, "gasLimit") + ); + assert_eq!( + Some("653145".to_string()), + get_pretty_block_attr::(&block, "gasUsed") ); - assert_eq!(Some("653145".to_string()), get_pretty_block_attr(&block, "gasLimit")); - assert_eq!(Some("653145".to_string()), get_pretty_block_attr(&block, "gasUsed")); assert_eq!( Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), - get_pretty_block_attr(&block, "hash") + get_pretty_block_attr::(&block, "hash") ); - assert_eq!(Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), get_pretty_block_attr(&block, "logsBloom")); + assert_eq!(Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), get_pretty_block_attr::(&block, "logsBloom")); assert_eq!( Some("0x0000000000000000000000000000000000000001".to_string()), - get_pretty_block_attr(&block, "miner") + get_pretty_block_attr::(&block, "miner") ); assert_eq!( Some("0x1010101010101010101010101010101010101010101010101010101010101010".to_string()), - get_pretty_block_attr(&block, "mixHash") + get_pretty_block_attr::(&block, "mixHash") + ); + assert_eq!( + Some("0x0000000000000000".to_string()), + get_pretty_block_attr::(&block, "nonce") + ); + assert_eq!( + Some("436".to_string()), + get_pretty_block_attr::(&block, "number") ); - assert_eq!(Some("0x0000000000000000".to_string()), get_pretty_block_attr(&block, "nonce")); - assert_eq!(Some("436".to_string()), get_pretty_block_attr(&block, "number")); assert_eq!( Some("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5".to_string()), - get_pretty_block_attr(&block, "parentHash") + get_pretty_block_attr::(&block, "parentHash") ); assert_eq!( Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".to_string()), - get_pretty_block_attr(&block, "transactionsRoot") + get_pretty_block_attr::(&block, "transactionsRoot") ); assert_eq!( Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".to_string()), - get_pretty_block_attr(&block, "receiptsRoot") + get_pretty_block_attr::(&block, "receiptsRoot") ); assert_eq!( Some("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".to_string()), - get_pretty_block_attr(&block, "sha3Uncles") + get_pretty_block_attr::(&block, "sha3Uncles") + ); + assert_eq!( + Some("163591".to_string()), + get_pretty_block_attr::(&block, "size") ); - assert_eq!(Some("163591".to_string()), get_pretty_block_attr(&block, "size")); assert_eq!( Some("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff".to_string()), - get_pretty_block_attr(&block, "stateRoot") + get_pretty_block_attr::(&block, "stateRoot") + ); + assert_eq!( + Some("1424182926".to_string()), + get_pretty_block_attr::(&block, "timestamp") + ); + assert_eq!( + Some("163591".to_string()), + get_pretty_block_attr::(&block, "totalDifficulty") ); - assert_eq!(Some("1424182926".to_string()), get_pretty_block_attr(&block, "timestamp")); - assert_eq!(Some("163591".to_string()), get_pretty_block_attr(&block, "totalDifficulty")); } #[test] @@ -1670,7 +1762,7 @@ l1GasUsed 1600 "gasPrice":"0x2540be400" }"#; - let tx: AnyRpcTransaction = serde_json::from_str(s).unwrap(); + let tx: Transaction = serde_json::from_str(s).unwrap(); assert_eq!( tx.pretty().trim(), @@ -1682,22 +1774,53 @@ transactionIndex 0 effectiveGasPrice 10000000000 hash 0x6d6d8c102064e6dee44abad2024a8b1d37959230baab80e70efbf9b0c739c4fd type 118 -aaAuthorizationList [] -accessList [] -calls [{"data":null,"input":"0x095ea7b3000000000000000000000000dec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000989680","to":"0x20c0000000000000000000000000000000000000","value":"0x0"},{"data":null,"input":"0xf8856c0f00000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020c00000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000989680000000000000000000000000000000000000000000000000000000000097d330","to":"0xdec0000000000000000000000000000000000000","value":"0x0"}] chainId 42429 -feePayerSignature null feeToken 0x20C0000000000000000000000000000000000001 -gas 184696 -gasPrice 10000000000 -keyAuthorization null -maxFeePerGas 12000000000 maxPriorityFeePerGas 0 -nonce 0 +maxFeePerGas 12000000000 +gasLimit 184696 +calls [ + to: 0x20C0000000000000000000000000000000000000, value: 0, input: 0x095ea7b3000000000000000000000000dec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000989680 + to: 0xDEc0000000000000000000000000000000000000, value: 0, input: 0xf8856c0f00000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020c00000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000989680000000000000000000000000000000000000000000000000000000000097d330 +] +accessList [] nonceKey 0 -signature {"pubKeyX":"0xaacc80b21e45fb11f349424dce3a2f23547f60c0ff2f8bcaede2a247545ce8dd","pubKeyY":"0x87abf0dbb7a5c9507efae2e43833356651b45ac576c2e61cec4e9c0f41fcbf6e","r":"0xcfd45c3b19745a42f80b134dcb02a8ba099a0e4e7be1984da54734aa81d8f29f","s":"0x74bb9170ae6d25bd510c83fe35895ee5712efe13980a5edc8094c534e23af85e","type":"webAuthn","webauthnData":"0x7b98b7a8e6c68d7eac741a52e6fdae0560ce3c16ef5427ad46d7a54d0ed86dd41d000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2238453071464a7a50585167546e645473643649456659457776323173516e626966374c4741776e4b43626b222c226f726967696e223a2268747470733a2f2f74656d706f2d6465782e76657263656c2e617070222c2263726f73734f726967696e223a66616c73657d"} -validAfter null -validBefore null +nonce 0 +feePayerSignature +validBefore +validAfter +tempoSignature {"type":"webAuthn","r":"0xcfd45c3b19745a42f80b134dcb02a8ba099a0e4e7be1984da54734aa81d8f29f","s":"0x74bb9170ae6d25bd510c83fe35895ee5712efe13980a5edc8094c534e23af85e","pubKeyX":"0xaacc80b21e45fb11f349424dce3a2f23547f60c0ff2f8bcaede2a247545ce8dd","pubKeyY":"0x87abf0dbb7a5c9507efae2e43833356651b45ac576c2e61cec4e9c0f41fcbf6e","webauthnData":"0x7b98b7a8e6c68d7eac741a52e6fdae0560ce3c16ef5427ad46d7a54d0ed86dd41d000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2238453071464a7a50585167546e645473643649456659457776323173516e626966374c4741776e4b43626b222c226f726967696e223a2268747470733a2f2f74656d706f2d6465782e76657263656c2e617070222c2263726f73734f726967696e223a66616c73657d"} +"# + .trim() + ); + } + + #[test] + fn can_pretty_print_tempo_receipt() { + let s = r#"{"type":"0x76","status":"0x1","cumulativeGasUsed":"0x176d7f4","logs":[{"address":"0x20c0000000000000000000000000000000000000","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x00000000000000000000000000000000000000000000000000000000000003e8","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb8","removed":false},{"address":"0x20c0000000000000000000000000000000000003","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x000000000000000000000000feec000000000000000000000000000000000000"],"data":"0x0000000000000000000000000000000000000000000000000000000000000417","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb9","removed":false}],"logsBloom":"0x00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000100000000000000000000000000000000040008000004200000000000000008000000000000000000040000000000000400000000000002000000000000000000000000000000000000000000000010000000000000000000000000000000000020000000000000800000000000000000000000000020000000000000000000000000000000000400000000000000000000000000000002000000000000000400000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","gasUsed":"0xcc6a","effectiveGasPrice":"0x4a817c802","from":"0xa70ab0448e66cd77995bfbba5c5b64b41a85f3fd","to":"0x20c0000000000000000000000000000000000000","contractAddress":null,"feeToken":"0x20c0000000000000000000000000000000000003","feePayer":"0xa70ab0448e66cd77995bfbba5c5b64b41a85f3fd"}"#; + + let tx: TempoTransactionReceipt = serde_json::from_str(s).unwrap(); + + assert_eq!( + tx.pretty().trim(), + r#" +blockHash 0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66 +blockNumber 6922711 +contractAddress +cumulativeGasUsed 24565748 +effectiveGasPrice 20000000002 +from 0xa70ab0448e66cD77995bfBBa5c5b64B41a85F3fd +gasUsed 52330 +logs [{"address":"0x20c0000000000000000000000000000000000000","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x00000000000000000000000000000000000000000000000000000000000003e8","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb8","removed":false},{"address":"0x20c0000000000000000000000000000000000003","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000a70ab0448e66cd77995bfbba5c5b64b41a85f3fd","0x000000000000000000000000feec000000000000000000000000000000000000"],"data":"0x0000000000000000000000000000000000000000000000000000000000000417","blockHash":"0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66","blockNumber":"0x69a1d7","blockTimestamp":"0x69a5d790","transactionHash":"0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f","transactionIndex":"0x63","logIndex":"0xb9","removed":false}] +logsBloom 0x00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000100000000000000000000000000000000040008000004200000000000000008000000000000000000040000000000000400000000000002000000000000000000000000000000000000000000000010000000000000000000000000000000000020000000000000800000000000000000000000000020000000000000000000000000000000000400000000000000000000000000000002000000000000000400000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000 +root +status true +transactionHash 0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f +transactionIndex 99 +type 118 +feePayer 0xa70ab0448e66cD77995bfBBa5c5b64B41a85F3fd +feeToken 0x20C0000000000000000000000000000000000003 +to 0x20C0000000000000000000000000000000000000 "# .trim() ); @@ -1732,4 +1855,58 @@ validBefore null // Verify status is pretty printed correctly (boolean true for successful transaction) assert!(pretty_output.contains("true")); } + + #[test] + fn test_get_pretty_receipt_attr() { + let receipt_json = serde_json::json!({ + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "transactionHash": "0x1234567890123456789012345678901234567890123456789012345678901234", + "transactionIndex": "0x0", + "blockHash": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd", + "blockNumber": "0x1", + "gasUsed": "0x5208", + "effectiveGasPrice": "0x3b9aca00", + "from": "0x1234567890123456789012345678901234567890", + "to": "0x0987654321098765432109876543210987654321", + "contractAddress": null + }); + + let receipt: ::ReceiptResponse = + serde_json::from_value(receipt_json).unwrap(); + + // Test basic receipt attributes + assert_eq!( + Some("0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd".to_string()), + get_pretty_receipt_attr::(&receipt, "blockHash") + ); + assert_eq!( + Some("1".to_string()), + get_pretty_receipt_attr::(&receipt, "blockNumber") + ); + assert_eq!( + Some("0x1234567890123456789012345678901234567890123456789012345678901234".to_string()), + get_pretty_receipt_attr::(&receipt, "transactionHash") + ); + assert_eq!( + Some("21000".to_string()), + get_pretty_receipt_attr::(&receipt, "gasUsed") + ); + assert_eq!( + Some("true".to_string()), + get_pretty_receipt_attr::(&receipt, "status") + ); + assert_eq!( + Some("2".to_string()), + get_pretty_receipt_attr::(&receipt, "type") + ); + assert_eq!( + Some("[]".to_string()), + get_pretty_receipt_attr::(&receipt, "logs") + ); + assert!(get_pretty_receipt_attr::(&receipt, "logsBloom").is_some()); + } } diff --git a/crates/common/src/comments/inline_config.rs b/crates/common/src/comments/inline_config.rs index 7be6dd215b720..16ffd6993bfef 100644 --- a/crates/common/src/comments/inline_config.rs +++ b/crates/common/src/comments/inline_config.rs @@ -205,6 +205,7 @@ impl InlineConfig { let comment_range = result.data; let src = file.src.as_str(); + #[allow(clippy::collapsible_match)] match item { InlineConfigItem::DisableNextItem(ids) => { if let Some(next_item) = find_next_item(span.hi()) { diff --git a/crates/common/src/comments/mod.rs b/crates/common/src/comments/mod.rs index 033146da63ad2..a3e4a8f3ec743 100644 --- a/crates/common/src/comments/mod.rs +++ b/crates/common/src/comments/mod.rs @@ -195,6 +195,7 @@ impl<'ast> CommentGatherer<'ast> { self.disabled_block_depth -= 1; } + #[allow(clippy::collapsible_match)] match token.kind { TokenKind::Whitespace => { if let Some(mut idx) = token_text.find('\n') { @@ -436,12 +437,10 @@ pub fn line_with_tabs( (num_tabs, num_spaces) = (num_tabs + 1, 0); } } - Some(Consolidation::WithoutSpaces) => { - if num_spaces != 0 { - (num_tabs, num_spaces) = (num_tabs + 1, 0); - } + Some(Consolidation::WithoutSpaces) if num_spaces != 0 => { + (num_tabs, num_spaces) = (num_tabs + 1, 0); } - None => (), + _ => (), }; // Append the normalized indentation and the rest of the line to the output diff --git a/crates/common/src/fs.rs b/crates/common/src/fs.rs index 4cf3358f24400..cb22287fb68b4 100644 --- a/crates/common/src/fs.rs +++ b/crates/common/src/fs.rs @@ -62,11 +62,7 @@ pub fn read_json_gzip_file(path: &Path) -> Result { /// Reads the entire contents of a locked shared file into a string. pub fn locked_read_to_string(path: impl AsRef) -> Result { let path = path.as_ref(); - let mut file = - fs::OpenOptions::new().read(true).open(path).map_err(|err| FsPathError::open(err, path))?; - file.lock_shared().map_err(|err| FsPathError::lock(err, path))?; - let contents = read_inner(path, &mut file)?; - file.unlock().map_err(|err| FsPathError::unlock(err, path))?; + let contents = locked_read(path)?; String::from_utf8(contents).map_err(|err| FsPathError::read(std::io::Error::other(err), path)) } diff --git a/crates/common/src/mapping_slots.rs b/crates/common/src/mapping_slots.rs index 2c2a0c6e2894a..fccf2f1bff927 100644 --- a/crates/common/src/mapping_slots.rs +++ b/crates/common/src/mapping_slots.rs @@ -47,18 +47,17 @@ impl MappingSlots { /// Function to be used in Inspector::step to record mapping slots and keys #[cold] pub fn step(mapping_slots: &mut AddressHashMap, interpreter: &Interpreter) { + #[allow(clippy::collapsible_match)] match interpreter.bytecode.opcode() { - opcode::KECCAK256 => { - if interpreter.stack.peek(1) == Ok(U256::from(0x40)) { - let address = interpreter.input.target_address; - let offset = interpreter.stack.peek(0).expect("stack size > 1").saturating_to(); - let data = interpreter.memory.slice_len(offset, 0x40); - let low = B256::from_slice(&data[..0x20]); - let high = B256::from_slice(&data[0x20..]); - let result = keccak256(&*data); + opcode::KECCAK256 if interpreter.stack.peek(1) == Ok(U256::from(0x40)) => { + let address = interpreter.input.target_address; + let offset = interpreter.stack.peek(0).expect("stack size > 1").saturating_to(); + let data = interpreter.memory.slice_len(offset, 0x40); + let low = B256::from_slice(&data[..0x20]); + let high = B256::from_slice(&data[0x20..]); + let result = keccak256(&*data); - mapping_slots.entry(address).or_default().seen_sha3.insert(result, (low, high)); - } + mapping_slots.entry(address).or_default().seen_sha3.insert(result, (low, high)); } opcode::SSTORE => { if let Some(mapping_slots) = mapping_slots.get_mut(&interpreter.input.target_address) diff --git a/crates/common/src/preprocessor/deps.rs b/crates/common/src/preprocessor/deps.rs index 6d60241a346e8..804801d98d8ba 100644 --- a/crates/common/src/preprocessor/deps.rs +++ b/crates/common/src/preprocessor/deps.rs @@ -182,6 +182,7 @@ impl<'gcx> Visit<'gcx> for BytecodeDependencyCollector<'gcx, '_> { } fn visit_expr(&mut self, expr: &'gcx Expr<'gcx>) -> ControlFlow { + #[allow(clippy::collapsible_match)] match &expr.kind { ExprKind::Call(call_expr, call_args, named_args) => { if let Some(dependency) = handle_call_expr( diff --git a/crates/common/src/preprocessor/mod.rs b/crates/common/src/preprocessor/mod.rs index bc27cca580eee..7499776cf2c72 100644 --- a/crates/common/src/preprocessor/mod.rs +++ b/crates/common/src/preprocessor/mod.rs @@ -46,7 +46,7 @@ impl Preprocessor for DynamicTestLinkingPreprocessor { ) -> Result<()> { // Skip if we are not preprocessing any tests or scripts. Avoids unnecessary AST parsing. if !input.input.sources.iter().any(|(path, _)| paths.is_test_or_script(path)) { - trace!("no tests or sources to preprocess"); + trace!("no tests or scripts to preprocess"); return Ok(()); } diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 66d8b5018bf2e..764a44db55baa 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -8,16 +8,19 @@ use crate::{ provider::{curl_transport::CurlTransport, runtime_transport::RuntimeTransportBuilder}, }; use alloy_chains::NamedChain; +use alloy_network::{Network, NetworkWallet}; use alloy_provider::{ Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, - fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, + fillers::{FillProvider, JoinFill, RecommendedFillers, WalletFiller}, network::{AnyNetwork, EthereumWallet}, }; use alloy_rpc_client::ClientBuilder; use alloy_transport::{layers::RetryBackoffLayer, utils::guess_local_url}; use eyre::{Result, WrapErr}; +use foundry_config::Config; use reqwest::Url; use std::{ + marker::PhantomData, net::SocketAddr, path::{Path, PathBuf}, str::FromStr, @@ -36,20 +39,8 @@ const POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR: f32 = 0.6; pub type RetryProvider = RootProvider; /// Helper type alias for a retry provider with a signer -pub type RetryProviderWithSigner = FillProvider< - JoinFill< - JoinFill< - Identity, - JoinFill< - GasFiller, - JoinFill< - alloy_provider::fillers::BlobGasFiller, - JoinFill, - >, - >, - >, - WalletFiller, - >, +pub type RetryProviderWithSigner = FillProvider< + JoinFill::RecommendedFillers>, WalletFiller>, RootProvider, N, >; @@ -84,8 +75,10 @@ pub fn try_get_http_provider(builder: impl AsRef) -> Result } /// Helper type to construct a `RetryProvider` +/// +/// This builder is generic over the network type `N`, defaulting to `AnyNetwork`. #[derive(Debug)] -pub struct ProviderBuilder { +pub struct ProviderBuilder { // Note: this is a result, so we can easily chain builder calls url: Result, chain: NamedChain, @@ -100,12 +93,16 @@ pub struct ProviderBuilder { is_local: bool, /// Whether to accept invalid certificates. accept_invalid_certs: bool, + /// Whether to disable automatic proxy detection. + no_proxy: bool, /// Whether to output curl commands instead of making requests. curl_mode: bool, + /// Phantom data for the network type. + _network: PhantomData, } -impl ProviderBuilder { - /// Creates a new builder instance +impl ProviderBuilder { + /// Creates a new ProviderBuilder helper instance. pub fn new(url_str: &str) -> Self { // a copy is needed for the next lines to work let mut url_str = url_str; @@ -152,8 +149,39 @@ impl ProviderBuilder { headers: vec![], is_local, accept_invalid_certs: false, + no_proxy: false, curl_mode: false, + _network: PhantomData, + } + } + + /// Constructs a [ProviderBuilder] instantiated using [Config] values. + /// + /// Defaults to `http://localhost:8545` and `Mainnet`. + pub fn from_config(config: &Config) -> Result { + let url = config.get_rpc_url_or_localhost_http()?; + let mut builder = Self::new(url.as_ref()); + + builder = builder.accept_invalid_certs(config.eth_rpc_accept_invalid_certs); + builder = builder.curl_mode(config.eth_rpc_curl); + + if let Ok(chain) = config.chain.unwrap_or_default().try_into() { + builder = builder.chain(chain); + } + + if let Some(jwt) = config.get_rpc_jwt_secret()? { + builder = builder.jwt(jwt.as_ref()); + } + + if let Some(rpc_timeout) = config.eth_rpc_timeout { + builder = builder.timeout(Duration::from_secs(rpc_timeout)); + } + + if let Some(rpc_headers) = config.eth_rpc_headers.clone() { + builder = builder.headers(rpc_headers); } + + Ok(builder) } /// Enables a request timeout. @@ -256,6 +284,15 @@ impl ProviderBuilder { self } + /// Sets whether to disable automatic proxy detection. + /// + /// This can help in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) + /// where system proxy detection via SCDynamicStore causes crashes. + pub fn no_proxy(mut self, no_proxy: bool) -> Self { + self.no_proxy = no_proxy; + self + } + /// Sets whether to output curl commands instead of making requests. /// /// When enabled, the provider will print equivalent curl commands to stdout @@ -266,7 +303,7 @@ impl ProviderBuilder { } /// Constructs the `RetryProvider` taking all configs into account. - pub fn build(self) -> Result { + pub fn build(self) -> Result> { let Self { url, chain, @@ -278,7 +315,9 @@ impl ProviderBuilder { headers, is_local, accept_invalid_certs, + no_proxy, curl_mode, + .. } = self; let url = url?; @@ -290,7 +329,7 @@ impl ProviderBuilder { let transport = CurlTransport::new(url).with_headers(headers).with_jwt(jwt); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + let provider = AlloyProviderBuilder::<_, _, N>::default() .connect_provider(RootProvider::new(client)); return Ok(provider); @@ -301,6 +340,7 @@ impl ProviderBuilder { .with_headers(headers) .with_jwt(jwt) .accept_invalid_certs(accept_invalid_certs) + .no_proxy(no_proxy) .build(); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); @@ -316,14 +356,22 @@ impl ProviderBuilder { ); } - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() - .connect_provider(RootProvider::new(client)); + let provider = + AlloyProviderBuilder::<_, _, N>::default().connect_provider(RootProvider::new(client)); Ok(provider) } +} +impl ProviderBuilder { /// Constructs the `RetryProvider` with a wallet. - pub fn build_with_wallet(self, wallet: EthereumWallet) -> Result { + pub fn build_with_wallet + Clone>( + self, + wallet: W, + ) -> Result> + where + N: RecommendedFillers, + { let Self { url, chain, @@ -335,7 +383,9 @@ impl ProviderBuilder { headers, is_local, accept_invalid_certs, + no_proxy, curl_mode, + .. } = self; let url = url?; @@ -347,7 +397,7 @@ impl ProviderBuilder { let transport = CurlTransport::new(url).with_headers(headers).with_jwt(jwt); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + let provider = AlloyProviderBuilder::<_, _, N>::default() .with_recommended_fillers() .wallet(wallet) .connect_provider(RootProvider::new(client)); @@ -360,6 +410,7 @@ impl ProviderBuilder { .with_headers(headers) .with_jwt(jwt) .accept_invalid_certs(accept_invalid_certs) + .no_proxy(no_proxy) .build(); let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); @@ -376,7 +427,7 @@ impl ProviderBuilder { ); } - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + let provider = AlloyProviderBuilder::<_, _, N>::default() .with_recommended_fillers() .wallet(wallet) .connect_provider(RootProvider::new(client)); @@ -401,7 +452,11 @@ fn resolve_path(path: &Path) -> Result { { return Ok(path.to_path_buf()); } - Err(()) + if path.is_absolute() { + Ok(path.to_path_buf()) + } else { + std::env::current_dir().map(|d| d.join(path)).map_err(drop) + } } #[cfg(test)] @@ -410,7 +465,7 @@ mod tests { #[test] fn can_auto_correct_missing_prefix() { - let builder = ProviderBuilder::new("localhost:8545"); + let builder = ProviderBuilder::::new("localhost:8545"); assert!(builder.url.is_ok()); let url = builder.url.unwrap(); diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 6c175411640d8..b5962c9a72f63 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -80,6 +80,8 @@ pub struct RuntimeTransport { timeout: std::time::Duration, /// Whether to accept invalid certificates. accept_invalid_certs: bool, + /// Whether to disable automatic proxy detection. + no_proxy: bool, } /// A builder for [RuntimeTransport]. @@ -90,6 +92,7 @@ pub struct RuntimeTransportBuilder { jwt: Option, timeout: std::time::Duration, accept_invalid_certs: bool, + no_proxy: bool, } impl RuntimeTransportBuilder { @@ -101,6 +104,7 @@ impl RuntimeTransportBuilder { jwt: None, timeout: REQUEST_TIMEOUT, accept_invalid_certs: false, + no_proxy: false, } } @@ -128,6 +132,15 @@ impl RuntimeTransportBuilder { self } + /// Set whether to disable automatic proxy detection. + /// + /// This can help in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) + /// where system proxy detection via SCDynamicStore causes crashes. + pub fn no_proxy(mut self, no_proxy: bool) -> Self { + self.no_proxy = no_proxy; + self + } + /// Builds the [RuntimeTransport] and returns it in a disconnected state. /// The runtime transport will then connect when the first request happens. pub fn build(self) -> RuntimeTransport { @@ -138,6 +151,7 @@ impl RuntimeTransportBuilder { jwt: self.jwt, timeout: self.timeout, accept_invalid_certs: self.accept_invalid_certs, + no_proxy: self.no_proxy, } } } @@ -165,6 +179,14 @@ impl RuntimeTransport { .timeout(self.timeout) .tls_built_in_root_certs(self.url.scheme() == "https") .danger_accept_invalid_certs(self.accept_invalid_certs); + + // Disable automatic proxy detection if requested. This helps in sandboxed environments + // (e.g., Cursor IDE sandbox, macOS App Sandbox) where system proxy detection via + // SCDynamicStore causes crashes. See: https://github.com/foundry-rs/foundry/issues/12733 + if self.no_proxy { + client_builder = client_builder.no_proxy(); + } + 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/common/src/selectors.rs b/crates/common/src/selectors.rs index 97ce7a9416386..27bb2d7ef4369 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -535,6 +535,7 @@ pub fn parse_signatures(tokens: Vec) -> ParsedSignatures { RawSelectorImportData::default(), |mut data, signature| { let mut split = signature.split(' '); + #[allow(clippy::collapsible_match)] match split.next() { Some("function") => { if let Some(sig) = split.next() { diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 77d860a80038d..6fe1672d55000 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -1,7 +1,7 @@ //! terminal utils use foundry_compilers::{ artifacts::remappings::Remapping, - report::{self, BasicStdoutReporter, Reporter}, + report::{self, Reporter}, }; use itertools::Itertools; use semver::Version; @@ -209,19 +209,6 @@ impl Reporter for SpinnerReporter { } } -/// If the output medium is terminal, this calls `f` within the [`SpinnerReporter`] that displays a -/// spinning cursor to display solc progress. -/// -/// If no terminal is available this falls back to common `println!` in [`BasicStdoutReporter`]. -pub fn with_spinner_reporter(project_root: Option, f: impl FnOnce() -> T) -> T { - let reporter = if TERM_SETTINGS.indicate_progress { - report::Report::new(SpinnerReporter::spawn(project_root)) - } else { - report::Report::new(BasicStdoutReporter::default()) - }; - report::with_scoped(&reporter, f) -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/common/src/traits.rs b/crates/common/src/traits.rs index 3da6ff9847837..2f3540c453595 100644 --- a/crates/common/src/traits.rs +++ b/crates/common/src/traits.rs @@ -175,7 +175,7 @@ impl TestFunctionKind { Self::InvariantTest } _ if name.starts_with("table") => Self::TableTest, - _ if name.eq_ignore_ascii_case("setup") => Self::Setup, + _ if name.eq_ignore_ascii_case("setup") && !has_inputs => Self::Setup, _ if name.eq_ignore_ascii_case("afterinvariant") => Self::AfterInvariant, _ if name.starts_with("fixture") => Self::Fixture, _ => Self::Unknown, @@ -285,3 +285,18 @@ impl ErrorExt for T { alloy_sol_types::Revert::from(self.to_string()).abi_encode().into() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_setup_classification() { + // setUp() with no params should be classified as Setup + assert_eq!(TestFunctionKind::classify("setUp", false), TestFunctionKind::Setup); + + // setUp(bytes memory) with params should NOT be classified as Setup + // This is common in Gnosis Safe/Zodiac modules + assert_eq!(TestFunctionKind::classify("setUp", true), TestFunctionKind::Unknown); + } +} diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index b002a94e5fd41..555799326b798 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -2,7 +2,7 @@ use alloy_consensus::{Transaction, TxEnvelope, transaction::SignerRecoverable}; use alloy_eips::eip7702::SignedAuthorization; -use alloy_network::AnyTransactionReceipt; +use alloy_network::{AnyTransactionReceipt, Network, TransactionResponse}; use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_provider::{ Provider, @@ -11,55 +11,48 @@ use alloy_provider::{ use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; use eyre::Result; -use foundry_common_fmt::UIfmt; +use foundry_common_fmt::{UIfmt, UIfmtReceiptExt, get_pretty_receipt_attr}; use serde::{Deserialize, Serialize}; /// Helper type to carry a transaction along with an optional revert reason #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct TransactionReceiptWithRevertReason { +pub struct TransactionReceiptWithRevertReason { /// The underlying transaction receipt #[serde(flatten)] - pub receipt: AnyTransactionReceipt, + pub receipt: N::ReceiptResponse, /// The revert reason string if the transaction status is failed #[serde(skip_serializing_if = "Option::is_none", rename = "revertReason")] pub revert_reason: Option, } -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.coerce_status() - } - +impl TransactionReceiptWithRevertReason +where + N::TxEnvelope: Clone, + N::ReceiptResponse: UIfmtReceiptExt, +{ /// 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: &P, - ) -> 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: &P, - ) -> Result> { - if !self.is_failure() { + async fn fetch_revert_reason>(&self, provider: &P) -> Result> { + // If the transaction succeeded, there is no revert reason to fetch + if self.receipt.status() { return Ok(None); } let transaction = provider - .get_transaction_by_hash(self.receipt.transaction_hash) + .get_transaction_by_hash(self.receipt.transaction_hash()) .await .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 { - let mut call_request: WithOtherFields = - transaction.inner.inner.clone_inner().into(); - call_request.set_from(transaction.inner.inner.signer()); + if let Some(block_hash) = self.receipt.block_hash() { + let mut call_request: N::TransactionRequest = transaction.as_ref().clone().into(); + call_request.set_from(transaction.from()); match provider.call(call_request).block(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"), @@ -69,19 +62,22 @@ impl TransactionReceiptWithRevertReason { } } -impl From for TransactionReceiptWithRevertReason { +impl From for TransactionReceiptWithRevertReason { fn from(receipt: AnyTransactionReceipt) -> Self { Self { receipt, revert_reason: None } } } -impl From for AnyTransactionReceipt { - fn from(receipt_with_reason: TransactionReceiptWithRevertReason) -> Self { +impl From> for AnyTransactionReceipt { + fn from(receipt_with_reason: TransactionReceiptWithRevertReason) -> Self { receipt_with_reason.receipt } } -impl UIfmt for TransactionReceiptWithRevertReason { +impl UIfmt for TransactionReceiptWithRevertReason +where + N::ReceiptResponse: UIfmt, +{ fn pretty(&self) -> String { if let Some(revert_reason) = &self.revert_reason { format!( @@ -143,36 +139,20 @@ fn extract_revert_reason>(error_string: S) -> Option { .map(|index| error_string.as_ref().split_at(index + message_substr.len()).1.to_string()) } -/// Returns the `UiFmt::pretty()` formatted attribute of the transaction receipt -pub fn get_pretty_tx_receipt_attr( - receipt: &TransactionReceiptWithRevertReason, +/// Returns the `UiFmt::pretty()` formatted attribute of the transaction receipt with revert reason +pub fn get_pretty_receipt_w_reason_attr( + receipt: &TransactionReceiptWithRevertReason, attr: &str, -) -> Option { - match attr { - "blockHash" | "block_hash" => Some(receipt.receipt.block_hash.pretty()), - "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.inner.inner.inner.receipt.cumulative_gas_used.pretty()) - } - "effectiveGasPrice" | "effective_gas_price" => { - 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()) - } - "transactionHash" | "transaction_hash" => Some(receipt.receipt.transaction_hash.pretty()), - "transactionIndex" | "transaction_index" => { - 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()), - _ => None, +) -> Option +where + N: Network, + N::ReceiptResponse: UIfmtReceiptExt, +{ + // Handle revert reason first, then delegate to the receipt formatting function + if matches!(attr, "revertReason" | "revert_reason") { + return Some(receipt.revert_reason.pretty()); } + get_pretty_receipt_attr::(&receipt.receipt, attr) } /// Used for broadcasting transactions diff --git a/crates/config/README.md b/crates/config/README.md index b0f210edea79b..ac742cc05db01 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -54,327 +54,15 @@ and merge, at the per-key level: The selected profile is the value of the `FOUNDRY_PROFILE` environment variable, or if it is not set, "default". -### All Options +## Configuration Reference -The following is a foundry.toml file with all configuration options set. See also [/config/src/lib.rs](./src/lib.rs) and [/cli/tests/it/config.rs](../forge/tests/it/config.rs). +For a full list of all configuration options, see the [Foundry Book](https://getfoundry.sh/config/reference/overview): -```toml -## defaults for _all_ profiles -[profile.default] -src = 'src' -test = 'test' -script = 'script' -out = 'out' -libs = ['lib'] -auto_detect_remappings = true # recursive auto-detection of remappings -remappings = [] -# list of libraries to link in the form of `::
`: `"src/MyLib.sol:MyLib:0x8De6DDbCd5053d32292AAA0D2105A32d108484a6"` -# the supports remappings -libraries = [] -cache = true -cache_path = 'cache' -broadcast = 'broadcast' -# additional solc allow paths -allow_paths = [] -# additional solc include paths -include_paths = [] -force = false -evm_version = 'prague' -gas_reports = ['*'] -gas_reports_ignore = [] -## Sets the concrete solc version to use, this overrides the `auto_detect_solc` value -# solc = '0.8.10' -auto_detect_solc = true -offline = false -optimizer = false -optimizer_runs = 200 -model_checker = { contracts = { 'a.sol' = [ - 'A1', - 'A2', -], 'b.sol' = [ - 'B1', - 'B2', -] }, engine = 'chc', targets = [ - 'assert', - 'outOfBounds', -], timeout = 10000 } -verbosity = 0 -eth_rpc_url = "https://example.com/" -# Setting this option enables decoding of error traces from mainnet deployed / verified 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", "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"] -# Deny compiler warnings and/or notes with: -# - "never": default behavior, no denial -# - "warnings": warnings treated as errors -# - "notes": notes and warnings treated as notes -deny = "never" -match_test = "Foo" -no_match_test = "Bar" -match_contract = "Foo" -no_match_contract = "Bar" -match_path = "*/Foo*" -no_match_path = "*/Bar*" -no_match_coverage = "Baz" -# Number of threads to use. Specifying 0 defaults to the number of logical cores. -threads = 0 -# whether to show test execution progress -show_progress = true -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' -initial_balance = '0xffffffffffffffffffffffff' -block_number = 0 -fork_block_number = 0 -chain_id = 1 -# NOTE due to a toml-rs limitation, this value needs to be a string if the desired gas limit exceeds 2**63-1 (9223372036854775807). -# `gas_limit = "max"` is equivalent to `gas_limit = "18446744073709551615"`. This is not recommended -# as it will make infinite loops effectively hang during execution. -gas_limit = 1073741824 -gas_price = 0 -block_base_fee_per_gas = 0 -block_coinbase = '0x0000000000000000000000000000000000000000' -block_timestamp = 0 -block_difficulty = 0 -block_prevrandao = '0x0000000000000000000000000000000000000000' -block_gas_limit = 30000000 -memory_limit = 134217728 -extra_output = ["metadata"] -extra_output_files = [] -names = false -sizes = false -via_ir = false -ast = false -# caches storage retrieved locally for certain chains and endpoints -# can also be restricted to `chains = ["optimism", "mainnet"]` -# by default all endpoints will be cached, alternative options are "remote" for only caching non localhost endpoints and "" -# to disable storage caching entirely set `no_storage_caching = true` -rpc_storage_caching = { chains = "all", endpoints = "all" } -# this overrides `rpc_storage_caching` entirely -no_storage_caching = false -# Whether to store the referenced sources in the metadata as literal data. -use_literal_content = false -# use ipfs method to generate the metadata hash, solc's default. -# To not include the metadata hash, to allow for deterministic code: https://docs.soliditylang.org/en/latest/metadata.html, use "none" -bytecode_hash = "ipfs" -# Whether to append the CBOR-encoded metadata file. -cbor_metadata = true -# How to treat revert (and require) reason strings. -# Possible values are: "default", "strip", "debug" and "verboseDebug". -# "default" does not inject compiler-generated revert strings and keeps user-supplied ones. -# "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects -# "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now. -# "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented) -revert_strings = "default" -# If this option is enabled, Solc is instructed to generate output (bytecode) only for the required contracts -# this can reduce compile time for `forge test` a bit but is considered experimental at this point. -sparse_mode = false -build_info = true -build_info_path = "build-info" -root = "root" -# Configures permissions for cheatcodes that touch the filesystem like `vm.writeFile` -# `access` restricts how the `path` can be accessed via cheatcodes -# `read-write` | `true` => `read` + `write` access allowed (`vm.readFile` + `vm.writeFile`) -# `none`| `false` => no access -# `read` => only read access (`vm.readFile`) -# `write` => only write access (`vm.writeFile`) -# The `allowed_paths` further lists the paths that are considered, e.g. `./` represents the project root directory -# By default, only read access is granted to the project's out dir, so generated artifacts can be read by default -# following example enables read-write access for the project dir : -# `fs_permissions = [{ access = "read-write", path = "./"}]` -fs_permissions = [{ access = "read", path = "./out"}] -# whether failed assertions should revert -# note that this only applies to native (cheatcode) assertions, invoked on Vm contract -assertions_revert = true -# whether `failed()` should be invoked to check if the test have failed -legacy_assertions = false - -[fuzz] -runs = 256 -max_test_rejects = 65536 -seed = '0x3e8' -# Fails the fuzz test if a revert occurs -fail_on_revert = true -# Number of runs to execute and include in the gas report -gas_report_samples = 256 -# Show `console.log` in fuzz test -show_logs = false -# Optional timeout (in seconds) for each property test -# timeout = 60 -# Path where fuzz failures are recorded and replayed -# failure_persist_dir = 'cache/fuzz' - -# Fuzz dictionary configuration -dictionary_weight = 40 -include_storage = true -include_push_bytes = true -# Maximum addresses to record for the fuzz dictionary -max_fuzz_dictionary_addresses = 15728640 -# Maximum values to record for the fuzz dictionary -max_fuzz_dictionary_values = 9830400 -# Maximum literals to seed from the AST for the fuzz dictionary -max_fuzz_dictionary_literals = 6553600 - -# Fuzz corpus configuration (for coverage-guided fuzzing) -# Path to corpus directory, enables coverage guided fuzzing mode -# corpus_dir = 'corpus' -# Whether corpus uses gzip compression -corpus_gzip = true -# Number of mutations until entry marked as eligible to be flushed from in-memory corpus -corpus_min_mutations = 5 -# Minimum corpus size that won't be evicted from memory -corpus_min_size = 0 -# Whether to collect and display edge coverage metrics -show_edge_coverage = false - -[invariant] -runs = 256 -depth = 500 -fail_on_revert = false -call_override = false -# The maximum number of attempts to shrink the sequence -shrink_run_limit = 5000 -# The maximum number of rejects via `vm.assume` which can be encountered during a single invariant run -max_assume_rejects = 65536 -# Number of runs to execute and include in the gas report -gas_report_samples = 256 -# Whether to collect and display fuzzed selectors metrics -show_metrics = true -# Optional timeout (in seconds) for each invariant test -# timeout = 60 -# Display counterexample as solidity calls -show_solidity = false -# Maximum time (in seconds) between generated txs -# max_time_delay = 86400 -# Maximum number of blocks elapsed between generated txs -# max_block_delay = 10000 -# Path where invariant failures are recorded and replayed -# failure_persist_dir = 'cache/invariant' - -# Invariant dictionary configuration -dictionary_weight = 80 -include_storage = true -include_push_bytes = true -# Maximum addresses to record for the fuzz dictionary -max_fuzz_dictionary_addresses = 15728640 -# Maximum values to record for the fuzz dictionary -max_fuzz_dictionary_values = 9830400 -# Maximum literals to seed from the AST for the fuzz dictionary -max_fuzz_dictionary_literals = 6553600 - -# Invariant corpus configuration (for coverage-guided fuzzing) -# Path to corpus directory, enables coverage guided fuzzing mode -# corpus_dir = 'corpus' -# Whether corpus uses gzip compression -corpus_gzip = true -# Number of mutations until entry marked as eligible to be flushed from in-memory corpus -corpus_min_mutations = 5 -# Minimum corpus size that won't be evicted from memory -corpus_min_size = 0 -# Whether to collect and display edge coverage metrics -show_edge_coverage = false - -[fmt] -line_length = 100 -tab_width = 2 -bracket_spacing = true -``` - -#### Additional Optimizer settings - -Optimizer components can be tweaked with the `OptimizerDetails` object: - -See [Compiler Input Description `settings.optimizer.details`](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) - -The `optimizer_details` (`optimizerDetails` also works) settings must be prefixed with the profile they correspond -to: `[profile.default.optimizer_details]` -belongs to the `[profile.default]` profile - -```toml -[profile.default.optimizer_details] -constantOptimizer = true -yul = true -# this sets the `yulDetails` of the `optimizer_details` for the `default` profile -[profile.default.optimizer_details.yulDetails] -stackAllocation = true -optimizerSteps = 'dhfoDgvulfnTUtnIf' -``` - -#### RPC-Endpoints settings - -The `rpc_endpoints` value accepts a list of `alias = ""` pairs. - -The following example declares two pairs: -The alias `optimism` references the endpoint URL directly. -The alias `mainnet` references the environment variable `RPC_MAINNET` which holds the entire URL. -The alias `goerli` references an endpoint that will be interpolated with the value the `GOERLI_API_KEY` holds. - -Environment variables need to be wrapped in `${}` - -```toml -[rpc_endpoints] -optimism = "https://optimism.alchemyapi.io/v2/1234567" -mainnet = "${RPC_MAINNET}" -goerli = "https://eth-goerli.alchemyapi.io/v2/${GOERLI_API_KEY}" -``` - -#### Etherscan API Key settings - -The `etherscan` value accepts a list of `alias = "{key = "", url? ="", chain?= """""}"` items. - -the `key` attribute is always required and should contain the actual API key for that chain or an env var that holds the key in the form `${ENV_VAR}` -The `chain` attribute is optional if the `alias` is the already the `chain` name, such as in `mainnet = { key = "${ETHERSCAN_MAINNET_KEY}"}` -The optional `url` attribute can be used to explicitly set the Etherscan API url, this is the recommended setting for chains not natively supported by name. - -```toml -[etherscan] -mainnet = { key = "${ETHERSCAN_MAINNET_KEY}" } -mainnet2 = { key = "ABCDEFG", chain = "mainnet" } -optimism = { key = "1234576", chain = 42 } -unknownchain = { key = "ABCDEFG", url = "https://" } -``` - -##### Additional Model Checker settings - -[Solidity's built-in model checker](https://docs.soliditylang.org/en/latest/smtchecker.html#tutorial) -is an opt-in module that can be enabled via the `ModelChecker` object. - -See [Compiler Input Description `settings.modelChecker`](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) -and [the model checker's options](https://docs.soliditylang.org/en/latest/smtchecker.html#smtchecker-options-and-tuning). - -The module is available in `solc` release binaries for OSX and Linux. -The latter requires the z3 library version [4.8.8, 4.8.14] to be installed -in the system (SO version 4.8). - -Similarly to the optimizer settings above, the `model_checker` settings must be -prefixed with the profile they correspond to: `[profile.default.model_checker]` belongs -to the `[profile.default]` profile. - -```toml -[profile.default.model_checker] -contracts = { 'src/Contract.sol' = [ 'Contract' ] } -engine = 'chc' -timeout = 10000 -targets = [ 'assert' ] -``` - -The fields above are recommended when using the model checker. -Setting which contract should be verified is extremely important, otherwise all -available contracts will be verified which can consume a lot of time. -The recommended engine is `chc`, but `bmc` and `all` (runs both) are also -accepted. -It is also important to set a proper timeout (given in milliseconds), since the -default time given to the underlying solvers may not be enough. -If no verification targets are given, only assertions will be checked. - -The model checker will run when `forge build` is invoked, and will show -findings as warnings if any. +- [Default Configuration](https://getfoundry.sh/config/reference/default-config) - All `[profile.default]` options +- [Testing Configuration](https://getfoundry.sh/config/reference/testing) - Fuzz and invariant testing options +- [Formatter Configuration](https://getfoundry.sh/config/reference/formatter) - Code formatting options +- [Linter Configuration](https://getfoundry.sh/config/reference/linter) - Linting options +- [Doc Generator Configuration](https://getfoundry.sh/config/reference/doc-generator) - Documentation generation options ## Environment Variables @@ -386,4 +74,3 @@ supported, this means that `FOUNDRY_SRC` and `DAPP_SRC` are equivalent. Some exceptions to the above are [explicitly ignored](https://github.com/foundry-rs/foundry/blob/10440422e63aae660104e079dfccd5b0ae5fd720/config/src/lib.rs#L1539-L15522) due to security concerns. Environment variables take precedence over values in `foundry.toml`. Values are parsed as a loose form of TOML syntax. -Consider the following examples: diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index 0901842a00bff..af25127beb585 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -63,22 +63,6 @@ pub enum RpcEndpointType { } impl RpcEndpointType { - /// Returns the string variant - pub fn as_endpoint_string(&self) -> Option<&RpcEndpointUrl> { - match self { - Self::String(url) => Some(url), - Self::Config(_) => None, - } - } - - /// Returns the config variant - pub fn as_endpoint_config(&self) -> Option<&RpcEndpoint> { - match self { - Self::Config(config) => Some(config), - Self::String(_) => None, - } - } - /// Returns the url or config this type holds /// /// # Error @@ -137,14 +121,6 @@ impl RpcEndpointUrl { } } - /// Returns the env variant - pub fn as_env(&self) -> Option<&str> { - match self { - Self::Env(val) => Some(val), - Self::Url(_) => None, - } - } - /// Returns the url this type holds /// /// # Error diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 67cc8d7e04762..4f9eddc5e3ad6 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -140,6 +140,10 @@ pub enum SolidityErrorCode { TransientStorageUsed, /// There are more than 256 warnings. Ignoring the rest. TooManyWarnings, + /// Warning: 'transfer' is deprecated and scheduled for removal. + TransferDeprecated, + /// Warning: Natspec memory-safe-assembly special comment for inline assembly is deprecated. + NatspecMemorySafeAssemblyDeprecated, /// All other error codes Other(u64), } @@ -167,6 +171,8 @@ impl SolidityErrorCode { Self::PragmaSolidity => "pragma-solidity", Self::TransientStorageUsed => "transient-storage", Self::TooManyWarnings => "too-many-warnings", + Self::TransferDeprecated => "transfer-deprecated", + Self::NatspecMemorySafeAssemblyDeprecated => "natspec-memory-safe-assembly-deprecated", Self::Other(code) => return Err(*code), }; Ok(s) @@ -193,6 +199,8 @@ impl From for u64 { SolidityErrorCode::PragmaSolidity => 3420, SolidityErrorCode::TransientStorageUsed => 2394, SolidityErrorCode::TooManyWarnings => 4591, + SolidityErrorCode::TransferDeprecated => 9207, + SolidityErrorCode::NatspecMemorySafeAssemblyDeprecated => 2424, SolidityErrorCode::Other(code) => code, } } @@ -229,6 +237,8 @@ impl FromStr for SolidityErrorCode { "pragma-solidity" => Self::PragmaSolidity, "transient-storage" => Self::TransientStorageUsed, "too-many-warnings" => Self::TooManyWarnings, + "transfer-deprecated" => Self::TransferDeprecated, + "natspec-memory-safe-assembly-deprecated" => Self::NatspecMemorySafeAssemblyDeprecated, _ => return Err(format!("Unknown variant {s}")), }; @@ -256,6 +266,8 @@ impl From for SolidityErrorCode { 3420 => Self::PragmaSolidity, 2394 => Self::TransientStorageUsed, 4591 => Self::TooManyWarnings, + 9207 => Self::TransferDeprecated, + 2424 => Self::NatspecMemorySafeAssemblyDeprecated, other => Self::Other(other), } } diff --git a/crates/config/src/etherscan.rs b/crates/config/src/etherscan.rs index 5999825e94681..7fb68deb7accc 100644 --- a/crates/config/src/etherscan.rs +++ b/crates/config/src/etherscan.rs @@ -319,10 +319,17 @@ impl ResolvedEtherscanConfig { .with_client(client) .with_api_key(api_key) .with_cache(cache, Duration::from_secs(24 * 60 * 60)); - if let Some(browser_url) = browser_url { + if let Some(ref browser_url) = browser_url { client_builder = client_builder.with_url(browser_url)?; } - client_builder.chain(chain)?.build() + + // Use the provided URL (either custom from foundry.toml or chain's default from resolve()) + client_builder = client_builder.with_api_url(api_url.clone())?; + // Fallback: Use api_url as browser URL if browser_url is not set + if browser_url.is_none() { + client_builder = client_builder.with_url(api_url)?; + } + client_builder.build() } } @@ -343,22 +350,6 @@ pub enum EtherscanApiKey { } impl EtherscanApiKey { - /// Returns the key variant - pub fn as_key(&self) -> Option<&str> { - match self { - Self::Key(url) => Some(url), - Self::Env(_) => None, - } - } - - /// Returns the env variant - pub fn as_env(&self) -> Option<&str> { - match self { - Self::Env(val) => Some(val), - Self::Key(_) => None, - } - } - /// Returns the key this type holds /// /// # Error @@ -478,10 +469,8 @@ mod tests { let config = resolved.remove("mainnet").unwrap().unwrap(); assert_eq!(config.key, "ABCDEFG"); let client = config.into_client().unwrap(); - assert_eq!( - client.etherscan_api_url().as_str(), - "https://api.etherscan.io/v2/api?chainid=1" - ); + // Custom URL should be used even when chain has a default URL + assert_eq!(client.etherscan_api_url().as_str(), "https://api.etherscan.io/api"); unsafe { std::env::remove_var(env); @@ -518,4 +507,50 @@ mod tests { let resolved = config.resolve(Some("base-sepolia")).unwrap(); assert_eq!(resolved.chain, Some(Chain::base_sepolia())); } + + #[test] + fn can_create_client_with_custom_url_for_chain_without_default_url() { + // Chains without default Etherscan URLs (e.g., Dev, AnvilHardhat networks) + // should work if a custom URL is provided in foundry.toml. + let mut configs = EtherscanConfigs::default(); + configs.insert( + "dev".to_string(), + EtherscanConfig { + chain: Some(Chain::dev()), + url: Some("https://custom.api.url/verify/etherscan".to_string()), + key: EtherscanApiKey::Key("test_key".to_string()), + }, + ); + + let mut resolved = configs.resolved(); + let config = resolved.remove("dev").unwrap().unwrap(); + let result = config.into_client(); + assert!( + result.is_ok(), + "Should succeed with custom URL even for chains without default Etherscan URLs" + ); + } + + #[test] + fn fails_without_custom_url_for_chain_without_default_url() { + // Chains without default Etherscan URLs (e.g., Dev, AnvilHardhat networks) + // should fail if no custom URL is provided in foundry.toml. + let mut configs = EtherscanConfigs::default(); + configs.insert( + "dev".to_string(), + EtherscanConfig { + chain: Some(Chain::dev()), + url: None, + key: EtherscanApiKey::Key("test_key".to_string()), + }, + ); + + let mut resolved = configs.resolved(); + let config = resolved.remove("dev").unwrap(); + + assert!( + config.is_err(), + "Should fail: chains without default Etherscan URLs require custom URL" + ); + } } diff --git a/crates/config/src/fmt.rs b/crates/config/src/fmt.rs index 673b692fbe4fd..bea82b9a45449 100644 --- a/crates/config/src/fmt.rs +++ b/crates/config/src/fmt.rs @@ -37,6 +37,8 @@ pub struct FormatterConfig { pub contract_new_lines: bool, /// Sort import statements alphabetically in groups (a group is separated by a newline). pub sort_imports: bool, + /// Choose between `import "a" as name` and `import * as name from "a"` + pub namespace_import_style: NamespaceImportStyle, /// Whether to suppress spaces around the power operator (`**`). pub pow_no_space: bool, /// Style that determines if a broken list, should keep its elements together on their own @@ -189,6 +191,18 @@ impl MultilineFuncHeaderStyle { } } +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum NamespaceImportStyle { + /// prefer plain imports: `import "source" as name;` + #[default] + PreferPlain, + /// prefer glob imports: `import * as name from "source";` + PreferGlob, + /// preserve the original style + Preserve, +} + /// Style that determines if a broken list, should keep its elements together on their own line, /// before breaking individually. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -250,6 +264,7 @@ impl Default for FormatterConfig { ignore: vec![], contract_new_lines: false, sort_imports: false, + namespace_import_style: NamespaceImportStyle::default(), pow_no_space: false, prefer_compact: PreferCompact::default(), docs_style: DocCommentStyle::default(), diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index 9f2ba141897a8..cb964f97f68be 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -12,11 +12,8 @@ pub struct FuzzConfig { pub runs: u32, /// Fails the fuzzed test if a revert occurs. pub fail_on_revert: bool, - /// The maximum number of test case rejections allowed by proptest, to be - /// encountered during usage of `vm.assume` cheatcode. This will be used - /// to set the `max_global_rejects` value in proptest test runner config. - /// `max_local_rejects` option isn't exposed here since we're not using - /// `prop_filter`. + /// The maximum number of test case rejections allowed, + /// encountered during usage of `vm.assume` cheatcode. pub max_test_rejects: u32, /// Optional seed for the fuzzing RNG algorithm pub seed: Option, diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 4a34a894b6d2e..591af88efdee4 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -41,6 +41,14 @@ pub struct InvariantConfig { pub max_time_delay: Option, /// Maximum number of blocks elapsed between generated txs. pub max_block_delay: Option, + /// Number of calls to execute between invariant assertions. + /// + /// - `0`: Only assert on the last call of each run (fastest, but may miss exact breaking call) + /// - `1` (default): Assert after every call (current behavior, most precise) + /// - `N`: Assert every N calls AND always on the last call + /// + /// Example: `check_interval = 10` means assert after calls 10, 20, 30, ... and the last call. + pub check_interval: u32, } impl Default for InvariantConfig { @@ -61,6 +69,7 @@ impl Default for InvariantConfig { show_solidity: false, max_time_delay: None, max_block_delay: None, + check_interval: 1, } } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d54cb0aac63f5..c133fa01291c4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -286,6 +286,11 @@ pub struct Config { pub eth_rpc_url: Option, /// Whether to accept invalid certificates for the rpc server. pub eth_rpc_accept_invalid_certs: bool, + /// Whether to disable automatic proxy detection for the rpc server. + /// + /// This can help in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) + /// where system proxy detection via SCDynamicStore causes crashes. + pub eth_rpc_no_proxy: bool, /// JWT secret that should be used for any rpc calls pub eth_rpc_jwt: Option, /// Timeout that should be used for any rpc calls @@ -299,6 +304,8 @@ pub struct Config { /// You can also the ETH_RPC_HEADERS env variable like so: /// `ETH_RPC_HEADERS="x-custom-header:value x-another-header:another-value"` pub eth_rpc_headers: Option>, + /// Print the equivalent curl command instead of making the RPC request. + pub eth_rpc_curl: bool, /// etherscan API key, or alias for an `EtherscanConfig` in `etherscan` table pub etherscan_api_key: Option, /// Multiple etherscan api configs and their aliases @@ -347,6 +354,8 @@ pub struct Config { pub invariant: InvariantConfig, /// Whether to allow ffi cheatcodes in test pub ffi: bool, + /// Whether to show `console.log` outputs in realtime during script/test execution + pub live_logs: bool, /// Whether to allow `expectRevert` for internal functions. pub allow_internal_expect_revert: bool, /// Use the create 2 factory in all cases including tests and non-broadcasting scripts. @@ -747,6 +756,18 @@ impl Config { Self::from_provider(Self::figment_with_root(root.as_ref())) } + /// Loads the `Config` from the given root directory, allowing profile fallback. + /// + /// Unlike [`load_with_root`](Self::load_with_root), if the selected profile (via + /// `FOUNDRY_PROFILE`) does not exist in the config, this falls back to the default profile + /// instead of returning an error. This is useful for loading nested lib/dependency configs + /// that may not define all profiles the main project uses. + #[track_caller] + pub fn load_with_root_and_fallback(root: impl AsRef) -> Result { + let figment = Self::figment_with_root(root.as_ref()); + Self::from_figment_fallback(Figment::from(figment)) + } + /// Attempts to extract a `Config` from `provider`, returning the result. /// /// # Example @@ -774,25 +795,49 @@ impl Config { } fn from_figment(figment: Figment) -> Result { + Self::from_figment_inner(figment, true) + } + + /// Same as `from_figment` but allows unknown profiles, falling back to default profile. + /// Used when loading nested lib configs that may not define all profiles. + fn from_figment_fallback(figment: Figment) -> Result { + Self::from_figment_inner(figment, false) + } + + fn from_figment_inner( + figment: Figment, + strict_profile: bool, + ) -> Result { let mut config = figment.extract::().map_err(ExtractConfigError::new)?; - config.profile = figment.profile().clone(); + let selected_profile = figment.profile().clone(); // The `"profile"` profile contains all the profiles as keys. - let mut add_profile = |profile: &Profile| { - if !config.profiles.contains(profile) { - config.profiles.push(profile.clone()); + fn add_profile(profiles: &mut Vec, profile: &Profile) { + if !profiles.contains(profile) { + profiles.push(profile.clone()); } - }; + } let figment = figment.select(Self::PROFILE_SECTION); if let Ok(data) = figment.data() && let Some(profiles) = data.get(&Profile::new(Self::PROFILE_SECTION)) { for profile in profiles.keys() { - add_profile(&Profile::new(profile)); + add_profile(&mut config.profiles, &Profile::new(profile)); } } - add_profile(&Self::DEFAULT_PROFILE); - add_profile(&config.profile); + add_profile(&mut config.profiles, &Self::DEFAULT_PROFILE); + + // Check if the selected profile exists. + if config.profiles.contains(&selected_profile) { + config.profile = selected_profile; + } else if strict_profile { + return Err(ExtractConfigError::new(Error::from(format!( + "selected profile `{selected_profile}` does not exist" + )))); + } else { + // Fall back to default profile for nested lib configs. + config.profile = Self::DEFAULT_PROFILE; + } config.normalize_optimizer_settings(); @@ -1822,11 +1867,6 @@ impl Config { src: paths.sources.file_name().unwrap().into(), out: artifacts.clone(), libs: paths.libraries.into_iter().map(|lib| lib.file_name().unwrap().into()).collect(), - remappings: paths - .remappings - .into_iter() - .map(|r| RelativeRemapping::new(r, root)) - .collect(), fs_permissions: FsPermissions::new([PathPermission::read(artifacts)]), ..Self::default() } @@ -2287,8 +2327,8 @@ impl Config { figment = figment.merge(("evm_version", version)); } - // Normalize `deny` based on the provided `deny_warnings` version. - if self.deny_warnings + // Normalize `deny` based on the provided `deny_warnings` value. + if figment.extract_inner::("deny_warnings").unwrap_or(false) && let Ok(DenyLevel::Never) = figment.extract_inner("deny") { figment = figment.merge(("deny", DenyLevel::Warnings)); @@ -2499,7 +2539,7 @@ impl Default for Config { allow_paths: vec![], include_paths: vec![], force: false, - evm_version: EvmVersion::Prague, + evm_version: EvmVersion::Osaka, gas_reports: vec!["*".to_string()], gas_reports_ignore: vec![], gas_reports_include_tests: false, @@ -2529,6 +2569,7 @@ impl Default for Config { invariant: InvariantConfig::new("cache/invariant".into()), always_use_create_2_factory: false, ffi: false, + live_logs: false, allow_internal_expect_revert: false, prompt_timeout: 120, sender: Self::DEFAULT_SENDER, @@ -2551,9 +2592,11 @@ impl Default for Config { memory_limit: 1 << 27, // 2**27 = 128MiB = 134_217_728 bytes eth_rpc_url: None, eth_rpc_accept_invalid_certs: false, + eth_rpc_no_proxy: false, eth_rpc_jwt: None, eth_rpc_timeout: None, eth_rpc_headers: None, + eth_rpc_curl: false, etherscan_api_key: None, verbosity: 0, remappings: vec![], @@ -2564,6 +2607,8 @@ impl Default for Config { SolidityErrorCode::ContractExceeds24576Bytes, SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes, SolidityErrorCode::TransientStorageUsed, + SolidityErrorCode::TransferDeprecated, + SolidityErrorCode::NatspecMemorySafeAssemblyDeprecated, ], ignored_file_paths: vec![], deny: DenyLevel::Never, @@ -4625,6 +4670,48 @@ mod tests { }); } + #[test] + fn test_model_checker_settings_with_bool_flags() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r" + [profile.default] + + [profile.default.model_checker] + engine = 'chc' + show_unproved = true + show_unsupported = true + show_proved_safe = false + div_mod_with_slacks = true + ", + )?; + let mut loaded = Config::load().unwrap(); + clear_warning(&mut loaded); + + let mc = loaded.model_checker.as_ref().unwrap(); + assert_eq!(mc.show_unproved, Some(true)); + assert_eq!(mc.show_unsupported, Some(true)); + assert_eq!(mc.show_proved_safe, Some(false)); + assert_eq!(mc.div_mod_with_slacks, Some(true)); + + // Test round-trip: serialize and reload + let s = loaded.to_string_pretty().unwrap(); + jail.create_file("foundry.toml", &s)?; + + let mut reloaded = Config::load().unwrap(); + clear_warning(&mut reloaded); + + let mc_reloaded = reloaded.model_checker.as_ref().unwrap(); + assert_eq!(mc_reloaded.show_unproved, Some(true)); + assert_eq!(mc_reloaded.show_unsupported, Some(true)); + assert_eq!(mc_reloaded.show_proved_safe, Some(false)); + assert_eq!(mc_reloaded.div_mod_with_slacks, Some(true)); + + Ok(()) + }); + } + #[test] fn test_model_checker_settings_relative_paths() { figment::Jail::expect_with(|jail| { @@ -6466,4 +6553,643 @@ mod tests { Ok(()) }); } + + #[test] + fn warns_on_unknown_keys_in_all_config_sections() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + unknown_profile_key = "should_warn" + + # Standalone sections with unknown keys + [fmt] + line_length = 120 + unknown_fmt_key = "should_warn" + + [lint] + severity = ["high"] + unknown_lint_key = "should_warn" + + [doc] + out = "docs" + unknown_doc_key = "should_warn" + + [fuzz] + runs = 256 + unknown_fuzz_key = "should_warn" + + [invariant] + runs = 256 + unknown_invariant_key = "should_warn" + + [vyper] + unknown_vyper_key = "should_warn" + + [bind_json] + out = "bindings.sol" + unknown_bind_json_key = "should_warn" + + # Nested profile sections with unknown keys + [profile.default.fmt] + line_length = 100 + unknown_nested_fmt_key = "should_warn" + + [profile.default.lint] + severity = ["low"] + unknown_nested_lint_key = "should_warn" + + [profile.default.doc] + out = "documentation" + unknown_nested_doc_key = "should_warn" + + [profile.default.fuzz] + runs = 512 + unknown_nested_fuzz_key = "should_warn" + + [profile.default.invariant] + runs = 512 + unknown_nested_invariant_key = "should_warn" + + [profile.default.vyper] + unknown_nested_vyper_key = "should_warn" + + [profile.default.bind_json] + out = "nested_bindings.sol" + unknown_nested_bind_json_key = "should_warn" + + # Array sections with unknown keys + [[profile.default.compilation_restrictions]] + paths = "src/*.sol" + unknown_compilation_key = "should_warn" + + [[profile.default.additional_compiler_profiles]] + name = "via-ir" + via_ir = true + unknown_compiler_profile_key = "should_warn" + "#, + )?; + + let cfg = Config::load().unwrap(); + + // Expected warnings for profile-level unknown key + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownKey { key, .. } if key == "unknown_profile_key" + )), + "Expected warning for 'unknown_profile_key' in profile, got: {:?}", + cfg.warnings + ); + + // Expected warnings for standalone sections + let standalone_expected = [ + ("unknown_fmt_key", "fmt"), + ("unknown_lint_key", "lint"), + ("unknown_doc_key", "doc"), + ("unknown_fuzz_key", "fuzz"), + ("unknown_invariant_key", "invariant"), + ("unknown_vyper_key", "vyper"), + ("unknown_bind_json_key", "bind_json"), + ]; + + for (expected_key, expected_section) in standalone_expected { + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownSectionKey { key, section, .. } + if key == expected_key && section == expected_section + )), + "Expected warning for '{}' in standalone section '{}', got: {:?}", + expected_key, + expected_section, + cfg.warnings + ); + } + + // Expected warnings for nested profile sections + let nested_expected = [ + ("unknown_nested_fmt_key", "fmt"), + ("unknown_nested_lint_key", "lint"), + ("unknown_nested_doc_key", "doc"), + ("unknown_nested_fuzz_key", "fuzz"), + ("unknown_nested_invariant_key", "invariant"), + ("unknown_nested_vyper_key", "vyper"), + ("unknown_nested_bind_json_key", "bind_json"), + ]; + + for (expected_key, expected_section) in nested_expected { + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownSectionKey { key, section, .. } + if key == expected_key && section == expected_section + )), + "Expected warning for '{}' in nested section '{}', got: {:?}", + expected_key, + expected_section, + cfg.warnings + ); + } + + // Expected warnings for array item sections + let array_expected = [ + ("unknown_compilation_key", "compilation_restrictions"), + ("unknown_compiler_profile_key", "additional_compiler_profiles"), + ]; + + for (expected_key, expected_section) in array_expected { + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownSectionKey { key, section, .. } + if key == expected_key && section == expected_section + )), + "Expected warning for '{}' in array section '{}', got: {:?}", + expected_key, + expected_section, + cfg.warnings + ); + } + + // Verify total count of unknown key warnings + let unknown_key_warnings: Vec<_> = cfg + .warnings + .iter() + .filter(|w| { + matches!(w, crate::Warning::UnknownKey { .. }) + || matches!(w, crate::Warning::UnknownSectionKey { .. }) + }) + .collect(); + + // 1 profile key + 7 standalone + 7 nested + 2 array = 17 total + assert_eq!( + unknown_key_warnings.len(), + 17, + "Expected 17 unknown key warnings (1 profile + 7 standalone + 7 nested + 2 array), got {}: {:?}", + unknown_key_warnings.len(), + unknown_key_warnings + ); + + Ok(()) + }); + } + + #[test] + fn warns_on_unknown_keys_in_extended_config() { + figment::Jail::expect_with(|jail| { + // Create base config with unknown keys + jail.create_file( + "base.toml", + r#" + [profile.default] + optimizer_runs = 800 + unknown_base_profile_key = "should_warn" + + [lint] + severity = ["high"] + unknown_base_lint_key = "should_warn" + + [fmt] + line_length = 100 + unknown_base_fmt_key = "should_warn" + "#, + )?; + + // Create local config that extends base with its own unknown keys + jail.create_file( + "foundry.toml", + r#" + [profile.default] + extends = "base.toml" + src = "src" + unknown_local_profile_key = "should_warn" + + [lint] + unknown_local_lint_key = "should_warn" + + [fuzz] + runs = 512 + unknown_local_fuzz_key = "should_warn" + + [[profile.default.compilation_restrictions]] + paths = "src/*.sol" + unknown_local_restriction_key = "should_warn" + "#, + )?; + + let cfg = Config::load().unwrap(); + + // Verify base config values are inherited + assert_eq!(cfg.optimizer_runs, Some(800)); + + // Unknown keys from both base and local configs should be detected. + // Note: Due to how figment merges configs before validation, the source + // will show the local config file for all warnings. This is a known + // limitation - proper source attribution for extended configs would + // require validating each file before the merge. + + // Verify all expected unknown keys are detected + let expected_unknown_keys = ["unknown_base_profile_key", "unknown_local_profile_key"]; + for expected_key in expected_unknown_keys { + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownKey { key, .. } if key == expected_key + )), + "Expected warning for '{}', got: {:?}", + expected_key, + cfg.warnings + ); + } + + let expected_section_keys = [ + ("unknown_base_lint_key", "lint"), + ("unknown_base_fmt_key", "fmt"), + ("unknown_local_lint_key", "lint"), + ("unknown_local_fuzz_key", "fuzz"), + ("unknown_local_restriction_key", "compilation_restrictions"), + ]; + for (expected_key, expected_section) in expected_section_keys { + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownSectionKey { key, section, .. } + if key == expected_key && section == expected_section + )), + "Expected warning for '{}' in section '{}', got: {:?}", + expected_key, + expected_section, + cfg.warnings + ); + } + + // Verify total: 2 profile keys + 5 section keys = 7 warnings + let unknown_warnings: Vec<_> = cfg + .warnings + .iter() + .filter(|w| { + matches!(w, crate::Warning::UnknownKey { .. }) + || matches!(w, crate::Warning::UnknownSectionKey { .. }) + }) + .collect(); + assert_eq!( + unknown_warnings.len(), + 7, + "Expected 7 unknown key warnings, got {}: {:?}", + unknown_warnings.len(), + unknown_warnings + ); + + Ok(()) + }); + } + + // Test for issue #12844: FOUNDRY_PROFILE=nonexistent should fail + #[test] + fn fails_on_unknown_profile() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + "#, + )?; + + jail.set_env("FOUNDRY_PROFILE", "nonexistent"); + let err = Config::load().expect_err("expected unknown profile to fail"); + let err_msg = err.to_string(); + assert!( + err_msg.contains("selected profile `nonexistent` does not exist"), + "Expected error about nonexistent profile, got: {err_msg}" + ); + + Ok(()) + }); + } + + // Test for issue #13316: vyper config keys should not trigger unknown key warnings + #[test] + fn no_false_warnings_for_vyper_config_keys() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [vyper] + optimize = "gas" + path = "/usr/bin/vyper" + experimental_codegen = true + "#, + )?; + + let cfg = Config::load().unwrap(); + // None of the valid vyper keys should trigger warnings + let vyper_warnings: Vec<_> = cfg + .warnings + .iter() + .filter(|w| { + matches!( + w, + crate::Warning::UnknownSectionKey { section, .. } if section == "vyper" + ) + }) + .collect(); + + assert!( + vyper_warnings.is_empty(), + "Valid vyper keys should not trigger warnings, got: {vyper_warnings:?}" + ); + + Ok(()) + }); + } + + // Test for issue #13316: vyper config in profile should not trigger false warnings + #[test] + fn no_false_warnings_for_nested_vyper_config_keys() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [profile.default.vyper] + optimize = "codesize" + path = "/opt/vyper/bin/vyper" + experimental_codegen = false + "#, + )?; + + let cfg = Config::load().unwrap(); + // None of the valid vyper keys should trigger warnings + let vyper_warnings: Vec<_> = cfg + .warnings + .iter() + .filter(|w| { + matches!( + w, + crate::Warning::UnknownSectionKey { section, .. } if section == "vyper" + ) + }) + .collect(); + + assert!( + vyper_warnings.is_empty(), + "Valid nested vyper keys should not trigger warnings, got: {vyper_warnings:?}" + ); + + Ok(()) + }); + } + + // Test for issue #13316: inline vyper config format should not trigger false warnings + // This matches the exact format used in https://github.com/pcaversaccio/snekmate + #[test] + fn no_false_warnings_for_inline_vyper_config() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + vyper = { optimize = "gas" } + + [profile.default-venom] + vyper = { experimental_codegen = true } + + [profile.ci-venom] + vyper = { experimental_codegen = true } + "#, + )?; + + let cfg = Config::load().unwrap(); + let vyper_warnings: Vec<_> = cfg + .warnings + .iter() + .filter(|w| { + matches!( + w, + crate::Warning::UnknownSectionKey { section, .. } if section == "vyper" + ) + }) + .collect(); + + assert!( + vyper_warnings.is_empty(), + "Valid inline vyper config should not trigger warnings, got: {vyper_warnings:?}" + ); + + Ok(()) + }); + } + + // Test for issue #13316: unknown vyper keys should still warn + #[test] + fn warns_on_unknown_vyper_keys() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [vyper] + optimize = "gas" + unknown_vyper_option = true + "#, + )?; + + let cfg = Config::load().unwrap(); + assert!( + cfg.warnings.iter().any(|w| matches!( + w, + crate::Warning::UnknownSectionKey { key, section, .. } + if key == "unknown_vyper_option" && section == "vyper" + )), + "Unknown vyper key should trigger warning, got: {:?}", + cfg.warnings + ); + + Ok(()) + }); + } + + // Test for issue #12844: known profile should work + #[test] + fn succeeds_on_known_profile() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [profile.ci] + src = "src" + fuzz = { runs = 10000 } + "#, + )?; + + jail.set_env("FOUNDRY_PROFILE", "ci"); + let config = Config::load().expect("known profile should work"); + assert_eq!(config.profile.as_str(), "ci"); + assert_eq!(config.fuzz.runs, 10000); + + Ok(()) + }); + } + + // Test for issue #12963: nested lib configs should fallback to default profile + // when they don't define the requested profile + #[test] + fn nested_lib_config_falls_back_to_default_profile() { + figment::Jail::expect_with(|jail| { + // Create a lib directory with only default profile + let lib_path = jail.directory().join("lib/mylib"); + std::fs::create_dir_all(&lib_path).unwrap(); + jail.create_file( + "lib/mylib/foundry.toml", + r#" + [profile.default] + src = "contracts" + "#, + )?; + + // Set a profile that doesn't exist in the lib + jail.set_env("FOUNDRY_PROFILE", "ci"); + + // load_with_root_and_fallback should succeed and fall back to default + let config = Config::load_with_root_and_fallback(&lib_path) + .expect("lib config should load with fallback"); + assert_eq!(config.profile, Config::DEFAULT_PROFILE); + assert_eq!(config.src.as_os_str(), "contracts"); + + Ok(()) + }); + } + + // Test for issue #12963: nested lib configs should use requested profile if it exists + #[test] + fn nested_lib_config_uses_profile_if_exists() { + figment::Jail::expect_with(|jail| { + // Create a lib directory with both default and ci profiles + let lib_path = jail.directory().join("lib/mylib"); + std::fs::create_dir_all(&lib_path).unwrap(); + jail.create_file( + "lib/mylib/foundry.toml", + r#" + [profile.default] + src = "contracts" + + [profile.ci] + src = "contracts" + fuzz = { runs = 5000 } + "#, + )?; + + // Set a profile that exists in the lib + jail.set_env("FOUNDRY_PROFILE", "ci"); + + // load_with_root_and_fallback should use the ci profile + let config = Config::load_with_root_and_fallback(&lib_path) + .expect("lib config should load with profile"); + assert_eq!(config.profile.as_str(), "ci"); + assert_eq!(config.fuzz.runs, 5000); + + Ok(()) + }); + } + + // Test for issue #13170: profile names with hyphens should work correctly + #[test] + fn succeeds_on_hyphenated_profile_name() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [profile.ci-venom] + src = "src" + fuzz = { runs = 7500 } + + [profile.default-venom] + src = "src" + fuzz = { runs = 8000 } + "#, + )?; + + // Test ci-venom profile + jail.set_env("FOUNDRY_PROFILE", "ci-venom"); + let config = Config::load().expect("hyphenated profile should work"); + assert_eq!(config.profile.as_str(), "ci-venom"); + assert_eq!(config.fuzz.runs, 7500); + + // Test default-venom profile + jail.set_env("FOUNDRY_PROFILE", "default-venom"); + let config = Config::load().expect("hyphenated profile should work"); + assert_eq!(config.profile.as_str(), "default-venom"); + assert_eq!(config.fuzz.runs, 8000); + + // Verify the profiles list contains hyphenated names + assert!( + config.profiles.iter().any(|p| p.as_str() == "ci-venom"), + "profiles should contain 'ci-venom', got: {:?}", + config.profiles + ); + assert!( + config.profiles.iter().any(|p| p.as_str() == "default-venom"), + "profiles should contain 'default-venom', got: {:?}", + config.profiles + ); + + Ok(()) + }); + } + + // Test for issue #13170: hyphenated profile with nested config keys + #[test] + fn hyphenated_profile_with_nested_sections() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "src" + + [profile.ci-venom] + src = "src" + optimizer_runs = 500 + + [profile.ci-venom.fuzz] + runs = 10000 + max_test_rejects = 350000 + + [profile.ci-venom.invariant] + runs = 375 + depth = 500 + "#, + )?; + + jail.set_env("FOUNDRY_PROFILE", "ci-venom"); + let config = + Config::load().expect("hyphenated profile with nested sections should work"); + assert_eq!(config.profile.as_str(), "ci-venom"); + assert_eq!(config.optimizer_runs, Some(500)); + assert_eq!(config.fuzz.runs, 10000); + assert_eq!(config.fuzz.max_test_rejects, 350000); + assert_eq!(config.invariant.runs, 375); + assert_eq!(config.invariant.depth, 500); + + Ok(()) + }); + } } diff --git a/crates/config/src/lint.rs b/crates/config/src/lint.rs index c726e3ecc8f5b..25094ead98cf8 100644 --- a/crates/config/src/lint.rs +++ b/crates/config/src/lint.rs @@ -28,8 +28,8 @@ pub struct LinterConfig { /// Configurable patterns that should be excluded when performing `mixedCase` lint checks. /// - /// Default's to ["ERC", "URI"] to allow common names like `rescueERC20`, `ERC721TokenReceiver` - /// or `tokenURI`. + /// Defaults to common abbreviations: `ERC`, `URI`, `ID`, `URL`, `API`, `JSON`, `XML`, `HTML`, + /// `HTTP`, `HTTPS`. This allows names like `marketID`, `tokenURI`, `apiURL`, `parseJSON`, etc. pub mixed_case_exceptions: Vec, } @@ -40,7 +40,18 @@ impl Default for LinterConfig { severity: vec![Severity::High, Severity::Med, Severity::Low], exclude_lints: Vec::new(), ignore: Vec::new(), - mixed_case_exceptions: vec!["ERC".to_string(), "URI".to_string()], + mixed_case_exceptions: vec![ + "ERC".to_string(), + "URI".to_string(), + "ID".to_string(), + "URL".to_string(), + "API".to_string(), + "JSON".to_string(), + "XML".to_string(), + "HTML".to_string(), + "HTTP".to_string(), + "HTTPS".to_string(), + ], } } } diff --git a/crates/config/src/providers/ext.rs b/crates/config/src/providers/ext.rs index e5d2d6e3ade81..a6404da946cd3 100644 --- a/crates/config/src/providers/ext.rs +++ b/crates/config/src/providers/ext.rs @@ -253,6 +253,9 @@ impl Provider for TomlFileProvider { /// A Provider that ensures all keys are snake case if they're not standalone sections, See /// `Config::STANDALONE_SECTIONS` +/// +/// For the `[profile]` section, profile names (like `ci-venom`) are preserved as-is, +/// but the config keys within each profile are still converted to snake_case. pub(crate) struct ForcedSnakeCaseData

(pub(crate) P); impl Provider for ForcedSnakeCaseData

{ @@ -267,6 +270,22 @@ impl Provider for ForcedSnakeCaseData

{ // don't force snake case for keys in standalone sections continue; } + + if profile.as_str().as_str() == Config::PROFILE_SECTION { + // For the `[profile]` section, we need to preserve profile names (the keys) + // but snake_case the config keys within each profile's dict. + let dict2 = std::mem::take(dict); + *dict = dict2 + .into_iter() + .map(|(profile_name, v)| { + // Keep the profile name exactly as-is (e.g., "ci-venom" stays "ci-venom") + let v = snake_case_value_keys(v); + (profile_name, v) + }) + .collect(); + continue; + } + let dict2 = std::mem::take(dict); *dict = dict2.into_iter().map(|(k, v)| (k.to_snake_case(), v)).collect(); } @@ -278,6 +297,24 @@ impl Provider for ForcedSnakeCaseData

{ } } +/// Recursively converts all keys in a Value (if it's a Dict) to snake_case. +fn snake_case_value_keys(value: Value) -> Value { + match value { + Value::Dict(tag, dict) => { + let new_dict = dict + .into_iter() + .map(|(k, v)| (k.to_snake_case(), snake_case_value_keys(v))) + .collect(); + Value::Dict(tag, new_dict) + } + Value::Array(tag, arr) => { + let new_arr = arr.into_iter().map(snake_case_value_keys).collect(); + Value::Array(tag, new_arr) + } + other => other, + } +} + /// A Provider that handles breaking changes in toml files pub(crate) struct BackwardsCompatTomlProvider

(pub(crate) P); diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 8890de642487c..0f308aa1ba523 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -166,14 +166,15 @@ impl RemappingsProvider<'_> { ) { let context_mappings = mappings.entry(context).or_default(); match context_mappings.entry(key) { - Entry::Occupied(mut e) => { - if e.get().components().count() > path.components().count() { - e.insert(path); - } + Entry::Occupied(mut e) + if e.get().components().count() > path.components().count() => + { + e.insert(path); } Entry::Vacant(e) => { e.insert(path); } + _ => {} } } @@ -258,8 +259,9 @@ impl RemappingsProvider<'_> { } fn nested_foundry_remappings(&self, lib: &Path) -> Vec { - // load config, of the nested lib if it exists - let Ok(config) = Config::load_with_root(lib) else { return vec![] }; + // load config of the nested lib if it exists, using fallback mode since libs may not + // define all profiles the main project uses + let Ok(config) = Config::load_with_root_and_fallback(lib) else { return vec![] }; let config = config.sanitized(); // if the configured _src_ directory is set to something that diff --git a/crates/config/src/providers/warnings.rs b/crates/config/src/providers/warnings.rs index 881399bae0bbe..9113fcd76fffa 100644 --- a/crates/config/src/providers/warnings.rs +++ b/crates/config/src/providers/warnings.rs @@ -4,7 +4,41 @@ use figment::{ value::{Dict, Map, Value}, }; use heck::ToSnakeCase; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; + +/// Allowed keys for CompilationRestrictions. +const COMPILATION_RESTRICTIONS_KEYS: &[&str] = &[ + "paths", + "version", + "via_ir", + "bytecode_hash", + "min_optimizer_runs", + "optimizer_runs", + "max_optimizer_runs", + "min_evm_version", + "evm_version", + "max_evm_version", +]; + +/// Allowed keys for SettingsOverrides. +const SETTINGS_OVERRIDES_KEYS: &[&str] = + &["name", "via_ir", "evm_version", "optimizer", "optimizer_runs", "bytecode_hash"]; + +/// Allowed keys for VyperConfig. +/// Required because VyperConfig uses `skip_serializing_if = "Option::is_none"` on all fields, +/// causing the default serialization to produce an empty dict. +const VYPER_KEYS: &[&str] = &["optimize", "path", "experimental_codegen"]; + +/// Allowed keys for DocConfig. +/// Required because DocConfig uses `skip_serializing_if = "Option::is_none"` on some fields +/// (`repository`, `path`), whose defaults are `None` and thus excluded from serialization. +const DOC_KEYS: &[&str] = &["out", "title", "book", "homepage", "repository", "path", "ignore"]; + +/// Reserved keys that should not trigger unknown key warnings. +const RESERVED_KEYS: &[&str] = &["extends"]; + +/// Keys kept for backward compatibility that should not trigger unknown key warnings. +const BACKWARD_COMPATIBLE_KEYS: &[&str] = &["solc_version"]; /// Generate warnings for unknown sections and deprecated keys pub struct WarningsProvider

{ @@ -84,9 +118,8 @@ impl WarningsProvider

{ if let Ok(default_map) = figment::providers::Serialized::defaults(&Config::default()).data() && let Some(default_dict) = default_map.get(&Config::DEFAULT_PROFILE) { - let allowed_keys: std::collections::BTreeSet = - default_dict.keys().cloned().collect(); - for profile_map in profiles { + let allowed_keys: BTreeSet = default_dict.keys().cloned().collect(); + for profile_map in profiles.clone() { for (profile, value) in profile_map { let Some(profile_dict) = value.as_dict() else { continue; @@ -103,8 +136,10 @@ impl WarningsProvider

{ !DEPRECATIONS.iter().any(|(deprecated_key, _)| *deprecated_key == key); let is_not_allowed = !allowed_keys.contains(key) && !allowed_keys.contains(&key.to_snake_case()); - let is_not_reserved = key != "extends" && key != Self::WARNINGS_KEY; - let is_not_backward_compatible = key != "solc_version"; + let is_not_reserved = + !RESERVED_KEYS.contains(&key.as_str()) && key != Self::WARNINGS_KEY; + let is_not_backward_compatible = + !BACKWARD_COMPATIBLE_KEYS.contains(&key.as_str()); if is_not_deprecated && is_not_allowed @@ -118,12 +153,160 @@ impl WarningsProvider

{ }); } } + + // Add warning for unknown keys in nested sections within profiles. + self.collect_nested_section_warnings( + profile_dict, + default_dict, + &source, + &mut out, + ); } } + + // Add warning for unknown keys in standalone sections. + self.collect_standalone_section_warnings(&data, default_dict, &mut out); } Ok(out) } + + /// Collects warnings for unknown keys in standalone sections like `[lint]`, `[fmt]`, etc. + fn collect_standalone_section_warnings( + &self, + data: &Map, + default_dict: &Dict, + out: &mut Vec, + ) { + let source = self + .provider + .metadata() + .source + .map(|s| s.to_string()) + .unwrap_or(Config::FILE_NAME.to_string()); + + for section_name in Config::STANDALONE_SECTIONS { + // Get the section from the parsed data + let section_profile = Profile::new(section_name); + let Some(section_dict) = data.get(§ion_profile) else { + continue; + }; + + // Get allowed keys for this section from the default config + // Special case for vyper: VyperConfig uses skip_serializing_if on all Option fields, + // so the default serialization produces an empty dict. Use explicit keys instead. + let allowed_keys: BTreeSet = if *section_name == "vyper" { + VYPER_KEYS.iter().map(|s| s.to_string()).collect() + } else if *section_name == "doc" { + DOC_KEYS.iter().map(|s| s.to_string()).collect() + } else { + let Some(default_section_value) = default_dict.get(*section_name) else { + continue; + }; + let Some(default_section_dict) = default_section_value.as_dict() else { + continue; + }; + default_section_dict.keys().cloned().collect() + }; + + for key in section_dict.keys() { + let is_not_allowed = + !allowed_keys.contains(key) && !allowed_keys.contains(&key.to_snake_case()); + if is_not_allowed { + out.push(Warning::UnknownSectionKey { + key: key.clone(), + section: section_name.to_string(), + source: source.clone(), + }); + } + } + } + } + + /// Collects warnings for unknown keys in nested sections within profiles, + /// like `compilation_restrictions`. + fn collect_nested_section_warnings( + &self, + profile_dict: &Dict, + default_dict: &Dict, + source: &str, + out: &mut Vec, + ) { + // Check nested sections that are dicts (like `lint`, `fmt` when defined in profile) + for (key, value) in profile_dict { + let Some(nested_dict) = value.as_dict() else { + // Also check arrays of dicts (like `compilation_restrictions`) + if let Some(arr) = value.as_array() { + // Get allowed keys for known array item types + let allowed_keys = Self::get_array_item_allowed_keys(key); + + if allowed_keys.is_empty() { + continue; + } + + for item in arr { + let Some(item_dict) = item.as_dict() else { + continue; + }; + for item_key in item_dict.keys() { + let is_not_allowed = !allowed_keys.contains(item_key) + && !allowed_keys.contains(&item_key.to_snake_case()); + if is_not_allowed { + out.push(Warning::UnknownSectionKey { + key: item_key.clone(), + section: key.clone(), + source: source.to_string(), + }); + } + } + } + } + continue; + }; + + // Get allowed keys from the default config for this nested section + // Special case for vyper: VyperConfig uses skip_serializing_if on all Option fields, + // so the default serialization produces an empty dict. Use explicit keys instead. + let allowed_keys: BTreeSet = if key == "vyper" { + VYPER_KEYS.iter().map(|s| s.to_string()).collect() + } else if key == "doc" { + DOC_KEYS.iter().map(|s| s.to_string()).collect() + } else { + let Some(default_value) = default_dict.get(key) else { + continue; + }; + let Some(default_nested_dict) = default_value.as_dict() else { + continue; + }; + default_nested_dict.keys().cloned().collect() + }; + + for nested_key in nested_dict.keys() { + let is_not_allowed = !allowed_keys.contains(nested_key) + && !allowed_keys.contains(&nested_key.to_snake_case()); + if is_not_allowed { + out.push(Warning::UnknownSectionKey { + key: nested_key.clone(), + section: key.clone(), + source: source.to_string(), + }); + } + } + } + } + + /// Returns the allowed keys for array item types based on the section name. + fn get_array_item_allowed_keys(section_name: &str) -> BTreeSet { + match section_name { + "compilation_restrictions" => { + COMPILATION_RESTRICTIONS_KEYS.iter().map(|s| s.to_string()).collect() + } + "additional_compiler_profiles" => { + SETTINGS_OVERRIDES_KEYS.iter().map(|s| s.to_string()).collect() + } + _ => BTreeSet::new(), + } + } } impl Provider for WarningsProvider

{ diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index 52e751cd2ca03..624dd5a35f81b 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -213,7 +213,7 @@ where deserialize_u64_or_max(deserializer)?.try_into().map_err(D::Error::custom) } -/// Deserialize into `U256` from either a `u64` or a `U256` hex string. +/// Deserialize into `U256` from either a `u64`, a `U256` hex string, or a decimal string. pub fn deserialize_u64_to_u256<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -223,11 +223,16 @@ where enum NumericValue { U256(U256), U64(u64), + String(String), } match NumericValue::deserialize(deserializer)? { NumericValue::U64(n) => Ok(U256::from(n)), NumericValue::U256(n) => Ok(n), + NumericValue::String(s) => { + // Handle decimal strings (e.g., "18446744073709551615") + U256::from_str(&s).map_err(D::Error::custom) + } } } diff --git a/crates/config/src/warning.rs b/crates/config/src/warning.rs index 4476f1f5e9fcd..1c1518f5d7cc7 100644 --- a/crates/config/src/warning.rs +++ b/crates/config/src/warning.rs @@ -55,6 +55,15 @@ pub enum Warning { /// The config file where the key was found source: String, }, + /// An unknown key was encountered in a section in a TOML file + UnknownSectionKey { + /// The unknown key name + key: String, + /// The section where the key was found + section: String, + /// The config file where the key was found + source: String, + }, } impl fmt::Display for Warning { @@ -102,6 +111,12 @@ impl fmt::Display for Warning { "Found unknown `{key}` config for profile `{profile}` defined in {source}." ) } + Self::UnknownSectionKey { key, section, source } => { + write!( + f, + "Found unknown `{key}` config key in section `{section}` defined in {source}." + ) + } } } } diff --git a/crates/debugger/src/node.rs b/crates/debugger/src/node.rs index f7b18a978a4f4..e74ae58d66323 100644 --- a/crates/debugger/src/node.rs +++ b/crates/debugger/src/node.rs @@ -14,6 +14,8 @@ pub struct DebugNode { pub kind: CallKind, /// Calldata of the call. pub calldata: Bytes, + /// The gas limit of the call. + pub gas_limit: u64, /// The debug steps. pub steps: Vec, } @@ -25,8 +27,9 @@ impl DebugNode { kind: CallKind, steps: Vec, calldata: Bytes, + gas_limit: u64, ) -> Self { - Self { address, kind, steps, calldata } + Self { address, kind, steps, calldata, gas_limit } } } @@ -78,7 +81,7 @@ pub fn flatten_call_trace(arena: CallTraceArena, out: &mut Vec) { let call = &arena_nodes[pending.node_idx].trace; let calldata = if call.kind.is_any_create() { Bytes::new() } else { call.data.clone() }; - let node = DebugNode::new(call.address, call.kind, steps, calldata); + let node = DebugNode::new(call.address, call.kind, steps, calldata, call.gas_limit); out.push(node); } diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index 5f1d023640160..c3f38b681aac1 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -196,11 +196,11 @@ impl TUIContext<'_> { } // 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; - } + 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; } // Step forward diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 6badd441fdb94..daf2d01d1da84 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -372,10 +372,11 @@ impl TUIContext<'_> { .collect::>(); let title = format!( - "Address: {} | PC: {} | Gas used in call: {}", + "Address: {} | PC: {} | Gas used: {} | Gas refund: {}", self.address(), self.current_step().pc, - self.current_step().gas_used, + self.debug_call().gas_limit - self.current_step().gas_remaining, + self.current_step().gas_refund_counter ); let block = Block::default().title(title).borders(Borders::ALL); let list = List::new(items) diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index f04f6eb340176..c007bb2ebfcb0 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -183,7 +183,7 @@ impl DocBuilder { HashMap>, HashMap>, ) = funcs.into_iter().partition(|(_, v)| v.len() == 1); - remaining.extend(items.into_iter().flat_map(|(_, v)| v)); + remaining.extend(items.into_values().flatten()); // Each regular item will be written into its own file. files = remaining diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 455cd4082bb61..e3358b2f437dd 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -7,7 +7,7 @@ use crate::{ }; use forge_fmt::solang_ext::SafeUnwrap; use itertools::Itertools; -use solang_parser::pt::{Base, FunctionDefinition}; +use solang_parser::pt::{Base, FunctionDefinition, VariableAttribute}; use std::path::Path; /// The result of [`AsDoc::as_doc`]. @@ -181,18 +181,45 @@ impl AsDoc for Document { writer.writeln_doc(&item.comments)?; - if let Some(state_vars) = item.variables() { - writer.write_subtitle("State Variables")?; - state_vars.into_iter().try_for_each(|(item, comments, code)| { - let comments = comments.merge_inheritdoc( - &item.name.safe_unwrap().name, - read_context!(self, INHERITDOC_ID, Inheritdoc), - ); + if let Some(all_vars) = item.variables() { + let (constants, state_vars): (Vec<_>, Vec<_>) = + all_vars.into_iter().partition(|(item, _, _)| { + item.attrs.iter().any(|attr| { + matches!( + attr, + VariableAttribute::Constant(_) + | VariableAttribute::Immutable(_) + ) + }) + }); + + if !constants.is_empty() { + writer.write_subtitle("Constants")?; + constants.into_iter().try_for_each(|(item, comments, code)| { + let comments = comments.merge_inheritdoc( + &item.name.safe_unwrap().name, + read_context!(self, INHERITDOC_ID, Inheritdoc), + ); + + writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_section(&comments, code)?; + writer.writeln() + })?; + } - writer.write_heading(&item.name.safe_unwrap().name)?; - writer.write_section(&comments, code)?; - writer.writeln() - })?; + if !state_vars.is_empty() { + writer.write_subtitle("State Variables")?; + state_vars.into_iter().try_for_each(|(item, comments, code)| { + let comments = comments.merge_inheritdoc( + &item.name.safe_unwrap().name, + read_context!(self, INHERITDOC_ID, Inheritdoc), + ); + + writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_section(&comments, code)?; + writer.writeln() + })?; + } } if let Some(funcs) = item.functions() { diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index a2118233465a3..8b6ccb7004bd5 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -21,7 +21,7 @@ use revm::{ context_interface::result::ResultAndState, database::DatabaseRef, primitives::{HashMap as Map, hardfork::SpecId}, - state::{Account, AccountInfo}, + state::{Account, AccountInfo, EvmState}, }; use std::{borrow::Cow, collections::BTreeMap}; @@ -47,16 +47,15 @@ pub struct CowBackend<'a> { /// /// 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, - /// The [SpecId] of the current backend. - spec_id: SpecId, + /// The [SpecId] to initialize the backend with on first mutable access. + /// `None` means the backend has already been initialized for the current call. + spec_id: Option, } impl<'a> CowBackend<'a> { /// Creates a new `CowBackend` with the given `Backend`. pub fn new_borrowed(backend: &'a Backend) -> Self { - Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::default() } + Self { backend: Cow::Borrowed(backend), spec_id: Some(SpecId::default()) } } /// Executes the configured transaction of the `env` without committing state changes @@ -71,8 +70,7 @@ impl<'a> CowBackend<'a> { ) -> 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; - self.spec_id = env.evm_env.cfg_env.spec; + self.spec_id = Some(env.evm_env.cfg_env.spec); let mut evm = crate::evm::new_evm_with_inspector(self, env.to_owned(), inspector); @@ -94,12 +92,11 @@ impl<'a> CowBackend<'a> { /// /// If this is the first time this is called, the backed is cloned and initialized. fn backend_mut(&mut self, env: &EnvMut<'_>) -> &mut Backend { - if !self.is_initialized { + if let Some(spec_id) = self.spec_id.take() { let backend = self.backend.to_mut(); let mut env = env.to_owned(); - env.evm_env.cfg_env.spec = self.spec_id; + env.evm_env.cfg_env.spec = spec_id; backend.initialize(&env); - self.is_initialized = true; return backend; } self.backend.to_mut() @@ -107,7 +104,7 @@ impl<'a> CowBackend<'a> { /// Returns a mutable instance of the Backend if it is initialized. fn initialized_backend_mut(&mut self) -> Option<&mut Backend> { - if self.is_initialized { + if self.spec_id.is_none() { return Some(self.backend.to_mut()); } None @@ -232,12 +229,8 @@ impl DatabaseExt for CowBackend<'_> { self.backend.ensure_fork_id(id) } - fn diagnose_revert( - &self, - callee: Address, - journaled_state: &JournaledState, - ) -> Option { - self.backend.diagnose_revert(callee, journaled_state) + fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option { + self.backend.diagnose_revert(callee, evm_state) } fn load_allocs( @@ -245,7 +238,7 @@ impl DatabaseExt for CowBackend<'_> { allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), BackendError> { - self.backend_mut(&Env::default().as_env_mut()).load_allocs(allocs, journaled_state) + self.backend.to_mut().load_allocs(allocs, journaled_state) } fn clone_account( @@ -254,11 +247,7 @@ impl DatabaseExt for CowBackend<'_> { target: &Address, journaled_state: &mut JournaledState, ) -> Result<(), BackendError> { - self.backend_mut(&Env::default().as_env_mut()).clone_account( - source, - target, - journaled_state, - ) + self.backend.to_mut().clone_account(source, target, journaled_state) } fn is_persistent(&self, acc: &Address) -> bool { diff --git a/crates/evm/core/src/backend/error.rs b/crates/evm/core/src/backend/error.rs index 42456cceadd16..d6a08fa19e6b4 100644 --- a/crates/evm/core/src/backend/error.rs +++ b/crates/evm/core/src/backend/error.rs @@ -24,8 +24,6 @@ pub enum BackendError { For a test environment, you can use `etch` to place the required bytecode at that address." )] MissingCreate2Deployer, - #[error("{0}")] - Other(String), } impl BackendError { diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 6429ceb3db23f..b1b937736b5f7 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -11,19 +11,24 @@ use crate::{ use alloy_consensus::Typed2718; use alloy_evm::Evm; use alloy_genesis::GenesisAccount; -use alloy_network::{AnyRpcBlock, AnyTxEnvelope, TransactionResponse}; +use alloy_network::{ + AnyNetwork, AnyRpcBlock, AnyRpcTransaction, AnyTxEnvelope, TransactionResponse, +}; use alloy_primitives::{Address, B256, TxKind, U256, keccak256, uint}; use alloy_rpc_types::{BlockNumberOrTag, Transaction, TransactionRequest}; use eyre::Context; use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender}; pub use foundry_fork_db::{BlockchainDb, SharedBackend, cache::BlockchainDbMeta}; use revm::{ - Database, DatabaseCommit, JournalEntry, + Database, DatabaseCommit, Journal, JournalEntry, bytecode::Bytecode, context::JournalInner, - context_interface::{block::BlobExcessGasAndPrice, result::ResultAndState}, + context_interface::{ + block::BlobExcessGasAndPrice, journaled_state::account::JournaledAccountTr, + result::ResultAndState, + }, database::{CacheDB, DatabaseRef}, - inspector::NoOpInspector, + inspector::{JournalExt, NoOpInspector}, precompile::{PrecompileSpecId, Precompiles}, primitives::{HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId}, state::{Account, AccountInfo, EvmState, EvmStorageSlot}, @@ -271,11 +276,7 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug /// the contract is deployed there. /// /// Returns a more useful error message if that's the case - fn diagnose_revert( - &self, - callee: Address, - journaled_state: &JournaledState, - ) -> Option; + fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option; /// Loads the account allocs from the given `allocs` map into the passed [JournaledState]. /// @@ -379,6 +380,38 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug struct _ObjectSafe(dyn DatabaseExt); +/// Extension trait for [`Journal`] providing borrow splitting and state replacement. +/// +/// Generic code accesses the journal via `ctx.journal_mut()` which returns `&mut impl JournalTr`. +/// This trait adds the ability to split the journal into its database and inner state components, +/// enabling direct [`DatabaseExt`] method calls with zero-copy borrow splitting. +pub trait FoundryJournalExt: JournalExt { + /// Returns mutable references to the database and journal inner state. + /// + /// Enables calling [`DatabaseExt`] methods directly, e.g.: + /// ```ignore + /// let (journal, env) = ctx.journal_and_env_mut(); // FoundryContextExt + /// let (db, inner) = journal.as_db_and_inner(); // FoundryJournalExt + /// db.select_fork(id, &env, inner)?; // DatabaseExt + /// ``` + fn as_db_and_inner(&mut self) -> (&mut dyn DatabaseExt, &mut JournaledState); + + /// Replaces the journal inner state. + /// + /// Used by sub-EVM execution to write back modified state after running a closure. + fn set_inner(&mut self, inner: JournaledState); +} + +impl FoundryJournalExt for Journal { + fn as_db_and_inner(&mut self) -> (&mut dyn DatabaseExt, &mut JournaledState) { + (&mut self.database, &mut self.inner) + } + + fn set_inner(&mut self, inner: JournaledState) { + self.inner = inner; + } +} + /// Provides the underlying `revm::Database` implementation. /// /// A `Backend` can be initialised in two forms: @@ -435,7 +468,7 @@ struct _ObjectSafe(dyn DatabaseExt); #[must_use] pub struct Backend { /// The access point for managing forks - forks: MultiFork, + forks: MultiFork, // The default in memory db mem_db: FoundryEvmInMemoryDB, /// The journaled_state to use to initialize new forks with @@ -478,7 +511,7 @@ impl Backend { /// database. /// /// Prefer using [`spawn`](Self::spawn) instead. - pub fn new(forks: MultiFork, fork: Option) -> eyre::Result { + pub fn new(forks: MultiFork, fork: Option) -> eyre::Result { trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend"); // Note: this will take of registering the `fork` let inner = BackendInner { @@ -899,7 +932,7 @@ impl Backend { trace!(tx=?tx.tx_hash(), "committing transaction"); commit_transaction( - &tx.inner, + tx, &mut env.as_env_mut(), journaled_state, fork, @@ -1293,7 +1326,7 @@ impl DatabaseExt for Backend { let fork = self.inner.get_fork_by_id_mut(id)?; commit_transaction( - &tx.inner, + &tx, &mut env.as_env_mut(), journaled_state, fork, @@ -1315,7 +1348,7 @@ impl DatabaseExt for Backend { self.commit(journaled_state.state.clone()); let res = { - configure_tx_req_env(&mut env.as_env_mut(), tx, None)?; + configure_tx_req_env(&mut env.as_env_mut(), tx)?; let mut db = self.clone(); let mut evm = new_evm_with_inspector(&mut db, env.to_owned(), inspector); @@ -1352,11 +1385,7 @@ impl DatabaseExt for Backend { self.inner.ensure_fork_id(id) } - fn diagnose_revert( - &self, - callee: Address, - journaled_state: &JournaledState, - ) -> Option { + fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option { let active_id = self.active_fork_id()?; let active_fork = self.active_fork()?; @@ -1366,7 +1395,7 @@ impl DatabaseExt for Backend { return None; } - if !active_fork.is_contract(callee) && !is_contract_in_state(journaled_state, callee) { + if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) { // no contract for `callee` available on current fork, check if available on other forks let mut available_on = Vec::new(); for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) { @@ -1603,7 +1632,7 @@ impl Fork { { return true; } - is_contract_in_state(&self.journaled_state, acc) + is_contract_in_state(&self.journaled_state.state, acc) } } @@ -1933,12 +1962,8 @@ fn merge_db_account_data( } /// Returns true of the address is a contract -fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool { - journaled_state - .state - .get(&acc) - .map(|acc| acc.info.code_hash != KECCAK_EMPTY) - .unwrap_or_default() +fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool { + evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default() } /// Updates the env's block with the block's data @@ -1962,7 +1987,7 @@ fn update_env_block(env: &mut EnvMut<'_>, block: &AnyRpcBlock) { /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an inspector. fn commit_transaction( - tx: &Transaction, + tx: &AnyRpcTransaction, env: &mut EnvMut<'_>, journaled_state: &mut JournaledState, fork: &mut Fork, diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 9fe6a7b9b5c3e..0e28d92d91cae 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -54,7 +54,7 @@ pub fn decode_console_logs(logs: &[Log]) -> Vec { /// /// This function returns [None] if it is not a DSTest log or the result of a Hardhat /// `console.log`. -fn decode_console_log(log: &Log) -> Option { +pub fn decode_console_log(log: &Log) -> Option { console::ds::ConsoleEvents::decode_log(log).ok().map(|decoded| decoded.to_string()) } diff --git a/crates/evm/core/src/either_evm.rs b/crates/evm/core/src/either_evm.rs index 58f6d3063ffd1..40a9953e68fe3 100644 --- a/crates/evm/core/src/either_evm.rs +++ b/crates/evm/core/src/either_evm.rs @@ -286,6 +286,6 @@ where /// Maps [`EvmEnv`] to [`EvmEnv`]. fn map_env(env: EvmEnv) -> EvmEnv { let eth_spec_id = env.spec_id().into_eth_spec(); - let cfg = env.cfg_env.with_spec(eth_spec_id); + let cfg = env.cfg_env.with_spec_and_mainnet_gas_params(eth_spec_id); EvmEnv { cfg_env: cfg, block_env: env.block_env } } diff --git a/crates/evm/core/src/env.rs b/crates/evm/core/src/env.rs index 000f3ad06b0ed..d21bc4bc57b8c 100644 --- a/crates/evm/core/src/env.rs +++ b/crates/evm/core/src/env.rs @@ -1,7 +1,8 @@ pub use alloy_evm::EvmEnv; use revm::{ - Context, Database, Journal, JournalEntry, - context::{BlockEnv, CfgEnv, JournalInner, JournalTr, TxEnv}, + Context, Database, + context::{BlockEnv, CfgEnv, JournalTr, TxEnv}, + context_interface::ContextTr, primitives::hardfork::SpecId, }; @@ -48,6 +49,17 @@ impl EnvMut<'_> { tx: self.tx.to_owned(), } } + + /// Writes an owned [`Env`] back into the context. + /// + /// Counterpart to [`to_owned`](Self::to_owned): completes the read/write pair so callers + /// that receive an updated [`Env`] by value (e.g. after a fork switch or snapshot revert) + /// can apply it without manually assigning each field. + pub fn set_env(&mut self, env: Env) { + *self.block = env.evm_env.block_env; + *self.cfg = env.evm_env.cfg_env; + *self.tx = env.tx; + } } pub trait AsEnvMut { @@ -78,25 +90,62 @@ impl, C> AsEnvMut } } -pub trait ContextExt { - type DB: Database; - - fn as_db_env_and_journal( - &mut self, - ) -> (&mut Self::DB, &mut JournalInner, EnvMut<'_>); +/// Extension trait providing mutable field access to block, tx, and cfg environments. +/// +/// [`ContextTr`] only exposes immutable references for block, tx, and cfg. +/// Cheatcodes like `vm.warp()`, `vm.roll()`, `vm.chainId()` need to mutate these fields. +/// +/// Also provides [`journal_and_env_mut`](FoundryContextExt::journal_and_env_mut) for +/// simultaneous mutable access to journal and env — needed because calling `journal_mut()` +/// and `block_mut()` separately would create conflicting borrows on `&mut self`. +pub trait FoundryContextExt: ContextTr { + /// Mutable reference to the block environment. + fn block_mut(&mut self) -> &mut BlockEnv; + /// Mutable reference to the transaction environment. + fn tx_mut(&mut self) -> &mut TxEnv; + /// Mutable reference to the configuration environment. + fn cfg_mut(&mut self) -> &mut CfgEnv; + + /// Returns a cloned snapshot of the current environment. + fn to_env(&self) -> Env; + + /// Applies an owned [`Env`] to this context, replacing block, cfg, and tx. + fn apply_env(&mut self, env: Env); + + /// Returns mutable references to the journal and environment simultaneously. + /// + /// This solves the borrow-splitting problem: calling `self.journal_mut()` and + /// `self.block_mut()` separately would both borrow `&mut self`. This method + /// splits the borrows at the field level in one call. + fn journal_and_env_mut(&mut self) -> (&mut Self::Journal, EnvMut<'_>); } -impl ContextExt - for Context, C> +impl, C> FoundryContextExt + for Context { - type DB = DB; - - fn as_db_env_and_journal( - &mut self, - ) -> (&mut Self::DB, &mut JournalInner, EnvMut<'_>) { + fn block_mut(&mut self) -> &mut BlockEnv { + &mut self.block + } + fn tx_mut(&mut self) -> &mut TxEnv { + &mut self.tx + } + fn cfg_mut(&mut self) -> &mut CfgEnv { + &mut self.cfg + } + fn to_env(&self) -> Env { + Env { + evm_env: EvmEnv { cfg_env: self.cfg.clone(), block_env: self.block.clone() }, + tx: self.tx.clone(), + } + } + fn apply_env(&mut self, env: Env) { + self.block = env.evm_env.block_env; + self.cfg = env.evm_env.cfg_env; + self.tx = env.tx; + } + fn journal_and_env_mut(&mut self) -> (&mut J, EnvMut<'_>) { ( - &mut self.journaled_state.database, - &mut self.journaled_state.inner, + &mut self.journaled_state, EnvMut { block: &mut self.block, cfg: &mut self.cfg, tx: &mut self.tx }, ) } diff --git a/crates/evm/core/src/evm.rs b/crates/evm/core/src/evm.rs index 379b1ba1f8414..24f24e18bd104 100644 --- a/crates/evm/core/src/evm.rs +++ b/crates/evm/core/src/evm.rs @@ -4,7 +4,9 @@ use std::{ }; use crate::{ - Env, InspectorExt, backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, + Env, FoundryContextExt, InspectorExt, + backend::{DatabaseExt, FoundryJournalExt, JournaledState}, + constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, }; use alloy_consensus::constants::KECCAK_EMPTY; use alloy_evm::{Evm, EvmEnv, eth::EthEvmContext, precompiles::PrecompilesMap}; @@ -101,16 +103,16 @@ fn get_create2_factory_call_inputs( inputs: &CreateInputs, deployer: Address, ) -> CallInputs { - let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat(); + let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat(); CallInputs { - caller: inputs.caller, + caller: inputs.caller(), bytecode_address: deployer, known_bytecode: None, target_address: deployer, scheme: CallScheme::Call, - value: CallValue::Transfer(inputs.value), + value: CallValue::Transfer(inputs.value()), input: CallInput::Bytes(calldata.into()), - gas_limit: inputs.gas_limit, + gas_limit: inputs.gas_limit(), is_static: false, return_memory_offset: 0..0, } @@ -252,6 +254,131 @@ impl DerefMut for FoundryEvm<'_, I> { } } +/// Object-safe trait exposing the operations that cheatcode nested EVM closures need. +/// +/// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.) +/// so that cheatcode impls can build and run nested EVMs without knowing the concrete type. +pub trait NestedEvm { + /// Returns a mutable reference to the journal inner state (`JournaledState`). + fn journal_inner_mut(&mut self) -> &mut JournaledState; + + /// Runs a single execution frame (create or call) through the EVM handler loop. + fn run_execution(&mut self, frame: FrameInput) -> Result>; + + /// Executes a full transaction with the given `TxEnv`. + fn transact( + &mut self, + tx: TxEnv, + ) -> Result, EVMError>; +} + +impl NestedEvm for FoundryEvm<'_, I> { + fn journal_inner_mut(&mut self) -> &mut JournaledState { + &mut self.inner.ctx.journaled_state.inner + } + + fn run_execution(&mut self, frame: FrameInput) -> Result> { + FoundryEvm::run_execution(self, frame) + } + + fn transact( + &mut self, + tx: TxEnv, + ) -> Result, EVMError> { + Evm::transact_raw(self, tx) + } +} + +/// Extension trait for building nested EVMs from a generic context. +/// +/// Each network provides its own impl that constructs the right EVM type +/// (instructions, precompiles) for that network. +pub trait NestedEvmExt: FoundryContextExt { + /// Clones the current context (env + journal), builds a nested EVM, runs the closure, + /// and writes modified state back. Used by `exec_create` (`deployCode`). + fn with_nested_evm( + &mut self, + inspector: &mut dyn InspectorExt, + f: impl FnOnce(&mut dyn NestedEvm) -> Result>, + ) -> Result> + where + Self::Journal: FoundryJournalExt; + + /// Creates a fresh nested EVM from a database, environment, and inspector. + /// Used by `executeTransactionCall`. + fn new_nested_evm<'a>( + db: &'a mut dyn DatabaseExt, + env: Env, + inspector: &'a mut dyn InspectorExt, + ) -> Box + where + Self: Sized; +} + +impl, J: JournalTr, C> NestedEvmExt + for Context +{ + fn with_nested_evm( + &mut self, + inspector: &mut dyn InspectorExt, + f: impl FnOnce(&mut dyn NestedEvm) -> Result>, + ) -> Result> + where + Self::Journal: FoundryJournalExt, + { + // Clone env fields via field-level borrows on Context. + let block_clone = self.block.clone(); + let cfg_clone = self.cfg.clone(); + let tx_clone = self.tx.clone(); + let error = std::mem::replace(&mut self.error, Ok(())); + + // Split journal into (db, inner) and clone the inner state. + let (db, journal_inner) = self.journaled_state.as_db_and_inner(); + let journal_inner_clone = journal_inner.clone(); + + // Build a nested EVM context. The db reference borrows self.journaled_state. + let ctx = EthEvmContext { + block: block_clone, + cfg: cfg_clone, + tx: tx_clone, + journaled_state: Journal { inner: journal_inner_clone, database: db }, + local: LocalContext::default(), + chain: (), + error, + }; + + let mut evm = new_evm_with_existing_context(ctx, inspector); + let res = f(&mut evm); + + // Destructure the nested EVM context to release the db borrow. + let sub_ctx = evm.into_context(); + let Context { block, cfg, tx, journaled_state: sub_journal, error: sub_error, .. } = + sub_ctx; + // Dropping `database` releases the mutable borrow on self.journaled_state. + let Journal { inner: sub_inner, database: _ } = sub_journal; + + // Write back modified state. + self.block = block; + self.cfg = cfg; + self.tx = tx; + self.error = sub_error; + self.journaled_state.set_inner(sub_inner); + + res + } + + fn new_nested_evm<'a>( + db: &'a mut dyn DatabaseExt, + env: Env, + inspector: &'a mut dyn InspectorExt, + ) -> Box + where + Self: Sized, + { + Box::new(new_evm_with_inspector(db, env, inspector)) + } +} + pub struct FoundryHandler<'db, I: InspectorExt> { create2_overrides: Vec<(usize, CallInputs)>, _phantom: PhantomData<(&'db mut dyn DatabaseExt, I)>, @@ -286,12 +413,12 @@ impl<'db, I: InspectorExt> FoundryHandler<'db, I> { init: &mut FrameInit, ) -> Result, ::Error> { if let FrameInput::Create(inputs) = &init.frame_input - && let CreateScheme::Create2 { salt } = inputs.scheme + && let CreateScheme::Create2 { salt } = inputs.scheme() { let (ctx, inspector) = evm.ctx_inspector(); - if inspector.should_use_create2_factory(ctx, inputs) { - let gas_limit = inputs.gas_limit; + if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) { + let gas_limit = inputs.gas_limit(); // Get CREATE2 deployer. let create2_deployer = evm.inspector().create2_deployer(); diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 6a53a19843b1b..65686ade1aada 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -4,6 +4,7 @@ use crate::{ backend::{RevertStateSnapshotAction, StateSnapshot}, state_snapshot::StateSnapshots, }; +use alloy_network::Network; use alloy_primitives::{Address, B256, U256, map::HashMap}; use alloy_rpc_types::BlockId; use foundry_fork_db::{BlockchainDb, DatabaseError, SharedBackend}; @@ -23,28 +24,28 @@ use std::sync::Arc; /// This database uses the `backend` for read and the `db` for write operations. But note the /// `backend` will also write (missing) data to the `db` in the background #[derive(Clone, Debug)] -pub struct ForkedDatabase { +pub struct ForkedDatabase { /// Responsible for fetching missing data. /// /// This is responsible for getting data. - backend: SharedBackend, + backend: SharedBackend, /// Cached Database layer, ensures that changes are not written to the database that /// exclusively stores the state of the remote client. /// /// This separates Read/Write operations /// - reads from the `SharedBackend as DatabaseRef` writes to the internal cache storage. - cache_db: CacheDB, + cache_db: CacheDB>, /// Contains all the data already fetched. /// /// This exclusively stores the _unchanged_ remote client state. db: BlockchainDb, /// Holds the state snapshots of a blockchain. - state_snapshots: Arc>>, + state_snapshots: Arc>>>, } -impl ForkedDatabase { +impl ForkedDatabase { /// Creates a new instance of this DB - pub fn new(backend: SharedBackend, db: BlockchainDb) -> Self { + pub fn new(backend: SharedBackend, db: BlockchainDb) -> Self { Self { cache_db: CacheDB::new(backend.clone()), backend, @@ -53,15 +54,15 @@ impl ForkedDatabase { } } - pub fn database(&self) -> &CacheDB { + pub fn database(&self) -> &CacheDB> { &self.cache_db } - pub fn database_mut(&mut self) -> &mut CacheDB { + pub fn database_mut(&mut self) -> &mut CacheDB> { &mut self.cache_db } - pub fn state_snapshots(&self) -> &Arc>> { + pub fn state_snapshots(&self) -> &Arc>>> { &self.state_snapshots } @@ -93,7 +94,7 @@ impl ForkedDatabase { &self.db } - pub fn create_state_snapshot(&self) -> ForkDbStateSnapshot { + pub fn create_state_snapshot(&self) -> ForkDbStateSnapshot { let db = self.db.db(); let state_snapshot = StateSnapshot { accounts: db.accounts.read().clone(), @@ -150,7 +151,7 @@ impl ForkedDatabase { } } -impl Database for ForkedDatabase { +impl Database for ForkedDatabase { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { @@ -173,7 +174,7 @@ impl Database for ForkedDatabase { } } -impl DatabaseRef for ForkedDatabase { +impl DatabaseRef for ForkedDatabase { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -193,7 +194,7 @@ impl DatabaseRef for ForkedDatabase { } } -impl DatabaseCommit for ForkedDatabase { +impl DatabaseCommit for ForkedDatabase { fn commit(&mut self, changes: HashMap) { self.database_mut().commit(changes) } @@ -203,12 +204,12 @@ impl DatabaseCommit for ForkedDatabase { /// /// This mimics `revm::CacheDB` #[derive(Clone, Debug)] -pub struct ForkDbStateSnapshot { - pub local: CacheDB, +pub struct ForkDbStateSnapshot { + pub local: CacheDB>, pub state_snapshot: StateSnapshot, } -impl ForkDbStateSnapshot { +impl ForkDbStateSnapshot { fn get_storage(&self, address: Address, index: U256) -> Option { self.local .cache @@ -222,7 +223,7 @@ impl ForkDbStateSnapshot { // This `DatabaseRef` implementation works similar to `CacheDB` which prioritizes modified elements, // and uses another db as fallback // We prioritize stored changed accounts/storage -impl DatabaseRef for ForkDbStateSnapshot { +impl DatabaseRef for ForkDbStateSnapshot { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 186183b9a8d2a..728d380153257 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -6,6 +6,7 @@ use super::CreateFork; use crate::Env; use alloy_consensus::BlockHeader; +use alloy_network::Network; use alloy_primitives::{U256, map::HashMap}; use alloy_provider::network::BlockResponse; use foundry_config::Config; @@ -67,14 +68,14 @@ impl> From for ForkId { /// Can send requests to the `MultiForkHandler` to create forks. #[derive(Clone, Debug)] #[must_use] -pub struct MultiFork { +pub struct MultiFork { /// Channel to send `Request`s to the handler. - handler: Sender, + handler: Sender>, /// Ensures that all rpc resources get flushed properly. - _shutdown: Arc, + _shutdown: Arc>, } -impl MultiFork { +impl MultiFork { /// Creates a new pair and spawns the `MultiForkHandler` on a background thread. pub fn spawn() -> Self { trace!(target: "fork::multi", "spawning multifork"); @@ -116,7 +117,7 @@ impl MultiFork { /// /// Use [`spawn`](Self::spawn) instead. #[doc(hidden)] - pub fn new() -> (Self, MultiForkHandler) { + pub fn new() -> (Self, MultiForkHandler) { let (handler, handler_rx) = channel(1); let _shutdown = Arc::new(ShutDownMultiFork { handler: Some(handler.clone()) }); (Self { handler, _shutdown }, MultiForkHandler::new(handler_rx)) @@ -125,7 +126,7 @@ impl MultiFork { /// Returns a fork backend. /// /// If no matching fork backend exists it will be created. - pub fn create_fork(&self, fork: CreateFork) -> eyre::Result<(ForkId, SharedBackend, Env)> { + pub fn create_fork(&self, fork: CreateFork) -> eyre::Result<(ForkId, SharedBackend, Env)> { trace!("Creating new fork, url={}, block={:?}", fork.url, fork.evm_opts.fork_block_number); let (sender, rx) = oneshot_channel(); let req = Request::CreateFork(Box::new(fork), sender); @@ -140,7 +141,7 @@ impl MultiFork { &self, fork: ForkId, block: u64, - ) -> eyre::Result<(ForkId, SharedBackend, Env)> { + ) -> eyre::Result<(ForkId, SharedBackend, Env)> { trace!(?fork, ?block, "rolling fork"); let (sender, rx) = oneshot_channel(); let req = Request::RollFork(fork, block, sender); @@ -181,7 +182,7 @@ impl MultiFork { /// Returns the corresponding fork if it exists. /// /// Returns `None` if no matching fork backend is available. - pub fn get_fork(&self, id: impl Into) -> eyre::Result> { + pub fn get_fork(&self, id: impl Into) -> eyre::Result>> { let id = id.into(); trace!(?id, "get fork backend"); let (sender, rx) = oneshot_channel(); @@ -201,21 +202,20 @@ impl MultiFork { } } -type Handler = BackendHandler; -type CreateFuture = - Pin> + Send>>; -type CreateSender = OneshotSender>; +type CreateFuture = + Pin, BackendHandler)>> + Send>>; +type CreateSender = OneshotSender, Env)>>; type GetEnvSender = OneshotSender>; /// Request that's send to the handler. #[derive(Debug)] -enum Request { +enum Request { /// Creates a new ForkBackend. - CreateFork(Box, CreateSender), + CreateFork(Box, CreateSender), /// Returns the Fork backend for the `ForkId` if it exists. - GetFork(ForkId, OneshotSender>), + GetFork(ForkId, OneshotSender>>), /// Adjusts the block that's being forked, by creating a new fork at the new block. - RollFork(ForkId, u64, CreateSender), + RollFork(ForkId, u64, CreateSender), /// Returns the environment of the fork. GetEnv(ForkId, GetEnvSender), /// Updates the block number and timestamp of the fork. @@ -228,37 +228,37 @@ enum Request { GetForkUrl(ForkId, OneshotSender>), } -enum ForkTask { +enum ForkTask { /// Contains the future that will establish a new fork. - Create(CreateFuture, ForkId, CreateSender, Vec), + Create(CreateFuture, ForkId, CreateSender, Vec>), } /// The type that manages connections in the background. #[must_use = "futures do nothing unless polled"] -pub struct MultiForkHandler { +pub struct MultiForkHandler { /// Incoming requests from the `MultiFork`. - incoming: Fuse>, + incoming: Fuse>>, /// All active handlers. /// /// It's expected that this list will be rather small (<10). - handlers: Vec<(ForkId, Handler)>, + handlers: Vec<(ForkId, BackendHandler)>, // tasks currently in progress - pending_tasks: Vec, + pending_tasks: Vec>, /// All _unique_ forkids mapped to their corresponding backend. /// /// Note: The backend can be shared by multiple ForkIds if the target the same provider and /// block number. - forks: HashMap, + forks: HashMap>, /// Optional periodic interval to flush rpc cache. flush_cache_interval: Option, } -impl MultiForkHandler { - fn new(incoming: Receiver) -> Self { +impl MultiForkHandler { + fn new(incoming: Receiver>) -> Self { Self { incoming: incoming.fuse(), handlers: Default::default(), @@ -276,19 +276,16 @@ impl MultiForkHandler { } /// Returns the list of additional senders of a matching task for the given id, if any. - #[expect(irrefutable_let_patterns)] - fn find_in_progress_task(&mut self, id: &ForkId) -> Option<&mut Vec> { - for task in &mut self.pending_tasks { - if let ForkTask::Create(_, in_progress, _, additional) = task - && in_progress == id - { + fn find_in_progress_task(&mut self, id: &ForkId) -> Option<&mut Vec>> { + for ForkTask::Create(_, in_progress, _, additional) in &mut self.pending_tasks { + if in_progress == id { return Some(additional); } } None } - fn create_fork(&mut self, fork: CreateFork, sender: CreateSender) { + fn create_fork(&mut self, fork: CreateFork, sender: CreateSender) { let fork_id = ForkId::new(&fork.url, fork.evm_opts.fork_block_number); trace!(?fork_id, "created new forkId"); @@ -306,9 +303,9 @@ impl MultiForkHandler { fn insert_new_fork( &mut self, fork_id: ForkId, - fork: CreatedFork, - sender: CreateSender, - additional_senders: Vec, + fork: CreatedFork, + sender: CreateSender, + additional_senders: Vec>, ) { self.forks.insert(fork_id.clone(), fork.clone()); let _ = sender.send(Ok((fork_id.clone(), fork.backend.clone(), fork.opts.env.clone()))); @@ -336,7 +333,7 @@ impl MultiForkHandler { } } - fn on_request(&mut self, req: Request) { + fn on_request(&mut self, req: Request) { match req { Request::CreateFork(fork, sender) => self.create_fork(*fork, sender), Request::GetFork(fork_id, sender) => { @@ -380,7 +377,7 @@ impl MultiForkHandler { // Drives all handler to completion. // This future will finish once all underlying BackendHandler are completed. -impl Future for MultiForkHandler { +impl Future for MultiForkHandler { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -481,18 +478,18 @@ impl Future for MultiForkHandler { /// Tracks the created Fork #[derive(Debug, Clone)] -struct CreatedFork { +struct CreatedFork { /// How the fork was initially created. opts: CreateFork, /// Copy of the sender. - backend: SharedBackend, + backend: SharedBackend, /// How many consumers there are, since a `SharedBacked` can be used by multiple /// consumers. num_senders: Arc, } -impl CreatedFork { - pub fn new(opts: CreateFork, backend: SharedBackend) -> Self { +impl CreatedFork { + pub fn new(opts: CreateFork, backend: SharedBackend) -> Self { Self { opts, backend, num_senders: Arc::new(AtomicUsize::new(1)) } } @@ -514,11 +511,11 @@ impl CreatedFork { /// This type intentionally does not implement `Clone` since it's intended that there's only once /// instance. #[derive(Debug)] -struct ShutDownMultiFork { - handler: Option>, +struct ShutDownMultiFork { + handler: Option>>, } -impl Drop for ShutDownMultiFork { +impl Drop for ShutDownMultiFork { fn drop(&mut self) { trace!(target: "fork::multi", "initiating shutdown"); let (sender, rx) = oneshot_channel(); @@ -535,11 +532,14 @@ impl Drop for ShutDownMultiFork { /// Creates a new fork. /// /// This will establish a new `Provider` to the endpoint and return the Fork Backend. -async fn create_fork(mut fork: CreateFork) -> eyre::Result<(ForkId, CreatedFork, Handler)> { +async fn create_fork( + mut fork: CreateFork, +) -> eyre::Result<(ForkId, CreatedFork, BackendHandler)> { let provider = fork.evm_opts.fork_provider_with_url(&fork.url)?; // Initialise the fork environment. - let (env, block) = fork.evm_opts.fork_evm_env_with_provider(&fork.url, &provider).await?; + let (env, block) = + fork.evm_opts.fork_evm_env_with_provider::<_, N>(&fork.url, &provider).await?; fork.env = env; let meta = BlockchainDbMeta::new(fork.env.evm_env.block_env.clone(), fork.url.clone()); diff --git a/crates/evm/core/src/hardfork.rs b/crates/evm/core/src/hardfork.rs index fb97db475584d..0176f562d55af 100644 --- a/crates/evm/core/src/hardfork.rs +++ b/crates/evm/core/src/hardfork.rs @@ -113,6 +113,7 @@ mod tests { // Test latest hardforks assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Cancun), SpecId::CANCUN); assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Prague), SpecId::PRAGUE); + assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Osaka), SpecId::OSAKA); } #[test] diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 153748c7b8662..6982ff20dc163 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -43,19 +43,18 @@ pub mod precompiles; pub mod state_snapshot; pub mod utils; -/// An extension trait that allows us to add additional hooks to Inspector for later use in -/// handlers. +/// Foundry-specific inspector methods, decoupled from any particular EVM context type. +/// +/// This trait holds Foundry-specific extensions (create2 factory, console logging, +/// network config, deployer address). It has no `Inspector` supertrait so it can +/// be used in generic code with `I: FoundryInspectorExt + Inspector`. #[auto_impl(&mut, Box)] -pub trait InspectorExt: for<'a> Inspector> { +pub trait FoundryInspectorExt { /// 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 EthEvmContext<&mut dyn DatabaseExt>, - _inputs: &CreateInputs, - ) -> bool { + fn should_use_create2_factory(&mut self, _depth: usize, _inputs: &CreateInputs) -> bool { false } @@ -75,6 +74,20 @@ pub trait InspectorExt: for<'a> Inspector } } -impl InspectorExt for NoOpInspector {} +/// Combined trait: `Inspector>` + [`FoundryInspectorExt`]. +/// +/// Used as a trait object (`dyn InspectorExt`) in backend code that is Eth-specific. +/// For generic multi-network code, use `I: FoundryInspectorExt + Inspector` instead. +pub trait InspectorExt: + for<'a> Inspector> + FoundryInspectorExt +{ +} + +impl InspectorExt for T where + T: for<'a> Inspector> + FoundryInspectorExt +{ +} + +impl FoundryInspectorExt for NoOpInspector {} -impl InspectorExt for AccessListInspector {} +impl FoundryInspectorExt for AccessListInspector {} diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index a5fa6f079b301..f2868ea84cd8e 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -4,14 +4,11 @@ use crate::{ constants::DEFAULT_CREATE2_DEPLOYER, fork::{CreateFork, configure_env}, }; -use alloy_network::Network; +use alloy_network::{AnyNetwork, Network}; use alloy_primitives::{Address, B256, U256}; -use alloy_provider::{Provider, network::AnyRpcBlock}; +use alloy_provider::{Provider, RootProvider, network::AnyRpcBlock}; use eyre::WrapErr; -use foundry_common::{ - ALCHEMY_FREE_TIER_CUPS, - provider::{ProviderBuilder, RetryProvider}, -}; +use foundry_common::{ALCHEMY_FREE_TIER_CUPS, provider::ProviderBuilder}; use foundry_config::{Chain, Config, GasLimit}; use foundry_evm_networks::NetworkConfigs; use revm::context::{BlockEnv, TxEnv}; @@ -116,8 +113,12 @@ impl Default for EvmOpts { } impl EvmOpts { - /// Returns a `RetryProvider` for the given fork URL configured with options in `self`. - pub fn fork_provider_with_url(&self, fork_url: &str) -> eyre::Result { + /// Returns a `RootProvider` for the given fork URL configured with options in `self` and + /// annotated `Network` type. + pub fn fork_provider_with_url( + &self, + fork_url: &str, + ) -> eyre::Result> { ProviderBuilder::new(fork_url) .maybe_max_retry(self.fork_retries) .maybe_initial_backoff(self.fork_retry_backoff) @@ -141,7 +142,7 @@ impl EvmOpts { /// Returns the `revm::Env` that is configured with settings retrieved from the endpoint, /// and the block that was used to configure the environment. pub async fn fork_evm_env(&self, fork_url: &str) -> eyre::Result<(crate::Env, AnyRpcBlock)> { - let provider = self.fork_provider_with_url(fork_url)?; + let provider = self.fork_provider_with_url::(fork_url)?; self.fork_evm_env_with_provider(fork_url, &provider).await } @@ -257,7 +258,7 @@ impl EvmOpts { /// Returns the chain ID from the RPC, if any. pub async fn get_remote_chain_id(&self) -> Option { if let Some(url) = &self.fork_url - && let Ok(provider) = self.fork_provider_with_url(url) + && let Ok(provider) = self.fork_provider_with_url::(url) { trace!(?url, "retrieving chain via eth_chainId"); diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index efcda2f08e15c..b25a202e0514c 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -3,10 +3,10 @@ use alloy_chains::Chain; use alloy_consensus::{BlockHeader, private::alloy_eips::eip7840::BlobParams}; use alloy_hardforks::EthereumHardfork; use alloy_json_abi::{Function, JsonAbi}; -use alloy_network::{AnyTxEnvelope, TransactionResponse}; -use alloy_primitives::{Address, B256, ChainId, Selector, TxKind, U256}; +use alloy_network::{AnyRpcTransaction, TransactionResponse}; +use alloy_primitives::{B256, ChainId, Selector, TxKind, U256}; use alloy_provider::{Network, network::BlockResponse}; -use alloy_rpc_types::{Transaction, TransactionRequest}; +use alloy_rpc_types::TransactionRequest; use foundry_config::NamedChain; use foundry_evm_networks::NetworkConfigs; use revm::primitives::{ @@ -138,26 +138,19 @@ pub fn get_function<'a>( /// Configures the env for the given RPC transaction. /// Accounts for an impersonated transaction by resetting the `env.tx.caller` field to `tx.from`. -pub fn configure_tx_env(env: &mut EnvMut<'_>, tx: &Transaction) { +pub fn configure_tx_env(env: &mut EnvMut<'_>, tx: &AnyRpcTransaction) { let from = tx.from(); - if let AnyTxEnvelope::Ethereum(tx) = &tx.inner.inner() { + if let Some(tx) = tx.as_envelope() { configure_tx_req_env( env, &TransactionRequest::from_transaction_with_sender(tx.clone(), from), - Some(from), ) .expect("cannot fail"); } } /// Configures the env for the given RPC transaction request. -/// `impersonated_from` is the address of the impersonated account. This helps account for an -/// impersonated transaction by resetting the `env.tx.caller` field to `impersonated_from`. -pub fn configure_tx_req_env( - env: &mut EnvMut<'_>, - tx: &TransactionRequest, - impersonated_from: Option

, -) -> eyre::Result<()> { +pub fn configure_tx_req_env(env: &mut EnvMut<'_>, tx: &TransactionRequest) -> eyre::Result<()> { // If no transaction type is provided, we need to infer it from the other fields. let tx_type = tx.transaction_type.unwrap_or_else(|| tx.minimal_tx_type() as u8); env.tx.tx_type = tx_type; @@ -183,13 +176,7 @@ pub fn configure_tx_req_env( // If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction env.tx.kind = to.unwrap_or(TxKind::Create); - // If the transaction is impersonated, we need to set the caller to the from - // address Ref: https://github.com/foundry-rs/foundry/issues/9541 - env.tx.caller = if let Some(caller) = impersonated_from { - caller - } else { - from.ok_or_else(|| eyre::eyre!("missing `from` field"))? - }; + env.tx.caller = from.ok_or_else(|| eyre::eyre!("missing `from` field"))?; env.tx.gas_limit = gas.ok_or_else(|| eyre::eyre!("missing `gas` field"))?; env.tx.nonce = nonce.unwrap_or_default(); env.tx.value = value.unwrap_or_default(); diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index b3e9553f40304..a69c19cbdb2bd 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -56,3 +56,5 @@ indicatif.workspace = true serde_json.workspace = true serde.workspace = true uuid.workspace = true +rayon.workspace = true +tokio.workspace = true diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index a62a9b9abcf1e..5a6cc4e91bafb 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -1,9 +1,10 @@ use crate::executors::{ DURATION_BETWEEN_METRICS_REPORT, EarlyExit, Executor, FuzzTestTimer, RawCallResult, + corpus::{GlobalCorpusMetrics, WorkerCorpus}, }; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes, Log, U256, map::HashMap}; +use alloy_primitives::{Address, Bytes, Log, U256, keccak256, map::HashMap}; use eyre::Result; use foundry_common::sh_println; use foundry_config::FuzzConfig; @@ -22,41 +23,134 @@ use foundry_evm_traces::SparsedTraceArena; use indicatif::ProgressBar; use proptest::{ strategy::Strategy, - test_runner::{TestCaseError, TestRunner}, + test_runner::{RngAlgorithm, TestCaseError, TestRng, TestRunner}, }; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde_json::json; -use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use std::{ + sync::{ + Arc, OnceLock, + atomic::{AtomicU32, Ordering}, + }, + time::{Instant, SystemTime, UNIX_EPOCH}, +}; mod types; -use crate::executors::corpus::CorpusManager; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; -/// Contains data collected during fuzz test runs. +/// Corpus syncs across workers every `SYNC_INTERVAL` runs. +const SYNC_INTERVAL: u32 = 1000; + +/// Minimum number of runs per worker. +/// This is mainly to reduce the overall number of rayon jobs. +const MIN_RUNS_PER_WORKER: u32 = 64; + #[derive(Default)] -struct FuzzTestData { - // Stores the first fuzz case. - first_case: Option, - // Stored gas usage per fuzz case. +struct WorkerState { + /// Worker identifier + id: usize, + /// First fuzz case this worker encountered (with global run number) + first_case: Option<(u32, FuzzCase)>, + /// Gas usage for all cases this worker ran gas_by_case: Vec<(u64, u64)>, - // Stores the result and calldata of the last failed call, if any. + /// Counterexample if this worker found one counterexample: (Bytes, RawCallResult), - // Stores up to `max_traces_to_collect` traces. + /// Traces collected by this worker + /// + /// Stores up to `max_traces_to_collect` which is `config.gas_report_samples / num_workers` traces: Vec, - // Stores breakpoints for the last fuzz case. + /// Last breakpoints from this worker breakpoints: Option, - // Stores coverage information for all fuzz cases. + /// Coverage collected by this worker coverage: Option, - // Stores logs for all fuzz cases (when show_logs is true) or just the last run (when show_logs - // is false) + /// Logs from all cases this worker ran logs: Vec, - // Deprecated cheatcodes mapped to their replacements. + /// Deprecated cheatcodes seen by this worker deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, - // Runs performed in fuzz test. + /// Number of runs this worker completed runs: u32, - // Current assume rejects of the fuzz run. - rejects: u32, - // Test failure. + /// Failure reason if this worker failed failure: Option, + /// Last run timestamp in milliseconds + /// + /// Used to identify which worker ran last and collect its traces and call breakpoints + last_run_timestamp: u128, + /// Failed corpus replays + failed_corpus_replays: usize, +} + +impl WorkerState { + fn new(worker_id: usize) -> Self { + Self { id: worker_id, ..Default::default() } + } +} + +/// Shared state for coordinating parallel fuzz workers +struct SharedFuzzState { + state: EvmFuzzState, + /// Total runs across workers + total_runs: Arc, + /// Found failure + /// + /// The worker that found the failure sets it's ID. + /// + /// This ID is then used to correctly extract the failure reason and counterexample. + failed_worker_id: OnceLock, + /// Total rejects across workers + total_rejects: Arc, + /// Fuzz timer + timer: FuzzTestTimer, + /// Global corpus metrics + global_corpus_metrics: GlobalCorpusMetrics, + + /// Global test suite early exit. + global_early_exit: EarlyExit, + /// Local fuzz early exit. + local_early_exit: EarlyExit, +} + +impl SharedFuzzState { + fn new(state: EvmFuzzState, timeout: Option, early_exit: EarlyExit) -> Self { + Self { + state, + total_runs: Arc::new(AtomicU32::new(0)), + failed_worker_id: OnceLock::new(), + total_rejects: Arc::new(AtomicU32::new(0)), + timer: FuzzTestTimer::new(timeout), + global_corpus_metrics: GlobalCorpusMetrics::default(), + global_early_exit: early_exit, + local_early_exit: EarlyExit::new(true), + } + } + + /// Increments the number of runs and returns the new value. + fn increment_runs(&self) -> u32 { + self.total_runs.fetch_add(1, Ordering::Relaxed) + 1 + } + + /// Increments and returns the new value of the number of rejected tests. + fn increment_rejects(&self) -> u32 { + self.total_rejects.fetch_add(1, Ordering::Relaxed) + 1 + } + + /// Returns `true` if the worker should continue running. + fn should_continue(&self) -> bool { + !(self.global_early_exit.should_stop() + || self.local_early_exit.should_stop() + || self.timer.is_timed_out()) + } + + /// Returns true if the worker was able to claim the failure, false if failure was set by + /// another worker + fn try_claim_failure(&self, worker_id: usize) -> bool { + let mut claimed = false; + let _ = self.failed_worker_id.get_or_init(|| { + claimed = true; + self.local_early_exit.record_failure(); + worker_id + }); + claimed + } } /// Wrapper around an [`Executor`] which provides fuzzing support using [`proptest`]. @@ -66,7 +160,7 @@ struct FuzzTestData { /// configuration which can be overridden via [environment variables](proptest::test_runner::Config) pub struct FuzzedExecutor { /// The EVM executor. - executor: Executor, + executor_f: Executor, /// The fuzzer runner: TestRunner, /// The account that calls tests. @@ -75,6 +169,8 @@ pub struct FuzzedExecutor { config: FuzzConfig, /// The persisted counterexample to be replayed, if any. persisted_failure: Option, + /// The number of parallel workers. + num_workers: usize, } impl FuzzedExecutor { @@ -86,7 +182,12 @@ impl FuzzedExecutor { config: FuzzConfig, persisted_failure: Option, ) -> Self { - Self { executor, runner, sender, config, persisted_failure } + let mut max_workers = Ord::max(1, config.runs / MIN_RUNS_PER_WORKER); + if config.runs == 0 { + max_workers = 0; + } + let num_workers = Ord::min(rayon::current_num_threads(), max_workers as usize); + Self { executor_f: executor, runner, sender, config, persisted_failure, num_workers } } /// Fuzzes the provided function, assuming it is available at the contract at `address` @@ -104,14 +205,210 @@ impl FuzzedExecutor { rd: &RevertDecoder, progress: Option<&ProgressBar>, early_exit: &EarlyExit, + tokio_handle: &tokio::runtime::Handle, ) -> Result { - let state = &state; - // Stores the fuzz test execution data. - let mut test_data = FuzzTestData::default(); + let shared_state = SharedFuzzState::new(state, self.config.timeout, early_exit.clone()); + + debug!(n = self.num_workers, "spawning workers"); + let workers = (0..self.num_workers) + .into_par_iter() + .map(|worker_id| { + let _guard = tokio_handle.enter(); + let _guard = info_span!("fuzz_worker", id = worker_id).entered(); + let timer = Instant::now(); + let r = self.run_worker( + worker_id, + func, + fuzz_fixtures, + address, + rd, + &shared_state, + progress, + ); + debug!("finished in {:?}", timer.elapsed()); + r + }) + .collect::>>()?; + + Ok(self.aggregate_results(workers, func, &shared_state)) + } + + /// Granular and single-step function that runs only one fuzz and returns either a `CaseOutcome` + /// or a `CounterExampleOutcome` + fn single_fuzz( + &self, + executor: &Executor, + address: Address, + calldata: Bytes, + coverage_metrics: &mut WorkerCorpus, + ) -> Result { + let mut call = executor + .call_raw(self.sender, address, calldata.clone(), U256::ZERO) + .map_err(|e| TestCaseError::fail(e.to_string()))?; + let new_coverage = coverage_metrics.merge_edge_coverage(&mut call); + coverage_metrics.process_inputs( + &[BasicTxDetails { + warp: None, + roll: None, + sender: self.sender, + call_details: CallDetails { target: address, calldata: calldata.clone() }, + }], + new_coverage, + ); + + // Handle `vm.assume`. + if call.result.as_ref() == MAGIC_ASSUME { + return Err(TestCaseError::reject(FuzzError::AssumeReject)); + } + + let (breakpoints, deprecated_cheatcodes) = + call.cheatcodes.as_ref().map_or_else(Default::default, |cheats| { + (cheats.breakpoints.clone(), cheats.deprecated.clone()) + }); + + // Consider call success if test should not fail on reverts and reverter is not the + // cheatcode or test address. + let success = if !self.config.fail_on_revert + && call + .reverter + .is_some_and(|reverter| reverter != address && reverter != CHEATCODE_ADDRESS) + { + true + } else { + executor.is_raw_call_mut_success(address, &mut call, false) + }; + + if success { + Ok(FuzzOutcome::Case(CaseOutcome { + case: FuzzCase { gas: call.gas_used, stipend: call.stipend }, + traces: call.traces, + coverage: call.line_coverage, + breakpoints, + logs: call.logs, + deprecated_cheatcodes, + })) + } else { + Ok(FuzzOutcome::CounterExample(CounterExampleOutcome { + exit_reason: call.exit_reason, + counterexample: (calldata, call), + breakpoints, + })) + } + } + + /// Aggregates the results from all workers + fn aggregate_results( + &self, + mut workers: Vec, + func: &Function, + shared_state: &SharedFuzzState, + ) -> FuzzTestResult { + let mut result = FuzzTestResult::default(); + if workers.is_empty() { + result.success = true; + return result; + } + + // Find first case and last run worker. Set `failed_corpus_replays`. + let mut first_case_candidate = None; + let mut last_run_worker = None; + for (i, worker) in workers.iter().enumerate() { + if let Some((run, ref case)) = worker.first_case + && first_case_candidate.as_ref().is_none_or(|&(r, _)| run < r) + { + first_case_candidate = Some((run, case.clone())); + } + + if last_run_worker.is_none_or(|(t, _)| worker.last_run_timestamp > t) { + last_run_worker = Some((worker.last_run_timestamp, i)); + } + + // Only set replays from master which is responsible for replaying persisted corpus. + if worker.id == 0 { + result.failed_corpus_replays = worker.failed_corpus_replays; + } + } + result.first_case = first_case_candidate.map(|(_, case)| case).unwrap_or_default(); + let (_, last_run_worker_idx) = last_run_worker.expect("at least one worker"); + + if let Some(&failed_worker_id) = shared_state.failed_worker_id.get() { + result.success = false; + + let failed_worker_idx = workers.iter().position(|w| w.id == failed_worker_id).unwrap(); + let failed_worker = &mut workers[failed_worker_idx]; + + let (calldata, call) = std::mem::take(&mut failed_worker.counterexample); + result.labels = call.labels; + result.traces = call.traces.clone(); + result.breakpoints = call.cheatcodes.map(|c| c.breakpoints); + + match &failed_worker.failure { + Some(TestCaseError::Fail(reason)) => { + let reason = reason.to_string(); + result.reason = (!reason.is_empty()).then_some(reason); + let args = if let Some(data) = calldata.get(4..) { + func.abi_decode_input(data).unwrap_or_default() + } else { + vec![] + }; + result.counterexample = Some(CounterExample::Single( + BaseCounterExample::from_fuzz_call(calldata, args, call.traces), + )); + } + Some(TestCaseError::Reject(reason)) => { + let reason = reason.to_string(); + result.reason = (!reason.is_empty()).then_some(reason); + } + None => {} + } + } else { + let last_run_worker = &workers[last_run_worker_idx]; + result.success = true; + result.traces = last_run_worker.traces.last().cloned(); + result.breakpoints = last_run_worker.breakpoints.clone(); + } + + if !self.config.show_logs { + result.logs = workers[last_run_worker_idx].logs.clone(); + } + + for mut worker in workers { + result.gas_by_case.append(&mut worker.gas_by_case); + if self.config.show_logs { + result.logs.append(&mut worker.logs); + } + result.gas_report_traces.extend(worker.traces.into_iter().map(|t| t.arena)); + HitMaps::merge_opt(&mut result.line_coverage, worker.coverage); + result.deprecated_cheatcodes.extend(worker.deprecated_cheatcodes); + } + + if let Some(reason) = &result.reason + && let Some(reason) = SkipReason::decode_self(reason) + { + result.skipped = true; + result.reason = reason.0; + } + + result + } + + /// Runs a single fuzz worker + #[allow(clippy::too_many_arguments)] + fn run_worker( + &self, + worker_id: usize, + func: &Function, + fuzz_fixtures: &FuzzFixtures, + address: Address, + rd: &RevertDecoder, + shared_state: &SharedFuzzState, + progress: Option<&ProgressBar>, + ) -> Result { + // Prepare let dictionary_weight = self.config.dictionary.dictionary_weight.min(100); let strategy = proptest::prop_oneof![ 100 - dictionary_weight => fuzz_calldata(func.clone(), fuzz_fixtures), - dictionary_weight => fuzz_calldata_from_state(func.clone(), state), + dictionary_weight => fuzz_calldata_from_state(func.clone(), &shared_state.state), ] .prop_map(move |calldata| BasicTxDetails { warp: None, @@ -119,272 +416,231 @@ impl FuzzedExecutor { sender: Default::default(), call_details: CallDetails { target: Default::default(), calldata }, }); - // 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; - let mut corpus_manager = CorpusManager::new( + let mut corpus = WorkerCorpus::new( + worker_id, self.config.corpus.clone(), strategy.boxed(), - &self.executor, + // Master worker replays the persisted corpus using the executor + if worker_id == 0 { Some(&self.executor_f) } else { None }, Some(func), - None, + None, // fuzzed_contracts for invariant tests )?; + let mut executor = self.executor_f.clone(); - // Start timer for this fuzz test. - let timer = FuzzTestTimer::new(self.config.timeout); - let mut last_metrics_report = Instant::now(); - let max_runs = self.config.runs; - let continue_campaign = |runs: u32| { - if early_exit.should_stop() { - return false; - } + let mut worker = WorkerState::new(worker_id); + // 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 / self.num_workers as u32); - if timer.is_enabled() { !timer.is_timed_out() } else { runs < max_runs } + let worker_runs = self.runs_per_worker(worker_id); + debug!(worker_runs); + + let mut runner_config = self.runner.config().clone(); + runner_config.cases = worker_runs; + + let mut runner = if let Some(seed) = self.config.seed { + // For deterministic parallel fuzzing, derive a unique seed for each worker + let worker_seed = if worker_id == 0 { + // Master worker uses the provided seed as is. + seed + } else { + // Derive a worker-specific seed using keccak256(seed || worker_id) + let seed_data = + [&seed.to_be_bytes::<32>()[..], &worker_id.to_be_bytes()[..]].concat(); + U256::from_be_bytes(keccak256(seed_data).0) + }; + trace!(target: "forge::test", ?worker_seed, "deterministic seed for worker {worker_id}"); + let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &worker_seed.to_be_bytes::<32>()); + TestRunner::new_with_rng(runner_config, rng) + } else { + TestRunner::new(runner_config) }; - 'stop: while continue_campaign(test_data.runs) { + let mut persisted_failure = self.persisted_failure.as_ref().filter(|_| worker_id == 0); + + // Offset to stagger corpus syncs across workers; so that workers don't sync at the same + // time. + let sync_offset = worker_id as u32 * 100; + let sync_threshold = SYNC_INTERVAL + sync_offset; + let mut runs_since_sync = sync_threshold; // Always sync at the start. + let mut last_metrics_report = Instant::now(); + // Continue while: + // 1. Global state allows (not timed out, not at global limit, no failure found) + // 2. Worker hasn't reached its specific run limit + 'stop: while shared_state.should_continue() && worker.runs < worker_runs { // If counterexample recorded, replay it first, without incrementing runs. - let input = if let Some(failure) = self.persisted_failure.take() + let input = if worker_id == 0 + && let Some(failure) = persisted_failure.take() && failure.calldata.get(..4).is_some_and(|selector| func.selector() == selector) { failure.calldata.clone() } else { - // If running with progress, then increment current run. - if let Some(progress) = progress { - progress.inc(1); - // Display metrics in progress bar. - if self.config.corpus.collect_edge_coverage() { - progress.set_message(format!("{}", &corpus_manager.metrics)); - } - } else if self.config.corpus.collect_edge_coverage() - && last_metrics_report.elapsed() > DURATION_BETWEEN_METRICS_REPORT - { - // Display metrics inline. - let metrics = json!({ - "timestamp": SystemTime::now() - .duration_since(UNIX_EPOCH)? - .as_secs(), - "test": func.name, - "metrics": &corpus_manager.metrics, - }); - let _ = sh_println!("{}", serde_json::to_string(&metrics)?); - last_metrics_report = Instant::now(); - }; - - if let Some(cheats) = self.executor.inspector_mut().cheatcodes.as_mut() + runs_since_sync += 1; + if runs_since_sync >= sync_threshold { + let timer = Instant::now(); + corpus.sync( + self.num_workers, + &executor, + Some(func), + None, + &shared_state.global_corpus_metrics, + )?; + trace!("finished corpus sync in {:?}", timer.elapsed()); + runs_since_sync = 0; + } + + if let Some(cheats) = executor.inspector_mut().cheatcodes.as_mut() && let Some(seed) = self.config.seed { - cheats.set_seed(seed.wrapping_add(U256::from(test_data.runs))); + cheats.set_seed(seed.wrapping_add(U256::from(worker.runs))); } - test_data.runs += 1; - match corpus_manager.new_input(&mut self.runner, state, func) { + match corpus.new_input(&mut runner, &shared_state.state, func) { Ok(input) => input, Err(err) => { - test_data.failure = Some(TestCaseError::fail(format!( - "failed to generate fuzzed input: {err}" + worker.failure = Some(TestCaseError::fail(format!( + "failed to generate fuzzed input in worker {}: {err}", + worker.id ))); + shared_state.try_claim_failure(worker_id); break 'stop; } } }; - match self.single_fuzz(address, input, &mut corpus_manager) { + let mut inc_runs = || { + let total_runs = shared_state.increment_runs(); + debug_assert!( + shared_state.timer.is_enabled() || total_runs <= self.config.runs, + "worker runs were not distributed correctly" + ); + worker.runs += 1; + if let Some(progress) = progress { + progress.inc(1); + } + total_runs + }; + + worker.last_run_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis(); + match self.single_fuzz(&executor, address, input, &mut corpus) { Ok(fuzz_outcome) => match fuzz_outcome { FuzzOutcome::Case(case) => { - test_data.gas_by_case.push((case.case.gas, case.case.stipend)); + let total_runs = inc_runs(); - if test_data.first_case.is_none() { - test_data.first_case.replace(case.case); + if worker_id == 0 && self.config.corpus.collect_edge_coverage() { + if let Some(progress) = progress { + corpus.sync_metrics(&shared_state.global_corpus_metrics); + progress + .set_message(format!("{}", shared_state.global_corpus_metrics)); + } else if last_metrics_report.elapsed() + > DURATION_BETWEEN_METRICS_REPORT + { + corpus.sync_metrics(&shared_state.global_corpus_metrics); + // Display metrics inline. + let metrics = json!({ + "timestamp": SystemTime::now() + .duration_since(UNIX_EPOCH)? + .as_secs(), + "test": func.name, + "metrics": shared_state.global_corpus_metrics.load(), + }); + let _ = sh_println!("{metrics}"); + last_metrics_report = Instant::now(); + } + } + + worker.gas_by_case.push((case.case.gas, case.case.stipend)); + + if worker.first_case.is_none() { + worker.first_case = Some((total_runs, case.case)); } if let Some(call_traces) = case.traces { - if test_data.traces.len() == max_traces_to_collect { - test_data.traces.pop(); + if worker.traces.len() == max_traces_to_collect as usize { + worker.traces.pop(); } - test_data.traces.push(call_traces); - test_data.breakpoints.replace(case.breakpoints); + worker.traces.push(call_traces); + worker.breakpoints = Some(case.breakpoints); } // Always store logs from the last run in test_data.logs for display at // verbosity >= 2. When show_logs is true, // accumulate all logs. When false, only keep the last run's logs. if self.config.show_logs { - test_data.logs.extend(case.logs); + worker.logs.extend(case.logs); } else { - test_data.logs = case.logs; + worker.logs = case.logs; } - HitMaps::merge_opt(&mut test_data.coverage, case.coverage); - test_data.deprecated_cheatcodes = case.deprecated_cheatcodes; + HitMaps::merge_opt(&mut worker.coverage, case.coverage); + worker.deprecated_cheatcodes = case.deprecated_cheatcodes; } FuzzOutcome::CounterExample(CounterExampleOutcome { exit_reason: status, counterexample: outcome, .. }) => { + inc_runs(); + let reason = rd.maybe_decode(&outcome.1.result, status); - test_data.logs.extend(outcome.1.logs.clone()); - test_data.counterexample = outcome; - test_data.failure = Some(TestCaseError::fail(reason.unwrap_or_default())); + worker.logs.extend(outcome.1.logs.clone()); + worker.counterexample = outcome; + worker.failure = Some(TestCaseError::fail(reason.unwrap_or_default())); + shared_state.try_claim_failure(worker_id); break 'stop; } }, - Err(err) => { - match err { - TestCaseError::Fail(_) => { - test_data.failure = Some(err); - break 'stop; - } - TestCaseError::Reject(_) => { - // Discard run and apply max rejects if configured. Saturate to handle - // the case of replayed failure, which doesn't count as a run. - test_data.runs = test_data.runs.saturating_sub(1); - test_data.rejects += 1; - - // Update progress bar to reflect rejected runs. - if let Some(progress) = progress { - progress.set_message(format!("([{}] rejected)", test_data.rejects)); - progress.dec(1); - } - - if self.config.max_test_rejects > 0 - && test_data.rejects >= self.config.max_test_rejects - { - test_data.failure = Some(TestCaseError::reject( - FuzzError::TooManyRejects(self.config.max_test_rejects), - )); - break 'stop; - } - } + Err(err) => match err { + TestCaseError::Fail(_) => { + worker.failure = Some(err); + shared_state.try_claim_failure(worker_id); + break 'stop; } - } - } - } + TestCaseError::Reject(_) => { + let max = self.config.max_test_rejects; - let (calldata, call) = test_data.counterexample; - let mut traces = test_data.traces; - let (last_run_traces, last_run_breakpoints) = if test_data.failure.is_none() { - (traces.pop(), test_data.breakpoints) - } else { - (call.traces.clone(), call.cheatcodes.map(|c| c.breakpoints)) - }; + let total = shared_state.increment_rejects(); - // test_data.logs already contains the appropriate logs: - // - For failed tests: logs from the counterexample - // - For successful tests with show_logs=true: all logs from all runs - // - For successful tests with show_logs=false: logs from the last run only - let result_logs = test_data.logs; - - let mut result = FuzzTestResult { - first_case: test_data.first_case.unwrap_or_default(), - gas_by_case: test_data.gas_by_case, - success: test_data.failure.is_none(), - skipped: false, - reason: None, - counterexample: None, - logs: result_logs, - labels: call.labels, - traces: last_run_traces, - breakpoints: last_run_breakpoints, - gas_report_traces: traces.into_iter().map(|a| a.arena).collect(), - line_coverage: test_data.coverage, - deprecated_cheatcodes: test_data.deprecated_cheatcodes, - failed_corpus_replays: corpus_manager.failed_replays(), - }; + // Update progress bar to reflect rejected runs. + // TODO(dani): (pre-existing) conflicts with corpus metrics `set_message` + if !self.config.corpus.collect_edge_coverage() + && let Some(progress) = progress + { + progress.set_message(format!("([{total}] rejected)")); + } - match test_data.failure { - Some(TestCaseError::Fail(reason)) => { - let reason = reason.to_string(); - result.reason = (!reason.is_empty()).then_some(reason); - let args = if let Some(data) = calldata.get(4..) { - func.abi_decode_input(data).unwrap_or_default() - } else { - vec![] - }; - result.counterexample = Some(CounterExample::Single( - BaseCounterExample::from_fuzz_call(calldata, args, call.traces), - )); - } - Some(TestCaseError::Reject(reason)) => { - let reason = reason.to_string(); - result.reason = (!reason.is_empty()).then_some(reason); + if max > 0 && total > max { + worker.failure = + Some(TestCaseError::reject(FuzzError::TooManyRejects(max))); + shared_state.try_claim_failure(worker_id); + break 'stop; + } + } + }, } - None => {} } - if let Some(reason) = &result.reason - && let Some(reason) = SkipReason::decode_self(reason) - { - result.skipped = true; - result.reason = reason.0; + if worker_id == 0 { + worker.failed_corpus_replays = corpus.failed_replays; } - state.log_stats(); + // Logs stats + trace!("worker {worker_id} fuzz stats"); + shared_state.state.log_stats(); - Ok(result) + Ok(worker) } - /// Granular and single-step function that runs only one fuzz and returns either a `CaseOutcome` - /// or a `CounterExampleOutcome` - fn single_fuzz( - &mut self, - address: Address, - calldata: Bytes, - coverage_metrics: &mut CorpusManager, - ) -> Result { - let mut call = self - .executor - .call_raw(self.sender, address, calldata.clone(), U256::ZERO) - .map_err(|e| TestCaseError::fail(e.to_string()))?; - let new_coverage = coverage_metrics.merge_edge_coverage(&mut call); - coverage_metrics.process_inputs( - &[BasicTxDetails { - warp: None, - roll: None, - sender: self.sender, - call_details: CallDetails { target: address, calldata: calldata.clone() }, - }], - new_coverage, - ); - - // Handle `vm.assume`. - if call.result.as_ref() == MAGIC_ASSUME { - return Err(TestCaseError::reject(FuzzError::AssumeReject)); - } - - let (breakpoints, deprecated_cheatcodes) = - call.cheatcodes.as_ref().map_or_else(Default::default, |cheats| { - (cheats.breakpoints.clone(), cheats.deprecated.clone()) - }); - - // Consider call success if test should not fail on reverts and reverter is not the - // cheatcode or test address. - let success = if !self.config.fail_on_revert - && call - .reverter - .is_some_and(|reverter| reverter != address && reverter != CHEATCODE_ADDRESS) - { - true - } else { - self.executor.is_raw_call_mut_success(address, &mut call, false) - }; - - if success { - Ok(FuzzOutcome::Case(CaseOutcome { - case: FuzzCase { calldata, gas: call.gas_used, stipend: call.stipend }, - traces: call.traces, - coverage: call.line_coverage, - breakpoints, - logs: call.logs, - deprecated_cheatcodes, - })) - } else { - Ok(FuzzOutcome::CounterExample(CounterExampleOutcome { - exit_reason: call.exit_reason, - counterexample: (calldata, call), - breakpoints, - })) - } + /// Determines the number of runs per worker. + fn runs_per_worker(&self, worker_id: usize) -> u32 { + let worker_id = worker_id as u32; + let total_runs = self.config.runs; + let n = self.num_workers as u32; + let runs = total_runs / n; + let remainder = total_runs % n; + // Distribute the remainder evenly among the first `remainder` workers, + // assuming `worker_id` is in `0..n`. + if worker_id < remainder { runs + 1 } else { runs } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 4ea542ae94a39..86e8e18581764 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -1,14 +1,21 @@ use crate::{ - executors::{Executor, RawCallResult}, + executors::{ + DURATION_BETWEEN_METRICS_REPORT, EarlyExit, EvmError, Executor, FuzzTestTimer, + RawCallResult, corpus::WorkerCorpus, + }, inspectors::Fuzzer, }; use alloy_primitives::{ - Address, Bytes, FixedBytes, Selector, U256, + Address, Bytes, FixedBytes, I256, Selector, U256, map::{AddressMap, HashMap}, }; use alloy_sol_types::{SolCall, sol}; use eyre::{ContextCompat, Result, eyre}; -use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_common::{ + TestFunctionExt, + contracts::{ContractsByAddress, ContractsByArtifact}, + sh_println, +}; use foundry_config::InvariantConfig; use foundry_evm_core::{ constants::{ @@ -30,6 +37,8 @@ use parking_lot::RwLock; use proptest::{strategy::Strategy, test_runner::TestRunner}; use result::{assert_after_invariant, assert_invariants, can_continue}; use revm::state::Account; +use serde::{Deserialize, Serialize}; +use serde_json::json; use std::{ collections::{HashMap as Map, btree_map::Entry}, sync::Arc, @@ -44,16 +53,10 @@ mod replay; pub use replay::{replay_error, replay_run}; mod result; -use foundry_common::{TestFunctionExt, sh_println}; pub use result::InvariantFuzzTestResult; -use serde::{Deserialize, Serialize}; -use serde_json::json; mod shrink; -use crate::executors::{ - DURATION_BETWEEN_METRICS_REPORT, EarlyExit, EvmError, FuzzTestTimer, corpus::CorpusManager, -}; -pub use shrink::check_sequence; +pub use shrink::{check_sequence, check_sequence_value}; sol! { interface IInvariantTest { @@ -142,6 +145,11 @@ struct InvariantTestData { // until the desired `depth` so we can use the evolving fuzz dictionary // during the run. branch_runner: TestRunner, + + // Optimization mode state: tracks the best (maximum) value and the sequence that produced it. + // Only used when invariant function returns int256. + optimization_best_value: Option, + optimization_best_sequence: Vec, } /// Contains invariant test data. @@ -176,6 +184,8 @@ impl InvariantTest { line_coverage: None, metrics: Map::default(), branch_runner, + optimization_best_value: None, + optimization_best_sequence: vec![], }; Self { fuzz_state, targeted_contracts, test_data } } @@ -244,6 +254,14 @@ impl InvariantTest { // Revert state to not persist values between runs. self.fuzz_state.revert(); } + + /// Updates the optimization state if the new value is better (higher) than the current best. + fn update_optimization_value(&mut self, value: I256, sequence: &[BasicTxDetails]) { + if self.test_data.optimization_best_value.is_none_or(|best| value > best) { + self.test_data.optimization_best_value = Some(value); + self.test_data.optimization_best_sequence = sequence.to_vec(); + } + } } /// Contains data for an invariant test run. @@ -428,7 +446,7 @@ impl<'a> InvariantExecutor<'a> { // inconsistencies whenever proptest tries to use the input case after test // execution. // See . - let mut state_changeset = call_result.state_changeset.clone(); + let mut state_changeset = std::mem::take(&mut call_result.state_changeset); if !call_result.reverted { collect_data( &invariant_test, @@ -452,22 +470,65 @@ impl<'a> InvariantExecutor<'a> { { warn!(target: "forge::test", "{error}"); } - current_run.fuzz_runs.push(FuzzCase { - calldata: tx.call_details.calldata.clone(), - gas: call_result.gas_used, - stipend: call_result.stipend, - }); + current_run + .fuzz_runs + .push(FuzzCase { gas: call_result.gas_used, stipend: call_result.stipend }); // Determine if test can continue or should exit. - let result = can_continue( - &invariant_contract, - &mut invariant_test, - &mut current_run, - &self.config, - call_result, - &state_changeset, - ) - .map_err(|e| eyre!(e.to_string()))?; + // Check invariants based on check_interval to improve deep run performance. + // - check_interval=0: only assert on the last call + // - check_interval=1 (default): assert after every call + // - check_interval=N: assert every N calls AND always on the last call + let is_last_call = current_run.depth == self.config.depth - 1; + let should_check_invariant = if self.config.check_interval == 0 { + is_last_call + } else { + self.config.check_interval == 1 + || (current_run.depth + 1).is_multiple_of(self.config.check_interval) + || is_last_call + }; + + let result = if should_check_invariant { + can_continue( + &invariant_contract, + &mut invariant_test, + &mut current_run, + &self.config, + call_result, + &state_changeset, + ) + .map_err(|e| eyre!(e.to_string()))? + } else { + // Skip invariant check but still track reverts + if call_result.reverted { + invariant_test.test_data.failures.reverts += 1; + if self.config.fail_on_revert { + let case_data = error::FailedInvariantCaseData::new( + &invariant_contract, + &self.config, + &invariant_test.targeted_contracts, + ¤t_run.inputs, + call_result, + &[], + ); + invariant_test.test_data.failures.revert_reason = + Some(case_data.revert_reason.clone()); + invariant_test.test_data.failures.error = + Some(InvariantFuzzError::Revert(case_data)); + result::RichInvariantResults::new(false, None) + } else if !invariant_contract.is_optimization() { + // In optimization mode, keep reverted calls to preserve + // warp/roll values for correct replay during shrinking. + current_run.inputs.pop(); + result::RichInvariantResults::new(true, None) + } else { + result::RichInvariantResults::new(true, None) + } + } else { + result::RichInvariantResults::new(true, None) + } + }; + if !result.can_continue || current_run.depth == self.config.depth - 1 { invariant_test.set_last_run_inputs(¤t_run.inputs); } @@ -541,7 +602,9 @@ impl<'a> InvariantExecutor<'a> { gas_report_traces: result.gas_report_traces, line_coverage: result.line_coverage, metrics: result.metrics, - failed_corpus_replays: corpus_manager.failed_replays(), + failed_corpus_replays: corpus_manager.failed_replays, + optimization_best_value: result.optimization_best_value, + optimization_best_sequence: result.optimization_best_sequence, }) } @@ -553,7 +616,7 @@ impl<'a> InvariantExecutor<'a> { invariant_contract: &InvariantContract<'_>, fuzz_fixtures: &FuzzFixtures, fuzz_state: EvmFuzzState, - ) -> Result<(InvariantTest, CorpusManager)> { + ) -> Result<(InvariantTest, WorkerCorpus)> { // Finds out the chosen deployed contracts and/or senders. self.select_contract_artifacts(invariant_contract.address)?; let (targeted_senders, targeted_contracts) = @@ -569,25 +632,6 @@ impl<'a> InvariantExecutor<'a> { ) .no_shrink(); - // Allows `override_call_strat` to use the address given by the Fuzzer inspector during - // EVM execution. - let mut call_generator = None; - if self.config.call_override { - let target_contract_ref = Arc::new(RwLock::new(Address::ZERO)); - - call_generator = Some(RandomCallGenerator::new( - invariant_contract.address, - self.runner.clone(), - override_call_strat( - fuzz_state.clone(), - targeted_contracts.clone(), - target_contract_ref.clone(), - fuzz_fixtures.clone(), - ), - target_contract_ref, - )); - } - // If any of the targeted contracts have the storage layout enabled then we can sample // mapping values. To accomplish, we need to record the mapping storage slots and keys. let fuzz_state = @@ -597,8 +641,11 @@ impl<'a> InvariantExecutor<'a> { fuzz_state }; + // Set up fuzzer WITHOUT call_generator initially. + // We defer call_override until after the initial invariant check to avoid + // injecting random calls during setup which would break the invariant assertion. self.executor.inspector_mut().set_fuzzer(Fuzzer { - call_generator, + call_generator: None, fuzz_state: fuzz_state.clone(), collect: true, }); @@ -620,13 +667,44 @@ impl<'a> InvariantExecutor<'a> { return Err(eyre!(error.revert_reason().unwrap_or_default())); } - let corpus_manager = CorpusManager::new( + // NOW enable call_override after the initial invariant check has passed. + // This allows `override_call_strat` to inject calls during actual fuzz runs + // for reentrancy vulnerability detection. + if self.config.call_override { + let target_contract_ref = Arc::new(RwLock::new(Address::ZERO)); + + // Collect handler addresses - these are the contracts we want to inject + // reentrancy into (simulating malicious receive() functions). + let handler_addresses: std::collections::HashSet
= + targeted_contracts.targets.lock().keys().copied().collect(); + + let call_generator = RandomCallGenerator::new( + invariant_contract.address, + handler_addresses, + self.runner.clone(), + override_call_strat( + fuzz_state.clone(), + targeted_contracts.clone(), + target_contract_ref.clone(), + fuzz_fixtures.clone(), + ), + target_contract_ref, + ); + + if let Some(fuzzer) = self.executor.inspector_mut().fuzzer.as_mut() { + fuzzer.call_generator = Some(call_generator); + } + } + + let worker = WorkerCorpus::new( + 0, self.config.corpus.clone(), strategy.boxed(), - &self.executor, + Some(&self.executor), None, Some(&targeted_contracts), )?; + let invariant_test = InvariantTest::new( fuzz_state, targeted_contracts, @@ -635,7 +713,7 @@ impl<'a> InvariantExecutor<'a> { self.runner.clone(), ); - Ok((invariant_test, corpus_manager)) + Ok((invariant_test, worker)) } /// Fills the `InvariantExecutor` with the artifact identifier filters (in `path:name` string @@ -1019,28 +1097,32 @@ pub(crate) fn call_invariant_function( Ok((call_result, success)) } -/// Calls the invariant selector and returns call result and if succeeded. -/// Updates the block number and block timestamp if configured. +/// Executes a fuzz call and returns the result. +/// Applies any block timestamp (warp) and block number (roll) adjustments before the call. pub(crate) fn execute_tx(executor: &mut Executor, tx: &BasicTxDetails) -> Result { - // Apply pre-call block adjustments. - if let Some(warp) = tx.warp { + let warp = tx.warp.unwrap_or_default(); + let roll = tx.roll.unwrap_or_default(); + + if warp > 0 || roll > 0 { + // Apply pre-call block adjustments to the executor's env. executor.env_mut().evm_env.block_env.timestamp += warp; - } - if let Some(roll) = tx.roll { executor.env_mut().evm_env.block_env.number += roll; + + // Also update the inspector's cheatcodes.block if set. + // The inspector's block may override the env during interpreter initialization, + // so we need to add our warp/roll on top of any existing cheatcode-set values. + let block_env = executor.env().evm_env.block_env.clone(); + if let Some(cheatcodes) = executor.inspector_mut().cheatcodes.as_mut() { + if let Some(block) = cheatcodes.block.as_mut() { + block.timestamp += warp; + block.number += roll; + } else { + cheatcodes.block = Some(block_env); + } + } } - // Perform the raw call. - let mut call_result = executor + executor .call_raw(tx.sender, tx.call_details.target, tx.call_details.calldata.clone(), U256::ZERO) - .map_err(|e| eyre!(format!("Could not make raw evm call: {e}")))?; - - // Propagate block adjustments to call result which will be committed. - if let Some(warp) = tx.warp { - call_result.env.evm_env.block_env.timestamp += warp; - } - if let Some(roll) = tx.roll { - call_result.env.evm_env.block_env.number += roll; - } - Ok(call_result) + .map_err(|e| eyre!(format!("Could not make raw evm call: {e}"))) } diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index e720feba9dd77..fe507123f9fd7 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -1,7 +1,10 @@ use super::{call_after_invariant_function, call_invariant_function, execute_tx}; -use crate::executors::{EarlyExit, Executor, invariant::shrink::shrink_sequence}; +use crate::executors::{ + EarlyExit, Executor, + invariant::shrink::{shrink_sequence, shrink_sequence_value}, +}; use alloy_dyn_abi::JsonAbiExt; -use alloy_primitives::{Log, map::HashMap}; +use alloy_primitives::{I256, Log, map::HashMap}; use eyre::Result; use foundry_common::{ContractsByAddress, ContractsByArtifact}; use foundry_config::InvariantConfig; @@ -87,13 +90,17 @@ pub fn replay_run( Ok(counterexample_sequence) } -/// Replays the error case, shrinks the failing sequence and collects all necessary traces. +/// Replays and shrinks a call sequence, collecting logs and traces. +/// +/// For check mode (target_value=None): shrinks to find shortest failing sequence. +/// For optimization mode (target_value=Some): shrinks to find shortest sequence producing target. #[expect(clippy::too_many_arguments)] pub fn replay_error( config: InvariantConfig, mut executor: Executor, calls: &[BasicTxDetails], inner_sequence: Option>>, + target_value: Option, invariant_contract: &InvariantContract<'_>, known_contracts: &ContractsByArtifact, ided_contracts: ContractsByAddress, @@ -104,15 +111,24 @@ pub fn replay_error( progress: Option<&ProgressBar>, early_exit: &EarlyExit, ) -> Result> { - // Shrink sequence of failed calls. - let calls = - shrink_sequence(&config, invariant_contract, calls, &executor, progress, early_exit)?; + let calls = if let Some(target) = target_value { + shrink_sequence_value( + &config, + invariant_contract, + calls, + &executor, + target, + progress, + early_exit, + )? + } else { + shrink_sequence(&config, invariant_contract, calls, &executor, progress, early_exit)? + }; if let Some(sequence) = inner_sequence { set_up_inner_replay(&mut executor, &sequence); } - // Replay calls to get the counterexample and to collect logs, traces and coverage. replay_run( invariant_contract, executor, diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 611f9fb6bfbc8..b6cd1879d29d0 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -4,6 +4,7 @@ use super::{ }; use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; +use alloy_primitives::I256; use eyre::Result; use foundry_config::InvariantConfig; use foundry_evm_core::utils::StateChangeset; @@ -32,8 +33,13 @@ pub struct InvariantFuzzTestResult { pub line_coverage: Option, /// Fuzzed selectors metrics collected during the invariant test runs. pub metrics: HashMap, - /// NUmber of failed replays from persisted corpus. + /// Number of failed replays from persisted corpus. pub failed_corpus_replays: usize, + /// For optimization mode (int256 return): the best (maximum) value achieved. + /// None means standard invariant check mode. + pub optimization_best_value: Option, + /// For optimization mode: the call sequence that produced the best value. + pub optimization_best_sequence: Vec, } /// Enriched results of an invariant run check. @@ -45,7 +51,7 @@ pub(crate) struct RichInvariantResults { } impl RichInvariantResults { - fn new(can_continue: bool, call_result: Option) -> Self { + pub(crate) fn new(can_continue: bool, call_result: Option) -> Self { Self { can_continue, call_result } } } @@ -95,6 +101,9 @@ pub(crate) fn assert_invariants( /// Returns if invariant test can continue and last successful call result of the invariant test /// function (if it can continue). +/// +/// For optimization mode (int256 return), tracks the max value but never fails on invariant. +/// For check mode, asserts the invariant and fails if broken. pub(crate) fn can_continue( invariant_contract: &InvariantContract<'_>, invariant_test: &mut InvariantTest, @@ -104,6 +113,7 @@ pub(crate) fn can_continue( state_changeset: &StateChangeset, ) -> Result { let mut call_results = None; + let is_optimization = invariant_contract.is_optimization(); let handlers_succeeded = || { invariant_test.targeted_contracts.targets.lock().keys().all(|address| { @@ -116,22 +126,38 @@ pub(crate) fn can_continue( }) }; - // 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 { invariant_run.run_traces.push(traces); } - call_results = assert_invariants( - invariant_contract, - invariant_config, - &invariant_test.targeted_contracts, - &invariant_run.executor, - &invariant_run.inputs, - &mut invariant_test.test_data.failures, - )?; - if call_results.is_none() { - return Ok(RichInvariantResults::new(false, None)); + if is_optimization { + // Optimization mode: call invariant and track max value, never fail. + let (inv_result, success) = call_invariant_function( + &invariant_run.executor, + invariant_contract.address, + invariant_contract.invariant_function.abi_encode_input(&[])?.into(), + )?; + if success + && inv_result.result.len() >= 32 + && let Some(value) = I256::try_from_be_slice(&inv_result.result[..32]) + { + invariant_test.update_optimization_value(value, &invariant_run.inputs); + } + call_results = Some(inv_result); + } else { + // Check mode: assert invariants and fail if broken. + call_results = assert_invariants( + invariant_contract, + invariant_config, + &invariant_test.targeted_contracts, + &invariant_run.executor, + &invariant_run.inputs, + &mut invariant_test.test_data.failures, + )?; + if call_results.is_none() { + return Ok(RichInvariantResults::new(false, None)); + } } } else { // Increase the amount of reverts. @@ -151,9 +177,10 @@ pub(crate) fn can_continue( invariant_data.failures.error = Some(InvariantFuzzError::Revert(case_data)); return Ok(RichInvariantResults::new(false, None)); - } else if call_result.reverted { + } else if call_result.reverted && !is_optimization { // If we don't fail test on revert then remove last reverted call from inputs. - // This improves shrinking performance as irrelevant calls won't be checked again. + // In optimization mode, we keep reverted calls to preserve warp/roll values + // for correct replay during shrinking. invariant_run.inputs.pop(); } } diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 315cdfa261db8..1e56a019cc576 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -2,7 +2,7 @@ use crate::executors::{ EarlyExit, Executor, invariant::{call_after_invariant_function, call_invariant_function, execute_tx}, }; -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::{Address, Bytes, I256, U256}; use foundry_config::InvariantConfig; use foundry_evm_core::constants::MAGIC_ASSUME; use foundry_evm_fuzz::{BasicTxDetails, invariant::InvariantContract}; @@ -30,6 +30,50 @@ impl CallSequenceShrinker { fn current(&self) -> impl Iterator + '_ { (0..self.call_sequence_len).filter(|&call_id| self.included_calls.test(call_id)) } + + /// Advance to the next call index, wrapping around to 0 at the end. + fn next_index(&self, call_idx: usize) -> usize { + if call_idx + 1 == self.call_sequence_len { 0 } else { call_idx + 1 } + } +} + +/// Resets the progress bar for shrinking. +fn reset_shrink_progress(config: &InvariantConfig, progress: Option<&ProgressBar>) { + if let Some(progress) = progress { + progress.set_length(config.shrink_run_limit as u64); + progress.reset(); + progress.set_message(" Shrink"); + } +} + +/// Applies accumulated warp/roll to a call, returning a modified copy. +fn apply_warp_roll(call: &BasicTxDetails, warp: U256, roll: U256) -> BasicTxDetails { + let mut result = call.clone(); + if warp > U256::ZERO { + result.warp = Some(warp); + } + if roll > U256::ZERO { + result.roll = Some(roll); + } + result +} + +/// Applies warp/roll adjustments directly to the executor's environment. +fn apply_warp_roll_to_env(executor: &mut Executor, warp: U256, roll: U256) { + if warp > U256::ZERO || roll > U256::ZERO { + executor.env_mut().evm_env.block_env.timestamp += warp; + executor.env_mut().evm_env.block_env.number += roll; + + let block_env = executor.env().evm_env.block_env.clone(); + if let Some(cheatcodes) = executor.inspector_mut().cheatcodes.as_mut() { + if let Some(block) = cheatcodes.block.as_mut() { + block.timestamp += warp; + block.number += roll; + } else { + cheatcodes.block = Some(block_env); + } + } + } } pub(crate) fn shrink_sequence( @@ -42,12 +86,7 @@ pub(crate) fn shrink_sequence( ) -> 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(config.shrink_run_limit as u64); - progress.reset(); - progress.set_message(" Shrink"); - } + reset_shrink_progress(config, progress); let target_address = invariant_contract.address; let calldata: Bytes = invariant_contract.invariant_function.selector().to_vec().into(); @@ -59,14 +98,13 @@ pub(crate) fn shrink_sequence( } let mut call_idx = 0; - let mut shrinker = CallSequenceShrinker::new(calls.len()); + for _ in 0..config.shrink_run_limit { if early_exit.should_stop() { break; } - // Remove call at current index. shrinker.included_calls.clear(call_idx); match check_sequence( @@ -89,12 +127,7 @@ pub(crate) fn shrink_sequence( progress.inc(1); } - // Restart from first call once we reach the end of sequence. - if call_idx + 1 == shrinker.call_sequence_len { - call_idx = 0; - } else { - call_idx += 1; - }; + call_idx = shrinker.next_index(call_idx); } Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) @@ -140,3 +173,135 @@ pub fn check_sequence( Ok((success, true)) } + +/// Shrinks a call sequence to the shortest sequence that still produces the target optimization +/// value. This is specifically for optimization mode where we want to find the minimal sequence +/// that achieves the maximum value. +/// +/// Unlike `shrink_sequence` (for check mode), this function: +/// - Accumulates warp/roll values from removed calls into the next kept call +/// - Checks for target value equality rather than invariant failure +pub(crate) fn shrink_sequence_value( + config: &InvariantConfig, + invariant_contract: &InvariantContract<'_>, + calls: &[BasicTxDetails], + executor: &Executor, + target_value: I256, + progress: Option<&ProgressBar>, + early_exit: &EarlyExit, +) -> eyre::Result> { + trace!(target: "forge::test", "Shrinking optimization sequence of {} calls for target value {}.", calls.len(), target_value); + + reset_shrink_progress(config, progress); + + let target_address = invariant_contract.address; + let calldata: Bytes = invariant_contract.invariant_function.selector().to_vec().into(); + + // Special case: check if target value is achieved with 0 calls. + if check_sequence_value(executor.clone(), calls, vec![], target_address, calldata.clone())? + == Some(target_value) + { + return Ok(vec![]); + } + + let mut call_idx = 0; + let mut shrinker = CallSequenceShrinker::new(calls.len()); + + for _ in 0..config.shrink_run_limit { + if early_exit.should_stop() { + break; + } + + shrinker.included_calls.clear(call_idx); + + let keeps_target = check_sequence_value( + executor.clone(), + calls, + shrinker.current().collect(), + target_address, + calldata.clone(), + )? == Some(target_value); + + if keeps_target { + if shrinker.included_calls.count() == 1 { + break; + } + } else { + shrinker.included_calls.set(call_idx); + } + + if let Some(progress) = progress { + progress.inc(1); + } + + call_idx = shrinker.next_index(call_idx); + } + + // Build the final shrunk sequence, accumulating warp/roll from removed calls. + let mut result = Vec::new(); + let mut accumulated_warp = U256::ZERO; + let mut accumulated_roll = U256::ZERO; + + for (idx, call) in calls.iter().enumerate() { + accumulated_warp += call.warp.unwrap_or(U256::ZERO); + accumulated_roll += call.roll.unwrap_or(U256::ZERO); + + if shrinker.included_calls.test(idx) { + result.push(apply_warp_roll(call, accumulated_warp, accumulated_roll)); + accumulated_warp = U256::ZERO; + accumulated_roll = U256::ZERO; + } + } + + Ok(result) +} + +/// Executes a call sequence and returns the optimization value (int256) from the invariant +/// function. Used during shrinking for optimization mode. +/// +/// Returns `None` if the invariant call fails or doesn't return a valid int256. +/// Unlike `check_sequence`, this applies warp/roll from ALL calls (including removed ones). +pub fn check_sequence_value( + mut executor: Executor, + calls: &[BasicTxDetails], + sequence: Vec, + test_address: Address, + calldata: Bytes, +) -> eyre::Result> { + let mut accumulated_warp = U256::ZERO; + let mut accumulated_roll = U256::ZERO; + let mut seq_iter = sequence.iter().peekable(); + + for (idx, tx) in calls.iter().enumerate() { + accumulated_warp += tx.warp.unwrap_or(U256::ZERO); + accumulated_roll += tx.roll.unwrap_or(U256::ZERO); + + if seq_iter.peek() == Some(&&idx) { + seq_iter.next(); + + let tx_with_accumulated = apply_warp_roll(tx, accumulated_warp, accumulated_roll); + let mut call_result = execute_tx(&mut executor, &tx_with_accumulated)?; + + if !call_result.reverted { + executor.commit(&mut call_result); + } + + accumulated_warp = U256::ZERO; + accumulated_roll = U256::ZERO; + } + } + + // Apply any remaining accumulated warp/roll before calling invariant. + apply_warp_roll_to_env(&mut executor, accumulated_warp, accumulated_roll); + + let (inv_result, success) = call_invariant_function(&executor, test_address, calldata)?; + + if success + && inv_result.result.len() >= 32 + && let Some(value) = I256::try_from_be_slice(&inv_result.result[..32]) + { + return Ok(Some(value)); + } + + Ok(None) +} diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index fa9d15b0e4d12..23bd0cd97ce67 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -91,11 +91,14 @@ sol! { #[derive(Clone, Debug)] pub struct Executor { /// The underlying `revm::Database` that contains the EVM storage. + /// + /// Wrapped in `Arc` for efficient cloning during parallel fuzzing. Use [`Arc::make_mut`] + /// for copy-on-write semantics when mutation is needed. // Note: We do not store an EVM here, since we are really // only interested in the database. REVM's `EVM` is a thin // wrapper around spawning a new EVM on every call anyway, // so the performance difference should be negligible. - backend: Backend, + backend: Arc, /// The EVM environment. env: Env, /// The Revm inspector stack. @@ -107,12 +110,6 @@ pub struct Executor { } impl Executor { - /// Creates a new `ExecutorBuilder`. - #[inline] - pub fn builder() -> ExecutorBuilder { - ExecutorBuilder::new() - } - /// Creates a new `Executor` with the given arguments. #[inline] pub fn new( @@ -135,7 +132,7 @@ impl Executor { }, ); - Self { backend, env, inspector, gas_limit, legacy_assertions } + Self { backend: Arc::new(backend), env, inspector, gas_limit, legacy_assertions } } fn clone_with_backend(&self, backend: Backend) -> Self { @@ -145,7 +142,13 @@ impl Executor { self.env.tx.clone(), self.spec_id(), ); - Self::new(backend, env, self.inspector().clone(), self.gas_limit, self.legacy_assertions) + Self { + backend: Arc::new(backend), + env, + inspector: self.inspector().clone(), + gas_limit: self.gas_limit, + legacy_assertions: self.legacy_assertions, + } } /// Returns a reference to the EVM backend. @@ -154,8 +157,11 @@ impl Executor { } /// Returns a mutable reference to the EVM backend. + /// + /// Uses copy-on-write semantics: if other clones of this executor share the backend, + /// this will clone the backend first. pub fn backend_mut(&mut self) -> &mut Backend { - &mut self.backend + Arc::make_mut(&mut self.backend) } /// Returns a reference to the EVM environment. diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 7b63c019e848c..ac506855355dc 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -29,8 +29,8 @@ impl TracingExecutor { state_overrides: Option, ) -> eyre::Result { let db = Backend::spawn(Some(fork))?; - // configures a bare version of the evm executor: no cheatcode inspector is enabled, - // tracing will be enabled only for the targeted transaction + // configures a bare version of the evm executor: no cheatcode and log_collector inspector + // is enabled, tracing will be enabled only for the targeted transaction let mut executor = ExecutorBuilder::new() .inspectors(|stack| { stack.trace_mode(trace_mode).networks(networks).create2_deployer(create2_deployer) diff --git a/crates/evm/evm/src/inspectors/custom_printer.rs b/crates/evm/evm/src/inspectors/custom_printer.rs index 206d4fe3a65a9..b6b1a2aa756c5 100644 --- a/crates/evm/evm/src/inspectors/custom_printer.rs +++ b/crates/evm/evm/src/inspectors/custom_printer.rs @@ -94,11 +94,11 @@ where fn create(&mut self, _context: &mut CTX, inputs: &mut CreateInputs) -> Option { let _ = sh_println!( "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", - inputs.caller, - inputs.scheme, - inputs.value, - inputs.init_code, - inputs.gas_limit + inputs.caller(), + inputs.scheme(), + inputs.value(), + inputs.init_code(), + inputs.gas_limit() ); None } diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index 4dd550caaaf0f..bc09521fa771d 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -1,7 +1,10 @@ use alloy_primitives::Log; use alloy_sol_types::{SolEvent, SolInterface, SolValue}; -use foundry_common::{ErrorExt, fmt::ConsoleFmt}; -use foundry_evm_core::{InspectorExt, abi::console, constants::HARDHAT_CONSOLE_ADDRESS}; +use foundry_common::{ErrorExt, fmt::ConsoleFmt, sh_println}; +use foundry_evm_core::{ + FoundryInspectorExt, abi::console, constants::HARDHAT_CONSOLE_ADDRESS, + decode::decode_console_log, +}; use revm::{ Inspector, context::ContextTr, @@ -14,13 +17,22 @@ use revm::{ /// An inspector that collects logs during execution. /// /// The inspector collects logs from the `LOG` opcodes as well as Hardhat-style `console.sol` logs. -#[derive(Clone, Debug, Default)] -pub struct LogCollector { +#[derive(Clone, Debug)] +pub enum LogCollector { /// The collected logs. Includes both `LOG` opcodes and Hardhat-style `console.sol` logs. - pub logs: Vec, + Capture { logs: Vec }, + /// Print logs directly to stdout. + LiveLogs, } impl LogCollector { + pub fn into_captured_logs(self) -> Option> { + match self { + Self::Capture { logs } => Some(logs), + Self::LiveLogs => None, + } + } + #[cold] fn do_hardhat_log(&mut self, context: &mut CTX, inputs: &CallInputs) -> Option where @@ -41,9 +53,32 @@ impl LogCollector { fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> { let decoded = console::hh::ConsoleCalls::abi_decode(data)?; - self.logs.push(hh_to_ds(&decoded)); + self.push_msg(&decoded.fmt(Default::default())); Ok(()) } + + fn push_raw_log(&mut self, log: Log) { + match self { + Self::Capture { logs } => logs.push(log), + Self::LiveLogs => { + if let Some(msg) = decode_console_log(&log) { + sh_println!("{msg}").expect("fail printing to stdout"); + } else { + // This case should not happen if the users call through forge-std. + // We print the log data for the user nonetheless. + sh_println!("console.log({:?}, {})", log.data.topics(), log.data.data) + .expect("fail printing to stdout"); + } + } + } + } + + fn push_msg(&mut self, msg: &str) { + match self { + Self::Capture { logs } => logs.push(new_console_log(msg)), + Self::LiveLogs => sh_println!("{msg}").expect("fail printing to stdout"), + } + } } impl Inspector for LogCollector @@ -51,7 +86,7 @@ where CTX: ContextTr, { fn log(&mut self, _context: &mut CTX, log: Log) { - self.logs.push(log); + self.push_raw_log(log); } fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option { @@ -62,19 +97,12 @@ where } } -impl InspectorExt for LogCollector { +impl FoundryInspectorExt for LogCollector { fn console_log(&mut self, msg: &str) { - self.logs.push(new_console_log(msg)); + self.push_msg(msg); } } -/// Converts a Hardhat `console.log` call to a DSTest `log(string)` event. -fn hh_to_ds(call: &console::hh::ConsoleCalls) -> Log { - // Convert the parameters of the call to their string representation using `ConsoleFmt`. - let msg = call.fmt(Default::default()); - new_console_log(&msg) -} - /// Creates a `console.log(string)` event. fn new_console_log(msg: &str) -> Log { Log::new_unchecked( diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index a76409308a0b3..7f3f99e6b6a0e 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -11,8 +11,9 @@ use foundry_cheatcodes::{CheatcodeAnalysis, CheatcodesExecutor, Wallets}; use foundry_common::compile::Analysis; use foundry_compilers::ProjectPathsConfig; use foundry_evm_core::{ - ContextExt, Env, InspectorExt, - backend::{DatabaseExt, JournaledState}, + Env, FoundryInspectorExt, InspectorExt, + backend::{DatabaseExt, FoundryJournalExt, JournaledState}, + env::FoundryContextExt, evm::new_evm_with_inspector, }; use foundry_evm_coverage::HitMaps; @@ -21,10 +22,11 @@ use foundry_evm_traces::{SparsedTraceArena, TraceMode}; use revm::{ Inspector, context::{ - BlockEnv, + BlockEnv, Cfg, ContextTr, JournalTr, result::{ExecutionResult, Output}, }, context_interface::CreateScheme, + inspector::JournalExt, interpreter::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, @@ -59,6 +61,9 @@ pub struct InspectorStackBuilder { /// Whether to enable tracing and revert diagnostics. pub trace_mode: TraceMode, /// Whether logs should be collected. + /// - None for no log collection. + /// - Some(true) for realtime console.log-ing. + /// - Some(false) for log collection. pub logs: Option, /// Whether line coverage info should be collected. pub line_coverage: Option, @@ -134,10 +139,10 @@ impl InspectorStackBuilder { self } - /// Set whether to collect logs. + /// Set the log collector, and whether to print the logs directly to stdout. #[inline] - pub fn logs(mut self, yes: bool) -> Self { - self.logs = Some(yes); + pub fn logs(mut self, live_logs: bool) -> Self { + self.logs = Some(live_logs); self } @@ -228,7 +233,7 @@ impl InspectorStackBuilder { stack.set_chisel(chisel_state); } stack.collect_line_coverage(line_coverage.unwrap_or(false)); - stack.collect_logs(logs.unwrap_or(true)); + stack.collect_logs(logs); stack.print(print.unwrap_or(false)); stack.tracing(trace_mode); @@ -366,6 +371,17 @@ impl CheatcodesExecutor for InspectorStackInner { fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> { self.tracer.as_deref_mut() } + + fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option
) { + self.in_inner_context = enabled; + self.inner_context_data = if enabled { + Some(InnerContextData { + original_origin: original_origin.expect("origin required when enabling inner ctx"), + }) + } else { + None + }; + } } impl InspectorStack { @@ -479,9 +495,18 @@ impl InspectorStack { } /// Set whether to enable the log collector. + /// - None for no log collection. + /// - Some(true) for realtime console.log-ing. + /// - Some(false) for log collection. #[inline] - pub fn collect_logs(&mut self, yes: bool) { - self.log_collector = yes.then(Default::default); + pub fn collect_logs(&mut self, live_logs: Option) { + self.log_collector = live_logs.map(|live_logs| { + Box::new(if live_logs { + LogCollector::LiveLogs + } else { + LogCollector::Capture { logs: Vec::new() } + }) + }); } /// Set whether to enable the trace printer. @@ -556,7 +581,7 @@ impl InspectorStack { }); InspectorData { - logs: log_collector.map(|logs| logs.logs).unwrap_or_default(), + logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(), labels: cheatcodes .as_ref() .map(|cheatcodes| cheatcodes.labels.clone()) @@ -579,7 +604,7 @@ impl InspectorStackRefMut<'_> { fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); - ecx.tx.caller = inner_context_data.original_origin; + ecx.tx_mut().caller = inner_context_data.original_origin; } fn do_call_end( @@ -654,29 +679,30 @@ impl InspectorStackRefMut<'_> { gas_limit: u64, value: U256, ) -> (InterpreterResult, Option
) { - let cached_env = Env::from(ecx.cfg.clone(), ecx.block.clone(), ecx.tx.clone()); - - ecx.block.basefee = 0; - ecx.tx.chain_id = Some(ecx.cfg.chain_id); - ecx.tx.caller = caller; - ecx.tx.kind = kind; - ecx.tx.data = input; - ecx.tx.value = value; + let cached_env = Env::from(ecx.cfg().clone(), ecx.block().clone(), ecx.tx().clone()); + + ecx.block_mut().basefee = 0; + ecx.tx_mut().chain_id = Some(ecx.cfg().chain_id()); + ecx.tx_mut().caller = caller; + ecx.tx_mut().kind = kind; + ecx.tx_mut().data = input; + ecx.tx_mut().value = value; // Add 21000 to the gas limit to account for the base cost of transaction. - ecx.tx.gas_limit = gas_limit + 21000; + ecx.tx_mut().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 !ecx.cfg.disable_block_gas_limit { - ecx.tx.gas_limit = std::cmp::min(ecx.tx.gas_limit, ecx.block.gas_limit); + if !ecx.cfg().disable_block_gas_limit { + ecx.tx_mut().gas_limit = std::cmp::min(ecx.tx().gas_limit, ecx.block().gas_limit); } - ecx.tx.gas_price = 0; + ecx.tx_mut().gas_price = 0; self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller }); self.in_inner_context = true; let res = self.with_inspector(|inspector| { - let (db, journal, env) = ecx.as_db_env_and_journal(); + let (journal, env) = ecx.journal_and_env_mut(); + let (db, journal) = journal.as_db_and_inner(); let mut evm = new_evm_with_inspector(db, env.to_owned(), inspector); evm.journaled_state.state = { @@ -726,8 +752,8 @@ impl InspectorStackRefMut<'_> { }; for (addr, mut acc) in res.state { - let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else { - ecx.journaled_state.state.insert(addr, acc); + let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else { + ecx.journal_mut().evm_state_mut().insert(addr, acc); continue; }; @@ -797,7 +823,7 @@ impl InspectorStackRefMut<'_> { if self.enable_isolation { // If we're in isolation mode, we need to keep track of the state at the beginning of // the frame to be able to roll back on revert - self.top_frame_journal.clone_from(&ecx.journaled_state.state); + self.top_frame_journal.clone_from(ecx.journal().evm_state()); } } @@ -821,7 +847,7 @@ impl InspectorStackRefMut<'_> { // created We can't rely on revm's journal because it doesn't account for changes // made by isolated calls if self.enable_isolation { - ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal); + *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal); } } @@ -934,12 +960,12 @@ impl Inspector> for InspectorStackRefMut<'_> ecx: &mut EthEvmContext<&mut dyn DatabaseExt>, call: &mut CallInputs, ) -> Option { - if self.in_inner_context && ecx.journaled_state.depth == 1 { + if self.in_inner_context && ecx.journal().depth() == 1 { self.adjust_evm_data_for_inner_context(ecx); return None; } - if ecx.journaled_state.depth == 0 { + if ecx.journal().depth() == 0 { self.top_level_frame_start(ecx); } @@ -963,7 +989,7 @@ impl Inspector> for InspectorStackRefMut<'_> if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() { // Handle mocked functions, replace bytecode address with mock if matched. - if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) { + if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) { let input_bytes = call.input.bytes(ecx); // Check if any mock function set for call data or if catch-all mock function set // for selector. @@ -981,7 +1007,7 @@ impl Inspector> for InspectorStackRefMut<'_> } } - if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 { + if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 { match call.scheme { // Isolate CALLs CallScheme::Call => { @@ -1003,8 +1029,8 @@ impl Inspector> for InspectorStackRefMut<'_> } // Mark accounts and storage cold before STATICCALLs CallScheme::StaticCall => { - let JournaledState { state, warm_addresses, .. } = - &mut ecx.journaled_state.inner; + let (_, journal_inner) = ecx.journal_mut().as_db_and_inner(); + let JournaledState { state, warm_addresses, .. } = journal_inner; for (addr, acc_mut) in state { // Do not mark accounts and storage cold accounts with arbitrary storage. if let Some(cheatcodes) = &self.cheatcodes @@ -1038,13 +1064,13 @@ impl Inspector> for InspectorStackRefMut<'_> ) { // We are processing inner context outputs in the outer context, so need to avoid processing // twice. - if self.in_inner_context && ecx.journaled_state.depth == 1 { + if self.in_inner_context && ecx.journal().depth() == 1 { return; } self.do_call_end(ecx, inputs, outcome); - if ecx.journaled_state.depth == 0 { + if ecx.journal().depth() == 0 { self.top_level_frame_end(ecx, outcome.result.result); } } @@ -1054,12 +1080,12 @@ impl Inspector> for InspectorStackRefMut<'_> ecx: &mut EthEvmContext<&mut dyn DatabaseExt>, create: &mut CreateInputs, ) -> Option { - if self.in_inner_context && ecx.journaled_state.depth == 1 { + if self.in_inner_context && ecx.journal().depth() == 1 { self.adjust_evm_data_for_inner_context(ecx); return None; } - if ecx.journaled_state.depth == 0 { + if ecx.journal().depth() == 0 { self.top_level_frame_start(ecx); } @@ -1069,18 +1095,18 @@ impl Inspector> for InspectorStackRefMut<'_> |inspector| inspector.create(ecx, create).map(Some), ); - if !matches!(create.scheme, CreateScheme::Create2 { .. }) + if !matches!(create.scheme(), CreateScheme::Create2 { .. }) && self.enable_isolation && !self.in_inner_context - && ecx.journaled_state.depth == 1 + && ecx.journal().depth() == 1 { let (result, address) = self.transact_inner( ecx, TxKind::Create, - create.caller, - create.init_code.clone(), - create.gas_limit, - create.value, + create.caller(), + create.init_code().clone(), + create.gas_limit(), + create.value(), ); return Some(CreateOutcome { result, address }); } @@ -1096,13 +1122,13 @@ impl Inspector> for InspectorStackRefMut<'_> ) { // We are processing inner context outputs in the outer context, so need to avoid processing // twice. - if self.in_inner_context && ecx.journaled_state.depth == 1 { + if self.in_inner_context && ecx.journal().depth() == 1 { return; } self.do_create_end(ecx, call, outcome); - if ecx.journaled_state.depth == 0 { + if ecx.journal().depth() == 0 { self.top_level_frame_end(ecx, outcome.result.result); } } @@ -1116,23 +1142,19 @@ impl Inspector> for InspectorStackRefMut<'_> } } -impl InspectorExt for InspectorStackRefMut<'_> { - fn should_use_create2_factory( - &mut self, - ecx: &mut EthEvmContext<&mut dyn DatabaseExt>, - inputs: &CreateInputs, - ) -> bool { +impl FoundryInspectorExt for InspectorStackRefMut<'_> { + fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool { call_inspectors!( #[ret] [&mut self.cheatcodes], - |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) }, + |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) }, ); false } fn console_log(&mut self, msg: &str) { - call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log( + call_inspectors!([&mut self.log_collector], |inspector| FoundryInspectorExt::console_log( inspector, msg )); } @@ -1223,13 +1245,9 @@ impl Inspector> for InspectorStack { } } -impl InspectorExt for InspectorStack { - fn should_use_create2_factory( - &mut self, - ecx: &mut EthEvmContext<&mut dyn DatabaseExt>, - inputs: &CreateInputs, - ) -> bool { - self.as_mut().should_use_create2_factory(ecx, inputs) +impl FoundryInspectorExt for InspectorStack { + fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool { + self.as_mut().should_use_create2_factory(depth, inputs) } fn get_networks(&self) -> NetworkConfigs { diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index 2cbc3675c6453..5c40b2c264c7b 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -13,7 +13,8 @@ pub mod inspectors; pub use foundry_evm_core as core; pub use foundry_evm_core::{ - Env, EnvMut, EvmEnv, InspectorExt, backend, constants, decode, fork, hardfork, opts, utils, + Env, EnvMut, EvmEnv, FoundryInspectorExt, InspectorExt, backend, constants, decode, fork, + hardfork, opts, utils, }; pub use foundry_evm_coverage as coverage; pub use foundry_evm_fuzz as fuzz; diff --git a/crates/evm/fuzz/src/inspector.rs b/crates/evm/fuzz/src/inspector.rs index 1dfd29ead8278..3776df17001aa 100644 --- a/crates/evm/fuzz/src/inspector.rs +++ b/crates/evm/fuzz/src/inspector.rs @@ -1,10 +1,11 @@ use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState}; use foundry_common::mapping_slots::step as mapping_step; +use foundry_evm_core::constants::CHEATCODE_ADDRESS; use revm::{ Inspector, - context::{ContextTr, Transaction}, + context::{ContextTr, JournalTr, Transaction}, inspector::JournalExt, - interpreter::{CallInput, CallInputs, CallOutcome, CallScheme, Interpreter}, + interpreter::{CallInput, CallInputs, CallOutcome, CallScheme, CallValue, Interpreter}, }; /// An inspector that can fuzz and collect data for that effect. @@ -36,7 +37,7 @@ where fn call(&mut self, ecx: &mut CTX, 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.tx().caller() != inputs.caller { - self.override_call(inputs); + self.override_call(ecx, inputs); } // We only collect `stack` and `memory` data before and after calls. @@ -48,7 +49,10 @@ where fn call_end(&mut self, _context: &mut CTX, _inputs: &CallInputs, _outcome: &mut CallOutcome) { if let Some(ref mut call_generator) = self.call_generator { - call_generator.used = false; + // Decrement depth when any call ends while inside an override + if call_generator.override_depth > 0 { + call_generator.override_depth -= 1; + } } // We only collect `stack` and `memory` data before and after calls. @@ -74,25 +78,75 @@ impl Fuzzer { self.collect = false; } - /// Overrides an external call and tries to call any method of msg.sender. - 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.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.caller, call.target_address) { - call.input = CallInput::Bytes(tx.call_details.calldata.0.into()); - call.caller = tx.sender; - call.target_address = tx.call_details.target; - - // TODO: in what scenarios can the following be problematic - call.bytecode_address = tx.call_details.target; - call_generator.used = true; - } - } + /// Overrides an external call to simulate reentrancy attacks. + /// + /// This function detects reentrancy vulnerabilities by replacing external calls + /// with callbacks that reenter the caller contract. + /// + /// For calls with value (ETH transfers): + /// 1. Performs the ETH transfer via the journal first + /// 2. Replaces the call with a reentrant callback (value = 0) + /// + /// For calls without value: + /// - Replaces the call entirely with a reentrant callback + /// + /// This simulates malicious contracts that immediately reenter when called. + fn override_call(&mut self, ecx: &mut CTX, call: &mut CallInputs) + where + CTX: ContextTr, + { + let Some(ref mut call_generator) = self.call_generator else { + return; + }; + + // Skip if: + // - Caller is test contract (don't override the initial calls from the test) + // - Not a CALL scheme (only override CALLs, not STATICCALLs, DELEGATECALLs, etc.) + // - Inside an override (prevent recursive overrides) + // - Target is cheatcode address + // - Neither caller nor target is a handler contract + // + // We override calls when either the caller OR target is a handler. This covers: + // 1. EtherStore pattern: handler sends ETH out, attacker reenters handler + // 2. Rari pattern: external protocol sends ETH to handler, handler reenters protocol + let caller_is_handler = call_generator.is_handler(call.caller); + let target_is_handler = call_generator.is_handler(call.target_address); + if call.caller == call_generator.test_address + || call.scheme != CallScheme::Call + || call_generator.override_depth > 0 + || call.target_address == CHEATCODE_ADDRESS + || (!caller_is_handler && !target_is_handler) + { + return; } + + // There's only a ~27% chance that an override happens (90% * 30% from strategy). + let Some(tx) = call_generator.next(call.caller, call.target_address) else { + return; + }; + + // For value transfers, perform the ETH transfer before injecting the callback. + // This simulates a malicious receive() that gets the ETH and then reenters. + let value = call.transfer_value().unwrap_or_default(); + let has_value = !value.is_zero() && call.gas_limit > 2300; + if has_value && ecx.journal_mut().transfer(call.caller, call.target_address, value).is_err() + { + return; + } + + // Replace the call with a reentrant callback + call.input = CallInput::Bytes(tx.call_details.calldata.0.into()); + call.caller = tx.sender; + call.target_address = tx.call_details.target; + call.bytecode_address = tx.call_details.target; + // Clear known_bytecode to force REVM to load bytecode from the new target. + // Without this, REVM uses cached bytecode from the original target (e.g., empty + // bytecode for EOA), causing the call to short-circuit before executing any code. + call.known_bytecode = None; + // Clear value since ETH was already transferred above + call.value = CallValue::Transfer(alloy_primitives::U256::ZERO); + + // Track that we're inside an overridden call to avoid recursive overrides + call_generator.override_depth = 1; } } diff --git a/crates/evm/fuzz/src/invariant/call_override.rs b/crates/evm/fuzz/src/invariant/call_override.rs index 0c33c54d4e48d..a9fdb1fc36bfc 100644 --- a/crates/evm/fuzz/src/invariant/call_override.rs +++ b/crates/evm/fuzz/src/invariant/call_override.rs @@ -6,22 +6,30 @@ use proptest::{ strategy::{SBoxedStrategy, Strategy, ValueTree}, test_runner::TestRunner, }; -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; /// Given a TestRunner and a strategy, it generates calls. Used inside the Fuzzer inspector to -/// override external calls to test for potential reentrancy vulnerabilities.. +/// override external calls to test for potential reentrancy vulnerabilities. +/// +/// The key insight is that we only override calls TO handler contracts (targeted contracts). +/// This simulates a malicious contract that reenters when receiving ETH via its receive() function. #[derive(Clone, Debug)] pub struct RandomCallGenerator { /// Address of the test contract. pub test_address: Address, + /// Addresses of handler contracts that can be reentered. + /// We only inject callbacks when the call target is one of these. + pub handler_addresses: Arc>>, /// 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>, /// 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. - pub used: bool, + /// Tracks the call depth when an override is active. When > 0, we're inside an overridden + /// call and should not override nested calls. Incremented when we override a call, + /// decremented when any call ends while inside an override. + pub override_depth: usize, /// If set to `true`, consumes the next call from `last_sequence`, otherwise queries it from /// the strategy. pub replay: bool, @@ -32,21 +40,28 @@ pub struct RandomCallGenerator { impl RandomCallGenerator { pub fn new( test_address: Address, + handler_addresses: HashSet
, runner: TestRunner, strategy: impl Strategy + Send + Sync + 'static, target_reference: Arc>, ) -> Self { Self { test_address, + handler_addresses: Arc::new(RwLock::new(handler_addresses)), runner: Arc::new(Mutex::new(runner)), strategy: weighted(0.9, strategy).sboxed(), target_reference, last_sequence: Arc::default(), replay: false, - used: false, + override_depth: 0, } } + /// Check if the given address is a handler that can be reentered. + pub fn is_handler(&self, address: Address) -> bool { + self.handler_addresses.read().contains(&address) + } + /// All `self.next()` calls will now pop `self.last_sequence`. Used to replay an invariant /// failure. pub fn set_replay(&mut self, status: bool) { diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index a773e8f04bb27..cb99d71771954 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -14,6 +14,12 @@ pub use filters::{ArtifactFilters, SenderFilters}; use foundry_common::{ContractsByAddress, ContractsByArtifact}; use foundry_evm_core::utils::{StateChangeset, get_function}; +/// Returns true if the function returns `int256`, indicating optimization mode. +/// In optimization mode, the fuzzer maximizes the return value instead of checking invariants. +pub fn is_optimization_invariant(func: &Function) -> bool { + func.outputs.len() == 1 && func.outputs[0].ty == "int256" +} + /// Contracts identified as targets during a fuzz run. /// /// During execution, any newly created contract is added as target and used through the rest of @@ -264,3 +270,20 @@ pub struct InvariantContract<'a> { /// ABI of the test contract. pub abi: &'a JsonAbi, } + +impl<'a> InvariantContract<'a> { + /// Creates a new invariant contract. + pub fn new( + address: Address, + invariant_function: &'a Function, + call_after_invariant: bool, + abi: &'a JsonAbi, + ) -> Self { + Self { address, invariant_function, call_after_invariant, abi } + } + + /// Returns true if this is an optimization mode invariant (returns int256). + pub fn is_optimization(&self) -> bool { + is_optimization_invariant(self.invariant_function) + } +} diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index ae1f011413606..44d71fb6deee3 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -36,25 +36,35 @@ pub use inspector::Fuzzer; /// Details of a transaction generated by fuzz strategy for fuzzing a target. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BasicTxDetails { - // Time (in seconds) to increase block timestamp before executing the tx. + /// Time (in seconds) to increase block timestamp before executing the tx. + #[serde(default, skip_serializing_if = "Option::is_none")] pub warp: Option, - // Number to increase block number before executing the tx. + /// Number to increase block number before executing the tx. + #[serde(default, skip_serializing_if = "Option::is_none")] pub roll: Option, - // Transaction sender address. + /// Transaction sender address. pub sender: Address, - // Transaction call details. + /// Transaction call details. + #[serde(flatten)] pub call_details: CallDetails, } /// Call details of a transaction generated to fuzz. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CallDetails { - // Address of target contract. + /// Address of target contract. pub target: Address, - // The data of the transaction. + /// The data of the transaction. pub calldata: Bytes, } +impl BasicTxDetails { + /// Returns an estimate of the serialized (JSON) size in bytes. + pub fn estimate_serialized_size(&self) -> usize { + size_of::() + self.call_details.calldata.len() * 2 + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[expect(clippy::large_enum_variant)] pub enum CounterExample { @@ -277,7 +287,7 @@ pub struct FuzzTestResult { // Deprecated cheatcodes mapped to their replacements. pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>, - /// NUmber of failed replays from persisted corpus. + /// Number of failed replays from persisted corpus. pub failed_corpus_replays: usize, } @@ -307,8 +317,6 @@ impl FuzzTestResult { /// Data of a single fuzz test case #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct FuzzCase { - /// The calldata used for this fuzz test - pub calldata: Bytes, /// Consumed gas pub gas: u64, /// The initial gas stipend for the transaction diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 07495a9cbeba4..0f7ba181be7ac 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -29,20 +29,30 @@ pub fn override_call_strat( let fuzz_state = fuzz_state.clone(); let fuzz_fixtures = fuzz_fixtures.clone(); - let func = { + let (actual_target, func) = { let contracts = contracts.targets.lock(); - 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. - contracts.values().choose(&mut rand::rng()).unwrap() - }); + // If the target address is in the contracts map, use it directly. + // Otherwise, fall back to a random contract from the targeted contracts. + // This can happen when call_override sets target_reference to a contract + // that is not in targetContracts (e.g., the protocol contract during reentrancy). + let (actual_target, contract) = + contracts.get(&target_address).map(|c| (target_address, c)).unwrap_or_else(|| { + let entry = contracts + .iter() + .choose(&mut rand::rng()) + .expect("at least one target contract"); + (*entry.0, entry.1) + }); let fuzzed_functions: Vec<_> = contract.abi_fuzzed_functions().cloned().collect(); - any::().prop_map(move |index| index.get(&fuzzed_functions).clone()) + ( + actual_target, + any::() + .prop_map(move |index| index.get(&fuzzed_functions).clone()), + ) }; func.prop_flat_map(move |func| { - fuzz_contract_with_calldata(&fuzz_state, &fuzz_fixtures, target_address, func) + fuzz_contract_with_calldata(&fuzz_state, &fuzz_fixtures, actual_target, func) }) }) } diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index 115ba8c1e8ffc..ceed0df29f701 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -5,7 +5,10 @@ mod uint; pub use uint::UintStrategy; mod param; -pub use param::{fuzz_param, fuzz_param_from_state, fuzz_param_with_fixtures, mutate_param_value}; +pub use param::{ + fuzz_param, fuzz_param_from_state, fuzz_param_with_fixtures, mutate_param_value, + mutate_param_value_with_senders, +}; mod calldata; pub use calldata::{fuzz_calldata, fuzz_calldata_from_state}; diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index aeef938bbc1a2..54bc341d767e7 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -1,6 +1,9 @@ use super::state::EvmFuzzState; -use crate::strategies::mutators::{ - BitMutator, GaussianNoiseMutator, IncrementDecrementMutator, InterestingWordMutator, +use crate::{ + invariant::SenderFilters, + strategies::mutators::{ + BitMutator, GaussianNoiseMutator, IncrementDecrementMutator, InterestingWordMutator, + }, }; use alloy_dyn_abi::{DynSolType, DynSolValue, Word}; use alloy_primitives::{Address, B256, I256, U256}; @@ -280,12 +283,86 @@ pub fn fuzz_param_from_state( } } +/// Selects a random address for mutation, respecting sender filters if provided. +/// +/// Priority: +/// 1. If `senders` has targeted addresses, pick randomly from those +/// 2. Otherwise, pick from the dictionary state values (excluding any in `senders.excluded`) +/// 3. Returns `None` if no suitable address is found or if the selected address equals `current` +fn select_random_address( + current: Address, + test_runner: &mut TestRunner, + state: &EvmFuzzState, + senders: Option<&SenderFilters>, +) -> Option
{ + if let Some(senders) = senders { + if !senders.targeted.is_empty() { + // Pick from targeted senders + let index = test_runner.rng().random_range(0..senders.targeted.len()); + let addr = senders.targeted[index]; + return (addr != current).then_some(addr); + } + + // Pick from dictionary state values, excluding addresses in the exclusion list + let dict = state.dictionary_read(); + let values = dict.values(); + if values.is_empty() { + return None; + } + + // Try a few times to find a non-excluded address + for _ in 0..10 { + let index = test_runner.rng().random_range(0..values.len()); + let addr = Address::from_word(values[index]); + if addr != current && !senders.excluded.contains(&addr) { + return Some(addr); + } + } + None + } else { + // No sender filters, just pick from dictionary state values + let dict = state.dictionary_read(); + let values = dict.values(); + if values.is_empty() { + None + } else { + let index = test_runner.rng().random_range(0..values.len()); + let addr = Address::from_word(values[index]); + (addr != current).then_some(addr) + } + } +} + /// Mutates the current value of the given parameter type and value. pub fn mutate_param_value( param: &DynSolType, value: DynSolValue, test_runner: &mut TestRunner, state: &EvmFuzzState, +) -> DynSolValue { + mutate_param_value_inner(param, value, test_runner, state, None) +} + +/// Mutates the current value of the given parameter type and value, with optional sender filters. +/// +/// When `senders` is provided and has targeted addresses, address mutations will prefer +/// selecting from those targeted addresses (similar to `select_random_sender` behavior). +pub fn mutate_param_value_with_senders( + param: &DynSolType, + value: DynSolValue, + test_runner: &mut TestRunner, + state: &EvmFuzzState, + senders: &SenderFilters, +) -> DynSolValue { + mutate_param_value_inner(param, value, test_runner, state, Some(senders)) +} + +fn mutate_param_value_inner( + param: &DynSolType, + value: DynSolValue, + test_runner: &mut TestRunner, + state: &EvmFuzzState, + senders: Option<&SenderFilters>, ) -> DynSolValue { let new_value = |param: &DynSolType, test_runner: &mut TestRunner| { fuzz_param_from_state(param, state) @@ -322,12 +399,14 @@ pub fn mutate_param_value( _ => unreachable!(), } .map(|v| DynSolValue::Int(v, size)), - DynSolValue::Address(val) => match test_runner.rng().random_range(0..=4) { + DynSolValue::Address(val) => match test_runner.rng().random_range(0..=5) { 0 => Address::flip_random_bit(val, 20, test_runner), 1 => Address::mutate_interesting_byte(val, 20, test_runner), 2 => Address::mutate_interesting_word(val, 20, test_runner), 3 => Address::mutate_interesting_dword(val, 20, test_runner), - 4 => None, + // Replace with a random address from targeted senders or dictionary. + 4 => select_random_address(val, test_runner, state, senders), + 5 => None, _ => unreachable!(), } .map(DynSolValue::Address), @@ -343,7 +422,13 @@ pub fn mutate_param_value( // Increase array size. 1 => values.push(new_value(param_type, test_runner)), // Mutate random array element. - 2 => mutate_random_array_value(&mut values, param_type, test_runner, state), + 2 => mutate_random_array_value( + &mut values, + param_type, + test_runner, + state, + senders, + ), _ => unreachable!(), } Some(DynSolValue::Array(values)) @@ -355,7 +440,7 @@ pub fn mutate_param_value( if let DynSolType::FixedArray(param_type, _size) = param && !values.is_empty() { - mutate_random_array_value(&mut values, param_type, test_runner, state); + mutate_random_array_value(&mut values, param_type, test_runner, state, senders); Some(DynSolValue::FixedArray(values)) } else { None @@ -376,7 +461,7 @@ pub fn mutate_param_value( && !values.is_empty() { // Mutate random struct element. - mutate_random_tuple_value(&mut values, tuple_types, test_runner, state); + mutate_random_tuple_value(&mut values, tuple_types, test_runner, state, senders); Some(DynSolValue::CustomStruct { name, prop_names, tuple: values }) } else { None @@ -387,7 +472,7 @@ pub fn mutate_param_value( && !values.is_empty() { // Mutate random tuple element. - mutate_random_tuple_value(&mut values, tuple_types, test_runner, state); + mutate_random_tuple_value(&mut values, tuple_types, test_runner, state, senders); Some(DynSolValue::Tuple(values)) } else { None @@ -404,11 +489,12 @@ fn mutate_random_tuple_value( tuple_types: &[DynSolType], test_runner: &mut TestRunner, state: &EvmFuzzState, + senders: Option<&SenderFilters>, ) { let id = test_runner.rng().random_range(0..tuple_values.len()); let param_type = &tuple_types[id]; let old_val = replace(&mut tuple_values[id], DynSolValue::Bool(false)); - let new_val = mutate_param_value(param_type, old_val, test_runner, state); + let new_val = mutate_param_value_inner(param_type, old_val, test_runner, state, senders); tuple_values[id] = new_val; } @@ -418,10 +504,11 @@ fn mutate_random_array_value( element_type: &DynSolType, test_runner: &mut TestRunner, state: &EvmFuzzState, + senders: Option<&SenderFilters>, ) { let elem = array_values.choose_mut(&mut test_runner.rng()).unwrap(); let old_val = replace(elem, DynSolValue::Bool(false)); - let new_val = mutate_param_value(element_type, old_val, test_runner, state); + let new_val = mutate_param_value_inner(element_type, old_val, test_runner, state, senders); *elem = new_val; } @@ -506,4 +593,151 @@ mod tests { assert!(generated_hashes.contains(&keccak256("hello"))); assert!(generated_hashes.contains(&keccak256("world"))); } + + #[test] + fn mutate_address_can_select_from_dictionary() { + use super::mutate_param_value; + use alloy_dyn_abi::{DynSolType, DynSolValue}; + use alloy_primitives::Address; + + let state = EvmFuzzState::test(); + + // Add addresses to dictionary via state values. + let addr1 = Address::repeat_byte(0x11); + let addr2 = Address::repeat_byte(0x22); + let addr3 = Address::repeat_byte(0x33); + state.collect_values([addr1.into_word(), addr2.into_word(), addr3.into_word()]); + + let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; + let mut runner = proptest::test_runner::TestRunner::new(cfg); + + // Mutate an address many times and verify we can get addresses from the dictionary. + let original = Address::repeat_byte(0xff); + let mut got_addr1 = false; + let mut got_addr2 = false; + let mut got_addr3 = false; + + for _ in 0..1000 { + let mutated = mutate_param_value( + &DynSolType::Address, + DynSolValue::Address(original), + &mut runner, + &state, + ); + if let DynSolValue::Address(addr) = mutated { + if addr == addr1 { + got_addr1 = true; + } + if addr == addr2 { + got_addr2 = true; + } + if addr == addr3 { + got_addr3 = true; + } + } + if got_addr1 && got_addr2 && got_addr3 { + break; + } + } + + // We should have seen at least one dictionary address in 1000 iterations. + assert!( + got_addr1 || got_addr2 || got_addr3, + "Address mutation should select addresses from dictionary" + ); + } + + #[test] + fn mutate_address_prefers_targeted_senders() { + use super::select_random_address; + use crate::invariant::SenderFilters; + use alloy_primitives::Address; + + let state = EvmFuzzState::test(); + + // Add addresses to dictionary (these should NOT be selected when targeted is set). + let dict_addr = Address::repeat_byte(0xdd); + state.collect_values([dict_addr.into_word()]); + + // Set up targeted senders. + let targeted1 = Address::repeat_byte(0x11); + let targeted2 = Address::repeat_byte(0x22); + let senders = SenderFilters::new(vec![targeted1, targeted2], vec![]); + + let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; + let mut runner = proptest::test_runner::TestRunner::new(cfg); + + // Call select_random_address directly to verify it uses targeted senders. + let original = Address::repeat_byte(0xff); + let mut got_targeted1 = false; + let mut got_targeted2 = false; + let mut got_dict = false; + + for _ in 0..100 { + if let Some(addr) = select_random_address(original, &mut runner, &state, Some(&senders)) + { + if addr == targeted1 { + got_targeted1 = true; + } + if addr == targeted2 { + got_targeted2 = true; + } + if addr == dict_addr { + got_dict = true; + } + } + } + + // Should see targeted addresses, never dictionary address. + assert!( + got_targeted1 || got_targeted2, + "select_random_address should select from targeted senders" + ); + assert!( + !got_dict, + "select_random_address should not select from dictionary when targeted senders are set" + ); + } + + #[test] + fn mutate_address_respects_excluded_senders() { + use super::select_random_address; + use crate::invariant::SenderFilters; + use alloy_primitives::Address; + + let state = EvmFuzzState::test(); + + // Add addresses to dictionary. + let addr1 = Address::repeat_byte(0x11); + let addr2 = Address::repeat_byte(0x22); + let excluded_addr = Address::repeat_byte(0xee); + state.collect_values([addr1.into_word(), addr2.into_word(), excluded_addr.into_word()]); + + // Exclude one address. + let senders = SenderFilters::new(vec![], vec![excluded_addr]); + + let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; + let mut runner = proptest::test_runner::TestRunner::new(cfg); + + // Call select_random_address directly to verify it respects excluded senders. + let original = Address::repeat_byte(0xff); + let mut got_excluded = false; + let mut got_valid = false; + + for _ in 0..100 { + if let Some(addr) = select_random_address(original, &mut runner, &state, Some(&senders)) + { + if addr == excluded_addr { + got_excluded = true; + break; + } + if addr == addr1 || addr == addr2 { + got_valid = true; + } + } + } + + assert!(!got_excluded, "select_random_address should not select excluded addresses"); + assert!(got_valid, "select_random_address should select valid (non-excluded) addresses"); + } } diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 4eed2fcf4f10b..0d31cfca6a7ae 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -47,6 +47,3 @@ tempfile.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true yansi.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 38f40b244c042..f312f2a4def1b 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -323,7 +323,7 @@ impl CallTraceDecoder { self.contracts.entry(address).or_insert(contract); } - if let Some(label) = label { + if let Some(label) = label.filter(|s| !s.is_empty()) { self.labels.entry(address).or_insert(label); } diff --git a/crates/evm/traces/src/folded_stack_trace.rs b/crates/evm/traces/src/folded_stack_trace.rs index 80ac8a8dcb620..2b4e58b8665a7 100644 --- a/crates/evm/traces/src/folded_stack_trace.rs +++ b/crates/evm/traces/src/folded_stack_trace.rs @@ -5,20 +5,26 @@ use revm_inspectors::tracing::{ }; /// Builds a folded stack trace from a call trace arena. -pub fn build(arena: &CallTraceArena) -> Vec { - let mut fst = EvmFoldedStackTraceBuilder::default(); +pub fn build(arena: &CallTraceArena, isolate: bool) -> Vec { + let mut fst = EvmFoldedStackTraceBuilder::new(isolate); fst.process_call_node(arena.nodes(), 0); fst.build() } /// Wrapper for building a folded stack trace using EVM call trace node. -#[derive(Default)] pub struct EvmFoldedStackTraceBuilder { + /// Trace produced in isolate mode, meaning refund needs to be reversed at the depth=1 + /// frame for consistent gas values. + isolate: bool, /// Raw folded stack trace builder. fst: FoldedStackTraceBuilder, } impl EvmFoldedStackTraceBuilder { + pub fn new(isolate: bool) -> Self { + Self { isolate, fst: FoldedStackTraceBuilder::default() } + } + /// Returns the folded stack trace. pub fn build(self) -> Vec { self.fst.build() @@ -57,7 +63,13 @@ impl EvmFoldedStackTraceBuilder { } }; - self.fst.enter(func_name, node.trace.gas_used as i64); + let mut gas_used = node.trace.gas_used; + let max_refund_adjust_depth = if self.isolate { 1 } else { 0 }; + if node.trace.depth <= max_refund_adjust_depth { + gas_used += node.trace.gas_refund_counter; + } + + self.fst.enter(func_name, gas_used); // Track internal function step exits to do in this call context. let mut step_exits = vec![]; @@ -99,8 +111,8 @@ impl EvmFoldedStackTraceBuilder { if let Some(decoded_step) = &step.decoded { match decoded_step.as_ref() { DecodedTraceStep::InternalCall(decoded_internal_call, step_end_idx) => { - let gas_used = steps[*step_end_idx].gas_used.saturating_sub(step.gas_used); - self.fst.enter(decoded_internal_call.func_name.clone(), gas_used as i64); + let gas_used = step.gas_remaining - steps[*step_end_idx].gas_remaining; + self.fst.enter(decoded_internal_call.func_name.clone(), gas_used); step_exits.push(*step_end_idx); } DecodedTraceStep::Line(_) => {} @@ -158,13 +170,13 @@ pub struct FoldedStackTraceBuilder { struct TraceEntry { /// Names of all functions in the call stack of this trace. names: Vec, - /// Gas consumed by this function, allowed to be negative due to refunds. - gas: i64, + /// Gas consumed by this function, not including refunds. + gas: u64, } impl FoldedStackTraceBuilder { /// Enter execution of a function call that consumes `gas`. - pub fn enter(&mut self, label: String, gas: i64) { + pub fn enter(&mut self, label: String, gas: u64) { let mut names = self.traces.last().map(|entry| entry.names.clone()).unwrap_or_default(); while self.exits > 0 { @@ -189,7 +201,7 @@ impl FoldedStackTraceBuilder { /// Internal method to build the folded stack trace without subtracting gas consumed by /// the children function calls. - fn build_without_subtraction(&mut self) -> Vec { + pub fn build_without_subtraction(&mut self) -> Vec { let mut lines = Vec::new(); for TraceEntry { names, gas } in &self.traces { lines.push(format!("{} {}", names.join(";"), gas)); diff --git a/crates/evm/traces/src/identifier/external.rs b/crates/evm/traces/src/identifier/external.rs index b489882a13d39..6702287200680 100644 --- a/crates/evm/traces/src/identifier/external.rs +++ b/crates/evm/traces/src/identifier/external.rs @@ -2,7 +2,7 @@ use super::{IdentifiedAddress, TraceIdentifier}; use crate::debug::ContractSources; use alloy_primitives::{ Address, - map::{Entry, HashMap}, + map::{Entry, HashMap, HashSet}, }; use eyre::WrapErr; use foundry_block_explorers::{contract::Metadata, errors::EtherscanError}; @@ -61,7 +61,14 @@ impl ExternalIdentifier { } if let Some(config) = config { debug!(target: "evm::traces::external", chain=?config.chain, url=?config.api_url, "using etherscan identifier"); - fetchers.push(Arc::new(EtherscanFetcher::new(config.into_client()?))); + match config.into_client() { + Ok(client) => { + fetchers.push(Arc::new(EtherscanFetcher::new(client))); + } + Err(err) => { + warn!(target: "evm::traces::external", ?err, "failed to create etherscan client"); + } + } } if fetchers.is_empty() { debug!(target: "evm::traces::external", "no fetchers enabled"); @@ -151,7 +158,7 @@ impl TraceIdentifier for ExternalIdentifier { trace!(target: "evm::traces::external", "identify {} addresses", nodes.len()); let mut identities = Vec::new(); - let mut to_fetch = Vec::new(); + let mut to_fetch = HashSet::new(); // Check cache first. for &node in nodes { @@ -163,7 +170,7 @@ impl TraceIdentifier for ExternalIdentifier { // Do nothing. We know that this contract was not verified. } } else { - to_fetch.push(address); + to_fetch.insert(address); } } @@ -172,6 +179,7 @@ impl TraceIdentifier for ExternalIdentifier { } trace!(target: "evm::traces::external", "fetching {} addresses", to_fetch.len()); + let to_fetch = to_fetch.into_iter().collect::>(); let fetchers = self.fetchers.iter().map(|fetcher| ExternalFetcher::new(fetcher.clone(), &to_fetch)); let fetched_identities = foundry_common::block_on( @@ -408,8 +416,6 @@ impl ExternalFetcherT for SourcifyFetcher { let url = format!("{url}/{address}?fields=abi,compilation", url = self.url); let response = self.client.get(url).send().await?; let code = response.status(); - let response: SourcifyResponse = response.json().await?; - trace!(target: "evm::traces::external", "Sourcify response for {address}: {response:#?}"); match code.as_u16() { // Not verified. 404 => return Err(EtherscanError::ContractCodeNotVerified(address)), @@ -417,6 +423,8 @@ impl ExternalFetcherT for SourcifyFetcher { 429 => return Err(EtherscanError::RateLimitExceeded), _ => {} } + let response: SourcifyResponse = response.json().await?; + trace!(target: "evm::traces::external", "Sourcify response for {address}: {response:#?}"); match response { SourcifyResponse::Success(metadata) => Ok(Some(metadata.into())), SourcifyResponse::Error(error) => Err(EtherscanError::Unknown(format!("{error:#?}"))), diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index 57884af234e6b..bad5c577bc69e 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -24,6 +24,5 @@ similar = { version = "2", features = ["inline"] } [dev-dependencies] foundry-test-utils.workspace = true -itertools.workspace = true toml.workspace = true snapbox.workspace = true diff --git a/crates/fmt/README.md b/crates/fmt/README.md index 65f05d6c82847..2a151009b1019 100644 --- a/crates/fmt/README.md +++ b/crates/fmt/README.md @@ -127,6 +127,7 @@ The formatter supports multiple configuration options defined in `foundry.toml`. | `ignore` | `[]` | Globs to ignore. | | `contract_new_lines` | `false` | Add a new line at the start and end of contract declarations. | | `sort_imports` | `false` | Sort import statements alphabetically in groups. A group is a set of imports separated by a newline. | +| `namespace_import_style` | `prefer_plain` | Style for namespace imports. Options: `prefer_plain` (`import "foo" as foo;`), `prefer_glob` (`import * as foo from "foo";`), `preserve`. | | `pow_no_space` | `false` | Suppress spaces around the power operator (`**`). | | `single_line_imports` | `false` | Keep single imports on a single line, even if they exceed the line length limit. | diff --git a/crates/fmt/src/state/mod.rs b/crates/fmt/src/state/mod.rs index 762ecdb4ce645..e98eac6f4e5c1 100644 --- a/crates/fmt/src/state/mod.rs +++ b/crates/fmt/src/state/mod.rs @@ -38,15 +38,18 @@ pub(super) struct CallContext { /// The size of the callee's head, excluding its arguments. pub(super) size: usize, + + /// Whether this chain context added its own indentation box. + pub(super) has_indent: bool, } impl CallContext { pub(super) fn nested(size: usize) -> Self { - Self { kind: CallContextKind::Nested, size } + Self { kind: CallContextKind::Nested, size, has_indent: false } } - pub(super) fn chained(size: usize) -> Self { - Self { kind: CallContextKind::Chained, size } + pub(super) fn chained(size: usize, has_indent: bool) -> Self { + Self { kind: CallContextKind::Chained, size, has_indent } } pub(super) fn is_nested(&self) -> bool { @@ -83,8 +86,10 @@ impl CallStack { self.last().is_some_and(|call| call.is_nested()) } - pub(crate) fn is_chain(&self) -> bool { - self.last().is_some_and(|call| call.is_chained()) + /// Returns true if any chained call in the stack has its own indentation. + /// Used to determine if commasep should skip its own indentation (to avoid double indent). + pub(crate) fn has_chain_with_indent(&self) -> bool { + self.stack.iter().any(|call| call.is_chained() && call.has_indent) } } diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index a8c9bd6647de1..f46455b2a6b5d 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -205,16 +205,32 @@ impl<'ast> State<'_, 'ast> { fn print_import(&mut self, import: &'ast ast::ImportDirective<'ast>) { let ast::ImportDirective { path, items } = import; self.word("import "); - match items { - ast::ImportItems::Plain(_) | ast::ImportItems::Glob(_) => { + + use ast::ImportItems; + use config::NamespaceImportStyle as NIStyle; + + match (items, self.config.namespace_import_style) { + (ImportItems::Plain(None), _) => { self.print_ast_str_lit(path); - if let Some(ident) = items.source_alias() { - self.word(" as "); - self.print_ident(&ident); - } } - ast::ImportItems::Aliases(aliases) => { + (ImportItems::Plain(Some(source_alias)), NIStyle::Preserve | NIStyle::PreferPlain) + | (ImportItems::Glob(source_alias), NIStyle::PreferPlain) => { + self.print_ast_str_lit(path); + self.word(" as "); + self.print_ident(source_alias); + } + + (ImportItems::Glob(source_alias), NIStyle::Preserve | NIStyle::PreferGlob) + | (ImportItems::Plain(Some(source_alias)), NIStyle::PreferGlob) => { + self.word("*"); + self.word(" as "); + self.print_ident(source_alias); + self.word(" from "); + self.print_ast_str_lit(path); + } + + (ImportItems::Aliases(aliases), _) => { // Check if we should keep single imports on one line let use_single_line = self.config.single_line_imports && aliases.len() == 1; @@ -391,7 +407,9 @@ impl<'ast> State<'_, 'ast> { self.block_depth -= 1; } else { if self.print_comments(span.hi(), CommentConfig::skip_ws()).is_some() { - self.zerobreak(); + // Adjust the offset of the trailing break from comment printing + // so the closing brace is not indented + self.s.offset(-self.ind); } else if self.config.bracket_spacing { self.nbsp(); }; @@ -1335,6 +1353,11 @@ impl<'ast> State<'_, 'ast> { match member_expr.kind { ast::ExprKind::Ident(_) | ast::ExprKind::Type(_) => (), ast::ExprKind::Index(..) if s.skip_index_break => (), + // Don't add break when accessing a field after a call with named args. + // e.g., `_lzSend({_dstEid: x, ...}).guid` should keep `.guid` + // on the same line as the closing `})`. + // See: https://github.com/foundry-rs/foundry/issues/12399 + _ if is_call_with_named_args(&member_expr.kind) => (), _ => s.zerobreak(), } s.word("."); @@ -1667,11 +1690,6 @@ impl<'ast> State<'_, 'ast> { let callee_size = get_callee_head_size(child_expr) + member_or_args.member_size(); let expr_size = self.estimate_size(child_expr.span); - // Start a new chain if needed - if is_call_chain(&child_expr.kind, false) { - self.call_stack.push(CallContext::chained(callee_size)); - } - let callee_fits_line = self.space_left() > callee_size + 1; let total_fits_line = self.space_left() > expr_size + member_or_args.size() + 2; let no_cmnt_or_mixed = @@ -1683,13 +1701,20 @@ impl<'ast> State<'_, 'ast> { extra_box = true; } - if !is_call_chain(&child_expr.kind, true) - && (no_cmnt_or_mixed || matches!(&child_expr.kind, ast::ExprKind::CallOptions(..))) - && callee_fits_line - && (member_depth(0, child_expr) < 2 - // calls with cmnts between the args always break - || (total_fits_line && !member_or_args.has_comments())) - { + // Determine if this chain will add its own indentation + let chain_has_indent = is_call_chain(&child_expr.kind, true) + || !(no_cmnt_or_mixed + || matches!(&child_expr.kind, ast::ExprKind::CallOptions(..))) + || !callee_fits_line + || (member_depth(0, child_expr) >= 2 + && (!total_fits_line || member_or_args.has_comments())); + + // Start a new chain if needed + if is_call_chain(&child_expr.kind, false) { + self.call_stack.push(CallContext::chained(callee_size, chain_has_indent)); + } + + if !chain_has_indent { self.skip_index_break = true; self.cbox(0); } else { @@ -1796,7 +1821,7 @@ impl<'ast> State<'_, 'ast> { list_format .break_cmnts() .break_single(true) - .without_ind(self.call_stack.is_chain()) + .without_ind(self.call_stack.has_chain_with_indent()) .with_delimiters(!self.call_with_opts_and_args), ); } else if self.config.bracket_spacing { @@ -2408,20 +2433,19 @@ impl<'ast> State<'_, 'ast> { // If possible, take an early decision based on the block style configuration. match self.config.single_line_statement_blocks { - config::SingleLineBlockStyle::Preserve => { - if self.is_stmt_in_new_line(cond, then) || self.is_multiline_block_stmt(then, true) - { - return Decision { outcome: false, is_cached: false }; - } + config::SingleLineBlockStyle::Preserve + if self.is_stmt_in_new_line(cond, then) + || self.is_multiline_block_stmt(then, true) => + { + return Decision { outcome: false, is_cached: false }; } - config::SingleLineBlockStyle::Single => { - if self.is_multiline_block_stmt(then, true) { - return Decision { outcome: false, is_cached: false }; - } + config::SingleLineBlockStyle::Single if self.is_multiline_block_stmt(then, true) => { + return Decision { outcome: false, is_cached: false }; } config::SingleLineBlockStyle::Multi => { return Decision { outcome: false, is_cached: false }; } + _ => {} }; // If no decision was made, estimate the length to be formatted. @@ -2505,11 +2529,17 @@ impl<'ast> State<'_, 'ast> { false } - /// Checks if a block statement `{ ... }` contains more than one line of actual code. + /// Checks if a block statement `{ ... }` should be treated as multiline, + /// either because it spans multiple lines or contains multiple statements. fn is_multiline_block(&self, block: &'ast ast::Block<'ast>, empty_as_multiline: bool) -> bool { if block.stmts.is_empty() { return empty_as_multiline; } + // A block with multiple statements should never be inlined, regardless of + // whether it was written on a single line in the source. + if block.stmts.len() > 1 { + return true; + } if self.sm.is_multiline(block.span) && let Ok(snip) = self.sm.span_to_snippet(block.span) { @@ -2873,6 +2903,18 @@ fn is_call(expr_kind: &ast::ExprKind<'_>) -> bool { matches!(expr_kind, ast::ExprKind::Call(..)) } +/// Returns true if this is a call with named arguments (struct-style syntax). +/// Used to determine if `.field` after such a call should avoid breaking. +/// E.g., `_lzSend({_dstEid: x, ...}).guid` → true (named args call) +/// E.g., `someFunc(a, b).field` → false (positional args) +fn is_call_with_named_args(expr_kind: &ast::ExprKind<'_>) -> bool { + if let ast::ExprKind::Call(_, args) = expr_kind { + matches!(args.kind, ast::CallArgsKind::Named(_)) + } else { + false + } +} + fn is_call_chain(expr_kind: &ast::ExprKind<'_>, must_have_child: bool) -> bool { if let ast::ExprKind::Member(child, ..) = expr_kind { is_call_chain(&child.kind, false) diff --git a/crates/fmt/testdata/CommentEmptyLine/fmt.sol b/crates/fmt/testdata/CommentEmptyLine/fmt.sol new file mode 100644 index 0000000000000..a2407017f4be9 --- /dev/null +++ b/crates/fmt/testdata/CommentEmptyLine/fmt.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment +} diff --git a/crates/fmt/testdata/CommentEmptyLine/original.sol b/crates/fmt/testdata/CommentEmptyLine/original.sol new file mode 100644 index 0000000000000..9c7f35770e9f3 --- /dev/null +++ b/crates/fmt/testdata/CommentEmptyLine/original.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment + +} diff --git a/crates/fmt/testdata/ImportDirective/namespace-import-prefer-glob.fmt.sol b/crates/fmt/testdata/ImportDirective/namespace-import-prefer-glob.fmt.sol new file mode 100644 index 0000000000000..e930ceee0754d --- /dev/null +++ b/crates/fmt/testdata/ImportDirective/namespace-import-prefer-glob.fmt.sol @@ -0,0 +1,26 @@ +// config: namespace_import_style = "prefer_glob" +import "SomeFile.sol"; +import "SomeFile.sol"; +import * as SomeOtherFile from "SomeFile.sol"; +import * as SomeOtherFile from "SomeFile.sol"; +import * as SomeSymbol from "AnotherFile.sol"; +import * as SomeSymbol from "AnotherFile.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/ImportDirective/namespace-import-prefer-plain.fmt.sol b/crates/fmt/testdata/ImportDirective/namespace-import-prefer-plain.fmt.sol new file mode 100644 index 0000000000000..6ea4cabeeb1ec --- /dev/null +++ b/crates/fmt/testdata/ImportDirective/namespace-import-prefer-plain.fmt.sol @@ -0,0 +1,26 @@ +// config: namespace_import_style = "prefer_plain" +import "SomeFile.sol"; +import "SomeFile.sol"; +import "SomeFile.sol" as SomeOtherFile; +import "SomeFile.sol" as SomeOtherFile; +import "AnotherFile.sol" as SomeSymbol; +import "AnotherFile.sol" as SomeSymbol; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/ImportDirective/namespace-import-preserve.fmt.sol b/crates/fmt/testdata/ImportDirective/namespace-import-preserve.fmt.sol new file mode 100644 index 0000000000000..607817b9eadfb --- /dev/null +++ b/crates/fmt/testdata/ImportDirective/namespace-import-preserve.fmt.sol @@ -0,0 +1,26 @@ +// config: namespace_import_style = "preserve" +import "SomeFile.sol"; +import "SomeFile.sol"; +import "SomeFile.sol" as SomeOtherFile; +import "SomeFile.sol" as SomeOtherFile; +import * as SomeSymbol from "AnotherFile.sol"; +import * as SomeSymbol from "AnotherFile.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index 3ccbaab706ea1..771c86e6a641e 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -393,3 +393,37 @@ contract WETHHook is BaseTokenWrapperHook { weth = WETH(payable(_weth)); } } + +// https://github.com/foundry-rs/foundry/issues/12529 +library TransferMessageLib { + struct TransferMessage { + bytes32 sender; + address receiver; + address token; + uint256 amount; + } + + function pack(TransferMessage memory m) + internal + pure + returns (bytes memory) + { + return ""; + } +} + +contract ChainedStructCall { + using TransferMessageLib for TransferMessageLib.TransferMessage; + bytes32 someBytes32Value; + address someAddressValue; + uint256 someUint256Value; + + function test_chainedStructIndentation() public { + bytes memory payload = TransferMessageLib.TransferMessage({ + sender: someBytes32Value, + receiver: someAddressValue, + token: someAddressValue, + amount: someUint256Value + }).pack(); + } +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index f9ae36eb3f2fe..0a37ed6f9807d 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -405,3 +405,22 @@ contract WETHHook is BaseTokenWrapperHook { weth = WETH(payable(_weth)); } } + +// https://github.com/foundry-rs/foundry/issues/12529 +library TransferMessageLib { + struct TransferMessage { bytes32 sender; address receiver; address token; uint256 amount; } + function pack(TransferMessage memory m) internal pure returns (bytes memory) { return ""; } +} + +contract ChainedStructCall { + using TransferMessageLib for TransferMessageLib.TransferMessage; + bytes32 someBytes32Value; + address someAddressValue; + uint256 someUint256Value; + + function test_chainedStructIndentation() public { + bytes memory payload = TransferMessageLib.TransferMessage({ + sender: someBytes32Value, receiver: someAddressValue, token: someAddressValue, amount: someUint256Value + }).pack(); + } +} diff --git a/crates/fmt/testdata/Repros/sorted.fmt.sol b/crates/fmt/testdata/Repros/sorted.fmt.sol index 4a155b28c8b42..0d56601d6c098 100644 --- a/crates/fmt/testdata/Repros/sorted.fmt.sol +++ b/crates/fmt/testdata/Repros/sorted.fmt.sol @@ -394,3 +394,37 @@ contract WETHHook is BaseTokenWrapperHook { weth = WETH(payable(_weth)); } } + +// https://github.com/foundry-rs/foundry/issues/12529 +library TransferMessageLib { + struct TransferMessage { + bytes32 sender; + address receiver; + address token; + uint256 amount; + } + + function pack(TransferMessage memory m) + internal + pure + returns (bytes memory) + { + return ""; + } +} + +contract ChainedStructCall { + using TransferMessageLib for TransferMessageLib.TransferMessage; + bytes32 someBytes32Value; + address someAddressValue; + uint256 someUint256Value; + + function test_chainedStructIndentation() public { + bytes memory payload = TransferMessageLib.TransferMessage({ + sender: someBytes32Value, + receiver: someAddressValue, + token: someAddressValue, + amount: someUint256Value + }).pack(); + } +} diff --git a/crates/fmt/testdata/Repros/tab.fmt.sol b/crates/fmt/testdata/Repros/tab.fmt.sol index e398157b75ae6..b423e45507513 100644 --- a/crates/fmt/testdata/Repros/tab.fmt.sol +++ b/crates/fmt/testdata/Repros/tab.fmt.sol @@ -394,3 +394,37 @@ contract WETHHook is BaseTokenWrapperHook { weth = WETH(payable(_weth)); } } + +// https://github.com/foundry-rs/foundry/issues/12529 +library TransferMessageLib { + struct TransferMessage { + bytes32 sender; + address receiver; + address token; + uint256 amount; + } + + function pack(TransferMessage memory m) + internal + pure + returns (bytes memory) + { + return ""; + } +} + +contract ChainedStructCall { + using TransferMessageLib for TransferMessageLib.TransferMessage; + bytes32 someBytes32Value; + address someAddressValue; + uint256 someUint256Value; + + function test_chainedStructIndentation() public { + bytes memory payload = TransferMessageLib.TransferMessage({ + sender: someBytes32Value, + receiver: someAddressValue, + token: someAddressValue, + amount: someUint256Value + }).pack(); + } +} diff --git a/crates/fmt/testdata/StructFieldAccess/fmt.sol b/crates/fmt/testdata/StructFieldAccess/fmt.sol new file mode 100644 index 0000000000000..22b16bde3f77c --- /dev/null +++ b/crates/fmt/testdata/StructFieldAccess/fmt.sol @@ -0,0 +1,46 @@ +// config: line_length = 120 +// https://github.com/foundry-rs/foundry/issues/12399 +contract StructFieldAccess { + function a() external { + bytes32 guid = + _lzSend({ + _dstEid: dstEid, + _message: message, + _options: OptionsBuilder.newOptions().addExecutorLzReceiveOption({_gas: gasLimit, _value: 0}), + _fee: MessagingFee({nativeFee: msg.value, lzTokenFee: 0}), + _refundAddress: msg.sender + }).guid; + } + + function b() external view returns (uint256) { + return _quote({ + _dstEid: dstEid, + _message: message, + _options: OptionsBuilder.newOptions().addExecutorLzReceiveOption({_gas: gasLimit, _value: 0}), + _payInLzToken: false + }).nativeFee; + } + + // Simple cases + function c() external { + uint256 val = getData().value; + bool flag = getStruct({param: 1}).isActive; + } + + // Nested struct field access + function d() external { + uint256 nested = getOuter().inner.value; + } + + // Chained calls with named args + function e() external { + bytes32 guid = + _lzSend({ + _dstEid: dstEid, + _message: message, + _options: OptionsBuilder.newOptions().addExecutorLzReceiveOption({_gas: gasLimit, _value: 0}), + _fee: MessagingFee({nativeFee: msg.value, lzTokenFee: 0}), + _refundAddress: msg.sender + }).wrap({wrapper: wrapperAddress, extraData: bytes("")}).guid; + } +} diff --git a/crates/fmt/testdata/StructFieldAccess/original.sol b/crates/fmt/testdata/StructFieldAccess/original.sol new file mode 100644 index 0000000000000..7671273c028be --- /dev/null +++ b/crates/fmt/testdata/StructFieldAccess/original.sol @@ -0,0 +1,43 @@ +// https://github.com/foundry-rs/foundry/issues/12399 +contract StructFieldAccess { + function a() external { + bytes32 guid = _lzSend({ + _dstEid: dstEid, + _message: message, + _options: OptionsBuilder.newOptions().addExecutorLzReceiveOption({_gas: gasLimit, _value: 0}), + _fee: MessagingFee({nativeFee: msg.value, lzTokenFee: 0}), + _refundAddress: msg.sender + }).guid; + } + + function b() external view returns (uint256) { + return _quote({ + _dstEid: dstEid, + _message: message, + _options: OptionsBuilder.newOptions().addExecutorLzReceiveOption({_gas: gasLimit, _value: 0}), + _payInLzToken: false + }).nativeFee; + } + + // Simple cases + function c() external { + uint256 val = getData().value; + bool flag = getStruct({param: 1}).isActive; + } + + // Nested struct field access + function d() external { + uint256 nested = getOuter().inner.value; + } + + // Chained calls with named args + function e() external { + bytes32 guid = _lzSend({ + _dstEid: dstEid, + _message: message, + _options: OptionsBuilder.newOptions().addExecutorLzReceiveOption({_gas: gasLimit, _value: 0}), + _fee: MessagingFee({nativeFee: msg.value, lzTokenFee: 0}), + _refundAddress: msg.sender + }).wrap({wrapper: wrapperAddress, extraData: bytes("")}).guid; + } +} diff --git a/crates/fmt/testdata/WhileStatement/block-multi.fmt.sol b/crates/fmt/testdata/WhileStatement/block-multi.fmt.sol index 1f3b3b7e8302e..5fddf215b8980 100644 --- a/crates/fmt/testdata/WhileStatement/block-multi.fmt.sol +++ b/crates/fmt/testdata/WhileStatement/block-multi.fmt.sol @@ -53,6 +53,11 @@ contract WhileStatement { doIt(); } + while (condition) { + doIt(); + doIt(); + } + while (condition) { doIt(); } diff --git a/crates/fmt/testdata/WhileStatement/block-single.fmt.sol b/crates/fmt/testdata/WhileStatement/block-single.fmt.sol index f4f695a398868..58f2c08463e3e 100644 --- a/crates/fmt/testdata/WhileStatement/block-single.fmt.sol +++ b/crates/fmt/testdata/WhileStatement/block-single.fmt.sol @@ -33,6 +33,11 @@ contract WhileStatement { while (condition) doIt(); + while (condition) { + doIt(); + doIt(); + } + while (condition) doIt(); while ( // comment1 diff --git a/crates/fmt/testdata/WhileStatement/fmt.sol b/crates/fmt/testdata/WhileStatement/fmt.sol index 57a882aee102e..7e366786f0816 100644 --- a/crates/fmt/testdata/WhileStatement/fmt.sol +++ b/crates/fmt/testdata/WhileStatement/fmt.sol @@ -40,6 +40,11 @@ contract WhileStatement { while (condition) doIt(); + while (condition) { + doIt(); + doIt(); + } + while (condition) doIt(); while ( // comment1 diff --git a/crates/fmt/testdata/WhileStatement/original.sol b/crates/fmt/testdata/WhileStatement/original.sol index 8b245b0cfa0f8..12fe15fb600a4 100644 --- a/crates/fmt/testdata/WhileStatement/original.sol +++ b/crates/fmt/testdata/WhileStatement/original.sol @@ -36,6 +36,8 @@ contract WhileStatement { while(condition) { doIt(); } + while(condition) { doIt(); doIt(); } + while (condition) doIt(); diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index 953dc6b48dbba..f337b5b3657fa 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -169,6 +169,7 @@ fmt_tests! { ArrayExpressions, BlockComments, BlockCommentsFunction, + CommentEmptyLine, ConditionalOperatorExpression, ConstructorDefinition, ConstructorModifierStyle, @@ -210,6 +211,7 @@ fmt_tests! { SortedImports, StatementBlock, StructDefinition, + StructFieldAccess, ThisExpression, #[ignore = "Solar errors when parsing inputs with trailing commas"] TrailingComma, @@ -223,3 +225,28 @@ fmt_tests! { Yul, YulStrings, } + +#[test] +fn test_comment_empty_line_bug() { + init_tracing(); + let source = r#"pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment + +} +"#; + + let expected = r#"pragma solidity ^0.8.0; + +contract ProofOfConcept { + // some comment +} +"#; + + let fmt_config = Arc::new(FormatterConfig::default()); + let path = Path::new("test.sol"); + let formatted = format(source, path, fmt_config); + + assert_eq!(formatted, expected, "Formatting mismatch"); +} diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 8169750642c6c..3be249dd24b1a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -35,6 +35,7 @@ foundry-linking.workspace = true comfy-table.workspace = true eyre.workspace = true proptest.workspace = true +rand.workspace = true rayon.workspace = true serde.workspace = true tracing.workspace = true @@ -98,6 +99,7 @@ opener = "0.8" # soldeer soldeer-commands.workspace = true +soldeer-core.workspace = true quick-junit = "0.5.2" [dev-dependencies] @@ -105,7 +107,6 @@ alloy-hardforks.workspace = true anvil.workspace = true forge-script-sequence.workspace = true foundry-test-utils.workspace = true -reqwest = { workspace = true, features = ["json"] } mockall = "0.14" globset = "0.4" diff --git a/crates/forge/assets/solidity/workflowTemplate.yml b/crates/forge/assets/solidity/workflowTemplate.yml index 03824c4abd03c..8aeae90e03e63 100644 --- a/crates/forge/assets/solidity/workflowTemplate.yml +++ b/crates/forge/assets/solidity/workflowTemplate.yml @@ -7,9 +7,6 @@ on: pull_request: workflow_dispatch: -env: - FOUNDRY_PROFILE: ci - jobs: check: name: Foundry project diff --git a/crates/forge/assets/tempo/workflowTemplate.yml b/crates/forge/assets/tempo/workflowTemplate.yml index e1e368ed6406a..f2294304b402a 100644 --- a/crates/forge/assets/tempo/workflowTemplate.yml +++ b/crates/forge/assets/tempo/workflowTemplate.yml @@ -7,9 +7,6 @@ on: pull_request: workflow_dispatch: -env: - FOUNDRY_PROFILE: ci - jobs: check: name: Tempo Foundry project diff --git a/crates/forge/assets/vyper/workflowTemplate.yml b/crates/forge/assets/vyper/workflowTemplate.yml index a189658705d85..7a5fc54a1b084 100644 --- a/crates/forge/assets/vyper/workflowTemplate.yml +++ b/crates/forge/assets/vyper/workflowTemplate.yml @@ -7,9 +7,6 @@ on: pull_request: workflow_dispatch: -env: - FOUNDRY_PROFILE: ci - jobs: check: name: Foundry project diff --git a/crates/forge/src/args.rs b/crates/forge/src/args.rs index aada974c91b9a..4f55f028a3690 100644 --- a/crates/forge/src/args.rs +++ b/crates/forge/src/args.rs @@ -13,6 +13,8 @@ use foundry_evm::inspectors::cheatcodes::{ForgeContext, set_execution_context}; pub fn run() -> Result<()> { setup()?; + foundry_cli::opts::GlobalArgs::check_markdown_help::(); + let args = Forge::parse(); args.global.init()?; diff --git a/crates/forge/src/cmd/bind_json.rs b/crates/forge/src/cmd/bind_json.rs index fec5133ac8718..f03fc64f467f7 100644 --- a/crates/forge/src/cmd/bind_json.rs +++ b/crates/forge/src/cmd/bind_json.rs @@ -285,7 +285,7 @@ impl BindJsonArgs { } } - for (s, fn_name) in structs_to_write.iter_mut().zip(fn_names.into_iter()) { + for (s, fn_name) in structs_to_write.iter_mut().zip(fn_names) { s.name_in_fns = fn_name.unwrap_or(s.name.clone()); } } diff --git a/crates/forge/src/cmd/build.rs b/crates/forge/src/cmd/build.rs index a98fdc8d7eeb0..51c257a52965e 100644 --- a/crates/forge/src/cmd/build.rs +++ b/crates/forge/src/cmd/build.rs @@ -4,7 +4,7 @@ use eyre::{Context, Result}; use forge_lint::{linter::Linter, sol::SolidityLinter}; use foundry_cli::{ opts::{BuildOpts, configure_pcx_from_solc, get_solar_sources_from_compile_output}, - utils::{LoadConfig, cache_local_signatures}, + utils::{Git, LoadConfig, cache_local_signatures}, }; use foundry_common::{compile::ProjectCompiler, shell}; use foundry_compilers::{ @@ -80,6 +80,9 @@ impl BuildArgs { config = self.load_config()?; } + self.check_soldeer_lock_consistency(&config).await; + self.check_foundry_lock_consistency(&config); + let project = config.project()?; // Collect sources to compile if build subdirectories specified. @@ -178,9 +181,7 @@ impl BuildArgs { get_solar_sources_from_compile_output(config, output, Some(&input_files), None)?; if solar_sources.input.sources.is_empty() { if !input_files.is_empty() { - sh_warn!( - "unable to lint. Solar only supports Solidity versions prior to 0.8.0" - )?; + sh_warn!("unable to lint. Solar only supports Solidity versions >=0.8.0")?; } return Ok(()); } @@ -228,6 +229,97 @@ impl BuildArgs { Ok([config.src, config.test, config.script, foundry_toml]) }) } + + /// Check soldeer.lock file consistency using soldeer_core APIs + async fn check_soldeer_lock_consistency(&self, config: &Config) { + let soldeer_lock_path = config.root.join("soldeer.lock"); + if !soldeer_lock_path.exists() { + return; + } + + // Note: read_lockfile returns Ok with empty entries for malformed files + let Ok(lockfile) = soldeer_core::lock::read_lockfile(&soldeer_lock_path) else { + return; + }; + + let deps_dir = config.root.join("dependencies"); + for entry in &lockfile.entries { + let dep_name = entry.name(); + + // Use soldeer_core's integrity check + match soldeer_core::install::check_dependency_integrity(entry, &deps_dir).await { + Ok(status) => { + use soldeer_core::install::DependencyStatus; + // Check if status indicates a problem + if matches!( + status, + DependencyStatus::Missing | DependencyStatus::FailedIntegrity + ) { + sh_warn!("Dependency '{}' integrity check failed: {:?}", dep_name, status) + .ok(); + } + } + Err(e) => { + sh_warn!("Dependency '{}' integrity check error: {}", dep_name, e).ok(); + } + } + } + } + + /// Check foundry.lock file consistency with git submodules + fn check_foundry_lock_consistency(&self, config: &Config) { + use crate::lockfile::{DepIdentifier, FOUNDRY_LOCK, Lockfile}; + + let foundry_lock_path = config.root.join(FOUNDRY_LOCK); + if !foundry_lock_path.exists() { + return; + } + + let git = Git::new(&config.root); + + let mut lockfile = Lockfile::new(&config.root).with_git(&git); + if let Err(e) = lockfile.read() { + if !e.to_string().contains("Lockfile not found") { + sh_warn!("Failed to parse foundry.lock: {}", e).ok(); + } + return; + } + + for (dep_path, dep_identifier) in lockfile.iter() { + let full_path = config.root.join(dep_path); + + if !full_path.exists() { + sh_warn!("Dependency '{}' not found at expected path", dep_path.display()).ok(); + continue; + } + + let actual_rev = match git.get_rev("HEAD", &full_path) { + Ok(rev) => rev, + Err(_) => { + sh_warn!("Failed to get git revision for dependency '{}'", dep_path.display()) + .ok(); + continue; + } + }; + + // Compare with the expected revision from lockfile + let expected_rev = match dep_identifier { + DepIdentifier::Branch { rev, .. } + | DepIdentifier::Tag { rev, .. } + | DepIdentifier::Rev { rev, .. } => rev.clone(), + }; + + if actual_rev != expected_rev { + sh_warn!( + "Dependency '{}' revision mismatch: expected '{}', found '{}'", + dep_path.display(), + expected_rev, + actual_rev + ) + .ok(); + } + } + } } // Make this args a `figment::Provider` so that it can be merged into the `Config` diff --git a/crates/forge/src/cmd/clone.rs b/crates/forge/src/cmd/clone.rs index 97337907ac201..c8f5680f16793 100644 --- a/crates/forge/src/cmd/clone.rs +++ b/crates/forge/src/cmd/clone.rs @@ -1090,7 +1090,8 @@ mod tests { /// 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 temp_dir = tempfile::tempdir().unwrap(); + let mut project_root = temp_dir.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()) @@ -1115,7 +1116,6 @@ mod tests { 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")] diff --git a/crates/forge/src/cmd/compiler.rs b/crates/forge/src/cmd/compiler.rs index a939d8823308b..39d38b8b249ba 100644 --- a/crates/forge/src/cmd/compiler.rs +++ b/crates/forge/src/cmd/compiler.rs @@ -72,8 +72,8 @@ impl ResolveArgs { .iter() .map(|(version, sources, _)| { let paths: Vec = sources - .iter() - .filter_map(|(path_file, _)| { + .keys() + .filter_map(|path_file| { let path_str = path_file .strip_prefix(&project.paths.root) .unwrap_or(path_file) diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index 9ab9174a630ba..2e3ac14f70de7 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -137,13 +137,14 @@ impl CoverageArgs { 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 production ready since Solidity 0.8.13 and above.\n\ - See more: https://github.com/foundry-rs/foundry/issues/3357" + See more: https://book.getfoundry.sh/guides/best-practices/stack-too-deep" )?; } else { sh_warn!( "optimizer settings and `viaIR` have been disabled for accurate coverage reports.\n\ If you encounter \"stack too deep\" errors, consider using `--ir-minimum` which \ - enables `viaIR` with minimum optimization resolving most of the errors" + enables `viaIR` with minimum optimization resolving most of the errors.\n\ + See more: https://book.getfoundry.sh/guides/best-practices/stack-too-deep" )?; } @@ -254,7 +255,7 @@ impl CoverageArgs { let known_contracts = outcome.runner.as_ref().unwrap().known_contracts.clone(); // Add hit data to the coverage report - let data = outcome.results.iter().flat_map(|(_, suite)| { + let data = outcome.results.values().flat_map(|suite| { let mut hits = Vec::new(); for result in suite.test_results.values() { let Some(hit_maps) = result.line_coverage.as_ref() else { continue }; diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 0187020721af1..625d93974f248 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -316,6 +316,15 @@ impl CreateArgs { deployer.tx.set_value(value); } + // set access list if specified + if let Some(access_list) = match self.tx.access_list { + None => None, + Some(None) => Some(provider.create_access_list(&deployer.tx).await?.access_list), + Some(Some(ref access_list)) => Some(access_list.clone()), + } { + deployer.tx.set_access_list(access_list); + } + deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { Ok(gas_limit.to()) } else { @@ -455,6 +464,14 @@ impl CreateArgs { constructor: &Constructor, constructor_args: &[String], ) -> Result> { + if constructor.inputs.len() != constructor_args.len() { + eyre::bail!( + "Constructor argument count mismatch: expected {} but got {}", + constructor.inputs.len(), + constructor_args.len() + ); + } + let mut params = Vec::with_capacity(constructor.inputs.len()); for (input, arg) in constructor.inputs.iter().zip(constructor_args) { // resolve the input type directly diff --git a/crates/forge/src/cmd/eip712.rs b/crates/forge/src/cmd/eip712.rs index 00a872a1df7a4..5d6b1bace69ca 100644 --- a/crates/forge/src/cmd/eip712.rs +++ b/crates/forge/src/cmd/eip712.rs @@ -84,7 +84,7 @@ impl Eip712Args { if compiler.sess().dcx.has_errors().is_err() { eyre::bail!("{diags}"); } else { - let _ = sh_print!("{diags}"); + let _ = sh_eprint!("{diags}"); } Ok(()) diff --git a/crates/forge/src/cmd/inspect.rs b/crates/forge/src/cmd/inspect.rs index de8a1a66548b4..9c1fb32775019 100644 --- a/crates/forge/src/cmd/inspect.rs +++ b/crates/forge/src/cmd/inspect.rs @@ -177,7 +177,7 @@ impl InspectArgs { fn parse_errors(abi: &JsonAbi) -> Map { let mut out = serde_json::Map::new(); - for er in abi.errors.iter().flat_map(|(_, errors)| errors) { + for er in abi.errors.values().flatten() { let types = get_ty_sig(&er.inputs); let sig = format!("{:x}", er.selector()); let sig_trimmed = &sig[0..8]; @@ -188,7 +188,7 @@ fn parse_errors(abi: &JsonAbi) -> Map { fn parse_events(abi: &JsonAbi) -> Map { let mut out = serde_json::Map::new(); - for ev in abi.events.iter().flat_map(|(_, events)| events) { + for ev in abi.events.values().flatten() { let types = parse_event_params(&ev.inputs); let topic = hex::encode(keccak256(ev.signature())); out.insert(format!("{}({})", ev.name, types), format!("0x{topic}").into()); @@ -219,14 +219,14 @@ fn print_abi(abi: &JsonAbi, should_wrap: bool) -> Result<()> { headers, |table| { // Print events - for ev in abi.events.iter().flat_map(|(_, events)| events) { + for ev in abi.events.values().flatten() { let types = parse_event_params(&ev.inputs); let selector = ev.selector().to_string(); table.add_row(["event", &format!("{}({})", ev.name, types), &selector]); } // Print errors - for er in abi.errors.iter().flat_map(|(_, errors)| errors) { + for er in abi.errors.values().flatten() { let selector = er.selector().to_string(); table.add_row([ "error", @@ -236,7 +236,7 @@ fn print_abi(abi: &JsonAbi, should_wrap: bool) -> Result<()> { } // Print functions - for func in abi.functions.iter().flat_map(|(_, f)| f) { + for func in abi.functions.values().flatten() { let selector = func.selector().to_string(); let state_mut = func.state_mutability.as_json_str(); let func_sig = if !func.outputs.is_empty() { diff --git a/crates/forge/src/cmd/lint.rs b/crates/forge/src/cmd/lint.rs index 1d5e5857ee594..5176903cbb50d 100644 --- a/crates/forge/src/cmd/lint.rs +++ b/crates/forge/src/cmd/lint.rs @@ -111,9 +111,7 @@ impl LintArgs { let solar_sources = get_solar_sources_from_compile_output(&config, &output, Some(&input), Some(&ignored))?; if solar_sources.input.sources.is_empty() { - return Err(eyre!( - "unable to lint. Solar only supports Solidity versions prior to 0.8.0" - )); + return Err(eyre!("unable to lint. Solar only supports Solidity versions >=0.8.0")); } // NOTE(rusowsky): Once solar can drop unsupported versions, rather than creating a new diff --git a/crates/forge/src/cmd/selectors.rs b/crates/forge/src/cmd/selectors.rs index dc2fe6de50ca4..78c80c19ec6da 100644 --- a/crates/forge/src/cmd/selectors.rs +++ b/crates/forge/src/cmd/selectors.rs @@ -416,12 +416,12 @@ impl SelectorsSubcommands { table.set_header(["Type", "Signature", "Selector", "Contract"]); + let selector_str = selector.strip_prefix("0x").unwrap_or(selector.as_str()); + let selector_bytes = hex::decode(selector_str)?; + for (_file, contract, artifact) in artifacts { let abi = artifact.abi.ok_or_else(|| eyre::eyre!("Unable to fetch abi"))?; - let selector_bytes = - hex::decode(selector.strip_prefix("0x").unwrap_or(&selector))?; - for func in abi.functions() { if func.selector().as_slice().starts_with(selector_bytes.as_slice()) { table.add_row([ diff --git a/crates/forge/src/cmd/snapshot.rs b/crates/forge/src/cmd/snapshot.rs index 4d6e4cae4b54b..1a37bd02aa4eb 100644 --- a/crates/forge/src/cmd/snapshot.rs +++ b/crates/forge/src/cmd/snapshot.rs @@ -270,6 +270,7 @@ impl FromStr for GasSnapshotEntry { reverts: reverts.as_str().parse().unwrap(), metrics: HashMap::default(), failed_corpus_replays: 0, + optimization_best_value: None, }, }) } @@ -625,6 +626,7 @@ mod tests { reverts: 200, metrics: HashMap::default(), failed_corpus_replays: 0, + optimization_best_value: None, } } ); @@ -645,6 +647,7 @@ mod tests { reverts: 2388, metrics: HashMap::default(), failed_corpus_replays: 0, + optimization_best_value: None, } } ); diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index a37a422e6688b..158d0f07e2938 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -43,6 +43,7 @@ use foundry_evm::{ opts::EvmOpts, traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth}, }; +use rand::Rng; use regex::Regex; use std::{ collections::{BTreeMap, BTreeSet}, @@ -304,6 +305,12 @@ impl TestArgs { config.invariant.gas_report_samples = 0; } + // Generate a random fuzz seed if none provided, for reproducibility. + config.fuzz.seed = config + .fuzz + .seed + .or_else(|| Some(U256::from_be_bytes(rand::rng().random::<[u8; 32]>()))); + // Create test options from general project settings and compiler output. let should_debug = self.debug; let should_draw = self.flamegraph || self.flamechart; @@ -361,7 +368,7 @@ impl TestArgs { // Decode traces. let decoder = outcome.last_run_decoder.as_ref().unwrap(); decode_trace_arena(arena, decoder).await; - let mut fst = folded_stack_trace::build(arena); + let mut fst = folded_stack_trace::build(arena, self.evm.isolate); let label = if self.flamegraph { "flamegraph" } else { "flamechart" }; let contract = suite_name.split(':').next_back().unwrap(); @@ -429,6 +436,7 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, output: &ProjectCompileOutput, ) -> eyre::Result { + let fuzz_seed = config.fuzz.seed; if self.list { return list(runner, filter); } @@ -504,13 +512,13 @@ impl TestArgs { } }); sh_println!("{}", serde_json::to_string(&results)?)?; - return Ok(TestOutcome::new(Some(runner), results, self.allow_failure)); + return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed)); } if self.junit { let results = runner.test_collect(filter)?; sh_println!("{}", junit_xml_report(&results, verbosity).to_string()?)?; - return Ok(TestOutcome::new(Some(runner), results, self.allow_failure)); + return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed)); } let remote_chain = @@ -532,8 +540,9 @@ impl TestArgs { let mut identifier = TraceIdentifiers::new().with_local(&known_contracts); // Avoid using external identifiers for gas report as we decode more traces and this will be - // expensive. - if !self.gas_report { + // expensive. Also skip external identifiers for local tests (no remote chain) to avoid + // unnecessary Etherscan API calls that significantly slow down test execution. + if !self.gas_report && remote_chain.is_some() { identifier = identifier.with_external(&config, remote_chain)?; } @@ -566,6 +575,7 @@ impl TestArgs { let mut gas_snapshots = BTreeMap::>::new(); let mut outcome = TestOutcome::empty(None, self.allow_failure); + outcome.fuzz_seed = fuzz_seed; let mut any_test_failed = false; let mut backtrace_builder = None; @@ -745,7 +755,7 @@ impl TestArgs { |mut found, (group, snapshots)| { // If the snapshot file doesn't exist, we can't compare so we skip. if !&config.snapshots.join(format!("{group}.json")).exists() { - return false; + return found; } let previous_snapshots: BTreeMap = diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index bb9e749cbcff2..040f51d587bf3 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -135,6 +135,19 @@ impl CoverageReporter for LcovReporter { writeln!(out, "TN:")?; writeln!(out, "SF:{}", path.display())?; + // First pass: collect line hits for DA records. + // Track both which lines have been recorded and the max hits per line. + let mut line_hits: HashMap = HashMap::default(); + for item in &items { + if matches!(item.kind, CoverageItemKind::Line | CoverageItemKind::Statement) { + let line = item.loc.lines.start; + line_hits + .entry(line) + .and_modify(|h| *h = (*h).max(item.hits)) + .or_insert(item.hits); + } + } + let mut recorded_lines = HashSet::new(); for item in items { @@ -160,15 +173,26 @@ impl CoverageReporter for LcovReporter { } } // Add lines / statement hits only once. - CoverageItemKind::Line | CoverageItemKind::Statement => { - if recorded_lines.insert(line) { - writeln!(out, "DA:{line},{hits}")?; - } + CoverageItemKind::Line | CoverageItemKind::Statement + if recorded_lines.insert(line) => + { + writeln!(out, "DA:{line},{hits}")?; } CoverageItemKind::Branch { branch_id, path_id, .. } => { - let hits_str = if hits == 0 { "-" } else { &hits.to_string() }; + // Per LCOV spec: "-" means the expression was never evaluated (line not + // executed), "0" means branch exists but was never taken. + // Check if the line containing this branch was hit. + let line_was_hit = line_hits.get(&line).is_some_and(|&h| h > 0); + let hits_str = if hits > 0 { + hits.to_string() + } else if line_was_hit { + "0".to_string() + } else { + "-".to_string() + }; writeln!(out, "BRDA:{line},{branch_id},{path_id},{hits_str}")?; } + _ => {} } } diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 985febef2b964..95b16c93a146c 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -5,7 +5,9 @@ use crate::{ traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData}, }; use alloy_primitives::map::HashSet; -use comfy_table::{Cell, Color, Table, modifiers::UTF8_ROUND_CORNERS, presets::ASCII_MARKDOWN}; +use comfy_table::{ + Cell, CellAlignment, Color, Table, modifiers::UTF8_ROUND_CORNERS, presets::ASCII_MARKDOWN, +}; use foundry_common::{TestFunctionExt, calc, shell}; use foundry_evm::traces::CallKind; @@ -175,8 +177,8 @@ impl GasReport { let functions = contract .functions - .iter() - .flat_map(|(_, sigs)| { + .values() + .flat_map(|sigs| { sigs.iter().map(|(sig, gas_info)| { let display_name = sig.replace(':', ""); (display_name, gas_info) @@ -213,8 +215,8 @@ impl GasReport { Cell::new("Deployment Size").fg(Color::Cyan), ]); table.add_row(vec![ - Cell::new(contract.gas.to_string()), - Cell::new(contract.size.to_string()), + Cell::new(contract.gas.to_string()).set_alignment(CellAlignment::Right), + Cell::new(contract.size.to_string()).set_alignment(CellAlignment::Right), ]); // Add a blank row to separate deployment info from function info. @@ -237,11 +239,19 @@ impl GasReport { table.add_row(vec![ Cell::new(display_name), - Cell::new(gas_info.min.to_string()).fg(Color::Green), - Cell::new(gas_info.mean.to_string()).fg(Color::Yellow), - Cell::new(gas_info.median.to_string()).fg(Color::Yellow), - Cell::new(gas_info.max.to_string()).fg(Color::Red), - Cell::new(gas_info.calls.to_string()), + Cell::new(gas_info.min.to_string()) + .fg(Color::Green) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.mean.to_string()) + .fg(Color::Yellow) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.median.to_string()) + .fg(Color::Yellow) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.max.to_string()) + .fg(Color::Red) + .set_alignment(CellAlignment::Right), + Cell::new(gas_info.calls.to_string()).set_alignment(CellAlignment::Right), ]); }) }); diff --git a/crates/forge/src/lockfile.rs b/crates/forge/src/lockfile.rs index bbcbc5dedc4e8..68330db657317 100644 --- a/crates/forge/src/lockfile.rs +++ b/crates/forge/src/lockfile.rs @@ -55,11 +55,10 @@ impl<'a> Lockfile<'a> { pub fn sync(&mut self, lib: &Path) -> Result> { match self.read() { Ok(_) => {} - Err(e) => { - if !e.to_string().contains("Lockfile not found") { - return Err(e); - } + Err(e) if !e.to_string().contains("Lockfile not found") => { + return Err(e); } + _ => {} } if let Some(git) = &self.git { @@ -81,10 +80,8 @@ impl<'a> Lockfile<'a> { let entry = self.deps.entry(rel_path.to_path_buf()); match entry { - Entry::Occupied(e) => { - if e.get().rev() != rev { - out_of_sync.insert(rel_path.to_path_buf(), e.get().clone()); - } + Entry::Occupied(e) if e.get().rev() != rev => { + out_of_sync.insert(rel_path.to_path_buf(), e.get().clone()); } Entry::Vacant(e) => { // Check if there is branch specified for the submodule at rel_path in @@ -108,6 +105,7 @@ impl<'a> Lockfile<'a> { e.insert(dep_id.clone()); out_of_sync.insert(rel_path.to_path_buf(), dep_id); } + _ => {} } } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index f7e36dc323427..988191b7d7c0f 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -377,6 +377,7 @@ impl TestRunnerConfig { ExecutorBuilder::new() .inspectors(|stack| { stack + .logs(self.config.live_logs) .cheatcodes(cheats_config) .trace_mode(self.trace_mode()) .line_coverage(self.line_coverage) @@ -514,8 +515,8 @@ impl MultiContractRunnerBuilder { // Build revert decoder from ABIs of all artifacts. let abis = linker .contracts - .iter() - .filter_map(|(_, contract)| contract.abi.as_ref().map(|abi| abi.borrow())); + .values() + .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( diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 7a0f42846f4c1..693bea6f1eeb7 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -6,7 +6,7 @@ use crate::{ gas_report::GasReport, }; use alloy_primitives::{ - Address, Log, + Address, I256, Log, U256, map::{AddressHashMap, HashMap}, }; use eyre::Report; @@ -46,6 +46,8 @@ pub struct TestOutcome { pub gas_report: Option, /// The runner used to execute the tests. pub runner: Option, + /// The fuzz seed used for the test run. + pub fuzz_seed: Option, } impl TestOutcome { @@ -54,13 +56,14 @@ impl TestOutcome { runner: Option, results: BTreeMap, allow_failure: bool, + fuzz_seed: Option, ) -> Self { - Self { results, allow_failure, last_run_decoder: None, gas_report: None, runner } + Self { results, allow_failure, last_run_decoder: None, gas_report: None, runner, fuzz_seed } } /// Creates a new empty test outcome. pub fn empty(runner: Option, allow_failure: bool) -> Self { - Self::new(runner, BTreeMap::new(), allow_failure) + Self::new(runner, BTreeMap::new(), allow_failure, None) } /// Returns an iterator over all individual succeeding tests and their names. @@ -130,6 +133,11 @@ impl TestOutcome { self.failures().count() } + /// Returns `true` if any fuzz or invariant test failed. + pub fn has_fuzz_failures(&self) -> bool { + self.failures().any(|(_, t)| t.kind.is_fuzz() || t.kind.is_invariant()) + } + /// Sums up all the durations of all individual test suites. /// /// Note that this is not necessarily the wall clock time of the entire test run. @@ -200,6 +208,17 @@ impl TestOutcome { test_word )?; + // Print seed for fuzz/invariant test failures to enable reproduction. + if let Some(seed) = self.fuzz_seed + && outcome.has_fuzz_failures() + { + sh_println!( + "\nFuzz seed: {} (use {} to reproduce)", + format!("{seed:#x}").cyan(), + "`--fuzz-seed`".cyan() + )?; + } + std::process::exit(1); } @@ -444,7 +463,25 @@ pub struct TestResult { impl fmt::Display for TestResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.status { - TestStatus::Success => "[PASS]".green().fmt(f), + TestStatus::Success => { + // For optimization mode, show the best example sequence in green. + if let Some(CounterExample::Sequence(original, sequence)) = &self.counterexample { + let mut s = String::from("[PASS]"); + s.push_str( + format!( + "\n\t[Best sequence] (original: {original}, shrunk: {})\n", + sequence.len() + ) + .as_str(), + ); + for ex in sequence { + writeln!(s, "{ex}").unwrap(); + } + s.green().wrap().fmt(f) + } else { + "[PASS]".green().fmt(f) + } + } TestStatus::Skipped => { let mut s = String::from("[SKIP"); if let Some(reason) = &self.reason { @@ -561,8 +598,9 @@ impl TestResult { reason: Option, raw_call_result: RawCallResult, ) { - self.kind = - TestKind::Unit { gas: raw_call_result.gas_used.wrapping_sub(raw_call_result.stipend) }; + self.kind = TestKind::Unit { + gas: raw_call_result.gas_used.saturating_sub(raw_call_result.stipend), + }; extend!(self, raw_call_result, TraceKind::Execution); @@ -620,6 +658,7 @@ impl TestResult { failed_corpus_replays: 0, }; self.status = TestStatus::Failure; + debug!(?e, "failed to set up fuzz testing environment"); self.reason = Some(format!("failed to set up fuzz testing environment: {e}")); } @@ -631,6 +670,7 @@ impl TestResult { reverts: 1, metrics: HashMap::default(), failed_corpus_replays: 0, + optimization_best_value: None, }; self.status = TestStatus::Skipped; self.reason = reason.0; @@ -649,6 +689,7 @@ impl TestResult { reverts: 1, metrics: HashMap::default(), failed_corpus_replays: 0, + optimization_best_value: None, }; self.status = TestStatus::Failure; self.reason = if replayed_entirely { @@ -667,6 +708,7 @@ impl TestResult { reverts: 0, metrics: HashMap::default(), failed_corpus_replays: 0, + optimization_best_value: None, }; self.status = TestStatus::Failure; self.reason = Some(format!("failed to set up invariant testing environment: {e}")); @@ -684,6 +726,7 @@ impl TestResult { reverts: usize, metrics: Map, failed_corpus_replays: usize, + optimization_best_value: Option, ) { self.kind = TestKind::Invariant { runs: cases.len(), @@ -691,10 +734,13 @@ impl TestResult { reverts, metrics, failed_corpus_replays, + optimization_best_value, }; - self.status = match success { - true => TestStatus::Success, - false => TestStatus::Failure, + // For optimization mode (Some value), always succeed. For check mode (None), use success. + self.status = if optimization_best_value.is_some() || success { + TestStatus::Success + } else { + TestStatus::Failure }; self.reason = reason; self.counterexample = counterexample; @@ -767,6 +813,8 @@ pub enum TestKindReport { reverts: usize, metrics: Map, failed_corpus_replays: usize, + /// For optimization mode (int256 return): the best value achieved. None = check mode. + optimization_best_value: Option, }, Table { runs: usize, @@ -791,8 +839,18 @@ impl fmt::Display for TestKindReport { write!(f, "(runs: {runs}, μ: {mean_gas}, ~: {median_gas})") } } - Self::Invariant { runs, calls, reverts, metrics: _, failed_corpus_replays } => { - if *failed_corpus_replays != 0 { + Self::Invariant { + runs, + calls, + reverts, + metrics: _, + failed_corpus_replays, + optimization_best_value, + } => { + // If optimization_best_value is Some, this is optimization mode. + if let Some(best_value) = optimization_best_value { + write!(f, "(best: {best_value}, runs: {runs}, calls: {calls})") + } else if *failed_corpus_replays != 0 { write!( f, "(runs: {runs}, calls: {calls}, reverts: {reverts}, failed corpus replays: {failed_corpus_replays})" @@ -842,6 +900,8 @@ pub enum TestKind { reverts: usize, metrics: Map, failed_corpus_replays: usize, + /// For optimization mode (int256 return): the best value achieved. None = check mode. + optimization_best_value: Option, }, /// A table test. Table { runs: usize, mean_gas: u64, median_gas: u64 }, @@ -854,6 +914,16 @@ impl Default for TestKind { } impl TestKind { + /// Returns `true` if this is a fuzz test. + pub fn is_fuzz(&self) -> bool { + matches!(self, Self::Fuzz { .. }) + } + + /// Returns `true` if this is an invariant test. + pub fn is_invariant(&self) -> bool { + matches!(self, Self::Invariant { .. }) + } + /// The gas consumed by this test pub fn report(&self) -> TestKindReport { match self { @@ -866,15 +936,21 @@ impl TestKind { failed_corpus_replays: *failed_corpus_replays, } } - Self::Invariant { runs, calls, reverts, metrics: _, failed_corpus_replays } => { - TestKindReport::Invariant { - runs: *runs, - calls: *calls, - reverts: *reverts, - metrics: HashMap::default(), - failed_corpus_replays: *failed_corpus_replays, - } - } + Self::Invariant { + runs, + calls, + reverts, + metrics: _, + failed_corpus_replays, + optimization_best_value, + } => TestKindReport::Invariant { + runs: *runs, + calls: *calls, + reverts: *reverts, + metrics: HashMap::default(), + failed_corpus_replays: *failed_corpus_replays, + optimization_best_value: *optimization_best_value, + }, Self::Table { runs, mean_gas, median_gas } => { TestKindReport::Table { runs: *runs, mean_gas: *mean_gas, median_gas: *median_gas } } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index abf4805cf1343..8fb9a29c7512a 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -754,12 +754,8 @@ impl<'a> FunctionRunner<'a> { identified_contracts, &self.cr.mcr.known_contracts, ); - let invariant_contract = InvariantContract { - address: self.address, - invariant_function: func, - call_after_invariant, - abi: &self.cr.contract.abi, - }; + let invariant_contract = + InvariantContract::new(self.address, func, call_after_invariant, &self.cr.contract.abi); let show_solidity = invariant_config.show_solidity; let progress = start_fuzz_progress( @@ -818,6 +814,7 @@ impl<'a> FunctionRunner<'a> { self.clone_executor(), &txes, None, + None, // check mode &invariant_contract, &self.cr.mcr.known_contracts, identified_contracts.clone(), @@ -828,21 +825,20 @@ impl<'a> FunctionRunner<'a> { progress.as_ref(), &self.tcfg.early_exit, ) { - Ok(replayed_call_sequence) => { - if !replayed_call_sequence.is_empty() { - call_sequence = replayed_call_sequence; - // Persist error in invariant failure dir. - record_invariant_failure( - failure_dir.as_path(), - failure_file.as_path(), - &call_sequence, - test_bytecode, - ); - } + Ok(replayed_call_sequence) if !replayed_call_sequence.is_empty() => { + call_sequence = replayed_call_sequence; + // Persist error in invariant failure dir. + record_invariant_failure( + failure_dir.as_path(), + failure_file.as_path(), + &call_sequence, + test_bytecode, + ); } Err(err) => { error!(%err, "Failed to replay invariant error"); } + _ => {} } self.result.invariant_replay_fail( @@ -889,6 +885,7 @@ impl<'a> FunctionRunner<'a> { self.clone_executor(), calls, Some(case_data.inner_sequence), + None, // check mode &invariant_contract, &self.cr.mcr.known_contracts, identified_contracts.clone(), @@ -899,33 +896,31 @@ impl<'a> FunctionRunner<'a> { progress.as_ref(), &self.tcfg.early_exit, ) { - Ok(call_sequence) => { - if !call_sequence.is_empty() { - // Persist error in invariant failure dir. - record_invariant_failure( - failure_dir.as_path(), - failure_file.as_path(), - &call_sequence, - test_bytecode, - ); - - let original_seq_len = if let TestError::Fail(_, calls) = - &case_data.test_error - { + Ok(call_sequence) if !call_sequence.is_empty() => { + // Persist error in invariant failure dir. + record_invariant_failure( + failure_dir.as_path(), + failure_file.as_path(), + &call_sequence, + test_bytecode, + ); + + let original_seq_len = + if let TestError::Fail(_, calls) = &case_data.test_error { calls.len() } else { call_sequence.len() }; - counterexample = Some(CounterExample::Sequence( - original_seq_len, - call_sequence, - )) - } + counterexample = Some(CounterExample::Sequence( + original_seq_len, + call_sequence, + )) } Err(err) => { error!(%err, "Failed to replay invariant error"); } + _ => {} } } }; @@ -933,22 +928,53 @@ impl<'a> FunctionRunner<'a> { InvariantFuzzError::MaxAssumeRejects(_) => {} }, - // If invariants ran successfully, replay the last run to collect logs and - // traces. + // If invariants ran successfully, replay the last run to collect logs and traces. _ => { - if let Err(err) = replay_run( - &invariant_contract, - self.clone_executor(), - &self.cr.mcr.known_contracts, - identified_contracts.clone(), - &mut self.result.logs, - &mut self.result.traces, - &mut self.result.line_coverage, - &mut self.result.deprecated_cheatcodes, - &invariant_result.last_run_inputs, - show_solidity, - ) { - error!(%err, "Failed to replay last invariant run"); + if let Some(best_value) = invariant_result.optimization_best_value { + // Optimization mode: replay and shrink to find shortest best sequence. + match replay_error( + evm.config(), + self.clone_executor(), + &invariant_result.optimization_best_sequence, + None, + Some(best_value), + &invariant_contract, + &self.cr.mcr.known_contracts, + identified_contracts.clone(), + &mut self.result.logs, + &mut self.result.traces, + &mut self.result.line_coverage, + &mut self.result.deprecated_cheatcodes, + progress.as_ref(), + &self.tcfg.early_exit, + ) { + Ok(best_sequence) if !best_sequence.is_empty() => { + counterexample = Some(CounterExample::Sequence( + invariant_result.optimization_best_sequence.len(), + best_sequence, + )); + } + Err(err) => { + error!(%err, "Failed to replay optimization best sequence"); + } + _ => {} + } + } else { + // Standard check mode: replay last run for traces. + if let Err(err) = replay_run( + &invariant_contract, + self.clone_executor(), + &self.cr.mcr.known_contracts, + identified_contracts.clone(), + &mut self.result.logs, + &mut self.result.traces, + &mut self.result.line_coverage, + &mut self.result.deprecated_cheatcodes, + &invariant_result.last_run_inputs, + show_solidity, + ) { + error!(%err, "Failed to replay last invariant run"); + } } } } @@ -962,6 +988,7 @@ impl<'a> FunctionRunner<'a> { invariant_result.reverts, invariant_result.metrics, invariant_result.failed_corpus_replays, + invariant_result.optimization_best_value, ); self.result } @@ -1017,6 +1044,7 @@ impl<'a> FunctionRunner<'a> { &self.cr.mcr.revert_decoder, progress.as_ref(), &self.tcfg.early_exit, + self.cr.tokio_handle, ) { Ok(x) => x, Err(e) => { diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 979521c33e489..01be19cb6298f 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,6 +1,7 @@ use crate::utils::generate_large_init_contract; -use foundry_test_utils::{forgetest, snapbox::IntoData, str}; +use foundry_test_utils::{forgetest, forgetest_init, snapbox::IntoData, str}; use globset::Glob; +use std::fs; forgetest_init!(can_parse_build_filters, |prj, cmd| { prj.initialize_default_contracts(); @@ -439,22 +440,74 @@ contract ContractB { cmd.args(["build", "src/ContractWithInvalidNatspec.sol"]).assert_success().stderr_eq(str![[ r#" warning: invalid natspec tag '@deprecated', custom tags must use format '@custom:name' - [FILE]:5:5 - | -5 | /// @deprecated quoteExactOutputSingle and exactOutput. Use QuoterV2 instead. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | + [FILE]:5:5 + │ +5 │ /// @deprecated quoteExactOutputSingle and exactOutput. Use QuoterV2 instead. + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ ... warning: invalid natspec tag '@note', custom tags must use format '@custom:name' - [FILE]:9:1 - | -9 | /// @note foo bar - | ^^^^^^^^^^^^^^^^^ - | + [FILE]:9:1 + │ +9 │ /// @note foo bar + │ ━━━━━━━━━━━━━━━━━ + │ ... "# ]]); }); + +// tests that build succeeds without warning when no soldeer.lock exists +forgetest_init!(build_no_warning_without_soldeer_lock, |prj, cmd| { + let soldeer_lock = prj.root().join("soldeer.lock"); + // soldeer.lock should not exist in a fresh project + assert!(!soldeer_lock.exists()); + + cmd.args(["build"]).assert_success().stderr_eq(str![[r#" +"#]]); +}); + +// tests that malformed foundry.lock triggers a warning during build +forgetest_init!(build_warns_on_malformed_foundry_lock, |prj, cmd| { + let foundry_lock = prj.root().join("foundry.lock"); + fs::write(&foundry_lock, "this is not valid toml { [ }").unwrap(); + + cmd.args(["build"]).assert_success().stderr_eq(str![[r#" +Warning: Failed to parse foundry.lock: [..] +... +"#]]); +}); + +// tests that build succeeds without warning when no foundry.lock exists +forgetest_init!(build_no_warning_without_foundry_lock, |prj, cmd| { + let foundry_lock = prj.root().join("foundry.lock"); + // Remove foundry.lock if it exists from template + let _ = fs::remove_file(&foundry_lock); + + cmd.args(["build"]).assert_success().stderr_eq(str![[r#" +"#]]); +}); + +// tests that build warns when foundry.lock revision differs from actual submodule revision +forgetest_init!(build_warns_on_foundry_lock_revision_mismatch, |prj, cmd| { + let foundry_lock = prj.root().join("foundry.lock"); + + // Write a foundry.lock with a fake/old revision for forge-std that differs from the actual + let lockfile_content = r#"{ + "lib/forge-std": { + "tag": { + "name": "v1.9.7", + "rev": "0000000000000000000000000000000000000000" + } + } +}"#; + fs::write(&foundry_lock, lockfile_content).unwrap(); + + cmd.args(["build"]).assert_success().stderr_eq(str![[r#" +Warning: Dependency 'lib/forge-std' revision mismatch: expected '0000000000000000000000000000000000000000', found '[..]' + +"#]]); +}); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index e0b2c43c499ac..30f0f43001b60 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -696,7 +696,7 @@ Initializing [..] from https://github.com/foundry-rs/forge-template... }); // checks that clone works -forgetest!(can_clone, |prj, cmd| { +forgetest!(flaky_can_clone, |prj, cmd| { prj.wipe(); let foundry_toml = prj.root().join(Config::FILE_NAME); @@ -728,7 +728,7 @@ Compiler run successful! }); // Checks that quiet mode does not print anything for clone -forgetest!(can_clone_quiet, |prj, cmd| { +forgetest!(flaky_can_clone_quiet, |prj, cmd| { prj.wipe(); cmd.args([ @@ -743,7 +743,7 @@ forgetest!(can_clone_quiet, |prj, cmd| { }); // checks that clone works with sourcify -forgetest!(can_clone_sourcify, |prj, cmd| { +forgetest!(flaky_can_clone_sourcify, |prj, cmd| { prj.wipe(); let foundry_toml = prj.root().join(Config::FILE_NAME); @@ -770,7 +770,7 @@ Compiler run successful! }); // checks that clone works with --no-remappings-txt -forgetest!(can_clone_no_remappings_txt, |prj, cmd| { +forgetest!(flaky_can_clone_no_remappings_txt, |prj, cmd| { prj.wipe(); let foundry_toml = prj.root().join(Config::FILE_NAME); @@ -803,7 +803,7 @@ Compiler run successful! }); // checks that clone works with --keep-directory-structure -forgetest!(can_clone_keep_directory_structure, |prj, cmd| { +forgetest!(flaky_can_clone_keep_directory_structure, |prj, cmd| { prj.wipe(); let foundry_toml = prj.root().join(Config::FILE_NAME); @@ -935,7 +935,7 @@ Installing tempo-std in [..] (url: https://github.com/tempoxyz/tempo-std, tag: N // checks that clone works with raw src containing `node_modules` // -forgetest!(can_clone_with_node_modules, |prj, cmd| { +forgetest!(flaky_can_clone_with_node_modules, |prj, cmd| { prj.wipe(); let foundry_toml = prj.root().join(Config::FILE_NAME); @@ -1630,13 +1630,13 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1644,13 +1644,13 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1658,13 +1658,13 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -1735,13 +1735,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1749,13 +1749,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1763,13 +1763,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -1840,13 +1840,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1854,13 +1854,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1868,13 +1868,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -1948,13 +1948,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -1962,13 +1962,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -1976,13 +1976,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2060,13 +2060,13 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2107,13 +2107,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2154,13 +2154,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -2209,13 +2209,13 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -2223,13 +2223,13 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2290,13 +2290,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -2304,13 +2304,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -2379,13 +2379,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| foo | 45656 | 45656 | 45656 | 45656 | 1 | +| foo | 45656 | 45656 | 45656 | 45656 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -2393,13 +2393,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133243 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+--------+--------+--------+---------| -| baz | 287711 | 287711 | 287711 | 287711 | 1 | +| baz | 287711 | 287711 | 287711 | 287711 | 1 | ╰------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭----------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -2407,13 +2407,13 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133027 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+-------+--------+-------+---------| -| bar | 67683 | 67683 | 67683 | 67683 | 1 | +| bar | 67683 | 67683 | 67683 | 67683 | 1 | ╰----------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2542,19 +2542,19 @@ contract CounterTest is DSTest { +=======================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| -| 172107 | 578 | | | | | +| 172107 | 578 | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------+-----------------+-------+--------+-------+---------| -| a | 2402 | 2402 | 2402 | 2402 | 1 | +| a | 2402 | 2402 | 2402 | 2402 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| b | 2447 | 2447 | 2447 | 2447 | 1 | +| b | 2447 | 2447 | 2447 | 2447 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| setNumber(int256) | 23851 | 33807 | 33807 | 43763 | 2 | +| setNumber(int256) | 23851 | 33807 | 33807 | 43763 | 2 | |----------------------------------+-----------------+-------+--------+-------+---------| -| setNumber(uint256) | 23806 | 33762 | 33762 | 43718 | 2 | +| setNumber(uint256) | 23806 | 33762 | 33762 | 43718 | 2 | ╰----------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2669,15 +2669,15 @@ contract GasReportFallbackTest is Test { +========================================================================================================+ | Deployment Cost | Deployment Size | | | | | |---------------------------------------------------+-----------------+-------+--------+-------+---------| -| 117171 | 471 | | | | | +| 117171 | 471 | | | | | |---------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |---------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |---------------------------------------------------+-----------------+-------+--------+-------+---------| -| deposit | 21185 | 21185 | 21185 | 21185 | 1 | +| deposit | 21185 | 21185 | 21185 | 21185 | 1 | |---------------------------------------------------+-----------------+-------+--------+-------+---------| -| fallback | 29758 | 29758 | 29758 | 29758 | 1 | +| fallback | 29758 | 29758 | 29758 | 29758 | 1 | ╰---------------------------------------------------+-----------------+-------+--------+-------+---------╯ ╭-----------------------------------------------------+-----------------+------+--------+------+---------╮ @@ -2685,13 +2685,13 @@ contract GasReportFallbackTest is Test { +========================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-----------------------------------------------------+-----------------+------+--------+------+---------| -| 153531 | 494 | | | | | +| 153531 | 494 | | | | | |-----------------------------------------------------+-----------------+------+--------+------+---------| | | | | | | | |-----------------------------------------------------+-----------------+------+--------+------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------------------+-----------------+------+--------+------+---------| -| deposit | 3661 | 3661 | 3661 | 3661 | 1 | +| deposit | 3661 | 3661 | 3661 | 3661 | 1 | ╰-----------------------------------------------------+-----------------+------+--------+------+---------╯ @@ -2751,7 +2751,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) }); // -forgetest_init!(gas_report_fallback_with_calldata, |prj, cmd| { +forgetest_init!(flaky_gas_report_fallback_with_calldata, |prj, cmd| { prj.initialize_default_contracts(); prj.add_test( "FallbackWithCalldataTest.sol", @@ -2807,13 +2807,13 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] +=====================================================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| 132459 | 396 | | | | | +| 132471 | 396 | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| fallback | 43461 | 43461 | 43461 | 43461 | 1 | +| fallback | 43461 | 43461 | 43461 | 43461 | 1 | ╰----------------------------------------------------------------+-----------------+-------+--------+-------+---------╯ @@ -2830,7 +2830,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "test/FallbackWithCalldataTest.sol:CounterWithFallback", "deployment": { - "gas": 132459, + "gas": 132471, "size": 396 }, "functions": { @@ -2892,13 +2892,13 @@ contract NestedDeploy is Test { +======================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-------------------------------------------------+-----------------+-------+--------+-------+---------| -| 0 | 132 | | | | | +| 0 | 132 | | | | | |-------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |-------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-------------------------------------------------+-----------------+-------+--------+-------+---------| -| w | 21185 | 21185 | 21185 | 21185 | 1 | +| w | 21185 | 21185 | 21185 | 21185 | 1 | ╰-------------------------------------------------+-----------------+-------+--------+-------+---------╯ ╭------------------------------------------+-----------------+------+--------+------+---------╮ @@ -2906,13 +2906,13 @@ contract NestedDeploy is Test { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+------+--------+------+---------| -| 0 | 731 | | | | | +| 0 | 731 | | | | | |------------------------------------------+-----------------+------+--------+------+---------| | | | | | | | |------------------------------------------+-----------------+------+--------+------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------+-----------------+------+--------+------+---------| -| child | 2681 | 2681 | 2681 | 2681 | 1 | +| child | 2681 | 2681 | 2681 | 2681 | 1 | ╰------------------------------------------+-----------------+------+--------+------+---------╯ ╭-------------------------------------------+-----------------+-----+--------+-----+---------╮ @@ -2920,13 +2920,13 @@ contract NestedDeploy is Test { +============================================================================================+ | Deployment Cost | Deployment Size | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| -| 328961 | 1163 | | | | | +| 328961 | 1163 | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| | | | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-------------------------------------------+-----------------+-----+--------+-----+---------| -| child | 525 | 525 | 525 | 525 | 1 | +| child | 525 | 525 | 525 | 525 | 1 | ╰-------------------------------------------+-----------------+-----+--------+-----+---------╯ @@ -3670,7 +3670,7 @@ forgetest_init!(can_inspect_standard_json, |prj, cmd| { ] } }, - "evmVersion": "prague", + "evmVersion": "osaka", "viaIR": false, "libraries": {} } @@ -3732,17 +3732,17 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { +=======================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| -| 156813 | 509 | | | | | +| 156813 | 509 | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------+-----------------+-------+--------+-------+---------| -| increment | 43482 | 43482 | 43482 | 43482 | 1 | +| increment | 43482 | 43482 | 43482 | 43482 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| number | 2424 | 2424 | 2424 | 2424 | 1 | +| number | 2424 | 2424 | 2424 | 2424 | 1 | |----------------------------------+-----------------+-------+--------+-------+---------| -| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | +| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | ╰----------------------------------+-----------------+-------+--------+-------+---------╯ ╭-----------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -3750,15 +3750,15 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { +================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| 1544498 | 7573 | | | | | +| 1544498 | 7573 | | | | | |-----------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |-----------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| setUp | 218902 | 218902 | 218902 | 218902 | 1 | +| setUp | 218902 | 218902 | 218902 | 218902 | 1 | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | +| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | ╰-----------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -3774,21 +3774,21 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) | src/Counter.sol:Counter Contract | | | | | | |----------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 156813 | 509 | | | | | +| 156813 | 509 | | | | | | | | | | | | | Function Name | Min | Avg | Median | Max | # Calls | -| increment | 43482 | 43482 | 43482 | 43482 | 1 | -| number | 2424 | 2424 | 2424 | 2424 | 1 | -| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | +| increment | 43482 | 43482 | 43482 | 43482 | 1 | +| number | 2424 | 2424 | 2424 | 2424 | 1 | +| setNumber | 23784 | 23784 | 23784 | 23784 | 1 | | test/Counter.t.sol:CounterTest Contract | | | | | | |-----------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 1544498 | 7573 | | | | | +| 1544498 | 7573 | | | | | | | | | | | | | Function Name | Min | Avg | Median | Max | # Calls | -| setUp | 218902 | 218902 | 218902 | 218902 | 1 | -| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | +| setUp | 218902 | 218902 | 218902 | 218902 | 1 | +| test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index 7c549e1ce3490..eec4873b0ae32 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -18,14 +18,14 @@ contract ContractB {} const CONTRACT_C: &str = r#" // SPDX-license-identifier: MIT -pragma solidity 0.8.30; +pragma solidity 0.8.33; contract ContractC {} "#; const CONTRACT_D: &str = r#" // SPDX-license-identifier: MIT -pragma solidity 0.8.30; +pragma solidity 0.8.33; contract ContractD {} "#; @@ -111,7 +111,7 @@ forgetest!(can_list_resolved_compiler_versions_verbose, |prj, cmd| { cmd.args(["compiler", "resolve", "-v"]).assert_success().stdout_eq(str![[r#" Solidity: -0.8.30: +0.8.33: ├── src/ContractC.sol └── src/ContractD.sol @@ -128,7 +128,7 @@ forgetest!(can_list_resolved_compiler_versions_verbose_json, |prj, cmd| { { "Solidity": [ { - "version": "0.8.30", + "version": "0.8.33", "paths": [ "src/ContractC.sol", "src/ContractD.sol" @@ -153,7 +153,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions, |prj, cmd| { Solidity: - 0.8.4 - 0.8.11 -- 0.8.30 +- 0.8.33 Vyper: - 0.4.3 @@ -198,7 +198,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped_json, |prj, cmd| { "Solidity": [ { - "version": "0.8.30", + "version": "0.8.33", "paths": [ "src/ContractD.sol" ] @@ -236,7 +236,7 @@ Solidity: 0.8.11 (<= london): └── src/ContractB.sol -0.8.30 (<= prague): +0.8.33 (<= prague): ├── src/ContractC.sol └── src/ContractD.sol @@ -277,7 +277,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_verbose_json, |prj, cmd| ] }, { - "version": "0.8.30", + "version": "0.8.33", "evm_version": "[..]", "paths": [ "src/ContractC.sol", diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 468d79e0730b9..b172ea70c24e0 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -49,7 +49,7 @@ allow_paths = [] include_paths = [] skip = [] force = false -evm_version = "prague" +evm_version = "osaka" gas_reports = ["*"] gas_reports_ignore = [] gas_reports_include_tests = false @@ -59,17 +59,22 @@ optimizer = false optimizer_runs = 200 verbosity = 0 eth_rpc_accept_invalid_certs = false +eth_rpc_no_proxy = false +eth_rpc_curl = false ignored_error_codes = [ "license", "code-size", "init-code-size", "transient-storage", + "transfer-deprecated", + "natspec-memory-safe-assembly-deprecated", ] ignored_warnings_from = [] deny = "never" test_failures_file = "cache/test-failures" show_progress = false ffi = false +live_logs = false allow_internal_expect_revert = false always_use_create_2_factory = false prompt_timeout = 120 @@ -137,6 +142,7 @@ docs_style = "preserve" ignore = [] contract_new_lines = false sort_imports = false +namespace_import_style = "prefer_plain" pow_no_space = false prefer_compact = "all" single_line_imports = false @@ -153,6 +159,14 @@ lint_on_build = true mixed_case_exceptions = [ "ERC", "URI", + "ID", + "URL", + "API", + "JSON", + "XML", + "HTML", + "HTTP", + "HTTPS", ] [doc] @@ -201,6 +215,7 @@ show_edge_coverage = false failure_persist_dir = "cache/invariant" show_metrics = true show_solidity = false +check_interval = 1 [labels] @@ -283,6 +298,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { ..Default::default() }, ffi: true, + live_logs: true, allow_internal_expect_revert: false, always_use_create_2_factory: false, prompt_timeout: 0, @@ -306,9 +322,11 @@ forgetest!(can_extract_config_values, |prj, cmd| { memory_limit: 1 << 27, eth_rpc_url: Some("localhost".to_string()), eth_rpc_accept_invalid_certs: false, + eth_rpc_no_proxy: false, eth_rpc_jwt: None, eth_rpc_timeout: None, eth_rpc_headers: None, + eth_rpc_curl: false, etherscan_api_key: None, etherscan: Default::default(), verbosity: 4, @@ -1195,7 +1213,7 @@ forgetest_init!(test_default_config, |prj, cmd| { "include_paths": [], "skip": [], "force": false, - "evm_version": "prague", + "evm_version": "osaka", "gas_reports": [ "*" ], @@ -1211,15 +1229,19 @@ forgetest_init!(test_default_config, |prj, cmd| { "verbosity": 0, "eth_rpc_url": null, "eth_rpc_accept_invalid_certs": false, + "eth_rpc_no_proxy": false, "eth_rpc_jwt": null, "eth_rpc_timeout": null, "eth_rpc_headers": null, + "eth_rpc_curl": false, "etherscan_api_key": null, "ignored_error_codes": [ "license", "code-size", "init-code-size", - "transient-storage" + "transient-storage", + "transfer-deprecated", + "natspec-memory-safe-assembly-deprecated" ], "ignored_warnings_from": [], "deny": "never", @@ -1278,9 +1300,11 @@ forgetest_init!(test_default_config, |prj, cmd| { "timeout": null, "show_solidity": false, "max_time_delay": null, - "max_block_delay": null + "max_block_delay": null, + "check_interval": 1 }, "ffi": false, + "live_logs": false, "allow_internal_expect_revert": false, "always_use_create_2_factory": false, "prompt_timeout": 120, @@ -1336,6 +1360,7 @@ forgetest_init!(test_default_config, |prj, cmd| { "ignore": [], "contract_new_lines": false, "sort_imports": false, + "namespace_import_style": "prefer_plain", "pow_no_space": false, "prefer_compact": "all", "single_line_imports": false @@ -1351,7 +1376,15 @@ forgetest_init!(test_default_config, |prj, cmd| { "lint_on_build": true, "mixed_case_exceptions": [ "ERC", - "URI" + "URI", + "ID", + "URL", + "API", + "JSON", + "XML", + "HTML", + "HTTP", + "HTTPS" ] }, "doc": { @@ -1790,7 +1823,7 @@ contract Counter { let v1_profile = SettingsOverrides { name: "v1".to_string(), via_ir: Some(true), - evm_version: Some(EvmVersion::Prague), + evm_version: Some(EvmVersion::Osaka), optimizer: None, optimizer_runs: Some(44444444), bytecode_hash: None, @@ -1876,19 +1909,19 @@ contract Counter { let (via_ir, evm_version, enabled, runs) = artifact_settings("Counter.sol/Counter.json"); assert_eq!(None, via_ir); - assert_eq!("\"prague\"", evm_version.unwrap().to_string()); + assert_eq!("\"osaka\"", evm_version.unwrap().to_string()); assert_eq!("false", enabled.unwrap().to_string()); assert_eq!("200", runs.unwrap().to_string()); let (via_ir, evm_version, enabled, runs) = artifact_settings("v1/Counter.sol/Counter.json"); assert_eq!("true", via_ir.unwrap().to_string()); - assert_eq!("\"prague\"", evm_version.unwrap().to_string()); + assert_eq!("\"osaka\"", evm_version.unwrap().to_string()); assert_eq!("true", enabled.unwrap().to_string()); assert_eq!("44444444", runs.unwrap().to_string()); let (via_ir, evm_version, enabled, runs) = artifact_settings("v2/Counter.sol/Counter.json"); assert_eq!("true", via_ir.unwrap().to_string()); - assert_eq!("\"prague\"", evm_version.unwrap().to_string()); + assert_eq!("\"osaka\"", evm_version.unwrap().to_string()); assert_eq!("true", enabled.unwrap().to_string()); assert_eq!("111", runs.unwrap().to_string()); diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index 96c40723b3a8a..a8805ff9c4c34 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -2204,6 +2204,90 @@ contract CounterTest is DSTest { "#]]); }); +// +// Test BRDA hit values follow LCOV spec: "-" when line never executed, "0" when line hit but +// branch not taken. This ensures `genhtml` consistency. +forgetest!(brda_lcov_consistency, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source( + "Counter.sol", + r#" +contract Counter { + uint256 public number; + + function setPositive(uint256 newNumber) public { + if (newNumber > 0) { + number = newNumber; + } else { + number = 1; + } + } + + function neverCalled(uint256 x) public { + if (x > 100) { + number = x; + } else { + number = 100; + } + } +} + "#, + ); + + prj.add_source( + "Counter.t.sol", + r#" +import "./test.sol"; +import "./Counter.sol"; + +contract CounterTest is DSTest { + function test_only_positive_branch() public { + Counter counter = new Counter(); + counter.setPositive(42); + counter.setPositive(100); + } +} + "#, + ); + + // Verify BRDA values: + // - BRDA:8,0,0,2 - if branch taken 2 times + // - BRDA:8,0,1,0 - else branch NOT taken but line was hit (outputs "0", not "-") + // - BRDA:16,1,0,- - if branch NOT taken AND line never executed (outputs "-") + // - BRDA:16,1,1,- - else branch NOT taken AND line never executed (outputs "-") + assert_lcov( + cmd.arg("coverage"), + str![[r#" +TN: +SF:src/Counter.sol +DA:7,2 +FN:7,Counter.setPositive +FNDA:2,Counter.setPositive +DA:8,2 +BRDA:8,0,0,2 +BRDA:8,0,1,0 +DA:9,2 +DA:11,0 +DA:15,0 +FN:15,Counter.neverCalled +FNDA:0,Counter.neverCalled +DA:16,0 +BRDA:16,1,0,- +BRDA:16,1,1,- +DA:17,0 +DA:19,0 +FNF:2 +FNH:1 +LF:8 +LH:3 +BRF:4 +BRH:1 +end_of_record + +"#]], + ); +}); + // Test that coverage files are written even when tests fail. forgetest!(coverage_with_failing_tests, |prj, cmd| { prj.insert_ds_test(); diff --git a/crates/forge/tests/cli/doc.rs b/crates/forge/tests/cli/doc.rs index f1f6a8b2dec59..947c7a6a9acec 100644 --- a/crates/forge/tests/cli/doc.rs +++ b/crates/forge/tests/cli/doc.rs @@ -112,3 +112,178 @@ contract Derived is IBase { content.lines().find(|line| line.contains("[IBase]")).unwrap_or("not found") ); }); + +// Test that constants and immutables are documented under "Constants" section when only constants +// are present fixes +forgetest_init!(constants_and_immutables_are_documented_under_constants_section, |prj, cmd| { + prj.add_source( + "CounterConstants.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19; + +contract CounterConstants { + uint256 public constant FOO = 1; + uint256 public immutable BAR; + + constructor() { + BAR = 2; + } +} +"#, + ); + + cmd.args(["doc", "--build"]).assert_success(); + + let doc_path = + prj.root().join("docs/src/src/CounterConstants.sol/contract.CounterConstants.md"); + let content = std::fs::read_to_string(&doc_path).unwrap(); + + // Check that Constants section exists + assert!(content.contains("## Constants"), "Should have Constants section"); + // Check that State Variables section does not exist + assert!(!content.contains("## State Variables"), "Should not have State Variables section"); + + // Get the position of the Constants section and of the Functions section + let constants_section_pos = content.find("## Constants").unwrap(); + let functions_section_pos = content.find("## Functions").unwrap(); + + // Check that Constants section contains the constant + assert!(content.contains("### FOO"), "Should have FOO constant"); + let foo_constant_pos = content.find("## FOO").unwrap(); + assert!( + foo_constant_pos > constants_section_pos && foo_constant_pos < functions_section_pos, + "FOO constant should be after Constants section and before Functions section" + ); + + // Check that Constants section contains the immutable + let bar_immutable_pos = content.find("## BAR").unwrap(); + assert!(content.contains("### BAR"), "Should have BAR immutable"); + assert!( + bar_immutable_pos > constants_section_pos && bar_immutable_pos < functions_section_pos, + "BAR immutable should be after Constants section and before Functions section" + ); +}); + +// Test that state variables are documented under "State Variables" section when only state +// variables are present fixes +forgetest_init!(state_variables_are_documented_under_state_variables_section, |prj, cmd| { + prj.add_source( + "CounterStateVariables.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19; + +contract CounterStateVariables { + uint256 public baz; + + function increment() public { + baz++; + } +} +"#, + ); + + cmd.args(["doc", "--build"]).assert_success(); + + let doc_path = + prj.root().join("docs/src/src/CounterStateVariables.sol/contract.CounterStateVariables.md"); + let content = std::fs::read_to_string(&doc_path).unwrap(); + + // Check that Constants section does not exist + assert!(!content.contains("## Constants"), "Should not have Constants section"); + // Check that State Variables section exists + assert!(content.contains("## State Variables"), "Should have State Variables section"); + + // Get the position of the State Variables section and of the Functions section + let state_variables_section_pos = content.find("## State Variables").unwrap(); + let functions_section_pos = content.find("## Functions").unwrap(); + + // Check that State Variables section contains the state variable + assert!(content.contains("### baz"), "Should have baz state variable"); + let baz_state_variable_pos = content.find("## baz").unwrap(); + assert!( + baz_state_variable_pos > state_variables_section_pos + && baz_state_variable_pos < functions_section_pos, + "baz state variable should be after State Variables section and before Functions section" + ); +}); + +// Test that constants/immutables and state-variables are documented under separate sections when +// both are present fixes +forgetest_init!( + constants_and_immutables_and_state_variables_are_documented_under_separate_sections, + |prj, cmd| { + prj.add_source( + "CounterMixedVariables.sol", + r#" +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19; + +contract CounterMixedVariables { + uint256 public constant FOO = 1; + uint256 public immutable BAR; + uint256 public baz; + + constructor() { + BAR = 2; + } + + function increment() public { + baz++; + } +} +"#, + ); + + cmd.args(["doc", "--build"]).assert_success(); + + let doc_path = prj + .root() + .join("docs/src/src/CounterMixedVariables.sol/contract.CounterMixedVariables.md"); + let content = std::fs::read_to_string(&doc_path).unwrap(); + + // Check that Constants section and the State Variables section exist + assert!(content.contains("## Constants"), "Should have Constants section"); + assert!(content.contains("## State Variables"), "Should have State Variables section"); + + // Get the position of the Constants, State Variables, and Functions sections + let constants_section_pos = content.find("## Constants").unwrap(); + let state_variables_section_pos = content.find("## State Variables").unwrap(); + let functions_section_pos = content.find("## Functions").unwrap(); + + // Validate that the sections are in the correct order + assert!( + constants_section_pos < state_variables_section_pos + && state_variables_section_pos < functions_section_pos, + "Constants section should be before State Variables section and before Functions section" + ); + + // Check that Constants section contains the constant + assert!(content.contains("### FOO"), "Should have FOO constant"); + let foo_constant_pos = content.find("## FOO").unwrap(); + assert!( + foo_constant_pos > constants_section_pos + && foo_constant_pos < state_variables_section_pos, + "FOO constant should be after Constants section and before State Variables section" + ); + + // Check that Constants section contains the immutable + assert!(content.contains("### BAR"), "Should have BAR immutable"); + let bar_immutable_pos = content.find("## BAR").unwrap(); + assert!( + bar_immutable_pos > constants_section_pos + && bar_immutable_pos < state_variables_section_pos, + "BAR immutable should be after Constants section and before State Variables section" + ); + + // Check that State Variables section contains the state variable + assert!(content.contains("### baz"), "Should have baz state variable"); + let baz_state_variable_pos = content.find("## baz").unwrap(); + assert!( + baz_state_variable_pos > state_variables_section_pos + && baz_state_variable_pos < functions_section_pos, + "baz state variable should be after State Variables section and before Functions section" + ); + } +); diff --git a/crates/forge/tests/cli/eip712.rs b/crates/forge/tests/cli/eip712.rs index 1340c86565389..919f93366b23b 100644 --- a/crates/forge/tests/cli/eip712.rs +++ b/crates/forge/tests/cli/eip712.rs @@ -284,8 +284,7 @@ contract Eip712Test is DSTest { [r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] -Compiler run successful! - +... Ran 1 test for src/Eip712Cheat.sol:Eip712Test [PASS] testEip712HashType() ([GAS]) Logs: diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index b4fd3d908133d..fbd84739635c5 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -64,7 +64,17 @@ fn sablier_v2_core() { // #[test] fn solady() { - ExtTester::new("Vectorized", "solady", "cbcfe0009477aa329574f17e8db0a05703bb8bdd").run(); + let mut tester = + ExtTester::new("Vectorized", "solady", "cbcfe0009477aa329574f17e8db0a05703bb8bdd"); + + // This test expects the mover contract created via CREATE2 to be selfdestructed within the + // same transaction. In isolation mode, each top-level call runs as a separate transaction + // context, so the selfdestruct doesn't clear the code as expected by the test. + if cfg!(feature = "isolate-by-default") { + tester = tester.args(["--nmt", "testSafeMoveETHViaMover"]); + } + + tester.run(); } // diff --git a/crates/forge/tests/cli/failure_assertions.rs b/crates/forge/tests/cli/failure_assertions.rs index f6b3f15f3fd71..087b1fc9491ac 100644 --- a/crates/forge/tests/cli/failure_assertions.rs +++ b/crates/forge/tests/cli/failure_assertions.rs @@ -190,7 +190,7 @@ Suite result: FAILED. 0 passed; 8 failed; 0 skipped; [ELAPSED] "#]]); }); -forgetest!(expect_emit_tests_should_fail, |prj, cmd| { +forgetest!(flaky_expect_emit_tests_should_fail, |prj, cmd| { prj.insert_ds_test(); prj.insert_vm(); @@ -239,7 +239,7 @@ Suite result: FAILED. 0 passed; 5 failed; 0 skipped; [ELAPSED] ); }); -forgetest!(expect_emit_params_tests_should_fail, |prj, cmd| { +forgetest!(flaky_expect_emit_params_tests_should_fail, |prj, cmd| { prj.insert_ds_test(); prj.insert_vm(); prj.update_config(|config| { diff --git a/crates/forge/tests/cli/fmt.rs b/crates/forge/tests/cli/fmt.rs index c2deb78b00d61..0865f381854de 100644 --- a/crates/forge/tests/cli/fmt.rs +++ b/crates/forge/tests/cli/fmt.rs @@ -3,7 +3,7 @@ use foundry_test_utils::{forgetest, forgetest_init}; const UNFORMATTED: &str = r#"// SPDX-License-Identifier: MIT -pragma solidity =0.8.30 ; +pragma solidity =0.8.33 ; contract Test { uint256 public value ; @@ -13,7 +13,7 @@ contract Test { }"#; const FORMATTED: &str = r#"// SPDX-License-Identifier: MIT -pragma solidity =0.8.30; +pragma solidity =0.8.33; contract Test { uint256 public value; @@ -88,8 +88,8 @@ forgetest!(fmt_check_mode_stdin, |_prj, cmd| { cmd.assert_failure().stderr_eq("").stdout_eq(str![[r#" Diff in stdin: 1 1 | // SPDX-License-Identifier: MIT -2 |-pragma solidity =0.8.30 ; - 2 |+pragma solidity =0.8.30; +2 |-pragma solidity =0.8.33 ; + 2 |+pragma solidity =0.8.33; ... 4 |-contract Test { 5 |- uint256 public value ; diff --git a/crates/forge/tests/cli/lint.rs b/crates/forge/tests/cli/lint.rs index f707de279d0e8..5b91c1bf9bcf6 100644 --- a/crates/forge/tests/cli/lint.rs +++ b/crates/forge/tests/cli/lint.rs @@ -1,5 +1,5 @@ use forge_lint::{linter::Lint, sol::med::REGISTERED_LINTS}; -use foundry_config::{DenyLevel, LintSeverity, LinterConfig}; +use foundry_config::{DenyLevel, LintSeverity, LinterConfig, SolidityErrorCode}; mod geiger; @@ -128,12 +128,12 @@ forgetest!(can_use_config, |prj, cmd| { }); cmd.arg("lint").assert_success().stderr_eq(str![[r#" warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - [FILE]:16:9 - | -16 | (1 / 2) * 3; - | ^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + [FILE]:16:9 + │ +16 │ (1 / 2) * 3; + │ ━━━━━━━━━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply "#]]); @@ -155,12 +155,12 @@ forgetest!(can_use_config_ignore, |prj, cmd| { }); cmd.arg("lint").assert_success().stderr_eq(str![[r#" note[mixed-case-function]: function names should use mixedCase - [FILE]:9:14 - | -9 | function functionMIXEDCaseInfo() public {} - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `functionMixedCaseInfo` - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + [FILE]:9:14 + │ +9 │ function functionMIXEDCaseInfo() public {} + │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCaseInfo` + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function "#]]); @@ -211,12 +211,12 @@ forgetest!(can_override_config_severity, |prj, cmd| { }); cmd.arg("lint").args(["--severity", "info"]).assert_success().stderr_eq(str![[r#" note[mixed-case-function]: function names should use mixedCase - [FILE]:9:14 - | -9 | function functionMIXEDCaseInfo() public {} - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `functionMixedCaseInfo` - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + [FILE]:9:14 + │ +9 │ function functionMIXEDCaseInfo() public {} + │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCaseInfo` + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function "#]]); @@ -238,12 +238,12 @@ forgetest!(can_override_config_path, |prj, cmd| { }); cmd.arg("lint").arg("src/ContractWithLints.sol").assert_success().stderr_eq(str![[r#" warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - [FILE]:16:9 - | -16 | (1 / 2) * 3; - | ^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + [FILE]:16:9 + │ +16 │ (1 / 2) * 3; + │ ━━━━━━━━━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply "#]]); @@ -266,12 +266,12 @@ forgetest!(can_override_config_lint, |prj, cmd| { cmd.arg("lint").args(["--only-lint", "incorrect-shift"]).assert_success().stderr_eq(str![[ r#" warning[incorrect-shift]: the order of args in a shift operation is incorrect - [FILE]:13:26 - | -13 | uint256 result = 8 >> localValue; - | ^^^^^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + [FILE]:13:26 + │ +13 │ uint256 result = 8 >> localValue; + │ ━━━━━━━━━━━━━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift "# @@ -295,12 +295,12 @@ forgetest!(build_runs_linter_by_default, |prj, cmd| { // Run forge build and expect linting output before compilation cmd.arg("build").assert_success().stderr_eq(str![[r#" warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision - [FILE]:16:9 - | -16 | (1 / 2) * 3; - | ^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + [FILE]:16:9 + │ +16 │ (1 / 2) * 3; + │ ━━━━━━━━━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply "#]]).stdout_eq(str![[r#" @@ -456,12 +456,12 @@ forgetest!(can_use_only_lint_with_multilint_passes, |prj, cmd| { prj.add_source("OnlyImports", ONLY_IMPORTS); cmd.arg("lint").args(["--only-lint", "unused-import"]).assert_success().stderr_eq(str![[r#" note[unused-import]: unused imports should be removed - [FILE]:8:10 - | -8 | import { _PascalCaseInfo } from "./ContractWithLints.sol"; - | ^^^^^^^^^^^^^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + [FILE]:8:10 + │ +8 │ import { _PascalCaseInfo } from "./ContractWithLints.sol"; + │ ━━━━━━━━━━━━━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import "#]]); @@ -488,12 +488,12 @@ note[mixed-case-variable]: mutable variables should use mixedCase let args = ["build", "src/CounterBWithLints.sol"]; cmd.forge_fuse().args(args).assert_success().stderr_eq(str![[r#" note[mixed-case-variable]: mutable variables should use mixedCase - [FILE]:6:20 - | -6 | uint256 public CounterB_Fail_Lint; - | ^^^^^^^^^^^^^^^^^^ help: consider using: `counterBFailLint` - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + [FILE]:6:20 + │ +6 │ uint256 public CounterB_Fail_Lint; + │ ━━━━━━━━━━━━━━━━━━ help: consider using: `counterBFailLint` + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable "#]]); @@ -654,7 +654,7 @@ forgetest!(lint_json_output_no_ansi_escape_codes, |prj, cmd| { "rendered": null } ], - "rendered": "note[unwrapped-modifier-logic]: wrap modifier logic to reduce code size\n\n --> src/UnwrappedModifierTest.sol:8:13\n |\n 8 | / modifier onlyOwner() {\n 9 | | require(isOwner[msg.sender], \"Not owner\");\n10 | | require(msg.sender != address(0), \"Zero address\");\n11 | | _;\n12 | | }\n | |_____________^\n |\nhelp: wrap modifier logic to reduce code size\n |\n 8 ~ modifier onlyOwner() {\n 9 + _onlyOwner();\n10 + _;\n11 + }\n12 + \n13 + function _onlyOwner() internal {\n14 + require(isOwner[msg.sender], \"Not owner\");\n15 + require(msg.sender != address(0), \"Zero address\");\n16 + }\n |\n = help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic\n" + "rendered": "note[unwrapped-modifier-logic]: wrap modifier logic to reduce code size\n\nhelp: wrap modifier logic to reduce code size\n 9 + _onlyOwner();\n10 + _;\n11 + }\n12 + \n13 + function _onlyOwner() internal {\n14 + require(isOwner[msg.sender], \"Not owner\");\n15 + require(msg.sender != address(0), \"Zero address\");\n16 + }\n ╭▸ src/UnwrappedModifierTest.sol:8:13\n │\n 8 │ ┏ modifier onlyOwner() {\n 9 │ ┃ require(isOwner[msg.sender], \"Not owner\");\n10 │ ┃ require(msg.sender != address(0), \"Zero address\");\n11 │ ┃ _;\n12 │ ┃ }\n │ ┗━━━━━━━━━━━━━┛\n │\n ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic\n ╭╴\n 8 ± modifier onlyOwner() {\n ╰╴\n" } "#]], ); @@ -735,8 +735,7 @@ Warning: Key `deny_warnings` is being deprecated in favor of `deny = warnings`. #[tokio::test] async fn ensure_lint_rule_docs() { - const FOUNDRY_BOOK_LINT_PAGE_URL: &str = - "https://book.getfoundry.sh/reference/forge/forge-lint"; + const FOUNDRY_BOOK_LINT_PAGE_URL: &str = "https://book.getfoundry.sh/forge/linting"; // Fetch the content of the lint reference let content = match reqwest::get(FOUNDRY_BOOK_LINT_PAGE_URL).await { @@ -762,8 +761,11 @@ async fn ensure_lint_rule_docs() { // Ensure no missing lints let mut missing_lints = Vec::new(); for lint in REGISTERED_LINTS { - let selector = format!("#{}", lint.id()); - if !content.contains(&selector) { + let selector = lint.id().to_lowercase(); + let selector_with_space = selector.replace("-", " "); + if !content.to_lowercase().contains(&selector) + && !content.to_lowercase().contains(&selector_with_space) + { missing_lints.push(lint.id()); } } @@ -787,6 +789,51 @@ fn ensure_no_privileged_lint_id() { } } +// +forgetest!(dependency_warnings_do_not_affect_lint_exit_code, |prj, cmd| { + // Library with code that triggers a solc warning (unused local variable) + const LIB_WITH_WARNING: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +library LibWithWarning { + function foo() internal pure returns (uint256) { + uint256 unusedVar = 42; + return 1; + } +} +"#; + + // Clean contract that imports the library but has no lint issues + const CLEAN_CONTRACT: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { LibWithWarning } from "../lib/LibWithWarning.sol"; + +contract CleanContract { + function bar() public pure returns (uint256) { + return LibWithWarning.foo(); + } +} +"#; + + prj.add_lib("LibWithWarning", LIB_WITH_WARNING); + prj.add_source("CleanContract", CLEAN_CONTRACT); + + // Ignore the solc warning so compilation succeeds, but it still gets counted in diagnostics + prj.update_config(|config| { + config.ignored_error_codes = vec![SolidityErrorCode::UnusedLocalVariable]; + }); + + // Clear cache to force recompilation during lint + prj.clear_cache(); + + // Lint with deny = notes via CLI flag. + // Should succeed because the linter only counts lint diagnostics, not build-phase warnings. + cmd.args(["lint", "-D", "notes"]).assert_success(); +}); + forgetest!(skips_linting_for_old_solidity_versions, |prj, cmd| { const OLD_CONTRACT: &str = r#" // SPDX-License-Identifier: MIT @@ -813,14 +860,14 @@ contract OldContract { // Run forge build - should SUCCEED without linting cmd.arg("build").assert_success().stderr_eq(str![[ - r#"Warning: unable to lint. Solar only supports Solidity versions prior to 0.8.0 + r#"Warning: unable to lint. Solar only supports Solidity versions >=0.8.0 "# ]]); // Run forge lint - should FAIL cmd.forge_fuse().arg("lint").assert_failure().stderr_eq(str![[ - r#"Error: unable to lint. Solar only supports Solidity versions prior to 0.8.0 + r#"Error: unable to lint. Solar only supports Solidity versions >=0.8.0 "# ]]); diff --git a/crates/forge/tests/cli/lint/geiger.rs b/crates/forge/tests/cli/lint/geiger.rs index 9b768f21ec239..faecfb212fb90 100644 --- a/crates/forge/tests/cli/lint/geiger.rs +++ b/crates/forge/tests/cli/lint/geiger.rs @@ -16,12 +16,12 @@ forgetest_init!(call, |prj, cmd| { cmd.arg("geiger").assert_failure().stderr_eq(str![[r#" ... note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations - [FILE]:9:20 - | -9 | vm.ffi(inputs); - | ^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + [FILE]:9:20 + │ +9 │ vm.ffi(inputs); + │ ━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode Error: aborting due to 1 linter note(s) ... @@ -47,12 +47,12 @@ forgetest_init!(assignment, |prj, cmd| { cmd.arg("geiger").assert_failure().stderr_eq(str![[r#" ... note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations - [FILE]:9:41 - | -9 | bytes memory stuff = vm.ffi(inputs); - | ^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + [FILE]:9:41 + │ +9 │ bytes memory stuff = vm.ffi(inputs); + │ ━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode Error: aborting due to 1 linter note(s) ... @@ -79,28 +79,28 @@ forgetest_init!(exit_code, |prj, cmd| { cmd.arg("geiger").assert_failure().stderr_eq(str![[r#" ... note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations - [FILE]:9:20 - | -9 | vm.ffi(inputs); - | ^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + [FILE]:9:20 + │ +9 │ vm.ffi(inputs); + │ ━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations - [FILE]:10:20 - | -10 | vm.ffi(inputs); - | ^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + [FILE]:10:20 + │ +10 │ vm.ffi(inputs); + │ ━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations - [FILE]:11:20 - | -11 | vm.ffi(inputs); - | ^^^ - | - = help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + [FILE]:11:20 + │ +11 │ vm.ffi(inputs); + │ ━━━ + │ + ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode Error: aborting due to 3 linter note(s) ... diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index d0aa25f918f76..7f3dc7b9cd7a5 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1945,7 +1945,7 @@ contract SimpleScript is Script { ]) .assert_success() .stdout_eq(str![[r#" -{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"[..]","output":"[..]","gas_used":"{...}","gas_limit":"{...}","status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":"{...}","gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":"true","call_data":{"signature":"run()","args":[]}}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":"{...}","gas_limit":1056940999,"status":"Return","steps":[],"decoded":{"label":"VM","return_data":null,"call_data":{"signature":"startBroadcast()","args":[]}}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":"{...}","gas_limit":1056940650,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":"{...}","labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} +{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"[..]","output":"[..]","gas_used":"{...}","gas_limit":"{...}","gas_refund_counter":0,"status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":"{...}","gas_limit":1073720760,"gas_refund_counter":0,"status":"Return","steps":[],"decoded":{"label":"SimpleScript","return_data":"true","call_data":{"signature":"run()","args":[]}}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":"{...}","gas_limit":1056940999,"gas_refund_counter":0,"status":"Return","steps":[],"decoded":{"label":"VM","return_data":null,"call_data":{"signature":"startBroadcast()","args":[]}}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":"{...}","gas_limit":1056940650,"gas_refund_counter":0,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":"{...}","labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} {"chain":31337,"estimated_gas_price":"{...}","estimated_total_gas_used":"{...}","estimated_amount_required":"{...}","token_symbol":"ETH"} {"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":"{...}","gas_price":"{...}"} {"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} @@ -3144,7 +3144,7 @@ contract CounterScript is Script { Compiler run successful! Traces: [..] → new CounterScript@[..] - └─ ← [Return] 2200 bytes of code + └─ ← [Return] 2162 bytes of code [..] CounterScript::setUp() └─ ← [Stop] @@ -3298,7 +3298,7 @@ ONCHAIN EXECUTION COMPLETE & SUCCESSFUL. "#]]); }); -forgetest_async!(can_deploy_with_broadcast_in_setup, |prj, cmd| { +forgetest_async!(flaky_can_deploy_with_broadcast_in_setup, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); prj.add_script( "Deploy.s.sol", @@ -3422,3 +3422,72 @@ forgetest_async!(can_execute_script_with_createx_and_via_ir, |prj, cmd| { ]) .assert_success(); }); + +forgetest_async!(script_can_run_with_live_logs_flag, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo.s.sol", + r#" +import {Script, console} from "forge-std/Script.sol"; + +contract Foo is Script { + function setUp() pure public { + console.log("Setup"); + } + + function run() pure public { + console.log("Run %d", uint256(1)); + } +} + "#, + ); + + cmd.forge_fuse() + .args(["script", "script/Foo.s.sol", "--live-logs"]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Setup +Run 1 +Script ran successfully. +[GAS] + +"#]]); +}); + +forgetest_async!(script_can_run_with_live_logs_config, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.update_config(|config| { + config.live_logs = true; + }); + + prj.add_script( + "Foo.s.sol", + r#" +import {Script, console} from "forge-std/Script.sol"; + +contract Foo is Script { + function setUp() pure public { + console.log("Setup"); + } + + function run() pure public { + console.log("Run %d", uint256(1)); + } +} + "#, + ); + + cmd.forge_fuse().args(["script", "script/Foo.s.sol"]).assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Setup +Run 1 +Script ran successfully. +[GAS] + +"#]]); +}); diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index 8d9fd6811b5ec..b80805cbecfc4 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, 30); +const LATEST_SOLC: Version = Version::new(0, 8, 34); macro_rules! ensure_svm_releases { ($($test:ident => $platform:ident),* $(,)?) => {$( diff --git a/crates/forge/tests/cli/test_cmd/fuzz.rs b/crates/forge/tests/cli/test_cmd/fuzz.rs index d908c2d5542ff..fefefb30d9b15 100644 --- a/crates/forge/tests/cli/test_cmd/fuzz.rs +++ b/crates/forge/tests/cli/test_cmd/fuzz.rs @@ -88,7 +88,7 @@ forgetest_init!(test_fuzz_timeout, |prj, cmd| { import {Test} from "forge-std/Test.sol"; contract FuzzTimeoutTest is Test { - /// forge-config: default.fuzz.max-test-rejects = 50000 + /// forge-config: default.fuzz.max-test-rejects = 0 /// forge-config: default.fuzz.timeout = 1 function test_fuzz_bound(uint256 a) public pure { vm.assume(a == 0); @@ -97,7 +97,7 @@ contract FuzzTimeoutTest is Test { "#, ); - cmd.args(["test"]).assert_success().stdout_eq(str![[r#" + cmd.args(["test", "-j2"]).assert_success().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -186,7 +186,7 @@ contract Counter { ); // Tests should fail as revert happens in cheatcode (assert) and test (require) contract. - cmd.assert_failure().stdout_eq(str![[r#" + cmd.args(["-j1"]).assert_failure().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] Compiler run successful! @@ -248,7 +248,7 @@ contract CounterTest is Test { "#, ); // Tests should fail and record counterexample with value 200. - cmd.args(["test"]).assert_failure().stdout_eq(str![[r#" + cmd.args(["test", "-j1"]).assert_failure().stdout_eq(str![[r#" ... Failing tests: Encountered 1 failing test in test/Counter.t.sol:CounterTest @@ -324,7 +324,7 @@ contract CounterTest is Test { "#, ); // Test should fail with replayed counterexample 200 (0 runs). - cmd.forge_fuse().args(["test"]).assert_failure().stdout_eq(str![[r#" + cmd.forge_fuse().args(["test", "-j1"]).assert_failure().stdout_eq(str![[r#" ... Failing tests: Encountered 1 failing test in test/Counter.t.sol:CounterTest @@ -384,6 +384,8 @@ Encountered a total of 1 failing tests, 2 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -525,7 +527,7 @@ Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] assert.stdout_eq(expected.clone()); }; - cmd.arg("test"); + cmd.args(["test", "-j1"]); // Run several times, asserting that the failure persists and is the same. for _ in 0..3 { @@ -775,12 +777,12 @@ forgetest_init!(should_fuzz_literals, |prj, cmd| { ); // Helper to create expected output for a test failure - let expected_fail = |test_name: &str, type_sig: &str, value: &str, runs: u32| -> String { + let expected_fail = |test_name: &str, type_sig: &str, value: &str| -> String { format!( r#"No files changed, compilation skipped Ran 1 test for test/MagicFuzz.t.sol:MagicTest -[FAIL: panic: assertion failed (0x01); counterexample: calldata=[..] args=[{value}]] {test_name}({type_sig}) (runs: {runs}, [AVG_GAS]) +[FAIL: panic: assertion failed (0x01); counterexample: calldata=[..] args=[{value}]] {test_name}({type_sig}) (runs: [..], [AVG_GAS]) [..] Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) @@ -797,41 +799,39 @@ Encountered a total of 1 failing tests, 0 tests succeeded let mut test_literal = |seed: u32, test_name: &'static str, type_sig: &'static str, - expected_value: &'static str, - expected_runs: u32| { + expected_value: &'static str| { // the fuzzer is UNABLE to find a breaking input (fast) when NOT seeding from the AST prj.update_config(|config| { config.fuzz.runs = 100; config.fuzz.dictionary.max_fuzz_dictionary_literals = 0; config.fuzz.seed = Some(U256::from(seed)); }); - cmd.forge_fuse().args(["test", "--match-test", test_name]).assert_success(); + cmd.forge_fuse().args(["test", "--match-test", test_name, "-j1"]).assert_success(); // the fuzzer is ABLE to find a breaking input when seeding from the AST prj.update_config(|config| { config.fuzz.dictionary.max_fuzz_dictionary_literals = 10_000; }); - let expected_output = expected_fail(test_name, type_sig, expected_value, expected_runs); + let expected_output = expected_fail(test_name, type_sig, expected_value); cmd.forge_fuse() - .args(["test", "--match-test", test_name]) + .args(["test", "--match-test", test_name, "-j1"]) .assert_failure() .stdout_eq(expected_output); }; - test_literal(100, "testFuzz_Addr", "address", "0x6B175474E89094C44Da98b954EedeAC495271d0F", 28); - test_literal(200, "testFuzz_Number", "uint64", "1122334455 [1.122e9]", 5); - test_literal(300, "testFuzz_Integer", "int32", "-777", 0); + test_literal(100, "testFuzz_Addr", "address", "0x6B175474E89094C44Da98b954EedeAC495271d0F"); + test_literal(200, "testFuzz_Number", "uint64", "1122334455 [1.122e9]"); + test_literal(300, "testFuzz_Integer", "int32", "-777"); test_literal( 400, "testFuzz_Word", "bytes32", "0x6162636431323334000000000000000000000000000000000000000000000000", /* bytes32("abcd1234") */ - 7, ); - test_literal(500, "testFuzz_BytesFromHex", "bytes", "0xdeadbeef", 5); - test_literal(600, "testFuzz_String", "string", "\"xyzzy\"", 35); - test_literal(999, "testFuzz_BytesFromString", "bytes", "0x78797a7a79", 19); // abi.encodePacked("xyzzy") + test_literal(500, "testFuzz_BytesFromHex", "bytes", "0xdeadbeef"); + test_literal(600, "testFuzz_String", "string", "\"xyzzy\""); + test_literal(999, "testFuzz_BytesFromString", "bytes", "0x78797a7a79"); // abi.encodePacked("xyzzy") }); // Tests that `vm.randomUint()` produces different values across fuzz runs. diff --git a/crates/forge/tests/cli/test_cmd/invariant/common.rs b/crates/forge/tests/cli/test_cmd/invariant/common.rs index 09cebfd41b9ea..eb28ca8c15ca0 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/common.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/common.rs @@ -98,6 +98,8 @@ Encountered a total of 2 failing tests, 1 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -115,8 +117,10 @@ forgetest_init!(invariant_assume, |prj, cmd| { import "forge-std/Test.sol"; contract Handler is Test { + uint256 public count; function doSomething(uint256 param) public { vm.assume(param == 0); + count++; } } @@ -135,13 +139,7 @@ contract InvariantAssume is Test { assert_invariant(cmd.args(["test"])).success().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] -Compiler run successful with warnings: -Warning (2018): Function state mutability can be restricted to pure - [FILE]:7:5: - | -7 | function doSomething(uint256 param) public { - | ^ (Relevant source part starts here and spans across multiple lines). - +Compiler run successful! Ran 1 test for test/InvariantAssume.t.sol:InvariantAssume [PASS] invariant_dummy() ([RUNS]) @@ -179,6 +177,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -308,6 +308,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -377,6 +379,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -392,10 +396,12 @@ forgetest_init!(invariant_excluded_senders, |prj, cmd| { import "forge-std/Test.sol"; contract InvariantSenders { + uint256 public count; 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"); + count++; } } @@ -414,13 +420,7 @@ contract InvariantExcludedSendersTest is Test { assert_invariant(cmd.args(["test"])).success().stdout_eq(str![[r#" [COMPILING_FILES] with [SOLC_VERSION] [SOLC_VERSION] [ELAPSED] -Compiler run successful with warnings: -Warning (2018): Function state mutability can be restricted to view - [FILE]:7:5: - | -7 | function checkSender() external { - | ^ (Relevant source part starts here and spans across multiple lines). - +Compiler run successful! Ran 1 test for test/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest [PASS] invariant_check_sender() ([RUNS]) @@ -546,6 +546,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -632,6 +634,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -704,6 +708,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -787,6 +793,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); // `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2. @@ -896,6 +904,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -988,6 +998,122 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + +"#]]); +}); + +// Tests that call_override detects the classic DAO-style reentrancy vulnerability +// in EtherStore where balances are updated AFTER the external call. +forgetest!(invariant_reentrancy_ether_store, |prj, cmd| { + prj.insert_utils(); + prj.update_config(|config| { + config.invariant.depth = 15; + config.invariant.fail_on_revert = false; + config.invariant.call_override = true; + }); + + prj.add_test( + "InvariantReentrancyEtherStore.t.sol", + r#" +import "./utils/Test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +// Classic reentrancy-vulnerable contract +contract EtherStore { + mapping(address => uint256) public balances; + + function deposit() public payable { + balances[msg.sender] += msg.value; + } + + function withdraw() public { + uint256 bal = balances[msg.sender]; + require(bal > 0); + // BUG: External call before state update + (bool sent,) = msg.sender.call{value: bal}(""); + require(sent, "Failed to send Ether"); + balances[msg.sender] = 0; + } +} + +contract InvariantReentrancyEtherStore is Test { + EtherStore store; + address attacker; + + function setUp() public { + store = new EtherStore(); + attacker = address(0x1337); + + vm.deal(address(this), 10 ether); + store.deposit{value: 5 ether}(); + + // Attacker gets 2 ether, deposits 1 ether, keeps 1 ether in wallet. + // After withdrawing their deposit, attacker wallet balance cannot exceed 2 ether. + vm.deal(attacker, 2 ether); + vm.prank(attacker); + store.deposit{value: 1 ether}(); + } + + function targetContracts() public view returns (address[] memory) { + address[] memory targets = new address[](1); + targets[0] = address(store); + return targets; + } + + function targetSenders() public view returns (address[] memory) { + address[] memory senders = new address[](1); + senders[0] = attacker; + return senders; + } + + function targetSelectors() public view returns (FuzzSelector[] memory) { + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = EtherStore.withdraw.selector; + FuzzSelector[] memory targets = new FuzzSelector[](1); + targets[0] = FuzzSelector(address(store), selectors); + return targets; + } + + // Attacker should never have more than 2 ether (1 kept + 1 withdrawn) + function invariantSolvency() public view { + require(attacker.balance <= 2 ether, "reentrancy: attacker wallet > 2 ether"); + } +} +"#, + ); + + assert_invariant(cmd.args(["test"])).failure().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +... +Ran 1 test for test/InvariantReentrancyEtherStore.t.sol:InvariantReentrancyEtherStore +[FAIL: reentrancy: attacker wallet > 2 ether] + [SEQUENCE] + invariantSolvency() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) + +Failing tests: +Encountered 1 failing test in test/InvariantReentrancyEtherStore.t.sol:InvariantReentrancyEtherStore +[FAIL: reentrancy: attacker wallet > 2 ether] + [SEQUENCE] + invariantSolvency() ([RUNS]) + +Encountered a total of 1 failing tests, 0 tests succeeded + +Tip: Run `forge test --rerun` to retry only the 1 failed test + +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1026,7 +1152,7 @@ contract InvariantRollForkBlockTest is Test { /// forge-config: default.invariant.runs = 2 /// forge-config: default.invariant.depth = 4 - function invariant_fork_handler_block() public { + function invariant_fork_handler_block() public view { require(block.number < 19812634, "too many blocks mined"); } } @@ -1040,7 +1166,7 @@ contract InvariantRollForkStateTest is Test { } /// forge-config: default.invariant.runs = 1 - function invariant_fork_handler_state() public { + function invariant_fork_handler_state() public view { require(forkHandler.totalSupply() < 3254378807384273078310283461, "wrong supply"); } } @@ -1048,24 +1174,47 @@ contract InvariantRollForkStateTest is Test { ); assert_invariant(cmd.args(["test", "-j1"])).failure().stdout_eq(str![[r#" -... +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/InvariantRollFork.t.sol:InvariantRollForkBlockTest +[FAIL: too many blocks mined] + [SEQUENCE] + invariant_fork_handler_block() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/InvariantRollFork.t.sol:InvariantRollForkStateTest +[FAIL: wrong supply] + [SEQUENCE] + invariant_fork_handler_state() ([RUNS]) + +[STATS] + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] + Ran 2 test suites [ELAPSED]: 0 tests passed, 2 failed, 0 skipped (2 total tests) Failing tests: Encountered 1 failing test in test/InvariantRollFork.t.sol:InvariantRollForkBlockTest [FAIL: too many blocks mined] -... + [SEQUENCE] invariant_fork_handler_block() ([RUNS]) Encountered 1 failing test in test/InvariantRollFork.t.sol:InvariantRollForkStateTest [FAIL: wrong supply] -... + [SEQUENCE] invariant_fork_handler_state() ([RUNS]) Encountered a total of 2 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1165,6 +1314,8 @@ Encountered a total of 2 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1464,6 +1615,8 @@ Encountered a total of 2 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 2 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1603,14 +1756,14 @@ Compiler run successful! Ran 1 test for test/HandlerWarpAndRoll.t.sol:HandlerWarpAndRoll [FAIL: max timestamp] - [Sequence] (original: 7, shrunk: 7) + [Sequence] (original: 5, shrunk: 5) sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=6280 roll=21461 calldata=setNumber(uint256) args=[200000 [2e5]] sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=92060 roll=51816 calldata=setNumber(uint256) args=[0] sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=198040 roll=60259 calldata=increment() args=[] sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=20609 roll=27086 calldata=setNumber(uint256) args=[26717227324157985679793128079000084308648530834088529513797156275625002 [2.671e70]] sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=409368 roll=24864 calldata=increment() args=[] - sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=218105 roll=17834 calldata=setNumber(uint256) args=[24752675372815722001736610830 [2.475e28]] - sender=[..] addr=[test/HandlerWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=579093 roll=23244 calldata=increment() args=[] + invariant_handler() (runs: 0, calls: 0, reverts: 1) + ... "#]]); @@ -1670,3 +1823,182 @@ Logs: ... "#]]); }); + +// Test optimization mode for invariant testing. +// When an invariant function returns int256, it becomes an optimization target. +// The fuzzer maximizes the return value instead of checking for failures. +forgetest!(invariant_optimization_mode, |prj, cmd| { + prj.insert_vm(); + prj.insert_ds_test(); + + prj.add_test( + "InvariantOptimize.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract OptimizationHandler { + int256 public value; + + // Each call adds exactly 10 to value + function increment() external { + value += 10; + } +} + +contract InvariantOptimizeTest is Test { + OptimizationHandler handler; + + function setUp() public { + handler = new OptimizationHandler(); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = handler.increment.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 5 + /// @notice Optimization mode: returns int256 to maximize. + /// With depth=5 and only increment(), max value should be 50. + function invariant_optimize_value() public view returns (int256) { + return handler.value(); + } +} +"#, + ); + + // Optimization mode: best value shown first, should reach 50 (5 calls * 10 each) + // Shows [Best sequence] with calls in output + cmd.args(["test", "-vvv"]).assert_success().stdout_eq(str![[r#" +... +[PASS] + [Best sequence] [..] +[..]calldata=increment()[..] +[..]calldata=increment()[..] +[..]calldata=increment()[..] +[..]calldata=increment()[..] +[..]calldata=increment()[..] + invariant_optimize_value() (best: 50, runs: 1, calls: 5) +... +"#]]); +}); + +// Test that optimization mode works with negative values (finding max of negative range). +forgetest!(invariant_optimization_negative_values, |prj, cmd| { + prj.insert_vm(); + prj.insert_ds_test(); + + prj.add_test( + "InvariantOptimizeNegative.t.sol", + r#" +import { DSTest as Test } from "src/test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract NegativeHandler { + int256 public value = -100; + + // Each call adds exactly 25 to value + function increase() external { + value += 25; + } +} + +contract InvariantOptimizeNegativeTest is Test { + NegativeHandler handler; + + function setUp() public { + handler = new NegativeHandler(); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = handler.increase.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 4 + /// Starting at -100, 4 calls of +25 each = -100 + 100 = 0 + function invariant_optimize_negative() public view returns (int256) { + return handler.value(); + } +} +"#, + ); + + // Optimization should reach 0: starting at -100, 4 calls * +25 = 0 + // Shows [Best sequence] with calls in output + cmd.args(["test", "-vvv"]).assert_success().stdout_eq(str![[r#" +... +[PASS] + [Best sequence] [..] +[..]calldata=increase()[..] +[..]calldata=increase()[..] +[..]calldata=increase()[..] +[..]calldata=increase()[..] + invariant_optimize_negative() (best: 0, runs: 1, calls: 4) +... +"#]]); +}); + +// Test optimization mode with time-dependent logic using warp and fixed seed for reproducibility. +// This test ensures warp values are correctly accumulated during shrinking. +forgetest_init!(invariant_optimization_with_warp, |prj, cmd| { + prj.add_test( + "InvariantOptimizeWarp.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract InvariantOptimizeWarpTest is Test { + int256 public maxValue; + + function setUp() public { + targetContract(address(this)); + } + + // Simulates time-dependent pricing with warp. Higher timestamp = higher value. + function updateValue(uint256 multiplier) public { + if (multiplier == 0 || multiplier > 100) revert(); + // Value depends on block.timestamp which can be warped + int256 newValue = int256(block.timestamp * multiplier / 1000); + if (newValue > maxValue) { + maxValue = newValue; + } + } + + /// forge-config: default.invariant.runs = 10 + /// forge-config: default.invariant.depth = 15 + /// forge-config: default.invariant.max_time_delay = 604800 + function invariant_optimize_max_value() public view returns (int256) { + return maxValue; + } +} +"#, + ); + + // Use fixed seed for deterministic output. The optimizer finds sequences that + // maximize value through time manipulation (warp). Shrinking reduces to 1 call. + cmd.args(["test", "-vvv", "--fuzz-seed", "12345"]).assert_success().stdout_eq(str![[r#" +... +[PASS] + [Best sequence] (original: 9, shrunk: 1) + sender=0x0000000000000000000000000000000000000637 addr=[test/InvariantOptimizeWarp.t.sol:InvariantOptimizeWarpTest]0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 warp=3249628 calldata=updateValue(uint256) args=[100] + invariant_optimize_max_value() (best: 324962, runs: 10, calls: 150) +... +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd/invariant/mod.rs b/crates/forge/tests/cli/test_cmd/invariant/mod.rs index 6f2f93363e293..3cbeeb4a46db7 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/mod.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/mod.rs @@ -431,6 +431,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); @@ -458,6 +460,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); @@ -481,6 +485,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); }); @@ -991,3 +997,183 @@ Ran 3 test suites [ELAPSED]: 6 tests passed, 0 failed, 0 skipped (6 total tests) prj.root().join("fuzz_corpus").join("Counter2Test").join("testFuzz_SetNumber").exists() ); }); + +// Tests that check_interval=0 only asserts on the last call of each run. +forgetest_init!(check_interval_zero_only_checks_last_call, |prj, cmd| { + prj.update_config(|config| { + config.invariant.runs = 5; + config.invariant.depth = 10; + config.invariant.check_interval = 0; + }); + prj.add_test( + "CheckIntervalTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterHandler { + uint256 public counter; + + function increment() public { + counter++; + } +} + +contract CheckIntervalTest is Test { + CounterHandler handler; + + function setUp() public { + handler = new CounterHandler(); + targetContract(address(handler)); + } + + // This invariant would fail on intermediate calls (counter 1-9) but passes on call 10 + // With check_interval=0, only the last call is checked, so if depth=10 and counter=10 + // at the end, this should pass even though intermediate states violated the invariant. + function invariant_counter_multiple_of_depth() public view { + // Only passes when counter is 0 or 10 (depth). Fails for 1-9. + require(handler.counter() == 0 || handler.counter() == 10, "not multiple of depth"); + } +} + "#, + ); + + cmd.args(["test", "--mt", "invariant_counter"]).assert_success().stdout_eq(str![[r#" +... +[PASS] invariant_counter_multiple_of_depth() (runs: 5, calls: 50, reverts: 0) +... +"#]]); +}); + +// Tests that check_interval=1 (default) asserts after every call. +forgetest_init!(check_interval_one_checks_every_call, |prj, cmd| { + prj.update_config(|config| { + config.invariant.runs = 1; + config.invariant.depth = 10; + config.invariant.check_interval = 1; + }); + prj.add_test( + "CheckIntervalTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterHandler { + uint256 public counter; + + function increment() public { + counter++; + } +} + +contract CheckIntervalTest is Test { + CounterHandler handler; + + function setUp() public { + handler = new CounterHandler(); + targetContract(address(handler)); + } + + // This invariant fails as soon as counter > 5. + // With check_interval=1, it should fail on call 6. + function invariant_counter_le_five() public view { + require(handler.counter() <= 5, "counter > 5"); + } +} + "#, + ); + + assert_invariant(cmd.args(["test", "--mt", "invariant_counter"])).failure().stdout_eq(str![[ + r#" +... +[FAIL: counter > 5] + [SEQUENCE] +... +"# + ]]); +}); + +// Tests that check_interval=N checks every N calls AND always on the last call. +forgetest_init!(check_interval_n_checks_every_n_calls, |prj, cmd| { + prj.update_config(|config| { + config.invariant.runs = 1; + config.invariant.depth = 20; + config.invariant.check_interval = 5; + }); + prj.add_test( + "CheckIntervalTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterHandler { + uint256 public counter; + + function increment() public { + counter++; + } +} + +contract CheckIntervalTest is Test { + CounterHandler handler; + + function setUp() public { + handler = new CounterHandler(); + targetContract(address(handler)); + } + + // With check_interval=5 and depth=20, invariant is checked at calls 5,10,15,20. + // This passes because 5,10,15,20 are all multiples of 5. + function invariant_counter_multiple_of_five() public view { + require(handler.counter() % 5 == 0, "not multiple of 5"); + } +} + "#, + ); + + cmd.args(["test", "--mt", "invariant_counter"]).assert_success().stdout_eq(str![[r#" +... +[PASS] invariant_counter_multiple_of_five() (runs: 1, calls: 20, reverts: 0) +... +"#]]); +}); + +// Tests check_interval via inline config annotation. +forgetest_init!(check_interval_inline_config, |prj, cmd| { + prj.add_test( + "CheckIntervalInlineTest.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract CounterHandler { + uint256 public counter; + + function increment() public { + counter++; + } +} + +contract CheckIntervalInlineTest is Test { + CounterHandler handler; + + function setUp() public { + handler = new CounterHandler(); + targetContract(address(handler)); + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 10 + /// forge-config: default.invariant.check_interval = 0 + function invariant_only_last_checked() public view { + // Only passes when counter is 0 or 10. With check_interval=0, only last call is checked. + require(handler.counter() == 0 || handler.counter() == 10, "not at boundary"); + } +} + "#, + ); + + cmd.args(["test", "--mt", "invariant_only_last_checked"]).assert_success().stdout_eq(str![[ + r#" +... +[PASS] invariant_only_last_checked() (runs: 1, calls: 10, reverts: 0) +... +"# + ]]); +}); diff --git a/crates/forge/tests/cli/test_cmd/invariant/target.rs b/crates/forge/tests/cli/test_cmd/invariant/target.rs index 41fadd3cb59d3..a2b766926bb09 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/target.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/target.rs @@ -627,6 +627,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); // Test TargetSelectors @@ -670,6 +672,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]], ); @@ -715,6 +719,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); // Test TargetArtifactSelectors @@ -763,6 +769,8 @@ Encountered a total of 1 failing tests, 1 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); diff --git a/crates/forge/tests/cli/test_cmd/logs.rs b/crates/forge/tests/cli/test_cmd/logs.rs index c9d1e39dc6155..0dba7fa8baa7c 100644 --- a/crates/forge/tests/cli/test_cmd/logs.rs +++ b/crates/forge/tests/cli/test_cmd/logs.rs @@ -752,3 +752,145 @@ Ran 1 test suite [ELAPSED]: 52 tests passed, 0 failed, 0 skipped (52 total tests "#]]); }); + +forgetest_init!(test_can_run_with_live_logs_flag, |prj, cmd| { + prj.add_test( + "Foo.t.sol", + r#" +import {Test, console} from "forge-std/Test.sol"; + +contract Foo is Test { + function setUp() pure public { + console.log("Setup"); + } + + function test1() pure public { + console.log("Test 1"); + } +} + "#, + ); + + cmd.forge_fuse() + .args(["test", "--live-logs", "--match-test", "test1"]) + .assert_success() + .stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Setup +Test 1 + +Ran 1 test for test/Foo.t.sol:Foo +[PASS] test1() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"#]]); +}); + +forgetest_init!(test_can_run_with_live_logs_config, |prj, cmd| { + prj.update_config(|config| { + config.live_logs = true; + }); + + prj.add_test( + "Foo.t.sol", + r#" +import {Test, console} from "forge-std/Test.sol"; + +contract Foo is Test { + function setUp() pure public { + console.log("Setup"); + } + + function test1() pure public { + console.log("Test 1"); + } +} + "#, + ); + + cmd.forge_fuse().args(["test", "--match-test", "test1"]).assert_success().stdout_eq(str![[ + r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Setup +Test 1 + +Ran 1 test for test/Foo.t.sol:Foo +[PASS] test1() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) + +"# + ]]); +}); + +forgetest_init!(test_can_run_with_live_logs_flag_race_condition, |prj, cmd| { + prj.add_test( + "Foo.t.sol", + r#" +import {Test, console} from "forge-std/Test.sol"; + +contract Foo is Test { + function setUp() pure public { + console.log("Setup"); + } + + function test1() pure public { + console.log("Test 1"); + } + + function test2() pure public { + console.log("Test 2"); + } +} + "#, + ); + + // Two threads. Inconsistent printing order. + cmd.forge_fuse().args(["test", "--live-logs", "--threads", "2"]).assert_success().stdout_eq( + str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! +Setup +Test [..] +Test [..] + +Ran 2 tests for test/Foo.t.sol:Foo +[PASS] test1() ([GAS]) +[PASS] test2() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]], + ); + + // Single thread. Deterministic printing order. + + for _ in 0..10 { + cmd.forge_fuse() + .args(["test", "--live-logs", "--threads", "1"]) + .assert_success() + .stdout_eq(str![[r#" +No files changed, compilation skipped +Setup +Test 1 +Test 2 + +Ran 2 tests for test/Foo.t.sol:Foo +[PASS] test1() ([GAS]) +[PASS] test2() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) + +"#]]); + } +}); diff --git a/crates/forge/tests/cli/test_cmd/mod.rs b/crates/forge/tests/cli/test_cmd/mod.rs index 0abffff57c4a8..8a2f0e2e7fafe 100644 --- a/crates/forge/tests/cli/test_cmd/mod.rs +++ b/crates/forge/tests/cli/test_cmd/mod.rs @@ -902,19 +902,21 @@ contract CounterTest is Test { Compiler run successful! Ran 1 test for test/CounterFuzz.t.sol:CounterTest -[FAIL: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe args=[115792089237316195423570985008687907853269984665640564039457584007913129639934 [1.157e77]]] testAddOne(uint256) (runs: 27, [AVG_GAS]) +[FAIL: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=[..] args=[..]] testAddOne(uint256) (runs: [..], [AVG_GAS]) Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) Failing tests: Encountered 1 failing test in test/CounterFuzz.t.sol:CounterTest -[FAIL: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=0xa76d58f5fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe args=[115792089237316195423570985008687907853269984665640564039457584007913129639934 [1.157e77]]] testAddOne(uint256) (runs: 27, [AVG_GAS]) +[FAIL: panic: arithmetic underflow or overflow (0x11); counterexample: calldata=[..] args=[..]] testAddOne(uint256) (runs: [..], [AVG_GAS]) Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -966,6 +968,8 @@ Encountered a total of 1 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 1 failed test +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -1420,10 +1424,10 @@ contract SimpleContractTest is Test { ... Traces: [..] SimpleContractTest::test() - ├─ [370554] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f - │ └─ ← [Return] 1737 bytes of code - ├─ [2511] SimpleContract::setStr("new value") - │ ├─ [1588] SimpleContract::_setStr("new value") + ├─ [..] → new SimpleContract@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] [..] bytes of code + ├─ [..] SimpleContract::setStr("new value") + │ ├─ [..] SimpleContract::_setStr("new value") │ │ └─ ← "initial value" │ └─ ← [Stop] └─ ← [Stop] @@ -2719,7 +2723,7 @@ Ran 8 tests for src/AssumeNoRevertTest.t.sol:ReverterTest "#]]); }); -forgetest_async!(can_get_broadcast_txs, |prj, cmd| { +forgetest_async!(flaky_can_get_broadcast_txs, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); let (_api, handle) = spawn(NodeConfig::test().silent()).await; @@ -2860,7 +2864,7 @@ forgetest_async!(can_get_broadcast_txs, |prj, cmd| { 31337 ); - assertEq(deployedAddress, address(0xD32c10E38A626Db0b0978B1A5828eb2957665668)); + assertGt(uint160(deployedAddress), 0); } function test_getDeployments() public { @@ -2870,8 +2874,10 @@ forgetest_async!(can_get_broadcast_txs, |prj, cmd| { ); assertEq(deployments.length, 2); - assertEq(deployments[0], address(0xD32c10E38A626Db0b0978B1A5828eb2957665668)); // Create2 address - latest deployment - assertEq(deployments[1], address(0x5FbDB2315678afecb367f032d93F642f64180aa3)); // Create address - oldest deployment + // Verify valid addresses returned and they're different (CREATE vs CREATE2) + assertGt(uint160(deployments[0]), 0); + assertGt(uint160(deployments[1]), 0); + assertTrue(deployments[0] != deployments[1]); } } "#; @@ -3400,7 +3406,7 @@ Traces: }); // -forgetest_init!(can_upload_selectors_with_path, |prj, cmd| { +forgetest_init!(flaky_can_upload_selectors_with_path, |prj, cmd| { prj.initialize_default_contracts(); prj.add_source( "CounterV1.sol", @@ -3865,7 +3871,7 @@ Ran 1 test suite [ELAPSED]: 2 tests passed, 0 failed, 0 skipped (2 total tests) }); // -forgetest_init!(should_not_panic_on_cool, |prj, cmd| { +forgetest_init!(flaky_should_not_panic_on_cool, |prj, cmd| { prj.initialize_default_contracts(); prj.add_test( "Counter.t.sol", @@ -4130,7 +4136,7 @@ Tip: Run `forge test --rerun` to retry only the 1 failed test // This test is a copy of `error_event_decode_with_cache` in cast/tests/cli/selectors.rs // but it uses `forge build` to check that the project selectors are cached by default. -forgetest_init!(build_with_selectors_cache, |prj, cmd| { +forgetest_init!(flaky_build_with_selectors_cache, |prj, cmd| { prj.initialize_default_contracts(); prj.add_source( "LocalProjectContract", @@ -4411,3 +4417,46 @@ Tip: Run `forge test --rerun` to retry only the 1 failed test "#]]); }); + +forgetest_init!(zero_runs, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "ZeroRuns.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Handler is Test { + function doSomething(uint256 param) public { + revert("unreachable"); + } +} + +contract ZeroRuns is Test { + Handler handler = new Handler(); + + /// forge-config: default.fuzz.runs = 0 + function test_fuzzZeroRuns(uint256 x) public { + revert("unreachable"); + } + + /// forge-config: default.invariant.runs = 0 + function invariant_zeroRuns() public {} + + /// forge-config: default.invariant.depth = 0 + function invariant_zeroDepth() public {} +} +"#, + ); + + cmd.args(["test"]).assert_success().stdout_eq(str![[r#" +... +Ran 3 tests for test/ZeroRuns.t.sol:ZeroRuns +[PASS] invariant_zeroDepth() (runs: 256, calls: 0, reverts: 0) +[PASS] invariant_zeroRuns() (runs: 0, calls: 0, reverts: 0) +[PASS] test_fuzzZeroRuns(uint256) (runs: 0, [AVG_GAS]) +Suite result: ok. 3 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test suite [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/cli/test_cmd/repros.rs b/crates/forge/tests/cli/test_cmd/repros.rs index 32c042f32d050..82ec6744a6172 100644 --- a/crates/forge/tests/cli/test_cmd/repros.rs +++ b/crates/forge/tests/cli/test_cmd/repros.rs @@ -63,6 +63,8 @@ Encountered a total of 3 failing tests, 0 tests succeeded Tip: Run `forge test --rerun` to retry only the 3 failed tests +[SEED] (use `--fuzz-seed` to reproduce) + "#]]); }); @@ -780,3 +782,99 @@ ParserError: Source "Missing.sol" not found: File not found. Searched the follow "#]]); }); + +// https://github.com/foundry-rs/foundry/issues/12803 +// Test gas underflow prevention on Cancun (no EIP-7702 gas floor) +forgetest_init!(issue_12803_cancun, |prj, cmd| { + prj.add_test( + "Issue12803.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Issue12803Test is Test { + uint a; + function test_negativeGas() public { + vm.pauseGasMetering(); + a = 100; + vm.resumeGasMetering(); + delete a; + } +} +"#, + ); + + cmd.args(["test", "--evm-version=cancun"]).with_no_redact().assert_success().stdout_eq(str![[ + r#" +... +Ran 1 test for test/Issue12803.t.sol:Issue12803Test +[PASS] test_negativeGas() (gas: 0) +... +"# + ]]); +}); + +// https://github.com/foundry-rs/foundry/issues/12803 +// Test gas underflow prevention on Shanghai (also no EIP-7702 gas floor) +forgetest_init!(issue_12803_shanghai, |prj, cmd| { + prj.add_test( + "Issue12803.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Issue12803Test is Test { + uint a; + function test_negativeGas() public { + vm.pauseGasMetering(); + a = 100; + vm.resumeGasMetering(); + delete a; + } +} +"#, + ); + + cmd.args(["test", "--evm-version=shanghai"]).with_no_redact().assert_success().stdout_eq(str![ + [r#" +... +Ran 1 test for test/Issue12803.t.sol:Issue12803Test +[PASS] test_negativeGas() (gas: 0) +... +"#] + ]); +}); + +// https://github.com/foundry-rs/foundry/issues/12803 +// Test multiple storage deletions (higher refund) don't cause underflow +forgetest_init!(issue_12803_multiple_deletes, |prj, cmd| { + prj.add_test( + "Issue12803Multi.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Issue12803MultiTest is Test { + uint a; + uint b; + uint c; + function test_multipleDeletes() public { + vm.pauseGasMetering(); + a = 100; + b = 200; + c = 300; + vm.resumeGasMetering(); + delete a; + delete b; + delete c; + } +} +"#, + ); + + cmd.args(["test", "--evm-version=cancun"]).with_no_redact().assert_success().stdout_eq(str![[ + r#" +... +Ran 1 test for test/Issue12803Multi.t.sol:Issue12803MultiTest +[PASS] test_multipleDeletes() (gas: 0) +... +"# + ]]); +}); diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index b5dbc0919d5c1..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1057,7 +1057,7 @@ Compiling 1 files with [..] }); // Test preprocessing contracts with payable constructor, value and salt named args. -forgetest_init!(preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { +forgetest_init!(flaky_preprocess_contracts_with_payable_constructor_and_salt, |prj, cmd| { prj.update_config(|config| { config.dynamic_test_linking = true; }); @@ -1119,7 +1119,9 @@ contract CounterTest is Test { function test_Increment_In_Counter_With_Salt() public { CounterWithSalt counter = new CounterWithSalt{value: 111, salt: bytes32("preprocess_counter_with_salt")}(1); - assertEq(address(counter), 0x223e63BE3BF01DD04f852d70f1bE217017055f49); + assertGt(uint160(address(counter)), 0); + counter.increment(); + assertEq(counter.number(), 112); } } "#, @@ -1194,7 +1196,7 @@ contract CounterWithSalt { Compiling 1 files with [..] ... [FAIL: assertion failed: 113 != 112] test_Increment_In_Counter() (gas: [..]) -[FAIL: assertion failed: 0x11acEfcD29A1BA964A05C0E7F3901054BEfb17c0 != 0x223e63BE3BF01DD04f852d70f1bE217017055f49] test_Increment_In_Counter_With_Salt() (gas: [..]) +[FAIL: assertion failed: 113 != 112] test_Increment_In_Counter_With_Salt() (gas: [..]) ... "#]]); diff --git a/crates/forge/tests/cli/verify_bytecode.rs b/crates/forge/tests/cli/verify_bytecode.rs index 4e0f3c1862715..c8368f324f2c8 100644 --- a/crates/forge/tests/cli/verify_bytecode.rs +++ b/crates/forge/tests/cli/verify_bytecode.rs @@ -1,13 +1,16 @@ +use alloy_chains::Chain; use foundry_compilers::artifacts::{BytecodeHash, EvmVersion}; use foundry_config::Config; use foundry_test_utils::{ - TestCommand, TestProject, forgetest_async, + TestCommand, TestProject, + etherscan::fetch_etherscan_source_flattened, + forgetest_async, rpc::{next_etherscan_api_key, next_http_archive_rpc_url}, util::OutputExt, }; #[expect(clippy::too_many_arguments)] -fn test_verify_bytecode( +async fn test_verify_bytecode( prj: TestProject, mut cmd: TestCommand, addr: &str, @@ -17,17 +20,15 @@ fn test_verify_bytecode( verifier: &str, verifier_url: &str, expected_matches: (&str, &str), + chain: Chain, ) { let etherscan_key = next_etherscan_api_key(); let rpc_url = next_http_archive_rpc_url(); - // fetch and flatten source code - let source_code = cmd - .cast_fuse() - .args(["source", addr, "--flatten", "--etherscan-api-key", ðerscan_key]) - .assert_success() - .get_output() - .stdout_lossy(); + // fetch and flatten source code using the library directly + let source_code = fetch_etherscan_source_flattened(addr, ðerscan_key, chain) + .await + .expect("failed to fetch source code from etherscan"); prj.add_source(contract_name, &source_code); prj.write_config(config); @@ -65,7 +66,7 @@ fn test_verify_bytecode( } #[expect(clippy::too_many_arguments)] -fn test_verify_bytecode_with_ignore( +async fn test_verify_bytecode_with_ignore( prj: TestProject, mut cmd: TestCommand, addr: &str, @@ -75,26 +76,15 @@ fn test_verify_bytecode_with_ignore( verifier_url: &str, expected_matches: (&str, &str), ignore: &str, - chain: &str, + chain: Chain, ) { let etherscan_key = next_etherscan_api_key(); let rpc_url = next_http_archive_rpc_url(); - // fetch and flatten source code - let source_code = cmd - .cast_fuse() - .args([ - "source", - addr, - "--flatten", - "--etherscan-api-key", - ðerscan_key, - "--chain", - chain, - ]) - .assert_success() - .get_output() - .stdout_lossy(); + // fetch and flatten source code using the library directly + let source_code = fetch_etherscan_source_flattened(addr, ðerscan_key, chain) + .await + .expect("failed to fetch source code from etherscan"); prj.add_source(contract_name, &source_code); prj.write_config(config); @@ -145,221 +135,197 @@ fn test_verify_bytecode_with_ignore( } } -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_verify_bytecode_no_metadata, - |prj, cmd| { - test_verify_bytecode( - prj, - cmd, - "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", - "SystemConfig", - None, - Config { - evm_version: EvmVersion::London, - optimizer_runs: Some(999999), - optimizer: Some(true), - cbor_metadata: false, - bytecode_hash: BytecodeHash::None, - ..Default::default() - }, - "etherscan", - "https://api.etherscan.io/v2/api?chainid=1", - ("partial", "partial"), - ); - } -); +forgetest_async!(flaky_verify_bytecode_no_metadata, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + None, + Config { + evm_version: EvmVersion::London, + optimizer_runs: Some(999999), + optimizer: Some(true), + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/v2/api?chainid=1", + ("partial", "partial"), + Chain::mainnet(), + ) + .await; +}); -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_verify_bytecode_with_metadata, - |prj, cmd| { - test_verify_bytecode( - prj, - cmd, - "0xb8901acb165ed027e32754e0ffe830802919727f", - "L1_ETH_Bridge", - None, - Config { - evm_version: EvmVersion::Paris, - optimizer_runs: Some(50000), - optimizer: Some(true), - ..Default::default() - }, - "etherscan", - "https://api.etherscan.io/v2/api?chainid=1", - ("partial", "partial"), - ); - } -); +forgetest_async!(flaky_verify_bytecode_with_metadata, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0xb8901acb165ed027e32754e0ffe830802919727f", + "L1_ETH_Bridge", + None, + Config { + evm_version: EvmVersion::Paris, + optimizer_runs: Some(50000), + optimizer: Some(true), + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/v2/api?chainid=1", + ("partial", "partial"), + Chain::mainnet(), + ) + .await; +}); // Test non-CREATE2 deployed contract with blockscout -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_verify_bytecode_with_blockscout, - |prj, cmd| { - test_verify_bytecode( - prj, - cmd, - "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b", - "StrategyManager", - None, - Config { - evm_version: EvmVersion::London, - optimizer: Some(true), - optimizer_runs: Some(200), - ..Default::default() - }, - "blockscout", - "https://eth.blockscout.com/api", - ("partial", "partial"), - ); - } -); +forgetest_async!(flaky_verify_bytecode_with_blockscout, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b", + "StrategyManager", + None, + Config { + evm_version: EvmVersion::London, + optimizer: Some(true), + optimizer_runs: Some(200), + ..Default::default() + }, + "blockscout", + "https://eth.blockscout.com/api", + ("partial", "partial"), + Chain::mainnet(), + ) + .await; +}); // Test CREATE2 deployed contract with blockscout -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_vb_create2_with_blockscout, - |prj, cmd| { - test_verify_bytecode( - prj, - cmd, - "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", - "SystemConfig", - None, - Config { - evm_version: EvmVersion::London, - optimizer_runs: Some(999999), - optimizer: Some(true), - cbor_metadata: false, - bytecode_hash: BytecodeHash::None, - ..Default::default() - }, - "blockscout", - "https://eth.blockscout.com/api", - ("partial", "partial"), - ); - } -); +forgetest_async!(flaky_verify_bytecode_create2_with_blockscout, |prj, cmd| { + test_verify_bytecode( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + None, + Config { + evm_version: EvmVersion::London, + optimizer_runs: Some(999999), + optimizer: Some(true), + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + "blockscout", + "https://eth.blockscout.com/api", + ("partial", "partial"), + Chain::mainnet(), + ) + .await; +}); // Test `--constructor-args` -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_verify_bytecode_with_constructor_args, - |prj, cmd| { - let constructor_args = vec![ - "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", - "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", - "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", - ]; - test_verify_bytecode( - prj, - cmd, - "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b", - "StrategyManager", - Some(constructor_args), - Config { - evm_version: EvmVersion::London, - optimizer: Some(true), - optimizer_runs: Some(200), - ..Default::default() - }, - "etherscan", - "https://api.etherscan.io/v2/api?chainid=1", - ("partial", "partial"), - ); - } -); +forgetest_async!(flaky_verify_bytecode_with_constructor_args, |prj, cmd| { + let constructor_args = vec![ + "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", + "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", + "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", + ]; + test_verify_bytecode( + prj, + cmd, + "0x70f44C13944d49a236E3cD7a94f48f5daB6C619b", + "StrategyManager", + Some(constructor_args), + Config { + evm_version: EvmVersion::London, + optimizer: Some(true), + optimizer_runs: Some(200), + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/v2/api?chainid=1", + ("partial", "partial"), + Chain::mainnet(), + ) + .await; +}); // `--ignore` tests -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_ignore_creation, - |prj, cmd| { - test_verify_bytecode_with_ignore( - prj, - cmd, - "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", - "SystemConfig", - Config { - evm_version: EvmVersion::London, - optimizer_runs: Some(999999), - optimizer: Some(true), - cbor_metadata: false, - bytecode_hash: BytecodeHash::None, - ..Default::default() - }, - "etherscan", - "https://api.etherscan.io/v2/api?chainid=1", - ("ignored", "partial"), - "creation", - "1", - ); - } -); - -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_ignore_runtime, - |prj, cmd| { - test_verify_bytecode_with_ignore( - prj, - cmd, - "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", - "SystemConfig", - Config { - evm_version: EvmVersion::London, - optimizer_runs: Some(999999), - optimizer: Some(true), - cbor_metadata: false, - bytecode_hash: BytecodeHash::None, - ..Default::default() - }, - "etherscan", - "https://api.etherscan.io/v2/api?chainid=1", - ("partial", "ignored"), - "runtime", - "1", - ); - } -); - -// Test that verification fails when source code doesn't match deployed bytecode -forgetest_async!( - #[ignore = "flaky due to rate limits"] - can_verify_bytecode_fails_on_source_mismatch, - |prj, cmd| { - let etherscan_key = next_etherscan_api_key(); - let rpc_url = next_http_archive_rpc_url(); - - // Fetch real source code - let real_source = cmd - .cast_fuse() - .args([ - "source", - "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", - "--flatten", - "--etherscan-api-key", - ðerscan_key, - ]) - .assert_success() - .get_output() - .stdout_lossy(); +forgetest_async!(flaky_verify_bytecode_can_ignore_creation, |prj, cmd| { + test_verify_bytecode_with_ignore( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + Config { + evm_version: EvmVersion::London, + optimizer_runs: Some(999999), + optimizer: Some(true), + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }, + "etherscan", + "https://api.etherscan.io/v2/api?chainid=1", + ("ignored", "partial"), + "creation", + Chain::mainnet(), + ) + .await; +}); - prj.add_source("SystemConfig", &real_source); - prj.write_config(Config { +forgetest_async!(flaky_verify_bytecode_can_ignore_runtime, |prj, cmd| { + test_verify_bytecode_with_ignore( + prj, + cmd, + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + Config { evm_version: EvmVersion::London, optimizer_runs: Some(999999), optimizer: Some(true), cbor_metadata: false, bytecode_hash: BytecodeHash::None, ..Default::default() - }); - // Build once with correct source (creates cache) - cmd.forge_fuse().arg("build").assert_success(); + }, + "etherscan", + "https://api.etherscan.io/v2/api?chainid=1", + ("partial", "ignored"), + "runtime", + Chain::mainnet(), + ) + .await; +}); + +// Test that verification fails when source code doesn't match deployed bytecode +forgetest_async!(flaky_can_verify_bytecode_fails_on_source_mismatch, |prj, cmd| { + let etherscan_key = next_etherscan_api_key(); + let rpc_url = next_http_archive_rpc_url(); - let source_code = r#" + // Fetch real source code using the library directly + let real_source = fetch_etherscan_source_flattened( + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + ðerscan_key, + Chain::mainnet(), + ) + .await + .expect("failed to fetch source code from etherscan"); + + prj.add_source("SystemConfig", &real_source); + prj.write_config(Config { + evm_version: EvmVersion::London, + optimizer_runs: Some(999999), + optimizer: Some(true), + cbor_metadata: false, + bytecode_hash: BytecodeHash::None, + ..Default::default() + }); + // Build once with correct source (creates cache) + cmd.forge_fuse().arg("build").assert_success(); + + let source_code = r#" contract SystemConfig { uint256 public constant MODIFIED_VALUE = 999; @@ -369,28 +335,28 @@ forgetest_async!( } "#; - // Now replace with different incorrect source code - prj.add_source("SystemConfig", source_code); - let args = vec![ - "verify-bytecode", - "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", - "SystemConfig", - "--etherscan-api-key", - ðerscan_key, - "--verifier", - "etherscan", - "--verifier-url", - "https://api.etherscan.io/v2/api?chainid=1", - "--rpc-url", - &rpc_url, - ]; - let output = cmd.forge_fuse().args(args).assert_success().get_output().stderr_lossy(); + // Now replace with different incorrect source code + prj.add_source("SystemConfig", source_code); + let etherscan_key = next_etherscan_api_key(); + let args = vec![ + "verify-bytecode", + "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1", + "SystemConfig", + "--etherscan-api-key", + ðerscan_key, + "--verifier", + "etherscan", + "--verifier-url", + "https://api.etherscan.io/v2/api?chainid=1", + "--rpc-url", + &rpc_url, + ]; + let output = cmd.forge_fuse().args(args).assert_success().get_output().stderr_lossy(); - // Verify that bytecode does NOT match (recompiled with incorrect source) - assert!(output.contains("Error: Creation code did not match".to_string().as_str())); - assert!(output.contains("Error: Runtime code did not match".to_string().as_str())); - } -); + // Verify that bytecode does NOT match (recompiled with incorrect source) + assert!(output.contains("Error: Creation code did not match".to_string().as_str())); + assert!(output.contains("Error: Runtime code did not match".to_string().as_str())); +}); // Test predeploy contracts // TODO: Add test utils for base such as basescan keys and alchemy keys. @@ -413,6 +379,6 @@ forgetest_async!( // "https://api.basescan.org/api", // ("ignored", "partial"), // "creation", -// "base", -// ); +// Chain::base_mainnet(), +// ).await; // }); diff --git a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json index 96fb5dffe1fa4..244a2d94fc7eb 100644 --- a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json +++ b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json @@ -45,6 +45,7 @@ "output": "{...}", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Return", "steps": [], "decoded": null @@ -78,6 +79,7 @@ "output": "0x", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Stop", "steps": [ { @@ -970,7 +972,7 @@ "0x142" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -992,7 +994,7 @@ "0x1c2" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1014,7 +1016,7 @@ "0x267" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1030,7 +1032,7 @@ "op": 91, "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2"], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1046,7 +1048,7 @@ "op": 96, "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2"], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1062,7 +1064,7 @@ "op": 81, "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2", "0x40"], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1078,7 +1080,7 @@ "op": 128, "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2", "0x80"], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1101,7 +1103,7 @@ "0x80" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1124,7 +1126,7 @@ "0x1c2" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1140,7 +1142,7 @@ "op": 144, "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x80", "0x142"], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1156,7 +1158,7 @@ "op": 95, "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x142", "0x80"], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1179,7 +1181,7 @@ "0x0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1200,7 +1202,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1222,7 +1224,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629525, "gas_refund_counter": 0, @@ -1244,7 +1246,7 @@ "0x0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629522, "gas_refund_counter": 0, @@ -1267,7 +1269,7 @@ "0x0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629519, "gas_refund_counter": 0, @@ -1290,7 +1292,7 @@ "0x1" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629516, "gas_refund_counter": 0, @@ -1314,7 +1316,7 @@ "0x280" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629513, "gas_refund_counter": 0, @@ -1336,7 +1338,7 @@ "0x0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629503, "gas_refund_counter": 0, @@ -1358,7 +1360,7 @@ "0x0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629502, "gas_refund_counter": 0, @@ -1379,7 +1381,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629500, "gas_refund_counter": 0, @@ -1400,7 +1402,7 @@ "0x0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629497, "gas_refund_counter": 0, @@ -1420,7 +1422,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629495, "gas_refund_counter": 0, @@ -1441,7 +1443,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629492, "gas_refund_counter": 0, @@ -1463,7 +1465,7 @@ "0xffffffffffffffffffffffffffffffffffffffff" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629489, "gas_refund_counter": 0, @@ -1484,7 +1486,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629486, "gas_refund_counter": 0, @@ -1506,7 +1508,7 @@ "0xe26d1474" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629483, "gas_refund_counter": 0, @@ -1529,7 +1531,7 @@ "0x64" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629480, "gas_refund_counter": 0, @@ -1553,7 +1555,7 @@ "0x40" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629477, "gas_refund_counter": 0, @@ -1577,7 +1579,7 @@ "0x80" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629474, "gas_refund_counter": 0, @@ -1602,7 +1604,7 @@ "0xe26d1474" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629471, "gas_refund_counter": 0, @@ -1628,7 +1630,7 @@ "0xffffffff" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629468, "gas_refund_counter": 0, @@ -1653,7 +1655,7 @@ "0xe26d1474" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629465, "gas_refund_counter": 0, @@ -1679,7 +1681,7 @@ "0xe0" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629462, "gas_refund_counter": 0, @@ -1704,7 +1706,7 @@ "0xe26d147400000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629459, "gas_refund_counter": 0, @@ -1730,7 +1732,7 @@ "0x80" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629456, "gas_refund_counter": 0, @@ -1754,7 +1756,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629453, "gas_refund_counter": 0, @@ -1779,7 +1781,7 @@ "0x4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629450, "gas_refund_counter": 0, @@ -1803,7 +1805,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629447, "gas_refund_counter": 0, @@ -1828,7 +1830,7 @@ "0x2bd" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629444, "gas_refund_counter": 0, @@ -1853,7 +1855,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629441, "gas_refund_counter": 0, @@ -1878,7 +1880,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629438, "gas_refund_counter": 0, @@ -1904,7 +1906,7 @@ "0x651" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629435, "gas_refund_counter": 0, @@ -1929,7 +1931,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629427, "gas_refund_counter": 0, @@ -1954,7 +1956,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629426, "gas_refund_counter": 0, @@ -1980,7 +1982,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629424, "gas_refund_counter": 0, @@ -2007,7 +2009,7 @@ "0x20" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629421, "gas_refund_counter": 0, @@ -2035,7 +2037,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629418, "gas_refund_counter": 0, @@ -2062,7 +2064,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629415, "gas_refund_counter": 0, @@ -2089,7 +2091,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629412, "gas_refund_counter": 0, @@ -2115,7 +2117,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629410, "gas_refund_counter": 0, @@ -2142,7 +2144,7 @@ "0x664" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629407, "gas_refund_counter": 0, @@ -2170,7 +2172,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629405, "gas_refund_counter": 0, @@ -2199,7 +2201,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629402, "gas_refund_counter": 0, @@ -2227,7 +2229,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629399, "gas_refund_counter": 0, @@ -2256,7 +2258,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629396, "gas_refund_counter": 0, @@ -2286,7 +2288,7 @@ "0x642" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629393, "gas_refund_counter": 0, @@ -2315,7 +2317,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629385, "gas_refund_counter": 0, @@ -2344,7 +2346,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629384, "gas_refund_counter": 0, @@ -2374,7 +2376,7 @@ "0x64b" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629381, "gas_refund_counter": 0, @@ -2405,7 +2407,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629378, "gas_refund_counter": 0, @@ -2437,7 +2439,7 @@ "0x621" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629375, "gas_refund_counter": 0, @@ -2468,7 +2470,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629367, "gas_refund_counter": 0, @@ -2499,7 +2501,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629366, "gas_refund_counter": 0, @@ -2531,7 +2533,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629364, "gas_refund_counter": 0, @@ -2564,7 +2566,7 @@ "0x63b" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629361, "gas_refund_counter": 0, @@ -2598,7 +2600,7 @@ "0x636" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629358, "gas_refund_counter": 0, @@ -2633,7 +2635,7 @@ "0x631" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629355, "gas_refund_counter": 0, @@ -2669,7 +2671,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629352, "gas_refund_counter": 0, @@ -2706,7 +2708,7 @@ "0x606" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629349, "gas_refund_counter": 0, @@ -2742,7 +2744,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629341, "gas_refund_counter": 0, @@ -2778,7 +2780,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629340, "gas_refund_counter": 0, @@ -2815,7 +2817,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629338, "gas_refund_counter": 0, @@ -2853,7 +2855,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629335, "gas_refund_counter": 0, @@ -2891,7 +2893,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629332, "gas_refund_counter": 0, @@ -2928,7 +2930,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629330, "gas_refund_counter": 0, @@ -2965,7 +2967,7 @@ "0x631" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629327, "gas_refund_counter": 0, @@ -3002,7 +3004,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629324, "gas_refund_counter": 0, @@ -3038,7 +3040,7 @@ "0x631" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629322, "gas_refund_counter": 0, @@ -3073,7 +3075,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629314, "gas_refund_counter": 0, @@ -3108,7 +3110,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629313, "gas_refund_counter": 0, @@ -3144,7 +3146,7 @@ "0x618" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629310, "gas_refund_counter": 0, @@ -3179,7 +3181,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629302, "gas_refund_counter": 0, @@ -3214,7 +3216,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629301, "gas_refund_counter": 0, @@ -3250,7 +3252,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629299, "gas_refund_counter": 0, @@ -3287,7 +3289,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629296, "gas_refund_counter": 0, @@ -3324,7 +3326,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629293, "gas_refund_counter": 0, @@ -3360,7 +3362,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629291, "gas_refund_counter": 0, @@ -3396,7 +3398,7 @@ "0x636" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629288, "gas_refund_counter": 0, @@ -3432,7 +3434,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629285, "gas_refund_counter": 0, @@ -3467,7 +3469,7 @@ "0x636" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629283, "gas_refund_counter": 0, @@ -3501,7 +3503,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629275, "gas_refund_counter": 0, @@ -3535,7 +3537,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629274, "gas_refund_counter": 0, @@ -3570,7 +3572,7 @@ "0x60f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629271, "gas_refund_counter": 0, @@ -3604,7 +3606,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629263, "gas_refund_counter": 0, @@ -3638,7 +3640,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629262, "gas_refund_counter": 0, @@ -3673,7 +3675,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629260, "gas_refund_counter": 0, @@ -3709,7 +3711,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629257, "gas_refund_counter": 0, @@ -3745,7 +3747,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629254, "gas_refund_counter": 0, @@ -3780,7 +3782,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629252, "gas_refund_counter": 0, @@ -3815,7 +3817,7 @@ "0x63b" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629249, "gas_refund_counter": 0, @@ -3850,7 +3852,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629246, "gas_refund_counter": 0, @@ -3884,7 +3886,7 @@ "0x63b" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629244, "gas_refund_counter": 0, @@ -3917,7 +3919,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629236, "gas_refund_counter": 0, @@ -3950,7 +3952,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629235, "gas_refund_counter": 0, @@ -3983,7 +3985,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629232, "gas_refund_counter": 0, @@ -4015,7 +4017,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629230, "gas_refund_counter": 0, @@ -4047,7 +4049,7 @@ "0x64b" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629227, "gas_refund_counter": 0, @@ -4079,7 +4081,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629224, "gas_refund_counter": 0, @@ -4110,7 +4112,7 @@ "0x64b" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629222, "gas_refund_counter": 0, @@ -4140,7 +4142,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629214, "gas_refund_counter": 0, @@ -4170,7 +4172,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629213, "gas_refund_counter": 0, @@ -4201,7 +4203,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629210, "gas_refund_counter": 0, @@ -4230,7 +4232,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629207, "gas_refund_counter": 0, @@ -4258,7 +4260,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629205, "gas_refund_counter": 0, @@ -4285,7 +4287,7 @@ "0x664" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629203, "gas_refund_counter": 0, @@ -4311,7 +4313,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629195, "gas_refund_counter": 0, @@ -4337,7 +4339,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629194, "gas_refund_counter": 0, @@ -4363,7 +4365,7 @@ "0x2bd" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629191, "gas_refund_counter": 0, @@ -4389,7 +4391,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629188, "gas_refund_counter": 0, @@ -4414,7 +4416,7 @@ "0x84" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629186, "gas_refund_counter": 0, @@ -4438,7 +4440,7 @@ "0x2bd" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629184, "gas_refund_counter": 0, @@ -4461,7 +4463,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629176, "gas_refund_counter": 0, @@ -4484,7 +4486,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629175, "gas_refund_counter": 0, @@ -4508,7 +4510,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629173, "gas_refund_counter": 0, @@ -4533,7 +4535,7 @@ "0x40" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629170, "gas_refund_counter": 0, @@ -4558,7 +4560,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629167, "gas_refund_counter": 0, @@ -4584,7 +4586,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629164, "gas_refund_counter": 0, @@ -4611,7 +4613,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629161, "gas_refund_counter": 0, @@ -4637,7 +4639,7 @@ "0x24" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629158, "gas_refund_counter": 0, @@ -4664,7 +4666,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629155, "gas_refund_counter": 0, @@ -4692,7 +4694,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629153, "gas_refund_counter": 0, @@ -4721,7 +4723,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629150, "gas_refund_counter": 0, @@ -4751,7 +4753,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629147, "gas_refund_counter": 0, @@ -4781,7 +4783,7 @@ "0x126" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629047, "gas_refund_counter": 0, @@ -4811,7 +4813,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629044, "gas_refund_counter": 0, @@ -4842,7 +4844,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629041, "gas_refund_counter": 0, @@ -4873,7 +4875,7 @@ "0x1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629038, "gas_refund_counter": 0, @@ -4905,7 +4907,7 @@ "0x2d4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629035, "gas_refund_counter": 0, @@ -4935,7 +4937,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629025, "gas_refund_counter": 0, @@ -4965,7 +4967,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629024, "gas_refund_counter": 0, @@ -4994,7 +4996,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629022, "gas_refund_counter": 0, @@ -5024,7 +5026,7 @@ "0x3ffe475c" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073629020, "gas_refund_counter": 0, @@ -5048,7 +5050,7 @@ "0x1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606406, "gas_refund_counter": 0, @@ -5072,7 +5074,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606403, "gas_refund_counter": 0, @@ -5097,7 +5099,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606400, "gas_refund_counter": 0, @@ -5122,7 +5124,7 @@ "0x1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606397, "gas_refund_counter": 0, @@ -5148,7 +5150,7 @@ "0x2e6" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606394, "gas_refund_counter": 0, @@ -5172,7 +5174,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606384, "gas_refund_counter": 0, @@ -5196,7 +5198,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606383, "gas_refund_counter": 0, @@ -5219,7 +5221,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606381, "gas_refund_counter": 0, @@ -5241,7 +5243,7 @@ "0xe26d1474" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606379, "gas_refund_counter": 0, @@ -5262,7 +5264,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606377, "gas_refund_counter": 0, @@ -5282,7 +5284,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606375, "gas_refund_counter": 0, @@ -5303,7 +5305,7 @@ "0x2f4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606372, "gas_refund_counter": 0, @@ -5325,7 +5327,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606369, "gas_refund_counter": 0, @@ -5348,7 +5350,7 @@ "0x32e" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606366, "gas_refund_counter": 0, @@ -5370,7 +5372,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606358, "gas_refund_counter": 0, @@ -5392,7 +5394,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606357, "gas_refund_counter": 0, @@ -5415,7 +5417,7 @@ "0x3c4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606354, "gas_refund_counter": 0, @@ -5439,7 +5441,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606351, "gas_refund_counter": 0, @@ -5464,7 +5466,7 @@ "0x40" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606348, "gas_refund_counter": 0, @@ -5489,7 +5491,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606345, "gas_refund_counter": 0, @@ -5515,7 +5517,7 @@ "0x24" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606342, "gas_refund_counter": 0, @@ -5540,7 +5542,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606339, "gas_refund_counter": 0, @@ -5566,7 +5568,7 @@ "0x342" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606336, "gas_refund_counter": 0, @@ -5592,7 +5594,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606333, "gas_refund_counter": 0, @@ -5618,7 +5620,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606330, "gas_refund_counter": 0, @@ -5645,7 +5647,7 @@ "0x679" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606327, "gas_refund_counter": 0, @@ -5671,7 +5673,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606319, "gas_refund_counter": 0, @@ -5697,7 +5699,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606318, "gas_refund_counter": 0, @@ -5724,7 +5726,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606316, "gas_refund_counter": 0, @@ -5752,7 +5754,7 @@ "0x20" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606313, "gas_refund_counter": 0, @@ -5781,7 +5783,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606310, "gas_refund_counter": 0, @@ -5809,7 +5811,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606307, "gas_refund_counter": 0, @@ -5837,7 +5839,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606304, "gas_refund_counter": 0, @@ -5864,7 +5866,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606302, "gas_refund_counter": 0, @@ -5892,7 +5894,7 @@ "0x68c" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606299, "gas_refund_counter": 0, @@ -5921,7 +5923,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606297, "gas_refund_counter": 0, @@ -5951,7 +5953,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606294, "gas_refund_counter": 0, @@ -5980,7 +5982,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606291, "gas_refund_counter": 0, @@ -6010,7 +6012,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606288, "gas_refund_counter": 0, @@ -6041,7 +6043,7 @@ "0x66a" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606285, "gas_refund_counter": 0, @@ -6071,7 +6073,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606277, "gas_refund_counter": 0, @@ -6101,7 +6103,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606276, "gas_refund_counter": 0, @@ -6132,7 +6134,7 @@ "0x673" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606273, "gas_refund_counter": 0, @@ -6164,7 +6166,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606270, "gas_refund_counter": 0, @@ -6197,7 +6199,7 @@ "0x60f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606267, "gas_refund_counter": 0, @@ -6229,7 +6231,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606259, "gas_refund_counter": 0, @@ -6261,7 +6263,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606258, "gas_refund_counter": 0, @@ -6294,7 +6296,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606256, "gas_refund_counter": 0, @@ -6328,7 +6330,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606253, "gas_refund_counter": 0, @@ -6362,7 +6364,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606250, "gas_refund_counter": 0, @@ -6395,7 +6397,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606248, "gas_refund_counter": 0, @@ -6428,7 +6430,7 @@ "0x673" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606245, "gas_refund_counter": 0, @@ -6461,7 +6463,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606242, "gas_refund_counter": 0, @@ -6493,7 +6495,7 @@ "0x673" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606240, "gas_refund_counter": 0, @@ -6524,7 +6526,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606232, "gas_refund_counter": 0, @@ -6555,7 +6557,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606231, "gas_refund_counter": 0, @@ -6587,7 +6589,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606228, "gas_refund_counter": 0, @@ -6617,7 +6619,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606225, "gas_refund_counter": 0, @@ -6646,7 +6648,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606223, "gas_refund_counter": 0, @@ -6674,7 +6676,7 @@ "0x68c" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606221, "gas_refund_counter": 0, @@ -6701,7 +6703,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606213, "gas_refund_counter": 0, @@ -6728,7 +6730,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606212, "gas_refund_counter": 0, @@ -6755,7 +6757,7 @@ "0x342" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606209, "gas_refund_counter": 0, @@ -6782,7 +6784,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606206, "gas_refund_counter": 0, @@ -6808,7 +6810,7 @@ "0xa4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606204, "gas_refund_counter": 0, @@ -6833,7 +6835,7 @@ "0x342" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606202, "gas_refund_counter": 0, @@ -6857,7 +6859,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606194, "gas_refund_counter": 0, @@ -6881,7 +6883,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606193, "gas_refund_counter": 0, @@ -6906,7 +6908,7 @@ "0x40" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606190, "gas_refund_counter": 0, @@ -6931,7 +6933,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606187, "gas_refund_counter": 0, @@ -6957,7 +6959,7 @@ "0x20" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606184, "gas_refund_counter": 0, @@ -6984,7 +6986,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606181, "gas_refund_counter": 0, @@ -7012,7 +7014,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606178, "gas_refund_counter": 0, @@ -7039,7 +7041,7 @@ "0x44" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606175, "gas_refund_counter": 0, @@ -7065,7 +7067,7 @@ "0x24" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606172, "gas_refund_counter": 0, @@ -7092,7 +7094,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606169, "gas_refund_counter": 0, @@ -7117,7 +7119,7 @@ "0x80" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606166, "gas_refund_counter": 0, @@ -7142,7 +7144,7 @@ "0xc4" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606163, "gas_refund_counter": 0, @@ -7168,7 +7170,7 @@ "0x40" ], "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606160, "gas_refund_counter": 0, @@ -7192,7 +7194,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606157, "gas_refund_counter": 0, @@ -7217,7 +7219,7 @@ "0xf82c50f100000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606154, "gas_refund_counter": 0, @@ -7243,7 +7245,7 @@ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606151, "gas_refund_counter": 0, @@ -7269,7 +7271,7 @@ "0xffffffff00000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606148, "gas_refund_counter": 0, @@ -7294,7 +7296,7 @@ "0xf82c50f100000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606145, "gas_refund_counter": 0, @@ -7320,7 +7322,7 @@ "0x20" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606142, "gas_refund_counter": 0, @@ -7347,7 +7349,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606139, "gas_refund_counter": 0, @@ -7373,7 +7375,7 @@ "0xa0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606136, "gas_refund_counter": 0, @@ -7400,7 +7402,7 @@ "0xa0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606133, "gas_refund_counter": 0, @@ -7427,7 +7429,7 @@ "0x6400000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606130, "gas_refund_counter": 0, @@ -7455,7 +7457,7 @@ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606127, "gas_refund_counter": 0, @@ -7484,7 +7486,7 @@ "0xf82c50f100000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606124, "gas_refund_counter": 0, @@ -7514,7 +7516,7 @@ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606121, "gas_refund_counter": 0, @@ -7545,7 +7547,7 @@ "0x6400000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606118, "gas_refund_counter": 0, @@ -7575,7 +7577,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606115, "gas_refund_counter": 0, @@ -7604,7 +7606,7 @@ "0xf82c50f100000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606112, "gas_refund_counter": 0, @@ -7634,7 +7636,7 @@ "0xa0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606109, "gas_refund_counter": 0, @@ -7662,7 +7664,7 @@ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606106, "gas_refund_counter": 0, @@ -7689,7 +7691,7 @@ "0x6400000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606104, "gas_refund_counter": 0, @@ -7715,7 +7717,7 @@ "0xa0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606102, "gas_refund_counter": 0, @@ -7740,7 +7742,7 @@ "0xf82c50f100000000000000000000000000000000000000000000000000000000" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606100, "gas_refund_counter": 0, @@ -7764,7 +7766,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606098, "gas_refund_counter": 0, @@ -7789,7 +7791,7 @@ "0x3c7" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606095, "gas_refund_counter": 0, @@ -7813,7 +7815,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606087, "gas_refund_counter": 0, @@ -7837,7 +7839,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606086, "gas_refund_counter": 0, @@ -7862,7 +7864,7 @@ "0x3de" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606083, "gas_refund_counter": 0, @@ -7888,7 +7890,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606080, "gas_refund_counter": 0, @@ -7915,7 +7917,7 @@ "0x3d6" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606077, "gas_refund_counter": 0, @@ -7943,7 +7945,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606074, "gas_refund_counter": 0, @@ -7972,7 +7974,7 @@ "0x400" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606071, "gas_refund_counter": 0, @@ -8000,7 +8002,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606063, "gas_refund_counter": 0, @@ -8028,7 +8030,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606062, "gas_refund_counter": 0, @@ -8057,7 +8059,7 @@ "0x418" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606059, "gas_refund_counter": 0, @@ -8087,7 +8089,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606056, "gas_refund_counter": 0, @@ -8117,7 +8119,7 @@ "0x418" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606053, "gas_refund_counter": 0, @@ -8146,7 +8148,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606051, "gas_refund_counter": 0, @@ -8175,7 +8177,7 @@ "0x3d6" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606048, "gas_refund_counter": 0, @@ -8204,7 +8206,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606045, "gas_refund_counter": 0, @@ -8232,7 +8234,7 @@ "0x3d6" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606043, "gas_refund_counter": 0, @@ -8259,7 +8261,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606035, "gas_refund_counter": 0, @@ -8286,7 +8288,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606034, "gas_refund_counter": 0, @@ -8314,7 +8316,7 @@ "0xffffffff" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606031, "gas_refund_counter": 0, @@ -8341,7 +8343,7 @@ "0x3e1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606028, "gas_refund_counter": 0, @@ -8367,7 +8369,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606020, "gas_refund_counter": 0, @@ -8393,7 +8395,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606019, "gas_refund_counter": 0, @@ -8420,7 +8422,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606017, "gas_refund_counter": 0, @@ -8448,7 +8450,7 @@ "0x636f6e736f6c652e6c6f67" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606014, "gas_refund_counter": 0, @@ -8476,7 +8478,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606011, "gas_refund_counter": 0, @@ -8503,7 +8505,7 @@ "0x636f6e736f6c652e6c6f67" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606009, "gas_refund_counter": 0, @@ -8531,7 +8533,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606007, "gas_refund_counter": 0, @@ -8560,7 +8562,7 @@ "0x0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606005, "gas_refund_counter": 0, @@ -8590,7 +8592,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073606002, "gas_refund_counter": 0, @@ -8620,7 +8622,7 @@ "0x24" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073605999, "gas_refund_counter": 0, @@ -8651,7 +8653,7 @@ "0x20" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073605996, "gas_refund_counter": 0, @@ -8683,7 +8685,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073605993, "gas_refund_counter": 0, @@ -8714,7 +8716,7 @@ "0xa0" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073605990, "gas_refund_counter": 0, @@ -8746,7 +8748,7 @@ "0x636f6e736f6c652e6c6f67" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073605987, "gas_refund_counter": 0, @@ -8779,7 +8781,7 @@ "0x3ffded61" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073605985, "gas_refund_counter": 0, @@ -8807,7 +8809,7 @@ "0x1" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603385, "gas_refund_counter": 0, @@ -8834,7 +8836,7 @@ "0x636f6e736f6c652e6c6f67" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603383, "gas_refund_counter": 0, @@ -8860,7 +8862,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603381, "gas_refund_counter": 0, @@ -8885,7 +8887,7 @@ "0x3de" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603379, "gas_refund_counter": 0, @@ -8909,7 +8911,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603371, "gas_refund_counter": 0, @@ -8933,7 +8935,7 @@ "0x80" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603370, "gas_refund_counter": 0, @@ -8956,7 +8958,7 @@ "0x3c4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603368, "gas_refund_counter": 0, @@ -8978,7 +8980,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603360, "gas_refund_counter": 0, @@ -9000,7 +9002,7 @@ "0x64" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603359, "gas_refund_counter": 0, @@ -9021,7 +9023,7 @@ "0x2f4" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603357, "gas_refund_counter": 0, @@ -9041,7 +9043,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603349, "gas_refund_counter": 0, @@ -9061,7 +9063,7 @@ "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" ], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603348, "gas_refund_counter": 0, @@ -9077,7 +9079,7 @@ "op": 86, "stack": ["0xf8a8fd6d", "0x92"], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603346, "gas_refund_counter": 0, @@ -9093,7 +9095,7 @@ "op": 91, "stack": ["0xf8a8fd6d"], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603338, "gas_refund_counter": 0, @@ -9109,7 +9111,7 @@ "op": 0, "stack": ["0xf8a8fd6d"], "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e0033000000000000000000000000000000000000000000000000000000000000", + "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1073603337, "gas_refund_counter": 0, @@ -10196,6 +10198,7 @@ "output": "{...}", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Return", "steps": [ { @@ -10459,7 +10462,7 @@ "stack": ["0x126"], "push_stack": null, - "memory": "0x6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e00330000000000000000000000000000000000000000000000000000", + "memory": "0x6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c634300082100330000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1056911949, "gas_refund_counter": 0, @@ -10476,7 +10479,7 @@ "stack": ["0x126", "0x0"], "push_stack": null, - "memory": "0x6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea26469706673582212204fa676a81446bb9567311b8206428a7b97acb6b735a9b7fec033df5f3ffbbf0c64736f6c634300081e00330000000000000000000000000000000000000000000000000000", + "memory": "0x6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c634300082100330000000000000000000000000000000000000000000000000000", "returndata": "0x", "gas_remaining": 1056911947, "gas_refund_counter": 0, @@ -10564,6 +10567,7 @@ "output": "0x", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Stop", "steps": [ { @@ -13836,6 +13840,7 @@ "output": "0x", "gas_used": "{...}", "gas_limit": "{...}", + "gas_refund_counter": 0, "status": "Stop", "steps": [], "decoded": null diff --git a/crates/forge/tests/fixtures/invariant_traces.svg b/crates/forge/tests/fixtures/invariant_traces.svg index 37c7bc8ebcf84..8844062c2553c 100644 --- a/crates/forge/tests/fixtures/invariant_traces.svg +++ b/crates/forge/tests/fixtures/invariant_traces.svg @@ -1,4 +1,4 @@ - + (&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.as_ref().serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for FoundryTransactionRequest { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - WithOtherFields::::deserialize(deserializer).map(Self) - } +/// This is a union of different transaction request types, instantiated from a +/// [`WithOtherFields`]. The specific variant is determined by the transaction +/// type field and/or the presence of certain fields: +/// - **Ethereum**: Default variant when no special fields are present +/// - **Op**: When `sourceHash`, `mint`, and `isSystemTx` fields are present, or transaction type is +/// `DEPOSIT_TX_TYPE_ID` +/// - **Tempo**: When `feeToken` or `nonceKey` fields are present, or transaction type is +/// `TEMPO_TX_TYPE_ID` +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FoundryTransactionRequest { + Ethereum(TransactionRequest), + Op(WithOtherFields), + Tempo(Box), } impl FoundryTransactionRequest { @@ -44,79 +36,40 @@ impl FoundryTransactionRequest { /// [`WithOtherFields`]. #[inline] pub fn new(inner: WithOtherFields) -> Self { - Self(inner) + inner.into() } - /// Consume self and return the inner [`WithOtherFields`]. - #[inline] - pub fn into_inner(self) -> WithOtherFields { - self.0 - } - - /// Check if this is a deposit transaction. - #[inline] - pub fn is_deposit(&self) -> bool { - self.as_ref().transaction_type == Some(DEPOSIT_TX_TYPE_ID) + /// Consume the [`FoundryTransactionRequest`] and return the inner transaction request. + pub fn into_inner(self) -> TransactionRequest { + match self { + Self::Ethereum(tx) => tx, + Self::Op(tx) => tx.inner, + Self::Tempo(tx) => tx.inner, + } } - /// Check if this is a Tempo transaction. + /// Get the deposit transaction parts from the request, calling [`get_deposit_tx_parts`] helper + /// with OtherFields. /// - /// Returns true if the transaction type is explicitly set to Tempo (0x76) or if - /// a `feeToken` is set in OtherFields. - #[inline] - pub fn is_tempo(&self) -> bool { - self.as_ref().transaction_type == Some(TEMPO_TX_TYPE_ID) - || self.as_ref().other.contains_key("feeToken") - || self.as_ref().other.contains_key("nonceKey") - } - - /// Get the Tempo fee token from OtherFields if present. - fn get_tempo_fee_token(&self) -> Option
{ - self.as_ref().other.get_deserialized::
("feeToken").transpose().ok().flatten() - } - - /// Get the Tempo nonce sequence key from OtherFields if present. - fn get_tempo_nonce_key(&self) -> U256 { - self.as_ref() - .other - .get_deserialized::("nonceKey") - .transpose() - .ok() - .flatten() - .unwrap_or_default() - } - - /// Check if all necessary keys are present to build a Tempo transaction, returning a list of - /// keys that are missing. - pub fn complete_tempo(&self) -> Result<(), Vec<&'static str>> { - let mut missing = Vec::new(); - if self.chain_id().is_none() { - missing.push("chain_id"); - } - if self.gas_limit().is_none() { - missing.push("gas_limit"); - } - if self.max_fee_per_gas().is_none() { - missing.push("max_fee_per_gas"); - } - if self.max_priority_fee_per_gas().is_none() { - missing.push("max_priority_fee_per_gas"); - } - if self.nonce().is_none() { - missing.push("nonce"); + /// # Returns + /// - Ok(deposit_tx_parts) if all necessary keys are present to build a deposit transaction. + /// - Err(missing) if some keys are missing to build a deposit transaction. + pub fn get_deposit_tx_parts(&self) -> Result> { + match self { + Self::Op(tx) => get_deposit_tx_parts(&tx.other), + // Not a deposit transaction request, so missing at least sourceHash, mint, and + // isSystemTx + _ => Err(vec!["sourceHash", "mint", "isSystemTx"]), } - if missing.is_empty() { Ok(()) } else { Err(missing) } } /// Returns the minimal transaction type this request can be converted into based on the fields /// that are set. See [`TransactionRequest::preferred_type`]. pub fn preferred_type(&self) -> FoundryTxType { - if self.is_deposit() { - FoundryTxType::Deposit - } else if self.is_tempo() { - FoundryTxType::Tempo - } else { - self.as_ref().preferred_type().into() + match self { + Self::Ethereum(tx) => tx.preferred_type().into(), + Self::Op(_) => FoundryTxType::Deposit, + Self::Tempo(_) => FoundryTxType::Tempo, } } @@ -139,23 +92,17 @@ impl FoundryTransactionRequest { /// Check if all necessary keys are present to build a Deposit transaction, returning a list of /// keys that are missing. pub fn complete_deposit(&self) -> Result<(), Vec<&'static str>> { - get_deposit_tx_parts(&self.as_ref().other).map(|_| ()) + self.get_deposit_tx_parts().map(|_| ()) } - /// Return the tx type this request can be built as. Computed by checking - /// the preferred type, and then checking for completeness. - pub fn buildable_type(&self) -> Option { - let pref = self.preferred_type(); - match pref { - FoundryTxType::Legacy => self.as_ref().complete_legacy().ok(), - FoundryTxType::Eip2930 => self.as_ref().complete_2930().ok(), - FoundryTxType::Eip1559 => self.as_ref().complete_1559().ok(), - FoundryTxType::Eip4844 => self.as_ref().complete_4844().ok(), - FoundryTxType::Eip7702 => self.as_ref().complete_7702().ok(), - FoundryTxType::Deposit => self.complete_deposit().ok(), - FoundryTxType::Tempo => self.complete_tempo().ok(), - }?; - Some(pref) + /// Check if all necessary keys are present to build a Tempo transaction, returning a list of + /// keys that are missing. + pub fn complete_tempo(&self) -> Result<(), Vec<&'static str>> { + match self { + Self::Tempo(tx) => tx.complete_type(TempoTxType::AA).map(|_| ()), + // Not a Tempo transaction request, so missing at least feeToken and nonceKey + _ => Err(vec!["feeToken", "nonceKey"]), + } } /// Check if all necessary keys are present to build a transaction. @@ -186,8 +133,8 @@ impl FoundryTransactionRequest { /// Converts the request into a `FoundryTypedTx`, handling all Ethereum and OP-stack transaction /// types. pub fn build_typed_tx(self) -> Result { - // Handle deposit transactions - if let Ok(deposit_tx_parts) = get_deposit_tx_parts(&self.as_ref().other) { + if let Ok(deposit_tx_parts) = self.get_deposit_tx_parts() { + // Build deposit transaction Ok(FoundryTypedTx::Deposit(TxDeposit { from: self.from().unwrap_or_default(), source_hash: deposit_tx_parts.source_hash, @@ -198,35 +145,26 @@ impl FoundryTransactionRequest { is_system_transaction: deposit_tx_parts.is_system_transaction, input: self.input().cloned().unwrap_or_default(), })) - } else if self.is_tempo() { - // Build Tempo transaction from request fields - Ok(FoundryTypedTx::Tempo(TempoTransaction { - chain_id: self.chain_id().unwrap_or_default(), - fee_token: self.get_tempo_fee_token(), - max_fee_per_gas: self.max_fee_per_gas().unwrap_or_default(), - max_priority_fee_per_gas: self.max_priority_fee_per_gas().unwrap_or_default(), - gas_limit: self.gas_limit().unwrap_or_default(), - nonce_key: self.get_tempo_nonce_key(), - nonce: self.nonce().unwrap_or_default(), - calls: vec![Call { - to: self.kind().unwrap_or_default(), - value: self.value().unwrap_or_default(), - input: self.input().cloned().unwrap_or_default(), - }], - access_list: self.access_list().cloned().unwrap_or_default(), - ..Default::default() - })) - } else if self.as_ref().has_eip4844_fields() && self.as_ref().blob_sidecar().is_none() { - // if request has eip4844 fields but no blob sidecar, try to build to eip4844 without - // sidecar - self.0 - .into_inner() + } else if self.complete_tempo().is_ok() + && let Self::Tempo(tx_req) = self + { + // Build Tempo transaction + Ok(FoundryTypedTx::Tempo( + tx_req.build_aa().map_err(|e| Self::Tempo(Box::new(e.into_value())))?, + )) + } else if self.as_ref().has_eip4844_fields() + && self.blob_sidecar().is_none() + && alloy_network::TransactionBuilder7594::blob_sidecar_7594(self.as_ref()).is_none() + { + // if request has eip4844 fields but no blob sidecar (neither eip4844 nor eip7594 + // format), try to build to eip4844 without sidecar + self.into_inner() .build_4844_without_sidecar() - .map_err(|e| Self(e.into_value().into())) + .map_err(|e| Self::Ethereum(e.into_value())) .map(|tx| FoundryTypedTx::Eip4844(tx.into())) } else { // Use the inner transaction request to build EthereumTypedTransaction - let typed_tx = self.0.into_inner().build_typed_tx().map_err(|tx| Self(tx.into()))?; + let typed_tx = self.into_inner().build_typed_tx().map_err(Self::Ethereum)?; // Convert EthereumTypedTransaction to FoundryTypedTx Ok(match typed_tx { EthereumTypedTransaction::Legacy(tx) => FoundryTypedTx::Legacy(tx), @@ -239,14 +177,90 @@ impl FoundryTransactionRequest { } } +impl Default for FoundryTransactionRequest { + fn default() -> Self { + Self::Ethereum(TransactionRequest::default()) + } +} + +impl Serialize for FoundryTransactionRequest { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Self::Ethereum(tx) => tx.serialize(serializer), + Self::Op(tx) => tx.serialize(serializer), + Self::Tempo(tx) => tx.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for FoundryTransactionRequest { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + WithOtherFields::::deserialize(deserializer).map(Into::::into) + } +} + +impl AsRef for FoundryTransactionRequest { + fn as_ref(&self) -> &TransactionRequest { + match self { + Self::Ethereum(tx) => tx, + Self::Op(tx) => tx, + Self::Tempo(tx) => tx, + } + } +} + +impl AsMut for FoundryTransactionRequest { + fn as_mut(&mut self) -> &mut TransactionRequest { + match self { + Self::Ethereum(tx) => tx, + Self::Op(tx) => tx, + Self::Tempo(tx) => tx, + } + } +} + +impl From> for FoundryTransactionRequest { + fn from(tx: WithOtherFields) -> Self { + if tx.transaction_type == Some(TEMPO_TX_TYPE_ID) + || tx.other.contains_key("feeToken") + || tx.other.contains_key("nonceKey") + { + let mut tempo_tx_req: TempoTransactionRequest = tx.inner.into(); + if let Some(fee_token) = + tx.other.get_deserialized::
("feeToken").transpose().ok().flatten() + { + tempo_tx_req.fee_token = Some(fee_token); + } + if let Some(nonce_key) = + tx.other.get_deserialized::("nonceKey").transpose().ok().flatten() + { + tempo_tx_req.set_nonce_key(nonce_key); + } + Self::Tempo(Box::new(tempo_tx_req)) + } else if tx.transaction_type == Some(DEPOSIT_TX_TYPE_ID) + || get_deposit_tx_parts(&tx.other).is_ok() + { + Self::Op(tx) + } else { + Self::Ethereum(tx.into_inner()) + } + } +} + impl From for FoundryTransactionRequest { fn from(tx: FoundryTypedTx) -> Self { match tx { - FoundryTypedTx::Legacy(tx) => Self(Into::::into(tx).into()), - FoundryTypedTx::Eip2930(tx) => Self(Into::::into(tx).into()), - FoundryTypedTx::Eip1559(tx) => Self(Into::::into(tx).into()), - FoundryTypedTx::Eip4844(tx) => Self(Into::::into(tx).into()), - FoundryTypedTx::Eip7702(tx) => Self(Into::::into(tx).into()), + FoundryTypedTx::Legacy(tx) => Self::Ethereum(Into::::into(tx)), + FoundryTypedTx::Eip2930(tx) => Self::Ethereum(Into::::into(tx)), + FoundryTypedTx::Eip1559(tx) => Self::Ethereum(Into::::into(tx)), + FoundryTypedTx::Eip4844(tx) => Self::Ethereum(Into::::into(tx)), + FoundryTypedTx::Eip7702(tx) => Self::Ethereum(Into::::into(tx)), FoundryTypedTx::Deposit(tx) => { let other = OtherFields::from_iter([ ("sourceHash", tx.source_hash.to_string().into()), @@ -418,8 +432,8 @@ impl TransactionBuilder for FoundryTransactionRequest { fn can_build(&self) -> bool { self.as_ref().can_build() - || get_deposit_tx_parts(&self.as_ref().other).is_ok() - || self.is_tempo() + || self.complete_deposit().is_ok() + || self.complete_tempo().is_ok() } fn output_tx_type(&self) -> FoundryTxType { @@ -427,7 +441,17 @@ impl TransactionBuilder for FoundryTransactionRequest { } fn output_tx_type_checked(&self) -> Option { - self.buildable_type() + let pref = self.preferred_type(); + match pref { + FoundryTxType::Legacy => self.as_ref().complete_legacy().ok(), + FoundryTxType::Eip2930 => self.as_ref().complete_2930().ok(), + FoundryTxType::Eip1559 => self.as_ref().complete_1559().ok(), + FoundryTxType::Eip4844 => self.as_ref().complete_4844().ok(), + FoundryTxType::Eip7702 => self.as_ref().complete_7702().ok(), + FoundryTxType::Deposit => self.complete_deposit().ok(), + FoundryTxType::Tempo => self.complete_tempo().ok(), + }?; + Some(pref) } /// Prepares [`FoundryTransactionRequest`] by trimming conflicting fields, and filling with @@ -436,19 +460,19 @@ impl TransactionBuilder for FoundryTransactionRequest { let preferred_type = self.preferred_type(); let inner = self.as_mut(); inner.transaction_type = Some(preferred_type as u8); - inner.gas_limit().is_none().then(|| inner.set_gas_limit(Default::default())); + inner.gas.is_none().then(|| inner.set_gas_limit(Default::default())); if !matches!(preferred_type, FoundryTxType::Deposit | FoundryTxType::Tempo) { inner.trim_conflicting_keys(); inner.populate_blob_hashes(); } if preferred_type != FoundryTxType::Deposit { - inner.nonce().is_none().then(|| inner.set_nonce(Default::default())); + inner.nonce.is_none().then(|| inner.set_nonce(Default::default())); } if matches!(preferred_type, FoundryTxType::Legacy | FoundryTxType::Eip2930) { - inner.gas_price().is_none().then(|| inner.set_gas_price(Default::default())); + inner.gas_price.is_none().then(|| inner.set_gas_price(Default::default())); } if preferred_type == FoundryTxType::Eip2930 { - inner.access_list().is_none().then(|| inner.set_access_list(Default::default())); + inner.access_list.is_none().then(|| inner.set_access_list(Default::default())); } if matches!( preferred_type, @@ -458,13 +482,10 @@ impl TransactionBuilder for FoundryTransactionRequest { | FoundryTxType::Tempo ) { inner - .max_priority_fee_per_gas() + .max_priority_fee_per_gas .is_none() .then(|| inner.set_max_priority_fee_per_gas(Default::default())); - inner - .max_fee_per_gas() - .is_none() - .then(|| inner.set_max_fee_per_gas(Default::default())); + inner.max_fee_per_gas.is_none().then(|| inner.set_max_fee_per_gas(Default::default())); } if preferred_type == FoundryTxType::Eip4844 { inner @@ -491,6 +512,24 @@ impl TransactionBuilder for FoundryTransactionRequest { } } +impl TransactionBuilder4844 for FoundryTransactionRequest { + fn max_fee_per_blob_gas(&self) -> Option { + self.as_ref().max_fee_per_blob_gas() + } + + fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) { + self.as_mut().set_max_fee_per_blob_gas(max_fee_per_blob_gas); + } + + fn blob_sidecar(&self) -> Option<&BlobTransactionSidecar> { + self.as_ref().blob_sidecar() + } + + fn set_blob_sidecar(&mut self, sidecar: BlobTransactionSidecar) { + self.as_mut().set_blob_sidecar(sidecar); + } +} + /// Converts `OtherFields` to `DepositTransactionParts`, produces error with missing fields pub fn get_deposit_tx_parts( other: &OtherFields, @@ -524,3 +563,121 @@ pub fn get_deposit_tx_parts( Err(missing) } } + +#[cfg(test)] +mod tests { + use super::*; + + fn default_tx_req() -> TransactionRequest { + TransactionRequest::default() + .with_to(Address::random()) + .with_nonce(1) + .with_value(U256::from(1000000)) + .with_gas_limit(1000000) + .with_max_fee_per_gas(1000000) + .with_max_priority_fee_per_gas(1000000) + } + + #[test] + fn test_routing_ethereum_default() { + let tx = default_tx_req(); + let req: FoundryTransactionRequest = WithOtherFields::new(tx).into(); + + assert!(matches!(req, FoundryTransactionRequest::Ethereum(_))); + assert!(matches!(req.build_unsigned(), Ok(FoundryTypedTx::Eip1559(_)))); + } + + #[test] + fn test_routing_tempo_by_fee_token() { + let tx = default_tx_req(); + let mut other = OtherFields::default(); + other.insert("feeToken".to_string(), serde_json::to_value(Address::random()).unwrap()); + + let req: FoundryTransactionRequest = WithOtherFields { inner: tx, other }.into(); + + assert!(matches!(req, FoundryTransactionRequest::Tempo(_))); + assert!(matches!(req.build_unsigned(), Ok(FoundryTypedTx::Tempo(_)))); + } + + #[test] + fn test_routing_op_by_deposit_fields() { + let tx = default_tx_req(); + let mut other = OtherFields::default(); + other.insert("sourceHash".to_string(), serde_json::to_value(B256::ZERO).unwrap()); + other.insert("mint".to_string(), serde_json::to_value(U256::from(1000)).unwrap()); + other.insert("isSystemTx".to_string(), serde_json::to_value(false).unwrap()); + + let req: FoundryTransactionRequest = WithOtherFields { inner: tx, other }.into(); + + assert!(matches!(req, FoundryTransactionRequest::Op(_))); + assert!(matches!(req.build_unsigned(), Ok(FoundryTypedTx::Deposit(_)))); + } + + #[test] + fn test_op_incomplete_routes_to_ethereum() { + let tx = default_tx_req(); + let mut other = OtherFields::default(); + // Only provide 2 of 3 required Op fields + other.insert("sourceHash".to_string(), serde_json::to_value(B256::ZERO).unwrap()); + other.insert("mint".to_string(), serde_json::to_value(U256::from(1000)).unwrap()); + + let req: FoundryTransactionRequest = WithOtherFields { inner: tx, other }.into(); + + assert!(matches!(req, FoundryTransactionRequest::Ethereum(_))); + assert!(matches!(req.build_unsigned(), Ok(FoundryTypedTx::Eip1559(_)))); + } + + #[test] + fn test_ethereum_with_unrelated_other_fields() { + let tx = default_tx_req(); + let mut other = OtherFields::default(); + other.insert("anotherField".to_string(), serde_json::to_value(123).unwrap()); + + let req: FoundryTransactionRequest = WithOtherFields { inner: tx, other }.into(); + + assert!(matches!(req, FoundryTransactionRequest::Ethereum(_))); + assert!(matches!(req.build_unsigned(), Ok(FoundryTypedTx::Eip1559(_)))); + } + + #[test] + fn test_serialization_ethereum() { + let tx = default_tx_req(); + let original: FoundryTransactionRequest = WithOtherFields::new(tx).into(); + + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: FoundryTransactionRequest = serde_json::from_str(&serialized).unwrap(); + + assert!(matches!(deserialized, FoundryTransactionRequest::Ethereum(_))); + } + + #[test] + fn test_serialization_op() { + let tx = default_tx_req(); + let mut other = OtherFields::default(); + other.insert("sourceHash".to_string(), serde_json::to_value(B256::ZERO).unwrap()); + other.insert("mint".to_string(), serde_json::to_value(U256::from(1000)).unwrap()); + other.insert("isSystemTx".to_string(), serde_json::to_value(false).unwrap()); + + let original: FoundryTransactionRequest = WithOtherFields { inner: tx, other }.into(); + + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: FoundryTransactionRequest = serde_json::from_str(&serialized).unwrap(); + + assert!(matches!(deserialized, FoundryTransactionRequest::Op(_))); + } + + #[test] + fn test_serialization_tempo() { + let tx = default_tx_req(); + let mut other = OtherFields::default(); + other.insert("feeToken".to_string(), serde_json::to_value(Address::ZERO).unwrap()); + other.insert("nonceKey".to_string(), serde_json::to_value(U256::from(42)).unwrap()); + + let original: FoundryTransactionRequest = WithOtherFields { inner: tx, other }.into(); + + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: FoundryTransactionRequest = serde_json::from_str(&serialized).unwrap(); + + assert!(matches!(deserialized, FoundryTransactionRequest::Tempo(_))); + } +} diff --git a/crates/script-sequence/src/reader.rs b/crates/script-sequence/src/reader.rs index 4867f4f50e767..bfe14e14f3fd2 100644 --- a/crates/script-sequence/src/reader.rs +++ b/crates/script-sequence/src/reader.rs @@ -43,6 +43,12 @@ impl BroadcastReader { self } + fn matches_filters(&self, tx: &TransactionWithMetadata) -> bool { + let name_filter = tx.contract_name.as_ref().is_some_and(|cn| *cn == self.contract_name); + let type_filter = self.tx_type.is_empty() || self.tx_type.contains(&tx.opcode); + name_filter && type_filter + } + /// Read all broadcast files in the broadcast directory. /// /// Example structure: @@ -113,19 +119,12 @@ impl BroadcastReader { return false; } - broadcast.transactions.iter().any(move |tx| { - let name_filter = - tx.contract_name.as_ref().is_some_and(|cn| *cn == self.contract_name); - - let type_filter = self.tx_type.is_empty() || self.tx_type.contains(&tx.opcode); - - name_filter && type_filter - }) + broadcast.transactions.iter().any(|tx| self.matches_filters(tx)) }) .collect::>(); // Sort by descending timestamp - seqs.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); + seqs.sort_by_key(|s| std::cmp::Reverse(s.timestamp)); seqs } @@ -146,13 +145,7 @@ impl BroadcastReader { let ScriptSequence { transactions, receipts, .. } = broadcast; let mut targets = Vec::new(); - for tx in transactions.into_iter().filter(|tx| { - let name_filter = tx.contract_name.as_ref().is_some_and(|cn| *cn == self.contract_name); - - let type_filter = self.tx_type.is_empty() || self.tx_type.contains(&tx.opcode); - - name_filter && type_filter - }) { + for tx in transactions.into_iter().filter(|tx| self.matches_filters(tx)) { let maybe_receipt = receipts .iter() .find(|receipt| tx.hash.is_some_and(|hash| hash == receipt.transaction_hash)); @@ -163,7 +156,7 @@ impl BroadcastReader { } // Sort by descending block number - targets.sort_by(|a, b| b.1.block_number.cmp(&a.1.block_number)); + targets.sort_by_key(|t| std::cmp::Reverse(t.1.block_number)); targets } diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index 56b7c6b36f79c..4a7d69093d23b 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -8,7 +8,6 @@ use foundry_config::Config; use serde::{Deserialize, Serialize}; use std::{ collections::VecDeque, - io::{BufWriter, Write}, path::PathBuf, time::{Duration, SystemTime, UNIX_EPOCH}, }; @@ -108,9 +107,7 @@ impl ScriptSequence { // broadcast folder writes //../run-latest.json - let mut writer = BufWriter::new(fs::create_file(path)?); - serde_json::to_writer_pretty(&mut writer, &self)?; - writer.flush()?; + fs::write_pretty_json_file(path, &self)?; if save_ts { //../run-[timestamp].json fs::copy(path, path.with_file_name(&ts_name))?; @@ -118,9 +115,7 @@ impl ScriptSequence { // cache folder writes //../run-latest.json - let mut writer = BufWriter::new(fs::create_file(sensitive_path)?); - serde_json::to_writer_pretty(&mut writer, &sensitive_script_sequence)?; - writer.flush()?; + fs::write_pretty_json_file(sensitive_path, &sensitive_script_sequence)?; if save_ts { //../run-[timestamp].json fs::copy(sensitive_path, sensitive_path.with_file_name(&ts_name))?; @@ -166,8 +161,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]-latest.json` and + /// `./cache/[contract_filename]/[chain_id]/[sig]-latest.json`. pub fn get_paths( config: &Config, sig: &str, diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 76b8302fd3c92..aa2bb83d3600c 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -12,6 +12,7 @@ use alloy_primitives::{ use alloy_provider::{Provider, utils::Eip1559Estimation}; use alloy_rpc_types::TransactionRequest; use alloy_serde::WithOtherFields; +use alloy_signer::Signer; use eyre::{Context, Result, bail}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::Wallets; @@ -22,6 +23,7 @@ use foundry_common::{ shell, }; use foundry_config::Config; +use foundry_wallets::{WalletSigner, wallet_browser::signer::BrowserSigner}; use futures::{FutureExt, StreamExt, future::join_all, stream::FuturesUnordered}; use itertools::Itertools; @@ -64,6 +66,7 @@ pub async fn next_nonce( pub enum SendTransactionKind<'a> { Unlocked(WithOtherFields), Raw(WithOtherFields, &'a EthereumWallet), + Browser(WithOtherFields, &'a BrowserSigner), Signed(TxEnvelope), } @@ -82,7 +85,7 @@ impl<'a> SendTransactionKind<'a> { estimate_via_rpc: bool, estimate_multiplier: u64, ) -> Result<()> { - if let Self::Raw(tx, _) | Self::Unlocked(tx) = self { + if let Self::Raw(tx, _) | Self::Unlocked(tx) | Self::Browser(tx, _) = self { if sequential_broadcast { let from = tx.from.expect("no sender"); @@ -131,27 +134,34 @@ impl<'a> SendTransactionKind<'a> { /// - Sign and submit via `eth_sendRawTransaction` for raw transactions /// - Submit pre-signed transaction via `eth_sendRawTransaction` pub async fn send(self, provider: Arc) -> Result { - let pending = match self { + match self { Self::Unlocked(tx) => { debug!("sending transaction from unlocked account {:?}", tx); // Submit the transaction - provider.send_transaction(tx).await? + let pending = provider.send_transaction(tx).await?; + Ok(*pending.tx_hash()) } Self::Raw(tx, signer) => { debug!("sending transaction: {:?}", tx); let signed = tx.build(signer).await?; // Submit the raw transaction - provider.send_raw_transaction(signed.encoded_2718().as_ref()).await? + let pending = provider.send_raw_transaction(signed.encoded_2718().as_ref()).await?; + Ok(*pending.tx_hash()) } Self::Signed(tx) => { debug!("sending transaction: {:?}", tx); - provider.send_raw_transaction(tx.encoded_2718().as_ref()).await? + let pending = provider.send_raw_transaction(tx.encoded_2718().as_ref()).await?; + Ok(*pending.tx_hash()) } - }; + Self::Browser(tx, signer) => { + debug!("sending transaction: {:?}", tx); - Ok(*pending.tx_hash()) + // Sign and send the transaction via the browser wallet + Ok(signer.send_transaction_via_browser(tx.into_inner()).await?) + } + } } /// Prepares and sends the transaction in one operation. @@ -179,12 +189,36 @@ impl<'a> SendTransactionKind<'a> { } } +/// Convenience enum to represent either an Ethereum wallet or a browser signer +pub enum EitherSigner { + Ethereum(EthereumWallet), + Browser(BrowserSigner), +} + +impl From for EitherSigner { + fn from(wallet: EthereumWallet) -> Self { + Self::Ethereum(wallet) + } +} + +impl From for EitherSigner { + fn from(wallet: WalletSigner) -> Self { + EthereumWallet::new(wallet).into() + } +} + +impl From for EitherSigner { + fn from(wallet: BrowserSigner) -> Self { + Self::Browser(wallet) + } +} + /// Represents how to send _all_ transactions pub enum SendTransactionsKind { /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. Unlocked(AddressHashSet), /// Send a signed transaction via `eth_sendRawTransaction` - Raw(AddressHashMap), + Raw(AddressHashMap), } impl SendTransactionsKind { @@ -205,7 +239,12 @@ impl SendTransactionsKind { } Self::Raw(wallets) => { if let Some(wallet) = wallets.get(addr) { - Ok(SendTransactionKind::Raw(tx, wallet)) + match wallet { + EitherSigner::Ethereum(wallet) => Ok(SendTransactionKind::Raw(tx, wallet)), + EitherSigner::Browser(signer) => { + Ok(SendTransactionKind::Browser(tx, signer)) + } + } } else { bail!("No matching signer for {:?} found", addr) } @@ -282,11 +321,12 @@ impl BundledState { let send_kind = if self.args.unlocked { SendTransactionsKind::Unlocked(required_addresses.clone()) } else { - let signers = self.script_wallets.into_multi_wallet().into_signers()?; + let signers: Vec
= + self.script_wallets.signers().map_err(|e| eyre::eyre!("{e}"))?; let mut missing_addresses = Vec::new(); for addr in &required_addresses { - if !signers.contains_key(addr) { + if !signers.contains(addr) { missing_addresses.push(addr); } } @@ -295,14 +335,16 @@ impl BundledState { eyre::bail!( "No associated wallet for addresses: {:?}. Unlocked wallets: {:?}", missing_addresses, - signers.keys().collect::>() + signers ); } - let signers = signers - .into_iter() - .map(|(addr, signer)| (addr, EthereumWallet::new(signer))) - .collect(); + let (signers, browser) = self.script_wallets.into_multi_wallet().into_signers()?; + let mut signers: AddressHashMap = + signers.into_iter().map(|(addr, signer)| (addr, signer.into())).collect(); + if let Some(browser) = browser { + signers.insert(browser.address(), browser.into()); + } SendTransactionsKind::Raw(signers) }; @@ -504,8 +546,10 @@ impl BundledState { (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 / sequence.receipts.len() as u64, 9) - .unwrap_or_else(|_| "N/A".to_string()); + let avg_gas_price = total_gas_price + .checked_div(sequence.receipts.len() as u64) + .and_then(|avg| format_units(avg, 9).ok()) + .unwrap_or_else(|| "N/A".to_string()); let token_symbol = NamedChain::try_from(sequence.chain) .unwrap_or_default() diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 0e6d1fd302656..9044d635666e0 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -134,7 +134,7 @@ pub struct ScriptArgs { /// Send via `eth_sendTransaction` using the `--sender` argument as sender. #[arg( long, - conflicts_with_all = &["private_key", "private_keys", "ledger", "trezor", "aws"], + conflicts_with_all = &["private_key", "private_keys", "ledger", "trezor", "aws", "browser"], )] pub unlocked: bool, @@ -233,6 +233,15 @@ impl ScriptArgs { if let Some(sender) = self.maybe_load_private_key()? { evm_opts.sender = sender; + } else if self.evm.sender.is_none() { + // If no sender was explicitly set via --sender and there's exactly one signer + // (e.g. from --account or --keystore), use that signer's address as the sender. + // This makes --account behave consistently with --private-key. + if let Ok(signers) = script_wallets.signers() + && signers.len() == 1 + { + evm_opts.sender = signers[0]; + } } let script_config = ScriptConfig::new(config, evm_opts).await?; @@ -340,9 +349,13 @@ impl ScriptArgs { Ok(()) } - /// In case the user has loaded *only* one private-key, we can assume that he's using it as the - /// `--sender` + /// In case the user has loaded *only* one private-key or a single remote signer (e.g., + /// Turnkey), we can assume that they're using it as the `--sender`. fn maybe_load_private_key(&self) -> Result> { + if let Some(turnkey_address) = self.wallets.turnkey_address() { + return Ok(Some(turnkey_address)); + } + let maybe_sender = self .wallets .private_keys()? @@ -646,6 +659,7 @@ impl ScriptConfig { let mut builder = ExecutorBuilder::new() .inspectors(|stack| { stack + .logs(self.config.live_logs) .trace_mode(if debug { TraceMode::Debug } else { TraceMode::Call }) .networks(self.evm_opts.networks) .create2_deployer(self.evm_opts.create2_deployer) diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index fb8f07d0d425b..128df9460e95b 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -6,10 +6,7 @@ use foundry_common::{fs, shell}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; -use std::{ - io::{BufWriter, Write}, - path::PathBuf, -}; +use std::path::PathBuf; /// Holds the sequences of multiple chain deployments. #[derive(Clone, Default, Serialize, Deserialize)] @@ -119,9 +116,7 @@ impl MultiChainSequence { // 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()?; + fs::write_pretty_json_file(&self.path, self)?; if save_ts { //../Contract-[timestamp]/run.json @@ -133,9 +128,7 @@ impl MultiChainSequence { // 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()?; + fs::write_pretty_json_file(&self.sensitive_path, &sensitive_sequence)?; if save_ts { //../Contract-[timestamp]/run.json diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index 314c4d4983f3a..df2d2ba2dae02 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -242,7 +242,11 @@ impl ScriptProgress { Ok(TxStatus::Success(receipt)) => { trace!(tx_hash=?tx_hash, "received tx receipt"); - let msg = format_receipt(deployment_sequence.chain.into(), &receipt); + let msg = format_receipt( + deployment_sequence.chain.into(), + &receipt, + Some(deployment_sequence), + ); seq_progress.inner.write().finish_tx_spinner_with_msg(tx_hash, &msg)?; deployment_sequence.remove_pending(receipt.transaction_hash); @@ -255,7 +259,11 @@ impl ScriptProgress { warn!(tx_hash=?tx_hash, "Transaction Failure"); deployment_sequence.remove_pending(receipt.transaction_hash); - let msg = format_receipt(deployment_sequence.chain.into(), &receipt); + let msg = format_receipt( + deployment_sequence.chain.into(), + &receipt, + Some(deployment_sequence), + ); seq_progress.inner.write().finish_tx_spinner_with_msg(tx_hash, &msg)?; errors.push(format!("Transaction Failure: {:?}", receipt.transaction_hash)); diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 169153f996a28..478ccbf8ab7f9 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,8 +1,9 @@ use alloy_chains::{Chain, NamedChain}; -use alloy_network::AnyTransactionReceipt; +use alloy_network::{AnyTransactionReceipt, ReceiptResponse}; use alloy_primitives::{TxHash, U256, utils::format_units}; use alloy_provider::{PendingTransactionBuilder, PendingTransactionError, Provider, WatchTxError}; use eyre::{Result, eyre}; +use forge_script_sequence::ScriptSequence; use foundry_common::{provider::RetryProvider, retry, retry::RetryError, shell}; use std::time::Duration; @@ -24,11 +25,7 @@ pub enum TxStatus { impl From for TxStatus { fn from(receipt: AnyTransactionReceipt) -> Self { - if !receipt.inner.inner.inner.receipt.status.coerce_status() { - Self::Revert(receipt) - } else { - Self::Success(receipt) - } + if !receipt.status() { Self::Revert(receipt) } else { Self::Success(receipt) } } } @@ -89,34 +86,67 @@ pub async fn check_tx_status( } /// Prints parts of the receipt to stdout -pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { +pub fn format_receipt( + chain: Chain, + receipt: &AnyTransactionReceipt, + sequence: Option<&ScriptSequence>, +) -> String { let gas_used = receipt.gas_used; let gas_price = receipt.effective_gas_price; let block_number = receipt.block_number.unwrap_or_default(); let success = receipt.inner.inner.inner.receipt.status.coerce_status(); + let (contract_name, function) = sequence + .and_then(|seq| { + seq.transactions + .iter() + .find(|tx| tx.hash == Some(receipt.transaction_hash)) + .map(|tx| (tx.contract_name.clone(), tx.function.clone())) + }) + .unwrap_or((None, None)); + if shell::is_json() { - let _ = sh_println!( - "{}", - serde_json::json!({ - "chain": chain, - "status": if success { - "success" - } else { - "failed" - }, - "tx_hash": receipt.transaction_hash, - "contract_address": receipt.contract_address.map(|addr| addr.to_string()), - "block_number": block_number, - "gas_used": gas_used, - "gas_price": gas_price, - }) - ); + let mut json = serde_json::json!({ + "chain": chain, + "status": if success { + "success" + } else { + "failed" + }, + "tx_hash": receipt.transaction_hash, + "contract_address": receipt.contract_address.map(|addr| addr.to_string()), + "block_number": block_number, + "gas_used": gas_used, + "gas_price": gas_price, + }); + + if let Some(name) = &contract_name + && !name.is_empty() + { + json["contract_name"] = serde_json::Value::String(name.clone()); + } + if let Some(func) = &function + && !func.is_empty() + { + json["function"] = serde_json::Value::String(func.clone()); + } + + let _ = sh_println!("{}", json); String::new() } else { + let contract_info = match &contract_name { + Some(name) if !name.is_empty() => format!("\nContract: {name}"), + _ => String::new(), + }; + + let function_info = match &function { + Some(func) if !func.is_empty() => format!("\nFunction: {func}"), + _ => String::new(), + }; + format!( - "\n##### {chain}\n{status} Hash: {tx_hash:?}{contract_address}\nBlock: {block_number}\n{gas}\n\n", + "\n##### {chain}\n{status} Hash: {tx_hash:?}{contract_info}{function_info}{contract_address}\nBlock: {block_number}\n{gas}\n\n", status = if success { "✅ [Success]" } else { "❌ [Failed]" }, tx_hash = receipt.transaction_hash, contract_address = if let Some(addr) = &receipt.contract_address { @@ -145,3 +175,92 @@ pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { ) } } + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::B256; + use std::collections::VecDeque; + + fn mock_receipt(tx_hash: B256, success: bool) -> AnyTransactionReceipt { + serde_json::from_value(serde_json::json!({ + "type": "0x02", "status": if success { "0x1" } else { "0x0" }, + "cumulativeGasUsed": "0x5208", "logs": [], "transactionHash": tx_hash, + "logsBloom": format!("0x{}", "0".repeat(512)), + "transactionIndex": "0x0", "blockHash": B256::ZERO, "blockNumber": "0x3039", + "gasUsed": "0x5208", "effectiveGasPrice": "0x4a817c800", + "from": "0x0000000000000000000000000000000000000000", + "to": "0x0000000000000000000000000000000000000000", "contractAddress": null + })) + .unwrap() + } + + fn mock_sequence(tx_hash: B256, contract: Option<&str>, func: Option<&str>) -> ScriptSequence { + let tx = serde_json::from_value(serde_json::json!({ + "hash": tx_hash, "transactionType": "CALL", + "contractName": contract, "contractAddress": null, "function": func, + "arguments": null, "additionalContracts": [], "isFixedGasLimit": false, + "transaction": { + "type": "0x02", "chainId": "0x1", "nonce": "0x0", "gas": "0x5208", + "maxFeePerGas": "0x4a817c800", "maxPriorityFeePerGas": "0x3b9aca00", + "to": "0x0000000000000000000000000000000000000000", + "value": "0x0", "input": "0x", "accessList": [] + }, + })) + .unwrap(); + ScriptSequence { transactions: VecDeque::from([tx]), chain: 1, ..Default::default() } + } + + #[test] + fn format_receipt_displays_contract_and_function() { + let hash = B256::repeat_byte(0x42); + let seq = mock_sequence(hash, Some("MyContract"), Some("init(address)")); + let out = format_receipt(Chain::mainnet(), &mock_receipt(hash, true), Some(&seq)); + + assert!(out.contains("Contract: MyContract")); + assert!(out.contains("Function: init(address)")); + assert!(out.contains("✅ [Success]")); + } + + #[test] + fn format_receipt_without_sequence_omits_metadata() { + let hash = B256::repeat_byte(0x42); + let out = format_receipt(Chain::mainnet(), &mock_receipt(hash, true), None); + + assert!(!out.contains("Contract:")); + assert!(!out.contains("Function:")); + } + + #[test] + fn format_receipt_skips_empty_contract_name() { + let hash = B256::repeat_byte(0x42); + let seq = mock_sequence(hash, Some(""), Some("transfer(address)")); + let out = format_receipt(Chain::mainnet(), &mock_receipt(hash, true), Some(&seq)); + + assert!(!out.contains("Contract:")); + assert!(out.contains("Function: transfer(address)")); + } + + #[test] + fn format_receipt_handles_missing_tx_in_sequence() { + let seq = mock_sequence(B256::repeat_byte(0x99), Some("Other"), Some("other()")); + let out = format_receipt( + Chain::mainnet(), + &mock_receipt(B256::repeat_byte(0x42), true), + Some(&seq), + ); + + assert!(!out.contains("Contract:")); + assert!(!out.contains("Function:")); + } + + #[test] + fn format_receipt_shows_contract_on_failure() { + let hash = B256::repeat_byte(0x42); + let seq = mock_sequence(hash, Some("FailContract"), Some("fail()")); + let out = format_receipt(Chain::mainnet(), &mock_receipt(hash, false), Some(&seq)); + + assert!(out.contains("❌ [Failed]")); + assert!(out.contains("Contract: FailContract")); + } +} diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 0b2be58ca30d2..05834ed8bb036 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -59,7 +59,7 @@ impl PreSimulationState { .map(|tx| { let rpc = tx.rpc.expect("missing broadcastable tx rpc url"); let sender = tx.transaction.from().expect("all transactions should have a sender"); - let nonce = tx.transaction.nonce().expect("all transactions should have a sender"); + let nonce = tx.transaction.nonce().expect("all transactions should have a nonce"); let to = tx.transaction.to(); let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index ceec637e66661..cc460f732b4d7 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -119,6 +119,7 @@ impl VerifyBundle { if artifact.source.extension().is_some_and(|e| e.to_str() == Some("vy")) { warn!("Skipping verification of Vyper contract: {}", artifact.name); + return None; } // Strip artifact profile from contract name when creating contract info. diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs index 088f8f921fde8..6d98a26d79cb3 100644 --- a/crates/sol-macro-gen/src/sol_macro_gen.rs +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -391,7 +391,7 @@ edition = "2021" } fn write_mod_name(contents: &mut String, name: &str) -> Result<()> { - if syn::parse_str::(&format!("pub mod {name};")).is_ok() { + if syn::parse_str::(name).is_ok() { write!(contents, "pub mod {name};")?; } else { write!(contents, "pub mod r#{name};")?; diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 82c8c8e1b9e05..d29f6358e93d2 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -15,10 +15,12 @@ repository.workspace = true workspace = true [dependencies] +foundry-block-explorers.workspace = true foundry-common.workspace = true foundry-compilers = { workspace = true, features = ["project-util"] } foundry-config.workspace = true +alloy-chains.workspace = true alloy-primitives.workspace = true alloy-provider.workspace = true @@ -42,4 +44,3 @@ idna_adapter.workspace = true [dev-dependencies] tokio.workspace = true -foundry-block-explorers.workspace = true diff --git a/crates/test-utils/src/etherscan.rs b/crates/test-utils/src/etherscan.rs new file mode 100644 index 0000000000000..7ba33e0bbfbf5 --- /dev/null +++ b/crates/test-utils/src/etherscan.rs @@ -0,0 +1,32 @@ +//! Etherscan utilities for tests. + +use alloy_chains::Chain; +use alloy_primitives::Address; +use eyre::Result; +use foundry_block_explorers::Client; +use foundry_common::{compile::etherscan_project, flatten}; +use std::str::FromStr; + +/// Fetches the source code of a verified contract from Etherscan, flattens it, and returns it. +/// +/// This provides the same functionality as `cast source --flatten` but using the library directly, +/// avoiding the need to shell out to the `cast` binary. +pub async fn fetch_etherscan_source_flattened( + address: &str, + etherscan_api_key: &str, + chain: Chain, +) -> Result { + let client = Client::builder().chain(chain)?.with_api_key(etherscan_api_key).build()?; + + let address = Address::from_str(address)?; + let metadata = client.contract_source_code(address).await?; + let Some(metadata) = metadata.items.first() else { + eyre::bail!("Empty contract source code for {address}") + }; + + let tmp = tempfile::tempdir()?; + let project = etherscan_project(metadata, tmp.path())?; + let target_path = project.find_contract_path(&metadata.contract_name)?; + + flatten(project, &target_path) +} diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index ce195026db52b..64c68d69a5415 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -14,6 +14,7 @@ extern crate tracing; #[macro_use] mod macros; +pub mod etherscan; pub mod rpc; pub mod fd_lock; diff --git a/crates/test-utils/src/prj.rs b/crates/test-utils/src/prj.rs index 63fb09e637328..098eab059c075 100644 --- a/crates/test-utils/src/prj.rs +++ b/crates/test-utils/src/prj.rs @@ -1,11 +1,8 @@ use crate::{init_tracing, rpc::rpc_endpoints}; use eyre::{Result, WrapErr}; use foundry_compilers::{ - ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, - artifacts::Contract, - cache::CompilerCache, - compilers::multi::MultiCompiler, - project_util::{TempProject, copy_dir}, + ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, artifacts::Contract, + cache::CompilerCache, compilers::multi::MultiCompiler, project_util::TempProject, solc::SolcSettings, }; use foundry_config::Config; @@ -25,7 +22,7 @@ use std::{ }, }; -use crate::util::{SOLC_VERSION, pretty_err}; +use crate::util::{SOLC_VERSION, copy_dir_filtered, pretty_err}; static CURRENT_DIR_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); @@ -356,12 +353,12 @@ impl TestProject { config_paths_exist(&paths, self.inner.project().cached); } - /// Copies the project's root directory to the given target + /// Copies the project's root directory to the given target, excluding build artifacts. #[track_caller] pub fn copy_to(&self, target: impl AsRef) { let target = target.as_ref(); pretty_err(target, fs::create_dir_all(target)); - pretty_err(target, copy_dir(self.root(), target)); + pretty_err(target, copy_dir_filtered(self.root(), target)); } /// Creates a file with contents `contents` in the test project's directory. The @@ -820,7 +817,7 @@ fn test_redactions() -> snapbox::Redactions { ("[GAS_COST]", r"[Gg]as cost\s*\(\d+\)"), ("[GAS_LIMIT]", r"[Gg]as limit\s*\(\d+\)"), ("[AVG_GAS]", r"μ: \d+, ~: \d+"), - ("[FILE]", r"-->.*\.sol"), + ("[FILE]", r"(-->|╭▸).*\.sol"), ("[FILE]", r"Location(.|\n)*\.rs(.|\n)*Backtrace"), ("[COMPILING_FILES]", r"Compiling \d+ files?"), ("[TX_HASH]", r"Transaction hash: 0x[0-9A-Fa-f]{64}"), @@ -836,6 +833,7 @@ fn test_redactions() -> snapbox::Redactions { "[ESTIMATED_AMOUNT_REQUIRED]", r"Estimated amount required:\s*(\d+(\.\d+)?)\s*[A-Z]{3}", ), + ("[SEED]", r"Fuzz seed: 0x[0-9A-Fa-f]+"), ]) }); REDACTIONS.clone() diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index cccde297ef967..8a31098e824b8 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -44,30 +44,28 @@ shuffled_list!( HTTP_ARCHIVE_DOMAINS, vec![ // - "reth-ethereum.ithaca.xyz/rpc", + "ethereum.reth.rs/rpc", ], ); shuffled_list!( HTTP_DOMAINS, vec![ // - "reth-ethereum.ithaca.xyz/rpc", - // "reth-ethereum-full.ithaca.xyz/rpc", + "ethereum.reth.rs/rpc", ], ); shuffled_list!( WS_ARCHIVE_DOMAINS, vec![ // - "reth-ethereum.ithaca.xyz/ws", + "ethereum.reth.rs/ws", ], ); shuffled_list!( WS_DOMAINS, vec![ // - "reth-ethereum.ithaca.xyz/ws", - // "reth-ethereum-full.ithaca.xyz/ws", + "ethereum.reth.rs/ws", ], ); @@ -108,6 +106,9 @@ pub fn rpc_endpoints() -> RpcEndpoints { ("bsc", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::BinanceSmartChain))), ("avaxTestnet", RpcEndpointUrl::Url("https://api.avax-test.network/ext/bc/C/rpc".into())), ("moonbeam", RpcEndpointUrl::Url("https://moonbeam-rpc.publicnode.com".into())), + ("polkadotTestnet", RpcEndpointUrl::Url("https://eth-rpc-testnet.polkadot.io".into())), + ("kusama", RpcEndpointUrl::Url("https://eth-rpc-kusama.polkadot.io".into())), + ("polkadot", RpcEndpointUrl::Url("https://eth-rpc.polkadot.io".into())), ("rpcEnvAlias", RpcEndpointUrl::Env("${RPC_ENV_ALIAS}".into())), ]) } @@ -136,12 +137,12 @@ pub fn next_ws_endpoint(chain: NamedChain) -> String { next_url(true, chain) } -/// Returns a websocket URL that has access to archive state +/// Returns an HTTP URL that has access to archive state pub fn next_http_archive_rpc_url() -> String { next_archive_url(false) } -/// Returns an HTTP URL that has access to archive state +/// Returns a websocket URL that has access to archive state pub fn next_ws_archive_rpc_url() -> String { next_archive_url(true) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index e265581d35a59..6fdcbafb94404 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1,24 +1,24 @@ -use foundry_compilers::{ - Project, ProjectCompileOutput, Vyper, project_util::copy_dir, utils::RuntimeOrHandle, -}; +use foundry_compilers::{Project, ProjectCompileOutput, Vyper, utils::RuntimeOrHandle}; use foundry_config::Config; use std::{ env, fs::{self, File}, - io::{IsTerminal, Read, Seek, Write}, + io::{Read, Seek, Write}, path::{Path, PathBuf}, process::Command, sync::LazyLock, }; +/// Directories to skip when copying project directories. +/// These are build artifacts and runtime-generated files that should not be copied to temp +/// workspaces. +const SKIP_DIRS: &[&str] = &["out", "cache", "broadcast"]; + pub use crate::{ext::*, prj::*}; /// The commit of forge-std to use. pub const FORGE_STD_REVISION: &str = include_str!("../../../testdata/forge-std-rev"); -/// Stores whether `stdout` is a tty / terminal. -pub static IS_TTY: LazyLock = LazyLock::new(|| std::io::stdout().is_terminal()); - /// Global default template path. Contains the global template project from which all other /// temp projects are initialized. See [`initialize()`] for more info. static TEMPLATE_PATH: LazyLock = @@ -30,7 +30,7 @@ static TEMPLATE_LOCK: LazyLock = LazyLock::new(|| env::temp_dir().join("foundry-forge-test-template.lock")); /// The default Solc version used when compiling tests. -pub const SOLC_VERSION: &str = "0.8.30"; +pub const SOLC_VERSION: &str = "0.8.33"; /// Another Solc version used when compiling tests. /// @@ -101,8 +101,8 @@ pub fn initialize(target: &Path) { // 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)); + // Copy the template to the global template path, excluding build artifacts. + pretty_err(tpath, copy_dir_filtered(prj.root(), tpath)); // Update lockfile to mark that template is initialized. write.set_len(0).unwrap(); @@ -117,7 +117,7 @@ pub fn initialize(target: &Path) { test_debug!("- copying template dir from {}", tpath.display()); pretty_err(target, fs::create_dir_all(target)); - pretty_err(target, copy_dir(tpath, target)); + pretty_err(target, copy_dir_filtered(tpath, target)); } /// Compile the project with a lock for the cache. @@ -225,3 +225,52 @@ pub fn read_string(path: impl AsRef) -> String { let path = path.as_ref(); pretty_err(path, std::fs::read_to_string(path)) } + +/// Copies the directory at `src` to `dst`, skipping build artifact directories. +/// +/// This is similar to `foundry_compilers::project_util::copy_dir`, but skips directories +/// like `out/`, `cache/`, and `broadcast/` which are build artifacts that should not be +/// copied to temporary test workspaces. +pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { + fs::create_dir_all(dst)?; + // Canonicalize the source once and treat it as the base directory for all traversal. + let base = src.canonicalize()?; + copy_dir_filtered_inner(src, dst, &base, true) +} + +fn copy_dir_filtered_inner( + src: &Path, + dst: &Path, + base: &Path, + is_root: bool, +) -> std::io::Result<()> { + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + let src_path = entry.path(); + // Ensure that any path we operate on stays within the original base directory. + let canonical_src_path = src_path.canonicalize()?; + if !canonical_src_path.starts_with(base) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed base directory", + )); + } + let dst_path = dst.join(entry.file_name()); + + if ty.is_dir() { + // Skip build artifact directories at the root level + if is_root + && let Some(name) = entry.file_name().to_str() + && SKIP_DIRS.contains(&name) + { + continue; + } + fs::create_dir_all(&dst_path)?; + copy_dir_filtered_inner(&src_path, &dst_path, base, false)?; + } else { + fs::copy(&canonical_src_path, &dst_path)?; + } + } + Ok(()) +} diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 67301a6c83dcf..efabcab549006 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -259,7 +259,7 @@ impl VerifyBytecodeArgs { gen_tx_req.gas_price = block.header.base_fee_per_gas.map(|g| g as u128); } - configure_tx_req_env(&mut env.as_env_mut(), &gen_tx_req, None) + configure_tx_req_env(&mut env.as_env_mut(), &gen_tx_req) .wrap_err("Failed to configure tx request env")?; // Seed deployer account with funds @@ -335,6 +335,7 @@ impl VerifyBytecodeArgs { ); }; + let creation_block = transaction.block_number; let mut transaction: TransactionRequest = match transaction.inner.inner.inner() { AnyTxEnvelope::Ethereum(tx) => tx.clone().into(), AnyTxEnvelope::Unknown(_) => unreachable!("Unknown transaction type"), @@ -440,13 +441,7 @@ impl VerifyBytecodeArgs { 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))?.ok_or_else(|| { - eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash) - })? - .block_number.ok_or_else(|| { + creation_block.ok_or_else(|| { eyre::eyre!("Failed to get block number of the contract creation tx, specify using the --block flag") })? } @@ -495,7 +490,7 @@ impl VerifyBytecodeArgs { break; } - configure_tx_env(&mut env.as_env_mut(), &tx.inner); + configure_tx_env(&mut env.as_env_mut(), tx); if let TxKind::Call(_) = tx.inner.kind() { executor.transact_with_env(env.clone()).wrap_err_with(|| { @@ -538,7 +533,7 @@ impl VerifyBytecodeArgs { } // configure_req__env(&mut env, &transaction.inner); - configure_tx_req_env(&mut env.as_env_mut(), &transaction, None) + configure_tx_req_env(&mut env.as_env_mut(), &transaction) .wrap_err("Failed to configure tx request env")?; let fork_address = crate::utils::deploy_contract( diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 2d9b465c00b85..b55f8f7669ce1 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -267,9 +267,8 @@ impl EtherscanVerificationProvider { || (verifier_type.is_sourcify() && etherscan_key.is_some()); let etherscan_config = config.get_etherscan_config_with_chain(Some(chain))?; - let etherscan_api_url = verifier_url.or(None).map(str::to_owned); - - let api_url = etherscan_api_url.as_deref(); + let api_url = + verifier_url.or_else(|| etherscan_config.as_ref().map(|c| c.api_url.as_str())); let base_url = etherscan_config .as_ref() .and_then(|c| c.browser_url.as_deref()) @@ -419,9 +418,9 @@ impl EtherscanVerificationProvider { .ok_or_eyre("Couldn't fetch transaction receipt from RPC")?; let maybe_creation_code = if receipt.contract_address == Some(args.address) { - transaction.inner.inner.input() + transaction.input() } else if transaction.to() == Some(DEFAULT_CREATE2_DEPLOYER) { - &transaction.inner.inner.input()[32..] + &transaction.input()[32..] } else { eyre::bail!( "Fetching of constructor arguments is not supported for contracts created by contracts" @@ -492,10 +491,8 @@ mod tests { let etherscan = EtherscanVerificationProvider::default(); let client = etherscan.client(&args.etherscan, &args.verifier, &config).unwrap(); - assert_eq!( - client.etherscan_api_url().as_str(), - "https://api.etherscan.io/v2/api?chainid=80002" - ); + // Custom URL from foundry.toml should be used + assert_eq!(client.etherscan_api_url().as_str(), "https://amoy.polygonscan.com/"); assert!(format!("{client:?}").contains("dummykey")); @@ -552,10 +549,8 @@ mod tests { let client = etherscan.client(&args.etherscan, &args.verifier, &config).unwrap(); - assert_eq!( - client.etherscan_api_url().as_str(), - "https://api.etherscan.io/v2/api?chainid=80002" - ); + // Custom URL from foundry.toml should be used + assert_eq!(client.etherscan_api_url().as_str(), "https://amoy.polygonscan.com/"); assert!(format!("{client:?}").contains("dummykey")); let args: VerifyArgs = VerifyArgs::parse_from([ diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 03315ae024176..62124f6613382 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -8,11 +8,8 @@ use async_trait::async_trait; use eyre::{OptionExt, Result}; use foundry_common::compile::ProjectCompiler; use foundry_compilers::{ - Graph, Project, - artifacts::{Metadata, Source, output_selection::OutputSelection}, - compilers::solc::SolcCompiler, - multi::{MultiCompilerParser, MultiCompilerSettings}, - solc::Solc, + Project, artifacts::output_selection::OutputSelection, compilers::solc::SolcCompiler, + multi::MultiCompilerSettings, solc::Solc, }; use foundry_config::{Chain, Config, EtherscanConfigError}; use semver::Version; @@ -50,7 +47,7 @@ impl VerificationContext { pub fn get_target_abi(&self) -> Result { let mut project = self.project.clone(); project.update_output_selection(|selection| { - *selection = OutputSelection::common_output_selection(["abi".to_string()]) + *selection = OutputSelection::common_output_selection(["abi".to_string()]); }); let output = ProjectCompiler::new() @@ -64,34 +61,6 @@ impl VerificationContext { 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.update_output_selection(|selection| { - *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, &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 metadata") - } - - /// 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().map(Into::into).collect()) - } } /// An abstraction for various verification providers such as etherscan, sourcify, blockscout @@ -189,6 +158,7 @@ impl VerificationProviderType { if self.is_etherscan() { if let Some(chain) = chain && chain.etherscan_urls().is_none() + && !has_url { eyre::bail!(EtherscanConfigError::UnknownChain( "when using Etherscan verifier".to_string(), @@ -229,3 +199,27 @@ impl VerificationProviderType { matches!(self, Self::Etherscan) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn etherscan_allows_unknown_chain_with_verifier_url() { + let chain = Chain::from(3658348u64); + let res = VerificationProviderType::Etherscan.client(Some("key"), Some(chain), true); + assert!(res.is_ok()); + } + + #[test] + fn etherscan_rejects_unknown_chain_without_verifier_url() { + let chain = Chain::from(3658348u64); + let res = VerificationProviderType::Etherscan.client(Some("key"), Some(chain), false); + match res { + Ok(_) => panic!("expected unknown-chain error"), + Err(err) => { + assert!(err.to_string().contains("No known Etherscan API URL")); + } + } + } +} diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 603fd8d99b775..3aacfc1926996 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -20,8 +20,13 @@ use foundry_common::{ use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}; use foundry_config::Config; use foundry_evm::{ - Env, EnvMut, constants::DEFAULT_CREATE2_DEPLOYER, core::AsEnvMut, executors::TracingExecutor, - opts::EvmOpts, traces::TraceMode, utils::apply_chain_and_block_specific_env_changes, + Env, EnvMut, + constants::DEFAULT_CREATE2_DEPLOYER, + core::{AsEnvMut, decode::RevertDecoder}, + executors::TracingExecutor, + opts::EvmOpts, + traces::TraceMode, + utils::apply_chain_and_block_specific_env_changes, }; use foundry_evm_networks::NetworkConfigs; use reqwest::Url; @@ -320,9 +325,28 @@ pub fn deploy_contract( let result = executor.transact_with_env(env)?; trace!(transact_result = ?result.exit_reason); + + if result.reverted { + let decoded_reason = if result.result.is_empty() { + String::new() + } else { + format!(": {}", RevertDecoder::default().decode(&result.result, result.exit_reason)) + }; + eyre::bail!( + "Failed to deploy contract via CREATE2 on fork at block{decoded_reason}.\n\ + This typically happens when your local bytecode differs from what was actually deployed.\n\ + Common causes:\n\ + - Your contract source is not at the same commit used during deployment\n\ + - Cached build artifacts are stale (try `forge clean && forge build`)\n\ + - Compiler settings (optimizer, evm_version, via_ir) don't match the deployment" + ); + } + if result.result.len() != 20 { eyre::bail!( - "Failed to deploy contract on fork at block: call result is not exactly 20 bytes" + "Failed to deploy contract via CREATE2 on fork at block: deployer returned {} bytes instead of 20.\n\ + This may indicate a bytecode mismatch - ensure your source code matches the deployed contract.", + result.result.len() ); } @@ -358,11 +382,8 @@ pub async fn get_runtime_codes( ) })?; - let onchain_runtime_code = if let Some(block) = block { - provider.get_code_at(address).block_id(BlockId::number(block)).await? - } else { - provider.get_code_at(address).await? - }; + let block_id = block.map_or_else(BlockId::latest, BlockId::number); + let onchain_runtime_code = provider.get_code_at(address).block_id(block_id).await?; Ok((fork_runtime_code, onchain_runtime_code)) } diff --git a/crates/verify/src/verify.rs b/crates/verify/src/verify.rs index f9ce32a415633..cab8462ebb25e 100644 --- a/crates/verify/src/verify.rs +++ b/crates/verify/src/verify.rs @@ -16,7 +16,9 @@ use foundry_cli::{ }; use foundry_common::{ContractsByArtifact, compile::ProjectCompiler}; use foundry_compilers::{artifacts::EvmVersion, compilers::solc::Solc, info::ContractInfo}; -use foundry_config::{Config, SolcReq, figment, impl_figment_convert, impl_figment_convert_cast}; +use foundry_config::{ + Chain, Config, SolcReq, figment, impl_figment_convert, impl_figment_convert_cast, +}; use itertools::Itertools; use reqwest::Url; use semver::BuildMetadata; @@ -248,6 +250,14 @@ impl VerifyArgs { self.etherscan.chain = Some(chain); self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); + // For chains with Sourcify-compatible APIs, use the chain's URL from etherscan_urls + if self.verifier.verifier.is_sourcify() + && self.verifier.verifier_url.is_none() + && let Some(url) = sourcify_api_url(chain) + { + self.verifier.verifier_url = Some(url); + } + if self.show_standard_json_input { let args = EtherscanVerificationProvider::default() .create_verify_request(&self, &context) @@ -275,18 +285,17 @@ impl VerifyArgs { self.verifier.verifier.client(self.etherscan.key().as_deref(), self.etherscan.chain, self.verifier.verifier_url.is_some())?.verify(self, context).await.map_err(|err| { if let Some(verifier_url) = verifier_url { match Url::parse(&verifier_url) { - Ok(url) => { - if is_host_only(&url) { - return err.wrap_err(format!( - "Provided URL `{verifier_url}` is host only.\n Did you mean to use the API endpoint`{verifier_url}/api` ?" - )) - } + Ok(url) if is_host_only(&url) => { + return err.wrap_err(format!( + "Provided URL `{verifier_url}` is host only.\n Did you mean to use the API endpoint`{verifier_url}/api` ?" + )) } Err(url_err) => { return err.wrap_err(format!( "Invalid URL {verifier_url} provided: {url_err}" )) } + _ => {} } } @@ -412,7 +421,7 @@ impl VerifyArgs { &project.settings } else { eyre::bail!( - "If cache is disabled, compilation profile must be provided with `--compiler-version` option or set in foundry.toml" + "If cache is disabled, compilation profile must be provided with `--compilation-profile` option or set in foundry.toml" ) }; @@ -528,6 +537,21 @@ impl figment::Provider for VerifyCheckArgs { } } +/// Returns the Sourcify-compatible API URL for chains that have one registered in `etherscan_urls`. +/// +/// Some chains register their Sourcify-compatible verification API under `etherscan_urls` in +/// alloy-chains. This function returns the properly formatted URL for such chains. +fn sourcify_api_url(chain: Chain) -> Option { + if chain.is_custom_sourcify() { + chain.etherscan_urls().map(|(api_url, _)| { + let api_url = api_url.trim_end_matches('/'); + format!("{api_url}/") + }) + } else { + None + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 24013c74f925a..e68de25e9b06e 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -14,7 +14,6 @@ workspace = true [dependencies] foundry-config.workspace = true -foundry-primitives.workspace = true alloy-primitives.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } @@ -26,10 +25,7 @@ alloy-consensus.workspace = true alloy-sol-types.workspace = true alloy-dyn-abi.workspace = true -tempo-primitives.workspace = true - # browser wallet -alloy-rpc-types.workspace = true axum.workspace = true foundry-common.workspace = true serde_json.workspace = true @@ -60,7 +56,6 @@ tracing.workspace = true eth-keystore = "0.5.0" [dev-dependencies] -tokio = { workspace = true, features = ["macros"] } reqwest = { workspace = true, features = ["json"] } [features] diff --git a/crates/wallets/src/lib.rs b/crates/wallets/src/lib.rs index e29e6484e2c15..f27e908b63a40 100644 --- a/crates/wallets/src/lib.rs +++ b/crates/wallets/src/lib.rs @@ -21,6 +21,7 @@ pub mod wallet_raw; pub use opts::WalletOpts; pub use signer::{PendingSigner, WalletSigner}; +pub use wallet_browser::opts::BrowserWalletOpts; pub use wallet_multi::MultiWalletOpts; pub use wallet_raw::RawWalletOpts; diff --git a/crates/wallets/src/opts.rs b/crates/wallets/src/opts.rs index 9265a7c79ebbb..d1c4a97690411 100644 --- a/crates/wallets/src/opts.rs +++ b/crates/wallets/src/opts.rs @@ -102,36 +102,6 @@ pub struct WalletOpts { /// See: #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "turnkey"))] pub turnkey: bool, - - /// Use a browser wallet. - #[arg(long, help_heading = "Wallet options - browser")] - pub browser: bool, - - /// Port for the browser wallet server. - #[arg( - long, - help_heading = "Wallet options - browser", - value_name = "PORT", - default_value = "9545", - requires = "browser" - )] - pub browser_port: u16, - - /// Whether to open the browser for wallet connection. - #[arg( - long, - help_heading = "Wallet options - browser", - default_value_t = false, - requires = "browser" - )] - pub browser_disable_open: bool, - - /// Enable development mode for the browser wallet. - /// This relaxes certain security features for local development. - /// - /// **WARNING**: This should only be used in a development environment. - #[arg(long, help_heading = "Wallet options - browser", hide = true)] - pub browser_development: bool, } impl WalletOpts { @@ -169,13 +139,6 @@ impl WalletOpts { eyre::eyre!("TURNKEY_ADDRESS could not be parsed as an Ethereum address") })?; WalletSigner::from_turnkey(api_private_key, organization_id, address)? - } else if self.browser { - WalletSigner::from_browser( - self.browser_port, - !self.browser_disable_open, - self.browser_development, - ) - .await? } else if let Some(raw_wallet) = self.raw.signer()? { raw_wallet } else if let Some(path) = utils::maybe_get_keystore_path( @@ -281,10 +244,6 @@ mod tests { aws: false, gcp: false, turnkey: false, - browser: false, - browser_port: 9545, - browser_development: false, - browser_disable_open: false, }; match wallet.signer().await { Ok(_) => { diff --git a/crates/wallets/src/signer.rs b/crates/wallets/src/signer.rs index 21ab2c8530162..ddbd5a47fe978 100644 --- a/crates/wallets/src/signer.rs +++ b/crates/wallets/src/signer.rs @@ -1,7 +1,7 @@ -use crate::{error::WalletSignerError, wallet_browser::signer::BrowserSigner}; -use alloy_consensus::{Sealed, SignableTransaction}; +use crate::error::WalletSignerError; +use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; -use alloy_network::{NetworkWallet, TransactionBuilder, TxSigner}; +use alloy_network::TxSigner; use alloy_primitives::{Address, B256, ChainId, Signature, hex}; use alloy_signer::Signer; use alloy_signer_ledger::{HDPath as LedgerHDPath, LedgerSigner}; @@ -9,11 +9,7 @@ use alloy_signer_local::{MnemonicBuilder, PrivateKeySigner, coins_bip39::English use alloy_signer_trezor::{HDPath as TrezorHDPath, TrezorSigner}; use alloy_sol_types::{Eip712Domain, SolStruct}; use async_trait::async_trait; -use foundry_primitives::{ - FoundryNetwork, FoundryTransactionRequest, FoundryTxEnvelope, FoundryTypedTx, -}; -use std::{collections::HashSet, path::PathBuf, time::Duration}; -use tempo_primitives::TempoSignature; +use std::{collections::HashSet, path::PathBuf}; use tracing::warn; #[cfg(feature = "aws-kms")] @@ -42,8 +38,6 @@ pub enum WalletSigner { Ledger(LedgerSigner), /// Wrapper around Trezor signer. Trezor(TrezorSigner), - /// Wrapper around browser wallet. - Browser(BrowserSigner), /// Wrapper around AWS KMS signer. #[cfg(feature = "aws-kms")] Aws(AwsSigner), @@ -66,18 +60,6 @@ impl WalletSigner { Ok(Self::Trezor(trezor)) } - pub async fn from_browser( - port: u16, - open_browser: bool, - browser_development: bool, - ) -> Result { - let browser_signer = - BrowserSigner::new(port, open_browser, Duration::from_secs(300), browser_development) - .await - .map_err(|e| WalletSignerError::Browser(e.into()))?; - Ok(Self::Browser(browser_signer)) - } - pub async fn from_aws(key_id: String) -> Result { #[cfg(feature = "aws-kms")] { @@ -164,7 +146,7 @@ impl WalletSigner { let _ = api_private_key; let _ = organization_id; let _ = address; - Err(WalletSignerError::UnsupportedSigner("Turnkey")) + Err(WalletSignerError::turnkey_unsupported()) } } @@ -223,9 +205,6 @@ impl WalletSigner { } } } - Self::Browser(browser) => { - senders.insert(alloy_signer::Signer::address(browser)); - } #[cfg(feature = "aws-kms")] Self::Aws(aws) => { senders.insert(alloy_signer::Signer::address(aws)); @@ -270,7 +249,6 @@ macro_rules! delegate { Self::Local($inner) => $e, Self::Ledger($inner) => $e, Self::Trezor($inner) => $e, - Self::Browser($inner) => $e, #[cfg(feature = "aws-kms")] Self::Aws($inner) => $e, #[cfg(feature = "gcp-kms")] @@ -337,74 +315,6 @@ impl TxSigner for WalletSigner { } } -impl NetworkWallet for WalletSigner { - fn default_signer_address(&self) -> Address { - alloy_signer::Signer::address(self) - } - - fn has_signer_for(&self, address: &Address) -> bool { - self.default_signer_address() == *address - } - - fn signer_addresses(&self) -> impl Iterator { - std::iter::once(self.default_signer_address()) - } - - async fn sign_transaction_from( - &self, - sender: Address, - tx: FoundryTypedTx, - ) -> alloy_signer::Result { - if sender != self.default_signer_address() { - return Err(alloy_signer::Error::other("Signer address mismatch")); - } - - match tx { - FoundryTypedTx::Legacy(mut inner) => { - let sig = TxSigner::sign_transaction(self, &mut inner).await?; - Ok(FoundryTxEnvelope::Legacy(inner.into_signed(sig))) - } - FoundryTypedTx::Eip2930(mut inner) => { - let sig = TxSigner::sign_transaction(self, &mut inner).await?; - Ok(FoundryTxEnvelope::Eip2930(inner.into_signed(sig))) - } - FoundryTypedTx::Eip1559(mut inner) => { - let sig = TxSigner::sign_transaction(self, &mut inner).await?; - Ok(FoundryTxEnvelope::Eip1559(inner.into_signed(sig))) - } - FoundryTypedTx::Eip4844(mut inner) => { - let sig = TxSigner::sign_transaction(self, &mut inner).await?; - Ok(FoundryTxEnvelope::Eip4844(inner.into_signed(sig))) - } - FoundryTypedTx::Eip7702(mut inner) => { - let sig = TxSigner::sign_transaction(self, &mut inner).await?; - Ok(FoundryTxEnvelope::Eip7702(inner.into_signed(sig))) - } - FoundryTypedTx::Deposit(inner) => { - // Deposit transactions don't require signing - Ok(FoundryTxEnvelope::Deposit(Sealed::new(inner))) - } - FoundryTypedTx::Tempo(mut inner) => { - let sig = TxSigner::sign_transaction(self, &mut inner).await?; - let tempo_sig: TempoSignature = sig.into(); - Ok(FoundryTxEnvelope::Tempo(inner.into_signed(tempo_sig))) - } - } - } - - #[doc(hidden)] - async fn sign_request( - &self, - request: FoundryTransactionRequest, - ) -> alloy_signer::Result { - let sender = request.from().unwrap_or_else(|| self.default_signer_address()); - let tx = request.build_typed_tx().map_err(|_| { - alloy_signer::Error::other("Failed to build typed transaction from request") - })?; - self.sign_transaction_from(sender, tx).await - } -} - /// Signers that require user action to be obtained. #[derive(Debug, Clone)] pub enum PendingSigner { diff --git a/crates/wallets/src/wallet_browser/app/assets/main.js b/crates/wallets/src/wallet_browser/app/assets/main.js index 88265ee32ed48..060f0198f6c2c 100644 --- a/crates/wallets/src/wallet_browser/app/assets/main.js +++ b/crates/wallets/src/wallet_browser/app/assets/main.js @@ -63,7 +63,7 @@ Resources:`;for(let t of c){if(!RB(t))throw new VB({field:`resources`,metaMessag [zustand devtools middleware] Unsupported __setState action format. When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } - `);let t=e.state[s];if(t==null)return;JSON.stringify(i.getState())!==JSON.stringify(t)&&p(t);return}i.dispatchFromDevtools&&typeof i.dispatch==`function`&&i.dispatch(e)});case`DISPATCH`:switch(e.payload.type){case`RESET`:return p(m),s===void 0?u?.init(i.getState()):u?.init(pV(c.name));case`COMMIT`:if(s===void 0){u?.init(i.getState());return}return u?.init(pV(c.name));case`ROLLBACK`:return vV(e.state,e=>{if(s===void 0){p(e),u?.init(i.getState());return}p(e[s]),u?.init(pV(c.name))});case`JUMP_TO_STATE`:case`JUMP_TO_ACTION`:return vV(e.state,e=>{if(s===void 0){p(e);return}JSON.stringify(i.getState())!==JSON.stringify(e[s])&&p(e[s])});case`IMPORT_STATE`:{let{nextLiftedState:t}=e.payload,n=t.computedStates.slice(-1)[0]?.state;if(!n)return;p(s===void 0?n:n[s]),u?.send(null,t);return}case`PAUSE_RECORDING`:return f=!f}return}}),m},vV=(e,t)=>{let n;try{n=JSON.parse(e)}catch(e){console.error(`[zustand devtools middleware] Could not parse the received json`,e)}n!==void 0&&t(n)},yV=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};function bV(e,t){let n;try{n=e()}catch{return}return{getItem:e=>{let r=e=>e===null?null:JSON.parse(e,t?.reviver),i=n.getItem(e)??null;return i instanceof Promise?i.then(r):r(i)},setItem:(e,r)=>n.setItem(e,JSON.stringify(r,t?.replacer)),removeItem:e=>n.removeItem(e)}}var xV=e=>t=>{try{let n=e(t);return n instanceof Promise?n:{then(e){return xV(e)(n)},catch(e){return this}}}catch(e){return{then(e){return this},catch(t){return xV(t)(e)}}}},SV=(e,t)=>(n,r,i)=>{let a={storage:bV(()=>localStorage),partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1,s=new Set,c=new Set,l=a.storage;if(!l)return e((...e)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),n(...e)},r,i);let u=()=>{let e=a.partialize({...r()});return l.setItem(a.name,{state:e,version:a.version})},d=i.setState;i.setState=(e,t)=>(d(e,t),u());let f=e((...e)=>(n(...e),u()),r,i);i.getInitialState=()=>f;let p,m=()=>{if(!l)return;o=!1,s.forEach(e=>e(r()??f));let e=a.onRehydrateStorage?.call(a,r()??f)||void 0;return xV(l.getItem.bind(l))(a.name).then(e=>{if(e)if(typeof e.version==`number`&&e.version!==a.version){if(a.migrate){let t=a.migrate(e.state,e.version);return t instanceof Promise?t.then(e=>[!0,e]):[!0,t]}console.error(`State loaded from storage couldn't be migrated since no migrate function was provided`)}else return[!1,e.state];return[!1,void 0]}).then(e=>{let[t,i]=e;if(p=a.merge(i,r()??f),n(p,!0),t)return u()}).then(()=>{e?.(p,void 0),p=r(),o=!0,c.forEach(e=>e(p))}).catch(t=>{e?.(void 0,t)})};return i.persist={setOptions:e=>{a={...a,...e},e.storage&&(l=e.storage)},clearStorage:()=>{l?.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>m(),hasHydrated:()=>o,onHydrate:e=>(s.add(e),()=>{s.delete(e)}),onFinishHydration:e=>(c.add(e),()=>{c.delete(e)})},a.skipHydration||m(),p||f},CV=e=>{let t,n=new Set,r=(e,r)=>{let i=typeof e==`function`?e(t):e;if(!Object.is(i,t)){let e=t;t=r??(typeof i!=`object`||!i)?i:Object.assign({},t,i),n.forEach(n=>n(t,e))}},i=()=>t,a={setState:r,getState:i,getInitialState:()=>o,subscribe:e=>(n.add(e),()=>n.delete(e))},o=t=e(r,i,a);return a},wV=(e=>e?CV(e):CV);function TV(e){return new Promise((t,n)=>{e.oncomplete=e.onsuccess=()=>t(e.result),e.onabort=e.onerror=()=>n(e.error)})}function EV(e,t){let n,r=()=>{if(n)return n;let r=indexedDB.open(e);return r.onupgradeneeded=()=>r.result.createObjectStore(t),n=TV(r),n.then(e=>{e.onclose=()=>n=void 0},()=>{}),n};return(e,n)=>r().then(r=>n(r.transaction(t,e).objectStore(t)))}var DV;function OV(){return DV||=EV(`keyval-store`,`keyval`),DV}function kV(e,t=OV()){return t(`readonly`,t=>TV(t.get(e)))}function AV(e,t,n=OV()){return n(`readwrite`,n=>(n.put(t,e),TV(n.transaction)))}function jV(e,t=OV()){return t(`readwrite`,t=>(t.delete(e),TV(t.transaction)))}function MV(e){return e}function NV(){let e=typeof indexedDB<`u`?EV(`porto`,`store`):void 0;return MV({async getItem(t){let n=await kV(t,e);return n===null?null:n},async removeItem(t){await jV(t,e)},async setItem(t,n){await AV(t,pN(n),e)},sizeLimit:1024*1024*50})}function PV(){let e=new Map;return MV({getItem(t){return e.get(t)??null},removeItem(t){e.delete(t)},setItem(t,n){e.set(t,n)},sizeLimit:1/0})}var FV=typeof window<`u`&&typeof document<`u`;const IV={announceProvider:!0,chains:CM,mode:FV?lV({host:bN.prod}):cV(),relay:zv(rz.prod.http),storage:FV&&typeof indexedDB<`u`?NV():PV(),storageKey:`porto.store`};function LV(e={}){let t=e.chains??IV.chains,n=Object.fromEntries(t.map(t=>[t.id,e.transports?.[t.id]??zv()])),r={announceProvider:e.announceProvider??IV.announceProvider,authUrl:e.authUrl,chains:t,feeToken:e.feeToken,merchantUrl:e.merchantUrl,mode:e.mode??IV.mode,relay:e.relay??IV.relay,storage:e.storage??IV.storage,storageKey:e.storageKey??IV.storageKey,transports:n},i=wV(_V(yV(SV(e=>({accounts:[],chainIds:r.chains.map(e=>e.id),feeToken:r.feeToken,requestQueue:[]}),{merge(e,t){let n=e,i=r.chains.find(e=>e.id===n.chainIds[0])?.id??r.chains[0].id,a=[i,...r.chains.map(e=>e.id).filter(e=>e!==i)];return{...t,...n,chainIds:a}},name:r.storageKey,partialize:e=>({accounts:e.accounts.map(e=>pN(e)),chainIds:e.chainIds}),storage:r.storage,version:5})))),a=r.mode,o={config:r,getMode(){return a},id:hN(),setMode(e){return c?.(),a=e,c=e.setup({internal:o}),c},store:i},s=gB(o),c=a===null?()=>{}:a.setup({internal:o});return{_internal:o,config:r,destroy(){c(),s._internal.destroy()},provider:s}}const RV=Object.freeze(Object.values(xM)),zV=e=>RV.find(t=>t.id===e);var BV=e=>{if(typeof e==`number`)return Number.isFinite(e)?e:void 0;if(typeof e!=`string`)return;let t=e.trim();if(/^0x[0-9a-fA-F]+$/.test(t)){let e=Number.parseInt(t,16);return Number.isNaN(e)?void 0:e}if(/^\d+$/.test(t)){let e=Number.parseInt(t,10);return Number.isNaN(e)?void 0:e}};const VV=(e,t,n)=>{let r=BV(e);t(r),n(r==null?void 0:zV(r))},HV=async(e,t=`GET`,n)=>{let r={"Content-Type":`application/json`},i=typeof window<`u`?window.__SESSION_TOKEN__:void 0;i&&(r[`X-Session-Token`]=i);let a=await fetch(`http://127.0.0.1:9545${e}`,{method:t,headers:r,body:n===void 0?void 0:JSON.stringify(n)});if(!a.ok)throw Error(`API request failed: ${a.status} ${a.statusText}`);try{return await a.json()}catch{throw Error(`Invalid JSON response`)}},UV=e=>JSON.stringify(e,(e,t)=>typeof t==`bigint`?t.toString():t,2),WV=e=>{if(e==null)return UV(e);if(typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`){let t=e;try{let n=JSON.parse(t.message);return UV({...e,message:n})}catch{return UV(e)}}return UV(e)},GV=e=>!!e&&e.status===`ok`;var KV=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),$=u(s(((e,t)=>{t.exports=KV()}))());function qV(){(0,y.useEffect)(()=>{window.__PORTO__||(window.__PORTO__=LV())},[]);let[e,t]=(0,y.useState)([]),[n,r]=(0,y.useState)(!1),[i,a]=(0,y.useState)(null),[o,s]=(0,y.useState)(null),[c,l]=(0,y.useState)(null),u=e.find(e=>e.info.uuid===c)??null,[d,f]=(0,y.useState)(),[p,m]=(0,y.useState)(),[h,g]=(0,y.useState)(),[_,v]=(0,y.useState)(null),[b,x]=(0,y.useState)(null),[S,C]=(0,y.useState)(null),w=(0,y.useRef)(null),ee=(0,y.useRef)(null),te=(0,y.useRef)(null),ne=async()=>{if(!u||n)return;let e=await u.provider.request({method:`eth_requestAccounts`});f(e?.[0]??void 0);try{let e=await u.provider.request({method:`eth_chainId`});VV(e,m,g)}catch{m(void 0),g(void 0)}},re=async()=>{if(!(!d||p==null)){try{await HV(`/api/connection`,`POST`,[d,p])}catch{return}r(!0)}},ie=async()=>{if(!u||!o)return;let{id:e,signType:t,request:n}=o,r=n.address,i=n.message;try{let n;switch(t){case`PersonalSign`:n=await u.provider.request({method:`personal_sign`,params:[i,r]});break;case`SignTypedDataV4`:n=await u.provider.request({method:`eth_signTypedData_v4`,params:[r,i]});break;default:throw Error(`Unsupported signType: ${t}`)}await HV(`/api/signing/response`,`POST`,{id:e,signature:n,error:null}),C(n),s(null)}catch(t){let n=typeof t==`object`&&t&&`message`in t&&typeof t.message==`string`?t.message:String(t);try{await HV(`/api/signing/response`,`POST`,{id:e,signature:null,error:n})}catch{}C(null),s(null)}},ae=async()=>{if(!u||!i?.request)return;let e=Mv({transport:Pv(u.provider),chain:h??void 0});try{let t=await u.provider.request({method:`eth_sendTransaction`,params:[i.request]});x(t),await HV(`/api/transaction/response`,`POST`,{id:i.id,hash:t,error:null});let n=await uv(e,{hash:t});v(n)}catch(e){let t=typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`?e.message:String(e);console.error(`send failed:`,t);try{await HV(`/api/transaction/response`,`POST`,{id:i.id,hash:null,error:t})}catch{}}},oe=(0,y.useCallback)(()=>{w.current&&=(window.clearInterval(w.current),null),ee.current&&=(window.clearInterval(ee.current),null),a(null),s(null),x(null),v(null),f(void 0),m(void 0),g(void 0),r(!1),HV(`/api/connection`,`POST`,null)},[]);return(0,y.useEffect)(()=>{te.current&&c&&te.current!==c&&oe(),te.current=c},[c,oe]),(0,y.useEffect)(()=>{e.length===1&&!u&&l(e[0].info.uuid)},[e,u]),(0,y.useEffect)(()=>{let e=e=>{let{info:n,provider:r}=e.detail;t(e=>e.some(e=>e.info.uuid===n.uuid)?e:[...e,{info:n,provider:r}])};return window.addEventListener(`eip6963:announceProvider`,e),window.dispatchEvent(new Event(`eip6963:requestProvider`)),()=>window.removeEventListener(`eip6963:announceProvider`,e)},[]),(0,y.useEffect)(()=>{if(!u)return;let e=e=>{n||f(e[0]??void 0)},t=e=>{n||VV(e,m,g)};return u.provider.on?.(`accountsChanged`,e),u.provider.on?.(`chainChanged`,t),()=>{u.provider.removeListener?.(`accountsChanged`,e),u.provider.removeListener?.(`chainChanged`,t)}},[u,n]),(0,y.useEffect)(()=>{if(!n||i||o)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await HV(`/api/transaction/request`);GV(n)&&(window.clearInterval(t),e&&a(n.data))}catch{}},1e3);return w.current=t,()=>{e=!1,window.clearInterval(t),w.current===t&&(w.current=null)}},[n,i,o]),(0,y.useEffect)(()=>{if(!n||o||i)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await HV(`/api/signing/request`);GV(n)&&(window.clearInterval(t),e&&s(n.data))}catch{}},1e3);return ee.current=t,()=>{e=!1,window.clearInterval(t),ee.current===t&&(ee.current=null)}},[n,o,i]),(0,$.jsx)(`div`,{className:`wrapper`,children:(0,$.jsxs)(`div`,{className:`container`,children:[(0,$.jsx)(`div`,{className:`notice`,children:`Browser wallet is still in early development. Use with caution!`}),(0,$.jsx)(`img`,{className:`banner`,src:`banner.png`,alt:`Foundry Browser Wallet`}),e.length>1&&(0,$.jsx)(`div`,{className:`wallet-selector`,children:(0,$.jsx)(`label`,{children:(0,$.jsxs)(`select`,{value:c??``,onChange:e=>l(e.target.value||null),disabled:n,children:[(0,$.jsx)(`option`,{value:``,disabled:!0,children:`Select wallet…`}),e.map(({info:e})=>(0,$.jsxs)(`option`,{value:e.uuid,children:[e.name,` (`,e.rdns,`)`]},e.uuid))]})})}),e.length===0&&(0,$.jsx)(`p`,{children:`No wallets found.`}),u&&!d&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-connect`,onClick:ne,disabled:n,children:`Connect Wallet`}),u&&d&&!n&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-confirm`,onClick:re,disabled:!d||p==null,children:`Confirm Connection`}),u&&d&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Connected`}),(0,$.jsx)(`pre`,{className:`box`,children:`\ + `);let t=e.state[s];if(t==null)return;JSON.stringify(i.getState())!==JSON.stringify(t)&&p(t);return}i.dispatchFromDevtools&&typeof i.dispatch==`function`&&i.dispatch(e)});case`DISPATCH`:switch(e.payload.type){case`RESET`:return p(m),s===void 0?u?.init(i.getState()):u?.init(pV(c.name));case`COMMIT`:if(s===void 0){u?.init(i.getState());return}return u?.init(pV(c.name));case`ROLLBACK`:return vV(e.state,e=>{if(s===void 0){p(e),u?.init(i.getState());return}p(e[s]),u?.init(pV(c.name))});case`JUMP_TO_STATE`:case`JUMP_TO_ACTION`:return vV(e.state,e=>{if(s===void 0){p(e);return}JSON.stringify(i.getState())!==JSON.stringify(e[s])&&p(e[s])});case`IMPORT_STATE`:{let{nextLiftedState:t}=e.payload,n=t.computedStates.slice(-1)[0]?.state;if(!n)return;p(s===void 0?n:n[s]),u?.send(null,t);return}case`PAUSE_RECORDING`:return f=!f}return}}),m},vV=(e,t)=>{let n;try{n=JSON.parse(e)}catch(e){console.error(`[zustand devtools middleware] Could not parse the received json`,e)}n!==void 0&&t(n)},yV=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};function bV(e,t){let n;try{n=e()}catch{return}return{getItem:e=>{let r=e=>e===null?null:JSON.parse(e,t?.reviver),i=n.getItem(e)??null;return i instanceof Promise?i.then(r):r(i)},setItem:(e,r)=>n.setItem(e,JSON.stringify(r,t?.replacer)),removeItem:e=>n.removeItem(e)}}var xV=e=>t=>{try{let n=e(t);return n instanceof Promise?n:{then(e){return xV(e)(n)},catch(e){return this}}}catch(e){return{then(e){return this},catch(t){return xV(t)(e)}}}},SV=(e,t)=>(n,r,i)=>{let a={storage:bV(()=>localStorage),partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1,s=new Set,c=new Set,l=a.storage;if(!l)return e((...e)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),n(...e)},r,i);let u=()=>{let e=a.partialize({...r()});return l.setItem(a.name,{state:e,version:a.version})},d=i.setState;i.setState=(e,t)=>(d(e,t),u());let f=e((...e)=>(n(...e),u()),r,i);i.getInitialState=()=>f;let p,m=()=>{if(!l)return;o=!1,s.forEach(e=>e(r()??f));let e=a.onRehydrateStorage?.call(a,r()??f)||void 0;return xV(l.getItem.bind(l))(a.name).then(e=>{if(e)if(typeof e.version==`number`&&e.version!==a.version){if(a.migrate){let t=a.migrate(e.state,e.version);return t instanceof Promise?t.then(e=>[!0,e]):[!0,t]}console.error(`State loaded from storage couldn't be migrated since no migrate function was provided`)}else return[!1,e.state];return[!1,void 0]}).then(e=>{let[t,i]=e;if(p=a.merge(i,r()??f),n(p,!0),t)return u()}).then(()=>{e?.(p,void 0),p=r(),o=!0,c.forEach(e=>e(p))}).catch(t=>{e?.(void 0,t)})};return i.persist={setOptions:e=>{a={...a,...e},e.storage&&(l=e.storage)},clearStorage:()=>{l?.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>m(),hasHydrated:()=>o,onHydrate:e=>(s.add(e),()=>{s.delete(e)}),onFinishHydration:e=>(c.add(e),()=>{c.delete(e)})},a.skipHydration||m(),p||f},CV=e=>{let t,n=new Set,r=(e,r)=>{let i=typeof e==`function`?e(t):e;if(!Object.is(i,t)){let e=t;t=r??(typeof i!=`object`||!i)?i:Object.assign({},t,i),n.forEach(n=>n(t,e))}},i=()=>t,a={setState:r,getState:i,getInitialState:()=>o,subscribe:e=>(n.add(e),()=>n.delete(e))},o=t=e(r,i,a);return a},wV=(e=>e?CV(e):CV);function TV(e){return new Promise((t,n)=>{e.oncomplete=e.onsuccess=()=>t(e.result),e.onabort=e.onerror=()=>n(e.error)})}function EV(e,t){let n,r=()=>{if(n)return n;let r=indexedDB.open(e);return r.onupgradeneeded=()=>r.result.createObjectStore(t),n=TV(r),n.then(e=>{e.onclose=()=>n=void 0},()=>{}),n};return(e,n)=>r().then(r=>n(r.transaction(t,e).objectStore(t)))}var DV;function OV(){return DV||=EV(`keyval-store`,`keyval`),DV}function kV(e,t=OV()){return t(`readonly`,t=>TV(t.get(e)))}function AV(e,t,n=OV()){return n(`readwrite`,n=>(n.put(t,e),TV(n.transaction)))}function jV(e,t=OV()){return t(`readwrite`,t=>(t.delete(e),TV(t.transaction)))}function MV(e){return e}function NV(){let e=typeof indexedDB<`u`?EV(`porto`,`store`):void 0;return MV({async getItem(t){let n=await kV(t,e);return n===null?null:n},async removeItem(t){await jV(t,e)},async setItem(t,n){await AV(t,pN(n),e)},sizeLimit:1024*1024*50})}function PV(){let e=new Map;return MV({getItem(t){return e.get(t)??null},removeItem(t){e.delete(t)},setItem(t,n){e.set(t,n)},sizeLimit:1/0})}var FV=typeof window<`u`&&typeof document<`u`;const IV={announceProvider:!0,chains:CM,mode:FV?lV({host:bN.prod}):cV(),relay:zv(rz.prod.http),storage:FV&&typeof indexedDB<`u`?NV():PV(),storageKey:`porto.store`};function LV(e={}){let t=e.chains??IV.chains,n=Object.fromEntries(t.map(t=>[t.id,e.transports?.[t.id]??zv()])),r={announceProvider:e.announceProvider??IV.announceProvider,authUrl:e.authUrl,chains:t,feeToken:e.feeToken,merchantUrl:e.merchantUrl,mode:e.mode??IV.mode,relay:e.relay??IV.relay,storage:e.storage??IV.storage,storageKey:e.storageKey??IV.storageKey,transports:n},i=wV(_V(yV(SV(e=>({accounts:[],chainIds:r.chains.map(e=>e.id),feeToken:r.feeToken,requestQueue:[]}),{merge(e,t){let n=e,i=r.chains.find(e=>e.id===n.chainIds[0])?.id??r.chains[0].id,a=[i,...r.chains.map(e=>e.id).filter(e=>e!==i)];return{...t,...n,chainIds:a}},name:r.storageKey,partialize:e=>({accounts:e.accounts.map(e=>pN(e)),chainIds:e.chainIds}),storage:r.storage,version:5})))),a=r.mode,o={config:r,getMode(){return a},id:hN(),setMode(e){return c?.(),a=e,c=e.setup({internal:o}),c},store:i},s=gB(o),c=a===null?()=>{}:a.setup({internal:o});return{_internal:o,config:r,destroy(){c(),s._internal.destroy()},provider:s}}const RV=Object.freeze(Object.values(xM)),zV=e=>RV.find(t=>t.id===e);var BV=e=>{if(typeof e==`number`)return Number.isFinite(e)?e:void 0;if(typeof e!=`string`)return;let t=e.trim();if(/^0x[0-9a-fA-F]+$/.test(t)){let e=Number.parseInt(t,16);return Number.isNaN(e)?void 0:e}if(/^\d+$/.test(t)){let e=Number.parseInt(t,10);return Number.isNaN(e)?void 0:e}};const VV=(e,t,n)=>{let r=BV(e);t(r),n(r==null?void 0:zV(r))},HV=async(e,t=`GET`,n)=>{let r={"Content-Type":`application/json`},i=typeof window<`u`?window.__SESSION_TOKEN__:void 0;i&&(r[`X-Session-Token`]=i);let a=await fetch(`http://127.0.0.1:9545${e}`,{method:t,headers:r,body:n===void 0?void 0:JSON.stringify(n)});if(!a.ok)throw Error(`API request failed: ${a.status} ${a.statusText}`);try{return await a.json()}catch{throw Error(`Invalid JSON response`)}},UV=e=>JSON.stringify(e,(e,t)=>typeof t==`bigint`?t.toString():t,2),WV=e=>{if(e==null)return UV(e);if(typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`){let t=e;try{let n=JSON.parse(t.message);return UV({...e,message:n})}catch{return UV(e)}}return UV(e)},GV=e=>!!e&&e.status===`ok`;var KV=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),$=u(s(((e,t)=>{t.exports=KV()}))());function qV(){(0,y.useEffect)(()=>{window.__PORTO__||(window.__PORTO__=LV())},[]);let[e,t]=(0,y.useState)([]),[n,r]=(0,y.useState)(!1),[i,a]=(0,y.useState)(null),[o,s]=(0,y.useState)(null),[c,l]=(0,y.useState)(null),u=e.find(e=>e.info.uuid===c)??null,[d,f]=(0,y.useState)(),[p,m]=(0,y.useState)(),[h,g]=(0,y.useState)(),[_,v]=(0,y.useState)(null),[b,x]=(0,y.useState)(null),[S,C]=(0,y.useState)(null),w=(0,y.useRef)(null),ee=(0,y.useRef)(null),te=(0,y.useRef)(null),ne=async()=>{if(!u||n)return;let e=await u.provider.request({method:`eth_requestAccounts`});f(e?.[0]??void 0);try{let e=await u.provider.request({method:`eth_chainId`});VV(e,m,g)}catch{m(void 0),g(void 0)}},re=async()=>{if(!(!d||p==null)){try{await HV(`/api/connection`,`POST`,[d,p])}catch{return}r(!0)}},ie=async()=>{if(!u||!o)return;let{id:e,signType:t,request:n}=o,r=n.address,i=n.message;try{let n;switch(t){case`PersonalSign`:n=await u.provider.request({method:`personal_sign`,params:[i,r]});break;case`SignTypedDataV4`:n=await u.provider.request({method:`eth_signTypedData_v4`,params:[r,i]});break;default:throw Error(`Unsupported signType: ${t}`)}await HV(`/api/signing/response`,`POST`,{id:e,signature:n,error:null}),C(n),s(null)}catch(t){let n=typeof t==`object`&&t&&`message`in t&&typeof t.message==`string`?t.message:String(t);try{await HV(`/api/signing/response`,`POST`,{id:e,signature:null,error:n})}catch{}C(null),s(null)}},ae=async()=>{if(!u||!i?.request)return;let e=Mv({transport:Pv(u.provider),chain:h});try{let{from:t,input:n,to:r,...a}=i.request,o=await e.sendTransaction({...a,account:t||(await e.getAddresses())[0],...n?{data:n}:{},...r?{to:r}:{},chain:h});x(o),await HV(`/api/transaction/response`,`POST`,{id:i.id,hash:o,error:null});let s=await uv(e,{hash:o});v(s)}catch(e){let t=typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`?e.message:String(e);console.error(`send failed:`,t);try{await HV(`/api/transaction/response`,`POST`,{id:i.id,hash:null,error:t})}catch{}}},oe=(0,y.useCallback)(()=>{w.current&&=(window.clearInterval(w.current),null),ee.current&&=(window.clearInterval(ee.current),null),a(null),s(null),x(null),v(null),f(void 0),m(void 0),g(void 0),r(!1),HV(`/api/connection`,`POST`,null)},[]);return(0,y.useEffect)(()=>{te.current&&c&&te.current!==c&&oe(),te.current=c},[c,oe]),(0,y.useEffect)(()=>{e.length===1&&!u&&l(e[0].info.uuid)},[e,u]),(0,y.useEffect)(()=>{let e=e=>{let{info:n,provider:r}=e.detail;t(e=>e.some(e=>e.info.uuid===n.uuid)?e:[...e,{info:n,provider:r}])};return window.addEventListener(`eip6963:announceProvider`,e),window.dispatchEvent(new Event(`eip6963:requestProvider`)),()=>window.removeEventListener(`eip6963:announceProvider`,e)},[]),(0,y.useEffect)(()=>{if(!u)return;let e=e=>{n||f(e[0]??void 0)},t=e=>{n||VV(e,m,g)};return u.provider.on?.(`accountsChanged`,e),u.provider.on?.(`chainChanged`,t),()=>{u.provider.removeListener?.(`accountsChanged`,e),u.provider.removeListener?.(`chainChanged`,t)}},[u,n]),(0,y.useEffect)(()=>{if(!n||i||o)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await HV(`/api/transaction/request`);GV(n)&&(window.clearInterval(t),e&&a(n.data))}catch{}},1e3);return w.current=t,()=>{e=!1,window.clearInterval(t),w.current===t&&(w.current=null)}},[n,i,o]),(0,y.useEffect)(()=>{if(!n||o||i)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await HV(`/api/signing/request`);GV(n)&&(window.clearInterval(t),e&&s(n.data))}catch{}},1e3);return ee.current=t,()=>{e=!1,window.clearInterval(t),ee.current===t&&(ee.current=null)}},[n,o,i]),(0,$.jsx)(`div`,{className:`wrapper`,children:(0,$.jsxs)(`div`,{className:`container`,children:[(0,$.jsx)(`div`,{className:`notice`,children:`Browser wallet is still in early development. Use with caution!`}),(0,$.jsx)(`img`,{className:`banner`,src:`banner.png`,alt:`Foundry Browser Wallet`}),e.length>1&&(0,$.jsx)(`div`,{className:`wallet-selector`,children:(0,$.jsx)(`label`,{children:(0,$.jsxs)(`select`,{value:c??``,onChange:e=>l(e.target.value||null),disabled:n,children:[(0,$.jsx)(`option`,{value:``,disabled:!0,children:`Select wallet…`}),e.map(({info:e})=>(0,$.jsxs)(`option`,{value:e.uuid,children:[e.name,` (`,e.rdns,`)`]},e.uuid))]})})}),e.length===0&&(0,$.jsx)(`p`,{children:`No wallets found.`}),u&&!d&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-connect`,onClick:ne,disabled:n,children:`Connect Wallet`}),u&&d&&!n&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-confirm`,onClick:re,disabled:!d||p==null,children:`Confirm Connection`}),u&&d&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Connected`}),(0,$.jsx)(`pre`,{className:`box`,children:`\ account: ${d} chain: ${h?`${h.name} (${p})`:p??`unknown`} rpc: ${h?.rpcUrls?.default?.http?.[0]??h?.rpcUrls?.public?.http?.[0]??`unknown`}`})]}),u&&d&&n&&!i&&!o&&!b&&!S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction To Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:`No pending transaction or signing request`})})]}),u&&d&&n&&!b&&i&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction to Sign & Send`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:UV(i.request)})}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:ae,children:`Sign & Send`})]}),u&&d&&n&&!i&&o&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Message / Data to Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:WV(o.request)})}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:ie,children:`Sign`})]}),u&&d&&b&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction Hash`}),(0,$.jsx)(`pre`,{className:`box`,children:b}),(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Receipt`}),(0,$.jsx)(`pre`,{className:`box`,children:_?UV(_):`Waiting for receipt...`})]})]}),u&&d&&n&&S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Signature Result`}),(0,$.jsx)(`pre`,{className:`box`,children:S})]})]})})}var JV=document.getElementById(`root`);if(JV)(0,v.createRoot)(JV).render((0,$.jsx)(y.StrictMode,{children:(0,$.jsx)(qV,{})}));else throw Error(`Root element with id "root" not found`); \ No newline at end of file diff --git a/crates/wallets/src/wallet_browser/handlers.rs b/crates/wallets/src/wallet_browser/handlers.rs index 918bdbb161131..4b210dca60d7f 100644 --- a/crates/wallets/src/wallet_browser/handlers.rs +++ b/crates/wallets/src/wallet_browser/handlers.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use alloy_network::Network; use axum::{ Json, extract::State, @@ -46,8 +47,8 @@ pub(crate) async fn serve_css() -> impl axum::response::IntoResponse { } /// Serve main.js with injected session token. -pub(crate) async fn serve_js( - State(state): State>, +pub(crate) async fn serve_js( + State(state): State>>, ) -> impl axum::response::IntoResponse { let token = state.session_token(); let js = format!("window.__SESSION_TOKEN__ = \"{}\";\n{}", token, contents::MAIN_JS); @@ -81,9 +82,9 @@ pub(crate) async fn serve_logo_png() -> impl axum::response::IntoResponse { /// Get the next pending transaction request. /// Route: GET /api/transaction/request -pub(crate) async fn get_next_transaction_request( - State(state): State>, -) -> Json> { +pub(crate) async fn get_next_transaction_request( + State(state): State>>, +) -> Json>> { match state.read_next_transaction_request().await { Some(tx) => Json(BrowserApiResponse::with_data(tx)), None => Json(BrowserApiResponse::error("No pending transaction request")), @@ -92,8 +93,8 @@ pub(crate) async fn get_next_transaction_request( /// Post a transaction response (signed or error). /// Route: POST /api/transaction/response -pub(crate) async fn post_transaction_response( - State(state): State>, +pub(crate) async fn post_transaction_response( + State(state): State>>, Json(body): Json, ) -> Json { // Ensure that the transaction request exists. @@ -134,8 +135,8 @@ pub(crate) async fn post_transaction_response( /// Get the next pending signing request. /// Route: GET /api/signing/request -pub(crate) async fn get_next_signing_request( - State(state): State>, +pub(crate) async fn get_next_signing_request( + State(state): State>>, ) -> Json> { match state.read_next_signing_request().await { Some(req) => Json(BrowserApiResponse::with_data(req)), @@ -145,8 +146,8 @@ pub(crate) async fn get_next_signing_request( /// Post a signing response (signature or error). /// Route: POST /api/signing/response -pub(crate) async fn post_signing_response( - State(state): State>, +pub(crate) async fn post_signing_response( + State(state): State>>, Json(body): Json, ) -> Json { // Ensure that the signing request exists. @@ -174,8 +175,8 @@ pub(crate) async fn post_signing_response( /// Get current connection information. /// Route: GET /api/connection -pub(crate) async fn get_connection_info( - State(state): State>, +pub(crate) async fn get_connection_info( + State(state): State>>, ) -> Json>> { let connection = state.get_connection().await; @@ -184,8 +185,8 @@ pub(crate) async fn get_connection_info( /// Post connection update (connect or disconnect). /// Route: POST /api/connection -pub(crate) async fn post_connection_update( - State(state): State>, +pub(crate) async fn post_connection_update( + State(state): State>>, Json(body): Json>, ) -> Json { state.set_connection(body).await; diff --git a/crates/wallets/src/wallet_browser/mod.rs b/crates/wallets/src/wallet_browser/mod.rs index 00cf2b46b1c44..dc7df0a01b107 100644 --- a/crates/wallets/src/wallet_browser/mod.rs +++ b/crates/wallets/src/wallet_browser/mod.rs @@ -1,4 +1,5 @@ pub mod error; +pub mod opts; pub mod server; pub mod signer; pub mod state; @@ -13,8 +14,8 @@ mod types; mod tests { use std::time::Duration; + use alloy_network::{Ethereum, Network, TransactionBuilder}; use alloy_primitives::{Address, Bytes, TxHash, TxKind, U256, address}; - use alloy_rpc_types::TransactionRequest; use axum::http::{HeaderMap, HeaderValue}; use tokio::task::JoinHandle; use uuid::Uuid; @@ -36,7 +37,7 @@ mod tests { #[tokio::test] async fn test_setup_server() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); // Check initial state @@ -56,7 +57,7 @@ mod tests { #[tokio::test] async fn test_connect_disconnect_wallet() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); @@ -93,7 +94,7 @@ mod tests { #[tokio::test] async fn test_switch_wallet() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); @@ -116,7 +117,7 @@ mod tests { #[tokio::test] async fn test_transaction_response_both_hash_and_error_rejected() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -151,7 +152,7 @@ mod tests { #[tokio::test] async fn test_transaction_response_neither_hash_nor_error_rejected() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -181,7 +182,7 @@ mod tests { #[tokio::test] async fn test_transaction_response_zero_hash_rejected() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -216,7 +217,7 @@ mod tests { #[tokio::test] async fn test_send_transaction_client_accept() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -252,7 +253,7 @@ mod tests { #[tokio::test] async fn test_send_transaction_client_not_requested() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -288,7 +289,7 @@ mod tests { #[tokio::test] async fn test_send_transaction_invalid_response_format() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -314,7 +315,7 @@ mod tests { #[tokio::test] async fn test_send_transaction_client_reject() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -356,7 +357,7 @@ mod tests { #[tokio::test] async fn test_send_multiple_transaction_requests() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -377,8 +378,10 @@ mod tests { .await .unwrap(); - let BrowserApiResponse::Ok(pending_tx) = - resp.json::>().await.unwrap() + let BrowserApiResponse::Ok(pending_tx) = resp + .json::>>() + .await + .unwrap() else { panic!("expected BrowserApiResponse::Ok with a pending transaction"); }; @@ -421,8 +424,10 @@ mod tests { .await .unwrap(); - let BrowserApiResponse::Ok(pending_tx) = - resp.json::>().await.unwrap() + let BrowserApiResponse::Ok(pending_tx) = resp + .json::>>() + .await + .unwrap() else { panic!("expected BrowserApiResponse::Ok with a pending transaction"); }; @@ -467,7 +472,7 @@ mod tests { #[tokio::test] async fn test_send_sign_response_both_signature_and_error_rejected() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -501,7 +506,7 @@ mod tests { #[tokio::test] async fn test_send_sign_response_neither_hash_nor_error_rejected() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -531,7 +536,7 @@ mod tests { #[tokio::test] async fn test_send_sign_client_accept() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -567,7 +572,7 @@ mod tests { #[tokio::test] async fn test_send_sign_client_not_requested() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -603,7 +608,7 @@ mod tests { #[tokio::test] async fn test_send_sign_invalid_response_format() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -629,7 +634,7 @@ mod tests { #[tokio::test] async fn test_send_sign_client_reject() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -666,7 +671,7 @@ mod tests { #[tokio::test] async fn test_send_multiple_sign_requests() { - let mut server = create_server(); + let mut server = create_server::(); let client = client_with_token(&server); server.start().await.unwrap(); connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; @@ -770,21 +775,21 @@ mod tests { } /// Helper to create a default browser wallet server. - fn create_server() -> BrowserWalletServer { + fn create_server() -> BrowserWalletServer { BrowserWalletServer::new(0, false, DEFAULT_TIMEOUT, DEFAULT_DEVELOPMENT) } /// Helper to create a reqwest client with the session token header. - fn client_with_token(server: &BrowserWalletServer) -> reqwest::Client { + fn client_with_token(server: &BrowserWalletServer) -> reqwest::Client { let mut headers = HeaderMap::new(); headers.insert("X-Session-Token", HeaderValue::from_str(server.session_token()).unwrap()); reqwest::Client::builder().default_headers(headers).build().unwrap() } /// Helper to connect a wallet to the server. - async fn connect_wallet( + async fn connect_wallet( client: &reqwest::Client, - server: &BrowserWalletServer, + server: &BrowserWalletServer, connection: Connection, ) { let resp = client @@ -795,7 +800,10 @@ mod tests { } /// Helper to disconnect a wallet from the server. - async fn disconnect_wallet(client: &reqwest::Client, server: &BrowserWalletServer) { + async fn disconnect_wallet( + client: &reqwest::Client, + server: &BrowserWalletServer, + ) { let resp = client .post(format!("http://localhost:{}/api/connection", server.port())) .json(&Option::::None) @@ -804,9 +812,9 @@ mod tests { } /// Spawn the transaction signing flow in the background and return the join handle. - async fn wait_for_transaction_signing( - server: &BrowserWalletServer, - tx_request: BrowserTransactionRequest, + async fn wait_for_transaction_signing( + server: &BrowserWalletServer, + tx_request: BrowserTransactionRequest, ) -> JoinHandle> { // Spawn the signing flow in the background let browser_server = server.clone(); @@ -819,8 +827,8 @@ mod tests { } /// Spawn the message signing flow in the background and return the join handle. - async fn wait_for_message_signing( - server: &BrowserWalletServer, + async fn wait_for_message_signing( + server: &BrowserWalletServer, sign_request: BrowserSignRequest, ) -> JoinHandle> { // Spawn the signing flow in the background @@ -834,32 +842,25 @@ mod tests { } /// Create a simple browser transaction request. - fn create_browser_transaction_request() -> (Uuid, BrowserTransactionRequest) { + fn create_browser_transaction_request() -> (Uuid, BrowserTransactionRequest) { let id = Uuid::new_v4(); - let tx = BrowserTransactionRequest { - id, - request: TransactionRequest { - from: Some(ALICE), - to: Some(TxKind::Call(BOB)), - value: Some(U256::from(1000)), - ..Default::default() - }, - }; + let request = N::TransactionRequest::default() + .with_from(ALICE) + .with_to(BOB) + .with_value(U256::from(1000)); + let tx = BrowserTransactionRequest { id, request }; (id, tx) } /// Create a different browser transaction request (from the first one). - fn create_different_browser_transaction_request() -> (Uuid, BrowserTransactionRequest) { + fn create_different_browser_transaction_request() + -> (Uuid, BrowserTransactionRequest) { let id = Uuid::new_v4(); - let tx = BrowserTransactionRequest { - id, - request: TransactionRequest { - from: Some(BOB), - to: Some(TxKind::Call(ALICE)), - value: Some(U256::from(2000)), - ..Default::default() - }, - }; + let request = N::TransactionRequest::default() + .with_from(BOB) + .with_to(ALICE) + .with_value(U256::from(2000)); + let tx = BrowserTransactionRequest { id, request }; (id, tx) } @@ -886,9 +887,9 @@ mod tests { } /// Check that the transaction request queue is empty, if not panic. - async fn check_transaction_request_queue_empty( + async fn check_transaction_request_queue_empty( client: &reqwest::Client, - server: &BrowserWalletServer, + server: &BrowserWalletServer, ) { let resp = client .get(format!("http://localhost:{}/api/transaction/request", server.port())) @@ -897,7 +898,7 @@ mod tests { .unwrap(); let BrowserApiResponse::Error { message } = - resp.json::>().await.unwrap() + resp.json::>>().await.unwrap() else { panic!("expected BrowserApiResponse::Error (no pending transaction), but got Ok"); }; @@ -906,9 +907,9 @@ mod tests { } /// Check that the transaction request matches the expected request ID and fields. - async fn check_transaction_request_content( + async fn check_transaction_request_content( client: &reqwest::Client, - server: &BrowserWalletServer, + server: &BrowserWalletServer, tx_request_id: Uuid, ) { let resp = client @@ -918,21 +919,21 @@ mod tests { .unwrap(); let BrowserApiResponse::Ok(pending_tx) = - resp.json::>().await.unwrap() + resp.json::>>().await.unwrap() else { panic!("expected BrowserApiResponse::Ok with a pending transaction"); }; assert_eq!(pending_tx.id, tx_request_id); - assert_eq!(pending_tx.request.from, Some(ALICE)); - assert_eq!(pending_tx.request.to, Some(TxKind::Call(BOB))); - assert_eq!(pending_tx.request.value, Some(U256::from(1000))); + assert_eq!(pending_tx.request.from(), Some(ALICE)); + assert_eq!(pending_tx.request.kind(), Some(TxKind::Call(BOB))); + assert_eq!(pending_tx.request.value(), Some(U256::from(1000))); } /// Check that the sign request queue is empty, if not panic. - async fn check_sign_request_queue_empty( + async fn check_sign_request_queue_empty( client: &reqwest::Client, - server: &BrowserWalletServer, + server: &BrowserWalletServer, ) { let resp = client .get(format!("http://localhost:{}/api/signing/request", server.port())) @@ -950,9 +951,9 @@ mod tests { } /// Check that the sign request matches the expected request ID and fields. - async fn check_sign_request_content( + async fn check_sign_request_content( client: &reqwest::Client, - server: &BrowserWalletServer, + server: &BrowserWalletServer, sign_request_id: Uuid, ) { let resp = client diff --git a/crates/wallets/src/wallet_browser/opts.rs b/crates/wallets/src/wallet_browser/opts.rs new file mode 100644 index 0000000000000..15e766ce1a1b0 --- /dev/null +++ b/crates/wallets/src/wallet_browser/opts.rs @@ -0,0 +1,49 @@ +use std::time::Duration; + +use clap::Parser; +use eyre::Result; +use serde::Serialize; + +use crate::wallet_browser::signer::BrowserSigner; + +/// Browser wallet options +#[derive(Clone, Debug, Default, Serialize, Parser)] +#[command(next_help_heading = "Browser wallet options")] +pub struct BrowserWalletOpts { + /// Use a browser wallet. + #[arg(long, help_heading = "")] + pub browser: bool, + + /// Port for the browser wallet server. + #[arg(long, value_name = "PORT", default_value = "9545", requires = "browser")] + pub browser_port: u16, + + /// Whether to open the browser for wallet connection. + #[arg(long, default_value_t = false, requires = "browser")] + pub browser_disable_open: bool, + + /// Enable development mode for the browser wallet. + /// This relaxes certain security features for local development. + /// + /// **WARNING**: This should only be used in a development environment. + #[arg(long, hide = true)] + pub browser_development: bool, +} + +impl BrowserWalletOpts { + pub async fn run(&self) -> Result> { + Ok(if self.browser { + Some( + BrowserSigner::new( + self.browser_port, + !self.browser_disable_open, + Duration::from_secs(300), + self.browser_development, + ) + .await?, + ) + } else { + None + }) + } +} diff --git a/crates/wallets/src/wallet_browser/queue.rs b/crates/wallets/src/wallet_browser/queue.rs index 608c7e18dbbde..5df44a09e9fde 100644 --- a/crates/wallets/src/wallet_browser/queue.rs +++ b/crates/wallets/src/wallet_browser/queue.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, VecDeque}; +use alloy_network::Network; use uuid::Uuid; use crate::wallet_browser::types::{BrowserSignRequest, BrowserTransactionRequest}; @@ -69,7 +70,7 @@ pub(crate) trait HasId { fn id(&self) -> &Uuid; } -impl HasId for BrowserTransactionRequest { +impl HasId for BrowserTransactionRequest { fn id(&self) -> &Uuid { &self.id } diff --git a/crates/wallets/src/wallet_browser/router.rs b/crates/wallets/src/wallet_browser/router.rs index f62a5d3977054..cb29e9122a5d8 100644 --- a/crates/wallets/src/wallet_browser/router.rs +++ b/crates/wallets/src/wallet_browser/router.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use alloy_network::Network; use axum::{ Router, extract::{Request, State}, @@ -13,7 +14,7 @@ use tower_http::{cors::CorsLayer, set_header::SetResponseHeaderLayer}; use crate::wallet_browser::{handlers, state::BrowserWalletState}; -pub async fn build_router(state: Arc, port: u16) -> Router { +pub async fn build_router(state: Arc>, port: u16) -> Router { let api = Router::new() .route("/transaction/request", get(handlers::get_next_transaction_request)) .route("/transaction/response", post(handlers::post_transaction_response)) @@ -76,8 +77,8 @@ pub async fn build_router(state: Arc, port: u16) -> Router { .with_state(state) } -async fn require_session_token( - State(state): State>, +async fn require_session_token( + State(state): State>>, req: Request, next: Next, ) -> Result { diff --git a/crates/wallets/src/wallet_browser/server.rs b/crates/wallets/src/wallet_browser/server.rs index 4f067c3bbcbab..507338a3bd291 100644 --- a/crates/wallets/src/wallet_browser/server.rs +++ b/crates/wallets/src/wallet_browser/server.rs @@ -5,6 +5,7 @@ use std::{ }; use alloy_dyn_abi::TypedData; +use alloy_network::Network; use alloy_primitives::{Address, Bytes, TxHash}; use tokio::{ net::TcpListener, @@ -24,15 +25,15 @@ use crate::wallet_browser::{ /// Browser wallet server. #[derive(Debug, Clone)] -pub struct BrowserWalletServer { +pub struct BrowserWalletServer { port: u16, - state: Arc, + state: Arc>, shutdown_tx: Option>>>>, open_browser: bool, timeout: Duration, } -impl BrowserWalletServer { +impl BrowserWalletServer { /// Create a new browser wallet server. pub fn new(port: u16, open_browser: bool, timeout: Duration, development: bool) -> Self { Self { @@ -118,7 +119,7 @@ impl BrowserWalletServer { /// Request a transaction to be signed and sent via the browser wallet. pub async fn request_transaction( &self, - request: BrowserTransactionRequest, + request: BrowserTransactionRequest, ) -> Result { if !self.is_connected().await { return Err(BrowserWalletError::NotConnected); diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs index 78cf166a35ad0..c698017d7ee61 100644 --- a/crates/wallets/src/wallet_browser/signer.rs +++ b/crates/wallets/src/wallet_browser/signer.rs @@ -5,9 +5,8 @@ use std::{ use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; -use alloy_network::TxSigner; +use alloy_network::{Ethereum, Network, TransactionBuilder, TxSigner}; use alloy_primitives::{Address, B256, ChainId, hex}; -use alloy_rpc_types::TransactionRequest; use alloy_signer::{Result, Signature, Signer, SignerSync}; use alloy_sol_types::{Eip712Domain, SolStruct}; use async_trait::async_trait; @@ -20,13 +19,13 @@ use crate::wallet_browser::{ }; #[derive(Clone, Debug)] -pub struct BrowserSigner { - server: Arc>, +pub struct BrowserSigner { + server: Arc>>, address: Address, chain_id: ChainId, } -impl BrowserSigner { +impl BrowserSigner { pub async fn new( port: u16, open_browser: bool, @@ -62,9 +61,9 @@ impl BrowserSigner { /// Send a transaction through the browser wallet. pub async fn send_transaction_via_browser( &self, - tx_request: TransactionRequest, + tx_request: N::TransactionRequest, ) -> Result { - if let Some(from) = tx_request.from + if let Some(from) = tx_request.from() && from != self.address { return Err(alloy_signer::Error::other( @@ -72,7 +71,7 @@ impl BrowserSigner { )); } - if let Some(chain_id) = tx_request.chain_id + if let Some(chain_id) = tx_request.chain_id() && chain_id != self.chain_id { return Err(alloy_signer::Error::other( @@ -92,7 +91,7 @@ impl BrowserSigner { } } -impl SignerSync for BrowserSigner { +impl SignerSync for BrowserSigner { fn sign_hash_sync(&self, _hash: &B256) -> Result { Err(alloy_signer::Error::other( "Browser wallets cannot sign raw hashes. Use sign_message or send_transaction instead.", @@ -111,7 +110,7 @@ impl SignerSync for BrowserSigner { } #[async_trait] -impl Signer for BrowserSigner { +impl Signer for BrowserSigner { async fn sign_hash(&self, _hash: &B256) -> Result { Err(alloy_signer::Error::other( "Browser wallets sign and send transactions in one step. Use eth_sendTransaction instead.", @@ -175,7 +174,7 @@ impl Signer for BrowserSigner { } #[async_trait] -impl TxSigner for BrowserSigner { +impl TxSigner for BrowserSigner { fn address(&self) -> Address { Signer::address(self) } @@ -188,7 +187,7 @@ impl TxSigner for BrowserSigner { } } -impl Drop for BrowserSigner { +impl Drop for BrowserSigner { fn drop(&mut self) { let server = self.server.clone(); diff --git a/crates/wallets/src/wallet_browser/state.rs b/crates/wallets/src/wallet_browser/state.rs index 07954654a7185..0fa38da9cc110 100644 --- a/crates/wallets/src/wallet_browser/state.rs +++ b/crates/wallets/src/wallet_browser/state.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use alloy_network::Network; use tokio::sync::{Mutex, RwLock}; use uuid::Uuid; @@ -12,11 +13,12 @@ use crate::wallet_browser::{ }; #[derive(Debug, Clone)] -pub(crate) struct BrowserWalletState { +pub(crate) struct BrowserWalletState { /// Current information about the wallet connection. connection: Arc>>, /// Request/response queue for transactions. - transactions: Arc>>, + transactions: + Arc, BrowserTransactionResponse>>>, /// Request/response queue for signings. signings: Arc>>, /// Unique session token for the wallet browser instance. @@ -29,7 +31,7 @@ pub(crate) struct BrowserWalletState { development: bool, } -impl BrowserWalletState { +impl BrowserWalletState { /// Create a new browser wallet state. pub fn new(session_token: String, development: bool) -> Self { Self { @@ -70,7 +72,7 @@ impl BrowserWalletState { } /// Add a transaction request. - pub async fn add_transaction_request(&self, request: BrowserTransactionRequest) { + pub async fn add_transaction_request(&self, request: BrowserTransactionRequest) { self.transactions.lock().await.add_request(request); } @@ -80,7 +82,7 @@ impl BrowserWalletState { } /// Read the next transaction request. - pub async fn read_next_transaction_request(&self) -> Option { + pub async fn read_next_transaction_request(&self) -> Option> { self.transactions.lock().await.read_request().cloned() } diff --git a/crates/wallets/src/wallet_browser/types.rs b/crates/wallets/src/wallet_browser/types.rs index 61f3edf3d85ca..da4ac924a2b41 100644 --- a/crates/wallets/src/wallet_browser/types.rs +++ b/crates/wallets/src/wallet_browser/types.rs @@ -1,6 +1,6 @@ use alloy_dyn_abi::TypedData; +use alloy_network::Network; use alloy_primitives::{Address, Bytes, ChainId, TxHash}; -use alloy_rpc_types::TransactionRequest; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -37,11 +37,11 @@ impl BrowserApiResponse { /// Represents a transaction request sent to the browser wallet. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] -pub struct BrowserTransactionRequest { +pub struct BrowserTransactionRequest { /// The unique identifier for the transaction. pub id: Uuid, /// The transaction request details. - pub request: TransactionRequest, + pub request: N::TransactionRequest, } /// Represents a transaction response sent from the browser wallet. diff --git a/crates/wallets/src/wallet_multi/mod.rs b/crates/wallets/src/wallet_multi/mod.rs index 7e098002a26e0..fe9556c60af77 100644 --- a/crates/wallets/src/wallet_multi/mod.rs +++ b/crates/wallets/src/wallet_multi/mod.rs @@ -1,6 +1,8 @@ use crate::{ + BrowserWalletOpts, signer::{PendingSigner, WalletSigner}, utils, + wallet_browser::signer::BrowserSigner, }; use alloy_primitives::map::AddressHashMap; use alloy_signer::Signer; @@ -19,12 +21,18 @@ pub struct MultiWallet { pending_signers: Vec, /// Contains unlocked signers. signers: AddressHashMap, + /// Browser signer + browser: Option, } impl MultiWallet { - pub fn new(pending_signers: Vec, signers: Vec) -> Self { + pub fn new( + pending_signers: Vec, + signers: Vec, + browser: Option, + ) -> Self { let signers = signers.into_iter().map(|signer| (signer.address(), signer)).collect(); - Self { pending_signers, signers } + Self { pending_signers, signers, browser } } fn maybe_unlock_pending(&mut self) -> Result<()> { @@ -35,14 +43,14 @@ impl MultiWallet { Ok(()) } - pub fn signers(&mut self) -> Result<&AddressHashMap> { + pub fn signers(&mut self) -> Result<(&AddressHashMap, Option<&BrowserSigner>)> { self.maybe_unlock_pending()?; - Ok(&self.signers) + Ok((&self.signers, self.browser.as_ref())) } - pub fn into_signers(mut self) -> Result> { + pub fn into_signers(mut self) -> Result<(AddressHashMap, Option)> { self.maybe_unlock_pending()?; - Ok(self.signers) + Ok((self.signers, self.browser)) } pub fn add_signer(&mut self, signer: WalletSigner) { @@ -229,6 +237,10 @@ pub struct MultiWalletOpts { /// See: #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "turnkey"))] pub turnkey: bool, + + /// Browser wallet options + #[command(flatten)] + pub browser: BrowserWalletOpts, } impl MultiWalletOpts { @@ -236,6 +248,7 @@ impl MultiWalletOpts { pub async fn get_multi_wallet(&self) -> Result { let mut pending = Vec::new(); let mut signers: Vec = Vec::new(); + let browser = self.browser_signer().await?; if let Some(ledgers) = self.ledgers().await? { signers.extend(ledgers); @@ -272,7 +285,7 @@ impl MultiWalletOpts { )); } - Ok(MultiWallet::new(pending, signers)) + Ok(MultiWallet::new(pending, signers, browser)) } pub fn private_keys(&self) -> Result>> { @@ -477,6 +490,21 @@ impl MultiWalletOpts { Ok(None) } + + /// Returns the Turnkey address if `--turnkey` flag is set and `TURNKEY_ADDRESS` is available. + pub fn turnkey_address(&self) -> Option { + #[cfg(feature = "turnkey")] + if self.turnkey { + return std::env::var("TURNKEY_ADDRESS").ok().and_then(|addr| addr.parse().ok()); + } + + None + } + + /// Launches and returns the Browser signer if `--browser` flag is set + pub async fn browser_signer(&self) -> Result> { + self.browser.run().await + } } #[cfg(test)] @@ -528,6 +556,42 @@ mod tests { assert_eq!(unlocked[0].address(), address!("0xec554aeafe75601aaab43bd4621a22284db566c2")); } + // https://github.com/foundry-rs/foundry/issues/12916 + #[test] + #[cfg(feature = "turnkey")] + fn turnkey_address_returns_address_when_flag_set() { + let args: MultiWalletOpts = MultiWalletOpts::parse_from(["foundry-cli", "--turnkey"]); + assert!(args.turnkey); + + unsafe { + std::env::set_var("TURNKEY_ADDRESS", "0x1234567890123456789012345678901234567890"); + } + + let addr = args.turnkey_address(); + assert_eq!(addr, Some(address!("0x1234567890123456789012345678901234567890"))); + + unsafe { + std::env::remove_var("TURNKEY_ADDRESS"); + } + } + + #[test] + fn turnkey_address_returns_none_when_flag_not_set() { + let args: MultiWalletOpts = MultiWalletOpts::parse_from(["foundry-cli"]); + assert!(!args.turnkey); + + unsafe { + std::env::set_var("TURNKEY_ADDRESS", "0x1234567890123456789012345678901234567890"); + } + + let addr = args.turnkey_address(); + assert_eq!(addr, None); + + unsafe { + std::env::remove_var("TURNKEY_ADDRESS"); + } + } + // https://github.com/foundry-rs/foundry/issues/5179 #[test] fn should_not_require_the_mnemonics_flag_with_mnemonic_indexes() { diff --git a/deny.toml b/deny.toml index 8e5996c2bacdc..2b9258cd3991a 100644 --- a/deny.toml +++ b/deny.toml @@ -7,6 +7,8 @@ yanked = "warn" ignore = [ # https://rustsec.org/advisories/RUSTSEC-2024-0436 paste! is unmaintained "RUSTSEC-2024-0436", + # https://rustsec.org/advisories/RUSTSEC-2025-0141 bincode is unmaintained + "RUSTSEC-2025-0141", ] # This section is considered when running `cargo deny check bans`. @@ -61,7 +63,6 @@ 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 = "tiny-keccak" }, { allow = ["CC0-1.0"], name = "trezor-client" }, { allow = ["CC0-1.0"], name = "notify" }, { allow = ["CC0-1.0"], name = "dunce" }, diff --git a/flake.lock b/flake.lock index 9dafaa33fa15b..a11c598e7a872 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1768027312, - "narHash": "sha256-MW85tjeLZHPB1y0bQaBat/4vtDVaSot2nSWre2rj9nU=", + "lastModified": 1772261909, + "narHash": "sha256-8XbJXrhMFhLgoBrjFIJx5XJi+SD+7/gbvaIXCuqy9Z0=", "owner": "nix-community", "repo": "fenix", - "rev": "334c4b4a8a2554b8dd19df8932d43345232f7f84", + "rev": "e4c413b9546d6c9e6426b33b4d6de1a49a375024", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1768032153, - "narHash": "sha256-6kD1MdY9fsE6FgSwdnx29hdH2UcBKs3/+JJleMShuJg=", + "lastModified": 1772173633, + "narHash": "sha256-MOH58F4AIbCkh6qlQcwMycyk5SWvsqnS/TCfnqDlpj4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3146c6aa9995e7351a398e17470e15305e6e18ff", + "rev": "c0f3d81a7ddbc2b1332be0d8481a672b4f6004d6", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1767961401, - "narHash": "sha256-7PZ/pXpRrd3JfgCSEuBeYW6nCm3UEZ1TLDztFbnjPgU=", + "lastModified": 1772178959, + "narHash": "sha256-DkjUvrEnnhHjOcjMx6aXfYGIZ0PWmcYzvVayhRj1r4M=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "714d0476ae96352f09933e1d8d51e027be25e5c3", + "rev": "8494a8b3b769c17e8594d811012cc1b0fab090c7", "type": "github" }, "original": { diff --git a/foundryup/foundryup b/foundryup/foundryup index 2e867184de9d2..2dcbb5c2f6d0b 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -55,11 +55,6 @@ main() { esac; shift done - # Tempo only distributes a subset of the binaries - if [[ "$FOUNDRYUP_NETWORK" == "tempo" ]]; then - BINS=(forge cast) - fi - CARGO_BUILD_ARGS=(--release) if [ -n "$FOUNDRYUP_JOBS" ]; then @@ -333,8 +328,8 @@ main() { # Compute the URL of the release tarball in the Tempo Foundry repository. RELEASE_URL="https://github.com/${FOUNDRYUP_REPO}/releases/download/${FOUNDRYUP_TAG}/" - BIN_ARCHIVE_URL="${RELEASE_URL}foundry_nightly_${PLATFORM}_${ARCHITECTURE}.$EXT" - MAN_TARBALL_URL="${RELEASE_URL}foundry_man_nightly.tar.gz" + BIN_ARCHIVE_URL="${RELEASE_URL}foundry_${FOUNDRYUP_VERSION}_${PLATFORM}_${ARCHITECTURE}.$EXT" + MAN_TARBALL_URL="${RELEASE_URL}foundry_man_${FOUNDRYUP_VERSION}.tar.gz" ensure mkdir -p "$FOUNDRY_VERSIONS_DIR" diff --git a/testdata/default/cheats/Ed25519.t.sol b/testdata/default/cheats/Ed25519.t.sol new file mode 100644 index 0000000000000..a7cdeae4ce3fc --- /dev/null +++ b/testdata/default/cheats/Ed25519.t.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "utils/Test.sol"; + +contract Ed25519Test is Test { + function testCreateEd25519Key() public { + bytes32 salt = bytes32(uint256(1)); + (bytes32 publicKey, bytes32 privateKey) = vm.createEd25519Key(salt); + assertTrue(publicKey != bytes32(0), "public key should not be zero"); + assertEq(privateKey, salt, "private key should equal salt"); + } + + function testCreateEd25519KeyDeterministic() public { + bytes32 salt = bytes32(uint256(42)); + (bytes32 pub1, bytes32 priv1) = vm.createEd25519Key(salt); + (bytes32 pub2, bytes32 priv2) = vm.createEd25519Key(salt); + assertEq(pub1, pub2, "same salt should produce same public key"); + assertEq(priv1, priv2, "same salt should produce same private key"); + } + + function testCreateEd25519KeyDifferentSalts() public { + bytes32 salt1 = bytes32(uint256(1)); + bytes32 salt2 = bytes32(uint256(2)); + (bytes32 pub1,) = vm.createEd25519Key(salt1); + (bytes32 pub2,) = vm.createEd25519Key(salt2); + assertTrue(pub1 != pub2, "different salts should produce different public keys"); + } + + function testPublicKeyEd25519() public { + bytes32 salt = bytes32(uint256(123)); + (bytes32 expectedPub, bytes32 privateKey) = vm.createEd25519Key(salt); + bytes32 derivedPub = vm.publicKeyEd25519(privateKey); + assertEq(derivedPub, expectedPub, "derived public key should match created one"); + } + + function testSignAndVerifyEd25519() public { + bytes32 salt = bytes32(uint256(0xdeadbeef)); + (bytes32 publicKey, bytes32 privateKey) = vm.createEd25519Key(salt); + + bytes memory namespace = "test.namespace"; + bytes memory message = "hello world"; + + bytes memory signature = vm.signEd25519(namespace, message, privateKey); + assertEq(signature.length, 64, "signature should be 64 bytes"); + + bool valid = vm.verifyEd25519(signature, namespace, message, publicKey); + assertTrue(valid, "signature should be valid"); + } + + function testVerifyEd25519WrongMessage() public { + bytes32 salt = bytes32(uint256(0xdeadbeef)); + (bytes32 publicKey, bytes32 privateKey) = vm.createEd25519Key(salt); + + bytes memory namespace = "ns"; + bytes memory signature = vm.signEd25519(namespace, "correct message", privateKey); + + bool valid = vm.verifyEd25519(signature, namespace, "wrong message", publicKey); + vm.assertFalse(valid, "signature should not verify with wrong message"); + } + + function testVerifyEd25519NamespaceSeparation() public { + bytes32 salt = bytes32(uint256(0xdeadbeef)); + (bytes32 publicKey, bytes32 privateKey) = vm.createEd25519Key(salt); + + bytes memory message = "message"; + bytes memory signature = vm.signEd25519("namespace.a", message, privateKey); + + bool valid = vm.verifyEd25519(signature, "namespace.b", message, publicKey); + vm.assertFalse(valid, "signature should not verify with different namespace"); + + valid = vm.verifyEd25519(signature, "namespace.a", message, publicKey); + assertTrue(valid, "signature should verify with correct namespace"); + } + + function testVerifyEd25519InvalidSignature() public { + bytes32 salt = bytes32(uint256(0xdeadbeef)); + (bytes32 publicKey,) = vm.createEd25519Key(salt); + + bytes memory invalidSig = new bytes(64); + bool valid = vm.verifyEd25519(invalidSig, "ns", "msg", publicKey); + vm.assertFalse(valid, "zero signature should not verify"); + } + + function testVerifyEd25519WrongSignatureLength() public { + bytes32 salt = bytes32(uint256(0xdeadbeef)); + (bytes32 publicKey,) = vm.createEd25519Key(salt); + + bytes memory shortSig = new bytes(32); + bool valid = vm.verifyEd25519(shortSig, "ns", "msg", publicKey); + vm.assertFalse(valid, "short signature should not verify"); + } + + function testSignEd25519Deterministic() public { + bytes32 salt = bytes32(uint256(0xdeadbeef)); + (, bytes32 privateKey) = vm.createEd25519Key(salt); + + bytes memory namespace = "ns"; + bytes memory message = "msg"; + + bytes memory sig1 = vm.signEd25519(namespace, message, privateKey); + bytes memory sig2 = vm.signEd25519(namespace, message, privateKey); + assertEq(sig1, sig2, "same inputs should produce same signature"); + } +} diff --git a/testdata/default/cheats/ExecuteTransaction.t.sol b/testdata/default/cheats/ExecuteTransaction.t.sol new file mode 100644 index 0000000000000..b86a3b96c42ff --- /dev/null +++ b/testdata/default/cheats/ExecuteTransaction.t.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "utils/Test.sol"; + +contract ExecuteTransactionTest is Test { + function test_revert_not_a_tx() public { + vm._expectCheatcodeRevert("failed to decode RLP-encoded transaction: unexpected string"); + vm.executeTransaction(hex"0102"); + } + + function test_execute_legacy_transfer() public { + vm.fee(1); + vm.chainId(1); + + address from = 0x5316812db67073C4d4af8BB3000C5B86c2877e94; + address to = 0x6Fd0A0CFF9A87aDF51695b40b4fA267855a8F4c6; + + uint256 balance = 1 ether; + uint256 amountSent = 17; + + vm.deal(address(from), balance); + assertEq(address(from).balance, balance); + assertEq(address(to).balance, 0); + + /* + Legacy signed transaction (type 0): + { from: 0x5316812db67073c4d4af8bb3000c5b86c2877e94, to: 0x6fd0a0cff9a87adf51695b40b4fa267855a8f4c6, gas: 200000, gasPrice: 100, value: 17, nonce: 0, chainId: 1 } + */ + vm.executeTransaction( + hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" + ); + + // Gas price is set to 0 in isolated execution, so no gas cost deducted. + assertEq(address(from).balance, balance - amountSent); + assertEq(address(to).balance, amountSent); + } + + function test_execute_eip1559_transfer() public { + vm.chainId(1); + + address from = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; + address to = 0x6Fd0A0CFF9A87aDF51695b40b4fA267855a8F4c6; + + uint256 balance = 1 ether; + uint256 amountSent = 42; + + vm.deal(from, balance); + assertEq(from.balance, balance); + assertEq(to.balance, 0); + + /* + EIP-1559 signed transaction (type 2): + { from: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8, to: 0x6fd0a0cff9a87adf51695b40b4fa267855a8f4c6, gas: 21000, maxFeePerGas: 100, maxPriorityFeePerGas: 10, value: 42, nonce: 0, chainId: 1 } + */ + vm.executeTransaction( + hex"02f86201800a64825208946fd0a0cff9a87adf51695b40b4fa267855a8f4c62a80c080a03447a5bb5068bea134c052824759b5dd973aefcf745d0d67a6e2ee6543571f2ca05f3ee9f04a4d3cbc883f5a8b68cb6149fbc47083bb7f4abf644df780f2f11638" + ); + + // Gas price is set to 0 in isolated execution, so no gas cost deducted. + assertEq(from.balance, balance - amountSent); + assertEq(to.balance, amountSent); + } + + function test_execute_erc20_transfer() public { + vm.fee(1); + vm.chainId(1); + + address alice = 0x7ED31830602f9F7419307235c0610Fb262AA0375; + address bob = 0x70CF146aB98ffD5dE24e75dd7423F16181Da8E13; + address charlie = 0xae0900Cf97f8C233c64F7089cEC7d5457215BB8d; + + bytes memory code = + hex"608060405234801561001057600080fd5b50600436106100625760003560e01c8063095ea7b31461006757806323b872dd1461008f57806370a08231146100a257806394bf804d146100d9578063a9059cbb146100ee578063dd62ed3e14610101575b600080fd5b61007a61007536600461051d565b61013a565b60405190151581526020015b60405180910390f35b61007a61009d366004610547565b610152565b6100cb6100b0366004610583565b6001600160a01b031660009081526020819052604090205490565b604051908152602001610086565b6100ec6100e73660046105a5565b610176565b005b61007a6100fc36600461051d565b610184565b6100cb61010f3660046105d1565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600033610148818585610192565b5060019392505050565b600033610160858285610286565b61016b858585610318565b506001949350505050565b6101808183610489565b5050565b600033610148818585610318565b6001600160a01b0383166101f95760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084015b60405180910390fd5b6001600160a01b03821661025a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016101f0565b6001600160a01b0392831660009081526001602090815260408083209490951682529290925291902055565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461031257818110156103055760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016101f0565b6103128484848403610192565b50505050565b6001600160a01b03831661037c5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016101f0565b6001600160a01b0382166103de5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016101f0565b6001600160a01b038316600090815260208190526040902054818110156104565760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016101f0565b6001600160a01b039384166000908152602081905260408082209284900390925592909316825291902080549091019055565b6001600160a01b0382166104df5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016101f0565b6001600160a01b03909116600090815260208190526040902080549091019055565b80356001600160a01b038116811461051857600080fd5b919050565b6000806040838503121561053057600080fd5b61053983610501565b946020939093013593505050565b60008060006060848603121561055c57600080fd5b61056584610501565b925061057360208501610501565b9150604084013590509250925092565b60006020828403121561059557600080fd5b61059e82610501565b9392505050565b600080604083850312156105b857600080fd5b823591506105c860208401610501565b90509250929050565b600080604083850312156105e457600080fd5b6105ed83610501565b91506105c86020840161050156fea2646970667358221220e1fee5cd1c5bbf066a9ce9228e1baf7e7fcb77b5050506c7d614aaf8608b42e364736f6c63430008110033"; + + MyERC20 token = MyERC20(address(uint160(uint256(keccak256(abi.encodePacked("mytoken")))))); + vm.etch(address(token), code); + + token.mint(100, alice); + + assertEq(token.balanceOf(alice), 100); + assertEq(token.balanceOf(bob), 0); + assertEq(token.balanceOf(charlie), 0); + + vm.deal(alice, 10 ether); + + /* + Signed transaction: + { + from: '0x7ED31830602f9F7419307235c0610Fb262AA0375', + to: '0x5bF11839F61EF5ccEEaf1F4153e44df5D02825f7', + value: 0, + data: '0x095ea7b300000000000000000000000070cf146ab98ffd5de24e75dd7423f16181da8e130000000000000000000000000000000000000000000000000000000000000032', + nonce: 0, + gasPrice: 100, + gasLimit: 200000, + chainId: 1 + } + */ + // alice approves bob for 50 tokens + vm.executeTransaction( + hex"f8a5806483030d40945bf11839f61ef5cceeaf1f4153e44df5d02825f780b844095ea7b300000000000000000000000070cf146ab98ffd5de24e75dd7423f16181da8e13000000000000000000000000000000000000000000000000000000000000003225a0e25b9ef561d9a413b21755cc0e4bb6e80f2a88a8a52305690956130d612074dfa07bfd418bc2ad3c3f435fa531cdcdc64887f64ed3fb0d347d6b0086e320ad4eb1" + ); + + assertEq(token.allowance(alice, bob), 50); + + // Use the allowance via a normal prank call. + vm.deal(bob, 1 ether); + vm.prank(bob); + token.transferFrom(alice, charlie, 20); + + assertEq(token.balanceOf(alice), 80); + assertEq(token.balanceOf(bob), 0); + assertEq(token.balanceOf(charlie), 20); + } + + // Verify state isolation: operations after executeTransaction should work correctly. + function test_execute_then_interact() public { + vm.fee(1); + vm.chainId(1); + + address from = 0x5316812db67073C4d4af8BB3000C5B86c2877e94; + address to = 0x6Fd0A0CFF9A87aDF51695b40b4fA267855a8F4c6; + address random = address(uint160(uint256(keccak256(abi.encodePacked("random"))))); + + uint256 balance = 1 ether; + + vm.deal(address(from), balance); + + vm.executeTransaction( + hex"f860806483030d40946fd0a0cff9a87adf51695b40b4fa267855a8f4c6118025a03ebeabbcfe43c2c982e99b376b5fb6e765059d7f215533c8751218cac99bbd80a00a56cf5c382442466770a756e81272d06005c9e90fb8dbc5b53af499d5aca856" + ); + + assertEq(address(to).balance, 17); + + // Interact with the state after executeTransaction. + uint256 value = 5; + vm.prank(to); + (bool success,) = random.call{value: value}(""); + require(success); + assertEq(address(to).balance, 17 - value); + assertEq(address(random).balance, value); + } +} + +contract MyERC20 { + mapping(address => uint256) private _balances; + mapping(address => mapping(address => uint256)) private _allowances; + + function mint(uint256 amount, address to) public { + _mint(to, amount); + } + + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + + function transfer(address to, uint256 amount) public returns (bool) { + address owner = msg.sender; + _transfer(owner, to, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + function approve(address spender, uint256 amount) public returns (bool) { + address owner = msg.sender; + _approve(owner, spender, amount); + return true; + } + + function transferFrom(address from, address to, uint256 amount) public returns (bool) { + address spender = msg.sender; + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + function _transfer(address from, address to, uint256 amount) internal { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + _balances[to] += amount; + } + } + + function _mint(address account, uint256 amount) internal { + require(account != address(0), "ERC20: mint to the zero address"); + unchecked { + _balances[account] += amount; + } + } + + function _approve(address owner, address spender, uint256 amount) internal { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + _allowances[owner][spender] = amount; + } + + function _spendAllowance(address owner, address spender, uint256 amount) internal { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } +} diff --git a/testdata/default/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol index 6f3ddf305b302..839d97962aa94 100644 --- a/testdata/default/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -413,6 +413,18 @@ contract ExpectRevertCountWithReverter is Test { } } +contract ExpectRevertPrecompileTest is Test { + /// Test that vm.expectRevert works when the next external call targets a + /// precompile address directly. Precompile calls don't create an interpreter + /// frame (no `initialize_interp`), so depth tracking must account for them. + function testExpectRevertDirectPrecompileCall() public { + // BLAKE2F precompile (0x09) expects exactly 213 bytes of input. + // Calling it with invalid input reverts. + vm.expectRevert(); + address(0x09).call(hex"00"); + } +} + contract ExpectRevertWithErrorTest is Test { /// Ref: function test_f() external { diff --git a/testdata/default/cheats/MockFunction.t.sol b/testdata/default/cheats/MockFunction.t.sol index 3defa8cfd3580..6cd93253c3953 100644 --- a/testdata/default/cheats/MockFunction.t.sol +++ b/testdata/default/cheats/MockFunction.t.sol @@ -3,7 +3,13 @@ pragma solidity ^0.8.18; import "utils/Test.sol"; -contract MockFunctionContract { +interface IMockFunctionContract { + function a() external view returns (uint256); + function mocked_function() external; + function mocked_args_function(uint256 x) external; +} + +contract MockFunctionContract is IMockFunctionContract { uint256 public a; function mocked_function() public { @@ -15,7 +21,7 @@ contract MockFunctionContract { } } -contract ModelMockFunctionContract { +contract ModelMockFunctionContract is IMockFunctionContract { uint256 public a; function mocked_function() public { @@ -27,13 +33,53 @@ contract ModelMockFunctionContract { } } +contract Proxy { + address immutable impl; + + constructor(address impl_) { + impl = impl_; + } + + fallback() external { + _delegate(impl); + } + + // code from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/239795bea728c8dca4deb6c66856dd58a6991112/contracts/proxy/Proxy.sol#L22-L45 + function _delegate(address implementation) internal virtual { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0x00, 0x00, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0x00, calldatasize(), 0x00, 0x00) + + // Copy the returned data. + returndatacopy(0x00, 0x00, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0x00, returndatasize()) + } + default { + return(0x00, returndatasize()) + } + } + } +} + contract MockFunctionTest is Test { MockFunctionContract my_contract; ModelMockFunctionContract model_contract; + IMockFunctionContract my_proxy; function setUp() public { my_contract = new MockFunctionContract(); model_contract = new ModelMockFunctionContract(); + my_proxy = IMockFunctionContract(address(new Proxy(address(my_contract)))); } function test_mock_function() public { @@ -69,4 +115,132 @@ contract MockFunctionTest is Test { my_contract.mocked_args_function(789); assertEq(my_contract.a(), 123 + 789); } + + function test_mock_function_via_proxy() public { + vm.mockFunction( + address(my_proxy), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_function.selector) + ); + my_proxy.mocked_function(); + assertEq(my_proxy.a(), 123, "mocked function should be called via proxy"); + + // reset mock + vm.mockFunction( + address(my_proxy), address(my_proxy), abi.encodeWithSelector(MockFunctionContract.mocked_function.selector) + ); + my_proxy.mocked_function(); + assertEq(my_proxy.a(), 321, "after reset, original function should be called"); + } + + function test_mock_function_via_proxy_concrete_args() public { + vm.mockFunction( + address(my_proxy), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector, 100) + ); + my_proxy.mocked_args_function(100); + assertEq(my_proxy.a(), 123 + 100, "mocked args function should be called via proxy"); + my_proxy.mocked_args_function(200); + assertEq(my_proxy.a(), 321 + 200, "original args function should be called for different args"); + + // reset mock + vm.mockFunction( + address(my_proxy), + address(my_proxy), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector, 100) + ); + my_proxy.mocked_args_function(100); + assertEq(my_proxy.a(), 321 + 100, "after reset, original args function should be called"); + my_proxy.mocked_args_function(200); + assertEq(my_proxy.a(), 321 + 200, "original args function should be called for different args"); + } + + function test_mock_function_via_proxy_all_args() public { + vm.mockFunction( + address(my_proxy), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector) + ); + my_proxy.mocked_args_function(300); + assertEq(my_proxy.a(), 123 + 300, "mocked args function should be called via proxy"); + my_proxy.mocked_args_function(400); + assertEq(my_proxy.a(), 123 + 400, "mocked args function should be called via proxy"); + + // reset mock + vm.mockFunction( + address(my_proxy), + address(my_proxy), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector) + ); + my_proxy.mocked_args_function(300); + assertEq(my_proxy.a(), 321 + 300, "after reset, original args function should be called"); + my_proxy.mocked_args_function(400); + assertEq(my_proxy.a(), 321 + 400, "after reset, original args function should be called"); + } + + function test_mock_function_via_impl() public { + vm.mockFunction( + address(my_contract), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_function.selector) + ); + my_proxy.mocked_function(); + assertEq(my_proxy.a(), 123, "mocked function should be called via impl address"); + + // reset mock + vm.mockFunction( + address(my_contract), + address(my_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_function.selector) + ); + my_proxy.mocked_function(); + assertEq(my_proxy.a(), 321, "after reset, original function should be called"); + } + + function test_mock_function_via_impl_concrete_args() public { + vm.mockFunction( + address(my_contract), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector, 200) + ); + my_proxy.mocked_args_function(200); + assertEq(my_proxy.a(), 123 + 200, "mocked args function should be called via impl address"); + my_proxy.mocked_args_function(300); + assertEq(my_proxy.a(), 321 + 300, "original args function should be called for different args"); + + // reset mock + vm.mockFunction( + address(my_contract), + address(my_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector, 200) + ); + my_proxy.mocked_args_function(200); + assertEq(my_proxy.a(), 321 + 200, "after reset, original args function should be called"); + my_proxy.mocked_args_function(300); + assertEq(my_proxy.a(), 321 + 300, "original args function should be called for different args"); + } + + function test_mock_function_via_impl_all_args() public { + vm.mockFunction( + address(my_contract), + address(model_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector) + ); + my_proxy.mocked_args_function(400); + assertEq(my_proxy.a(), 123 + 400, "mocked args function should be called via impl address"); + my_proxy.mocked_args_function(500); + assertEq(my_proxy.a(), 123 + 500, "mocked args function should be called via impl address"); + + // reset mock + vm.mockFunction( + address(my_contract), + address(my_contract), + abi.encodeWithSelector(MockFunctionContract.mocked_args_function.selector) + ); + my_proxy.mocked_args_function(400); + assertEq(my_proxy.a(), 321 + 400, "after reset, original args function should be called"); + my_proxy.mocked_args_function(500); + assertEq(my_proxy.a(), 321 + 500, "after reset, original args function should be called"); + } } diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index 321b5b3cbbeed..97ee9ad3a9293 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -7,7 +7,7 @@ contract RpcUrlTest is Test { // returns the correct url function testCanGetRpcUrl() public { string memory url = vm.rpcUrl("mainnet"); - assertTrue(bytes(url).length >= 36); + assertTrue(bytes(url).length != 0); } // returns an error if env alias does not exist diff --git a/testdata/forge-std-rev b/testdata/forge-std-rev index 213471e492846..8bc543e5cf68b 100644 --- a/testdata/forge-std-rev +++ b/testdata/forge-std-rev @@ -1 +1 @@ -1801b0541f4fda118a10798fd3486bb7051c5dd6 \ No newline at end of file +0844d7e1fc5e60d77b68e469bff60265f236c398 \ No newline at end of file diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 0fd8135bbcd22..8c60909ff66c9 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -8,11 +8,13 @@ ignored_error_codes = [ 1878, # SPDX license identifier not provided 2018, # Function state mutability can be restricted 2072, # Unused local variable + 2424, # Natspec memory-safe-assembly special comment deprecated 2519, # This declaration shadows an existing declaration 3860, # Contract init code size exceeds limit 5159, # Selfdestruct has been deprecated 5574, # Contract code size exceeds limit 5667, # Unused function parameter + 9207, # 'transfer' is deprecated ] extra_output = ["storageLayout"] @@ -57,4 +59,7 @@ polygon = "${RPC_POLYGON}" bsc = "${RPC_BSC}" avaxTestnet = "https://api.avax-test.network/ext/bc/C/rpc" moonbeam = "https://moonbeam-rpc.publicnode.com" +polkadotTestnet = "https://eth-rpc-testnet.polkadot.io" +kusama = "https://eth-rpc-kusama.polkadot.io" +polkadot = "https://eth-rpc.polkadot.io" rpcEnvAlias = "${RPC_ENV_ALIAS}" diff --git a/testdata/utils/Vm.sol b/testdata/utils/Vm.sol index e06ed8facda10..e4b11e8d42e64 100644 --- a/testdata/utils/Vm.sol +++ b/testdata/utils/Vm.sol @@ -182,6 +182,7 @@ interface Vm { function copyFile(string calldata from, string calldata to) external returns (uint64 copied); function copyStorage(address from, address to) external; function createDir(string calldata path, bool recursive) external; + function createEd25519Key(bytes32 salt) external pure returns (bytes32 publicKey, bytes32 privateKey); function createFork(string calldata urlOrAlias) external returns (uint256 forkId); function createFork(string calldata urlOrAlias, uint256 blockNumber) external returns (uint256 forkId); function createFork(string calldata urlOrAlias, bytes32 txHash) external returns (uint256 forkId); @@ -247,6 +248,7 @@ interface Vm { function envUint(string calldata name, string calldata delim) external view returns (uint256[] memory value); function etch(address target, bytes calldata newRuntimeBytecode) external; function eth_getLogs(uint256 fromBlock, uint256 toBlock, address target, bytes32[] calldata topics) external view returns (EthGetLogs[] memory logs); + function executeTransaction(bytes calldata rawTx) external returns (bytes memory); function exists(string calldata path) external view returns (bool result); function expectCallMinGas(address callee, uint256 msgValue, uint64 minGas, bytes calldata data) external; function expectCallMinGas(address callee, uint256 msgValue, uint64 minGas, bytes calldata data, uint64 count) external; @@ -414,6 +416,7 @@ interface Vm { 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 publicKeyEd25519(bytes32 privateKey) external pure returns (bytes32 publicKey); function publicKeyP256(uint256 privateKey) external pure returns (uint256 publicKeyX, uint256 publicKeyY); function randomAddress() external view returns (address); function randomBool() external view returns (bool); @@ -500,6 +503,7 @@ interface Vm { function signDelegation(address implementation, uint256 privateKey) external returns (SignedDelegation memory signedDelegation); function signDelegation(address implementation, uint256 privateKey, uint64 nonce) external returns (SignedDelegation memory signedDelegation); function signDelegation(address implementation, uint256 privateKey, bool crossChain) external returns (SignedDelegation memory signedDelegation); + function signEd25519(bytes calldata namespace, bytes calldata message, bytes32 privateKey) external pure returns (bytes memory signature); function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); function signWithNonceUnsafe(uint256 privateKey, bytes32 digest, uint256 nonce) external pure returns (uint8 v, bytes32 r, bytes32 s); function sign(Wallet calldata wallet, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); @@ -559,6 +563,7 @@ interface Vm { function tryFfi(string[] calldata commandInput) external returns (FfiResult memory result); function txGasPrice(uint256 newGasPrice) external; function unixTime() external view returns (uint256 milliseconds); + function verifyEd25519(bytes calldata signature, bytes calldata namespace, bytes calldata message, bytes32 publicKey) external pure returns (bool valid); function warmSlot(address target, bytes32 slot) external; function warp(uint256 newTimestamp) external; function writeFile(string calldata path, string calldata data) external; diff --git a/typos.toml b/typos.toml index 2e86c7c3aaffe..136333b3b57e3 100644 --- a/typos.toml +++ b/typos.toml @@ -22,3 +22,4 @@ Caf = "Caf" froms = "froms" strat = "strat" ba = "ba" +consts = "consts" From 3d621aaefcf4b233fccc1f969c13b90f18039af2 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 6 Mar 2026 03:10:46 +0000 Subject: [PATCH 260/374] Potential fix for code scanning alert no. 155: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/util.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 8e0d7912871bb..d899a1e764629 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -298,17 +298,25 @@ fn resolve_and_validate_under_base(path: &Path) -> io::Result { } fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Result<()> { - for entry in fs::read_dir(src)? { + // Ensure that each recursion step operates on paths that are constrained to the + // configured base directory. This guarantees that any `src_path` passed to + // filesystem operations cannot escape the allowed workspace even if the initial + // input was influenced by the user. + let src = resolve_and_validate_under_base(src)?; + let dst = resolve_and_validate_under_base(dst)?; + + for entry in fs::read_dir(&src)? { let entry = entry?; let ty = entry.file_type()?; - let src_path = entry.path(); - let dst_path = dst.join(entry.file_name()); + let name = entry.file_name(); + let src_path = src.join(&name); + let dst_path = dst.join(&name); if ty.is_dir() { // Skip build artifact directories at the root level if is_root - && let Some(name) = entry.file_name().to_str() - && SKIP_DIRS.contains(&name) + && let Some(name_str) = name.to_str() + && SKIP_DIRS.contains(&name_str) { continue; } From 65e57c91505cc1895026e6b7836c5077f34b28c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 03:12:53 +0000 Subject: [PATCH 261/374] chore(deps): bump the cargo group across 1 directory with 9 updates (#377) Bumps the cargo group with 3 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing), [keccak](https://github.com/RustCrypto/sponges) and [slab](https://github.com/tokio-rs/slab). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `time` from 0.3.41 to 0.3.47 - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.41...v0.3.47) Updates `alloy-dyn-abi` from 1.3.0 to 1.5.7 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.3.0...v1.5.7) Updates `bytes` from 1.10.1 to 1.11.1 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.1...v1.11.1) Updates `keccak` from 0.1.5 to 0.1.6 - [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6) Updates `lru` from 0.12.5 to 0.16.3 - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.12.5...0.16.3) Updates `protobuf` from 3.3.0 to 3.7.2 Updates `ruint` from 1.15.0 to 1.17.2 - [Release notes](https://github.com/recmo/uint/releases) - [Changelog](https://github.com/recmo/uint/blob/main/CHANGELOG.md) - [Commits](https://github.com/recmo/uint/compare/v1.15.0...v1.17.2) Updates `slab` from 0.4.10 to 0.4.12 - [Release notes](https://github.com/tokio-rs/slab/releases) - [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.12) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: time dependency-version: 0.3.47 dependency-type: direct:production dependency-group: cargo - dependency-name: alloy-dyn-abi dependency-version: 1.5.7 dependency-type: direct:production dependency-group: cargo - dependency-name: bytes dependency-version: 1.11.1 dependency-type: direct:production dependency-group: cargo - dependency-name: keccak dependency-version: 0.1.6 dependency-type: indirect dependency-group: cargo - dependency-name: lru dependency-version: 0.16.3 dependency-type: indirect dependency-group: cargo - dependency-name: protobuf dependency-version: 3.7.2 dependency-type: indirect dependency-group: cargo - dependency-name: ruint dependency-version: 1.17.2 dependency-type: indirect dependency-group: cargo - dependency-name: slab dependency-version: 0.4.12 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- Cargo.lock | 3757 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 2423 insertions(+), 1334 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4eba64171dd1a..a977601d2c7be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -65,14 +74,14 @@ dependencies = [ "alloy-primitives", "num_enum", "serde", - "strum 0.27.2", + "strum", ] [[package]] name = "alloy-consensus" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6093bc69509849435a2d68237a2e9fea79d27390c8e62f1e4012c460aabad8" +checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" dependencies = [ "alloy-eips", "alloy-primitives", @@ -81,23 +90,25 @@ dependencies = [ "alloy-trie", "alloy-tx-macros", "auto_impl", + "borsh", "c-kzg", - "derive_more 2.0.1", + "derive_more", "either", "k256", "once_cell", "rand 0.8.5", "secp256k1 0.30.0", "serde", + "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-consensus-any" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d1cfed4fefd13b5620cb81cdb6ba397866ff0de514c1b24806e6e79cdff5570" +checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" dependencies = [ "alloy-consensus", "alloy-eips", @@ -109,9 +120,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28074a21cd4f7c3a7ab218c4f38fae6be73944e1feae3b670c68b60bf85ca40" +checksum = "ca63b7125a981415898ffe2a2a696c83696c9c6bdb1671c8a912946bbd8e49e7" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -127,22 +138,21 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-dyn-abi" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9e8a436f0aad7df8bb47f144095fba61202265d9f5f09a70b0e3227881a668e" +checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-type-parser", "alloy-sol-types", "arbitrary", - "derive_arbitrary", - "derive_more 2.0.1", + "derive_more", "itoa", "proptest", "serde", @@ -160,105 +170,141 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-eip2930" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" +checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" dependencies = [ "alloy-primitives", "alloy-rlp", + "borsh", + "serde", +] + +[[package]] +name = "alloy-eip5792" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ec415c2141bef46ea7d1b80877d7dc4639c6ccf6a774b75f05e3deadb27469" +dependencies = [ + "alloy-primitives", + "alloy-serde", "serde", + "serde_json", ] [[package]] name = "alloy-eip7702" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" +checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" dependencies = [ "alloy-primitives", "alloy-rlp", + "borsh", "k256", "serde", - "thiserror 2.0.12", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip7928" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", ] [[package]] name = "alloy-eips" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5937e2d544e9b71000942d875cbc57965b32859a666ea543cc57aae5a06d602d" +checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", + "alloy-eip7928", "alloy-primitives", "alloy-rlp", "alloy-serde", "auto_impl", + "borsh", "c-kzg", - "derive_more 2.0.1", + "derive_more", "either", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive", "serde", + "serde_with", "sha2 0.10.9", + "thiserror 2.0.18", ] [[package]] name = "alloy-ens" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7b88162405231467e75e4230785f88672780bbaf19710fe4aaae597f793126" +checksum = "79460500df1a98836253ab24871ecd54d12809ac9045b0d10eed727c3555efa9" dependencies = [ "alloy-contract", "alloy-primitives", "alloy-provider", "alloy-sol-types", "async-trait", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-evm" -version = "0.16.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4042e855163839443cba91147fb7737c4aba02df4767cb322b0e8cea5a77642c" +checksum = "7b99ba7b74a87176f31ee1cd26768f7155b0eeff61ed925f59b13085ffe5f891" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-hardforks", + "alloy-op-hardforks", "alloy-primitives", + "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-sol-types", "auto_impl", - "derive_more 2.0.1", - "op-alloy-consensus", + "derive_more", + "op-alloy", "op-revm", "revm", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-genesis" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51b4c13e02a8104170a4de02ccf006d7c233e6c10ab290ee16e7041e6ac221d" +checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-serde", "alloy-trie", + "borsh", "serde", + "serde_with", ] [[package]] name = "alloy-hardforks" -version = "0.2.13" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165210652f71dfc094b051602bafd691f506c54050a174b1cba18fb5ef706a3" +checksum = "83ba208044232d14d4adbfa77e57d6329f51bc1acc21f5667bb7db72d88a0831" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -269,9 +315,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459f98c6843f208856f338bfb25e65325467f7aff35dfeb0484d0a76e059134b" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -281,24 +327,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b590caa6b6d8bc10e6e7a7696c59b1e550e89f27f50d1ee13071150d3a3e3f66" +checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" dependencies = [ "alloy-primitives", "alloy-sol-types", "http 1.3.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-network" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36fe5af1fca03277daa56ad4ce5f6d623d3f4c2273ea30b9ee8674d18cefc1fa" +checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -313,18 +359,18 @@ dependencies = [ "alloy-sol-types", "async-trait", "auto_impl", - "derive_more 2.0.1", + "derive_more", "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-network-primitives" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793df1e3457573877fbde8872e4906638fde565ee2d3bd16d04aad17d43dbf0e" +checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" dependencies = [ "alloy-consensus", "alloy-eips", @@ -335,9 +381,9 @@ dependencies = [ [[package]] name = "alloy-op-evm" -version = "0.16.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c0bc6a883d3198c43c4018aa952448a303dec265439fa1c2e7c4397beeb289" +checksum = "646a01ebc9778ee08bcc33cfa6714efc548f94e53de1aff6b34d9da55a2e5d41" dependencies = [ "alloy-consensus", "alloy-eips", @@ -345,39 +391,40 @@ dependencies = [ "alloy-op-hardforks", "alloy-primitives", "auto_impl", - "op-alloy-consensus", + "op-alloy", "op-revm", "revm", + "thiserror 2.0.18", ] [[package]] name = "alloy-op-hardforks" -version = "0.2.13" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3417f4187eaf7f7fb0d7556f0197bca26f0b23c4bb3aca0c9d566dc1c5d727a2" +checksum = "6472c610150c4c4c15be9e1b964c9b78068f933bda25fb9cdf09b9ac2bb66f36" dependencies = [ "alloy-chains", "alloy-hardforks", + "alloy-primitives", "auto_impl", ] [[package]] name = "alloy-primitives" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cfebde8c581a5d37b678d0a48a32decb51efd7a63a08ce2517ddec26db705c8" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" dependencies = [ "alloy-rlp", "arbitrary", "bytes", "cfg-if", "const-hex", - "derive_arbitrary", - "derive_more 2.0.1", - "foldhash", - "getrandom 0.3.3", - "hashbrown 0.15.4", - "indexmap 2.10.0", + "derive_more", + "foldhash 0.2.0", + "getrandom 0.4.2", + "hashbrown 0.16.1", + "indexmap 2.13.0", "itoa", "k256", "keccak-asm", @@ -385,18 +432,18 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.9.2", + "rapidhash", "ruint", "rustc-hash 2.1.1", "serde", "sha3", - "tiny-keccak", ] [[package]] name = "alloy-provider" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59879a772ebdcde9dc4eb38b2535d32e8503d3175687cc09e763a625c5fcf32" +checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" dependencies = [ "alloy-chains", "alloy-consensus", @@ -424,14 +471,13 @@ dependencies = [ "either", "futures", "futures-utils-wasm", - "http 1.3.1", - "lru 0.13.0", + "lru", "parking_lot", "pin-project 1.1.10", "reqwest", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -440,13 +486,14 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdfb2899b54b7cb0063fa8e61938320f9be6b81b681be69c203abf130a87baa" +checksum = "e8bd82953194dec221aa4cbbbb0b1e2df46066fe9d0333ac25b43a311e122d13" dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-transport", + "auto_impl", "bimap", "futures", "parking_lot", @@ -478,14 +525,14 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "alloy-rpc-client" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f060e3bb9f319eb01867a2d6d1ff9e0114e8877f5ca8f5db447724136106cae" +checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -509,9 +556,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d47b637369245d2dafef84b223b1ff5ea59e6cd3a98d2d3516e32788a0b216df" +checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -525,9 +572,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b1f499acb3fc729615147bc113b8b798b17379f19d43058a687edc5792c102" +checksum = "e0a3100b76987c1b1dc81f3abe592b7edc29e92b1242067a69d65e0030b35cf9" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -537,48 +584,68 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e26b4dd90b33bd158975307fb9cf5fafa737a0e33cbb772a8648bf8be13c104" +checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", "alloy-serde", ] +[[package]] +name = "alloy-rpc-types-beacon" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a22e13215866f5dfd5d3278f4c41f1fad9410dc68ce39022f58593c873c26f8" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "derive_more", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + [[package]] name = "alloy-rpc-types-debug" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71841e6fc8e221892035a74f7d5b279c0a2bf27a7e1c93e7476c64ce9056624e" +checksum = "e1b21e1ad18ff1b31ff1030e046462ab8168cf8894e6778cd805c8bdfe2bd649" dependencies = [ "alloy-primitives", + "derive_more", "serde", + "serde_with", ] [[package]] name = "alloy-rpc-types-engine" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f9cbf5f781b9ee39cfdddea078fdef6015424f4c8282ef0e5416d15ca352c4" +checksum = "e4ac61f03f1edabccde1c687b5b25fff28f183afee64eaa2e767def3929e4457" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-serde", - "derive_more 2.0.1", + "derive_more", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive", "jsonwebtoken", "rand 0.8.5", "serde", - "strum 0.27.2", + "strum", ] [[package]] name = "alloy-rpc-types-eth" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46586ec3c278639fc0e129f0eb73dbfa3d57f683c44b2ff5e066fab7ba63fa1f" +checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -592,28 +659,28 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-rpc-types-trace" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9a2184493c374ca1dbba9569d37215c23e489970f8c3994f731cb3ed6b0b7d" +checksum = "1ad79f1e27e161943b5a4f99fe5534ef0849876214be411e0032c12f38e94daa" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-rpc-types-txpool" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aaf142f4f6c0bdd06839c422179bae135024407d731e6f365380f88cd4730e" +checksum = "d459f902a2313737bc66d18ed094c25d2aeb268b74d98c26bbbda2aa44182ab0" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -623,9 +690,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1722bc30feef87cc0fa824e43c9013f9639cc6c037be7be28a31361c788be2" +checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" dependencies = [ "alloy-primitives", "serde", @@ -634,9 +701,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3674beb29e68fbbc7be302b611cf35fe07b736e308012a280861df5a2361395" +checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -646,32 +713,33 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "alloy-signer-aws" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605b1659b320b16708bb84b41038b2f0e2a60d90972c28319c4f5a4866f0efd4" +checksum = "e38b411077d7b17e464de7dfa599f5b94161cdffc25c2f28a90a3a345b6d6490" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", + "aws-config", "aws-sdk-kms", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-signer-gcp" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a207671ef0bf6f61e9c80c9ccb6d203439071252fb35886d6a89aae5431cd9c" +checksum = "485d0c73c53a36580be4d882a5c6c9a069759088de88ff759e59342a793adb16" dependencies = [ "alloy-consensus", "alloy-network", @@ -681,15 +749,15 @@ dependencies = [ "gcloud-sdk", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-signer-ledger" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce5e8c97a526d39052035a99652b9cfacf0d646d4a3625fac9c919d20a46fb0" +checksum = "ff7a41e469bce9a836a9fbba7c09f8eba25703062accf6a64bd90b5ed61c1b01" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -701,15 +769,15 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.26", - "thiserror 2.0.12", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-signer-local" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7094c39cd41b03ed642145b0bd37251e31a9cf2ed19e1ce761f089867356a6" +checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" dependencies = [ "alloy-consensus", "alloy-network", @@ -721,14 +789,15 @@ dependencies = [ "eth-keystore", "k256", "rand 0.8.5", - "thiserror 2.0.12", + "thiserror 2.0.18", + "zeroize", ] [[package]] name = "alloy-signer-trezor" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fc3a082bf4dcf2e330b040378c73a113d31fe03068607a4d80368b47e60c44" +checksum = "fb778ec3bef46eb5c0edab6e25e0c6f40d98a5e81e423940f819824b879680a8" dependencies = [ "alloy-consensus", "alloy-network", @@ -736,49 +805,65 @@ dependencies = [ "alloy-signer", "async-trait", "semver 1.0.26", - "thiserror 2.0.12", + "thiserror 2.0.18", "tracing", "trezor-client", ] +[[package]] +name = "alloy-signer-turnkey" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589b334f4cf9de0d80568e8eaf11479b492e74dc2c08991306361065bde2321a" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "thiserror 2.0.18", + "tracing", + "turnkey_client", +] + [[package]] name = "alloy-sol-macro" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedac07a10d4c2027817a43cc1f038313fc53c7ac866f7363239971fd01f9f18" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f9a598f010f048d8b8226492b6401104f5a5c1273c2869b72af29b48bb4ba9" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.10.0", + "indexmap 2.13.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.104", + "sha3", + "syn 2.0.117", "syn-solidity", - "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f494adf9d60e49aa6ce26dfd42c7417aa6d4343cf2ae621f20e4d92a5ad07d85" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" dependencies = [ "alloy-json-abi", "const-hex", @@ -788,15 +873,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.104", + "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52db32fbd35a9c0c0e538b58b81ebbae08a51be029e7ad60e08b60481c2ec6c3" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", "winnow", @@ -804,9 +889,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a285b46e3e0c177887028278f04cc8262b76fd3b8e0e20e93cea0a58c35f5ac5" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -816,20 +901,20 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89bec2f59a41c0e259b6fe92f78dfc49862c17d10f938db9c33150d5a7f42b6" +checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" dependencies = [ "alloy-json-rpc", - "alloy-primitives", + "auto_impl", "base64 0.22.1", - "derive_more 2.0.1", + "derive_more", "futures", "futures-utils-wasm", "parking_lot", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "tower", "tracing", @@ -839,12 +924,13 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3615ec64d775fec840f4e9d5c8e1f739eb1854d8d28db093fb3d4805e0cb53" +checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" dependencies = [ "alloy-json-rpc", "alloy-transport", + "itertools 0.14.0", "reqwest", "serde_json", "tower", @@ -854,9 +940,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374db72669d8ee09063b9aa1a316e812d5cdfce7fc9a99a3eceaa0e5512300d2" +checksum = "c2ef85688e5ac2da72afc804e0a1f153a1f309f05a864b1998bbbed7804dbaab" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -874,15 +960,14 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5dbaa6851875d59c8803088f4b6ec72eaeddf7667547ae8995c1a19fbca6303" +checksum = "b9f00445db69d63298e2b00a0ea1d859f00e6424a3144ffc5eba9c31da995e16" dependencies = [ "alloy-pubsub", "alloy-transport", "futures", "http 1.3.1", - "rustls", "serde_json", "tokio", "tokio-tungstenite", @@ -892,52 +977,33 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bada1fc392a33665de0dc50d401a3701b62583c655e3522a323490a5da016962" +checksum = "4d7fd448ab0a017de542de1dcca7a58e7019fe0e7a34ed3f9543ebddf6aceffa" dependencies = [ "alloy-primitives", "alloy-rlp", "arrayvec", - "derive_more 2.0.1", + "derive_more", "nybbles", "serde", "smallvec", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-tx-macros" -version = "1.0.23" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f916ff6d52f219c44a9684aea764ce2c7e1d53bd4a724c9b127863aeacc30bb" +checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" dependencies = [ - "alloy-primitives", - "darling 0.20.11", + "darling 0.21.0", "proc-macro2", "quote", - "syn 2.0.104", -] - -[[package]] -name = "ammonia" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f" -dependencies = [ - "cssparser", - "html5ever", - "maplit", - "tendril", - "url", + "syn 2.0.117", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -952,6 +1018,16 @@ name = "annotate-snippets" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +dependencies = [ + "anstyle", + "unicode-width 0.2.0", +] + +[[package]] +name = "annotate-snippets" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96401ca08501972288ecbcde33902fce858bf73fbcbdf91dab8c3a9544e106bb" dependencies = [ "anstyle", "memchr", @@ -1032,24 +1108,25 @@ dependencies = [ [[package]] name = "anvil" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-contract", "alloy-dyn-abi", + "alloy-eip5792", "alloy-eips", "alloy-evm", "alloy-genesis", - "alloy-hardforks", "alloy-network", "alloy-op-evm", - "alloy-op-hardforks", "alloy-primitives", "alloy-provider", "alloy-pubsub", "alloy-rlp", "alloy-rpc-types", + "alloy-rpc-types-beacon", + "alloy-rpc-types-eth", "alloy-serde", "alloy-signer", "alloy-signer-local", @@ -1064,8 +1141,8 @@ dependencies = [ "chrono", "clap", "clap_complete", - "clap_complete_fig", "ctrlc", + "ethereum_ssz 0.10.1", "eyre", "fdlimit", "flate2", @@ -1073,7 +1150,8 @@ dependencies = [ "foundry-common", "foundry-config", "foundry-evm", - "foundry-evm-core", + "foundry-evm-networks", + "foundry-primitives", "foundry-test-utils", "futures", "hyper", @@ -1084,26 +1162,28 @@ dependencies = [ "parking_lot", "rand 0.8.5", "rand 0.9.2", + "reqwest", "revm", "revm-inspectors", "serde", "serde_json", "tempfile", - "thiserror 2.0.12", + "tempo-primitives", + "thiserror 2.0.18", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", "yansi", ] [[package]] name = "anvil-core" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", + "alloy-eip5792", "alloy-eips", - "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", @@ -1111,18 +1191,17 @@ dependencies = [ "bytes", "foundry-common", "foundry-evm", - "op-alloy-consensus", - "op-revm", + "foundry-primitives", "rand 0.9.2", "revm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "anvil-rpc" -version = "1.3.1" +version = "1.6.0" dependencies = [ "serde", "serde_json", @@ -1130,7 +1209,7 @@ dependencies = [ [[package]] name = "anvil-server" -version = "1.3.1" +version = "1.6.0" dependencies = [ "anvil-rpc", "async-trait", @@ -1143,7 +1222,7 @@ dependencies = [ "pin-project 1.1.10", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio-util", "tower-http", "tracing", @@ -1151,9 +1230,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" @@ -1164,16 +1243,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "ariadne" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f5e3dca4e09a6f340a61a0e9c7b61e030c69fc27bf29d73218f7e5e3b7638f" -dependencies = [ - "unicode-width 0.1.14", - "yansi", -] - [[package]] name = "ark-bls12-381" version = "0.5.0" @@ -1304,7 +1373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1342,7 +1411,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1431,7 +1500,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1529,7 +1598,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1540,7 +1609,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1593,7 +1662,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -1688,7 +1757,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.17.0", + "uuid 1.21.0", ] [[package]] @@ -2028,12 +2097,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - [[package]] name = "backtrace" version = "0.3.75" @@ -2101,7 +2164,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -2114,7 +2177,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.104", + "syn 2.0.117", "which 4.4.2", ] @@ -2157,11 +2220,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -2197,9 +2260,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" dependencies = [ "cc", "glob", @@ -2209,116 +2272,123 @@ dependencies = [ [[package]] name = "boa_ast" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c340fe0f0b267787095cbe35240c6786ff19da63ec7b69367ba338eace8169b" +checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "boa_interner", "boa_macros", "boa_string", - "indexmap 2.10.0", + "indexmap 2.13.0", "num-bigint", "rustc-hash 2.1.1", ] [[package]] name = "boa_engine" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f620c3f06f51e65c0504ddf04978be1b814ac6586f0b45f6019801ab5efd37f9" +checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" dependencies = [ + "aligned-vec", "arrayvec", - "bitflags 2.9.1", + "bitflags 2.11.0", "boa_ast", "boa_gc", "boa_interner", "boa_macros", "boa_parser", - "boa_profiler", "boa_string", "bytemuck", "cfg-if", + "cow-utils", "dashmap", + "dynify", "fast-float2", - "hashbrown 0.15.4", + "float16", + "futures-channel", + "futures-concurrency", + "futures-lite", + "hashbrown 0.16.1", "icu_normalizer", - "indexmap 2.10.0", + "indexmap 2.13.0", "intrusive-collections", - "itertools 0.13.0", + "itertools 0.14.0", "num-bigint", "num-integer", "num-traits", "num_enum", - "once_cell", - "pollster", + "paste", "portable-atomic", - "rand 0.8.5", + "rand 0.9.2", "regress", "rustc-hash 2.1.1", "ryu-js", "serde", "serde_json", - "sptr", + "small_btree", "static_assertions", + "tag_ptr", "tap", "thin-vec", - "thiserror 2.0.12", + "thiserror 2.0.18", "time", + "xsum", ] [[package]] name = "boa_gc" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2425c0b7720d42d73eaa6a883fbb77a5c920da8694964a3d79a67597ac55cce2" +checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" dependencies = [ "boa_macros", - "boa_profiler", "boa_string", - "hashbrown 0.15.4", + "hashbrown 0.16.1", "thin-vec", ] [[package]] name = "boa_interner" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42407a3b724cfaecde8f7d4af566df4b56af32a2f11f0956f5570bb974e7f749" +checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" dependencies = [ "boa_gc", "boa_macros", - "hashbrown 0.15.4", - "indexmap 2.10.0", + "hashbrown 0.16.1", + "indexmap 2.13.0", "once_cell", - "phf", + "phf 0.13.1", "rustc-hash 2.1.1", "static_assertions", ] [[package]] name = "boa_macros" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd3f870829131332587f607a7ff909f1af5fc523fd1b192db55fbbdf52e8d3c" +checksum = "7f36418a46544b152632c141b0a0b7a453cd69ca150caeef83aee9e2f4b48b7d" dependencies = [ + "cfg-if", + "cow-utils", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", "synstructure", ] [[package]] name = "boa_parser" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc142dac798cdc6e2dbccfddeb50f36d2523bb977a976e19bdb3ae19b740804" +checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "boa_ast", "boa_interner", "boa_macros", - "boa_profiler", "fast-float2", "icu_properties", "num-bigint", @@ -2327,30 +2397,25 @@ dependencies = [ "rustc-hash 2.1.1", ] -[[package]] -name = "boa_profiler" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4064908e7cdf9b6317179e9b04dcb27f1510c1c144aeab4d0394014f37a0f922" - [[package]] name = "boa_string" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7debc13fbf7997bf38bf8e9b20f1ad5e2a7d27a900e1f6039fe244ce30f589b5" +checksum = "45ce9d7aa5563a2e14eab111e2ae1a06a69a812f6c0c3d843196c9d03fbef440" dependencies = [ "fast-float2", + "itoa", "paste", "rustc-hash 2.1.1", - "sptr", + "ryu-js", "static_assertions", ] [[package]] name = "bon" -version = "3.6.5" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d9ef19ae5263a138da9a86871eca537478ab0332a7770bac7e3f08b801f89f" +checksum = "2d13a61f2963b88eef9c1be03df65d42f6996dfeac1054870d950fcf66686f83" dependencies = [ "bon-macros", "rustversion", @@ -2358,9 +2423,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.6.5" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577ae008f2ca11ca7641bd44601002ee5ab49ef0af64846ce1ab6057218a5cc1" +checksum = "d314cc62af2b6b0c65780555abb4d02a03dd3b799cd42419044f0c38d99738c0" dependencies = [ "darling 0.21.0", "ident_case", @@ -2368,7 +2433,30 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.104", + "syn 2.0.117", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -2394,7 +2482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", - "regex-automata 0.4.9", + "regex-automata", "serde", ] @@ -2412,22 +2500,22 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.0" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -2438,9 +2526,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -2457,9 +2545,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.1" +version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" +checksum = "1a0f582957c24870b7bfd12bf562c40b4734b533cafbaf8ded31d6d85f462c01" dependencies = [ "blst", "cc", @@ -2502,15 +2590,9 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "cast" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -2526,6 +2608,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", + "alloy-rpc-types-beacon", "alloy-serde", "alloy-signer", "alloy-signer-local", @@ -2535,23 +2618,24 @@ dependencies = [ "chrono", "clap", "clap_complete", - "clap_complete_fig", "comfy-table", + "dirs", "dunce", "evmole", "eyre", + "forge-fmt", "foundry-block-explorers", "foundry-cli", "foundry-common", "foundry-compilers", "foundry-config", + "foundry-debugger", "foundry-evm", - "foundry-evm-core", + "foundry-primitives", "foundry-test-utils", "foundry-wallets", "futures", "itertools 0.14.0", - "op-alloy-consensus", "op-alloy-flz", "rand 0.8.5", "rand 0.9.2", @@ -2588,6 +2672,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -2611,7 +2701,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chisel" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -2619,6 +2709,7 @@ dependencies = [ "clap", "dirs", "eyre", + "forge-doc", "forge-fmt", "foundry-cli", "foundry-common", @@ -2626,35 +2717,35 @@ dependencies = [ "foundry-config", "foundry-evm", "foundry-solang-parser", - "regex", + "foundry-test-utils", + "itertools 0.14.0", "reqwest", - "revm", + "rexpect", "rustyline", "semver 1.0.26", "serde", "serde_json", - "solar-parse", - "strum 0.27.2", + "solar-compiler", + "tempfile", "time", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", "walkdir", "yansi", ] [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2750,10 +2841,10 @@ dependencies = [ ] [[package]] -name = "clap_complete_fig" -version = "4.5.2" +name = "clap_complete_nushell" +version = "4.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d494102c8ff3951810c72baf96910b980fb065ca5d3101243e6a8dc19747c86b" +checksum = "685bc86fd34b7467e0532a4f8435ab107960d69a243785ef0275e571b35b641a" dependencies = [ "clap", "clap_complete", @@ -2768,7 +2859,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -2785,7 +2876,7 @@ checksum = "85a8ab73a1c02b0c15597b22e09c7dc36e63b2f601f9d1e83ac0c3decd38b1ae" dependencies = [ "nix 0.29.0", "terminfo", - "thiserror 2.0.12", + "thiserror 2.0.18", "which 8.0.0", "windows-sys 0.59.0", ] @@ -2907,7 +2998,7 @@ dependencies = [ "eyre", "indenter", "once_cell", - "owo-colors 4.2.2", + "owo-colors", "tracing-error", ] @@ -2918,7 +3009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" dependencies = [ "once_cell", - "owo-colors 4.2.2", + "owo-colors", "tracing-core", "tracing-error", ] @@ -2931,12 +3022,21 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" -version = "2.2.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.60.2", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", ] [[package]] @@ -2958,9 +3058,9 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "compact_str" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" dependencies = [ "castaway", "cfg-if", @@ -3056,9 +3156,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -3089,6 +3189,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -3122,6 +3228,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -3162,13 +3274,10 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "crossterm_winapi", - "mio", "parking_lot", "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", "winapi", ] @@ -3178,13 +3287,13 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "crossterm_winapi", - "derive_more 2.0.1", + "derive_more", "document-features", "mio", "parking_lot", - "rustix 1.0.8", + "rustix 1.1.4", "signal-hook", "signal-hook-mio", "winapi", @@ -3227,29 +3336,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cssparser" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa", - "phf", - "smallvec", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.104", -] - [[package]] name = "ctr" version = "0.9.2" @@ -3270,12 +3356,25 @@ dependencies = [ ] [[package]] -name = "darling" -version = "0.20.11" +name = "curve25519-dalek-ng" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" dependencies = [ - "darling_core 0.20.11", + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", "darling_macro 0.20.11", ] @@ -3300,7 +3399,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3313,8 +3412,9 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3325,7 +3425,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3336,7 +3436,7 @@ checksum = "e79f8e61677d5df9167cd85265f8e5f64b215cdea3fb55eebc3e622e44c7a146" dependencies = [ "darling_core 0.21.0", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3372,12 +3472,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -3393,13 +3493,13 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3410,7 +3510,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3431,7 +3531,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3441,65 +3541,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.104", -] - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl 1.0.0", + "syn 2.0.117", ] [[package]] name = "derive_more" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" -dependencies = [ - "derive_more-impl 2.0.1", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", - "unicode-xid", + "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.104", + "rustc_version 0.4.1", + "syn 2.0.117", "unicode-xid", ] [[package]] name = "dialoguer" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" dependencies = [ - "console 0.15.11", + "console 0.16.0", "shell-words", - "tempfile", - "thiserror 1.0.69", "zeroize", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.9.0" @@ -3550,7 +3634,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3580,21 +3664,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" -[[package]] -name = "dtoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" - -[[package]] -name = "dtoa-short" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" -dependencies = [ - "dtoa", -] - [[package]] name = "dunce" version = "1.0.5" @@ -3607,6 +3676,26 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "dynify" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81acb15628a3e22358bf73de5e7e62360b8a777dbcb5fc9ac7dfa9ae73723747" +dependencies = [ + "dynify-macros", +] + +[[package]] +name = "dynify-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -3622,6 +3711,21 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "thiserror 1.0.69", + "zeroize", +] + [[package]] name = "educe" version = "0.6.0" @@ -3631,9 +3735,15 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] +[[package]] +name = "ego-tree" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" + [[package]] name = "either" version = "1.15.0" @@ -3736,7 +3846,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -3768,6 +3878,26 @@ dependencies = [ "log", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -3822,6 +3952,61 @@ dependencies = [ "uuid 0.8.2", ] +[[package]] +name = "ethereum_serde_utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc1355dbb41fbbd34ec28d4fb2a57d9a70c67ac3c19f6a5ca4d4a176b9e997a" +dependencies = [ + "alloy-primitives", + "hex", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ethereum_ssz" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dcddb2554d19cde19b099fadddde576929d7a4d0c1cd3512d1fd95cf174375c" +dependencies = [ + "alloy-primitives", + "ethereum_serde_utils", + "itertools 0.13.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2128a84f7a3850d54ee343334e3392cca61f9f6aa9441eec481b9394b43c238b" +dependencies = [ + "alloy-primitives", + "ethereum_serde_utils", + "itertools 0.14.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz_derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "event-listener" version = "4.0.3" @@ -3835,9 +4020,9 @@ dependencies = [ [[package]] name = "evm-disassembler" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded685d9f07315ff689ba56e7d84e6f1e782db19b531a46c34061a733bba7258" +checksum = "ace07bd9e99c61333fa7b95b264f0f814bdfc4ec6714f67ffd8e703b25edb2be" dependencies = [ "eyre", "hex", @@ -3851,7 +4036,7 @@ checksum = "c29ecc930ee2ed03083436c2ddd7e5292c3c3bcda65f6a37369502d578a853f1" dependencies = [ "alloy-dyn-abi", "alloy-primitives", - "indexmap 2.10.0", + "indexmap 2.13.0", ] [[package]] @@ -3905,7 +4090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.0.8", + "rustix 1.1.4", "windows-sys 0.59.0", ] @@ -3930,17 +4115,16 @@ dependencies = [ ] [[package]] -name = "figment" -version = "0.10.19" +name = "figment2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +checksum = "4380ce44915a6227efbb61e3885bc1c8e99fb9820f5db612abfac2c5cfc46871" dependencies = [ "atomic", "parking_lot", - "pear", "serde", "tempfile", - "toml 0.8.23", + "toml_edit 0.23.10+spec-1.0.0", "uncased", "version_check", ] @@ -3974,6 +4158,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float16" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bffafbd079d520191c7c2779ae9cf757601266cf4167d3f659ff09617ff8483" +dependencies = [ + "cfg-if", + "rustc_version 0.2.3", +] + [[package]] name = "fnv" version = "1.0.7" @@ -3986,9 +4180,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "font-awesome-as-a-crate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc" + [[package]] name = "forge" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", @@ -4007,7 +4213,6 @@ dependencies = [ "chrono", "clap", "clap_complete", - "clap_complete_fig", "clearscreen", "comfy-table", "dunce", @@ -4027,23 +4232,21 @@ dependencies = [ "foundry-config", "foundry-debugger", "foundry-evm", - "foundry-evm-core", + "foundry-evm-networks", "foundry-linking", - "foundry-solang-parser", "foundry-test-utils", "foundry-wallets", - "futures", "globset", - "indicatif 0.18.0", + "indicatif 0.18.4", "inferno", "itertools 0.14.0", "mockall", "opener", "parking_lot", - "paste", "path-slash", "proptest", "quick-junit", + "rand 0.9.2", "rayon", "regex", "reqwest", @@ -4053,17 +4256,18 @@ dependencies = [ "serde_json", "similar", "similar-asserts", - "solar-parse", - "solar-sema", + "solar-compiler", "soldeer-commands", - "strum 0.27.2", + "soldeer-core", + "strum", "svm-rs", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", - "toml_edit 0.23.2", + "toml_edit 0.24.1+spec-1.1.0", "tower-http", "tracing", + "url", "watchexec", "watchexec-events", "watchexec-signals", @@ -4072,10 +4276,10 @@ dependencies = [ [[package]] name = "forge-doc" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-primitives", - "derive_more 2.0.1", + "derive_more", "eyre", "forge-fmt", "foundry-common", @@ -4083,52 +4287,48 @@ dependencies = [ "foundry-config", "foundry-solang-parser", "itertools 0.14.0", - "mdbook", + "mdbook-driver", "rayon", "regex", "serde", "serde_json", - "thiserror 2.0.12", - "toml 0.9.4", + "solar-compiler", + "thiserror 2.0.18", + "toml", "tracing", ] [[package]] name = "forge-fmt" -version = "1.3.1" +version = "1.6.0" dependencies = [ - "alloy-primitives", - "ariadne", + "foundry-common", "foundry-config", - "foundry-solang-parser", + "foundry-test-utils", "itertools 0.14.0", - "similar-asserts", - "thiserror 2.0.12", - "toml 0.9.4", - "tracing", - "tracing-subscriber 0.3.19", + "similar", + "snapbox", + "solar-compiler", + "toml", ] [[package]] name = "forge-lint" -version = "1.3.1" +version = "1.6.0" dependencies = [ + "eyre", "foundry-common", "foundry-compilers", "foundry-config", "heck", "rayon", - "solar-ast", - "solar-data-structures", - "solar-interface", - "solar-parse", - "solar-sema", - "thiserror 2.0.12", + "solar-compiler", + "thiserror 2.0.18", ] [[package]] name = "forge-script" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4147,7 +4347,6 @@ dependencies = [ "eyre", "forge-script-sequence", "forge-verify", - "foundry-block-explorers", "foundry-cheatcodes", "foundry-cli", "foundry-common", @@ -4158,7 +4357,7 @@ dependencies = [ "foundry-linking", "foundry-wallets", "futures", - "indicatif 0.18.0", + "indicatif 0.18.4", "itertools 0.14.0", "parking_lot", "revm-inspectors", @@ -4166,6 +4365,7 @@ dependencies = [ "serde", "serde_json", "tempfile", + "thiserror 2.0.18", "tokio", "tracing", "yansi", @@ -4173,7 +4373,7 @@ dependencies = [ [[package]] name = "forge-script-sequence" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-network", "alloy-primitives", @@ -4189,7 +4389,7 @@ dependencies = [ [[package]] name = "forge-sol-macro-gen" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -4199,13 +4399,12 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "serde_json", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "forge-verify" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -4221,7 +4420,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm", - "foundry-evm-core", + "foundry-evm-networks", "foundry-test-utils", "futures", "itertools 0.14.0", @@ -4234,6 +4433,7 @@ dependencies = [ "tempfile", "tokio", "tracing", + "url", "yansi", ] @@ -4248,7 +4448,7 @@ dependencies = [ [[package]] name = "foundry-bench" -version = "0.1.0" +version = "1.6.0" dependencies = [ "chrono", "clap", @@ -4256,22 +4456,18 @@ dependencies = [ "eyre", "foundry-common", "foundry-compilers", - "foundry-config", "foundry-test-utils", - "num_cpus", "once_cell", "rayon", "serde", "serde_json", - "tempfile", - "tokio", ] [[package]] name = "foundry-block-explorers" -version = "0.20.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc107bbc3b4480995fdf337ca0ddedc631728175f418d3136ead9df8f4dc465e" +checksum = "ff814624bb21bfe43b70fb736ab39527b405d04cdc94d90b7e182fba28b25ec7" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -4281,13 +4477,13 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", ] [[package]] name = "foundry-cheatcodes" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4307,6 +4503,7 @@ dependencies = [ "base64 0.22.1", "dialoguer", "ecdsa", + "ed25519-consensus", "eyre", "forge-script-sequence", "foundry-cheatcodes-spec", @@ -4314,7 +4511,9 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm-core", + "foundry-evm-fuzz", "foundry-evm-traces", + "foundry-primitives", "foundry-wallets", "itertools 0.14.0", "jsonpath_lib", @@ -4329,26 +4528,27 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "thiserror 2.0.12", - "toml 0.9.4", + "solar-compiler", + "thiserror 2.0.18", + "toml", "tracing", "walkdir", ] [[package]] name = "foundry-cheatcodes-spec" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-sol-types", "foundry-macros", - "schemars 1.0.4", + "schemars 1.2.1", "serde", "serde_json", ] [[package]] name = "foundry-cli" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", @@ -4360,43 +4560,54 @@ dependencies = [ "alloy-rlp", "cfg-if", "clap", + "clap_complete", + "clap_complete_nushell", "color-eyre", "dotenvy", "dunce", "eyre", - "forge-fmt", "foundry-block-explorers", + "foundry-cli-markdown", "foundry-common", "foundry-compilers", "foundry-config", - "foundry-debugger", "foundry-evm", "foundry-wallets", "futures", - "indicatif 0.18.0", + "indicatif 0.18.4", "itertools 0.14.0", "mimalloc", + "path-slash", "rayon", "regex", "rustls", "serde", "serde_json", - "solar-sema", + "solar-compiler", "strsim", - "strum 0.27.2", + "strum", "tempfile", "tikv-jemallocator", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", "tracing-tracy", "yansi", ] +[[package]] +name = "foundry-cli-markdown" +version = "1.6.0" +dependencies = [ + "clap", + "pretty_assertions", +] + [[package]] name = "foundry-common" -version = "1.3.1" +version = "1.6.0" dependencies = [ + "alloy-chains", "alloy-consensus", "alloy-dyn-abi", "alloy-eips", @@ -4434,25 +4645,24 @@ dependencies = [ "path-slash", "regex", "reqwest", + "revm", "semver 1.0.26", "serde", "serde_json", - "solar-parse", - "solar-sema", - "terminal_size", - "thiserror 2.0.12", + "solar-compiler", + "thiserror 2.0.18", "tokio", "tower", "tracing", "url", - "vergen", + "vergen-gitcl", "walkdir", "yansi", ] [[package]] name = "foundry-common-fmt" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -4461,46 +4671,44 @@ dependencies = [ "alloy-rpc-types", "alloy-serde", "chrono", + "eyre", "foundry-macros", + "foundry-primitives", "revm", "serde", "serde_json", "similar-asserts", + "tempo-alloy", "yansi", ] [[package]] name = "foundry-compilers" -version = "0.18.2" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e953daf389ec3f8d82566cfcdadae58ca01b1bf0a7c467b2b03d447971589df" +checksum = "e639f98fe54d1cc0011a4bdb2eb1d838b379c9f004991ae7555a4cc09e8da32a" dependencies = [ "alloy-json-abi", "alloy-primitives", "auto_impl", - "derive_more 1.0.0", - "dirs", + "derive_more", "dyn-clone", "foundry-compilers-artifacts", "foundry-compilers-core", "fs_extra", - "futures-util", - "home", "itertools 0.14.0", "path-slash", - "rand 0.8.5", + "rand 0.9.2", "rayon", "semver 1.0.26", "serde", "serde_json", "sha2 0.10.9", - "solar-parse", - "solar-sema", + "solar-compiler", "svm-rs", "svm-rs-builds", "tempfile", - "thiserror 2.0.12", - "tokio", + "thiserror 2.0.18", "tracing", "winnow", "yansi", @@ -4508,9 +4716,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.18.2" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88824bfa6a5c2d7ecd076ba232cf37532af611c764fc27bb74371a91f662c36" +checksum = "93ec96df20055211f4e46b5a61fa479b2ea7d1ce0659818e0359afadfcded8d2" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -4518,32 +4726,30 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.18.2" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55273eea0135b2d6e80d8475f660c87c855100999ce6f3e8c065e2fc55b95d7c" +checksum = "f8a206e475b5dd1a77dc33cd917cde4846148f5136729a24edb3a16ab431b90a" dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers-core", - "futures-util", + "memchr", "path-slash", "rayon", "regex", "semver 1.0.26", "serde", "serde_json", - "thiserror 2.0.12", - "tokio", + "thiserror 2.0.18", "tracing", - "walkdir", "yansi", ] [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.18.2" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d0254354ec10a7fc3b5a38ea0e6d9c841c35159f9b052e0bd87769e28c00da" +checksum = "f74883db8036522fa21d0853c21ac318e165ec88e141f1ef1d6f7b4dfa841ff7" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -4556,9 +4762,9 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.18.2" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0773827e62d4eba29b04dff112211c1cadb614352da1cc2d951c33ca446415be" +checksum = "2ab384daeaea5c33cad8c3c094a1eb6f98e70922e18380c660980c74c19e362b" dependencies = [ "alloy-primitives", "cfg-if", @@ -4571,7 +4777,7 @@ dependencies = [ "serde_json", "svm-rs", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "walkdir", "xxhash-rust", @@ -4579,7 +4785,7 @@ dependencies = [ [[package]] name = "foundry-config" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4587,49 +4793,51 @@ dependencies = [ "dirs", "dunce", "eyre", - "figment", + "figment2", "foundry-block-explorers", "foundry-compilers", + "foundry-evm-networks", "glob", "globset", "heck", "itertools 0.14.0", "mesc", - "number_prefix", "path-slash", + "rayon", "regex", "reqwest", "revm", - "schemars 1.0.4", + "schemars 1.2.1", "semver 1.0.26", "serde", "serde_json", "similar-asserts", - "solar-interface", - "solar-parse", + "snapbox", + "solar-compiler", "soldeer-core", "tempfile", - "thiserror 2.0.12", - "toml 0.9.4", - "toml_edit 0.23.2", + "thiserror 2.0.18", + "toml", + "toml_edit 0.24.1+spec-1.1.0", "tracing", + "unit-prefix", "walkdir", "yansi", ] [[package]] name = "foundry-config-spec" -version = "1.3.1" +version = "1.6.0" dependencies = [ "foundry-config", - "schemars 1.0.4", + "schemars 1.2.1", "serde", "serde_json", ] [[package]] name = "foundry-debugger" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "crossterm 0.29.0", @@ -4647,7 +4855,7 @@ dependencies = [ [[package]] name = "foundry-evm" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-evm", @@ -4663,26 +4871,29 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-fuzz", + "foundry-evm-networks", "foundry-evm-traces", - "indicatif 0.18.0", + "indicatif 0.18.4", "parking_lot", "proptest", + "rayon", "revm", "revm-inspectors", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", + "tokio", "tracing", - "uuid 1.17.0", + "uuid 1.21.0", ] [[package]] name = "foundry-evm-abi" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "alloy-sol-types", - "derive_more 2.0.1", + "derive_more", "foundry-common-fmt", "foundry-macros", "itertools 0.14.0", @@ -4690,7 +4901,7 @@ dependencies = [ [[package]] name = "foundry-evm-core" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4701,6 +4912,7 @@ dependencies = [ "alloy-json-abi", "alloy-network", "alloy-op-evm", + "alloy-op-hardforks", "alloy-primitives", "alloy-provider", "alloy-rpc-types", @@ -4711,6 +4923,7 @@ dependencies = [ "foundry-common", "foundry-config", "foundry-evm-abi", + "foundry-evm-networks", "foundry-fork-db", "foundry-test-utils", "futures", @@ -4721,7 +4934,7 @@ dependencies = [ "revm-inspectors", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -4729,7 +4942,7 @@ dependencies = [ [[package]] name = "foundry-evm-coverage" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "eyre", @@ -4739,12 +4952,13 @@ dependencies = [ "rayon", "revm", "semver 1.0.26", + "solar-compiler", "tracing", ] [[package]] name = "foundry-evm-fuzz" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -4762,18 +4976,34 @@ dependencies = [ "rand 0.9.2", "revm", "serde", - "thiserror 2.0.12", + "solar-compiler", + "thiserror 2.0.18", "tracing", ] +[[package]] +name = "foundry-evm-networks" +version = "1.6.0" +dependencies = [ + "alloy-chains", + "alloy-eips", + "alloy-evm", + "alloy-op-hardforks", + "alloy-primitives", + "clap", + "revm", + "serde", +] + [[package]] name = "foundry-evm-traces" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", "alloy-sol-types", + "async-trait", "eyre", "foundry-block-explorers", "foundry-common", @@ -4783,22 +5013,25 @@ dependencies = [ "foundry-linking", "futures", "itertools 0.14.0", + "memchr", "rayon", + "reqwest", "revm", "revm-inspectors", "serde", "serde_json", - "solar-parse", + "solar-compiler", "tempfile", "tokio", "tracing", + "yansi", ] [[package]] name = "foundry-fork-db" -version = "0.16.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdf390c3633b0eb14c6bb26a0aeb63ea0200f1350ccbe07493f23148f58c4a5" +checksum = "ad537243394b8cda523e63749f2c9833be4c612500d35518efad34a3d1086710" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4812,7 +5045,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -4820,22 +5053,48 @@ dependencies = [ [[package]] name = "foundry-linking" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-primitives", "foundry-compilers", + "rayon", "semver 1.0.26", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "foundry-macros" -version = "1.3.1" +version = "1.6.0" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", +] + +[[package]] +name = "foundry-primitives" +version = "1.6.0" +dependencies = [ + "alloy-consensus", + "alloy-evm", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rlp", + "alloy-rpc-types", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "derive_more", + "op-alloy-consensus", + "op-alloy-rpc-types", + "op-revm", + "revm", + "serde", + "serde_json", + "tempo-alloy", + "tempo-primitives", ] [[package]] @@ -4847,15 +5106,16 @@ dependencies = [ "itertools 0.14.0", "lalrpop", "lalrpop-util", - "phf", - "thiserror 2.0.12", + "phf 0.11.3", + "thiserror 2.0.18", "unicode-xid", ] [[package]] name = "foundry-test-utils" -version = "1.3.1" +version = "1.6.0" dependencies = [ + "alloy-chains", "alloy-primitives", "alloy-provider", "eyre", @@ -4868,18 +5128,20 @@ dependencies = [ "parking_lot", "rand 0.9.2", "regex", + "reqwest", "serde_json", "snapbox", + "svm-rs", "tempfile", "tokio", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", "ui_test", ] [[package]] name = "foundry-wallets" -version = "1.3.1" +version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -4891,21 +5153,28 @@ dependencies = [ "alloy-signer-ledger", "alloy-signer-local", "alloy-signer-trezor", + "alloy-signer-turnkey", "alloy-sol-types", "async-trait", "aws-config", - "aws-sdk-kms", + "axum", "clap", "derive_builder", "eth-keystore", "eyre", + "foundry-common", "foundry-config", - "gcloud-sdk", + "reqwest", "rpassword", "serde", - "thiserror 2.0.12", + "serde_json", + "thiserror 2.0.18", "tokio", + "tower", + "tower-http", "tracing", + "uuid 1.21.0", + "webbrowser", ] [[package]] @@ -4920,7 +5189,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" dependencies = [ - "rustix 1.0.8", + "rustix 1.1.4", "windows-sys 0.59.0", ] @@ -4980,6 +5249,19 @@ dependencies = [ "futures-sink", ] +[[package]] +name = "futures-concurrency" +version = "7.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" +dependencies = [ + "fixedbitset", + "futures-core", + "futures-lite", + "pin-project 1.1.10", + "smallvec", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -5003,6 +5285,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -5011,7 +5306,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -5052,9 +5347,9 @@ checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] name = "gcloud-sdk" -version = "0.27.3" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac903b34cd86b6e3479924e8a9517edba8d5deebee0c1013353b05108ea9bd3" +checksum = "c8458d2ad7741b6a16981b84e66b7e4d8026423096da721894769c6980d06ecc" dependencies = [ "async-trait", "bytes", @@ -5063,8 +5358,8 @@ dependencies = [ "hyper", "jsonwebtoken", "once_cell", - "prost", - "prost-types", + "prost 0.13.5", + "prost-types 0.13.5", "reqwest", "secret-vault-value", "serde", @@ -5125,11 +5420,36 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "getset" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "gimli" version = "0.31.1" @@ -5144,25 +5464,15 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "gmp-mpfr-sys" -version = "1.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d61197a68f6323b9afa616cf83d55d69191e1bf364d4eb7d35ae18defe776" -dependencies = [ - "libc", - "windows-sys 0.59.0", + "regex-automata", + "regex-syntax", ] [[package]] @@ -5188,7 +5498,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.10.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -5218,7 +5528,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] @@ -5241,8 +5551,20 @@ checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", "serde", + "serde_core", ] [[package]] @@ -5316,13 +5638,12 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.35.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4" +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" dependencies = [ "log", "markup5ever", - "match_token", ] [[package]] @@ -5401,13 +5722,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http 1.3.1", "http-body 1.0.1", @@ -5415,6 +5737,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -5503,54 +5826,36 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", + "serde", "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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "8b24a59706036ba941c9476a55cd57b82b77f38a3c667d637ee7cabbc85eaedc" dependencies = [ "displaydoc", "icu_collections", @@ -5559,65 +5864,60 @@ dependencies = [ "icu_provider", "smallvec", "utf16_iter", - "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "f5a97b8ac6235e69506e8dacfb2adf38461d2ce6d3e9bd9c94c4cbc3cd4400a4" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", + "serde", "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] [[package]] -name = "icu_provider_macros" -version = "1.5.0" +name = "id-arena" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "ident_case" @@ -5658,15 +5958,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.23" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.9", + "regex-automata", "same-file", "walkdir", "winapi-util", @@ -5689,7 +5989,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -5717,14 +6017,15 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] @@ -5742,9 +6043,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.0" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" dependencies = [ "console 0.16.0", "portable-atomic", @@ -5775,19 +6076,13 @@ dependencies = [ "str_stack", ] -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - [[package]] name = "inotify" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "inotify-sys", "libc", ] @@ -5820,7 +6115,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -5866,7 +6161,7 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "libc", ] @@ -5953,12 +6248,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", - "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde", - "windows-sys 0.59.0", ] [[package]] @@ -5969,23 +6262,30 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] -name = "jiff-tzdb" -version = "0.1.4" +name = "jni" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] [[package]] -name = "jiff-tzdb-platform" -version = "0.1.3" +name = "jni-sys" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" -dependencies = [ - "jiff-tzdb", -] +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" @@ -6048,20 +6348,31 @@ dependencies = [ "signature", ] +[[package]] +name = "kasuari" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures", ] [[package]] name = "keccak-asm" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -6100,9 +6411,9 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.5", + "regex-syntax", "sha3", - "string_cache", + "string_cache 0.8.9", "term", "unicode-xid", "walkdir", @@ -6114,7 +6425,7 @@ version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" dependencies = [ - "regex-automata 0.4.9", + "regex-automata", "rustversion", ] @@ -6130,6 +6441,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "levenshtein" version = "1.0.5" @@ -6138,9 +6455,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libloading" @@ -6174,56 +6491,10 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "libc", ] -[[package]] -name = "libsecp256k1" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" -dependencies = [ - "arrayref", - "base64 0.22.1", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - [[package]] name = "libusb1-sys" version = "0.7.0" @@ -6246,22 +6517,31 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "line-clipping" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - +checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" @@ -6298,25 +6578,16 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", ] [[package]] name = "lru" -version = "0.12.5" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown 0.15.4", -] - -[[package]] -name = "lru" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" -dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.16.1", ] [[package]] @@ -6339,20 +6610,14 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "markup5ever" -version = "0.35.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3" +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" dependencies = [ "log", "tendril", @@ -6360,71 +6625,137 @@ dependencies = [ ] [[package]] -name = "match_cfg" -version = "0.1.0" +name = "matchers" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] [[package]] -name = "match_token" -version = "0.35.0" +name = "matchit" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] -name = "matchers" -version = "0.1.0" +name = "mdbook-core" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "39a3873d4afac65583f1acb56ff058df989d5b4a2464bb02c785549727d307ee" dependencies = [ - "regex-automata 0.1.10", + "anyhow", + "regex", + "serde", + "serde_json", + "toml", + "tracing", ] [[package]] -name = "matchit" -version = "0.8.4" +name = "mdbook-driver" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +checksum = "a229930b29a9908560883e1f386eae25d8a971d259a80f49916a50627f04a42d" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "mdbook-core", + "mdbook-html", + "mdbook-markdown", + "mdbook-preprocessor", + "mdbook-renderer", + "mdbook-summary", + "regex", + "serde", + "serde_json", + "shlex", + "tempfile", + "toml", + "topological-sort", + "tracing", +] [[package]] -name = "mdbook" -version = "0.4.52" +name = "mdbook-html" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c284d2855916af7c5919cf9ad897cfc77d3c2db6f55429c7cfb769182030ec" +checksum = "9dee80c03c65e3212fb528b8c9be5568a6a85cf795d03cf9fd6ba39ad52069ca" dependencies = [ - "ammonia", "anyhow", - "chrono", - "clap", - "clap_complete", + "ego-tree", "elasticlunr-rs", - "env_logger", + "font-awesome-as-a-crate", "handlebars", "hex", - "log", - "memchr", - "opener", + "html5ever", + "indexmap 2.13.0", + "mdbook-core", + "mdbook-markdown", + "mdbook-renderer", "pulldown-cmark", "regex", "serde", "serde_json", "sha2 0.10.9", - "shlex", - "tempfile", - "toml 0.5.11", - "topological-sort", + "tracing", +] + +[[package]] +name = "mdbook-markdown" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c41bf35212f5d8b83e543aa6a4887dc5709c8489c5fb9ed00f1b51ce1a2cc6" +dependencies = [ + "pulldown-cmark", + "regex", + "tracing", +] + +[[package]] +name = "mdbook-preprocessor" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d87bf40be0597f26f0822f939a64f02bf92c4655ba04490aadbf83601a013bb" +dependencies = [ + "anyhow", + "mdbook-core", + "serde", + "serde_json", +] + +[[package]] +name = "mdbook-renderer" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ed59f225b3ae4283c56bea633db83184627a090d892908bd66990c68e10b43" +dependencies = [ + "anyhow", + "mdbook-core", + "serde", + "serde_json", +] + +[[package]] +name = "mdbook-summary" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00d85b291d67a69c92e939450390fe34d6ea418a868c8f7b42f0b300af35a7b" +dependencies = [ + "anyhow", + "mdbook-core", + "memchr", + "pulldown-cmark", + "serde", + "tracing", ] [[package]] name = "memchr" -version = "2.7.5" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -6474,7 +6805,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -6531,9 +6862,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" dependencies = [ "cfg-if", "downcast", @@ -6545,16 +6876,43 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", +] + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -6567,7 +6925,7 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a17d82edb1c8a6c20c238747ae7aae9181133e766bc92cd2556fdd764407d0d1" dependencies = [ - "uuid 1.17.0", + "uuid 1.21.0", ] [[package]] @@ -6598,7 +6956,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -6610,7 +6968,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -6653,7 +7011,7 @@ version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3163f59cd3fa0e9ef8c32f242966a7b9994fd7378366099593e0e73077cd8c97" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "fsevent-sys", "inotify", "kqueue", @@ -6673,12 +7031,11 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.60.2", ] [[package]] @@ -6717,9 +7074,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-format" @@ -6816,7 +7173,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -6848,6 +7205,31 @@ dependencies = [ "smallvec", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + [[package]] name = "object" version = "0.36.7" @@ -6862,6 +7244,10 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "once_cell_polyfill" @@ -6881,11 +7267,24 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "op-alloy" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b8fee21003dd4f076563de9b9d26f8c97840157ef78593cd7f262c5ca99848" +dependencies = [ + "op-alloy-consensus", + "op-alloy-network", + "op-alloy-provider", + "op-alloy-rpc-types", + "op-alloy-rpc-types-engine", +] + [[package]] name = "op-alloy-consensus" -version = "0.18.14" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c88d2940558fd69f8f07b3cbd7bb3c02fc7d31159c1a7ba9deede50e7881024" +checksum = "736381a95471d23e267263cfcee9e1d96d30b9754a94a2819148f83379de8a86" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6894,9 +7293,9 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types-eth", "alloy-serde", - "derive_more 2.0.1", + "derive_more", "serde", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] @@ -6905,11 +7304,42 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc" +[[package]] +name = "op-alloy-network" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4034183dca6bff6632e7c24c92e75ff5f0eabb58144edb4d8241814851334d47" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-signer", + "op-alloy-consensus", + "op-alloy-rpc-types", +] + +[[package]] +name = "op-alloy-provider" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6753d90efbaa8ea8bcb89c1737408ca85fa60d7adb875049d3f382c063666f86" +dependencies = [ + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-engine", + "alloy-transport", + "async-trait", + "op-alloy-rpc-types-engine", +] + [[package]] name = "op-alloy-rpc-types" -version = "0.18.14" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22201e53e8cbb67a053e88b534b4e7f02265c5406994bf35978482a9ad0ae26" +checksum = "ddd87c6b9e5b6eee8d6b76f41b04368dca0e9f38d83338e5b00e730c282098a4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6917,21 +7347,42 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", - "derive_more 2.0.1", + "derive_more", "op-alloy-consensus", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", +] + +[[package]] +name = "op-alloy-rpc-types-engine" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727699310a18cdeed32da3928c709e2704043b6584ed416397d5da65694efc" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "alloy-serde", + "derive_more", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive", + "op-alloy-consensus", + "serde", + "sha2 0.10.9", + "snap", + "thiserror 2.0.18", ] [[package]] name = "op-revm" -version = "8.1.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce1dc7533f4e5716c55cd3d62488c6200cb4dfda96e0c75a7e484652464343b" +checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40" dependencies = [ "auto_impl", - "once_cell", "revm", "serde", ] @@ -6971,18 +7422,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "owo-colors" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" - [[package]] name = "owo-colors" version = "4.2.2" @@ -6998,18 +7437,10 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", + "serdect", "sha2 0.10.9", ] -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width 0.1.14", -] - [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -7035,7 +7466,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -7098,29 +7529,6 @@ dependencies = [ "hmac", ] -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.104", -] - [[package]] name = "pem" version = "3.0.5" @@ -7153,7 +7561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.18", "ucd-trie", ] @@ -7177,7 +7585,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -7197,7 +7605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.10.0", + "indexmap 2.13.0", ] [[package]] @@ -7216,8 +7624,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", "serde", ] @@ -7227,8 +7645,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", ] [[package]] @@ -7237,21 +7665,44 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -7263,6 +7714,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "0.4.30" @@ -7300,7 +7760,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -7331,17 +7791,11 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" @@ -7352,6 +7806,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -7399,14 +7862,23 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "prettydiff" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abec3fb083c10660b3854367697da94c674e9e82aa7511014dc958beeb7215e9" +checksum = "ac17546d82912e64874e3d5b40681ce32eac4e5834344f51efcf689ff1550a65" dependencies = [ - "owo-colors 3.5.0", - "pad", + "owo-colors", ] [[package]] @@ -7416,7 +7888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -7426,6 +7898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve", + "serdect", ] [[package]] @@ -7467,7 +7940,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -7479,19 +7952,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", - "version_check", - "yansi", -] - [[package]] name = "process-wrap" version = "8.2.1" @@ -7499,7 +7959,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3ef4f2f0422f23a82ec9f628ea2acd12871c81a9362b02c43c1aa86acfc3ba1" dependencies = [ "futures", - "indexmap 2.10.0", + "indexmap 2.13.0", "nix 0.30.1", "tokio", "tracing", @@ -7508,19 +7968,18 @@ dependencies = [ [[package]] name = "proptest" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.1", - "lazy_static", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax 0.8.5", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -7528,13 +7987,23 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", ] [[package]] @@ -7544,7 +8013,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.13.5", +] + +[[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.117", ] [[package]] @@ -7557,7 +8039,16 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost 0.12.6", ] [[package]] @@ -7566,14 +8057,14 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" dependencies = [ - "prost", + "prost 0.13.5", ] [[package]] name = "protobuf" -version = "3.3.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" dependencies = [ "once_cell", "protobuf-support", @@ -7582,20 +8073,20 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.3.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ "thiserror 1.0.69", ] [[package]] name = "pulldown-cmark" -version = "0.10.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" +checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "memchr", "pulldown-cmark-escape", "unicase", @@ -7603,9 +8094,9 @@ dependencies = [ [[package]] name = "pulldown-cmark-escape" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" [[package]] name = "quick-error" @@ -7615,17 +8106,17 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-junit" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed1a693391a16317257103ad06a88c6529ac640846021da7c435a06fffdacd7" +checksum = "6ee9342d671fae8d66b3ae9fd7a9714dfd089c04d2a8b1ec0436ef77aee15e5f" dependencies = [ "chrono", - "indexmap 2.10.0", + "indexmap 2.13.0", "newtype-uuid", - "quick-xml 0.37.5", + "quick-xml 0.38.4", "strip-ansi-escapes", - "thiserror 2.0.12", - "uuid 1.17.0", + "thiserror 2.0.18", + "uuid 1.21.0", ] [[package]] @@ -7646,6 +8137,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.8" @@ -7660,7 +8160,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustls", "socket2 0.5.10", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -7681,7 +8181,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -7703,9 +8203,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -7716,6 +8216,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -7803,32 +8309,84 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rand 0.9.2", + "rustversion", +] + [[package]] name = "ratatui" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" dependencies = [ - "bitflags 2.9.1", - "cassowary", + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.0", "compact_str", - "crossterm 0.28.1", + "hashbrown 0.16.1", "indoc", - "instability", - "itertools 0.13.0", - "lru 0.12.5", - "paste", - "strum 0.26.3", + "itertools 0.14.0", + "kasuari", + "lru", + "strum", + "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", ] +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm 0.29.0", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.16.1", + "indoc", + "instability", + "itertools 0.14.0", + "line-clipping", + "ratatui-core", + "strum", + "time", + "unicode-segmentation", + "unicode-width 0.2.0", +] + [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -7836,9 +8394,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -7856,7 +8414,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", ] [[package]] @@ -7867,7 +8425,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] @@ -7887,39 +8445,30 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] @@ -7928,12 +8477,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -7998,13 +8541,91 @@ dependencies = [ "webpki-roots 1.0.2", ] +[[package]] +name = "reth-codecs" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "bytes", + "modular-bitfield", + "op-alloy-consensus", + "reth-codecs-derive", + "reth-zstd-compressors", + "serde", +] + +[[package]] +name = "reth-codecs-derive" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "reth-ethereum-primitives" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "reth-codecs", + "reth-primitives-traits", + "serde", +] + +[[package]] +name = "reth-primitives-traits" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-trie", + "auto_impl", + "bytes", + "derive_more", + "once_cell", + "op-alloy-consensus", + "reth-codecs", + "revm-bytecode 7.1.1", + "revm-primitives 21.0.2", + "revm-state 8.1.1", + "secp256k1 0.30.0", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-zstd-compressors" +version = "1.10.0" +source = "git+https://github.com/paradigmxyz/reth?rev=b25f32a977b489f9b84254c7811a2a5a25a81369#b25f32a977b489f9b84254c7811a2a5a25a81369" +dependencies = [ + "zstd", +] + [[package]] name = "revm" -version = "27.1.0" +version = "34.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6bf82101a1ad8a2b637363a37aef27f88b4efc8a6e24c72bf5f64923dc5532" +checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" dependencies = [ - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-context", "revm-context-interface", "revm-database", @@ -8013,106 +8634,131 @@ dependencies = [ "revm-inspector", "revm-interpreter", "revm-precompile", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 9.0.0", ] [[package]] name = "revm-bytecode" -version = "6.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6922f7f4fbc15ca61ea459711ff75281cc875648c797088c34e4e064de8b8a7c" +checksum = "e2c6b5e6e8dd1e28a4a60e5f46615d4ef0809111c9e63208e55b5c7058200fb0" dependencies = [ "bitvec", - "once_cell", - "phf", - "revm-primitives", + "phf 0.13.1", + "revm-primitives 21.0.2", + "serde", +] + +[[package]] +name = "revm-bytecode" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d1e5c1eaa44d39d537f668bc5c3409dc01e5c8be954da6c83370bbdf006457" +dependencies = [ + "bitvec", + "phf 0.13.1", + "revm-primitives 22.1.0", + "serde", +] + +[[package]] +name = "revm-bytecode" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86e468df3cf5cf59fa7ef71a3e9ccabb76bb336401ea2c0674f563104cf3c5e" +dependencies = [ + "bitvec", + "phf 0.13.1", + "revm-primitives 22.1.0", "serde", ] [[package]] name = "revm-context" -version = "8.0.4" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd508416a35a4d8a9feaf5ccd06ac6d6661cd31ee2dc0252f9f7316455d71f9" +checksum = "892ff3e6a566cf8d72ffb627fdced3becebbd9ba64089c25975b9b028af326a5" dependencies = [ + "bitvec", "cfg-if", "derive-where", - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-context-interface", "revm-database-interface", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-context-interface" -version = "9.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc90302642d21c8f93e0876e201f3c5f7913c4fcb66fb465b0fd7b707dfe1c79" +checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98" dependencies = [ "alloy-eip2930", "alloy-eip7702", "auto_impl", "either", "revm-database-interface", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-database" -version = "7.0.2" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61495e01f01c343dd90e5cb41f406c7081a360e3506acf1be0fc7880bfb04eb" +checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" dependencies = [ "alloy-eips", - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-database-interface", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-database-interface" -version = "7.0.2" +version = "9.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20628d6cd62961a05f981230746c16854f903762d01937f13244716530bf98f" +checksum = "8bc1da862c71ba380b0e2ee0b19b186e282f931ba4f479dfbc83903294a5e596" dependencies = [ "auto_impl", "either", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 10.0.0", "serde", + "thiserror 2.0.18", ] [[package]] name = "revm-handler" -version = "8.1.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1529c8050e663be64010e80ec92bf480315d21b1f2dbf65540028653a621b27d" +checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2" dependencies = [ "auto_impl", "derive-where", - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-context", "revm-context-interface", "revm-database-interface", "revm-interpreter", "revm-precompile", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-inspector" -version = "8.1.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78db140e332489094ef314eaeb0bd1849d6d01172c113ab0eb6ea8ab9372926" +checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" dependencies = [ "auto_impl", "either", @@ -8120,17 +8766,17 @@ dependencies = [ "revm-database-interface", "revm-handler", "revm-interpreter", - "revm-primitives", - "revm-state", + "revm-primitives 22.1.0", + "revm-state 9.0.0", "serde", "serde_json", ] [[package]] name = "revm-inspectors" -version = "0.27.1" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aad27cab355b0aa905d0744f3222e716b40ad48b32276ac4b0a615f2c3364c97" +checksum = "6e435414e9de50a1b930da602067c76365fea2fea11e80ceb50783c94ddd127f" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -8143,26 +8789,27 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] name = "revm-interpreter" -version = "24.0.0" +version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff9d7d9d71e8a33740b277b602165b6e3d25fff091ba3d7b5a8d373bf55f28a7" +checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" dependencies = [ - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-context-interface", - "revm-primitives", + "revm-primitives 22.1.0", + "revm-state 9.0.0", "serde", ] [[package]] name = "revm-precompile" -version = "25.0.0" +version = "32.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cee3f336b83621294b4cfe84d817e3eef6f3d0fce00951973364cc7f860424d" +checksum = "e2ec11f45deec71e4945e1809736bb20d454285f9167ab53c5159dae1deb603f" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -8175,39 +8822,87 @@ dependencies = [ "c-kzg", "cfg-if", "k256", - "libsecp256k1", + "p256", + "revm-primitives 22.1.0", + "ripemd", + "secp256k1 0.31.1", + "sha2 0.10.9", +] + +[[package]] +name = "revm-primitives" +version = "21.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29e161db429d465c09ba9cbff0df49e31049fe6b549e28eb0b7bd642fcbd4412" +dependencies = [ + "alloy-primitives", + "num_enum", "once_cell", - "p256", - "revm-primitives", - "ripemd", - "rug", - "secp256k1 0.31.1", - "sha2 0.10.9", + "serde", ] [[package]] name = "revm-primitives" -version = "20.1.0" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66145d3dc61c0d6403f27fc0d18e0363bb3b7787e67970a05c71070092896599" +checksum = "4bcfb5ce6cf18b118932bcdb7da05cd9c250f2cb9f64131396b55f3fe3537c35" dependencies = [ "alloy-primitives", "num_enum", + "once_cell", + "serde", +] + +[[package]] +name = "revm-state" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8be953b7e374dbdea0773cf360debed8df394ea8d82a8b240a6b5da37592fc" +dependencies = [ + "bitflags 2.11.0", + "revm-bytecode 7.1.1", + "revm-primitives 21.0.2", + "serde", +] + +[[package]] +name = "revm-state" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" +dependencies = [ + "alloy-eip7928", + "bitflags 2.11.0", + "revm-bytecode 8.0.0", + "revm-primitives 22.1.0", "serde", ] [[package]] name = "revm-state" -version = "7.0.2" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc830a0fd2600b91e371598e3d123480cd7bb473dd6def425a51213aa6c6d57" +checksum = "d29404707763da607e5d6e4771cb203998c28159279c2f64cc32de08d2814651" dependencies = [ - "bitflags 2.9.1", - "revm-bytecode", - "revm-primitives", + "alloy-eip7928", + "bitflags 2.11.0", + "revm-bytecode 9.0.0", + "revm-primitives 22.1.0", "serde", ] +[[package]] +name = "rexpect" +version = "0.6.2" +source = "git+https://github.com/rust-cli/rexpect?rev=2ed0b1898d7edaf6a85bedbae71a01cc578958fc#2ed0b1898d7edaf6a85bedbae71a01cc578958fc" +dependencies = [ + "comma", + "nix 0.30.1", + "regex", + "tempfile", + "thiserror 2.0.18", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -8281,28 +8976,17 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rug" -version = "1.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4207e8d668e5b8eb574bda8322088ccd0d7782d3d03c7e8d562e82ed82bdcbc3" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", - "libm", -] - [[package]] name = "ruint" -version = "1.15.0" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" dependencies = [ "alloy-rlp", "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", + "ark-ff 0.5.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -8316,7 +9000,7 @@ dependencies = [ "rand 0.9.2", "rlp", "ruint-macro", - "serde", + "serde_core", "valuable", "zeroize", ] @@ -8364,6 +9048,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.3.3" @@ -8400,7 +9093,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -8409,14 +9102,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys 0.12.1", "windows-sys 0.60.2", ] @@ -8472,9 +9165,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" @@ -8490,11 +9183,11 @@ dependencies = [ [[package]] name = "rustyline" -version = "16.0.0" +version = "17.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fd9ca5ebc709e8535e8ef7c658eb51457987e48c98ead2be482172accc408d" +checksum = "e902948a25149d50edc1a8e0141aad50f54e22ba83ff988cf8f7c9ef07f50564" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "cfg-if", "clipboard-win", "fd-lock", @@ -8507,7 +9200,7 @@ dependencies = [ "unicode-segmentation", "unicode-width 0.2.0", "utf8parse", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -8572,9 +9265,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -8585,14 +9278,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -8681,8 +9374,8 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc32a777b53b3433b974c9c26b6d502a50037f8da94e46cb8ce2ced2cfdfaea0" dependencies = [ - "prost", - "prost-types", + "prost 0.13.5", + "prost-types 0.13.5", "serde", "serde_json", "zeroize", @@ -8694,7 +9387,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -8711,13 +9404,22 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser", + "semver-parser 0.10.3", ] [[package]] @@ -8729,6 +9431,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "semver-parser" version = "0.10.3" @@ -8746,22 +9454,32 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -8772,7 +9490,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -8786,15 +9504,16 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.13.0", "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -8809,20 +9528,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_spanned" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -8847,9 +9557,9 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.2.1", "serde", "serde_derive", "serde_json", @@ -8866,7 +9576,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -8926,9 +9636,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" dependencies = [ "cc", "cfg-if", @@ -9029,7 +9739,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.12", + "thiserror 2.0.18", "time", ] @@ -9041,9 +9751,18 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "small_btree" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "0ba60d2df92ba73864714808ca68c059734853e6ab722b40e1cf543ebb3a057a" +dependencies = [ + "arrayvec", +] [[package]] name = "smallvec" @@ -9060,6 +9779,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "snapbox" version = "0.6.21" @@ -9108,41 +9833,52 @@ dependencies = [ [[package]] name = "solar-ast" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7f30449c304fd09db4637209dc73bde7ec203e6e5c691fc9eed26b68cd105a" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-primitives", "bumpalo", "either", - "num-bigint", "num-rational", "semver 1.0.26", "solar-data-structures", "solar-interface", "solar-macros", - "strum 0.27.2", - "typed-arena", + "strum", +] + +[[package]] +name = "solar-compiler" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" +dependencies = [ + "alloy-primitives", + "solar-ast", + "solar-config", + "solar-data-structures", + "solar-interface", + "solar-macros", + "solar-parse", + "solar-sema", ] [[package]] name = "solar-config" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643ddf85ab917f5643ec47eb79ee6db6c6158cfaf7415e39840e154eecf7176f" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ - "strum 0.27.2", + "colorchoice", + "strum", ] [[package]] name = "solar-data-structures" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc21b4df6061e1c825c16faf8e1f16c2341f4c46a2b2a60e03069c4453fc5ac" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "bumpalo", "index_vec", - "indexmap 2.10.0", + "indexmap 2.13.0", "parking_lot", "rayon", "rustc-hash 2.1.1", @@ -9151,58 +9887,56 @@ dependencies = [ [[package]] name = "solar-interface" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2571bfb2f54c5a24688afe6876682117e96f6fd35393398dbac43e5f6f7144" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.12.5", "anstream", "anstyle", - "const-hex", - "derive_more 2.0.1", + "derive_more", "dunce", "inturn", "itertools 0.14.0", "itoa", - "match_cfg", "normalize-path", + "once_map", "rayon", "scoped-tls", + "semver 1.0.26", "serde", "serde_json", "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.12", + "thiserror 1.0.69", "tracing", "unicode-width 0.2.0", ] [[package]] name = "solar-macros" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca57257a3b6ef16bd7995d23da3e661e89cebdae6fb9e42e4331ba0f033bd1d" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "solar-parse" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b4d9e2ddd3c7bb90079b3eb253b957ef9eb5c860ed6d6a4068884ec3a8b85" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-primitives", - "bitflags 2.9.1", + "bitflags 2.11.0", "bumpalo", "itertools 0.14.0", "memchr", "num-bigint", "num-rational", "num-traits", + "ruint", "smallvec", "solar-ast", "solar-data-structures", @@ -9212,15 +9946,14 @@ dependencies = [ [[package]] name = "solar-sema" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b017d4017ee8324e669c6e5e6fe9a282ed07eb6171e5384ddd17b8a854766f" +version = "0.1.8" +source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "alloy-json-abi", "alloy-primitives", - "bitflags 2.9.1", + "bitflags 2.11.0", "bumpalo", - "derive_more 2.0.1", + "derive_more", "either", "once_map", "paste", @@ -9232,23 +9965,22 @@ dependencies = [ "solar-interface", "solar-macros", "solar-parse", - "strum 0.27.2", + "strum", "thread_local", "tracing", - "typed-arena", ] [[package]] name = "soldeer-commands" -version = "0.6.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ff0e7ac2832b40dafe5b80811be1be41e6cab457c53aec3adcc80d8e03d02" +checksum = "dfd18a52d398ab7defa85ea5a3f94ca335e8ae0b40c76e8f7d5cc1ea0924e356" dependencies = [ "bon", "clap", "clap-verbosity-flag", "cliclack", - "derive_more 2.0.1", + "derive_more", "email-address-parser", "env_logger", "path-slash", @@ -9259,14 +9991,14 @@ dependencies = [ [[package]] name = "soldeer-core" -version = "0.6.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92fd37a392b41211f12efbe0d5475bc7effde301dddc088998be8ada02e39941" +checksum = "295509f269318c6e3f11ea3bd09b29c974d00403bdf5291577c0bc6adaa9c2fd" dependencies = [ "bon", "chrono", "const-hex", - "derive_more 2.0.1", + "derive_more", "dunce", "home", "ignore", @@ -9280,10 +10012,10 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", - "toml_edit 0.22.27", - "uuid 1.17.0", + "toml_edit 0.23.10+spec-1.0.0", + "uuid 1.21.0", "zip", "zip-extract", ] @@ -9309,12 +10041,6 @@ dependencies = [ "der", ] -[[package]] -name = "sptr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -9341,19 +10067,30 @@ checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared", + "phf_shared 0.11.3", + "precomputed-hash", +] + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", "precomputed-hash", - "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", ] @@ -9373,35 +10110,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - [[package]] name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.104", + "strum_macros", ] [[package]] @@ -9413,7 +10128,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -9422,6 +10137,12 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "sval" version = "2.14.1" @@ -9515,7 +10236,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "tempfile", - "thiserror 2.0.12", + "thiserror 1.0.69", "url", "zip", ] @@ -9545,9 +10266,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -9556,14 +10277,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a985ff4ffd7373e10e0fb048110fb11a162e5a4c47f92ddb8787a6f766b769" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -9583,7 +10304,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -9592,7 +10313,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -9607,6 +10328,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tag_ptr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0e973b34477b7823833469eb0f5a3a60370fef7a453e02d751b59180d0a5a05" + [[package]] name = "tap" version = "1.0.1" @@ -9615,15 +10342,68 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", - "windows-sys 0.59.0", + "rustix 1.1.4", + "windows-sys 0.60.2", +] + +[[package]] +name = "tempo-alloy" +version = "1.0.0" +source = "git+https://github.com/tempoxyz/tempo?tag=v1.0.0#9ad04e2058993dfba4984cc7d413474b0278d427" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-eips", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "derive_more", + "serde", + "tempo-contracts", + "tempo-primitives", +] + +[[package]] +name = "tempo-contracts" +version = "1.0.0" +source = "git+https://github.com/tempoxyz/tempo?tag=v1.0.0#9ad04e2058993dfba4984cc7d413474b0278d427" +dependencies = [ + "alloy-contract", + "alloy-primitives", + "alloy-sol-types", +] + +[[package]] +name = "tempo-primitives" +version = "1.0.0" +source = "git+https://github.com/tempoxyz/tempo?tag=v1.0.0#9ad04e2058993dfba4984cc7d413474b0278d427" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "base64 0.22.1", + "derive_more", + "p256", + "reth-codecs", + "reth-ethereum-primitives", + "reth-primitives-traits", + "serde", + "serde_json", + "sha2 0.10.9", ] [[package]] @@ -9652,7 +10432,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" dependencies = [ - "rustix 1.0.8", + "rustix 1.1.4", "windows-sys 0.59.0", ] @@ -9664,8 +10444,8 @@ checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" dependencies = [ "fnv", "nom", - "phf", - "phf_codegen", + "phf 0.11.3", + "phf_codegen 0.11.3", ] [[package]] @@ -9702,11 +10482,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.18", ] [[package]] @@ -9717,18 +10497,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -9771,9 +10551,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -9782,43 +10562,35 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", + "serde_core", "zerovec", ] @@ -9865,7 +10637,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -9921,35 +10693,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.8.23" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1" -dependencies = [ - "indexmap 2.10.0", - "serde", - "serde_spanned 1.0.0", - "toml_datetime 0.7.0", + "indexmap 2.13.0", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow", @@ -9958,19 +10709,16 @@ dependencies = [ [[package]] name = "toml_datetime" version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_datetime" -version = "0.7.0" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -9979,47 +10727,53 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", - "serde", - "serde_spanned 0.6.9", + "indexmap 2.13.0", "toml_datetime 0.6.11", - "toml_write", "winnow", ] [[package]] name = "toml_edit" -version = "0.23.2" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1dee9dc43ac2aaf7d3b774e2fba5148212bf2bd9374f4e50152ebe9afd03d42" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.10.0", - "toml_datetime 0.7.0", + "indexmap 2.13.0", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow", ] [[package]] -name = "toml_parser" -version = "1.0.1" +name = "toml_edit" +version = "0.24.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +checksum = "01f2eadbbc6b377a847be05f60791ef1058d9f696ecb51d2c07fe911d8569d8e" dependencies = [ + "indexmap 2.13.0", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_parser" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] [[package]] name = "toml_writer" -version = "1.0.2" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tonic" @@ -10040,7 +10794,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project 1.1.10", - "prost", + "prost 0.13.5", "rustls-native-certs", "socket2 0.5.10", "tokio", @@ -10066,7 +10820,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap 2.10.0", + "indexmap 2.13.0", "pin-project-lite", "slab", "sync_wrapper", @@ -10083,7 +10837,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", "bytes", "futures-core", "futures-util", @@ -10131,9 +10885,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -10143,20 +10897,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -10169,7 +10923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", ] [[package]] @@ -10194,14 +10948,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -10217,7 +10971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba" dependencies = [ "tracing-core", - "tracing-subscriber 0.3.19", + "tracing-subscriber 0.3.22", "tracy-client", ] @@ -10245,15 +10999,15 @@ dependencies = [ [[package]] name = "trezor-client" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10636211ab89c96ed2824adc5ec0d081e1080aeacc24c37abb318dcb31dcc779" +checksum = "87873db279766278a7e56b01139943e00a45afc079fc8fa6651e949f2234c3f6" dependencies = [ "byteorder", "hex", "protobuf", "rusb", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", ] @@ -10278,15 +11032,43 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.12", + "thiserror 2.0.18", "utf-8", ] [[package]] -name = "typed-arena" -version = "2.0.2" +name = "turnkey_api_key_stamper" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b72664037582371dfa96bfaa2e272446ea2551e269455e9fe3166445c76736" +dependencies = [ + "base64 0.22.1", + "hex", + "k256", + "p256", + "rand_core 0.6.4", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "turnkey_client" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +checksum = "4cbf8cea094b6536ecc5cfad42cd45d2f9abf523cc7dacf7de23f132412d0ec3" +dependencies = [ + "mime", + "prost 0.12.6", + "prost-types 0.12.6", + "reqwest", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", + "tokio", + "turnkey_api_key_stamper", +] [[package]] name = "typeid" @@ -10308,11 +11090,11 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "ui_test" -version = "0.30.2" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b56a6897cc4bb6f8daf1939b0b39cd9645856997f46f4d0b3e3cb7122dfe9251" +checksum = "ada249620d81f010b9a1472b63a5077ac7c722dd0f4bacf6528b313d0b8c15d8" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.11.5", "anyhow", "bstr", "cargo-platform", @@ -10321,7 +11103,7 @@ dependencies = [ "colored", "comma", "crossbeam-channel", - "indicatif 0.17.11", + "indicatif 0.18.4", "levenshtein", "prettydiff", "regex", @@ -10406,13 +11188,13 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-truncate" -version = "1.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" dependencies = [ - "itertools 0.13.0", + "itertools 0.14.0", "unicode-segmentation", - "unicode-width 0.1.14", + "unicode-width 0.2.0", ] [[package]] @@ -10435,9 +11217,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unit-prefix" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "untrusted" @@ -10454,6 +11236,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -10504,13 +11287,13 @@ dependencies = [ [[package]] name = "uuid" -version = "1.17.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.4.2", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -10564,14 +11347,41 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "8.3.2" +version = "10.0.0-beta.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +checksum = "4065d92137ae477a3701e18a7afccdf584229fe504eaa4d290bc722df4076141" dependencies = [ "anyhow", - "cfg-if", + "bon", + "rustversion", + "time", + "vergen-lib", +] + +[[package]] +name = "vergen-gitcl" +version = "10.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3911418c678932ff5b8f8162bfb73753b6f3d1e22bf37d07d53aff44a6f224" +dependencies = [ + "anyhow", + "bon", "rustversion", "time", + "vergen", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "10.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbabf186269781b2b81f38937d07c37bbd980853dea6edc89b90820c09aab4c3" +dependencies = [ + "anyhow", + "bon", + "getset", + "rustversion", ] [[package]] @@ -10638,6 +11448,24 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -10660,7 +11488,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -10695,7 +11523,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10709,6 +11537,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + [[package]] name = "wasm-streams" version = "0.4.2" @@ -10722,6 +11572,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.4", + "indexmap 2.13.0", + "semver 1.0.26", +] + [[package]] name = "wasmtimer" version = "0.4.2" @@ -10748,7 +11610,7 @@ dependencies = [ "miette", "normalize-path", "notify", - "thiserror 2.0.12", + "thiserror 2.0.18", "tokio", "tracing", "watchexec-events", @@ -10774,7 +11636,7 @@ checksum = "377729679262964c27e6a28f360a84b7aedb172b59841301c1c77922305dfd83" dependencies = [ "miette", "nix 0.30.1", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] @@ -10813,16 +11675,32 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.1.3" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" dependencies = [ - "phf", - "phf_codegen", - "string_cache", + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", "string_cache_codegen", ] +[[package]] +name = "webbrowser" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f00bb839c1cf1e3036066614cbdcd035ecf215206691ea646aa3c60a24f68f2" +dependencies = [ + "core-foundation 0.10.1", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + [[package]] name = "webpki-roots" version = "0.26.11" @@ -10860,7 +11738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ "env_home", - "rustix 1.0.8", + "rustix 1.1.4", "winsafe", ] @@ -10910,7 +11788,7 @@ dependencies = [ "windows-collections", "windows-core", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -10931,7 +11809,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -10943,7 +11821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -10955,7 +11833,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -10966,7 +11844,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -10975,6 +11853,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -10982,7 +11866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -10991,7 +11875,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -11002,7 +11886,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -11011,7 +11895,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -11041,6 +11934,21 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -11063,7 +11971,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -11080,9 +11988,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -11095,6 +12009,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -11107,6 +12027,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -11131,6 +12057,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -11143,6 +12075,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -11155,6 +12093,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -11167,6 +12111,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -11181,9 +12131,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -11194,13 +12144,101 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.11.0", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", ] [[package]] @@ -11211,9 +12249,9 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "ws_stream_wasm" @@ -11228,7 +12266,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper", - "thiserror 2.0.12", + "thiserror 2.0.18", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -11249,6 +12287,12 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" +[[package]] +name = "xsum" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0637d3a5566a82fa5214bae89087bc8c9fb94cd8e8a3c07feb691bb8d9c632db" + [[package]] name = "xxhash-rust" version = "0.8.15" @@ -11266,11 +12310,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -11278,13 +12321,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", "synstructure", ] @@ -11305,7 +12348,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -11325,7 +12368,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", "synstructure", ] @@ -11346,15 +12389,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", ] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ + "serde", "yoke", "zerofrom", "zerovec-derive", @@ -11362,13 +12417,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.117", ] [[package]] @@ -11380,7 +12435,7 @@ dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.10.0", + "indexmap 2.13.0", "memchr", "zopfli", ] @@ -11392,7 +12447,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fa5b9958fd0b5b685af54f2c3fa21fca05fe295ebaf3e77b6d24d96c4174037" dependencies = [ "log", - "thiserror 2.0.12", + "thiserror 2.0.18", "zip", ] @@ -11402,6 +12457,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zopfli" version = "0.8.2" @@ -11413,3 +12474,31 @@ dependencies = [ "log", "simd-adler32", ] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] From 3053d38bced0735050459294d6ae503b55ff63a8 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 6 Mar 2026 12:54:12 +0700 Subject: [PATCH 262/374] Create static.yml (#381) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/static.yml | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/static.yml diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000000000..0ba82305f82b2 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 417dca8fcec1714baad22df0a47eba7525a04ac8 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 6 Mar 2026 06:27:13 +0000 Subject: [PATCH 263/374] Potential fix for code scanning alert no. 170: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/util.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index d899a1e764629..0a3fad720ef57 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -13,7 +13,12 @@ use std::{ /// Using a fixed directory under the system temp dir avoids trusting the current /// working directory (which may be user-controlled) as a security boundary. static TEST_UTIL_BASE: LazyLock = LazyLock::new(|| { - let mut base = env::temp_dir(); + // Resolve the system temp directory to an absolute, canonical path where possible. + // If canonicalization fails for any reason, fall back to the raw temp_dir value. + let tmp = env::temp_dir(); + let mut base = tmp + .canonicalize() + .unwrap_or(tmp); base.push("foundry_test_utils"); // Ignore errors here; they will surface when the path is actually used. let _ = fs::create_dir_all(&base); From 1c2294924ae0c1e4f408f6c488d189d8d4ce30d4 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 13 Mar 2026 03:44:29 +0700 Subject: [PATCH 264/374] Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/util.rs | 65 +++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 6fdcbafb94404..ef76a4628819f 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -235,25 +235,28 @@ pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { fs::create_dir_all(dst)?; // Canonicalize the source once and treat it as the base directory for all traversal. let base = src.canonicalize()?; - copy_dir_filtered_inner(src, dst, &base, true) + // Canonicalize the destination once and treat it as the base directory for all writes. + let dst_base = dst.canonicalize()?; + copy_dir_filtered_inner(src, dst, &base, &dst_base, true) } fn copy_dir_filtered_inner( src: &Path, dst: &Path, - base: &Path, + base_src: &Path, + base_dst: &Path, is_root: bool, ) -> std::io::Result<()> { for entry in fs::read_dir(src)? { let entry = entry?; let ty = entry.file_type()?; let src_path = entry.path(); - // Ensure that any path we operate on stays within the original base directory. + // Ensure that any path we operate on stays within the original source base directory. let canonical_src_path = src_path.canonicalize()?; - if !canonical_src_path.starts_with(base) { + if !canonical_src_path.starts_with(base_src) { return Err(std::io::Error::new( std::io::ErrorKind::PermissionDenied, - "attempted to access path outside of allowed base directory", + "attempted to access path outside of allowed source base directory", )); } let dst_path = dst.join(entry.file_name()); @@ -266,10 +269,58 @@ fn copy_dir_filtered_inner( { continue; } + // Ensure that the destination directory stays within the allowed destination base. + let canonical_dst_dir = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // Directory does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_dir.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } fs::create_dir_all(&dst_path)?; - copy_dir_filtered_inner(&src_path, &dst_path, base, false)?; + copy_dir_filtered_inner(&src_path, &dst_path, base_src, base_dst, false)?; } else { - fs::copy(&canonical_src_path, &dst_path)?; + // Ensure that the destination file path stays within the allowed destination base. + let canonical_dst_path = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // File does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_path.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + fs::copy(&canonical_src_path, &canonical_dst_path)?; } } Ok(()) From 6ced0a646f6041dfb1f23bf580911bccd519a6aa Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 13 Mar 2026 00:00:51 +0000 Subject: [PATCH 265/374] Hardhat project (#389) * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot --- .circleci/ci_deploy.yml | 34 ++++++++++++++++ crates/test-utils/src/util.rs | 74 +++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 86c611c12a95a..ef76a4628819f 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -233,14 +233,32 @@ pub fn read_string(path: impl AsRef) -> String { /// copied to temporary test workspaces. pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { fs::create_dir_all(dst)?; - copy_dir_filtered_inner(src, dst, true) + // Canonicalize the source once and treat it as the base directory for all traversal. + let base = src.canonicalize()?; + // Canonicalize the destination once and treat it as the base directory for all writes. + let dst_base = dst.canonicalize()?; + copy_dir_filtered_inner(src, dst, &base, &dst_base, true) } -fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Result<()> { +fn copy_dir_filtered_inner( + src: &Path, + dst: &Path, + base_src: &Path, + base_dst: &Path, + is_root: bool, +) -> std::io::Result<()> { for entry in fs::read_dir(src)? { let entry = entry?; let ty = entry.file_type()?; let src_path = entry.path(); + // Ensure that any path we operate on stays within the original source base directory. + let canonical_src_path = src_path.canonicalize()?; + if !canonical_src_path.starts_with(base_src) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } let dst_path = dst.join(entry.file_name()); if ty.is_dir() { @@ -251,10 +269,58 @@ fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Re { continue; } + // Ensure that the destination directory stays within the allowed destination base. + let canonical_dst_dir = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // Directory does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_dir.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } fs::create_dir_all(&dst_path)?; - copy_dir_filtered_inner(&src_path, &dst_path, false)?; + copy_dir_filtered_inner(&src_path, &dst_path, base_src, base_dst, false)?; } else { - fs::copy(&src_path, &dst_path)?; + // Ensure that the destination file path stays within the allowed destination base. + let canonical_dst_path = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // File does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_path.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + fs::copy(&canonical_src_path, &canonical_dst_path)?; } } Ok(()) From da97bc160f9b6193d3acd5a38d9ccb3f74addad1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 26 Mar 2026 12:18:16 +0000 Subject: [PATCH 266/374] foundry-rs#13763 (#398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: update EtherlinkTestnet -> EtherlinkShadownet for alloy-chains v0.2.31 (#13763) Co-authored-by: Matthias Seitz <19890894+mattsse@users.noreply.github.com> * chore(evm): make `FoundryCfg` generic over `Spec` (#13757) * chore(deps): weekly `cargo update` (#13760) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/alloy-rs/evm.git` Updating git repository `https://github.com/foundry-rs/optimism` Updating git submodule `https://github.com/flashbots/op-rbuilder` Updating git submodule `https://github.com/foundry-rs/forge-std` Updating git submodule `https://github.com/runtimeverification/kontrol-cheatcodes` Updating git submodule `https://github.com/ethereum-optimism/lib-keccak` Updating git submodule `https://github.com/dapphub/ds-test` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts` Updating git submodule `https://github.com/a16z/erc4626-tests.git` Updating git submodule `https://github.com/safe-global/safe-contracts` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/transmissions11/solmate` Updating git submodule `https://github.com/ethereum-optimism/superchain-registry` Updating git submodule `https://github.com/flashbots/rollup-boost` Updating git repository `https://github.com/foundry-rs/foundry-fork-db` Updating git repository `https://github.com/paradigmxyz/revm-inspectors.git` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git submodule `https://github.com/foundry-rs/forge-std` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/tempoxyz/tempo-std` Updating git repository `https://github.com/stevencartavia/reth` Locking 31 packages to latest compatible versions Updating alloy-chains v0.2.30 -> v0.2.31 Updating alloy-trie v0.9.4 -> v0.9.5 Adding anstream v1.0.0 Unchanged anstream v0.6.21 (available: v1.0.0) Updating anstyle v1.0.13 -> v1.0.14 Updating anstyle-lossy v1.1.4 -> v1.1.5 Adding anstyle-parse v1.0.0 Updating bon v3.9.0 -> v3.9.1 Updating bon-macros v3.9.0 -> v3.9.1 Updating c-kzg v2.1.6 -> v2.1.7 Updating cc v1.2.56 -> v1.2.57 Updating clap v4.5.60 -> v4.6.0 Updating clap_builder v4.5.60 -> v4.6.0 Updating clap_complete v4.5.66 -> v4.6.0 Updating clap_complete_nushell v4.5.10 -> v4.6.0 Updating clap_derive v4.5.55 -> v4.6.0 Updating clap_lex v1.0.0 -> v1.1.0 Updating colorchoice v1.0.4 -> v1.0.5 Updating console v0.16.2 -> v0.16.3 Removing darling v0.21.3 Removing darling_core v0.21.3 Removing darling_macro v0.21.3 Updating derive-where v1.6.0 -> v1.6.1 Updating evmole v0.8.2 -> v0.8.4 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating kasuari v0.4.11 -> v0.4.12 Unchanged matchit v0.8.4 (available: v0.8.6) Updating once_cell v1.21.3 -> v1.21.4 Unchanged op-revm v15.0.0 (available: v17.0.0) Updating portable-atomic-util v0.2.5 -> v0.2.6 Unchanged quick-junit v0.5.2 (available: v0.6.0) Unchanged rand v0.8.5 (available: v0.10.0) Unchanged rand v0.9.2 (available: v0.10.0) Unchanged revm v34.0.0 (available: v36.0.0) Updating schannel v0.1.28 -> v0.1.29 Updating serde_with v3.17.0 -> v3.18.0 Updating serde_with_macros v3.17.0 -> v3.18.0 Unchanged snapbox v0.6.24 (available: v1.1.0) Unchanged soldeer-commands v0.10.0 (available: v0.10.1) Unchanged soldeer-core v0.10.0 (available: v0.10.1) Unchanged strum v0.27.2 (available: v0.28.0) Updating tempfile v3.26.0 -> v3.27.0 Updating tinyvec v1.10.0 -> v1.11.0 Unchanged toml v0.9.12+spec-1.1.0 (available: v1.0.6+spec-1.1.0) Unchanged toml_edit v0.24.1+spec-1.1.0 (available: v0.25.4+spec-1.1.0) Updating tracing-subscriber v0.3.22 -> v0.3.23 Updating zerocopy v0.8.41 -> v0.8.42 Updating zerocopy-derive v0.8.41 -> v0.8.42 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * feat(cheatcodes): bubble-up `Network` generic to `Wallets` (#13768) * feat(script): generic `TxStatus` receipt type (#13770) feat(script): generic `TxStatus` * chore(script): idiomatic `BroadcastReader::into_tx_receipts` (#13771) * refactor(evm): simplify `Backend::initialize` and `CowBackend::backend_mut` (#13755) `initialize` now takes `(SpecId, Address, TxKind)` instead of `&Env`, since those are the only fields it reads. This removes the need for `backend_mut` to clone `EvmEnv` — it only needs `&TxEnv` now, because the `SpecId` already comes from `self.spec_id`. Moves towards aligning with alloy-evm's BlockExecutor ownership model where `EvmEnv` is block-scoped and `TxEnv` is tx-scoped. * feat(cheatcodes): make `Cheatcodes` context-generic (#13767) * feat(cheatcodes): make `Cheatcodes` context-generic * fix: merge conflict --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): add `--network` flag to `cast tx` for network-specific raw encoding (#13745) * feat(cast): add --network flag to `cast tx` for multi-network raw encoding Replace the `FoundryNetwork`-based `transaction_raw` workaround with proper network selection via a new `--network` / `-n` CLI flag. This moves raw tx encoding back into `Cast::transaction` dispatching to the correct provider (Ethereum, Optimism, or Tempo) based on the flag. - Add `NetworkVariant` enum (Ethereum/Optimism/Tempo) in foundry-cli opts - use it w/ `--network` through CastSubcommand::Tx and provider construction - Add test for Tempo raw tx encoding (`tx_raw_tempo`) * fix: network num args * feat(cast): `block --raw` network selection (#13754) * rebase PR 13745 * feat(cast): `block` for multi-network raw encoding Co-authored-by: figtracer <1gusredo@gmail.com> * fix: doctest --------- Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): make mined_receipts generic (#13761) * chore(evm): split `Executor::env` into `evm_env` and `tx_env` fields (#13773) Split the getters/setters accordingly, and update all call sites. * refactor(cheatcodes): `CheatcodesExecutor` generic (#13774) refactor(cheatcodes): make `CheatcodesExecutor` use generic env types from `ContextTr` Replace concrete `EvmEnv`/`TxEnv` in `with_fresh_nested_evm` with `EvmEnv<::Spec, CTX::Block>` and `CTX::Tx` to support non-Eth networks. Add `FoundryContextTr` trait as fully generic counterpart to the concrete `FoundryContextExt`. Remove unused `clone_to_cfg_env`/`apply_cfg_env` from `FoundryCfg`. * fix(anvil): flaky `test_trace_filter()` (#13764) fix * chore(cast): granular bounds on `Cast` (#13776) * refactor(evm): `FoundryContextExt` generic types (#13778) * fix(cheatcodes): create file in writeJson/writeToml 3-arg overload (#13777) Closes foundry-rs/foundry#13775 Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): make EthApi generic over `N: Network` (#13751) * refactor(anvil): make EthApi generic over N: Network * rm todo comments --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): move `CheatsCtxExt` trait to `foundry-evm-core` (#13781) Previously `CheatsCtxExt` was defined in `foundry-cheatcodes`. Move it to `foundry-evm-core`, and rename it to `EthCheatCtx` to make clear it pins the context to Ethereum-specific env types (`BlockEnv`/`TxEnv`/`CfgEnv`) as a temporary alias during the transition to fully generic EVM and cheatcodes. * refactor(evm): make `NestedEvm` trait generic with associated types (#13782) Add `Tx`, `Block`, and `Spec` associated types to `NestedEvm` so each network can specify its own environment types. The trait remains object-safe when used as `dyn NestedEvm`. Update `NestedEvmClosure` to pin the associated types to Eth types, and set the concrete types in the `FoundryEvm` implementation. * refactor(anvil): propagate `EthApi` to all holders (#13783) refactor(anvil): propagate EthApi to all holders * refactor(evm): rename `NestedEvmClosure` and move to `foundry-evm-core` (#13785) refactor(evm): rename `NestedEvmClosure` to `EthNestedEvmClosure` and move to `foundry-evm-core` * refactor(evm): remove `Env` abstraction from `Executor` impl (#13790) * refactor(anvil): remove redundant param (#13792) * refactor(cheatcodes): tighten verbose bounds to `EthCheatCtx` (#13791) * refactor(evm): remove `eth_*_mut()` from `FoundryContextExt` (#13789) * feat(script): generic `TransactionWithMetadata` + generic pprinting `TransactionMaybeSigned` (#13795) * refactor(evm): `DatabaseExt` generic over env types (#13797) refactor(evm): make DatabaseExt generic over environment types Add generic parameters with defaults to DatabaseExt: `DatabaseExt` This makes the trait generic over EVM environment types while remaining fully backwards-compatible — all existing code uses the defaults. Non-Ethereum networks (e.g. Tempo) can now implement DatabaseExt with their own environment types. 9 method signatures updated: snapshot_state, revert_state, create_select_fork, create_select_fork_at_transaction, select_fork, roll_fork, roll_fork_to_transaction, transact, transact_from_tx. * test(cast): mark flaky revert_reason_from and wildcard RPC-dependent tail (#13796) * fix(anvil): reject invalid versioned_hashes in beacon blobs endpoint (#13787) * fix(cheatcodes): prevent panic in expectRevert with empty bytes (#13769) * fix(cheatcodes): prevent panic in expectRevert with empty bytes When vm.expectRevert(bytes('')) catches a revert with non-empty data, decode_error in alloy-dyn-abi panics on the empty expected_reason (slice index out of range). Guard the decode_error call with a length check. Closes #13766 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test: add regression test for expectRevert empty bytes panic Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test: fix snapshot for expectRevert empty bytes regression test Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): add `DB` associated type to `FoundryJournalExt` (#13799) * refactor(evm): make DatabaseExt generic over environment types Add generic parameters with defaults to DatabaseExt: `DatabaseExt` This makes the trait generic over EVM environment types while remaining fully backwards-compatible — all existing code uses the defaults. Non-Ethereum networks (e.g. Tempo) can now implement DatabaseExt with their own environment types. 9 method signatures updated: snapshot_state, revert_state, create_select_fork, create_select_fork_at_transaction, select_fork, roll_fork, roll_fork_to_transaction, transact, transact_from_tx. * refactor(evm): replace dyn DatabaseExt in FoundryJournalExt with associated type Replace `&mut dyn DatabaseExt` return type in `FoundryJournalExt` with an associated type `type DB: DatabaseExt`. This removes 3 uses of `dyn DatabaseExt` while remaining backwards-compatible — `&mut DB` auto-coerces to `&mut dyn DatabaseExt` at call sites that still need it. `FoundryJournalExt` is never used as a trait object itself, so adding the associated type has no object-safety impact. --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(anvil): add `AnvilBlockExecutor` and `FoundryReceiptBuilder` (#13788) feat(anvil): add AnvilBlockExecutor and FoundryReceiptBuilder * fix(anvil): swap param order in get_next_block_blob_excess_gas to match callers (#13740) * feat(script): `Network`-generic `ScriptSequence` (#13803) * fix(config): add symmetric serialization for FuzzDictionaryConfig usize fields (#13723) * chore(evm): remove `Env::new_with_spec_id()` method (#13806) * fix(install): clean up nested submodules when using --no-git (#13779) * fix(install): clean up nested submodules when using --no-git Fixes #13688 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019cf6ab-6839-70a9-98a9-289974db717b * test: mark regression test as flaky_ instead of ignored Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * style: fix fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019cf6ab-6839-70a9-98a9-289974db717b --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): use associated types in `with_cloned_context` (#13802) refactor(evm): use associated types in `with_cloned_context` closure signature * refactor(evm): propagate env types through `FoundryJournalExt` (#13808) * refactor(evm): simplify `FoundryCfg` to marker trait (#13810) * feat(anvil): add `AnvilBlockExecutorFactory` (#13811) * feat(script): `Network`-generic `ScriptSequenceKind` (#13809) * feature(evm): owned `Tx`/`Evm` getters and `Evm` setter for `FoundryContextExt` (#13812) * chore(evm): remove `Env::{clone_evm_and_tx,apply_evm_and_tx}` methods (#13813) * chore(evm): rename `InspectorExt` to `EthInspectorExt` (#13815) * refactor(evm): add `Fork::backend()` accessor (#13817) * refactor(evm): remove `Env` from `commit_transaction` and `replay_until` (#13816) * feat(script): generic `BundledState` impl (#13825) * chore: bump alloy chains (#13827) * chore(evm): remove `Env` abstraction (#13826) * chore(evm): remove `Env` abstraction completely * fix: nit Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(macros): use correct index for tuple struct fields in ConsoleFmt (#13829) * chore(evm): fix stale `Env` references in doc comments (#13828) The combined `Env` wrapper struct was removed in #13826. Update remaining doc comments that still reference it to use the current `EvmEnv`/`TxEnv` names instead. * refactor(anvil): wire `AnvilBlockExecutorFactory` into `Backend::mine_block` (#13814) refactor(anvil): wire AnvilBlockExecutorFactory into Backend::mine_block * feat(script): generic `ScriptTransactionBuilder` (#13830) Remove hardcoded `Ethereum` default from `TransactionWithMetadata`, making `ScriptTransactionBuilder`, `simulate_and_fill`, and broadcast reader calls `Network`-generic. Cheatcode broadcast helpers now explicitly use Ethereum. * fix(`foundryup`): bump foundryup version (#13832) bump foundryup version * fix(foundryup): tempo-foundry now ships all binaries (#13834) nit * chore(deps): bump taiki-e/install-action from 2.68.17 to 2.68.35 (#13821) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump oven-sh/setup-bun from 2.1.2 to 2.2.0 (#13819) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump Swatinem/rust-cache from 2.8.2 to 2.9.1 (#13818) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.5 to 1.44.0 (#13820) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(evm): backend env helpers generic (#13836) Update `update_env_block` and `update_current_env_with_fork_env` to use generic `FoundryBlock`/`FoundryTransaction` bounds and `BlockHeader` trait methods via setters, replacing direct field access on `AnyRpcBlock`. * refactor(anvil): rm `TransactionExecutor`, mv revm callers to `AnvilBlockExecutorFactory` (#13835) refactor(anvil): rm , mv remai callers AnvilBlockExecutorFactory * refactor(script): extract `BrowserSigner` from `MultiWallet` (#13839) * refactor(anvil): mv `Backend` methods to generic impl, thread N through NodeConfig/spawn (#13840) * refactor(anvil): extract `block_env_from_header` utility (#13838) * chore(evm): clean-up `FoundryEvm` impl (#13844) * feat(cheatcodes): add `currentFilePath` cheatcode (#13735) * feat(cheatcodes): add `currentFilePath` cheatcode Add `vm.currentFilePath()` that returns the source file path of the currently running test or script contract, relative to the project root. This enables contracts to locate sibling files (calldata JSONs, markdown descriptions, configs) without hardcoding paths. A common pattern in proposal/script repos is overriding a `dirPath()` function with a hardcoded string — this cheatcode eliminates that boilerplate. Implementation leverages `CheatsConfig::running_artifact` which already tracks the entry-point `ArtifactId`. The `source` field is stripped of the project root prefix to return a consistent relative path. * fix: rustfmt Amp-Thread-ID: https://ampcode.com/threads/T-019cfb9f-8fce-717d-b9de-fedd8ee7d555 Co-authored-by: Amp * fix: remove view from test functions, fix forge-fmt Amp-Thread-ID: https://ampcode.com/threads/T-019cfba7-4986-77c6-9630-574261e9d580 Co-authored-by: Amp --------- Co-authored-by: Alex Netto Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(evm): simplify nested Evm handling (#13846) refactor(evm): simplify nested EVM handling - Replace `to_env()` (returning cfg+block+tx tuple) with `to_evm_env()` returning only `EvmEnv` (cfg+block), since tx is not needed by callers - Remove unused `with_cloned_context` generic type parameter `R` - Drop `set_tx` call in `with_cloned_context` since nested tx modifications should not propagate back to the outer context - Remove `Evm` useless `db_mut`, `precompiles`, `precompiles_mut`, `inspector`, `inspector_mut` provideds methods reimplementations * refactor(cheatcodes): extract fork env helper to reduce duplication (#13848) Extract `fork_env_op` helper that handles the common pattern shared by all fork-switching cheatcodes: clone EVM/tx env → run db operation → write env back. Deduplicates 7 call sites (rollFork × 4, selectFork, createSelectFork × 2). * refactor(cheatcodes): `BroadcastableTransaction` network-agnostic (#13849) refactor(cheatcodes): make BroadcastableTransaction network-agnostic Remove the `Network` type parameter from `BroadcastableTransaction` by storing raw EVM data (from, to, value, input, nonce, gas) as primitive fields instead of wrapping `TransactionMaybeSigned`. This prevents the `Network` generic from leaking through `Cheatcodes`, `RawCallResult`, and `ScriptResult`. The conversion to network-specific types (`TransactionMaybeSigned`) now happens in the script layer (`simulate.rs`) at the point where transactions are handed off to `ScriptTransactionBuilder`. * feat(evm): generic `NestedEvmClosure` (#13850) refactor(evm): generalize NestedEvmClosure with generic type params Replace `EthNestedEvmClosure` (pinned to Eth types) with generic `NestedEvmClosure` to support non-Eth networks. * feat(evm): `FoundryContextExt` generic impl (#13857) * feat(evm): wire Inspector and DatabaseExt Context generics (#13856) * refactor(anvil): make `PendingTransaction` generic over tx type (#13854) * chore(cheatcodes): remove `Cheatcodes` context generic (#13861) * refactor(evm): delegate to alloy's `EthEvmFactory` in `new_evm_with_inspector` (#13860) * chore: add `id` attributes to issue templates (#13864) * chore: add `id` attributes to issue templates Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0b22-cf39-75b7-b3d7-9280780eecd5 * chore: add `id` attributes to required issue template fields only Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0b22-cf39-75b7-b3d7-9280780eecd5 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * refactor(evm): merge `FoundryJournalExt` into `FoundryContextExt` (#13863) * refactor(evm): merge `FoundryJournalExt` into `FoundryContextExt` * fix: failed merge * fix(traces): fix verbosity trace mode and unify verbosity handling (#13859) * fix(traces): use max instead of min for verbosity trace mode Add regression tests for TraceMode::with_verbosity to ensure verbosity 5 raises the trace mode to at least Steps rather than lowering it. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: James Niken <155266991+dizer-ti@users.noreply.github.com> * fix(traces): unify verbosity handling in TraceMode with_verbosity now raises to RecordStateDiff at verbosity 5, matching the documented behavior of -vvvvv (storage changes + backtraces). This removes the separate with_state_changes(verbosity() > 4) call in trace_mode() which used the global shell verbosity instead of the local evm_opts.verbosity — these two values can diverge when --gas-report or --flamegraph bumps evm_opts.verbosity to 3. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(forge): only show backtraces at verbosity 5 Backtraces require opcode-level step recording which is expensive. Gate backtrace display at verbosity >= 5 (-vvvvv) instead of >= 3, matching the documented behavior and the step recording threshold. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): comprehensive unit tests for TraceMode verbosity levels Tests every verbosity level (0-5) × every TraceMode variant: - verbosity 0-2: noop across all modes - verbosity 3-4: raises to Call, never downgrades - verbosity 5: raises to RecordStateDiff, never downgrades - into_config correctness at each level (record_steps, record_state_diff) - monotonicity invariant: with_verbosity never lowers any mode Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): add integration test for backtrace verbosity levels Runs the same failing test at verbosity 1, 3, 4, and 5 with snapshot assertions to verify backtraces only appear at -vvvvv. Removes the monotonicity unit test in favor of concrete integration coverage. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(traces): keep backtraces at verbosity >= 3, add source locations at 5 Backtraces are useful even without source locations — they show contract/function names at -vvv/-vvvv. Source file locations are only added at -vvvvv when step recording is enabled. Updates integration test to assert all three behaviors: - -vvv: backtrace with function names only - -vvvv: same, plus setup traces - -vvvvv: backtrace with source file:line:col locations Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: fix rustfmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): handle compiler warnings in snapshot Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): add [staticcall] to snapshot for pure function Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * perf(traces): RecordStateDiff should not enable debug-level recording RecordStateDiff now behaves as Steps + state diff, not Debug + state diff. This avoids recording full stack snapshots (memcpy per opcode), memory snapshots, and unfiltered opcode recording at -vvvvv. Before: 50k opcodes → 50k step records with full stack copies (~16MB) After: 50k opcodes → ~500 step records (JUMP/JUMPDEST only), no copies Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): assert Debug mode config is unchanged Locks in that Debug mode still enables full memory/stack snapshots, returndata, immediate bytes, and unfiltered opcode recording — ensuring the RecordStateDiff optimization doesn't affect the debugger or cheatcode recording paths. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(traces): state diff needs unfiltered opcodes for SLOAD/SSTORE record_state_diff captures storage changes in the step() callback, which only fires for recorded opcodes. With a JUMP/JUMPDEST filter, SSTORE steps are skipped and state diffs are lost. RecordStateDiff now disables the opcode filter (like before) but still skips memory/stack snapshots — saving the expensive per-opcode memcpy. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): update JSON fixture for RecordStateDiff config Stack and memory snapshots are no longer recorded at -vvvvv since RecordStateDiff now uses Steps-level config. These fields are null in the JSON output instead of populated. Full debugger-level data is still available via --debug. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> * refactor(anvil): make Block generic over tx type (#13865) * refactor(evm): `FoundryContextExt` bound, use generic `Spec` in `EthCheatCtx` (#13866) * refactor(evm): use `TxEnv` directly in `DatabaseExt` instead of `TransactionRequest` (#13867) * chore(deps): weekly `cargo update` (#13878) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * Update flake.lock (#13877) Co-authored-by: github-actions[bot] * fix(deps): bump to Foundry browser wallet version 0.2.0 (#13890) bump to https://github.com/foundry-rs/foundry-browser-wallet/releases/tag/v0.2.0 * revert: "BroadcastableTransaction network-agnostic" (#13849) (#13891) * perf(anvil): remove redundant clone in create_access_list (#13887) * perf(evm): make `NestedEvm::to_evm_env` consuming to avoid useless clone (#13893) Change `to_evm_env(&self)` to `to_evm_env(self)` so the implementation can use `Evm::finish()` to destructure the EVM without an extra clone of `cfg_env` and `block_env`. Update `with_fresh_nested_evm` to return `EvmEnv` after consuming the EVM post-closure, so callers no longer need to capture the env inside a `NestedEvmClosure` (which only has `&mut dyn NestedEvm` and cannot call a consuming method). Fix the `sub_inner` / `sub_evm_env` ordering in `with_nested_evm` impls so the journal is cloned before `to_evm_env` consumes the EVM. * chore(common): consistency fix on `TransactionMaybeSigned::to()` (#13895) * feat(cheatcodes): Network/Evm generic `Cheatcodes` (#13894) * feat(cheatcodes): Network/Evm generic `Cheatcodes` * fix: tx create detection * fix(ci): adapt to `TransactionMaybeSigned::to()` return type change (#13896) * chore(cheatcodes): remove unused `cheats` param from `CheatcodesExecutor::console_log` (#13897) * refactor(evm): remove `tx_env` param from EVM constructor helper (#13903) Remove the `tx_env` parameter from `new_eth_evm_with_inspector`, `with_cloned_context`, and `with_fresh_nested_evm`. Instead of setting the transaction environment at EVM construction time (where it may become stale or be set redundantly), callers now pass the tx env directly to `evm.transact()` at execution time. This avoids some clones. * chore: remove dead code `paths_config` and `log_status` from inspector stack (#13905) chore: remove dead code in InspectorStackInner Remove two unused methods: - `InspectorStack::paths_config()`: zero callers across the codebase - `InspectorStackInner::log_status()`: zero callers across the codebase Also removes the now-unused `foundry_compilers::ProjectPathsConfig` import. * chore: remove no-op `spec_id` reassignment in `Executor::clone_with_backend` (#13906) chore: remove no-op spec_id reassignment in clone_with_backend After cloning `self.evm_env`, `evm_env.cfg_env.spec` already equals `self.spec_id()` (which simply returns `self.evm_env.cfg_env.spec`). The reassignment is a no-op. * feat(evm): `Backend` generic network (#13579) * feat(evm): `Backend` generic over `Network` * fix(evm): use `AnyNetwork` for fork env init to support non-standard chains When forking non-standard EVM chains (e.g. Moonbeam, Arbitrum), their block headers may lack standard Ethereum fields like `mixHash`. Using the generic `N`-typed provider (defaulting to `Ethereum`) caused deserialization failures for these chains. * feat(evm): add `TryAnyIntoTxEnv` trait for `AnyTxEnvelope` to `TxEnv` conversion Introduces TryAnyIntoTxEnv trait in foundry-evm-core to replace the TxEnv: FromRecoveredTx bound on Backend. This allows Backend to default to AnyNetwork: - TxEnvelope (Ethereum): delegates to FromRecoveredTx directly - AnyTxEnvelope: extracts inner TxEnvelope via as_envelope(), then delegates to FromRecoveredTx; returns error for unknown tx types Co-authored-by: Amp * chore: fix `cargo deny` failure --------- Co-authored-by: Amp * refactor(evm): `NestedEvm` based on revm's `Evm` instead of alloy-evm (#13908) * Add feature: `forge inspect linearization` to see a Solidity contract's linearized inheritance (#13704) * feat: Tempo wallet access key support for cast (#13909) * refactor(script-sequence): rename misleading `opcode` field to `call_kind` (#13907) * fix(fmt): swap valid_before/valid_after in TempoTransaction pretty print (#13910) * fix(fmt): prefer header total_difficulty for totalDifficulty (#13919) * feat(evm): `FoundryContextExt` impls for OP (#13925) * refactor(evm): simplify `NestedEvm` trait by removing `Block` and `Spec` (#13933) * refactor(evm): make `FoundryInspectorExt` generic over `CTX` and rename traits (#13922) * refactor(evm): make `FoundryInspectorExt` generic over `CTX` and rename traits Restructure the inspector extension traits for clarity and genericity: - Rename FoundryInspectorExt -> InspectorExt: the context-free base trait providing Foundry-specific inspector methods (console logging, network config) without any Inspector supertrait. - Rename EthInspectorExt -> FoundryInspectorExt: the combined trait that unifies Inspector + InspectorExt. Generic over any CTX that implements FoundryContextExt, rather than being hardcoded to specific BLOCK/TX/SPEC type parameters with for<'a> HRTB baked in. - Introduce EthEvmCtx<'db> type alias for the concrete Eth context (Context), keeping usage sites concise. This makes the trait hierarchy composable for multi-network support while keeping the Eth-specific default path ergonomic via the type alias during refactor. Co-authored-by: Amp * fix: use `EthEvmContext` instead of EthEvmCtx as default --------- Co-authored-by: Amp * fix(evm): restore `code_size_limit` config in `CfgEnv` (#13912) `cfg_env()` unconditionally set `limit_contract_code_size = Some(usize::MAX)`, ignoring the user's `code_size_limit` setting from foundry.toml. Use the configured value when present, falling back to `usize::MAX` (disabled). * perf(evm): remove unnecessary clones in do_call_end/do_create_end (#13913) perf(evm): remove unnecessary clones in `do_call_end`/`do_create_end` Both methods returned `CallOutcome`/`CreateOutcome` but all callers discarded the return value — the outcome is already mutated in-place via `&mut`. This removes the final `outcome.clone()` and changes the early-return in the `#[ret]` macro from `then_some(outcome.clone())` to `then(|| ())`, eliminating up to 2 clones per call/create end. * fix(wallets): browser wallet CLI help heading formatting (#13876) fix: browser wallet CLI help heading formatting - Set proper help_heading on BrowserWalletOpts to use 'Wallet options - browser wallet' consistently - Add next_help_heading to Erc20TxOpts to prevent transaction options from leaking into the browser wallet section Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(forge-script): Tempo access key support for forge script (#13917) * refactor(anvil): encapsulate per-tx inspector lifecycle in `finish_transaction` (#13932) * refactor(anvil): encapsulate per-tx inspector lifecycle in `finish_transaction` Extract the repeated ~20-line drain-and-reset block from 3 locations in the block mining loop into `AnvilInspector::finish_transaction()`. The method prints traces/logs, drains the tracer into `Vec`, and reinstalls fresh tracer + log collector for the next transaction. Introduces `InspectorTxConfig` to carry the print/tracing settings, replacing direct field access into the inspector internals. * style: fix rustfmt * refactor(evm): generalize `TryAnyToTxEnv` trait over TxEnv + simplification (#13924) * refactor(evm): generalize `TryAnyToTxEnv` trait over TxEnv + simplification The old trait converted `TxEnvelope` (the consensus-layer type) into `TxEnv`, requiring callers to separately pass the sender address. This was a leaky abstraction: the sender is already carried by the RPC transaction wrapper (TransactionResponse::from()), so callers had to extract it themselves before calling `try_into_tx_env`. The new trait `TryAnyToTxEnv` is generic over the output TxEnv type and takes the full RPC transaction as receiver, which means: - Implementations for `alloy_rpc_types::Transaction` and `AnyRpcTransaction` can call ToTxEnv / FromRecoveredTx internally without leaking address handling to callers. - A new impl for `op_alloy_rpc_types::Transaction` produces `OpTransaction`, enabling OP-stack fork replay without a separate code path. - `Backend` now constrains `N::TransactionResponse: TryAnyToTxEnv` instead of `N::TxEnvelope: TryAnyIntoTxEnv`, which is both more accurate and unlocks multi-network support. * fix: docs --------- Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(evm): simplify `replay_until` to use single `ForkDB` clone (#13931) * refactor: simplify replay_until to use single ForkDB clone Replace the per-transaction Backend + EVM creation in replay_until with a single cloned ForkDB and one persistent EthEvm instance. Before: for each tx in the block, commit_transaction would clone Fork + JournaledState, spawn a new Backend (including MultiFork), create a new FoundryEvm, transact, then apply_state_changeset. In a block with N preceding transactions, this meant N full clones. After: clone the fork's CacheDB once (SharedBackend is Arc-backed, so only the local cache layer is duplicated), create one EthEvm via EthEvmFactory, call transact_commit for each tx, then replace the fork's DB and refresh journaled states once at the end. Also: - Remove unused tx_env parameter from replay_until - Extract Fork::refresh_journaled_states helper to deduplicate the paired update_state calls in both replay_until and apply_state_changeset - Remove unused NoOpInspector import Amp-Thread-ID: https://ampcode.com/threads/T-019d2512-8074-72ac-92d8-e8f887911219 Co-authored-by: Amp * style: fix rustfmt * refactor: simplify tx collection loop and remove stale comments --------- Co-authored-by: Amp Co-authored-by: zerosnacks * fix(evm): use try_any_to_tx_env in replay_until (#13938) The merge of #13931 introduced a call to the old `try_into_tx_env` method which was renamed to `try_any_to_tx_env` in #13924. This fixes the docs CI build. Amp-Thread-ID: https://ampcode.com/threads/T-019d2952-9ec2-76f9-8f90-b7b3735d4ce3 Co-authored-by: Amp * chore(deps): bump taiki-e/install-action from 2.68.35 to 2.69.8 (#13915) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.68.35 to 2.69.8. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/94a7388bec5d4c8dd93e3ebf09e0ff448f3f6f4d...7bc99eee1f1b8902a125006cf790a1f4c8461e63) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.69.8 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.17.0 to 3.17.1 (#13914) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/131015bad844610e5e6300f8a143bf625d3e74f4...a18f73c54ca8525de051e73c31512a67f44df919) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.17.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(evm): simplify `FoundryContextExt` spec handling (#13935) - Introduced `FoundryContextExt::Spec` type for convenience - Removed complex bounds on `ContextTr::Cfg` using instead the new `Spec` type - Simplified accordingly the rest of the code Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(anvil): remove some intermediate `Env` wrappers, pass `EvmEnv` directly (#13941) refactor(anvil): remove some intermediate Env wrapper, pass EvmEnv directly Replace usage of the `Env` struct wrapper with direct `EvmEnv` references in `new_eth_evm_with_inspector`, `validate_pool_transaction_for`, and `validate_for`. The optimism flag is now passed explicitly as `is_optimism: bool` instead of being extracted from `env.networks`. This eliminates unnecessary `Env::new(...)` construction in several hot paths (block mining, pending block simulation, tx replay) and simplifies the function signatures. * refactor(anvil): remove Env from storage, call env, and executor APIs (#13942) Continue the cleanup of the Env wrapper by passing EvmEnv directly in remaining call sites: - BlockchainStorage::new / Blockchain::new: drop the redundant spec_id parameter, derive it from evm_env.spec_id() instead - build_call_env: return (EvmEnv, OpTransaction) instead of Env, consumers destructure and use directly - new_eth_evm_with_inspector_ref: accept &EvmEnv instead of &Env, derive is_optimism from self.is_optimism() - build_tx_env_for_pending: take is_optimism: bool instead of NetworkConfigs + &EvmEnv - inspect_tx / replay_block_transactions_with_inspector: extract only evm_env from next_env(), avoid cloning the whole Env - api.rs: use spec_id() / chain_id() helpers instead of digging into env.evm_env.cfg_env fields directly - Various minor cleanups: inline single-use env write guards, use self.chain_id() / self.spec_id() helpers consistently --------- Signed-off-by: dependabot[bot] Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Matthias Seitz <19890894+mattsse@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: Nikki Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Edgar Richards Co-authored-by: Red Swan Co-authored-by: onbjerg Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/BUG-FORM.yml | 2 + .github/ISSUE_TEMPLATE/FEATURE-FORM.yml | 2 + .github/workflows/ci.yml | 12 +- .github/workflows/docs.yml | 2 +- .github/workflows/nix.yml | 4 +- .github/workflows/npm.yml | 4 +- .github/workflows/test-flaky.yml | 4 +- .github/workflows/test-isolate.yml | 4 +- .github/workflows/test.yml | 6 +- Cargo.lock | 697 +- Cargo.toml | 2 + crates/anvil/core/src/eth/block.rs | 20 +- crates/anvil/core/src/eth/transaction/mod.rs | 10 +- crates/anvil/src/cmd.rs | 11 +- crates/anvil/src/config.rs | 28 +- crates/anvil/src/eth/api.rs | 172 +- crates/anvil/src/eth/backend/db.rs | 99 +- crates/anvil/src/eth/backend/executor.rs | 793 +- crates/anvil/src/eth/backend/fork.rs | 4 +- crates/anvil/src/eth/backend/info.rs | 13 +- crates/anvil/src/eth/backend/mem/inspector.rs | 44 +- crates/anvil/src/eth/backend/mem/mod.rs | 2592 +++--- crates/anvil/src/eth/backend/mem/storage.rs | 34 +- crates/anvil/src/eth/backend/validate.rs | 13 +- crates/anvil/src/eth/fees.rs | 6 +- crates/anvil/src/eth/miner.rs | 3 +- crates/anvil/src/eth/otterscan/api.rs | 3 +- crates/anvil/src/eth/pool/mod.rs | 11 +- crates/anvil/src/eth/pool/transactions.rs | 16 +- crates/anvil/src/filter.rs | 111 +- crates/anvil/src/lib.rs | 15 +- crates/anvil/src/pubsub.rs | 50 +- crates/anvil/src/server/beacon/handlers.rs | 38 +- crates/anvil/src/server/beacon/mod.rs | 3 +- crates/anvil/src/server/mod.rs | 11 +- crates/anvil/src/server/rpc_handlers.rs | 11 +- crates/anvil/src/service.rs | 60 +- crates/anvil/src/tasks/mod.rs | 5 +- crates/anvil/tests/it/fork.rs | 7 +- crates/anvil/tests/it/proof.rs | 5 +- crates/anvil/tests/it/traces.rs | 6 +- crates/cast/Cargo.toml | 1 + crates/cast/src/args.rs | 84 +- crates/cast/src/cmd/access_list.rs | 12 +- crates/cast/src/cmd/call.rs | 24 +- crates/cast/src/cmd/erc20.rs | 225 +- crates/cast/src/cmd/run.rs | 78 +- crates/cast/src/cmd/send.rs | 97 +- crates/cast/src/lib.rs | 533 +- crates/cast/src/opts.rs | 10 +- crates/cast/src/rlp_converter.rs | 31 +- crates/cast/src/tempo.rs | 21 + crates/cast/src/tx.rs | 6 +- crates/cast/tests/cli/main.rs | 50 +- crates/cheatcodes/assets/cheatcodes.json | 20 + crates/cheatcodes/spec/src/vm.rs | 5 + crates/cheatcodes/src/crypto.rs | 2 +- crates/cheatcodes/src/evm.rs | 172 +- crates/cheatcodes/src/evm/fork.rs | 136 +- crates/cheatcodes/src/fs.rs | 52 +- crates/cheatcodes/src/inspector.rs | 183 +- crates/cheatcodes/src/json.rs | 12 +- crates/cheatcodes/src/lib.rs | 6 +- crates/cheatcodes/src/script.rs | 11 +- crates/cheatcodes/src/test.rs | 18 +- crates/cheatcodes/src/test/assert.rs | 8 +- crates/cheatcodes/src/test/revert_handlers.rs | 3 +- crates/cheatcodes/src/toml.rs | 14 +- crates/cheatcodes/src/utils.rs | 4 +- crates/chisel/src/executor.rs | 6 +- crates/cli/src/opts/mod.rs | 2 + crates/cli/src/opts/network.rs | 11 + crates/cli/src/utils/cmd.rs | 2 +- crates/common/Cargo.toml | 1 + crates/common/fmt/src/ui.rs | 27 +- crates/common/src/transactions.rs | 67 +- crates/config/src/fuzz.rs | 15 +- crates/config/src/utils.rs | 15 + crates/evm/core/Cargo.toml | 3 + crates/evm/core/src/backend/cow.rs | 82 +- crates/evm/core/src/backend/mod.rs | 475 +- crates/evm/core/src/env.rs | 445 +- crates/evm/core/src/evm.rs | 256 +- crates/evm/core/src/lib.rs | 26 +- crates/evm/core/src/opts.rs | 37 +- crates/evm/core/src/utils.rs | 25 +- crates/evm/evm/Cargo.toml | 1 + crates/evm/evm/src/executors/builder.rs | 21 +- crates/evm/evm/src/executors/invariant/mod.rs | 6 +- .../evm/evm/src/executors/invariant/shrink.rs | 6 +- crates/evm/evm/src/executors/mod.rs | 235 +- crates/evm/evm/src/executors/trace.rs | 24 +- crates/evm/evm/src/inspectors/logs.rs | 5 +- crates/evm/evm/src/inspectors/stack.rs | 162 +- crates/evm/evm/src/lib.rs | 4 +- crates/evm/traces/src/lib.rs | 128 +- crates/forge/src/cmd/inspect.rs | 151 +- crates/forge/src/cmd/install.rs | 45 +- crates/forge/src/cmd/test/mod.rs | 12 +- crates/forge/src/multi_runner.rs | 18 +- crates/forge/tests/cli/backtrace.rs | 138 + crates/forge/tests/cli/cmd.rs | 93 + crates/forge/tests/cli/install.rs | 43 + crates/forge/tests/cli/script.rs | 10 +- crates/forge/tests/cli/test_cmd/repros.rs | 30 + .../fixtures/SimpleContractTestVerbose.json | 6995 +++-------------- crates/macros/src/console_fmt.rs | 5 +- crates/primitives/src/network/transaction.rs | 11 + crates/primitives/src/transaction/envelope.rs | 56 +- crates/primitives/src/transaction/request.rs | 4 +- crates/script-sequence/Cargo.toml | 1 - crates/script-sequence/src/reader.rs | 54 +- crates/script-sequence/src/sequence.rs | 71 +- crates/script-sequence/src/transaction.rs | 29 +- crates/script/Cargo.toml | 1 + crates/script/src/broadcast.rs | 148 +- crates/script/src/build.rs | 89 +- crates/script/src/execute.rs | 19 +- crates/script/src/lib.rs | 28 +- crates/script/src/multi_sequence.rs | 25 +- crates/script/src/progress.rs | 24 +- crates/script/src/receipts.rs | 47 +- crates/script/src/runner.rs | 17 +- crates/script/src/sequence.rs | 57 +- crates/script/src/simulate.rs | 35 +- crates/script/src/transaction.rs | 26 +- crates/script/src/verify.rs | 33 +- crates/verify/src/bytecode.rs | 60 +- crates/verify/src/utils.rs | 51 +- crates/wallets/Cargo.toml | 7 + crates/wallets/src/lib.rs | 2 + crates/wallets/src/opts.rs | 41 +- crates/wallets/src/tempo.rs | 196 + .../src/wallet_browser/app/assets/main.js | 114 +- .../src/wallet_browser/app/assets/styles.css | 2 +- crates/wallets/src/wallet_browser/opts.rs | 4 +- crates/wallets/src/wallet_browser/signer.rs | 4 +- crates/wallets/src/wallet_multi/mod.rs | 45 +- deny.toml | 1 + flake.lock | 18 +- foundryup/foundryup | 4 +- testdata/default/cheats/CurrentFilePath.t.sol | 21 + testdata/default/cheats/Json.t.sol | 15 + testdata/utils/Vm.sol | 1 + 144 files changed, 7882 insertions(+), 9857 deletions(-) create mode 100644 crates/cast/src/tempo.rs create mode 100644 crates/cli/src/opts/network.rs create mode 100644 crates/wallets/src/tempo.rs create mode 100644 testdata/default/cheats/CurrentFilePath.t.sol diff --git a/.github/ISSUE_TEMPLATE/BUG-FORM.yml b/.github/ISSUE_TEMPLATE/BUG-FORM.yml index bebfe0343ee69..281cf2fd254ff 100644 --- a/.github/ISSUE_TEMPLATE/BUG-FORM.yml +++ b/.github/ISSUE_TEMPLATE/BUG-FORM.yml @@ -10,6 +10,7 @@ body: Thanks for taking the time to report this bug! - type: dropdown + id: component attributes: label: Component description: What component is the bug in? @@ -52,6 +53,7 @@ body: - macOS (Apple Silicon) - Linux - type: textarea + id: description attributes: label: Describe the bug description: Please include relevant Solidity snippets as well if relevant. diff --git a/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml b/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml index a07529633cc94..4268a73baa8b6 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE-FORM.yml @@ -10,6 +10,7 @@ body: Thanks for helping us improve Foundry! - type: dropdown + id: component attributes: label: Component description: What component is the feature for? @@ -24,6 +25,7 @@ body: validations: required: true - type: textarea + id: description attributes: label: Describe the feature you would like description: Please also describe what the feature is aiming to solve, if relevant. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b12bb1b3242d4..a2ef7e83d79db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - run: cargo test --workspace --doc typos: @@ -59,7 +59,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: crate-ci/typos@57b11c6b7e54c402ccd9cda953f1072ec4f78e33 # v1 + - uses: crate-ci/typos@631208b7aac2daa8b707f55e7331f9112b0e062d # v1 shellcheck: runs-on: depot-ubuntu-latest @@ -89,7 +89,7 @@ jobs: components: clippy - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - run: cargo clippy --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings @@ -123,7 +123,7 @@ jobs: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: forge fmt shell: bash run: ./.github/scripts/format.sh --check @@ -141,11 +141,11 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 + - uses: taiki-e/install-action@7bc99eee1f1b8902a125006cf790a1f4c8461e63 # v2 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - run: cargo hack check deny: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2ace7a6c6fcf1..baa407fd5573e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,7 +32,7 @@ jobs: toolchain: nightly - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: Build documentation run: cargo doc --workspace --all-features --no-deps --document-private-items env: diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index ed4bbd1246799..9b71107ca4c68 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,7 +19,7 @@ jobs: contents: write pull-requests: write steps: - - uses: DeterminateSystems/determinate-nix-action@131015bad844610e5e6300f8a143bf625d3e74f4 # v3 + - uses: DeterminateSystems/determinate-nix-action@a18f73c54ca8525de051e73c31512a67f44df919 # v3 - uses: actions/checkout@v6 with: persist-credentials: false @@ -38,7 +38,7 @@ jobs: permissions: contents: read steps: - - uses: DeterminateSystems/determinate-nix-action@131015bad844610e5e6300f8a143bf625d3e74f4 # v3 + - uses: DeterminateSystems/determinate-nix-action@a18f73c54ca8525de051e73c31512a67f44df919 # v3 - uses: actions/checkout@v6 with: persist-credentials: false diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index fad46ce27bf27..766beae8cc8d7 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -137,7 +137,7 @@ jobs: run-id: ${{ github.event.workflow_run.id || inputs.run_id }} - name: Setup Bun - uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: bun-version: latest @@ -259,7 +259,7 @@ jobs: persist-credentials: false - name: Setup Bun - uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: bun-version: latest diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index c64b3ef1d963e..6f992dc80c4ea 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -33,11 +33,11 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 + - uses: taiki-e/install-action@7bc99eee1f1b8902a125006cf790a1f4c8461e63 # v2 with: tool: nextest - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: Test flaky tests env: SVM_TARGET_PLATFORM: linux-amd64 diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index a4aaefdbfe7df..d2526945e929a 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -37,11 +37,11 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 + - uses: taiki-e/install-action@7bc99eee1f1b8902a125006cf790a1f4c8461e63 # v2 with: tool: nextest - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: Test flaky tests with isolation env: SVM_TARGET_PLATFORM: linux-amd64 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2f9c5793ab138..b99ff97bda688 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@edba51d32f66935a112c0516fec65a13d420cbc6 # v2 + - uses: taiki-e/install-action@7bc99eee1f1b8902a125006cf790a1f4c8461e63 # v2 with: tool: nextest @@ -78,7 +78,7 @@ jobs: node-version: 24 - name: Install Bun if: contains(matrix.name, 'external') - uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: bun-version: latest - name: Setup Python @@ -102,7 +102,7 @@ jobs: restore-keys: | ${{ runner.os }}-foundry-${{ matrix.name }}- - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: Setup Git config run: | git config --global user.name "GitHub Actions Bot" diff --git a/Cargo.lock b/Cargo.lock index 7e41f8ccbadbc..0cac3a43e8812 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.30" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f374d3c6d729268bbe2d0e0ff992bb97898b2df756691a62ee1d5f0506bc39" +checksum = "9247f0a399ef71aeb68f497b2b8fb348014f742b50d3b83b1e00dfe1b7d64b3d" dependencies = [ "alloy-primitives", "num_enum", @@ -155,7 +155,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -298,7 +298,7 @@ dependencies = [ "derive_more", "op-alloy", "op-revm", - "revm", + "revm 34.0.0", "thiserror 2.0.18", "tracing", ] @@ -406,7 +406,7 @@ dependencies = [ "auto_impl", "op-alloy", "op-revm", - "revm", + "revm 34.0.0", "thiserror 2.0.18", ] @@ -887,7 +887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -978,13 +978,12 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d7fd448ab0a017de542de1dcca7a58e7019fe0e7a34ed3f9543ebddf6aceffa" +checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" dependencies = [ "alloy-primitives", "alloy-rlp", - "arrayvec", "derive_more", "nybbles", "serde", @@ -1041,7 +1040,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", - "anstyle-parse", + "anstyle-parse 0.2.7", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse 1.0.0", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -1051,15 +1065,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-lossy" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d3a5dc826f84d0ea11882bb8054ff7f3d482602e11bb181101303a279ea01f" +checksum = "d9ca7d0f520afcd6d817970d0b2d5fd7c630c75e7783cae046b8b8a783c5befa" dependencies = [ "anstyle", ] @@ -1073,13 +1087,22 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + [[package]] name = "anstyle-query" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1090,7 +1113,7 @@ checksum = "e22d9f3dea8bbda97c75bd0f0203e23f1e190d6d6f27a40e10063946dc4d4362" dependencies = [ "anstyle", "anstyle-lossy", - "anstyle-parse", + "anstyle-parse 0.2.7", "html-escape", "unicode-width 0.2.2", ] @@ -1103,7 +1126,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1162,7 +1185,7 @@ dependencies = [ "rand 0.8.5", "rand 0.9.2", "reqwest 0.13.2", - "revm", + "revm 34.0.0", "revm-inspectors", "serde", "serde_json", @@ -1171,7 +1194,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "yansi", ] @@ -1193,7 +1216,7 @@ dependencies = [ "foundry-evm", "foundry-primitives", "rand 0.9.2", - "revm", + "revm 34.0.0", "serde", "serde_json", "thiserror 2.0.18", @@ -1544,9 +1567,6 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] [[package]] name = "ascii-canvas" @@ -1714,9 +1734,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.16.1" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" dependencies = [ "aws-lc-sys", "zeroize", @@ -1724,9 +1744,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" dependencies = [ "cc", "cmake", @@ -1761,9 +1781,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.103.0" +version = "1.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6bfd8dfb5a562f9a605bbd5c3aef09db9231c826a1e0488ce3f4338c70dbbb" +checksum = "c41ae6a33da941457e89075ef8ca5b4870c8009fe4dceeba82fce2f30f313ac6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1785,9 +1805,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.96.0" +version = "1.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64a6eded248c6b453966e915d32aeddb48ea63ad17932682774eb026fbef5b1" +checksum = "9aadc669e184501caaa6beafb28c6267fc1baef0810fb58f9b205485ca3f2567" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1809,9 +1829,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.98.0" +version = "1.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db96d720d3c622fcbe08bae1c4b04a72ce6257d8b0584cb5418da00ae20a344f" +checksum = "1342a7db8f358d3de0aed2007a0b54e875458e39848d54cc1d46700b2bfcb0a8" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1833,9 +1853,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.100.0" +version = "1.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fafbdda43b93f57f699c5dfe8328db590b967b8a820a13ccdd6687355dfcc7ca" +checksum = "ab41ad64e4051ecabeea802d6a17845a91e83287e1dd249e6963ea1ba78c428a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -2006,9 +2026,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b1117b3b2bbe166d11199b540ceed0d0f7676e36e7b962b5a437a9971eac75" +checksum = "9d73dbfbaa8e4bc57b9045137680b958d274823509a360abfd8e1d514d40c95c" dependencies = [ "base64-simd", "bytes", @@ -2407,9 +2427,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d13a61f2963b88eef9c1be03df65d42f6996dfeac1054870d950fcf66686f83" +checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" dependencies = [ "bon-macros", "rustversion", @@ -2417,11 +2437,11 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.9.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d314cc62af2b6b0c65780555abb4d02a03dd3b799cd42419044f0c38d99738c0" +checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.23.0", + "darling 0.20.11", "ident_case", "prettyplease", "proc-macro2", @@ -2432,19 +2452,20 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ "borsh-derive", + "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" dependencies = [ "once_cell", "proc-macro-crate", @@ -2539,9 +2560,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.6" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0f582957c24870b7bfd12bf562c40b4734b533cafbaf8ded31d6d85f462c01" +checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" dependencies = [ "blst", "cc", @@ -2632,11 +2653,12 @@ dependencies = [ "futures", "itertools 0.14.0", "op-alloy-flz", + "op-alloy-network", "rand 0.8.5", "rand 0.9.2", "rayon", "regex", - "revm", + "revm 34.0.0", "rpassword", "semver 1.0.27", "serde", @@ -2659,9 +2681,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.56" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", @@ -2717,7 +2739,7 @@ dependencies = [ "tempfile", "time", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "walkdir", "yansi", ] @@ -2775,9 +2797,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -2795,11 +2817,11 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "anstream", + "anstream 1.0.0", "anstyle", "clap_lex", "strsim", @@ -2810,18 +2832,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.66" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c757a3b7e39161a4e56f9365141ada2a6c915a8622c408ab6bb4b5d047371031" +checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb" dependencies = [ "clap", ] [[package]] name = "clap_complete_nushell" -version = "4.5.10" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685bc86fd34b7467e0532a4f8435ab107960d69a243785ef0275e571b35b641a" +checksum = "fbb9e9715d29a754b468591be588f6b926f5b0a1eb6a8b62acabeb66ff84d897" dependencies = [ "clap", "clap_complete", @@ -2829,9 +2851,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", @@ -2841,21 +2863,21 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clearscreen" -version = "4.0.5" +version = "4.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5def4343d62f01f67ff1a49147e4a15112e936c6a6a3f8ff7a29394e76468244" +checksum = "d669bb552908e336ad5681789752033b45566b7e591aeaac7a614e58e5d6d8f2" dependencies = [ "nix 0.31.2", "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2992,9 +3014,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "colored" @@ -3002,7 +3024,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3087,13 +3109,12 @@ dependencies = [ [[package]] name = "console" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "once_cell", "unicode-width 0.2.2", "windows-sys 0.61.2", ] @@ -3358,16 +3379,6 @@ dependencies = [ "darling_macro 0.20.11", ] -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - [[package]] name = "darling" version = "0.23.0" @@ -3392,20 +3403,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - [[package]] name = "darling_core" version = "0.23.0" @@ -3431,17 +3428,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.117", -] - [[package]] name = "darling_macro" version = "0.23.0" @@ -3508,9 +3494,9 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", @@ -3588,7 +3574,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" dependencies = [ - "console 0.16.2", + "console 0.16.3", "shell-words", "zeroize", ] @@ -3638,7 +3624,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3666,9 +3652,9 @@ dependencies = [ [[package]] name = "doctest-file" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" [[package]] name = "document-features" @@ -3892,7 +3878,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "env_filter", "jiff", @@ -3943,7 +3929,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4037,9 +4023,9 @@ dependencies = [ [[package]] name = "evmole" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc83a05bd5d83b886d1aa5b725649d42172532c6f6243da9cd4a3c25fbda20f6" +checksum = "61e332670cb19161dee3f15ae86bfb5e4361bb1716d7c1995bd309b5360cb630" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -4098,7 +4084,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4263,7 +4249,7 @@ dependencies = [ "rayon", "regex", "reqwest 0.13.2", - "revm", + "revm 34.0.0", "semver 1.0.27", "serde", "serde_json", @@ -4379,6 +4365,7 @@ dependencies = [ "serde", "serde_json", "tempfile", + "tempo-alloy", "thiserror 2.0.18", "tokio", "tracing", @@ -4391,7 +4378,6 @@ version = "1.6.0" dependencies = [ "alloy-network", "alloy-primitives", - "alloy-rpc-types-eth", "eyre", "foundry-common", "foundry-compilers", @@ -4443,7 +4429,7 @@ dependencies = [ "itertools 0.14.0", "regex", "reqwest 0.13.2", - "revm", + "revm 34.0.0", "semver 1.0.27", "serde", "serde_json", @@ -4540,7 +4526,7 @@ dependencies = [ "parking_lot", "proptest", "rand 0.9.2", - "revm", + "revm 34.0.0", "revm-inspectors", "semver 1.0.27", "serde", @@ -4610,7 +4596,7 @@ dependencies = [ "tikv-jemallocator", "tokio", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "tracing-tracy", "yansi", ] @@ -4644,7 +4630,7 @@ dependencies = [ "alloy-transport-http", "alloy-transport-ipc", "alloy-transport-ws", - "anstream", + "anstream 0.6.21", "anstyle", "axum", "chrono", @@ -4658,13 +4644,14 @@ dependencies = [ "foundry-common-fmt", "foundry-compilers", "foundry-config", + "foundry-primitives", "itertools 0.14.0", "jiff", "num-format", "path-slash", "regex", "reqwest 0.13.2", - "revm", + "revm 34.0.0", "semver 1.0.27", "serde", "serde_json", @@ -4695,7 +4682,7 @@ dependencies = [ "foundry-primitives", "op-alloy-consensus", "op-alloy-rpc-types", - "revm", + "revm 34.0.0", "serde", "serde_json", "similar-asserts", @@ -4731,7 +4718,7 @@ dependencies = [ "tempfile", "thiserror 2.0.18", "tracing", - "winnow", + "winnow 0.7.15", "yansi", ] @@ -4826,7 +4813,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "revm", + "revm 34.0.0", "semver 1.0.27", "serde", "serde_json", @@ -4856,7 +4843,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-traces", "ratatui", - "revm", + "revm 34.0.0", "revm-inspectors", "serde", "tracing", @@ -4869,6 +4856,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-evm", "alloy-json-abi", + "alloy-network", "alloy-primitives", "alloy-rpc-types", "alloy-sol-types", @@ -4886,7 +4874,7 @@ dependencies = [ "parking_lot", "proptest", "rayon", - "revm", + "revm 34.0.0", "revm-inspectors", "serde", "serde_json", @@ -4925,6 +4913,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", + "alloy-serde 2.0.0-rc.0", "alloy-sol-types", "auto_impl", "eyre", @@ -4937,9 +4926,11 @@ dependencies = [ "foundry-test-utils", "futures", "itertools 0.14.0", + "op-alloy-consensus", + "op-alloy-rpc-types", "op-revm", "parking_lot", - "revm", + "revm 34.0.0", "revm-inspectors", "serde", "serde_json", @@ -4959,7 +4950,7 @@ dependencies = [ "foundry-compilers", "foundry-evm-core", "rayon", - "revm", + "revm 34.0.0", "semver 1.0.27", "solar-compiler", "tracing", @@ -4983,7 +4974,7 @@ dependencies = [ "parking_lot", "proptest", "rand 0.9.2", - "revm", + "revm 34.0.0", "serde", "solar-compiler", "thiserror 2.0.18", @@ -5000,7 +4991,7 @@ dependencies = [ "alloy-op-hardforks", "alloy-primitives", "clap", - "revm", + "revm 34.0.0", "serde", ] @@ -5025,7 +5016,7 @@ dependencies = [ "memchr", "rayon", "reqwest 0.13.2", - "revm", + "revm 34.0.0", "revm-inspectors", "serde", "serde_json", @@ -5050,7 +5041,7 @@ dependencies = [ "eyre", "futures", "parking_lot", - "revm", + "revm 34.0.0", "serde", "serde_json", "thiserror 2.0.18", @@ -5098,7 +5089,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-rpc-types", "op-revm", - "revm", + "revm 34.0.0", "serde", "serde_json", "tempo-alloy", @@ -5143,7 +5134,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "ui_test", ] @@ -5153,8 +5144,10 @@ version = "1.6.0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", + "alloy-eips 2.0.0-rc.0", "alloy-network", "alloy-primitives", + "alloy-rlp", "alloy-signer", "alloy-signer-aws", "alloy-signer-gcp", @@ -5168,6 +5161,7 @@ dependencies = [ "axum", "clap", "derive_builder", + "dirs", "eth-keystore", "eyre", "foundry-common", @@ -5176,8 +5170,11 @@ dependencies = [ "rpassword", "serde", "serde_json", + "tempo-alloy", + "tempo-primitives", "thiserror 2.0.18", "tokio", + "toml", "tower", "tower-http", "tracing", @@ -5785,7 +5782,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -6027,7 +6024,7 @@ version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" dependencies = [ - "console 0.16.2", + "console 0.16.3", "portable-atomic", "unicode-width 0.2.2", "unit-prefix", @@ -6090,9 +6087,9 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" dependencies = [ "darling 0.23.0", "indoc", @@ -6162,7 +6159,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6209,9 +6206,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" @@ -6255,9 +6252,9 @@ dependencies = [ [[package]] name = "jni" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295dc9997acda1562fdf8d299f56063c936443b60f078e63a5d8d3c34ef2642b" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ "cfg-if", "combine", @@ -6272,9 +6269,9 @@ dependencies = [ [[package]] name = "jni-macros" -version = "0.22.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c3d1da60c95c98847b26b9d45f4360fee718b31de746df016d9cd6de916a7ef" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" dependencies = [ "proc-macro2", "quote", @@ -6371,9 +6368,9 @@ dependencies = [ [[package]] name = "kasuari" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" dependencies = [ "hashbrown 0.16.1", "portable-atomic", @@ -6566,7 +6563,7 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -7027,7 +7024,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7148,9 +7145,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -7158,9 +7155,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7227,9 +7224,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" dependencies = [ "critical-section", "portable-atomic", @@ -7363,7 +7360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40" dependencies = [ "auto_impl", - "revm", + "revm 34.0.0", "serde", ] @@ -7778,9 +7775,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" dependencies = [ "portable-atomic", ] @@ -7897,7 +7894,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.4+spec-1.1.0", + "toml_edit 0.25.5+spec-1.1.0", ] [[package]] @@ -8025,7 +8022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.117", @@ -8038,7 +8035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.117", @@ -8093,9 +8090,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" +checksum = "14104c5a24d9bcf7eb2c24753e0f49fe14555d8bd565ea3d38e4b4303267259d" dependencies = [ "bitflags 2.11.0", "memchr", @@ -8161,7 +8158,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.3", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -8199,9 +8196,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -8644,9 +8641,9 @@ dependencies = [ "once_cell", "op-alloy-consensus", "reth-codecs", - "revm-bytecode", + "revm-bytecode 8.0.0", "revm-primitives", - "revm-state", + "revm-state 9.0.0", "secp256k1 0.30.0", "serde", "thiserror 2.0.18", @@ -8666,17 +8663,36 @@ version = "34.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" dependencies = [ - "revm-bytecode", - "revm-context", - "revm-context-interface", - "revm-database", - "revm-database-interface", - "revm-handler", - "revm-inspector", - "revm-interpreter", + "revm-bytecode 8.0.0", + "revm-context 13.0.0", + "revm-context-interface 14.0.0", + "revm-database 10.0.0", + "revm-database-interface 9.0.0", + "revm-handler 15.0.0", + "revm-inspector 15.0.0", + "revm-interpreter 32.0.0", "revm-precompile", "revm-primitives", - "revm-state", + "revm-state 9.0.0", +] + +[[package]] +name = "revm" +version = "36.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0abc15d09cd211e9e73410ada10134069c794d4bcdb787dfc16a1bf0939849c" +dependencies = [ + "revm-bytecode 9.0.0", + "revm-context 15.0.0", + "revm-context-interface 16.0.0", + "revm-database 12.0.0", + "revm-database-interface 10.0.0", + "revm-handler 17.0.0", + "revm-inspector 17.0.0", + "revm-interpreter 34.0.0", + "revm-precompile", + "revm-primitives", + "revm-state 10.0.0", ] [[package]] @@ -8691,6 +8707,18 @@ dependencies = [ "serde", ] +[[package]] +name = "revm-bytecode" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86e468df3cf5cf59fa7ef71a3e9ccabb76bb336401ea2c0674f563104cf3c5e" +dependencies = [ + "bitvec", + "phf 0.13.1", + "revm-primitives", + "serde", +] + [[package]] name = "revm-context" version = "13.0.0" @@ -8700,11 +8728,28 @@ dependencies = [ "bitvec", "cfg-if", "derive-where", - "revm-bytecode", - "revm-context-interface", - "revm-database-interface", + "revm-bytecode 8.0.0", + "revm-context-interface 14.0.0", + "revm-database-interface 9.0.0", "revm-primitives", - "revm-state", + "revm-state 9.0.0", + "serde", +] + +[[package]] +name = "revm-context" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1f0a76b14d684a444fc52f7bf6b7564bf882599d91ee62e76d602e7a187c7" +dependencies = [ + "bitvec", + "cfg-if", + "derive-where", + "revm-bytecode 9.0.0", + "revm-context-interface 16.0.0", + "revm-database-interface 10.0.0", + "revm-primitives", + "revm-state 10.0.0", "serde", ] @@ -8718,9 +8763,25 @@ dependencies = [ "alloy-eip7702", "auto_impl", "either", - "revm-database-interface", + "revm-database-interface 9.0.0", "revm-primitives", - "revm-state", + "revm-state 9.0.0", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc256b27743e2912ca16899568e6652a372eb5d1d573e6edb16c7836b16cf487" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface 10.0.0", + "revm-primitives", + "revm-state 10.0.0", "serde", ] @@ -8731,10 +8792,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" dependencies = [ "alloy-eips 1.7.3", - "revm-bytecode", - "revm-database-interface", + "revm-bytecode 8.0.0", + "revm-database-interface 9.0.0", "revm-primitives", - "revm-state", + "revm-state 9.0.0", + "serde", +] + +[[package]] +name = "revm-database" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0a7d6da41061f2c50f99a2632571026b23684b5449ff319914151f4449b6c8" +dependencies = [ + "alloy-eips 1.7.3", + "revm-bytecode 9.0.0", + "revm-database-interface 10.0.0", + "revm-primitives", + "revm-state 10.0.0", "serde", ] @@ -8747,7 +8822,21 @@ dependencies = [ "auto_impl", "either", "revm-primitives", - "revm-state", + "revm-state 9.0.0", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "revm-database-interface" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd497a38a79057b94a049552cb1f925ad15078bc1a479c132aeeebd1d2ccc768" +dependencies = [ + "auto_impl", + "either", + "revm-primitives", + "revm-state 10.0.0", "serde", "thiserror 2.0.18", ] @@ -8760,14 +8849,33 @@ checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2" dependencies = [ "auto_impl", "derive-where", - "revm-bytecode", - "revm-context", - "revm-context-interface", - "revm-database-interface", - "revm-interpreter", + "revm-bytecode 8.0.0", + "revm-context 13.0.0", + "revm-context-interface 14.0.0", + "revm-database-interface 9.0.0", + "revm-interpreter 32.0.0", "revm-precompile", "revm-primitives", - "revm-state", + "revm-state 9.0.0", + "serde", +] + +[[package]] +name = "revm-handler" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1eed729ca9b228ae98688f352235871e9b8be3d568d488e4070f64c56e9d3d" +dependencies = [ + "auto_impl", + "derive-where", + "revm-bytecode 9.0.0", + "revm-context 15.0.0", + "revm-context-interface 16.0.0", + "revm-database-interface 10.0.0", + "revm-interpreter 34.0.0", + "revm-precompile", + "revm-primitives", + "revm-state 10.0.0", "serde", ] @@ -8779,16 +8887,33 @@ checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" dependencies = [ "auto_impl", "either", - "revm-context", - "revm-database-interface", - "revm-handler", - "revm-interpreter", + "revm-context 13.0.0", + "revm-database-interface 9.0.0", + "revm-handler 15.0.0", + "revm-interpreter 32.0.0", "revm-primitives", - "revm-state", + "revm-state 9.0.0", "serde", "serde_json", ] +[[package]] +name = "revm-inspector" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf5102391706513689f91cb3cb3d97b5f13a02e8647e6e9cb7620877ef84847" +dependencies = [ + "auto_impl", + "either", + "revm-context 15.0.0", + "revm-database-interface 10.0.0", + "revm-handler 17.0.0", + "revm-interpreter 34.0.0", + "revm-primitives", + "revm-state 10.0.0", + "serde", +] + [[package]] name = "revm-inspectors" version = "0.36.0" @@ -8802,7 +8927,7 @@ dependencies = [ "boa_engine", "boa_gc", "colorchoice", - "revm", + "revm 34.0.0", "serde", "serde_json", "thiserror 2.0.18", @@ -8814,10 +8939,23 @@ version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" dependencies = [ - "revm-bytecode", - "revm-context-interface", + "revm-bytecode 8.0.0", + "revm-context-interface 14.0.0", "revm-primitives", - "revm-state", + "revm-state 9.0.0", + "serde", +] + +[[package]] +name = "revm-interpreter" +version = "34.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf22f80612bb8f58fd1f578750281f2afadb6c93835b14ae6a4d6b75ca26f445" +dependencies = [ + "revm-bytecode 9.0.0", + "revm-context-interface 16.0.0", + "revm-primitives", + "revm-state 10.0.0", "serde", ] @@ -8865,7 +9003,20 @@ checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" dependencies = [ "alloy-eip7928", "bitflags 2.11.0", - "revm-bytecode", + "revm-bytecode 8.0.0", + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-state" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29404707763da607e5d6e4771cb203998c28159279c2f64cc32de08d2814651" +dependencies = [ + "alloy-eip7928", + "bitflags 2.11.0", + "revm-bytecode 9.0.0", "revm-primitives", "serde", ] @@ -9070,7 +9221,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9129,7 +9280,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9140,9 +9291,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "aws-lc-rs", "ring", @@ -9231,9 +9382,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -9538,9 +9689,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.17.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64 0.22.1", "chrono", @@ -9557,11 +9708,11 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.17.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.117", @@ -9796,7 +9947,7 @@ version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c1abc378119f77310836665f8523018532cf7e3faeb3b10b01da5a7321bf8e1" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "anstyle-svg", "normalize-line-endings", @@ -9813,7 +9964,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b750c344002d7cc69afb9da00ebd9b5c0f8ac2eb7d115d9d45d5b5f47718d74" dependencies = [ - "anstream", + "anstream 0.6.21", ] [[package]] @@ -9833,7 +9984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -9896,12 +10047,12 @@ version = "0.1.8" source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "annotate-snippets 0.12.13", - "anstream", + "anstream 0.6.21", "anstyle", "derive_more", "dunce", "inturn", - "itertools 0.14.0", + "itertools 0.12.1", "itoa", "normalize-path", "once_map", @@ -9913,7 +10064,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.18", + "thiserror 1.0.69", "tracing", "unicode-width 0.2.2", ] @@ -9936,7 +10087,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.0", "bumpalo", - "itertools 0.14.0", + "itertools 0.12.1", "memchr", "num-bigint", "num-rational", @@ -10240,7 +10391,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "tempfile", - "thiserror 2.0.18", + "thiserror 1.0.69", "url", "zip", ] @@ -10346,21 +10497,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] name = "tempo-alloy" -version = "1.3.0" -source = "git+https://github.com/tempoxyz/tempo?branch=alloy-2.0#f609d775e8e1fe6a1c0282d97b567ac60b93b7cb" +version = "1.4.2" +source = "git+https://github.com/tempoxyz/tempo?branch=alloy-2.0#b364dab518df5e0ac7859faa24d52e591e5b48fa" dependencies = [ "alloy-consensus", "alloy-contract", @@ -10372,7 +10523,10 @@ dependencies = [ "alloy-serde 2.0.0-rc.0", "alloy-signer-local", "alloy-transport", + "async-trait", + "dashmap", "derive_more", + "futures", "serde", "tempo-contracts", "tempo-primitives", @@ -10381,18 +10535,19 @@ dependencies = [ [[package]] name = "tempo-contracts" -version = "1.3.0" -source = "git+https://github.com/tempoxyz/tempo?branch=alloy-2.0#f609d775e8e1fe6a1c0282d97b567ac60b93b7cb" +version = "1.4.2" +source = "git+https://github.com/tempoxyz/tempo?branch=alloy-2.0#b364dab518df5e0ac7859faa24d52e591e5b48fa" dependencies = [ "alloy-contract", "alloy-primitives", "alloy-sol-types", + "serde", ] [[package]] name = "tempo-primitives" -version = "1.3.0" -source = "git+https://github.com/tempoxyz/tempo?branch=alloy-2.0#f609d775e8e1fe6a1c0282d97b567ac60b93b7cb" +version = "1.4.2" +source = "git+https://github.com/tempoxyz/tempo?branch=alloy-2.0#b364dab518df5e0ac7859faa24d52e591e5b48fa" dependencies = [ "alloy-consensus", "alloy-eips 2.0.0-rc.0", @@ -10406,10 +10561,11 @@ dependencies = [ "reth-codecs", "reth-ethereum-primitives", "reth-primitives-traits", - "revm", + "revm 36.0.0", "serde", "serde_json", "sha2 0.10.9", + "tempo-contracts", ] [[package]] @@ -10429,7 +10585,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -10602,9 +10758,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -10706,7 +10862,7 @@ dependencies = [ "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -10720,9 +10876,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.0+spec-1.1.0" +version = "1.0.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" dependencies = [ "serde_core", ] @@ -10739,7 +10895,7 @@ dependencies = [ "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -10752,35 +10908,35 @@ dependencies = [ "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_edit" -version = "0.25.4+spec-1.1.0" +version = "0.25.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1" dependencies = [ "indexmap 2.13.0", - "toml_datetime 1.0.0+spec-1.1.0", + "toml_datetime 1.0.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.0.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" dependencies = [ - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" [[package]] name = "tonic" @@ -10931,7 +11087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -10956,9 +11112,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -10979,7 +11135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba" dependencies = [ "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "tracy-client", ] @@ -11605,7 +11761,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -11682,7 +11838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe985f41e291eecef5e5c0770a18d28390addb03331c043964d9e916453d6f16" dependencies = [ "core-foundation 0.10.1", - "jni 0.22.3", + "jni 0.22.4", "log", "ndk-context", "objc2", @@ -11755,7 +11911,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -12125,6 +12281,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -12305,18 +12470,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.41" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e13bc581734df6250836c59a5f44f3c57db9f9acb9dc8e3eaabdaf6170254d" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.41" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3545ea9e86d12ab9bba9fcd99b54c1556fd3199007def5a03c375623d05fac1c" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index a515ea6807c20..8ac9de957302a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -335,6 +335,7 @@ alloy-trie = "0.9" ## op-alloy op-alloy-consensus = "0.24.0" +op-alloy-network = "0.24.0" op-alloy-rpc-types = "0.24.0" op-alloy-flz = "0.13.1" @@ -501,6 +502,7 @@ alloy-evm = { git = "https://github.com/alloy-rs/evm.git", branch = "alloy-2.0" ## op-alloy / alloy-op-evm op-alloy-consensus = { git = "https://github.com/foundry-rs/optimism", branch = "alloy-2.0" } +op-alloy-network = { git = "https://github.com/foundry-rs/optimism", branch = "alloy-2.0" } op-alloy-rpc-types = { git = "https://github.com/foundry-rs/optimism", branch = "alloy-2.0" } op-alloy = { git = "https://github.com/foundry-rs/optimism", branch = "alloy-2.0" } alloy-op-evm = { git = "https://github.com/foundry-rs/optimism", branch = "alloy-2.0" } diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 23c21b1b647d6..12f35ef94eaad 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -4,13 +4,14 @@ use alloy_consensus::{ }; use alloy_eips::eip2718::Encodable2718; use alloy_network::Network; -use foundry_primitives::FoundryNetwork; +use foundry_primitives::{FoundryNetwork, FoundryTxEnvelope}; use std::fmt::Debug; use crate::eth::transaction::MaybeImpersonatedTransaction; -/// Type alias for Ethereum Block with Anvil's transaction type -pub type Block = alloy_consensus::Block; +/// Type alias for Ethereum Block with Anvil's transaction type, generic over the transaction +/// envelope with a default of [`FoundryTxEnvelope`]. +pub type Block = alloy_consensus::Block>; /// Anvil's concrete block info type. pub type BlockInfo = TypedBlockInfo; @@ -23,13 +24,18 @@ pub struct TypedBlockInfo { pub receipts: Vec, } -/// Helper function to create a new block with Header and Anvil transactions +/// Helper function to create a new block with Header and Anvil transactions, generic over the +/// transaction envelope with a default of [`FoundryTxEnvelope`]. /// /// Note: if the `impersonate-tx` feature is enabled this will also accept /// `MaybeImpersonatedTransaction`. -pub fn create_block(mut header: Header, transactions: impl IntoIterator) -> Block +pub fn create_block( + mut header: Header, + transactions: impl IntoIterator, +) -> Block where - T: Into, + Tx: Encodable2718, + T: Into>, { let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect(); let transactions_root = calculate_transaction_root(&transactions); @@ -211,7 +217,7 @@ mod tests { let data = hex::decode("f9034df90348a0fbdbd8d2d0ac5f14bd5fa90e547fe6f1d15019c724f8e7b60972d381cd5d9cf8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c9577e7945db22e38fc060909f2278c7746b0f9ba05017cfa3b0247e35197215ae8d610265ffebc8edca8ea66d6567eb0adecda867a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018355bb7b871fffffffffffff808462bd0e1ab9014bf90148a00000000000000000000000000000000000000000000000000000000000000000f85494319fa8f1bc4e53410e92d10d918659b16540e60a945a573efb304d04c1224cd012313e827eca5dce5d94a9c831c5a268031176ebf5f3de5051e8cba0dbfe94c9577e7945db22e38fc060909f2278c7746b0f9b808400000000f8c9b841a6946f2d16f68338cbcbd8b117374ab421128ce422467088456bceba9d70c34106128e6d4564659cf6776c08a4186063c0a05f7cffd695c10cf26a6f301b67f800b8412b782100c18c35102dc0a37ece1a152544f04ad7dc1868d18a9570f744ace60870f822f53d35e89a2ea9709ccbf1f4a25ee5003944faa845d02dde0a41d5704601b841d53caebd6c8a82456e85c2806a9e08381f959a31fb94a77e58f00e38ad97b2e0355b8519ab2122662cbe022f2a4ef7ff16adc0b2d5dcd123181ec79705116db300a063746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365880000000000000000c0c0").unwrap(); - let block = Block::decode(&mut data.as_slice()).unwrap(); + let block = ::decode(&mut data.as_slice()).unwrap(); // encode and check that it matches the original data let mut encoded = Vec::new(); diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 02bcc749bcb82..5a355358f1146 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -20,7 +20,7 @@ use std::ops::Deref; /// This is a helper that carries the `impersonated` sender so that the right hash /// can be created. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct MaybeImpersonatedTransaction { +pub struct MaybeImpersonatedTransaction { transaction: T, impersonated_sender: Option
, } @@ -93,13 +93,13 @@ impl Encodable for MaybeImpersonatedTransaction { } } -impl From for FoundryTxEnvelope { - fn from(value: MaybeImpersonatedTransaction) -> Self { +impl From> for FoundryTxEnvelope { + fn from(value: MaybeImpersonatedTransaction) -> Self { value.transaction } } -impl From for MaybeImpersonatedTransaction { +impl From for MaybeImpersonatedTransaction { fn from(value: FoundryTxEnvelope) -> Self { Self::new(value) } @@ -127,7 +127,7 @@ impl Deref for MaybeImpersonatedTransaction { /// Queued transaction #[derive(Clone, Debug, PartialEq, Eq)] -pub struct PendingTransaction { +pub struct PendingTransaction { /// The actual transaction pub transaction: MaybeImpersonatedTransaction, /// the recovered sender of this transaction diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 9dac717e1f8a2..636d6f7c0e945 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -13,6 +13,7 @@ use foundry_common::shell; use foundry_config::{Chain, Config, FigmentProviders}; use foundry_evm::hardfork::{EthereumHardfork, OpHardfork}; use foundry_evm_networks::NetworkConfigs; +use foundry_primitives::FoundryNetwork; use futures::FutureExt; use rand_08::{SeedableRng, rngs::StdRng}; use std::{ @@ -639,7 +640,7 @@ impl AnvilEvmArgs { /// Helper type to periodically dump the state of the chain to disk struct PeriodicStateDumper { in_progress_dump: Option + Send + Sync + 'static>>>, - api: EthApi, + api: EthApi, dump_state: Option, preserve_historical_states: bool, interval: Interval, @@ -647,7 +648,7 @@ struct PeriodicStateDumper { impl PeriodicStateDumper { fn new( - api: EthApi, + api: EthApi, dump_state: Option, interval: Duration, preserve_historical_states: bool, @@ -671,7 +672,11 @@ impl PeriodicStateDumper { } /// Infallible state dump - async fn dump_state(api: EthApi, dump_state: PathBuf, preserve_historical_states: bool) { + async fn dump_state( + api: EthApi, + dump_state: PathBuf, + preserve_historical_states: bool, + ) { trace!(path=?dump_state, "Dumping state on shutdown"); match api.serialized_state(preserve_historical_states).await { Ok(state) => { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 56a5e4ddcfa81..b5d6745495f9b 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -42,9 +42,12 @@ use foundry_evm::{ FoundryHardfork, OpHardfork, ethereum_hardfork_from_block_tag, spec_id_from_ethereum_hardfork, }, - utils::{apply_chain_and_block_specific_env_changes, get_blob_base_fee_update_fraction}, + utils::{ + apply_chain_and_block_specific_env_changes, block_env_from_header, + get_blob_base_fee_update_fraction, + }, }; -use foundry_primitives::FoundryNetwork; +use foundry_primitives::FoundryTxEnvelope; use itertools::Itertools; use op_revm::OpTransaction; use parking_lot::RwLock; @@ -1053,7 +1056,13 @@ impl NodeConfig { /// [Backend](mem::Backend) /// /// *Note*: only memory based backend for now - pub(crate) async fn setup(&mut self) -> Result> { + pub(crate) async fn setup(&mut self) -> Result> + where + N: alloy_network::Network< + TxEnvelope = foundry_primitives::FoundryTxEnvelope, + ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope, + >, + { // configure the revm environment let mut cfg = CfgEnv::default(); @@ -1175,10 +1184,6 @@ impl NodeConfig { .wrap_err("failed to create default create2 deployer")?; } - if let Some(state) = self.init_state.clone() { - backend.load_state(state).await.wrap_err("failed to load init state")?; - } - Ok(backend) } @@ -1285,16 +1290,11 @@ latest block number: {latest_block}" self.gas_limit = Some(gas_limit); env.evm_env.block_env = BlockEnv { - number: U256::from(fork_block_number), - 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, // Keep previous `coinbase` and `basefee` value beneficiary: env.evm_env.block_env.beneficiary, basefee: env.evm_env.block_env.basefee, - ..Default::default() + ..block_env_from_header(&block.header) }; // Determine chain_id early so we can use it consistently @@ -1451,7 +1451,7 @@ latest block number: {latest_block}" async fn derive_block_and_transactions( fork_choice: &ForkChoice, provider: &Arc, -) -> eyre::Result<(BlockNumber, Option>)> { +) -> eyre::Result<(BlockNumber, Option>>)> { match fork_choice { ForkChoice::Block(block_number) => { let block_number = *block_number; diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 1b298befb35a7..9f12c66e5c7b5 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -39,7 +39,7 @@ use alloy_eips::{ }; use alloy_evm::overrides::{OverrideBlockHashes, apply_state_overrides}; use alloy_network::{ - AnyRpcBlock, AnyRpcTransaction, BlockResponse, ReceiptResponse, TransactionBuilder, + AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, ReceiptResponse, TransactionBuilder, TransactionBuilder4844, TransactionResponse, eip2718::Decodable2718, }; use alloy_primitives::{ @@ -105,16 +105,16 @@ pub const CLIENT_VERSION: &str = concat!("anvil/v", env!("CARGO_PKG_VERSION")); /// The entry point for executing eth api RPC call - The Eth RPC interface. /// /// This type is cheap to clone and can be used concurrently -pub struct EthApi { +pub struct EthApi { /// The transaction pool - pool: Arc>, + pool: Arc>, /// Holds all blockchain related data /// In-Memory only for now - pub backend: Arc>, + pub backend: Arc>, /// Whether this node is mining is_mining: bool, /// available signers - signers: Arc>>>, + signers: Arc>>>, /// data required for `eth_feeHistory` fee_history_cache: FeeHistoryCache, /// max number of items kept in fee cache @@ -123,11 +123,11 @@ pub struct EthApi { /// /// This access is required in order to adjust miner settings based on requests received from /// custom RPC endpoints - miner: Miner, + miner: Miner, /// allows to enabled/disable logging logger: LoggingManager, /// Tracks all active filters - filters: Filters, + filters: Filters, /// How transactions are ordered in the pool transaction_order: Arc>, /// Whether we're listening for RPC calls @@ -136,7 +136,7 @@ pub struct EthApi { instance_id: Arc>, } -impl Clone for EthApi { +impl Clone for EthApi { fn clone(&self) -> Self { Self { pool: self.pool.clone(), @@ -155,20 +155,20 @@ impl Clone for EthApi { } } -// == impl EthApi generic methods == +// == impl EthApi generic methods == -impl EthApi { +impl EthApi { /// Creates a new instance #[expect(clippy::too_many_arguments)] pub fn new( - pool: Arc>, - backend: Arc>, - signers: Arc>>>, + pool: Arc>, + backend: Arc>, + signers: Arc>>>, fee_history_cache: FeeHistoryCache, fee_history_limit: u64, - miner: Miner, + miner: Miner, logger: LoggingManager, - filters: Filters, + filters: Filters, transactions_order: TransactionOrder, ) -> Self { Self { @@ -286,26 +286,6 @@ impl EthApi { 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. - /// - /// Handler for RPC call: `anvil_reset` - pub async fn anvil_reset(&self, forking: Option) -> Result<()> { - self.reset_instance_id(); - node_info!("anvil_reset"); - if let Some(forking) = forking { - // if we're resetting the fork we need to reset the instance id - self.backend.reset_fork(forking).await?; - } else { - // Reset to a fresh in-memory state - self.backend.reset_to_in_mem().await?; - } - // Clear pending transactions since they reference the old chain state. - self.pool.clear(); - Ok(()) - } - pub async fn anvil_set_chain_id(&self, chain_id: u64) -> Result<()> { node_info!("anvil_setChainId"); self.backend.set_chain_id(chain_id); @@ -401,35 +381,6 @@ impl EthApi { Ok(()) } - /// Create a buffer that represents all state on the chain, which can be loaded to separate - /// process by calling `anvil_loadState` - /// - /// Handler for RPC call: `anvil_dumpState` - pub async fn anvil_dump_state( - &self, - preserve_historical_states: Option, - ) -> Result { - node_info!("anvil_dumpState"); - self.backend.dump_state(preserve_historical_states.unwrap_or(false)).await - } - - /// Returns the current state - pub async fn serialized_state( - &self, - preserve_historical_states: bool, - ) -> Result { - self.backend.serialized_state(preserve_historical_states).await - } - - /// Append chain state buffer to current chain. Will overwrite any conflicting addresses or - /// storage. - /// - /// Handler for RPC call: `anvil_loadState` - pub async fn anvil_load_state(&self, buf: Bytes) -> Result { - node_info!("anvil_loadState"); - self.backend.load_state_bytes(buf).await - } - /// Retrieves the Anvil node configuration params. /// /// Handler for RPC call: `anvil_nodeInfo` @@ -439,7 +390,7 @@ impl EthApi { let env = self.backend.env().read(); let fork_config = self.backend.get_fork(); let tx_order = self.transaction_order.read(); - let hard_fork: &str = env.evm_env.cfg_env.spec.into(); + let hard_fork: &str = (*env.evm_env.spec_id()).into(); Ok(NodeInfo { current_block_number: self.backend.best_number(), @@ -643,7 +594,7 @@ impl EthApi { /// Returns the first signer that can sign for the given address #[expect(clippy::borrowed_box)] - pub fn get_signer(&self, address: Address) -> Option<&Box>> { + pub fn get_signer(&self, address: Address) -> Option<&Box>> { self.signers.iter().find(|signer| signer.is_signer_for(address)) } @@ -666,12 +617,64 @@ impl EthApi { pub fn is_impersonated(&self, addr: Address) -> bool { self.backend.cheats().is_impersonated(addr) } + + /// Returns a new accessor for certain storage elements + pub fn storage_info(&self) -> StorageInfo { + StorageInfo::new(Arc::clone(&self.backend)) + } } // == impl EthApi anvil endpoints == -impl EthApi { - // TODO: move to `impl EthApi` once `Backend::block_by_hash` is network-generic. +impl EthApi { + /// Reset the fork to a fresh forked state, and optionally update the fork config. + /// + /// If `forking` is `None` then this will disable forking entirely. + /// + /// Handler for RPC call: `anvil_reset` + pub async fn anvil_reset(&self, forking: Option) -> Result<()> { + self.reset_instance_id(); + node_info!("anvil_reset"); + if let Some(forking) = forking { + // if we're resetting the fork we need to reset the instance id + self.backend.reset_fork(forking).await?; + } else { + // Reset to a fresh in-memory state + self.backend.reset_to_in_mem().await?; + } + // Clear pending transactions since they reference the old chain state. + self.pool.clear(); + Ok(()) + } + + /// Create a buffer that represents all state on the chain, which can be loaded to separate + /// process by calling `anvil_loadState` + /// + /// Handler for RPC call: `anvil_dumpState` + pub async fn anvil_dump_state( + &self, + preserve_historical_states: Option, + ) -> Result { + node_info!("anvil_dumpState"); + self.backend.dump_state(preserve_historical_states.unwrap_or(false)).await + } + + /// Returns the current state + pub async fn serialized_state( + &self, + preserve_historical_states: bool, + ) -> Result { + self.backend.serialized_state(preserve_historical_states).await + } + + /// Append chain state buffer to current chain. Will overwrite any conflicting addresses or + /// storage. + /// + /// Handler for RPC call: `anvil_loadState` + pub async fn anvil_load_state(&self, buf: Bytes) -> Result { + node_info!("anvil_loadState"); + self.backend.load_state_bytes(buf).await + } /// Revert the state of the blockchain to a previous snapshot. /// Takes a single parameter, which is the snapshot id to revert to. @@ -682,7 +685,10 @@ impl EthApi { self.backend.revert_state_snapshot(id).await } - async fn block_request(&self, block_number: Option) -> Result { + async fn block_request( + &self, + block_number: Option, + ) -> Result> { let block_request = match block_number { Some(BlockId::Number(BlockNumber::Pending)) => { let pending_txs = self.pool.ready_transactions().collect(); @@ -870,11 +876,6 @@ impl EthApi { self.backend.new_block_notifications() } - /// Returns a new accessor for certain storage elements - pub fn storage_info(&self) -> StorageInfo { - StorageInfo::new(Arc::clone(&self.backend)) - } - /// Executes the [EthRequest] and returns an RPC [ResponseResult]. pub async fn execute(&self, request: EthRequest) -> ResponseResult { trace!(target: "rpc::api", "executing eth request"); @@ -1972,12 +1973,8 @@ impl EthApi { // execute again but with access list set request.access_list = Some(access_list.clone()); - let (exit, out, gas_used, _) = self.backend.call_with_state( - &state, - request.clone(), - FeeDetails::zero(), - block_env, - )?; + let (exit, out, gas_used, _) = + self.backend.call_with_state(&state, request, FeeDetails::zero(), block_env)?; ensure_return_ok(exit, &out)?; Ok(AccessListResult { @@ -2322,7 +2319,7 @@ impl EthApi { current: EthForkConfig { activation_time: 0, blob_schedule: self.backend.blob_params(), - chain_id: self.backend.env().read().evm_env.cfg_env.chain_id, + chain_id: self.backend.chain_id().to::(), fork_id: Bytes::from_static(&[0; 4]), precompiles: self.backend.precompiles(), system_contracts: self.backend.system_contracts(), @@ -2662,7 +2659,7 @@ impl EthApi { // == impl EthApi anvil endpoints == -impl EthApi { +impl EthApi { /// Send transactions impersonating specific account and contract addresses. /// /// Handler for ETH RPC call: `anvil_impersonateAccount` @@ -2921,7 +2918,8 @@ impl EthApi { // address -> cumulative nonce let mut nonces: HashMap = HashMap::default(); - let mut txs: HashMap>> = HashMap::default(); + let mut txs: HashMap>>> = + HashMap::default(); for pair in pairs { let (tx_data, block_index) = pair; @@ -3087,7 +3085,7 @@ impl EthApi { node_info!("txpool_inspect"); let mut inspect = TxpoolInspect::default(); - fn convert(tx: Arc) -> TxpoolInspectSummary { + fn convert(tx: Arc>) -> TxpoolInspectSummary { let tx = &tx.pending_transaction.transaction; let to = tx.to(); let gas_price = tx.max_fee_per_gas(); @@ -3124,7 +3122,7 @@ impl EthApi { pub async fn txpool_content(&self) -> Result> { node_info!("txpool_content"); let mut content = TxpoolContent::::default(); - fn convert(tx: Arc) -> Result { + fn convert(tx: Arc>) -> Result { let from = *tx.pending_transaction.sender(); let tx = transaction_build( Some(tx.hash()), @@ -3160,7 +3158,7 @@ impl EthApi { } } -impl EthApi { +impl EthApi { /// Executes the `evm_mine` and returns the number of blocks mined async fn do_evm_mine(&self, opts: Option) -> Result { let mut blocks_to_mine = 1u64; @@ -3421,7 +3419,7 @@ impl EthApi { /// Adds the given transaction to the pool fn add_pending_transaction( &self, - pending_transaction: PendingTransaction, + pending_transaction: PendingTransaction, requires: Vec, provides: Vec, ) -> Result { diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 14691862a225e..fdbac6c54a5eb 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -8,6 +8,7 @@ use std::{ use alloy_consensus::{BlockBody, Header}; use alloy_eips::eip4895::Withdrawals; +use alloy_evm::block::StateDB; use alloy_primitives::{ Address, B256, Bytes, U256, keccak256, map::{AddressMap, HashMap}, @@ -90,6 +91,96 @@ pub trait MaybeForkedDatabase { fn maybe_inner(&self) -> Result<&BlockchainDb, String>; } +/// `dyn Db` satisfies all `alloy_evm::Database` requirements via its supertraits, but the +/// blanket impl has an implicit `Sized` bound. Provide an explicit impl. +impl alloy_evm::Database for dyn Db {} + +impl StateDB for dyn Db { + fn set_state_clear_flag(&mut self, _has_state_clear: bool) { + // Anvil does not use the revm State wrapper, so this is a no-op. + } +} + +/// A wrapper around [`CacheDB`] that implements [`StateDB`]. +/// +/// `StateDB` is a foreign trait with an orphan-rule constraint, so we cannot +/// implement it directly for `CacheDB`. This newtype sidesteps the orphan +/// rule while delegating all [`Database`], [`DatabaseCommit`] and +/// [`DatabaseRef`] calls to the inner `CacheDB`. +#[derive(Debug)] +pub struct AnvilCacheDB(pub CacheDB); + +impl> AnvilCacheDB { + pub fn new(inner: T) -> Self { + Self(CacheDB::new(inner)) + } +} + +impl> std::ops::Deref for AnvilCacheDB { + type Target = CacheDB; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl> std::ops::DerefMut for AnvilCacheDB { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl + fmt::Debug> Database for AnvilCacheDB { + type Error = DatabaseError; + + fn basic(&mut self, address: Address) -> Result, Self::Error> { + self.0.basic(address) + } + + fn code_by_hash(&mut self, code_hash: B256) -> Result { + self.0.code_by_hash(code_hash) + } + + fn storage(&mut self, address: Address, index: U256) -> Result { + self.0.storage(address, index) + } + + fn block_hash(&mut self, number: u64) -> Result { + self.0.block_hash(number) + } +} + +impl> DatabaseRef for AnvilCacheDB { + type Error = DatabaseError; + + fn basic_ref(&self, address: Address) -> Result, Self::Error> { + self.0.basic_ref(address) + } + + fn code_by_hash_ref(&self, code_hash: B256) -> Result { + self.0.code_by_hash_ref(code_hash) + } + + fn storage_ref(&self, address: Address, index: U256) -> Result { + self.0.storage_ref(address, index) + } + + fn block_hash_ref(&self, number: u64) -> Result { + self.0.block_hash_ref(number) + } +} + +impl + fmt::Debug> DatabaseCommit for AnvilCacheDB { + fn commit(&mut self, changes: revm::state::EvmState) { + self.0.commit(changes) + } +} + +impl + fmt::Debug> StateDB for AnvilCacheDB { + fn set_state_clear_flag(&mut self, _has_state_clear: bool) { + // Anvil does not use the revm State wrapper, so this is a no-op. + } +} + /// This bundles all required revm traits pub trait Db: DatabaseRef @@ -576,7 +667,7 @@ where #[serde(untagged)] pub enum SerializableTransactionType { TypedTransaction(FoundryTxEnvelope), - MaybeImpersonatedTransaction(MaybeImpersonatedTransaction), + MaybeImpersonatedTransaction(MaybeImpersonatedTransaction), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -608,13 +699,13 @@ impl From for Block { } } -impl From for SerializableTransactionType { - fn from(transaction: MaybeImpersonatedTransaction) -> Self { +impl From> for SerializableTransactionType { + fn from(transaction: MaybeImpersonatedTransaction) -> Self { Self::MaybeImpersonatedTransaction(transaction) } } -impl From for MaybeImpersonatedTransaction { +impl From for MaybeImpersonatedTransaction { fn from(transaction: SerializableTransactionType) -> Self { match transaction { SerializableTransactionType::TypedTransaction(tx) => Self::new(tx), diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 06dc14796cc54..d583933717567 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -1,571 +1,376 @@ -use crate::{ - PrecompileFactory, - eth::{ - backend::{ - cheats::{CheatEcrecover, CheatsManager}, - db::Db, - env::Env, - mem::op_haltreason_to_instruction_result, - validate::TransactionValidator, - }, - error::InvalidTransactionError, - pool::transactions::PoolTransaction, - }, - mem::inspector::AnvilInspector, -}; -use alloy_consensus::{ - Header, Receipt, ReceiptWithBloom, Transaction, constants::EMPTY_WITHDRAWALS, - proofs::calculate_receipt_root, transaction::Either, -}; +use crate::{eth::backend::cheats::CheatsManager, mem::inspector::AnvilInspector}; +use alloy_consensus::{Eip658Value, Transaction, TransactionEnvelope, transaction::Either}; use alloy_eips::{ Encodable2718, eip2935, eip4788, - eip7685::EMPTY_REQUESTS_HASH, eip7702::{RecoveredAuthority, RecoveredAuthorization}, - eip7840::BlobParams, }; use alloy_evm::{ - EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, - eth::EthEvmContext, - precompiles::{DynPrecompile, Precompile, PrecompilesMap}, + EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, FromTxWithEncoded, RecoveredTx, + block::{ + BlockExecutionError, BlockExecutionResult, BlockExecutor, BlockValidationError, + ExecutableTx, OnStateHook, StateChangeSource, StateDB, TxResult, + }, + eth::{ + EthEvmContext, EthTxResult, + receipt_builder::{ReceiptBuilder, ReceiptBuilderCtx}, + }, + precompiles::PrecompilesMap, }; -use alloy_network::Network; use alloy_op_evm::OpEvmFactory; -use alloy_primitives::{B256, Bloom, BloomInput, Bytes, Log}; -use anvil_core::eth::{ - block::{TypedBlockInfo, create_block}, - transaction::{PendingTransaction, TransactionInfo}, -}; -use foundry_evm::{ - backend::DatabaseError, - core::{either_evm::EitherEvm, precompiles::EC_RECOVER}, - traces::{CallTraceDecoder, CallTraceNode}, -}; -use foundry_evm_networks::NetworkConfigs; -use foundry_primitives::{FoundryNetwork, FoundryReceiptEnvelope, FoundryTxEnvelope}; +use alloy_primitives::{Address, B256, Bytes}; +use anvil_core::eth::transaction::PendingTransaction; +use foundry_evm::{backend::DatabaseError, core::either_evm::EitherEvm}; +use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope, FoundryTxType}; use op_revm::{OpContext, OpTransaction}; use revm::{ - Database, Inspector, - context::{Block as RevmBlock, Cfg, TxEnv}, - context_interface::result::{EVMError, ExecutionResult, Output}, - interpreter::InstructionResult, - primitives::hardfork::SpecId, + Database, DatabaseCommit, Inspector, + context::{Block as RevmBlock, TxEnv}, + context_interface::result::ResultAndState, }; -use std::{fmt, fmt::Debug, sync::Arc}; - -/// Represents an executed transaction (transacted on the DB) -pub struct ExecutedTransaction { - transaction: Arc>, - exit_reason: InstructionResult, - out: Option, - gas_used: u64, - logs: Vec, - traces: Vec, - nonce: u64, -} - -impl fmt::Debug for ExecutedTransaction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ExecutedTransaction") - .field("exit_reason", &self.exit_reason) - .field("gas_used", &self.gas_used) - .field("nonce", &self.nonce) - .finish_non_exhaustive() - } -} - -// == impl ExecutedTransaction == - -impl ExecutedTransaction { - /// Creates the receipt for the transaction - fn create_receipt(&self, cumulative_gas_used: &mut u64) -> FoundryReceiptEnvelope { - 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.is_ok()); - let receipt_with_bloom: ReceiptWithBloom = Receipt { - status: (status_code == 1).into(), - cumulative_gas_used: *cumulative_gas_used, - logs, +use std::{fmt, fmt::Debug}; + +/// Receipt builder for Foundry/Anvil that handles all transaction types +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub struct FoundryReceiptBuilder; + +impl ReceiptBuilder for FoundryReceiptBuilder { + type Transaction = FoundryTxEnvelope; + type Receipt = FoundryReceiptEnvelope; + + fn build_receipt( + &self, + ctx: ReceiptBuilderCtx<'_, FoundryTxType, E>, + ) -> FoundryReceiptEnvelope { + let receipt = alloy_consensus::Receipt { + status: Eip658Value::Eip658(ctx.result.is_success()), + cumulative_gas_used: ctx.cumulative_gas_used, + logs: ctx.result.into_logs(), } - .into(); - - match self.transaction.pending_transaction.transaction.as_ref() { - FoundryTxEnvelope::Legacy(_) => FoundryReceiptEnvelope::Legacy(receipt_with_bloom), - FoundryTxEnvelope::Eip2930(_) => FoundryReceiptEnvelope::Eip2930(receipt_with_bloom), - FoundryTxEnvelope::Eip1559(_) => FoundryReceiptEnvelope::Eip1559(receipt_with_bloom), - FoundryTxEnvelope::Eip4844(_) => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom), - FoundryTxEnvelope::Eip7702(_) => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom), - FoundryTxEnvelope::Deposit(_tx) => { - FoundryReceiptEnvelope::Deposit(op_alloy_consensus::OpDepositReceiptWithBloom { - receipt: op_alloy_consensus::OpDepositReceipt { - inner: receipt_with_bloom.receipt, - deposit_nonce: Some(0), - deposit_receipt_version: Some(1), - }, - logs_bloom: receipt_with_bloom.logs_bloom, - }) + .with_bloom(); + + match ctx.tx_type { + FoundryTxType::Legacy => FoundryReceiptEnvelope::Legacy(receipt), + FoundryTxType::Eip2930 => FoundryReceiptEnvelope::Eip2930(receipt), + FoundryTxType::Eip1559 => FoundryReceiptEnvelope::Eip1559(receipt), + FoundryTxType::Eip4844 => FoundryReceiptEnvelope::Eip4844(receipt), + FoundryTxType::Eip7702 => FoundryReceiptEnvelope::Eip7702(receipt), + FoundryTxType::Deposit => { + unreachable!("deposit receipts are built in commit_transaction") } - // TODO(onbjerg): we should impl support for Tempo transactions - FoundryTxEnvelope::Tempo(_) => todo!(), + FoundryTxType::Tempo => FoundryReceiptEnvelope::Tempo(receipt), } } } -/// Represents the outcome of mining a new block -pub struct ExecutedTransactions { - /// The block created after executing the `included` transactions - pub block: TypedBlockInfo, - /// All transactions included in the block - pub included: Vec>>, - /// All transactions that were invalid at the point of their execution and were not included in - /// the block - pub invalid: Vec>>, +/// Result of executing a transaction in [`AnvilBlockExecutor`]. +/// +/// Wraps [`EthTxResult`] with the sender address, needed for deposit nonce resolution. +#[derive(Debug)] +pub struct AnvilTxResult { + pub inner: EthTxResult, + pub sender: Address, } -impl fmt::Debug for ExecutedTransactions { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ExecutedTransactions") - .field("included", &self.included.len()) - .field("invalid", &self.invalid.len()) - .finish_non_exhaustive() +impl TxResult for AnvilTxResult { + type HaltReason = H; + + fn result(&self) -> &ResultAndState { + self.inner.result() } } -/// An executor for a series of transactions -pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator, T = FoundryTxEnvelope> { - /// where to insert the transactions - pub db: &'a mut Db, - /// type used to validate before inclusion - pub validator: &'a V, - /// all pending transactions - pub pending: std::vec::IntoIter>>, - pub evm_env: EvmEnv, +/// Execution context for [`AnvilBlockExecutor`], providing block-level data +/// needed for pre/post execution system calls. +#[derive(Debug, Clone)] +pub struct AnvilExecutionCtx { + /// Parent block hash — needed for EIP-2935 system call. pub parent_hash: B256, - /// Cumulative gas used by all executed transactions - pub gas_used: u64, - /// Cumulative blob gas used by all executed transactions - pub blob_gas_used: u64, - pub enable_steps_tracing: bool, - pub networks: NetworkConfigs, - pub print_logs: bool, - pub print_traces: bool, - /// Recorder used for decoding traces, used together with print_traces - pub call_trace_decoder: Arc, - /// Precompiles to inject to the EVM. - pub precompile_factory: Option>, - pub blob_params: BlobParams, - pub cheats: CheatsManager, + /// Whether Prague hardfork is active. + pub is_prague: bool, + /// Whether Cancun hardfork is active. + pub is_cancun: bool, } -impl TransactionExecutor<'_, DB, V> { - /// Executes all transactions and puts them in a new block with the provided `timestamp` - pub fn execute(mut self) -> ExecutedTransactions { - let mut transactions = Vec::new(); - let mut transaction_infos = Vec::new(); - let mut receipts = Vec::new(); - let mut bloom = Bloom::default(); - let mut cumulative_gas_used = 0u64; - let mut invalid = Vec::new(); - let mut included = Vec::new(); - let gas_limit = self.evm_env.block_env().gas_limit; - let parent_hash = self.parent_hash; - let block_number = self.evm_env.block_env().number; - let difficulty = self.evm_env.block_env().difficulty; - let mix_hash = self.evm_env.block_env().prevrandao; - let beneficiary = self.evm_env.block_env().beneficiary; - let timestamp = self.evm_env.block_env().timestamp; - let base_fee = if self.evm_env.cfg_env().spec.is_enabled_in(SpecId::LONDON) { - Some(self.evm_env.block_env().basefee) - } else { - None - }; - - let is_shanghai = self.evm_env.cfg_env().spec >= SpecId::SHANGHAI; - let is_cancun = self.evm_env.cfg_env().spec >= SpecId::CANCUN; - let is_prague = self.evm_env.cfg_env().spec >= SpecId::PRAGUE; - let excess_blob_gas = - if is_cancun { self.evm_env.block_env().blob_excess_gas() } else { None }; - let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None }; - - // EIP-2935: store parent block hash in history storage contract. - if is_prague && !block_number.is_zero() { - let env = Env::new(self.evm_env.clone(), Default::default(), self.networks); - let mut inspector = AnvilInspector::default(); - let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector); - // SYSTEM_ADDRESS is defined in EIP-4788 and reused by EIP-2935. - match evm.transact_system_call( - eip4788::SYSTEM_ADDRESS, - eip2935::HISTORY_STORAGE_ADDRESS, - Bytes::copy_from_slice(parent_hash.as_slice()), - ) { - Ok(result) => { - self.db.commit(result.state); - } - Err(err) => { - warn!(target: "backend", "EIP-2935 system call failed: {:?}", err); - } - } - } - - for tx in self.into_iter() { - let tx = match tx { - TransactionExecutionOutcome::Executed(tx) => { - included.push(tx.transaction.clone()); - tx - } - TransactionExecutionOutcome::BlockGasExhausted(tx) => { - 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_used().unwrap_or_default(), ?tx, "block blob gas limit exhausting, skipping transaction"); - continue; - } - TransactionExecutionOutcome::TransactionGasExhausted(tx) => { - trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "transaction gas limit exhausting, skipping transaction"); - continue; - } - TransactionExecutionOutcome::Invalid(tx, _) => { - trace!(target: "backend", ?tx, "skipping invalid transaction"); - invalid.push(tx); - continue; - } - TransactionExecutionOutcome::DatabaseError(_, err) => { - // Note: this is only possible in forking mode, if for example a rpc request - // failed - trace!(target: "backend", ?err, "Failed to execute transaction due to database error"); - continue; - } - }; - if is_cancun { - let tx_blob_gas = - tx.transaction.pending_transaction.transaction.blob_gas_used().unwrap_or(0); - cumulative_blob_gas_used = - Some(cumulative_blob_gas_used.unwrap_or(0u64).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, &mut bloom); - - // For contract creation transactions, compute the contract address from sender + nonce. - // This should be set even if the transaction reverted, matching geth's behavior. - let sender = *transaction.pending_transaction.sender(); - let contract_address = if transaction.pending_transaction.transaction.to().is_none() { - let addr = sender.create(tx.nonce); - trace!(target: "backend", "Contract creation tx: computed address {:?}", addr); - Some(addr) - } else { - None - }; - - 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, - traces, - exit, - out: out.map(Output::into_data), - nonce: tx.nonce, - gas_used: tx.gas_used, - }; - - transaction_infos.push(info); - receipts.push(receipt); - transactions.push(transaction.pending_transaction.transaction.clone()); - } - - let receipts_root = calculate_receipt_root(&receipts); - - let header = Header { - parent_hash, - ommers_hash: Default::default(), - beneficiary, - state_root: self.db.maybe_state_root().unwrap_or_default(), - transactions_root: Default::default(), // Will be computed by create_block - receipts_root, - logs_bloom: bloom, - difficulty, - number: block_number.saturating_to(), - gas_limit, - gas_used: cumulative_gas_used, - timestamp: timestamp.saturating_to(), - extra_data: Default::default(), - mix_hash: mix_hash.unwrap_or_default(), - nonce: Default::default(), - base_fee_per_gas: base_fee, - parent_beacon_block_root: is_cancun.then_some(Default::default()), - blob_gas_used: cumulative_blob_gas_used, - excess_blob_gas, - withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), - requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), - }; +/// Block executor for Anvil that implements [`BlockExecutor`]. +/// +/// Wraps an EVM instance and produces [`FoundryReceiptEnvelope`] receipts. +/// Validation (gas limits, blob gas, transaction validity) is handled by the +/// caller before transactions are fed to this executor. +pub struct AnvilBlockExecutor { + /// The EVM instance used for execution. + evm: E, + /// Execution context. + ctx: AnvilExecutionCtx, + /// Receipt builder. + receipt_builder: FoundryReceiptBuilder, + /// Receipts of executed transactions. + receipts: Vec, + /// Total gas used by transactions in this block. + gas_used: u64, + /// Blob gas used by the block. + blob_gas_used: u64, + /// Optional state change hook. + state_hook: Option>, +} - let block = create_block(header, transactions); - let block = TypedBlockInfo { block, transactions: transaction_infos, receipts }; - ExecutedTransactions { block, included, invalid } +impl fmt::Debug for AnvilBlockExecutor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AnvilBlockExecutor") + .field("evm", &self.evm) + .field("ctx", &self.ctx) + .field("gas_used", &self.gas_used) + .field("blob_gas_used", &self.blob_gas_used) + .field("receipts", &self.receipts.len()) + .finish_non_exhaustive() } +} - fn env_for(&self, tx: &PendingTransaction) -> Env { - let mut tx_env: OpTransaction = - FromRecoveredTx::from_recovered_tx(tx.transaction.as_ref(), *tx.sender()); - - if let FoundryTxEnvelope::Eip7702(tx_7702) = tx.transaction.as_ref() - && self.cheats.has_recover_overrides() - { - // Override invalid recovered authorizations with signature overrides from cheat manager - let cheated_auths = tx_7702 - .tx() - .authorization_list - .iter() - .zip(tx_env.base.authorization_list) - .map(|(signed_auth, either_auth)| { - either_auth.right_and_then(|recovered_auth| { - if recovered_auth.authority().is_none() - && let Ok(signature) = signed_auth.signature() - && let Some(override_addr) = - self.cheats.get_recover_override(&signature.as_bytes().into()) - { - Either::Right(RecoveredAuthorization::new_unchecked( - recovered_auth.into_parts().0, - RecoveredAuthority::Valid(override_addr), - )) - } else { - Either::Right(recovered_auth) - } - }) - }) - .collect(); - tx_env.base.authorization_list = cheated_auths; - } - - if self.networks.is_optimism() { - tx_env.enveloped_tx = Some(tx.transaction.encoded_2718().into()); +impl AnvilBlockExecutor { + /// Creates a new [`AnvilBlockExecutor`]. + pub fn new(evm: E, ctx: AnvilExecutionCtx) -> Self { + Self { + evm, + ctx, + receipt_builder: FoundryReceiptBuilder, + receipts: Vec::new(), + gas_used: 0, + blob_gas_used: 0, + state_hook: None, } - - Env::new(self.evm_env.clone(), tx_env, self.networks) } } -/// Represents the result of a single transaction execution attempt -pub enum TransactionExecutionOutcome { - /// Transaction successfully executed - Executed(ExecutedTransaction), - /// Invalid transaction not executed - Invalid(Arc>, InvalidTransactionError), - /// Execution skipped because could exceed block gas limit - BlockGasExhausted(Arc>), - /// Execution skipped because it exceeded the blob gas limit - BlobGasExhausted(Arc>), - /// Execution skipped because it exceeded the transaction gas limit - TransactionGasExhausted(Arc>), - /// When an error occurred during execution - DatabaseError(Arc>, DatabaseError), -} +impl BlockExecutor for AnvilBlockExecutor +where + E: Evm< + DB: StateDB, + Tx: FromRecoveredTx + FromTxWithEncoded, + >, +{ + type Transaction = FoundryTxEnvelope; + type Receipt = FoundryReceiptEnvelope; + type Evm = E; + type Result = AnvilTxResult; -impl fmt::Debug for TransactionExecutionOutcome { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Executed(_) => write!(f, "Executed(..)"), - Self::Invalid(_, err) => write!(f, "Invalid({err:?})"), - Self::BlockGasExhausted(_) => write!(f, "BlockGasExhausted(..)"), - Self::BlobGasExhausted(_) => write!(f, "BlobGasExhausted(..)"), - Self::TransactionGasExhausted(_) => write!(f, "TransactionGasExhausted(..)"), - Self::DatabaseError(_, err) => write!(f, "DatabaseError({err:?})"), + fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> { + // EIP-2935: store parent block hash in history storage contract. + if self.ctx.is_prague { + let result = self + .evm + .transact_system_call( + eip4788::SYSTEM_ADDRESS, + eip2935::HISTORY_STORAGE_ADDRESS, + Bytes::copy_from_slice(self.ctx.parent_hash.as_slice()), + ) + .map_err(BlockExecutionError::other)?; + + if let Some(hook) = &mut self.state_hook { + hook.on_state( + StateChangeSource::PreBlock( + alloy_evm::block::StateChangePreBlockSource::BlockHashesContract, + ), + &result.state, + ); + } + self.evm.db_mut().commit(result.state); } + Ok(()) } -} - -impl Iterator for &mut TransactionExecutor<'_, DB, V> { - type Item = TransactionExecutionOutcome; - fn next(&mut self) -> Option { - let transaction = self.pending.next()?; - let sender = *transaction.pending_transaction.sender(); - let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) { - Ok(account) => account, - 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, if not disabled - let max_block_gas = self.gas_used.saturating_add(env.tx.base.gas_limit); - if !env.evm_env.cfg_env.disable_block_gas_limit - && max_block_gas > env.evm_env.block_env.gas_limit - { - return Some(TransactionExecutionOutcome::BlockGasExhausted(transaction)); + fn execute_transaction_without_commit( + &mut self, + tx: impl ExecutableTx, + ) -> Result { + let (tx_env, tx) = tx.into_parts(); + + let block_available_gas = self.evm.block().gas_limit() - self.gas_used; + if tx.tx().gas_limit() > block_available_gas { + return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { + transaction_gas_limit: tx.tx().gas_limit(), + block_available_gas, + } + .into()); } - // check that we comply with the transaction's gas limit as imposed by Osaka (EIP-7825) - if env.evm_env.cfg_env.tx_gas_limit_cap.is_none() - && transaction.pending_transaction.transaction.gas_limit() - > env.evm_env.cfg_env().tx_gas_limit_cap() - { - return Some(TransactionExecutionOutcome::TransactionGasExhausted(transaction)); - } + let sender = *tx.signer(); + + let result = self.evm.transact(tx_env).map_err(|err| { + let hash = tx.tx().trie_hash(); + BlockExecutionError::evm(err, hash) + })?; + + Ok(AnvilTxResult { + inner: EthTxResult { + result, + blob_gas_used: tx.tx().blob_gas_used().unwrap_or_default(), + tx_type: tx.tx().tx_type(), + }, + sender, + }) + } - // 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.blob_gas_used().unwrap_or(0), - ); - if max_blob_gas > self.blob_params.max_blob_gas_per_block() { - return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction)); - } + fn commit_transaction(&mut self, output: Self::Result) -> Result { + let AnvilTxResult { + inner: EthTxResult { result: ResultAndState { result, state }, blob_gas_used, tx_type }, + sender, + } = output; - // validate before executing - if let Err(err) = self.validator.validate_pool_transaction_for( - &transaction.pending_transaction, - &account, - &env, - ) { - warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", transaction.hash(), err); - return Some(TransactionExecutionOutcome::Invalid(transaction, err)); + if let Some(hook) = &mut self.state_hook { + hook.on_state(StateChangeSource::Transaction(self.receipts.len()), &state); } - let nonce = account.nonce; + let gas_used = result.gas_used(); + self.gas_used += gas_used; - let mut inspector = AnvilInspector::default().with_tracing(); - if self.enable_steps_tracing { - inspector = inspector.with_steps_tracing(); - } - if self.print_logs { - inspector = inspector.with_log_collector(); + if self.ctx.is_cancun { + self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas_used); } - if self.print_traces { - inspector = inspector.with_trace_printer(); - } - - let exec_result = { - let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector); - self.networks.inject_precompiles(evm.precompiles_mut()); - if let Some(factory) = &self.precompile_factory { - evm.precompiles_mut().extend_precompiles(factory.precompiles()); - } - - let cheats = Arc::new(self.cheats.clone()); - if cheats.has_recover_overrides() { - let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats)); - evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| { - Some(DynPrecompile::new_stateful( - cheat_ecrecover.precompile_id().clone(), - move |input| cheat_ecrecover.call(input), - )) - }); - } - - trace!(target: "backend", "[{:?}] executing", transaction.hash()); - // transact and commit the transaction - match evm.transact_commit(env.tx) { - 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: {e}"), - } - } + let receipt = if tx_type == FoundryTxType::Deposit { + let deposit_nonce = state.get(&sender).map(|acc| acc.info.nonce); + let receipt = alloy_consensus::Receipt { + status: Eip658Value::Eip658(result.is_success()), + cumulative_gas_used: self.gas_used, + logs: result.into_logs(), } + .with_bloom(); + FoundryReceiptEnvelope::Deposit(op_alloy_consensus::OpDepositReceiptWithBloom { + receipt: op_alloy_consensus::OpDepositReceipt { + inner: receipt.receipt, + deposit_nonce, + deposit_receipt_version: deposit_nonce.map(|_| 1), + }, + logs_bloom: receipt.logs_bloom, + }) + } else { + self.receipt_builder.build_receipt(ReceiptBuilderCtx { + tx_type, + evm: &self.evm, + result, + state: &state, + cumulative_gas_used: self.gas_used, + }) }; - if self.print_traces { - inspector.print_traces(self.call_trace_decoder.clone()); - } - inspector.print_logs(); - - let (exit_reason, gas_used, out, logs) = match exec_result { - ExecutionResult::Success { reason, gas_used, logs, output, .. } => { - (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 } => { - (op_haltreason_to_instruction_result(reason), gas_used, None, None) - } - }; + self.receipts.push(receipt); + self.evm.db_mut().commit(state); - if exit_reason == InstructionResult::OutOfGas { - // this currently useful for debugging estimations - warn!(target: "backend", "[{:?}] executed with out of gas", transaction.hash()) - } + Ok(gas_used) + } - trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out); + fn finish( + self, + ) -> Result<(Self::Evm, BlockExecutionResult), BlockExecutionError> + { + Ok(( + self.evm, + BlockExecutionResult { + receipts: self.receipts, + requests: Default::default(), + gas_used: self.gas_used, + blob_gas_used: self.blob_gas_used, + }, + )) + } - // Track the total gas used for total gas per block checks - self.gas_used = self.gas_used.saturating_add(gas_used); + fn set_state_hook(&mut self, hook: Option>) { + self.state_hook = hook; + } - // Track the total blob gas used for total blob gas per blob checks - if let Some(blob_gas) = transaction.pending_transaction.transaction.blob_gas_used() { - self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas); - } + fn evm_mut(&mut self) -> &mut Self::Evm { + &mut self.evm + } - trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used); + fn evm(&self) -> &Self::Evm { + &self.evm + } - let tx = ExecutedTransaction { - transaction, - exit_reason, - out, - gas_used, - logs: logs.unwrap_or_default(), - traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(), - nonce, - }; + fn receipts(&self) -> &[FoundryReceiptEnvelope] { + &self.receipts + } +} - Some(TransactionExecutionOutcome::Executed(tx)) +pub struct AnvilBlockExecutorFactory; + +impl AnvilBlockExecutorFactory { + pub fn create_executor( + evm: EitherEvm, + ctx: AnvilExecutionCtx, + ) -> AnvilBlockExecutor> + where + DB: StateDB, + { + AnvilBlockExecutor::new(evm, ctx) } } -/// Inserts all logs into the bloom -fn build_logs_bloom(logs: &[Log], bloom: &mut Bloom) { - for log in logs { - bloom.accrue(BloomInput::Raw(&log.address[..])); - for topic in log.topics() { - bloom.accrue(BloomInput::Raw(&topic[..])); - } +/// Builds the per-tx `OpTransaction` from a pending transaction, replicating the logic +/// from `TransactionExecutor::env_for`. +pub fn build_tx_env_for_pending( + tx: &PendingTransaction, + cheats: &CheatsManager, + is_optimism: bool, +) -> OpTransaction { + let mut tx_env: OpTransaction = + FromRecoveredTx::from_recovered_tx(tx.transaction.as_ref(), *tx.sender()); + + if let FoundryTxEnvelope::Eip7702(tx_7702) = tx.transaction.as_ref() + && cheats.has_recover_overrides() + { + let cheated_auths = tx_7702 + .tx() + .authorization_list + .iter() + .zip(tx_env.base.authorization_list) + .map(|(signed_auth, either_auth)| { + either_auth.right_and_then(|recovered_auth| { + if recovered_auth.authority().is_none() + && let Ok(signature) = signed_auth.signature() + && let Some(override_addr) = + cheats.get_recover_override(&signature.as_bytes().into()) + { + Either::Right(RecoveredAuthorization::new_unchecked( + recovered_auth.into_parts().0, + RecoveredAuthority::Valid(override_addr), + )) + } else { + Either::Right(recovered_auth) + } + }) + }) + .collect(); + tx_env.base.authorization_list = cheated_auths; } + + if is_optimism { + tx_env.enveloped_tx = Some(tx.transaction.encoded_2718().into()); + } + + tx_env } /// Creates a database with given database and inspector. -pub fn new_evm_with_inspector( +pub fn new_eth_evm_with_inspector( db: DB, - env: &Env, + evm_env: &EvmEnv, inspector: I, + is_optimism: bool, ) -> EitherEvm where DB: Database + Debug, I: Inspector> + Inspector>, { - if env.networks.is_optimism() { + if is_optimism { let evm_env = EvmEnv::new( - env.evm_env - .cfg_env - .clone() - .with_spec_and_mainnet_gas_params(op_revm::OpSpecId::ISTHMUS), - env.evm_env.block_env.clone(), + evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(op_revm::OpSpecId::ISTHMUS), + evm_env.block_env.clone(), ); EitherEvm::Op(OpEvmFactory::default().create_evm_with_inspector(db, evm_env, inspector)) } else { EitherEvm::Eth(EthEvmFactory::default().create_evm_with_inspector( db, - env.evm_env.clone(), + evm_env.clone(), inspector, )) } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index b6f5345aa850d..423173471db65 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -26,7 +26,7 @@ use alloy_rpc_types::{ }; use alloy_transport::TransportError; use foundry_common::provider::{ProviderBuilder, RetryProvider}; -use foundry_primitives::FoundryTxReceipt; +use foundry_primitives::{FoundryTxEnvelope, FoundryTxReceipt}; use parking_lot::{ RawRwLock, RwLock, lock_api::{RwLockReadGuard, RwLockWriteGuard}, @@ -664,7 +664,7 @@ pub struct ClientForkConfig { /// total difficulty of the chain until this block pub total_difficulty: U256, /// Transactions to force include in the forked chain - pub force_transactions: Option>, + pub force_transactions: Option>>, } impl ClientForkConfig { diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index 72acc5e62a39e..220cfe93dddea 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -1,10 +1,10 @@ //! Handler that can get current storage related data use crate::mem::Backend; +use alloy_consensus::TxReceipt; use alloy_network::{AnyRpcBlock, Network}; use alloy_primitives::B256; use anvil_core::eth::block::Block; -use foundry_primitives::{FoundryNetwork, FoundryReceiptEnvelope}; use std::{fmt, sync::Arc}; /// A type that can fetch data related to the ethereum storage. @@ -39,16 +39,17 @@ impl StorageInfo { } } -impl StorageInfo { - // TODO: receipts methods require N::ReceiptEnvelope generalization - +impl StorageInfo +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ /// Returns the receipts of the current block - pub fn current_receipts(&self) -> Option> { + pub fn current_receipts(&self) -> Option> { self.backend.mined_receipts(self.backend.best_hash()) } /// Returns the receipts of the block with the given hash - pub fn receipts(&self, hash: B256) -> Option> { + pub fn receipts(&self, hash: B256) -> Option> { self.backend.mined_receipts(hash) } } diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index e50f2115f80c8..bb63e74e912e0 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -7,7 +7,8 @@ use foundry_evm::{ decode::decode_console_logs, inspectors::{LogCollector, TracingInspector}, traces::{ - CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig, render_trace_arena_inner, + CallTraceDecoder, CallTraceNode, SparsedTraceArena, TracingInspectorConfig, + render_trace_arena_inner, }, }; use revm::{ @@ -33,7 +34,48 @@ pub struct AnvilInspector { pub transfer: Option, } +/// Configuration for per-transaction inspector lifecycle. +#[derive(Clone, Debug)] +pub struct InspectorTxConfig { + /// Whether to print traces to stdout. + pub print_traces: bool, + /// Whether to print logs to stdout. + pub print_logs: bool, + /// Whether to enable step-level tracing (with state diffs). + pub enable_steps_tracing: bool, + /// Decoder for populating trace labels. + pub call_trace_decoder: Arc, +} + impl AnvilInspector { + /// Finish a transaction: print traces/logs, drain the tracer, and reset for the next tx. + /// + /// Returns the collected call trace nodes from the finished transaction. + pub fn finish_transaction(&mut self, config: &InspectorTxConfig) -> Vec { + // Print before draining so the tracer is still populated. + if config.print_traces { + self.print_traces(config.call_trace_decoder.clone()); + } + self.print_logs(); + + let traces = self.tracer.take().map(|t| t.into_traces().into_nodes()).unwrap_or_default(); + + // Reinstall tracer for next tx. + let tracing_config = if config.enable_steps_tracing { + TracingInspectorConfig::all().with_state_diffs() + } else { + TracingInspectorConfig::all().set_steps(false) + }; + self.tracer = Some(TracingInspector::new(tracing_config)); + + // Reset log collector for next tx. + if config.print_logs { + self.log_collector = Some(LogCollector::Capture { logs: Vec::new() }); + } + + traces + } + /// Called after the inspecting the evm /// /// This will log all `console.sol` logs diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d0cbce6fe3ecd..d542bcf17db19 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1,15 +1,15 @@ //! In-memory blockchain backend. use self::state::trie_storage; -use super::executor::new_evm_with_inspector; +use super::executor::new_eth_evm_with_inspector; use crate::{ ForkChoice, NodeConfig, PrecompileFactory, config::PruneStateHistoryConfig, eth::{ backend::{ cheats::{CheatEcrecover, CheatsManager}, - db::{Db, MaybeFullDatabase, SerializableState, StateDb}, + db::{AnvilCacheDB, Db, MaybeFullDatabase, SerializableState, StateDb}, env::Env, - executor::{ExecutedTransactions, TransactionExecutor}, + executor::{AnvilBlockExecutorFactory, AnvilExecutionCtx, build_tx_env_for_pending}, fork::ClientFork, genesis::GenesisConfig, mem::{ @@ -27,7 +27,7 @@ use crate::{ sign::build_impersonated, }, mem::{ - inspector::AnvilInspector, + inspector::{AnvilInspector, InspectorTxConfig}, storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome}, }, }; @@ -35,15 +35,17 @@ use alloy_chains::NamedChain; use alloy_consensus::{ Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait, TrieAccount, TxEnvelope, TxReceipt, Typed2718, + constants::EMPTY_WITHDRAWALS, proofs::{calculate_receipt_root, calculate_transaction_root}, transaction::Recovered, }; use alloy_eips::{ - BlockNumHash, Encodable2718, eip2935, eip4844::kzg_to_versioned_hash, eip7840::BlobParams, - eip7910::SystemContract, + BlockNumHash, Encodable2718, eip2935, eip4844::kzg_to_versioned_hash, + eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams, eip7910::SystemContract, }; use alloy_evm::{ - Database, Evm, FromRecoveredTx, + Database, Evm, EvmEnv, FromRecoveredTx, + block::BlockExecutor, eth::EthEvmContext, overrides::{OverrideBlockHashes, apply_state_overrides}, precompiles::{DynPrecompile, Precompile, PrecompilesMap}, @@ -53,7 +55,7 @@ use alloy_network::{ ReceiptResponse, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction, }; use alloy_primitives::{ - Address, B256, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom, + Address, B256, Bloom, BloomInput, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom, map::{AddressMap, HashMap, HashSet}, }; use alloy_rpc_types::{ @@ -77,7 +79,7 @@ use alloy_rpc_types::{ use alloy_serde::{OtherFields, WithOtherFields}; use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer}; use anvil_core::eth::{ - block::{Block, BlockInfo}, + block::{Block, BlockInfo, TypedBlockInfo, create_block}, transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo}, }; use anvil_rpc::error::RpcError; @@ -94,7 +96,10 @@ use foundry_evm::{ CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector, TracingInspectorConfig, }, - utils::{get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id}, + utils::{ + block_env_from_header, get_blob_base_fee_update_fraction, + get_blob_base_fee_update_fraction_by_spec_id, + }, }; use foundry_primitives::{ FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope, @@ -105,7 +110,7 @@ use op_alloy_consensus::DEPOSIT_TX_TYPE_ID; use op_revm::{OpContext, OpHaltReason, OpTransaction}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use revm::{ - DatabaseCommit, Inspector, + Database as RevmDatabase, DatabaseCommit, Inspector, context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv}, context_interface::{ block::BlobExcessGasAndPrice, @@ -151,7 +156,7 @@ pub const MIN_CREATE_GAS: u128 = 53000; pub type State = foundry_evm::utils::StateChangeset; /// A block request, which includes the Pool Transactions if it's Pending -pub enum BlockRequest { +pub enum BlockRequest { Pending(Vec>>), Number(u64), } @@ -281,8 +286,7 @@ impl Backend { return true; } // Ensure EIP-3607 is disabled - let mut env = self.env.write(); - env.evm_env.cfg_env.disable_eip3607 = true; + self.env.write().evm_env.cfg_env.disable_eip3607 = true; self.cheats.impersonate(addr) } @@ -368,8 +372,7 @@ impl Backend { /// Sets the block number pub fn set_block_number(&self, number: u64) { - let mut env = self.env.write(); - env.evm_env.block_env.number = U256::from(number); + self.env.write().evm_env.block_env.number = U256::from(number); } /// Returns the client coinbase address. @@ -433,7 +436,7 @@ impl Backend { /// Returns the configured specid pub fn spec_id(&self) -> SpecId { - self.env.read().evm_env.cfg_env.spec + *self.env.read().evm_env.spec_id() } /// Returns true for post London @@ -468,7 +471,7 @@ impl Backend { /// Returns the precompiles for the current spec. pub fn precompiles(&self) -> BTreeMap { - let spec_id = self.env.read().evm_env.cfg_env.spec; + let spec_id = self.spec_id(); let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)); let mut precompiles_map = BTreeMap::::default(); @@ -492,7 +495,7 @@ impl Backend { pub fn system_contracts(&self) -> BTreeMap { let mut system_contracts = BTreeMap::::default(); - let spec_id = self.env.read().evm_env.cfg_env.spec; + let spec_id = self.spec_id(); if spec_id >= SpecId::CANCUN { system_contracts.extend(SystemContract::cancun()); @@ -507,7 +510,7 @@ impl Backend { /// Returns [`BlobParams`] corresponding to the current spec. pub fn blob_params(&self) -> BlobParams { - let spec_id = self.env.read().evm_env.cfg_env.spec; + let spec_id = self.spec_id(); if spec_id >= SpecId::OSAKA { return BlobParams::osaka(); @@ -889,7 +892,7 @@ impl Backend { let mut block = WithOtherFields::new(block); // If Arbitrum, apply chain specifics to converted block. - if is_arbitrum(self.env.read().evm_env.cfg_env.chain_id) { + if is_arbitrum(self.chain_id().to::()) { // Set `l1BlockNumber` field. block.other.insert("l1BlockNumber".to_string(), number.into()); } @@ -1000,10 +1003,10 @@ impl Backend { } /// Creates an EVM instance with optionally injected precompiles. - fn new_evm_with_inspector_ref<'db, I, DB>( + fn new_eth_evm_with_inspector_ref<'db, I, DB>( &self, db: &'db DB, - env: &Env, + evm_env: &EvmEnv, inspector: &'db mut I, ) -> EitherEvm, &'db mut I, PrecompilesMap> where @@ -1012,8 +1015,9 @@ impl Backend { + Inspector>>, WrapDatabaseRef<&'db DB>: Database, { - let mut evm = new_evm_with_inspector(WrapDatabaseRef(db), env, inspector); - self.env.read().networks.inject_precompiles(evm.precompiles_mut()); + let mut evm = + new_eth_evm_with_inspector(WrapDatabaseRef(db), evm_env, inspector, self.is_optimism()); + self.env.write().networks.inject_precompiles(evm.precompiles_mut()); if let Some(factory) = &self.precompile_factory { evm.precompiles_mut().extend_precompiles(factory.precompiles()); @@ -1046,7 +1050,7 @@ impl Backend { request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Env { + ) -> (EvmEnv, OpTransaction) { let tx_type = request.minimal_tx_type() as u8; let WithOtherFields:: { @@ -1076,22 +1080,22 @@ impl Backend { } = fee_details; let gas_limit = gas.unwrap_or(block_env.gas_limit); - let mut env = self.env.read().clone(); - env.evm_env.block_env = block_env; + let mut evm_env = self.env.read().evm_env.clone(); + evm_env.block_env = block_env; // we want to disable this in eth_call, since this is common practice used by other node // impls and providers - env.evm_env.cfg_env.disable_block_gas_limit = true; - env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); + evm_env.cfg_env.disable_block_gas_limit = true; + evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); // The basefee should be ignored for calls against state for // - eth_call // - eth_estimateGas // - eth_createAccessList // - tracing - env.evm_env.cfg_env.disable_base_fee = true; + evm_env.cfg_env.disable_base_fee = true; // Disable nonce check in revm - env.evm_env.cfg_env.disable_nonce_check = true; + evm_env.cfg_env.disable_nonce_check = true; let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| { self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE) @@ -1107,7 +1111,7 @@ impl Backend { max_fee_per_blob_gas: max_fee_per_blob_gas .or_else(|| { if !blob_hashes.is_empty() { - env.evm_env.block_env.blob_gasprice() + evm_env.block_env.blob_gasprice() } else { Some(0) } @@ -1120,30 +1124,30 @@ impl Backend { tx_type, value: value.unwrap_or_default(), data: input.into_input().unwrap_or_default(), - chain_id: Some(chain_id.unwrap_or(self.env.read().evm_env.cfg_env.chain_id)), + chain_id: Some(chain_id.unwrap_or(self.chain_id().to::())), access_list: access_list.unwrap_or_default(), blob_hashes, ..Default::default() }; base.set_signed_authorization(authorization_list.unwrap_or_default()); - env.tx = OpTransaction { base, ..Default::default() }; + let mut tx_env = OpTransaction { base, ..Default::default() }; if let Some(nonce) = nonce { - env.tx.base.nonce = nonce; + tx_env.base.nonce = nonce; } - if env.evm_env.block_env.basefee == 0 { + if evm_env.block_env.basefee == 0 { // this is an edge case because the evm fails if `tx.effective_gas_price < base_fee` // 0 is only possible if it's manually set - env.evm_env.cfg_env.disable_base_fee = true; + evm_env.cfg_env.disable_base_fee = true; } // Deposit transaction? if let Ok(deposit) = get_deposit_tx_parts(&other) { - env.tx.deposit = deposit; + tx_env.deposit = deposit; } - env + (evm_env, tx_env) } pub fn call_with_state( @@ -1155,9 +1159,9 @@ impl Backend { ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { let mut inspector = self.build_inspector(); - let env = self.build_call_env(request, fee_details, block_env); - let mut evm = self.new_evm_with_inspector_ref(state, &env, &mut inspector); - let ResultAndState { result, state } = evm.transact(env.tx)?; + let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let mut evm = self.new_eth_evm_with_inspector_ref(state, &evm_env, &mut inspector); + let ResultAndState { result, state } = evm.transact(tx_env)?; let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { (reason.into(), gas_used, Some(output)) @@ -1189,9 +1193,9 @@ impl Backend { let mut inspector = AccessListInspector::new(request.access_list.clone().unwrap_or_default()); - let env = self.build_call_env(request, fee_details, block_env); - let mut evm = self.new_evm_with_inspector_ref(state, &env, &mut inspector); - let ResultAndState { result, state: _ } = evm.transact(env.tx)?; + let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let mut evm = self.new_eth_evm_with_inspector_ref(state, &evm_env, &mut inspector); + let ResultAndState { result, state: _ } = evm.transact(tx_env)?; let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { (reason.into(), gas_used, Some(output)) @@ -1431,8 +1435,8 @@ impl Backend { let mut results = Vec::new(); // Configure the block environment - let mut env = self.env.read().clone(); - env.evm_env.block_env = block_env_from_header(&block.header); + let mut evm_env = self.env.read().evm_env.clone(); + evm_env.block_env = block_env_from_header(&block.header); // Execute each transaction in the block with tracing for tx_envelope in &block.body.transactions { @@ -1448,12 +1452,12 @@ impl Backend { pending_tx.transaction.as_ref(), *pending_tx.sender(), ); - if env.networks.is_optimism() { + if self.is_optimism() { tx_env.enveloped_tx = Some(pending_tx.transaction.encoded_2718().into()); } // Execute the transaction with the inspector - let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); + let mut evm = self.new_eth_evm_with_inspector_ref(&cache_db, &evm_env, &mut inspector); let result = evm.transact(tx_env.clone()).ok()?; // Build TraceResults from the inspector and execution result @@ -1566,121 +1570,7 @@ impl Backend { } Ok(None) } -} - -impl Backend -where - N::ReceiptEnvelope: alloy_consensus::TxReceipt, -{ - /// Returns all `Log`s mined by the node that were emitted in the `block` and match the `Filter` - fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec { - let mut all_logs = Vec::new(); - let mut block_log_index = 0u32; - - let storage = self.blockchain.storage.read(); - - for tx in block.body.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 { - if filter.matches(log) { - all_logs.push(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, - }); - } - block_log_index += 1; - } - } - all_logs - } - - /// Returns the logs of the block that match the filter - async fn logs_for_block( - &self, - filter: Filter, - hash: B256, - ) -> Result, BlockchainError> { - if let Some(block) = self.blockchain.get_block_by_hash(&hash) { - return Ok(self.mined_logs_for_block(filter, block, hash)); - } - - if let Some(fork) = self.get_fork() { - return Ok(fork.logs(&filter).await?); - } - - Ok(Vec::new()) - } - /// Returns the logs that match the filter in the given range of blocks - async fn logs_for_range( - &self, - filter: &Filter, - mut from: u64, - to: u64, - ) -> Result, BlockchainError> { - let mut all_logs = Vec::new(); - - // get the range that predates the fork if any - if let Some(fork) = self.get_fork() { - let mut to_on_fork = to; - - if !fork.predates_fork(to) { - // adjust the ranges - to_on_fork = fork.block_number(); - } - - if fork.predates_fork_inclusive(from) { - // this data is only available on the forked client - let filter = filter.clone().from_block(from).to_block(to_on_fork); - all_logs = fork.logs(&filter).await?; - - // update the range - from = fork.block_number() + 1; - } - } - - for number in from..=to { - if let Some((block, hash)) = self.get_block_with_hash(number) { - all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash)); - } - } - - Ok(all_logs) - } - - /// Returns the logs according to the filter - pub async fn logs(&self, filter: Filter) -> Result, BlockchainError> { - trace!(target: "backend", "get logs [{:?}]", filter); - if let Some(hash) = filter.get_block_hash() { - self.logs_for_block(filter, hash).await - } else { - let best = self.best_number(); - let to_block = - self.convert_block_number(filter.block_option.get_to_block().copied()).min(best); - let from_block = - self.convert_block_number(filter.block_option.get_from_block().copied()); - if from_block > best { - return Err(BlockchainError::BlockOutOfRange(best, from_block)); - } - - self.logs_for_range(&filter, from_block, to_block).await - } - } -} - -impl Backend { /// Initialises the balance of the given accounts #[expect(clippy::too_many_arguments)] pub async fn with_genesis( @@ -1707,8 +1597,7 @@ impl Backend { } else { let env = env.read(); Blockchain::new( - &env, - env.evm_env.cfg_env.spec, + &env.evm_env, fees.is_eip1559().then(|| fees.base_fee()), genesis.timestamp, genesis.number, @@ -1959,7 +1848,6 @@ impl Backend { let env = self.env.read().clone(); let genesis_timestamp = self.genesis.timestamp; let genesis_number = self.genesis.number; - let spec_id = self.spec_id(); // Reset environment to genesis state { @@ -1974,7 +1862,7 @@ impl Backend { // Clear all storage and reinitialize with genesis let base_fee = if self.fees.is_eip1559() { Some(self.fees.base_fee()) } else { None }; *self.blockchain.storage.write() = - BlockchainStorage::new(&env, spec_id, base_fee, genesis_timestamp, genesis_number); + BlockchainStorage::new(&env.evm_env, base_fee, genesis_timestamp, genesis_number); self.states.write().clear(); // Clear the database @@ -2065,263 +1953,197 @@ impl Backend { Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove)) } - /// Get the current state. - pub async fn serialized_state( + /// executes the transactions without writing to the underlying database + pub async fn inspect_tx( &self, - preserve_historical_states: bool, - ) -> Result { - let at = self.env.read().evm_env.block_env.clone(); - let best_number = self.blockchain.storage.read().best_number; - let blocks = self.blockchain.storage.read().serialized_blocks(); - let transactions = self.blockchain.storage.read().serialized_transactions(); - let historical_states = if preserve_historical_states { - Some(self.states.write().serialized_states()) - } else { - None + tx: Arc>, + ) -> Result< + (InstructionResult, Option, u64, State, Vec), + BlockchainError, + > { + let evm_env = self.next_env().evm_env; + let mut tx_env: OpTransaction = FromRecoveredTx::from_recovered_tx( + tx.pending_transaction.transaction.as_ref(), + *tx.pending_transaction.sender(), + ); + + if self.is_optimism() { + tx_env.enveloped_tx = Some(tx.pending_transaction.transaction.encoded_2718().into()); + } + + let db = self.db.read().await; + let mut inspector = self.build_inspector(); + let mut evm = self.new_eth_evm_with_inspector_ref(&**db, &evm_env, &mut inspector); + let ResultAndState { result, state } = evm.transact(tx_env)?; + let (exit_reason, gas_used, out, logs) = match result { + ExecutionResult::Success { reason, gas_used, logs, output, .. } => { + (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 } => { + let eth_reason = op_haltreason_to_instruction_result(reason); + (eth_reason, gas_used, None, None) + } }; - let state = self.db.read().await.dump_state( - at, - best_number, - blocks, - transactions, - historical_states, - )?; - state.ok_or_else(|| { - RpcError::invalid_params("Dumping state not supported with the current configuration") - .into() - }) - } + drop(evm); + inspector.print_logs(); - /// Write all chain data to serialized bytes buffer - pub async fn dump_state( - &self, - preserve_historical_states: bool, - ) -> Result { - let state = self.serialized_state(preserve_historical_states).await?; - let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); - encoder - .write_all(&serde_json::to_vec(&state).unwrap_or_default()) - .map_err(|_| BlockchainError::DataUnavailable)?; - Ok(encoder.finish().unwrap_or_default().into()) - } + if self.print_traces { + inspector.print_traces(self.call_trace_decoder.clone()); + } - /// Apply [SerializableState] data to the backend storage. - pub async fn load_state(&self, state: SerializableState) -> Result { - // load the blocks and transactions into the storage - self.blockchain.storage.write().load_blocks(state.blocks.clone()); - self.blockchain.storage.write().load_transactions(state.transactions.clone()); - // reset the block env - if let Some(block) = state.block.clone() { - self.env.write().evm_env.block_env = block.clone(); + Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default())) + } +} - // Set the current best block number. - // Defaults to block number for compatibility with existing state files. - let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash())); +impl Backend +where + N::ReceiptEnvelope: alloy_consensus::TxReceipt + Clone, +{ + /// Returns all `Log`s mined by the node that were emitted in the `block` and match the `Filter` + fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec { + let mut all_logs = Vec::new(); + let mut block_log_index = 0u32; - let best_number = state.best_block_number.unwrap_or(block.number.saturating_to()); - if let Some((number, hash)) = fork_num_and_hash { - trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number); - // If the state.block_number is greater than the fork block number, set best number - // to the state block number. - // Ref: https://github.com/foundry-rs/foundry/issues/9539 - if best_number > number { - self.blockchain.storage.write().best_number = best_number; - let best_hash = - self.blockchain.storage.read().hash(best_number.into()).ok_or_else( - || { - BlockchainError::RpcError(RpcError::internal_error_with(format!( - "Best hash not found for best number {best_number}", - ))) - }, - )?; - self.blockchain.storage.write().best_hash = best_hash; - } else { - // If loading state file on a fork, set best number to the fork block number. - // Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838 - self.blockchain.storage.write().best_number = number; - self.blockchain.storage.write().best_hash = hash; - } - } else { - self.blockchain.storage.write().best_number = best_number; + let storage = self.blockchain.storage.read(); - // Set the current best block hash; - let best_hash = - self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| { - BlockchainError::RpcError(RpcError::internal_error_with(format!( - "Best hash not found for best number {best_number}", - ))) - })?; + for tx in block.body.transactions { + let Some(tx) = storage.transactions.get(&tx.hash()) else { + continue; + }; - self.blockchain.storage.write().best_hash = best_hash; - } - } + let logs = tx.receipt.logs(); + let transaction_hash = tx.info.transaction_hash; - if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number()) { - let header = &latest.header; - let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - header.gas_used(), - 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, - get_blob_base_fee_update_fraction( - self.env.read().evm_env.cfg_env.chain_id, - header.timestamp, - ), - )); + for log in logs { + if filter.matches(log) { + all_logs.push(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, + }); + } + block_log_index += 1; + } } + all_logs + } - if !self.db.write().await.load_state(state.clone())? { - return Err(RpcError::invalid_params( - "Loading state not supported with the current configuration", - ) - .into()); + /// Returns the logs of the block that match the filter + async fn logs_for_block( + &self, + filter: Filter, + hash: B256, + ) -> Result, BlockchainError> { + if let Some(block) = self.blockchain.get_block_by_hash(&hash) { + return Ok(self.mined_logs_for_block(filter, block, hash)); } - if let Some(historical_states) = state.historical_states { - self.states.write().load_states(historical_states); + if let Some(fork) = self.get_fork() { + return Ok(fork.logs(&filter).await?); } - Ok(true) - } - - /// Deserialize and add all chain data to the backend storage - pub async fn load_state_bytes(&self, buf: Bytes) -> Result { - let orig_buf = &buf.0[..]; - let mut decoder = GzDecoder::new(orig_buf); - let mut decoded_data = Vec::new(); - - let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() { - decoder - .read_to_end(decoded_data.as_mut()) - .map_err(|_| BlockchainError::FailedToDecodeStateDump)?; - &decoded_data - } else { - &buf.0 - }) - .map_err(|_| BlockchainError::FailedToDecodeStateDump)?; - - self.load_state(state).await + Ok(Vec::new()) } - /// executes the transactions without writing to the underlying database - pub async fn inspect_tx( + /// Returns the logs that match the filter in the given range of blocks + async fn logs_for_range( &self, - tx: Arc, - ) -> Result< - (InstructionResult, Option, u64, State, Vec), - BlockchainError, - > { - let mut env = self.next_env(); - env.tx = FromRecoveredTx::from_recovered_tx( - tx.pending_transaction.transaction.as_ref(), - *tx.pending_transaction.sender(), - ); + filter: &Filter, + mut from: u64, + to: u64, + ) -> Result, BlockchainError> { + let mut all_logs = Vec::new(); - if env.networks.is_optimism() { - env.tx.enveloped_tx = Some(tx.pending_transaction.transaction.encoded_2718().into()); - } + // get the range that predates the fork if any + if let Some(fork) = self.get_fork() { + let mut to_on_fork = to; - let db = self.db.read().await; - let mut inspector = self.build_inspector(); - let mut evm = self.new_evm_with_inspector_ref(&**db, &env, &mut inspector); - let ResultAndState { result, state } = evm.transact(env.tx)?; - let (exit_reason, gas_used, out, logs) = match result { - ExecutionResult::Success { reason, gas_used, logs, output, .. } => { - (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 } => { - let eth_reason = op_haltreason_to_instruction_result(reason); - (eth_reason, gas_used, None, None) + if !fork.predates_fork(to) { + // adjust the ranges + to_on_fork = fork.block_number(); } - }; - drop(evm); - inspector.print_logs(); + if fork.predates_fork_inclusive(from) { + // this data is only available on the forked client + let filter = filter.clone().from_block(from).to_block(to_on_fork); + all_logs = fork.logs(&filter).await?; - if self.print_traces { - inspector.print_traces(self.call_trace_decoder.clone()); + // update the range + from = fork.block_number() + 1; + } } - Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default())) - } + for number in from..=to { + if let Some((block, hash)) = self.get_block_with_hash(number) { + all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash)); + } + } - /// Creates the pending block - /// - /// This will execute all transaction in the order they come but will not mine the block - pub async fn pending_block(&self, pool_transactions: Vec>) -> BlockInfo { - self.with_pending_block(pool_transactions, |_, block| block).await + Ok(all_logs) } - /// Creates the pending block - /// - /// This will execute all transaction in the order they come but will not mine the block - pub async fn with_pending_block( - &self, - pool_transactions: Vec>, - f: F, - ) -> T - where - F: FnOnce(Box, BlockInfo) -> T, - { - let db = self.db.read().await; - let env = self.next_env(); + /// Returns the logs according to the filter + pub async fn logs(&self, filter: Filter) -> Result, BlockchainError> { + trace!(target: "backend", "get logs [{:?}]", filter); + if let Some(hash) = filter.get_block_hash() { + self.logs_for_block(filter, hash).await + } else { + let best = self.best_number(); + let to_block = + self.convert_block_number(filter.block_option.get_to_block().copied()).min(best); + let from_block = + self.convert_block_number(filter.block_option.get_from_block().copied()); + if from_block > best { + return Err(BlockchainError::BlockOutOfRange(best, from_block)); + } - let mut cache_db = CacheDB::new(&*db); + self.logs_for_range(&filter, from_block, to_block).await + } + } + /// Returns all receipts of the block + pub fn mined_receipts(&self, hash: B256) -> Option> { + let block = self.mined_block_by_hash(hash)?; + let mut receipts = Vec::new(); let storage = self.blockchain.storage.read(); - - let executor = TransactionExecutor { - db: &mut cache_db, - validator: self, - pending: pool_transactions.into_iter(), - evm_env: env.evm_env, - parent_hash: storage.best_hash, - gas_used: 0, - blob_gas_used: 0, - enable_steps_tracing: self.enable_steps_tracing, - print_logs: self.print_logs, - print_traces: self.print_traces, - call_trace_decoder: self.call_trace_decoder.clone(), - precompile_factory: self.precompile_factory.clone(), - networks: self.env.read().networks, - blob_params: self.blob_params(), - cheats: self.cheats().clone(), - }; - - // create a new pending block - let executed = executor.execute(); - f(Box::new(cache_db), executed.block) + for tx in block.transactions.hashes() { + let receipt = storage.transactions.get(&tx)?.receipt.clone(); + receipts.push(receipt); + } + Some(receipts) } +} +// Mining methods — generic over N: Network, with Foundry-associated-type bounds for now. +impl Backend +where + Self: TransactionValidator, + N: Network, +{ /// Mines a new block and stores it. /// /// this will execute all transaction in the order they come in and return all the markers they /// provide. pub async fn mine_block( &self, - pool_transactions: Vec>, - ) -> MinedBlockOutcome { + pool_transactions: Vec>>, + ) -> MinedBlockOutcome { self.do_mine_block(pool_transactions).await } async fn do_mine_block( &self, - pool_transactions: Vec>, - ) -> MinedBlockOutcome { + pool_transactions: Vec>>, + ) -> MinedBlockOutcome { let _mining_guard = self.mining.lock().await; trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len()); @@ -2364,7 +2186,7 @@ impl Backend { self.states.write().insert(best_hash, db); } - let (executed_tx, block_hash) = { + let (block_info, included, invalid, block_hash) = { let mut db = self.db.write().await; // finally set the next block timestamp, this is done just before execution, because @@ -2372,38 +2194,283 @@ impl Backend { // to ensure the timestamp is as close as possible to the actual execution. env.evm_env.block_env.timestamp = U256::from(self.time.next_timestamp()); - let executor = TransactionExecutor { - db: &mut **db, - validator: self, - pending: pool_transactions.into_iter(), - evm_env: env.evm_env.clone(), - parent_hash: best_hash, - gas_used: 0, - blob_gas_used: 0, - enable_steps_tracing: self.enable_steps_tracing, - print_logs: self.print_logs, + let spec_id = *env.evm_env.spec_id(); + let is_shanghai = spec_id >= SpecId::SHANGHAI; + let is_cancun = spec_id >= SpecId::CANCUN; + let is_prague = spec_id >= SpecId::PRAGUE; + let gas_limit = env.evm_env.block_env.gas_limit; + let difficulty = env.evm_env.block_env.difficulty; + let mix_hash = env.evm_env.block_env.prevrandao; + let beneficiary = env.evm_env.block_env.beneficiary; + let timestamp = env.evm_env.block_env.timestamp; + let base_fee = if spec_id >= SpecId::LONDON { + Some(env.evm_env.block_env.basefee) + } else { + None + }; + let excess_blob_gas = + if is_cancun { env.evm_env.block_env.blob_excess_gas() } else { None }; + + // 1. Build inspector (per-block, NOT per-tx) + let mut inspector = AnvilInspector::default().with_tracing(); + if self.enable_steps_tracing { + inspector = inspector.with_steps_tracing(); + } + if self.print_logs { + inspector = inspector.with_log_collector(); + } + if self.print_traces { + inspector = inspector.with_trace_printer(); + } + + // 2. Create EVM + let mut evm = new_eth_evm_with_inspector( + &mut **db, + &env.evm_env, + inspector, + env.networks.is_optimism(), + ); + + // 3. Inject precompiles (once, before the tx loop) + env.networks.inject_precompiles(evm.precompiles_mut()); + if let Some(factory) = &self.precompile_factory { + evm.precompiles_mut().extend_precompiles(factory.precompiles()); + } + let cheats = self.cheats().clone(); + if cheats.has_recover_overrides() { + let cheats_arc = Arc::new(cheats.clone()); + let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats_arc)); + evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| { + Some(DynPrecompile::new_stateful( + cheat_ecrecover.precompile_id().clone(), + move |input| cheat_ecrecover.call(input), + )) + }); + } + + // 4. Create executor via AnvilBlockExecutorFactory + let exec_ctx = AnvilExecutionCtx { parent_hash: best_hash, is_prague, is_cancun }; + let mut executor = AnvilBlockExecutorFactory::create_executor(evm, exec_ctx); + executor.apply_pre_execution_changes().expect("pre-execution changes failed"); + + // 5. Per-tx loop + let mut included: Vec>> = Vec::new(); + let mut invalid: Vec>> = Vec::new(); + let mut transaction_infos: Vec = Vec::new(); + let mut transactions = Vec::new(); + let mut bloom = Bloom::default(); + + let blob_params = self.blob_params(); + let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None }; + + let inspector_tx_config = InspectorTxConfig { print_traces: self.print_traces, + print_logs: self.print_logs, + enable_steps_tracing: self.enable_steps_tracing, call_trace_decoder: self.call_trace_decoder.clone(), - networks: self.env.read().networks, - precompile_factory: self.precompile_factory.clone(), - blob_params: self.blob_params(), - cheats: self.cheats().clone(), }; - 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_slow(); - db.insert_block_hash( - U256::from(executed_tx.block.block.header.number()), - block_hash, - ); + for pool_tx in pool_transactions { + let pending = &pool_tx.pending_transaction; + let sender = *pending.sender(); + + let account = match executor + .evm_mut() + .db_mut() + .basic(sender) + .map(|a| a.unwrap_or_default()) + { + Ok(acc) => acc, + Err(err) => { + trace!(target: "backend", ?err, "db error for tx {:?}, skipping", pool_tx.hash()); + continue; + } + }; + + // Build the per-tx env + let tx_env = build_tx_env_for_pending(pending, &cheats, self.is_optimism()); + + // Gas limit checks (same logic as TransactionExecutor::next) + let cumulative_gas = + executor.receipts().last().map(|r| r.cumulative_gas_used()).unwrap_or(0); + let max_block_gas = + cumulative_gas.saturating_add(pending.transaction.gas_limit()); + if !env.evm_env.cfg_env.disable_block_gas_limit && max_block_gas > gas_limit { + trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "block gas limit exhausting, skipping transaction"); + continue; + } + + // Osaka EIP-7825 tx gas limit cap check + if env.evm_env.cfg_env.tx_gas_limit_cap.is_none() + && pending.transaction.gas_limit() > env.evm_env.cfg_env.tx_gas_limit_cap() + { + trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "transaction gas limit exhausting, skipping transaction"); + continue; + } + + // Blob gas check + let tx_blob_gas = pending.transaction.blob_gas_used().unwrap_or(0); + let current_blob_gas = cumulative_blob_gas_used.unwrap_or(0); + if current_blob_gas.saturating_add(tx_blob_gas) + > blob_params.max_blob_gas_per_block() + { + trace!(target: "backend", blob_gas = %tx_blob_gas, ?pool_tx, "block blob gas limit exhausting, skipping transaction"); + continue; + } + + // Validate + if let Err(err) = + self.validate_pool_transaction_for(pending, &account, &env.evm_env) + { + warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", pool_tx.hash(), err); + invalid.push(pool_tx.clone()); + continue; + } + + let nonce = account.nonce; + + // Execute via block executor + let recovered = + Recovered::new_unchecked(pending.transaction.as_ref().clone(), sender); + trace!(target: "backend", "[{:?}] executing", pool_tx.hash()); + match executor.execute_transaction_without_commit((tx_env, recovered)) { + Ok(result) => { + let exec_result = result.inner.result.result.clone(); + let gas_used = result.inner.result.result.gas_used(); + + executor.commit_transaction(result).expect("commit failed"); + + let traces = executor + .evm_mut() + .inspector_mut() + .finish_transaction(&inspector_tx_config); + + // Track blob gas + if is_cancun { + cumulative_blob_gas_used = Some( + cumulative_blob_gas_used + .unwrap_or(0) + .saturating_add(tx_blob_gas), + ); + } + + let (exit_reason, out, logs) = match exec_result { + ExecutionResult::Success { + reason, + gas_used: _, + logs, + output, + .. + } => (reason.into(), Some(output), logs), + ExecutionResult::Revert { gas_used: _, output } => ( + InstructionResult::Revert, + Some(Output::Call(output)), + Vec::new(), + ), + ExecutionResult::Halt { reason, gas_used: _ } => { + (op_haltreason_to_instruction_result(reason), None, Vec::new()) + } + }; + + if exit_reason == InstructionResult::OutOfGas { + warn!(target: "backend", "[{:?}] executed with out of gas", pool_tx.hash()); + } + + trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", pool_tx.hash(), out); + trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", pool_tx.hash(), exit_reason, gas_used); + + // Build bloom from logs + for log in &logs { + bloom.accrue(BloomInput::Raw(&log.address[..])); + for topic in log.topics() { + bloom.accrue(BloomInput::Raw(&topic[..])); + } + } + + // Contract address for creation txs + let contract_address = if pending.transaction.to().is_none() { + let addr = sender.create(nonce); + trace!(target: "backend", "Contract creation tx: computed address {:?}", addr); + Some(addr) + } else { + None + }; + + let transaction_index = transaction_infos.len() as u64; + let info = TransactionInfo { + transaction_hash: pool_tx.hash(), + transaction_index, + from: sender, + to: pending.transaction.to(), + contract_address, + traces, + exit: exit_reason, + out: out.map(Output::into_data), + nonce, + gas_used, + }; + + included.push(pool_tx.clone()); + transaction_infos.push(info); + transactions.push(pending.transaction.clone()); + } + Err(err) => { + trace!(target: "backend", ?err, "tx execution error, skipping {:?}", pool_tx.hash()); + } + } + } + + // 6. Finish — drop EVM BEFORE accessing db again + let (evm, block_result) = executor.finish().expect("executor finish failed"); + drop(evm); + + let state_root = db.maybe_state_root().unwrap_or_default(); + + // 7. Build block header + let receipts_root = calculate_receipt_root(&block_result.receipts); + + let cumulative_gas_used = block_result.gas_used; + + let header = Header { + parent_hash: best_hash, + ommers_hash: Default::default(), + beneficiary, + state_root, + transactions_root: Default::default(), + receipts_root, + logs_bloom: bloom, + difficulty, + number: block_number, + gas_limit, + gas_used: cumulative_gas_used, + timestamp: timestamp.saturating_to(), + extra_data: Default::default(), + mix_hash: mix_hash.unwrap_or_default(), + nonce: Default::default(), + base_fee_per_gas: base_fee, + parent_beacon_block_root: is_cancun.then_some(Default::default()), + blob_gas_used: cumulative_blob_gas_used, + excess_blob_gas, + withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), + requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), + }; + + let block = create_block(header, transactions); + let block_info = TypedBlockInfo { + block, + transactions: transaction_infos, + receipts: block_result.receipts, + }; + + // update the new blockhash in the db itself + let block_hash = block_info.block.header.hash_slow(); + db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash); - (executed_tx, block_hash) + (block_info, included, invalid, block_hash) }; // create the new block with the current timestamp - let ExecutedTransactions { block, included, invalid } = executed_tx; - let BlockInfo { block, transactions, receipts } = block; + let BlockInfo { block, transactions, receipts } = block_info; let header = block.header.clone(); @@ -2504,16 +2571,291 @@ impl Backend { outcome } - /// Executes the [TransactionRequest] without writing to the DB + /// Reorg the chain to a common height and execute blocks to build new chain. /// - /// # Errors + /// The state of the chain is rewound using `rewind` to the common block, including the db, + /// storage, and env. /// - /// Returns an error if the `block_number` is greater than the current height - pub async fn call( + /// Finally, `do_mine_block` is called to create the new chain. + pub async fn reorg( &self, - request: WithOtherFields, + depth: u64, + tx_pairs: HashMap>>>, + common_block: Block, + ) -> Result<(), BlockchainError> { + self.rollback(common_block).await?; + // Create the new reorged chain, filling the blocks with transactions if supplied + for i in 0..depth { + let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new); + let outcome = self.do_mine_block(to_be_mined).await; + node_info!( + " Mined reorg block number {}. With {} valid txs and with invalid {} txs", + outcome.block_number, + outcome.included.len(), + outcome.invalid.len() + ); + } + + Ok(()) + } + + /// Creates the pending block + /// + /// This will execute all transaction in the order they come but will not mine the block + pub async fn pending_block( + &self, + pool_transactions: Vec>>, + ) -> BlockInfo { + self.with_pending_block(pool_transactions, |_, block| block).await + } + + /// Creates the pending block + /// + /// This will execute all transaction in the order they come but will not mine the block + pub async fn with_pending_block( + &self, + pool_transactions: Vec>>, + f: F, + ) -> T + where + F: FnOnce(Box, BlockInfo) -> T, + { + let db = self.db.read().await; + let evm_env = self.next_env().evm_env; + + let mut cache_db = AnvilCacheDB::new(&*db); + + let parent_hash = self.blockchain.storage.read().best_hash; + + let spec_id = *evm_env.spec_id(); + let is_shanghai = spec_id >= SpecId::SHANGHAI; + let is_cancun = spec_id >= SpecId::CANCUN; + let is_prague = spec_id >= SpecId::PRAGUE; + let gas_limit = evm_env.block_env.gas_limit; + let difficulty = evm_env.block_env.difficulty; + let mix_hash = evm_env.block_env.prevrandao; + let beneficiary = evm_env.block_env.beneficiary; + let timestamp = evm_env.block_env.timestamp; + let base_fee = + if spec_id >= SpecId::LONDON { Some(evm_env.block_env.basefee) } else { None }; + let excess_blob_gas = if is_cancun { evm_env.block_env.blob_excess_gas() } else { None }; + + let mut inspector = AnvilInspector::default().with_tracing(); + if self.enable_steps_tracing { + inspector = inspector.with_steps_tracing(); + } + if self.print_logs { + inspector = inspector.with_log_collector(); + } + if self.print_traces { + inspector = inspector.with_trace_printer(); + } + + let mut evm = + new_eth_evm_with_inspector(&mut cache_db, &evm_env, inspector, self.is_optimism()); + + self.env.read().networks.inject_precompiles(evm.precompiles_mut()); + if let Some(factory) = &self.precompile_factory { + evm.precompiles_mut().extend_precompiles(factory.precompiles()); + } + let cheats = self.cheats().clone(); + if cheats.has_recover_overrides() { + let cheats_arc = Arc::new(cheats.clone()); + let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats_arc)); + evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| { + Some(DynPrecompile::new_stateful( + cheat_ecrecover.precompile_id().clone(), + move |input| cheat_ecrecover.call(input), + )) + }); + } + + let exec_ctx = AnvilExecutionCtx { parent_hash, is_prague, is_cancun }; + let mut executor = AnvilBlockExecutorFactory::create_executor(evm, exec_ctx); + executor.apply_pre_execution_changes().expect("pre-execution changes failed"); + + let mut transaction_infos: Vec = Vec::new(); + let mut transactions = Vec::new(); + let mut bloom = Bloom::default(); + + let blob_params = self.blob_params(); + let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None }; + + let inspector_tx_config = InspectorTxConfig { + print_traces: self.print_traces, + print_logs: self.print_logs, + enable_steps_tracing: self.enable_steps_tracing, + call_trace_decoder: self.call_trace_decoder.clone(), + }; + + for pool_tx in pool_transactions { + let pending = &pool_tx.pending_transaction; + let sender = *pending.sender(); + + let account = match executor + .evm_mut() + .db_mut() + .basic(sender) + .map(|a| a.unwrap_or_default()) + { + Ok(acc) => acc, + Err(err) => { + trace!(target: "backend", ?err, "db error for tx {:?}, skipping", pool_tx.hash()); + continue; + } + }; + + let tx_env = build_tx_env_for_pending(pending, &cheats, self.is_optimism()); + + let cumulative_gas = + executor.receipts().last().map(|r| r.cumulative_gas_used()).unwrap_or(0); + let max_block_gas = cumulative_gas.saturating_add(pending.transaction.gas_limit()); + if !evm_env.cfg_env.disable_block_gas_limit && max_block_gas > gas_limit { + trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "block gas limit exhausting, skipping transaction"); + continue; + } + + if evm_env.cfg_env.tx_gas_limit_cap.is_none() + && pending.transaction.gas_limit() > evm_env.cfg_env.tx_gas_limit_cap() + { + trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "transaction gas limit exhausting, skipping transaction"); + continue; + } + + let tx_blob_gas = pending.transaction.blob_gas_used().unwrap_or(0); + let current_blob_gas = cumulative_blob_gas_used.unwrap_or(0); + if current_blob_gas.saturating_add(tx_blob_gas) > blob_params.max_blob_gas_per_block() { + trace!(target: "backend", blob_gas = %tx_blob_gas, ?pool_tx, "block blob gas limit exhausting, skipping transaction"); + continue; + } + + if let Err(err) = self.validate_pool_transaction_for(pending, &account, &evm_env) { + warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", pool_tx.hash(), err); + continue; + } + + let nonce = account.nonce; + + let recovered = Recovered::new_unchecked(pending.transaction.as_ref().clone(), sender); + trace!(target: "backend", "[{:?}] executing", pool_tx.hash()); + match executor.execute_transaction_without_commit((tx_env, recovered)) { + Ok(result) => { + let exec_result = result.inner.result.result.clone(); + let gas_used = result.inner.result.result.gas_used(); + + executor.commit_transaction(result).expect("commit failed"); + + let traces = + executor.evm_mut().inspector_mut().finish_transaction(&inspector_tx_config); + + if is_cancun { + cumulative_blob_gas_used = + Some(cumulative_blob_gas_used.unwrap_or(0).saturating_add(tx_blob_gas)); + } + + let (exit_reason, out, logs) = match exec_result { + ExecutionResult::Success { reason, gas_used: _, logs, output, .. } => { + (reason.into(), Some(output), logs) + } + ExecutionResult::Revert { gas_used: _, output } => { + (InstructionResult::Revert, Some(Output::Call(output)), Vec::new()) + } + ExecutionResult::Halt { reason, gas_used: _ } => { + (op_haltreason_to_instruction_result(reason), None, Vec::new()) + } + }; + + for log in &logs { + bloom.accrue(BloomInput::Raw(&log.address[..])); + for topic in log.topics() { + bloom.accrue(BloomInput::Raw(&topic[..])); + } + } + + let contract_address = if pending.transaction.to().is_none() { + let addr = sender.create(nonce); + Some(addr) + } else { + None + }; + + let transaction_index = transaction_infos.len() as u64; + let info = TransactionInfo { + transaction_hash: pool_tx.hash(), + transaction_index, + from: sender, + to: pending.transaction.to(), + contract_address, + traces, + exit: exit_reason, + out: out.map(Output::into_data), + nonce, + gas_used, + }; + + transaction_infos.push(info); + transactions.push(pending.transaction.clone()); + } + Err(err) => { + trace!(target: "backend", ?err, "tx execution error, skipping {:?}", pool_tx.hash()); + } + } + } + + let (evm, block_result) = executor.finish().expect("executor finish failed"); + drop(evm); + + // Extract inner CacheDB (which implements MaybeFullDatabase) + let cache_db = cache_db.0; + + let state_root = cache_db.maybe_state_root().unwrap_or_default(); + let receipts_root = calculate_receipt_root(&block_result.receipts); + let cumulative_gas_used = block_result.gas_used; + + let header = Header { + parent_hash, + ommers_hash: Default::default(), + beneficiary, + state_root, + transactions_root: Default::default(), + receipts_root, + logs_bloom: bloom, + difficulty, + number: evm_env.block_env.number.saturating_to(), + gas_limit, + gas_used: cumulative_gas_used, + timestamp: timestamp.saturating_to(), + extra_data: Default::default(), + mix_hash: mix_hash.unwrap_or_default(), + nonce: Default::default(), + base_fee_per_gas: base_fee, + parent_beacon_block_root: is_cancun.then_some(Default::default()), + blob_gas_used: cumulative_blob_gas_used, + excess_blob_gas, + withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), + requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), + }; + + let block = create_block(header, transactions); + let block_info = TypedBlockInfo { + block, + transactions: transaction_infos, + receipts: block_result.receipts, + }; + + f(Box::new(cache_db), block_info) + } + + /// Executes the [TransactionRequest] without writing to the DB + /// + /// # Errors + /// + /// Returns an error if the `block_number` is greater than the current height + pub async fn call( + &self, + request: WithOtherFields, fee_details: FeeDetails, - block_request: Option, + block_request: Option>, overrides: EvmOverrides, ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { self.with_database_at(block_request, |state, mut block| { @@ -2533,293 +2875,85 @@ impl Backend { }).await? } - /// Simulates the payload by executing the calls in request. - pub async fn simulate( + pub async fn call_with_tracing( &self, - request: SimulatePayload, - block_request: Option, - ) -> Result>, BlockchainError> { - self.with_database_at(block_request, |state, mut block_env| { - let SimulatePayload { - block_state_calls, - trace_transfers, - validation, - return_full_transactions, - } = request; - let mut cache_db = CacheDB::new(state); - let mut block_res = Vec::with_capacity(block_state_calls.len()); - - // execute the blocks - for block in block_state_calls { - let SimBlock { block_overrides, state_overrides, calls } = block; - let mut call_res = Vec::with_capacity(calls.len()); - let mut log_index = 0; - let mut gas_used = 0; - let mut transactions = Vec::with_capacity(calls.len()); - let mut logs= Vec::new(); - - // apply state overrides before executing the transactions - if let Some(state_overrides) = state_overrides { - apply_state_overrides(state_overrides, &mut cache_db)?; - } - if let Some(block_overrides) = block_overrides { - cache_db.apply_block_overrides(block_overrides, &mut block_env); - } + request: WithOtherFields, + fee_details: FeeDetails, + block_request: Option>, + opts: GethDebugTracingCallOptions, + ) -> Result { + let GethDebugTracingCallOptions { + tracing_options, block_overrides, state_overrides, .. + } = opts; + let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options; - // execute all calls in that block - for (req_idx, request) in calls.into_iter().enumerate() { - let fee_details = FeeDetails::new( - request.gas_price, - request.max_fee_per_gas, - request.max_priority_fee_per_gas, - request.max_fee_per_blob_gas, - )? - .or_zero_fees(); + self.with_database_at(block_request, |state, mut block| { + let block_number = block.number; - let mut env = self.build_call_env( - WithOtherFields::new(request.clone()), - fee_details, - block_env.clone(), - ); + let mut cache_db = CacheDB::new(state); + if let Some(state_overrides) = state_overrides { + apply_state_overrides(state_overrides, &mut cache_db)?; + } + if let Some(block_overrides) = block_overrides { + cache_db.apply_block_overrides(block_overrides, &mut block); + } - // Always disable EIP-3607 - env.evm_env.cfg_env.disable_eip3607 = true; + if let Some(tracer) = tracer { + return match tracer { + GethDebugTracerType::BuiltInTracer(tracer) => match tracer { + GethDebugBuiltInTracerType::CallTracer => { + let call_config = tracer_config + .into_call_config() + .map_err(|e| RpcError::invalid_params(e.to_string()))?; - if !validation { - env.evm_env.cfg_env.disable_base_fee = !validation; - env.evm_env.block_env.basefee = 0; - } + let mut inspector = self.build_inspector().with_tracing_config( + TracingInspectorConfig::from_geth_call_config(&call_config), + ); - let mut inspector = self.build_inspector(); + let (evm_env, tx_env) = + self.build_call_env(request, fee_details, block); + let mut evm = self.new_eth_evm_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + ); + let ResultAndState { result, state: _ } = evm.transact(tx_env)?; - // transact - let ResultAndState { result, state } = if trace_transfers { - // prepare inspector to capture transfer inside the evm so they are - // recorded and included in logs - inspector = inspector.with_transfers(); - let mut evm= self.new_evm_with_inspector_ref( - &cache_db, - &env, - &mut inspector, - ); + drop(evm); - trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env"); - evm.transact(env.tx)? - } else { - let mut evm = self.new_evm_with_inspector_ref( - &cache_db, - &env, - &mut inspector, - ); - trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env"); - evm.transact(env.tx)? - }; - trace!(target: "backend", ?result, ?request, "simulate call"); + inspector.print_logs(); + if self.print_traces { + inspector.print_traces(self.call_trace_decoder.clone()); + } - inspector.print_logs(); - if self.print_traces { - inspector.into_print_traces(self.call_trace_decoder.clone()); - } + let tracing_inspector = inspector.tracer.expect("tracer disappeared"); - // commit the transaction - cache_db.commit(state); - gas_used += result.gas_used(); + Ok(tracing_inspector + .into_geth_builder() + .geth_call_traces(call_config, result.gas_used()) + .into()) + } + GethDebugBuiltInTracerType::PreStateTracer => { + let pre_state_config = tracer_config + .into_pre_state_config() + .map_err(|e| RpcError::invalid_params(e.to_string()))?; - // create the transaction from a request - let from = request.from.unwrap_or_default(); + let mut inspector = TracingInspector::new( + TracingInspectorConfig::from_geth_prestate_config( + &pre_state_config, + ), + ); - let mut request = Into::::into(WithOtherFields::new(request)); - request.prep_for_submission(); + let (evm_env, tx_env) = + self.build_call_env(request, fee_details, block); + let mut evm = self.new_eth_evm_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + ); + let result = evm.transact(tx_env)?; - let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?; - - let tx = build_impersonated(typed_tx); - let tx_hash = tx.hash(); - let rpc_tx = transaction_build( - None, - MaybeImpersonatedTransaction::impersonated(tx, from), - None, - None, - Some(block_env.basefee), - ); - transactions.push(rpc_tx); - - let return_data = result.output().cloned().unwrap_or_default(); - let sim_res = SimCallResult { - return_data, - gas_used: result.gas_used(), - status: result.is_success(), - error: result.is_success().not().then(|| { - alloy_rpc_types::simulate::SimulateError { - code: -3200, - message: "execution failed".to_string(), - data: None, - } - }), - logs: result.clone() - .into_logs() - .into_iter() - .enumerate() - .map(|(idx, log)| Log { - inner: log, - block_number: Some(block_env.number.saturating_to()), - block_timestamp: Some(block_env.timestamp.saturating_to()), - transaction_index: Some(req_idx as u64), - log_index: Some((idx + log_index) as u64), - removed: false, - - block_hash: None, - transaction_hash: Some(tx_hash), - }) - .collect(), - }; - logs.extend(sim_res.logs.iter().map(|log| log.inner.clone())); - log_index += sim_res.logs.len(); - call_res.push(sim_res); - } - - let transactions_envelopes: Vec = transactions - .iter() - .map(|tx| AnyTxEnvelope::from(tx.clone())) - .collect(); - let header = Header { - logs_bloom: logs_bloom(logs.iter()), - transactions_root: calculate_transaction_root(&transactions_envelopes), - receipts_root: calculate_receipt_root(&transactions_envelopes), - parent_hash: Default::default(), - beneficiary: block_env.beneficiary, - state_root: Default::default(), - difficulty: Default::default(), - number: block_env.number.saturating_to(), - gas_limit: block_env.gas_limit, - gas_used, - timestamp: block_env.timestamp.saturating_to(), - extra_data: Default::default(), - mix_hash: Default::default(), - nonce: Default::default(), - base_fee_per_gas: Some(block_env.basefee), - withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - requests_hash: None, - ..Default::default() - }; - let mut block = alloy_rpc_types::Block { - header: AnyRpcHeader { - hash: header.hash_slow(), - inner: header.into(), - total_difficulty: None, - size: None, - }, - uncles: vec![], - transactions: BlockTransactions::Full(transactions), - withdrawals: None, - }; - - if !return_full_transactions { - block.transactions.convert_to_hashes(); - } - - for res in &mut call_res { - res.logs.iter_mut().for_each(|log| { - log.block_hash = Some(block.header.hash); - }); - } - - let simulated_block = SimulatedBlock { - inner: AnyRpcBlock::new(WithOtherFields::new(block)), - calls: call_res, - }; - - // update block env - block_env.number += U256::from(1); - block_env.timestamp += U256::from(12); - block_env.basefee = simulated_block - .inner - .header - .next_block_base_fee(self.fees.base_fee_params()) - .unwrap_or_default(); - - block_res.push(simulated_block); - } - - Ok(block_res) - }) - .await? - } - - pub async fn call_with_tracing( - &self, - request: WithOtherFields, - fee_details: FeeDetails, - block_request: Option, - opts: GethDebugTracingCallOptions, - ) -> Result { - let GethDebugTracingCallOptions { - tracing_options, block_overrides, state_overrides, .. - } = opts; - let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options; - - self.with_database_at(block_request, |state, mut block| { - let block_number = block.number; - - let mut cache_db = CacheDB::new(state); - if let Some(state_overrides) = state_overrides { - apply_state_overrides(state_overrides, &mut cache_db)?; - } - if let Some(block_overrides) = block_overrides { - cache_db.apply_block_overrides(block_overrides, &mut block); - } - - if let Some(tracer) = tracer { - return match tracer { - GethDebugTracerType::BuiltInTracer(tracer) => match tracer { - GethDebugBuiltInTracerType::CallTracer => { - let call_config = tracer_config - .into_call_config() - .map_err(|e| RpcError::invalid_params(e.to_string()))?; - - let mut inspector = self.build_inspector().with_tracing_config( - TracingInspectorConfig::from_geth_call_config(&call_config), - ); - - let env = self.build_call_env(request, fee_details, block); - let mut evm = - self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); - let ResultAndState { result, state: _ } = evm.transact(env.tx)?; - - drop(evm); - - inspector.print_logs(); - if self.print_traces { - inspector.print_traces(self.call_trace_decoder.clone()); - } - - let tracing_inspector = inspector.tracer.expect("tracer disappeared"); - - Ok(tracing_inspector - .into_geth_builder() - .geth_call_traces(call_config, result.gas_used()) - .into()) - } - GethDebugBuiltInTracerType::PreStateTracer => { - let pre_state_config = tracer_config - .into_pre_state_config() - .map_err(|e| RpcError::invalid_params(e.to_string()))?; - - let mut inspector = TracingInspector::new( - TracingInspectorConfig::from_geth_prestate_config( - &pre_state_config, - ), - ); - - let env = self.build_call_env(request, fee_details, block); - let mut evm = - self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); - let result = evm.transact(env.tx)?; - - drop(evm); + drop(evm); Ok(inspector .into_geth_builder() @@ -2846,13 +2980,17 @@ impl Backend { revm_inspectors::tracing::js::JsInspector::new(code, config) .map_err(|err| BlockchainError::Message(err.to_string()))?; - let env = self.build_call_env(request, fee_details, block.clone()); - let mut evm = - self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); - let result = evm.transact(env.tx.clone())?; + let (evm_env, tx_env) = + self.build_call_env(request, fee_details, block.clone()); + let mut evm = self.new_eth_evm_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + ); + let result = evm.transact(tx_env.clone())?; let res = evm .inspector_mut() - .json_result(result, &env.tx.into_tx_env(), &block, &cache_db) + .json_result(result, &tx_env.into_tx_env(), &block, &cache_db) .map_err(|err| BlockchainError::Message(err.to_string()))?; Ok(GethTrace::JS(res)) @@ -2865,9 +3003,9 @@ impl Backend { .build_inspector() .with_tracing_config(TracingInspectorConfig::from_geth_config(&config)); - let env = self.build_call_env(request, fee_details, block); - let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); - let ResultAndState { result, state: _ } = evm.transact(env.tx)?; + let (evm_env, tx_env) = self.build_call_env(request, fee_details, block); + let mut evm = self.new_eth_evm_with_inspector_ref(&cache_db, &evm_env, &mut inspector); + let ResultAndState { result, state: _ } = evm.transact(tx_env)?; let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { @@ -2897,27 +3035,10 @@ impl Backend { .await? } - /// returns all receipts for the given transactions - fn get_receipts( - &self, - tx_hashes: impl IntoIterator, - ) -> Vec { - let storage = self.blockchain.storage.read(); - let mut receipts = vec![]; - - for hash in tx_hashes { - if let Some(tx) = storage.transactions.get(&hash) { - receipts.push(tx.receipt.clone()); - } - } - - receipts - } - /// Helper function to execute a closure with the database at a specific block pub async fn with_database_at( &self, - block_request: Option, + block_request: Option>, f: F, ) -> Result where @@ -2974,7 +3095,7 @@ impl Backend { &self, address: Address, index: U256, - block_request: Option, + block_request: Option>, ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get storage for {:?} at {:?}", address, index); @@ -2991,7 +3112,7 @@ impl Backend { pub async fn get_code( &self, address: Address, - block_request: Option, + block_request: Option>, ) -> Result { self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await? } @@ -3002,7 +3123,7 @@ impl Backend { pub async fn get_balance( &self, address: Address, - block_request: Option, + block_request: Option>, ) -> Result { self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address)) .await? @@ -3011,7 +3132,7 @@ impl Backend { pub async fn get_account_at_block( &self, address: Address, - block_request: Option, + block_request: Option>, ) -> Result { self.with_database_at(block_request, |block_db, _| { let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; @@ -3031,7 +3152,7 @@ impl Backend { pub async fn get_nonce( &self, address: Address, - block_request: BlockRequest, + block_request: BlockRequest, ) -> Result { if let BlockRequest::Pending(pool_transactions) = &block_request && let Some(value) = get_pool_transactions_nonce(pool_transactions, address) @@ -3050,32 +3171,6 @@ impl Backend { .await? } - /// Returns the traces for the given transaction - pub async fn debug_trace_transaction( - &self, - hash: B256, - opts: GethDebugTracingOptions, - ) -> Result { - #[cfg(feature = "js-tracer")] - if let Some(tracer_type) = opts.tracer.as_ref() - && tracer_type.is_js() - { - return self - .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone()) - .await; - } - - if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await { - return trace; - } - - if let Some(fork) = self.get_fork() { - return Ok(fork.debug_trace_transaction(hash, opts).await?); - } - - Ok(GethTrace::Default(Default::default())) - } - fn replay_tx_with_inspector( &self, hash: B256, @@ -3107,7 +3202,8 @@ impl Backend { .position(|tx| tx.hash() == hash) .expect("transaction not found in block"); - let pool_txs: Vec> = block.body.transactions[..index] + let pool_txs: Vec>> = block.body.transactions + [..index] .iter() .map(|tx| { let pending_tx = @@ -3122,91 +3218,737 @@ impl Backend { .collect(); let trace = |parent_state: &StateDb| -> Result { - let mut cache_db = CacheDB::new(Box::new(parent_state)); + let mut cache_db = AnvilCacheDB::new(Box::new(parent_state)); // configure the blockenv for the block of the transaction let mut env = self.env.read().clone(); env.evm_env.block_env = block_env_from_header(&block.header); - let executor = TransactionExecutor { - db: &mut cache_db, - validator: self, - pending: pool_txs.into_iter(), - evm_env: env.evm_env.clone(), - parent_hash: block.header.parent_hash, - gas_used: 0, - blob_gas_used: 0, - enable_steps_tracing: self.enable_steps_tracing, - print_logs: self.print_logs, + let spec_id = *env.evm_env.spec_id(); + let is_cancun = spec_id >= SpecId::CANCUN; + let is_prague = spec_id >= SpecId::PRAGUE; + let gas_limit = env.evm_env.block_env.gas_limit; + + let mut inspector_replay = AnvilInspector::default().with_tracing(); + if self.enable_steps_tracing { + inspector_replay = inspector_replay.with_steps_tracing(); + } + if self.print_logs { + inspector_replay = inspector_replay.with_log_collector(); + } + if self.print_traces { + inspector_replay = inspector_replay.with_trace_printer(); + } + + let mut evm_replay = new_eth_evm_with_inspector( + &mut cache_db, + &env.evm_env, + inspector_replay, + env.networks.is_optimism(), + ); + + env.networks.inject_precompiles(evm_replay.precompiles_mut()); + if let Some(factory) = &self.precompile_factory { + evm_replay.precompiles_mut().extend_precompiles(factory.precompiles()); + } + let cheats = self.cheats().clone(); + if cheats.has_recover_overrides() { + let cheats_arc = Arc::new(cheats.clone()); + let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats_arc)); + evm_replay.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| { + Some(DynPrecompile::new_stateful( + cheat_ecrecover.precompile_id().clone(), + move |input| cheat_ecrecover.call(input), + )) + }); + } + + let exec_ctx = + AnvilExecutionCtx { parent_hash: block.header.parent_hash, is_prague, is_cancun }; + let mut replay_executor = + AnvilBlockExecutorFactory::create_executor(evm_replay, exec_ctx); + replay_executor.apply_pre_execution_changes().expect("pre-execution changes failed"); + + let blob_params = self.blob_params(); + let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None }; + + let inspector_tx_config = InspectorTxConfig { print_traces: self.print_traces, + print_logs: self.print_logs, + enable_steps_tracing: self.enable_steps_tracing, call_trace_decoder: self.call_trace_decoder.clone(), - precompile_factory: self.precompile_factory.clone(), - networks: self.env.read().networks, - blob_params: self.blob_params(), - cheats: self.cheats().clone(), }; - let _ = executor.execute(); + for pool_tx in pool_txs { + let pending = &pool_tx.pending_transaction; + let sender = *pending.sender(); - let target_tx = block.body.transactions[index].clone(); - let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?; - let mut tx_env: OpTransaction = FromRecoveredTx::from_recovered_tx( - target_tx.transaction.as_ref(), - *target_tx.sender(), - ); - if env.networks.is_optimism() { - tx_env.enveloped_tx = Some(target_tx.transaction.encoded_2718().into()); - } + let account = match replay_executor + .evm_mut() + .db_mut() + .basic(sender) + .map(|a| a.unwrap_or_default()) + { + Ok(acc) => acc, + Err(_) => continue, + }; + + let tx_env = build_tx_env_for_pending(pending, &cheats, self.is_optimism()); + + let cumulative_gas = + replay_executor.receipts().last().map(|r| r.cumulative_gas_used()).unwrap_or(0); + let max_block_gas = cumulative_gas.saturating_add(pending.transaction.gas_limit()); + if !env.evm_env.cfg_env.disable_block_gas_limit && max_block_gas > gas_limit { + continue; + } + + if env.evm_env.cfg_env.tx_gas_limit_cap.is_none() + && pending.transaction.gas_limit() > env.evm_env.cfg_env.tx_gas_limit_cap() + { + continue; + } + + let tx_blob_gas = pending.transaction.blob_gas_used().unwrap_or(0); + let current_blob_gas = cumulative_blob_gas_used.unwrap_or(0); + if current_blob_gas.saturating_add(tx_blob_gas) + > blob_params.max_blob_gas_per_block() + { + continue; + } + + if self.validate_pool_transaction_for(pending, &account, &env.evm_env).is_err() { + continue; + } + + let recovered = + Recovered::new_unchecked(pending.transaction.as_ref().clone(), sender); + if let Ok(result) = + replay_executor.execute_transaction_without_commit((tx_env, recovered)) + { + replay_executor.commit_transaction(result).expect("commit failed"); + + replay_executor + .evm_mut() + .inspector_mut() + .finish_transaction(&inspector_tx_config); + + if is_cancun { + cumulative_blob_gas_used = + Some(cumulative_blob_gas_used.unwrap_or(0).saturating_add(tx_blob_gas)); + } + } + } + + let (evm_done, _) = replay_executor.finish().expect("executor finish failed"); + drop(evm_done); + + // Extract inner CacheDB to match the expected types for the target tx execution + let cache_db = cache_db.0; + + let target_tx = block.body.transactions[index].clone(); + let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?; + let mut tx_env: OpTransaction = FromRecoveredTx::from_recovered_tx( + target_tx.transaction.as_ref(), + *target_tx.sender(), + ); + if env.networks.is_optimism() { + tx_env.enveloped_tx = Some(target_tx.transaction.encoded_2718().into()); + } + + let mut evm = + self.new_eth_evm_with_inspector_ref(&cache_db, &env.evm_env, &mut inspector); + + let result = evm + .transact(tx_env.clone()) + .map_err(|err| BlockchainError::Message(err.to_string()))?; + + Ok(f(result, cache_db, inspector, tx_env.base, env)) + }; + + let read_guard = self.states.upgradable_read(); + if let Some(state) = read_guard.get_state(&block.header.parent_hash) { + trace(state) + } else { + let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard); + let state = write_guard + .get_on_disk_state(&block.header.parent_hash) + .ok_or(BlockchainError::BlockNotFound)?; + trace(state) + } + } + + /// Traces the transaction with the js tracer + #[cfg(feature = "js-tracer")] + pub async fn trace_tx_with_js_tracer( + &self, + hash: B256, + code: String, + opts: GethDebugTracingOptions, + ) -> Result { + let GethDebugTracingOptions { tracer_config, .. } = opts; + let config = tracer_config.into_json(); + let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config) + .map_err(|err| BlockchainError::Message(err.to_string()))?; + let trace = self.replay_tx_with_inspector( + hash, + inspector, + |result, cache_db, mut inspector, tx_env, env| { + inspector + .json_result( + result, + &alloy_evm::IntoTxEnv::into_tx_env(tx_env), + &env.evm_env.block_env, + &cache_db, + ) + .map_err(|e| BlockchainError::Message(e.to_string())) + }, + )??; + Ok(GethTrace::JS(trace)) + } + + /// Prove an account's existence or nonexistence in the state trie. + /// + /// Returns a merkle proof of the account's trie node, `account_key` == keccak(address) + pub async fn prove_account_at( + &self, + address: Address, + keys: Vec, + block_request: Option>, + ) -> Result { + 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 = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; + let account = db.get(&address).cloned().unwrap_or_default(); + + let mut builder = HashBuilder::default() + .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))])); + + for (key, account) in trie_accounts(db) { + builder.add_leaf(key, &account); + } + + let _ = builder.root(); + + let proof = builder + .take_proof_nodes() + .into_nodes_sorted() + .into_iter() + .map(|(_, v)| v) + .collect(); + let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys); + + let account_proof = AccountProof { + address, + balance: account.info.balance, + nonce: account.info.nonce, + code_hash: account.info.code_hash, + storage_hash, + account_proof: proof, + storage_proof: keys + .into_iter() + .zip(storage_proofs) + .map(|(key, proof)| { + let storage_key: U256 = key.into(); + let value = account.storage.get(&storage_key).copied().unwrap_or_default(); + StorageProof { key: JsonStorageKey::Hash(key), value, proof } + }) + .collect(), + }; + + Ok(account_proof) + }) + .await? + } + + /// Rollback the chain to a common height. + /// + /// The state of the chain is rewound using `rewind` to the common block, including the db, + /// storage, and env. + pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> { + let hash = common_block.header.hash_slow(); + + // Get the database at the common block + let common_state = { + let return_state_or_throw_err = + |db: Option<&StateDb>| -> Result, BlockchainError> { + let state_db = db.ok_or(BlockchainError::DataUnavailable)?; + let db_full = + state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; + Ok(db_full.clone()) + }; + + let read_guard = self.states.upgradable_read(); + if let Some(db) = read_guard.get_state(&hash) { + return_state_or_throw_err(Some(db))? + } else { + let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard); + return_state_or_throw_err(write_guard.get_on_disk_state(&hash))? + } + }; + + { + // Unwind the storage back to the common ancestor first + self.blockchain.storage.write().unwind_to(common_block.header.number(), hash); + + // Set environment back to common block + let mut env = self.env.write(); + env.evm_env.block_env.number = U256::from(common_block.header.number()); + env.evm_env.block_env.timestamp = U256::from(common_block.header.timestamp()); + env.evm_env.block_env.gas_limit = common_block.header.gas_limit(); + env.evm_env.block_env.difficulty = common_block.header.difficulty(); + env.evm_env.block_env.prevrandao = common_block.header.mix_hash(); + + self.time.reset(env.evm_env.block_env.timestamp.saturating_to()); + } + + { + // Collect block hashes before acquiring db lock to avoid holding blockchain storage + // lock across await. Only collect the last 256 blocks since that's all BLOCKHASH can + // access. + let block_hashes: Vec<_> = { + let storage = self.blockchain.storage.read(); + let min_block = common_block.header.number().saturating_sub(256); + storage + .hashes + .iter() + .filter(|(num, _)| **num >= min_block) + .map(|(&num, &hash)| (num, hash)) + .collect() + }; + + // Acquire db lock once for the entire restore operation to reduce lock churn. + let mut db = self.db.write().await; + db.clear(); + + // Insert account info before storage to prevent fork-mode RPC fetches after clear. + for (address, acc) in common_state { + db.insert_account(address, acc.info); + for (key, value) in acc.storage { + db.set_storage_at(address, key.into(), value.into())?; + } + } + + // Restore block hashes from blockchain storage (now unwound, contains only valid + // blocks). + for (block_num, hash) in block_hashes { + db.insert_block_hash(U256::from(block_num), hash); + } + } + + Ok(()) + } +} + +impl Backend { + /// Get the current state. + pub async fn serialized_state( + &self, + preserve_historical_states: bool, + ) -> Result { + let at = self.env.read().evm_env.block_env.clone(); + let best_number = self.blockchain.storage.read().best_number; + let blocks = self.blockchain.storage.read().serialized_blocks(); + let transactions = self.blockchain.storage.read().serialized_transactions(); + let historical_states = if preserve_historical_states { + Some(self.states.write().serialized_states()) + } else { + None + }; + + let state = self.db.read().await.dump_state( + at, + best_number, + blocks, + transactions, + historical_states, + )?; + state.ok_or_else(|| { + RpcError::invalid_params("Dumping state not supported with the current configuration") + .into() + }) + } + + /// Write all chain data to serialized bytes buffer + pub async fn dump_state( + &self, + preserve_historical_states: bool, + ) -> Result { + let state = self.serialized_state(preserve_historical_states).await?; + let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); + encoder + .write_all(&serde_json::to_vec(&state).unwrap_or_default()) + .map_err(|_| BlockchainError::DataUnavailable)?; + Ok(encoder.finish().unwrap_or_default().into()) + } + + /// Apply [SerializableState] data to the backend storage. + pub async fn load_state(&self, state: SerializableState) -> Result { + // load the blocks and transactions into the storage + self.blockchain.storage.write().load_blocks(state.blocks.clone()); + self.blockchain.storage.write().load_transactions(state.transactions.clone()); + // reset the block env + if let Some(block) = state.block.clone() { + self.env.write().evm_env.block_env = block.clone(); + + // Set the current best block number. + // Defaults to block number for compatibility with existing state files. + let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash())); + + let best_number = state.best_block_number.unwrap_or(block.number.saturating_to()); + if let Some((number, hash)) = fork_num_and_hash { + trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number); + // If the state.block_number is greater than the fork block number, set best number + // to the state block number. + // Ref: https://github.com/foundry-rs/foundry/issues/9539 + if best_number > number { + self.blockchain.storage.write().best_number = best_number; + let best_hash = + self.blockchain.storage.read().hash(best_number.into()).ok_or_else( + || { + BlockchainError::RpcError(RpcError::internal_error_with(format!( + "Best hash not found for best number {best_number}", + ))) + }, + )?; + self.blockchain.storage.write().best_hash = best_hash; + } else { + // If loading state file on a fork, set best number to the fork block number. + // Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838 + self.blockchain.storage.write().best_number = number; + self.blockchain.storage.write().best_hash = hash; + } + } else { + self.blockchain.storage.write().best_number = best_number; + + // Set the current best block hash; + let best_hash = + self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| { + BlockchainError::RpcError(RpcError::internal_error_with(format!( + "Best hash not found for best number {best_number}", + ))) + })?; + + self.blockchain.storage.write().best_hash = best_hash; + } + } + + if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number()) { + let header = &latest.header; + let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( + header.gas_used(), + 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, + get_blob_base_fee_update_fraction( + self.env.read().evm_env.cfg_env.chain_id, + header.timestamp, + ), + )); + } + + if !self.db.write().await.load_state(state.clone())? { + return Err(RpcError::invalid_params( + "Loading state not supported with the current configuration", + ) + .into()); + } + + if let Some(historical_states) = state.historical_states { + self.states.write().load_states(historical_states); + } + + Ok(true) + } + + /// Deserialize and add all chain data to the backend storage + pub async fn load_state_bytes(&self, buf: Bytes) -> Result { + let orig_buf = &buf.0[..]; + let mut decoder = GzDecoder::new(orig_buf); + let mut decoded_data = Vec::new(); + + let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() { + decoder + .read_to_end(decoded_data.as_mut()) + .map_err(|_| BlockchainError::FailedToDecodeStateDump)?; + &decoded_data + } else { + &buf.0 + }) + .map_err(|_| BlockchainError::FailedToDecodeStateDump)?; + + self.load_state(state).await + } + + /// Simulates the payload by executing the calls in request. + pub async fn simulate( + &self, + request: SimulatePayload, + block_request: Option>, + ) -> Result>, BlockchainError> { + self.with_database_at(block_request, |state, mut block_env| { + let SimulatePayload { + block_state_calls, + trace_transfers, + validation, + return_full_transactions, + } = request; + let mut cache_db = CacheDB::new(state); + let mut block_res = Vec::with_capacity(block_state_calls.len()); + + // execute the blocks + for block in block_state_calls { + let SimBlock { block_overrides, state_overrides, calls } = block; + let mut call_res = Vec::with_capacity(calls.len()); + let mut log_index = 0; + let mut gas_used = 0; + let mut transactions = Vec::with_capacity(calls.len()); + let mut logs= Vec::new(); + + // apply state overrides before executing the transactions + if let Some(state_overrides) = state_overrides { + apply_state_overrides(state_overrides, &mut cache_db)?; + } + if let Some(block_overrides) = block_overrides { + cache_db.apply_block_overrides(block_overrides, &mut block_env); + } + + // execute all calls in that block + for (req_idx, request) in calls.into_iter().enumerate() { + let fee_details = FeeDetails::new( + request.gas_price, + request.max_fee_per_gas, + request.max_priority_fee_per_gas, + request.max_fee_per_blob_gas, + )? + .or_zero_fees(); + + let (mut evm_env, tx_env) = self.build_call_env( + WithOtherFields::new(request.clone()), + fee_details, + block_env.clone(), + ); + + // Always disable EIP-3607 + evm_env.cfg_env.disable_eip3607 = true; + + if !validation { + evm_env.cfg_env.disable_base_fee = !validation; + evm_env.block_env.basefee = 0; + } + + let mut inspector = self.build_inspector(); + + // transact + let ResultAndState { result, state } = if trace_transfers { + // prepare inspector to capture transfer inside the evm so they are + // recorded and included in logs + inspector = inspector.with_transfers(); + let mut evm= self.new_eth_evm_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + ); + + trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env"); + evm.transact(tx_env)? + } else { + let mut evm = self.new_eth_evm_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + ); + trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env"); + evm.transact(tx_env)? + }; + trace!(target: "backend", ?result, ?request, "simulate call"); + + inspector.print_logs(); + if self.print_traces { + inspector.into_print_traces(self.call_trace_decoder.clone()); + } + + // commit the transaction + cache_db.commit(state); + gas_used += result.gas_used(); + + // create the transaction from a request + let from = request.from.unwrap_or_default(); + + let mut request = Into::::into(WithOtherFields::new(request)); + request.prep_for_submission(); + + let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?; + + let tx = build_impersonated(typed_tx); + let tx_hash = tx.hash(); + let rpc_tx = transaction_build( + None, + MaybeImpersonatedTransaction::impersonated(tx, from), + None, + None, + Some(block_env.basefee), + ); + transactions.push(rpc_tx); + + let return_data = result.output().cloned().unwrap_or_default(); + let sim_res = SimCallResult { + return_data, + gas_used: result.gas_used(), + status: result.is_success(), + error: result.is_success().not().then(|| { + alloy_rpc_types::simulate::SimulateError { + code: -3200, + message: "execution failed".to_string(), + data: None, + } + }), + logs: result.clone() + .into_logs() + .into_iter() + .enumerate() + .map(|(idx, log)| Log { + inner: log, + block_number: Some(block_env.number.saturating_to()), + block_timestamp: Some(block_env.timestamp.saturating_to()), + transaction_index: Some(req_idx as u64), + log_index: Some((idx + log_index) as u64), + removed: false, + + block_hash: None, + transaction_hash: Some(tx_hash), + }) + .collect(), + }; + logs.extend(sim_res.logs.iter().map(|log| log.inner.clone())); + log_index += sim_res.logs.len(); + call_res.push(sim_res); + } + + let transactions_envelopes: Vec = transactions + .iter() + .map(|tx| AnyTxEnvelope::from(tx.clone())) + .collect(); + let header = Header { + logs_bloom: logs_bloom(logs.iter()), + transactions_root: calculate_transaction_root(&transactions_envelopes), + receipts_root: calculate_receipt_root(&transactions_envelopes), + parent_hash: Default::default(), + beneficiary: block_env.beneficiary, + state_root: Default::default(), + difficulty: Default::default(), + number: block_env.number.saturating_to(), + gas_limit: block_env.gas_limit, + gas_used, + timestamp: block_env.timestamp.saturating_to(), + extra_data: Default::default(), + mix_hash: Default::default(), + nonce: Default::default(), + base_fee_per_gas: Some(block_env.basefee), + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + requests_hash: None, + ..Default::default() + }; + let mut block = alloy_rpc_types::Block { + header: AnyRpcHeader { + hash: header.hash_slow(), + inner: header.into(), + total_difficulty: None, + size: None, + }, + uncles: vec![], + transactions: BlockTransactions::Full(transactions), + withdrawals: None, + }; + + if !return_full_transactions { + block.transactions.convert_to_hashes(); + } + + for res in &mut call_res { + res.logs.iter_mut().for_each(|log| { + log.block_hash = Some(block.header.hash); + }); + } + + let simulated_block = SimulatedBlock { + inner: AnyRpcBlock::new(WithOtherFields::new(block)), + calls: call_res, + }; + + // update block env + block_env.number += U256::from(1); + block_env.timestamp += U256::from(12); + block_env.basefee = simulated_block + .inner + .header + .next_block_base_fee(self.fees.base_fee_params()) + .unwrap_or_default(); - let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector); + block_res.push(simulated_block); + } - let result = evm - .transact(tx_env.clone()) - .map_err(|err| BlockchainError::Message(err.to_string()))?; + Ok(block_res) + }) + .await? + } - Ok(f(result, cache_db, inspector, tx_env.base, env)) - }; + /// returns all receipts for the given transactions + fn get_receipts( + &self, + tx_hashes: impl IntoIterator, + ) -> Vec { + let storage = self.blockchain.storage.read(); + let mut receipts = vec![]; - let read_guard = self.states.upgradable_read(); - if let Some(state) = read_guard.get_state(&block.header.parent_hash) { - trace(state) - } else { - let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard); - let state = write_guard - .get_on_disk_state(&block.header.parent_hash) - .ok_or(BlockchainError::BlockNotFound)?; - trace(state) + for hash in tx_hashes { + if let Some(tx) = storage.transactions.get(&hash) { + receipts.push(tx.receipt.clone()); + } } + + receipts } - /// Traces the transaction with the js tracer - #[cfg(feature = "js-tracer")] - pub async fn trace_tx_with_js_tracer( + /// Returns the traces for the given transaction + pub async fn debug_trace_transaction( &self, hash: B256, - code: String, opts: GethDebugTracingOptions, ) -> Result { - let GethDebugTracingOptions { tracer_config, .. } = opts; - let config = tracer_config.into_json(); - let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config) - .map_err(|err| BlockchainError::Message(err.to_string()))?; - let trace = self.replay_tx_with_inspector( - hash, - inspector, - |result, cache_db, mut inspector, tx_env, env| { - inspector - .json_result( - result, - &alloy_evm::IntoTxEnv::into_tx_env(tx_env), - &env.evm_env.block_env, - &cache_db, - ) - .map_err(|e| BlockchainError::Message(e.to_string())) - }, - )??; - Ok(GethTrace::JS(trace)) + #[cfg(feature = "js-tracer")] + if let Some(tracer_type) = opts.tracer.as_ref() + && tracer_type.is_js() + { + return self + .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone()) + .await; + } + + if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await { + return trace; + } + + if let Some(fork) = self.get_fork() { + return Ok(fork.debug_trace_transaction(hash, opts).await?); + } + + Ok(GethTrace::Default(Default::default())) } fn geth_trace( @@ -3327,18 +4069,6 @@ impl Backend { Ok(None) } - /// Returns all receipts of the block - pub fn mined_receipts(&self, hash: B256) -> Option> { - let block = self.mined_block_by_hash(hash)?; - let mut receipts = Vec::new(); - let storage = self.blockchain.storage.read(); - for tx in block.transactions.hashes() { - let receipt = storage.transactions.get(&tx)?.receipt.clone(); - receipts.push(receipt); - } - Some(receipts) - } - /// Returns all transaction receipts of the block pub fn mined_block_receipts(&self, id: impl Into) -> Option> { let mut receipts = Vec::new(); @@ -3439,187 +4169,11 @@ impl Backend { Ok(None) } - - /// Prove an account's existence or nonexistence in the state trie. - /// - /// Returns a merkle proof of the account's trie node, `account_key` == keccak(address) - pub async fn prove_account_at( - &self, - address: Address, - keys: Vec, - block_request: Option, - ) -> Result { - 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 = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; - let account = db.get(&address).cloned().unwrap_or_default(); - - let mut builder = HashBuilder::default() - .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))])); - - for (key, account) in trie_accounts(db) { - builder.add_leaf(key, &account); - } - - let _ = builder.root(); - - let proof = builder - .take_proof_nodes() - .into_nodes_sorted() - .into_iter() - .map(|(_, v)| v) - .collect(); - let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys); - - let account_proof = AccountProof { - address, - balance: account.info.balance, - nonce: account.info.nonce, - code_hash: account.info.code_hash, - storage_hash, - account_proof: proof, - storage_proof: keys - .into_iter() - .zip(storage_proofs) - .map(|(key, proof)| { - let storage_key: U256 = key.into(); - let value = account.storage.get(&storage_key).copied().unwrap_or_default(); - StorageProof { key: JsonStorageKey::Hash(key), value, proof } - }) - .collect(), - }; - - Ok(account_proof) - }) - .await? - } - - /// Reorg the chain to a common height and execute blocks to build new chain. - /// - /// The state of the chain is rewound using `rewind` to the common block, including the db, - /// storage, and env. - /// - /// Finally, `do_mine_block` is called to create the new chain. - pub async fn reorg( - &self, - depth: u64, - tx_pairs: HashMap>>, - common_block: Block, - ) -> Result<(), BlockchainError> { - self.rollback(common_block).await?; - // Create the new reorged chain, filling the blocks with transactions if supplied - for i in 0..depth { - let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new); - let outcome = self.do_mine_block(to_be_mined).await; - node_info!( - " Mined reorg block number {}. With {} valid txs and with invalid {} txs", - outcome.block_number, - outcome.included.len(), - outcome.invalid.len() - ); - } - - Ok(()) - } - - /// Rollback the chain to a common height. - /// - /// The state of the chain is rewound using `rewind` to the common block, including the db, - /// storage, and env. - pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> { - let hash = common_block.header.hash_slow(); - - // Get the database at the common block - let common_state = { - let return_state_or_throw_err = - |db: Option<&StateDb>| -> Result, BlockchainError> { - let state_db = db.ok_or(BlockchainError::DataUnavailable)?; - let db_full = - state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; - Ok(db_full.clone()) - }; - - let read_guard = self.states.upgradable_read(); - if let Some(db) = read_guard.get_state(&hash) { - return_state_or_throw_err(Some(db))? - } else { - let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard); - return_state_or_throw_err(write_guard.get_on_disk_state(&hash))? - } - }; - - { - // Unwind the storage back to the common ancestor first - self.blockchain.storage.write().unwind_to(common_block.header.number(), hash); - - // Set environment back to common block - let mut env = self.env.write(); - env.evm_env.block_env.number = U256::from(common_block.header.number()); - env.evm_env.block_env.timestamp = U256::from(common_block.header.timestamp()); - env.evm_env.block_env.gas_limit = common_block.header.gas_limit(); - env.evm_env.block_env.difficulty = common_block.header.difficulty(); - env.evm_env.block_env.prevrandao = common_block.header.mix_hash(); - - self.time.reset(env.evm_env.block_env.timestamp.saturating_to()); - } - - { - // Collect block hashes before acquiring db lock to avoid holding blockchain storage - // lock across await. Only collect the last 256 blocks since that's all BLOCKHASH can - // access. - let block_hashes: Vec<_> = { - let storage = self.blockchain.storage.read(); - let min_block = common_block.header.number().saturating_sub(256); - storage - .hashes - .iter() - .filter(|(num, _)| **num >= min_block) - .map(|(&num, &hash)| (num, hash)) - .collect() - }; - - // Acquire db lock once for the entire restore operation to reduce lock churn. - let mut db = self.db.write().await; - db.clear(); - - // Insert account info before storage to prevent fork-mode RPC fetches after clear. - for (address, acc) in common_state { - db.insert_account(address, acc.info); - for (key, value) in acc.storage { - db.set_storage_at(address, key.into(), value.into())?; - } - } - - // Restore block hashes from blockchain storage (now unwound, contains only valid - // blocks). - for (block_num, hash) in block_hashes { - db.insert_block_hash(U256::from(block_num), hash); - } - } - - Ok(()) - } -} - -/// Constructs a `BlockEnv` from a block header. -fn block_env_from_header(header: &impl BlockHeader) -> BlockEnv { - BlockEnv { - number: U256::from(header.number()), - beneficiary: header.beneficiary(), - timestamp: U256::from(header.timestamp()), - difficulty: header.difficulty(), - prevrandao: header.mix_hash(), - basefee: header.base_fee_per_gas().unwrap_or_default(), - gas_limit: header.gas_limit(), - ..Default::default() - } } /// Get max nonce from transaction pool by address. fn get_pool_transactions_nonce( - pool_transactions: &[Arc], + pool_transactions: &[Arc>], address: Address, ) -> Option { if let Some(highest_nonce) = pool_transactions @@ -3635,22 +4189,21 @@ fn get_pool_transactions_nonce( } #[async_trait::async_trait] -impl TransactionValidator for Backend { +impl TransactionValidator for Backend { async fn validate_pool_transaction( &self, - tx: &PendingTransaction, + tx: &PendingTransaction, ) -> Result<(), BlockchainError> { let address = *tx.sender(); let account = self.get_account(address).await?; - let env = self.next_env(); - Ok(self.validate_pool_transaction_for(tx, &account, &env)?) + Ok(self.validate_pool_transaction_for(tx, &account, &self.next_env().evm_env)?) } fn validate_pool_transaction_for( &self, - pending: &PendingTransaction, + pending: &PendingTransaction, account: &AccountInfo, - env: &Env, + evm_env: &EvmEnv, ) -> Result<(), InvalidTransactionError> { let tx = &pending.transaction; @@ -3659,9 +4212,7 @@ impl TransactionValidator for Backend { if chain_id.to::() != tx_chain_id { if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() { // - if env.evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON - && tx.chain_id().is_none() - { + if evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON && tx.chain_id().is_none() { warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V"); return Err(InvalidTransactionError::IncompatibleEIP155); } @@ -3681,7 +4232,7 @@ impl TransactionValidator for Backend { } // EIP-4844 structural validation - if env.evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() { + if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() { // Heavy (blob validation) checks let blob_tx = match tx.as_ref() { FoundryTxEnvelope::Eip4844(tx) => tx.tx(), @@ -3710,9 +4261,8 @@ impl TransactionValidator for Backend { } // EIP-3860 initcode size validation, respects --code-size-limit / --disable-code-size-limit - if env.evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create { - let max_initcode_size = env - .evm_env + if evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create { + let max_initcode_size = evm_env .cfg_env .limit_contract_code_size .map(|limit| limit.saturating_mul(2)) @@ -3731,8 +4281,8 @@ impl TransactionValidator for Backend { } // Check tx gas limit against block gas limit, if block gas limit is set. - if !env.evm_env.cfg_env.disable_block_gas_limit - && tx.gas_limit() > env.evm_env.block_env.gas_limit + if !evm_env.cfg_env.disable_block_gas_limit + && tx.gas_limit() > evm_env.block_env.gas_limit { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh(ErrDetail { @@ -3741,8 +4291,8 @@ impl TransactionValidator for Backend { } // Check tx gas limit against tx gas limit cap (Osaka hard fork and later). - if env.evm_env.cfg_env.tx_gas_limit_cap.is_none() - && tx.gas_limit() > env.evm_env.cfg_env().tx_gas_limit_cap() + if evm_env.cfg_env.tx_gas_limit_cap.is_none() + && tx.gas_limit() > evm_env.cfg_env().tx_gas_limit_cap() { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh(ErrDetail { @@ -3751,9 +4301,9 @@ impl TransactionValidator for Backend { } // EIP-1559 fee validation (London hard fork and later). - if env.evm_env.cfg_env.spec >= SpecId::LONDON { - if tx.max_fee_per_gas() < env.evm_env.block_env.basefee.into() && !is_deposit_tx { - warn!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), env.evm_env.block_env.basefee); + if evm_env.cfg_env.spec >= SpecId::LONDON { + if tx.max_fee_per_gas() < evm_env.block_env.basefee.into() && !is_deposit_tx { + warn!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), evm_env.block_env.basefee); return Err(InvalidTransactionError::FeeCapTooLow); } @@ -3767,10 +4317,10 @@ impl TransactionValidator for Backend { } // EIP-4844 blob fee validation - if env.evm_env.cfg_env.spec >= SpecId::CANCUN + if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas() - && let Some(blob_gas_and_price) = &env.evm_env.block_env.blob_excess_gas_and_price + && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price && max_fee_per_blob_gas < 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); @@ -3819,11 +4369,11 @@ impl TransactionValidator for Backend { fn validate_for( &self, - tx: &PendingTransaction, + tx: &PendingTransaction, account: &AccountInfo, - env: &Env, + evm_env: &EvmEnv, ) -> Result<(), InvalidTransactionError> { - self.validate_pool_transaction_for(tx, account, env)?; + self.validate_pool_transaction_for(tx, account, evm_env)?; if tx.nonce() > account.nonce { return Err(InvalidTransactionError::NonceTooHigh); } @@ -3834,7 +4384,7 @@ impl TransactionValidator for Backend { /// Creates a `AnyRpcTransaction` as it's expected for the `eth` RPC api from storage data pub fn transaction_build( tx_hash: Option, - eth_transaction: MaybeImpersonatedTransaction, + eth_transaction: MaybeImpersonatedTransaction, block: Option<&Block>, info: Option, base_fee: Option, diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 1efa73501d11d..51f8000bd44bf 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -5,13 +5,13 @@ use crate::eth::{ MaybeFullDatabase, SerializableBlock, SerializableHistoricalStates, SerializableTransaction, StateDb, }, - env::Env, mem::cache::DiskStateCache, }, pool::transactions::PoolTransaction, }; use alloy_consensus::{BlockHeader, Header, constants::EMPTY_WITHDRAWALS}; use alloy_eips::eip7685::EMPTY_REQUESTS_HASH; +use alloy_evm::EvmEnv; use alloy_network::Network; use alloy_primitives::{ B256, Bytes, U256, @@ -280,32 +280,32 @@ pub struct BlockchainStorage { impl BlockchainStorage { /// Creates a new storage with a genesis block pub fn new( - env: &Env, - spec_id: SpecId, + evm_env: &EvmEnv, base_fee: Option, timestamp: u64, genesis_number: u64, ) -> Self { - let is_shanghai = spec_id >= SpecId::SHANGHAI; - let is_cancun = spec_id >= SpecId::CANCUN; - let is_prague = spec_id >= SpecId::PRAGUE; + let is_shanghai = *evm_env.spec_id() >= SpecId::SHANGHAI; + let is_cancun = *evm_env.spec_id() >= SpecId::CANCUN; + let is_prague = *evm_env.spec_id() >= SpecId::PRAGUE; // create a dummy genesis block let header = Header { timestamp, base_fee_per_gas: base_fee, - gas_limit: env.evm_env.block_env.gas_limit, - beneficiary: env.evm_env.block_env.beneficiary, - difficulty: env.evm_env.block_env.difficulty, - blob_gas_used: env.evm_env.block_env.blob_excess_gas_and_price.as_ref().map(|_| 0), - excess_blob_gas: env.evm_env.block_env.blob_excess_gas(), + gas_limit: evm_env.block_env.gas_limit, + beneficiary: evm_env.block_env.beneficiary, + difficulty: evm_env.block_env.difficulty, + blob_gas_used: evm_env.block_env.blob_excess_gas_and_price.as_ref().map(|_| 0), + excess_blob_gas: evm_env.block_env.blob_excess_gas(), number: genesis_number, parent_beacon_block_root: is_cancun.then_some(Default::default()), withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), ..Default::default() }; - let block = create_block(header, Vec::::new()); + let block = + create_block(header, Vec::>::new()); let genesis_hash = block.header.hash_slow(); let best_hash = genesis_hash; let best_number = genesis_number; @@ -471,16 +471,14 @@ pub struct Blockchain { impl Blockchain { /// Creates a new storage with a genesis block pub fn new( - env: &Env, - spec_id: SpecId, + evm_env: &EvmEnv, base_fee: Option, timestamp: u64, genesis_number: u64, ) -> Self { Self { storage: Arc::new(RwLock::new(BlockchainStorage::new( - env, - spec_id, + evm_env, base_fee, timestamp, genesis_number, @@ -521,7 +519,7 @@ impl Blockchain { } /// Represents the outcome of mining a new block -pub struct MinedBlockOutcome { +pub struct MinedBlockOutcome { /// The block that was mined pub block_number: u64, /// All transactions included in the block @@ -724,7 +722,7 @@ mod tests { let header = Header { gas_limit: 123456, ..Default::default() }; let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..]; - let tx: MaybeImpersonatedTransaction = + let tx: MaybeImpersonatedTransaction = FoundryTxEnvelope::decode(&mut &bytes_first[..]).unwrap().into(); let block = create_block(header.clone(), vec![tx.clone()]); let block_hash = block.header.hash_slow(); diff --git a/crates/anvil/src/eth/backend/validate.rs b/crates/anvil/src/eth/backend/validate.rs index 78a8579094158..858602787ca60 100644 --- a/crates/anvil/src/eth/backend/validate.rs +++ b/crates/anvil/src/eth/backend/validate.rs @@ -1,16 +1,13 @@ //! Support for validating transactions at certain stages -use crate::eth::{ - backend::env::Env, - error::{BlockchainError, InvalidTransactionError}, -}; +use crate::eth::error::{BlockchainError, InvalidTransactionError}; +use alloy_evm::EvmEnv; use anvil_core::eth::transaction::PendingTransaction; -use foundry_primitives::FoundryTxEnvelope; use revm::state::AccountInfo; /// A trait for validating transactions #[async_trait::async_trait] -pub trait TransactionValidator { +pub trait TransactionValidator { /// Validates the transaction's validity when it comes to nonce, payment /// /// This is intended to be checked before the transaction makes it into the pool and whether it @@ -25,7 +22,7 @@ pub trait TransactionValidator { &self, tx: &PendingTransaction, account: &AccountInfo, - env: &Env, + evm_env: &EvmEnv, ) -> Result<(), InvalidTransactionError>; /// Validates the transaction against a specific account @@ -35,6 +32,6 @@ pub trait TransactionValidator { &self, tx: &PendingTransaction, account: &AccountInfo, - env: &Env, + evm_env: &EvmEnv, ) -> Result<(), InvalidTransactionError>; } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index ab3904409776b..f6dad33b2d336 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -168,9 +168,9 @@ impl FeeManager { self.blob_params().calc_blob_fee(self.blob_excess_gas_and_price.read().excess_blob_gas) } - /// 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: u64, blob_excess_gas: u64) -> u64 { + /// Calculates the next block blob excess gas, using the provided parent blob excess gas and + /// parent blob gas used + pub fn get_next_block_blob_excess_gas(&self, blob_excess_gas: u64, blob_gas_used: u64) -> u64 { self.blob_params().next_block_excess_blob_gas_osaka( blob_excess_gas, blob_gas_used, diff --git a/crates/anvil/src/eth/miner.rs b/crates/anvil/src/eth/miner.rs index 74c960c795b1f..723adbbab76c4 100644 --- a/crates/anvil/src/eth/miner.rs +++ b/crates/anvil/src/eth/miner.rs @@ -2,7 +2,6 @@ use crate::eth::pool::{Pool, transactions::PoolTransaction}; use alloy_primitives::TxHash; -use foundry_primitives::FoundryTxEnvelope; use futures::{ channel::mpsc::Receiver, stream::{Fuse, StreamExt}, @@ -17,7 +16,7 @@ use std::{ }; use tokio::time::{Interval, MissedTickBehavior}; -pub struct Miner { +pub struct Miner { /// The mode this miner currently operates in mode: Arc>, /// used for task wake up when the mining mode was forcefully changed diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 8f2040f37a240..4da86933020ae 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -19,10 +19,11 @@ use alloy_rpc_types::{ parity::{Action, CreateAction, CreateOutput, TraceOutput}, }, }; +use foundry_primitives::FoundryNetwork; use futures::future::join_all; use itertools::Itertools; -impl EthApi { +impl EthApi { /// Otterscan currently requires this endpoint, even though it's not part of the `ots_*`. /// Ref: /// diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 6a692d1d5a360..b2f2e09fbe864 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -40,7 +40,6 @@ use alloy_consensus::Transaction; use alloy_primitives::{Address, TxHash}; use alloy_rpc_types::txpool::TxpoolStatus; use anvil_core::eth::transaction::PendingTransaction; -use foundry_primitives::FoundryTxEnvelope; use futures::channel::mpsc::{Receiver, Sender, channel}; use parking_lot::{Mutex, RwLock}; use std::{collections::VecDeque, fmt, sync::Arc}; @@ -48,7 +47,7 @@ use std::{collections::VecDeque, fmt, sync::Arc}; pub mod transactions; /// Transaction pool that performs validation. -pub struct Pool { +pub struct Pool { /// processes all pending transactions inner: RwLock>, /// listeners for new ready transactions @@ -226,7 +225,7 @@ impl Pool { /// /// Contains all transactions that are ready to be executed #[derive(Debug)] -struct PoolInner { +struct PoolInner { ready_transactions: ReadyTransactions, pending_transactions: PendingTransactions, } @@ -434,7 +433,7 @@ impl PoolInner { } /// Represents the outcome of a prune -pub struct PruneResult { +pub struct PruneResult { /// a list of added transactions that a pruned marker satisfied pub promoted: Vec>, /// all transactions that failed to be promoted and now are discarded @@ -463,7 +462,7 @@ impl fmt::Debug for PruneResult { } #[derive(Clone, Debug)] -pub struct ReadyTransaction { +pub struct ReadyTransaction { /// the hash of the submitted transaction hash: TxHash, /// transactions promoted to the ready queue @@ -486,7 +485,7 @@ impl ReadyTransaction { } #[derive(Clone, Debug)] -pub enum AddedTransaction { +pub enum AddedTransaction { /// transaction was successfully added and being processed Ready(ReadyTransaction), /// Transaction was successfully added but not yet queued for processing diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 81059c7b48d20..d6acb34f5e002 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -73,7 +73,7 @@ pub struct TransactionPriority(pub u128); /// Internal Transaction type #[derive(Clone, PartialEq, Eq)] -pub struct PoolTransaction { +pub struct PoolTransaction { /// the pending eth transaction pub pending_transaction: PendingTransaction, /// Markers required by the transaction @@ -128,7 +128,7 @@ impl fmt::Debug for PoolTransaction { } } -impl TryFrom for PoolTransaction { +impl TryFrom for PoolTransaction { type Error = eyre::Error; fn try_from(value: AnyRpcTransaction) -> Result { let typed_transaction = FoundryTxEnvelope::try_from(value)?; @@ -146,7 +146,7 @@ impl TryFrom for PoolTransaction { /// /// Keeps a set of transactions that are waiting for other transactions #[derive(Clone, Debug)] -pub struct PendingTransactions { +pub struct PendingTransactions { /// markers that aren't yet provided by any transaction required_markers: HashMap>, /// mapping of the markers of a transaction to the hash of the transaction @@ -289,7 +289,7 @@ impl PendingTransactions { /// A transaction in the pool #[derive(Clone)] -pub struct PendingPoolTransaction { +pub struct PendingPoolTransaction { pub transaction: Arc>, /// markers required and have not been satisfied yet by other transactions in the pool pub missing_markers: HashSet, @@ -337,7 +337,7 @@ impl fmt::Debug for PendingPoolTransaction { } } -pub struct TransactionsIterator { +pub struct TransactionsIterator { all: HashMap>, awaiting: HashMap)>, independent: BTreeSet>, @@ -394,7 +394,7 @@ impl Iterator for TransactionsIterator { /// transactions that are ready to be included in a block. #[derive(Clone, Debug)] -pub struct ReadyTransactions { +pub struct ReadyTransactions { /// keeps track of transactions inserted in the pool /// /// this way we can determine when transactions where submitted to the pool @@ -704,7 +704,7 @@ impl ReadyTransactions { /// A reference to a transaction in the pool #[derive(Debug)] -pub struct PoolTransactionRef { +pub struct PoolTransactionRef { /// actual transaction pub transaction: Arc>, /// identifier used to internally compare the transaction in the pool @@ -741,7 +741,7 @@ impl Ord for PoolTransactionRef { } #[derive(Debug)] -pub struct ReadyTransaction { +pub struct ReadyTransaction { /// ref to the actual transaction pub transaction: PoolTransactionRef, /// tracks the transactions that get unlocked by this transaction diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 9c4de9ae7b519..c576cb2050e40 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -4,6 +4,8 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::ToRpcResponseResult}, pubsub::filter_logs, }; +use alloy_consensus::TxReceipt; +use alloy_network::Network; use alloy_primitives::{TxHash, map::HashMap}; use alloy_rpc_types::{Filter, FilteredParams, Log}; use anvil_core::eth::subscription::SubscriptionId; @@ -11,7 +13,6 @@ use anvil_rpc::{ error::{ErrorCode, RpcError}, response::ResponseResult, }; -use foundry_primitives::FoundryNetwork; use futures::{Stream, StreamExt, channel::mpsc::Receiver}; use std::{ pin::Pin, @@ -22,23 +23,34 @@ use std::{ use tokio::sync::Mutex; /// Type alias for filters identified by their id and their expiration timestamp -type FilterMap = Arc>>; +type FilterMap = Arc, Instant)>>>; /// timeout after which to remove an active filter if it wasn't polled since then pub const ACTIVE_FILTER_TIMEOUT_SECS: u64 = 60 * 5; /// Contains all registered filters -#[derive(Clone, Debug)] -pub struct Filters { +pub struct Filters { /// all currently active filters - active_filters: FilterMap, + active_filters: FilterMap, /// How long we keep a live the filter after the last poll keepalive: Duration, } -impl Filters { +impl Clone for Filters { + fn clone(&self) -> Self { + Self { active_filters: self.active_filters.clone(), keepalive: self.keepalive } + } +} + +impl std::fmt::Debug for Filters { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Filters").field("keepalive", &self.keepalive).finish_non_exhaustive() + } +} + +impl Filters { /// Adds a new `EthFilter` to the set - pub async fn add_filter(&self, filter: EthFilter) -> String { + pub async fn add_filter(&self, filter: EthFilter) -> String { let id = new_id(); trace!(target: "node::filter", "Adding new filter id {}", id); let mut filters = self.active_filters.lock().await; @@ -46,26 +58,6 @@ impl Filters { id } - pub async fn get_filter_changes(&self, id: &str) -> ResponseResult { - { - let mut filters = self.active_filters.lock().await; - if let Some((filter, deadline)) = filters.get_mut(id) { - let resp = filter - .next() - .await - .unwrap_or_else(|| ResponseResult::success(Vec::<()>::new())); - *deadline = self.next_deadline(); - return resp; - } - } - warn!(target: "node::filter", "No filter found for {}", id); - ResponseResult::error(RpcError { - code: ErrorCode::ServerError(-32000), - message: "filter not found".into(), - data: None, - }) - } - /// Returns the original `Filter` of an `eth_newFilter` pub async fn get_log_filter(&self, id: &str) -> Option { let filters = self.active_filters.lock().await; @@ -76,7 +68,7 @@ impl Filters { } /// Removes the filter identified with the `id` - pub async fn uninstall_filter(&self, id: &str) -> Option { + pub async fn uninstall_filter(&self, id: &str) -> Option> { trace!(target: "node::filter", "Uninstalling filter id {}", id); self.active_filters.lock().await.remove(id).map(|(f, _)| f) } @@ -106,7 +98,32 @@ impl Filters { } } -impl Default for Filters { +impl Filters +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ + pub async fn get_filter_changes(&self, id: &str) -> ResponseResult { + { + let mut filters = self.active_filters.lock().await; + if let Some((filter, deadline)) = filters.get_mut(id) { + let resp = filter + .next() + .await + .unwrap_or_else(|| ResponseResult::success(Vec::<()>::new())); + *deadline = self.next_deadline(); + return resp; + } + } + warn!(target: "node::filter", "No filter found for {}", id); + ResponseResult::error(RpcError { + code: ErrorCode::ServerError(-32000), + message: "filter not found".into(), + data: None, + }) + } +} + +impl Default for Filters { fn default() -> Self { Self { active_filters: Arc::new(Default::default()), @@ -121,14 +138,26 @@ fn new_id() -> String { } /// Represents a poll based filter -#[derive(Debug)] -pub enum EthFilter { - Logs(Box), +pub enum EthFilter { + Logs(Box>), Blocks(NewBlockNotifications), PendingTransactions(Receiver), } -impl Stream for EthFilter { +impl std::fmt::Debug for EthFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Logs(_) => f.debug_tuple("Logs").finish(), + Self::Blocks(_) => f.debug_tuple("Blocks").finish(), + Self::PendingTransactions(_) => f.debug_tuple("PendingTransactions").finish(), + } + } +} + +impl Stream for EthFilter +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ type Item = ResponseResult; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -154,12 +183,11 @@ impl Stream for EthFilter { } /// Listens for new blocks and matching logs emitted in that block -#[derive(Debug)] -pub struct LogsFilter { +pub struct LogsFilter { /// listener for new blocks pub blocks: NewBlockNotifications, /// accessor for block storage - pub storage: StorageInfo, + pub storage: StorageInfo, /// matcher with all provided filter params pub filter: FilteredParams, /// existing logs that matched the filter when the listener was installed @@ -168,7 +196,16 @@ pub struct LogsFilter { pub historic: Option>, } -impl LogsFilter { +impl std::fmt::Debug for LogsFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LogsFilter").field("filter", &self.filter).finish_non_exhaustive() + } +} + +impl LogsFilter +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ /// Returns all the logs since the last time this filter was polled pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { let mut logs = self.historic.take().unwrap_or_default(); diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index af0d05657e476..db97548c12006 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -23,9 +23,10 @@ use alloy_eips::eip7840::BlobParams; use alloy_primitives::{Address, U256}; use alloy_signer_local::PrivateKeySigner; use eth::backend::fork::ClientFork; -use eyre::Result; +use eyre::{Result, WrapErr}; use foundry_common::provider::{ProviderBuilder, RetryProvider}; pub use foundry_evm::hardfork::EthereumHardfork; +use foundry_primitives::FoundryNetwork; use futures::{FutureExt, TryFutureExt}; use parking_lot::Mutex; use revm::primitives::hardfork::SpecId; @@ -111,7 +112,7 @@ extern crate tracing; /// # Ok(()) /// # } /// ``` -pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) { +pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) { try_spawn(config).await.expect("failed to spawn node") } @@ -135,11 +136,17 @@ pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) { /// # Ok(()) /// # } /// ``` -pub async fn try_spawn(mut config: NodeConfig) -> Result<(EthApi, NodeHandle)> { +pub async fn try_spawn(mut config: NodeConfig) -> Result<(EthApi, NodeHandle)> { let logger = if config.enable_tracing { init_tracing() } else { Default::default() }; logger.set_enabled(!config.silent); - let backend = Arc::new(config.setup().await?); + let backend = config.setup::().await?; + + if let Some(state) = config.init_state.clone() { + backend.load_state(state).await.wrap_err("failed to load init state")?; + } + + let backend = Arc::new(backend); if config.enable_auto_impersonate { backend.auto_impersonate_account(true); diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index c62427eb9a4c8..8a72447333543 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -3,12 +3,11 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::to_rpc_result}, }; use alloy_consensus::{BlockHeader, TxReceipt}; -use alloy_network::AnyRpcTransaction; +use alloy_network::{AnyRpcTransaction, Network}; use alloy_primitives::{B256, TxHash}; use alloy_rpc_types::{FilteredParams, Log, Transaction, pubsub::SubscriptionResult}; use anvil_core::eth::{block::Block, subscription::SubscriptionId}; use anvil_rpc::{request::Version, response::ResponseResult}; -use foundry_primitives::FoundryNetwork; use futures::{Stream, StreamExt, channel::mpsc::Receiver, ready}; use serde::Serialize; use std::{ @@ -19,16 +18,27 @@ use std::{ use tokio::sync::mpsc::UnboundedReceiver; /// Listens for new blocks and matching logs emitted in that block -#[derive(Debug)] -pub struct LogsSubscription { +pub struct LogsSubscription { pub blocks: NewBlockNotifications, - pub storage: StorageInfo, + pub storage: StorageInfo, pub filter: FilteredParams, pub queued: VecDeque, pub id: SubscriptionId, } -impl LogsSubscription { +impl std::fmt::Debug for LogsSubscription { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LogsSubscription") + .field("filter", &self.filter) + .field("id", &self.id) + .finish_non_exhaustive() + } +} + +impl LogsSubscription +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { loop { if let Some(log) = self.queued.pop_front() { @@ -85,15 +95,28 @@ pub struct EthSubscriptionParams { } /// Represents an ethereum Websocket subscription -#[derive(Debug)] -pub enum EthSubscription { - Logs(Box), - Header(NewBlockNotifications, StorageInfo, SubscriptionId), +pub enum EthSubscription { + Logs(Box>), + Header(NewBlockNotifications, StorageInfo, SubscriptionId), PendingTransactions(Receiver, SubscriptionId), FullPendingTransactions(UnboundedReceiver, SubscriptionId), } -impl EthSubscription { +impl std::fmt::Debug for EthSubscription { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Logs(_) => f.debug_tuple("Logs").finish(), + Self::Header(..) => f.debug_tuple("Header").finish(), + Self::PendingTransactions(..) => f.debug_tuple("PendingTransactions").finish(), + Self::FullPendingTransactions(..) => f.debug_tuple("FullPendingTransactions").finish(), + } + } +} + +impl EthSubscription +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ fn poll_response(&mut self, cx: &mut Context<'_>) -> Poll> { match self { Self::Logs(listener) => listener.poll(cx), @@ -136,7 +159,10 @@ impl EthSubscription { } } -impl Stream for EthSubscription { +impl Stream for EthSubscription +where + N::ReceiptEnvelope: TxReceipt + Clone, +{ type Item = serde_json::Value; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/crates/anvil/src/server/beacon/handlers.rs b/crates/anvil/src/server/beacon/handlers.rs index 9310a7960293c..7f6bf2838b9fc 100644 --- a/crates/anvil/src/server/beacon/handlers.rs +++ b/crates/anvil/src/server/beacon/handlers.rs @@ -12,6 +12,7 @@ use axum::{ http::HeaderMap, response::{IntoResponse, Response}, }; +use foundry_primitives::FoundryNetwork; use ssz::Encode; use std::{collections::HashMap, str::FromStr as _}; @@ -21,7 +22,7 @@ use std::{collections::HashMap, str::FromStr as _}; /// /// GET /eth/v1/beacon/blob_sidecars/{block_id} pub async fn handle_get_blob_sidecars( - State(_api): State, + State(_api): State>, Path(_block_id): Path, Query(_params): Query>, ) -> Response { @@ -34,7 +35,7 @@ pub async fn handle_get_blob_sidecars( /// GET /eth/v1/beacon/blobs/{block_id} pub async fn handle_get_blobs( headers: HeaderMap, - State(api): State, + State(api): State>, Path(block_id): Path, Query(versioned_hashes): Query>, ) -> Response { @@ -43,12 +44,31 @@ pub async fn handle_get_blobs( return BeaconError::invalid_block_id(block_id).into_response(); }; - // Parse indices from query parameters - // Supports both comma-separated (?indices=1,2,3) and repeated parameters (?indices=1&indices=2) - let versioned_hashes: Vec = versioned_hashes - .get("versioned_hashes") - .map(|s| s.split(',').filter_map(|hash| B256::from_str(hash.trim()).ok()).collect()) - .unwrap_or_default(); + // Parse versioned hashes from query parameters + // Supports comma-separated format: ?versioned_hashes=0x...,0x... + let versioned_hashes: Vec = match versioned_hashes.get("versioned_hashes") { + Some(s) => { + let mut hashes = Vec::new(); + for hash in s.split(',') { + let hash = hash.trim(); + if hash.is_empty() { + continue; + } + match B256::from_str(hash) { + Ok(h) => hashes.push(h), + Err(_) => { + return BeaconError::new( + super::error::BeaconErrorCode::BadRequest, + format!("Invalid versioned hash: {hash}"), + ) + .into_response(); + } + } + } + hashes + } + None => Vec::new(), + }; // Get the blob sidecars using existing EthApi logic match api.anvil_get_blobs_by_block_id(block_id, versioned_hashes) { @@ -74,7 +94,7 @@ pub async fn handle_get_blobs( /// Only returns the `genesis_time`, other fields are set to zero. /// /// GET /eth/v1/beacon/genesis -pub async fn handle_get_genesis(State(api): State) -> Response { +pub async fn handle_get_genesis(State(api): State>) -> Response { match api.anvil_get_genesis_time() { Ok(genesis_time) => Json(GenesisResponse { data: GenesisData { diff --git a/crates/anvil/src/server/beacon/mod.rs b/crates/anvil/src/server/beacon/mod.rs index 9100186d436d5..331062316a217 100644 --- a/crates/anvil/src/server/beacon/mod.rs +++ b/crates/anvil/src/server/beacon/mod.rs @@ -3,13 +3,14 @@ use axum::{Router, routing::get}; use crate::eth::EthApi; +use foundry_primitives::FoundryNetwork; mod error; mod handlers; mod utils; /// Configures an [`axum::Router`] that handles Beacon REST API calls. -pub fn router(api: EthApi) -> Router { +pub fn router(api: EthApi) -> Router { Router::new() .route("/eth/v1/beacon/blob_sidecars/{block_id}", get(handlers::handle_get_blob_sidecars)) .route("/eth/v1/beacon/blobs/{block_id}", get(handlers::handle_get_blobs)) diff --git a/crates/anvil/src/server/mod.rs b/crates/anvil/src/server/mod.rs index 9d8238a56b8b5..1f6d78d2a9466 100644 --- a/crates/anvil/src/server/mod.rs +++ b/crates/anvil/src/server/mod.rs @@ -4,6 +4,7 @@ use crate::{EthApi, IpcTask}; use anvil_server::{ServerConfig, ipc::IpcEndpoint}; use axum::Router; +use foundry_primitives::FoundryNetwork; use futures::StreamExt; use rpc_handlers::{HttpEthRpcHandler, PubSubEthRpcHandler}; use std::{io, net::SocketAddr, pin::pin}; @@ -18,7 +19,7 @@ mod rpc_handlers; /// future that runs it. pub async fn serve( addr: SocketAddr, - api: EthApi, + api: EthApi, config: ServerConfig, ) -> io::Result>> { let tcp_listener = TcpListener::bind(addr).await?; @@ -28,7 +29,7 @@ pub async fn serve( /// Configures a server that handles [`EthApi`] related JSON-RPC calls via HTTP and WS. pub async fn serve_on( tcp_listener: TcpListener, - api: EthApi, + api: EthApi, config: ServerConfig, ) -> io::Result<()> { axum::serve(tcp_listener, router(api, config).into_make_service()).await @@ -36,7 +37,7 @@ pub async fn serve_on( /// Configures an [`axum::Router`] that handles [`EthApi`] related JSON-RPC calls via HTTP and WS, /// and Beacon REST API calls. -pub fn router(api: EthApi, config: ServerConfig) -> Router { +pub fn router(api: EthApi, config: ServerConfig) -> Router { let http = HttpEthRpcHandler::new(api.clone()); let ws = PubSubEthRpcHandler::new(api.clone()); @@ -56,12 +57,12 @@ pub fn router(api: EthApi, config: ServerConfig) -> Router { /// /// Panics if setting up the IPC connection was unsuccessful. #[track_caller] -pub fn spawn_ipc(api: EthApi, path: String) -> IpcTask { +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: String) -> io::Result { +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()?; diff --git a/crates/anvil/src/server/rpc_handlers.rs b/crates/anvil/src/server/rpc_handlers.rs index e880e5e5628a9..321970bd03bcf 100644 --- a/crates/anvil/src/server/rpc_handlers.rs +++ b/crates/anvil/src/server/rpc_handlers.rs @@ -11,17 +11,18 @@ use alloy_rpc_types::{ use anvil_core::eth::{EthPubSub, EthRequest, EthRpcCall, subscription::SubscriptionId}; use anvil_rpc::{error::RpcError, response::ResponseResult}; use anvil_server::{PubSubContext, PubSubRpcHandler, RpcHandler}; +use foundry_primitives::FoundryNetwork; /// A `RpcHandler` that expects `EthRequest` rpc calls via http #[derive(Clone)] pub struct HttpEthRpcHandler { /// Access to the node - api: EthApi, + api: EthApi, } impl HttpEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` - pub fn new(api: EthApi) -> Self { + pub fn new(api: EthApi) -> Self { Self { api } } } @@ -39,12 +40,12 @@ impl RpcHandler for HttpEthRpcHandler { #[derive(Clone)] pub struct PubSubEthRpcHandler { /// Access to the node - api: EthApi, + api: EthApi, } impl PubSubEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` - pub fn new(api: EthApi) -> Self { + pub fn new(api: EthApi) -> Self { Self { api } } @@ -129,7 +130,7 @@ impl PubSubEthRpcHandler { impl PubSubRpcHandler for PubSubEthRpcHandler { type Request = EthRpcCall; type SubscriptionId = SubscriptionId; - type Subscription = EthSubscription; + type Subscription = EthSubscription; async fn on_request(&self, request: Self::Request, cx: PubSubContext) -> ResponseResult { trace!(target: "rpc", "received pubsub request {:?}", request); diff --git a/crates/anvil/src/service.rs b/crates/anvil/src/service.rs index c0071505157ae..83edd774951c0 100644 --- a/crates/anvil/src/service.rs +++ b/crates/anvil/src/service.rs @@ -3,6 +3,7 @@ use crate::{ NodeResult, eth::{ + backend::validate::TransactionValidator, fees::FeeHistoryService, miner::Miner, pool::{Pool, transactions::PoolTransaction}, @@ -10,7 +11,8 @@ use crate::{ filter::Filters, mem::{Backend, storage::MinedBlockOutcome}, }; -use foundry_primitives::{FoundryNetwork, FoundryTxEnvelope}; +use alloy_network::Network; +use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope}; use futures::{FutureExt, Stream, StreamExt}; use std::{ collections::VecDeque, @@ -26,28 +28,32 @@ use tokio::{task::JoinHandle, time::Interval}; /// 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 { +pub struct NodeService { /// The pool that holds all transactions. - pool: Arc>, + pool: Arc>, /// Creates new blocks. - block_producer: BlockProducer, + block_producer: BlockProducer, /// The miner responsible to select transactions from the `pool`. - miner: Miner, + miner: Miner, /// Maintenance task for fee history related tasks. fee_history: FeeHistoryService, /// Tracks all active filters - filters: Filters, + filters: Filters, /// The interval at which to check for filters that need to be evicted filter_eviction_interval: Interval, } -impl NodeService { +impl NodeService +where + Backend: TransactionValidator, + N: Network, +{ pub fn new( - pool: Arc, - backend: Arc>, - miner: Miner, + pool: Arc>, + backend: Arc>, + miner: Miner, fee_history: FeeHistoryService, - filters: Filters, + filters: Filters, ) -> Self { let start = tokio::time::Instant::now() + filters.keep_alive(); let filter_eviction_interval = tokio::time::interval_at(start, filters.keep_alive()); @@ -62,7 +68,11 @@ impl NodeService { } } -impl Future for NodeService { +impl Future for NodeService +where + Backend: TransactionValidator, + N: Network, +{ type Output = NodeResult<()>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -101,27 +111,35 @@ impl Future for NodeService { } } -type MiningResult = (MinedBlockOutcome, Arc>); +type MiningResult = (MinedBlockOutcome<::TxEnvelope>, Arc>); /// A type that exclusively mines one block at a time #[must_use = "streams do nothing unless polled"] -struct BlockProducer { +struct BlockProducer { /// Holds the backend if no block is being mined - idle_backend: Option>>, + 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>>>, + queued: VecDeque>>>, } -impl BlockProducer { - fn new(backend: Arc>) -> Self { +impl BlockProducer +where + Backend: TransactionValidator, + N: Network, +{ + fn new(backend: Arc>) -> Self { Self { idle_backend: Some(backend), block_mining: None, queued: Default::default() } } } -impl Stream for BlockProducer { - type Item = MinedBlockOutcome; +impl Stream for BlockProducer +where + Backend: TransactionValidator + Send + Sync + 'static, + N: Network + 'static, +{ + type Item = MinedBlockOutcome; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index ee0b7f7e610f8..3d0b38bbb64cd 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -8,6 +8,7 @@ use alloy_network::{BlockResponse, Network}; use alloy_primitives::B256; use alloy_provider::Provider; use alloy_rpc_types::anvil::Forking; +use foundry_primitives::FoundryNetwork; use futures::StreamExt; use std::fmt; use tokio::{runtime::Handle, task::JoinHandle}; @@ -69,7 +70,7 @@ impl TaskManager { /// 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 N: Network, P: Provider + Clone + Unpin + 'static, @@ -129,7 +130,7 @@ impl TaskManager { /// /// # } /// ``` - 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 N: Network, P: Provider + 'static, diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 2430901049de2..19ebdc314f7ca 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -24,6 +24,7 @@ use anvil::{EthereumHardfork, NodeConfig, NodeHandle, PrecompileFactory, eth::Et use foundry_common::provider::get_http_provider; use foundry_config::Config; use foundry_evm_networks::NetworkConfigs; +use foundry_primitives::FoundryNetwork; use foundry_test_utils::rpc::{self, next_http_rpc_endpoint, next_rpc_endpoint}; use futures::StreamExt; use std::{ @@ -41,9 +42,9 @@ const BLOCK_TIMESTAMP: u64 = 1_650_274_250u64; /// Represents an anvil fork of an anvil node #[expect(unused)] pub struct LocalFork { - origin_api: EthApi, + origin_api: EthApi, origin_handle: NodeHandle, - fork_api: EthApi, + fork_api: EthApi, fork_handle: NodeHandle, } @@ -1585,7 +1586,7 @@ async fn test_reset_updates_cache_path_when_rpc_url_not_provided() { let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); - async fn get_block_from_cache_path(api: &mut EthApi) -> u64 { + async fn get_block_from_cache_path(api: &mut EthApi) -> u64 { let db = api.backend.get_db().read().await; let cache_path = db.maybe_inner().unwrap().cache().cache_path().unwrap(); cache_path diff --git a/crates/anvil/tests/it/proof.rs b/crates/anvil/tests/it/proof.rs index 52c633b058790..a03ac47646405 100644 --- a/crates/anvil/tests/it/proof.rs +++ b/crates/anvil/tests/it/proof.rs @@ -2,10 +2,11 @@ use alloy_primitives::{Address, B256, Bytes, U256, address, fixed_bytes}; use anvil::{NodeConfig, eth::EthApi, spawn}; +use foundry_primitives::FoundryNetwork; use std::{collections::BTreeMap, str::FromStr}; async fn verify_account_proof( - api: &EthApi, + api: &EthApi, address: Address, proof: impl IntoIterator, ) { @@ -17,7 +18,7 @@ async fn verify_account_proof( } async fn verify_storage_proof( - api: &EthApi, + api: &EthApi, address: Address, slot: B256, proof: impl IntoIterator, diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 916dcbb728f63..caaf6ca6466c7 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -777,7 +777,7 @@ async fn test_trace_address_fork2() { } #[tokio::test(flavor = "multi_thread")] -async fn flaky_test_trace_filter() { +async fn test_trace_filter() { let (api, handle) = spawn(NodeConfig::test()).await; let provider = handle.ws_provider(); @@ -802,11 +802,11 @@ async fn flaky_test_trace_filter() { for i in 0..=5 { let tx = TransactionRequest::default().to(to).value(U256::from(i)).from(from); let tx = WithOtherFields::new(tx); - api.send_transaction(tx).await.unwrap(); + provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); } let traces = api.trace_filter(tracer).await.unwrap(); - assert_eq!(traces.len(), 5); + assert_eq!(traces.len(), 6); // Test filtering by address let tracer = TraceFilter { diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 77772a44223fa..420f79479ab68 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -60,6 +60,7 @@ tempo-alloy.workspace = true alloy-evm.workspace = true op-alloy-flz.workspace = true +op-alloy-network.workspace = true chrono.workspace = true eyre.workspace = true diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index f99dfdada1703..d181735902be9 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -9,17 +9,22 @@ use alloy_consensus::transaction::Recovered; use alloy_dyn_abi::{DynSolValue, ErrorExt, EventExt}; use alloy_eips::eip7702::SignedAuthorization; use alloy_ens::{ProviderEnsExt, namehash}; +use alloy_network::Ethereum; use alloy_primitives::{Address, B256, eip191_hash_message, hex, keccak256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; use clap::{CommandFactory, Parser}; use clap_complete::generate; use eyre::Result; -use foundry_cli::{utils, utils::LoadConfig}; +use foundry_cli::{ + opts::NetworkVariant, + utils::{self, LoadConfig}, +}; use foundry_common::{ abi::{get_error, get_event}, fmt::{format_tokens, format_uint_exp, serialize_value_as_json}, fs, + provider::ProviderBuilder, selectors::{ ParsedSignatures, SelectorImportData, SelectorKind, decode_calldata, decode_event_topic, decode_function_selector, decode_selectors, import_selectors, parse_signatures, @@ -27,7 +32,9 @@ use foundry_common::{ }, shell, stdin, }; +use op_alloy_network::Optimism; use std::time::Instant; +use tempo_alloy::TempoNetwork; /// Run the `cast` command-line interface. pub fn run() -> Result<()> { @@ -341,18 +348,42 @@ pub async fn run_command(args: CastArgs) -> Result<()> { Cast::new(provider).base_fee(block.unwrap_or(BlockId::Number(Latest))).await? )? } - CastSubcommand::Block { block, full, fields, raw, rpc } => { + CastSubcommand::Block { block, full, fields, raw, rpc, network } => { let config = rpc.load_config()?; - let provider = utils::get_provider(&config)?; // Can use either --raw or specify raw as a field - let raw = raw || fields.contains(&"raw".into()); + let output = if raw || fields.contains(&"raw".into()) { + match network { + Some(NetworkVariant::Optimism) => { + let provider = + ProviderBuilder::::from_config(&config)?.build()?; - sh_println!( - "{}", + Cast::new(&provider) + .block_raw(block.unwrap_or(BlockId::Number(Latest)), full) + .await? + } + Some(NetworkVariant::Tempo) => { + let provider = + ProviderBuilder::::from_config(&config)?.build()?; + Cast::new(&provider) + .block_raw(block.unwrap_or(BlockId::Number(Latest)), full) + .await? + } + // Ethereum (default) or no --raw flag + _ => { + let provider = + ProviderBuilder::::from_config(&config)?.build()?; + Cast::new(&provider) + .block_raw(block.unwrap_or(BlockId::Number(Latest)), full) + .await? + } + } + } else { + let provider = utils::get_provider(&config)?; Cast::new(provider) - .block(block.unwrap_or(BlockId::Number(Latest)), full, fields, raw) + .block(block.unwrap_or(BlockId::Number(Latest)), full, fields) .await? - )? + }; + sh_println!("{}", output)? } CastSubcommand::BlockNumber { rpc, block } => { let config = rpc.load_config()?; @@ -533,23 +564,34 @@ pub async fn run_command(args: CastArgs) -> Result<()> { } CastSubcommand::Run(cmd) => cmd.run().await?, CastSubcommand::SendTx(cmd) => cmd.run().await?, - CastSubcommand::Tx { tx_hash, from, nonce, field, raw, rpc, to_request } => { + CastSubcommand::Tx { tx_hash, from, nonce, field, raw, rpc, to_request, network } => { let config = rpc.load_config()?; // Can use either --raw or specify raw as a field - if raw || field.as_ref().is_some_and(|f| f == "raw") { - // Temporary workaround to handle tx raw encoding through FoundryNetwork - // TODO: Once the Network selection UI will be finalized, bring back --raw - // handling to `Cast::transaction` - sh_println!("{}", crate::transaction_raw(&config, tx_hash, from, nonce).await?)? - } else { - let provider = utils::get_provider(&config)?; - sh_println!( - "{}", + let is_raw = raw || field.as_ref().is_some_and(|f| f == "raw"); + let output = match network { + Some(NetworkVariant::Optimism) => { + let provider = ProviderBuilder::::from_config(&config)?.build()?; + Cast::new(&provider) - .transaction(tx_hash, from, nonce, field, to_request) + .transaction(tx_hash, from, nonce, field, is_raw, to_request) .await? - )? - } + } + Some(NetworkVariant::Tempo) => { + let provider = + ProviderBuilder::::from_config(&config)?.build()?; + Cast::new(&provider) + .transaction(tx_hash, from, nonce, field, is_raw, to_request) + .await? + } + // Ethereum (default) or no --raw flag + _ => { + let provider = utils::get_provider(&config)?; + Cast::new(&provider) + .transaction(tx_hash, from, nonce, field, is_raw, to_request) + .await? + } + }; + sh_println!("{}", output)? } // 4Byte diff --git a/crates/cast/src/cmd/access_list.rs b/crates/cast/src/cmd/access_list.rs index ae533483fb820..3c3427f5f0747 100644 --- a/crates/cast/src/cmd/access_list.rs +++ b/crates/cast/src/cmd/access_list.rs @@ -1,6 +1,5 @@ use crate::{ Cast, - rlp_converter::TryIntoRlpEncodable, tx::{CastTxBuilder, SenderKind}, }; use alloy_ens::NameOrAddress; @@ -12,13 +11,9 @@ use foundry_cli::{ opts::{RpcOpts, TransactionOpts}, utils::LoadConfig, }; -use foundry_common::{ - fmt::{UIfmt, UIfmtHeaderExt, UIfmtSignatureExt}, - provider::ProviderBuilder, -}; +use foundry_common::provider::ProviderBuilder; use foundry_primitives::FoundryTransactionBuilder; use foundry_wallets::WalletOpts; -use serde::Serialize; use std::str::FromStr; use tempo_alloy::TempoNetwork; @@ -74,12 +69,7 @@ impl AccessListArgs { pub async fn run_with_network(self) -> Result<()> where - N::TxEnvelope: Serialize + UIfmtSignatureExt, - N::Header: TryIntoRlpEncodable, N::TransactionRequest: FoundryTransactionBuilder, - N::TransactionResponse: UIfmt, - N::HeaderResponse: UIfmtHeaderExt, - N::BlockResponse: UIfmt, { let Self { to, mut sig, args, data, tx, rpc, wallet, block } = self; diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index d410861c943cd..f3b436438829c 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -2,7 +2,6 @@ use super::run::fetch_contracts_bytecode_from_trace; use crate::{ Cast, debug::handle_traces, - rlp_converter::TryIntoRlpEncodable, traces::TraceKind, tx::{CastTxBuilder, SenderKind}, }; @@ -22,7 +21,6 @@ use foundry_cli::{ }; use foundry_common::{ abi::{encode_function_args, get_func}, - fmt::{UIfmt, UIfmtHeaderExt, UIfmtSignatureExt}, provider::{ProviderBuilder, curl_transport::generate_curl_command}, sh_println, shell, }; @@ -44,7 +42,6 @@ use foundry_wallets::WalletOpts; use itertools::Either; use regex::Regex; use revm::context::TransactionType; -use serde::Serialize; use std::{str::FromStr, sync::LazyLock}; use tempo_alloy::TempoNetwork; @@ -232,12 +229,7 @@ impl CallArgs { pub async fn run_with_network(self) -> Result<()> where - N::TxEnvelope: Serialize + UIfmtSignatureExt, - N::Header: TryIntoRlpEncodable, N::TransactionRequest: FoundryTransactionBuilder, - N::TransactionResponse: UIfmt, - N::HeaderResponse: UIfmtHeaderExt, - N::BlockResponse: UIfmt, { let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); let evm_opts = figment.extract::()?; @@ -306,21 +298,21 @@ impl CallArgs { } let create2_deployer = evm_opts.create2_deployer; - let (mut env, fork, chain, networks) = + let (mut evm_env, tx_env, fork, chain, networks) = TracingExecutor::get_fork_material(&mut config, evm_opts).await?; // modify settings that usually set in eth_call - env.evm_env.cfg_env.disable_block_gas_limit = true; - env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); - env.evm_env.block_env.gas_limit = u64::MAX; + evm_env.cfg_env.disable_block_gas_limit = true; + evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); + evm_env.block_env.gas_limit = u64::MAX; // Apply the block overrides. if let Some(block_overrides) = block_overrides { if let Some(number) = block_overrides.number { - env.evm_env.block_env.number = number.to(); + evm_env.block_env.number = number.to(); } if let Some(time) = block_overrides.time { - env.evm_env.block_env.timestamp = U256::from(time); + evm_env.block_env.timestamp = U256::from(time); } } @@ -333,7 +325,7 @@ impl CallArgs { }) .with_state_changes(shell::verbosity() > 4); let mut executor = TracingExecutor::new( - env, + (evm_env, tx_env), fork, evm_version, trace_mode, @@ -345,7 +337,7 @@ impl CallArgs { let value = tx.value().unwrap_or_default(); let input = tx.input().cloned().unwrap_or_default(); let tx_kind = tx.kind().expect("set by builder"); - let env_tx = &mut executor.env_mut().tx; + let env_tx = executor.tx_env_mut(); // Set transaction options with --trace if let Some(gas_limit) = tx.gas_limit() { diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index 0cdaa05dff39a..b3c39936fe759 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -22,6 +22,7 @@ use foundry_common::{ #[doc(hidden)] pub use foundry_config::{Chain, utils::*}; use foundry_primitives::FoundryTransactionBuilder; +use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; sol! { @@ -45,6 +46,7 @@ sol! { /// /// This struct contains only the transaction options relevant to ERC20 token interactions #[derive(Debug, Clone, Args)] +#[command(next_help_heading = "Transaction options")] pub struct Erc20TxOpts { /// Gas limit for the transaction. #[arg(long, env = "ETH_GAS_LIMIT")] @@ -66,16 +68,16 @@ pub struct Erc20TxOpts { pub tempo: TempoOpts, } -/// Creates a provider with wallet for signing transactions locally. -pub(crate) async fn get_provider_with_wallet( +/// Creates a provider with a pre-resolved signer. +pub(crate) fn build_provider_with_signer( tx_opts: &SendTxOpts, + signer: WalletSigner, ) -> eyre::Result> where N::TxEnvelope: From>, N::UnsignedTx: SignableTransaction, { let config = tx_opts.eth.load_config()?; - let signer = tx_opts.eth.wallet.signer().await?; let wallet = EthereumWallet::from(signer); let provider = ProviderBuilder::::from_config(&config)?.build_with_wallet(wallet)?; if let Some(interval) = tx_opts.poll_interval { @@ -328,16 +330,38 @@ impl Erc20Subcommand { } pub async fn run(self) -> eyre::Result<()> { - if let Some(erc20) = self.erc20_opts() - && erc20.tempo.is_tempo() - { - self.run_generic::().await + // Resolve the signer once for state-changing variants. + let (signer, tempo_access_key) = match &self { + Self::Transfer { send_tx, .. } + | Self::Approve { send_tx, .. } + | Self::Mint { send_tx, .. } + | Self::Burn { send_tx, .. } => { + // Only attempt Tempo lookup if --from is set (avoids unnecessary I/O). + if send_tx.eth.wallet.from.is_some() { + let (s, ak) = send_tx.eth.wallet.maybe_signer().await?; + (s, ak) + } else { + (None, None) + } + } + _ => (None, None), + }; + + let is_tempo = self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) + || tempo_access_key.is_some(); + + if is_tempo { + self.run_generic::(signer, tempo_access_key).await } else { - self.run_generic::().await + self.run_generic::(signer, None).await } } - pub async fn run_generic(self) -> eyre::Result<()> + pub async fn run_generic( + self, + pre_resolved_signer: Option, + tempo_keychain: Option, + ) -> eyre::Result<()> where N::TxEnvelope: From>, N::UnsignedTx: SignableTransaction, @@ -346,6 +370,62 @@ impl Erc20Subcommand { { let config = self.rpc_opts().load_config()?; + // Macro to DRY the keychain-vs-normal send pattern for state-changing ops. + // The only thing that varies per variant is the IERC20 call expression. + macro_rules! erc20_send { + ( + $token:expr, + $send_tx:expr, + $tx_opts:expr, | + $erc20:ident, + $provider:ident | + $build_tx:expr + ) => {{ + let timeout = $send_tx.timeout.unwrap_or(config.transaction_timeout); + if let Some(ref access_key) = tempo_keychain { + let signer = + pre_resolved_signer.as_ref().expect("signer required for access key"); + let $provider = + ProviderBuilder::::from_config(&config)?.build()?; + let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); + let mut tx = { $build_tx }.into_transaction_request(); + $tx_opts.apply::( + &mut tx, + get_chain(config.chain, &$provider).await?.is_legacy(), + ); + apply_tempo_access_key::(&mut tx, Some(access_key)); + send_tempo_keychain( + &$provider, + tx, + signer, + access_key, + $send_tx.cast_async, + $send_tx.confirmations, + timeout, + ) + .await? + } else { + let signer = pre_resolved_signer.unwrap_or($send_tx.eth.wallet.signer().await?); + let $provider = build_provider_with_signer::(&$send_tx, signer)?; + let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); + let mut tx = { $build_tx }.into_transaction_request(); + $tx_opts.apply::( + &mut tx, + get_chain(config.chain, &$provider).await?.is_legacy(), + ); + cast_send( + $provider, + tx, + $send_tx.cast_async, + $send_tx.sync, + $send_tx.confirmations, + timeout, + ) + .await? + } + }}; + } + match self { // Read-only Self::Allowance { token, owner, spender, block, .. } => { @@ -448,78 +528,77 @@ impl Erc20Subcommand { } // State-changing Self::Transfer { token, to, amount, send_tx, tx: tx_opts, .. } => { - let provider = get_provider_with_wallet::(&send_tx).await?; - let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) - .transfer(to.resolve(&provider).await?, U256::from_str(&amount)?) - .into_transaction_request(); - - tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); - - cast_send( - provider, - tx, - send_tx.cast_async, - send_tx.sync, - send_tx.confirmations, - send_tx.timeout.unwrap_or(config.transaction_timeout), - ) - .await? + erc20_send!(token, send_tx, tx_opts, |erc20, provider| { + erc20.transfer(to.resolve(&provider).await?, U256::from_str(&amount)?) + }) } Self::Approve { token, spender, amount, send_tx, tx: tx_opts, .. } => { - let provider = get_provider_with_wallet::(&send_tx).await?; - let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) - .approve(spender.resolve(&provider).await?, U256::from_str(&amount)?) - .into_transaction_request(); - - tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); - - cast_send( - provider, - tx, - send_tx.cast_async, - send_tx.sync, - send_tx.confirmations, - send_tx.timeout.unwrap_or(config.transaction_timeout), - ) - .await? + erc20_send!(token, send_tx, tx_opts, |erc20, provider| { + erc20.approve(spender.resolve(&provider).await?, U256::from_str(&amount)?) + }) } Self::Mint { token, to, amount, send_tx, tx: tx_opts, .. } => { - let provider = get_provider_with_wallet::(&send_tx).await?; - let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) - .mint(to.resolve(&provider).await?, U256::from_str(&amount)?) - .into_transaction_request(); - - tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); - - cast_send( - provider, - tx, - send_tx.cast_async, - send_tx.sync, - send_tx.confirmations, - send_tx.timeout.unwrap_or(config.transaction_timeout), - ) - .await? + erc20_send!(token, send_tx, tx_opts, |erc20, provider| { + erc20.mint(to.resolve(&provider).await?, U256::from_str(&amount)?) + }) } Self::Burn { token, amount, send_tx, tx: tx_opts, .. } => { - let provider = get_provider_with_wallet::(&send_tx).await?; - let mut tx = IERC20::new(token.resolve(&provider).await?, &provider) - .burn(U256::from_str(&amount)?) - .into_transaction_request(); - - tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); - - cast_send( - provider, - tx, - send_tx.cast_async, - send_tx.sync, - send_tx.confirmations, - send_tx.timeout.unwrap_or(config.transaction_timeout), - ) - .await? + erc20_send!(token, send_tx, tx_opts, |erc20, provider| { + erc20.burn(U256::from_str(&amount)?) + }) } }; Ok(()) } } + +/// Applies Tempo access key fields (from, key_id) to a transaction request. +/// +/// Note: `key_authorization` is intentionally not set here. It is only included +/// if the key is not yet provisioned on-chain (checked in [`send_tempo_keychain`]). +fn apply_tempo_access_key( + tx: &mut N::TransactionRequest, + config: Option<&TempoAccessKeyConfig>, +) where + N::TransactionRequest: FoundryTransactionBuilder, +{ + if let Some(config) = config { + tx.set_from(config.wallet_address); + tx.set_key_id(config.key_address); + } +} + +/// Sends a Tempo transaction using access key (keychain V2 mode). +/// +/// Signs the transaction with the access key and sends it via `send_raw_transaction`, +/// bypassing `EthereumWallet`. Only includes `key_authorization` if the key is not yet +/// provisioned on-chain. +async fn send_tempo_keychain>( + provider: &P, + mut tx: ::TransactionRequest, + signer: &WalletSigner, + access_key: &TempoAccessKeyConfig, + cast_async: bool, + confirmations: u64, + timeout: u64, +) -> eyre::Result<()> { + // Only include key_authorization if the key is not yet provisioned on-chain. + if let Some(ref auth) = access_key.key_authorization + && !crate::tempo::is_key_provisioned( + provider, + access_key.wallet_address, + access_key.key_address, + ) + .await + { + tx.set_key_authorization(auth.clone()); + } + + let raw_tx = + foundry_wallets::tempo::sign_with_access_key(tx, signer, access_key.wallet_address).await?; + + let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); + + let cast = crate::tx::CastTxSender::new(provider); + cast.print_tx_result(tx_hash, cast_async, confirmations, timeout).await +} diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 41a59972c79f0..9b9b153b2f66c 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -1,4 +1,7 @@ -use crate::{debug::handle_traces, utils::apply_chain_and_block_specific_env_changes}; +use crate::{ + debug::handle_traces, + utils::{apply_chain_and_block_specific_env_changes, block_env_from_header}, +}; use alloy_consensus::{BlockHeader, Transaction}; use alloy_evm::FromRecoveredTx; use alloy_network::{AnyNetwork, TransactionResponse}; @@ -24,7 +27,6 @@ use foundry_config::{ }, }; use foundry_evm::{ - Env, executors::{EvmError, Executor, TracingExecutor}, opts::EvmOpts, traces::{InternalTraceMode, TraceMode, Traces}, @@ -163,7 +165,7 @@ impl RunArgs { config.fork_block_number = Some(tx_block_number - 1); let create2_deployer = evm_opts.create2_deployer; - let (block, (mut env, fork, chain, networks)) = tokio::try_join!( + let (block, (mut evm_env, tx_env, fork, chain, networks)) = tokio::try_join!( // fetch the block the transaction was mined in provider.get_block(tx_block_number.into()).full().into_future().map_err(Into::into), TracingExecutor::get_fork_material(&mut config, evm_opts) @@ -171,24 +173,19 @@ impl RunArgs { let mut evm_version = self.evm_version; - env.evm_env.cfg_env.disable_block_gas_limit = self.disable_block_gas_limit; + evm_env.cfg_env.disable_block_gas_limit = self.disable_block_gas_limit; // By default do not enforce transaction gas limits imposed by Osaka (EIP-7825). // Users can opt-in to enable these limits by setting `enable_tx_gas_limit` to true. if !self.enable_tx_gas_limit { - env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); + evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); } - env.evm_env.cfg_env.limit_contract_code_size = None; - env.evm_env.block_env.number = U256::from(tx_block_number); + evm_env.cfg_env.limit_contract_code_size = None; + evm_env.block_env.number = U256::from(tx_block_number); if let Some(block) = &block { - env.evm_env.block_env.timestamp = U256::from(block.header.timestamp()); - env.evm_env.block_env.beneficiary = block.header.beneficiary(); - env.evm_env.block_env.difficulty = block.header.difficulty(); - env.evm_env.block_env.prevrandao = Some(block.header.mix_hash().unwrap_or_default()); - env.evm_env.block_env.basefee = block.header.base_fee_per_gas().unwrap_or_default(); - env.evm_env.block_env.gas_limit = block.header.gas_limit(); + evm_env.block_env = block_env_from_header(&block.header); // TODO: we need a smarter way to map the block to the corresponding evm_version for // commonly used chains @@ -199,7 +196,7 @@ impl RunArgs { } } apply_chain_and_block_specific_env_changes::( - &mut env.evm_env, + &mut evm_env, block, config.networks, ); @@ -214,7 +211,7 @@ impl RunArgs { }) .with_state_changes(shell::verbosity() > 4); let mut executor = TracingExecutor::new( - env.clone(), + (evm_env.clone(), tx_env), fork, evm_version, trace_mode, @@ -222,12 +219,8 @@ impl RunArgs { create2_deployer, None, )?; - let mut env = Env::new_with_spec_id( - env.evm_env.cfg_env.clone(), - env.evm_env.block_env.clone(), - env.tx.clone(), - executor.spec_id(), - ); + + evm_env.cfg_env.set_spec(executor.spec_id()); // Set the state to the moment right before the transaction if !self.quick { @@ -258,24 +251,28 @@ impl RunArgs { break; } - if let Some(tx_envelope) = tx.as_envelope() { - env.tx = TxEnv::from_recovered_tx(tx_envelope, tx.from()); - } + let tx_env = tx.as_envelope().map_or(Default::default(), |tx_envelope| { + TxEnv::from_recovered_tx(tx_envelope, tx.from()) + }); - env.evm_env.cfg_env.disable_balance_check = true; + evm_env.cfg_env.disable_balance_check = true; if let Some(to) = Transaction::to(tx) { trace!(tx=?tx.tx_hash(),?to, "executing previous call transaction"); - executor.transact_with_env(env.clone()).wrap_err_with(|| { - format!( - "Failed to execute transaction: {:?} in block {}", - tx.tx_hash(), - env.evm_env.block_env.number - ) - })?; + executor.transact_with_env(evm_env.clone(), tx_env.clone()).wrap_err_with( + || { + format!( + "Failed to execute transaction: {:?} in block {}", + tx.tx_hash(), + evm_env.block_env.number + ) + }, + )?; } else { trace!(tx=?tx.tx_hash(), "executing previous create transaction"); - if let Err(error) = executor.deploy_with_env(env.clone(), None) { + if let Err(error) = + executor.deploy_with_env(evm_env.clone(), tx_env.clone(), None) + { match error { // Reverted transactions should be skipped EvmError::Execution(_) => (), @@ -284,7 +281,7 @@ impl RunArgs { format!( "Failed to deploy transaction: {:?} in block {}", tx.tx_hash(), - env.evm_env.block_env.number + evm_env.block_env.number ) }); } @@ -301,19 +298,20 @@ impl RunArgs { let result = { executor.set_trace_printer(self.trace_printer); - if let Some(tx_envelope) = tx.as_envelope() { - env.tx = TxEnv::from_recovered_tx(tx_envelope, tx.from()); - } + let tx_env = tx.as_envelope().map_or(Default::default(), |tx_envelope| { + TxEnv::from_recovered_tx(tx_envelope, tx.from()) + }); + if is_impersonated_tx(tx.as_ref()) { - env.evm_env.cfg_env.disable_balance_check = true; + evm_env.cfg_env.disable_balance_check = true; } if let Some(to) = Transaction::to(&tx) { trace!(tx=?tx.tx_hash(), to=?to, "executing call transaction"); - TraceResult::try_from(executor.transact_with_env(env))? + TraceResult::try_from(executor.transact_with_env(evm_env, tx_env))? } else { trace!(tx=?tx.tx_hash(), "executing create transaction"); - TraceResult::try_from(executor.deploy_with_env(env, None))? + TraceResult::try_from(executor.deploy_with_env(evm_env, tx_env, None))? } }; diff --git a/crates/cast/src/cmd/send.rs b/crates/cast/src/cmd/send.rs index 1b590fb889f99..6b07d7966a467 100644 --- a/crates/cast/src/cmd/send.rs +++ b/crates/cast/src/cmd/send.rs @@ -13,6 +13,7 @@ use foundry_common::{ provider::ProviderBuilder, }; use foundry_primitives::FoundryTransactionBuilder; +use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; use crate::tx::{self, CastTxBuilder, CastTxSender, SendTxOpts}; @@ -83,14 +84,97 @@ pub enum SendTxSubcommands { impl SendTxArgs { pub async fn run(self) -> Result<()> { - if self.tx.tempo.is_tempo() { - self.run_generic::().await + // Resolve the signer early so we know if it's a Tempo access key. + let (signer, tempo_access_key) = self.send_tx.eth.wallet.maybe_signer().await?; + + if let Some(tempo_access_key) = tempo_access_key { + // Tempo keychain mode: always uses TempoNetwork. + self.run_tempo_keychain( + signer.expect("signer required for access key"), + tempo_access_key, + ) + .await + } else if self.tx.tempo.is_tempo() { + self.run_generic::(signer).await + } else { + self.run_generic::(signer).await + } + } + + /// Handles Tempo access key (keychain mode) transactions. + /// + /// Bypasses `EthereumWallet` and manually constructs a `KeychainSignature`, + /// then sends the raw transaction. + async fn run_tempo_keychain( + self, + signer: WalletSigner, + access_key: TempoAccessKeyConfig, + ) -> Result<()> { + let Self { to, mut sig, mut args, data, send_tx, mut tx, command, unlocked: _, path } = + self; + + let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; + + if let Some(data) = data { + sig = Some(data); + } + + let code = if let Some(SendTxSubcommands::Create { + code, + sig: constructor_sig, + args: constructor_args, + }) = command + { + sig = constructor_sig; + args = constructor_args; + Some(code) } else { - self.run_generic::().await + None + }; + + // Inject access key ID into TempoOpts so it's set before gas estimation. + tx.tempo.key_id = Some(access_key.key_address); + + let config = send_tx.eth.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + if let Some(interval) = send_tx.poll_interval { + provider.client().set_poll_interval(Duration::from_secs(interval)) + } + + let builder = CastTxBuilder::new(&provider, tx, &config) + .await? + .with_to(to) + .await? + .with_code_sig_and_args(code, sig, args) + .await? + .with_blob_data(blob_data)?; + + let from = access_key.wallet_address; + + // Build using wallet address for correct nonce/gas estimation. + let (mut tx_request, _) = builder.build(from).await?; + + // Only include key_authorization if the key is not yet provisioned on-chain. + if let Some(auth) = access_key.key_authorization + && !crate::tempo::is_key_provisioned(&provider, from, access_key.key_address).await + { + tx_request.set_key_authorization(auth); } + + let raw_tx = crate::tempo::sign_with_access_key(tx_request, &signer, from).await?; + + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); + let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); + + let cast = CastTxSender::new(&provider); + cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await } - pub async fn run_generic(self) -> Result<()> + pub async fn run_generic( + self, + pre_resolved_signer: Option, + ) -> Result<()> where N::TxEnvelope: From>, N::UnsignedTx: SignableTransaction, @@ -214,7 +298,10 @@ impl SendTxArgs { // If we cannot successfully instantiate a local signer, then we will assume we don't have // enough information to sign and we must bail. } else { - let signer = send_tx.eth.wallet.signer().await?; + let signer = match pre_resolved_signer { + Some(s) => s, + None => send_tx.eth.wallet.signer().await?, + }; let from = signer.address(); tx::validate_from_address(send_tx.eth.wallet.from, from)?; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index dedd15e460728..c1d4e26209657 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -18,7 +18,7 @@ use alloy_primitives::{ utils::{ParseUnits, Unit, keccak256}, }; use alloy_provider::{PendingTransactionBuilder, Provider, network::eip2718::Decodable2718}; -use alloy_rlp::Decodable; +use alloy_rlp::{Decodable, Encodable}; use alloy_rpc_types::{ BlockId, BlockNumberOrTag, BlockOverrides, Filter, FilterBlockOption, Log, state::StateOverride, }; @@ -31,13 +31,11 @@ use foundry_common::{ compile::etherscan_project, flatten, fmt::*, - fs, - provider::ProviderBuilder, - shell, + fs, shell, }; -use foundry_config::{Chain, Config}; +use foundry_config::Chain; use foundry_evm::core::bytecode::InstIter; -use foundry_primitives::{FoundryNetwork, FoundryTxEnvelope}; +use foundry_primitives::FoundryTxEnvelope; use futures::{FutureExt, StreamExt, future::Either}; use rayon::prelude::*; @@ -63,12 +61,11 @@ pub mod base; pub(crate) mod debug; pub mod errors; mod rlp_converter; +pub mod tempo; pub mod tx; use rlp_converter::Item; -use crate::rlp_converter::TryIntoRlpEncodable; - // TODO: CastContract with common contract initializers? Same for CastProviders? pub struct Cast { @@ -76,14 +73,7 @@ pub struct Cast { _phantom: PhantomData, } -impl + Clone + Unpin, N: Network> Cast -where - N::TxEnvelope: Serialize + UIfmtSignatureExt, - N::Header: TryIntoRlpEncodable, - N::TransactionResponse: UIfmt, - N::HeaderResponse: UIfmtHeaderExt, - N::BlockResponse: UIfmt, -{ +impl + Clone + Unpin, N: Network> Cast { /// Creates a new Cast instance from the provided client /// /// # Example @@ -294,160 +284,6 @@ where Ok(res) } - /// # Example - /// - /// ``` - /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; - /// use cast::Cast; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = - /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; - /// let cast = Cast::new(provider); - /// let block = cast.block(5, true, vec![], false).await?; - /// println!("{}", block); - /// # Ok(()) - /// # } - /// ``` - pub async fn block>( - &self, - block: B, - full: bool, - fields: Vec, - raw: bool, - ) -> Result { - let block = block.into(); - if fields.contains(&"transactions".into()) && !full { - eyre::bail!("use --full to view transactions") - } - - let block = self - .provider - .get_block(block) - .kind(full.into()) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; - - Ok(if raw { - let encoded = block.header().as_ref().try_rlp_encode()?; - format!("0x{}", hex::encode(encoded)) - } else if !fields.is_empty() { - let mut result = String::new(); - for field in fields { - result.push_str( - &get_pretty_block_attr::(&block, &field) - .unwrap_or_else(|| format!("{field} is not a valid block field")), - ); - - result.push('\n'); - } - result.trim_end().to_string() - } else if shell::is_json() { - serde_json::to_value(&block).unwrap().to_string() - } else { - block.pretty() - }) - } - - async fn block_field_as_num>(&self, block: B, field: String) -> Result { - Self::block( - self, - block.into(), - false, - // Select only select field - vec![field], - false, - ) - .await? - .parse() - .map_err(Into::into) - } - - pub async fn base_fee>(&self, block: B) -> Result { - Self::block_field_as_num(self, block, String::from("baseFeePerGas")).await - } - - pub async fn age>(&self, block: B) -> Result { - let timestamp_str = - 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 { - Self::block_field_as_num(self, block, "timestamp".to_string()).await - } - - pub async fn chain(&self) -> Result<&str> { - let genesis_hash = Self::block( - self, - 0, - false, - // Select only block hash - vec![String::from("hash")], - false, - ) - .await?; - - Ok(match &genesis_hash[..] { - "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { - match &(Self::block(self, 1920000, false, vec![String::from("hash")], false) - .await?)[..] - { - "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { - "etclive" - } - _ => "ethlive", - } - } - "0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9" => "kovan", - "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" => "ropsten", - "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" => { - "optimism-mainnet" - } - "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" => { - "optimism-goerli" - } - "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5" => { - "optimism-kovan" - } - "0x521982bd54239dc71269eefb58601762cc15cfb2978e0becb46af7962ed6bfaa" => "fraxtal", - "0x910f5c4084b63fd860d0c2f9a04615115a5a991254700b39ba072290dbd77489" => { - "fraxtal-testnet" - } - "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442" => { - "arbitrum-mainnet" - } - "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" => "morden", - "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177" => "rinkeby", - "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" => "goerli", - "0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4" => "kotti", - "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b" => "polygon-pos", - "0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697" => { - "polygon-pos-amoy-testnet" - } - "0x81005434635456a16f74ff7023fbe0bf423abbc8a8deb093ffff455c0ad3b741" => "polygon-zkevm", - "0x676c1a76a6c5855a32bdf7c61977a0d1510088a4eeac1330466453b3d08b60b9" => { - "polygon-zkevm-cardona-testnet" - } - "0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756" => "gnosis", - "0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a" => "chiado", - "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", - "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", - "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { - match &(Self::block(self, 1, false, vec![String::from("hash")], false).await?)[..] { - "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { - "avalanche-fuji" - } - _ => "avalanche", - } - } - "0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee" => "ink", - "0xe5fd5cf0be56af58ad5751b401410d6b7a09d830fa459789746a3d0dd1c79834" => "ink-sepolia", - _ => "unknown", - }) - } - pub async fn chain_id(&self) -> Result { Ok(self.provider.get_chain_id().await?) } @@ -710,67 +546,6 @@ where Ok(code.len().to_string()) } - /// # Example - /// - /// ``` - /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; - /// use cast::Cast; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = - /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; - /// let cast = Cast::new(provider); - /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; - /// let tx = cast.transaction(Some(tx_hash.to_string()), None, None, None, false).await?; - /// println!("{}", tx); - /// # Ok(()) - /// # } - /// ``` - pub async fn transaction( - &self, - tx_hash: Option, - from: Option, - nonce: Option, - field: Option, - to_request: bool, - ) -> Result { - let tx = if let Some(tx_hash) = tx_hash { - let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; - self.provider - .get_transaction_by_hash(tx_hash) - .await? - .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))? - } else if let Some(from) = from { - // If nonce is not provided, uses 0. - let nonce = U64::from(nonce.unwrap_or_default()); - let from = from.resolve(self.provider.root()).await?; - - self.provider - .raw_request::<_, Option>( - "eth_getTransactionBySenderAndNonce".into(), - (from, nonce), - ) - .await? - .ok_or_else(|| { - eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::()) - })? - } else { - eyre::bail!("tx hash or from address is required") - }; - - Ok(if let Some(ref field) = field { - get_pretty_tx_attr::(&tx, field.as_str()) - .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? - } else if shell::is_json() { - // to_value first to sort json object keys - serde_json::to_value(&tx)?.to_string() - } else if to_request { - serde_json::to_string_pretty(&Into::::into(tx))? - } else { - tx.pretty() - }) - } - /// Perform a raw JSON-RPC request /// /// # Example @@ -1106,6 +881,264 @@ where } } +impl, N: Network> Cast +where + N::HeaderResponse: UIfmtHeaderExt, + N::BlockResponse: UIfmt, +{ + /// # Example + /// + /// ``` + /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; + /// use cast::Cast; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let block = cast.block(5, true, vec![]).await?; + /// println!("{}", block); + /// # Ok(()) + /// # } + /// ``` + pub async fn block>( + &self, + block: B, + full: bool, + fields: Vec, + ) -> Result { + let block = block.into(); + if fields.contains(&"transactions".into()) && !full { + eyre::bail!("use --full to view transactions") + } + + let block = self + .provider + .get_block(block) + .kind(full.into()) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; + + Ok(if !fields.is_empty() { + let mut result = String::new(); + for field in fields { + result.push_str( + &get_pretty_block_attr::(&block, &field) + .unwrap_or_else(|| format!("{field} is not a valid block field")), + ); + + result.push('\n'); + } + result.trim_end().to_string() + } else if shell::is_json() { + serde_json::to_value(&block).unwrap().to_string() + } else { + block.pretty() + }) + } + + async fn block_field_as_num>(&self, block: B, field: String) -> Result { + Self::block( + self, + block.into(), + false, + // Select only select field + vec![field], + ) + .await? + .parse() + .map_err(Into::into) + } + + pub async fn base_fee>(&self, block: B) -> Result { + Self::block_field_as_num(self, block, String::from("baseFeePerGas")).await + } + + pub async fn age>(&self, block: B) -> Result { + let timestamp_str = + 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 { + Self::block_field_as_num(self, block, "timestamp".to_string()).await + } + + pub async fn chain(&self) -> Result<&str> { + let genesis_hash = Self::block( + self, + 0, + false, + // Select only block hash + vec![String::from("hash")], + ) + .await?; + + Ok(match &genesis_hash[..] { + "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { + match &(Self::block(self, 1920000, false, vec![String::from("hash")]).await?)[..] { + "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { + "etclive" + } + _ => "ethlive", + } + } + "0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9" => "kovan", + "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" => "ropsten", + "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" => { + "optimism-mainnet" + } + "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" => { + "optimism-goerli" + } + "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5" => { + "optimism-kovan" + } + "0x521982bd54239dc71269eefb58601762cc15cfb2978e0becb46af7962ed6bfaa" => "fraxtal", + "0x910f5c4084b63fd860d0c2f9a04615115a5a991254700b39ba072290dbd77489" => { + "fraxtal-testnet" + } + "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442" => { + "arbitrum-mainnet" + } + "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" => "morden", + "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177" => "rinkeby", + "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" => "goerli", + "0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4" => "kotti", + "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b" => "polygon-pos", + "0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697" => { + "polygon-pos-amoy-testnet" + } + "0x81005434635456a16f74ff7023fbe0bf423abbc8a8deb093ffff455c0ad3b741" => "polygon-zkevm", + "0x676c1a76a6c5855a32bdf7c61977a0d1510088a4eeac1330466453b3d08b60b9" => { + "polygon-zkevm-cardona-testnet" + } + "0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756" => "gnosis", + "0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a" => "chiado", + "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", + "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", + "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { + match &(Self::block(self, 1, false, vec![String::from("hash")]).await?)[..] { + "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { + "avalanche-fuji" + } + _ => "avalanche", + } + } + "0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee" => "ink", + "0xe5fd5cf0be56af58ad5751b401410d6b7a09d830fa459789746a3d0dd1c79834" => "ink-sepolia", + _ => "unknown", + }) + } +} + +impl, N: Network> Cast +where + N::Header: Encodable, +{ + /// # Example + /// + /// ``` + /// use alloy_provider::{ProviderBuilder, RootProvider, network::Ethereum}; + /// use cast::Cast; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, Ethereum>::default().connect("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let block = cast.block_raw(5, true).await?; + /// println!("{}", block); + /// # Ok(()) + /// # } + /// ``` + pub async fn block_raw>(&self, block: B, full: bool) -> Result { + let block_id = block.into(); + + let block = self + .provider + .get_block(block_id) + .kind(full.into()) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block_id))?; + + let encoded = alloy_rlp::encode(block.header().as_ref()); + + Ok(format!("0x{}", hex::encode(encoded))) + } +} + +impl, N: Network> Cast +where + N::TxEnvelope: Serialize + UIfmtSignatureExt, + N::TransactionResponse: UIfmt, +{ + /// # Example + /// + /// ``` + /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; + /// use cast::Cast; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; + /// let tx = cast.transaction(Some(tx_hash.to_string()), None, None, None, false, false).await?; + /// println!("{}", tx); + /// # Ok(()) + /// # } + /// ``` + pub async fn transaction( + &self, + tx_hash: Option, + from: Option, + nonce: Option, + field: Option, + raw: bool, + to_request: bool, + ) -> Result { + let tx = if let Some(tx_hash) = tx_hash { + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; + self.provider + .get_transaction_by_hash(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))? + } else if let Some(from) = from { + // If nonce is not provided, uses 0. + let nonce = U64::from(nonce.unwrap_or_default()); + let from = from.resolve(self.provider.root()).await?; + + self.provider + .raw_request::<_, Option>( + "eth_getTransactionBySenderAndNonce".into(), + (from, nonce), + ) + .await? + .ok_or_else(|| { + eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::()) + })? + } else { + eyre::bail!("tx hash or from address is required") + }; + + Ok(if raw { + let encoded = tx.as_ref().encoded_2718(); + format!("0x{}", hex::encode(encoded)) + } else if let Some(ref field) = field { + get_pretty_tx_attr::(&tx, field.as_str()) + .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? + } else if shell::is_json() { + // to_value first to sort json object keys + serde_json::to_value(&tx)?.to_string() + } else if to_request { + serde_json::to_string_pretty(&Into::::into(tx))? + } else { + tx.pretty() + }) + } +} + pub struct SimpleCast; impl SimpleCast { @@ -2358,44 +2391,6 @@ fn explorer_client( builder.build().map_err(Into::into) } -// Temporary workaround to handle tx raw encoding through FoundryNetwork -// TODO: Once the Network selection UI will be finalized, bring back --raw -// handling to `Cast::transaction` -pub async fn transaction_raw( - config: &Config, - tx_hash: Option, - from: Option, - nonce: Option, -) -> Result { - let provider = ProviderBuilder::::from_config(config)?.build()?; - let tx = if let Some(tx_hash) = tx_hash { - let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; - provider - .get_transaction_by_hash(tx_hash) - .await? - .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))? - } else if let Some(from) = from { - // If nonce is not provided, uses 0. - let nonce = U64::from(nonce.unwrap_or_default()); - let from = from.resolve(provider.root()).await?; - - provider - .raw_request::<_, Option<::TransactionResponse>>( - "eth_getTransactionBySenderAndNonce".into(), - (from, nonce), - ) - .await? - .ok_or_else(|| { - eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::()) - })? - } else { - eyre::bail!("tx hash or from address is required") - }; - - let encoded = tx.as_ref().encoded_2718(); - Ok(format!("0x{}", hex::encode(encoded))) -} - #[cfg(test)] mod tests { use super::{DynSolValue, SimpleCast as Cast, serialize_value_as_json}; diff --git a/crates/cast/src/opts.rs b/crates/cast/src/opts.rs index 3e160c725039b..4eacf4a9c53d5 100644 --- a/crates/cast/src/opts.rs +++ b/crates/cast/src/opts.rs @@ -11,7 +11,7 @@ use alloy_primitives::{Address, B256, Selector, U256}; use alloy_rpc_types::BlockId; use clap::{ArgAction, Parser, Subcommand, ValueHint}; use eyre::Result; -use foundry_cli::opts::{EtherscanOpts, GlobalArgs, RpcOpts}; +use foundry_cli::opts::{EtherscanOpts, GlobalArgs, NetworkVariant, RpcOpts}; use foundry_common::version::{LONG_VERSION, SHORT_VERSION}; use std::{path::PathBuf, str::FromStr}; /// A Swiss Army knife for interacting with Ethereum applications from the command line. @@ -390,6 +390,10 @@ pub enum CastSubcommand { #[command(flatten)] rpc: RpcOpts, + + /// Specify the Network for correct encoding. + #[arg(long, short, num_args = 1, value_name = "NETWORK")] + network: Option, }, /// Get the latest block number. @@ -519,6 +523,10 @@ pub enum CastSubcommand { /// If specified, the transaction will be converted to a TransactionRequest JSON format. #[arg(long)] to_request: bool, + + /// Specify the Network for correct encoding. + #[arg(long, short, num_args = 1, value_name = "NETWORK")] + network: Option, }, /// Get the transaction receipt for a transaction. diff --git a/crates/cast/src/rlp_converter.rs b/crates/cast/src/rlp_converter.rs index 3ebc6e6d70892..f386edddc541e 100644 --- a/crates/cast/src/rlp_converter.rs +++ b/crates/cast/src/rlp_converter.rs @@ -1,10 +1,8 @@ -use alloy_network::AnyHeader; use alloy_primitives::{U256, hex}; use alloy_rlp::{Buf, Decodable, Encodable, Header}; -use eyre::{Context, Result}; +use eyre::Context; use serde_json::Value; use std::fmt; -use tempo_alloy::primitives::TempoHeader; /// Arbitrary nested data. /// @@ -94,33 +92,6 @@ impl fmt::Display for Item { } } -/// Trait for types that can be RLP-encoded, possibly after a fallible conversion. -/// -/// Types that directly implement [`Encodable`] can opt in via [`RlpEncodable`] -/// to get a blanket implementation. Types like [`AnyHeader`] that require -/// conversion provide their own implementation. -pub trait TryIntoRlpEncodable { - fn try_rlp_encode(&self) -> Result>; -} - -/// Marker trait for [`Encodable`] types to opt into [`TryIntoRlpEncodable`]. -trait RlpEncodable: Encodable {} - -impl RlpEncodable for alloy_consensus::Header {} -impl RlpEncodable for TempoHeader {} - -impl TryIntoRlpEncodable for T { - fn try_rlp_encode(&self) -> Result> { - Ok(alloy_rlp::encode(self)) - } -} - -impl TryIntoRlpEncodable for AnyHeader { - fn try_rlp_encode(&self) -> Result> { - Ok(alloy_rlp::encode(self.clone().try_into_header()?)) - } -} - #[cfg(test)] mod test { use crate::rlp_converter::Item; diff --git a/crates/cast/src/tempo.rs b/crates/cast/src/tempo.rs new file mode 100644 index 0000000000000..3c64491aaea63 --- /dev/null +++ b/crates/cast/src/tempo.rs @@ -0,0 +1,21 @@ +use alloy_primitives::Address; +use alloy_provider::Provider; +use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; + +pub use foundry_wallets::tempo::sign_with_access_key; + +/// Checks whether an access key is already provisioned on-chain. +/// +/// Queries the AccountKeychain precompile's `getKey` function. A key is considered +/// provisioned if the returned `keyId` is non-zero (i.e. the key exists and has not +/// been revoked). +pub async fn is_key_provisioned>( + provider: &P, + wallet_address: Address, + key_address: Address, +) -> bool { + match provider.get_keychain_key(wallet_address, key_address).await { + Ok(info) => info.keyId != Address::ZERO, + Err(_) => false, + } +} diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index ebbfd5c64fc06..80c52e565c6cb 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -86,10 +86,10 @@ impl SenderKind<'_> { /// If from is not specified, but there is a signer configured, returns the signer's address /// If from is not specified and there is no signer configured, returns zero address pub async fn from_wallet_opts(opts: WalletOpts) -> Result { - if let Some(from) = opts.from { - Ok(from.into()) - } else if let Ok(signer) = opts.signer().await { + if let (Some(signer), _) = opts.maybe_signer().await? { Ok(Self::OwnedSigner(Box::new(signer))) + } else if let Some(from) = opts.from { + Ok(from.into()) } else { Ok(Address::ZERO.into()) } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 778033145424e..a33252e17dc7d 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -181,6 +181,32 @@ casttest!(block_raw, |_prj, cmd| { ); }); +casttest!(block_raw_tempo, |_prj, cmd| { + // https://explore.tempo.xyz/block/8386710 + let output = cmd + .args([ + "block", + "8386710", + "--rpc-url", + "https://rpc.moderato.tempo.xyz", + "--raw", + "-n", + "tempo", + ]) + .assert_success() + .get_output() + .stdout_lossy() + .trim() + .to_string(); + + let hash = alloy_primitives::keccak256(hex::decode(output).unwrap()); + + assert_eq!( + hash.to_string(), + "0xcd6170dc28b888bcb93ed1ad76a6bea4ad9977b678db5d462df83d35ec9b8d15" + ); +}); + // tests that the `cast find-block` command works correctly casttest!(finds_block, |_prj, cmd| { // Construct args @@ -1636,7 +1662,9 @@ revertReason [..]Transaction too old, data: "0x08c379a000000000000000000 "#,"","","","")); }); // tests that the revert reason is loaded using the correct `from` address. -casttest!(revert_reason_from, |_prj, cmd| { +// Flaky: Sepolia RPC may not return the revertReason field depending on provider +// support for debug/trace APIs. +casttest!(flaky_revert_reason_from, |_prj, cmd| { let rpc = next_rpc_endpoint(NamedChain::Sepolia); // https://sepolia.etherscan.io/tx/0x10ee70cf9f5ced5c515e8d53bfab5ea9f5c72cd61b25fba455c8355ee286c4e4 cmd.args([ @@ -1664,7 +1692,7 @@ type 0 blobGasPrice {} blobGasUsed {} to 0x91b5d4111a4C038153b24e31F75ccdC47123595d -revertReason Counter is too large, data: "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000014436f756e74657220697320746f6f206c61726765000000000000000000000000" +... "#, "", "", "", "")); }); @@ -3978,6 +4006,8 @@ casttest!(tx_raw_opstack_deposit, |_prj, cmd| { "tx", "0xf403cba612d1c01c027455c0d97427ccd5f7f99aac30017e065f81d1e30244ea", "--raw", + "-n", + "optimism", "--rpc-url", "https://sepolia.base.org", ]).assert_success() @@ -3987,6 +4017,22 @@ casttest!(tx_raw_opstack_deposit, |_prj, cmd| { "#]]); }); +casttest!(tx_raw_tempo, |_prj, cmd| { + cmd.args([ + "tx", + "0xa24c6bbeea629a80be79e970a9749d0cbc6ee31625a0b75f585c173ab15a18ec", + "--raw", + "-n", + "tempo", + "--rpc-url", + "https://rpc.moderato.tempo.xyz", + ]).assert_success() + .stdout_eq(str![[r#" +0x76f8cf82a5bf1485059682f018830494e5f85ef85c9420c0000000000000000000007d9cc57068833ea780b84440c10f190000000000000000000000008a871f4189067637cfc4cc1500abd6244bf1df740000000000000000000000000000000000000000000000000000000005f5e100c08082057e80809420c000000000000000000000000000000000000080c0b841eb100c4cbd96903bf9e97968c0982670bb90fc191ee4544c7ff32d44e901dbea3f6fbdd58255051135c2fe1aa81583a270d96009cbe375f4605ef15971273a4f1b + +"#]]); +}); + // Test that cast send --create works correctly with constructor arguments // forgetest_async!(cast_send_create_with_constructor_args, |prj, cmd| { diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 7a865cc348386..ec91dec4a5e44 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3980,6 +3980,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "currentFilePath", + "description": "Get the source file path of the currently running test or script contract,\nrelative to the project root.", + "declaration": "function currentFilePath() external view returns (string memory path);", + "visibility": "external", + "mutability": "view", + "signature": "currentFilePath()", + "selector": "0x9b45555c", + "selectorBytes": [ + 155, + 69, + 85, + 92 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "deal", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index c703c2fc5fd9a..31b579848f3db 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1828,6 +1828,11 @@ interface Vm { #[cheatcode(group = Filesystem)] function projectRoot() external view returns (string memory path); + /// Get the source file path of the currently running test or script contract, + /// relative to the project root. + #[cheatcode(group = Filesystem)] + function currentFilePath() external view returns (string memory path); + /// Returns the time since unix epoch in milliseconds. #[cheatcode(group = Filesystem)] function unixTime() external view returns (uint256 milliseconds); diff --git a/crates/cheatcodes/src/crypto.rs b/crates/cheatcodes/src/crypto.rs index 95b925a157b66..fad6f945aadf5 100644 --- a/crates/cheatcodes/src/crypto.rs +++ b/crates/cheatcodes/src/crypto.rs @@ -377,7 +377,7 @@ fn sign_with_wallet( let mut wallets = state.wallets().inner.lock(); let maybe_provided_sender = wallets.provided_sender; - let (signers, _) = wallets.multi_wallet.signers()?; + let signers = wallets.multi_wallet.signers()?; let signer = if let Some(signer) = signer { signer diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 8fa56c256e5aa..c709550fb7ad4 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -1,11 +1,11 @@ //! Implementations of [`Evm`](spec::Group::Evm) cheatcodes. use crate::{ - BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxExt, CheatsCtxt, - Error, Result, Vm::*, inspector::RecordDebugStepInfo, + BroadcastableTransaction, Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Error, + EthCheatCtx, Result, Vm::*, inspector::RecordDebugStepInfo, }; -use alloy_consensus::TxEnvelope; -use alloy_evm::{EvmEnv, FromRecoveredTx}; +use alloy_consensus::{TxEnvelope, transaction::SignerRecoverable}; +use alloy_evm::FromRecoveredTx; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_network::eip2718::EIP4844_TX_TYPE_ID; use alloy_primitives::{ @@ -15,6 +15,7 @@ use alloy_primitives::{ use alloy_rlp::Decodable; use alloy_sol_types::SolValue; use foundry_common::{ + TransactionMaybeSigned, fs::{read_json_file, write_json_file}, slot_identifier::{ ENCODING_BYTES, ENCODING_DYN_ARRAY, ENCODING_INPLACE, ENCODING_MAPPING, SlotIdentifier, @@ -23,8 +24,8 @@ use foundry_common::{ }; use foundry_compilers::artifacts::EvmVersion; use foundry_evm_core::{ - Env, FoundryBlock, FoundryCfg, FoundryTransaction, - backend::{DatabaseExt, FoundryJournalExt, RevertStateSnapshotAction}, + FoundryBlock, FoundryTransaction, + backend::{DatabaseExt, RevertStateSnapshotAction}, constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, env::FoundryContextExt, utils::get_blob_base_fee_update_fraction_by_spec_id, @@ -318,7 +319,7 @@ impl Cheatcode for loadCall { } impl Cheatcode for loadAllocsCall { - fn apply_stateful>( + fn apply_stateful>( &self, ccx: &mut CheatsCtxt<'_, CTX>, ) -> Result { @@ -338,7 +339,7 @@ impl Cheatcode for loadAllocsCall { }; // Then, load the allocs into the database. - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + let (db, inner) = ccx.ecx.db_journal_inner_mut(); db.load_allocs(&allocs, inner) .map(|()| Vec::default()) .map_err(|e| fmt_err!("failed to load allocs: {e}")) @@ -346,7 +347,7 @@ impl Cheatcode for loadAllocsCall { } impl Cheatcode for cloneAccountCall { - fn apply_stateful>( + fn apply_stateful>( &self, ccx: &mut CheatsCtxt<'_, CTX>, ) -> Result { @@ -354,7 +355,7 @@ impl Cheatcode for cloneAccountCall { let account = ccx.ecx.journal_mut().load_account(*source)?; let genesis = genesis_account(account.data); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + let (db, inner) = ccx.ecx.db_journal_inner_mut(); db.clone_account(&genesis, target, inner)?; // Cloned account should persist in forked envs. ccx.ecx.db_mut().add_persistent_account(*target); @@ -495,10 +496,10 @@ impl Cheatcode for getChainIdCall { } impl Cheatcode for chainIdCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newChainId } = self; ensure!(*newChainId <= U256::from(u64::MAX), "chain ID must be less than 2^64"); - ccx.ecx.cfg_mut().set_chain_id(newChainId.to()); + ccx.ecx.cfg_mut().chain_id = newChainId.to(); Ok(Default::default()) } } @@ -515,7 +516,7 @@ impl Cheatcode for difficultyCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newDifficulty } = self; ensure!( - ccx.ecx.cfg().spec().into() < SpecId::MERGE, + (*ccx.ecx.cfg().spec()).into() < SpecId::MERGE, "`difficulty` is not supported after the Paris hard fork, use `prevrandao` instead; \ see EIP-4399: https://eips.ethereum.org/EIPS/eip-4399" ); @@ -537,7 +538,7 @@ impl Cheatcode for prevrandao_0Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newPrevrandao } = self; ensure!( - ccx.ecx.cfg().spec().into() >= SpecId::MERGE, + (*ccx.ecx.cfg().spec()).into() >= SpecId::MERGE, "`prevrandao` is not supported before the Paris hard fork, use `difficulty` instead; \ see EIP-4399: https://eips.ethereum.org/EIPS/eip-4399" ); @@ -550,7 +551,7 @@ impl Cheatcode for prevrandao_1Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newPrevrandao } = self; ensure!( - ccx.ecx.cfg().spec().into() >= SpecId::MERGE, + (*ccx.ecx.cfg().spec()).into() >= SpecId::MERGE, "`prevrandao` is not supported before the Paris hard fork, use `difficulty` instead; \ see EIP-4399: https://eips.ethereum.org/EIPS/eip-4399" ); @@ -563,7 +564,7 @@ impl Cheatcode for blobhashesCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { hashes } = self; ensure!( - ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, + (*ccx.ecx.cfg().spec()).into() >= SpecId::CANCUN, "`blobhashes` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); @@ -575,10 +576,10 @@ impl Cheatcode for blobhashesCall { } impl Cheatcode for getBlobhashesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; ensure!( - ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, + (*ccx.ecx.cfg().spec()).into() >= SpecId::CANCUN, "`getBlobhashes` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); @@ -629,12 +630,12 @@ impl Cheatcode for blobBaseFeeCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { newBlobBaseFee } = self; ensure!( - ccx.ecx.cfg().spec().into() >= SpecId::CANCUN, + (*ccx.ecx.cfg().spec()).into() >= SpecId::CANCUN, "`blobBaseFee` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - let spec: SpecId = ccx.ecx.cfg().spec().into(); + let spec: SpecId = (*ccx.ecx.cfg().spec()).into(); ccx.ecx.block_mut().set_blob_excess_gas_and_price( (*newBlobBaseFee).to(), get_blob_base_fee_update_fraction_by_spec_id(spec), @@ -889,20 +890,14 @@ impl Cheatcode for stopSnapshotGas_2Call { // Deprecated in favor of `snapshotStateCall` impl Cheatcode for snapshotCall { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_snapshot_state(ccx) } } impl Cheatcode for snapshotStateCall { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self {} = self; inner_snapshot_state(ccx) } @@ -910,20 +905,14 @@ impl Cheatcode for snapshotStateCall { // Deprecated in favor of `revertToStateCall` impl Cheatcode for revertToCall { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state(ccx, *snapshotId) } } impl Cheatcode for revertToStateCall { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state(ccx, *snapshotId) } @@ -931,20 +920,14 @@ impl Cheatcode for revertToStateCall { // Deprecated in favor of `revertToStateAndDeleteCall` impl Cheatcode for revertToAndDeleteCall { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state_and_delete(ccx, *snapshotId) } } impl Cheatcode for revertToStateAndDeleteCall { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { snapshotId } = self; inner_revert_to_state_and_delete(ccx, *snapshotId) } @@ -1130,7 +1113,7 @@ impl Cheatcode for getStorageAccessesCall { } impl Cheatcode for broadcastRawTransactionCall { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -1138,12 +1121,15 @@ impl Cheatcode for broadcastRawTransactionCall { let tx = TxEnvelope::decode(&mut self.data.as_ref()) .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?; - executor.transact_from_tx_on_db(ccx.state, ccx.ecx, &tx.clone().into())?; + let from = tx.recover_signer()?; + let tx_env = FromRecoveredTx::from_recovered_tx(&tx, from); + + executor.transact_from_tx_on_db(ccx.state, ccx.ecx, &tx_env)?; if ccx.state.broadcast.is_some() { ccx.state.broadcastable_transactions.push_back(BroadcastableTransaction { rpc: ccx.ecx.db().active_fork_url(), - transaction: tx.try_into()?, + transaction: TransactionMaybeSigned::new_signed(tx)?, }); } @@ -1170,7 +1156,7 @@ impl Cheatcode for setBlockhashCall { } impl Cheatcode for executeTransactionCall { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -1207,24 +1193,25 @@ impl Cheatcode for executeTransactionCall { let tx_env = >::from_recovered_tx(&tx, sender); // Save current env for restoration after execution. - let (cached_evm_env, cached_tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let cached_evm_env = ccx.ecx.evm_clone(); + let cached_tx_env = ccx.ecx.tx_clone(); // Override env for isolated execution. ccx.ecx.block_mut().set_basefee(0); - *ccx.ecx.tx_mut() = tx_env; + ccx.ecx.set_tx(tx_env); ccx.ecx.tx_mut().set_gas_price(0); ccx.ecx.tx_mut().set_gas_priority_fee(None); // Enable nonce checks for realistic simulation. - ccx.ecx.cfg_mut().set_disable_nonce_check(false); + ccx.ecx.cfg_mut().disable_nonce_check = false; // EIP-3860: enforce initcode size limit. - ccx.ecx - .cfg_mut() - .set_limit_contract_initcode_size(Some(revm::primitives::eip3860::MAX_INITCODE_SIZE)); + ccx.ecx.cfg_mut().limit_contract_initcode_size = + Some(revm::primitives::eip3860::MAX_INITCODE_SIZE); // Snapshot the modified env for EVM construction. - let (modified_evm_env, modified_tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let modified_evm_env = ccx.ecx.evm_clone(); + let modified_tx_env = ccx.ecx.tx_clone(); // Mark as inner context so isolation mode doesn't trigger a nested transact_inner // when the inner EVM executes calls at depth == 1. @@ -1232,7 +1219,7 @@ impl Cheatcode for executeTransactionCall { // Clone journaled state and mark all accounts/slots cold. let cold_state = { - let (_, journal) = ccx.ecx.journal_mut().as_db_and_inner(); + let (_, journal) = ccx.ecx.db_journal_inner_mut(); let mut state = journal.state.clone(); for (addr, acc_mut) in &mut state { if journal.warm_addresses.is_cold(addr) { @@ -1247,28 +1234,19 @@ impl Cheatcode for executeTransactionCall { }; let mut res = None; - let mut nested_env = None; let mut cold_state = Some(cold_state); - let modified_tx = modified_tx_env.clone(); - { - let (db, _) = ccx.ecx.journal_mut().as_db_and_inner(); - executor.with_fresh_nested_evm( - ccx.state, - db, - modified_evm_env, - modified_tx_env, - &mut |evm| { - // SAFETY: closure is called exactly once by the executor. - evm.journal_inner_mut().state = cold_state.take().expect("called once"); - // Set depth to 1 for proper trace collection. - evm.journal_inner_mut().depth = 1; - res = Some(evm.transact(modified_tx.clone())); - nested_env = Some(evm.to_env()); - Ok(()) - }, - )?; - } - let (res, (mut nested_evm_env, _)) = (res.unwrap(), nested_env.unwrap()); + let mut nested_evm_env = { + let (db, _) = ccx.ecx.db_journal_inner_mut(); + executor.with_fresh_nested_evm(ccx.state, db, modified_evm_env, &mut |evm| { + // SAFETY: closure is called exactly once by the executor. + evm.journal_inner_mut().state = cold_state.take().expect("called once"); + // Set depth to 1 for proper trace collection. + evm.journal_inner_mut().depth = 1; + res = Some(evm.transact_raw(modified_tx_env.clone())); + Ok(()) + })? + }; + let res = res.unwrap(); // Restore env, preserving cheatcode cfg/block changes from the nested EVM // but restoring the original tx and basefee (which we zeroed for the nested call) @@ -1277,7 +1255,8 @@ impl Cheatcode for executeTransactionCall { nested_evm_env.cfg_env.disable_nonce_check = cached_evm_env.cfg_env.disable_nonce_check; nested_evm_env.cfg_env.limit_contract_initcode_size = cached_evm_env.cfg_env.limit_contract_initcode_size; - Env::apply_evm_and_tx(ccx.ecx, nested_evm_env, cached_tx_env); + ccx.ecx.set_evm(nested_evm_env); + ccx.ecx.set_tx(cached_tx_env); // Reset inner context flag. executor.set_in_inner_context(false, None); @@ -1327,7 +1306,7 @@ impl Cheatcode for executeTransactionCall { } impl Cheatcode for startDebugTraceRecordingCall { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -1357,7 +1336,7 @@ impl Cheatcode for startDebugTraceRecordingCall { } impl Cheatcode for stopAndReturnDebugTraceRecordingCall { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -1408,8 +1387,8 @@ impl Cheatcode for setEvmVersionCall { } impl Cheatcode for getEvmVersionCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { - let spec: SpecId = ccx.ecx.cfg().spec().into(); + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + let spec = (*ccx.ecx.cfg().spec()).into(); Ok(spec.to_string().to_lowercase().abi_encode()) } } @@ -1422,22 +1401,20 @@ pub(super) fn get_nonce>( Ok(account.data.info.nonce.abi_encode()) } -fn inner_snapshot_state>( - ccx: &mut CheatsCtxt<'_, CTX>, -) -> Result { - let evm_env = - EvmEnv { cfg_env: ccx.ecx.cfg_mut().clone(), block_env: ccx.ecx.block_mut().clone() }; - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); +fn inner_snapshot_state(ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + let evm_env = ccx.ecx.evm_clone(); + let (db, inner) = ccx.ecx.db_journal_inner_mut(); let id = db.snapshot_state(inner, &evm_env); Ok(id.abi_encode()) } -fn inner_revert_to_state>( +fn inner_revert_to_state( ccx: &mut CheatsCtxt<'_, CTX>, snapshot_id: U256, ) -> Result { - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + let mut evm_env = ccx.ecx.evm_clone(); + let mut tx_env = ccx.ecx.tx_clone(); + let (db, inner) = ccx.ecx.db_journal_inner_mut(); if let Some(restored) = db.revert_state( snapshot_id, inner, @@ -1446,19 +1423,21 @@ fn inner_revert_to_state>( RevertStateSnapshotAction::RevertKeep, ) { *inner = restored; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + ccx.ecx.set_evm(evm_env); + ccx.ecx.set_tx(tx_env); Ok(true.abi_encode()) } else { Ok(false.abi_encode()) } } -fn inner_revert_to_state_and_delete>( +fn inner_revert_to_state_and_delete( ccx: &mut CheatsCtxt<'_, CTX>, snapshot_id: U256, ) -> Result { - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + let mut evm_env = ccx.ecx.evm_clone(); + let mut tx_env = ccx.ecx.tx_clone(); + let (db, inner) = ccx.ecx.db_journal_inner_mut(); if let Some(restored) = db.revert_state( snapshot_id, inner, @@ -1467,7 +1446,8 @@ fn inner_revert_to_state_and_delete>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias } = self; create_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createFork_1Call { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, blockNumber } = self; create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createFork_2Call { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, txHash } = self; create_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for createSelectFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias } = self; create_select_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createSelectFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, blockNumber } = self; create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createSelectFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, txHash } = self; create_select_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for rollFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { blockNumber } = self; persist_caller(ccx); - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - db.roll_fork(None, (*blockNumber).to(), &mut evm_env, &mut tx_env, inner)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(Default::default()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.roll_fork(None, (*blockNumber).to(), evm_env, tx_env, inner) + }) } } impl Cheatcode for rollFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { txHash } = self; persist_caller(ccx); - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - db.roll_fork_to_transaction(None, *txHash, &mut evm_env, &mut tx_env, inner)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(Default::default()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.roll_fork_to_transaction(None, *txHash, evm_env, tx_env, inner) + }) } } impl Cheatcode for rollFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId, blockNumber } = self; persist_caller(ccx); - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - db.roll_fork(Some(*forkId), (*blockNumber).to(), &mut evm_env, &mut tx_env, inner)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(Default::default()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.roll_fork(Some(*forkId), (*blockNumber).to(), evm_env, tx_env, inner) + }) } } impl Cheatcode for rollFork_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId, txHash } = self; persist_caller(ccx); - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - db.roll_fork_to_transaction(Some(*forkId), *txHash, &mut evm_env, &mut tx_env, inner)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(Default::default()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.roll_fork_to_transaction(Some(*forkId), *txHash, evm_env, tx_env, inner) + }) } } impl Cheatcode for selectForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId } = self; persist_caller(ccx); check_broadcast(ccx.state)?; - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - db.select_fork(*forkId, &mut evm_env, &mut tx_env, inner)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(Default::default()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.select_fork(*forkId, evm_env, tx_env, inner) + }) } } impl Cheatcode for transact_0Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -151,7 +132,7 @@ impl Cheatcode for transact_0Call { } impl Cheatcode for transact_1Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -346,7 +327,7 @@ impl Cheatcode for getRawBlockHeaderCall { } /// Creates and then also selects the new fork -fn create_select_fork( +fn create_select_fork( ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, @@ -354,15 +335,13 @@ fn create_select_fork( check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, block)?; - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - let id = db.create_select_fork(fork, &mut evm_env, &mut tx_env, inner)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(id.abi_encode()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.create_select_fork(fork, evm_env, tx_env, inner) + }) } /// Creates a new fork -fn create_fork>( +fn create_fork( ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, @@ -373,7 +352,7 @@ fn create_fork>( } /// Creates and then also selects the new fork at the given transaction -fn create_select_fork_at_transaction( +fn create_select_fork_at_transaction( ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, transaction: &B256, @@ -381,16 +360,13 @@ fn create_select_fork_at_transaction( check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, None)?; - let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); - let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); - let id = - db.create_select_fork_at_transaction(fork, &mut evm_env, &mut tx_env, inner, *transaction)?; - Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); - Ok(id.abi_encode()) + fork_env_op(ccx, |db, evm_env, tx_env, inner| { + db.create_select_fork_at_transaction(fork, evm_env, tx_env, inner, *transaction) + }) } /// Creates a new fork at the given transaction -fn create_fork_at_transaction>( +fn create_fork_at_transaction( ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, transaction: &B256, @@ -401,7 +377,7 @@ fn create_fork_at_transaction>( } /// Creates the request object for a new fork request -fn create_fork_request>( +fn create_fork_request( ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, @@ -421,15 +397,33 @@ fn create_fork_request>( enable_caching: !ccx.state.config.no_storage_caching && ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), url, - evm_env: EvmEnv { - cfg_env: ccx.ecx.cfg_mut().clone(), - block_env: ccx.ecx.block_mut().clone(), - }, + evm_env: ccx.ecx.evm_clone(), evm_opts, }; Ok(fork) } +/// Clones the EVM and tx environments, runs a fork operation that may modify them, then writes +/// them back. This is the common pattern for all fork-switching cheatcodes (rollFork, selectFork, +/// createSelectFork). +fn fork_env_op( + ccx: &mut CheatsCtxt<'_, CTX>, + f: impl FnOnce( + &mut dyn DatabaseExt, + &mut EvmEnv, + &mut CTX::Tx, + &mut JournaledState, + ) -> eyre::Result, +) -> Result { + let mut evm_env = ccx.ecx.evm_clone(); + let mut tx_env = ccx.ecx.tx_clone(); + let (db, inner) = ccx.ecx.db_journal_inner_mut(); + let result = f(db, &mut evm_env, &mut tx_env, inner)?; + ccx.ecx.set_evm(evm_env); + ccx.ecx.set_tx(tx_env); + Ok(result.abi_encode()) +} + fn check_broadcast(state: &Cheatcodes) -> Result<()> { if state.broadcast.is_none() { Ok(()) @@ -438,7 +432,7 @@ fn check_broadcast(state: &Cheatcodes) -> Result<()> { } } -fn transact>( +fn transact( ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, transaction: B256, diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index f599b3f09a325..1bfc95b789ea3 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -2,19 +2,18 @@ use super::string::parse; use crate::{ - Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*, inspector::exec_create, + Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, EthCheatCtx, Result, Vm::*, + inspector::exec_create, }; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; -use alloy_network::ReceiptResponse; +use alloy_network::{Ethereum, Network, ReceiptResponse}; use alloy_primitives::{Bytes, U256, hex, map::Entry}; -use alloy_rpc_types::TransactionReceipt; use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; use forge_script_sequence::{BroadcastReader, TransactionWithMetadata}; use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; -use foundry_evm_core::{backend::FoundryJournalExt, env::FoundryContextExt}; use revm::{ context::{Cfg, ContextTr, CreateScheme, JournalTr}, interpreter::CreateInputs, @@ -88,6 +87,19 @@ impl Cheatcode for projectRootCall { } } +impl Cheatcode for currentFilePathCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self {} = self; + let artifact = state + .config + .running_artifact + .as_ref() + .ok_or_else(|| fmt_err!("no running contract found"))?; + let relative = artifact.source.strip_prefix(&state.config.root).unwrap_or(&artifact.source); + Ok(relative.display().to_string().abi_encode()) + } +} + impl Cheatcode for unixTimeCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self {} = self; @@ -299,7 +311,7 @@ impl Cheatcode for getDeployedCodeCall { } impl Cheatcode for deployCode_0Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -310,7 +322,7 @@ impl Cheatcode for deployCode_0Call { } impl Cheatcode for deployCode_1Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -321,7 +333,7 @@ impl Cheatcode for deployCode_1Call { } impl Cheatcode for deployCode_2Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -332,7 +344,7 @@ impl Cheatcode for deployCode_2Call { } impl Cheatcode for deployCode_3Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -343,7 +355,7 @@ impl Cheatcode for deployCode_3Call { } impl Cheatcode for deployCode_4Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -354,7 +366,7 @@ impl Cheatcode for deployCode_4Call { } impl Cheatcode for deployCode_5Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -365,7 +377,7 @@ impl Cheatcode for deployCode_5Call { } impl Cheatcode for deployCode_6Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -376,7 +388,7 @@ impl Cheatcode for deployCode_6Call { } impl Cheatcode for deployCode_7Call { - fn apply_full>( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -388,7 +400,7 @@ impl Cheatcode for deployCode_7Call { /// Helper function to deploy contract from artifact code. /// Uses CREATE2 scheme if salt specified. -fn deploy_code>( +fn deploy_code( ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, path: &str, @@ -785,7 +797,7 @@ impl Cheatcode for getBroadcasts_0Call { let reader = BroadcastReader::new(contractName.clone(), *chainId, &state.config.broadcast)? .with_tx_type(map_broadcast_tx_type(*txType)); - let broadcasts = reader.read()?; + let broadcasts = reader.read::()?; let summaries = broadcasts .into_iter() @@ -805,7 +817,7 @@ impl Cheatcode for getBroadcasts_1Call { let reader = BroadcastReader::new(contractName.clone(), *chainId, &state.config.broadcast)?; - let broadcasts = reader.read()?; + let broadcasts = reader.read::()?; let summaries = broadcasts .into_iter() @@ -858,7 +870,7 @@ impl Cheatcode for getDeploymentsCall { .with_tx_type(CallKind::Create) .with_tx_type(CallKind::Create2); - let broadcasts = reader.read()?; + let broadcasts = reader.read::()?; let summaries = broadcasts .into_iter() @@ -884,15 +896,15 @@ fn map_broadcast_tx_type(tx_type: BroadcastTxType) -> CallKind { } } -fn parse_broadcast_results( - results: Vec<(TransactionWithMetadata, TransactionReceipt)>, +fn parse_broadcast_results( + results: Vec<(TransactionWithMetadata, N::ReceiptResponse)>, ) -> Vec { results .into_iter() .map(|(tx, receipt)| BroadcastTxSummary { txHash: receipt.transaction_hash(), blockNumber: receipt.block_number().unwrap_or_default(), - txType: match tx.opcode { + txType: match tx.call_kind { CallKind::Call => BroadcastTxType::Call, CallKind::Create => BroadcastTxType::Create, CallKind::Create2 => BroadcastTxType::Create2, @@ -916,7 +928,7 @@ fn latest_broadcast( reader = reader.with_tx_type(filter); } - let broadcast = reader.read_latest()?; + let broadcast = reader.read_latest::()?; let results = reader.into_tx_receipts(broadcast); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 0e5310c9e0c62..2a7307f6ee584 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -21,55 +21,54 @@ use crate::{ utils::IgnoredTraces, }; use alloy_consensus::BlobTransactionSidecarVariant; -use alloy_network::TransactionBuilder4844; +use alloy_network::{Ethereum, Network, TransactionBuilder}; use alloy_primitives::{ Address, B256, Bytes, Log, TxKind, U256, hex, map::{AddressHashMap, HashMap, HashSet}, }; -use alloy_rpc_types::{ - AccessList, - request::{TransactionInput, TransactionRequest}, -}; +use alloy_rpc_types::{AccessList, TransactionRequest}; use alloy_sol_types::{SolCall, SolInterface, SolValue}; use foundry_common::{ SELECTOR_LEN, TransactionMaybeSigned, mapping_slots::{MappingSlots, step as mapping_step}, }; use foundry_evm_core::{ - Breakpoints, Env, EvmEnv, FoundryInspectorExt, FoundryTransaction, + Breakpoints, EthCheatCtx, EvmEnv, FoundryTransaction, InspectorExt, abi::Vm::stopExpectSafeMemoryCall, - backend::{DatabaseError, DatabaseExt, FoundryJournalExt, RevertDiagnostic}, + backend::{DatabaseError, DatabaseExt, RevertDiagnostic}, constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, env::FoundryContextExt, - evm::{NestedEvm, new_evm_with_inspector, with_cloned_context}, + evm::{NestedEvm, NestedEvmClosure, new_revm_with_inspector, with_cloned_context}, }; use foundry_evm_traces::{ TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier, }; +use foundry_primitives::FoundryTransactionBuilder; use foundry_wallets::wallet_multi::MultiWallet; use itertools::Itertools; use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; use rand::Rng; use revm::{ - Inspector, + Context, Inspector, bytecode::opcode as op, context::{ - BlockEnv, Cfg, ContextTr, JournalTr, Transaction, TransactionType, TxEnv, result::EVMError, + BlockEnv, Cfg, CfgEnv, ContextTr, JournalTr, Transaction, TransactionType, TxEnv, + result::EVMError, }, context_interface::{CreateScheme, transaction::SignedAuthorization}, - handler::FrameResult, + handler::{EvmTr, FrameResult}, inspector::JournalExt, interpreter::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, interpreter_types::{Jumps, LoopControl, MemoryTr}, }, - primitives::hardfork::SpecId, }; use serde_json::Value; use std::{ cmp::max, collections::{BTreeMap, VecDeque}, + fmt::Debug, fs::File, io::BufReader, ops::Range, @@ -82,33 +81,15 @@ mod utils; pub mod analysis; pub use analysis::CheatcodeAnalysis; -/// Bounds for the generic `Inspector` impl on `Cheatcodes`. -/// -/// Shorthand used internally to avoid repeating the full where-clause. -/// Any `EthEvmContext<&mut dyn DatabaseExt>` satisfies these bounds, so all -/// existing call-sites (e.g. `InspectorStackRefMut`) keep working unchanged. -pub trait CheatsCtxExt: FoundryContextExt {} -impl CheatsCtxExt for CTX where - CTX: FoundryContextExt -{ -} - -/// Closure type used by [`CheatcodesExecutor`] methods that run nested EVM operations. -pub type NestedEvmClosure<'a> = - &'a mut dyn FnMut(&mut dyn NestedEvm) -> Result<(), EVMError>; - /// Helper trait for running nested EVM operations from inside cheatcode implementations. -/// -/// The executor assembles the full inspector stack internally and never exposes it across the -/// trait boundary. This keeps the trait free of `InspectorExt` (which is Eth-specific). -pub trait CheatcodesExecutor { +pub trait CheatcodesExecutor { /// Runs a closure with a nested EVM built from the current context. /// The inspector is assembled internally — never exposed to the caller. fn with_nested_evm( &mut self, cheats: &mut Cheatcodes, ecx: &mut CTX, - f: NestedEvmClosure<'_>, + f: NestedEvmClosure<'_, CTX::Tx>, ) -> Result<(), EVMError>; /// Replays a historical transaction on the database. Inspector is assembled internally. @@ -125,23 +106,24 @@ pub trait CheatcodesExecutor { &mut self, cheats: &mut Cheatcodes, ecx: &mut CTX, - tx: &TransactionRequest, + tx: &CTX::Tx, ) -> eyre::Result<()>; /// Runs a closure with a fresh nested EVM built from a raw database and environment. /// Unlike `with_nested_evm`, this does NOT clone from `ecx` and does NOT write back. /// The caller is responsible for state merging. Used by `executeTransactionCall`. + /// Returns the final EVM environment after the closure runs (consumed without cloning). + #[allow(clippy::type_complexity)] fn with_fresh_nested_evm( &mut self, cheats: &mut Cheatcodes, - db: &mut dyn DatabaseExt, - evm_env: EvmEnv, - tx_env: TxEnv, - f: NestedEvmClosure<'_>, - ) -> Result<(), EVMError>; + db: &mut dyn DatabaseExt::Spec>, + evm_env: EvmEnv<::Spec, CTX::Block>, + f: NestedEvmClosure<'_, CTX::Tx>, + ) -> Result::Spec, CTX::Block>, EVMError>; /// Simulates `console.log` invocation. - fn console_log(&mut self, cheats: &mut Cheatcodes, msg: &str); + fn console_log(&mut self, msg: &str); /// Returns a mutable reference to the tracing inspector if it is available. fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> { @@ -155,7 +137,7 @@ pub trait CheatcodesExecutor { } /// Builds a sub-EVM from the current context and executes the given CREATE frame. -pub(crate) fn exec_create>( +pub(crate) fn exec_create( executor: &mut dyn CheatcodesExecutor, inputs: CreateInputs, ccx: &mut CheatsCtxt<'_, CTX>, @@ -186,35 +168,33 @@ pub(crate) fn exec_create>( #[derive(Debug, Default, Clone, Copy)] struct TransparentCheatcodesExecutor; -impl> CheatcodesExecutor - for TransparentCheatcodesExecutor -{ +impl CheatcodesExecutor for TransparentCheatcodesExecutor { fn with_nested_evm( &mut self, cheats: &mut Cheatcodes, ecx: &mut CTX, - f: NestedEvmClosure<'_>, + f: NestedEvmClosure<'_, CTX::Tx>, ) -> Result<(), EVMError> { - with_cloned_context(ecx, |db, evm_env, tx_env, journal_inner| { - let mut evm = new_evm_with_inspector(db, evm_env, tx_env, cheats); + with_cloned_context(ecx, |db, evm_env, journal_inner| { + let mut evm = new_revm_with_inspector(db, evm_env, cheats); *evm.journal_inner_mut() = journal_inner; f(&mut evm)?; - let (sub_evm_env, sub_tx) = evm.to_env(); - let sub_inner = evm.into_context().journaled_state.inner; - Ok(((), sub_evm_env, sub_tx, sub_inner)) + let sub_inner = evm.journaled_state.inner.clone(); + let sub_evm_env = evm.ctx_ref().evm_clone(); + Ok((sub_evm_env, sub_inner)) }) } fn with_fresh_nested_evm( &mut self, cheats: &mut Cheatcodes, - db: &mut dyn DatabaseExt, - evm_env: EvmEnv, - tx_env: TxEnv, - f: NestedEvmClosure<'_>, - ) -> Result<(), EVMError> { - let mut evm = new_evm_with_inspector(db, evm_env, tx_env, cheats); - f(&mut evm) + db: &mut dyn DatabaseExt, + evm_env: EvmEnv, + f: NestedEvmClosure<'_, CTX::Tx>, + ) -> Result, EVMError> { + let mut evm = new_revm_with_inspector(db, evm_env, cheats); + f(&mut evm)?; + Ok(evm.ctx_ref().evm_clone()) } fn transact_on_db( @@ -224,8 +204,9 @@ impl> CheatcodesExecutor fork_id: Option, transaction: B256, ) -> eyre::Result<()> { - let (evm_env, tx_env) = Env::clone_evm_and_tx(ecx); - let (db, inner) = ecx.journal_mut().as_db_and_inner(); + let evm_env = ecx.evm_clone(); + let tx_env = ecx.tx_clone(); + let (db, inner) = ecx.db_journal_inner_mut(); db.transact(fork_id, transaction, evm_env, tx_env, inner, cheats) } @@ -233,14 +214,14 @@ impl> CheatcodesExecutor &mut self, cheats: &mut Cheatcodes, ecx: &mut CTX, - tx: &TransactionRequest, + tx: &CTX::Tx, ) -> eyre::Result<()> { - let (evm_env, tx_env) = Env::clone_evm_and_tx(ecx); - let (db, inner) = ecx.journal_mut().as_db_and_inner(); - db.transact_from_tx(tx, evm_env, tx_env, inner, cheats) + let evm_env = ecx.evm_clone(); + let (db, inner) = ecx.db_journal_inner_mut(); + db.transact_from_tx(tx, evm_env, inner, cheats) } - fn console_log(&mut self, _cheats: &mut Cheatcodes, _msg: &str) {} + fn console_log(&mut self, _msg: &str) {} } macro_rules! try_or_return { @@ -275,11 +256,11 @@ impl TestContext { /// Helps collecting transactions from different forks. #[derive(Clone, Debug)] -pub struct BroadcastableTransaction { +pub struct BroadcastableTransaction { /// The optional RPC URL. pub rpc: Option, /// The transaction to broadcast. - pub transaction: TransactionMaybeSigned, + pub transaction: TransactionMaybeSigned, } #[derive(Clone, Debug, Copy)] @@ -432,7 +413,7 @@ impl ArbitraryStorage { } /// List of transactions that can be broadcasted. -pub type BroadcastableTransactions = VecDeque; +pub type BroadcastableTransactions = VecDeque>; /// An EVM inspector that handles calls to various cheatcodes, each with their own behavior. /// @@ -452,7 +433,10 @@ pub type BroadcastableTransactions = VecDeque; /// cheatcode address: by default, the caller, test contract and newly deployed contracts are /// allowed to execute cheatcodes #[derive(Clone, Debug)] -pub struct Cheatcodes { +pub struct Cheatcodes< + CTX: FoundryContextExt = Context, + N: Network = Ethereum, +> { /// Solar compiler instance, to grant syntactic and semantic analysis capabilities pub analysis: Option, @@ -460,7 +444,7 @@ pub struct Cheatcodes { /// /// Used in the cheatcode handler to overwrite the block environment separately from the /// execution block environment. - pub block: Option, + pub block: Option, /// Currently active EIP-7702 delegations that will be consumed when building the next /// transaction. Set by `vm.attachDelegation()` and consumed via `.take()` during @@ -531,7 +515,7 @@ pub struct Cheatcodes { pub broadcast: Option, /// Scripting based transactions - pub broadcastable_transactions: BroadcastableTransactions, + pub broadcastable_transactions: BroadcastableTransactions, /// Current EIP-2930 access lists. pub access_list: Option, @@ -591,7 +575,7 @@ pub struct Cheatcodes { /// Used to determine whether the broadcasted call has dynamic gas limit. pub dynamic_gas_limit: bool, // Custom execution evm version. - pub execution_evm_version: Option, + pub execution_evm_version: Option, } // This is not derived because calling this in `fn new` with `..Default::default()` creates a second @@ -686,7 +670,7 @@ impl Cheatcodes { } /// Decodes the input data and applies the cheatcode. - fn apply_cheatcode( + fn apply_cheatcode( &mut self, ecx: &mut CTX, call: &CallInputs, @@ -776,7 +760,7 @@ impl Cheatcodes { } } - pub fn call_with_executor( + pub fn call_with_executor( &mut self, ecx: &mut CTX, call: &mut CallInputs, @@ -990,22 +974,22 @@ impl Cheatcodes { }); } - let input = TransactionInput::new(call.input.bytes(ecx)); + let input = call.input.bytes(ecx); let chain_id = ecx.cfg().chain_id(); let rpc = ecx.db().active_fork_url(); let account = ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap(); - let mut tx_req = TransactionRequest { - from: Some(broadcast.new_origin), - to: Some(TxKind::from(Some(call.target_address))), - value: call.transfer_value(), - input, - nonce: Some(account.info.nonce), - chain_id: Some(chain_id), - gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None }, - ..Default::default() - }; + let mut tx_req = TransactionRequest::default() + .with_from(broadcast.new_origin) + .with_to(call.target_address) + .with_value(call.transfer_value().unwrap_or_default()) + .with_input(input) + .with_nonce(account.info.nonce) + .with_chain_id(chain_id); + if is_fixed_gas_limit { + tx_req.set_gas_limit(call.gas_limit) + } let active_delegations = std::mem::take(&mut self.active_delegations); // Set active blob sidecar, if any. @@ -1039,11 +1023,13 @@ impl Cheatcodes { account.info.nonce += 1; } } - tx_req.authorization_list = Some(active_delegations); + tx_req.set_authorization_list(active_delegations); } - self.broadcastable_transactions - .push_back(BroadcastableTransaction { rpc, transaction: tx_req.into() }); + self.broadcastable_transactions.push_back(BroadcastableTransaction { + rpc, + transaction: TransactionMaybeSigned::new(tx_req), + }); debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call"); // Explicitly increment nonce if calls are not isolated. @@ -1193,12 +1179,12 @@ impl Cheatcodes { } } -impl Inspector for Cheatcodes { +impl Inspector for Cheatcodes { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { // 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() { - *ecx.block_mut() = block; + ecx.set_block(block); } if let Some(gas_price) = self.gas_price.take() { ecx.tx_mut().set_gas_price(gas_price); @@ -1787,15 +1773,14 @@ impl Inspector for Cheatcodes { let account = &ecx.journal().evm_state()[&broadcast.new_origin]; self.broadcastable_transactions.push_back(BroadcastableTransaction { rpc, - transaction: TransactionRequest { - from: Some(broadcast.new_origin), - to: None, - value: Some(input.value()), - input: TransactionInput::new(input.init_code()), - nonce: Some(account.info.nonce), - ..Default::default() - } - .into(), + transaction: TransactionMaybeSigned::new( + TransactionRequest::default() + .with_from(broadcast.new_origin) + .with_kind(TxKind::Create) + .with_value(input.value()) + .with_input(input.init_code()) + .with_nonce(account.info.nonce), + ), }); input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create)); @@ -1968,7 +1953,7 @@ impl Inspector for Cheatcodes { } } -impl FoundryInspectorExt for Cheatcodes { +impl InspectorExt for Cheatcodes { fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool { if let CreateScheme::Create2 { .. } = inputs.scheme() { let target_depth = if let Some(prank) = &self.get_prank(depth) { @@ -2600,7 +2585,7 @@ fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str { } /// Dispatches the cheatcode call to the appropriate function. -fn apply_dispatch( +fn apply_dispatch( calls: &Vm::VmCalls, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 02fa0777def82..bae97dab5b7a2 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -352,11 +352,15 @@ impl Cheatcode for writeJson_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json: value, path, valueKey } = self; - // Read, parse, and update the JSON object + // Read, parse, and update the JSON object. + // If the file doesn't exist, start with an empty JSON object so the file is created. let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; - let data_string = fs::locked_read_to_string(&data_path)?; - let mut data = - serde_json::from_str(&data_string).unwrap_or_else(|_| Value::String(data_string)); + let mut data = if data_path.exists() { + let data_string = fs::locked_read_to_string(&data_path)?; + serde_json::from_str(&data_string).unwrap_or_else(|_| Value::String(data_string)) + } else { + Value::Object(Default::default()) + }; upsert_json_value(&mut data, value, valueKey)?; // Write the updated content back to the file diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 7775d61841b2c..3c609cc2dbf19 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -22,9 +22,9 @@ use revm::context::{ContextTr, JournalTr}; pub use Vm::ForgeContext; pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; +pub use foundry_evm_core::{EthCheatCtx, evm::NestedEvmClosure}; pub use inspector::{ BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, CheatcodesExecutor, - CheatsCtxExt, NestedEvmClosure, }; pub use spec::{CheatcodeDef, Vm}; @@ -77,7 +77,7 @@ pub(crate) trait Cheatcode: CheatcodeDef { /// /// Implement this function if you need access to the EVM data. #[inline(always)] - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { self.apply(ccx.state) } @@ -85,7 +85,7 @@ pub(crate) trait Cheatcode: CheatcodeDef { /// /// Implement this function if you need access to the executor. #[inline(always)] - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 1284f6d51a230..9f5c88d57405d 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -384,18 +384,13 @@ impl Wallets { /// Locks inner Mutex and returns all signer addresses in the [MultiWallet]. pub fn signers(&self) -> Result> { - let mut wallets = self.inner.lock(); - let (signers, browser) = wallets.multi_wallet.signers()?; - Ok(signers.keys().copied().chain(browser.map(|b| b.address())).collect()) + Ok(self.inner.lock().multi_wallet.signers()?.keys().copied().collect()) } /// Number of signers in the [MultiWallet]. pub fn len(&self) -> usize { let mut inner = self.inner.lock(); - match inner.multi_wallet.signers() { - Ok((signers, browser)) => signers.len() + usize::from(browser.is_some()), - Err(_) => 0, - } + inner.multi_wallet.signers().map_or(0, |signers| signers.len()) } /// Whether the [MultiWallet] is empty. @@ -424,7 +419,7 @@ fn broadcast>( if let Some(provided_sender) = wallets.provided_sender { new_origin = Some(provided_sender); } else { - let (signers, _) = wallets.multi_wallet.signers()?; + let signers = wallets.multi_wallet.signers()?; if signers.len() == 1 { let address = signers.keys().next().unwrap(); new_origin = Some(*address); diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index c6441007ab0ce..5b5491afb58a8 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -1,15 +1,11 @@ //! Implementations of [`Testing`](spec::Group::Testing) cheatcodes. -use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, EthCheatCtx, Result, Vm::*}; use alloy_chains::Chain as AlloyChain; use alloy_primitives::{Address, U256}; use alloy_sol_types::SolValue; use foundry_common::version::SEMVER_VERSION; -use foundry_evm_core::{ - backend::{DatabaseExt, FoundryJournalExt}, - constants::MAGIC_SKIP, - env::FoundryContextExt, -}; +use foundry_evm_core::constants::MAGIC_SKIP; use revm::context::{ContextTr, JournalTr}; use std::str::FromStr; @@ -71,20 +67,14 @@ impl Cheatcode for sleepCall { } impl Cheatcode for skip_0Call { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { skipTest } = *self; skip_1Call { skipTest, reason: String::new() }.apply_stateful(ccx) } } impl Cheatcode for skip_1Call { - fn apply_stateful>( - &self, - ccx: &mut CheatsCtxt<'_, CTX>, - ) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { skipTest, reason } = self; if *skipTest { // Skip should not work if called deeper than at test level. diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index dd8c5ae94b853..88f38b700e993 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -1,4 +1,4 @@ -use crate::{CheatcodesExecutor, CheatsCtxExt, CheatsCtxt, Result, Vm::*}; +use crate::{CheatcodesExecutor, CheatsCtxt, EthCheatCtx, Result, Vm::*}; use alloy_primitives::{I256, U256, U512}; use foundry_evm_core::{ abi::console::{format_units_int, format_units_uint}, @@ -211,7 +211,7 @@ fn handle_assertion_result_mono>( if ccx.state.config.assertions_revert { Err(msg.into_owned().into()) } else { - executor.console_log(ccx.state, &msg); + executor.console_log(&msg); ccx.ecx.journal_mut().sstore(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT, U256::from(1))?; Ok(Default::default()) } @@ -246,7 +246,7 @@ macro_rules! impl_assertions { (@impl $no_error:ident, $with_error:ident, ($($arg:ident),*), $body:expr, $error_formatter:expr) => { impl crate::Cheatcode for $no_error { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -260,7 +260,7 @@ macro_rules! impl_assertions { } impl crate::Cheatcode for $with_error { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, diff --git a/crates/cheatcodes/src/test/revert_handlers.rs b/crates/cheatcodes/src/test/revert_handlers.rs index 6b1b4f6d9ceab..3b0610ebf07a4 100644 --- a/crates/cheatcodes/src/test/revert_handlers.rs +++ b/crates/cheatcodes/src/test/revert_handlers.rs @@ -102,7 +102,8 @@ fn handle_revert( // If expected reason is `Error(string)` then decode and compare with actual revert. // See - if let Ok(e) = get_error("Error(string)") + if expected_reason.len() >= 4 + && let Ok(e) = get_error("Error(string)") && let Ok(dec) = e.decode_error(expected_reason) && let Some(DynSolValue::String(revert_str)) = dec.body.first() && revert_str.as_str() == String::from_utf8_lossy(&actual_revert) diff --git a/crates/cheatcodes/src/toml.rs b/crates/cheatcodes/src/toml.rs index 7fb8210c88d66..98b46a33f9f1d 100644 --- a/crates/cheatcodes/src/toml.rs +++ b/crates/cheatcodes/src/toml.rs @@ -205,13 +205,15 @@ impl Cheatcode for writeToml_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json: value, path, valueKey } = self; - // Read and parse the TOML file + // Read and parse the TOML file. + // If the file doesn't exist, start with an empty object so the file is created. let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; - let toml_data = fs::locked_read_to_string(&data_path)?; - - // Convert to JSON and update the object - let mut json_data: JsonValue = - toml::from_str(&toml_data).map_err(|e| fmt_err!("failed parsing TOML: {e}"))?; + let mut json_data: JsonValue = if data_path.exists() { + let toml_data = fs::locked_read_to_string(&data_path)?; + toml::from_str(&toml_data).map_err(|e| fmt_err!("failed parsing TOML: {e}"))? + } else { + JsonValue::Object(Default::default()) + }; upsert_json_value(&mut json_data, value, valueKey)?; // Serialize back to TOML and write the updated content back to the file diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 491a321525d9e..f025410e3e458 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -177,7 +177,7 @@ impl Cheatcode for randomBytes8Call { } impl Cheatcode for pauseTracingCall { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, @@ -200,7 +200,7 @@ impl Cheatcode for pauseTracingCall { } impl Cheatcode for resumeTracingCall { - fn apply_full( + fn apply_full( &self, ccx: &mut CheatsCtxt<'_, CTX>, executor: &mut dyn CheatcodesExecutor, diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 8ebeca5ee2549..9fa67804352e7 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -199,13 +199,13 @@ impl SessionSource { } async fn build_runner(&mut self, final_pc: usize) -> Result { - let env = self.config.evm_opts.env().await?; + let (evm_env, tx_env) = self.config.evm_opts.env().await?; let backend = match self.config.backend.clone() { Some(backend) => backend, None => { let fork = - self.config.evm_opts.get_fork(&self.config.foundry_config, env.evm_env.clone()); + self.config.evm_opts.get_fork(&self.config.foundry_config, evm_env.clone()); let backend = Backend::spawn(fork)?; self.config.backend = Some(backend.clone()); backend @@ -231,7 +231,7 @@ impl SessionSource { .gas_limit(self.config.evm_opts.gas_limit()) .spec_id(self.config.foundry_config.evm_spec_id()) .legacy_assertions(self.config.foundry_config.legacy_assertions) - .build(env, backend); + .build(evm_env, tx_env, backend); Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone())) } diff --git a/crates/cli/src/opts/mod.rs b/crates/cli/src/opts/mod.rs index c173ebe3fd5f8..ce1234528a94b 100644 --- a/crates/cli/src/opts/mod.rs +++ b/crates/cli/src/opts/mod.rs @@ -3,6 +3,7 @@ mod chain; mod dependency; mod evm; mod global; +mod network; mod rpc; mod tempo; mod transaction; @@ -12,6 +13,7 @@ pub use chain::*; pub use dependency::*; pub use evm::*; pub use global::*; +pub use network::*; pub use rpc::*; pub use tempo::*; pub use transaction::*; diff --git a/crates/cli/src/opts/network.rs b/crates/cli/src/opts/network.rs new file mode 100644 index 0000000000000..f78af85842c95 --- /dev/null +++ b/crates/cli/src/opts/network.rs @@ -0,0 +1,11 @@ +/// Network selection, defaulting to Ethereum +#[derive(Clone, Debug, Default, clap::ValueEnum)] +pub enum NetworkVariant { + /// Ethereum (default) + #[default] + Ethereum, + /// Optimism / OP-stack + Optimism, + /// Tempo + Tempo, +} diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 722a80fbe2d3c..2dc021f12b4b1 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -120,7 +120,7 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { | NamedChain::AcalaMandalaTestnet | NamedChain::AcalaTestnet | NamedChain::Etherlink - | NamedChain::EtherlinkTestnet + | NamedChain::EtherlinkShadownet | NamedChain::Karura | NamedChain::KaruraTestnet | NamedChain::Mantle diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 99156568b8c37..852b1ab2141ea 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,6 +17,7 @@ foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-common-fmt.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true +foundry-primitives.workspace = true alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index b62dd39ed8ceb..36407b3f0c9f2 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -519,8 +519,8 @@ validAfter {}", self.nonce_key.pretty(), self.nonce.pretty(), self.fee_payer_signature.pretty(), - self.valid_after.pretty(), self.valid_before.pretty(), + self.valid_after.pretty(), ) } } @@ -815,24 +815,37 @@ blobGasUsed {}", pub trait UIfmtHeaderExt { fn size_pretty(&self) -> String; + fn total_difficulty_pretty(&self) -> String; } impl UIfmtHeaderExt for Header { fn size_pretty(&self) -> String { self.size.pretty() } + + fn total_difficulty_pretty(&self) -> String { + self.total_difficulty.unwrap_or_else(|| self.difficulty()).pretty() + } } impl UIfmtHeaderExt for AnyRpcHeader { fn size_pretty(&self) -> String { self.size.pretty() } + + fn total_difficulty_pretty(&self) -> String { + self.total_difficulty.unwrap_or_else(|| self.difficulty()).pretty() + } } impl UIfmtHeaderExt for TempoHeaderResponse { fn size_pretty(&self) -> String { self.inner.size.pretty() } + + fn total_difficulty_pretty(&self) -> String { + self.inner.total_difficulty.unwrap_or_else(|| self.inner.difficulty()).pretty() + } } pub trait UIfmtSignatureExt { @@ -1054,7 +1067,7 @@ where "size" => Some(block.header().size_pretty()), "stateRoot" | "state_root" => Some(block.header().state_root().pretty()), "timestamp" => Some(block.header().timestamp().pretty()), - "totalDifficulty" | "total_difficulty" => Some(block.header().difficulty().pretty()), + "totalDifficulty" | "total_difficulty" => Some(block.header().total_difficulty_pretty()), "blobGasUsed" | "blob_gas_used" => Some(block.header().blob_gas_used().pretty()), "excessBlobGas" | "excess_blob_gas" => Some(block.header().excess_blob_gas().pretty()), "requestsHash" | "requests_hash" => Some(block.header().requests_hash().pretty()), @@ -1143,7 +1156,7 @@ requestsHash {}", header.timestamp().pretty(), fmt_timestamp(header.timestamp()), header.withdrawals_root().pretty(), - header.difficulty().pretty(), + header.total_difficulty_pretty(), header.blob_gas_used().pretty(), header.excess_blob_gas().pretty(), header.requests_hash().pretty(), @@ -1638,7 +1651,7 @@ yParity 0" "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", - "difficulty": "0x27f07", + "difficulty": "0x1", "totalDifficulty": "0x27f07", "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", "size": "0x27f07", @@ -1660,7 +1673,7 @@ yParity 0" get_pretty_block_attr::(&block, "baseFeePerGas") ); assert_eq!( - Some("163591".to_string()), + Some("1".to_string()), get_pretty_block_attr::(&block, "difficulty") ); assert_eq!( @@ -1728,6 +1741,10 @@ yParity 0" Some("163591".to_string()), get_pretty_block_attr::(&block, "totalDifficulty") ); + + let pretty = pretty_generic_header_response(block.header()); + assert!(pretty.contains("difficulty 1"), "{pretty}"); + assert!(pretty.contains("totalDifficulty 163591"), "{pretty}"); } #[test] diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 893e7806d1dbc..c0bdb636796d4 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -1,18 +1,17 @@ //! Wrappers for transactions. -use alloy_consensus::{Transaction, TxEnvelope, transaction::SignerRecoverable}; +use alloy_consensus::{Transaction, transaction::SignerRecoverable}; use alloy_eips::eip7702::SignedAuthorization; -use alloy_network::{ - AnyTransactionReceipt, Ethereum, Network, TransactionBuilder7702, TransactionResponse, -}; -use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_network::{AnyTransactionReceipt, Network, TransactionResponse}; +use alloy_primitives::{Address, Bytes, U256}; use alloy_provider::{ Provider, network::{AnyNetwork, ReceiptResponse, TransactionBuilder}, }; -use alloy_rpc_types::{BlockId, TransactionRequest}; +use alloy_rpc_types::BlockId; use eyre::Result; use foundry_common_fmt::{UIfmt, UIfmtReceiptExt, get_pretty_receipt_attr}; +use foundry_primitives::FoundryTransactionBuilder; use serde::{Deserialize, Serialize}; /// Helper type to carry a transaction along with an optional revert reason @@ -93,7 +92,11 @@ revertReason {}", } } -impl UIfmt for TransactionMaybeSigned { +impl UIfmt for TransactionMaybeSigned +where + N::TxEnvelope: UIfmt, + N::TransactionRequest: FoundryTransactionBuilder, +{ fn pretty(&self) -> String { match self { Self::Signed { tx, .. } => tx.pretty(), @@ -111,22 +114,22 @@ nonce {} to {} type {} value {}", - tx.access_list + tx.access_list() .as_ref() .map(|a| a.iter().collect::>()) .unwrap_or_default() .pretty(), - tx.chain_id.pretty(), + tx.chain_id().pretty(), tx.gas_limit().unwrap_or_default(), - tx.gas_price.pretty(), - tx.input.input.pretty(), - tx.max_fee_per_blob_gas.pretty(), - tx.max_fee_per_gas.pretty(), - tx.max_priority_fee_per_gas.pretty(), - tx.nonce.pretty(), - tx.to.as_ref().map(|a| a.to()).unwrap_or_default().pretty(), - tx.transaction_type.unwrap_or_default(), - tx.value.pretty(), + tx.gas_price().pretty(), + tx.input().pretty(), + tx.max_fee_per_blob_gas().pretty(), + tx.max_fee_per_gas().pretty(), + tx.max_priority_fee_per_gas().pretty(), + tx.nonce().pretty(), + tx.to().pretty(), + tx.output_tx_type(), + tx.value().pretty(), ), } } @@ -157,11 +160,11 @@ where } /// Used for broadcasting transactions -/// A transaction can either be a [`TransactionRequest`] waiting to be signed -/// or a [`TxEnvelope`], already signed +/// A transaction can either be a `TransactionRequest` waiting to be signed +/// or a `TxEnvelope`, already signed #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(untagged)] -pub enum TransactionMaybeSigned { +pub enum TransactionMaybeSigned { Signed { #[serde(flatten)] tx: N::TxEnvelope, @@ -212,10 +215,10 @@ impl TransactionMaybeSigned { } } - pub fn to(&self) -> Option { + pub fn to(&self) -> Option
{ match self { - Self::Signed { tx, .. } => Some(tx.kind()), - Self::Unsigned(tx) => tx.kind(), + Self::Signed { tx, .. } => tx.to(), + Self::Unsigned(tx) => tx.to(), } } @@ -242,7 +245,7 @@ impl TransactionMaybeSigned { pub fn authorization_list(&self) -> Option> where - N::TransactionRequest: TransactionBuilder7702, + N::TransactionRequest: FoundryTransactionBuilder, { match self { Self::Signed { tx, .. } => tx.authorization_list().map(|auths| auths.to_vec()), @@ -252,20 +255,6 @@ impl TransactionMaybeSigned { } } -impl From for TransactionMaybeSigned { - fn from(tx: TransactionRequest) -> Self { - Self::new(tx) - } -} - -impl TryFrom for TransactionMaybeSigned { - type Error = alloy_consensus::crypto::RecoveryError; - - fn try_from(tx: TxEnvelope) -> core::result::Result { - Self::new_signed(tx) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index cb964f97f68be..d5bb4baa980ab 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -71,16 +71,25 @@ pub struct FuzzDictionaryConfig { /// Once the fuzzer exceeds this limit, it will start evicting random entries /// /// This limit is put in place to prevent memory blowup. - #[serde(deserialize_with = "crate::deserialize_usize_or_max")] + #[serde( + deserialize_with = "crate::deserialize_usize_or_max", + serialize_with = "crate::serialize_usize_or_max" + )] pub max_fuzz_dictionary_addresses: usize, /// How many values to record at most. /// Once the fuzzer exceeds this limit, it will start evicting random entries - #[serde(deserialize_with = "crate::deserialize_usize_or_max")] + #[serde( + deserialize_with = "crate::deserialize_usize_or_max", + serialize_with = "crate::serialize_usize_or_max" + )] pub max_fuzz_dictionary_values: usize, /// How many literal values to seed from the AST, at most. /// /// This value is independent from the max amount of addresses and values. - #[serde(deserialize_with = "crate::deserialize_usize_or_max")] + #[serde( + deserialize_with = "crate::deserialize_usize_or_max", + serialize_with = "crate::serialize_usize_or_max" + )] pub max_fuzz_dictionary_literals: usize, } diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index 624dd5a35f81b..8570397a37d20 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -213,6 +213,21 @@ where deserialize_u64_or_max(deserializer)?.try_into().map_err(D::Error::custom) } +/// Serialize a `usize` as `"max"` if it equals `usize::MAX`, as a string if it exceeds +/// `i64::MAX` (TOML integer limit), or as a plain number otherwise. +pub(crate) fn serialize_usize_or_max(value: &usize, serializer: S) -> Result +where + S: Serializer, +{ + if *value == usize::MAX { + serializer.serialize_str("max") + } else if *value > i64::MAX as usize { + serializer.serialize_str(&value.to_string()) + } else { + serializer.serialize_u64(*value as u64) + } +} + /// Deserialize into `U256` from either a `u64`, a `U256` hex string, or a decimal string. pub fn deserialize_u64_to_u256<'de, D>(deserializer: D) -> Result where diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 18d9458d46c1b..c3db588640cdc 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -39,6 +39,7 @@ alloy-consensus.workspace = true alloy-rpc-types.workspace = true alloy-sol-types.workspace = true foundry-fork-db.workspace = true +op-alloy-rpc-types.workspace = true revm = { workspace = true, features = [ "std", @@ -68,4 +69,6 @@ tracing.workspace = true url.workspace = true [dev-dependencies] +alloy-serde.workspace = true +op-alloy-consensus.workspace = true foundry-test-utils.workspace = true diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 8c80b21121c37..b4a484304c9a0 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -2,17 +2,16 @@ use super::BackendError; use crate::{ - Env, InspectorExt, + FoundryInspectorExt, backend::{ Backend, DatabaseExt, JournaledState, LocalForkId, RevertStateSnapshotAction, diagnostic::RevertDiagnostic, }, fork::{CreateFork, ForkId}, }; -use alloy_evm::{Evm, EvmEnv}; +use alloy_evm::{Evm, EvmEnv, eth::EthEvmContext}; use alloy_genesis::GenesisAccount; -use alloy_primitives::{Address, B256, U256}; -use alloy_rpc_types::TransactionRequest; +use alloy_primitives::{Address, B256, TxKind, U256}; use eyre::WrapErr; use foundry_fork_db::DatabaseError; use revm::{ @@ -48,15 +47,15 @@ pub struct CowBackend<'a> { /// /// No calls on the `CowBackend` will ever persistently modify the `backend`'s state. pub backend: Cow<'a, Backend>, - /// The [SpecId] to initialize the backend with on first mutable access. + /// Pending initialization params for the backend on first mutable access. /// `None` means the backend has already been initialized for the current call. - spec_id: Option, + pending_init: Option<(SpecId, Address, TxKind)>, } impl<'a> CowBackend<'a> { /// Creates a new `CowBackend` with the given `Backend`. pub fn new_borrowed(backend: &'a Backend) -> Self { - Self { backend: Cow::Borrowed(backend), spec_id: Some(SpecId::default()) } + Self { backend: Cow::Borrowed(backend), pending_init: None } } /// Executes the configured transaction of the `env` without committing state changes @@ -64,25 +63,22 @@ 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. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect( + pub fn inspect FoundryInspectorExt>>( &mut self, - env: &mut Env, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, 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.spec_id = Some(env.evm_env.cfg_env.spec); + self.pending_init = Some((evm_env.cfg_env.spec, tx_env.caller, tx_env.kind)); - let mut evm = crate::evm::new_evm_with_inspector( - self, - env.evm_env.clone(), - env.tx.clone(), - inspector, - ); + let mut evm = crate::evm::new_eth_evm_with_inspector(self, evm_env.clone(), inspector); - let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?; + let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?; - *env = Env::from(evm.cfg.clone(), evm.block.clone(), evm.tx.clone()); + *evm_env = EvmEnv::new(evm.cfg.clone(), evm.block.clone()); + *tx_env = evm.tx.clone(); Ok(res) } @@ -97,12 +93,10 @@ impl<'a> CowBackend<'a> { /// Returns a mutable instance of the Backend. /// /// If this is the first time this is called, the backed is cloned and initialized. - fn backend_mut(&mut self, evm_env: &EvmEnv, tx_env: &TxEnv) -> &mut Backend { - if let Some(spec_id) = self.spec_id.take() { + fn backend_mut(&mut self) -> &mut Backend { + if let Some((spec_id, caller, tx_kind)) = self.pending_init.take() { let backend = self.backend.to_mut(); - let mut env = Env { evm_env: evm_env.clone(), tx: tx_env.clone() }; - env.evm_env.cfg_env.spec = spec_id; - backend.initialize(&env); + backend.initialize(spec_id, caller, tx_kind); return backend; } self.backend.to_mut() @@ -110,7 +104,7 @@ impl<'a> CowBackend<'a> { /// Returns a mutable instance of the Backend if it is initialized. fn initialized_backend_mut(&mut self) -> Option<&mut Backend> { - if self.spec_id.is_none() { + if self.pending_init.is_none() { return Some(self.backend.to_mut()); } None @@ -119,7 +113,7 @@ impl<'a> CowBackend<'a> { impl DatabaseExt for CowBackend<'_> { fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256 { - self.backend_mut(evm_env, &TxEnv::default()).snapshot_state(journaled_state, evm_env) + self.backend_mut().snapshot_state(journaled_state, evm_env) } fn revert_state( @@ -130,7 +124,7 @@ impl DatabaseExt for CowBackend<'_> { tx_env: &mut TxEnv, action: RevertStateSnapshotAction, ) -> Option { - self.backend_mut(evm_env, tx_env).revert_state(id, journaled_state, evm_env, tx_env, action) + self.backend_mut().revert_state(id, journaled_state, evm_env, tx_env, action) } fn delete_state_snapshot(&mut self, id: U256) -> bool { @@ -166,7 +160,7 @@ impl DatabaseExt for CowBackend<'_> { tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - self.backend_mut(evm_env, tx_env).select_fork(id, evm_env, tx_env, journaled_state) + self.backend_mut().select_fork(id, evm_env, tx_env, journaled_state) } fn roll_fork( @@ -177,13 +171,7 @@ impl DatabaseExt for CowBackend<'_> { tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - self.backend_mut(evm_env, tx_env).roll_fork( - id, - block_number, - evm_env, - tx_env, - journaled_state, - ) + self.backend_mut().roll_fork(id, block_number, evm_env, tx_env, journaled_state) } fn roll_fork_to_transaction( @@ -194,7 +182,7 @@ impl DatabaseExt for CowBackend<'_> { tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - self.backend_mut(evm_env, tx_env).roll_fork_to_transaction( + self.backend_mut().roll_fork_to_transaction( id, transaction, evm_env, @@ -210,33 +198,19 @@ impl DatabaseExt for CowBackend<'_> { evm_env: EvmEnv, tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn for<'db> FoundryInspectorExt>, ) -> eyre::Result<()> { - self.backend_mut(&evm_env, &tx_env).transact( - id, - transaction, - evm_env, - tx_env, - journaled_state, - inspector, - ) + self.backend_mut().transact(id, transaction, evm_env, tx_env, journaled_state, inspector) } fn transact_from_tx( &mut self, - transaction: &TransactionRequest, + tx_env: &TxEnv, evm_env: EvmEnv, - tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn for<'db> FoundryInspectorExt>, ) -> eyre::Result<()> { - self.backend_mut(&evm_env, &tx_env).transact_from_tx( - transaction, - evm_env, - tx_env, - journaled_state, - inspector, - ) + self.backend_mut().transact_from_tx(tx_env, evm_env, journaled_state, inspector) } fn active_fork_id(&self) -> Option { diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 61f03d8608412..5719121fd4eb0 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1,34 +1,28 @@ //! Foundry's main executor backend abstraction and implementation. use crate::{ - Env, InspectorExt, + FoundryBlock, FoundryInspectorExt, FoundryTransaction, TryAnyToTxEnv, constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS}, - evm::new_evm_with_inspector, + evm::new_eth_evm_with_inspector, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, utils::get_blob_base_fee_update_fraction, }; use alloy_consensus::{BlockHeader, Typed2718}; -use alloy_evm::{Evm, EvmEnv, FromRecoveredTx, rpc::TryIntoTxEnv}; +use alloy_evm::{Evm, EvmEnv, EvmFactory, eth::EthEvmContext}; use alloy_genesis::GenesisAccount; -use alloy_network::{ - AnyNetwork, AnyRpcBlock, AnyRpcTransaction, AnyTxEnvelope, TransactionResponse, -}; +use alloy_network::{AnyNetwork, BlockResponse, Network, TransactionResponse}; use alloy_primitives::{Address, B256, TxKind, U256, keccak256, uint}; -use alloy_rpc_types::{BlockNumberOrTag, Transaction, TransactionRequest}; +use alloy_rpc_types::BlockNumberOrTag; use eyre::Context; use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender}; pub use foundry_fork_db::{BlockchainDb, SharedBackend, cache::BlockchainDbMeta}; use revm::{ - Database, DatabaseCommit, Journal, JournalEntry, + Database, DatabaseCommit, JournalEntry, bytecode::Bytecode, - context::{JournalInner, TxEnv}, - context_interface::{ - block::BlobExcessGasAndPrice, journaled_state::account::JournaledAccountTr, - result::ResultAndState, - }, + context::{BlockEnv, JournalInner, TxEnv}, + context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState}, database::{CacheDB, DatabaseRef}, - inspector::{JournalExt, NoOpInspector}, precompile::{PrecompileSpecId, Precompiles}, primitives::{HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId}, state::{Account, AccountInfo, EvmState, EvmStorageSlot}, @@ -55,7 +49,7 @@ mod snapshot; pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot}; // A `revm::Database` that is used in forking mode -type ForkDB = CacheDB; +type ForkDB = CacheDB>; /// Represents a numeric `ForkId` valid only for the existence of the `Backend`. /// @@ -82,13 +76,19 @@ pub type JournaledState = JournalInner; /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut)] -pub trait DatabaseExt: Database + DatabaseCommit + Debug { +pub trait DatabaseExt: + Database + DatabaseCommit + Debug +{ /// Creates a new state snapshot at the current point of execution. /// /// A state snapshot is associated with a new unique id that's created for the snapshot. /// State snapshots can be reverted: [DatabaseExt::revert_state], however, depending on the /// [RevertStateSnapshotAction], it will keep the snapshot alive or delete it. - fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256; + fn snapshot_state( + &mut self, + journaled_state: &JournaledState, + evm_env: &EvmEnv, + ) -> U256; /// Reverts the snapshot if it exists /// @@ -98,16 +98,16 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug /// **N.B.** While this reverts the state of the evm to the snapshot, it keeps new logs made /// since the snapshots was created. This way we can show logs that were emitted between /// snapshot and its revert. - /// This will also revert any changes in the `Env` and replace it with the captured `Env` of - /// `Self::snapshot_state`. + /// This will also revert any changes in the `EvmEnv` and `TxEnv` and replace them with the + /// captured values from `Self::snapshot_state`. /// /// Depending on [RevertStateSnapshotAction] it will keep the snapshot alive or delete it. fn revert_state( &mut self, id: U256, journaled_state: &JournaledState, - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TX, action: RevertStateSnapshotAction, ) -> Option; @@ -126,8 +126,8 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug fn create_select_fork( &mut self, fork: CreateFork, - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TX, journaled_state: &mut JournaledState, ) -> eyre::Result { let id = self.create_fork(fork)?; @@ -141,8 +141,8 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug fn create_select_fork_at_transaction( &mut self, fork: CreateFork, - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TX, journaled_state: &mut JournaledState, transaction: B256, ) -> eyre::Result { @@ -163,7 +163,7 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug /// Selects the fork's state /// - /// This will also modify the current `Env`. + /// This will also modify the current `EvmEnv` and `TxEnv`. /// /// **Note**: this does not change the local state, but swaps the remote state /// @@ -173,8 +173,8 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug fn select_fork( &mut self, id: LocalForkId, - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TX, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -189,8 +189,8 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug &mut self, id: Option, block_number: u64, - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TX, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -206,8 +206,8 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug &mut self, id: Option, transaction: B256, - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TX, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -216,20 +216,19 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug &mut self, id: Option, transaction: B256, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv, + tx_env: TX, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn for<'db> FoundryInspectorExt>, ) -> eyre::Result<()>; /// Executes a given TransactionRequest, commits the new state to the DB fn transact_from_tx( &mut self, - transaction: &TransactionRequest, - evm_env: EvmEnv, - tx_env: TxEnv, + tx_env: &TX, + evm_env: EvmEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn for<'db> FoundryInspectorExt>, ) -> eyre::Result<()>; /// Returns the `ForkId` that's currently used in the database, if fork mode is on @@ -388,38 +387,6 @@ pub trait DatabaseExt: Database + DatabaseCommit + Debug struct _ObjectSafe(dyn DatabaseExt); -/// Extension trait for [`Journal`] providing borrow splitting and state replacement. -/// -/// Generic code accesses the journal via `ctx.journal_mut()` which returns `&mut impl JournalTr`. -/// This trait adds the ability to split the journal into its database and inner state components, -/// enabling direct [`DatabaseExt`] method calls with zero-copy borrow splitting. -pub trait FoundryJournalExt: JournalExt { - /// Returns mutable references to the database and journal inner state. - /// - /// Enables calling [`DatabaseExt`] methods directly, e.g.: - /// ```ignore - /// let (journal, env) = ctx.journal_and_env_mut(); // FoundryContextExt - /// let (db, inner) = journal.as_db_and_inner(); // FoundryJournalExt - /// db.select_fork(id, &env, inner)?; // DatabaseExt - /// ``` - fn as_db_and_inner(&mut self) -> (&mut dyn DatabaseExt, &mut JournaledState); - - /// Replaces the journal inner state. - /// - /// Used by sub-EVM execution to write back modified state after running a closure. - fn set_inner(&mut self, inner: JournaledState); -} - -impl FoundryJournalExt for Journal { - fn as_db_and_inner(&mut self) -> (&mut dyn DatabaseExt, &mut JournaledState) { - (&mut self.database, &mut self.inner) - } - - fn set_inner(&mut self, inner: JournaledState) { - self.inner = inner; - } -} - /// Provides the underlying `revm::Database` implementation. /// /// A `Backend` can be initialised in two forms: @@ -452,7 +419,7 @@ impl FoundryJournalExt for Journal { /// Multiple "forks" can be created `Backend::create_fork()`, however only 1 can be used by the /// `db`. However, their state can be hot-swapped by swapping the read half of `db` from one fork to /// another. -/// When swapping forks (`Backend::select_fork()`) we also update the current `Env` of the `EVM` +/// When swapping forks (`Backend::select_fork()`) we also update the current `EvmEnv` of the `EVM` /// accordingly, so that all `block.*` config values match /// /// When another for is selected [`DatabaseExt::select_fork()`] the entire storage, including @@ -474,9 +441,9 @@ impl FoundryJournalExt for Journal { /// after reverting the snapshot. #[derive(Clone, Debug)] #[must_use] -pub struct Backend { +pub struct Backend { /// The access point for managing forks - forks: MultiFork, + forks: MultiFork, // The default in memory db mem_db: FoundryEvmInMemoryDB, /// The journaled_state to use to initialize new forks with @@ -501,10 +468,13 @@ pub struct Backend { /// If this is set, then the Backend is currently in forking mode active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>, /// holds additional Backend data - inner: BackendInner, + inner: BackendInner, } -impl Backend { +impl Backend +where + N::TransactionResponse: TryAnyToTxEnv, +{ /// Creates a new Backend with a spawned multi fork thread. /// /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory @@ -519,7 +489,7 @@ impl Backend { /// database. /// /// Prefer using [`spawn`](Self::spawn) instead. - pub fn new(forks: MultiFork, fork: Option) -> eyre::Result { + pub fn new(forks: MultiFork, fork: Option) -> eyre::Result { trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend"); // Note: this will take of registering the `fork` let inner = BackendInner { @@ -556,7 +526,7 @@ impl Backend { /// as active pub(crate) fn new_with_fork( id: &ForkId, - fork: Fork, + fork: Fork, journaled_state: JournaledState, ) -> eyre::Result { let mut backend = Self::spawn(None)?; @@ -618,7 +588,7 @@ impl Backend { /// Returns all snapshots created in this backend pub fn state_snapshots( &self, - ) -> &StateSnapshots> { + ) -> &StateSnapshots>> { &self.inner.state_snapshots } @@ -673,7 +643,7 @@ impl Backend { pub(crate) fn update_fork_db( &self, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork, + target_fork: &mut Fork, ) { self.update_fork_db_contracts( self.inner.persistent_accounts.iter().copied(), @@ -687,7 +657,7 @@ impl Backend { &self, accounts: impl IntoIterator, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork, + target_fork: &mut Fork, ) { if let Some(db) = self.active_fork_db() { merge_account_data(accounts, db, active_journaled_state, target_fork) @@ -712,22 +682,22 @@ impl Backend { } /// Returns the currently active `Fork`, if any - pub fn active_fork(&self) -> Option<&Fork> { + pub fn active_fork(&self) -> Option<&Fork> { self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx)) } /// Returns the currently active `Fork`, if any - pub fn active_fork_mut(&mut self) -> Option<&mut Fork> { + pub fn active_fork_mut(&mut self) -> Option<&mut Fork> { self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx)) } /// Returns the currently active `ForkDB`, if any - pub fn active_fork_db(&self) -> Option<&ForkDB> { + pub fn active_fork_db(&self) -> Option<&ForkDB> { self.active_fork().map(|f| &f.db) } /// Returns the currently active `ForkDB`, if any - pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB> { + pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB> { self.active_fork_mut().map(|f| &mut f.db) } @@ -748,7 +718,7 @@ impl Backend { } /// Creates a snapshot of the currently active database - pub(crate) fn create_db_snapshot(&self) -> BackendDatabaseSnapshot { + pub(crate) fn create_db_snapshot(&self) -> BackendDatabaseSnapshot { if let Some((id, idx)) = self.active_fork_ids { let fork = self.inner.get_fork(idx).clone(); let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed"); @@ -784,18 +754,16 @@ 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) { - self.set_caller(env.tx.caller); - self.set_spec_id(env.evm_env.cfg_env.spec); + pub(crate) fn initialize(&mut self, spec_id: SpecId, caller: Address, tx_kind: TxKind) { + self.set_caller(caller); + self.set_spec_id(spec_id); - let test_contract = match env.tx.kind { + let test_contract = match tx_kind { TxKind::Call(to) => to, TxKind::Create => { - let nonce = self - .basic_ref(env.tx.caller) - .map(|b| b.unwrap_or_default().nonce) - .unwrap_or_default(); - env.tx.caller.create(nonce) + let nonce = + self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default(); + caller.create(nonce) } }; self.set_test_contract(test_contract); @@ -806,22 +774,19 @@ impl Backend { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect( + pub fn inspect FoundryInspectorExt>>( &mut self, - env: &mut Env, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, inspector: I, ) -> eyre::Result { - self.initialize(env); - let mut evm = crate::evm::new_evm_with_inspector( - self, - env.evm_env.to_owned(), - env.tx.to_owned(), - inspector, - ); + self.initialize(evm_env.cfg_env.spec, tx_env.caller, tx_env.kind); + let mut evm = crate::evm::new_eth_evm_with_inspector(self, evm_env.to_owned(), inspector); - let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?; + let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?; - *env = Env::from(evm.cfg.clone(), evm.block.clone(), evm.tx.clone()); + *evm_env = EvmEnv::new(evm.cfg.clone(), evm.block.clone()); + *tx_env = evm.tx.clone(); Ok(res) } @@ -888,22 +853,22 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(u64, AnyRpcBlock)> { + ) -> eyre::Result<(u64, N::BlockResponse)> { let fork = self.inner.get_fork_by_id(id)?; - let tx = fork.db.db.get_transaction(transaction)?; + let tx = fork.backend().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)?; + if let Some(tx_block) = tx.block_number() { + let block = fork.backend().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 - 1; Ok((fork_block, block)) } else { - let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; + let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?; - let number = block.header.number(); + let number = block.header().number(); Ok((number, block)) } @@ -915,51 +880,67 @@ impl Backend { pub fn replay_until( &mut self, id: LocalForkId, - mut env: Env, + evm_env: EvmEnv, tx_hash: B256, journaled_state: &mut JournaledState, - ) -> eyre::Result>> { + ) -> eyre::Result> { trace!(?id, ?tx_hash, "replay until transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); - let fork_id = self.ensure_fork_id(id)?.clone(); let fork = self.inner.get_fork_by_id_mut(id)?; let full_block = - fork.db.db.get_full_block(env.evm_env.block_env.number.saturating_to::())?; + fork.backend().get_full_block(evm_env.block_env.number.saturating_to::())?; - for tx in full_block.inner.transactions.txns() { - // 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.inner().inner.signer()) - || tx.ty() == SYSTEM_TRANSACTION_TYPE - { - trace!(tx=?tx.tx_hash(), "skipping system transaction"); - continue; - } + // Collect non-system transactions up to and including the target. + let txs = full_block + .transactions() + .txns() + .filter(|tx| !is_known_system_sender(tx.from()) && tx.ty() != SYSTEM_TRANSACTION_TYPE); + let mut txs_to_replay = Vec::new(); + let mut target_tx = None; + for tx in txs { if tx.tx_hash() == tx_hash { - // found the target transaction - return Ok(Some(tx.inner.clone())); + target_tx = Some(tx.clone()); + break; } - trace!(tx=?tx.tx_hash(), "committing transaction"); - - commit_transaction( - tx, - &mut env, - journaled_state, - fork, - &fork_id, - &persistent_accounts, - &mut NoOpInspector, - )?; + txs_to_replay.push(tx.clone()); + } + + // Replay all preceding transactions using a single EVM + cloned ForkDB. + if !txs_to_replay.is_empty() { + let now = Instant::now(); + + // Clone the fork's CacheDB once. The underlying SharedBackend is Arc-backed, + // so only the local cache layer is actually duplicated. + let replay_db = fork.db.clone(); + let mut evm = alloy_evm::EthEvmFactory::default().create_evm(replay_db, evm_env); + + for tx in &txs_to_replay { + let tx_env: TxEnv = tx.try_any_to_tx_env()?; + trace!(tx=?tx.tx_hash(), "committing transaction"); + evm.transact_commit(tx_env).wrap_err("backend: failed committing transaction")?; + } + + // Extract the DB back and replace the fork's database with the replayed state. + fork.db = evm.into_db(); + + // Refresh journaled states from the updated database, preserving persistent + // accounts (cheatcode address, CREATE2 deployer, test contract, etc.). + fork.refresh_journaled_states(journaled_state, &persistent_accounts)?; + + trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions"); } - Ok(None) + Ok(target_tx) } } -impl DatabaseExt for Backend { +impl DatabaseExt for Backend +where + N::TransactionResponse: TryAnyToTxEnv, +{ fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256 { trace!("create snapshot"); let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new( @@ -1299,7 +1280,7 @@ impl DatabaseExt for Backend { self.roll_fork(Some(id), fork_block, evm_env, tx_env, journaled_state)?; // we need to update the env to the block - update_env_block(evm_env, &block); + update_env_block(evm_env, block.header()); // after we forked at the fork block we need to properly update the block env to the block // env of the tx's block @@ -1307,10 +1288,8 @@ impl DatabaseExt for Backend { .forks .update_block_env(self.inner.ensure_fork_id(id).cloned()?, evm_env.block_env.clone()); - let env = Env { evm_env: evm_env.clone(), tx: tx_env.clone() }; - // replay all transactions that came before - self.replay_until(id, env, transaction, journaled_state)?; + self.replay_until(id, evm_env.clone(), transaction, journaled_state)?; Ok(()) } @@ -1320,9 +1299,9 @@ impl DatabaseExt for Backend { maybe_id: Option, transaction: B256, mut evm_env: EvmEnv, - tx_env: TxEnv, + mut tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn for<'db> FoundryInspectorExt>, ) -> eyre::Result<()> { trace!(?maybe_id, ?transaction, "execute transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); @@ -1331,7 +1310,7 @@ impl DatabaseExt for Backend { let tx = { let fork = self.inner.get_fork_by_id_mut(id)?; - fork.db.db.get_transaction(transaction)? + fork.backend().get_transaction(transaction)? }; // This is a bit ambiguous because the user wants to transact an arbitrary transaction in @@ -1342,13 +1321,13 @@ impl DatabaseExt for Backend { // So we modify the env to match the transaction's block. let (_fork_block, block) = self.get_block_number_and_block_for_transaction(id, transaction)?; - update_env_block(&mut evm_env, &block); + update_env_block(&mut evm_env, block.header()); - let mut env = Env { evm_env, tx: tx_env }; let fork = self.inner.get_fork_by_id_mut(id)?; commit_transaction( &tx, - &mut env, + &mut evm_env, + &mut tx_env, journaled_state, fork, &fork_id, @@ -1359,29 +1338,20 @@ impl DatabaseExt for Backend { fn transact_from_tx( &mut self, - tx: &TransactionRequest, + tx_env: &TxEnv, evm_env: EvmEnv, - tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn InspectorExt, + inspector: &mut dyn for<'db> FoundryInspectorExt>, ) -> eyre::Result<()> { - trace!(?tx, "execute signed transaction"); + trace!(?tx_env, "execute signed transaction"); self.commit(journaled_state.state.clone()); - let mut env = Env { evm_env, tx: tx_env }; let res = { - env.tx = tx.clone().try_into_tx_env(&env.evm_env)?; - let mut db = self.clone(); - let mut evm = new_evm_with_inspector( - &mut db, - env.evm_env.to_owned(), - env.tx.to_owned(), - inspector, - ); + let mut evm = new_eth_evm_with_inspector(&mut db, evm_env, inspector); evm.journaled_state.depth = journaled_state.depth + 1; - evm.transact(env.tx)? + evm.transact_raw(tx_env.to_owned())? }; self.commit(res.state); @@ -1555,7 +1525,10 @@ impl DatabaseExt for Backend { } } -impl DatabaseRef for Backend { +impl DatabaseRef for Backend +where + N::TransactionResponse: TryAnyToTxEnv, +{ type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -1591,7 +1564,10 @@ impl DatabaseRef for Backend { } } -impl DatabaseCommit for Backend { +impl DatabaseCommit for Backend +where + N::TransactionResponse: TryAnyToTxEnv, +{ fn commit(&mut self, changes: Map) { if let Some(db) = self.active_fork_db_mut() { db.commit(changes) @@ -1601,7 +1577,10 @@ impl DatabaseCommit for Backend { } } -impl Database for Backend { +impl Database for Backend +where + N::TransactionResponse: TryAnyToTxEnv, +{ type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { if let Some(db) = self.active_fork_db_mut() { @@ -1638,21 +1617,26 @@ impl Database for Backend { /// Variants of a [revm::Database] #[derive(Clone, Debug)] -pub enum BackendDatabaseSnapshot { +pub enum BackendDatabaseSnapshot { /// Simple in-memory [revm::Database] InMemory(FoundryEvmInMemoryDB), /// Contains the entire forking mode database - Forked(LocalForkId, ForkId, ForkLookupIndex, Box), + Forked(LocalForkId, ForkId, ForkLookupIndex, Box>), } /// Represents a fork #[derive(Clone, Debug)] -pub struct Fork { - db: ForkDB, +pub struct Fork { + db: ForkDB, journaled_state: JournaledState, } -impl Fork { +impl Fork { + /// Returns a reference to the underlying [`SharedBackend`]. + pub fn backend(&self) -> &SharedBackend { + &self.db.db + } + /// Returns true if the account is a contract pub fn is_contract(&self, acc: Address) -> bool { if let Ok(Some(acc)) = self.db.basic_ref(acc) @@ -1662,11 +1646,23 @@ impl Fork { } is_contract_in_state(&self.journaled_state.state, acc) } + + /// Refreshes the given journaled state and the fork's own journaled state from the + /// database, preserving persistent accounts. + fn refresh_journaled_states( + &mut self, + journaled_state: &mut JournaledState, + persistent_accounts: &HashSet
, + ) -> Result<(), BackendError> { + update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?; + update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?; + Ok(()) + } } /// Container type for various Backend related data #[derive(Clone, Debug)] -pub struct BackendInner { +pub struct BackendInner { /// Stores the `ForkId` of the fork the `Backend` launched with from the start. /// /// In other words if [`Backend::spawn()`] was called with a `CreateFork` command, to launch @@ -1689,9 +1685,9 @@ pub struct BackendInner { pub created_forks: HashMap, /// Holds all created fork databases // Note: data is stored in an `Option` so we can remove it without reshuffling - pub forks: Vec>, + pub forks: Vec>>, /// Contains state snapshots made at a certain point - pub state_snapshots: StateSnapshots>, + pub state_snapshots: StateSnapshots>>, /// Tracks whether there was a failure in a snapshot that was reverted /// /// The Test contract contains a bool variable that is set to true when an `assert` function @@ -1716,7 +1712,7 @@ 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 .get(&id) @@ -1736,51 +1732,51 @@ impl BackendInner { /// Returns the underlying fork mapped to the index #[track_caller] - fn get_fork(&self, idx: ForkLookupIndex) -> &Fork { + fn get_fork(&self, idx: ForkLookupIndex) -> &Fork { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].as_ref().unwrap() } /// Returns the underlying fork mapped to the index #[track_caller] - fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork { + fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].as_mut().unwrap() } /// Returns the underlying fork corresponding to the id #[track_caller] - fn get_fork_by_id_mut(&mut self, id: LocalForkId) -> eyre::Result<&mut Fork> { + fn get_fork_by_id_mut(&mut self, id: LocalForkId) -> eyre::Result<&mut Fork> { let idx = self.ensure_fork_index_by_local_id(id)?; Ok(self.get_fork_mut(idx)) } /// Returns the underlying fork corresponding to the id #[track_caller] - fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork> { + fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork> { let idx = self.ensure_fork_index_by_local_id(id)?; Ok(self.get_fork(idx)) } /// Removes the fork - fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork { + fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].take().unwrap() } - fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork) { + fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork) { self.forks[idx] = Some(fork) } /// Returns an iterator over Forks - pub fn forks_iter(&self) -> impl Iterator + '_ { + pub fn forks_iter(&self) -> impl Iterator)> + '_ { self.issued_local_fork_ids .iter() .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id]))) } /// Returns a mutable iterator over all Forks - pub fn forks_iter_mut(&mut self) -> impl Iterator + '_ { + pub fn forks_iter_mut(&mut self) -> impl Iterator> + '_ { self.forks.iter_mut().filter_map(|f| f.as_mut()) } @@ -1790,7 +1786,7 @@ impl BackendInner { id: LocalForkId, fork_id: ForkId, idx: ForkLookupIndex, - fork: Fork, + fork: Fork, ) { self.created_forks.insert(fork_id.clone(), idx); self.issued_local_fork_ids.insert(id, fork_id); @@ -1802,7 +1798,7 @@ impl BackendInner { &mut self, id: LocalForkId, fork_id: ForkId, - db: ForkDB, + db: ForkDB, journaled_state: JournaledState, ) -> ForkLookupIndex { let idx = self.forks.len(); @@ -1818,7 +1814,7 @@ impl BackendInner { &mut self, id: LocalForkId, new_fork_id: ForkId, - backend: SharedBackend, + backend: SharedBackend, ) -> eyre::Result { let fork_id = self.ensure_fork_id(id)?; let idx = self.ensure_fork_index(fork_id)?; @@ -1843,7 +1839,7 @@ impl BackendInner { pub fn insert_new_fork( &mut self, fork_id: ForkId, - db: ForkDB, + db: ForkDB, journaled_state: JournaledState, ) -> (LocalForkId, ForkLookupIndex) { let idx = self.forks.len(); @@ -1889,7 +1885,7 @@ impl BackendInner { } } -impl Default for BackendInner { +impl Default for BackendInner { fn default() -> Self { Self { launched_with_fork: None, @@ -1914,22 +1910,26 @@ impl Default for BackendInner { } /// This updates the currently used env with the fork's environment -pub(crate) fn update_current_env_with_fork_env( - evm_env: &mut EvmEnv, - tx_env: &mut TxEnv, - fork_evm_env: EvmEnv, +pub(crate) fn update_current_env_with_fork_env< + SPEC, + BLOCK: FoundryBlock, + TX: FoundryTransaction, +>( + evm_env: &mut EvmEnv, + tx_env: &mut TX, + fork_evm_env: EvmEnv, ) { - tx_env.chain_id = Some(fork_evm_env.cfg_env.chain_id); + tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id)); *evm_env = fork_evm_env; } /// Clones the data of the given `accounts` from the `active` database into the `fork_db` /// This includes the data held in storage (`CacheDB`) and kept in the `JournaledState`. -pub(crate) fn merge_account_data( +pub(crate) fn merge_account_data( accounts: impl IntoIterator, active: &CacheDB, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork, + target_fork: &mut Fork, ) { for addr in accounts.into_iter() { merge_db_account_data(addr, active, &mut target_fork.db); @@ -1958,10 +1958,10 @@ fn merge_journaled_state_data( } /// Clones the account data from the `active` db into the `ForkDB` -fn merge_db_account_data( +fn merge_db_account_data( addr: Address, active: &CacheDB, - fork_db: &mut ForkDB, + fork_db: &mut ForkDB, ) { trace!(?addr, "merging database data"); @@ -1998,38 +1998,44 @@ fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool { } /// Updates the evm env's block with the block's data -fn update_env_block(evm_env: &mut EvmEnv, block: &AnyRpcBlock) { +fn update_env_block( + evm_env: &mut EvmEnv, + header: &impl BlockHeader, +) { let block_env = &mut evm_env.block_env; - block_env.timestamp = U256::from(block.header.timestamp()); - block_env.beneficiary = block.header.beneficiary(); - block_env.difficulty = block.header.difficulty(); - block_env.prevrandao = Some(block.header.mix_hash().unwrap_or_default()); - block_env.basefee = block.header.base_fee_per_gas().unwrap_or_default(); - block_env.gas_limit = block.header.gas_limit(); - block_env.number = U256::from(block.header.number()); - - if let Some(excess_blob_gas) = block.header.excess_blob_gas() { - evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( + block_env.set_timestamp(U256::from(header.timestamp())); + block_env.set_beneficiary(header.beneficiary()); + block_env.set_difficulty(header.difficulty()); + block_env.set_prevrandao(header.mix_hash()); + block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default()); + block_env.set_gas_limit(header.gas_limit()); + block_env.set_number(U256::from(header.number())); + + if let Some(excess_blob_gas) = header.excess_blob_gas() { + evm_env.block_env.set_blob_excess_gas_and_price( excess_blob_gas, - get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, block.header.timestamp()), - )); + get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, header.timestamp()), + ); } } /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an inspector. -fn commit_transaction( - tx: &AnyRpcTransaction, - env: &mut Env, +#[allow(clippy::too_many_arguments)] +fn commit_transaction( + tx: &N::TransactionResponse, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, - fork: &mut Fork, + fork: &mut Fork, fork_id: &ForkId, persistent_accounts: &HashSet
, - inspector: &mut dyn InspectorExt, -) -> eyre::Result<()> { - if let Some(tx_envelope) = tx.as_envelope() { - env.tx = TxEnv::from_recovered_tx(tx_envelope, tx.from()); - } + inspector: &mut dyn for<'db> FoundryInspectorExt>, +) -> eyre::Result<()> +where + N::TransactionResponse: TryAnyToTxEnv, +{ + *tx_env = tx.try_any_to_tx_env()?; let now = Instant::now(); let res = { @@ -2038,15 +2044,11 @@ fn commit_transaction( let depth = journaled_state.depth; let mut db = Backend::new_with_fork(fork_id, fork, journaled_state)?; - let mut evm = crate::evm::new_evm_with_inspector( - &mut db as _, - env.evm_env.to_owned(), - env.tx.to_owned(), - inspector, - ); + let mut evm = + crate::evm::new_eth_evm_with_inspector(&mut db as _, evm_env.to_owned(), inspector); // Adjust inner EVM depth to ensure that inspectors receive accurate data. evm.journaled_state.depth = depth + 1; - evm.transact(env.tx.clone()).wrap_err("backend: failed committing transaction")? + evm.transact(tx_env.clone()).wrap_err("backend: failed committing transaction")? }; trace!(elapsed = ?now.elapsed(), "transacted transaction"); @@ -2075,24 +2077,21 @@ pub fn update_state( /// Applies the changeset of a transaction to the active journaled state and also commits it in the /// forked db -fn apply_state_changeset( +fn apply_state_changeset( state: Map, journaled_state: &mut JournaledState, - fork: &mut Fork, + fork: &mut Fork, persistent_accounts: &HashSet
, ) -> Result<(), BackendError> { // commit the state and update the loaded accounts fork.db.commit(state); - - 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(()) + fork.refresh_journaled_states(journaled_state, persistent_accounts) } #[cfg(test)] mod tests { use crate::{backend::Backend, opts::EvmOpts}; + use alloy_network::Ethereum; use alloy_primitives::{U256, address}; use alloy_provider::Provider; use foundry_common::provider::get_http_provider; @@ -2111,11 +2110,11 @@ mod tests { evm_opts.fork_url = Some(endpoint.to_string()); evm_opts.fork_block_number = Some(block_num); - let env = evm_opts.env().await.unwrap(); + let (evm_env, _) = evm_opts.env().await.unwrap(); - let fork = evm_opts.get_fork(&Config::default(), env.evm_env.clone()).unwrap(); + let fork = evm_opts.get_fork(&Config::default(), evm_env.clone()).unwrap(); - let backend = Backend::spawn(Some(fork)).unwrap(); + let backend = Backend::::spawn(Some(fork)).unwrap(); // some rng contract from etherscan let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3"); @@ -2129,7 +2128,7 @@ mod tests { let meta = BlockchainDbMeta { chain: None, - block_env: env.evm_env.block_env, + block_env: evm_env.block_env, hosts: Default::default(), }; diff --git a/crates/evm/core/src/env.rs b/crates/evm/core/src/env.rs index 4f707ac295705..dd31cf100f908 100644 --- a/crates/evm/core/src/env.rs +++ b/crates/evm/core/src/env.rs @@ -1,47 +1,22 @@ +use std::fmt::Debug; + pub use alloy_evm::EvmEnv; +use alloy_evm::{FromRecoveredTx, ToTxEnv}; +use alloy_network::{AnyRpcTransaction, TransactionResponse}; use alloy_primitives::{Address, B256, Bytes, U256}; +use op_revm::{ + OpTransaction, + transaction::{OpTxTr, deposit::DEPOSIT_TRANSACTION_TYPE}, +}; use revm::{ - Context, Database, - context::{Block, BlockEnv, Cfg, CfgEnv, JournalTr, Transaction, TxEnv}, + Context, Database, Journal, + context::{Block, BlockEnv, Cfg, CfgEnv, Transaction, TxEnv}, context_interface::{ContextTr, transaction::AccessList}, + inspector::JournalExt, primitives::{TxKind, hardfork::SpecId}, }; -/// Helper container type for [`EvmEnv`] and [`TxEnv`]. -#[derive(Clone, Debug, Default)] -pub struct Env { - pub evm_env: EvmEnv, - pub tx: TxEnv, -} - -/// Helper container type for [`EvmEnv`] and [`TxEnv`]. -impl Env { - pub fn from(cfg: CfgEnv, block: BlockEnv, tx: TxEnv) -> Self { - Self { evm_env: EvmEnv { cfg_env: cfg, block_env: block }, tx } - } - - pub fn new_with_spec_id(cfg: CfgEnv, block: BlockEnv, tx: TxEnv, spec_id: SpecId) -> Self { - let mut cfg = cfg; - cfg.spec = spec_id; - - Self::from(cfg, block, tx) - } - - /// Clones the evm env and tx env separately from a [`FoundryContextExt`] context. - pub fn clone_evm_and_tx(ecx: &mut impl FoundryContextExt) -> (EvmEnv, TxEnv) { - ( - EvmEnv { cfg_env: ecx.cfg_mut().clone(), block_env: ecx.block_mut().clone() }, - ecx.tx_mut().clone(), - ) - } - - /// Writes the split evm env and tx env back into a [`FoundryContextExt`] context. - pub fn apply_evm_and_tx(ecx: &mut impl FoundryContextExt, evm_env: EvmEnv, tx_env: TxEnv) { - *ecx.block_mut() = evm_env.block_env; - *ecx.cfg_mut() = evm_env.cfg_env; - *ecx.tx_mut() = tx_env; - } -} +use crate::backend::{DatabaseExt, JournaledState}; /// Extension of [`Block`] with mutable setters, allowing EVM-agnostic mutation of block fields. pub trait FoundryBlock: Block { @@ -153,6 +128,45 @@ pub trait FoundryTransaction: Transaction { /// Sets the max fee per blob gas. fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128); + + // `OpTransaction` methods + + /// Enveloped transaction bytes. + fn enveloped_tx(&self) -> Option<&Bytes> { + None + } + + /// Set Enveloped transaction bytes. + fn set_enveloped_tx(&mut self, _bytes: Bytes) {} + + /// Source hash of the deposit transaction. + fn source_hash(&self) -> Option { + None + } + + /// Sets source hash of the deposit transaction. + fn set_source_hash(&mut self, _source_hash: B256) {} + + /// Mint of the deposit transaction + fn mint(&self) -> Option { + None + } + + /// Sets mint of the deposit transaction. + fn set_mint(&mut self, _mint: u128) {} + + /// Whether the transaction is a system transaction + fn is_system_transaction(&self) -> bool { + false + } + + /// Sets whether the transaction is a system transaction + fn set_system_transaction(&mut self, _is_system_transaction: bool) {} + + /// Returns `true` if transaction is of type [`DEPOSIT_TRANSACTION_TYPE`]. + fn is_deposit(&self) -> bool { + self.tx_type() == DEPOSIT_TRANSACTION_TYPE + } } impl FoundryTransaction for TxEnv { @@ -209,65 +223,91 @@ impl FoundryTransaction for TxEnv { } } -/// Extension of [`Cfg`] with mutable setters, allowing EVM-agnostic mutation of EVM configuration -/// fields. -pub trait FoundryCfg: Cfg { - /// Sets the EVM spec (hardfork). - fn set_spec(&mut self, spec: SpecId); +impl FoundryTransaction for OpTransaction { + fn set_tx_type(&mut self, tx_type: u8) { + self.base.set_tx_type(tx_type); + } - /// Sets the chain ID. - fn set_chain_id(&mut self, chain_id: u64); + fn set_caller(&mut self, caller: Address) { + self.base.set_caller(caller); + } - /// Sets the contract code size limit. - fn set_limit_contract_code_size(&mut self, limit: Option); + fn set_gas_limit(&mut self, gas_limit: u64) { + self.base.set_gas_limit(gas_limit); + } - /// Sets the contract initcode size limit. - fn set_limit_contract_initcode_size(&mut self, limit: Option); + fn set_gas_price(&mut self, gas_price: u128) { + self.base.set_gas_price(gas_price); + } - /// Sets whether nonce checks are disabled. - fn set_disable_nonce_check(&mut self, disabled: bool); + fn set_kind(&mut self, kind: TxKind) { + self.base.set_kind(kind); + } - /// Sets the max blobs per transaction. - fn set_max_blobs_per_tx(&mut self, max: Option); + fn set_value(&mut self, value: U256) { + self.base.set_value(value); + } - /// Sets the blob base fee update fraction. - fn set_blob_base_fee_update_fraction(&mut self, fraction: Option); + fn set_data(&mut self, data: Bytes) { + self.base.set_data(data); + } - /// Sets the transaction gas limit cap. - fn set_tx_gas_limit_cap(&mut self, cap: Option); -} + fn set_nonce(&mut self, nonce: u64) { + self.base.set_nonce(nonce); + } -impl FoundryCfg for CfgEnv { - fn set_spec(&mut self, spec: SpecId) { - self.spec = spec; + fn set_chain_id(&mut self, chain_id: Option) { + self.base.set_chain_id(chain_id); } - fn set_chain_id(&mut self, chain_id: u64) { - self.chain_id = chain_id; + fn set_access_list(&mut self, access_list: AccessList) { + self.base.set_access_list(access_list); } - fn set_limit_contract_code_size(&mut self, limit: Option) { - self.limit_contract_code_size = limit; + fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { + self.base.set_gas_priority_fee(gas_priority_fee); } - fn set_limit_contract_initcode_size(&mut self, limit: Option) { - self.limit_contract_initcode_size = limit; + fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} + + fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} + + fn enveloped_tx(&self) -> Option<&Bytes> { + OpTxTr::enveloped_tx(self) } - fn set_disable_nonce_check(&mut self, disabled: bool) { - self.disable_nonce_check = disabled; + fn set_enveloped_tx(&mut self, bytes: Bytes) { + self.enveloped_tx = Some(bytes); } - fn set_max_blobs_per_tx(&mut self, max: Option) { - self.max_blobs_per_tx = max; + fn source_hash(&self) -> Option { + OpTxTr::source_hash(self) } - fn set_blob_base_fee_update_fraction(&mut self, fraction: Option) { - self.blob_base_fee_update_fraction = fraction; + fn set_source_hash(&mut self, source_hash: B256) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.source_hash = source_hash; + } } - fn set_tx_gas_limit_cap(&mut self, cap: Option) { - self.tx_gas_limit_cap = cap; + fn mint(&self) -> Option { + OpTxTr::mint(self) + } + + fn set_mint(&mut self, mint: u128) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.mint = Some(mint); + } + } + + fn is_system_transaction(&self) -> bool { + OpTxTr::is_system_transaction(self) + } + + fn set_system_transaction(&mut self, is_system_transaction: bool) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.is_system_transaction = is_system_transaction; + } } } @@ -276,26 +316,265 @@ impl FoundryCfg for CfgEnv { /// [`ContextTr`] only exposes immutable references for block, tx, and cfg. /// Cheatcodes like `vm.warp()`, `vm.roll()`, `vm.chainId()` need to mutate these fields. pub trait FoundryContextExt: - ContextTr + ContextTr< + Block: FoundryBlock + Clone, + Tx: FoundryTransaction + Clone, + Cfg = CfgEnv, + Journal: JournalExt, + > { + /// Specification id type + /// + /// Bubbled-up from `ContextTr::Cfg` for convenience and simplified bounds. + type Spec: Into + Copy + Debug; + /// Mutable reference to the block environment. - fn block_mut(&mut self) -> &mut BlockEnv; + fn block_mut(&mut self) -> &mut Self::Block; + /// Mutable reference to the transaction environment. - fn tx_mut(&mut self) -> &mut TxEnv; + fn tx_mut(&mut self) -> &mut Self::Tx; + /// Mutable reference to the configuration environment. - fn cfg_mut(&mut self) -> &mut CfgEnv; + fn cfg_mut(&mut self) -> &mut Self::Cfg; + + /// Mutable reference to the db and the journal inner. + fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState); + + /// Sets block environment. + fn set_block(&mut self, block: Self::Block) { + *self.block_mut() = block; + } + + /// Sets transaction environment. + fn set_tx(&mut self, tx: Self::Tx) { + *self.tx_mut() = tx; + } + + /// Sets configuration environment. + fn set_cfg(&mut self, cfg: Self::Cfg) { + *self.cfg_mut() = cfg; + } + + /// Sets journal inner. + fn set_journal_inner(&mut self, journal_inner: JournaledState) { + *self.db_journal_inner_mut().1 = journal_inner; + } + + /// Sets EVM environment. + fn set_evm(&mut self, evm_env: EvmEnv) { + *self.cfg_mut() = evm_env.cfg_env; + *self.block_mut() = evm_env.block_env; + } + + /// Cloned transaction environment. + fn tx_clone(&self) -> Self::Tx { + self.tx().clone() + } + + /// Cloned EVM environment (Cfg + Block). + fn evm_clone(&self) -> EvmEnv { + EvmEnv::new(self.cfg().clone(), self.block().clone()) + } } -impl, C> FoundryContextExt - for Context +impl< + BLOCK: FoundryBlock + Clone, + TX: FoundryTransaction + Clone, + SPEC: Into + Copy + Debug, + DB: Database, + C, +> FoundryContextExt for Context, DB, Journal, C> { - fn block_mut(&mut self) -> &mut BlockEnv { + type Spec = ::Spec; + + fn block_mut(&mut self) -> &mut Self::Block { &mut self.block } - fn tx_mut(&mut self) -> &mut TxEnv { + + fn tx_mut(&mut self) -> &mut Self::Tx { &mut self.tx } - fn cfg_mut(&mut self) -> &mut CfgEnv { + + fn cfg_mut(&mut self) -> &mut Self::Cfg { &mut self.cfg } + + fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) { + (&mut self.journaled_state.database, &mut self.journaled_state.inner) + } +} + +/// Temporary bound alias used during the transition to a fully generic foundry-evm and +/// foundry-cheatcodes. +/// +/// Pins the EVM context to Ethereum-specific environment types (`BlockEnv`, `TxEnv`, `CfgEnv`) +/// so that cheatcode implementations don't need to repeat the full where-clause everywhere. +/// Once cheatcodes are fully generic over network/environment types this alias will be removed. +pub trait EthCheatCtx: + FoundryContextExt< + Spec = SpecId, + Block = BlockEnv, + Tx = TxEnv, + Cfg = CfgEnv, + Db: DatabaseExt, + > +{ +} +impl EthCheatCtx for CTX where + CTX: FoundryContextExt< + Spec = SpecId, + Block = BlockEnv, + Tx = TxEnv, + Cfg = CfgEnv, + Db: DatabaseExt, + > +{ +} + +/// Abstraction trait for converting any RPC transaction into corresponding `TxEnv`. +/// +/// This trait bridges the gap between different network RPC transaction types and the EVM's +/// `TxEnv`: +/// - For [`alloy_rpc_types::Transaction`] (Ethereum): delegates to [`ToTxEnv`]. +/// - For [`AnyRpcTransaction`] (AnyNetwork): extracts the inner [`alloy_consensus::TxEnvelope`] via +/// [`as_envelope()`](alloy_network::AnyTxEnvelope::as_envelope) then delegates to +/// [`FromRecoveredTx`]. +/// - For [`op_alloy_rpc_types::Transaction`] (Optimism): delegates to [`ToTxEnv`]. +pub trait TryAnyToTxEnv { + /// Tries to convert this RPC transaction into a [`TxEnv`]. + fn try_any_to_tx_env(&self) -> eyre::Result; +} + +impl TryAnyToTxEnv for alloy_rpc_types::Transaction { + fn try_any_to_tx_env(&self) -> eyre::Result { + Ok(self.as_recovered().to_tx_env()) + } +} + +impl TryAnyToTxEnv for AnyRpcTransaction { + fn try_any_to_tx_env(&self) -> eyre::Result { + if let Some(envelope) = self.as_envelope() { + Ok(TxEnv::from_recovered_tx(envelope, self.from())) + } else { + eyre::bail!("cannot convert unknown transaction type to TxEnv") + } + } +} + +impl TryAnyToTxEnv> for op_alloy_rpc_types::Transaction { + fn try_any_to_tx_env(&self) -> eyre::Result> { + Ok(self.as_recovered().to_tx_env()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::{Sealed, Signed, TxEip1559, transaction::Recovered}; + use alloy_network::{AnyTxEnvelope, AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction}; + use alloy_primitives::Signature; + use alloy_rpc_types::{Transaction, TransactionInfo}; + use alloy_serde::WithOtherFields; + use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo}; + + fn make_signed_eip1559() -> Signed { + Signed::new_unchecked( + TxEip1559 { + chain_id: 1, + nonce: 42, + gas_limit: 21001, + to: TxKind::Call(Address::with_last_byte(0xBB)), + value: U256::from(101), + ..Default::default() + }, + Signature::new(U256::ZERO, U256::ZERO, false), + B256::ZERO, + ) + } + + #[test] + fn try_any_to_tx_env_for_eth_and_any_transactions() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + let tx = Transaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + TransactionInfo::default(), + ); + let tx_env: TxEnv = tx.try_any_to_tx_env().unwrap(); + + assert_eq!(tx_env.caller, from); + assert_eq!(tx_env.nonce, 42); + assert_eq!(tx_env.gas_limit, 21001); + assert_eq!(tx_env.value, U256::from(101)); + assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB))); + + // Wrap as AnyRpcTransaction (Ethereum variant) via From>. + let any_tx = >::from(tx); + let any_tx_env: TxEnv = any_tx.try_any_to_tx_env().unwrap(); + + // TxEnv from AnyRpcTransaction must be the same + assert_eq!(tx_env, any_tx_env); + } + + #[test] + fn try_any_to_tx_env_for_op_transactions() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + + // Build the eth TxEnv to compare against op base + let eth_tx = Transaction::from_transaction( + Recovered::new_unchecked(signed_tx.clone().into(), from), + TransactionInfo::default(), + ); + let expected_base: TxEnv = eth_tx.try_any_to_tx_env().unwrap(); + + let op_tx = op_alloy_rpc_types::Transaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + OpTransactionInfo::default(), + ); + let op_tx_env: OpTransaction = op_tx.try_any_to_tx_env().unwrap(); + + assert_eq!(op_tx_env.base, expected_base); + + // Test with Deposit tx + let op_deposit_tx = op_alloy_rpc_types::Transaction::from_transaction( + Recovered::new_unchecked( + OpTxEnvelope::Deposit(Sealed::new(TxDeposit { + from, + mint: 1111, + ..Default::default() + })), + from, + ), + OpTransactionInfo::default(), + ); + let op_deposit_tx_env: OpTransaction = op_deposit_tx.try_any_to_tx_env().unwrap(); + + assert_eq!(op_deposit_tx_env.deposit.mint, Some(1111)); + assert_eq!(op_deposit_tx_env.base.caller, from); + } + + #[test] + fn try_any_to_tx_env_unknown_envelope_errors() { + let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope { + hash: B256::ZERO, + inner: UnknownTypedTransaction { + ty: AnyTxType(0xFF), + fields: Default::default(), + memo: Default::default(), + }, + }); + let from = Address::random(); + let any_tx = AnyRpcTransaction::new(WithOtherFields::new(Transaction { + inner: Recovered::new_unchecked(unknown, from), + block_hash: None, + block_number: None, + transaction_index: None, + effective_gas_price: None, + block_timestamp: None, + })); + + let result = any_tx.try_any_to_tx_env().unwrap_err(); + assert!(result.to_string().contains("unknown transaction type")); + } } diff --git a/crates/evm/core/src/evm.rs b/crates/evm/core/src/evm.rs index 7e2cc3da7393f..6ba7d4798b92d 100644 --- a/crates/evm/core/src/evm.rs +++ b/crates/evm/core/src/evm.rs @@ -4,24 +4,23 @@ use std::{ }; use crate::{ - Env, FoundryContextExt, InspectorExt, - backend::{DatabaseExt, FoundryJournalExt, JournaledState}, + FoundryContextExt, FoundryInspectorExt, + backend::{DatabaseExt, JournaledState}, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, }; use alloy_consensus::constants::KECCAK_EMPTY; -use alloy_evm::{Evm, EvmEnv, eth::EthEvmContext, precompiles::PrecompilesMap}; +use alloy_evm::{Evm, EvmEnv, EvmFactory, eth::EthEvmContext, precompiles::PrecompilesMap}; use alloy_primitives::{Address, Bytes, U256}; use foundry_fork_db::DatabaseError; use revm::{ - Context, Journal, + Context, context::{ - BlockEnv, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContext, - LocalContextTr, TxEnv, + BlockEnv, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContextTr, + TxEnv, result::{EVMError, ExecResultAndState, ExecutionResult, HaltReason, ResultAndState}, }, handler::{ - EthFrame, EthPrecompiles, EvmTr, FrameResult, FrameTr, Handler, ItemOrResult, - instructions::EthInstructions, + EthFrame, EvmTr, FrameResult, FrameTr, Handler, ItemOrResult, instructions::EthInstructions, }, inspector::{InspectorEvmTr, InspectorHandler}, interpreter::{ @@ -29,56 +28,43 @@ use revm::{ FrameInput, Gas, InstructionResult, InterpreterResult, SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit, return_ok, }, - precompile::{PrecompileSpecId, Precompiles}, primitives::hardfork::SpecId, }; -pub fn new_evm_with_inspector<'db, I: InspectorExt>( +pub fn new_revm_with_inspector< + 'db, + I: FoundryInspectorExt>, +>( + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: I, +) -> EthRevmEvm<'db, I> { + let mut revm = alloy_evm::EthEvmFactory::default() + .create_evm_with_inspector(db, evm_env, inspector) + .into_inner(); + revm.ctx.cfg.tx_chain_id_check = true; + revm.inspector.get_networks().inject_precompiles(&mut revm.precompiles); + revm +} + +pub fn new_eth_evm_with_inspector< + 'db, + I: FoundryInspectorExt>, +>( db: &'db mut dyn DatabaseExt, evm_env: EvmEnv, - tx_env: TxEnv, inspector: I, ) -> FoundryEvm<'db, I> { - let mut ctx = EthEvmContext { - journaled_state: { - let mut journal = Journal::new(db); - journal.set_spec_id(evm_env.cfg_env.spec); - journal - }, - block: evm_env.block_env, - cfg: evm_env.cfg_env, - tx: tx_env, - chain: (), - local: LocalContext::default(), - error: Ok(()), - }; - ctx.cfg.tx_chain_id_check = true; - let spec = ctx.cfg.spec; - - let mut evm = FoundryEvm { - inner: RevmEvm::new_with_inspector( - ctx, - inspector, - EthInstructions::default(), - get_precompiles(spec), - ), - }; + let eth_evm = + alloy_evm::EthEvmFactory::default().create_evm_with_inspector(db, evm_env, inspector); + let mut inner = eth_evm.into_inner(); + inner.ctx.cfg.tx_chain_id_check = true; + let mut evm = FoundryEvm { inner }; evm.inspector().get_networks().inject_precompiles(evm.precompiles_mut()); evm } -/// Get the precompiles for the given spec. -fn get_precompiles(spec: SpecId) -> PrecompilesMap { - PrecompilesMap::from_static( - EthPrecompiles { - precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), - spec, - } - .precompiles, - ) -} - /// Get the call inputs for the CREATE2 factory. fn get_create2_factory_call_inputs( salt: U256, @@ -100,44 +86,21 @@ fn get_create2_factory_call_inputs( } } -pub struct FoundryEvm<'db, I: InspectorExt> { - #[allow(clippy::type_complexity)] - inner: RevmEvm< - EthEvmContext<&'db mut dyn DatabaseExt>, - I, - EthInstructions>, - PrecompilesMap, - EthFrame, - >, -} -impl<'db, I: InspectorExt> FoundryEvm<'db, I> { - /// Consumes the EVM and returns the inner context. - pub fn into_context(self) -> EthEvmContext<&'db mut dyn DatabaseExt> { - self.inner.ctx - } - - pub fn run_execution( - &mut self, - frame: FrameInput, - ) -> Result> { - let mut handler = FoundryHandler::::default(); - - // Create first frame - let memory = - SharedMemory::new_with_buffer(self.inner.ctx().local().shared_memory_buffer().clone()); - let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; - - // Run execution loop - let mut frame_result = handler.inspect_run_exec_loop(&mut self.inner, first_frame_input)?; +type EthRevmEvm<'db, I> = RevmEvm< + EthEvmContext<&'db mut dyn DatabaseExt>, + I, + EthInstructions>, + PrecompilesMap, + EthFrame, +>; - // Handle last frame result - handler.last_frame_result(&mut self.inner, &mut frame_result)?; - - Ok(frame_result) - } +pub struct FoundryEvm<'db, I: FoundryInspectorExt>> { + inner: EthRevmEvm<'db, I>, } -impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { +impl<'db, I: FoundryInspectorExt>> Evm + for FoundryEvm<'db, I> +{ type Precompiles = PrecompilesMap; type Inspector = I; type DB = &'db mut dyn DatabaseExt; @@ -167,26 +130,6 @@ impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { ) } - fn db_mut(&mut self) -> &mut Self::DB { - &mut self.inner.ctx.journaled_state.database - } - - fn precompiles(&self) -> &Self::Precompiles { - &self.inner.precompiles - } - - fn precompiles_mut(&mut self) -> &mut Self::Precompiles { - &mut self.inner.precompiles - } - - fn inspector(&self) -> &Self::Inspector { - &self.inner.inspector - } - - fn inspector_mut(&mut self) -> &mut Self::Inspector { - &mut self.inner.inspector - } - fn set_inspector_enabled(&mut self, _enabled: bool) { unimplemented!("FoundryEvm is always inspecting") } @@ -195,7 +138,7 @@ impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { &mut self, tx: Self::Tx, ) -> Result, Self::Error> { - self.inner.ctx.tx = tx; + self.inner.set_tx(tx); let mut handler = FoundryHandler::::default(); let result = handler.inspect_run(&mut self.inner)?; @@ -222,7 +165,9 @@ impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { } } -impl<'db, I: InspectorExt> Deref for FoundryEvm<'db, I> { +impl<'db, I: FoundryInspectorExt>> Deref + for FoundryEvm<'db, I> +{ type Target = Context; fn deref(&self) -> &Self::Target { @@ -230,7 +175,9 @@ impl<'db, I: InspectorExt> Deref for FoundryEvm<'db, I> { } } -impl DerefMut for FoundryEvm<'_, I> { +impl<'db, I: FoundryInspectorExt>> DerefMut + for FoundryEvm<'db, I> +{ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner.ctx } @@ -241,83 +188,102 @@ impl DerefMut for FoundryEvm<'_, I> { /// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.) /// so that cheatcode impls can build and run nested EVMs without knowing the concrete type. pub trait NestedEvm { + /// The transaction environment type. + type Tx; + /// Returns a mutable reference to the journal inner state (`JournaledState`). fn journal_inner_mut(&mut self) -> &mut JournaledState; /// Runs a single execution frame (create or call) through the EVM handler loop. fn run_execution(&mut self, frame: FrameInput) -> Result>; - /// Executes a full transaction with the given `TxEnv`. - fn transact( + /// Executes a full transaction with the given tx env. + fn transact_raw( &mut self, - tx: TxEnv, + tx: Self::Tx, ) -> Result, EVMError>; - - /// Returns a snapshot of the current environment (cfg + block, tx). - fn to_env(&self) -> (EvmEnv, TxEnv); } -impl NestedEvm for FoundryEvm<'_, I> { +impl<'db, I: FoundryInspectorExt>> NestedEvm + for EthRevmEvm<'db, I> +{ + type Tx = TxEnv; + fn journal_inner_mut(&mut self) -> &mut JournaledState { - &mut self.inner.ctx.journaled_state.inner + &mut self.ctx_mut().journaled_state.inner } fn run_execution(&mut self, frame: FrameInput) -> Result> { - FoundryEvm::run_execution(self, frame) + let mut handler = FoundryHandler::::default(); + + // Create first frame + let memory = + SharedMemory::new_with_buffer(self.ctx().local().shared_memory_buffer().clone()); + let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; + + // Run execution loop + let mut frame_result = handler.inspect_run_exec_loop(self, first_frame_input)?; + + // Handle last frame result + handler.last_frame_result(self, &mut frame_result)?; + + Ok(frame_result) } - fn transact( + fn transact_raw( &mut self, - tx: TxEnv, + tx: Self::Tx, ) -> Result, EVMError> { - Evm::transact_raw(self, tx) - } + self.set_tx(tx); - fn to_env(&self) -> (EvmEnv, TxEnv) { - ( - EvmEnv { cfg_env: self.inner.ctx.cfg.clone(), block_env: self.inner.ctx.block.clone() }, - self.inner.ctx.tx.clone(), - ) + let mut handler = FoundryHandler::::default(); + let result = handler.inspect_run(self)?; + + Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone())) } } +/// Closure type used by `CheatcodesExecutor` methods that run nested EVM operations. +pub type NestedEvmClosure<'a, Tx> = + &'a mut dyn FnMut(&mut dyn NestedEvm) -> Result<(), EVMError>; + /// Clones the current context (env + journal), passes the database, cloned env, /// and cloned journal inner to the callback. The callback builds whatever EVM it /// needs, runs its operations, and returns `(result, modified_env, modified_journal)`. /// Modified state is written back after the callback returns. -pub fn with_cloned_context( +pub fn with_cloned_context< + CTX: FoundryContextExt>, +>( ecx: &mut CTX, f: impl FnOnce( - &mut dyn DatabaseExt, - EvmEnv, - TxEnv, + &mut dyn DatabaseExt, + EvmEnv, JournaledState, - ) -> Result<(R, EvmEnv, TxEnv, JournaledState), EVMError>, -) -> Result> -where - CTX::Journal: FoundryJournalExt, -{ - let (evm_env, tx_env) = Env::clone_evm_and_tx(ecx); + ) + -> Result<(EvmEnv, JournaledState), EVMError>, +) -> Result<(), EVMError> { + let evm_env = ecx.evm_clone(); - let journal = ecx.journal_mut(); - let (db, journal_inner) = journal.as_db_and_inner(); + let (db, journal_inner) = ecx.db_journal_inner_mut(); let journal_inner_clone = journal_inner.clone(); - let (result, sub_evm_env, sub_tx, sub_inner) = f(db, evm_env, tx_env, journal_inner_clone)?; + let (sub_evm_env, sub_inner) = f(db, evm_env, journal_inner_clone)?; // Write back modified state. The db borrow was released when f returned. - ecx.journal_mut().set_inner(sub_inner); - Env::apply_evm_and_tx(ecx, sub_evm_env, sub_tx); + ecx.set_journal_inner(sub_inner); + ecx.set_evm(sub_evm_env); - Ok(result) + Ok(()) } -pub struct FoundryHandler<'db, I: InspectorExt> { +pub struct FoundryHandler<'db, I: FoundryInspectorExt>> { create2_overrides: Vec<(usize, CallInputs)>, _phantom: PhantomData<(&'db mut dyn DatabaseExt, I)>, } -impl Default for FoundryHandler<'_, I> { +impl<'db, I: FoundryInspectorExt>> Default + for FoundryHandler<'db, I> +{ fn default() -> Self { Self { create2_overrides: Vec::new(), _phantom: PhantomData } } @@ -325,7 +291,9 @@ impl Default for FoundryHandler<'_, I> { // Blanket Handler implementation for FoundryHandler, needed for implementing the InspectorHandler // trait. -impl<'db, I: InspectorExt> Handler for FoundryHandler<'db, I> { +impl<'db, I: FoundryInspectorExt>> Handler + for FoundryHandler<'db, I> +{ type Evm = RevmEvm< EthEvmContext<&'db mut dyn DatabaseExt>, I, @@ -337,7 +305,7 @@ impl<'db, I: InspectorExt> Handler for FoundryHandler<'db, I> { type HaltReason = HaltReason; } -impl<'db, I: InspectorExt> FoundryHandler<'db, I> { +impl<'db, I: FoundryInspectorExt>> FoundryHandler<'db, I> { /// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2 /// factory. fn handle_create_frame( @@ -431,7 +399,9 @@ impl<'db, I: InspectorExt> FoundryHandler<'db, I> { } } -impl InspectorHandler for FoundryHandler<'_, I> { +impl<'db, I: FoundryInspectorExt>> InspectorHandler + for FoundryHandler<'db, I> +{ type IT = EthInterpreter; fn inspect_run_exec_loop( diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 6982ff20dc163..32c699ee49e3e 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -6,10 +6,8 @@ #![cfg_attr(docsrs, feature(doc_cfg))] use crate::constants::DEFAULT_CREATE2_DEPLOYER; -use alloy_evm::eth::EthEvmContext; use alloy_primitives::{Address, map::HashMap}; use auto_impl::auto_impl; -use backend::DatabaseExt; use revm::{Inspector, inspector::NoOpInspector, interpreter::CreateInputs}; use revm_inspectors::access_list::AccessListInspector; @@ -49,7 +47,7 @@ pub mod utils; /// network config, deployer address). It has no `Inspector` supertrait so it can /// be used in generic code with `I: FoundryInspectorExt + Inspector`. #[auto_impl(&mut, Box)] -pub trait FoundryInspectorExt { +pub trait InspectorExt { /// 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 @@ -74,20 +72,14 @@ pub trait FoundryInspectorExt { } } -/// Combined trait: `Inspector>` + [`FoundryInspectorExt`]. -/// -/// Used as a trait object (`dyn InspectorExt`) in backend code that is Eth-specific. -/// For generic multi-network code, use `I: FoundryInspectorExt + Inspector` instead. -pub trait InspectorExt: - for<'a> Inspector> + FoundryInspectorExt -{ -} +/// A combined inspector trait that integrates revm's [`Inspector`] with Foundry-specific +/// extensions. Automatically implemented for any type that implements both [`Inspector`] +/// and [`InspectorExt`]. +pub trait FoundryInspectorExt: Inspector + InspectorExt {} -impl InspectorExt for T where - T: for<'a> Inspector> + FoundryInspectorExt -{ -} +impl FoundryInspectorExt for T where T: Inspector + InspectorExt +{} -impl FoundryInspectorExt for NoOpInspector {} +impl InspectorExt for NoOpInspector {} -impl FoundryInspectorExt for AccessListInspector {} +impl InspectorExt for AccessListInspector {} diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index b27727bb3bd5e..8b00016dff853 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,6 +1,8 @@ use crate::{ - EvmEnv, constants::DEFAULT_CREATE2_DEPLOYER, fork::CreateFork, - utils::apply_chain_and_block_specific_env_changes, + EvmEnv, + constants::DEFAULT_CREATE2_DEPLOYER, + fork::CreateFork, + utils::{apply_chain_and_block_specific_env_changes, block_env_from_header}, }; use alloy_consensus::BlockHeader; use alloy_network::{AnyNetwork, BlockResponse, Network}; @@ -127,18 +129,18 @@ impl EvmOpts { .build() } - /// Assembles a complete [`Env`] + /// Returns a tuple with [`EvmEnv`] and [`TxEnv`] /// /// If a `fork_url` is set, creates a provider and passes it to both `EvmOpts::fork_evm_env` /// and `EvmOpts::fork_tx_env`. Falls back to local settings when no fork URL is configured. - pub async fn env(&self) -> eyre::Result { + pub async fn env(&self) -> eyre::Result<(EvmEnv, TxEnv)> { if let Some(ref fork_url) = self.fork_url { let provider = self.fork_provider_with_url::(fork_url)?; let ((evm_env, _block), tx) = tokio::try_join!(self.fork_evm_env(&provider), self.fork_tx_env(&provider))?; - Ok(crate::Env { evm_env, tx }) + Ok((evm_env, tx)) } else { - Ok(crate::Env { evm_env: self.local_evm_env(), tx: self.local_tx_env() }) + Ok((self.local_evm_env(), self.local_tx_env())) } } @@ -200,16 +202,7 @@ impl EvmOpts { let block_number = block.header().number(); let mut evm_env = EvmEnv { cfg_env: self.cfg_env(chain_id), - block_env: BlockEnv { - number: U256::from(block_number), - timestamp: U256::from(block.header().timestamp()), - beneficiary: block.header().beneficiary(), - difficulty: block.header().difficulty(), - prevrandao: block.header().mix_hash(), - basefee: block.header().base_fee_per_gas().unwrap_or_default(), - gas_limit: block.header().gas_limit(), - ..Default::default() - }, + block_env: block_env_from_header(block.header()), }; apply_chain_and_block_specific_env_changes::(&mut evm_env, &block, self.networks); @@ -267,7 +260,7 @@ impl EvmOpts { let mut cfg = CfgEnv::default(); cfg.chain_id = chain_id; cfg.memory_limit = self.memory_limit; - cfg.limit_contract_code_size = Some(usize::MAX); + cfg.limit_contract_code_size = self.env.code_size_limit.or(Some(usize::MAX)); // EIP-3607 rejects transactions from senders with deployed code. // 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. @@ -428,12 +421,12 @@ mod tests { assert!(evm_opts.fork_block_number.is_none()); // Fetch the environment (this resolves "latest" to an actual block number) - let env = evm_opts.env().await.unwrap(); - let resolved_block = env.evm_env.block_env.number; + let (evm_env, _) = evm_opts.env().await.unwrap(); + let resolved_block = evm_env.block_env.number; assert!(resolved_block > U256::ZERO, "should have resolved to a real block number"); // Create the fork - this should pin the block number - let fork = evm_opts.get_fork(&Config::default(), env.evm_env).unwrap(); + let fork = evm_opts.get_fork(&Config::default(), evm_env).unwrap(); // The fork's evm_opts should now have fork_block_number set to the resolved block assert_eq!( @@ -453,9 +446,9 @@ mod tests { // Set an explicit block number evm_opts.fork_block_number = Some(12345678); - let env = evm_opts.env().await.unwrap(); + let (evm_env, _) = evm_opts.env().await.unwrap(); - let fork = evm_opts.get_fork(&Config::default(), env.evm_env).unwrap(); + let fork = evm_opts.get_fork(&Config::default(), evm_env).unwrap(); // Should preserve the explicit block number, not override it assert_eq!( diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 826384b5ea259..3d5e432d3d45b 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -7,11 +7,14 @@ use alloy_primitives::{B256, ChainId, Selector, U256}; use alloy_provider::{Network, network::BlockResponse}; use foundry_config::NamedChain; use foundry_evm_networks::NetworkConfigs; -use revm::primitives::{ - eip4844::{BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE}, - hardfork::SpecId, -}; pub use revm::state::EvmState as StateChangeset; +use revm::{ + context::BlockEnv, + primitives::{ + eip4844::{BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE}, + hardfork::SpecId, + }, +}; /// Hints to the compiler that this is a cold path, i.e. unlikely to be taken. #[cold] @@ -20,6 +23,20 @@ pub fn cold_path() { // TODO: remove `#[cold]` and call `std::hint::cold_path` once stable. } +/// Constructs a [`BlockEnv`] from a block header. +pub fn block_env_from_header(header: &impl BlockHeader) -> BlockEnv { + BlockEnv { + number: U256::from(header.number()), + beneficiary: header.beneficiary(), + timestamp: U256::from(header.timestamp()), + difficulty: header.difficulty(), + prevrandao: header.mix_hash(), + basefee: header.base_fee_per_gas().unwrap_or_default(), + gas_limit: header.gas_limit(), + ..Default::default() + } +} + /// 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/Cargo.toml b/crates/evm/evm/Cargo.toml index a69c19cbdb2bd..c2f1436c1a575 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -27,6 +27,7 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-evm.workspace = true alloy-json-abi.workspace = true +alloy-network.workspace = true alloy-primitives = { workspace = true, features = [ "serde", "getrandom", diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index d3336fcc9013c..a42a6ead54849 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -1,6 +1,6 @@ use crate::{executors::Executor, inspectors::InspectorStackBuilder}; -use foundry_evm_core::{Env, backend::Backend}; -use revm::primitives::hardfork::SpecId; +use foundry_evm_core::{EvmEnv, backend::Backend}; +use revm::{context::TxEnv, primitives::hardfork::SpecId}; /// The builder that allows to configure an evm [`Executor`] which a stack of optional /// [`revm::Inspector`]s, such as [`Cheatcodes`]. @@ -73,21 +73,16 @@ impl ExecutorBuilder { /// Builds the executor as configured. #[inline] - pub fn build(self, env: Env, db: Backend) -> Executor { + pub fn build(self, mut evm_env: EvmEnv, tx_env: TxEnv, db: Backend) -> Executor { let Self { mut stack, gas_limit, spec_id, legacy_assertions } = self; if stack.block.is_none() { - stack.block = Some(env.evm_env.block_env.clone()); + stack.block = Some(evm_env.block_env.clone()); } if stack.gas_price.is_none() { - stack.gas_price = Some(env.tx.gas_price); + stack.gas_price = Some(tx_env.gas_price); } - let gas_limit = gas_limit.unwrap_or(env.evm_env.block_env.gas_limit); - let env = Env::new_with_spec_id( - env.evm_env.cfg_env.clone(), - env.evm_env.block_env.clone(), - env.tx, - spec_id, - ); - Executor::new(db, env, stack.build(), gas_limit, legacy_assertions) + let gas_limit = gas_limit.unwrap_or(evm_env.block_env.gas_limit); + evm_env.cfg_env.set_spec(spec_id); + Executor::new(db, evm_env, tx_env, stack.build(), gas_limit, legacy_assertions) } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 86e8e18581764..e47a94e3fa99d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -1105,13 +1105,13 @@ pub(crate) fn execute_tx(executor: &mut Executor, tx: &BasicTxDetails) -> Result if warp > 0 || roll > 0 { // Apply pre-call block adjustments to the executor's env. - executor.env_mut().evm_env.block_env.timestamp += warp; - executor.env_mut().evm_env.block_env.number += roll; + executor.evm_env_mut().block_env.timestamp += warp; + executor.evm_env_mut().block_env.number += roll; // Also update the inspector's cheatcodes.block if set. // The inspector's block may override the env during interpreter initialization, // so we need to add our warp/roll on top of any existing cheatcode-set values. - let block_env = executor.env().evm_env.block_env.clone(); + let block_env = executor.evm_env().block_env.clone(); if let Some(cheatcodes) = executor.inspector_mut().cheatcodes.as_mut() { if let Some(block) = cheatcodes.block.as_mut() { block.timestamp += warp; diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 1e56a019cc576..8296417dea405 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -61,10 +61,10 @@ fn apply_warp_roll(call: &BasicTxDetails, warp: U256, roll: U256) -> BasicTxDeta /// Applies warp/roll adjustments directly to the executor's environment. fn apply_warp_roll_to_env(executor: &mut Executor, warp: U256, roll: U256) { if warp > U256::ZERO || roll > U256::ZERO { - executor.env_mut().evm_env.block_env.timestamp += warp; - executor.env_mut().evm_env.block_env.number += roll; + executor.evm_env_mut().block_env.timestamp += warp; + executor.evm_env_mut().block_env.number += roll; - let block_env = executor.env().evm_env.block_env.clone(); + let block_env = executor.evm_env().block_env.clone(); if let Some(cheatcodes) = executor.inspector_mut().cheatcodes.as_mut() { if let Some(block) = cheatcodes.block.as_mut() { block.timestamp += warp; diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 23bd0cd97ce67..331a79d0ec04c 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -6,14 +6,12 @@ // `Executor` struct should be accessed using a trait defined in `foundry-evm-core` instead of // the concrete `Executor` type. -use crate::{ - Env, - inspectors::{ - Cheatcodes, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions, - }, +use crate::inspectors::{ + Cheatcodes, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions, }; use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt}; use alloy_json_abi::Function; +use alloy_network::Ethereum; use alloy_primitives::{ Address, Bytes, Log, TxKind, U256, keccak256, map::{AddressHashMap, HashMap}, @@ -99,8 +97,10 @@ pub struct Executor { // wrapper around spawning a new EVM on every call anyway, // so the performance difference should be negligible. backend: Arc, - /// The EVM environment. - env: Env, + /// The EVM environment (block and cfg). + evm_env: EvmEnv, + /// The transaction environment. + tx_env: TxEnv, /// The Revm inspector stack. inspector: InspectorStack, /// The gas limit for calls and deployments. @@ -114,7 +114,8 @@ impl Executor { #[inline] pub fn new( mut backend: Backend, - env: Env, + evm_env: EvmEnv, + tx_env: TxEnv, inspector: InspectorStack, gas_limit: u64, legacy_assertions: bool, @@ -132,19 +133,22 @@ impl Executor { }, ); - Self { backend: Arc::new(backend), env, inspector, gas_limit, legacy_assertions } + Self { + backend: Arc::new(backend), + evm_env, + tx_env, + inspector, + gas_limit, + legacy_assertions, + } } fn clone_with_backend(&self, backend: Backend) -> Self { - let env = Env::new_with_spec_id( - self.env.evm_env.cfg_env.clone(), - self.env.evm_env.block_env.clone(), - self.env.tx.clone(), - self.spec_id(), - ); + let evm_env = self.evm_env.clone(); Self { backend: Arc::new(backend), - env, + evm_env, + tx_env: self.tx_env.clone(), inspector: self.inspector().clone(), gas_limit: self.gas_limit, legacy_assertions: self.legacy_assertions, @@ -164,14 +168,24 @@ impl Executor { Arc::make_mut(&mut self.backend) } - /// Returns a reference to the EVM environment. - pub fn env(&self) -> &Env { - &self.env + /// Returns a reference to the EVM environment (block and cfg). + pub fn evm_env(&self) -> &EvmEnv { + &self.evm_env } - /// Returns a mutable reference to the EVM environment. - pub fn env_mut(&mut self) -> &mut Env { - &mut self.env + /// Returns a mutable reference to the EVM environment (block and cfg). + pub fn evm_env_mut(&mut self) -> &mut EvmEnv { + &mut self.evm_env + } + + /// Returns a reference to the transaction environment. + pub fn tx_env(&self) -> &TxEnv { + &self.tx_env + } + + /// Returns a mutable reference to the transaction environment. + pub fn tx_env_mut(&mut self) -> &mut TxEnv { + &mut self.tx_env } /// Returns a reference to the EVM inspector. @@ -186,12 +200,12 @@ impl Executor { /// Returns the EVM spec ID. pub fn spec_id(&self) -> SpecId { - self.env.evm_env.cfg_env.spec + self.evm_env.cfg_env.spec } /// Sets the EVM spec ID. pub fn set_spec_id(&mut self, spec_id: SpecId) { - self.env.evm_env.cfg_env.spec = spec_id; + self.evm_env.cfg_env.spec = spec_id; } /// Returns the gas limit for calls and deployments. @@ -263,7 +277,7 @@ impl Executor { let mut account = self.backend().basic_ref(address)?.unwrap_or_default(); account.nonce = nonce; self.backend_mut().insert_account_info(address, account); - self.env_mut().tx.nonce = nonce; + self.tx_env_mut().nonce = nonce; Ok(()) } @@ -340,8 +354,8 @@ impl Executor { value: U256, rd: Option<&RevertDecoder>, ) -> Result { - let env = self.build_test_env(from, TxKind::Create, code, value); - self.deploy_with_env(env, rd) + let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value); + self.deploy_with_env(evm_env, tx_env, rd) } /// Deploys a contract using the given `env` and commits the new state to the underlying @@ -349,21 +363,22 @@ impl Executor { /// /// # Panics /// - /// Panics if `env.tx.kind` is not `TxKind::Create(_)`. + /// Panics if `tx_env.kind` is not `TxKind::Create(_)`. #[instrument(name = "deploy", level = "debug", skip_all)] pub fn deploy_with_env( &mut self, - env: Env, + evm_env: EvmEnv, + tx_env: TxEnv, rd: Option<&RevertDecoder>, ) -> Result { assert!( - matches!(env.tx.kind, TxKind::Create), + matches!(tx_env.kind, TxKind::Create), "Expected create transaction, got {:?}", - env.tx.kind + tx_env.kind ); - trace!(sender=%env.tx.caller, "deploying contract"); + trace!(sender=%tx_env.caller, "deploying contract"); - let mut result = self.transact_with_env(env)?; + let mut result = self.transact_with_env(evm_env, tx_env)?; result = result.into_result(rd)?; let Some(Output::Create(_, Some(address))) = result.out else { panic!("Deployment succeeded, but no address was returned: {result:#?}"); @@ -400,9 +415,9 @@ impl Executor { res = res.into_result(rd)?; // record any changes made to the block's environment during setup - self.env_mut().evm_env.block_env = res.env.evm_env.block_env.clone(); + self.evm_env_mut().block_env = res.evm_env.block_env.clone(); // and also the chainid, which can be set manually - self.env_mut().evm_env.cfg_env.chain_id = res.env.evm_env.cfg_env.chain_id; + self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id; let success = self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false); @@ -466,8 +481,8 @@ impl Executor { calldata: Bytes, value: U256, ) -> eyre::Result { - let env = self.build_test_env(from, TxKind::Call(to), calldata, value); - self.call_with_env(env) + let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value); + self.call_with_env(evm_env, tx_env) } /// Performs a raw call to an account on the current state of the VM with an EIP-7702 @@ -480,10 +495,10 @@ impl Executor { value: U256, authorization_list: Vec, ) -> eyre::Result { - let mut env = self.build_test_env(from, to.into(), calldata, value); - env.tx.set_signed_authorization(authorization_list); - env.tx.tx_type = 4; - self.call_with_env(env) + let (evm_env, mut tx_env) = self.build_test_env(from, to.into(), calldata, value); + tx_env.set_signed_authorization(authorization_list); + tx_env.tx_type = 4; + self.call_with_env(evm_env, tx_env) } /// Performs a raw call to an account on the current state of the VM. @@ -494,8 +509,8 @@ impl Executor { calldata: Bytes, value: U256, ) -> eyre::Result { - let env = self.build_test_env(from, TxKind::Call(to), calldata, value); - self.transact_with_env(env) + let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value); + self.transact_with_env(evm_env, tx_env) } /// Performs a raw call to an account on the current state of the VM with an EIP-7702 @@ -508,31 +523,51 @@ impl Executor { value: U256, authorization_list: Vec, ) -> eyre::Result { - let mut env = self.build_test_env(from, TxKind::Call(to), calldata, value); - env.tx.set_signed_authorization(authorization_list); - env.tx.tx_type = 4; - self.transact_with_env(env) + let (evm_env, mut tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value); + tx_env.set_signed_authorization(authorization_list); + tx_env.tx_type = 4; + self.transact_with_env(evm_env, tx_env) } - /// Execute the transaction configured in `env.tx`. + /// Execute the transaction configured in `tx_env`. /// /// The state after the call is **not** persisted. #[instrument(name = "call", level = "debug", skip_all)] - pub fn call_with_env(&self, mut env: Env) -> eyre::Result { + pub fn call_with_env( + &self, + mut evm_env: EvmEnv, + mut tx_env: TxEnv, + ) -> eyre::Result { let mut stack = self.inspector().clone(); let mut backend = CowBackend::new_borrowed(self.backend()); - let result = backend.inspect(&mut env, stack.as_inspector())?; - convert_executed_result(env, stack, result, backend.has_state_snapshot_failure()) + let result = backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?; + convert_executed_result( + evm_env, + tx_env, + stack, + result, + backend.has_state_snapshot_failure(), + ) } - /// Execute the transaction configured in `env.tx`. + /// Execute the transaction configured in `tx_env`. #[instrument(name = "transact", level = "debug", skip_all)] - pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result { + pub fn transact_with_env( + &mut self, + mut evm_env: EvmEnv, + mut tx_env: TxEnv, + ) -> eyre::Result { let mut stack = self.inspector().clone(); let backend = self.backend_mut(); - let result = backend.inspect(&mut env, stack.as_inspector())?; - let mut result = - convert_executed_result(env, stack, result, backend.has_state_snapshot_failure())?; + let result: revm::context::result::ExecResultAndState = + backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?; + let mut result = convert_executed_result( + evm_env, + tx_env, + stack, + result, + backend.has_state_snapshot_failure(), + )?; self.commit(&mut result); Ok(result) } @@ -561,7 +596,7 @@ impl Executor { } // Persist the changed environment. - self.inspector_mut().set_env(&result.env); + self.inspector_mut().set_env(&result.evm_env, &result.tx_env); } /// Returns `true` if a test can be considered successful. @@ -703,36 +738,41 @@ impl Executor { /// /// If using a backend with cheatcodes, `tx.gas_price` and `block.number` will be overwritten by /// the cheatcode state in between calls. - fn build_test_env(&self, caller: Address, kind: TxKind, data: Bytes, value: U256) -> Env { - Env { - evm_env: EvmEnv { - cfg_env: { - let mut cfg = self.env().evm_env.cfg_env.clone(); - cfg.spec = self.spec_id(); - cfg - }, - // 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 the cheatcode handler if it is enabled - block_env: BlockEnv { - basefee: 0, - gas_limit: self.gas_limit, - ..self.env().evm_env.block_env.clone() - }, + fn build_test_env( + &self, + caller: Address, + kind: TxKind, + data: Bytes, + value: U256, + ) -> (EvmEnv, TxEnv) { + let evm_env = EvmEnv { + cfg_env: { + let mut cfg = self.evm_env.cfg_env.clone(); + cfg.spec = self.spec_id(); + cfg }, - tx: TxEnv { - caller, - kind, - data, - value, - // As above, we set the gas price to 0. - gas_price: 0, - gas_priority_fee: None, + // 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 the cheatcode handler if it is enabled + block_env: BlockEnv { + basefee: 0, gas_limit: self.gas_limit, - chain_id: Some(self.env().evm_env.cfg_env.chain_id), - ..self.env().tx.clone() + ..self.evm_env.block_env.clone() }, - } + }; + let tx_env = TxEnv { + caller, + kind, + data, + value, + // As above, we set the gas price to 0. + gas_price: 0, + gas_priority_fee: None, + gas_limit: self.gas_limit, + chain_id: Some(self.evm_env.cfg_env.chain_id), + ..self.tx_env.clone() + }; + (evm_env, tx_env) } pub fn call_sol_default(&self, to: Address, args: &C) -> C::Return @@ -866,11 +906,13 @@ pub struct RawCallResult { /// The edge coverage info collected during the call pub edge_coverage: Option>, /// Scripted transactions generated from this call - pub transactions: Option, + pub transactions: Option>, /// The changeset of the state. pub state_changeset: StateChangeset, - /// The `revm::Env` after the call - pub env: Env, + /// The `EvmEnv` after the call + pub evm_env: EvmEnv, + /// The `TxEnv` after the call + pub tx_env: TxEnv, /// The cheatcode states after execution pub cheatcodes: Option>, /// The raw output of the execution @@ -897,7 +939,8 @@ impl Default for RawCallResult { edge_coverage: None, transactions: None, state_changeset: HashMap::default(), - env: Env::default(), + evm_env: EvmEnv::default(), + tx_env: TxEnv::default(), cheatcodes: Default::default(), out: None, chisel_state: None, @@ -959,7 +1002,7 @@ impl RawCallResult { } /// Returns the transactions generated from this call. - pub fn transactions(&self) -> Option<&BroadcastableTransactions> { + pub fn transactions(&self) -> Option<&BroadcastableTransactions> { self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions) } @@ -1032,7 +1075,8 @@ impl std::ops::DerefMut for CallResult { /// Converts the data aggregated in the `inspector` and `call` to a `RawCallResult` fn convert_executed_result( - env: Env, + evm_env: EvmEnv, + tx_env: TxEnv, inspector: InspectorStack, ResultAndState { result, state: state_changeset }: ResultAndState, has_state_snapshot_failure: bool, @@ -1050,10 +1094,10 @@ fn convert_executed_result( } }; let gas = revm::interpreter::gas::calculate_initial_tx_gas( - env.evm_env.cfg_env.spec, - &env.tx.data, - env.tx.kind.is_create(), - env.tx.access_list.len().try_into()?, + evm_env.cfg_env.spec, + &tx_env.data, + tx_env.kind.is_create(), + tx_env.access_list.len().try_into()?, 0, 0, ); @@ -1098,7 +1142,8 @@ fn convert_executed_result( edge_coverage, transactions, state_changeset, - env, + evm_env, + tx_env, cheatcodes, out, chisel_state, diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index a1721276f0fb0..a08239c77f91a 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -1,7 +1,5 @@ -use crate::{ - Env, - executors::{Executor, ExecutorBuilder}, -}; +use crate::executors::{Executor, ExecutorBuilder}; +use alloy_evm::EvmEnv; use alloy_primitives::{Address, U256, map::HashMap}; use alloy_rpc_types::state::StateOverride; use eyre::Context; @@ -10,7 +8,7 @@ use foundry_config::{Chain, Config, utils::evm_spec_id}; use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use foundry_evm_networks::NetworkConfigs; use foundry_evm_traces::TraceMode; -use revm::{primitives::hardfork::SpecId, state::Bytecode}; +use revm::{context::TxEnv, primitives::hardfork::SpecId, state::Bytecode}; use std::ops::{Deref, DerefMut}; /// A default executor with tracing enabled @@ -20,7 +18,7 @@ pub struct TracingExecutor { impl TracingExecutor { pub fn new( - env: Env, + env: (EvmEnv, TxEnv), fork: CreateFork, version: Option, trace_mode: TraceMode, @@ -36,7 +34,7 @@ impl TracingExecutor { stack.trace_mode(trace_mode).networks(networks).create2_deployer(create2_deployer) }) .spec_id(evm_spec_id(version.unwrap_or_default())) - .build(env, db); + .build(env.0, env.1, db); // Apply the state overrides. if let Some(state_overrides) = state_overrides { @@ -79,18 +77,18 @@ impl TracingExecutor { pub async fn get_fork_material( config: &mut Config, mut evm_opts: EvmOpts, - ) -> eyre::Result<(Env, CreateFork, Chain, NetworkConfigs)> { + ) -> eyre::Result<(EvmEnv, TxEnv, CreateFork, Chain, NetworkConfigs)> { evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); evm_opts.fork_block_number = config.fork_block_number; - let env = evm_opts.env().await?; + let (evm_env, tx_env) = evm_opts.env().await?; - let fork = evm_opts.get_fork(config, env.evm_env.clone()).unwrap(); - let networks = evm_opts.networks.with_chain_id(env.evm_env.cfg_env.chain_id); + let fork = evm_opts.get_fork(config, evm_env.clone()).unwrap(); + let networks = evm_opts.networks.with_chain_id(evm_env.cfg_env.chain_id); config.labels.extend(networks.precompiles_label()); - let chain = env.tx.chain_id.unwrap().into(); - Ok((env, fork, chain, networks)) + let chain = tx_env.chain_id.unwrap().into(); + Ok((evm_env, tx_env, fork, chain, networks)) } } diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index bc09521fa771d..84c05f69db1c2 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -2,8 +2,7 @@ use alloy_primitives::Log; use alloy_sol_types::{SolEvent, SolInterface, SolValue}; use foundry_common::{ErrorExt, fmt::ConsoleFmt, sh_println}; use foundry_evm_core::{ - FoundryInspectorExt, abi::console, constants::HARDHAT_CONSOLE_ADDRESS, - decode::decode_console_log, + InspectorExt, abi::console, constants::HARDHAT_CONSOLE_ADDRESS, decode::decode_console_log, }; use revm::{ Inspector, @@ -97,7 +96,7 @@ where } } -impl FoundryInspectorExt for LogCollector { +impl InspectorExt for LogCollector { fn console_log(&mut self, msg: &str) { self.push_msg(msg); } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 59abc04347893..d3a124381d2a0 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -2,21 +2,20 @@ use super::{ Cheatcodes, CheatsConfig, ChiselState, CustomPrintTracer, Fuzzer, LineCoverageCollector, LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector, }; +use alloy_evm::EvmEnv; use alloy_primitives::{ Address, B256, Bytes, Log, TxKind, U256, map::{AddressHashMap, HashMap}, }; -use alloy_rpc_types::request::TransactionRequest; use foundry_cheatcodes::{ - CheatcodeAnalysis, CheatcodesExecutor, CheatsCtxExt, NestedEvmClosure, Wallets, + CheatcodeAnalysis, CheatcodesExecutor, EthCheatCtx, NestedEvmClosure, Wallets, }; use foundry_common::compile::Analysis; -use foundry_compilers::ProjectPathsConfig; use foundry_evm_core::{ - Env, FoundryBlock, FoundryInspectorExt, FoundryTransaction, InspectorExt, - backend::{DatabaseError, DatabaseExt, FoundryJournalExt, JournaledState}, + FoundryBlock, FoundryTransaction, InspectorExt, + backend::{DatabaseError, DatabaseExt, JournaledState}, env::FoundryContextExt, - evm::{NestedEvm, new_evm_with_inspector, with_cloned_context}, + evm::{NestedEvm, new_revm_with_inspector, with_cloned_context}, }; use foundry_evm_coverage::HitMaps; use foundry_evm_networks::NetworkConfigs; @@ -24,10 +23,11 @@ use foundry_evm_traces::{SparsedTraceArena, TraceMode}; use revm::{ Inspector, context::{ - Block, BlockEnv, Cfg, ContextTr, JournalTr, Transaction, + Block, BlockEnv, Cfg, ContextTr, JournalTr, Transaction, TxEnv, result::{EVMError, ExecutionResult, Output}, }, context_interface::CreateScheme, + handler::EvmTr, inspector::JournalExt, interpreter::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, @@ -318,12 +318,6 @@ pub struct InspectorStack { pub inner: InspectorStackInner, } -impl InspectorStack { - pub fn paths_config(&self) -> Option<&ProjectPathsConfig> { - self.cheatcodes.as_ref().map(|c| &c.config.paths) - } -} - /// All used inpectors besides [Cheatcodes]. /// /// See [`InspectorStack`]. @@ -345,7 +339,7 @@ pub struct InspectorStackInner { pub script_execution_inspector: Option>, pub tracer: Option>, - // InspectorExt and other internal data. + // FoundryInspectorExt and other internal data. pub enable_isolation: bool, pub networks: NetworkConfigs, pub create2_deployer: Address, @@ -364,37 +358,35 @@ pub struct InspectorStackRefMut<'a> { pub inner: &'a mut InspectorStackInner, } -impl> CheatcodesExecutor - for InspectorStackInner -{ +impl CheatcodesExecutor for InspectorStackInner { fn with_nested_evm( &mut self, cheats: &mut Cheatcodes, ecx: &mut CTX, - f: NestedEvmClosure<'_>, + f: NestedEvmClosure<'_, CTX::Tx>, ) -> Result<(), EVMError> { let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self }; - with_cloned_context(ecx, |db, evm_env, tx_env, journal_inner| { - let mut evm = new_evm_with_inspector(db, evm_env, tx_env, &mut inspector); + with_cloned_context(ecx, |db, evm_env, journal_inner| { + let mut evm = new_revm_with_inspector(db, evm_env, &mut inspector); *evm.journal_inner_mut() = journal_inner; f(&mut evm)?; - let (sub_evm_env, sub_tx) = evm.to_env(); - let sub_inner = evm.into_context().journaled_state.inner; - Ok(((), sub_evm_env, sub_tx, sub_inner)) + let sub_inner = evm.journaled_state.inner.clone(); + let sub_evm_env = evm.ctx_ref().evm_clone(); + Ok((sub_evm_env, sub_inner)) }) } fn with_fresh_nested_evm( &mut self, cheats: &mut Cheatcodes, - db: &mut dyn DatabaseExt, - evm_env: foundry_evm_core::EvmEnv, - tx_env: revm::context::TxEnv, - f: NestedEvmClosure<'_>, - ) -> Result<(), EVMError> { + db: &mut dyn DatabaseExt, + evm_env: EvmEnv, + f: NestedEvmClosure<'_, CTX::Tx>, + ) -> Result, EVMError> { let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self }; - let mut evm = new_evm_with_inspector(db, evm_env, tx_env, &mut inspector); - f(&mut evm) + let mut evm = new_revm_with_inspector(db, evm_env, &mut inspector); + f(&mut evm)?; + Ok(evm.ctx_ref().evm_clone()) } fn transact_on_db( @@ -404,9 +396,10 @@ impl> CheatcodesExecutor fork_id: Option, transaction: B256, ) -> eyre::Result<()> { - let (evm_env, tx_env) = Env::clone_evm_and_tx(ecx); + let evm_env = ecx.evm_clone(); + let tx_env = ecx.tx_clone(); let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self }; - let (db, inner) = ecx.journal_mut().as_db_and_inner(); + let (db, inner) = ecx.db_journal_inner_mut(); db.transact(fork_id, transaction, evm_env, tx_env, inner, &mut inspector) } @@ -414,17 +407,17 @@ impl> CheatcodesExecutor &mut self, cheats: &mut Cheatcodes, ecx: &mut CTX, - tx: &TransactionRequest, + tx_env: &CTX::Tx, ) -> eyre::Result<()> { - let (evm_env, tx_env) = Env::clone_evm_and_tx(ecx); + let evm_env = ecx.evm_clone(); let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self }; - let (db, inner) = ecx.journal_mut().as_db_and_inner(); - db.transact_from_tx(tx, evm_env, tx_env, inner, &mut inspector) + let (db, inner) = ecx.db_journal_inner_mut(); + db.transact_from_tx(tx_env, evm_env, inner, &mut inspector) } - fn console_log(&mut self, _cheats: &mut Cheatcodes, msg: &str) { + fn console_log(&mut self, msg: &str) { if let Some(ref mut collector) = self.log_collector { - FoundryInspectorExt::console_log(&mut **collector, msg); + InspectorExt::console_log(&mut **collector, msg); } } @@ -455,27 +448,6 @@ 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, line_coverage, fuzzer, log_collector, printer, tracer); - if self.enable_isolation { - enabled.push("isolation"); - } - format!("[{}]", enabled.join(", ")) - }); - } - /// Set the solar compiler instance. #[inline] pub fn set_analysis(&mut self, analysis: Analysis) { @@ -484,9 +456,9 @@ impl InspectorStack { /// Set variables from an environment for the relevant inspectors. #[inline] - pub fn set_env(&mut self, env: &Env) { - self.set_block(&env.evm_env.block_env); - self.set_gas_price(env.tx.gas_price); + pub fn set_env(&mut self, evm_env: &EvmEnv, tx_env: &TxEnv) { + self.set_block(&evm_env.block_env); + self.set_gas_price(tx_env.gas_price); } /// Sets the block for the relevant inspectors. @@ -600,12 +572,6 @@ impl InspectorStack { InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner } } - /// Returns an [`InspectorExt`] using this stack's inspectors. - #[inline] - pub fn as_inspector(&mut self) -> impl InspectorExt + '_ { - self - } - /// Collects all the data gathered during inspection into a single struct. pub fn collect(self) -> InspectorData { let Self { @@ -667,12 +633,12 @@ impl InspectorStackRefMut<'_> { ecx.tx_mut().set_caller(inner_context_data.original_origin); } - fn do_call_end( + fn do_call_end( &mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome, - ) -> CallOutcome { + ) { let result = outcome.result.result; call_inspectors!( #[ret] @@ -692,7 +658,7 @@ impl InspectorStackRefMut<'_> { let different = outcome.result.result != result || (outcome.result.result == InstructionResult::Revert && outcome.output() != previous_outcome.output()); - different.then_some(outcome.clone()) + different.then_some(()) }, ); @@ -700,16 +666,14 @@ impl InspectorStackRefMut<'_> { if result.is_revert() && self.reverter.is_none() { self.reverter = Some(inputs.target_address); } - - outcome.clone() } - fn do_create_end( + fn do_create_end( &mut self, ecx: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome, - ) -> CreateOutcome { + ) { let result = outcome.result.result; call_inspectors!( #[ret] @@ -723,14 +687,12 @@ impl InspectorStackRefMut<'_> { let different = outcome.result.result != result || (outcome.result.result == InstructionResult::Revert && outcome.output() != previous_outcome.output()); - different.then_some(outcome.clone()) + different.then_some(()) }, ); - - outcome.clone() } - fn transact_inner( + fn transact_inner( &mut self, ecx: &mut CTX, kind: TxKind, @@ -738,11 +700,9 @@ impl InspectorStackRefMut<'_> { input: Bytes, gas_limit: u64, value: U256, - ) -> (InterpreterResult, Option
) - where - CTX::Journal: FoundryJournalExt, - { - let (cached_evm_env, cached_tx_env) = Env::clone_evm_and_tx(ecx); + ) -> (InterpreterResult, Option
) { + let cached_evm_env = ecx.evm_clone(); + let cached_tx_env = ecx.tx_clone(); ecx.block_mut().set_basefee(0); @@ -766,12 +726,13 @@ impl InspectorStackRefMut<'_> { self.inner_context_data = Some(InnerContextData { original_origin: cached_tx_env.caller }); self.in_inner_context = true; - let (evm_env, tx_env) = Env::clone_evm_and_tx(ecx); + let evm_env = ecx.evm_clone(); + let tx_env = ecx.tx_clone(); let res = self.with_inspector(|mut inspector| { let (res, nested_env) = { - let (db, journal) = ecx.journal_mut().as_db_and_inner(); - let mut evm = new_evm_with_inspector(db, evm_env, tx_env.clone(), &mut inspector); + let (db, journal) = ecx.db_journal_inner_mut(); + let mut evm = new_revm_with_inspector(db, evm_env, &mut inspector); evm.journal_inner_mut().state = { let mut state = journal.state.clone(); @@ -795,8 +756,8 @@ impl InspectorStackRefMut<'_> { // set depth to 1 to make sure traces are collected correctly evm.journal_inner_mut().depth = 1; - let res = evm.transact(tx_env); - let (nested_evm_env, _) = evm.to_env(); + let res = evm.transact_raw(tx_env); + let nested_evm_env = evm.ctx_ref().evm_clone(); (res, nested_evm_env) }; @@ -804,7 +765,8 @@ impl InspectorStackRefMut<'_> { // but restoring the original tx and basefee (which we zeroed for the nested call). let mut restored_evm_env = nested_env; restored_evm_env.block_env.basefee = cached_evm_env.block_env.basefee; - Env::apply_evm_and_tx(ecx, restored_evm_env, cached_tx_env); + ecx.set_evm(restored_evm_env); + ecx.set_tx(cached_tx_env); res }); @@ -927,7 +889,7 @@ impl InspectorStackRefMut<'_> { // delegate to `InspectorStackRefMut` in this case. #[inline(always)] - fn step_inlined(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { + fn step_inlined(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { call_inspectors!( [ // These are sorted in definition order. @@ -946,11 +908,7 @@ impl InspectorStackRefMut<'_> { } #[inline(always)] - fn step_end_inlined( - &mut self, - interpreter: &mut Interpreter, - ecx: &mut CTX, - ) { + fn step_end_inlined(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { call_inspectors!( [ // These are sorted in definition order. @@ -966,7 +924,7 @@ impl InspectorStackRefMut<'_> { } } -impl Inspector for InspectorStackRefMut<'_> { +impl Inspector for InspectorStackRefMut<'_> { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { call_inspectors!( [ @@ -1074,7 +1032,7 @@ impl Inspector for InspectorStackRefMut<'_> { } // Mark accounts and storage cold before STATICCALLs CallScheme::StaticCall => { - let (_, journal_inner) = ecx.journal_mut().as_db_and_inner(); + let (_, journal_inner) = ecx.db_journal_inner_mut(); let JournaledState { state, warm_addresses, .. } = journal_inner; for (addr, acc_mut) in state { // Do not mark accounts and storage cold accounts with arbitrary storage. @@ -1171,7 +1129,7 @@ impl Inspector for InspectorStackRefMut<'_> { } } -impl FoundryInspectorExt for InspectorStackRefMut<'_> { +impl InspectorExt for InspectorStackRefMut<'_> { fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool { call_inspectors!( #[ret] @@ -1183,7 +1141,7 @@ impl FoundryInspectorExt for InspectorStackRefMut<'_> { } fn console_log(&mut self, msg: &str) { - call_inspectors!([&mut self.log_collector], |inspector| FoundryInspectorExt::console_log( + call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log( inspector, msg )); } @@ -1197,7 +1155,7 @@ impl FoundryInspectorExt for InspectorStackRefMut<'_> { } } -impl Inspector for InspectorStack { +impl Inspector for InspectorStack { fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) { self.as_mut().step_inlined(interpreter, ecx) } @@ -1241,7 +1199,7 @@ impl Inspector for InspectorStack { } } -impl FoundryInspectorExt for InspectorStack { +impl InspectorExt for InspectorStack { fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool { self.as_mut().should_use_create2_factory(depth, inputs) } diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index b93f07dee9327..a301ddfd08786 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -13,8 +13,8 @@ pub mod inspectors; pub use foundry_evm_core as core; pub use foundry_evm_core::{ - Env, EvmEnv, FoundryInspectorExt, InspectorExt, backend, constants, decode, fork, hardfork, - opts, utils, + EvmEnv, FoundryInspectorExt, InspectorExt, backend, constants, decode, fork, hardfork, opts, + utils, }; pub use foundry_evm_coverage as coverage; pub use foundry_evm_fuzz as fuzz; diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 5e8c452c8695a..2c5965b1141f7 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -321,7 +321,10 @@ pub enum TraceMode { /// /// Used by debugger. Debug, - /// Debug trace with storage changes. + /// Step trace with storage change recording. + /// + /// Records JUMP/JUMPDEST steps (like `Steps`) plus storage diffs on SLOAD/SSTORE. + /// Does not enable memory/stack snapshots or unfiltered opcode recording. RecordStateDiff, } @@ -370,9 +373,9 @@ impl TraceMode { match verbosity { 0..3 => self, 3..=4 => std::cmp::max(self, Self::Call), - // Enable step recording for backtraces when verbosity is 5 or higher. - // We need to ensure we're recording JUMP AND JUMPDEST steps. - _ => std::cmp::min(self, Self::Steps), + // Enable step recording and state diff recording when verbosity is 5 or higher. + // This includes backtraces (JUMP/JUMPDEST steps) and storage changes. + _ => std::cmp::max(self, Self::RecordStateDiff), } } @@ -380,23 +383,128 @@ impl TraceMode { if self.is_none() { None } else { + // RecordStateDiff is Steps + state diff recording, not Debug + state diff. + // It should not enable memory/stack snapshots. + // State diff recording requires all opcodes (no filter) since it needs + // SLOAD/SSTORE steps, not just JUMP/JUMPDEST. + let effective = if self.record_state_diff() { Self::Steps } else { self }; TracingInspectorConfig { record_steps: self >= Self::Steps, - record_memory_snapshots: self >= Self::Jump, - record_stack_snapshots: if self > Self::Steps { + record_memory_snapshots: effective >= Self::Jump, + record_stack_snapshots: if effective > Self::Steps { StackSnapshotType::Full } else { StackSnapshotType::None }, record_logs: true, record_state_diff: self.record_state_diff(), - record_returndata_snapshots: self.is_debug(), - record_opcodes_filter: (self.is_steps() || self.is_jump() || self.is_jump_simple()) - .then(|| OpcodeFilter::new().enabled(OpCode::JUMP).enabled(OpCode::JUMPDEST)), + record_returndata_snapshots: effective.is_debug(), + // State diff needs all opcodes recorded to capture SLOAD/SSTORE. + record_opcodes_filter: if self.record_state_diff() { + None + } else { + (effective.is_steps() || effective.is_jump() || effective.is_jump_simple()) + .then(|| { + OpcodeFilter::new().enabled(OpCode::JUMP).enabled(OpCode::JUMPDEST) + }) + }, exclude_precompile_calls: false, - record_immediate_bytes: self.is_debug(), + record_immediate_bytes: effective.is_debug(), } .into() } } } + +#[cfg(test)] +mod tests { + use super::*; + + // -- TraceMode::with_verbosity level tests -- + + #[test] + fn verbosity_0_through_2_is_noop() { + for v in 0..=2 { + assert_eq!(TraceMode::None.with_verbosity(v), TraceMode::None, "v={v}"); + assert_eq!(TraceMode::Call.with_verbosity(v), TraceMode::Call, "v={v}"); + assert_eq!(TraceMode::Debug.with_verbosity(v), TraceMode::Debug, "v={v}"); + } + } + + #[test] + fn verbosity_3_and_4_raises_to_call() { + for v in 3..=4 { + assert_eq!(TraceMode::None.with_verbosity(v), TraceMode::Call, "v={v}"); + // Already above Call — must not downgrade. + assert_eq!(TraceMode::Debug.with_verbosity(v), TraceMode::Debug, "v={v}"); + assert_eq!( + TraceMode::RecordStateDiff.with_verbosity(v), + TraceMode::RecordStateDiff, + "v={v}" + ); + } + } + + #[test] + fn verbosity_5_raises_to_record_state_diff() { + assert_eq!(TraceMode::None.with_verbosity(5), TraceMode::RecordStateDiff); + assert_eq!(TraceMode::Call.with_verbosity(5), TraceMode::RecordStateDiff); + assert_eq!(TraceMode::Steps.with_verbosity(5), TraceMode::RecordStateDiff); + assert_eq!(TraceMode::Debug.with_verbosity(5), TraceMode::RecordStateDiff); + // Already at the top — stays the same. + assert_eq!(TraceMode::RecordStateDiff.with_verbosity(5), TraceMode::RecordStateDiff); + } + + // -- into_config at each verbosity level -- + + #[test] + fn config_at_verbosity_0_is_none() { + let mode = TraceMode::None.with_verbosity(0); + assert!(mode.into_config().is_none()); + } + + #[test] + fn config_at_verbosity_3_records_calls_only() { + let cfg = TraceMode::None.with_verbosity(3).into_config().unwrap(); + assert!(!cfg.record_steps, "verbosity 3 should not record steps"); + assert!(!cfg.record_state_diff, "verbosity 3 should not record state diff"); + assert!(cfg.record_logs, "verbosity 3 should record logs"); + } + + #[test] + fn config_at_verbosity_5_records_steps_and_state_diff() { + let cfg = TraceMode::None.with_verbosity(5).into_config().unwrap(); + assert!(cfg.record_steps, "verbosity 5 must record steps for backtraces"); + assert!(cfg.record_state_diff, "verbosity 5 must record state diff"); + assert!(cfg.record_logs, "verbosity 5 must record logs"); + // RecordStateDiff should NOT enable expensive debug-level features. + assert!(!cfg.record_memory_snapshots, "verbosity 5 should not record memory snapshots"); + assert_eq!( + cfg.record_stack_snapshots, + StackSnapshotType::None, + "verbosity 5 should not record stack snapshots" + ); + // State diff requires all opcodes to capture SLOAD/SSTORE, so no filter. + assert!( + cfg.record_opcodes_filter.is_none(), + "verbosity 5 needs unfiltered opcodes for state diff" + ); + } + + #[test] + fn config_debug_mode_unchanged() { + // Debug mode must still enable full recording for the debugger. + let cfg = TraceMode::Debug.into_config().unwrap(); + assert!(cfg.record_steps); + assert!(cfg.record_memory_snapshots, "Debug must record memory snapshots"); + assert_eq!( + cfg.record_stack_snapshots, + StackSnapshotType::Full, + "Debug must record full stack snapshots" + ); + assert!(cfg.record_returndata_snapshots, "Debug must record returndata"); + assert!(cfg.record_immediate_bytes, "Debug must record immediate bytes"); + assert!(cfg.record_opcodes_filter.is_none(), "Debug must record all opcodes (no filter)"); + assert!(!cfg.record_state_diff, "Debug alone should not record state diff"); + } +} diff --git a/crates/forge/src/cmd/inspect.rs b/crates/forge/src/cmd/inspect.rs index 9c1fb32775019..ac4c697b5e5ac 100644 --- a/crates/forge/src/cmd/inspect.rs +++ b/crates/forge/src/cmd/inspect.rs @@ -9,6 +9,7 @@ use foundry_common::{ find_matching_contract_artifact, find_target_path, shell, }; use foundry_compilers::{ + ProjectCompileOutput, artifacts::{ StorageLayout, output_selection::{ @@ -18,9 +19,11 @@ use foundry_compilers::{ }, solc::SolcLanguage, }; +use path_slash::PathExt; use regex::Regex; use serde_json::{Map, Value}; -use std::{collections::BTreeMap, fmt, str::FromStr, sync::LazyLock}; +use solar::sema::interface::source_map::FileName; +use std::{collections::BTreeMap, fmt, ops::ControlFlow, path::Path, str::FromStr, sync::LazyLock}; /// CLI arguments for `forge inspect`. #[derive(Clone, Debug, Parser)] @@ -76,8 +79,13 @@ impl InspectArgs { // Build the project let project = modified_build_args.project()?; - let compiler = ProjectCompiler::new().quiet(true); let target_path = find_target_path(&project, &contract)?; + if field == ContractArtifactField::Linearization && !is_solidity_source(&target_path) { + eyre::bail!( + "linearization inspection is only supported for Solidity contracts (.sol targets)" + ); + } + let compiler = ProjectCompiler::new().quiet(true); let mut output = compiler.files([target_path.clone()]).compile(&project)?; // Find the artifact @@ -169,6 +177,15 @@ impl InspectArgs { )?; } } + ContractArtifactField::Linearization => { + print_linearization( + &mut output, + project.root(), + &target_path, + contract.name(), + wrap, + )?; + } }; Ok(()) @@ -406,6 +423,111 @@ fn print_table( Ok(()) } +fn print_linearization( + output: &mut ProjectCompileOutput, + root: &Path, + target_path: &Path, + target_name: Option<&str>, + should_wrap: bool, +) -> Result<()> { + let mut chain = Vec::new(); + let mut lowered = false; + let compiler = output.parser_mut().solc_mut().compiler_mut(); + compiler.enter_mut(|compiler| -> Result<()> { + let Ok(ControlFlow::Continue(())) = compiler.lower_asts() else { return Ok(()) }; + lowered = true; + + let hir = &compiler.gcx().hir; + let matching_contracts = hir + .contract_ids() + .filter(|id| { + let contract = hir.contract(*id); + if let Some(target_name) = target_name + && contract.name.as_str() != target_name + { + return false; + } + + matches!( + &hir.source(contract.source).file.name, + FileName::Real(path) if path == target_path + ) + }) + .collect::>(); + + let target_contract = match matching_contracts.as_slice() { + [id] => *id, + [] => { + if let Some(target_name) = target_name { + eyre::bail!( + "Could not find contract `{target_name}` in `{}`", + target_path.display() + ); + } + eyre::bail!("Could not find contract in `{}`", target_path.display()); + } + _ => { + eyre::bail!( + "Multiple contracts found in the same file, please specify the target : or " + ); + } + }; + + for (order, base_id) in hir.contract(target_contract).linearized_bases.iter().enumerate() { + let contract = hir.contract(*base_id); + let source = hir.source(contract.source); + let FileName::Real(path) = &source.file.name else { continue }; + let path = path.strip_prefix(root).unwrap_or(path); + chain.push(( + order, + path.to_slash_lossy().into_owned(), + contract.name.as_str().to_string(), + )); + } + + Ok(()) + })?; + + // `compiler.sess()` inside of `ProjectCompileOutput` is built with `with_buffer_emitter`. + let diags = compiler.sess().dcx.emitted_diagnostics().unwrap(); + if compiler.sess().dcx.has_errors().is_err() { + eyre::bail!("{diags}"); + } else { + let _ = sh_eprint!("{diags}"); + } + if !lowered { + eyre::bail!( + "unable to inspect linearization: failed to lower Solidity ASTs for `{}`", + target_path.display() + ); + } + + if shell::is_json() { + let contracts = chain + .into_iter() + .map(|(order, source, contract)| { + serde_json::json!({ + "order": order, + "source": source, + "contract": contract, + }) + }) + .collect::>(); + return print_json(&contracts); + } + + let headers = vec![Cell::new("Order"), Cell::new("Source"), Cell::new("Contract")]; + print_table( + headers, + |table| { + for (order, source, contract) in &chain { + table.add_row([order.to_string(), source.to_string(), contract.to_string()]); + } + }, + should_wrap, + ) +} + /// Contract level output selection #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ContractArtifactField { @@ -428,6 +550,7 @@ pub enum ContractArtifactField { Events, StandardJson, Libraries, + Linearization, } macro_rules! impl_value_enum { @@ -512,6 +635,9 @@ impl_value_enum! { Events => "events" | "ev", StandardJson => "standardJson" | "standard-json" | "standard_json", Libraries => "libraries" | "lib" | "libs", + Linearization => "linearization" | "linearizedInheritance" + | "linearized-inheritance" | "linearized_inheritance" + | "linearizedBases" | "linearized-bases" | "linearized_bases", } } @@ -545,6 +671,9 @@ impl TryFrom for ContractOutputSelection { Err(eyre!("StandardJson is not supported for ContractOutputSelection")) } Caf::Libraries => Err(eyre!("Libraries is not supported for ContractOutputSelection")), + Caf::Linearization => { + Err(eyre!("Linearization is not supported for ContractOutputSelection")) + } } } } @@ -585,7 +714,11 @@ impl ContractArtifactField { pub const fn can_skip_field(&self) -> bool { matches!( self, - Self::Bytecode | Self::DeployedBytecode | Self::StandardJson | Self::Libraries + Self::Bytecode + | Self::DeployedBytecode + | Self::StandardJson + | Self::Libraries + | Self::Linearization ) } } @@ -632,6 +765,10 @@ fn get_json_str(obj: &impl serde::Serialize, key: Option<&str>) -> Result bool { + path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| ext.eq_ignore_ascii_case("sol")) +} + fn missing_error(field: &str) -> eyre::Error { eyre!( "{field} missing from artifact; \ @@ -662,6 +799,14 @@ mod tests { .to_string() .eq("Libraries is not supported for ContractOutputSelection") ); + } else if field == ContractArtifactField::Linearization { + let selection: Result = field.try_into(); + assert!( + selection + .unwrap_err() + .to_string() + .eq("Linearization is not supported for ContractOutputSelection") + ); } else { let selection: ContractOutputSelection = field.try_into().unwrap(); assert_eq!(field, selection); diff --git a/crates/forge/src/cmd/install.rs b/crates/forge/src/cmd/install.rs index b7aa4ca294098..b56d774a2b2ca 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -327,8 +327,10 @@ impl Installer<'_> { dep.tag = self.last_tag(path); } - // checkout the tag if necessary - self.git_checkout(&dep, path, false)?; + // checkout the tag if necessary, using recursive checkout to properly clean up + // nested submodules that may exist on the default branch but not on the target tag. + // See: https://github.com/foundry-rs/foundry/issues/13688 + self.git_checkout(&dep, path, true)?; trace!("updating dependency submodules recursively"); self.git.root(path).submodule_update( @@ -339,12 +341,51 @@ impl Installer<'_> { std::iter::empty::(), )?; + // remove nested .git directories from submodules before removing the top-level .git + Self::remove_nested_git_dirs(path)?; + // remove git artifacts fs::remove_dir_all(path.join(".git"))?; Ok(dep.tag) } + /// Recursively removes `.git` files/directories from nested submodules within `root`. + /// + /// Submodules typically have a `.git` file (not a directory) pointing to the parent's + /// `.git/modules/` directory. This cleans those up so the result is a plain folder tree. + fn remove_nested_git_dirs(root: &Path) -> Result<()> { + Self::remove_nested_git_dirs_inner(root, root) + } + + fn remove_nested_git_dirs_inner(root: &Path, dir: &Path) -> Result<()> { + let entries = match std::fs::read_dir(dir) { + Ok(entries) => entries, + Err(_) => return Ok(()), + }; + for entry in entries { + let entry = entry?; + let ft = entry.file_type()?; + + // never follow symlinks + if ft.is_symlink() { + continue; + } + + let path = entry.path(); + if path.file_name() == Some(".git".as_ref()) && path.parent() != Some(root) { + if ft.is_dir() { + fs::remove_dir_all(&path)?; + } else { + fs::remove_file(&path)?; + } + } else if ft.is_dir() { + Self::remove_nested_git_dirs_inner(root, &path)?; + } + } + Ok(()) + } + /// Installs the dependency as new submodule. /// /// This will add the git submodule to the given dir, initialize it and checkout the tag if diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index d8cea4a9e2b77..85d69e7c2488a 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -321,7 +321,7 @@ impl TestArgs { evm_opts.verbosity = 3; } - let env = evm_opts.env().await?; + let (evm_env, tx_env) = evm_opts.env().await?; // Enable internal tracing for more informative flamegraph. if should_draw && !self.decode_internal { @@ -345,12 +345,12 @@ impl TestArgs { .initial_balance(evm_opts.initial_balance) .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) - .with_fork(evm_opts.get_fork(&config, env.evm_env.clone())) + .with_fork(evm_opts.get_fork(&config, evm_env.clone())) .enable_isolation(evm_opts.isolate) .networks(evm_opts.networks) .fail_fast(self.fail_fast) .set_coverage(coverage) - .build::(output, env, evm_opts)?; + .build::(output, evm_env, tx_env, evm_opts)?; let libraries = runner.libraries.clone(); let mut outcome = self.run_tests_inner(runner, config, verbosity, filter, output).await?; @@ -522,7 +522,7 @@ impl TestArgs { } let remote_chain = - if runner.fork.is_some() { runner.env.tx.chain_id.map(Into::into) } else { None }; + if runner.fork.is_some() { runner.tx_env.chain_id.map(Into::into) } else { None }; let known_contracts = runner.known_contracts.clone(); let libraries = runner.libraries.clone(); @@ -682,7 +682,9 @@ impl TestArgs { } } - // Extract and display backtrace for failed tests when verbosity >= 3 + // Extract and display backtrace for failed tests when verbosity >= 3. + // At verbosity 3-4 backtraces show contract/function names only. + // At verbosity 5 backtraces include source file locations. if !silent && result.status.is_failure() && verbosity >= 3 diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 988191b7d7c0f..8d1e6970e9bbc 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -10,7 +10,6 @@ use eyre::Result; use foundry_cli::opts::configure_pcx_from_compile_output; use foundry_common::{ ContractsByArtifact, ContractsByArtifactBuilder, TestFunctionExt, get_contract_name, - shell::verbosity, }; use foundry_compilers::{ Artifact, ArtifactId, Compiler, ProjectCompileOutput, @@ -18,7 +17,7 @@ use foundry_compilers::{ }; use foundry_config::{Config, InlineConfig}; use foundry_evm::{ - Env, + EvmEnv, backend::Backend, decode::RevertDecoder, executors::{EarlyExit, Executor, ExecutorBuilder}, @@ -31,7 +30,7 @@ use foundry_evm::{ use foundry_evm_networks::NetworkConfigs; use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; -use revm::primitives::hardfork::SpecId; +use revm::{context::TxEnv, primitives::hardfork::SpecId}; use std::{ borrow::Borrow, collections::BTreeMap, @@ -294,7 +293,9 @@ pub struct TestRunnerConfig { /// EVM configuration. pub evm_opts: EvmOpts, /// EVM environment. - pub env: Env, + pub evm_env: EvmEnv, + /// Transaction environment. + pub tx_env: TxEnv, /// EVM version. pub spec_id: SpecId, /// The address which will be used to deploy the initial contracts and send all transactions. @@ -389,7 +390,7 @@ impl TestRunnerConfig { .spec_id(self.spec_id) .gas_limit(self.evm_opts.gas_limit()) .legacy_assertions(self.config.legacy_assertions) - .build(self.env.clone(), db) + .build(self.evm_env.clone(), self.tx_env.clone(), db) } fn trace_mode(&self) -> TraceMode { @@ -397,7 +398,6 @@ impl TestRunnerConfig { .with_debug(self.debug) .with_decode_internal(self.decode_internal) .with_verbosity(self.evm_opts.verbosity) - .with_state_changes(verbosity() > 4) } } @@ -502,7 +502,8 @@ impl MultiContractRunnerBuilder { pub fn build>( self, output: &ProjectCompileOutput, - env: Env, + evm_env: EvmEnv, + tx_env: TxEnv, evm_opts: EvmOpts, ) -> Result { let root = &self.config.root; @@ -601,7 +602,8 @@ impl MultiContractRunnerBuilder { tcfg: TestRunnerConfig { evm_opts, - env, + evm_env, + tx_env, spec_id: self.evm_spec.unwrap_or_else(|| self.config.evm_spec_id()), sender: self.sender.unwrap_or(self.config.sender), line_coverage: self.line_coverage, diff --git a/crates/forge/tests/cli/backtrace.rs b/crates/forge/tests/cli/backtrace.rs index a38c60de6bc30..434fa4cb289ba 100644 --- a/crates/forge/tests/cli/backtrace.rs +++ b/crates/forge/tests/cli/backtrace.rs @@ -548,3 +548,141 @@ Backtrace: ... "#]]); }); + +// Test that backtraces only appear at verbosity 5 (-vvvvv). +// Runs the same failing test at every verbosity level to assert correct output. +#[cfg(not(feature = "isolate-by-default"))] +forgetest!(test_backtrace_verbosity_levels, |prj, cmd| { + prj.insert_ds_test(); + prj.insert_vm(); + + prj.add_source( + "SimpleRevert.sol", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SimpleRevert { + function doRevert() public pure { + revert("Simple revert message"); + } +} +"#, + ); + + prj.add_test( + "BacktraceVerbosity.t.sol", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../src/test.sol"; +import "../src/SimpleRevert.sol"; + +contract BacktraceVerbosityTest is DSTest { + SimpleRevert simpleRevert; + + function setUp() public { + simpleRevert = new SimpleRevert(); + } + + function testRevert() public { + simpleRevert.doRevert(); + } +} +"#, + ); + + // -v (verbosity 1): no traces, no backtrace. + cmd.args(["test", "--mc", "BacktraceVerbosityTest", "-v"]).assert_failure().stdout_eq(str![[ + r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +... +Ran 1 test for test/BacktraceVerbosity.t.sol:BacktraceVerbosityTest +[FAIL: Simple revert message] testRevert() ([GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"# + ]]); + + // -vvv (verbosity 3): traces and backtrace WITHOUT source locations. + cmd.forge_fuse() + .args(["test", "--mc", "BacktraceVerbosityTest", "-vvv"]) + .assert_failure() + .stdout_eq(str![[r#" +No files changed, compilation skipped + +Ran 1 test for test/BacktraceVerbosity.t.sol:BacktraceVerbosityTest +[FAIL: Simple revert message] testRevert() ([GAS]) +Traces: + [..] BacktraceVerbosityTest::testRevert() + ├─ [..] SimpleRevert::doRevert() [staticcall] + │ └─ ← [Revert] Simple revert message + └─ ← [Revert] Simple revert message + +Backtrace: + at SimpleRevert.doRevert + at BacktraceVerbosityTest.testRevert + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"#]]); + + // -vvvv (verbosity 4): traces with setup and backtrace WITHOUT source locations. + cmd.forge_fuse() + .args(["test", "--mc", "BacktraceVerbosityTest", "-vvvv"]) + .assert_failure() + .stdout_eq(str![[r#" +No files changed, compilation skipped + +Ran 1 test for test/BacktraceVerbosity.t.sol:BacktraceVerbosityTest +[FAIL: Simple revert message] testRevert() ([GAS]) +Traces: + [..] BacktraceVerbosityTest::setUp() + ├─ [..] → new SimpleRevert@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] [..] + └─ ← [Stop] + + [..] BacktraceVerbosityTest::testRevert() + ├─ [..] SimpleRevert::doRevert() [staticcall] + │ └─ ← [Revert] Simple revert message + └─ ← [Revert] Simple revert message + +Backtrace: + at SimpleRevert.doRevert + at BacktraceVerbosityTest.testRevert + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"#]]); + + // -vvvvv (verbosity 5): traces with setup, storage changes, and backtrace WITH source + // locations. + cmd.forge_fuse() + .args(["test", "--mc", "BacktraceVerbosityTest", "-vvvvv"]) + .assert_failure() + .stdout_eq(str![[r#" +No files changed, compilation skipped + +Ran 1 test for test/BacktraceVerbosity.t.sol:BacktraceVerbosityTest +[FAIL: Simple revert message] testRevert() ([GAS]) +Traces: + [..] BacktraceVerbosityTest::setUp() + ├─ [..] → new SimpleRevert@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f + │ └─ ← [Return] [..] + └─ ← [Stop] + + [..] BacktraceVerbosityTest::testRevert() + ├─ [..] SimpleRevert::doRevert() [staticcall] + │ └─ ← [Revert] Simple revert message + └─ ← [Revert] Simple revert message + +Backtrace: + at SimpleRevert.doRevert (src/SimpleRevert.sol:[..]:[..]) + at BacktraceVerbosityTest.testRevert (test/BacktraceVerbosity.t.sol:[..]:[..]) + +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"#]]); +}); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 30f0f43001b60..9f352a0109a66 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1227,6 +1227,99 @@ contract Foo { ]]); }); +forgetest!(can_inspect_linearization_markdown, |prj, cmd| { + prj.add_source("A.sol", "contract A {}"); + prj.add_source("B.sol", r#"import {A} from "./A.sol"; contract B is A {}"#); + prj.add_source("C.sol", r#"import {B} from "./B.sol"; contract C is B {}"#); + + cmd.forge_fuse().args(["inspect", "C", "linearization", "--md"]).assert_success().stdout_eq( + str![[r#" + +| Order | Source | Contract | +|-------|-----------|----------| +| 0 | src/C.sol | C | +| 1 | src/B.sol | B | +| 2 | src/A.sol | A | + + +"#]], + ); +}); + +forgetest!(can_inspect_linearization_json, |prj, cmd| { + prj.add_source("A.sol", "contract A {}"); + prj.add_source("B.sol", r#"import {A} from "./A.sol"; contract B is A {}"#); + prj.add_source("C.sol", r#"import {B} from "./B.sol"; contract C is B {}"#); + + cmd.forge_fuse().args(["inspect", "C", "linearization", "--json"]).assert_success().stdout_eq( + str![[r#" +[ + { + "order": 0, + "source": "src/C.sol", + "contract": "C" + }, + { + "order": 1, + "source": "src/B.sol", + "contract": "B" + }, + { + "order": 2, + "source": "src/A.sol", + "contract": "A" + } +] + +"#]], + ); +}); + +forgetest!(can_inspect_linearization_path_qualified_contract, |prj, cmd| { + prj.add_source("one/Base.sol", "contract Base {}"); + prj.add_source( + "one/Target.sol", + r#"import {Base} from "./Base.sol"; contract Target is Base {}"#, + ); + + prj.add_source("two/Base.sol", "contract Base {}"); + prj.add_source( + "two/Target.sol", + r#"import {Base} from "./Base.sol"; contract Target is Base {}"#, + ); + + cmd.forge_fuse() + .args(["inspect", "src/two/Target.sol:Target", "linearization", "--json"]) + .assert_success() + .stdout_eq(str![[r#" +[ + { + "order": 0, + "source": "src/two/Target.sol", + "contract": "Target" + }, + { + "order": 1, + "source": "src/two/Base.sol", + "contract": "Base" + } +] + +"#]]); +}); + +forgetest!(cannot_inspect_linearization_non_solidity_target, |prj, cmd| { + prj.create_file("src/NotSol.vy", "x: uint256"); + + cmd.forge_fuse() + .args(["inspect", "src/NotSol.vy", "linearization"]) + .assert_failure() + .stderr_eq(str![[r#" +Error: linearization inspection is only supported for Solidity contracts (.sol targets) + +"#]]); +}); + // test that `forge snapshot` commands work forgetest!(can_check_snapshot, |prj, cmd| { prj.insert_ds_test(); diff --git a/crates/forge/tests/cli/install.rs b/crates/forge/tests/cli/install.rs index f95f865f92bfb..838d791c5d0e7 100644 --- a/crates/forge/tests/cli/install.rs +++ b/crates/forge/tests/cli/install.rs @@ -591,6 +591,49 @@ async fn correctly_sync_dep_with_multiple_version() { assert_eq!(solday_v_245.rev(), submod_solday_v_245.rev()); } +// Regression test: `forge install --no-git` should clean up nested submodule contents +// when installing a tag that does not use submodules for its dependencies. +// https://github.com/foundry-rs/foundry/issues/13688 +forgetest!(flaky_install_no_git_cleans_nested_submodules, |prj, cmd| { + cmd.git_init(); + + // Install openzeppelin-contracts-upgradeable at v4.7.3 with --no-git. + // The default branch has submodules in lib/ (e.g. openzeppelin-contracts, erc4626-tests), + // but v4.7.3 does not use submodules for dependencies. + cmd.forge_fuse() + .args(["install", "--no-git", "OpenZeppelin/openzeppelin-contracts-upgradeable@v4.7.3"]) + .assert_success(); + + let dep_dir = prj.root().join("lib").join("openzeppelin-contracts-upgradeable"); + assert!(dep_dir.exists(), "dependency should be installed"); + + // The nested lib/ directory should either not exist or be empty — v4.7.3 does not use + // submodules so there should be no leftover submodule contents from the default branch. + let nested_lib = dep_dir.join("lib"); + if nested_lib.exists() { + let entries: Vec<_> = fs::read_dir(&nested_lib).unwrap().collect(); + assert!( + entries.is_empty(), + "nested lib/ should be empty after --no-git install at v4.7.3, found: {entries:?}" + ); + } + + // There should be no .git file or directory anywhere under the installed dependency. + fn assert_no_git(dir: &Path) { + for entry in fs::read_dir(dir).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.file_name() == Some(".git".as_ref()) { + panic!("found leftover .git at {}", path.display()); + } + if path.is_dir() { + assert_no_git(&path); + } + } + } + assert_no_git(&dep_dir); +}); + forgetest_init!(sync_on_forge_update, |prj, cmd| { let git = Git::new(prj.root()); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 7f3dc7b9cd7a5..2714011ec8d3d 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -2,6 +2,7 @@ use crate::constants::TEMPLATE_CONTRACT; use alloy_hardforks::EthereumHardfork; +use alloy_network::Ethereum; use alloy_primitives::{Address, Bytes, address, hex}; use anvil::{NodeConfig, spawn}; use forge_script_sequence::ScriptSequence; @@ -2419,7 +2420,8 @@ contract ContractScript is Script { .find(|file| file.ends_with("run-latest.json")) .expect("No broadcast artifacts"); - let sequence: ScriptSequence = foundry_common::fs::read_json_file(&run_latest).unwrap(); + let sequence: ScriptSequence = + foundry_common::fs::read_json_file(&run_latest).unwrap(); assert_eq!(sequence.transactions.len(), 2); assert_eq!(sequence.transactions[1].additional_contracts.len(), 1); @@ -2566,7 +2568,7 @@ maxFeePerGas maxPriorityFeePerGas nonce 0 to -type 0 +type EIP-1559 value 0 ### Transaction 2 ### @@ -2581,7 +2583,7 @@ maxFeePerGas maxPriorityFeePerGas nonce 1 to 0x5FbDB2315678afecb367f032d93F642f64180aa3 -type 0 +type EIP-1559 value 0 contract: Called(0x5FbDB2315678afecb367f032d93F642f64180aa3) data (decoded): run(uint256,uint256)( @@ -3061,7 +3063,7 @@ contract FactoryScript is Script { .assert_success(); let broadcast_log = prj.root().join("broadcast/Factory.s.sol/31337/run-latest.json"); - let script_sequence: ScriptSequence = serde_json::from_reader( + let script_sequence: ScriptSequence = serde_json::from_reader( fs::File::open(prj.artifacts().join(broadcast_log)).expect("no broadcast log"), ) .expect("no script sequence"); diff --git a/crates/forge/tests/cli/test_cmd/repros.rs b/crates/forge/tests/cli/test_cmd/repros.rs index 82ec6744a6172..32bfe6a98a9fd 100644 --- a/crates/forge/tests/cli/test_cmd/repros.rs +++ b/crates/forge/tests/cli/test_cmd/repros.rs @@ -843,6 +843,36 @@ Ran 1 test for test/Issue12803.t.sol:Issue12803Test ]); }); +// https://github.com/foundry-rs/foundry/issues/13766 +// vm.expectRevert(bytes("")) should not panic when actual revert has data +forgetest_init!(issue_13766, |prj, cmd| { + prj.add_test( + "Issue13766.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Reverter { + error CustomError(); + function revertWithData() public pure { revert CustomError(); } +} + +contract Issue13766Test is Test { + function test_expectRevertEmptyBytes() public { + Reverter r = new Reverter(); + vm.expectRevert(bytes("")); + r.revertWithData(); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +[FAIL: Error != expected error: CustomError() != EvmError: Revert] test_expectRevertEmptyBytes() ([GAS]) +... +"#]]); +}); + // https://github.com/foundry-rs/foundry/issues/12803 // Test multiple storage deletions (higher refund) don't cause underflow forgetest_init!(issue_12803_multiple_deletes, |prj, cmd| { diff --git a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json index 244a2d94fc7eb..39544b6ccbbe9 100644 --- a/crates/forge/tests/fixtures/SimpleContractTestVerbose.json +++ b/crates/forge/tests/fixtures/SimpleContractTestVerbose.json @@ -15,7 +15,9 @@ "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033130300000000000000000000000000000000000000000000000000000000000" } ], - "decoded_logs": ["100"], + "decoded_logs": [ + "100" + ], "kind": { "Unit": { "gas": "{...}" @@ -62,7 +64,11 @@ "arena": [ { "parent": null, - "children": [1, 2, 3], + "children": [ + 1, + 2, + 3 + ], "idx": 0, "trace": { "depth": 0, @@ -85,9 +91,9 @@ { "pc": 0, "op": 96, - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -101,9 +107,9 @@ { "pc": 2, "op": 96, - "stack": ["0x80"], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -117,9 +123,9 @@ { "pc": 4, "op": 82, - "stack": ["0x80", "0x40"], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -133,9 +139,9 @@ { "pc": 5, "op": 52, - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -149,9 +155,9 @@ { "pc": 6, "op": 128, - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -165,9 +171,9 @@ { "pc": 7, "op": 21, - "stack": ["0x0", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -181,9 +187,9 @@ { "pc": 8, "op": 97, - "stack": ["0x0", "0x1"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -197,9 +203,9 @@ { "pc": 11, "op": 87, - "stack": ["0x0", "0x1", "0xf"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -213,9 +219,9 @@ { "pc": 15, "op": 91, - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -229,9 +235,9 @@ { "pc": 16, "op": 80, - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -245,9 +251,9 @@ { "pc": 17, "op": 96, - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -261,9 +267,9 @@ { "pc": 19, "op": 54, - "stack": ["0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -277,9 +283,9 @@ { "pc": 20, "op": 16, - "stack": ["0x4", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -293,9 +299,9 @@ { "pc": 21, "op": 97, - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -309,9 +315,9 @@ { "pc": 24, "op": 87, - "stack": ["0x0", "0x4a"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -325,9 +331,9 @@ { "pc": 25, "op": 95, - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -341,9 +347,9 @@ { "pc": 26, "op": 53, - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -357,11 +363,9 @@ { "pc": 27, "op": 96, - "stack": [ - "0xf8a8fd6d00000000000000000000000000000000000000000000000000000000" - ], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -375,12 +379,9 @@ { "pc": 29, "op": 28, - "stack": [ - "0xf8a8fd6d00000000000000000000000000000000000000000000000000000000", - "0xe0" - ], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -394,9 +395,9 @@ { "pc": 30, "op": 128, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -410,9 +411,9 @@ { "pc": 31, "op": 99, - "stack": ["0xf8a8fd6d", "0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -426,9 +427,9 @@ { "pc": 36, "op": 20, - "stack": ["0xf8a8fd6d", "0xf8a8fd6d", "0xba414fa6"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -442,9 +443,9 @@ { "pc": 37, "op": 97, - "stack": ["0xf8a8fd6d", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -458,9 +459,9 @@ { "pc": 40, "op": 87, - "stack": ["0xf8a8fd6d", "0x0", "0x4e"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -474,9 +475,9 @@ { "pc": 41, "op": 128, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -490,9 +491,9 @@ { "pc": 42, "op": 99, - "stack": ["0xf8a8fd6d", "0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -506,9 +507,9 @@ { "pc": 47, "op": 20, - "stack": ["0xf8a8fd6d", "0xf8a8fd6d", "0xf6e62afc"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -522,9 +523,9 @@ { "pc": 48, "op": 97, - "stack": ["0xf8a8fd6d", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -538,9 +539,9 @@ { "pc": 51, "op": 87, - "stack": ["0xf8a8fd6d", "0x0", "0x6c"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -554,9 +555,9 @@ { "pc": 52, "op": 128, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -570,9 +571,9 @@ { "pc": 53, "op": 99, - "stack": ["0xf8a8fd6d", "0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -586,9 +587,9 @@ { "pc": 58, "op": 20, - "stack": ["0xf8a8fd6d", "0xf8a8fd6d", "0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -602,9 +603,9 @@ { "pc": 59, "op": 97, - "stack": ["0xf8a8fd6d", "0x1"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -618,9 +619,9 @@ { "pc": 62, "op": 87, - "stack": ["0xf8a8fd6d", "0x1", "0x8a"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -634,9 +635,9 @@ { "pc": 138, "op": 91, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -650,9 +651,9 @@ { "pc": 139, "op": 97, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -666,9 +667,9 @@ { "pc": 142, "op": 97, - "stack": ["0xf8a8fd6d", "0x92"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -682,9 +683,9 @@ { "pc": 145, "op": 86, - "stack": ["0xf8a8fd6d", "0x92", "0x25a"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -698,9 +699,9 @@ { "pc": 602, "op": 91, - "stack": ["0xf8a8fd6d", "0x92"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -714,9 +715,9 @@ { "pc": 603, "op": 95, - "stack": ["0xf8a8fd6d", "0x92"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -730,9 +731,9 @@ { "pc": 604, "op": 96, - "stack": ["0xf8a8fd6d", "0x92", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -746,9 +747,9 @@ { "pc": 606, "op": 81, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x40"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -762,9 +763,9 @@ { "pc": 607, "op": 97, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x80"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -778,9 +779,9 @@ { "pc": 610, "op": 144, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x80", "0x267"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -794,9 +795,9 @@ { "pc": 611, "op": 97, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x267", "0x80"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -810,16 +811,9 @@ { "pc": 614, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x80", - "0x40b" - ], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -833,9 +827,9 @@ { "pc": 1035, "op": 91, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x267", "0x80"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -849,9 +843,9 @@ { "pc": 1036, "op": 97, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x267", "0x80"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -865,16 +859,9 @@ { "pc": 1039, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x80", - "0x142" - ], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -888,17 +875,9 @@ { "pc": 1040, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x80", - "0x142", - "0x142" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -912,18 +891,9 @@ { "pc": 1043, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x80", - "0x142", - "0x142", - "0x6c0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -937,19 +907,9 @@ { "pc": 1044, "op": 57, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x80", - "0x142", - "0x142", - "0x6c0", - "0x80" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -963,16 +923,9 @@ { "pc": 1045, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x80", - "0x142" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -986,15 +939,9 @@ { "pc": 1046, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x267", - "0x1c2" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1008,15 +955,9 @@ { "pc": 1047, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x1c2", - "0x267" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1030,9 +971,9 @@ { "pc": 615, "op": 91, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2"], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1046,9 +987,9 @@ { "pc": 616, "op": 96, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2"], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1062,9 +1003,9 @@ { "pc": 618, "op": 81, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2", "0x40"], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1078,9 +1019,9 @@ { "pc": 619, "op": 128, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x1c2", "0x80"], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1094,16 +1035,9 @@ { "pc": 620, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x1c2", - "0x80", - "0x80" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1117,16 +1051,9 @@ { "pc": 621, "op": 3, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x80", - "0x80", - "0x1c2" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1140,9 +1067,9 @@ { "pc": 622, "op": 144, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x80", "0x142"], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1156,9 +1083,9 @@ { "pc": 623, "op": 95, - "stack": ["0xf8a8fd6d", "0x92", "0x0", "0x142", "0x80"], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1172,16 +1099,9 @@ { "pc": 624, "op": 240, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x142", - "0x80", - "0x0" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1195,14 +1115,9 @@ { "pc": 625, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -1216,15 +1131,9 @@ { "pc": 626, "op": 21, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629525, "gas_refund_counter": 0, @@ -1238,15 +1147,9 @@ { "pc": 627, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629522, "gas_refund_counter": 0, @@ -1260,16 +1163,9 @@ { "pc": 628, "op": 21, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0", - "0x0" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629519, "gas_refund_counter": 0, @@ -1283,16 +1179,9 @@ { "pc": 629, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0", - "0x1" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629516, "gas_refund_counter": 0, @@ -1306,17 +1195,9 @@ { "pc": 632, "op": 87, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0", - "0x1", - "0x280" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629513, "gas_refund_counter": 0, @@ -1330,15 +1211,9 @@ { "pc": 640, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629503, "gas_refund_counter": 0, @@ -1352,15 +1227,9 @@ { "pc": 641, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629502, "gas_refund_counter": 0, @@ -1374,14 +1243,9 @@ { "pc": 642, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629500, "gas_refund_counter": 0, @@ -1395,14 +1259,9 @@ { "pc": 643, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629497, "gas_refund_counter": 0, @@ -1416,13 +1275,9 @@ { "pc": 644, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629495, "gas_refund_counter": 0, @@ -1436,14 +1291,9 @@ { "pc": 645, "op": 115, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629492, "gas_refund_counter": 0, @@ -1457,15 +1307,9 @@ { "pc": 666, "op": 22, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xffffffffffffffffffffffffffffffffffffffff" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629489, "gas_refund_counter": 0, @@ -1479,14 +1323,9 @@ { "pc": 667, "op": 99, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629486, "gas_refund_counter": 0, @@ -1500,15 +1339,9 @@ { "pc": 672, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629483, "gas_refund_counter": 0, @@ -1522,16 +1355,9 @@ { "pc": 674, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64" - ], + "stack": null, "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629480, "gas_refund_counter": 0, @@ -1545,17 +1371,9 @@ { "pc": 676, "op": 81, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x40" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629477, "gas_refund_counter": 0, @@ -1569,17 +1387,9 @@ { "pc": 677, "op": 130, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629474, "gas_refund_counter": 0, @@ -1593,18 +1403,9 @@ { "pc": 678, "op": 99, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0xe26d1474" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629471, "gas_refund_counter": 0, @@ -1618,19 +1419,9 @@ { "pc": 683, "op": 22, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0xe26d1474", - "0xffffffff" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629468, "gas_refund_counter": 0, @@ -1644,18 +1435,9 @@ { "pc": 684, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0xe26d1474" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629465, "gas_refund_counter": 0, @@ -1669,19 +1451,9 @@ { "pc": 686, "op": 27, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0xe26d1474", - "0xe0" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629462, "gas_refund_counter": 0, @@ -1695,18 +1467,9 @@ { "pc": 687, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0xe26d147400000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629459, "gas_refund_counter": 0, @@ -1720,19 +1483,9 @@ { "pc": 688, "op": 82, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0xe26d147400000000000000000000000000000000000000000000000000000000", - "0x80" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000006080604052348015600e575f5ffd5b506101268061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629456, "gas_refund_counter": 0, @@ -1746,17 +1499,9 @@ { "pc": 689, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629453, "gas_refund_counter": 0, @@ -1770,18 +1515,9 @@ { "pc": 691, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x80", - "0x4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629450, "gas_refund_counter": 0, @@ -1795,17 +1531,9 @@ { "pc": 692, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629447, "gas_refund_counter": 0, @@ -1819,18 +1547,9 @@ { "pc": 695, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x64", - "0x84", - "0x2bd" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629444, "gas_refund_counter": 0, @@ -1844,18 +1563,9 @@ { "pc": 696, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x84", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629441, "gas_refund_counter": 0, @@ -1869,18 +1579,9 @@ { "pc": 697, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629438, "gas_refund_counter": 0, @@ -1894,19 +1595,9 @@ { "pc": 700, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0x651" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629435, "gas_refund_counter": 0, @@ -1920,18 +1611,9 @@ { "pc": 1617, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629427, "gas_refund_counter": 0, @@ -1945,18 +1627,9 @@ { "pc": 1618, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629426, "gas_refund_counter": 0, @@ -1970,19 +1643,9 @@ { "pc": 1619, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629424, "gas_refund_counter": 0, @@ -1996,20 +1659,9 @@ { "pc": 1621, "op": 130, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0x0", - "0x20" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629421, "gas_refund_counter": 0, @@ -2023,21 +1675,9 @@ { "pc": 1622, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0x0", - "0x20", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629418, "gas_refund_counter": 0, @@ -2051,20 +1691,9 @@ { "pc": 1623, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0x0", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629415, "gas_refund_counter": 0, @@ -2078,20 +1707,9 @@ { "pc": 1624, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629412, "gas_refund_counter": 0, @@ -2105,19 +1723,9 @@ { "pc": 1625, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629410, "gas_refund_counter": 0, @@ -2131,20 +1739,9 @@ { "pc": 1628, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629407, "gas_refund_counter": 0, @@ -2158,21 +1755,9 @@ { "pc": 1629, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629405, "gas_refund_counter": 0, @@ -2186,22 +1771,9 @@ { "pc": 1630, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x0", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629402, "gas_refund_counter": 0, @@ -2215,21 +1787,9 @@ { "pc": 1631, "op": 132, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629399, "gas_refund_counter": 0, @@ -2243,22 +1803,9 @@ { "pc": 1632, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629396, "gas_refund_counter": 0, @@ -2272,23 +1819,9 @@ { "pc": 1635, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x642" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629393, "gas_refund_counter": 0, @@ -2302,22 +1835,9 @@ { "pc": 1602, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629385, "gas_refund_counter": 0, @@ -2331,22 +1851,9 @@ { "pc": 1603, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629384, "gas_refund_counter": 0, @@ -2360,23 +1867,9 @@ { "pc": 1606, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629381, "gas_refund_counter": 0, @@ -2390,24 +1883,9 @@ { "pc": 1607, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629378, "gas_refund_counter": 0, @@ -2421,25 +1899,9 @@ { "pc": 1610, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x621" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629375, "gas_refund_counter": 0, @@ -2453,24 +1915,9 @@ { "pc": 1569, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629367, "gas_refund_counter": 0, @@ -2484,24 +1931,9 @@ { "pc": 1570, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629366, "gas_refund_counter": 0, @@ -2515,25 +1947,9 @@ { "pc": 1571, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629364, "gas_refund_counter": 0, @@ -2547,26 +1963,9 @@ { "pc": 1574, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629361, "gas_refund_counter": 0, @@ -2580,27 +1979,9 @@ { "pc": 1577, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629358, "gas_refund_counter": 0, @@ -2614,28 +1995,9 @@ { "pc": 1580, "op": 132, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629355, "gas_refund_counter": 0, @@ -2649,29 +2011,9 @@ { "pc": 1581, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629352, "gas_refund_counter": 0, @@ -2685,30 +2027,9 @@ { "pc": 1584, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64", - "0x606" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629349, "gas_refund_counter": 0, @@ -2722,29 +2043,9 @@ { "pc": 1542, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629341, "gas_refund_counter": 0, @@ -2758,29 +2059,9 @@ { "pc": 1543, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629340, "gas_refund_counter": 0, @@ -2794,30 +2075,9 @@ { "pc": 1544, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629338, "gas_refund_counter": 0, @@ -2831,31 +2091,9 @@ { "pc": 1545, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629335, "gas_refund_counter": 0, @@ -2869,31 +2107,9 @@ { "pc": 1546, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629332, "gas_refund_counter": 0, @@ -2907,30 +2123,9 @@ { "pc": 1547, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x631", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629330, "gas_refund_counter": 0, @@ -2944,30 +2139,9 @@ { "pc": 1548, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x64", - "0x631" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629327, "gas_refund_counter": 0, @@ -2981,30 +2155,9 @@ { "pc": 1549, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x631", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629324, "gas_refund_counter": 0, @@ -3018,29 +2171,9 @@ { "pc": 1550, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x631" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629322, "gas_refund_counter": 0, @@ -3054,28 +2187,9 @@ { "pc": 1585, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629314, "gas_refund_counter": 0, @@ -3089,28 +2203,9 @@ { "pc": 1586, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629313, "gas_refund_counter": 0, @@ -3124,29 +2219,9 @@ { "pc": 1589, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x618" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629310, "gas_refund_counter": 0, @@ -3160,28 +2235,9 @@ { "pc": 1560, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629302, "gas_refund_counter": 0, @@ -3195,28 +2251,9 @@ { "pc": 1561, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629301, "gas_refund_counter": 0, @@ -3230,29 +2267,9 @@ { "pc": 1562, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629299, "gas_refund_counter": 0, @@ -3266,30 +2283,9 @@ { "pc": 1563, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629296, "gas_refund_counter": 0, @@ -3303,30 +2299,9 @@ { "pc": 1564, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629293, "gas_refund_counter": 0, @@ -3340,29 +2315,9 @@ { "pc": 1565, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x636", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629291, "gas_refund_counter": 0, @@ -3376,29 +2331,9 @@ { "pc": 1566, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x64", - "0x636" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629288, "gas_refund_counter": 0, @@ -3412,29 +2347,9 @@ { "pc": 1567, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x636", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629285, "gas_refund_counter": 0, @@ -3448,28 +2363,9 @@ { "pc": 1568, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x636" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629283, "gas_refund_counter": 0, @@ -3483,27 +2379,9 @@ { "pc": 1590, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629275, "gas_refund_counter": 0, @@ -3517,27 +2395,9 @@ { "pc": 1591, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629274, "gas_refund_counter": 0, @@ -3551,28 +2411,9 @@ { "pc": 1594, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x60f" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629271, "gas_refund_counter": 0, @@ -3586,27 +2427,9 @@ { "pc": 1551, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629263, "gas_refund_counter": 0, @@ -3620,27 +2443,9 @@ { "pc": 1552, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629262, "gas_refund_counter": 0, @@ -3654,28 +2459,9 @@ { "pc": 1553, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629260, "gas_refund_counter": 0, @@ -3689,29 +2475,9 @@ { "pc": 1554, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629257, "gas_refund_counter": 0, @@ -3725,29 +2491,9 @@ { "pc": 1555, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629254, "gas_refund_counter": 0, @@ -3761,28 +2507,9 @@ { "pc": 1556, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x63b", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629252, "gas_refund_counter": 0, @@ -3796,28 +2523,9 @@ { "pc": 1557, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x64", - "0x64", - "0x63b" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629249, "gas_refund_counter": 0, @@ -3831,28 +2539,9 @@ { "pc": 1558, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x64", - "0x63b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629246, "gas_refund_counter": 0, @@ -3866,27 +2555,9 @@ { "pc": 1559, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x64", - "0x63b" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629244, "gas_refund_counter": 0, @@ -3900,26 +2571,9 @@ { "pc": 1595, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629236, "gas_refund_counter": 0, @@ -3933,26 +2587,9 @@ { "pc": 1596, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629235, "gas_refund_counter": 0, @@ -3966,26 +2603,9 @@ { "pc": 1597, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629232, "gas_refund_counter": 0, @@ -3999,25 +2619,9 @@ { "pc": 1598, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64b", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629230, "gas_refund_counter": 0, @@ -4031,25 +2635,9 @@ { "pc": 1599, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64", - "0x64", - "0x64b" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629227, "gas_refund_counter": 0, @@ -4063,25 +2651,9 @@ { "pc": 1600, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64", - "0x64b", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629224, "gas_refund_counter": 0, @@ -4095,24 +2667,9 @@ { "pc": 1601, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64", - "0x64b" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629222, "gas_refund_counter": 0, @@ -4126,23 +2683,9 @@ { "pc": 1611, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629214, "gas_refund_counter": 0, @@ -4156,23 +2699,9 @@ { "pc": 1612, "op": 130, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629213, "gas_refund_counter": 0, @@ -4186,24 +2715,9 @@ { "pc": 1613, "op": 82, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64", - "0x64", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629210, "gas_refund_counter": 0, @@ -4217,22 +2731,9 @@ { "pc": 1614, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629207, "gas_refund_counter": 0, @@ -4246,21 +2747,9 @@ { "pc": 1615, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629205, "gas_refund_counter": 0, @@ -4274,20 +2763,9 @@ { "pc": 1616, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4", - "0x664" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629203, "gas_refund_counter": 0, @@ -4301,19 +2779,9 @@ { "pc": 1636, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629195, "gas_refund_counter": 0, @@ -4327,19 +2795,9 @@ { "pc": 1637, "op": 146, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0x2bd", - "0x64", - "0x84", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629194, "gas_refund_counter": 0, @@ -4353,19 +2811,9 @@ { "pc": 1638, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x64", - "0x84", - "0x2bd" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629191, "gas_refund_counter": 0, @@ -4379,19 +2827,9 @@ { "pc": 1639, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x2bd", - "0x84", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629188, "gas_refund_counter": 0, @@ -4405,18 +2843,9 @@ { "pc": 1640, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x2bd", - "0x84" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629186, "gas_refund_counter": 0, @@ -4430,17 +2859,9 @@ { "pc": 1641, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x2bd" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629184, "gas_refund_counter": 0, @@ -4454,16 +2875,9 @@ { "pc": 701, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629176, "gas_refund_counter": 0, @@ -4477,16 +2891,9 @@ { "pc": 702, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073629175, "gas_refund_counter": 0, @@ -4500,17 +2907,9 @@ { "pc": 703, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629173, "gas_refund_counter": 0, @@ -4524,18 +2923,9 @@ { "pc": 705, "op": 81, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x40" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629170, "gas_refund_counter": 0, @@ -4549,18 +2939,9 @@ { "pc": 706, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629167, "gas_refund_counter": 0, @@ -4574,19 +2955,9 @@ { "pc": 707, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629164, "gas_refund_counter": 0, @@ -4600,20 +2971,9 @@ { "pc": 708, "op": 3, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x80", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629161, "gas_refund_counter": 0, @@ -4627,19 +2987,9 @@ { "pc": 709, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629158, "gas_refund_counter": 0, @@ -4653,20 +3003,9 @@ { "pc": 710, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629155, "gas_refund_counter": 0, @@ -4680,21 +3019,9 @@ { "pc": 711, "op": 135, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629153, "gas_refund_counter": 0, @@ -4708,22 +3035,9 @@ { "pc": 712, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629150, "gas_refund_counter": 0, @@ -4737,23 +3051,9 @@ { "pc": 713, "op": 59, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629147, "gas_refund_counter": 0, @@ -4767,23 +3067,9 @@ { "pc": 714, "op": 21, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x126" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629047, "gas_refund_counter": 0, @@ -4797,23 +3083,9 @@ { "pc": 715, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629044, "gas_refund_counter": 0, @@ -4827,24 +3099,9 @@ { "pc": 716, "op": 21, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629041, "gas_refund_counter": 0, @@ -4858,24 +3115,9 @@ { "pc": 717, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0", - "0x1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629038, "gas_refund_counter": 0, @@ -4889,25 +3131,9 @@ { "pc": 720, "op": 87, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0", - "0x1", - "0x2d4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629035, "gas_refund_counter": 0, @@ -4921,23 +3147,9 @@ { "pc": 724, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629025, "gas_refund_counter": 0, @@ -4951,23 +3163,9 @@ { "pc": 725, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629024, "gas_refund_counter": 0, @@ -4981,22 +3179,9 @@ { "pc": 726, "op": 90, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629022, "gas_refund_counter": 0, @@ -5010,23 +3195,9 @@ { "pc": 727, "op": 241, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x80", - "0x24", - "0x80", - "0x0", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x3ffe475c" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073629020, "gas_refund_counter": 0, @@ -5040,17 +3211,9 @@ { "pc": 728, "op": 21, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606406, "gas_refund_counter": 0, @@ -5064,17 +3227,9 @@ { "pc": 729, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606403, "gas_refund_counter": 0, @@ -5088,18 +3243,9 @@ { "pc": 730, "op": 21, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606400, "gas_refund_counter": 0, @@ -5113,18 +3259,9 @@ { "pc": 731, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606397, "gas_refund_counter": 0, @@ -5138,19 +3275,9 @@ { "pc": 734, "op": 87, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0", - "0x1", - "0x2e6" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606394, "gas_refund_counter": 0, @@ -5164,17 +3291,9 @@ { "pc": 742, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606384, "gas_refund_counter": 0, @@ -5188,17 +3307,9 @@ { "pc": 743, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606383, "gas_refund_counter": 0, @@ -5212,16 +3323,9 @@ { "pc": 744, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474", - "0xa4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606381, "gas_refund_counter": 0, @@ -5235,15 +3339,9 @@ { "pc": 745, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0xe26d1474" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606379, "gas_refund_counter": 0, @@ -5257,14 +3355,9 @@ { "pc": 746, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606377, "gas_refund_counter": 0, @@ -5278,13 +3371,9 @@ { "pc": 747, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606375, "gas_refund_counter": 0, @@ -5298,14 +3387,9 @@ { "pc": 750, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606372, "gas_refund_counter": 0, @@ -5319,15 +3403,9 @@ { "pc": 752, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606369, "gas_refund_counter": 0, @@ -5341,16 +3419,9 @@ { "pc": 755, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x32e" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606366, "gas_refund_counter": 0, @@ -5364,15 +3435,9 @@ { "pc": 814, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606358, "gas_refund_counter": 0, @@ -5386,15 +3451,9 @@ { "pc": 815, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606357, "gas_refund_counter": 0, @@ -5408,16 +3467,9 @@ { "pc": 818, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073606354, "gas_refund_counter": 0, @@ -5431,17 +3483,9 @@ { "pc": 819, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606351, "gas_refund_counter": 0, @@ -5455,18 +3499,9 @@ { "pc": 821, "op": 81, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x64", - "0x40" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606348, "gas_refund_counter": 0, @@ -5480,18 +3515,9 @@ { "pc": 822, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x64", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606345, "gas_refund_counter": 0, @@ -5505,19 +3531,9 @@ { "pc": 824, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x64", - "0x80", - "0x24" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606342, "gas_refund_counter": 0, @@ -5531,18 +3547,9 @@ { "pc": 825, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x64", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606339, "gas_refund_counter": 0, @@ -5556,19 +3563,9 @@ { "pc": 828, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x64", - "0xa4", - "0x342" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606336, "gas_refund_counter": 0, @@ -5582,19 +3579,9 @@ { "pc": 829, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0xa4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606333, "gas_refund_counter": 0, @@ -5608,19 +3595,9 @@ { "pc": 830, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606330, "gas_refund_counter": 0, @@ -5634,20 +3611,9 @@ { "pc": 833, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0x679" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606327, "gas_refund_counter": 0, @@ -5661,19 +3627,9 @@ { "pc": 1657, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606319, "gas_refund_counter": 0, @@ -5687,19 +3643,9 @@ { "pc": 1658, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606318, "gas_refund_counter": 0, @@ -5713,20 +3659,9 @@ { "pc": 1659, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606316, "gas_refund_counter": 0, @@ -5740,21 +3675,9 @@ { "pc": 1661, "op": 130, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0x0", - "0x20" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606313, "gas_refund_counter": 0, @@ -5768,22 +3691,9 @@ { "pc": 1662, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0x0", - "0x20", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606310, "gas_refund_counter": 0, @@ -5797,21 +3707,9 @@ { "pc": 1663, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0x0", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606307, "gas_refund_counter": 0, @@ -5825,21 +3723,9 @@ { "pc": 1664, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606304, "gas_refund_counter": 0, @@ -5853,20 +3739,9 @@ { "pc": 1665, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606302, "gas_refund_counter": 0, @@ -5880,21 +3755,9 @@ { "pc": 1668, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606299, "gas_refund_counter": 0, @@ -5908,22 +3771,9 @@ { "pc": 1669, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606297, "gas_refund_counter": 0, @@ -5937,23 +3787,9 @@ { "pc": 1670, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0x0", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606294, "gas_refund_counter": 0, @@ -5967,22 +3803,9 @@ { "pc": 1671, "op": 132, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606291, "gas_refund_counter": 0, @@ -5996,23 +3819,9 @@ { "pc": 1672, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606288, "gas_refund_counter": 0, @@ -6026,24 +3835,9 @@ { "pc": 1675, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x66a" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606285, "gas_refund_counter": 0, @@ -6057,23 +3851,9 @@ { "pc": 1642, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606277, "gas_refund_counter": 0, @@ -6087,23 +3867,9 @@ { "pc": 1643, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606276, "gas_refund_counter": 0, @@ -6117,24 +3883,9 @@ { "pc": 1646, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606273, "gas_refund_counter": 0, @@ -6148,25 +3899,9 @@ { "pc": 1647, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606270, "gas_refund_counter": 0, @@ -6180,26 +3915,9 @@ { "pc": 1650, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64", - "0x60f" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606267, "gas_refund_counter": 0, @@ -6213,25 +3931,9 @@ { "pc": 1551, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606259, "gas_refund_counter": 0, @@ -6245,25 +3947,9 @@ { "pc": 1552, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606258, "gas_refund_counter": 0, @@ -6277,26 +3963,9 @@ { "pc": 1553, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606256, "gas_refund_counter": 0, @@ -6310,27 +3979,9 @@ { "pc": 1554, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606253, "gas_refund_counter": 0, @@ -6344,27 +3995,9 @@ { "pc": 1555, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606250, "gas_refund_counter": 0, @@ -6378,26 +4011,9 @@ { "pc": 1556, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x673", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606248, "gas_refund_counter": 0, @@ -6411,26 +4027,9 @@ { "pc": 1557, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x64", - "0x64", - "0x673" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606245, "gas_refund_counter": 0, @@ -6444,26 +4043,9 @@ { "pc": 1558, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x64", - "0x673", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606242, "gas_refund_counter": 0, @@ -6477,25 +4059,9 @@ { "pc": 1559, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x64", - "0x673" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606240, "gas_refund_counter": 0, @@ -6509,24 +4075,9 @@ { "pc": 1651, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606232, "gas_refund_counter": 0, @@ -6540,24 +4091,9 @@ { "pc": 1652, "op": 130, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606231, "gas_refund_counter": 0, @@ -6571,25 +4107,9 @@ { "pc": 1653, "op": 82, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64", - "0x64", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d14740000000000000000000000000000000000000000000000000000000000000064600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606228, "gas_refund_counter": 0, @@ -6603,23 +4123,9 @@ { "pc": 1654, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606225, "gas_refund_counter": 0, @@ -6633,22 +4139,9 @@ { "pc": 1655, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606223, "gas_refund_counter": 0, @@ -6662,21 +4155,9 @@ { "pc": 1656, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4", - "0x68c" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606221, "gas_refund_counter": 0, @@ -6690,20 +4171,9 @@ { "pc": 1676, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606213, "gas_refund_counter": 0, @@ -6717,20 +4187,9 @@ { "pc": 1677, "op": 146, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x342", - "0x64", - "0xa4", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606212, "gas_refund_counter": 0, @@ -6744,20 +4203,9 @@ { "pc": 1678, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x64", - "0xa4", - "0x342" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606209, "gas_refund_counter": 0, @@ -6771,20 +4219,9 @@ { "pc": 1679, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x342", - "0xa4", - "0x64" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606206, "gas_refund_counter": 0, @@ -6798,19 +4235,9 @@ { "pc": 1680, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x342", - "0xa4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606204, "gas_refund_counter": 0, @@ -6824,18 +4251,9 @@ { "pc": 1681, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x342" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606202, "gas_refund_counter": 0, @@ -6849,17 +4267,9 @@ { "pc": 834, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606194, "gas_refund_counter": 0, @@ -6873,17 +4283,9 @@ { "pc": 835, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606193, "gas_refund_counter": 0, @@ -6897,18 +4299,9 @@ { "pc": 837, "op": 81, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x40" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606190, "gas_refund_counter": 0, @@ -6922,18 +4315,9 @@ { "pc": 838, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606187, "gas_refund_counter": 0, @@ -6947,19 +4331,9 @@ { "pc": 840, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80", - "0x20" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606184, "gas_refund_counter": 0, @@ -6973,20 +4347,9 @@ { "pc": 841, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80", - "0x20", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606181, "gas_refund_counter": 0, @@ -7000,21 +4363,9 @@ { "pc": 842, "op": 3, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80", - "0x20", - "0x80", - "0xc4" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606178, "gas_refund_counter": 0, @@ -7028,20 +4379,9 @@ { "pc": 843, "op": 3, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80", - "0x20", - "0x44" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606175, "gas_refund_counter": 0, @@ -7055,19 +4395,9 @@ { "pc": 844, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80", - "0x24" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606172, "gas_refund_counter": 0, @@ -7081,20 +4411,9 @@ { "pc": 845, "op": 82, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80", - "0x24", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000e26d147400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606169, "gas_refund_counter": 0, @@ -7108,18 +4427,9 @@ { "pc": 846, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0xc4", - "0x80" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606166, "gas_refund_counter": 0, @@ -7133,18 +4443,9 @@ { "pc": 847, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xc4" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606163, "gas_refund_counter": 0, @@ -7158,19 +4459,9 @@ { "pc": 849, "op": 82, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xc4", - "0x40" - ], - "push_stack": null, - "memory": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606160, "gas_refund_counter": 0, @@ -7184,17 +4475,9 @@ { "pc": 850, "op": 127, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606157, "gas_refund_counter": 0, @@ -7208,18 +4491,9 @@ { "pc": 883, "op": 123, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606154, "gas_refund_counter": 0, @@ -7233,19 +4507,9 @@ { "pc": 912, "op": 25, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606151, "gas_refund_counter": 0, @@ -7259,19 +4523,9 @@ { "pc": 913, "op": 22, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xffffffff00000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606148, "gas_refund_counter": 0, @@ -7285,18 +4539,9 @@ { "pc": 914, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606145, "gas_refund_counter": 0, @@ -7310,19 +4555,9 @@ { "pc": 916, "op": 130, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0x20" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606142, "gas_refund_counter": 0, @@ -7336,20 +4571,9 @@ { "pc": 917, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0x20", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606139, "gas_refund_counter": 0, @@ -7363,19 +4587,9 @@ { "pc": 918, "op": 128, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606136, "gas_refund_counter": 0, @@ -7389,20 +4603,9 @@ { "pc": 919, "op": 81, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0xa0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606133, "gas_refund_counter": 0, @@ -7416,20 +4619,9 @@ { "pc": 920, "op": 123, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606130, "gas_refund_counter": 0, @@ -7443,21 +4635,9 @@ { "pc": 949, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606127, "gas_refund_counter": 0, @@ -7471,22 +4651,9 @@ { "pc": 950, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0xf82c50f100000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606124, "gas_refund_counter": 0, @@ -7500,23 +4667,9 @@ { "pc": 951, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606121, "gas_refund_counter": 0, @@ -7530,24 +4683,9 @@ { "pc": 952, "op": 22, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0x6400000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606118, "gas_refund_counter": 0, @@ -7561,23 +4699,9 @@ { "pc": 953, "op": 23, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606115, "gas_refund_counter": 0, @@ -7591,22 +4715,9 @@ { "pc": 954, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0xf82c50f100000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606112, "gas_refund_counter": 0, @@ -7620,23 +4731,9 @@ { "pc": 955, "op": 82, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000640000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606109, "gas_refund_counter": 0, @@ -7650,21 +4747,9 @@ { "pc": 956, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606106, "gas_refund_counter": 0, @@ -7678,20 +4763,9 @@ { "pc": 957, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0", - "0x6400000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606104, "gas_refund_counter": 0, @@ -7705,19 +4779,9 @@ { "pc": 958, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000", - "0xa0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606102, "gas_refund_counter": 0, @@ -7731,18 +4795,9 @@ { "pc": 959, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0xf82c50f100000000000000000000000000000000000000000000000000000000" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606100, "gas_refund_counter": 0, @@ -7756,17 +4811,9 @@ { "pc": 960, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606098, "gas_refund_counter": 0, @@ -7780,18 +4827,9 @@ { "pc": 963, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3c7" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606095, "gas_refund_counter": 0, @@ -7805,17 +4843,9 @@ { "pc": 967, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606087, "gas_refund_counter": 0, @@ -7829,17 +4859,9 @@ { "pc": 968, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606086, "gas_refund_counter": 0, @@ -7853,18 +4875,9 @@ { "pc": 971, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606083, "gas_refund_counter": 0, @@ -7878,19 +4891,9 @@ { "pc": 972, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606080, "gas_refund_counter": 0, @@ -7904,20 +4907,9 @@ { "pc": 975, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606077, "gas_refund_counter": 0, @@ -7931,21 +4923,9 @@ { "pc": 978, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606074, "gas_refund_counter": 0, @@ -7959,22 +4939,9 @@ { "pc": 981, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1", - "0x400" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606071, "gas_refund_counter": 0, @@ -7988,21 +4955,9 @@ { "pc": 1024, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606063, "gas_refund_counter": 0, @@ -8016,21 +4971,9 @@ { "pc": 1025, "op": 97, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606062, "gas_refund_counter": 0, @@ -8044,22 +4987,9 @@ { "pc": 1028, "op": 129, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1", - "0x418" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606059, "gas_refund_counter": 0, @@ -8073,23 +5003,9 @@ { "pc": 1029, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1", - "0x418", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606056, "gas_refund_counter": 0, @@ -8103,23 +5019,9 @@ { "pc": 1030, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1", - "0x3e1", - "0x418" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606053, "gas_refund_counter": 0, @@ -8133,22 +5035,9 @@ { "pc": 1031, "op": 145, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3d6", - "0x3e1", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606051, "gas_refund_counter": 0, @@ -8162,22 +5051,9 @@ { "pc": 1032, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1", - "0x3e1", - "0x3d6" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606048, "gas_refund_counter": 0, @@ -8191,22 +5067,9 @@ { "pc": 1033, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1", - "0x3d6", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606045, "gas_refund_counter": 0, @@ -8220,21 +5083,9 @@ { "pc": 1034, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1", - "0x3d6" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606043, "gas_refund_counter": 0, @@ -8248,20 +5099,9 @@ { "pc": 982, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606035, "gas_refund_counter": 0, @@ -8275,20 +5115,9 @@ { "pc": 983, "op": 99, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606034, "gas_refund_counter": 0, @@ -8302,21 +5131,9 @@ { "pc": 988, "op": 22, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1", - "0xffffffff" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606031, "gas_refund_counter": 0, @@ -8330,20 +5147,9 @@ { "pc": 989, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x3e1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606028, "gas_refund_counter": 0, @@ -8357,19 +5163,9 @@ { "pc": 993, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606020, "gas_refund_counter": 0, @@ -8383,19 +5179,9 @@ { "pc": 994, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606019, "gas_refund_counter": 0, @@ -8409,20 +5195,9 @@ { "pc": 995, "op": 106, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606017, "gas_refund_counter": 0, @@ -8436,21 +5211,9 @@ { "pc": 1007, "op": 144, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x0", - "0x636f6e736f6c652e6c6f67" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606014, "gas_refund_counter": 0, @@ -8464,21 +5227,9 @@ { "pc": 1008, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606011, "gas_refund_counter": 0, @@ -8492,20 +5243,9 @@ { "pc": 1009, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606009, "gas_refund_counter": 0, @@ -8519,21 +5259,9 @@ { "pc": 1010, "op": 95, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606007, "gas_refund_counter": 0, @@ -8547,22 +5275,9 @@ { "pc": 1011, "op": 131, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606005, "gas_refund_counter": 0, @@ -8576,23 +5291,9 @@ { "pc": 1012, "op": 81, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073606002, "gas_refund_counter": 0, @@ -8606,23 +5307,9 @@ { "pc": 1013, "op": 96, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x24" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073605999, "gas_refund_counter": 0, @@ -8636,24 +5323,9 @@ { "pc": 1015, "op": 133, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x24", - "0x20" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073605996, "gas_refund_counter": 0, @@ -8667,25 +5339,9 @@ { "pc": 1016, "op": 1, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x24", - "0x20", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073605993, "gas_refund_counter": 0, @@ -8699,24 +5355,9 @@ { "pc": 1017, "op": 132, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x24", - "0xa0" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073605990, "gas_refund_counter": 0, @@ -8730,25 +5371,9 @@ { "pc": 1018, "op": 90, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x24", - "0xa0", - "0x636f6e736f6c652e6c6f67" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073605987, "gas_refund_counter": 0, @@ -8762,26 +5387,9 @@ { "pc": 1019, "op": 250, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x0", - "0x0", - "0x24", - "0xa0", - "0x636f6e736f6c652e6c6f67", - "0x3ffded61" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073605985, "gas_refund_counter": 0, @@ -8795,21 +5403,9 @@ { "pc": 1020, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67", - "0x1" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073603385, "gas_refund_counter": 0, @@ -8823,20 +5419,9 @@ { "pc": 1021, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80", - "0x636f6e736f6c652e6c6f67" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073603383, "gas_refund_counter": 0, @@ -8850,19 +5435,9 @@ { "pc": 1022, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073603381, "gas_refund_counter": 0, @@ -8876,18 +5451,9 @@ { "pc": 1023, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80", - "0x3de" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073603379, "gas_refund_counter": 0, @@ -8901,17 +5467,9 @@ { "pc": 990, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073603371, "gas_refund_counter": 0, @@ -8925,17 +5483,9 @@ { "pc": 991, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4", - "0x80" - ], - "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1073603370, "gas_refund_counter": 0, @@ -8949,16 +5499,9 @@ { "pc": 992, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64", - "0x3c4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603368, "gas_refund_counter": 0, @@ -8972,15 +5515,9 @@ { "pc": 964, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603360, "gas_refund_counter": 0, @@ -8994,15 +5531,9 @@ { "pc": 965, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4", - "0x64" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603359, "gas_refund_counter": 0, @@ -9016,14 +5547,9 @@ { "pc": 966, "op": 86, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "0x2f4" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603357, "gas_refund_counter": 0, @@ -9037,13 +5563,9 @@ { "pc": 756, "op": 91, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603349, "gas_refund_counter": 0, @@ -9057,13 +5579,9 @@ { "pc": 757, "op": 80, - "stack": [ - "0xf8a8fd6d", - "0x92", - "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f" - ], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603348, "gas_refund_counter": 0, @@ -9077,9 +5595,9 @@ { "pc": 758, "op": 86, - "stack": ["0xf8a8fd6d", "0x92"], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603346, "gas_refund_counter": 0, @@ -9093,9 +5611,9 @@ { "pc": 146, "op": 91, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603338, "gas_refund_counter": 0, @@ -9109,9 +5627,9 @@ { "pc": 147, "op": 0, - "stack": ["0xf8a8fd6d"], + "stack": null, "push_stack": null, - "memory": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f82c50f10000000000000000000000000000000000000000000000000000000000000064e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c63430008210033000000000000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1073603337, "gas_refund_counter": 0, @@ -10204,10 +6722,9 @@ { "pc": 0, "op": 96, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10221,10 +6738,9 @@ { "pc": 2, "op": 96, - - "stack": ["0x80"], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": 1056912053, "gas_refund_counter": 0, @@ -10238,10 +6754,9 @@ { "pc": 4, "op": 82, - - "stack": ["0x80", "0x40"], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": 1056912050, "gas_refund_counter": 0, @@ -10255,10 +6770,9 @@ { "pc": 5, "op": 52, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912038, "gas_refund_counter": 0, @@ -10272,10 +6786,9 @@ { "pc": 6, "op": 128, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912036, "gas_refund_counter": 0, @@ -10289,10 +6802,9 @@ { "pc": 7, "op": 21, - - "stack": ["0x0", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912033, "gas_refund_counter": 0, @@ -10306,10 +6818,9 @@ { "pc": 8, "op": 96, - - "stack": ["0x0", "0x1"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912030, "gas_refund_counter": 0, @@ -10323,10 +6834,9 @@ { "pc": 10, "op": 87, - - "stack": ["0x0", "0x1", "0xe"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912027, "gas_refund_counter": 0, @@ -10340,10 +6850,9 @@ { "pc": 14, "op": 91, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912017, "gas_refund_counter": 0, @@ -10357,10 +6866,9 @@ { "pc": 15, "op": 80, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912016, "gas_refund_counter": 0, @@ -10374,10 +6882,9 @@ { "pc": 16, "op": 97, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912014, "gas_refund_counter": 0, @@ -10391,10 +6898,9 @@ { "pc": 19, "op": 128, - - "stack": ["0x126"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912011, "gas_refund_counter": 0, @@ -10408,10 +6914,9 @@ { "pc": 20, "op": 97, - - "stack": ["0x126", "0x126"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912008, "gas_refund_counter": 0, @@ -10425,10 +6930,9 @@ { "pc": 23, "op": 95, - - "stack": ["0x126", "0x126", "0x1c"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912005, "gas_refund_counter": 0, @@ -10442,10 +6946,9 @@ { "pc": 24, "op": 57, - - "stack": ["0x126", "0x126", "0x1c", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056912003, "gas_refund_counter": 0, @@ -10459,10 +6962,9 @@ { "pc": 25, "op": 95, - - "stack": ["0x126"], + "stack": null, "push_stack": null, - "memory": "0x6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c634300082100330000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1056911949, "gas_refund_counter": 0, @@ -10476,10 +6978,9 @@ { "pc": 26, "op": 243, - - "stack": ["0x126", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x6080604052348015600e575f5ffd5b50600436106030575f3560e01c80634e70b1dc146034578063e26d147414604e575b5f5ffd5b603a6066565b60405160459190608a565b60405180910390f35b606460048036038101906060919060ca565b606b565b005b5f5481565b805f8190555050565b5f819050919050565b6084816074565b82525050565b5f602082019050609b5f830184607d565b92915050565b5f5ffd5b60ac816074565b811460b5575f5ffd5b50565b5f8135905060c48160a5565b92915050565b5f6020828403121560dc5760db60a1565b5b5f60e78482850160b8565b9150509291505056fea2646970667358221220ea1195a62b411681af2ab1c55fe6e4f679055b53f56d5984b82acb0a50450db664736f6c634300082100330000000000000000000000000000000000000000000000000000", + "memory": null, "returndata": "0x", "gas_remaining": 1056911947, "gas_refund_counter": 0, @@ -10573,10 +7074,9 @@ { "pc": 0, "op": 96, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10590,10 +7090,9 @@ { "pc": 2, "op": 96, - - "stack": ["0x80"], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10607,10 +7106,9 @@ { "pc": 4, "op": 82, - - "stack": ["0x80", "0x40"], + "stack": null, "push_stack": null, - "memory": "0x", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10624,10 +7122,9 @@ { "pc": 5, "op": 52, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10641,10 +7138,9 @@ { "pc": 6, "op": 128, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10658,10 +7154,9 @@ { "pc": 7, "op": 21, - - "stack": ["0x0", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10675,10 +7170,9 @@ { "pc": 8, "op": 96, - - "stack": ["0x0", "0x1"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10692,10 +7186,9 @@ { "pc": 10, "op": 87, - - "stack": ["0x0", "0x1", "0xe"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10709,10 +7202,9 @@ { "pc": 14, "op": 91, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10726,10 +7218,9 @@ { "pc": 15, "op": 80, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10743,10 +7234,9 @@ { "pc": 16, "op": 96, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10760,10 +7250,9 @@ { "pc": 18, "op": 54, - - "stack": ["0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10777,10 +7266,9 @@ { "pc": 19, "op": 16, - - "stack": ["0x4", "0x24"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10794,10 +7282,9 @@ { "pc": 20, "op": 96, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10811,10 +7298,9 @@ { "pc": 22, "op": 87, - - "stack": ["0x0", "0x30"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10828,10 +7314,9 @@ { "pc": 23, "op": 95, - - "stack": [], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10845,10 +7330,9 @@ { "pc": 24, "op": 53, - - "stack": ["0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10862,12 +7346,9 @@ { "pc": 25, "op": 96, - - "stack": [ - "0xe26d147400000000000000000000000000000000000000000000000000000000" - ], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10881,13 +7362,9 @@ { "pc": 27, "op": 28, - - "stack": [ - "0xe26d147400000000000000000000000000000000000000000000000000000000", - "0xe0" - ], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10901,10 +7378,9 @@ { "pc": 28, "op": 128, - - "stack": ["0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10918,10 +7394,9 @@ { "pc": 29, "op": 99, - - "stack": ["0xe26d1474", "0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10935,10 +7410,9 @@ { "pc": 34, "op": 20, - - "stack": ["0xe26d1474", "0xe26d1474", "0x4e70b1dc"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10952,10 +7426,9 @@ { "pc": 35, "op": 96, - - "stack": ["0xe26d1474", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10969,10 +7442,9 @@ { "pc": 37, "op": 87, - - "stack": ["0xe26d1474", "0x0", "0x34"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": "{...}", "gas_refund_counter": 0, @@ -10986,10 +7458,9 @@ { "pc": 38, "op": 128, - - "stack": ["0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853373, "gas_refund_counter": 0, @@ -11003,10 +7474,9 @@ { "pc": 39, "op": 99, - - "stack": ["0xe26d1474", "0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853370, "gas_refund_counter": 0, @@ -11020,10 +7490,9 @@ { "pc": 44, "op": 20, - - "stack": ["0xe26d1474", "0xe26d1474", "0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853367, "gas_refund_counter": 0, @@ -11037,10 +7506,9 @@ { "pc": 45, "op": 96, - - "stack": ["0xe26d1474", "0x1"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853364, "gas_refund_counter": 0, @@ -11054,10 +7522,9 @@ { "pc": 47, "op": 87, - - "stack": ["0xe26d1474", "0x1", "0x4e"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853361, "gas_refund_counter": 0, @@ -11071,10 +7538,9 @@ { "pc": 78, "op": 91, - - "stack": ["0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853351, "gas_refund_counter": 0, @@ -11088,10 +7554,9 @@ { "pc": 79, "op": 96, - - "stack": ["0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853350, "gas_refund_counter": 0, @@ -11105,10 +7570,9 @@ { "pc": 81, "op": 96, - - "stack": ["0xe26d1474", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853347, "gas_refund_counter": 0, @@ -11122,10 +7586,9 @@ { "pc": 83, "op": 128, - - "stack": ["0xe26d1474", "0x64", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853344, "gas_refund_counter": 0, @@ -11139,10 +7602,9 @@ { "pc": 84, "op": 54, - - "stack": ["0xe26d1474", "0x64", "0x4", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853341, "gas_refund_counter": 0, @@ -11156,10 +7618,9 @@ { "pc": 85, "op": 3, - - "stack": ["0xe26d1474", "0x64", "0x4", "0x4", "0x24"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853339, "gas_refund_counter": 0, @@ -11173,10 +7634,9 @@ { "pc": 86, "op": 129, - - "stack": ["0xe26d1474", "0x64", "0x4", "0x20"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853336, "gas_refund_counter": 0, @@ -11190,10 +7650,9 @@ { "pc": 87, "op": 1, - - "stack": ["0xe26d1474", "0x64", "0x4", "0x20", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853333, "gas_refund_counter": 0, @@ -11207,10 +7666,9 @@ { "pc": 88, "op": 144, - - "stack": ["0xe26d1474", "0x64", "0x4", "0x24"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853330, "gas_refund_counter": 0, @@ -11224,10 +7682,9 @@ { "pc": 89, "op": 96, - - "stack": ["0xe26d1474", "0x64", "0x24", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853327, "gas_refund_counter": 0, @@ -11241,10 +7698,9 @@ { "pc": 91, "op": 145, - - "stack": ["0xe26d1474", "0x64", "0x24", "0x4", "0x60"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853324, "gas_refund_counter": 0, @@ -11258,10 +7714,9 @@ { "pc": 92, "op": 144, - - "stack": ["0xe26d1474", "0x64", "0x60", "0x4", "0x24"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853321, "gas_refund_counter": 0, @@ -11275,10 +7730,9 @@ { "pc": 93, "op": 96, - - "stack": ["0xe26d1474", "0x64", "0x60", "0x24", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853318, "gas_refund_counter": 0, @@ -11292,17 +7746,9 @@ { "pc": 95, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0xca" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853315, "gas_refund_counter": 0, @@ -11316,10 +7762,9 @@ { "pc": 202, "op": 91, - - "stack": ["0xe26d1474", "0x64", "0x60", "0x24", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853307, "gas_refund_counter": 0, @@ -11333,10 +7778,9 @@ { "pc": 203, "op": 95, - - "stack": ["0xe26d1474", "0x64", "0x60", "0x24", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853306, "gas_refund_counter": 0, @@ -11350,17 +7794,9 @@ { "pc": 204, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853304, "gas_refund_counter": 0, @@ -11374,18 +7810,9 @@ { "pc": 206, "op": 130, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x20" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853301, "gas_refund_counter": 0, @@ -11399,19 +7826,9 @@ { "pc": 207, "op": 132, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x20", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853298, "gas_refund_counter": 0, @@ -11425,20 +7842,9 @@ { "pc": 208, "op": 3, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x20", - "0x4", - "0x24" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853295, "gas_refund_counter": 0, @@ -11452,19 +7858,9 @@ { "pc": 209, "op": 18, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x20", - "0x20" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853292, "gas_refund_counter": 0, @@ -11478,18 +7874,9 @@ { "pc": 210, "op": 21, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853289, "gas_refund_counter": 0, @@ -11503,18 +7890,9 @@ { "pc": 211, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x1" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853286, "gas_refund_counter": 0, @@ -11528,19 +7906,9 @@ { "pc": 213, "op": 87, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x1", - "0xdc" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853283, "gas_refund_counter": 0, @@ -11554,17 +7922,9 @@ { "pc": 220, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853273, "gas_refund_counter": 0, @@ -11578,17 +7938,9 @@ { "pc": 221, "op": 95, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853272, "gas_refund_counter": 0, @@ -11602,18 +7954,9 @@ { "pc": 222, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853270, "gas_refund_counter": 0, @@ -11627,19 +7970,9 @@ { "pc": 224, "op": 132, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853267, "gas_refund_counter": 0, @@ -11653,20 +7986,9 @@ { "pc": 225, "op": 130, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853264, "gas_refund_counter": 0, @@ -11680,21 +8002,9 @@ { "pc": 226, "op": 133, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853261, "gas_refund_counter": 0, @@ -11708,22 +8018,9 @@ { "pc": 227, "op": 1, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x0", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853258, "gas_refund_counter": 0, @@ -11737,21 +8034,9 @@ { "pc": 228, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853255, "gas_refund_counter": 0, @@ -11765,22 +8050,9 @@ { "pc": 230, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0xb8" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853252, "gas_refund_counter": 0, @@ -11794,21 +8066,9 @@ { "pc": 184, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853244, "gas_refund_counter": 0, @@ -11822,21 +8082,9 @@ { "pc": 185, "op": 95, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853243, "gas_refund_counter": 0, @@ -11850,22 +8098,9 @@ { "pc": 186, "op": 129, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853241, "gas_refund_counter": 0, @@ -11879,23 +8114,9 @@ { "pc": 187, "op": 53, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x0", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853238, "gas_refund_counter": 0, @@ -11909,23 +8130,9 @@ { "pc": 188, "op": 144, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853235, "gas_refund_counter": 0, @@ -11939,23 +8146,9 @@ { "pc": 189, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853232, "gas_refund_counter": 0, @@ -11969,22 +8162,9 @@ { "pc": 190, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853230, "gas_refund_counter": 0, @@ -11998,23 +8178,9 @@ { "pc": 192, "op": 129, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853227, "gas_refund_counter": 0, @@ -12028,24 +8194,9 @@ { "pc": 193, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853224, "gas_refund_counter": 0, @@ -12059,25 +8210,9 @@ { "pc": 195, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xa5" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853221, "gas_refund_counter": 0, @@ -12091,24 +8226,9 @@ { "pc": 165, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853213, "gas_refund_counter": 0, @@ -12122,24 +8242,9 @@ { "pc": 166, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853212, "gas_refund_counter": 0, @@ -12153,25 +8258,9 @@ { "pc": 168, "op": 129, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853209, "gas_refund_counter": 0, @@ -12185,26 +8274,9 @@ { "pc": 169, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853206, "gas_refund_counter": 0, @@ -12218,27 +8290,9 @@ { "pc": 171, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64", - "0x74" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853203, "gas_refund_counter": 0, @@ -12252,26 +8306,9 @@ { "pc": 116, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853195, "gas_refund_counter": 0, @@ -12285,26 +8322,9 @@ { "pc": 117, "op": 95, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853194, "gas_refund_counter": 0, @@ -12318,27 +8338,9 @@ { "pc": 118, "op": 129, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853192, "gas_refund_counter": 0, @@ -12352,28 +8354,9 @@ { "pc": 119, "op": 144, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853189, "gas_refund_counter": 0, @@ -12387,28 +8370,9 @@ { "pc": 120, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853186, "gas_refund_counter": 0, @@ -12422,27 +8386,9 @@ { "pc": 121, "op": 145, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0xac", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853184, "gas_refund_counter": 0, @@ -12456,27 +8402,9 @@ { "pc": 122, "op": 144, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x64", - "0x64", - "0xac" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853181, "gas_refund_counter": 0, @@ -12490,27 +8418,9 @@ { "pc": 123, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x64", - "0xac", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853178, "gas_refund_counter": 0, @@ -12524,26 +8434,9 @@ { "pc": 124, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x64", - "0xac" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853176, "gas_refund_counter": 0, @@ -12557,25 +8450,9 @@ { "pc": 172, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853168, "gas_refund_counter": 0, @@ -12589,25 +8466,9 @@ { "pc": 173, "op": 129, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853167, "gas_refund_counter": 0, @@ -12621,26 +8482,9 @@ { "pc": 174, "op": 20, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x64", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853164, "gas_refund_counter": 0, @@ -12654,25 +8498,9 @@ { "pc": 175, "op": 96, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x1" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853161, "gas_refund_counter": 0, @@ -12686,26 +8514,9 @@ { "pc": 177, "op": 87, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64", - "0x1", - "0xb5" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853158, "gas_refund_counter": 0, @@ -12719,24 +8530,9 @@ { "pc": 181, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853148, "gas_refund_counter": 0, @@ -12750,24 +8546,9 @@ { "pc": 182, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853147, "gas_refund_counter": 0, @@ -12781,23 +8562,9 @@ { "pc": 183, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64", - "0xc4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853145, "gas_refund_counter": 0, @@ -12811,22 +8578,9 @@ { "pc": 196, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853137, "gas_refund_counter": 0, @@ -12840,22 +8594,9 @@ { "pc": 197, "op": 146, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0xe7", - "0x24", - "0x4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853136, "gas_refund_counter": 0, @@ -12869,22 +8610,9 @@ { "pc": 198, "op": 145, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0x64", - "0x24", - "0x4", - "0xe7" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853133, "gas_refund_counter": 0, @@ -12898,22 +8626,9 @@ { "pc": 199, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0x64", - "0xe7", - "0x4", - "0x24" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853130, "gas_refund_counter": 0, @@ -12927,21 +8642,9 @@ { "pc": 200, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0x64", - "0xe7", - "0x4" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853128, "gas_refund_counter": 0, @@ -12955,20 +8658,9 @@ { "pc": 201, "op": 86, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0x64", - "0xe7" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853126, "gas_refund_counter": 0, @@ -12982,19 +8674,9 @@ { "pc": 231, "op": 91, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853118, "gas_refund_counter": 0, @@ -13008,19 +8690,9 @@ { "pc": 232, "op": 145, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x0", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853117, "gas_refund_counter": 0, @@ -13034,19 +8706,9 @@ { "pc": 233, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x64", - "0x0", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853114, "gas_refund_counter": 0, @@ -13060,18 +8722,9 @@ { "pc": 234, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853112, "gas_refund_counter": 0, @@ -13085,17 +8738,9 @@ { "pc": 235, "op": 146, - - "stack": [ - "0xe26d1474", - "0x64", - "0x60", - "0x24", - "0x4", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853110, "gas_refund_counter": 0, @@ -13109,17 +8754,9 @@ { "pc": 236, "op": 145, - - "stack": [ - "0xe26d1474", - "0x64", - "0x64", - "0x24", - "0x4", - "0x60" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853107, "gas_refund_counter": 0, @@ -13133,17 +8770,9 @@ { "pc": 237, "op": 80, - - "stack": [ - "0xe26d1474", - "0x64", - "0x64", - "0x60", - "0x4", - "0x24" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853104, "gas_refund_counter": 0, @@ -13157,10 +8786,9 @@ { "pc": 238, "op": 80, - - "stack": ["0xe26d1474", "0x64", "0x64", "0x60", "0x4"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853102, "gas_refund_counter": 0, @@ -13174,10 +8802,9 @@ { "pc": 239, "op": 86, - - "stack": ["0xe26d1474", "0x64", "0x64", "0x60"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853100, "gas_refund_counter": 0, @@ -13191,10 +8818,9 @@ { "pc": 96, "op": 91, - - "stack": ["0xe26d1474", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853092, "gas_refund_counter": 0, @@ -13208,10 +8834,9 @@ { "pc": 97, "op": 96, - - "stack": ["0xe26d1474", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853091, "gas_refund_counter": 0, @@ -13225,10 +8850,9 @@ { "pc": 99, "op": 86, - - "stack": ["0xe26d1474", "0x64", "0x64", "0x6b"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853088, "gas_refund_counter": 0, @@ -13242,10 +8866,9 @@ { "pc": 107, "op": 91, - - "stack": ["0xe26d1474", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853080, "gas_refund_counter": 0, @@ -13259,10 +8882,9 @@ { "pc": 108, "op": 128, - - "stack": ["0xe26d1474", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853079, "gas_refund_counter": 0, @@ -13276,10 +8898,9 @@ { "pc": 109, "op": 95, - - "stack": ["0xe26d1474", "0x64", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853076, "gas_refund_counter": 0, @@ -13293,10 +8914,9 @@ { "pc": 110, "op": 129, - - "stack": ["0xe26d1474", "0x64", "0x64", "0x64", "0x0"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056853074, "gas_refund_counter": 0, @@ -13310,17 +8930,9 @@ { "pc": 111, "op": 144, - - "stack": [ - "0xe26d1474", - "0x64", - "0x64", - "0x64", - "0x0", - "0x64" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853071, "gas_refund_counter": 0, @@ -13334,17 +8946,9 @@ { "pc": 112, "op": 85, - - "stack": [ - "0xe26d1474", - "0x64", - "0x64", - "0x64", - "0x64", - "0x0" - ], - "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "stack": null, + "push_stack": null, + "memory": null, "returndata": "0x", "gas_remaining": 1056853068, "gas_refund_counter": 0, @@ -13363,10 +8967,9 @@ { "pc": 113, "op": 80, - - "stack": ["0xe26d1474", "0x64", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056830968, "gas_refund_counter": 0, @@ -13380,10 +8983,9 @@ { "pc": 114, "op": 80, - - "stack": ["0xe26d1474", "0x64", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056830966, "gas_refund_counter": 0, @@ -13397,10 +8999,9 @@ { "pc": 115, "op": 86, - - "stack": ["0xe26d1474", "0x64"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056830964, "gas_refund_counter": 0, @@ -13414,10 +9015,9 @@ { "pc": 100, "op": 91, - - "stack": ["0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056830956, "gas_refund_counter": 0, @@ -13431,10 +9031,9 @@ { "pc": 101, "op": 0, - - "stack": ["0xe26d1474"], + "stack": null, "push_stack": null, - "memory": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "memory": null, "returndata": "0x", "gas_remaining": 1056830955, "gas_refund_counter": 0, diff --git a/crates/macros/src/console_fmt.rs b/crates/macros/src/console_fmt.rs index 2372756c2efc7..e8a8e6319bd09 100644 --- a/crates/macros/src/console_fmt.rs +++ b/crates/macros/src/console_fmt.rs @@ -47,10 +47,7 @@ fn impl_struct(s: &DataStruct) -> Option { .into_iter() .map(|member| match member { Member::Named(ident) => quote!(&self.#ident), - // For Tuple structs generated by the sol!. - // These are generated only in case of a single unnamed field, hence it is safe to - // hardcode the index to `.0`. - Member::Unnamed(_) => quote!(&self.0), + Member::Unnamed(idx) => quote!(&self.#idx), }) .collect(); diff --git a/crates/primitives/src/network/transaction.rs b/crates/primitives/src/network/transaction.rs index 4b9c7517718fd..683352466f83e 100644 --- a/crates/primitives/src/network/transaction.rs +++ b/crates/primitives/src/network/transaction.rs @@ -5,6 +5,7 @@ use alloy_network::{AnyNetwork, Ethereum, Network, TransactionBuilder}; use alloy_primitives::{Address, B256, Signature, U256}; use alloy_rpc_types::SignedAuthorization; use tempo_alloy::TempoNetwork; +use tempo_primitives::transaction::SignedKeyAuthorization; /// Composite transaction builder trait for Foundry transactions. /// @@ -226,6 +227,12 @@ pub trait FoundryTransactionBuilder: TransactionBuilder { fn compute_sponsor_hash(&self, _from: Address) -> Option { None } + + /// Set the key authorization for a Tempo transaction. + /// + /// Embeds a [`SignedKeyAuthorization`] in the transaction body, provisioning the access key + /// on-chain as part of this transaction. + fn set_key_authorization(&mut self, _key_authorization: SignedKeyAuthorization) {} } impl FoundryTransactionBuilder for ::TransactionRequest { @@ -371,4 +378,8 @@ impl FoundryTransactionBuilder for ::Tran let tx = self.clone().build_aa().ok()?; Some(tx.fee_payer_signature_hash(from)) } + + fn set_key_authorization(&mut self, key_authorization: SignedKeyAuthorization) { + self.key_authorization = Some(key_authorization); + } } diff --git a/crates/primitives/src/transaction/envelope.rs b/crates/primitives/src/transaction/envelope.rs index f99865a8d4da5..240ad65ec3a31 100644 --- a/crates/primitives/src/transaction/envelope.rs +++ b/crates/primitives/src/transaction/envelope.rs @@ -7,12 +7,12 @@ use alloy_consensus::{ eip4844::{TxEip4844Variant, TxEip4844WithSidecar}, }, }; -use alloy_evm::FromRecoveredTx; +use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse}; -use alloy_primitives::{Address, B256, TxHash}; +use alloy_primitives::{Address, B256, Bytes, TxHash}; use alloy_rpc_types::ConversionError; use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait, TxDeposit}; -use op_revm::OpTransaction; +use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; use revm::context::TxEnv; use tempo_primitives::{AASigned, TempoTransaction}; @@ -210,7 +210,7 @@ impl FromRecoveredTx for TxEnv { FoundryTxEnvelope::Deposit(sealed_tx) => { Self::from_recovered_tx(sealed_tx.inner(), caller) } - FoundryTxEnvelope::Tempo(_) => panic!("unsupported tx type on ethereum"), + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Ethereum context"), } } } @@ -226,7 +226,51 @@ impl FromRecoveredTx for OpTransaction { FoundryTxEnvelope::Deposit(sealed_tx) => { Self::from_recovered_tx(sealed_tx.inner(), caller) } - FoundryTxEnvelope::Tempo(_) => panic!("unsupported tx type on optimism"), + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), + } + } +} + +impl FromTxWithEncoded for TxEnv { + fn from_encoded_tx(tx: &FoundryTxEnvelope, sender: Address, _encoded: Bytes) -> Self { + Self::from_recovered_tx(tx, sender) + } +} + +impl FromTxWithEncoded for OpTransaction { + fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { + match tx { + FoundryTxEnvelope::Legacy(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip2930(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip1559(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip4844(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip7702(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Deposit(sealed_tx) => { + let deposit_tx = sealed_tx.inner(); + let base = TxEnv::from_recovered_tx(deposit_tx, caller); + let deposit = DepositTransactionParts { + source_hash: deposit_tx.source_hash, + mint: Some(deposit_tx.mint), + is_system_transaction: deposit_tx.is_system_transaction, + }; + Self { base, enveloped_tx: Some(encoded), deposit } + } + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), } } } @@ -275,7 +319,7 @@ impl From for FoundryTypedTx { mod tests { use std::str::FromStr; - use alloy_primitives::{Bytes, TxKind, U256, b256, hex}; + use alloy_primitives::{TxKind, U256, b256, hex}; use alloy_rlp::Decodable; use alloy_signer::Signature; diff --git a/crates/primitives/src/transaction/request.rs b/crates/primitives/src/transaction/request.rs index b80fd10d8b096..c0451a6d377de 100644 --- a/crates/primitives/src/transaction/request.rs +++ b/crates/primitives/src/transaction/request.rs @@ -207,7 +207,7 @@ impl AsRef for FoundryTransactionRequest { match self { Self::Ethereum(tx) => tx, Self::Op(tx) => tx, - Self::Tempo(tx) => tx, + Self::Tempo(tx) => tx.as_ref(), } } } @@ -217,7 +217,7 @@ impl AsMut for FoundryTransactionRequest { match self { Self::Ethereum(tx) => tx, Self::Op(tx) => tx, - Self::Tempo(tx) => tx, + Self::Tempo(tx) => tx.as_mut(), } } } diff --git a/crates/script-sequence/Cargo.toml b/crates/script-sequence/Cargo.toml index d6cd35bec0836..7f112ce1bbda8 100644 --- a/crates/script-sequence/Cargo.toml +++ b/crates/script-sequence/Cargo.toml @@ -27,4 +27,3 @@ revm-inspectors.workspace = true alloy-network.workspace = true alloy-primitives.workspace = true -alloy-rpc-types-eth.workspace = true diff --git a/crates/script-sequence/src/reader.rs b/crates/script-sequence/src/reader.rs index 8350b842dbb66..839ab77664202 100644 --- a/crates/script-sequence/src/reader.rs +++ b/crates/script-sequence/src/reader.rs @@ -1,9 +1,9 @@ use crate::{ScriptSequence, TransactionWithMetadata}; -use alloy_network::ReceiptResponse; -use alloy_rpc_types_eth::TransactionReceipt; +use alloy_network::{Network, ReceiptResponse}; use eyre::{Result, bail}; use foundry_common::fs; use revm_inspectors::tracing::types::CallKind; +use serde::Deserialize; use std::path::{Component, Path, PathBuf}; /// This type reads broadcast files in the @@ -44,9 +44,9 @@ impl BroadcastReader { self } - fn matches_filters(&self, tx: &TransactionWithMetadata) -> bool { + fn matches_filters(&self, tx: &TransactionWithMetadata) -> bool { let name_filter = tx.contract_name.as_ref().is_some_and(|cn| *cn == self.contract_name); - let type_filter = self.tx_type.is_empty() || self.tx_type.contains(&tx.opcode); + let type_filter = self.tx_type.is_empty() || self.tx_type.contains(&tx.call_kind); name_filter && type_filter } @@ -56,7 +56,10 @@ impl BroadcastReader { /// /// project-root/broadcast/{script_name}.s.sol/{chain_id}/*.json /// project-root/broadcast/multi/{multichain_script_name}.s.sol-{timestamp}/deploy.json - pub fn read(&self) -> eyre::Result> { + pub fn read(&self) -> eyre::Result>> + where + N::TxEnvelope: for<'d> Deserialize<'d>, + { // 1. Recursively read all .json files in the broadcast directory let mut broadcasts = vec![]; for entry in walkdir::WalkDir::new(&self.broadcast_path).into_iter() { @@ -77,7 +80,8 @@ impl BroadcastReader { let multichain_deployments = broadcast .get("deployments") .and_then(|deployments| { - serde_json::from_value::>(deployments.clone()).ok() + serde_json::from_value::>>(deployments.clone()) + .ok() }) .unwrap_or_default(); @@ -85,7 +89,7 @@ impl BroadcastReader { continue; } - let broadcast = fs::read_json_file::(path)?; + let broadcast = fs::read_json_file::>(path)?; broadcasts.push(broadcast); } } @@ -98,7 +102,10 @@ impl BroadcastReader { /// Attempts read the latest broadcast file in the broadcast directory. /// /// This may be the `run-latest.json` file or the broadcast file with the latest timestamp. - pub fn read_latest(&self) -> eyre::Result { + pub fn read_latest(&self) -> eyre::Result> + where + N::TxEnvelope: for<'d> Deserialize<'d>, + { let broadcasts = self.read()?; // Find the broadcast with the latest timestamp @@ -111,7 +118,10 @@ impl BroadcastReader { } /// Applies the filters and sorts the broadcasts by descending timestamp. - pub fn filter_and_sort(&self, broadcasts: Vec) -> Vec { + pub fn filter_and_sort( + &self, + broadcasts: Vec>, + ) -> Vec> { // Apply the filters let mut seqs = broadcasts .into_iter() @@ -139,22 +149,22 @@ impl BroadcastReader { /// Transactions that don't have a corresponding receipt are ignored. /// /// Sorts the transactions by descending block number. - pub fn into_tx_receipts( + pub fn into_tx_receipts( &self, - broadcast: ScriptSequence, - ) -> Vec<(TransactionWithMetadata, TransactionReceipt)> { + broadcast: ScriptSequence, + ) -> Vec<(TransactionWithMetadata, N::ReceiptResponse)> { let ScriptSequence { transactions, receipts, .. } = broadcast; - let mut targets = Vec::new(); - for tx in transactions.into_iter().filter(|tx| self.matches_filters(tx)) { - let maybe_receipt = receipts - .iter() - .find(|receipt| tx.hash.is_some_and(|hash| hash == receipt.transaction_hash())); - - if let Some(receipt) = maybe_receipt { - targets.push((tx, receipt.clone())); - } - } + let mut targets: Vec<_> = transactions + .into_iter() + .filter(|tx| self.matches_filters(tx)) + .filter_map(|tx| { + let receipt = receipts + .iter() + .find(|r| tx.hash.is_some_and(|hash| hash == r.transaction_hash()))?; + Some((tx, receipt.clone())) + }) + .collect(); // Sort by descending block number targets.sort_by_key(|t| std::cmp::Reverse(t.1.block_number())); diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index 18805f49a2d20..85a7a955f2c5f 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -1,7 +1,6 @@ use crate::transaction::TransactionWithMetadata; -use alloy_network::ReceiptResponse; +use alloy_network::{Network, ReceiptResponse}; use alloy_primitives::{TxHash, hex, map::HashMap}; -use alloy_rpc_types_eth::TransactionReceipt; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{SELECTOR_LEN, TransactionMaybeSigned, fs, shell}; use foundry_compilers::ArtifactId; @@ -21,12 +20,28 @@ pub struct NestedValue { pub value: String, } +/// Sensitive values from the transactions in a script sequence +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveTransactionMetadata { + pub rpc: String, +} + +/// Sensitive info from the script sequence which is saved into the cache folder +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveScriptSequence { + pub transactions: VecDeque, +} + /// Helper that saves the transactions sequence and its state on which transactions have been /// broadcasted -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct ScriptSequence { - pub transactions: VecDeque, - pub receipts: Vec, +#[derive(Clone, Serialize, Deserialize)] +#[serde(bound( + serialize = "N::TransactionRequest: Serialize, N::TxEnvelope: Serialize", + deserialize = "N::TransactionRequest: for<'de2> Deserialize<'de2>, N::TxEnvelope: for<'de2> Deserialize<'de2>" +))] +pub struct ScriptSequence { + pub transactions: VecDeque>, + pub receipts: Vec, pub libraries: Vec, pub pending: Vec, #[serde(skip)] @@ -39,20 +54,24 @@ pub struct ScriptSequence { pub commit: Option, } -/// Sensitive values from the transactions in a script sequence -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveTransactionMetadata { - pub rpc: String, -} - -/// Sensitive info from the script sequence which is saved into the cache folder -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveScriptSequence { - pub transactions: VecDeque, +impl Default for ScriptSequence { + fn default() -> Self { + Self { + transactions: Default::default(), + receipts: Default::default(), + libraries: Default::default(), + pending: Default::default(), + paths: Default::default(), + returns: Default::default(), + timestamp: Default::default(), + chain: Default::default(), + commit: Default::default(), + } + } } -impl From<&ScriptSequence> for SensitiveScriptSequence { - fn from(sequence: &ScriptSequence) -> Self { +impl From<&ScriptSequence> for SensitiveScriptSequence { + fn from(sequence: &ScriptSequence) -> Self { Self { transactions: sequence .transactions @@ -63,7 +82,7 @@ impl From<&ScriptSequence> for SensitiveScriptSequence { } } -impl ScriptSequence { +impl ScriptSequence { /// Loads The sequence for the corresponding json file pub fn load( config: &Config, @@ -71,7 +90,10 @@ impl ScriptSequence { target: &ArtifactId, chain_id: u64, dry_run: bool, - ) -> Result { + ) -> Result + where + N::TxEnvelope: for<'d> Deserialize<'d>, + { let (path, sensitive_path) = Self::get_paths(config, sig, target, chain_id, dry_run)?; let mut script_sequence: Self = fs::read_json_file(&path) @@ -92,7 +114,10 @@ impl ScriptSequence { /// Saves the transactions as file if it's a standalone deployment. /// `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<()> { + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> + where + N::TxEnvelope: Serialize, + { self.sort_receipts(); if self.transactions.is_empty() { @@ -141,7 +166,7 @@ impl ScriptSequence { Ok(()) } - pub fn add_receipt(&mut self, receipt: TransactionReceipt) { + pub fn add_receipt(&mut self, receipt: N::ReceiptResponse) { self.receipts.push(receipt); } @@ -204,7 +229,7 @@ impl ScriptSequence { } /// Returns the list of the transactions without the metadata. - pub fn transactions(&self) -> impl Iterator { + pub fn transactions(&self) -> impl Iterator> { self.transactions.iter().map(|tx| tx.tx()) } diff --git a/crates/script-sequence/src/transaction.rs b/crates/script-sequence/src/transaction.rs index cf3c92d7eb5e2..3e4b46d3fa327 100644 --- a/crates/script-sequence/src/transaction.rs +++ b/crates/script-sequence/src/transaction.rs @@ -1,3 +1,4 @@ +use alloy_network::Network; use alloy_primitives::{Address, B256, Bytes}; use foundry_common::TransactionMaybeSigned; use revm_inspectors::tracing::types::CallKind; @@ -7,18 +8,24 @@ use serde::{Deserialize, Serialize}; #[serde(rename_all = "camelCase")] pub struct AdditionalContract { #[serde(rename = "transactionType")] - pub opcode: CallKind, + pub call_kind: CallKind, pub contract_name: Option, pub address: Address, pub init_code: Bytes, } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionWithMetadata { +#[serde( + rename_all = "camelCase", + bound( + serialize = "N::TransactionRequest: Serialize, N::TxEnvelope: Serialize", + deserialize = "N::TransactionRequest: for<'de2> Deserialize<'de2>, N::TxEnvelope: for<'de2> Deserialize<'de2>" + ) +)] +pub struct TransactionWithMetadata { pub hash: Option, #[serde(rename = "transactionType")] - pub opcode: CallKind, + pub call_kind: CallKind, #[serde(default = "default_string")] pub contract_name: Option, #[serde(default = "default_address")] @@ -29,7 +36,7 @@ pub struct TransactionWithMetadata { pub arguments: Option>, #[serde(skip)] pub rpc: String, - pub transaction: TransactionMaybeSigned, + pub transaction: TransactionMaybeSigned, #[serde(default)] pub additional_contracts: Vec, #[serde(default)] @@ -48,12 +55,12 @@ fn default_vec_of_strings() -> Option> { Some(vec![]) } -impl TransactionWithMetadata { - pub fn from_tx_request(transaction: TransactionMaybeSigned) -> Self { +impl TransactionWithMetadata { + pub fn from_tx_request(transaction: TransactionMaybeSigned) -> Self { Self { transaction, hash: Default::default(), - opcode: Default::default(), + call_kind: Default::default(), contract_name: Default::default(), contract_address: Default::default(), function: Default::default(), @@ -64,15 +71,15 @@ impl TransactionWithMetadata { } } - pub fn tx(&self) -> &TransactionMaybeSigned { + pub fn tx(&self) -> &TransactionMaybeSigned { &self.transaction } - pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { + pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { &mut self.transaction } pub fn is_create2(&self) -> bool { - self.opcode == CallKind::Create2 + self.call_kind == CallKind::Create2 } } diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 90cfc39e6ed7b..c445045623e9d 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -55,6 +55,7 @@ alloy-primitives.workspace = true alloy-eips.workspace = true alloy-consensus.workspace = true thiserror.workspace = true +tempo-alloy.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 265e89adbcc48..05b3033212f62 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,16 +1,16 @@ use std::{cmp::Ordering, sync::Arc, time::Duration}; use alloy_chains::{Chain, NamedChain}; -use alloy_consensus::TxEnvelope; +use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::{BlockId, eip2718::Encodable2718}; -use alloy_network::{Ethereum, EthereumWallet, Network, ReceiptResponse, TransactionBuilder}; +use alloy_network::{EthereumWallet, Network, ReceiptResponse, TransactionBuilder}; use alloy_primitives::{ Address, TxHash, map::{AddressHashMap, AddressHashSet}, utils::format_units, }; use alloy_provider::{Provider, RootProvider, utils::Eip1559Estimation}; -use alloy_rpc_types::TransactionRequest; +use alloy_signer::Signature; use eyre::{Context, Result, bail}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::Wallets; @@ -22,9 +22,11 @@ use foundry_common::{ }; use foundry_config::Config; use foundry_primitives::FoundryTransactionBuilder; -use foundry_wallets::wallet_browser::signer::BrowserSigner; +use foundry_wallets::{TempoAccessKeyConfig, WalletSigner, wallet_browser::signer::BrowserSigner}; use futures::{FutureExt, StreamExt, future::join_all, stream::FuturesUnordered}; use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use tempo_alloy::provider::TempoProviderExt; use crate::{ ScriptArgs, ScriptConfig, build::LinkedBuildData, progress::ScriptProgress, @@ -65,14 +67,21 @@ pub async fn next_nonce( /// Represents how to send a single transaction. #[derive(Clone)] -pub enum SendTransactionKind<'a> { - Unlocked(TransactionRequest), - Raw(TransactionRequest, &'a EthereumWallet), - Browser(TransactionRequest, &'a BrowserSigner), - Signed(TxEnvelope), +pub enum SendTransactionKind<'a, N: Network> { + Unlocked(N::TransactionRequest), + Raw(N::TransactionRequest, &'a EthereumWallet), + Browser(N::TransactionRequest, &'a BrowserSigner), + Signed(N::TxEnvelope), + AccessKey(N::TransactionRequest, &'a WalletSigner, &'a TempoAccessKeyConfig, String), } -impl<'a> SendTransactionKind<'a> { +impl<'a, N: Network> SendTransactionKind<'a, N> +where + N::TxEnvelope: From>, + N::UnsignedTx: SignableTransaction, + N::TransactionRequest: + FoundryTransactionBuilder + Into, +{ /// Prepares the transaction for broadcasting by synchronizing nonce and estimating gas. /// /// This method performs two key operations: @@ -81,17 +90,21 @@ impl<'a> SendTransactionKind<'a> { /// 2. Gas estimation: Re-estimates gas right before broadcasting for chains that require it pub async fn prepare( &mut self, - provider: &RootProvider, + provider: &RootProvider, sequential_broadcast: bool, is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, ) -> Result<()> { - if let Self::Raw(tx, _) | Self::Unlocked(tx) | Self::Browser(tx, _) = self { + if let Self::Raw(tx, _) + | Self::Unlocked(tx) + | Self::Browser(tx, _) + | Self::AccessKey(tx, _, _, _) = self + { if sequential_broadcast { - let from = tx.from.expect("no sender"); + let from = tx.from().expect("no sender"); - let tx_nonce = tx.nonce.expect("no nonce"); + let tx_nonce = tx.nonce().expect("no nonce"); for attempt in 0..5 { let nonce = provider.get_transaction_count(from).await?; match nonce.cmp(&tx_nonce) { @@ -135,7 +148,7 @@ impl<'a> SendTransactionKind<'a> { /// - Submit via `eth_sendTransaction` for unlocked accounts /// - Sign and submit via `eth_sendRawTransaction` for raw transactions /// - Submit pre-signed transaction via `eth_sendRawTransaction` - pub async fn send(self, provider: Arc>) -> Result { + pub async fn send(self, provider: Arc>) -> Result { match self { Self::Unlocked(tx) => { debug!("sending transaction from unlocked account {:?}", tx); @@ -163,6 +176,35 @@ impl<'a> SendTransactionKind<'a> { // Sign and send the transaction via the browser wallet Ok(signer.send_transaction_via_browser(tx).await?) } + Self::AccessKey(mut tx, signer, access_key, rpc_url) => { + debug!("sending transaction via tempo access key: {:?}", tx); + + // Check if the key needs on-chain provisioning. + if let Some(auth) = &access_key.key_authorization { + let tempo_provider = foundry_common::provider::ProviderBuilder::< + tempo_alloy::TempoNetwork, + >::new(&rpc_url) + .build()?; + if !tempo_provider + .get_keychain_key(access_key.wallet_address, access_key.key_address) + .await + .map(|info| info.keyId != Address::ZERO) + .unwrap_or(false) + { + tx.set_key_authorization(auth.clone()); + } + } + + let raw_tx = foundry_wallets::tempo::sign_with_access_key( + tx, + signer, + access_key.wallet_address, + ) + .await?; + + let pending = provider.send_raw_transaction(&raw_tx).await?; + Ok(*pending.tx_hash()) + } } } @@ -172,7 +214,7 @@ impl<'a> SendTransactionKind<'a> { /// [`send`](Self::send) into a single call. pub async fn prepare_and_send( mut self, - provider: Arc>, + provider: Arc>, sequential_broadcast: bool, is_fixed_gas_limit: bool, estimate_via_rpc: bool, @@ -192,22 +234,27 @@ impl<'a> SendTransactionKind<'a> { } /// Represents how to send _all_ transactions -pub enum SendTransactionsKind { +pub enum SendTransactionsKind { /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. Unlocked(AddressHashSet), /// Send a signed transaction via `eth_sendRawTransaction`, or via browser - Raw { eth_wallets: AddressHashMap, browser: Option }, + Raw { + eth_wallets: AddressHashMap, + browser: Option>, + access_keys: AddressHashMap<(WalletSigner, TempoAccessKeyConfig)>, + }, } -impl SendTransactionsKind { +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, - tx: TransactionRequest, - ) -> Result> { + tx: N::TransactionRequest, + rpc_url: &str, + ) -> Result> { match self { Self::Unlocked(unlocked) => { if !unlocked.contains(addr) { @@ -215,8 +262,10 @@ impl SendTransactionsKind { } Ok(SendTransactionKind::Unlocked(tx)) } - Self::Raw { eth_wallets, browser } => { - if let Some(wallet) = eth_wallets.get(addr) { + Self::Raw { eth_wallets, browser, access_keys } => { + if let Some((signer, config)) = access_keys.get(addr) { + Ok(SendTransactionKind::AccessKey(tx, signer, config, rpc_url.to_string())) + } else if let Some(wallet) = eth_wallets.get(addr) { Ok(SendTransactionKind::Raw(tx, wallet)) } else if let Some(b) = browser && b.address() == *addr @@ -233,15 +282,24 @@ impl SendTransactionsKind { /// State after we have bundled all /// [`TransactionWithMetadata`](forge_script_sequence::TransactionWithMetadata) objects into a /// single [`ScriptSequenceKind`] object containing one or more script sequences. -pub struct BundledState { +pub struct BundledState +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, +{ pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, - pub sequence: ScriptSequenceKind, + pub sequence: ScriptSequenceKind, } -impl BundledState { +impl BundledState +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, +{ pub async fn wait_for_pending(mut self) -> Result { let progress = ScriptProgress::default(); let progress_ref = &progress; @@ -276,7 +334,13 @@ impl BundledState { } /// Broadcasts transactions from all sequences. - pub async fn broadcast(mut self) -> Result { + pub async fn broadcast(mut self) -> Result> + where + N::TxEnvelope: From>, + N::UnsignedTx: SignableTransaction, + N::TransactionRequest: + FoundryTransactionBuilder + Into, + { let required_addresses = self .sequence .sequences() @@ -298,13 +362,29 @@ impl BundledState { let send_kind = if self.args.unlocked { SendTransactionsKind::Unlocked(required_addresses.clone()) } else { - let signers: Vec
= - self.script_wallets.signers().map_err(|e| eyre::eyre!("{e}"))?; + let signers: Vec
= self + .script_wallets + .signers() + .map_err(|e| eyre::eyre!("{e}"))? + .into_iter() + .chain(self.browser_wallet.as_ref().map(|b| b.address())) + .collect(); + + // For addresses without an explicit signer, try Tempo access key lookup. + let mut access_keys: AddressHashMap<(WalletSigner, TempoAccessKeyConfig)> = + AddressHashMap::default(); let mut missing_addresses = Vec::new(); for addr in &required_addresses { if !signers.contains(addr) { - missing_addresses.push(addr); + match foundry_wallets::tempo::lookup_signer(*addr) { + Ok(foundry_wallets::tempo::TempoLookup::Keychain(signer, config)) => { + access_keys.insert(*addr, (signer, *config)); + } + _ => { + missing_addresses.push(addr); + } + } } } @@ -316,11 +396,11 @@ impl BundledState { ); } - let (signers, browser) = self.script_wallets.into_multi_wallet().into_signers()?; + let signers = self.script_wallets.into_multi_wallet().into_signers()?; let eth_wallets = signers.into_iter().map(|(addr, signer)| (addr, signer.into())).collect(); - SendTransactionsKind::Raw { eth_wallets, browser } + SendTransactionsKind::Raw { eth_wallets, browser: self.browser_wallet, access_keys } }; let progress = ScriptProgress::default(); @@ -379,7 +459,7 @@ impl BundledState { SendTransactionKind::Signed(tx) } TransactionMaybeSigned::Unsigned(mut tx) => { - let from = tx.from.expect("No sender for onchain transaction!"); + let from = tx.from().expect("No sender for onchain transaction!"); tx.set_chain_id(sequence.chain); @@ -399,7 +479,7 @@ impl BundledState { tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); } - send_kind.for_sender(&from, tx)? + send_kind.for_sender(&from, tx, sequence.rpc_url())? } }; diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 7c5ea9e14d54a..3388989b35457 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -2,7 +2,7 @@ use crate::{ ScriptArgs, ScriptConfig, broadcast::BundledState, execute::LinkedState, multi_sequence::MultiChainSequence, sequence::ScriptSequenceKind, }; -use alloy_network::AnyNetwork; +use alloy_network::{AnyNetwork, Ethereum}; use alloy_primitives::{B256, Bytes}; use alloy_provider::Provider; use eyre::{OptionExt, Result}; @@ -20,6 +20,7 @@ use foundry_compilers::{ }; use foundry_evm::traces::debug::ContractSources; use foundry_linking::Linker; +use foundry_wallets::wallet_browser::signer::BrowserSigner; use std::{path::PathBuf, str::FromStr, sync::Arc}; /// Container for the compiled contracts. @@ -157,13 +158,14 @@ pub struct PreprocessedState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, } 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 Self { args, script_config, script_wallets, browser_wallet } = self; let project = script_config.config.project()?; let mut target_name = args.target_contract.clone(); @@ -232,6 +234,7 @@ impl PreprocessedState { args, script_config, script_wallets, + browser_wallet, build_data: BuildData { output, target, project_root: project.root().to_path_buf() }, }) } @@ -242,21 +245,22 @@ pub struct CompiledState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: BuildData, } impl CompiledState { /// Uses provided sender address to compute library addresses and link contracts with them. pub async fn link(self) -> Result { - let Self { args, script_config, script_wallets, build_data } = self; + let Self { args, script_config, script_wallets, browser_wallet, build_data } = self; let build_data = build_data.link(&script_config).await?; - Ok(LinkedState { args, script_config, script_wallets, build_data }) + Ok(LinkedState { args, script_config, script_wallets, browser_wallet, build_data }) } /// Tries loading the resumed state from the cache files, skipping simulation stage. - pub async fn resume(self) -> Result { + pub async fn resume(self) -> Result> { let chain = if self.args.multi { None } else { @@ -285,35 +289,49 @@ impl CompiledState { } }; - 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)) { - // IF we are missing required signers, execute script as we might need to collect - // private keys from the execution. - let executed = self.link().await?.prepare_execution().await?.execute().await?; + let (args, build_data, script_wallets, browser_wallet, 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)) { + // IF we are missing required signers, execute script as we might need to + // collect private keys from the execution. + let executed = self.link().await?.prepare_execution().await?.execute().await?; + ( + executed.args, + executed.build_data.build_data, + executed.script_wallets, + executed.browser_wallet, + executed.script_config, + ) + } else { + ( + self.args, + self.build_data, + self.script_wallets, + self.browser_wallet, + self.script_config, + ) + } + } else { ( - executed.args, - executed.build_data.build_data, - executed.script_wallets, - executed.script_config, + self.args, + self.build_data, + self.script_wallets, + self.browser_wallet, + self.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 { @@ -328,12 +346,17 @@ impl CompiledState { args, script_config, script_wallets, + browser_wallet, build_data: linked_build_data, sequence, }) } - fn try_load_sequence(&self, chain: Option, dry_run: bool) -> Result { + fn try_load_sequence( + &self, + chain: Option, + dry_run: bool, + ) -> Result> { if let Some(chain) = chain { let sequence = ScriptSequence::load( &self.script_config.config, diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 326960c23ac66..06b54403e1480 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; -use alloy_network::AnyNetwork; +use alloy_network::{AnyNetwork, Ethereum}; use alloy_primitives::{ Address, Bytes, map::{HashMap, HashSet}, @@ -32,6 +32,7 @@ use foundry_evm::{ render_trace_arena, }, }; +use foundry_wallets::wallet_browser::signer::BrowserSigner; use futures::future::join_all; use itertools::Itertools; use std::path::Path; @@ -43,6 +44,7 @@ pub struct LinkedState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, } @@ -63,7 +65,7 @@ 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 Self { args, script_config, script_wallets, browser_wallet, build_data } = self; let target_contract = build_data.get_target_contract()?; @@ -77,6 +79,7 @@ impl LinkedState { args, script_config, script_wallets, + browser_wallet, execution_data: ExecutionData { func, calldata, @@ -94,6 +97,7 @@ pub struct PreExecutionState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, } @@ -123,6 +127,7 @@ impl PreExecutionState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data.build_data, }; @@ -133,6 +138,7 @@ impl PreExecutionState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, execution_data: self.execution_data, execution_result: result, @@ -180,7 +186,7 @@ impl PreExecutionState { /// them instead. fn maybe_new_sender( &self, - transactions: Option<&BroadcastableTransactions>, + transactions: Option<&BroadcastableTransactions>, ) -> Result> { let mut new_sender = None; @@ -220,7 +226,7 @@ pub struct RpcData { impl RpcData { /// Iterates over script transactions and collects RPC urls. - fn from_transactions(txs: &BroadcastableTransactions) -> Self { + 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::>(); @@ -276,6 +282,7 @@ pub struct ExecutedState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, pub execution_result: ScriptResult, @@ -288,7 +295,8 @@ impl ExecutedState { let decoder = self.build_trace_decoder(&self.build_data.known_contracts).await?; - let mut txs = self.execution_result.transactions.clone().unwrap_or_default(); + let mut txs: BroadcastableTransactions = + self.execution_result.transactions.clone().unwrap_or_default(); // Ensure that unsigned transactions have both `data` and `input` populated to avoid // issues with eth_estimateGas and eth_sendTransaction requests. @@ -314,6 +322,7 @@ impl ExecutedState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, execution_data: self.execution_data, execution_result: self.execution_result, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 684bd42baae89..6d7e65598c68f 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -13,8 +13,9 @@ extern crate tracing; use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; +use alloy_network::Ethereum; use alloy_primitives::{ - Address, Bytes, Log, TxKind, U256, hex, + Address, Bytes, Log, U256, hex, map::{AddressHashMap, HashMap}, }; use alloy_signer::Signer; @@ -228,25 +229,28 @@ pub struct ScriptArgs { impl ScriptArgs { pub async fn preprocess(self) -> Result { let script_wallets = Wallets::new(self.wallets.get_multi_wallet().await?, self.evm.sender); + let browser_wallet = self.wallets.browser_signer::().await?; let (config, mut evm_opts) = self.load_config_and_evm_opts()?; if let Some(sender) = self.maybe_load_private_key()? { evm_opts.sender = sender; } else if self.evm.sender.is_none() { - // If no sender was explicitly set via --sender and there's exactly one signer - // (e.g. from --account or --keystore), use that signer's address as the sender. - // This makes --account behave consistently with --private-key. + // If no sender was explicitly set via --sender, auto-detect it from available signers: + // use the sole signer's address if there's exactly one, or fall back to the browser + // wallet address if present. if let Ok(signers) = script_wallets.signers() && signers.len() == 1 { evm_opts.sender = signers[0]; + } else if let Some(signer) = browser_wallet.as_ref().map(|b| b.address()) { + evm_opts.sender = signer } } let script_config = ScriptConfig::new(config, evm_opts).await?; - Ok(PreprocessedState { args: self, script_config, script_wallets }) + Ok(PreprocessedState { args: self, script_config, script_wallets, browser_wallet }) } /// Executes the script @@ -464,15 +468,13 @@ impl ScriptArgs { let mut offset = 0; // Find if it's a CREATE or CREATE2. Otherwise, skip transaction. - if let Some(TxKind::Call(to)) = to { + if let Some(to) = to { if to == create2_deployer { // Size of the salt prefix. offset = 32; } else { continue; } - } else if let Some(TxKind::Create) = to { - // Pass } // Find artifact with a deployment code same as the data. @@ -541,7 +543,7 @@ pub struct ScriptResult { pub gas_used: u64, pub labeled_addresses: AddressHashMap, #[serde(skip)] - pub transactions: Option, + pub transactions: Option>, pub returned: Bytes, pub address: Option
, #[serde(skip)] @@ -563,7 +565,7 @@ impl ScriptResult { .find_by_creation_code(init_code.as_ref()) .map(|artifact| artifact.0.name.clone()); return Some(AdditionalContract { - opcode: node.trace.kind, + call_kind: node.trace.kind, address: node.trace.address, contract_name, init_code, @@ -636,13 +638,13 @@ impl ScriptConfig { debug: bool, ) -> Result { trace!("preparing script runner"); - let env = self.evm_opts.env().await?; + let (evm_env, tx_env) = self.evm_opts.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.evm_env.clone()); + let fork = self.evm_opts.get_fork(&self.config, evm_env.clone()); let backend = Backend::spawn(fork)?; self.backends.insert(fork_url.clone(), backend.clone()); backend @@ -685,7 +687,7 @@ impl ScriptConfig { }); } - Ok(ScriptRunner::new(builder.build(env, db), self.evm_opts.clone())) + Ok(ScriptRunner::new(builder.build(evm_env, tx_env, db), self.evm_opts.clone())) } } diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index 128df9460e95b..3c1e7e448ae05 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -1,3 +1,4 @@ +use alloy_network::Network; use eyre::{ContextCompat, Result, WrapErr}; use forge_script_sequence::{ DRY_RUN_DIR, ScriptSequence, SensitiveScriptSequence, now, sig_to_file_name, @@ -10,8 +11,12 @@ use std::path::PathBuf; /// Holds the sequences of multiple chain deployments. #[derive(Clone, Default, Serialize, Deserialize)] -pub struct MultiChainSequence { - pub deployments: Vec, +#[serde(bound( + serialize = "N::TransactionRequest: Serialize, N::TxEnvelope: Serialize", + deserialize = "N::TransactionRequest: for<'de2> Deserialize<'de2>, N::TxEnvelope: for<'de2> Deserialize<'de2>" +))] +pub struct MultiChainSequence { + pub deployments: Vec>, #[serde(skip)] pub path: PathBuf, #[serde(skip)] @@ -26,16 +31,16 @@ pub struct SensitiveMultiChainSequence { } impl SensitiveMultiChainSequence { - fn from_multi_sequence(sequence: &MultiChainSequence) -> Self { + fn from_multi_sequence(sequence: &MultiChainSequence) -> Self { Self { deployments: sequence.deployments.iter().map(SensitiveScriptSequence::from).collect(), } } } -impl MultiChainSequence { +impl MultiChainSequence { pub fn new( - deployments: Vec, + deployments: Vec>, sig: &str, target: &ArtifactId, config: &Config, @@ -88,7 +93,10 @@ impl MultiChainSequence { } /// Loads the sequences for the multi chain deployment. - pub fn load(config: &Config, sig: &str, target: &ArtifactId, dry_run: bool) -> Result { + pub fn load(config: &Config, sig: &str, target: &ArtifactId, dry_run: bool) -> Result + where + N::TxEnvelope: for<'d> Deserialize<'d>, + { 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.")?; @@ -107,7 +115,10 @@ impl MultiChainSequence { } /// Saves the transactions as file if it's a standalone deployment. - pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> + where + N::TxEnvelope: Serialize, + { self.deployments.iter_mut().for_each(|sequence| sequence.sort_receipts()); self.timestamp = now().as_millis(); diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index 4038d800a784b..303d5125c8521 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -1,6 +1,6 @@ use crate::receipts::{PendingReceiptError, TxStatus, check_tx_status, format_receipt}; use alloy_chains::Chain; -use alloy_network::{Ethereum, ReceiptResponse}; +use alloy_network::{Network, ReceiptResponse}; use alloy_primitives::{ B256, map::{B256HashMap, HashMap}, @@ -32,7 +32,11 @@ pub struct SequenceProgressState { } impl SequenceProgressState { - pub fn new(sequence_idx: usize, sequence: &ScriptSequence, multi: MultiProgress) -> Self { + pub fn new( + sequence_idx: usize, + sequence: &ScriptSequence, + multi: MultiProgress, + ) -> Self { let mut state = if shell::is_quiet() || shell::is_json() { let top_spinner = ProgressBar::hidden(); let txs = ProgressBar::hidden(); @@ -143,7 +147,11 @@ pub struct SequenceProgress { } impl SequenceProgress { - pub fn new(sequence_idx: usize, sequence: &ScriptSequence, multi: MultiProgress) -> Self { + pub fn new( + sequence_idx: usize, + sequence: &ScriptSequence, + multi: MultiProgress, + ) -> Self { Self { inner: Arc::new(RwLock::new(SequenceProgressState::new(sequence_idx, sequence, multi))), } @@ -160,10 +168,10 @@ pub struct ScriptProgress { impl ScriptProgress { /// Returns a [SequenceProgress] instance for the given sequence index. If it doesn't exist, /// creates one. - pub fn get_sequence_progress( + pub fn get_sequence_progress( &self, sequence_idx: usize, - sequence: &ScriptSequence, + sequence: &ScriptSequence, ) -> SequenceProgress { if let Some(progress) = self.state.read().get(&sequence_idx) { return progress.clone(); @@ -183,11 +191,11 @@ impl ScriptProgress { /// 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( + pub async fn wait_for_pending( &self, sequence_idx: usize, - deployment_sequence: &mut ScriptSequence, - provider: &RootProvider, + deployment_sequence: &mut ScriptSequence, + provider: &RootProvider, timeout: u64, ) -> Result<()> { if deployment_sequence.pending.is_empty() { diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index afa4e8ab03e4d..217cfc36c088a 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,6 +1,6 @@ use alloy_chains::{Chain, NamedChain}; -use alloy_network::{Ethereum, ReceiptResponse}; -use alloy_primitives::{TxHash, U256, utils::format_units}; +use alloy_network::{Network, ReceiptResponse}; +use alloy_primitives::{Address, TxHash, U256, utils::format_units}; use alloy_provider::{ PendingTransactionBuilder, PendingTransactionError, Provider, RootProvider, WatchTxError, }; @@ -10,6 +10,18 @@ use forge_script_sequence::ScriptSequence; use foundry_common::{retry, retry::RetryError, shell}; use std::time::Duration; +/// Helper trait providing `contract_address` setter for generic `ReceiptResponse` +pub trait FoundryReceiptResponse { + /// Sets address of the created contract, or `None` if the transaction was not a deployment. + fn set_contract_address(&mut self, contract_address: Address); +} + +impl FoundryReceiptResponse for TransactionReceipt { + fn set_contract_address(&mut self, contract_address: Address) { + self.contract_address = Some(contract_address); + } +} + /// Marker error type for pending receipts #[derive(Debug, thiserror::Error)] #[error( @@ -20,25 +32,25 @@ pub struct PendingReceiptError { } /// Convenience enum for internal signalling of transaction status -pub enum TxStatus { +pub enum TxStatus { Dropped, - Success(TransactionReceipt), - Revert(TransactionReceipt), + Success(R), + Revert(R), } -impl From for TxStatus { - fn from(receipt: TransactionReceipt) -> Self { +impl From for TxStatus { + fn from(receipt: R) -> Self { if !receipt.status() { Self::Revert(receipt) } else { Self::Success(receipt) } } } /// Checks the status of a txhash by first polling for a receipt, then for /// mempool inclusion. Returns the tx hash, and a status -pub async fn check_tx_status( - provider: &RootProvider, +pub async fn check_tx_status( + provider: &RootProvider, hash: TxHash, timeout: u64, -) -> (TxHash, Result) { +) -> (TxHash, Result, eyre::Report>) { let result = retry::Retry::new_no_delay(3) .run_async_until_break(|| async { match PendingTransactionBuilder::new(provider.clone(), hash) @@ -89,10 +101,10 @@ pub async fn check_tx_status( } /// Prints parts of the receipt to stdout -pub fn format_receipt( +pub fn format_receipt( chain: Chain, - receipt: &TransactionReceipt, - sequence: Option<&ScriptSequence>, + receipt: &N::ReceiptResponse, + sequence: Option<&ScriptSequence>, ) -> String { let gas_used = receipt.gas_used(); let gas_price = receipt.effective_gas_price(); @@ -182,6 +194,7 @@ pub fn format_receipt( #[cfg(test)] mod tests { use super::*; + use alloy_network::Ethereum; use alloy_primitives::B256; use std::collections::VecDeque; @@ -198,7 +211,11 @@ mod tests { .unwrap() } - fn mock_sequence(tx_hash: B256, contract: Option<&str>, func: Option<&str>) -> ScriptSequence { + fn mock_sequence( + tx_hash: B256, + contract: Option<&str>, + func: Option<&str>, + ) -> ScriptSequence { let tx = serde_json::from_value(serde_json::json!({ "hash": tx_hash, "transactionType": "CALL", "contractName": contract, "contractAddress": null, "function": func, @@ -228,7 +245,7 @@ mod tests { #[test] fn format_receipt_without_sequence_omits_metadata() { let hash = B256::repeat_byte(0x42); - let out = format_receipt(Chain::mainnet(), &mock_receipt(hash, true), None); + let out = format_receipt::(Chain::mainnet(), &mock_receipt(hash, true), None); assert!(!out.contains("Contract:")); assert!(!out.contains("Function:")); diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index d3d0b83319bc6..510a5afaf2833 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -5,6 +5,7 @@ use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_rpc_types::TransactionRequest; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; +use foundry_common::TransactionMaybeSigned; use foundry_config::Config; use foundry_evm::{ constants::CALLER, @@ -73,13 +74,12 @@ impl ScriptRunner { library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), - transaction: TransactionRequest { + transaction: TransactionMaybeSigned::new(TransactionRequest { from: Some(self.evm_opts.sender), input: code.clone().into(), nonce: Some(sender_nonce + library_transactions.len() as u64), ..Default::default() - } - .into(), + }), }) }), ScriptPredeployLibraries::Create2(libraries, salt) => { @@ -107,14 +107,13 @@ impl ScriptRunner { library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), - transaction: TransactionRequest { + transaction: TransactionMaybeSigned::new(TransactionRequest { from: Some(self.evm_opts.sender), input: calldata.into(), nonce: Some(sender_nonce + library_transactions.len() as u64), to: Some(TxKind::Call(create2_deployer)), ..Default::default() - } - .into(), + }), }); } @@ -369,14 +368,14 @@ impl ScriptRunner { let mut gas_used = res.gas_used; if matches!(res.exit_reason, Some(return_ok!())) { // Store the current gas limit and reset it later. - let init_gas_limit = self.executor.env().tx.gas_limit; + let init_gas_limit = self.executor.tx_env().gas_limit; let mut highest_gas_limit = gas_used * 3; let mut lowest_gas_limit = gas_used; let mut last_highest_gas_limit = highest_gas_limit; while (highest_gas_limit - lowest_gas_limit) > 1 { let mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; - self.executor.env_mut().tx.gas_limit = mid_gas_limit; + self.executor.tx_env_mut().gas_limit = mid_gas_limit; let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?; match res.exit_reason { Some(InstructionResult::Revert) @@ -402,7 +401,7 @@ impl ScriptRunner { } } // Reset gas limit in the executor. - self.executor.env_mut().tx.gas_limit = init_gas_limit; + self.executor.tx_env_mut().gas_limit = init_gas_limit; } Ok(gas_used) } diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index c0a7e9deddca7..b2a74cc182e19 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -1,23 +1,33 @@ use crate::multi_sequence::MultiChainSequence; +use alloy_network::Network; use eyre::Result; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cli::utils::Git; use foundry_common::fmt::UIfmt; use foundry_compilers::ArtifactId; use foundry_config::Config; +use foundry_primitives::FoundryTransactionBuilder; +use serde::{Deserialize, Serialize}; use std::{ fmt::{Error, Write}, path::Path, }; /// Format transaction details for display -fn format_transaction(index: usize, tx: &TransactionWithMetadata) -> Result { +fn format_transaction( + index: usize, + tx: &TransactionWithMetadata, +) -> Result +where + N::TxEnvelope: UIfmt, + N::TransactionRequest: FoundryTransactionBuilder, +{ let mut output = String::new(); writeln!(output, "### Transaction {index} ###")?; writeln!(output, "{}", tx.tx().pretty())?; // Show contract name and address if available - if !tx.opcode.is_any_create() + if !tx.call_kind.is_any_create() && let (Some(name), Some(addr)) = (&tx.contract_name, &tx.contract_address) { writeln!(output, "contract: {name}({addr})")?; @@ -45,12 +55,20 @@ pub fn get_commit_hash(root: &Path) -> Option { Git::new(root).commit_hash(true, "HEAD").ok() } -pub enum ScriptSequenceKind { - Single(ScriptSequence), - Multi(MultiChainSequence), +pub enum ScriptSequenceKind +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, +{ + Single(ScriptSequence), + Multi(MultiChainSequence), } -impl ScriptSequenceKind { +impl ScriptSequenceKind +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, +{ pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { match self { Self::Single(sequence) => sequence.save(silent, save_ts), @@ -58,14 +76,14 @@ impl ScriptSequenceKind { } } - pub fn sequences(&self) -> &[ScriptSequence] { + pub fn sequences(&self) -> &[ScriptSequence] { match self { Self::Single(sequence) => std::slice::from_ref(sequence), Self::Multi(sequence) => &sequence.deployments, } } - pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] { + pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] { match self { Self::Single(sequence) => std::slice::from_mut(sequence), Self::Multi(sequence) => &mut sequence.deployments, @@ -80,19 +98,28 @@ impl ScriptSequenceKind { ) -> Result<()> { match self { Self::Single(sequence) => { - sequence.paths = - Some(ScriptSequence::get_paths(config, sig, target, sequence.chain, false)?); + sequence.paths = Some(ScriptSequence::::get_paths( + config, + sig, + target, + sequence.chain, + false, + )?); } Self::Multi(sequence) => { (sequence.path, sequence.sensitive_path) = - MultiChainSequence::get_paths(config, sig, target, false)?; + MultiChainSequence::::get_paths(config, sig, target, false)?; } }; Ok(()) } - pub fn show_transactions(&self) -> Result<()> { + pub fn show_transactions(&self) -> Result<()> + where + N::TxEnvelope: UIfmt, + N::TransactionRequest: FoundryTransactionBuilder, + { for sequence in self.sequences() { if !sequence.transactions.is_empty() { sh_println!("\nChain {}\n", sequence.chain)?; @@ -107,7 +134,11 @@ impl ScriptSequenceKind { } } -impl Drop for ScriptSequenceKind { +impl Drop for ScriptSequenceKind +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, +{ fn drop(&mut self) { if let Err(err) = self.save(false, true) { error!(?err, "could not save deployment sequence"); diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 00869a34dd3cf..e32b8e870c7e3 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -10,8 +10,8 @@ use crate::{ sequence::get_commit_hash, }; use alloy_chains::NamedChain; -use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, TxKind, U256, map::HashMap, utils::format_units}; +use alloy_network::{Ethereum, Network, TransactionBuilder}; +use alloy_primitives::{Address, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; @@ -19,6 +19,8 @@ use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{ContractData, shell}; use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; +use foundry_primitives::FoundryTransactionBuilder; +use foundry_wallets::wallet_browser::signer::BrowserSigner; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ @@ -36,6 +38,7 @@ pub struct PreSimulationState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, pub execution_result: ScriptResult, @@ -65,7 +68,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if let Some(TxKind::Call(_)) = to { + if to.is_some() { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, @@ -89,6 +92,7 @@ impl PreSimulationState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, execution_artifacts: self.execution_artifacts, transactions, @@ -99,10 +103,13 @@ impl PreSimulationState { /// transactions in those environments. /// /// Collects gas usage and metadata for each transaction. - pub async fn simulate_and_fill( + pub async fn simulate_and_fill( &self, - transactions: VecDeque, - ) -> Result> { + transactions: VecDeque>, + ) -> Result>> + where + N::TransactionRequest: FoundryTransactionBuilder, + { trace!(target: "script", "executing onchain simulation"); let runners = Arc::new( @@ -122,7 +129,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; + let to = tx.to(); let result = runner .simulate( tx.from() @@ -140,7 +147,7 @@ impl PreSimulationState { // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { - runner.executor.env_mut().evm_env.block_env.number += U256::from(1); + runner.executor.evm_env_mut().block_env.number += U256::from(1); } let is_noop_tx = if let Some(to) = to { @@ -253,9 +260,10 @@ pub struct FilledTransactionsState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, - pub transactions: VecDeque, + pub transactions: VecDeque>, } impl FilledTransactionsState { @@ -264,7 +272,7 @@ impl FilledTransactionsState { /// chain deployment. /// /// Each transaction will be added with the correct transaction type and gas estimation. - pub async fn bundle(mut self) -> Result { + pub async fn bundle(mut 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() { @@ -414,6 +422,7 @@ impl FilledTransactionsState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, sequence, }) @@ -424,14 +433,14 @@ impl FilledTransactionsState { &self, multi: bool, chain: u64, - transactions: VecDeque, - ) -> Result { + 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( + Some(ScriptSequence::::get_paths( &self.script_config.config, &self.args.sig, &self.build_data.build_data.target, diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 9efbe363b23ef..4d742eebe6dc2 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,8 +1,8 @@ use super::ScriptResult; use crate::build::LinkedBuildData; use alloy_dyn_abi::JsonAbiExt; -use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, B256, TxKind, hex}; +use alloy_network::{Network, TransactionBuilder}; +use alloy_primitives::{Address, B256, hex}; use eyre::Result; use forge_script_sequence::TransactionWithMetadata; use foundry_common::{ContractData, SELECTOR_LEN, TransactionMaybeSigned, fmt::format_token_raw}; @@ -12,12 +12,12 @@ use revm_inspectors::tracing::types::CallKind; use std::collections::BTreeMap; #[derive(Debug)] -pub struct ScriptTransactionBuilder { - transaction: TransactionWithMetadata, +pub struct ScriptTransactionBuilder { + transaction: TransactionWithMetadata, } -impl ScriptTransactionBuilder { - pub fn new(transaction: TransactionMaybeSigned, rpc: String) -> Self { +impl ScriptTransactionBuilder { + pub fn new(transaction: TransactionMaybeSigned, rpc: String) -> Self { let mut transaction = TransactionWithMetadata::from_tx_request(transaction); transaction.rpc = rpc; // If tx.gas is already set that means it was specified in script @@ -33,7 +33,7 @@ impl ScriptTransactionBuilder { decoder: &CallTraceDecoder, create2_deployer: Address, ) -> Result<()> { - if let Some(TxKind::Call(to)) = self.transaction.transaction.to() { + if let Some(to) = self.transaction.transaction.to() { if to == create2_deployer { if let Some(input) = self.transaction.transaction.input() { let (salt, init_code) = input.split_at(32); @@ -45,7 +45,7 @@ impl ScriptTransactionBuilder { )?; } } else { - self.transaction.opcode = CallKind::Call; + self.transaction.call_kind = CallKind::Call; self.transaction.contract_address = Some(to); let Some(data) = self.transaction.transaction.input() else { return Ok(()) }; @@ -97,9 +97,9 @@ impl ScriptTransactionBuilder { contracts: &BTreeMap, ) -> Result<()> { if is_create2 { - self.transaction.opcode = CallKind::Create2; + self.transaction.call_kind = CallKind::Create2; } else { - self.transaction.opcode = CallKind::Create; + self.transaction.call_kind = CallKind::Create; } let info = contracts.get(&address); @@ -171,13 +171,13 @@ impl ScriptTransactionBuilder { self } - pub fn build(self) -> TransactionWithMetadata { + pub fn build(self) -> TransactionWithMetadata { self.transaction } } -impl From for ScriptTransactionBuilder { - fn from(transaction: TransactionWithMetadata) -> Self { +impl From> for ScriptTransactionBuilder { + fn from(transaction: TransactionWithMetadata) -> Self { Self { transaction } } } diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 1bfac6d9564bc..156e32b7aea06 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -1,9 +1,10 @@ use crate::{ ScriptArgs, ScriptConfig, build::LinkedBuildData, + receipts::FoundryReceiptResponse, sequence::{ScriptSequenceKind, get_commit_hash}, }; -use alloy_network::ReceiptResponse; +use alloy_network::{Network, ReceiptResponse}; use alloy_primitives::{Address, hex}; use eyre::{Result, eyre}; use forge_script_sequence::{AdditionalContract, ScriptSequence}; @@ -13,18 +14,28 @@ use foundry_common::ContractsByArtifact; use foundry_compilers::{Project, artifacts::EvmVersion, info::ContractInfo}; use foundry_config::{Chain, Config}; use semver::Version; +use serde::{Deserialize, Serialize}; /// 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 struct BroadcastedState +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, +{ pub args: ScriptArgs, pub script_config: ScriptConfig, pub build_data: LinkedBuildData, - pub sequence: ScriptSequenceKind, + pub sequence: ScriptSequenceKind, } -impl BroadcastedState { +impl BroadcastedState +where + N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, + N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, + N::ReceiptResponse: FoundryReceiptResponse, +{ pub async fn verify(self) -> Result<()> { let Self { args, script_config, build_data, mut sequence, .. } = self; @@ -179,8 +190,8 @@ impl VerifyBundle { /// Given the broadcast log, it matches transactions with receipts, and tries to verify any /// created contract on etherscan. -async fn verify_contracts( - sequence: &mut ScriptSequence, +async fn verify_contracts>( + sequence: &mut ScriptSequence, config: &Config, mut verify: VerifyBundle, ) -> Result<()> { @@ -202,8 +213,10 @@ async fn verify_contracts( // create2 hash offset let mut offset = 0; - if tx.is_create2() { - receipt.contract_address = tx.contract_address; + if tx.is_create2() + && let Some(contract_address) = tx.contract_address + { + receipt.set_contract_address(contract_address); offset = 32; } @@ -266,8 +279,8 @@ async fn verify_contracts( Ok(()) } -fn check_unverified( - sequence: &ScriptSequence, +fn check_unverified( + sequence: &ScriptSequence, unverifiable_contracts: Vec
, verify: VerifyBundle, ) { diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index c22b9c857abed..4f442839ccc8b 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -231,7 +231,7 @@ impl VerifyBytecodeArgs { // Deploy at genesis let gen_blk_num = 0_u64; let (mut fork_config, evm_opts) = config.clone().load_config_and_evm_opts()?; - let (mut env, mut executor) = crate::utils::get_tracing_executor( + let (mut evm_env, _, mut executor) = crate::utils::get_tracing_executor( &mut fork_config, gen_blk_num, etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()), @@ -239,10 +239,10 @@ impl VerifyBytecodeArgs { ) .await?; - env.evm_env.block_env.number = U256::ZERO; + evm_env.block_env.number = U256::ZERO; let genesis_block = provider.get_block(gen_blk_num.into()).full().await?; - // Setup genesis tx and env. + // Setup genesis tx_env and evm_evm. let deployer = Address::with_last_byte(0x1); let mut gen_tx_req = TransactionRequest::default() .with_from(deployer) @@ -250,14 +250,14 @@ impl VerifyBytecodeArgs { .into_create(); if let Some(ref block) = genesis_block { - configure_env_block(&mut env, block, config.networks); + configure_env_block(&mut evm_env, block, config.networks); gen_tx_req.max_fee_per_gas = block.header.base_fee_per_gas().map(|g| g as u128); gen_tx_req.gas = Some(block.header.gas_limit()); gen_tx_req.gas_price = block.header.base_fee_per_gas().map(|g| g as u128); } let kind = gen_tx_req.kind(); - env.tx = gen_tx_req.try_into_tx_env(&env.evm_env)?; + let tx_env = gen_tx_req.try_into_tx_env(&evm_env)?; // Seed deployer account with funds let account_info = AccountInfo { @@ -267,8 +267,13 @@ impl VerifyBytecodeArgs { }; executor.backend_mut().insert_account_info(deployer, account_info); - let fork_address = - crate::utils::deploy_contract(&mut executor, &env, config.evm_spec_id(), kind)?; + let fork_address = crate::utils::deploy_contract( + &mut executor, + &evm_env, + &tx_env, + config.evm_spec_id(), + kind, + )?; // Compare runtime bytecode let (deployed_bytecode, onchain_runtime_code) = crate::utils::get_runtime_codes( @@ -442,14 +447,14 @@ impl VerifyBytecodeArgs { // Fork the chain at `simulation_block`. let (mut fork_config, evm_opts) = config.clone().load_config_and_evm_opts()?; - let (mut env, mut executor) = crate::utils::get_tracing_executor( + let (mut evm_env, mut tx_env, mut executor) = crate::utils::get_tracing_executor( &mut fork_config, simulation_block - 1, // env.fork_block_number etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()), evm_opts, ) .await?; - env.evm_env.block_env.number = U256::from(simulation_block); + evm_env.block_env.number = U256::from(simulation_block); let block = provider.get_block(simulation_block.into()).full().await?; // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same @@ -465,7 +470,7 @@ impl VerifyBytecodeArgs { transaction.set_nonce(prev_block_nonce); if let Some(ref block) = block { - configure_env_block(&mut env, block, config.networks); + configure_env_block(&mut evm_env, block, config.networks); let BlockTransactions::Full(ref txs) = block.transactions else { return Err(eyre::eyre!("Could not get block txs")); @@ -484,18 +489,22 @@ impl VerifyBytecodeArgs { } if let Some(tx_envelope) = tx.as_envelope() { - env.tx = TxEnv::from_recovered_tx(tx_envelope, tx.from()); + tx_env = TxEnv::from_recovered_tx(tx_envelope, tx.from()); } if let TxKind::Call(_) = tx.inner.kind() { - executor.transact_with_env(env.clone()).wrap_err_with(|| { - format!( - "Failed to execute transaction: {:?} in block {}", - tx.tx_hash(), - env.evm_env.block_env.number - ) - })?; - } else if let Err(error) = executor.deploy_with_env(env.clone(), None) { + executor.transact_with_env(evm_env.clone(), tx_env.clone()).wrap_err_with( + || { + format!( + "Failed to execute transaction: {:?} in block {}", + tx.tx_hash(), + evm_env.block_env.number + ) + }, + )?; + } else if let Err(error) = + executor.deploy_with_env(evm_env.clone(), tx_env.clone(), None) + { match error { // Reverted transactions should be skipped EvmError::Execution(_) => (), @@ -504,7 +513,7 @@ impl VerifyBytecodeArgs { format!( "Failed to deploy transaction: {:?} in block {}", tx.tx_hash(), - env.evm_env.block_env.number + evm_env.block_env.number ) }); } @@ -528,10 +537,15 @@ impl VerifyBytecodeArgs { } let kind = transaction.kind(); - env.tx = transaction.try_into_tx_env(&env.evm_env)?; + tx_env = transaction.try_into_tx_env(&evm_env)?; - let fork_address = - crate::utils::deploy_contract(&mut executor, &env, config.evm_spec_id(), kind)?; + let fork_address = crate::utils::deploy_contract( + &mut executor, + &evm_env, + &tx_env, + config.evm_spec_id(), + kind, + )?; // State committed using deploy_with_env, now get the runtime bytecode from the db. let (fork_runtime_code, onchain_runtime_code) = crate::utils::get_runtime_codes( diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index b0b2a7cc1ad74..27d9bdf8e37a5 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -1,7 +1,7 @@ use crate::{bytecode::VerifyBytecodeArgs, types::VerificationType}; -use alloy_consensus::BlockHeader; use alloy_dyn_abi::DynSolValue; -use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_evm::EvmEnv; +use alloy_primitives::{Address, Bytes, TxKind}; use alloy_provider::{ Provider, network::{AnyNetwork, AnyRpcBlock}, @@ -18,13 +18,16 @@ use foundry_common::{abi::encode_args, compile::ProjectCompiler, ignore_metadata use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}; use foundry_config::Config; use foundry_evm::{ - Env, constants::DEFAULT_CREATE2_DEPLOYER, core::decode::RevertDecoder, - executors::TracingExecutor, opts::EvmOpts, traces::TraceMode, - utils::apply_chain_and_block_specific_env_changes, + constants::DEFAULT_CREATE2_DEPLOYER, + core::decode::RevertDecoder, + executors::TracingExecutor, + opts::EvmOpts, + traces::TraceMode, + utils::{apply_chain_and_block_specific_env_changes, block_env_from_header}, }; use foundry_evm_networks::NetworkConfigs; use reqwest::Url; -use revm::{bytecode::Bytecode, database::Database, primitives::hardfork::SpecId}; +use revm::{bytecode::Bytecode, context::TxEnv, database::Database, primitives::hardfork::SpecId}; use semver::{BuildMetadata, Version}; use serde::{Deserialize, Serialize}; use yansi::Paint; @@ -265,16 +268,16 @@ pub async fn get_tracing_executor( fork_blk_num: u64, evm_version: EvmVersion, evm_opts: EvmOpts, -) -> Result<(Env, TracingExecutor)> { +) -> Result<(EvmEnv, TxEnv, TracingExecutor)> { fork_config.fork_block_number = Some(fork_blk_num); fork_config.evm_version = evm_version; let create2_deployer = evm_opts.create2_deployer; - let (env, fork, _chain, networks) = + let (evm_env, tx_env, fork, _chain, networks) = TracingExecutor::get_fork_material(fork_config, evm_opts).await?; let executor = TracingExecutor::new( - env.clone(), + (evm_env.clone(), tx_env.clone()), fork, Some(fork_config.evm_version), TraceMode::Call, @@ -283,31 +286,25 @@ pub async fn get_tracing_executor( None, )?; - Ok((env, executor)) + Ok((evm_env, tx_env, executor)) } -pub fn configure_env_block(env: &mut Env, block: &AnyRpcBlock, config: NetworkConfigs) { - env.evm_env.block_env.timestamp = U256::from(block.header.timestamp()); - env.evm_env.block_env.beneficiary = block.header.beneficiary(); - env.evm_env.block_env.difficulty = block.header.difficulty(); - env.evm_env.block_env.prevrandao = Some(block.header.mix_hash().unwrap_or_default()); - env.evm_env.block_env.basefee = block.header.base_fee_per_gas().unwrap_or_default(); - env.evm_env.block_env.gas_limit = block.header.gas_limit(); - apply_chain_and_block_specific_env_changes::(&mut env.evm_env, block, config); +pub fn configure_env_block(evm_env: &mut EvmEnv, block: &AnyRpcBlock, config: NetworkConfigs) { + let number = evm_env.block_env.number; + evm_env.block_env = block_env_from_header(&block.header); + evm_env.block_env.number = number; + apply_chain_and_block_specific_env_changes::(evm_env, block, config); } pub fn deploy_contract( executor: &mut TracingExecutor, - env: &Env, + evm_env: &EvmEnv, + tx_env: &TxEnv, spec_id: SpecId, to: Option, ) -> Result { - let env = Env::new_with_spec_id( - env.evm_env.cfg_env.clone(), - env.evm_env.block_env.clone(), - env.tx.clone(), - spec_id, - ); + let mut evm_env = evm_env.clone(); + evm_env.cfg_env.set_spec(spec_id); if to.is_some_and(|to| to.is_call()) { let TxKind::Call(to) = to.unwrap() else { unreachable!() }; @@ -316,7 +313,7 @@ pub fn deploy_contract( "Transaction `to` address is not the default create2 deployer i.e the tx is not a contract creation tx." ); } - let result = executor.transact_with_env(env)?; + let result = executor.transact_with_env(evm_env, tx_env.clone())?; trace!(transact_result = ?result.exit_reason); @@ -346,7 +343,7 @@ pub fn deploy_contract( Ok(Address::from_slice(&result.result)) } else { - let deploy_result = executor.deploy_with_env(env, None)?; + let deploy_result = executor.deploy_with_env(evm_env, tx_env.clone(), None)?; trace!(deploy_result = ?deploy_result.raw.exit_reason); Ok(deploy_result.address) } diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index e68de25e9b06e..381d8b2c600f7 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -43,13 +43,20 @@ alloy-signer-gcp = { workspace = true, features = ["eip712"], optional = true } # turnkey alloy-signer-turnkey = { workspace = true, features = ["eip712"], optional = true } +tempo-primitives.workspace = true +tempo-alloy.workspace = true +alloy-eips.workspace = true +alloy-rlp.workspace = true + async-trait.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } derive_builder = "0.20" +dirs.workspace = true eyre.workspace = true rpassword = "7" serde.workspace = true thiserror.workspace = true +toml.workspace = true tower.workspace = true tower-http = { workspace = true, features = ["cors", "set-header"] } tracing.workspace = true diff --git a/crates/wallets/src/lib.rs b/crates/wallets/src/lib.rs index f27e908b63a40..435da79ace3d8 100644 --- a/crates/wallets/src/lib.rs +++ b/crates/wallets/src/lib.rs @@ -14,6 +14,7 @@ extern crate tracing; pub mod error; pub mod opts; pub mod signer; +pub mod tempo; pub mod utils; pub mod wallet_browser; pub mod wallet_multi; @@ -21,6 +22,7 @@ pub mod wallet_raw; pub use opts::WalletOpts; pub use signer::{PendingSigner, WalletSigner}; +pub use tempo::TempoAccessKeyConfig; pub use wallet_browser::opts::BrowserWalletOpts; pub use wallet_multi::MultiWalletOpts; pub use wallet_raw::RawWalletOpts; diff --git a/crates/wallets/src/opts.rs b/crates/wallets/src/opts.rs index d1c4a97690411..5ca010929a7f0 100644 --- a/crates/wallets/src/opts.rs +++ b/crates/wallets/src/opts.rs @@ -1,4 +1,4 @@ -use crate::{signer::WalletSigner, utils, wallet_raw::RawWalletOpts}; +use crate::{signer::WalletSigner, tempo::TempoAccessKeyConfig, utils, wallet_raw::RawWalletOpts}; use alloy_primitives::Address; use clap::Parser; use eyre::Result; @@ -105,7 +105,16 @@ pub struct WalletOpts { } impl WalletOpts { - pub async fn signer(&self) -> Result { + /// Attempts to resolve a signer from the configured wallet options. + /// + /// Returns the signer and, for Tempo keychain mode, an [`TempoAccessKeyConfig`] describing the + /// root wallet and provisioning data. + /// + /// Returns `Ok((None, None))` if no wallet option was configured and no Tempo fallback + /// matched. + pub async fn maybe_signer( + &self, + ) -> Result<(Option, Option)> { trace!("start finding signer"); let get_env = |key: &str| { @@ -158,7 +167,29 @@ impl WalletOpts { unreachable!() } } else { - eyre::bail!( + // No explicit wallet option was provided. Try Tempo wallet as a fallback + // if `--from` is set. + if let Some(from) = self.from { + match crate::tempo::lookup_signer(from)? { + crate::tempo::TempoLookup::Direct(signer) => { + return Ok((Some(signer), None)); + } + crate::tempo::TempoLookup::Keychain(signer, config) => { + return Ok((Some(signer), Some(*config))); + } + crate::tempo::TempoLookup::NotFound => {} + } + } + + return Ok((None, None)); + }; + + Ok((Some(signer), None)) + } + + pub async fn signer(&self) -> Result { + self.maybe_signer().await?.0.ok_or_else(|| { + eyre::eyre!( "\ Error accessing local wallet. Did you pass a keystore, hardware wallet, private key or mnemonic? @@ -182,9 +213,7 @@ respectively. The sender address can be specified by setting the `ETH_FROM` envi variable to the desired unlocked account address, or by providing the address directly using the --from flag." ) - }; - - Ok(signer) + }) } } diff --git a/crates/wallets/src/tempo.rs b/crates/wallets/src/tempo.rs new file mode 100644 index 0000000000000..a86b568fdea2b --- /dev/null +++ b/crates/wallets/src/tempo.rs @@ -0,0 +1,196 @@ +use alloy_eips::Encodable2718; +use alloy_primitives::{Address, hex}; +use alloy_rlp::Decodable; +use alloy_signer::Signer; +use eyre::Result; +use std::path::PathBuf; +use tempo_alloy::rpc::TempoTransactionRequest; +use tempo_primitives::transaction::{ + KeychainSignature, PrimitiveSignature, SignedKeyAuthorization, TempoSignature, +}; + +use crate::{WalletSigner, utils}; + +/// Wallet type: how this wallet was created. +#[derive(Clone, Copy, Default, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +enum WalletType { + #[default] + Local, + Passkey, +} + +/// Cryptographic key type. +#[derive(Clone, Copy, Default, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +enum KeyType { + #[default] + Secp256k1, + P256, + WebAuthn, +} + +/// A single entry from Tempo's `keys.toml`. +#[derive(serde::Deserialize)] +#[allow(dead_code)] +struct KeyEntry { + #[serde(default)] + wallet_type: WalletType, + #[serde(default)] + wallet_address: Address, + #[serde(default)] + chain_id: u64, + #[serde(default)] + key_type: KeyType, + #[serde(default)] + key_address: Option
, + #[serde(default)] + key: Option, + #[serde(default)] + key_authorization: Option, + #[serde(default)] + expiry: Option, + #[serde(default)] + limits: Vec, +} + +/// Per-token spending limit stored in `keys.toml`. +#[derive(serde::Deserialize)] +struct StoredTokenLimit { + #[allow(dead_code)] + currency: Address, + #[allow(dead_code)] + limit: String, +} + +/// The top-level structure of `~/.tempo/wallet/keys.toml`. +#[derive(serde::Deserialize)] +struct KeysFile { + #[serde(default)] + keys: Vec, +} + +/// Configuration for a Tempo access key (keychain mode). +/// +/// When a Tempo wallet entry uses keychain mode (`wallet_address != key_address`), the signer +/// is an access key that signs on behalf of the root wallet. This struct carries the metadata +/// needed to construct the correct transaction. +#[derive(Debug, Clone)] +pub struct TempoAccessKeyConfig { + /// The root wallet address (the `from` address for transactions). + pub wallet_address: Address, + /// The access key's address (derived from the private key that actually signs). + pub key_address: Address, + /// Decoded key authorization for on-chain provisioning. + /// + /// When present, callers should check whether the key is already provisioned on-chain + /// (via the AccountKeychain precompile) before including this in a transaction. + pub key_authorization: Option, +} + +/// Result of looking up an address in Tempo's key store. +pub enum TempoLookup { + /// A direct (EOA) signer was found — `wallet_address == key_address`. + Direct(WalletSigner), + /// A keychain (access key) signer was found — `wallet_address != key_address`. + Keychain(WalletSigner, Box), + /// No matching entry was found. + NotFound, +} + +/// Returns the path to Tempo's keys file. +/// +/// Respects `TEMPO_HOME` env var, defaulting to `~/.tempo`. +fn keys_path() -> Option { + let base = std::env::var_os("TEMPO_HOME") + .map(PathBuf::from) + .or_else(|| dirs::home_dir().map(|h| h.join(".tempo")))?; + Some(base.join("wallet").join("keys.toml")) +} + +/// Decodes a hex-encoded, RLP-encoded [`SignedKeyAuthorization`]. +fn decode_key_authorization(hex_str: &str) -> Result { + let bytes = hex::decode(hex_str)?; + let auth = SignedKeyAuthorization::decode(&mut bytes.as_slice())?; + Ok(auth) +} + +/// Looks up a signer for the given address in Tempo's `keys.toml`. +/// +/// Returns [`TempoLookup::Direct`] if a direct-mode (EOA) key is found, +/// [`TempoLookup::Keychain`] if a keychain-mode access key is found, +/// or [`TempoLookup::NotFound`] if no entry matches. +pub fn lookup_signer(from: Address) -> Result { + let path = match keys_path() { + Some(p) if p.is_file() => p, + _ => return Ok(TempoLookup::NotFound), + }; + + let contents = std::fs::read_to_string(&path)?; + let file: KeysFile = toml::from_str(&contents)?; + + for entry in &file.keys { + if entry.wallet_address != from { + continue; + } + + let Some(key) = &entry.key else { + continue; + }; + + // Direct mode: wallet_address == key_address (or key_address is absent). + let is_direct = + entry.key_address.is_none() || entry.key_address == Some(entry.wallet_address); + + let signer = utils::create_private_key_signer(key)?; + + if is_direct { + return Ok(TempoLookup::Direct(signer)); + } + + // Keychain mode: the key is an access key signing on behalf of wallet_address. + let key_authorization = + entry.key_authorization.as_deref().map(decode_key_authorization).transpose()?; + + let config = TempoAccessKeyConfig { + wallet_address: entry.wallet_address, + // SAFETY: `is_direct` was false, so `key_address` is `Some` and != wallet_address + key_address: entry.key_address.unwrap(), + key_authorization, + }; + return Ok(TempoLookup::Keychain(signer, Box::new(config))); + } + + Ok(TempoLookup::NotFound) +} + +/// Signs a Tempo transaction request using an access key (keychain V2 mode). +/// +/// Bypasses the standard `EthereumWallet` signing path and instead: +/// 1. Builds the `TempoTransaction` from the request +/// 2. Computes the V2 keychain signing hash +/// 3. Signs with the access key +/// 4. Wraps in a `KeychainSignature` and encodes to EIP-2718 wire format +pub async fn sign_with_access_key( + tx_request: impl Into, + signer: &impl Signer, + wallet_address: Address, +) -> Result> { + let tx_request: TempoTransactionRequest = tx_request.into(); + let tempo_tx = tx_request + .build_aa() + .map_err(|e| eyre::eyre!("failed to build Tempo AA transaction: {e}"))?; + + let sig_hash = tempo_tx.signature_hash(); + let signing_hash = KeychainSignature::signing_hash(sig_hash, wallet_address); + let raw_sig = signer.sign_hash(&signing_hash).await?; + + let keychain_sig = + KeychainSignature::new(wallet_address, PrimitiveSignature::Secp256k1(raw_sig)); + let aa_signed = tempo_tx.into_signed(TempoSignature::Keychain(keychain_sig)); + + let mut buf = Vec::new(); + aa_signed.encode_2718(&mut buf); + + Ok(buf) +} diff --git a/crates/wallets/src/wallet_browser/app/assets/main.js b/crates/wallets/src/wallet_browser/app/assets/main.js index 060f0198f6c2c..6d89aa9e303f1 100644 --- a/crates/wallets/src/wallet_browser/app/assets/main.js +++ b/crates/wallets/src/wallet_browser/app/assets/main.js @@ -1,69 +1,71 @@ -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(e&&(t=e(e=0)),t),s=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),c=e=>{let n={};for(var r in e)t(n,r,{get:e[r],enumerable:!0});return n},l=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},u=(n,r,a)=>(a=n==null?{}:e(i(n)),l(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),d=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},ee=Object.prototype.hasOwnProperty;function te(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function ne(e,t){return te(e.type,t,e.props)}function re(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function ie(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var ae=/\/+/g;function oe(e,t){return typeof e==`object`&&e&&e.key!=null?ie(``+e.key):t.toString(36)}function se(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function ce(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,ce(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+oe(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(ae,`$&/`)+`/`),ce(o,r,i,``,function(e){return e})):o!=null&&(re(o)&&(o=ne(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(ae,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=d()})),p=s((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,re());else{var t=n(l);t!==null&&oe(x,t.startTime-e)}}var S=!1,C=-1,w=5,ee=-1;function te(){return g?!0:!(e.unstable_now()-eet&&te());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&oe(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?re():S=!1}}}var re;if(typeof y==`function`)re=function(){y(ne)};else if(typeof MessageChannel<`u`){var ie=new MessageChannel,ae=ie.port2;ie.port1.onmessage=ne,re=function(){ae.postMessage(null)}}else re=function(){_(ne,0)};function oe(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,oe(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,re()))),r},e.unstable_shouldYield=te,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),m=s(((e,t)=>{t.exports=p()})),h=s((e=>{var t=f();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=h()})),_=s((e=>{var t=m(),n=f(),r=g();function i(e){var t=`https://react.dev/errors/`+e;if(1me||(e.current=pe[me],pe[me]=null,me--)}function _e(e,t){me++,pe[me]=e.current,e.current=t}var ve=he(null),ye=he(null),be=he(null),xe=he(null);function Se(e,t){switch(_e(be,t),_e(ye,e),_e(ve,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Sf(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Sf(t),e=Cf(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}ge(ve),_e(ve,e)}function Ce(){ge(ve),ge(ye),ge(be)}function we(e){e.memoizedState!==null&&_e(xe,e);var t=ve.current,n=Cf(t,e.type);t!==n&&(_e(ye,e),_e(ve,n))}function Te(e){ye.current===e&&(ge(ve),ge(ye)),xe.current===e&&(ge(xe),jp._currentValue=fe)}var Ee,De;function Oe(e){if(Ee===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);Ee=t&&t[1]||``,De=-1()=>(e&&(t=e(e=0)),t),s=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),c=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},l=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},u=(n,r,a)=>(a=n==null?{}:e(i(n)),l(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),d=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function ee(){}var C={H:null,A:null,T:null,S:null},te=Object.prototype.hasOwnProperty;function ne(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function re(e,t){return ne(e.type,t,e.props)}function ie(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function ae(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var oe=/\/+/g;function se(e,t){return typeof e==`object`&&e&&e.key!=null?ae(``+e.key):t.toString(36)}function ce(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(ee,ee):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function le(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,le(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+se(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(oe,`$&/`)+`/`),le(o,r,i,``,function(e){return e})):o!=null&&(ie(o)&&(o=re(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(oe,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=d()})),p=s((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,ie());else{var t=n(l);t!==null&&se(x,t.startTime-e)}}var S=!1,ee=-1,C=5,te=-1;function ne(){return g?!0:!(e.unstable_now()-tet&&ne());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&se(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?ie():S=!1}}}var ie;if(typeof y==`function`)ie=function(){y(re)};else if(typeof MessageChannel<`u`){var ae=new MessageChannel,oe=ae.port2;ae.port1.onmessage=re,ie=function(){oe.postMessage(null)}}else ie=function(){_(re,0)};function se(t,n){ee=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(ee),ee=-1):h=!0,se(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,ie()))),r},e.unstable_shouldYield=ne,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),m=s(((e,t)=>{t.exports=p()})),h=s((e=>{var t=f();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=h()})),_=s((e=>{var t=m(),n=f(),r=g();function i(e){var t=`https://react.dev/errors/`+e;if(1he||(e.current=me[he],me[he]=null,he--)}function ve(e,t){he++,me[he]=e.current,e.current=t}var ye=ge(null),be=ge(null),xe=ge(null),Se=ge(null);function Ce(e,t){switch(ve(xe,t),ve(be,e),ve(ye,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?sf(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=sf(t),e=cf(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}_e(ye),ve(ye,e)}function we(){_e(ye),_e(be),_e(xe)}function Te(e){e.memoizedState!==null&&ve(Se,e);var t=ye.current,n=cf(t,e.type);t!==n&&(ve(be,e),ve(ye,n))}function Ee(e){be.current===e&&(_e(ye),_e(be)),Se.current===e&&(_e(Se),hp._currentValue=pe)}var De,Oe;function ke(e){if(De===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);De=t&&t[1]||``,Oe=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{ke=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?Oe(n):``}function je(e,t){switch(e.tag){case 26:case 27:case 5:return Oe(e.type);case 16:return Oe(`Lazy`);case 13:return e.child!==t&&t!==null?Oe(`Suspense Fallback`):Oe(`Suspense`);case 19:return Oe(`SuspenseList`);case 0:case 15:return Ae(e.type,!1);case 11:return Ae(e.type.render,!1);case 1:return Ae(e.type,!0);case 31:return Oe(`Activity`);default:return``}}function Me(e){try{var t=``,n=null;do t+=je(e,n),n=e,e=e.return;while(e);return t}catch(e){return` +`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{Ae=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?ke(n):``}function Me(e,t){switch(e.tag){case 26:case 27:case 5:return ke(e.type);case 16:return ke(`Lazy`);case 13:return e.child!==t&&t!==null?ke(`Suspense Fallback`):ke(`Suspense`);case 19:return ke(`SuspenseList`);case 0:case 15:return je(e.type,!1);case 11:return je(e.type.render,!1);case 1:return je(e.type,!0);case 31:return ke(`Activity`);default:return``}}function Ne(e){try{var t=``,n=null;do t+=Me(e,n),n=e,e=e.return;while(e);return t}catch(e){return` Error generating stack: `+e.message+` -`+e.stack}}var Ne=Object.prototype.hasOwnProperty,Pe=t.unstable_scheduleCallback,Fe=t.unstable_cancelCallback,Ie=t.unstable_shouldYield,Le=t.unstable_requestPaint,Re=t.unstable_now,ze=t.unstable_getCurrentPriorityLevel,Be=t.unstable_ImmediatePriority,Ve=t.unstable_UserBlockingPriority,He=t.unstable_NormalPriority,Ue=t.unstable_LowPriority,We=t.unstable_IdlePriority,Ge=t.log,Ke=t.unstable_setDisableYieldValue,qe=null,Je=null;function Ye(e){if(typeof Ge==`function`&&Ke(e),Je&&typeof Je.setStrictMode==`function`)try{Je.setStrictMode(qe,e)}catch{}}var Xe=Math.clz32?Math.clz32:$e,Ze=Math.log,Qe=Math.LN2;function $e(e){return e>>>=0,e===0?32:31-(Ze(e)/Qe|0)|0}var et=256,tt=262144,nt=4194304;function rt(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function it(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=rt(n))):i=rt(o):i=rt(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=rt(n))):i=rt(o)):i=rt(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function at(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function ot(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function st(){var e=nt;return nt<<=1,!(nt&62914560)&&(nt=4194304),e}function ct(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function lt(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function ut(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),bn=!1;if(yn)try{var xn={};Object.defineProperty(xn,`passive`,{get:function(){bn=!0}}),window.addEventListener(`test`,xn,xn),window.removeEventListener(`test`,xn,xn)}catch{bn=!1}var Sn=null,Cn=null,wn=null;function Tn(){if(wn)return wn;var e,t=Cn,n=t.length,r,i=`value`in Sn?Sn.value:Sn.textContent,a=i.length;for(e=0;e=fr),hr=` `,gr=!1;function _r(e,t){switch(e){case`keyup`:return ur.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function vr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var yr=!1;function br(e,t){switch(e){case`compositionend`:return vr(t);case`keypress`:return t.which===32?(gr=!0,hr):null;case`textInput`:return e=t.data,e===hr&&gr?null:e;default:return null}}function xr(e,t){if(yr)return e===`compositionend`||!dr&&_r(e,t)?(e=Tn(),wn=Cn=Sn=null,yr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Ur(n)}}function Gr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Gr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Kr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Kt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Kt(e.document)}return t}function qr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Jr=yn&&`documentMode`in document&&11>=document.documentMode,Yr=null,Xr=null,Zr=null,Qr=!1;function $r(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Qr||Yr==null||Yr!==Kt(r)||(r=Yr,`selectionStart`in r&&qr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Zr&&Hr(Zr,r)||(Zr=r,r=af(Xr,`onSelect`),0>=o,i-=o,Gi=1<<32-Xe(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),ea&&qi(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),ea&&qi(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return ea&&qi(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),ea&&qi(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===re&&Ja(l)===r.type){n(e,r.sibling),c=a(r,o.props),to(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=Mi(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=ji(o.type,o.key,o.props,null,e.mode,c),to(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=Fi(o,e.mode,c),c.return=e,e=c}return s(e);case re:return o=Ja(o),b(e,r,o,c)}if(ue(o))return h(e,r,o,c);if(se(o)){if(l=se(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,eo(o),c);if(o.$$typeof===C)return b(e,r,Sa(e,o),c);no(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=Ni(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{$a=0;var i=b(e,t,n,r);return Qa=null,i}catch(t){if(t===Ha||t===Wa)throw t;var a=Di(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var io=ro(!0),ao=ro(!1),oo=!1;function so(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function co(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function lo(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function uo(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,fu&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=wi(e),Ci(e,null,n),t}return bi(e,r,t,n),wi(e)}function fo(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ft(e,n)}}function po(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var mo=!1;function ho(){if(mo){var e=Na;if(e!==null)throw e}}function go(e,t,n,r){mo=!1;var i=e.updateQueue;oo=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,m=f!==s.lane;if(m?(N&f)===f:(r&f)===f){f!==0&&f===Ma&&(mo=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=p({},d,f);break a;case 2:oo=!0}}f=s.callback,f!==null&&(e.flags|=64,m&&(e.flags|=8192),m=i.callbacks,m===null?i.callbacks=[f]:m.push(f))}else m={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=m,c=d):u=u.next=m,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;m=s,s=m.next,m.next=null,i.lastBaseUpdate=m,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),xu|=o,e.lanes=o,e.memoizedState=d}}function _o(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function vo(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=T.T,s={};T.T=s,ac(e,!1,t,n);try{var c=i(),l=T.S;if(l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`){var u=Ia(c,r);ic(e,t,u,Uu(e))}else ic(e,t,r,Uu(e))}catch(n){ic(e,t,{then:function(){},status:`rejected`,reason:n},Uu())}finally{de.p=a,o!==null&&s.types!==null&&(o.types=s.types),T.T=o}}function Js(){}function Ys(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Xs(e).queue;qs(e,a,t,fe,n===null?Js:function(){return Zs(e),n(r)})}function Xs(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:fe,baseState:fe,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:os,lastRenderedState:fe},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:os,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Zs(e){var t=Xs(e);t.next===null&&(t=e.alternate.memoizedState),ic(e,t.next.queue,{},Uu())}function Qs(){return xa(jp)}function $s(){return $o().memoizedState}function ec(){return $o().memoizedState}function tc(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=Uu();e=lo(n);var r=uo(t,e,n);r!==null&&(Gu(r,t,n),fo(r,t,n)),t={cache:Oa()},e.payload=t;return}t=t.return}}function nc(e,t,n){var r=Uu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},oc(e)?sc(t,n):(n=xi(e,t,n,r),n!==null&&(Gu(n,e,r),cc(n,t,r)))}function rc(e,t,n){var r=Uu();ic(e,t,n,r)}function ic(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(oc(e))sc(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,Vr(s,o))return bi(e,t,i,0),pu===null&&yi(),!1}catch{}if(n=xi(e,t,i,r),n!==null)return Gu(n,e,r),cc(n,t,r),!0}return!1}function ac(e,t,n,r){if(r={lane:2,revertLane:Hd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},oc(e)){if(t)throw Error(i(479))}else t=xi(e,n,r,2),t!==null&&Gu(t,e,2)}function oc(e){var t=e.alternate;return e===j||t!==null&&t===j}function sc(e,t){Lo=Io=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function cc(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ft(e,n)}}var lc={readContext:xa,use:ns,useCallback:Uo,useContext:Uo,useEffect:Uo,useImperativeHandle:Uo,useLayoutEffect:Uo,useInsertionEffect:Uo,useMemo:Uo,useReducer:Uo,useRef:Uo,useState:Uo,useDebugValue:Uo,useDeferredValue:Uo,useTransition:Uo,useSyncExternalStore:Uo,useId:Uo,useHostTransitionStatus:Uo,useFormState:Uo,useActionState:Uo,useOptimistic:Uo,useMemoCache:Uo,useCacheRefresh:Uo};lc.useEffectEvent=Uo;var uc={readContext:xa,use:ns,useCallback:function(e,t){return Qo().memoizedState=[e,t===void 0?null:t],e},useContext:xa,useEffect:Ps,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),Ms(4194308,4,Bs.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ms(4194308,4,e,t)},useInsertionEffect:function(e,t){Ms(4,2,e,t)},useMemo:function(e,t){var n=Qo();t=t===void 0?null:t;var r=e();if(Ro){Ye(!0);try{e()}finally{Ye(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Qo();if(n!==void 0){var i=n(t);if(Ro){Ye(!0);try{n(t)}finally{Ye(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=nc.bind(null,j,e),[r.memoizedState,e]},useRef:function(e){var t=Qo();return e={current:e},t.memoizedState=e},useState:function(e){e=gs(e);var t=e.queue,n=rc.bind(null,j,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:Hs,useDeferredValue:function(e,t){var n=Qo();return Gs(n,e,t)},useTransition:function(){var e=gs(!1);return e=qs.bind(null,j,e.queue,!0,!1),Qo().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=j,a=Qo();if(ea){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),pu===null)throw Error(i(349));N&127||ds(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,Ps(ps.bind(null,r,o,e),[e]),r.flags|=2048,As(9,{destroy:void 0},fs.bind(null,r,o,n,t),null),n},useId:function(){var e=Qo(),t=pu.identifierPrefix;if(ea){var n=Ki,r=Gi;n=(r&~(1<<32-Xe(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=zo++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[yt]=t,o[bt]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(hf(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&il(t)}}return ll(t),al(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&il(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=be.current,sa(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Qi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[yt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||ff(e.nodeValue,n)),e||ia(t,!0)}else e=xf(e).createTextNode(r),e[yt]=t,t.stateNode=e}return ll(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=sa(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[yt]=t}else ca(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;ll(t),e=!1}else n=la(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(Ao(t),t):(Ao(t),null);if(t.flags&128)throw Error(i(558))}return ll(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=sa(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[yt]=t}else ca(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;ll(t),a=!1}else a=la(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(Ao(t),t):(Ao(t),null)}return Ao(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),sl(t,t.updateQueue),ll(t),null);case 4:return Ce(),e===null&&ef(t.stateNode.containerInfo),ll(t),null;case 10:return ha(t.type),ll(t),null;case 19:if(ge(jo),r=t.memoizedState,r===null)return ll(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)cl(r,!1);else{if(bu!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=Mo(e),o!==null){for(t.flags|=128,cl(r,!1),e=o.updateQueue,t.updateQueue=e,sl(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)Ai(n,e),n=n.sibling;return _e(jo,jo.current&1|2),ea&&qi(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&Re()>ju&&(t.flags|=128,a=!0,cl(r,!1),t.lanes=4194304)}else{if(!a)if(e=Mo(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,sl(t,e),cl(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!ea)return ll(t),null}else 2*Re()-r.renderingStartTime>ju&&n!==536870912&&(t.flags|=128,a=!0,cl(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(ll(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=Re(),e.sibling=null,n=jo.current,_e(jo,a?n&1|2:n&1),ea&&qi(t,r.treeForkCount),e);case 22:case 23:return Ao(t),Co(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(ll(t),t.subtreeFlags&6&&(t.flags|=8192)):ll(t),n=t.updateQueue,n!==null&&sl(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&ge(Ra),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),ha(Da),ll(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function dl(e,t){switch(Xi(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return ha(Da),Ce(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Te(t),null;case 31:if(t.memoizedState!==null){if(Ao(t),t.alternate===null)throw Error(i(340));ca()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Ao(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));ca()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return ge(jo),null;case 4:return Ce(),null;case 10:return ha(t.type),null;case 22:case 23:return Ao(t),Co(),e!==null&&ge(Ra),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return ha(Da),null;case 25:return null;default:return null}}function fl(e,t){switch(Xi(t),t.tag){case 3:ha(Da),Ce();break;case 26:case 27:case 5:Te(t);break;case 4:Ce();break;case 31:t.memoizedState!==null&&Ao(t);break;case 13:Ao(t);break;case 19:ge(jo);break;case 10:ha(t.type);break;case 22:case 23:Ao(t),Co(),e!==null&&ge(Ra);break;case 24:ha(Da)}}function pl(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){xd(t,t.return,e)}}function ml(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){xd(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){xd(t,t.return,e)}}function hl(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{vo(t,n)}catch(t){xd(e,e.return,t)}}}function gl(e,t,n){n.props=_c(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){xd(e,t,n)}}function _l(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){xd(e,t,n)}}function vl(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){xd(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){xd(e,t,n)}else n.current=null}function yl(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){xd(e,e.return,t)}}function bl(e,t,n){try{var r=e.stateNode;gf(r,e.type,n,t),r[bt]=t}catch(t){xd(e,e.return,t)}}function xl(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Mf(e.type)||e.tag===4}function Sl(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||xl(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Mf(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Cl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=un));else if(r!==4&&(r===27&&Mf(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Cl(e,t,n),e=e.sibling;e!==null;)Cl(e,t,n),e=e.sibling}function wl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&Mf(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(wl(e,t,n),e=e.sibling;e!==null;)wl(e,t,n),e=e.sibling}function Tl(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);hf(t,r,n),t[yt]=e,t[bt]=n}catch(t){xd(e,e.return,t)}}var El=!1,Dl=!1,Ol=!1,kl=typeof WeakSet==`function`?WeakSet:Set,Al=null;function jl(e,t){if(e=e.containerInfo,yf=Bp,e=Kr(e),qr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(bf={focusedElem:e,selectionRange:n},Bp=!1,Al=t;Al!==null;)if(t=Al,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,Al=e;else for(;Al!==null;){switch(t=Al,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),hf(o,r,n),o[yt]=e,jt(o),r=o;break a;case`link`:var s=bp(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Wr(s,h),v=Wr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,T.T=null,n=zu,zu=null;var o=Fu,s=Lu;if(Pu=0,Iu=Fu=null,Lu=0,fu&6)throw Error(i(331));var c=fu;if(fu|=4,su(o.current),$l(o,o.current,s,n),fu=c,Fd(0,!1),Je&&typeof Je.onPostCommitFiberRoot==`function`)try{Je.onPostCommitFiberRoot(qe,o)}catch{}return!0}finally{de.p=a,T.T=r,_d(e,t)}}function bd(e,t,n){t=Li(n,t),t=Cc(e.stateNode,t,2),e=uo(e,t,2),e!==null&&(lt(e,2),Pd(e))}function xd(e,t,n){if(e.tag===3)bd(e,e,n);else for(;t!==null;){if(t.tag===3){bd(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(Nu===null||!Nu.has(r))){e=Li(n,e),n=wc(2),r=uo(t,n,2),r!==null&&(Tc(n,r,t,e),lt(r,2),Pd(r));break}}t=t.return}}function Sd(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new du;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(vu=!0,i.add(n),e=Cd.bind(null,e,t,n),t.then(e,e))}function Cd(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,pu===e&&(N&n)===n&&(bu===4||bu===3&&(N&62914560)===N&&300>Re()-ku?!(fu&2)&&Qu(e,0):Cu|=n,Tu===N&&(Tu=0)),Pd(e)}function wd(e,t){t===0&&(t=st()),e=Si(e,t),e!==null&&(lt(e,t),Pd(e))}function Td(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),wd(e,n)}function Ed(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),wd(e,n)}function Dd(e,t){return Pe(e,t)}var Od=null,kd=null,Ad=!1,jd=!1,Md=!1,Nd=0;function Pd(e){e!==kd&&e.next===null&&(kd===null?Od=kd=e:kd=kd.next=e),jd=!0,Ad||(Ad=!0,Vd())}function Fd(e,t){if(!Md&&jd){Md=!0;do for(var n=!1,r=Od;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Xe(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,Bd(r,a))}else a=N,a=it(r,r===pu?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||at(r,a)||(n=!0,Bd(r,a));r=r.next}while(n);Md=!1}}function Id(){Ld()}function Ld(){jd=Ad=!1;var e=0;Nd!==0&&Ef()&&(e=Nd);for(var t=Re(),n=null,r=Od;r!==null;){var i=r.next,a=Rd(r,t);a===0?(r.next=null,n===null?Od=i:n.next=i,i===null&&(kd=n)):(n=r,(e!==0||a&3)&&(jd=!0)),r=i}Pu!==0&&Pu!==5||Fd(e,!1),Nd!==0&&(Nd=0)}function Rd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&_f(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function ep(e,t,n){var r=$f;if(r&&typeof t==`string`&&t){var i=Jt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),Jf.has(i)||(Jf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),hf(t,`link`,e),jt(t),r.head.appendChild(t)))}}function tp(e){Xf.D(e),ep(`dns-prefetch`,e,null)}function np(e,t){Xf.C(e,t),ep(`preconnect`,e,t)}function rp(e,t,n){Xf.L(e,t,n);var r=$f;if(r&&e&&t){var i=`link[rel="preload"][as="`+Jt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Jt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Jt(n.imageSizes)+`"]`)):i+=`[href="`+Jt(e)+`"]`;var a=i;switch(t){case`style`:a=lp(e);break;case`script`:a=pp(e)}qf.has(a)||(e=p({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),qf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(up(a))||t===`script`&&r.querySelector(mp(a))||(t=r.createElement(`link`),hf(t,`link`,e),jt(t),r.head.appendChild(t)))}}function ip(e,t){Xf.m(e,t);var n=$f;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Jt(r)+`"][href="`+Jt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=pp(e)}if(!qf.has(a)&&(e=p({rel:`modulepreload`,href:e},t),qf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(mp(a)))return}r=n.createElement(`link`),hf(r,`link`,e),jt(r),n.head.appendChild(r)}}}function ap(e,t,n){Xf.S(e,t,n);var r=$f;if(r&&e){var i=At(r).hoistableStyles,a=lp(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(up(a)))s.loading=5;else{e=p({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=qf.get(a))&&_p(e,n);var c=o=r.createElement(`link`);jt(c),hf(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,gp(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function op(e,t){Xf.X(e,t);var n=$f;if(n&&e){var r=At(n).hoistableScripts,i=pp(e),a=r.get(i);a||(a=n.querySelector(mp(i)),a||(e=p({src:e,async:!0},t),(t=qf.get(i))&&vp(e,t),a=n.createElement(`script`),jt(a),hf(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function sp(e,t){Xf.M(e,t);var n=$f;if(n&&e){var r=At(n).hoistableScripts,i=pp(e),a=r.get(i);a||(a=n.querySelector(mp(i)),a||(e=p({src:e,async:!0,type:`module`},t),(t=qf.get(i))&&vp(e,t),a=n.createElement(`script`),jt(a),hf(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function cp(e,t,n,r){var a=(a=be.current)?Yf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=lp(n.href),n=At(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=lp(n.href);var o=At(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(up(e)))&&!o._p&&(s.instance=o,s.state.loading=5),qf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},qf.set(e,n),o||fp(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=pp(n),n=At(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function lp(e){return`href="`+Jt(e)+`"`}function up(e){return`link[rel="stylesheet"][`+e+`]`}function dp(e){return p({},e,{"data-precedence":e.precedence,precedence:null})}function fp(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),hf(t,`link`,n),jt(t),e.head.appendChild(t))}function pp(e){return`[src="`+Jt(e)+`"]`}function mp(e){return`script[async]`+e}function hp(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Jt(n.href)+`"]`);if(r)return t.instance=r,jt(r),r;var a=p({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),jt(r),hf(r,`style`,a),gp(r,n.precedence,e),t.instance=r;case`stylesheet`:a=lp(n.href);var o=e.querySelector(up(a));if(o)return t.state.loading|=4,t.instance=o,jt(o),o;r=dp(n),(a=qf.get(a))&&_p(r,a),o=(e.ownerDocument||e).createElement(`link`),jt(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),hf(o,`link`,r),t.state.loading|=4,gp(o,n.precedence,e),t.instance=o;case`script`:return o=pp(n.src),(a=e.querySelector(mp(o)))?(t.instance=a,jt(a),a):(r=n,(a=qf.get(o))&&(r=p({},n),vp(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),jt(a),hf(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,gp(r,n.precedence,e));return t.instance}function gp(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Sp(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function Cp(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function wp(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=lp(r.href),a=t.querySelector(up(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Dp.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,jt(a);return}a=t.ownerDocument||t,r=dp(r),(i=qf.get(i))&&_p(r,i),a=a.createElement(`link`),jt(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),hf(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Dp.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var Tp=0;function Ep(e,t){return e.stylesheets&&e.count===0&&kp(e,e.stylesheets),0Tp?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Dp(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)kp(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Op=null;function kp(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Op=new Map,t.forEach(Ap,e),Op=null,Dp.call(e))}function Ap(e,t){if(!(t.state.loading&4)){var n=Op.get(e);if(n)var r=n.get(null);else{n=new Map,Op.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=_()}))()),y=u(f()),b,x=o((()=>{b=`1.1.0`})),S,C=o((()=>{x(),S=class e extends Error{constructor(t,n={}){let r=n.cause instanceof e?n.cause.details:n.cause?.message?n.cause.message:n.details,i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=[t||`An error occurred.`,``,...n.metaMessages?[...n.metaMessages,``]:[],...i?[`Docs: https://abitype.dev${i}`]:[],...r?[`Details: ${r}`]:[],`Version: abitype@${b}`].join(` -`);super(a),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`metaMessages`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiTypeError`}),n.cause&&(this.cause=n.cause),this.details=r,this.docsPath=i,this.metaMessages=n.metaMessages,this.shortMessage=t}}}));function w(e,t){return e.exec(t)?.groups}var ee,te,ne,re=o((()=>{ee=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,te=/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/,ne=/^\(.+?\).*?$/}));function ie(e){let t=e.type;if(ae.test(e.type)&&`components`in e){t=`(`;let n=e.components.length;for(let r=0;r{re(),ae=/^tuple(?(\[(\d*)\])*)$/}));function se(e){let t=``,n=e.length;for(let r=0;r{oe()}));function le(e){return e.type===`function`?`function ${e.name}(${se(e.inputs)})${e.stateMutability&&e.stateMutability!==`nonpayable`?` ${e.stateMutability}`:``}${e.outputs?.length?` returns (${se(e.outputs)})`:``}`:e.type===`event`?`event ${e.name}(${se(e.inputs)})`:e.type===`error`?`error ${e.name}(${se(e.inputs)})`:e.type===`constructor`?`constructor(${se(e.inputs)})${e.stateMutability===`payable`?` payable`:``}`:e.type===`fallback`?`fallback() external${e.stateMutability===`payable`?` payable`:``}`:`receive() external payable`}var ue=o((()=>{ce()}));function T(e){return Ce.test(e)}function de(e){return w(Ce,e)}function fe(e){return we.test(e)}function pe(e){return w(we,e)}function me(e){return Te.test(e)}function he(e){return w(Te,e)}function ge(e){return Ee.test(e)}function _e(e){return w(Ee,e)}function ve(e){return De.test(e)}function ye(e){return w(De,e)}function be(e){return Oe.test(e)}function xe(e){return w(Oe,e)}function Se(e){return ke.test(e)}var Ce,we,Te,Ee,De,Oe,ke,Ae,je,Me,Ne=o((()=>{re(),Ce=/^error (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)$/,we=/^event (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)$/,Te=/^function (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)(?: (?external|public{1}))?(?: (?pure|view|nonpayable|payable{1}))?(?: returns\s?\((?.*?)\))?$/,Ee=/^struct (?[a-zA-Z$_][a-zA-Z0-9$_]*) \{(?.*?)\}$/,De=/^constructor\((?.*?)\)(?:\s(?payable{1}))?$/,Oe=/^fallback\(\) external(?:\s(?payable{1}))?$/,ke=/^receive\(\) external payable$/,Ae=new Set([`memory`,`indexed`,`storage`,`calldata`]),je=new Set([`indexed`]),Me=new Set([`calldata`,`memory`,`storage`])})),Pe,Fe,Ie,Le=o((()=>{C(),Pe=class extends S{constructor({signature:e}){super(`Failed to parse ABI item.`,{details:`parseAbiItem(${JSON.stringify(e,null,2)})`,docsPath:`/api/human#parseabiitem-1`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiItemError`})}},Fe=class extends S{constructor({type:e}){super(`Unknown type.`,{metaMessages:[`Type "${e}" is not a valid ABI type. Perhaps you forgot to include a struct signature?`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownTypeError`})}},Ie=class extends S{constructor({type:e}){super(`Unknown type.`,{metaMessages:[`Type "${e}" is not a valid ABI type.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownSolidityTypeError`})}}})),Re,ze,Be,Ve,He,Ue,We=o((()=>{C(),Re=class extends S{constructor({params:e}){super(`Failed to parse ABI parameters.`,{details:`parseAbiParameters(${JSON.stringify(e,null,2)})`,docsPath:`/api/human#parseabiparameters-1`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiParametersError`})}},ze=class extends S{constructor({param:e}){super(`Invalid ABI parameter.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidParameterError`})}},Be=class extends S{constructor({param:e,name:t}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`"${t}" is a protected Solidity keyword. More info: https://docs.soliditylang.org/en/latest/cheatsheet.html`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SolidityProtectedKeywordError`})}},Ve=class extends S{constructor({param:e,type:t,modifier:n}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`Modifier "${n}" not allowed${t?` in "${t}" type`:``}.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidModifierError`})}},He=class extends S{constructor({param:e,type:t,modifier:n}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`Modifier "${n}" not allowed${t?` in "${t}" type`:``}.`,`Data location can only be specified for array, struct, or mapping types, but "${n}" was given.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidFunctionModifierError`})}},Ue=class extends S{constructor({abiParameter:e}){super(`Invalid ABI parameter.`,{details:JSON.stringify(e,null,2),metaMessages:[`ABI parameter type is invalid.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiTypeParameterError`})}}})),Ge,Ke,qe,Je=o((()=>{C(),Ge=class extends S{constructor({signature:e,type:t}){super(`Invalid ${t} signature.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidSignatureError`})}},Ke=class extends S{constructor({signature:e}){super(`Unknown signature.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownSignatureError`})}},qe=class extends S{constructor({signature:e}){super(`Invalid struct signature.`,{details:e,metaMessages:[`No properties exist.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidStructSignatureError`})}}})),Ye,Xe=o((()=>{C(),Ye=class extends S{constructor({type:e}){super(`Circular reference detected.`,{metaMessages:[`Struct "${e}" is a circular reference.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`CircularReferenceError`})}}})),Ze,Qe=o((()=>{C(),Ze=class extends S{constructor({current:e,depth:t}){super(`Unbalanced parentheses.`,{metaMessages:[`"${e.trim()}" has too many ${t>0?`opening`:`closing`} parentheses.`],details:`Depth "${t}"`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidParenthesisError`})}}}));function $e(e,t,n){let r=``;if(n)for(let e of Object.entries(n)){if(!e)continue;let t=``;for(let n of e[1])t+=`[${n.type}${n.name?`:${n.name}`:``}]`;r+=`(${e[0]}{${t}})`}return t?`${t}:${e}${r}`:e}var et,tt=o((()=>{et=new Map([[`address`,{type:`address`}],[`bool`,{type:`bool`}],[`bytes`,{type:`bytes`}],[`bytes32`,{type:`bytes32`}],[`int`,{type:`int256`}],[`int256`,{type:`int256`}],[`string`,{type:`string`}],[`uint`,{type:`uint256`}],[`uint8`,{type:`uint8`}],[`uint16`,{type:`uint16`}],[`uint24`,{type:`uint24`}],[`uint32`,{type:`uint32`}],[`uint64`,{type:`uint64`}],[`uint96`,{type:`uint96`}],[`uint112`,{type:`uint112`}],[`uint160`,{type:`uint160`}],[`uint192`,{type:`uint192`}],[`uint256`,{type:`uint256`}],[`address owner`,{type:`address`,name:`owner`}],[`address to`,{type:`address`,name:`to`}],[`bool approved`,{type:`bool`,name:`approved`}],[`bytes _data`,{type:`bytes`,name:`_data`}],[`bytes data`,{type:`bytes`,name:`data`}],[`bytes signature`,{type:`bytes`,name:`signature`}],[`bytes32 hash`,{type:`bytes32`,name:`hash`}],[`bytes32 r`,{type:`bytes32`,name:`r`}],[`bytes32 root`,{type:`bytes32`,name:`root`}],[`bytes32 s`,{type:`bytes32`,name:`s`}],[`string name`,{type:`string`,name:`name`}],[`string symbol`,{type:`string`,name:`symbol`}],[`string tokenURI`,{type:`string`,name:`tokenURI`}],[`uint tokenId`,{type:`uint256`,name:`tokenId`}],[`uint8 v`,{type:`uint8`,name:`v`}],[`uint256 balance`,{type:`uint256`,name:`balance`}],[`uint256 tokenId`,{type:`uint256`,name:`tokenId`}],[`uint256 value`,{type:`uint256`,name:`value`}],[`event:address indexed from`,{type:`address`,name:`from`,indexed:!0}],[`event:address indexed to`,{type:`address`,name:`to`,indexed:!0}],[`event:uint indexed tokenId`,{type:`uint256`,name:`tokenId`,indexed:!0}],[`event:uint256 indexed tokenId`,{type:`uint256`,name:`tokenId`,indexed:!0}]])}));function nt(e,t={}){if(me(e))return rt(e,t);if(fe(e))return it(e,t);if(T(e))return at(e,t);if(ve(e))return ot(e,t);if(be(e))return st(e);if(Se(e))return{type:`receive`,stateMutability:`payable`};throw new Ke({signature:e})}function rt(e,t={}){let n=he(e);if(!n)throw new Ge({signature:e,type:`function`});let r=lt(n.parameters),i=[],a=r.length;for(let e=0;e{re(),Le(),We(),Je(),Qe(),tt(),Ne(),pt=/^(?[a-zA-Z$_][a-zA-Z0-9$_]*(?:\spayable)?)(?(?:\[\d*?\])+?)?(?:\s(?calldata|indexed|memory|storage{1}))?(?:\s(?[a-zA-Z$_][a-zA-Z0-9$_]*))?$/,mt=/^\((?.+?)\)(?(?:\[\d*?\])+?)?(?:\s(?calldata|indexed|memory|storage{1}))?(?:\s(?[a-zA-Z$_][a-zA-Z0-9$_]*))?$/,ht=/^u?int$/,gt=/^(?:after|alias|anonymous|apply|auto|byte|calldata|case|catch|constant|copyof|default|defined|error|event|external|false|final|function|immutable|implements|in|indexed|inline|internal|let|mapping|match|memory|mutable|null|of|override|partial|private|promise|public|pure|reference|relocatable|return|returns|sizeof|static|storage|struct|super|supports|switch|this|true|try|typedef|typeof|var|view|virtual)$/}));function vt(e){let t={},n=e.length;for(let r=0;r{re(),Le(),We(),Je(),Xe(),Ne(),_t(),bt=/^(?[a-zA-Z$_][a-zA-Z0-9$_]*)(?(?:\[\d*?\])+?)?$/}));function St(e){let t=vt(e),n=[],r=e.length;for(let i=0;i{Ne(),xt(),_t()}));function wt(e){let t;if(typeof e==`string`)t=nt(e);else{let n=vt(e),r=e.length;for(let i=0;i{Le(),Ne(),xt(),_t()}));function Et(e){let t=[];if(typeof e==`string`){let n=lt(e),r=n.length;for(let e=0;e{We(),Ne(),xt(),_t()})),Ot=o((()=>{ue(),ce(),Ct(),Tt(),Dt()}));function E(e,t,n){let r=e[t.name];if(typeof r==`function`)return r;let i=e[n];return typeof i==`function`?i:n=>t(e,n)}function kt(e,{includeName:t=!1}={}){if(e.type!==`function`&&e.type!==`event`&&e.type!==`error`)throw new on(e.type);return`${e.name}(${At(e.inputs,{includeName:t})})`}function At(e,{includeName:t=!1}={}){return e?e.map(e=>jt(e,{includeName:t})).join(t?`, `:`,`):``}function jt(e,{includeName:t}){return e.type.startsWith(`tuple`)?`(${At(e.components,{includeName:t})})${e.type.slice(5)}`:e.type+(t&&e.name?` ${e.name}`:``)}var Mt=o((()=>{sn()}));function Nt(e,{strict:t=!0}={}){return!e||typeof e!=`string`?!1:t?/^0x[0-9a-fA-F]*$/.test(e):e.startsWith(`0x`)}var Pt=o((()=>{}));function Ft(e){return Nt(e,{strict:!1})?Math.ceil((e.length-2)/2):e.length}var It=o((()=>{Pt()})),Lt,Rt=o((()=>{Lt=`2.38.3`}));function zt(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause!==void 0?zt(e.cause,t):t?null:e}var Bt,D,O=o((()=>{Rt(),Bt={getDocsUrl:({docsBaseUrl:e,docsPath:t=``,docsSlug:n})=>t?`${e??`https://viem.sh`}${t}${n?`#${n}`:``}`:void 0,version:`viem@${Lt}`},D=class e extends Error{constructor(t,n={}){let r=(()=>n.cause instanceof e?n.cause.details:n.cause?.message?n.cause.message:n.details)(),i=(()=>n.cause instanceof e&&n.cause.docsPath||n.docsPath)(),a=Bt.getDocsUrl?.({...n,docsPath:i}),o=[t||`An error occurred.`,``,...n.metaMessages?[...n.metaMessages,``]:[],...a?[`Docs: ${a}`]:[],...r?[`Details: ${r}`]:[],...Bt.version?[`Version: ${Bt.version}`]:[]].join(` -`);super(o,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`metaMessages`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.details=r,this.docsPath=i,this.metaMessages=n.metaMessages,this.name=n.name??this.name,this.shortMessage=t,this.version=Lt}walk(e){return zt(this,e)}}})),Vt,Ht,Ut,Wt,Gt,Kt,qt,Jt,Yt,Xt,Zt,Qt,$t,en,tn,nn,rn,an,on,sn=o((()=>{Mt(),It(),O(),Vt=class extends D{constructor({docsPath:e}){super([`A constructor was not found on the ABI.`,`Make sure you are using the correct ABI and that the constructor exists on it.`].join(` -`),{docsPath:e,name:`AbiConstructorNotFoundError`})}},Ht=class extends D{constructor({docsPath:e}){super(["Constructor arguments were provided (`args`), but a constructor parameters (`inputs`) were not found on the ABI.","Make sure you are using the correct ABI, and that the `inputs` attribute on the constructor exists."].join(` -`),{docsPath:e,name:`AbiConstructorParamsNotFoundError`})}},Ut=class extends D{constructor({data:e,params:t,size:n}){super([`Data size of ${n} bytes is too small for given parameters.`].join(` -`),{metaMessages:[`Params: (${At(t,{includeName:!0})})`,`Data: ${e} (${n} bytes)`],name:`AbiDecodingDataSizeTooSmallError`}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`params`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`size`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=e,this.params=t,this.size=n}},Wt=class extends D{constructor(){super(`Cannot decode zero data ("0x") with ABI parameters.`,{name:`AbiDecodingZeroDataError`})}},Gt=class extends D{constructor({expectedLength:e,givenLength:t,type:n}){super([`ABI encoding array length mismatch for type ${n}.`,`Expected length: ${e}`,`Given length: ${t}`].join(` -`),{name:`AbiEncodingArrayLengthMismatchError`})}},Kt=class extends D{constructor({expectedSize:e,value:t}){super(`Size of bytes "${t}" (bytes${Ft(t)}) does not match expected size (bytes${e}).`,{name:`AbiEncodingBytesSizeMismatchError`})}},qt=class extends D{constructor({expectedLength:e,givenLength:t}){super([`ABI encoding params/values length mismatch.`,`Expected length (params): ${e}`,`Given length (values): ${t}`].join(` -`),{name:`AbiEncodingLengthMismatchError`})}},Jt=class extends D{constructor(e,{docsPath:t}){super([`Arguments (\`args\`) were provided to "${e}", but "${e}" on the ABI does not contain any parameters (\`inputs\`).`,`Cannot encode error result without knowing what the parameter types are.`,`Make sure you are using the correct ABI and that the inputs exist on it.`].join(` -`),{docsPath:t,name:`AbiErrorInputsNotFoundError`})}},Yt=class extends D{constructor(e,{docsPath:t}={}){super([`Error ${e?`"${e}" `:``}not found on ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`].join(` -`),{docsPath:t,name:`AbiErrorNotFoundError`})}},Xt=class extends D{constructor(e,{docsPath:t}){super([`Encoded error signature "${e}" not found on ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`,`You can look up the decoded signature here: https://openchain.xyz/signatures?query=${e}.`].join(` -`),{docsPath:t,name:`AbiErrorSignatureNotFoundError`}),Object.defineProperty(this,`signature`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.signature=e}},Zt=class extends D{constructor(e,{docsPath:t}={}){super([`Function ${e?`"${e}" `:``}not found on ABI.`,`Make sure you are using the correct ABI and that the function exists on it.`].join(` -`),{docsPath:t,name:`AbiFunctionNotFoundError`})}},Qt=class extends D{constructor(e,{docsPath:t}){super([`Function "${e}" does not contain any \`outputs\` on ABI.`,`Cannot decode function result without knowing what the parameter types are.`,`Make sure you are using the correct ABI and that the function exists on it.`].join(` -`),{docsPath:t,name:`AbiFunctionOutputsNotFoundError`})}},$t=class extends D{constructor(e,{docsPath:t}){super([`Encoded function signature "${e}" not found on ABI.`,`Make sure you are using the correct ABI and that the function exists on it.`,`You can look up the signature here: https://openchain.xyz/signatures?query=${e}.`].join(` -`),{docsPath:t,name:`AbiFunctionSignatureNotFoundError`})}},en=class extends D{constructor(e,t){super(`Found ambiguous types in overloaded ABI items.`,{metaMessages:[`\`${e.type}\` in \`${kt(e.abiItem)}\`, and`,`\`${t.type}\` in \`${kt(t.abiItem)}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`],name:`AbiItemAmbiguityError`})}},tn=class extends D{constructor({expectedSize:e,givenSize:t}){super(`Expected bytes${e}, got bytes${t}.`,{name:`BytesSizeMismatchError`})}},nn=class extends D{constructor(e,{docsPath:t}){super([`Type "${e}" is not a valid encoding type.`,`Please provide a valid ABI type.`].join(` -`),{docsPath:t,name:`InvalidAbiEncodingType`})}},rn=class extends D{constructor(e,{docsPath:t}){super([`Type "${e}" is not a valid decoding type.`,`Please provide a valid ABI type.`].join(` -`),{docsPath:t,name:`InvalidAbiDecodingType`})}},an=class extends D{constructor(e){super([`Value "${e}" is not a valid array.`].join(` -`),{name:`InvalidArrayError`})}},on=class extends D{constructor(e){super([`"${e}" is not a valid definition type.`,`Valid types: "function", "event", "error"`].join(` -`),{name:`InvalidDefinitionTypeError`})}}})),cn,ln,un,dn=o((()=>{O(),cn=class extends D{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset "${e}" is out-of-bounds (size: ${n}).`,{name:`SliceOffsetOutOfBoundsError`})}},ln=class extends D{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (${e}) exceeds padding size (${t}).`,{name:`SizeExceedsPaddingSizeError`})}},un=class extends D{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} is expected to be ${t} ${n} long, but is ${e} ${n} long.`,{name:`InvalidBytesLengthError`})}}}));function fn(e,{dir:t,size:n=32}={}){return typeof e==`string`?pn(e,{dir:t,size:n}):mn(e,{dir:t,size:n})}function pn(e,{dir:t,size:n=32}={}){if(n===null)return e;let r=e.replace(`0x`,``);if(r.length>n*2)throw new ln({size:Math.ceil(r.length/2),targetSize:n,type:`hex`});return`0x${r[t===`right`?`padEnd`:`padStart`](n*2,`0`)}`}function mn(e,{dir:t,size:n=32}={}){if(n===null)return e;if(e.length>n)throw new ln({size:e.length,targetSize:n,type:`bytes`});let r=new Uint8Array(n);for(let i=0;i{dn()})),gn,_n,vn,yn,bn=o((()=>{O(),gn=class extends D{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number "${i}" is not in safe ${r?`${r*8}-bit ${n?`signed`:`unsigned`} `:``}integer range ${e?`(${t} to ${e})`:`(above ${t})`}`,{name:`IntegerOutOfRangeError`})}},_n=class extends D{constructor(e){super(`Bytes value "${e}" is not a valid boolean. The bytes array must contain a single byte of either a 0 or 1 value.`,{name:`InvalidBytesBooleanError`})}},vn=class extends D{constructor(e){super(`Hex value "${e}" is not a valid boolean. The hex value must be "0x0" (false) or "0x1" (true).`,{name:`InvalidHexBooleanError`})}},yn=class extends D{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed ${t} bytes. Given size: ${e} bytes.`,{name:`SizeOverflowError`})}}}));function xn(e,{dir:t=`left`}={}){let n=typeof e==`string`?e.replace(`0x`,``):e,r=0;for(let e=0;e{}));function Cn(e,{size:t}){if(Ft(e)>t)throw new yn({givenSize:Ft(e),maxSize:t})}function wn(e,t={}){let{signed:n}=t;t.size&&Cn(e,{size:t.size});let r=BigInt(e);if(!n)return r;let i=(e.length-2)/2,a=(1n<{bn(),It(),Sn()}));function On(e,t={}){return typeof e==`number`||typeof e==`bigint`?k(e,t):typeof e==`string`?jn(e,t):typeof e==`boolean`?kn(e,t):An(e,t)}function kn(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(Cn(n,{size:t.size}),fn(n,{size:t.size})):n}function An(e,t={}){let n=``;for(let t=0;ta||i{bn(),hn(),Dn(),Mn=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),Nn=new TextEncoder}));function Pn(e,t={}){return typeof e==`number`||typeof e==`bigint`?Rn(e,t):typeof e==`boolean`?Fn(e,t):Nt(e)?Ln(e,t):zn(e,t)}function Fn(e,t={}){let n=new Uint8Array(1);return n[0]=Number(e),typeof t.size==`number`?(Cn(n,{size:t.size}),fn(n,{size:t.size})):n}function In(e){if(e>=Vn.zero&&e<=Vn.nine)return e-Vn.zero;if(e>=Vn.A&&e<=Vn.F)return e-(Vn.A-10);if(e>=Vn.a&&e<=Vn.f)return e-(Vn.a-10)}function Ln(e,t={}){let n=e;t.size&&(Cn(n,{size:t.size}),n=fn(n,{dir:`right`,size:t.size}));let r=n.slice(2);r.length%2&&(r=`0${r}`);let i=r.length/2,a=new Uint8Array(i);for(let e=0,t=0;e{O(),Pt(),hn(),Dn(),A(),Bn=new TextEncoder,Vn={zero:48,nine:57,A:65,F:70,a:97,f:102}}));function Un(e,t=!1){return t?{h:Number(e&Kn),l:Number(e>>qn&Kn)}:{h:Number(e>>qn&Kn)|0,l:Number(e&Kn)|0}}function Wn(e,t=!1){let n=e.length,r=new Uint32Array(n),i=new Uint32Array(n);for(let a=0;a>>0)+(r>>>0);return{h:e+n+(i/2**32|0)|0,l:i|0}}var Kn,qn,Jn,Yn,Xn,Zn,Qn,$n,er,tr,nr,rr,ir,ar,or,sr,cr,lr,ur=o((()=>{Kn=BigInt(2**32-1),qn=BigInt(32),Jn=(e,t,n)=>e>>>n,Yn=(e,t,n)=>e<<32-n|t>>>n,Xn=(e,t,n)=>e>>>n|t<<32-n,Zn=(e,t,n)=>e<<32-n|t>>>n,Qn=(e,t,n)=>e<<64-n|t>>>n-32,$n=(e,t,n)=>e>>>n-32|t<<64-n,er=(e,t,n)=>e<>>32-n,tr=(e,t,n)=>t<>>32-n,nr=(e,t,n)=>t<>>64-n,rr=(e,t,n)=>e<>>64-n,ir=(e,t,n)=>(e>>>0)+(t>>>0)+(n>>>0),ar=(e,t,n,r)=>t+n+r+(e/2**32|0)|0,or=(e,t,n,r)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0),sr=(e,t,n,r,i)=>t+n+r+i+(e/2**32|0)|0,cr=(e,t,n,r,i)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0)+(i>>>0),lr=(e,t,n,r,i,a)=>t+n+r+i+a+(e/2**32|0)|0})),dr,fr=o((()=>{dr=typeof globalThis==`object`&&`crypto`in globalThis?globalThis.crypto:void 0}));function pr(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`}function mr(e){if(!Number.isSafeInteger(e)||e<0)throw Error(`positive integer expected, got `+e)}function hr(e,...t){if(!pr(e))throw Error(`Uint8Array expected`);if(t.length>0&&!t.includes(e.length))throw Error(`Uint8Array expected of length `+t+`, got length=`+e.length)}function gr(e){if(typeof e!=`function`||typeof e.create!=`function`)throw Error(`Hash should be wrapped by utils.createHasher`);mr(e.outputLen),mr(e.blockLen)}function _r(e,t=!0){if(e.destroyed)throw Error(`Hash instance has been destroyed`);if(t&&e.finished)throw Error(`Hash#digest() has already been called`)}function vr(e,t){hr(e);let n=t.outputLen;if(e.length>>t}function Cr(e){return e<<24&4278190080|e<<8&16711680|e>>>8&65280|e>>>24&255}function wr(e){for(let t=0;te().update(Er(t)).digest(),n=e();return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=()=>e(),t}function kr(e=32){if(dr&&typeof dr.getRandomValues==`function`)return dr.getRandomValues(new Uint8Array(e));if(dr&&typeof dr.randomBytes==`function`)return Uint8Array.from(dr.randomBytes(e));throw Error(`crypto.getRandomValues must be defined`)}var Ar,jr,Mr,Nr=o((()=>{fr(),Ar=(()=>new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68)(),jr=Ar?e=>e:wr,Mr=class{}}));function Pr(e,t=24){let n=new Uint32Array(10);for(let r=24-t;r<24;r++){for(let t=0;t<10;t++)n[t]=e[t]^e[t+10]^e[t+20]^e[t+30]^e[t+40];for(let t=0;t<10;t+=2){let r=(t+8)%10,i=(t+2)%10,a=n[i],o=n[i+1],s=qr(a,o,1)^n[r],c=Jr(a,o,1)^n[r+1];for(let n=0;n<50;n+=10)e[t+n]^=s,e[t+n+1]^=c}let t=e[2],i=e[3];for(let n=0;n<24;n++){let r=Hr[n],a=qr(t,i,r),o=Jr(t,i,r),s=Vr[n];t=e[s],i=e[s+1],e[s]=a,e[s+1]=o}for(let t=0;t<50;t+=10){for(let r=0;r<10;r++)n[r]=e[t+r];for(let r=0;r<10;r++)e[t+r]^=~n[(r+2)%10]&n[(r+4)%10]}e[0]^=Gr[r],e[1]^=Kr[r]}br(n)}var Fr,Ir,Lr,Rr,zr,Br,Vr,Hr,Ur,Wr,Gr,Kr,qr,Jr,Yr,Xr,Zr,Qr=o((()=>{ur(),Nr(),Fr=BigInt(0),Ir=BigInt(1),Lr=BigInt(2),Rr=BigInt(7),zr=BigInt(256),Br=BigInt(113),Vr=[],Hr=[],Ur=[];for(let e=0,t=Ir,n=1,r=0;e<24;e++){[n,r]=[r,(2*n+3*r)%5],Vr.push(2*(5*r+n)),Hr.push((e+1)*(e+2)/2%64);let i=Fr;for(let e=0;e<7;e++)t=(t<>Rr)*Br)%zr,t&Lr&&(i^=Ir<<(Ir<n>32?nr(e,t,n):er(e,t,n),Jr=(e,t,n)=>n>32?rr(e,t,n):tr(e,t,n),Yr=class e extends Mr{constructor(e,t,n,r=!1,i=24){if(super(),this.pos=0,this.posOut=0,this.finished=!1,this.destroyed=!1,this.enableXOF=!1,this.blockLen=e,this.suffix=t,this.outputLen=n,this.enableXOF=r,this.rounds=i,mr(n),!(0=n&&this.keccak();let a=Math.min(n-this.posOut,i-r);e.set(t.subarray(this.posOut,this.posOut+a),r),this.posOut+=a,r+=a}return e}xofInto(e){if(!this.enableXOF)throw Error(`XOF is not possible for this instance`);return this.writeInto(e)}xof(e){return mr(e),this.xofInto(new Uint8Array(e))}digestInto(e){if(vr(e,this),this.finished)throw Error(`digest() was already called`);return this.writeInto(e),this.destroy(),e}digest(){return this.digestInto(new Uint8Array(this.outputLen))}destroy(){this.destroyed=!0,br(this.state)}_cloneInto(t){let{blockLen:n,suffix:r,outputLen:i,rounds:a,enableXOF:o}=this;return t||=new e(n,r,i,o,a),t.state32.set(this.state32),t.pos=this.pos,t.posOut=this.posOut,t.finished=this.finished,t.rounds=a,t.suffix=r,t.outputLen=i,t.enableXOF=o,t.destroyed=this.destroyed,t}},Xr=(e,t,n)=>Or(()=>new Yr(t,e,n)),Zr=(()=>Xr(1,136,256/8))()}));function $r(e,t){let n=t||`hex`,r=Zr(Nt(e,{strict:!1})?Pn(e):e);return n===`bytes`?r:On(r)}var ei=o((()=>{Qr(),Pt(),Hn(),A()}));function ti(e){return ni(e)}var ni,ri=o((()=>{Hn(),ei(),ni=e=>$r(Pn(e))}));function ii(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;o{O()})),oi,si=o((()=>{Ot(),ai(),oi=e=>{let t=(()=>typeof e==`string`?e:le(e))();return ii(t)}}));function ci(e){return ti(oi(e))}var li=o((()=>{ri(),si()})),ui,di=o((()=>{li(),ui=ci})),fi,pi=o((()=>{O(),fi=class extends D{constructor({address:e}){super(`Address "${e}" is invalid.`,{metaMessages:[`- Address must be a hex value of 20 bytes (40 hex characters).`,`- Address must match its checksum counterpart.`],name:`InvalidAddressError`})}}})),mi,hi=o((()=>{mi=class extends Map{constructor(e){super(),Object.defineProperty(this,`maxSize`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&t!==void 0&&(this.delete(e),super.set(e,t)),t}set(e,t){if(super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=this.keys().next().value;e&&this.delete(e)}return this}}}));function gi(e,t){if(vi.has(`${e}.${t}`))return vi.get(`${e}.${t}`);let n=t?`${t}${e.toLowerCase()}`:e.substring(2).toLowerCase(),r=$r(zn(n),`bytes`),i=(t?n.substring(`${t}0x`.length):n).split(``);for(let e=0;e<40;e+=2)r[e>>1]>>4>=8&&i[e]&&(i[e]=i[e].toUpperCase()),(r[e>>1]&15)>=8&&i[e+1]&&(i[e+1]=i[e+1].toUpperCase());let a=`0x${i.join(``)}`;return vi.set(`${e}.${t}`,a),a}function _i(e,t){if(!bi(e,{strict:!1}))throw new fi({address:e});return gi(e,t)}var vi,yi=o((()=>{pi(),Hn(),ei(),hi(),Ci(),vi=new mi(8192)}));function bi(e,t){let{strict:n=!0}=t??{},r=`${e}.${n}`;if(Si.has(r))return Si.get(r);let i=(()=>xi.test(e)?e.toLowerCase()===e?!0:n?gi(e)===e:!0:!1)();return Si.set(r,i),i}var xi,Si,Ci=o((()=>{hi(),yi(),xi=/^0x[a-fA-F0-9]{40}$/,Si=new mi(8192)}));function wi(e){return typeof e[0]==`string`?Ei(e):Ti(e)}function Ti(e){let t=0;for(let n of e)t+=n.length;let n=new Uint8Array(t),r=0;for(let t of e)n.set(t,r),r+=t.length;return n}function Ei(e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}var Di=o((()=>{}));function Oi(e,t,n,{strict:r}={}){return Nt(e,{strict:!1})?Mi(e,t,n,{strict:r}):ji(e,t,n,{strict:r})}function ki(e,t){if(typeof t==`number`&&t>0&&t>Ft(e)-1)throw new cn({offset:t,position:`start`,size:Ft(e)})}function Ai(e,t,n){if(typeof t==`number`&&typeof n==`number`&&Ft(e)!==n-t)throw new cn({offset:n,position:`end`,size:Ft(e)})}function ji(e,t,n,{strict:r}={}){ki(e,t);let i=e.slice(t,n);return r&&Ai(i,t,n),i}function Mi(e,t,n,{strict:r}={}){ki(e,t);let i=`0x${e.replace(`0x`,``).slice((t??0)*2,(n??e.length)*2)}`;return r&&Ai(i,t,n),i}var Ni=o((()=>{dn(),Pt(),It()})),Pi,Fi,Ii=o((()=>{Pi=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,Fi=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/}));function Li(e,t){if(e.length!==t.length)throw new qt({expectedLength:e.length,givenLength:t.length});let n=Ri({params:e,values:t}),r=Bi(n);return r.length===0?`0x`:r}function Ri({params:e,values:t}){let n=[];for(let r=0;r0?wi([t,e]):t}}if(i)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:wi(a.map(({encoded:e})=>e))}}function Ui(e,{param:t}){let[,n]=t.type.split(`bytes`),r=Ft(e);if(!n){let t=e;return r%32!=0&&(t=pn(t,{dir:`right`,size:Math.ceil((e.length-2)/2/32)*32})),{dynamic:!0,encoded:wi([pn(k(r,{size:32})),t])}}if(r!==Number.parseInt(n,10))throw new Kt({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:pn(e,{dir:`right`})}}function Wi(e){if(typeof e!=`boolean`)throw new D(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:pn(kn(e))}}function Gi(e,{signed:t,size:n=256}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function Ji(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}var Yi=o((()=>{sn(),pi(),O(),bn(),Ci(),Di(),hn(),It(),Ni(),A(),Ii()})),Xi,Zi=o((()=>{Ni(),li(),Xi=e=>Oi(ci(e),0,4)}));function Qi(e){let{abi:t,args:n=[],name:r}=e,i=Nt(r,{strict:!1}),a=t.filter(e=>i?e.type===`function`?Xi(e)===r:e.type===`event`?ui(e)===r:!1:`name`in e&&e.name===r);if(a.length===0)return;if(a.length===1)return a[0];let o;for(let e of a)if(`inputs`in e){if(!n||n.length===0){if(!e.inputs||e.inputs.length===0)return e;continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===n.length&&n.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?$i(t,r):!1})){if(o&&`inputs`in o&&o.inputs){let t=ea(e.inputs,o.inputs,n);if(t)throw new en({abiItem:e,type:t[0]},{abiItem:o,type:t[1]})}o=e}}return o||a[0]}function $i(e,t){let n=typeof e,r=t.type;switch(r){case`address`:return bi(e,{strict:!1});case`bool`:return n===`boolean`;case`function`:return n===`string`;case`string`:return n===`string`;default:return r===`tuple`&&`components`in t?Object.values(t.components).every((t,n)=>$i(Object.values(e)[n],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>$i(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function ea(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return ea(i.components,a.components,n[r]);let o=[i.type,a.type];if((()=>o.includes(`address`)&&o.includes(`bytes20`)?!0:o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`)?bi(n[r],{strict:!1}):!1)())return o}}var ta=o((()=>{sn(),Pt(),Ci(),di(),Zi()}));function na(e){return typeof e==`string`?{address:e,type:`json-rpc`}:e}var ra=o((()=>{}));function ia(e){let{abi:t,args:n,functionName:r}=e,i=t[0];if(r){let e=Qi({abi:t,args:n,name:r});if(!e)throw new Zt(r,{docsPath:aa});i=e}if(i.type!==`function`)throw new Zt(void 0,{docsPath:aa});return{abi:[i],functionName:Xi(kt(i))}}var aa,oa=o((()=>{sn(),Zi(),Mt(),ta(),aa=`/docs/contract/encodeFunctionData`}));function sa(e){let{args:t}=e,{abi:n,functionName:r}=(()=>e.abi.length===1&&e.functionName?.startsWith(`0x`)?e:ia(e))(),i=n[0],a=r,o=`inputs`in i&&i.inputs?Li(i.inputs,t??[]):void 0;return Ei([a,o??`0x`])}var ca=o((()=>{Di(),Yi(),oa()})),la,ua,da,fa=o((()=>{la={1:"An `assert` condition failed.",17:`Arithmetic operation resulted in underflow or overflow.`,18:"Division or modulo by zero (e.g. `5 / 0` or `23 % 0`).",33:`Attempted to convert to an invalid type.`,34:`Attempted to access a storage byte array that is incorrectly encoded.`,49:"Performed `.pop()` on an empty array",50:`Array index is out of bounds.`,65:`Allocated too much memory or created an array which is too large.`,81:`Attempted to call a zero-initialized variable of internal function type.`},ua={inputs:[{name:`message`,type:`string`}],name:`Error`,type:`error`},da={inputs:[{name:`reason`,type:`uint256`}],name:`Panic`,type:`error`}})),pa,ma,ha,ga=o((()=>{O(),pa=class extends D{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`,{name:`NegativeOffsetError`})}},ma=class extends D{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`,{name:`PositionOutOfBoundsError`})}},ha=class extends D{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`,{name:`RecursiveReadLimitExceededError`})}}}));function _a(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(va);return n.bytes=e,n.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var va,ya=o((()=>{ga(),va={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new ha({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new ma({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new pa({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new pa({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}}}));function ba(e,t={}){t.size!==void 0&&Cn(e,{size:t.size});let n=An(e,t);return wn(n,t)}function xa(e,t={}){let n=e;if(t.size!==void 0&&(Cn(n,{size:t.size}),n=xn(n)),n.length>1||n[0]>1)throw new _n(n);return!!n[0]}function Sa(e,t={}){t.size!==void 0&&Cn(e,{size:t.size});let n=An(e,t);return En(n,t)}function Ca(e,t={}){let n=e;return t.size!==void 0&&(Cn(n,{size:t.size}),n=xn(n,{dir:`right`})),new TextDecoder().decode(n)}var wa=o((()=>{bn(),Sn(),Dn(),A()}));function Ta(e,t){let n=typeof t==`string`?Ln(t):t,r=_a(n);if(Ft(n)===0&&e.length>0)throw new Wt;if(Ft(t)&&Ft(t)<32)throw new Ut({data:typeof t==`string`?t:An(t),params:e,size:Ft(t)});let i=0,a=[];for(let t=0;t48?ba(i,{signed:n}):Sa(i,{signed:n}),32]}function Ma(e,t,{staticPosition:n}){let r=t.components.length===0||t.components.some(({name:e})=>!e),i=r?[]:{},a=0;if(Pa(t)){let o=Sa(e.readBytes(Ia)),s=n+o;for(let n=0;n{sn(),yi(),ya(),It(),Ni(),Sn(),wa(),Hn(),A(),Yi(),Fa=32,Ia=32}));function Ra(e){let{abi:t,data:n}=e,r=Oi(n,0,4);if(r===`0x`)throw new Wt;let i=[...t||[],ua,da].find(e=>e.type===`error`&&r===Xi(kt(e)));if(!i)throw new Xt(r,{docsPath:`/docs/contract/decodeErrorResult`});return{abiItem:i,args:`inputs`in i&&i.inputs&&i.inputs.length>0?Ta(i.inputs,Oi(n,4)):void 0,errorName:i.name}}var za=o((()=>{fa(),sn(),Ni(),Zi(),La(),Mt()})),Ba,Va=o((()=>{Ba=(e,t,n)=>JSON.stringify(e,(e,n)=>{let r=typeof n==`bigint`?n.toString():n;return typeof t==`function`?t(e,r):r},n)}));function Ha({abiItem:e,args:t,includeFunctionName:n=!0,includeName:r=!1}){if(`name`in e&&`inputs`in e&&e.inputs)return`${n?e.name:``}(${e.inputs.map((e,n)=>`${r&&e.name?`${e.name}: `:``}${typeof t[n]==`object`?Ba(t[n]):t[n]}`).join(`, `)})`}var Ua=o((()=>{Va()})),Wa,Ga,Ka=o((()=>{Wa={gwei:9,wei:18},Ga={ether:-9,wei:9}}));function qa(e,t){let n=e.toString(),r=n.startsWith(`-`);r&&(n=n.slice(1)),n=n.padStart(t,`0`);let[i,a]=[n.slice(0,n.length-t),n.slice(n.length-t)];return a=a.replace(/(0+)$/,``),`${r?`-`:``}${i||`0`}${a?`.${a}`:``}`}var Ja=o((()=>{}));function Ya(e,t=`wei`){return qa(e,Wa[t])}var Xa=o((()=>{Ka(),Ja()}));function Za(e,t=`wei`){return qa(e,Ga[t])}var Qa=o((()=>{Ka(),Ja()}));function $a(e){return e.reduce((e,{slot:t,value:n})=>`${e} ${t}: ${n}\n`,``)}function eo(e){return e.reduce((e,{address:t,...n})=>{let r=`${e} ${t}:\n`;return n.nonce&&(r+=` nonce: ${n.nonce}\n`),n.balance&&(r+=` balance: ${n.balance}\n`),n.code&&(r+=` code: ${n.code}\n`),n.state&&(r+=` state: -`,r+=$a(n.state)),n.stateDiff&&(r+=` stateDiff: -`,r+=$a(n.stateDiff)),r},` State Override: -`).slice(0,-1)}var to,no,ro=o((()=>{O(),to=class extends D{constructor({address:e}){super(`State for account "${e}" is set multiple times.`,{name:`AccountStateConflictError`})}},no=class extends D{constructor(){super(`state and stateDiff are set on the same account.`,{name:`StateAssignmentConflictError`})}}}));function io(e){let t=Object.entries(e).map(([e,t])=>t===void 0||t===!1?null:[e,t]).filter(Boolean),n=t.reduce((e,[t])=>Math.max(e,t.length),0);return t.map(([e,t])=>` ${`${e}:`.padEnd(n+1)} ${t}`).join(` -`)}var ao,oo,so,co,lo,uo,fo,po,mo,ho=o((()=>{Xa(),Qa(),O(),ao=class extends D{constructor(){super(["Cannot specify both a `gasPrice` and a `maxFeePerGas`/`maxPriorityFeePerGas`.","Use `maxFeePerGas`/`maxPriorityFeePerGas` for EIP-1559 compatible networks, and `gasPrice` for others."].join(` -`),{name:`FeeConflictError`})}},oo=class extends D{constructor({v:e}){super(`Invalid \`v\` value "${e}". Expected 27 or 28.`,{name:`InvalidLegacyVError`})}},so=class extends D{constructor({transaction:e}){super(`Cannot infer a transaction type from provided transaction.`,{metaMessages:[`Provided Transaction:`,`{`,io(e),`}`,``,`To infer the type, either provide:`,"- a `type` to the Transaction, or","- an EIP-1559 Transaction with `maxFeePerGas`, or","- an EIP-2930 Transaction with `gasPrice` & `accessList`, or","- an EIP-4844 Transaction with `blobs`, `blobVersionedHashes`, `sidecars`, or","- an EIP-7702 Transaction with `authorizationList`, or","- a Legacy Transaction with `gasPrice`"],name:`InvalidSerializableTransactionError`})}},co=class extends D{constructor({storageKey:e}){super(`Size for storage key "${e}" is invalid. Expected 32 bytes. Got ${Math.floor((e.length-2)/2)} bytes.`,{name:`InvalidStorageKeySizeError`})}},lo=class extends D{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d}){let f=io({chain:r&&`${r?.name} (id: ${r?.id})`,from:t?.address,to:u,value:d!==void 0&&`${Ya(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${Za(o)} gwei`,maxFeePerGas:s!==void 0&&`${Za(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${Za(c)} gwei`,nonce:l});super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Request Arguments:`,f].filter(Boolean),name:`TransactionExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},uo=class extends D{constructor({blockHash:e,blockNumber:t,blockTag:n,hash:r,index:i}){let a=`Transaction`;n&&i!==void 0&&(a=`Transaction at block time "${n}" at index "${i}"`),e&&i!==void 0&&(a=`Transaction at block hash "${e}" at index "${i}"`),t&&i!==void 0&&(a=`Transaction at block number "${t}" at index "${i}"`),r&&(a=`Transaction with hash "${r}"`),super(`${a} could not be found.`,{name:`TransactionNotFoundError`})}},fo=class extends D{constructor({hash:e}){super(`Transaction receipt with hash "${e}" could not be found. The Transaction may not be processed on a block yet.`,{name:`TransactionReceiptNotFoundError`})}},po=class extends D{constructor({receipt:e}){super(`Transaction with hash "${e.transactionHash}" reverted.`,{metaMessages:[`The receipt marked the transaction as "reverted". This could mean that the function on the contract you are trying to call threw an error.`,` `,`You can attempt to extract the revert reason by:`,"- calling the `simulateContract` or `simulateCalls` Action with the `abi` and `functionName` of the contract","- using the `call` Action with raw `data`"],name:`TransactionReceiptRevertedError`}),Object.defineProperty(this,`receipt`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.receipt=e}},mo=class extends D{constructor({hash:e}){super(`Timed out while waiting for transaction with hash "${e}" to be confirmed.`,{name:`WaitForTransactionReceiptTimeoutError`})}}})),go,_o,vo=o((()=>{go=e=>e,_o=e=>e})),yo,bo,xo,So,Co,wo,To=o((()=>{ra(),fa(),za(),Mt(),Ua(),ta(),Xa(),Qa(),sn(),O(),ro(),ho(),vo(),yo=class extends D{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d,stateOverride:f}){let p=t?na(t):void 0,m=io({from:p?.address,to:u,value:d!==void 0&&`${Ya(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${Za(o)} gwei`,maxFeePerGas:s!==void 0&&`${Za(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${Za(c)} gwei`,nonce:l});f&&(m+=`\n${eo(f)}`),super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Raw Call Arguments:`,m].filter(Boolean),name:`CallExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},bo=class extends D{constructor(e,{abi:t,args:n,contractAddress:r,docsPath:i,functionName:a,sender:o}){let s=Qi({abi:t,args:n,name:a}),c=s?Ha({abiItem:s,args:n,includeFunctionName:!1,includeName:!1}):void 0,l=s?kt(s,{includeName:!0}):void 0,u=io({address:r&&go(r),function:l,args:c&&c!==`()`&&`${[...Array(a?.length??0).keys()].map(()=>` `).join(``)}${c}`,sender:o});super(e.shortMessage||`An unknown error occurred while executing the contract function "${a}".`,{cause:e,docsPath:i,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],u&&`Contract Call:`,u].filter(Boolean),name:`ContractFunctionExecutionError`}),Object.defineProperty(this,`abi`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`args`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`contractAddress`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`formattedArgs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`functionName`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`sender`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.abi=t,this.args=n,this.cause=e,this.contractAddress=r,this.functionName=a,this.sender=o}},xo=class extends D{constructor({abi:e,data:t,functionName:n,message:r}){let i,a,o,s;if(t&&t!==`0x`)try{a=Ra({abi:e,data:t});let{abiItem:n,errorName:r,args:i}=a;if(r===`Error`)s=i[0];else if(r===`Panic`){let[e]=i;s=la[e]}else{let e=n?kt(n,{includeName:!0}):void 0,t=n&&i?Ha({abiItem:n,args:i,includeFunctionName:!1,includeName:!1}):void 0;o=[e?`Error: ${e}`:``,t&&t!==`()`?` ${[...Array(r?.length??0).keys()].map(()=>` `).join(``)}${t}`:``]}}catch(e){i=e}else r&&(s=r);let c;i instanceof Xt&&(c=i.signature,o=[`Unable to decode signature "${c}" as it was not found on the provided ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`,`You can look up the decoded signature here: https://openchain.xyz/signatures?query=${c}.`]),super(s&&s!==`execution reverted`||c?[`The contract function "${n}" reverted with the following ${c?`signature`:`reason`}:`,s||c].join(` -`):`The contract function "${n}" reverted.`,{cause:i,metaMessages:o,name:`ContractFunctionRevertedError`}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`raw`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`reason`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`signature`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=a,this.raw=t,this.reason=s,this.signature=c}},So=class extends D{constructor({functionName:e}){super(`The contract function "${e}" returned no data ("0x").`,{metaMessages:[`This could be due to any of the following:`,` - The contract does not have the function "${e}",`,` - The parameters passed to the contract function may be invalid, or`,` - The address is not a contract.`],name:`ContractFunctionZeroDataError`})}},Co=class extends D{constructor({factory:e}){super(`Deployment for counterfactual contract call failed${e?` for factory "${e}".`:``}`,{metaMessages:[`Please ensure:`,"- The `factory` is a valid contract deployment factory (ie. Create2 Factory, ERC-4337 Factory, etc).","- The `factoryData` is a valid encoded function call for contract deployment function on the factory."],name:`CounterfactualDeploymentFailedError`})}},wo=class extends D{constructor({data:e,message:t}){super(t||``,{name:`RawContractError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:3}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=e}}})),Eo,Do,Oo,ko=o((()=>{Va(),O(),vo(),Eo=class extends D{constructor({body:e,cause:t,details:n,headers:r,status:i,url:a}){super(`HTTP request failed.`,{cause:t,details:n,metaMessages:[i&&`Status: ${i}`,`URL: ${_o(a)}`,e&&`Request body: ${Ba(e)}`].filter(Boolean),name:`HttpRequestError`}),Object.defineProperty(this,`body`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`headers`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`status`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.body=e,this.headers=r,this.status=i,this.url=a}},Do=class extends D{constructor({body:e,error:t,url:n}){super(`RPC Request failed.`,{cause:t,details:t.message,metaMessages:[`URL: ${_o(n)}`,`Request body: ${Ba(e)}`],name:`RpcRequestError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.code=t.code,this.data=t.data}},Oo=class extends D{constructor({body:e,url:t}){super(`The request took too long to respond.`,{details:`The request timed out.`,metaMessages:[`URL: ${_o(t)}`,`Request body: ${Ba(e)}`],name:`TimeoutError`})}}})),Ao,jo,Mo,No,j,Po,Fo,Io,Lo,Ro,zo,Bo,Vo,Ho,Uo,Wo,Go,Ko,qo,Jo,Yo,Xo,Zo,Qo,$o,es,ts,ns,rs,os=o((()=>{O(),ko(),Ao=-1,jo=class extends D{constructor(e,{code:t,docsPath:n,metaMessages:r,name:i,shortMessage:a}){super(a,{cause:e,docsPath:n,metaMessages:r||e?.metaMessages,name:i||`RpcError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.name=i||e.name,this.code=e instanceof Do?e.code:t??Ao}},Mo=class extends jo{constructor(e,t){super(e,t),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=t.data}},No=class e extends jo{constructor(t){super(t,{code:e.code,name:`ParseRpcError`,shortMessage:`Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.`})}},Object.defineProperty(No,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700}),j=class e extends jo{constructor(t){super(t,{code:e.code,name:`InvalidRequestRpcError`,shortMessage:`JSON is not a valid request object.`})}},Object.defineProperty(j,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600}),Po=class e extends jo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`MethodNotFoundRpcError`,shortMessage:`The method${n?` "${n}"`:``} does not exist / is not available.`})}},Object.defineProperty(Po,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601}),Fo=class e extends jo{constructor(t){super(t,{code:e.code,name:`InvalidParamsRpcError`,shortMessage:[`Invalid parameters were provided to the RPC method.`,`Double check you have provided the correct parameters.`].join(` -`)})}},Object.defineProperty(Fo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602}),Io=class e extends jo{constructor(t){super(t,{code:e.code,name:`InternalRpcError`,shortMessage:`An internal error was received.`})}},Object.defineProperty(Io,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603}),Lo=class e extends jo{constructor(t){super(t,{code:e.code,name:`InvalidInputRpcError`,shortMessage:[`Missing or invalid parameters.`,`Double check you have provided the correct parameters.`].join(` -`)})}},Object.defineProperty(Lo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3}),Ro=class e extends jo{constructor(t){super(t,{code:e.code,name:`ResourceNotFoundRpcError`,shortMessage:`Requested resource not found.`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`ResourceNotFoundRpcError`})}},Object.defineProperty(Ro,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001}),zo=class e extends jo{constructor(t){super(t,{code:e.code,name:`ResourceUnavailableRpcError`,shortMessage:`Requested resource not available.`})}},Object.defineProperty(zo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002}),Bo=class e extends jo{constructor(t){super(t,{code:e.code,name:`TransactionRejectedRpcError`,shortMessage:`Transaction creation failed.`})}},Object.defineProperty(Bo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003}),Vo=class e extends jo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`MethodNotSupportedRpcError`,shortMessage:`Method${n?` "${n}"`:``} is not supported.`})}},Object.defineProperty(Vo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004}),Ho=class e extends jo{constructor(t){super(t,{code:e.code,name:`LimitExceededRpcError`,shortMessage:`Request exceeds defined limit.`})}},Object.defineProperty(Ho,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005}),Uo=class e extends jo{constructor(t){super(t,{code:e.code,name:`JsonRpcVersionUnsupportedError`,shortMessage:`Version of JSON-RPC protocol is not supported.`})}},Object.defineProperty(Uo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006}),Wo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`UserRejectedRequestError`,shortMessage:`User rejected the request.`})}},Object.defineProperty(Wo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001}),Go=class e extends Mo{constructor(t){super(t,{code:e.code,name:`UnauthorizedProviderError`,shortMessage:`The requested method and/or account has not been authorized by the user.`})}},Object.defineProperty(Go,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100}),Ko=class e extends Mo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`UnsupportedProviderMethodError`,shortMessage:`The Provider does not support the requested method${n?` " ${n}"`:``}.`})}},Object.defineProperty(Ko,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200}),qo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ProviderDisconnectedError`,shortMessage:`The Provider is disconnected from all chains.`})}},Object.defineProperty(qo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900}),Jo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ChainDisconnectedError`,shortMessage:`The Provider is not connected to the requested chain.`})}},Object.defineProperty(Jo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901}),Yo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`SwitchChainError`,shortMessage:`An error occurred when attempting to switch chain.`})}},Object.defineProperty(Yo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902}),Xo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`UnsupportedNonOptionalCapabilityError`,shortMessage:`This Wallet does not support a capability that was not marked as optional.`})}},Object.defineProperty(Xo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700}),Zo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`UnsupportedChainIdError`,shortMessage:`This Wallet does not support the requested chain ID.`})}},Object.defineProperty(Zo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710}),Qo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`DuplicateIdError`,shortMessage:`There is already a bundle submitted with this ID.`})}},Object.defineProperty(Qo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720}),$o=class e extends Mo{constructor(t){super(t,{code:e.code,name:`UnknownBundleIdError`,shortMessage:`This bundle id is unknown / has not been submitted`})}},Object.defineProperty($o,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730}),es=class e extends Mo{constructor(t){super(t,{code:e.code,name:`BundleTooLargeError`,shortMessage:`The call bundle is too large for the Wallet to process.`})}},Object.defineProperty(es,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740}),ts=class e extends Mo{constructor(t){super(t,{code:e.code,name:`AtomicReadyWalletRejectedUpgradeError`,shortMessage:`The Wallet can support atomicity after an upgrade, but the user rejected the upgrade.`})}},Object.defineProperty(ts,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750}),ns=class e extends Mo{constructor(t){super(t,{code:e.code,name:`AtomicityNotSupportedError`,shortMessage:`The wallet does not support atomic execution but the request requires it.`})}},Object.defineProperty(ns,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760}),rs=class extends jo{constructor(e){super(e,{name:`UnknownRpcError`,shortMessage:`An unknown RPC error occurred.`})}}}));sn(),O(),To(),ko(),os();var ss=3;function cs(e,{abi:t,address:n,args:r,docsPath:i,functionName:a,sender:o}){let s=e instanceof wo?e:e instanceof D?e.walk(e=>`data`in e)||e.walk():{},{code:c,data:l,details:u,message:d,shortMessage:f}=s,p=(()=>e instanceof Wt?new So({functionName:a}):[ss,Io.code].includes(c)&&(l||u||d||f)?new xo({abi:t,data:typeof l==`object`?l.data:l,functionName:a,message:s instanceof Do?u:f??d}):e)();return new bo(p,{abi:t,args:r,contractAddress:n,docsPath:i,functionName:a,sender:o})}yi(),ei();function ls(e){let t=$r(`0x${e.substring(4)}`).substring(26);return gi(`0x${t}`)}var us,ds,fs,ps,ms=o((()=>{us=(function(){let e=typeof document<`u`&&document.createElement(`link`).relList;return e&&e.supports&&e.supports(`modulepreload`)?`modulepreload`:`preload`})(),ds=function(e){return`/`+e},fs={},ps=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=ds(t,n),t in fs)return;fs[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:us,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})}}));function hs(e,t,n,r){if(typeof e.setBigUint64==`function`)return e.setBigUint64(t,n,r);let i=BigInt(32),a=BigInt(4294967295),o=Number(n>>i&a),s=Number(n&a),c=r?4:0,l=r?0:4;e.setUint32(t+c,o,r),e.setUint32(t+l,s,r)}function gs(e,t,n){return e&t^~e&n}function _s(e,t,n){return e&t^e&n^t&n}var vs,ys,bs,xs,Ss=o((()=>{Nr(),vs=class extends Mr{constructor(e,t,n,r){super(),this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=e,this.outputLen=t,this.padOffset=n,this.isLE=r,this.buffer=new Uint8Array(e),this.view=xr(this.buffer)}update(e){_r(this),e=Er(e),hr(e);let{view:t,buffer:n,blockLen:r}=this,i=e.length;for(let a=0;ar-a&&(this.process(n,0),a=0);for(let e=a;el.length)throw Error(`_sha2: outputLen bigger than state`);for(let e=0;e{Ss(),ur(),Nr(),Cs=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),ws=new Uint32Array(64),Ts=class extends vs{constructor(e=32){super(64,e,8,!1),this.A=ys[0]|0,this.B=ys[1]|0,this.C=ys[2]|0,this.D=ys[3]|0,this.E=ys[4]|0,this.F=ys[5]|0,this.G=ys[6]|0,this.H=ys[7]|0}get(){let{A:e,B:t,C:n,D:r,E:i,F:a,G:o,H:s}=this;return[e,t,n,r,i,a,o,s]}set(e,t,n,r,i,a,o,s){this.A=e|0,this.B=t|0,this.C=n|0,this.D=r|0,this.E=i|0,this.F=a|0,this.G=o|0,this.H=s|0}process(e,t){for(let n=0;n<16;n++,t+=4)ws[n]=e.getUint32(t,!1);for(let e=16;e<64;e++){let t=ws[e-15],n=ws[e-2],r=Sr(t,7)^Sr(t,18)^t>>>3;ws[e]=(Sr(n,17)^Sr(n,19)^n>>>10)+ws[e-7]+r+ws[e-16]|0}let{A:n,B:r,C:i,D:a,E:o,F:s,G:c,H:l}=this;for(let e=0;e<64;e++){let t=Sr(o,6)^Sr(o,11)^Sr(o,25),u=l+t+gs(o,s,c)+Cs[e]+ws[e]|0,d=(Sr(n,2)^Sr(n,13)^Sr(n,22))+_s(n,r,i)|0;l=c,c=s,s=o,o=a+u|0,a=i,i=r,r=n,n=u+d|0}n=n+this.A|0,r=r+this.B|0,i=i+this.C|0,a=a+this.D|0,o=o+this.E|0,s=s+this.F|0,c=c+this.G|0,l=l+this.H|0,this.set(n,r,i,a,o,s,c,l)}roundClean(){br(ws)}destroy(){this.set(0,0,0,0,0,0,0,0),br(this.buffer)}},Es=(()=>Wn(`0x428a2f98d728ae22.0x7137449123ef65cd.0xb5c0fbcfec4d3b2f.0xe9b5dba58189dbbc.0x3956c25bf348b538.0x59f111f1b605d019.0x923f82a4af194f9b.0xab1c5ed5da6d8118.0xd807aa98a3030242.0x12835b0145706fbe.0x243185be4ee4b28c.0x550c7dc3d5ffb4e2.0x72be5d74f27b896f.0x80deb1fe3b1696b1.0x9bdc06a725c71235.0xc19bf174cf692694.0xe49b69c19ef14ad2.0xefbe4786384f25e3.0x0fc19dc68b8cd5b5.0x240ca1cc77ac9c65.0x2de92c6f592b0275.0x4a7484aa6ea6e483.0x5cb0a9dcbd41fbd4.0x76f988da831153b5.0x983e5152ee66dfab.0xa831c66d2db43210.0xb00327c898fb213f.0xbf597fc7beef0ee4.0xc6e00bf33da88fc2.0xd5a79147930aa725.0x06ca6351e003826f.0x142929670a0e6e70.0x27b70a8546d22ffc.0x2e1b21385c26c926.0x4d2c6dfc5ac42aed.0x53380d139d95b3df.0x650a73548baf63de.0x766a0abb3c77b2a8.0x81c2c92e47edaee6.0x92722c851482353b.0xa2bfe8a14cf10364.0xa81a664bbc423001.0xc24b8b70d0f89791.0xc76c51a30654be30.0xd192e819d6ef5218.0xd69906245565a910.0xf40e35855771202a.0x106aa07032bbd1b8.0x19a4c116b8d2d0c8.0x1e376c085141ab53.0x2748774cdf8eeb99.0x34b0bcb5e19b48a8.0x391c0cb3c5c95a63.0x4ed8aa4ae3418acb.0x5b9cca4f7763e373.0x682e6ff3d6b2b8a3.0x748f82ee5defb2fc.0x78a5636f43172f60.0x84c87814a1f0ab72.0x8cc702081a6439ec.0x90befffa23631e28.0xa4506cebde82bde9.0xbef9a3f7b2c67915.0xc67178f2e372532b.0xca273eceea26619c.0xd186b8c721c0c207.0xeada7dd6cde0eb1e.0xf57d4f7fee6ed178.0x06f067aa72176fba.0x0a637dc5a2c898a6.0x113f9804bef90dae.0x1b710b35131c471b.0x28db77f523047d84.0x32caab7b40c72493.0x3c9ebe0a15c9bebc.0x431d67c49c100d4c.0x4cc5d4becb3e42b6.0x597f299cfc657e2a.0x5fcb6fab3ad6faec.0x6c44198c4a475817`.split(`.`).map(e=>BigInt(e))))(),Ds=(()=>Es[0])(),Os=(()=>Es[1])(),ks=new Uint32Array(80),As=new Uint32Array(80),js=class extends vs{constructor(e=64){super(128,e,16,!1),this.Ah=xs[0]|0,this.Al=xs[1]|0,this.Bh=xs[2]|0,this.Bl=xs[3]|0,this.Ch=xs[4]|0,this.Cl=xs[5]|0,this.Dh=xs[6]|0,this.Dl=xs[7]|0,this.Eh=xs[8]|0,this.El=xs[9]|0,this.Fh=xs[10]|0,this.Fl=xs[11]|0,this.Gh=xs[12]|0,this.Gl=xs[13]|0,this.Hh=xs[14]|0,this.Hl=xs[15]|0}get(){let{Ah:e,Al:t,Bh:n,Bl:r,Ch:i,Cl:a,Dh:o,Dl:s,Eh:c,El:l,Fh:u,Fl:d,Gh:f,Gl:p,Hh:m,Hl:h}=this;return[e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h]}set(e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h){this.Ah=e|0,this.Al=t|0,this.Bh=n|0,this.Bl=r|0,this.Ch=i|0,this.Cl=a|0,this.Dh=o|0,this.Dl=s|0,this.Eh=c|0,this.El=l|0,this.Fh=u|0,this.Fl=d|0,this.Gh=f|0,this.Gl=p|0,this.Hh=m|0,this.Hl=h|0}process(e,t){for(let n=0;n<16;n++,t+=4)ks[n]=e.getUint32(t),As[n]=e.getUint32(t+=4);for(let e=16;e<80;e++){let t=ks[e-15]|0,n=As[e-15]|0,r=Xn(t,n,1)^Xn(t,n,8)^Jn(t,n,7),i=Zn(t,n,1)^Zn(t,n,8)^Yn(t,n,7),a=ks[e-2]|0,o=As[e-2]|0,s=Xn(a,o,19)^Qn(a,o,61)^Jn(a,o,6),c=Zn(a,o,19)^$n(a,o,61)^Yn(a,o,6),l=or(i,c,As[e-7],As[e-16]);ks[e]=sr(l,r,s,ks[e-7],ks[e-16])|0,As[e]=l|0}let{Ah:n,Al:r,Bh:i,Bl:a,Ch:o,Cl:s,Dh:c,Dl:l,Eh:u,El:d,Fh:f,Fl:p,Gh:m,Gl:h,Hh:g,Hl:_}=this;for(let e=0;e<80;e++){let t=Xn(u,d,14)^Xn(u,d,18)^Qn(u,d,41),v=Zn(u,d,14)^Zn(u,d,18)^$n(u,d,41),y=u&f^~u&m,b=d&p^~d&h,x=cr(_,v,b,Os[e],As[e]),S=lr(x,g,t,y,Ds[e],ks[e]),C=x|0,w=Xn(n,r,28)^Qn(n,r,34)^Qn(n,r,39),ee=Zn(n,r,28)^$n(n,r,34)^$n(n,r,39),te=n&i^n&o^i&o,ne=r&a^r&s^a&s;g=m|0,_=h|0,m=f|0,h=p|0,f=u|0,p=d|0,{h:u,l:d}=Gn(c|0,l|0,S|0,C|0),c=o|0,l=s|0,o=i|0,s=a|0,i=n|0,a=r|0;let re=ir(C,ee,ne);n=ar(re,S,w,te),r=re|0}({h:n,l:r}=Gn(this.Ah|0,this.Al|0,n|0,r|0)),{h:i,l:a}=Gn(this.Bh|0,this.Bl|0,i|0,a|0),{h:o,l:s}=Gn(this.Ch|0,this.Cl|0,o|0,s|0),{h:c,l}=Gn(this.Dh|0,this.Dl|0,c|0,l|0),{h:u,l:d}=Gn(this.Eh|0,this.El|0,u|0,d|0),{h:f,l:p}=Gn(this.Fh|0,this.Fl|0,f|0,p|0),{h:m,l:h}=Gn(this.Gh|0,this.Gl|0,m|0,h|0),{h:g,l:_}=Gn(this.Hh|0,this.Hl|0,g|0,_|0),this.set(n,r,i,a,o,s,c,l,u,d,f,p,m,h,g,_)}roundClean(){br(ks,As)}destroy(){br(this.buffer),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}},Ms=class extends js{constructor(){super(48),this.Ah=bs[0]|0,this.Al=bs[1]|0,this.Bh=bs[2]|0,this.Bl=bs[3]|0,this.Ch=bs[4]|0,this.Cl=bs[5]|0,this.Dh=bs[6]|0,this.Dl=bs[7]|0,this.Eh=bs[8]|0,this.El=bs[9]|0,this.Fh=bs[10]|0,this.Fl=bs[11]|0,this.Gh=bs[12]|0,this.Gl=bs[13]|0,this.Hh=bs[14]|0,this.Hl=bs[15]|0}},Ns=Or(()=>new Ts),Ps=Or(()=>new js),Fs=Or(()=>new Ms)})),Ls,Rs,zs=o((()=>{Nr(),Ls=class extends Mr{constructor(e,t){super(),this.finished=!1,this.destroyed=!1,gr(e);let n=Er(t);if(this.iHash=e.create(),typeof this.iHash.update!=`function`)throw Error(`Expected instance of class which extends utils.Hash`);this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;let r=this.blockLen,i=new Uint8Array(r);i.set(n.length>r?e.create().update(n).digest():n);for(let e=0;enew Ls(e,t).update(n).digest(),Rs.create=(e,t)=>new Ls(e,t)}));function Bs(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`}function Vs(e){if(!Bs(e))throw Error(`Uint8Array expected`)}function Hs(e,t){if(typeof t!=`boolean`)throw Error(e+` boolean expected, got `+t)}function Us(e){let t=e.toString(16);return t.length&1?`0`+t:t}function Ws(e){if(typeof e!=`string`)throw Error(`hex string expected, got `+typeof e);return e===``?sc:BigInt(`0x`+e)}function Gs(e){if(Vs(e),lc)return e.toHex();let t=``;for(let n=0;n=dc._0&&e<=dc._9)return e-dc._0;if(e>=dc.A&&e<=dc.F)return e-(dc.A-10);if(e>=dc.a&&e<=dc.f)return e-(dc.a-10)}function qs(e){if(typeof e!=`string`)throw Error(`hex string expected, got `+typeof e);if(lc)return Uint8Array.fromHex(e);let t=e.length,n=t/2;if(t%2)throw Error(`hex string expected, got unpadded hex of length `+t);let r=new Uint8Array(n);for(let t=0,i=0;tsc;e>>=cc,t+=1);return t}function ic(e,t,n){if(typeof e!=`number`||e<2)throw Error(`hashLen must be a number`);if(typeof t!=`number`||t<2)throw Error(`qByteLen must be a number`);if(typeof n!=`function`)throw Error(`hmacFn must be a function`);let r=mc(e),i=mc(e),a=0,o=()=>{r.fill(1),i.fill(0),a=0},s=(...e)=>n(i,r,...e),c=(e=mc(0))=>{i=s(hc([0]),e),r=s(),e.length!==0&&(i=s(hc([1]),e),r=s())},l=()=>{if(a++>=1e3)throw Error(`drbg: tried 1000 values`);let e=0,n=[];for(;e{o(),c(e);let n;for(;!(n=t(l()));)c();return o(),n}}function ac(e,t,n={}){let r=(t,n,r)=>{let i=gc[n];if(typeof i!=`function`)throw Error(`invalid validator function`);let a=e[t];if(!(r&&a===void 0)&&!i(a,e))throw Error(`param `+String(t)+` is invalid. Expected `+n+`, got `+a)};for(let[e,n]of Object.entries(t))r(e,n,!1);for(let[e,t]of Object.entries(n))r(e,t,!0);return e}function oc(e){let t=new WeakMap;return(n,...r)=>{let i=t.get(n);if(i!==void 0)return i;let a=e(n,...r);return t.set(n,a),a}}var sc,cc,lc,uc,dc,fc,pc,mc,hc,gc,_c=o((()=>{sc=BigInt(0),cc=BigInt(1),lc=typeof Uint8Array.from([]).toHex==`function`&&typeof Uint8Array.fromHex==`function`,uc=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),dc={_0:48,_9:57,A:65,F:70,a:97,f:102},fc=e=>typeof e==`bigint`&&sc<=e,pc=e=>(cc<new Uint8Array(e),hc=e=>Uint8Array.from(e),gc={bigint:e=>typeof e==`bigint`,function:e=>typeof e==`function`,boolean:e=>typeof e==`boolean`,string:e=>typeof e==`string`,stringOrUint8Array:e=>typeof e==`string`||Bs(e),isSafeInteger:e=>Number.isSafeInteger(e),array:e=>Array.isArray(e),field:(e,t)=>t.Fp.isValid(e),hash:e=>typeof e==`function`&&Number.isSafeInteger(e.outputLen)}}));function vc(e,t){let n=e%t;return n>=Pc?n:t+n}function yc(e,t,n){let r=e;for(;t-- >Pc;)r*=r,r%=n;return r}function bc(e,t){if(e===Pc)throw Error(`invert: expected non-zero number`);if(t<=Pc)throw Error(`invert: expected positive modulus, got `+t);let n=vc(e,t),r=t,i=Pc,a=Fc,o=Fc,s=Pc;for(;n!==Pc;){let e=r/n,t=r%n,c=i-o*e,l=a-s*e;r=n,n=t,i=o,a=s,o=c,s=l}if(r!==Fc)throw Error(`invert: does not exist`);return vc(i,t)}function xc(e,t){let n=(e.ORDER+Fc)/Rc,r=e.pow(t,n);if(!e.eql(e.sqr(r),t))throw Error(`Cannot find square root`);return r}function Sc(e,t){let n=(e.ORDER-zc)/Bc,r=e.mul(t,Ic),i=e.pow(r,n),a=e.mul(t,i),o=e.mul(e.mul(a,Ic),i),s=e.mul(a,e.sub(o,e.ONE));if(!e.eql(e.sqr(s),t))throw Error(`Cannot find square root`);return s}function Cc(e){if(e1e3)throw Error(`Cannot find square root: probably non-prime P`);if(n===1)return xc;let a=i.pow(r,t),o=(t+Fc)/Ic;return function(e,r){if(e.is0(r))return r;if(Oc(e,r)!==1)throw Error(`Cannot find square root`);let i=n,s=e.mul(e.ONE,a),c=e.pow(r,t),l=e.pow(r,o);for(;!e.eql(c,e.ONE);){if(e.is0(c))return e.ZERO;let t=1,n=e.sqr(c);for(;!e.eql(n,e.ONE);)if(t++,n=e.sqr(n),t===i)throw Error(`Cannot find square root`);let r=Fc<(e[t]=`function`,e),{ORDER:`bigint`,MASK:`bigint`,BYTES:`isSafeInteger`,BITS:`isSafeInteger`});return ac(e,t)}function Ec(e,t,n){if(nPc;)n&Fc&&(r=e.mul(r,i)),i=e.sqr(i),n>>=Fc;return r}function Dc(e,t,n=!1){let r=Array(t.length).fill(n?e.ZERO:void 0),i=t.reduce((t,n,i)=>e.is0(n)?t:(r[i]=t,e.mul(t,n)),e.ONE),a=e.inv(i);return t.reduceRight((t,n,i)=>e.is0(n)?t:(r[i]=e.mul(t,r[i]),e.mul(t,n)),a),r}function Oc(e,t){let n=(e.ORDER-Fc)/Ic,r=e.pow(t,n),i=e.eql(r,e.ONE),a=e.eql(r,e.ZERO),o=e.eql(r,e.neg(e.ONE));if(!i&&!a&&!o)throw Error(`invalid Legendre symbol result`);return i?1:a?0:-1}function kc(e,t){t!==void 0&&mr(t);let n=t===void 0?e.toString(2).length:t,r=Math.ceil(n/8);return{nBitLength:n,nByteLength:r}}function Ac(e,t,n=!1,r={}){if(e<=Pc)throw Error(`invalid field: expected ORDER > 0, got `+e);let{nBitLength:i,nByteLength:a}=kc(e,t);if(a>2048)throw Error(`invalid field: expected ORDER of <= 2048 bytes`);let o,s=Object.freeze({ORDER:e,isLE:n,BITS:i,BYTES:a,MASK:pc(i),ZERO:Pc,ONE:Fc,create:t=>vc(t,e),isValid:t=>{if(typeof t!=`bigint`)throw Error(`invalid field element: expected bigint, got `+typeof t);return Pc<=t&&te===Pc,isOdd:e=>(e&Fc)===Fc,neg:t=>vc(-t,e),eql:(e,t)=>e===t,sqr:t=>vc(t*t,e),add:(t,n)=>vc(t+n,e),sub:(t,n)=>vc(t-n,e),mul:(t,n)=>vc(t*n,e),pow:(e,t)=>Ec(s,e,t),div:(t,n)=>vc(t*bc(n,e),e),sqrN:e=>e*e,addN:(e,t)=>e+t,subN:(e,t)=>e-t,mulN:(e,t)=>e*t,inv:t=>bc(t,e),sqrt:r.sqrt||(t=>(o||=wc(e),o(s,t))),toBytes:e=>n?Zs(e,a):Xs(e,a),fromBytes:e=>{if(e.length!==a)throw Error(`Field.fromBytes: expected `+a+` bytes, got `+e.length);return n?Ys(e):Js(e)},invertBatch:e=>Dc(s,e),cmov:(e,t,n)=>n?t:e});return Object.freeze(s)}function jc(e){if(typeof e!=`bigint`)throw Error(`field order must be bigint`);let t=e.toString(2).length;return Math.ceil(t/8)}function Mc(e){let t=jc(e);return t+Math.ceil(t/2)}function Nc(e,t,n=!1){let r=e.length,i=jc(t),a=Mc(t);if(r<16||r1024)throw Error(`expected `+a+`-1024 bytes of input, got `+r);let o=n?Ys(e):Js(e),s=vc(o,t-Fc)+Fc;return n?Zs(s,i):Xs(s,i)}var Pc,Fc,Ic,Lc,Rc,zc,Bc,Vc,Hc=o((()=>{Nr(),_c(),Pc=BigInt(0),Fc=BigInt(1),Ic=BigInt(2),Lc=BigInt(3),Rc=BigInt(4),zc=BigInt(5),Bc=BigInt(8),Vc=[`create`,`isValid`,`is0`,`neg`,`inv`,`sqrt`,`sqr`,`eql`,`add`,`sub`,`mul`,`pow`,`div`,`addN`,`subN`,`mulN`,`sqrN`]}));function Uc(e,t){let n=t.negate();return e?n:t}function Wc(e,t){if(!Number.isSafeInteger(e)||e<=0||e>t)throw Error(`invalid window size, expected [1..`+t+`], got W=`+e)}function Gc(e,t){Wc(e,t);let n=Math.ceil(t/e)+1,r=2**(e-1),i=2**e,a=pc(e),o=BigInt(e);return{windows:n,windowSize:r,mask:a,maxNumber:i,shiftBy:o}}function Kc(e,t,n){let{windowSize:r,mask:i,maxNumber:a,shiftBy:o}=n,s=Number(e&i),c=e>>o;s>r&&(s-=a,c+=el);let l=t*r,u=l+Math.abs(s)-1,d=s===0,f=s<0,p=t%2!=0;return{nextN:c,offset:u,isZero:d,isNeg:f,isNegF:p,offsetF:l}}function qc(e,t){if(!Array.isArray(e))throw Error(`array expected`);e.forEach((e,n)=>{if(!(e instanceof t))throw Error(`invalid point at index `+n)})}function Jc(e,t){if(!Array.isArray(e))throw Error(`array of scalars expected`);e.forEach((e,n)=>{if(!t.isValid(e))throw Error(`invalid scalar at index `+n)})}function Yc(e){return nl.get(e)||1}function Xc(e,t){return{constTimeNegate:Uc,hasPrecomputes(e){return Yc(e)!==1},unsafeLadder(t,n,r=e.ZERO){let i=t;for(;n>$c;)n&el&&(r=r.add(i)),i=i.double(),n>>=el;return r},precomputeWindow(e,n){let{windows:r,windowSize:i}=Gc(n,t),a=[],o=e,s=o;for(let e=0;e12?c=s-3:s>4?c=s-2:s>0&&(c=2);let l=pc(c),u=Array(Number(l)+1).fill(o),d=Math.floor((t.BITS-1)/c)*c,f=o;for(let e=d;e>=0;e-=c){u.fill(o);for(let t=0;t>BigInt(e)&l);u[a]=u[a].add(n[t])}let t=o;for(let e=u.length-1,n=o;e>0;e--)n=n.add(u[e]),t=t.add(n);if(f=f.add(t),e!==0)for(let e=0;e{Hc(),_c(),$c=BigInt(0),el=BigInt(1),tl=new WeakMap,nl=new WeakMap}));function il(e){e.lowS!==void 0&&Hs(`lowS`,e.lowS),e.prehash!==void 0&&Hs(`prehash`,e.prehash)}function al(e){let t=Qc(e);ac(t,{a:`field`,b:`field`},{allowInfinityPoint:`boolean`,allowedPrivateKeyLengths:`array`,clearCofactor:`function`,fromBytes:`function`,isTorsionFree:`function`,toBytes:`function`,wrapPrivateKey:`boolean`});let{endo:n,Fp:r,a:i}=t;if(n){if(!r.eql(i,r.ZERO))throw Error(`invalid endo: CURVE.a must be 0`);if(typeof n!=`object`||typeof n.beta!=`bigint`||typeof n.splitScalar!=`function`)throw Error(`invalid endo: expected "beta": bigint and "splitScalar": function`)}return Object.freeze({...t})}function ol(e,t){return Gs(Xs(e,t))}function sl(e){let t=al(e),{Fp:n}=t,r=Ac(t.n,t.nBitLength),i=t.toBytes||((e,t,r)=>{let i=t.toAffine();return $s(Uint8Array.from([4]),n.toBytes(i.x),n.toBytes(i.y))}),a=t.fromBytes||(e=>{let t=e.subarray(1),r=n.fromBytes(t.subarray(0,n.BYTES)),i=n.fromBytes(t.subarray(n.BYTES,2*n.BYTES));return{x:r,y:i}});function o(e){let{a:r,b:i}=t,a=n.sqr(e),o=n.mul(a,e);return n.add(n.add(o,n.mul(e,r)),i)}function s(e,t){let r=n.sqr(t),i=o(e);return n.eql(r,i)}if(!s(t.Gx,t.Gy))throw Error(`bad curve params: generator point`);let c=n.mul(n.pow(t.a,_l),vl),l=n.mul(n.sqr(t.b),BigInt(27));if(n.is0(n.add(c,l)))throw Error(`bad curve params: a or b`);function u(e){return tc(e,hl,t.n)}function d(e){let{allowedPrivateKeyLengths:n,nByteLength:r,wrapPrivateKey:i,n:a}=t;if(n&&typeof e!=`bigint`){if(Bs(e)&&(e=Gs(e)),typeof e!=`string`||!n.includes(e.length))throw Error(`invalid private key`);e=e.padStart(r*2,`0`)}let o;try{o=typeof e==`bigint`?e:Js(Qs(`private key`,e,r))}catch{throw Error(`invalid private key, expected hex or `+r+` bytes, got `+typeof e)}return i&&(o=vc(o,a)),nc(`private key`,o,hl,a),o}function f(e){if(!(e instanceof h))throw Error(`ProjectivePoint expected`)}let p=oc((e,t)=>{let{px:r,py:i,pz:a}=e;if(n.eql(a,n.ONE))return{x:r,y:i};let o=e.is0();t??=o?n.ONE:n.inv(a);let s=n.mul(r,t),c=n.mul(i,t),l=n.mul(a,t);if(o)return{x:n.ZERO,y:n.ZERO};if(!n.eql(l,n.ONE))throw Error(`invZ was invalid`);return{x:s,y:c}}),m=oc(e=>{if(e.is0()){if(t.allowInfinityPoint&&!n.is0(e.py))return;throw Error(`bad point: ZERO`)}let{x:r,y:i}=e.toAffine();if(!n.isValid(r)||!n.isValid(i))throw Error(`bad point: x or y not FE`);if(!s(r,i))throw Error(`bad point: equation left != right`);if(!e.isTorsionFree())throw Error(`bad point: not in prime-order subgroup`);return!0});class h{constructor(e,t,r){if(e==null||!n.isValid(e))throw Error(`x required`);if(t==null||!n.isValid(t)||n.is0(t))throw Error(`y required`);if(r==null||!n.isValid(r))throw Error(`z required`);this.px=e,this.py=t,this.pz=r,Object.freeze(this)}static fromAffine(e){let{x:t,y:r}=e||{};if(!e||!n.isValid(t)||!n.isValid(r))throw Error(`invalid affine point`);if(e instanceof h)throw Error(`projective point not allowed`);let i=e=>n.eql(e,n.ZERO);return i(t)&&i(r)?h.ZERO:new h(t,r,n.ONE)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}static normalizeZ(e){let t=Dc(n,e.map(e=>e.pz));return e.map((e,n)=>e.toAffine(t[n])).map(h.fromAffine)}static fromHex(e){let t=h.fromAffine(a(Qs(`pointHex`,e)));return t.assertValidity(),t}static fromPrivateKey(e){return h.BASE.multiply(d(e))}static msm(e,t){return Zc(h,r,e,t)}_setWindowSize(e){v.setWindowSize(this,e)}assertValidity(){m(this)}hasEvenY(){let{y:e}=this.toAffine();if(n.isOdd)return!n.isOdd(e);throw Error(`Field doesn't support isOdd`)}equals(e){f(e);let{px:t,py:r,pz:i}=this,{px:a,py:o,pz:s}=e,c=n.eql(n.mul(t,s),n.mul(a,i)),l=n.eql(n.mul(r,s),n.mul(o,i));return c&&l}negate(){return new h(this.px,n.neg(this.py),this.pz)}double(){let{a:e,b:r}=t,i=n.mul(r,_l),{px:a,py:o,pz:s}=this,c=n.ZERO,l=n.ZERO,u=n.ZERO,d=n.mul(a,a),f=n.mul(o,o),p=n.mul(s,s),m=n.mul(a,o);return m=n.add(m,m),u=n.mul(a,s),u=n.add(u,u),c=n.mul(e,u),l=n.mul(i,p),l=n.add(c,l),c=n.sub(f,l),l=n.add(f,l),l=n.mul(c,l),c=n.mul(m,c),u=n.mul(i,u),p=n.mul(e,p),m=n.sub(d,p),m=n.mul(e,m),m=n.add(m,u),u=n.add(d,d),d=n.add(u,d),d=n.add(d,p),d=n.mul(d,m),l=n.add(l,d),p=n.mul(o,s),p=n.add(p,p),d=n.mul(p,m),c=n.sub(c,d),u=n.mul(p,f),u=n.add(u,u),u=n.add(u,u),new h(c,l,u)}add(e){f(e);let{px:r,py:i,pz:a}=this,{px:o,py:s,pz:c}=e,l=n.ZERO,u=n.ZERO,d=n.ZERO,p=t.a,m=n.mul(t.b,_l),g=n.mul(r,o),_=n.mul(i,s),v=n.mul(a,c),y=n.add(r,i),b=n.add(o,s);y=n.mul(y,b),b=n.add(g,_),y=n.sub(y,b),b=n.add(r,a);let x=n.add(o,c);return b=n.mul(b,x),x=n.add(g,v),b=n.sub(b,x),x=n.add(i,a),l=n.add(s,c),x=n.mul(x,l),l=n.add(_,v),x=n.sub(x,l),d=n.mul(p,b),l=n.mul(m,v),d=n.add(l,d),l=n.sub(_,d),d=n.add(_,d),u=n.mul(l,d),_=n.add(g,g),_=n.add(_,g),v=n.mul(p,v),b=n.mul(m,b),_=n.add(_,v),v=n.sub(g,v),v=n.mul(p,v),b=n.add(b,v),g=n.mul(_,b),u=n.add(u,g),g=n.mul(x,b),l=n.mul(y,l),l=n.sub(l,g),g=n.mul(y,_),d=n.mul(x,d),d=n.add(d,g),new h(l,u,d)}subtract(e){return this.add(e.negate())}is0(){return this.equals(h.ZERO)}wNAF(e){return v.wNAFCached(this,e,h.normalizeZ)}multiplyUnsafe(e){let{endo:r,n:i}=t;nc(`scalar`,e,ml,i);let a=h.ZERO;if(e===ml)return a;if(this.is0()||e===hl)return this;if(!r||v.hasPrecomputes(this))return v.wNAFCachedUnsafe(this,e,h.normalizeZ);let{k1neg:o,k1:s,k2neg:c,k2:l}=r.splitScalar(e),u=a,d=a,f=this;for(;s>ml||l>ml;)s&hl&&(u=u.add(f)),l&hl&&(d=d.add(f)),f=f.double(),s>>=hl,l>>=hl;return o&&(u=u.negate()),c&&(d=d.negate()),d=new h(n.mul(d.px,r.beta),d.py,d.pz),u.add(d)}multiply(e){let{endo:r,n:i}=t;nc(`scalar`,e,hl,i);let a,o;if(r){let{k1neg:t,k1:i,k2neg:s,k2:c}=r.splitScalar(e),{p:l,f:u}=this.wNAF(i),{p:d,f}=this.wNAF(c);l=v.constTimeNegate(t,l),d=v.constTimeNegate(s,d),d=new h(n.mul(d.px,r.beta),d.py,d.pz),a=l.add(d),o=u.add(f)}else{let{p:t,f:n}=this.wNAF(e);a=t,o=n}return h.normalizeZ([a,o])[0]}multiplyAndAddUnsafe(e,t,n){let r=h.BASE,i=(e,t)=>t===ml||t===hl||!e.equals(r)?e.multiplyUnsafe(t):e.multiply(t),a=i(this,t).add(i(e,n));return a.is0()?void 0:a}toAffine(e){return p(this,e)}isTorsionFree(){let{h:e,isTorsionFree:n}=t;if(e===hl)return!0;if(n)return n(h,this);throw Error(`isTorsionFree() has not been declared for the elliptic curve`)}clearCofactor(){let{h:e,clearCofactor:n}=t;return e===hl?this:n?n(h,this):this.multiplyUnsafe(t.h)}toRawBytes(e=!0){return Hs(`isCompressed`,e),this.assertValidity(),i(h,this,e)}toHex(e=!0){return Hs(`isCompressed`,e),Gs(this.toRawBytes(e))}}h.BASE=new h(t.Gx,t.Gy,n.ONE),h.ZERO=new h(n.ZERO,n.ONE,n.ZERO);let{endo:g,nBitLength:_}=t,v=Xc(h,g?Math.ceil(_/2):_);return{CURVE:t,ProjectivePoint:h,normPrivateKeyToScalar:d,weierstrassEquation:o,isWithinCurveOrder:u}}function cl(e){let t=Qc(e);return ac(t,{hash:`hash`,hmac:`function`,randomBytes:`function`},{bits2int:`function`,bits2int_modN:`function`,lowS:`boolean`}),Object.freeze({lowS:!0,...t})}function ll(e){let t=cl(e),{Fp:n,n:r,nByteLength:i,nBitLength:a}=t,o=n.BYTES+1,s=2*n.BYTES+1;function c(e){return vc(e,r)}function l(e){return bc(e,r)}let{ProjectivePoint:u,normPrivateKeyToScalar:d,weierstrassEquation:f,isWithinCurveOrder:p}=sl({...t,toBytes(e,t,r){let i=t.toAffine(),a=n.toBytes(i.x),o=$s;return Hs(`isCompressed`,r),r?o(Uint8Array.from([t.hasEvenY()?2:3]),a):o(Uint8Array.from([4]),a,n.toBytes(i.y))},fromBytes(e){let t=e.length,r=e[0],i=e.subarray(1);if(t===o&&(r===2||r===3)){let e=Js(i);if(!tc(e,hl,n.ORDER))throw Error(`Point is not on curve`);let t=f(e),a;try{a=n.sqrt(t)}catch(e){let t=e instanceof Error?`: `+e.message:``;throw Error(`Point is not on curve`+t)}let o=(a&hl)===hl;return(r&1)==1!==o&&(a=n.neg(a)),{x:e,y:a}}else if(t===s&&r===4){let e=n.fromBytes(i.subarray(0,n.BYTES)),t=n.fromBytes(i.subarray(n.BYTES,2*n.BYTES));return{x:e,y:t}}else{let e=o,n=s;throw Error(`invalid Point, expected length of `+e+`, or uncompressed `+n+`, got `+t)}}});function m(e){let t=r>>hl;return e>t}function h(e){return m(e)?c(-e):e}let g=(e,t,n)=>Js(e.slice(t,n));class _{constructor(e,t,n){nc(`r`,e,hl,r),nc(`s`,t,hl,r),this.r=e,this.s=t,n!=null&&(this.recovery=n),Object.freeze(this)}static fromCompact(e){let t=i;return e=Qs(`compactSignature`,e,t*2),new _(g(e,0,t),g(e,t,2*t))}static fromDER(e){let{r:t,s:n}=pl.toSig(Qs(`DER`,e));return new _(t,n)}assertValidity(){}addRecoveryBit(e){return new _(this.r,this.s,e)}recoverPublicKey(e){let{r,s:i,recovery:a}=this,o=C(Qs(`msgHash`,e));if(a==null||![0,1,2,3].includes(a))throw Error(`recovery id invalid`);let s=a===2||a===3?r+t.n:r;if(s>=n.ORDER)throw Error(`recovery id 2 or 3 invalid`);let d=a&1?`03`:`02`,f=u.fromHex(d+ol(s,n.BYTES)),p=l(s),m=c(-o*p),h=c(i*p),g=u.BASE.multiplyAndAddUnsafe(f,m,h);if(!g)throw Error(`point at infinify`);return g.assertValidity(),g}hasHighS(){return m(this.s)}normalizeS(){return this.hasHighS()?new _(this.r,c(-this.s),this.recovery):this}toDERRawBytes(){return qs(this.toDERHex())}toDERHex(){return pl.hexFromSig(this)}toCompactRawBytes(){return qs(this.toCompactHex())}toCompactHex(){let e=i;return ol(this.r,e)+ol(this.s,e)}}let v={isValidPrivateKey(e){try{return d(e),!0}catch{return!1}},normPrivateKeyToScalar:d,randomPrivateKey:()=>{let e=Mc(t.n);return Nc(t.randomBytes(e),t.n)},precompute(e=8,t=u.BASE){return t._setWindowSize(e),t.multiply(BigInt(3)),t}};function y(e,t=!0){return u.fromPrivateKey(e).toRawBytes(t)}function b(e){if(typeof e==`bigint`)return!1;if(e instanceof u)return!0;let r=Qs(`key`,e).length,a=n.BYTES,o=a+1,s=2*a+1;if(!(t.allowedPrivateKeyLengths||i===o))return r===o||r===s}function x(e,t,n=!0){if(b(e)===!0)throw Error(`first arg must be private key`);if(b(t)===!1)throw Error(`second arg must be public key`);return u.fromHex(t).multiply(d(e)).toRawBytes(n)}let S=t.bits2int||function(e){if(e.length>8192)throw Error(`input is too large`);let t=Js(e),n=e.length*8-a;return n>0?t>>BigInt(n):t},C=t.bits2int_modN||function(e){return c(S(e))},w=pc(a);function ee(e){return nc(`num < 2^`+a,e,ml,w),Xs(e,i)}function te(e,r,i=ne){if([`recovered`,`canonical`].some(e=>e in i))throw Error(`sign() legacy options not supported`);let{hash:a,randomBytes:o}=t,{lowS:s,prehash:f,extraEntropy:g}=i;s??=!0,e=Qs(`msgHash`,e),il(i),f&&(e=Qs(`prehashed msgHash`,a(e)));let v=C(e),y=d(r),b=[ee(y),ee(v)];if(g!=null&&g!==!1){let e=g===!0?o(n.BYTES):g;b.push(Qs(`extraEntropy`,e))}let x=$s(...b),w=v;function te(e){let t=S(e);if(!p(t))return;let n=l(t),r=u.BASE.multiply(t).toAffine(),i=c(r.x);if(i===ml)return;let a=c(n*c(w+i*y));if(a===ml)return;let o=(r.x===i?0:2)|Number(r.y&hl),d=a;return s&&m(a)&&(d=h(a),o^=1),new _(i,d,o)}return{seed:x,k2sig:te}}let ne={lowS:t.lowS,prehash:!1},re={lowS:t.lowS,prehash:!1};function ie(e,n,r=ne){let{seed:i,k2sig:a}=te(e,n,r),o=t;return ic(o.hash.outputLen,o.nByteLength,o.hmac)(i,a)}u.BASE._setWindowSize(8);function ae(e,n,r,i=re){let a=e;n=Qs(`msgHash`,n),r=Qs(`publicKey`,r);let{lowS:o,prehash:s,format:d}=i;if(il(i),`strict`in i)throw Error(`options.strict was renamed to lowS`);if(d!==void 0&&d!==`compact`&&d!==`der`)throw Error(`format must be compact or der`);let f=typeof a==`string`||Bs(a),p=!f&&!d&&typeof a==`object`&&!!a&&typeof a.r==`bigint`&&typeof a.s==`bigint`;if(!f&&!p)throw Error(`invalid signature, expected Uint8Array, hex string or Signature instance`);let m,h;try{if(p&&(m=new _(a.r,a.s)),f){try{d!==`compact`&&(m=_.fromDER(a))}catch(e){if(!(e instanceof pl.Err))throw e}!m&&d!==`der`&&(m=_.fromCompact(a))}h=u.fromHex(r)}catch{return!1}if(!m||o&&m.hasHighS())return!1;s&&(n=t.hash(n));let{r:g,s:v}=m,y=C(n),b=l(v),x=c(y*b),S=c(g*b),w=u.BASE.multiplyAndAddUnsafe(h,x,S)?.toAffine();return w?c(w.x)===g:!1}return{CURVE:t,getPublicKey:y,getSharedSecret:x,sign:ie,verify:ae,ProjectivePoint:u,Signature:_,utils:v}}function ul(e,t){let n=e.ORDER,r=ml;for(let e=n-hl;e%gl===ml;e/=gl)r+=hl;let i=r,a=gl<{let r=d,a=e.pow(n,l),o=e.sqr(a);o=e.mul(o,n);let s=e.mul(t,o);s=e.pow(s,c),s=e.mul(s,a),a=e.mul(s,n),o=e.mul(s,t);let p=e.mul(o,a);s=e.pow(p,u);let m=e.eql(s,e.ONE);a=e.mul(o,f),s=e.mul(p,r),o=e.cmov(a,o,m),p=e.cmov(s,p,m);for(let t=i;t>hl;t--){let n=t-gl;n=gl<{let a=e.sqr(i),o=e.mul(t,i);a=e.mul(a,o);let s=e.pow(a,n);s=e.mul(s,o);let c=e.mul(s,r),l=e.mul(e.sqr(s),i),u=e.eql(l,t),d=e.cmov(c,s,u);return{isValid:u,value:d}}}return p}function dl(e,t){if(Tc(e),!e.isValid(t.A)||!e.isValid(t.B)||!e.isValid(t.Z))throw Error(`mapToCurveSimpleSWU: invalid opts`);let n=ul(e,t.Z);if(!e.isOdd)throw Error(`Fp.isOdd is not implemented!`);return r=>{let i,a,o,s,c,l,u,d;i=e.sqr(r),i=e.mul(i,t.Z),a=e.sqr(i),a=e.add(a,i),o=e.add(a,e.ONE),o=e.mul(o,t.B),s=e.cmov(t.Z,e.neg(a),!e.eql(a,e.ZERO)),s=e.mul(s,t.A),a=e.sqr(o),l=e.sqr(s),c=e.mul(l,t.A),a=e.add(a,c),a=e.mul(a,o),l=e.mul(l,s),c=e.mul(l,t.B),a=e.add(a,c),u=e.mul(i,o);let{isValid:f,value:p}=n(a,l);d=e.mul(i,r),d=e.mul(d,p),u=e.cmov(u,o,f),d=e.cmov(d,p,f);let m=e.isOdd(r)===e.isOdd(d);d=e.cmov(e.neg(d),d,m);let h=Dc(e,[s],!0)[0];return u=e.mul(u,h),{x:u,y:d}}}var fl,pl,ml,hl,gl,_l,vl,yl=o((()=>{rl(),Hc(),_c(),fl=class extends Error{constructor(e=``){super(e)}},pl={Err:fl,_tlv:{encode:(e,t)=>{let{Err:n}=pl;if(e<0||e>256)throw new n(`tlv.encode: wrong tag`);if(t.length&1)throw new n(`tlv.encode: unpadded data`);let r=t.length/2,i=Us(r);if(i.length/2&128)throw new n(`tlv.encode: long form length too big`);let a=r>127?Us(i.length/2|128):``;return Us(e)+a+i+t},decode(e,t){let{Err:n}=pl,r=0;if(e<0||e>256)throw new n(`tlv.encode: wrong tag`);if(t.length<2||t[r++]!==e)throw new n(`tlv.decode: wrong tlv`);let i=t[r++],a=!!(i&128),o=0;if(!a)o=i;else{let e=i&127;if(!e)throw new n(`tlv.decode(long): indefinite length not supported`);if(e>4)throw new n(`tlv.decode(long): byte length is too big`);let a=t.subarray(r,r+e);if(a.length!==e)throw new n(`tlv.decode: length bytes not complete`);if(a[0]===0)throw new n(`tlv.decode(long): zero leftmost byte`);for(let e of a)o=o<<8|e;if(r+=e,o<128)throw new n(`tlv.decode(long): not minimal encoding`)}let s=t.subarray(r,r+o);if(s.length!==o)throw new n(`tlv.decode: wrong value length`);return{v:s,l:t.subarray(r+o)}}},_int:{encode(e){let{Err:t}=pl;if(eRs(e,t,Dr(...n)),randomBytes:kr}}function xl(e,t){let n=t=>ll({...e,...bl(t)});return{...n(t),create:n}}var Sl=o((()=>{zs(),Nr(),yl()}));function Cl(e,t){if(Tl(e),Tl(t),e<0||e>=1<<8*t)throw Error(`invalid I2OSP input: `+e);let n=Array.from({length:t}).fill(0);for(let r=t-1;r>=0;r--)n[r]=e&255,e>>>=8;return new Uint8Array(n)}function wl(e,t){let n=new Uint8Array(e.length);for(let r=0;r255&&(t=r($s(ec(`H2C-OVERSIZE-DST-`),t)));let{outputLen:i,blockLen:a}=r,o=Math.ceil(n/i);if(n>65535||o>255)throw Error(`expand_message_xmd: invalid lenInBytes`);let s=$s(t,Cl(t.length,1)),c=Cl(0,a),l=Cl(n,2),u=Array(o),d=r($s(c,e,l,Cl(0,1),s));u[0]=r($s(d,Cl(1,1),s));for(let e=1;e<=o;e++){let t=[wl(d,u[e-1]),Cl(e+1,1),s];u[e]=r($s(...t))}return $s(...u).slice(0,n)}function Dl(e,t,n,r,i){if(Vs(e),Vs(t),Tl(n),t.length>255){let e=Math.ceil(2*r/8);t=i.create({dkLen:e}).update(ec(`H2C-OVERSIZE-DST-`)).update(t).digest()}if(n>65535||t.length>255)throw Error(`expand_message_xof: invalid lenInBytes`);return i.create({dkLen:n}).update(e).update(Cl(n,2)).update(t).update(Cl(t.length,1)).digest()}function Ol(e,t,n){ac(n,{DST:`stringOrUint8Array`,p:`bigint`,m:`isSafeInteger`,k:`isSafeInteger`,hash:`hash`});let{p:r,k:i,m:a,hash:o,expand:s,DST:c}=n;Vs(e),Tl(t);let l=typeof c==`string`?ec(c):c,u=r.toString(2).length,d=Math.ceil((u+i)/8),f=t*a*d,p;if(s===`xmd`)p=El(e,l,f,o);else if(s===`xof`)p=Dl(e,l,f,i,o);else if(s===`_internal_pass`)p=e;else throw Error(`expand must be "xmd" or "xof"`);let m=Array(t);for(let e=0;eArray.from(e).reverse());return(t,r)=>{let[i,a,o,s]=n.map(n=>n.reduce((n,r)=>e.add(e.mul(n,t),r))),[c,l]=Dc(e,[a,s],!0);return t=e.mul(i,c),r=e.mul(r,e.mul(o,l)),{x:t,y:r}}}function Al(e,t,n){if(typeof t!=`function`)throw Error(`mapToCurve() must be defined`);function r(n){return e.fromAffine(t(n))}function i(t){let n=t.clearCofactor();return n.equals(e.ZERO)?e.ZERO:(n.assertValidity(),n)}return{defaults:n,hashToCurve(e,t){let a=Ol(e,2,{...n,DST:n.DST,...t}),o=r(a[0]),s=r(a[1]);return i(o.add(s))},encodeToCurve(e,t){let a=Ol(e,1,{...n,DST:n.encodeDST,...t});return i(r(a[0]))},mapToCurve(e){if(!Array.isArray(e))throw Error(`expected array of bigints`);for(let t of e)if(typeof t!=`bigint`)throw Error(`expected array of bigints`);return i(r(e))}}}var jl,Ml=o((()=>{Hc(),_c(),jl=Js})),Nl=c({encodeToCurve:()=>lu,hashToCurve:()=>cu,schnorr:()=>iu,secp256k1:()=>Yl,secp256k1_hasher:()=>su});function Pl(e){let t=Hl,n=BigInt(3),r=BigInt(6),i=BigInt(11),a=BigInt(22),o=BigInt(23),s=BigInt(44),c=BigInt(88),l=e*e*e%t,u=l*l*e%t,d=yc(u,n,t)*u%t,f=yc(d,n,t)*u%t,p=yc(f,Kl,t)*l%t,m=yc(p,i,t)*p%t,h=yc(m,a,t)*m%t,g=yc(h,s,t)*h%t,_=yc(g,c,t)*g%t,v=yc(_,s,t)*h%t,y=yc(v,n,t)*u%t,b=yc(y,o,t)*m%t,x=yc(b,r,t)*l%t,S=yc(x,Kl,t);if(!Jl.eql(Jl.sqr(S),e))throw Error(`Cannot find square root`);return S}function Fl(e,...t){let n=Xl[e];if(n===void 0){let t=Ns(Uint8Array.from(e,e=>e.charCodeAt(0)));n=$s(t,t),Xl[e]=n}return Ns($s(n,...t))}function Il(e){let t=Yl.utils.normPrivateKeyToScalar(e),n=tu.fromPrivateKey(t);return{scalar:n.hasEvenY()?t:eu(-t),bytes:Zl(n)}}function Ll(e){nc(`x`,e,Gl,Hl);let t=$l(e*e),n=$l(t*e+BigInt(7)),r=Pl(n);r%Kl!==Wl&&(r=$l(-r));let i=new tu(e,r,Gl);return i.assertValidity(),i}function Rl(...e){return eu(ru(Fl(`BIP0340/challenge`,...e)))}function zl(e){return Il(e).bytes}function Bl(e,t,n=kr(32)){let r=Qs(`message`,e),{bytes:i,scalar:a}=Il(t),o=Qs(`auxRand`,n,32),s=Ql(a^ru(Fl(`BIP0340/aux`,o))),c=Fl(`BIP0340/nonce`,s,i,r),l=eu(ru(c));if(l===Wl)throw Error(`sign failed: k is zero`);let{bytes:u,scalar:d}=Il(l),f=Rl(u,i,r),p=new Uint8Array(64);if(p.set(u,0),p.set(Ql(eu(d+f*a)),32),!Vl(p,r,i))throw Error(`sign: Invalid signature produced`);return p}function Vl(e,t,n){let r=Qs(`signature`,e,64),i=Qs(`message`,t),a=Qs(`publicKey`,n,32);try{let e=Ll(ru(a)),t=ru(r.subarray(0,32));if(!tc(t,Gl,Hl))return!1;let n=ru(r.subarray(32,64));if(!tc(n,Gl,Ul))return!1;let o=Rl(Ql(t),Zl(e),i),s=nu(e,n,eu(-o));return!(!s||!s.hasEvenY()||s.toAffine().x!==t)}catch{return!1}}var Hl,Ul,Wl,Gl,Kl,ql,Jl,Yl,Xl,Zl,Ql,$l,eu,tu,nu,ru,iu,au,ou,su,cu,lu,uu=o((()=>{Is(),Nr(),Sl(),Ml(),Hc(),_c(),yl(),Hl=BigInt(`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f`),Ul=BigInt(`0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141`),Wl=BigInt(0),Gl=BigInt(1),Kl=BigInt(2),ql=(e,t)=>(e+t/Kl)/t,Jl=Ac(Hl,void 0,void 0,{sqrt:Pl}),Yl=xl({a:Wl,b:BigInt(7),Fp:Jl,n:Ul,Gx:BigInt(`55066263022277343669578718895168534326250603453777594175500187360389116729240`),Gy:BigInt(`32670510020758816978083085130507043184471273380659243275938904335757337482424`),h:BigInt(1),lowS:!0,endo:{beta:BigInt(`0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee`),splitScalar:e=>{let t=Ul,n=BigInt(`0x3086d221a7d46bcde86c90e49284eb15`),r=-Gl*BigInt(`0xe4437ed6010e88286f547fa90abfe4c3`),i=BigInt(`0x114ca50f7a8e2f3f657c1108d9d44cfd8`),a=n,o=BigInt(`0x100000000000000000000000000000000`),s=ql(a*e,t),c=ql(-r*e,t),l=vc(e-s*n-c*i,t),u=vc(-s*r-c*a,t),d=l>o,f=u>o;if(d&&(l=t-l),f&&(u=t-u),l>o||u>o)throw Error(`splitScalar: Endomorphism failed, k=`+e);return{k1neg:d,k1:l,k2neg:f,k2:u}}}},Ns),Xl={},Zl=e=>e.toRawBytes(!0).slice(1),Ql=e=>Xs(e,32),$l=e=>vc(e,Hl),eu=e=>vc(e,Ul),tu=(()=>Yl.ProjectivePoint)(),nu=(e,t,n)=>tu.BASE.multiplyAndAddUnsafe(e,t,n),ru=Js,iu=(()=>({getPublicKey:zl,sign:Bl,verify:Vl,utils:{randomPrivateKey:Yl.utils.randomPrivateKey,lift_x:Ll,pointToBytes:Zl,numberToBytesBE:Xs,bytesToNumberBE:Js,taggedHash:Fl,mod:vc}}))(),au=(()=>kl(Jl,[[`0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7`,`0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581`,`0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262`,`0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c`],[`0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b`,`0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14`,`0x0000000000000000000000000000000000000000000000000000000000000001`],[`0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c`,`0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3`,`0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931`,`0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84`],[`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b`,`0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573`,`0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f`,`0x0000000000000000000000000000000000000000000000000000000000000001`]].map(e=>e.map(e=>BigInt(e)))))(),ou=(()=>dl(Jl,{A:BigInt(`0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533`),B:BigInt(`1771`),Z:Jl.create(BigInt(`-11`))}))(),su=(()=>Al(Yl.ProjectivePoint,e=>{let{x:t,y:n}=ou(Jl.create(e[0]));return au(t,n)},{DST:`secp256k1_XMD:SHA-256_SSWU_RO_`,encodeDST:`secp256k1_XMD:SHA-256_SSWU_NU_`,p:Jl.ORDER,m:1,k:128,expand:`xmd`,hash:Ns}))(),cu=(()=>su.hashToCurve)(),lu=(()=>su.encodeToCurve)()}));Pt(),It(),Dn(),A(),ms();async function du({hash:e,signature:t}){let n=Nt(e)?e:On(e),{secp256k1:r}=await ps(async()=>{let{secp256k1:e}=await Promise.resolve().then(()=>(uu(),Nl));return{secp256k1:e}},void 0);return`0x${(()=>{if(typeof t==`object`&&`r`in t&&`s`in t){let{r:e,s:n,v:i,yParity:a}=t,o=fu(Number(a??i));return new r.Signature(wn(e),wn(n)).addRecoveryBit(o)}let e=Nt(t)?t:On(t);if(Ft(e)!==65)throw Error(`invalid signature length`);let n=En(`0x${e.slice(130)}`),i=fu(n);return r.Signature.fromCompact(e.substring(2,130)).addRecoveryBit(i)})().recoverPublicKey(n.substring(2)).toHex(!1)}`}function fu(e){if(e===0||e===1)return e;if(e===27)return 0;if(e===28)return 1;throw Error(`Invalid yParityOrV value`)}async function pu({hash:e,signature:t}){return ls(await du({hash:e,signature:t}))}O(),ya(),Hn(),A();function M(e,t=`hex`){let n=N(e),r=_a(new Uint8Array(n.length));return n.encode(r),t===`hex`?An(r.bytes):r.bytes}function N(e){return Array.isArray(e)?mu(e.map(e=>N(e))):hu(e)}function mu(e){let t=e.reduce((e,t)=>e+t.length,0),n=gu(t);return{length:(()=>t<=55?1+t:1+n+t)(),encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function hu(e){let t=typeof e==`string`?Ln(e):e,n=gu(t.length);return{length:(()=>t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length)(),encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function gu(e){if(e<2**8)return 1;if(e<2**16)return 2;if(e<2**24)return 3;if(e<2**32)return 4;throw new D(`Length is too large.`)}Di(),Hn(),A(),ei();function _u(e){let{chainId:t,nonce:n,to:r}=e,i=e.contractAddress??e.address,a=$r(Ei([`0x05`,M([t?k(t):`0x`,i,n?k(n):`0x`])]));return r===`bytes`?Ln(a):a}async function vu(e){let{authorization:t,signature:n}=e;return pu({hash:_u(t),signature:n??t})}Xa(),Qa(),O(),ho();var yu=class extends D{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d}){let f=io({from:t?.address,to:u,value:d!==void 0&&`${Ya(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${Za(o)} gwei`,maxFeePerGas:s!==void 0&&`${Za(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${Za(c)} gwei`,nonce:l});super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Estimate Gas Arguments:`,f].filter(Boolean),name:`EstimateGasExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},bu,xu,Su,Cu,wu,Tu,Eu,Du,Ou,ku,Au,ju,Mu=o((()=>{Qa(),O(),bu=class extends D{constructor({cause:e,message:t}={}){let n=t?.replace(`execution reverted: `,``)?.replace(`execution reverted`,``);super(`Execution reverted ${n?`with reason: ${n}`:`for an unknown reason`}.`,{cause:e,name:`ExecutionRevertedError`})}},Object.defineProperty(bu,`code`,{enumerable:!0,configurable:!0,writable:!0,value:3}),Object.defineProperty(bu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/execution reverted/}),xu=class extends D{constructor({cause:e,maxFeePerGas:t}={}){super(`The fee cap (\`maxFeePerGas\`${t?` = ${Za(t)} gwei`:``}) cannot be higher than the maximum allowed value (2^256-1).`,{cause:e,name:`FeeCapTooHighError`})}},Object.defineProperty(xu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max fee per gas higher than 2\^256-1|fee cap higher than 2\^256-1/}),Su=class extends D{constructor({cause:e,maxFeePerGas:t}={}){super(`The fee cap (\`maxFeePerGas\`${t?` = ${Za(t)}`:``} gwei) cannot be lower than the block base fee.`,{cause:e,name:`FeeCapTooLowError`})}},Object.defineProperty(Su,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max fee per gas less than block base fee|fee cap less than block base fee|transaction is outdated/}),Cu=class extends D{constructor({cause:e,nonce:t}={}){super(`Nonce provided for the transaction ${t?`(${t}) `:``}is higher than the next one expected.`,{cause:e,name:`NonceTooHighError`})}},Object.defineProperty(Cu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce too high/}),wu=class extends D{constructor({cause:e,nonce:t}={}){super([`Nonce provided for the transaction ${t?`(${t}) `:``}is lower than the current nonce of the account.`,"Try increasing the nonce or find the latest nonce with `getTransactionCount`."].join(` -`),{cause:e,name:`NonceTooLowError`})}},Object.defineProperty(wu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce too low|transaction already imported|already known/}),Tu=class extends D{constructor({cause:e,nonce:t}={}){super(`Nonce provided for the transaction ${t?`(${t}) `:``}exceeds the maximum allowed nonce.`,{cause:e,name:`NonceMaxValueError`})}},Object.defineProperty(Tu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce has max value/}),Eu=class extends D{constructor({cause:e}={}){super([`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`].join(` -`),{cause:e,metaMessages:[`This error could arise when the account does not have enough funds to:`,` - pay for the total gas fee,`,` - pay for the value to send.`,` `,"The cost of the transaction is calculated as `gas * gas fee + value`, where:"," - `gas` is the amount of gas needed for transaction to execute,"," - `gas fee` is the gas fee,"," - `value` is the amount of ether to send to the recipient."],name:`InsufficientFundsError`})}},Object.defineProperty(Eu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/insufficient funds|exceeds transaction sender account balance/}),Du=class extends D{constructor({cause:e,gas:t}={}){super(`The amount of gas ${t?`(${t}) `:``}provided for the transaction exceeds the limit allowed for the block.`,{cause:e,name:`IntrinsicGasTooHighError`})}},Object.defineProperty(Du,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/intrinsic gas too high|gas limit reached/}),Ou=class extends D{constructor({cause:e,gas:t}={}){super(`The amount of gas ${t?`(${t}) `:``}provided for the transaction is too low.`,{cause:e,name:`IntrinsicGasTooLowError`})}},Object.defineProperty(Ou,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/intrinsic gas too low/}),ku=class extends D{constructor({cause:e}){super(`The transaction type is not supported for this chain.`,{cause:e,name:`TransactionTypeNotSupportedError`})}},Object.defineProperty(ku,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/transaction type not valid/}),Au=class extends D{constructor({cause:e,maxPriorityFeePerGas:t,maxFeePerGas:n}={}){super([`The provided tip (\`maxPriorityFeePerGas\`${t?` = ${Za(t)} gwei`:``}) cannot be higher than the fee cap (\`maxFeePerGas\`${n?` = ${Za(n)} gwei`:``}).`].join(` -`),{cause:e,name:`TipAboveFeeCapError`})}},Object.defineProperty(Au,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max priority fee per gas higher than max fee per gas|tip higher than fee cap/}),ju=class extends D{constructor({cause:e}){super(`An error occurred while executing: ${e?.shortMessage}`,{cause:e,name:`UnknownNodeError`})}}}));function Nu(e,t){let n=(e.details||``).toLowerCase(),r=e instanceof D?e.walk(e=>e?.code===bu.code):e;return r instanceof D?new bu({cause:e,message:r.details}):bu.nodeMessage.test(n)?new bu({cause:e,message:e.details}):xu.nodeMessage.test(n)?new xu({cause:e,maxFeePerGas:t?.maxFeePerGas}):Su.nodeMessage.test(n)?new Su({cause:e,maxFeePerGas:t?.maxFeePerGas}):Cu.nodeMessage.test(n)?new Cu({cause:e,nonce:t?.nonce}):wu.nodeMessage.test(n)?new wu({cause:e,nonce:t?.nonce}):Tu.nodeMessage.test(n)?new Tu({cause:e,nonce:t?.nonce}):Eu.nodeMessage.test(n)?new Eu({cause:e}):Du.nodeMessage.test(n)?new Du({cause:e,gas:t?.gas}):Ou.nodeMessage.test(n)?new Ou({cause:e,gas:t?.gas}):ku.nodeMessage.test(n)?new ku({cause:e}):Au.nodeMessage.test(n)?new Au({cause:e,maxFeePerGas:t?.maxFeePerGas,maxPriorityFeePerGas:t?.maxPriorityFeePerGas}):new ju({cause:e})}var Pu=o((()=>{O(),Mu()}));Mu(),Pu();function Fu(e,{docsPath:t,...n}){let r=(()=>{let t=Nu(e,n);return t instanceof ju?e:t})();return new yu(r,{docsPath:t,...n})}function Iu(e,{format:t}){if(!t)return{};let n={};function r(t){let i=Object.keys(t);for(let a of i)a in e&&(n[a]=e[a]),t[a]&&typeof t[a]==`object`&&!Array.isArray(t[a])&&r(t[a])}let i=t(e||{});return r(i),n}var Lu=o((()=>{}));function Ru(e,t){return({exclude:n,format:r})=>({exclude:n,format:(e,i)=>{let a=t(e,i);if(n)for(let e of n)delete a[e];return{...a,...r(e,i)}},type:e})}var zu=o((()=>{}));function Bu(e,t){let n={};return e.authorizationList!==void 0&&(n.authorizationList=Vu(e.authorizationList)),e.accessList!==void 0&&(n.accessList=e.accessList),e.blobVersionedHashes!==void 0&&(n.blobVersionedHashes=e.blobVersionedHashes),e.blobs!==void 0&&(typeof e.blobs[0]==`string`?n.blobs=e.blobs:n.blobs=e.blobs.map(e=>An(e))),e.data!==void 0&&(n.data=e.data),e.from!==void 0&&(n.from=e.from),e.gas!==void 0&&(n.gas=k(e.gas)),e.gasPrice!==void 0&&(n.gasPrice=k(e.gasPrice)),e.maxFeePerBlobGas!==void 0&&(n.maxFeePerBlobGas=k(e.maxFeePerBlobGas)),e.maxFeePerGas!==void 0&&(n.maxFeePerGas=k(e.maxFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(n.maxPriorityFeePerGas=k(e.maxPriorityFeePerGas)),e.nonce!==void 0&&(n.nonce=k(e.nonce)),e.to!==void 0&&(n.to=e.to),e.type!==void 0&&(n.type=Hu[e.type]),e.value!==void 0&&(n.value=k(e.value)),n}function Vu(e){return e.map(e=>({address:e.address,r:e.r?k(BigInt(e.r)):e.r,s:e.s?k(BigInt(e.s)):e.s,chainId:k(e.chainId),nonce:k(e.nonce),...e.yParity===void 0?{}:{yParity:k(e.yParity)},...e.v!==void 0&&e.yParity===void 0?{v:k(e.v)}:{}}))}var Hu,Uu,Wu=o((()=>{A(),zu(),Hu={legacy:`0x0`,eip2930:`0x1`,eip1559:`0x2`,eip4844:`0x3`,eip7702:`0x4`},Uu=Ru(`transactionRequest`,Bu)}));function Gu(e){if(!(!e||e.length===0))return e.reduce((e,{slot:t,value:n})=>{if(t.length!==66)throw new un({size:t.length,targetSize:66,type:`hex`});if(n.length!==66)throw new un({size:n.length,targetSize:66,type:`hex`});return e[t]=n,e},{})}function Ku(e){let{balance:t,nonce:n,state:r,stateDiff:i,code:a}=e,o={};if(a!==void 0&&(o.code=a),t!==void 0&&(o.balance=k(t)),n!==void 0&&(o.nonce=k(n)),r!==void 0&&(o.state=Gu(r)),i!==void 0){if(o.state)throw new no;o.stateDiff=Gu(i)}return o}function qu(e){if(!e)return;let t={};for(let{address:n,...r}of e){if(!bi(n,{strict:!1}))throw new fi({address:n});if(t[n])throw new to({address:n});t[n]=Ku(r)}return t}var Ju=o((()=>{pi(),dn(),ro(),Ci(),A()})),Yu,Xu,Zu=o((()=>{2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n)),Yu=2n**16n-1n,Xu=2n**256n-1n}));function Qu(e){let{account:t,gasPrice:n,maxFeePerGas:r,maxPriorityFeePerGas:i,to:a}=e,o=t?na(t):void 0;if(o&&!bi(o.address))throw new fi({address:o.address});if(a&&!bi(a))throw new fi({address:a});if(n!==void 0&&(r!==void 0||i!==void 0))throw new ao;if(r&&r>Xu)throw new xu({maxFeePerGas:r});if(i&&r&&i>r)throw new Au({maxFeePerGas:r,maxPriorityFeePerGas:i})}var $u=o((()=>{ra(),Zu(),pi(),Mu(),ho(),Ci()}));Qa(),O();var ed=class extends D{constructor(){super("`baseFeeMultiplier` must be greater than 1.",{name:`BaseFeeScalarError`})}},td=class extends D{constructor(){super(`Chain does not support EIP-1559 fees.`,{name:`Eip1559FeesNotSupportedError`})}},nd=class extends D{constructor({maxPriorityFeePerGas:e}){super(`\`maxFeePerGas\` cannot be less than the \`maxPriorityFeePerGas\` (${Za(e)} gwei).`,{name:`MaxFeePerGasTooLowError`})}};O();var rd=class extends D{constructor({blockHash:e,blockNumber:t}){let n=`Block`;e&&(n=`Block at hash "${e}"`),t&&(n=`Block at number "${t}"`),super(`${n} could not be found.`,{name:`BlockNotFoundError`})}};Dn(),zu();const id={"0x0":`legacy`,"0x1":`eip2930`,"0x2":`eip1559`,"0x3":`eip4844`,"0x4":`eip7702`};function ad(e,t){let n={...e,blockHash:e.blockHash?e.blockHash:null,blockNumber:e.blockNumber?BigInt(e.blockNumber):null,chainId:e.chainId?En(e.chainId):void 0,gas:e.gas?BigInt(e.gas):void 0,gasPrice:e.gasPrice?BigInt(e.gasPrice):void 0,maxFeePerBlobGas:e.maxFeePerBlobGas?BigInt(e.maxFeePerBlobGas):void 0,maxFeePerGas:e.maxFeePerGas?BigInt(e.maxFeePerGas):void 0,maxPriorityFeePerGas:e.maxPriorityFeePerGas?BigInt(e.maxPriorityFeePerGas):void 0,nonce:e.nonce?En(e.nonce):void 0,to:e.to?e.to:null,transactionIndex:e.transactionIndex?Number(e.transactionIndex):null,type:e.type?id[e.type]:void 0,typeHex:e.type?e.type:void 0,value:e.value?BigInt(e.value):void 0,v:e.v?BigInt(e.v):void 0};return e.authorizationList&&(n.authorizationList=sd(e.authorizationList)),n.yParity=(()=>{if(e.yParity)return Number(e.yParity);if(typeof n.v==`bigint`){if(n.v===0n||n.v===27n)return 0;if(n.v===1n||n.v===28n)return 1;if(n.v>=35n)return n.v%2n==0n?1:0}})(),n.type===`legacy`&&(delete n.accessList,delete n.maxFeePerBlobGas,delete n.maxFeePerGas,delete n.maxPriorityFeePerGas,delete n.yParity),n.type===`eip2930`&&(delete n.maxFeePerBlobGas,delete n.maxFeePerGas,delete n.maxPriorityFeePerGas),n.type===`eip1559`&&delete n.maxFeePerBlobGas,n}const od=Ru(`transaction`,ad);function sd(e){return e.map(e=>({address:e.address,chainId:Number(e.chainId),nonce:Number(e.nonce),r:e.r,s:e.s,yParity:Number(e.yParity)}))}zu();function cd(e,t){let n=(e.transactions??[]).map(e=>typeof e==`string`?e:ad(e));return{...e,baseFeePerGas:e.baseFeePerGas?BigInt(e.baseFeePerGas):null,blobGasUsed:e.blobGasUsed?BigInt(e.blobGasUsed):void 0,difficulty:e.difficulty?BigInt(e.difficulty):void 0,excessBlobGas:e.excessBlobGas?BigInt(e.excessBlobGas):void 0,gasLimit:e.gasLimit?BigInt(e.gasLimit):void 0,gasUsed:e.gasUsed?BigInt(e.gasUsed):void 0,hash:e.hash?e.hash:null,logsBloom:e.logsBloom?e.logsBloom:null,nonce:e.nonce?e.nonce:null,number:e.number?BigInt(e.number):null,size:e.size?BigInt(e.size):void 0,timestamp:e.timestamp?BigInt(e.timestamp):void 0,transactions:n,totalDifficulty:e.totalDifficulty?BigInt(e.totalDifficulty):null}}const ld=Ru(`block`,cd);A();async function ud(e,{blockHash:t,blockNumber:n,blockTag:r=e.experimental_blockTag??`latest`,includeTransactions:i}={}){let a=i??!1,o=n===void 0?void 0:k(n),s=null;if(s=t?await e.request({method:`eth_getBlockByHash`,params:[t,a]},{dedupe:!0}):await e.request({method:`eth_getBlockByNumber`,params:[o||r,a]},{dedupe:!!o}),!s)throw new rd({blockHash:t,blockNumber:n});return(e.chain?.formatters?.block?.format||cd)(s,`getBlock`)}async function dd(e){let t=await e.request({method:`eth_gasPrice`});return BigInt(t)}Dn();async function fd(e,t){let{block:n,chain:r=e.chain,request:i}=t||{};try{let t=r?.fees?.maxPriorityFeePerGas??r?.fees?.defaultPriorityFee;if(typeof t==`function`){let r=n||await E(e,ud,`getBlock`)({}),a=await t({block:r,client:e,request:i});if(a===null)throw Error();return a}if(t!==void 0)return t;let a=await e.request({method:`eth_maxPriorityFeePerGas`});return wn(a)}catch{let[t,r]=await Promise.all([n?Promise.resolve(n):E(e,ud,`getBlock`)({}),E(e,dd,`getGasPrice`)({})]);if(typeof t.baseFeePerGas!=`bigint`)throw new td;let i=r-t.baseFeePerGas;return i<0n?0n:i}}async function pd(e,t){let{block:n,chain:r=e.chain,request:i,type:a=`eip1559`}=t||{},o=await(async()=>typeof r?.fees?.baseFeeMultiplier==`function`?r.fees.baseFeeMultiplier({block:n,client:e,request:i}):r?.fees?.baseFeeMultiplier??1.2)();if(o<1)throw new ed;let s=10**(o.toString().split(`.`)[1]?.length??0),c=e=>e*BigInt(Math.ceil(o*s))/BigInt(s),l=n||await E(e,ud,`getBlock`)({});if(typeof r?.fees?.estimateFeesPerGas==`function`){let t=await r.fees.estimateFeesPerGas({block:n,client:e,multiply:c,request:i,type:a});if(t!==null)return t}if(a===`eip1559`){if(typeof l.baseFeePerGas!=`bigint`)throw new td;let t=typeof i?.maxPriorityFeePerGas==`bigint`?i.maxPriorityFeePerGas:await fd(e,{block:l,chain:r,request:i}),n=c(l.baseFeePerGas);return{maxFeePerGas:i?.maxFeePerGas??n+t,maxPriorityFeePerGas:t}}return{gasPrice:i?.gasPrice??c(await E(e,dd,`getGasPrice`)({}))}}Dn(),A();async function md(e,{address:t,blockTag:n=`latest`,blockNumber:r}){let i=await e.request({method:`eth_getTransactionCount`,params:[t,typeof r==`bigint`?k(r):n]},{dedupe:!!r});return En(i)}Wu(),Hn(),A();function hd(e){let{kzg:t}=e,n=e.to??(typeof e.blobs[0]==`string`?`hex`:`bytes`),r=typeof e.blobs[0]==`string`?e.blobs.map(e=>Ln(e)):e.blobs,i=[];for(let e of r)i.push(Uint8Array.from(t.blobToKzgCommitment(e)));return n===`bytes`?i:i.map(e=>An(e))}Hn(),A();function gd(e){let{kzg:t}=e,n=e.to??(typeof e.blobs[0]==`string`?`hex`:`bytes`),r=typeof e.blobs[0]==`string`?e.blobs.map(e=>Ln(e)):e.blobs,i=typeof e.commitments[0]==`string`?e.commitments.map(e=>Ln(e)):e.commitments,a=[];for(let e=0;eAn(e))}Is();const _d=Ns;Pt(),Hn(),A();function vd(e,t){let n=t||`hex`,r=_d(Nt(e,{strict:!1})?Pn(e):e);return n===`bytes`?r:On(r)}A();function yd(e){let{commitment:t,version:n=1}=e,r=e.to??(typeof t==`string`?`hex`:`bytes`),i=vd(t,`bytes`);return i.set([n],0),r===`bytes`?i:An(i)}function bd(e){let{commitments:t,version:n}=e,r=e.to??(typeof t[0]==`string`?`hex`:`bytes`),i=[];for(let e of t)i.push(yd({commitment:e,to:r,version:n}));return i}var xd=6;const Sd=4096,Cd=32*Sd,wd=Cd*xd-1-1*Sd*xd;O();var Td=class extends D{constructor({maxSize:e,size:t}){super(`Blob size is too large.`,{metaMessages:[`Max: ${e} bytes`,`Given: ${t} bytes`],name:`BlobSizeTooLargeError`})}},Ed=class extends D{constructor(){super(`Blob data must not be empty.`,{name:`EmptyBlobError`})}},Dd=class extends D{constructor({hash:e,size:t}){super(`Versioned hash "${e}" size is invalid.`,{metaMessages:[`Expected: 32`,`Received: ${t}`],name:`InvalidVersionedHashSizeError`})}},Od=class extends D{constructor({hash:e,version:t}){super(`Versioned hash "${e}" version is invalid.`,{metaMessages:[`Expected: 1`,`Received: ${t}`],name:`InvalidVersionedHashVersionError`})}};ya(),It(),Hn(),A();function kd(e){let t=e.to??(typeof e.data==`string`?`hex`:`bytes`),n=typeof e.data==`string`?Ln(e.data):e.data,r=Ft(n);if(!r)throw new Ed;if(r>761855)throw new Td({maxSize:wd,size:r});let i=[],a=!0,o=0;for(;a;){let e=_a(new Uint8Array(Cd)),t=0;for(;te.bytes):i.map(e=>An(e.bytes))}function Ad(e){let{data:t,kzg:n,to:r}=e,i=e.blobs??kd({data:t,to:r}),a=e.commitments??hd({blobs:i,kzg:n,to:r}),o=e.proofs??gd({blobs:i,commitments:a,kzg:n,to:r}),s=[];for(let e=0;e{if(v.to)return v.to;if(i&&i.length>0)return await vu({authorization:i[0]}).catch(()=>{throw new D("`to` is required. Could not infer from `authorizationList`")})})();Qu(t);let S=e.chain?.formatters?.transactionRequest?.format,C=(S||Bu)({...Iu(v,{format:S}),from:r?.address,accessList:n,authorizationList:i,blobs:a,blobVersionedHashes:o,data:l,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,to:x,value:g},`estimateGas`);return BigInt(await e.request({method:`eth_estimateGas`,params:b?[C,y??e.experimental_blockTag??`latest`,b]:y?[C,y]:[C]}))}catch(n){throw Fu(n,{...t,account:r,chain:e.chain})}}function Ld(e,t){if(!bi(e,{strict:!1}))throw new fi({address:e});if(!bi(t,{strict:!1}))throw new fi({address:t});return e.toLowerCase()===t.toLowerCase()}var Rd=o((()=>{pi(),Ci()}));function zd(e,{args:t,eventName:n}={}){return{...e,blockHash:e.blockHash?e.blockHash:null,blockNumber:e.blockNumber?BigInt(e.blockNumber):null,logIndex:e.logIndex?Number(e.logIndex):null,transactionHash:e.transactionHash?e.transactionHash:null,transactionIndex:e.transactionIndex?Number(e.transactionIndex):null,...n?{args:t,eventName:n}:{}}}function Bd(e){let{abi:t,args:n,functionName:r,data:i}=e,a=t[0];if(r){let e=Qi({abi:t,args:n,name:r});if(!e)throw new Zt(r,{docsPath:Vd});a=e}if(a.type!==`function`)throw new Zt(void 0,{docsPath:Vd});if(!a.outputs)throw new Qt(a.name,{docsPath:Vd});let o=Ta(a.outputs,i);if(o&&o.length>1)return o;if(o&&o.length===1)return o[0]}var Vd,Hd=o((()=>{sn(),La(),ta(),Vd=`/docs/contract/decodeFunctionResult`})),Ud,Wd=o((()=>{Ud=`0.1.1`}));function Gd(){return Ud}var Kd=o((()=>{Wd()}));function qd(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause?qd(e.cause,t):t?null:e}var P,Jd=o((()=>{Kd(),P=class e extends Error{constructor(t,n={}){let r=(()=>{if(n.cause instanceof e){if(n.cause.details)return n.cause.details;if(n.cause.shortMessage)return n.cause.shortMessage}return n.cause&&`details`in n.cause&&typeof n.cause.details==`string`?n.cause.details:n.cause?.message?n.cause.message:n.details})(),i=(()=>n.cause instanceof e&&n.cause.docsPath||n.docsPath)(),a=`https://oxlib.sh${i??``}`,o=[t||`An error occurred.`,...n.metaMessages?[``,...n.metaMessages]:[],...r||i?[``,r?`Details: ${r}`:void 0,i?`See: ${a}`:void 0]:[]].filter(e=>typeof e==`string`).join(` -`);super(o,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:`ox@${Gd()}`}),this.cause=n.cause,this.details=r,this.docs=a,this.docsPath=i,this.shortMessage=t}walk(e){return qd(this,e)}}}));function Yd(e,t){if(bf(e)>t)throw new Pf({givenSize:bf(e),maxSize:t})}function Xd(e,t){if(typeof t==`number`&&t>0&&t>bf(e)-1)throw new Ff({offset:t,position:`start`,size:bf(e)})}function Zd(e,t,n){if(typeof t==`number`&&typeof n==`number`&&bf(e)!==n-t)throw new Ff({offset:n,position:`end`,size:bf(e)})}function F(e){if(e>=ef.zero&&e<=ef.nine)return e-ef.zero;if(e>=ef.A&&e<=ef.F)return e-(ef.A-10);if(e>=ef.a&&e<=ef.f)return e-(ef.a-10)}function Qd(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;if(e.length>r)throw new If({size:e.length,targetSize:r,type:`Bytes`});let i=new Uint8Array(r);for(let t=0;t{Lf(),ef={zero:48,nine:57,A:65,F:70,a:97,f:102}}));function nf(e,t){if(qf(e)>t)throw new rp({givenSize:qf(e),maxSize:t})}function rf(e,t){if(typeof t==`number`&&t>0&&t>qf(e)-1)throw new ip({offset:t,position:`start`,size:qf(e)})}function af(e,t,n){if(typeof t==`number`&&typeof n==`number`&&qf(e)!==n-t)throw new ip({offset:n,position:`end`,size:qf(e)})}function sf(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;let i=e.replace(`0x`,``);if(i.length>r*2)throw new ap({size:Math.ceil(i.length/2),targetSize:r,type:`Hex`});return`0x${i[n===`right`?`padEnd`:`padStart`](r*2,`0`)}`}function cf(e,t={}){let{dir:n=`left`}=t,r=e.replace(`0x`,``),i=0;for(let e=0;e{op()}));function uf(e,t){return JSON.parse(e,(e,n)=>{let r=n;return typeof r==`string`&&r.endsWith(ff)?BigInt(r.slice(0,-9)):typeof t==`function`?t(e,r):r})}function df(e,t,n){return JSON.stringify(e,(e,n)=>typeof t==`function`?t(e,n):typeof n==`bigint`?n.toString()+ff:n,n)}var ff,pf=o((()=>{ff=`#__bigint`}));function mf(e){if(!(e instanceof Uint8Array)&&(!e||typeof e!=`object`||!(`BYTES_PER_ELEMENT`in e)||e.BYTES_PER_ELEMENT!==1||e.constructor.name!==`Uint8Array`))throw new Nf(e)}function hf(e){return e instanceof Uint8Array?e:typeof e==`string`?_f(e):gf(e)}function gf(e){return e instanceof Uint8Array?e:new Uint8Array(e)}function _f(e,t={}){let{size:n}=t,r=e;n&&(nf(e,n),r=Gf(e,n));let i=r.slice(2);i.length%2&&(i=`0${i}`);let a=i.length/2,o=new Uint8Array(a);for(let e=0,t=0;e1||r[0]>1)throw new Mf(r);return!!r[0]}function wf(e,t={}){return Hf(e,t)}function Tf(e,t={}){let{size:n}=t;n!==void 0&&Yd(e,n);let r=Hf(e,t);return Xf(r,t)}function Ef(e,t={}){let{size:n}=t,r=e;return n!==void 0&&(Yd(r,n),r=Of(r)),Af.decode(r)}function Df(e){return $d(e,{dir:`left`})}function Of(e){return $d(e,{dir:`right`})}function kf(e){try{return mf(e),!0}catch{return!1}}var Af,jf,Mf,Nf,Pf,Ff,If,Lf=o((()=>{Jd(),op(),tf(),lf(),pf(),Af=new TextDecoder,jf=new TextEncoder,Mf=class extends P{constructor(e){super(`Bytes value \`${e}\` is not a valid boolean.`,{metaMessages:["The bytes array must contain a single byte of either a `0` or `1` value."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.InvalidBytesBooleanError`})}},Nf=class extends P{constructor(e){super(`Value \`${typeof e==`object`?df(e):e}\` of type \`${typeof e}\` is an invalid Bytes value.`,{metaMessages:["Bytes values must be of type `Bytes`."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.InvalidBytesTypeError`})}},Pf=class extends P{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SizeOverflowError`})}},Ff=class extends P{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SliceOffsetOutOfBoundsError`})}},If=class extends P{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SizeExceedsPaddingSizeError`})}}}));function Rf(e,t={}){let{strict:n=!1}=t;if(!e||typeof e!=`string`)throw new tp(e);if(n&&!/^0x[0-9a-fA-F]*$/.test(e)||!e.startsWith(`0x`))throw new np(e)}function zf(...e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}function Bf(e){return e instanceof Uint8Array?Hf(e):Array.isArray(e)?Hf(new Uint8Array(e)):e}function Vf(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(nf(n,t.size),Wf(n,t.size)):n}function Hf(e,t={}){let n=``;for(let t=0;ta||i>1n;return r<=o?r:r-a-1n}function Xf(e,t={}){let{signed:n,size:r}=t;return!n&&!r?Number(e):Number(Yf(e,t))}function Zf(e,t={}){let{strict:n=!1}=t;try{return Rf(e,{strict:n}),!0}catch{return!1}}var Qf,$f,ep,tp,np,rp,ip,ap,op=o((()=>{Jd(),lf(),pf(),Qf=new TextEncoder,$f=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),ep=class extends P{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number \`${i}\` is not in safe${r?` ${r*8}-bit`:``}${n?` signed`:` unsigned`} integer range ${e?`(\`${t}\` to \`${e}\`)`:`(above \`${t}\`)`}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.IntegerOutOfRangeError`})}},tp=class extends P{constructor(e){super(`Value \`${typeof e==`object`?df(e):e}\` of type \`${typeof e}\` is an invalid hex type.`,{metaMessages:['Hex types must be represented as `"0x${string}"`.']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexTypeError`})}},np=class extends P{constructor(e){super(`Value \`${e}\` is an invalid hex value.`,{metaMessages:['Hex values must start with `"0x"` and contain only hexadecimal characters (0-9, a-f, A-F).']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexValueError`})}},rp=class extends P{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeOverflowError`})}},ip=class extends P{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SliceOffsetOutOfBoundsError`})}},ap=class extends P{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeExceedsPaddingSizeError`})}}}));function sp(e){return{address:e.address,amount:I(e.amount),index:I(e.index),validatorIndex:I(e.validatorIndex)}}var cp=o((()=>{op()}));function lp(e){return{...typeof e.baseFeePerGas==`bigint`&&{baseFeePerGas:I(e.baseFeePerGas)},...typeof e.blobBaseFee==`bigint`&&{blobBaseFee:I(e.blobBaseFee)},...typeof e.feeRecipient==`string`&&{feeRecipient:e.feeRecipient},...typeof e.gasLimit==`bigint`&&{gasLimit:I(e.gasLimit)},...typeof e.number==`bigint`&&{number:I(e.number)},...typeof e.prevRandao==`bigint`&&{prevRandao:I(e.prevRandao)},...typeof e.time==`bigint`&&{time:I(e.time)},...e.withdrawals&&{withdrawals:e.withdrawals.map(sp)}}}var up=o((()=>{op(),cp()})),dp,fp,pp,mp,hp,gp=o((()=>{dp=[{inputs:[{components:[{name:`target`,type:`address`},{name:`allowFailure`,type:`bool`},{name:`callData`,type:`bytes`}],name:`calls`,type:`tuple[]`}],name:`aggregate3`,outputs:[{components:[{name:`success`,type:`bool`},{name:`returnData`,type:`bytes`}],name:`returnData`,type:`tuple[]`}],stateMutability:`view`,type:`function`},{inputs:[],name:`getCurrentBlockTimestamp`,outputs:[{internalType:`uint256`,name:`timestamp`,type:`uint256`}],stateMutability:`view`,type:`function`}],fp=[{name:`query`,type:`function`,stateMutability:`view`,inputs:[{type:`tuple[]`,name:`queries`,components:[{type:`address`,name:`sender`},{type:`string[]`,name:`urls`},{type:`bytes`,name:`data`}]}],outputs:[{type:`bool[]`,name:`failures`},{type:`bytes[]`,name:`responses`}]},{name:`HttpError`,type:`error`,inputs:[{type:`uint16`,name:`status`},{type:`string`,name:`message`}]}],pp=[{inputs:[{name:`dns`,type:`bytes`}],name:`DNSDecodingFailed`,type:`error`},{inputs:[{name:`ens`,type:`string`}],name:`DNSEncodingFailed`,type:`error`},{inputs:[],name:`EmptyAddress`,type:`error`},{inputs:[{name:`status`,type:`uint16`},{name:`message`,type:`string`}],name:`HttpError`,type:`error`},{inputs:[],name:`InvalidBatchGatewayResponse`,type:`error`},{inputs:[{name:`errorData`,type:`bytes`}],name:`ResolverError`,type:`error`},{inputs:[{name:`name`,type:`bytes`},{name:`resolver`,type:`address`}],name:`ResolverNotContract`,type:`error`},{inputs:[{name:`name`,type:`bytes`}],name:`ResolverNotFound`,type:`error`},{inputs:[{name:`primary`,type:`string`},{name:`primaryAddress`,type:`bytes`}],name:`ReverseAddressMismatch`,type:`error`},{inputs:[{internalType:`bytes4`,name:`selector`,type:`bytes4`}],name:`UnsupportedResolverProfile`,type:`error`}],[...pp],[...pp],mp=[{name:`isValidSignature`,type:`function`,stateMutability:`view`,inputs:[{name:`hash`,type:`bytes32`},{name:`signature`,type:`bytes`}],outputs:[{name:``,type:`bytes4`}]}],hp=[{inputs:[{name:`_signer`,type:`address`},{name:`_hash`,type:`bytes32`},{name:`_signature`,type:`bytes`}],stateMutability:`nonpayable`,type:`constructor`},{inputs:[{name:`_signer`,type:`address`},{name:`_hash`,type:`bytes32`},{name:`_signature`,type:`bytes`}],outputs:[{type:`bool`}],stateMutability:`nonpayable`,type:`function`,name:`isValidSig`}]})),_p=o((()=>{})),vp,yp,bp,xp,Sp=o((()=>{vp=`0x608060405234801561001057600080fd5b5060405161018e38038061018e83398101604081905261002f91610124565b6000808351602085016000f59050803b61004857600080fd5b6000808351602085016000855af16040513d6000823e81610067573d81fd5b3d81f35b634e487b7160e01b600052604160045260246000fd5b600082601f83011261009257600080fd5b81516001600160401b038111156100ab576100ab61006b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156100d9576100d961006b565b6040528181528382016020018510156100f157600080fd5b60005b82811015610110576020818601810151838301820152016100f4565b506000918101602001919091529392505050565b6000806040838503121561013757600080fd5b82516001600160401b0381111561014d57600080fd5b61015985828601610081565b602085015190935090506001600160401b0381111561017757600080fd5b61018385828601610081565b915050925092905056fe`,yp=`0x608060405234801561001057600080fd5b506040516102c03803806102c083398101604081905261002f916101e6565b836001600160a01b03163b6000036100e457600080836001600160a01b03168360405161005c9190610270565b6000604051808303816000865af19150503d8060008114610099576040519150601f19603f3d011682016040523d82523d6000602084013e61009e565b606091505b50915091508115806100b857506001600160a01b0386163b155b156100e1578060405163101bb98d60e01b81526004016100d8919061028c565b60405180910390fd5b50505b6000808451602086016000885af16040513d6000823e81610103573d81fd5b3d81f35b80516001600160a01b038116811461011e57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561015457818101518382015260200161013c565b50506000910152565b600082601f83011261016e57600080fd5b81516001600160401b0381111561018757610187610123565b604051601f8201601f19908116603f011681016001600160401b03811182821017156101b5576101b5610123565b6040528181528382016020018510156101cd57600080fd5b6101de826020830160208701610139565b949350505050565b600080600080608085870312156101fc57600080fd5b61020585610107565b60208601519094506001600160401b0381111561022157600080fd5b61022d8782880161015d565b93505061023c60408601610107565b60608601519092506001600160401b0381111561025857600080fd5b6102648782880161015d565b91505092959194509250565b60008251610282818460208701610139565b9190910192915050565b60208152600082518060208401526102ab816040850160208701610139565b601f01601f1916919091016040019291505056fe`,bp=`0x608060405234801561001057600080fd5b5060405161069438038061069483398101604081905261002f9161051e565b600061003c848484610048565b9050806000526001601ff35b60007f64926492649264926492649264926492649264926492649264926492649264926100748361040c565b036101e7576000606080848060200190518101906100929190610577565b60405192955090935091506000906001600160a01b038516906100b69085906105dd565b6000604051808303816000865af19150503d80600081146100f3576040519150601f19603f3d011682016040523d82523d6000602084013e6100f8565b606091505b50509050876001600160a01b03163b60000361016057806101605760405162461bcd60e51b815260206004820152601e60248201527f5369676e617475726556616c696461746f723a206465706c6f796d656e74000060448201526064015b60405180910390fd5b604051630b135d3f60e11b808252906001600160a01b038a1690631626ba7e90610190908b9087906004016105f9565b602060405180830381865afa1580156101ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d19190610633565b6001600160e01b03191614945050505050610405565b6001600160a01b0384163b1561027a57604051630b135d3f60e11b808252906001600160a01b03861690631626ba7e9061022790879087906004016105f9565b602060405180830381865afa158015610244573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102689190610633565b6001600160e01b031916149050610405565b81516041146102df5760405162461bcd60e51b815260206004820152603a602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610157565b6102e7610425565b5060208201516040808401518451859392600091859190811061030c5761030c61065d565b016020015160f81c9050601b811480159061032b57508060ff16601c14155b1561038c5760405162461bcd60e51b815260206004820152603b602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e617475726520762076616c756500000000006064820152608401610157565b60408051600081526020810180835289905260ff83169181019190915260608101849052608081018390526001600160a01b0389169060019060a0016020604051602081039080840390855afa1580156103ea573d6000803e3d6000fd5b505050602060405103516001600160a01b0316149450505050505b9392505050565b600060208251101561041d57600080fd5b508051015190565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b038116811461045857600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561048c578181015183820152602001610474565b50506000910152565b600082601f8301126104a657600080fd5b81516001600160401b038111156104bf576104bf61045b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156104ed576104ed61045b565b60405281815283820160200185101561050557600080fd5b610516826020830160208701610471565b949350505050565b60008060006060848603121561053357600080fd5b835161053e81610443565b6020850151604086015191945092506001600160401b0381111561056157600080fd5b61056d86828701610495565b9150509250925092565b60008060006060848603121561058c57600080fd5b835161059781610443565b60208501519093506001600160401b038111156105b357600080fd5b6105bf86828701610495565b604086015190935090506001600160401b0381111561056157600080fd5b600082516105ef818460208701610471565b9190910192915050565b828152604060208201526000825180604084015261061e816060850160208701610471565b601f01601f1916919091016060019392505050565b60006020828403121561064557600080fd5b81516001600160e01b03198116811461040557600080fd5b634e487b7160e01b600052603260045260246000fdfe5369676e617475726556616c696461746f72237265636f7665725369676e6572`,xp=`0x608060405234801561001057600080fd5b506115b9806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e14610325578063bce38bd714610350578063c3077fa914610380578063ee82ac5e146103b2576100f3565b80634d2301cc1461026257806372425d9d1461029f57806382ad56cb146102ca57806386d516e8146102fa576100f3565b80633408e470116100c65780633408e470146101af578063399542e9146101da5780633e64a6961461020c57806342cbb15c14610237576100f3565b80630f28c97d146100f8578063174dea7114610123578063252dba421461015357806327e86d6e14610184575b600080fd5b34801561010457600080fd5b5061010d6103ef565b60405161011a9190610c0a565b60405180910390f35b61013d60048036038101906101389190610c94565b6103f7565b60405161014a9190610e94565b60405180910390f35b61016d60048036038101906101689190610f0c565b610615565b60405161017b92919061101b565b60405180910390f35b34801561019057600080fd5b506101996107ab565b6040516101a69190611064565b60405180910390f35b3480156101bb57600080fd5b506101c46107b7565b6040516101d19190610c0a565b60405180910390f35b6101f460048036038101906101ef91906110ab565b6107bf565b6040516102039392919061110b565b60405180910390f35b34801561021857600080fd5b506102216107e1565b60405161022e9190610c0a565b60405180910390f35b34801561024357600080fd5b5061024c6107e9565b6040516102599190610c0a565b60405180910390f35b34801561026e57600080fd5b50610289600480360381019061028491906111a7565b6107f1565b6040516102969190610c0a565b60405180910390f35b3480156102ab57600080fd5b506102b4610812565b6040516102c19190610c0a565b60405180910390f35b6102e460048036038101906102df919061122a565b61081a565b6040516102f19190610e94565b60405180910390f35b34801561030657600080fd5b5061030f6109e4565b60405161031c9190610c0a565b60405180910390f35b34801561033157600080fd5b5061033a6109ec565b6040516103479190611286565b60405180910390f35b61036a600480360381019061036591906110ab565b6109f4565b6040516103779190610e94565b60405180910390f35b61039a60048036038101906103959190610f0c565b610ba6565b6040516103a99392919061110b565b60405180910390f35b3480156103be57600080fd5b506103d960048036038101906103d491906112cd565b610bca565b6040516103e69190611064565b60405180910390f35b600042905090565b60606000808484905090508067ffffffffffffffff81111561041c5761041b6112fa565b5b60405190808252806020026020018201604052801561045557816020015b610442610bd5565b81526020019060019003908161043a5790505b5092503660005b828110156105c957600085828151811061047957610478611329565b5b6020026020010151905087878381811061049657610495611329565b5b90506020028101906104a89190611367565b925060008360400135905080860195508360000160208101906104cb91906111a7565b73ffffffffffffffffffffffffffffffffffffffff16818580606001906104f2919061138f565b604051610500929190611431565b60006040518083038185875af1925050503d806000811461053d576040519150601f19603f3d011682016040523d82523d6000602084013e610542565b606091505b5083600001846020018290528215151515815250505081516020850135176105bc577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b826001019250505061045c565b5082341461060c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610603906114a7565b60405180910390fd5b50505092915050565b6000606043915060008484905090508067ffffffffffffffff81111561063e5761063d6112fa565b5b60405190808252806020026020018201604052801561067157816020015b606081526020019060019003908161065c5790505b5091503660005b828110156107a157600087878381811061069557610694611329565b5b90506020028101906106a791906114c7565b92508260000160208101906106bc91906111a7565b73ffffffffffffffffffffffffffffffffffffffff168380602001906106e2919061138f565b6040516106f0929190611431565b6000604051808303816000865af19150503d806000811461072d576040519150601f19603f3d011682016040523d82523d6000602084013e610732565b606091505b5086848151811061074657610745611329565b5b60200260200101819052819250505080610795576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078c9061153b565b60405180910390fd5b81600101915050610678565b5050509250929050565b60006001430340905090565b600046905090565b6000806060439250434091506107d68686866109f4565b905093509350939050565b600048905090565b600043905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b600044905090565b606060008383905090508067ffffffffffffffff81111561083e5761083d6112fa565b5b60405190808252806020026020018201604052801561087757816020015b610864610bd5565b81526020019060019003908161085c5790505b5091503660005b828110156109db57600084828151811061089b5761089a611329565b5b602002602001015190508686838181106108b8576108b7611329565b5b90506020028101906108ca919061155b565b92508260000160208101906108df91906111a7565b73ffffffffffffffffffffffffffffffffffffffff16838060400190610905919061138f565b604051610913929190611431565b6000604051808303816000865af19150503d8060008114610950576040519150601f19603f3d011682016040523d82523d6000602084013e610955565b606091505b5082600001836020018290528215151515815250505080516020840135176109cf577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b8160010191505061087e565b50505092915050565b600045905090565b600041905090565b606060008383905090508067ffffffffffffffff811115610a1857610a176112fa565b5b604051908082528060200260200182016040528015610a5157816020015b610a3e610bd5565b815260200190600190039081610a365790505b5091503660005b82811015610b9c576000848281518110610a7557610a74611329565b5b60200260200101519050868683818110610a9257610a91611329565b5b9050602002810190610aa491906114c7565b9250826000016020810190610ab991906111a7565b73ffffffffffffffffffffffffffffffffffffffff16838060200190610adf919061138f565b604051610aed929190611431565b6000604051808303816000865af19150503d8060008114610b2a576040519150601f19603f3d011682016040523d82523d6000602084013e610b2f565b606091505b508260000183602001829052821515151581525050508715610b90578060000151610b8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b869061153b565b60405180910390fd5b5b81600101915050610a58565b5050509392505050565b6000806060610bb7600186866107bf565b8093508194508295505050509250925092565b600081409050919050565b6040518060400160405280600015158152602001606081525090565b6000819050919050565b610c0481610bf1565b82525050565b6000602082019050610c1f6000830184610bfb565b92915050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f840112610c5457610c53610c2f565b5b8235905067ffffffffffffffff811115610c7157610c70610c34565b5b602083019150836020820283011115610c8d57610c8c610c39565b5b9250929050565b60008060208385031215610cab57610caa610c25565b5b600083013567ffffffffffffffff811115610cc957610cc8610c2a565b5b610cd585828601610c3e565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60008115159050919050565b610d2281610d0d565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610d62578082015181840152602081019050610d47565b83811115610d71576000848401525b50505050565b6000601f19601f8301169050919050565b6000610d9382610d28565b610d9d8185610d33565b9350610dad818560208601610d44565b610db681610d77565b840191505092915050565b6000604083016000830151610dd96000860182610d19565b5060208301518482036020860152610df18282610d88565b9150508091505092915050565b6000610e0a8383610dc1565b905092915050565b6000602082019050919050565b6000610e2a82610ce1565b610e348185610cec565b935083602082028501610e4685610cfd565b8060005b85811015610e825784840389528151610e638582610dfe565b9450610e6e83610e12565b925060208a01995050600181019050610e4a565b50829750879550505050505092915050565b60006020820190508181036000830152610eae8184610e1f565b905092915050565b60008083601f840112610ecc57610ecb610c2f565b5b8235905067ffffffffffffffff811115610ee957610ee8610c34565b5b602083019150836020820283011115610f0557610f04610c39565b5b9250929050565b60008060208385031215610f2357610f22610c25565b5b600083013567ffffffffffffffff811115610f4157610f40610c2a565b5b610f4d85828601610eb6565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000610f918383610d88565b905092915050565b6000602082019050919050565b6000610fb182610f59565b610fbb8185610f64565b935083602082028501610fcd85610f75565b8060005b858110156110095784840389528151610fea8582610f85565b9450610ff583610f99565b925060208a01995050600181019050610fd1565b50829750879550505050505092915050565b60006040820190506110306000830185610bfb565b81810360208301526110428184610fa6565b90509392505050565b6000819050919050565b61105e8161104b565b82525050565b60006020820190506110796000830184611055565b92915050565b61108881610d0d565b811461109357600080fd5b50565b6000813590506110a58161107f565b92915050565b6000806000604084860312156110c4576110c3610c25565b5b60006110d286828701611096565b935050602084013567ffffffffffffffff8111156110f3576110f2610c2a565b5b6110ff86828701610eb6565b92509250509250925092565b60006060820190506111206000830186610bfb565b61112d6020830185611055565b818103604083015261113f8184610e1f565b9050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061117482611149565b9050919050565b61118481611169565b811461118f57600080fd5b50565b6000813590506111a18161117b565b92915050565b6000602082840312156111bd576111bc610c25565b5b60006111cb84828501611192565b91505092915050565b60008083601f8401126111ea576111e9610c2f565b5b8235905067ffffffffffffffff81111561120757611206610c34565b5b60208301915083602082028301111561122357611222610c39565b5b9250929050565b6000806020838503121561124157611240610c25565b5b600083013567ffffffffffffffff81111561125f5761125e610c2a565b5b61126b858286016111d4565b92509250509250929050565b61128081611169565b82525050565b600060208201905061129b6000830184611277565b92915050565b6112aa81610bf1565b81146112b557600080fd5b50565b6000813590506112c7816112a1565b92915050565b6000602082840312156112e3576112e2610c25565b5b60006112f1848285016112b8565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b60008235600160800383360303811261138357611382611358565b5b80830191505092915050565b600080833560016020038436030381126113ac576113ab611358565b5b80840192508235915067ffffffffffffffff8211156113ce576113cd61135d565b5b6020830192506001820236038313156113ea576113e9611362565b5b509250929050565b600081905092915050565b82818337600083830152505050565b600061141883856113f2565b93506114258385846113fd565b82840190509392505050565b600061143e82848661140c565b91508190509392505050565b600082825260208201905092915050565b7f4d756c746963616c6c333a2076616c7565206d69736d61746368000000000000600082015250565b6000611491601a8361144a565b915061149c8261145b565b602082019050919050565b600060208201905081810360008301526114c081611484565b9050919050565b6000823560016040038336030381126114e3576114e2611358565b5b80830191505092915050565b7f4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000600082015250565b600061152560178361144a565b9150611530826114ef565b602082019050919050565b6000602082019050818103600083015261155481611518565b9050919050565b60008235600160600383360303811261157757611576611358565b5b8083019150509291505056fea264697066735822122020c1bc9aacf8e4a6507193432a895a8e77094f45a1395583f07b24e860ef06cd64736f6c634300080c0033`})),Cp,wp,Tp,Ep,Dp,Op=o((()=>{O(),Cp=class extends D{constructor({blockNumber:e,chain:t,contract:n}){super(`Chain "${t.name}" does not support contract "${n.name}".`,{metaMessages:[`This could be due to any of the following:`,...e&&n.blockCreated&&n.blockCreated>e?[`- The contract "${n.name}" was not deployed until block ${n.blockCreated} (current block ${e}).`]:[`- The chain does not have the contract "${n.name}" configured.`]],name:`ChainDoesNotSupportContract`})}},wp=class extends D{constructor({chain:e,currentChainId:t}){super(`The current chain of the wallet (id: ${t}) does not match the target chain for the transaction (id: ${e.id} – ${e.name}).`,{metaMessages:[`Current Chain ID: ${t}`,`Expected Chain ID: ${e.id} – ${e.name}`],name:`ChainMismatchError`})}},Tp=class extends D{constructor(){super([`No chain was provided to the request.`,"Please provide a chain with the `chain` argument on the Action, or by supplying a `chain` to WalletClient."].join(` -`),{name:`ChainNotFoundError`})}},Ep=class extends D{constructor(){super(`No chain was provided to the Client.`,{name:`ClientChainNotConfiguredError`})}},Dp=class extends D{constructor({chainId:e}){super(typeof e==`number`?`Chain ID "${e}" is invalid.`:`Chain ID is invalid.`,{name:`InvalidChainIdError`})}}}));function kp(e){let{abi:t,args:n,bytecode:r}=e;if(!n||n.length===0)return r;let i=t.find(e=>`type`in e&&e.type===`constructor`);if(!i)throw new Vt({docsPath:Ap});if(!(`inputs`in i)||!i.inputs||i.inputs.length===0)throw new Ht({docsPath:Ap});let a=Li(i.inputs,n);return Ei([r,a])}var Ap,jp=o((()=>{sn(),Di(),Yi(),Ap=`/docs/contract/encodeDeployData`}));function Mp({blockNumber:e,chain:t,contract:n}){let r=t?.contracts?.[n];if(!r)throw new Cp({chain:t,contract:{name:n}});if(e&&r.blockCreated&&r.blockCreated>e)throw new Cp({blockNumber:e,chain:t,contract:{name:n,blockCreated:r.blockCreated}});return r.address}var Np=o((()=>{Op()}));function Pp(e,{docsPath:t,...n}){let r=(()=>{let t=Nu(e,n);return t instanceof ju?e:t})();return new yo(r,{docsPath:t,...n})}var Fp=o((()=>{To(),Mu(),Pu()}));function Ip(){let e=()=>void 0,t=()=>void 0;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var Lp=o((()=>{}));function Rp({fn:e,id:t,shouldSplitBatch:n,wait:r=0,sort:i}){let a=async()=>{let t=c();o();let n=t.map(({args:e})=>e);n.length!==0&&e(n).then(e=>{i&&Array.isArray(e)&&e.sort(i);for(let n=0;n{for(let n=0;nzp.delete(t),s=()=>c().map(({args:e})=>e),c=()=>zp.get(t)||[],l=e=>zp.set(t,[...c(),e]);return{flush:o,async schedule(e){let{promise:t,resolve:i,reject:o}=Ip();return n?.([...s(),e])&&a(),c().length>0?(l({args:e,resolve:i,reject:o}),t):(l({args:e,resolve:i,reject:o}),setTimeout(a,r),t)}}}var zp,Bp=o((()=>{Lp(),zp=new Map})),Vp,Hp,Up,Wp=o((()=>{Va(),O(),vo(),Vp=class extends D{constructor({callbackSelector:e,cause:t,data:n,extraData:r,sender:i,urls:a}){super(t.shortMessage||`An error occurred while fetching for an offchain result.`,{cause:t,metaMessages:[...t.metaMessages||[],t.metaMessages?.length?``:[],`Offchain Gateway Call:`,a&&[` Gateway URL(s):`,...a.map(e=>` ${_o(e)}`)],` Sender: ${i}`,` Data: ${n}`,` Callback selector: ${e}`,` Extra data: ${r}`].flat(),name:`OffchainLookupError`})}},Hp=class extends D{constructor({result:e,url:t}){super(`Offchain gateway response is malformed. Response data must be a hex value.`,{metaMessages:[`Gateway URL: ${_o(t)}`,`Response: ${Ba(e)}`],name:`OffchainLookupResponseMalformedError`})}},Up=class extends D{constructor({sender:e,to:t}){super("Reverted sender address does not match target contract address (`to`).",{metaMessages:[`Contract address: ${t}`,`OffchainLookup sender address: ${e}`],name:`OffchainLookupSenderMismatchError`})}}}));function Gp(e){let{abi:t,data:n}=e,r=Oi(n,0,4),i=t.find(e=>e.type===`function`&&r===Xi(kt(e)));if(!i)throw new $t(r,{docsPath:`/docs/contract/decodeFunctionData`});return{functionName:i.name,args:`inputs`in i&&i.inputs&&i.inputs.length>0?Ta(i.inputs,Oi(n,4)):void 0}}var Kp=o((()=>{sn(),Ni(),Zi(),La(),Mt()}));function qp(e){let{abi:t,errorName:n,args:r}=e,i=t[0];if(n){let e=Qi({abi:t,args:r,name:n});if(!e)throw new Yt(n,{docsPath:Jp});i=e}if(i.type!==`error`)throw new Yt(void 0,{docsPath:Jp});let a=kt(i),o=Xi(a),s=`0x`;if(r&&r.length>0){if(!i.inputs)throw new Jt(i.name,{docsPath:Jp});s=Li(i.inputs,r)}return Ei([o,s])}var Jp,Yp=o((()=>{sn(),Di(),Zi(),Yi(),Mt(),ta(),Jp=`/docs/contract/encodeErrorResult`}));function Xp(e){let{abi:t,functionName:n,result:r}=e,i=t[0];if(n){let e=Qi({abi:t,name:n});if(!e)throw new Zt(n,{docsPath:Zp});i=e}if(i.type!==`function`)throw new Zt(void 0,{docsPath:Zp});if(!i.outputs)throw new Qt(i.name,{docsPath:Zp});let a=(()=>{if(i.outputs.length===0)return[];if(i.outputs.length===1)return[r];if(Array.isArray(r))return r;throw new an(r)})();return Li(i.outputs,a)}var Zp,Qp=o((()=>{sn(),Yi(),ta(),Zp=`/docs/contract/encodeFunctionResult`}));async function $p(e){let{data:t,ccipRequest:n}=e,{args:[r]}=Gp({abi:fp,data:t}),i=[],a=[];return await Promise.all(r.map(async(e,t)=>{try{a[t]=e.urls.includes(`x-batch-gateway:true`)?await $p({data:e.data,ccipRequest:n}):await n(e),i[t]=!1}catch(e){i[t]=!0,a[t]=em(e)}})),Xp({abi:fp,functionName:`query`,result:[i,a]})}function em(e){return e.name===`HttpRequestError`&&e.status?qp({abi:fp,errorName:`HttpError`,args:[e.status,e.shortMessage]}):qp({abi:[ua],errorName:`Error`,args:[`shortMessage`in e?e.shortMessage:e.message]})}var tm=o((()=>{gp(),fa(),Kp(),Yp(),Qp()})),nm=c({ccipRequest:()=>im,offchainLookup:()=>rm,offchainLookupAbiItem:()=>om,offchainLookupSignature:()=>am});async function rm(e,{blockNumber:t,blockTag:n,data:r,to:i}){let{args:a}=Ra({data:r,abi:[om]}),[o,s,c,l,u]=a,{ccipRead:d}=e,f=d&&typeof d?.request==`function`?d.request:im;try{if(!Ld(i,o))throw new Up({sender:o,to:i});let r=s.includes(`x-batch-gateway:true`)?await $p({data:c,ccipRequest:f}):await f({data:c,sender:o,urls:s}),{data:a}=await cm(e,{blockNumber:t,blockTag:n,data:wi([l,Li([{type:`bytes`},{type:`bytes`}],[r,u])]),to:i});return a}catch(e){throw new Vp({callbackSelector:l,cause:e,data:r,extraData:u,sender:o,urls:s})}}async function im({data:e,sender:t,urls:n}){let r=Error(`An unknown error occurred.`);for(let i=0;i{mm(),Wp(),ko(),za(),Yi(),Rd(),Di(),Pt(),tm(),Va(),am=`0x556f1830`,om={name:`OffchainLookup`,type:`error`,inputs:[{name:`sender`,type:`address`},{name:`urls`,type:`string[]`},{name:`callData`,type:`bytes`},{name:`callbackFunction`,type:`bytes4`},{name:`extraData`,type:`bytes`}]}}));async function cm(e,t){let{account:n=e.account,authorizationList:r,batch:i=!!e.batch?.multicall,blockNumber:a,blockTag:o=e.experimental_blockTag??`latest`,accessList:s,blobs:c,blockOverrides:l,code:u,data:d,factory:f,factoryData:p,gas:m,gasPrice:h,maxFeePerBlobGas:g,maxFeePerGas:_,maxPriorityFeePerGas:v,nonce:y,to:b,value:x,stateOverride:S,...C}=t,w=n?na(n):void 0;if(u&&(f||p))throw new D("Cannot provide both `code` & `factory`/`factoryData` as parameters.");if(u&&b)throw new D("Cannot provide both `code` & `to` as parameters.");let ee=u&&d,te=f&&p&&b&&d,ne=ee||te,re=(()=>ee?dm({code:u,data:d}):te?fm({data:d,factory:f,factoryData:p,to:b}):d)();try{Qu(t);let n=(typeof a==`bigint`?k(a):void 0)||o,u=l?lp(l):void 0,d=qu(S),f=e.chain?.formatters?.transactionRequest?.format,p=(f||Bu)({...Iu(C,{format:f}),from:w?.address,accessList:s,authorizationList:r,blobs:c,data:re,gas:m,gasPrice:h,maxFeePerBlobGas:g,maxFeePerGas:_,maxPriorityFeePerGas:v,nonce:y,to:ne?void 0:b,value:x},`call`);if(i&&lm({request:p})&&!d&&!u)try{return await um(e,{...p,blockNumber:a,blockTag:o})}catch(e){if(!(e instanceof Ep)&&!(e instanceof Cp))throw e}let ee=(()=>{let e=[p,n];return d&&u?[...e,d,u]:d?[...e,d]:u?[...e,{},u]:e})(),te=await e.request({method:`eth_call`,params:ee});return te===`0x`?{data:void 0}:{data:te}}catch(n){let r=pm(n),{offchainLookup:i,offchainLookupSignature:a}=await ps(async()=>{let{offchainLookup:e,offchainLookupSignature:t}=await Promise.resolve().then(()=>(sm(),nm));return{offchainLookup:e,offchainLookupSignature:t}},void 0);if(e.ccipRead!==!1&&r?.slice(0,10)===a&&b)return{data:await i(e,{data:r,to:b})};throw ne&&r?.slice(0,10)===`0x101bb98d`?new Co({factory:f}):Pp(n,{...t,account:w,chain:e.chain})}}function lm({request:e}){let{data:t,to:n,...r}=e;return!(!t||t.startsWith(`0x82ad56cb`)||!n||Object.values(r).filter(e=>e!==void 0).length>0)}async function um(e,t){let{batchSize:n=1024,deployless:r=!1,wait:i=0}=typeof e.batch?.multicall==`object`?e.batch.multicall:{},{blockNumber:a,blockTag:o=e.experimental_blockTag??`latest`,data:s,to:c}=t,l=(()=>{if(r)return null;if(t.multicallAddress)return t.multicallAddress;if(e.chain)return Mp({blockNumber:a,chain:e.chain,contract:`multicall3`});throw new Ep})(),u=(typeof a==`bigint`?k(a):void 0)||o,{schedule:d}=Rp({id:`${e.uid}.${u}`,wait:i,shouldSplitBatch(e){return e.reduce((e,{data:t})=>e+(t.length-2),0)>n*2},fn:async t=>{let n=t.map(e=>({allowFailure:!0,callData:e.data,target:e.to})),r=sa({abi:dp,args:[n],functionName:`aggregate3`}),i=await e.request({method:`eth_call`,params:[{...l===null?{data:dm({code:xp,data:r})}:{to:l,data:r}},u]});return Bd({abi:dp,args:[n],functionName:`aggregate3`,data:i||`0x`})}}),[{returnData:f,success:p}]=await d({data:s,to:c});if(!p)throw new wo({data:f});return f===`0x`?{data:void 0}:{data:f}}function dm(e){let{code:t,data:n}=e;return kp({abi:St([`constructor(bytes, bytes)`]),bytecode:vp,args:[t,n]})}function fm(e){let{data:t,factory:n,factoryData:r,to:i}=e;return kp({abi:St([`constructor(address, bytes, address, bytes)`]),bytecode:yp,args:[i,t,n,r]})}function pm(e){if(!(e instanceof D))return;let t=e.walk();return typeof t?.data==`object`?t.data?.data:t.data}var mm=o((()=>{Ot(),up(),ra(),gp(),_p(),Sp(),O(),Op(),To(),Hd(),jp(),ca(),Np(),A(),Fp(),Lu(),Wu(),Bp(),Ju(),$u(),ms()}));Hd(),ca(),mm();async function hm(e,t){let{abi:n,address:r,args:i,functionName:a,...o}=t,s=sa({abi:n,args:i,functionName:a});try{let{data:t}=await E(e,cm,`call`)({...o,data:s,to:r});return Bd({abi:n,args:i,functionName:a,data:t||`0x`})}catch(e){throw cs(e,{abi:n,address:r,args:i,docsPath:`/docs/contract/readContract`,functionName:a})}}const gm=new Map,_m=new Map;var vm=0;function ym(e,t,n){let r=++vm,i=()=>gm.get(e)||[],a=()=>{let t=i();gm.set(e,t.filter(e=>e.id!==r))},o=()=>{let t=i();if(!t.some(e=>e.id===r))return;let n=_m.get(e);if(t.length===1&&n){let e=n();e instanceof Promise&&e.catch(()=>{})}a()},s=i();if(gm.set(e,[...s,{id:r,fns:t}]),s&&s.length>0)return o;let c={};for(let e in t)c[e]=((...t)=>{let n=i();if(n.length!==0)for(let r of n)r.fns[e]?.(...t)});let l=n(c);return typeof l==`function`&&_m.set(e,l),o}async function bm(e){return new Promise(t=>setTimeout(t,e))}function xm(e,{emitOnBegin:t,initialWaitTime:n,interval:r}){let i=!0,a=()=>i=!1;return(async()=>{let o;t&&(o=await e({unpoll:a}));let s=await n?.(o)??r;await bm(s);let c=async()=>{i&&(await e({unpoll:a}),await bm(r),c())};c()})(),a}const Sm=new Map,Cm=new Map;function wm(e){let t=(e,t)=>({clear:()=>t.delete(e),get:()=>t.get(e),set:n=>t.set(e,n)}),n=t(e,Sm),r=t(e,Cm);return{clear:()=>{n.clear(),r.clear()},promise:n,response:r}}async function Tm(e,{cacheKey:t,cacheTime:n=1/0}){let r=wm(t),i=r.response.get();if(i&&n>0&&Date.now()-i.created.getTime()`blockNumber.${e}`;async function Dm(e,{cacheTime:t=e.cacheTime}={}){let n=await Tm(()=>e.request({method:`eth_blockNumber`}),{cacheKey:Em(e.uid),cacheTime:t});return BigInt(n)}O();var Om=class extends D{constructor({docsPath:e}={}){super([`Could not find an Account to execute with this Action.`,"Please provide an Account with the `account` argument on the Action, or by supplying an `account` to the Client."].join(` -`),{docsPath:e,docsSlug:`account`,name:`AccountNotFoundError`})}},km=class extends D{constructor({docsPath:e,metaMessages:t,type:n}){super(`Account type "${n}" is not supported.`,{docsPath:e,metaMessages:t,name:`AccountTypeNotSupportedError`})}};Op();function Am({chain:e,currentChainId:t}){if(!e)throw new Tp;if(t!==e.id)throw new wp({chain:e,currentChainId:t})}Mu(),ho(),Pu();function jm(e,{docsPath:t,...n}){let r=(()=>{let t=Nu(e,n);return t instanceof ju?e:t})();return new lo(r,{docsPath:t,...n})}async function Mm(e,{serializedTransaction:t}){return e.request({method:`eth_sendRawTransaction`,params:[t]},{retryCount:0})}ra(),O(),Lu(),Wu(),hi(),$u();var Nm=new mi(128);async function Pm(e,t){let{account:n=e.account,chain:r=e.chain,accessList:i,authorizationList:a,blobs:o,data:s,gas:c,gasPrice:l,maxFeePerBlobGas:u,maxFeePerGas:d,maxPriorityFeePerGas:f,nonce:p,type:m,value:h,...g}=t;if(n===void 0)throw new Om({docsPath:`/docs/actions/wallet/sendTransaction`});let _=n?na(n):null;try{Qu(t);let n=await(async()=>{if(t.to)return t.to;if(t.to!==null&&a&&a.length>0)return await vu({authorization:a[0]}).catch(()=>{throw new D("`to` is required. Could not infer from `authorizationList`.")})})();if(_?.type===`json-rpc`||_===null){let t;r!==null&&(t=await E(e,Md,`getChainId`)({}),Am({currentChainId:t,chain:r}));let v=e.chain?.formatters?.transactionRequest?.format,y=(v||Bu)({...Iu(g,{format:v}),accessList:i,authorizationList:a,blobs:o,chainId:t,data:s,from:_?.address,gas:c,gasPrice:l,maxFeePerBlobGas:u,maxFeePerGas:d,maxPriorityFeePerGas:f,nonce:p,to:n,type:m,value:h},`sendTransaction`),b=Nm.get(e.uid),x=b?`wallet_sendTransaction`:`eth_sendTransaction`;try{return await e.request({method:x,params:[y]},{retryCount:0})}catch(t){if(b===!1)throw t;let n=t;if(n.name===`InvalidInputRpcError`||n.name===`InvalidParamsRpcError`||n.name===`MethodNotFoundRpcError`||n.name===`MethodNotSupportedRpcError`)return await e.request({method:`wallet_sendTransaction`,params:[y]},{retryCount:0}).then(t=>(Nm.set(e.uid,!0),t)).catch(t=>{let r=t;throw r.name===`MethodNotFoundRpcError`||r.name===`MethodNotSupportedRpcError`?(Nm.set(e.uid,!1),n):r});throw n}}if(_?.type===`local`){let t=await E(e,Fd,`prepareTransactionRequest`)({account:_,accessList:i,authorizationList:a,blobs:o,chain:r,data:s,gas:c,gasPrice:l,maxFeePerBlobGas:u,maxFeePerGas:d,maxPriorityFeePerGas:f,nonce:p,nonceManager:_.nonceManager,parameters:[...Nd,`sidecars`],type:m,value:h,...g,to:n}),v=r?.serializers?.transaction,y=await _.signTransaction(t,{serializer:v});return await E(e,Mm,`sendRawTransaction`)({serializedTransaction:y})}throw _?.type===`smart`?new km({metaMessages:["Consider using the `sendUserOperation` Action instead."],docsPath:`/docs/actions/bundler/sendUserOperation`,type:`smart`}):new km({docsPath:`/docs/actions/wallet/sendTransaction`,type:_?.type})}catch(e){throw e instanceof km?e:jm(e,{...t,account:_,chain:t.chain||void 0})}}ra(),ca();async function Fm(e,t){return Fm.internal(e,Pm,`sendTransaction`,t)}(function(e){async function t(e,t,n,r){let{abi:i,account:a=e.account,address:o,args:s,dataSuffix:c,functionName:l,...u}=r;if(a===void 0)throw new Om({docsPath:`/docs/contract/writeContract`});let d=a?na(a):null,f=sa({abi:i,args:s,functionName:l});try{return await E(e,t,n)({data:`${f}${c?c.replace(`0x`,``):``}`,to:o,account:d,...u})}catch(e){throw cs(e,{abi:i,address:o,args:s,docsPath:`/docs/contract/writeContract`,functionName:l,sender:d?.address})}}e.internal=t})(Fm||={}),O();var Im=class extends D{constructor(e){super(`Call bundle failed with status: ${e.statusCode}`,{name:`BundleFailedError`}),Object.defineProperty(this,`result`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.result=e}};function Lm(e,{delay:t=100,retryCount:n=2,shouldRetry:r=()=>!0}={}){return new Promise((i,a)=>{let o=async({count:s=0}={})=>{let c=async({error:e})=>{let n=typeof t==`function`?t({count:s,error:e}):t;n&&await bm(n),o({count:s+1})};try{let t=await e();i(t)}catch(e){if(szd(e)):null,to:e.to?e.to:null,transactionIndex:e.transactionIndex?En(e.transactionIndex):null,status:e.status?Rm[e.status]:null,type:e.type?id[e.type]||e.type:null};return e.blobGasPrice&&(n.blobGasPrice=BigInt(e.blobGasPrice)),e.blobGasUsed&&(n.blobGasUsed=BigInt(e.blobGasUsed)),n}const Bm=Ru(`transactionReceipt`,zm);ra(),O(),os(),ca(),Di(),Dn(),A();const Vm=k(0,{size:32});async function Hm(e,t){let{account:n=e.account,capabilities:r,chain:i=e.chain,experimental_fallback:a,experimental_fallbackDelay:o=32,forceAtomic:s=!1,id:c,version:l=`2.0.0`}=t,u=n?na(n):null,d=t.calls.map(e=>{let t=e,n=t.abi?sa({abi:t.abi,functionName:t.functionName,args:t.args}):t.data;return{data:t.dataSuffix&&n?wi([n,t.dataSuffix]):n,to:t.to,value:t.value?k(t.value):void 0}});try{let t=await e.request({method:`wallet_sendCalls`,params:[{atomicRequired:s,calls:d,capabilities:r,chainId:k(i.id),from:u?.address,id:c,version:l}]},{retryCount:0});return typeof t==`string`?{id:t}:t}catch(n){let c=n;if(a&&(c.name===`MethodNotFoundRpcError`||c.name===`MethodNotSupportedRpcError`||c.name===`UnknownRpcError`||c.details.toLowerCase().includes(`does not exist / is not available`)||c.details.toLowerCase().includes(`missing or invalid. request()`)||c.details.toLowerCase().includes(`did not match any variant of untagged enum`)||c.details.toLowerCase().includes(`account upgraded to unsupported contract`)||c.details.toLowerCase().includes(`eip-7702 not supported`)||c.details.toLowerCase().includes(`unsupported wc_ method`)||c.details.toLowerCase().includes(`feature toggled misconfigured`)||c.details.toLowerCase().includes(`jsonrpcengine: response has no error or result for request`))){if(r&&Object.values(r).some(e=>!e.optional)){let e="non-optional `capabilities` are not supported on fallback to `eth_sendTransaction`.";throw new Xo(new D(e,{details:e}))}if(s&&d.length>1){let e="`forceAtomic` is not supported on fallback to `eth_sendTransaction`.";throw new ns(new D(e,{details:e}))}let t=[];for(let n of d){let r=Pm(e,{account:u,chain:i,data:n.data,to:n.to,value:n.value?wn(n.value):void 0});t.push(r),o>0&&await new Promise(e=>setTimeout(e,o))}let n=await Promise.allSettled(t);if(n.every(e=>e.status===`rejected`))throw n[0].reason;let a=n.map(e=>e.status===`fulfilled`?e.value:Vm);return{id:wi([...a,k(i.id,{size:32}),`0x5792579257925792579257925792579257925792579257925792579257925792`])}}throw jm(n,{...t,account:u,chain:t.chain})}}Ni(),Sn(),Dn();async function Um(e,t){async function n(t){if(t.endsWith(`5792579257925792579257925792579257925792579257925792579257925792`)){let n=xn(Mi(t,-64,-32)),r=Mi(t,0,-64).slice(2).match(/.{1,64}/g),i=await Promise.all(r.map(t=>Vm.slice(2)===t?void 0:e.request({method:`eth_getTransactionReceipt`,params:[`0x${t}`]},{dedupe:!0}))),a=(()=>i.some(e=>e===null)?100:i.every(e=>e?.status===`0x1`)?200:i.every(e=>e?.status===`0x0`)?500:600)();return{atomic:!1,chainId:En(n),receipts:i.filter(Boolean),status:a,version:`2.0.0`}}return e.request({method:`wallet_getCallsStatus`,params:[t]})}let{atomic:r=!1,chainId:i,receipts:a,version:o=`2.0.0`,...s}=await n(t.id),[c,l]=(()=>{let e=s.status;return e>=100&&e<200?[`pending`,e]:e>=200&&e<300?[`success`,e]:e>=300&&e<700?[`failure`,e]:e===`CONFIRMED`?[`success`,200]:e===`PENDING`?[`pending`,100]:[void 0,e]})();return{...s,atomic:r,chainId:i?En(i):void 0,receipts:a?.map(e=>({...e,blockNumber:wn(e.blockNumber),gasUsed:wn(e.gasUsed),status:Rm[e.status]}))??[],statusCode:l,status:c,version:o}}O(),Lp(),Va();async function Wm(e,t){let{id:n,pollingInterval:r=e.pollingInterval,status:i=({statusCode:e})=>e===200||e>=300,retryCount:a=4,retryDelay:o=({count:e})=>~~(1<{let s=xm(async()=>{let r=e=>{clearTimeout(p),s(),e(),m()};try{let s=await Lm(async()=>{let t=await E(e,Um,`getCallsStatus`)({id:n});if(c&&t.status===`failure`)throw new Im(t);return t},{retryCount:a,delay:o});if(!i(s))return;r(()=>t.resolve(s))}catch(e){r(()=>t.reject(e))}},{interval:r,emitOnBegin:!0});return s});return p=s?setTimeout(()=>{m(),clearTimeout(p),f(new Gm({id:n}))},s):void 0,await u}var Gm=class extends D{constructor({id:e}){super(`Timed out while waiting for call bundle with id "${e}" to be confirmed.`,{name:`WaitForCallsStatusTimeoutError`})}},Km=256,qm=Km,Jm;function Ym(e=11){if(!Jm||qm+e>Km*2){Jm=``,qm=0;for(let e=0;e{let n=t(e);for(let e in _)delete n[e];let r={...e,...n};return Object.assign(r,{extend:v(r)})}}return Object.assign(_,{extend:v(_)})}A();async function Zm(e,{address:t,blockNumber:n,blockTag:r=`latest`}){let i=n===void 0?void 0:k(n),a=await e.request({method:`eth_getCode`,params:[t,i||r]},{dedupe:!!i});if(a!==`0x`)return a}O();var Qm=class extends D{constructor({address:e}){super(`No EIP-712 domain found on contract "${e}".`,{metaMessages:[`Ensure that:`,`- The contract is deployed at the address "${e}".`,"- `eip712Domain()` function exists on the contract.","- `eip712Domain()` function matches signature to ERC-5267 specification."],name:`Eip712DomainNotFoundError`})}};async function $m(e,t){let{address:n,factory:r,factoryData:i}=t;try{let[t,a,o,s,c,l,u]=await E(e,hm,`readContract`)({abi:eh,address:n,functionName:`eip712Domain`,factory:r,factoryData:i});return{domain:{name:a,version:o,chainId:Number(s),verifyingContract:c,salt:l},extensions:u,fields:t}}catch(e){let t=e;throw t.name===`ContractFunctionExecutionError`&&t.cause.name===`ContractFunctionZeroDataError`?new Qm({address:n}):t}}var eh=[{inputs:[],name:`eip712Domain`,outputs:[{name:`fields`,type:`bytes1`},{name:`name`,type:`string`},{name:`version`,type:`string`},{name:`chainId`,type:`uint256`},{name:`verifyingContract`,type:`address`},{name:`salt`,type:`bytes32`},{name:`extensions`,type:`uint256[]`}],stateMutability:`view`,type:`function`}];Zu(),pi(),O(),Op(),Mu(),Ci(),It(),Ni(),Dn();function th(e){let{authorizationList:t}=e;if(t)for(let e of t){let{chainId:t}=e,n=e.address;if(!bi(n))throw new fi({address:n});if(t<0)throw new Dp({chainId:t})}rh(e)}function nh(e){let{blobVersionedHashes:t}=e;if(t){if(t.length===0)throw new Ed;for(let e of t){let t=Ft(e),n=En(Oi(e,0,1));if(t!==32)throw new Dd({hash:e,size:t});if(n!==1)throw new Od({hash:e,version:n})}}rh(e)}function rh(e){let{chainId:t,maxPriorityFeePerGas:n,maxFeePerGas:r,to:i}=e;if(t<=0)throw new Dp({chainId:t});if(i&&!bi(i))throw new fi({address:i});if(r&&r>Xu)throw new xu({maxFeePerGas:r});if(n&&r&&n>r)throw new Au({maxFeePerGas:r,maxPriorityFeePerGas:n})}function ih(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a}=e;if(t<=0)throw new Dp({chainId:t});if(a&&!bi(a))throw new fi({address:a});if(n||i)throw new D("`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.");if(r&&r>Xu)throw new xu({maxFeePerGas:r})}function ah(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a}=e;if(a&&!bi(a))throw new fi({address:a});if(t!==void 0&&t<=0)throw new Dp({chainId:t});if(n||i)throw new D("`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.");if(r&&r>Xu)throw new xu({maxFeePerGas:r})}pi(),ho(),Ci();function oh(e){if(!e||e.length===0)return[];let t=[];for(let n=0;nAn(e)),n=e.kzg,r=hd({blobs:t,kzg:n});if(f===void 0&&(f=bd({commitments:r})),p===void 0){let e=gd({blobs:t,commitments:r,kzg:n});p=Ad({blobs:t,commitments:r,proofs:e})}}let m=oh(u),h=[k(n),i?k(i):`0x`,l?k(l):`0x`,c?k(c):`0x`,r?k(r):`0x`,a??`0x`,o?k(o):`0x`,d??`0x`,m,s?k(s):`0x`,f??[],...ph(e,t)],g=[],_=[],v=[];if(p)for(let e=0;e{if(t.v>=35n)return(t.v-35n)/2n>0?t.v:27n+(t.v===35n?0n:1n);if(n>0)return BigInt(n*2)+BigInt(35n+t.v-27n);let e=27n+(t.v===27n?0n:1n);if(t.v!==e)throw new oo({v:t.v});return e})(),r=xn(t.r),i=xn(t.s);l=[...l,k(e),r===`0x00`?`0x`:r,i===`0x00`?`0x`:i]}else n>0&&(l=[...l,k(n),`0x`,`0x`]);return M(l)}function ph(e,t){let n=t??e,{v:r,yParity:i}=n;if(n.r===void 0||n.s===void 0||r===void 0&&i===void 0)return[];let a=xn(n.r),o=xn(n.s);return[(()=>typeof i==`number`?i?k(1):`0x`:r===0n?`0x`:r===1n?k(1):r===27n?`0x`:k(1))(),a===`0x00`?`0x`:a,o===`0x00`?`0x`:o]}A();function mh(e){if(!e||e.length===0)return[];let t=[];for(let n of e){let{chainId:e,nonce:r,...i}=n,a=n.address;t.push([e?On(e):`0x`,a,r?On(r):`0x`,...ph({},i)])}return t}yi(),Rd();async function hh({address:e,authorization:t,signature:n}){return Ld(_i(e),await vu({authorization:t,signature:n}))}hi();const gh=new mi(8192);function _h(e,{enabled:t=!0,id:n}){if(!t||!n)return e();if(gh.get(n))return gh.get(n);let r=e().finally(()=>gh.delete(n));return gh.set(n,r),r}O(),ko(),os(),A(),Va();function vh(e,t={}){return async(n,r={})=>{let{dedupe:i=!1,methods:a,retryDelay:o=150,retryCount:s=3,uid:c}={...t,...r},{method:l}=n;if(a?.exclude?.includes(l)||a?.include&&!a.include.includes(l))throw new Vo(Error(`method not supported`),{method:l});let u=i?jn(`${c}.${Ba(n)}`):void 0;return _h(()=>Lm(async()=>{try{return await e(n)}catch(e){let t=e;switch(t.code){case No.code:throw new No(t);case j.code:throw new j(t);case Po.code:throw new Po(t,{method:n.method});case Fo.code:throw new Fo(t);case Io.code:throw new Io(t);case Lo.code:throw new Lo(t);case Ro.code:throw new Ro(t);case zo.code:throw new zo(t);case Bo.code:throw new Bo(t);case Vo.code:throw new Vo(t,{method:n.method});case Ho.code:throw new Ho(t);case Uo.code:throw new Uo(t);case Wo.code:throw new Wo(t);case Go.code:throw new Go(t);case Ko.code:throw new Ko(t);case qo.code:throw new qo(t);case Jo.code:throw new Jo(t);case Yo.code:throw new Yo(t);case Xo.code:throw new Xo(t);case Zo.code:throw new Zo(t);case Qo.code:throw new Qo(t);case $o.code:throw new $o(t);case es.code:throw new es(t);case ts.code:throw new ts(t);case ns.code:throw new ns(t);case 5e3:throw new Wo(t);default:throw e instanceof D?e:new rs(t)}}},{delay:({count:e,error:t})=>{if(t&&t instanceof Eo){let e=t?.headers?.get(`Retry-After`);if(e?.match(/\d/))return Number.parseInt(e,10)*1e3}return~~(1<yh(e)}),{enabled:i,id:u})}}function yh(e){return`code`in e&&typeof e.code==`number`?e.code===-1||e.code===Ho.code||e.code===Io.code:e instanceof Eo&&e.status?e.status===403||e.status===408||e.status===413||e.status===429||e.status===500||e.status===502||e.status===503||e.status===504:!0}function L(e){return{formatters:void 0,fees:void 0,serializers:void 0,...e}}function bh(e,{errorInstance:t=Error(`timed out`),timeout:n,signal:r}){return new Promise((i,a)=>{(async()=>{let o;try{let s=new AbortController;n>0&&(o=setTimeout(()=>{r?s.abort():a(t)},n)),i(await e({signal:s?.signal||null}))}catch(e){e?.name===`AbortError`&&a(t),a(e)}finally{clearTimeout(o)}})()})}function xh(){return{current:0,take(){return this.current++},reset(){this.current=0}}}const Sh=xh();ko(),Va();function Ch(e,t={}){return{async request(n){let{body:r,fetchFn:i=t.fetchFn??fetch,onRequest:a=t.onRequest,onResponse:o=t.onResponse,timeout:s=t.timeout??1e4}=n,c={...t.fetchOptions??{},...n.fetchOptions??{}},{headers:l,method:u,signal:d}=c;try{let t=await bh(async({signal:t})=>{let n={...c,body:Ba(Array.isArray(r)?r.map(e=>({jsonrpc:`2.0`,id:e.id??Sh.take(),...e})):{jsonrpc:`2.0`,id:r.id??Sh.take(),...r}),headers:{"Content-Type":`application/json`,...l},method:u||`POST`,signal:d||(s>0?t:null)},o=new Request(e,n),f=await a?.(o,n)??{...n,url:e};return await i(f.url??e,f)},{errorInstance:new Oo({body:r,url:e}),timeout:s,signal:!0});o&&await o(t);let n;if(t.headers.get(`Content-Type`)?.startsWith(`application/json`))n=await t.json();else{n=await t.text();try{n=JSON.parse(n||`{}`)}catch(e){if(t.ok)throw e;n={error:n}}}if(!t.ok)throw new Eo({body:r,details:Ba(n.error)||t.statusText,headers:t.headers,status:t.status,url:e});return n}catch(t){throw t instanceof Eo||t instanceof Oo?t:new Eo({body:r,cause:t,url:e})}}}}Di(),It(),A();function wh(e){let t=(()=>typeof e==`string`?jn(e):typeof e.raw==`string`?e.raw:An(e.raw))(),n=jn(`Ethereum Signed Message: -${Ft(t)}`);return wi([n,t])}ei();function Th(e,t){return $r(wh(e),t)}Va(),O();var Eh=class extends D{constructor({domain:e}){super(`Invalid domain "${Ba(e)}".`,{metaMessages:[`Must be a valid EIP-712 domain.`]})}},Dh=class extends D{constructor({primaryType:e,types:t}){super(`Invalid primary type \`${e}\` must be one of \`${JSON.stringify(Object.keys(t))}\`.`,{docsPath:`/api/glossary/Errors#typeddatainvalidprimarytypeerror`,metaMessages:["Check that the primary type is a key in `types`."]})}},Oh=class extends D{constructor({type:e}){super(`Struct type "${e}" is invalid.`,{metaMessages:[`Struct type must not be a Solidity type.`],name:`InvalidStructTypeError`})}};sn(),pi(),Ci(),It(),A(),Ii(),Va();function kh(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{let n={...t};for(let t of e){let{name:e,type:r}=t;r===`address`&&(n[e]=n[e].toLowerCase())}return n},o=(()=>!i.EIP712Domain||!t?{}:a(i.EIP712Domain,t))(),s=(()=>{if(r!==`EIP712Domain`)return a(i[r],n)})();return Ba({domain:o,message:s,primaryType:r,types:i})}function Ah(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{for(let n of e){let{name:e,type:r}=n,o=t[e],s=r.match(Fi);if(s&&(typeof o==`number`||typeof o==`bigint`)){let[e,t,n]=s;k(o,{signed:t===`int`,size:Number.parseInt(n,10)/8})}if(r===`address`&&typeof o==`string`&&!bi(o))throw new fi({address:o});let c=r.match(Pi);if(c){let[e,t]=c;if(t&&Ft(o)!==Number.parseInt(t,10))throw new tn({expectedSize:Number.parseInt(t,10),givenSize:Ft(o)})}let l=i[r];l&&(Mh(r),a(l,o))}};if(i.EIP712Domain&&t){if(typeof t!=`object`)throw new Eh({domain:t});a(i.EIP712Domain,t)}if(r!==`EIP712Domain`)if(i[r])a(i[r],n);else throw new Dh({primaryType:r,types:i})}function jh({domain:e}){return[typeof e?.name==`string`&&{name:`name`,type:`string`},e?.version&&{name:`version`,type:`string`},(typeof e?.chainId==`number`||typeof e?.chainId==`bigint`)&&{name:`chainId`,type:`uint256`},e?.verifyingContract&&{name:`verifyingContract`,type:`address`},e?.salt&&{name:`salt`,type:`bytes32`}].filter(Boolean)}function Mh(e){if(e===`address`||e===`bool`||e===`string`||e.startsWith(`bytes`)||e.startsWith(`uint`)||e.startsWith(`int`))throw new Oh({type:e})}Yi(),Di(),A(),ei();function Nh(e){let{domain:t={},message:n,primaryType:r}=e,i={EIP712Domain:jh({domain:t}),...e.types};Ah({domain:t,message:n,primaryType:r,types:i});let a=[`0x1901`];return t&&a.push(Ph({domain:t,types:i})),r!==`EIP712Domain`&&a.push(Fh({data:n,primaryType:r,types:i})),$r(wi(a))}function Ph({domain:e,types:t}){return Fh({data:e,primaryType:`EIP712Domain`,types:t})}function Fh({data:e,primaryType:t,types:n}){let r=Ih({data:e,primaryType:t,types:n});return $r(r)}function Ih({data:e,primaryType:t,types:n}){let r=[{type:`bytes32`}],i=[Lh({primaryType:t,types:n})];for(let a of n[t]){let[t,o]=Bh({types:n,name:a.name,type:a.type,value:e[a.name]});r.push(t),i.push(o)}return Li(r,i)}function Lh({primaryType:e,types:t}){let n=On(Rh({primaryType:e,types:t}));return $r(n)}function Rh({primaryType:e,types:t}){let n=``,r=zh({primaryType:e,types:t});r.delete(e);let i=[e,...Array.from(r).sort()];for(let e of i)n+=`${e}(${t[e].map(({name:e,type:t})=>`${t} ${e}`).join(`,`)})`;return n}function zh({primaryType:e,types:t},n=new Set){let r=e.match(/^\w*/u)?.[0];if(n.has(r)||t[r]===void 0)return n;n.add(r);for(let e of t[r])zh({primaryType:e.type,types:t},n);return n}function Bh({types:e,name:t,type:n,value:r}){if(e[n]!==void 0)return[{type:`bytes32`},$r(Ih({data:r,primaryType:n,types:e}))];if(n===`bytes`)return r=`0x${(r.length%2?`0`:``)+r.slice(2)}`,[{type:`bytes32`},$r(r)];if(n===`string`)return[{type:`bytes32`},$r(On(r))];if(n.lastIndexOf(`]`)===n.length-1){let i=n.slice(0,n.lastIndexOf(`[`)),a=r.map(n=>Bh({name:t,type:i,types:e,value:n}));return[{type:`bytes32`},$r(Li(a.map(([e])=>e),a.map(([,e])=>e)))]}return[{type:n},r]}const Vh={checksum:new class extends Map{constructor(e){super(),Object.defineProperty(this,`maxSize`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&t!==void 0&&(this.delete(e),super.set(e,t)),t}set(e,t){if(super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=this.keys().next().value;e&&this.delete(e)}return this}}(8192)}.checksum;Qr(),Lf(),op();function Hh(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=Zr(hf(e));return n===`Bytes`?r:Hf(r)}function Uh(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=_d(hf(e));return n===`Bytes`?r:Hf(r)}Lf(),Jd(),op(),pf();function Wh(e,t={}){let{compressed:n}=t,{prefix:r,x:i,y:a}=e;if(n===!1||typeof i==`bigint`&&typeof a==`bigint`){if(r!==4)throw new Xh({prefix:r,cause:new Qh});return}if(n===!0||typeof i==`bigint`&&a===void 0){if(r!==3&&r!==2)throw new Xh({prefix:r,cause:new Zh});return}throw new Yh({publicKey:e})}function Gh(e){let t=(()=>{if(Zf(e))return qh(e);if(kf(e))return Kh(e);let{prefix:t,x:n,y:r}=e;return typeof n==`bigint`&&typeof r==`bigint`?{prefix:t??4,x:n,y:r}:{prefix:t,x:n}})();return Wh(t),t}function Kh(e){return qh(Hf(e))}function qh(e){if(e.length!==132&&e.length!==130&&e.length!==68)throw new $h({publicKey:e});if(e.length===130){let t=BigInt(Kf(e,0,32)),n=BigInt(Kf(e,32,64));return{prefix:4,x:t,y:n}}if(e.length===132){let t=Number(Kf(e,0,1)),n=BigInt(Kf(e,1,33)),r=BigInt(Kf(e,33,65));return{prefix:t,x:n,y:r}}let t=Number(Kf(e,0,1)),n=BigInt(Kf(e,1,33));return{prefix:t,x:n}}function Jh(e,t={}){Wh(e);let{prefix:n,x:r,y:i}=e,{includePrefix:a=!0}=t;return zf(a?I(n,{size:1}):`0x`,I(r,{size:32}),typeof i==`bigint`?I(i,{size:32}):`0x`)}var Yh=class extends P{constructor({publicKey:e}){super(`Value \`${df(e)}\` is not a valid public key.`,{metaMessages:[`Public key must contain:`,"- an `x` and `prefix` value (compressed)","- an `x`, `y`, and `prefix` value (uncompressed)"]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidError`})}},Xh=class extends P{constructor({prefix:e,cause:t}){super(`Prefix "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidPrefixError`})}},Zh=class extends P{constructor(){super(`Prefix must be 2 or 3 for compressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidCompressedPrefixError`})}},Qh=class extends P{constructor(){super(`Prefix must be 4 for uncompressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidUncompressedPrefixError`})}},$h=class extends P{constructor({publicKey:e}){super(`Value \`${e}\` is an invalid public key size.`,{metaMessages:[`Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).`,`Received ${qf(Bf(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidSerializedSizeError`})}};Lf(),Jd();var eg=/^0x[a-fA-F0-9]{40}$/;function tg(e,t={}){let{strict:n=!0}=t;if(!eg.test(e))throw new sg({address:e,cause:new cg});if(n){if(e.toLowerCase()===e)return;if(ng(e)!==e)throw new sg({address:e,cause:new lg})}}function ng(e){if(Vh.has(e))return Vh.get(e);tg(e,{strict:!1});let t=e.substring(2).toLowerCase(),n=Hh(vf(t),{as:`Bytes`}),r=t.split(``);for(let e=0;e<40;e+=2)n[e>>1]>>4>=8&&r[e]&&(r[e]=r[e].toUpperCase()),(n[e>>1]&15)>=8&&r[e+1]&&(r[e+1]=r[e+1].toUpperCase());let i=`0x${r.join(``)}`;return Vh.set(e,i),i}function rg(e,t={}){let{checksum:n=!1}=t;return tg(e),n?ng(e):e}function ig(e,t={}){let n=Hh(`0x${Jh(e).slice(4)}`).substring(26);return rg(`0x${n}`,t)}function ag(e,t){return tg(e,{strict:!1}),tg(t,{strict:!1}),e.toLowerCase()===t.toLowerCase()}function og(e,t={}){let{strict:n=!0}=t??{};try{return tg(e,{strict:n}),!0}catch{return!1}}var sg=class extends P{constructor({address:e,cause:t}){super(`Address "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidAddressError`})}},cg=class extends P{constructor(){super(`Address is not a 20 byte (40 hexadecimal character) value.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidInputError`})}},lg=class extends P{constructor(){super(`Address does not match its checksum counterpart.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidChecksumError`})}};const ug=/^(.*)\[([0-9]*)\]$/,dg=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,fg=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n));const pg=2n**256n-1n;Lf(),Jd(),op();function mg(e,t,n){let{checksumAddress:r,staticPosition:i}=n,a=Pg(t.type);if(a){let[n,o]=a;return vg(e,{...t,type:o},{checksumAddress:r,length:n,staticPosition:i})}if(t.type===`tuple`)return Sg(e,t,{checksumAddress:r,staticPosition:i});if(t.type===`address`)return _g(e,{checksum:r});if(t.type===`bool`)return yg(e);if(t.type.startsWith(`bytes`))return bg(e,t,{staticPosition:i});if(t.type.startsWith(`uint`)||t.type.startsWith(`int`))return xg(e,t);if(t.type===`string`)return Cg(e,{staticPosition:i});throw new Zg(t.type)}var hg=32,gg=32;function _g(e,t={}){let{checksum:n=!1}=t,r=e.readBytes(32);return[(e=>n?ng(e):e)(Hf(xf(r,-20))),32]}function vg(e,t,n){let{checksumAddress:r,length:i,staticPosition:a}=n;if(!i){let n=Tf(e.readBytes(gg)),i=a+n,o=i+hg;e.setPosition(i);let s=Tf(e.readBytes(hg)),c=Fg(t),l=0,u=[];for(let n=0;n48?Sf(i,{signed:n}):Tf(i,{signed:n}),32]}function Sg(e,t,n){let{checksumAddress:r,staticPosition:i}=n,a=t.components.length===0||t.components.some(({name:e})=>!e),o=a?[]:{},s=0;if(Fg(t)){let n=Tf(e.readBytes(gg)),c=i+n;for(let n=0;n0?zf(t,e):t}}if(o)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:zf(...s.map(({encoded:e})=>e))}}function kg(e,{type:t}){let[,n]=t.split(`bytes`),r=qf(e);if(!n){let t=e;return r%32!=0&&(t=Gf(t,Math.ceil((e.length-2)/2/32)*32)),{dynamic:!0,encoded:zf(Wf(I(r,{size:32})),t)}}if(r!==Number.parseInt(n,10))throw new Jg({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:Gf(e)}}function Ag(e){if(typeof e!=`boolean`)throw new P(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:Wf(Vf(e))}}function jg(e,{signed:t,size:n}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function Pg(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}function Fg(e){let{type:t}=e;if(t===`string`||t===`bytes`||t.endsWith(`[]`))return!0;if(t===`tuple`)return e.components?.some(Fg);let n=Pg(e.type);return!!(n&&Fg({...e,type:n[1]}))}Jd();var Ig={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new Bg({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new zg({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new Rg({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new Rg({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}};function Lg(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(Ig);return n.bytes=e,n.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var Rg=class extends P{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.NegativeOffsetError`})}},zg=class extends P{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.PositionOutOfBoundsError`})}},Bg=class extends P{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.RecursiveReadLimitExceededError`})}};Ot(),Lf(),Jd(),op();function Vg(e,t,n={}){let{as:r=`Array`,checksumAddress:i=!1}=n,a=typeof t==`string`?_f(t):t,o=Lg(a);if(bf(a)===0&&e.length>0)throw new Kg;if(bf(a)&&bf(a)<32)throw new Gg({data:typeof t==`string`?t:Hf(t),parameters:e,size:bf(a)});let s=0,c=r===`Array`?[]:{};for(let t=0;te_(e))):n_(e)}function t_(e){let t=e.reduce((e,t)=>e+t.length,0),n=r_(t);return{length:(()=>t<=55?1+t:1+n+t)(),encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function n_(e){let t=typeof e==`string`?_f(e):e,n=r_(t.length);return{length:(()=>t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length)(),encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function r_(e){if(e<2**8)return 1;if(e<2**16)return 2;if(e<2**24)return 3;if(e<2**32)return 4;throw new P(`Length is too large.`)}Jd(),op(),pf();function i_(e,t={}){let{recovered:n}=t;if(e.r===void 0||e.s===void 0||n&&e.yParity===void 0)throw new g_({signature:e});if(e.r<0n||e.r>pg)throw new __({value:e.r});if(e.s<0n||e.s>pg)throw new v_({value:e.s});if(typeof e.yParity==`number`&&e.yParity!==0&&e.yParity!==1)throw new y_({value:e.yParity})}function a_(e){return o_(Hf(e))}function o_(e){if(e.length!==130&&e.length!==132)throw new h_({signature:e});let t=BigInt(Kf(e,0,32)),n=BigInt(Kf(e,32,64)),r=(()=>{let t=Number(`0x${e.slice(130)}`);if(!Number.isNaN(t))try{return p_(t)}catch{throw new y_({value:t})}})();return r===void 0?{r:t,s:n}:{r:t,s:n,yParity:r}}function s_(e){if(e.r!==void 0&&e.s!==void 0)return c_(e)}function c_(e){let t=(()=>typeof e==`string`?o_(e):e instanceof Uint8Array?a_(e):typeof e.r==`string`?u_(e):e.v?l_(e):{r:e.r,s:e.s,...e.yParity===void 0?{}:{yParity:e.yParity}})();return i_(t),t}function l_(e){return{r:e.r,s:e.s,yParity:p_(e.v)}}function u_(e){let t=(()=>{let t=e.v?Number(e.v):void 0,n=e.yParity?Number(e.yParity):void 0;if(typeof t==`number`&&typeof n!=`number`&&(n=p_(t)),typeof n!=`number`)throw new y_({value:e.yParity});return n})();return{r:BigInt(e.r),s:BigInt(e.s),yParity:t}}function d_(e){i_(e);let t=e.r,n=e.s;return zf(I(t,{size:32}),I(n,{size:32}),typeof e.yParity==`number`?I(m_(e.yParity),{size:1}):`0x`)}function f_(e){let{r:t,s:n,yParity:r}=e;return[r?`0x01`:`0x`,t===0n?`0x`:Jf(I(t)),n===0n?`0x`:Jf(I(n))]}function p_(e){if(e===0||e===27)return 0;if(e===1||e===28)return 1;if(e>=35)return e%2==0?1:0;throw new b_({value:e})}function m_(e){if(e===0)return 27;if(e===1)return 28;throw new y_({value:e})}var h_=class extends P{constructor({signature:e}){super(`Value \`${e}\` is an invalid signature size.`,{metaMessages:[`Expected: 64 bytes or 65 bytes.`,`Received ${qf(Bf(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSerializedSizeError`})}},g_=class extends P{constructor({signature:e}){super(`Signature \`${df(e)}\` is missing either an \`r\`, \`s\`, or \`yParity\` property.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.MissingPropertiesError`})}},__=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid r value. r must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidRError`})}},v_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid s value. s must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSError`})}},y_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid y-parity value. Y-parity must be 0 or 1.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidYParityError`})}},b_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid v value. v must be 27, 28 or >=35.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidVError`})}};op();function x_(e,t={}){return typeof e.chainId==`string`?S_(e):{...e,...t.signature}}function S_(e){let{address:t,chainId:n,nonce:r}=e,i=s_(e);return{address:t,chainId:Number(n),nonce:BigInt(r),...i}}function C_(e){return w_(e,{presign:!0})}function w_(e,t={}){let{presign:n}=t;return Hh(zf(`0x05`,$g(T_(n?{address:e.address,chainId:e.chainId,nonce:e.nonce}:e))))}function T_(e){let{address:t,chainId:n,nonce:r}=e,i=s_(e);return[n?I(n):`0x`,t,r?I(r):`0x`,...i?f_(i):[]]}uu(),Lf(),op();function E_(e){let{privateKey:t}=e,n=Yl.ProjectivePoint.fromPrivateKey(Bf(t).slice(2));return Gh(n)}function D_(e={}){let{as:t=`Hex`}=e,n=Yl.utils.randomPrivateKey();return t===`Hex`?Hf(n):n}function O_(e){return ig(k_(e))}function k_(e){let{payload:t,signature:n}=e,{r,s:i,yParity:a}=n,o=new Yl.Signature(BigInt(r),BigInt(i)).addRecoveryBit(a).recoverPublicKey(Bf(t).substring(2));return Gh(o)}function A_(e){let{extraEntropy:t=!1,hash:n,payload:r,privateKey:i}=e,{r:a,s:o,recovery:s}=Yl.sign(hf(r),hf(i),{extraEntropy:typeof t==`boolean`?t:Bf(t).slice(2),lowS:!0,...n?{prehash:!0}:{}});return{r:a,s:o,yParity:s}}Jd(),op();const j_=Wg(`(uint256 chainId, address delegation, uint256 nonce, uint8 yParity, uint256 r, uint256 s), address to, bytes data`);function M_(e){if(typeof e==`string`){if(Kf(e,-32)!==`0x8010801080108010801080108010801080108010801080108010801080108010`)throw new I_(e)}else i_(e.authorization)}function N_(e){M_(e);let t=Xf(Kf(e,-64,-32)),n=Kf(e,-t-64,-64),r=Kf(e,0,-t-64),[i,a,o]=Vg(j_,n);return{authorization:x_({address:i.delegation,chainId:Number(i.chainId),nonce:i.nonce,yParity:i.yParity,r:i.r,s:i.s}),signature:r,...o&&o!==`0x`?{data:o,to:a}:{}}}function P_(e){let{data:t,signature:n}=e;M_(e);let r=O_({payload:C_(e.authorization),signature:c_(e.authorization)}),i=Hg(j_,[{...e.authorization,delegation:e.authorization.address,chainId:BigInt(e.authorization.chainId)},e.to??r,t??`0x`]),a=I(qf(i),{size:32});return zf(n,i,a,`0x8010801080108010801080108010801080108010801080108010801080108010`)}function F_(e){try{return M_(e),!0}catch{return!1}}var I_=class extends P{constructor(e){super(`Value \`${e}\` is an invalid ERC-8010 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc8010.InvalidWrappedSignatureError`})}};ho(),A();async function L_(e,{blockHash:t,blockNumber:n,blockTag:r,hash:i,index:a}){let o=r||`latest`,s=n===void 0?void 0:k(n),c=null;if(i?c=await e.request({method:`eth_getTransactionByHash`,params:[i]},{dedupe:!0}):t?c=await e.request({method:`eth_getTransactionByBlockHashAndIndex`,params:[t,k(a)]},{dedupe:!0}):(s||o)&&(c=await e.request({method:`eth_getTransactionByBlockNumberAndIndex`,params:[s||o,k(a)]},{dedupe:!!s})),!c)throw new uo({blockHash:t,blockNumber:n,blockTag:o,hash:i,index:a});return(e.chain?.formatters?.transaction?.format||ad)(c,`getTransaction`)}ho();async function R_(e,{hash:t}){let n=await e.request({method:`eth_getTransactionReceipt`,params:[t]},{dedupe:!0});if(!n)throw new fo({hash:t});return(e.chain?.formatters?.transactionReceipt?.format||zm)(n,`getTransactionReceipt`)}Jd();function z_(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;oB_(Object.values(e)[n],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>B_(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function V_(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return V_(i.components,a.components,n[r]);let o=[i.type,a.type];if((()=>o.includes(`address`)&&o.includes(`bytes20`)?!0:o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`)?og(n[r],{strict:!1}):!1)())return o}}Ot(),Jd(),op();function H_(e,t={}){let{prepare:n=!0}=t,r=(()=>Array.isArray(e)||typeof e==`string`?wt(e):e)();return{...r,...n?{hash:K_(r)}:{}}}function U_(e,t,n){let{args:r=[],prepare:i=!0}=n??{},a=Zf(t,{strict:!1}),o=e.filter(e=>a?e.type===`function`||e.type===`error`?W_(e)===Kf(t,0,4):e.type===`event`?K_(e)===t:!1:`name`in e&&e.name===t);if(o.length===0)throw new J_({name:t});if(o.length===1)return{...o[0],...i?{hash:K_(o[0])}:{}};let s;for(let e of o)if(`inputs`in e){if(!r||r.length===0){if(!e.inputs||e.inputs.length===0)return{...e,...i?{hash:K_(e)}:{}};continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===r.length&&r.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?B_(t,r):!1})){if(s&&`inputs`in s&&s.inputs){let t=V_(e.inputs,s.inputs,r);if(t)throw new q_({abiItem:e,type:t[0]},{abiItem:s,type:t[1]})}s=e}}let c=(()=>{if(s)return s;let[e,...t]=o;return{...e,overloads:t}})();if(!c)throw new J_({name:t});return{...c,...i?{hash:K_(c)}:{}}}function W_(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return U_(t,n)}return e[0]})();return Kf(K_(t),0,4)}function G_(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return U_(t,n)}return e[0]})(),n=(()=>typeof t==`string`?t:le(t))();return z_(n)}function K_(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return U_(t,n)}return e[0]})();return typeof t!=`string`&&`hash`in t&&t.hash?t.hash:Hh(Uf(G_(t)))}var q_=class extends P{constructor(e,t){super(`Found ambiguous types in overloaded ABI Items.`,{metaMessages:[`\`${e.type}\` in \`${z_(le(e.abiItem))}\`, and`,`\`${t.type}\` in \`${z_(le(t.abiItem))}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.AmbiguityError`})}},J_=class extends P{constructor({name:e,data:t,type:n=`item`}){let r=(()=>e?` with name "${e}"`:t?` with data "${t}"`:``)();super(`ABI ${n}${r} not found.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.NotFoundError`})}};op();function Y_(...e){let[t,n=[]]=(()=>{if(Array.isArray(e[0])){let[t,n,r]=e;return[X_(t,n,{args:r}),r]}let[t,n]=e;return[t,n]})(),{overloads:r}=t,i=r?X_([t,...r],t.name,{args:n}):t,a=Z_(i),o=n.length>0?Hg(i.inputs,n):void 0;return o?zf(a,o):a}function X_(e,t,n){let r=U_(e,t,n);if(r.type!==`function`)throw new J_({name:t,type:`function`});return r}function Z_(e){return W_(e)}const Q_=`0x0000000000000000000000000000000000000000`;Jd(),op();function $_(e){if(Kf(e,-32)!==`0x6492649264926492649264926492649264926492649264926492649264926492`)throw new nv(e)}function ev(e){let{data:t,signature:n,to:r}=e;return zf(Hg(Wg(`address, bytes, bytes`),[r,t,n]),`0x6492649264926492649264926492649264926492649264926492649264926492`)}function tv(e){try{return $_(e),!0}catch{return!1}}var nv=class extends P{constructor(e){super(`Value \`${e}\` is an invalid ERC-6492 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc6492.InvalidWrappedSignatureError`})}};uu(),Dn(),Hn();function rv({r:e,s:t,to:n=`hex`,v:r,yParity:i}){let a=(()=>{if(i===0||i===1)return i;if(r&&(r===27n||r===28n||r>=35n))return r%2n==0n?1:0;throw Error("Invalid `v` or `yParity` value")})(),o=`0x${new Yl.Signature(wn(e),wn(t)).toCompactHex()}${a===0?`1b`:`1c`}`;return n===`hex`?o:Ln(o)}gp(),Sp(),To(),jp(),ca(),yi(),Rd(),Di(),Pt(),Dn(),A(),mm();async function iv(e,t){let{address:n,hash:r,erc6492VerifierAddress:i=t.universalSignatureVerifierAddress??e.chain?.contracts?.erc6492Verifier?.address,multicallAddress:a=t.multicallAddress??e.chain?.contracts?.multicall3?.address}=t,o=(()=>{let e=t.signature;return Nt(e)?e:typeof e==`object`&&`r`in e&&`s`in e?rv(e):An(e)})();try{return F_(o)?await av(e,{...t,multicallAddress:a,signature:o}):await ov(e,{...t,verifierAddress:i,signature:o})}catch(e){try{if(Ld(_i(n),await pu({hash:r,signature:o})))return!0}catch{}if(e instanceof cv)return!1;throw e}}async function av(e,t){let{address:n,blockNumber:r,blockTag:i,hash:a,multicallAddress:o}=t,{authorization:s,data:c,signature:l,to:u}=N_(t.signature);if(await Zm(e,{address:n,blockNumber:r,blockTag:i})===Ei([`0xef0100`,s.address]))return await sv(e,{address:n,blockNumber:r,blockTag:i,hash:a,signature:l});let d={address:s.address,chainId:Number(s.chainId),nonce:Number(s.nonce),r:k(s.r,{size:32}),s:k(s.s,{size:32}),yParity:s.yParity};if(!await hh({address:n,authorization:d}))throw new cv;let f=await E(e,hm,`readContract`)({...o?{address:o}:{code:xp},authorizationList:[d],abi:dp,blockNumber:r,blockTag:`pending`,functionName:`aggregate3`,args:[[...c?[{allowFailure:!0,target:u??n,callData:c}]:[],{allowFailure:!0,target:n,callData:sa({abi:mp,functionName:`isValidSignature`,args:[a,l]})}]]});if((f[f.length-1]?.returnData)?.startsWith(`0x1626ba7e`))return!0;throw new cv}async function ov(e,t){let{address:n,factory:r,factoryData:i,hash:a,signature:o,verifierAddress:s,...c}=t,l=await(async()=>!r&&!i||tv(o)?o:ev({data:i,signature:o,to:r}))(),u=s?{to:s,data:sa({abi:hp,functionName:`isValidSig`,args:[n,a,l]}),...c}:{data:kp({abi:hp,args:[n,a,l],bytecode:bp}),...c},{data:d}=await E(e,cm,`call`)(u).catch(e=>{throw e instanceof yo?new cv:e});if(Tn(d??`0x0`))return!0;throw new cv}async function sv(e,t){let{address:n,blockNumber:r,blockTag:i,hash:a,signature:o}=t;if((await E(e,hm,`readContract`)({address:n,abi:mp,args:[a,o],blockNumber:r,blockTag:i,functionName:`isValidSignature`}).catch(e=>{throw e instanceof bo?new cv:e})).startsWith(`0x1626ba7e`))return!0;throw new cv}var cv=class extends Error{};Dn(),Va();function lv(e,{emitOnBegin:t=!1,emitMissed:n=!1,onBlockNumber:r,onError:i,poll:a,pollingInterval:o=e.pollingInterval}){let s=(()=>a===void 0?!(e.transport.type===`webSocket`||e.transport.type===`ipc`||e.transport.type===`fallback`&&(e.transport.transports[0].config.type===`webSocket`||e.transport.transports[0].config.type===`ipc`)):a)(),c;return s?(()=>{let a=Ba([`watchBlockNumber`,e.uid,t,n,o]);return ym(a,{onBlockNumber:r,onError:i},r=>xm(async()=>{try{let t=await E(e,Dm,`getBlockNumber`)({cacheTime:0});if(c!==void 0){if(t===c)return;if(t-c>1&&n)for(let e=c+1n;ec)&&(r.onBlockNumber(t,c),c=t)}catch(e){r.onError?.(e)}},{emitOnBegin:t,interval:o}))})():(()=>{let a=Ba([`watchBlockNumber`,e.uid,t,n]);return ym(a,{onBlockNumber:r,onError:i},t=>{let n=!0,r=()=>n=!1;return(async()=>{try{let{unsubscribe:i}=await(()=>{if(e.transport.type===`fallback`){let t=e.transport.transports.find(e=>e.config.type===`webSocket`||e.config.type===`ipc`);return t?t.value:e.transport}return e.transport})().subscribe({params:[`newHeads`],onData(e){if(!n)return;let r=wn(e.result?.number);t.onBlockNumber(r,c),c=r},onError(e){t.onError?.(e)}});r=i,n||r()}catch(e){i?.(e)}})(),()=>r()})})()}ho(),Lp(),Va();async function uv(e,t){let{checkReplacement:n=!0,confirmations:r=1,hash:i,onReplaced:a,retryCount:o=6,retryDelay:s=({count:e})=>~~(1<t.pollingInterval?t.pollingInterval:e.chain?.experimental_preconfirmationTime?e.chain.experimental_preconfirmationTime:e.pollingInterval)(),d,f,p,m=!1,h,g,{promise:_,resolve:v,reject:y}=Ip(),b=c?setTimeout(()=>{g?.(),h?.(),y(new mo({hash:i}))},c):void 0;return h=ym(l,{onReplaced:a,resolve:v,reject:y},async t=>{if(p=await E(e,R_,`getTransactionReceipt`)({hash:i}).catch(()=>void 0),p&&r<=1){clearTimeout(b),t.resolve(p),h?.();return}g=E(e,lv,`watchBlockNumber`)({emitMissed:!0,emitOnBegin:!0,poll:!0,pollingInterval:u,async onBlockNumber(a){let c=e=>{clearTimeout(b),g?.(),e(),h?.()},l=a;if(!m)try{if(p){if(r>1&&(!p.blockNumber||l-p.blockNumber+1nt.resolve(p));return}if(n&&!d&&(m=!0,await Lm(async()=>{d=await E(e,L_,`getTransaction`)({hash:i}),d.blockNumber&&(l=d.blockNumber)},{delay:s,retryCount:o}),m=!1),p=await E(e,R_,`getTransactionReceipt`)({hash:i}),r>1&&(!p.blockNumber||l-p.blockNumber+1nt.resolve(p))}catch(n){if(n instanceof uo||n instanceof fo){if(!d){m=!1;return}try{f=d,m=!0;let n=await Lm(()=>E(e,ud,`getBlock`)({blockNumber:l,includeTransactions:!0}),{delay:s,retryCount:o,shouldRetry:({error:e})=>e instanceof rd});m=!1;let i=n.transactions.find(({from:e,nonce:t})=>e===f.from&&t===f.nonce);if(!i||(p=await E(e,R_,`getTransactionReceipt`)({hash:i.hash}),r>1&&(!p.blockNumber||l-p.blockNumber+1n{t.onReplaced?.({reason:a,replacedTransaction:f,transaction:i,transactionReceipt:p}),t.resolve(p)})}catch(e){c(()=>t.reject(e))}}else c(()=>t.reject(n))}}})}),_}ho();async function dv(e,{serializedTransaction:t,throwOnReceiptRevert:n,timeout:r}){let i=await e.request({method:`eth_sendRawTransactionSync`,params:r?[t,k(r)]:[t]},{retryCount:0}),a=(e.chain?.formatters?.transactionReceipt?.format||zm)(i);if(a.status===`reverted`&&n)throw new po({receipt:a});return a}A();async function fv(e,{chain:t}){let{id:n,name:r,nativeCurrency:i,rpcUrls:a,blockExplorers:o}=t;await e.request({method:`wallet_addEthereumChain`,params:[{chainId:k(n),chainName:r,nativeCurrency:i,rpcUrls:a.default.http,blockExplorerUrls:o?Object.values(o).map(({url:e})=>e):void 0}]},{dedupe:!0,retryCount:0})}jp();function pv(e,t){let{abi:n,args:r,bytecode:i,...a}=t,o=kp({abi:n,args:r,bytecode:i});return Pm(e,{...a,...a.authorizationList?{to:null}:{},data:o})}yi();async function mv(e){return e.account?.type===`local`?[e.account.address]:(await e.request({method:`eth_accounts`},{dedupe:!0})).map(e=>gi(e))}ra(),A();async function hv(e,t={}){let{account:n=e.account,chainId:r}=t,i=n?na(n):void 0,a=r?[i?.address,[k(r)]]:[i?.address],o=await e.request({method:`wallet_getCapabilities`,params:a}),s={};for(let[e,t]of Object.entries(o)){s[Number(e)]={};for(let[n,r]of Object.entries(t))n===`addSubAccount`&&(n=`unstable_addSubAccount`),s[Number(e)][n]=r}return typeof r==`number`?s[r]:s}async function gv(e){return await e.request({method:`wallet_getPermissions`},{dedupe:!0})}ra(),Rd();async function _v(e,t){let{account:n=e.account,chainId:r,nonce:i}=t;if(!n)throw new Om({docsPath:`/docs/eip7702/prepareAuthorization`});let a=na(n),o=(()=>{if(t.executor)return t.executor===`self`?t.executor:na(t.executor)})(),s={address:t.contractAddress??t.address,chainId:r,nonce:i};return s.chainId===void 0&&(s.chainId=e.chain?.id??await E(e,Md,`getChainId`)({})),s.nonce===void 0&&(s.nonce=await E(e,md,`getTransactionCount`)({address:a.address,blockTag:`pending`}),(o===`self`||o?.address&&Ld(o.address,a.address))&&(s.nonce+=1)),s}yi();async function vv(e){return(await e.request({method:`eth_requestAccounts`},{dedupe:!0,retryCount:0})).map(e=>_i(e))}async function yv(e,t){return e.request({method:`wallet_requestPermissions`,params:[t]},{retryCount:0})}async function bv(e,t){let{chain:n=e.chain}=t,r=t.timeout??Math.max((n?.blockTime??0)*3,5e3),i=await Hm(e,t);return await Wm(e,{...t,id:i.id,timeout:r})}ra(),O(),ho(),Lu(),Wu(),hi(),$u();var xv=new mi(128);async function Sv(e,t){let{account:n=e.account,chain:r=e.chain,accessList:i,authorizationList:a,blobs:o,data:s,gas:c,gasPrice:l,maxFeePerBlobGas:u,maxFeePerGas:d,maxPriorityFeePerGas:f,nonce:p,pollingInterval:m,throwOnReceiptRevert:h,type:g,value:_,...v}=t,y=t.timeout??Math.max((r?.blockTime??0)*3,5e3);if(n===void 0)throw new Om({docsPath:`/docs/actions/wallet/sendTransactionSync`});let b=n?na(n):null;try{Qu(t);let n=await(async()=>{if(t.to)return t.to;if(t.to!==null&&a&&a.length>0)return await vu({authorization:a[0]}).catch(()=>{throw new D("`to` is required. Could not infer from `authorizationList`.")})})();if(b?.type===`json-rpc`||b===null){let t;r!==null&&(t=await E(e,Md,`getChainId`)({}),Am({currentChainId:t,chain:r}));let x=e.chain?.formatters?.transactionRequest?.format,S=(x||Bu)({...Iu(v,{format:x}),accessList:i,authorizationList:a,blobs:o,chainId:t,data:s,from:b?.address,gas:c,gasPrice:l,maxFeePerBlobGas:u,maxFeePerGas:d,maxPriorityFeePerGas:f,nonce:p,to:n,type:g,value:_},`sendTransaction`),C=xv.get(e.uid),w=C?`wallet_sendTransaction`:`eth_sendTransaction`,ee=await(async()=>{try{return await e.request({method:w,params:[S]},{retryCount:0})}catch(t){if(C===!1)throw t;let n=t;if(n.name===`InvalidInputRpcError`||n.name===`InvalidParamsRpcError`||n.name===`MethodNotFoundRpcError`||n.name===`MethodNotSupportedRpcError`)return await e.request({method:`wallet_sendTransaction`,params:[S]},{retryCount:0}).then(t=>(xv.set(e.uid,!0),t)).catch(t=>{let r=t;throw r.name===`MethodNotFoundRpcError`||r.name===`MethodNotSupportedRpcError`?(xv.set(e.uid,!1),n):r});throw n}})(),te=await E(e,uv,`waitForTransactionReceipt`)({checkReplacement:!1,hash:ee,pollingInterval:m,timeout:y});if(h&&te.status===`reverted`)throw new po({receipt:te});return te}if(b?.type===`local`){let t=await E(e,Fd,`prepareTransactionRequest`)({account:b,accessList:i,authorizationList:a,blobs:o,chain:r,data:s,gas:c,gasPrice:l,maxFeePerBlobGas:u,maxFeePerGas:d,maxPriorityFeePerGas:f,nonce:p,nonceManager:b.nonceManager,parameters:[...Nd,`sidecars`],type:g,value:_,...v,to:n}),m=r?.serializers?.transaction,y=await b.signTransaction(t,{serializer:m});return await E(e,dv,`sendRawTransactionSync`)({serializedTransaction:y,throwOnReceiptRevert:h})}throw b?.type===`smart`?new km({metaMessages:["Consider using the `sendUserOperation` Action instead."],docsPath:`/docs/actions/bundler/sendUserOperation`,type:`smart`}):new km({docsPath:`/docs/actions/wallet/sendTransactionSync`,type:b?.type})}catch(e){throw e instanceof km?e:jm(e,{...t,account:b,chain:t.chain||void 0})}}async function Cv(e,t){let{id:n}=t;await e.request({method:`wallet_showCallsStatus`,params:[n]})}ra();async function wv(e,t){let{account:n=e.account}=t;if(!n)throw new Om({docsPath:`/docs/eip7702/signAuthorization`});let r=na(n);if(!r.signAuthorization)throw new km({docsPath:`/docs/eip7702/signAuthorization`,metaMessages:["The `signAuthorization` Action does not support JSON-RPC Accounts."],type:r.type});let i=await _v(e,t);return r.signAuthorization(i)}ra(),A();async function Tv(e,{account:t=e.account,message:n}){if(!t)throw new Om({docsPath:`/docs/actions/wallet/signMessage`});let r=na(t);if(r.signMessage)return r.signMessage({message:n});let i=(()=>typeof n==`string`?jn(n):n.raw instanceof Uint8Array?On(n.raw):n.raw)();return e.request({method:`personal_sign`,params:[i,r.address]},{retryCount:0})}ra(),A(),Wu(),$u();async function Ev(e,t){let{account:n=e.account,chain:r=e.chain,...i}=t;if(!n)throw new Om({docsPath:`/docs/actions/wallet/signTransaction`});let a=na(n);Qu({account:a,...t});let o=await E(e,Md,`getChainId`)({});r!==null&&Am({currentChainId:o,chain:r});let s=(r?.formatters||e.chain?.formatters)?.transactionRequest?.format||Bu;return a.signTransaction?a.signTransaction({...i,chainId:o},{serializer:e.chain?.serializers?.transaction}):await e.request({method:`eth_signTransaction`,params:[{...s(i,`signTransaction`),chainId:k(o),from:a.address}]},{retryCount:0})}ra();async function Dv(e,t){let{account:n=e.account,domain:r,message:i,primaryType:a}=t;if(!n)throw new Om({docsPath:`/docs/actions/wallet/signTypedData`});let o=na(n),s={EIP712Domain:jh({domain:r}),...t.types};if(Ah({domain:r,message:i,primaryType:a,types:s}),o.signTypedData)return o.signTypedData({domain:r,message:i,primaryType:a,types:s});let c=kh({domain:r,message:i,primaryType:a,types:s});return e.request({method:`eth_signTypedData_v4`,params:[o.address,c]},{retryCount:0})}A();async function Ov(e,{id:t}){await e.request({method:`wallet_switchEthereumChain`,params:[{chainId:k(t)}]},{retryCount:0})}async function kv(e,t){return await e.request({method:`wallet_watchAsset`,params:t},{retryCount:0})}async function Av(e,t){return Fm.internal(e,Sv,`sendTransactionSync`,t)}function jv(e){return{addChain:t=>fv(e,t),deployContract:t=>pv(e,t),getAddresses:()=>mv(e),getCallsStatus:t=>Um(e,t),getCapabilities:t=>hv(e,t),getChainId:()=>Md(e),getPermissions:()=>gv(e),prepareAuthorization:t=>_v(e,t),prepareTransactionRequest:t=>Fd(e,t),requestAddresses:()=>vv(e),requestPermissions:t=>yv(e,t),sendCalls:t=>Hm(e,t),sendCallsSync:t=>bv(e,t),sendRawTransaction:t=>Mm(e,t),sendRawTransactionSync:t=>dv(e,t),sendTransaction:t=>Pm(e,t),sendTransactionSync:t=>Sv(e,t),showCallsStatus:t=>Cv(e,t),signAuthorization:t=>wv(e,t),signMessage:t=>Tv(e,t),signTransaction:t=>Ev(e,t),signTypedData:t=>Dv(e,t),switchChain:t=>Ov(e,t),waitForCallsStatus:t=>Wm(e,t),watchAsset:t=>kv(e,t),writeContract:t=>Fm(e,t),writeContractSync:t=>Av(e,t)}}function Mv(e){let{key:t=`wallet`,name:n=`Wallet Client`,transport:r}=e;return Xm({...e,key:t,name:n,transport:r,type:`walletClient`}).extend(jv)}function Nv({key:e,methods:t,name:n,request:r,retryCount:i=3,retryDelay:a=150,timeout:o,type:s},c){let l=Ym();return{config:{key:e,methods:t,name:n,request:r,retryCount:i,retryDelay:a,timeout:o,type:s},request:vh(r,{methods:t,retryCount:i,retryDelay:a,uid:l}),value:c}}function Pv(e,t={}){let{key:n=`custom`,methods:r,name:i=`Custom Provider`,retryDelay:a}=t;return({retryCount:o})=>Nv({key:n,methods:r,name:i,request:e.request.bind(e),retryCount:t.retryCount??o,retryDelay:a,type:`custom`})}Mu(),os();function Fv(e,t={}){let{key:n=`fallback`,name:r=`Fallback`,rank:i=!1,shouldThrow:a=Iv,retryCount:o,retryDelay:s}=t;return(({chain:t,pollingInterval:c=4e3,timeout:l,...u})=>{let d=e,f=()=>{},p=Nv({key:n,name:r,async request({method:e,params:n}){let r,i=async(o=0)=>{let s=d[o]({...u,chain:t,retryCount:0,timeout:l});try{let t=await s.request({method:e,params:n});return f({method:e,params:n,response:t,transport:s,status:`success`}),t}catch(c){if(f({error:c,method:e,params:n,transport:s,status:`error`}),a(c)||o===d.length-1||(r??=d.slice(o+1).some(n=>{let{include:r,exclude:i}=n({chain:t}).config.methods||{};return r?r.includes(e):i?!i.includes(e):!0}),!r))throw c;return i(o+1)}};return i()},retryCount:o,retryDelay:s,type:`fallback`},{onResponse:e=>f=e,transports:d.map(e=>e({chain:t,retryCount:0}))});if(i){let e=typeof i==`object`?i:{};Lv({chain:t,interval:e.interval??c,onTransports:e=>d=e,ping:e.ping,sampleCount:e.sampleCount,timeout:e.timeout,transports:d,weights:e.weights})}return p})}function Iv(e){return!!(`code`in e&&typeof e.code==`number`&&(e.code===Bo.code||e.code===Wo.code||bu.nodeMessage.test(e.message)||e.code===5e3))}function Lv({chain:e,interval:t=4e3,onTransports:n,ping:r,sampleCount:i=10,timeout:a=1e3,transports:o,weights:s={}}){let{stability:c=.7,latency:l=.3}=s,u=[],d=async()=>{let s=await Promise.all(o.map(async t=>{let n=t({chain:e,retryCount:0,timeout:a}),i=Date.now(),o,s;try{await(r?r({transport:n}):n.request({method:`net_listening`})),s=1}catch{s=0}finally{o=Date.now()}return{latency:o-i,success:s}}));u.push(s),u.length>i&&u.shift();let f=Math.max(...u.map(e=>Math.max(...e.map(({latency:e})=>e)))),p=o.map((e,t)=>{let n=u.map(e=>e[t].latency),r=1-n.reduce((e,t)=>e+t,0)/n.length/f,i=u.map(e=>e[t].success),a=i.reduce((e,t)=>e+t,0)/i.length;return a===0?[0,t]:[l*r+c*a,t]}).sort((e,t)=>t[0]-e[0]);n(p.map(([,e])=>o[e])),await bm(t),d()};d()}O();var Rv=class extends D{constructor(){super(`No URL was provided to the Transport. Please provide a valid RPC URL to the Transport.`,{docsPath:`/docs/clients/intro`,name:`UrlRequiredError`})}};ko(),Bp();function zv(e,t={}){let{batch:n,fetchFn:r,fetchOptions:i,key:a=`http`,methods:o,name:s=`HTTP JSON-RPC`,onFetchRequest:c,onFetchResponse:l,retryDelay:u,raw:d}=t;return({chain:f,retryCount:p,timeout:m})=>{let{batchSize:h=1e3,wait:g=0}=typeof n==`object`?n:{},_=t.retryCount??p,v=m??t.timeout??1e4,y=e||f?.rpcUrls.default.http[0];if(!y)throw new Rv;let b=Ch(y,{fetchFn:r,fetchOptions:i,onRequest:c,onResponse:l,timeout:v});return Nv({key:a,methods:o,name:s,async request({method:e,params:t}){let r={method:e,params:t},{schedule:i}=Rp({id:y,wait:g,shouldSplitBatch(e){return e.length>h},fn:e=>b.request({body:e}),sort:(e,t)=>e.id-t.id}),[{error:a,result:o}]=await(async e=>n?i(e):[await b.request({body:e})])(r);if(d)return{error:a,result:o};if(a)throw new Do({body:r,error:a,url:y});return o},retryCount:_,retryDelay:u,timeout:v,type:`http`},{fetchOptions:i,url:y})}}const Bv=L({id:16600,name:`0G Newton Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-newton.0g.ai`}},testnet:!0}),Vv=L({id:16601,name:`0G Galileo Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-galileo.0g.ai`}},testnet:!0}),Hv=L({id:16661,name:`0G Mainnet`,nativeCurrency:{name:`0G`,symbol:`0G`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan.0g.ai`}},testnet:!1}),Uv=L({id:995,name:`5ireChain`,nativeCurrency:{name:`5ire Token`,symbol:`5IRE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.5ire.network`]}},blockExplorers:{default:{name:`5ireChain Mainnet Explorer`,url:`https://5irescan.io/`}},testnet:!1}),Wv=L({id:179,name:`ABEY Mainnet`,nativeCurrency:{name:`ABEY`,symbol:`ABEY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.abeychain.com`]}},blockExplorers:{default:{name:`Abey Scan`,url:`https://abeyscan.com`}},testnet:!1});Zu();const Gv=50000n,Kv=Yu*32n;Dn(),Hn(),A(),Wu();const qv={block:ld({format(e){let t=e.transactions?.map(e=>{if(typeof e==`string`)return e;let t=qv.transaction?.format(e);return t.typeHex===`0x71`?t.type=`eip712`:t.typeHex===`0xff`&&(t.type=`priority`),t});return{l1BatchNumber:e.l1BatchNumber?wn(e.l1BatchNumber):null,l1BatchTimestamp:e.l1BatchTimestamp?wn(e.l1BatchTimestamp):null,transactions:t}}}),transaction:od({format(e){let t={};return e.type===`0x71`?t.type=`eip712`:e.type===`0xff`&&(t.type=`priority`),{...t,l1BatchNumber:e.l1BatchNumber?wn(e.l1BatchNumber):null,l1BatchTxIndex:e.l1BatchTxIndex?wn(e.l1BatchTxIndex):null}}}),transactionReceipt:Bm({format(e){return{l1BatchNumber:e.l1BatchNumber?wn(e.l1BatchNumber):null,l1BatchTxIndex:e.l1BatchTxIndex?wn(e.l1BatchTxIndex):null,logs:e.logs.map(e=>({...zd(e),l1BatchNumber:e.l1BatchNumber?wn(e.l1BatchNumber):null,transactionLogIndex:En(e.transactionLogIndex),logType:e.logType})),l2ToL1Logs:e.l2ToL1Logs.map(e=>({blockNumber:wn(e.blockHash),blockHash:e.blockHash,l1BatchNumber:e.l1BatchNumber?wn(e.l1BatchNumber):null,transactionIndex:wn(e.transactionIndex),shardId:wn(e.shardId),isService:e.isService,sender:e.sender,key:e.key,value:e.value,transactionHash:e.transactionHash,logIndex:wn(e.logIndex)}))}}}),transactionRequest:Uu({exclude:[`customSignature`,`factoryDeps`,`gasPerPubdata`,`paymaster`,`paymasterInput`],format(e){return e.gasPerPubdata||e.paymaster&&e.paymasterInput||e.factoryDeps||e.customSignature?{eip712Meta:{...e.gasPerPubdata?{gasPerPubdata:On(e.gasPerPubdata)}:{gasPerPubdata:On(Gv)},...e.paymaster&&e.paymasterInput?{paymasterParams:{paymaster:e.paymaster,paymasterInput:Array.from(Ln(e.paymasterInput))}}:{},...e.factoryDeps?{factoryDeps:e.factoryDeps.map(e=>Array.from(Ln(e)))}:{},...e.customSignature?{customSignature:Array.from(Ln(e.customSignature))}:{}},type:`0x71`}:{}}})};O();var Jv=class extends D{constructor(){super([`Transaction is not an EIP712 transaction.`,``,`Transaction must:`,' - include `type: "eip712"`'," - include one of the following: `customSignature`, `paymaster`, `paymasterInput`, `gasPerPubdata`, `factoryDeps`"].join(` -`),{name:`InvalidEip712TransactionError`})}};function Yv(e){return!!(e.type===`eip712`||`customSignature`in e&&e.customSignature||`paymaster`in e&&e.paymaster||`paymasterInput`in e&&e.paymasterInput||`gasPerPubdata`in e&&typeof e.gasPerPubdata==`bigint`||`factoryDeps`in e&&e.factoryDeps)}pi(),O(),Op(),Ci();function Xv(e){let{chainId:t,to:n,from:r,paymaster:i,paymasterInput:a}=e;if(!Yv(e))throw new Jv;if(!t||t<=0)throw new Dp({chainId:t});if(n&&!bi(n))throw new fi({address:n});if(r&&!bi(r))throw new fi({address:r});if(i&&!bi(i))throw new fi({address:i});if(i&&!a)throw new D("`paymasterInput` must be provided when `paymaster` is defined");if(!i&&a)throw new D("`paymaster` must be provided when `paymasterInput` is defined")}Di(),A();function Zv(e,t){return Yv(e)?$v(e):sh(e,t)}const Qv={transaction:Zv};function $v(e){let{chainId:t,gas:n,nonce:r,to:i,from:a,value:o,maxFeePerGas:s,maxPriorityFeePerGas:c,customSignature:l,factoryDeps:u,paymaster:d,paymasterInput:f,gasPerPubdata:p,data:m}=e;Xv(e);let h=[r?On(r):`0x`,c?On(c):`0x`,s?On(s):`0x`,n?On(n):`0x`,i??`0x`,o?On(o):`0x`,m??`0x`,On(t),On(``),On(``),On(t),a??`0x`,On(p||Gv),u??[],l??`0x`,d&&f?[d,f]:[]];return Ei([`0x71`,M(h)])}O();var ey=class extends D{constructor({givenLength:e,maxBytecodeSize:t}){super(`Bytecode cannot be longer than ${t} bytes. Given length: ${e}`,{name:`BytecodeLengthExceedsMaxSizeError`})}},ty=class extends D{constructor({givenLengthInWords:e}){super(`Bytecode length in 32-byte words must be odd. Given length in words: ${e}`,{name:`BytecodeLengthInWordsMustBeOddError`})}},ny=class extends D{constructor({givenLength:e}){super(`The bytecode length in bytes must be divisible by 32. Given length: ${e}`,{name:`BytecodeLengthMustBeDivisibleBy32Error`})}};hn(),Hn();function ry(e){let t=Pn(e);if(t.length%32!=0)throw new ny({givenLength:t.length});if(t.length>Kv)throw new ey({givenLength:t.length,maxBytecodeSize:Kv});let n=vd(t),r=Pn(n),i=t.length/32;if(i%2==0)throw new ty({givenLengthInWords:i});let a=Pn(i),o=fn(a,{size:2}),s=new Uint8Array([1,0]);return r.set(s,0),r.set(o,2),r}A();const iy=e=>{Xv(e);let t=ay(e);return{domain:{name:`zkSync`,version:`2`,chainId:e.chainId},types:{Transaction:[{name:`txType`,type:`uint256`},{name:`from`,type:`uint256`},{name:`to`,type:`uint256`},{name:`gasLimit`,type:`uint256`},{name:`gasPerPubdataByteLimit`,type:`uint256`},{name:`maxFeePerGas`,type:`uint256`},{name:`maxPriorityFeePerGas`,type:`uint256`},{name:`paymaster`,type:`uint256`},{name:`nonce`,type:`uint256`},{name:`value`,type:`uint256`},{name:`data`,type:`bytes`},{name:`factoryDeps`,type:`bytes32[]`},{name:`paymasterInput`,type:`bytes`}]},primaryType:`Transaction`,message:t}};function ay(e){let{gas:t,nonce:n,to:r,from:i,value:a,maxFeePerGas:o,maxPriorityFeePerGas:s,factoryDeps:c,paymaster:l,paymasterInput:u,gasPerPubdata:d,data:f}=e;return{txType:113n,from:BigInt(i),to:r?BigInt(r):0n,gasLimit:t??0n,gasPerPubdataByteLimit:d??50000n,maxFeePerGas:o??0n,maxPriorityFeePerGas:s??0n,paymaster:l?BigInt(l):0n,nonce:n?BigInt(n):0n,value:a??0n,data:f??`0x`,factoryDeps:c?.map(e=>On(ry(e)))??[],paymasterInput:u||`0x`}}const oy={blockTime:1e3,formatters:qv,serializers:Qv,custom:{getEip712Domain:iy}},sy=L({...oy,id:2741,name:`Abstract`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.mainnet.abs.xyz`],webSocket:[`wss://api.mainnet.abs.xyz/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://abscan.org`},native:{name:`Abstract Explorer`,url:`https://explorer.mainnet.abs.xyz`}},contracts:{multicall3:{address:`0xAa4De41dba0Ca5dCBb288b7cC6b708F3aaC759E7`,blockCreated:5288},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:5263}}}),cy=L({...oy,id:11124,name:`Abstract Testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.testnet.abs.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.abscan.org`},native:{name:`Abstract Explorer`,url:`https://explorer.testnet.abs.xyz`}},testnet:!0,contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:358349},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:431682}}}),ly=L({id:787,name:`Acala`,network:`acala`,nativeCurrency:{name:`Acala`,symbol:`ACA`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-acala.aca-api.network`],webSocket:[`wss://eth-rpc-acala.aca-api.network`]}},blockExplorers:{default:{name:`Acala Blockscout`,url:`https://blockscout.acala.network`,apiUrl:`https://blockscout.acala.network/api`}},testnet:!1}),uy=L({id:47,name:`Acria IntelliChain`,nativeCurrency:{decimals:18,name:`ACRIA`,symbol:`ACRIA`},rpcUrls:{default:{http:[`https://aic.acria.ai`]}},blockExplorers:{default:{name:`Acria Explorer`,url:`https://explorer.acria.ai`}},testnet:!1}),dy=L({id:1215,name:`ADF Chain`,nativeCurrency:{name:`ADDFILL`,symbol:`ADF`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.adftechnology.com`]}},blockExplorers:{default:{name:`ADF Mainnet Explorer`,url:`https://explorer.adftechnology.com`}},testnet:!1}),fy=L({id:9990,name:`Agung Network`,nativeCurrency:{decimals:18,name:`Agung`,symbol:`AGNG`},rpcUrls:{default:{http:[`https://wss-async.agung.peaq.network`],webSocket:[`wss://wss-async.agung.peaq.network`]}},blockExplorers:{default:{name:`Subscan`,url:`https://agung-testnet.subscan.io`}},testnet:!0}),py=L({id:168,name:`AIOZ Network`,nativeCurrency:{decimals:18,name:`AIOZ`,symbol:`AIOZ`},rpcUrls:{default:{http:[`https://eth-dataseed.aioz.network`]}},blockExplorers:{default:{name:`AIOZ Explorer`,url:`https://explorer.aioz.network`}},testnet:!1}),my=L({id:41455,name:`Aleph Zero`,nativeCurrency:{name:`Aleph Zero`,symbol:`AZERO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alephzero.raas.gelato.cloud`]}},blockExplorers:{default:{name:`Aleph Zero EVM Explorer`,url:`https://evm-explorer.alephzero.org`,apiUrl:`https://evm-explorer.alephzero.org/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4603377}}}),hy=L({id:2039,name:`Aleph Zero Testnet`,nativeCurrency:{name:`TZERO`,symbol:`TZERO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alephzero-testnet.gelato.digital`],webSocket:[`wss://ws.alephzero-testnet.gelato.digital`]}},blockExplorers:{default:{name:`Aleph Zero EVM Testnet explorer`,url:`https://evm-explorer-testnet.alephzero.org`,apiUrl:`https://evm-explorer-testnet.alephzero.org/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2861745}},testnet:!0}),gy=L({id:10241024,name:`AlienX Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alienxchain.io/http`]}},blockExplorers:{default:{name:`AlienX Explorer`,url:`https://explorer.alienxchain.io`}},testnet:!1}),_y=L({id:10241025,name:`ALIENX Hal Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://hal-rpc.alienxchain.io/http`]}},blockExplorers:{default:{name:`AlienX Explorer`,url:`https://hal-explorer.alienxchain.io`}},testnet:!0}),vy={gasPriceOracle:{address:`0x420000000000000000000000000000000000000F`},l1Block:{address:`0x4200000000000000000000000000000000000015`},l2CrossDomainMessenger:{address:`0x4200000000000000000000000000000000000007`},l2Erc721Bridge:{address:`0x4200000000000000000000000000000000000014`},l2StandardBridge:{address:`0x4200000000000000000000000000000000000010`},l2ToL1MessagePasser:{address:`0x4200000000000000000000000000000000000016`}};Dn();const yy={block:ld({format(e){return{transactions:e.transactions?.map(e=>{if(typeof e==`string`)return e;let t=ad(e);return t.typeHex===`0x7e`&&(t.isSystemTx=e.isSystemTx,t.mint=e.mint?wn(e.mint):void 0,t.sourceHash=e.sourceHash,t.type=`deposit`),t}),stateRoot:e.stateRoot}}}),transaction:od({format(e){let t={};return e.type===`0x7e`&&(t.isSystemTx=e.isSystemTx,t.mint=e.mint?wn(e.mint):void 0,t.sourceHash=e.sourceHash,t.type=`deposit`),t}}),transactionReceipt:Bm({format(e){return{l1GasPrice:e.l1GasPrice?wn(e.l1GasPrice):null,l1GasUsed:e.l1GasUsed?wn(e.l1GasUsed):null,l1Fee:e.l1Fee?wn(e.l1Fee):null,l1FeeScalar:e.l1FeeScalar?Number(e.l1FeeScalar):null}}})};pi(),Ci(),Di(),A();function by(e,t){return Cy(e)?Sy(e):sh(e,t)}const xy={transaction:by};function Sy(e){wy(e);let{sourceHash:t,data:n,from:r,gas:i,isSystemTx:a,mint:o,to:s,value:c}=e,l=[t,r,s??`0x`,o?On(o):`0x`,c?On(c):`0x`,i?On(i):`0x`,a?`0x1`:`0x`,n??`0x`];return Ei([`0x7e`,M(l)])}function Cy(e){return e.type===`deposit`||e.sourceHash!==void 0}function wy(e){let{from:t,to:n}=e;if(t&&!bi(t))throw new fi({address:t});if(n&&!bi(n))throw new fi({address:n})}const R={blockTime:2e3,contracts:vy,formatters:yy,serializers:xy};var Ty=1;const Ey=L({...R,id:888888888,name:`Ancient8`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.ancient8.gg`]}},blockExplorers:{default:{name:`Ancient8 explorer`,url:`https://scan.ancient8.gg`,apiUrl:`https://scan.ancient8.gg/api`}},contracts:{...R.contracts,l2OutputOracle:{[Ty]:{address:`0xB09DC08428C8b4EFB4ff9C0827386CDF34277996`}},portal:{[Ty]:{address:`0x639F2AECE398Aa76b07e59eF6abe2cFe32bacb68`,blockCreated:19070571}},l1StandardBridge:{[Ty]:{address:`0xd5e3eDf5b68135D559D572E26bF863FBC1950033`,blockCreated:19070571}}},sourceId:Ty});var Dy=11155111;const Oy=L({...R,id:28122024,name:`Ancient8 Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpcv2-testnet.ancient8.gg`]}},blockExplorers:{default:{name:`Ancient8 Celestia Testnet explorer`,url:`https://scanv2-testnet.ancient8.gg`,apiUrl:`https://scanv2-testnet.ancient8.gg/api`}},contracts:{...R.contracts,l2OutputOracle:{[Dy]:{address:`0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB`}},portal:{[Dy]:{address:`0xfa1d9E26A6aCD7b22115D27572c1221B9803c960`,blockCreated:4972908}},l1StandardBridge:{[Dy]:{address:`0xF6Bc0146d3c74D48306e79Ae134A260E418C9335`,blockCreated:4972908}}},sourceId:Dy}),ky=L({id:31337,name:`Anvil`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`],webSocket:[`ws://127.0.0.1:8545`]}}}),Ay=L({id:33139,name:`Ape Chain`,nativeCurrency:{name:`ApeCoin`,symbol:`APE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.apechain.com/http`],webSocket:[`wss://rpc.apechain.com/ws`]}},blockExplorers:{default:{name:`Apescan`,url:`https://apescan.io`,apiUrl:`https://api.apescan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:20889}},sourceId:42161}),jy=L({id:3993,name:`APEX Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.apexlayer.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp-testnet.apexlayer.xyz`,apiUrl:`https://exp-testnet.apexlayer.xyz/api`}},contracts:{multicall3:{address:`0xf7642be33a6b18D16a995657adb5a68CD0438aE2`,blockCreated:283775}},testnet:!0}),My=L({id:42161,name:`Arbitrum One`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:250,rpcUrls:{default:{http:[`https://arb1.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://arbiscan.io`,apiUrl:`https://api.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7654707}}}),Ny=L({id:421613,name:`Arbitrum Goerli`,nativeCurrency:{name:`Arbitrum Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli-rollup.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://goerli.arbiscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:88114}},testnet:!0}),Py=L({id:42170,name:`Arbitrum Nova`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://nova.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://nova.arbiscan.io`,apiUrl:`https://api-nova.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1746963}}}),Fy=L({id:421614,name:`Arbitrum Sepolia`,blockTime:250,nativeCurrency:{name:`Arbitrum Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rollup.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://sepolia.arbiscan.io`,apiUrl:`https://api-sepolia.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:81930}},testnet:!0}),Iy=L({id:7897,name:`Arena-Z`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.arena-z.gg`]}},blockExplorers:{default:{name:`Arena-Z Explorer`,url:`https://explorer.arena-z.gg`,apiUrl:`https://explorer.arena-z.gg`}}}),Ly=L({id:463,name:`Areon Network`,nativeCurrency:{decimals:18,name:`AREA`,symbol:`AREA`},rpcUrls:{default:{http:[`https://mainnet-rpc.areon.network`],webSocket:[`wss://mainnet-ws.areon.network`]}},blockExplorers:{default:{name:`Areonscan`,url:`https://areonscan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:353286}},testnet:!1}),Ry=L({id:462,name:`Areon Network Testnet`,nativeCurrency:{decimals:18,name:`TAREA`,symbol:`TAREA`},rpcUrls:{default:{http:[`https://testnet-rpc.areon.network`],webSocket:[`wss://testnet-ws.areon.network`]}},blockExplorers:{default:{name:`Areonscan`,url:`https://areonscan.com`}},testnet:!0}),zy=L({id:463,name:`Areum`,nativeCurrency:{decimals:18,name:`AREA`,symbol:`AREA`},rpcUrls:{default:{http:[`https://mainnet-rpc.areum.network`],webSocket:[`wss://mainnet-ws.areum.network`]}},blockExplorers:{default:{name:`Areum Explorer`,url:`https://explorer.areum.network`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:353286}},testnet:!1}),By=L({id:11822,name:`Artela Testnet`,nativeCurrency:{name:`ART`,symbol:`ART`,decimals:18},rpcUrls:{default:{http:[`https://betanet-rpc1.artela.network`]}},blockExplorers:{default:{name:`Artela`,url:`https://betanet-scan.artela.network`,apiUrl:`https://betanet-scan.artela.network/api`}},contracts:{multicall3:{address:`0xd07c8635f76e8745Ee7092fbb6e8fbc5FeF09DD7`,blockCreated:7001871}},testnet:!0}),Vy=L({id:10242,name:`Arthera`,nativeCurrency:{name:`Arthera`,symbol:`AA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.arthera.net`]}},blockExplorers:{default:{name:`Arthera EVM Explorer`,url:`https://explorer.arthera.net`,apiUrl:`https://explorer.arthera.net/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4502791}}}),Hy=L({id:10243,name:`Arthera Testnet`,nativeCurrency:{name:`Arthera`,symbol:`AA`,decimals:18},rpcUrls:{default:{http:[`https://rpc-test.arthera.net`]}},blockExplorers:{default:{name:`Arthera EVM Explorer`,url:`https://explorer-test.arthera.net`,apiUrl:`https://explorer-test.arthera.net/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:22051}}}),Uy=L({id:42420,name:`AssetChain Mainnet`,nativeCurrency:{decimals:18,name:`Real World Asset`,symbol:`RWA`},rpcUrls:{default:{http:[`https://mainnet-rpc.assetchain.org`]}},blockExplorers:{default:{name:`Asset Chain Explorer`,url:`https://scan.assetchain.org`,apiUrl:`https://scan.assetchain.org/api`}},testnet:!1,contracts:{}}),Wy=L({id:42421,name:`AssetChain Testnet`,nativeCurrency:{decimals:18,name:`Real World Asset`,symbol:`RWA`},rpcUrls:{default:{http:[`https://enugu-rpc.assetchain.org`]}},blockExplorers:{default:{name:`Asset Chain Testnet Explorer`,url:`https://scan-testnet.assetchain.org`,apiUrl:`https://scan-testnet.assetchain.org/api`}},testnet:!0,contracts:{multicall3:{address:`0x989F832D35988cb5e3eB001Fa2Fe789469EC31Ea`,blockCreated:17177}}}),Gy=L({id:592,name:`Astar`,network:`astar-mainnet`,nativeCurrency:{name:`Astar`,symbol:`ASTR`,decimals:18},rpcUrls:{default:{http:[`https://astar.api.onfinality.io/public`]}},blockExplorers:{default:{name:`Astar Subscan`,url:`https://astar.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:761794}},testnet:!1}),Ky=L({id:3776,name:`Astar zkEVM`,network:`AstarZkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-zkevm.astar.network`]}},blockExplorers:{default:{name:`Astar zkEVM Explorer`,url:`https://astar-zkevm.explorer.startale.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:93528}},testnet:!1}),qy=L({id:6038361,name:`Astar zkEVM Testnet zKyoto`,network:`zKyoto`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.startale.com/zkyoto`]}},blockExplorers:{default:{name:`zKyoto Explorer`,url:`https://zkyoto.explorer.startale.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:196153}},testnet:!0}),Jy=L({id:2340,name:`Atleta Olympia`,nativeCurrency:{decimals:18,name:`Atla`,symbol:`ATLA`},rpcUrls:{default:{http:[`https://testnet-rpc.atleta.network:9944`,`https://testnet-rpc.atleta.network`],ws:[`wss://testnet-rpc.atleta.network:9944`]}},blockExplorers:{default:{name:`Atleta Olympia Explorer`,url:`https://blockscout.atleta.network`,apiUrl:`https://blockscout.atleta.network/api`}},contracts:{multicall3:{address:`0x1472ec6392180fb84F345d2455bCC75B26577115`,blockCreated:1076473}},testnet:!0}),Yy=L({id:1313161554,name:`Aurora`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.aurora.dev`]}},blockExplorers:{default:{name:`Aurorascan`,url:`https://aurorascan.dev`,apiUrl:`https://aurorascan.dev/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:62907816}}}),Xy=L({id:1313161555,name:`Aurora Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet.aurora.dev`]}},blockExplorers:{default:{name:`Aurorascan`,url:`https://testnet.aurorascan.dev`,apiUrl:`https://testnet.aurorascan.dev/api`}},testnet:!0}),Zy=L({id:205205,name:`Auroria Testnet`,network:`auroria`,nativeCurrency:{name:`Auroria Stratis`,symbol:`tSTRAX`,decimals:18},rpcUrls:{default:{http:[`https://auroria.rpc.stratisevm.com`]}},blockExplorers:{default:{name:`Auroria Testnet Explorer`,url:`https://auroria.explorer.stratisevm.com`}},testnet:!0}),Qy=L({id:785,name:`Autheo Testnet`,nativeCurrency:{decimals:18,name:`Autheo`,symbol:`THEO`},rpcUrls:{default:{http:[`https://testnet-rpc1.autheo.com`,`https://testnet-rpc2.autheo.com`]}},blockExplorers:{default:{name:`Autheo Testnet Block Explorer`,url:`https://testnet-explorer.autheo.com/`}}}),$y=L({id:43114,name:`Avalanche`,blockTime:1700,nativeCurrency:{decimals:18,name:`Avalanche`,symbol:`AVAX`},rpcUrls:{default:{http:[`https://api.avax.network/ext/bc/C/rpc`]}},blockExplorers:{default:{name:`SnowTrace`,url:`https://snowtrace.io`,apiUrl:`https://api.snowtrace.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11907934}}}),eb=L({id:43113,name:`Avalanche Fuji`,nativeCurrency:{decimals:18,name:`Avalanche Fuji`,symbol:`AVAX`},rpcUrls:{default:{http:[`https://api.avax-test.network/ext/bc/C/rpc`]}},blockExplorers:{default:{name:`SnowTrace`,url:`https://testnet.snowtrace.io`,apiUrl:`https://api-testnet.snowtrace.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7096959}},testnet:!0}),tb=L({id:8333,name:`B3`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.b3.fun/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.b3.fun`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}},sourceId:8453}),nb=L({id:1993,name:`B3 Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.b3.fun/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia.explorer.b3.fun`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}},testnet:!0,sourceId:168587773}),rb=L({id:5165,network:`bahamut`,name:`Bahamut`,nativeCurrency:{name:`Fasttoken`,symbol:`FTN`,decimals:18},rpcUrls:{default:{http:[`https://rpc1.bahamut.io`,`https://bahamut-rpc.publicnode.com`,`https://rpc2.bahamut.io`],webSocket:[`wss://ws1.sahara.bahamutchain.com`,`wss://bahamut-rpc.publicnode.com`,`wss://ws2.sahara.bahamutchain.com`]}},blockExplorers:{default:{name:`Ftnscan`,url:`https://www.ftnscan.com`,apiUrl:`https://www.ftnscan.com/api`}}});var ib=1;const ab=L({...R,id:8453,name:`Base`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://basescan.org`,apiUrl:`https://api.basescan.org/api`}},contracts:{...R.contracts,disputeGameFactory:{[ib]:{address:`0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e`}},l2OutputOracle:{[ib]:{address:`0x56315b90c40730925ec5485cf004d835058518A0`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:5022},portal:{[ib]:{address:`0x49048044D57e1C92A77f79988d21Fa8fAF74E97e`,blockCreated:17482143}},l1StandardBridge:{[ib]:{address:`0x3154Cf16ccdb4C6d922629664174b904d80F2C35`,blockCreated:17482143}}},sourceId:ib}),ob=L({...ab,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://mainnet-preconf.base.org`]}}}),sb=L({id:123420001114,name:`Basecamp Testnet`,nativeCurrency:{decimals:18,name:`Camp`,symbol:`CAMP`},rpcUrls:{default:{http:[`https://rpc.basecamp.t.raas.gelato.cloud`]}},blockExplorers:{default:{name:`basecamp`,url:`https://basecamp.cloud.blockscout.com`}},testnet:!0});var cb=5;const lb=L({...R,id:84531,name:`Base Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://goerli.basescan.org`,apiUrl:`https://goerli.basescan.org/api`}},contracts:{...R.contracts,l2OutputOracle:{[cb]:{address:`0x2A35891ff30313CcFa6CE88dcf3858bb075A2298`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1376988},portal:{[cb]:{address:`0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA`}},l1StandardBridge:{[cb]:{address:`0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a`}}},testnet:!0,sourceId:cb});var ub=11155111;const db=L({...R,id:84532,network:`base-sepolia`,name:`Base Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://sepolia.basescan.org`,apiUrl:`https://api-sepolia.basescan.org/api`}},contracts:{...R.contracts,disputeGameFactory:{[ub]:{address:`0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1`}},l2OutputOracle:{[ub]:{address:`0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254`}},portal:{[ub]:{address:`0x49f53e41452c74589e85ca1677426ba426459e85`,blockCreated:4446677}},l1StandardBridge:{[ub]:{address:`0xfd0Bf71F60660E2f608ed56e1659C450eB113120`,blockCreated:4446677}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1059647}},testnet:!0,sourceId:ub}),fb=L({...db,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://sepolia-preconf.base.org`]}}}),pb=L({id:4337,name:`Beam`,network:`beam`,nativeCurrency:{decimals:18,name:`Beam`,symbol:`BEAM`},rpcUrls:{default:{http:[`https://build.onbeam.com/rpc`],webSocket:[`wss://build.onbeam.com/ws`]}},blockExplorers:{default:{name:`Beam Explorer`,url:`https://subnets.avax.network/beam`}},contracts:{multicall3:{address:`0x4956f15efdc3dc16645e90cc356eafa65ffc65ec`,blockCreated:1}}}),mb=L({id:13337,name:`Beam Testnet`,network:`beam`,nativeCurrency:{decimals:18,name:`Beam`,symbol:`BEAM`},rpcUrls:{default:{http:[`https://build.onbeam.com/rpc/testnet`],webSocket:[`wss://build.onbeam.com/ws/testnet`]}},blockExplorers:{default:{name:`Beam Explorer`,url:`https://subnets-test.avax.network/beam`}},contracts:{multicall3:{address:`0x9bf49b704ee2a095b95c1f2d4eb9010510c41c9e`,blockCreated:3}},testnet:!0}),hb=L({id:641230,name:`Bear Network Chain Mainnet`,nativeCurrency:{decimals:18,name:`BearNetworkChain`,symbol:`BRNKC`},rpcUrls:{default:{http:[`https://brnkc-mainnet.bearnetwork.net`]}},blockExplorers:{default:{name:`BrnkScan`,url:`https://brnkscan.bearnetwork.net`,apiUrl:`https://brnkscan.bearnetwork.net/api`}}}),gb=L({id:751230,name:`Bear Network Chain Testnet`,nativeCurrency:{decimals:18,name:`tBRNKC`,symbol:`tBRNKC`},rpcUrls:{default:{http:[`https://brnkc-test.bearnetwork.net`]}},blockExplorers:{default:{name:`BrnkTestScan`,url:`https://brnktest-scan.bearnetwork.net`,apiUrl:`https://brnktest-scan.bearnetwork.net/api`}},testnet:!0}),_b=L({id:80094,name:`Berachain`,blockTime:2e3,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},ensRegistry:{address:`0x5b22280886a2f5e09a49bea7e320eab0e5320e28`,blockCreated:877007},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:9310021}},rpcUrls:{default:{http:[`https://rpc.berachain.com`]}},blockExplorers:{default:{name:`Berascan`,url:`https://berascan.com`}},ensTlds:[`.bera`],testnet:!1}),vb=L({id:80069,blockTime:2e3,name:`Berachain Bepolia`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},rpcUrls:{default:{http:[`https://bepolia.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berascan`,url:`https://bepolia.beratrail.io`}},testnet:!0}),yb=L({id:80085,name:`Berachain Artio`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},rpcUrls:{default:{http:[`https://artio.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berachain`,url:`https://artio.beratrail.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:866924}},testnet:!0}),bb=L({id:80084,name:`Berachain bArtio`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:109269},ensRegistry:{address:`0xB0eef18971290b333450586D33dcA6cE122651D2`,blockCreated:7736794},ensUniversalResolver:{address:`0x41692Ef1EA0C79E6b73077E4A67572D2BDbD7057`,blockCreated:7736795}},ensTlds:[`.bera`],rpcUrls:{default:{http:[`https://bartio.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berachain bArtio Beratrail`,url:`https://bartio.beratrail.io`}},testnet:!0}),xb=L({id:11501,name:`BEVM Mainnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet-1.bevm.io`]}},blockExplorers:{default:{name:`Bevmscan`,url:`https://scan-mainnet.bevm.io`,apiUrl:`https://scan-mainnet-api.bevm.io/api`}}}),Sb=L({id:3068,name:`Bifrost Mainnet`,nativeCurrency:{name:`BFC`,symbol:`BFC`,decimals:18},rpcUrls:{default:{http:[`https://public-01.mainnet.bifrostnetwork.com/rpc`]}},blockExplorers:{default:{name:`Bifrost Blockscout`,url:`https://explorer.mainnet.bifrostnetwork.com`}},testnet:!1}),Cb=L({id:53456,name:`BirdLayer`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.birdlayer.xyz`,`https://rpc1.birdlayer.xyz`],webSocket:[`wss://rpc.birdlayer.xyz/ws`]}},blockExplorers:{default:{name:`BirdLayer Explorer`,url:`https://scan.birdlayer.xyz`}}}),wb=L({id:32520,name:`Bitgert Mainnet`,nativeCurrency:{decimals:18,name:`Brise`,symbol:`Brise`},rpcUrls:{default:{http:[`https://rpc-bitgert.icecreamswap.com`]}},blockExplorers:{default:{name:`Bitgert Scan`,url:`https://brisescan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2118034}},testnet:!1}),Tb=L({id:96,name:`KUB Mainnet`,nativeCurrency:{name:`KUB Coin`,symbol:`KUB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitkubchain.io`]}},blockExplorers:{default:{name:`KUB Chain Mainnet Explorer`,url:`https://www.bkcscan.com`,apiUrl:`https://www.bkcscan.com/api`}}}),Eb=L({id:25925,name:`Bitkub Testnet`,network:`Bitkub Testnet`,nativeCurrency:{name:`Bitkub Test`,symbol:`tKUB`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.bitkubchain.io`]}},blockExplorers:{default:{name:`Bitkub Chain Testnet Explorer`,url:`https://testnet.bkcscan.com`,apiUrl:`https://testnet.bkcscan.com/api`}},testnet:!0}),Db=L({id:200901,name:`Bitlayer Mainnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitlayer.org`],webSocket:[`wss://ws.bitlayer.org`]}},blockExplorers:{default:{name:`bitlayer mainnet scan`,url:`https://www.btrscan.com`}},contracts:{multicall3:{address:`0x5B256fE9e993902eCe49D138a5b1162cBb529474`,blockCreated:2421963}}}),Ob=L({id:200810,name:`Bitlayer Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bitlayer.org`],webSocket:[`wss://testnet-ws.bitlayer.org`]}},blockExplorers:{default:{name:`bitlayer testnet scan`,url:`https://testnet.btrscan.com`}},contracts:{multicall3:{address:`0x5B256fE9e993902eCe49D138a5b1162cBb529474`,blockCreated:4135671}},testnet:!0}),kb=L({id:7171,name:`Bitrock Mainnet`,nativeCurrency:{name:`BROCK`,symbol:`BROCK`,decimals:18},rpcUrls:{default:{http:[`https://brockrpc.io`]}},blockExplorers:{default:{name:`Bitrock Explorer`,url:`https://explorer.bit-rock.io`}},testnet:!1}),Ab=L({id:199,name:`BitTorrent`,network:`bittorrent-chain-mainnet`,nativeCurrency:{name:`BitTorrent`,symbol:`BTT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bittorrentchain.io`]}},blockExplorers:{default:{name:`Bttcscan`,url:`https://bttcscan.com`,apiUrl:`https://api.bttcscan.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:31078552}}}),jb=L({id:1028,name:`BitTorrent Chain Testnet`,network:`bittorrent-chain-testnet`,nativeCurrency:{name:`BitTorrent`,symbol:`BTT`,decimals:18},rpcUrls:{default:{http:[`https://testrpc.bittorrentchain.io`]}},blockExplorers:{default:{name:`Bttcscan`,url:`https://testnet.bttcscan.com`,apiUrl:`https://testnet.bttcscan.com/api`}},testnet:!0});var Mb=1;const Nb=L({...R,id:81457,name:`Blast`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.blast.io`]}},blockExplorers:{default:{name:`Blastscan`,url:`https://blastscan.io`,apiUrl:`https://api.blastscan.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:212929},l2OutputOracle:{[Mb]:{address:`0x826D1B0D4111Ad9146Eb8941D7Ca2B6a44215c76`,blockCreated:19300358}},portal:{[Mb]:{address:`0x0Ec68c5B10F21EFFb74f2A5C61DFe6b08C0Db6Cb`,blockCreated:19300357}},l1StandardBridge:{[Mb]:{address:`0x697402166Fbf2F22E970df8a6486Ef171dbfc524`,blockCreated:19300360}}},sourceId:Mb}),Pb=L({id:168587773,name:`Blast Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.blast.io`]}},blockExplorers:{default:{name:`Blastscan`,url:`https://sepolia.blastscan.io`,apiUrl:`https://api-sepolia.blastscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:756690}},testnet:!0,sourceId:11155111});var Fb=1;const Ib=L({...R,id:60808,name:`BOB`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.gobob.xyz`],webSocket:[`wss://rpc.gobob.xyz`]}},blockExplorers:{default:{name:`BOB Explorer`,url:`https://explorer.gobob.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:23131},l2OutputOracle:{[Fb]:{address:`0xdDa53E23f8a32640b04D7256e651C1db98dB11C1`,blockCreated:4462615}},portal:{[Fb]:{address:`0x8AdeE124447435fE03e3CD24dF3f4cAE32E65a3E`,blockCreated:4462615}}},sourceId:Fb}),Lb=L({id:288,name:`Boba Network`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.boba.network`]}},blockExplorers:{default:{name:`BOBAScan`,url:`https://bobascan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:446859}}}),Rb=L({id:28882,name:`Boba Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.boba.network`]}},blockExplorers:{default:{name:`BOBAScan`,url:`https://testnet.bobascan.com`}},testnet:!0});var zb=11155111;const Bb=L({...R,id:808813,name:`BOB Sepolia`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://bob-sepolia.rpc.gobob.xyz`],webSocket:[`wss://bob-sepolia.rpc.gobob.xyz`]}},blockExplorers:{default:{name:`BOB Sepolia Explorer`,url:`https://bob-sepolia.explorer.gobob.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:35677},l2OutputOracle:{[zb]:{address:`0x14D0069452b4AE2b250B395b8adAb771E4267d2f`,blockCreated:4462615}},portal:{[zb]:{address:`0x867B1Aa872b9C8cB5E9F7755feDC45BB24Ad0ae4`,blockCreated:4462615}}},testnet:!0,sourceId:zb}),Vb=L({id:11100,name:`Bool Beta Mainnet`,nativeCurrency:{decimals:18,name:`BOL`,symbol:`BOL`},rpcUrls:{default:{http:[`https://beta-rpc-node-http.bool.network`]}},blockExplorers:{default:{name:`BoolScan`,url:`https://beta-mainnet.boolscan.com/`}},testnet:!1}),Hb=L({id:3637,name:`Botanix`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.botanixlabs.com`],webSocket:[`wss://rpc.botanixlabs.com/ws`]}},blockExplorers:{default:{name:`Botanixscan`,url:`https://botanixscan.io`}}}),Ub=L({id:3636,name:`Botanix Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://node.botanixlabs.dev`]}},blockExplorers:{default:{name:`Botanix Testnet Explorer`,url:`https://testnet.botanixscan.io`}},testnet:!0}),Wb=L({id:6001,name:`BounceBit Mainnet`,nativeCurrency:{name:`BounceBit`,symbol:`BB`,decimals:18},rpcUrls:{default:{http:[`https://fullnode-mainnet.bouncebitapi.com`]}},blockExplorers:{default:{name:`BB Scan`,url:`https://bbscan.io`}},testnet:!1}),Gb=L({id:6e3,name:`BounceBit Testnet`,nativeCurrency:{name:`BounceBit`,symbol:`BB`,decimals:18},rpcUrls:{default:{http:[`https://fullnode-testnet.bouncebitapi.com`]}},blockExplorers:{default:{name:`BB Scan`,url:`https://testnet.bbscan.io`}},testnet:!0}),Kb=L({id:1039,name:`Bronos`,nativeCurrency:{decimals:18,name:`BRO`,symbol:`BRO`},rpcUrls:{default:{http:[`https://evm.bronos.org`]}},blockExplorers:{default:{name:`BronoScan`,url:`https://broscan.bronos.org`}}}),qb=L({id:1038,name:`Bronos Testnet`,nativeCurrency:{decimals:18,name:`Bronos Coin`,symbol:`tBRO`},rpcUrls:{default:{http:[`https://evm-testnet.bronos.org`]}},blockExplorers:{default:{name:`BronoScan`,url:`https://tbroscan.bronos.org`}},testnet:!0}),Jb=L({id:56,name:`BNB Smart Chain`,blockTime:750,nativeCurrency:{decimals:18,name:`BNB`,symbol:`BNB`},rpcUrls:{default:{http:[`https://56.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`BscScan`,url:`https://bscscan.com`,apiUrl:`https://api.bscscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15921452}}}),Yb=L({id:1017,name:`BNB Greenfield Chain`,nativeCurrency:{decimals:18,name:`BNB`,symbol:`BNB`},rpcUrls:{default:{http:[`https://greenfield-chain.bnbchain.org`]}},blockExplorers:{default:{name:`BNB Greenfield Mainnet Scan`,url:`https://greenfieldscan.com`}},testnet:!1}),Xb=L({id:97,name:`BNB Smart Chain Testnet`,nativeCurrency:{decimals:18,name:`BNB`,symbol:`tBNB`},rpcUrls:{default:{http:[`https://data-seed-prebsc-1-s1.bnbchain.org:8545`]}},blockExplorers:{default:{name:`BscScan`,url:`https://testnet.bscscan.com`,apiUrl:`https://api-testnet.bscscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:17422483}},testnet:!0}),Zb=L({id:223,name:`B2`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bsquared.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.bsquared.network`}}}),Qb=L({id:1123,name:`B2 Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bsquared.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet-explorer.bsquared.network`}},testnet:!0}),$b=L({id:200901,name:`Bitlayer`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitlayer.org`,`https://rpc.bitlayer-rpc.com`],webSocket:[`wss://ws.bitlayer.org`,`wss://ws.bitlayer-rpc.com`]}},blockExplorers:{default:{name:`Bitlayer(BTR) Scan`,url:`https://www.btrscan.com`}}}),ex=L({id:200810,name:`Bitlayer Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bitlayer.org`],webSocket:[`wss://testnet-ws.bitlayer.org`,`wss://testnet-ws.bitlayer-rpc.com`]}},blockExplorers:{default:{name:`Bitlayer(BTR) Scan`,url:`https://testnet.btrscan.com`}},testnet:!0}),tx=L({id:4999,name:`BlackFort Exchange Network`,nativeCurrency:{name:`BlackFort Token`,symbol:`BXN`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.blackfort.network/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.blackfort.network`,apiUrl:`https://explorer.blackfort.network/api`}}}),nx=L({id:4777,name:`BlackFort Exchange Network Testnet`,nativeCurrency:{name:`BlackFort Testnet Token`,symbol:`TBXN`,decimals:18},rpcUrls:{default:{http:[`https://testnet.blackfort.network/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.blackfort.network`,apiUrl:`https://testnet-explorer.blackfort.network/api`}},testnet:!0}),rx=L({id:13370,name:`Cannon`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),ix=L({id:7700,name:`Canto`,nativeCurrency:{decimals:18,name:`Canto`,symbol:`CANTO`},rpcUrls:{default:{http:[`https://canto.gravitychain.io`]}},blockExplorers:{default:{name:`Tuber.Build (Blockscout)`,url:`https://tuber.build`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2905789}}}),ax={estimateFeesPerGas:async e=>{if(!e.request?.feeCurrency)return null;let[t,n]=await Promise.all([ox(e.client,e.request.feeCurrency),sx(e.client,e.request.feeCurrency)]);return{maxFeePerGas:e.multiply(t-n)+n,maxPriorityFeePerGas:n}}};async function ox(e,t){let n=await e.request({method:`eth_gasPrice`,params:[t]});return BigInt(n)}async function sx(e,t){let n=await e.request({method:`eth_maxPriorityFeePerGas`,params:[t]});return BigInt(n)}Sn();function cx(e){return e===0||e===0n||e==null||e===`0`||e===``||typeof e==`string`&&(xn(e).toLowerCase()===`0x`||xn(e).toLowerCase()===`0x00`)}function lx(e){return!cx(e)}function ux(e){return e.maxFeePerGas!==void 0&&e.maxPriorityFeePerGas!==void 0}function dx(e){return e.type===`cip64`?!0:ux(e)&&lx(e.feeCurrency)}Dn(),Wu();const fx={block:ld({format(e){return{transactions:e.transactions?.map(e=>typeof e==`string`?e:{...ad(e),...e.gatewayFee?{gatewayFee:wn(e.gatewayFee),gatewayFeeRecipient:e.gatewayFeeRecipient}:{},feeCurrency:e.feeCurrency})}}}),transaction:od({format(e){if(e.type===`0x7e`)return{isSystemTx:e.isSystemTx,mint:e.mint?wn(e.mint):void 0,sourceHash:e.sourceHash,type:`deposit`};let t={feeCurrency:e.feeCurrency};return e.type===`0x7b`?t.type=`cip64`:(e.type===`0x7c`&&(t.type=`cip42`),t.gatewayFee=e.gatewayFee?wn(e.gatewayFee):null,t.gatewayFeeRecipient=e.gatewayFeeRecipient),t}}),transactionRequest:Uu({format(e){let t={};return e.feeCurrency&&(t.feeCurrency=e.feeCurrency),dx(e)&&(t.type=`0x7b`),t}})};Zu(),pi(),O(),Op(),Mu(),Ci(),Di(),A();function px(e,t){return dx(e)?hx(e,t):by(e,t)}const mx={transaction:px};function hx(e,t){_x(e);let{chainId:n,gas:r,nonce:i,to:a,value:o,maxFeePerGas:s,maxPriorityFeePerGas:c,accessList:l,feeCurrency:u,data:d}=e,f=[On(n),i?On(i):`0x`,c?On(c):`0x`,s?On(s):`0x`,r?On(r):`0x`,a??`0x`,o?On(o):`0x`,d??`0x`,oh(l),u,...ph(e,t)];return Ei([`0x7b`,M(f)])}var gx=Xu;function _x(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a,feeCurrency:o}=e;if(t<=0)throw new Dp({chainId:t});if(a&&!bi(a))throw new fi({address:a});if(r)throw new D("`gasPrice` is not a valid CIP-64 Transaction attribute.");if(lx(i)&&i>gx)throw new xu({maxFeePerGas:i});if(lx(n)&&lx(i)&&n>i)throw new Au({maxFeePerGas:i,maxPriorityFeePerGas:n});if(lx(o)&&!bi(o))throw new D("`feeCurrency` MUST be a token address for CIP-64 transactions.");if(cx(o))throw new D("`feeCurrency` must be provided for CIP-64 transactions.")}const vx={blockTime:1e3,contracts:vy,formatters:fx,serializers:mx,fees:ax},yx=L({...vx,id:42220,name:`Celo`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`CELO`},rpcUrls:{default:{http:[`https://forno.celo.org`]}},blockExplorers:{default:{name:`Celo Explorer`,url:`https://celoscan.io`,apiUrl:`https://api.celoscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:13112599}},testnet:!1});var bx=17e3;const xx=L({...vx,id:44787,name:`Alfajores`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`A-CELO`},rpcUrls:{default:{http:[`https://alfajores-forno.celo-testnet.org`]}},blockExplorers:{default:{name:`Celo Alfajores Explorer`,url:`https://celo-alfajores.blockscout.com`,apiUrl:`https://celo-alfajores.blockscout.com/api`}},contracts:{...vx.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:14569001},portal:{[bx]:{address:`0x82527353927d8D069b3B452904c942dA149BA381`,blockCreated:2411324}},disputeGameFactory:{[bx]:{address:`0xE28AAdcd9883746c0e5068F58f9ea06027b214cb`,blockCreated:2411324}},l2OutputOracle:{[bx]:{address:`0x4a2635e9e4f6e45817b1D402ac4904c1d1752438`,blockCreated:2411324}},l1StandardBridge:{[bx]:{address:`0xD1B0E0581973c9eB7f886967A606b9441A897037`,blockCreated:2411324}}},testnet:!0});var Sx=11155111;const Cx=L({...vx,id:11142220,name:`Celo Sepolia Testnet`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`S-CELO`},rpcUrls:{default:{http:[`https://forno.celo-sepolia.celo-testnet.org`]}},blockExplorers:{default:{name:`Celo Sepolia Explorer`,url:`https://celo-sepolia.blockscout.com/`,apiUrl:`https://celo-sepolia.blockscout.com/api`}},contracts:{...vx.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1},portal:{[Sx]:{address:`0x44ae3d41a335a7d05eb533029917aad35662dcc2`,blockCreated:8825790}},disputeGameFactory:{[Sx]:{address:`0x57c45d82d1a995f1e135b8d7edc0a6bb5211cfaa`,blockCreated:8825790}},l1StandardBridge:{[Sx]:{address:`0xec18a3c30131a0db4246e785355fbc16e2eaf408`,blockCreated:8825790}}},testnet:!0}),wx=L({id:5858,name:`Chang Chain Foundation Mainnet`,nativeCurrency:{decimals:18,name:`CTH`,symbol:`CTH`},rpcUrls:{default:{http:[`https://rpc.cthscan.com`]}},blockExplorers:{default:{name:`Chang Chain explorer`,url:`https://cthscan.com`}}}),Tx=L({id:88888,name:`Chiliz Chain`,network:`chiliz-chain`,nativeCurrency:{decimals:18,name:`CHZ`,symbol:`CHZ`},rpcUrls:{default:{http:[`https://rpc.chiliz.com`]}},blockExplorers:{default:{name:`Chiliz Explorer`,url:`https://scan.chiliz.com`,apiUrl:`https://scan.chiliz.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:8080847}}}),Ex=L({id:2882,name:`Chips Network`,network:`CHIPS`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://node.chips.ooo/wasp/api/v1/chains/iota1pp3d3mnap3ufmgqnjsnw344sqmf5svjh26y2khnmc89sv6788y3r207a8fn/evm`]}}}),Dx=L({id:5115,name:`Citrea Testnet`,nativeCurrency:{name:`cBTC`,symbol:`cBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.citrea.xyz`]}},blockExplorers:{default:{name:`Citrea Explorer`,url:`https://explorer.testnet.citrea.xyz`,apiUrl:`https://explorer.testnet.citrea.xyz/api`}},testnet:!0}),Ox=L({id:61,name:`Ethereum Classic`,nativeCurrency:{decimals:18,name:`ETC`,symbol:`ETC`},rpcUrls:{default:{http:[`https://etc.rivet.link`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.com/etc/mainnet`}}}),kx=L({id:112,name:`Coinbit Mainnet`,nativeCurrency:{name:`GIDR`,symbol:`GIDR`,decimals:18},rpcUrls:{default:{http:[`https://coinbit-rpc-mainnet.chain.sbcrypto.app`]}},blockExplorers:{default:{name:`Coinbit Explorer`,url:`https://coinbit-explorer.chain.sbcrypto.app`}},testnet:!1}),Ax=L({id:52,name:`CoinEx Mainnet`,nativeCurrency:{name:`cet`,symbol:`cet`,decimals:18},rpcUrls:{default:{http:[`https://rpc.coinex.net`]}},blockExplorers:{default:{name:`CoinEx Explorer`,url:`https://www.coinex.net`}},testnet:!1}),jx=L({id:1030,name:`Conflux eSpace`,nativeCurrency:{name:`Conflux`,symbol:`CFX`,decimals:18},rpcUrls:{default:{http:[`https://evm.confluxrpc.com`],webSocket:[`wss://evm.confluxrpc.com/ws`]}},blockExplorers:{default:{name:`ConfluxScan`,url:`https://evm.confluxscan.org`}},contracts:{multicall3:{address:`0xEFf0078910f638cd81996cc117bccD3eDf2B072F`,blockCreated:68602935}}}),Mx=L({id:71,name:`Conflux eSpace Testnet`,network:`cfx-espace-testnet`,testnet:!0,nativeCurrency:{name:`Conflux`,symbol:`CFX`,decimals:18},rpcUrls:{default:{http:[`https://evmtestnet.confluxrpc.com`],webSocket:[`wss://evmtestnet.confluxrpc.com/ws`]}},blockExplorers:{default:{name:`ConfluxScan`,url:`https://evmtestnet.confluxscan.org`}},contracts:{multicall3:{address:`0xEFf0078910f638cd81996cc117bccD3eDf2B072F`,blockCreated:117499050}}}),Nx=L({id:1116,name:`Core Dao`,nativeCurrency:{decimals:18,name:`Core`,symbol:`CORE`},rpcUrls:{default:{http:[`https://rpc.coredao.org`]}},blockExplorers:{default:{name:`CoreDao`,url:`https://scan.coredao.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:11907934}},testnet:!1}),Px=L({id:1115,name:`Core Testnet`,nativeCurrency:{decimals:18,name:`tCore`,symbol:`TCORE`},rpcUrls:{default:{http:[`https://rpc.test.btcs.network`]}},blockExplorers:{default:{name:`Core Testnet`,url:`https://scan.test.btcs.network`,apiUrl:`https://api.test.btcs.network/api`}},contracts:{multicall3:{address:`0xCcddF20A1932537123C2E48Bd8e00b108B8f7569`,blockCreated:29350509}},testnet:!0}),Fx=L({id:1114,name:`Core Testnet2`,nativeCurrency:{decimals:18,name:`tCore2`,symbol:`TCORE2`},rpcUrls:{default:{http:[`https://rpc.test2.btcs.network`]}},blockExplorers:{default:{name:`Core Testnet2`,url:`https://scan.test2.btcs.network`,apiUrl:`https://api.test2.btcs.network/api`}},contracts:{multicall3:{address:`0x3CB285ff3Cd5C7C7e570b1E7DE3De17A0f985e56`,blockCreated:3838600}},testnet:!0}),Ix=L({id:21e6,name:`Corn`,nativeCurrency:{decimals:18,name:`Bitcorn`,symbol:`BTCN`},rpcUrls:{default:{http:[`https://21000000.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Corn Explorer`,url:`https://cornscan.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/21000000/etherscan/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3228}},sourceId:1}),Lx=L({id:21000001,name:`Corn Testnet`,nativeCurrency:{decimals:18,name:`Bitcorn`,symbol:`BTCN`},rpcUrls:{default:{http:[`https://21000001.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Corn Testnet Explorer`,url:`https://testnet.cornscan.io`,apiUrl:`https://api.routescan.io/v2/network/testnet/evm/21000001/etherscan/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4886}},testnet:!0,sourceId:11155111}),Rx=L({id:44,name:`Crab Network`,nativeCurrency:{decimals:18,name:`Crab Network Native Token`,symbol:`CRAB`},rpcUrls:{default:{http:[`https://crab-rpc.darwinia.network`],webSocket:[`wss://crab-rpc.darwinia.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://crab-scan.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3032593}}}),zx=L({id:66665,name:`Creator`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.creatorchain.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.creatorchain.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0}),Bx=L({id:102032,name:`Creditcoin Devnet`,nativeCurrency:{name:`Devnet CTC`,symbol:`devCTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cc3-devnet.creditcoin.network`],webSocket:[`wss://rpc.cc3-devnet.creditcoin.network/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin-devnet.blockscout.com`,apiUrl:`https://creditcoin3-dev.subscan.io`}},testnet:!0}),Vx=L({id:102030,name:`Creditcoin`,nativeCurrency:{name:`Creditcoin`,symbol:`CTC`,decimals:18},rpcUrls:{default:{http:[`https://mainnet3.creditcoin.network`],webSocket:[`wss://mainnet3.creditcoin.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin.blockscout.com`,apiUrl:`https://creditcoin.blockscout.com/api`}},testnet:!1}),Hx=L({id:102031,name:`Creditcoin Testnet`,nativeCurrency:{name:`Creditcoin Testnet`,symbol:`tCTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cc3-testnet.creditcoin.network`],webSocket:[`wss://rpc.cc3-testnet.creditcoin.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin-testnet.blockscout.com`,apiUrl:`https://creditcoin-testnet.blockscout.com/api`}},testnet:!0}),Ux=L({id:25,name:`Cronos Mainnet`,nativeCurrency:{decimals:18,name:`Cronos`,symbol:`CRO`},rpcUrls:{default:{http:[`https://evm.cronos.org`]}},blockExplorers:{default:{name:`Cronos Explorer`,url:`https://explorer.cronos.org`,apiUrl:`https://explorer-api.cronos.org/mainnet/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1963112}}}),Wx=L({id:338,name:`Cronos Testnet`,nativeCurrency:{decimals:18,name:`CRO`,symbol:`tCRO`},rpcUrls:{default:{http:[`https://evm-t3.cronos.org`]}},blockExplorers:{default:{name:`Cronos Explorer (Testnet)`,url:`https://explorer.cronos.org/testnet`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:10191251}},testnet:!0}),Gx=L({id:388,name:`Cronos zkEVM Mainnet`,nativeCurrency:{decimals:18,name:`Cronos zkEVM CRO`,symbol:`zkCRO`},rpcUrls:{default:{http:[`https://mainnet.zkevm.cronos.org`]}},blockExplorers:{default:{name:`Cronos zkEVM (Mainnet) Chain Explorer`,url:`https://explorer.zkevm.cronos.org`}},contracts:{multicall3:{address:`0x06f4487d7c4a5983d2660db965cc6d2565e4cfaa`,blockCreated:72}}}),Kx=L({id:282,name:`Cronos zkEVM Testnet`,nativeCurrency:{decimals:18,name:`Cronos zkEVM Test Coin`,symbol:`zkTCRO`},rpcUrls:{default:{http:[`https://testnet.zkevm.cronos.org`]}},blockExplorers:{default:{name:`Cronos zkEVM Testnet Explorer`,url:`https://explorer.zkevm.cronos.org/testnet`}},testnet:!0}),qx=L({id:3737,name:`Crossbell`,nativeCurrency:{decimals:18,name:`CSB`,symbol:`CSB`},rpcUrls:{default:{http:[`https://rpc.crossbell.io`]}},blockExplorers:{default:{name:`CrossScan`,url:`https://scan.crossbell.io`,apiUrl:`https://scan.crossbell.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:38246031}}}),Jx=L({id:4158,name:`CrossFi Mainnet`,nativeCurrency:{decimals:18,name:`CrossFi`,symbol:`XFI`},rpcUrls:{default:{http:[`https://rpc.mainnet.ms`]}},blockExplorers:{default:{name:`CrossFi Blockchain Explorer`,url:`https://xfiscan.com`}},testnet:!1}),Yx=L({id:33111,name:`Curtis`,nativeCurrency:{name:`ApeCoin`,symbol:`APE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.curtis.apechain.com`]}},blockExplorers:{default:{name:`Curtis Explorer`,url:`https://explorer.curtis.apechain.com`}},testnet:!0}),Xx=L({id:7560,name:`Cyber`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://cyber.alt.technology`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://cyberscan.co`,apiUrl:`https://cyberscan.co/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),Zx=L({id:111557560,name:`Cyber Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://cyber-testnet.alt.technology`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet.cyberscan.co`,apiUrl:`https://testnet.cyberscan.co/api`}},contracts:{multicall3:{address:`0xffc391F0018269d4758AEA1a144772E8FB99545E`,blockCreated:304545}},testnet:!0}),Qx=L({id:824,name:`Daily Network Mainnet`,nativeCurrency:{decimals:18,name:`Daily`,symbol:`DLY`},rpcUrls:{default:{http:[`https://rpc.mainnet.dailycrypto.net`]}},blockExplorers:{default:{name:`Daily Mainnet Explorer`,url:`https://explorer.mainnet.dailycrypto.net`}},testnet:!1}),$x=L({id:825,name:`Daily Network Testnet`,nativeCurrency:{decimals:18,name:`Daily`,symbol:`DLY`},rpcUrls:{default:{http:[`https://rpc.testnet.dailycrypto.net`]}},blockExplorers:{default:{name:`Daily Testnet Explorer`,url:`https://explorer.testnet.dailycrypto.net`}},testnet:!0}),eS=L({id:46,name:`Darwinia Network`,nativeCurrency:{decimals:18,name:`RING`,symbol:`RING`},rpcUrls:{default:{http:[`https://rpc.darwinia.network`],webSocket:[`wss://rpc.darwinia.network`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:69420}}}),tS=L({id:20240603,name:`DBK chain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.dbkchain.io`]}},blockExplorers:{default:{name:`DBK Chain Explorer`,url:`https://scan.dbkchain.io`}},testnet:!1}),nS=L({...R,id:0x9a697f88076c8,name:`Dchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://dchain-2716446429837000-1.jsonrpc.sagarpc.io`]}},blockExplorers:{default:{name:`Dchain Explorer`,url:`https://dchain-2716446429837000-1.sagaexplorer.io`,apiUrl:`https://api-dchain-2716446429837000-1.sagaexplorer.io/api`}},contracts:{...R.contracts}}),rS=L({...R,id:0x9a379ba03cf10,name:`Dchain Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://dchaintestnet-2713017997578000-1.jsonrpc.testnet.sagarpc.io`]}},blockExplorers:{default:{name:`Dchain Explorer`,url:`https://dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io`,apiUrl:`https://api-dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io/api`}},contracts:{...R.contracts}}),iS=L({id:1130,network:`defichain-evm`,name:`DeFiChain EVM Mainnet`,nativeCurrency:{name:`DeFiChain`,symbol:`DFI`,decimals:18},rpcUrls:{default:{http:[`https://eth.mainnet.ocean.jellyfishsdk.com`]}},blockExplorers:{default:{name:`DeFiScan`,url:`https://meta.defiscan.live`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:137852}}}),aS=L({id:1131,network:`defichain-evm-testnet`,name:`DeFiChain EVM Testnet`,nativeCurrency:{name:`DeFiChain`,symbol:`DFI`,decimals:18},rpcUrls:{default:{http:[`https://eth.testnet.ocean.jellyfishsdk.com`]}},blockExplorers:{default:{name:`DeFiScan`,url:`https://meta.defiscan.live/?network=TestNet`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:156462}},testnet:!0}),oS=L({id:666666666,name:`Degen`,nativeCurrency:{decimals:18,name:`Degen`,symbol:`DEGEN`},rpcUrls:{default:{http:[`https://rpc.degen.tips`],webSocket:[`wss://rpc.degen.tips`]}},blockExplorers:{default:{name:`Degen Chain Explorer`,url:`https://explorer.degen.tips`,apiUrl:`https://explorer.degen.tips/api/v2`}}}),sS=L({id:53935,name:`DFK Chain`,nativeCurrency:{decimals:18,name:`Jewel`,symbol:`JEWEL`},rpcUrls:{default:{http:[`https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc`]}},blockExplorers:{default:{name:`DFKSubnetScan`,url:`https://subnets.avax.network/defi-kingdoms`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14790551}}}),cS=L({id:15,name:`Diode Prenet`,nativeCurrency:{decimals:18,name:`DIODE`,symbol:`DIODE`},rpcUrls:{default:{http:[`https://prenet.diode.io:8443`],webSocket:[`wss://prenet.diode.io:8443/ws`]}},blockExplorers:{default:{name:`Diode Explorer`,url:`https://diode.io/prenet`}},testnet:!1}),lS=L({id:513100,name:`DisChain`,nativeCurrency:{decimals:18,name:`DIS`,symbol:`DIS`},rpcUrls:{default:{http:[`https://rpc.dischain.xyz`]}},blockExplorers:{default:{name:`DisChain Explorer`,url:`https://www.oklink.com/dis`}}}),uS=L({id:53457,name:`DODOchain Testnet`,nativeCurrency:{decimals:18,name:`DODO`,symbol:`DODO`},rpcUrls:{default:{http:[`https://dodochain-testnet.alt.technology`],webSocket:[`wss://dodochain-testnet.alt.technology/ws`]}},blockExplorers:{default:{name:`DODOchain Testnet (Sepolia) Explorer`,url:`https://testnet-scan.dodochain.com`}},testnet:!0}),dS=L({id:2e3,name:`Dogechain`,nativeCurrency:{decimals:18,name:`Wrapped Dogecoin`,symbol:`WDOGE`},rpcUrls:{default:{http:[`https://rpc.dogechain.dog`]}},blockExplorers:{default:{name:`DogeChainExplorer`,url:`https://explorer.dogechain.dog`,apiUrl:`https://explorer.dogechain.dog/api`}},contracts:{multicall3:{address:`0x68a8609a60a008EFA633dfdec592c03B030cC508`,blockCreated:25384031}}}),fS=L({id:97476,name:`Doma Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc-testnet.doma.xyz`]}},blockExplorers:{default:{name:`Doma Testnet Explorer`,url:`https://explorer-testnet.doma.xyz`}},testnet:!0}),pS=L({id:42026,name:`Donatuz`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.donatuz.com`]}},blockExplorers:{default:{name:`Donatuz Explorer`,url:`https://explorer.donatuz.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),mS=L({id:7979,name:`DOS Chain`,nativeCurrency:{decimals:18,name:`DOS Chain`,symbol:`DOS`},rpcUrls:{default:{http:[`https://main.doschain.com`]}},blockExplorers:{default:{name:`DOS Chain Explorer`,url:`https://doscan.io`,apiUrl:`https://api.doscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:161908}}}),hS=L({id:3939,name:`DOS Chain Testnet`,nativeCurrency:{decimals:18,name:`DOS Chain Testnet`,symbol:`DOS`},rpcUrls:{default:{http:[`https://test.doschain.com`]}},blockExplorers:{default:{name:`DOS Chain Testnet Explorer`,url:`https://test.doscan.io`,apiUrl:`https://api-test.doscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:69623}},testnet:!0}),gS=L({id:23451,name:`DreyerX Mainnet`,nativeCurrency:{name:`DreyerX`,symbol:`DRX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.dreyerx.com`]}},blockExplorers:{default:{name:`DreyerX Scan`,url:`https://scan.dreyerx.com`}}}),_S=L({id:23452,name:`DreyerX Testnet`,nativeCurrency:{name:`DreyerX`,symbol:`DRX`,decimals:18},rpcUrls:{default:{http:[`http://testnet-rpc.dreyerx.com`]}},blockExplorers:{default:{name:`DreyerX Testnet Scan`,url:`https://testnet-scan.dreyerx.com`}},testnet:!0}),vS=L({id:555888,name:`DustBoy IoT`,nativeCurrency:{name:`Ether`,symbol:`DST`,decimals:18},rpcUrls:{default:{http:[`https://dustboy-rpc.jibl2.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://dustboy.jibl2.com`,apiUrl:`https://dustboy.jibl2.com/api`}},contracts:{multicall3:{address:`0xFFD34aa2C62B2D52E00A361e466C229788f4eD6a`,blockCreated:526569}},testnet:!1}),yS=L({id:1100,name:`Dymension`,nativeCurrency:{name:`DYM`,symbol:`DYM`,decimals:18},rpcUrls:{default:{http:[`https://dymension-evm-rpc.publicnode.com`],webSocket:[`wss://dymension-evm-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Dym FYI`,url:`https://dym.fyi`}},testnet:!1}),bS=L({id:5424,name:`edeXa`,nativeCurrency:{name:`edeXa`,symbol:`EDX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.edexa.network`]}},blockExplorers:{default:{name:`edeXa Explorer`,url:`https://explorer.edexa.network`,apiUrl:`https://explorer.edexa.network/api/v2`}}}),xS=L({id:1995,name:`edeXa Testnet`,nativeCurrency:{name:`edeXa`,symbol:`tEDX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.edexa.network`]}},blockExplorers:{default:{name:`edeXa Testnet Explorer`,url:`https://explorer.testnet.edexa.network`,apiUrl:`https://explorer.testnet.edexa.network/api/v2`}},testnet:!0}),SS=L({id:2026,name:`Edgeless Network`,nativeCurrency:{name:`Edgeless Wrapped ETH`,symbol:`EwETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.edgeless.network/http`],webSocket:[`wss://rpc.edgeless.network/ws`]}},blockExplorers:{default:{name:`Edgeless Explorer`,url:`https://explorer.edgeless.network`}}}),CS=L({id:202,name:`Edgeless Testnet`,nativeCurrency:{name:`Edgeless Wrapped ETH`,symbol:`EwETH`,decimals:18},rpcUrls:{default:{http:[`https://edgeless-testnet.rpc.caldera.xyz/http`],webSocket:[`wss://edgeless-testnet.rpc.caldera.xyz/ws`]}},blockExplorers:{default:{name:`Edgeless Testnet Explorer`,url:`https://testnet.explorer.edgeless.network`}}}),wS=L({id:2021,name:`Edgeware EdgeEVM Mainnet`,nativeCurrency:{decimals:18,name:`Edgeware`,symbol:`EDG`},rpcUrls:{default:{http:[`https://edgeware-evm.jelliedowl.net`]}},blockExplorers:{default:{name:`Edgscan by Bharathcoorg`,url:`https://edgscan.live`,apiUrl:`https://edgscan.live/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:18117872}}}),TS=L({id:2022,name:`Beresheet BereEVM Testnet`,nativeCurrency:{decimals:18,name:`Testnet EDG`,symbol:`tEDG`},rpcUrls:{default:{http:[`https://beresheet-evm.jelliedowl.net`]}},blockExplorers:{default:{name:`Edgscan by Bharathcoorg`,url:`https://testnet.edgscan.live`,apiUrl:`https://testnet.edgscan.live/api`}}}),ES=L({id:41923,name:`EDU Chain`,nativeCurrency:{decimals:18,name:`EDU`,symbol:`EDU`},rpcUrls:{default:{http:[`https://rpc.edu-chain.raas.gelato.cloud`]}},blockExplorers:{default:{name:`EDU Chain Explorer`,url:`https://educhain.blockscout.com/`}},testnet:!1}),DS=L({id:656476,name:`EDU Chain Testnet`,nativeCurrency:{decimals:18,name:`EDU`,symbol:`EDU`},rpcUrls:{default:{http:[`https://rpc.open-campus-codex.gelato.digital/`],webSocket:[`wss://ws.open-campus-codex.gelato.digital`]}},blockExplorers:{default:{name:`EDU Chain Testnet Explorer`,url:`https://opencampus-codex.blockscout.com`,apiUrl:`https://opencampus-codex.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:15514133}},testnet:!0}),OS=L({id:1994,name:`Ekta`,nativeCurrency:{decimals:18,name:`EKTA`,symbol:`EKTA`},rpcUrls:{default:{http:[`https://main.ekta.io`]}},blockExplorers:{default:{name:`Ektascan`,url:`https://ektascan.io`,apiUrl:`https://ektascan.io/api`}}}),kS=L({id:1004,name:`Ekta Testnet`,nativeCurrency:{decimals:18,name:`EKTA`,symbol:`EKTA`},rpcUrls:{default:{http:[`https://test.ekta.io:8545`]}},blockExplorers:{default:{name:`Test Ektascan`,url:`https://test.ektascan.io`,apiUrl:`https://test.ektascan.io/api`}},testnet:!0}),AS=L({id:20,name:`Elastos Smart Chain`,nativeCurrency:{name:`ELA`,symbol:`ELA`,decimals:18},rpcUrls:{default:{http:[`https://api2.elastos.io/eth`]}},blockExplorers:{default:{name:`Elastos Explorer`,url:`https://esc.elastos.io`}},testnet:!1}),jS=L({id:21,name:`Elastos Smart Chain Testnet`,nativeCurrency:{name:`tELA`,symbol:`tELA`,decimals:18},rpcUrls:{default:{http:[`https://api-testnet.elastos.io/eth`]}},blockExplorers:{default:{name:`Elastos Explorer`,url:`https://esc-testnet.elastos.io`}},testnet:!0}),MS=L({id:52014,name:`Electroneum Mainnet`,nativeCurrency:{name:`ETN`,symbol:`ETN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.electroneum.com`]}},blockExplorers:{default:{name:`Electroneum Block Explorer`,url:`https://blockexplorer.electroneum.com`}},testnet:!1}),NS=L({id:5201420,name:`Electroneum Testnet`,nativeCurrency:{name:`ETN`,symbol:`ETN`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.electroneum.com`]}},blockExplorers:{default:{name:`Electroneum Block Explorer`,url:`https://blockexplorer.thesecurityteam.rocks`}},testnet:!0}),PS=L({...R,id:1338,name:`Elysium Testnet`,nativeCurrency:{decimals:18,name:`LAVA`,symbol:`LAVA`},rpcUrls:{default:{http:[`https://elysium-test-rpc.vulcanforged.com`]}},blockExplorers:{default:{name:`Elysium testnet explorer`,url:`https://elysium-explorer.vulcanforged.com`}},testnet:!0}),FS=L({id:246,name:`Energy Mainnet`,nativeCurrency:{name:`EWT`,symbol:`EWT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.energyweb.org`]}},blockExplorers:{default:{name:`EnergyWeb Explorer`,url:`https://explorer.energyweb.org`}},testnet:!1}),IS=L({id:173,name:`ENI Mainnet`,nativeCurrency:{decimals:18,name:`ENI`,symbol:`ENI`},rpcUrls:{default:{http:[`https://rpc.eniac.network`]}},blockExplorers:{default:{name:`ENI Explorer`,url:`https://scan.eniac.network`}},testnet:!1}),LS=L({id:6912115,name:`ENI Testnet`,nativeCurrency:{decimals:18,name:`ENI Testnet Token`,symbol:`ENI`},rpcUrls:{default:{http:[`https://rpc-testnet.eniac.network`]}},blockExplorers:{default:{name:`ENI Testnet Explorer`,url:`https://scan-testnet.eniac.network`}},testnet:!0}),RS=L({id:119,name:`ENULS Mainnet`,nativeCurrency:{decimals:18,name:`NULS`,symbol:`NULS`},rpcUrls:{default:{http:[`https://evmapi2.nuls.io`]}},blockExplorers:{default:{name:`ENULS Explorer`,url:`https://evmscan.nuls.io`}},testnet:!1}),zS=L({id:7332,name:`Horizen EON`,nativeCurrency:{decimals:18,name:`ZEN`,symbol:`ZEN`},rpcUrls:{default:{http:[`https://eon-rpc.horizenlabs.io/ethv1`]}},blockExplorers:{default:{name:`EON Explorer`,url:`https://eon-explorer.horizenlabs.io`}},contracts:{}}),BS=L({id:17777,name:`EOS EVM`,nativeCurrency:{decimals:18,name:`EOS`,symbol:`EOS`},rpcUrls:{default:{http:[`https://api.evm.eosnetwork.com`]}},blockExplorers:{default:{name:`EOS EVM Explorer`,url:`https://explorer.evm.eosnetwork.com`,apiUrl:`https://explorer.evm.eosnetwork.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7943933}}}),VS=L({id:15557,name:`EOS EVM Testnet`,nativeCurrency:{decimals:18,name:`EOS`,symbol:`EOS`},rpcUrls:{default:{http:[`https://api.testnet.evm.eosnetwork.com`]}},blockExplorers:{default:{name:`EOS EVM Testnet Explorer`,url:`https://explorer.testnet.evm.eosnetwork.com`,apiUrl:`https://explorer.testnet.evm.eosnetwork.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:9067940}},testnet:!0}),HS=L({id:140,name:`Eteria`,nativeCurrency:{name:`Eteria`,symbol:`ERA`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.eteria.io/v1`]}},blockExplorers:{default:{name:`Eteria Explorer`,url:`https://explorer.eteria.io`,apiUrl:`https://explorer.eteria.io/api`}}}),US=L({id:42793,name:`Etherlink`,blockTime:4830,nativeCurrency:{decimals:18,name:`Tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.mainnet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink`,url:`https://explorer.etherlink.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:33899}}}),WS=L({id:128123,name:`Etherlink Testnet`,nativeCurrency:{decimals:18,name:`Tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.ghostnet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink Testnet`,url:`https://testnet.explorer.etherlink.com`}},testnet:!0}),GS=L({id:183,name:`Ethernity`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.ethernitychain.io`]}},blockExplorers:{default:{name:`Ethernity Explorer`,url:`https://ernscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!1}),KS=L({id:20256789,name:`ETP Mainnet`,nativeCurrency:{decimals:18,name:`ETP Chain Native Token`,symbol:`ETP`},rpcUrls:{default:{http:[`https://rpc.etpscan.xyz`]}},blockExplorers:{default:{name:`ETP Scan`,url:`https://etpscan.xyz`}}}),qS=L({id:9001,name:`Evmos`,nativeCurrency:{decimals:18,name:`Evmos`,symbol:`EVMOS`},rpcUrls:{default:{http:[`https://eth.bd.evmos.org:8545`]}},blockExplorers:{default:{name:`Evmos Block Explorer`,url:`https://escan.live`}}}),JS=L({id:9e3,name:`Evmos Testnet`,nativeCurrency:{decimals:18,name:`Evmos`,symbol:`EVMOS`},rpcUrls:{default:{http:[`https://eth.bd.evmos.dev:8545`]}},blockExplorers:{default:{name:`Evmos Testnet Block Explorer`,url:`https://evm.evmos.dev/`}}}),YS=L({id:22052002,name:`Excelon Mainnet`,network:`XLON`,nativeCurrency:{decimals:18,name:`Excelon`,symbol:`xlon`},rpcUrls:{default:{http:[`https://edgewallet1.xlon.org`]}},blockExplorers:{default:{name:`Excelon explorer`,url:`https://explorer.excelon.io`}}}),XS=L({id:2,name:`Expanse Network`,nativeCurrency:{decimals:18,name:`EXP`,symbol:`EXP`},rpcUrls:{default:{http:[`https://node.expanse.tech`]}},blockExplorers:{default:{name:`Expanse Explorer`,url:`https://explorer.expanse.tech`}},testnet:!1}),ZS=L({id:7200,name:`exSat Network`,nativeCurrency:{decimals:18,name:`BTC`,symbol:`BTC`},rpcUrls:{default:{http:[`https://evm.exsat.network`]}},blockExplorers:{default:{name:`exSat Explorer`,url:`https://scan.exsat.network`,apiUrl:`https://scan.exsat.network/api`}}}),QS=L({id:839999,name:`exSat Testnet`,nativeCurrency:{decimals:18,name:`BTC`,symbol:`BTC`},rpcUrls:{default:{http:[`https://evm-tst3.exsat.network`]}},blockExplorers:{default:{name:`exSat Explorer`,url:`https://scan-testnet.exsat.network`,apiUrl:`https://scan-testnet.exsat.network/api`}}}),$S=L({id:250,name:`Fantom`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://250.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`FTMScan`,url:`https://ftmscan.com`,apiUrl:`https://api.ftmscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:33001987}}}),eC=L({id:64240,name:`Fantom Sonic Open Testnet`,network:`fantom-sonic-testnet`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://rpcapi.sonic.fantom.network`]}},blockExplorers:{default:{name:`Fantom Sonic Open Testnet Explorer`,url:`https://public-sonic.fantom.network`}},testnet:!0}),tC=L({id:4002,name:`Fantom Testnet`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://rpc.testnet.fantom.network`]}},blockExplorers:{default:{name:`FTMScan`,url:`https://testnet.ftmscan.com`,apiUrl:`https://testnet.ftmscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:8328688}},testnet:!0}),nC=L({id:12306,name:`Fibo Chain`,nativeCurrency:{decimals:18,name:`fibo`,symbol:`FIBO`},rpcUrls:{default:{http:[`https://network.hzroc.art`]}},blockExplorers:{default:{name:`FiboScan`,url:`https://scan.fibochain.org`}}}),rC=L({id:314,name:`Filecoin Mainnet`,nativeCurrency:{decimals:18,name:`filecoin`,symbol:`FIL`},rpcUrls:{default:{http:[`https://api.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filfox`,url:`https://filfox.info/en`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3328594}}}),iC=L({id:314159,name:`Filecoin Calibration`,nativeCurrency:{decimals:18,name:`testnet filecoin`,symbol:`tFIL`},rpcUrls:{default:{http:[`https://api.calibration.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filscan`,url:`https://calibration.filscan.io`}},testnet:!0}),aC=L({id:3141,name:`Filecoin Hyperspace`,nativeCurrency:{decimals:18,name:`testnet filecoin`,symbol:`tFIL`},rpcUrls:{default:{http:[`https://api.hyperspace.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filfox`,url:`https://hyperspace.filfox.info/en`}},testnet:!0}),oC=L({id:253368190,name:`Flame`,network:`flame`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.flame.astria.org`],webSocket:[`wss://ws.flame.astria.org`]}},blockExplorers:{default:{name:`Flame Explorer`,url:`https://explorer.flame.astria.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6829148}}}),sC=L({id:14,name:`Flare Mainnet`,nativeCurrency:{decimals:18,name:`Flare`,symbol:`FLR`},rpcUrls:{default:{http:[`https://flare-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Flare Explorer`,url:`https://flare-explorer.flare.network`,apiUrl:`https://flare-explorer.flare.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3002461}}}),cC=L({id:114,name:`Flare Testnet Coston2`,nativeCurrency:{decimals:18,name:`Coston2 Flare`,symbol:`C2FLR`},rpcUrls:{default:{http:[`https://coston2-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Coston2 Explorer`,url:`https://coston2-explorer.flare.network`,apiUrl:`https://coston2-explorer.flare.network/api`}},testnet:!0}),lC=L({id:747,name:`Flow EVM Mainnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://mainnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Mainnet Explorer`,url:`https://evm.flowscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6205}},blockTime:800}),uC=L({id:646,name:`Flow EVM Previewnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://previewnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Previewnet Explorer`,url:`https://previewnet.flowdiver.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6205}}}),dC=L({id:545,name:`Flow EVM Testnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://testnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Flow Diver`,url:`https://evm-testnet.flowscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:137518}},testnet:!0,blockTime:800}),fC=L({id:9999999,name:`Fluence`,nativeCurrency:{name:`FLT`,symbol:`FLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.fluence.dev`],webSocket:[`wss://ws.mainnet.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.mainnet.fluence.dev`,apiUrl:`https://blockscout.mainnet.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:207583}}}),pC=L({id:123420000220,name:`Fluence Stage`,nativeCurrency:{name:`tFLT`,symbol:`tFLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stage.fluence.dev`],webSocket:[`wss://ws.stage.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.stage.fluence.dev`,apiUrl:`https://blockscout.stage.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:83227}},testnet:!0}),mC=L({id:52164803,name:`Fluence Testnet`,nativeCurrency:{name:`tFLT`,symbol:`tFLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.fluence.dev`],webSocket:[`wss://ws.testnet.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.testnet.fluence.dev`,apiUrl:`https://blockscout.testnet.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96424}},testnet:!0}),hC=L({id:20993,name:`Fluent Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.dev.gblend.xyz`]}},blockExplorers:{default:{name:`Fluent Explorer`,url:`https://blockscout.dev.gblend.xyz`}},testnet:!0});var gC=1;const _C=L({id:478,name:`Form Network`,nativeCurrency:{decimals:18,name:`Ethereum`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.form.network/http`],webSocket:[`wss://rpc.form.network/ws`]}},blockExplorers:{default:{name:`Form Explorer`,url:`https://explorer.form.network`}},contracts:{...R.contracts,addressManager:{[gC]:{address:`0x15c249E46A2F924C2dB3A1560CF86729bAD1f07B`}},l1CrossDomainMessenger:{[gC]:{address:`0xF333158DCCad1dF6C3F0a3aEe8BC31fA94d9eD5c`}},l2OutputOracle:{[gC]:{address:`0x4ccAAF69F41c5810cA875183648B577CaCf1F67E`}},portal:{[gC]:{address:`0x4E259Ee5F4136408908160dD32295A5031Fa426F`}},l1StandardBridge:{[gC]:{address:`0xdc20aA63D3DE59574E065957190D8f24e0F7B8Ba`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},sourceId:gC}),vC=L({id:984122,name:`Forma`,network:`forma`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.forma.art`],webSocket:[`wss://ws.forma.art`]}},blockExplorers:{default:{name:`Forma Explorer`,url:`https://explorer.forma.art`}},contracts:{multicall3:{address:`0xd53C6FFB123F7349A32980F87faeD8FfDc9ef079`,blockCreated:252705}}});var yC=11155111;const bC=L({id:132902,name:`Form Testnet`,nativeCurrency:{decimals:18,name:`Ethereum`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia-rpc.form.network/http`],webSocket:[`wss://sepolia-rpc.form.network/ws`]}},blockExplorers:{default:{name:`Form Testnet Explorer`,url:`https://sepolia-explorer.form.network`}},contracts:{...R.contracts,addressManager:{[yC]:{address:`0xd5C38fa934f7fd7477D4800F4f38a1c5BFdF1373`}},l1CrossDomainMessenger:{[yC]:{address:`0x37A68565c4BE9700b3E3Ec60cC4416cAC3052FAa`}},l2OutputOracle:{[yC]:{address:`0x9eA2239E65a59EC9C7F1ED4C116dD58Da71Fc1e2`}},portal:{[yC]:{address:`0x60377e3cE15dF4CCA24c4beF076b60314240b032`}},l1StandardBridge:{[yC]:{address:`0xD4531f633942b2725896F47cD2aFd260b44Ab1F7`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0,sourceId:yC}),xC=L({id:80931,name:`Forta Chain`,nativeCurrency:{symbol:`FORT`,name:`FORT`,decimals:18},rpcUrls:{default:{http:[`https://rpc-forta-chain-8gj1qndmfc.t.conduit.xyz`]}},blockExplorers:{default:{name:`Forta Explorer`,url:`https://explorer.forta.org`}}}),SC=L({id:31337,name:`Foundry`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`],webSocket:[`ws://127.0.0.1:8545`]}}});var CC=1;const wC=L({...R,id:252,name:`Fraxtal`,nativeCurrency:{name:`Frax`,symbol:`FRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.frax.com`]}},blockExplorers:{default:{name:`fraxscan`,url:`https://fraxscan.com`,apiUrl:`https://api.fraxscan.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[CC]:{address:`0x66CC916Ed5C6C2FA97014f7D1cD141528Ae171e4`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[CC]:{address:`0x36cb65c1967A0Fb0EEE11569C51C2f2aA1Ca6f6D`,blockCreated:19135323}},l1StandardBridge:{[CC]:{address:`0x34C0bD5877A5Ee7099D0f5688D65F4bB9158BDE2`,blockCreated:19135323}}},sourceId:CC});var TC=17e3;const EC=L({...R,id:2522,name:`Fraxtal Testnet`,nativeCurrency:{name:`Frax`,symbol:`FRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.frax.com`]}},blockExplorers:{default:{name:`fraxscan testnet`,url:`https://holesky.fraxscan.com`,apiUrl:`https://api-holesky.fraxscan.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[TC]:{address:`0x715EA64DA13F4d0831ece4Ad3E8c1aa013167F32`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[TC]:{address:`0xB9c64BfA498d5b9a8398Ed6f46eb76d90dE5505d`,blockCreated:318416}},l1StandardBridge:{[TC]:{address:`0x0BaafC217162f64930909aD9f2B27125121d6332`,blockCreated:318416}}},sourceId:TC});var DC=1;const OC=L({...R,id:33979,name:`Funki`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.funkichain.com`]}},blockExplorers:{default:{name:`Funki Mainnet Explorer`,url:`https://funkiscan.io`}},contracts:{...R.contracts},sourceId:DC});var kC=11155111;const AC=L({...R,id:3397901,network:`funkiSepolia`,name:`Funki Sepolia Sandbox`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://funki-testnet.alt.technology`]}},blockExplorers:{default:{name:`Funki Sepolia Sandbox Explorer`,url:`https://sepolia-sandbox.funkichain.com/`}},testnet:!0,contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1620204}},sourceId:kC}),jC=L({id:122,name:`Fuse`,nativeCurrency:{name:`Fuse`,symbol:`FUSE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.fuse.io`]}},blockExplorers:{default:{name:`Fuse Explorer`,url:`https://explorer.fuse.io`,apiUrl:`https://explorer.fuse.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:16146628}}}),MC=L({id:123,name:`Fuse Sparknet`,nativeCurrency:{name:`Spark`,symbol:`SPARK`,decimals:18},rpcUrls:{default:{http:[`https://rpc.fusespark.io`]}},blockExplorers:{default:{name:`Sparkent Explorer`,url:`https://explorer.fusespark.io`,apiUrl:`https://explorer.fusespark.io/api`}}}),NC=L({id:32659,name:`Fusion Mainnet`,nativeCurrency:{name:`Fusion`,symbol:`FSN`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.fusionnetwork.io`],webSocket:[`wss://mainnet.fusionnetwork.io`]}},blockExplorers:{default:{name:`FSNscan`,url:`https://fsnscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10441605}},testnet:!1}),PC=L({id:46688,name:`Fusion Testnet`,nativeCurrency:{name:`Fusion`,symbol:`FSN`,decimals:18},rpcUrls:{default:{http:[`https://testnet.fusionnetwork.io`],webSocket:[`wss://testnet.fusionnetwork.io`]}},blockExplorers:{default:{name:`FSNscan`,url:`https://testnet.fsnscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10428309}},testnet:!0});var FC=17e3;const IC=L({...R,name:`Garnet Testnet`,testnet:!0,id:17069,sourceId:FC,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.garnetchain.com`],webSocket:[`wss://rpc.garnetchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.garnetchain.com`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[FC]:{address:`0x57ee40586fbE286AfC75E67cb69511A6D9aF5909`,blockCreated:1274684}},l2OutputOracle:{[FC]:{address:`0xCb8E7AC561b8EF04F2a15865e9fbc0766FEF569B`,blockCreated:1274684}},l1StandardBridge:{[FC]:{address:`0x09bcDd311FE398F80a78BE37E489f5D440DB95DE`,blockCreated:1274684}}}}),LC=L({id:63157,name:`Geist Mainnet`,nativeCurrency:{decimals:18,name:`Aavegotchi GHST Token`,symbol:`GHST`},rpcUrls:{default:{http:[`https://geist-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://geist-mainnet.explorer.alchemy.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:660735}}}),RC=L({id:16507,name:`Genesys Mainnet`,nativeCurrency:{decimals:18,name:`GSYS`,symbol:`GSYS`},rpcUrls:{default:{http:[`https://rpc.genesys.network`]}},blockExplorers:{default:{name:`Genesys Explorer`,url:`https://gchainexplorer.genesys.network`}},testnet:!1});var zC=11155111;const BC=L({...R,id:91342,network:`giwa-sepolia`,name:`GIWA Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://sepolia-rpc.giwa.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia-explorer.giwa.io`,apiUrl:`https://sepolia-explorer.giwa.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},disputeGameFactory:{[zC]:{address:`0x37347caB2afaa49B776372279143D71ad1f354F6`}},portal:{[zC]:{address:`0x956962C34687A954e611A83619ABaA37Ce6bC78A`}},l1StandardBridge:{[zC]:{address:`0x77b2ffc0F57598cAe1DB76cb398059cF5d10A7E7`}}},testnet:!0,sourceId:zC}),VC=L({id:251,name:`Glide L1 Protocol XP`,nativeCurrency:{name:`GLXP`,symbol:`GLXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc-api.glideprotocol.xyz/l1-rpc`],webSocket:[`wss://rpc-api.glideprotocol.xyz/l1-rpc`]}},blockExplorers:{default:{name:`Glide Protocol Explore`,url:`https://blockchain-explorer.glideprotocol.xyz`}},testnet:!1}),HC=L({id:253,name:`Glide L2 Protocol XP`,nativeCurrency:{name:`GLXP`,symbol:`GLXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc-api.glideprotocol.xyz/l2-rpc`],webSocket:[`wss://rpc-api.glideprotocol.xyz/l2-rpc`]}},blockExplorers:{default:{name:`Glide Protocol Explore`,url:`https://blockchain-explorer.glideprotocol.xyz`}},testnet:!1}),UC=L({id:100,name:`Gnosis`,nativeCurrency:{decimals:18,name:`xDAI`,symbol:`XDAI`},blockTime:5e3,rpcUrls:{default:{http:[`https://rpc.gnosischain.com`],webSocket:[`wss://rpc.gnosischain.com/wss`]}},blockExplorers:{default:{name:`Gnosisscan`,url:`https://gnosisscan.io`,apiUrl:`https://api.gnosisscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:21022491}}}),WC=L({id:10200,name:`Gnosis Chiado`,nativeCurrency:{decimals:18,name:`Gnosis`,symbol:`xDAI`},blockTime:5e3,rpcUrls:{default:{http:[`https://rpc.chiadochain.net`],webSocket:[`wss://rpc.chiadochain.net/wss`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.chiadochain.net`,apiUrl:`https://blockscout.chiadochain.net/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4967313}},testnet:!0}),GC=L({id:2345,name:`GOAT`,nativeCurrency:{decimals:18,name:`Bitcoin`,symbol:`BTC`},rpcUrls:{default:{http:[`https://rpc.goat.network`]}},blockExplorers:{default:{name:`Goat Explorer`,url:`https://explorer.goat.network`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),KC=L({id:1663,name:`Horizen Gobi Testnet`,nativeCurrency:{decimals:18,name:`Test ZEN`,symbol:`tZEN`},rpcUrls:{default:{http:[`https://gobi-testnet.horizenlabs.io/ethv1`]}},blockExplorers:{default:{name:`Gobi Explorer`,url:`https://gobi-explorer.horizen.io`}},contracts:{},testnet:!0}),qC=L({id:60,name:`GoChain`,nativeCurrency:{decimals:18,name:`GO`,symbol:`GO`},rpcUrls:{default:{http:[`https://rpc.gochain.io`]}},blockExplorers:{default:{name:`GoChain Explorer`,url:`https://explorer.gochain.io`}},testnet:!1}),JC=L({id:71402,name:`Godwoken Mainnet`,nativeCurrency:{decimals:18,name:`pCKB`,symbol:`pCKB`},rpcUrls:{default:{http:[`https://v1.mainnet.godwoken.io/rpc`]}},blockExplorers:{default:{name:`GW Scan`,url:`https://v1.gwscan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:15034}},testnet:!1}),YC=L({id:5,name:`Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://5.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.etherscan.io`,apiUrl:`https://api-goerli.etherscan.io/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},ensUniversalResolver:{address:`0xfc4AC75C46C914aF5892d6d3eFFcebD7917293F1`,blockCreated:10339206},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6507670}},testnet:!0}),XC=L({id:440017,name:`Graphite Network`,nativeCurrency:{name:`Graphite`,symbol:`@G`,decimals:18},rpcUrls:{default:{http:[`https://anon-entrypoint-1.atgraphite.com`],webSocket:[`wss://ws-anon-entrypoint-1.atgraphite.com`]}},blockExplorers:{default:{name:`Graphite Spectre`,url:`https://main.atgraphite.com`,apiUrl:`https://api.main.atgraphite.com/api`}},testnet:!1}),ZC=L({id:54170,name:`Graphite Network Testnet`,nativeCurrency:{name:`Graphite`,symbol:`@G`,decimals:18},rpcUrls:{default:{http:[`https://anon-entrypoint-test-1.atgraphite.com`],webSocket:[`wss://ws-anon-entrypoint-test-1.atgraphite.com`]}},blockExplorers:{default:{name:`Graphite Testnet Spectre`,url:`https://test.atgraphite.com`,apiUrl:`https://api.test.atgraphite.com/api`}},testnet:!0}),QC=L({id:1625,name:`Gravity Alpha Mainnet`,nativeCurrency:{name:`G`,symbol:`G`,decimals:18},rpcUrls:{default:{http:[`https://rpc.gravity.xyz`]}},blockExplorers:{default:{name:`Gravity Explorer`,url:`https://explorer.gravity.xyz`,apiUrl:`https://explorer.gravity.xyz/api`}},contracts:{multicall3:{address:`0xf8ac4BEB2F75d2cFFb588c63251347fdD629B92c`,blockCreated:16851}}}),$C=L({id:43419,name:`Gunz Mainnet`,nativeCurrency:{name:`GUN`,symbol:`GUN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.gunzchain.io/ext/bc/2M47TxWHGnhNtq6pM5zPXdATBtuqubxn5EPFgFmEawCQr9WFML/rpc`]}},blockExplorers:{default:{name:`Gunz Explorer`,url:`https://gunzscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:70502}}}),ew=L({id:260,name:`Guru Network Mainnet`,nativeCurrency:{name:`GURU Token`,symbol:`GURU`,decimals:18},rpcUrls:{default:{http:[`https://rpc-main.gurunetwork.ai`,`https://rpc.gurunetwork.ai/archive/260`]}},blockExplorers:{default:{name:`Guruscan`,url:`https://scan.gurunetwork.ai`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:271691}},testnet:!1}),tw=L({id:261,name:`Guru Network Testnet`,nativeCurrency:{name:`tGURU Token`,symbol:`tGURU`,decimals:18},rpcUrls:{default:{http:[`https://rpc-test.gurunetwork.ai`,`https://rpc.gurunetwork.ai/archive/261`]}},blockExplorers:{default:{name:`Guruscan`,url:`https://sepolia.gurunetwork.ai`}},testnet:!0}),nw=L({id:5112,name:`Ham`,nativeCurrency:{decimals:18,name:`Ham`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.ham.fun`],webSocket:[`wss://rpc.ham.fun`]}},blockExplorers:{default:{name:`Ham Chain Explorer`,url:`https://explorer.ham.fun`,apiUrl:`https://explorer.ham.fun/api/v2`}}}),rw=L({id:216,name:`Happychain Testnet`,nativeCurrency:{symbol:`HAPPY`,name:`HAPPY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.happy.tech/http`],webSocket:[`wss://rpc.testnet.happy.tech/ws`]}},blockExplorers:{default:{name:`Happy Chain Testnet Explorer`,url:`https://explorer.testnet.happy.tech`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1}},testnet:!0}),iw=L({id:11235,name:`HAQQ Mainnet`,nativeCurrency:{decimals:18,name:`Islamic Coin`,symbol:`ISLM`},rpcUrls:{default:{http:[`https://rpc.eth.haqq.network`]}},blockExplorers:{default:{name:`HAQQ Explorer`,url:`https://explorer.haqq.network`,apiUrl:`https://explorer.haqq.network/api`}}}),aw=L({id:54211,name:`HAQQ Testedge 2`,nativeCurrency:{decimals:18,name:`Islamic Coin`,symbol:`ISLMT`},rpcUrls:{default:{http:[`https://rpc.eth.testedge2.haqq.network`]}},blockExplorers:{default:{name:`HAQQ Explorer`,url:`https://explorer.testedge2.haqq.network`,apiUrl:`https://explorer.testedge2.haqq.network/api`}}}),ow=L({id:31337,name:`Hardhat`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),sw=L({id:16666e5,name:`Harmony One`,nativeCurrency:{name:`Harmony`,symbol:`ONE`,decimals:18},rpcUrls:{default:{http:[`https://1666600000.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Harmony Explorer`,url:`https://explorer.harmony.one`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:24185753}}}),cw=L({id:177,name:`HashKey Chain`,nativeCurrency:{decimals:18,name:`HashKey EcoPoints`,symbol:`HSK`},rpcUrls:{default:{http:[`https://mainnet.hsk.xyz`]}},blockExplorers:{default:{name:`HashKey Chain Explorer`,url:`https://hashkey.blockscout.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),lw=L({id:133,name:`HashKey Chain Testnet`,nativeCurrency:{decimals:18,name:`HashKey EcoPoints`,symbol:`HSK`},rpcUrls:{default:{http:[`https://hashkeychain-testnet.alt.technology`]}},blockExplorers:{default:{name:`HashKey Chain Explorer`,url:`https://hashkeychain-testnet-explorer.alt.technology`}},testnet:!0}),uw=L({id:1523903251,name:`Haust Network Testnet`,nativeCurrency:{decimals:18,name:`HAUST`,symbol:`HAUST`},rpcUrls:{default:{http:[`https://rpc-testnet.haust.app`]}},blockExplorers:{default:{name:`Haust Network Testnet Explorer`,url:`https://explorer-testnet.haust.app`}},testnet:!0}),dw=L({id:295,name:`Hedera Mainnet`,network:`hedera-mainnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/mainnet`}},testnet:!1}),fw=L({id:297,name:`Hedera Previewnet`,network:`hedera-previewnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://previewnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/previewnet`}},testnet:!0}),pw=L({id:296,name:`Hedera Testnet`,network:`hedera-testnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://testnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/testnet`}},testnet:!0}),mw=L({id:8668,name:`Hela Mainnet`,nativeCurrency:{name:`HLUSD`,symbol:`HLUSD`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.helachain.com`]}},blockExplorers:{default:{name:`Hela explorer`,url:`https://mainnet-blockexplorer.helachain.com`}},testnet:!1}),hw=L({id:43111,name:`Hemi`,network:`Hemi`,blockTime:12e3,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hemi.network/rpc`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.hemi.xyz`}},testnet:!1}),gw=L({id:743111,name:`Hemi Sepolia`,network:`Hemi Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.rpc.hemi.network/rpc`]}},blockExplorers:{default:{name:`Hemi Sepolia explorer`,url:`https://testnet.explorer.hemi.xyz`}},testnet:!0}),_w=L({id:17e3,name:`Holesky`,nativeCurrency:{name:`Holesky Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://ethereum-holesky-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://holesky.etherscan.io`,apiUrl:`https://api-holesky.etherscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:77},ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:4295055}},testnet:!0}),vw=L({id:560048,name:`Hoodi`,nativeCurrency:{name:`Hoodi Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hoodi.ethpandaops.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://hoodi.etherscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2589}},testnet:!0}),yw=L({id:269,name:`High Performance Blockchain`,nativeCurrency:{name:`HPB`,symbol:`HPB`,decimals:18},rpcUrls:{default:{http:[`https://hpbnode.com`]}},blockExplorers:{default:{name:`hpbScan`,url:`https://hscan.org`}},testnet:!1}),bw=L({id:12323,name:`Huddle01 dRTC Chain`,nativeCurrency:{name:`Ethereum`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://huddle01.calderachain.xyz/http`],webSocket:[`wss://huddle01.calderachain.xyz/ws`]}},blockExplorers:{default:{name:`Huddle01 Caldera Explorer`,url:`https://huddle01.calderaexplorer.xyz`,apiUrl:`https://huddle01.calderaexplorer.xyz/api`}},sourceId:42161}),xw=L({id:2524852,name:`Huddle01 dRTC Chain Testnet`,nativeCurrency:{name:`Ethereum`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://huddle-testnet.rpc.caldera.xyz/http`],webSocket:[`wss://huddle-testnet.rpc.caldera.xyz/ws`]}},blockExplorers:{default:{name:`Huddle01 Caldera Explorer`,url:`https://huddle-testnet.explorer.caldera.xyz`,apiUrl:`https://huddle-testnet.explorer.caldera.xyz/api`}},sourceId:421614}),Sw=L({id:6985385,name:`Humanity`,nativeCurrency:{name:`H`,symbol:`H`,decimals:18},rpcUrls:{default:{http:[`https://humanity-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Humanity Mainnet Explorer`,url:`https://humanity-mainnet.explorer.alchemy.com`,apiUrl:`https://humanity-mainnet.explorer.alchemy.com/api`}},testnet:!1}),Cw=L({id:7080969,name:`Humanity Testnet`,nativeCurrency:{name:`tHP`,symbol:`tHP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.humanity.org`]}},blockExplorers:{default:{name:`Humanity Testnet Explorer`,url:`https://humanity-testnet.explorer.alchemy.com`,apiUrl:`https://humanity-testnet.explorer.alchemy.com/api`}},testnet:!0}),ww=L({id:5234,name:`Humanode`,nativeCurrency:{name:`HMND`,symbol:`HMND`,decimals:18},rpcUrls:{default:{http:[`https://explorer-rpc-http.mainnet.stages.humanode.io`],webSocket:[`wss://explorer-rpc-ws.mainnet.stages.humanode.io`]}},blockExplorers:{default:{name:`Subscan`,url:`https://humanode.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4413097}}}),Tw=L({id:14853,name:`Humanode Testnet 5`,nativeCurrency:{name:`HMND`,symbol:`HMND`,decimals:18},rpcUrls:{default:{http:[`https://explorer-rpc-http.testnet5.stages.humanode.io`],webSocket:[`wss://explorer-rpc-ws.testnet5.stages.humanode.io`]}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),Ew=L({id:2911,name:`HYCHAIN`,nativeCurrency:{name:`HYTOPIA`,symbol:`TOPIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hychain.com/http`]}},blockExplorers:{default:{name:`HYCHAIN Explorer`,url:`https://explorer.hychain.com`}},testnet:!1}),Dw=L({id:29112,name:`HYCHAIN Testnet`,nativeCurrency:{name:`HYTOPIA`,symbol:`TOPIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hychain.com/http`]}},blockExplorers:{default:{name:`HYCHAIN Explorer`,url:`https://testnet-rpc.hychain.com/http`}},testnet:!0}),Ow=L({id:998,name:`Hyperliquid EVM Testnet`,nativeCurrency:{name:`HYPE`,symbol:`HYPE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hyperliquid-testnet.xyz/evm`]}},testnet:!0}),kw=L({id:73115,name:`ICB Network`,nativeCurrency:{decimals:18,name:`ICB Native Token`,symbol:`ICBX`},rpcUrls:{default:{http:[`https://rpc1-mainnet.icbnetwork.info`]}},blockExplorers:{default:{name:`ICB Explorer`,url:`https://icbscan.io`,apiUrl:`https://icbscan.io/api`}},testnet:!1}),Aw=L({id:74,name:`IDChain Mainnet`,nativeCurrency:{decimals:18,name:`EIDI`,symbol:`EIDI`},rpcUrls:{default:{http:[`https://idchain.one/rpc`],webSocket:[`wss://idchain.one/ws`]}},blockExplorers:{default:{name:`IDChain Explorer`,url:`https://explorer.idchain.one`}},testnet:!1}),jw=L({id:13371,name:`Immutable zkEVM`,nativeCurrency:{decimals:18,name:`Immutable Coin`,symbol:`IMX`},rpcUrls:{default:{http:[`https://rpc.immutable.com`]}},blockExplorers:{default:{name:`Immutable Explorer`,url:`https://explorer.immutable.com`,apiUrl:`https://explorer.immutable.com/api`}},contracts:{multicall3:{address:`0x236bdA4589e44e6850f5aC6a74BfCa398a86c6c0`,blockCreated:4335972}}}),Mw=L({id:13473,name:`Immutable zkEVM Testnet`,nativeCurrency:{decimals:18,name:`Immutable Coin`,symbol:`IMX`},rpcUrls:{default:{http:[`https://rpc.testnet.immutable.com`]}},blockExplorers:{default:{name:`Immutable Testnet Explorer`,url:`https://explorer.testnet.immutable.com/`}},contracts:{multicall3:{address:`0x2CC787Ed364600B0222361C4188308Fa8E68bA60`,blockCreated:5977391}},testnet:!0}),Nw=L({id:2525,name:`inEVM Mainnet`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://mainnet.rpc.inevm.com/http`]}},blockExplorers:{default:{name:`inEVM Explorer`,url:`https://inevm.calderaexplorer.xyz`,apiUrl:`https://inevm.calderaexplorer.xyz/api/v2`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:118606}}}),Pw=L({id:7233,name:`InitVerse Mainnet`,nativeCurrency:{decimals:18,name:`InitVerse`,symbol:`INI`},rpcUrls:{default:{http:[`https://rpc-mainnet.inichain.com`]}},blockExplorers:{default:{name:`InitVerseScan`,url:`https://www.iniscan.com`,apiUrl:`https://explorer-api.inichain.com/api`}},contracts:{multicall3:{address:`0x83466BE48A067115FFF91f7b892Ed1726d032e47`,blockCreated:2318}}}),Fw=L({id:7234,name:`InitVerse Genesis Testnet`,nativeCurrency:{decimals:18,name:`InitVerse`,symbol:`INI`},rpcUrls:{default:{http:[`https://rpc-testnet.inichain.com`]}},blockExplorers:{default:{name:`InitVerseGenesisScan`,url:`https://genesis-testnet.iniscan.com`,apiUrl:`https://explorer-testnet-api.inichain.com/api`}},contracts:{multicall3:{address:`0x0cF32CBDd6c437331EA4f85ed2d881A5379B5a6F`,blockCreated:16361}},testnet:!0}),Iw=L({id:1776,name:`Injective`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://sentry.evm-rpc.injective.network`],webSocket:[`wss://sentry.evm-ws.injective.network`]}},blockExplorers:{default:{name:`Injective Explorer`,url:`https://blockscout.injective.network`,apiUrl:`https://blockscout.injective.network/api`}},testnet:!1}),Lw=L({id:1439,name:`Injective Testnet`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://k8s.testnet.json-rpc.injective.network`],webSocket:[`wss://k8s.testnet.ws.injective.network`]}},blockExplorers:{default:{name:`Injective Explorer`,url:`https://testnet.blockscout.injective.network`,apiUrl:`https://testnet.blockscout.injective.network/api`}},testnet:!0});var Rw=1;const zw=L({...R,id:57073,name:`Ink`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-gel.inkonchain.com`,`https://rpc-qnd.inkonchain.com`],webSocket:[`wss://rpc-gel.inkonchain.com`,`wss://rpc-qnd.inkonchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.inkonchain.com`,apiUrl:`https://explorer.inkonchain.com/api/v2`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},...R.contracts,disputeGameFactory:{[Rw]:{address:`0x10d7b35078d3baabb96dd45a9143b94be65b12cd`}},portal:{[Rw]:{address:`0x5d66c1782664115999c47c9fa5cd031f495d3e4f`}},l1StandardBridge:{[Rw]:{address:`0x88ff1e5b602916615391f55854588efcbb7663f0`}}},testnet:!1,sourceId:Rw});var Bw=11155111;const Vw=L({...R,id:763373,name:`Ink Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-gel-sepolia.inkonchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer-sepolia.inkonchain.com/`,apiUrl:`https://explorer-sepolia.inkonchain.com/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},disputeGameFactory:{[Bw]:{address:`0x860e626c700af381133d9f4af31412a2d1db3d5d`}},portal:{[Bw]:{address:`0x5c1d29c6c9c8b0800692acc95d700bcb4966a1d7`}},l1StandardBridge:{[Bw]:{address:`0x33f60714bbd74d62b66d79213c348614de51901c`}}},testnet:!0,sourceId:Bw}),Hw=L({id:8822,name:`IOTA EVM`,network:`iotaevm`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://json-rpc.evm.iotaledger.net`],webSocket:[`wss://ws.json-rpc.evm.iotaledger.net`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.evm.iota.org`,apiUrl:`https://explorer.evm.iota.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:25022}}}),Uw=L({id:1075,name:`IOTA EVM Testnet`,network:`iotaevm-testnet`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://json-rpc.evm.testnet.iotaledger.net`],webSocket:[`wss://ws.json-rpc.evm.testnet.iotaledger.net`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.evm.testnet.iotaledger.net`,apiUrl:`https://explorer.evm.testnet.iotaledger.net/api`}},testnet:!0}),Ww=L({id:4689,name:`IoTeX`,nativeCurrency:{decimals:18,name:`IoTeX`,symbol:`IOTX`},rpcUrls:{default:{http:[`https://babel-api.mainnet.iotex.io`],webSocket:[`wss://babel-api.mainnet.iotex.io`]}},blockExplorers:{default:{name:`IoTeXScan`,url:`https://iotexscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:22163670}}}),Gw=L({id:4690,name:`IoTeX Testnet`,nativeCurrency:{decimals:18,name:`IoTeX`,symbol:`IOTX`},rpcUrls:{default:{http:[`https://babel-api.testnet.iotex.io`],webSocket:[`wss://babel-api.testnet.iotex.io`]}},blockExplorers:{default:{name:`IoTeXScan`,url:`https://testnet.iotexscan.io`}},contracts:{multicall3:{address:`0xb5cecD6894c6f473Ec726A176f1512399A2e355d`,blockCreated:24347592}},testnet:!0}),Kw=L({id:8017,name:`iSunCoin Mainnet`,nativeCurrency:{decimals:18,name:`ISC`,symbol:`ISC`},rpcUrls:{default:{http:[`https://mainnet.isuncoin.com`]}},blockExplorers:{default:{name:`iSunCoin Explorer`,url:`https://baifa.io/app/chains/8017`}}}),qw=L({id:8899,name:`JB Chain`,network:`jbc`,nativeCurrency:{name:`JBC`,symbol:`JBC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-l1.jibchain.net`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp-l1.jibchain.net`,apiUrl:`https://exp-l1.jibchain.net/api`}},contracts:{multicall3:{address:`0xc0C8C486D1466C57Efe13C2bf000d4c56F47CBdC`,blockCreated:2299048}},testnet:!1}),Jw=L({id:88991,name:`Jibchain Testnet`,nativeCurrency:{name:`tJBC`,symbol:`tJBC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.jibchain.net`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp.testnet.jibchain.net`,apiUrl:`https://exp.testnet.jibchain.net/api`}},contracts:{multicall3:{address:`0xa1a858ad9041B4741e620355a3F96B3c78e70ecE`,blockCreated:32848}},testnet:!0}),Yw=L({id:81,name:`Japan Open Chain Mainnet`,nativeCurrency:{decimals:18,name:`Japan Open Chain Token`,symbol:`JOC`},rpcUrls:{default:{http:[`https://rpc-1.japanopenchain.org:8545`,`https://rpc-2.japanopenchain.org:8545`,`https://rpc-3.japanopenchain.org`]}},blockExplorers:{default:{name:`Block Explorer`,url:`https://explorer.japanopenchain.org`}},testnet:!1}),Xw=L({id:10081,name:`Japan Open Chain Testnet`,nativeCurrency:{decimals:18,name:`Japan Open Chain Testnet Token`,symbol:`JOCT`},rpcUrls:{default:{http:[`https://rpc-1.testnet.japanopenchain.org:8545`,`https://rpc-2.testnet.japanopenchain.org:8545`,`https://rpc-3.testnet.japanopenchain.org`]}},blockExplorers:{default:{name:`Testnet Block Explorer`,url:`https://explorer.testnet.japanopenchain.org`}},testnet:!0}),Zw=L({id:5734951,name:`Jovay Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.zan.top/public/jovay-mainnet`]}},blockExplorers:{default:{name:`Jovay Explorer`,url:`https://explorer.jovay.io`}},testnet:!1}),Qw=L({id:2019775,name:`Jovay Sepolia Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.zan.top/public/jovay-testnet`]}},blockExplorers:{default:{name:`Jovay Testnet Explorer`,url:`https://sepolia-explorer.jovay.io/l2`}},testnet:!0}),$w=L({id:45003,name:`Juneo JUNE-Chain`,nativeCurrency:{decimals:18,name:`JUNE-Chain`,symbol:`JUNE`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/JUNE/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/2`,apiUrl:`https://juneoscan.io/chain/2/api`}}}),eT=L({id:45013,name:`Juneo BCH1-Chain`,nativeCurrency:{decimals:18,name:`Juneo BCH1-Chain`,symbol:`BCH1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/BCH1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/12`,apiUrl:`https://juneoscan.io/chain/12/api`}}}),tT=L({id:45004,name:`Juneo DAI1-Chain`,nativeCurrency:{decimals:18,name:`Juneo DAI1-Chain`,symbol:`DAI1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/DAI1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/5`,apiUrl:`https://juneoscan.io/chain/5/api`}}}),nT=L({id:45010,name:`Juneo DOGE1-Chain`,nativeCurrency:{decimals:18,name:`Juneo DOGE1-Chain`,symbol:`DOGE1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/DOGE1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/10`,apiUrl:`https://juneoscan.io/chain/10/api`}}}),rT=L({id:45011,name:`Juneo EUR1-Chain`,nativeCurrency:{decimals:18,name:`Juneo EUR1-Chain`,symbol:`EUR1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/EUR1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/6`,apiUrl:`https://juneoscan.io/chain/6/api`}}}),iT=L({id:45008,name:`Juneo GLD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo GLD1-Chain`,symbol:`GLD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/GLD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/8`,apiUrl:`https://juneoscan.io/chain/8/api`}}}),aT=L({id:45014,name:`Juneo LINK1-Chain`,nativeCurrency:{decimals:18,name:`Juneo LINK1-Chain`,symbol:`LINK1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/LINK1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/13`,apiUrl:`https://juneoscan.io/chain/13/api`}}}),oT=L({id:45009,name:`Juneo LTC1-Chain`,nativeCurrency:{decimals:18,name:`Juneo LTC1-Chain`,symbol:`LTC1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/LTC1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/11`,apiUrl:`https://juneoscan.io/chain/11/api`}}}),sT=L({id:45007,name:`Juneo mBTC1-Chain`,nativeCurrency:{decimals:18,name:`Juneo mBTC1-Chain`,symbol:`mBTC1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/mBTC1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/9`,apiUrl:`https://juneoscan.io/chain/9/api`}}}),cT=L({id:45012,name:`Juneo SGD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo SGD1-Chain`,symbol:`SGD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/SGD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/7`,apiUrl:`https://juneoscan.io/chain/7/api`}}}),lT=L({id:101003,name:`Socotra JUNE-Chain`,nativeCurrency:{decimals:18,name:`Socotra JUNE-Chain`,symbol:`JUNE`},rpcUrls:{default:{http:[`https://rpc.socotra-testnet.network/ext/bc/JUNE/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://socotra.juneoscan.io/chain/2`,apiUrl:`https://socotra.juneoscan.io/chain/2/api`}},testnet:!0}),uT=L({id:45006,name:`Juneo USD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo USD1-Chain`,symbol:`USD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/USD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/4`,apiUrl:`https://juneoscan.io/chain/4/api`}}}),dT=L({id:45005,name:`Juneo USDT1-Chain`,nativeCurrency:{decimals:18,name:`Juneo USDT1-Chain`,symbol:`USDT1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/USDT1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/3`,apiUrl:`https://juneoscan.io/chain/3/api`}}}),fT=L({id:8217,name:`Kaia`,nativeCurrency:{decimals:18,name:`Kaia`,symbol:`KAIA`},rpcUrls:{default:{http:[`https://public-en.node.kaia.io`]}},blockExplorers:{default:{name:`KaiaScan`,url:`https://kaiascan.io`,apiUrl:`https://api-cypress.klaytnscope.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96002415}}}),pT=L({id:1001,name:`Kairos Testnet`,network:`kairos`,nativeCurrency:{decimals:18,name:`Kairos KAIA`,symbol:`KAIA`},rpcUrls:{default:{http:[`https://public-en-kairos.node.kaia.io`]}},blockExplorers:{default:{name:`KaiaScan`,url:`https://kairos.kaiascan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:123390593}},testnet:!0}),mT=L({id:1802203764,name:`Kakarot Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.kakarot.org`]}},blockExplorers:{default:{name:`Kakarot Scan`,url:`https://sepolia.kakarotscan.org`}},testnet:!0}),hT=L({id:920637907288165,name:`Kakarot Starknet Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.kakarot.org`]}},blockExplorers:{default:{name:`Kakarot Scan`,url:`https://sepolia.kakarotscan.org`}},testnet:!0}),gT=L({id:24,name:`KardiaChain Mainnet`,nativeCurrency:{name:`KAI`,symbol:`KAI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.kardiachain.io`]}},blockExplorers:{default:{name:`KardiaChain Explorer`,url:`https://explorer.kardiachain.io`}},testnet:!1}),_T=L({id:686,name:`Karura`,network:`karura`,nativeCurrency:{name:`Karura`,symbol:`KAR`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-karura.aca-api.network`],webSocket:[`wss://eth-rpc-karura.aca-api.network`]}},blockExplorers:{default:{name:`Karura Blockscout`,url:`https://blockscout.karura.network`,apiUrl:`https://blockscout.karura.network/api`}},testnet:!1}),vT=L({id:747474,name:`Katana`,network:`katana`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.katana.network`]}},blockExplorers:{default:{name:`katana explorer`,url:`https://explorer.katanarpc.com`}},testnet:!1}),yT=L({id:2222,name:`Kava EVM`,network:`kava-mainnet`,nativeCurrency:{name:`Kava`,symbol:`KAVA`,decimals:18},rpcUrls:{default:{http:[`https://evm.kava.io`]}},blockExplorers:{default:{name:`Kava EVM Explorer`,url:`https://kavascan.com`,apiUrl:`https://kavascan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3661165}},testnet:!1}),bT=L({id:2221,name:`Kava EVM Testnet`,network:`kava-testnet`,nativeCurrency:{name:`Kava`,symbol:`KAVA`,decimals:18},rpcUrls:{default:{http:[`https://evm.testnet.kava.io`]}},blockExplorers:{default:{name:`Kava EVM Testnet Explorer`,url:`https://testnet.kavascan.com/`,apiUrl:`https://testnet.kavascan.com/api`}},contracts:{multicall3:{address:`0xDf1D724A7166261eEB015418fe8c7679BBEa7fd6`,blockCreated:7242179}},testnet:!0}),xT=L({id:321,name:`KCC Mainnet`,network:`KCC Mainnet`,nativeCurrency:{decimals:18,name:`KCS`,symbol:`KCS`},rpcUrls:{default:{http:[`https://kcc-rpc.com`]}},blockExplorers:{default:{name:`KCC Explorer`,url:`https://explorer.kcc.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11760430}},testnet:!1}),ST=L({id:1336,name:`Kii Testnet Oro`,network:`kii-testnet-oro`,nativeCurrency:{name:`Kii`,symbol:`KII`,decimals:18},rpcUrls:{default:{http:[`https://json-rpc.uno.sentry.testnet.v3.kiivalidator.com`]}},blockExplorers:{default:{name:`KiiExplorer`,url:`https://explorer.kiichain.io/testnet`}},testnet:!0}),CT=L({id:7887,name:`Kinto Mainnet`,network:`Kinto Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.kinto.xyz/http`]}},blockExplorers:{default:{name:`Kinto Explorer`,url:`https://explorer.kinto.xyz`}},testnet:!1}),wT=L({id:8217,name:`Klaytn`,nativeCurrency:{decimals:18,name:`Klaytn`,symbol:`KLAY`},rpcUrls:{default:{http:[`https://public-en-cypress.klaytn.net`]}},blockExplorers:{default:{name:`KlaytnScope`,url:`https://scope.klaytn.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96002415}}}),TT=L({id:1001,name:`Klaytn Baobab Testnet`,network:`klaytn-baobab`,nativeCurrency:{decimals:18,name:`Baobab Klaytn`,symbol:`KLAY`},rpcUrls:{default:{http:[`https://public-en-baobab.klaytn.net`]}},blockExplorers:{default:{name:`KlaytnScope`,url:`https://baobab.klaytnscope.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:123390593}},testnet:!0}),ET=L({id:701,name:`Koi Network`,nativeCurrency:{decimals:18,name:`Koi Network Native Token`,symbol:`KRING`},rpcUrls:{default:{http:[`https://koi-rpc.darwinia.network`],webSocket:[`wss://koi-rpc.darwinia.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://koi-scan.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:180001}},testnet:!0}),DT=L({id:255,name:`Kroma`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://api.kroma.network`]}},blockExplorers:{default:{name:`Kroma Explorer`,url:`https://blockscout.kroma.network`,apiUrl:`https://blockscout.kroma.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:16054868}},testnet:!1}),OT=L({id:2358,name:`Kroma Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://api.sepolia.kroma.network`]}},blockExplorers:{default:{name:`Kroma Sepolia Explorer`,url:`https://blockscout.sepolia.kroma.network`,apiUrl:`https://blockscout.sepolia.kroma.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:8900914}},testnet:!0}),kT=L({id:12324,name:`L3X Protocol`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.l3x.com`],webSocket:[`wss://rpc-mainnet.l3x.com`]}},blockExplorers:{default:{name:`L3X Mainnet Explorer`,url:`https://explorer.l3x.com`,apiUrl:`https://explorer.l3x.com/api/v2`}},testnet:!1}),AT=L({id:12325,name:`L3X Protocol Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.l3x.com`],webSocket:[`wss://rpc-testnet.l3x.com`]}},blockExplorers:{default:{name:`L3X Testnet Explorer`,url:`https://explorer-testnet.l3x.com`,apiUrl:`https://explorer-testnet.l3x.com/api/v2`}},testnet:!0}),jT=L({id:360890,name:`LAVITA Mainnet`,nativeCurrency:{name:`vTFUEL`,symbol:`vTFUEL`,decimals:18},rpcUrls:{default:{http:[`https://tsub360890-eth-rpc.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`LAVITA Explorer`,url:`https://tsub360890-explorer.thetatoken.org`}},testnet:!1}),MT=L({id:232,name:`Lens`,nativeCurrency:{name:`GHO`,symbol:`GHO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.lens.xyz`]}},blockExplorers:{default:{name:`Lens Block Explorer`,url:`https://explorer.lens.xyz`,apiUrl:`https://explorer.lens.xyz/api`}}}),NT=L({id:37111,name:`Lens Testnet`,nativeCurrency:{name:`GRASS`,symbol:`GRASS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.lens.dev`],webSocket:[`wss://rpc.testnet.lens.dev/ws`]}},blockExplorers:{default:{name:`Lens Block Explorer`,url:`https://block-explorer.testnet.lens.dev`,apiUrl:`https://block-explorer-api.staging.lens.dev/api`}},testnet:!0}),PT=L({id:21363,name:`Lestnet`,nativeCurrency:{name:`Lestnet Ether`,symbol:`LETH`,decimals:18},rpcUrls:{default:{http:[`https://service.lestnet.org`]}},blockExplorers:{default:{name:`Lestnet Explorer`,url:`https://explore.lestnet.org`}},testnet:!0}),FT=L({id:1891,name:`LightLink Pegasus Testnet`,network:`lightlink-pegasus`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://replicator.pegasus.lightlink.io/rpc/v1`]}},blockExplorers:{default:{name:`LightLink Pegasus Explorer`,url:`https://pegasus.lightlink.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:127188532}},testnet:!0}),IT=L({id:1890,name:`LightLink Phoenix Mainnet`,network:`lightlink-phoenix`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://replicator.phoenix.lightlink.io/rpc/v1`]}},blockExplorers:{default:{name:`LightLink Phoenix Explorer`,url:`https://phoenix.lightlink.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:125499184}},testnet:!1});ra(),A(),Fp(),Lu(),Wu(),$u();async function LT(e,t){let{account:n=e.account}=t;if(!n)throw new Om;let r=na(n);try{let{accessList:n,blockNumber:i,blockTag:a,data:o,gas:s,gasPrice:c,maxFeePerGas:l,maxPriorityFeePerGas:u,nonce:d,to:f,value:p,...m}=t,h=(typeof i==`bigint`?k(i):void 0)||a;Qu(t);let g=e.chain?.formatters?.transactionRequest?.format,_=(g||Bu)({...Iu(m,{format:g}),from:r?.address,accessList:n,data:o,gas:s,gasPrice:c,maxFeePerGas:l,maxPriorityFeePerGas:u,nonce:d,to:f,value:p},`estimateGas`),{baseFeePerGas:v,gasLimit:y,priorityFeePerGas:b}=await e.request({method:`linea_estimateGas`,params:h?[_,h]:[_]});return{baseFeePerGas:BigInt(v),gasLimit:BigInt(y),priorityFeePerGas:BigInt(b)}}catch(n){throw Pp(n,{...t,account:r,chain:e.chain})}}const RT={fees:{estimateFeesPerGas:zT,async maxPriorityFeePerGas({block:e,client:t,request:n}){let r=await zT({block:e,client:t,multiply:e=>e,request:n,type:`eip1559`});return r?.maxPriorityFeePerGas?r.maxPriorityFeePerGas:null}}};async function zT({client:e,multiply:t,request:n,type:r}){try{let i=await LT(e,{...n,account:n?.account}),{priorityFeePerGas:a}=i,o=t(BigInt(i.baseFeePerGas))+a;return r===`legacy`?{gasPrice:o}:{maxFeePerGas:o,maxPriorityFeePerGas:a}}catch{return null}}const BT=L({...RT,id:59144,name:`Linea Mainnet`,blockTime:2e3,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.linea.build`],webSocket:[`wss://rpc.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://lineascan.build`,apiUrl:`https://api.lineascan.build/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:42},ensRegistry:{address:`0x50130b669B28C339991d8676FA73CF122a121267`,blockCreated:6682888},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:22222151}},ensTlds:[`.linea.eth`],testnet:!1}),VT=L({id:59140,name:`Linea Goerli Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.goerli.linea.build`],webSocket:[`wss://rpc.goerli.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.lineascan.build`,apiUrl:`https://api-goerli.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:498623}},testnet:!0}),HT=L({...RT,id:59141,name:`Linea Sepolia Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sepolia.linea.build`],webSocket:[`wss://rpc.sepolia.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.lineascan.build`,apiUrl:`https://api-sepolia.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:227427},ensRegistry:{address:`0x5B2636F0f2137B4aE722C01dd5122D7d3e9541f7`,blockCreated:2395094},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:17168484}},ensTlds:[`.linea.eth`],testnet:!0}),UT=L({id:59140,name:`Linea Goerli Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.goerli.linea.build`],webSocket:[`wss://rpc.goerli.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.lineascan.build`,apiUrl:`https://goerli.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:498623}},testnet:!0});var WT=1;const GT=L({...R,id:1135,name:`Lisk`,network:`lisk`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.api.lisk.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.lisk.com`,apiUrl:`https://blockscout.lisk.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xA9d71E1dd7ca26F26e656E66d6AA81ed7f745bf0`},l2OutputOracle:{[WT]:{address:`0x113cB99283AF242Da0A0C54347667edF531Aa7d6`}},portal:{[WT]:{address:`0x26dB93F8b8b4f7016240af62F7730979d353f9A7`}},l1StandardBridge:{[WT]:{address:`0x2658723Bf70c7667De6B25F99fcce13A16D25d08`}}},sourceId:WT});var KT=11155111;const qT=L({...R,id:4202,network:`lisk-sepolia`,name:`Lisk Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sepolia-api.lisk.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia-blockscout.lisk.com`,apiUrl:`https://sepolia-blockscout.lisk.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[KT]:{address:`0xA0E35F56C318DE1bD5D9ca6A94Fe7e37C5663348`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[KT]:{address:`0xe3d90F21490686Ec7eF37BE788E02dfC12787264`}},l1StandardBridge:{[KT]:{address:`0x1Fb30e446eA791cd1f011675E5F3f5311b70faF5`}}},testnet:!0,sourceId:KT}),JT=L({id:9496,name:`Load Alphanet`,nativeCurrency:{name:`Testnet LOAD`,symbol:`tLOAD`,decimals:18},rpcUrls:{default:{http:[`https://alphanet.load.network`]}},blockExplorers:{default:{name:`Load Alphanet Explorer`,url:`https://explorer.load.network`}},testnet:!0}),YT=L({id:1337,name:`Localhost`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),XT=L({id:15551,name:`LoopNetwork Mainnet`,nativeCurrency:{name:`LOOP`,symbol:`LOOP`,decimals:18},rpcUrls:{default:{http:[`https://api.mainnetloop.com`]}},blockExplorers:{default:{name:`LoopNetwork Blockchain Explorer`,url:`https://explorer.mainnetloop.com/`}},testnet:!1}),ZT=L({id:42,network:`lukso`,name:`LUKSO`,nativeCurrency:{name:`LUKSO`,symbol:`LYX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.lukso.network`],webSocket:[`wss://ws-rpc.mainnet.lukso.network`]}},blockExplorers:{default:{name:`LUKSO Mainnet Explorer`,url:`https://explorer.execution.mainnet.lukso.network`,apiUrl:`https://api.explorer.execution.mainnet.lukso.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:468183}}}),QT=L({id:4201,name:`LUKSO Testnet`,nativeCurrency:{decimals:18,name:`LUKSO Testnet`,symbol:`LYXt`},rpcUrls:{default:{http:[`https://rpc.testnet.lukso.network`],webSocket:[`wss://ws-rpc.testnet.lukso.network`]}},blockExplorers:{default:{name:`LUKSO Testnet Explorer`,url:`https://explorer.execution.testnet.lukso.network`,apiUrl:`https://api.explorer.execution.testnet.lukso.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:605348}},testnet:!0}),$T=L({id:994873017,name:`Lumia Mainnet`,network:`LumiaMainnet`,nativeCurrency:{name:`Lumia`,symbol:`LUMIA`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.lumia.org`]}},blockExplorers:{default:{name:`Lumia Explorer`,url:`https://explorer.lumia.org/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3975939}},testnet:!1}),eE=L({id:1952959480,name:`Lumia Testnet`,network:`LumiaTestnet`,nativeCurrency:{name:`Lumia`,symbol:`LUMIA`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.lumia.org`]}},blockExplorers:{default:{name:`Lumia Testnet Explorer`,url:`https://testnet-explorer.lumia.org/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2235063}},testnet:!0}),tE=L({id:96370,name:`Lumoz`,nativeCurrency:{decimals:18,name:`Lumoz Token`,symbol:`MOZ`},rpcUrls:{default:{http:[`https://rpc.lumoz.org`]}},blockExplorers:{default:{name:`Lumoz Scan`,url:`https://scan.lumoz.info`}},testnet:!1}),nE=L({id:105363,name:`Lumoz Testnet`,nativeCurrency:{decimals:18,name:`Lumoz Testnet Token`,symbol:`MOZ`},rpcUrls:{default:{http:[`https://testnet-rpc.lumoz.org`]}},testnet:!0}),rE=L({id:721,name:`Lycan`,nativeCurrency:{decimals:18,name:`Lycan`,symbol:`LYC`},rpcUrls:{default:{http:[`https://rpc.lycanchain.com`,`https://us-east.lycanchain.com`,`https://us-west.lycanchain.com`,`https://eu-north.lycanchain.com`,`https://eu-west.lycanchain.com`,`https://asia-southeast.lycanchain.com`],webSocket:[`wss://rpc.lycanchain.com`,`wss://us-east.lycanchain.com`,`wss://us-west.lycanchain.com`,`wss://eu-north.lycanchain.com`,`wss://eu-west.lycanchain.com`,`wss://asia-southeast.lycanchain.com`]}},blockExplorers:{default:{name:`Lycan Explorer`,url:`https://explorer.lycanchain.com`}}}),iE=L({id:957,name:`Lyra Chain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.lyra.finance`]}},blockExplorers:{default:{name:`Lyra Explorer`,url:`https://explorer.lyra.finance`,apiUrl:`https://explorer.lyra.finance/api/v2`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1935198}}}),aE=L({id:1,name:`Ethereum`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:12e3,rpcUrls:{default:{http:[`https://eth.merkle.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://etherscan.io`,apiUrl:`https://api.etherscan.io/api`}},contracts:{ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:23085558},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),oE=L({id:595,name:`Mandala TC9`,network:`mandala`,nativeCurrency:{name:`Mandala`,symbol:`mACA`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-tc9.aca-staging.network`],webSocket:[`wss://eth-rpc-tc9.aca-staging.network`]}},blockExplorers:{default:{name:`Mandala Blockscout`,url:`https://blockscout.mandala.aca-staging.network`,apiUrl:`https://blockscout.mandala.aca-staging.network/api`}},testnet:!0}),sE=L({id:169,name:`Manta Pacific Mainnet`,network:`manta`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://pacific-rpc.manta.network/http`]}},blockExplorers:{default:{name:`Manta Explorer`,url:`https://pacific-explorer.manta.network`,apiUrl:`https://pacific-explorer.manta.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:332890}}}),cE=L({id:3441006,name:`Manta Pacific Sepolia Testnet`,network:`manta-sepolia`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://pacific-rpc.sepolia-testnet.manta.network/http`]}},blockExplorers:{default:{name:`Manta Sepolia Testnet Explorer`,url:`https://pacific-explorer.sepolia-testnet.manta.network`,apiUrl:`https://pacific-explorer.sepolia-testnet.manta.network/api`}},contracts:{multicall3:{address:`0xca54918f7B525C8df894668846506767412b53E3`,blockCreated:479584}},testnet:!0}),lE=L({id:3441005,name:`Manta Pacific Testnet`,network:`manta-testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://manta-testnet.calderachain.xyz/http`]}},blockExplorers:{default:{name:`Manta Testnet Explorer`,url:`https://pacific-explorer.testnet.manta.network`,apiUrl:`https://pacific-explorer.testnet.manta.network/api`}},contracts:{multicall3:{address:`0x211B1643b95Fe76f11eD8880EE810ABD9A4cf56C`,blockCreated:419915}},testnet:!0}),uE=L({id:5e3,name:`Mantle`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Explorer`,url:`https://mantlescan.xyz/`,apiUrl:`https://api.mantlescan.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:304717}}}),dE=L({id:5003,name:`Mantle Sepolia Testnet`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.sepolia.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Testnet Explorer`,url:`https://explorer.sepolia.mantle.xyz/`,apiUrl:`https://explorer.sepolia.mantle.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4584012}},testnet:!0}),fE=L({id:5001,name:`Mantle Testnet`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.testnet.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Testnet Explorer`,url:`https://explorer.testnet.mantle.xyz`,apiUrl:`https://explorer.testnet.mantle.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:561333}},testnet:!0}),pE=L({id:5887,name:`MANTRA DuKong EVM Testnet`,nativeCurrency:{decimals:18,name:`OM`,symbol:`OM`},rpcUrls:{default:{http:[`https://evm.dukong.mantrachain.io`]}},blockExplorers:{default:{name:`MANTRAScan`,url:`https://mantrascan.io/dukong`}},testnet:!0}),mE=L({id:5888,name:`MANTRA EVM`,nativeCurrency:{decimals:18,name:`OM`,symbol:`OM`},rpcUrls:{default:{http:[`https://evm.mantrachain.io`],webSocket:[`https://evm.mantrachain.io/ws`]}},blockExplorers:{default:{name:`MANTRA Scan`,url:`https://mantrascan.io/mainnet`}}}),hE=L({id:22776,name:`MAP Protocol`,nativeCurrency:{decimals:18,name:`MAPO`,symbol:`MAPO`},rpcUrls:{default:{http:[`https://rpc.maplabs.io`]}},blockExplorers:{default:{name:`MAPO Scan`,url:`https://maposcan.io`}},testnet:!1}),gE=L({id:698,name:`Matchain`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.matchain.io`]}},blockExplorers:{default:{name:`Matchain Scan`,url:`https://matchscan.io`}}}),_E=L({id:699,name:`Matchain Testnet`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.matchain.io`]}},blockExplorers:{default:{name:`Matchain Scan`,url:`https://testnet.matchscan.io`}},testnet:!0}),vE=L({id:29548,name:`MCH Verse`,nativeCurrency:{name:`Oasys`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.oasys.mycryptoheroes.net`]}},blockExplorers:{default:{name:`MCH Verse Explorer`,url:`https://explorer.oasys.mycryptoheroes.net`,apiUrl:`https://explorer.oasys.mycryptoheroes.net/api`}},testnet:!1}),yE=L({id:6342,blockTime:1e3,name:`MegaETH Testnet`,nativeCurrency:{name:`MegaETH Testnet Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://carrot.megaeth.com/rpc`],webSocket:[`wss://carrot.megaeth.com/ws`]}},blockExplorers:{default:{name:`MegaETH Testnet Explorer`,url:`https://www.megaexplorer.xyz/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0}),bE=L({id:7078815900,name:`Mekong Pectra Devnet`,nativeCurrency:{name:`eth`,symbol:`eth`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mekong.ethpandaops.io`]}},blockExplorers:{default:{name:`Block Explorer`,url:`https://explorer.mekong.ethpandaops.io`}},testnet:!0}),xE=L({id:333000333,name:`Meld`,nativeCurrency:{decimals:18,name:`Meld`,symbol:`MELD`},rpcUrls:{default:{http:[`https://rpc-1.meld.com`]}},blockExplorers:{default:{name:`MELDscan`,url:`https://meldscan.io`}},contracts:{multicall3:{address:`0x769ee5a8e82c15c1b6e358f62ac8eb6e3abe8dc5`,blockCreated:360069}}}),SE=L({id:4352,name:`MemeCore`,nativeCurrency:{decimals:18,name:`M`,symbol:`M`},rpcUrls:{default:{http:[`https://rpc.memecore.net`],webSocket:[`wss://ws.memecore.net`]}},blockExplorers:{default:{name:`MemeCore Explorer`,url:`https://memecorescan.io`,apiUrl:`https://api.memecorescan.io/api`},okx:{name:`MemeCore Explorer`,url:`https://web3.okx.com/explorer/memecore`},memecore:{name:`MemeCore Explorer`,url:`https://blockscout.memecore.com`,apiUrl:`https://blockscout.memecore.com/api`}}}),CE=L({id:43521,name:`Formicarium`,nativeCurrency:{decimals:18,name:`M`,symbol:`M`},rpcUrls:{default:{http:[`https://rpc.formicarium.memecore.net`],webSocket:[`wss://ws.formicarium.memecore.net`]}},blockExplorers:{default:{name:`MemeCore Testnet Explorer`,url:`https://formicarium.memecorescan.io`},okx:{name:`MemeCore Testnet Explorer`,url:`https://web3.okx.com/explorer/formicarium-testnet`},memecore:{name:`MemeCore Testnet Explorer`,url:`https://formicarium.blockscout.memecore.com`,apiUrl:`https://formicarium.blockscout.memecore.com/api`}},testnet:!0}),wE=L({id:4200,name:`Merlin`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.merlinchain.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://scan.merlinchain.io`,apiUrl:`https://scan.merlinchain.io/api`}}}),TE=L({id:4203,name:`Merlin Erigon Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-erigon-rpc.merlinchain.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet-erigon-scan.merlinchain.io`,apiUrl:`https://testnet-erigon-scan.merlinchain.io/api`}},testnet:!0}),EE=L({id:571,name:`MetaChain Mainnet`,nativeCurrency:{name:`Metatime Coin`,symbol:`MTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.metatime.com`]}},blockExplorers:{default:{name:`MetaExplorer`,url:`https://explorer.metatime.com`}},contracts:{multicall3:{address:`0x0000000000000000000000000000000000003001`,blockCreated:0}}}),DE=L({id:1453,name:`MetaChain Istanbul`,nativeCurrency:{name:`Metatime Coin`,symbol:`MTC`,decimals:18},rpcUrls:{default:{http:[`https://istanbul-rpc.metachain.dev`]}},blockExplorers:{default:{name:`MetaExplorer`,url:`https://istanbul-explorer.metachain.dev`}},contracts:{multicall3:{address:`0x0000000000000000000000000000000000003001`,blockCreated:0}},testnet:!0}),OE=L({id:11,name:`Metadium Network`,nativeCurrency:{decimals:18,name:`META`,symbol:`META`},rpcUrls:{default:{http:[`https://api.metadium.com/prod`]}},blockExplorers:{default:{name:`Metadium Explorer`,url:`https://explorer.metadium.com`}},testnet:!1});var kE=1;const AE=L({...R,id:1750,name:`Metal L2`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.metall2.com`],webSocket:[`wss://rpc.metall2.com`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.metall2.com`,apiUrl:`https://explorer.metall2.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[kE]:{address:`0x3B1F7aDa0Fcc26B13515af752Dd07fB1CAc11426`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},portal:{[kE]:{address:`0x3F37aBdE2C6b5B2ed6F8045787Df1ED1E3753956`}},l1StandardBridge:{[kE]:{address:`0x6d0f65D59b55B0FEC5d2d15365154DcADC140BF3`}}},sourceId:kE}),jE=L({id:82,name:`Meter`,nativeCurrency:{decimals:18,name:`MTR`,symbol:`MTR`},rpcUrls:{default:{http:[`https://rpc.meter.io`]}},blockExplorers:{default:{name:`MeterScan`,url:`https://scan.meter.io`}}}),ME=L({id:83,name:`Meter Testnet`,nativeCurrency:{decimals:18,name:`MTR`,symbol:`MTR`},rpcUrls:{default:{http:[`https://rpctest.meter.io`]}},blockExplorers:{default:{name:`MeterTestnetScan`,url:`https://scan-warringstakes.meter.io`}}}),NE=L({id:1088,name:`Metis`,nativeCurrency:{decimals:18,name:`Metis`,symbol:`METIS`},rpcUrls:{default:{http:[`https://metis.rpc.hypersync.xyz`,`https://metis-pokt.nodies.app`,`https://api.blockeden.xyz/metis/67nCBdZQSH9z3YqDDjdm`,`https://metis-andromeda.rpc.thirdweb.com`,`https://metis-andromeda.gateway.tenderly.co`,`https://metis.api.onfinality.io/public`,`https://andromeda.metis.io/?owner=1088`,`https://metis-mainnet.public.blastapi.io`],webSocket:[`wss://metis-rpc.publicnode.com`,`wss://metis.drpc.org`]}},blockExplorers:{default:{name:`Metis Explorer`,url:`https://explorer.metis.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/1088/etherscan/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2338552}}}),PE=L({id:599,name:`Metis Goerli`,nativeCurrency:{decimals:18,name:`Metis Goerli`,symbol:`METIS`},rpcUrls:{default:{http:[`https://goerli.gateway.metisdevops.link`]}},blockExplorers:{default:{name:`Metis Goerli Explorer`,url:`https://goerli.explorer.metisdevops.link`,apiUrl:`https://goerli.explorer.metisdevops.link/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1006207}}}),FE=L({id:59902,name:`Metis Sepolia`,nativeCurrency:{decimals:18,name:`Test Metis`,symbol:`tMETIS`},rpcUrls:{default:{http:[`https://sepolia.metisdevops.link`,`https://metis-sepolia-rpc.publicnode.com`,`https://metis-sepolia.gateway.tenderly.co`],webSocket:[`wss://metis-sepolia-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Metis Sepolia Explorer`,url:`https://sepolia-explorer.metisdevops.link`,apiUrl:`https://sepolia-explorer.metisdevops.link/api-docs`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:224185}}}),IE=L({id:7518,name:`MEVerse Chain Mainnet`,nativeCurrency:{decimals:18,name:`MEVerse`,symbol:`MEV`},rpcUrls:{default:{http:[`https://rpc.meversemainnet.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://www.meversescan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:86881340}}}),LE=L({id:4759,name:`MEVerse Chain Testnet`,nativeCurrency:{decimals:18,name:`MEVerse`,symbol:`MEV`},rpcUrls:{default:{http:[`https://rpc.meversetestnet.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://testnet.meversescan.io/`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:64371115}},testnet:!0}),RE=L({id:185,name:`Mint Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mintchain.io`]}},blockExplorers:{default:{name:`Mintchain explorer`,url:`https://explorer.mintchain.io`}},testnet:!1}),zE=L({id:1686,name:`Mint Sepolia Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.mintchain.io`]}},blockExplorers:{default:{name:`Mintchain Testnet explorer`,url:`https://testnet-explorer.mintchain.io`}},testnet:!0}),BE=L({id:124832,name:`Mitosis Testnet`,nativeCurrency:{name:`MITO`,symbol:`MITO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.mitosis.org`]}},blockExplorers:{default:{name:`Mitosis testnet explorer`,url:`https://testnet.mitosiscan.xyz`}},testnet:!0});var VE=1;const HE=L({...R,id:34443,name:`Mode Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.mode.network`]}},blockExplorers:{default:{name:`Modescan`,url:`https://modescan.io`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2465882},l2OutputOracle:{[VE]:{address:`0x4317ba146D4933D889518a3e5E11Fe7a53199b04`}},portal:{[VE]:{address:`0x8B34b14c7c7123459Cf3076b8Cb929BE097d0C07`}},l1StandardBridge:{[VE]:{address:`0x735aDBbE72226BD52e818E7181953f42E3b0FF21`}}},sourceId:VE});var UE=11155111;const WE=L({...R,id:919,name:`Mode Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.mode.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia.explorer.mode.network`,apiUrl:`https://sepolia.explorer.mode.network/api`}},contracts:{...R.contracts,l2OutputOracle:{[UE]:{address:`0x2634BD65ba27AB63811c74A63118ACb312701Bfa`,blockCreated:3778393}},portal:{[UE]:{address:`0x320e1580effF37E008F1C92700d1eBa47c1B23fD`,blockCreated:3778395}},l1StandardBridge:{[UE]:{address:`0xbC5C679879B2965296756CD959C3C739769995E2`,blockCreated:3778392}},multicall3:{address:`0xBAba8373113Fb7a68f195deF18732e01aF8eDfCF`,blockCreated:3019007}},testnet:!0,sourceId:UE}),GE=L({id:10143,name:`Monad Testnet`,blockTime:400,nativeCurrency:{name:`Testnet MON Token`,symbol:`MON`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.monad.xyz`]}},blockExplorers:{default:{name:`Monad Testnet explorer`,url:`https://testnet.monadexplorer.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:251449}},testnet:!0}),KE=L({id:1287,name:`Moonbase Alpha`,nativeCurrency:{decimals:18,name:`DEV`,symbol:`DEV`},rpcUrls:{default:{http:[`https://rpc.api.moonbase.moonbeam.network`],webSocket:[`wss://wss.api.moonbase.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonbase.moonscan.io`,apiUrl:`https://moonbase.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1850686}},testnet:!0}),qE=L({id:1284,name:`Moonbeam`,nativeCurrency:{decimals:18,name:`GLMR`,symbol:`GLMR`},rpcUrls:{default:{http:[`https://moonbeam.public.blastapi.io`],webSocket:[`wss://moonbeam.public.blastapi.io`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonscan.io`,apiUrl:`https://api-moonbeam.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:609002}},testnet:!1}),JE=L({id:1281,name:`Moonbeam Development Node`,nativeCurrency:{decimals:18,name:`DEV`,symbol:`DEV`},rpcUrls:{default:{http:[`http://127.0.0.1:9944`],webSocket:[`wss://127.0.0.1:9944`]}}}),YE=L({id:1285,name:`Moonriver`,nativeCurrency:{decimals:18,name:`MOVR`,symbol:`MOVR`},rpcUrls:{default:{http:[`https://moonriver.public.blastapi.io`],webSocket:[`wss://moonriver.public.blastapi.io`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonriver.moonscan.io`,apiUrl:`https://api-moonriver.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1597904}},testnet:!1}),XE=L({id:2818,name:`Morph`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.morphl2.io`],webSocket:[`wss://rpc.morphl2.io:8443`]}},blockExplorers:{default:{name:`Morph Explorer`,url:`https://explorer.morphl2.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3654913}},testnet:!1}),ZE=L({id:2810,name:`Morph Holesky`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-quicknode-holesky.morphl2.io`],webSocket:[`wss://rpc-quicknode-holesky.morphl2.io`]}},blockExplorers:{default:{name:`Morph Holesky Explorer`,url:`https://explorer-holesky.morphl2.io`,apiUrl:`https://explorer-api-holesky.morphl2.io/api?`}},testnet:!0}),QE=L({id:2710,name:`Morph Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.morphl2.io`]}},blockExplorers:{default:{name:`Morph Testnet Explorer`,url:`https://explorer-testnet.morphl2.io`,apiUrl:`https://explorer-api-testnet.morphl2.io/api`}},testnet:!0}),$E=L({id:5551,name:`Nahmii 2 Mainnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://l2.nahmii.io`]}},blockExplorers:{default:{name:`Nahmii 2 Explorer`,url:`https://explorer.n2.nahmii.io`}},testnet:!1}),eD=L({id:22222,name:`Nautilus Mainnet`,nativeCurrency:{name:`ZBC`,symbol:`ZBC`,decimals:9},rpcUrls:{default:{http:[`https://api.nautilus.nautchain.xyz`]}},blockExplorers:{default:{name:`NautScan`,url:`https://nautscan.com`}}}),tD=L({id:397,name:`NEAR Protocol`,nativeCurrency:{decimals:18,name:`NEAR`,symbol:`NEAR`},rpcUrls:{default:{http:[`https://eth-rpc.mainnet.near.org`]}},blockExplorers:{default:{name:`NEAR Explorer`,url:`https://eth-explorer.near.org`}},testnet:!1}),nD=L({id:398,name:`NEAR Protocol Testnet`,nativeCurrency:{decimals:18,name:`NEAR`,symbol:`NEAR`},rpcUrls:{default:{http:[`https://eth-rpc.testnet.near.org`]}},blockExplorers:{default:{name:`NEAR Explorer`,url:`https://eth-explorer-testnet.near.org`}},testnet:!0}),rD=L({id:245022926,name:`Neon EVM DevNet`,nativeCurrency:{name:`NEON`,symbol:`NEON`,decimals:18},rpcUrls:{default:{http:[`https://devnet.neonevm.org`]}},blockExplorers:{default:{name:`Neonscan`,url:`https://devnet.neonscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:205206112}},testnet:!0}),iD=L({id:245022934,network:`neonMainnet`,name:`Neon EVM MainNet`,nativeCurrency:{name:`NEON`,symbol:`NEON`,decimals:18},rpcUrls:{default:{http:[`https://neon-proxy-mainnet.solana.p2p.org`]}},blockExplorers:{default:{name:`Neonscan`,url:`https://neonscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:206545524}},testnet:!1}),aD=L({id:47763,name:`Neo X Mainnet`,nativeCurrency:{name:`Gas`,symbol:`GAS`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-1.rpc.banelabs.org`,`https://mainnet-2.rpc.banelabs.org`]}},blockExplorers:{default:{name:`Neo X - Explorer`,url:`https://xexplorer.neo.org`}},testnet:!1}),oD=L({id:12227332,name:`Neo X Testnet T4`,nativeCurrency:{name:`Gas`,symbol:`GAS`,decimals:18},rpcUrls:{default:{http:[`https://testnet.rpc.banelabs.org/`]}},blockExplorers:{default:{name:`neox-scan`,url:`https://xt4scan.ngd.network`}},testnet:!0}),sD=L({id:1012,name:`Newton`,nativeCurrency:{name:`Newton`,symbol:`NEW`,decimals:18},rpcUrls:{default:{http:[`https://global.rpc.mainnet.newtonproject.org`]}},blockExplorers:{default:{name:`NewFi explorer`,url:`https://explorer.newtonproject.org/`}},testnet:!1}),cD=L({id:4242,name:`Nexi`,nativeCurrency:{name:`Nexi`,symbol:`NEXI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.chain.nexi.technology`]}},blockExplorers:{default:{name:`NexiScan`,url:`https://www.nexiscan.com`,apiUrl:`https://www.nexiscan.com/api`}},contracts:{multicall3:{address:`0x0277A46Cc69A57eE3A6C8c158bA874832F718B8E`,blockCreated:25770160}}}),lD=L({id:240,name:`Nexilix Smart Chain`,nativeCurrency:{decimals:18,name:`Nexilix`,symbol:`NEXILIX`},rpcUrls:{default:{http:[`https://rpcurl.pos.nexilix.com`]}},blockExplorers:{default:{name:`NexilixScan`,url:`https://scan.nexilix.com`}},contracts:{multicall3:{address:`0x58381c8e2BF9d0C2C4259cA14BdA9Afe02831244`,blockCreated:74448}}}),uD=L({id:6900,name:`Nibiru`,nativeCurrency:{decimals:18,name:`NIBI`,symbol:`NIBI`},rpcUrls:{default:{http:[`https://evm-rpc.nibiru.fi`]}},blockExplorers:{default:{name:`NibiScan`,url:`https://nibiscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:19587573}}}),dD=L({id:200024,name:`Nitrograph Testnet`,testnet:!0,rpcUrls:{default:{http:[`https://rpc-testnet.nitrograph.foundation`]}},nativeCurrency:{name:`Nitro`,symbol:`NOS`,decimals:18},blockExplorers:{default:{url:`https://explorer-testnet.nitrograph.foundation`,name:`Nitrograph Explorer`}}}),fD=L({id:4090,network:`oasis-testnet`,name:`Oasis Testnet`,nativeCurrency:{name:`Fasttoken`,symbol:`FTN`,decimals:18},rpcUrls:{default:{http:[`https://rpc1.oasis.bahamutchain.com`]}},blockExplorers:{default:{name:`Ftnscan`,url:`https://oasis.ftnscan.com`,apiUrl:`https://oasis.ftnscan.com/api`}},testnet:!0}),pD=L({id:248,name:`Oasys`,nativeCurrency:{name:`Oasys`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.oasys.games`]}},blockExplorers:{default:{name:`OasysScan`,url:`https://scan.oasys.games`,apiUrl:`https://scan.oasys.games/api`}}}),mD=L({id:911867,name:`Odyssey Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://odyssey.ithaca.xyz`]}},blockExplorers:{default:{name:`Odyssey Explorer`,url:`https://odyssey-explorer.ithaca.xyz`,apiUrl:`https://odyssey-explorer.ithaca.xyz/api`}},testnet:!0}),hD=L({id:66,name:`OKC`,nativeCurrency:{decimals:18,name:`OKT`,symbol:`OKT`},rpcUrls:{default:{http:[`https://exchainrpc.okex.org`]}},blockExplorers:{default:{name:`oklink`,url:`https://www.oklink.com/okc`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10364792}}}),gD=L({id:311,name:`Omax Mainnet`,nativeCurrency:{decimals:18,name:`OMAX`,symbol:`OMAX`},rpcUrls:{default:{http:[`https://mainapi.omaxray.com`]}},blockExplorers:{default:{name:`Omax Explorer`,url:`https://omaxscan.com`}},testnet:!1}),_D=L({id:166,name:`Omni`,nativeCurrency:{name:`Omni`,symbol:`OMNI`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.omni.network`],webSocket:[`wss://mainnet.omni.network`]}},blockExplorers:{default:{name:`OmniScan`,url:`https://omniscan.network`}},testnet:!1}),vD=L({id:164,name:`Omni Omega`,nativeCurrency:{name:`Omni`,symbol:`OMNI`,decimals:18},rpcUrls:{default:{http:[`https://omega.omni.network`],webSocket:[`wss://omega.omni.network`]}},blockExplorers:{default:{name:`Omega OmniScan`,url:`https://omega.omniscan.network/`}},testnet:!0}),yD=L({id:309075,name:`One World Chain Mainnet`,nativeCurrency:{decimals:18,name:`OWCT`,symbol:`OWCT`},rpcUrls:{default:{http:[`https://mainnet-rpc.oneworldchain.org`]}},blockExplorers:{default:{name:`One World Explorer`,url:`https://mainnet.oneworldchain.org`}},testnet:!1}),bD=L({id:9700,name:`OORT MainnetDev`,nativeCurrency:{decimals:18,name:`OORT`,symbol:`OORT`},rpcUrls:{default:{http:[`https://dev-rpc.oortech.com`]}},blockExplorers:{default:{name:`OORT MainnetDev Explorer`,url:`https://dev-scan.oortech.com`}}});var xD=56;const SD=L({id:204,name:`opBNB`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://opbnb-mainnet-rpc.bnbchain.org`]}},blockExplorers:{default:{name:`opBNB (BSCScan)`,url:`https://opbnb.bscscan.com`,apiUrl:`https://api-opbnb.bscscan.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:512881},l2OutputOracle:{[xD]:{address:`0x153CAB79f4767E2ff862C94aa49573294B13D169`}},portal:{[xD]:{address:`0x1876EA7702C0ad0C6A2ae6036DE7733edfBca519`}},l1StandardBridge:{[xD]:{address:`0xF05F0e4362859c3331Cb9395CBC201E3Fa6757Ea`}}},sourceId:xD});var CD=97;const wD=L({id:5611,name:`opBNB Testnet`,nativeCurrency:{decimals:18,name:`tBNB`,symbol:`tBNB`},rpcUrls:{default:{http:[`https://opbnb-testnet-rpc.bnbchain.org`]}},blockExplorers:{default:{name:`opbnbscan`,url:`https://testnet.opbnbscan.com`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3705108},l2OutputOracle:{[CD]:{address:`0xFf2394Bb843012562f4349C6632a0EcB92fC8810`}},portal:{[CD]:{address:`0x4386C8ABf2009aC0c263462Da568DD9d46e52a31`}},l1StandardBridge:{[CD]:{address:`0x677311Fd2cCc511Bbc0f581E8d9a07B033D5E840`}}},testnet:!0,sourceId:CD}),TD=L({id:1612,name:`OpenLedger`,nativeCurrency:{name:`Open`,symbol:`OPEN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.openledger.xyz`]}},blockExplorers:{default:{name:`OpenLedger Explorer`,url:`https://scan.openledger.xyz`}},testnet:!1});var ED=1;const DD=L({...R,id:10,name:`OP Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.optimism.io`]}},blockExplorers:{default:{name:`Optimism Explorer`,url:`https://optimistic.etherscan.io`,apiUrl:`https://api-optimistic.etherscan.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[ED]:{address:`0xe5965Ab5962eDc7477C8520243A95517CD252fA9`}},l2OutputOracle:{[ED]:{address:`0xdfe97868233d1aa22e815a266982f2cf17685a27`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4286263},portal:{[ED]:{address:`0xbEb5Fc579115071764c7423A4f12eDde41f106Ed`}},l1StandardBridge:{[ED]:{address:`0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1`}}},sourceId:ED});var OD=5;const kD=L({...R,id:420,name:`Optimism Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli.optimism.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli-optimism.etherscan.io`,apiUrl:`https://goerli-optimism.etherscan.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[OD]:{address:`0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:49461},portal:{[OD]:{address:`0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383`}},l1StandardBridge:{[OD]:{address:`0x636Af16bf2f682dD3109e60102b8E1A089FedAa8`}}},testnet:!0,sourceId:OD});var AD=11155111;const jD=L({...R,id:11155420,name:`OP Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.optimism.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://optimism-sepolia.blockscout.com`,apiUrl:`https://optimism-sepolia.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[AD]:{address:`0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1`}},l2OutputOracle:{[AD]:{address:`0x90E9c4f8a994a250F6aEfd61CAFb4F2e895D458F`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1620204},portal:{[AD]:{address:`0x16Fc5058F25648194471939df75CF27A2fdC48BC`}},l1StandardBridge:{[AD]:{address:`0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1`}}},testnet:!0,sourceId:AD}),MD=L({id:62050,name:`Optopia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.optopia.ai`]}},blockExplorers:{default:{name:`Optopia Explorer`,url:`https://scan.optopia.ai`}},testnet:!1}),ND=L({id:62049,name:`Optopia Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.optopia.ai`]}},blockExplorers:{default:{name:`Optopia Explorer`,url:`https://scan-testnet.optopia.ai`}},testnet:!0}),PD=L({id:291,name:`Orderly`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.orderly.network`]}},blockExplorers:{default:{name:`Orderly Explorer`,url:`https://explorer.orderly.network`}},testnet:!1}),FD=L({id:4460,name:`Orderly Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://l2-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz`]}},blockExplorers:{default:{name:`Orderly Explorer`,url:`https://explorerl2new-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz`}},testnet:!0}),ID=L({id:41144114,name:`Otim Devnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`http://devnet.otim.xyz`]}},contracts:{batchInvoker:{address:`0x5FbDB2315678afecb367f032d93F642f64180aa3`}}}),LD=L({id:11297108109,name:`Palm`,nativeCurrency:{decimals:18,name:`PALM`,symbol:`PALM`},rpcUrls:{default:{http:[`https://palm-mainnet.public.blastapi.io`],webSocket:[`wss://palm-mainnet.public.blastapi.io`]}},blockExplorers:{default:{name:`Chainlens`,url:`https://palm.chainlens.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15429248}}}),RD=L({id:11297108099,name:`Palm Testnet`,nativeCurrency:{decimals:18,name:`PALM`,symbol:`PALM`},rpcUrls:{default:{http:[`https://palm-mainnet.public.blastapi.io`],webSocket:[`wss://palm-mainnet.public.blastapi.io`]}},blockExplorers:{default:{name:`Chainlens`,url:`https://palm.chainlens.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15429248}},testnet:!0}),zD=L({id:3338,name:`Peaq`,nativeCurrency:{decimals:18,name:`peaq`,symbol:`PEAQ`},rpcUrls:{default:{http:[`https://quicknode1.peaq.xyz`,`https://quicknode2.peaq.xyz`,`https://quicknode3.peaq.xyz`],webSocket:[`wss://quicknode1.peaq.xyz`,`wss://quicknode2.peaq.xyz`,`wss://quicknode3.peaq.xyz`]}},blockExplorers:{default:{name:`Subscan`,url:`https://peaq.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3566354}}});var BD=1;const VD=L({id:424,network:`pgn`,name:`PGN`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.publicgoods.network`]}},blockExplorers:{default:{name:`PGN Explorer`,url:`https://explorer.publicgoods.network`,apiUrl:`https://explorer.publicgoods.network/api`}},contracts:{l2OutputOracle:{[BD]:{address:`0x9E6204F750cD866b299594e2aC9eA824E2e5f95c`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3380209},portal:{[BD]:{address:`0xb26Fd985c5959bBB382BAFdD0b879E149e48116c`}},l1StandardBridge:{[BD]:{address:`0xD0204B9527C1bA7bD765Fa5CCD9355d38338272b`}}},formatters:yy,sourceId:BD});var HD=11155111;const UD=L({id:58008,network:`pgn-testnet`,name:`PGN `,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.publicgoods.network`]}},blockExplorers:{default:{name:`PGN Testnet Explorer`,url:`https://explorer.sepolia.publicgoods.network`,apiUrl:`https://explorer.sepolia.publicgoods.network/api`}},contracts:{l2OutputOracle:{[HD]:{address:`0xD5bAc3152ffC25318F848B3DD5dA6C85171BaEEe`}},portal:{[HD]:{address:`0xF04BdD5353Bb0EFF6CA60CfcC78594278eBfE179`}},l1StandardBridge:{[HD]:{address:`0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3754925}},formatters:yy,sourceId:HD,testnet:!0}),WD=L({id:13381,name:`Phoenix Blockchain`,nativeCurrency:{name:`Phoenix`,symbol:`PHX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.phoenixplorer.com`]}},blockExplorers:{default:{name:`Phoenixplorer`,url:`https://phoenixplorer.com`,apiUrl:`https://phoenixplorer.com/api`}},contracts:{multicall3:{address:`0x498cF757a575cFF2c2Ed9f532f56Efa797f86442`,blockCreated:5620192}}}),GD=L({id:7070,name:`Planq Mainnet`,nativeCurrency:{decimals:18,name:`PLQ`,symbol:`PLQ`},rpcUrls:{default:{http:[`https://planq-rpc.nodies.app`,`https://evm-rpc.planq.network`,`https://jsonrpc.planq.nodestake.top`]}},blockExplorers:{default:{name:`Planq Explorer`,url:`https://evm.planq.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:8470015}},testnet:!1}),KD=L({id:9745,name:`Plasma`,blockTime:1e3,nativeCurrency:{name:`Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plasma.to`]}},blockExplorers:{default:{name:`PlasmaScan`,url:`https://plasmascan.to`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),qD=L({id:9747,name:`Plasma Devnet`,nativeCurrency:{name:`Devnet Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://devnet-rpc.plasma.to`]}},testnet:!0,contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),JD=L({id:9746,name:`Plasma Testnet`,nativeCurrency:{name:`Testnet Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plasma.to`]}},blockExplorers:{default:{name:`RouteScan`,url:`https://testnet.plasmascan.to`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),YD=L({...oy,id:1612127,name:`PlayFi Albireo Testnet`,network:`albireo`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://albireo-rpc.playfi.ai`],webSocket:[`wss://albireo-rpc-ws.playfi.ai/ws`]}},blockExplorers:{default:{name:`PlayFi Albireo Explorer`,url:`https://albireo-explorer.playfi.ai`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`}},testnet:!0}),XD=L({id:242,name:`Plinga`,nativeCurrency:{name:`Plinga`,symbol:`PLINGA`,decimals:18},rpcUrls:{default:{http:[`https://rpcurl.mainnet.plgchain.com`]}},blockExplorers:{default:{name:`Plgscan`,url:`https://www.plgscan.com`}},contracts:{multicall3:{address:`0x0989576160f2e7092908BB9479631b901060b6e4`,blockCreated:204489}}}),ZD=L({id:98865,name:`Plume (Legacy)`,nativeCurrency:{name:`Plume Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plumenetwork.xyz`],webSocket:[`wss://rpc.plumenetwork.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.plumenetwork.xyz`,apiUrl:`https://explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:48577}},sourceId:1}),QD=L({id:98864,name:`Plume Devnet (Legacy)`,nativeCurrency:{name:`Plume Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://test-rpc.plumenetwork.xyz`],webSocket:[`wss://test-rpc.plumenetwork.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://test-explorer.plumenetwork.xyz`,apiUrl:`https://test-explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:481948}},testnet:!0,sourceId:11155111}),$D=L({id:98866,name:`Plume`,nativeCurrency:{name:`Plume`,symbol:`PLUME`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plume.org`],webSocket:[`wss://rpc.plume.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.plume.org`,apiUrl:`https://explorer.plume.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:39679}},sourceId:1}),eO=L({id:98867,name:`Plume Testnet`,nativeCurrency:{name:`Plume`,symbol:`PLUME`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plume.org`],webSocket:[`wss://testnet-rpc.plume.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.plume.org`,apiUrl:`https://testnet-explorer.plume.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:199712}},testnet:!0,sourceId:11155111}),tO=L({id:161221135,name:`Plume Testnet (Legacy)`,nativeCurrency:{name:`Plume Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plumenetwork.xyz/http`],webSocket:[`wss://testnet-rpc.plumenetwork.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.plumenetwork.xyz`,apiUrl:`https://testnet-explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6022332}},testnet:!0,sourceId:11155111}),nO=L({id:631571,name:`Polter Testnet`,nativeCurrency:{decimals:18,name:`Polter GHST`,symbol:`GHST`},rpcUrls:{default:{http:[`https://geist-polter.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://polter-testnet.explorer.alchemy.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11245}},testnet:!0}),rO=L({id:137,name:`Polygon`,blockTime:2e3,nativeCurrency:{name:`POL`,symbol:`POL`,decimals:18},rpcUrls:{default:{http:[`https://polygon-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://polygonscan.com`,apiUrl:`https://api.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:25770160}}}),iO=L({id:80002,name:`Polygon Amoy`,nativeCurrency:{name:`POL`,symbol:`POL`,decimals:18},rpcUrls:{default:{http:[`https://rpc-amoy.polygon.technology`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://amoy.polygonscan.com`,apiUrl:`https://api-amoy.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3127388}},testnet:!0}),aO=L({id:80001,name:`Polygon Mumbai`,nativeCurrency:{name:`MATIC`,symbol:`MATIC`,decimals:18},rpcUrls:{default:{http:[`https://80001.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://mumbai.polygonscan.com`,apiUrl:`https://api-testnet.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:25770160}},testnet:!0}),oO=L({id:1101,name:`Polygon zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://zkevm-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://zkevm.polygonscan.com`,apiUrl:`https://api-zkevm.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:57746}}}),sO=L({id:2442,name:`Polygon zkEVM Cardona`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cardona.zkevm-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://cardona-zkevm.polygonscan.com`,apiUrl:`https://cardona-zkevm.polygonscan.com/api`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:114091}}}),cO=L({id:1442,name:`Polygon zkEVM Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.public.zkevm-test.net`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://testnet-zkevm.polygonscan.com`,apiUrl:`https://testnet-zkevm.polygonscan.com/api`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:525686}}}),lO=L({id:8008,name:`Polynomial`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.polynomial.fi`]}},blockExplorers:{default:{name:`Polynomial Scan`,url:`https://polynomialscan.io`}},testnet:!1,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),uO=L({id:80008,name:`Polynomia Sepolia`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.sepolia.polynomial.fi`]}},blockExplorers:{default:{name:`Polynomial Scan`,url:`https://sepolia.polynomialscan.io`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),dO=L({id:23023,name:`PremiumBlock Testnet`,nativeCurrency:{name:`Premium Block`,symbol:`PBLK`,decimals:18},rpcUrls:{default:{http:[`https://rpc.premiumblock.org`]}},blockExplorers:{default:{name:`PremiumBlocks Explorer`,url:`https://scan.premiumblock.org`}},testnet:!0}),fO=L({id:369,name:`PulseChain`,nativeCurrency:{name:`Pulse`,symbol:`PLS`,decimals:18},testnet:!1,blockTime:1e4,rpcUrls:{default:{http:[`https://rpc.pulsechain.com`],webSocket:[`wss://ws.pulsechain.com`]}},blockExplorers:{default:{name:`PulseScan`,url:`https://ipfs.scan.pulsechain.com`,apiUrl:`https://api.scan.pulsechain.com/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),pO=L({id:943,name:`PulseChain V4`,testnet:!0,nativeCurrency:{name:`V4 Pulse`,symbol:`v4PLS`,decimals:18},blockTime:1e4,rpcUrls:{default:{http:[`https://rpc.v4.testnet.pulsechain.com`],webSocket:[`wss://ws.v4.testnet.pulsechain.com`]}},blockExplorers:{default:{name:`PulseScan`,url:`https://scan.v4.testnet.pulsechain.com`,apiUrl:`https://scan.v4.testnet.pulsechain.com/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),mO=L({id:490092,name:`Pumpfi Testnet`,nativeCurrency:{decimals:18,name:`PMPT`,symbol:`PMPT`},rpcUrls:{default:{http:[`https://rpc1testnet.pumpfi.me`]}},blockExplorers:{default:{name:`Pumpfi Testnet Scan`,url:`https://testnetscan.pumpfi.me`}},testnet:!0});var hO=11155111;const gO=L({...R,name:`Pyrope Testnet`,testnet:!0,id:695569,sourceId:hO,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.pyropechain.com`],webSocket:[`wss://rpc.pyropechain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://pyrope.blockscout.com`}},contracts:{...R.contracts,l1StandardBridge:{[hO]:{address:`0xC24932c31D9621aE9e792576152B7ef010cFC2F8`}}}}),_O=L({id:766,name:`QL1`,nativeCurrency:{decimals:18,name:`QOM`,symbol:`QOM`},rpcUrls:{default:{http:[`https://rpc.qom.one`]}},blockExplorers:{default:{name:`Ql1 Explorer`,url:`https://scan.qom.one`}},contracts:{multicall3:{address:`0x7A52370716ea730585884F5BDB0f6E60C39b8C64`}},testnet:!1}),vO=L({id:35441,name:`Q Mainnet`,nativeCurrency:{decimals:18,name:`Q`,symbol:`Q`},rpcUrls:{default:{http:[`https://rpc.q.org`]}},blockExplorers:{default:{name:`Q Mainnet Explorer`,url:`https://explorer.q.org`,apiUrl:`https://explorer.q.org/api`}}}),yO=L({id:35443,name:`Q Testnet`,nativeCurrency:{decimals:18,name:`Q`,symbol:`Q`},rpcUrls:{default:{http:[`https://rpc.qtestnet.org`]}},blockExplorers:{default:{name:`Q Testnet Explorer`,url:`https://explorer.qtestnet.org`,apiUrl:`https://explorer.qtestnet.org/api`}},testnet:!0}),bO=L({id:111188,name:`re.al`,nativeCurrency:{name:`reETH`,decimals:18,symbol:`reETH`},rpcUrls:{default:{http:[`https://rpc.realforreal.gelato.digital`]}},blockExplorers:{default:{name:`re.al Explorer`,url:`https://explorer.re.al`,apiUrl:`https://explorer.re.al/api/v2`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:695}}}),xO=L({id:151,name:`Redbelly Network Mainnet`,nativeCurrency:{name:`Redbelly Native Coin`,symbol:`RBNT`,decimals:18},rpcUrls:{default:{http:[`https://governors.mainnet.redbelly.network`]}},blockExplorers:{default:{name:`Routescan`,url:`https://redbelly.routescan.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/151/etherscan/api`}},testnet:!1}),SO=L({id:153,name:`Redbelly Network Testnet`,nativeCurrency:{name:`Redbelly Native Coin`,symbol:`RBNT`,decimals:18},rpcUrls:{default:{http:[`https://governors.testnet.redbelly.network`]}},blockExplorers:{default:{name:`Routescan`,url:`https://redbelly.testnet.routescan.io`,apiUrl:`https://api.routescan.io/v2/network/testnet/evm/153_2/etherscan/api`}},testnet:!0}),CO=L({id:50342,name:`Reddio`,nativeCurrency:{name:`Reddio`,symbol:`RED`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.reddio.com/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://reddio.cloud.blockscout.com`,apiUrl:`https://reddio.cloud.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:848849}},testnet:!1}),wO=L({id:50341,name:`Reddio Sepolia`,nativeCurrency:{name:`Reddio`,symbol:`RED`,decimals:18},rpcUrls:{default:{http:[`https://reddio-dev.reddio.com`]}},blockExplorers:{default:{name:`Reddioscan`,url:`https://reddio-devnet.l2scan.co`,apiUrl:`https://reddio-devnet.l2scan.co/api`}},testnet:!0});var TO=1;const EO=L({...R,name:`Redstone`,id:690,sourceId:TO,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.redstonechain.com`],webSocket:[`wss://rpc.redstonechain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.redstone.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[TO]:{address:`0xC7bCb0e8839a28A1cFadd1CF716de9016CdA51ae`,blockCreated:19578329}},l2OutputOracle:{[TO]:{address:`0xa426A052f657AEEefc298b3B5c35a470e4739d69`,blockCreated:19578337}},l1StandardBridge:{[TO]:{address:`0xc473ca7E02af24c129c2eEf51F2aDf0411c1Df69`,blockCreated:19578331}}}}),DO=L({id:47805,name:`REI Mainnet`,nativeCurrency:{decimals:18,name:`REI`,symbol:`REI`},rpcUrls:{default:{http:[`https://rpc.rei.network`],webSocket:[`wss://rpc.rei.network`]}},blockExplorers:{default:{name:`REI Scan`,url:`https://scan.rei.network`}},testnet:!1}),OO=L({id:1729,name:`Reya Network`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.reya.network`],webSocket:[`wss://ws.reya.network`]}},blockExplorers:{default:{name:`Reya Network Explorer`,url:`https://explorer.reya.network`}},testnet:!1}),kO=L({id:11155931,name:`RISE Testnet`,nativeCurrency:{name:`RISE Testnet Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.riselabs.xyz`],webSocket:[`wss://testnet.riselabs.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.testnet.riselabs.xyz/`,apiUrl:`https://explorer.testnet.riselabs.xyz/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},testnet:!0}),AO=L({id:753,name:`Rivalz`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rivalz.calderachain.xyz/http`]}},blockExplorers:{default:{name:`Rivalz Caldera Explorer`,url:`https://rivalz.calderaexplorer.xyz`}},testnet:!1}),jO=L({id:570,name:`Rollux Mainnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.rollux.com`],webSocket:[`wss://rpc.rollux.com/wss`]}},blockExplorers:{default:{name:`RolluxExplorer`,url:`https://explorer.rollux.com`,apiUrl:`https://explorer.rollux.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:119222}}}),MO=L({id:57e3,name:`Rollux Testnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc-tanenbaum.rollux.com/`],webSocket:[`wss://rpc-tanenbaum.rollux.com/wss`]}},blockExplorers:{default:{name:`RolluxTestnetExplorer`,url:`https://rollux.tanenbaum.io`,apiUrl:`https://rollux.tanenbaum.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1813675}}}),NO=L({id:2020,name:`Ronin`,nativeCurrency:{name:`RON`,symbol:`RON`,decimals:18},rpcUrls:{default:{http:[`https://api.roninchain.com/rpc`]}},blockExplorers:{default:{name:`Ronin Explorer`,url:`https://app.roninchain.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:26023535}}}),PO=L({id:7668,name:`The Root Network`,nativeCurrency:{decimals:18,name:`XRP`,symbol:`XRP`},rpcUrls:{default:{http:[`https://root.rootnet.live/archive`],webSocket:[`wss://root.rootnet.live/archive/ws`]}},blockExplorers:{default:{name:`Rootscan`,url:`https://rootscan.io`}},contracts:{multicall3:{address:`0xc9C2E2429AeC354916c476B30d729deDdC94988d`,blockCreated:9218338}}}),FO=L({id:7672,name:`The Root Network - Porcini`,nativeCurrency:{decimals:18,name:`XRP`,symbol:`XRP`},rpcUrls:{default:{http:[`https://porcini.rootnet.app/archive`],webSocket:[`wss://porcini.rootnet.app/archive/ws`]}},blockExplorers:{default:{name:`Rootscan`,url:`https://porcini.rootscan.io`}},contracts:{multicall3:{address:`0xc9C2E2429AeC354916c476B30d729deDdC94988d`,blockCreated:10555692}},testnet:!0}),IO=L({id:30,name:`Rootstock Mainnet`,network:`rootstock`,nativeCurrency:{decimals:18,name:`Rootstock Bitcoin`,symbol:`RBTC`},rpcUrls:{default:{http:[`https://public-node.rsk.co`]}},blockExplorers:{default:{name:`RSK Explorer`,url:`https://explorer.rsk.co`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4249540}}}),LO=L({id:31,name:`Rootstock Testnet`,network:`rootstock`,nativeCurrency:{decimals:18,name:`Rootstock Bitcoin`,symbol:`tRBTC`},rpcUrls:{default:{http:[`https://public-node.testnet.rsk.co`]}},blockExplorers:{default:{name:`RSK Explorer`,url:`https://explorer.testnet.rootstock.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2771150}},testnet:!0});var RO=1;const zO=L({...R,id:12553,name:`RSS3 VSL Mainnet`,nativeCurrency:{name:`RSS3`,symbol:`RSS3`,decimals:18},rpcUrls:{default:{http:[`https://rpc.rss3.io`]}},blockExplorers:{default:{name:`RSS3 VSL Mainnet Scan`,url:`https://scan.rss3.io`,apiUrl:`https://scan.rss3.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[RO]:{address:`0xE6f24d2C32B3109B18ed33cF08eFb490b1e09C10`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14193},portal:{[RO]:{address:`0x6A12432491bbbE8d3babf75F759766774C778Db4`,blockCreated:19387057}},l1StandardBridge:{[RO]:{address:`0x4cbab69108Aa72151EDa5A3c164eA86845f18438`}}},sourceId:RO});var BO=11155111;const VO=L({...R,id:2331,name:`RSS3 VSL Sepolia Testnet`,nativeCurrency:{name:`RSS3`,symbol:`RSS3`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.rss3.io`]}},blockExplorers:{default:{name:`RSS3 VSL Sepolia Testnet Scan`,url:`https://scan.testnet.rss3.io`,apiUrl:`https://scan.testnet.rss3.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[BO]:{address:`0xDb5c46C3Eaa6Ed6aE8b2379785DF7dd029C0dC81`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:55697},portal:{[BO]:{address:`0xcBD77E8E1E7F06B25baDe67142cdE82652Da7b57`,blockCreated:5345035}},l1StandardBridge:{[BO]:{address:`0xdDD29bb63B0839FB1cE0eE439Ff027738595D07B`}}},testnet:!0,sourceId:BO}),HO=L({id:7225878,name:`Saakuru Mainnet`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.saakuru.network`]}},blockExplorers:{default:{name:`Saakuru Explorer`,url:`https://explorer.saakuru.network`}},testnet:!1}),UO=L({id:5464,name:`Saga`,network:`saga`,nativeCurrency:{decimals:18,name:`gas`,symbol:`GAS`},rpcUrls:{default:{http:[`https://sagaevm.jsonrpc.sagarpc.io`]}},blockExplorers:{default:{name:`Saga Explorer`,url:`https://sagaevm.sagaexplorer.io`}},contracts:{multicall3:{address:`0x864DDc9B50B9A0dF676d826c9B9EDe9F8913a160`,blockCreated:467530}}}),WO=L({id:2021,name:`Saigon Testnet`,nativeCurrency:{name:`RON`,symbol:`RON`,decimals:18},rpcUrls:{default:{http:[`https://saigon-testnet.roninchain.com/rpc`]}},blockExplorers:{default:{name:`Saigon Explorer`,url:`https://saigon-app.roninchain.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:18736871}},testnet:!0}),GO=L({id:1996,name:`Sanko`,nativeCurrency:{name:`DMT`,symbol:`DMT`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.sanko.xyz`]}},blockExplorers:{default:{name:`Sanko Explorer`,url:`https://explorer.sanko.xyz`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:37}},testnet:!1}),KO=L({id:23294,name:`Oasis Sapphire`,network:`sapphire`,nativeCurrency:{name:`Sapphire Rose`,symbol:`ROSE`,decimals:18},rpcUrls:{default:{http:[`https://sapphire.oasis.io`],webSocket:[`wss://sapphire.oasis.io/ws`]}},blockExplorers:{default:{name:`Oasis Explorer`,url:`https://explorer.oasis.io/mainnet/sapphire`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:734531}}}),qO=L({id:23295,name:`Oasis Sapphire Testnet`,network:`sapphire-testnet`,nativeCurrency:{name:`Sapphire Test Rose`,symbol:`TEST`,decimals:18},rpcUrls:{default:{http:[`https://testnet.sapphire.oasis.dev`],webSocket:[`wss://testnet.sapphire.oasis.dev/ws`]}},blockExplorers:{default:{name:`Oasis Explorer`,url:`https://explorer.oasis.io/testnet/sapphire`}},testnet:!0}),JO=L({id:3109,name:`SatoshiVM Alpha Mainnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://alpha-rpc-node-http.svmscan.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://svmscan.io`,apiUrl:`https://svmscan.io/api`}}}),YO=L({id:3110,name:`SatoshiVM Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://test-rpc-node-http.svmscan.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet.svmscan.io`,apiUrl:`https://testnet.svmscan.io/api`}},testnet:!0}),XO=L({id:534352,name:`Scroll`,blockTime:3e3,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.scroll.io`],webSocket:[`wss://wss-rpc.scroll.io/ws`]}},blockExplorers:{default:{name:`Scrollscan`,url:`https://scrollscan.com`,apiUrl:`https://api.scrollscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14}},testnet:!1}),ZO=L({id:534351,name:`Scroll Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.scroll.io`]}},blockExplorers:{default:{name:`Scrollscan`,url:`https://sepolia.scrollscan.com`,apiUrl:`https://api-sepolia.scrollscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:9473}},testnet:!0}),QO=L({id:1329,name:`Sei Network`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc.sei-apis.com/`],webSocket:[`wss://evm-ws.sei-apis.com/`]}},blockExplorers:{default:{name:`Seitrace`,url:`https://seitrace.com`,apiUrl:`https://seitrace.com/pacific-1/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}}}),$O=L({id:713715,name:`Sei Devnet`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc-arctic-1.sei-apis.com`]}},blockExplorers:{default:{name:`Seitrace`,url:`https://seitrace.com`}},testnet:!0}),ek=L({id:5124,name:`Seismic Devnet`,nativeCurrency:{name:`Seismic Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://node-2.seismicdev.net/rpc`]}},blockExplorers:{default:{name:`Seismic Devnet Explorer`,url:`https://explorer-2.seismicdev.net`}},testnet:!0}),tk=L({id:1328,name:`Sei Testnet`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc-testnet.sei-apis.com`],webSocket:[`wss://evm-ws-testnet.sei-apis.com`]}},blockExplorers:{default:{name:`Seitrace`,url:`https://seitrace.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:98697651}},testnet:!0}),nk=L({id:11155111,name:`Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.drpc.org`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.etherscan.io`,apiUrl:`https://api-sepolia.etherscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:751532},ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:8928790}},testnet:!0});var rk=1;const ik=L({...R,id:360,name:`Shape`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.shape.network`]}},blockExplorers:{default:{name:`shapescan`,url:`https://shapescan.xyz`,apiUrl:`https://shapescan.xyz/api`}},contracts:{...R.contracts,l2OutputOracle:{[rk]:{address:`0x6Ef8c69CfE4635d866e3E02732068022c06e724D`,blockCreated:20369940}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1},portal:{[rk]:{address:`0xEB06fFa16011B5628BaB98E29776361c83741dd3`,blockCreated:20369933}},l1StandardBridge:{[rk]:{address:`0x62Edd5f4930Ea92dCa3fB81689bDD9b9d076b57B`,blockCreated:20369935}}},sourceId:rk});var ak=11155111;const ok=L({...R,id:11011,name:`Shape Sepolia Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.shape.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer-sepolia.shape.network/`,apiUrl:`https://explorer-sepolia.shape.network/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1}},testnet:!0,sourceId:ak}),sk=L({id:8118,name:`Shardeum`,nativeCurrency:{name:`Shardeum`,symbol:`SHM`,decimals:18},rpcUrls:{default:{http:[`https://api.shardeum.org`]}},blockExplorers:{default:{name:`Shardeum Explorer`,url:`https://explorer.shardeum.org`}},testnet:!1}),ck=L({id:8082,name:`Shardeum Sphinx`,nativeCurrency:{name:`SHARDEUM`,symbol:`SHM`,decimals:18},rpcUrls:{default:{http:[`https://sphinx.shardeum.org`]}},blockExplorers:{default:{name:`Shardeum Explorer`,url:`https://explorer-sphinx.shardeum.org`}},testnet:!0}),lk=L({id:109,name:`Shibarium`,network:`shibarium`,nativeCurrency:{name:`Bone`,symbol:`BONE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.shibrpc.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://shibariumscan.io`}},contracts:{multicall3:{address:`0x864Bf681ADD6052395188A89101A1B37d3B4C961`,blockCreated:265900}}}),uk=L({id:157,name:`Puppynet Shibarium`,nativeCurrency:{decimals:18,name:`Bone`,symbol:`BONE`},rpcUrls:{default:{http:[`https://puppynet.shibrpc.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://puppyscan.shib.io`,apiUrl:`https://puppyscan.shib.io/api`}},contracts:{multicall3:{address:`0xA4029b74FBA366c926eDFA7Dd10B21C621170a4c`,blockCreated:3035769}},testnet:!0}),dk=L({id:336,name:`Shiden`,nativeCurrency:{decimals:18,name:`SDN`,symbol:`SDN`},rpcUrls:{default:{http:[`https://shiden.public.blastapi.io`],webSocket:[`wss://shiden-rpc.dwellir.com`]}},blockExplorers:{default:{name:`Shiden Scan`,url:`https://shiden.subscan.io`}},testnet:!1}),fk=L({id:148,name:`Shimmer`,network:`shimmer`,nativeCurrency:{decimals:18,name:`Shimmer`,symbol:`SMR`},rpcUrls:{default:{http:[`https://json-rpc.evm.shimmer.network`]}},blockExplorers:{default:{name:`Shimmer Network Explorer`,url:`https://explorer.evm.shimmer.network`,apiUrl:`https://explorer.evm.shimmer.network/api`}}}),pk=L({id:1073,name:`Shimmer Testnet`,network:`shimmer-testnet`,nativeCurrency:{decimals:18,name:`Shimmer`,symbol:`SMR`},rpcUrls:{default:{http:[`https://json-rpc.evm.testnet.shimmer.network`]}},blockExplorers:{default:{name:`Shimmer Network Explorer`,url:`https://explorer.evm.testnet.shimmer.network`,apiUrl:`https://explorer.evm.testnet.shimmer.network/api`}},testnet:!0}),mk=L({id:97453,name:`Sidra Chain`,nativeCurrency:{decimals:18,name:`Sidra Digital Asset`,symbol:`SDA`},rpcUrls:{default:{http:[`https://node.sidrachain.com`]}},blockExplorers:{default:{name:`Sidra Chain Explorer`,url:`https://ledger.sidrachain.com`}}}),hk=L({id:2355,name:`Silicon zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.silicon.network`,`https://silicon-mainnet.nodeinfra.com`]}},blockExplorers:{default:{name:`SiliconScope`,url:`https://scope.silicon.network`}}}),gk=L({id:1722641160,name:`Silicon Sepolia zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-sepolia.silicon.network`,`https://silicon-testnet.nodeinfra.com`]}},blockExplorers:{default:{name:`SiliconSepoliaScope`,url:`https://scope-sepolia.silicon.network`}},testnet:!0}),_k=L({id:98,name:`Six Protocol`,nativeCurrency:{decimals:18,name:`SIX`,symbol:`SIX`},rpcUrls:{default:{http:[`https://sixnet-rpc-evm.sixprotocol.net`]}},blockExplorers:{default:{name:`Six Protocol Scan`,url:`https://sixscan.io/sixnet`}},testnet:!1}),vk=L({id:391845894,name:`SKALE | Block Brawlers`,nativeCurrency:{name:`BRAWL`,symbol:`BRAWL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/frayed-decent-antares`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/frayed-decent-antares`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://frayed-decent-antares.explorer.mainnet.skalenodes.com`}},contracts:{}}),yk=L({id:1564830818,name:`SKALE Calypso Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/honorable-steel-rasalhague`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/honorable-steel-rasalhague`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3107626}}}),bk=L({id:974399131,name:`SKALE Calypso Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/giant-half-dual-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/giant-half-dual-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://giant-half-dual-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:103220}},testnet:!0}),xk=L({id:1026062157,name:`SKALE | CryptoBlades`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/affectionate-immediate-pollux`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/affectionate-immediate-pollux`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://affectionate-immediate-pollux.explorer.mainnet.skalenodes.com`}},contracts:{}}),Sk=L({id:1032942172,name:`SKALE | Crypto Colosseum`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/haunting-devoted-deneb`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/haunting-devoted-deneb`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://haunting-devoted-deneb.explorer.mainnet.skalenodes.com`}},contracts:{}}),Ck=L({id:2046399126,name:`SKALE Europa Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/elated-tan-skat`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/elated-tan-skat`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://elated-tan-skat.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3113495}}}),wk=L({id:1444673419,name:`SKALE Europa Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/juicy-low-small-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/juicy-low-small-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://juicy-low-small-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:110858}},testnet:!0}),Tk=L({id:2139927552,name:`Exorde Network`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/light-vast-diphda`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/light-vast-diphda`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://light-vast-diphda.explorer.mainnet.skalenodes.com`}},contracts:{}}),Ek=L({id:1273227453,name:`SKALE | Human Protocol`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/wan-red-ain`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/wan-red-ain`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://wan-red-ain.explorer.mainnet.skalenodes.com`}},contracts:{}}),Dk=L({id:1482601649,name:`SKALE Nebula Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/green-giddy-denebola`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/green-giddy-denebola`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://green-giddy-denebola.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2372986}}}),Ok=L({id:37084624,name:`SKALE Nebula Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/lanky-ill-funny-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/lanky-ill-funny-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://lanky-ill-funny-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:105141}},testnet:!0}),kk=L({id:278611351,name:`SKALE | Razor Network`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/turbulent-unique-scheat`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/turbulent-unique-scheat`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://turbulent-unique-scheat.explorer.mainnet.skalenodes.com`}},contracts:{}}),Ak=L({id:1350216234,name:`SKALE Titan Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/parallel-stormy-spica`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/parallel-stormy-spica`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://parallel-stormy-spica.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2076458}}}),jk=L({id:1020352220,name:`SKALE Titan Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/aware-fake-trim-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/aware-fake-trim-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://aware-fake-trim-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:104072}},testnet:!0}),Mk=L({id:984123,name:`Forma Sketchpad`,network:`sketchpad`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sketchpad-1.forma.art`],webSocket:[`wss://ws.sketchpad-1.forma.art`]}},blockExplorers:{default:{name:`Sketchpad Explorer`,url:`https://explorer.sketchpad-1.forma.art`}},testnet:!0});var Nk=1;const Pk=L({...R,id:2192,network:`snaxchain-mainnet`,name:`SnaxChain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.snaxchain.io`]}},blockExplorers:{default:{name:`Snax Explorer`,url:`https://explorer.snaxchain.io`,apiUrl:`https://explorer.snaxchain.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[Nk]:{address:`0x472562Fcf26D6b2793f8E0b0fB660ba0E5e08A46`}},l2OutputOracle:{[Nk]:{address:`0x2172e492Fc807F5d5645D0E3543f139ECF539294`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[Nk]:{address:`0x79f446D024d74D0Bb6E699C131c703463c5D65E9`}},l1StandardBridge:{[Nk]:{address:`0x6534Bdb6b5c060d3e6aa833433333135eFE8E0aA`}}},sourceId:Nk});var Fk=11155111;const Ik=L({...R,id:13001,network:`snaxchain-testnet`,name:`SnaxChain Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.snaxchain.io`]}},blockExplorers:{default:{name:`Snax Explorer`,url:`https://testnet-explorer.snaxchain.io`,apiUrl:`https://testnet-explorer.snaxchain.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[Fk]:{address:`0x206a75d89d45F146C54020F132FF93bEDD09f55E`}},l2OutputOracle:{[Fk]:{address:`0x60e3A368a4cdCEf85ffB964e372726F56A46221e`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[Fk]:{address:`0xb5afdd0E8dDF081Ef90e8A3e0c7b5798e66E954E`}},l1StandardBridge:{[Fk]:{address:`0xbd37E1a59D4C00C9A46F75018dffd84061bC5f74`}}},testnet:!0,sourceId:Fk}),Lk=L({id:50312,name:`Somnia Testnet`,nativeCurrency:{name:`STT`,symbol:`STT`,decimals:18},rpcUrls:{default:{http:[`https://dream-rpc.somnia.network`]}},blockExplorers:{default:{name:`Somnia Testnet Explorer`,url:`https://shannon-explorer.somnia.network/`,apiUrl:`https://shannon-explorer.somnia.network/api`}},contracts:{multicall3:{address:`0x841b8199E6d3Db3C6f264f6C2bd8848b3cA64223`,blockCreated:71314235}},testnet:!0});var Rk=1;const zk=L({...R,id:1868,name:`Soneium Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.soneium.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://soneium.blockscout.com`,apiUrl:`https://soneium.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[Rk]:{address:`0x512a3d2c7a43bd9261d2b8e8c9c70d4bd4d503c0`}},l2OutputOracle:{[Rk]:{address:`0x0000000000000000000000000000000000000000`}},portal:{[Rk]:{address:`0x88e529a6ccd302c948689cd5156c83d4614fae92`,blockCreated:7061266}},l1StandardBridge:{[Rk]:{address:`0xeb9bf100225c214efc3e7c651ebbadcf85177607`,blockCreated:7061266}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}},sourceId:Rk});var Bk=11155111;const Vk=L({...R,id:1946,name:`Soneium Minato Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.minato.soneium.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://soneium-minato.blockscout.com`,apiUrl:`https://soneium-minato.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[Bk]:{address:`0xB3Ad2c38E6e0640d7ce6aA952AB3A60E81bf7a01`}},l2OutputOracle:{[Bk]:{address:`0x710e5286C746eC38beeB7538d0146f60D27be343`}},portal:{[Bk]:{address:`0x65ea1489741A5D72fFdD8e6485B216bBdcC15Af3`,blockCreated:6466136}},l1StandardBridge:{[Bk]:{address:`0x5f5a404A5edabcDD80DB05E8e54A78c9EBF000C2`,blockCreated:6466136}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}},testnet:!0,sourceId:Bk}),Hk=L({id:19,name:`Songbird Canary-Network`,nativeCurrency:{decimals:18,name:`Songbird`,symbol:`SGB`},rpcUrls:{default:{http:[`https://songbird-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Songbird Explorer`,url:`https://songbird-explorer.flare.network`,apiUrl:`https://songbird-explorer.flare.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:13382504}}}),Uk=L({id:16,name:`Songbird Testnet Coston`,nativeCurrency:{decimals:18,name:`Coston Flare`,symbol:`CFLR`},rpcUrls:{default:{http:[`https://coston-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Coston Explorer`,url:`https://coston-explorer.flare.network`,apiUrl:`https://coston-explorer.flare.network/api`}},testnet:!0}),Wk=L({id:146,name:`Sonic`,blockTime:630,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Explorer`,url:`https://sonicscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:60}},testnet:!1}),Gk=L({id:57054,name:`Sonic Blaze Testnet`,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.blaze.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Blaze Testnet Explorer`,url:`https://testnet.sonicscan.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1100}},testnet:!0}),Kk=L({id:64165,name:`Sonic Testnet`,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.testnet.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Testnet Explorer`,url:`https://testnet.soniclabs.com/`}},testnet:!0}),qk=L({...oy,id:50104,name:`Sophon`,nativeCurrency:{decimals:18,name:`Sophon`,symbol:`SOPH`},rpcUrls:{default:{http:[`https://rpc.sophon.xyz`],webSocket:[`wss://rpc.sophon.xyz/ws`]}},blockExplorers:{default:{name:`Sophon Block Explorer`,url:`https://explorer.sophon.xyz`}},contracts:{multicall3:{address:`0x5f4867441d2416cA88B1b3fd38f21811680CD2C8`,blockCreated:116}},testnet:!1}),Jk=L({...oy,id:531050104,name:`Sophon Testnet`,nativeCurrency:{decimals:18,name:`Sophon`,symbol:`SOPH`},rpcUrls:{default:{http:[`https://rpc.testnet.sophon.xyz`],webSocket:[`wss://rpc.testnet.sophon.xyz/ws`]}},blockExplorers:{default:{name:`Sophon Block Explorer`,url:`https://explorer.testnet.sophon.xyz`}},contracts:{multicall3:{address:`0x83c04d112adedA2C6D9037bb6ecb42E7f0b108Af`,blockCreated:15642}},testnet:!0}),Yk=L({id:100021,name:`Sova`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.sova.io`]}},blockExplorers:{default:{name:`Sova Block Explorer`,url:`hhttps://explorer.sova.io`}},testnet:!1}),Xk=L({id:120893,name:`Sova Network Sepolia`,nativeCurrency:{decimals:18,name:`Sepolia Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.testnet.sova.io`]}},blockExplorers:{default:{name:`Sova Sepolia Explorer`,url:`https://explorer.testnet.sova.io`}},testnet:!0}),Zk=L({id:88882,name:`Chiliz Spicy Testnet`,network:`chiliz-spicy-Testnet`,nativeCurrency:{decimals:18,name:`CHZ`,symbol:`CHZ`},rpcUrls:{default:{http:[`https://spicy-rpc.chiliz.com`,`https://chiliz-spicy-rpc.publicnode.com`],webSocket:[`wss://spicy-rpc-ws.chiliz.com`,`wss://chiliz-spicy-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Chiliz Explorer`,url:`http://spicy-explorer.chiliz.com`,apiUrl:`http://spicy-explorer.chiliz.com/api`}},testnet:!0}),Qk=L({...RT,id:1660990954,name:`Status Network Sepolia`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://public.sepolia.rpc.status.network`],webSocket:[`wss://public.sepolia.rpc.status.network/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepoliascan.status.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1578364}},testnet:!0}),$k=L({id:1234,name:`Step Network`,nativeCurrency:{name:`FITFI`,symbol:`FITFI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.step.network`]}},blockExplorers:{default:{name:`Step Scan`,url:`https://stepscan.io`}},testnet:!1}),eA=L({id:1514,name:`Story`,nativeCurrency:{decimals:18,name:`IP Token`,symbol:`IP`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:340998},ensRegistry:{address:`0x5dc881dda4e4a8d312be3544ad13118d1a04cb17`,blockCreated:648924},ensUniversalResolver:{address:`0xddfb18888a9466688235887dec2a10c4f5effee9`,blockCreated:649114}},rpcUrls:{default:{http:[`https://mainnet.storyrpc.io`]}},blockExplorers:{default:{name:`Story explorer`,url:`https://storyscan.io`,apiUrl:`https://storyscan.io/api/v2`}},ensTlds:[`.ip`],testnet:!1}),tA=L({id:1315,name:`Story Aeneid`,network:`story-aeneid`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1792},ensRegistry:{address:`0x5dC881dDA4e4a8d312be3544AD13118D1a04Cb17`,blockCreated:1322033},ensUniversalResolver:{address:`0x6D3B3F99177FB2A5de7F9E928a9BD807bF7b5BAD`,blockCreated:1322097}},rpcUrls:{default:{http:[`https://aeneid.storyrpc.io`]}},blockExplorers:{default:{name:`Story Aeneid Explorer`,url:`https://aeneid.storyscan.io`,apiUrl:`https://aeneid.storyscan.io/api/v2`}},ensTlds:[`.ip`],testnet:!0}),nA=L({id:1516,name:`Story Odyssey`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},rpcUrls:{default:{http:[`https://rpc.odyssey.storyrpc.io`]}},blockExplorers:{default:{name:`Story Odyssey Explorer`,url:`https://odyssey.storyscan.xyz`}},testnet:!0}),rA=L({id:1513,name:`Story Testnet`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},rpcUrls:{default:{http:[`https://testnet.storyrpc.io`]}},blockExplorers:{default:{name:`Story Testnet Explorer`,url:`https://testnet.storyscan.xyz`}},testnet:!0}),iA=L({id:105105,name:`Stratis Mainnet`,network:`stratis`,nativeCurrency:{name:`Stratis`,symbol:`STRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stratisevm.com`]}},blockExplorers:{default:{name:`Stratis Explorer`,url:`https://explorer.stratisevm.com`}}}),aA=L({id:8866,name:`SuperLumio`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.lumio.io`]}},blockExplorers:{default:{name:`Lumio explorer`,url:`https://explorer.lumio.io`}},testnet:!1}),oA=L({id:55244,name:`Superposition`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.superposition.so`]}},blockExplorers:{default:{name:`Superposition Explorer`,url:`https://explorer.superposition.so`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:39}},testnet:!1});var sA=1;const cA=L({...R,id:5330,name:`Superseed`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.superseed.xyz`]}},blockExplorers:{default:{name:`Superseed Explorer`,url:`https://explorer.superseed.xyz`,apiUrl:`https://explorer.superseed.xyz/api/v2`}},contracts:{...R.contracts,disputeGameFactory:{[sA]:{address:`0x8b097CF1f9BbD9cbFD0DD561858a1FCbC8857Be0`,blockCreated:20737481}},l2OutputOracle:{[sA]:{address:`0x693A0F8854F458D282DE3C5b69E8eE5EEE8aA949`,blockCreated:20737481}},portal:{[sA]:{address:`0x2c2150aa5c75A24fB93d4fD2F2a895D618054f07`,blockCreated:20737481}},l1StandardBridge:{[sA]:{address:`0x8b0576E39F1233679109F9b40cFcC2a7E0901Ede`,blockCreated:20737481}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},sourceId:sA});var lA=11155111;const uA=L({...R,id:53302,name:`Superseed Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.superseed.xyz`]}},blockExplorers:{default:{name:`Superseed Sepolia Explorer`,url:`https://sepolia-explorer.superseed.xyz`,apiUrl:`https://sepolia-explorer.superseed.xyz/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},portal:{[lA]:{address:`0x7A0db8C51432d2C3eb4e8f360a2EeB26FF2809fB`,blockCreated:5523438}},l1StandardBridge:{[lA]:{address:`0x2B227A603fAAdB3De0ED050b63ADD232B5f2c28C`,blockCreated:5523442}}},testnet:!0,sourceId:lA}),dA=L({id:763375,name:`Surge Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://l2-rpc.hoodi.surge.wtf`],webSocket:[`wss://l2-ws.hoodi.surge.wtf`]}},blockExplorers:{default:{name:`Surge Testnet Blockscout`,url:`https://explorer.hoodi.surge.wtf`}},testnet:!0}),fA=L({id:254,name:`Swan Chain Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.swanchain.org`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://swanscan.io`}},testnet:!1}),pA=L({id:20241133,name:`Swan Proxima Testnet`,nativeCurrency:{name:`Swan Ether`,symbol:`sETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-proxima.swanchain.io`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://proxima-explorer.swanchain.io`}},testnet:!0}),mA=L({id:2024,name:`Swan Saturn Testnet`,nativeCurrency:{name:`Swan Ether`,symbol:`sETH`,decimals:18},rpcUrls:{default:{http:[`https://saturn-rpc.swanchain.io`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://saturn-explorer.swanchain.io`}},testnet:!0}),hA=L({...R,id:1923,name:`Swellchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://swell-mainnet.alt.technology`]}},blockExplorers:{default:{name:`Swell Explorer`,url:`https://explorer.swellnetwork.io`,apiUrl:`https://explorer.swellnetwork.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}}}),gA=L({...R,id:1924,name:`Swellchain Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://swell-testnet.alt.technology`]}},blockExplorers:{default:{name:`Swellchain Testnet Explorer`,url:`https://swell-testnet-explorer.alt.technology`,apiUrl:`https://swell-testnet-explorer.alt.technology/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}}}),_A=L({id:94,name:`SwissDLT Mainnet`,nativeCurrency:{decimals:18,name:`BCTS`,symbol:`BCTS`},rpcUrls:{default:{http:[`https://rpc.swissdlt.ch`]}},blockExplorers:{default:{name:`SwissDLT Explorer`,url:`https://explorer.swissdlt.ch`}},testnet:!1}),vA=L({id:57,name:`Syscoin Mainnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.syscoin.org`],webSocket:[`wss://rpc.syscoin.org/wss`]}},blockExplorers:{default:{name:`SyscoinExplorer`,url:`https://explorer.syscoin.org`,apiUrl:`https://explorer.syscoin.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:287139}}}),yA=L({id:5700,name:`Syscoin Tanenbaum Testnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.tanenbaum.io`],webSocket:[`wss://rpc.tanenbaum.io/wss`]}},blockExplorers:{default:{name:`SyscoinTestnetExplorer`,url:`https://tanenbaum.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:271288}}}),bA=L({id:239,name:`TAC`,nativeCurrency:{name:`TAC`,symbol:`TAC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.ankr.com/tac`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://tac.blockscout.com`,apiUrl:`https://tac.blockscout.com/api`},native:{name:`TAC Explorer`,url:`https://explorer.tac.build`,apiUrl:`https://explorer.tac.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),xA=L({id:2391,name:`TAC SPB Testnet`,nativeCurrency:{name:`TAC`,symbol:`TAC`,decimals:18},rpcUrls:{default:{http:[`https://spb.rpc.tac.build`]}},blockExplorers:{default:{name:`TAC`,url:`https://spb.explorer.tac.build`,apiUrl:`https://spb.explorer.tac.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:471429}},testnet:!0}),SA=L({id:167e3,name:`Taiko Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.mainnet.taiko.xyz`],webSocket:[`wss://ws.mainnet.taiko.xyz`]}},blockExplorers:{default:{name:`Taikoscan`,url:`https://taikoscan.io`,apiUrl:`https://api.taikoscan.io/api`}},contracts:{multicall3:{address:`0xcb2436774C3e191c85056d248EF4260ce5f27A9D`}}}),CA=L({id:167009,name:`Taiko Hekla L2`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hekla.taiko.xyz`]}},blockExplorers:{default:{name:`Taikoscan`,url:`https://hekla.taikoscan.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:59757}},testnet:!0}),wA=L({id:167007,name:`Taiko Jolnir (Alpha-5 Testnet)`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.jolnir.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.jolnir.taiko.xyz`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:732706}},testnet:!0}),TA=L({id:167008,name:`Taiko Katla (Alpha-6 Testnet)`,network:`tko-katla`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.katla.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.katla.taiko.xyz`}}}),EA=L({id:167005,name:`Taiko (Alpha-3 Testnet)`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.test.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.test.taiko.xyz`}}}),DA=L({id:841,name:`Taraxa Mainnet`,nativeCurrency:{name:`Tara`,symbol:`TARA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.taraxa.io`]}},blockExplorers:{default:{name:`Taraxa Explorer`,url:`https://explorer.mainnet.taraxa.io`}}}),OA=L({id:842,name:`Taraxa Testnet`,nativeCurrency:{name:`Tara`,symbol:`TARA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.taraxa.io`]}},blockExplorers:{default:{name:`Taraxa Explorer`,url:`https://explorer.testnet.taraxa.io`}},testnet:!0}),kA=L({id:10218,name:`Tea Sepolia`,nativeCurrency:{name:`Sepolia Tea`,symbol:`TEA`,decimals:18},rpcUrls:{default:{http:[`https://tea-sepolia.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Tea Sepolia Explorer`,url:`https://sepolia.tea.xyz`}},testnet:!0}),AA=L({id:2017,name:`Telcoin Adiri Testnet`,nativeCurrency:{name:`Telcoin`,symbol:`TEL`,decimals:18},rpcUrls:{default:{http:[`https://rpc.telcoin.network`]}},blockExplorers:{default:{name:`telscan`,url:`https://telscan.io`}},testnet:!0}),jA=L({id:40,name:`Telos`,nativeCurrency:{decimals:18,name:`Telos`,symbol:`TLOS`},rpcUrls:{default:{http:[`https://rpc.telos.net`]}},blockExplorers:{default:{name:`Teloscan`,url:`https://www.teloscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:246530709}}}),MA=L({id:41,name:`Telos`,nativeCurrency:{decimals:18,name:`Telos`,symbol:`TLOS`},rpcUrls:{default:{http:[`https://rpc.testnet.telos.net`]}},blockExplorers:{default:{name:`Teloscan (testnet)`,url:`https://testnet.teloscan.io/`}},testnet:!0}),NA=L({id:1559,name:`Tenet`,network:`tenet-mainnet`,nativeCurrency:{name:`TENET`,symbol:`TENET`,decimals:18},rpcUrls:{default:{http:[`https://rpc.tenet.org`]}},blockExplorers:{default:{name:`TenetScan Mainnet`,url:`https://tenetscan.io`,apiUrl:`https://tenetscan.io/api`}},testnet:!1}),PA=L({id:752025,name:`Ternoa`,nativeCurrency:{name:`Capsule Coin`,symbol:`CAPS`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.zkevm.ternoa.network`]}},blockExplorers:{default:{name:`Ternoa Explorer`,url:`https://explorer-mainnet.zkevm.ternoa.network`}},testnet:!1}),FA=L({id:7,name:`ThaiChain`,nativeCurrency:{name:`TCH`,symbol:`TCH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.thaichain.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp.thaichain.org`,apiUrl:`https://exp.thaichain.org/api`}},contracts:{multicall3:{address:`0x0DaD6130e832c21719C5CE3bae93454E16A84826`,blockCreated:4806386}},testnet:!1}),IA=L({id:8428,name:`THAT Mainnet`,nativeCurrency:{name:`THAT`,symbol:`THAT`,decimals:18},rpcUrls:{default:{http:[`https://api.thatchain.io/mainnet`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://that.blockscout.com`}},testnet:!1}),LA=L({id:361,name:`Theta Mainnet`,nativeCurrency:{name:`TFUEL`,symbol:`TFUEL`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-api.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`Theta Explorer`,url:`https://explorer.thetatoken.org`}},testnet:!1}),RA=L({id:365,name:`Theta Testnet`,nativeCurrency:{name:`TFUEL`,symbol:`TFUEL`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-api-testnet.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`Theta Explorer`,url:`https://testnet-explorer.thetatoken.org`}},testnet:!0}),zA=L({id:108,name:`ThunderCore Mainnet`,nativeCurrency:{name:`TT`,symbol:`TT`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.thundercore.com`]}},blockExplorers:{default:{name:`ThunderCore Explorer`,url:`https://explorer-mainnet.thundercore.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!1}),BA=L({id:997,name:`5ireChain Thunder Testnet`,nativeCurrency:{name:`5ire Token`,symbol:`5IRE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.5ire.network`]}},blockExplorers:{default:{name:`5ireChain Thunder Explorer`,url:`https://testnet.5irescan.io/`}},testnet:!0}),VA=L({id:62092,name:`TikTrix Testnet`,nativeCurrency:{name:`tTTX`,symbol:`tTTX`,decimals:18},rpcUrls:{default:{http:[`https://tiktrix-rpc.xyz`]}},blockExplorers:{default:{name:`TikTrix Testnet Explorer`,url:`https://tiktrix.xyz`}},testnet:!0}),HA=L({id:6969,name:`Tomb Mainnet`,nativeCurrency:{name:`TOMB`,symbol:`TOMB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.tombchain.com`]}},blockExplorers:{default:{name:`Tomb Explorer`,url:`https://tombscout.com`}},testnet:!1}),UA=L({...oy,id:61166,name:`Treasure`,nativeCurrency:{decimals:18,name:`MAGIC`,symbol:`MAGIC`},rpcUrls:{default:{http:[`https://rpc.treasure.lol`],webSocket:[`wss://rpc.treasure.lol/ws`]}},blockExplorers:{default:{name:`Treasure Block Explorer`,url:`https://treasurescan.io`}},contracts:{multicall3:{address:`0x2e29fe39496a56856D8698bD43e1dF4D0CE6266a`,blockCreated:101}},testnet:!1}),WA=L({...oy,id:978658,name:`Treasure Topaz Testnet`,nativeCurrency:{decimals:18,name:`MAGIC`,symbol:`MAGIC`},rpcUrls:{default:{http:[`https://rpc.topaz.treasure.lol`],webSocket:[`wss://rpc.topaz.treasure.lol/ws`]}},blockExplorers:{default:{name:`Treasure Topaz Block Explorer`,url:`https://topaz.treasurescan.io`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:108112}},testnet:!0}),GA=L({id:728126428,name:`Tron`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://api.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://tronscan.org`,apiUrl:`https://apilist.tronscanapi.com/api`}}}),KA=L({id:3448148188,name:`Tron Nile`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://nile.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://nile.tronscan.org`}},testnet:!0}),qA=L({id:2494104990,name:`Tron Shasta`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://api.shasta.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://shasta.tronscan.org`}},testnet:!0}),JA=L({id:8,name:`Ubiq Mainnet`,nativeCurrency:{name:`UBQ`,symbol:`UBQ`,decimals:18},rpcUrls:{default:{http:[`https://pyrus2.ubiqscan.io`]}},blockExplorers:{default:{name:`Ubiq Scan`,url:`https://ubiqscan.io`}},testnet:!1}),YA=L({id:19991,name:`Ultra EVM`,nativeCurrency:{decimals:18,name:`Ultra Token`,symbol:`UOS`},rpcUrls:{default:{http:[`https://evm.ultra.eosusa.io`]}},blockExplorers:{default:{name:`Ultra EVM Explorer`,url:`https://evmexplorer.ultra.io`}}}),XA=L({id:18881,name:`Ultra EVM Testnet`,nativeCurrency:{decimals:18,name:`Ultra Token`,symbol:`UOS`},rpcUrls:{default:{http:[`https://evm.test.ultra.eosusa.io`]}},blockExplorers:{default:{name:`Ultra EVM Testnet Explorer`,url:`https://evmexplorer.testnet.ultra.io`}},testnet:!0}),ZA=L({id:1231,name:`Ultron Mainnet`,nativeCurrency:{name:`ULX`,symbol:`ULX`,decimals:18},rpcUrls:{default:{http:[`https://ultron-rpc.net`]}},blockExplorers:{default:{name:`Ultron Scan`,url:`https://ulxscan.com`}},testnet:!1}),QA=L({id:1230,name:`Ultron Testnet`,nativeCurrency:{name:`ULX`,symbol:`ULX`,decimals:18},rpcUrls:{default:{http:[`https://ultron-dev.io`]}},blockExplorers:{default:{name:`Ultron Scan`,url:`https://explorer.ultron-dev.io`}},testnet:!0});var $A=1;const ej=L({...R,id:130,name:`Unichain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://mainnet.unichain.org/`]}},blockExplorers:{default:{name:`Uniscan`,url:`https://uniscan.xyz`,apiUrl:`https://api.uniscan.xyz/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[$A]:{address:`0x2F12d621a16e2d3285929C9996f478508951dFe4`}},portal:{[$A]:{address:`0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2`}},l1StandardBridge:{[$A]:{address:`0x81014F44b0a345033bB2b3B21C7a1A308B35fEeA`}}},sourceId:$A});var tj=11155111;const nj=L({...R,id:1301,name:`Unichain Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://sepolia.unichain.org`]}},blockExplorers:{default:{name:`Uniscan`,url:`https://sepolia.uniscan.xyz`,apiUrl:`https://api-sepolia.uniscan.xyz/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},portal:{[tj]:{address:`0x0d83dab629f0e0F9d36c0Cbc89B69a489f0751bD`}},l1StandardBridge:{[tj]:{address:`0xea58fcA6849d79EAd1f26608855c2D6407d54Ce2`}},disputeGameFactory:{[tj]:{address:`0xeff73e5aa3B9AEC32c659Aa3E00444d20a84394b`}}},testnet:!0,sourceId:tj}),rj=L({id:8880,name:`Unique Mainnet`,nativeCurrency:{decimals:18,name:`UNQ`,symbol:`UNQ`},rpcUrls:{default:{http:[`https://rpc.unique.network`]}},blockExplorers:{default:{name:`Unique Subscan`,url:`https://unique.subscan.io/`}}}),ij=L({id:8882,name:`Opal Testnet`,nativeCurrency:{decimals:18,name:`OPL`,symbol:`OPL`},rpcUrls:{default:{http:[`https://rpc-opal.unique.network`]}},blockExplorers:{default:{name:`Opal Subscan`,url:`https://opal.subscan.io/`}},testnet:!0}),aj=L({id:8881,name:`Quartz Mainnet`,nativeCurrency:{decimals:18,name:`QTZ`,symbol:`QTZ`},rpcUrls:{default:{http:[`https://rpc-quartz.unique.network`]}},blockExplorers:{default:{name:`Quartz Subscan`,url:`https://quartz.subscan.io/`}}}),oj=L({id:18233,name:`Unreal`,nativeCurrency:{name:`reETH`,decimals:18,symbol:`reETH`},rpcUrls:{default:{http:[`https://rpc.unreal-orbit.gelato.digital`]}},blockExplorers:{default:{name:`Unreal Explorer`,url:`https://unreal.blockscout.com`,apiUrl:`https://unreal.blockscout.com/api/v2`}},testnet:!0,contracts:{multicall3:{address:`0x8b6B0e60D8CD84898Ea8b981065A12F876eA5677`,blockCreated:1745}}}),sj=L({id:1480,name:`Vana`,blockTime:6e3,nativeCurrency:{decimals:18,name:`Vana`,symbol:`VANA`},rpcUrls:{default:{http:[`https://rpc.vana.org/`]}},blockExplorers:{default:{name:`Vana Block Explorer`,url:`https://vanascan.io`,apiUrl:`https://vanascan.io/api`}},contracts:{multicall3:{address:`0xD8d2dFca27E8797fd779F8547166A2d3B29d360E`,blockCreated:716763}}}),cj=L({id:14800,name:`Vana Moksha Testnet`,blockTime:6e3,nativeCurrency:{decimals:18,name:`Vana`,symbol:`VANA`},rpcUrls:{default:{http:[`https://rpc.moksha.vana.org`]}},blockExplorers:{default:{name:`Vana Moksha Testnet`,url:`https://moksha.vanascan.io`,apiUrl:`https://moksha.vanascan.io/api`}},contracts:{multicall3:{address:`0xD8d2dFca27E8797fd779F8547166A2d3B29d360E`,blockCreated:732283}},testnet:!0}),lj=L({id:2040,name:`Vanar Mainnet`,nativeCurrency:{name:`VANRY`,symbol:`VANRY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.vanarchain.com`]}},blockExplorers:{default:{name:`Vanar Mainnet Explorer`,url:`https://explorer.vanarchain.com/`}},testnet:!1}),uj=L({id:100009,name:`Vechain`,nativeCurrency:{name:`VeChain`,symbol:`VET`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.vechain.org`]}},blockExplorers:{default:{name:`Vechain Explorer`,url:`https://explore.vechain.org`},vechainStats:{name:`Vechain Stats`,url:`https://vechainstats.com`}}}),dj=L({id:106,name:`Velas EVM Mainnet`,nativeCurrency:{name:`VLX`,symbol:`VLX`,decimals:18},rpcUrls:{default:{http:[`https://evmexplorer.velas.com/rpc`]}},blockExplorers:{default:{name:`Velas Explorer`,url:`https://evmexplorer.velas.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:55883577}},testnet:!1}),fj=L({id:88,name:`Viction`,nativeCurrency:{name:`Viction`,symbol:`VIC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.viction.xyz`]}},blockExplorers:{default:{name:`VIC Scan`,url:`https://vicscan.xyz`}},testnet:!1}),pj=L({id:89,name:`Viction Testnet`,nativeCurrency:{name:`Viction`,symbol:`VIC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.viction.xyz`]}},blockExplorers:{default:{name:`VIC Scan`,url:`https://testnet.vicscan.xyz`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:12170179}},testnet:!0}),mj=L({id:888888,name:`Vision`,nativeCurrency:{name:`VISION`,symbol:`VS`,decimals:18},rpcUrls:{default:{http:[`https://infragrid.v.network/ethereum/compatible`]}},blockExplorers:{default:{name:`Vision Scan`,url:`https://visionscan.org`}},testnet:!1}),hj=L({id:666666,name:`Vision Testnet`,nativeCurrency:{name:`VISION`,symbol:`VS`,decimals:18},rpcUrls:{default:{http:[`https://vpioneer.infragrid.v.network/ethereum/compatible`]}},blockExplorers:{default:{name:`Vision Scan`,url:`https://visionscan.org/?chain=vpioneer`}},testnet:!0}),gj=L({id:888,name:`Wanchain`,nativeCurrency:{name:`WANCHAIN`,symbol:`WAN`,decimals:18},rpcUrls:{default:{http:[`https://gwan-ssl.wandevs.org:56891`,`https://gwan2-ssl.wandevs.org`]}},blockExplorers:{default:{name:`WanScan`,url:`https://wanscan.org`}},contracts:{multicall3:{address:`0xcDF6A1566e78EB4594c86Fe73Fcdc82429e97fbB`,blockCreated:25312390}}}),_j=L({id:999,name:`Wanchain Testnet`,nativeCurrency:{name:`WANCHAIN`,symbol:`WANt`,decimals:18},rpcUrls:{default:{http:[`https://gwan-ssl.wandevs.org:46891`]}},blockExplorers:{default:{name:`WanScanTest`,url:`https://wanscan.org`}},contracts:{multicall3:{address:`0x11c89bF4496c39FB80535Ffb4c92715839CC5324`,blockCreated:24743448}},testnet:!0}),vj=L({id:9496,name:`WeaveVM Alphanet`,nativeCurrency:{name:`Testnet WeaveVM`,symbol:`tWVM`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.wvm.dev`]}},blockExplorers:{default:{name:`WeaveVM Alphanet Explorer`,url:`https://explorer.wvm.dev`}},testnet:!0}),yj=L({id:1111,name:`WEMIX`,network:`wemix-mainnet`,nativeCurrency:{name:`WEMIX`,symbol:`WEMIX`,decimals:18},rpcUrls:{default:{http:[`https://api.wemix.com`]}},blockExplorers:{default:{name:`wemixExplorer`,url:`https://explorer.wemix.com`}}}),bj=L({id:1112,name:`WEMIX Testnet`,network:`wemix-testnet`,nativeCurrency:{name:`WEMIX`,symbol:`tWEMIX`,decimals:18},rpcUrls:{default:{http:[`https://api.test.wemix.com`]}},blockExplorers:{default:{name:`wemixExplorer`,url:`https://testnet.wemixscan.com`,apiUrl:`https://testnet.wemixscan.com/api`}},testnet:!0}),xj=L({id:420420421,name:`Westend Asset Hub`,nativeCurrency:{decimals:18,name:`Westies`,symbol:`WND`},rpcUrls:{default:{http:[`https://westend-asset-hub-eth-rpc.polkadot.io`]}},blockExplorers:{default:{name:`subscan`,url:`https://westend-asset-hub-eth-explorer.parity.io`}},testnet:!0}),Sj=L({testnet:!1,name:`Whitechain`,blockExplorers:{default:{name:`Whitechain Explorer`,url:`https://explorer.whitechain.io`}},id:1875,rpcUrls:{default:{http:[`https://rpc.whitechain.io`]}},nativeCurrency:{decimals:18,name:`WhiteBIT Coin`,symbol:`WBT`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:25212237}}}),Cj=L({testnet:!0,name:`Whitechain Testnet`,blockExplorers:{default:{name:`Whitechain Explorer`,url:`https://testnet.whitechain.io`}},id:2625,rpcUrls:{default:{http:[`https://rpc-testnet.whitechain.io`]}},nativeCurrency:{decimals:18,name:`WhiteBIT Coin`,symbol:`WBT`}}),wj=L({id:42070,name:`WMC Testnet`,nativeCurrency:{name:`WMTx`,symbol:`WMTx`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet-base.worldmobile.net`]}},blockExplorers:{default:{name:`WMC Explorer`,url:`https://explorer2-base-testnet.worldmobile.net`}},testnet:!0});var Tj=1;const Ej=L({...R,id:480,name:`World Chain`,network:`worldchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://worldchain-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Worldscan`,url:`https://worldscan.org`,apiUrl:`https://api.worldscan.org/api`},blockscout:{name:`Blockscout`,url:`https://worldchain-mainnet.explorer.alchemy.com`,apiUrl:`https://worldchain-mainnet.explorer.alchemy.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[Tj]:{address:`0x069c4c579671f8c120b1327a73217D01Ea2EC5ea`}},l2OutputOracle:{[Tj]:{address:`0x19A6d1E9034596196295CF148509796978343c5D`}},portal:{[Tj]:{address:`0xd5ec14a83B7d95BE1E2Ac12523e2dEE12Cbeea6C`}},l1StandardBridge:{[Tj]:{address:`0x470458C91978D2d929704489Ad730DC3E3001113`}}},testnet:!1,sourceId:Tj});var Dj=11155111;const Oj=L({...R,id:4801,name:`World Chain Sepolia`,network:`worldchain-sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://worldchain-sepolia.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Worldscan Sepolia`,url:`https://sepolia.worldscan.org`,apiUrl:`https://api-sepolia.worldscan.org/api`},blockscout:{name:`Blockscout`,url:`https://worldchain-sepolia.explorer.alchemy.com`,apiUrl:`https://worldchain-sepolia.explorer.alchemy.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[Dj]:{address:`0x8Ec1111f67Dad6b6A93B3F42DfBC92D81c98449A`}},l2OutputOracle:{[Dj]:{address:`0xc8886f8BAb6Eaeb215aDB5f1c686BF699248300e`}},portal:{[Dj]:{address:`0xFf6EBa109271fe6d4237EeeD4bAb1dD9A77dD1A4`}},l1StandardBridge:{[Dj]:{address:`0xd7DF54b3989855eb66497301a4aAEc33Dbb3F8DE`}}},testnet:!0,sourceId:Dj}),kj=L({id:103,name:`WorldLand Mainnet`,nativeCurrency:{decimals:18,name:`WLC`,symbol:`WLC`},rpcUrls:{default:{http:[`https://seoul.worldland.foundation`]}},blockExplorers:{default:{name:`WorldLand Scan`,url:`https://scan.worldland.foundation`}},testnet:!1}),Aj=L({id:660279,name:`Xai Mainnet`,nativeCurrency:{name:`Xai`,symbol:`XAI`,decimals:18},rpcUrls:{default:{http:[`https://xai-chain.net/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.xai-chain.net`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:222549}},testnet:!1}),jj=L({id:37714555429,name:`Xai Testnet`,nativeCurrency:{name:`sXai`,symbol:`sXAI`,decimals:18},rpcUrls:{default:{http:[`https://testnet-v2.xai-chain.net/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer-v2.xai-chain.net`}},testnet:!0}),Mj=L({id:50,name:`XDC Network`,nativeCurrency:{decimals:18,name:`XDC`,symbol:`XDC`},rpcUrls:{default:{http:[`https://rpc.xdcrpc.com`]}},blockExplorers:{default:{name:`XDCScan`,url:`https://xdcscan.com`}},contracts:{multicall3:{address:`0x0B1795ccA8E4eC4df02346a082df54D437F8D9aF`,blockCreated:75884020}}}),Nj=L({id:51,name:`Apothem Network`,nativeCurrency:{decimals:18,name:`TXDC`,symbol:`TXDC`},rpcUrls:{default:{http:[`https://erpc.apothem.network`]}},blockExplorers:{default:{name:`XDCScan`,url:`https://testnet.xdcscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:59765389}}}),Pj=L({id:196,name:`X Layer Mainnet`,nativeCurrency:{decimals:18,name:`OKB`,symbol:`OKB`},rpcUrls:{default:{http:[`https://rpc.xlayer.tech`]}},blockExplorers:{default:{name:`OKLink`,url:`https://www.oklink.com/xlayer`,apiUrl:`https://www.oklink.com/api/v5/explorer/xlayer/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:47416}}}),Fj=L({id:195,name:`X1 Testnet`,nativeCurrency:{decimals:18,name:`OKB`,symbol:`OKB`},rpcUrls:{default:{http:[`https://xlayertestrpc.okx.com`]}},blockExplorers:{default:{name:`OKLink`,url:`https://www.oklink.com/xlayer-test`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:624344}},testnet:!0}),Ij=L({id:20250217,name:`Xphere Mainnet`,nativeCurrency:{decimals:18,name:`XP`,symbol:`XP`},rpcUrls:{default:{http:[`https://en-bkk.x-phere.com`]}},blockExplorers:{default:{name:`Xphere Tamsa Explorer`,url:`https://xp.tamsa.io`}},testnet:!1}),Lj=L({id:1998991,name:`Xphere Testnet`,nativeCurrency:{decimals:18,name:`XPT`,symbol:`XPT`},rpcUrls:{default:{http:[`http://testnet.x-phere.com`]}},blockExplorers:{default:{name:`Xphere Tamsa Explorer`,url:`https://xpt.tamsa.io`}},testnet:!0}),Rj=L({id:273,name:`XR One`,nativeCurrency:{decimals:18,name:`XR1`,symbol:`XR1`},rpcUrls:{default:{http:[`https://xr1.calderachain.xyz/http`],webSocket:[`wss://xr1.calderachain.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://xr1.calderaexplorer.xyz`}},testnet:!1}),zj=L({id:1440002,name:`XRPL EVM Devnet`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xrplevm.org/`]},public:{http:[`https://rpc.xrplevm.org/`]}},blockExplorers:{default:{name:`XRPLEVM Devnet Explorer`,url:`https://explorer.xrplevm.org/`}},contracts:{multicall3:{address:`0x82Cc144D7d0AD4B1c27cb41420e82b82Ad6e9B31`,blockCreated:15237286}},testnet:!0}),Bj=L({id:1449e3,name:`XRPL EVM Testnet`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.xrplevm.org`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.testnet.xrplevm.org`,apiUrl:`https://explorer.testnet.xrplevm.org/api/v2`}},contracts:{multicall3:{address:`0x82Cc144D7d0AD4B1c27cb41420e82b82Ad6e9B31`,blockCreated:492302}},testnet:!0}),Vj=L({id:2730,name:`XR Sepolia`,nativeCurrency:{decimals:18,name:`tXR`,symbol:`tXR`},rpcUrls:{default:{http:[`https://xr-sepolia-testnet.rpc.caldera.xyz/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://xr-sepolia-testnet.explorer.caldera.xyz`}},testnet:!0}),Hj=L({id:50005,name:`Yooldo Verse`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.yooldo-verse.xyz`]}},blockExplorers:{default:{name:`Yooldo Verse Explorer`,url:`https://explorer.yooldo-verse.xyz`}}}),Uj=L({id:50006,name:`Yooldo Verse Testnet`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.yooldo-verse.xyz`]}},blockExplorers:{default:{name:`Yooldo Verse Testnet Explorer`,url:`https://explorer.testnet.yooldo-verse.xyz`}},testnet:!0}),Wj=L({id:8408,name:`ZenChain Testnet`,nativeCurrency:{decimals:18,name:`ZTC`,symbol:`ZTC`},rpcUrls:{default:{http:[`https://zenchain-testnet.api.onfinality.io/public`],webSocket:[`wss://zenchain-testnet.api.onfinality.io/public-ws`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:230019}},blockExplorers:{default:{name:`Zentrace`,url:`https://zentrace.io`}},testnet:!0}),Gj=L({id:383414847825,name:`Zeniq Mainnet`,nativeCurrency:{name:`ZENIQ`,symbol:`ZENIQ`,decimals:18},rpcUrls:{default:{http:[`https://api.zeniq.network`]}},blockExplorers:{default:{name:`Zeniq Explorer`,url:`https://zeniqscan.com`}},testnet:!1}),Kj=L({id:543210,name:`Zero Network`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.zerion.io/v1/zero`]}},blockExplorers:{default:{name:`Zero Network Explorer`,url:`https://explorer.zero.network`}},testnet:!1}),qj=L({id:7e3,name:`ZetaChain`,nativeCurrency:{decimals:18,name:`Zeta`,symbol:`ZETA`},rpcUrls:{default:{http:[`https://zetachain-evm.blockpi.network/v1/rpc/public`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1632781}},blockExplorers:{default:{name:`ZetaScan`,url:`https://zetascan.com`}},testnet:!1}),Jj=L({id:7001,name:`ZetaChain Athens Testnet`,nativeCurrency:{decimals:18,name:`Zeta`,symbol:`aZETA`},rpcUrls:{default:{http:[`https://zetachain-athens-evm.blockpi.network/v1/rpc/public`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2715217}},blockExplorers:{default:{name:`ZetaScan`,url:`https://testnet.zetascan.com`}},testnet:!0}),Yj=L({id:1337803,name:`Zhejiang`,nativeCurrency:{name:`Zhejiang Ether`,symbol:`ZhejETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.zhejiang.ethpandaops.io`]}},blockExplorers:{default:{name:`Beaconchain`,url:`https://zhejiang.beaconcha.in`}},testnet:!0}),Xj=L({id:32769,name:`Zilliqa`,network:`zilliqa`,nativeCurrency:{name:`Zilliqa`,symbol:`ZIL`,decimals:18},rpcUrls:{default:{http:[`https://api.zilliqa.com`]}},blockExplorers:{default:{name:`Ethernal`,url:`https://evmx.zilliqa.com`}},testnet:!1}),Zj=L({id:33101,name:`Zilliqa Testnet`,network:`zilliqa-testnet`,nativeCurrency:{name:`Zilliqa`,symbol:`ZIL`,decimals:18},rpcUrls:{default:{http:[`https://dev-api.zilliqa.com`]}},blockExplorers:{default:{name:`Ethernal`,url:`https://evmx.testnet.zilliqa.com`}},testnet:!0});var Qj=1;const $j=L({...R,id:48900,name:`Zircuit Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.zircuit.com`,`https://zircuit1-mainnet.liquify.com`,`https://zircuit1-mainnet.p2pify.com`,`https://zircuit-mainnet.drpc.org`]}},blockExplorers:{default:{name:`Zircuit Explorer`,url:`https://explorer.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},l2OutputOracle:{[Qj]:{address:`0x92Ef6Af472b39F1b363da45E35530c24619245A4`}},portal:{[Qj]:{address:`0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1`}},l1StandardBridge:{[Qj]:{address:`0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8`}}},testnet:!1});var eM=11155111;const tM=L({...R,id:48898,name:`Zircuit Garfield Testnet`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://garfield-testnet.zircuit.com/`]}},blockExplorers:{default:{name:`Zircuit Garfield Testnet Explorer`,url:`https://explorer.garfield-testnet.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},l2OutputOracle:{[eM]:{address:`0xd69D3AC5CA686cCF94b258291772bc520FEAf211`}},portal:{[eM]:{address:`0x4E21A71Ac3F7607Da5c06153A17B1DD20E702c21`}},l1StandardBridge:{[eM]:{address:`0x87a7E2bCA9E35BA49282E832a28A6023904460D8`}}},testnet:!0});var nM=11155111;const rM=L({...R,id:48899,name:`Zircuit Testnet`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.zircuit.com`,`https://zircuit1-testnet.p2pify.com`,`https://zircuit1-testnet.liquify.com`]}},blockExplorers:{default:{name:`Zircuit Testnet Explorer`,url:`https://explorer.testnet.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6040287},l2OutputOracle:{[nM]:{address:`0x740C2dac453aEf7140809F80b72bf0e647af8148`}},portal:{[nM]:{address:`0x787f1C8c5924178689E0560a43D848bF8E54b23e`}},l1StandardBridge:{[nM]:{address:`0x0545c5fe980098C16fcD0eCB5E79753afa6d9af9`}}},testnet:!0}),iM=L({id:42766,name:`ZKFair Mainnet`,network:`zkfair-mainnet`,nativeCurrency:{decimals:18,name:`USD Coin`,symbol:`USDC`},rpcUrls:{default:{http:[`https://rpc.zkfair.io`]}},blockExplorers:{default:{name:`zkFair Explorer`,url:`https://scan.zkfair.io`,apiUrl:`https://scan.zkfair.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6090959}},testnet:!1}),aM=L({id:43851,name:`ZKFair Testnet`,network:`zkfair-testnet`,nativeCurrency:{decimals:18,name:`USD Coin`,symbol:`USDC`},rpcUrls:{default:{http:[`https://testnet-rpc.zkfair.io`]}},blockExplorers:{default:{name:`zkFair Explorer`,url:`https://testnet-scan.zkfair.io`}},testnet:!0}),oM=L({id:810180,name:`zkLink Nova`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zklink.io`]}},blockExplorers:{default:{name:`zkLink Nova Block Explorer`,url:`https://explorer.zklink.io`}}}),sM=L({id:810181,name:`zkLink Nova Sepolia Testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia.rpc.zklink.io`]}},blockExplorers:{default:{name:`zkLink Nova Block Explorer`,url:`https://sepolia.explorer.zklink.io`}}}),cM=L({...oy,id:324,name:`ZKsync Era`,network:`zksync-era`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.era.zksync.io`],webSocket:[`wss://mainnet.era.zksync.io/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://era.zksync.network/`,apiUrl:`https://api-era.zksync.network/api`},native:{name:`ZKsync Explorer`,url:`https://explorer.zksync.io/`,apiUrl:`https://block-explorer-api.mainnet.zksync.io/api`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:45659388}}}),lM=L({...oy,id:260,name:`ZKsync InMemory Node`,network:`zksync-in-memory-node`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:8011`]}},testnet:!0}),uM=L({...oy,id:272,name:`ZKsync CLI Local Custom Hyperchain`,nativeCurrency:{name:`BAT`,symbol:`BAT`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15200`],webSocket:[`ws://localhost:15201`]}},blockExplorers:{default:{name:`ZKsync explorer`,url:`http://localhost:15005/`,apiUrl:`http://localhost:15005/api`}},testnet:!0}),dM=L({...oy,id:270,name:`ZKsync CLI Local Hyperchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15100`],webSocket:[`ws://localhost:15101`]}},blockExplorers:{default:{name:`ZKsync explorer`,url:`http://localhost:15005/`,apiUrl:`http://localhost:15005/api`}},testnet:!0}),fM=L({id:9,name:`ZKsync CLI Local Hyperchain L1`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15045`]}},blockExplorers:{default:{name:`Blockscout`,url:`http://localhost:15001/`,apiUrl:`http://localhost:15001/api/v2`}},testnet:!0}),pM=L({...oy,id:270,name:`ZKsync CLI Local Node`,network:`zksync-cli-local-node`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:3050`]}},testnet:!0}),mM=L({...oy,id:300,name:`ZKsync Sepolia Testnet`,network:`zksync-sepolia-testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.era.zksync.dev`],webSocket:[`wss://sepolia.era.zksync.dev/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia-era.zksync.network/`,apiUrl:`https://api-sepolia-era.zksync.network/api`},native:{name:`ZKsync Explorer`,url:`https://sepolia.explorer.zksync.io/`,blockExplorerApi:`https://block-explorer-api.sepolia.zksync.dev/api`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:3855712}},testnet:!0});var hM=1;const gM=L({...R,id:7777777,name:`Zora`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zora.energy`],webSocket:[`wss://rpc.zora.energy`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.zora.energy`,apiUrl:`https://explorer.zora.energy/api`}},contracts:{...R.contracts,l2OutputOracle:{[hM]:{address:`0x9E6204F750cD866b299594e2aC9eA824E2e5f95c`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:5882},portal:{[hM]:{address:`0x1a0ad011913A150f69f6A19DF447A0CfD9551054`}},l1StandardBridge:{[hM]:{address:`0x3e2Ea9B92B7E48A52296fD261dc26fd995284631`}}},sourceId:hM});var _M=11155111;const vM=L({...R,id:999999999,name:`Zora Sepolia`,network:`zora-sepolia`,nativeCurrency:{decimals:18,name:`Zora Sepolia`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia.rpc.zora.energy`],webSocket:[`wss://sepolia.rpc.zora.energy`]}},blockExplorers:{default:{name:`Zora Sepolia Explorer`,url:`https://sepolia.explorer.zora.energy/`,apiUrl:`https://sepolia.explorer.zora.energy/api`}},contracts:{...R.contracts,l2OutputOracle:{[_M]:{address:`0x2615B481Bd3E5A1C0C7Ca3Da1bdc663E8615Ade9`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:83160},portal:{[_M]:{address:`0xeffE2C6cA9Ab797D418f0D91eA60807713f3536f`}},l1StandardBridge:{[_M]:{address:`0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB`}}},sourceId:_M,testnet:!0});var yM=5;const bM=L({...R,id:999,name:`Zora Goerli Testnet`,nativeCurrency:{decimals:18,name:`Zora Goerli`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet.rpc.zora.energy`],webSocket:[`wss://testnet.rpc.zora.energy`]}},blockExplorers:{default:{name:`Explorer`,url:`https://testnet.explorer.zora.energy`,apiUrl:`https://testnet.explorer.zora.energy/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:189123},portal:{[yM]:{address:`0xDb9F51790365e7dc196e7D072728df39Be958ACe`}}},sourceId:yM,testnet:!0});var xM=c({abey:()=>Wv,abstract:()=>sy,abstractTestnet:()=>cy,acala:()=>ly,acria:()=>uy,adf:()=>dy,agungTestnet:()=>fy,aioz:()=>py,alephZero:()=>my,alephZeroTestnet:()=>hy,alienx:()=>gy,alienxHalTestnet:()=>_y,ancient8:()=>Ey,ancient8Sepolia:()=>Oy,anvil:()=>ky,apeChain:()=>Ay,apexTestnet:()=>jy,arbitrum:()=>My,arbitrumGoerli:()=>Ny,arbitrumNova:()=>Py,arbitrumSepolia:()=>Fy,arenaz:()=>Iy,areonNetwork:()=>Ly,areonNetworkTestnet:()=>Ry,areum:()=>zy,artelaTestnet:()=>By,arthera:()=>Vy,artheraTestnet:()=>Hy,assetChain:()=>Uy,assetChainTestnet:()=>Wy,astar:()=>Gy,astarZkEVM:()=>Ky,astarZkyoto:()=>qy,atletaOlympia:()=>Jy,aurora:()=>Yy,auroraTestnet:()=>Xy,auroria:()=>Zy,autheoTestnet:()=>Qy,avalanche:()=>$y,avalancheFuji:()=>eb,b3:()=>tb,b3Sepolia:()=>nb,bahamut:()=>rb,base:()=>ab,baseGoerli:()=>lb,basePreconf:()=>ob,baseSepolia:()=>db,baseSepoliaPreconf:()=>fb,basecampTestnet:()=>sb,beam:()=>pb,beamTestnet:()=>mb,bearNetworkChainMainnet:()=>hb,bearNetworkChainTestnet:()=>gb,berachain:()=>_b,berachainBepolia:()=>vb,berachainTestnet:()=>yb,berachainTestnetbArtio:()=>bb,bevmMainnet:()=>xb,bifrost:()=>Sb,birdlayer:()=>Cb,bitTorrent:()=>Ab,bitTorrentTestnet:()=>jb,bitgert:()=>wb,bitkub:()=>Tb,bitkubTestnet:()=>Eb,bitlayer:()=>Db,bitlayerTestnet:()=>Ob,bitrock:()=>kb,blast:()=>Nb,blastSepolia:()=>Pb,bob:()=>Ib,bobSepolia:()=>Bb,boba:()=>Lb,bobaSepolia:()=>Rb,boolBetaMainnet:()=>Vb,botanix:()=>Hb,botanixTestnet:()=>Ub,bounceBit:()=>Wb,bounceBitTestnet:()=>Gb,bronos:()=>Kb,bronosTestnet:()=>qb,bsc:()=>Jb,bscGreenfield:()=>Yb,bscTestnet:()=>Xb,bsquared:()=>Zb,bsquaredTestnet:()=>Qb,btr:()=>$b,btrTestnet:()=>ex,bxn:()=>tx,bxnTestnet:()=>nx,cannon:()=>rx,canto:()=>ix,celo:()=>yx,celoAlfajores:()=>xx,celoSepolia:()=>Cx,chang:()=>wx,chiliz:()=>Tx,chips:()=>Ex,citreaTestnet:()=>Dx,classic:()=>Ox,coinbit:()=>kx,coinex:()=>Ax,confluxESpace:()=>jx,confluxESpaceTestnet:()=>Mx,coreDao:()=>Nx,coreTestnet1:()=>Px,coreTestnet2:()=>Fx,corn:()=>Ix,cornTestnet:()=>Lx,crab:()=>Rx,creatorTestnet:()=>zx,creditCoin3Devnet:()=>Bx,creditCoin3Mainnet:()=>Vx,creditCoin3Testnet:()=>Hx,cronos:()=>Ux,cronosTestnet:()=>Wx,cronoszkEVM:()=>Gx,cronoszkEVMTestnet:()=>Kx,crossbell:()=>qx,crossfi:()=>Jx,curtis:()=>Yx,cyber:()=>Xx,cyberTestnet:()=>Zx,dailyNetwork:()=>Qx,dailyNetworkTestnet:()=>$x,darwinia:()=>eS,dbkchain:()=>tS,dchain:()=>nS,dchainTestnet:()=>rS,defichainEvm:()=>iS,defichainEvmTestnet:()=>aS,degen:()=>oS,dfk:()=>sS,diode:()=>cS,disChain:()=>lS,dodochainTestnet:()=>uS,dogechain:()=>dS,domaTestnet:()=>fS,donatuz:()=>pS,dosChain:()=>mS,dosChainTestnet:()=>hS,dreyerxMainnet:()=>gS,dreyerxTestnet:()=>_S,dustboyIoT:()=>vS,dymension:()=>yS,edexa:()=>bS,edexaTestnet:()=>xS,edgeless:()=>SS,edgelessTestnet:()=>CS,edgeware:()=>wS,edgewareTestnet:()=>TS,eduChain:()=>ES,eduChainTestnet:()=>DS,ekta:()=>OS,ektaTestnet:()=>kS,elastos:()=>AS,elastosTestnet:()=>jS,electroneum:()=>MS,electroneumTestnet:()=>NS,elysiumTestnet:()=>PS,energy:()=>FS,eni:()=>IS,eniTestnet:()=>LS,enuls:()=>RS,eon:()=>zS,eos:()=>BS,eosTestnet:()=>VS,eteria:()=>HS,etherlink:()=>US,etherlinkTestnet:()=>WS,ethernity:()=>GS,etp:()=>KS,evmos:()=>qS,evmosTestnet:()=>JS,excelonMainnet:()=>YS,expanse:()=>XS,exsat:()=>ZS,exsatTestnet:()=>QS,fantom:()=>$S,fantomSonicTestnet:()=>eC,fantomTestnet:()=>tC,fibo:()=>nC,filecoin:()=>rC,filecoinCalibration:()=>iC,filecoinHyperspace:()=>aC,fireChain:()=>Uv,flame:()=>oC,flare:()=>sC,flareTestnet:()=>cC,flowMainnet:()=>lC,flowPreviewnet:()=>uC,flowTestnet:()=>dC,fluence:()=>fC,fluenceStage:()=>pC,fluenceTestnet:()=>mC,fluentTestnet:()=>hC,form:()=>_C,formTestnet:()=>bC,forma:()=>vC,formicarium:()=>CE,forta:()=>xC,foundry:()=>SC,fraxtal:()=>wC,fraxtalTestnet:()=>EC,funkiMainnet:()=>OC,funkiSepolia:()=>AC,fuse:()=>jC,fuseSparknet:()=>MC,fusion:()=>NC,fusionTestnet:()=>PC,garnet:()=>IC,geist:()=>LC,genesys:()=>RC,giwaSepolia:()=>BC,glideL1Protocol:()=>VC,glideL2Protocol:()=>HC,gnosis:()=>UC,gnosisChiado:()=>WC,goChain:()=>qC,goat:()=>GC,gobi:()=>KC,godwoken:()=>JC,goerli:()=>YC,graphite:()=>XC,graphiteTestnet:()=>ZC,gravity:()=>QC,gunz:()=>$C,guruNetwork:()=>ew,guruTestnet:()=>tw,ham:()=>nw,happychainTestnet:()=>rw,haqqMainnet:()=>iw,haqqTestedge2:()=>aw,hardhat:()=>ow,harmonyOne:()=>sw,hashkey:()=>cw,hashkeyTestnet:()=>lw,haustTestnet:()=>uw,hedera:()=>dw,hederaPreviewnet:()=>fw,hederaTestnet:()=>pw,hela:()=>mw,hemi:()=>hw,hemiSepolia:()=>gw,holesky:()=>_w,hoodi:()=>vw,hpb:()=>yw,huddle01Mainnet:()=>bw,huddle01Testnet:()=>xw,humanity:()=>Sw,humanityTestnet:()=>Cw,humanode:()=>ww,humanodeTestnet5:()=>Tw,hychain:()=>Ew,hychainTestnet:()=>Dw,hyperliquidEvmTestnet:()=>Ow,iSunCoin:()=>Kw,icbNetwork:()=>kw,idchain:()=>Aw,immutableZkEvm:()=>jw,immutableZkEvmTestnet:()=>Mw,inEVM:()=>Nw,initVerse:()=>Pw,initVerseGenesis:()=>Fw,injective:()=>Iw,injectiveTestnet:()=>Lw,ink:()=>zw,inkSepolia:()=>Vw,iota:()=>Hw,iotaTestnet:()=>Uw,iotex:()=>Ww,iotexTestnet:()=>Gw,jbc:()=>qw,jbcTestnet:()=>Jw,jocMainnet:()=>Yw,jocTestnet:()=>Xw,jovay:()=>Zw,jovaySepolia:()=>Qw,juneo:()=>$w,juneoBCH1Chain:()=>eT,juneoDAI1Chain:()=>tT,juneoDOGE1Chain:()=>nT,juneoEUR1Chain:()=>rT,juneoGLD1Chain:()=>iT,juneoLINK1Chain:()=>aT,juneoLTC1Chain:()=>oT,juneoSGD1Chain:()=>cT,juneoSocotraTestnet:()=>lT,juneoUSD1Chain:()=>uT,juneoUSDT1Chain:()=>dT,juneomBTC1Chain:()=>sT,kaia:()=>fT,kairos:()=>pT,kakarotSepolia:()=>mT,kakarotStarknetSepolia:()=>hT,kardiaChain:()=>gT,karura:()=>_T,katana:()=>vT,kava:()=>yT,kavaTestnet:()=>bT,kcc:()=>xT,kiiTestnetOro:()=>ST,kinto:()=>CT,klaytn:()=>wT,klaytnBaobab:()=>TT,koi:()=>ET,kroma:()=>DT,kromaSepolia:()=>OT,l3x:()=>kT,l3xTestnet:()=>AT,lavita:()=>jT,lens:()=>MT,lensTestnet:()=>NT,lestnet:()=>PT,lightlinkPegasus:()=>FT,lightlinkPhoenix:()=>IT,linea:()=>BT,lineaGoerli:()=>VT,lineaSepolia:()=>HT,lineaTestnet:()=>UT,lisk:()=>GT,liskSepolia:()=>qT,loadAlphanet:()=>JT,localhost:()=>YT,loop:()=>XT,lukso:()=>ZT,luksoTestnet:()=>QT,lumiaMainnet:()=>$T,lumiaTestnet:()=>eE,lumoz:()=>tE,lumozTestnet:()=>nE,lycan:()=>rE,lyra:()=>iE,mainnet:()=>aE,mandala:()=>oE,manta:()=>sE,mantaSepoliaTestnet:()=>cE,mantaTestnet:()=>lE,mantle:()=>uE,mantleSepoliaTestnet:()=>dE,mantleTestnet:()=>fE,mantraDuKongEVMTestnet:()=>pE,mantraEVM:()=>mE,mapProtocol:()=>hE,matchain:()=>gE,matchainTestnet:()=>_E,mchVerse:()=>vE,megaethTestnet:()=>yE,mekong:()=>bE,meld:()=>xE,memecore:()=>SE,merlin:()=>wE,merlinErigonTestnet:()=>TE,metachain:()=>EE,metachainIstanbul:()=>DE,metadium:()=>OE,metalL2:()=>AE,meter:()=>jE,meterTestnet:()=>ME,metis:()=>NE,metisGoerli:()=>PE,metisSepolia:()=>FE,mev:()=>IE,mevTestnet:()=>LE,mint:()=>RE,mintSepoliaTestnet:()=>zE,mitosisTestnet:()=>BE,mode:()=>HE,modeTestnet:()=>WE,monadTestnet:()=>GE,moonbaseAlpha:()=>KE,moonbeam:()=>qE,moonbeamDev:()=>JE,moonriver:()=>YE,morph:()=>XE,morphHolesky:()=>ZE,morphSepolia:()=>QE,nahmii:()=>$E,nautilus:()=>eD,near:()=>tD,nearTestnet:()=>nD,neonDevnet:()=>rD,neonMainnet:()=>iD,neoxMainnet:()=>aD,neoxT4:()=>oD,newton:()=>sD,nexi:()=>cD,nexilix:()=>lD,nibiru:()=>uD,nitrographTestnet:()=>dD,oasisTestnet:()=>fD,oasys:()=>pD,odysseyTestnet:()=>mD,okc:()=>hD,omax:()=>gD,omni:()=>_D,omniOmega:()=>vD,oneWorld:()=>yD,oortMainnetDev:()=>bD,opBNB:()=>SD,opBNBTestnet:()=>wD,openledger:()=>TD,optimism:()=>DD,optimismGoerli:()=>kD,optimismSepolia:()=>jD,optopia:()=>MD,optopiaTestnet:()=>ND,orderly:()=>PD,orderlySepolia:()=>FD,otimDevnet:()=>ID,palm:()=>LD,palmTestnet:()=>RD,peaq:()=>zD,pgn:()=>VD,pgnTestnet:()=>UD,phoenix:()=>WD,planq:()=>GD,plasma:()=>KD,plasmaDevnet:()=>qD,plasmaTestnet:()=>JD,playfiAlbireo:()=>YD,plinga:()=>XD,plume:()=>ZD,plumeDevnet:()=>QD,plumeMainnet:()=>$D,plumeSepolia:()=>eO,plumeTestnet:()=>tO,polterTestnet:()=>nO,polygon:()=>rO,polygonAmoy:()=>iO,polygonMumbai:()=>aO,polygonZkEvm:()=>oO,polygonZkEvmCardona:()=>sO,polygonZkEvmTestnet:()=>cO,polynomial:()=>lO,polynomialSepolia:()=>uO,premiumBlockTestnet:()=>dO,pulsechain:()=>fO,pulsechainV4:()=>pO,pumpfiTestnet:()=>mO,pyrope:()=>gO,qMainnet:()=>vO,qTestnet:()=>yO,ql1:()=>_O,real:()=>bO,redbellyMainnet:()=>xO,redbellyTestnet:()=>SO,reddio:()=>CO,reddioSepolia:()=>wO,redstone:()=>EO,rei:()=>DO,reyaNetwork:()=>OO,riseTestnet:()=>kO,rivalz:()=>AO,rollux:()=>jO,rolluxTestnet:()=>MO,ronin:()=>NO,root:()=>PO,rootPorcini:()=>FO,rootstock:()=>IO,rootstockTestnet:()=>LO,rss3:()=>zO,rss3Sepolia:()=>VO,saakuru:()=>HO,saga:()=>UO,saigon:()=>WO,sanko:()=>GO,sapphire:()=>KO,sapphireTestnet:()=>qO,satoshiVM:()=>JO,satoshiVMTestnet:()=>YO,scroll:()=>XO,scrollSepolia:()=>ZO,sei:()=>QO,seiDevnet:()=>$O,seiTestnet:()=>tk,seismicDevnet:()=>ek,sepolia:()=>nk,shape:()=>ik,shapeSepolia:()=>ok,shardeum:()=>sk,shardeumSphinx:()=>ck,shibarium:()=>lk,shibariumTestnet:()=>uk,shiden:()=>dk,shimmer:()=>fk,shimmerTestnet:()=>pk,sidraChain:()=>mk,silicon:()=>hk,siliconSepolia:()=>gk,sixProtocol:()=>_k,skaleBlockBrawlers:()=>vk,skaleCalypso:()=>yk,skaleCalypsoTestnet:()=>bk,skaleCryptoBlades:()=>xk,skaleCryptoColosseum:()=>Sk,skaleEuropa:()=>Ck,skaleEuropaTestnet:()=>wk,skaleExorde:()=>Tk,skaleHumanProtocol:()=>Ek,skaleNebula:()=>Dk,skaleNebulaTestnet:()=>Ok,skaleRazor:()=>kk,skaleTitan:()=>Ak,skaleTitanTestnet:()=>jk,sketchpad:()=>Mk,snax:()=>Pk,snaxTestnet:()=>Ik,somniaTestnet:()=>Lk,soneium:()=>zk,soneiumMinato:()=>Vk,songbird:()=>Hk,songbirdTestnet:()=>Uk,sonic:()=>Wk,sonicBlazeTestnet:()=>Gk,sonicTestnet:()=>Kk,sophon:()=>qk,sophonTestnet:()=>Jk,sova:()=>Yk,sovaSepolia:()=>Xk,spicy:()=>Zk,statusNetworkSepolia:()=>Qk,statusSepolia:()=>Qk,step:()=>$k,story:()=>eA,storyAeneid:()=>tA,storyOdyssey:()=>nA,storyTestnet:()=>rA,stratis:()=>iA,superlumio:()=>aA,superposition:()=>oA,superseed:()=>cA,superseedSepolia:()=>uA,surgeTestnet:()=>dA,swan:()=>fA,swanProximaTestnet:()=>pA,swanSaturnTestnet:()=>mA,swellchain:()=>hA,swellchainTestnet:()=>gA,swissdlt:()=>_A,syscoin:()=>vA,syscoinTestnet:()=>yA,tac:()=>bA,tacSPB:()=>xA,taiko:()=>SA,taikoHekla:()=>CA,taikoJolnir:()=>wA,taikoKatla:()=>TA,taikoTestnetSepolia:()=>EA,taraxa:()=>DA,taraxaTestnet:()=>OA,teaSepolia:()=>kA,telcoinTestnet:()=>AA,telos:()=>jA,telosTestnet:()=>MA,tenet:()=>NA,ternoa:()=>PA,thaiChain:()=>FA,that:()=>IA,theta:()=>LA,thetaTestnet:()=>RA,thunderCore:()=>zA,thunderTestnet:()=>BA,tiktrixTestnet:()=>VA,tomb:()=>HA,treasure:()=>UA,treasureTopaz:()=>WA,tron:()=>GA,tronNile:()=>KA,tronShasta:()=>qA,ubiq:()=>JA,ultra:()=>YA,ultraTestnet:()=>XA,ultron:()=>ZA,ultronTestnet:()=>QA,unichain:()=>ej,unichainSepolia:()=>nj,unique:()=>rj,uniqueOpal:()=>ij,uniqueQuartz:()=>aj,unreal:()=>oj,vana:()=>sj,vanaMoksha:()=>cj,vanar:()=>lj,vechain:()=>uj,velas:()=>dj,viction:()=>fj,victionTestnet:()=>pj,vision:()=>mj,visionTestnet:()=>hj,wanchain:()=>gj,wanchainTestnet:()=>_j,weaveVMAlphanet:()=>vj,wemix:()=>yj,wemixTestnet:()=>bj,westendAssetHub:()=>xj,whitechain:()=>Sj,whitechainTestnet:()=>Cj,wmcTestnet:()=>wj,worldLand:()=>kj,worldchain:()=>Ej,worldchainSepolia:()=>Oj,x1Testnet:()=>Fj,xLayer:()=>Pj,xLayerTestnet:()=>Fj,xai:()=>Aj,xaiTestnet:()=>jj,xdc:()=>Mj,xdcTestnet:()=>Nj,xphereMainnet:()=>Ij,xphereTestnet:()=>Lj,xrOne:()=>Rj,xrSepolia:()=>Vj,xrplevmDevnet:()=>zj,xrplevmTestnet:()=>Bj,yooldoVerse:()=>Hj,yooldoVerseTestnet:()=>Uj,zenchainTestnet:()=>Wj,zeniq:()=>Gj,zeroG:()=>Bv,zeroGGalileoTestnet:()=>Vv,zeroGMainnet:()=>Hv,zeroNetwork:()=>Kj,zetachain:()=>qj,zetachainAthensTestnet:()=>Jj,zhejiang:()=>Yj,zilliqa:()=>Xj,zilliqaTestnet:()=>Zj,zircuit:()=>$j,zircuitGarfieldTestnet:()=>tM,zircuitTestnet:()=>rM,zkFair:()=>iM,zkFairTestnet:()=>aM,zkLinkNova:()=>oM,zkLinkNovaSepoliaTestnet:()=>sM,zkSync:()=>cM,zkSyncInMemoryNode:()=>lM,zkSyncLocalNode:()=>pM,zkSyncSepoliaTestnet:()=>mM,zksync:()=>cM,zksyncInMemoryNode:()=>lM,zksyncLocalCustomHyperchain:()=>uM,zksyncLocalHyperchain:()=>dM,zksyncLocalHyperchainL1:()=>fM,zksyncLocalNode:()=>pM,zksyncSepoliaTestnet:()=>mM,zora:()=>gM,zoraSepolia:()=>vM,zoraTestnet:()=>bM}),SM=c({arbitrum:()=>My,arbitrumSepolia:()=>Fy,base:()=>ab,baseSepolia:()=>db,berachain:()=>_b,berachainBepolia:()=>vb,bsc:()=>Jb,celo:()=>yx,gnosis:()=>UC,hoodi:()=>vw,katana:()=>vT,mainnet:()=>aE,optimism:()=>DD,optimismSepolia:()=>jD,polygon:()=>rO,sepolia:()=>nk});const CM=[ab,...Object.values(SM).filter(e=>e&&e.id!==ab.id)],wM=ky;({...wM}),{...wM};var TM=u(s(((e,t)=>{var n=Object.prototype.hasOwnProperty,r=`~`;function i(){}Object.create&&(i.prototype=Object.create(null),new i().__proto__||(r=!1));function a(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function o(e,t,n,i,o){if(typeof n!=`function`)throw TypeError(`The listener must be a function`);var s=new a(n,i||e,o),c=r?r+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],s]:e._events[c].push(s):(e._events[c]=s,e._eventsCount++),e}function s(e,t){--e._eventsCount===0?e._events=new i:delete e._events[t]}function c(){this._events=new i,this._eventsCount=0}c.prototype.eventNames=function(){var e=[],t,i;if(this._eventsCount===0)return e;for(i in t=this._events)n.call(t,i)&&e.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?e.concat(Object.getOwnPropertySymbols(t)):e},c.prototype.listeners=function(e){var t=r?r+e:e,n=this._events[t];if(!n)return[];if(n.fn)return[n.fn];for(var i=0,a=n.length,o=Array(a);i`IntersectionObserver`in window&&`IntersectionObserverEntry`in window&&`intersectionRatio`in IntersectionObserverEntry.prototype&&`isVisible`in IntersectionObserverEntry.prototype;function oN(e={}){let{prefix:t=`[Porto]`}=e,n=new Set;return{error:cN(console.error,{prefix:t}),errorOnce:cN(console.error,{memo:n,prefix:t}),log:cN(console.log,{prefix:t}),logOnce:cN(console.log,{memo:n,prefix:t}),warn:cN(console.warn,{prefix:t}),warnOnce:cN(console.warn,{memo:n,prefix:t})}}const sN=oN();function cN(e,t={}){let{memo:n,prefix:r}=t;return(...t)=>{let i=t.join(` `);n?.has(i)||(n?.add(i),e(`${r} ${i}`))}}function lN(){let e=navigator.userAgent.toLowerCase();return e.includes(`safari`)&&!e.includes(`chrome`)}function uN(){let e=navigator.userAgent.toLowerCase();return(e.includes(`firefox`)||e.includes(`fxios`))&&!e.includes(`seamonkey`)}function dN(){return window.navigator?.userAgentData?.mobile?!0:navigator.maxTouchPoints>1||/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(navigator.userAgent)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(navigator.userAgent.slice(0,4))}function fN(){let e=()=>void 0,t=()=>void 0;return{promise:new Promise((n,r)=>{e=n,t=r}),reject:t,resolve:e}}function pN(e){if(Array.isArray(e))return e.map(pN);if(typeof e==`function`)return;if(typeof e!=`object`||!e)return e;if(Object.getPrototypeOf(e)!==Object.prototype)try{return structuredClone(e)}catch{return}let t={};for(let[n,r]of Object.entries(e))t[n]=pN(r);return t}function mN(e,t){let n=[],r=new Set;for(let i of e){let e=t(i);r.has(e)||(r.add(e),n.push(i))}return n}function hN(){return typeof globalThis<`u`&&`crypto`in globalThis?globalThis.crypto.randomUUID():crypto.randomUUID()}function gN(e,{enabled:t=!0,id:n}){if(!t||!n)return e();if(gN.cache.get(n))return gN.cache.get(n);let r=e().finally(()=>gN.cache.delete(n));return gN.cache.set(n,r),r}(function(e){e.cache=new Map})(gN||={});function _N(e){return e}function vN(e,t={}){let{targetOrigin:n}=t,r=new Map;return _N({destroy(){for(let t of r.values())e.removeEventListener(`message`,t)},on(t,i,a){function o(e){e.data.topic===t&&(a&&e.data.id!==a||n&&e.origin!==n||i(e.data.payload,e))}return e.addEventListener(`message`,o),r.set(t,o),()=>e.removeEventListener(`message`,o)},async send(t,r,i){let a=hN();return e.postMessage(pN({id:a,payload:r,topic:t}),i??n??`*`),{id:a,payload:r,topic:t}},async sendAsync(e,t,n){let{id:r}=await this.send(e,t,n);return new Promise(t=>this.on(e,t,r))}})}function yN(e){let{from:t,to:n,waitForReady:r=!1}=e,i=!1,a=fN();t.on(`ready`,a.resolve);let o=_N({destroy(){t.destroy(),n.destroy(),i&&a.reject()},on(e,n,r){return t.on(e,n,r)},async send(e,t){return i=!0,r&&await a.promise.finally(()=>i=!1),n.send(e,t)},async sendAsync(e,t){return i=!0,r&&await a.promise.finally(()=>i=!1),n.sendAsync(e,t)}});return{...o,ready(e){o.send(`ready`,e)},waitForReady(){return a.promise}}}const bN={local:`http://localhost:5175/dialog/`,prod:`https://id.porto.sh/dialog`,stg:`https://stg.id.porto.sh/dialog`};function xN(e){return e}function SN(e={}){let{skipProtocolCheck:t,skipUnsupported:n}=e,r=e=>!n&&lN()&&e?.some(e=>[`wallet_connect`,`eth_requestAccounts`].includes(e.method));return typeof window>`u`?wN():xN({name:`iframe`,setup(e){let{host:n,internal:i,theme:a,themeController:o}=e,{store:s}=i,c=CN().setup(e),l=!1,u=new URL(n),d=document.createElement(`dialog`);d.dataset.porto=``,d.setAttribute(`role`,`dialog`),d.setAttribute(`aria-closed`,`true`),d.setAttribute(`aria-label`,`Porto Wallet`),d.setAttribute(`hidden`,`until-found`),Object.assign(d.style,{background:`transparent`,border:`0`,outline:`0`,padding:`0`,position:`fixed`}),document.body.appendChild(d);let f=document.createElement(`iframe`);f.setAttribute(`data-testid`,`porto`);let p=[`payment`,`publickey-credentials-get ${u.origin}`,`publickey-credentials-create ${u.origin}`];uN()||p.push(`clipboard-write`),f.setAttribute(`allow`,p.join(`; `)),f.setAttribute(`tabindex`,`0`),f.setAttribute(`sandbox`,`allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox`),f.setAttribute(`src`,AN(n)),f.setAttribute(`title`,`Porto`),Object.assign(f.style,{backgroundColor:`transparent`,border:`0`,colorScheme:`light dark`,height:`100%`,left:`0`,position:`fixed`,top:`0`,width:`100%`});let m=document.createElement(`style`);m.innerHTML=` +`+e.stack}}var Pe=Object.prototype.hasOwnProperty,Fe=t.unstable_scheduleCallback,Ie=t.unstable_cancelCallback,Le=t.unstable_shouldYield,Re=t.unstable_requestPaint,ze=t.unstable_now,Be=t.unstable_getCurrentPriorityLevel,Ve=t.unstable_ImmediatePriority,He=t.unstable_UserBlockingPriority,Ue=t.unstable_NormalPriority,We=t.unstable_LowPriority,Ge=t.unstable_IdlePriority,Ke=t.log,qe=t.unstable_setDisableYieldValue,Je=null,Ye=null;function Xe(e){if(typeof Ke==`function`&&qe(e),Ye&&typeof Ye.setStrictMode==`function`)try{Ye.setStrictMode(Je,e)}catch{}}var Ze=Math.clz32?Math.clz32:et,Qe=Math.log,$e=Math.LN2;function et(e){return e>>>=0,e===0?32:31-(Qe(e)/$e|0)|0}var tt=256,nt=262144,rt=4194304;function it(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function at(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=it(n))):i=it(o):i=it(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=it(n))):i=it(o)):i=it(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function ot(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function st(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ct(){var e=rt;return rt<<=1,!(rt&62914560)&&(rt=4194304),e}function lt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function ut(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function dt(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),xn=!1;if(bn)try{var Sn={};Object.defineProperty(Sn,`passive`,{get:function(){xn=!0}}),window.addEventListener(`test`,Sn,Sn),window.removeEventListener(`test`,Sn,Sn)}catch{xn=!1}var Cn=null,wn=null,Tn=null;function En(){if(Tn)return Tn;var e,t=wn,n=t.length,r,i=`value`in Cn?Cn.value:Cn.textContent,a=i.length;for(e=0;e=nr),ar=` `,or=!1;function sr(e,t){switch(e){case`keyup`:return er.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function cr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var lr=!1;function ur(e,t){switch(e){case`compositionend`:return cr(t);case`keypress`:return t.which===32?(or=!0,ar):null;case`textInput`:return e=t.data,e===ar&&or?null:e;default:return null}}function dr(e,t){if(lr)return e===`compositionend`||!tr&&sr(e,t)?(e=En(),Tn=wn=Cn=null,lr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Nr(n)}}function Fr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Fr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Ir(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=qt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=qt(e.document)}return t}function Lr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Rr=bn&&`documentMode`in document&&11>=document.documentMode,zr=null,Br=null,Vr=null,Hr=!1;function Ur(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Hr||zr==null||zr!==qt(r)||(r=zr,`selectionStart`in r&&Lr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Vr&&Mr(Vr,r)||(Vr=r,r=Gd(Br,`onSelect`),0>=o,i-=o,Fi=1<<32-Ze(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),Wi&&Li(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),Wi&&Li(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return Wi&&Li(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),Wi&&Li(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===ie&&Ra(l)===r.type){n(e,r.sibling),c=a(r,o.props),Ga(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=Si(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=xi(o.type,o.key,o.props,null,e.mode,c),Ga(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=Ti(o,e.mode,c),c.return=e,e=c}return s(e);case ie:return o=Ra(o),b(e,r,o,c)}if(de(o))return h(e,r,o,c);if(ce(o)){if(l=ce(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Wa(o),c);if(o.$$typeof===ee)return b(e,r,fa(e,o),c);Ka(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=Ci(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Ua=0;var i=b(e,t,n,r);return Ha=null,i}catch(t){if(t===Ma||t===Pa)throw t;var a=_i(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Ja=qa(!0),Ya=qa(!1),Xa=!1;function Za(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Qa(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function $a(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function eo(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,eu&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=mi(e),pi(e,null,n),t}return ui(e,r,t,n),mi(e)}function to(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,pt(e,n)}}function no(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var ro=!1;function io(){if(ro){var e=Ca;if(e!==null)throw e}}function ao(e,t,n,r){ro=!1;var i=e.updateQueue;Xa=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,m=f!==s.lane;if(m?(M&f)===f:(r&f)===f){f!==0&&f===Sa&&(ro=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=p({},d,f);break a;case 2:Xa=!0}}f=s.callback,f!==null&&(e.flags|=64,m&&(e.flags|=8192),m=i.callbacks,m===null?i.callbacks=[f]:m.push(f))}else m={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=m,c=d):u=u.next=m,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;m=s,s=m.next,m.next=null,i.lastBaseUpdate=m,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),lu|=o,e.lanes=o,e.memoizedState=d}}function oo(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function so(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=w.T,s={};w.T=s,qs(e,!1,t,n);try{var c=i(),l=w.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ks(e,t,Ea(c,r),ju(e)):Ks(e,t,r,ju(e))}catch(n){Ks(e,t,{then:function(){},status:`rejected`,reason:n},ju())}finally{fe.p=a,o!==null&&s.types!==null&&(o.types=s.types),w.T=o}}function Is(){}function Ls(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Rs(e).queue;Fs(e,a,t,pe,n===null?Is:function(){return zs(e),n(r)})}function Rs(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:pe,baseState:pe,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jo,lastRenderedState:pe},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function zs(e){var t=Rs(e);t.next===null&&(t=e.alternate.memoizedState),Ks(e,t.next.queue,{},ju())}function Bs(){return da(hp)}function Vs(){return Uo().memoizedState}function Hs(){return Uo().memoizedState}function Us(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=ju();e=$a(n);var r=eo(t,e,n);r!==null&&(Nu(r,t,n),to(r,t,n)),t={cache:va()},e.payload=t;return}t=t.return}}function Ws(e,t,n){var r=ju();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Js(e)?Ys(t,n):(n=di(e,t,n,r),n!==null&&(Nu(n,e,r),Xs(n,t,r)))}function Gs(e,t,n){Ks(e,t,n,ju())}function Ks(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Js(e))Ys(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,jr(s,o))return ui(e,t,i,0),tu===null&&li(),!1}catch{}if(n=di(e,t,i,r),n!==null)return Nu(n,e,r),Xs(n,t,r),!0}return!1}function qs(e,t,n,r){if(r={lane:2,revertLane:Ad(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Js(e)){if(t)throw Error(i(479))}else t=di(e,n,r,2),t!==null&&Nu(t,e,2)}function Js(e){var t=e.alternate;return e===A||t!==null&&t===A}function Ys(e,t){Do=Eo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Xs(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,pt(e,n)}}var Zs={readContext:da,use:Ko,useCallback:No,useContext:No,useEffect:No,useImperativeHandle:No,useLayoutEffect:No,useInsertionEffect:No,useMemo:No,useReducer:No,useRef:No,useState:No,useDebugValue:No,useDeferredValue:No,useTransition:No,useSyncExternalStore:No,useId:No,useHostTransitionStatus:No,useFormState:No,useActionState:No,useOptimistic:No,useMemoCache:No,useCacheRefresh:No};Zs.useEffectEvent=No;var Qs={readContext:da,use:Ko,useCallback:function(e,t){return Ho().memoizedState=[e,t===void 0?null:t],e},useContext:da,useEffect:Ss,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),bs(4194308,4,Os.bind(null,t,e),n)},useLayoutEffect:function(e,t){return bs(4194308,4,e,t)},useInsertionEffect:function(e,t){bs(4,2,e,t)},useMemo:function(e,t){var n=Ho();t=t===void 0?null:t;var r=e();if(Oo){Xe(!0);try{e()}finally{Xe(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Ho();if(n!==void 0){var i=n(t);if(Oo){Xe(!0);try{n(t)}finally{Xe(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=Ws.bind(null,A,e),[r.memoizedState,e]},useRef:function(e){var t=Ho();return e={current:e},t.memoizedState=e},useState:function(e){e=is(e);var t=e.queue,n=Gs.bind(null,A,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:As,useDeferredValue:function(e,t){return Ns(Ho(),e,t)},useTransition:function(){var e=is(!1);return e=Fs.bind(null,A,e.queue,!0,!1),Ho().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=A,a=Ho();if(Wi){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),tu===null)throw Error(i(349));M&127||$o(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,Ss(ts.bind(null,r,o,e),[e]),r.flags|=2048,vs(9,{destroy:void 0},es.bind(null,r,o,n,t),null),n},useId:function(){var e=Ho(),t=tu.identifierPrefix;if(Wi){var n=Ii,r=Fi;n=(r&~(1<<32-Ze(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=ko++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[bt]=t,o[xt]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch($d(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Kc(t)}}return Zc(t),qc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Kc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=xe.current,Zi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Hi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[bt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Zd(e.nodeValue,n)),e||Ji(t,!0)}else e=of(e).createTextNode(r),e[bt]=t,t.stateNode=e}return Zc(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Zi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[bt]=t}else Qi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Zc(t),e=!1}else n=$i(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(bo(t),t):(bo(t),null);if(t.flags&128)throw Error(i(558))}return Zc(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=Zi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[bt]=t}else Qi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Zc(t),a=!1}else a=$i(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(bo(t),t):(bo(t),null)}return bo(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Yc(t,t.updateQueue),Zc(t),null);case 4:return we(),e===null&&Vd(t.stateNode.containerInfo),Zc(t),null;case 10:return aa(t.type),Zc(t),null;case 19:if(_e(xo),r=t.memoizedState,r===null)return Zc(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Xc(r,!1);else{if(cu!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=So(e),o!==null){for(t.flags|=128,Xc(r,!1),e=o.updateQueue,t.updateQueue=e,Yc(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)bi(n,e),n=n.sibling;return ve(xo,xo.current&1|2),Wi&&Li(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&ze()>yu&&(t.flags|=128,a=!0,Xc(r,!1),t.lanes=4194304)}else{if(!a)if(e=So(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Yc(t,e),Xc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!Wi)return Zc(t),null}else 2*ze()-r.renderingStartTime>yu&&n!==536870912&&(t.flags|=128,a=!0,Xc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(Zc(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=ze(),e.sibling=null,n=xo.current,ve(xo,a?n&1|2:n&1),Wi&&Li(t,r.treeForkCount),e);case 22:case 23:return bo(t),po(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(Zc(t),t.subtreeFlags&6&&(t.flags|=8192)):Zc(t),n=t.updateQueue,n!==null&&Yc(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&_e(Oa),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),aa(_a),Zc(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function $c(e,t){switch(Bi(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return aa(_a),we(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Ee(t),null;case 31:if(t.memoizedState!==null){if(bo(t),t.alternate===null)throw Error(i(340));Qi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(bo(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Qi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return _e(xo),null;case 4:return we(),null;case 10:return aa(t.type),null;case 22:case 23:return bo(t),po(),e!==null&&_e(Oa),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return aa(_a),null;case 25:return null;default:return null}}function el(e,t){switch(Bi(t),t.tag){case 3:aa(_a),we();break;case 26:case 27:case 5:Ee(t);break;case 4:we();break;case 31:t.memoizedState!==null&&bo(t);break;case 13:bo(t);break;case 19:_e(xo);break;case 10:aa(t.type);break;case 22:case 23:bo(t),po(),e!==null&&_e(Oa);break;case 24:aa(_a)}}function tl(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){ld(t,t.return,e)}}function nl(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){ld(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){ld(t,t.return,e)}}function rl(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{so(t,n)}catch(t){ld(e,e.return,t)}}}function il(e,t,n){n.props=ac(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){ld(e,t,n)}}function al(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){ld(e,t,n)}}function ol(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){ld(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){ld(e,t,n)}else n.current=null}function sl(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){ld(e,e.return,t)}}function cl(e,t,n){try{var r=e.stateNode;ef(r,e.type,n,t),r[xt]=t}catch(t){ld(e,e.return,t)}}function ll(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&_f(e.type)||e.tag===4}function ul(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||ll(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&_f(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function dl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=dn));else if(r!==4&&(r===27&&_f(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(dl(e,t,n),e=e.sibling;e!==null;)dl(e,t,n),e=e.sibling}function fl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&_f(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(fl(e,t,n),e=e.sibling;e!==null;)fl(e,t,n),e=e.sibling}function pl(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);$d(t,r,n),t[bt]=e,t[xt]=n}catch(t){ld(e,e.return,t)}}var ml=!1,hl=!1,gl=!1,_l=typeof WeakSet==`function`?WeakSet:Set,vl=null;function yl(e,t){if(e=e.containerInfo,rf=wp,e=Ir(e),Lr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(af={focusedElem:e,selectionRange:n},wp=!1,vl=t;vl!==null;)if(t=vl,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,vl=e;else for(;vl!==null;){switch(t=vl,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),$d(o,r,n),o[bt]=e,Mt(o),r=o;break a;case`link`:var s=ip(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Pr(s,h),v=Pr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,w.T=null,n=Du,Du=null;var o=Cu,s=Tu;if(Su=0,wu=Cu=null,Tu=0,eu&6)throw Error(i(331));var c=eu;if(eu|=4,Yl(o.current),Vl(o,o.current,s,n),eu=c,Cd(0,!1),Ye&&typeof Ye.onPostCommitFiberRoot==`function`)try{Ye.onPostCommitFiberRoot(Je,o)}catch{}return!0}finally{fe.p=a,w.T=r,ad(e,t)}}function cd(e,t,n){t=Di(n,t),t=dc(e.stateNode,t,2),e=eo(e,t,2),e!==null&&(ut(e,2),Sd(e))}function ld(e,t,n){if(e.tag===3)cd(e,e,n);else for(;t!==null;){if(t.tag===3){cd(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(xu===null||!xu.has(r))){e=Di(n,e),n=fc(2),r=eo(t,n,2),r!==null&&(pc(n,r,t,e),ut(r,2),Sd(r));break}}t=t.return}}function ud(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new $l;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(ou=!0,i.add(n),e=dd.bind(null,e,t,n),t.then(e,e))}function dd(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,tu===e&&(M&n)===n&&(cu===4||cu===3&&(M&62914560)===M&&300>ze()-_u?!(eu&2)&&Bu(e,0):du|=n,pu===M&&(pu=0)),Sd(e)}function fd(e,t){t===0&&(t=ct()),e=fi(e,t),e!==null&&(ut(e,t),Sd(e))}function pd(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),fd(e,n)}function md(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),fd(e,n)}function hd(e,t){return Fe(e,t)}var gd=null,_d=null,vd=!1,yd=!1,bd=!1,xd=0;function Sd(e){e!==_d&&e.next===null&&(_d===null?gd=_d=e:_d=_d.next=e),yd=!0,vd||(vd=!0,kd())}function Cd(e,t){if(!bd&&yd){bd=!0;do for(var n=!1,r=gd;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ze(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,Od(r,a))}else a=M,a=at(r,r===tu?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||ot(r,a)||(n=!0,Od(r,a));r=r.next}while(n);bd=!1}}function wd(){Td()}function Td(){yd=vd=!1;var e=0;xd!==0&&df()&&(e=xd);for(var t=ze(),n=null,r=gd;r!==null;){var i=r.next,a=Ed(r,t);a===0?(r.next=null,n===null?gd=i:n.next=i,i===null&&(_d=n)):(n=r,(e!==0||a&3)&&(yd=!0)),r=i}Su!==0&&Su!==5||Cd(e,!1),xd!==0&&(xd=0)}function Ed(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&tf(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function Bf(e,t,n){var r=zf;if(r&&typeof t==`string`&&t){var i=Yt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),Pf.has(i)||(Pf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),$d(t,`link`,e),Mt(t),r.head.appendChild(t)))}}function Vf(e){If.D(e),Bf(`dns-prefetch`,e,null)}function Hf(e,t){If.C(e,t),Bf(`preconnect`,e,t)}function Uf(e,t,n){If.L(e,t,n);var r=zf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Yt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Yt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Yt(n.imageSizes)+`"]`)):i+=`[href="`+Yt(e)+`"]`;var a=i;switch(t){case`style`:a=Jf(e);break;case`script`:a=I(e)}Nf.has(a)||(e=p({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),Nf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(Yf(a))||t===`script`&&r.querySelector(Qf(a))||(t=r.createElement(`link`),$d(t,`link`,e),Mt(t),r.head.appendChild(t)))}}function Wf(e,t){If.m(e,t);var n=zf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Yt(r)+`"][href="`+Yt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=I(e)}if(!Nf.has(a)&&(e=p({rel:`modulepreload`,href:e},t),Nf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Qf(a)))return}r=n.createElement(`link`),$d(r,`link`,e),Mt(r),n.head.appendChild(r)}}}function Gf(e,t,n){If.S(e,t,n);var r=zf;if(r&&e){var i=jt(r).hoistableStyles,a=Jf(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(Yf(a)))s.loading=5;else{e=p({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=Nf.get(a))&&tp(e,n);var c=o=r.createElement(`link`);Mt(c),$d(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,ep(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Kf(e,t){If.X(e,t);var n=zf;if(n&&e){var r=jt(n).hoistableScripts,i=I(e),a=r.get(i);a||(a=n.querySelector(Qf(i)),a||(e=p({src:e,async:!0},t),(t=Nf.get(i))&&np(e,t),a=n.createElement(`script`),Mt(a),$d(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function qf(e,t){If.M(e,t);var n=zf;if(n&&e){var r=jt(n).hoistableScripts,i=I(e),a=r.get(i);a||(a=n.querySelector(Qf(i)),a||(e=p({src:e,async:!0,type:`module`},t),(t=Nf.get(i))&&np(e,t),a=n.createElement(`script`),Mt(a),$d(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function F(e,t,n,r){var a=(a=xe.current)?Ff(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Jf(n.href),n=jt(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Jf(n.href);var o=jt(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(Yf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),Nf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},Nf.set(e,n),o||Zf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=I(n),n=jt(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Jf(e){return`href="`+Yt(e)+`"`}function Yf(e){return`link[rel="stylesheet"][`+e+`]`}function Xf(e){return p({},e,{"data-precedence":e.precedence,precedence:null})}function Zf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),$d(t,`link`,n),Mt(t),e.head.appendChild(t))}function I(e){return`[src="`+Yt(e)+`"]`}function Qf(e){return`script[async]`+e}function $f(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Yt(n.href)+`"]`);if(r)return t.instance=r,Mt(r),r;var a=p({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),Mt(r),$d(r,`style`,a),ep(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Jf(n.href);var o=e.querySelector(Yf(a));if(o)return t.state.loading|=4,t.instance=o,Mt(o),o;r=Xf(n),(a=Nf.get(a))&&tp(r,a),o=(e.ownerDocument||e).createElement(`link`),Mt(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),$d(o,`link`,r),t.state.loading|=4,ep(o,n.precedence,e),t.instance=o;case`script`:return o=I(n.src),(a=e.querySelector(Qf(o)))?(t.instance=a,Mt(a),a):(r=n,(a=Nf.get(o))&&(r=p({},n),np(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),Mt(a),$d(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,ep(r,n.precedence,e));return t.instance}function ep(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function op(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function sp(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function cp(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Jf(r.href),a=t.querySelector(Yf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=dp.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,Mt(a);return}a=t.ownerDocument||t,r=Xf(r),(i=Nf.get(i))&&tp(r,i),a=a.createElement(`link`),Mt(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),$d(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=dp.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var lp=0;function up(e,t){return e.stylesheets&&e.count===0&&pp(e,e.stylesheets),0lp?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function dp(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)pp(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var fp=null;function pp(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,fp=new Map,t.forEach(mp,e),fp=null,dp.call(e))}function mp(e,t){if(!(t.state.loading&4)){var n=fp.get(e);if(n)var r=n.get(null);else{n=new Map,fp.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=_()}))(),y=f(),b,x=o((()=>{b=`1.2.3`})),S,ee=o((()=>{x(),S=class e extends Error{constructor(t,n={}){let r=n.cause instanceof e?n.cause.details:n.cause?.message?n.cause.message:n.details,i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=[t||`An error occurred.`,``,...n.metaMessages?[...n.metaMessages,``]:[],...i?[`Docs: https://abitype.dev${i}`]:[],...r?[`Details: ${r}`]:[],`Version: abitype@${b}`].join(` +`);super(a),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`metaMessages`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiTypeError`}),n.cause&&(this.cause=n.cause),this.details=r,this.docsPath=i,this.metaMessages=n.metaMessages,this.shortMessage=t}}}));function C(e,t){return e.exec(t)?.groups}var te,ne,re,ie=o((()=>{te=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,ne=/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/,re=/^\(.+?\).*?$/}));function ae(e){let t=e.type;if(oe.test(e.type)&&`components`in e){t=`(`;let n=e.components.length;for(let r=0;r{ie(),oe=/^tuple(?(\[(\d*)\])*)$/}));function ce(e){let t=``,n=e.length;for(let r=0;r{se()}));function ue(e){return e.type===`function`?`function ${e.name}(${ce(e.inputs)})${e.stateMutability&&e.stateMutability!==`nonpayable`?` ${e.stateMutability}`:``}${e.outputs?.length?` returns (${ce(e.outputs)})`:``}`:e.type===`event`?`event ${e.name}(${ce(e.inputs)})`:e.type===`error`?`error ${e.name}(${ce(e.inputs)})`:e.type===`constructor`?`constructor(${ce(e.inputs)})${e.stateMutability===`payable`?` payable`:``}`:e.type===`fallback`?`fallback() external${e.stateMutability===`payable`?` payable`:``}`:`receive() external payable`}var de=o((()=>{le()}));function w(e){return we.test(e)}function fe(e){return C(we,e)}function pe(e){return Te.test(e)}function me(e){return C(Te,e)}function he(e){return Ee.test(e)}function ge(e){return C(Ee,e)}function _e(e){return De.test(e)}function ve(e){return C(De,e)}function ye(e){return Oe.test(e)}function be(e){return C(Oe,e)}function xe(e){return ke.test(e)}function Se(e){return C(ke,e)}function Ce(e){return Ae.test(e)}var we,Te,Ee,De,Oe,ke,Ae,je,Me,Ne,Pe=o((()=>{ie(),we=/^error (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)$/,Te=/^event (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)$/,Ee=/^function (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)(?: (?external|public{1}))?(?: (?pure|view|nonpayable|payable{1}))?(?: returns\s?\((?.*?)\))?$/,De=/^struct (?[a-zA-Z$_][a-zA-Z0-9$_]*) \{(?.*?)\}$/,Oe=/^constructor\((?.*?)\)(?:\s(?payable{1}))?$/,ke=/^fallback\(\) external(?:\s(?payable{1}))?$/,Ae=/^receive\(\) external payable$/,je=new Set([`memory`,`indexed`,`storage`,`calldata`]),Me=new Set([`indexed`]),Ne=new Set([`calldata`,`memory`,`storage`])})),Fe,Ie,Le,Re=o((()=>{ee(),Fe=class extends S{constructor({signature:e}){super(`Failed to parse ABI item.`,{details:`parseAbiItem(${JSON.stringify(e,null,2)})`,docsPath:`/api/human#parseabiitem-1`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiItemError`})}},Ie=class extends S{constructor({type:e}){super(`Unknown type.`,{metaMessages:[`Type "${e}" is not a valid ABI type. Perhaps you forgot to include a struct signature?`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownTypeError`})}},Le=class extends S{constructor({type:e}){super(`Unknown type.`,{metaMessages:[`Type "${e}" is not a valid ABI type.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownSolidityTypeError`})}}})),ze,Be,Ve,He,Ue,We,Ge=o((()=>{ee(),ze=class extends S{constructor({params:e}){super(`Failed to parse ABI parameters.`,{details:`parseAbiParameters(${JSON.stringify(e,null,2)})`,docsPath:`/api/human#parseabiparameters-1`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiParametersError`})}},Be=class extends S{constructor({param:e}){super(`Invalid ABI parameter.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidParameterError`})}},Ve=class extends S{constructor({param:e,name:t}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`"${t}" is a protected Solidity keyword. More info: https://docs.soliditylang.org/en/latest/cheatsheet.html`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SolidityProtectedKeywordError`})}},He=class extends S{constructor({param:e,type:t,modifier:n}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`Modifier "${n}" not allowed${t?` in "${t}" type`:``}.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidModifierError`})}},Ue=class extends S{constructor({param:e,type:t,modifier:n}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`Modifier "${n}" not allowed${t?` in "${t}" type`:``}.`,`Data location can only be specified for array, struct, or mapping types, but "${n}" was given.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidFunctionModifierError`})}},We=class extends S{constructor({abiParameter:e}){super(`Invalid ABI parameter.`,{details:JSON.stringify(e,null,2),metaMessages:[`ABI parameter type is invalid.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiTypeParameterError`})}}})),Ke,qe,Je,Ye=o((()=>{ee(),Ke=class extends S{constructor({signature:e,type:t}){super(`Invalid ${t} signature.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidSignatureError`})}},qe=class extends S{constructor({signature:e}){super(`Unknown signature.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownSignatureError`})}},Je=class extends S{constructor({signature:e}){super(`Invalid struct signature.`,{details:e,metaMessages:[`No properties exist.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidStructSignatureError`})}}})),Xe,Ze=o((()=>{ee(),Xe=class extends S{constructor({type:e}){super(`Circular reference detected.`,{metaMessages:[`Struct "${e}" is a circular reference.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`CircularReferenceError`})}}})),Qe,$e=o((()=>{ee(),Qe=class extends S{constructor({current:e,depth:t}){super(`Unbalanced parentheses.`,{metaMessages:[`"${e.trim()}" has too many ${t>0?`opening`:`closing`} parentheses.`],details:`Depth "${t}"`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidParenthesisError`})}}}));function et(e,t,n){let r=``;if(n)for(let e of Object.entries(n)){if(!e)continue;let t=``;for(let n of e[1])t+=`[${n.type}${n.name?`:${n.name}`:``}]`;r+=`(${e[0]}{${t}})`}return t?`${t}:${e}${r}`:`${e}${r}`}var tt,nt=o((()=>{tt=new Map([[`address`,{type:`address`}],[`bool`,{type:`bool`}],[`bytes`,{type:`bytes`}],[`bytes32`,{type:`bytes32`}],[`int`,{type:`int256`}],[`int256`,{type:`int256`}],[`string`,{type:`string`}],[`uint`,{type:`uint256`}],[`uint8`,{type:`uint8`}],[`uint16`,{type:`uint16`}],[`uint24`,{type:`uint24`}],[`uint32`,{type:`uint32`}],[`uint64`,{type:`uint64`}],[`uint96`,{type:`uint96`}],[`uint112`,{type:`uint112`}],[`uint160`,{type:`uint160`}],[`uint192`,{type:`uint192`}],[`uint256`,{type:`uint256`}],[`address owner`,{type:`address`,name:`owner`}],[`address to`,{type:`address`,name:`to`}],[`bool approved`,{type:`bool`,name:`approved`}],[`bytes _data`,{type:`bytes`,name:`_data`}],[`bytes data`,{type:`bytes`,name:`data`}],[`bytes signature`,{type:`bytes`,name:`signature`}],[`bytes32 hash`,{type:`bytes32`,name:`hash`}],[`bytes32 r`,{type:`bytes32`,name:`r`}],[`bytes32 root`,{type:`bytes32`,name:`root`}],[`bytes32 s`,{type:`bytes32`,name:`s`}],[`string name`,{type:`string`,name:`name`}],[`string symbol`,{type:`string`,name:`symbol`}],[`string tokenURI`,{type:`string`,name:`tokenURI`}],[`uint tokenId`,{type:`uint256`,name:`tokenId`}],[`uint8 v`,{type:`uint8`,name:`v`}],[`uint256 balance`,{type:`uint256`,name:`balance`}],[`uint256 tokenId`,{type:`uint256`,name:`tokenId`}],[`uint256 value`,{type:`uint256`,name:`value`}],[`event:address indexed from`,{type:`address`,name:`from`,indexed:!0}],[`event:address indexed to`,{type:`address`,name:`to`,indexed:!0}],[`event:uint indexed tokenId`,{type:`uint256`,name:`tokenId`,indexed:!0}],[`event:uint256 indexed tokenId`,{type:`uint256`,name:`tokenId`,indexed:!0}]])}));function rt(e,t={}){if(he(e))return it(e,t);if(pe(e))return at(e,t);if(w(e))return ot(e,t);if(ye(e))return st(e,t);if(xe(e))return ct(e);if(Ce(e))return{type:`receive`,stateMutability:`payable`};throw new qe({signature:e})}function it(e,t={}){let n=ge(e);if(!n)throw new Ke({signature:e,type:`function`});let r=ut(n.parameters),i=[],a=r.length;for(let e=0;e{ie(),Re(),Ge(),Ye(),$e(),nt(),Pe(),mt=/^(?[a-zA-Z$_][a-zA-Z0-9$_]*(?:\spayable)?)(?(?:\[\d*?\])+?)?(?:\s(?calldata|indexed|memory|storage{1}))?(?:\s(?[a-zA-Z$_][a-zA-Z0-9$_]*))?$/,ht=/^\((?.+?)\)(?(?:\[\d*?\])+?)?(?:\s(?calldata|indexed|memory|storage{1}))?(?:\s(?[a-zA-Z$_][a-zA-Z0-9$_]*))?$/,gt=/^u?int$/,_t=/^(?:after|alias|anonymous|apply|auto|byte|calldata|case|catch|constant|copyof|default|defined|error|event|external|false|final|function|immutable|implements|in|indexed|inline|internal|let|mapping|match|memory|mutable|null|of|override|partial|private|promise|public|pure|reference|relocatable|return|returns|sizeof|static|storage|struct|super|supports|switch|this|true|try|typedef|typeof|var|view|virtual)$/}));function yt(e){let t={},n=e.length;for(let r=0;r{ie(),Re(),Ge(),Ye(),Ze(),Pe(),vt(),xt=/^(?[a-zA-Z$_][a-zA-Z0-9$_]*)(?(?:\[\d*?\])+?)?$/}));function Ct(e){let t=yt(e),n=[],r=e.length;for(let i=0;i{Pe(),St(),vt()}));function Tt(e){let t;if(typeof e==`string`)t=rt(e);else{let n=yt(e),r=e.length;for(let i=0;i{Re(),Pe(),St(),vt()}));function Dt(e){let t=[];if(typeof e==`string`){let n=ut(e),r=n.length;for(let e=0;e{Ge(),Pe(),St(),vt()})),kt=o((()=>{de(),le(),wt(),Et(),Ot()}));kt();function T(e,t,n){let r=e[t.name];if(typeof r==`function`)return r;let i=e[n];return typeof i==`function`?i:n=>t(e,n)}function At(e,{includeName:t=!1}={}){if(e.type!==`function`&&e.type!==`event`&&e.type!==`error`)throw new sn(e.type);return`${e.name}(${jt(e.inputs,{includeName:t})})`}function jt(e,{includeName:t=!1}={}){return e?e.map(e=>Mt(e,{includeName:t})).join(t?`, `:`,`):``}function Mt(e,{includeName:t}){return e.type.startsWith(`tuple`)?`(${jt(e.components,{includeName:t})})${e.type.slice(5)}`:e.type+(t&&e.name?` ${e.name}`:``)}var Nt=o((()=>{cn()}));function Pt(e,{strict:t=!0}={}){return!e||typeof e!=`string`?!1:t?/^0x[0-9a-fA-F]*$/.test(e):e.startsWith(`0x`)}var Ft=o((()=>{}));function It(e){return Pt(e,{strict:!1})?Math.ceil((e.length-2)/2):e.length}var Lt=o((()=>{Ft()})),Rt,zt=o((()=>{Rt=`2.47.6`}));function Bt(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause!==void 0?Bt(e.cause,t):t?null:e}var Vt,E,D=o((()=>{zt(),Vt={getDocsUrl:({docsBaseUrl:e,docsPath:t=``,docsSlug:n})=>t?`${e??`https://viem.sh`}${t}${n?`#${n}`:``}`:void 0,version:`viem@${Rt}`},E=class e extends Error{constructor(t,n={}){let r=n.cause instanceof e?n.cause.details:n.cause?.message?n.cause.message:n.details,i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=Vt.getDocsUrl?.({...n,docsPath:i}),o=[t||`An error occurred.`,``,...n.metaMessages?[...n.metaMessages,``]:[],...a?[`Docs: ${a}`]:[],...r?[`Details: ${r}`]:[],...Vt.version?[`Version: ${Vt.version}`]:[]].join(` +`);super(o,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`metaMessages`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.details=r,this.docsPath=i,this.metaMessages=n.metaMessages,this.name=n.name??this.name,this.shortMessage=t,this.version=Rt}walk(e){return Bt(this,e)}}})),Ht,Ut,Wt,Gt,Kt,qt,Jt,Yt,Xt,Zt,Qt,$t,en,tn,nn,rn,an,on,sn,cn=o((()=>{Nt(),Lt(),D(),Ht=class extends E{constructor({docsPath:e}){super([`A constructor was not found on the ABI.`,`Make sure you are using the correct ABI and that the constructor exists on it.`].join(` +`),{docsPath:e,name:`AbiConstructorNotFoundError`})}},Ut=class extends E{constructor({docsPath:e}){super(["Constructor arguments were provided (`args`), but a constructor parameters (`inputs`) were not found on the ABI.","Make sure you are using the correct ABI, and that the `inputs` attribute on the constructor exists."].join(` +`),{docsPath:e,name:`AbiConstructorParamsNotFoundError`})}},Wt=class extends E{constructor({data:e,params:t,size:n}){super([`Data size of ${n} bytes is too small for given parameters.`].join(` +`),{metaMessages:[`Params: (${jt(t,{includeName:!0})})`,`Data: ${e} (${n} bytes)`],name:`AbiDecodingDataSizeTooSmallError`}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`params`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`size`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=e,this.params=t,this.size=n}},Gt=class extends E{constructor({cause:e}={}){super(`Cannot decode zero data ("0x") with ABI parameters.`,{name:`AbiDecodingZeroDataError`,cause:e})}},Kt=class extends E{constructor({expectedLength:e,givenLength:t,type:n}){super([`ABI encoding array length mismatch for type ${n}.`,`Expected length: ${e}`,`Given length: ${t}`].join(` +`),{name:`AbiEncodingArrayLengthMismatchError`})}},qt=class extends E{constructor({expectedSize:e,value:t}){super(`Size of bytes "${t}" (bytes${It(t)}) does not match expected size (bytes${e}).`,{name:`AbiEncodingBytesSizeMismatchError`})}},Jt=class extends E{constructor({expectedLength:e,givenLength:t}){super([`ABI encoding params/values length mismatch.`,`Expected length (params): ${e}`,`Given length (values): ${t}`].join(` +`),{name:`AbiEncodingLengthMismatchError`})}},Yt=class extends E{constructor(e,{docsPath:t}){super([`Arguments (\`args\`) were provided to "${e}", but "${e}" on the ABI does not contain any parameters (\`inputs\`).`,`Cannot encode error result without knowing what the parameter types are.`,`Make sure you are using the correct ABI and that the inputs exist on it.`].join(` +`),{docsPath:t,name:`AbiErrorInputsNotFoundError`})}},Xt=class extends E{constructor(e,{docsPath:t}={}){super([`Error ${e?`"${e}" `:``}not found on ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`].join(` +`),{docsPath:t,name:`AbiErrorNotFoundError`})}},Zt=class extends E{constructor(e,{docsPath:t,cause:n}){super([`Encoded error signature "${e}" not found on ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`,`You can look up the decoded signature here: https://4byte.sourcify.dev/?q=${e}.`].join(` +`),{docsPath:t,name:`AbiErrorSignatureNotFoundError`,cause:n}),Object.defineProperty(this,`signature`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.signature=e}},Qt=class extends E{constructor(e,{docsPath:t}={}){super([`Function ${e?`"${e}" `:``}not found on ABI.`,`Make sure you are using the correct ABI and that the function exists on it.`].join(` +`),{docsPath:t,name:`AbiFunctionNotFoundError`})}},$t=class extends E{constructor(e,{docsPath:t}){super([`Function "${e}" does not contain any \`outputs\` on ABI.`,`Cannot decode function result without knowing what the parameter types are.`,`Make sure you are using the correct ABI and that the function exists on it.`].join(` +`),{docsPath:t,name:`AbiFunctionOutputsNotFoundError`})}},en=class extends E{constructor(e,{docsPath:t}){super([`Encoded function signature "${e}" not found on ABI.`,`Make sure you are using the correct ABI and that the function exists on it.`,`You can look up the signature here: https://4byte.sourcify.dev/?q=${e}.`].join(` +`),{docsPath:t,name:`AbiFunctionSignatureNotFoundError`})}},tn=class extends E{constructor(e,t){super(`Found ambiguous types in overloaded ABI items.`,{metaMessages:[`\`${e.type}\` in \`${At(e.abiItem)}\`, and`,`\`${t.type}\` in \`${At(t.abiItem)}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`],name:`AbiItemAmbiguityError`})}},nn=class extends E{constructor({expectedSize:e,givenSize:t}){super(`Expected bytes${e}, got bytes${t}.`,{name:`BytesSizeMismatchError`})}},rn=class extends E{constructor(e,{docsPath:t}){super([`Type "${e}" is not a valid encoding type.`,`Please provide a valid ABI type.`].join(` +`),{docsPath:t,name:`InvalidAbiEncodingType`})}},an=class extends E{constructor(e,{docsPath:t}){super([`Type "${e}" is not a valid decoding type.`,`Please provide a valid ABI type.`].join(` +`),{docsPath:t,name:`InvalidAbiDecodingType`})}},on=class extends E{constructor(e){super([`Value "${e}" is not a valid array.`].join(` +`),{name:`InvalidArrayError`})}},sn=class extends E{constructor(e){super([`"${e}" is not a valid definition type.`,`Valid types: "function", "event", "error"`].join(` +`),{name:`InvalidDefinitionTypeError`})}}})),ln,un,dn,fn=o((()=>{D(),ln=class extends E{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset "${e}" is out-of-bounds (size: ${n}).`,{name:`SliceOffsetOutOfBoundsError`})}},un=class extends E{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (${e}) exceeds padding size (${t}).`,{name:`SizeExceedsPaddingSizeError`})}},dn=class extends E{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} is expected to be ${t} ${n} long, but is ${e} ${n} long.`,{name:`InvalidBytesLengthError`})}}}));function pn(e,{dir:t,size:n=32}={}){return typeof e==`string`?mn(e,{dir:t,size:n}):hn(e,{dir:t,size:n})}function mn(e,{dir:t,size:n=32}={}){if(n===null)return e;let r=e.replace(`0x`,``);if(r.length>n*2)throw new un({size:Math.ceil(r.length/2),targetSize:n,type:`hex`});return`0x${r[t===`right`?`padEnd`:`padStart`](n*2,`0`)}`}function hn(e,{dir:t,size:n=32}={}){if(n===null)return e;if(e.length>n)throw new un({size:e.length,targetSize:n,type:`bytes`});let r=new Uint8Array(n);for(let i=0;i{fn()})),_n,vn,yn,bn,xn=o((()=>{D(),_n=class extends E{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number "${i}" is not in safe ${r?`${r*8}-bit ${n?`signed`:`unsigned`} `:``}integer range ${e?`(${t} to ${e})`:`(above ${t})`}`,{name:`IntegerOutOfRangeError`})}},vn=class extends E{constructor(e){super(`Bytes value "${e}" is not a valid boolean. The bytes array must contain a single byte of either a 0 or 1 value.`,{name:`InvalidBytesBooleanError`})}},yn=class extends E{constructor(e){super(`Hex value "${e}" is not a valid boolean. The hex value must be "0x0" (false) or "0x1" (true).`,{name:`InvalidHexBooleanError`})}},bn=class extends E{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed ${t} bytes. Given size: ${e} bytes.`,{name:`SizeOverflowError`})}}}));function Sn(e,{dir:t=`left`}={}){let n=typeof e==`string`?e.replace(`0x`,``):e,r=0;for(let e=0;e{}));function wn(e,{size:t}){if(It(e)>t)throw new bn({givenSize:It(e),maxSize:t})}function Tn(e,t={}){let{signed:n}=t;t.size&&wn(e,{size:t.size});let r=BigInt(e);if(!n)return r;let i=(e.length-2)/2;return r<=(1n<{xn(),Lt(),Cn()}));function kn(e,t={}){return typeof e==`number`||typeof e==`bigint`?O(e,t):typeof e==`string`?Mn(e,t):typeof e==`boolean`?An(e,t):jn(e,t)}function An(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(wn(n,{size:t.size}),pn(n,{size:t.size})):n}function jn(e,t={}){let n=``;for(let t=0;ta||i{xn(),gn(),On(),Nn=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),Pn=new TextEncoder}));function Fn(e,t={}){return typeof e==`number`||typeof e==`bigint`?zn(e,t):typeof e==`boolean`?In(e,t):Pt(e)?Rn(e,t):Bn(e,t)}function In(e,t={}){let n=new Uint8Array(1);return n[0]=Number(e),typeof t.size==`number`?(wn(n,{size:t.size}),pn(n,{size:t.size})):n}function Ln(e){if(e>=Hn.zero&&e<=Hn.nine)return e-Hn.zero;if(e>=Hn.A&&e<=Hn.F)return e-(Hn.A-10);if(e>=Hn.a&&e<=Hn.f)return e-(Hn.a-10)}function Rn(e,t={}){let n=e;t.size&&(wn(n,{size:t.size}),n=pn(n,{dir:`right`,size:t.size}));let r=n.slice(2);r.length%2&&(r=`0${r}`);let i=r.length/2,a=new Uint8Array(i);for(let e=0,t=0;e{D(),Ft(),gn(),On(),k(),Vn=new TextEncoder,Hn={zero:48,nine:57,A:65,F:70,a:97,f:102}}));function Wn(e,t=!1){return t?{h:Number(e&qn),l:Number(e>>Jn&qn)}:{h:Number(e>>Jn&qn)|0,l:Number(e&qn)|0}}function Gn(e,t=!1){let n=e.length,r=new Uint32Array(n),i=new Uint32Array(n);for(let a=0;a>>0)+(r>>>0);return{h:e+n+(i/2**32|0)|0,l:i|0}}var qn,Jn,Yn,Xn,Zn,Qn,$n,er,tr,nr,rr,ir,ar,or,sr,cr,lr,ur,dr=o((()=>{qn=BigInt(2**32-1),Jn=BigInt(32),Yn=(e,t,n)=>e>>>n,Xn=(e,t,n)=>e<<32-n|t>>>n,Zn=(e,t,n)=>e>>>n|t<<32-n,Qn=(e,t,n)=>e<<32-n|t>>>n,$n=(e,t,n)=>e<<64-n|t>>>n-32,er=(e,t,n)=>e>>>n-32|t<<64-n,tr=(e,t,n)=>e<>>32-n,nr=(e,t,n)=>t<>>32-n,rr=(e,t,n)=>t<>>64-n,ir=(e,t,n)=>e<>>64-n,ar=(e,t,n)=>(e>>>0)+(t>>>0)+(n>>>0),or=(e,t,n,r)=>t+n+r+(e/2**32|0)|0,sr=(e,t,n,r)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0),cr=(e,t,n,r,i)=>t+n+r+i+(e/2**32|0)|0,lr=(e,t,n,r,i)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0)+(i>>>0),ur=(e,t,n,r,i,a)=>t+n+r+i+a+(e/2**32|0)|0})),fr,pr=o((()=>{fr=typeof globalThis==`object`&&`crypto`in globalThis?globalThis.crypto:void 0}));function mr(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`}function hr(e){if(!Number.isSafeInteger(e)||e<0)throw Error(`positive integer expected, got `+e)}function gr(e,...t){if(!mr(e))throw Error(`Uint8Array expected`);if(t.length>0&&!t.includes(e.length))throw Error(`Uint8Array expected of length `+t+`, got length=`+e.length)}function _r(e){if(typeof e!=`function`||typeof e.create!=`function`)throw Error(`Hash should be wrapped by utils.createHasher`);hr(e.outputLen),hr(e.blockLen)}function vr(e,t=!0){if(e.destroyed)throw Error(`Hash instance has been destroyed`);if(t&&e.finished)throw Error(`Hash#digest() has already been called`)}function yr(e,t){gr(e);let n=t.outputLen;if(e.length>>t}function wr(e){return e<<24&4278190080|e<<8&16711680|e>>>8&65280|e>>>24&255}function Tr(e){for(let t=0;te().update(Dr(t)).digest(),n=e();return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=()=>e(),t}function Ar(e){let t=(t,n)=>e(n).update(Dr(t)).digest(),n=e({});return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=t=>e(t),t}function jr(e=32){if(fr&&typeof fr.getRandomValues==`function`)return fr.getRandomValues(new Uint8Array(e));if(fr&&typeof fr.randomBytes==`function`)return Uint8Array.from(fr.randomBytes(e));throw Error(`crypto.getRandomValues must be defined`)}var Mr,Nr,Pr,Fr=o((()=>{pr(),Mr=new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68,Nr=Mr?e=>e:Tr,typeof Uint8Array.from([]).toHex==`function`&&Uint8Array.fromHex,Pr=class{}}));function Ir(e,t=24){let n=new Uint32Array(10);for(let r=24-t;r<24;r++){for(let t=0;t<10;t++)n[t]=e[t]^e[t+10]^e[t+20]^e[t+30]^e[t+40];for(let t=0;t<10;t+=2){let r=(t+8)%10,i=(t+2)%10,a=n[i],o=n[i+1],s=Yr(a,o,1)^n[r],c=Xr(a,o,1)^n[r+1];for(let n=0;n<50;n+=10)e[t+n]^=s,e[t+n+1]^=c}let t=e[2],i=e[3];for(let n=0;n<24;n++){let r=Wr[n],a=Yr(t,i,r),o=Xr(t,i,r),s=Ur[n];t=e[s],i=e[s+1],e[s]=a,e[s+1]=o}for(let t=0;t<50;t+=10){for(let r=0;r<10;r++)n[r]=e[t+r];for(let r=0;r<10;r++)e[t+r]^=~n[(r+2)%10]&n[(r+4)%10]}e[0]^=qr[r],e[1]^=Jr[r]}xr(n)}var Lr,Rr,zr,Br,Vr,Hr,Ur,Wr,Gr,Kr,qr,Jr,Yr,Xr,Zr,Qr,$r,ei,ti=o((()=>{dr(),Fr(),Lr=BigInt(0),Rr=BigInt(1),zr=BigInt(2),Br=BigInt(7),Vr=BigInt(256),Hr=BigInt(113),Ur=[],Wr=[],Gr=[];for(let e=0,t=Rr,n=1,r=0;e<24;e++){[n,r]=[r,(2*n+3*r)%5],Ur.push(2*(5*r+n)),Wr.push((e+1)*(e+2)/2%64);let i=Lr;for(let e=0;e<7;e++)t=(t<>Br)*Hr)%Vr,t&zr&&(i^=Rr<<(Rr<n>32?rr(e,t,n):tr(e,t,n),Xr=(e,t,n)=>n>32?ir(e,t,n):nr(e,t,n),Zr=class e extends Pr{constructor(e,t,n,r=!1,i=24){if(super(),this.pos=0,this.posOut=0,this.finished=!1,this.destroyed=!1,this.enableXOF=!1,this.blockLen=e,this.suffix=t,this.outputLen=n,this.enableXOF=r,this.rounds=i,hr(n),!(0=n&&this.keccak();let a=Math.min(n-this.posOut,i-r);e.set(t.subarray(this.posOut,this.posOut+a),r),this.posOut+=a,r+=a}return e}xofInto(e){if(!this.enableXOF)throw Error(`XOF is not possible for this instance`);return this.writeInto(e)}xof(e){return hr(e),this.xofInto(new Uint8Array(e))}digestInto(e){if(yr(e,this),this.finished)throw Error(`digest() was already called`);return this.writeInto(e),this.destroy(),e}digest(){return this.digestInto(new Uint8Array(this.outputLen))}destroy(){this.destroyed=!0,xr(this.state)}_cloneInto(t){let{blockLen:n,suffix:r,outputLen:i,rounds:a,enableXOF:o}=this;return t||=new e(n,r,i,o,a),t.state32.set(this.state32),t.pos=this.pos,t.posOut=this.posOut,t.finished=this.finished,t.rounds=a,t.suffix=r,t.outputLen=i,t.enableXOF=o,t.destroyed=this.destroyed,t}},Qr=(e,t,n)=>kr(()=>new Zr(t,e,n)),Qr(6,144,224/8),Qr(6,136,256/8),Qr(6,104,384/8),Qr(6,72,512/8),Qr(1,144,224/8),$r=Qr(1,136,256/8),Qr(1,104,384/8),Qr(1,72,512/8),ei=(e,t,n)=>Ar((r={})=>new Zr(t,e,r.dkLen===void 0?n:r.dkLen,!0)),ei(31,168,128/8),ei(31,136,256/8)}));function ni(e,t){let n=t||`hex`,r=$r(Pt(e,{strict:!1})?Fn(e):e);return n===`bytes`?r:kn(r)}var ri=o((()=>{ti(),Ft(),Un(),k()}));function ii(e){return ai(e)}var ai,oi=o((()=>{Un(),ri(),ai=e=>ni(Fn(e))}));function si(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;o{D()})),li,ui=o((()=>{ci(),li=e=>si(typeof e==`string`?e:ue(e))}));function di(e){return ii(li(e))}var fi=o((()=>{oi(),ui()})),pi,mi=o((()=>{fi(),pi=di})),hi,gi=o((()=>{D(),hi=class extends E{constructor({address:e}){super(`Address "${e}" is invalid.`,{metaMessages:[`- Address must be a hex value of 20 bytes (40 hex characters).`,`- Address must match its checksum counterpart.`],name:`InvalidAddressError`})}}})),_i,vi=o((()=>{_i=class extends Map{constructor(e){super(),Object.defineProperty(this,`maxSize`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&(super.delete(e),super.set(e,t)),t}set(e,t){if(super.has(e)&&super.delete(e),super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=super.keys().next().value;e!==void 0&&super.delete(e)}return this}}}));function yi(e,t){if(xi.has(`${e}.${t}`))return xi.get(`${e}.${t}`);let n=t?`${t}${e.toLowerCase()}`:e.substring(2).toLowerCase(),r=ni(Bn(n),`bytes`),i=(t?n.substring(`${t}0x`.length):n).split(``);for(let e=0;e<40;e+=2)r[e>>1]>>4>=8&&i[e]&&(i[e]=i[e].toUpperCase()),(r[e>>1]&15)>=8&&i[e+1]&&(i[e+1]=i[e+1].toUpperCase());let a=`0x${i.join(``)}`;return xi.set(`${e}.${t}`,a),a}function bi(e,t){if(!Ci(e,{strict:!1}))throw new hi({address:e});return yi(e,t)}var xi,Si=o((()=>{gi(),Un(),ri(),vi(),Ei(),xi=new _i(8192)}));function Ci(e,t){let{strict:n=!0}=t??{},r=`${e}.${n}`;if(Ti.has(r))return Ti.get(r);let i=wi.test(e)?e.toLowerCase()===e?!0:n?yi(e)===e:!0:!1;return Ti.set(r,i),i}var wi,Ti,Ei=o((()=>{vi(),Si(),wi=/^0x[a-fA-F0-9]{40}$/,Ti=new _i(8192)}));function Di(e){return typeof e[0]==`string`?ki(e):Oi(e)}function Oi(e){let t=0;for(let n of e)t+=n.length;let n=new Uint8Array(t),r=0;for(let t of e)n.set(t,r),r+=t.length;return n}function ki(e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}var Ai=o((()=>{}));function ji(e,t,n,{strict:r}={}){return Pt(e,{strict:!1})?Fi(e,t,n,{strict:r}):Pi(e,t,n,{strict:r})}function Mi(e,t){if(typeof t==`number`&&t>0&&t>It(e)-1)throw new ln({offset:t,position:`start`,size:It(e)})}function Ni(e,t,n){if(typeof t==`number`&&typeof n==`number`&&It(e)!==n-t)throw new ln({offset:n,position:`end`,size:It(e)})}function Pi(e,t,n,{strict:r}={}){Mi(e,t);let i=e.slice(t,n);return r&&Ni(i,t,n),i}function Fi(e,t,n,{strict:r}={}){Mi(e,t);let i=`0x${e.replace(`0x`,``).slice((t??0)*2,(n??e.length)*2)}`;return r&&Ni(i,t,n),i}var Ii=o((()=>{fn(),Ft(),Lt()})),Li,Ri,zi=o((()=>{Li=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,Ri=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/}));function Bi(e,t){if(e.length!==t.length)throw new Jt({expectedLength:e.length,givenLength:t.length});let n=Ui(Vi({params:e,values:t}));return n.length===0?`0x`:n}function Vi({params:e,values:t}){let n=[];for(let r=0;r0?Di([t,e]):t}}if(i)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:Di(a.map(({encoded:e})=>e))}}function Ki(e,{param:t}){let[,n]=t.type.split(`bytes`),r=It(e);if(!n){let t=e;return r%32!=0&&(t=mn(t,{dir:`right`,size:Math.ceil((e.length-2)/2/32)*32})),{dynamic:!0,encoded:Di([mn(O(r,{size:32})),t])}}if(r!==Number.parseInt(n,10))throw new qt({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:mn(e,{dir:`right`})}}function qi(e){if(typeof e!=`boolean`)throw new E(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:mn(An(e))}}function Ji(e,{signed:t,size:n=256}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function Zi(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}var Qi=o((()=>{cn(),gi(),D(),xn(),Ei(),Ai(),gn(),Lt(),Ii(),k(),zi()})),$i,ea=o((()=>{Ii(),fi(),$i=e=>ji(di(e),0,4)}));function ta(e){let{abi:t,args:n=[],name:r}=e,i=Pt(r,{strict:!1}),a=t.filter(e=>i?e.type===`function`?$i(e)===r:e.type===`event`?pi(e)===r:!1:`name`in e&&e.name===r);if(a.length===0)return;if(a.length===1)return a[0];let o;for(let e of a)if(`inputs`in e){if(!n||n.length===0){if(!e.inputs||e.inputs.length===0)return e;continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===n.length&&n.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?na(t,r):!1})){if(o&&`inputs`in o&&o.inputs){let t=ra(e.inputs,o.inputs,n);if(t)throw new tn({abiItem:e,type:t[0]},{abiItem:o,type:t[1]})}o=e}}return o||a[0]}function na(e,t){let n=typeof e,r=t.type;switch(r){case`address`:return Ci(e,{strict:!1});case`bool`:return n===`boolean`;case`function`:return n===`string`;case`string`:return n===`string`;default:return r===`tuple`&&`components`in t?Object.values(t.components).every((t,r)=>n===`object`&&na(Object.values(e)[r],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>na(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function ra(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return ra(i.components,a.components,n[r]);let o=[i.type,a.type];if(o.includes(`address`)&&o.includes(`bytes20`)||(o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`))&&Ci(n[r],{strict:!1}))return o}}var ia=o((()=>{cn(),Ft(),Ei(),mi(),ea()}));function aa(e){return typeof e==`string`?{address:e,type:`json-rpc`}:e}var oa=o((()=>{}));function sa(e){let{abi:t,args:n,functionName:r}=e,i=t[0];if(r){let e=ta({abi:t,args:n,name:r});if(!e)throw new Qt(r,{docsPath:ca});i=e}if(i.type!==`function`)throw new Qt(void 0,{docsPath:ca});return{abi:[i],functionName:$i(At(i))}}var ca,la=o((()=>{cn(),ea(),Nt(),ia(),ca=`/docs/contract/encodeFunctionData`}));function ua(e){let{args:t}=e,{abi:n,functionName:r}=e.abi.length===1&&e.functionName?.startsWith(`0x`)?e:sa(e),i=n[0];return ki([r,(`inputs`in i&&i.inputs?Bi(i.inputs,t??[]):void 0)??`0x`])}var da=o((()=>{Ai(),Qi(),la()})),fa,pa,ma,ha=o((()=>{fa={1:"An `assert` condition failed.",17:`Arithmetic operation resulted in underflow or overflow.`,18:"Division or modulo by zero (e.g. `5 / 0` or `23 % 0`).",33:`Attempted to convert to an invalid type.`,34:`Attempted to access a storage byte array that is incorrectly encoded.`,49:"Performed `.pop()` on an empty array",50:`Array index is out of bounds.`,65:`Allocated too much memory or created an array which is too large.`,81:`Attempted to call a zero-initialized variable of internal function type.`},pa={inputs:[{name:`message`,type:`string`}],name:`Error`,type:`error`},ma={inputs:[{name:`reason`,type:`uint256`}],name:`Panic`,type:`error`}})),ga,_a,va,ya=o((()=>{D(),ga=class extends E{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`,{name:`NegativeOffsetError`})}},_a=class extends E{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`,{name:`PositionOutOfBoundsError`})}},va=class extends E{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`,{name:`RecursiveReadLimitExceededError`})}}}));function ba(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(xa);return n.bytes=e,n.dataView=new DataView(e.buffer??e,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var xa,Sa=o((()=>{ya(),xa={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new va({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new _a({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new ga({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new ga({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}}}));function Ca(e,t={}){return t.size!==void 0&&wn(e,{size:t.size}),Tn(jn(e,t),t)}function wa(e,t={}){let n=e;if(t.size!==void 0&&(wn(n,{size:t.size}),n=Sn(n)),n.length>1||n[0]>1)throw new vn(n);return!!n[0]}function Ta(e,t={}){return t.size!==void 0&&wn(e,{size:t.size}),Dn(jn(e,t),t)}function Ea(e,t={}){let n=e;return t.size!==void 0&&(wn(n,{size:t.size}),n=Sn(n,{dir:`right`})),new TextDecoder().decode(n)}var Da=o((()=>{xn(),Cn(),On(),k()}));function Oa(e,t){let n=typeof t==`string`?Rn(t):t,r=ba(n);if(It(n)===0&&e.length>0)throw new Gt;if(It(t)&&It(t)<32)throw new Wt({data:typeof t==`string`?t:jn(t),params:e,size:It(t)});let i=0,a=[];for(let t=0;t48?Ca(i,{signed:n}):Ta(i,{signed:n}),32]}function Fa(e,t,{staticPosition:n}){let r=t.components.length===0||t.components.some(({name:e})=>!e),i=r?[]:{},a=0;if(La(t)){let o=n+Ta(e.readBytes(za));for(let n=0;n{cn(),Si(),Sa(),Lt(),Ii(),Cn(),Da(),Un(),k(),Qi(),Ra=32,za=32}));function Va(e){let{abi:t,data:n,cause:r}=e,i=ji(n,0,4);if(i===`0x`)throw new Gt({cause:r});let a=[...t||[],pa,ma].find(e=>e.type===`error`&&i===$i(At(e)));if(!a)throw new Zt(i,{docsPath:`/docs/contract/decodeErrorResult`,cause:r});return{abiItem:a,args:`inputs`in a&&a.inputs&&a.inputs.length>0?Oa(a.inputs,ji(n,4)):void 0,errorName:a.name}}var Ha=o((()=>{ha(),cn(),Ii(),ea(),Ba(),Nt()})),Ua,Wa=o((()=>{Ua=(e,t,n)=>JSON.stringify(e,(e,n)=>{let r=typeof n==`bigint`?n.toString():n;return typeof t==`function`?t(e,r):r},n)}));function Ga({abiItem:e,args:t,includeFunctionName:n=!0,includeName:r=!1}){if(`name`in e&&`inputs`in e&&e.inputs)return`${n?e.name:``}(${e.inputs.map((e,n)=>`${r&&e.name?`${e.name}: `:``}${typeof t[n]==`object`?Ua(t[n]):t[n]}`).join(`, `)})`}var Ka=o((()=>{Wa()})),qa,Ja,Ya=o((()=>{qa={gwei:9,wei:18},Ja={ether:-9,wei:9}}));function Xa(e,t){let n=e.toString(),r=n.startsWith(`-`);r&&(n=n.slice(1)),n=n.padStart(t,`0`);let[i,a]=[n.slice(0,n.length-t),n.slice(n.length-t)];return a=a.replace(/(0+)$/,``),`${r?`-`:``}${i||`0`}${a?`.${a}`:``}`}var Za=o((()=>{}));function Qa(e,t=`wei`){return Xa(e,qa[t])}var $a=o((()=>{Ya(),Za()}));function eo(e,t=`wei`){return Xa(e,Ja[t])}var to=o((()=>{Ya(),Za()}));function no(e){return e.reduce((e,{slot:t,value:n})=>`${e} ${t}: ${n}\n`,``)}function ro(e){return e.reduce((e,{address:t,...n})=>{let r=`${e} ${t}:\n`;return n.nonce&&(r+=` nonce: ${n.nonce}\n`),n.balance&&(r+=` balance: ${n.balance}\n`),n.code&&(r+=` code: ${n.code}\n`),n.state&&(r+=` state: +`,r+=no(n.state)),n.stateDiff&&(r+=` stateDiff: +`,r+=no(n.stateDiff)),r},` State Override: +`).slice(0,-1)}var io,ao,oo=o((()=>{D(),io=class extends E{constructor({address:e}){super(`State for account "${e}" is set multiple times.`,{name:`AccountStateConflictError`})}},ao=class extends E{constructor(){super(`state and stateDiff are set on the same account.`,{name:`StateAssignmentConflictError`})}}}));function so(e){let t=Object.entries(e).map(([e,t])=>t===void 0||t===!1?null:[e,t]).filter(Boolean),n=t.reduce((e,[t])=>Math.max(e,t.length),0);return t.map(([e,t])=>` ${`${e}:`.padEnd(n+1)} ${t}`).join(` +`)}var co,lo,uo,fo,po,mo,ho,go,_o=o((()=>{$a(),to(),D(),co=class extends E{constructor({v:e}){super(`Invalid \`v\` value "${e}". Expected 27 or 28.`,{name:`InvalidLegacyVError`})}},lo=class extends E{constructor({transaction:e}){super(`Cannot infer a transaction type from provided transaction.`,{metaMessages:[`Provided Transaction:`,`{`,so(e),`}`,``,`To infer the type, either provide:`,"- a `type` to the Transaction, or","- an EIP-1559 Transaction with `maxFeePerGas`, or","- an EIP-2930 Transaction with `gasPrice` & `accessList`, or","- an EIP-4844 Transaction with `blobs`, `blobVersionedHashes`, `sidecars`, or","- an EIP-7702 Transaction with `authorizationList`, or","- a Legacy Transaction with `gasPrice`"],name:`InvalidSerializableTransactionError`})}},uo=class extends E{constructor({storageKey:e}){super(`Size for storage key "${e}" is invalid. Expected 32 bytes. Got ${Math.floor((e.length-2)/2)} bytes.`,{name:`InvalidStorageKeySizeError`})}},fo=class extends E{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d}){let f=so({chain:r&&`${r?.name} (id: ${r?.id})`,from:t?.address,to:u,value:d!==void 0&&`${Qa(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${eo(o)} gwei`,maxFeePerGas:s!==void 0&&`${eo(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${eo(c)} gwei`,nonce:l});super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Request Arguments:`,f].filter(Boolean),name:`TransactionExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},po=class extends E{constructor({blockHash:e,blockNumber:t,blockTag:n,hash:r,index:i}){let a=`Transaction`;n&&i!==void 0&&(a=`Transaction at block time "${n}" at index "${i}"`),e&&i!==void 0&&(a=`Transaction at block hash "${e}" at index "${i}"`),t&&i!==void 0&&(a=`Transaction at block number "${t}" at index "${i}"`),r&&(a=`Transaction with hash "${r}"`),super(`${a} could not be found.`,{name:`TransactionNotFoundError`})}},mo=class extends E{constructor({hash:e}){super(`Transaction receipt with hash "${e}" could not be found. The Transaction may not be processed on a block yet.`,{name:`TransactionReceiptNotFoundError`})}},ho=class extends E{constructor({receipt:e}){super(`Transaction with hash "${e.transactionHash}" reverted.`,{metaMessages:[`The receipt marked the transaction as "reverted". This could mean that the function on the contract you are trying to call threw an error.`,` `,`You can attempt to extract the revert reason by:`,"- calling the `simulateContract` or `simulateCalls` Action with the `abi` and `functionName` of the contract","- using the `call` Action with raw `data`"],name:`TransactionReceiptRevertedError`}),Object.defineProperty(this,`receipt`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.receipt=e}},go=class extends E{constructor({hash:e}){super(`Timed out while waiting for transaction with hash "${e}" to be confirmed.`,{name:`WaitForTransactionReceiptTimeoutError`})}}})),vo,yo,bo=o((()=>{vo=e=>e,yo=e=>e})),xo,So,Co,A,wo,To,Eo=o((()=>{oa(),ha(),Ha(),Nt(),Ka(),ia(),$a(),to(),cn(),D(),oo(),_o(),bo(),xo=class extends E{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d,stateOverride:f}){let p=so({from:(t?aa(t):void 0)?.address,to:u,value:d!==void 0&&`${Qa(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${eo(o)} gwei`,maxFeePerGas:s!==void 0&&`${eo(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${eo(c)} gwei`,nonce:l});f&&(p+=`\n${ro(f)}`),super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Raw Call Arguments:`,p].filter(Boolean),name:`CallExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},So=class extends E{constructor(e,{abi:t,args:n,contractAddress:r,docsPath:i,functionName:a,sender:o}){let s=ta({abi:t,args:n,name:a}),c=s?Ga({abiItem:s,args:n,includeFunctionName:!1,includeName:!1}):void 0,l=s?At(s,{includeName:!0}):void 0,u=so({address:r&&vo(r),function:l,args:c&&c!==`()`&&`${[...Array(a?.length??0).keys()].map(()=>` `).join(``)}${c}`,sender:o});super(e.shortMessage||`An unknown error occurred while executing the contract function "${a}".`,{cause:e,docsPath:i,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],u&&`Contract Call:`,u].filter(Boolean),name:`ContractFunctionExecutionError`}),Object.defineProperty(this,`abi`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`args`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`contractAddress`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`formattedArgs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`functionName`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`sender`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.abi=t,this.args=n,this.cause=e,this.contractAddress=r,this.functionName=a,this.sender=o}},Co=class extends E{constructor({abi:e,data:t,functionName:n,message:r,cause:i}){let a,o,s,c;if(t&&t!==`0x`)try{o=Va({abi:e,data:t,cause:i});let{abiItem:n,errorName:r,args:a}=o;if(r===`Error`)c=a[0];else if(r===`Panic`){let[e]=a;c=fa[e]}else{let e=n?At(n,{includeName:!0}):void 0,t=n&&a?Ga({abiItem:n,args:a,includeFunctionName:!1,includeName:!1}):void 0;s=[e?`Error: ${e}`:``,t&&t!==`()`?` ${[...Array(r?.length??0).keys()].map(()=>` `).join(``)}${t}`:``]}}catch(e){a=e}else r&&(c=r);let l;a instanceof Zt&&(l=a.signature,s=[`Unable to decode signature "${l}" as it was not found on the provided ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`,`You can look up the decoded signature here: https://4byte.sourcify.dev/?q=${l}.`]),super(c&&c!==`execution reverted`||l?[`The contract function "${n}" reverted with the following ${l?`signature`:`reason`}:`,c||l].join(` +`):`The contract function "${n}" reverted.`,{cause:a??i,metaMessages:s,name:`ContractFunctionRevertedError`}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`raw`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`reason`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`signature`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=o,this.raw=t,this.reason=c,this.signature=l}},A=class extends E{constructor({functionName:e,cause:t}){super(`The contract function "${e}" returned no data ("0x").`,{metaMessages:[`This could be due to any of the following:`,` - The contract does not have the function "${e}",`,` - The parameters passed to the contract function may be invalid, or`,` - The address is not a contract.`],name:`ContractFunctionZeroDataError`,cause:t})}},wo=class extends E{constructor({factory:e}){super(`Deployment for counterfactual contract call failed${e?` for factory "${e}".`:``}`,{metaMessages:[`Please ensure:`,"- The `factory` is a valid contract deployment factory (ie. Create2 Factory, ERC-4337 Factory, etc).","- The `factoryData` is a valid encoded function call for contract deployment function on the factory."],name:`CounterfactualDeploymentFailedError`})}},To=class extends E{constructor({data:e,message:t}){super(t||``,{name:`RawContractError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:3}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=e}}})),Do,Oo,ko,Ao=o((()=>{Wa(),D(),bo(),Do=class extends E{constructor({body:e,cause:t,details:n,headers:r,status:i,url:a}){super(`HTTP request failed.`,{cause:t,details:n,metaMessages:[i&&`Status: ${i}`,`URL: ${yo(a)}`,e&&`Request body: ${Ua(e)}`].filter(Boolean),name:`HttpRequestError`}),Object.defineProperty(this,`body`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`headers`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`status`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.body=e,this.headers=r,this.status=i,this.url=a}},Oo=class extends E{constructor({body:e,error:t,url:n}){super(`RPC Request failed.`,{cause:t,details:t.message,metaMessages:[`URL: ${yo(n)}`,`Request body: ${Ua(e)}`],name:`RpcRequestError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.code=t.code,this.data=t.data,this.url=n}},ko=class extends E{constructor({body:e,url:t}){super(`The request took too long to respond.`,{details:`The request timed out.`,metaMessages:[`URL: ${yo(t)}`,`Request body: ${Ua(e)}`],name:`TimeoutError`}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.url=t}}})),jo,Mo,No,Po,Fo,Io,Lo,Ro,zo,Bo,Vo,Ho,Uo,Wo,Go,Ko,qo,Jo,Yo,Xo,Zo,Qo,$o,es,ts,ns,rs,is,as,os,ss=o((()=>{D(),Ao(),jo=-1,Mo=class extends E{constructor(e,{code:t,docsPath:n,metaMessages:r,name:i,shortMessage:a}){super(a,{cause:e,docsPath:n,metaMessages:r||e?.metaMessages,name:i||`RpcError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.name=i||e.name,this.code=e instanceof Oo?e.code:t??jo}},No=class extends Mo{constructor(e,t){super(e,t),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=t.data}},Po=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ParseRpcError`,shortMessage:`Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.`})}},Object.defineProperty(Po,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700}),Fo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InvalidRequestRpcError`,shortMessage:`JSON is not a valid request object.`})}},Object.defineProperty(Fo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600}),Io=class e extends Mo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`MethodNotFoundRpcError`,shortMessage:`The method${n?` "${n}"`:``} does not exist / is not available.`})}},Object.defineProperty(Io,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601}),Lo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InvalidParamsRpcError`,shortMessage:[`Invalid parameters were provided to the RPC method.`,`Double check you have provided the correct parameters.`].join(` +`)})}},Object.defineProperty(Lo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602}),Ro=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InternalRpcError`,shortMessage:`An internal error was received.`})}},Object.defineProperty(Ro,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603}),zo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InvalidInputRpcError`,shortMessage:[`Missing or invalid parameters.`,`Double check you have provided the correct parameters.`].join(` +`)})}},Object.defineProperty(zo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3}),Bo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ResourceNotFoundRpcError`,shortMessage:`Requested resource not found.`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`ResourceNotFoundRpcError`})}},Object.defineProperty(Bo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001}),Vo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ResourceUnavailableRpcError`,shortMessage:`Requested resource not available.`})}},Object.defineProperty(Vo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002}),Ho=class e extends Mo{constructor(t){super(t,{code:e.code,name:`TransactionRejectedRpcError`,shortMessage:`Transaction creation failed.`})}},Object.defineProperty(Ho,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003}),Uo=class e extends Mo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`MethodNotSupportedRpcError`,shortMessage:`Method${n?` "${n}"`:``} is not supported.`})}},Object.defineProperty(Uo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004}),Wo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`LimitExceededRpcError`,shortMessage:`Request exceeds defined limit.`})}},Object.defineProperty(Wo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005}),Go=class e extends Mo{constructor(t){super(t,{code:e.code,name:`JsonRpcVersionUnsupportedError`,shortMessage:`Version of JSON-RPC protocol is not supported.`})}},Object.defineProperty(Go,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006}),Ko=class e extends No{constructor(t){super(t,{code:e.code,name:`UserRejectedRequestError`,shortMessage:`User rejected the request.`})}},Object.defineProperty(Ko,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001}),qo=class e extends No{constructor(t){super(t,{code:e.code,name:`UnauthorizedProviderError`,shortMessage:`The requested method and/or account has not been authorized by the user.`})}},Object.defineProperty(qo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100}),Jo=class e extends No{constructor(t,{method:n}={}){super(t,{code:e.code,name:`UnsupportedProviderMethodError`,shortMessage:`The Provider does not support the requested method${n?` " ${n}"`:``}.`})}},Object.defineProperty(Jo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200}),Yo=class e extends No{constructor(t){super(t,{code:e.code,name:`ProviderDisconnectedError`,shortMessage:`The Provider is disconnected from all chains.`})}},Object.defineProperty(Yo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900}),Xo=class e extends No{constructor(t){super(t,{code:e.code,name:`ChainDisconnectedError`,shortMessage:`The Provider is not connected to the requested chain.`})}},Object.defineProperty(Xo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901}),Zo=class e extends No{constructor(t){super(t,{code:e.code,name:`SwitchChainError`,shortMessage:`An error occurred when attempting to switch chain.`})}},Object.defineProperty(Zo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902}),Qo=class e extends No{constructor(t){super(t,{code:e.code,name:`UnsupportedNonOptionalCapabilityError`,shortMessage:`This Wallet does not support a capability that was not marked as optional.`})}},Object.defineProperty(Qo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700}),$o=class e extends No{constructor(t){super(t,{code:e.code,name:`UnsupportedChainIdError`,shortMessage:`This Wallet does not support the requested chain ID.`})}},Object.defineProperty($o,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710}),es=class e extends No{constructor(t){super(t,{code:e.code,name:`DuplicateIdError`,shortMessage:`There is already a bundle submitted with this ID.`})}},Object.defineProperty(es,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720}),ts=class e extends No{constructor(t){super(t,{code:e.code,name:`UnknownBundleIdError`,shortMessage:`This bundle id is unknown / has not been submitted`})}},Object.defineProperty(ts,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730}),ns=class e extends No{constructor(t){super(t,{code:e.code,name:`BundleTooLargeError`,shortMessage:`The call bundle is too large for the Wallet to process.`})}},Object.defineProperty(ns,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740}),rs=class e extends No{constructor(t){super(t,{code:e.code,name:`AtomicReadyWalletRejectedUpgradeError`,shortMessage:`The Wallet can support atomicity after an upgrade, but the user rejected the upgrade.`})}},Object.defineProperty(rs,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750}),is=class e extends No{constructor(t){super(t,{code:e.code,name:`AtomicityNotSupportedError`,shortMessage:`The wallet does not support atomic execution but the request requires it.`})}},Object.defineProperty(is,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760}),as=class e extends No{constructor(t){super(t,{code:e.code,name:`WalletConnectSessionSettlementError`,shortMessage:`WalletConnect session settlement failed.`})}},Object.defineProperty(as,`code`,{enumerable:!0,configurable:!0,writable:!0,value:7e3}),os=class extends Mo{constructor(e){super(e,{name:`UnknownRpcError`,shortMessage:`An unknown RPC error occurred.`})}}}));da(),cn(),D(),Eo(),Ao(),ss();var cs=3;function ls(e,{abi:t,address:n,args:r,docsPath:i,functionName:a,sender:o}){let s=e instanceof To?e:e instanceof E?e.walk(e=>`data`in e)||e.walk():{},{code:c,data:l,details:u,message:d,shortMessage:f}=s;return new So(e instanceof Gt?new A({functionName:a,cause:e}):[cs,Ro.code].includes(c)&&(l||u||d||f)||c===zo.code&&u===`execution reverted`&&l?new Co({abi:t,data:typeof l==`object`?l.data:l,functionName:a,message:s instanceof Oo?u:f??d,cause:e}):e,{abi:t,args:r,contractAddress:n,docsPath:i,functionName:a,sender:o})}Si(),ri();function us(e){return yi(`0x${ni(`0x${e.substring(4)}`).substring(26)}`)}var ds,fs,ps,ms,hs=o((()=>{ds=(function(){let e=typeof document<`u`&&document.createElement(`link`).relList;return e&&e.supports&&e.supports(`modulepreload`)?`modulepreload`:`preload`})(),fs=function(e){return`/`+e},ps={},ms=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=fs(t,n),t in ps)return;ps[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:ds,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})}}));function gs(e,t,n,r){if(typeof e.setBigUint64==`function`)return e.setBigUint64(t,n,r);let i=BigInt(32),a=BigInt(4294967295),o=Number(n>>i&a),s=Number(n&a),c=r?4:0,l=r?0:4;e.setUint32(t+c,o,r),e.setUint32(t+l,s,r)}function _s(e,t,n){return e&t^~e&n}function vs(e,t,n){return e&t^e&n^t&n}var ys,bs,xs,Ss,Cs=o((()=>{Fr(),ys=class extends Pr{constructor(e,t,n,r){super(),this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=e,this.outputLen=t,this.padOffset=n,this.isLE=r,this.buffer=new Uint8Array(e),this.view=Sr(this.buffer)}update(e){vr(this),e=Dr(e),gr(e);let{view:t,buffer:n,blockLen:r}=this,i=e.length;for(let a=0;ar-a&&(this.process(n,0),a=0);for(let e=a;el.length)throw Error(`_sha2: outputLen bigger than state`);for(let e=0;e{Cs(),dr(),Fr(),ws=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),Ts=new Uint32Array(64),Es=class extends ys{constructor(e=32){super(64,e,8,!1),this.A=bs[0]|0,this.B=bs[1]|0,this.C=bs[2]|0,this.D=bs[3]|0,this.E=bs[4]|0,this.F=bs[5]|0,this.G=bs[6]|0,this.H=bs[7]|0}get(){let{A:e,B:t,C:n,D:r,E:i,F:a,G:o,H:s}=this;return[e,t,n,r,i,a,o,s]}set(e,t,n,r,i,a,o,s){this.A=e|0,this.B=t|0,this.C=n|0,this.D=r|0,this.E=i|0,this.F=a|0,this.G=o|0,this.H=s|0}process(e,t){for(let n=0;n<16;n++,t+=4)Ts[n]=e.getUint32(t,!1);for(let e=16;e<64;e++){let t=Ts[e-15],n=Ts[e-2],r=Cr(t,7)^Cr(t,18)^t>>>3;Ts[e]=(Cr(n,17)^Cr(n,19)^n>>>10)+Ts[e-7]+r+Ts[e-16]|0}let{A:n,B:r,C:i,D:a,E:o,F:s,G:c,H:l}=this;for(let e=0;e<64;e++){let t=Cr(o,6)^Cr(o,11)^Cr(o,25),u=l+t+_s(o,s,c)+ws[e]+Ts[e]|0,d=(Cr(n,2)^Cr(n,13)^Cr(n,22))+vs(n,r,i)|0;l=c,c=s,s=o,o=a+u|0,a=i,i=r,r=n,n=u+d|0}n=n+this.A|0,r=r+this.B|0,i=i+this.C|0,a=a+this.D|0,o=o+this.E|0,s=s+this.F|0,c=c+this.G|0,l=l+this.H|0,this.set(n,r,i,a,o,s,c,l)}roundClean(){xr(Ts)}destroy(){this.set(0,0,0,0,0,0,0,0),xr(this.buffer)}},Ds=Gn(`0x428a2f98d728ae22.0x7137449123ef65cd.0xb5c0fbcfec4d3b2f.0xe9b5dba58189dbbc.0x3956c25bf348b538.0x59f111f1b605d019.0x923f82a4af194f9b.0xab1c5ed5da6d8118.0xd807aa98a3030242.0x12835b0145706fbe.0x243185be4ee4b28c.0x550c7dc3d5ffb4e2.0x72be5d74f27b896f.0x80deb1fe3b1696b1.0x9bdc06a725c71235.0xc19bf174cf692694.0xe49b69c19ef14ad2.0xefbe4786384f25e3.0x0fc19dc68b8cd5b5.0x240ca1cc77ac9c65.0x2de92c6f592b0275.0x4a7484aa6ea6e483.0x5cb0a9dcbd41fbd4.0x76f988da831153b5.0x983e5152ee66dfab.0xa831c66d2db43210.0xb00327c898fb213f.0xbf597fc7beef0ee4.0xc6e00bf33da88fc2.0xd5a79147930aa725.0x06ca6351e003826f.0x142929670a0e6e70.0x27b70a8546d22ffc.0x2e1b21385c26c926.0x4d2c6dfc5ac42aed.0x53380d139d95b3df.0x650a73548baf63de.0x766a0abb3c77b2a8.0x81c2c92e47edaee6.0x92722c851482353b.0xa2bfe8a14cf10364.0xa81a664bbc423001.0xc24b8b70d0f89791.0xc76c51a30654be30.0xd192e819d6ef5218.0xd69906245565a910.0xf40e35855771202a.0x106aa07032bbd1b8.0x19a4c116b8d2d0c8.0x1e376c085141ab53.0x2748774cdf8eeb99.0x34b0bcb5e19b48a8.0x391c0cb3c5c95a63.0x4ed8aa4ae3418acb.0x5b9cca4f7763e373.0x682e6ff3d6b2b8a3.0x748f82ee5defb2fc.0x78a5636f43172f60.0x84c87814a1f0ab72.0x8cc702081a6439ec.0x90befffa23631e28.0xa4506cebde82bde9.0xbef9a3f7b2c67915.0xc67178f2e372532b.0xca273eceea26619c.0xd186b8c721c0c207.0xeada7dd6cde0eb1e.0xf57d4f7fee6ed178.0x06f067aa72176fba.0x0a637dc5a2c898a6.0x113f9804bef90dae.0x1b710b35131c471b.0x28db77f523047d84.0x32caab7b40c72493.0x3c9ebe0a15c9bebc.0x431d67c49c100d4c.0x4cc5d4becb3e42b6.0x597f299cfc657e2a.0x5fcb6fab3ad6faec.0x6c44198c4a475817`.split(`.`).map(e=>BigInt(e))),Os=Ds[0],ks=Ds[1],As=new Uint32Array(80),js=new Uint32Array(80),Ms=class extends ys{constructor(e=64){super(128,e,16,!1),this.Ah=Ss[0]|0,this.Al=Ss[1]|0,this.Bh=Ss[2]|0,this.Bl=Ss[3]|0,this.Ch=Ss[4]|0,this.Cl=Ss[5]|0,this.Dh=Ss[6]|0,this.Dl=Ss[7]|0,this.Eh=Ss[8]|0,this.El=Ss[9]|0,this.Fh=Ss[10]|0,this.Fl=Ss[11]|0,this.Gh=Ss[12]|0,this.Gl=Ss[13]|0,this.Hh=Ss[14]|0,this.Hl=Ss[15]|0}get(){let{Ah:e,Al:t,Bh:n,Bl:r,Ch:i,Cl:a,Dh:o,Dl:s,Eh:c,El:l,Fh:u,Fl:d,Gh:f,Gl:p,Hh:m,Hl:h}=this;return[e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h]}set(e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h){this.Ah=e|0,this.Al=t|0,this.Bh=n|0,this.Bl=r|0,this.Ch=i|0,this.Cl=a|0,this.Dh=o|0,this.Dl=s|0,this.Eh=c|0,this.El=l|0,this.Fh=u|0,this.Fl=d|0,this.Gh=f|0,this.Gl=p|0,this.Hh=m|0,this.Hl=h|0}process(e,t){for(let n=0;n<16;n++,t+=4)As[n]=e.getUint32(t),js[n]=e.getUint32(t+=4);for(let e=16;e<80;e++){let t=As[e-15]|0,n=js[e-15]|0,r=Zn(t,n,1)^Zn(t,n,8)^Yn(t,n,7),i=Qn(t,n,1)^Qn(t,n,8)^Xn(t,n,7),a=As[e-2]|0,o=js[e-2]|0,s=Zn(a,o,19)^$n(a,o,61)^Yn(a,o,6),c=Qn(a,o,19)^er(a,o,61)^Xn(a,o,6),l=sr(i,c,js[e-7],js[e-16]);As[e]=cr(l,r,s,As[e-7],As[e-16])|0,js[e]=l|0}let{Ah:n,Al:r,Bh:i,Bl:a,Ch:o,Cl:s,Dh:c,Dl:l,Eh:u,El:d,Fh:f,Fl:p,Gh:m,Gl:h,Hh:g,Hl:_}=this;for(let e=0;e<80;e++){let t=Zn(u,d,14)^Zn(u,d,18)^$n(u,d,41),v=Qn(u,d,14)^Qn(u,d,18)^er(u,d,41),y=u&f^~u&m,b=d&p^~d&h,x=lr(_,v,b,ks[e],js[e]),S=ur(x,g,t,y,Os[e],As[e]),ee=x|0,C=Zn(n,r,28)^$n(n,r,34)^$n(n,r,39),te=Qn(n,r,28)^er(n,r,34)^er(n,r,39),ne=n&i^n&o^i&o,re=r&a^r&s^a&s;g=m|0,_=h|0,m=f|0,h=p|0,f=u|0,p=d|0,{h:u,l:d}=Kn(c|0,l|0,S|0,ee|0),c=o|0,l=s|0,o=i|0,s=a|0,i=n|0,a=r|0;let ie=ar(ee,te,re);n=or(ie,S,C,ne),r=ie|0}({h:n,l:r}=Kn(this.Ah|0,this.Al|0,n|0,r|0)),{h:i,l:a}=Kn(this.Bh|0,this.Bl|0,i|0,a|0),{h:o,l:s}=Kn(this.Ch|0,this.Cl|0,o|0,s|0),{h:c,l}=Kn(this.Dh|0,this.Dl|0,c|0,l|0),{h:u,l:d}=Kn(this.Eh|0,this.El|0,u|0,d|0),{h:f,l:p}=Kn(this.Fh|0,this.Fl|0,f|0,p|0),{h:m,l:h}=Kn(this.Gh|0,this.Gl|0,m|0,h|0),{h:g,l:_}=Kn(this.Hh|0,this.Hl|0,g|0,_|0),this.set(n,r,i,a,o,s,c,l,u,d,f,p,m,h,g,_)}roundClean(){xr(As,js)}destroy(){xr(this.buffer),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}},Ns=class extends Ms{constructor(){super(48),this.Ah=xs[0]|0,this.Al=xs[1]|0,this.Bh=xs[2]|0,this.Bl=xs[3]|0,this.Ch=xs[4]|0,this.Cl=xs[5]|0,this.Dh=xs[6]|0,this.Dl=xs[7]|0,this.Eh=xs[8]|0,this.El=xs[9]|0,this.Fh=xs[10]|0,this.Fl=xs[11]|0,this.Gh=xs[12]|0,this.Gl=xs[13]|0,this.Hh=xs[14]|0,this.Hl=xs[15]|0}},Ps=kr(()=>new Es),Fs=kr(()=>new Ms),Is=kr(()=>new Ns)})),Rs,zs,Bs=o((()=>{Fr(),Rs=class extends Pr{constructor(e,t){super(),this.finished=!1,this.destroyed=!1,_r(e);let n=Dr(t);if(this.iHash=e.create(),typeof this.iHash.update!=`function`)throw Error(`Expected instance of class which extends utils.Hash`);this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;let r=this.blockLen,i=new Uint8Array(r);i.set(n.length>r?e.create().update(n).digest():n);for(let e=0;enew Rs(e,t).update(n).digest(),zs.create=(e,t)=>new Rs(e,t)}));function Vs(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`}function Hs(e){if(!Vs(e))throw Error(`Uint8Array expected`)}function Us(e,t){if(typeof t!=`boolean`)throw Error(e+` boolean expected, got `+t)}function Ws(e){let t=e.toString(16);return t.length&1?`0`+t:t}function Gs(e){if(typeof e!=`string`)throw Error(`hex string expected, got `+typeof e);return e===``?lc:BigInt(`0x`+e)}function Ks(e){if(Hs(e),dc)return e.toHex();let t=``;for(let n=0;n=pc._0&&e<=pc._9)return e-pc._0;if(e>=pc.A&&e<=pc.F)return e-(pc.A-10);if(e>=pc.a&&e<=pc.f)return e-(pc.a-10)}function Js(e){if(typeof e!=`string`)throw Error(`hex string expected, got `+typeof e);if(dc)return Uint8Array.fromHex(e);let t=e.length,n=t/2;if(t%2)throw Error(`hex string expected, got unpadded hex of length `+t);let r=new Uint8Array(n);for(let t=0,i=0;tlc;e>>=uc,t+=1);return t}function oc(e,t,n){if(typeof e!=`number`||e<2)throw Error(`hashLen must be a number`);if(typeof t!=`number`||t<2)throw Error(`qByteLen must be a number`);if(typeof n!=`function`)throw Error(`hmacFn must be a function`);let r=gc(e),i=gc(e),a=0,o=()=>{r.fill(1),i.fill(0),a=0},s=(...e)=>n(i,r,...e),c=(e=gc(0))=>{i=s(_c([0]),e),r=s(),e.length!==0&&(i=s(_c([1]),e),r=s())},l=()=>{if(a++>=1e3)throw Error(`drbg: tried 1000 values`);let e=0,n=[];for(;e{o(),c(e);let n;for(;!(n=t(l()));)c();return o(),n}}function sc(e,t,n={}){let r=(t,n,r)=>{let i=vc[n];if(typeof i!=`function`)throw Error(`invalid validator function`);let a=e[t];if(!(r&&a===void 0)&&!i(a,e))throw Error(`param `+String(t)+` is invalid. Expected `+n+`, got `+a)};for(let[e,n]of Object.entries(t))r(e,n,!1);for(let[e,t]of Object.entries(n))r(e,t,!0);return e}function cc(e){let t=new WeakMap;return(n,...r)=>{let i=t.get(n);if(i!==void 0)return i;let a=e(n,...r);return t.set(n,a),a}}var lc,uc,dc,fc,pc,mc,hc,gc,_c,vc,yc=o((()=>{lc=BigInt(0),uc=BigInt(1),dc=typeof Uint8Array.from([]).toHex==`function`&&typeof Uint8Array.fromHex==`function`,fc=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),pc={_0:48,_9:57,A:65,F:70,a:97,f:102},mc=e=>typeof e==`bigint`&&lc<=e,hc=e=>(uc<new Uint8Array(e),_c=e=>Uint8Array.from(e),vc={bigint:e=>typeof e==`bigint`,function:e=>typeof e==`function`,boolean:e=>typeof e==`boolean`,string:e=>typeof e==`string`,stringOrUint8Array:e=>typeof e==`string`||Vs(e),isSafeInteger:e=>Number.isSafeInteger(e),array:e=>Array.isArray(e),field:(e,t)=>t.Fp.isValid(e),hash:e=>typeof e==`function`&&Number.isSafeInteger(e.outputLen)}}));function bc(e,t){let n=e%t;return n>=Ic?n:t+n}function xc(e,t,n){let r=e;for(;t-- >Ic;)r*=r,r%=n;return r}function Sc(e,t){if(e===Ic)throw Error(`invert: expected non-zero number`);if(t<=Ic)throw Error(`invert: expected positive modulus, got `+t);let n=bc(e,t),r=t,i=Ic,a=Lc,o=Lc,s=Ic;for(;n!==Ic;){let e=r/n,t=r%n,c=i-o*e,l=a-s*e;r=n,n=t,i=o,a=s,o=c,s=l}if(r!==Lc)throw Error(`invert: does not exist`);return bc(i,t)}function Cc(e,t){let n=(e.ORDER+Lc)/Bc,r=e.pow(t,n);if(!e.eql(e.sqr(r),t))throw Error(`Cannot find square root`);return r}function wc(e,t){let n=(e.ORDER-Vc)/Hc,r=e.mul(t,Rc),i=e.pow(r,n),a=e.mul(t,i),o=e.mul(e.mul(a,Rc),i),s=e.mul(a,e.sub(o,e.ONE));if(!e.eql(e.sqr(s),t))throw Error(`Cannot find square root`);return s}function Tc(e){if(e1e3)throw Error(`Cannot find square root: probably non-prime P`);if(n===1)return Cc;let a=i.pow(r,t),o=(t+Lc)/Rc;return function(e,r){if(e.is0(r))return r;if(Ac(e,r)!==1)throw Error(`Cannot find square root`);let i=n,s=e.mul(e.ONE,a),c=e.pow(r,t),l=e.pow(r,o);for(;!e.eql(c,e.ONE);){if(e.is0(c))return e.ZERO;let t=1,n=e.sqr(c);for(;!e.eql(n,e.ONE);)if(t++,n=e.sqr(n),t===i)throw Error(`Cannot find square root`);let r=Lc<(e[t]=`function`,e),{ORDER:`bigint`,MASK:`bigint`,BYTES:`isSafeInteger`,BITS:`isSafeInteger`}))}function Oc(e,t,n){if(nIc;)n&Lc&&(r=e.mul(r,i)),i=e.sqr(i),n>>=Lc;return r}function kc(e,t,n=!1){let r=Array(t.length).fill(n?e.ZERO:void 0),i=t.reduce((t,n,i)=>e.is0(n)?t:(r[i]=t,e.mul(t,n)),e.ONE),a=e.inv(i);return t.reduceRight((t,n,i)=>e.is0(n)?t:(r[i]=e.mul(t,r[i]),e.mul(t,n)),a),r}function Ac(e,t){let n=(e.ORDER-Lc)/Rc,r=e.pow(t,n),i=e.eql(r,e.ONE),a=e.eql(r,e.ZERO),o=e.eql(r,e.neg(e.ONE));if(!i&&!a&&!o)throw Error(`invalid Legendre symbol result`);return i?1:a?0:-1}function jc(e,t){t!==void 0&&hr(t);let n=t===void 0?e.toString(2).length:t;return{nBitLength:n,nByteLength:Math.ceil(n/8)}}function Mc(e,t,n=!1,r={}){if(e<=Ic)throw Error(`invalid field: expected ORDER > 0, got `+e);let{nBitLength:i,nByteLength:a}=jc(e,t);if(a>2048)throw Error(`invalid field: expected ORDER of <= 2048 bytes`);let o,s=Object.freeze({ORDER:e,isLE:n,BITS:i,BYTES:a,MASK:hc(i),ZERO:Ic,ONE:Lc,create:t=>bc(t,e),isValid:t=>{if(typeof t!=`bigint`)throw Error(`invalid field element: expected bigint, got `+typeof t);return Ic<=t&&te===Ic,isOdd:e=>(e&Lc)===Lc,neg:t=>bc(-t,e),eql:(e,t)=>e===t,sqr:t=>bc(t*t,e),add:(t,n)=>bc(t+n,e),sub:(t,n)=>bc(t-n,e),mul:(t,n)=>bc(t*n,e),pow:(e,t)=>Oc(s,e,t),div:(t,n)=>bc(t*Sc(n,e),e),sqrN:e=>e*e,addN:(e,t)=>e+t,subN:(e,t)=>e-t,mulN:(e,t)=>e*t,inv:t=>Sc(t,e),sqrt:r.sqrt||(t=>(o||=Ec(e),o(s,t))),toBytes:e=>n?Qs(e,a):Zs(e,a),fromBytes:e=>{if(e.length!==a)throw Error(`Field.fromBytes: expected `+a+` bytes, got `+e.length);return n?Xs(e):Ys(e)},invertBatch:e=>kc(s,e),cmov:(e,t,n)=>n?t:e});return Object.freeze(s)}function Nc(e){if(typeof e!=`bigint`)throw Error(`field order must be bigint`);let t=e.toString(2).length;return Math.ceil(t/8)}function Pc(e){let t=Nc(e);return t+Math.ceil(t/2)}function Fc(e,t,n=!1){let r=e.length,i=Nc(t),a=Pc(t);if(r<16||r1024)throw Error(`expected `+a+`-1024 bytes of input, got `+r);let o=bc(n?Xs(e):Ys(e),t-Lc)+Lc;return n?Qs(o,i):Zs(o,i)}var Ic,Lc,Rc,zc,Bc,Vc,Hc,Uc,Wc=o((()=>{Fr(),yc(),Ic=BigInt(0),Lc=BigInt(1),Rc=BigInt(2),zc=BigInt(3),Bc=BigInt(4),Vc=BigInt(5),Hc=BigInt(8),Uc=[`create`,`isValid`,`is0`,`neg`,`inv`,`sqrt`,`sqr`,`eql`,`add`,`sub`,`mul`,`pow`,`div`,`addN`,`subN`,`mulN`,`sqrN`]}));function Gc(e,t){let n=t.negate();return e?n:t}function Kc(e,t){if(!Number.isSafeInteger(e)||e<=0||e>t)throw Error(`invalid window size, expected [1..`+t+`], got W=`+e)}function qc(e,t){Kc(e,t);let n=Math.ceil(t/e)+1,r=2**(e-1),i=2**e;return{windows:n,windowSize:r,mask:hc(e),maxNumber:i,shiftBy:BigInt(e)}}function Jc(e,t,n){let{windowSize:r,mask:i,maxNumber:a,shiftBy:o}=n,s=Number(e&i),c=e>>o;s>r&&(s-=a,c+=nl);let l=t*r,u=l+Math.abs(s)-1,d=s===0,f=s<0,p=t%2!=0;return{nextN:c,offset:u,isZero:d,isNeg:f,isNegF:p,offsetF:l}}function Yc(e,t){if(!Array.isArray(e))throw Error(`array expected`);e.forEach((e,n)=>{if(!(e instanceof t))throw Error(`invalid point at index `+n)})}function Xc(e,t){if(!Array.isArray(e))throw Error(`array of scalars expected`);e.forEach((e,n)=>{if(!t.isValid(e))throw Error(`invalid scalar at index `+n)})}function Zc(e){return il.get(e)||1}function Qc(e,t){return{constTimeNegate:Gc,hasPrecomputes(e){return Zc(e)!==1},unsafeLadder(t,n,r=e.ZERO){let i=t;for(;n>tl;)n&nl&&(r=r.add(i)),i=i.double(),n>>=nl;return r},precomputeWindow(e,n){let{windows:r,windowSize:i}=qc(n,t),a=[],o=e,s=o;for(let e=0;e12?c=s-3:s>4?c=s-2:s>0&&(c=2);let l=hc(c),u=Array(Number(l)+1).fill(o),d=Math.floor((t.BITS-1)/c)*c,f=o;for(let e=d;e>=0;e-=c){u.fill(o);for(let t=0;t>BigInt(e)&l);u[a]=u[a].add(n[t])}let t=o;for(let e=u.length-1,n=o;e>0;e--)n=n.add(u[e]),t=t.add(n);if(f=f.add(t),e!==0)for(let e=0;e{Wc(),yc(),tl=BigInt(0),nl=BigInt(1),rl=new WeakMap,il=new WeakMap}));function ol(e){e.lowS!==void 0&&Us(`lowS`,e.lowS),e.prehash!==void 0&&Us(`prehash`,e.prehash)}function sl(e){let t=el(e);sc(t,{a:`field`,b:`field`},{allowInfinityPoint:`boolean`,allowedPrivateKeyLengths:`array`,clearCofactor:`function`,fromBytes:`function`,isTorsionFree:`function`,toBytes:`function`,wrapPrivateKey:`boolean`});let{endo:n,Fp:r,a:i}=t;if(n){if(!r.eql(i,r.ZERO))throw Error(`invalid endo: CURVE.a must be 0`);if(typeof n!=`object`||typeof n.beta!=`bigint`||typeof n.splitScalar!=`function`)throw Error(`invalid endo: expected "beta": bigint and "splitScalar": function`)}return Object.freeze({...t})}function cl(e,t){return Ks(Zs(e,t))}function ll(e){let t=sl(e),{Fp:n}=t,r=Mc(t.n,t.nBitLength),i=t.toBytes||((e,t,r)=>{let i=t.toAffine();return ec(Uint8Array.from([4]),n.toBytes(i.x),n.toBytes(i.y))}),a=t.fromBytes||(e=>{let t=e.subarray(1);return{x:n.fromBytes(t.subarray(0,n.BYTES)),y:n.fromBytes(t.subarray(n.BYTES,2*n.BYTES))}});function o(e){let{a:r,b:i}=t,a=n.sqr(e),o=n.mul(a,e);return n.add(n.add(o,n.mul(e,r)),i)}function s(e,t){let r=n.sqr(t),i=o(e);return n.eql(r,i)}if(!s(t.Gx,t.Gy))throw Error(`bad curve params: generator point`);let c=n.mul(n.pow(t.a,yl),bl),l=n.mul(n.sqr(t.b),BigInt(27));if(n.is0(n.add(c,l)))throw Error(`bad curve params: a or b`);function u(e){return rc(e,_l,t.n)}function d(e){let{allowedPrivateKeyLengths:n,nByteLength:r,wrapPrivateKey:i,n:a}=t;if(n&&typeof e!=`bigint`){if(Vs(e)&&(e=Ks(e)),typeof e!=`string`||!n.includes(e.length))throw Error(`invalid private key`);e=e.padStart(r*2,`0`)}let o;try{o=typeof e==`bigint`?e:Ys($s(`private key`,e,r))}catch{throw Error(`invalid private key, expected hex or `+r+` bytes, got `+typeof e)}return i&&(o=bc(o,a)),ic(`private key`,o,_l,a),o}function f(e){if(!(e instanceof h))throw Error(`ProjectivePoint expected`)}let p=cc((e,t)=>{let{px:r,py:i,pz:a}=e;if(n.eql(a,n.ONE))return{x:r,y:i};let o=e.is0();t??=o?n.ONE:n.inv(a);let s=n.mul(r,t),c=n.mul(i,t),l=n.mul(a,t);if(o)return{x:n.ZERO,y:n.ZERO};if(!n.eql(l,n.ONE))throw Error(`invZ was invalid`);return{x:s,y:c}}),m=cc(e=>{if(e.is0()){if(t.allowInfinityPoint&&!n.is0(e.py))return;throw Error(`bad point: ZERO`)}let{x:r,y:i}=e.toAffine();if(!n.isValid(r)||!n.isValid(i))throw Error(`bad point: x or y not FE`);if(!s(r,i))throw Error(`bad point: equation left != right`);if(!e.isTorsionFree())throw Error(`bad point: not in prime-order subgroup`);return!0});class h{constructor(e,t,r){if(e==null||!n.isValid(e))throw Error(`x required`);if(t==null||!n.isValid(t)||n.is0(t))throw Error(`y required`);if(r==null||!n.isValid(r))throw Error(`z required`);this.px=e,this.py=t,this.pz=r,Object.freeze(this)}static fromAffine(e){let{x:t,y:r}=e||{};if(!e||!n.isValid(t)||!n.isValid(r))throw Error(`invalid affine point`);if(e instanceof h)throw Error(`projective point not allowed`);let i=e=>n.eql(e,n.ZERO);return i(t)&&i(r)?h.ZERO:new h(t,r,n.ONE)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}static normalizeZ(e){let t=kc(n,e.map(e=>e.pz));return e.map((e,n)=>e.toAffine(t[n])).map(h.fromAffine)}static fromHex(e){let t=h.fromAffine(a($s(`pointHex`,e)));return t.assertValidity(),t}static fromPrivateKey(e){return h.BASE.multiply(d(e))}static msm(e,t){return $c(h,r,e,t)}_setWindowSize(e){v.setWindowSize(this,e)}assertValidity(){m(this)}hasEvenY(){let{y:e}=this.toAffine();if(n.isOdd)return!n.isOdd(e);throw Error(`Field doesn't support isOdd`)}equals(e){f(e);let{px:t,py:r,pz:i}=this,{px:a,py:o,pz:s}=e,c=n.eql(n.mul(t,s),n.mul(a,i)),l=n.eql(n.mul(r,s),n.mul(o,i));return c&&l}negate(){return new h(this.px,n.neg(this.py),this.pz)}double(){let{a:e,b:r}=t,i=n.mul(r,yl),{px:a,py:o,pz:s}=this,c=n.ZERO,l=n.ZERO,u=n.ZERO,d=n.mul(a,a),f=n.mul(o,o),p=n.mul(s,s),m=n.mul(a,o);return m=n.add(m,m),u=n.mul(a,s),u=n.add(u,u),c=n.mul(e,u),l=n.mul(i,p),l=n.add(c,l),c=n.sub(f,l),l=n.add(f,l),l=n.mul(c,l),c=n.mul(m,c),u=n.mul(i,u),p=n.mul(e,p),m=n.sub(d,p),m=n.mul(e,m),m=n.add(m,u),u=n.add(d,d),d=n.add(u,d),d=n.add(d,p),d=n.mul(d,m),l=n.add(l,d),p=n.mul(o,s),p=n.add(p,p),d=n.mul(p,m),c=n.sub(c,d),u=n.mul(p,f),u=n.add(u,u),u=n.add(u,u),new h(c,l,u)}add(e){f(e);let{px:r,py:i,pz:a}=this,{px:o,py:s,pz:c}=e,l=n.ZERO,u=n.ZERO,d=n.ZERO,p=t.a,m=n.mul(t.b,yl),g=n.mul(r,o),_=n.mul(i,s),v=n.mul(a,c),y=n.add(r,i),b=n.add(o,s);y=n.mul(y,b),b=n.add(g,_),y=n.sub(y,b),b=n.add(r,a);let x=n.add(o,c);return b=n.mul(b,x),x=n.add(g,v),b=n.sub(b,x),x=n.add(i,a),l=n.add(s,c),x=n.mul(x,l),l=n.add(_,v),x=n.sub(x,l),d=n.mul(p,b),l=n.mul(m,v),d=n.add(l,d),l=n.sub(_,d),d=n.add(_,d),u=n.mul(l,d),_=n.add(g,g),_=n.add(_,g),v=n.mul(p,v),b=n.mul(m,b),_=n.add(_,v),v=n.sub(g,v),v=n.mul(p,v),b=n.add(b,v),g=n.mul(_,b),u=n.add(u,g),g=n.mul(x,b),l=n.mul(y,l),l=n.sub(l,g),g=n.mul(y,_),d=n.mul(x,d),d=n.add(d,g),new h(l,u,d)}subtract(e){return this.add(e.negate())}is0(){return this.equals(h.ZERO)}wNAF(e){return v.wNAFCached(this,e,h.normalizeZ)}multiplyUnsafe(e){let{endo:r,n:i}=t;ic(`scalar`,e,gl,i);let a=h.ZERO;if(e===gl)return a;if(this.is0()||e===_l)return this;if(!r||v.hasPrecomputes(this))return v.wNAFCachedUnsafe(this,e,h.normalizeZ);let{k1neg:o,k1:s,k2neg:c,k2:l}=r.splitScalar(e),u=a,d=a,f=this;for(;s>gl||l>gl;)s&_l&&(u=u.add(f)),l&_l&&(d=d.add(f)),f=f.double(),s>>=_l,l>>=_l;return o&&(u=u.negate()),c&&(d=d.negate()),d=new h(n.mul(d.px,r.beta),d.py,d.pz),u.add(d)}multiply(e){let{endo:r,n:i}=t;ic(`scalar`,e,_l,i);let a,o;if(r){let{k1neg:t,k1:i,k2neg:s,k2:c}=r.splitScalar(e),{p:l,f:u}=this.wNAF(i),{p:d,f}=this.wNAF(c);l=v.constTimeNegate(t,l),d=v.constTimeNegate(s,d),d=new h(n.mul(d.px,r.beta),d.py,d.pz),a=l.add(d),o=u.add(f)}else{let{p:t,f:n}=this.wNAF(e);a=t,o=n}return h.normalizeZ([a,o])[0]}multiplyAndAddUnsafe(e,t,n){let r=h.BASE,i=(e,t)=>t===gl||t===_l||!e.equals(r)?e.multiplyUnsafe(t):e.multiply(t),a=i(this,t).add(i(e,n));return a.is0()?void 0:a}toAffine(e){return p(this,e)}isTorsionFree(){let{h:e,isTorsionFree:n}=t;if(e===_l)return!0;if(n)return n(h,this);throw Error(`isTorsionFree() has not been declared for the elliptic curve`)}clearCofactor(){let{h:e,clearCofactor:n}=t;return e===_l?this:n?n(h,this):this.multiplyUnsafe(t.h)}toRawBytes(e=!0){return Us(`isCompressed`,e),this.assertValidity(),i(h,this,e)}toHex(e=!0){return Us(`isCompressed`,e),Ks(this.toRawBytes(e))}}h.BASE=new h(t.Gx,t.Gy,n.ONE),h.ZERO=new h(n.ZERO,n.ONE,n.ZERO);let{endo:g,nBitLength:_}=t,v=Qc(h,g?Math.ceil(_/2):_);return{CURVE:t,ProjectivePoint:h,normPrivateKeyToScalar:d,weierstrassEquation:o,isWithinCurveOrder:u}}function ul(e){let t=el(e);return sc(t,{hash:`hash`,hmac:`function`,randomBytes:`function`},{bits2int:`function`,bits2int_modN:`function`,lowS:`boolean`}),Object.freeze({lowS:!0,...t})}function dl(e){let t=ul(e),{Fp:n,n:r,nByteLength:i,nBitLength:a}=t,o=n.BYTES+1,s=2*n.BYTES+1;function c(e){return bc(e,r)}function l(e){return Sc(e,r)}let{ProjectivePoint:u,normPrivateKeyToScalar:d,weierstrassEquation:f,isWithinCurveOrder:p}=ll({...t,toBytes(e,t,r){let i=t.toAffine(),a=n.toBytes(i.x),o=ec;return Us(`isCompressed`,r),r?o(Uint8Array.from([t.hasEvenY()?2:3]),a):o(Uint8Array.from([4]),a,n.toBytes(i.y))},fromBytes(e){let t=e.length,r=e[0],i=e.subarray(1);if(t===o&&(r===2||r===3)){let e=Ys(i);if(!rc(e,_l,n.ORDER))throw Error(`Point is not on curve`);let t=f(e),a;try{a=n.sqrt(t)}catch(e){let t=e instanceof Error?`: `+e.message:``;throw Error(`Point is not on curve`+t)}let o=(a&_l)===_l;return(r&1)==1!==o&&(a=n.neg(a)),{x:e,y:a}}else if(t===s&&r===4)return{x:n.fromBytes(i.subarray(0,n.BYTES)),y:n.fromBytes(i.subarray(n.BYTES,2*n.BYTES))};else{let e=o,n=s;throw Error(`invalid Point, expected length of `+e+`, or uncompressed `+n+`, got `+t)}}});function m(e){return e>r>>_l}function h(e){return m(e)?c(-e):e}let g=(e,t,n)=>Ys(e.slice(t,n));class _{constructor(e,t,n){ic(`r`,e,_l,r),ic(`s`,t,_l,r),this.r=e,this.s=t,n!=null&&(this.recovery=n),Object.freeze(this)}static fromCompact(e){let t=i;return e=$s(`compactSignature`,e,t*2),new _(g(e,0,t),g(e,t,2*t))}static fromDER(e){let{r:t,s:n}=hl.toSig($s(`DER`,e));return new _(t,n)}assertValidity(){}addRecoveryBit(e){return new _(this.r,this.s,e)}recoverPublicKey(e){let{r,s:i,recovery:a}=this,o=ee($s(`msgHash`,e));if(a==null||![0,1,2,3].includes(a))throw Error(`recovery id invalid`);let s=a===2||a===3?r+t.n:r;if(s>=n.ORDER)throw Error(`recovery id 2 or 3 invalid`);let d=a&1?`03`:`02`,f=u.fromHex(d+cl(s,n.BYTES)),p=l(s),m=c(-o*p),h=c(i*p),g=u.BASE.multiplyAndAddUnsafe(f,m,h);if(!g)throw Error(`point at infinify`);return g.assertValidity(),g}hasHighS(){return m(this.s)}normalizeS(){return this.hasHighS()?new _(this.r,c(-this.s),this.recovery):this}toDERRawBytes(){return Js(this.toDERHex())}toDERHex(){return hl.hexFromSig(this)}toCompactRawBytes(){return Js(this.toCompactHex())}toCompactHex(){let e=i;return cl(this.r,e)+cl(this.s,e)}}let v={isValidPrivateKey(e){try{return d(e),!0}catch{return!1}},normPrivateKeyToScalar:d,randomPrivateKey:()=>{let e=Pc(t.n);return Fc(t.randomBytes(e),t.n)},precompute(e=8,t=u.BASE){return t._setWindowSize(e),t.multiply(BigInt(3)),t}};function y(e,t=!0){return u.fromPrivateKey(e).toRawBytes(t)}function b(e){if(typeof e==`bigint`)return!1;if(e instanceof u)return!0;let r=$s(`key`,e).length,a=n.BYTES,o=a+1,s=2*a+1;if(!(t.allowedPrivateKeyLengths||i===o))return r===o||r===s}function x(e,t,n=!0){if(b(e)===!0)throw Error(`first arg must be private key`);if(b(t)===!1)throw Error(`second arg must be public key`);return u.fromHex(t).multiply(d(e)).toRawBytes(n)}let S=t.bits2int||function(e){if(e.length>8192)throw Error(`input is too large`);let t=Ys(e),n=e.length*8-a;return n>0?t>>BigInt(n):t},ee=t.bits2int_modN||function(e){return c(S(e))},C=hc(a);function te(e){return ic(`num < 2^`+a,e,gl,C),Zs(e,i)}function ne(e,r,i=re){if([`recovered`,`canonical`].some(e=>e in i))throw Error(`sign() legacy options not supported`);let{hash:a,randomBytes:o}=t,{lowS:s,prehash:f,extraEntropy:g}=i;s??=!0,e=$s(`msgHash`,e),ol(i),f&&(e=$s(`prehashed msgHash`,a(e)));let v=ee(e),y=d(r),b=[te(y),te(v)];if(g!=null&&g!==!1){let e=g===!0?o(n.BYTES):g;b.push($s(`extraEntropy`,e))}let x=ec(...b),C=v;function ne(e){let t=S(e);if(!p(t))return;let n=l(t),r=u.BASE.multiply(t).toAffine(),i=c(r.x);if(i===gl)return;let a=c(n*c(C+i*y));if(a===gl)return;let o=(r.x===i?0:2)|Number(r.y&_l),d=a;return s&&m(a)&&(d=h(a),o^=1),new _(i,d,o)}return{seed:x,k2sig:ne}}let re={lowS:t.lowS,prehash:!1},ie={lowS:t.lowS,prehash:!1};function ae(e,n,r=re){let{seed:i,k2sig:a}=ne(e,n,r),o=t;return oc(o.hash.outputLen,o.nByteLength,o.hmac)(i,a)}u.BASE._setWindowSize(8);function oe(e,n,r,i=ie){let a=e;n=$s(`msgHash`,n),r=$s(`publicKey`,r);let{lowS:o,prehash:s,format:d}=i;if(ol(i),`strict`in i)throw Error(`options.strict was renamed to lowS`);if(d!==void 0&&d!==`compact`&&d!==`der`)throw Error(`format must be compact or der`);let f=typeof a==`string`||Vs(a),p=!f&&!d&&typeof a==`object`&&!!a&&typeof a.r==`bigint`&&typeof a.s==`bigint`;if(!f&&!p)throw Error(`invalid signature, expected Uint8Array, hex string or Signature instance`);let m,h;try{if(p&&(m=new _(a.r,a.s)),f){try{d!==`compact`&&(m=_.fromDER(a))}catch(e){if(!(e instanceof hl.Err))throw e}!m&&d!==`der`&&(m=_.fromCompact(a))}h=u.fromHex(r)}catch{return!1}if(!m||o&&m.hasHighS())return!1;s&&(n=t.hash(n));let{r:g,s:v}=m,y=ee(n),b=l(v),x=c(y*b),S=c(g*b),C=u.BASE.multiplyAndAddUnsafe(h,x,S)?.toAffine();return C?c(C.x)===g:!1}return{CURVE:t,getPublicKey:y,getSharedSecret:x,sign:ae,verify:oe,ProjectivePoint:u,Signature:_,utils:v}}function fl(e,t){let n=e.ORDER,r=gl;for(let e=n-_l;e%vl===gl;e/=vl)r+=_l;let i=r,a=vl<{let r=d,a=e.pow(n,l),o=e.sqr(a);o=e.mul(o,n);let s=e.mul(t,o);s=e.pow(s,c),s=e.mul(s,a),a=e.mul(s,n),o=e.mul(s,t);let p=e.mul(o,a);s=e.pow(p,u);let m=e.eql(s,e.ONE);a=e.mul(o,f),s=e.mul(p,r),o=e.cmov(a,o,m),p=e.cmov(s,p,m);for(let t=i;t>_l;t--){let n=t-vl;n=vl<{let a=e.sqr(i),o=e.mul(t,i);a=e.mul(a,o);let s=e.pow(a,n);s=e.mul(s,o);let c=e.mul(s,r),l=e.mul(e.sqr(s),i),u=e.eql(l,t);return{isValid:u,value:e.cmov(c,s,u)}}}return p}function pl(e,t){if(Dc(e),!e.isValid(t.A)||!e.isValid(t.B)||!e.isValid(t.Z))throw Error(`mapToCurveSimpleSWU: invalid opts`);let n=fl(e,t.Z);if(!e.isOdd)throw Error(`Fp.isOdd is not implemented!`);return r=>{let i,a,o,s,c,l,u,d;i=e.sqr(r),i=e.mul(i,t.Z),a=e.sqr(i),a=e.add(a,i),o=e.add(a,e.ONE),o=e.mul(o,t.B),s=e.cmov(t.Z,e.neg(a),!e.eql(a,e.ZERO)),s=e.mul(s,t.A),a=e.sqr(o),l=e.sqr(s),c=e.mul(l,t.A),a=e.add(a,c),a=e.mul(a,o),l=e.mul(l,s),c=e.mul(l,t.B),a=e.add(a,c),u=e.mul(i,o);let{isValid:f,value:p}=n(a,l);d=e.mul(i,r),d=e.mul(d,p),u=e.cmov(u,o,f),d=e.cmov(d,p,f);let m=e.isOdd(r)===e.isOdd(d);d=e.cmov(e.neg(d),d,m);let h=kc(e,[s],!0)[0];return u=e.mul(u,h),{x:u,y:d}}}var ml,hl,gl,_l,vl,yl,bl,xl=o((()=>{al(),Wc(),yc(),ml=class extends Error{constructor(e=``){super(e)}},hl={Err:ml,_tlv:{encode:(e,t)=>{let{Err:n}=hl;if(e<0||e>256)throw new n(`tlv.encode: wrong tag`);if(t.length&1)throw new n(`tlv.encode: unpadded data`);let r=t.length/2,i=Ws(r);if(i.length/2&128)throw new n(`tlv.encode: long form length too big`);let a=r>127?Ws(i.length/2|128):``;return Ws(e)+a+i+t},decode(e,t){let{Err:n}=hl,r=0;if(e<0||e>256)throw new n(`tlv.encode: wrong tag`);if(t.length<2||t[r++]!==e)throw new n(`tlv.decode: wrong tlv`);let i=t[r++],a=!!(i&128),o=0;if(!a)o=i;else{let e=i&127;if(!e)throw new n(`tlv.decode(long): indefinite length not supported`);if(e>4)throw new n(`tlv.decode(long): byte length is too big`);let a=t.subarray(r,r+e);if(a.length!==e)throw new n(`tlv.decode: length bytes not complete`);if(a[0]===0)throw new n(`tlv.decode(long): zero leftmost byte`);for(let e of a)o=o<<8|e;if(r+=e,o<128)throw new n(`tlv.decode(long): not minimal encoding`)}let s=t.subarray(r,r+o);if(s.length!==o)throw new n(`tlv.decode: wrong value length`);return{v:s,l:t.subarray(r+o)}}},_int:{encode(e){let{Err:t}=hl;if(ezs(e,t,Or(...n)),randomBytes:jr}}function Cl(e,t){let n=t=>dl({...e,...Sl(t)});return{...n(t),create:n}}var wl=o((()=>{Bs(),Fr(),xl()}));function Tl(e,t){if(Dl(e),Dl(t),e<0||e>=1<<8*t)throw Error(`invalid I2OSP input: `+e);let n=Array.from({length:t}).fill(0);for(let r=t-1;r>=0;r--)n[r]=e&255,e>>>=8;return new Uint8Array(n)}function El(e,t){let n=new Uint8Array(e.length);for(let r=0;r255&&(t=r(ec(nc(`H2C-OVERSIZE-DST-`),t)));let{outputLen:i,blockLen:a}=r,o=Math.ceil(n/i);if(n>65535||o>255)throw Error(`expand_message_xmd: invalid lenInBytes`);let s=ec(t,Tl(t.length,1)),c=Tl(0,a),l=Tl(n,2),u=Array(o),d=r(ec(c,e,l,Tl(0,1),s));u[0]=r(ec(d,Tl(1,1),s));for(let e=1;e<=o;e++)u[e]=r(ec(El(d,u[e-1]),Tl(e+1,1),s));return ec(...u).slice(0,n)}function kl(e,t,n,r,i){if(Hs(e),Hs(t),Dl(n),t.length>255){let e=Math.ceil(2*r/8);t=i.create({dkLen:e}).update(nc(`H2C-OVERSIZE-DST-`)).update(t).digest()}if(n>65535||t.length>255)throw Error(`expand_message_xof: invalid lenInBytes`);return i.create({dkLen:n}).update(e).update(Tl(n,2)).update(t).update(Tl(t.length,1)).digest()}function Al(e,t,n){sc(n,{DST:`stringOrUint8Array`,p:`bigint`,m:`isSafeInteger`,k:`isSafeInteger`,hash:`hash`});let{p:r,k:i,m:a,hash:o,expand:s,DST:c}=n;Hs(e),Dl(t);let l=typeof c==`string`?nc(c):c,u=r.toString(2).length,d=Math.ceil((u+i)/8),f=t*a*d,p;if(s===`xmd`)p=Ol(e,l,f,o);else if(s===`xof`)p=kl(e,l,f,i,o);else if(s===`_internal_pass`)p=e;else throw Error(`expand must be "xmd" or "xof"`);let m=Array(t);for(let e=0;eArray.from(e).reverse());return(t,r)=>{let[i,a,o,s]=n.map(n=>n.reduce((n,r)=>e.add(e.mul(n,t),r))),[c,l]=kc(e,[a,s],!0);return t=e.mul(i,c),r=e.mul(r,e.mul(o,l)),{x:t,y:r}}}function Ml(e,t,n){if(typeof t!=`function`)throw Error(`mapToCurve() must be defined`);function r(n){return e.fromAffine(t(n))}function i(t){let n=t.clearCofactor();return n.equals(e.ZERO)?e.ZERO:(n.assertValidity(),n)}return{defaults:n,hashToCurve(e,t){let a=Al(e,2,{...n,DST:n.DST,...t}),o=r(a[0]),s=r(a[1]);return i(o.add(s))},encodeToCurve(e,t){return i(r(Al(e,1,{...n,DST:n.encodeDST,...t})[0]))},mapToCurve(e){if(!Array.isArray(e))throw Error(`expected array of bigints`);for(let t of e)if(typeof t!=`bigint`)throw Error(`expected array of bigints`);return i(r(e))}}}var Nl,Pl=o((()=>{Wc(),yc(),Nl=Ys})),Fl=c({encodeToCurve:()=>lu,hashToCurve:()=>cu,schnorr:()=>iu,secp256k1:()=>Zl,secp256k1_hasher:()=>su});function Il(e){let t=Wl,n=BigInt(3),r=BigInt(6),i=BigInt(11),a=BigInt(22),o=BigInt(23),s=BigInt(44),c=BigInt(88),l=e*e*e%t,u=l*l*e%t,d=xc(xc(xc(u,n,t)*u%t,n,t)*u%t,Jl,t)*l%t,f=xc(d,i,t)*d%t,p=xc(f,a,t)*f%t,m=xc(p,s,t)*p%t,h=xc(xc(xc(xc(xc(xc(m,c,t)*m%t,s,t)*p%t,n,t)*u%t,o,t)*f%t,r,t)*l%t,Jl,t);if(!Xl.eql(Xl.sqr(h),e))throw Error(`Cannot find square root`);return h}function Ll(e,...t){let n=Ql[e];if(n===void 0){let t=Ps(Uint8Array.from(e,e=>e.charCodeAt(0)));n=ec(t,t),Ql[e]=n}return Ps(ec(n,...t))}function Rl(e){let t=Zl.utils.normPrivateKeyToScalar(e),n=M.fromPrivateKey(t);return{scalar:n.hasEvenY()?t:j(-t),bytes:$l(n)}}function zl(e){ic(`x`,e,ql,Wl);let t=Il(tu(tu(e*e)*e+BigInt(7)));t%Jl!==Kl&&(t=tu(-t));let n=new M(e,t,ql);return n.assertValidity(),n}function Bl(...e){return j(ru(Ll(`BIP0340/challenge`,...e)))}function Vl(e){return Rl(e).bytes}function Hl(e,t,n=jr(32)){let r=$s(`message`,e),{bytes:i,scalar:a}=Rl(t),o=j(ru(Ll(`BIP0340/nonce`,eu(a^ru(Ll(`BIP0340/aux`,$s(`auxRand`,n,32)))),i,r)));if(o===Kl)throw Error(`sign failed: k is zero`);let{bytes:s,scalar:c}=Rl(o),l=Bl(s,i,r),u=new Uint8Array(64);if(u.set(s,0),u.set(eu(j(c+l*a)),32),!Ul(u,r,i))throw Error(`sign: Invalid signature produced`);return u}function Ul(e,t,n){let r=$s(`signature`,e,64),i=$s(`message`,t),a=$s(`publicKey`,n,32);try{let e=zl(ru(a)),t=ru(r.subarray(0,32));if(!rc(t,ql,Wl))return!1;let n=ru(r.subarray(32,64));if(!rc(n,ql,Gl))return!1;let o=nu(e,n,j(-Bl(eu(t),$l(e),i)));return!(!o||!o.hasEvenY()||o.toAffine().x!==t)}catch{return!1}}var Wl,Gl,Kl,ql,Jl,Yl,Xl,Zl,Ql,$l,eu,tu,j,M,nu,ru,iu,au,ou,su,cu,lu,uu=o((()=>{Ls(),Fr(),wl(),Pl(),Wc(),yc(),xl(),Wl=BigInt(`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f`),Gl=BigInt(`0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141`),Kl=BigInt(0),ql=BigInt(1),Jl=BigInt(2),Yl=(e,t)=>(e+t/Jl)/t,Xl=Mc(Wl,void 0,void 0,{sqrt:Il}),Zl=Cl({a:Kl,b:BigInt(7),Fp:Xl,n:Gl,Gx:BigInt(`55066263022277343669578718895168534326250603453777594175500187360389116729240`),Gy:BigInt(`32670510020758816978083085130507043184471273380659243275938904335757337482424`),h:BigInt(1),lowS:!0,endo:{beta:BigInt(`0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee`),splitScalar:e=>{let t=Gl,n=BigInt(`0x3086d221a7d46bcde86c90e49284eb15`),r=-ql*BigInt(`0xe4437ed6010e88286f547fa90abfe4c3`),i=BigInt(`0x114ca50f7a8e2f3f657c1108d9d44cfd8`),a=n,o=BigInt(`0x100000000000000000000000000000000`),s=Yl(a*e,t),c=Yl(-r*e,t),l=bc(e-s*n-c*i,t),u=bc(-s*r-c*a,t),d=l>o,f=u>o;if(d&&(l=t-l),f&&(u=t-u),l>o||u>o)throw Error(`splitScalar: Endomorphism failed, k=`+e);return{k1neg:d,k1:l,k2neg:f,k2:u}}}},Ps),Ql={},$l=e=>e.toRawBytes(!0).slice(1),eu=e=>Zs(e,32),tu=e=>bc(e,Wl),j=e=>bc(e,Gl),M=Zl.ProjectivePoint,nu=(e,t,n)=>M.BASE.multiplyAndAddUnsafe(e,t,n),ru=Ys,iu={getPublicKey:Vl,sign:Hl,verify:Ul,utils:{randomPrivateKey:Zl.utils.randomPrivateKey,lift_x:zl,pointToBytes:$l,numberToBytesBE:Zs,bytesToNumberBE:Ys,taggedHash:Ll,mod:bc}},au=jl(Xl,[[`0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7`,`0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581`,`0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262`,`0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c`],[`0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b`,`0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14`,`0x0000000000000000000000000000000000000000000000000000000000000001`],[`0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c`,`0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3`,`0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931`,`0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84`],[`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b`,`0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573`,`0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f`,`0x0000000000000000000000000000000000000000000000000000000000000001`]].map(e=>e.map(e=>BigInt(e)))),ou=pl(Xl,{A:BigInt(`0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533`),B:BigInt(`1771`),Z:Xl.create(BigInt(`-11`))}),su=Ml(Zl.ProjectivePoint,e=>{let{x:t,y:n}=ou(Xl.create(e[0]));return au(t,n)},{DST:`secp256k1_XMD:SHA-256_SSWU_RO_`,encodeDST:`secp256k1_XMD:SHA-256_SSWU_NU_`,p:Xl.ORDER,m:1,k:128,expand:`xmd`,hash:Ps}),cu=su.hashToCurve,lu=su.encodeToCurve}));Ft(),Lt(),On(),k(),hs();async function du({hash:e,signature:t}){let n=Pt(e)?e:kn(e),{secp256k1:r}=await ms(async()=>{let{secp256k1:e}=await Promise.resolve().then(()=>(uu(),Fl));return{secp256k1:e}},void 0);return`0x${(()=>{if(typeof t==`object`&&`r`in t&&`s`in t){let{r:e,s:n,v:i,yParity:a}=t,o=fu(Number(a??i));return new r.Signature(Tn(e),Tn(n)).addRecoveryBit(o)}let e=Pt(t)?t:kn(t);if(It(e)!==65)throw Error(`invalid signature length`);let n=fu(Dn(`0x${e.slice(130)}`));return r.Signature.fromCompact(e.substring(2,130)).addRecoveryBit(n)})().recoverPublicKey(n.substring(2)).toHex(!1)}`}function fu(e){if(e===0||e===1)return e;if(e===27)return 0;if(e===28)return 1;throw Error(`Invalid yParityOrV value`)}async function pu({hash:e,signature:t}){return us(await du({hash:e,signature:t}))}D(),Sa(),Un(),k();function mu(e,t=`hex`){let n=hu(e),r=ba(new Uint8Array(n.length));return n.encode(r),t===`hex`?jn(r.bytes):r.bytes}function hu(e){return Array.isArray(e)?gu(e.map(e=>hu(e))):_u(e)}function gu(e){let t=e.reduce((e,t)=>e+t.length,0),n=vu(t);return{length:t<=55?1+t:1+n+t,encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function _u(e){let t=typeof e==`string`?Rn(e):e,n=vu(t.length);return{length:t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length,encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function vu(e){if(e<2**8)return 1;if(e<2**16)return 2;if(e<2**24)return 3;if(e<2**32)return 4;throw new E(`Length is too large.`)}Ai(),Un(),k(),ri();function yu(e){let{chainId:t,nonce:n,to:r}=e,i=e.contractAddress??e.address,a=ni(ki([`0x05`,mu([t?O(t):`0x`,i,n?O(n):`0x`])]));return r===`bytes`?Rn(a):a}async function bu(e){let{authorization:t,signature:n}=e;return pu({hash:yu(t),signature:n??t})}$a(),to(),D(),_o();var xu=class extends E{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d}){let f=so({from:t?.address,to:u,value:d!==void 0&&`${Qa(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${eo(o)} gwei`,maxFeePerGas:s!==void 0&&`${eo(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${eo(c)} gwei`,nonce:l});super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Estimate Gas Arguments:`,f].filter(Boolean),name:`EstimateGasExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},Su,Cu,wu,Tu,Eu,Du,Ou,ku,Au,ju,Mu,Nu,Pu=o((()=>{to(),D(),Su=class extends E{constructor({cause:e,message:t}={}){let n=t?.replace(`execution reverted: `,``)?.replace(`execution reverted`,``);super(`Execution reverted ${n?`with reason: ${n}`:`for an unknown reason`}.`,{cause:e,name:`ExecutionRevertedError`})}},Object.defineProperty(Su,`code`,{enumerable:!0,configurable:!0,writable:!0,value:3}),Object.defineProperty(Su,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/execution reverted|gas required exceeds allowance/}),Cu=class extends E{constructor({cause:e,maxFeePerGas:t}={}){super(`The fee cap (\`maxFeePerGas\`${t?` = ${eo(t)} gwei`:``}) cannot be higher than the maximum allowed value (2^256-1).`,{cause:e,name:`FeeCapTooHighError`})}},Object.defineProperty(Cu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max fee per gas higher than 2\^256-1|fee cap higher than 2\^256-1/}),wu=class extends E{constructor({cause:e,maxFeePerGas:t}={}){super(`The fee cap (\`maxFeePerGas\`${t?` = ${eo(t)}`:``} gwei) cannot be lower than the block base fee.`,{cause:e,name:`FeeCapTooLowError`})}},Object.defineProperty(wu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max fee per gas less than block base fee|fee cap less than block base fee|transaction is outdated/}),Tu=class extends E{constructor({cause:e,nonce:t}={}){super(`Nonce provided for the transaction ${t?`(${t}) `:``}is higher than the next one expected.`,{cause:e,name:`NonceTooHighError`})}},Object.defineProperty(Tu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce too high/}),Eu=class extends E{constructor({cause:e,nonce:t}={}){super([`Nonce provided for the transaction ${t?`(${t}) `:``}is lower than the current nonce of the account.`,"Try increasing the nonce or find the latest nonce with `getTransactionCount`."].join(` +`),{cause:e,name:`NonceTooLowError`})}},Object.defineProperty(Eu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce too low|transaction already imported|already known/}),Du=class extends E{constructor({cause:e,nonce:t}={}){super(`Nonce provided for the transaction ${t?`(${t}) `:``}exceeds the maximum allowed nonce.`,{cause:e,name:`NonceMaxValueError`})}},Object.defineProperty(Du,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce has max value/}),Ou=class extends E{constructor({cause:e}={}){super([`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`].join(` +`),{cause:e,metaMessages:[`This error could arise when the account does not have enough funds to:`,` - pay for the total gas fee,`,` - pay for the value to send.`,` `,"The cost of the transaction is calculated as `gas * gas fee + value`, where:"," - `gas` is the amount of gas needed for transaction to execute,"," - `gas fee` is the gas fee,"," - `value` is the amount of ether to send to the recipient."],name:`InsufficientFundsError`})}},Object.defineProperty(Ou,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/insufficient funds|exceeds transaction sender account balance/}),ku=class extends E{constructor({cause:e,gas:t}={}){super(`The amount of gas ${t?`(${t}) `:``}provided for the transaction exceeds the limit allowed for the block.`,{cause:e,name:`IntrinsicGasTooHighError`})}},Object.defineProperty(ku,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/intrinsic gas too high|gas limit reached/}),Au=class extends E{constructor({cause:e,gas:t}={}){super(`The amount of gas ${t?`(${t}) `:``}provided for the transaction is too low.`,{cause:e,name:`IntrinsicGasTooLowError`})}},Object.defineProperty(Au,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/intrinsic gas too low/}),ju=class extends E{constructor({cause:e}){super(`The transaction type is not supported for this chain.`,{cause:e,name:`TransactionTypeNotSupportedError`})}},Object.defineProperty(ju,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/transaction type not valid/}),Mu=class extends E{constructor({cause:e,maxPriorityFeePerGas:t,maxFeePerGas:n}={}){super([`The provided tip (\`maxPriorityFeePerGas\`${t?` = ${eo(t)} gwei`:``}) cannot be higher than the fee cap (\`maxFeePerGas\`${n?` = ${eo(n)} gwei`:``}).`].join(` +`),{cause:e,name:`TipAboveFeeCapError`})}},Object.defineProperty(Mu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max priority fee per gas higher than max fee per gas|tip higher than fee cap/}),Nu=class extends E{constructor({cause:e}){super(`An error occurred while executing: ${e?.shortMessage}`,{cause:e,name:`UnknownNodeError`})}}}));function Fu(e,t){let n=(e.details||``).toLowerCase(),r=e instanceof E?e.walk(e=>e?.code===Su.code):e;return r instanceof E?new Su({cause:e,message:r.details}):Su.nodeMessage.test(n)?new Su({cause:e,message:e.details}):Cu.nodeMessage.test(n)?new Cu({cause:e,maxFeePerGas:t?.maxFeePerGas}):wu.nodeMessage.test(n)?new wu({cause:e,maxFeePerGas:t?.maxFeePerGas}):Tu.nodeMessage.test(n)?new Tu({cause:e,nonce:t?.nonce}):Eu.nodeMessage.test(n)?new Eu({cause:e,nonce:t?.nonce}):Du.nodeMessage.test(n)?new Du({cause:e,nonce:t?.nonce}):Ou.nodeMessage.test(n)?new Ou({cause:e}):ku.nodeMessage.test(n)?new ku({cause:e,gas:t?.gas}):Au.nodeMessage.test(n)?new Au({cause:e,gas:t?.gas}):ju.nodeMessage.test(n)?new ju({cause:e}):Mu.nodeMessage.test(n)?new Mu({cause:e,maxFeePerGas:t?.maxFeePerGas,maxPriorityFeePerGas:t?.maxPriorityFeePerGas}):new Nu({cause:e})}var Iu=o((()=>{D(),Pu()}));Pu(),Iu();function Lu(e,{docsPath:t,...n}){return new xu((()=>{let t=Fu(e,n);return t instanceof Nu?e:t})(),{docsPath:t,...n})}function Ru(e,{format:t}){if(!t)return{};let n={};function r(t){let i=Object.keys(t);for(let a of i)a in e&&(n[a]=e[a]),t[a]&&typeof t[a]==`object`&&!Array.isArray(t[a])&&r(t[a])}return r(t(e||{})),n}var zu=o((()=>{}));function Bu(e,t){return({exclude:n,format:r})=>({exclude:n,format:(e,i)=>{let a=t(e,i);if(n)for(let e of n)delete a[e];return{...a,...r(e,i)}},type:e})}var Vu=o((()=>{}));function Hu(e,t){let n={};return e.authorizationList!==void 0&&(n.authorizationList=Uu(e.authorizationList)),e.accessList!==void 0&&(n.accessList=e.accessList),e.blobVersionedHashes!==void 0&&(n.blobVersionedHashes=e.blobVersionedHashes),e.blobs!==void 0&&(typeof e.blobs[0]==`string`?n.blobs=e.blobs:n.blobs=e.blobs.map(e=>jn(e))),e.data!==void 0&&(n.data=e.data),e.account&&(n.from=e.account.address),e.from!==void 0&&(n.from=e.from),e.gas!==void 0&&(n.gas=O(e.gas)),e.gasPrice!==void 0&&(n.gasPrice=O(e.gasPrice)),e.maxFeePerBlobGas!==void 0&&(n.maxFeePerBlobGas=O(e.maxFeePerBlobGas)),e.maxFeePerGas!==void 0&&(n.maxFeePerGas=O(e.maxFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(n.maxPriorityFeePerGas=O(e.maxPriorityFeePerGas)),e.nonce!==void 0&&(n.nonce=O(e.nonce)),e.to!==void 0&&(n.to=e.to),e.type!==void 0&&(n.type=Wu[e.type]),e.value!==void 0&&(n.value=O(e.value)),n}function Uu(e){return e.map(e=>({address:e.address,r:e.r?O(BigInt(e.r)):e.r,s:e.s?O(BigInt(e.s)):e.s,chainId:O(e.chainId),nonce:O(e.nonce),...e.yParity===void 0?{}:{yParity:O(e.yParity)},...e.v!==void 0&&e.yParity===void 0?{v:O(e.v)}:{}}))}var Wu,Gu,Ku=o((()=>{k(),Vu(),Wu={legacy:`0x0`,eip2930:`0x1`,eip1559:`0x2`,eip4844:`0x3`,eip7702:`0x4`},Gu=Bu(`transactionRequest`,Hu)}));function qu(e){if(!(!e||e.length===0))return e.reduce((e,{slot:t,value:n})=>{if(t.length!==66)throw new dn({size:t.length,targetSize:66,type:`hex`});if(n.length!==66)throw new dn({size:n.length,targetSize:66,type:`hex`});return e[t]=n,e},{})}function Ju(e){let{balance:t,nonce:n,state:r,stateDiff:i,code:a}=e,o={};if(a!==void 0&&(o.code=a),t!==void 0&&(o.balance=O(t)),n!==void 0&&(o.nonce=O(n)),r!==void 0&&(o.state=qu(r)),i!==void 0){if(o.state)throw new ao;o.stateDiff=qu(i)}return o}function Yu(e){if(!e)return;let t={};for(let{address:n,...r}of e){if(!Ci(n,{strict:!1}))throw new hi({address:n});if(t[n])throw new io({address:n});t[n]=Ju(r)}return t}var Xu=o((()=>{gi(),fn(),oo(),Ei(),k()})),Zu,Qu,$u=o((()=>{2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n)),Zu=2n**16n-1n,Qu=2n**256n-1n}));function ed(e){let{account:t,maxFeePerGas:n,maxPriorityFeePerGas:r,to:i}=e,a=t?aa(t):void 0;if(a&&!Ci(a.address))throw new hi({address:a.address});if(i&&!Ci(i))throw new hi({address:i});if(n&&n>Qu)throw new Cu({maxFeePerGas:n});if(r&&n&&r>n)throw new Mu({maxFeePerGas:n,maxPriorityFeePerGas:r})}var td=o((()=>{oa(),$u(),gi(),Pu(),Ei()}));to(),D();var nd=class extends E{constructor(){super("`baseFeeMultiplier` must be greater than 1.",{name:`BaseFeeScalarError`})}},rd=class extends E{constructor(){super(`Chain does not support EIP-1559 fees.`,{name:`Eip1559FeesNotSupportedError`})}},id=class extends E{constructor({maxPriorityFeePerGas:e}){super(`\`maxFeePerGas\` cannot be less than the \`maxPriorityFeePerGas\` (${eo(e)} gwei).`,{name:`MaxFeePerGasTooLowError`})}};D();var ad=class extends E{constructor({blockHash:e,blockNumber:t}){let n=`Block`;e&&(n=`Block at hash "${e}"`),t&&(n=`Block at number "${t}"`),super(`${n} could not be found.`,{name:`BlockNotFoundError`})}};On(),Vu();var od={"0x0":`legacy`,"0x1":`eip2930`,"0x2":`eip1559`,"0x3":`eip4844`,"0x4":`eip7702`};function sd(e,t){let n={...e,blockHash:e.blockHash?e.blockHash:null,blockNumber:e.blockNumber?BigInt(e.blockNumber):null,chainId:e.chainId?Dn(e.chainId):void 0,gas:e.gas?BigInt(e.gas):void 0,gasPrice:e.gasPrice?BigInt(e.gasPrice):void 0,maxFeePerBlobGas:e.maxFeePerBlobGas?BigInt(e.maxFeePerBlobGas):void 0,maxFeePerGas:e.maxFeePerGas?BigInt(e.maxFeePerGas):void 0,maxPriorityFeePerGas:e.maxPriorityFeePerGas?BigInt(e.maxPriorityFeePerGas):void 0,nonce:e.nonce?Dn(e.nonce):void 0,to:e.to?e.to:null,transactionIndex:e.transactionIndex?Number(e.transactionIndex):null,type:e.type?od[e.type]:void 0,typeHex:e.type?e.type:void 0,value:e.value?BigInt(e.value):void 0,v:e.v?BigInt(e.v):void 0};return e.authorizationList&&(n.authorizationList=ld(e.authorizationList)),n.yParity=(()=>{if(e.yParity)return Number(e.yParity);if(typeof n.v==`bigint`){if(n.v===0n||n.v===27n)return 0;if(n.v===1n||n.v===28n)return 1;if(n.v>=35n)return n.v%2n==0n?1:0}})(),n.type===`legacy`&&(delete n.accessList,delete n.maxFeePerBlobGas,delete n.maxFeePerGas,delete n.maxPriorityFeePerGas,delete n.yParity),n.type===`eip2930`&&(delete n.maxFeePerBlobGas,delete n.maxFeePerGas,delete n.maxPriorityFeePerGas),n.type===`eip1559`&&delete n.maxFeePerBlobGas,n}var cd=Bu(`transaction`,sd);function ld(e){return e.map(e=>({address:e.address,chainId:Number(e.chainId),nonce:Number(e.nonce),r:e.r,s:e.s,yParity:Number(e.yParity)}))}Vu();function ud(e,t){let n=(e.transactions??[]).map(e=>typeof e==`string`?e:sd(e));return{...e,baseFeePerGas:e.baseFeePerGas?BigInt(e.baseFeePerGas):null,blobGasUsed:e.blobGasUsed?BigInt(e.blobGasUsed):void 0,difficulty:e.difficulty?BigInt(e.difficulty):void 0,excessBlobGas:e.excessBlobGas?BigInt(e.excessBlobGas):void 0,gasLimit:e.gasLimit?BigInt(e.gasLimit):void 0,gasUsed:e.gasUsed?BigInt(e.gasUsed):void 0,hash:e.hash?e.hash:null,logsBloom:e.logsBloom?e.logsBloom:null,nonce:e.nonce?e.nonce:null,number:e.number?BigInt(e.number):null,size:e.size?BigInt(e.size):void 0,timestamp:e.timestamp?BigInt(e.timestamp):void 0,transactions:n,totalDifficulty:e.totalDifficulty?BigInt(e.totalDifficulty):null}}var dd=Bu(`block`,ud);k();async function fd(e,{blockHash:t,blockNumber:n,blockTag:r=e.experimental_blockTag??`latest`,includeTransactions:i}={}){let a=i??!1,o=n===void 0?void 0:O(n),s=null;if(s=t?await e.request({method:`eth_getBlockByHash`,params:[t,a]},{dedupe:!0}):await e.request({method:`eth_getBlockByNumber`,params:[o||r,a]},{dedupe:!!o}),!s)throw new ad({blockHash:t,blockNumber:n});return(e.chain?.formatters?.block?.format||ud)(s,`getBlock`)}async function pd(e){let t=await e.request({method:`eth_gasPrice`});return BigInt(t)}On();async function md(e,t){let{block:n,chain:r=e.chain,request:i}=t||{};try{let t=r?.fees?.maxPriorityFeePerGas??r?.fees?.defaultPriorityFee;if(typeof t==`function`){let r=await t({block:n||await T(e,fd,`getBlock`)({}),client:e,request:i});if(r===null)throw Error();return r}return t===void 0?Tn(await e.request({method:`eth_maxPriorityFeePerGas`})):t}catch{let[t,r]=await Promise.all([n?Promise.resolve(n):T(e,fd,`getBlock`)({}),T(e,pd,`getGasPrice`)({})]);if(typeof t.baseFeePerGas!=`bigint`)throw new rd;let i=r-t.baseFeePerGas;return i<0n?0n:i}}async function hd(e,t){let{block:n,chain:r=e.chain,request:i,type:a=`eip1559`}=t||{},o=await(async()=>typeof r?.fees?.baseFeeMultiplier==`function`?r.fees.baseFeeMultiplier({block:n,client:e,request:i}):r?.fees?.baseFeeMultiplier??1.2)();if(o<1)throw new nd;let s=10**(o.toString().split(`.`)[1]?.length??0),c=e=>e*BigInt(Math.ceil(o*s))/BigInt(s),l=n||await T(e,fd,`getBlock`)({});if(typeof r?.fees?.estimateFeesPerGas==`function`){let t=await r.fees.estimateFeesPerGas({block:n,client:e,multiply:c,request:i,type:a});if(t!==null)return t}if(a===`eip1559`){if(typeof l.baseFeePerGas!=`bigint`)throw new rd;let t=typeof i?.maxPriorityFeePerGas==`bigint`?i.maxPriorityFeePerGas:await md(e,{block:l,chain:r,request:i}),n=c(l.baseFeePerGas);return{maxFeePerGas:i?.maxFeePerGas??n+t,maxPriorityFeePerGas:t}}return{gasPrice:i?.gasPrice??c(await T(e,pd,`getGasPrice`)({}))}}On(),k();async function gd(e,{address:t,blockTag:n=`latest`,blockNumber:r}){return Dn(await e.request({method:`eth_getTransactionCount`,params:[t,typeof r==`bigint`?O(r):n]},{dedupe:!!r}))}Ku(),Un(),k();function _d(e){let{kzg:t}=e,n=e.to??(typeof e.blobs[0]==`string`?`hex`:`bytes`),r=typeof e.blobs[0]==`string`?e.blobs.map(e=>Rn(e)):e.blobs,i=[];for(let e of r)i.push(Uint8Array.from(t.blobToKzgCommitment(e)));return n===`bytes`?i:i.map(e=>jn(e))}Un(),k();function vd(e){let{kzg:t}=e,n=e.to??(typeof e.blobs[0]==`string`?`hex`:`bytes`),r=typeof e.blobs[0]==`string`?e.blobs.map(e=>Rn(e)):e.blobs,i=typeof e.commitments[0]==`string`?e.commitments.map(e=>Rn(e)):e.commitments,a=[];for(let e=0;ejn(e))}Ls();var yd=Ps;Ft(),Un(),k();function bd(e,t){let n=t||`hex`,r=yd(Pt(e,{strict:!1})?Fn(e):e);return n===`bytes`?r:kn(r)}k();function xd(e){let{commitment:t,version:n=1}=e,r=e.to??(typeof t==`string`?`hex`:`bytes`),i=bd(t,`bytes`);return i.set([n],0),r===`bytes`?i:jn(i)}function Sd(e){let{commitments:t,version:n}=e,r=e.to??(typeof t[0]==`string`?`hex`:`bytes`),i=[];for(let e of t)i.push(xd({commitment:e,to:r,version:n}));return i}var Cd=6,wd=4096,Td=32*wd,Ed=Td*Cd-1-1*wd*Cd;D();var Dd=class extends E{constructor({maxSize:e,size:t}){super(`Blob size is too large.`,{metaMessages:[`Max: ${e} bytes`,`Given: ${t} bytes`],name:`BlobSizeTooLargeError`})}},Od=class extends E{constructor(){super(`Blob data must not be empty.`,{name:`EmptyBlobError`})}},kd=class extends E{constructor({hash:e,size:t}){super(`Versioned hash "${e}" size is invalid.`,{metaMessages:[`Expected: 32`,`Received: ${t}`],name:`InvalidVersionedHashSizeError`})}},Ad=class extends E{constructor({hash:e,version:t}){super(`Versioned hash "${e}" version is invalid.`,{metaMessages:[`Expected: 1`,`Received: ${t}`],name:`InvalidVersionedHashVersionError`})}};Sa(),Lt(),Un(),k();function jd(e){let t=e.to??(typeof e.data==`string`?`hex`:`bytes`),n=typeof e.data==`string`?Rn(e.data):e.data,r=It(n);if(!r)throw new Od;if(r>761855)throw new Dd({maxSize:Ed,size:r});let i=[],a=!0,o=0;for(;a;){let e=ba(new Uint8Array(Td)),t=0;for(;te.bytes):i.map(e=>jn(e.bytes))}function Md(e){let{data:t,kzg:n,to:r}=e,i=e.blobs??jd({data:t,to:r}),a=e.commitments??_d({blobs:i,kzg:n,to:r}),o=e.proofs??vd({blobs:i,commitments:a,kzg:n,to:r}),s=[];for(let e=0;e{let t=Fu(e,n);return t instanceof Nu?e:t})(),{docsPath:t,...n})}On();async function Fd(e){return Dn(await e.request({method:`eth_chainId`},{dedupe:!0}))}oa(),zu(),td();async function Id(e,t){let{account:n=e.account,accessList:r,authorizationList:i,chain:a=e.chain,blobVersionedHashes:o,blobs:s,data:c,gas:l,gasPrice:u,maxFeePerBlobGas:d,maxFeePerGas:f,maxPriorityFeePerGas:p,nonce:m,nonceManager:h,to:g,type:_,value:v,...y}=t,b=await(async()=>{if(!n||!h||m!==void 0)return m;let t=aa(n),r=a?a.id:await T(e,Fd,`getChainId`)({});return await h.consume({address:t.address,chainId:r,client:e})})();ed(t);let x=a?.formatters?.transactionRequest?.format,S=(x||Hu)({...Ru(y,{format:x}),account:n?aa(n):void 0,accessList:r,authorizationList:i,blobs:s,blobVersionedHashes:o,data:c,gas:l,gasPrice:u,maxFeePerBlobGas:d,maxFeePerGas:f,maxPriorityFeePerGas:p,nonce:b,to:g,type:_,value:v},`fillTransaction`);try{let n=await e.request({method:`eth_fillTransaction`,params:[S]}),r=(a?.formatters?.transaction?.format||sd)(n.tx);delete r.blockHash,delete r.blockNumber,delete r.r,delete r.s,delete r.transactionIndex,delete r.v,delete r.yParity,r.data=r.input,r.gas&&=t.gas??r.gas,r.gasPrice&&=t.gasPrice??r.gasPrice,r.maxFeePerBlobGas&&=t.maxFeePerBlobGas??r.maxFeePerBlobGas,r.maxFeePerGas&&=t.maxFeePerGas??r.maxFeePerGas,r.maxPriorityFeePerGas&&=t.maxPriorityFeePerGas??r.maxPriorityFeePerGas,r.nonce&&=t.nonce??r.nonce;let i=await(async()=>{if(typeof a?.fees?.baseFeeMultiplier==`function`){let n=await T(e,fd,`getBlock`)({});return a.fees.baseFeeMultiplier({block:n,client:e,request:t})}return a?.fees?.baseFeeMultiplier??1.2})();if(i<1)throw new nd;let o=10**(i.toString().split(`.`)[1]?.length??0),s=e=>e*BigInt(Math.ceil(i*o))/BigInt(o);return r.maxFeePerGas&&!t.maxFeePerGas&&(r.maxFeePerGas=s(r.maxFeePerGas)),r.gasPrice&&!t.gasPrice&&(r.gasPrice=s(r.gasPrice)),{raw:n.raw,transaction:{from:S.from,...r}}}catch(n){throw Pd(n,{...t,chain:e.chain})}}oa(),vi(),td();var Ld=[`blobVersionedHashes`,`chainId`,`fees`,`gas`,`nonce`,`type`],Rd=new Map,N=new _i(128);async function zd(e,t){let n=t;n.account??=e.account,n.parameters??=Ld;let{account:r,chain:i=e.chain,nonceManager:a,parameters:o}=n,s=(()=>{if(typeof i?.prepareTransactionRequest==`function`)return{fn:i.prepareTransactionRequest,runAt:[`beforeFillTransaction`]};if(Array.isArray(i?.prepareTransactionRequest))return{fn:i.prepareTransactionRequest[0],runAt:i.prepareTransactionRequest[1].runAt}})(),c;async function l(){return c||(n.chainId===void 0?i?i.id:(c=await T(e,Fd,`getChainId`)({}),c):n.chainId)}let u=r&&aa(r),d=n.nonce;if(o.includes(`nonce`)&&d===void 0&&u&&a){let t=await l();d=await a.consume({address:u.address,chainId:t,client:e})}s?.fn&&s.runAt?.includes(`beforeFillTransaction`)&&(n=await s.fn({...n,chain:i},{phase:`beforeFillTransaction`}),d??=n.nonce);let f=!((o.includes(`blobVersionedHashes`)||o.includes(`sidecars`))&&n.kzg&&n.blobs||N.get(e.uid)===!1||![`fees`,`gas`].some(e=>o.includes(e)))&&(o.includes(`chainId`)&&typeof n.chainId!=`number`||o.includes(`nonce`)&&typeof d!=`number`||o.includes(`fees`)&&typeof n.gasPrice!=`bigint`&&(typeof n.maxFeePerGas!=`bigint`||typeof n.maxPriorityFeePerGas!=`bigint`)||o.includes(`gas`)&&typeof n.gas!=`bigint`)?await T(e,Id,`fillTransaction`)({...n,nonce:d}).then(t=>{let{chainId:r,from:i,gas:a,gasPrice:o,nonce:s,maxFeePerBlobGas:c,maxFeePerGas:l,maxPriorityFeePerGas:u,type:d,...f}=t.transaction;return N.set(e.uid,!0),{...n,...i?{from:i}:{},...d&&!n.type?{type:d}:{},...r===void 0?{}:{chainId:r},...a===void 0?{}:{gas:a},...o===void 0?{}:{gasPrice:o},...s===void 0?{}:{nonce:s},...c!==void 0&&n.type!==`legacy`&&n.type!==`eip2930`?{maxFeePerBlobGas:c}:{},...l!==void 0&&n.type!==`legacy`&&n.type!==`eip2930`?{maxFeePerGas:l}:{},...u!==void 0&&n.type!==`legacy`&&n.type!==`eip2930`?{maxPriorityFeePerGas:u}:{},...`nonceKey`in f&&f.nonceKey!==void 0?{nonceKey:f.nonceKey}:{}}}).catch(t=>{let r=t;return r.name===`TransactionExecutionError`&&r.walk?.(e=>{let t=e;return t.name===`MethodNotFoundRpcError`||t.name===`MethodNotSupportedRpcError`||t.message?.includes(`eth_fillTransaction is not available`)})&&N.set(e.uid,!1),n}):n;d??=f.nonce,n={...f,...u?{from:u?.address}:{},...d?{nonce:d}:{}};let{blobs:p,gas:m,kzg:h,type:g}=n;s?.fn&&s.runAt?.includes(`beforeFillParameters`)&&(n=await s.fn({...n,chain:i},{phase:`beforeFillParameters`}));let _;async function v(){return _||(_=await T(e,fd,`getBlock`)({blockTag:`latest`}),_)}if(o.includes(`nonce`)&&d===void 0&&u&&!a&&(n.nonce=await T(e,gd,`getTransactionCount`)({address:u.address,blockTag:`pending`})),(o.includes(`blobVersionedHashes`)||o.includes(`sidecars`))&&p&&h){let e=_d({blobs:p,kzg:h});if(o.includes(`blobVersionedHashes`)){let t=Sd({commitments:e,to:`hex`});n.blobVersionedHashes=t}if(o.includes(`sidecars`)){let t=Md({blobs:p,commitments:e,proofs:vd({blobs:p,commitments:e,kzg:h}),to:`hex`});n.sidecars=t}}if(o.includes(`chainId`)&&(n.chainId=await l()),(o.includes(`fees`)||o.includes(`type`))&&g===void 0)try{n.type=Nd(n)}catch{let t=Rd.get(e.uid);t===void 0&&(t=typeof(await v())?.baseFeePerGas==`bigint`,Rd.set(e.uid,t)),n.type=t?`eip1559`:`legacy`}if(o.includes(`fees`))if(n.type!==`legacy`&&n.type!==`eip2930`){if(n.maxFeePerGas===void 0||n.maxPriorityFeePerGas===void 0){let{maxFeePerGas:t,maxPriorityFeePerGas:r}=await hd(e,{block:await v(),chain:i,request:n});if(n.maxPriorityFeePerGas===void 0&&n.maxFeePerGas&&n.maxFeePerGas{if(Array.isArray(r))return r;if(i?.type!==`local`)return[`blobVersionedHashes`]})();try{let n=await(async()=>{if(t.to)return t.to;if(t.authorizationList&&t.authorizationList.length>0)return await bu({authorization:t.authorizationList[0]}).catch(()=>{throw new E("`to` is required. Could not infer from `authorizationList`")})})(),{accessList:o,authorizationList:s,blobs:c,blobVersionedHashes:l,blockNumber:u,blockTag:d,data:f,gas:p,gasPrice:m,maxFeePerBlobGas:h,maxFeePerGas:g,maxPriorityFeePerGas:_,nonce:v,value:y,stateOverride:b,...x}=r?await zd(e,{...t,parameters:a,to:n}):t;if(p&&t.gas!==p)return p;let S=(typeof u==`bigint`?O(u):void 0)||d,ee=Yu(b);ed(t);let C=e.chain?.formatters?.transactionRequest?.format,te=(C||Hu)({...Ru(x,{format:C}),account:i,accessList:o,authorizationList:s,blobs:c,blobVersionedHashes:l,data:f,gasPrice:m,maxFeePerBlobGas:h,maxFeePerGas:g,maxPriorityFeePerGas:_,nonce:v,to:n,value:y},`estimateGas`);return BigInt(await e.request({method:`eth_estimateGas`,params:ee?[te,S??e.experimental_blockTag??`latest`,ee]:S?[te,S]:[te]}))}catch(n){throw Lu(n,{...t,account:i,chain:e.chain})}}function Vd(e,t){if(!Ci(e,{strict:!1}))throw new hi({address:e});if(!Ci(t,{strict:!1}))throw new hi({address:t});return e.toLowerCase()===t.toLowerCase()}var Hd=o((()=>{gi(),Ei()}));function Ud(e,{args:t,eventName:n}={}){return{...e,blockHash:e.blockHash?e.blockHash:null,blockNumber:e.blockNumber?BigInt(e.blockNumber):null,blockTimestamp:e.blockTimestamp?BigInt(e.blockTimestamp):e.blockTimestamp===null?null:void 0,logIndex:e.logIndex?Number(e.logIndex):null,transactionHash:e.transactionHash?e.transactionHash:null,transactionIndex:e.transactionIndex?Number(e.transactionIndex):null,...n?{args:t,eventName:n}:{}}}function Wd(e){let{abi:t,args:n,functionName:r,data:i}=e,a=t[0];if(r){let e=ta({abi:t,args:n,name:r});if(!e)throw new Qt(r,{docsPath:Gd});a=e}if(a.type!==`function`)throw new Qt(void 0,{docsPath:Gd});if(!a.outputs)throw new $t(a.name,{docsPath:Gd});let o=Oa(a.outputs,i);if(o&&o.length>1)return o;if(o&&o.length===1)return o[0]}var Gd,Kd=o((()=>{cn(),Ba(),ia(),Gd=`/docs/contract/decodeFunctionResult`})),qd,Jd=o((()=>{qd=`0.1.1`}));function Yd(){return qd}var Xd=o((()=>{Jd()}));function Zd(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause?Zd(e.cause,t):t?null:e}var P,Qd=o((()=>{Xd(),P=class e extends Error{static setStaticOptions(t){e.prototype.docsOrigin=t.docsOrigin,e.prototype.showVersion=t.showVersion,e.prototype.version=t.version}constructor(t,n={}){let r=(()=>{if(n.cause instanceof e){if(n.cause.details)return n.cause.details;if(n.cause.shortMessage)return n.cause.shortMessage}return n.cause&&`details`in n.cause&&typeof n.cause.details==`string`?n.cause.details:n.cause?.message?n.cause.message:n.details})(),i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=n.docsOrigin??e.prototype.docsOrigin,o=`${a}${i??``}`,s=!!(n.version??e.prototype.showVersion),c=n.version??e.prototype.version,l=[t||`An error occurred.`,...n.metaMessages?[``,...n.metaMessages]:[],...r||i||s?[``,r?`Details: ${r}`:void 0,i?`See: ${o}`:void 0,s?`Version: ${c}`:void 0]:[]].filter(e=>typeof e==`string`).join(` +`);super(l,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsOrigin`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`showVersion`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.cause=n.cause,this.details=r,this.docs=o,this.docsOrigin=a,this.docsPath=i,this.shortMessage=t,this.showVersion=s,this.version=c}walk(e){return Zd(this,e)}},Object.defineProperty(P,`defaultStaticOptions`,{enumerable:!0,configurable:!0,writable:!0,value:{docsOrigin:`https://oxlib.sh`,showVersion:!1,version:`ox@${Yd()}`}}),P.setStaticOptions(P.defaultStaticOptions)}));function $d(e,t){if(Ef(e)>t)throw new zf({givenSize:Ef(e),maxSize:t})}function ef(e,t){if(typeof t==`number`&&t>0&&t>Ef(e)-1)throw new Bf({offset:t,position:`start`,size:Ef(e)})}function tf(e,t,n){if(typeof t==`number`&&typeof n==`number`&&Ef(e)!==n-t)throw new Bf({offset:n,position:`end`,size:Ef(e)})}function nf(e){if(e>=of.zero&&e<=of.nine)return e-of.zero;if(e>=of.A&&e<=of.F)return e-(of.A-10);if(e>=of.a&&e<=of.f)return e-(of.a-10)}function rf(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;if(e.length>r)throw new Vf({size:e.length,targetSize:r,type:`Bytes`});let i=new Uint8Array(r);for(let t=0;t{Hf(),of={zero:48,nine:57,A:65,F:70,a:97,f:102}}));function cf(e,t){if(Qf(e)>t)throw new up({givenSize:Qf(e),maxSize:t})}function lf(e,t){if(typeof t==`number`&&t>0&&t>Qf(e)-1)throw new dp({offset:t,position:`start`,size:Qf(e)})}function uf(e,t,n){if(typeof t==`number`&&typeof n==`number`&&Qf(e)!==n-t)throw new dp({offset:n,position:`end`,size:Qf(e)})}function df(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;let i=e.replace(`0x`,``);if(i.length>r*2)throw new fp({size:Math.ceil(i.length/2),targetSize:r,type:`Hex`});return`0x${i[n===`right`?`padEnd`:`padStart`](r*2,`0`)}`}function ff(e,t={}){let{dir:n=`left`}=t,r=e.replace(`0x`,``),i=0;for(let e=0;e{pp()}));function mf(e,t,n){return JSON.stringify(e,(e,n)=>typeof t==`function`?t(e,n):typeof n==`bigint`?n.toString()+hf:n,n)}var hf,gf=o((()=>{hf=`#__bigint`}));function _f(e){if(!(e instanceof Uint8Array)&&(!e||typeof e!=`object`||!(`BYTES_PER_ELEMENT`in e)||e.BYTES_PER_ELEMENT!==1||e.constructor.name!==`Uint8Array`))throw new Rf(e)}function vf(...e){let t=0;for(let n of e)t+=n.length;let n=new Uint8Array(t);for(let t=0,r=0;t1||r[0]>1)throw new Lf(r);return!!r[0]}function Af(e,t={}){let{size:n}=t;return n!==void 0&&$d(e,n),tp(qf(e,t),t)}function jf(e,t={}){let{size:n}=t,r=e;return n!==void 0&&($d(r,n),r=Nf(r)),Ff.decode(r)}function Mf(e){return af(e,{dir:`left`})}function Nf(e){return af(e,{dir:`right`})}function Pf(e){try{return _f(e),!0}catch{return!1}}var Ff,If,Lf,Rf,zf,Bf,Vf,Hf=o((()=>{yc(),Qd(),pp(),sf(),pf(),gf(),Ff=new TextDecoder,If=new TextEncoder,Lf=class extends P{constructor(e){super(`Bytes value \`${e}\` is not a valid boolean.`,{metaMessages:["The bytes array must contain a single byte of either a `0` or `1` value."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.InvalidBytesBooleanError`})}},Rf=class extends P{constructor(e){super(`Value \`${typeof e==`object`?mf(e):e}\` of type \`${typeof e}\` is an invalid Bytes value.`,{metaMessages:["Bytes values must be of type `Bytes`."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.InvalidBytesTypeError`})}},zf=class extends P{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SizeOverflowError`})}},Bf=class extends P{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SliceOffsetOutOfBoundsError`})}},Vf=class extends P{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SizeExceedsPaddingSizeError`})}}}));function Uf(e,t={}){let{strict:n=!1}=t;if(!e||typeof e!=`string`)throw new sp(e);if(n&&!/^0x[0-9a-fA-F]*$/.test(e)||!e.startsWith(`0x`))throw new cp(e)}function Wf(...e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}function Gf(e){return e instanceof Uint8Array?qf(e):Array.isArray(e)?qf(new Uint8Array(e)):e}function Kf(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(cf(n,t.size),Yf(n,t.size)):n}function qf(e,t={}){let n=``;for(let t=0;ta||i>1n?r:r-a-1n}function tp(e,t={}){let{signed:n,size:r}=t;return Number(!n&&!r?e:ep(e,t))}function np(e,t={}){let{size:n}=t,r=xf(e);return n&&($d(r,n),r=Nf(r)),new TextDecoder().decode(r)}function rp(e,t={}){let{strict:n=!1}=t;try{return Uf(e,{strict:n}),!0}catch{return!1}}var ip,ap,op,sp,cp,lp,up,dp,fp,pp=o((()=>{Hf(),Qd(),sf(),pf(),gf(),ip=new TextEncoder,ap=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),op=class extends P{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number \`${i}\` is not in safe${r?` ${r*8}-bit`:``}${n?` signed`:` unsigned`} integer range ${e?`(\`${t}\` to \`${e}\`)`:`(above \`${t}\`)`}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.IntegerOutOfRangeError`})}},sp=class extends P{constructor(e){super(`Value \`${typeof e==`object`?mf(e):e}\` of type \`${typeof e}\` is an invalid hex type.`,{metaMessages:['Hex types must be represented as `"0x${string}"`.']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexTypeError`})}},cp=class extends P{constructor(e){super(`Value \`${e}\` is an invalid hex value.`,{metaMessages:['Hex values must start with `"0x"` and contain only hexadecimal characters (0-9, a-f, A-F).']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexValueError`})}},lp=class extends P{constructor(e){super(`Hex value \`"${e}"\` is an odd length (${e.length-2} nibbles).`,{metaMessages:[`It must be an even length.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidLengthError`})}},up=class extends P{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeOverflowError`})}},dp=class extends P{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SliceOffsetOutOfBoundsError`})}},fp=class extends P{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeExceedsPaddingSizeError`})}}}));function mp(e){return{address:e.address,amount:F(e.amount),index:F(e.index),validatorIndex:F(e.validatorIndex)}}var hp=o((()=>{pp()}));function gp(e){return{...typeof e.baseFeePerGas==`bigint`&&{baseFeePerGas:F(e.baseFeePerGas)},...typeof e.blobBaseFee==`bigint`&&{blobBaseFee:F(e.blobBaseFee)},...typeof e.feeRecipient==`string`&&{feeRecipient:e.feeRecipient},...typeof e.gasLimit==`bigint`&&{gasLimit:F(e.gasLimit)},...typeof e.number==`bigint`&&{number:F(e.number)},...typeof e.prevRandao==`bigint`&&{prevRandao:F(e.prevRandao)},...typeof e.time==`bigint`&&{time:F(e.time)},...e.withdrawals&&{withdrawals:e.withdrawals.map(mp)}}}var _p=o((()=>{pp(),hp()})),vp,yp,bp,xp,Sp,Cp=o((()=>{vp=[{inputs:[{components:[{name:`target`,type:`address`},{name:`allowFailure`,type:`bool`},{name:`callData`,type:`bytes`}],name:`calls`,type:`tuple[]`}],name:`aggregate3`,outputs:[{components:[{name:`success`,type:`bool`},{name:`returnData`,type:`bytes`}],name:`returnData`,type:`tuple[]`}],stateMutability:`view`,type:`function`},{inputs:[{name:`addr`,type:`address`}],name:`getEthBalance`,outputs:[{name:`balance`,type:`uint256`}],stateMutability:`view`,type:`function`},{inputs:[],name:`getCurrentBlockTimestamp`,outputs:[{internalType:`uint256`,name:`timestamp`,type:`uint256`}],stateMutability:`view`,type:`function`}],yp=[{name:`query`,type:`function`,stateMutability:`view`,inputs:[{type:`tuple[]`,name:`queries`,components:[{type:`address`,name:`sender`},{type:`string[]`,name:`urls`},{type:`bytes`,name:`data`}]}],outputs:[{type:`bool[]`,name:`failures`},{type:`bytes[]`,name:`responses`}]},{name:`HttpError`,type:`error`,inputs:[{type:`uint16`,name:`status`},{type:`string`,name:`message`}]}],bp=[{inputs:[{name:`dns`,type:`bytes`}],name:`DNSDecodingFailed`,type:`error`},{inputs:[{name:`ens`,type:`string`}],name:`DNSEncodingFailed`,type:`error`},{inputs:[],name:`EmptyAddress`,type:`error`},{inputs:[{name:`status`,type:`uint16`},{name:`message`,type:`string`}],name:`HttpError`,type:`error`},{inputs:[],name:`InvalidBatchGatewayResponse`,type:`error`},{inputs:[{name:`errorData`,type:`bytes`}],name:`ResolverError`,type:`error`},{inputs:[{name:`name`,type:`bytes`},{name:`resolver`,type:`address`}],name:`ResolverNotContract`,type:`error`},{inputs:[{name:`name`,type:`bytes`}],name:`ResolverNotFound`,type:`error`},{inputs:[{name:`primary`,type:`string`},{name:`primaryAddress`,type:`bytes`}],name:`ReverseAddressMismatch`,type:`error`},{inputs:[{internalType:`bytes4`,name:`selector`,type:`bytes4`}],name:`UnsupportedResolverProfile`,type:`error`}],[...bp],[...bp],xp=[{name:`isValidSignature`,type:`function`,stateMutability:`view`,inputs:[{name:`hash`,type:`bytes32`},{name:`signature`,type:`bytes`}],outputs:[{name:``,type:`bytes4`}]}],Sp=[{inputs:[{name:`_signer`,type:`address`},{name:`_hash`,type:`bytes32`},{name:`_signature`,type:`bytes`}],stateMutability:`nonpayable`,type:`constructor`},{inputs:[{name:`_signer`,type:`address`},{name:`_hash`,type:`bytes32`},{name:`_signature`,type:`bytes`}],outputs:[{type:`bool`}],stateMutability:`nonpayable`,type:`function`,name:`isValidSig`}]})),wp=o((()=>{})),Tp,Ep,Dp,Op,kp=o((()=>{Tp=`0x608060405234801561001057600080fd5b5060405161018e38038061018e83398101604081905261002f91610124565b6000808351602085016000f59050803b61004857600080fd5b6000808351602085016000855af16040513d6000823e81610067573d81fd5b3d81f35b634e487b7160e01b600052604160045260246000fd5b600082601f83011261009257600080fd5b81516001600160401b038111156100ab576100ab61006b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156100d9576100d961006b565b6040528181528382016020018510156100f157600080fd5b60005b82811015610110576020818601810151838301820152016100f4565b506000918101602001919091529392505050565b6000806040838503121561013757600080fd5b82516001600160401b0381111561014d57600080fd5b61015985828601610081565b602085015190935090506001600160401b0381111561017757600080fd5b61018385828601610081565b915050925092905056fe`,Ep=`0x608060405234801561001057600080fd5b506040516102c03803806102c083398101604081905261002f916101e6565b836001600160a01b03163b6000036100e457600080836001600160a01b03168360405161005c9190610270565b6000604051808303816000865af19150503d8060008114610099576040519150601f19603f3d011682016040523d82523d6000602084013e61009e565b606091505b50915091508115806100b857506001600160a01b0386163b155b156100e1578060405163101bb98d60e01b81526004016100d8919061028c565b60405180910390fd5b50505b6000808451602086016000885af16040513d6000823e81610103573d81fd5b3d81f35b80516001600160a01b038116811461011e57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561015457818101518382015260200161013c565b50506000910152565b600082601f83011261016e57600080fd5b81516001600160401b0381111561018757610187610123565b604051601f8201601f19908116603f011681016001600160401b03811182821017156101b5576101b5610123565b6040528181528382016020018510156101cd57600080fd5b6101de826020830160208701610139565b949350505050565b600080600080608085870312156101fc57600080fd5b61020585610107565b60208601519094506001600160401b0381111561022157600080fd5b61022d8782880161015d565b93505061023c60408601610107565b60608601519092506001600160401b0381111561025857600080fd5b6102648782880161015d565b91505092959194509250565b60008251610282818460208701610139565b9190910192915050565b60208152600082518060208401526102ab816040850160208701610139565b601f01601f1916919091016040019291505056fe`,Dp=`0x608060405234801561001057600080fd5b5060405161069438038061069483398101604081905261002f9161051e565b600061003c848484610048565b9050806000526001601ff35b60007f64926492649264926492649264926492649264926492649264926492649264926100748361040c565b036101e7576000606080848060200190518101906100929190610577565b60405192955090935091506000906001600160a01b038516906100b69085906105dd565b6000604051808303816000865af19150503d80600081146100f3576040519150601f19603f3d011682016040523d82523d6000602084013e6100f8565b606091505b50509050876001600160a01b03163b60000361016057806101605760405162461bcd60e51b815260206004820152601e60248201527f5369676e617475726556616c696461746f723a206465706c6f796d656e74000060448201526064015b60405180910390fd5b604051630b135d3f60e11b808252906001600160a01b038a1690631626ba7e90610190908b9087906004016105f9565b602060405180830381865afa1580156101ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d19190610633565b6001600160e01b03191614945050505050610405565b6001600160a01b0384163b1561027a57604051630b135d3f60e11b808252906001600160a01b03861690631626ba7e9061022790879087906004016105f9565b602060405180830381865afa158015610244573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102689190610633565b6001600160e01b031916149050610405565b81516041146102df5760405162461bcd60e51b815260206004820152603a602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610157565b6102e7610425565b5060208201516040808401518451859392600091859190811061030c5761030c61065d565b016020015160f81c9050601b811480159061032b57508060ff16601c14155b1561038c5760405162461bcd60e51b815260206004820152603b602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e617475726520762076616c756500000000006064820152608401610157565b60408051600081526020810180835289905260ff83169181019190915260608101849052608081018390526001600160a01b0389169060019060a0016020604051602081039080840390855afa1580156103ea573d6000803e3d6000fd5b505050602060405103516001600160a01b0316149450505050505b9392505050565b600060208251101561041d57600080fd5b508051015190565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b038116811461045857600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561048c578181015183820152602001610474565b50506000910152565b600082601f8301126104a657600080fd5b81516001600160401b038111156104bf576104bf61045b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156104ed576104ed61045b565b60405281815283820160200185101561050557600080fd5b610516826020830160208701610471565b949350505050565b60008060006060848603121561053357600080fd5b835161053e81610443565b6020850151604086015191945092506001600160401b0381111561056157600080fd5b61056d86828701610495565b9150509250925092565b60008060006060848603121561058c57600080fd5b835161059781610443565b60208501519093506001600160401b038111156105b357600080fd5b6105bf86828701610495565b604086015190935090506001600160401b0381111561056157600080fd5b600082516105ef818460208701610471565b9190910192915050565b828152604060208201526000825180604084015261061e816060850160208701610471565b601f01601f1916919091016060019392505050565b60006020828403121561064557600080fd5b81516001600160e01b03198116811461040557600080fd5b634e487b7160e01b600052603260045260246000fdfe5369676e617475726556616c696461746f72237265636f7665725369676e6572`,Op=`0x608060405234801561001057600080fd5b506115b9806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e14610325578063bce38bd714610350578063c3077fa914610380578063ee82ac5e146103b2576100f3565b80634d2301cc1461026257806372425d9d1461029f57806382ad56cb146102ca57806386d516e8146102fa576100f3565b80633408e470116100c65780633408e470146101af578063399542e9146101da5780633e64a6961461020c57806342cbb15c14610237576100f3565b80630f28c97d146100f8578063174dea7114610123578063252dba421461015357806327e86d6e14610184575b600080fd5b34801561010457600080fd5b5061010d6103ef565b60405161011a9190610c0a565b60405180910390f35b61013d60048036038101906101389190610c94565b6103f7565b60405161014a9190610e94565b60405180910390f35b61016d60048036038101906101689190610f0c565b610615565b60405161017b92919061101b565b60405180910390f35b34801561019057600080fd5b506101996107ab565b6040516101a69190611064565b60405180910390f35b3480156101bb57600080fd5b506101c46107b7565b6040516101d19190610c0a565b60405180910390f35b6101f460048036038101906101ef91906110ab565b6107bf565b6040516102039392919061110b565b60405180910390f35b34801561021857600080fd5b506102216107e1565b60405161022e9190610c0a565b60405180910390f35b34801561024357600080fd5b5061024c6107e9565b6040516102599190610c0a565b60405180910390f35b34801561026e57600080fd5b50610289600480360381019061028491906111a7565b6107f1565b6040516102969190610c0a565b60405180910390f35b3480156102ab57600080fd5b506102b4610812565b6040516102c19190610c0a565b60405180910390f35b6102e460048036038101906102df919061122a565b61081a565b6040516102f19190610e94565b60405180910390f35b34801561030657600080fd5b5061030f6109e4565b60405161031c9190610c0a565b60405180910390f35b34801561033157600080fd5b5061033a6109ec565b6040516103479190611286565b60405180910390f35b61036a600480360381019061036591906110ab565b6109f4565b6040516103779190610e94565b60405180910390f35b61039a60048036038101906103959190610f0c565b610ba6565b6040516103a99392919061110b565b60405180910390f35b3480156103be57600080fd5b506103d960048036038101906103d491906112cd565b610bca565b6040516103e69190611064565b60405180910390f35b600042905090565b60606000808484905090508067ffffffffffffffff81111561041c5761041b6112fa565b5b60405190808252806020026020018201604052801561045557816020015b610442610bd5565b81526020019060019003908161043a5790505b5092503660005b828110156105c957600085828151811061047957610478611329565b5b6020026020010151905087878381811061049657610495611329565b5b90506020028101906104a89190611367565b925060008360400135905080860195508360000160208101906104cb91906111a7565b73ffffffffffffffffffffffffffffffffffffffff16818580606001906104f2919061138f565b604051610500929190611431565b60006040518083038185875af1925050503d806000811461053d576040519150601f19603f3d011682016040523d82523d6000602084013e610542565b606091505b5083600001846020018290528215151515815250505081516020850135176105bc577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b826001019250505061045c565b5082341461060c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610603906114a7565b60405180910390fd5b50505092915050565b6000606043915060008484905090508067ffffffffffffffff81111561063e5761063d6112fa565b5b60405190808252806020026020018201604052801561067157816020015b606081526020019060019003908161065c5790505b5091503660005b828110156107a157600087878381811061069557610694611329565b5b90506020028101906106a791906114c7565b92508260000160208101906106bc91906111a7565b73ffffffffffffffffffffffffffffffffffffffff168380602001906106e2919061138f565b6040516106f0929190611431565b6000604051808303816000865af19150503d806000811461072d576040519150601f19603f3d011682016040523d82523d6000602084013e610732565b606091505b5086848151811061074657610745611329565b5b60200260200101819052819250505080610795576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078c9061153b565b60405180910390fd5b81600101915050610678565b5050509250929050565b60006001430340905090565b600046905090565b6000806060439250434091506107d68686866109f4565b905093509350939050565b600048905090565b600043905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b600044905090565b606060008383905090508067ffffffffffffffff81111561083e5761083d6112fa565b5b60405190808252806020026020018201604052801561087757816020015b610864610bd5565b81526020019060019003908161085c5790505b5091503660005b828110156109db57600084828151811061089b5761089a611329565b5b602002602001015190508686838181106108b8576108b7611329565b5b90506020028101906108ca919061155b565b92508260000160208101906108df91906111a7565b73ffffffffffffffffffffffffffffffffffffffff16838060400190610905919061138f565b604051610913929190611431565b6000604051808303816000865af19150503d8060008114610950576040519150601f19603f3d011682016040523d82523d6000602084013e610955565b606091505b5082600001836020018290528215151515815250505080516020840135176109cf577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b8160010191505061087e565b50505092915050565b600045905090565b600041905090565b606060008383905090508067ffffffffffffffff811115610a1857610a176112fa565b5b604051908082528060200260200182016040528015610a5157816020015b610a3e610bd5565b815260200190600190039081610a365790505b5091503660005b82811015610b9c576000848281518110610a7557610a74611329565b5b60200260200101519050868683818110610a9257610a91611329565b5b9050602002810190610aa491906114c7565b9250826000016020810190610ab991906111a7565b73ffffffffffffffffffffffffffffffffffffffff16838060200190610adf919061138f565b604051610aed929190611431565b6000604051808303816000865af19150503d8060008114610b2a576040519150601f19603f3d011682016040523d82523d6000602084013e610b2f565b606091505b508260000183602001829052821515151581525050508715610b90578060000151610b8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b869061153b565b60405180910390fd5b5b81600101915050610a58565b5050509392505050565b6000806060610bb7600186866107bf565b8093508194508295505050509250925092565b600081409050919050565b6040518060400160405280600015158152602001606081525090565b6000819050919050565b610c0481610bf1565b82525050565b6000602082019050610c1f6000830184610bfb565b92915050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f840112610c5457610c53610c2f565b5b8235905067ffffffffffffffff811115610c7157610c70610c34565b5b602083019150836020820283011115610c8d57610c8c610c39565b5b9250929050565b60008060208385031215610cab57610caa610c25565b5b600083013567ffffffffffffffff811115610cc957610cc8610c2a565b5b610cd585828601610c3e565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60008115159050919050565b610d2281610d0d565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610d62578082015181840152602081019050610d47565b83811115610d71576000848401525b50505050565b6000601f19601f8301169050919050565b6000610d9382610d28565b610d9d8185610d33565b9350610dad818560208601610d44565b610db681610d77565b840191505092915050565b6000604083016000830151610dd96000860182610d19565b5060208301518482036020860152610df18282610d88565b9150508091505092915050565b6000610e0a8383610dc1565b905092915050565b6000602082019050919050565b6000610e2a82610ce1565b610e348185610cec565b935083602082028501610e4685610cfd565b8060005b85811015610e825784840389528151610e638582610dfe565b9450610e6e83610e12565b925060208a01995050600181019050610e4a565b50829750879550505050505092915050565b60006020820190508181036000830152610eae8184610e1f565b905092915050565b60008083601f840112610ecc57610ecb610c2f565b5b8235905067ffffffffffffffff811115610ee957610ee8610c34565b5b602083019150836020820283011115610f0557610f04610c39565b5b9250929050565b60008060208385031215610f2357610f22610c25565b5b600083013567ffffffffffffffff811115610f4157610f40610c2a565b5b610f4d85828601610eb6565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000610f918383610d88565b905092915050565b6000602082019050919050565b6000610fb182610f59565b610fbb8185610f64565b935083602082028501610fcd85610f75565b8060005b858110156110095784840389528151610fea8582610f85565b9450610ff583610f99565b925060208a01995050600181019050610fd1565b50829750879550505050505092915050565b60006040820190506110306000830185610bfb565b81810360208301526110428184610fa6565b90509392505050565b6000819050919050565b61105e8161104b565b82525050565b60006020820190506110796000830184611055565b92915050565b61108881610d0d565b811461109357600080fd5b50565b6000813590506110a58161107f565b92915050565b6000806000604084860312156110c4576110c3610c25565b5b60006110d286828701611096565b935050602084013567ffffffffffffffff8111156110f3576110f2610c2a565b5b6110ff86828701610eb6565b92509250509250925092565b60006060820190506111206000830186610bfb565b61112d6020830185611055565b818103604083015261113f8184610e1f565b9050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061117482611149565b9050919050565b61118481611169565b811461118f57600080fd5b50565b6000813590506111a18161117b565b92915050565b6000602082840312156111bd576111bc610c25565b5b60006111cb84828501611192565b91505092915050565b60008083601f8401126111ea576111e9610c2f565b5b8235905067ffffffffffffffff81111561120757611206610c34565b5b60208301915083602082028301111561122357611222610c39565b5b9250929050565b6000806020838503121561124157611240610c25565b5b600083013567ffffffffffffffff81111561125f5761125e610c2a565b5b61126b858286016111d4565b92509250509250929050565b61128081611169565b82525050565b600060208201905061129b6000830184611277565b92915050565b6112aa81610bf1565b81146112b557600080fd5b50565b6000813590506112c7816112a1565b92915050565b6000602082840312156112e3576112e2610c25565b5b60006112f1848285016112b8565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b60008235600160800383360303811261138357611382611358565b5b80830191505092915050565b600080833560016020038436030381126113ac576113ab611358565b5b80840192508235915067ffffffffffffffff8211156113ce576113cd61135d565b5b6020830192506001820236038313156113ea576113e9611362565b5b509250929050565b600081905092915050565b82818337600083830152505050565b600061141883856113f2565b93506114258385846113fd565b82840190509392505050565b600061143e82848661140c565b91508190509392505050565b600082825260208201905092915050565b7f4d756c746963616c6c333a2076616c7565206d69736d61746368000000000000600082015250565b6000611491601a8361144a565b915061149c8261145b565b602082019050919050565b600060208201905081810360008301526114c081611484565b9050919050565b6000823560016040038336030381126114e3576114e2611358565b5b80830191505092915050565b7f4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000600082015250565b600061152560178361144a565b9150611530826114ef565b602082019050919050565b6000602082019050818103600083015261155481611518565b9050919050565b60008235600160600383360303811261157757611576611358565b5b8083019150509291505056fea264697066735822122020c1bc9aacf8e4a6507193432a895a8e77094f45a1395583f07b24e860ef06cd64736f6c634300080c0033`})),Ap,jp,Mp,Np,Pp,Fp=o((()=>{D(),Ap=class extends E{constructor({blockNumber:e,chain:t,contract:n}){super(`Chain "${t.name}" does not support contract "${n.name}".`,{metaMessages:[`This could be due to any of the following:`,...e&&n.blockCreated&&n.blockCreated>e?[`- The contract "${n.name}" was not deployed until block ${n.blockCreated} (current block ${e}).`]:[`- The chain does not have the contract "${n.name}" configured.`]],name:`ChainDoesNotSupportContract`})}},jp=class extends E{constructor({chain:e,currentChainId:t}){super(`The current chain of the wallet (id: ${t}) does not match the target chain for the transaction (id: ${e.id} – ${e.name}).`,{metaMessages:[`Current Chain ID: ${t}`,`Expected Chain ID: ${e.id} – ${e.name}`],name:`ChainMismatchError`})}},Mp=class extends E{constructor(){super([`No chain was provided to the request.`,"Please provide a chain with the `chain` argument on the Action, or by supplying a `chain` to WalletClient."].join(` +`),{name:`ChainNotFoundError`})}},Np=class extends E{constructor(){super(`No chain was provided to the Client.`,{name:`ClientChainNotConfiguredError`})}},Pp=class extends E{constructor({chainId:e}){super(typeof e==`number`?`Chain ID "${e}" is invalid.`:`Chain ID is invalid.`,{name:`InvalidChainIdError`})}}}));function Ip(e){let{abi:t,args:n,bytecode:r}=e;if(!n||n.length===0)return r;let i=t.find(e=>`type`in e&&e.type===`constructor`);if(!i)throw new Ht({docsPath:Lp});if(!(`inputs`in i)||!i.inputs||i.inputs.length===0)throw new Ut({docsPath:Lp});return ki([r,Bi(i.inputs,n)])}var Lp,Rp=o((()=>{cn(),Ai(),Qi(),Lp=`/docs/contract/encodeDeployData`}));function zp({blockNumber:e,chain:t,contract:n}){let r=t?.contracts?.[n];if(!r)throw new Ap({chain:t,contract:{name:n}});if(e&&r.blockCreated&&r.blockCreated>e)throw new Ap({blockNumber:e,chain:t,contract:{name:n,blockCreated:r.blockCreated}});return r.address}var Bp=o((()=>{Fp()}));function Vp(e,{docsPath:t,...n}){return new xo((()=>{let t=Fu(e,n);return t instanceof Nu?e:t})(),{docsPath:t,...n})}var Hp=o((()=>{Eo(),Pu(),Iu()}));function Up(){let e=()=>void 0,t=()=>void 0;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var Wp=o((()=>{}));function Gp({fn:e,id:t,shouldSplitBatch:n,wait:r=0,sort:i}){let a=async()=>{let t=c();o();let n=t.map(({args:e})=>e);n.length!==0&&e(n).then(e=>{i&&Array.isArray(e)&&e.sort(i);for(let n=0;n{for(let n=0;nKp.delete(t),s=()=>c().map(({args:e})=>e),c=()=>Kp.get(t)||[],l=e=>Kp.set(t,[...c(),e]);return{flush:o,async schedule(e){let{promise:t,resolve:i,reject:o}=Up();return n?.([...s(),e])&&a(),c().length>0?(l({args:e,resolve:i,reject:o}),t):(l({args:e,resolve:i,reject:o}),setTimeout(a,r),t)}}}var Kp,qp=o((()=>{Wp(),Kp=new Map})),Jp,Yp,Xp,Zp=o((()=>{Wa(),D(),bo(),Jp=class extends E{constructor({callbackSelector:e,cause:t,data:n,extraData:r,sender:i,urls:a}){super(t.shortMessage||`An error occurred while fetching for an offchain result.`,{cause:t,metaMessages:[...t.metaMessages||[],t.metaMessages?.length?``:[],`Offchain Gateway Call:`,a&&[` Gateway URL(s):`,...a.map(e=>` ${yo(e)}`)],` Sender: ${i}`,` Data: ${n}`,` Callback selector: ${e}`,` Extra data: ${r}`].flat(),name:`OffchainLookupError`})}},Yp=class extends E{constructor({result:e,url:t}){super(`Offchain gateway response is malformed. Response data must be a hex value.`,{metaMessages:[`Gateway URL: ${yo(t)}`,`Response: ${Ua(e)}`],name:`OffchainLookupResponseMalformedError`})}},Xp=class extends E{constructor({sender:e,to:t}){super("Reverted sender address does not match target contract address (`to`).",{metaMessages:[`Contract address: ${t}`,`OffchainLookup sender address: ${e}`],name:`OffchainLookupSenderMismatchError`})}}}));function Qp(e){let{abi:t,data:n}=e,r=ji(n,0,4),i=t.find(e=>e.type===`function`&&r===$i(At(e)));if(!i)throw new en(r,{docsPath:`/docs/contract/decodeFunctionData`});return{functionName:i.name,args:`inputs`in i&&i.inputs&&i.inputs.length>0?Oa(i.inputs,ji(n,4)):void 0}}var $p=o((()=>{cn(),Ii(),ea(),Ba(),Nt()}));function em(e){let{abi:t,errorName:n,args:r}=e,i=t[0];if(n){let e=ta({abi:t,args:r,name:n});if(!e)throw new Xt(n,{docsPath:tm});i=e}if(i.type!==`error`)throw new Xt(void 0,{docsPath:tm});let a=$i(At(i)),o=`0x`;if(r&&r.length>0){if(!i.inputs)throw new Yt(i.name,{docsPath:tm});o=Bi(i.inputs,r)}return ki([a,o])}var tm,nm=o((()=>{cn(),Ai(),ea(),Qi(),Nt(),ia(),tm=`/docs/contract/encodeErrorResult`}));function rm(e){let{abi:t,functionName:n,result:r}=e,i=t[0];if(n){let e=ta({abi:t,name:n});if(!e)throw new Qt(n,{docsPath:im});i=e}if(i.type!==`function`)throw new Qt(void 0,{docsPath:im});if(!i.outputs)throw new $t(i.name,{docsPath:im});let a=(()=>{if(i.outputs.length===0)return[];if(i.outputs.length===1)return[r];if(Array.isArray(r))return r;throw new on(r)})();return Bi(i.outputs,a)}var im,am=o((()=>{cn(),Qi(),ia(),im=`/docs/contract/encodeFunctionResult`}));async function om(e){let{data:t,ccipRequest:n}=e,{args:[r]}=Qp({abi:yp,data:t}),i=[],a=[];return await Promise.all(r.map(async(e,t)=>{try{a[t]=e.urls.includes(`x-batch-gateway:true`)?await om({data:e.data,ccipRequest:n}):await n(e),i[t]=!1}catch(e){i[t]=!0,a[t]=sm(e)}})),rm({abi:yp,functionName:`query`,result:[i,a]})}function sm(e){return e.name===`HttpRequestError`&&e.status?em({abi:yp,errorName:`HttpError`,args:[e.status,e.shortMessage]}):em({abi:[pa],errorName:`Error`,args:[`shortMessage`in e?e.shortMessage:e.message]})}var cm=o((()=>{Cp(),ha(),$p(),nm(),am()})),lm=c({ccipRequest:()=>dm,offchainLookup:()=>um,offchainLookupAbiItem:()=>pm,offchainLookupSignature:()=>fm});async function um(e,{blockNumber:t,blockTag:n,data:r,to:i}){let{args:a}=Va({data:r,abi:[pm]}),[o,s,c,l,u]=a,{ccipRead:d}=e,f=d&&typeof d?.request==`function`?d.request:dm;try{if(!Vd(i,o))throw new Xp({sender:o,to:i});let{data:r}=await hm(e,{blockNumber:t,blockTag:n,data:Di([l,Bi([{type:`bytes`},{type:`bytes`}],[s.includes(`x-batch-gateway:true`)?await om({data:c,ccipRequest:f}):await f({data:c,sender:o,urls:s}),u])]),to:i});return r}catch(e){throw new Jp({callbackSelector:l,cause:e,data:r,extraData:u,sender:o,urls:s})}}async function dm({data:e,sender:t,urls:n}){let r=Error(`An unknown error occurred.`);for(let i=0;i{xm(),Zp(),Ao(),Ha(),Qi(),Hd(),Ai(),Ft(),cm(),Wa(),fm=`0x556f1830`,pm={name:`OffchainLookup`,type:`error`,inputs:[{name:`sender`,type:`address`},{name:`urls`,type:`string[]`},{name:`callData`,type:`bytes`},{name:`callbackFunction`,type:`bytes4`},{name:`extraData`,type:`bytes`}]}}));async function hm(e,t){let{account:n=e.account,authorizationList:r,batch:i=!!e.batch?.multicall,blockNumber:a,blockTag:o=e.experimental_blockTag??`latest`,accessList:s,blobs:c,blockOverrides:l,code:u,data:d,factory:f,factoryData:p,gas:m,gasPrice:h,maxFeePerBlobGas:g,maxFeePerGas:_,maxPriorityFeePerGas:v,nonce:y,to:b,value:x,stateOverride:S,...ee}=t,C=n?aa(n):void 0;if(u&&(f||p))throw new E("Cannot provide both `code` & `factory`/`factoryData` as parameters.");if(u&&b)throw new E("Cannot provide both `code` & `to` as parameters.");let te=u&&d,ne=f&&p&&b&&d,re=te||ne,ie=te?vm({code:u,data:d}):ne?ym({data:d,factory:f,factoryData:p,to:b}):d;try{ed(t);let n=(typeof a==`bigint`?O(a):void 0)||o,u=l?gp(l):void 0,d=Yu(S),f=e.chain?.formatters?.transactionRequest?.format,p=(f||Hu)({...Ru(ee,{format:f}),accessList:s,account:C,authorizationList:r,blobs:c,data:ie,gas:m,gasPrice:h,maxFeePerBlobGas:g,maxFeePerGas:_,maxPriorityFeePerGas:v,nonce:y,to:re?void 0:b,value:x},`call`);if(i&&gm({request:p})&&!d&&!u)try{return await _m(e,{...p,blockNumber:a,blockTag:o})}catch(e){if(!(e instanceof Np)&&!(e instanceof Ap))throw e}let te=(()=>{let e=[p,n];return d&&u?[...e,d,u]:d?[...e,d]:u?[...e,{},u]:e})(),ne=await e.request({method:`eth_call`,params:te});return ne===`0x`?{data:void 0}:{data:ne}}catch(n){let r=bm(n),{offchainLookup:i,offchainLookupSignature:a}=await ms(async()=>{let{offchainLookup:e,offchainLookupSignature:t}=await Promise.resolve().then(()=>(mm(),lm));return{offchainLookup:e,offchainLookupSignature:t}},void 0);if(e.ccipRead!==!1&&r?.slice(0,10)===a&&b)return{data:await i(e,{data:r,to:b})};throw re&&r?.slice(0,10)===`0x101bb98d`?new wo({factory:f}):Vp(n,{...t,account:C,chain:e.chain})}}function gm({request:e}){let{data:t,to:n,...r}=e;return!(!t||t.startsWith(`0x82ad56cb`)||!n||Object.values(r).filter(e=>e!==void 0).length>0)}async function _m(e,t){let{batchSize:n=1024,deployless:r=!1,wait:i=0}=typeof e.batch?.multicall==`object`?e.batch.multicall:{},{blockNumber:a,blockTag:o=e.experimental_blockTag??`latest`,data:s,to:c}=t,l=(()=>{if(r)return null;if(t.multicallAddress)return t.multicallAddress;if(e.chain)return zp({blockNumber:a,chain:e.chain,contract:`multicall3`});throw new Np})(),u=(typeof a==`bigint`?O(a):void 0)||o,{schedule:d}=Gp({id:`${e.uid}.${u}`,wait:i,shouldSplitBatch(e){return e.reduce((e,{data:t})=>e+(t.length-2),0)>n*2},fn:async t=>{let n=t.map(e=>({allowFailure:!0,callData:e.data,target:e.to})),r=ua({abi:vp,args:[n],functionName:`aggregate3`}),i=await e.request({method:`eth_call`,params:[{...l===null?{data:vm({code:Op,data:r})}:{to:l,data:r}},u]});return Wd({abi:vp,args:[n],functionName:`aggregate3`,data:i||`0x`})}}),[{returnData:f,success:p}]=await d({data:s,to:c});if(!p)throw new To({data:f});return f===`0x`?{data:void 0}:{data:f}}function vm(e){let{code:t,data:n}=e;return Ip({abi:Ct([`constructor(bytes, bytes)`]),bytecode:Tp,args:[t,n]})}function ym(e){let{data:t,factory:n,factoryData:r,to:i}=e;return Ip({abi:Ct([`constructor(address, bytes, address, bytes)`]),bytecode:Ep,args:[i,t,n,r]})}function bm(e){if(!(e instanceof E))return;let t=e.walk();return typeof t?.data==`object`?t.data?.data:t.data}var xm=o((()=>{kt(),_p(),oa(),Cp(),wp(),kp(),D(),Fp(),Eo(),Kd(),Rp(),Bp(),k(),Hp(),zu(),Ku(),qp(),Xu(),td(),hs()}));Kd(),da(),xm();async function Sm(e,t){let{abi:n,address:r,args:i,functionName:a,...o}=t,s=ua({abi:n,args:i,functionName:a});try{let{data:t}=await T(e,hm,`call`)({...o,data:s,to:r});return Wd({abi:n,args:i,functionName:a,data:t||`0x`})}catch(e){throw ls(e,{abi:n,address:r,args:i,docsPath:`/docs/contract/readContract`,functionName:a})}}var Cm=new Map,wm=new Map,Tm=0;function Em(e,t,n){let r=++Tm,i=()=>Cm.get(e)||[],a=()=>{let t=i();Cm.set(e,t.filter(e=>e.id!==r))},o=()=>{let t=i();if(!t.some(e=>e.id===r))return;let n=wm.get(e);if(t.length===1&&n){let e=n();e instanceof Promise&&e.catch(()=>{})}a()},s=i();if(Cm.set(e,[...s,{id:r,fns:t}]),s&&s.length>0)return o;let c={};for(let e in t)c[e]=((...t)=>{let n=i();if(n.length!==0)for(let r of n)r.fns[e]?.(...t)});let l=n(c);return typeof l==`function`&&wm.set(e,l),o}async function Dm(e){return new Promise(t=>setTimeout(t,e))}function Om(e,{emitOnBegin:t,initialWaitTime:n,interval:r}){let i=!0,a=()=>i=!1;return(async()=>{let o;t&&(o=await e({unpoll:a})),await Dm(await n?.(o)??r);let s=async()=>{i&&(await e({unpoll:a}),await Dm(r),s())};s()})(),a}var km=new Map,Am=new Map;function jm(e){let t=(e,t)=>({clear:()=>t.delete(e),get:()=>t.get(e),set:n=>t.set(e,n)}),n=t(e,km),r=t(e,Am);return{clear:()=>{n.clear(),r.clear()},promise:n,response:r}}async function Mm(e,{cacheKey:t,cacheTime:n=1/0}){let r=jm(t),i=r.response.get();if(i&&n>0&&Date.now()-i.created.getTime()`blockNumber.${e}`;async function Pm(e,{cacheTime:t=e.cacheTime}={}){let n=await Mm(()=>e.request({method:`eth_blockNumber`}),{cacheKey:Nm(e.uid),cacheTime:t});return BigInt(n)}D();var Fm=class extends E{constructor({docsPath:e}={}){super([`Could not find an Account to execute with this Action.`,"Please provide an Account with the `account` argument on the Action, or by supplying an `account` to the Client."].join(` +`),{docsPath:e,docsSlug:`account`,name:`AccountNotFoundError`})}},Im=class extends E{constructor({docsPath:e,metaMessages:t,type:n}){super(`Account type "${n}" is not supported.`,{docsPath:e,metaMessages:t,name:`AccountTypeNotSupportedError`})}};Fp();function Lm({chain:e,currentChainId:t}){if(!e)throw new Mp;if(t!==e.id)throw new jp({chain:e,currentChainId:t})}async function Rm(e,{serializedTransaction:t}){return e.request({method:`eth_sendRawTransaction`,params:[t]},{retryCount:0})}oa(),D(),Ai(),zu(),Ku(),vi(),td();var zm=new _i(128);async function Bm(e,t){let{account:n=e.account,assertChainId:r=!0,chain:i=e.chain,accessList:a,authorizationList:o,blobs:s,data:c,dataSuffix:l=typeof e.dataSuffix==`string`?e.dataSuffix:e.dataSuffix?.value,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,type:g,value:_,...v}=t;if(n===void 0)throw new Fm({docsPath:`/docs/actions/wallet/sendTransaction`});let y=n?aa(n):null;try{ed(t);let n=await(async()=>{if(t.to)return t.to;if(t.to!==null&&o&&o.length>0)return await bu({authorization:o[0]}).catch(()=>{throw new E("`to` is required. Could not infer from `authorizationList`.")})})();if(y?.type===`json-rpc`||y===null){let t;i!==null&&(t=await T(e,Fd,`getChainId`)({}),r&&Lm({currentChainId:t,chain:i}));let b=e.chain?.formatters?.transactionRequest?.format,x=(b||Hu)({...Ru(v,{format:b}),accessList:a,account:y,authorizationList:o,blobs:s,chainId:t,data:l?Di([c??`0x`,l]):c,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,to:n,type:g,value:_},`sendTransaction`),S=zm.get(e.uid),ee=S?`wallet_sendTransaction`:`eth_sendTransaction`;try{return await e.request({method:ee,params:[x]},{retryCount:0})}catch(t){if(S===!1)throw t;let n=t;if(n.name===`InvalidInputRpcError`||n.name===`InvalidParamsRpcError`||n.name===`MethodNotFoundRpcError`||n.name===`MethodNotSupportedRpcError`)return await e.request({method:`wallet_sendTransaction`,params:[x]},{retryCount:0}).then(t=>(zm.set(e.uid,!0),t)).catch(t=>{let r=t;throw r.name===`MethodNotFoundRpcError`||r.name===`MethodNotSupportedRpcError`?(zm.set(e.uid,!1),n):r});throw n}}if(y?.type===`local`){let t=await T(e,zd,`prepareTransactionRequest`)({account:y,accessList:a,authorizationList:o,blobs:s,chain:i,data:l?Di([c??`0x`,l]):c,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,nonceManager:y.nonceManager,parameters:[...Ld,`sidecars`],type:g,value:_,...v,to:n}),r=i?.serializers?.transaction,b=await y.signTransaction(t,{serializer:r});return await T(e,Rm,`sendRawTransaction`)({serializedTransaction:b})}throw y?.type===`smart`?new Im({metaMessages:["Consider using the `sendUserOperation` Action instead."],docsPath:`/docs/actions/bundler/sendUserOperation`,type:`smart`}):new Im({docsPath:`/docs/actions/wallet/sendTransaction`,type:y?.type})}catch(e){throw e instanceof Im?e:Pd(e,{...t,account:y,chain:t.chain||void 0})}}oa(),da();async function Vm(e,t){return Vm.internal(e,Bm,`sendTransaction`,t)}(function(e){async function t(e,t,n,r){let{abi:i,account:a=e.account,address:o,args:s,functionName:c,...l}=r;if(a===void 0)throw new Fm({docsPath:`/docs/contract/writeContract`});let u=a?aa(a):null,d=ua({abi:i,args:s,functionName:c});try{return await T(e,t,n)({data:d,to:o,account:u,...l})}catch(e){throw ls(e,{abi:i,address:o,args:s,docsPath:`/docs/contract/writeContract`,functionName:c,sender:u?.address})}}e.internal=t})(Vm||={}),D();var Hm=class extends E{constructor(e){super(`Call bundle failed with status: ${e.statusCode}`,{name:`BundleFailedError`}),Object.defineProperty(this,`result`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.result=e}};function Um(e,{delay:t=100,retryCount:n=2,shouldRetry:r=()=>!0}={}){return new Promise((i,a)=>{let o=async({count:s=0}={})=>{let c=async({error:e})=>{let n=typeof t==`function`?t({count:s,error:e}):t;n&&await Dm(n),o({count:s+1})};try{i(await e())}catch(e){if(sUd(e)):null,to:e.to?e.to:null,transactionIndex:e.transactionIndex?Dn(e.transactionIndex):null,status:e.status?Wm[e.status]:null,type:e.type?od[e.type]||e.type:null};return e.blobGasPrice&&(n.blobGasPrice=BigInt(e.blobGasPrice)),e.blobGasUsed&&(n.blobGasUsed=BigInt(e.blobGasUsed)),n}var Km=Bu(`transactionReceipt`,Gm);oa(),D(),ss(),da(),Ai(),On(),k();var qm=`0x5792579257925792579257925792579257925792579257925792579257925792`,Jm=O(0,{size:32});async function Ym(e,t){let{account:n=e.account,chain:r=e.chain,experimental_fallback:i,experimental_fallbackDelay:a=32,forceAtomic:o=!1,id:s,version:c=`2.0.0`}=t,l=n?aa(n):null,u=t.capabilities;e.dataSuffix&&!t.capabilities?.dataSuffix&&(u=typeof e.dataSuffix==`string`?{...t.capabilities,dataSuffix:{value:e.dataSuffix,optional:!0}}:{...t.capabilities,dataSuffix:{value:e.dataSuffix.value,...e.dataSuffix.required?{}:{optional:!0}}});let d=t.calls.map(e=>{let t=e,n=t.abi?ua({abi:t.abi,functionName:t.functionName,args:t.args}):t.data;return{data:t.dataSuffix&&n?Di([n,t.dataSuffix]):n,to:t.to,value:t.value?O(t.value):void 0}});try{let t=await e.request({method:`wallet_sendCalls`,params:[{atomicRequired:o,calls:d,capabilities:u,chainId:O(r.id),from:l?.address,id:s,version:c}]},{retryCount:0});return typeof t==`string`?{id:t}:t}catch(n){let s=n;if(i&&(s.name===`MethodNotFoundRpcError`||s.name===`MethodNotSupportedRpcError`||s.name===`UnknownRpcError`||s.details.toLowerCase().includes(`does not exist / is not available`)||s.details.toLowerCase().includes(`missing or invalid. request()`)||s.details.toLowerCase().includes(`did not match any variant of untagged enum`)||s.details.toLowerCase().includes(`account upgraded to unsupported contract`)||s.details.toLowerCase().includes(`eip-7702 not supported`)||s.details.toLowerCase().includes(`unsupported wc_ method`)||s.details.toLowerCase().includes(`feature toggled misconfigured`)||s.details.toLowerCase().includes(`jsonrpcengine: response has no error or result for request`))){if(u&&Object.values(u).some(e=>!e.optional)){let e="non-optional `capabilities` are not supported on fallback to `eth_sendTransaction`.";throw new Qo(new E(e,{details:e}))}if(o&&d.length>1){let e="`forceAtomic` is not supported on fallback to `eth_sendTransaction`.";throw new is(new E(e,{details:e}))}let t=[];for(let n of d){let i=Bm(e,{account:l,chain:r,data:n.data,to:n.to,value:n.value?Tn(n.value):void 0});t.push(i),a>0&&await new Promise(e=>setTimeout(e,a))}let n=await Promise.allSettled(t);if(n.every(e=>e.status===`rejected`))throw n[0].reason;return{id:Di([...n.map(e=>e.status===`fulfilled`?e.value:Jm),O(r.id,{size:32}),qm])}}throw Pd(n,{...t,account:l,chain:t.chain})}}Ii(),Cn(),On();async function Xm(e,t){async function n(t){if(t.endsWith(`5792579257925792579257925792579257925792579257925792579257925792`)){let n=Sn(Fi(t,-64,-32)),r=Fi(t,0,-64).slice(2).match(/.{1,64}/g),i=await Promise.all(r.map(t=>Jm.slice(2)===t?void 0:e.request({method:`eth_getTransactionReceipt`,params:[`0x${t}`]},{dedupe:!0}))),a=i.some(e=>e===null)?100:i.every(e=>e?.status===`0x1`)?200:i.every(e=>e?.status===`0x0`)?500:600;return{atomic:!1,chainId:Dn(n),receipts:i.filter(Boolean),status:a,version:`2.0.0`}}return e.request({method:`wallet_getCallsStatus`,params:[t]})}let{atomic:r=!1,chainId:i,receipts:a,version:o=`2.0.0`,...s}=await n(t.id),[c,l]=(()=>{let e=s.status;return e>=100&&e<200?[`pending`,e]:e>=200&&e<300?[`success`,e]:e>=300&&e<700?[`failure`,e]:e===`CONFIRMED`?[`success`,200]:e===`PENDING`?[`pending`,100]:[void 0,e]})();return{...s,atomic:r,chainId:i?Dn(i):void 0,receipts:a?.map(e=>({...e,blockNumber:Tn(e.blockNumber),gasUsed:Tn(e.gasUsed),status:Wm[e.status]}))??[],statusCode:l,status:c,version:o}}D(),Wp(),Wa();async function Zm(e,t){let{id:n,pollingInterval:r=e.pollingInterval,status:i=({statusCode:e})=>e===200||e>=300,retryCount:a=4,retryDelay:o=({count:e})=>~~(1<{let s=Om(async()=>{let r=e=>{clearTimeout(p),s(),e(),m()};try{let s=await Um(async()=>{let t=await T(e,Xm,`getCallsStatus`)({id:n});if(c&&t.status===`failure`)throw new Hm(t);return t},{retryCount:a,delay:o});if(!i(s))return;r(()=>t.resolve(s))}catch(e){r(()=>t.reject(e))}},{interval:r,emitOnBegin:!0});return s});return p=s?setTimeout(()=>{m(),clearTimeout(p),f(new Qm({id:n}))},s):void 0,await u}var Qm=class extends E{constructor({id:e}){super(`Timed out while waiting for call bundle with id "${e}" to be confirmed.`,{name:`WaitForCallsStatusTimeoutError`})}},$m=256,eh=$m,th;function nh(e=11){if(!th||eh+e>$m*2){th=``,eh=0;for(let e=0;e<$m;e++)th+=(256+Math.random()*256|0).toString(16).substring(1)}return th.substring(eh,eh+++e)}oa();function rh(e){let{batch:t,chain:n,ccipRead:r,dataSuffix:i,key:a=`base`,name:o=`Base Client`,type:s=`base`}=e,c=e.experimental_blockTag??(typeof n?.experimental_preconfirmationTime==`number`?`pending`:void 0),l=n?.blockTime??12e3,u=Math.min(Math.max(Math.floor(l/2),500),4e3),d=e.pollingInterval??u,f=e.cacheTime??d,p=e.account?aa(e.account):void 0,{config:m,request:h,value:g}=e.transport({account:p,chain:n,pollingInterval:d}),_={account:p,batch:t,cacheTime:f,ccipRead:r,chain:n,dataSuffix:i,key:a,name:o,pollingInterval:d,request:h,transport:{...m,...g},type:s,uid:nh(),...c?{experimental_blockTag:c}:{}};function v(e){return t=>{let n=t(e);for(let e in _)delete n[e];let r={...e,...n};return Object.assign(r,{extend:v(r)})}}return Object.assign(_,{extend:v(_)})}k();async function ih(e,{address:t,blockNumber:n,blockTag:r=`latest`}){let i=n===void 0?void 0:O(n),a=await e.request({method:`eth_getCode`,params:[t,i||r]},{dedupe:!!i});if(a!==`0x`)return a}D();var ah=class extends E{constructor({address:e}){super(`No EIP-712 domain found on contract "${e}".`,{metaMessages:[`Ensure that:`,`- The contract is deployed at the address "${e}".`,"- `eip712Domain()` function exists on the contract.","- `eip712Domain()` function matches signature to ERC-5267 specification."],name:`Eip712DomainNotFoundError`})}};async function oh(e,t){let{address:n,factory:r,factoryData:i}=t;try{let[t,a,o,s,c,l,u]=await T(e,Sm,`readContract`)({abi:sh,address:n,functionName:`eip712Domain`,factory:r,factoryData:i});return{domain:{name:a,version:o,chainId:Number(s),verifyingContract:c,salt:l},extensions:u,fields:t}}catch(e){let t=e;throw t.name===`ContractFunctionExecutionError`&&t.cause.name===`ContractFunctionZeroDataError`?new ah({address:n}):t}}var sh=[{inputs:[],name:`eip712Domain`,outputs:[{name:`fields`,type:`bytes1`},{name:`name`,type:`string`},{name:`version`,type:`string`},{name:`chainId`,type:`uint256`},{name:`verifyingContract`,type:`address`},{name:`salt`,type:`bytes32`},{name:`extensions`,type:`uint256[]`}],stateMutability:`view`,type:`function`}];$u(),gi(),D(),Fp(),Pu(),Ei(),Lt(),Ii(),On();function ch(e){let{authorizationList:t}=e;if(t)for(let e of t){let{chainId:t}=e,n=e.address;if(!Ci(n))throw new hi({address:n});if(t<0)throw new Pp({chainId:t})}uh(e)}function lh(e){let{blobVersionedHashes:t}=e;if(t){if(t.length===0)throw new Od;for(let e of t){let t=It(e),n=Dn(ji(e,0,1));if(t!==32)throw new kd({hash:e,size:t});if(n!==1)throw new Ad({hash:e,version:n})}}uh(e)}function uh(e){let{chainId:t,maxPriorityFeePerGas:n,maxFeePerGas:r,to:i}=e;if(t<=0)throw new Pp({chainId:t});if(i&&!Ci(i))throw new hi({address:i});if(r&&r>Qu)throw new Cu({maxFeePerGas:r});if(n&&r&&n>r)throw new Mu({maxFeePerGas:r,maxPriorityFeePerGas:n})}function dh(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a}=e;if(t<=0)throw new Pp({chainId:t});if(a&&!Ci(a))throw new hi({address:a});if(n||i)throw new E("`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.");if(r&&r>Qu)throw new Cu({maxFeePerGas:r})}function fh(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a}=e;if(a&&!Ci(a))throw new hi({address:a});if(t!==void 0&&t<=0)throw new Pp({chainId:t});if(n||i)throw new E("`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.");if(r&&r>Qu)throw new Cu({maxFeePerGas:r})}gi(),_o(),Ei();function ph(e){if(!e||e.length===0)return[];let t=[];for(let n=0;njn(e)),n=e.kzg,r=_d({blobs:t,kzg:n});f===void 0&&(f=Sd({commitments:r})),p===void 0&&(p=Md({blobs:t,commitments:r,proofs:vd({blobs:t,commitments:r,kzg:n})}))}let m=ph(u),h=[O(n),i?O(i):`0x`,l?O(l):`0x`,c?O(c):`0x`,r?O(r):`0x`,a??`0x`,o?O(o):`0x`,d??`0x`,m,s?O(s):`0x`,f??[],...bh(e,t)],g=[],_=[],v=[];if(p)for(let e=0;e{if(t.v>=35n)return(t.v-35n)/2n>0?t.v:27n+(t.v===35n?0n:1n);if(n>0)return BigInt(n*2)+BigInt(35n+t.v-27n);let e=27n+(t.v===27n?0n:1n);if(t.v!==e)throw new co({v:t.v});return e})(),r=Sn(t.r),i=Sn(t.s);l=[...l,O(e),r===`0x00`?`0x`:r,i===`0x00`?`0x`:i]}else n>0&&(l=[...l,O(n),`0x`,`0x`]);return mu(l)}function bh(e,t){let n=t??e,{v:r,yParity:i}=n;if(n.r===void 0||n.s===void 0||r===void 0&&i===void 0)return[];let a=Sn(n.r),o=Sn(n.s);return[typeof i==`number`?i?O(1):`0x`:r===0n?`0x`:r===1n?O(1):r===27n?`0x`:O(1),a===`0x00`?`0x`:a,o===`0x00`?`0x`:o]}k();function xh(e){if(!e||e.length===0)return[];let t=[];for(let n of e){let{chainId:e,nonce:r,...i}=n,a=n.address;t.push([e?kn(e):`0x`,a,r?kn(r):`0x`,...bh({},i)])}return t}Si(),Hd();async function Sh({address:e,authorization:t,signature:n}){return Vd(bi(e),await bu({authorization:t,signature:n}))}vi();var Ch=new _i(8192);function wh(e,{enabled:t=!0,id:n}){if(!t||!n)return e();if(Ch.get(n))return Ch.get(n);let r=e().finally(()=>Ch.delete(n));return Ch.set(n,r),r}D(),Ao(),ss(),k(),Wa();function Th(e,t={}){return async(n,r={})=>{let{dedupe:i=!1,methods:a,retryDelay:o=150,retryCount:s=3,uid:c}={...t,...r},{method:l}=n;if(a?.exclude?.includes(l)||a?.include&&!a.include.includes(l))throw new Uo(Error(`method not supported`),{method:l});return wh(()=>Um(async()=>{try{return await e(n)}catch(e){let t=e;switch(t.code){case Po.code:throw new Po(t);case Fo.code:throw new Fo(t);case Io.code:throw new Io(t,{method:n.method});case Lo.code:throw new Lo(t);case Ro.code:throw new Ro(t);case zo.code:throw new zo(t);case Bo.code:throw new Bo(t);case Vo.code:throw new Vo(t);case Ho.code:throw new Ho(t);case Uo.code:throw new Uo(t,{method:n.method});case Wo.code:throw new Wo(t);case Go.code:throw new Go(t);case Ko.code:throw new Ko(t);case qo.code:throw new qo(t);case Jo.code:throw new Jo(t);case Yo.code:throw new Yo(t);case Xo.code:throw new Xo(t);case Zo.code:throw new Zo(t);case Qo.code:throw new Qo(t);case $o.code:throw new $o(t);case es.code:throw new es(t);case ts.code:throw new ts(t);case ns.code:throw new ns(t);case rs.code:throw new rs(t);case is.code:throw new is(t);case 5e3:throw new Ko(t);case as.code:throw new as(t);default:throw e instanceof E?e:new os(t)}}},{delay:({count:e,error:t})=>{if(t&&t instanceof Do){let e=t?.headers?.get(`Retry-After`);if(e?.match(/\d/))return Number.parseInt(e,10)*1e3}return~~(1<Eh(e)}),{enabled:i,id:i?Mn(`${c}.${Ua(n)}`):void 0})}}function Eh(e){return`code`in e&&typeof e.code==`number`?e.code===-1||e.code===Wo.code||e.code===Ro.code:e instanceof Do&&e.status?e.status===403||e.status===408||e.status===413||e.status===429||e.status===500||e.status===502||e.status===503||e.status===504:!0}function L(e){let t={formatters:void 0,fees:void 0,serializers:void 0,...e};function n(e){return t=>{let r=typeof t==`function`?t(e):t,i={...e,...r};return Object.assign(i,{extend:n(i)})}}return Object.assign(t,{extend:n(t)})}function Dh(){return{}}function Oh(e,{errorInstance:t=Error(`timed out`),timeout:n,signal:r}){return new Promise((i,a)=>{(async()=>{let o;try{let s=new AbortController;n>0&&(o=setTimeout(()=>{r?s.abort():a(t)},n)),i(await e({signal:s?.signal||null}))}catch(e){e?.name===`AbortError`&&a(t),a(e)}finally{clearTimeout(o)}})()})}function kh(){return{current:0,take(){return this.current++},reset(){this.current=0}}}var Ah=kh();Ao(),Wa();function jh(e,t={}){let{url:n,headers:r}=Mh(e);return{async request(e){let{body:i,fetchFn:a=t.fetchFn??fetch,onRequest:o=t.onRequest,onResponse:s=t.onResponse,timeout:c=t.timeout??1e4}=e,l={...t.fetchOptions??{},...e.fetchOptions??{}},{headers:u,method:d,signal:f}=l;try{let e=await Oh(async({signal:e})=>{let t={...l,body:Ua(Array.isArray(i)?i.map(e=>({jsonrpc:`2.0`,id:e.id??Ah.take(),...e})):{jsonrpc:`2.0`,id:i.id??Ah.take(),...i}),headers:{...r,"Content-Type":`application/json`,...u},method:d||`POST`,signal:f||(c>0?e:null)},s=new Request(n,t),p=await o?.(s,t)??{...t,url:n};return await a(p.url??n,p)},{errorInstance:new ko({body:i,url:n}),timeout:c,signal:!0});s&&await s(e);let t;if(e.headers.get(`Content-Type`)?.startsWith(`application/json`))t=await e.json();else{t=await e.text();try{t=JSON.parse(t||`{}`)}catch(n){if(e.ok)throw n;t={error:t}}}if(!e.ok){if(typeof t.error?.code==`number`&&typeof t.error?.message==`string`)return t;throw new Do({body:i,details:Ua(t.error)||e.statusText,headers:e.headers,status:e.status,url:n})}return t}catch(e){throw e instanceof Do||e instanceof ko?e:new Do({body:i,cause:e,url:n})}}}}function Mh(e){try{let t=new URL(e),n=(()=>{if(t.username){let e=`${decodeURIComponent(t.username)}:${decodeURIComponent(t.password)}`;return t.username=``,t.password=``,{url:t.toString(),headers:{Authorization:`Basic ${btoa(e)}`}}}})();return{url:t.toString(),...n}}catch{return{url:e}}}var Nh=`Ethereum Signed Message: +`;Ai(),Lt(),k();function Ph(e){let t=typeof e==`string`?Mn(e):typeof e.raw==`string`?e.raw:jn(e.raw);return Di([Mn(`${Nh}${It(t)}`),t])}ri();function Fh(e,t){return ni(Ph(e),t)}Wa(),D();var Ih=class extends E{constructor({domain:e}){super(`Invalid domain "${Ua(e)}".`,{metaMessages:[`Must be a valid EIP-712 domain.`]})}},Lh=class extends E{constructor({primaryType:e,types:t}){super(`Invalid primary type \`${e}\` must be one of \`${JSON.stringify(Object.keys(t))}\`.`,{docsPath:`/api/glossary/Errors#typeddatainvalidprimarytypeerror`,metaMessages:["Check that the primary type is a key in `types`."]})}},Rh=class extends E{constructor({type:e}){super(`Struct type "${e}" is invalid.`,{metaMessages:[`Struct type must not be a Solidity type.`],name:`InvalidStructTypeError`})}};cn(),gi(),Ei(),Lt(),k(),zi(),Wa();function zh(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{let n={...t};for(let t of e){let{name:e,type:r}=t;r===`address`&&(n[e]=n[e].toLowerCase())}return n};return Ua({domain:!i.EIP712Domain||!t?{}:a(i.EIP712Domain,t),message:(()=>{if(r!==`EIP712Domain`)return a(i[r],n)})(),primaryType:r,types:i})}function Bh(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{for(let n of e){let{name:e,type:r}=n,o=t[e],s=r.match(Ri);if(s&&(typeof o==`number`||typeof o==`bigint`)){let[e,t,n]=s;O(o,{signed:t===`int`,size:Number.parseInt(n,10)/8})}if(r===`address`&&typeof o==`string`&&!Ci(o))throw new hi({address:o});let c=r.match(Li);if(c){let[e,t]=c;if(t&&It(o)!==Number.parseInt(t,10))throw new nn({expectedSize:Number.parseInt(t,10),givenSize:It(o)})}let l=i[r];l&&(Hh(r),a(l,o))}};if(i.EIP712Domain&&t){if(typeof t!=`object`)throw new Ih({domain:t});a(i.EIP712Domain,t)}if(r!==`EIP712Domain`)if(i[r])a(i[r],n);else throw new Lh({primaryType:r,types:i})}function Vh({domain:e}){return[typeof e?.name==`string`&&{name:`name`,type:`string`},e?.version&&{name:`version`,type:`string`},(typeof e?.chainId==`number`||typeof e?.chainId==`bigint`)&&{name:`chainId`,type:`uint256`},e?.verifyingContract&&{name:`verifyingContract`,type:`address`},e?.salt&&{name:`salt`,type:`bytes32`}].filter(Boolean)}function Hh(e){if(e===`address`||e===`bool`||e===`string`||e.startsWith(`bytes`)||e.startsWith(`uint`)||e.startsWith(`int`))throw new Rh({type:e})}Qi(),Ai(),k(),ri();function Uh(e){let{domain:t={},message:n,primaryType:r}=e,i={EIP712Domain:Vh({domain:t}),...e.types};Bh({domain:t,message:n,primaryType:r,types:i});let a=[`0x1901`];return t&&a.push(Wh({domain:t,types:i})),r!==`EIP712Domain`&&a.push(Gh({data:n,primaryType:r,types:i})),ni(Di(a))}function Wh({domain:e,types:t}){return Gh({data:e,primaryType:`EIP712Domain`,types:t})}function Gh({data:e,primaryType:t,types:n}){return ni(Kh({data:e,primaryType:t,types:n}))}function Kh({data:e,primaryType:t,types:n}){let r=[{type:`bytes32`}],i=[qh({primaryType:t,types:n})];for(let a of n[t]){let[t,o]=Xh({types:n,name:a.name,type:a.type,value:e[a.name]});r.push(t),i.push(o)}return Bi(r,i)}function qh({primaryType:e,types:t}){return ni(kn(Jh({primaryType:e,types:t})))}function Jh({primaryType:e,types:t}){let n=``,r=Yh({primaryType:e,types:t});r.delete(e);let i=[e,...Array.from(r).sort()];for(let e of i)n+=`${e}(${t[e].map(({name:e,type:t})=>`${t} ${e}`).join(`,`)})`;return n}function Yh({primaryType:e,types:t},n=new Set){let r=e.match(/^\w*/u)?.[0];if(n.has(r)||t[r]===void 0)return n;n.add(r);for(let e of t[r])Yh({primaryType:e.type,types:t},n);return n}function Xh({types:e,name:t,type:n,value:r}){if(e[n]!==void 0)return[{type:`bytes32`},ni(Kh({data:r,primaryType:n,types:e}))];if(n===`bytes`)return[{type:`bytes32`},ni(r)];if(n===`string`)return[{type:`bytes32`},ni(kn(r))];if(n.lastIndexOf(`]`)===n.length-1){let i=n.slice(0,n.lastIndexOf(`[`)),a=r.map(n=>Xh({name:t,type:i,types:e,value:n}));return[{type:`bytes32`},ni(Bi(a.map(([e])=>e),a.map(([,e])=>e)))]}return[{type:n},r]}var Zh={checksum:new class extends Map{constructor(e){super(),Object.defineProperty(this,`maxSize`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&t!==void 0&&(this.delete(e),super.set(e,t)),t}set(e,t){if(super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=this.keys().next().value;e&&this.delete(e)}return this}}(8192)}.checksum;ti(),Hf(),pp();function Qh(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=$r(yf(e));return n===`Bytes`?r:qf(r)}function $h(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=yd(yf(e));return n===`Bytes`?r:qf(r)}function eg(e){return rp(e)&&Qf(e)===32}Hf(),Qd(),pp(),gf();function tg(e,t={}){let{compressed:n}=t,{prefix:r,x:i,y:a}=e;if(n===!1||typeof i==`bigint`&&typeof a==`bigint`){if(r!==4)throw new cg({prefix:r,cause:new ug});return}if(n===!0||typeof i==`bigint`&&a===void 0){if(r!==3&&r!==2)throw new cg({prefix:r,cause:new lg});return}throw new sg({publicKey:e})}function ng(e){let t=(()=>{if(rp(e))return ig(e);if(Pf(e))return rg(e);let{prefix:t,x:n,y:r}=e;return typeof n==`bigint`&&typeof r==`bigint`?{prefix:t??4,x:n,y:r}:{prefix:t,x:n}})();return tg(t),t}function rg(e){return ig(qf(e))}function ig(e){if(e.length!==132&&e.length!==130&&e.length!==68)throw new dg({publicKey:e});return e.length===130?{prefix:4,x:BigInt(I(e,0,32)),y:BigInt(I(e,32,64))}:e.length===132?{prefix:Number(I(e,0,1)),x:BigInt(I(e,1,33)),y:BigInt(I(e,33,65))}:{prefix:Number(I(e,0,1)),x:BigInt(I(e,1,33))}}function ag(e,t={}){return xf(og(e,t))}function og(e,t={}){tg(e);let{prefix:n,x:r,y:i}=e,{includePrefix:a=!0}=t;return Wf(a?F(n,{size:1}):`0x`,F(r,{size:32}),typeof i==`bigint`?F(i,{size:32}):`0x`)}var sg=class extends P{constructor({publicKey:e}){super(`Value \`${mf(e)}\` is not a valid public key.`,{metaMessages:[`Public key must contain:`,"- an `x` and `prefix` value (compressed)","- an `x`, `y`, and `prefix` value (uncompressed)"]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidError`})}},cg=class extends P{constructor({prefix:e,cause:t}){super(`Prefix "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidPrefixError`})}},lg=class extends P{constructor(){super(`Prefix must be 2 or 3 for compressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidCompressedPrefixError`})}},ug=class extends P{constructor(){super(`Prefix must be 4 for uncompressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidUncompressedPrefixError`})}},dg=class extends P{constructor({publicKey:e}){super(`Value \`${e}\` is an invalid public key size.`,{metaMessages:[`Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).`,`Received ${Qf(Gf(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidSerializedSizeError`})}};Hf(),Qd();var fg=/^0x[a-fA-F0-9]{40}$/;function pg(e,t={}){let{strict:n=!0}=t;if(!fg.test(e))throw new yg({address:e,cause:new bg});if(n){if(e.toLowerCase()===e)return;if(mg(e)!==e)throw new yg({address:e,cause:new xg})}}function mg(e){if(Zh.has(e))return Zh.get(e);pg(e,{strict:!1});let t=e.substring(2).toLowerCase(),n=Qh(Sf(t),{as:`Bytes`}),r=t.split(``);for(let e=0;e<40;e+=2)n[e>>1]>>4>=8&&r[e]&&(r[e]=r[e].toUpperCase()),(n[e>>1]&15)>=8&&r[e+1]&&(r[e+1]=r[e+1].toUpperCase());let i=`0x${r.join(``)}`;return Zh.set(e,i),i}function hg(e,t={}){let{checksum:n=!1}=t;return pg(e),n?mg(e):e}function gg(e,t={}){return hg(`0x${Qh(`0x${og(e).slice(4)}`).substring(26)}`,t)}function _g(e,t){return pg(e,{strict:!1}),pg(t,{strict:!1}),e.toLowerCase()===t.toLowerCase()}function vg(e,t={}){let{strict:n=!0}=t??{};try{return pg(e,{strict:n}),!0}catch{return!1}}var yg=class extends P{constructor({address:e,cause:t}){super(`Address "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidAddressError`})}},bg=class extends P{constructor(){super(`Address is not a 20 byte (40 hexadecimal character) value.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidInputError`})}},xg=class extends P{constructor(){super(`Address does not match its checksum counterpart.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidChecksumError`})}},Sg=/^(.*)\[([0-9]*)\]$/,Cg=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,wg=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n));var Tg=2n**256n-1n;Hf(),Qd(),pp();function Eg(e,t,n){let{checksumAddress:r,staticPosition:i}=n,a=Kg(t.type);if(a){let[n,o]=a;return Ag(e,{...t,type:o},{checksumAddress:r,length:n,staticPosition:i})}if(t.type===`tuple`)return Pg(e,t,{checksumAddress:r,staticPosition:i});if(t.type===`address`)return kg(e,{checksum:r});if(t.type===`bool`)return jg(e);if(t.type.startsWith(`bytes`))return Mg(e,t,{staticPosition:i});if(t.type.startsWith(`uint`)||t.type.startsWith(`int`))return Ng(e,t);if(t.type===`string`)return Fg(e,{staticPosition:i});throw new l_(t.type)}var Dg=32,Og=32;function kg(e,t={}){let{checksum:n=!1}=t;return[(e=>n?mg(e):e)(qf(Df(e.readBytes(32),-20))),32]}function Ag(e,t,n){let{checksumAddress:r,length:i,staticPosition:a}=n;if(!i){let n=a+Af(e.readBytes(Og)),i=n+Dg;e.setPosition(n);let o=Af(e.readBytes(Dg)),s=qg(t),c=0,l=[];for(let n=0;n48?Of(i,{signed:n}):Af(i,{signed:n}),32]}function Pg(e,t,n){let{checksumAddress:r,staticPosition:i}=n,a=t.components.length===0||t.components.some(({name:e})=>!e),o=a?[]:{},s=0;if(qg(t)){let n=i+Af(e.readBytes(Og));for(let i=0;i0?Wf(t,e):t}}if(o)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:Wf(...s.map(({encoded:e})=>e))}}function Vg(e,{type:t}){let[,n]=t.split(`bytes`),r=Qf(e);if(!n){let t=e;return r%32!=0&&(t=Xf(t,Math.ceil((e.length-2)/2/32)*32)),{dynamic:!0,encoded:Wf(Yf(F(r,{size:32})),t)}}if(r!==Number.parseInt(n,10))throw new o_({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:Xf(e)}}function Hg(e){if(typeof e!=`boolean`)throw new P(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:Yf(Kf(e))}}function Ug(e,{signed:t,size:n}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function Kg(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}function qg(e){let{type:t}=e;if(t===`string`||t===`bytes`||t.endsWith(`[]`))return!0;if(t===`tuple`)return e.components?.some(qg);let n=Kg(e.type);return!!(n&&qg({...e,type:n[1]}))}Qd();var Jg={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new Qg({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new Zg({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new Xg({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new Xg({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}};function Yg(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(Jg);return n.bytes=e,n.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var Xg=class extends P{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.NegativeOffsetError`})}},Zg=class extends P{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.PositionOutOfBoundsError`})}},Qg=class extends P{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.RecursiveReadLimitExceededError`})}};kt(),Hf(),Qd(),pp();function $g(e,t,n={}){let{as:r=`Array`,checksumAddress:i=!1}=n,a=typeof t==`string`?xf(t):t,o=Yg(a);if(Ef(a)===0&&e.length>0)throw new i_;if(Ef(a)&&Ef(a)<32)throw new r_({data:typeof t==`string`?t:qf(t),parameters:e,size:Ef(a)});let s=0,c=r===`Array`?[]:{};for(let t=0;t{if(typeof e==`string`){if(e.length>3&&e.length%2!=0)throw new lp(e);return xf(e)}return e})(),{recursiveReadLimit:1/0}),n)}function f_(e,t=`Hex`){if(e.bytes.length===0)return t===`Hex`?qf(e.bytes):e.bytes;let n=e.readByte();if(n<128&&e.decrementPosition(1),n<192){let r=p_(e,n,128),i=e.readBytes(r);return t===`Hex`?qf(i):i}return m_(e,p_(e,n,192),t)}function p_(e,t,n){if(n===128&&t<128)return 1;if(t<=n+55)return t-n;if(t===n+55+1)return e.readUint8();if(t===n+55+2)return e.readUint16();if(t===n+55+3)return e.readUint24();if(t===n+55+4)return e.readUint32();throw new P(`Invalid RLP prefix`)}function m_(e,t,n){let r=e.position,i=[];for(;e.position-r__(e))):y_(e)}function v_(e){let t=e.reduce((e,t)=>e+t.length,0),n=b_(t);return{length:t<=55?1+t:1+n+t,encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function y_(e){let t=typeof e==`string`?xf(e):e,n=b_(t.length);return{length:t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length,encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function b_(e){if(e<=255)return 1;if(e<=65535)return 2;if(e<=16777215)return 3;if(e<=4294967295)return 4;throw new P(`Length is too large.`)}Qd(),pp(),gf();function x_(e,t={}){let{recovered:n}=t;if(e.r===void 0||e.s===void 0||n&&e.yParity===void 0)throw new F_({signature:e});if(e.r<0n||e.r>Tg)throw new I_({value:e.r});if(e.s<0n||e.s>Tg)throw new L_({value:e.s});if(typeof e.yParity==`number`&&e.yParity!==0&&e.yParity!==1)throw new R_({value:e.yParity})}function S_(e){return C_(qf(e))}function C_(e){if(e.length!==130&&e.length!==132)throw new P_({signature:e});let t=BigInt(I(e,0,32)),n=BigInt(I(e,32,64)),r=(()=>{let t=Number(`0x${e.slice(130)}`);if(!Number.isNaN(t))try{return M_(t)}catch{throw new R_({value:t})}})();return r===void 0?{r:t,s:n}:{r:t,s:n,yParity:r}}function w_(e){if(e.r!==void 0&&e.s!==void 0)return T_(e)}function T_(e){let t=typeof e==`string`?C_(e):e instanceof Uint8Array?S_(e):typeof e.r==`string`?D_(e):e.v?E_(e):{r:e.r,s:e.s,...e.yParity===void 0?{}:{yParity:e.yParity}};return x_(t),t}function E_(e){return{r:e.r,s:e.s,yParity:M_(e.v)}}function D_(e){let t=(()=>{let t=e.v?Number(e.v):void 0,n=e.yParity?Number(e.yParity):void 0;if(typeof t==`number`&&typeof n!=`number`&&(n=M_(t)),typeof n!=`number`)throw new R_({value:e.yParity});return n})();return{r:BigInt(e.r),s:BigInt(e.s),yParity:t}}function O_(e){let[t,n,r]=e;return T_({r:n===`0x`?0n:BigInt(n),s:r===`0x`?0n:BigInt(r),yParity:t===`0x`?0:Number(t)})}function k_(e){x_(e);let t=e.r,n=e.s;return Wf(F(t,{size:32}),F(n,{size:32}),typeof e.yParity==`number`?F(N_(e.yParity),{size:1}):`0x`)}function A_(e){let{r:t,s:n,yParity:r}=e;return{r:F(t,{size:32}),s:F(n,{size:32}),yParity:r===0?`0x0`:`0x1`}}function j_(e){let{r:t,s:n,yParity:r}=e;return[r?`0x01`:`0x`,t===0n?`0x`:$f(F(t)),n===0n?`0x`:$f(F(n))]}function M_(e){if(e===0||e===27)return 0;if(e===1||e===28)return 1;if(e>=35)return e%2==0?1:0;throw new z_({value:e})}function N_(e){if(e===0)return 27;if(e===1)return 28;throw new R_({value:e})}var P_=class extends P{constructor({signature:e}){super(`Value \`${e}\` is an invalid signature size.`,{metaMessages:[`Expected: 64 bytes or 65 bytes.`,`Received ${Qf(Gf(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSerializedSizeError`})}},F_=class extends P{constructor({signature:e}){super(`Signature \`${mf(e)}\` is missing either an \`r\`, \`s\`, or \`yParity\` property.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.MissingPropertiesError`})}},I_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid r value. r must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidRError`})}},L_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid s value. s must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSError`})}},R_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid y-parity value. Y-parity must be 0 or 1.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidYParityError`})}},z_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid v value. v must be 27, 28 or >=35.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidVError`})}};pp();function B_(e,t={}){return typeof e.chainId==`string`?V_(e):{...e,...t.signature}}function V_(e){let{address:t,chainId:n,nonce:r}=e,i=w_(e);return{address:t,chainId:Number(n),nonce:BigInt(r),...i}}function H_(e){return e.map(V_)}function U_(e){let{address:t,chainId:n,nonce:r,...i}=e;return{address:t,chainId:F(n),nonce:F(r),...A_(i)}}function W_(e){return e.map(U_)}uu(),Hf(),pp();function G_(e){return gg(K_(e))}function K_(e){let{payload:t,signature:n}=e,{r,s:i,yParity:a}=n;return ng(new Zl.Signature(BigInt(r),BigInt(i)).addRecoveryBit(a).recoverPublicKey(Gf(t).substring(2)))}function q_(e){let{address:t,hash:n,payload:r,publicKey:i,signature:a}=e;return t?_g(t,G_({payload:r,signature:a})):Zl.verify(a,yf(r),ag(i),...n?[{prehash:!0,lowS:!0}]:[])}Qd(),pp();var J_=n_(`(uint256 chainId, address delegation, uint256 nonce, uint8 yParity, uint256 r, uint256 s), address to, bytes data`);function Y_(e){if(typeof e==`string`){if(I(e,-32)!==`0x8010801080108010801080108010801080108010801080108010801080108010`)throw new Q_(e)}else x_(e.authorization)}function X_(e){Y_(e);let t=tp(I(e,-64,-32)),n=I(e,-t-64,-64),r=I(e,0,-t-64),[i,a,o]=$g(J_,n);return{authorization:B_({address:i.delegation,chainId:Number(i.chainId),nonce:i.nonce,yParity:i.yParity,r:i.r,s:i.s}),signature:r,...o&&o!==`0x`?{data:o,to:a}:{}}}function Z_(e){try{return Y_(e),!0}catch{return!1}}var Q_=class extends P{constructor(e){super(`Value \`${e}\` is an invalid ERC-8010 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc8010.InvalidWrappedSignatureError`})}};_o(),k();async function $_(e,{blockHash:t,blockNumber:n,blockTag:r,hash:i,index:a,sender:o,nonce:s}){let c=r||`latest`,l=n===void 0?void 0:O(n),u=null;if(i?u=await e.request({method:`eth_getTransactionByHash`,params:[i]},{dedupe:!0}):t?u=await e.request({method:`eth_getTransactionByBlockHashAndIndex`,params:[t,O(a)]},{dedupe:!0}):(l||c)&&typeof a==`number`?u=await e.request({method:`eth_getTransactionByBlockNumberAndIndex`,params:[l||c,O(a)]},{dedupe:!!l}):o&&typeof s==`number`&&(u=await e.request({method:`eth_getTransactionBySenderAndNonce`,params:[o,O(s)]},{dedupe:!0})),!u)throw new po({blockHash:t,blockNumber:n,blockTag:c,hash:i,index:a});return(e.chain?.formatters?.transaction?.format||sd)(u,`getTransaction`)}_o();async function ev(e,{hash:t}){let n=await e.request({method:`eth_getTransactionReceipt`,params:[t]},{dedupe:!0});if(!n)throw new mo({hash:t});return(e.chain?.formatters?.transactionReceipt?.format||Gm)(n,`getTransactionReceipt`)}Qd();function tv(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;onv(Object.values(e)[n],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>nv(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function rv(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return rv(i.components,a.components,n[r]);let o=[i.type,a.type];if(o.includes(`address`)&&o.includes(`bytes20`)||(o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`))&&vg(n[r],{strict:!1}))return o}}kt(),Qd(),pp();function iv(e,t={}){let{prepare:n=!0}=t,r=Array.isArray(e)||typeof e==`string`?Tt(e):e;return{...r,...n?{hash:cv(r)}:{}}}function av(e,t,n){let{args:r=[],prepare:i=!0}=n??{},a=rp(t,{strict:!1}),o=e.filter(e=>a?e.type===`function`||e.type===`error`?ov(e)===I(t,0,4):e.type===`event`?cv(e)===t:!1:`name`in e&&e.name===t);if(o.length===0)throw new uv({name:t});if(o.length===1)return{...o[0],...i?{hash:cv(o[0])}:{}};let s;for(let e of o)if(`inputs`in e){if(!r||r.length===0){if(!e.inputs||e.inputs.length===0)return{...e,...i?{hash:cv(e)}:{}};continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===r.length&&r.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?nv(t,r):!1})){if(s&&`inputs`in s&&s.inputs){let t=rv(e.inputs,s.inputs,r);if(t)throw new lv({abiItem:e,type:t[0]},{abiItem:s,type:t[1]})}s=e}}let c=(()=>{if(s)return s;let[e,...t]=o;return{...e,overloads:t}})();if(!c)throw new uv({name:t});return{...c,...i?{hash:cv(c)}:{}}}function ov(...e){return I(cv((()=>{if(Array.isArray(e[0])){let[t,n]=e;return av(t,n)}return e[0]})()),0,4)}function sv(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return av(t,n)}return e[0]})();return tv(typeof t==`string`?t:ue(t))}function cv(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return av(t,n)}return e[0]})();return typeof t!=`string`&&`hash`in t&&t.hash?t.hash:Qh(Jf(sv(t)))}var lv=class extends P{constructor(e,t){super(`Found ambiguous types in overloaded ABI Items.`,{metaMessages:[`\`${e.type}\` in \`${tv(ue(e.abiItem))}\`, and`,`\`${t.type}\` in \`${tv(ue(t.abiItem))}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.AmbiguityError`})}},uv=class extends P{constructor({name:e,data:t,type:n=`item`}){let r=e?` with name "${e}"`:t?` with data "${t}"`:``;super(`ABI ${n}${r} not found.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.NotFoundError`})}},dv=`0x0000000000000000000000000000000000000000`;Qd(),pp();var fv=`0x6492649264926492649264926492649264926492649264926492649264926492`;function pv(e){if(I(e,-32)!==`0x6492649264926492649264926492649264926492649264926492649264926492`)throw new gv(e)}function mv(e){let{data:t,signature:n,to:r}=e;return Wf(e_(n_(`address, bytes, bytes`),[r,t,n]),fv)}function hv(e){try{return pv(e),!0}catch{return!1}}var gv=class extends P{constructor(e){super(`Value \`${e}\` is an invalid ERC-6492 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc6492.InvalidWrappedSignatureError`})}};uu(),On(),Un();function _v({r:e,s:t,to:n=`hex`,v:r,yParity:i}){let a=(()=>{if(i===0||i===1)return i;if(r&&(r===27n||r===28n||r>=35n))return r%2n==0n?1:0;throw Error("Invalid `v` or `yParity` value")})(),o=`0x${new Zl.Signature(Tn(e),Tn(t)).toCompactHex()}${a===0?`1b`:`1c`}`;return n===`hex`?o:Rn(o)}Cp(),kp(),Eo(),Rp(),da(),Si(),Hd(),Ai(),Ft(),On(),k(),xm();async function vv(e,t){let{address:n,chain:r=e.chain,hash:i,erc6492VerifierAddress:a=t.universalSignatureVerifierAddress??r?.contracts?.erc6492Verifier?.address,multicallAddress:o=t.multicallAddress??r?.contracts?.multicall3?.address,mode:s=`auto`}=t;if(r?.verifyHash)return await r.verifyHash(e,t);let c=(()=>{let e=t.signature;return Pt(e)?e:typeof e==`object`&&`r`in e&&`s`in e?_v(e):jn(e)})();try{if(s===`eoa`)try{if(Vd(bi(n),await pu({hash:i,signature:c})))return!0}catch{}return Z_(c)?await yv(e,{...t,multicallAddress:o,signature:c}):await bv(e,{...t,verifierAddress:a,signature:c})}catch(e){if(s!==`eoa`)try{if(Vd(bi(n),await pu({hash:i,signature:c})))return!0}catch{}if(e instanceof Sv)return!1;throw e}}async function yv(e,t){let{address:n,blockNumber:r,blockTag:i,hash:a,multicallAddress:o}=t,{authorization:s,data:c,signature:l,to:u}=X_(t.signature);if(await ih(e,{address:n,blockNumber:r,blockTag:i})===ki([`0xef0100`,s.address]))return await xv(e,{address:n,blockNumber:r,blockTag:i,hash:a,signature:l});let d={address:s.address,chainId:Number(s.chainId),nonce:Number(s.nonce),r:O(s.r,{size:32}),s:O(s.s,{size:32}),yParity:s.yParity};if(!await Sh({address:n,authorization:d}))throw new Sv;let f=await T(e,Sm,`readContract`)({...o?{address:o}:{code:Op},authorizationList:[d],abi:vp,blockNumber:r,blockTag:`pending`,functionName:`aggregate3`,args:[[...c?[{allowFailure:!0,target:u??n,callData:c}]:[],{allowFailure:!0,target:n,callData:ua({abi:xp,functionName:`isValidSignature`,args:[a,l]})}]]});if((f[f.length-1]?.returnData)?.startsWith(`0x1626ba7e`))return!0;throw new Sv}async function bv(e,t){let{address:n,factory:r,factoryData:i,hash:a,signature:o,verifierAddress:s,...c}=t,l=await(async()=>!r&&!i||hv(o)?o:mv({data:i,signature:o,to:r}))(),u=s?{to:s,data:ua({abi:Sp,functionName:`isValidSig`,args:[n,a,l]}),...c}:{data:Ip({abi:Sp,args:[n,a,l],bytecode:Dp}),...c},{data:d}=await T(e,hm,`call`)(u).catch(e=>{throw e instanceof xo?new Sv:e});if(En(d??`0x0`))return!0;throw new Sv}async function xv(e,t){let{address:n,blockNumber:r,blockTag:i,hash:a,signature:o}=t;if((await T(e,Sm,`readContract`)({address:n,abi:xp,args:[a,o],blockNumber:r,blockTag:i,functionName:`isValidSignature`}).catch(e=>{throw e instanceof So?new Sv:e})).startsWith(`0x1626ba7e`))return!0;throw new Sv}var Sv=class extends Error{};On(),Wa();function Cv(e,{emitOnBegin:t=!1,emitMissed:n=!1,onBlockNumber:r,onError:i,poll:a,pollingInterval:o=e.pollingInterval}){let s=a===void 0?!(e.transport.type===`webSocket`||e.transport.type===`ipc`||e.transport.type===`fallback`&&(e.transport.transports[0].config.type===`webSocket`||e.transport.transports[0].config.type===`ipc`)):a,c;return s?Em(Ua([`watchBlockNumber`,e.uid,t,n,o]),{onBlockNumber:r,onError:i},r=>Om(async()=>{try{let t=await T(e,Pm,`getBlockNumber`)({cacheTime:0});if(c!==void 0){if(t===c)return;if(t-c>1&&n)for(let e=c+1n;ec)&&(r.onBlockNumber(t,c),c=t)}catch(e){r.onError?.(e)}},{emitOnBegin:t,interval:o})):Em(Ua([`watchBlockNumber`,e.uid,t,n]),{onBlockNumber:r,onError:i},t=>{let n=!0,r=()=>n=!1;return(async()=>{try{let{unsubscribe:i}=await(()=>{if(e.transport.type===`fallback`){let t=e.transport.transports.find(e=>e.config.type===`webSocket`||e.config.type===`ipc`);return t?t.value:e.transport}return e.transport})().subscribe({params:[`newHeads`],onData(e){if(!n)return;let r=Tn(e.result?.number);t.onBlockNumber(r,c),c=r},onError(e){t.onError?.(e)}});r=i,n||r()}catch(e){i?.(e)}})(),()=>r()})}_o(),Wp(),Wa();async function wv(e,t){let{checkReplacement:n=!0,confirmations:r=1,hash:i,onReplaced:a,retryCount:o=6,retryDelay:s=({count:e})=>~~(1<{g?.(),h?.(),y(new go({hash:i}))},c):void 0;return h=Em(l,{onReplaced:a,resolve:v,reject:y},async t=>{if(p=await T(e,ev,`getTransactionReceipt`)({hash:i}).catch(()=>void 0),p&&r<=1){clearTimeout(b),t.resolve(p),h?.();return}g=T(e,Cv,`watchBlockNumber`)({emitMissed:!0,emitOnBegin:!0,poll:!0,pollingInterval:u,async onBlockNumber(a){let c=e=>{clearTimeout(b),g?.(),e(),h?.()},l=a;if(!m)try{if(p){if(r>1&&(!p.blockNumber||l-p.blockNumber+1nt.resolve(p));return}if(n&&!d&&(m=!0,await Um(async()=>{d=await T(e,$_,`getTransaction`)({hash:i}),d.blockNumber&&(l=d.blockNumber)},{delay:s,retryCount:o}),m=!1),p=await T(e,ev,`getTransactionReceipt`)({hash:i}),r>1&&(!p.blockNumber||l-p.blockNumber+1nt.resolve(p))}catch(n){if(n instanceof po||n instanceof mo){if(!d){m=!1;return}try{f=d,m=!0;let n=await Um(()=>T(e,fd,`getBlock`)({blockNumber:l,includeTransactions:!0}),{delay:s,retryCount:o,shouldRetry:({error:e})=>e instanceof ad});m=!1;let i=n.transactions.find(({from:e,nonce:t})=>e===f.from&&t===f.nonce);if(!i||(p=await T(e,ev,`getTransactionReceipt`)({hash:i.hash}),r>1&&(!p.blockNumber||l-p.blockNumber+1n{t.onReplaced?.({reason:a,replacedTransaction:f,transaction:i,transactionReceipt:p}),t.resolve(p)})}catch(e){c(()=>t.reject(e))}}else c(()=>t.reject(n))}}})}),_}_o();async function Tv(e,{serializedTransaction:t,throwOnReceiptRevert:n,timeout:r}){let i=await e.request({method:`eth_sendRawTransactionSync`,params:r?[t,r]:[t]},{retryCount:0}),a=(e.chain?.formatters?.transactionReceipt?.format||Gm)(i);if(a.status===`reverted`&&n)throw new ho({receipt:a});return a}k();async function Ev(e,{chain:t}){let{id:n,name:r,nativeCurrency:i,rpcUrls:a,blockExplorers:o}=t;await e.request({method:`wallet_addEthereumChain`,params:[{chainId:O(n),chainName:r,nativeCurrency:i,rpcUrls:a.default.http,blockExplorerUrls:o?Object.values(o).map(({url:e})=>e):void 0}]},{dedupe:!0,retryCount:0})}Rp();function Dv(e,t){let{abi:n,args:r,bytecode:i,...a}=t,o=Ip({abi:n,args:r,bytecode:i});return Bm(e,{...a,...a.authorizationList?{to:null}:{},data:o})}Si();async function Ov(e){return e.account?.type===`local`?[e.account.address]:(await e.request({method:`eth_accounts`},{dedupe:!0})).map(e=>yi(e))}oa(),k();async function kv(e,t={}){let{account:n=e.account,chainId:r}=t,i=n?aa(n):void 0,a=r?[i?.address,[O(r)]]:[i?.address],o=await e.request({method:`wallet_getCapabilities`,params:a}),s={};for(let[e,t]of Object.entries(o)){s[Number(e)]={};for(let[n,r]of Object.entries(t))n===`addSubAccount`&&(n=`unstable_addSubAccount`),s[Number(e)][n]=r}return typeof r==`number`?s[r]:s}async function Av(e){return await e.request({method:`wallet_getPermissions`},{dedupe:!0})}oa(),Hd();async function jv(e,t){let{account:n=e.account,chainId:r,nonce:i}=t;if(!n)throw new Fm({docsPath:`/docs/eip7702/prepareAuthorization`});let a=aa(n),o=(()=>{if(t.executor)return t.executor===`self`?t.executor:aa(t.executor)})(),s={address:t.contractAddress??t.address,chainId:r,nonce:i};return s.chainId===void 0&&(s.chainId=e.chain?.id??await T(e,Fd,`getChainId`)({})),s.nonce===void 0&&(s.nonce=await T(e,gd,`getTransactionCount`)({address:a.address,blockTag:`pending`}),(o===`self`||o?.address&&Vd(o.address,a.address))&&(s.nonce+=1)),s}Si();async function Mv(e){return(await e.request({method:`eth_requestAccounts`},{dedupe:!0,retryCount:0})).map(e=>bi(e))}async function Nv(e,t){return e.request({method:`wallet_requestPermissions`,params:[t]},{retryCount:0})}async function Pv(e,t){let{chain:n=e.chain}=t,r=t.timeout??Math.max((n?.blockTime??0)*3,5e3),i=await T(e,Ym,`sendCalls`)(t);return await T(e,Zm,`waitForCallsStatus`)({...t,id:i.id,timeout:r})}oa(),D(),_o(),Ai(),zu(),Ku(),vi(),td();var Fv=new _i(128);async function Iv(e,t){let{account:n=e.account,assertChainId:r=!0,chain:i=e.chain,accessList:a,authorizationList:o,blobs:s,data:c,dataSuffix:l=typeof e.dataSuffix==`string`?e.dataSuffix:e.dataSuffix?.value,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,pollingInterval:g,throwOnReceiptRevert:_,type:v,value:y,...b}=t,x=t.timeout??Math.max((i?.blockTime??0)*3,5e3);if(n===void 0)throw new Fm({docsPath:`/docs/actions/wallet/sendTransactionSync`});let S=n?aa(n):null;try{ed(t);let n=await(async()=>{if(t.to)return t.to;if(t.to!==null&&o&&o.length>0)return await bu({authorization:o[0]}).catch(()=>{throw new E("`to` is required. Could not infer from `authorizationList`.")})})();if(S?.type===`json-rpc`||S===null){let t;i!==null&&(t=await T(e,Fd,`getChainId`)({}),r&&Lm({currentChainId:t,chain:i}));let ee=e.chain?.formatters?.transactionRequest?.format,C=(ee||Hu)({...Ru(b,{format:ee}),accessList:a,account:S,authorizationList:o,blobs:s,chainId:t,data:c&&Di([c,l??`0x`]),gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,to:n,type:v,value:y},`sendTransaction`),te=Fv.get(e.uid),ne=te?`wallet_sendTransaction`:`eth_sendTransaction`,re=await(async()=>{try{return await e.request({method:ne,params:[C]},{retryCount:0})}catch(t){if(te===!1)throw t;let n=t;if(n.name===`InvalidInputRpcError`||n.name===`InvalidParamsRpcError`||n.name===`MethodNotFoundRpcError`||n.name===`MethodNotSupportedRpcError`)return await e.request({method:`wallet_sendTransaction`,params:[C]},{retryCount:0}).then(t=>(Fv.set(e.uid,!0),t)).catch(t=>{let r=t;throw r.name===`MethodNotFoundRpcError`||r.name===`MethodNotSupportedRpcError`?(Fv.set(e.uid,!1),n):r});throw n}})(),ie=await T(e,wv,`waitForTransactionReceipt`)({checkReplacement:!1,hash:re,pollingInterval:g,timeout:x});if(_&&ie.status===`reverted`)throw new ho({receipt:ie});return ie}if(S?.type===`local`){let r=await T(e,zd,`prepareTransactionRequest`)({account:S,accessList:a,authorizationList:o,blobs:s,chain:i,data:c&&Di([c,l??`0x`]),gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,nonceManager:S.nonceManager,parameters:[...Ld,`sidecars`],type:v,value:y,...b,to:n}),g=i?.serializers?.transaction,x=await S.signTransaction(r,{serializer:g});return await T(e,Tv,`sendRawTransactionSync`)({serializedTransaction:x,throwOnReceiptRevert:_,timeout:t.timeout})}throw S?.type===`smart`?new Im({metaMessages:["Consider using the `sendUserOperation` Action instead."],docsPath:`/docs/actions/bundler/sendUserOperation`,type:`smart`}):new Im({docsPath:`/docs/actions/wallet/sendTransactionSync`,type:S?.type})}catch(e){throw e instanceof Im?e:Pd(e,{...t,account:S,chain:t.chain||void 0})}}async function Lv(e,t){let{id:n}=t;await e.request({method:`wallet_showCallsStatus`,params:[n]})}oa();async function Rv(e,t){let{account:n=e.account}=t;if(!n)throw new Fm({docsPath:`/docs/eip7702/signAuthorization`});let r=aa(n);if(!r.signAuthorization)throw new Im({docsPath:`/docs/eip7702/signAuthorization`,metaMessages:["The `signAuthorization` Action does not support JSON-RPC Accounts."],type:r.type});let i=await jv(e,t);return r.signAuthorization(i)}oa(),k();async function zv(e,{account:t=e.account,message:n}){if(!t)throw new Fm({docsPath:`/docs/actions/wallet/signMessage`});let r=aa(t);if(r.signMessage)return r.signMessage({message:n});let i=typeof n==`string`?Mn(n):n.raw instanceof Uint8Array?kn(n.raw):n.raw;return e.request({method:`personal_sign`,params:[i,r.address]},{retryCount:0})}oa(),k(),Ku(),td();async function Bv(e,t){let{account:n=e.account,chain:r=e.chain,...i}=t;if(!n)throw new Fm({docsPath:`/docs/actions/wallet/signTransaction`});let a=aa(n);ed({account:a,...t});let o=await T(e,Fd,`getChainId`)({});r!==null&&Lm({currentChainId:o,chain:r});let s=(r?.formatters||e.chain?.formatters)?.transactionRequest?.format||Hu;return a.signTransaction?a.signTransaction({...i,chainId:o},{serializer:e.chain?.serializers?.transaction}):await e.request({method:`eth_signTransaction`,params:[{...s({...i,account:a},`signTransaction`),chainId:O(o),from:a.address}]},{retryCount:0})}oa();async function Vv(e,t){let{account:n=e.account,domain:r,message:i,primaryType:a}=t;if(!n)throw new Fm({docsPath:`/docs/actions/wallet/signTypedData`});let o=aa(n),s={EIP712Domain:Vh({domain:r}),...t.types};if(Bh({domain:r,message:i,primaryType:a,types:s}),o.signTypedData)return o.signTypedData({domain:r,message:i,primaryType:a,types:s});let c=zh({domain:r,message:i,primaryType:a,types:s});return e.request({method:`eth_signTypedData_v4`,params:[o.address,c]},{retryCount:0})}k();async function Hv(e,{id:t}){await e.request({method:`wallet_switchEthereumChain`,params:[{chainId:O(t)}]},{retryCount:0})}async function Uv(e,t){return await e.request({method:`wallet_watchAsset`,params:t},{retryCount:0})}async function Wv(e,t){return Vm.internal(e,Iv,`sendTransactionSync`,t)}function Gv(e){return{addChain:t=>Ev(e,t),deployContract:t=>Dv(e,t),fillTransaction:t=>Id(e,t),getAddresses:()=>Ov(e),getCallsStatus:t=>Xm(e,t),getCapabilities:t=>kv(e,t),getChainId:()=>Fd(e),getPermissions:()=>Av(e),prepareAuthorization:t=>jv(e,t),prepareTransactionRequest:t=>zd(e,t),requestAddresses:()=>Mv(e),requestPermissions:t=>Nv(e,t),sendCalls:t=>Ym(e,t),sendCallsSync:t=>Pv(e,t),sendRawTransaction:t=>Rm(e,t),sendRawTransactionSync:t=>Tv(e,t),sendTransaction:t=>Bm(e,t),sendTransactionSync:t=>Iv(e,t),showCallsStatus:t=>Lv(e,t),signAuthorization:t=>Rv(e,t),signMessage:t=>zv(e,t),signTransaction:t=>Bv(e,t),signTypedData:t=>Vv(e,t),switchChain:t=>Hv(e,t),waitForCallsStatus:t=>Zm(e,t),watchAsset:t=>Uv(e,t),writeContract:t=>Vm(e,t),writeContractSync:t=>Wv(e,t)}}function Kv(e){let{key:t=`wallet`,name:n=`Wallet Client`,transport:r}=e;return rh({...e,key:t,name:n,transport:r,type:`walletClient`}).extend(Gv)}function qv({key:e,methods:t,name:n,request:r,retryCount:i=3,retryDelay:a=150,timeout:o,type:s},c){let l=nh();return{config:{key:e,methods:t,name:n,request:r,retryCount:i,retryDelay:a,timeout:o,type:s},request:Th(r,{methods:t,retryCount:i,retryDelay:a,uid:l}),value:c}}function Jv(e,t={}){let{key:n=`custom`,methods:r,name:i=`Custom Provider`,retryDelay:a}=t;return({retryCount:o})=>qv({key:n,methods:r,name:i,request:e.request.bind(e),retryCount:t.retryCount??o,retryDelay:a,type:`custom`})}Pu(),ss();function Yv(e,t={}){let{key:n=`fallback`,name:r=`Fallback`,rank:i=!1,shouldThrow:a=Xv,retryCount:o,retryDelay:s}=t;return(({chain:t,pollingInterval:c=4e3,timeout:l,...u})=>{let d=e,f=()=>{},p=qv({key:n,name:r,async request({method:e,params:n}){let r,i=async(o=0)=>{let s=d[o]({...u,chain:t,retryCount:0,timeout:l});try{let t=await s.request({method:e,params:n});return f({method:e,params:n,response:t,transport:s,status:`success`}),t}catch(c){if(f({error:c,method:e,params:n,transport:s,status:`error`}),a(c)||o===d.length-1||(r??=d.slice(o+1).some(n=>{let{include:r,exclude:i}=n({chain:t}).config.methods||{};return r?r.includes(e):i?!i.includes(e):!0}),!r))throw c;return i(o+1)}};return i()},retryCount:o,retryDelay:s,type:`fallback`},{onResponse:e=>f=e,transports:d.map(e=>e({chain:t,retryCount:0}))});if(i){let e=typeof i==`object`?i:{};Zv({chain:t,interval:e.interval??c,onTransports:e=>d=e,ping:e.ping,sampleCount:e.sampleCount,timeout:e.timeout,transports:d,weights:e.weights})}return p})}function Xv(e){return!!(`code`in e&&typeof e.code==`number`&&(e.code===Ho.code||e.code===Ko.code||e.code===as.code||Su.nodeMessage.test(e.message)||e.code===5e3))}function Zv({chain:e,interval:t=4e3,onTransports:n,ping:r,sampleCount:i=10,timeout:a=1e3,transports:o,weights:s={}}){let{stability:c=.7,latency:l=.3}=s,u=[],d=async()=>{let s=await Promise.all(o.map(async t=>{let n=t({chain:e,retryCount:0,timeout:a}),i=Date.now(),o,s;try{await(r?r({transport:n}):n.request({method:`net_listening`})),s=1}catch{s=0}finally{o=Date.now()}return{latency:o-i,success:s}}));u.push(s),u.length>i&&u.shift();let f=Math.max(...u.map(e=>Math.max(...e.map(({latency:e})=>e))));n(o.map((e,t)=>{let n=u.map(e=>e[t].latency),r=1-n.reduce((e,t)=>e+t,0)/n.length/f,i=u.map(e=>e[t].success),a=i.reduce((e,t)=>e+t,0)/i.length;return a===0?[0,t]:[l*r+c*a,t]}).sort((e,t)=>t[0]-e[0]).map(([,e])=>o[e])),await Dm(t),d()};d()}D();var Qv=class extends E{constructor(){super(`No URL was provided to the Transport. Please provide a valid RPC URL to the Transport.`,{docsPath:`/docs/clients/intro`,name:`UrlRequiredError`})}};Ao(),qp();function $v(e,t={}){let{batch:n,fetchFn:r,fetchOptions:i,key:a=`http`,methods:o,name:s=`HTTP JSON-RPC`,onFetchRequest:c,onFetchResponse:l,retryDelay:u,raw:d}=t;return({chain:f,retryCount:p,timeout:m})=>{let{batchSize:h=1e3,wait:g=0}=typeof n==`object`?n:{},_=t.retryCount??p,v=m??t.timeout??1e4,y=e||f?.rpcUrls.default.http[0];if(!y)throw new Qv;let b=jh(y,{fetchFn:r,fetchOptions:i,onRequest:c,onResponse:l,timeout:v});return qv({key:a,methods:o,name:s,async request({method:e,params:t}){let r={method:e,params:t},{schedule:i}=Gp({id:y,wait:g,shouldSplitBatch(e){return e.length>h},fn:e=>b.request({body:e}),sort:(e,t)=>e.id-t.id}),[{error:a,result:o}]=await(async e=>n?i(e):[await b.request({body:e})])(r);if(d)return{error:a,result:o};if(a)throw new Oo({body:r,error:a,url:y});return o},retryCount:_,retryDelay:u,timeout:v,type:`http`},{fetchOptions:i,url:y})}}var ey=L({id:16600,name:`0G Newton Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-newton.0g.ai`}},testnet:!0}),ty=L({id:16601,name:`0G Galileo Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-galileo.0g.ai`}},testnet:!0}),ny=L({id:16661,name:`0G Mainnet`,nativeCurrency:{name:`0G`,symbol:`0G`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan.0g.ai`}},testnet:!1}),ry=L({id:16602,name:`0G Galileo Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-galileo.0g.ai`}},testnet:!0}),iy=L({id:995,name:`5ireChain`,nativeCurrency:{name:`5ire Token`,symbol:`5IRE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.5ire.network`]}},blockExplorers:{default:{name:`5ireChain Mainnet Explorer`,url:`https://5irescan.io/`}},testnet:!1}),ay=L({id:179,name:`ABEY Mainnet`,nativeCurrency:{name:`ABEY`,symbol:`ABEY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.abeychain.com`]}},blockExplorers:{default:{name:`Abey Scan`,url:`https://abeyscan.com`}},testnet:!1});$u();var oy=50000n,sy=Zu*32n;On(),Un(),k(),Ku();var cy={block:dd({format(e){let t=e.transactions?.map(e=>{if(typeof e==`string`)return e;let t=cy.transaction?.format(e);return t.typeHex===`0x71`?t.type=`eip712`:t.typeHex===`0xff`&&(t.type=`priority`),t});return{l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,l1BatchTimestamp:e.l1BatchTimestamp?Tn(e.l1BatchTimestamp):null,transactions:t}}}),transaction:cd({format(e){let t={};return e.type===`0x71`?t.type=`eip712`:e.type===`0xff`&&(t.type=`priority`),{...t,l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,l1BatchTxIndex:e.l1BatchTxIndex?Tn(e.l1BatchTxIndex):null}}}),transactionReceipt:Km({format(e){return{l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,l1BatchTxIndex:e.l1BatchTxIndex?Tn(e.l1BatchTxIndex):null,logs:e.logs.map(e=>({...Ud(e),l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,transactionLogIndex:Dn(e.transactionLogIndex),logType:e.logType})),l2ToL1Logs:e.l2ToL1Logs.map(e=>({blockNumber:Tn(e.blockHash),blockHash:e.blockHash,l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,transactionIndex:Tn(e.transactionIndex),shardId:Tn(e.shardId),isService:e.isService,sender:e.sender,key:e.key,value:e.value,transactionHash:e.transactionHash,logIndex:Tn(e.logIndex)}))}}}),transactionRequest:Gu({exclude:[`customSignature`,`factoryDeps`,`gasPerPubdata`,`paymaster`,`paymasterInput`],format(e){return e.gasPerPubdata||e.paymaster&&e.paymasterInput||e.factoryDeps||e.customSignature?{eip712Meta:{...e.gasPerPubdata?{gasPerPubdata:kn(e.gasPerPubdata)}:{gasPerPubdata:kn(oy)},...e.paymaster&&e.paymasterInput?{paymasterParams:{paymaster:e.paymaster,paymasterInput:Array.from(Rn(e.paymasterInput))}}:{},...e.factoryDeps?{factoryDeps:e.factoryDeps.map(e=>Array.from(Rn(e)))}:{},...e.customSignature?{customSignature:Array.from(Rn(e.customSignature))}:{}},type:`0x71`}:{}}})};D();var ly=class extends E{constructor(){super([`Transaction is not an EIP712 transaction.`,``,`Transaction must:`,' - include `type: "eip712"`'," - include one of the following: `customSignature`, `paymaster`, `paymasterInput`, `gasPerPubdata`, `factoryDeps`"].join(` +`),{name:`InvalidEip712TransactionError`})}};function uy(e){return!!(e.type===`eip712`||`customSignature`in e&&e.customSignature||`paymaster`in e&&e.paymaster||`paymasterInput`in e&&e.paymasterInput||`gasPerPubdata`in e&&typeof e.gasPerPubdata==`bigint`||`factoryDeps`in e&&e.factoryDeps)}gi(),D(),Fp(),Ei();function dy(e){let{chainId:t,to:n,from:r,paymaster:i,paymasterInput:a}=e;if(!uy(e))throw new ly;if(!t||t<=0)throw new Pp({chainId:t});if(n&&!Ci(n))throw new hi({address:n});if(r&&!Ci(r))throw new hi({address:r});if(i&&!Ci(i))throw new hi({address:i});if(i&&!a)throw new E("`paymasterInput` must be provided when `paymaster` is defined");if(!i&&a)throw new E("`paymaster` must be provided when `paymasterInput` is defined")}Ai(),k();function fy(e,t){return uy(e)?my(e):mh(e,t)}var py={transaction:fy};function my(e){let{chainId:t,gas:n,nonce:r,to:i,from:a,value:o,maxFeePerGas:s,maxPriorityFeePerGas:c,customSignature:l,factoryDeps:u,paymaster:d,paymasterInput:f,gasPerPubdata:p,data:m}=e;return dy(e),ki([`0x71`,mu([r?kn(r):`0x`,c?kn(c):`0x`,s?kn(s):`0x`,n?kn(n):`0x`,i??`0x`,o?kn(o):`0x`,m??`0x`,kn(t),kn(``),kn(``),kn(t),a??`0x`,kn(p||oy),u??[],l??`0x`,d&&f?[d,f]:[]])])}D();var hy=class extends E{constructor({givenLength:e,maxBytecodeSize:t}){super(`Bytecode cannot be longer than ${t} bytes. Given length: ${e}`,{name:`BytecodeLengthExceedsMaxSizeError`})}},gy=class extends E{constructor({givenLengthInWords:e}){super(`Bytecode length in 32-byte words must be odd. Given length in words: ${e}`,{name:`BytecodeLengthInWordsMustBeOddError`})}},_y=class extends E{constructor({givenLength:e}){super(`The bytecode length in bytes must be divisible by 32. Given length: ${e}`,{name:`BytecodeLengthMustBeDivisibleBy32Error`})}};gn(),Un();function vy(e){let t=Fn(e);if(t.length%32!=0)throw new _y({givenLength:t.length});if(t.length>sy)throw new hy({givenLength:t.length,maxBytecodeSize:sy});let n=Fn(bd(t)),r=t.length/32;if(r%2==0)throw new gy({givenLengthInWords:r});let i=pn(Fn(r),{size:2}),a=new Uint8Array([1,0]);return n.set(a,0),n.set(i,2),n}k();var yy=e=>{dy(e);let t=by(e);return{domain:{name:`zkSync`,version:`2`,chainId:e.chainId},types:{Transaction:[{name:`txType`,type:`uint256`},{name:`from`,type:`uint256`},{name:`to`,type:`uint256`},{name:`gasLimit`,type:`uint256`},{name:`gasPerPubdataByteLimit`,type:`uint256`},{name:`maxFeePerGas`,type:`uint256`},{name:`maxPriorityFeePerGas`,type:`uint256`},{name:`paymaster`,type:`uint256`},{name:`nonce`,type:`uint256`},{name:`value`,type:`uint256`},{name:`data`,type:`bytes`},{name:`factoryDeps`,type:`bytes32[]`},{name:`paymasterInput`,type:`bytes`}]},primaryType:`Transaction`,message:t}};function by(e){let{gas:t,nonce:n,to:r,from:i,value:a,maxFeePerGas:o,maxPriorityFeePerGas:s,factoryDeps:c,paymaster:l,paymasterInput:u,gasPerPubdata:d,data:f}=e;return{txType:113n,from:BigInt(i),to:r?BigInt(r):0n,gasLimit:t??0n,gasPerPubdataByteLimit:d??50000n,maxFeePerGas:o??0n,maxPriorityFeePerGas:s??0n,paymaster:l?BigInt(l):0n,nonce:n?BigInt(n):0n,value:a??0n,data:f??`0x`,factoryDeps:c?.map(e=>kn(vy(e)))??[],paymasterInput:u||`0x`}}var xy={blockTime:1e3,formatters:cy,serializers:py,custom:{getEip712Domain:yy}},Sy=L({...xy,blockTime:200,id:2741,name:`Abstract`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.mainnet.abs.xyz`],webSocket:[`wss://api.mainnet.abs.xyz/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://abscan.org`},native:{name:`Abstract Explorer`,url:`https://explorer.mainnet.abs.xyz`}},contracts:{multicall3:{address:`0xAa4De41dba0Ca5dCBb288b7cC6b708F3aaC759E7`,blockCreated:5288},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:5263}}}),Cy=L({...xy,blockTime:200,id:11124,name:`Abstract Testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.testnet.abs.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.abscan.org`},native:{name:`Abstract Explorer`,url:`https://explorer.testnet.abs.xyz`}},testnet:!0,contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:358349},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:431682}}}),wy=L({id:787,name:`Acala`,network:`acala`,nativeCurrency:{name:`Acala`,symbol:`ACA`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-acala.aca-api.network`],webSocket:[`wss://eth-rpc-acala.aca-api.network`]}},blockExplorers:{default:{name:`Acala Blockscout`,url:`https://blockscout.acala.network`,apiUrl:`https://blockscout.acala.network/api`}},testnet:!1}),Ty=L({id:47,name:`Acria IntelliChain`,nativeCurrency:{decimals:18,name:`ACRIA`,symbol:`ACRIA`},rpcUrls:{default:{http:[`https://aic.acria.ai`]}},blockExplorers:{default:{name:`Acria Explorer`,url:`https://explorer.acria.ai`}},testnet:!1}),Ey=L({id:1215,name:`ADF Chain`,nativeCurrency:{name:`ADDFILL`,symbol:`ADF`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.adftechnology.com`]}},blockExplorers:{default:{name:`ADF Mainnet Explorer`,url:`https://explorer.adftechnology.com`}},testnet:!1}),Dy=L({id:36900,name:`ADI_Chain`,nativeCurrency:{decimals:18,name:`ADI`,symbol:`ADI`},rpcUrls:{default:{http:[`https://rpc.adifoundation.ai`]}},blockExplorers:{default:{name:`ADI Explorer`,url:`https://explorer.adifoundation.ai`}},testnet:!1}),Oy=L({id:9990,name:`Agung Network`,nativeCurrency:{decimals:18,name:`Agung`,symbol:`AGNG`},rpcUrls:{default:{http:[`https://wss-async.agung.peaq.network`],webSocket:[`wss://wss-async.agung.peaq.network`]}},blockExplorers:{default:{name:`Subscan`,url:`https://agung-testnet.subscan.io`}},testnet:!0}),ky=L({id:168,name:`AIOZ Network`,nativeCurrency:{decimals:18,name:`AIOZ`,symbol:`AIOZ`},rpcUrls:{default:{http:[`https://eth-dataseed.aioz.network`]}},blockExplorers:{default:{name:`AIOZ Explorer`,url:`https://explorer.aioz.network`}},testnet:!1}),Ay=L({id:41455,name:`Aleph Zero`,nativeCurrency:{name:`Aleph Zero`,symbol:`AZERO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alephzero.raas.gelato.cloud`]}},blockExplorers:{default:{name:`Aleph Zero EVM Explorer`,url:`https://evm-explorer.alephzero.org`,apiUrl:`https://evm-explorer.alephzero.org/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4603377}}}),jy=L({id:2039,name:`Aleph Zero Testnet`,nativeCurrency:{name:`TZERO`,symbol:`TZERO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alephzero-testnet.gelato.digital`],webSocket:[`wss://ws.alephzero-testnet.gelato.digital`]}},blockExplorers:{default:{name:`Aleph Zero EVM Testnet explorer`,url:`https://evm-explorer-testnet.alephzero.org`,apiUrl:`https://evm-explorer-testnet.alephzero.org/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2861745}},testnet:!0}),My=L({id:10241024,name:`AlienX Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alienxchain.io/http`]}},blockExplorers:{default:{name:`AlienX Explorer`,url:`https://explorer.alienxchain.io`}},testnet:!1}),Ny=L({id:10241025,name:`ALIENX Hal Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://hal-rpc.alienxchain.io/http`]}},blockExplorers:{default:{name:`AlienX Explorer`,url:`https://hal-explorer.alienxchain.io`}},testnet:!0}),Py=L({id:8150,name:`Alpen Testnet`,nativeCurrency:{name:`Signet BTC`,symbol:`sBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.alpenlabs.io`]}},blockExplorers:{default:{name:`Alpen Explorer`,url:`https://explorer.testnet.alpenlabs.io`,apiUrl:`https://explorer.testnet.alpenlabs.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:290408}},testnet:!0}),Fy={gasPriceOracle:{address:`0x420000000000000000000000000000000000000F`},l1Block:{address:`0x4200000000000000000000000000000000000015`},l2CrossDomainMessenger:{address:`0x4200000000000000000000000000000000000007`},l2Erc721Bridge:{address:`0x4200000000000000000000000000000000000014`},l2StandardBridge:{address:`0x4200000000000000000000000000000000000010`},l2ToL1MessagePasser:{address:`0x4200000000000000000000000000000000000016`}};On();var Iy={block:dd({format(e){return{transactions:e.transactions?.map(e=>{if(typeof e==`string`)return e;let t=sd(e);return t.typeHex===`0x7e`&&(t.isSystemTx=e.isSystemTx,t.mint=e.mint?Tn(e.mint):void 0,t.sourceHash=e.sourceHash,t.type=`deposit`),t}),stateRoot:e.stateRoot}}}),transaction:cd({format(e){let t={};return e.type===`0x7e`&&(t.isSystemTx=e.isSystemTx,t.mint=e.mint?Tn(e.mint):void 0,t.sourceHash=e.sourceHash,t.type=`deposit`),t}}),transactionReceipt:Km({format(e){return{l1GasPrice:e.l1GasPrice?Tn(e.l1GasPrice):null,l1GasUsed:e.l1GasUsed?Tn(e.l1GasUsed):null,l1Fee:e.l1Fee?Tn(e.l1Fee):null,l1FeeScalar:e.l1FeeScalar?Number(e.l1FeeScalar):null}}})};gi(),Ei(),Ai(),k();function Ly(e,t){return By(e)?zy(e):mh(e,t)}var Ry={transaction:Ly};function zy(e){Vy(e);let{sourceHash:t,data:n,from:r,gas:i,isSystemTx:a,mint:o,to:s,value:c}=e;return ki([`0x7e`,mu([t,r,s??`0x`,o?kn(o):`0x`,c?kn(c):`0x`,i?kn(i):`0x`,a?`0x1`:`0x`,n??`0x`])])}function By(e){return e.type===`deposit`||e.sourceHash!==void 0}function Vy(e){let{from:t,to:n}=e;if(t&&!Ci(t))throw new hi({address:t});if(n&&!Ci(n))throw new hi({address:n})}var R={blockTime:2e3,contracts:Fy,formatters:Iy,serializers:Ry},Hy=1,Uy=L({...R,id:888888888,name:`Ancient8`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.ancient8.gg`]}},blockExplorers:{default:{name:`Ancient8 explorer`,url:`https://scan.ancient8.gg`,apiUrl:`https://scan.ancient8.gg/api`}},contracts:{...R.contracts,l2OutputOracle:{[Hy]:{address:`0xB09DC08428C8b4EFB4ff9C0827386CDF34277996`}},portal:{[Hy]:{address:`0x639F2AECE398Aa76b07e59eF6abe2cFe32bacb68`,blockCreated:19070571}},l1StandardBridge:{[Hy]:{address:`0xd5e3eDf5b68135D559D572E26bF863FBC1950033`,blockCreated:19070571}}},sourceId:Hy}),Wy=11155111,Gy=L({...R,id:28122024,name:`Ancient8 Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpcv2-testnet.ancient8.gg`]}},blockExplorers:{default:{name:`Ancient8 Celestia Testnet explorer`,url:`https://scanv2-testnet.ancient8.gg`,apiUrl:`https://scanv2-testnet.ancient8.gg/api`}},contracts:{...R.contracts,l2OutputOracle:{[Wy]:{address:`0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB`}},portal:{[Wy]:{address:`0xfa1d9E26A6aCD7b22115D27572c1221B9803c960`,blockCreated:4972908}},l1StandardBridge:{[Wy]:{address:`0xF6Bc0146d3c74D48306e79Ae134A260E418C9335`,blockCreated:4972908}}},sourceId:Wy}),Ky=L({id:31337,name:`Anvil`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`],webSocket:[`ws://127.0.0.1:8545`]}}}),qy=L({id:33139,name:`ApeChain`,nativeCurrency:{name:`ApeCoin`,symbol:`APE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.apechain.com/http`],webSocket:[`wss://rpc.apechain.com/ws`]}},blockExplorers:{default:{name:`Apescan`,url:`https://apescan.io`,apiUrl:`https://api.apescan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:20889}},sourceId:42161}),Jy=L({id:3993,name:`APEX Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.apexlayer.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp-testnet.apexlayer.xyz`,apiUrl:`https://exp-testnet.apexlayer.xyz/api`}},contracts:{multicall3:{address:`0xf7642be33a6b18D16a995657adb5a68CD0438aE2`,blockCreated:283775}},testnet:!0}),Yy=L({id:62606,name:`Apollo`,nativeCurrency:{decimals:18,name:`Apollo`,symbol:`APOLLO`},rpcUrls:{default:{http:[`https://mainnet-rpc.apolloscan.io`]}},blockExplorers:{default:{name:`Apollo Explorer`,url:`https://apolloscan.io`}}}),Xy=L({id:42161,name:`Arbitrum One`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:250,rpcUrls:{default:{http:[`https://arb1.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://arbiscan.io`,apiUrl:`https://api.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7654707}}}),Zy=L({id:421613,name:`Arbitrum Goerli`,nativeCurrency:{name:`Arbitrum Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli-rollup.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://goerli.arbiscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:88114}},testnet:!0}),Qy=L({id:42170,name:`Arbitrum Nova`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://nova.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://nova.arbiscan.io`,apiUrl:`https://api-nova.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1746963}}}),$y=L({id:421614,name:`Arbitrum Sepolia`,blockTime:250,nativeCurrency:{name:`Arbitrum Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rollup.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://sepolia.arbiscan.io`,apiUrl:`https://api-sepolia.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:81930}},testnet:!0}),eb=L({id:5042002,name:`Arc Testnet`,nativeCurrency:{name:`USDC`,symbol:`USDC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.arc.network`,`https://rpc.quicknode.testnet.arc.network`,`https://rpc.blockdaemon.testnet.arc.network`],webSocket:[`wss://rpc.testnet.arc.network`,`wss://rpc.quicknode.testnet.arc.network`]}},blockExplorers:{default:{name:`ArcScan`,url:`https://testnet.arcscan.app`,apiUrl:`https://testnet.arcscan.app/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),tb=L({id:7897,name:`Arena-Z`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.arena-z.gg`]}},blockExplorers:{default:{name:`Arena-Z Explorer`,url:`https://explorer.arena-z.gg`,apiUrl:`https://explorer.arena-z.gg`}}}),nb=L({id:463,name:`Areon Network`,nativeCurrency:{decimals:18,name:`AREA`,symbol:`AREA`},rpcUrls:{default:{http:[`https://mainnet-rpc.areon.network`],webSocket:[`wss://mainnet-ws.areon.network`]}},blockExplorers:{default:{name:`Areonscan`,url:`https://areonscan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:353286}},testnet:!1}),rb=L({id:462,name:`Areon Network Testnet`,nativeCurrency:{decimals:18,name:`TAREA`,symbol:`TAREA`},rpcUrls:{default:{http:[`https://testnet-rpc.areon.network`],webSocket:[`wss://testnet-ws.areon.network`]}},blockExplorers:{default:{name:`Areonscan`,url:`https://areonscan.com`}},testnet:!0}),ib=L({id:463,name:`Areum`,nativeCurrency:{decimals:18,name:`AREA`,symbol:`AREA`},rpcUrls:{default:{http:[`https://mainnet-rpc.areum.network`],webSocket:[`wss://mainnet-ws.areum.network`]}},blockExplorers:{default:{name:`Areum Explorer`,url:`https://explorer.areum.network`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:353286}},testnet:!1}),ab=L({id:11822,name:`Artela Testnet`,nativeCurrency:{name:`ART`,symbol:`ART`,decimals:18},rpcUrls:{default:{http:[`https://betanet-rpc1.artela.network`]}},blockExplorers:{default:{name:`Artela`,url:`https://betanet-scan.artela.network`,apiUrl:`https://betanet-scan.artela.network/api`}},contracts:{multicall3:{address:`0xd07c8635f76e8745Ee7092fbb6e8fbc5FeF09DD7`,blockCreated:7001871}},testnet:!0}),ob=L({id:10242,name:`Arthera`,nativeCurrency:{name:`Arthera`,symbol:`AA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.arthera.net`]}},blockExplorers:{default:{name:`Arthera EVM Explorer`,url:`https://explorer.arthera.net`,apiUrl:`https://explorer.arthera.net/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4502791}}}),sb=L({id:10243,name:`Arthera Testnet`,nativeCurrency:{name:`Arthera`,symbol:`AA`,decimals:18},rpcUrls:{default:{http:[`https://rpc-test.arthera.net`]}},blockExplorers:{default:{name:`Arthera EVM Explorer`,url:`https://explorer-test.arthera.net`,apiUrl:`https://explorer-test.arthera.net/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:22051}}}),cb=L({id:42420,name:`AssetChain Mainnet`,nativeCurrency:{decimals:18,name:`Real World Asset`,symbol:`RWA`},rpcUrls:{default:{http:[`https://mainnet-rpc.assetchain.org`]}},blockExplorers:{default:{name:`Asset Chain Explorer`,url:`https://scan.assetchain.org`,apiUrl:`https://scan.assetchain.org/api`}},testnet:!1,contracts:{}}),lb=L({id:42421,name:`AssetChain Testnet`,nativeCurrency:{decimals:18,name:`Real World Asset`,symbol:`RWA`},rpcUrls:{default:{http:[`https://enugu-rpc.assetchain.org`]}},blockExplorers:{default:{name:`Asset Chain Testnet Explorer`,url:`https://scan-testnet.assetchain.org`,apiUrl:`https://scan-testnet.assetchain.org/api`}},testnet:!0,contracts:{multicall3:{address:`0x989F832D35988cb5e3eB001Fa2Fe789469EC31Ea`,blockCreated:17177}}}),ub=L({id:592,name:`Astar`,network:`astar-mainnet`,nativeCurrency:{name:`Astar`,symbol:`ASTR`,decimals:18},rpcUrls:{default:{http:[`https://astar.api.onfinality.io/public`]}},blockExplorers:{default:{name:`Astar Subscan`,url:`https://astar.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:761794}},testnet:!1}),db=L({id:3776,name:`Astar zkEVM`,network:`AstarZkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-zkevm.astar.network`]}},blockExplorers:{default:{name:`Astar zkEVM Explorer`,url:`https://astar-zkevm.explorer.startale.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:93528}},testnet:!1}),fb=L({id:6038361,name:`Astar zkEVM Testnet zKyoto`,network:`zKyoto`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.startale.com/zkyoto`]}},blockExplorers:{default:{name:`zKyoto Explorer`,url:`https://zkyoto.explorer.startale.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:196153}},testnet:!0}),pb=L({id:2340,name:`Atleta Olympia`,nativeCurrency:{decimals:18,name:`Atla`,symbol:`ATLA`},rpcUrls:{default:{http:[`https://testnet-rpc.atleta.network:9944`,`https://testnet-rpc.atleta.network`],ws:[`wss://testnet-rpc.atleta.network:9944`]}},blockExplorers:{default:{name:`Atleta Olympia Explorer`,url:`https://blockscout.atleta.network`,apiUrl:`https://blockscout.atleta.network/api`}},contracts:{multicall3:{address:`0x1472ec6392180fb84F345d2455bCC75B26577115`,blockCreated:1076473}},testnet:!0}),mb=L({id:1313161554,name:`Aurora`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.aurora.dev`]}},blockExplorers:{default:{name:`Aurorascan`,url:`https://aurorascan.dev`,apiUrl:`https://aurorascan.dev/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:62907816}}}),hb=L({id:1313161555,name:`Aurora Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet.aurora.dev`]}},blockExplorers:{default:{name:`Aurorascan`,url:`https://testnet.aurorascan.dev`,apiUrl:`https://testnet.aurorascan.dev/api`}},testnet:!0}),gb=L({id:205205,name:`Auroria Testnet`,network:`auroria`,nativeCurrency:{name:`Auroria Stratis`,symbol:`tSTRAX`,decimals:18},rpcUrls:{default:{http:[`https://auroria.rpc.stratisevm.com`]}},blockExplorers:{default:{name:`Auroria Testnet Explorer`,url:`https://auroria.explorer.stratisevm.com`}},testnet:!0}),_b=L({id:785,name:`Autheo Testnet`,nativeCurrency:{decimals:18,name:`Autheo`,symbol:`THEO`},rpcUrls:{default:{http:[`https://testnet-rpc1.autheo.com`,`https://testnet-rpc2.autheo.com`]}},blockExplorers:{default:{name:`Autheo Testnet Block Explorer`,url:`https://testnet-explorer.autheo.com/`}}}),vb=L({id:43114,name:`Avalanche`,blockTime:1700,nativeCurrency:{decimals:18,name:`Avalanche`,symbol:`AVAX`},rpcUrls:{default:{http:[`https://api.avax.network/ext/bc/C/rpc`]}},blockExplorers:{default:{name:`SnowTrace`,url:`https://snowtrace.io`,apiUrl:`https://api.snowtrace.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11907934}}}),yb=L({id:43113,name:`Avalanche Fuji`,nativeCurrency:{decimals:18,name:`Avalanche Fuji`,symbol:`AVAX`},rpcUrls:{default:{http:[`https://api.avax-test.network/ext/bc/C/rpc`]}},blockExplorers:{default:{name:`SnowTrace`,url:`https://testnet.snowtrace.io`,apiUrl:`https://api-testnet.snowtrace.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7096959}},testnet:!0}),bb=L({id:8333,name:`B3`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.b3.fun/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.b3.fun`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}},sourceId:8453}),xb=L({id:1993,name:`B3 Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.b3.fun/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia.explorer.b3.fun`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}},testnet:!0,sourceId:168587773}),Sb=L({id:5165,network:`bahamut`,name:`Bahamut`,nativeCurrency:{name:`Fasttoken`,symbol:`FTN`,decimals:18},rpcUrls:{default:{http:[`https://rpc1.bahamut.io`,`https://bahamut-rpc.publicnode.com`,`https://rpc2.bahamut.io`],webSocket:[`wss://ws1.sahara.bahamutchain.com`,`wss://bahamut-rpc.publicnode.com`,`wss://ws2.sahara.bahamutchain.com`]}},blockExplorers:{default:{name:`Ftnscan`,url:`https://www.ftnscan.com`,apiUrl:`https://www.ftnscan.com/api`}}}),Cb=1,wb=L({...R,id:8453,name:`Base`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://basescan.org`,apiUrl:`https://api.basescan.org/api`}},contracts:{...R.contracts,disputeGameFactory:{[Cb]:{address:`0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e`}},l2OutputOracle:{[Cb]:{address:`0x56315b90c40730925ec5485cf004d835058518A0`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:5022},portal:{[Cb]:{address:`0x49048044D57e1C92A77f79988d21Fa8fAF74E97e`,blockCreated:17482143}},l1StandardBridge:{[Cb]:{address:`0x3154Cf16ccdb4C6d922629664174b904d80F2C35`,blockCreated:17482143}}},sourceId:Cb}),Tb=L({...wb,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://mainnet-preconf.base.org`]}}}),Eb=L({id:123420001114,name:`Basecamp Testnet`,nativeCurrency:{decimals:18,name:`Camp`,symbol:`CAMP`},rpcUrls:{default:{http:[`https://rpc.basecamp.t.raas.gelato.cloud`]}},blockExplorers:{default:{name:`basecamp`,url:`https://basecamp.cloud.blockscout.com`}},testnet:!0}),Db=5,Ob=L({...R,id:84531,name:`Base Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://goerli.basescan.org`,apiUrl:`https://goerli.basescan.org/api`}},contracts:{...R.contracts,l2OutputOracle:{[Db]:{address:`0x2A35891ff30313CcFa6CE88dcf3858bb075A2298`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1376988},portal:{[Db]:{address:`0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA`}},l1StandardBridge:{[Db]:{address:`0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a`}}},testnet:!0,sourceId:Db}),kb=11155111,Ab=L({...R,id:84532,network:`base-sepolia`,name:`Base Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://sepolia.basescan.org`,apiUrl:`https://api-sepolia.basescan.org/api`}},contracts:{...R.contracts,disputeGameFactory:{[kb]:{address:`0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1`}},l2OutputOracle:{[kb]:{address:`0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254`}},portal:{[kb]:{address:`0x49f53e41452c74589e85ca1677426ba426459e85`,blockCreated:4446677}},l1StandardBridge:{[kb]:{address:`0xfd0Bf71F60660E2f608ed56e1659C450eB113120`,blockCreated:4446677}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1059647}},testnet:!0,sourceId:kb}),jb=L({...Ab,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://sepolia-preconf.base.org`]}}}),Mb=L({id:4337,name:`Beam`,network:`beam`,nativeCurrency:{decimals:18,name:`Beam`,symbol:`BEAM`},rpcUrls:{default:{http:[`https://build.onbeam.com/rpc`],webSocket:[`wss://build.onbeam.com/ws`]}},blockExplorers:{default:{name:`Beam Explorer`,url:`https://subnets.avax.network/beam`}},contracts:{multicall3:{address:`0x4956f15efdc3dc16645e90cc356eafa65ffc65ec`,blockCreated:1}}}),Nb=L({id:13337,name:`Beam Testnet`,network:`beam`,nativeCurrency:{decimals:18,name:`Beam`,symbol:`BEAM`},rpcUrls:{default:{http:[`https://build.onbeam.com/rpc/testnet`],webSocket:[`wss://build.onbeam.com/ws/testnet`]}},blockExplorers:{default:{name:`Beam Explorer`,url:`https://subnets-test.avax.network/beam`}},contracts:{multicall3:{address:`0x9bf49b704ee2a095b95c1f2d4eb9010510c41c9e`,blockCreated:3}},testnet:!0}),Pb=L({id:641230,name:`Bear Network Chain Mainnet`,nativeCurrency:{decimals:18,name:`BearNetworkChain`,symbol:`BRNKC`},rpcUrls:{default:{http:[`https://brnkc-mainnet.bearnetwork.net`]}},blockExplorers:{default:{name:`BrnkScan`,url:`https://brnkscan.bearnetwork.net`,apiUrl:`https://brnkscan.bearnetwork.net/api`}}}),Fb=L({id:751230,name:`Bear Network Chain Testnet`,nativeCurrency:{decimals:18,name:`tBRNKC`,symbol:`tBRNKC`},rpcUrls:{default:{http:[`https://brnkc-test.bearnetwork.net`]}},blockExplorers:{default:{name:`BrnkTestScan`,url:`https://brnktest-scan.bearnetwork.net`,apiUrl:`https://brnktest-scan.bearnetwork.net/api`}},testnet:!0}),Ib=L({id:80094,name:`Berachain`,blockTime:2e3,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},ensRegistry:{address:`0x5b22280886a2f5e09a49bea7e320eab0e5320e28`,blockCreated:877007},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:9310021}},rpcUrls:{default:{http:[`https://rpc.berachain.com`]}},blockExplorers:{default:{name:`Berascan`,url:`https://berascan.com`}},ensTlds:[`.bera`],testnet:!1}),Lb=L({id:80069,blockTime:2e3,name:`Berachain Bepolia`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},rpcUrls:{default:{http:[`https://bepolia.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berascan`,url:`https://bepolia.beratrail.io`}},testnet:!0}),Rb=L({id:80085,name:`Berachain Artio`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},rpcUrls:{default:{http:[`https://artio.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berachain`,url:`https://artio.beratrail.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:866924}},testnet:!0}),zb=L({id:80084,name:`Berachain bArtio`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:109269},ensRegistry:{address:`0xB0eef18971290b333450586D33dcA6cE122651D2`,blockCreated:7736794},ensUniversalResolver:{address:`0x41692Ef1EA0C79E6b73077E4A67572D2BDbD7057`,blockCreated:7736795}},ensTlds:[`.bera`],rpcUrls:{default:{http:[`https://bartio.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berachain bArtio Beratrail`,url:`https://bartio.beratrail.io`}},testnet:!0}),Bb=L({id:11501,name:`BEVM Mainnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet-1.bevm.io`]}},blockExplorers:{default:{name:`Bevmscan`,url:`https://scan-mainnet.bevm.io`,apiUrl:`https://scan-mainnet-api.bevm.io/api`}}}),Vb=L({id:3068,name:`Bifrost Mainnet`,nativeCurrency:{name:`BFC`,symbol:`BFC`,decimals:18},rpcUrls:{default:{http:[`https://public-01.mainnet.bifrostnetwork.com/rpc`]}},blockExplorers:{default:{name:`Bifrost Blockscout`,url:`https://explorer.mainnet.bifrostnetwork.com`}},testnet:!1}),Hb=L({id:53456,name:`BirdLayer`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.birdlayer.xyz`,`https://rpc1.birdlayer.xyz`],webSocket:[`wss://rpc.birdlayer.xyz/ws`]}},blockExplorers:{default:{name:`BirdLayer Explorer`,url:`https://scan.birdlayer.xyz`}}}),Ub=L({id:32520,name:`Bitgert Mainnet`,nativeCurrency:{decimals:18,name:`Brise`,symbol:`Brise`},rpcUrls:{default:{http:[`https://rpc-bitgert.icecreamswap.com`]}},blockExplorers:{default:{name:`Bitgert Scan`,url:`https://brisescan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2118034}},testnet:!1}),Wb=L({id:96,name:`KUB Mainnet`,nativeCurrency:{name:`KUB Coin`,symbol:`KUB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitkubchain.io`]}},blockExplorers:{default:{name:`KUB Chain Mainnet Explorer`,url:`https://www.bkcscan.com`,apiUrl:`https://www.bkcscan.com/api`}}}),Gb=L({id:25925,name:`Bitkub Testnet`,network:`Bitkub Testnet`,nativeCurrency:{name:`Bitkub Test`,symbol:`tKUB`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.bitkubchain.io`]}},blockExplorers:{default:{name:`Bitkub Chain Testnet Explorer`,url:`https://testnet.bkcscan.com`,apiUrl:`https://testnet.bkcscan.com/api`}},testnet:!0}),Kb=L({id:200901,name:`Bitlayer Mainnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitlayer.org`],webSocket:[`wss://ws.bitlayer.org`]}},blockExplorers:{default:{name:`bitlayer mainnet scan`,url:`https://www.btrscan.com`}},contracts:{multicall3:{address:`0x5B256fE9e993902eCe49D138a5b1162cBb529474`,blockCreated:2421963}}}),qb=L({id:200810,name:`Bitlayer Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bitlayer.org`],webSocket:[`wss://testnet-ws.bitlayer.org`]}},blockExplorers:{default:{name:`bitlayer testnet scan`,url:`https://testnet.btrscan.com`}},contracts:{multicall3:{address:`0x5B256fE9e993902eCe49D138a5b1162cBb529474`,blockCreated:4135671}},testnet:!0}),Jb=L({id:7171,name:`Bitrock Mainnet`,nativeCurrency:{name:`BROCK`,symbol:`BROCK`,decimals:18},rpcUrls:{default:{http:[`https://brockrpc.io`]}},blockExplorers:{default:{name:`Bitrock Explorer`,url:`https://explorer.bit-rock.io`}},testnet:!1}),Yb=L({id:199,name:`BitTorrent`,network:`bittorrent-chain-mainnet`,nativeCurrency:{name:`BitTorrent`,symbol:`BTT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bittorrentchain.io`]}},blockExplorers:{default:{name:`Bttcscan`,url:`https://bttcscan.com`,apiUrl:`https://api.bttcscan.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:31078552}}}),Xb=L({id:1028,name:`BitTorrent Chain Testnet`,network:`bittorrent-chain-testnet`,nativeCurrency:{name:`BitTorrent`,symbol:`BTT`,decimals:18},rpcUrls:{default:{http:[`https://testrpc.bittorrentchain.io`]}},blockExplorers:{default:{name:`Bttcscan`,url:`https://testnet.bttcscan.com`,apiUrl:`https://testnet.bttcscan.com/api`}},testnet:!0}),Zb=1,Qb=L({...R,id:81457,name:`Blast`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.blast.io`]}},blockExplorers:{default:{name:`Blastscan`,url:`https://blastscan.io`,apiUrl:`https://api.blastscan.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:212929},l2OutputOracle:{[Zb]:{address:`0x826D1B0D4111Ad9146Eb8941D7Ca2B6a44215c76`,blockCreated:19300358}},portal:{[Zb]:{address:`0x0Ec68c5B10F21EFFb74f2A5C61DFe6b08C0Db6Cb`,blockCreated:19300357}},l1StandardBridge:{[Zb]:{address:`0x697402166Fbf2F22E970df8a6486Ef171dbfc524`,blockCreated:19300360}}},sourceId:Zb}),$b=L({id:168587773,name:`Blast Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.blast.io`]}},blockExplorers:{default:{name:`Blastscan`,url:`https://sepolia.blastscan.io`,apiUrl:`https://api-sepolia.blastscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:756690}},testnet:!0,sourceId:11155111}),ex=1,tx=L({...R,id:60808,name:`BOB`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.gobob.xyz`],webSocket:[`wss://rpc.gobob.xyz`]}},blockExplorers:{default:{name:`BOB Explorer`,url:`https://explorer.gobob.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:23131},l2OutputOracle:{[ex]:{address:`0xdDa53E23f8a32640b04D7256e651C1db98dB11C1`,blockCreated:4462615}},portal:{[ex]:{address:`0x8AdeE124447435fE03e3CD24dF3f4cAE32E65a3E`,blockCreated:4462615}}},sourceId:ex}),nx=L({id:288,name:`Boba Network`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.boba.network`]}},blockExplorers:{default:{name:`BOBAScan`,url:`https://bobascan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:446859}}}),rx=L({id:28882,name:`Boba Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.boba.network`]}},blockExplorers:{default:{name:`BOBAScan`,url:`https://testnet.bobascan.com`}},testnet:!0}),ix=11155111,ax=L({...R,id:808813,name:`BOB Sepolia`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://bob-sepolia.rpc.gobob.xyz`],webSocket:[`wss://bob-sepolia.rpc.gobob.xyz`]}},blockExplorers:{default:{name:`BOB Sepolia Explorer`,url:`https://bob-sepolia.explorer.gobob.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:35677},l2OutputOracle:{[ix]:{address:`0x14D0069452b4AE2b250B395b8adAb771E4267d2f`,blockCreated:4462615}},portal:{[ix]:{address:`0x867B1Aa872b9C8cB5E9F7755feDC45BB24Ad0ae4`,blockCreated:4462615}}},testnet:!0,sourceId:ix}),ox=L({id:11100,name:`Bool Beta Mainnet`,nativeCurrency:{decimals:18,name:`BOL`,symbol:`BOL`},rpcUrls:{default:{http:[`https://beta-rpc-node-http.bool.network`]}},blockExplorers:{default:{name:`BoolScan`,url:`https://beta-mainnet.boolscan.com/`}},testnet:!1}),sx=L({id:3637,name:`Botanix`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.botanixlabs.com`],webSocket:[`wss://rpc.botanixlabs.com/ws`]}},blockExplorers:{default:{name:`Botanixscan`,url:`https://botanixscan.io`}}}),cx=L({id:3636,name:`Botanix Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://node.botanixlabs.dev`]}},blockExplorers:{default:{name:`Botanix Testnet Explorer`,url:`https://testnet.botanixscan.io`}},testnet:!0}),lx=L({id:6001,name:`BounceBit Mainnet`,nativeCurrency:{name:`BounceBit`,symbol:`BB`,decimals:18},rpcUrls:{default:{http:[`https://fullnode-mainnet.bouncebitapi.com`]}},blockExplorers:{default:{name:`BB Scan`,url:`https://bbscan.io`}},testnet:!1}),ux=L({id:6e3,name:`BounceBit Testnet`,nativeCurrency:{name:`BounceBit`,symbol:`BB`,decimals:18},rpcUrls:{default:{http:[`https://fullnode-testnet.bouncebitapi.com`]}},blockExplorers:{default:{name:`BB Scan`,url:`https://testnet.bbscan.io`}},testnet:!0}),dx=L({id:1039,name:`Bronos`,nativeCurrency:{decimals:18,name:`BRO`,symbol:`BRO`},rpcUrls:{default:{http:[`https://evm.bronos.org`]}},blockExplorers:{default:{name:`BronoScan`,url:`https://broscan.bronos.org`}}}),fx=L({id:1038,name:`Bronos Testnet`,nativeCurrency:{decimals:18,name:`Bronos Coin`,symbol:`tBRO`},rpcUrls:{default:{http:[`https://evm-testnet.bronos.org`]}},blockExplorers:{default:{name:`BronoScan`,url:`https://tbroscan.bronos.org`}},testnet:!0}),px=L({id:56,name:`BNB Smart Chain`,blockTime:750,nativeCurrency:{decimals:18,name:`BNB`,symbol:`BNB`},rpcUrls:{default:{http:[`https://56.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`BscScan`,url:`https://bscscan.com`,apiUrl:`https://api.bscscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15921452}}}),mx=L({id:1017,name:`BNB Greenfield Chain`,nativeCurrency:{decimals:18,name:`BNB`,symbol:`BNB`},rpcUrls:{default:{http:[`https://greenfield-chain.bnbchain.org`]}},blockExplorers:{default:{name:`BNB Greenfield Mainnet Scan`,url:`https://greenfieldscan.com`}},testnet:!1}),hx=L({id:97,name:`BNB Smart Chain Testnet`,nativeCurrency:{decimals:18,name:`BNB`,symbol:`tBNB`},rpcUrls:{default:{http:[`https://data-seed-prebsc-1-s1.bnbchain.org:8545`]}},blockExplorers:{default:{name:`BscScan`,url:`https://testnet.bscscan.com`,apiUrl:`https://api-testnet.bscscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:17422483}},testnet:!0}),gx=L({id:223,name:`B2`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bsquared.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.bsquared.network`}}}),_x=L({id:1123,name:`B2 Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bsquared.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet-explorer.bsquared.network`}},testnet:!0}),vx=L({id:200901,name:`Bitlayer`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitlayer.org`,`https://rpc.bitlayer-rpc.com`],webSocket:[`wss://ws.bitlayer.org`,`wss://ws.bitlayer-rpc.com`]}},blockExplorers:{default:{name:`Bitlayer(BTR) Scan`,url:`https://www.btrscan.com`}}}),yx=L({id:200810,name:`Bitlayer Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bitlayer.org`],webSocket:[`wss://testnet-ws.bitlayer.org`,`wss://testnet-ws.bitlayer-rpc.com`]}},blockExplorers:{default:{name:`Bitlayer(BTR) Scan`,url:`https://testnet.btrscan.com`}},testnet:!0}),bx=L({id:4999,name:`BlackFort Exchange Network`,nativeCurrency:{name:`BlackFort Token`,symbol:`BXN`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.blackfort.network/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.blackfort.network`,apiUrl:`https://explorer.blackfort.network/api`}}}),xx=L({id:4777,name:`BlackFort Exchange Network Testnet`,nativeCurrency:{name:`BlackFort Testnet Token`,symbol:`TBXN`,decimals:18},rpcUrls:{default:{http:[`https://testnet.blackfort.network/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.blackfort.network`,apiUrl:`https://testnet-explorer.blackfort.network/api`}},testnet:!0}),Sx=L({id:13370,name:`Cannon`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),Cx=L({id:7700,name:`Canto`,nativeCurrency:{decimals:18,name:`Canto`,symbol:`CANTO`},rpcUrls:{default:{http:[`https://canto.gravitychain.io`]}},blockExplorers:{default:{name:`Tuber.Build (Blockscout)`,url:`https://tuber.build`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2905789}}}),wx={estimateFeesPerGas:async e=>{if(!e.request?.feeCurrency)return null;let[t,n]=await Promise.all([Tx(e.client,e.request.feeCurrency),Ex(e.client,e.request.feeCurrency)]);return{maxFeePerGas:e.multiply(t-n)+n,maxPriorityFeePerGas:n}}};async function Tx(e,t){let n=await e.request({method:`eth_gasPrice`,params:[t]});return BigInt(n)}async function Ex(e,t){let n=await e.request({method:`eth_maxPriorityFeePerGas`,params:[t]});return BigInt(n)}Cn();function Dx(e){return e===0||e===0n||e==null||e===`0`||e===``||typeof e==`string`&&(Sn(e).toLowerCase()===`0x`||Sn(e).toLowerCase()===`0x00`)}function Ox(e){return!Dx(e)}function kx(e){return e.maxFeePerGas!==void 0&&e.maxPriorityFeePerGas!==void 0}function Ax(e){return e.type===`cip64`?!0:kx(e)&&Ox(e.feeCurrency)}On(),Ku();var jx={block:dd({format(e){return{transactions:e.transactions?.map(e=>typeof e==`string`?e:{...sd(e),...e.gatewayFee?{gatewayFee:Tn(e.gatewayFee),gatewayFeeRecipient:e.gatewayFeeRecipient}:{},feeCurrency:e.feeCurrency})}}}),transaction:cd({format(e){if(e.type===`0x7e`)return{isSystemTx:e.isSystemTx,mint:e.mint?Tn(e.mint):void 0,sourceHash:e.sourceHash,type:`deposit`};let t={feeCurrency:e.feeCurrency};return e.type===`0x7b`?t.type=`cip64`:(e.type===`0x7c`&&(t.type=`cip42`),t.gatewayFee=e.gatewayFee?Tn(e.gatewayFee):null,t.gatewayFeeRecipient=e.gatewayFeeRecipient),t}}),transactionRequest:Gu({format(e){let t={};return e.feeCurrency&&(t.feeCurrency=e.feeCurrency),Ax(e)&&(t.type=`0x7b`),t}})};$u(),gi(),D(),Fp(),Pu(),Ei(),Ai(),k();function Mx(e,t){return Ax(e)?Px(e,t):Ly(e,t)}var Nx={transaction:Mx};function Px(e,t){Ix(e);let{chainId:n,gas:r,nonce:i,to:a,value:o,maxFeePerGas:s,maxPriorityFeePerGas:c,accessList:l,feeCurrency:u,data:d}=e;return ki([`0x7b`,mu([kn(n),i?kn(i):`0x`,c?kn(c):`0x`,s?kn(s):`0x`,r?kn(r):`0x`,a??`0x`,o?kn(o):`0x`,d??`0x`,ph(l),u,...bh(e,t)])])}var Fx=Qu;function Ix(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a,feeCurrency:o}=e;if(t<=0)throw new Pp({chainId:t});if(a&&!Ci(a))throw new hi({address:a});if(r)throw new E("`gasPrice` is not a valid CIP-64 Transaction attribute.");if(Ox(i)&&i>Fx)throw new Cu({maxFeePerGas:i});if(Ox(n)&&Ox(i)&&n>i)throw new Mu({maxFeePerGas:i,maxPriorityFeePerGas:n});if(Ox(o)&&!Ci(o))throw new E("`feeCurrency` MUST be a token address for CIP-64 transactions.");if(Dx(o))throw new E("`feeCurrency` must be provided for CIP-64 transactions.")}var Lx={blockTime:1e3,contracts:Fy,formatters:jx,serializers:Nx,fees:wx},Rx=L({...Lx,id:42220,name:`Celo`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`CELO`},rpcUrls:{default:{http:[`https://forno.celo.org`]}},blockExplorers:{default:{name:`Celo Explorer`,url:`https://celoscan.io`,apiUrl:`https://api.celoscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:13112599}},testnet:!1}),zx=17e3,Bx=L({...Lx,id:44787,name:`Alfajores`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`A-CELO`},rpcUrls:{default:{http:[`https://alfajores-forno.celo-testnet.org`]}},blockExplorers:{default:{name:`Celo Alfajores Explorer`,url:`https://celo-alfajores.blockscout.com`,apiUrl:`https://celo-alfajores.blockscout.com/api`}},contracts:{...Lx.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:14569001},portal:{[zx]:{address:`0x82527353927d8D069b3B452904c942dA149BA381`,blockCreated:2411324}},disputeGameFactory:{[zx]:{address:`0xE28AAdcd9883746c0e5068F58f9ea06027b214cb`,blockCreated:2411324}},l2OutputOracle:{[zx]:{address:`0x4a2635e9e4f6e45817b1D402ac4904c1d1752438`,blockCreated:2411324}},l1StandardBridge:{[zx]:{address:`0xD1B0E0581973c9eB7f886967A606b9441A897037`,blockCreated:2411324}}},testnet:!0}),Vx=11155111,Hx=L({...Lx,id:11142220,name:`Celo Sepolia Testnet`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`S-CELO`},rpcUrls:{default:{http:[`https://forno.celo-sepolia.celo-testnet.org`]}},blockExplorers:{default:{name:`Celo Sepolia Explorer`,url:`https://celo-sepolia.blockscout.com/`,apiUrl:`https://celo-sepolia.blockscout.com/api`}},contracts:{...Lx.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1},portal:{[Vx]:{address:`0x44ae3d41a335a7d05eb533029917aad35662dcc2`,blockCreated:8825790}},disputeGameFactory:{[Vx]:{address:`0x57c45d82d1a995f1e135b8d7edc0a6bb5211cfaa`,blockCreated:8825790}},l1StandardBridge:{[Vx]:{address:`0xec18a3c30131a0db4246e785355fbc16e2eaf408`,blockCreated:8825790}}},testnet:!0}),Ux=L({id:5858,name:`Chang Chain Foundation Mainnet`,nativeCurrency:{decimals:18,name:`CTH`,symbol:`CTH`},rpcUrls:{default:{http:[`https://rpc.cthscan.com`]}},blockExplorers:{default:{name:`Chang Chain explorer`,url:`https://cthscan.com`}}}),Wx=L({id:88888,name:`Chiliz Chain`,network:`chiliz-chain`,nativeCurrency:{decimals:18,name:`CHZ`,symbol:`CHZ`},rpcUrls:{default:{http:[`https://rpc.chiliz.com`]}},blockExplorers:{default:{name:`Chiliz Explorer`,url:`https://scan.chiliz.com`,apiUrl:`https://scan.chiliz.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:8080847}}}),Gx=L({id:2882,name:`Chips Network`,network:`CHIPS`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://node.chips.ooo/wasp/api/v1/chains/iota1pp3d3mnap3ufmgqnjsnw344sqmf5svjh26y2khnmc89sv6788y3r207a8fn/evm`]}}}),Kx=L({id:4114,name:`Citrea Mainnet`,nativeCurrency:{name:`Citrea Bitcoin`,symbol:`cBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.citrea.xyz`]}},blockExplorers:{default:{name:`Citrea Explorer`,url:`https://explorer.mainnet.citrea.xyz`,apiUrl:`https://explorer.mainnet.citrea.xyz/api`}},testnet:!1}),qx=L({id:5115,name:`Citrea Testnet`,nativeCurrency:{name:`cBTC`,symbol:`cBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.citrea.xyz`]}},blockExplorers:{default:{name:`Citrea Explorer`,url:`https://explorer.testnet.citrea.xyz`,apiUrl:`https://explorer.testnet.citrea.xyz/api`}},testnet:!0}),Jx=L({id:61,name:`Ethereum Classic`,nativeCurrency:{decimals:18,name:`ETC`,symbol:`ETC`},rpcUrls:{default:{http:[`https://etc.rivet.link`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.com/etc/mainnet`}}}),Yx=1,Xx=L({...R,id:81224,name:`Codex`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.codex.xyz`]}},blockExplorers:{default:{name:`Codex Explorer`,url:`https://explorer.codex.xyz`,apiUrl:`https://explorer.codex.xyz/api`}},contracts:{...R.contracts,disputeGameFactory:{[Yx]:{address:`0x6A3855dc26e2beA8Ac73f82Cda79f3808B6C6F6C`}},portal:{[Yx]:{address:`0x52759C07A759c81BAab28AE1BE5A19e6450959bD`}},l1StandardBridge:{[Yx]:{address:`0xa6b1A05a592719B0C8a70c69eac114C48410aDE4`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},sourceId:Yx}),Zx=11155111,Qx=L({...R,id:812242,name:`Codex Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.codex-stg.xyz`]}},blockExplorers:{default:{name:`Codex Testnet Explorer`,url:`https://explorer.codex-stg.xyz`,apiUrl:`https://explorer.codex-stg.xyz/api`}},contracts:{...R.contracts,disputeGameFactory:{[Zx]:{address:`0x390e24E8324E56f13A8d48eB938b6f9De24CD205`}},portal:{[Zx]:{address:`0x037F161D12c829A9ca4742eEd9371830CA54fcB2`}},l1StandardBridge:{[Zx]:{address:`0xCf4df2bDB14C8FDB25FdacCEC10Ce5C4bAEDB3De`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},sourceId:Zx}),$x=L({id:112,name:`Coinbit Mainnet`,nativeCurrency:{name:`GIDR`,symbol:`GIDR`,decimals:18},rpcUrls:{default:{http:[`https://coinbit-rpc-mainnet.chain.sbcrypto.app`]}},blockExplorers:{default:{name:`Coinbit Explorer`,url:`https://coinbit-explorer.chain.sbcrypto.app`}},testnet:!1}),eS=L({id:52,name:`CoinEx Mainnet`,nativeCurrency:{name:`cet`,symbol:`cet`,decimals:18},rpcUrls:{default:{http:[`https://rpc.coinex.net`]}},blockExplorers:{default:{name:`CoinEx Explorer`,url:`https://www.coinex.net`}},testnet:!1}),tS=L({id:1030,name:`Conflux eSpace`,nativeCurrency:{name:`Conflux`,symbol:`CFX`,decimals:18},rpcUrls:{default:{http:[`https://evm.confluxrpc.com`],webSocket:[`wss://evm.confluxrpc.com/ws`]}},blockExplorers:{default:{name:`ConfluxScan`,url:`https://evm.confluxscan.org`}},contracts:{multicall3:{address:`0xEFf0078910f638cd81996cc117bccD3eDf2B072F`,blockCreated:68602935}}}),nS=L({id:71,name:`Conflux eSpace Testnet`,network:`cfx-espace-testnet`,testnet:!0,nativeCurrency:{name:`Conflux`,symbol:`CFX`,decimals:18},rpcUrls:{default:{http:[`https://evmtestnet.confluxrpc.com`],webSocket:[`wss://evmtestnet.confluxrpc.com/ws`]}},blockExplorers:{default:{name:`ConfluxScan`,url:`https://evmtestnet.confluxscan.org`}},contracts:{multicall3:{address:`0xEFf0078910f638cd81996cc117bccD3eDf2B072F`,blockCreated:117499050}}}),rS=L({id:1116,name:`Core Dao`,nativeCurrency:{decimals:18,name:`Core`,symbol:`CORE`},rpcUrls:{default:{http:[`https://rpc.coredao.org`]}},blockExplorers:{default:{name:`CoreDao`,url:`https://scan.coredao.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:11907934}},testnet:!1}),iS=L({id:1115,name:`Core Testnet`,nativeCurrency:{decimals:18,name:`tCore`,symbol:`TCORE`},rpcUrls:{default:{http:[`https://rpc.test.btcs.network`]}},blockExplorers:{default:{name:`Core Testnet`,url:`https://scan.test.btcs.network`,apiUrl:`https://api.test.btcs.network/api`}},contracts:{multicall3:{address:`0xCcddF20A1932537123C2E48Bd8e00b108B8f7569`,blockCreated:29350509}},testnet:!0}),aS=L({id:1114,name:`Core Testnet2`,nativeCurrency:{decimals:18,name:`tCore2`,symbol:`TCORE2`},rpcUrls:{default:{http:[`https://rpc.test2.btcs.network`]}},blockExplorers:{default:{name:`Core Testnet2`,url:`https://scan.test2.btcs.network`,apiUrl:`https://api.test2.btcs.network/api`}},contracts:{multicall3:{address:`0x3CB285ff3Cd5C7C7e570b1E7DE3De17A0f985e56`,blockCreated:3838600}},testnet:!0}),oS=L({id:21e6,name:`Corn`,nativeCurrency:{decimals:18,name:`Bitcorn`,symbol:`BTCN`},rpcUrls:{default:{http:[`https://21000000.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Corn Explorer`,url:`https://cornscan.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/21000000/etherscan/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3228}},sourceId:1}),sS=L({id:21000001,name:`Corn Testnet`,nativeCurrency:{decimals:18,name:`Bitcorn`,symbol:`BTCN`},rpcUrls:{default:{http:[`https://21000001.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Corn Testnet Explorer`,url:`https://testnet.cornscan.io`,apiUrl:`https://api.routescan.io/v2/network/testnet/evm/21000001/etherscan/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4886}},testnet:!0,sourceId:11155111}),cS=L({id:86608,name:`CpChain`,nativeCurrency:{decimals:18,name:`CpChain`,symbol:`CP`},rpcUrls:{default:{http:[`https://rpc.cpchain.com`]}},blockExplorers:{default:{name:`CpChain Explorer`,url:`https://explorer.cpchain.com`}},testnet:!1}),lS=L({id:44,name:`Crab Network`,nativeCurrency:{decimals:18,name:`Crab Network Native Token`,symbol:`CRAB`},rpcUrls:{default:{http:[`https://crab-rpc.darwinia.network`],webSocket:[`wss://crab-rpc.darwinia.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://crab-scan.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3032593}}}),uS=L({id:66665,name:`Creator`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.creatorchain.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.creatorchain.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0}),dS=L({id:102032,name:`Creditcoin Devnet`,nativeCurrency:{name:`Devnet CTC`,symbol:`devCTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cc3-devnet.creditcoin.network`],webSocket:[`wss://rpc.cc3-devnet.creditcoin.network/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin-devnet.blockscout.com`,apiUrl:`https://creditcoin3-dev.subscan.io`}},testnet:!0}),fS=L({id:102030,name:`Creditcoin`,nativeCurrency:{name:`Creditcoin`,symbol:`CTC`,decimals:18},rpcUrls:{default:{http:[`https://mainnet3.creditcoin.network`],webSocket:[`wss://mainnet3.creditcoin.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin.blockscout.com`,apiUrl:`https://creditcoin.blockscout.com/api`}},testnet:!1}),pS=L({id:102031,name:`Creditcoin Testnet`,nativeCurrency:{name:`Creditcoin Testnet`,symbol:`tCTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cc3-testnet.creditcoin.network`],webSocket:[`wss://rpc.cc3-testnet.creditcoin.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin-testnet.blockscout.com`,apiUrl:`https://creditcoin-testnet.blockscout.com/api`}},testnet:!0}),mS=L({id:25,name:`Cronos Mainnet`,nativeCurrency:{decimals:18,name:`Cronos`,symbol:`CRO`},rpcUrls:{default:{http:[`https://evm.cronos.org`]}},blockExplorers:{default:{name:`Cronos Explorer`,url:`https://explorer.cronos.org`,apiUrl:`https://explorer-api.cronos.org/mainnet/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1963112}}}),hS=L({id:338,name:`Cronos Testnet`,nativeCurrency:{decimals:18,name:`CRO`,symbol:`tCRO`},rpcUrls:{default:{http:[`https://evm-t3.cronos.org`]}},blockExplorers:{default:{name:`Cronos Explorer (Testnet)`,url:`https://explorer.cronos.org/testnet`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:10191251}},testnet:!0}),gS=L({id:388,name:`Cronos zkEVM Mainnet`,nativeCurrency:{decimals:18,name:`Cronos zkEVM CRO`,symbol:`zkCRO`},rpcUrls:{default:{http:[`https://mainnet.zkevm.cronos.org`]}},blockExplorers:{default:{name:`Cronos zkEVM (Mainnet) Chain Explorer`,url:`https://explorer.zkevm.cronos.org`}},contracts:{multicall3:{address:`0x06f4487d7c4a5983d2660db965cc6d2565e4cfaa`,blockCreated:72}}}),_S=L({id:282,name:`Cronos zkEVM Testnet`,nativeCurrency:{decimals:18,name:`Cronos zkEVM Test Coin`,symbol:`zkTCRO`},rpcUrls:{default:{http:[`https://testnet.zkevm.cronos.org`]}},blockExplorers:{default:{name:`Cronos zkEVM Testnet Explorer`,url:`https://explorer.zkevm.cronos.org/testnet`}},testnet:!0}),vS=L({id:3737,name:`Crossbell`,nativeCurrency:{decimals:18,name:`CSB`,symbol:`CSB`},rpcUrls:{default:{http:[`https://rpc.crossbell.io`]}},blockExplorers:{default:{name:`CrossScan`,url:`https://scan.crossbell.io`,apiUrl:`https://scan.crossbell.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:38246031}}}),yS=L({id:4158,name:`CrossFi Mainnet`,nativeCurrency:{decimals:18,name:`CrossFi`,symbol:`XFI`},rpcUrls:{default:{http:[`https://rpc.mainnet.ms`]}},blockExplorers:{default:{name:`CrossFi Blockchain Explorer`,url:`https://xfiscan.com`}},testnet:!1}),bS=L({id:33111,name:`Curtis`,nativeCurrency:{name:`ApeCoin`,symbol:`APE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.curtis.apechain.com`]}},blockExplorers:{default:{name:`Curtis Explorer`,url:`https://explorer.curtis.apechain.com`}},testnet:!0}),xS=L({id:7560,name:`Cyber`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://cyber.alt.technology`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://cyberscan.co`,apiUrl:`https://cyberscan.co/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),SS=L({id:111557560,name:`Cyber Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://cyber-testnet.alt.technology`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet.cyberscan.co`,apiUrl:`https://testnet.cyberscan.co/api`}},contracts:{multicall3:{address:`0xffc391F0018269d4758AEA1a144772E8FB99545E`,blockCreated:304545}},testnet:!0}),CS=L({id:824,name:`Daily Network Mainnet`,nativeCurrency:{decimals:18,name:`Daily`,symbol:`DLY`},rpcUrls:{default:{http:[`https://rpc.mainnet.dailycrypto.net`]}},blockExplorers:{default:{name:`Daily Mainnet Explorer`,url:`https://explorer.mainnet.dailycrypto.net`}},testnet:!1}),wS=L({id:825,name:`Daily Network Testnet`,nativeCurrency:{decimals:18,name:`Daily`,symbol:`DLY`},rpcUrls:{default:{http:[`https://rpc.testnet.dailycrypto.net`]}},blockExplorers:{default:{name:`Daily Testnet Explorer`,url:`https://explorer.testnet.dailycrypto.net`}},testnet:!0}),TS=L({id:46,name:`Darwinia Network`,nativeCurrency:{decimals:18,name:`RING`,symbol:`RING`},rpcUrls:{default:{http:[`https://rpc.darwinia.network`],webSocket:[`wss://rpc.darwinia.network`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:69420}}}),ES=L({id:55931,name:`Datahaven Testnet`,nativeCurrency:{decimals:18,name:`MOCK`,symbol:`MOCK`},rpcUrls:{default:{http:[`https://services.datahaven-testnet.network/testnet`],webSocket:[`wss://services.datahaven-testnet.network/testnet`]}},blockExplorers:{default:{name:`DhScan`,url:`https://testnet.dhscan.io/`,apiUrl:`https://testnet.dhscan.io/api-docs`}},contracts:{},testnet:!0}),DS=L({id:20240603,name:`DBK chain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.dbkchain.io`]}},blockExplorers:{default:{name:`DBK Chain Explorer`,url:`https://scan.dbkchain.io`}},testnet:!1}),OS=L({...R,id:0x9a697f88076c8,name:`Dchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://dchain-2716446429837000-1.jsonrpc.sagarpc.io`]}},blockExplorers:{default:{name:`Dchain Explorer`,url:`https://dchain-2716446429837000-1.sagaexplorer.io`,apiUrl:`https://api-dchain-2716446429837000-1.sagaexplorer.io/api`}},contracts:{...R.contracts}}),kS=L({...R,id:0x9a379ba03cf10,name:`Dchain Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://dchaintestnet-2713017997578000-1.jsonrpc.testnet.sagarpc.io`]}},blockExplorers:{default:{name:`Dchain Explorer`,url:`https://dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io`,apiUrl:`https://api-dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io/api`}},contracts:{...R.contracts}}),AS=L({id:1130,network:`defichain-evm`,name:`DeFiChain EVM Mainnet`,nativeCurrency:{name:`DeFiChain`,symbol:`DFI`,decimals:18},rpcUrls:{default:{http:[`https://eth.mainnet.ocean.jellyfishsdk.com`]}},blockExplorers:{default:{name:`DeFiScan`,url:`https://meta.defiscan.live`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:137852}}}),jS=L({id:1131,network:`defichain-evm-testnet`,name:`DeFiChain EVM Testnet`,nativeCurrency:{name:`DeFiChain`,symbol:`DFI`,decimals:18},rpcUrls:{default:{http:[`https://eth.testnet.ocean.jellyfishsdk.com`]}},blockExplorers:{default:{name:`DeFiScan`,url:`https://meta.defiscan.live/?network=TestNet`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:156462}},testnet:!0}),MS=L({id:666666666,name:`Degen`,nativeCurrency:{decimals:18,name:`Degen`,symbol:`DEGEN`},rpcUrls:{default:{http:[`https://rpc.degen.tips`],webSocket:[`wss://rpc.degen.tips`]}},blockExplorers:{default:{name:`Degen Chain Explorer`,url:`https://explorer.degen.tips`,apiUrl:`https://explorer.degen.tips/api/v2`}}}),NS=L({id:53935,name:`DFK Chain`,nativeCurrency:{decimals:18,name:`Jewel`,symbol:`JEWEL`},rpcUrls:{default:{http:[`https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc`]}},blockExplorers:{default:{name:`DFKSubnetScan`,url:`https://subnets.avax.network/defi-kingdoms`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14790551}}}),PS=L({id:15,name:`Diode Prenet`,nativeCurrency:{decimals:18,name:`DIODE`,symbol:`DIODE`},rpcUrls:{default:{http:[`https://prenet.diode.io:8443`],webSocket:[`wss://prenet.diode.io:8443/ws`]}},blockExplorers:{default:{name:`Diode Explorer`,url:`https://diode.io/prenet`}},testnet:!1}),FS=L({id:513100,name:`DisChain`,nativeCurrency:{decimals:18,name:`DIS`,symbol:`DIS`},rpcUrls:{default:{http:[`https://rpc.dischain.xyz`]}},blockExplorers:{default:{name:`DisChain Explorer`,url:`https://www.oklink.com/dis`}}}),IS=L({id:53457,name:`DODOchain Testnet`,nativeCurrency:{decimals:18,name:`DODO`,symbol:`DODO`},rpcUrls:{default:{http:[`https://dodochain-testnet.alt.technology`],webSocket:[`wss://dodochain-testnet.alt.technology/ws`]}},blockExplorers:{default:{name:`DODOchain Testnet (Sepolia) Explorer`,url:`https://testnet-scan.dodochain.com`}},testnet:!0}),LS=L({id:2e3,name:`Dogechain`,nativeCurrency:{decimals:18,name:`Wrapped Dogecoin`,symbol:`WDOGE`},rpcUrls:{default:{http:[`https://rpc.dogechain.dog`]}},blockExplorers:{default:{name:`DogeChainExplorer`,url:`https://explorer.dogechain.dog`,apiUrl:`https://explorer.dogechain.dog/api`}},contracts:{multicall3:{address:`0x68a8609a60a008EFA633dfdec592c03B030cC508`,blockCreated:25384031}}}),RS=L({id:97476,name:`Doma Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc-testnet.doma.xyz`]}},blockExplorers:{default:{name:`Doma Testnet Explorer`,url:`https://explorer-testnet.doma.xyz`}},testnet:!0}),zS=L({id:42026,name:`Donatuz`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.donatuz.com`]}},blockExplorers:{default:{name:`Donatuz Explorer`,url:`https://explorer.donatuz.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),BS=L({id:7979,name:`DOS Chain`,nativeCurrency:{decimals:18,name:`DOS Chain`,symbol:`DOS`},rpcUrls:{default:{http:[`https://main.doschain.com`]}},blockExplorers:{default:{name:`DOS Chain Explorer`,url:`https://doscan.io`,apiUrl:`https://api.doscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:161908}}}),VS=L({id:3939,name:`DOS Chain Testnet`,nativeCurrency:{decimals:18,name:`DOS Chain Testnet`,symbol:`DOS`},rpcUrls:{default:{http:[`https://test.doschain.com`]}},blockExplorers:{default:{name:`DOS Chain Testnet Explorer`,url:`https://test.doscan.io`,apiUrl:`https://api-test.doscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:69623}},testnet:!0}),HS=L({id:23451,name:`DreyerX Mainnet`,nativeCurrency:{name:`DreyerX`,symbol:`DRX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.dreyerx.com`]}},blockExplorers:{default:{name:`DreyerX Scan`,url:`https://scan.dreyerx.com`}}}),US=L({id:23452,name:`DreyerX Testnet`,nativeCurrency:{name:`DreyerX`,symbol:`DRX`,decimals:18},rpcUrls:{default:{http:[`http://testnet-rpc.dreyerx.com`]}},blockExplorers:{default:{name:`DreyerX Testnet Scan`,url:`https://testnet-scan.dreyerx.com`}},testnet:!0}),WS=L({id:555888,name:`DustBoy IoT`,nativeCurrency:{name:`Ether`,symbol:`DST`,decimals:18},rpcUrls:{default:{http:[`https://dustboy-rpc.jibl2.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://dustboy.jibl2.com`,apiUrl:`https://dustboy.jibl2.com/api`}},contracts:{multicall3:{address:`0xFFD34aa2C62B2D52E00A361e466C229788f4eD6a`,blockCreated:526569}},testnet:!1}),GS=L({id:1100,name:`Dymension`,nativeCurrency:{name:`DYM`,symbol:`DYM`,decimals:18},rpcUrls:{default:{http:[`https://dymension-evm-rpc.publicnode.com`],webSocket:[`wss://dymension-evm-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Dym FYI`,url:`https://dym.fyi`}},testnet:!1}),KS=L({id:5424,name:`edeXa`,nativeCurrency:{name:`edeXa`,symbol:`EDX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.edexa.network`]}},blockExplorers:{default:{name:`edeXa Explorer`,url:`https://explorer.edexa.network`,apiUrl:`https://explorer.edexa.network/api/v2`}}}),qS=L({id:1995,name:`edeXa Testnet`,nativeCurrency:{name:`edeXa`,symbol:`tEDX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.edexa.network`]}},blockExplorers:{default:{name:`edeXa Testnet Explorer`,url:`https://explorer.testnet.edexa.network`,apiUrl:`https://explorer.testnet.edexa.network/api/v2`}},testnet:!0}),JS=L({id:2026,name:`Edgeless Network`,nativeCurrency:{name:`Edgeless Wrapped ETH`,symbol:`EwETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.edgeless.network/http`],webSocket:[`wss://rpc.edgeless.network/ws`]}},blockExplorers:{default:{name:`Edgeless Explorer`,url:`https://explorer.edgeless.network`}}}),YS=L({id:202,name:`Edgeless Testnet`,nativeCurrency:{name:`Edgeless Wrapped ETH`,symbol:`EwETH`,decimals:18},rpcUrls:{default:{http:[`https://edgeless-testnet.rpc.caldera.xyz/http`],webSocket:[`wss://edgeless-testnet.rpc.caldera.xyz/ws`]}},blockExplorers:{default:{name:`Edgeless Testnet Explorer`,url:`https://testnet.explorer.edgeless.network`}}}),XS=L({id:2021,name:`Edgeware EdgeEVM Mainnet`,nativeCurrency:{decimals:18,name:`Edgeware`,symbol:`EDG`},rpcUrls:{default:{http:[`https://edgeware-evm.jelliedowl.net`]}},blockExplorers:{default:{name:`Edgscan by Bharathcoorg`,url:`https://edgscan.live`,apiUrl:`https://edgscan.live/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:18117872}}}),ZS=L({id:2022,name:`Beresheet BereEVM Testnet`,nativeCurrency:{decimals:18,name:`Testnet EDG`,symbol:`tEDG`},rpcUrls:{default:{http:[`https://beresheet-evm.jelliedowl.net`]}},blockExplorers:{default:{name:`Edgscan by Bharathcoorg`,url:`https://testnet.edgscan.live`,apiUrl:`https://testnet.edgscan.live/api`}}}),QS=L({id:41923,name:`EDU Chain`,nativeCurrency:{decimals:18,name:`EDU`,symbol:`EDU`},rpcUrls:{default:{http:[`https://rpc.edu-chain.raas.gelato.cloud`]}},blockExplorers:{default:{name:`EDU Chain Explorer`,url:`https://educhain.blockscout.com/`}},testnet:!1}),$S=L({id:656476,name:`EDU Chain Testnet`,nativeCurrency:{decimals:18,name:`EDU`,symbol:`EDU`},rpcUrls:{default:{http:[`https://rpc.open-campus-codex.gelato.digital/`],webSocket:[`wss://ws.open-campus-codex.gelato.digital`]}},blockExplorers:{default:{name:`EDU Chain Testnet Explorer`,url:`https://opencampus-codex.blockscout.com`,apiUrl:`https://opencampus-codex.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:15514133}},testnet:!0}),eC=L({id:20,name:`Elastos Smart Chain`,nativeCurrency:{name:`ELA`,symbol:`ELA`,decimals:18},rpcUrls:{default:{http:[`https://api2.elastos.io/eth`]}},blockExplorers:{default:{name:`Elastos Explorer`,url:`https://esc.elastos.io`}},testnet:!1}),tC=L({id:21,name:`Elastos Smart Chain Testnet`,nativeCurrency:{name:`tELA`,symbol:`tELA`,decimals:18},rpcUrls:{default:{http:[`https://api-testnet.elastos.io/eth`]}},blockExplorers:{default:{name:`Elastos Explorer`,url:`https://esc-testnet.elastos.io`}},testnet:!0}),nC=L({id:52014,name:`Electroneum Mainnet`,nativeCurrency:{name:`ETN`,symbol:`ETN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.electroneum.com`]}},blockExplorers:{default:{name:`Electroneum Block Explorer`,url:`https://blockexplorer.electroneum.com`}},testnet:!1}),rC=L({id:5201420,name:`Electroneum Testnet`,nativeCurrency:{name:`ETN`,symbol:`ETN`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.electroneum.com`]}},blockExplorers:{default:{name:`Electroneum Block Explorer`,url:`https://blockexplorer.thesecurityteam.rocks`}},testnet:!0}),iC=L({...R,id:1338,name:`Elysium Testnet`,nativeCurrency:{decimals:18,name:`LAVA`,symbol:`LAVA`},rpcUrls:{default:{http:[`https://elysium-test-rpc.vulcanforged.com`]}},blockExplorers:{default:{name:`Elysium testnet explorer`,url:`https://elysium-explorer.vulcanforged.com`}},testnet:!0}),aC=L({id:246,name:`Energy Mainnet`,nativeCurrency:{name:`EWT`,symbol:`EWT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.energyweb.org`]}},blockExplorers:{default:{name:`EnergyWeb Explorer`,url:`https://explorer.energyweb.org`}},testnet:!1}),oC=L({id:173,name:`ENI Mainnet`,nativeCurrency:{decimals:18,name:`ENI`,symbol:`ENI`},rpcUrls:{default:{http:[`https://rpc.eniac.network`]}},blockExplorers:{default:{name:`ENI Explorer`,url:`https://scan.eniac.network`}},testnet:!1}),sC=L({id:6912115,name:`ENI Testnet`,nativeCurrency:{decimals:18,name:`ENI Testnet Token`,symbol:`ENI`},rpcUrls:{default:{http:[`https://rpc-testnet.eniac.network`]}},blockExplorers:{default:{name:`ENI Testnet Explorer`,url:`https://scan-testnet.eniac.network`}},testnet:!0}),cC=L({id:119,name:`ENULS Mainnet`,nativeCurrency:{decimals:18,name:`NULS`,symbol:`NULS`},rpcUrls:{default:{http:[`https://evmapi2.nuls.io`]}},blockExplorers:{default:{name:`ENULS Explorer`,url:`https://evmscan.nuls.io`}},testnet:!1}),lC=L({id:7332,name:`Horizen EON`,nativeCurrency:{decimals:18,name:`ZEN`,symbol:`ZEN`},rpcUrls:{default:{http:[`https://eon-rpc.horizenlabs.io/ethv1`]}},blockExplorers:{default:{name:`EON Explorer`,url:`https://eon-explorer.horizenlabs.io`}},contracts:{}}),uC=L({id:17777,name:`EOS EVM`,nativeCurrency:{decimals:18,name:`EOS`,symbol:`EOS`},rpcUrls:{default:{http:[`https://api.evm.eosnetwork.com`]}},blockExplorers:{default:{name:`EOS EVM Explorer`,url:`https://explorer.evm.eosnetwork.com`,apiUrl:`https://explorer.evm.eosnetwork.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7943933}}}),dC=L({id:15557,name:`EOS EVM Testnet`,nativeCurrency:{decimals:18,name:`EOS`,symbol:`EOS`},rpcUrls:{default:{http:[`https://api.testnet.evm.eosnetwork.com`]}},blockExplorers:{default:{name:`EOS EVM Testnet Explorer`,url:`https://explorer.testnet.evm.eosnetwork.com`,apiUrl:`https://explorer.testnet.evm.eosnetwork.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:9067940}},testnet:!0}),fC=L({id:140,name:`Eteria`,nativeCurrency:{name:`Eteria`,symbol:`ERA`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.eteria.io/v1`]}},blockExplorers:{default:{name:`Eteria Explorer`,url:`https://explorer.eteria.io`,apiUrl:`https://explorer.eteria.io/api`}}}),pC=L({id:42793,name:`Etherlink`,blockTime:4830,nativeCurrency:{decimals:18,name:`Tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.mainnet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink`,url:`https://explorer.etherlink.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:33899}}}),mC=L({id:127823,name:`Etherlink Shadownet Testnet`,nativeCurrency:{decimals:18,name:`tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.shadownet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink Shadownet Testnet Explorer`,url:`https://shadownet.explorer.etherlink.com`}},testnet:!0}),hC=L({id:128123,name:`Etherlink Testnet`,nativeCurrency:{decimals:18,name:`Tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.ghostnet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink Testnet`,url:`https://testnet.explorer.etherlink.com`}},testnet:!0}),gC=L({id:183,name:`Ethernity`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.ethernitychain.io`]}},blockExplorers:{default:{name:`Ethernity Explorer`,url:`https://ernscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!1}),_C=L({id:20256789,name:`ETP Mainnet`,nativeCurrency:{decimals:18,name:`ETP Chain Native Token`,symbol:`ETP`},rpcUrls:{default:{http:[`https://rpc.etpscan.xyz`]}},blockExplorers:{default:{name:`ETP Scan`,url:`https://etpscan.xyz`}}}),vC=L({id:9001,name:`Evmos`,nativeCurrency:{decimals:18,name:`Evmos`,symbol:`EVMOS`},rpcUrls:{default:{http:[`https://eth.bd.evmos.org:8545`]}},blockExplorers:{default:{name:`Evmos Block Explorer`,url:`https://escan.live`}}}),yC=L({id:9e3,name:`Evmos Testnet`,nativeCurrency:{decimals:18,name:`Evmos`,symbol:`EVMOS`},rpcUrls:{default:{http:[`https://eth.bd.evmos.dev:8545`]}},blockExplorers:{default:{name:`Evmos Testnet Block Explorer`,url:`https://evm.evmos.dev/`}}}),bC=L({id:22052002,name:`Excelon Mainnet`,network:`XLON`,nativeCurrency:{decimals:18,name:`Excelon`,symbol:`xlon`},rpcUrls:{default:{http:[`https://edgewallet1.xlon.org`]}},blockExplorers:{default:{name:`Excelon explorer`,url:`https://explorer.excelon.io`}}}),xC=L({id:2,name:`Expanse Network`,nativeCurrency:{decimals:18,name:`EXP`,symbol:`EXP`},rpcUrls:{default:{http:[`https://node.expanse.tech`]}},blockExplorers:{default:{name:`Expanse Explorer`,url:`https://explorer.expanse.tech`}},testnet:!1}),SC=L({id:7200,name:`exSat Network`,nativeCurrency:{decimals:18,name:`BTC`,symbol:`BTC`},rpcUrls:{default:{http:[`https://evm.exsat.network`]}},blockExplorers:{default:{name:`exSat Explorer`,url:`https://scan.exsat.network`,apiUrl:`https://scan.exsat.network/api`}}}),CC=L({id:839999,name:`exSat Testnet`,nativeCurrency:{decimals:18,name:`BTC`,symbol:`BTC`},rpcUrls:{default:{http:[`https://evm-tst3.exsat.network`]}},blockExplorers:{default:{name:`exSat Explorer`,url:`https://scan-testnet.exsat.network`,apiUrl:`https://scan-testnet.exsat.network/api`}}}),wC=L({id:250,name:`Fantom`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://250.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`FTMScan`,url:`https://ftmscan.com`,apiUrl:`https://api.ftmscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:33001987}}}),TC=L({id:64240,name:`Fantom Sonic Open Testnet`,network:`fantom-sonic-testnet`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://rpcapi.sonic.fantom.network`]}},blockExplorers:{default:{name:`Fantom Sonic Open Testnet Explorer`,url:`https://public-sonic.fantom.network`}},testnet:!0}),EC=L({id:4002,name:`Fantom Testnet`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://rpc.testnet.fantom.network`]}},blockExplorers:{default:{name:`FTMScan`,url:`https://testnet.ftmscan.com`,apiUrl:`https://testnet.ftmscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:8328688}},testnet:!0}),DC=L({id:12306,name:`Fibo Chain`,nativeCurrency:{decimals:18,name:`fibo`,symbol:`FIBO`},rpcUrls:{default:{http:[`https://network.hzroc.art`]}},blockExplorers:{default:{name:`FiboScan`,url:`https://scan.fibochain.org`}}}),OC=L({id:314,name:`Filecoin Mainnet`,nativeCurrency:{decimals:18,name:`filecoin`,symbol:`FIL`},rpcUrls:{default:{http:[`https://api.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filfox`,url:`https://filfox.info/en`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3328594}}}),kC=L({id:314159,name:`Filecoin Calibration`,nativeCurrency:{decimals:18,name:`testnet filecoin`,symbol:`tFIL`},rpcUrls:{default:{http:[`https://api.calibration.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filscan`,url:`https://calibration.filscan.io`}},testnet:!0}),AC=L({id:3141,name:`Filecoin Hyperspace`,nativeCurrency:{decimals:18,name:`testnet filecoin`,symbol:`tFIL`},rpcUrls:{default:{http:[`https://api.hyperspace.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filfox`,url:`https://hyperspace.filfox.info/en`}},testnet:!0}),jC=L({id:253368190,name:`Flame`,network:`flame`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.flame.astria.org`],webSocket:[`wss://ws.flame.astria.org`]}},blockExplorers:{default:{name:`Flame Explorer`,url:`https://explorer.flame.astria.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6829148}}}),MC=L({id:14,name:`Flare Mainnet`,nativeCurrency:{decimals:18,name:`Flare`,symbol:`FLR`},rpcUrls:{default:{http:[`https://flare-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Flare Explorer`,url:`https://flare-explorer.flare.network`,apiUrl:`https://flare-explorer.flare.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3002461}}}),NC=L({id:114,name:`Flare Testnet Coston2`,nativeCurrency:{decimals:18,name:`Coston2 Flare`,symbol:`C2FLR`},rpcUrls:{default:{http:[`https://coston2-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Coston2 Explorer`,url:`https://coston2-explorer.flare.network`,apiUrl:`https://coston2-explorer.flare.network/api`}},testnet:!0}),PC=L({id:747,name:`Flow EVM Mainnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://mainnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Mainnet Explorer`,url:`https://evm.flowscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6205}},blockTime:800}),FC=L({id:646,name:`Flow EVM Previewnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://previewnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Previewnet Explorer`,url:`https://previewnet.flowdiver.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6205}}}),IC=L({id:545,name:`Flow EVM Testnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://testnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Flow Diver`,url:`https://evm-testnet.flowscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:137518}},testnet:!0,blockTime:800}),LC=L({id:9999999,name:`Fluence`,nativeCurrency:{name:`FLT`,symbol:`FLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.fluence.dev`],webSocket:[`wss://ws.mainnet.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.mainnet.fluence.dev`,apiUrl:`https://blockscout.mainnet.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:207583}}}),RC=L({id:123420000220,name:`Fluence Stage`,nativeCurrency:{name:`tFLT`,symbol:`tFLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stage.fluence.dev`],webSocket:[`wss://ws.stage.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.stage.fluence.dev`,apiUrl:`https://blockscout.stage.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:83227}},testnet:!0}),zC=L({id:52164803,name:`Fluence Testnet`,nativeCurrency:{name:`tFLT`,symbol:`tFLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.fluence.dev`],webSocket:[`wss://ws.testnet.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.testnet.fluence.dev`,apiUrl:`https://blockscout.testnet.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96424}},testnet:!0}),BC=L({id:20993,name:`Fluent Devnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.devnet.fluent.xyz`]}},blockExplorers:{default:{name:`Fluent Devnet Explorer`,url:`https://devnet.fluentscan.xyz`}},testnet:!0}),VC=L({id:20994,name:`Fluent Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.fluent.xyz`]}},blockExplorers:{default:{name:`Fluent Testnet Explorer`,url:`https://testnet.fluentscan.xyz`}},testnet:!0}),HC=1,UC=L({id:478,name:`Form Network`,nativeCurrency:{decimals:18,name:`Ethereum`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.form.network/http`],webSocket:[`wss://rpc.form.network/ws`]}},blockExplorers:{default:{name:`Form Explorer`,url:`https://explorer.form.network`}},contracts:{...R.contracts,addressManager:{[HC]:{address:`0x15c249E46A2F924C2dB3A1560CF86729bAD1f07B`}},l1CrossDomainMessenger:{[HC]:{address:`0xF333158DCCad1dF6C3F0a3aEe8BC31fA94d9eD5c`}},l2OutputOracle:{[HC]:{address:`0x4ccAAF69F41c5810cA875183648B577CaCf1F67E`}},portal:{[HC]:{address:`0x4E259Ee5F4136408908160dD32295A5031Fa426F`}},l1StandardBridge:{[HC]:{address:`0xdc20aA63D3DE59574E065957190D8f24e0F7B8Ba`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},sourceId:HC}),WC=L({id:984122,name:`Forma`,network:`forma`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.forma.art`],webSocket:[`wss://ws.forma.art`]}},blockExplorers:{default:{name:`Forma Explorer`,url:`https://explorer.forma.art`}},contracts:{multicall3:{address:`0xd53C6FFB123F7349A32980F87faeD8FfDc9ef079`,blockCreated:252705}}}),GC=11155111,KC=L({id:132902,name:`Form Testnet`,nativeCurrency:{decimals:18,name:`Ethereum`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia-rpc.form.network/http`],webSocket:[`wss://sepolia-rpc.form.network/ws`]}},blockExplorers:{default:{name:`Form Testnet Explorer`,url:`https://sepolia-explorer.form.network`}},contracts:{...R.contracts,addressManager:{[GC]:{address:`0xd5C38fa934f7fd7477D4800F4f38a1c5BFdF1373`}},l1CrossDomainMessenger:{[GC]:{address:`0x37A68565c4BE9700b3E3Ec60cC4416cAC3052FAa`}},l2OutputOracle:{[GC]:{address:`0x9eA2239E65a59EC9C7F1ED4C116dD58Da71Fc1e2`}},portal:{[GC]:{address:`0x60377e3cE15dF4CCA24c4beF076b60314240b032`}},l1StandardBridge:{[GC]:{address:`0xD4531f633942b2725896F47cD2aFd260b44Ab1F7`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0,sourceId:GC}),qC=L({id:80931,name:`Forta Chain`,nativeCurrency:{symbol:`FORT`,name:`FORT`,decimals:18},rpcUrls:{default:{http:[`https://rpc-forta-chain-8gj1qndmfc.t.conduit.xyz`]}},blockExplorers:{default:{name:`Forta Explorer`,url:`https://explorer.forta.org`}}}),JC=L({id:31337,name:`Foundry`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`],webSocket:[`ws://127.0.0.1:8545`]}}}),YC=1,XC=L({...R,id:252,name:`Fraxtal`,nativeCurrency:{name:`Frax`,symbol:`FRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.frax.com`]}},blockExplorers:{default:{name:`fraxscan`,url:`https://fraxscan.com`,apiUrl:`https://api.fraxscan.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[YC]:{address:`0x66CC916Ed5C6C2FA97014f7D1cD141528Ae171e4`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[YC]:{address:`0x36cb65c1967A0Fb0EEE11569C51C2f2aA1Ca6f6D`,blockCreated:19135323}},l1StandardBridge:{[YC]:{address:`0x34C0bD5877A5Ee7099D0f5688D65F4bB9158BDE2`,blockCreated:19135323}}},sourceId:YC}),ZC=17e3,QC=L({...R,id:2522,name:`Fraxtal Testnet`,nativeCurrency:{name:`Frax`,symbol:`FRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.frax.com`]}},blockExplorers:{default:{name:`fraxscan testnet`,url:`https://holesky.fraxscan.com`,apiUrl:`https://api-holesky.fraxscan.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[ZC]:{address:`0x715EA64DA13F4d0831ece4Ad3E8c1aa013167F32`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[ZC]:{address:`0xB9c64BfA498d5b9a8398Ed6f46eb76d90dE5505d`,blockCreated:318416}},l1StandardBridge:{[ZC]:{address:`0x0BaafC217162f64930909aD9f2B27125121d6332`,blockCreated:318416}}},sourceId:ZC}),$C=1,ew=L({...R,id:33979,name:`Funki`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.funkichain.com`]}},blockExplorers:{default:{name:`Funki Mainnet Explorer`,url:`https://funkiscan.io`}},contracts:{...R.contracts},sourceId:$C}),tw=11155111,nw=L({...R,id:3397901,network:`funkiSepolia`,name:`Funki Sepolia Sandbox`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://funki-testnet.alt.technology`]}},blockExplorers:{default:{name:`Funki Sepolia Sandbox Explorer`,url:`https://sepolia-sandbox.funkichain.com/`}},testnet:!0,contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1620204}},sourceId:tw}),rw=L({id:122,name:`Fuse`,nativeCurrency:{name:`Fuse`,symbol:`FUSE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.fuse.io`]}},blockExplorers:{default:{name:`Fuse Explorer`,url:`https://explorer.fuse.io`,apiUrl:`https://explorer.fuse.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:16146628}}}),iw=L({id:123,name:`Fuse Sparknet`,nativeCurrency:{name:`Spark`,symbol:`SPARK`,decimals:18},rpcUrls:{default:{http:[`https://rpc.fusespark.io`]}},blockExplorers:{default:{name:`Sparkent Explorer`,url:`https://explorer.fusespark.io`,apiUrl:`https://explorer.fusespark.io/api`}}}),aw=L({id:32659,name:`Fusion Mainnet`,nativeCurrency:{name:`Fusion`,symbol:`FSN`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.fusionnetwork.io`],webSocket:[`wss://mainnet.fusionnetwork.io`]}},blockExplorers:{default:{name:`FSNscan`,url:`https://fsnscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10441605}},testnet:!1}),ow=L({id:46688,name:`Fusion Testnet`,nativeCurrency:{name:`Fusion`,symbol:`FSN`,decimals:18},rpcUrls:{default:{http:[`https://testnet.fusionnetwork.io`],webSocket:[`wss://testnet.fusionnetwork.io`]}},blockExplorers:{default:{name:`FSNscan`,url:`https://testnet.fsnscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10428309}},testnet:!0}),sw=17e3,cw=L({...R,name:`Garnet Testnet`,testnet:!0,id:17069,sourceId:sw,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.garnetchain.com`],webSocket:[`wss://rpc.garnetchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.garnetchain.com`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[sw]:{address:`0x57ee40586fbE286AfC75E67cb69511A6D9aF5909`,blockCreated:1274684}},l2OutputOracle:{[sw]:{address:`0xCb8E7AC561b8EF04F2a15865e9fbc0766FEF569B`,blockCreated:1274684}},l1StandardBridge:{[sw]:{address:`0x09bcDd311FE398F80a78BE37E489f5D440DB95DE`,blockCreated:1274684}}}}),lw=L({id:86,name:`GateChain Mainnet`,nativeCurrency:{name:`GateChainToken`,symbol:`GT`,decimals:18},rpcUrls:{default:{http:[`https://evm.nodeinfo.cc`],webSocket:[`wss://evm-ws.gatenode.cc`]}},blockExplorers:{default:{name:`Gate Scan`,url:`https://www.gatescan.org`,apiUrl:`https://gatescan.org/api`}},testnet:!1}),uw=L({id:63157,name:`Geist Mainnet`,nativeCurrency:{decimals:18,name:`Aavegotchi GHST Token`,symbol:`GHST`},rpcUrls:{default:{http:[`https://geist-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://geist-mainnet.explorer.alchemy.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:660735}}}),dw=L({id:16507,name:`Genesys Mainnet`,nativeCurrency:{decimals:18,name:`GSYS`,symbol:`GSYS`},rpcUrls:{default:{http:[`https://rpc.genesys.network`]}},blockExplorers:{default:{name:`Genesys Explorer`,url:`https://gchainexplorer.genesys.network`}},testnet:!1}),fw=11155111,pw=L({...R,id:91342,network:`giwa-sepolia`,name:`GIWA Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://sepolia-rpc.giwa.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia-explorer.giwa.io`,apiUrl:`https://sepolia-explorer.giwa.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},disputeGameFactory:{[fw]:{address:`0x37347caB2afaa49B776372279143D71ad1f354F6`}},portal:{[fw]:{address:`0x956962C34687A954e611A83619ABaA37Ce6bC78A`}},l1StandardBridge:{[fw]:{address:`0x77b2ffc0F57598cAe1DB76cb398059cF5d10A7E7`}}},testnet:!0,sourceId:fw}),mw=L({...pw,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://sepolia-rpc-flashblocks.giwa.io`]}}}),hw=L({id:251,name:`Glide L1 Protocol XP`,nativeCurrency:{name:`GLXP`,symbol:`GLXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc-api.glideprotocol.xyz/l1-rpc`],webSocket:[`wss://rpc-api.glideprotocol.xyz/l1-rpc`]}},blockExplorers:{default:{name:`Glide Protocol Explore`,url:`https://blockchain-explorer.glideprotocol.xyz`}},testnet:!1}),gw=L({id:253,name:`Glide L2 Protocol XP`,nativeCurrency:{name:`GLXP`,symbol:`GLXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc-api.glideprotocol.xyz/l2-rpc`],webSocket:[`wss://rpc-api.glideprotocol.xyz/l2-rpc`]}},blockExplorers:{default:{name:`Glide Protocol Explore`,url:`https://blockchain-explorer.glideprotocol.xyz`}},testnet:!1}),_w=L({id:100,name:`Gnosis`,nativeCurrency:{decimals:18,name:`xDAI`,symbol:`XDAI`},blockTime:5e3,rpcUrls:{default:{http:[`https://rpc.gnosischain.com`],webSocket:[`wss://rpc.gnosischain.com/wss`]}},blockExplorers:{default:{name:`Gnosisscan`,url:`https://gnosisscan.io`,apiUrl:`https://api.gnosisscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:21022491}}}),vw=L({id:10200,name:`Gnosis Chiado`,nativeCurrency:{decimals:18,name:`Gnosis`,symbol:`xDAI`},blockTime:5e3,rpcUrls:{default:{http:[`https://rpc.chiadochain.net`],webSocket:[`wss://rpc.chiadochain.net/wss`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.chiadochain.net`,apiUrl:`https://blockscout.chiadochain.net/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4967313}},testnet:!0}),yw=L({id:2345,name:`GOAT`,nativeCurrency:{decimals:18,name:`Bitcoin`,symbol:`BTC`},rpcUrls:{default:{http:[`https://rpc.goat.network`]}},blockExplorers:{default:{name:`Goat Explorer`,url:`https://explorer.goat.network`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),bw=L({id:1663,name:`Horizen Gobi Testnet`,nativeCurrency:{decimals:18,name:`Test ZEN`,symbol:`tZEN`},rpcUrls:{default:{http:[`https://gobi-testnet.horizenlabs.io/ethv1`]}},blockExplorers:{default:{name:`Gobi Explorer`,url:`https://gobi-explorer.horizen.io`}},contracts:{},testnet:!0}),xw=L({id:60,name:`GoChain`,nativeCurrency:{decimals:18,name:`GO`,symbol:`GO`},rpcUrls:{default:{http:[`https://rpc.gochain.io`]}},blockExplorers:{default:{name:`GoChain Explorer`,url:`https://explorer.gochain.io`}},testnet:!1}),Sw=L({id:71402,name:`Godwoken Mainnet`,nativeCurrency:{decimals:18,name:`pCKB`,symbol:`pCKB`},rpcUrls:{default:{http:[`https://v1.mainnet.godwoken.io/rpc`]}},blockExplorers:{default:{name:`GW Scan`,url:`https://v1.gwscan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:15034}},testnet:!1}),Cw=L({id:5,name:`Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://5.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.etherscan.io`,apiUrl:`https://api-goerli.etherscan.io/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},ensUniversalResolver:{address:`0xfc4AC75C46C914aF5892d6d3eFFcebD7917293F1`,blockCreated:10339206},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6507670}},testnet:!0}),ww=L({id:440017,name:`Graphite Network`,nativeCurrency:{name:`Graphite`,symbol:`@G`,decimals:18},rpcUrls:{default:{http:[`https://anon-entrypoint-1.atgraphite.com`],webSocket:[`wss://ws-anon-entrypoint-1.atgraphite.com`]}},blockExplorers:{default:{name:`Graphite Spectre`,url:`https://main.atgraphite.com`,apiUrl:`https://api.main.atgraphite.com/api`}},testnet:!1}),Tw=L({id:54170,name:`Graphite Network Testnet`,nativeCurrency:{name:`Graphite`,symbol:`@G`,decimals:18},rpcUrls:{default:{http:[`https://anon-entrypoint-test-1.atgraphite.com`],webSocket:[`wss://ws-anon-entrypoint-test-1.atgraphite.com`]}},blockExplorers:{default:{name:`Graphite Testnet Spectre`,url:`https://test.atgraphite.com`,apiUrl:`https://api.test.atgraphite.com/api`}},testnet:!0}),Ew=L({id:1625,name:`Gravity Alpha Mainnet`,nativeCurrency:{name:`G`,symbol:`G`,decimals:18},rpcUrls:{default:{http:[`https://rpc.gravity.xyz`]}},blockExplorers:{default:{name:`Gravity Explorer`,url:`https://explorer.gravity.xyz`,apiUrl:`https://explorer.gravity.xyz/api`}},contracts:{multicall3:{address:`0xf8ac4BEB2F75d2cFFb588c63251347fdD629B92c`,blockCreated:16851}}}),Dw=L({id:43419,name:`Gunz Mainnet`,nativeCurrency:{name:`GUN`,symbol:`GUN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.gunzchain.io/ext/bc/2M47TxWHGnhNtq6pM5zPXdATBtuqubxn5EPFgFmEawCQr9WFML/rpc`]}},blockExplorers:{default:{name:`Gunz Explorer`,url:`https://gunzscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:70502}}}),Ow=L({id:260,name:`Guru Network Mainnet`,nativeCurrency:{name:`GURU Token`,symbol:`GURU`,decimals:18},rpcUrls:{default:{http:[`https://rpc-main.gurunetwork.ai`,`https://rpc.gurunetwork.ai/archive/260`]}},blockExplorers:{default:{name:`Guruscan`,url:`https://scan.gurunetwork.ai`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:271691}},testnet:!1}),kw=L({id:261,name:`Guru Network Testnet`,nativeCurrency:{name:`tGURU Token`,symbol:`tGURU`,decimals:18},rpcUrls:{default:{http:[`https://rpc-test.gurunetwork.ai`,`https://rpc.gurunetwork.ai/archive/261`]}},blockExplorers:{default:{name:`Guruscan`,url:`https://sepolia.gurunetwork.ai`}},testnet:!0}),Aw=L({id:5112,name:`Ham`,nativeCurrency:{decimals:18,name:`Ham`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.ham.fun`],webSocket:[`wss://rpc.ham.fun`]}},blockExplorers:{default:{name:`Ham Chain Explorer`,url:`https://explorer.ham.fun`,apiUrl:`https://explorer.ham.fun/api/v2`}}}),jw=L({id:216,name:`Happychain Testnet`,nativeCurrency:{symbol:`HAPPY`,name:`HAPPY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.happy.tech/http`],webSocket:[`wss://rpc.testnet.happy.tech/ws`]}},blockExplorers:{default:{name:`Happy Chain Testnet Explorer`,url:`https://explorer.testnet.happy.tech`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1}},testnet:!0}),Mw=L({id:11235,name:`HAQQ Mainnet`,nativeCurrency:{decimals:18,name:`Islamic Coin`,symbol:`ISLM`},rpcUrls:{default:{http:[`https://rpc.eth.haqq.network`]}},blockExplorers:{default:{name:`HAQQ Explorer`,url:`https://explorer.haqq.network`,apiUrl:`https://explorer.haqq.network/api`}}}),Nw=L({id:54211,name:`HAQQ Testedge 2`,nativeCurrency:{decimals:18,name:`Islamic Coin`,symbol:`ISLMT`},rpcUrls:{default:{http:[`https://rpc.eth.testedge2.haqq.network`]}},blockExplorers:{default:{name:`HAQQ Explorer`,url:`https://explorer.testedge2.haqq.network`,apiUrl:`https://explorer.testedge2.haqq.network/api`}}}),Pw=L({id:31337,name:`Hardhat`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),Fw=L({id:16666e5,name:`Harmony One`,nativeCurrency:{name:`Harmony`,symbol:`ONE`,decimals:18},rpcUrls:{default:{http:[`https://1666600000.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Harmony Explorer`,url:`https://explorer.harmony.one`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:24185753}}}),Iw=L({id:177,name:`HashKey Chain`,nativeCurrency:{decimals:18,name:`HashKey EcoPoints`,symbol:`HSK`},rpcUrls:{default:{http:[`https://mainnet.hsk.xyz`]}},blockExplorers:{default:{name:`HashKey Chain Explorer`,url:`https://hashkey.blockscout.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),Lw=L({id:133,name:`HashKey Chain Testnet`,nativeCurrency:{decimals:18,name:`HashKey EcoPoints`,symbol:`HSK`},rpcUrls:{default:{http:[`https://testnet.hsk.xyz`]}},blockExplorers:{default:{name:`HashKey Chain Testnet explorer`,url:`https://testnet-explorer.hsk.xyz`}},testnet:!0}),Rw=L({id:1523903251,name:`Haust Network Testnet`,nativeCurrency:{decimals:18,name:`HAUST`,symbol:`HAUST`},rpcUrls:{default:{http:[`https://rpc-testnet.haust.app`]}},blockExplorers:{default:{name:`Haust Network Testnet Explorer`,url:`https://explorer-testnet.haust.app`}},testnet:!0}),zw=L({id:295,name:`Hedera Mainnet`,network:`hedera-mainnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/mainnet`}},testnet:!1}),Bw=L({id:297,name:`Hedera Previewnet`,network:`hedera-previewnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://previewnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/previewnet`}},testnet:!0}),Vw=L({id:296,name:`Hedera Testnet`,network:`hedera-testnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://testnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/testnet`}},testnet:!0}),Hw=L({id:8668,name:`Hela Mainnet`,nativeCurrency:{name:`HLUSD`,symbol:`HLUSD`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.helachain.com`]}},blockExplorers:{default:{name:`Hela explorer`,url:`https://mainnet-blockexplorer.helachain.com`}},testnet:!1}),Uw=L({id:42e3,name:`Helios Testnet`,network:`helios-testnet`,nativeCurrency:{symbol:`HLS`,name:`Helios`,decimals:18},rpcUrls:{default:{http:[`https://testnet1.helioschainlabs.org`]}},blockExplorers:{default:{name:`Helios Testnet Explorer`,url:`https://explorer.helioschainlabs.org/`}},testnet:!0}),Ww=L({id:43111,name:`Hemi`,network:`Hemi`,blockTime:12e3,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hemi.network/rpc`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.hemi.xyz`}},testnet:!1}),Gw=L({id:743111,name:`Hemi Sepolia`,network:`Hemi Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.rpc.hemi.network/rpc`]}},blockExplorers:{default:{name:`Hemi Sepolia explorer`,url:`https://testnet.explorer.hemi.xyz`}},testnet:!0}),Kw=L({id:68414,name:`Henesys`,nativeCurrency:{name:`NEXPACE`,symbol:`NXPC`,decimals:18},rpcUrls:{default:{http:[`https://henesys-rpc.msu.io`]}},blockExplorers:{default:{name:`Avalanche Explorer`,url:`https://subnets.avax.network/henesys`}}}),qw=L({id:17e3,name:`Holesky`,nativeCurrency:{name:`Holesky Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://ethereum-holesky-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://holesky.etherscan.io`,apiUrl:`https://api-holesky.etherscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:77},ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:4295055}},testnet:!0}),Jw=L({id:560048,name:`Hoodi`,nativeCurrency:{name:`Hoodi Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hoodi.ethpandaops.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://hoodi.etherscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2589}},testnet:!0}),Yw=L({id:2651420,name:`Horizen Testnet`,network:`horizen-testnet`,nativeCurrency:{symbol:`Sepolia Ether`,name:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://horizen-testnet.rpc.caldera.xyz/http`]}},blockExplorers:{default:{name:`Horizen Testnet Caldera Explorer`,url:`https://horizen-testnet.explorer.caldera.xyz`}},testnet:!0}),Xw=L({id:269,name:`High Performance Blockchain`,nativeCurrency:{name:`HPB`,symbol:`HPB`,decimals:18},rpcUrls:{default:{http:[`https://hpbnode.com`]}},blockExplorers:{default:{name:`hpbScan`,url:`https://hscan.org`}},testnet:!1}),Zw=L({id:190415,name:`HPP Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.hpp.io`],webSocket:[`wss://mainnet.hpp.io`]}},blockExplorers:{default:{name:`HPP Mainnet Explorer`,url:`https://explorer.hpp.io`}},testnet:!1}),Qw=L({id:181228,name:`HPP Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.hpp.io`],webSocket:[`wss://testnet.hpp.io`]}},blockExplorers:{default:{name:`HPP Sepolia Explorer`,url:`https://sepolia-explorer.hpp.io`}},testnet:!0}),$w=L({id:12323,name:`Huddle01 dRTC Chain`,nativeCurrency:{name:`Ethereum`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://huddle01.calderachain.xyz/http`],webSocket:[`wss://huddle01.calderachain.xyz/ws`]}},blockExplorers:{default:{name:`Huddle01 Caldera Explorer`,url:`https://huddle01.calderaexplorer.xyz`,apiUrl:`https://huddle01.calderaexplorer.xyz/api`}},sourceId:42161}),eT=L({id:2524852,name:`Huddle01 dRTC Chain Testnet`,nativeCurrency:{name:`Ethereum`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://huddle-testnet.rpc.caldera.xyz/http`],webSocket:[`wss://huddle-testnet.rpc.caldera.xyz/ws`]}},blockExplorers:{default:{name:`Huddle01 Caldera Explorer`,url:`https://huddle-testnet.explorer.caldera.xyz`,apiUrl:`https://huddle-testnet.explorer.caldera.xyz/api`}},sourceId:421614}),tT=L({id:6985385,name:`Humanity`,nativeCurrency:{name:`H`,symbol:`H`,decimals:18},rpcUrls:{default:{http:[`https://humanity-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Humanity Mainnet Explorer`,url:`https://humanity-mainnet.explorer.alchemy.com`,apiUrl:`https://humanity-mainnet.explorer.alchemy.com/api`}},testnet:!1}),nT=L({id:7080969,name:`Humanity Testnet`,nativeCurrency:{name:`tHP`,symbol:`tHP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.humanity.org`]}},blockExplorers:{default:{name:`Humanity Testnet Explorer`,url:`https://humanity-testnet.explorer.alchemy.com`,apiUrl:`https://humanity-testnet.explorer.alchemy.com/api`}},testnet:!0}),rT=L({id:5234,name:`Humanode`,nativeCurrency:{name:`HMND`,symbol:`HMND`,decimals:18},rpcUrls:{default:{http:[`https://explorer-rpc-http.mainnet.stages.humanode.io`],webSocket:[`wss://explorer-rpc-ws.mainnet.stages.humanode.io`]}},blockExplorers:{default:{name:`Subscan`,url:`https://humanode.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4413097}}}),iT=L({id:14853,name:`Humanode Testnet 5`,nativeCurrency:{name:`HMND`,symbol:`HMND`,decimals:18},rpcUrls:{default:{http:[`https://explorer-rpc-http.testnet5.stages.humanode.io`],webSocket:[`wss://explorer-rpc-ws.testnet5.stages.humanode.io`]}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),aT=L({id:2911,name:`HYCHAIN`,nativeCurrency:{name:`HYTOPIA`,symbol:`TOPIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hychain.com/http`]}},blockExplorers:{default:{name:`HYCHAIN Explorer`,url:`https://explorer.hychain.com`}},testnet:!1}),oT=L({id:29112,name:`HYCHAIN Testnet`,nativeCurrency:{name:`HYTOPIA`,symbol:`TOPIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hychain.com/http`]}},blockExplorers:{default:{name:`HYCHAIN Explorer`,url:`https://testnet-rpc.hychain.com/http`}},testnet:!0}),sT=L({id:999,name:`HyperEVM`,nativeCurrency:{name:`HYPE`,symbol:`HYPE`,decimals:18},blockExplorers:{default:{name:`HyperEVMScan`,url:`https://hyperevmscan.io`}},rpcUrls:{default:{http:[`https://rpc.hyperliquid.xyz/evm`]}},testnet:!1}),cT=L({id:998,name:`Hyperliquid EVM Testnet`,nativeCurrency:{name:`HYPE`,symbol:`HYPE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hyperliquid-testnet.xyz/evm`]}},testnet:!0}),lT=L({id:73115,name:`ICB Network`,nativeCurrency:{decimals:18,name:`ICB Native Token`,symbol:`ICBX`},rpcUrls:{default:{http:[`https://rpc1-mainnet.icbnetwork.info`]}},blockExplorers:{default:{name:`ICB Explorer`,url:`https://icbscan.io`,apiUrl:`https://icbscan.io/api`}},testnet:!1}),uT=L({id:74,name:`IDChain Mainnet`,nativeCurrency:{decimals:18,name:`EIDI`,symbol:`EIDI`},rpcUrls:{default:{http:[`https://idchain.one/rpc`],webSocket:[`wss://idchain.one/ws`]}},blockExplorers:{default:{name:`IDChain Explorer`,url:`https://explorer.idchain.one`}},testnet:!1}),dT=L({id:13371,name:`Immutable zkEVM`,nativeCurrency:{decimals:18,name:`Immutable Coin`,symbol:`IMX`},rpcUrls:{default:{http:[`https://rpc.immutable.com`]}},blockExplorers:{default:{name:`Immutable Explorer`,url:`https://explorer.immutable.com`,apiUrl:`https://explorer.immutable.com/api`}},contracts:{multicall3:{address:`0x236bdA4589e44e6850f5aC6a74BfCa398a86c6c0`,blockCreated:4335972}}}),fT=L({id:13473,name:`Immutable zkEVM Testnet`,nativeCurrency:{decimals:18,name:`Immutable Coin`,symbol:`IMX`},rpcUrls:{default:{http:[`https://rpc.testnet.immutable.com`]}},blockExplorers:{default:{name:`Immutable Testnet Explorer`,url:`https://explorer.testnet.immutable.com/`}},contracts:{multicall3:{address:`0x2CC787Ed364600B0222361C4188308Fa8E68bA60`,blockCreated:5977391}},testnet:!0}),pT=L({id:2525,name:`inEVM Mainnet`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://mainnet.rpc.inevm.com/http`]}},blockExplorers:{default:{name:`inEVM Explorer`,url:`https://inevm.calderaexplorer.xyz`,apiUrl:`https://inevm.calderaexplorer.xyz/api/v2`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:118606}}}),mT=L({id:7233,name:`InitVerse Mainnet`,nativeCurrency:{decimals:18,name:`InitVerse`,symbol:`INI`},rpcUrls:{default:{http:[`https://rpc-mainnet.inichain.com`]}},blockExplorers:{default:{name:`InitVerseScan`,url:`https://www.iniscan.com`,apiUrl:`https://explorer-api.inichain.com/api`}},contracts:{multicall3:{address:`0x83466BE48A067115FFF91f7b892Ed1726d032e47`,blockCreated:2318}}}),hT=L({id:7234,name:`InitVerse Genesis Testnet`,nativeCurrency:{decimals:18,name:`InitVerse`,symbol:`INI`},rpcUrls:{default:{http:[`https://rpc-testnet.inichain.com`]}},blockExplorers:{default:{name:`InitVerseGenesisScan`,url:`https://genesis-testnet.iniscan.com`,apiUrl:`https://explorer-testnet-api.inichain.com/api`}},contracts:{multicall3:{address:`0x0cF32CBDd6c437331EA4f85ed2d881A5379B5a6F`,blockCreated:16361}},testnet:!0}),gT=L({id:1776,name:`Injective`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://sentry.evm-rpc.injective.network`],webSocket:[`wss://sentry.evm-ws.injective.network`]}},blockExplorers:{default:{name:`Injective Explorer`,url:`https://blockscout.injective.network`,apiUrl:`https://blockscout.injective.network/api`}},testnet:!1}),_T=L({id:1439,name:`Injective Testnet`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://k8s.testnet.json-rpc.injective.network`],webSocket:[`wss://k8s.testnet.ws.injective.network`]}},blockExplorers:{default:{name:`Injective Explorer`,url:`https://testnet.blockscout.injective.network`,apiUrl:`https://testnet.blockscout.injective.network/api`}},testnet:!0}),vT=1,yT=L({...R,id:57073,name:`Ink`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-gel.inkonchain.com`,`https://rpc-qnd.inkonchain.com`],webSocket:[`wss://rpc-gel.inkonchain.com`,`wss://rpc-qnd.inkonchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.inkonchain.com`,apiUrl:`https://explorer.inkonchain.com/api/v2`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},...R.contracts,disputeGameFactory:{[vT]:{address:`0x10d7b35078d3baabb96dd45a9143b94be65b12cd`}},portal:{[vT]:{address:`0x5d66c1782664115999c47c9fa5cd031f495d3e4f`}},l1StandardBridge:{[vT]:{address:`0x88ff1e5b602916615391f55854588efcbb7663f0`}}},testnet:!1,sourceId:vT}),bT=11155111,xT=L({...R,id:763373,name:`Ink Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-gel-sepolia.inkonchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer-sepolia.inkonchain.com/`,apiUrl:`https://explorer-sepolia.inkonchain.com/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},disputeGameFactory:{[bT]:{address:`0x860e626c700af381133d9f4af31412a2d1db3d5d`}},portal:{[bT]:{address:`0x5c1d29c6c9c8b0800692acc95d700bcb4966a1d7`}},l1StandardBridge:{[bT]:{address:`0x33f60714bbd74d62b66d79213c348614de51901c`}}},testnet:!0,sourceId:bT}),ST=L({id:8822,name:`IOTA EVM`,network:`iotaevm`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://json-rpc.evm.iotaledger.net`],webSocket:[`wss://ws.json-rpc.evm.iotaledger.net`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.evm.iota.org`,apiUrl:`https://explorer.evm.iota.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:25022}}}),CT=L({id:1075,name:`IOTA EVM Testnet`,network:`iotaevm-testnet`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://json-rpc.evm.testnet.iotaledger.net`],webSocket:[`wss://ws.json-rpc.evm.testnet.iotaledger.net`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.evm.testnet.iotaledger.net`,apiUrl:`https://explorer.evm.testnet.iotaledger.net/api`}},testnet:!0}),wT=L({id:4689,name:`IoTeX`,nativeCurrency:{decimals:18,name:`IoTeX`,symbol:`IOTX`},rpcUrls:{default:{http:[`https://babel-api.mainnet.iotex.io`],webSocket:[`wss://babel-api.mainnet.iotex.io`]}},blockExplorers:{default:{name:`IoTeXScan`,url:`https://iotexscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:22163670}}}),TT=L({id:4690,name:`IoTeX Testnet`,nativeCurrency:{decimals:18,name:`IoTeX`,symbol:`IOTX`},rpcUrls:{default:{http:[`https://babel-api.testnet.iotex.io`],webSocket:[`wss://babel-api.testnet.iotex.io`]}},blockExplorers:{default:{name:`IoTeXScan`,url:`https://testnet.iotexscan.io`}},contracts:{multicall3:{address:`0xb5cecD6894c6f473Ec726A176f1512399A2e355d`,blockCreated:24347592}},testnet:!0}),ET=L({id:8017,name:`iSunCoin Mainnet`,nativeCurrency:{decimals:18,name:`ISC`,symbol:`ISC`},rpcUrls:{default:{http:[`https://mainnet.isuncoin.com`]}},blockExplorers:{default:{name:`iSunCoin Explorer`,url:`https://baifa.io/app/chains/8017`}}}),DT=L({id:680,name:`Jasmy Chain`,network:`jasmyChain`,nativeCurrency:{name:`JasmyCoin`,symbol:`JASMY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.jasmychain.io`],webSocket:[`wss://rpc.jasmychain.io`]}},testnet:!1}),OT=L({id:681,name:`Jasmy Chain Testnet`,network:`jasmyChainTestnet`,nativeCurrency:{name:`JasmyCoin`,symbol:`JASMY`,decimals:18},rpcUrls:{default:{http:[`https://rpc_testnet.jasmychain.io`],webSocket:[`wss://rpc_testnet.jasmychain.io`]}},testnet:!0}),kT=L({id:8899,name:`JB Chain`,network:`jbc`,nativeCurrency:{name:`JBC`,symbol:`JBC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-l1.jibchain.net`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp-l1.jibchain.net`,apiUrl:`https://exp-l1.jibchain.net/api`}},contracts:{multicall3:{address:`0xc0C8C486D1466C57Efe13C2bf000d4c56F47CBdC`,blockCreated:2299048}},testnet:!1}),AT=L({id:88991,name:`Jibchain Testnet`,nativeCurrency:{name:`tJBC`,symbol:`tJBC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.jibchain.net`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp.testnet.jibchain.net`,apiUrl:`https://exp.testnet.jibchain.net/api`}},contracts:{multicall3:{address:`0xa1a858ad9041B4741e620355a3F96B3c78e70ecE`,blockCreated:32848}},testnet:!0}),jT=L({id:81,name:`Japan Open Chain Mainnet`,nativeCurrency:{decimals:18,name:`Japan Open Chain Token`,symbol:`JOC`},rpcUrls:{default:{http:[`https://rpc-1.japanopenchain.org:8545`,`https://rpc-2.japanopenchain.org:8545`,`https://rpc-3.japanopenchain.org`]}},blockExplorers:{default:{name:`Block Explorer`,url:`https://explorer.japanopenchain.org`}},testnet:!1}),MT=L({id:10081,name:`Japan Open Chain Testnet`,nativeCurrency:{decimals:18,name:`Japan Open Chain Testnet Token`,symbol:`JOCT`},rpcUrls:{default:{http:[`https://rpc-1.testnet.japanopenchain.org:8545`,`https://rpc-2.testnet.japanopenchain.org:8545`,`https://rpc-3.testnet.japanopenchain.org`]}},blockExplorers:{default:{name:`Testnet Block Explorer`,url:`https://explorer.testnet.japanopenchain.org`}},testnet:!0}),NT=L({id:5734951,name:`Jovay Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.jovay.io`]}},blockExplorers:{default:{name:`Jovay Explorer`,url:`https://explorer.jovay.io/l2`}},testnet:!1}),PT=L({id:2019775,name:`Jovay Sepolia Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.zan.top/public/jovay-testnet`]}},blockExplorers:{default:{name:`Jovay Testnet Explorer`,url:`https://sepolia-explorer.jovay.io/l2`}},testnet:!0}),FT=L({id:45003,name:`Juneo JUNE-Chain`,nativeCurrency:{decimals:18,name:`JUNE-Chain`,symbol:`JUNE`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/JUNE/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/2`,apiUrl:`https://juneoscan.io/chain/2/api`}}}),IT=L({id:45013,name:`Juneo BCH1-Chain`,nativeCurrency:{decimals:18,name:`Juneo BCH1-Chain`,symbol:`BCH1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/BCH1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/12`,apiUrl:`https://juneoscan.io/chain/12/api`}}}),LT=L({id:45004,name:`Juneo DAI1-Chain`,nativeCurrency:{decimals:18,name:`Juneo DAI1-Chain`,symbol:`DAI1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/DAI1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/5`,apiUrl:`https://juneoscan.io/chain/5/api`}}}),RT=L({id:45010,name:`Juneo DOGE1-Chain`,nativeCurrency:{decimals:18,name:`Juneo DOGE1-Chain`,symbol:`DOGE1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/DOGE1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/10`,apiUrl:`https://juneoscan.io/chain/10/api`}}}),zT=L({id:45011,name:`Juneo EUR1-Chain`,nativeCurrency:{decimals:18,name:`Juneo EUR1-Chain`,symbol:`EUR1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/EUR1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/6`,apiUrl:`https://juneoscan.io/chain/6/api`}}}),BT=L({id:45008,name:`Juneo GLD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo GLD1-Chain`,symbol:`GLD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/GLD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/8`,apiUrl:`https://juneoscan.io/chain/8/api`}}}),VT=L({id:45014,name:`Juneo LINK1-Chain`,nativeCurrency:{decimals:18,name:`Juneo LINK1-Chain`,symbol:`LINK1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/LINK1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/13`,apiUrl:`https://juneoscan.io/chain/13/api`}}}),HT=L({id:45009,name:`Juneo LTC1-Chain`,nativeCurrency:{decimals:18,name:`Juneo LTC1-Chain`,symbol:`LTC1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/LTC1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/11`,apiUrl:`https://juneoscan.io/chain/11/api`}}}),UT=L({id:45007,name:`Juneo mBTC1-Chain`,nativeCurrency:{decimals:18,name:`Juneo mBTC1-Chain`,symbol:`mBTC1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/mBTC1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/9`,apiUrl:`https://juneoscan.io/chain/9/api`}}}),WT=L({id:45012,name:`Juneo SGD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo SGD1-Chain`,symbol:`SGD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/SGD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/7`,apiUrl:`https://juneoscan.io/chain/7/api`}}}),GT=L({id:101003,name:`Socotra JUNE-Chain`,nativeCurrency:{decimals:18,name:`Socotra JUNE-Chain`,symbol:`JUNE`},rpcUrls:{default:{http:[`https://rpc.socotra-testnet.network/ext/bc/JUNE/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://socotra.juneoscan.io/chain/2`,apiUrl:`https://socotra.juneoscan.io/chain/2/api`}},testnet:!0}),KT=L({id:45006,name:`Juneo USD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo USD1-Chain`,symbol:`USD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/USD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/4`,apiUrl:`https://juneoscan.io/chain/4/api`}}}),qT=L({id:45005,name:`Juneo USDT1-Chain`,nativeCurrency:{decimals:18,name:`Juneo USDT1-Chain`,symbol:`USDT1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/USDT1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/3`,apiUrl:`https://juneoscan.io/chain/3/api`}}}),JT=L({id:8217,name:`Kaia`,nativeCurrency:{decimals:18,name:`Kaia`,symbol:`KAIA`},rpcUrls:{default:{http:[`https://public-en.node.kaia.io`]}},blockExplorers:{default:{name:`KaiaScan`,url:`https://kaiascan.io`,apiUrl:`https://api-cypress.klaytnscope.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96002415}}}),YT=L({id:1001,name:`Kairos Testnet`,network:`kairos`,nativeCurrency:{decimals:18,name:`Kairos KAIA`,symbol:`KAIA`},rpcUrls:{default:{http:[`https://public-en-kairos.node.kaia.io`]}},blockExplorers:{default:{name:`KaiaScan`,url:`https://kairos.kaiascan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:123390593}},testnet:!0}),XT=L({id:1802203764,name:`Kakarot Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.kakarot.org`]}},blockExplorers:{default:{name:`Kakarot Scan`,url:`https://sepolia.kakarotscan.org`}},testnet:!0}),ZT=L({id:920637907288165,name:`Kakarot Starknet Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.kakarot.org`]}},blockExplorers:{default:{name:`Kakarot Scan`,url:`https://sepolia.kakarotscan.org`}},testnet:!0}),QT=L({id:24,name:`KardiaChain Mainnet`,nativeCurrency:{name:`KAI`,symbol:`KAI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.kardiachain.io`]}},blockExplorers:{default:{name:`KardiaChain Explorer`,url:`https://explorer.kardiachain.io`}},testnet:!1}),$T=L({id:686,name:`Karura`,network:`karura`,nativeCurrency:{name:`Karura`,symbol:`KAR`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-karura.aca-api.network`],webSocket:[`wss://eth-rpc-karura.aca-api.network`]}},blockExplorers:{default:{name:`Karura Blockscout`,url:`https://blockscout.karura.network`,apiUrl:`https://blockscout.karura.network/api`}},testnet:!1}),eE=L({id:747474,name:`Katana`,network:`katana`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.katana.network`]}},blockExplorers:{default:{name:`katana explorer`,url:`https://katanascan.com`}},testnet:!1}),tE=L({id:2222,name:`Kava EVM`,network:`kava-mainnet`,nativeCurrency:{name:`Kava`,symbol:`KAVA`,decimals:18},rpcUrls:{default:{http:[`https://evm.kava.io`]}},blockExplorers:{default:{name:`Kava EVM Explorer`,url:`https://kavascan.com`,apiUrl:`https://kavascan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3661165}},testnet:!1}),nE=L({id:2221,name:`Kava EVM Testnet`,network:`kava-testnet`,nativeCurrency:{name:`Kava`,symbol:`KAVA`,decimals:18},rpcUrls:{default:{http:[`https://evm.testnet.kava.io`]}},blockExplorers:{default:{name:`Kava EVM Testnet Explorer`,url:`https://testnet.kavascan.com/`,apiUrl:`https://testnet.kavascan.com/api`}},contracts:{multicall3:{address:`0xDf1D724A7166261eEB015418fe8c7679BBEa7fd6`,blockCreated:7242179}},testnet:!0}),rE=L({id:321,name:`KCC Mainnet`,network:`KCC Mainnet`,nativeCurrency:{decimals:18,name:`KCS`,symbol:`KCS`},rpcUrls:{default:{http:[`https://kcc-rpc.com`]}},blockExplorers:{default:{name:`KCC Explorer`,url:`https://explorer.kcc.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11760430}},testnet:!1}),iE=L({id:1783,name:`KiiChain`,network:`kii-chain`,nativeCurrency:{name:`Kii`,symbol:`KII`,decimals:18},rpcUrls:{default:{http:[`https://json-rpc.kiivalidator.com`]}},blockExplorers:{default:{name:`KiiExplorer`,url:`https://explorer.kiichain.io`}}}),aE=L({id:1336,name:`Kii Testnet Oro`,network:`kii-testnet-oro`,nativeCurrency:{name:`Kii`,symbol:`KII`,decimals:18},rpcUrls:{default:{http:[`https://json-rpc.uno.sentry.testnet.v3.kiivalidator.com`]}},blockExplorers:{default:{name:`KiiExplorer`,url:`https://testnet.explorer.kiichain.io`}},testnet:!0}),oE=L({id:7887,name:`Kinto Mainnet`,network:`Kinto Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.kinto.xyz/http`]}},blockExplorers:{default:{name:`Kinto Explorer`,url:`https://explorer.kinto.xyz`}},testnet:!1}),sE=L({id:8217,name:`Klaytn`,nativeCurrency:{decimals:18,name:`Klaytn`,symbol:`KLAY`},rpcUrls:{default:{http:[`https://public-en-cypress.klaytn.net`]}},blockExplorers:{default:{name:`KlaytnScope`,url:`https://scope.klaytn.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96002415}}}),cE=L({id:1001,name:`Klaytn Baobab Testnet`,network:`klaytn-baobab`,nativeCurrency:{decimals:18,name:`Baobab Klaytn`,symbol:`KLAY`},rpcUrls:{default:{http:[`https://public-en-baobab.klaytn.net`]}},blockExplorers:{default:{name:`KlaytnScope`,url:`https://baobab.klaytnscope.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:123390593}},testnet:!0}),lE=L({id:701,name:`Koi Network`,nativeCurrency:{decimals:18,name:`Koi Network Native Token`,symbol:`KRING`},rpcUrls:{default:{http:[`https://koi-rpc.darwinia.network`],webSocket:[`wss://koi-rpc.darwinia.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://koi-scan.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:180001}},testnet:!0}),uE=L({id:255,name:`Kroma`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://api.kroma.network`]}},blockExplorers:{default:{name:`Kroma Explorer`,url:`https://blockscout.kroma.network`,apiUrl:`https://blockscout.kroma.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:16054868}},testnet:!1}),dE=L({id:2358,name:`Kroma Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://api.sepolia.kroma.network`]}},blockExplorers:{default:{name:`Kroma Sepolia Explorer`,url:`https://blockscout.sepolia.kroma.network`,apiUrl:`https://blockscout.sepolia.kroma.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:8900914}},testnet:!0}),fE=L({id:1983,name:`Krown`,nativeCurrency:{decimals:18,name:`Krown`,symbol:`KROWN`},rpcUrls:{default:{http:[`https://mainnet.krown.network`]}},blockExplorers:{default:{name:`Krown Explorer`,url:`https://explorer.krown.network`}},testnet:!1}),pE=L({id:12324,name:`L3X Protocol`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.l3x.com`],webSocket:[`wss://rpc-mainnet.l3x.com`]}},blockExplorers:{default:{name:`L3X Mainnet Explorer`,url:`https://explorer.l3x.com`,apiUrl:`https://explorer.l3x.com/api/v2`}},testnet:!1}),mE=L({id:12325,name:`L3X Protocol Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.l3x.com`],webSocket:[`wss://rpc-testnet.l3x.com`]}},blockExplorers:{default:{name:`L3X Testnet Explorer`,url:`https://explorer-testnet.l3x.com`,apiUrl:`https://explorer-testnet.l3x.com/api/v2`}},testnet:!0}),hE=L({id:360890,name:`LAVITA Mainnet`,nativeCurrency:{name:`vTFUEL`,symbol:`vTFUEL`,decimals:18},rpcUrls:{default:{http:[`https://tsub360890-eth-rpc.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`LAVITA Explorer`,url:`https://tsub360890-explorer.thetatoken.org`}},testnet:!1}),gE=L({id:232,name:`Lens`,nativeCurrency:{name:`GHO`,symbol:`GHO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.lens.xyz`]}},blockExplorers:{default:{name:`Lens Block Explorer`,url:`https://explorer.lens.xyz`,apiUrl:`https://explorer.lens.xyz/api`}}}),_E=L({id:37111,name:`Lens Testnet`,nativeCurrency:{name:`GRASS`,symbol:`GRASS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.lens.dev`],webSocket:[`wss://rpc.testnet.lens.dev/ws`]}},blockExplorers:{default:{name:`Lens Block Explorer`,url:`https://block-explorer.testnet.lens.dev`,apiUrl:`https://block-explorer-api.staging.lens.dev/api`}},testnet:!0}),vE=L({id:21363,name:`Lestnet`,nativeCurrency:{name:`Lestnet Ether`,symbol:`LETH`,decimals:18},rpcUrls:{default:{http:[`https://service.lestnet.org`]}},blockExplorers:{default:{name:`Lestnet Explorer`,url:`https://explore.lestnet.org`}},testnet:!0}),yE=L({id:1891,name:`LightLink Pegasus Testnet`,network:`lightlink-pegasus`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://replicator.pegasus.lightlink.io/rpc/v1`]}},blockExplorers:{default:{name:`LightLink Pegasus Explorer`,url:`https://pegasus.lightlink.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:127188532}},testnet:!0}),bE=L({id:1890,name:`LightLink Phoenix Mainnet`,network:`lightlink-phoenix`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://replicator.phoenix.lightlink.io/rpc/v1`]}},blockExplorers:{default:{name:`LightLink Phoenix Explorer`,url:`https://phoenix.lightlink.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:125499184}},testnet:!1});oa(),k(),Hp(),zu(),Ku(),td();async function xE(e,t){let{account:n=e.account}=t;if(!n)throw new Fm;let r=aa(n);try{let{accessList:n,blockNumber:i,blockTag:a,data:o,gas:s,gasPrice:c,maxFeePerGas:l,maxPriorityFeePerGas:u,nonce:d,to:f,value:p,...m}=t,h=(typeof i==`bigint`?O(i):void 0)||a;ed(t);let g=e.chain?.formatters?.transactionRequest?.format,_=(g||Hu)({...Ru(m,{format:g}),account:r,accessList:n,data:o,gas:s,gasPrice:c,maxFeePerGas:l,maxPriorityFeePerGas:u,nonce:d,to:f,value:p},`estimateGas`),{baseFeePerGas:v,gasLimit:y,priorityFeePerGas:b}=await e.request({method:`linea_estimateGas`,params:h?[_,h]:[_]});return{baseFeePerGas:BigInt(v),gasLimit:BigInt(y),priorityFeePerGas:BigInt(b)}}catch(n){throw Vp(n,{...t,account:r,chain:e.chain})}}var SE={fees:{estimateFeesPerGas:CE,async maxPriorityFeePerGas({block:e,client:t,request:n}){let r=await CE({block:e,client:t,multiply:e=>e,request:n,type:`eip1559`});return r?.maxPriorityFeePerGas?r.maxPriorityFeePerGas:null}}};async function CE({client:e,multiply:t,request:n,type:r}){try{let i=await xE(e,{...n,account:n?.account}),{priorityFeePerGas:a}=i,o=t(BigInt(i.baseFeePerGas))+a;return r===`legacy`?{gasPrice:o}:{maxFeePerGas:o,maxPriorityFeePerGas:a}}catch{return null}}var wE=L({...SE,id:59144,name:`Linea Mainnet`,blockTime:2e3,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.linea.build`],webSocket:[`wss://rpc.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://lineascan.build`,apiUrl:`https://api.lineascan.build/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:42},ensRegistry:{address:`0x50130b669B28C339991d8676FA73CF122a121267`,blockCreated:6682888},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:22222151}},ensTlds:[`.linea.eth`],testnet:!1}),TE=L({id:59140,name:`Linea Goerli Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.goerli.linea.build`],webSocket:[`wss://rpc.goerli.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.lineascan.build`,apiUrl:`https://api-goerli.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:498623}},testnet:!0}),EE=L({...SE,id:59141,name:`Linea Sepolia Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sepolia.linea.build`],webSocket:[`wss://rpc.sepolia.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.lineascan.build`,apiUrl:`https://api-sepolia.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:227427},ensRegistry:{address:`0x5B2636F0f2137B4aE722C01dd5122D7d3e9541f7`,blockCreated:2395094},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:17168484}},ensTlds:[`.linea.eth`],testnet:!0}),DE=L({id:59140,name:`Linea Goerli Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.goerli.linea.build`],webSocket:[`wss://rpc.goerli.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.lineascan.build`,apiUrl:`https://goerli.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:498623}},testnet:!0}),OE=1,kE=L({...R,id:1135,name:`Lisk`,network:`lisk`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.api.lisk.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.lisk.com`,apiUrl:`https://blockscout.lisk.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[OE]:{address:`0x0CF7D3706a27CCE2017aEB11E8a9c8b5388c282C`}},multicall3:{address:`0xA9d71E1dd7ca26F26e656E66d6AA81ed7f745bf0`},l2OutputOracle:{[OE]:{address:`0x113cB99283AF242Da0A0C54347667edF531Aa7d6`}},portal:{[OE]:{address:`0x26dB93F8b8b4f7016240af62F7730979d353f9A7`}},l1StandardBridge:{[OE]:{address:`0x2658723Bf70c7667De6B25F99fcce13A16D25d08`}}},sourceId:OE}),AE=11155111,jE=L({...R,id:4202,network:`lisk-sepolia`,name:`Lisk Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sepolia-api.lisk.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia-blockscout.lisk.com`,apiUrl:`https://sepolia-blockscout.lisk.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[AE]:{address:`0xA0E35F56C318DE1bD5D9ca6A94Fe7e37C5663348`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[AE]:{address:`0xe3d90F21490686Ec7eF37BE788E02dfC12787264`}},l1StandardBridge:{[AE]:{address:`0x1Fb30e446eA791cd1f011675E5F3f5311b70faF5`}}},testnet:!0,sourceId:AE}),ME=L({id:9496,name:`Load Alphanet`,nativeCurrency:{name:`Testnet LOAD`,symbol:`tLOAD`,decimals:18},rpcUrls:{default:{http:[`https://alphanet.load.network`]}},blockExplorers:{default:{name:`Load Alphanet Explorer`,url:`https://explorer.load.network`}},testnet:!0}),NE=L({id:1337,name:`Localhost`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),PE=L({id:15551,name:`LoopNetwork Mainnet`,nativeCurrency:{name:`LOOP`,symbol:`LOOP`,decimals:18},rpcUrls:{default:{http:[`https://api.mainnetloop.com`]}},blockExplorers:{default:{name:`LoopNetwork Blockchain Explorer`,url:`https://explorer.mainnetloop.com/`}},testnet:!1}),FE=L({id:42,network:`lukso`,name:`LUKSO`,nativeCurrency:{name:`LUKSO`,symbol:`LYX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.lukso.network`],webSocket:[`wss://ws-rpc.mainnet.lukso.network`]}},blockExplorers:{default:{name:`LUKSO Mainnet Explorer`,url:`https://explorer.execution.mainnet.lukso.network`,apiUrl:`https://api.explorer.execution.mainnet.lukso.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:468183}}}),IE=L({id:4201,name:`LUKSO Testnet`,nativeCurrency:{decimals:18,name:`LUKSO Testnet`,symbol:`LYXt`},rpcUrls:{default:{http:[`https://rpc.testnet.lukso.network`],webSocket:[`wss://ws-rpc.testnet.lukso.network`]}},blockExplorers:{default:{name:`LUKSO Testnet Explorer`,url:`https://explorer.execution.testnet.lukso.network`,apiUrl:`https://api.explorer.execution.testnet.lukso.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:605348}},testnet:!0}),LE=L({id:994873017,name:`Lumia Mainnet`,network:`LumiaMainnet`,nativeCurrency:{name:`Lumia`,symbol:`LUMIA`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.lumia.org`]}},blockExplorers:{default:{name:`Lumia Explorer`,url:`https://explorer.lumia.org/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3975939}},testnet:!1}),RE=L({id:1952959480,name:`Lumia Testnet`,network:`LumiaTestnet`,nativeCurrency:{name:`Lumia`,symbol:`LUMIA`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.lumia.org`]}},blockExplorers:{default:{name:`Lumia Testnet Explorer`,url:`https://testnet-explorer.lumia.org/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2235063}},testnet:!0}),zE=L({id:96370,name:`Lumoz`,nativeCurrency:{decimals:18,name:`Lumoz Token`,symbol:`MOZ`},rpcUrls:{default:{http:[`https://rpc.lumoz.org`]}},blockExplorers:{default:{name:`Lumoz Scan`,url:`https://scan.lumoz.info`}},testnet:!1}),BE=L({id:105363,name:`Lumoz Testnet`,nativeCurrency:{decimals:18,name:`Lumoz Testnet Token`,symbol:`MOZ`},rpcUrls:{default:{http:[`https://testnet-rpc.lumoz.org`]}},testnet:!0}),VE=L({id:1122,name:`LuxePorts`,network:`luxeports`,nativeCurrency:{name:`LuxePorts`,symbol:`LXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.luxeports.com`,`https://erpc.luxeports.com`],webSocket:[`wss://rpc.luxeports.com/ws`,`wss://erpc.luxeports.com/ws`]}},blockExplorers:{default:{name:`LXPScan`,url:`https://lxpscan.com`}},testnet:!1}),HE=L({id:721,name:`Lycan`,nativeCurrency:{decimals:18,name:`Lycan`,symbol:`LYC`},rpcUrls:{default:{http:[`https://rpc.lycanchain.com`,`https://us-east.lycanchain.com`,`https://us-west.lycanchain.com`,`https://eu-north.lycanchain.com`,`https://eu-west.lycanchain.com`,`https://asia-southeast.lycanchain.com`],webSocket:[`wss://rpc.lycanchain.com`,`wss://us-east.lycanchain.com`,`wss://us-west.lycanchain.com`,`wss://eu-north.lycanchain.com`,`wss://eu-west.lycanchain.com`,`wss://asia-southeast.lycanchain.com`]}},blockExplorers:{default:{name:`Lycan Explorer`,url:`https://explorer.lycanchain.com`}}}),UE=L({id:957,name:`Lyra Chain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.lyra.finance`]}},blockExplorers:{default:{name:`Lyra Explorer`,url:`https://explorer.lyra.finance`,apiUrl:`https://explorer.lyra.finance/api/v2`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1935198}}}),WE=L({id:1,name:`Ethereum`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:12e3,rpcUrls:{default:{http:[`https://eth.merkle.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://etherscan.io`,apiUrl:`https://api.etherscan.io/api`}},contracts:{ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:23085558},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),GE=L({id:595,name:`Mandala TC9`,network:`mandala`,nativeCurrency:{name:`Mandala`,symbol:`mACA`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-tc9.aca-staging.network`],webSocket:[`wss://eth-rpc-tc9.aca-staging.network`]}},blockExplorers:{default:{name:`Mandala Blockscout`,url:`https://blockscout.mandala.aca-staging.network`,apiUrl:`https://blockscout.mandala.aca-staging.network/api`}},testnet:!0}),KE=L({id:169,name:`Manta Pacific Mainnet`,network:`manta`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://pacific-rpc.manta.network/http`]}},blockExplorers:{default:{name:`Manta Explorer`,url:`https://pacific-explorer.manta.network`,apiUrl:`https://pacific-explorer.manta.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:332890}}}),qE=L({id:3441006,name:`Manta Pacific Sepolia Testnet`,network:`manta-sepolia`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://pacific-rpc.sepolia-testnet.manta.network/http`]}},blockExplorers:{default:{name:`Manta Sepolia Testnet Explorer`,url:`https://pacific-explorer.sepolia-testnet.manta.network`,apiUrl:`https://pacific-explorer.sepolia-testnet.manta.network/api`}},contracts:{multicall3:{address:`0xca54918f7B525C8df894668846506767412b53E3`,blockCreated:479584}},testnet:!0}),JE=L({id:3441005,name:`Manta Pacific Testnet`,network:`manta-testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://manta-testnet.calderachain.xyz/http`]}},blockExplorers:{default:{name:`Manta Testnet Explorer`,url:`https://pacific-explorer.testnet.manta.network`,apiUrl:`https://pacific-explorer.testnet.manta.network/api`}},contracts:{multicall3:{address:`0x211B1643b95Fe76f11eD8880EE810ABD9A4cf56C`,blockCreated:419915}},testnet:!0}),YE=L({id:5e3,name:`Mantle`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Explorer`,url:`https://mantlescan.xyz/`,apiUrl:`https://api.mantlescan.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:304717}}}),XE=L({id:5003,name:`Mantle Sepolia Testnet`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.sepolia.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Testnet Explorer`,url:`https://explorer.sepolia.mantle.xyz/`,apiUrl:`https://explorer.sepolia.mantle.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4584012}},testnet:!0}),ZE=L({id:5001,name:`Mantle Testnet`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.testnet.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Testnet Explorer`,url:`https://explorer.testnet.mantle.xyz`,apiUrl:`https://explorer.testnet.mantle.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:561333}},testnet:!0}),QE=L({id:5887,name:`MANTRA DuKong EVM Testnet`,nativeCurrency:{decimals:18,name:`MANTRA`,symbol:`MANTRA`},rpcUrls:{default:{http:[`https://evm.dukong.mantrachain.io`]}},blockExplorers:{default:{name:`MANTRAScan`,url:`https://mantrascan.io/dukong`}},testnet:!0}),$E=L({id:5888,name:`MANTRA EVM`,nativeCurrency:{decimals:18,name:`MANTRA`,symbol:`MANTRA`},rpcUrls:{default:{http:[`https://evm.mantrachain.io`],webSocket:[`https://evm.mantrachain.io/ws`]}},blockExplorers:{default:{name:`MANTRA Blockscout Explorer`,url:`https://blockscout.mantrascan.io`}}}),eD=L({id:22776,name:`MAP Protocol`,nativeCurrency:{decimals:18,name:`MAPO`,symbol:`MAPO`},rpcUrls:{default:{http:[`https://rpc.maplabs.io`]}},blockExplorers:{default:{name:`MAPO Scan`,url:`https://maposcan.io`}},testnet:!1}),tD=L({id:698,name:`Matchain`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.matchain.io`]}},blockExplorers:{default:{name:`Matchain Scan`,url:`https://matchscan.io`}}}),nD=L({id:699,name:`Matchain Testnet`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.matchain.io`]}},blockExplorers:{default:{name:`Matchain Scan`,url:`https://testnet.matchscan.io`}},testnet:!0}),rD=L({id:29548,name:`MCH Verse`,nativeCurrency:{name:`Oasys`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.oasys.mycryptoheroes.net`]}},blockExplorers:{default:{name:`MCH Verse Explorer`,url:`https://explorer.oasys.mycryptoheroes.net`,apiUrl:`https://explorer.oasys.mycryptoheroes.net/api`}},testnet:!1}),iD=L({id:4326,blockTime:1e3,name:`MegaETH`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.megaeth.com/rpc`],webSocket:[`wss://mainnet.megaeth.com/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://mega.etherscan.io`,apiUrl:`https://api.etherscan.io/v2/api`},blockscout:{name:`Etherscan`,url:`https://mega.etherscan.io`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),aD=L({id:6343,blockTime:1e3,name:`MegaETH Testnet`,nativeCurrency:{name:`MegaETH Testnet Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://carrot.megaeth.com/rpc`],webSocket:[`wss://carrot.megaeth.com/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://testnet-mega.etherscan.io`,apiUrl:`https://api.etherscan.io/v2/api`},blockscout:{name:`Blockscout`,url:`https://megaeth-testnet-v2.blockscout.com`,apiUrl:`https://megaeth-testnet-v2.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),oD=L({id:7078815900,name:`Mekong Pectra Devnet`,nativeCurrency:{name:`eth`,symbol:`eth`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mekong.ethpandaops.io`]}},blockExplorers:{default:{name:`Block Explorer`,url:`https://explorer.mekong.ethpandaops.io`}},testnet:!0}),sD=L({id:333000333,name:`Meld`,nativeCurrency:{decimals:18,name:`Meld`,symbol:`MELD`},rpcUrls:{default:{http:[`https://rpc-1.meld.com`]}},blockExplorers:{default:{name:`MELDscan`,url:`https://meldscan.io`}},contracts:{multicall3:{address:`0x769ee5a8e82c15c1b6e358f62ac8eb6e3abe8dc5`,blockCreated:360069}}}),cD=L({id:4352,name:`MemeCore`,nativeCurrency:{decimals:18,name:`M`,symbol:`M`},rpcUrls:{default:{http:[`https://rpc.memecore.net`],webSocket:[`wss://ws.memecore.net`]}},blockExplorers:{default:{name:`MemeCore Explorer`,url:`https://memecorescan.io`,apiUrl:`https://api.memecorescan.io/api`},okx:{name:`MemeCore Explorer`,url:`https://web3.okx.com/explorer/memecore`},memecore:{name:`MemeCore Explorer`,url:`https://blockscout.memecore.com`,apiUrl:`https://blockscout.memecore.com/api`}}}),lD=L({id:43521,name:`Formicarium`,nativeCurrency:{decimals:18,name:`M`,symbol:`M`},rpcUrls:{default:{http:[`https://rpc.formicarium.memecore.net`],webSocket:[`wss://ws.formicarium.memecore.net`]}},blockExplorers:{default:{name:`MemeCore Testnet Explorer`,url:`https://formicarium.memecorescan.io`},okx:{name:`MemeCore Testnet Explorer`,url:`https://web3.okx.com/explorer/formicarium-testnet`},memecore:{name:`MemeCore Testnet Explorer`,url:`https://formicarium.blockscout.memecore.com`,apiUrl:`https://formicarium.blockscout.memecore.com/api`}},testnet:!0}),uD=L({id:4200,name:`Merlin`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.merlinchain.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://scan.merlinchain.io`,apiUrl:`https://scan.merlinchain.io/api`}}}),dD=L({id:4203,name:`Merlin Erigon Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-erigon-rpc.merlinchain.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet-erigon-scan.merlinchain.io`,apiUrl:`https://testnet-erigon-scan.merlinchain.io/api`}},testnet:!0}),fD=L({id:571,name:`MetaChain Mainnet`,nativeCurrency:{name:`Metatime Coin`,symbol:`MTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.metatime.com`]}},blockExplorers:{default:{name:`MetaExplorer`,url:`https://explorer.metatime.com`}},contracts:{multicall3:{address:`0x0000000000000000000000000000000000003001`,blockCreated:0}}}),pD=L({id:1453,name:`MetaChain Istanbul`,nativeCurrency:{name:`Metatime Coin`,symbol:`MTC`,decimals:18},rpcUrls:{default:{http:[`https://istanbul-rpc.metachain.dev`]}},blockExplorers:{default:{name:`MetaExplorer`,url:`https://istanbul-explorer.metachain.dev`}},contracts:{multicall3:{address:`0x0000000000000000000000000000000000003001`,blockCreated:0}},testnet:!0}),mD=L({id:11,name:`Metadium Network`,nativeCurrency:{decimals:18,name:`META`,symbol:`META`},rpcUrls:{default:{http:[`https://api.metadium.com/prod`]}},blockExplorers:{default:{name:`Metadium Explorer`,url:`https://explorer.metadium.com`}},testnet:!1}),hD=1,gD=L({...R,id:1750,name:`Metal L2`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.metall2.com`],webSocket:[`wss://rpc.metall2.com`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.metall2.com`,apiUrl:`https://explorer.metall2.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[hD]:{address:`0x3B1F7aDa0Fcc26B13515af752Dd07fB1CAc11426`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},portal:{[hD]:{address:`0x3F37aBdE2C6b5B2ed6F8045787Df1ED1E3753956`}},l1StandardBridge:{[hD]:{address:`0x6d0f65D59b55B0FEC5d2d15365154DcADC140BF3`}}},sourceId:hD}),_D=L({id:82,name:`Meter`,nativeCurrency:{decimals:18,name:`MTR`,symbol:`MTR`},rpcUrls:{default:{http:[`https://rpc.meter.io`]}},blockExplorers:{default:{name:`MeterScan`,url:`https://scan.meter.io`}}}),vD=L({id:83,name:`Meter Testnet`,nativeCurrency:{decimals:18,name:`MTR`,symbol:`MTR`},rpcUrls:{default:{http:[`https://rpctest.meter.io`]}},blockExplorers:{default:{name:`MeterTestnetScan`,url:`https://scan-warringstakes.meter.io`}}}),yD=L({id:1088,name:`Metis`,nativeCurrency:{decimals:18,name:`Metis`,symbol:`METIS`},rpcUrls:{default:{http:[`https://metis.rpc.hypersync.xyz`,`https://metis-pokt.nodies.app`,`https://api.blockeden.xyz/metis/67nCBdZQSH9z3YqDDjdm`,`https://metis-andromeda.rpc.thirdweb.com`,`https://metis-andromeda.gateway.tenderly.co`,`https://metis.api.onfinality.io/public`,`https://andromeda.metis.io/?owner=1088`,`https://metis-mainnet.public.blastapi.io`],webSocket:[`wss://metis-rpc.publicnode.com`,`wss://metis.drpc.org`]}},blockExplorers:{default:{name:`Metis Explorer`,url:`https://explorer.metis.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/1088/etherscan/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2338552}}}),bD=L({id:599,name:`Metis Goerli`,nativeCurrency:{decimals:18,name:`Metis Goerli`,symbol:`METIS`},rpcUrls:{default:{http:[`https://goerli.gateway.metisdevops.link`]}},blockExplorers:{default:{name:`Metis Goerli Explorer`,url:`https://goerli.explorer.metisdevops.link`,apiUrl:`https://goerli.explorer.metisdevops.link/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1006207}}}),xD=L({id:59902,name:`Metis Sepolia`,nativeCurrency:{decimals:18,name:`Test Metis`,symbol:`tMETIS`},rpcUrls:{default:{http:[`https://sepolia.metisdevops.link`,`https://metis-sepolia-rpc.publicnode.com`,`https://metis-sepolia.gateway.tenderly.co`],webSocket:[`wss://metis-sepolia-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Metis Sepolia Explorer`,url:`https://sepolia-explorer.metisdevops.link`,apiUrl:`https://sepolia-explorer.metisdevops.link/api-docs`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:224185}}}),SD=L({id:7518,name:`MEVerse Chain Mainnet`,nativeCurrency:{decimals:18,name:`MEVerse`,symbol:`MEV`},rpcUrls:{default:{http:[`https://rpc.meversemainnet.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://www.meversescan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:86881340}}}),CD=L({id:4759,name:`MEVerse Chain Testnet`,nativeCurrency:{decimals:18,name:`MEVerse`,symbol:`MEV`},rpcUrls:{default:{http:[`https://rpc.meversetestnet.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://testnet.meversescan.io/`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:64371115}},testnet:!0}),wD=L({id:185,name:`Mint Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mintchain.io`]}},blockExplorers:{default:{name:`Mintchain explorer`,url:`https://explorer.mintchain.io`}},testnet:!1}),TD=L({id:1686,name:`Mint Sepolia Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.mintchain.io`]}},blockExplorers:{default:{name:`Mintchain Testnet explorer`,url:`https://testnet-explorer.mintchain.io`}},testnet:!0}),ED=L({id:124832,name:`Mitosis Testnet`,nativeCurrency:{name:`MITO`,symbol:`MITO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.mitosis.org`]}},blockExplorers:{default:{name:`Mitosis testnet explorer`,url:`https://testnet.mitosiscan.xyz`}},testnet:!0}),DD=1,OD=L({...R,id:34443,name:`Mode Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.mode.network`]}},blockExplorers:{default:{name:`Modescan`,url:`https://modescan.io`}},contracts:{...R.contracts,disputeGameFactory:{[DD]:{address:`0x6f13EFadABD9269D6cEAd22b448d434A1f1B433E`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2465882},l2OutputOracle:{[DD]:{address:`0x4317ba146D4933D889518a3e5E11Fe7a53199b04`}},portal:{[DD]:{address:`0x8B34b14c7c7123459Cf3076b8Cb929BE097d0C07`}},l1StandardBridge:{[DD]:{address:`0x735aDBbE72226BD52e818E7181953f42E3b0FF21`}}},sourceId:DD}),kD=11155111,AD=L({...R,id:919,name:`Mode Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.mode.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia.explorer.mode.network`,apiUrl:`https://sepolia.explorer.mode.network/api`}},contracts:{...R.contracts,l2OutputOracle:{[kD]:{address:`0x2634BD65ba27AB63811c74A63118ACb312701Bfa`,blockCreated:3778393}},portal:{[kD]:{address:`0x320e1580effF37E008F1C92700d1eBa47c1B23fD`,blockCreated:3778395}},l1StandardBridge:{[kD]:{address:`0xbC5C679879B2965296756CD959C3C739769995E2`,blockCreated:3778392}},multicall3:{address:`0xBAba8373113Fb7a68f195deF18732e01aF8eDfCF`,blockCreated:3019007}},testnet:!0,sourceId:kD}),jD=L({id:143,name:`Monad`,blockTime:400,nativeCurrency:{name:`Monad`,symbol:`MON`,decimals:18},rpcUrls:{default:{http:[`https://rpc.monad.xyz`,`https://rpc1.monad.xyz`],webSocket:[`wss://rpc.monad.xyz`,`wss://rpc1.monad.xyz`]}},blockExplorers:{default:{name:`MonadVision`,url:`https://monadvision.com`},monadscan:{name:`Monadscan`,url:`https://monadscan.com`,apiUrl:`https://api.monadscan.com/api`}},testnet:!1,contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:9248132}}}),MD=L({id:10143,name:`Monad Testnet`,blockTime:400,nativeCurrency:{name:`Testnet MON Token`,symbol:`MON`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.monad.xyz`]}},blockExplorers:{default:{name:`Monad Testnet explorer`,url:`https://testnet.monadexplorer.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:251449}},testnet:!0}),ND=L({id:1287,name:`Moonbase Alpha`,nativeCurrency:{decimals:18,name:`DEV`,symbol:`DEV`},rpcUrls:{default:{http:[`https://rpc.api.moonbase.moonbeam.network`],webSocket:[`wss://wss.api.moonbase.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonbase.moonscan.io`,apiUrl:`https://moonbase.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1850686}},testnet:!0}),PD=L({id:1284,name:`Moonbeam`,nativeCurrency:{decimals:18,name:`GLMR`,symbol:`GLMR`},rpcUrls:{default:{http:[`https://rpc.api.moonbeam.network`],webSocket:[`wss://wss.api.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonscan.io`,apiUrl:`https://api-moonbeam.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:609002}},testnet:!1}),FD=L({id:1281,name:`Moonbeam Development Node`,nativeCurrency:{decimals:18,name:`DEV`,symbol:`DEV`},rpcUrls:{default:{http:[`http://127.0.0.1:9944`],webSocket:[`wss://127.0.0.1:9944`]}}}),ID=L({id:1285,name:`Moonriver`,nativeCurrency:{decimals:18,name:`MOVR`,symbol:`MOVR`},rpcUrls:{default:{http:[`https://rpc.api.moonriver.moonbeam.network`],webSocket:[`wss://wss.api.moonriver.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonriver.moonscan.io`,apiUrl:`https://api-moonriver.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1597904}},testnet:!1}),LD=L({id:2818,name:`Morph`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.morphl2.io`],webSocket:[`wss://rpc.morphl2.io:8443`]}},blockExplorers:{default:{name:`Morph Explorer`,url:`https://explorer.morphl2.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3654913}},testnet:!1}),RD=L({id:2810,name:`Morph Holesky`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-quicknode-holesky.morphl2.io`],webSocket:[`wss://rpc-quicknode-holesky.morphl2.io`]}},blockExplorers:{default:{name:`Morph Holesky Explorer`,url:`https://explorer-holesky.morphl2.io`,apiUrl:`https://explorer-api-holesky.morphl2.io/api?`}},testnet:!0}),zD=L({id:2710,name:`Morph Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.morphl2.io`]}},blockExplorers:{default:{name:`Morph Testnet Explorer`,url:`https://explorer-testnet.morphl2.io`,apiUrl:`https://explorer-api-testnet.morphl2.io/api`}},testnet:!0}),BD=L({id:5551,name:`Nahmii 2 Mainnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://l2.nahmii.io`]}},blockExplorers:{default:{name:`Nahmii 2 Explorer`,url:`https://explorer.n2.nahmii.io`}},testnet:!1}),VD=L({id:22222,name:`Nautilus Mainnet`,nativeCurrency:{name:`ZBC`,symbol:`ZBC`,decimals:9},rpcUrls:{default:{http:[`https://api.nautilus.nautchain.xyz`]}},blockExplorers:{default:{name:`NautScan`,url:`https://nautscan.com`}}}),HD=L({id:397,name:`NEAR Protocol`,nativeCurrency:{decimals:18,name:`NEAR`,symbol:`NEAR`},rpcUrls:{default:{http:[`https://eth-rpc.mainnet.near.org`]}},blockExplorers:{default:{name:`NEAR Explorer`,url:`https://eth-explorer.near.org`}},testnet:!1}),UD=L({id:398,name:`NEAR Protocol Testnet`,nativeCurrency:{decimals:18,name:`NEAR`,symbol:`NEAR`},rpcUrls:{default:{http:[`https://eth-rpc.testnet.near.org`]}},blockExplorers:{default:{name:`NEAR Explorer`,url:`https://eth-explorer-testnet.near.org`}},testnet:!0}),WD=L({id:245022926,name:`Neon EVM DevNet`,nativeCurrency:{name:`NEON`,symbol:`NEON`,decimals:18},rpcUrls:{default:{http:[`https://devnet.neonevm.org`]}},blockExplorers:{default:{name:`Neonscan`,url:`https://devnet.neonscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:205206112}},testnet:!0}),GD=L({id:245022934,network:`neonMainnet`,name:`Neon EVM MainNet`,nativeCurrency:{name:`NEON`,symbol:`NEON`,decimals:18},rpcUrls:{default:{http:[`https://neon-proxy-mainnet.solana.p2p.org`]}},blockExplorers:{default:{name:`Neonscan`,url:`https://neonscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:206545524}},testnet:!1}),KD=L({id:47763,name:`Neo X Mainnet`,nativeCurrency:{name:`Gas`,symbol:`GAS`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-1.rpc.banelabs.org`,`https://mainnet-2.rpc.banelabs.org`]}},blockExplorers:{default:{name:`Neo X - Explorer`,url:`https://xexplorer.neo.org`}},testnet:!1}),qD=L({id:12227332,name:`Neo X Testnet T4`,nativeCurrency:{name:`Gas`,symbol:`GAS`,decimals:18},rpcUrls:{default:{http:[`https://testnet.rpc.banelabs.org/`]}},blockExplorers:{default:{name:`neox-scan`,url:`https://xt4scan.ngd.network`}},testnet:!0}),JD=L({id:1012,name:`Newton`,nativeCurrency:{name:`Newton`,symbol:`NEW`,decimals:18},rpcUrls:{default:{http:[`https://global.rpc.mainnet.newtonproject.org`]}},blockExplorers:{default:{name:`NewFi explorer`,url:`https://explorer.newtonproject.org/`}},testnet:!1}),YD=L({id:4242,name:`Nexi`,nativeCurrency:{name:`Nexi`,symbol:`NEXI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.chain.nexi.technology`]}},blockExplorers:{default:{name:`NexiScan`,url:`https://www.nexiscan.com`,apiUrl:`https://www.nexiscan.com/api`}},contracts:{multicall3:{address:`0x0277A46Cc69A57eE3A6C8c158bA874832F718B8E`,blockCreated:25770160}}}),XD=L({id:240,name:`Nexilix Smart Chain`,nativeCurrency:{decimals:18,name:`Nexilix`,symbol:`NEXILIX`},rpcUrls:{default:{http:[`https://rpcurl.pos.nexilix.com`]}},blockExplorers:{default:{name:`NexilixScan`,url:`https://scan.nexilix.com`}},contracts:{multicall3:{address:`0x58381c8e2BF9d0C2C4259cA14BdA9Afe02831244`,blockCreated:74448}}}),ZD=L({id:6900,name:`Nibiru`,nativeCurrency:{decimals:18,name:`NIBI`,symbol:`NIBI`},rpcUrls:{default:{http:[`https://evm-rpc.nibiru.fi`]}},blockExplorers:{default:{name:`NibiScan`,url:`https://nibiscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:19587573}}}),QD=L({id:200024,name:`Nitrograph Testnet`,testnet:!0,rpcUrls:{default:{http:[`https://rpc-testnet.nitrograph.foundation`]}},nativeCurrency:{name:`Nitro`,symbol:`NOS`,decimals:18},blockExplorers:{default:{url:`https://explorer-testnet.nitrograph.foundation`,name:`Nitrograph Explorer`}}}),$D=L({id:166,name:`Nomina`,nativeCurrency:{name:`Nomina`,symbol:`NOM`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.nomina.io`],webSocket:[`wss://mainnet.nomina.io`]}},blockExplorers:{default:{name:`Nomina Explorer`,url:`https://nomscan.io`}},testnet:!1}),eO=L({id:4090,network:`oasis-testnet`,name:`Oasis Testnet`,nativeCurrency:{name:`Fasttoken`,symbol:`FTN`,decimals:18},rpcUrls:{default:{http:[`https://rpc1.oasis.bahamutchain.com`]}},blockExplorers:{default:{name:`Ftnscan`,url:`https://oasis.ftnscan.com`,apiUrl:`https://oasis.ftnscan.com/api`}},testnet:!0}),tO=L({id:248,name:`Oasys`,nativeCurrency:{name:`Oasys`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.oasys.games`]}},blockExplorers:{default:{name:`OasysScan`,url:`https://scan.oasys.games`,apiUrl:`https://scan.oasys.games/api`}}}),nO=L({id:911867,name:`Odyssey Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://odyssey.ithaca.xyz`]}},blockExplorers:{default:{name:`Odyssey Explorer`,url:`https://odyssey-explorer.ithaca.xyz`,apiUrl:`https://odyssey-explorer.ithaca.xyz/api`}},testnet:!0}),rO=L({id:66,name:`OKC`,nativeCurrency:{decimals:18,name:`OKT`,symbol:`OKT`},rpcUrls:{default:{http:[`https://exchainrpc.okex.org`]}},blockExplorers:{default:{name:`oklink`,url:`https://www.oklink.com/okc`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10364792}}}),iO=L({id:311,name:`Omax Mainnet`,nativeCurrency:{decimals:18,name:`OMAX`,symbol:`OMAX`},rpcUrls:{default:{http:[`https://mainapi.omaxray.com`]}},blockExplorers:{default:{name:`Omax Explorer`,url:`https://omaxscan.com`}},testnet:!1}),aO=L({id:166,name:`Omni`,nativeCurrency:{name:`Omni`,symbol:`OMNI`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.omni.network`],webSocket:[`wss://mainnet.omni.network`]}},blockExplorers:{default:{name:`OmniScan`,url:`https://omniscan.network`}},testnet:!1}),oO=L({id:164,name:`Omni Omega`,nativeCurrency:{name:`Omni`,symbol:`OMNI`,decimals:18},rpcUrls:{default:{http:[`https://omega.omni.network`],webSocket:[`wss://omega.omni.network`]}},blockExplorers:{default:{name:`Omega OmniScan`,url:`https://omega.omniscan.network/`}},testnet:!0}),sO=L({id:309075,name:`One World Chain Mainnet`,nativeCurrency:{decimals:18,name:`OWCT`,symbol:`OWCT`},rpcUrls:{default:{http:[`https://mainnet-rpc.oneworldchain.org`]}},blockExplorers:{default:{name:`One World Explorer`,url:`https://mainnet.oneworldchain.org`}},testnet:!1}),cO=L({id:9700,name:`OORT MainnetDev`,nativeCurrency:{decimals:18,name:`OORT`,symbol:`OORT`},rpcUrls:{default:{http:[`https://dev-rpc.oortech.com`]}},blockExplorers:{default:{name:`OORT MainnetDev Explorer`,url:`https://dev-scan.oortech.com`}}}),lO=56,uO=L({id:204,name:`opBNB`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://opbnb-mainnet-rpc.bnbchain.org`]}},blockExplorers:{default:{name:`opBNB (BSCScan)`,url:`https://opbnb.bscscan.com`,apiUrl:`https://api-opbnb.bscscan.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:512881},l2OutputOracle:{[lO]:{address:`0x153CAB79f4767E2ff862C94aa49573294B13D169`}},portal:{[lO]:{address:`0x1876EA7702C0ad0C6A2ae6036DE7733edfBca519`}},l1StandardBridge:{[lO]:{address:`0xF05F0e4362859c3331Cb9395CBC201E3Fa6757Ea`}}},sourceId:lO}),dO=97,fO=L({id:5611,name:`opBNB Testnet`,nativeCurrency:{decimals:18,name:`tBNB`,symbol:`tBNB`},rpcUrls:{default:{http:[`https://opbnb-testnet-rpc.bnbchain.org`]}},blockExplorers:{default:{name:`opbnbscan`,url:`https://testnet.opbnbscan.com`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3705108},l2OutputOracle:{[dO]:{address:`0xFf2394Bb843012562f4349C6632a0EcB92fC8810`}},portal:{[dO]:{address:`0x4386C8ABf2009aC0c263462Da568DD9d46e52a31`}},l1StandardBridge:{[dO]:{address:`0x677311Fd2cCc511Bbc0f581E8d9a07B033D5E840`}}},testnet:!0,sourceId:dO}),pO=L({id:1612,name:`OpenLedger`,nativeCurrency:{name:`Open`,symbol:`OPEN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.openledger.xyz`]}},blockExplorers:{default:{name:`OpenLedger Explorer`,url:`https://scan.openledger.xyz`}},testnet:!1}),mO=1,hO=L({...R,id:10,name:`OP Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.optimism.io`]}},blockExplorers:{default:{name:`Optimism Explorer`,url:`https://optimistic.etherscan.io`,apiUrl:`https://api-optimistic.etherscan.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[mO]:{address:`0xe5965Ab5962eDc7477C8520243A95517CD252fA9`}},l2OutputOracle:{[mO]:{address:`0xdfe97868233d1aa22e815a266982f2cf17685a27`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4286263},portal:{[mO]:{address:`0xbEb5Fc579115071764c7423A4f12eDde41f106Ed`}},l1StandardBridge:{[mO]:{address:`0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1`}}},sourceId:mO}),gO=5,_O=L({...R,id:420,name:`Optimism Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli.optimism.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli-optimism.etherscan.io`,apiUrl:`https://goerli-optimism.etherscan.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[gO]:{address:`0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:49461},portal:{[gO]:{address:`0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383`}},l1StandardBridge:{[gO]:{address:`0x636Af16bf2f682dD3109e60102b8E1A089FedAa8`}}},testnet:!0,sourceId:gO}),vO=11155111,yO=L({...R,id:11155420,name:`OP Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.optimism.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://optimism-sepolia.blockscout.com`,apiUrl:`https://optimism-sepolia.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[vO]:{address:`0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1`}},l2OutputOracle:{[vO]:{address:`0x90E9c4f8a994a250F6aEfd61CAFb4F2e895D458F`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1620204},portal:{[vO]:{address:`0x16Fc5058F25648194471939df75CF27A2fdC48BC`}},l1StandardBridge:{[vO]:{address:`0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1`}}},testnet:!0,sourceId:vO}),bO=L({id:62050,name:`Optopia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.optopia.ai`]}},blockExplorers:{default:{name:`Optopia Explorer`,url:`https://scan.optopia.ai`}},testnet:!1}),xO=L({id:62049,name:`Optopia Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.optopia.ai`]}},blockExplorers:{default:{name:`Optopia Explorer`,url:`https://scan-testnet.optopia.ai`}},testnet:!0}),SO=L({id:291,name:`Orderly`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.orderly.network`]}},blockExplorers:{default:{name:`Orderly Explorer`,url:`https://explorer.orderly.network`}},testnet:!1}),CO=L({id:4460,name:`Orderly Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://l2-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz`]}},blockExplorers:{default:{name:`Orderly Explorer`,url:`https://explorerl2new-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz`}},testnet:!0}),wO=L({id:41144114,name:`Otim Devnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`http://devnet.otim.xyz`]}},contracts:{batchInvoker:{address:`0x5FbDB2315678afecb367f032d93F642f64180aa3`}}}),TO=L({id:11297108109,name:`Palm`,nativeCurrency:{decimals:18,name:`PALM`,symbol:`PALM`},rpcUrls:{default:{http:[`https://palm-mainnet.public.blastapi.io`],webSocket:[`wss://palm-mainnet.public.blastapi.io`]}},blockExplorers:{default:{name:`Chainlens`,url:`https://palm.chainlens.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15429248}}}),EO=L({id:11297108099,name:`Palm Testnet`,nativeCurrency:{decimals:18,name:`PALM`,symbol:`PALM`},rpcUrls:{default:{http:[`https://palm-mainnet.public.blastapi.io`],webSocket:[`wss://palm-mainnet.public.blastapi.io`]}},blockExplorers:{default:{name:`Chainlens`,url:`https://palm.chainlens.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15429248}},testnet:!0}),DO=L({id:420420422,name:`Paseo PassetHub`,nativeCurrency:{name:`PAS`,symbol:`PAS`,decimals:18},rpcUrls:{default:{http:[`https://testnet-passet-hub-eth-rpc.polkadot.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout-passet-hub.parity-testnet.parity.io`}},testnet:!0}),OO=L({id:3338,name:`Peaq`,nativeCurrency:{decimals:18,name:`peaq`,symbol:`PEAQ`},rpcUrls:{default:{http:[`https://quicknode1.peaq.xyz`,`https://quicknode2.peaq.xyz`,`https://quicknode3.peaq.xyz`],webSocket:[`wss://quicknode1.peaq.xyz`,`wss://quicknode2.peaq.xyz`,`wss://quicknode3.peaq.xyz`]}},blockExplorers:{default:{name:`Subscan`,url:`https://peaq.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3566354}}}),kO=1,AO=L({id:424,network:`pgn`,name:`PGN`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.publicgoods.network`]}},blockExplorers:{default:{name:`PGN Explorer`,url:`https://explorer.publicgoods.network`,apiUrl:`https://explorer.publicgoods.network/api`}},contracts:{l2OutputOracle:{[kO]:{address:`0x9E6204F750cD866b299594e2aC9eA824E2e5f95c`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3380209},portal:{[kO]:{address:`0xb26Fd985c5959bBB382BAFdD0b879E149e48116c`}},l1StandardBridge:{[kO]:{address:`0xD0204B9527C1bA7bD765Fa5CCD9355d38338272b`}}},formatters:Iy,sourceId:kO}),jO=11155111,MO=L({id:58008,network:`pgn-testnet`,name:`PGN`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.publicgoods.network`]}},blockExplorers:{default:{name:`PGN Testnet Explorer`,url:`https://explorer.sepolia.publicgoods.network`,apiUrl:`https://explorer.sepolia.publicgoods.network/api`}},contracts:{l2OutputOracle:{[jO]:{address:`0xD5bAc3152ffC25318F848B3DD5dA6C85171BaEEe`}},portal:{[jO]:{address:`0xF04BdD5353Bb0EFF6CA60CfcC78594278eBfE179`}},l1StandardBridge:{[jO]:{address:`0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3754925}},formatters:Iy,sourceId:jO,testnet:!0}),NO=L({id:13381,name:`Phoenix Blockchain`,nativeCurrency:{name:`Phoenix`,symbol:`PHX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.phoenixplorer.com`]}},blockExplorers:{default:{name:`Phoenixplorer`,url:`https://phoenixplorer.com`,apiUrl:`https://phoenixplorer.com/api`}},contracts:{multicall3:{address:`0x498cF757a575cFF2c2Ed9f532f56Efa797f86442`,blockCreated:5620192}}}),PO=L({id:7070,name:`Planq Mainnet`,nativeCurrency:{decimals:18,name:`PLQ`,symbol:`PLQ`},rpcUrls:{default:{http:[`https://planq-rpc.nodies.app`,`https://evm-rpc.planq.network`,`https://jsonrpc.planq.nodestake.top`]}},blockExplorers:{default:{name:`Planq Explorer`,url:`https://evm.planq.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:8470015}},testnet:!1}),FO=L({id:9745,name:`Plasma`,blockTime:1e3,nativeCurrency:{name:`Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plasma.to`]}},blockExplorers:{default:{name:`PlasmaScan`,url:`https://plasmascan.to`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),IO=L({id:9747,name:`Plasma Devnet`,nativeCurrency:{name:`Devnet Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://devnet-rpc.plasma.to`]}},testnet:!0,contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),LO=L({id:9746,name:`Plasma Testnet`,nativeCurrency:{name:`Testnet Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plasma.to`]}},blockExplorers:{default:{name:`RouteScan`,url:`https://testnet.plasmascan.to`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),RO=L({...xy,id:1612127,name:`PlayFi Albireo Testnet`,network:`albireo`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://albireo-rpc.playfi.ai`],webSocket:[`wss://albireo-rpc-ws.playfi.ai/ws`]}},blockExplorers:{default:{name:`PlayFi Albireo Explorer`,url:`https://albireo-explorer.playfi.ai`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`}},testnet:!0}),zO=L({id:242,name:`Plinga`,nativeCurrency:{name:`Plinga`,symbol:`PLINGA`,decimals:18},rpcUrls:{default:{http:[`https://rpcurl.mainnet.plgchain.com`]}},blockExplorers:{default:{name:`Plgscan`,url:`https://www.plgscan.com`}},contracts:{multicall3:{address:`0x0989576160f2e7092908BB9479631b901060b6e4`,blockCreated:204489}}}),BO=L({id:98865,name:`Plume (Legacy)`,nativeCurrency:{name:`Plume Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plumenetwork.xyz`],webSocket:[`wss://rpc.plumenetwork.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.plumenetwork.xyz`,apiUrl:`https://explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:48577}},sourceId:1}),VO=L({id:98864,name:`Plume Devnet (Legacy)`,nativeCurrency:{name:`Plume Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://test-rpc.plumenetwork.xyz`],webSocket:[`wss://test-rpc.plumenetwork.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://test-explorer.plumenetwork.xyz`,apiUrl:`https://test-explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:481948}},testnet:!0,sourceId:11155111}),HO=L({id:98866,name:`Plume`,nativeCurrency:{name:`Plume`,symbol:`PLUME`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plume.org`],webSocket:[`wss://rpc.plume.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.plume.org`,apiUrl:`https://explorer.plume.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:39679}},sourceId:1}),UO=L({id:98867,name:`Plume Testnet`,nativeCurrency:{name:`Plume`,symbol:`PLUME`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plume.org`],webSocket:[`wss://testnet-rpc.plume.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.plume.org`,apiUrl:`https://testnet-explorer.plume.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:199712}},testnet:!0,sourceId:11155111}),WO=L({id:161221135,name:`Plume Testnet (Legacy)`,nativeCurrency:{name:`Plume Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plumenetwork.xyz/http`],webSocket:[`wss://testnet-rpc.plumenetwork.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.plumenetwork.xyz`,apiUrl:`https://testnet-explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6022332}},testnet:!0,sourceId:11155111}),GO=L({id:631571,name:`Polter Testnet`,nativeCurrency:{decimals:18,name:`Polter GHST`,symbol:`GHST`},rpcUrls:{default:{http:[`https://geist-polter.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://polter-testnet.explorer.alchemy.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11245}},testnet:!0}),KO=L({id:137,name:`Polygon`,blockTime:2e3,nativeCurrency:{name:`POL`,symbol:`POL`,decimals:18},rpcUrls:{default:{http:[`https://polygon.drpc.org`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://polygonscan.com`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:25770160}}}),qO=L({id:80002,name:`Polygon Amoy`,nativeCurrency:{name:`POL`,symbol:`POL`,decimals:18},rpcUrls:{default:{http:[`https://rpc-amoy.polygon.technology`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://amoy.polygonscan.com`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3127388}},testnet:!0}),JO=L({id:80001,name:`Polygon Mumbai`,nativeCurrency:{name:`MATIC`,symbol:`MATIC`,decimals:18},rpcUrls:{default:{http:[`https://80001.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://mumbai.polygonscan.com`,apiUrl:`https://api-testnet.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:25770160}},testnet:!0}),YO=L({id:1101,name:`Polygon zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://zkevm-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://zkevm.polygonscan.com`,apiUrl:`https://api-zkevm.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:57746}}}),XO=L({id:2442,name:`Polygon zkEVM Cardona`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cardona.zkevm-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://cardona-zkevm.polygonscan.com`,apiUrl:`https://cardona-zkevm.polygonscan.com/api`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:114091}}}),ZO=L({id:1442,name:`Polygon zkEVM Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.public.zkevm-test.net`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://testnet-zkevm.polygonscan.com`,apiUrl:`https://testnet-zkevm.polygonscan.com/api`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:525686}}}),QO=L({id:8008,name:`Polynomial`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.polynomial.fi`]}},blockExplorers:{default:{name:`Polynomial Scan`,url:`https://polynomialscan.io`}},testnet:!1,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),$O=L({id:80008,name:`Polynomia Sepolia`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.sepolia.polynomial.fi`]}},blockExplorers:{default:{name:`Polynomial Scan`,url:`https://sepolia.polynomialscan.io`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),ek=L({id:60603,name:`POTOS Mainnet`,nativeCurrency:{decimals:18,name:`POTOS Token`,symbol:`POT`},rpcUrls:{default:{http:[`https://rpc.potos.hk`]}},blockExplorers:{default:{name:`POTOS Mainnet explorer`,url:`https://scan.potos.hk`}},testnet:!1}),tk=L({id:60600,name:`POTOS Testnet`,nativeCurrency:{decimals:18,name:`POTOS Token`,symbol:`POT`},rpcUrls:{default:{http:[`https://rpc-testnet.potos.hk`]}},blockExplorers:{default:{name:`POTOS Testnet explorer`,url:`https://scan-testnet.potos.hk`}},testnet:!0}),nk=L({id:23023,name:`PremiumBlock Testnet`,nativeCurrency:{name:`Premium Block`,symbol:`PBLK`,decimals:18},rpcUrls:{default:{http:[`https://rpc.premiumblock.org`]}},blockExplorers:{default:{name:`PremiumBlocks Explorer`,url:`https://scan.premiumblock.org`}},testnet:!0}),rk=L({id:369,name:`PulseChain`,nativeCurrency:{name:`Pulse`,symbol:`PLS`,decimals:18},testnet:!1,blockTime:1e4,rpcUrls:{default:{http:[`https://rpc.pulsechain.com`],webSocket:[`wss://ws.pulsechain.com`]}},blockExplorers:{default:{name:`PulseScan`,url:`https://ipfs.scan.pulsechain.com`,apiUrl:`https://api.scan.pulsechain.com/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),ik=L({id:943,name:`PulseChain V4`,testnet:!0,nativeCurrency:{name:`V4 Pulse`,symbol:`v4PLS`,decimals:18},blockTime:1e4,rpcUrls:{default:{http:[`https://rpc.v4.testnet.pulsechain.com`],webSocket:[`wss://ws.v4.testnet.pulsechain.com`]}},blockExplorers:{default:{name:`PulseScan`,url:`https://scan.v4.testnet.pulsechain.com`,apiUrl:`https://scan.v4.testnet.pulsechain.com/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),ak=L({id:490092,name:`Pumpfi Testnet`,nativeCurrency:{decimals:18,name:`PMPT`,symbol:`PMPT`},rpcUrls:{default:{http:[`https://rpc1testnet.pumpfi.me`]}},blockExplorers:{default:{name:`Pumpfi Testnet Scan`,url:`https://testnetscan.pumpfi.me`}},testnet:!0}),ok=11155111,sk=L({...R,name:`Pyrope Testnet`,testnet:!0,id:695569,sourceId:ok,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.pyropechain.com`],webSocket:[`wss://rpc.pyropechain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://pyrope.blockscout.com`}},contracts:{...R.contracts,l1StandardBridge:{[ok]:{address:`0xC24932c31D9621aE9e792576152B7ef010cFC2F8`}}}}),ck=L({id:766,name:`QL1`,nativeCurrency:{decimals:18,name:`QOM`,symbol:`QOM`},rpcUrls:{default:{http:[`https://rpc.qom.one`]}},blockExplorers:{default:{name:`Ql1 Explorer`,url:`https://scan.qom.one`}},contracts:{multicall3:{address:`0x7A52370716ea730585884F5BDB0f6E60C39b8C64`}},testnet:!1}),lk=L({id:35441,name:`Q Mainnet`,nativeCurrency:{decimals:18,name:`Q`,symbol:`Q`},rpcUrls:{default:{http:[`https://rpc.q.org`]}},blockExplorers:{default:{name:`Q Mainnet Explorer`,url:`https://explorer.q.org`,apiUrl:`https://explorer.q.org/api`}}}),uk=L({id:35443,name:`Q Testnet`,nativeCurrency:{decimals:18,name:`Q`,symbol:`Q`},rpcUrls:{default:{http:[`https://rpc.qtestnet.org`]}},blockExplorers:{default:{name:`Q Testnet Explorer`,url:`https://explorer.qtestnet.org`,apiUrl:`https://explorer.qtestnet.org/api`}},testnet:!0}),dk=L({id:9,name:`Quai Network Mainnet`,nativeCurrency:{decimals:18,name:`Quai`,symbol:`QUAI`},rpcUrls:{default:{http:[`https://rpc.quai.network/cyprus1`]}},blockExplorers:{default:{name:`Quaiscan`,url:`https://quaiscan.io`,apiUrl:`https://quaiscan.io/api`}},testnet:!1}),fk=L({id:15e3,name:`Quai Network Testnet`,nativeCurrency:{decimals:18,name:`Quai`,symbol:`QUAI`},rpcUrls:{default:{http:[`https://orchard.rpc.quai.network/cyprus1`]}},blockExplorers:{default:{name:`Orchard Quaiscan`,url:`https://orchard.quaiscan.io`,apiUrl:`https://orchard.quaiscan.io/api`}},testnet:!0}),pk=L({id:5318007,name:`Reactive Lasna Testnet`,nativeCurrency:{decimals:18,name:`Lasna React`,symbol:`lREACT`},rpcUrls:{default:{http:[`https://lasna-rpc.rnk.dev`]}},blockExplorers:{default:{name:`Reactscan`,url:`https://lasna.reactscan.net`}},testnet:!0}),mk=L({id:111188,name:`re.al`,nativeCurrency:{name:`reETH`,decimals:18,symbol:`reETH`},rpcUrls:{default:{http:[`https://rpc.realforreal.gelato.digital`]}},blockExplorers:{default:{name:`re.al Explorer`,url:`https://explorer.re.al`,apiUrl:`https://explorer.re.al/api/v2`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:695}}}),hk=L({id:151,name:`Redbelly Network Mainnet`,nativeCurrency:{name:`Redbelly Native Coin`,symbol:`RBNT`,decimals:18},rpcUrls:{default:{http:[`https://governors.mainnet.redbelly.network`]}},blockExplorers:{default:{name:`Routescan`,url:`https://redbelly.routescan.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/151/etherscan/api`}},testnet:!1}),gk=L({id:153,name:`Redbelly Network Testnet`,nativeCurrency:{name:`Redbelly Native Coin`,symbol:`RBNT`,decimals:18},rpcUrls:{default:{http:[`https://governors.testnet.redbelly.network`]}},blockExplorers:{default:{name:`Routescan`,url:`https://redbelly.testnet.routescan.io`,apiUrl:`https://api.routescan.io/v2/network/testnet/evm/153_2/etherscan/api`}},testnet:!0}),_k=L({id:50342,name:`Reddio`,nativeCurrency:{name:`Reddio`,symbol:`RED`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.reddio.com/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://reddio.cloud.blockscout.com`,apiUrl:`https://reddio.cloud.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:848849}},testnet:!1}),vk=L({id:50341,name:`Reddio Sepolia`,nativeCurrency:{name:`Reddio`,symbol:`RED`,decimals:18},rpcUrls:{default:{http:[`https://reddio-dev.reddio.com`]}},blockExplorers:{default:{name:`Reddioscan`,url:`https://reddio-devnet.l2scan.co`,apiUrl:`https://reddio-devnet.l2scan.co/api`}},testnet:!0}),yk=1,bk=L({...R,name:`Redstone`,id:690,sourceId:yk,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.redstonechain.com`],webSocket:[`wss://rpc.redstonechain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.redstone.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[yk]:{address:`0xC7bCb0e8839a28A1cFadd1CF716de9016CdA51ae`,blockCreated:19578329}},l2OutputOracle:{[yk]:{address:`0xa426A052f657AEEefc298b3B5c35a470e4739d69`,blockCreated:19578337}},l1StandardBridge:{[yk]:{address:`0xc473ca7E02af24c129c2eEf51F2aDf0411c1Df69`,blockCreated:19578331}}}}),xk=L({id:47805,name:`REI Mainnet`,nativeCurrency:{decimals:18,name:`REI`,symbol:`REI`},rpcUrls:{default:{http:[`https://rpc.rei.network`],webSocket:[`wss://rpc.rei.network`]}},blockExplorers:{default:{name:`REI Scan`,url:`https://scan.rei.network`}},testnet:!1}),Sk=L({id:1729,name:`Reya Network`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.reya.network`],webSocket:[`wss://ws.reya.network`]}},blockExplorers:{default:{name:`Reya Network Explorer`,url:`https://explorer.reya.network`}},testnet:!1}),Ck=L({id:4153,name:`RISE`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.risechain.com`],webSocket:[`wss://rpc.risechain.com/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.risechain.com`,apiUrl:`https://explorer.risechain.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),wk=L({id:11155931,name:`RISE Testnet`,nativeCurrency:{name:`RISE Testnet Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.riselabs.xyz`],webSocket:[`wss://testnet.riselabs.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.testnet.riselabs.xyz/`,apiUrl:`https://explorer.testnet.riselabs.xyz/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},testnet:!0}),Tk=L({id:753,name:`Rivalz`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rivalz.calderachain.xyz/http`]}},blockExplorers:{default:{name:`Rivalz Caldera Explorer`,url:`https://rivalz.calderaexplorer.xyz`}},testnet:!1}),Ek=L({id:570,name:`Rollux Mainnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.rollux.com`],webSocket:[`wss://rpc.rollux.com/wss`]}},blockExplorers:{default:{name:`RolluxExplorer`,url:`https://explorer.rollux.com`,apiUrl:`https://explorer.rollux.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:119222}}}),Dk=L({id:57e3,name:`Rollux Testnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc-tanenbaum.rollux.com/`],webSocket:[`wss://rpc-tanenbaum.rollux.com/wss`]}},blockExplorers:{default:{name:`RolluxTestnetExplorer`,url:`https://rollux.tanenbaum.io`,apiUrl:`https://rollux.tanenbaum.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1813675}}}),Ok=L({id:2020,name:`Ronin`,nativeCurrency:{name:`RON`,symbol:`RON`,decimals:18},rpcUrls:{default:{http:[`https://api.roninchain.com/rpc`]}},blockExplorers:{default:{name:`Ronin Explorer`,url:`https://app.roninchain.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:26023535}}}),kk=L({id:7668,name:`The Root Network`,nativeCurrency:{decimals:18,name:`XRP`,symbol:`XRP`},rpcUrls:{default:{http:[`https://root.rootnet.live/archive`],webSocket:[`wss://root.rootnet.live/archive/ws`]}},blockExplorers:{default:{name:`Rootscan`,url:`https://rootscan.io`}},contracts:{multicall3:{address:`0xc9C2E2429AeC354916c476B30d729deDdC94988d`,blockCreated:9218338}}}),Ak=L({id:7672,name:`The Root Network - Porcini`,nativeCurrency:{decimals:18,name:`XRP`,symbol:`XRP`},rpcUrls:{default:{http:[`https://porcini.rootnet.app/archive`],webSocket:[`wss://porcini.rootnet.app/archive/ws`]}},blockExplorers:{default:{name:`Rootscan`,url:`https://porcini.rootscan.io`}},contracts:{multicall3:{address:`0xc9C2E2429AeC354916c476B30d729deDdC94988d`,blockCreated:10555692}},testnet:!0}),jk=L({id:30,name:`Rootstock Mainnet`,network:`rootstock`,nativeCurrency:{decimals:18,name:`Rootstock Bitcoin`,symbol:`RBTC`},rpcUrls:{default:{http:[`https://public-node.rsk.co`]}},blockExplorers:{default:{name:`RSK Explorer`,url:`https://explorer.rsk.co`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4249540}}}),Mk=L({id:31,name:`Rootstock Testnet`,network:`rootstock`,nativeCurrency:{decimals:18,name:`Rootstock Bitcoin`,symbol:`tRBTC`},rpcUrls:{default:{http:[`https://public-node.testnet.rsk.co`]}},blockExplorers:{default:{name:`RSK Explorer`,url:`https://explorer.testnet.rootstock.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2771150}},testnet:!0}),Nk=1,Pk=L({...R,id:12553,name:`RSS3 VSL Mainnet`,nativeCurrency:{name:`RSS3`,symbol:`RSS3`,decimals:18},rpcUrls:{default:{http:[`https://rpc.rss3.io`]}},blockExplorers:{default:{name:`RSS3 VSL Mainnet Scan`,url:`https://scan.rss3.io`,apiUrl:`https://scan.rss3.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[Nk]:{address:`0xE6f24d2C32B3109B18ed33cF08eFb490b1e09C10`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14193},portal:{[Nk]:{address:`0x6A12432491bbbE8d3babf75F759766774C778Db4`,blockCreated:19387057}},l1StandardBridge:{[Nk]:{address:`0x4cbab69108Aa72151EDa5A3c164eA86845f18438`}}},sourceId:Nk}),Fk=11155111,Ik=L({...R,id:2331,name:`RSS3 VSL Sepolia Testnet`,nativeCurrency:{name:`RSS3`,symbol:`RSS3`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.rss3.io`]}},blockExplorers:{default:{name:`RSS3 VSL Sepolia Testnet Scan`,url:`https://scan.testnet.rss3.io`,apiUrl:`https://scan.testnet.rss3.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[Fk]:{address:`0xDb5c46C3Eaa6Ed6aE8b2379785DF7dd029C0dC81`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:55697},portal:{[Fk]:{address:`0xcBD77E8E1E7F06B25baDe67142cdE82652Da7b57`,blockCreated:5345035}},l1StandardBridge:{[Fk]:{address:`0xdDD29bb63B0839FB1cE0eE439Ff027738595D07B`}}},testnet:!0,sourceId:Fk}),Lk=L({id:7225878,name:`Saakuru Mainnet`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.saakuru.network`]}},blockExplorers:{default:{name:`Saakuru Explorer`,url:`https://explorer.saakuru.network`}},testnet:!1}),Rk=L({id:5464,name:`Saga`,network:`saga`,nativeCurrency:{decimals:18,name:`gas`,symbol:`GAS`},rpcUrls:{default:{http:[`https://sagaevm.jsonrpc.sagarpc.io`]}},blockExplorers:{default:{name:`Saga Explorer`,url:`https://sagaevm.sagaexplorer.io`}},contracts:{multicall3:{address:`0x864DDc9B50B9A0dF676d826c9B9EDe9F8913a160`,blockCreated:467530}}}),zk=L({id:202601,name:`Ronin Saigon Testnet`,nativeCurrency:{name:`RON`,symbol:`RON`,decimals:18},rpcUrls:{default:{http:[`https://saigon-testnet.roninchain.com/rpc`]}},blockExplorers:{default:{name:`Saigon Explorer`,url:`https://saigon-explorer.roninchain.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:18736871}},testnet:!0}),Bk=L({id:1996,name:`Sanko`,nativeCurrency:{name:`DMT`,symbol:`DMT`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.sanko.xyz`]}},blockExplorers:{default:{name:`Sanko Explorer`,url:`https://explorer.sanko.xyz`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:37}},testnet:!1}),Vk=L({id:23294,name:`Oasis Sapphire`,network:`sapphire`,nativeCurrency:{name:`Sapphire Rose`,symbol:`ROSE`,decimals:18},rpcUrls:{default:{http:[`https://sapphire.oasis.io`],webSocket:[`wss://sapphire.oasis.io/ws`]}},blockExplorers:{default:{name:`Oasis Explorer`,url:`https://explorer.oasis.io/mainnet/sapphire`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:734531}}}),Hk=L({id:23295,name:`Oasis Sapphire Testnet`,network:`sapphire-testnet`,nativeCurrency:{name:`Sapphire Test Rose`,symbol:`TEST`,decimals:18},rpcUrls:{default:{http:[`https://testnet.sapphire.oasis.dev`],webSocket:[`wss://testnet.sapphire.oasis.dev/ws`]}},blockExplorers:{default:{name:`Oasis Explorer`,url:`https://explorer.oasis.io/testnet/sapphire`}},testnet:!0}),Uk=L({id:3109,name:`SatoshiVM Alpha Mainnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://alpha-rpc-node-http.svmscan.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://svmscan.io`,apiUrl:`https://svmscan.io/api`}}}),Wk=L({id:3110,name:`SatoshiVM Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://test-rpc-node-http.svmscan.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet.svmscan.io`,apiUrl:`https://testnet.svmscan.io/api`}},testnet:!0}),Gk=L({id:534352,name:`Scroll`,blockTime:3e3,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.scroll.io`],webSocket:[`wss://wss-rpc.scroll.io/ws`]}},blockExplorers:{default:{name:`Scrollscan`,url:`https://scrollscan.com`,apiUrl:`https://api.scrollscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14}},testnet:!1}),Kk=L({id:534351,name:`Scroll Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.scroll.io`]}},blockExplorers:{default:{name:`Scrollscan`,url:`https://sepolia.scrollscan.com`,apiUrl:`https://api-sepolia.scrollscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:9473}},testnet:!0}),qk=L({id:1329,name:`Sei Network`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc.sei-apis.com/`],webSocket:[`wss://evm-ws.sei-apis.com/`]}},blockExplorers:{default:{name:`Seiscan`,url:`https://seiscan.io`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}}}),Jk=L({id:5124,name:`Seismic Devnet`,nativeCurrency:{name:`Seismic Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://node-2.seismicdev.net/rpc`]}},blockExplorers:{default:{name:`Seismic Devnet Explorer`,url:`https://explorer-2.seismicdev.net`}},testnet:!0}),Yk=L({id:1328,name:`Sei Testnet`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc-testnet.sei-apis.com`],webSocket:[`wss://evm-ws-testnet.sei-apis.com`]}},blockExplorers:{default:{name:`Seiscan`,url:`https://testnet.seiscan.io`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:98697651}},testnet:!0}),Xk=L({id:11155111,name:`Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://11155111.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.etherscan.io`,apiUrl:`https://api-sepolia.etherscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:751532},ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:8928790}},testnet:!0}),Zk=1,Qk=L({...R,id:360,name:`Shape`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.shape.network`]}},blockExplorers:{default:{name:`shapescan`,url:`https://shapescan.xyz`,apiUrl:`https://shapescan.xyz/api`}},contracts:{...R.contracts,l2OutputOracle:{[Zk]:{address:`0x6Ef8c69CfE4635d866e3E02732068022c06e724D`,blockCreated:20369940}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1},portal:{[Zk]:{address:`0xEB06fFa16011B5628BaB98E29776361c83741dd3`,blockCreated:20369933}},l1StandardBridge:{[Zk]:{address:`0x62Edd5f4930Ea92dCa3fB81689bDD9b9d076b57B`,blockCreated:20369935}}},sourceId:Zk}),$k=11155111,eA=L({...R,id:11011,name:`Shape Sepolia Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.shape.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer-sepolia.shape.network/`,apiUrl:`https://explorer-sepolia.shape.network/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1}},testnet:!0,sourceId:$k}),tA=L({id:8118,name:`Shardeum`,nativeCurrency:{name:`Shardeum`,symbol:`SHM`,decimals:18},rpcUrls:{default:{http:[`https://api.shardeum.org`]}},blockExplorers:{default:{name:`Shardeum Explorer`,url:`https://explorer.shardeum.org`}},testnet:!1}),nA=L({id:8082,name:`Shardeum Sphinx`,nativeCurrency:{name:`SHARDEUM`,symbol:`SHM`,decimals:18},rpcUrls:{default:{http:[`https://sphinx.shardeum.org`]}},blockExplorers:{default:{name:`Shardeum Explorer`,url:`https://explorer-sphinx.shardeum.org`}},testnet:!0}),rA=L({id:109,name:`Shibarium`,network:`shibarium`,nativeCurrency:{name:`Bone`,symbol:`BONE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.shibrpc.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://shibariumscan.io`}},contracts:{multicall3:{address:`0x864Bf681ADD6052395188A89101A1B37d3B4C961`,blockCreated:265900}}}),iA=L({id:157,name:`Puppynet Shibarium`,nativeCurrency:{decimals:18,name:`Bone`,symbol:`BONE`},rpcUrls:{default:{http:[`https://puppynet.shibrpc.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://puppyscan.shib.io`,apiUrl:`https://puppyscan.shib.io/api`}},contracts:{multicall3:{address:`0xA4029b74FBA366c926eDFA7Dd10B21C621170a4c`,blockCreated:3035769}},testnet:!0}),aA=L({id:336,name:`Shiden`,nativeCurrency:{decimals:18,name:`SDN`,symbol:`SDN`},rpcUrls:{default:{http:[`https://shiden.public.blastapi.io`],webSocket:[`wss://shiden-rpc.dwellir.com`]}},blockExplorers:{default:{name:`Shiden Scan`,url:`https://shiden.subscan.io`}},testnet:!1}),oA=L({id:148,name:`Shimmer`,network:`shimmer`,nativeCurrency:{decimals:18,name:`Shimmer`,symbol:`SMR`},rpcUrls:{default:{http:[`https://json-rpc.evm.shimmer.network`]}},blockExplorers:{default:{name:`Shimmer Network Explorer`,url:`https://explorer.evm.shimmer.network`,apiUrl:`https://explorer.evm.shimmer.network/api`}}}),sA=L({id:1073,name:`Shimmer Testnet`,network:`shimmer-testnet`,nativeCurrency:{decimals:18,name:`Shimmer`,symbol:`SMR`},rpcUrls:{default:{http:[`https://json-rpc.evm.testnet.shimmer.network`]}},blockExplorers:{default:{name:`Shimmer Network Explorer`,url:`https://explorer.evm.testnet.shimmer.network`,apiUrl:`https://explorer.evm.testnet.shimmer.network/api`}},testnet:!0}),cA=L({id:97453,name:`Sidra Chain`,nativeCurrency:{decimals:18,name:`Sidra Digital Asset`,symbol:`SDA`},rpcUrls:{default:{http:[`https://node.sidrachain.com`]}},blockExplorers:{default:{name:`Sidra Chain Explorer`,url:`https://ledger.sidrachain.com`}}}),lA=L({id:380929,name:`Silent Data Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.silentdata.com`]}},blockExplorers:{default:{name:`Silent Data Mainnet Explorer`,url:`https://explorer-mainnet.rollup.silentdata.com`}},testnet:!1}),uA=L({id:2355,name:`Silicon zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.silicon.network`,`https://silicon-mainnet.nodeinfra.com`]}},blockExplorers:{default:{name:`SiliconScope`,url:`https://scope.silicon.network`}}}),dA=L({id:1722641160,name:`Silicon Sepolia zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-sepolia.silicon.network`,`https://silicon-testnet.nodeinfra.com`]}},blockExplorers:{default:{name:`SiliconSepoliaScope`,url:`https://scope-sepolia.silicon.network`}},testnet:!0}),fA=L({id:98,name:`Six Protocol`,nativeCurrency:{decimals:18,name:`SIX`,symbol:`SIX`},rpcUrls:{default:{http:[`https://sixnet-rpc-evm.sixprotocol.net`]}},blockExplorers:{default:{name:`Six Protocol Scan`,url:`https://sixscan.io/sixnet`}},testnet:!1}),pA=L({id:391845894,name:`SKALE | Block Brawlers`,nativeCurrency:{name:`BRAWL`,symbol:`BRAWL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/frayed-decent-antares`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/frayed-decent-antares`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://frayed-decent-antares.explorer.mainnet.skalenodes.com`}},contracts:{}}),mA=L({id:1564830818,name:`SKALE Calypso Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/honorable-steel-rasalhague`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/honorable-steel-rasalhague`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3107626}}}),hA=L({id:974399131,name:`SKALE Calypso Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/giant-half-dual-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/giant-half-dual-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://giant-half-dual-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:103220}},testnet:!0}),gA=L({id:1026062157,name:`SKALE | CryptoBlades`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/affectionate-immediate-pollux`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/affectionate-immediate-pollux`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://affectionate-immediate-pollux.explorer.mainnet.skalenodes.com`}},contracts:{}}),_A=L({id:1032942172,name:`SKALE | Crypto Colosseum`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/haunting-devoted-deneb`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/haunting-devoted-deneb`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://haunting-devoted-deneb.explorer.mainnet.skalenodes.com`}},contracts:{}}),vA=L({id:2046399126,name:`SKALE Europa Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/elated-tan-skat`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/elated-tan-skat`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://elated-tan-skat.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3113495}}}),yA=L({id:1444673419,name:`SKALE Europa Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/juicy-low-small-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/juicy-low-small-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://juicy-low-small-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:110858}},testnet:!0}),bA=L({id:2139927552,name:`Exorde Network`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/light-vast-diphda`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/light-vast-diphda`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://light-vast-diphda.explorer.mainnet.skalenodes.com`}},contracts:{}}),xA=L({id:1273227453,name:`SKALE | Human Protocol`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/wan-red-ain`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/wan-red-ain`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://wan-red-ain.explorer.mainnet.skalenodes.com`}},contracts:{}}),SA=L({id:1482601649,name:`SKALE Nebula Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/green-giddy-denebola`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/green-giddy-denebola`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://green-giddy-denebola.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2372986}}}),CA=L({id:37084624,name:`SKALE Nebula Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/lanky-ill-funny-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/lanky-ill-funny-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://lanky-ill-funny-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:105141}},testnet:!0}),wA=L({id:278611351,name:`SKALE | Razor Network`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/turbulent-unique-scheat`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/turbulent-unique-scheat`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://turbulent-unique-scheat.explorer.mainnet.skalenodes.com`}},contracts:{}}),TA=L({id:1187947933,name:`SKALE Base`,nativeCurrency:{name:`Credits`,symbol:`CREDIT`,decimals:18},rpcUrls:{default:{http:[`https://skale-base.skalenodes.com/v1/base`],webSocket:[`wss://skale-base.skalenodes.com/v1/ws/base`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://skale-base-explorer.skalenodes.com/`}},testnet:!0}),EA=L({id:324705682,name:`SKALE Base Sepolia Testnet`,nativeCurrency:{name:`Credits`,symbol:`CREDIT`,decimals:18},rpcUrls:{default:{http:[`https://base-sepolia-testnet.skalenodes.com/v1/base-testnet`],webSocket:[`wss://base-sepolia-testnet.skalenodes.com/v1/ws/base-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://base-sepolia-testnet-explorer.skalenodes.com/`}},testnet:!0}),DA=L({id:1350216234,name:`SKALE Titan Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/parallel-stormy-spica`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/parallel-stormy-spica`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://parallel-stormy-spica.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2076458}}}),OA=L({id:1020352220,name:`SKALE Titan Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/aware-fake-trim-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/aware-fake-trim-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://aware-fake-trim-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:104072}},testnet:!0}),kA=L({id:984123,name:`Forma Sketchpad`,network:`sketchpad`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sketchpad-1.forma.art`],webSocket:[`wss://ws.sketchpad-1.forma.art`]}},blockExplorers:{default:{name:`Sketchpad Explorer`,url:`https://explorer.sketchpad-1.forma.art`}},testnet:!0}),AA=1,jA=L({...R,id:2192,network:`snaxchain-mainnet`,name:`SnaxChain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.snaxchain.io`]}},blockExplorers:{default:{name:`Snax Explorer`,url:`https://explorer.snaxchain.io`,apiUrl:`https://explorer.snaxchain.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[AA]:{address:`0x472562Fcf26D6b2793f8E0b0fB660ba0E5e08A46`}},l2OutputOracle:{[AA]:{address:`0x2172e492Fc807F5d5645D0E3543f139ECF539294`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[AA]:{address:`0x79f446D024d74D0Bb6E699C131c703463c5D65E9`}},l1StandardBridge:{[AA]:{address:`0x6534Bdb6b5c060d3e6aa833433333135eFE8E0aA`}}},sourceId:AA}),MA=11155111,NA=L({...R,id:13001,network:`snaxchain-testnet`,name:`SnaxChain Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.snaxchain.io`]}},blockExplorers:{default:{name:`Snax Explorer`,url:`https://testnet-explorer.snaxchain.io`,apiUrl:`https://testnet-explorer.snaxchain.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[MA]:{address:`0x206a75d89d45F146C54020F132FF93bEDD09f55E`}},l2OutputOracle:{[MA]:{address:`0x60e3A368a4cdCEf85ffB964e372726F56A46221e`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[MA]:{address:`0xb5afdd0E8dDF081Ef90e8A3e0c7b5798e66E954E`}},l1StandardBridge:{[MA]:{address:`0xbd37E1a59D4C00C9A46F75018dffd84061bC5f74`}}},testnet:!0,sourceId:MA}),PA=L({id:5031,name:`Somnia`,nativeCurrency:{name:`Somnia`,symbol:`SOMI`,decimals:18},rpcUrls:{default:{http:[`https://api.infra.mainnet.somnia.network`]}},blockExplorers:{default:{name:`Somnia Explorer`,url:`https://explorer.somnia.network`,apiUrl:`https://explorer.somnia.network/api`}},testnet:!1}),FA=L({id:50312,name:`Somnia Testnet`,nativeCurrency:{name:`STT`,symbol:`STT`,decimals:18},rpcUrls:{default:{http:[`https://dream-rpc.somnia.network`]}},blockExplorers:{default:{name:`Somnia Testnet Explorer`,url:`https://shannon-explorer.somnia.network/`,apiUrl:`https://shannon-explorer.somnia.network/api`}},contracts:{multicall3:{address:`0x841b8199E6d3Db3C6f264f6C2bd8848b3cA64223`,blockCreated:71314235}},testnet:!0}),IA=1,LA=L({...R,id:1868,name:`Soneium Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.soneium.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://soneium.blockscout.com`,apiUrl:`https://soneium.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[IA]:{address:`0x512a3d2c7a43bd9261d2b8e8c9c70d4bd4d503c0`}},l2OutputOracle:{[IA]:{address:`0x0000000000000000000000000000000000000000`}},portal:{[IA]:{address:`0x88e529a6ccd302c948689cd5156c83d4614fae92`,blockCreated:7061266}},l1StandardBridge:{[IA]:{address:`0xeb9bf100225c214efc3e7c651ebbadcf85177607`,blockCreated:7061266}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}},sourceId:IA}),RA=11155111,zA=L({...R,id:1946,name:`Soneium Minato Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.minato.soneium.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://soneium-minato.blockscout.com`,apiUrl:`https://soneium-minato.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[RA]:{address:`0xB3Ad2c38E6e0640d7ce6aA952AB3A60E81bf7a01`}},l2OutputOracle:{[RA]:{address:`0x710e5286C746eC38beeB7538d0146f60D27be343`}},portal:{[RA]:{address:`0x65ea1489741A5D72fFdD8e6485B216bBdcC15Af3`,blockCreated:6466136}},l1StandardBridge:{[RA]:{address:`0x5f5a404A5edabcDD80DB05E8e54A78c9EBF000C2`,blockCreated:6466136}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}},testnet:!0,sourceId:RA}),BA=L({id:19,name:`Songbird Canary-Network`,nativeCurrency:{decimals:18,name:`Songbird`,symbol:`SGB`},rpcUrls:{default:{http:[`https://songbird-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Songbird Explorer`,url:`https://songbird-explorer.flare.network`,apiUrl:`https://songbird-explorer.flare.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:13382504}}}),VA=L({id:16,name:`Songbird Testnet Coston`,nativeCurrency:{decimals:18,name:`Coston Flare`,symbol:`CFLR`},rpcUrls:{default:{http:[`https://coston-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Coston Explorer`,url:`https://coston-explorer.flare.network`,apiUrl:`https://coston-explorer.flare.network/api`}},testnet:!0}),HA=L({id:146,name:`Sonic`,blockTime:630,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Explorer`,url:`https://sonicscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:60}},testnet:!1}),UA=L({id:57054,name:`Sonic Blaze Testnet`,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.blaze.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Blaze Testnet Explorer`,url:`https://testnet.sonicscan.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1100}},testnet:!0}),WA=L({id:64165,name:`Sonic Testnet`,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.testnet.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Testnet Explorer`,url:`https://testnet.soniclabs.com/`}},testnet:!0}),GA=L({...xy,blockTime:200,id:50104,name:`Sophon`,nativeCurrency:{decimals:18,name:`Sophon`,symbol:`SOPH`},rpcUrls:{default:{http:[`https://rpc.sophon.xyz`],webSocket:[`wss://rpc.sophon.xyz/ws`]}},blockExplorers:{default:{name:`Sophon Block Explorer`,url:`https://explorer.sophon.xyz`}},contracts:{multicall3:{address:`0x5f4867441d2416cA88B1b3fd38f21811680CD2C8`,blockCreated:116}},testnet:!1}),KA=L({...xy,blockTime:200,id:531050104,name:`Sophon Testnet`,nativeCurrency:{decimals:18,name:`Sophon`,symbol:`SOPH`},rpcUrls:{default:{http:[`https://rpc.testnet.sophon.xyz`],webSocket:[`wss://rpc.testnet.sophon.xyz/ws`]}},blockExplorers:{default:{name:`Sophon Block Explorer`,url:`https://explorer.testnet.sophon.xyz`}},contracts:{multicall3:{address:`0x83c04d112adedA2C6D9037bb6ecb42E7f0b108Af`,blockCreated:15642}},testnet:!0}),qA=L({id:100021,name:`Sova`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.sova.io`]}},blockExplorers:{default:{name:`Sova Block Explorer`,url:`hhttps://explorer.sova.io`}},testnet:!1}),JA=L({id:120893,name:`Sova Network Sepolia`,nativeCurrency:{decimals:18,name:`Sepolia Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.testnet.sova.io`]}},blockExplorers:{default:{name:`Sova Sepolia Explorer`,url:`https://explorer.testnet.sova.io`}},testnet:!0}),YA=L({id:88882,name:`Chiliz Spicy Testnet`,network:`chiliz-spicy-Testnet`,nativeCurrency:{decimals:18,name:`CHZ`,symbol:`CHZ`},rpcUrls:{default:{http:[`https://spicy-rpc.chiliz.com`,`https://chiliz-spicy-rpc.publicnode.com`],webSocket:[`wss://spicy-rpc-ws.chiliz.com`,`wss://chiliz-spicy-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Chiliz Explorer`,url:`http://spicy-explorer.chiliz.com`,apiUrl:`http://spicy-explorer.chiliz.com/api`}},testnet:!0}),XA=L({id:988,name:`Stable Mainnet`,blockTime:700,nativeCurrency:{name:`USDT0`,symbol:`USDT0`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stable.xyz`],webSocket:[`wss://rpc.stable.xyz`]}},blockExplorers:{default:{name:`Stablescan`,url:`https://stablescan.xyz`,apiUrl:`https://api.etherscan.io/v2/api?chainid=988`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2423647}},testnet:!1}),ZA=L({id:2201,name:`Stable Testnet`,blockTime:700,nativeCurrency:{name:`USDT0`,symbol:`USDT0`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.stable.xyz`],webSocket:[`wss://rpc.testnet.stable.xyz`]}},blockExplorers:{default:{name:`Stablescan`,url:`https://testnet.stablescan.xyz`,apiUrl:`https://api.etherscan.io/v2/api?chainid=2201`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:22364430}},testnet:!0}),QA=L({...SE,id:1660990954,name:`Status Network Sepolia`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://public.sepolia.rpc.status.network`],webSocket:[`wss://public.sepolia.rpc.status.network/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepoliascan.status.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1578364}},testnet:!0}),$A=L({id:1234,name:`Step Network`,nativeCurrency:{name:`FITFI`,symbol:`FITFI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.step.network`]}},blockExplorers:{default:{name:`Step Scan`,url:`https://stepscan.io`}},testnet:!1}),ej=L({id:1514,name:`Story`,nativeCurrency:{decimals:18,name:`IP Token`,symbol:`IP`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:340998},ensRegistry:{address:`0x5dc881dda4e4a8d312be3544ad13118d1a04cb17`,blockCreated:648924},ensUniversalResolver:{address:`0xddfb18888a9466688235887dec2a10c4f5effee9`,blockCreated:649114}},rpcUrls:{default:{http:[`https://mainnet.storyrpc.io`]}},blockExplorers:{default:{name:`Story explorer`,url:`https://storyscan.io`,apiUrl:`https://storyscan.io/api/v2`}},ensTlds:[`.ip`],testnet:!1}),tj=L({id:1315,name:`Story Aeneid`,network:`story-aeneid`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1792},ensRegistry:{address:`0x5dC881dDA4e4a8d312be3544AD13118D1a04Cb17`,blockCreated:1322033},ensUniversalResolver:{address:`0x6D3B3F99177FB2A5de7F9E928a9BD807bF7b5BAD`,blockCreated:1322097}},rpcUrls:{default:{http:[`https://aeneid.storyrpc.io`]}},blockExplorers:{default:{name:`Story Aeneid Explorer`,url:`https://aeneid.storyscan.io`,apiUrl:`https://aeneid.storyscan.io/api/v2`}},ensTlds:[`.ip`],testnet:!0}),nj=L({id:1516,name:`Story Odyssey`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},rpcUrls:{default:{http:[`https://rpc.odyssey.storyrpc.io`]}},blockExplorers:{default:{name:`Story Odyssey Explorer`,url:`https://odyssey.storyscan.xyz`}},testnet:!0}),rj=L({id:1513,name:`Story Testnet`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},rpcUrls:{default:{http:[`https://testnet.storyrpc.io`]}},blockExplorers:{default:{name:`Story Testnet Explorer`,url:`https://testnet.storyscan.xyz`}},testnet:!0}),ij=L({id:105105,name:`Stratis Mainnet`,network:`stratis`,nativeCurrency:{name:`Stratis`,symbol:`STRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stratisevm.com`]}},blockExplorers:{default:{name:`Stratis Explorer`,url:`https://explorer.stratisevm.com`}}}),aj=L({id:964,name:`Subtensor EVM`,nativeCurrency:{decimals:18,name:`TAO`,symbol:`TAO`},rpcUrls:{default:{http:[`https://lite.chain.opentensor.ai`]}},blockExplorers:{default:{name:`Taostats EVM Explorer`,url:`https://evm.taostats.io`,apiUrl:`https://evm.taostats.io/api`}},testnet:!1}),oj=L({id:8866,name:`SuperLumio`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.lumio.io`]}},blockExplorers:{default:{name:`Lumio explorer`,url:`https://explorer.lumio.io`}},testnet:!1}),sj=L({id:55244,name:`Superposition`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.superposition.so`]}},blockExplorers:{default:{name:`Superposition Explorer`,url:`https://explorer.superposition.so`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:39}},testnet:!1}),cj=1,lj=L({...R,id:5330,name:`Superseed`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.superseed.xyz`]}},blockExplorers:{default:{name:`Superseed Explorer`,url:`https://explorer.superseed.xyz`,apiUrl:`https://explorer.superseed.xyz/api/v2`}},contracts:{...R.contracts,disputeGameFactory:{[cj]:{address:`0x8b097CF1f9BbD9cbFD0DD561858a1FCbC8857Be0`,blockCreated:20737481}},l2OutputOracle:{[cj]:{address:`0x693A0F8854F458D282DE3C5b69E8eE5EEE8aA949`,blockCreated:20737481}},portal:{[cj]:{address:`0x2c2150aa5c75A24fB93d4fD2F2a895D618054f07`,blockCreated:20737481}},l1StandardBridge:{[cj]:{address:`0x8b0576E39F1233679109F9b40cFcC2a7E0901Ede`,blockCreated:20737481}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},sourceId:cj}),uj=11155111,dj=L({...R,id:53302,name:`Superseed Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.superseed.xyz`]}},blockExplorers:{default:{name:`Superseed Sepolia Explorer`,url:`https://sepolia-explorer.superseed.xyz`,apiUrl:`https://sepolia-explorer.superseed.xyz/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},portal:{[uj]:{address:`0x7A0db8C51432d2C3eb4e8f360a2EeB26FF2809fB`,blockCreated:5523438}},l1StandardBridge:{[uj]:{address:`0x2B227A603fAAdB3De0ED050b63ADD232B5f2c28C`,blockCreated:5523442}}},testnet:!0,sourceId:uj}),fj=L({id:763375,name:`Surge Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://l2-rpc.hoodi.surge.wtf`],webSocket:[`wss://l2-ws.hoodi.surge.wtf`]}},blockExplorers:{default:{name:`Surge Testnet Blockscout`,url:`https://explorer.hoodi.surge.wtf`}},testnet:!0}),pj=L({id:254,name:`Swan Chain Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.swanchain.org`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://swanscan.io`}},testnet:!1}),mj=L({id:20241133,name:`Swan Proxima Testnet`,nativeCurrency:{name:`Swan Ether`,symbol:`sETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-proxima.swanchain.io`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://proxima-explorer.swanchain.io`}},testnet:!0}),hj=L({id:2024,name:`Swan Saturn Testnet`,nativeCurrency:{name:`Swan Ether`,symbol:`sETH`,decimals:18},rpcUrls:{default:{http:[`https://saturn-rpc.swanchain.io`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://saturn-explorer.swanchain.io`}},testnet:!0}),gj=L({...R,id:1923,name:`Swellchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://swell-mainnet.alt.technology`]}},blockExplorers:{default:{name:`Swell Explorer`,url:`https://explorer.swellnetwork.io`,apiUrl:`https://explorer.swellnetwork.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}}}),_j=L({...R,id:1924,name:`Swellchain Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://swell-testnet.alt.technology`]}},blockExplorers:{default:{name:`Swellchain Testnet Explorer`,url:`https://swell-testnet-explorer.alt.technology`,apiUrl:`https://swell-testnet-explorer.alt.technology/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}}}),vj=L({id:94,name:`SwissDLT Mainnet`,nativeCurrency:{decimals:18,name:`BCTS`,symbol:`BCTS`},rpcUrls:{default:{http:[`https://rpc.swissdlt.ch`]}},blockExplorers:{default:{name:`SwissDLT Explorer`,url:`https://explorer.swissdlt.ch`}},testnet:!1}),yj=L({id:57,name:`Syscoin Mainnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.syscoin.org`],webSocket:[`wss://rpc.syscoin.org/wss`]}},blockExplorers:{default:{name:`SyscoinExplorer`,url:`https://explorer.syscoin.org`,apiUrl:`https://explorer.syscoin.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:287139}}}),bj=L({id:5700,name:`Syscoin Tanenbaum Testnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.tanenbaum.io`],webSocket:[`wss://rpc.tanenbaum.io/wss`]}},blockExplorers:{default:{name:`SyscoinTestnetExplorer`,url:`https://tanenbaum.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:271288}}}),xj=L({id:239,name:`TAC`,nativeCurrency:{name:`TAC`,symbol:`TAC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.ankr.com/tac`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://tac.blockscout.com`,apiUrl:`https://tac.blockscout.com/api`},native:{name:`TAC Explorer`,url:`https://explorer.tac.build`,apiUrl:`https://explorer.tac.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),Sj=L({id:2391,name:`TAC SPB Testnet`,nativeCurrency:{name:`TAC`,symbol:`TAC`,decimals:18},rpcUrls:{default:{http:[`https://spb.rpc.tac.build`]}},blockExplorers:{default:{name:`TAC`,url:`https://spb.explorer.tac.build`,apiUrl:`https://spb.explorer.tac.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:471429}},testnet:!0}),Cj=L({id:167e3,name:`Taiko Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.mainnet.taiko.xyz`],webSocket:[`wss://ws.mainnet.taiko.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://taikoscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:11269}}}),wj=L({id:167009,name:`Taiko Hekla L2`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hekla.taiko.xyz`]}},blockExplorers:{default:{name:`Taikoscan`,url:`https://hekla.taikoscan.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:59757}},testnet:!0}),Tj=L({id:167013,name:`Taiko Hoodi`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hoodi.taiko.xyz`],webSocket:[`wss://ws.hoodi.taiko.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://hoodi.taikoscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:581116}},testnet:!0}),Ej=L({id:167007,name:`Taiko Jolnir (Alpha-5 Testnet)`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.jolnir.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.jolnir.taiko.xyz`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:732706}},testnet:!0}),Dj=L({id:167008,name:`Taiko Katla (Alpha-6 Testnet)`,network:`tko-katla`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.katla.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.katla.taiko.xyz`}}}),Oj=L({id:167005,name:`Taiko (Alpha-3 Testnet)`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.test.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.test.taiko.xyz`}}}),kj=L({id:841,name:`Taraxa Mainnet`,nativeCurrency:{name:`Tara`,symbol:`TARA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.taraxa.io`]}},blockExplorers:{default:{name:`Taraxa Explorer`,url:`https://explorer.mainnet.taraxa.io`}}}),Aj=L({id:842,name:`Taraxa Testnet`,nativeCurrency:{name:`Tara`,symbol:`TARA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.taraxa.io`]}},blockExplorers:{default:{name:`Taraxa Explorer`,url:`https://explorer.testnet.taraxa.io`}},testnet:!0}),jj=L({id:10218,name:`Tea Sepolia`,nativeCurrency:{name:`Sepolia Tea`,symbol:`TEA`,decimals:18},rpcUrls:{default:{http:[`https://tea-sepolia.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Tea Sepolia Explorer`,url:`https://sepolia.tea.xyz`}},testnet:!0}),Mj=L({id:2017,name:`Telcoin Adiri Testnet`,nativeCurrency:{name:`Telcoin`,symbol:`TEL`,decimals:18},rpcUrls:{default:{http:[`https://rpc.telcoin.network`]}},blockExplorers:{default:{name:`telscan`,url:`https://telscan.io`}},testnet:!0}),Nj=L({id:40,name:`Telos`,nativeCurrency:{decimals:18,name:`Telos`,symbol:`TLOS`},rpcUrls:{default:{http:[`https://rpc.telos.net`]}},blockExplorers:{default:{name:`Teloscan`,url:`https://www.teloscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:246530709}}}),Pj=L({id:41,name:`Telos`,nativeCurrency:{decimals:18,name:`Telos`,symbol:`TLOS`},rpcUrls:{default:{http:[`https://rpc.testnet.telos.net`]}},blockExplorers:{default:{name:`Teloscan (testnet)`,url:`https://testnet.teloscan.io/`}},testnet:!0});Ls(),wl(),Pl(),Wc(),xl();var Fj=Mc(BigInt(`0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff`)),Ij=Fj.create(BigInt(`-3`)),Lj=BigInt(`0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`),Rj=Cl({a:Ij,b:Lj,Fp:Fj,n:BigInt(`0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551`),Gx:BigInt(`0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296`),Gy:BigInt(`0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5`),h:BigInt(1),lowS:!1},Ps),zj=Rj,Bj=pl(Fj,{A:Ij,B:Lj,Z:Fj.create(BigInt(`-10`))}),Vj=Ml(zj.ProjectivePoint,e=>Bj(e[0]),{DST:`P256_XMD:SHA-256_SSWU_RO_`,encodeDST:`P256_XMD:SHA-256_SSWU_NU_`,p:Fj.ORDER,m:1,k:128,expand:`xmd`,hash:Ps}),Hj=Mc(BigInt(`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff`)),Uj=Hj.create(BigInt(`-3`)),Wj=BigInt(`0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef`),Gj=Cl({a:Uj,b:Wj,Fp:Hj,n:BigInt(`0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973`),Gx:BigInt(`0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7`),Gy:BigInt(`0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f`),h:BigInt(1),lowS:!1},Is),Kj=pl(Hj,{A:Uj,B:Wj,Z:Hj.create(BigInt(`-12`))});Ml(Gj.ProjectivePoint,e=>Kj(e[0]),{DST:`P384_XMD:SHA-384_SSWU_RO_`,encodeDST:`P384_XMD:SHA-384_SSWU_NU_`,p:Hj.ORDER,m:1,k:192,expand:`xmd`,hash:Is});var qj=Mc(BigInt(`0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`)),Jj=qj.create(BigInt(`-3`)),Yj=BigInt(`0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00`),Xj=Cl({a:Jj,b:Yj,Fp:qj,n:BigInt(`0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409`),Gx:BigInt(`0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66`),Gy:BigInt(`0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650`),h:BigInt(1),lowS:!1,allowedPrivateKeyLengths:[130,131,132]},Fs),Zj=pl(qj,{A:Jj,B:Yj,Z:qj.create(BigInt(`-4`))});Ml(Xj.ProjectivePoint,e=>Zj(e[0]),{DST:`P521_XMD:SHA-512_SSWU_RO_`,encodeDST:`P521_XMD:SHA-512_SSWU_NU_`,p:qj.ORDER,m:1,k:256,expand:`xmd`,hash:Fs});var Qj=Rj,$j=Rj;Vj.hashToCurve,Vj.encodeToCurve,Hf();function eM(e){let{hash:t,payload:n,publicKey:r,signature:i}=e;return $j.verify(i,n instanceof Uint8Array?n:xf(n),og(r).substring(2),{lowS:!0,...t?{prehash:!0}:{}})}Hf();var tM=new TextEncoder;Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[t,e.charCodeAt(0)]);var nM={...Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[e.charCodeAt(0),t])),61:0,45:62,95:63};function rM(e){let t=e.replace(/=+$/,``),n=t.length,r=new Uint8Array(n+3);tM.encodeInto(t+`===`,r);for(let e=0,n=0;e>16,r[n+1]=t>>8&255,r[n+2]=t&255}let i=(n>>2)*3+(n%4&&n%4-1);return new Uint8Array(r.buffer,0,i)}Hf(),Qd(),pp();var iM=class extends P{constructor({majorType:e}){super(`Invalid CBOR major type: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidMajorTypeError`})}},aM=class extends P{constructor({additionalInfo:e}){super(`Invalid CBOR additional info: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidAdditionalInfoError`})}},oM=class extends P{constructor(){super(`64-bit integers are not supported in CBOR decoding.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.Unsupported64BitIntegerError`})}},sM=class extends P{constructor({tag:e}){super(`CBOR tagged data (tag ${e}) is not yet supported.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.UnsupportedTagError`})}},cM=class extends P{constructor({type:e}){super(`Invalid chunk type in indefinite-length ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidIndefiniteLengthChunkError`})}},lM=class extends P{constructor({value:e}){super(`Invalid CBOR simple value: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidSimpleValueError`})}},uM=class extends P{constructor(){super(`BigInt values are not supported in CBOR encoding.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.UnsupportedBigIntError`})}},dM=class extends P{constructor({token:e}){super(`Unexpected token: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.UnexpectedTokenError`})}},fM=class extends P{constructor({number:e}){super(`Number exceeds maximum safe integer (${2**53-1}): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.NumberTooLargeError`})}},pM=class extends P{constructor({size:e}){super(`String length exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.StringTooLargeError`})}},mM=class extends P{constructor({size:e}){super(`Array length exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.ArrayTooLargeError`})}},hM=class extends P{constructor({size:e}){super(`Object size exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.ObjectTooLargeError`})}},gM=class extends P{constructor({size:e}){super(`Byte string length exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.ByteStringTooLargeError`})}};function _M(e){if(e===void 0)return{length:1,encode:e=>e.pushUint8(247)};if(e===null)return{length:1,encode:e=>e.pushUint8(246)};if(typeof e==`boolean`)return{length:1,encode:t=>t.pushUint8(e?245:244)};if(typeof e==`number`)return _M.number(e);if(typeof e==`bigint`)throw new uM;if(typeof e==`string`)return _M.string(e);if(Array.isArray(e))return _M.array(e);if(e instanceof Uint8Array)return _M.byteString(e);if(e instanceof ArrayBuffer)return _M.byteString(new Uint8Array(e));if(ArrayBuffer.isView(e))return _M.byteString(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));if(e instanceof Map)return _M.map(e);if(typeof e==`object`)return _M.object(e);throw new dM({token:String(e)})}(function(e){function t(e){if(!Number.isSafeInteger(e)){let t=Math.fround(e);return Number.isNaN(e)||e===t?{length:5,encode(t){t.pushUint8(250),t.dataView.setFloat32(t.position,e,!1),t.position+=4}}:{length:9,encode(t){t.pushUint8(251),t.dataView.setFloat64(t.position,e,!1),t.position+=8}}}if(e>=0){if(e<=23)return{length:1,encode:t=>t.pushUint8(e)};if(e<=255)return{length:2,encode:t=>{t.pushUint8(24),t.pushUint8(e)}};if(e<=65535)return{length:3,encode:t=>{t.pushUint8(25),t.pushUint16(e)}};if(e<=4294967295)return{length:5,encode:t=>{t.pushUint8(26),t.pushUint32(e)}};throw new fM({number:e.toString(10)})}let t=-1-e;if(e>=-24)return{length:1,encode:e=>e.pushUint8(32+t)};if(t<=255)return{length:2,encode:e=>{e.pushUint8(56),e.pushUint8(t)}};if(t<=65535)return{length:3,encode:e=>{e.pushUint8(57),e.pushUint16(t)}};if(t<=4294967295)return{length:5,encode:e=>{e.pushUint8(58),e.pushUint32(t)}};throw new fM({number:e.toString(10)})}e.number=t;function n(e){let t=Sf(e),n=t.length;if(n<=23)return{length:1+n,encode(e){e.pushUint8(96+n),n>0&&e.pushBytes(t)}};if(n<=255)return{length:2+n,encode(e){e.pushUint8(120),e.pushUint8(n),e.pushBytes(t)}};if(n<=65535)return{length:3+n,encode(e){e.pushUint8(121),e.pushUint16(n),e.pushBytes(t)}};if(n<=4294967295)return{length:5+n,encode(e){e.pushUint8(122),e.pushUint32(n),e.pushBytes(t)}};throw new pM({size:n})}e.string=n;function r(t){let n=t.map(t=>e(t)),r=n.reduce((e,t)=>e+t.length,0),i=t.length;if(i<=23)return{length:1+r,encode(e){e.pushUint8(128+i);for(let t of n)t.encode(e)}};if(i<=255)return{length:2+r,encode(e){e.pushUint8(152),e.pushUint8(i);for(let t of n)t.encode(e)}};if(i<=65535)return{length:3+r,encode(e){e.pushUint8(153),e.pushUint16(i);for(let t of n)t.encode(e)}};if(i<=4294967295)return{length:5+r,encode(e){e.pushUint8(154),e.pushUint32(i);for(let t of n)t.encode(e)}};throw new mM({size:i})}e.array=r;function i(e){let t=e.byteLength;if(t<=23)return{length:1+t,encode(n){n.pushUint8(64+t),n.pushBytes(e)}};if(t<=255)return{length:2+t,encode(n){n.pushUint8(88),n.pushUint8(t),n.pushBytes(e)}};if(t<=65535)return{length:3+t,encode(n){n.pushUint8(89),n.pushUint16(t),n.pushBytes(e)}};if(t<=4294967295)return{length:5+t,encode(n){n.pushUint8(90),n.pushUint32(t),n.pushBytes(e)}};throw new gM({size:t})}e.byteString=i;function a(t){let n=Object.keys(t),r=n.map(n=>({key:e(n),value:e(t[n])})),i=r.reduce((e,t)=>e+t.key.length+t.value.length,0),a=n.length;if(a<=23)return{length:1+i,encode(e){e.pushUint8(160+a);for(let t of r)t.key.encode(e),t.value.encode(e)}};if(a<=255)return{length:2+i,encode(e){e.pushUint8(184),e.pushUint8(a);for(let t of r)t.key.encode(e),t.value.encode(e)}};if(a<=65535)return{length:3+i,encode(e){e.pushUint8(185),e.pushUint16(a);for(let t of r)t.key.encode(e),t.value.encode(e)}};if(a<=4294967295)return{length:5+i,encode(e){e.pushUint8(186),e.pushUint32(a);for(let t of r)t.key.encode(e),t.value.encode(e)}};throw new hM({size:a})}e.object=a;function o(t){let n=[];for(let[r,i]of t)n.push({key:e(r),value:e(i)});let r=n.reduce((e,t)=>e+t.key.length+t.value.length,0),i=t.size;if(i<=23)return{length:1+r,encode(e){e.pushUint8(160+i);for(let t of n)t.key.encode(e),t.value.encode(e)}};if(i<=255)return{length:2+r,encode(e){e.pushUint8(184),e.pushUint8(i);for(let t of n)t.key.encode(e),t.value.encode(e)}};if(i<=65535)return{length:3+r,encode(e){e.pushUint8(185),e.pushUint16(i);for(let t of n)t.key.encode(e),t.value.encode(e)}};if(i<=4294967295)return{length:5+r,encode(e){e.pushUint8(186),e.pushUint32(i);for(let t of n)t.key.encode(e),t.value.encode(e)}};throw new hM({size:i})}e.map=o})(_M||={});function vM(e){let t=e.readUint8(),n=t>>5,r=t&31;switch(n){case 0:return vM.readUnsignedInteger(e,r);case 1:return vM.readNegativeInteger(e,r);case 2:return vM.readByteString(e,r);case 3:return vM.readTextString(e,r);case 4:return vM.readArray(e,r);case 5:return vM.readMap(e,r);case 6:throw new sM({tag:r});case 7:return vM.readSimpleOrFloat(e,r);default:throw new iM({majorType:n})}}(function(e){function t(e,t){if(t<24)return t;if(t===24)return e.readUint8();if(t===25)return e.readUint16();if(t===26)return e.readUint32();throw t===27?new oM:new aM({additionalInfo:t})}function n(e,n){return t(e,n)}e.readUnsignedInteger=n;function r(e,n){return-1-t(e,n)}e.readNegativeInteger=r;function i(n,r){if(r===31){let t=[],r=0;for(;;){if(n.inspectUint8()===255){n.readUint8();break}let i=e(n);if(!(i instanceof Uint8Array))throw new cM({type:`byte string`});t.push(i),r+=i.length}let i=new Uint8Array(r),a=0;for(let e of t)i.set(e,a),a+=e.length;return i}let i=t(n,r);return n.readBytes(i)}e.readByteString=i;function a(n,r){if(r===31){let t=[];for(;;){if(n.inspectUint8()===255){n.readUint8();break}let r=e(n);if(typeof r!=`string`)throw new cM({type:`text string`});t.push(r)}return t.join(``)}let i=t(n,r);return jf(n.readBytes(i))}e.readTextString=a;function o(n,r){if(r===31){let t=[];for(;;){if(n.inspectUint8()===255){n.readUint8();break}t.push(e(n))}return t}let i=t(n,r),a=[];for(let t=0;t>15&1,n=e>>10&31,r=e&1023;if(n===0){if(r===0)return t?-0:0;let e=2**-14*(r/1024);return t?-e:e}if(n===31)return r===0?t?-1/0:1/0:NaN;let i=2**(n-15)*(1+r/1024);return t?-i:i}})(vM||={}),Hf(),Uint8Array.from([105,171,180,181,160,222,75,198,42,42,32,31,141,37,186,233]),pp(),Hf(),pp();function yM(e){let{challenge:t,metadata:n,origin:r,publicKey:i,rpId:a,signature:o}=e,{authenticatorData:s,clientDataJSON:c,userVerificationRequired:l}=n,u=xf(s);if(u.length<37||a!==void 0&&!Cf(u.slice(0,32),$h(Jf(a),{as:`Bytes`})))return!1;let d=u[32];if((d&1)!=1||l&&(d&4)!=4||(d&8)!=8&&(d&16)==16)return!1;let f=JSON.parse(c);return f.type!==`webauthn.get`||!f.challenge||qf(rM(f.challenge))!==t||r!==void 0&&!(Array.isArray(r)?r:[r]).includes(f.origin)?!1:eM({hash:!0,payload:vf(u,$h(Sf(c),{as:`Bytes`})),publicKey:i,signature:o})}function bM(e){return yM(e)}Qd(),pp(),gf();var xM=`0x01`,SM=`0x02`,CM=`0x03`,wM=`0x04`,TM=`0x7777777777777777777777777777777777777777777777777777777777777777`;function EM(e){let{signature:t,root:n}=e;return t.type===`keychain`?n?t.userAddress:EM({...e,signature:t.inner}):gg(DM(e))}function DM(e){let{payload:t,signature:n}=e;switch(n.type){case`secp256k1`:return K_({payload:t,signature:n.signature});case`p256`:case`webAuthn`:return n.publicKey;case`keychain`:return DM({payload:t,signature:n.inner})}}function OM(e){let t=e.endsWith(`7777777777777777777777777777777777777777777777777777777777777777`)?I(e,0,-Qf(TM)):e;if(Qf(t)===65){let e=C_(t);return x_(e),{signature:e,type:`secp256k1`}}let n=I(t,0,1),r=I(t,1),i=Qf(r);if(n===xM){if(i!==129)throw new IM({reason:`Invalid P256 signature envelope size: expected 129 bytes, got ${i} bytes`,serialized:t});return{publicKey:{prefix:4,x:ep(I(r,64,96)),y:ep(I(r,96,128))},prehash:tp(I(r,128,129))!==0,signature:{r:ep(I(r,0,32)),s:ep(I(r,32,64))},type:`p256`}}if(n===SM){if(i<128)throw new IM({reason:`Invalid WebAuthn signature envelope size: expected at least 128 bytes, got ${i} bytes`,serialized:t});let e=i-128,n=I(r,0,e),a,o;for(let t=37;t{let n=e.inner;return n.type===`p256`||n.type===`webAuthn`?{keyId:gg(n.publicKey)}:n.type===`secp256k1`&&t?.payload?{keyId:gg(K_({payload:t.payload,signature:n.signature}))}:{}})()}:{},type:n}}function AM(e){if(e.type===`secp256k1`)return{signature:D_(e),type:`secp256k1`};if(e.type===`p256`)return{prehash:e.preHash,publicKey:{prefix:4,x:ep(e.pubKeyX),y:ep(e.pubKeyY)},signature:{r:ep(e.r),s:ep(e.s)},type:`p256`};if(e.type===`webAuthn`){let t=e.webauthnData,n=Qf(t),r,i;for(let e=37;e{if(t.address)return t.address;if(t.publicKey)return gg(t.publicKey)})();if(!r)return!1;let i=kM(e);if(i.type===`secp256k1`)return r?q_({address:r,payload:n,signature:i.signature}):!1;if(i.type===`p256`)return _g(gg(i.publicKey),r)?eM({hash:i.prehash,publicKey:i.publicKey,payload:n,signature:i.signature}):!1;if(i.type===`webAuthn`)return _g(gg(i.publicKey),r)?bM({challenge:Gf(n),metadata:i.metadata,publicKey:i.publicKey,signature:i.signature}):!1;throw new LM(`Unable to verify signature envelope of type "${i.type}".`)}var FM=class extends P{constructor({envelope:e}){super(`Unable to coerce value (\`${mf(e)}\`) to a valid signature envelope.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureEnvelope.CoercionError`})}},IM=class extends P{constructor({reason:e,serialized:t}){super(`Unable to deserialize signature envelope: ${e}`,{metaMessages:[`Serialized: ${t}`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureEnvelope.InvalidSerializedError`})}},LM=class extends P{constructor(){super(...arguments),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureEnvelope.VerificationError`})}};Qd(),pp();function RM(e){return e.startsWith(`tempo`)?zM(e).address:e}function zM(e){if(!e.startsWith(`tempox`))throw new BM({address:e});let t=e.slice(6);return Uf(t,{strict:!0}),{address:mg(t)}}var BM=class extends P{constructor({address:e}){super(`Tempo address "${e}" has an invalid prefix. Expected "tempox".`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TempoAddress.InvalidPrefixError`})}};pp();function VM(e,t={}){if(typeof e.chainId==`string`)return HM(e);let n={...e,address:RM(e.address)};return t.signature?{...n,signature:t.signature}:n}function HM(e){let{address:t,chainId:n,nonce:r}=e,i=AM(e.signature);return{address:t,chainId:Number(n),nonce:BigInt(r),signature:i}}function UM(e){return e.map(e=>HM(e))}function WM(e){let[t,n,r,i]=e,a={address:n,chainId:t===`0x`?0:Number(t),nonce:r===`0x`?0n:BigInt(r)};return i&&(a.signature=OM(i)),VM(a)}function GM(e){let t=[];for(let n of e)t.push(WM(n));return t}function KM(e){let{address:t,chainId:n,nonce:r,signature:i}=e;return{address:t,chainId:F(n),nonce:F(r),signature:NM(i)}}function qM(e){return e.map(e=>KM(e))}function JM(e){let{address:t,chainId:n,nonce:r}=e,i=e.signature?MM(e.signature):void 0;return[n?F(n):`0x`,t,r?F(r):`0x`,...i?[i]:[]]}function YM(e){if(!e||e.length===0)return[];let t=[];for(let n of e)t.push(JM(n));return t}pp();function XM(e,t={}){if(`keyId`in e)return ZM(e);let n=e,r={...n,address:RM(n.address),...n.limits?{limits:n.limits.map(e=>({...e,token:RM(e.token)}))}:{}};return t.signature?{...r,signature:kM(t.signature)}:r}function ZM(e){let{chainId:t,keyId:n,expiry:r=0,limits:i,keyType:a}=e,o=AM(e.signature);return{address:n,chainId:t===`0x`?0n:ep(t),expiry:Number(r),limits:i?.map(e=>({token:e.token,limit:BigInt(e.limit)})),signature:o,type:a}}function QM(e){let[t,n]=e,[r,i,a,o,s]=t,c=(()=>{switch(i){case`0x`:case`0x00`:return`secp256k1`;case`0x01`:return`p256`;case`0x02`:return`webAuthn`;default:throw Error(`Invalid key type: ${i}`)}})(),l={address:a,expiry:o===void 0?void 0:iN(o),type:c,chainId:r===`0x`?0n:ep(r),...o===void 0?{}:{expiry:iN(o)},...s===void 0?{}:{limits:s.map(([e,t])=>({token:e,limit:rN(t)}))}};return n&&(l.signature=OM(n)),XM(l)}function $M(e){let{address:t,chainId:n,expiry:r,limits:i,type:a,signature:o}=e;return{chainId:n===0n?`0x`:F(n),expiry:typeof r==`number`?F(r):null,limits:i?.map(({token:e,limit:t})=>({token:e,limit:F(t)})),keyId:RM(t),signature:NM(o),keyType:a}}function eN(e){let{address:t,chainId:n,expiry:r,limits:i}=e,a=e.signature?MM(e.signature):void 0,o=(()=>{switch(e.type){case`secp256k1`:return`0x`;case`p256`:return`0x01`;case`webAuthn`:return`0x02`;default:throw Error(`Invalid key type: ${e.type}`)}})(),s=i?.map(e=>[e.token,tN(e.limit)]);return[[tN(n),o,t,typeof r==`number`||s?nN(r??0):void 0,s].filter(Boolean),...a?[a]:[]]}function tN(e){return e===0n?`0x`:F(e)}function nN(e){return e===0?`0x`:F(e)}function rN(e){return e===`0x`?0n:BigInt(e)}function iN(e){return e===`0x`?0:tp(e)}pp();var aN=`0x20c0`;function oN(e){if(typeof e==`string`){let t=RM(e);return pg(t),t}return Wf(aN,F(e,{size:18}))}var sN={legacy:`0x0`,eip2930:`0x1`,eip1559:`0x2`,eip4844:`0x3`,eip7702:`0x4`},cN={"0x0":`legacy`,"0x1":`eip2930`,"0x2":`eip1559`,"0x3":`eip4844`,"0x4":`eip7702`};function lN(e,t={}){if(!e)return null;let n=w_(e),r={...e,...n};return r.blockNumber=e.blockNumber?BigInt(e.blockNumber):null,r.data=e.input,r.gas=BigInt(e.gas??0n),r.nonce=BigInt(e.nonce??0n),r.transactionIndex=e.transactionIndex?Number(e.transactionIndex):null,r.value=BigInt(e.value??0n),e.authorizationList&&(r.authorizationList=H_(e.authorizationList)),e.chainId&&(r.chainId=Number(e.chainId)),e.gasPrice&&(r.gasPrice=BigInt(e.gasPrice)),e.maxFeePerBlobGas&&(r.maxFeePerBlobGas=BigInt(e.maxFeePerBlobGas)),e.maxFeePerGas&&(r.maxFeePerGas=BigInt(e.maxFeePerGas)),e.maxPriorityFeePerGas&&(r.maxPriorityFeePerGas=BigInt(e.maxPriorityFeePerGas)),e.type&&(r.type=cN[e.type]??e.type),n&&(r.v=N_(n.yParity)),r}var uN={...sN,tempo:`0x76`},dN={...cN,"0x76":`tempo`};function fN(e,t={}){if(!e)return null;let n=lN(e);return n.type=dN[e.type],e.aaAuthorizationList&&(n.authorizationList=UM(e.aaAuthorizationList),delete n.aaAuthorizationList),e.calls&&(n.calls=e.calls.map(e=>({to:e.to,value:e.value&&e.value!==`0x`?BigInt(e.value):void 0,data:e.input||e.data||`0x`}))),e.feeToken&&(n.feeToken=e.feeToken),e.nonceKey&&(n.nonceKey=BigInt(e.nonceKey)),e.signature&&(n.signature=AM(e.signature)),e.validAfter&&(n.validAfter=Number(e.validAfter)),e.validBefore&&(n.validBefore=Number(e.validBefore)),e.keyAuthorization&&(n.keyAuthorization=ZM(e.keyAuthorization)),e.feePayerSignature&&(n.feePayerSignature=D_(e.feePayerSignature),n.feePayerSignature.v=N_(n.feePayerSignature.yParity)),n}pp();function pN(e){let t={};return e.accessList!==void 0&&(t.accessList=e.accessList),e.authorizationList!==void 0&&(t.authorizationList=W_(e.authorizationList)),e.blobVersionedHashes!==void 0&&(t.blobVersionedHashes=e.blobVersionedHashes),e.blobs!==void 0&&(t.blobs=e.blobs),e.chainId!==void 0&&(t.chainId=F(e.chainId)),e.data===void 0?e.input!==void 0&&(t.data=e.input,t.input=e.input):(t.data=e.data,t.input=e.data),e.from!==void 0&&(t.from=e.from),e.gas!==void 0&&(t.gas=F(e.gas)),e.gasPrice!==void 0&&(t.gasPrice=F(e.gasPrice)),e.maxFeePerBlobGas!==void 0&&(t.maxFeePerBlobGas=F(e.maxFeePerBlobGas)),e.maxFeePerGas!==void 0&&(t.maxFeePerGas=F(e.maxFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(t.maxPriorityFeePerGas=F(e.maxPriorityFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(t.maxPriorityFeePerGas=F(e.maxPriorityFeePerGas)),e.nonce!==void 0&&(t.nonce=F(e.nonce)),e.to!==void 0&&(t.to=e.to),e.type!==void 0&&(t.type=sN[e.type]||e.type),e.value!==void 0&&(t.value=F(e.value)),t}pp();function mN(e){let t=pN({...e,authorizationList:void 0});e.authorizationList&&(t.authorizationList=qM(e.authorizationList)),e.calls&&(t.calls=e.calls.map(e=>({to:e.to?RM(e.to):e.to,value:e.value?F(e.value):`0x`,data:e.data??`0x`}))),e.feeToken!==void 0&&(t.feeToken=oN(e.feeToken)),e.keyAuthorization&&(t.keyAuthorization=$M(e.keyAuthorization)),e.validBefore!==void 0&&(t.validBefore=F(e.validBefore)),e.validAfter!==void 0&&(t.validAfter=F(e.validAfter));let n=(()=>{if(e.nonceKey===`random`)return Zf(6);if(typeof e.nonceKey==`bigint`)return F(e.nonceKey)})();return n&&(t.nonceKey=n),(e.calls!==void 0||e.feeToken!==void 0||e.keyAuthorization!==void 0||e.nonceKey!==void 0||e.validBefore!==void 0||e.validAfter!==void 0||e.type===`tempo`)&&(t.type=uN.tempo,delete t.data,delete t.input,delete t.to,delete t.value),t}Qd(),pp();function hN(e){let t=[];for(let n=0;neg(e)?e:$f(e))})}return t}function gN(e){if(!e||e.length===0)return[];let t=[];for(let{address:n,storageKeys:r}of e){for(let e=0;et===void 0?e:void 0).filter(Boolean);super(`Invalid serialized transaction of type "${n}" was provided.`,{metaMessages:[`Serialized Transaction: "${t}"`,r.length>0?`Missing Attributes: ${r.join(`, `)}`:``].filter(Boolean)}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TransactionEnvelope.InvalidSerializedError`})}},wN=class extends P{constructor({maxPriorityFeePerGas:e,maxFeePerGas:t}={}){super([`The provided tip (\`maxPriorityFeePerGas\`${e?` = ${bN(e)} gwei`:``}) cannot be higher than the fee cap (\`maxFeePerGas\`${t?` = ${bN(t)} gwei`:``}).`].join(` +`)),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TransactionEnvelope.TipAboveFeeCapError`})}};Qd(),pp();var TN=`0x78`,EN=`0x76`,DN=`tempo`;function ON(e){let{calls:t,chainId:n,maxFeePerGas:r,maxPriorityFeePerGas:i,validBefore:a,validAfter:o}=e;if(!t||t.length===0)throw new FN;if(typeof a==`number`&&typeof o==`number`&&a<=o)throw new IN({validBefore:a,validAfter:o});if(t)for(let e of t)e.to&&pg(e.to,{strict:!1});if(n<=0)throw new SN({chainId:n});if(r&&BigInt(r)>2n**256n-1n)throw new xN({feeCap:r});if(i&&r&&i>r)throw new wN({maxFeePerGas:r,maxPriorityFeePerGas:i})}function kN(e){let t=u_(I(e,1)),[n,r,i,a,o,s,c,l,u,d,f,p,m,h,g]=t,_=Array.isArray(h)?h:void 0,v=_?g:h;if(!(t.length===13||t.length===14||t.length===15))throw new CN({attributes:{authorizationList:m,chainId:n,maxPriorityFeePerGas:r,maxFeePerGas:i,gas:a,calls:o,accessList:s,keyAuthorization:_,nonceKey:c,nonce:l,validBefore:u,validAfter:d,feeToken:f,feePayerSignatureOrSender:p,...t.length>12?{signature:v}:{}},serialized:e,type:DN});let y={chainId:Number(n),type:DN};rp(a)&&a!==`0x`&&(y.gas=BigInt(a)),rp(l)&&(y.nonce=l===`0x`?0n:BigInt(l)),rp(i)&&i!==`0x`&&(y.maxFeePerGas=BigInt(i)),rp(r)&&r!==`0x`&&(y.maxPriorityFeePerGas=BigInt(r)),rp(c)&&(y.nonceKey=c===`0x`?0n:BigInt(c)),rp(u)&&u!==`0x`&&(y.validBefore=Number(u)),rp(d)&&d!==`0x`&&(y.validAfter=Number(d)),rp(f)&&f!==`0x`&&(y.feeToken=f),o&&o!==`0x`&&(y.calls=o.map(e=>{let[t,n,r]=e,i={};return t&&t!==`0x`&&(i.to=t),n&&n!==`0x`&&(i.value=BigInt(n)),r&&r!==`0x`&&(i.data=r),i})),s?.length!==0&&s!==`0x`&&(y.accessList=hN(s)),m?.length!==0&&m!==`0x`&&(y.authorizationList=GM(m)),p!==`0x`&&p!==void 0&&(p===`0x00`||vg(p)?(y.feePayerSignature=null,vg(p)&&(y.from=p)):y.feePayerSignature=O_(p)),_&&(y.keyAuthorization=QM(_));let b=v?OM(v):void 0;if(b&&(y={...y,signature:b}),!y.from&&b)try{y.from=EM({payload:MN(AN(y)),signature:b,root:!0})}catch{}return ON(y),y}function AN(e,t={}){let{feePayerSignature:n,signature:r}=t,i=typeof e==`string`?kN(e):e;return i.from&&=RM(i.from),i.calls&&=i.calls.map(e=>({...e,...e.to?{to:RM(e.to)}:{}})),ON(i),{...i,...r?{signature:kM(r)}:{},...n?{feePayerSignature:T_(n)}:{},type:`tempo`}}function jN(e,t={}){let{accessList:n,authorizationList:r,calls:i,chainId:a,feeToken:o,gas:s,keyAuthorization:c,nonce:l,nonceKey:u,maxFeePerGas:d,maxPriorityFeePerGas:f,validBefore:p,validAfter:m}=e;ON(e);let h=gN(n),g=t.signature||e.signature,_=YM(r),v=i.map(e=>[e.to?RM(e.to):`0x`,e.value?F(e.value):`0x`,e.data??`0x`]),y=!1,b=(()=>{if(t.sender)return t.sender;if(t.format===`feePayer`&&g){let t=kM(g);if(t.type===`keychain`)return t.userAddress;if(t.type===`p256`||t.type===`webAuthn`)return gg(t.publicKey);if(t.type===`secp256k1`)return G_({payload:MN(AN(e)),signature:t.signature})}let n=t.feePayerSignature===void 0?e.feePayerSignature:t.feePayerSignature;return n===null?(y=!0,`0x00`):n?j_(n):`0x`})(),x=[F(a),f?F(f):`0x`,d?F(d):`0x`,s?F(s):`0x`,v,h,u?F(u):`0x`,l?F(l):`0x`,typeof p==`number`?F(p):`0x`,typeof m==`number`?F(m):`0x`,!y&&(typeof o==`bigint`||typeof o==`string`)?oN(o):`0x`,b,_,...c?[eN(c)]:[],...g?[MM(kM(g))]:[]];return Wf(t.format===`feePayer`?TN:EN,g_(x))}function MN(e,t={}){let n=NN(e,{presign:!0});return t.from?Qh(Wf(`0x04`,n,RM(t.from))):n}function NN(e,t={}){return Qh(jN({...e,...t.presign?{signature:void 0,...e.feePayerSignature===void 0?{}:{feePayerSignature:null}}:{}}))}function PN(e,t){let n=RM(t.sender);return Qh(jN({...e,signature:void 0},{sender:n,format:`feePayer`}))}var FN=class extends P{constructor(){super(`Calls list cannot be empty.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TxEnvelopeTempo.CallsEmptyError`})}},IN=class extends P{constructor({validBefore:e,validAfter:t}){super(`validBefore (${e}) must be greater than validAfter (${t}).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TxEnvelopeTempo.InvalidValidityWindowError`})}};pp();function LN(e){let t=e.account;return t?.keyType&&t.keyType!==`secp256k1`||e.calls!==void 0||e.feePayer!==void 0||e.feeToken!==void 0||e.keyAuthorization!==void 0||e.nonceKey!==void 0||e.signature!==void 0||e.validBefore!==void 0||e.validAfter!==void 0?`tempo`:e.type?e.type:Nd(e)}function RN(e){try{return LN(e)===`tempo`}catch{return!1}}async function zN(e,t){if(!RN(e)){if(t&&`type`in t&&t.type!==`secp256k1`)throw Error("Unsupported signature type. Expected `secp256k1` but got `"+t.type+"`.");if(t&&`type`in t){let{r:n,s:r,yParity:i}=t?.signature;return mh(e,{r:F(n,{size:32}),s:F(r,{size:32}),yParity:i})}return mh(e,t)}if(LN(e)===`tempo`)return BN(e,t);throw Error(`Unsupported transaction type`)}async function BN(e,t){let n=(()=>{if(e.signature)return e.signature;if(t&&`type`in t)return t;if(t)return kM({r:BigInt(t.r),s:BigInt(t.s),yParity:Number(t.yParity)})})(),{chainId:r,feePayer:i,feePayerSignature:a,nonce:o,...s}=e,c={...s,calls:s.calls?.length?s.calls:[{to:s.to||(!s.data||s.data===`0x`?`0x0000000000000000000000000000000000000000`:void 0),value:s.value,data:s.data}],chainId:Number(r),feePayerSignature:a?{r:BigInt(a.r),s:BigInt(a.s),yParity:Number(a.yParity)}:i?null:void 0,type:`tempo`,...o?{nonce:BigInt(o)}:{}};if(i===!0&&delete c.feeToken,n&&typeof e.feePayer==`object`){let t=AN(c,{signature:n}),r=PN(t,{sender:(()=>{if(e.from)return e.from;if(n.type===`secp256k1`)return G_({payload:MN(t),signature:n.signature});throw Error(`Unable to extract sender from transaction or signature.`)})()});return jN(t,{feePayerSignature:T_(await e.feePayer.sign({hash:r}))})}return i===!0?n?jN(c,{format:`feePayer`,sender:e.from,signature:n}):jN(c,{feePayerSignature:null}):jN({...c,...i?{feeToken:void 0}:{}},{feePayerSignature:void 0,signature:n})}pp(),oa(),Ku();function VN(e){if(!RN(e))return sd(e);let{feePayerSignature:t,gasPrice:n,nonce:r,...i}=fN(e);return{...i,accessList:i.accessList,feePayerSignature:t?{r:F(t.r,{size:32}),s:F(t.s,{size:32}),v:BigInt(t.v??27),yParity:t.yParity}:void 0,nonce:Number(r),typeHex:uN[i.type],type:i.type}}function HN(e){return Gm(e)}function UN(e,t){let n=e,r=n.account?aa(n.account):void 0;if(!RN(n))return Hu(e,t);t&&(n.calls=n.calls??[{to:e.to||(!e.data||e.data===`0x`?`0x0000000000000000000000000000000000000000`:void 0),value:e.value,data:e.data}]),n.feePayer===!0&&delete n.feeToken;let i=mN({...n,type:`tempo`});t===`estimateGas`&&(i.maxFeePerGas=void 0,i.maxPriorityFeePerGas=void 0),i.to=void 0,i.data=void 0,i.value=void 0;let[a,o]=(()=>{let e=r&&`keyType`in r?r.keyType:r?.source;return e?e===`webAuthn`?[`webAuthn`,`0x${`ff`.repeat(1400)}`]:[`p256`,`secp256k1`].includes(e)?[e,void 0]:[void 0,void 0]:[void 0,void 0]})(),s=r&&`accessKeyAddress`in r?r.accessKeyAddress:void 0;return{...i,...o?{keyData:o}:{},...s?{keyId:s}:{},...a?{keyType:a}:{},...n.feePayer?{feePayer:typeof n.feePayer==`object`?aa(n.feePayer):n.feePayer}:{}}}var WN=new Map;async function GN(e){WN.set(e,(WN.get(e)??0)+1),await Promise.resolve();let t=(WN.get(e)??0)>1;return queueMicrotask(()=>{let t=WN.get(e)??0;t<=1?WN.delete(e):WN.set(e,t-1)}),t}$u(),Ku();var KN=25,qN={blockTime:1e3,extendSchema:Dh(),formatters:{transaction:cd({exclude:[`aaAuthorizationList`],format:VN}),transactionReceipt:Km({format:HN}),transactionRequest:Gu({format:UN})},prepareTransactionRequest:[async(e,{phase:t})=>{let n=e;return t===`afterFillParameters`?(n.feePayer&&n.keyAuthorization?.signature.type===`webAuthn`&&(n.gas=(n.gas??0n)+20000n),n):(await(async()=>{if(n.nonceKey===`expiring`||n.feePayer&&n.nonceKey===void 0)return!0;let e=n.account?.address;return e&&n.nonceKey===void 0?await GN(e):!1})()?(n.nonceKey=Qu,n.nonce=0,n.validBefore===void 0&&(n.validBefore=Math.floor(Date.now()/1e3)+KN)):n.nonceKey!==void 0&&(n.nonce=typeof n.nonce==`number`?n.nonce:0),!n.feeToken&&n.chain?.feeToken&&(n.feeToken=n.chain.feeToken),n)},{runAt:[`beforeFillTransaction`,`afterFillParameters`]}],serializers:{transaction:((e,t)=>zN(e,t))},async verifyHash(e,t){let{address:n,hash:r,signature:i}=t;if(typeof i==`string`&&i.endsWith(`7777777777777777777777777777777777777777777777777777777777777777`)){let a=OM(i);if(a.type!==`keychain`){let i=await ih(e,{address:n,blockNumber:t.blockNumber,blockTag:t.blockTag});if(!i||i===`0xef01007702c00000000000000000000000000000000000`)return PM(a,{address:n,payload:r})}}return await T(e,vv,`verifyHash`)({...t,chain:null})}},JN=L({...qN,id:4217,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.tempo.xyz`}},name:`Tempo Mainnet`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.presto.tempo.xyz`],webSocket:[`wss://rpc.presto.tempo.xyz`]}}}),YN=L({...qN,id:42429,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.testnet.tempo.xyz`}},name:`Tempo Testnet (Andantino)`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.testnet.tempo.xyz`],webSocket:[`wss://rpc.testnet.tempo.xyz`]}}}),XN=L({...qN,id:31318,name:`Tempo Devnet`,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.devnet.tempo.xyz`}},nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.devnet.tempoxyz.dev`],webSocket:[`wss://rpc.devnet.tempoxyz.dev`]}}}),ZN=L({...qN,id:1337,name:`Tempo`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`http://localhost:8545`]}}}),QN=L({...qN,id:42431,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.moderato.tempo.xyz`}},name:`Tempo Testnet (Moderato)`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.moderato.tempo.xyz`],webSocket:[`wss://rpc.moderato.tempo.xyz`]}}}),$N=L({id:1559,name:`Tenet`,network:`tenet-mainnet`,nativeCurrency:{name:`TENET`,symbol:`TENET`,decimals:18},rpcUrls:{default:{http:[`https://rpc.tenet.org`]}},blockExplorers:{default:{name:`TenetScan Mainnet`,url:`https://tenetscan.io`,apiUrl:`https://tenetscan.io/api`}},testnet:!1}),eP=L({id:752025,name:`Ternoa`,nativeCurrency:{name:`Capsule Coin`,symbol:`CAPS`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.zkevm.ternoa.network`]}},blockExplorers:{default:{name:`Ternoa Explorer`,url:`https://explorer-mainnet.zkevm.ternoa.network`}},testnet:!1}),tP=L({id:7,name:`ThaiChain`,nativeCurrency:{name:`TCH`,symbol:`TCH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.thaichain.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp.thaichain.org`,apiUrl:`https://exp.thaichain.org/api`}},contracts:{multicall3:{address:`0x0DaD6130e832c21719C5CE3bae93454E16A84826`,blockCreated:4806386}},testnet:!1}),nP=L({id:8428,name:`THAT Mainnet`,nativeCurrency:{name:`THAT`,symbol:`THAT`,decimals:18},rpcUrls:{default:{http:[`https://api.thatchain.io/mainnet`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://that.blockscout.com`}},testnet:!1}),rP=L({id:361,name:`Theta Mainnet`,nativeCurrency:{name:`TFUEL`,symbol:`TFUEL`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-api.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`Theta Explorer`,url:`https://explorer.thetatoken.org`}},testnet:!1}),iP=L({id:365,name:`Theta Testnet`,nativeCurrency:{name:`TFUEL`,symbol:`TFUEL`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-api-testnet.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`Theta Explorer`,url:`https://testnet-explorer.thetatoken.org`}},testnet:!0}),aP=L({id:108,name:`ThunderCore Mainnet`,nativeCurrency:{name:`TT`,symbol:`TT`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.thundercore.com`]}},blockExplorers:{default:{name:`ThunderCore Explorer`,url:`https://explorer-mainnet.thundercore.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!1}),oP=L({id:997,name:`5ireChain Thunder Testnet`,nativeCurrency:{name:`5ire Token`,symbol:`5IRE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.5ire.network`]}},blockExplorers:{default:{name:`5ireChain Thunder Explorer`,url:`https://testnet.5irescan.io/`}},testnet:!0}),sP=L({id:62092,name:`TikTrix Testnet`,nativeCurrency:{name:`tTTX`,symbol:`tTTX`,decimals:18},rpcUrls:{default:{http:[`https://tiktrix-rpc.xyz`]}},blockExplorers:{default:{name:`TikTrix Testnet Explorer`,url:`https://tiktrix.xyz`}},testnet:!0}),cP=L({id:6969,name:`Tomb Mainnet`,nativeCurrency:{name:`TOMB`,symbol:`TOMB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.tombchain.com`]}},blockExplorers:{default:{name:`Tomb Explorer`,url:`https://tombscout.com`}},testnet:!1}),lP=L({...xy,id:61166,name:`Treasure`,nativeCurrency:{decimals:18,name:`MAGIC`,symbol:`MAGIC`},rpcUrls:{default:{http:[`https://rpc.treasure.lol`],webSocket:[`wss://rpc.treasure.lol/ws`]}},blockExplorers:{default:{name:`Treasure Block Explorer`,url:`https://treasurescan.io`}},contracts:{multicall3:{address:`0x2e29fe39496a56856D8698bD43e1dF4D0CE6266a`,blockCreated:101}},testnet:!1}),uP=L({...xy,id:978658,name:`Treasure Topaz Testnet`,nativeCurrency:{decimals:18,name:`MAGIC`,symbol:`MAGIC`},rpcUrls:{default:{http:[`https://rpc.topaz.treasure.lol`],webSocket:[`wss://rpc.topaz.treasure.lol/ws`]}},blockExplorers:{default:{name:`Treasure Topaz Block Explorer`,url:`https://topaz.treasurescan.io`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:108112}},testnet:!0}),dP=L({id:728126428,name:`Tron`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://api.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://tronscan.org`,apiUrl:`https://apilist.tronscanapi.com/api`}}}),fP=L({id:3448148188,name:`Tron Nile`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://nile.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://nile.tronscan.org`}},testnet:!0}),pP=L({id:2494104990,name:`Tron Shasta`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://api.shasta.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://shasta.tronscan.org`}},testnet:!0}),mP=L({id:8,name:`Ubiq Mainnet`,nativeCurrency:{name:`UBQ`,symbol:`UBQ`,decimals:18},rpcUrls:{default:{http:[`https://pyrus2.ubiqscan.io`]}},blockExplorers:{default:{name:`Ubiq Scan`,url:`https://ubiqscan.io`}},testnet:!1}),hP=L({id:19991,name:`Ultra EVM`,nativeCurrency:{decimals:18,name:`Ultra Token`,symbol:`UOS`},rpcUrls:{default:{http:[`https://evm.ultra.eosusa.io`]}},blockExplorers:{default:{name:`Ultra EVM Explorer`,url:`https://evmexplorer.ultra.io`}}}),gP=L({id:18881,name:`Ultra EVM Testnet`,nativeCurrency:{decimals:18,name:`Ultra Token`,symbol:`UOS`},rpcUrls:{default:{http:[`https://evm.test.ultra.eosusa.io`]}},blockExplorers:{default:{name:`Ultra EVM Testnet Explorer`,url:`https://evmexplorer.testnet.ultra.io`}},testnet:!0}),_P=L({id:1231,name:`Ultron Mainnet`,nativeCurrency:{name:`ULX`,symbol:`ULX`,decimals:18},rpcUrls:{default:{http:[`https://ultron-rpc.net`]}},blockExplorers:{default:{name:`Ultron Scan`,url:`https://ulxscan.com`}},testnet:!1}),vP=L({id:1230,name:`Ultron Testnet`,nativeCurrency:{name:`ULX`,symbol:`ULX`,decimals:18},rpcUrls:{default:{http:[`https://ultron-dev.io`]}},blockExplorers:{default:{name:`Ultron Scan`,url:`https://explorer.ultron-dev.io`}},testnet:!0}),yP=1,bP=L({...R,id:130,name:`Unichain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://mainnet.unichain.org/`]}},blockExplorers:{default:{name:`Uniscan`,url:`https://uniscan.xyz`,apiUrl:`https://api.uniscan.xyz/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[yP]:{address:`0x2F12d621a16e2d3285929C9996f478508951dFe4`}},portal:{[yP]:{address:`0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2`}},l1StandardBridge:{[yP]:{address:`0x81014F44b0a345033bB2b3B21C7a1A308B35fEeA`}}},sourceId:yP}),xP=11155111,SP=L({...R,id:1301,name:`Unichain Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://sepolia.unichain.org`]}},blockExplorers:{default:{name:`Uniscan`,url:`https://sepolia.uniscan.xyz`,apiUrl:`https://api-sepolia.uniscan.xyz/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},portal:{[xP]:{address:`0x0d83dab629f0e0F9d36c0Cbc89B69a489f0751bD`}},l1StandardBridge:{[xP]:{address:`0xea58fcA6849d79EAd1f26608855c2D6407d54Ce2`}},disputeGameFactory:{[xP]:{address:`0xeff73e5aa3B9AEC32c659Aa3E00444d20a84394b`}}},testnet:!0,sourceId:xP}),CP=L({id:8880,name:`Unique Mainnet`,nativeCurrency:{decimals:18,name:`UNQ`,symbol:`UNQ`},rpcUrls:{default:{http:[`https://rpc.unique.network`]}},blockExplorers:{default:{name:`Unique Subscan`,url:`https://unique.subscan.io/`}}}),wP=L({id:8882,name:`Opal Testnet`,nativeCurrency:{decimals:18,name:`OPL`,symbol:`OPL`},rpcUrls:{default:{http:[`https://rpc-opal.unique.network`]}},blockExplorers:{default:{name:`Opal Subscan`,url:`https://opal.subscan.io/`}},testnet:!0}),TP=L({id:8881,name:`Quartz Mainnet`,nativeCurrency:{decimals:18,name:`QTZ`,symbol:`QTZ`},rpcUrls:{default:{http:[`https://rpc-quartz.unique.network`]}},blockExplorers:{default:{name:`Quartz Subscan`,url:`https://quartz.subscan.io/`}}}),EP=L({id:18233,name:`Unreal`,nativeCurrency:{name:`reETH`,decimals:18,symbol:`reETH`},rpcUrls:{default:{http:[`https://rpc.unreal-orbit.gelato.digital`]}},blockExplorers:{default:{name:`Unreal Explorer`,url:`https://unreal.blockscout.com`,apiUrl:`https://unreal.blockscout.com/api/v2`}},testnet:!0,contracts:{multicall3:{address:`0x8b6B0e60D8CD84898Ea8b981065A12F876eA5677`,blockCreated:1745}}}),DP=L({id:1480,name:`Vana`,blockTime:6e3,nativeCurrency:{decimals:18,name:`Vana`,symbol:`VANA`},rpcUrls:{default:{http:[`https://rpc.vana.org/`]}},blockExplorers:{default:{name:`Vana Block Explorer`,url:`https://vanascan.io`,apiUrl:`https://vanascan.io/api`}},contracts:{multicall3:{address:`0xD8d2dFca27E8797fd779F8547166A2d3B29d360E`,blockCreated:716763}}}),OP=L({id:14800,name:`Vana Moksha Testnet`,blockTime:6e3,nativeCurrency:{decimals:18,name:`Vana`,symbol:`VANA`},rpcUrls:{default:{http:[`https://rpc.moksha.vana.org`]}},blockExplorers:{default:{name:`Vana Moksha Testnet`,url:`https://moksha.vanascan.io`,apiUrl:`https://moksha.vanascan.io/api`}},contracts:{multicall3:{address:`0xD8d2dFca27E8797fd779F8547166A2d3B29d360E`,blockCreated:732283}},testnet:!0}),kP=L({id:2040,name:`Vanar Mainnet`,nativeCurrency:{name:`VANRY`,symbol:`VANRY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.vanarchain.com`]}},blockExplorers:{default:{name:`Vanar Mainnet Explorer`,url:`https://explorer.vanarchain.com/`}},testnet:!1}),AP=L({id:100009,name:`Vechain`,nativeCurrency:{name:`VeChain`,symbol:`VET`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.vechain.org`]}},blockExplorers:{default:{name:`Vechain Explorer`,url:`https://explore.vechain.org`},vechainStats:{name:`Vechain Stats`,url:`https://vechainstats.com`}}}),jP=L({id:106,name:`Velas EVM Mainnet`,nativeCurrency:{name:`VLX`,symbol:`VLX`,decimals:18},rpcUrls:{default:{http:[`https://evmexplorer.velas.com/rpc`]}},blockExplorers:{default:{name:`Velas Explorer`,url:`https://evmexplorer.velas.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:55883577}},testnet:!1}),MP=L({id:88,name:`Viction`,nativeCurrency:{name:`Viction`,symbol:`VIC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.viction.xyz`]}},blockExplorers:{default:{name:`VIC Scan`,url:`https://vicscan.xyz`}},testnet:!1}),NP=L({id:89,name:`Viction Testnet`,nativeCurrency:{name:`Viction`,symbol:`VIC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.viction.xyz`]}},blockExplorers:{default:{name:`VIC Scan`,url:`https://testnet.vicscan.xyz`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:12170179}},testnet:!0}),PP=L({id:888888,name:`Vision`,nativeCurrency:{name:`VISION`,symbol:`VS`,decimals:18},rpcUrls:{default:{http:[`https://infragrid.v.network/ethereum/compatible`]}},blockExplorers:{default:{name:`Vision Scan`,url:`https://visionscan.org`}},testnet:!1}),FP=L({id:666666,name:`Vision Testnet`,nativeCurrency:{name:`VISION`,symbol:`VS`,decimals:18},rpcUrls:{default:{http:[`https://vpioneer.infragrid.v.network/ethereum/compatible`]}},blockExplorers:{default:{name:`Vision Scan`,url:`https://visionscan.org/?chain=vpioneer`}},testnet:!0}),IP=L({id:888,name:`Wanchain`,nativeCurrency:{name:`WANCHAIN`,symbol:`WAN`,decimals:18},rpcUrls:{default:{http:[`https://gwan-ssl.wandevs.org:56891`,`https://gwan2-ssl.wandevs.org`]}},blockExplorers:{default:{name:`WanScan`,url:`https://wanscan.org`}},contracts:{multicall3:{address:`0xcDF6A1566e78EB4594c86Fe73Fcdc82429e97fbB`,blockCreated:25312390}}}),LP=L({id:999,name:`Wanchain Testnet`,nativeCurrency:{name:`WANCHAIN`,symbol:`WANt`,decimals:18},rpcUrls:{default:{http:[`https://gwan-ssl.wandevs.org:46891`]}},blockExplorers:{default:{name:`WanScanTest`,url:`https://wanscan.org`}},contracts:{multicall3:{address:`0x11c89bF4496c39FB80535Ffb4c92715839CC5324`,blockCreated:24743448}},testnet:!0}),RP=L({id:9496,name:`WeaveVM Alphanet`,nativeCurrency:{name:`Testnet WeaveVM`,symbol:`tWVM`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.wvm.dev`]}},blockExplorers:{default:{name:`WeaveVM Alphanet Explorer`,url:`https://explorer.wvm.dev`}},testnet:!0}),zP=L({id:1111,name:`WEMIX`,network:`wemix-mainnet`,nativeCurrency:{name:`WEMIX`,symbol:`WEMIX`,decimals:18},rpcUrls:{default:{http:[`https://api.wemix.com`]}},blockExplorers:{default:{name:`wemixExplorer`,url:`https://explorer.wemix.com`}}}),BP=L({id:1112,name:`WEMIX Testnet`,network:`wemix-testnet`,nativeCurrency:{name:`WEMIX`,symbol:`tWEMIX`,decimals:18},rpcUrls:{default:{http:[`https://api.test.wemix.com`]}},blockExplorers:{default:{name:`wemixExplorer`,url:`https://testnet.wemixscan.com`,apiUrl:`https://testnet.wemixscan.com/api`}},testnet:!0}),VP=L({id:420420421,name:`Westend Asset Hub`,nativeCurrency:{decimals:18,name:`Westies`,symbol:`WND`},rpcUrls:{default:{http:[`https://westend-asset-hub-eth-rpc.polkadot.io`]}},blockExplorers:{default:{name:`subscan`,url:`https://westend-asset-hub-eth-explorer.parity.io`}},testnet:!0}),HP=L({testnet:!1,name:`Whitechain`,blockExplorers:{default:{name:`Whitechain Explorer`,url:`https://explorer.whitechain.io`}},id:1875,rpcUrls:{default:{http:[`https://rpc.whitechain.io`]}},nativeCurrency:{decimals:18,name:`WhiteBIT Coin`,symbol:`WBT`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:25212237}}}),UP=L({testnet:!0,name:`Whitechain Testnet`,blockExplorers:{default:{name:`Whitechain Explorer`,url:`https://testnet.whitechain.io`}},id:2625,rpcUrls:{default:{http:[`https://rpc-testnet.whitechain.io`]}},nativeCurrency:{decimals:18,name:`WhiteBIT Coin`,symbol:`WBT`}}),WP=L({id:42070,name:`WMC Testnet`,nativeCurrency:{name:`WMTx`,symbol:`WMTx`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet-base.worldmobile.net`]}},blockExplorers:{default:{name:`WMC Explorer`,url:`https://explorer2-base-testnet.worldmobile.net`}},testnet:!0}),GP=1,KP=L({...R,id:480,name:`World Chain`,network:`worldchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://worldchain-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Worldscan`,url:`https://worldscan.org`,apiUrl:`https://api.worldscan.org/api`},blockscout:{name:`Blockscout`,url:`https://worldchain-mainnet.explorer.alchemy.com`,apiUrl:`https://worldchain-mainnet.explorer.alchemy.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[GP]:{address:`0x069c4c579671f8c120b1327a73217D01Ea2EC5ea`}},l2OutputOracle:{[GP]:{address:`0x19A6d1E9034596196295CF148509796978343c5D`}},portal:{[GP]:{address:`0xd5ec14a83B7d95BE1E2Ac12523e2dEE12Cbeea6C`}},l1StandardBridge:{[GP]:{address:`0x470458C91978D2d929704489Ad730DC3E3001113`}}},testnet:!1,sourceId:GP}),qP=11155111,JP=L({...R,id:4801,name:`World Chain Sepolia`,network:`worldchain-sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://worldchain-sepolia.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Worldscan Sepolia`,url:`https://sepolia.worldscan.org`,apiUrl:`https://api-sepolia.worldscan.org/api`},blockscout:{name:`Blockscout`,url:`https://worldchain-sepolia.explorer.alchemy.com`,apiUrl:`https://worldchain-sepolia.explorer.alchemy.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[qP]:{address:`0x8Ec1111f67Dad6b6A93B3F42DfBC92D81c98449A`}},l2OutputOracle:{[qP]:{address:`0xc8886f8BAb6Eaeb215aDB5f1c686BF699248300e`}},portal:{[qP]:{address:`0xFf6EBa109271fe6d4237EeeD4bAb1dD9A77dD1A4`}},l1StandardBridge:{[qP]:{address:`0xd7DF54b3989855eb66497301a4aAEc33Dbb3F8DE`}}},testnet:!0,sourceId:qP}),YP=L({id:103,name:`WorldLand Mainnet`,nativeCurrency:{decimals:18,name:`WLC`,symbol:`WLC`},rpcUrls:{default:{http:[`https://seoul.worldland.foundation`]}},blockExplorers:{default:{name:`WorldLand Scan`,url:`https://scan.worldland.foundation`}},testnet:!1}),XP=L({id:660279,name:`Xai Mainnet`,nativeCurrency:{name:`Xai`,symbol:`XAI`,decimals:18},rpcUrls:{default:{http:[`https://xai-chain.net/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.xai-chain.net`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:222549}},testnet:!1}),ZP=L({id:37714555429,name:`Xai Testnet`,nativeCurrency:{name:`sXai`,symbol:`sXAI`,decimals:18},rpcUrls:{default:{http:[`https://testnet-v2.xai-chain.net/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer-v2.xai-chain.net`}},testnet:!0}),QP=L({id:50,name:`XDC Network`,nativeCurrency:{decimals:18,name:`XDC`,symbol:`XDC`},rpcUrls:{default:{http:[`https://rpc.xdcrpc.com`]}},blockExplorers:{default:{name:`XDCScan`,url:`https://xdcscan.com`}},contracts:{multicall3:{address:`0x0B1795ccA8E4eC4df02346a082df54D437F8D9aF`,blockCreated:75884020}}}),$P=L({id:51,name:`Apothem Network`,nativeCurrency:{decimals:18,name:`TXDC`,symbol:`TXDC`},rpcUrls:{default:{http:[`https://erpc.apothem.network`]}},blockExplorers:{default:{name:`XDCScan`,url:`https://testnet.xdcscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:59765389}}}),eF=L({id:1643,name:`XGR Mainnet`,nativeCurrency:{name:`XGR`,symbol:`XGR`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xgr.network`]}},blockExplorers:{default:{name:`XGR Explorer`,url:`https://explorer.xgr.network`}}}),tF=L({id:196,name:`X Layer Mainnet`,nativeCurrency:{decimals:18,name:`OKB`,symbol:`OKB`},rpcUrls:{default:{http:[`https://xlayerrpc.okx.com`]}},blockExplorers:{default:{name:`OKLink`,url:`https://www.oklink.com/xlayer`,apiUrl:`https://www.oklink.com/api/v5/explorer/xlayer/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:47416}}}),nF=L({id:1952,name:`X1 Testnet`,nativeCurrency:{decimals:18,name:`OKB`,symbol:`OKB`},rpcUrls:{default:{http:[`https://xlayertestrpc.okx.com`]}},blockExplorers:{default:{name:`OKLink`,url:`https://www.oklink.com/xlayer-test`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:624344}},testnet:!0}),rF=L({id:3721,name:`Xone Chain Mainnet`,nativeCurrency:{decimals:18,name:`XOC`,symbol:`XOC`},rpcUrls:{default:{http:[`https://rpc.xone.org`]}},blockExplorers:{default:{name:`Xone Mainnet Explorer`,url:`https://xonescan.com`,apiUrl:`http://api.xonescan.com/api`}},testnet:!1}),iF=L({id:33772211,name:`Xone Chain Testnet`,nativeCurrency:{decimals:18,name:`XOC`,symbol:`XOC`},rpcUrls:{default:{http:[`https://rpc-testnet.xone.org`,`https://rpc-testnet.xone.plus`,`https://rpc-testnet.knight.center`]}},blockExplorers:{default:{name:`Xone Testnet Explorer`,url:`https://testnet.xonescan.com`,apiUrl:`http://api.testnet.xonescan.com/api`}},testnet:!0}),aF=L({id:20250217,name:`Xphere Mainnet`,nativeCurrency:{decimals:18,name:`XP`,symbol:`XP`},rpcUrls:{default:{http:[`https://en-bkk.x-phere.com`]}},blockExplorers:{default:{name:`Xphere Tamsa Explorer`,url:`https://xp.tamsa.io`}},testnet:!1}),oF=L({id:1998991,name:`Xphere Testnet`,nativeCurrency:{decimals:18,name:`XPT`,symbol:`XPT`},rpcUrls:{default:{http:[`http://testnet.x-phere.com`]}},blockExplorers:{default:{name:`Xphere Tamsa Explorer`,url:`https://xpt.tamsa.io`}},testnet:!0}),sF=L({id:37,name:`CONX Chain`,nativeCurrency:{decimals:18,name:`XPLA`,symbol:`XPLA`},rpcUrls:{default:{http:[`https://dimension-evm-rpc.xpla.dev`]}},blockExplorers:{default:{name:`CONX Explorer`,url:`https://explorer.conx.xyz`}},testnet:!1}),cF=L({id:273,name:`XR One`,nativeCurrency:{decimals:18,name:`XR1`,symbol:`XR1`},rpcUrls:{default:{http:[`https://xr1.calderachain.xyz/http`],webSocket:[`wss://xr1.calderachain.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://xr1.calderaexplorer.xyz`}},testnet:!1}),lF=L({id:144e4,name:`XRPL EVM`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xrplevm.org`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.xrplevm.org`,apiUrl:`https://explorer.xrplevm.org/api/v2`}},testnet:!1}),uF=L({id:1440002,name:`XRPL EVM Devnet`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xrplevm.org/`]},public:{http:[`https://rpc.xrplevm.org/`]}},blockExplorers:{default:{name:`XRPLEVM Devnet Explorer`,url:`https://explorer.xrplevm.org/`}},contracts:{multicall3:{address:`0x82Cc144D7d0AD4B1c27cb41420e82b82Ad6e9B31`,blockCreated:15237286}},testnet:!0}),dF=L({id:1449e3,name:`XRPL EVM Testnet`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.xrplevm.org`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.testnet.xrplevm.org`,apiUrl:`https://explorer.testnet.xrplevm.org/api/v2`}},contracts:{multicall3:{address:`0x82Cc144D7d0AD4B1c27cb41420e82b82Ad6e9B31`,blockCreated:492302}},testnet:!0}),fF=L({id:2730,name:`XR Sepolia`,nativeCurrency:{decimals:18,name:`tXR`,symbol:`tXR`},rpcUrls:{default:{http:[`https://xr-sepolia-testnet.rpc.caldera.xyz/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://xr-sepolia-testnet.explorer.caldera.xyz`}},testnet:!0}),pF=L({id:50005,name:`Yooldo Verse`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.yooldo-verse.xyz`]}},blockExplorers:{default:{name:`Yooldo Verse Explorer`,url:`https://explorer.yooldo-verse.xyz`}}}),mF=L({id:50006,name:`Yooldo Verse Testnet`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.yooldo-verse.xyz`]}},blockExplorers:{default:{name:`Yooldo Verse Testnet Explorer`,url:`https://explorer.testnet.yooldo-verse.xyz`}},testnet:!0}),hF=L({id:8408,name:`ZenChain Testnet`,nativeCurrency:{decimals:18,name:`ZTC`,symbol:`ZTC`},rpcUrls:{default:{http:[`https://zenchain-testnet.api.onfinality.io/public`],webSocket:[`wss://zenchain-testnet.api.onfinality.io/public-ws`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:230019}},blockExplorers:{default:{name:`Zentrace`,url:`https://zentrace.io`}},testnet:!0}),gF=L({id:383414847825,name:`Zeniq Mainnet`,nativeCurrency:{name:`ZENIQ`,symbol:`ZENIQ`,decimals:18},rpcUrls:{default:{http:[`https://api.zeniq.network`]}},blockExplorers:{default:{name:`Zeniq Explorer`,url:`https://zeniqscan.com`}},testnet:!1}),_F=L({id:543210,name:`Zero Network`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.zerion.io/v1/zero`]}},blockExplorers:{default:{name:`Zero Network Explorer`,url:`https://explorer.zero.network`}},testnet:!1}),vF=L({id:7e3,name:`ZetaChain`,nativeCurrency:{decimals:18,name:`Zeta`,symbol:`ZETA`},rpcUrls:{default:{http:[`https://zetachain-evm.blockpi.network/v1/rpc/public`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1632781}},blockExplorers:{default:{name:`ZetaScan`,url:`https://zetascan.com`}},testnet:!1}),yF=L({id:7001,name:`ZetaChain Athens Testnet`,nativeCurrency:{decimals:18,name:`Zeta`,symbol:`aZETA`},rpcUrls:{default:{http:[`https://zetachain-athens-evm.blockpi.network/v1/rpc/public`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2715217}},blockExplorers:{default:{name:`ZetaScan`,url:`https://testnet.zetascan.com`}},testnet:!0}),bF=L({id:1337803,name:`Zhejiang`,nativeCurrency:{name:`Zhejiang Ether`,symbol:`ZhejETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.zhejiang.ethpandaops.io`]}},blockExplorers:{default:{name:`Beaconchain`,url:`https://zhejiang.beaconcha.in`}},testnet:!0}),xF=L({id:32769,name:`Zilliqa`,network:`zilliqa`,nativeCurrency:{name:`Zilliqa`,symbol:`ZIL`,decimals:18},rpcUrls:{default:{http:[`https://api.zilliqa.com`]}},blockExplorers:{default:{name:`Ethernal`,url:`https://evmx.zilliqa.com`}},testnet:!1}),SF=L({id:33101,name:`Zilliqa Testnet`,network:`zilliqa-testnet`,nativeCurrency:{name:`Zilliqa`,symbol:`ZIL`,decimals:18},rpcUrls:{default:{http:[`https://dev-api.zilliqa.com`]}},blockExplorers:{default:{name:`Ethernal`,url:`https://evmx.testnet.zilliqa.com`}},testnet:!0}),CF=1,wF=L({...R,id:48900,name:`Zircuit Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.zircuit.com`]}},blockExplorers:{default:{name:`Zircuit Explorer`,url:`https://explorer.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},l2OutputOracle:{[CF]:{address:`0x92Ef6Af472b39F1b363da45E35530c24619245A4`}},portal:{[CF]:{address:`0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1`}},l1StandardBridge:{[CF]:{address:`0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8`}}},testnet:!1}),TF=11155111,EF=L({...R,id:48898,name:`Zircuit Garfield Testnet`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://garfield-testnet.zircuit.com/`]}},blockExplorers:{default:{name:`Zircuit Garfield Testnet Explorer`,url:`https://explorer.garfield-testnet.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},l2OutputOracle:{[TF]:{address:`0xd69D3AC5CA686cCF94b258291772bc520FEAf211`}},portal:{[TF]:{address:`0x4E21A71Ac3F7607Da5c06153A17B1DD20E702c21`}},l1StandardBridge:{[TF]:{address:`0x87a7E2bCA9E35BA49282E832a28A6023904460D8`}}},testnet:!0}),DF=L({id:42766,name:`ZKFair Mainnet`,network:`zkfair-mainnet`,nativeCurrency:{decimals:18,name:`USD Coin`,symbol:`USDC`},rpcUrls:{default:{http:[`https://rpc.zkfair.io`]}},blockExplorers:{default:{name:`zkFair Explorer`,url:`https://scan.zkfair.io`,apiUrl:`https://scan.zkfair.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6090959}},testnet:!1}),OF=L({id:43851,name:`ZKFair Testnet`,network:`zkfair-testnet`,nativeCurrency:{decimals:18,name:`USD Coin`,symbol:`USDC`},rpcUrls:{default:{http:[`https://testnet-rpc.zkfair.io`]}},blockExplorers:{default:{name:`zkFair Explorer`,url:`https://testnet-scan.zkfair.io`}},testnet:!0}),kF=L({id:810180,name:`zkLink Nova`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zklink.io`]}},blockExplorers:{default:{name:`zkLink Nova Block Explorer`,url:`https://explorer.zklink.io`}}}),AF=L({id:810181,name:`zkLink Nova Sepolia Testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia.rpc.zklink.io`]}},blockExplorers:{default:{name:`zkLink Nova Block Explorer`,url:`https://sepolia.explorer.zklink.io`}}}),jF=L({...xy,blockTime:200,id:324,name:`ZKsync Era`,network:`zksync-era`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.era.zksync.io`],webSocket:[`wss://mainnet.era.zksync.io/ws`]}},blockExplorers:{default:{name:`ZKsync Explorer`,url:`https://explorer.zksync.io/`,apiUrl:`https://block-explorer-api.mainnet.zksync.io/api`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:3908235},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:45659388}}}),MF=L({...xy,id:260,name:`ZKsync InMemory Node`,network:`zksync-in-memory-node`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:8011`]}},testnet:!0}),NF=L({...xy,id:272,name:`ZKsync CLI Local Custom Hyperchain`,nativeCurrency:{name:`BAT`,symbol:`BAT`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15200`],webSocket:[`ws://localhost:15201`]}},blockExplorers:{default:{name:`ZKsync explorer`,url:`http://localhost:15005/`,apiUrl:`http://localhost:15005/api`}},testnet:!0}),PF=L({...xy,id:270,name:`ZKsync CLI Local Hyperchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15100`],webSocket:[`ws://localhost:15101`]}},blockExplorers:{default:{name:`ZKsync explorer`,url:`http://localhost:15005/`,apiUrl:`http://localhost:15005/api`}},testnet:!0}),FF=L({id:9,name:`ZKsync CLI Local Hyperchain L1`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15045`]}},blockExplorers:{default:{name:`Blockscout`,url:`http://localhost:15001/`,apiUrl:`http://localhost:15001/api/v2`}},testnet:!0}),IF=L({...xy,id:270,name:`ZKsync CLI Local Node`,network:`zksync-cli-local-node`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:3050`]}},testnet:!0}),LF=L({...xy,blockTime:200,id:300,name:`ZKsync Sepolia Testnet`,network:`zksync-sepolia-testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.era.zksync.dev`],webSocket:[`wss://sepolia.era.zksync.dev/ws`]}},blockExplorers:{default:{name:`ZKsync Explorer`,url:`https://sepolia.explorer.zksync.io/`,blockExplorerApi:`https://block-explorer-api.sepolia.zksync.dev/api`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:3855712}},testnet:!0}),RF=L({id:375,name:`zkXPLA Mainnet`,network:`zkxpla`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zkxpla.io`]}},blockExplorers:{default:{name:`zkXPLA Mainnet Explorer`,url:`https://explorer.zkxpla.io`,apiUrl:`https://explorer.zkxpla.io/api`}},testnet:!1}),zF=L({id:475,name:`zkXPLA Testnet`,network:`zkxpla-testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet-rpc.zkxpla.io`]}},blockExplorers:{default:{name:`zkXPLA Testnet Explorer`,url:`https://testnet-explorer.zkxpla.io`,apiUrl:`https://testnet-explorer.zkxpla.io/api`}},testnet:!0}),BF=1,VF=L({...R,id:7777777,name:`Zora`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zora.energy`],webSocket:[`wss://rpc.zora.energy`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.zora.energy`,apiUrl:`https://explorer.zora.energy/api`}},contracts:{...R.contracts,disputeGameFactory:{[BF]:{address:`0xB0F15106fa1e473Ddb39790f197275BC979Aa37e`}},l2OutputOracle:{[BF]:{address:`0x9E6204F750cD866b299594e2aC9eA824E2e5f95c`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:5882},portal:{[BF]:{address:`0x1a0ad011913A150f69f6A19DF447A0CfD9551054`}},l1StandardBridge:{[BF]:{address:`0x3e2Ea9B92B7E48A52296fD261dc26fd995284631`}}},sourceId:BF}),HF=11155111,UF=L({...R,id:999999999,name:`Zora Sepolia`,network:`zora-sepolia`,nativeCurrency:{decimals:18,name:`Zora Sepolia`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia.rpc.zora.energy`],webSocket:[`wss://sepolia.rpc.zora.energy`]}},blockExplorers:{default:{name:`Zora Sepolia Explorer`,url:`https://sepolia.explorer.zora.energy/`,apiUrl:`https://sepolia.explorer.zora.energy/api`}},contracts:{...R.contracts,l2OutputOracle:{[HF]:{address:`0x2615B481Bd3E5A1C0C7Ca3Da1bdc663E8615Ade9`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:83160},portal:{[HF]:{address:`0xeffE2C6cA9Ab797D418f0D91eA60807713f3536f`}},l1StandardBridge:{[HF]:{address:`0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB`}}},sourceId:HF,testnet:!0}),WF=5,GF=L({...R,id:999,name:`Zora Goerli Testnet`,nativeCurrency:{decimals:18,name:`Zora Goerli`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet.rpc.zora.energy`],webSocket:[`wss://testnet.rpc.zora.energy`]}},blockExplorers:{default:{name:`Explorer`,url:`https://testnet.explorer.zora.energy`,apiUrl:`https://testnet.explorer.zora.energy/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:189123},portal:{[WF]:{address:`0xDb9F51790365e7dc196e7D072728df39Be958ACe`}}},sourceId:WF,testnet:!0}),KF=c({abey:()=>ay,abstract:()=>Sy,abstractTestnet:()=>Cy,acala:()=>wy,acria:()=>Ty,adf:()=>Ey,adi:()=>Dy,agungTestnet:()=>Oy,aioz:()=>ky,alephZero:()=>Ay,alephZeroTestnet:()=>jy,alienx:()=>My,alienxHalTestnet:()=>Ny,alpenTestnet:()=>Py,ancient8:()=>Uy,ancient8Sepolia:()=>Gy,anvil:()=>Ky,apeChain:()=>qy,apexTestnet:()=>Jy,apollo:()=>Yy,arbitrum:()=>Xy,arbitrumGoerli:()=>Zy,arbitrumNova:()=>Qy,arbitrumSepolia:()=>$y,arcTestnet:()=>eb,arenaz:()=>tb,areonNetwork:()=>nb,areonNetworkTestnet:()=>rb,areum:()=>ib,artelaTestnet:()=>ab,arthera:()=>ob,artheraTestnet:()=>sb,assetChain:()=>cb,assetChainTestnet:()=>lb,astar:()=>ub,astarZkEVM:()=>db,astarZkyoto:()=>fb,atletaOlympia:()=>pb,aurora:()=>mb,auroraTestnet:()=>hb,auroria:()=>gb,autheoTestnet:()=>_b,avalanche:()=>vb,avalancheFuji:()=>yb,b3:()=>bb,b3Sepolia:()=>xb,bahamut:()=>Sb,base:()=>wb,baseGoerli:()=>Ob,basePreconf:()=>Tb,baseSepolia:()=>Ab,baseSepoliaPreconf:()=>jb,basecampTestnet:()=>Eb,beam:()=>Mb,beamTestnet:()=>Nb,bearNetworkChainMainnet:()=>Pb,bearNetworkChainTestnet:()=>Fb,berachain:()=>Ib,berachainBepolia:()=>Lb,berachainTestnet:()=>Rb,berachainTestnetbArtio:()=>zb,bevmMainnet:()=>Bb,bifrost:()=>Vb,birdlayer:()=>Hb,bitTorrent:()=>Yb,bitTorrentTestnet:()=>Xb,bitgert:()=>Ub,bitkub:()=>Wb,bitkubTestnet:()=>Gb,bitlayer:()=>Kb,bitlayerTestnet:()=>qb,bitrock:()=>Jb,blast:()=>Qb,blastSepolia:()=>$b,bob:()=>tx,bobSepolia:()=>ax,boba:()=>nx,bobaSepolia:()=>rx,boolBetaMainnet:()=>ox,botanix:()=>sx,botanixTestnet:()=>cx,bounceBit:()=>lx,bounceBitTestnet:()=>ux,bronos:()=>dx,bronosTestnet:()=>fx,bsc:()=>px,bscGreenfield:()=>mx,bscTestnet:()=>hx,bsquared:()=>gx,bsquaredTestnet:()=>_x,btr:()=>vx,btrTestnet:()=>yx,bxn:()=>bx,bxnTestnet:()=>xx,cannon:()=>Sx,canto:()=>Cx,celo:()=>Rx,celoAlfajores:()=>Bx,celoSepolia:()=>Hx,chang:()=>Ux,chiliz:()=>Wx,chips:()=>Gx,citrea:()=>Kx,citreaTestnet:()=>qx,classic:()=>Jx,codex:()=>Xx,codexTestnet:()=>Qx,coinbit:()=>$x,coinex:()=>eS,confluxESpace:()=>tS,confluxESpaceTestnet:()=>nS,coreDao:()=>rS,coreTestnet1:()=>iS,coreTestnet2:()=>aS,corn:()=>oS,cornTestnet:()=>sS,cpchain:()=>cS,crab:()=>lS,creatorTestnet:()=>uS,creditCoin3Devnet:()=>dS,creditCoin3Mainnet:()=>fS,creditCoin3Testnet:()=>pS,cronos:()=>mS,cronosTestnet:()=>hS,cronoszkEVM:()=>gS,cronoszkEVMTestnet:()=>_S,crossbell:()=>vS,crossfi:()=>yS,curtis:()=>bS,cyber:()=>xS,cyberTestnet:()=>SS,dailyNetwork:()=>CS,dailyNetworkTestnet:()=>wS,darwinia:()=>TS,datahavenTestnet:()=>ES,dbkchain:()=>DS,dchain:()=>OS,dchainTestnet:()=>kS,defichainEvm:()=>AS,defichainEvmTestnet:()=>jS,degen:()=>MS,dfk:()=>NS,diode:()=>PS,disChain:()=>FS,dodochainTestnet:()=>IS,dogechain:()=>LS,domaTestnet:()=>RS,donatuz:()=>zS,dosChain:()=>BS,dosChainTestnet:()=>VS,dreyerxMainnet:()=>HS,dreyerxTestnet:()=>US,dustboyIoT:()=>WS,dymension:()=>GS,edexa:()=>KS,edexaTestnet:()=>qS,edgeless:()=>JS,edgelessTestnet:()=>YS,edgeware:()=>XS,edgewareTestnet:()=>ZS,eduChain:()=>QS,eduChainTestnet:()=>$S,elastos:()=>eC,elastosTestnet:()=>tC,electroneum:()=>nC,electroneumTestnet:()=>rC,elysiumTestnet:()=>iC,energy:()=>aC,eni:()=>oC,eniTestnet:()=>sC,enuls:()=>cC,eon:()=>lC,eos:()=>uC,eosTestnet:()=>dC,eteria:()=>fC,etherlink:()=>pC,etherlinkShadownetTestnet:()=>mC,etherlinkTestnet:()=>hC,ethernity:()=>gC,etp:()=>_C,evmos:()=>vC,evmosTestnet:()=>yC,excelonMainnet:()=>bC,expanse:()=>xC,exsat:()=>SC,exsatTestnet:()=>CC,fantom:()=>wC,fantomSonicTestnet:()=>TC,fantomTestnet:()=>EC,fibo:()=>DC,filecoin:()=>OC,filecoinCalibration:()=>kC,filecoinHyperspace:()=>AC,fireChain:()=>iy,flame:()=>jC,flare:()=>MC,flareTestnet:()=>NC,flowMainnet:()=>PC,flowPreviewnet:()=>FC,flowTestnet:()=>IC,fluence:()=>LC,fluenceStage:()=>RC,fluenceTestnet:()=>zC,fluentDevnet:()=>BC,fluentTestnet:()=>VC,form:()=>UC,formTestnet:()=>KC,forma:()=>WC,formicarium:()=>lD,forta:()=>qC,foundry:()=>JC,fraxtal:()=>XC,fraxtalTestnet:()=>QC,funkiMainnet:()=>ew,funkiSepolia:()=>nw,fuse:()=>rw,fuseSparknet:()=>iw,fusion:()=>aw,fusionTestnet:()=>ow,garnet:()=>cw,gatechain:()=>lw,geist:()=>uw,genesys:()=>dw,giwaSepolia:()=>pw,giwaSepoliaPreconf:()=>mw,glideL1Protocol:()=>hw,glideL2Protocol:()=>gw,gnosis:()=>_w,gnosisChiado:()=>vw,goChain:()=>xw,goat:()=>yw,gobi:()=>bw,godwoken:()=>Sw,goerli:()=>Cw,graphite:()=>ww,graphiteTestnet:()=>Tw,gravity:()=>Ew,gunz:()=>Dw,guruNetwork:()=>Ow,guruTestnet:()=>kw,ham:()=>Aw,happychainTestnet:()=>jw,haqqMainnet:()=>Mw,haqqTestedge2:()=>Nw,hardhat:()=>Pw,harmonyOne:()=>Fw,hashkey:()=>Iw,hashkeyTestnet:()=>Lw,haustTestnet:()=>Rw,hedera:()=>zw,hederaPreviewnet:()=>Bw,hederaTestnet:()=>Vw,hela:()=>Hw,heliosTestnet:()=>Uw,hemi:()=>Ww,hemiSepolia:()=>Gw,henesys:()=>Kw,holesky:()=>qw,hoodi:()=>Jw,horizenTestnet:()=>Yw,hpb:()=>Xw,hpp:()=>Zw,hppSepolia:()=>Qw,huddle01Mainnet:()=>$w,huddle01Testnet:()=>eT,humanity:()=>tT,humanityTestnet:()=>nT,humanode:()=>rT,humanodeTestnet5:()=>iT,hychain:()=>aT,hychainTestnet:()=>oT,hyperEvm:()=>sT,hyperliquid:()=>sT,hyperliquidEvmTestnet:()=>cT,iSunCoin:()=>ET,icbNetwork:()=>lT,idchain:()=>uT,immutableZkEvm:()=>dT,immutableZkEvmTestnet:()=>fT,inEVM:()=>pT,initVerse:()=>mT,initVerseGenesis:()=>hT,injective:()=>gT,injectiveTestnet:()=>_T,ink:()=>yT,inkSepolia:()=>xT,iota:()=>ST,iotaTestnet:()=>CT,iotex:()=>wT,iotexTestnet:()=>TT,jasmyChain:()=>DT,jasmyChainTestnet:()=>OT,jbc:()=>kT,jbcTestnet:()=>AT,jocMainnet:()=>jT,jocTestnet:()=>MT,jovay:()=>NT,jovaySepolia:()=>PT,juneo:()=>FT,juneoBCH1Chain:()=>IT,juneoDAI1Chain:()=>LT,juneoDOGE1Chain:()=>RT,juneoEUR1Chain:()=>zT,juneoGLD1Chain:()=>BT,juneoLINK1Chain:()=>VT,juneoLTC1Chain:()=>HT,juneoSGD1Chain:()=>WT,juneoSocotraTestnet:()=>GT,juneoUSD1Chain:()=>KT,juneoUSDT1Chain:()=>qT,juneomBTC1Chain:()=>UT,kaia:()=>JT,kairos:()=>YT,kakarotSepolia:()=>XT,kakarotStarknetSepolia:()=>ZT,kardiaChain:()=>QT,karura:()=>$T,katana:()=>eE,kava:()=>tE,kavaTestnet:()=>nE,kcc:()=>rE,kii:()=>iE,kiiTestnetOro:()=>aE,kinto:()=>oE,klaytn:()=>sE,klaytnBaobab:()=>cE,koi:()=>lE,kroma:()=>uE,kromaSepolia:()=>dE,krown:()=>fE,l3x:()=>pE,l3xTestnet:()=>mE,lavita:()=>hE,lens:()=>gE,lensTestnet:()=>_E,lestnet:()=>vE,lightlinkPegasus:()=>yE,lightlinkPhoenix:()=>bE,linea:()=>wE,lineaGoerli:()=>TE,lineaSepolia:()=>EE,lineaTestnet:()=>DE,lisk:()=>kE,liskSepolia:()=>jE,loadAlphanet:()=>ME,localhost:()=>NE,loop:()=>PE,lukso:()=>FE,luksoTestnet:()=>IE,lumiaMainnet:()=>LE,lumiaTestnet:()=>RE,lumoz:()=>zE,lumozTestnet:()=>BE,luxeports:()=>VE,lycan:()=>HE,lyra:()=>UE,mainnet:()=>WE,mandala:()=>GE,manta:()=>KE,mantaSepoliaTestnet:()=>qE,mantaTestnet:()=>JE,mantle:()=>YE,mantleSepoliaTestnet:()=>XE,mantleTestnet:()=>ZE,mantraDuKongEVMTestnet:()=>QE,mantraEVM:()=>$E,mapProtocol:()=>eD,matchain:()=>tD,matchainTestnet:()=>nD,mchVerse:()=>rD,megaeth:()=>iD,megaethTestnet:()=>aD,mekong:()=>oD,meld:()=>sD,memecore:()=>cD,merlin:()=>uD,merlinErigonTestnet:()=>dD,metachain:()=>fD,metachainIstanbul:()=>pD,metadium:()=>mD,metalL2:()=>gD,meter:()=>_D,meterTestnet:()=>vD,metis:()=>yD,metisGoerli:()=>bD,metisSepolia:()=>xD,mev:()=>SD,mevTestnet:()=>CD,mint:()=>wD,mintSepoliaTestnet:()=>TD,mitosisTestnet:()=>ED,mode:()=>OD,modeTestnet:()=>AD,monad:()=>jD,monadTestnet:()=>MD,moonbaseAlpha:()=>ND,moonbeam:()=>PD,moonbeamDev:()=>FD,moonriver:()=>ID,morph:()=>LD,morphHolesky:()=>RD,morphSepolia:()=>zD,nahmii:()=>BD,nautilus:()=>VD,near:()=>HD,nearTestnet:()=>UD,neonDevnet:()=>WD,neonMainnet:()=>GD,neoxMainnet:()=>KD,neoxT4:()=>qD,newton:()=>JD,nexi:()=>YD,nexilix:()=>XD,nibiru:()=>ZD,nitrographTestnet:()=>QD,nomina:()=>$D,oasisTestnet:()=>eO,oasys:()=>tO,odysseyTestnet:()=>nO,okc:()=>rO,omax:()=>iO,omni:()=>aO,omniOmega:()=>oO,oneWorld:()=>sO,oortMainnetDev:()=>cO,opBNB:()=>uO,opBNBTestnet:()=>fO,openledger:()=>pO,optimism:()=>hO,optimismGoerli:()=>_O,optimismSepolia:()=>yO,optopia:()=>bO,optopiaTestnet:()=>xO,orderly:()=>SO,orderlySepolia:()=>CO,otimDevnet:()=>wO,palm:()=>TO,palmTestnet:()=>EO,paseoPassetHub:()=>DO,peaq:()=>OO,pgn:()=>AO,pgnTestnet:()=>MO,phoenix:()=>NO,planq:()=>PO,plasma:()=>FO,plasmaDevnet:()=>IO,plasmaTestnet:()=>LO,playfiAlbireo:()=>RO,plinga:()=>zO,plume:()=>BO,plumeDevnet:()=>VO,plumeMainnet:()=>HO,plumeSepolia:()=>UO,plumeTestnet:()=>WO,polterTestnet:()=>GO,polygon:()=>KO,polygonAmoy:()=>qO,polygonMumbai:()=>JO,polygonZkEvm:()=>YO,polygonZkEvmCardona:()=>XO,polygonZkEvmTestnet:()=>ZO,polynomial:()=>QO,polynomialSepolia:()=>$O,potos:()=>ek,potosTestnet:()=>tk,premiumBlockTestnet:()=>nk,pulsechain:()=>rk,pulsechainV4:()=>ik,pumpfiTestnet:()=>ak,pyrope:()=>sk,qMainnet:()=>lk,qTestnet:()=>uk,ql1:()=>ck,quai:()=>dk,quaiTestnet:()=>fk,reactiveTestnet:()=>pk,real:()=>mk,redbellyMainnet:()=>hk,redbellyTestnet:()=>gk,reddio:()=>_k,reddioSepolia:()=>vk,redstone:()=>bk,rei:()=>xk,reyaNetwork:()=>Sk,rise:()=>Ck,riseTestnet:()=>wk,rivalz:()=>Tk,rollux:()=>Ek,rolluxTestnet:()=>Dk,ronin:()=>Ok,root:()=>kk,rootPorcini:()=>Ak,rootstock:()=>jk,rootstockTestnet:()=>Mk,rss3:()=>Pk,rss3Sepolia:()=>Ik,saakuru:()=>Lk,saga:()=>Rk,saigon:()=>zk,sanko:()=>Bk,sapphire:()=>Vk,sapphireTestnet:()=>Hk,satoshiVM:()=>Uk,satoshiVMTestnet:()=>Wk,scroll:()=>Gk,scrollSepolia:()=>Kk,sei:()=>qk,seiTestnet:()=>Yk,seismicDevnet:()=>Jk,sepolia:()=>Xk,shape:()=>Qk,shapeSepolia:()=>eA,shardeum:()=>tA,shardeumSphinx:()=>nA,shibarium:()=>rA,shibariumTestnet:()=>iA,shiden:()=>aA,shimmer:()=>oA,shimmerTestnet:()=>sA,sidraChain:()=>cA,silentData:()=>lA,silicon:()=>uA,siliconSepolia:()=>dA,sixProtocol:()=>fA,skaleBase:()=>TA,skaleBaseSepoliaTestnet:()=>EA,skaleBlockBrawlers:()=>pA,skaleCalypso:()=>mA,skaleCalypsoTestnet:()=>hA,skaleCryptoBlades:()=>gA,skaleCryptoColosseum:()=>_A,skaleEuropa:()=>vA,skaleEuropaTestnet:()=>yA,skaleExorde:()=>bA,skaleHumanProtocol:()=>xA,skaleNebula:()=>SA,skaleNebulaTestnet:()=>CA,skaleRazor:()=>wA,skaleTitan:()=>DA,skaleTitanTestnet:()=>OA,sketchpad:()=>kA,snax:()=>jA,snaxTestnet:()=>NA,somnia:()=>PA,somniaTestnet:()=>FA,soneium:()=>LA,soneiumMinato:()=>zA,songbird:()=>BA,songbirdTestnet:()=>VA,sonic:()=>HA,sonicBlazeTestnet:()=>UA,sonicTestnet:()=>WA,sophon:()=>GA,sophonTestnet:()=>KA,sova:()=>qA,sovaSepolia:()=>JA,spicy:()=>YA,stable:()=>XA,stableTestnet:()=>ZA,statusNetworkSepolia:()=>QA,statusSepolia:()=>QA,step:()=>$A,story:()=>ej,storyAeneid:()=>tj,storyOdyssey:()=>nj,storyTestnet:()=>rj,stratis:()=>ij,subtensorEvm:()=>aj,superlumio:()=>oj,superposition:()=>sj,superseed:()=>lj,superseedSepolia:()=>dj,surgeTestnet:()=>fj,swan:()=>pj,swanProximaTestnet:()=>mj,swanSaturnTestnet:()=>hj,swellchain:()=>gj,swellchainTestnet:()=>_j,swissdlt:()=>vj,syscoin:()=>yj,syscoinTestnet:()=>bj,tac:()=>xj,tacSPB:()=>Sj,taiko:()=>Cj,taikoHekla:()=>wj,taikoHoodi:()=>Tj,taikoJolnir:()=>Ej,taikoKatla:()=>Dj,taikoTestnetSepolia:()=>Oj,taraxa:()=>kj,taraxaTestnet:()=>Aj,teaSepolia:()=>jj,telcoinTestnet:()=>Mj,telos:()=>Nj,telosTestnet:()=>Pj,tempo:()=>JN,tempoAndantino:()=>YN,tempoDevnet:()=>XN,tempoLocalnet:()=>ZN,tempoModerato:()=>QN,tempoTestnet:()=>YN,tenet:()=>$N,ternoa:()=>eP,thaiChain:()=>tP,that:()=>nP,theta:()=>rP,thetaTestnet:()=>iP,thunderCore:()=>aP,thunderTestnet:()=>oP,tiktrixTestnet:()=>sP,tomb:()=>cP,treasure:()=>lP,treasureTopaz:()=>uP,tron:()=>dP,tronNile:()=>fP,tronShasta:()=>pP,ubiq:()=>mP,ultra:()=>hP,ultraTestnet:()=>gP,ultron:()=>_P,ultronTestnet:()=>vP,unichain:()=>bP,unichainSepolia:()=>SP,unique:()=>CP,uniqueOpal:()=>wP,uniqueQuartz:()=>TP,unreal:()=>EP,vana:()=>DP,vanaMoksha:()=>OP,vanar:()=>kP,vechain:()=>AP,velas:()=>jP,viction:()=>MP,victionTestnet:()=>NP,vision:()=>PP,visionTestnet:()=>FP,wanchain:()=>IP,wanchainTestnet:()=>LP,weaveVMAlphanet:()=>RP,wemix:()=>zP,wemixTestnet:()=>BP,westendAssetHub:()=>VP,whitechain:()=>HP,whitechainTestnet:()=>UP,wmcTestnet:()=>WP,worldLand:()=>YP,worldchain:()=>KP,worldchainSepolia:()=>JP,x1Testnet:()=>nF,xLayer:()=>tF,xLayerTestnet:()=>nF,xai:()=>XP,xaiTestnet:()=>ZP,xdc:()=>QP,xdcTestnet:()=>$P,xgr:()=>eF,xoneMainnet:()=>rF,xoneTestnet:()=>iF,xphereMainnet:()=>aF,xphereTestnet:()=>oF,xpla:()=>sF,xrOne:()=>cF,xrSepolia:()=>fF,xrplevm:()=>lF,xrplevmDevnet:()=>uF,xrplevmTestnet:()=>dF,yooldoVerse:()=>pF,yooldoVerseTestnet:()=>mF,zenchainTestnet:()=>hF,zeniq:()=>gF,zeroG:()=>ey,zeroGGalileoTestnet:()=>ty,zeroGMainnet:()=>ny,zeroGTestnet:()=>ry,zeroNetwork:()=>_F,zetachain:()=>vF,zetachainAthensTestnet:()=>yF,zhejiang:()=>bF,zilliqa:()=>xF,zilliqaTestnet:()=>SF,zircuit:()=>wF,zircuitGarfieldTestnet:()=>EF,zkFair:()=>DF,zkFairTestnet:()=>OF,zkLinkNova:()=>kF,zkLinkNovaSepoliaTestnet:()=>AF,zkSync:()=>jF,zkSyncInMemoryNode:()=>MF,zkSyncLocalNode:()=>IF,zkSyncSepoliaTestnet:()=>LF,zkXPLA:()=>RF,zkXPLATestnet:()=>zF,zksync:()=>jF,zksyncInMemoryNode:()=>MF,zksyncLocalCustomHyperchain:()=>NF,zksyncLocalHyperchain:()=>PF,zksyncLocalHyperchainL1:()=>FF,zksyncLocalNode:()=>IF,zksyncSepoliaTestnet:()=>LF,zora:()=>VF,zoraSepolia:()=>UF,zoraTestnet:()=>GF}),qF=[wb,...Object.values(c({arbitrum:()=>Xy,arbitrumSepolia:()=>$y,base:()=>wb,baseSepolia:()=>Ab,berachain:()=>Ib,berachainBepolia:()=>Lb,bsc:()=>px,celo:()=>Rx,gnosis:()=>_w,hoodi:()=>Jw,katana:()=>eE,mainnet:()=>WE,optimism:()=>hO,optimismSepolia:()=>yO,polygon:()=>KO,sepolia:()=>Xk})).filter(e=>e&&e.id!==wb.id)],JF=Ky;({...JF}),{...JF};var YF=`#__bigint`;function XF(e,t){return JSON.parse(e,(e,n)=>{let r=n;return typeof r==`string`&&r.endsWith(YF)?BigInt(r.slice(0,-9)):typeof t==`function`?t(e,r):r})}function ZF(e,t,n){return JSON.stringify(e,(e,n)=>typeof t==`function`?t(e,n):typeof n==`bigint`?n.toString()+YF:n,n)}var QF=u(s(((e,t)=>{var n=Object.prototype.hasOwnProperty,r=`~`;function i(){}Object.create&&(i.prototype=Object.create(null),new i().__proto__||(r=!1));function a(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function o(e,t,n,i,o){if(typeof n!=`function`)throw TypeError(`The listener must be a function`);var s=new a(n,i||e,o),c=r?r+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],s]:e._events[c].push(s):(e._events[c]=s,e._eventsCount++),e}function s(e,t){--e._eventsCount===0?e._events=new i:delete e._events[t]}function c(){this._events=new i,this._eventsCount=0}c.prototype.eventNames=function(){var e=[],t,i;if(this._eventsCount===0)return e;for(i in t=this._events)n.call(t,i)&&e.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?e.concat(Object.getOwnPropertySymbols(t)):e},c.prototype.listeners=function(e){var t=r?r+e:e,n=this._events[t];if(!n)return[];if(n.fn)return[n.fn];for(var i=0,a=n.length,o=Array(a);i{if(n.cause instanceof e){if(n.cause.details)return n.cause.details;if(n.cause.shortMessage)return n.cause.shortMessage}return n.cause&&`details`in n.cause&&typeof n.cause.details==`string`?n.cause.details:n.cause?.message?n.cause.message:n.details})(),i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=n.docsOrigin??e.prototype.docsOrigin,o=`${a}${i??``}`,s=!!(n.version??e.prototype.showVersion),c=n.version??e.prototype.version,l=[t||`An error occurred.`,...n.metaMessages?[``,...n.metaMessages]:[],...r||i||s?[``,r?`Details: ${r}`:void 0,i?`See: ${o}`:void 0,s?`Version: ${c}`:void 0]:[]].filter(e=>typeof e==`string`).join(` +`);super(l,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsOrigin`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`showVersion`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.cause=n.cause,this.details=r,this.docs=o,this.docsOrigin=a,this.docsPath=i,this.shortMessage=t,this.showVersion=s,this.version=c}walk(e){return tI(this,e)}};Object.defineProperty(z,`defaultStaticOptions`,{enumerable:!0,configurable:!0,writable:!0,value:{docsOrigin:`https://oxlib.sh`,showVersion:!1,version:`ox@${eI()}`}}),z.setStaticOptions(z.defaultStaticOptions);function tI(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause?tI(e.cause,t):t?null:e}function nI(e,t={}){let{raw:n=!1}=t,r=e;if(n)return e;if(r.error)throw rI(r.error);return r.result}function rI(e){let t=e;if(t instanceof Error&&!(`code`in t))return new hI({cause:t,data:t,message:t.message,stack:t.stack});let{code:n}=t;return n===hI.code?new hI(t):n===aI.code?new aI(t):n===mI.code?new mI(t):n===fI.code?new fI(t):n===uI.code?new uI(t):n===pI.code?new pI(t):n===lI.code?new lI(t):n===gI.code?new gI(t):n===oI.code?new oI(t):n===sI.code?new sI(t):n===cI.code?new cI(t):n===dI.code?new dI(t):new hI({cause:t instanceof Error?t:void 0,data:t,message:t.message,stack:t instanceof Error?t.stack:void 0})}var iI=class extends Error{constructor(e){let{cause:t,code:n,message:r,data:i,stack:a}=e;super(r,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.BaseError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`stack`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=t,this.code=n,this.data=i,this.stack=a??``}},aI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Missing or invalid parameters.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InvalidInputError`})}};Object.defineProperty(aI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3});var oI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Requested resource not found.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.ResourceNotFoundError`})}};Object.defineProperty(oI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001});var sI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Requested resource not available.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.ResourceUnavailableError`})}};Object.defineProperty(sI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002});var cI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Transaction creation failed.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.TransactionRejectedError`})}};Object.defineProperty(cI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003});var lI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Method is not implemented.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.MethodNotSupportedError`})}};Object.defineProperty(lI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004});var uI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Rate limit exceeded.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.LimitExceededError`})}};Object.defineProperty(uI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005});var dI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`JSON-RPC version not supported.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.VersionNotSupportedError`})}};Object.defineProperty(dI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006});var fI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Input is not a valid JSON-RPC request.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InvalidRequestError`})}};Object.defineProperty(fI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600});var pI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Method does not exist.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.MethodNotFoundError`})}};Object.defineProperty(pI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601});var mI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Invalid method parameters.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InvalidParamsError`})}};Object.defineProperty(mI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602});var hI=class e extends iI{constructor(t={}){super({cause:t.cause,code:e.code,data:t.data,message:t.message??`Internal JSON-RPC error.`,stack:t.stack}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InternalError`})}};Object.defineProperty(hI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603});var gI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Failed to parse JSON-RPC response.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.ParseError`})}};Object.defineProperty(gI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700});var _I=class extends Error{constructor(e,t){super(t),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`ProviderRpcError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.code=e,this.details=t}},vI=class extends _I{constructor({message:e=`The user rejected the request.`}={}){super(4001,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UserRejectedRequestError`})}};Object.defineProperty(vI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001});var yI=class extends _I{constructor({message:e=`The requested method and/or account has not been authorized by the user.`}={}){super(4100,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnauthorizedError`})}};Object.defineProperty(yI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100});var bI=class extends _I{constructor({message:e=`The provider does not support the requested method.`}={}){super(4200,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnsupportedMethodError`})}};Object.defineProperty(bI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200});var xI=class extends _I{constructor({message:e=`The provider is disconnected from all chains.`}={}){super(4900,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.DisconnectedError`})}};Object.defineProperty(xI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900});var SI=class extends _I{constructor({message:e=`The provider is not connected to the requested chain.`}={}){super(4901,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.ChainDisconnectedError`})}};Object.defineProperty(SI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901});var CI=class extends _I{constructor({message:e=`An error occurred when attempting to switch chain.`}={}){super(4902,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.SwitchChainError`})}};Object.defineProperty(CI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902});var wI=class extends _I{constructor({message:e=`This Wallet does not support a capability that was not marked as optional.`}={}){super(5700,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnsupportedNonOptionalCapabilityError`})}};Object.defineProperty(wI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700});var TI=class extends _I{constructor({message:e=`This Wallet does not support the requested chain ID.`}={}){super(5710,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnsupportedChainIdError`})}};Object.defineProperty(TI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710});var EI=class extends _I{constructor({message:e=`There is already a bundle submitted with this ID.`}={}){super(5720,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.DuplicateIdError`})}};Object.defineProperty(EI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720});var DI=class extends _I{constructor({message:e=`This bundle id is unknown / has not been submitted.`}={}){super(5730,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnknownBundleIdError`})}};Object.defineProperty(DI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730});var OI=class extends _I{constructor({message:e=`The call bundle is too large for the Wallet to process.`}={}){super(5740,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.BundleTooLargeError`})}};Object.defineProperty(OI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740});var kI=class extends _I{constructor({message:e=`The Wallet can support atomicity after an upgrade, but the user rejected the upgrade.`}={}){super(5750,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.AtomicReadyWalletRejectedUpgradeError`})}};Object.defineProperty(kI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750});var AI=class extends _I{constructor({message:e=`The wallet does not support atomic execution but the request requires it.`}={}){super(5760,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.AtomicityNotSupportedError`})}};Object.defineProperty(AI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760});function jI(){let e=new QF.default;return{get eventNames(){return e.eventNames.bind(e)},get listenerCount(){return e.listenerCount.bind(e)},get listeners(){return e.listeners.bind(e)},addListener:e.addListener.bind(e),emit:e.emit.bind(e),off:e.off.bind(e),on:e.on.bind(e),once:e.once.bind(e),removeAllListeners:e.removeAllListeners.bind(e),removeListener:e.removeListener.bind(e)}}function MI(e,t={}){if(!e)throw new PI;return{...e,async request(t){try{let n=await e.request(t);return n&&typeof n==`object`&&`jsonrpc`in n?nI(n):n}catch(e){throw NI(e)}}}}function NI(e){let t=rI(e);if(t instanceof hI){if(!t.data)return t;let{code:e}=t.data;if(e===xI.code)return new xI(t);if(e===SI.code)return new SI(t);if(e===vI.code)return new vI(t);if(e===yI.code)return new yI(t);if(e===bI.code)return new bI(t);if(e===CI.code)return new CI(t);if(e===kI.code)return new kI(t);if(e===AI.code)return new AI(t);if(e===OI.code)return new OI(t);if(e===DI.code)return new DI(t);if(e===EI.code)return new EI(t);if(e===TI.code)return new TI(t);if(e===wI.code)return new wI(t)}return t}var PI=class extends z{constructor(){super("`provider` is undefined."),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.IsUndefinedError`})}};function FI(e,t){if(SL(e)>t)throw new OL({givenSize:SL(e),maxSize:t})}function II(e,t){if(typeof t==`number`&&t>0&&t>SL(e)-1)throw new kL({offset:t,position:`start`,size:SL(e)})}function LI(e,t,n){if(typeof t==`number`&&typeof n==`number`&&SL(e)!==n-t)throw new kL({offset:n,position:`end`,size:SL(e)})}var RI={zero:48,nine:57,A:65,F:70,a:97,f:102};function zI(e){if(e>=RI.zero&&e<=RI.nine)return e-RI.zero;if(e>=RI.A&&e<=RI.F)return e-(RI.A-10);if(e>=RI.a&&e<=RI.f)return e-(RI.a-10)}function BI(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;if(e.length>r)throw new AL({size:e.length,targetSize:r,type:`Bytes`});let i=new Uint8Array(r);for(let t=0;tt)throw new fL({givenSize:iL(e),maxSize:t})}function HI(e,t){if(typeof t==`number`&&t>0&&t>iL(e)-1)throw new pL({offset:t,position:`start`,size:iL(e)})}function UI(e,t,n){if(typeof t==`number`&&typeof n==`number`&&iL(e)!==n-t)throw new pL({offset:n,position:`end`,size:iL(e)})}function WI(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;let i=e.replace(`0x`,``);if(i.length>r*2)throw new mL({size:Math.ceil(i.length/2),targetSize:r,type:`Hex`});return`0x${i[n===`right`?`padEnd`:`padStart`](r*2,`0`)}`}function GI(e,t={}){let{dir:n=`left`}=t,r=e.replace(`0x`,``),i=0;for(let e=0;et.toString(16).padStart(2,`0`));function JI(e,t={}){let{strict:n=!1}=t;if(!e||typeof e!=`string`)throw new uL(e);if(n&&!/^0x[0-9a-fA-F]*$/.test(e)||!e.startsWith(`0x`))throw new dL(e)}function YI(...e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}function XI(e){return e instanceof Uint8Array?QI(e):Array.isArray(e)?QI(new Uint8Array(e)):e}function ZI(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(VI(n,t.size),tL(n,t.size)):n}function QI(e,t={}){let n=``;for(let t=0;ta||i>1n?r:r-a-1n}function sL(e,t={}){let{signed:n,size:r}=t;return Number(!n&&!r?e:oL(e,t))}function cL(e,t={}){let{strict:n=!1}=t;try{return JI(e,{strict:n}),!0}catch{return!1}}var lL=class extends z{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number \`${i}\` is not in safe${r?` ${r*8}-bit`:``}${n?` signed`:` unsigned`} integer range ${e?`(\`${t}\` to \`${e}\`)`:`(above \`${t}\`)`}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.IntegerOutOfRangeError`})}},uL=class extends z{constructor(e){super(`Value \`${typeof e==`object`?ZF(e):e}\` of type \`${typeof e}\` is an invalid hex type.`,{metaMessages:['Hex types must be represented as `"0x${string}"`.']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexTypeError`})}},dL=class extends z{constructor(e){super(`Value \`${e}\` is an invalid hex value.`,{metaMessages:['Hex values must start with `"0x"` and contain only hexadecimal characters (0-9, a-f, A-F).']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexValueError`})}},fL=class extends z{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeOverflowError`})}},pL=class extends z{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SliceOffsetOutOfBoundsError`})}},mL=class extends z{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeExceedsPaddingSizeError`})}},hL=new TextEncoder;function gL(e){if(!(e instanceof Uint8Array)&&(!e||typeof e!=`object`||!(`BYTES_PER_ELEMENT`in e)||e.BYTES_PER_ELEMENT!==1||e.constructor.name!==`Uint8Array`))throw new DL(e)}function _L(e){return e instanceof Uint8Array?e:typeof e==`string`?yL(e):vL(e)}function vL(e){return e instanceof Uint8Array?e:new Uint8Array(e)}function yL(e,t={}){let{size:n}=t,r=e;n&&(VI(e,n),r=nL(e,n));let i=r.slice(2);i.length%2&&(i=`0${i}`);let a=i.length/2,o=new Uint8Array(a);for(let e=0,t=0;ethis.maxSize){let e=this.keys().next().value;e&&this.delete(e)}return this}}(8192)}.checksum;ti();function ML(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=$r(_L(e));return n===`Bytes`?r:QI(r)}function NL(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=yd(_L(e));return n===`Bytes`?r:QI(r)}function PL(e,t={}){let{compressed:n}=t,{prefix:r,x:i,y:a}=e;if(n===!1||typeof i==`bigint`&&typeof a==`bigint`){if(r!==4)throw new BL({prefix:r,cause:new HL});return}if(n===!0||typeof i==`bigint`&&a===void 0){if(r!==3&&r!==2)throw new BL({prefix:r,cause:new VL});return}throw new zL({publicKey:e})}function FL(e){let t=(()=>{if(cL(e))return LL(e);if(EL(e))return IL(e);let{prefix:t,x:n,y:r}=e;return typeof n==`bigint`&&typeof r==`bigint`?{prefix:t??4,x:n,y:r}:{prefix:t,x:n}})();return PL(t),t}function IL(e){return LL(QI(e))}function LL(e){if(e.length!==132&&e.length!==130&&e.length!==68)throw new UL({publicKey:e});return e.length===130?{prefix:4,x:BigInt(rL(e,0,32)),y:BigInt(rL(e,32,64))}:e.length===132?{prefix:Number(rL(e,0,1)),x:BigInt(rL(e,1,33)),y:BigInt(rL(e,33,65))}:{prefix:Number(rL(e,0,1)),x:BigInt(rL(e,1,33))}}function RL(e,t={}){PL(e);let{prefix:n,x:r,y:i}=e,{includePrefix:a=!0}=t;return YI(a?$I(n,{size:1}):`0x`,$I(r,{size:32}),typeof i==`bigint`?$I(i,{size:32}):`0x`)}var zL=class extends z{constructor({publicKey:e}){super(`Value \`${ZF(e)}\` is not a valid public key.`,{metaMessages:[`Public key must contain:`,"- an `x` and `prefix` value (compressed)","- an `x`, `y`, and `prefix` value (uncompressed)"]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidError`})}},BL=class extends z{constructor({prefix:e,cause:t}){super(`Prefix "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidPrefixError`})}},VL=class extends z{constructor(){super(`Prefix must be 2 or 3 for compressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidCompressedPrefixError`})}},HL=class extends z{constructor(){super(`Prefix must be 4 for uncompressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidUncompressedPrefixError`})}},UL=class extends z{constructor({publicKey:e}){super(`Value \`${e}\` is an invalid public key size.`,{metaMessages:[`Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).`,`Received ${iL(XI(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidSerializedSizeError`})}},WL=/^0x[a-fA-F0-9]{40}$/;function GL(e,t={}){let{strict:n=!0}=t;if(!WL.test(e))throw new ZL({address:e,cause:new QL});if(n){if(e.toLowerCase()===e)return;if(KL(e)!==e)throw new ZL({address:e,cause:new $L})}}function KL(e){if(jL.has(e))return jL.get(e);GL(e,{strict:!1});let t=e.substring(2).toLowerCase(),n=ML(bL(t),{as:`Bytes`}),r=t.split(``);for(let e=0;e<40;e+=2)n[e>>1]>>4>=8&&r[e]&&(r[e]=r[e].toUpperCase()),(n[e>>1]&15)>=8&&r[e+1]&&(r[e+1]=r[e+1].toUpperCase());let i=`0x${r.join(``)}`;return jL.set(e,i),i}function qL(e,t={}){let{checksum:n=!1}=t;return GL(e),n?KL(e):e}function JL(e,t={}){return qL(`0x${ML(`0x${RL(e).slice(4)}`).substring(26)}`,t)}function YL(e,t){return GL(e,{strict:!1}),GL(t,{strict:!1}),e.toLowerCase()===t.toLowerCase()}function XL(e,t={}){let{strict:n=!0}=t??{};try{return GL(e,{strict:n}),!0}catch{return!1}}var ZL=class extends z{constructor({address:e,cause:t}){super(`Address "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidAddressError`})}},QL=class extends z{constructor(){super(`Address is not a 20 byte (40 hexadecimal character) value.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidInputError`})}},$L=class extends z{constructor(){super(`Address does not match its checksum counterpart.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidChecksumError`})}},eR=!1;uu();function tR(e){let{privateKey:t}=e;return FL(Zl.ProjectivePoint.fromPrivateKey(XI(t).slice(2)))}function nR(e={}){let{as:t=`Hex`}=e,n=Zl.utils.randomPrivateKey();return t===`Hex`?QI(n):n}function rR(e){return JL(iR(e))}function iR(e){let{payload:t,signature:n}=e,{r,s:i,yParity:a}=n;return FL(new Zl.Signature(BigInt(r),BigInt(i)).addRecoveryBit(a).recoverPublicKey(XI(t).substring(2)))}function aR(e){let{extraEntropy:t=eR,hash:n,payload:r,privateKey:i}=e,{r:a,s:o,recovery:s}=Zl.sign(_L(r),_L(i),{extraEntropy:typeof t==`boolean`?t:XI(t).slice(2),lowS:!0,...n?{prehash:!0}:{}});return{r:a,s:o,yParity:s}}var oR=/^(.*)\[([0-9]*)\]$/,sR=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,cR=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n));var lR=2n**256n-1n;function uR(e,t={}){let{recovered:n}=t;if(e.r===void 0||e.s===void 0||n&&e.yParity===void 0)throw new SR({signature:e});if(e.r<0n||e.r>lR)throw new CR({value:e.r});if(e.s<0n||e.s>lR)throw new wR({value:e.s});if(typeof e.yParity==`number`&&e.yParity!==0&&e.yParity!==1)throw new TR({value:e.yParity})}function dR(e){return fR(QI(e))}function fR(e){if(e.length!==130&&e.length!==132)throw new xR({signature:e});let t=BigInt(rL(e,0,32)),n=BigInt(rL(e,32,64)),r=(()=>{let t=Number(`0x${e.slice(130)}`);if(!Number.isNaN(t))try{return yR(t)}catch{throw new TR({value:t})}})();return r===void 0?{r:t,s:n}:{r:t,s:n,yParity:r}}function pR(e){if(e.r!==void 0&&e.s!==void 0)return mR(e)}function mR(e){let t=typeof e==`string`?fR(e):e instanceof Uint8Array?dR(e):typeof e.r==`string`?gR(e):e.v?hR(e):{r:e.r,s:e.s,...e.yParity===void 0?{}:{yParity:e.yParity}};return uR(t),t}function hR(e){return{r:e.r,s:e.s,yParity:yR(e.v)}}function gR(e){let t=(()=>{let t=e.v?Number(e.v):void 0,n=e.yParity?Number(e.yParity):void 0;if(typeof t==`number`&&typeof n!=`number`&&(n=yR(t)),typeof n!=`number`)throw new TR({value:e.yParity});return n})();return{r:BigInt(e.r),s:BigInt(e.s),yParity:t}}function _R(e){uR(e);let t=e.r,n=e.s;return YI($I(t,{size:32}),$I(n,{size:32}),typeof e.yParity==`number`?$I(bR(e.yParity),{size:1}):`0x`)}function vR(e){let{r:t,s:n,yParity:r}=e;return[r?`0x01`:`0x`,t===0n?`0x`:aL($I(t)),n===0n?`0x`:aL($I(n))]}function yR(e){if(e===0||e===27)return 0;if(e===1||e===28)return 1;if(e>=35)return e%2==0?1:0;throw new ER({value:e})}function bR(e){if(e===0)return 27;if(e===1)return 28;throw new TR({value:e})}var xR=class extends z{constructor({signature:e}){super(`Value \`${e}\` is an invalid signature size.`,{metaMessages:[`Expected: 64 bytes or 65 bytes.`,`Received ${iL(XI(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSerializedSizeError`})}},SR=class extends z{constructor({signature:e}){super(`Signature \`${ZF(e)}\` is missing either an \`r\`, \`s\`, or \`yParity\` property.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.MissingPropertiesError`})}},CR=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid r value. r must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidRError`})}},wR=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid s value. s must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSError`})}},TR=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid y-parity value. Y-parity must be 0 or 1.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidYParityError`})}},ER=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid v value. v must be 27, 28 or >=35.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidVError`})}};function DR({checksumAddress:e,parameters:t,values:n}){let r=[];for(let i=0;i0?YI(t,e):t}}if(o)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:YI(...s.map(({encoded:e})=>e))}}function MR(e,{type:t}){let[,n]=t.split(`bytes`),r=iL(e);if(!n){let t=e;return r%32!=0&&(t=nL(t,Math.ceil((e.length-2)/2/32)*32)),{dynamic:!0,encoded:YI(tL($I(r,{size:32})),t)}}if(r!==Number.parseInt(n,10))throw new qR({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:nL(e)}}function NR(e){if(typeof e!=`boolean`)throw new z(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:tL(ZI(e))}}function PR(e,{signed:t,size:n}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function LR(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}var RR={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new HR({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new VR({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new BR({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new BR({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}};function zR(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(RR);return n.bytes=e,n.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var BR=class extends z{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.NegativeOffsetError`})}},VR=class extends z{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.PositionOutOfBoundsError`})}},HR=class extends z{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.RecursiveReadLimitExceededError`})}};kt();function UR(e,t,n){let{checksumAddress:r=!1}=n??{};if(e.length!==t.length)throw new JR({expectedLength:e.length,givenLength:t.length});let i=kR(DR({checksumAddress:r,parameters:e,values:t}));return i.length===0?`0x`:i}function WR(e,t){if(e.length!==t.length)throw new JR({expectedLength:e.length,givenLength:t.length});let n=[];for(let r=0;r{for(let n of e){let{name:e,type:r}=n,o=t[e],s=r.match(cR);if(s&&(typeof o==`number`||typeof o==`bigint`)){let[,e,t]=s;$I(o,{signed:e===`int`,size:Number.parseInt(t??``,10)/8})}if(r===`address`&&typeof o==`string`&&!XL(o))throw new ZL({address:o,cause:new QL});let c=r.match(sR);if(c){let[,e]=c;if(e&&iL(o)!==Number.parseInt(e,10))throw new iz({expectedSize:Number.parseInt(e,10),givenSize:iL(o)})}let l=i[r];l&&(fz(r),a(l,o))}};if(i.EIP712Domain&&t){if(typeof t!=`object`)throw new az({domain:t});a(i.EIP712Domain,t)}if(r!==`EIP712Domain`)if(i[r])a(i[r],n);else throw new oz({primaryType:r,types:i})}function QR(e){let{domain:t={},message:n,primaryType:r}=e,i={EIP712Domain:ez(t),...e.types};ZR({domain:t,message:n,primaryType:r,types:i});let a=[`0x19`,`0x01`];return t&&a.push(nz({domain:t,types:i})),r!==`EIP712Domain`&&a.push(rz({data:n,primaryType:r,types:i})),YI(...a)}function $R(e){let{primaryType:t,types:n}=e,r=``,i=dz({primaryType:t,types:n});i.delete(t);let a=[t,...Array.from(i).sort()];for(let e of a)r+=`${e}(${(n[e]??[]).map(({name:e,type:t})=>`${t} ${e}`).join(`,`)})`;return r}function ez(e){return[typeof e?.name==`string`&&{name:`name`,type:`string`},e?.version&&{name:`version`,type:`string`},(typeof e?.chainId==`number`||typeof e?.chainId==`bigint`)&&{name:`chainId`,type:`uint256`},e?.verifyingContract&&{name:`verifyingContract`,type:`address`},e?.salt&&{name:`salt`,type:`bytes32`}].filter(Boolean)}function tz(e){return ML(QR(e))}function nz(e){let{domain:t,types:n}=e;return rz({data:t,primaryType:`EIP712Domain`,types:{...n,EIP712Domain:n?.EIP712Domain||ez(t)}})}function rz(e){let{data:t,primaryType:n,types:r}=e;return ML(cz({data:t,primaryType:n,types:r}))}var iz=class extends z{constructor({expectedSize:e,givenSize:t}){super(`Expected bytes${e}, got bytes${t}.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.BytesSizeMismatchError`})}},az=class extends z{constructor({domain:e}){super(`Invalid domain "${ZF(e)}".`,{metaMessages:[`Must be a valid EIP-712 domain.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidDomainError`})}},oz=class extends z{constructor({primaryType:e,types:t}){super(`Invalid primary type \`${e}\` must be one of \`${JSON.stringify(Object.keys(t))}\`.`,{metaMessages:["Check that the primary type is a key in `types`."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidPrimaryTypeError`})}},sz=class extends z{constructor({type:e}){super(`Struct type "${e}" is invalid.`,{metaMessages:[`Struct type must not be a Solidity type.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidStructTypeError`})}};function cz(e){let{data:t,primaryType:n,types:r}=e,i=[{type:`bytes32`}],a=[lz({primaryType:n,types:r})];for(let e of r[n]??[]){let[n,o]=uz({types:r,name:e.name,type:e.type,value:t[e.name]});i.push(n),a.push(o)}return UR(i,a)}function lz(e){let{primaryType:t,types:n}=e;return ML(eL($R({primaryType:t,types:n})))}function uz(e){let{types:t,name:n,type:r,value:i}=e;if(t[r]!==void 0)return[{type:`bytes32`},ML(cz({data:i,primaryType:r,types:t}))];if(r===`bytes`)return i=`0x${(i.length%2?`0`:``)+i.slice(2)}`,[{type:`bytes32`},ML(i,{as:`Hex`})];if(r===`string`)return[{type:`bytes32`},ML(bL(i),{as:`Hex`})];if(r.lastIndexOf(`]`)===r.length-1){let e=r.slice(0,r.lastIndexOf(`[`)),a=i.map(r=>uz({name:n,type:e,types:t,value:r}));return[{type:`bytes32`},ML(UR(a.map(([e])=>e),a.map(([,e])=>e)))]}return[{type:r},i]}function dz(e,t=new Set){let{primaryType:n,types:r}=e,i=n.match(/^\w*/u)?.[0];if(t.has(i)||r[i]===void 0)return t;t.add(i);for(let e of r[i])dz({primaryType:e.type,types:r},t);return t}function fz(e){if(e===`address`||e===`bool`||e===`string`||e.startsWith(`bytes`)||e.startsWith(`uint`)||e.startsWith(`int`))throw new sz({type:e})}gi(),Ei();function pz(e){if(typeof e==`string`){if(!Ci(e,{strict:!1}))throw new hi({address:e});return{address:e,type:`json-rpc`}}if(!Ci(e.address,{strict:!1}))throw new hi({address:e.address});return{address:e.address,nonceManager:e.nonceManager,sign:e.sign,signAuthorization:e.signAuthorization,signMessage:e.signMessage,signTransaction:e.signTransaction,signTypedData:e.signTypedData,source:`custom`,type:`local`}}function mz(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;ohz(Object.values(e)[n],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>hz(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function gz(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return gz(i.components,a.components,n[r]);let o=[i.type,a.type];if(o.includes(`address`)&&o.includes(`bytes20`)||(o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`))&&XL(n[r],{strict:!1}))return o}}kt();function _z(e,t={}){let{prepare:n=!0}=t,r=Array.isArray(e)||typeof e==`string`?Tt(e):e;return{...r,...n?{hash:xz(r)}:{}}}function vz(e,t,n){let{args:r=[],prepare:i=!0}=n??{},a=cL(t,{strict:!1}),o=e.filter(e=>a?e.type===`function`||e.type===`error`?yz(e)===rL(t,0,4):e.type===`event`?xz(e)===t:!1:`name`in e&&e.name===t);if(o.length===0)throw new Cz({name:t});if(o.length===1)return{...o[0],...i?{hash:xz(o[0])}:{}};let s;for(let e of o)if(`inputs`in e){if(!r||r.length===0){if(!e.inputs||e.inputs.length===0)return{...e,...i?{hash:xz(e)}:{}};continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===r.length&&r.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?hz(t,r):!1})){if(s&&`inputs`in s&&s.inputs){let t=gz(e.inputs,s.inputs,r);if(t)throw new Sz({abiItem:e,type:t[0]},{abiItem:s,type:t[1]})}s=e}}let c=(()=>{if(s)return s;let[e,...t]=o;return{...e,overloads:t}})();if(!c)throw new Cz({name:t});return{...c,...i?{hash:xz(c)}:{}}}function yz(...e){return rL(xz((()=>{if(Array.isArray(e[0])){let[t,n]=e;return vz(t,n)}return e[0]})()),0,4)}function bz(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return vz(t,n)}return e[0]})();return mz(typeof t==`string`?t:ue(t))}function xz(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return vz(t,n)}return e[0]})();return typeof t!=`string`&&`hash`in t&&t.hash?t.hash:ML(eL(bz(t)))}var Sz=class extends z{constructor(e,t){super(`Found ambiguous types in overloaded ABI Items.`,{metaMessages:[`\`${e.type}\` in \`${mz(ue(e.abiItem))}\`, and`,`\`${t.type}\` in \`${mz(ue(t.abiItem))}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.AmbiguityError`})}},Cz=class extends z{constructor({name:e,data:t,type:n=`item`}){let r=e?` with name "${e}"`:t?` with data "${t}"`:``;super(`ABI ${n}${r} not found.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.NotFoundError`})}};function wz(...e){let[t,n=[]]=(()=>{if(Array.isArray(e[0])){let[t,n,r]=e;return[Tz(t,n,{args:r}),r]}let[t,n]=e;return[t,n]})(),{overloads:r}=t,i=r?Tz([t,...r],t.name,{args:n}):t,a=Ez(i),o=n.length>0?UR(i.inputs,n):void 0;return o?YI(a,o):a}function Tz(e,t,n){let r=vz(e,t,n);if(r.type!==`function`)throw new Cz({name:t,type:`function`});return r}function Ez(e){return yz(e)}function Dz(e){let{privateKey:t}=e;return FL($j.ProjectivePoint.fromPrivateKey(typeof t==`string`?t.slice(2):QI(t).slice(2)))}function Oz(e={}){let{as:t=`Hex`}=e,n=$j.utils.randomPrivateKey();return t===`Hex`?QI(n):n}function kz(e){let{extraEntropy:t=eR,hash:n,payload:r,privateKey:i}=e,{r:a,s:o,recovery:s}=$j.sign(r instanceof Uint8Array?r:yL(r),i instanceof Uint8Array?i:yL(i),{extraEntropy:typeof t==`boolean`?t:XI(t).slice(2),lowS:!0,...n?{prehash:!0}:{}});return{r:a,s:o,yParity:s}}function Az(e,t=0){if(!/^(-?)([0-9]*)\.?([0-9]*)$/.test(e))throw new jz({value:e});let[n=``,r=`0`]=e.split(`.`),i=n.startsWith(`-`);if(i&&(n=n.slice(1)),r=r.replace(/(0+)$/,``),t===0)Math.round(Number(`.${r}`))===1&&(n=`${BigInt(n)+1n}`),r=``;else if(r.length>t){let[e,i,a]=[r.slice(0,t-1),r.slice(t-1,t),r.slice(t)],o=Math.round(Number(`${i}.${a}`));r=o>9?`${BigInt(e)+BigInt(1)}0`.padStart(e.length+1,`0`):`${e}${o}`,r.length>t&&(r=r.slice(1),n=`${BigInt(n)+1n}`),r=r.slice(0,t)}else r=r.padEnd(t,`0`);return BigInt(`${i?`-`:``}${n}${r}`)}var jz=class extends z{constructor({value:e}){super(`Value \`${e}\` is not a valid decimal number.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Value.InvalidDecimalNumberError`})}},Mz=new TextEncoder,Nz=new TextDecoder,Pz=Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[t,e.charCodeAt(0)])),Fz={...Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[e.charCodeAt(0),t])),61:0,45:62,95:63};function Iz(e,t={}){let{pad:n=!0,url:r=!1}=t,i=new Uint8Array(Math.ceil(e.length/3)*4);for(let t=0,n=0;n>18],i[t+1]=Pz[r>>12&63],i[t+2]=Pz[r>>6&63],i[t+3]=Pz[r&63]}let a=e.length%3,o=Math.floor(e.length/3)*4+(a&&a+1),s=Nz.decode(new Uint8Array(i.buffer,0,o));return n&&a===1&&(s+=`==`),n&&a===2&&(s+=`=`),r&&(s=s.replaceAll(`+`,`-`).replaceAll(`/`,`_`)),s}function Lz(e,t={}){return Iz(yL(e),t)}function Rz(e){let t=e.replace(/=+$/,``),n=t.length,r=new Uint8Array(n+3);Mz.encodeInto(t+`===`,r);for(let e=0,n=0;e>16,r[n+1]=t>>8&255,r[n+2]=t&255}let i=(n>>2)*3+(n%4&&n%4-1);return new Uint8Array(r.buffer,0,i)}function zz(e){let t=e[4]===0?5:4,n=t+32,r=e[n+2]===0?n+3:n+2,i=BigInt(QI(e.slice(t,n))),a=BigInt(QI(e.slice(r)));return{r:i,s:a>Qj.CURVE.n/2n?Qj.CURVE.n-a:a}}async function Bz(e){try{let t=e.getPublicKey();if(!t)throw new Yz;let n=new Uint8Array(t),r=await crypto.subtle.importKey(`spki`,new Uint8Array(n),{name:`ECDSA`,namedCurve:`P-256`,hash:`SHA-256`},!0,[`verify`]);return FL(new Uint8Array(await crypto.subtle.exportKey(`raw`,r)))}catch(t){if(t.message!==`Permission denied to access object`)throw t;let n=new Uint8Array(e.attestationObject),r=e=>{let t=new Uint8Array([e,88,32]);for(let e=0;en[e+r]===t))return e+t.length;throw new Yz},i=r(33),a=r(34);return FL(new Uint8Array([4,...n.slice(i,i+32),...n.slice(a,a+32)]))}}var Vz=Uint8Array.from([105,171,180,181,160,222,75,198,42,42,32,31,141,37,186,233]);async function Hz(e){let{createFn:t=window.navigator.credentials.create.bind(window.navigator.credentials),...n}=e,r=Gz(n);try{let e=await t(r);if(!e)throw new Yz;let n=e.response,i=await Bz(n);return{id:e.id,publicKey:i,raw:e}}catch(e){throw new Yz({cause:e})}}function Uz(e={}){let{flag:t=5,rpId:n=window.location.hostname,signCount:r=0}=e;return YI(NL(eL(n)),$I(t,{size:1}),$I(r,{size:4}))}function Wz(e){let{challenge:t,crossOrigin:n=!1,extraClientData:r,origin:i=window.location.origin}=e;return JSON.stringify({type:`webauthn.get`,challenge:Lz(t,{url:!0,pad:!1}),origin:i,crossOrigin:n,...r})}function Gz(e){let{attestation:t=`none`,authenticatorSelection:n={residentKey:`preferred`,requireResidentKey:!1,userVerification:`required`},challenge:r=Vz,excludeCredentialIds:i,extensions:a,name:o,rp:s={id:window.location.hostname,name:window.document.title},user:c}=e,l=c?.name??o;return{publicKey:{attestation:t,authenticatorSelection:n,challenge:typeof r==`string`?yL(r):r,...i?{excludeCredentials:i?.map(e=>({id:Rz(e),type:`public-key`}))}:{},pubKeyCredParams:[{type:`public-key`,alg:-7}],...a&&{extensions:a},rp:s,user:{id:c?.id??ML(bL(l),{as:`Bytes`}),name:l,displayName:c?.displayName??l}}}}function Kz(e){let{credentialId:t,challenge:n,extensions:r,rpId:i=window.location.hostname,userVerification:a=`required`}=e;return{publicKey:{...t?{allowCredentials:Array.isArray(t)?t.map(e=>({id:Rz(e),type:`public-key`})):[{id:Rz(t),type:`public-key`}]}:{},challenge:yL(n),...r&&{extensions:r},rpId:i,userVerification:a}}}function qz(e){let{challenge:t,crossOrigin:n,extraClientData:r,flag:i,origin:a,rpId:o,signCount:s,userVerification:c=`required`}=e,l=Uz({flag:i,rpId:o,signCount:s}),u=Wz({challenge:t,crossOrigin:n,extraClientData:r,origin:a}),d=NL(eL(u));return{metadata:{authenticatorData:l,clientDataJSON:u,challengeIndex:u.indexOf(`"challenge"`),typeIndex:u.indexOf(`"type"`),userVerificationRequired:c===`required`},payload:YI(l,d)}}async function Jz(e){let{getFn:t=window.navigator.credentials.get.bind(window.navigator.credentials),...n}=e,r=Kz(n);try{let e=await t(r);if(!e)throw new Xz;let n=e.response,i=String.fromCharCode(...new Uint8Array(n.clientDataJSON)),a=i.indexOf(`"challenge"`),o=i.indexOf(`"type"`),s=zz(new Uint8Array(n.signature));return{metadata:{authenticatorData:QI(new Uint8Array(n.authenticatorData)),clientDataJSON:i,challengeIndex:a,typeIndex:o,userVerificationRequired:r.publicKey.userVerification===`required`},signature:s,raw:e}}catch(e){throw new Xz({cause:e})}}var Yz=class extends z{constructor({cause:e}={}){super(`Failed to create credential.`,{cause:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`WebAuthnP256.CredentialCreationFailedError`})}},Xz=class extends z{constructor({cause:e}={}){super(`Failed to request credential.`,{cause:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`WebAuthnP256.CredentialRequestFailedError`})}};async function Zz(e={}){let{extractable:t=!1}=e,n=await globalThis.crypto.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},t,[`sign`,`verify`]),r=await globalThis.crypto.subtle.exportKey(`raw`,n.publicKey),i=FL(new Uint8Array(r));return{privateKey:n.privateKey,publicKey:i}}async function Qz(e){let{payload:t,privateKey:n}=e,r=await globalThis.crypto.subtle.sign({name:`ECDSA`,hash:`SHA-256`},n,_L(t)),i=vL(new Uint8Array(r)),a=wL(CL(i,0,32)),o=wL(CL(i,32,64));return o>Qj.CURVE.n/2n&&(o=Qj.CURVE.n-o),{r:a,s:o}}var $z=`0x32323232`,eB={p256:`p256`,secp256k1:`secp256k1`,webauthnp256:`webauthn-p256`},tB={admin:`admin`,normal:`session`},nB={0:`minute`,1:`hour`,2:`day`,3:`week`,4:`month`,5:`year`},rB={address:`secp256k1`,p256:`p256`,secp256k1:`secp256k1`,"webauthn-p256":`webauthnp256`},iB={admin:`admin`,session:`normal`},aB={address:2,p256:0,secp256k1:2,"webauthn-p256":1},oB={day:2,hour:1,minute:0,month:4,week:3,year:5};function sB(e={}){let t=Oz();return fB({...e,privateKey:t})}async function cB(e){let{createFn:t,label:n,rpId:r,userId:i}=e,a=await Hz({authenticatorSelection:{requireResidentKey:!0,residentKey:`required`,userVerification:`required`},createFn:t,extensions:{credProps:!0},rp:r?{id:r,name:r}:void 0,user:{displayName:n,id:new Uint8Array(i??bL(n)),name:n}});return mB({...e,credential:{id:a.id,publicKey:a.publicKey},id:i?TL(i):RL(a.publicKey,{includePrefix:!1})})}function lB(e={}){let t=Oz();return hB({...e,privateKey:t})}async function uB(e={}){let t=await Zz();return gB({...e,keyPair:t})}function dB(e,t={}){let{chainId:n=e.chainId}=t,{expiry:r=0,id:i,prehash:a=!1,role:o=`admin`,type:s}=e,c=(()=>{let t=e.publicKey;return t===`0x`?t:s===`secp256k1`||s===`address`?iL(t)===20||oL(rL(t,0,12))===0n?rL(t,-20):JL(LL(t)):t})();return{...e,chainId:n,expiry:r,hash:_B({publicKey:c,type:s}),id:(i??c).toLowerCase(),prehash:a,publicKey:c.toLowerCase(),role:o,type:s}}function fB(e){let{chainId:t,expiry:n,feeToken:r,permissions:i,privateKey:a,role:o}=e;return dB({chainId:t,expiry:n,feeToken:r,permissions:i,privateKey(){return a},publicKey:RL(Dz({privateKey:a}),{includePrefix:!1}),role:o,type:`p256`})}function pB(e,t){let{chainId:n}=t,{publicKey:r}=e,i=iL(r)===20||oL(rL(r,0,12))===0n,a={};for(let t of e.permissions)t.type===`call`&&(a.calls??=[],a.calls.push({signature:t.selector,to:t.to===`0x3232323232323232323232323232323232323232`?void 0:t.to})),t.type===`spend`&&(a.spend??=[],a.spend.push({limit:t.limit,period:t.period,token:t.token}));return dB({chainId:n,expiry:e.expiry,permissions:a,publicKey:e.publicKey,role:tB[e.role],type:i?`address`:eB[e.type]})}function mB(e){let{credential:t,id:n,rpId:r}=e,i=RL(t.publicKey,{includePrefix:!1});return dB({chainId:e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,id:n,permissions:e.permissions,privateKey:{credential:t,rpId:r},publicKey:i,role:e.role,type:`webauthn-p256`})}function hB(e){let{privateKey:t}=e,n=RL(Dz({privateKey:t}),{includePrefix:!1});return dB({chainId:e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,permissions:e.permissions,privateKey:{privateKey(){return t}},publicKey:n,role:e.role,type:`webauthn-p256`})}function gB(e){let{chainId:t,expiry:n,feeToken:r,keyPair:i,permissions:a,role:o}=e,{privateKey:s}=i;return dB({chainId:t,expiry:n,feeToken:r,permissions:a,prehash:!0,privateKey:s,publicKey:RL(i.publicKey,{includePrefix:!1}),role:o,type:`p256`})}function _B(e){let{type:t}=e,n=vB(e.publicKey);return ML(UR([{type:`uint8`},{type:`bytes32`}],[aB[t],ML(n)]))}function vB(e){return iL(e)<32?tL(e,32):e}async function yB(e,t){let{address:n,storage:r,webAuthn:i,wrap:a=!0}=t,{privateKey:o,publicKey:s,type:c}=e;if(!o)throw Error(`Key does not have a private key to sign with. + +Key: +`+ZF(e,null,2));let l=n?tz({domain:{verifyingContract:n},message:{digest:t.payload},primaryType:`ERC1271Sign`,types:{ERC1271Sign:[{name:`digest`,type:`bytes32`}]}}):t.payload,[u,d]=await(async()=>{if(c===`p256`){let{privateKey:t}=e;if(typeof t==`function`)return[_R(kz({payload:l,privateKey:t()})),!1];if(t instanceof CryptoKey)return[_R(await Qz({payload:l,privateKey:t})),!0]}if(c===`secp256k1`)return[_R(aR({payload:l,privateKey:o()})),!1];if(c===`webauthn-p256`){if(o.privateKey){let{payload:e,metadata:t}=qz({challenge:l,origin:`https://ithaca.xyz`,rpId:`ithaca.xyz`}),{r:n,s:r}=kz({hash:!0,payload:e,privateKey:o.privateKey()});return[CB({metadata:t,signature:{r:n,s:r}}),!1]}let{credential:t,rpId:n}=o,a=`porto.webauthnVerified.${e.hash}`,s=Date.now(),c=!0;if(r){let e=await r.getItem(a);c=!e||s-e>6e5}let{signature:{r:u,s:d},raw:f,metadata:p}=await Jz({challenge:l,credentialId:t.id,getFn:i?.getFn,rpId:n,userVerification:c?`required`:`preferred`}),m=f.response;if(!m?.userHandle)throw Error(`No user handle in response`,{cause:{response:m}});let h=TL(new Uint8Array(m.userHandle));if(e.id&&XL(e.id)&&!YL(e.id,h))throw Error(`supplied webauthn key "${e.id}" does not match signature webauthn key "${h}"`,{cause:{id:h,key:e}});return c&&r&&await r.setItem(a,s),[CB({metadata:p,signature:{r:u,s:d}}),!1]}throw Error(`Key type "${c}" is not supported.\n\nKey:\n`+ZF(e,null,2))})();return a?wB(u,{keyType:c,prehash:d,publicKey:s}):u}function bB(e,t={}){let{expiry:n=0,prehash:r=!1,publicKey:i,role:a=`admin`,type:o}=e,{feeTokens:s,orchestrator:c}=t,l=Object.entries(xB(e,{feeTokens:s})).map(([e,t])=>{if(e===`calls`)return t.map(({signature:e,to:t})=>({selector:e?cL(e)?e:Ez(e):$z,to:t??`0x3232323232323232323232323232323232323232`,type:`call`}));if(e!==`feeToken`){if(e===`spend`)return t.map(({limit:e,period:t,token:n})=>({limit:e,period:t,token:n,type:`spend`}));throw Error(`Invalid permission type "${e}".`)}}).flat().filter(Boolean);return e.role===`session`&&c&&l.push({selector:$z,to:c,type:`call`}),{expiry:n,permissions:l??[],prehash:r,publicKey:vB(i),role:iB[a],type:rB[o]}}function xB(e,t){let{permissions:n}=e,r=n?.calls?[...n.calls]:[],i=n?.spend?[...n.spend]:[],a=t.feeTokens?.filter(e=>e.feeToken);if(a&&a.length>0){let t=SB(e,{feeTokens:a});if(t){let e=-1,n=oB.year;for(let r=0;re.feeToken.symbol===t.symbol?!0:!e.feeToken.symbol||e.feeToken.symbol===`native`?t.address===dv:!1);if(!r)return;let i=Az(e.feeToken.limit,r.decimals);return{...r,value:i}}function CB(e){let{metadata:t,signature:n}=e;return UR(GR([`struct WebAuthnAuth { bytes authenticatorData; string clientDataJSON; uint256 challengeIndex; uint256 typeIndex; bytes32 r; bytes32 s; }`,`WebAuthnAuth auth`]),[{authenticatorData:t.authenticatorData,challengeIndex:BigInt(t.challengeIndex),clientDataJSON:t.clientDataJSON,r:$I(n.r,{size:32}),s:$I(n.s,{size:32}),typeIndex:BigInt(t.typeIndex)}])}function wB(e,t){let{keyType:n,prehash:r=!1,publicKey:i}=t,a=_B({publicKey:i,type:n});return WR([`bytes`,`bytes32`,`bool`],[e,a,r])}function TB(e){let t=typeof e==`string`?{address:e}:e,n=t.sign?`privateKey`:`porto`,{address:r,sign:i,signMessage:a,signTransaction:o,signTypedData:s,type:c}=pz({address:t.address,sign({hash:e}){if(n===`porto`)throw Error("`sign` not supported on porto accounts.");if(!t.sign)throw Error("`sign` not supported.");return t.sign({hash:e})},signMessage({message:e}){return this.sign({hash:Fh(e)})},signTransaction(){throw Error("`signTransaction` not supported on porto accounts.")},signTypedData(e){return this.sign({hash:Uh(e)})}});return{address:r,keys:t.keys??void 0,sign:i,signMessage:a,signTransaction:o,signTypedData:s,source:n,type:c}}function EB(e,t={}){let{keys:n}=t;return TB({address:JL(tR({privateKey:e})),keys:n,async sign({hash:t}){return _R(aR({payload:t,privateKey:e}))},source:`privateKey`})}function DB(e,t={}){let{key:n,role:r}=t;if(n!==null){if(typeof n==`object`)return n;if(e.keys&&e.keys.length>0)return typeof n==`number`?e.keys[n]:e.keys.find(e=>e.privateKey&&(!r||e.role===r))}}async function OB(e,t){let{storage:n,replaySafe:r=!0,wrap:i=!0,webAuthn:a}=t,o=DB(e,t),s=r?tz({domain:{verifyingContract:e.address},message:{digest:t.payload},primaryType:`ERC1271Sign`,types:{ERC1271Sign:[{name:`digest`,type:`bytes32`}]}}):t.payload,c=o?({hash:e})=>yB(o,{address:null,payload:e,storage:n,webAuthn:a,wrap:i}):e.source===`privateKey`?e.sign:void 0;if(!c)throw Error(`cannot find key to sign with.`);return await c({hash:s})}var kB=()=>`IntersectionObserver`in window&&`IntersectionObserverEntry`in window&&`intersectionRatio`in IntersectionObserverEntry.prototype&&`isVisible`in IntersectionObserverEntry.prototype;function AB(e={}){let{prefix:t=`[Porto]`}=e,n=new Set;return{error:MB(console.error,{prefix:t}),errorOnce:MB(console.error,{memo:n,prefix:t}),log:MB(console.log,{prefix:t}),logOnce:MB(console.log,{memo:n,prefix:t}),warn:MB(console.warn,{prefix:t}),warnOnce:MB(console.warn,{memo:n,prefix:t})}}var jB=AB();function MB(e,t={}){let{memo:n,prefix:r}=t;return(...t)=>{let i=t.join(` `);n?.has(i)||(n?.add(i),e(`${r} ${i}`))}}function NB(){let e=navigator.userAgent.toLowerCase();return e.includes(`safari`)&&!e.includes(`chrome`)}function PB(){let e=navigator.userAgent.toLowerCase();return(e.includes(`firefox`)||e.includes(`fxios`))&&!e.includes(`seamonkey`)}function FB(){return window.navigator?.userAgentData?.mobile?!0:navigator.maxTouchPoints>1||/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(navigator.userAgent)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(navigator.userAgent.slice(0,4))}function IB(){let e=()=>void 0,t=()=>void 0;return{promise:new Promise((n,r)=>{e=n,t=r}),reject:t,resolve:e}}function LB(e){if(Array.isArray(e))return e.map(LB);if(typeof e==`function`)return;if(typeof e!=`object`||!e)return e;if(Object.getPrototypeOf(e)!==Object.prototype)try{return structuredClone(e)}catch{return}let t={};for(let[n,r]of Object.entries(e))t[n]=LB(r);return t}function RB(e,t){let n=[],r=new Set;for(let i of e){let e=t(i);r.has(e)||(r.add(e),n.push(i))}return n}function zB(){return typeof globalThis<`u`&&`crypto`in globalThis?globalThis.crypto.randomUUID():crypto.randomUUID()}function BB(e,{enabled:t=!0,id:n}){if(!t||!n)return e();if(BB.cache.get(n))return BB.cache.get(n);let r=e().finally(()=>BB.cache.delete(n));return BB.cache.set(n,r),r}(function(e){e.cache=new Map})(BB||={});function VB(e){return e}function HB(e,t={}){let{targetOrigin:n}=t,r=new Map;return VB({destroy(){for(let t of r.values())e.removeEventListener(`message`,t)},on(t,i,a){function o(e){e.data.topic===t&&(a&&e.data.id!==a||n&&e.origin!==n||i(e.data.payload,e))}return e.addEventListener(`message`,o),r.set(t,o),()=>e.removeEventListener(`message`,o)},async send(t,r,i){let a=zB();return e.postMessage(LB({id:a,payload:r,topic:t}),i??n??`*`),{id:a,payload:r,topic:t}},async sendAsync(e,t,n){let{id:r}=await this.send(e,t,n);return new Promise(t=>this.on(e,t,r))}})}function UB(e){let{from:t,to:n,waitForReady:r=!1}=e,i=!1,a=IB();t.on(`ready`,a.resolve);let o=VB({destroy(){t.destroy(),n.destroy(),i&&a.reject()},on(e,n,r){return t.on(e,n,r)},async send(e,t){return i=!0,r&&await a.promise.finally(()=>i=!1),n.send(e,t)},async sendAsync(e,t){return i=!0,r&&await a.promise.finally(()=>i=!1),n.sendAsync(e,t)}});return{...o,ready(e){o.send(`ready`,e)},waitForReady(){return a.promise}}}var WB={local:`http://localhost:5175/dialog/`,prod:`https://id.porto.sh/dialog`,stg:`https://stg.id.porto.sh/dialog`};function GB(e){return e}function KB(e={}){let{skipProtocolCheck:t,skipUnsupported:n}=e,r=e=>!n&&NB()&&e?.some(e=>[`wallet_connect`,`eth_requestAccounts`].includes(e.method));return typeof window>`u`?JB():GB({name:`iframe`,setup(e){let{host:n,internal:i,theme:a,themeController:o}=e,{store:s}=i,c=qB().setup(e),l=!1,u=new URL(n),d=document.createElement(`dialog`);d.dataset.porto=``,d.setAttribute(`role`,`dialog`),d.setAttribute(`aria-closed`,`true`),d.setAttribute(`aria-label`,`Porto Wallet`),d.setAttribute(`hidden`,`until-found`),Object.assign(d.style,{background:`transparent`,border:`0`,outline:`0`,padding:`0`,position:`fixed`}),document.body.appendChild(d);let f=document.createElement(`iframe`);f.setAttribute(`data-testid`,`porto`);let p=[`payment`,`publickey-credentials-get ${u.origin}`,`publickey-credentials-create ${u.origin}`];PB()||p.push(`clipboard-write`),f.setAttribute(`allow`,p.join(`; `)),f.setAttribute(`tabindex`,`0`),f.setAttribute(`sandbox`,`allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox`),f.setAttribute(`src`,tV(n)),f.setAttribute(`title`,`Porto`),Object.assign(f.style,{backgroundColor:`transparent`,border:`0`,colorScheme:`light dark`,height:`100%`,left:`0`,position:`fixed`,top:`0`,width:`100%`});let m=document.createElement(`style`);m.innerHTML=` dialog[data-porto]::backdrop { background: transparent!important; } - `,d.appendChild(m),d.appendChild(f);let h=yN({from:vN(window,{targetOrigin:u.origin}),to:vN(f.contentWindow,{targetOrigin:u.origin}),waitForReady:!0});o?._setup(h,!0);let g=window.matchMedia(`(max-width: 460px)`),_=()=>{h.send(`__internal`,{type:`resize`,width:g.matches?460:461})};g.addEventListener(`change`,_),h.on(`ready`,t=>{let n=e.internal.store.getState().chainIds.filter(e=>t.chainIds.includes(e));n.length===0&&(n=t.chainIds),s.setState(e=>({...e,chainIds:n})),h.send(`__internal`,{chainIds:n,mode:`iframe`,referrer:DN(),theme:a,type:`init`}),_()}),h.on(`rpc-response`,e=>{r([e._request])&&(f.src=f.src),kN(s,e)}),h.on(`__internal`,e=>{e.type===`switch`&&e.mode===`popup`&&(c.open(),c.syncRequests(s.getState().requestQueue))});let v=null,y=null,b=()=>ON(s),x=e=>{e.key===`Escape`&&ON(s)},S=new MutationObserver(e=>{for(let t of e){if(t.type!==`attributes`)continue;let e=t.attributeName;e&&e===`inert`&&d.removeAttribute(e)}});S.observe(d,{attributeOldValue:!0,attributes:!0});let C=!1,w=()=>{C&&(C=!1,d.removeEventListener(`click`,b),document.removeEventListener(`keydown`,x),d.style.pointerEvents=`none`,y?.focus(),y=null,Object.assign(document.body.style,v??``),document.body.style.overflow=v?.overflow??``)},ee=()=>{C||(C=!0,d.addEventListener(`click`,b),document.addEventListener(`keydown`,x),f.focus(),d.style.pointerEvents=`auto`,v=Object.assign({},document.body.style),document.body.style.overflow=`hidden`)},te=!1,ne=()=>{te||(te=!0,document.activeElement instanceof HTMLElement&&(y=document.activeElement),d.removeAttribute(`hidden`),d.removeAttribute(`aria-closed`),d.showModal())},re=()=>{if(te){te=!1,d.setAttribute(`hidden`,`true`),d.setAttribute(`aria-closed`,`true`),d.close();for(let e of d.parentNode?Array.from(d.parentNode.children):[])e!==d&&e.hasAttribute(`inert`)&&e.removeAttribute(`inert`)}};return{close(){c.close(),l=!1,h.send(`__internal`,{mode:`iframe`,referrer:DN(),type:`init`}),re(),w()},destroy(){c.close(),l=!1,w(),re(),c.destroy(),h.destroy(),d.remove(),S.disconnect(),g.removeEventListener(`change`,_)},open(){l||(l=!0,ne(),ee(),h.send(`__internal`,{mode:`iframe`,referrer:DN(),type:`init`}))},async secure(){let{trustedHosts:e}=await h.waitForReady(),n=(()=>{if(t)return!0;let e=window.location.protocol.startsWith(`https`);return e||sN.warnOnce(`Detected insecure protocol (HTTP).`,`\n\nThe Porto iframe is not supported on HTTP origins (${window.location.origin})`,`due to lack of WebAuthn support.`,`See https://porto.sh/sdk#secure-origins-https for more information.`),e})(),r=aN(),i=!!e?.includes(window.location.hostname),a=!!(r||i);return a||sN.warnOnce([`Warning: Browser does not support IntersectionObserver v2 or host "${u.hostname}" is not trusted by Porto.`,`This may result in the dialog falling back to a popup.`,``,`Add "${u.hostname}" to the trusted hosts list to enable iframe dialog: https://github.com/ithacaxyz/porto/edit/main/src/trusted-hosts.ts`].join(` -`)),{frame:a,host:i,protocol:n}},async syncRequests(e){let{methodPolicies:t}=await h.waitForReady(),n=await this.secure(),i=e?.every(e=>t?.find(t=>t.method===e.request.method)?.modes?.headless===!0),a=r(e.map(e=>e.request));if(!i&&(a||!n.protocol||!n.frame))c.syncRequests(e);else{let n=e.some(e=>EN(e.request,{methodPolicies:t,targetOrigin:u.origin}));!l&&n&&this.open(),h.send(`rpc-requests`,e)}}}},supportsHeadless:!0})}function CN(e={}){if(typeof window>`u`)return wN();let{type:t=`auto`,size:n=TN}=e;return xN({name:`popup`,setup(e){let{host:r,internal:i,themeController:a}=e,{store:o}=i,s=new URL(r),c=null,l=t===`page`||t===`auto`&&dN()?`page`:`popup`;function u(){c&&ON(o)}let d=(()=>{let e=setInterval(()=>{c?.closed&&ON(o)},100);return()=>clearInterval(e)})(),f;return a?._setup(null,!0),{close(){c&&=(c.close(),null)},destroy(){this.close(),window.removeEventListener(`focus`,u),f?.destroy(),d()},open(){if(l===`popup`){let e=(window.innerWidth-n.width)/2+window.screenX,t=window.screenY+100;c=window.open(AN(r),`_blank`,`width=${n.width},height=${n.height},left=${e},top=${t}`)}else c=window.open(AN(r),`_blank`);if(!c)throw Error(`Failed to open popup`);f=yN({from:vN(window,{targetOrigin:s.origin}),to:vN(c,{targetOrigin:s.origin}),waitForReady:!0}),a?._setup(f,!1),f.send(`__internal`,{mode:l===`page`?`page`:`popup`,referrer:DN(),theme:a?.getTheme()??e.theme,type:`init`}),f.on(`rpc-response`,e=>kN(o,e)),window.removeEventListener(`focus`,u),window.addEventListener(`focus`,u)},async secure(){return{frame:!0,host:!0,protocol:!0}},async syncRequests(e){e.some(e=>EN(e.request))&&((!c||c.closed)&&this.open(),c?.focus()),f?.send(`rpc-requests`,e)}}},supportsHeadless:!1})}function wN(){return xN({name:`noop`,setup(){return{close(){},destroy(){},open(){},async secure(){return{frame:!0,host:!0,protocol:!0}},async syncRequests(){}}},supportsHeadless:!0})}const TN={height:282,width:360};function EN(e,t={}){let{methodPolicies:n,targetOrigin:r}=t,i=n?.find(t=>t.method===e.method);return i&&i.modes?.headless?!!(typeof i.modes.headless==`object`&&i.modes.headless.sameOrigin&&r!==window.location.origin):!0}function DN(){return{icon:(()=>{let e=document.querySelector(`link[rel="icon"][media="(prefers-color-scheme: dark)"]`)?.href,t=document.querySelector(`link[rel="icon"][media="(prefers-color-scheme: light)"]`)?.href??document.querySelector(`link[rel="icon"]`)?.href;return e&&t&&e!==t?{dark:e,light:t}:window.matchMedia(`(prefers-color-scheme: dark)`).matches?e:t})(),title:document.title}}function ON(e){e.setState(e=>({...e,requestQueue:e.requestQueue.map(e=>({account:e.account,error:new HM,request:e.request,status:`error`}))}))}function kN(e,t){e.setState(e=>({...e,requestQueue:e.requestQueue.map(e=>e.request.id===t.id?t.error?{account:e.account,error:t.error,request:e.request,status:`error`}:{account:e.account,request:e.request,result:t.result,status:`success`}:e)}))}function AN(e){let t=new URL(e),n=new URLSearchParams(window.location.search);for(let[e,r]of n.entries())e.startsWith(`porto.`)&&t.searchParams.set(e.slice(6),r);return t.toString()}function jN(e){let t=new CustomEvent(`eip6963:announceProvider`,{detail:Object.freeze(e)});window.dispatchEvent(t);let n=()=>window.dispatchEvent(t);return window.addEventListener(`eip6963:requestProvider`,n),()=>window.removeEventListener(`eip6963:requestProvider`,n)}Object.freeze({status:`aborted`});function z(e,t,n){function r(n,r){var i;for(let a in Object.defineProperty(n,`_zod`,{value:n._zod??{},enumerable:!1}),(i=n._zod).traits??(i.traits=new Set),n._zod.traits.add(e),t(n,r),o.prototype)a in n||Object.defineProperty(n,a,{value:o.prototype[a].bind(n)});n._zod.constr=o,n._zod.def=r}let i=n?.Parent??Object;class a extends i{}Object.defineProperty(a,`name`,{value:e});function o(e){var t;let i=n?.Parent?new a:this;r(i,e),(t=i._zod).deferred??(t.deferred=[]);for(let e of i._zod.deferred)e();return i}return Object.defineProperty(o,`init`,{value:r}),Object.defineProperty(o,Symbol.hasInstance,{value:t=>n?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,`name`,{value:e}),o}var MN=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const NN={};function PN(e){return e&&Object.assign(NN,e),NN}function FN(e,t){return typeof t==`bigint`?t.toString():t}function IN(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function LN(e){return e==null}function RN(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}var zN=Symbol(`evaluating`);function BN(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==zN)return r===void 0&&(r=zN,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}function VN(e,t,n){Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0})}function HN(...e){let t={};for(let n of e){let e=Object.getOwnPropertyDescriptors(n);Object.assign(t,e)}return Object.defineProperties({},t)}const UN=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function WN(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function GN(e){if(WN(e)===!1)return!1;let t=e.constructor;if(t===void 0)return!0;let n=t.prototype;return!(WN(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}const KN=new Set([`string`,`number`,`bigint`,`boolean`,`symbol`,`undefined`]);function qN(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function JN(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function YN(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error("Cannot specify both `message` and `error` params");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function XN(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function ZN(e,t){let n=e._zod.def,r=HN(e._zod.def,{get shape(){let e={};for(let r in t){if(!(r in n.shape))throw Error(`Unrecognized key: "${r}"`);t[r]&&(e[r]=n.shape[r])}return VN(this,`shape`,e),e},checks:[]});return JN(e,r)}function QN(e,t){let n=e._zod.def,r=HN(e._zod.def,{get shape(){let r={...e._zod.def.shape};for(let e in t){if(!(e in n.shape))throw Error(`Unrecognized key: "${e}"`);t[e]&&delete r[e]}return VN(this,`shape`,r),r},checks:[]});return JN(e,r)}function $N(e,t,n){let r=HN(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in r))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t])}else for(let t in r)i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t];return VN(this,`shape`,i),i},checks:[]});return JN(t,r)}function eP(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function nP(e){return typeof e==`string`?e:e?.message}function rP(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=nP(e.inst?._zod.def?.error?.(e))??nP(t?.error?.(e))??nP(n.customError?.(e))??nP(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}function iP(e){return Array.isArray(e)?`array`:typeof e==`string`?`string`:`unknown`}var aP=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,`_zod`,{value:e._zod,enumerable:!1}),Object.defineProperty(e,`issues`,{value:t,enumerable:!1}),e.message=JSON.stringify(t,FN,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})};const oP=z(`$ZodError`,aP),sP=z(`$ZodError`,aP,{Parent:Error}),cP=e=>(t,n,r,i)=>{let a=r?Object.assign(r,{async:!1}):{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new MN;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>rP(e,a,PN())));throw UN(t,i?.callee),t}return o.value},lP=cP(sP),uP=(e=>async(t,n,r,i)=>{let a=r?Object.assign(r,{async:!0}):{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>rP(e,a,PN())));throw UN(t,i?.callee),t}return o.value})(sP),dP=(e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new MN;return a.issues.length?{success:!1,error:new(e??oP)(a.issues.map(e=>rP(e,i,PN())))}:{success:!0,data:a.value}})(sP),fP=(e=>async(t,n,r)=>{let i=r?Object.assign(r,{async:!0}):{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>rP(e,i,PN())))}:{success:!0,data:a.value}})(sP),pP=(e=>(t,n,r)=>{let i=r?Object.assign(r,{direction:`backward`}):{direction:`backward`};return cP(e)(t,n,i)})(sP),mP=(e=>(t,n,r)=>cP(e)(t,n,r))(sP),hP=e=>{let t=e?`[\\s\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\s\\S]*`;return RegExp(`^${t}$`)},gP=/^-?\d+n?$/,_P=/^-?\d+(?:\.\d+)?/,vP=/^(?:true|false)$/i;var yP=/^null$/i,bP=/^undefined$/i;const xP=z(`$ZodCheck`,(e,t)=>{var n;e._zod??={},e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])});var SP={number:`number`,bigint:`bigint`,object:`date`};const CP=z(`$ZodCheckGreaterThan`,(e,t)=>{xP.init(e,t);let n=SP[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.minimum:n.exclusiveMinimum)??-1/0;t.value>r&&(t.inclusive?n.minimum=t.value:n.exclusiveMinimum=t.value)}),e._zod.check=r=>{(t.inclusive?r.value>=t.value:r.value>t.value)||r.issues.push({origin:n,code:`too_small`,minimum:t.value,input:r.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}}),wP=z(`$ZodCheckMinLength`,(e,t)=>{var n;xP.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!LN(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.minimum??-1/0;t.minimum>n&&(e._zod.bag.minimum=t.minimum)}),e._zod.check=n=>{let r=n.value;if(r.length>=t.minimum)return;let i=iP(r);n.issues.push({origin:i,code:`too_small`,minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),TP=z(`$ZodCheckStringFormat`,(e,t)=>{var n,r;xP.init(e,t),e._zod.onattach.push(e=>{let n=e._zod.bag;n.format=t.format,t.pattern&&(n.patterns??=new Set,n.patterns.add(t.pattern))}),t.pattern?(n=e._zod).check??(n.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:t.format,input:n.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(r=e._zod).check??(r.check=()=>{})}),EP=z(`$ZodCheckRegex`,(e,t)=>{TP.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:`regex`,input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}}),DP={major:4,minor:1,patch:12},OP=z(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=DP;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=eP(e),i;for(let a of t){if(a._zod.def.when){if(!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new MN;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=eP(e,t))});else{if(e.issues.length===t)continue;r||=eP(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(eP(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new MN;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new MN;return o.then(e=>t(e,r,a))}return t(o,r,a)}}e[`~standard`]={validate:t=>{try{let n=dP(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return fP(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}}),kP=z(`$ZodString`,(e,t)=>{OP.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??hP(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),AP=z(`$ZodNumber`,(e,t)=>{OP.init(e,t),e._zod.pattern=e._zod.bag.pattern??_P,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Number(n.value)}catch{}let i=n.value;if(typeof i==`number`&&!Number.isNaN(i)&&Number.isFinite(i))return n;let a=typeof i==`number`?Number.isNaN(i)?`NaN`:Number.isFinite(i)?void 0:`Infinity`:void 0;return n.issues.push({expected:`number`,code:`invalid_type`,input:i,inst:e,...a?{received:a}:{}}),n}}),jP=z(`$ZodBoolean`,(e,t)=>{OP.init(e,t),e._zod.pattern=vP,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=!!n.value}catch{}let i=n.value;return typeof i==`boolean`||n.issues.push({expected:`boolean`,code:`invalid_type`,input:i,inst:e}),n}}),MP=z(`$ZodBigInt`,(e,t)=>{OP.init(e,t),e._zod.pattern=gP,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=BigInt(n.value)}catch{}return typeof n.value==`bigint`||n.issues.push({expected:`bigint`,code:`invalid_type`,input:n.value,inst:e}),n}}),NP=z(`$ZodUndefined`,(e,t)=>{OP.init(e,t),e._zod.pattern=bP,e._zod.values=new Set([void 0]),e._zod.optin=`optional`,e._zod.optout=`optional`,e._zod.parse=(t,n)=>{let r=t.value;return r===void 0||t.issues.push({expected:`undefined`,code:`invalid_type`,input:r,inst:e}),t}}),PP=z(`$ZodNull`,(e,t)=>{OP.init(e,t),e._zod.pattern=yP,e._zod.values=new Set([null]),e._zod.parse=(t,n)=>{let r=t.value;return r===null||t.issues.push({expected:`null`,code:`invalid_type`,input:r,inst:e}),t}}),FP=z(`$ZodAny`,(e,t)=>{OP.init(e,t),e._zod.parse=e=>e}),IP=z(`$ZodUnknown`,(e,t)=>{OP.init(e,t),e._zod.parse=e=>e}),LP=z(`$ZodDate`,(e,t)=>{OP.init(e,t),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=new Date(n.value)}catch{}let i=n.value,a=i instanceof Date;return a&&!Number.isNaN(i.getTime())||n.issues.push({expected:`date`,code:`invalid_type`,input:i,...a?{received:`Invalid Date`}:{},inst:e}),n}});function RP(e,t,n){e.issues.length&&t.issues.push(...tP(n,e.issues)),t.value[n]=e.value}const zP=z(`$ZodArray`,(e,t)=>{OP.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;eRP(t,n,e))):RP(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function BP(e,t,n,r){e.issues.length&&t.issues.push(...tP(n,e.issues)),e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function VP(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key "${n}": expected a Zod schema`);let n=XN(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function HP(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type;for(let i of Object.keys(t)){if(s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>BP(e,n,i,t))):BP(a,n,i,t)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}const UP=z(`$ZodObject`,(e,t)=>{if(OP.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,`shape`,{get:()=>{let n={...e};return Object.defineProperty(t,`shape`,{value:n}),n}})}let n=IN(()=>VP(t));BN(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=WN,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e]._zod.run({value:s[e],issues:[]},o);n instanceof Promise?c.push(n.then(n=>BP(n,t,e,s))):BP(n,t,e,s)}return i?HP(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}});function WP(e,t,n,r){for(let n of e)if(n.issues.length===0)return t.value=n.value,t;let i=e.filter(e=>!eP(e));return i.length===1?(t.value=i[0].value,i[0]):(t.issues.push({code:`invalid_union`,input:t.value,inst:n,errors:e.map(e=>e.issues.map(e=>rP(e,r,PN())))}),t)}const GP=z(`$ZodUnion`,(e,t)=>{OP.init(e,t),BN(e._zod,`optin`,()=>t.options.some(e=>e._zod.optin===`optional`)?`optional`:void 0),BN(e._zod,`optout`,()=>t.options.some(e=>e._zod.optout===`optional`)?`optional`:void 0),BN(e._zod,`values`,()=>{if(t.options.every(e=>e._zod.values))return new Set(t.options.flatMap(e=>Array.from(e._zod.values)))}),BN(e._zod,`pattern`,()=>{if(t.options.every(e=>e._zod.pattern)){let e=t.options.map(e=>e._zod.pattern);return RegExp(`^(${e.map(e=>RN(e.source)).join(`|`)})$`)}});let n=t.options.length===1,r=t.options[0]._zod.run;e._zod.parse=(i,a)=>{if(n)return r(i,a);let o=!1,s=[];for(let e of t.options){let t=e._zod.run({value:i.value,issues:[]},a);if(t instanceof Promise)s.push(t),o=!0;else{if(t.issues.length===0)return t;s.push(t)}}return o?Promise.all(s).then(t=>WP(t,i,e,a)):WP(s,i,e,a)}}),KP=z(`$ZodDiscriminatedUnion`,(e,t)=>{GP.init(e,t);let n=e._zod.parse;BN(e._zod,`propValues`,()=>{let e={};for(let n of t.options){let r=n._zod.propValues;if(!r||Object.keys(r).length===0)throw Error(`Invalid discriminated union option at index "${t.options.indexOf(n)}"`);for(let[t,n]of Object.entries(r)){e[t]||(e[t]=new Set);for(let r of n)e[t].add(r)}}return e});let r=IN(()=>{let e=t.options,n=new Map;for(let r of e){let e=r._zod.propValues?.[t.discriminator];if(!e||e.size===0)throw Error(`Invalid discriminated union option at index "${t.options.indexOf(r)}"`);for(let t of e){if(n.has(t))throw Error(`Duplicate discriminator value "${String(t)}"`);n.set(t,r)}}return n});e._zod.parse=(i,a)=>{let o=i.value;if(!WN(o))return i.issues.push({code:`invalid_type`,expected:`object`,input:o,inst:e}),i;let s=r.value.get(o?.[t.discriminator]);return s?s._zod.run(i,a):t.unionFallback?n(i,a):(i.issues.push({code:`invalid_union`,errors:[],note:`No matching discriminator`,discriminator:t.discriminator,input:o,path:[t.discriminator],inst:e}),i)}}),qP=z(`$ZodTuple`,(e,t)=>{OP.init(e,t);let n=t.items,r=n.length-[...n].reverse().findIndex(e=>e._zod.optin!==`optional`);e._zod.parse=(i,a)=>{let o=i.value;if(!Array.isArray(o))return i.issues.push({input:o,inst:e,expected:`tuple`,code:`invalid_type`}),i;i.value=[];let s=[];if(!t.rest){let t=o.length>n.length,a=o.length=o.length&&c>=r)continue;let t=e._zod.run({value:o[c],issues:[]},a);t instanceof Promise?s.push(t.then(e=>JP(e,i,c))):JP(t,i,c)}if(t.rest){let e=o.slice(n.length);for(let n of e){c++;let e=t.rest._zod.run({value:n,issues:[]},a);e instanceof Promise?s.push(e.then(e=>JP(e,i,c))):JP(e,i,c)}}return s.length?Promise.all(s).then(()=>i):i}});function JP(e,t,n){e.issues.length&&t.issues.push(...tP(n,e.issues)),t.value[n]=e.value}const YP=z(`$ZodRecord`,(e,t)=>{OP.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!GN(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[];if(t.keyType._zod.values){let o=t.keyType._zod.values;n.value={};for(let e of o)if(typeof e==`string`||typeof e==`number`||typeof e==`symbol`){let o=t.valueType._zod.run({value:i[e],issues:[]},r);o instanceof Promise?a.push(o.then(t=>{t.issues.length&&n.issues.push(...tP(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...tP(e,o.issues)),n.value[e]=o.value)}let s;for(let e in i)o.has(e)||(s??=[],s.push(e));s&&s.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:s})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`)continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(s.issues.length){n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>rP(e,r,PN())),input:o,path:[o],inst:e}),n.value[s.value]=s.value;continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...tP(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...tP(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}}),XP=z(`$ZodLiteral`,(e,t)=>{if(OP.init(e,t),t.values.length===0)throw Error(`Cannot create literal schema with no valid values`);e._zod.values=new Set(t.values),e._zod.pattern=RegExp(`^(${t.values.map(e=>typeof e==`string`?qN(e):e?qN(e.toString()):String(e)).join(`|`)})$`),e._zod.parse=(n,r)=>{let i=n.value;return e._zod.values.has(i)||n.issues.push({code:`invalid_value`,values:t.values,input:i,inst:e}),n}});function ZP(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const QP=z(`$ZodOptional`,(e,t)=>{OP.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,BN(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),BN(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${RN(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(t=>ZP(t,e.value)):ZP(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}}),$P=z(`$ZodNullable`,(e,t)=>{OP.init(e,t),BN(e._zod,`optin`,()=>t.innerType._zod.optin),BN(e._zod,`optout`,()=>t.innerType._zod.optout),BN(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${RN(e.source)}|null)$`):void 0}),BN(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0),e._zod.parse=(e,n)=>e.value===null?e:t.innerType._zod.run(e,n)}),eF=z(`$ZodPipe`,(e,t)=>{OP.init(e,t),BN(e._zod,`values`,()=>t.in._zod.values),BN(e._zod,`optin`,()=>t.in._zod.optin),BN(e._zod,`optout`,()=>t.out._zod.optout),BN(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if(n.direction===`backward`){let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>tF(e,t.in,n)):tF(r,t.in,n)}let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>tF(e,t.out,n)):tF(r,t.out,n)}});function tF(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues},n)}const nF=z(`$ZodCodec`,(e,t)=>{OP.init(e,t),BN(e._zod,`values`,()=>t.in._zod.values),BN(e._zod,`optin`,()=>t.in._zod.optin),BN(e._zod,`optout`,()=>t.out._zod.optout),BN(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if((n.direction||`forward`)===`forward`){let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>rF(e,t,n)):rF(r,t,n)}else{let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>rF(e,t,n)):rF(r,t,n)}}});function rF(e,t,n){if(e.issues.length)return e.aborted=!0,e;if((n.direction||`forward`)===`forward`){let r=t.transform(e.value,e);return r instanceof Promise?r.then(r=>iF(e,r,t.out,n)):iF(e,r,t.out,n)}else{let r=t.reverseTransform(e.value,e);return r instanceof Promise?r.then(r=>iF(e,r,t.in,n)):iF(e,r,t.in,n)}}function iF(e,t,n,r){return e.issues.length?(e.aborted=!0,e):n._zod.run({value:t,issues:e.issues},r)}const aF=z(`$ZodReadonly`,(e,t)=>{OP.init(e,t),BN(e._zod,`propValues`,()=>t.innerType._zod.propValues),BN(e._zod,`values`,()=>t.innerType._zod.values),BN(e._zod,`optin`,()=>t.innerType._zod.optin),BN(e._zod,`optout`,()=>t.innerType._zod.optout),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(oF):oF(r)}});function oF(e){return e.value=Object.freeze(e.value),e}const sF=z(`$ZodTemplateLiteral`,(e,t)=>{OP.init(e,t);let n=[];for(let e of t.parts)if(typeof e==`object`&&e){if(!e._zod.pattern)throw Error(`Invalid template literal part, no pattern found: ${[...e._zod.traits].shift()}`);let t=e._zod.pattern instanceof RegExp?e._zod.pattern.source:e._zod.pattern;if(!t)throw Error(`Invalid template literal part: ${e._zod.traits}`);let r=t.startsWith(`^`)?1:0,i=t.endsWith(`$`)?t.length-1:t.length;n.push(t.slice(r,i))}else if(e===null||KN.has(typeof e))n.push(qN(`${e}`));else throw Error(`Invalid template literal part: ${e}`);e._zod.pattern=RegExp(`^${n.join(``)}$`),e._zod.parse=(n,r)=>typeof n.value==`string`?(e._zod.pattern.lastIndex=0,e._zod.pattern.test(n.value)||n.issues.push({input:n.value,inst:e,code:`invalid_format`,format:t.format??`template_literal`,pattern:e._zod.pattern.source}),n):(n.issues.push({input:n.value,inst:e,expected:`template_literal`,code:`invalid_type`}),n)});function cF(e,t){return new e({type:`string`,...YN(t)})}function lF(e,t){return new e({type:`number`,checks:[],...YN(t)})}function uF(e,t){return new e({type:`boolean`,...YN(t)})}function dF(e,t){return new e({type:`bigint`,...YN(t)})}function fF(e,t){return new e({type:`undefined`,...YN(t)})}function pF(e,t){return new e({type:`null`,...YN(t)})}function mF(e){return new e({type:`any`})}function hF(e){return new e({type:`unknown`})}function gF(e,t){return new e({type:`date`,...YN(t)})}function _F(e,t){return new CP({check:`greater_than`,...YN(t),value:e,inclusive:!0})}function vF(e,t){return new wP({check:`min_length`,...YN(t),minimum:e})}function yF(e,t){return new EP({check:`string_format`,format:`regex`,...YN(t),pattern:e})}const bF=z(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);OP.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>lP(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>dP(e,t,n),e.parseAsync=async(t,n)=>uP(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>fP(e,t,n),e.check=(...n)=>e.clone({...t,checks:[...t.checks??[],...n.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]}),e.clone=(t,n)=>JN(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e))}),xF=z(`ZodMiniString`,(e,t)=>{kP.init(e,t),bF.init(e,t)});function B(e){return cF(xF,e)}const SF=z(`ZodMiniNumber`,(e,t)=>{AP.init(e,t),bF.init(e,t)});function V(e){return lF(SF,e)}const CF=z(`ZodMiniBoolean`,(e,t)=>{jP.init(e,t),bF.init(e,t)});function wF(e){return uF(CF,e)}const TF=z(`ZodMiniBigInt`,(e,t)=>{MP.init(e,t),bF.init(e,t)});function EF(e){return dF(TF,e)}const DF=z(`ZodMiniUndefined`,(e,t)=>{NP.init(e,t),bF.init(e,t)});function OF(e){return fF(DF,e)}const kF=z(`ZodMiniNull`,(e,t)=>{PP.init(e,t),bF.init(e,t)});function AF(e){return pF(kF,e)}const jF=z(`ZodMiniAny`,(e,t)=>{FP.init(e,t),bF.init(e,t)});function MF(){return mF(jF)}const NF=z(`ZodMiniUnknown`,(e,t)=>{IP.init(e,t),bF.init(e,t)});function PF(){return hF(NF)}const FF=z(`ZodMiniDate`,(e,t)=>{LP.init(e,t),bF.init(e,t)});function IF(e){return gF(FF,e)}const LF=z(`ZodMiniArray`,(e,t)=>{zP.init(e,t),bF.init(e,t)});function H(e,t){return new LF({type:`array`,element:e,...YN(t)})}const RF=z(`ZodMiniObject`,(e,t)=>{UP.init(e,t),bF.init(e,t),BN(e,`shape`,()=>t.shape)});function U(e,t){let n={type:`object`,shape:e??{},...YN(t)};return new RF(n)}function zF(e,t){return ZN(e,t)}function BF(e,t){return QN(e,t)}function VF(e,t){return $N(YF,e,t)}const HF=z(`ZodMiniUnion`,(e,t)=>{GP.init(e,t),bF.init(e,t)});function W(e,t){return new HF({type:`union`,options:e,...YN(t)})}const UF=z(`ZodMiniDiscriminatedUnion`,(e,t)=>{KP.init(e,t),bF.init(e,t)});function WF(e,t,n){return new UF({type:`union`,options:t,discriminator:e,...YN(n)})}const GF=z(`ZodMiniTuple`,(e,t)=>{qP.init(e,t),bF.init(e,t)});function G(e,t,n){let r=t instanceof OP;return new GF({type:`tuple`,items:e,rest:r?t:null,...YN(r?n:t)})}const KF=z(`ZodMiniRecord`,(e,t)=>{YP.init(e,t),bF.init(e,t)});function qF(e,t,n){return new KF({type:`record`,keyType:e,valueType:t,...YN(n)})}const JF=z(`ZodMiniLiteral`,(e,t)=>{XP.init(e,t),bF.init(e,t)});function K(e,t){return new JF({type:`literal`,values:Array.isArray(e)?e:[e],...YN(t)})}const YF=z(`ZodMiniOptional`,(e,t)=>{QP.init(e,t),bF.init(e,t)});function q(e){return new YF({type:`optional`,innerType:e})}const XF=z(`ZodMiniNullable`,(e,t)=>{$P.init(e,t),bF.init(e,t)});function ZF(e){return new XF({type:`nullable`,innerType:e})}function QF(e){return q(ZF(e))}const $F=z(`ZodMiniPipe`,(e,t)=>{eF.init(e,t),bF.init(e,t)}),eI=z(`ZodMiniCodec`,(e,t)=>{$F.init(e,t),nF.init(e,t)});function tI(e,t,n){return new eI({type:`pipe`,in:e,out:t,transform:n.decode,reverseTransform:n.encode})}const nI=z(`ZodMiniReadonly`,(e,t)=>{aF.init(e,t),bF.init(e,t)});function J(e){return new nI({type:`readonly`,innerType:e})}const rI=z(`ZodMiniTemplateLiteral`,(e,t)=>{sF.init(e,t),bF.init(e,t)});function iI(e,t){return new rI({type:`template_literal`,parts:e,...YN(t)})}Lf(),Jd(),op(),pf();function aI(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{for(let n of e){let{name:e,type:r}=n,o=t[e],s=r.match(fg);if(s&&(typeof o==`number`||typeof o==`bigint`)){let[,e,t]=s;I(o,{signed:e===`int`,size:Number.parseInt(t??``,10)/8})}if(r===`address`&&typeof o==`string`&&!og(o))throw new sg({address:o,cause:new cg});let c=r.match(dg);if(c){let[,e]=c;if(e&&qf(o)!==Number.parseInt(e,10))throw new fI({expectedSize:Number.parseInt(e,10),givenSize:qf(o)})}let l=i[r];l&&(bI(r),a(l,o))}};if(i.EIP712Domain&&t){if(typeof t!=`object`)throw new pI({domain:t});a(i.EIP712Domain,t)}if(r!==`EIP712Domain`)if(i[r])a(i[r],n);else throw new mI({primaryType:r,types:i})}function oI(e){let{domain:t={},message:n,primaryType:r}=e,i={EIP712Domain:cI(t),...e.types};aI({domain:t,message:n,primaryType:r,types:i});let a=[`0x19`,`0x01`];return t&&a.push(uI({domain:t,types:i})),r!==`EIP712Domain`&&a.push(dI({data:n,primaryType:r,types:i})),zf(...a)}function sI(e){let{primaryType:t,types:n}=e,r=``,i=yI({primaryType:t,types:n});i.delete(t);let a=[t,...Array.from(i).sort()];for(let e of a)r+=`${e}(${(n[e]??[]).map(({name:e,type:t})=>`${t} ${e}`).join(`,`)})`;return r}function cI(e){return[typeof e?.name==`string`&&{name:`name`,type:`string`},e?.version&&{name:`version`,type:`string`},(typeof e?.chainId==`number`||typeof e?.chainId==`bigint`)&&{name:`chainId`,type:`uint256`},e?.verifyingContract&&{name:`verifyingContract`,type:`address`},e?.salt&&{name:`salt`,type:`bytes32`}].filter(Boolean)}function lI(e){return Hh(oI(e))}function uI(e){let{domain:t,types:n}=e;return dI({data:t,primaryType:`EIP712Domain`,types:{...n,EIP712Domain:n?.EIP712Domain||cI(t)}})}function dI(e){let{data:t,primaryType:n,types:r}=e,i=gI({data:t,primaryType:n,types:r});return Hh(i)}var fI=class extends P{constructor({expectedSize:e,givenSize:t}){super(`Expected bytes${e}, got bytes${t}.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.BytesSizeMismatchError`})}},pI=class extends P{constructor({domain:e}){super(`Invalid domain "${df(e)}".`,{metaMessages:[`Must be a valid EIP-712 domain.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidDomainError`})}},mI=class extends P{constructor({primaryType:e,types:t}){super(`Invalid primary type \`${e}\` must be one of \`${JSON.stringify(Object.keys(t))}\`.`,{metaMessages:["Check that the primary type is a key in `types`."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidPrimaryTypeError`})}},hI=class extends P{constructor({type:e}){super(`Struct type "${e}" is invalid.`,{metaMessages:[`Struct type must not be a Solidity type.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidStructTypeError`})}};function gI(e){let{data:t,primaryType:n,types:r}=e,i=[{type:`bytes32`}],a=[_I({primaryType:n,types:r})];for(let e of r[n]??[]){let[n,o]=vI({types:r,name:e.name,type:e.type,value:t[e.name]});i.push(n),a.push(o)}return Hg(i,a)}function _I(e){let{primaryType:t,types:n}=e,r=Uf(sI({primaryType:t,types:n}));return Hh(r)}function vI(e){let{types:t,name:n,type:r,value:i}=e;if(t[r]!==void 0)return[{type:`bytes32`},Hh(gI({data:i,primaryType:r,types:t}))];if(r===`bytes`)return i=`0x${(i.length%2?`0`:``)+i.slice(2)}`,[{type:`bytes32`},Hh(i,{as:`Hex`})];if(r===`string`)return[{type:`bytes32`},Hh(vf(i),{as:`Hex`})];if(r.lastIndexOf(`]`)===r.length-1){let e=r.slice(0,r.lastIndexOf(`[`)),a=i.map(r=>vI({name:n,type:e,types:t,value:r}));return[{type:`bytes32`},Hh(Hg(a.map(([e])=>e),a.map(([,e])=>e)))]}return[{type:r},i]}function yI(e,t=new Set){let{primaryType:n,types:r}=e,i=n.match(/^\w*/u)?.[0];if(t.has(i)||r[i]===void 0)return t;t.add(i);for(let e of r[i])yI({primaryType:e.type,types:r},t);return t}function bI(e){if(e===`address`||e===`bool`||e===`string`||e.startsWith(`bytes`)||e.startsWith(`uint`)||e.startsWith(`int`))throw new hI({type:e})}pi(),Ci();function xI(e){if(typeof e==`string`){if(!bi(e,{strict:!1}))throw new fi({address:e});return{address:e,type:`json-rpc`}}if(!bi(e.address,{strict:!1}))throw new fi({address:e.address});return{address:e.address,nonceManager:e.nonceManager,sign:e.sign,signAuthorization:e.signAuthorization,signMessage:e.signMessage,signTransaction:e.signTransaction,signTypedData:e.signTypedData,source:`custom`,type:`local`}}Is(),Sl(),Hc();var SI=Ac(BigInt(`0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff`)),CI=SI.create(BigInt(`-3`)),wI=BigInt(`0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`);const TI=xl({a:CI,b:wI,Fp:SI,n:BigInt(`0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551`),Gx:BigInt(`0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296`),Gy:BigInt(`0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5`),h:BigInt(1),lowS:!1},Ns);var EI=Ac(BigInt(`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff`)),DI=EI.create(BigInt(`-3`)),OI=BigInt(`0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef`);xl({a:DI,b:OI,Fp:EI,n:BigInt(`0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973`),Gx:BigInt(`0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7`),Gy:BigInt(`0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f`),h:BigInt(1),lowS:!1},Fs);var kI=Ac(BigInt(`0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`)),AI=kI.create(BigInt(`-3`)),jI=BigInt(`0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00`);xl({a:AI,b:jI,Fp:kI,n:BigInt(`0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409`),Gx:BigInt(`0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66`),Gy:BigInt(`0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650`),h:BigInt(1),lowS:!1,allowedPrivateKeyLengths:[130,131,132]},Ps);const MI=TI,NI=TI;Lf(),op();function PI(e){let{privateKey:t}=e,n=NI.ProjectivePoint.fromPrivateKey(typeof t==`string`?t.slice(2):Hf(t).slice(2));return Gh(n)}function FI(e={}){let{as:t=`Hex`}=e,n=NI.utils.randomPrivateKey();return t===`Hex`?Hf(n):n}function II(e){let{extraEntropy:t=!1,hash:n,payload:r,privateKey:i}=e,{r:a,s:o,recovery:s}=NI.sign(r instanceof Uint8Array?r:_f(r),i instanceof Uint8Array?i:_f(i),{extraEntropy:typeof t==`boolean`?t:Bf(t).slice(2),lowS:!0,...n?{prehash:!0}:{}});return{r:a,s:o,yParity:s}}Jd();function LI(e,t=0){if(!/^(-?)([0-9]*)\.?([0-9]*)$/.test(e))throw new RI({value:e});let[n=``,r=`0`]=e.split(`.`),i=n.startsWith(`-`);if(i&&(n=n.slice(1)),r=r.replace(/(0+)$/,``),t===0)Math.round(Number(`.${r}`))===1&&(n=`${BigInt(n)+1n}`),r=``;else if(r.length>t){let[e,i,a]=[r.slice(0,t-1),r.slice(t-1,t),r.slice(t)],o=Math.round(Number(`${i}.${a}`));r=o>9?`${BigInt(e)+BigInt(1)}0`.padStart(e.length+1,`0`):`${e}${o}`,r.length>t&&(r=r.slice(1),n=`${BigInt(n)+1n}`),r=r.slice(0,t)}else r=r.padEnd(t,`0`);return BigInt(`${i?`-`:``}${n}${r}`)}var RI=class extends P{constructor({value:e}){super(`Value \`${e}\` is not a valid decimal number.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Value.InvalidDecimalNumberError`})}};Lf();var zI=new TextEncoder,BI=new TextDecoder,VI=Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[t,e.charCodeAt(0)])),HI={...Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[e.charCodeAt(0),t])),61:0,45:62,95:63};function UI(e,t={}){let{pad:n=!0,url:r=!1}=t,i=new Uint8Array(Math.ceil(e.length/3)*4);for(let t=0,n=0;n>18],i[t+1]=VI[r>>12&63],i[t+2]=VI[r>>6&63],i[t+3]=VI[r&63]}let a=e.length%3,o=Math.floor(e.length/3)*4+(a&&a+1),s=BI.decode(new Uint8Array(i.buffer,0,o));return n&&a===1&&(s+=`==`),n&&a===2&&(s+=`=`),r&&(s=s.replaceAll(`+`,`-`).replaceAll(`/`,`_`)),s}function WI(e,t={}){return UI(_f(e),t)}function GI(e){let t=e.replace(/=+$/,``),n=t.length,r=new Uint8Array(n+3);zI.encodeInto(t+`===`,r);for(let e=0,n=0;e>16,r[n+1]=t>>8&255,r[n+2]=t&255}let i=(n>>2)*3+(n%4&&n%4-1);return new Uint8Array(r.buffer,0,i)}op();function KI(e){let t=e[4]===0?5:4,n=t+32,r=e[n+2]===0?n+3:n+2,i=BigInt(Hf(e.slice(t,n))),a=BigInt(Hf(e.slice(r)));return{r:i,s:a>MI.CURVE.n/2n?MI.CURVE.n-a:a}}async function qI(e){try{let t=e.getPublicKey();if(!t)throw new nL;let n=new Uint8Array(t),r=await crypto.subtle.importKey(`spki`,new Uint8Array(n),{name:`ECDSA`,namedCurve:`P-256`,hash:`SHA-256`},!0,[`verify`]),i=new Uint8Array(await crypto.subtle.exportKey(`raw`,r));return Gh(i)}catch(t){if(t.message!==`Permission denied to access object`)throw t;let n=new Uint8Array(e.attestationObject),r=e=>{let t=new Uint8Array([e,88,32]);for(let e=0;en[e+r]===t))return e+t.length;throw new nL},i=r(33),a=r(34);return Gh(new Uint8Array([4,...n.slice(i,i+32),...n.slice(a,a+32)]))}}Lf(),Jd(),op();const JI=Uint8Array.from([105,171,180,181,160,222,75,198,42,42,32,31,141,37,186,233]);async function YI(e){let{createFn:t=window.navigator.credentials.create.bind(window.navigator.credentials),...n}=e,r=QI(n);try{let e=await t(r);if(!e)throw new nL;let n=e.response,i=await qI(n);return{id:e.id,publicKey:i,raw:e}}catch(e){throw new nL({cause:e})}}function XI(e={}){let{flag:t=5,rpId:n=window.location.hostname,signCount:r=0}=e,i=Uh(Uf(n)),a=I(t,{size:1}),o=I(r,{size:4});return zf(i,a,o)}function ZI(e){let{challenge:t,crossOrigin:n=!1,extraClientData:r,origin:i=window.location.origin}=e;return JSON.stringify({type:`webauthn.get`,challenge:WI(t,{url:!0,pad:!1}),origin:i,crossOrigin:n,...r})}function QI(e){let{attestation:t=`none`,authenticatorSelection:n={residentKey:`preferred`,requireResidentKey:!1,userVerification:`required`},challenge:r=JI,excludeCredentialIds:i,extensions:a,name:o,rp:s={id:window.location.hostname,name:window.document.title},user:c}=e,l=c?.name??o;return{publicKey:{attestation:t,authenticatorSelection:n,challenge:r,...i?{excludeCredentials:i?.map(e=>({id:GI(e),type:`public-key`}))}:{},pubKeyCredParams:[{type:`public-key`,alg:-7}],...a&&{extensions:a},rp:s,user:{id:c?.id??Hh(vf(l),{as:`Bytes`}),name:l,displayName:c?.displayName??l}}}}function $I(e){let{credentialId:t,challenge:n,extensions:r,rpId:i=window.location.hostname,userVerification:a=`required`}=e;return{publicKey:{...t?{allowCredentials:Array.isArray(t)?t.map(e=>({id:GI(e),type:`public-key`})):[{id:GI(t),type:`public-key`}]}:{},challenge:_f(n),...r&&{extensions:r},rpId:i,userVerification:a}}}function eL(e){let{challenge:t,crossOrigin:n,extraClientData:r,flag:i,origin:a,rpId:o,signCount:s,userVerification:c=`required`}=e,l=XI({flag:i,rpId:o,signCount:s}),u=ZI({challenge:t,crossOrigin:n,extraClientData:r,origin:a}),d=Uh(Uf(u)),f=u.indexOf(`"challenge"`),p=u.indexOf(`"type"`),m={authenticatorData:l,clientDataJSON:u,challengeIndex:f,typeIndex:p,userVerificationRequired:c===`required`},h=zf(l,d);return{metadata:m,payload:h}}async function tL(e){let{getFn:t=window.navigator.credentials.get.bind(window.navigator.credentials),...n}=e,r=$I(n);try{let e=await t(r);if(!e)throw new rL;let n=e.response,i=String.fromCharCode(...new Uint8Array(n.clientDataJSON)),a=i.indexOf(`"challenge"`),o=i.indexOf(`"type"`),s=KI(new Uint8Array(n.signature));return{metadata:{authenticatorData:Hf(new Uint8Array(n.authenticatorData)),clientDataJSON:i,challengeIndex:a,typeIndex:o,userVerificationRequired:r.publicKey.userVerification===`required`},signature:s,raw:e}}catch(e){throw new rL({cause:e})}}var nL=class extends P{constructor({cause:e}={}){super(`Failed to create credential.`,{cause:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`WebAuthnP256.CredentialCreationFailedError`})}},rL=class extends P{constructor({cause:e}={}){super(`Failed to request credential.`,{cause:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`WebAuthnP256.CredentialRequestFailedError`})}};Lf();async function iL(e={}){let{extractable:t=!1}=e,n=await globalThis.crypto.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},t,[`sign`,`verify`]),r=await globalThis.crypto.subtle.exportKey(`raw`,n.publicKey),i=Gh(new Uint8Array(r));return{privateKey:n.privateKey,publicKey:i}}async function aL(e){let{payload:t,privateKey:n}=e,r=await globalThis.crypto.subtle.sign({name:`ECDSA`,hash:`SHA-256`},n,hf(t)),i=gf(new Uint8Array(r)),a=Sf(xf(i,0,32)),o=Sf(xf(i,32,64));return o>MI.CURVE.n/2n&&(o=MI.CURVE.n-o),{r:a,s:o}}const oL=`0x32323232`;Lf(),op(),pf();const sL={p256:`p256`,secp256k1:`secp256k1`,webauthnp256:`webauthn-p256`},cL={admin:`admin`,normal:`session`},lL={0:`minute`,1:`hour`,2:`day`,3:`week`,4:`month`,5:`year`},uL={address:`secp256k1`,p256:`p256`,secp256k1:`secp256k1`,"webauthn-p256":`webauthnp256`},dL={admin:`admin`,session:`normal`},fL={address:2,p256:0,secp256k1:2,"webauthn-p256":1},pL={day:2,hour:1,minute:0,month:4,week:3,year:5};function mL(e={}){let t=FI();return yL({...e,privateKey:t})}async function hL(e){let{createFn:t,label:n,rpId:r,userId:i}=e,a=await YI({authenticatorSelection:{requireResidentKey:!0,residentKey:`required`,userVerification:`required`},createFn:t,extensions:{credProps:!0},rp:r?{id:r,name:r}:void 0,user:{displayName:n,id:new Uint8Array(i??vf(n)),name:n}});return xL({...e,credential:{id:a.id,publicKey:a.publicKey},id:i?wf(i):Jh(a.publicKey,{includePrefix:!1})})}function gL(e={}){let t=FI();return SL({...e,privateKey:t})}async function _L(e={}){let t=await iL();return CL({...e,keyPair:t})}function vL(e,t={}){let{chainId:n=e.chainId}=t,{expiry:r=0,id:i,prehash:a=!1,role:o=`admin`,type:s}=e,c=(()=>{let t=e.publicKey;return t===`0x`?t:s===`secp256k1`||s===`address`?qf(t)===20||Yf(Kf(t,0,12))===0n?Kf(t,-20):ig(qh(t)):t})();return{...e,chainId:n,expiry:r,hash:wL({publicKey:c,type:s}),id:(i??c).toLowerCase(),prehash:a,publicKey:c.toLowerCase(),role:o,type:s}}function yL(e){let{chainId:t,expiry:n,feeToken:r,permissions:i,privateKey:a,role:o}=e,s=Jh(PI({privateKey:a}),{includePrefix:!1});return vL({chainId:t,expiry:n,feeToken:r,permissions:i,privateKey(){return a},publicKey:s,role:o,type:`p256`})}function bL(e,t){let{chainId:n}=t,{publicKey:r}=e,i=qf(r)===20||Yf(Kf(r,0,12))===0n,a={};for(let t of e.permissions)t.type===`call`&&(a.calls??=[],a.calls.push({signature:t.selector,to:t.to===`0x3232323232323232323232323232323232323232`?void 0:t.to})),t.type===`spend`&&(a.spend??=[],a.spend.push({limit:t.limit,period:t.period,token:t.token}));return vL({chainId:n,expiry:e.expiry,permissions:a,publicKey:e.publicKey,role:cL[e.role],type:i?`address`:sL[e.type]})}function xL(e){let{credential:t,id:n,rpId:r}=e,i=Jh(t.publicKey,{includePrefix:!1});return vL({chainId:e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,id:n,permissions:e.permissions,privateKey:{credential:t,rpId:r},publicKey:i,role:e.role,type:`webauthn-p256`})}function SL(e){let{privateKey:t}=e,n=Jh(PI({privateKey:t}),{includePrefix:!1});return vL({chainId:e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,permissions:e.permissions,privateKey:{privateKey(){return t}},publicKey:n,role:e.role,type:`webauthn-p256`})}function CL(e){let{chainId:t,expiry:n,feeToken:r,keyPair:i,permissions:a,role:o}=e,{privateKey:s}=i,c=Jh(i.publicKey,{includePrefix:!1});return vL({chainId:t,expiry:n,feeToken:r,permissions:a,prehash:!0,privateKey:s,publicKey:c,role:o,type:`p256`})}function wL(e){let{type:t}=e,n=TL(e.publicKey);return Hh(Hg([{type:`uint8`},{type:`bytes32`}],[fL[t],Hh(n)]))}function TL(e){return qf(e)<32?Wf(e,32):e}async function EL(e,t){let{address:n,storage:r,webAuthn:i,wrap:a=!0}=t,{privateKey:o,publicKey:s,type:c}=e;if(!o)throw Error(`Key does not have a private key to sign with. - -Key: -`+df(e,null,2));let l=(()=>n?lI({domain:{verifyingContract:n},message:{digest:t.payload},primaryType:`ERC1271Sign`,types:{ERC1271Sign:[{name:`digest`,type:`bytes32`}]}}):t.payload)(),[u,d]=await(async()=>{if(c===`p256`){let{privateKey:t}=e;if(typeof t==`function`)return[d_(II({payload:l,privateKey:t()})),!1];if(t instanceof CryptoKey)return[d_(await aL({payload:l,privateKey:t})),!0]}if(c===`secp256k1`)return[d_(A_({payload:l,privateKey:o()})),!1];if(c===`webauthn-p256`){if(o.privateKey){let{payload:e,metadata:t}=eL({challenge:l,origin:`https://ithaca.xyz`,rpId:`ithaca.xyz`}),{r:n,s:r}=II({hash:!0,payload:e,privateKey:o.privateKey()});return[AL({metadata:t,signature:{r:n,s:r}}),!1]}let{credential:t,rpId:n}=o,a=`porto.webauthnVerified.${e.hash}`,s=Date.now(),c=!0;if(r){let e=await r.getItem(a);c=!e||s-e>6e5}let{signature:{r:u,s:d},raw:f,metadata:p}=await tL({challenge:l,credentialId:t.id,getFn:i?.getFn,rpId:n,userVerification:c?`required`:`preferred`}),m=f.response;if(!m?.userHandle)throw Error(`No user handle in response`,{cause:{response:m}});let h=wf(new Uint8Array(m.userHandle));if(e.id&&og(e.id)&&!ag(e.id,h))throw Error(`supplied webauthn key "${e.id}" does not match signature webauthn key "${h}"`,{cause:{id:h,key:e}});return c&&r&&await r.setItem(a,s),[AL({metadata:p,signature:{r:u,s:d}}),!1]}throw Error(`Key type "${c}" is not supported.\n\nKey:\n`+df(e,null,2))})();return a?jL(u,{keyType:c,prehash:d,publicKey:s}):u}function DL(e,t={}){let{expiry:n=0,prehash:r=!1,publicKey:i,role:a=`admin`,type:o}=e,{feeTokens:s,orchestrator:c}=t,l=Object.entries(OL(e,{feeTokens:s})).map(([e,t])=>{if(e===`calls`)return t.map(({signature:e,to:t})=>({selector:(()=>e?Zf(e)?e:Z_(e):oL)(),to:t??`0x3232323232323232323232323232323232323232`,type:`call`}));if(e!==`feeToken`){if(e===`spend`)return t.map(({limit:e,period:t,token:n})=>({limit:e,period:t,token:n,type:`spend`}));throw Error(`Invalid permission type "${e}".`)}}).flat().filter(Boolean);return e.role===`session`&&c&&l.push({selector:oL,to:c,type:`call`}),{expiry:n,permissions:l??[],prehash:r,publicKey:TL(i),role:dL[a],type:uL[o]}}function OL(e,t){let{permissions:n}=e,r=n?.calls?[...n.calls]:[],i=n?.spend?[...n.spend]:[],a=t.feeTokens?.filter(e=>e.feeToken);if(a&&a.length>0){let t=kL(e,{feeTokens:a});if(t){let e=-1,n=pL.year;for(let r=0;re.feeToken.symbol===t.symbol?!0:!e.feeToken.symbol||e.feeToken.symbol===`native`?t.address===Q_:!1);if(!r)return;let i=LI(e.feeToken.limit,r.decimals);return{...r,value:i}}function AL(e){let{metadata:t,signature:n}=e;return Hg(Wg([`struct WebAuthnAuth { bytes authenticatorData; string clientDataJSON; uint256 challengeIndex; uint256 typeIndex; bytes32 r; bytes32 s; }`,`WebAuthnAuth auth`]),[{authenticatorData:t.authenticatorData,challengeIndex:BigInt(t.challengeIndex),clientDataJSON:t.clientDataJSON,r:I(n.r,{size:32}),s:I(n.s,{size:32}),typeIndex:BigInt(t.typeIndex)}])}function jL(e,t){let{keyType:n,prehash:r=!1,publicKey:i}=t,a=wL({publicKey:i,type:n});return Ug([`bytes`,`bytes32`,`bool`],[e,a,r])}function ML(e){let t=typeof e==`string`?{address:e}:e,n=t.sign?`privateKey`:`porto`,{address:r,sign:i,signMessage:a,signTransaction:o,signTypedData:s,type:c}=xI({address:t.address,sign({hash:e}){if(n===`porto`)throw Error("`sign` not supported on porto accounts.");if(!t.sign)throw Error("`sign` not supported.");return t.sign({hash:e})},signMessage({message:e}){return this.sign({hash:Th(e)})},signTransaction(){throw Error("`signTransaction` not supported on porto accounts.")},signTypedData(e){return this.sign({hash:Nh(e)})}});return{address:r,keys:t.keys??void 0,sign:i,signMessage:a,signTransaction:o,signTypedData:s,source:n,type:c}}function NL(e,t={}){let{keys:n}=t,r=ig(E_({privateKey:e}));return ML({address:r,keys:n,async sign({hash:t}){return d_(A_({payload:t,privateKey:e}))},source:`privateKey`})}function PL(e,t={}){let{key:n,role:r}=t;if(n!==null){if(typeof n==`object`)return n;if(e.keys&&e.keys.length>0)return typeof n==`number`?e.keys[n]:e.keys.find(e=>e.privateKey&&(!r||e.role===r))}}async function FL(e,t){let{storage:n,replaySafe:r=!0,wrap:i=!0,webAuthn:a}=t,o=PL(e,t),s=(()=>r?lI({domain:{verifyingContract:e.address},message:{digest:t.payload},primaryType:`ERC1271Sign`,types:{ERC1271Sign:[{name:`digest`,type:`bytes32`}]}}):t.payload)(),c=(()=>o?({hash:e})=>EL(o,{address:null,payload:e,storage:n,webAuthn:a,wrap:i}):e.source===`privateKey`?e.sign:void 0)();if(!c)throw Error(`cannot find key to sign with.`);return await c({hash:s})}op();function IL(e,t={}){return H_(e,t)}function LL(e,t,n){if(t===`Error`)return zL;if(t===`Panic`)return BL;if(Zf(t,{strict:!1})){let e=Kf(t,0,4);if(e===`0x08c379a0`)return zL;if(e===`0x4e487b71`)return BL}let r=U_(e,t,n);if(r.type!==`error`)throw new J_({name:t,type:`error`});return r}function RL(e){return W_(e)}const zL=IL({inputs:[{name:`message`,type:`string`}],name:`Error`,type:`error`}),BL=IL({inputs:[{name:`reason`,type:`uint8`}],name:`Panic`,type:`error`});O();var VL=class extends D{constructor(){super(`Function is not recognized.`,{metaMessages:[`This could be due to any of the following:`,` - The contract does not have the function,`,` - The address is not a contract.`],name:`FunctionSelectorNotRecognizedError`})}};za();function HL(e,t){let n=e.walk(e=>`data`in e);if(!n?.data)return e;if(n.data===RL(IL(`error FnSelectorNotRecognized()`)))return new VL;let r=null;for(let e of t.calls){let t=e;if(t.abi)try{if(!Ra({abi:t.abi,data:n.data}))continue;r=t}catch{}}return r?cs(n,{abi:r.abi,address:r.to,args:r.args,functionName:r.functionName}):e}Jd(),op();const Y=()=>iI([`0x`,B()],{message:`Needs string in format ^0x[A-Fa-f0-9]{40}$.`}),X=()=>iI([`0x`,B()],{message:`Needs string in format ^0x[A-Fa-f0-9]+$.`}),Z=()=>tI(X(),V(),{decode:e=>Xf(e),encode:e=>I(e)}),Q=()=>tI(X(),EF({message:`Required bigint`}),{decode:e=>Yf(e),encode:e=>I(e)});function UL(e){return W(e)}var WL=class extends P{constructor(){super(...arguments),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Schema.ValidationError`})}};function GL(e){let t=e,n=`Validation failed with ${t.issues.length} error${t.issues.length===1?``:`s`}:`;n+=` + `,d.appendChild(m),d.appendChild(f);let h=UB({from:HB(window,{targetOrigin:u.origin}),to:HB(f.contentWindow,{targetOrigin:u.origin}),waitForReady:!0});o?._setup(h,!0);let g=window.matchMedia(`(max-width: 460px)`),_=()=>{h.send(`__internal`,{type:`resize`,width:g.matches?460:461})};g.addEventListener(`change`,_),h.on(`ready`,t=>{let n=e.internal.store.getState().chainIds.filter(e=>t.chainIds.includes(e));n.length===0&&(n=t.chainIds),s.setState(e=>({...e,chainIds:n})),h.send(`__internal`,{chainIds:n,mode:`iframe`,referrer:ZB(),theme:a,type:`init`}),_()}),h.on(`rpc-response`,e=>{r([e._request])&&(f.src=f.src),$B(s,e)}),h.on(`account`,e=>{eV(s,e)}),h.on(`__internal`,e=>{e.type===`switch`&&e.mode===`popup`&&(c.open(),c.syncRequests(s.getState().requestQueue))});let v=null,y=null,b=()=>QB(s),x=e=>{e.key===`Escape`&&QB(s)},S=new MutationObserver(e=>{for(let t of e){if(t.type!==`attributes`)continue;let e=t.attributeName;e&&e===`inert`&&d.removeAttribute(e)}});S.observe(d,{attributeOldValue:!0,attributes:!0});let ee=!1,C=()=>{ee&&(ee=!1,d.removeEventListener(`click`,b),document.removeEventListener(`keydown`,x),d.style.pointerEvents=`none`,y?.focus(),y=null,Object.assign(document.body.style,v??``),document.body.style.overflow=v?.overflow??``)},te=()=>{ee||(ee=!0,d.addEventListener(`click`,b),document.addEventListener(`keydown`,x),f.focus(),d.style.pointerEvents=`auto`,v=Object.assign({},document.body.style),document.body.style.overflow=`hidden`)},ne=!1,re=()=>{ne||(ne=!0,document.activeElement instanceof HTMLElement&&(y=document.activeElement),d.removeAttribute(`hidden`),d.removeAttribute(`aria-closed`),d.showModal())},ie=()=>{if(ne){ne=!1,d.setAttribute(`hidden`,`true`),d.setAttribute(`aria-closed`,`true`),d.close();for(let e of d.parentNode?Array.from(d.parentNode.children):[])e!==d&&e.hasAttribute(`inert`)&&e.removeAttribute(`inert`)}};return{close(){c.close(),l=!1,h.send(`__internal`,{mode:`iframe`,referrer:ZB(),type:`init`}),ie(),C()},destroy(){c.close(),l=!1,C(),ie(),c.destroy(),h.destroy(),d.remove(),S.disconnect(),g.removeEventListener(`change`,_)},open(){l||(l=!0,re(),te(),h.send(`__internal`,{mode:`iframe`,referrer:ZB(),type:`init`}))},async secure(){let{trustedHosts:e}=await h.waitForReady(),n=(()=>{if(t)return!0;let e=window.location.protocol.startsWith(`https`);return e||jB.warnOnce(`Detected insecure protocol (HTTP).`,`\n\nThe Porto iframe is not supported on HTTP origins (${window.location.origin})`,`due to lack of WebAuthn support.`,`See https://porto.sh/sdk#secure-origins-https for more information.`),e})(),r=kB(),i=!!e?.includes(window.location.hostname),a=!!(r||i);return a||jB.warnOnce([`Warning: Browser does not support IntersectionObserver v2 or host "${u.hostname}" is not trusted by Porto.`,`This may result in the dialog falling back to a popup.`,``,`Add "${u.hostname}" to the trusted hosts list to enable iframe dialog: https://github.com/ithacaxyz/porto/edit/main/src/trusted-hosts.ts`].join(` +`)),{frame:a,host:i,protocol:n}},async syncRequests(e){let{methodPolicies:t}=await h.waitForReady(),n=await this.secure(),i=e?.every(e=>t?.find(t=>t.method===e.request.method)?.modes?.headless===!0),a=r(e.map(e=>e.request));if(!i&&(a||!n.protocol||!n.frame))c.syncRequests(e);else{let n=e.some(e=>XB(e.request,{methodPolicies:t,targetOrigin:u.origin}));!l&&n&&this.open(),h.send(`rpc-requests`,e)}}}},supportsHeadless:!0})}function qB(e={}){if(typeof window>`u`)return JB();let{type:t=`auto`,size:n=YB}=e;return GB({name:`popup`,setup(e){let{host:r,internal:i,themeController:a}=e,{store:o}=i,s=new URL(r),c=null,l=t===`page`||t===`auto`&&FB()?`page`:`popup`;function u(){c&&QB(o)}let d=(()=>{let e=setInterval(()=>{c?.closed&&QB(o)},100);return()=>clearInterval(e)})(),f;return a?._setup(null,!0),{close(){c&&=(c.close(),null)},destroy(){this.close(),window.removeEventListener(`focus`,u),f?.destroy(),d()},open(){if(l===`popup`){let e=(window.innerWidth-n.width)/2+window.screenX,t=window.screenY+100;c=window.open(tV(r),`_blank`,`width=${n.width},height=${n.height},left=${e},top=${t}`)}else c=window.open(tV(r),`_blank`);if(!c)throw Error(`Failed to open popup`);f=UB({from:HB(window,{targetOrigin:s.origin}),to:HB(c,{targetOrigin:s.origin}),waitForReady:!0}),a?._setup(f,!1),f.send(`__internal`,{mode:l===`page`?`page`:`popup`,referrer:ZB(),theme:a?.getTheme()??e.theme,type:`init`}),f.on(`rpc-response`,e=>$B(o,e)),f.on(`account`,e=>{eV(o,e)}),window.removeEventListener(`focus`,u),window.addEventListener(`focus`,u)},async secure(){return{frame:!0,host:!0,protocol:!0}},async syncRequests(e){e.some(e=>XB(e.request))&&((!c||c.closed)&&this.open(),c?.focus()),f?.send(`rpc-requests`,e)}}},supportsHeadless:!1})}function JB(){return GB({name:`noop`,setup(){return{close(){},destroy(){},open(){},async secure(){return{frame:!0,host:!0,protocol:!0}},async syncRequests(){}}},supportsHeadless:!0})}var YB={height:282,width:360};function XB(e,t={}){let{methodPolicies:n,targetOrigin:r}=t,i=n?.find(t=>t.method===e.method);return i&&i.modes?.headless?!!(typeof i.modes.headless==`object`&&i.modes.headless.sameOrigin&&r!==window.location.origin):!0}function ZB(){return{icon:(()=>{let e=document.querySelector(`link[rel="icon"][media="(prefers-color-scheme: dark)"]`)?.href,t=document.querySelector(`link[rel="icon"][media="(prefers-color-scheme: light)"]`)?.href??document.querySelector(`link[rel="icon"]`)?.href;return e&&t&&e!==t?{dark:e,light:t}:window.matchMedia(`(prefers-color-scheme: dark)`).matches?e:t})(),title:document.title}}function QB(e){e.setState(e=>({...e,requestQueue:e.requestQueue.map(e=>({account:e.account,error:new vI,request:e.request,status:`error`}))}))}function $B(e,t){e.setState(e=>({...e,requestQueue:e.requestQueue.map(e=>e.request.id===t.id?t.error?{account:e.account,error:t.error,request:e.request,status:`error`}:{account:e.account,request:e.request,result:t.result,status:`success`}:e)}))}function eV(e,t){let{account:n}=t;e.setState(e=>({...e,accounts:[TB({address:n.address,keys:n.capabilities?.admins?.map(e=>dB(e))??[]})]}))}function tV(e){let t=new URL(e),n=new URLSearchParams(window.location.search);for(let[e,r]of n.entries())e.startsWith(`porto.`)&&t.searchParams.set(e.slice(6),r);return t.toString()}function nV(e){let t=new CustomEvent(`eip6963:announceProvider`,{detail:Object.freeze(e)});window.dispatchEvent(t);let n=()=>window.dispatchEvent(t);return window.addEventListener(`eip6963:requestProvider`,n),()=>window.removeEventListener(`eip6963:requestProvider`,n)}Object.freeze({status:`aborted`});function B(e,t,n){function r(n,r){if(n._zod||Object.defineProperty(n,`_zod`,{value:{def:r,constr:o,traits:new Set},enumerable:!1}),n._zod.traits.has(e))return;n._zod.traits.add(e),t(n,r);let i=o.prototype,a=Object.keys(i);for(let e=0;en?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,`name`,{value:e}),o}var rV=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}},iV={};function aV(e){return e&&Object.assign(iV,e),iV}function oV(e,t){return typeof t==`bigint`?t.toString():t}function sV(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function cV(e){return e==null}function lV(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}var uV=Symbol(`evaluating`);function dV(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==uV)return r===void 0&&(r=uV,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}function fV(e,t,n){Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0})}function pV(...e){let t={};for(let n of e)Object.assign(t,Object.getOwnPropertyDescriptors(n));return Object.defineProperties({},t)}var mV=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function hV(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function gV(e){if(hV(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(hV(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}var _V=new Set([`string`,`number`,`bigint`,`boolean`,`symbol`,`undefined`]);function vV(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function yV(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function bV(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error("Cannot specify both `message` and `error` params");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function xV(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function SV(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.pick() cannot be used on object schemas containing refinements`);return yV(e,pV(e._zod.def,{get shape(){let e={};for(let r in t){if(!(r in n.shape))throw Error(`Unrecognized key: "${r}"`);t[r]&&(e[r]=n.shape[r])}return fV(this,`shape`,e),e},checks:[]}))}function CV(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.omit() cannot be used on object schemas containing refinements`);return yV(e,pV(e._zod.def,{get shape(){let r={...e._zod.def.shape};for(let e in t){if(!(e in n.shape))throw Error(`Unrecognized key: "${e}"`);t[e]&&delete r[e]}return fV(this,`shape`,r),r},checks:[]}))}function wV(e,t,n){let r=t._zod.def.checks;if(r&&r.length>0)throw Error(`.partial() cannot be used on object schemas containing refinements`);return yV(t,pV(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in r))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t])}else for(let t in r)i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t];return fV(this,`shape`,i),i},checks:[]}))}function TV(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function DV(e){return typeof e==`string`?e:e?.message}function OV(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=DV(e.inst?._zod.def?.error?.(e))??DV(t?.error?.(e))??DV(n.customError?.(e))??DV(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}function kV(e){return Array.isArray(e)?`array`:typeof e==`string`?`string`:`unknown`}var AV=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,`_zod`,{value:e._zod,enumerable:!1}),Object.defineProperty(e,`issues`,{value:t,enumerable:!1}),e.message=JSON.stringify(t,oV,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},jV=B(`$ZodError`,AV),MV=B(`$ZodError`,AV,{Parent:Error}),NV=e=>(t,n,r,i)=>{let a=r?Object.assign(r,{async:!1}):{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new rV;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>OV(e,a,aV())));throw mV(t,i?.callee),t}return o.value},PV=NV(MV),FV=(e=>async(t,n,r,i)=>{let a=r?Object.assign(r,{async:!0}):{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>OV(e,a,aV())));throw mV(t,i?.callee),t}return o.value})(MV),IV=(e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new rV;return a.issues.length?{success:!1,error:new(e??jV)(a.issues.map(e=>OV(e,i,aV())))}:{success:!0,data:a.value}})(MV),LV=(e=>async(t,n,r)=>{let i=r?Object.assign(r,{async:!0}):{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>OV(e,i,aV())))}:{success:!0,data:a.value}})(MV),RV=(e=>(t,n,r)=>{let i=r?Object.assign(r,{direction:`backward`}):{direction:`backward`};return NV(e)(t,n,i)})(MV),zV=(e=>(t,n,r)=>NV(e)(t,n,r))(MV),BV=e=>{let t=e?`[\\s\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\s\\S]*`;return RegExp(`^${t}$`)},VV=/^-?\d+n?$/,HV=/^-?\d+(?:\.\d+)?$/,UV=/^(?:true|false)$/i,WV=/^null$/i,GV=/^undefined$/i,KV=B(`$ZodCheck`,(e,t)=>{var n;e._zod??={},e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])}),qV={number:`number`,bigint:`bigint`,object:`date`},JV=B(`$ZodCheckGreaterThan`,(e,t)=>{KV.init(e,t);let n=qV[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.minimum:n.exclusiveMinimum)??-1/0;t.value>r&&(t.inclusive?n.minimum=t.value:n.exclusiveMinimum=t.value)}),e._zod.check=r=>{(t.inclusive?r.value>=t.value:r.value>t.value)||r.issues.push({origin:n,code:`too_small`,minimum:typeof t.value==`object`?t.value.getTime():t.value,input:r.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}}),YV=B(`$ZodCheckMinLength`,(e,t)=>{var n;KV.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!cV(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.minimum??-1/0;t.minimum>n&&(e._zod.bag.minimum=t.minimum)}),e._zod.check=n=>{let r=n.value;if(r.length>=t.minimum)return;let i=kV(r);n.issues.push({origin:i,code:`too_small`,minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),XV=B(`$ZodCheckStringFormat`,(e,t)=>{var n,r;KV.init(e,t),e._zod.onattach.push(e=>{let n=e._zod.bag;n.format=t.format,t.pattern&&(n.patterns??=new Set,n.patterns.add(t.pattern))}),t.pattern?(n=e._zod).check??(n.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:t.format,input:n.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(r=e._zod).check??(r.check=()=>{})}),ZV=B(`$ZodCheckRegex`,(e,t)=>{XV.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:`regex`,input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}}),QV={major:4,minor:3,patch:6},$V=B(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=QV;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=TV(e),i;for(let a of t){if(a._zod.def.when){if(!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new rV;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=TV(e,t))});else{if(e.issues.length===t)continue;r||=TV(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(TV(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new rV;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new rV;return o.then(e=>t(e,r,a))}return t(o,r,a)}}dV(e,`~standard`,()=>({validate:t=>{try{let n=IV(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return LV(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),eH=B(`$ZodString`,(e,t)=>{$V.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??BV(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),tH=B(`$ZodNumber`,(e,t)=>{$V.init(e,t),e._zod.pattern=e._zod.bag.pattern??HV,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Number(n.value)}catch{}let i=n.value;if(typeof i==`number`&&!Number.isNaN(i)&&Number.isFinite(i))return n;let a=typeof i==`number`?Number.isNaN(i)?`NaN`:Number.isFinite(i)?void 0:`Infinity`:void 0;return n.issues.push({expected:`number`,code:`invalid_type`,input:i,inst:e,...a?{received:a}:{}}),n}}),nH=B(`$ZodBoolean`,(e,t)=>{$V.init(e,t),e._zod.pattern=UV,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=!!n.value}catch{}let i=n.value;return typeof i==`boolean`||n.issues.push({expected:`boolean`,code:`invalid_type`,input:i,inst:e}),n}}),rH=B(`$ZodBigInt`,(e,t)=>{$V.init(e,t),e._zod.pattern=VV,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=BigInt(n.value)}catch{}return typeof n.value==`bigint`||n.issues.push({expected:`bigint`,code:`invalid_type`,input:n.value,inst:e}),n}}),iH=B(`$ZodUndefined`,(e,t)=>{$V.init(e,t),e._zod.pattern=GV,e._zod.values=new Set([void 0]),e._zod.optin=`optional`,e._zod.optout=`optional`,e._zod.parse=(t,n)=>{let r=t.value;return r===void 0||t.issues.push({expected:`undefined`,code:`invalid_type`,input:r,inst:e}),t}}),aH=B(`$ZodNull`,(e,t)=>{$V.init(e,t),e._zod.pattern=WV,e._zod.values=new Set([null]),e._zod.parse=(t,n)=>{let r=t.value;return r===null||t.issues.push({expected:`null`,code:`invalid_type`,input:r,inst:e}),t}}),oH=B(`$ZodAny`,(e,t)=>{$V.init(e,t),e._zod.parse=e=>e}),sH=B(`$ZodUnknown`,(e,t)=>{$V.init(e,t),e._zod.parse=e=>e}),cH=B(`$ZodDate`,(e,t)=>{$V.init(e,t),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=new Date(n.value)}catch{}let i=n.value,a=i instanceof Date;return a&&!Number.isNaN(i.getTime())||n.issues.push({expected:`date`,code:`invalid_type`,input:i,...a?{received:`Invalid Date`}:{},inst:e}),n}});function lH(e,t,n){e.issues.length&&t.issues.push(...EV(n,e.issues)),t.value[n]=e.value}var uH=B(`$ZodArray`,(e,t)=>{$V.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;elH(t,n,e))):lH(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function dH(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...EV(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function fH(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key "${n}": expected a Zod schema`);let n=xV(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function pH(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optout===`optional`;for(let i in t){if(s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>dH(e,n,i,t,u))):dH(a,n,i,t,u)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}var mH=B(`$ZodObject`,(e,t)=>{if($V.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,`shape`,{get:()=>{let n={...e};return Object.defineProperty(t,`shape`,{value:n}),n}})}let n=sV(()=>fH(t));dV(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=hV,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e],r=n._zod.optout===`optional`,i=n._zod.run({value:s[e],issues:[]},o);i instanceof Promise?c.push(i.then(n=>dH(n,t,e,s,r))):dH(i,t,e,s,r)}return i?pH(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}});function hH(e,t,n,r){for(let n of e)if(n.issues.length===0)return t.value=n.value,t;let i=e.filter(e=>!TV(e));return i.length===1?(t.value=i[0].value,i[0]):(t.issues.push({code:`invalid_union`,input:t.value,inst:n,errors:e.map(e=>e.issues.map(e=>OV(e,r,aV())))}),t)}var gH=B(`$ZodUnion`,(e,t)=>{$V.init(e,t),dV(e._zod,`optin`,()=>t.options.some(e=>e._zod.optin===`optional`)?`optional`:void 0),dV(e._zod,`optout`,()=>t.options.some(e=>e._zod.optout===`optional`)?`optional`:void 0),dV(e._zod,`values`,()=>{if(t.options.every(e=>e._zod.values))return new Set(t.options.flatMap(e=>Array.from(e._zod.values)))}),dV(e._zod,`pattern`,()=>{if(t.options.every(e=>e._zod.pattern)){let e=t.options.map(e=>e._zod.pattern);return RegExp(`^(${e.map(e=>lV(e.source)).join(`|`)})$`)}});let n=t.options.length===1,r=t.options[0]._zod.run;e._zod.parse=(i,a)=>{if(n)return r(i,a);let o=!1,s=[];for(let e of t.options){let t=e._zod.run({value:i.value,issues:[]},a);if(t instanceof Promise)s.push(t),o=!0;else{if(t.issues.length===0)return t;s.push(t)}}return o?Promise.all(s).then(t=>hH(t,i,e,a)):hH(s,i,e,a)}}),_H=B(`$ZodDiscriminatedUnion`,(e,t)=>{t.inclusive=!1,gH.init(e,t);let n=e._zod.parse;dV(e._zod,`propValues`,()=>{let e={};for(let n of t.options){let r=n._zod.propValues;if(!r||Object.keys(r).length===0)throw Error(`Invalid discriminated union option at index "${t.options.indexOf(n)}"`);for(let[t,n]of Object.entries(r)){e[t]||(e[t]=new Set);for(let r of n)e[t].add(r)}}return e});let r=sV(()=>{let e=t.options,n=new Map;for(let r of e){let e=r._zod.propValues?.[t.discriminator];if(!e||e.size===0)throw Error(`Invalid discriminated union option at index "${t.options.indexOf(r)}"`);for(let t of e){if(n.has(t))throw Error(`Duplicate discriminator value "${String(t)}"`);n.set(t,r)}}return n});e._zod.parse=(i,a)=>{let o=i.value;if(!hV(o))return i.issues.push({code:`invalid_type`,expected:`object`,input:o,inst:e}),i;let s=r.value.get(o?.[t.discriminator]);return s?s._zod.run(i,a):t.unionFallback?n(i,a):(i.issues.push({code:`invalid_union`,errors:[],note:`No matching discriminator`,discriminator:t.discriminator,input:o,path:[t.discriminator],inst:e}),i)}}),vH=B(`$ZodTuple`,(e,t)=>{$V.init(e,t);let n=t.items;e._zod.parse=(r,i)=>{let a=r.value;if(!Array.isArray(a))return r.issues.push({input:a,inst:e,expected:`tuple`,code:`invalid_type`}),r;r.value=[];let o=[],s=[...n].reverse().findIndex(e=>e._zod.optin!==`optional`),c=s===-1?0:n.length-s;if(!t.rest){let t=a.length>n.length,i=a.length=a.length&&l>=c)continue;let t=e._zod.run({value:a[l],issues:[]},i);t instanceof Promise?o.push(t.then(e=>yH(e,r,l))):yH(t,r,l)}if(t.rest){let e=a.slice(n.length);for(let n of e){l++;let e=t.rest._zod.run({value:n,issues:[]},i);e instanceof Promise?o.push(e.then(e=>yH(e,r,l))):yH(e,r,l)}}return o.length?Promise.all(o).then(()=>r):r}});function yH(e,t,n){e.issues.length&&t.issues.push(...EV(n,e.issues)),t.value[n]=e.value}var bH=B(`$ZodRecord`,(e,t)=>{$V.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!gV(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[],o=t.keyType._zod.values;if(o){n.value={};let s=new Set;for(let e of o)if(typeof e==`string`||typeof e==`number`||typeof e==`symbol`){s.add(typeof e==`number`?e.toString():e);let o=t.valueType._zod.run({value:i[e],issues:[]},r);o instanceof Promise?a.push(o.then(t=>{t.issues.length&&n.issues.push(...EV(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...EV(e,o.issues)),n.value[e]=o.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`)continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&HV.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>OV(e,r,aV())),input:o,path:[o],inst:e});continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...EV(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...EV(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}}),xH=B(`$ZodLiteral`,(e,t)=>{if($V.init(e,t),t.values.length===0)throw Error(`Cannot create literal schema with no valid values`);let n=new Set(t.values);e._zod.values=n,e._zod.pattern=RegExp(`^(${t.values.map(e=>typeof e==`string`?vV(e):e?vV(e.toString()):String(e)).join(`|`)})$`),e._zod.parse=(r,i)=>{let a=r.value;return n.has(a)||r.issues.push({code:`invalid_value`,values:t.values,input:a,inst:e}),r}});function SH(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}var CH=B(`$ZodOptional`,(e,t)=>{$V.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,dV(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),dV(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${lV(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(t=>SH(t,e.value)):SH(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}}),wH=B(`$ZodNullable`,(e,t)=>{$V.init(e,t),dV(e._zod,`optin`,()=>t.innerType._zod.optin),dV(e._zod,`optout`,()=>t.innerType._zod.optout),dV(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${lV(e.source)}|null)$`):void 0}),dV(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0),e._zod.parse=(e,n)=>e.value===null?e:t.innerType._zod.run(e,n)}),TH=B(`$ZodPipe`,(e,t)=>{$V.init(e,t),dV(e._zod,`values`,()=>t.in._zod.values),dV(e._zod,`optin`,()=>t.in._zod.optin),dV(e._zod,`optout`,()=>t.out._zod.optout),dV(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if(n.direction===`backward`){let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>EH(e,t.in,n)):EH(r,t.in,n)}let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>EH(e,t.out,n)):EH(r,t.out,n)}});function EH(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues},n)}var DH=B(`$ZodCodec`,(e,t)=>{$V.init(e,t),dV(e._zod,`values`,()=>t.in._zod.values),dV(e._zod,`optin`,()=>t.in._zod.optin),dV(e._zod,`optout`,()=>t.out._zod.optout),dV(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if((n.direction||`forward`)===`forward`){let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>OH(e,t,n)):OH(r,t,n)}else{let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>OH(e,t,n)):OH(r,t,n)}}});function OH(e,t,n){if(e.issues.length)return e.aborted=!0,e;if((n.direction||`forward`)===`forward`){let r=t.transform(e.value,e);return r instanceof Promise?r.then(r=>kH(e,r,t.out,n)):kH(e,r,t.out,n)}else{let r=t.reverseTransform(e.value,e);return r instanceof Promise?r.then(r=>kH(e,r,t.in,n)):kH(e,r,t.in,n)}}function kH(e,t,n,r){return e.issues.length?(e.aborted=!0,e):n._zod.run({value:t,issues:e.issues},r)}var AH=B(`$ZodReadonly`,(e,t)=>{$V.init(e,t),dV(e._zod,`propValues`,()=>t.innerType._zod.propValues),dV(e._zod,`values`,()=>t.innerType._zod.values),dV(e._zod,`optin`,()=>t.innerType?._zod?.optin),dV(e._zod,`optout`,()=>t.innerType?._zod?.optout),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(jH):jH(r)}});function jH(e){return e.value=Object.freeze(e.value),e}var MH=B(`$ZodTemplateLiteral`,(e,t)=>{$V.init(e,t);let n=[];for(let e of t.parts)if(typeof e==`object`&&e){if(!e._zod.pattern)throw Error(`Invalid template literal part, no pattern found: ${[...e._zod.traits].shift()}`);let t=e._zod.pattern instanceof RegExp?e._zod.pattern.source:e._zod.pattern;if(!t)throw Error(`Invalid template literal part: ${e._zod.traits}`);let r=t.startsWith(`^`)?1:0,i=t.endsWith(`$`)?t.length-1:t.length;n.push(t.slice(r,i))}else if(e===null||_V.has(typeof e))n.push(vV(`${e}`));else throw Error(`Invalid template literal part: ${e}`);e._zod.pattern=RegExp(`^${n.join(``)}$`),e._zod.parse=(n,r)=>typeof n.value==`string`?(e._zod.pattern.lastIndex=0,e._zod.pattern.test(n.value)||n.issues.push({input:n.value,inst:e,code:`invalid_format`,format:t.format??`template_literal`,pattern:e._zod.pattern.source}),n):(n.issues.push({input:n.value,inst:e,expected:`string`,code:`invalid_type`}),n)}),NH,PH=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function FH(){return new PH}(NH=globalThis).__zod_globalRegistry??(NH.__zod_globalRegistry=FH()),globalThis.__zod_globalRegistry;function IH(e,t){return new e({type:`string`,...bV(t)})}function LH(e,t){return new e({type:`number`,checks:[],...bV(t)})}function RH(e,t){return new e({type:`boolean`,...bV(t)})}function zH(e,t){return new e({type:`bigint`,...bV(t)})}function BH(e,t){return new e({type:`undefined`,...bV(t)})}function VH(e,t){return new e({type:`null`,...bV(t)})}function HH(e){return new e({type:`any`})}function UH(e){return new e({type:`unknown`})}function WH(e,t){return new e({type:`date`,...bV(t)})}function GH(e,t){return new JV({check:`greater_than`,...bV(t),value:e,inclusive:!0})}function KH(e,t){return new YV({check:`min_length`,...bV(t),minimum:e})}function qH(e,t){return new ZV({check:`string_format`,format:`regex`,...bV(t),pattern:e})}var JH=B(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);$V.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>PV(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>IV(e,t,n),e.parseAsync=async(t,n)=>FV(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>LV(e,t,n),e.check=(...n)=>e.clone({...t,checks:[...t.checks??[],...n.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]},{parent:!0}),e.with=e.check,e.clone=(t,n)=>yV(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),YH=B(`ZodMiniString`,(e,t)=>{eH.init(e,t),JH.init(e,t)});function V(e){return IH(YH,e)}var XH=B(`ZodMiniNumber`,(e,t)=>{tH.init(e,t),JH.init(e,t)});function H(e){return LH(XH,e)}var ZH=B(`ZodMiniBoolean`,(e,t)=>{nH.init(e,t),JH.init(e,t)});function QH(e){return RH(ZH,e)}var $H=B(`ZodMiniBigInt`,(e,t)=>{rH.init(e,t),JH.init(e,t)});function eU(e){return zH($H,e)}var tU=B(`ZodMiniUndefined`,(e,t)=>{iH.init(e,t),JH.init(e,t)});function nU(e){return BH(tU,e)}var rU=B(`ZodMiniNull`,(e,t)=>{aH.init(e,t),JH.init(e,t)});function iU(e){return VH(rU,e)}var aU=B(`ZodMiniAny`,(e,t)=>{oH.init(e,t),JH.init(e,t)});function oU(){return HH(aU)}var sU=B(`ZodMiniUnknown`,(e,t)=>{sH.init(e,t),JH.init(e,t)});function cU(){return UH(sU)}var lU=B(`ZodMiniDate`,(e,t)=>{cH.init(e,t),JH.init(e,t)});function uU(e){return WH(lU,e)}var dU=B(`ZodMiniArray`,(e,t)=>{uH.init(e,t),JH.init(e,t)});function U(e,t){return new dU({type:`array`,element:e,...bV(t)})}var fU=B(`ZodMiniObject`,(e,t)=>{mH.init(e,t),JH.init(e,t),dV(e,`shape`,()=>t.shape)});function W(e,t){return new fU({type:`object`,shape:e??{},...bV(t)})}function pU(e,t){return SV(e,t)}function mU(e,t){return CV(e,t)}function hU(e,t){return wV(CU,e,t)}var gU=B(`ZodMiniUnion`,(e,t)=>{gH.init(e,t),JH.init(e,t)});function G(e,t){return new gU({type:`union`,options:e,...bV(t)})}var _U=B(`ZodMiniDiscriminatedUnion`,(e,t)=>{_H.init(e,t),JH.init(e,t)});function vU(e,t,n){return new _U({type:`union`,options:t,discriminator:e,...bV(n)})}var yU=B(`ZodMiniTuple`,(e,t)=>{vH.init(e,t),JH.init(e,t)});function K(e,t,n){let r=t instanceof $V;return new yU({type:`tuple`,items:e,rest:r?t:null,...bV(r?n:t)})}var bU=B(`ZodMiniRecord`,(e,t)=>{bH.init(e,t),JH.init(e,t)});function xU(e,t,n){return new bU({type:`record`,keyType:e,valueType:t,...bV(n)})}var SU=B(`ZodMiniLiteral`,(e,t)=>{xH.init(e,t),JH.init(e,t)});function q(e,t){return new SU({type:`literal`,values:Array.isArray(e)?e:[e],...bV(t)})}var CU=B(`ZodMiniOptional`,(e,t)=>{CH.init(e,t),JH.init(e,t)});function J(e){return new CU({type:`optional`,innerType:e})}var wU=B(`ZodMiniNullable`,(e,t)=>{wH.init(e,t),JH.init(e,t)});function TU(e){return new wU({type:`nullable`,innerType:e})}function EU(e){return J(TU(e))}var DU=B(`ZodMiniPipe`,(e,t)=>{TH.init(e,t),JH.init(e,t)}),OU=B(`ZodMiniCodec`,(e,t)=>{DU.init(e,t),DH.init(e,t)});function kU(e,t,n){return new OU({type:`pipe`,in:e,out:t,transform:n.decode,reverseTransform:n.encode})}var AU=B(`ZodMiniReadonly`,(e,t)=>{AH.init(e,t),JH.init(e,t)});function Y(e){return new AU({type:`readonly`,innerType:e})}var jU=B(`ZodMiniTemplateLiteral`,(e,t)=>{MH.init(e,t),JH.init(e,t)});function MU(e,t){return new jU({type:`template_literal`,parts:e,...bV(t)})}function NU(e,t={}){return _z(e,t)}function PU(e,t,n){if(t===`Error`)return FU;if(t===`Panic`)return IU;if(cL(t,{strict:!1})){let e=rL(t,0,4);if(e===`0x08c379a0`)return FU;if(e===`0x4e487b71`)return IU}let r=vz(e,t,n);if(r.type!==`error`)throw new Cz({name:t,type:`error`});return r}var FU=NU({inputs:[{name:`message`,type:`string`}],name:`Error`,type:`error`}),IU=NU({inputs:[{name:`reason`,type:`uint8`}],name:`Panic`,type:`error`});D();var LU=class extends E{constructor(){super(`Function is not recognized.`,{metaMessages:[`This could be due to any of the following:`,` - The contract does not have the function,`,` - The address is not a contract.`],name:`FunctionSelectorNotRecognizedError`})}};function RU(e,t={}){return iv(e,t)}function zU(e){return ov(e)}Ha();function BU(e,t){let n=e.walk(e=>`data`in e);if(!n?.data)return e;if(n.data===zU(RU(`error FnSelectorNotRecognized()`)))return new LU;let r=null;for(let e of t.calls){let t=e;if(t.abi)try{if(!Va({abi:t.abi,data:n.data}))continue;r=t}catch{}}return r?ls(n,{abi:r.abi,address:r.to,args:r.args,functionName:r.functionName}):e}var X=()=>MU([`0x`,V()],{message:`Needs string in format ^0x[A-Fa-f0-9]{40}$.`}),Z=()=>MU([`0x`,V()],{message:`Needs string in format ^0x[A-Fa-f0-9]+$.`}),Q=()=>kU(Z(),H(),{decode:e=>sL(e),encode:e=>$I(e)}),VU=()=>kU(Z(),eU({message:`Required bigint`}),{decode:e=>oL(e),encode:e=>$I(e)});function HU(e){return G(e)}var UU=class extends z{constructor(){super(...arguments),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Schema.ValidationError`})}};function WU(e){let t=e,n=`Validation failed with ${t.issues.length} error${t.issues.length===1?``:`s`}:`;n+=` `;for(let e of t.issues)e&&(n+=` -`,n+=KL(e));return new WL(n)}function KL(e,t=0){let n=qL(e.path),r=`- ${n?`${n}: `:``}`,i=` `.repeat(t+1),a=r;switch(e.code){case`invalid_type`:{let t=e.expected,n=e.input?JL(e):`undefined`;a+=`Expected ${t}. ${e.message===`Invalid input`?``:e.message}`,n!==`undefined`&&(a+=`but received ${n}`);break}case`too_big`:{let t=e.maximum,n=e.inclusive??!0;e.exact??!1?a+=`${e.origin} must be exactly ${t}`:a+=`${e.origin} must be ${n?`at most`:`less than`} ${t}`;break}case`too_small`:{let t=e.minimum,n=e.inclusive??!0;e.exact??!1?a+=`${e.origin} must be exactly ${t}`:a+=`${e.origin} must be ${n?`at least`:`greater than`} ${t}`;break}case`invalid_format`:switch(e.format){case`regex`:a+=`Must match pattern: ${e.pattern}`;break;case`starts_with`:a+=`Must start with "${e.prefix}"`;break;case`ends_with`:a+=`Must end with "${e.suffix}"`;break;case`includes`:a+=`Must include "${e.includes}"`;break;case`template_literal`:a+=`Must match pattern: ${e.pattern}`;break;default:a+=`Invalid ${e.format} format`}break;case`not_multiple_of`:a+=`Number must be a multiple of ${e.divisor}`;break;case`unrecognized_keys`:{let t=e.keys.map(e=>`"${e}"`).join(`, `);a+=`Unrecognized key${e.keys.length>1?`s`:``}: ${t}`;break}case`invalid_union`:{let n=e.errors&&e.errors.length>0;a+=`Invalid union value.`,n&&e.errors.forEach(e=>{e.length>0&&e.forEach(e=>{a+=` -`,a+=i,a+=KL(e,t+1)})});break}case`invalid_key`:a+=`Invalid ${e.origin} key`,e.issues&&e.issues.length>0&&e.issues.forEach(e=>{a+=` -`,a+=i,a+=KL(e,t+1)});break;case`invalid_element`:a+=`Invalid ${e.origin} element at key "${e.key}"`,e.issues&&e.issues.length>0&&e.issues.forEach(e=>{a+=` -`,a+=i,a+=KL(e,t+1)});break;case`invalid_value`:{let t=e.values.map(e=>JSON.stringify(e)).join(`, `);e.values.length>1?a+=`Expected one of: ${t}`:a+=`Expected ${t}`;break}case`custom`:a+=e.message||`Custom validation failed`;break;default:a+=e.message||`Validation failed`}return a}function qL(e){return e.length===0?``:"at `"+e.map((e,t)=>typeof e==`number`?`[${e}]`:typeof e==`symbol`?`[${e.toString()}]`:/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e)&&t>0?`.${e}`:t===0&&/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e)?e:`["${e}"]`).join(``)+"`"}function JL(e){let t=e.input;if(t===void 0)return`undefined`;if(t===null)return`null`;let n=typeof t;return n===`object`?Array.isArray(t)?`array`:t instanceof Date?`date`:t instanceof Map?`map`:t instanceof Set?`set`:`object`:n}const YL=U({selector:X(),to:Y(),type:K(`call`)}),XL=U({limit:Q(),period:W([K(`minute`),K(`hour`),K(`day`),K(`week`),K(`month`),K(`year`)]),token:q(W([Y(),AF()])),type:K(`spend`)}),ZL=W([YL,XL]),QL=U({expiry:Z(),prehash:q(wF()),publicKey:X(),role:W([K(`admin`),K(`normal`)]),type:W([K(`p256`),K(`secp256k1`),K(`webauthnp256`)])}),$L=U({...QL.shape,permissions:J(H(ZL))});var eR;(function(e){e.AssetDiffAsset=W([U({address:q(W([Y(),AF()])),decimals:q(W([V(),AF()])),direction:W([K(`incoming`),K(`outgoing`)]),fiat:q(U({currency:B(),value:tI(B(),V(),{decode:e=>Number(e),encode:e=>String(e)})})),name:q(W([B(),AF()])),symbol:B(),type:K(`erc20`),value:Q()}),U({address:q(W([Y(),AF()])),direction:W([K(`incoming`),K(`outgoing`)]),fiat:q(U({currency:B(),value:tI(B(),V(),{decode:e=>Number(e),encode:e=>String(e)})})),name:q(W([B(),AF()])),symbol:B(),type:K(`erc721`),uri:B(),value:Q()}),U({address:AF(),decimals:q(W([V(),AF()])),direction:W([K(`incoming`),K(`outgoing`)]),fiat:q(U({currency:B(),value:tI(B(),V(),{decode:e=>Number(e),encode:e=>String(e)})})),symbol:B(),type:AF(),value:Q()})]),e.Response=qF(X(),J(H(J(G([Y(),J(H(e.AssetDiffAsset))])))))})(eR||={});var tR;(function(e){e.Request=J(H($L)),e.Response=J(H(U({...$L.shape,hash:X()})))})(tR||={});var nR;(function(e){e.Response=qF(X(),U({currency:B(),value:B()}))})(nR||={});var rR;(function(e){e.Request=U({feePayer:q(Y()),feeToken:q(Y()),nonce:q(Q())})})(rR||={});var iR;(function(e){e.Request=J(H(U({address:Y(),value:Q()})))})(iR||={});var aR;(function(e){e.Request=J(H(U({hash:X()}))),e.Response=J(H(U({hash:X()})))})(aR||={});const oR=U({eoa:Y(),executionData:X(),nonce:X(),signature:X()}),sR=U({...oR.shape,chainId:Z()}),cR=W([U({combinedGas:Q(),encodedFundTransfers:J(H(X())),encodedPreCalls:J(H(X())),eoa:Y(),executionData:X(),expiry:Q(),funder:Y(),funderSignature:X(),isMultichain:wF(),nonce:Q(),payer:Y(),paymentAmount:Q(),paymentMaxAmount:Q(),paymentRecipient:Y(),paymentSignature:X(),paymentToken:Y(),settler:Y(),settlerContext:X(),signature:X(),supportedAccountImplementation:Y()}),U({combinedGas:Q(),encodedFundTransfers:J(H(X())),encodedPreCalls:J(H(X())),eoa:Y(),executionData:X(),expiry:Q(),funder:Y(),funderSignature:X(),isMultichain:wF(),nonce:Q(),payer:Y(),paymentRecipient:Y(),paymentSignature:X(),paymentToken:Y(),prePaymentAmount:Q(),prePaymentMaxAmount:Q(),settler:Y(),settlerContext:X(),signature:X(),supportedAccountImplementation:Y(),totalPaymentAmount:Q(),totalPaymentMaxAmount:Q()})]);U({eoa:Y(),executionData:X(),nonce:Q()});const lR=U({address:W([Y(),AF()]),decimals:q(V()),deficit:Q(),fiat:q(U({currency:B(),value:B()})),name:q(B()),required:Q(),symbol:q(B())}),uR=U({additionalAuthorization:QF(U({address:Y(),chainId:Z(),nonce:Z(),r:X(),s:X(),yParity:Z()})),assetDeficits:q(H(lR)),authorizationAddress:q(W([Y(),AF()])),chainId:Z(),ethPrice:Q(),extraPayment:Q(),feeTokenDeficit:Q(),intent:cR,nativeFeeEstimate:U({maxFeePerGas:Q(),maxPriorityFeePerGas:Q()}),orchestrator:Y(),paymentTokenDecimals:V(),txGas:Q()}),dR=U({multiChainRoot:q(W([X(),AF()])),quotes:J(H(uR)).check(vF(1)),ttl:V()}),fR=U({...dR.shape,hash:X(),r:X(),s:X(),v:q(X()),yParity:q(X())}),pR=U({address:Y(),decimals:V(),feeToken:q(wF()),interop:q(wF()),nativeRate:q(Q()),symbol:B(),uid:B()}),mR=B().check(yF(/^[A-Z0-9]+$/));var hR=U({address:Y(),chainId:Z(),nonce:Z()}),gR=U({...hR.shape,r:X(),s:X(),yParity:Z()}),_R=U({data:q(X()),to:Y(),value:q(Q())}),vR;(function(e){e.Parameters=U({address:Y(),secret:B()}),e.Request=U({method:K(`account_getOnrampContactInfo`),params:J(G([e.Parameters]))}),e.Response=U({email:q(B()),phone:q(B()),phoneVerifiedAt:q(V())})})(vR||={});var yR;(function(e){e.Parameters=U({address:Y()}),e.Request=U({method:K(`account_onrampStatus`),params:J(G([e.Parameters]))}),e.Response=U({email:q(V()),phone:q(V())})})(yR||={});var bR;(function(e){e.Parameters=U({phone:B(),walletAddress:Y()}),e.Request=U({method:K(`account_resendVerifyPhone`),params:J(G([e.Parameters]))}),e.Response=AF()})(bR||={});var xR;(function(e){e.Parameters=U({email:B().check(yF(/^.*@.*$/)),walletAddress:Y()}),e.Request=U({method:K(`account_setEmail`),params:J(G([e.Parameters]))}),e.Response=AF()})(xR||={});var SR;(function(e){e.Parameters=U({phone:B(),walletAddress:Y()}),e.Request=U({method:K(`account_setPhone`),params:J(G([e.Parameters]))}),e.Response=AF()})(SR||={});var CR;(function(e){e.Parameters=U({chainId:Z(),email:B(),signature:X(),token:B(),walletAddress:Y()}),e.Request=U({method:K(`account_verifyEmail`),params:J(G([e.Parameters]))}),e.Response=AF()})(CR||={});var wR;(function(e){e.Parameters=U({code:B(),phone:B(),walletAddress:Y()}),e.Request=U({method:K(`account_verifyPhone`),params:J(G([e.Parameters]))}),e.Response=AF()})(wR||={});var TR;(function(e){e.Request=U({method:K(`health`),params:OF()}),e.Response=U({quoteSigner:Y(),status:B(),version:B()})})(TR||={});var ER;(function(e){e.Parameters=U({address:Y(),chainId:Z(),tokenAddress:Y(),value:Q()}),e.Request=U({method:K(`wallet_addFaucetFunds`),params:J(G([e.Parameters]))}),e.Response=U({message:q(B()),transactionHash:X()})})(ER||={});var DR;(function(e){e.Parameters=U({chainId:Z(),id:X()}),e.Request=U({method:K(`wallet_getAccounts`),params:J(G([e.Parameters]))}),e.Response=J(H(U({address:Y(),keys:tR.Response})))})(DR||={});var OR;(function(e){e.Parameters=U({address:Y()}),e.Request=U({method:K(`wallet_getAuthorization`),params:J(G([e.Parameters]))}),e.Response=U({authorization:gR,data:X(),to:Y()})})(OR||={});var kR;(function(e){e.Request=U({method:K(`wallet_getCapabilities`),params:q(G([J(H(V()))]))});let t=U({address:Y(),version:q(W([B(),AF()]))});e.Response=qF(X(),U({contracts:U({accountImplementation:t,accountProxy:t,legacyAccountImplementations:J(H(t)),legacyOrchestrators:J(H(W([U({orchestrator:t,simulator:t}),t]))),orchestrator:t,simulator:t}),fees:U({quoteConfig:U({constantRate:q(W([V(),AF()])),gas:q(U({intentBuffer:q(V()),txBuffer:q(V())})),rateTtl:V(),ttl:V()}),recipient:Y(),tokens:J(H(pR))})}))})(kR||={});var AR;(function(e){let t=W([K(`native`),K(`erc20`),K(`erc721`),B()]);e.Parameters=U({account:Y(),assetFilter:q(qF(X(),J(H(U({address:W([Y(),K(`native`)]),type:t}))))),assetTypeFilter:q(J(H(t))),chainFilter:q(J(H(Z())))}),e.Request=U({method:K(`wallet_getAssets`),params:J(G([e.Parameters]))}),e.Price=U({currency:B(),value:tI(B(),V(),{decode:e=>Number(e),encode:e=>String(e)})}),e.Response=qF(B(),J(H(UL([U({address:Y(),balance:Q(),metadata:ZF(U({decimals:V(),fiat:QF(e.Price),name:B(),symbol:B()})),type:K(`erc20`)}),U({address:ZF(K(`native`)),balance:Q(),metadata:ZF(U({decimals:V(),fiat:QF(e.Price),name:q(B()),symbol:q(B())})),type:K(`native`)})]))))})(AR||={});var jR;(function(e){e.Request=U({method:K(`wallet_getCallsStatus`),params:J(G([X()]))}),e.Response=U({id:B(),receipts:q(J(H(U({blockHash:X(),blockNumber:Z(),chainId:Z(),gasUsed:Z(),logs:J(H(U({address:Y(),data:X(),topics:J(H(X()))}))),status:X(),transactionHash:X()})))),status:V()})})(jR||={});var MR;(function(e){e.Parameters=U({address:Y(),chainIds:q(J(H(Z())))}),e.Request=U({method:K(`wallet_getKeys`),params:J(G([e.Parameters]))}),e.Response=qF(X(),tR.Response)})(MR||={});var NR;(function(e){e.Capabilities=U({authorizeKeys:q(tR.Request),meta:rR.Request,preCall:q(wF()),preCalls:q(J(H(oR))),requiredFunds:q(iR.Request),revokeKeys:q(aR.Request)}),e.ResponseCapabilities=U({assetDiffs:q(eR.Response),authorizeKeys:QF(tR.Response),feePayerDigest:q(X()),feeSignature:q(X()),feeTotals:q(nR.Response),revokeKeys:QF(aR.Response)}),e.Parameters=U({calls:J(H(_R)),capabilities:e.Capabilities,chainId:Z(),from:q(Y()),key:q(U({prehash:wF(),publicKey:X(),type:QL.shape.type}))}),e.Request=U({method:K(`wallet_prepareCalls`),params:J(G([e.Parameters]))}),e.Response=U({capabilities:e.ResponseCapabilities,context:U({preCall:q(VF(sR)),quote:q(VF(fR))}),digest:X(),key:QF(U({prehash:wF(),publicKey:X(),type:QL.shape.type})),signature:X(),typedData:U({domain:W([U({chainId:W([Z(),V()]),name:B(),verifyingContract:Y(),version:B()}),U({})]),message:qF(B(),PF()),primaryType:B(),types:qF(B(),PF())})})})(NR||={});var PR;(function(e){e.Capabilities=U({authorizeKeys:tR.Request}),e.Parameters=U({address:Y(),capabilities:e.Capabilities,chainId:q(V()),delegation:Y()}),e.Request=U({method:K(`wallet_prepareUpgradeAccount`),params:J(G([e.Parameters]))}),e.Response=U({capabilities:e.Capabilities,chainId:Z(),context:U({address:Y(),authorization:hR,chainId:Z(),preCall:oR}),digests:U({auth:X(),exec:X()}),typedData:U({domain:W([U({chainId:W([Z(),V()]),name:B(),verifyingContract:Y(),version:B()}),U({})]),message:qF(B(),PF()),primaryType:B(),types:qF(B(),PF())})})})(PR||={});var FR;(function(e){e.Request=U({method:K(`wallet_feeTokens`),params:q(OF())}),e.Response=qF(X(),J(H(U({address:Y(),decimals:V(),nativeRate:q(Q()),symbol:B()}))))})(FR||={});var IR;(function(e){e.Parameters=U({capabilities:q(U({feeSignature:q(X())})),context:U({preCall:q(VF(sR)),quote:q(VF(fR))}),key:q(U({prehash:wF(),publicKey:X(),type:QL.shape.type})),signature:X()}),e.Request=U({method:K(`wallet_sendPreparedCalls`),params:J(G([e.Parameters]))}),e.Response=U({id:X()})})(IR||={});var LR;(function(e){e.Parameters=U({context:U({address:Y(),authorization:hR,chainId:Z(),preCall:oR}),signatures:U({auth:X(),exec:X()})}),e.Request=U({method:K(`wallet_upgradeAccount`),params:J(G([e.Parameters]))}),e.Response=OF()})(LR||={});var RR;(function(e){e.Parameters=U({address:X(),chainId:Z(),digest:X(),signature:X()}),e.Request=U({method:K(`wallet_verifySignature`),params:J(G([e.Parameters]))}),e.Response=U({proof:QF(U({account:Y(),initPreCall:QF(oR),keyHash:X()})),valid:wF()})})(RR||={}),Jd(),op();async function zR(e,t){try{let n=`wallet_getAuthorization`,r=await Tm(()=>e.request({method:n,params:[pP(OR.Parameters,t)]}),{cacheKey:`${e.uid}.${n}.${t.address}`});return mP(OR.Response,r)}catch(e){throw tz(e),e}}async function BR(e,t={}){let n=(()=>{if(t.chainId)return[t.chainId];if(t.chainIds!==`all`)return t.chainIds?t.chainIds:[e.chain.id]})();try{let r=`wallet_getCapabilities`,i=await Tm(()=>e.request({method:r,params:n?[n]:void 0},{retryCount:0}),{cacheKey:`${e.uid}.${r}.${n?.join(`,`)}`}),a=(()=>t.raw?i:mP(kR.Response,i))();return t.chainIds?a:Object.values(a)[0]}catch(e){throw tz(e),e}}async function VR(e,t){let{account:n,assetFilter:r,assetTypeFilter:i,chainFilter:a}=t;try{let t=await e.request({method:`wallet_getAssets`,params:[pP(AR.Parameters,{account:n,assetFilter:r,assetTypeFilter:i,chainFilter:a})]}),o=mP(AR.Response,t),s=Object.entries(o).reduce((e,[t,n])=>(e[Xf(t)]=n,e),{}),c={};for(let e of Object.values(s))for(let t of e){let e=JSON.stringify(t.metadata);c[e]={...t,balance:t.balance+(c[e]?.balance??0n)}}return{...s,0:Object.values(c)}}catch(e){throw tz(e),e}}async function HR(e,t){let{id:n}=t;try{let t=await e.request({method:`wallet_getCallsStatus`,params:[n]});return mP(jR.Response,t)}catch(e){throw tz(e),e}}async function UR(e,t){let{address:n,chainIds:r}=t;try{let t=await e.request({method:`wallet_getKeys`,params:[pP(MR.Parameters,{address:n,chainIds:r})]});return mP(MR.Response,t)}catch(e){throw tz(e),e}}async function WR(e){let t=`health`,n=await Tm(()=>e.request({method:t}),{cacheKey:`${e.uid}.${t}`});return mP(TR.Response,n)}async function GR(e,t){let{address:n,capabilities:r,chain:i=e.chain,key:a}=t,o=t.calls.map(e=>({data:e.abi?Y_(X_(e.abi,e.functionName),e.args):e.data??`0x`,to:e.to,value:e.value??0n}));try{let t=await e.request({method:`wallet_prepareCalls`,params:[pP(NR.Parameters,{calls:o,capabilities:{...r,meta:{...r?.meta}},chainId:i?.id,from:n,key:a?{prehash:a.prehash,publicKey:a.publicKey,type:a.type}:void 0})]},{retryCount:0});return Object.assign(mP(NR.Response,t),{_raw:t})}catch(e){throw tz(e),$R(e,{calls:t.calls}),e}}async function KR(e,t){let{address:n,chain:r=e.chain,delegation:i,...a}=t;try{let t=await e.request({method:`wallet_prepareUpgradeAccount`,params:[pP(PR.Parameters,pN({address:n,capabilities:a,chainId:r?.id,delegation:i}))]},{retryCount:0});return mP(PR.Response,t)}catch(e){throw tz(e),$R(e),e}}async function qR(e,t){let{capabilities:n,context:r,key:i,signature:a}=t;try{let t=await e.request({method:`wallet_sendPreparedCalls`,params:[pP(IR.Parameters,{capabilities:n,context:{preCall:r.preCall,quote:r.quote},key:i?{prehash:i.prehash,publicKey:i.publicKey,type:i.type}:void 0,signature:a})]},{retryCount:0});return mP(IR.Response,t)}catch(e){throw tz(e),$R(e),e}}async function JR(e,t){let{email:n,walletAddress:r}=t;try{let t=await e.request({method:`account_setEmail`,params:[pP(xR.Parameters,{email:n,walletAddress:r})]},{retryCount:0});return mP(xR.Response,t)}catch(e){throw tz(e),$R(e),e}}async function YR(e,t){let{context:n,signatures:r}=t;try{await e.request({method:`wallet_upgradeAccount`,params:[pP(LR.Parameters,{context:n,signatures:r})]},{retryCount:0})}catch(e){throw tz(e),$R(e),e}}async function XR(e,t){let{chainId:n,email:r,signature:i,token:a,walletAddress:o}=t;try{let t=await e.request({method:`account_verifyEmail`,params:[pP(CR.Parameters,{chainId:n,email:r,signature:i,token:a,walletAddress:o})]},{retryCount:0});return mP(CR.Response,t)}catch(e){throw tz(e),$R(e),e}}async function ZR(e,t){let{signature:n}=t,{signature:r,capabilities:{feeSignature:i,...a},...o}=t.response,s=ez({capabilities:a,...o}),c=Hh(Uf(JSON.stringify(s))),l=O_({payload:c,signature:o_(n)}),{quoteSigner:u}=await WR(e);return l===u}async function QR(e,t){let{address:n,chain:r=e.chain,digest:i,signature:a}=t;try{async function t(){return{proof:null,valid:await iv(e,{address:n,hash:i,signature:a})}}let o=await(async()=>{let o=await e.request({method:`wallet_verifySignature`,params:[pP(RR.Parameters,{address:n,chainId:r?.id,digest:i,signature:a})]},{retryCount:0}).catch(t);return o.valid?o:t()})();return mP(RR.Response,o)}catch(e){throw tz(e),e}}function $R(e,{calls:t}={}){if(!(e instanceof D))return;let n=e=>{try{if(e.name===`ContractFunctionExecutionError`){let t=e.cause.name===`ContractFunctionRevertedError`?e.cause.data:void 0;if(t)return LL([t.abiItem],t.errorName)}let t=e.walk(e=>!(e instanceof Error)&&e.code===3);if(!t)return;let{data:n,message:r}=t;return n===`0xd0d5039b`?IL(`error Unauthorized()`):{inputs:[],name:(r??n).split(`(`)[0],type:`error`}}catch{return}},r=HL(e,{calls:t??[]}),i=n(r);if(!(r===e&&!i))throw new nz(Object.assign(r,{abiError:i}))}function ez(e){if(typeof e==`object`&&e){if(Array.isArray(e))return e.map(ez);let t={};for(let n of Object.keys(e).sort())t[n]=ez(e[n]);return t}return e}function tz(e){if(e.name===`$ZodError`)throw GL(e)}var nz=class extends P{constructor(e){super(`An error occurred while executing calls.`,{cause:e,metaMessages:[e.abiError&&`Reason: `+e.abiError.name].filter(Boolean)}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Rpc.ExecutionError`}),Object.defineProperty(this,`abiError`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.abiError=e.abiError}};const rz={anvil:{http:`http://localhost:9119`},prod:{http:`https://rpc.porto.sh`},stg:{http:`https://stg-rpc.porto.sh`}};function iz(e){return t=>{let n=e.public(t),r=e.relay(t);return Nv({key:iz.type,name:`Relay Proxy`,async request({method:e,params:t},i){return az(e)?r.request({method:e,params:t},i):n.request({method:e,params:t},i)},type:iz.type})}}(function(e){e.type=`relayProxy`})(iz||={});function az(e){return!!(e.startsWith(`wallet_`)||e.startsWith(`account_`)||e===`health`)}pf();var oz=new Map;function sz(e,t={}){let{config:n,id:r,store:i}=e._internal,{chains:a,relay:o}=n,s=i.getState(),c=t.chainId??s.chainIds[0],l=a.find(e=>e.id===c);if(!l)throw Error([`Could not find a compatible Porto chain on the given chain configuration.`,``,`Provided chains: [${a.map(e=>`${e.name} (id: ${e.id})`).join(`, `)}]`,`Needed chain (id): ${c}`,`Please add this chain (id) to your chain configuration.`].join(` -`));let u=iz({public:n.transports[l.id]??Fv(l.rpcUrls.default.http.map(e=>zv(e))),relay:o}),d=[r,df(l)].filter(Boolean).join(`:`);if(oz.has(d))return oz.get(d);let f=Xm({...t,chain:l,pollingInterval:1e3,transport:u});return oz.set(d,f),f}const cz=U({chainId:q(Z()),expiry:Z(),hash:X(),id:X(),prehash:q(wF()),publicKey:X(),role:W([K(`admin`),K(`session`)]),type:W([K(`address`),K(`p256`),K(`secp256k1`),K(`webauthn-p256`)])}),lz=J(H(UL([U({signature:B(),to:Y()}),U({signature:B()}),U({to:Y()})])).check(vF(1))),uz=U({limit:W([iI([V(),`.`,V()]),iI([V()])]).check(yF(/^\d+(\.\d+)?$/)),symbol:q(W([K(`native`),mR]))}),dz=U({addresses:J(H(Y()))}),fz=J(H(U({limit:Q(),period:W([K(`minute`),K(`hour`),K(`day`),K(`week`),K(`month`),K(`year`)]),token:q(Y())}))),pz=U({calls:q(lz),signatureVerification:q(dz),spend:q(fz)}),mz=U({...cz.shape,feeToken:q(ZF(uz)),permissions:q(pz)}),hz=U({address:Y(),chainId:q(Z()),expiry:V(),id:X(),key:zF(cz,{publicKey:!0,type:!0}),permissions:U({calls:lz,signatureVerification:q(dz),spend:q(fz)})}),gz=U({address:q(Y()),chainId:q(Z()),expiry:V().check(_F(1)),feeToken:ZF(uz),key:q(zF(cz,{publicKey:!0,type:!0})),permissions:U({calls:lz,signatureVerification:q(dz),spend:q(fz)})}),_z=hz;function vz(e,t){let{chainId:n,expiry:r,permissions:i,id:a,publicKey:o,type:s}=e,{address:c}=t;return{address:c,chainId:n,expiry:r,id:a,key:{publicKey:o,type:s},permissions:i??{}}}function yz(e){let{chainId:t,expiry:n,key:r}=e;return vL({chainId:t,expiry:n,permissions:e.permissions??{},publicKey:r.publicKey,role:`session`,type:r.type})}var bz;(function(e){e.GetCapabilitiesResponse=U({status:W([K(`supported`),K(`unsupported`)])})})(bz||={});var xz;(function(e){e.Request=W([wF(),U({chainId:q(Z()),label:q(B())})])})(xz||={});var Sz;(function(e){e.Request=UL([U({chainId:q(V()),domain:q(B()),expirationTime:q(IF()),issuedAt:q(IF()),nonce:B(),notBefore:q(IF()),requestId:q(B()),resources:q(J(H(B()))),scheme:q(B()),statement:q(B()),uri:q(B()),version:q(K(`1`))}),U({authUrl:W([B(),U({logout:B(),nonce:B(),verify:B()})]),chainId:q(Z()),domain:q(B()),expirationTime:q(IF()),issuedAt:q(IF()),notBefore:q(IF()),requestId:q(B()),resources:q(J(H(B()))),scheme:q(B()),statement:q(B()),uri:q(B()),version:q(K(`1`))})]),e.Response=U({message:B(),signature:X(),token:q(B())})})(Sz||={});var Cz;(function(e){e.GetCapabilitiesResponse=U({supported:wF(),tokens:J(H(pR))}),e.Request=W([mR,Y()])})(Cz||={});var wz;(function(e){e.Request=gz})(wz||={});var Tz;(function(e){e.GetCapabilitiesResponse=U({supported:wF()})})(Tz||={});var Ez;(function(e){e.GetCapabilitiesResponse=U({supported:wF()}),e.Request=U({id:q(W([X(),AF()]))}),e.Response=J(H(hz))})(Ez||={});var Dz;(function(e){e.Request=J(H(U({context:PF(),signature:X()}))),e.Response=e.Request})(Dz||={});var Oz;(function(e){e.Request=B()})(Oz||={});var kz;(function(e){e.GetCapabilitiesResponse=U({supported:wF(),tokens:J(H(pR))}),e.Request=J(H(UL([U({address:Y(),value:Q()}),U({symbol:mR,value:W([iI([V(),`.`,V()]),iI([V()])]).check(yF(/^\d+(\.\d+)?$/))})])))})(kz||={});var Az=U({...zF(cz,{id:!0,publicKey:!0,type:!0}).shape,credentialId:q(B()),privateKey:q(MF())}),jz;(function(e){e.Parameters=U({address:Y(),secret:B()}),e.Request=U({method:K(`account_getOnrampContactInfo`),params:J(G([e.Parameters]))}),e.Response=U({email:q(B()),phone:q(B()),phoneVerifiedAt:q(V())})})(jz||={});var Mz;(function(e){e.Parameters=U({address:Y()}),e.Request=U({method:K(`account_onrampStatus`),params:J(G([e.Parameters]))}),e.Response=U({email:q(V()),phone:q(V())})})(Mz||={});var Nz;(function(e){e.Parameters=U({email:B(),walletAddress:Y()}),e.Request=U({method:K(`account_resendVerifyPhone`),params:J(G([e.Parameters]))}),e.Response=AF()})(Nz||={});var Pz;(function(e){e.Parameters=U({email:B(),walletAddress:Y()}),e.Request=U({method:K(`account_setEmail`),params:J(G([e.Parameters]))}),e.Response=AF()})(Pz||={});var Fz;(function(e){e.Parameters=U({email:B(),walletAddress:Y()}),e.Request=U({method:K(`account_setPhone`),params:J(G([e.Parameters]))}),e.Response=AF()})(Fz||={});var Iz;(function(e){e.Parameters=U({chainId:Z(),email:B(),token:B(),walletAddress:Y()}),e.Request=U({method:K(`account_verifyEmail`),params:J(G([e.Parameters]))}),e.Response=AF()})(Iz||={});var Lz;(function(e){e.Parameters=U({code:B(),phone:B(),walletAddress:Y()}),e.Request=U({method:K(`account_verifyPhone`),params:J(G([e.Parameters]))}),e.Response=AF()})(Lz||={});var Rz;(function(e){e.Parameters=U({address:q(Y()),chainId:q(Z()),token:q(Y()),value:q(B())}),e.Request=U({method:K(`wallet_addFunds`),params:J(G([e.Parameters]))}),e.Response=U({id:X()})})(Rz||={});var zz;(function(e){e.Request=U({method:K(`eth_accounts`),params:q(PF())}),e.Response=J(H(Y()))})(zz||={});var Bz;(function(e){e.Request=U({method:K(`eth_chainId`),params:q(PF())}),e.Response=X()})(Bz||={});var Vz;(function(e){e.Request=U({method:K(`eth_requestAccounts`),params:q(PF())}),e.Response=J(H(Y()))})(Vz||={});var Hz;(function(e){e.Request=U({method:K(`eth_sendTransaction`),params:J(G([U({capabilities:q(U({feeToken:q(Cz.Request),merchantUrl:q(Oz.Request),preCalls:q(Dz.Request)})),chainId:q(Z()),data:q(X()),from:q(Y()),to:Y(),value:q(Q())})]))}),e.Response=X()})(Hz||={});var Uz;(function(e){e.Request=U({method:K(`eth_signTypedData_v4`),params:J(G([Y(),B()]))}),e.Response=X()})(Uz||={});var Wz;(function(e){e.Parameters=U({address:q(Y()),chainId:q(Z())}),e.Request=U({method:K(`wallet_getAdmins`),params:q(J(G([e.Parameters])))}),e.Key=Az,e.Response=U({address:Y(),chainId:Z(),keys:J(H(e.Key))})})(Wz||={});var Gz;(function(e){e.Capabilities=U({feeToken:q(Cz.Request)}),e.Parameters=U({address:q(Y()),capabilities:q(e.Capabilities),chainId:q(Z()),key:zF(cz,{publicKey:!0,type:!0})}),e.Request=U({method:K(`wallet_grantAdmin`),params:J(G([e.Parameters]))}),e.Response=U({address:Y(),chainId:Z(),key:Wz.Key})})(Gz||={});var Kz;(function(e){e.Parameters=gz,e.Request=U({method:K(`wallet_grantPermissions`),params:J(G([e.Parameters]))}),e.ResponseCapabilities=U({preCalls:q(Dz.Response)}),e.Response=U({...hz.shape,capabilities:q(MF())})})(Kz||={});var qz;(function(e){e.Parameters=U({address:q(Y())}),e.Request=U({method:K(`wallet_getAccountVersion`),params:q(J(G([e.Parameters])))}),e.Response=U({current:B(),latest:B()})})(qz||={});var Jz;(function(e){e.Parameters=U({address:q(Y()),chainIds:q(J(H(Z())))}),e.Request=U({method:K(`wallet_getPermissions`),params:q(J(G([e.Parameters])))}),e.Response=Ez.Response})(Jz||={});var Yz;(function(e){e.Capabilities=U({feeToken:q(Cz.Request)}),e.Parameters=U({address:q(Y()),capabilities:q(e.Capabilities),chainId:q(Z()),id:X()}),e.Request=U({method:K(`wallet_revokeAdmin`),params:J(G([e.Parameters]))}),e.Response=void 0})(Yz||={});var Xz;(function(e){e.Capabilities=U({feeToken:q(Cz.Request)}),e.Parameters=U({address:q(Y()),capabilities:q(e.Capabilities),id:X()}),e.Request=U({method:K(`wallet_revokePermissions`),params:J(G([e.Parameters]))}),e.Response=void 0})(Xz||={});var Zz;(function(e){e.Request=U({method:K(`wallet_switchEthereumChain`),params:J(G([U({chainId:X()})]))})})(Zz||={});var Qz;(function(e){e.Parameters=U({context:PF(),signatures:U({auth:X(),exec:X()})}),e.Request=U({method:K(`wallet_upgradeAccount`),params:J(G([e.Parameters]))}),e.ResponseCapabilities=U({admins:q(J(H(Wz.Key))),permissions:q(Ez.Response)}),e.Response=U({address:Y(),capabilities:q(e.ResponseCapabilities)})})(Qz||={});var $z;(function(e){e.Request=U({method:K(`personal_sign`),params:J(G([X(),Y()]))}),e.Response=X()})($z||={});var eB;(function(e){e.Request=U({method:K(`porto_ping`),params:q(OF())}),e.Response=K(`pong`)})(eB||={});var tB;(function(e){e.Capabilities=U({createAccount:q(xz.Request),email:q(wF()),grantAdmins:q(J(H(zF(cz,{publicKey:!0,type:!0})))),grantPermissions:q(wz.Request),preCalls:q(Dz.Request),selectAccount:q(W([wF(),U({address:Y(),key:q(U({credentialId:q(B()),publicKey:X()}))})])),signInWithEthereum:q(Sz.Request)}),e.Parameters=U({capabilities:q(e.Capabilities),chainIds:q(J(H(Z())))}),e.Request=U({method:K(`wallet_connect`),params:q(J(G([e.Parameters])))}),e.ResponseCapabilities=U({admins:q(J(H(U({...zF(cz,{id:!0,publicKey:!0,type:!0}).shape,credentialId:q(B())})))),permissions:q(Ez.Response),preCalls:q(Dz.Response),signInWithEthereum:q(Sz.Response)}),e.Response=U({accounts:J(H(U({address:Y(),capabilities:q(e.ResponseCapabilities)}))),chainIds:J(H(Z()))})})(tB||={});var nB;(function(e){e.Request=U({method:K(`wallet_disconnect`),params:q(PF())}),e.Response=void 0})(nB||={});var rB;(function(e){e.Parameters=AR.Parameters,e.Request=AR.Request,e.Response=AR.Response})(rB||={});var iB;(function(e){e.Request=U({method:K(`wallet_getCallsStatus`),params:G([X()])}),e.Response=U({atomic:wF(),chainId:Z(),id:B(),receipts:q(J(H(U({blockHash:X(),blockNumber:X(),gasUsed:X(),logs:J(H(U({address:Y(),data:X(),topics:J(H(X()))}))),status:X(),transactionHash:X()})))),status:V(),version:B()})})(iB||={});var aB;(function(e){e.Request=U({method:K(`wallet_getCapabilities`),params:q(W([J(G([W([X(),OF()])])),J(G([W([X(),OF()]),J(H(Z()))]))]))}),e.Response=qF(X(),U({atomic:bz.GetCapabilitiesResponse,feeToken:Cz.GetCapabilitiesResponse,merchant:Tz.GetCapabilitiesResponse,permissions:Ez.GetCapabilitiesResponse,requiredFunds:kz.GetCapabilitiesResponse}))})(aB||={});var oB;(function(e){e.Parameters=U({address:Y(),chainIds:q(J(H(Z())))}),e.Request=U({method:K(`wallet_getKeys`),params:J(G([e.Parameters]))}),e.Response=J(H(mz))})(oB||={});var sB;(function(e){e.Capabilities=U({feeToken:q(Cz.Request),merchantUrl:q(Oz.Request),permissions:q(Ez.Request),preCalls:q(Dz.Request),requiredFunds:q(kz.Request)}),e.Parameters=U({calls:J(H(U({data:q(X()),to:Y(),value:q(Q())}))),capabilities:q(e.Capabilities),chainId:q(Z()),from:q(Y()),key:q(zF(cz,{prehash:!0,publicKey:!0,type:!0})),version:q(B())}),e.Request=U({method:K(`wallet_prepareCalls`),params:J(G([e.Parameters]))}),e.Response=U({capabilities:q(U({...NR.ResponseCapabilities.shape,quote:q(fR)})),chainId:X(),context:U({account:U({address:Y()}),calls:e.Parameters.shape.calls,nonce:Q(),quote:q(VF(fR))}),digest:X(),key:zF(cz,{prehash:!0,publicKey:!0,type:!0}),typedData:U({domain:W([U({chainId:Z(),name:B(),verifyingContract:Y(),version:B()}),U({})]),message:qF(B(),PF()),primaryType:B(),types:qF(B(),PF())})})})(sB||={});var cB;(function(e){e.Capabilities=U({...tB.Capabilities.shape,label:q(B())}),e.Parameters=U({address:Y(),capabilities:q(e.Capabilities),chainId:q(Z())}),e.Request=U({method:K(`wallet_prepareUpgradeAccount`),params:J(G([e.Parameters]))}),e.Response=U({context:PF(),digests:U({auth:X(),exec:X()})})})(cB||={});var lB;(function(e){e.Capabilities=sB.Capabilities,e.Request=U({method:K(`wallet_sendCalls`),params:J(G([BF(sB.Parameters,{key:!0})]))}),e.Response=U({id:X()})})(lB||={});var uB;(function(e){e.Parameters=U({capabilities:sB.Response.shape.capabilities,chainId:X(),context:sB.Response.shape.context,key:sB.Response.shape.key,signature:X()}),e.Request=U({method:K(`wallet_sendPreparedCalls`),params:J(G([e.Parameters]))}),e.Response=J(H(U({capabilities:q(qF(B(),PF())),id:X()})))})(uB||={});var dB;(function(e){e.Parameters=U({address:Y(),chainId:q(Z()),digest:X(),signature:X()}),e.Request=U({method:K(`wallet_verifySignature`),params:J(G([e.Parameters]))}),e.Response=U({address:Y(),chainId:Z(),proof:q(PF()),valid:wF()})})(dB||={});const fB=WF(`method`,[Iz.Request,Rz.Request,zz.Request,Bz.Request,Vz.Request,Hz.Request,Uz.Request,qz.Request,Wz.Request,Jz.Request,Gz.Request,Kz.Request,cB.Request,Yz.Request,Xz.Request,Qz.Request,$z.Request,eB.Request,tB.Request,nB.Request,rB.Request,iB.Request,aB.Request,oB.Request,sB.Request,lB.Request,uB.Request,Zz.Request,dB.Request]);function pB(e,t){let n=dP(e,t);if(n.error){let e=n.error.issues.at(0);throw e?.code===`invalid_union`&&e.note===`No matching discriminator`?new NM:new RM(GL(n.error))}return{...t,_decoded:n.data}}async function mB(e){e.persist.hasHydrated()||await new Promise(t=>{e.persist.onFinishHydration(()=>t(!0)),setTimeout(()=>t(!0),100)})}function hB(e){if(e)return e.startsWith(`/`)?`${window.location.origin}${e}`:e}op(),pf();function gB(e){let{config:t,getMode:n,id:r,store:i}=e,{announceProvider:a}=t;function o(e={}){let a=s(),o=e.request??pB(fB,{method:`wallet_getCapabilities`,params:e.chainIds?[void 0,e.chainIds]:void 0});return Tm(()=>n().actions.getCapabilities({chainIds:e.chainIds,internal:{client:a,config:t,request:o,store:i}}),{cacheKey:`getCapabilities.${r}.${e.chainIds?.join(`,`)}`})}function s(t){let n=typeof t==`string`?Xf(t):t;return sz({_internal:e},{chainId:n})}let c=new Map,l=[],u=tN(),d=nN({...u,async request(e){await mB(i);let r=[`eth_accounts`,`eth_chainId`,`eth_requestAccounts`,`wallet_getAssets`,`wallet_getCapabilities`,`wallet_getKeys`,`wallet_getPermissions`,`wallet_getAccountVersion`,`wallet_connect`].includes(e.method);return gN(async()=>{let r;try{r=pB(fB,e)}catch(t){let n=t;if(!(n instanceof NM))throw n;if(e.method.startsWith(`wallet_`))throw new WM;return s().request(e)}let a=i.getState();switch(r.method){case`account_verifyEmail`:{if(a.accounts.length===0)throw new GM;let[e]=r._decoded.params,{chainId:o,email:c,token:l,walletAddress:u}=e,d=s(o);if(o&&o!==d.chain.id)throw new KM;let f=u?a.accounts.find(e=>ag(e.address,u)):a.accounts[0];if(!f)throw new UM;return await n().actions.verifyEmail({account:f,chainId:o,email:c,internal:{client:d,config:t,request:r,store:i},token:l,walletAddress:u})}case`wallet_addFunds`:{if(a.accounts.length===0)throw new GM;let{address:e,value:o,token:c}=r.params[0]??{},l=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!l)throw new UM;let d=s(),f=await n().actions.addFunds({address:l.address,internal:{client:d,config:t,request:r,store:i},token:c,value:o});return u.emit(`message`,{data:null,type:`assetsChanged`}),f}case`eth_accounts`:if(a.accounts.length===0)throw new GM;return a.accounts.map(bB);case`eth_chainId`:return I(a.chainIds[0]);case`eth_requestAccounts`:{if(a.accounts.length>0&&c.get(`eth_requestAccounts`))return a.accounts.map(bB);let e=s(),{accounts:o}=await n().actions.loadAccounts({internal:{client:e,config:t,request:r,store:i}});return i.setState(e=>({...e,accounts:o})),u.emit(`connect`,{chainId:I(e.chain.id)}),c.set(`eth_requestAccounts`,!0),setTimeout(()=>c.delete(`eth_requestAccounts`),1e3),o.map(bB)}case`eth_sendTransaction`:{if(a.accounts.length===0)throw new GM;let[{capabilities:e,chainId:o,data:c=`0x`,from:l,to:u,value:d}]=r._decoded.params,f=s(o);if(o&&o!==f.chain.id)throw new KM;let p=l?a.accounts.find(e=>ag(e.address,l)):a.accounts[0];if(!p)throw new UM;let{id:m}=await n().actions.sendCalls({account:p,asTxHash:!0,calls:[{data:c,to:u,value:d}],chainId:f.chain.id,internal:{client:f,config:t,request:r,store:i},merchantUrl:hB(t.merchantUrl??e?.merchantUrl)});return m}case`eth_signTypedData_v4`:{if(a.accounts.length===0)throw new GM;let[e,o]=r._decoded.params,c=a.accounts.find(t=>ag(t.address,e));if(!c)throw new UM;let l=s();return await n().actions.signTypedData({account:c,data:o,internal:{client:l,config:t,request:r,store:i}})}case`wallet_grantAdmin`:{if(a.accounts.length===0)throw new GM;let[{address:e,capabilities:o,chainId:c,key:l}]=r._decoded.params??[{}],d=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!d)throw new UM;let f=s(c);if(vB([...d.keys??[]])?.some(e=>e.publicKey?.toLowerCase()===l.publicKey.toLowerCase()))throw new RM({message:`Key already granted as admin.`});let{key:p}=await n().actions.grantAdmin({account:d,feeToken:o?.feeToken,internal:{client:f,config:t,request:r,store:i},key:l});i.setState(e=>{let t=e.accounts.findIndex(e=>d?ag(e.address,d.address):!0);return t===-1?e:{...e,accounts:e.accounts.map((e,n)=>n===t?{...e,keys:[...e.keys??[],p]}:e)}});let m=vB([...d.keys??[],p]);return u.emit(`message`,{data:null,type:`adminsChanged`}),pP(Gz.Response,{address:d.address,chainId:f.chain.id,key:m.at(-1)})}case`wallet_grantPermissions`:{if(a.accounts.length===0)throw new GM;let[{address:e,chainId:o,...c}]=r._decoded.params??[{}],l=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!l)throw new UM;let d=s(o),{key:f}=await n().actions.grantPermissions({account:l,internal:{client:d,config:t,request:r,store:i},permissions:c});return i.setState(e=>{let t=e.accounts.findIndex(e=>l?ag(e.address,l.address):!0);return t===-1?e:{...e,accounts:e.accounts.map((e,n)=>n===t?{...e,keys:[...e.keys??[],f]}:e)}}),u.emit(`message`,{data:null,type:`permissionsChanged`}),pP(Kz.Response,{...vz(f,{address:l.address})})}case`wallet_getAdmins`:{if(a.accounts.length===0)throw new GM;let[{address:e,chainId:o}]=r._decoded.params??[{}],c=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!c)throw new UM;let l=s(o),u=await n().actions.getKeys({account:c,internal:{client:l,config:t,request:r,store:i}}),d=vB(u);return pP(Wz.Response,{address:c.address,chainId:l.chain.id,keys:d})}case`wallet_prepareUpgradeAccount`:{let[{address:e,capabilities:a,chainId:o}]=r._decoded.params??[{}],{email:c,label:u,grantPermissions:d}=a??{},f=s(o),{context:p,digests:m}=await n().actions.prepareUpgradeAccount({address:e,email:c,internal:{client:f,config:t,request:r,store:i},label:u,permissions:d});return l.push(p.account),{context:p,digests:m}}case`wallet_getAccountVersion`:{if(a.accounts.length===0)throw new GM;let[{address:e}]=r._decoded.params??[{}],o=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!o)throw new UM;let c=s(),{current:l,latest:u}=await n().actions.getAccountVersion({address:o.address,internal:{client:c,config:t,request:r,store:i}});return{current:l,latest:u}}case`wallet_getKeys`:{if(a.accounts.length===0)throw new GM;let[{address:e,chainIds:o}]=r._decoded.params??[{}],c=a.accounts.find(t=>ag(t.address,e));if(!c)throw new UM;let l=s(),u=await n().actions.getKeys({account:c,chainIds:o,internal:{client:l,config:t,request:r,store:i}});return pP(oB.Response,u)}case`wallet_getPermissions`:{if(a.accounts.length===0)throw new GM;let[{address:e,chainIds:o}]=r._decoded.params??[{}],c=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!c)throw new UM;let l=s(),u=await n().actions.getKeys({account:c,chainIds:o,internal:{client:l,config:t,request:r,store:i}});return yB(u,{address:c.address})}case`wallet_revokeAdmin`:{if(a.accounts.length===0)throw new GM;let[{address:e,capabilities:o,id:c}]=r._decoded.params,l=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!l)throw new UM;let d=s();await n().actions.revokeAdmin({account:l,feeToken:o?.feeToken,id:c,internal:{client:d,config:t,request:r,store:i}});let f=l.keys?.filter(e=>e.id.toLowerCase()!==c.toLowerCase());i.setState(e=>({...e,accounts:e.accounts.map(e=>ag(e.address,l.address)?{...e,keys:f}:e)})),u.emit(`message`,{data:null,type:`adminsChanged`});return}case`wallet_revokePermissions`:{if(a.accounts.length===0)throw new GM;let[{address:e,capabilities:o,id:c}]=r._decoded.params,l=e?a.accounts.find(t=>ag(t.address,e)):a.accounts[0];if(!l)throw new UM;let d=s();await n().actions.revokePermissions({account:l,feeToken:o?.feeToken,id:c,internal:{client:d,config:t,request:r,store:i}});let f=l.keys?.filter(e=>e.id.toLowerCase()!==c.toLowerCase());i.setState(e=>({...e,accounts:e.accounts.map(e=>ag(e.address,l.address)?{...e,keys:f}:e)})),u.emit(`message`,{data:null,type:`permissionsChanged`});return}case`wallet_upgradeAccount`:{let[{context:e,signatures:a}]=r._decoded.params??[{}],o=s(),c=l.find(t=>ag(t.address,e.account.address));if(!c)throw new UM;let{account:d}=await n().actions.upgradeAccount({account:c,context:e,internal:{client:o,config:t,request:r,store:i},signatures:a}),f=vB(d.keys??[]),p=yB(d.keys??[],{address:d.address});return i.setState(e=>({...e,accounts:[d]})),u.emit(`connect`,{chainId:I(o.chain.id)}),{address:d.address,capabilities:{admins:f,...p.length>0?{permissions:p}:{}}}}case`porto_ping`:return`pong`;case`personal_sign`:{if(a.accounts.length===0)throw new GM;let[e,o]=r._decoded.params,c=a.accounts.find(e=>ag(e.address,o));if(!c)throw new UM;let l=s();return await n().actions.signPersonalMessage({account:c,data:e,internal:{client:l,config:t,request:r,store:i}})}case`wallet_connect`:{let[{capabilities:e,chainIds:o}]=r._decoded.params??[{}],c=s(o?.[0]),l=c.chain.id,{createAccount:d,email:f,grantAdmins:p,grantPermissions:m,selectAccount:h,signInWithEthereum:g}=e??{},_={client:c,config:t,request:r,store:i},{accounts:v}=await(async()=>{if(f||d){let{label:e=void 0}=typeof d==`object`?d:{},{account:t}=await n().actions.createAccount({admins:p,email:f,internal:_,label:e,permissions:m,signInWithEthereum:g});return{accounts:[t]}}let e=a.accounts[0],{address:t,key:r}=(()=>{if(h)return typeof h==`object`?h:{address:void 0,key:void 0};for(let t of e?.keys??[])if(t.type===`webauthn-p256`&&t.role===`admin`)return{address:e?.address,key:{credentialId:t.credentialId??t.privateKey?.credential?.id,publicKey:t.publicKey}};return{address:void 0,key:void 0}})(),i={internal:_,permissions:m,signInWithEthereum:g};try{return await n().actions.loadAccounts({address:t,key:r,...i})}catch(e){if(e instanceof HM)throw e;if(t&&r)return await n().actions.loadAccounts(i);throw e}})();i.setState(e=>({...e,accounts:v}));let y=[l,...i.getState().chainIds.filter(e=>e!==l)];return u.emit(`connect`,{chainId:I(y[0])}),{accounts:v.map(e=>({address:bB(e),capabilities:{admins:e.keys?vB(e.keys):[],permissions:e.keys?yB(e.keys,{address:e.address}):[],...e.signInWithEthereum&&{signInWithEthereum:e.signInWithEthereum}}})),chainIds:y.map(e=>I(e))}}case`wallet_disconnect`:{let e=s();await n().actions.disconnect?.({internal:{client:e,config:t,request:r,store:i}}),i.setState(e=>({...e,accounts:[]})),u.emit(`disconnect`,new GM);return}case`wallet_getAssets`:{let[e]=r._decoded.params??[],{account:a,chainFilter:o,assetFilter:c,assetTypeFilter:l}=e,u=s(),d=await n().actions.getAssets({account:a,assetFilter:c,assetTypeFilter:l,chainFilter:o,internal:{client:u,config:t,request:r,store:i}}),f=Object.entries(d).reduce((e,[t,n])=>(e[I(Number(t))]=n,e),{});return pP(rB.Response,f)}case`wallet_getCallsStatus`:{let[e]=r._decoded.params??[],a=s();return await n().actions.getCallsStatus({id:e,internal:{client:a,config:t,request:r,store:i}})}case`wallet_getCapabilities`:{let[e,t]=r.params??[];return await o({chainIds:t,request:r})}case`wallet_prepareCalls`:{let[e]=r._decoded.params,{calls:o,capabilities:c,chainId:l,key:u,from:d}=e,f=s(l),p=d??a.accounts[0];if(!p)throw new UM;if(l&&l!==f.chain.id)throw new KM;let{digest:m,...h}=await n().actions.prepareCalls({account:ML(p),calls:o,feeToken:c?.feeToken,internal:{client:f,config:t,request:r,store:i},key:u,merchantUrl:hB(t.merchantUrl??c?.merchantUrl),requiredFunds:c?.requiredFunds});return pP(sB.Response,{capabilities:h.capabilities,chainId:I(h.chainId??f.chain.id),context:{...h.context,account:{address:h.account.address},calls:h.context.calls??[],nonce:h.context.nonce??0n},digest:m,key:h.key,typedData:h.typedData})}case`wallet_sendPreparedCalls`:{let[e]=r._decoded.params,{chainId:a,context:o,key:c,signature:l}=e,{account:u}=e.context,d=s(a);if(a&&Xf(a)!==d.chain.id)throw new KM;return[{id:await n().actions.sendPreparedCalls({account:ML(u),context:o,internal:{client:d,config:t,request:r,store:i},key:c,signature:l})}]}case`wallet_sendCalls`:{if(a.accounts.length===0)throw new GM;let[e]=r._decoded.params,{calls:o,capabilities:c,chainId:l,from:u}=e,d=s(l);if(l&&l!==d.chain.id)throw new KM;let f=u?a.accounts.find(e=>ag(e.address,u)):a.accounts[0];if(!f)throw new UM;let{id:p}=await n().actions.sendCalls({account:f,calls:o,chainId:d.chain.id,feeToken:c?.feeToken,internal:{client:d,config:t,request:r,store:i},merchantUrl:hB(t.merchantUrl??c?.merchantUrl),permissionsId:c?.permissions?.id,requiredFunds:c?.requiredFunds});return{id:p}}case`wallet_switchEthereumChain`:{let[e]=r._decoded.params,{chainId:a}=e,o=Xf(a);if(!t.chains.find(e=>e.id===o))throw new YM;let c=s(a);await n().actions.switchChain?.({chainId:c.chain.id,internal:{client:c,config:t,request:r,store:i}}),i.setState(e=>({...e,chainIds:[o,...e.chainIds.filter(e=>e!==o)]}));return}case`wallet_verifySignature`:{let[e]=r._decoded.params,{address:t,chainId:n,digest:i,signature:a}=e,o=s(n);return{...await QR(o,{address:t,digest:i,signature:a}),address:t,chainId:I(o.chain.id)}}}},{enabled:r,id:df(e)})}});function f(){let e=()=>{},t=()=>{};mB(i).then(()=>{o().catch(()=>{}),e(),e=i.subscribe(e=>e.accounts,e=>{u.emit(`accountsChanged`,e.map(bB))},{equalityFn:(e,t)=>e.every((e,n)=>e.address===t[n]?.address)}),t(),t=i.subscribe(e=>e.chainIds[0],(e,t)=>{e!==t&&u.emit(`chainChanged`,I(e))})});let n=_B(d,a);return()=>{e(),t(),n()}}let p=f();return Object.assign(d,{_internal:{destroy:p}})}function _B(e,t){if(!t||typeof window>`u`||!window.dispatchEvent)return()=>{};let{icon:n=`data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIyIiBoZWlnaHQ9IjQyMiIgdmlld0JveD0iMCAwIDQyMiA0MjIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI0MjIiIGhlaWdodD0iNDIyIiBmaWxsPSJibGFjayIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMV8xNSkiPgo8cGF0aCBkPSJNODEgMjg2LjM2NkM4MSAyODAuODkzIDg1LjQ1MDUgMjc2LjQ1NSA5MC45NDA0IDI3Ni40NTVIMzI5LjUxMUMzMzUuMDAxIDI3Ni40NTUgMzM5LjQ1MiAyODAuODkzIDMzOS40NTIgMjg2LjM2NlYzMDYuMTg4QzMzOS40NTIgMzExLjY2MiAzMzUuMDAxIDMxNi4wOTkgMzI5LjUxMSAzMTYuMDk5SDkwLjk0MDRDODUuNDUwNSAzMTYuMDk5IDgxIDMxMS42NjIgODEgMzA2LjE4OFYyODYuMzY2WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC41Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTAuOTQwNCAyMzQuODI4Qzg1LjQ1MDUgMjM0LjgyOCA4MSAyMzkuMjY2IDgxIDI0NC43MzlWMjcxLjUzMUM4My44NDMyIDI2OS42MzMgODcuMjYyMiAyNjguNTI2IDkwLjk0MDQgMjY4LjUyNkgzMjkuNTExQzMzMy4xODggMjY4LjUyNiAzMzYuNjA4IDI2OS42MzMgMzM5LjQ1MiAyNzEuNTMxVjI0NC43MzlDMzM5LjQ1MiAyMzkuMjY2IDMzNS4wMDEgMjM0LjgyOCAzMjkuNTExIDIzNC44MjhIOTAuOTQwNFpNMzM5LjQ1MiAyODYuMzY2QzMzOS40NTIgMjgwLjg5MyAzMzUuMDAxIDI3Ni40NTUgMzI5LjUxMSAyNzYuNDU1SDkwLjk0MDRDODUuNDUwNSAyNzYuNDU1IDgxIDI4MC44OTMgODEgMjg2LjM2NlYzMDYuMTlDODEgMzExLjY2NCA4NS40NTA1IDMxNi4xMDEgOTAuOTQwNCAzMTYuMTAxSDMyOS41MTFDMzM1LjAwMSAzMTYuMTAxIDMzOS40NTIgMzExLjY2NCAzMzkuNDUyIDMwNi4xOVYyODYuMzY2WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC41Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTAuOTQwNCAxOTMuMjAxQzg1LjQ1MDUgMTkzLjIwMSA4MSAxOTcuNjM4IDgxIDIwMy4xMTJWMjI5LjkwM0M4My44NDMyIDIyOC4wMDYgODcuMjYyMiAyMjYuODk5IDkwLjk0MDQgMjI2Ljg5OUgzMjkuNTExQzMzMy4xODggMjI2Ljg5OSAzMzYuNjA4IDIyOC4wMDYgMzM5LjQ1MiAyMjkuOTAzVjIwMy4xMTJDMzM5LjQ1MiAxOTcuNjM4IDMzNS4wMDEgMTkzLjIwMSAzMjkuNTExIDE5My4yMDFIOTAuOTQwNFpNMzM5LjQ1MiAyNDQuNzM5QzMzOS40NTIgMjM5LjI2NSAzMzUuMDAxIDIzNC44MjggMzI5LjUxMSAyMzQuODI4SDkwLjk0MDRDODUuNDUwNSAyMzQuODI4IDgxIDIzOS4yNjUgODEgMjQ0LjczOVYyNzEuNTNDODEuMjE3NSAyNzEuMzg1IDgxLjQzODMgMjcxLjI0NSA4MS42NjI0IDI3MS4xMDlDODMuODMyNSAyNjkuNzk0IDg2LjMwNTQgMjY4LjkyNyA4OC45NTIzIDI2OC42MzVDODkuNjA1MSAyNjguNTYzIDkwLjI2ODQgMjY4LjUyNiA5MC45NDA0IDI2OC41MjZIMzI5LjUxMUMzMzAuMTgzIDI2OC41MjYgMzMwLjg0NiAyNjguNTYzIDMzMS40OTggMjY4LjYzNUMzMzQuNDE5IDI2OC45NTcgMzM3LjEyOCAyNjkuOTggMzM5LjQ1MiAyNzEuNTNWMjQ0LjczOVpNMzM5LjQ1MiAyODYuMzY2QzMzOS40NTIgMjgxLjAyMSAzMzUuMjA2IDI3Ni42NjMgMzI5Ljg5MyAyNzYuNDYyQzMyOS43NjcgMjc2LjQ1NyAzMjkuNjQgMjc2LjQ1NSAzMjkuNTExIDI3Ni40NTVIOTAuOTQwNEM4NS40NTA1IDI3Ni40NTUgODEgMjgwLjg5MyA4MSAyODYuMzY2VjMwNi4xODhDODEgMzExLjY2MiA4NS40NTA1IDMxNi4xMDEgOTAuOTQwNCAzMTYuMTAxSDMyOS41MTFDMzM1LjAwMSAzMTYuMTAxIDMzOS40NTIgMzExLjY2MiAzMzkuNDUyIDMwNi4xODhWMjg2LjM2NloiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNSIvPgo8cGF0aCBvcGFjaXR5PSIwLjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTguMDE0NiAxMDRDODguNjE3NyAxMDQgODEgMTExLjU5NSA4MSAxMjAuOTY1VjE4OC4yNzZDODMuODQzMiAxODYuMzc5IDg3LjI2MjIgMTg1LjI3MiA5MC45NDA0IDE4NS4yNzJIMzI5LjUxMUMzMzMuMTg4IDE4NS4yNzIgMzM2LjYwOCAxODYuMzc5IDMzOS40NTIgMTg4LjI3NlYxMjAuOTY1QzMzOS40NTIgMTExLjU5NSAzMzEuODMzIDEwNCAzMjIuNDM3IDEwNEg5OC4wMTQ2Wk0zMzkuNDUyIDIwMy4xMTJDMzM5LjQ1MiAxOTcuNjM4IDMzNS4wMDEgMTkzLjIwMSAzMjkuNTExIDE5My4yMDFIOTAuOTQwNEM4NS40NTA1IDE5My4yMDEgODEgMTk3LjYzOCA4MSAyMDMuMTEyVjIyOS45MDNDODEuMjE3NSAyMjkuNzU4IDgxLjQzODMgMjI5LjYxOCA4MS42NjI0IDIyOS40ODJDODMuODMyNSAyMjguMTY3IDg2LjMwNTQgMjI3LjMgODguOTUyMyAyMjcuMDA4Qzg5LjYwNTEgMjI2LjkzNiA5MC4yNjg0IDIyNi44OTkgOTAuOTQwNCAyMjYuODk5SDMyOS41MTFDMzMwLjE4MyAyMjYuODk5IDMzMC44NDYgMjI2LjkzNiAzMzEuNDk4IDIyNy4wMDhDMzM0LjQxOSAyMjcuMzMgMzM3LjEyOCAyMjguMzUyIDMzOS40NTIgMjI5LjkwM1YyMDMuMTEyWk0zMzkuNDUyIDI0NC43MzlDMzM5LjQ1MiAyMzkuMzkzIDMzNS4yMDYgMjM1LjAzNiAzMjkuODkzIDIzNC44MzVDMzI5Ljc2NyAyMzQuODMgMzI5LjY0IDIzNC44MjggMzI5LjUxMSAyMzQuODI4SDkwLjk0MDRDODUuNDUwNSAyMzQuODI4IDgxIDIzOS4yNjUgODEgMjQ0LjczOVYyNzEuNTNMODEuMDcwNyAyNzEuNDgzQzgxLjI2NTMgMjcxLjM1NSA4MS40NjI1IDI3MS4yMyA4MS42NjI0IDI3MS4xMDlDODEuOTA4MyAyNzAuOTYgODIuMTU4MSAyNzAuODE3IDgyLjQxMTcgMjcwLjY3OUM4NC4zOTUzIDI2OS42MDUgODYuNjA1NCAyNjguODk0IDg4Ljk1MjMgMjY4LjYzNUM4OS4wMDUyIDI2OC42MjkgODkuMDU4IDI2OC42MjQgODkuMTExIDI2OC42MThDODkuNzEyNSAyNjguNTU3IDkwLjMyMjggMjY4LjUyNiA5MC45NDA0IDI2OC41MjZIMzI5LjUxMUMzMjkuNzM4IDI2OC41MjYgMzI5Ljk2NSAyNjguNTMgMzMwLjE5MiAyNjguNTM5QzMzMC42MzEgMjY4LjU1NSAzMzEuMDY3IDI2OC41ODcgMzMxLjQ5OCAyNjguNjM1QzMzNC40MTkgMjY4Ljk1NyAzMzcuMTI4IDI2OS45OCAzMzkuNDUyIDI3MS41M1YyNDQuNzM5Wk0zMzkuNDUyIDI4Ni4zNjZDMzM5LjQ1MiAyODEuMDIxIDMzNS4yMDYgMjc2LjY2MyAzMjkuODkzIDI3Ni40NjJMMzI5Ljg2NSAyNzYuNDYxQzMyOS43NDggMjc2LjQ1NyAzMjkuNjI5IDI3Ni40NTUgMzI5LjUxMSAyNzYuNDU1SDkwLjk0MDRDODUuNDUwNSAyNzYuNDU1IDgxIDI4MC44OTMgODEgMjg2LjM2NlYzMDYuMTg4QzgxIDMxMS42NjIgODUuNDUwNSAzMTYuMTAxIDkwLjk0MDQgMzE2LjEwMUgzMjkuNTExQzMzNS4wMDEgMzE2LjEwMSAzMzkuNDUyIDMxMS42NjIgMzM5LjQ1MiAzMDYuMTg4VjI4Ni4zNjZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjY5Ljg2OCAxMzEuNzUyQzI2OS44NjggMTI2LjI3OCAyNzQuMzE4IDEyMS44NCAyNzkuODA4IDEyMS44NEgzMTEuNjE4QzMxNy4xMDggMTIxLjg0IDMyMS41NTggMTI2LjI3OCAzMjEuNTU4IDEzMS43NTJWMTYxLjQ4NUMzMjEuNTU4IDE2Ni45NTkgMzE3LjEwOCAxNzEuMzk2IDMxMS42MTggMTcxLjM5NkgyNzkuODA4QzI3NC4zMTggMTcxLjM5NiAyNjkuODY4IDE2Ni45NTkgMjY5Ljg2OCAxNjEuNDg1VjEzMS43NTJaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzFfMTUiPgo8cmVjdCB3aWR0aD0iMjU5IiBoZWlnaHQ9IjIxMyIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgxIDEwNCkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K`,name:r=`Porto`,rdns:i=`xyz.ithaca.porto`}=typeof t==`object`?t:{};return jN({info:{icon:n,name:r,rdns:i,uuid:hN()},provider:e})}function vB(e){return e.map(e=>{if(e.role===`admin`)try{return pP(Wz.Key,{id:e.id??e.publicKey,publicKey:e.publicKey,type:e.type,...e.type===`webauthn-p256`?{credentialId:e.privateKey?.credential?.id,privateKey:{credential:{id:e.privateKey?.credential?.id},rpId:e.privateKey?.rpId}}:{}})}catch{return}}).filter(Boolean)}function yB(e,{address:t}){return e.map(e=>{if(e.chainId&&e.role===`session`&&!(e.expiry>0&&e.expiry()=>{})}}async function SB(e){let{account:t,calls:n,permissionsId:r}=e;if(r!==void 0){if(r===null)return;let e=t.keys?.find(e=>e.publicKey===r&&e.privateKey);if(!e)throw Error(`permission (id: ${r}) does not exist.`);return e}let i=t.keys?.find(e=>!e.privateKey||e.role!==`session`||e.expirye.permissions?.calls?.some(e=>{if(e.to&&e.to!==t.to)return!1;if(e.signature){if(!t.data)return!1;let n=Kf(t.data,0,4);if(Zf(e.signature))return e.signature===n;if(W_(e.signature)!==n)return!1}return!0}))),a=t.keys?.find(e=>e.role===`admin`&&e.privateKey);return i??a}function CB(e={}){let t=e.id??0;return{prepare(e){return wB({id:t++,...e})},get id(){return t}}}function wB(e){return{...e,jsonrpc:`2.0`}}function TB(){return null}const EB=gz;function DB(e){let{expiry:t,feeToken:n,permissions:r,publicKey:i,type:a}=e;return{expiry:t,feeToken:n??null,key:{publicKey:i,type:a},permissions:r??{}}}async function OB(e,t={}){if(!e)return;let n=t.chainId??e.chainId,r=e.expiry??0,i=e.feeToken,a=OL(e,{feeTokens:t.feeTokens}),o={chainId:n,expiry:r,feeToken:i,permissions:a,role:`session`};if(e?.key)return vL({...o,publicKey:e.key.publicKey,type:e.key.type??`secp256k1`});if(typeof globalThis.crypto?.subtle?.generateKey==`function`)try{return await _L(o)}catch(e){if(!kB(e))throw e}return mL(o)}function kB(e){if(!(e instanceof Error))return!1;let t=e.message?.toLowerCase()??``;return e.name===`TypeError`||e.name===`ReferenceError`||t.includes(`subtle`)||t.includes(`generatekey`)}Jd();const AB=/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:[0-9]{1,5})?$/,jB=/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:[0-9]{1,5})?$/,MB=/^localhost(:[0-9]{1,5})?$/,NB=/^[a-zA-Z0-9]{8,}$/,PB=/^([a-zA-Z][a-zA-Z0-9+-.]*)$/,FB=/^(?:(?[a-zA-Z][a-zA-Z0-9+-.]*):\/\/)?(?[a-zA-Z0-9+-.]*(?::[0-9]{1,5})?) (?:wants you to sign in with your Ethereum account:\n)(?
0x[a-fA-F0-9]{40})\n\n(?:(?.*)\n\n)?/,IB=/(?:URI: (?.+))\n(?:Version: (?.+))\n(?:Chain ID: (?\d+))\n(?:Nonce: (?[a-zA-Z0-9]+))\n(?:Issued At: (?.+))(?:\nExpiration Time: (?.+))?(?:\nNot Before: (?.+))?(?:\nRequest ID: (?.+))?/;function LB(e){let{chainId:t,domain:n,expirationTime:r,issuedAt:i=new Date,nonce:a,notBefore:o,requestId:s,resources:c,scheme:l,uri:u,version:d}=e;{if(t!==Math.floor(t))throw new VB({field:`chainId`,metaMessages:[`- Chain ID must be a EIP-155 chain ID.`,`- See https://eips.ethereum.org/EIPS/eip-155`,``,`Provided value: ${t}`]});if(!(AB.test(n)||jB.test(n)||MB.test(n)))throw new VB({field:`domain`,metaMessages:[`- Domain must be an RFC 3986 authority.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${n}`]});if(!NB.test(a))throw new VB({field:`nonce`,metaMessages:[`- Nonce must be at least 8 characters.`,`- Nonce must be alphanumeric.`,``,`Provided value: ${a}`]});if(!RB(u))throw new VB({field:`uri`,metaMessages:[`- URI must be a RFC 3986 URI referring to the resource that is the subject of the signing.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${u}`]});if(d!==`1`)throw new VB({field:`version`,metaMessages:[`- Version must be '1'.`,``,`Provided value: ${d}`]});if(l&&!PB.test(l))throw new VB({field:`scheme`,metaMessages:[`- Scheme must be an RFC 3986 URI scheme.`,`- See https://www.rfc-editor.org/rfc/rfc3986#section-3.1`,``,`Provided value: ${l}`]});let r=e.statement;if(r?.includes(` -`))throw new VB({field:`statement`,metaMessages:[`- Statement must not include '\\n'.`,``,`Provided value: ${r}`]})}let f=rg(e.address,{checksum:!0}),p=(()=>l?`${l}://${n}`:n)(),m=(()=>e.statement?`${e.statement}\n`:``)(),h=`${p} wants you to sign in with your Ethereum account:\n${f}\n\n${m}`,g=`URI: ${u}\nVersion: ${d}\nChain ID: ${t}\nNonce: ${a}\nIssued At: ${i.toISOString()}`;if(r&&(g+=`\nExpiration Time: ${r.toISOString()}`),o&&(g+=`\nNot Before: ${o.toISOString()}`),s&&(g+=`\nRequest ID: ${s}`),c){let e=` -Resources:`;for(let t of c){if(!RB(t))throw new VB({field:`resources`,metaMessages:[`- Every resource must be a RFC 3986 URI.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${t}`]});e+=`\n- ${t}`}g+=e}return`${h}\n${g}`}function RB(e){if(/[^a-z0-9:/?#[\]@!$&'()*+,;=.\-_~%]/i.test(e)||/%[^0-9a-f]/i.test(e)||/%[0-9a-f](:?[^0-9a-f]|$)/i.test(e))return!1;let t=zB(e),n=t[1],r=t[2],i=t[3],a=t[4],o=t[5];if(!(n?.length&&i&&i.length>=0))return!1;if(r?.length){if(!(i.length===0||/^\//.test(i)))return!1}else if(/^\/\//.test(i))return!1;if(!/^[a-z][a-z0-9+\-.]*$/.test(n.toLowerCase()))return!1;let s=``;return s+=`${n}:`,r?.length&&(s+=`//${r}`),s+=i,a?.length&&(s+=`?${a}`),o?.length&&(s+=`#${o}`),s}function zB(e){return e.match(/(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/)}function BB(e){let{scheme:t,statement:n,...r}=e.match(FB)?.groups??{},{chainId:i,expirationTime:a,issuedAt:o,notBefore:s,requestId:c,...l}=e.match(IB)?.groups??{},u=e.split(`Resources:`)[1]?.split(` -- `).slice(1);return{...r,...l,...i?{chainId:Number(i)}:{},...a?{expirationTime:new Date(a)}:{},...o?{issuedAt:new Date(o)}:{},...s?{notBefore:new Date(s)}:{},...c?{requestId:c}:{},...u?{resources:u}:{},...t?{scheme:t}:{},...n?{statement:n}:{}}}var VB=class extends P{constructor(e){let{field:t,metaMessages:n}=e;super(`Invalid Sign-In with Ethereum message field "${t}".`,{metaMessages:n}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Siwe.InvalidMessageFieldError`})}};async function HB(e){let{address:t,authUrl:n,message:r,signature:i,publicKey:a}=e,{chainId:o}=BB(r);return await fetch(n.verify,{body:JSON.stringify({address:t,chainId:o,message:r,signature:i,walletAddress:t,...a&&{publicKey:a}}),credentials:`include`,headers:{"Content-Type":`application/json`},method:`POST`}).then(e=>e.json())}async function UB(e,t,n){let{chainId:r=e.chain?.id,domain:i,uri:a,resources:o,version:s=`1`}=t,{address:c}=n,l=t.authUrl?WB(t.authUrl):void 0;if(!r)throw Error("`chainId` is required.");if(!i)throw Error("`domain` is required.");if(!t.nonce&&!l?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");if(!a)throw Error("`uri` is required.");let u=await(async()=>{if(t.nonce)return t.nonce;if(!l?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");let e=await(await fetch(l.nonce,{body:JSON.stringify({address:c,chainId:r,walletAddress:c}),headers:{"Content-Type":`application/json`},method:`POST`})).json().catch(()=>void 0);if(!e?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");return e.nonce})();return LB({...t,address:n.address,chainId:r,domain:i,nonce:u,resources:o,uri:a,version:s})}function WB(e,t=``){if(!e)return;let n=(()=>{if(typeof e==`string`){let t=e.replace(/\/$/,``);return{logout:t+`/logout`,nonce:t+`/nonce`,verify:t+`/verify`}}return e})();return{logout:GB(n.logout,t),nonce:GB(n.nonce,t),verify:GB(n.verify,t)}}function GB(e,t){return!t||!e.startsWith(`/`)?e:t+e}op();function KB(e){let t=Bf(e);return zf(`0x19`,Uf(`Ethereum Signed Message: -`+qf(t)),t)}function qB(e){return Hh(KB(e))}async function JB(e,t){let{account:n=e.account}=t,r=n?ML(n):void 0;if(!r)throw Error(`account is required.`);let{domain:{name:i,version:a}}=await $m(e,{address:r.address});if(!e.chain)throw Error(`client.chain is required`);return{chainId:e.chain.id,name:i,verifyingContract:r.address,version:a}}async function YB(e,t){let{account:n=e.account,chainIds:r}=t,i=n?ML(n):void 0;if(!i)throw Error(`account is required.`);let a=await UR(e,{address:i.address,chainIds:r});return Object.entries(a).flatMap(([e,t])=>t.map(t=>bL(t,{chainId:Number(e)})))}async function XB(e,t){let{account:n=e.account,calls:r,chain:i=e.chain,feePayer:a,merchantUrl:o,nonce:s,preCalls:c,requiredFunds:l,revokeKeys:u}=t,d=n?ML(n):void 0,f=t.key??(d?PL(d,{role:`admin`}):void 0),p=t.authorizeKeys?.some(e=>e.role===`session`),{contracts:m,fees:{tokens:h}}=await BR(e,{chainId:i?.id}),g=p?m.orchestrator.address:void 0,_=(t.authorizeKeys??[]).map(e=>DL(e,{feeTokens:h,orchestrator:g})),v=(()=>t.feeToken?t.feeToken:f?.permissions?.spend?.[0]?.token)(),y=typeof c==`boolean`?c:!1,b=typeof c==`object`?c.map(({context:e,signature:t})=>({...e.preCall,signature:t})):void 0,x={address:d?.address,calls:r??[],capabilities:{authorizeKeys:_,meta:{feePayer:a,feeToken:v,nonce:s},preCall:y,preCalls:b,requiredFunds:l,revokeKeys:u?.map(e=>({hash:e.hash}))},chain:i,key:f?DL(f,{feeTokens:h}):void 0},S=await(async()=>{if(o){let t=Xm({chain:e.chain,transport:zv(o)});return await GR(t,x).catch(t=>(console.error(t),GR(e,x)))}return await GR(e,x)})(),{capabilities:C,context:w,digest:ee,signature:te,typedData:ne}=S;if(o&&!await ZR(e,{response:S._raw,signature:te}))throw Error(`cannot verify integrity of \`wallet_prepareCalls\` response from ${o}`);return{capabilities:{...C,quote:w.quote},context:w,digest:ee,key:f,typedData:ne}}async function ZB(e,t){let{address:n,authorizeKeys:r,chain:i=e.chain}=t;if(!i)throw Error(`chain is required.`);let{contracts:a,fees:{tokens:o}}=await BR(e,{chainId:i.id}),s=t.delegation??a.accountProxy.address,c=r.some(e=>e.role===`session`)?a.orchestrator.address:void 0,l=r.map(e=>{let t=e.role===`session`?e.permissions:{};return DL({...e,permissions:t},{feeTokens:o,orchestrator:c})}),{capabilities:u,chainId:d,context:f,digests:p,typedData:m}=await KR(e,{address:n,authorizeKeys:l,chain:i,delegation:s}),h=ML({address:n,keys:r});return{capabilities:u,chainId:d,context:{...f,account:h},digests:p,typedData:m}}async function QB(e,t){let{account:n=e.account,chain:r=e.chain,webAuthn:i}=t;if(!r)throw Error("`chain` is required.");let a=n?ML(n):void 0;if(!a)throw Error("`account` is required.");let o=t.key??PL(a,t);if(!o&&!a.sign)throw Error("`key` or `account` with `sign` is required");let s=await Promise.all((t.preCalls??[]).map(async n=>{if(n.signature)return n;let{authorizeKeys:o,key:s,calls:c,revokeKeys:l}=n,{context:u,digest:d}=await XB(e,{account:a,authorizeKeys:o,calls:c,chain:r,feeToken:t.feeToken,key:s,preCalls:!0,revokeKeys:l}),f=await EL(s,{address:null,payload:d,webAuthn:i});return{context:u,signature:f}})),{capabilities:c,context:l,digest:u}=await XB(e,{...t,account:a,chain:r,key:o,preCalls:s}),d=await(async()=>o?await EL(o,{address:null,payload:u,webAuthn:i,wrap:!1}):await a.sign({hash:u}))();return await $B(e,{capabilities:c.feeSignature?{feeSignature:c.feeSignature}:void 0,context:l,key:o,signature:d})}async function $B(e,t){let{capabilities:n,context:r,key:i,signature:a}=t;return await qR(e,{capabilities:n,context:r,key:i?DL(i):void 0,signature:a})}async function eV(e,t){let{email:n,walletAddress:r}=t;return await JR(e,{email:n,walletAddress:r})}async function tV(e,t){if(t.account){let{account:n}=t,r=[...n.keys??[],...t.authorizeKeys??[]].filter((e,t,n)=>n.findIndex(t=>t.id===e.id)===t),{digests:i,...a}=await ZB(e,{...t,address:n.address,authorizeKeys:r}),o={auth:await n.sign({hash:i.auth}),exec:await n.sign({hash:i.exec})};return await tV(e,{...a,signatures:o})}let{context:n,signatures:r}=t,i=ML(n.account);return await YR(e,{context:n,signatures:r}),i}async function nV(e,t){let{chainId:n,email:r,signature:i,token:a,walletAddress:o}=t;return await XR(e,{chainId:n,email:r,signature:i,token:a,walletAddress:o})}async function rV(e,t){let{address:n}=t,{authorization:r,data:i,to:a}=await zR(e,{address:n});return P_({authorization:{...r,nonce:BigInt(r.nonce),r:BigInt(r.r),s:BigInt(r.s)},data:i,signature:t.signature,to:a})}function iV(e,t){let{tokens:n}=t,r=n.filter(e=>e.interop);return e.map(e=>{if(e.address)return e;let t=r.find(t=>t.symbol===e.symbol);if(!t)throw Error(`interop token not found: ${e.symbol}`);return{address:t.address,value:LI(e.value,t.decimals)}})}async function aV(e,t){let{chain:n=e.chain}=t??{};return await BR(e,{chainId:n?.id}).then(e=>e.fees.tokens)}async function oV(e,t){let{addressOrSymbol:n}=t;return(await aV(e,t)).find(oV.predicate(n))}(function(e){function t(e){return t=>e?og(e)?ag(t.address,e):e===`native`?t.address===Q_:e===t.symbol:!1}e.predicate=t})(oV||={});async function sV(e,t){let{chain:n=e.chain,store:r}=t??{},i=r?.getState()??{},a=t?.addressOrSymbol??i.feeToken;return(await aV(e,{chain:n}).then(e=>e.filter(e=>e.feeToken)))?.find(e=>a?a===`native`&&e.address===`0x0000000000000000000000000000000000000000`||og(a)&&ag(e.address,a)?!0:a===e.symbol:!1)}Lf(),op(),pf();function cV(e={}){let t=e,{mock:n,multichain:r=!0,webAuthn:i}=t,a,o,s=(()=>{if(t.keystoreHost!==`self`&&!(typeof window<`u`&&window.location?.hostname===`localhost`))return t.keystoreHost})();return xB({actions:{async addFunds(){throw new WM},async createAccount(e){let{admins:t,email:r,label:o,permissions:c,internal:l,signInWithEthereum:u}=e,{client:d}=l,f=NL(D_()),p=await aV(d),m=n?gL():await hL({createFn:i?.createFn,label:o||`${f.address.slice(0,8)}\u2026${f.address.slice(-6)}`,rpId:s,userId:hf(f.address)}),h=await OB(c,{chainId:d.chain.id,feeTokens:p}),g=t?.map(e=>vL(e)),_=await tV(d,{account:f,authorizeKeys:[m,...g??[],...h?[h]:[]]});a=f.address,r&&o&&await eV(d,{email:o,walletAddress:_.address});let v=await(async()=>{if(!u)return;let e=await UB(d,u,{address:_.address}),t=await FL(f,{payload:qB(Uf(e))}),n=await rV(d,{address:_.address,signature:t});return{message:e,signature:n}})();return{account:{..._,signInWithEthereum:v}}},async getAccountVersion(e){let{address:t,internal:n}=e,{client:r}=n,{contracts:i}=await BR(r),{accountImplementation:a}=i,o=await JB(r,{account:ML(a)}).then(e=>e.version),s=await JB(r,{account:t}).then(e=>e.version).catch(()=>o);if(!s||!o)throw Error(`version not found.`);return{current:s,latest:o}},async getAssets(e){let{account:t,chainFilter:n,assetFilter:r,assetTypeFilter:i,internal:a}=e,{client:o}=a;return await VR(o,{account:t,assetFilter:r,assetTypeFilter:i,chainFilter:n})},async getCallsStatus(e){let{id:t,internal:n}=e,{client:r}=n,i=await HR(r,{id:t});return{atomic:!0,chainId:I(r.chain.id),id:t,receipts:i.receipts?.map(e=>({blockHash:e.blockHash,blockNumber:I(e.blockNumber),gasUsed:I(e.gasUsed),logs:e.logs,status:e.status,transactionHash:e.transactionHash})),status:i.status,version:`1.0`}},async getCapabilities(e){let{chainIds:t,internal:n}=e,{client:i}=n,a={atomic:{status:`supported`},atomicBatch:{supported:!0},feeToken:{supported:!0,tokens:[]},merchant:{supported:!0},permissions:{supported:!0},requiredFunds:{supported:!!r,tokens:[]}},o=await BR(i,{chainIds:t?t.map(e=>Xf(e)):`all`,raw:!0});return Object.entries(o).reduce((e,[t,n])=>({...e,[t]:{...a,...n,feeToken:{supported:!0,tokens:n.fees.tokens},requiredFunds:{supported:!!r,tokens:r?n.fees.tokens.filter(e=>e.interop):[]}}}),{})},async getKeys(e){let{account:t,chainIds:n,internal:r}=e,{client:i}=r,a=await YB(i,{account:t,chainIds:n});return mN([...a,...t.keys??[]],e=>e.publicKey)},async grantAdmin(e){let{account:t,internal:n}=e,{client:r}=n,a=vL(e.key,{chainId:r.chain.id}),o=await sV(r,{addressOrSymbol:e.feeToken,store:n.store}),{id:s}=await QB(r,{account:t,authorizeKeys:[a],feeToken:o?.address,webAuthn:i});return await Wm(r,{id:s,pollingInterval:500}),{key:a}},async grantPermissions(e){let{account:t,internal:n,permissions:r}=e,{client:i}=n,a=await aV(i),o=await OB(r,{chainId:i.chain.id,feeTokens:a});if(!o)throw Error(`key to authorize not found.`);let s=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!s)throw Error(`admin key not found.`);let{context:c,digest:l}=await XB(i,{account:t,authorizeKeys:[o],key:s,preCalls:!0}),u=await EL(s,{address:null,payload:l});return await $B(i,{context:c,key:s,signature:u}),{key:o}},async loadAccounts(e){let{internal:t,permissions:r,signInWithEthereum:o}=e,{client:c}=t,l=await aV(c),u=await OB(r,{chainId:c.chain.id,feeTokens:l}),{digest:d,digestType:f,message:p}=await(async()=>{if(o&&e.address){let t=await UB(c,o,{address:e.address});return{context:void 0,digest:qB(Uf(t)),digestType:`siwe`,message:t}}return{context:void 0,digest:`0x`,message:void 0}})(),{address:m,credentialId:h,webAuthnSignature:g}=await(async()=>{if(n){if(!a)throw Error(`address_internal not found.`);return{address:a,credentialId:void 0}}if(e.address&&e.key)return{address:e.address,credentialId:e.key.credentialId};let t=await tL({challenge:d,getFn:i?.getFn,rpId:s}),r=t.raw.response,o=wf(new Uint8Array(r.userHandle)),c=t.raw.id;return{address:o,credentialId:c,webAuthnSignature:t}})(),_=await YB(c,{account:m,chainIds:[c.chain.id]}),v=ML({address:m,keys:[..._,...u?[u]:[]].map((e,t)=>t===0&&e.type===`webauthn-p256`?xL({...e,credential:{id:h,publicKey:qh(e.publicKey)},id:m,rpId:s}):e)}),y=PL(v,{role:`admin`}),b=await(async()=>{if(d!==`0x`)return g?jL(AL(g),{keyType:`webauthn-p256`,publicKey:y.publicKey}):await EL(y,{address:v.address,payload:d})})();if(u){let{context:e,digest:t}=await XB(c,{account:v,authorizeKeys:[u],preCalls:!0}),n=await EL(y,{address:null,payload:t});await $B(c,{context:e,key:y,signature:n})}let x=await(async()=>{if(o){if(f===`siwe`&&p&&b){let e=await rV(c,{address:v.address,signature:b});return{message:p,signature:e}}{let e=await UB(c,o,{address:v.address}),t=await FL(v,{payload:qB(Uf(e)),role:`admin`}),n=await rV(c,{address:v.address,signature:t});return{message:e,signature:n}}}})();return{accounts:[{...v,signInWithEthereum:x}]}},async prepareCalls(e){let{account:t,calls:n,internal:i,merchantUrl:a}=e,{client:o}=i,s=e.key??await SB({account:t,calls:n});if(!s)throw Error(`cannot find authorized key to sign with.`);let[c,l]=await Promise.all([aV(o),sV(o,{addressOrSymbol:e.feeToken,store:i.store})]),u=iV(e.requiredFunds??[],{tokens:c}),{capabilities:d,context:f,digest:p,typedData:m}=await XB(o,{account:t,calls:n,feeToken:l?.address,key:s,merchantUrl:a,requiredFunds:r?u:void 0}),h=f.quote?.quotes??[],g=h[h.length-1];return{account:t,capabilities:{...d,quote:f.quote},chainId:o.chain.id,context:{...f,account:t,calls:n,nonce:g?.intent.nonce},digest:p,key:s,typedData:m}},async prepareUpgradeAccount(e){let{address:t,email:r,label:a,internal:c,permissions:l}=e,{client:u}=c,[d,f]=await Promise.all([aV(u),sV(u,{store:c.store})]),p=n?gL():await hL({createFn:i?.createFn,label:a||`${t.slice(0,8)}\u2026${t.slice(-6)}`,rpId:s,userId:hf(t)}),m=await OB(l,{chainId:u.chain.id,feeTokens:d}),{context:h,digests:g}=await ZB(u,{address:t,authorizeKeys:[p,...m?[m]:[]],feeToken:f?.address});return r&&(o=a),{context:h,digests:g}},async revokeAdmin(e){let{account:t,id:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.id===n);if(o){if(o.type===`webauthn-p256`&&t.keys?.filter(e=>e.type===`webauthn-p256`).length===1)throw Error(`revoke the only WebAuthn key left.`);try{let n=await sV(a,{addressOrSymbol:e.feeToken,store:r.store}),{id:s}=await QB(a,{account:t,feeToken:n?.address,revokeKeys:[o],webAuthn:i});await Wm(a,{id:s})}catch(e){let t=e;if(t.name===`Rpc.ExecutionError`&&t.abiError?.name===`KeyDoesNotExist`)return;throw e}}},async revokePermissions(e){let{account:t,id:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.id===n);if(o){if(o.role===`admin`)throw Error(`cannot revoke admins.`);try{let n=await sV(a,{addressOrSymbol:e.feeToken,store:r.store}),{id:s}=await QB(a,{account:t,feeToken:n?.address,revokeKeys:[o],webAuthn:i});await Wm(a,{id:s})}catch(e){let t=e;if(t.name===`Rpc.ExecutionError`&&t.abiError?.name===`KeyDoesNotExist`)return;throw e}}},async sendCalls(e){let{account:t,asTxHash:n,calls:a,chainId:o,internal:s,merchantUrl:c}=e,{client:l}=s,u=await SB({account:t,calls:a,permissionsId:e.permissionsId}),[d,f]=await Promise.all([aV(l),sV(l,{addressOrSymbol:e.feeToken,store:s.store})]),p=iV(e.requiredFunds??[],{tokens:d}),m=await QB(l,{account:t,calls:a,feeToken:f?.address,key:u,merchantUrl:c,requiredFunds:r?p:void 0,webAuthn:i,...o?{chain:{id:o}}:{}});if(n){let{id:e,receipts:t,status:n}=await Wm(l,{id:m.id,pollingInterval:500});if(!t?.[0])throw n===`success`?new ZM({message:`Call bundle with id: `+e+` not found.`}):new MM({message:`Transaction failed under call bundle id: `+e+`.`});return{id:t[0].transactionHash}}return m},async sendPreparedCalls(e){let{context:t,key:n,internal:r,signature:i}=e,{client:a}=r,{id:o}=await $B(a,{context:t,key:n,signature:i});return o},async signPersonalMessage(e){let{account:t,data:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!o)throw Error(`cannot find admin key to sign with.`);let s=await FL(t,{key:o,payload:qB(n),webAuthn:i});return rV(a,{address:t.address,signature:s})},async signTypedData(e){let{account:t,internal:n}=e,{client:r}=n,a=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!a)throw Error(`cannot find admin key to sign with.`);let o=uf(e.data),s=o.domain?.name===`Orchestrator`,c=await FL(t,{key:a,payload:lI(o),replaySafe:!s,webAuthn:i});return s?c:rV(r,{address:t.address,signature:c})},async upgradeAccount(e){let{account:t,context:n,internal:r,signatures:i}=e,{client:a}=r;return await tV(a,{context:n,signatures:i}),o&&await eV(a,{email:o,walletAddress:t.address}),{account:t}},async verifyEmail(e){let{account:t,chainId:n,email:r,token:a,internal:o,walletAddress:s}=e,{client:c}=o,l=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!l)throw Error(`cannot find admin key to sign with.`);let u=await FL(t,{key:l,payload:Hh(Uf(`${r}${a}`)),webAuthn:i});return await nV(c,{chainId:n,email:r,signature:u,token:a,walletAddress:s})}},config:e,name:`rpc`})}op();function lV(e={}){let{fallback:t=cV(),host:n=bN.prod,renderer:r=SN(),theme:i,themeController:a}=e,o=new Set,s=CB();function c(e){return nN({async request(t){let n=s.prepare(t);return e.setState(e=>{let t=e.accounts[0],r=t?.keys?.find(e=>e.role===`admin`&&e.type===`webauthn-p256`);return{...e,requestQueue:[...e.requestQueue,{account:t?{address:t.address,key:r?{credentialId:r?.credentialId,publicKey:r.publicKey}:void 0}:void 0,request:n,status:`pending`}]}}),new Promise((t,r)=>{let i=a=>{let s=a.find(e=>e.request.id===n.id);if(!s&&a.length===0){o.delete(i),r(new HM);return}s&&(s.status!==`success`&&s.status!==`error`||(o.delete(i),s.status===`success`?t(s.result):r(rN(s.error)),e.setState(e=>({...e,requestQueue:e.requestQueue.filter(e=>e.request.id!==n.id)}))))};o.add(i)})}},{schema:TB()})}return xB({actions:{async addFunds(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`wallet_addFunds`)throw Error(`Cannot add funds for method: `+n.method);return await c(r).request(n)},async createAccount(e){let{internal:t}=e,{client:n,config:r,request:i,store:a}=t,{storage:o}=r,s=c(a);return{account:await(async()=>{if(i.method===`wallet_connect`){let[{capabilities:e,chainIds:t}]=i._decoded.params??[{}],a=dV(e?.signInWithEthereum?.authUrl??r.authUrl,{storage:o}),c=i.params?.[0]?.capabilities?.signInWithEthereum,l=await OB(e?.grantPermissions,{chainId:n.chain.id}),u=l?pP(EB,DB(l)):void 0,{accounts:d}=await s.request({...i,params:[{capabilities:{...i.params?.[0]?.capabilities,grantPermissions:u,signInWithEthereum:a||c?{...c,authUrl:a}:void 0},chainIds:t?.map(e=>I(e))}]}),[f]=d;if(!f)throw Error(`no account found.`);let p=f.capabilities?.admins?.map(e=>vL(e,{chainId:n.chain.id})).filter(Boolean),m=f.capabilities?.permissions?.map(e=>{try{let t=yz(mP(_z,e));return t.id===l?.id?{...t,...l,permissions:t.permissions}:t}catch{return}}).filter(Boolean),h=await(async()=>{if(!f.capabilities?.signInWithEthereum)return;let{message:e,signature:t}=f.capabilities.signInWithEthereum;if(!a)return{message:e,signature:t};let{token:n}=await HB({address:f.address,authUrl:a,message:e,publicKey:f.capabilities?.admins?.[0]?.publicKey,signature:t});return{message:e,signature:t,token:n}})();return{...ML({address:f.address,keys:[...p??[],...m??[]]}),signInWithEthereum:h}}throw Error(`Account creation not supported on method: ${i.method}`)})()}},async disconnect(e){let{internal:t}=e,{config:n}=t,{storage:r}=n,i=await r.getItem(`porto.authUrl`)||void 0,a=dV(n.authUrl??i,{storage:r});a&&await fetch(a.logout,{credentials:`include`,method:`POST`}).catch(()=>{})},async getAccountVersion(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getAccountVersion`)throw Error(`Cannot get version for method: `+a.method);return r.supportsHeadless?await c(i).request(a):t.actions.getAccountVersion(e)},async getAssets(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getAssets`)throw Error(`Cannot get assets for method: `+a.method);if(!r.supportsHeadless)return t.actions.getAssets(e);let o=await c(i).request(a);return mP(rB.Response,o)},async getCallsStatus(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCallsStatus`)throw Error(`Cannot get status for method: `+a.method);return r.supportsHeadless?await c(i).request(a):t.actions.getCallsStatus(e)},async getCapabilities(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCapabilities`)throw Error(`Cannot get capabilities for method: `+a.method);return r.supportsHeadless?await c(i).request(a):t.actions.getCapabilities(e)},async getKeys(e){let{account:n,chainIds:i,internal:a}=e,{store:o}=a,s=await(async()=>{if(!r.supportsHeadless)return t.actions.getKeys(e);let a=await c(o).request({method:`wallet_getKeys`,params:[pP(oB.Parameters,{address:n.address,chainIds:i})]});return mP(oB.Response,a)})();return mN([...s,...n.keys??[]],e=>e.publicKey)},async grantAdmin(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`wallet_grantAdmin`)throw Error(`Cannot authorize admin for method: `+n.method);let[i]=n._decoded.params,a=vL(i.key);if(!a)throw Error(`no key found.`);let o=await uV(t,e);return await c(r).request({method:`wallet_grantAdmin`,params:[{...n.params?.[0],capabilities:{...n.params?.[0]?.capabilities,feeToken:o}}]}),{key:a}},async grantPermissions(e){let{internal:t}=e,{client:n,request:r,store:i}=t;if(r.method!==`wallet_grantPermissions`)throw Error(`Cannot grant permissions for method: `+r.method);let[{address:a,...o}]=r._decoded.params,s=await OB(o,{chainId:n.chain.id});if(!s)throw Error(`no key found.`);let l=pP(EB,DB(s));return await c(i).request({method:`wallet_grantPermissions`,params:[l]}),{key:s}},async loadAccounts(e){let{internal:t}=e,{client:n,config:r,store:i}=t,{storage:a}=r,o=c(i),s=t.request;if(s.method!==`wallet_connect`&&s.method!==`eth_requestAccounts`)throw Error(`Cannot load accounts for method: `+s.method);return{accounts:await(async()=>{let[e]=s._decoded.params??[],{capabilities:t}=e??{},i=dV(t?.signInWithEthereum?.authUrl??r.authUrl,{storage:a}),c=s.params?.[0]?.capabilities?.signInWithEthereum,l=await OB(t?.grantPermissions,{chainId:n.chain.id}),u=l?pP(EB,DB(l)):void 0,{accounts:d}=await o.request({method:`wallet_connect`,params:[{...s.params?.[0],capabilities:{...s.params?.[0]?.capabilities,grantPermissions:u,signInWithEthereum:i||c?{...c,authUrl:i}:void 0}}]});return Promise.all(d.map(async e=>{let t=e.capabilities?.admins?.map(e=>vL(e)).filter(Boolean),n=e.capabilities?.permissions?.map(e=>{try{let t=yz(mP(_z,e));return t.id===l?.id?{...t,...l,permissions:t.permissions}:t}catch{return}}).filter(Boolean),r=await(async()=>{if(!e.capabilities?.signInWithEthereum)return;let{message:t,signature:n}=e.capabilities.signInWithEthereum;if(!i)return{message:t,signature:n};let{token:r}=await HB({address:e.address,authUrl:i,message:t,publicKey:e.capabilities?.admins?.[0]?.publicKey,signature:n});return{message:t,signature:n,token:r}})();return{...ML({address:e.address,keys:[...t??[],...n??[]]}),signInWithEthereum:r}}))})()}},async prepareCalls(e){let{account:n,internal:i}=e,{store:a,request:o}=i;if(o.method!==`wallet_prepareCalls`)throw Error(`Cannot prepare calls for method: `+o.method);if(!r.supportsHeadless)return t.actions.prepareCalls(e);let s=await uV(i,e),l=c(a),u=mP(sB.Response,await l.request({...o,params:[{...o.params?.[0],capabilities:{...o.params?.[0]?.capabilities,feeToken:s}}]}));return{account:n,chainId:Number(u.chainId),context:u.context,digest:u.digest,key:u.key,typedData:u.typedData}},async prepareUpgradeAccount(e){let{internal:n}=e,{client:i,store:a,request:o}=n;if(o.method!==`wallet_prepareUpgradeAccount`)throw Error(`Cannot prepare upgrade for method: `+o.method);if(!r.supportsHeadless)return t.actions.prepareUpgradeAccount(e);let[{capabilities:s}]=o._decoded.params??[{}],l=await OB(s?.grantPermissions,{chainId:i.chain.id}),u=l?pP(EB,DB(l)):void 0,{context:d,digests:f}=await c(a).request({...o,params:[{...o.params?.[0],capabilities:{...o.params?.[0]?.capabilities,grantPermissions:u}}]}),p=d.account.keys?.map(e=>e.id===l?.id?{...e,...l}:e);return{context:{...d,account:{...d.account,keys:p}},digests:f}},async revokeAdmin(e){let{account:t,id:n,internal:r}=e,{store:i,request:a}=r;if(a.method!==`wallet_revokeAdmin`)throw Error(`Cannot revoke admin for method: `+a.method);let o=t.keys?.find(e=>e.id===n);if(!o)return;if(o.type===`webauthn-p256`&&t.keys?.filter(e=>e.type===`webauthn-p256`).length===1)throw Error(`revoke the only WebAuthn key left.`);let s=await uV(r,e);return await c(i).request({...a,params:[{...a.params?.[0],capabilities:{...a.params?.[0]?.capabilities,feeToken:s}}]})},async revokePermissions(e){let{account:t,id:n,internal:r}=e,{store:i,request:a}=r;if(a.method!==`wallet_revokePermissions`)throw Error(`Cannot revoke permissions for method: `+a.method);let o=t.keys?.find(e=>e.id===n);if(o){if(o.role===`admin`)throw Error(`cannot revoke permissions.`);return await c(i).request(a)}},async sendCalls(e){let{account:n,asTxHash:i,calls:a,chainId:o,internal:s,merchantUrl:l,requiredFunds:u}=e,{client:d,store:f,request:p}=s,m=c(f),h=await uV(s,e),g=await SB({account:n,calls:a,permissionsId:e.permissionsId});if(g&&g.role===`session`){if(!r.supportsHeadless)return t.actions.sendCalls(e);try{let e=await m.request(pP(sB.Request,{method:`wallet_prepareCalls`,params:[{calls:a,capabilities:{...p._decoded.method===`wallet_sendCalls`?p._decoded.params?.[0]?.capabilities:void 0,feeToken:h,merchantUrl:l,requiredFunds:u},chainId:o,from:n.address,key:g}]})),t=e.capabilities?.quote?.quotes??[];if(t.some((e,n)=>n===t.length-1&&t.length>1?!1:Yf(e.feeTokenDeficit)>0n))throw Error(`insufficient funds`);let r=await EL(g,{address:null,payload:e.digest,wrap:!1}),s=(await m.request({method:`wallet_sendPreparedCalls`,params:[{...e,signature:r}]}))[0];if(!s)throw Error(`id not found`);if(i){let{id:e,receipts:t,status:n}=await Wm(d,{id:s.id,pollingInterval:500});if(!t?.[0])throw n===`success`?new ZM({message:`Call bundle with id: `+e+` not found.`}):new MM({message:`Transaction failed under call bundle id: `+e+`.`});return{id:t[0].transactionHash}}return s}catch{}}if(p.method===`eth_sendTransaction`)return{id:await m.request({...p,params:[{...p.params?.[0],capabilities:{feeToken:h,merchantUrl:l},...o?{chainId:I(o)}:{}}]})};if(p.method===`wallet_sendCalls`)return await m.request({method:`wallet_sendCalls`,params:[{...p.params?.[0],capabilities:{...p.params?.[0]?.capabilities,feeToken:h,merchantUrl:l},...o?{chainId:I(o)}:{}}]});throw Error(`Cannot execute for method: `+p.method)},async sendPreparedCalls(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_sendPreparedCalls`)throw Error(`Cannot send prepared calls for method: `+a.method);if(!r.supportsHeadless)return t.actions.sendPreparedCalls(e);let o=(await c(i).request(a))[0]?.id;if(!o)throw Error(`id not found`);return o},async signPersonalMessage(e){let{internal:t}=e,{store:n,request:r}=t;if(r.method!==`personal_sign`)throw Error(`Cannot sign personal message for method: `+r.method);return await c(n).request(r)},async signTypedData(e){let{internal:t}=e,{store:n,request:r}=t;if(r.method!==`eth_signTypedData_v4`)throw Error(`Cannot sign typed data for method: `+r.method);return await c(n).request(r)},async switchChain(e){let{internal:t}=e,{store:n,request:i}=t;if(i.method!==`wallet_switchEthereumChain`)throw Error(`Cannot switch chain for method: `+i.method);if(r.supportsHeadless)return await c(n).request(i)},async upgradeAccount(e){let{account:t,internal:n}=e,{store:r,request:i}=n;if(i.method!==`wallet_upgradeAccount`)throw Error(`Cannot upgrade account for method: `+i.method);return await c(r).request(i),{account:t}},async verifyEmail(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`account_verifyEmail`)throw Error(`Cannot verify email for method: `+n.method);return await c(r).request(n)}},config:e,name:`dialog`,setup(e){let{internal:t}=e,{store:s}=t,c=r.setup({host:n,internal:t,theme:i,themeController:a}),l=s.subscribe(e=>e.requestQueue,e=>{for(let t of o)t(e);let t=e.map(e=>e.status===`pending`?e:void 0).filter(Boolean);c.syncRequests(t).catch(()=>{}),t.length===0&&c.close()});return()=>{l(),c.destroy()}}})}async function uV(e,t){let{config:{feeToken:n}}=e,{feeToken:r}=t??{};return r??n}function dV(e,{storage:t}){if(!e)return;let n=WB(e,typeof window<`u`?window.location.origin:void 0);return n&&t.setItem(`porto.authUrl`,n),n}var fV=new Map,pV=e=>{let t=fV.get(e);return t?Object.fromEntries(Object.entries(t.stores).map(([e,t])=>[e,t.getState()])):{}},mV=(e,t,n)=>{if(e===void 0)return{type:`untracked`,connection:t.connect(n)};let r=fV.get(n.name);if(r)return{type:`tracked`,store:e,...r};let i={connection:t.connect(n),stores:{}};return fV.set(n.name,i),{type:`tracked`,store:e,...i}},hV=(e,t)=>{if(t===void 0)return;let n=fV.get(e);n&&(delete n.stores[t],Object.keys(n.stores).length===0&&fV.delete(e))},gV=e=>{if(!e)return;let t=e.split(` -`),n=t.findIndex(e=>e.includes(`api.setState`));if(n<0)return;let r=t[n+1]?.trim()||``;return/.+ (.+) .+/.exec(r)?.[1]},_V=(e,t={})=>(n,r,i)=>{let{enabled:a,anonymousActionType:o,store:s,...c}=t,l;try{l=(a??!1)&&window.__REDUX_DEVTOOLS_EXTENSION__}catch{}if(!l)return e(n,r,i);let{connection:u,...d}=mV(s,l,c),f=!0;i.setState=((e,t,a)=>{let l=n(e,t);if(!f)return l;let d=a===void 0?{type:o||gV(Error().stack)||`anonymous`}:typeof a==`string`?{type:a}:a;return s===void 0?(u?.send(d,r()),l):(u?.send({...d,type:`${s}/${d.type}`},{...pV(c.name),[s]:i.getState()}),l)}),i.devtools={cleanup:()=>{u&&typeof u.unsubscribe==`function`&&u.unsubscribe(),hV(c.name,s)}};let p=(...e)=>{let t=f;f=!1,n(...e),f=t},m=e(i.setState,r,i);if(d.type===`untracked`?u?.init(m):(d.stores[d.store]=i,u?.init(Object.fromEntries(Object.entries(d.stores).map(([e,t])=>[e,e===d.store?m:t.getState()])))),i.dispatchFromDevtools&&typeof i.dispatch==`function`){let e=i.dispatch;i.dispatch=(...t)=>{e(...t)}}return u.subscribe(e=>{switch(e.type){case`ACTION`:if(typeof e.payload!=`string`){console.error(`[zustand devtools middleware] Unsupported action format`);return}return vV(e.payload,e=>{if(e.type===`__setState`){if(s===void 0){p(e.state);return}Object.keys(e.state).length!==1&&console.error(` +`,n+=GU(e));return new UU(n)}function GU(e,t=0){let n=KU(e.path),r=`- ${n?`${n}: `:``}`,i=` `.repeat(t+1),a=r;switch(e.code){case`invalid_type`:{let t=e.expected,n=e.input?qU(e):`undefined`;a+=`Expected ${t}. ${e.message===`Invalid input`?``:e.message}`,n!==`undefined`&&(a+=`but received ${n}`);break}case`too_big`:{let t=e.maximum,n=e.inclusive??!0;e.exact??!1?a+=`${e.origin} must be exactly ${t}`:a+=`${e.origin} must be ${n?`at most`:`less than`} ${t}`;break}case`too_small`:{let t=e.minimum,n=e.inclusive??!0;e.exact??!1?a+=`${e.origin} must be exactly ${t}`:a+=`${e.origin} must be ${n?`at least`:`greater than`} ${t}`;break}case`invalid_format`:switch(e.format){case`regex`:a+=`Must match pattern: ${e.pattern}`;break;case`starts_with`:a+=`Must start with "${e.prefix}"`;break;case`ends_with`:a+=`Must end with "${e.suffix}"`;break;case`includes`:a+=`Must include "${e.includes}"`;break;case`template_literal`:a+=`Must match pattern: ${e.pattern}`;break;default:a+=`Invalid ${e.format} format`}break;case`not_multiple_of`:a+=`Number must be a multiple of ${e.divisor}`;break;case`unrecognized_keys`:{let t=e.keys.map(e=>`"${e}"`).join(`, `);a+=`Unrecognized key${e.keys.length>1?`s`:``}: ${t}`;break}case`invalid_union`:{let n=e.errors&&e.errors.length>0;a+=`Invalid union value.`,n&&e.errors.forEach(e=>{e.length>0&&e.forEach(e=>{a+=` +`,a+=i,a+=GU(e,t+1)})});break}case`invalid_key`:a+=`Invalid ${e.origin} key`,e.issues&&e.issues.length>0&&e.issues.forEach(e=>{a+=` +`,a+=i,a+=GU(e,t+1)});break;case`invalid_element`:a+=`Invalid ${e.origin} element at key "${e.key}"`,e.issues&&e.issues.length>0&&e.issues.forEach(e=>{a+=` +`,a+=i,a+=GU(e,t+1)});break;case`invalid_value`:{let t=e.values.map(e=>JSON.stringify(e)).join(`, `);e.values.length>1?a+=`Expected one of: ${t}`:a+=`Expected ${t}`;break}case`custom`:a+=e.message||`Custom validation failed`;break;default:a+=e.message||`Validation failed`}return a}function KU(e){return e.length===0?``:"at `"+e.map((e,t)=>typeof e==`number`?`[${e}]`:typeof e==`symbol`?`[${e.toString()}]`:/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e)&&t>0?`.${e}`:t===0&&/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e)?e:`["${e}"]`).join(``)+"`"}function qU(e){let t=e.input;if(t===void 0)return`undefined`;if(t===null)return`null`;let n=typeof t;return n===`object`?Array.isArray(t)?`array`:t instanceof Date?`date`:t instanceof Map?`map`:t instanceof Set?`set`:`object`:n}var JU=G([W({selector:Z(),to:X(),type:q(`call`)}),W({limit:VU(),period:G([q(`minute`),q(`hour`),q(`day`),q(`week`),q(`month`),q(`year`)]),token:J(G([X(),iU()])),type:q(`spend`)})]),YU=W({expiry:Q(),prehash:J(QH()),publicKey:Z(),role:G([q(`admin`),q(`normal`)]),type:G([q(`p256`),q(`secp256k1`),q(`webauthnp256`)])}),XU=W({...YU.shape,permissions:Y(U(JU))}),ZU;(function(e){e.AssetDiffAsset=G([W({address:J(G([X(),iU()])),decimals:J(G([H(),iU()])),direction:G([q(`incoming`),q(`outgoing`)]),fiat:J(W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})})),name:J(G([V(),iU()])),symbol:V(),type:q(`erc20`),value:VU()}),W({address:J(G([X(),iU()])),direction:G([q(`incoming`),q(`outgoing`)]),fiat:J(W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})})),name:J(G([V(),iU()])),symbol:V(),type:q(`erc721`),uri:V(),value:VU()}),W({address:iU(),decimals:J(G([H(),iU()])),direction:G([q(`incoming`),q(`outgoing`)]),fiat:J(W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})})),symbol:V(),type:iU(),value:VU()})]),e.Response=xU(Z(),Y(U(Y(K([X(),Y(U(e.AssetDiffAsset))])))))})(ZU||={});var QU;(function(e){e.Request=Y(U(XU)),e.Response=Y(U(W({...XU.shape,hash:Z()})))})(QU||={});var $U;(function(e){e.Response=xU(Z(),W({currency:V(),value:V()}))})($U||={});var eW;(function(e){e.Request=W({feePayer:J(X()),feeToken:J(X()),nonce:J(VU())})})(eW||={});var tW;(function(e){e.Request=Y(U(W({address:X(),value:VU()})))})(tW||={});var nW;(function(e){e.Request=Y(U(W({hash:Z()}))),e.Response=Y(U(W({hash:Z()})))})(nW||={});var rW=W({eoa:X(),executionData:Z(),nonce:Z(),signature:Z()}),iW=W({...rW.shape,chainId:Q()}),aW=G([W({combinedGas:VU(),encodedFundTransfers:Y(U(Z())),encodedPreCalls:Y(U(Z())),eoa:X(),executionData:Z(),expiry:VU(),funder:X(),funderSignature:Z(),isMultichain:QH(),nonce:VU(),payer:X(),paymentAmount:VU(),paymentMaxAmount:VU(),paymentRecipient:X(),paymentSignature:Z(),paymentToken:X(),settler:X(),settlerContext:Z(),signature:Z(),supportedAccountImplementation:X()}),W({combinedGas:VU(),encodedFundTransfers:Y(U(Z())),encodedPreCalls:Y(U(Z())),eoa:X(),executionData:Z(),expiry:VU(),funder:X(),funderSignature:Z(),isMultichain:QH(),nonce:VU(),payer:X(),paymentRecipient:X(),paymentSignature:Z(),paymentToken:X(),prePaymentAmount:VU(),prePaymentMaxAmount:VU(),settler:X(),settlerContext:Z(),signature:Z(),supportedAccountImplementation:X(),totalPaymentAmount:VU(),totalPaymentMaxAmount:VU()})]);VU();var oW=W({address:G([X(),iU()]),decimals:J(H()),deficit:VU(),fiat:J(W({currency:V(),value:V()})),name:J(V()),required:VU(),symbol:J(V())}),sW=W({additionalAuthorization:EU(W({address:X(),chainId:Q(),nonce:Q(),r:Z(),s:Z(),yParity:Q()})),assetDeficits:J(U(oW)),authorizationAddress:J(G([X(),iU()])),chainId:Q(),ethPrice:VU(),extraPayment:VU(),feeTokenDeficit:VU(),intent:aW,nativeFeeEstimate:W({maxFeePerGas:VU(),maxPriorityFeePerGas:VU()}),orchestrator:X(),paymentTokenDecimals:H(),txGas:VU()}),cW=W({...W({multiChainRoot:J(G([Z(),iU()])),quotes:Y(U(sW)).check(KH(1)),ttl:H()}).shape,hash:Z(),r:Z(),s:Z(),v:J(Z()),yParity:J(Z())}),lW=W({address:X(),decimals:H(),feeToken:J(QH()),interop:J(QH()),nativeRate:J(VU()),symbol:V(),uid:V()}),uW=V().check(qH(/^[A-Z0-9]+$/)),dW=W({address:X(),chainId:Q(),nonce:Q()}),fW=W({...dW.shape,r:Z(),s:Z(),yParity:Q()}),pW=W({data:J(Z()),to:X(),value:J(VU())}),mW;(function(e){e.Parameters=W({address:X(),secret:V()}),e.Request=W({method:q(`account_getOnrampContactInfo`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(V()),phone:J(V()),phoneVerifiedAt:J(H())})})(mW||={});var hW;(function(e){e.Parameters=W({address:X()}),e.Request=W({method:q(`account_onrampStatus`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(H()),phone:J(H())})})(hW||={});var gW;(function(e){e.Parameters=W({phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_resendVerifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(gW||={});var _W;(function(e){e.Parameters=W({email:V().check(qH(/^.*@.*$/)),walletAddress:X()}),e.Request=W({method:q(`account_setEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(_W||={});var vW;(function(e){e.Parameters=W({phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_setPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(vW||={});var yW;(function(e){e.Parameters=W({chainId:Q(),email:V(),signature:Z(),token:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(yW||={});var bW;(function(e){e.Parameters=W({code:V(),phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(bW||={});var xW;(function(e){e.Request=W({method:q(`health`),params:nU()}),e.Response=W({quoteSigner:X(),status:V(),version:V()})})(xW||={});var SW;(function(e){e.Parameters=W({address:X(),chainId:Q(),tokenAddress:X(),value:VU()}),e.Request=W({method:q(`wallet_addFaucetFunds`),params:Y(K([e.Parameters]))}),e.Response=W({message:J(V()),transactionHash:Z()})})(SW||={});var CW;(function(e){e.Parameters=W({chainId:Q(),id:Z()}),e.Request=W({method:q(`wallet_getAccounts`),params:Y(K([e.Parameters]))}),e.Response=Y(U(W({address:X(),keys:QU.Response})))})(CW||={});var wW;(function(e){e.Parameters=W({address:X()}),e.Request=W({method:q(`wallet_getAuthorization`),params:Y(K([e.Parameters]))}),e.Response=W({authorization:fW,data:Z(),to:X()})})(wW||={});var TW;(function(e){e.Request=W({method:q(`wallet_getCapabilities`),params:J(K([Y(U(H()))]))});let t=W({address:X(),version:J(G([V(),iU()]))});e.Response=xU(Z(),W({contracts:W({accountImplementation:t,accountProxy:t,legacyAccountImplementations:Y(U(t)),legacyOrchestrators:Y(U(G([W({orchestrator:t,simulator:t}),t]))),orchestrator:t,simulator:t}),fees:W({quoteConfig:W({constantRate:J(G([H(),iU()])),gas:J(W({intentBuffer:J(H()),txBuffer:J(H())})),rateTtl:H(),ttl:H()}),recipient:X(),tokens:Y(U(lW))})}))})(TW||={});var EW;(function(e){let t=G([q(`native`),q(`erc20`),q(`erc721`),V()]);e.Parameters=W({account:X(),assetFilter:J(xU(Z(),Y(U(W({address:G([X(),q(`native`)]),type:t}))))),assetTypeFilter:J(Y(U(t))),chainFilter:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getAssets`),params:Y(K([e.Parameters]))}),e.Price=W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})}),e.Response=xU(V(),Y(U(HU([W({address:X(),balance:VU(),metadata:TU(W({decimals:H(),fiat:EU(e.Price),name:V(),symbol:V()})),type:q(`erc20`)}),W({address:TU(q(`native`)),balance:VU(),metadata:TU(W({decimals:H(),fiat:EU(e.Price),name:J(V()),symbol:J(V())})),type:q(`native`)})]))))})(EW||={});var DW;(function(e){e.Request=W({method:q(`wallet_getCallsStatus`),params:Y(K([Z()]))}),e.Response=W({id:V(),receipts:J(Y(U(W({blockHash:Z(),blockNumber:Q(),chainId:Q(),gasUsed:Q(),logs:Y(U(W({address:X(),data:Z(),topics:Y(U(Z()))}))),status:Z(),transactionHash:Z()})))),status:H()})})(DW||={});var OW;(function(e){e.Parameters=W({address:X(),index:J(H()),limit:H(),sort:G([q(`asc`),q(`desc`)])}),e.Request=W({method:q(`wallet_getCallsHistory`),params:Y(K([e.Parameters]))}),e.Transaction=W({chainId:Q(),transactionHash:Z()}),e.Capabilities=W({assetDiffs:J(ZU.Response),feeTotals:J($U.Response),quotes:J(Y(U(sW)))}),e.Entry=W({capabilities:e.Capabilities,id:Z(),index:H(),keyHash:Z(),status:H(),timestamp:H(),transactions:Y(U(e.Transaction))}),e.Response=Y(U(e.Entry))})(OW||={});var kW;(function(e){e.Parameters=W({address:X(),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getKeys`),params:Y(K([e.Parameters]))}),e.Response=xU(Z(),QU.Response)})(kW||={});var AW;(function(e){e.Capabilities=W({authorizeKeys:J(QU.Request),meta:eW.Request,preCall:J(QH()),preCalls:J(Y(U(rW))),requiredFunds:J(tW.Request),revokeKeys:J(nW.Request)}),e.ResponseCapabilities=W({assetDiffs:J(ZU.Response),authorizeKeys:EU(QU.Response),feePayerDigest:J(Z()),feeSignature:J(Z()),feeTotals:J($U.Response),revokeKeys:EU(nW.Response)}),e.Parameters=W({calls:Y(U(pW)),capabilities:e.Capabilities,chainId:Q(),from:J(X()),key:J(W({prehash:QH(),publicKey:Z(),type:YU.shape.type}))}),e.Request=W({method:q(`wallet_prepareCalls`),params:Y(K([e.Parameters]))}),e.Response=W({capabilities:e.ResponseCapabilities,context:W({preCall:J(hU(iW)),quote:J(hU(cW))}),digest:Z(),key:EU(W({prehash:QH(),publicKey:Z(),type:YU.shape.type})),signature:Z(),typedData:W({domain:G([W({chainId:G([Q(),H()]),name:V(),verifyingContract:X(),version:V()}),W({})]),message:xU(V(),cU()),primaryType:V(),types:xU(V(),cU())})})})(AW||={});var jW;(function(e){e.Capabilities=W({authorizeKeys:QU.Request}),e.Parameters=W({address:X(),capabilities:e.Capabilities,chainId:J(H()),delegation:X()}),e.Request=W({method:q(`wallet_prepareUpgradeAccount`),params:Y(K([e.Parameters]))}),e.Response=W({capabilities:e.Capabilities,chainId:Q(),context:W({address:X(),authorization:dW,chainId:Q(),preCall:rW}),digests:W({auth:Z(),exec:Z()}),typedData:W({domain:G([W({chainId:G([Q(),H()]),name:V(),verifyingContract:X(),version:V()}),W({})]),message:xU(V(),cU()),primaryType:V(),types:xU(V(),cU())})})})(jW||={});var MW;(function(e){e.Request=W({method:q(`wallet_feeTokens`),params:J(nU())}),e.Response=xU(Z(),Y(U(W({address:X(),decimals:H(),nativeRate:J(VU()),symbol:V()}))))})(MW||={});var NW;(function(e){e.Parameters=W({capabilities:J(W({feeSignature:J(Z())})),context:W({preCall:J(hU(iW)),quote:J(hU(cW))}),key:J(W({prehash:QH(),publicKey:Z(),type:YU.shape.type})),signature:Z()}),e.Request=W({method:q(`wallet_sendPreparedCalls`),params:Y(K([e.Parameters]))}),e.Response=W({id:Z()})})(NW||={});var PW;(function(e){e.Parameters=W({context:W({address:X(),authorization:dW,chainId:Q(),preCall:rW}),signatures:W({auth:Z(),exec:Z()})}),e.Request=W({method:q(`wallet_upgradeAccount`),params:Y(K([e.Parameters]))}),e.Response=nU()})(PW||={});var FW;(function(e){e.Parameters=W({address:Z(),chainId:Q(),digest:Z(),signature:Z()}),e.Request=W({method:q(`wallet_verifySignature`),params:Y(K([e.Parameters]))}),e.Response=W({proof:EU(W({account:X(),initPreCall:EU(rW),keyHash:Z()})),valid:QH()})})(FW||={});async function IW(e,t){try{let n=`wallet_getAuthorization`,r=await Mm(()=>e.request({method:n,params:[RV(wW.Parameters,t)]}),{cacheKey:`${e.uid}.${n}.${t.address}`});return zV(wW.Response,r)}catch(e){throw $W(e),e}}async function LW(e,t={}){let n=(()=>{if(t.chainId)return[t.chainId];if(t.chainIds!==`all`)return t.chainIds?t.chainIds:[e.chain.id]})();try{let r=`wallet_getCapabilities`,i=await Mm(()=>e.request({method:r,params:n?[n]:void 0},{retryCount:0}),{cacheKey:`${e.uid}.${r}.${n?.join(`,`)}`}),a=t.raw?i:zV(TW.Response,i);return t.chainIds?a:Object.values(a)[0]}catch(e){throw $W(e),e}}async function RW(e,t){let{account:n,assetFilter:r,assetTypeFilter:i,chainFilter:a}=t;try{let t=await e.request({method:`wallet_getAssets`,params:[RV(EW.Parameters,{account:n,assetFilter:r,assetTypeFilter:i,chainFilter:a})]}),o=zV(EW.Response,t),s=Object.entries(o).reduce((e,[t,n])=>(e[sL(t)]=n,e),{}),c={};for(let e of Object.values(s))for(let t of e){let e=JSON.stringify(t.metadata);c[e]={...t,balance:t.balance+(c[e]?.balance??0n)}}return{...s,0:Object.values(c)}}catch(e){throw $W(e),e}}async function zW(e,t){let{id:n}=t;try{let t=await e.request({method:`wallet_getCallsStatus`,params:[n]});return zV(DW.Response,t)}catch(e){throw $W(e),e}}async function BW(e,t){try{let n=await e.request({method:`wallet_getCallsHistory`,params:[RV(OW.Parameters,t)]});return zV(OW.Response,n)}catch(e){throw $W(e),e}}async function VW(e,t){let{address:n,chainIds:r}=t;try{let t=await e.request({method:`wallet_getKeys`,params:[RV(kW.Parameters,{address:n,chainIds:r})]});return zV(kW.Response,t)}catch(e){throw $W(e),e}}async function HW(e){let t=`health`,n=await Mm(()=>e.request({method:t}),{cacheKey:`${e.uid}.${t}`});return zV(xW.Response,n)}async function UW(e,t){let{address:n,capabilities:r,chain:i=e.chain,key:a}=t,o=t.calls.map(e=>({data:e.abi?wz(Tz(e.abi,e.functionName),e.args):e.data??`0x`,to:e.to,value:e.value??0n}));try{let t=await e.request({method:`wallet_prepareCalls`,params:[RV(AW.Parameters,{calls:o,capabilities:{...r,meta:{...r?.meta}},chainId:i?.id,from:n,key:a?{prehash:a.prehash,publicKey:a.publicKey,type:a.type}:void 0})]},{retryCount:0});return Object.assign(zV(AW.Response,t),{_raw:t})}catch(e){throw $W(e),ZW(e,{calls:t.calls}),e}}async function WW(e,t){let{address:n,chain:r=e.chain,delegation:i,...a}=t;try{let t=await e.request({method:`wallet_prepareUpgradeAccount`,params:[RV(jW.Parameters,LB({address:n,capabilities:a,chainId:r?.id,delegation:i}))]},{retryCount:0});return zV(jW.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function GW(e,t){let{capabilities:n,context:r,key:i,signature:a}=t;try{let t=await e.request({method:`wallet_sendPreparedCalls`,params:[RV(NW.Parameters,{capabilities:n,context:{preCall:r.preCall,quote:r.quote},key:i?{prehash:i.prehash,publicKey:i.publicKey,type:i.type}:void 0,signature:a})]},{retryCount:0});return zV(NW.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function KW(e,t){let{email:n,walletAddress:r}=t;try{let t=await e.request({method:`account_setEmail`,params:[RV(_W.Parameters,{email:n,walletAddress:r})]},{retryCount:0});return zV(_W.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function qW(e,t){let{context:n,signatures:r}=t;try{await e.request({method:`wallet_upgradeAccount`,params:[RV(PW.Parameters,{context:n,signatures:r})]},{retryCount:0})}catch(e){throw $W(e),ZW(e),e}}async function JW(e,t){let{chainId:n,email:r,signature:i,token:a,walletAddress:o}=t;try{let t=await e.request({method:`account_verifyEmail`,params:[RV(yW.Parameters,{chainId:n,email:r,signature:i,token:a,walletAddress:o})]},{retryCount:0});return zV(yW.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function YW(e,t){let{signature:n}=t,{signature:r,capabilities:{feeSignature:i,...a},...o}=t.response,s=QW({capabilities:a,...o}),c=rR({payload:ML(eL(JSON.stringify(s))),signature:fR(n)}),{quoteSigner:l}=await HW(e);return c===l}async function XW(e,t){let{address:n,chain:r=e.chain,digest:i,signature:a}=t;try{async function t(){return{proof:null,valid:await vv(e,{address:n,hash:i,signature:a})}}let o=await(async()=>{let o=await e.request({method:`wallet_verifySignature`,params:[RV(FW.Parameters,{address:n,chainId:r?.id,digest:i,signature:a})]},{retryCount:0}).catch(t);return o.valid?o:t()})();return zV(FW.Response,o)}catch(e){throw $W(e),e}}function ZW(e,{calls:t}={}){if(!(e instanceof E))return;let n=e=>{try{if(e.name===`ContractFunctionExecutionError`){let t=e.cause.name===`ContractFunctionRevertedError`?e.cause.data:void 0;if(t)return PU([t.abiItem],t.errorName)}let t=e.walk(e=>!(e instanceof Error)&&e.code===3);if(!t)return;let{data:n,message:r}=t;return n===`0xd0d5039b`?NU(`error Unauthorized()`):{inputs:[],name:(r??n).split(`(`)[0],type:`error`}}catch{return}},r=BU(e,{calls:t??[]}),i=n(r);if(!(r===e&&!i))throw new eG(Object.assign(r,{abiError:i}))}function QW(e){if(typeof e==`object`&&e){if(Array.isArray(e))return e.map(QW);let t={};for(let n of Object.keys(e).sort())t[n]=QW(e[n]);return t}return e}function $W(e){if(e.name===`$ZodError`)throw WU(e)}var eG=class extends z{constructor(e){super(`An error occurred while executing calls.`,{cause:e,metaMessages:[e.abiError&&`Reason: `+e.abiError.name].filter(Boolean)}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Rpc.ExecutionError`}),Object.defineProperty(this,`abiError`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.abiError=e.abiError}},tG={anvil:{http:`http://localhost:9119`},prod:{http:`https://rpc.porto.sh`},stg:{http:`https://stg-rpc.porto.sh`}};function nG(e){return t=>{let n=e.public(t),r=e.relay(t);return qv({key:nG.type,name:`Relay Proxy`,async request({method:e,params:t},i){return rG(e)?r.request({method:e,params:t},i):n.request({method:e,params:t},i)},type:nG.type})}}(function(e){e.type=`relayProxy`})(nG||={});function rG(e){return!!(e.startsWith(`wallet_`)||e.startsWith(`account_`)||e===`health`)}var iG=new Map;function aG(e,t={}){let{config:n,id:r,store:i}=e._internal,{chains:a,relay:o}=n,s=i.getState(),c=t.chainId??s.chainIds[0],l=a.find(e=>e.id===c);if(!l)throw Error([`Could not find a compatible Porto chain on the given chain configuration.`,``,`Provided chains: [${a.map(e=>`${e.name} (id: ${e.id})`).join(`, `)}]`,`Needed chain (id): ${c}`,`Please add this chain (id) to your chain configuration.`].join(` +`));let u=nG({public:n.transports[l.id]??Yv(l.rpcUrls.default.http.map(e=>$v(e))),relay:o}),d=[r,ZF(l)].filter(Boolean).join(`:`);if(iG.has(d))return iG.get(d);let f=rh({...t,chain:l,pollingInterval:1e3,transport:u});return iG.set(d,f),f}var oG=W({chainId:J(Q()),expiry:Q(),hash:Z(),id:Z(),prehash:J(QH()),publicKey:Z(),role:G([q(`admin`),q(`session`)]),type:G([q(`address`),q(`p256`),q(`secp256k1`),q(`webauthn-p256`)])}),sG=Y(U(HU([W({signature:V(),to:X()}),W({signature:V()}),W({to:X()})])).check(KH(1))),cG=W({limit:G([MU([H(),`.`,H()]),MU([H()])]).check(qH(/^\d+(\.\d+)?$/)),symbol:J(G([q(`native`),uW]))}),lG=W({addresses:Y(U(X()))}),uG=Y(U(W({limit:VU(),period:G([q(`minute`),q(`hour`),q(`day`),q(`week`),q(`month`),q(`year`)]),token:J(X())}))),dG=W({calls:J(sG),signatureVerification:J(lG),spend:J(uG)}),fG=W({...oG.shape,feeToken:J(TU(cG)),permissions:J(dG)}),pG=W({address:X(),chainId:J(Q()),expiry:H(),id:Z(),key:pU(oG,{publicKey:!0,type:!0}),permissions:W({calls:sG,signatureVerification:J(lG),spend:J(uG)})}),mG=W({address:J(X()),chainId:J(Q()),expiry:H().check(GH(1)),feeToken:TU(cG),key:J(pU(oG,{publicKey:!0,type:!0})),permissions:W({calls:sG,signatureVerification:J(lG),spend:J(uG)})}),hG=pG;function gG(e,t){let{chainId:n,expiry:r,permissions:i,id:a,publicKey:o,type:s}=e,{address:c}=t;return{address:c,chainId:n,expiry:r,id:a,key:{publicKey:o,type:s},permissions:i??{}}}function _G(e){let{chainId:t,expiry:n,key:r}=e;return dB({chainId:t,expiry:n,permissions:e.permissions??{},publicKey:r.publicKey,role:`session`,type:r.type})}var vG;(function(e){e.GetCapabilitiesResponse=W({status:G([q(`supported`),q(`unsupported`)])})})(vG||={});var yG;(function(e){e.Request=G([QH(),W({chainId:J(Q()),label:J(V())})])})(yG||={});var bG;(function(e){e.Request=HU([W({chainId:J(H()),domain:J(V()),expirationTime:J(uU()),issuedAt:J(uU()),nonce:V(),notBefore:J(uU()),requestId:J(V()),resources:J(Y(U(V()))),scheme:J(V()),statement:J(V()),uri:J(V()),version:J(q(`1`))}),W({authUrl:G([V(),W({logout:V(),nonce:V(),verify:V()})]),chainId:J(Q()),domain:J(V()),expirationTime:J(uU()),issuedAt:J(uU()),notBefore:J(uU()),requestId:J(V()),resources:J(Y(U(V()))),scheme:J(V()),statement:J(V()),uri:J(V()),version:J(q(`1`))})]),e.Response=W({message:V(),signature:Z(),token:J(V())})})(bG||={});var xG;(function(e){e.GetCapabilitiesResponse=W({supported:QH(),tokens:Y(U(lW))}),e.Request=G([uW,X()])})(xG||={});var SG;(function(e){e.Request=mG})(SG||={});var CG;(function(e){e.GetCapabilitiesResponse=W({supported:QH()})})(CG||={});var wG;(function(e){e.GetCapabilitiesResponse=W({supported:QH()}),e.Request=W({id:J(G([Z(),iU()]))}),e.Response=Y(U(pG))})(wG||={});var TG;(function(e){e.Request=Y(U(W({context:cU(),signature:Z()}))),e.Response=e.Request})(TG||={});var EG;(function(e){e.Request=V()})(EG||={});var DG;(function(e){e.GetCapabilitiesResponse=W({supported:QH(),tokens:Y(U(lW))}),e.Request=Y(U(HU([W({address:X(),value:VU()}),W({symbol:uW,value:G([MU([H(),`.`,H()]),MU([H()])]).check(qH(/^\d+(\.\d+)?$/))})])))})(DG||={});var OG=W({...pU(oG,{id:!0,publicKey:!0,type:!0}).shape,credentialId:J(V()),privateKey:J(oU())}),kG;(function(e){e.Parameters=W({address:X(),secret:V()}),e.Request=W({method:q(`account_getOnrampContactInfo`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(V()),phone:J(V()),phoneVerifiedAt:J(H())})})(kG||={});var AG;(function(e){e.Parameters=W({address:X()}),e.Request=W({method:q(`account_onrampStatus`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(H()),phone:J(H())})})(AG||={});var jG;(function(e){e.Parameters=W({email:V(),walletAddress:X()}),e.Request=W({method:q(`account_resendVerifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(jG||={});var MG;(function(e){e.Parameters=W({email:V(),walletAddress:X()}),e.Request=W({method:q(`account_setEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(MG||={});var NG;(function(e){e.Parameters=W({email:V(),walletAddress:X()}),e.Request=W({method:q(`account_setPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(NG||={});var PG;(function(e){e.Parameters=W({chainId:Q(),email:V(),token:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(PG||={});var FG;(function(e){e.Parameters=W({code:V(),phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(FG||={});var IG;(function(e){e.Parameters=W({address:J(X()),chainId:J(Q()),token:J(X()),value:J(V())}),e.Request=W({method:q(`wallet_addFunds`),params:Y(K([e.Parameters]))}),e.Response=W({id:Z()})})(IG||={});var LG;(function(e){e.Request=W({method:q(`eth_accounts`),params:J(cU())}),e.Response=Y(U(X()))})(LG||={});var RG;(function(e){e.Request=W({method:q(`eth_chainId`),params:J(cU())}),e.Response=Z()})(RG||={});var zG;(function(e){e.Request=W({method:q(`eth_requestAccounts`),params:J(cU())}),e.Response=Y(U(X()))})(zG||={});var BG;(function(e){e.Request=W({method:q(`eth_sendTransaction`),params:Y(K([W({capabilities:J(W({feeToken:J(xG.Request),merchantUrl:J(EG.Request),preCalls:J(TG.Request)})),chainId:J(Q()),data:J(Z()),from:J(X()),to:X(),value:J(VU())})]))}),e.Response=Z()})(BG||={});var VG;(function(e){e.Request=W({method:q(`eth_signTypedData_v4`),params:Y(K([X(),V()]))}),e.Response=Z()})(VG||={});var HG;(function(e){e.Parameters=W({address:J(X()),chainId:J(Q())}),e.Request=W({method:q(`wallet_getAdmins`),params:J(Y(K([e.Parameters])))}),e.Key=OG,e.Response=W({address:X(),chainId:Q(),keys:Y(U(e.Key))})})(HG||={});var UG;(function(e){e.Capabilities=W({feeToken:J(xG.Request)}),e.Parameters=W({address:J(X()),capabilities:J(e.Capabilities),chainId:J(Q()),key:pU(oG,{publicKey:!0,type:!0})}),e.Request=W({method:q(`wallet_grantAdmin`),params:Y(K([e.Parameters]))}),e.Response=W({address:X(),chainId:Q(),key:HG.Key})})(UG||={});var WG;(function(e){e.Parameters=mG,e.Request=W({method:q(`wallet_grantPermissions`),params:Y(K([e.Parameters]))}),e.ResponseCapabilities=W({preCalls:J(TG.Response)}),e.Response=W({...pG.shape,capabilities:J(oU())})})(WG||={});var GG;(function(e){e.Parameters=W({address:J(X())}),e.Request=W({method:q(`wallet_getAccountVersion`),params:J(Y(K([e.Parameters])))}),e.Response=W({current:V(),latest:V()})})(GG||={});var KG;(function(e){e.Parameters=W({address:J(X()),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getPermissions`),params:J(Y(K([e.Parameters])))}),e.Response=wG.Response})(KG||={});var qG;(function(e){e.Capabilities=W({feeToken:J(xG.Request)}),e.Parameters=W({address:J(X()),capabilities:J(e.Capabilities),chainId:J(Q()),id:Z()}),e.Request=W({method:q(`wallet_revokeAdmin`),params:Y(K([e.Parameters]))}),e.Response=void 0})(qG||={});var JG;(function(e){e.Capabilities=W({feeToken:J(xG.Request)}),e.Parameters=W({address:J(X()),capabilities:J(e.Capabilities),id:Z()}),e.Request=W({method:q(`wallet_revokePermissions`),params:Y(K([e.Parameters]))}),e.Response=void 0})(JG||={});var YG;(function(e){e.Request=W({method:q(`wallet_switchEthereumChain`),params:Y(K([W({chainId:Z()})]))})})(YG||={});var XG;(function(e){e.Parameters=W({context:cU(),signatures:W({auth:Z(),exec:Z()})}),e.Request=W({method:q(`wallet_upgradeAccount`),params:Y(K([e.Parameters]))}),e.ResponseCapabilities=W({admins:J(Y(U(HG.Key))),permissions:J(wG.Response)}),e.Response=W({address:X(),capabilities:J(e.ResponseCapabilities)})})(XG||={});var ZG;(function(e){e.Request=W({method:q(`personal_sign`),params:Y(K([Z(),X()]))}),e.Response=Z()})(ZG||={});var QG;(function(e){e.Request=W({method:q(`porto_ping`),params:J(nU())}),e.Response=q(`pong`)})(QG||={});var $G;(function(e){e.Capabilities=W({createAccount:J(yG.Request),email:J(QH()),grantAdmins:J(Y(U(pU(oG,{publicKey:!0,type:!0})))),grantPermissions:J(SG.Request),preCalls:J(TG.Request),selectAccount:J(G([QH(),W({address:X(),key:J(W({credentialId:J(V()),publicKey:Z()}))})])),signInWithEthereum:J(bG.Request)}),e.Parameters=W({capabilities:J(e.Capabilities),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_connect`),params:J(Y(K([e.Parameters])))}),e.ResponseCapabilities=W({admins:J(Y(U(W({...pU(oG,{id:!0,publicKey:!0,type:!0}).shape,credentialId:J(V())})))),permissions:J(wG.Response),preCalls:J(TG.Response),signInWithEthereum:J(bG.Response)}),e.Response=W({accounts:Y(U(W({address:X(),capabilities:J(e.ResponseCapabilities)}))),chainIds:Y(U(Q()))})})($G||={});var eK;(function(e){e.Request=W({method:q(`wallet_disconnect`),params:J(cU())}),e.Response=void 0})(eK||={});var tK;(function(e){e.Parameters=EW.Parameters,e.Request=EW.Request,e.Response=EW.Response})(tK||={});var nK;(function(e){e.Request=W({method:q(`wallet_getCallsStatus`),params:K([Z()])}),e.Response=W({atomic:QH(),chainId:Q(),id:V(),receipts:J(Y(U(W({blockHash:Z(),blockNumber:Z(),gasUsed:Z(),logs:Y(U(W({address:X(),data:Z(),topics:Y(U(Z()))}))),status:Z(),transactionHash:Z()})))),status:H(),version:V()})})(nK||={});var rK;(function(e){e.Parameters=OW.Parameters,e.Request=OW.Request,e.Transaction=OW.Transaction,e.Capabilities=OW.Capabilities,e.Entry=OW.Entry,e.Response=OW.Response})(rK||={});var iK;(function(e){e.Request=W({method:q(`wallet_getCapabilities`),params:J(G([Y(K([G([Z(),nU()])])),Y(K([G([Z(),nU()]),Y(U(Q()))]))]))}),e.Response=xU(Z(),W({atomic:vG.GetCapabilitiesResponse,feeToken:xG.GetCapabilitiesResponse,merchant:CG.GetCapabilitiesResponse,permissions:wG.GetCapabilitiesResponse,requiredFunds:DG.GetCapabilitiesResponse}))})(iK||={});var aK;(function(e){e.Parameters=W({address:X(),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getKeys`),params:Y(K([e.Parameters]))}),e.Response=Y(U(fG))})(aK||={});var oK;(function(e){e.Capabilities=W({feeToken:J(xG.Request),merchantUrl:J(EG.Request),permissions:J(wG.Request),preCalls:J(TG.Request),requiredFunds:J(DG.Request)}),e.Parameters=W({calls:Y(U(W({data:J(Z()),to:X(),value:J(VU())}))),capabilities:J(e.Capabilities),chainId:J(Q()),from:J(X()),key:J(pU(oG,{prehash:!0,publicKey:!0,type:!0})),version:J(V())}),e.Request=W({method:q(`wallet_prepareCalls`),params:Y(K([e.Parameters]))}),e.Response=W({capabilities:J(W({...AW.ResponseCapabilities.shape,quote:J(cW)})),chainId:Z(),context:W({account:W({address:X()}),calls:e.Parameters.shape.calls,nonce:VU(),quote:J(hU(cW))}),digest:Z(),key:pU(oG,{prehash:!0,publicKey:!0,type:!0}),typedData:W({domain:G([W({chainId:Q(),name:V(),verifyingContract:X(),version:V()}),W({})]),message:xU(V(),cU()),primaryType:V(),types:xU(V(),cU())})})})(oK||={});var sK;(function(e){e.Capabilities=W({...$G.Capabilities.shape,label:J(V())}),e.Parameters=W({address:X(),capabilities:J(e.Capabilities),chainId:J(Q())}),e.Request=W({method:q(`wallet_prepareUpgradeAccount`),params:Y(K([e.Parameters]))}),e.Response=W({context:cU(),digests:W({auth:Z(),exec:Z()})})})(sK||={});var cK;(function(e){e.Capabilities=oK.Capabilities,e.Request=W({method:q(`wallet_sendCalls`),params:Y(K([mU(oK.Parameters,{key:!0})]))}),e.Response=W({id:Z()})})(cK||={});var lK;(function(e){e.Parameters=W({capabilities:oK.Response.shape.capabilities,chainId:Z(),context:oK.Response.shape.context,key:oK.Response.shape.key,signature:Z()}),e.Request=W({method:q(`wallet_sendPreparedCalls`),params:Y(K([e.Parameters]))}),e.Response=Y(U(W({capabilities:J(xU(V(),cU())),id:Z()})))})(lK||={});var uK;(function(e){e.Parameters=W({address:X(),chainId:J(Q()),digest:Z(),signature:Z()}),e.Request=W({method:q(`wallet_verifySignature`),params:Y(K([e.Parameters]))}),e.Response=W({address:X(),chainId:Q(),proof:J(cU()),valid:QH()})})(uK||={});var dK=vU(`method`,[PG.Request,IG.Request,LG.Request,RG.Request,zG.Request,BG.Request,VG.Request,GG.Request,HG.Request,KG.Request,UG.Request,WG.Request,sK.Request,qG.Request,JG.Request,XG.Request,ZG.Request,QG.Request,$G.Request,eK.Request,tK.Request,nK.Request,rK.Request,iK.Request,aK.Request,oK.Request,cK.Request,lK.Request,YG.Request,uK.Request]);function fK(e,t){let n=IV(e,t);if(n.error){let e=n.error.issues.at(0);throw e?.code===`invalid_union`&&e.note===`No matching discriminator`?new lI:new mI(WU(n.error))}return{...t,_decoded:n.data}}async function pK(e){e.persist.hasHydrated()||await new Promise(t=>{e.persist.onFinishHydration(()=>t(!0)),setTimeout(()=>t(!0),100)})}function mK(e){if(e)return e.startsWith(`/`)?`${window.location.origin}${e}`:e}function hK(e){let{config:t,getMode:n,id:r,store:i}=e,{announceProvider:a}=t;function o(e={}){let a=s(),o=e.request??fK(dK,{method:`wallet_getCapabilities`,params:e.chainIds?[void 0,e.chainIds]:void 0});return Mm(()=>n().actions.getCapabilities({chainIds:e.chainIds,internal:{client:a,config:t,request:o,store:i}}),{cacheKey:`getCapabilities.${r}.${e.chainIds?.join(`,`)}`})}function s(t){let n=typeof t==`string`?sL(t):t;return aG({_internal:e},{chainId:n})}let c=new Map,l=[],u=jI(),d=MI({...u,async request(e){return await pK(i),BB(async()=>{let r;try{r=fK(dK,e)}catch(t){let n=t;if(!(n instanceof lI))throw n;if(e.method.startsWith(`wallet_`))throw new bI;return s().request(e)}let a=i.getState();switch(r.method){case`account_verifyEmail`:{if(a.accounts.length===0)throw new xI;let[e]=r._decoded.params,{chainId:o,email:c,token:l,walletAddress:u}=e,d=s(o);if(o&&o!==d.chain.id)throw new SI;let f=u?a.accounts.find(e=>YL(e.address,u)):a.accounts[0];if(!f)throw new yI;return await n().actions.verifyEmail({account:f,chainId:o,email:c,internal:{client:d,config:t,request:r,store:i},token:l,walletAddress:u})}case`wallet_addFunds`:{let{address:e,value:o,token:c}=r.params[0]??{},l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0],d=s(),f=await n().actions.addFunds({address:l?.address,internal:{client:d,config:t,request:r,store:i},token:c,value:o});return u.emit(`message`,{data:null,type:`assetsChanged`}),f}case`eth_accounts`:if(a.accounts.length===0)throw new xI;return a.accounts.map(yK);case`eth_chainId`:return $I(a.chainIds[0]);case`eth_requestAccounts`:{if(a.accounts.length>0&&c.get(`eth_requestAccounts`))return a.accounts.map(yK);let e=s(),{accounts:o}=await n().actions.loadAccounts({internal:{client:e,config:t,request:r,store:i}});return i.setState(e=>({...e,accounts:o})),u.emit(`connect`,{chainId:$I(e.chain.id)}),c.set(`eth_requestAccounts`,!0),setTimeout(()=>c.delete(`eth_requestAccounts`),1e3),o.map(yK)}case`eth_sendTransaction`:{let[{capabilities:e,chainId:o,data:c=`0x`,from:l,to:u,value:d}]=r._decoded.params,f=s(o);if(o&&o!==f.chain.id)throw new SI;let p=l?a.accounts.find(e=>YL(e.address,l)):a.accounts[0];if(l&&!p)throw new yI;let{id:m}=await n().actions.sendCalls({account:p,asTxHash:!0,calls:[{data:c,to:u,value:d}],chainId:f.chain.id,internal:{client:f,config:t,request:r,store:i},merchantUrl:mK(t.merchantUrl??e?.merchantUrl)});return m}case`eth_signTypedData_v4`:{if(a.accounts.length===0)throw new xI;let[e,o]=r._decoded.params,c=a.accounts.find(t=>YL(t.address,e));if(!c)throw new yI;let l=s();return await n().actions.signTypedData({account:c,data:o,internal:{client:l,config:t,request:r,store:i}})}case`wallet_grantAdmin`:{if(a.accounts.length===0)throw new xI;let[{address:e,capabilities:o,chainId:c,key:l}]=r._decoded.params??[{}],d=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!d)throw new yI;let f=s(c);if(_K([...d.keys??[]])?.some(e=>e.publicKey?.toLowerCase()===l.publicKey.toLowerCase()))throw new mI({message:`Key already granted as admin.`});let{key:p}=await n().actions.grantAdmin({account:d,feeToken:o?.feeToken,internal:{client:f,config:t,request:r,store:i},key:l});i.setState(e=>{let t=e.accounts.findIndex(e=>d?YL(e.address,d.address):!0);return t===-1?e:{...e,accounts:e.accounts.map((e,n)=>n===t?{...e,keys:[...e.keys??[],p]}:e)}});let m=_K([...d.keys??[],p]);return u.emit(`message`,{data:null,type:`adminsChanged`}),RV(UG.Response,{address:d.address,chainId:f.chain.id,key:m.at(-1)})}case`wallet_grantPermissions`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainId:o,...c}]=r._decoded.params??[{}],l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!l)throw new yI;let d=s(o),{key:f}=await n().actions.grantPermissions({account:l,internal:{client:d,config:t,request:r,store:i},permissions:c});return i.setState(e=>{let t=e.accounts.findIndex(e=>l?YL(e.address,l.address):!0);return t===-1?e:{...e,accounts:e.accounts.map((e,n)=>n===t?{...e,keys:[...e.keys??[],f]}:e)}}),u.emit(`message`,{data:null,type:`permissionsChanged`}),RV(WG.Response,{...gG(f,{address:l.address})})}case`wallet_getAdmins`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainId:o}]=r._decoded.params??[{}],c=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!c)throw new yI;let l=s(o),u=_K(await n().actions.getKeys({account:c,internal:{client:l,config:t,request:r,store:i}}));return RV(HG.Response,{address:c.address,chainId:l.chain.id,keys:u})}case`wallet_prepareUpgradeAccount`:{let[{address:e,capabilities:a,chainId:o}]=r._decoded.params??[{}],{email:c,label:u,grantPermissions:d}=a??{},f=s(o),{context:p,digests:m}=await n().actions.prepareUpgradeAccount({address:e,email:c,internal:{client:f,config:t,request:r,store:i},label:u,permissions:d});return l.push(p.account),{context:p,digests:m}}case`wallet_getAccountVersion`:{if(a.accounts.length===0)throw new xI;let[{address:e}]=r._decoded.params??[{}],o=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!o)throw new yI;let c=s(),{current:l,latest:u}=await n().actions.getAccountVersion({address:o.address,internal:{client:c,config:t,request:r,store:i}});return{current:l,latest:u}}case`wallet_getKeys`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainIds:o}]=r._decoded.params??[{}],c=a.accounts.find(t=>YL(t.address,e));if(!c)throw new yI;let l=s(),u=await n().actions.getKeys({account:c,chainIds:o,internal:{client:l,config:t,request:r,store:i}});return RV(aK.Response,u)}case`wallet_getPermissions`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainIds:o}]=r._decoded.params??[{}],c=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!c)throw new yI;let l=s();return vK(await n().actions.getKeys({account:c,chainIds:o,internal:{client:l,config:t,request:r,store:i}}),{address:c.address})}case`wallet_revokeAdmin`:{if(a.accounts.length===0)throw new xI;let[{address:e,capabilities:o,id:c}]=r._decoded.params,l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!l)throw new yI;let d=s();await n().actions.revokeAdmin({account:l,feeToken:o?.feeToken,id:c,internal:{client:d,config:t,request:r,store:i}});let f=l.keys?.filter(e=>e.id.toLowerCase()!==c.toLowerCase());i.setState(e=>({...e,accounts:e.accounts.map(e=>YL(e.address,l.address)?{...e,keys:f}:e)})),u.emit(`message`,{data:null,type:`adminsChanged`});return}case`wallet_revokePermissions`:{if(a.accounts.length===0)throw new xI;let[{address:e,capabilities:o,id:c}]=r._decoded.params,l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!l)throw new yI;let d=s();await n().actions.revokePermissions({account:l,feeToken:o?.feeToken,id:c,internal:{client:d,config:t,request:r,store:i}});let f=l.keys?.filter(e=>e.id.toLowerCase()!==c.toLowerCase());i.setState(e=>({...e,accounts:e.accounts.map(e=>YL(e.address,l.address)?{...e,keys:f}:e)})),u.emit(`message`,{data:null,type:`permissionsChanged`});return}case`wallet_upgradeAccount`:{let[{context:e,signatures:a}]=r._decoded.params??[{}],o=s(),c=l.find(t=>YL(t.address,e.account.address));if(!c)throw new yI;let{account:d}=await n().actions.upgradeAccount({account:c,context:e,internal:{client:o,config:t,request:r,store:i},signatures:a}),f=_K(d.keys??[]),p=vK(d.keys??[],{address:d.address});return i.setState(e=>({...e,accounts:[d]})),u.emit(`connect`,{chainId:$I(o.chain.id)}),{address:d.address,capabilities:{admins:f,...p.length>0?{permissions:p}:{}}}}case`porto_ping`:return`pong`;case`personal_sign`:{if(a.accounts.length===0)throw new xI;let[e,o]=r._decoded.params,c=a.accounts.find(e=>YL(e.address,o));if(!c)throw new yI;let l=s();return await n().actions.signPersonalMessage({account:c,data:e,internal:{client:l,config:t,request:r,store:i}})}case`wallet_connect`:{let[{capabilities:e,chainIds:o}]=r._decoded.params??[{}],c=s(o?.[0]),l=c.chain.id,{createAccount:d,email:f,grantAdmins:p,grantPermissions:m,selectAccount:h,signInWithEthereum:g}=e??{},_={client:c,config:t,request:r,store:i},{accounts:v}=await(async()=>{if(f||d){let{label:e=void 0}=typeof d==`object`?d:{},{account:t}=await n().actions.createAccount({admins:p,email:f,internal:_,label:e,permissions:m,signInWithEthereum:g});return{accounts:[t]}}let e=a.accounts[0],{address:t,key:r}=(()=>{if(h)return typeof h==`object`?h:{address:void 0,key:void 0};for(let t of e?.keys??[])if(t.type===`webauthn-p256`&&t.role===`admin`)return{address:e?.address,key:{credentialId:t.credentialId??t.privateKey?.credential?.id,publicKey:t.publicKey}};return{address:void 0,key:void 0}})(),i={internal:_,permissions:m,signInWithEthereum:g};try{return await n().actions.loadAccounts({address:t,key:r,...i})}catch(e){if(e instanceof vI)throw e;if(t&&r)return await n().actions.loadAccounts(i);throw e}})();i.setState(e=>({...e,accounts:v}));let y=[l,...i.getState().chainIds.filter(e=>e!==l)];return u.emit(`connect`,{chainId:$I(y[0])}),{accounts:v.map(e=>({address:yK(e),capabilities:{admins:e.keys?_K(e.keys):[],permissions:e.keys?vK(e.keys,{address:e.address}):[],...e.signInWithEthereum&&{signInWithEthereum:e.signInWithEthereum}}})),chainIds:y.map(e=>$I(e))}}case`wallet_disconnect`:{let e=s();await n().actions.disconnect?.({internal:{client:e,config:t,request:r,store:i}}),i.setState(e=>({...e,accounts:[]})),u.emit(`disconnect`,new xI);return}case`wallet_getAssets`:{let[e]=r._decoded.params??[],{account:a,chainFilter:o,assetFilter:c,assetTypeFilter:l}=e,u=s(),d=await n().actions.getAssets({account:a,assetFilter:c,assetTypeFilter:l,chainFilter:o,internal:{client:u,config:t,request:r,store:i}}),f=Object.entries(d).reduce((e,[t,n])=>(e[$I(Number(t))]=n,e),{});return RV(tK.Response,f)}case`wallet_getCallsStatus`:{let[e]=r._decoded.params??[],a=s();return await n().actions.getCallsStatus({id:e,internal:{client:a,config:t,request:r,store:i}})}case`wallet_getCallsHistory`:{let[e]=r._decoded.params??[],a=s(),o=await n().actions.getCallsHistory({...e,internal:{client:a,config:t,request:r,store:i}});return RV(rK.Response,o)}case`wallet_getCapabilities`:{let[e,t]=r.params??[];return await o({chainIds:t,request:r})}case`wallet_prepareCalls`:{let[e]=r._decoded.params,{calls:o,capabilities:c,chainId:l,key:u,from:d}=e,f=s(l),p=d??a.accounts[0];if(!p)throw new yI;if(l&&l!==f.chain.id)throw new SI;let{digest:m,...h}=await n().actions.prepareCalls({account:TB(p),calls:o,feeToken:c?.feeToken,internal:{client:f,config:t,request:r,store:i},key:u,merchantUrl:mK(t.merchantUrl??c?.merchantUrl),requiredFunds:c?.requiredFunds});return RV(oK.Response,{capabilities:h.capabilities,chainId:$I(h.chainId??f.chain.id),context:{...h.context,account:{address:h.account.address},calls:h.context.calls??[],nonce:h.context.nonce??0n},digest:m,key:h.key,typedData:h.typedData})}case`wallet_sendPreparedCalls`:{let[e]=r._decoded.params,{chainId:a,context:o,key:c,signature:l}=e,{account:u}=e.context,d=s(a);if(a&&sL(a)!==d.chain.id)throw new SI;return[{id:await n().actions.sendPreparedCalls({account:TB(u),context:o,internal:{client:d,config:t,request:r,store:i},key:c,signature:l})}]}case`wallet_sendCalls`:{let[e]=r._decoded.params,{calls:o,capabilities:c,chainId:l,from:u}=e,d=s(l);if(l&&l!==d.chain.id)throw new SI;let f=u?a.accounts.find(e=>YL(e.address,u)):a.accounts[0];if(u&&!f)throw new yI;let{id:p}=await n().actions.sendCalls({account:f,calls:o,chainId:d.chain.id,feeToken:c?.feeToken,internal:{client:d,config:t,request:r,store:i},merchantUrl:mK(t.merchantUrl??c?.merchantUrl),permissionsId:c?.permissions?.id,requiredFunds:c?.requiredFunds});return{id:p}}case`wallet_switchEthereumChain`:{let[e]=r._decoded.params,{chainId:a}=e,o=sL(a);if(!t.chains.find(e=>e.id===o))throw new TI;let c=s(a);await n().actions.switchChain?.({chainId:c.chain.id,internal:{client:c,config:t,request:r,store:i}}),i.setState(e=>({...e,chainIds:[o,...e.chainIds.filter(e=>e!==o)]}));return}case`wallet_verifySignature`:{let[e]=r._decoded.params,{address:t,chainId:n,digest:i,signature:a}=e,o=s(n);return{...await XW(o,{address:t,digest:i,signature:a}),address:t,chainId:$I(o.chain.id)}}}},{enabled:[`eth_accounts`,`eth_chainId`,`eth_requestAccounts`,`wallet_getAssets`,`wallet_getCapabilities`,`wallet_getKeys`,`wallet_getPermissions`,`wallet_getAccountVersion`,`wallet_connect`].includes(e.method),id:ZF(e)})}});function f(){let e=()=>{},t=()=>{};pK(i).then(()=>{o().catch(()=>{}),e(),e=i.subscribe(e=>e.accounts,e=>{u.emit(`accountsChanged`,e.map(yK))},{equalityFn:(e,t)=>e.every((e,n)=>e.address===t[n]?.address)}),t(),t=i.subscribe(e=>e.chainIds[0],(e,t)=>{e!==t&&u.emit(`chainChanged`,$I(e))})});let n=gK(d,a);return()=>{e(),t(),n()}}let p=f();return Object.assign(d,{_internal:{destroy:p}})}function gK(e,t){if(!t||typeof window>`u`||!window.dispatchEvent)return()=>{};let{icon:n=`data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIyIiBoZWlnaHQ9IjQyMiIgdmlld0JveD0iMCAwIDQyMiA0MjIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI0MjIiIGhlaWdodD0iNDIyIiBmaWxsPSJibGFjayIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMV8xNSkiPgo8cGF0aCBkPSJNODEgMjg2LjM2NkM4MSAyODAuODkzIDg1LjQ1MDUgMjc2LjQ1NSA5MC45NDA0IDI3Ni40NTVIMzI5LjUxMUMzMzUuMDAxIDI3Ni40NTUgMzM5LjQ1MiAyODAuODkzIDMzOS40NTIgMjg2LjM2NlYzMDYuMTg4QzMzOS40NTIgMzExLjY2MiAzMzUuMDAxIDMxNi4wOTkgMzI5LjUxMSAzMTYuMDk5SDkwLjk0MDRDODUuNDUwNSAzMTYuMDk5IDgxIDMxMS42NjIgODEgMzA2LjE4OFYyODYuMzY2WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC41Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTAuOTQwNCAyMzQuODI4Qzg1LjQ1MDUgMjM0LjgyOCA4MSAyMzkuMjY2IDgxIDI0NC43MzlWMjcxLjUzMUM4My44NDMyIDI2OS42MzMgODcuMjYyMiAyNjguNTI2IDkwLjk0MDQgMjY4LjUyNkgzMjkuNTExQzMzMy4xODggMjY4LjUyNiAzMzYuNjA4IDI2OS42MzMgMzM5LjQ1MiAyNzEuNTMxVjI0NC43MzlDMzM5LjQ1MiAyMzkuMjY2IDMzNS4wMDEgMjM0LjgyOCAzMjkuNTExIDIzNC44MjhIOTAuOTQwNFpNMzM5LjQ1MiAyODYuMzY2QzMzOS40NTIgMjgwLjg5MyAzMzUuMDAxIDI3Ni40NTUgMzI5LjUxMSAyNzYuNDU1SDkwLjk0MDRDODUuNDUwNSAyNzYuNDU1IDgxIDI4MC44OTMgODEgMjg2LjM2NlYzMDYuMTlDODEgMzExLjY2NCA4NS40NTA1IDMxNi4xMDEgOTAuOTQwNCAzMTYuMTAxSDMyOS41MTFDMzM1LjAwMSAzMTYuMTAxIDMzOS40NTIgMzExLjY2NCAzMzkuNDUyIDMwNi4xOVYyODYuMzY2WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC41Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTAuOTQwNCAxOTMuMjAxQzg1LjQ1MDUgMTkzLjIwMSA4MSAxOTcuNjM4IDgxIDIwMy4xMTJWMjI5LjkwM0M4My44NDMyIDIyOC4wMDYgODcuMjYyMiAyMjYuODk5IDkwLjk0MDQgMjI2Ljg5OUgzMjkuNTExQzMzMy4xODggMjI2Ljg5OSAzMzYuNjA4IDIyOC4wMDYgMzM5LjQ1MiAyMjkuOTAzVjIwMy4xMTJDMzM5LjQ1MiAxOTcuNjM4IDMzNS4wMDEgMTkzLjIwMSAzMjkuNTExIDE5My4yMDFIOTAuOTQwNFpNMzM5LjQ1MiAyNDQuNzM5QzMzOS40NTIgMjM5LjI2NSAzMzUuMDAxIDIzNC44MjggMzI5LjUxMSAyMzQuODI4SDkwLjk0MDRDODUuNDUwNSAyMzQuODI4IDgxIDIzOS4yNjUgODEgMjQ0LjczOVYyNzEuNTNDODEuMjE3NSAyNzEuMzg1IDgxLjQzODMgMjcxLjI0NSA4MS42NjI0IDI3MS4xMDlDODMuODMyNSAyNjkuNzk0IDg2LjMwNTQgMjY4LjkyNyA4OC45NTIzIDI2OC42MzVDODkuNjA1MSAyNjguNTYzIDkwLjI2ODQgMjY4LjUyNiA5MC45NDA0IDI2OC41MjZIMzI5LjUxMUMzMzAuMTgzIDI2OC41MjYgMzMwLjg0NiAyNjguNTYzIDMzMS40OTggMjY4LjYzNUMzMzQuNDE5IDI2OC45NTcgMzM3LjEyOCAyNjkuOTggMzM5LjQ1MiAyNzEuNTNWMjQ0LjczOVpNMzM5LjQ1MiAyODYuMzY2QzMzOS40NTIgMjgxLjAyMSAzMzUuMjA2IDI3Ni42NjMgMzI5Ljg5MyAyNzYuNDYyQzMyOS43NjcgMjc2LjQ1NyAzMjkuNjQgMjc2LjQ1NSAzMjkuNTExIDI3Ni40NTVIOTAuOTQwNEM4NS40NTA1IDI3Ni40NTUgODEgMjgwLjg5MyA4MSAyODYuMzY2VjMwNi4xODhDODEgMzExLjY2MiA4NS40NTA1IDMxNi4xMDEgOTAuOTQwNCAzMTYuMTAxSDMyOS41MTFDMzM1LjAwMSAzMTYuMTAxIDMzOS40NTIgMzExLjY2MiAzMzkuNDUyIDMwNi4xODhWMjg2LjM2NloiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNSIvPgo8cGF0aCBvcGFjaXR5PSIwLjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTguMDE0NiAxMDRDODguNjE3NyAxMDQgODEgMTExLjU5NSA4MSAxMjAuOTY1VjE4OC4yNzZDODMuODQzMiAxODYuMzc5IDg3LjI2MjIgMTg1LjI3MiA5MC45NDA0IDE4NS4yNzJIMzI5LjUxMUMzMzMuMTg4IDE4NS4yNzIgMzM2LjYwOCAxODYuMzc5IDMzOS40NTIgMTg4LjI3NlYxMjAuOTY1QzMzOS40NTIgMTExLjU5NSAzMzEuODMzIDEwNCAzMjIuNDM3IDEwNEg5OC4wMTQ2Wk0zMzkuNDUyIDIwMy4xMTJDMzM5LjQ1MiAxOTcuNjM4IDMzNS4wMDEgMTkzLjIwMSAzMjkuNTExIDE5My4yMDFIOTAuOTQwNEM4NS40NTA1IDE5My4yMDEgODEgMTk3LjYzOCA4MSAyMDMuMTEyVjIyOS45MDNDODEuMjE3NSAyMjkuNzU4IDgxLjQzODMgMjI5LjYxOCA4MS42NjI0IDIyOS40ODJDODMuODMyNSAyMjguMTY3IDg2LjMwNTQgMjI3LjMgODguOTUyMyAyMjcuMDA4Qzg5LjYwNTEgMjI2LjkzNiA5MC4yNjg0IDIyNi44OTkgOTAuOTQwNCAyMjYuODk5SDMyOS41MTFDMzMwLjE4MyAyMjYuODk5IDMzMC44NDYgMjI2LjkzNiAzMzEuNDk4IDIyNy4wMDhDMzM0LjQxOSAyMjcuMzMgMzM3LjEyOCAyMjguMzUyIDMzOS40NTIgMjI5LjkwM1YyMDMuMTEyWk0zMzkuNDUyIDI0NC43MzlDMzM5LjQ1MiAyMzkuMzkzIDMzNS4yMDYgMjM1LjAzNiAzMjkuODkzIDIzNC44MzVDMzI5Ljc2NyAyMzQuODMgMzI5LjY0IDIzNC44MjggMzI5LjUxMSAyMzQuODI4SDkwLjk0MDRDODUuNDUwNSAyMzQuODI4IDgxIDIzOS4yNjUgODEgMjQ0LjczOVYyNzEuNTNMODEuMDcwNyAyNzEuNDgzQzgxLjI2NTMgMjcxLjM1NSA4MS40NjI1IDI3MS4yMyA4MS42NjI0IDI3MS4xMDlDODEuOTA4MyAyNzAuOTYgODIuMTU4MSAyNzAuODE3IDgyLjQxMTcgMjcwLjY3OUM4NC4zOTUzIDI2OS42MDUgODYuNjA1NCAyNjguODk0IDg4Ljk1MjMgMjY4LjYzNUM4OS4wMDUyIDI2OC42MjkgODkuMDU4IDI2OC42MjQgODkuMTExIDI2OC42MThDODkuNzEyNSAyNjguNTU3IDkwLjMyMjggMjY4LjUyNiA5MC45NDA0IDI2OC41MjZIMzI5LjUxMUMzMjkuNzM4IDI2OC41MjYgMzI5Ljk2NSAyNjguNTMgMzMwLjE5MiAyNjguNTM5QzMzMC42MzEgMjY4LjU1NSAzMzEuMDY3IDI2OC41ODcgMzMxLjQ5OCAyNjguNjM1QzMzNC40MTkgMjY4Ljk1NyAzMzcuMTI4IDI2OS45OCAzMzkuNDUyIDI3MS41M1YyNDQuNzM5Wk0zMzkuNDUyIDI4Ni4zNjZDMzM5LjQ1MiAyODEuMDIxIDMzNS4yMDYgMjc2LjY2MyAzMjkuODkzIDI3Ni40NjJMMzI5Ljg2NSAyNzYuNDYxQzMyOS43NDggMjc2LjQ1NyAzMjkuNjI5IDI3Ni40NTUgMzI5LjUxMSAyNzYuNDU1SDkwLjk0MDRDODUuNDUwNSAyNzYuNDU1IDgxIDI4MC44OTMgODEgMjg2LjM2NlYzMDYuMTg4QzgxIDMxMS42NjIgODUuNDUwNSAzMTYuMTAxIDkwLjk0MDQgMzE2LjEwMUgzMjkuNTExQzMzNS4wMDEgMzE2LjEwMSAzMzkuNDUyIDMxMS42NjIgMzM5LjQ1MiAzMDYuMTg4VjI4Ni4zNjZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjY5Ljg2OCAxMzEuNzUyQzI2OS44NjggMTI2LjI3OCAyNzQuMzE4IDEyMS44NCAyNzkuODA4IDEyMS44NEgzMTEuNjE4QzMxNy4xMDggMTIxLjg0IDMyMS41NTggMTI2LjI3OCAzMjEuNTU4IDEzMS43NTJWMTYxLjQ4NUMzMjEuNTU4IDE2Ni45NTkgMzE3LjEwOCAxNzEuMzk2IDMxMS42MTggMTcxLjM5NkgyNzkuODA4QzI3NC4zMTggMTcxLjM5NiAyNjkuODY4IDE2Ni45NTkgMjY5Ljg2OCAxNjEuNDg1VjEzMS43NTJaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzFfMTUiPgo8cmVjdCB3aWR0aD0iMjU5IiBoZWlnaHQ9IjIxMyIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgxIDEwNCkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K`,name:r=`Porto`,rdns:i=`xyz.ithaca.porto`}=typeof t==`object`?t:{};return nV({info:{icon:n,name:r,rdns:i,uuid:zB()},provider:e})}function _K(e){return e.map(e=>{if(e.role===`admin`)try{return RV(HG.Key,{id:e.id??e.publicKey,publicKey:e.publicKey,type:e.type,...e.type===`webauthn-p256`?{credentialId:e.privateKey?.credential?.id,privateKey:{credential:{id:e.privateKey?.credential?.id},rpId:e.privateKey?.rpId}}:{}})}catch{return}}).filter(Boolean)}function vK(e,{address:t}){return e.map(e=>{if(e.chainId&&e.role===`session`&&!(e.expiry>0&&e.expiry()=>{})}}async function xK(e){let{account:t,calls:n,permissionsId:r}=e;if(r!==void 0){if(r===null)return;let e=t.keys?.find(e=>e.publicKey===r&&e.privateKey);if(!e)throw Error(`permission (id: ${r}) does not exist.`);return e}let i=t.keys?.find(e=>!e.privateKey||e.role!==`session`||e.expirye.permissions?.calls?.some(e=>{if(e.to&&e.to!==t.to)return!1;if(e.signature){if(!t.data)return!1;let n=rL(t.data,0,4);if(cL(e.signature))return e.signature===n;if(yz(e.signature)!==n)return!1}return!0}))),a=t.keys?.find(e=>e.role===`admin`&&e.privateKey);return i??a}function SK(e={}){let t=e.id??0;return{prepare(e){return CK({id:t++,...e})},get id(){return t}}}function CK(e){return{...e,jsonrpc:`2.0`}}function wK(){return null}var TK=mG;function EK(e){let{expiry:t,feeToken:n,permissions:r,publicKey:i,type:a}=e;return{expiry:t,feeToken:n??null,key:{publicKey:i,type:a},permissions:r??{}}}async function DK(e,t={}){if(!e)return;let n={chainId:t.chainId??e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,permissions:xB(e,{feeTokens:t.feeTokens}),role:`session`};if(e?.key)return dB({...n,publicKey:e.key.publicKey,type:e.key.type??`secp256k1`});if(typeof globalThis.crypto?.subtle?.generateKey==`function`)try{return await uB(n)}catch(e){if(!OK(e))throw e}return sB(n)}function OK(e){if(!(e instanceof Error))return!1;let t=e.message?.toLowerCase()??``;return e.name===`TypeError`||e.name===`ReferenceError`||t.includes(`subtle`)||t.includes(`generatekey`)}var kK=/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:[0-9]{1,5})?$/,AK=/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:[0-9]{1,5})?$/,jK=/^localhost(:[0-9]{1,5})?$/,MK=/^[a-zA-Z0-9]{8,}$/,NK=/^([a-zA-Z][a-zA-Z0-9+-.]*)$/,PK=/^(?:(?[a-zA-Z][a-zA-Z0-9+-.]*):\/\/)?(?[a-zA-Z0-9+-.]*(?::[0-9]{1,5})?) (?:wants you to sign in with your Ethereum account:\n)(?
0x[a-fA-F0-9]{40})\n\n(?:(?.*)\n\n)?/,FK=/(?:URI: (?.+))\n(?:Version: (?.+))\n(?:Chain ID: (?\d+))\n(?:Nonce: (?[a-zA-Z0-9]+))\n(?:Issued At: (?.+))(?:\nExpiration Time: (?.+))?(?:\nNot Before: (?.+))?(?:\nRequest ID: (?.+))?/;function IK(e){let{chainId:t,domain:n,expirationTime:r,issuedAt:i=new Date,nonce:a,notBefore:o,requestId:s,resources:c,scheme:l,uri:u,version:d}=e;{if(t!==Math.floor(t))throw new BK({field:`chainId`,metaMessages:[`- Chain ID must be a EIP-155 chain ID.`,`- See https://eips.ethereum.org/EIPS/eip-155`,``,`Provided value: ${t}`]});if(!(kK.test(n)||AK.test(n)||jK.test(n)))throw new BK({field:`domain`,metaMessages:[`- Domain must be an RFC 3986 authority.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${n}`]});if(!MK.test(a))throw new BK({field:`nonce`,metaMessages:[`- Nonce must be at least 8 characters.`,`- Nonce must be alphanumeric.`,``,`Provided value: ${a}`]});if(!LK(u))throw new BK({field:`uri`,metaMessages:[`- URI must be a RFC 3986 URI referring to the resource that is the subject of the signing.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${u}`]});if(d!==`1`)throw new BK({field:`version`,metaMessages:[`- Version must be '1'.`,``,`Provided value: ${d}`]});if(l&&!NK.test(l))throw new BK({field:`scheme`,metaMessages:[`- Scheme must be an RFC 3986 URI scheme.`,`- See https://www.rfc-editor.org/rfc/rfc3986#section-3.1`,``,`Provided value: ${l}`]});let r=e.statement;if(r?.includes(` +`))throw new BK({field:`statement`,metaMessages:[`- Statement must not include '\\n'.`,``,`Provided value: ${r}`]})}let f=qL(e.address,{checksum:!0}),p=`${l?`${l}://${n}`:n} wants you to sign in with your Ethereum account:\n${f}\n\n${e.statement?`${e.statement}\n`:``}`,m=`URI: ${u}\nVersion: ${d}\nChain ID: ${t}\nNonce: ${a}\nIssued At: ${i.toISOString()}`;if(r&&(m+=`\nExpiration Time: ${r.toISOString()}`),o&&(m+=`\nNot Before: ${o.toISOString()}`),s&&(m+=`\nRequest ID: ${s}`),c){let e=` +Resources:`;for(let t of c){if(!LK(t))throw new BK({field:`resources`,metaMessages:[`- Every resource must be a RFC 3986 URI.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${t}`]});e+=`\n- ${t}`}m+=e}return`${p}\n${m}`}function LK(e){if(/[^a-z0-9:/?#[\]@!$&'()*+,;=.\-_~%]/i.test(e)||/%[^0-9a-f]/i.test(e)||/%[0-9a-f](:?[^0-9a-f]|$)/i.test(e))return!1;let t=RK(e),n=t[1],r=t[2],i=t[3],a=t[4],o=t[5];if(!(n?.length&&i&&i.length>=0))return!1;if(r?.length){if(!(i.length===0||/^\//.test(i)))return!1}else if(/^\/\//.test(i))return!1;if(!/^[a-z][a-z0-9+\-.]*$/.test(n.toLowerCase()))return!1;let s=``;return s+=`${n}:`,r?.length&&(s+=`//${r}`),s+=i,a?.length&&(s+=`?${a}`),o?.length&&(s+=`#${o}`),s}function RK(e){return e.match(/(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/)}function zK(e){let{scheme:t,statement:n,...r}=e.match(PK)?.groups??{},{chainId:i,expirationTime:a,issuedAt:o,notBefore:s,requestId:c,...l}=e.match(FK)?.groups??{},u=e.split(`Resources:`)[1]?.split(` +- `).slice(1);return{...r,...l,...i?{chainId:Number(i)}:{},...a?{expirationTime:new Date(a)}:{},...o?{issuedAt:new Date(o)}:{},...s?{notBefore:new Date(s)}:{},...c?{requestId:c}:{},...u?{resources:u}:{},...t?{scheme:t}:{},...n?{statement:n}:{}}}var BK=class extends z{constructor(e){let{field:t,metaMessages:n}=e;super(`Invalid Sign-In with Ethereum message field "${t}".`,{metaMessages:n}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Siwe.InvalidMessageFieldError`})}};async function VK(e){let{address:t,authUrl:n,message:r,signature:i,publicKey:a}=e,{chainId:o}=zK(r);return await fetch(n.verify,{body:JSON.stringify({address:t,chainId:o,message:r,signature:i,walletAddress:t,...a&&{publicKey:a}}),credentials:`include`,headers:{"Content-Type":`application/json`},method:`POST`}).then(e=>e.json())}async function HK(e,t,n){let{chainId:r=e.chain?.id,domain:i,uri:a,resources:o,version:s=`1`}=t,{address:c}=n,l=t.authUrl?UK(t.authUrl):void 0;if(!r)throw Error("`chainId` is required.");if(!i)throw Error("`domain` is required.");if(!t.nonce&&!l?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");if(!a)throw Error("`uri` is required.");let u=await(async()=>{if(t.nonce)return t.nonce;if(!l?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");let e=await(await fetch(l.nonce,{body:JSON.stringify({address:c,chainId:r,walletAddress:c}),headers:{"Content-Type":`application/json`},method:`POST`})).json().catch(()=>void 0);if(!e?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");return e.nonce})();return IK({...t,address:n.address,chainId:r,domain:i,nonce:u,resources:o,uri:a,version:s})}function UK(e,t=``){if(!e)return;let n=(()=>{if(typeof e==`string`){let t=e.replace(/\/$/,``);return{logout:t+`/logout`,nonce:t+`/nonce`,verify:t+`/verify`}}return e})();return{logout:WK(n.logout,t),nonce:WK(n.nonce,t),verify:WK(n.verify,t)}}function WK(e,t){return!t||!e.startsWith(`/`)?e:t+e}function GK(e){let t=XI(e);return YI(`0x19`,eL(`Ethereum Signed Message: +`+iL(t)),t)}function KK(e){return ML(GK(e))}function qK(e,t){let{as:n}=t,r=YK(e),i=zR(new Uint8Array(r.length));return r.encode(i),n===`Hex`?QI(i.bytes):i.bytes}function JK(e,t={}){let{as:n=`Hex`}=t;return qK(e,{as:n})}function YK(e){return Array.isArray(e)?XK(e.map(e=>YK(e))):ZK(e)}function XK(e){let t=e.reduce((e,t)=>e+t.length,0),n=QK(t);return{length:t<=55?1+t:1+n+t,encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function ZK(e){let t=typeof e==`string`?yL(e):e,n=QK(t.length);return{length:t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length,encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function QK(e){if(e<=255)return 1;if(e<=65535)return 2;if(e<=16777215)return 3;if(e<=4294967295)return 4;throw new z(`Length is too large.`)}function $K(e){return eq(e,{presign:!0})}function eq(e,t={}){let{presign:n}=t;return ML(YI(`0x05`,JK(tq(n?{address:e.address,chainId:e.chainId,nonce:e.nonce}:e))))}function tq(e){let{address:t,chainId:n,nonce:r}=e,i=pR(e);return[n?$I(n):`0x`,t,r?$I(r):`0x`,...i?vR(i):[]]}async function nq(e,t){let{account:n=e.account}=t,r=n?TB(n):void 0;if(!r)throw Error(`account is required.`);let{domain:{name:i,version:a}}=await oh(e,{address:r.address});if(!e.chain)throw Error(`client.chain is required`);return{chainId:e.chain.id,name:i,verifyingContract:r.address,version:a}}async function rq(e,t){let{account:n=e.account,chainIds:r}=t,i=n?TB(n):void 0;if(!i)throw Error(`account is required.`);let a=await VW(e,{address:i.address,chainIds:r});return Object.entries(a).flatMap(([e,t])=>t.map(t=>pB(t,{chainId:Number(e)})))}async function iq(e,t){let{account:n=e.account,calls:r,chain:i=e.chain,feePayer:a,merchantUrl:o,nonce:s,preCalls:c,requiredFunds:l,revokeKeys:u}=t,d=n?TB(n):void 0,f=t.key??(d?DB(d,{role:`admin`}):void 0),p=t.authorizeKeys?.some(e=>e.role===`session`),{contracts:m,fees:{tokens:h}}=await LW(e,{chainId:i?.id}),g=p?m.orchestrator.address:void 0,_=(t.authorizeKeys??[]).map(e=>bB(e,{feeTokens:h,orchestrator:g})),v=t.feeToken?t.feeToken:f?.permissions?.spend?.[0]?.token,y=typeof c==`boolean`?c:!1,b=typeof c==`object`?c.map(({context:e,signature:t})=>({...e.preCall,signature:t})):void 0,x={address:d?.address,calls:r??[],capabilities:{authorizeKeys:_,meta:{feePayer:a,feeToken:v,nonce:s},preCall:y,preCalls:b,requiredFunds:l,revokeKeys:u?.map(e=>({hash:e.hash}))},chain:i,key:f?bB(f,{feeTokens:h}):void 0},S=await(async()=>o?await UW(rh({chain:e.chain,transport:$v(o)}),x).catch(t=>(console.error(t),UW(e,x))):await UW(e,x))(),{capabilities:ee,context:C,digest:te,signature:ne,typedData:re}=S;if(o&&!await YW(e,{response:S._raw,signature:ne}))throw Error(`cannot verify integrity of \`wallet_prepareCalls\` response from ${o}`);return{capabilities:{...ee,quote:C.quote},context:C,digest:te,key:f,typedData:re}}async function aq(e,t){let{address:n,authorizeKeys:r,chain:i=e.chain}=t;if(!i)throw Error(`chain is required.`);let{contracts:a,fees:{tokens:o}}=await LW(e,{chainId:i.id}),s=t.delegation??a.accountProxy.address,c=r.some(e=>e.role===`session`)?a.orchestrator.address:void 0,{capabilities:l,chainId:u,context:d,digests:f,typedData:p}=await WW(e,{address:n,authorizeKeys:r.map(e=>{let t=e.role===`session`?e.permissions:{};return bB({...e,permissions:t},{feeTokens:o,orchestrator:c})}),chain:i,delegation:s}),m=TB({address:n,keys:r});return{capabilities:l,chainId:u,context:{...d,account:m},digests:f,typedData:p}}async function oq(e,t){let{account:n=e.account,chain:r=e.chain,webAuthn:i}=t;if(!r)throw Error("`chain` is required.");let a=n?TB(n):void 0;if(!a)throw Error("`account` is required.");let o=t.key??DB(a,t);if(!o&&!a.sign)throw Error("`key` or `account` with `sign` is required");let s=await Promise.all((t.preCalls??[]).map(async n=>{if(n.signature)return n;let{authorizeKeys:o,key:s,calls:c,revokeKeys:l}=n,{context:u,digest:d}=await iq(e,{account:a,authorizeKeys:o,calls:c,chain:r,feeToken:t.feeToken,key:s,preCalls:!0,revokeKeys:l});return{context:u,signature:await yB(s,{address:null,payload:d,webAuthn:i})}})),{capabilities:c,context:l,digest:u}=await iq(e,{...t,account:a,chain:r,key:o,preCalls:s}),d=await(async()=>o?await yB(o,{address:null,payload:u,webAuthn:i,wrap:!1}):await a.sign({hash:u}))();return await sq(e,{capabilities:c.feeSignature?{feeSignature:c.feeSignature}:void 0,context:l,key:o,signature:d})}async function sq(e,t){let{capabilities:n,context:r,key:i,signature:a}=t;return await GW(e,{capabilities:n,context:r,key:i?bB(i):void 0,signature:a})}async function cq(e,t){let{email:n,walletAddress:r}=t;return await KW(e,{email:n,walletAddress:r})}async function lq(e,t){if(t.account){let{account:n}=t,r=[...n.keys??[],...t.authorizeKeys??[]].filter((e,t,n)=>n.findIndex(t=>t.id===e.id)===t),{digests:i,...a}=await aq(e,{...t,address:n.address,authorizeKeys:r}),o={auth:await n.sign({hash:i.auth}),exec:await n.sign({hash:i.exec})};return await lq(e,{...a,signatures:o})}let{context:n,signatures:r}=t,i=TB(n.account);return await qW(e,{context:n,signatures:r}),i}async function uq(e,t){let{chainId:n,email:r,signature:i,token:a,walletAddress:o}=t;return await JW(e,{chainId:n,email:r,signature:i,token:a,walletAddress:o})}var dq=`0x8010801080108010801080108010801080108010801080108010801080108010`,fq=GR(`(uint256 chainId, address delegation, uint256 nonce, uint8 yParity, uint256 r, uint256 s), address to, bytes data`);function pq(e){if(typeof e==`string`){if(rL(e,-32)!==`0x8010801080108010801080108010801080108010801080108010801080108010`)throw new hq(e)}else uR(e.authorization)}function mq(e){let{data:t,signature:n}=e;pq(e);let r=rR({payload:$K(e.authorization),signature:mR(e.authorization)}),i=UR(fq,[{...e.authorization,delegation:e.authorization.address,chainId:BigInt(e.authorization.chainId)},e.to??r,t??`0x`]);return YI(n,i,$I(iL(i),{size:32}),dq)}var hq=class extends z{constructor(e){super(`Value \`${e}\` is an invalid ERC-8010 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc8010.InvalidWrappedSignatureError`})}};async function gq(e,t){let{address:n}=t,{authorization:r,data:i,to:a}=await IW(e,{address:n});return mq({authorization:{...r,nonce:BigInt(r.nonce),r:BigInt(r.r),s:BigInt(r.s)},data:i,signature:t.signature,to:a})}function _q(e,t){let{tokens:n}=t,r=n.filter(e=>e.interop);return e.map(e=>{if(e.address)return e;let t=r.find(t=>t.symbol===e.symbol);if(!t)throw Error(`interop token not found: ${e.symbol}`);return{address:t.address,value:Az(e.value,t.decimals)}})}async function vq(e,t){let{chain:n=e.chain}=t??{};return await LW(e,{chainId:n?.id}).then(e=>e.fees.tokens)}async function yq(e,t){let{addressOrSymbol:n}=t;return(await vq(e,t)).find(yq.predicate(n))}(function(e){function t(e){return t=>e?XL(e)?YL(t.address,e):e===`native`?t.address===dv:e===t.symbol:!1}e.predicate=t})(yq||={});async function bq(e,t){let{chain:n=e.chain,store:r}=t??{},i=r?.getState()??{},a=t?.addressOrSymbol??i.feeToken;return(await vq(e,{chain:n}).then(e=>e.filter(e=>e.feeToken)))?.find(e=>a?a===`native`&&e.address===`0x0000000000000000000000000000000000000000`||XL(a)&&YL(e.address,a)?!0:a===e.symbol:!1)}function xq(e={}){let t=e,{mock:n,multichain:r=!0,webAuthn:i}=t,a,o,s=(()=>{if(t.keystoreHost!==`self`&&!(typeof window<`u`&&window.location?.hostname===`localhost`))return t.keystoreHost})();return bK({actions:{async addFunds(){throw new bI},async createAccount(e){let{admins:t,email:r,eoa:o,label:c,permissions:l,internal:u,signInWithEthereum:d}=e,{client:f}=u,p=o??EB(nR()),m=await vq(f),h=n?lB():await cB({createFn:i?.createFn,label:c||`${p.address.slice(0,8)}\u2026${p.address.slice(-6)}`,rpId:s,userId:_L(p.address)}),g=await DK(l,{chainId:f.chain.id,feeTokens:m}),_=t?.map(e=>dB(e)),v=await lq(f,{account:p,authorizeKeys:[h,..._??[],...g?[g]:[]]});a=p.address,r&&c&&await cq(f,{email:c,walletAddress:v.address});let y=await(async()=>{if(!d)return;let e=await HK(f,d,{address:v.address}),t=await OB(p,{payload:KK(eL(e))});return{message:e,signature:await gq(f,{address:v.address,signature:t})}})();return{account:{...v,signInWithEthereum:y}}},async getAccountVersion(e){let{address:t,internal:n}=e,{client:r}=n,{contracts:i}=await LW(r),{accountImplementation:a}=i,o=await nq(r,{account:TB(a)}).then(e=>e.version),s=await nq(r,{account:t}).then(e=>e.version).catch(()=>o);if(!s||!o)throw Error(`version not found.`);return{current:s,latest:o}},async getAssets(e){let{account:t,chainFilter:n,assetFilter:r,assetTypeFilter:i,internal:a}=e,{client:o}=a;return await RW(o,{account:t,assetFilter:r,assetTypeFilter:i,chainFilter:n})},async getCallsHistory(e){let{internal:t,...n}=e,{client:r}=t;return await BW(r,n)},async getCallsStatus(e){let{id:t,internal:n}=e,{client:r}=n,i=await zW(r,{id:t});return{atomic:!0,chainId:$I(r.chain.id),id:t,receipts:i.receipts?.map(e=>({blockHash:e.blockHash,blockNumber:$I(e.blockNumber),gasUsed:$I(e.gasUsed),logs:e.logs,status:e.status,transactionHash:e.transactionHash})),status:i.status,version:`1.0`}},async getCapabilities(e){let{chainIds:t,internal:n}=e,{client:i}=n,a={atomic:{status:`supported`},atomicBatch:{supported:!0},feeToken:{supported:!0,tokens:[]},merchant:{supported:!0},permissions:{supported:!0},requiredFunds:{supported:!!r,tokens:[]}},o=await LW(i,{chainIds:t?t.map(e=>sL(e)):`all`,raw:!0});return Object.entries(o).reduce((e,[t,n])=>({...e,[t]:{...a,...n,feeToken:{supported:!0,tokens:n.fees.tokens},requiredFunds:{supported:!!r,tokens:r?n.fees.tokens.filter(e=>e.interop):[]}}}),{})},async getKeys(e){let{account:t,chainIds:n,internal:r}=e,{client:i}=r;return RB([...await rq(i,{account:t,chainIds:n}),...t.keys??[]],e=>e.publicKey)},async grantAdmin(e){let{account:t,internal:n}=e,{client:r}=n,a=dB(e.key,{chainId:r.chain.id}),o=await bq(r,{addressOrSymbol:e.feeToken,store:n.store}),{id:s}=await oq(r,{account:t,authorizeKeys:[a],feeToken:o?.address,webAuthn:i});return await Zm(r,{id:s,pollingInterval:500}),{key:a}},async grantPermissions(e){let{account:t,internal:n,permissions:r}=e,{client:i}=n,a=await vq(i),o=await DK(r,{chainId:i.chain.id,feeTokens:a});if(!o)throw Error(`key to authorize not found.`);let s=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!s)throw Error(`admin key not found.`);let{context:c,digest:l}=await iq(i,{account:t,authorizeKeys:[o],key:s,preCalls:!0});return await sq(i,{context:c,key:s,signature:await yB(s,{address:null,payload:l})}),{key:o}},async loadAccounts(e){let{internal:t,permissions:r,signInWithEthereum:o}=e,{client:c}=t,l=await vq(c),u=await DK(r,{chainId:c.chain.id,feeTokens:l}),{digest:d,digestType:f,message:p}=await(async()=>{if(o&&e.address){let t=await HK(c,o,{address:e.address});return{context:void 0,digest:KK(eL(t)),digestType:`siwe`,message:t}}return{context:void 0,digest:`0x`,message:void 0}})(),{address:m,credentialId:h,webAuthnSignature:g}=await(async()=>{if(n){if(!a)throw Error(`address_internal not found.`);return{address:a,credentialId:void 0}}if(e.address&&e.key)return{address:e.address,credentialId:e.key.credentialId};let t=await Jz({challenge:d,getFn:i?.getFn,rpId:s}),r=t.raw.response;return{address:TL(new Uint8Array(r.userHandle)),credentialId:t.raw.id,webAuthnSignature:t}})(),_=TB({address:m,keys:[...await rq(c,{account:m,chainIds:[c.chain.id]}),...u?[u]:[]].map((e,t)=>t===0&&e.type===`webauthn-p256`?mB({...e,credential:{id:h,publicKey:LL(e.publicKey)},id:m,rpId:s}):e)}),v=DB(_,{role:`admin`}),y=await(async()=>{if(d!==`0x`)return g?wB(CB(g),{keyType:`webauthn-p256`,publicKey:v.publicKey}):await yB(v,{address:_.address,payload:d})})();if(u){let{context:e,digest:t}=await iq(c,{account:_,authorizeKeys:[u],preCalls:!0});await sq(c,{context:e,key:v,signature:await yB(v,{address:null,payload:t})})}let b=await(async()=>{if(o){if(f===`siwe`&&p&&y)return{message:p,signature:await gq(c,{address:_.address,signature:y})};{let e=await HK(c,o,{address:_.address}),t=await OB(_,{payload:KK(eL(e)),role:`admin`});return{message:e,signature:await gq(c,{address:_.address,signature:t})}}}})();return{accounts:[{..._,signInWithEthereum:b}]}},async prepareCalls(e){let{account:t,calls:n,internal:i,merchantUrl:a}=e,{client:o}=i,s=e.key??await xK({account:t,calls:n});if(!s)throw Error(`cannot find authorized key to sign with.`);let[c,l]=await Promise.all([vq(o),bq(o,{addressOrSymbol:e.feeToken,store:i.store})]),u=_q(e.requiredFunds??[],{tokens:c}),{capabilities:d,context:f,digest:p,typedData:m}=await iq(o,{account:t,calls:n,feeToken:l?.address,key:s,merchantUrl:a,requiredFunds:r?u:void 0}),h=f.quote?.quotes??[],g=h[h.length-1];return{account:t,capabilities:{...d,quote:f.quote},chainId:o.chain.id,context:{...f,account:t,calls:n,nonce:g?.intent.nonce},digest:p,key:s,typedData:m}},async prepareUpgradeAccount(e){let{address:t,email:r,label:a,internal:c,permissions:l}=e,{client:u}=c,[d,f]=await Promise.all([vq(u),bq(u,{store:c.store})]),p=n?lB():await cB({createFn:i?.createFn,label:a||`${t.slice(0,8)}\u2026${t.slice(-6)}`,rpId:s,userId:_L(t)}),m=await DK(l,{chainId:u.chain.id,feeTokens:d}),{context:h,digests:g}=await aq(u,{address:t,authorizeKeys:[p,...m?[m]:[]],feeToken:f?.address});return r&&(o=a),{context:h,digests:g}},async revokeAdmin(e){let{account:t,id:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.id===n);if(o){if(o.type===`webauthn-p256`&&t.keys?.filter(e=>e.type===`webauthn-p256`).length===1)throw Error(`revoke the only WebAuthn key left.`);try{let{id:n}=await oq(a,{account:t,feeToken:(await bq(a,{addressOrSymbol:e.feeToken,store:r.store}))?.address,revokeKeys:[o],webAuthn:i});await Zm(a,{id:n})}catch(e){let t=e;if(t.name===`Rpc.ExecutionError`&&t.abiError?.name===`KeyDoesNotExist`)return;throw e}}},async revokePermissions(e){let{account:t,id:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.id===n);if(o){if(o.role===`admin`)throw Error(`cannot revoke admins.`);try{let{id:n}=await oq(a,{account:t,feeToken:(await bq(a,{addressOrSymbol:e.feeToken,store:r.store}))?.address,revokeKeys:[o],webAuthn:i});await Zm(a,{id:n})}catch(e){let t=e;if(t.name===`Rpc.ExecutionError`&&t.abiError?.name===`KeyDoesNotExist`)return;throw e}}},async sendCalls(e){let{account:t,asTxHash:n,calls:a,chainId:o,internal:s,merchantUrl:c}=e,{client:l}=s;if(!t)throw Error(`account required for relay mode`);let u=await xK({account:t,calls:a,permissionsId:e.permissionsId}),[d,f]=await Promise.all([vq(l),bq(l,{addressOrSymbol:e.feeToken,store:s.store})]),p=_q(e.requiredFunds??[],{tokens:d}),m=await oq(l,{account:t,calls:a,feeToken:f?.address,key:u,merchantUrl:c,requiredFunds:r?p:void 0,webAuthn:i,...o?{chain:{id:o}}:{}});if(n){let{id:e,receipts:t,status:n}=await Zm(l,{id:m.id,pollingInterval:500});if(!t?.[0])throw n===`success`?new DI({message:`Call bundle with id: `+e+` not found.`}):new cI({message:`Transaction failed under call bundle id: `+e+`.`});return{id:t[0].transactionHash}}return m},async sendPreparedCalls(e){let{context:t,key:n,internal:r,signature:i}=e,{client:a}=r,{id:o}=await sq(a,{context:t,key:n,signature:i});return o},async signPersonalMessage(e){let{account:t,data:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!o)throw Error(`cannot find admin key to sign with.`);let s=await OB(t,{key:o,payload:KK(n),webAuthn:i});return gq(a,{address:t.address,signature:s})},async signTypedData(e){let{account:t,internal:n}=e,{client:r}=n,a=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!a)throw Error(`cannot find admin key to sign with.`);let o=XF(e.data),s=o.domain?.name===`Orchestrator`,c=await OB(t,{key:a,payload:tz(o),replaySafe:!s,webAuthn:i});return s?c:gq(r,{address:t.address,signature:c})},async upgradeAccount(e){let{account:t,context:n,internal:r,signatures:i}=e,{client:a}=r;return await lq(a,{context:n,signatures:i}),o&&await cq(a,{email:o,walletAddress:t.address}),{account:t}},async verifyEmail(e){let{account:t,chainId:n,email:r,token:a,internal:o,walletAddress:s}=e,{client:c}=o,l=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!l)throw Error(`cannot find admin key to sign with.`);return await uq(c,{chainId:n,email:r,signature:await OB(t,{key:l,payload:ML(eL(`${r}${a}`)),webAuthn:i}),token:a,walletAddress:s})}},config:e,name:`rpc`})}function Sq(e={}){let{fallback:t=xq(),host:n=WB.prod,renderer:r=KB(),theme:i,themeController:a}=e,o=new Set,s=SK();function c(e,t){return new Promise((n,r)=>{let i=a=>{let s=a.find(t=>t.request.id===e);if(!s&&a.length===0){o.delete(i),r(new vI);return}s&&(s.status!==`success`&&s.status!==`error`||(o.delete(i),s.status===`success`?n(s.result):r(NI(s.error)),t.setState(t=>({...t,requestQueue:t.requestQueue.filter(t=>t.request.id!==e)}))))};o.add(i)})}function l(e){return MI({async request(t){let n=s.prepare(t);return e.setState(e=>{let t=e.accounts[0],r=t?.keys?.find(e=>e.role===`admin`&&e.type===`webauthn-p256`);return{...e,requestQueue:[...e.requestQueue,{account:t?{address:t.address,key:r?{credentialId:r?.credentialId,publicKey:r.publicKey}:void 0}:void 0,request:n,status:`pending`}]}}),c(n.id,e)}},{schema:wK()})}return bK({actions:{async addFunds(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`wallet_addFunds`)throw Error(`Cannot add funds for method: `+n.method);return await l(r).request(n)},async createAccount(e){let{internal:t}=e,{client:n,config:r,request:i,store:a}=t,{storage:o}=r,s=l(a);return{account:await(async()=>{if(i.method===`wallet_connect`){let[{capabilities:e,chainIds:t}]=i._decoded.params??[{}],a=wq(e?.signInWithEthereum?.authUrl??r.authUrl,{storage:o}),c=i.params?.[0]?.capabilities?.signInWithEthereum,l=await DK(e?.grantPermissions,{chainId:n.chain.id}),u=l?RV(TK,EK(l)):void 0,{accounts:d}=await s.request({...i,params:[{capabilities:{...i.params?.[0]?.capabilities,grantPermissions:u,signInWithEthereum:a||c?{...c,authUrl:a}:void 0},chainIds:t?.map(e=>$I(e))}]}),[f]=d;if(!f)throw Error(`no account found.`);let p=f.capabilities?.admins?.map(e=>dB(e,{chainId:n.chain.id})).filter(Boolean),m=f.capabilities?.permissions?.map(e=>{try{let t=_G(zV(hG,e));return t.id===l?.id?{...t,...l,permissions:t.permissions}:t}catch{return}}).filter(Boolean),h=await(async()=>{if(!f.capabilities?.signInWithEthereum)return;let{message:e,signature:t}=f.capabilities.signInWithEthereum;if(!a)return{message:e,signature:t};let{token:n}=await VK({address:f.address,authUrl:a,message:e,publicKey:f.capabilities?.admins?.[0]?.publicKey,signature:t});return{message:e,signature:t,token:n}})();return{...TB({address:f.address,keys:[...p??[],...m??[]]}),signInWithEthereum:h}}throw Error(`Account creation not supported on method: ${i.method}`)})()}},async disconnect(e){let{internal:t}=e,{config:n}=t,{storage:r}=n,i=await r.getItem(`porto.authUrl`)||void 0,a=wq(n.authUrl??i,{storage:r});a&&await fetch(a.logout,{credentials:`include`,method:`POST`}).catch(()=>{})},async getAccountVersion(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getAccountVersion`)throw Error(`Cannot get version for method: `+a.method);return r.supportsHeadless?await l(i).request(a):t.actions.getAccountVersion(e)},async getAssets(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getAssets`)throw Error(`Cannot get assets for method: `+a.method);if(!r.supportsHeadless)return t.actions.getAssets(e);let o=await l(i).request(a);return zV(tK.Response,o)},async getCallsHistory(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCallsHistory`)throw Error(`Cannot get history for method: `+a.method);if(!r.supportsHeadless)return t.actions.getCallsHistory(e);let o=await l(i).request(a);return zV(rK.Response,o)},async getCallsStatus(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCallsStatus`)throw Error(`Cannot get status for method: `+a.method);return r.supportsHeadless?await l(i).request(a):t.actions.getCallsStatus(e)},async getCapabilities(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCapabilities`)throw Error(`Cannot get capabilities for method: `+a.method);return r.supportsHeadless?await l(i).request(a):t.actions.getCapabilities(e)},async getKeys(e){let{account:n,chainIds:i,internal:a}=e,{store:o}=a;return RB([...await(async()=>{if(!r.supportsHeadless)return t.actions.getKeys(e);let a=await l(o).request({method:`wallet_getKeys`,params:[RV(aK.Parameters,{address:n.address,chainIds:i})]});return zV(aK.Response,a)})(),...n.keys??[]],e=>e.publicKey)},async grantAdmin(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`wallet_grantAdmin`)throw Error(`Cannot authorize admin for method: `+n.method);let[i]=n._decoded.params,a=dB(i.key);if(!a)throw Error(`no key found.`);let o=await Cq(t,e);return await l(r).request({method:`wallet_grantAdmin`,params:[{...n.params?.[0],capabilities:{...n.params?.[0]?.capabilities,feeToken:o}}]}),{key:a}},async grantPermissions(e){let{internal:t}=e,{client:n,request:r,store:i}=t;if(r.method!==`wallet_grantPermissions`)throw Error(`Cannot grant permissions for method: `+r.method);let[{address:a,...o}]=r._decoded.params,s=await DK(o,{chainId:n.chain.id});if(!s)throw Error(`no key found.`);let c=RV(TK,EK(s));return await l(i).request({method:`wallet_grantPermissions`,params:[c]}),{key:s}},async loadAccounts(e){let{internal:t}=e,{client:n,config:r,store:i}=t,{storage:a}=r,o=l(i),s=t.request;if(s.method!==`wallet_connect`&&s.method!==`eth_requestAccounts`)throw Error(`Cannot load accounts for method: `+s.method);return{accounts:await(async()=>{let[e]=s._decoded.params??[],{capabilities:t}=e??{},i=wq(t?.signInWithEthereum?.authUrl??r.authUrl,{storage:a}),c=s.params?.[0]?.capabilities?.signInWithEthereum,l=await DK(t?.grantPermissions,{chainId:n.chain.id}),u=l?RV(TK,EK(l)):void 0,{accounts:d}=await o.request({method:`wallet_connect`,params:[{...s.params?.[0],capabilities:{...s.params?.[0]?.capabilities,grantPermissions:u,signInWithEthereum:i||c?{...c,authUrl:i}:void 0}}]});return Promise.all(d.map(async e=>{let t=e.capabilities?.admins?.map(e=>dB(e)).filter(Boolean),n=e.capabilities?.permissions?.map(e=>{try{let t=_G(zV(hG,e));return t.id===l?.id?{...t,...l,permissions:t.permissions}:t}catch{return}}).filter(Boolean),r=await(async()=>{if(!e.capabilities?.signInWithEthereum)return;let{message:t,signature:n}=e.capabilities.signInWithEthereum;if(!i)return{message:t,signature:n};let{token:r}=await VK({address:e.address,authUrl:i,message:t,publicKey:e.capabilities?.admins?.[0]?.publicKey,signature:n});return{message:t,signature:n,token:r}})();return{...TB({address:e.address,keys:[...t??[],...n??[]]}),signInWithEthereum:r}}))})()}},async prepareCalls(e){let{account:n,internal:i}=e,{store:a,request:o}=i;if(o.method!==`wallet_prepareCalls`)throw Error(`Cannot prepare calls for method: `+o.method);if(!r.supportsHeadless)return t.actions.prepareCalls(e);let s=await Cq(i,e),c=l(a),u=zV(oK.Response,await c.request({...o,params:[{...o.params?.[0],capabilities:{...o.params?.[0]?.capabilities,feeToken:s}}]}));return{account:n,chainId:Number(u.chainId),context:u.context,digest:u.digest,key:u.key,typedData:u.typedData}},async prepareUpgradeAccount(e){let{internal:n}=e,{client:i,store:a,request:o}=n;if(o.method!==`wallet_prepareUpgradeAccount`)throw Error(`Cannot prepare upgrade for method: `+o.method);if(!r.supportsHeadless)return t.actions.prepareUpgradeAccount(e);let[{capabilities:s}]=o._decoded.params??[{}],c=await DK(s?.grantPermissions,{chainId:i.chain.id}),u=c?RV(TK,EK(c)):void 0,{context:d,digests:f}=await l(a).request({...o,params:[{...o.params?.[0],capabilities:{...o.params?.[0]?.capabilities,grantPermissions:u}}]}),p=d.account.keys?.map(e=>e.id===c?.id?{...e,...c}:e);return{context:{...d,account:{...d.account,keys:p}},digests:f}},async revokeAdmin(e){let{account:t,id:n,internal:r}=e,{store:i,request:a}=r;if(a.method!==`wallet_revokeAdmin`)throw Error(`Cannot revoke admin for method: `+a.method);let o=t.keys?.find(e=>e.id===n);if(!o)return;if(o.type===`webauthn-p256`&&t.keys?.filter(e=>e.type===`webauthn-p256`).length===1)throw Error(`revoke the only WebAuthn key left.`);let s=await Cq(r,e);return await l(i).request({...a,params:[{...a.params?.[0],capabilities:{...a.params?.[0]?.capabilities,feeToken:s}}]})},async revokePermissions(e){let{account:t,id:n,internal:r}=e,{store:i,request:a}=r;if(a.method!==`wallet_revokePermissions`)throw Error(`Cannot revoke permissions for method: `+a.method);let o=t.keys?.find(e=>e.id===n);if(o){if(o.role===`admin`)throw Error(`cannot revoke permissions.`);return await l(i).request(a)}},async sendCalls(e){let{account:n,asTxHash:i,calls:a,chainId:o,internal:s,merchantUrl:c,requiredFunds:u}=e,{client:d,store:f,request:p}=s,m=l(f),h=await Cq(s,e),g=n?await xK({account:n,calls:a,permissionsId:e.permissionsId}):void 0;if(g?.role===`session`&&n){if(!r.supportsHeadless)return t.actions.sendCalls(e);try{let e=await m.request(RV(oK.Request,{method:`wallet_prepareCalls`,params:[{calls:a,capabilities:{...p._decoded.method===`wallet_sendCalls`?p._decoded.params?.[0]?.capabilities:void 0,feeToken:h,merchantUrl:c,requiredFunds:u},chainId:o,from:n.address,key:g}]})),t=e.capabilities?.quote?.quotes??[];if(t.some((e,n)=>n===t.length-1&&t.length>1?!1:oL(e.feeTokenDeficit)>0n))throw Error(`insufficient funds`);let r=await yB(g,{address:null,payload:e.digest,wrap:!1}),s=(await m.request({method:`wallet_sendPreparedCalls`,params:[{...e,signature:r}]}))[0];if(!s)throw Error(`id not found`);if(i){let{id:e,receipts:t,status:n}=await Zm(d,{id:s.id,pollingInterval:500});if(!t?.[0])throw n===`success`?new DI({message:`Call bundle with id: `+e+` not found.`}):new cI({message:`Transaction failed under call bundle id: `+e+`.`});return{id:t[0].transactionHash}}return s}catch{}}if(p.method===`eth_sendTransaction`)return{id:await m.request({...p,params:[{...p.params?.[0],capabilities:{feeToken:h,merchantUrl:c},...o?{chainId:$I(o)}:{}}]})};if(p.method===`wallet_sendCalls`)return await m.request({method:`wallet_sendCalls`,params:[{...p.params?.[0],capabilities:{...p.params?.[0]?.capabilities,feeToken:h,merchantUrl:c},...o?{chainId:$I(o)}:{}}]});throw Error(`Cannot execute for method: `+p.method)},async sendPreparedCalls(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_sendPreparedCalls`)throw Error(`Cannot send prepared calls for method: `+a.method);if(!r.supportsHeadless)return t.actions.sendPreparedCalls(e);let o=(await l(i).request(a))[0]?.id;if(!o)throw Error(`id not found`);return o},async signPersonalMessage(e){let{internal:t}=e,{store:n,request:r}=t;if(r.method!==`personal_sign`)throw Error(`Cannot sign personal message for method: `+r.method);return await l(n).request(r)},async signTypedData(e){let{internal:t}=e,{store:n,request:r}=t;if(r.method!==`eth_signTypedData_v4`)throw Error(`Cannot sign typed data for method: `+r.method);return await l(n).request(r)},async switchChain(e){let{internal:t}=e,{store:n,request:i}=t;if(i.method!==`wallet_switchEthereumChain`)throw Error(`Cannot switch chain for method: `+i.method);if(r.supportsHeadless)return await l(n).request(i)},async upgradeAccount(e){let{account:t,internal:n}=e,{store:r,request:i}=n;if(i.method!==`wallet_upgradeAccount`)throw Error(`Cannot upgrade account for method: `+i.method);return await l(r).request(i),{account:t}},async verifyEmail(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`account_verifyEmail`)throw Error(`Cannot verify email for method: `+n.method);return await l(r).request(n)}},config:e,name:`dialog`,setup(e){let{internal:t}=e,{store:s}=t,c=r.setup({host:n,internal:t,theme:i,themeController:a}),l=s.subscribe(e=>e.requestQueue,e=>{for(let t of o)t(e);let t=e.map(e=>e.status===`pending`?e:void 0).filter(Boolean);c.syncRequests(t).catch(()=>{}),t.length===0&&c.close()});return()=>{l(),c.destroy()}}})}async function Cq(e,t){let{config:{feeToken:n}}=e,{feeToken:r}=t??{};return r??n}function wq(e,{storage:t}){if(!e)return;let n=UK(e,typeof window<`u`?window.location.origin:void 0);return n&&t.setItem(`porto.authUrl`,n),n}var Tq=e=>!!e.dispatchFromDevtools&&typeof e.dispatch==`function`,Eq=new Map,Dq=e=>{let t=Eq.get(e);return t?Object.fromEntries(Object.entries(t.stores).map(([e,t])=>[e,t.getState()])):{}},Oq=(e,t,n)=>{if(e===void 0)return{type:`untracked`,connection:t.connect(n)};let r=Eq.get(n.name);if(r)return{type:`tracked`,store:e,...r};let i={connection:t.connect(n),stores:{}};return Eq.set(n.name,i),{type:`tracked`,store:e,...i}},kq=(e,t)=>{if(t===void 0)return;let n=Eq.get(e);n&&(delete n.stores[t],Object.keys(n.stores).length===0&&Eq.delete(e))},Aq=e=>{if(!e)return;let t=e.split(` +`),n=t.findIndex(e=>e.includes(`api.setState`));if(n<0)return;let r=t[n+1]?.trim()||``;return/.+ (.+) .+/.exec(r)?.[1]},jq=(e,t={})=>(n,r,i)=>{let{enabled:a,anonymousActionType:o,store:s,...c}=t,l;try{l=(a??!1)&&window.__REDUX_DEVTOOLS_EXTENSION__}catch{}if(!l)return e(n,r,i);let{connection:u,...d}=Oq(s,l,c),f=!0;i.setState=((e,t,a)=>{let l=n(e,t);if(!f)return l;let d=a===void 0?{type:o||Aq(Error().stack)||`anonymous`}:typeof a==`string`?{type:a}:a;return s===void 0?(u?.send(d,r()),l):(u?.send({...d,type:`${s}/${d.type}`},{...Dq(c.name),[s]:i.getState()}),l)}),i.devtools={cleanup:()=>{u&&typeof u.unsubscribe==`function`&&u.unsubscribe(),kq(c.name,s)}};let p=(...e)=>{let t=f;f=!1,n(...e),f=t},m=e(i.setState,r,i);if(d.type===`untracked`?u?.init(m):(d.stores[d.store]=i,u?.init(Object.fromEntries(Object.entries(d.stores).map(([e,t])=>[e,e===d.store?m:t.getState()])))),Tq(i)){let e=i.dispatch;i.dispatch=(...t)=>{e(...t)}}return u.subscribe(e=>{switch(e.type){case`ACTION`:if(typeof e.payload!=`string`){console.error(`[zustand devtools middleware] Unsupported action format`);return}return Mq(e.payload,e=>{if(e.type===`__setState`){if(s===void 0){p(e.state);return}Object.keys(e.state).length!==1&&console.error(` [zustand devtools middleware] Unsupported __setState action format. When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } - `);let t=e.state[s];if(t==null)return;JSON.stringify(i.getState())!==JSON.stringify(t)&&p(t);return}i.dispatchFromDevtools&&typeof i.dispatch==`function`&&i.dispatch(e)});case`DISPATCH`:switch(e.payload.type){case`RESET`:return p(m),s===void 0?u?.init(i.getState()):u?.init(pV(c.name));case`COMMIT`:if(s===void 0){u?.init(i.getState());return}return u?.init(pV(c.name));case`ROLLBACK`:return vV(e.state,e=>{if(s===void 0){p(e),u?.init(i.getState());return}p(e[s]),u?.init(pV(c.name))});case`JUMP_TO_STATE`:case`JUMP_TO_ACTION`:return vV(e.state,e=>{if(s===void 0){p(e);return}JSON.stringify(i.getState())!==JSON.stringify(e[s])&&p(e[s])});case`IMPORT_STATE`:{let{nextLiftedState:t}=e.payload,n=t.computedStates.slice(-1)[0]?.state;if(!n)return;p(s===void 0?n:n[s]),u?.send(null,t);return}case`PAUSE_RECORDING`:return f=!f}return}}),m},vV=(e,t)=>{let n;try{n=JSON.parse(e)}catch(e){console.error(`[zustand devtools middleware] Could not parse the received json`,e)}n!==void 0&&t(n)},yV=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};function bV(e,t){let n;try{n=e()}catch{return}return{getItem:e=>{let r=e=>e===null?null:JSON.parse(e,t?.reviver),i=n.getItem(e)??null;return i instanceof Promise?i.then(r):r(i)},setItem:(e,r)=>n.setItem(e,JSON.stringify(r,t?.replacer)),removeItem:e=>n.removeItem(e)}}var xV=e=>t=>{try{let n=e(t);return n instanceof Promise?n:{then(e){return xV(e)(n)},catch(e){return this}}}catch(e){return{then(e){return this},catch(t){return xV(t)(e)}}}},SV=(e,t)=>(n,r,i)=>{let a={storage:bV(()=>localStorage),partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1,s=new Set,c=new Set,l=a.storage;if(!l)return e((...e)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),n(...e)},r,i);let u=()=>{let e=a.partialize({...r()});return l.setItem(a.name,{state:e,version:a.version})},d=i.setState;i.setState=(e,t)=>(d(e,t),u());let f=e((...e)=>(n(...e),u()),r,i);i.getInitialState=()=>f;let p,m=()=>{if(!l)return;o=!1,s.forEach(e=>e(r()??f));let e=a.onRehydrateStorage?.call(a,r()??f)||void 0;return xV(l.getItem.bind(l))(a.name).then(e=>{if(e)if(typeof e.version==`number`&&e.version!==a.version){if(a.migrate){let t=a.migrate(e.state,e.version);return t instanceof Promise?t.then(e=>[!0,e]):[!0,t]}console.error(`State loaded from storage couldn't be migrated since no migrate function was provided`)}else return[!1,e.state];return[!1,void 0]}).then(e=>{let[t,i]=e;if(p=a.merge(i,r()??f),n(p,!0),t)return u()}).then(()=>{e?.(p,void 0),p=r(),o=!0,c.forEach(e=>e(p))}).catch(t=>{e?.(void 0,t)})};return i.persist={setOptions:e=>{a={...a,...e},e.storage&&(l=e.storage)},clearStorage:()=>{l?.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>m(),hasHydrated:()=>o,onHydrate:e=>(s.add(e),()=>{s.delete(e)}),onFinishHydration:e=>(c.add(e),()=>{c.delete(e)})},a.skipHydration||m(),p||f},CV=e=>{let t,n=new Set,r=(e,r)=>{let i=typeof e==`function`?e(t):e;if(!Object.is(i,t)){let e=t;t=r??(typeof i!=`object`||!i)?i:Object.assign({},t,i),n.forEach(n=>n(t,e))}},i=()=>t,a={setState:r,getState:i,getInitialState:()=>o,subscribe:e=>(n.add(e),()=>n.delete(e))},o=t=e(r,i,a);return a},wV=(e=>e?CV(e):CV);function TV(e){return new Promise((t,n)=>{e.oncomplete=e.onsuccess=()=>t(e.result),e.onabort=e.onerror=()=>n(e.error)})}function EV(e,t){let n,r=()=>{if(n)return n;let r=indexedDB.open(e);return r.onupgradeneeded=()=>r.result.createObjectStore(t),n=TV(r),n.then(e=>{e.onclose=()=>n=void 0},()=>{}),n};return(e,n)=>r().then(r=>n(r.transaction(t,e).objectStore(t)))}var DV;function OV(){return DV||=EV(`keyval-store`,`keyval`),DV}function kV(e,t=OV()){return t(`readonly`,t=>TV(t.get(e)))}function AV(e,t,n=OV()){return n(`readwrite`,n=>(n.put(t,e),TV(n.transaction)))}function jV(e,t=OV()){return t(`readwrite`,t=>(t.delete(e),TV(t.transaction)))}function MV(e){return e}function NV(){let e=typeof indexedDB<`u`?EV(`porto`,`store`):void 0;return MV({async getItem(t){let n=await kV(t,e);return n===null?null:n},async removeItem(t){await jV(t,e)},async setItem(t,n){await AV(t,pN(n),e)},sizeLimit:1024*1024*50})}function PV(){let e=new Map;return MV({getItem(t){return e.get(t)??null},removeItem(t){e.delete(t)},setItem(t,n){e.set(t,n)},sizeLimit:1/0})}var FV=typeof window<`u`&&typeof document<`u`;const IV={announceProvider:!0,chains:CM,mode:FV?lV({host:bN.prod}):cV(),relay:zv(rz.prod.http),storage:FV&&typeof indexedDB<`u`?NV():PV(),storageKey:`porto.store`};function LV(e={}){let t=e.chains??IV.chains,n=Object.fromEntries(t.map(t=>[t.id,e.transports?.[t.id]??zv()])),r={announceProvider:e.announceProvider??IV.announceProvider,authUrl:e.authUrl,chains:t,feeToken:e.feeToken,merchantUrl:e.merchantUrl,mode:e.mode??IV.mode,relay:e.relay??IV.relay,storage:e.storage??IV.storage,storageKey:e.storageKey??IV.storageKey,transports:n},i=wV(_V(yV(SV(e=>({accounts:[],chainIds:r.chains.map(e=>e.id),feeToken:r.feeToken,requestQueue:[]}),{merge(e,t){let n=e,i=r.chains.find(e=>e.id===n.chainIds[0])?.id??r.chains[0].id,a=[i,...r.chains.map(e=>e.id).filter(e=>e!==i)];return{...t,...n,chainIds:a}},name:r.storageKey,partialize:e=>({accounts:e.accounts.map(e=>pN(e)),chainIds:e.chainIds}),storage:r.storage,version:5})))),a=r.mode,o={config:r,getMode(){return a},id:hN(),setMode(e){return c?.(),a=e,c=e.setup({internal:o}),c},store:i},s=gB(o),c=a===null?()=>{}:a.setup({internal:o});return{_internal:o,config:r,destroy(){c(),s._internal.destroy()},provider:s}}const RV=Object.freeze(Object.values(xM)),zV=e=>RV.find(t=>t.id===e);var BV=e=>{if(typeof e==`number`)return Number.isFinite(e)?e:void 0;if(typeof e!=`string`)return;let t=e.trim();if(/^0x[0-9a-fA-F]+$/.test(t)){let e=Number.parseInt(t,16);return Number.isNaN(e)?void 0:e}if(/^\d+$/.test(t)){let e=Number.parseInt(t,10);return Number.isNaN(e)?void 0:e}};const VV=(e,t,n)=>{let r=BV(e);t(r),n(r==null?void 0:zV(r))},HV=async(e,t=`GET`,n)=>{let r={"Content-Type":`application/json`},i=typeof window<`u`?window.__SESSION_TOKEN__:void 0;i&&(r[`X-Session-Token`]=i);let a=await fetch(`http://127.0.0.1:9545${e}`,{method:t,headers:r,body:n===void 0?void 0:JSON.stringify(n)});if(!a.ok)throw Error(`API request failed: ${a.status} ${a.statusText}`);try{return await a.json()}catch{throw Error(`Invalid JSON response`)}},UV=e=>JSON.stringify(e,(e,t)=>typeof t==`bigint`?t.toString():t,2),WV=e=>{if(e==null)return UV(e);if(typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`){let t=e;try{let n=JSON.parse(t.message);return UV({...e,message:n})}catch{return UV(e)}}return UV(e)},GV=e=>!!e&&e.status===`ok`;var KV=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),$=u(s(((e,t)=>{t.exports=KV()}))());function qV(){(0,y.useEffect)(()=>{window.__PORTO__||(window.__PORTO__=LV())},[]);let[e,t]=(0,y.useState)([]),[n,r]=(0,y.useState)(!1),[i,a]=(0,y.useState)(null),[o,s]=(0,y.useState)(null),[c,l]=(0,y.useState)(null),u=e.find(e=>e.info.uuid===c)??null,[d,f]=(0,y.useState)(),[p,m]=(0,y.useState)(),[h,g]=(0,y.useState)(),[_,v]=(0,y.useState)(null),[b,x]=(0,y.useState)(null),[S,C]=(0,y.useState)(null),w=(0,y.useRef)(null),ee=(0,y.useRef)(null),te=(0,y.useRef)(null),ne=async()=>{if(!u||n)return;let e=await u.provider.request({method:`eth_requestAccounts`});f(e?.[0]??void 0);try{let e=await u.provider.request({method:`eth_chainId`});VV(e,m,g)}catch{m(void 0),g(void 0)}},re=async()=>{if(!(!d||p==null)){try{await HV(`/api/connection`,`POST`,[d,p])}catch{return}r(!0)}},ie=async()=>{if(!u||!o)return;let{id:e,signType:t,request:n}=o,r=n.address,i=n.message;try{let n;switch(t){case`PersonalSign`:n=await u.provider.request({method:`personal_sign`,params:[i,r]});break;case`SignTypedDataV4`:n=await u.provider.request({method:`eth_signTypedData_v4`,params:[r,i]});break;default:throw Error(`Unsupported signType: ${t}`)}await HV(`/api/signing/response`,`POST`,{id:e,signature:n,error:null}),C(n),s(null)}catch(t){let n=typeof t==`object`&&t&&`message`in t&&typeof t.message==`string`?t.message:String(t);try{await HV(`/api/signing/response`,`POST`,{id:e,signature:null,error:n})}catch{}C(null),s(null)}},ae=async()=>{if(!u||!i?.request)return;let e=Mv({transport:Pv(u.provider),chain:h});try{let{from:t,input:n,to:r,...a}=i.request,o=await e.sendTransaction({...a,account:t||(await e.getAddresses())[0],...n?{data:n}:{},...r?{to:r}:{},chain:h});x(o),await HV(`/api/transaction/response`,`POST`,{id:i.id,hash:o,error:null});let s=await uv(e,{hash:o});v(s)}catch(e){let t=typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`?e.message:String(e);console.error(`send failed:`,t);try{await HV(`/api/transaction/response`,`POST`,{id:i.id,hash:null,error:t})}catch{}}},oe=(0,y.useCallback)(()=>{w.current&&=(window.clearInterval(w.current),null),ee.current&&=(window.clearInterval(ee.current),null),a(null),s(null),x(null),v(null),f(void 0),m(void 0),g(void 0),r(!1),HV(`/api/connection`,`POST`,null)},[]);return(0,y.useEffect)(()=>{te.current&&c&&te.current!==c&&oe(),te.current=c},[c,oe]),(0,y.useEffect)(()=>{e.length===1&&!u&&l(e[0].info.uuid)},[e,u]),(0,y.useEffect)(()=>{let e=e=>{let{info:n,provider:r}=e.detail;t(e=>e.some(e=>e.info.uuid===n.uuid)?e:[...e,{info:n,provider:r}])};return window.addEventListener(`eip6963:announceProvider`,e),window.dispatchEvent(new Event(`eip6963:requestProvider`)),()=>window.removeEventListener(`eip6963:announceProvider`,e)},[]),(0,y.useEffect)(()=>{if(!u)return;let e=e=>{n||f(e[0]??void 0)},t=e=>{n||VV(e,m,g)};return u.provider.on?.(`accountsChanged`,e),u.provider.on?.(`chainChanged`,t),()=>{u.provider.removeListener?.(`accountsChanged`,e),u.provider.removeListener?.(`chainChanged`,t)}},[u,n]),(0,y.useEffect)(()=>{if(!n||i||o)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await HV(`/api/transaction/request`);GV(n)&&(window.clearInterval(t),e&&a(n.data))}catch{}},1e3);return w.current=t,()=>{e=!1,window.clearInterval(t),w.current===t&&(w.current=null)}},[n,i,o]),(0,y.useEffect)(()=>{if(!n||o||i)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await HV(`/api/signing/request`);GV(n)&&(window.clearInterval(t),e&&s(n.data))}catch{}},1e3);return ee.current=t,()=>{e=!1,window.clearInterval(t),ee.current===t&&(ee.current=null)}},[n,o,i]),(0,$.jsx)(`div`,{className:`wrapper`,children:(0,$.jsxs)(`div`,{className:`container`,children:[(0,$.jsx)(`div`,{className:`notice`,children:`Browser wallet is still in early development. Use with caution!`}),(0,$.jsx)(`img`,{className:`banner`,src:`banner.png`,alt:`Foundry Browser Wallet`}),e.length>1&&(0,$.jsx)(`div`,{className:`wallet-selector`,children:(0,$.jsx)(`label`,{children:(0,$.jsxs)(`select`,{value:c??``,onChange:e=>l(e.target.value||null),disabled:n,children:[(0,$.jsx)(`option`,{value:``,disabled:!0,children:`Select wallet…`}),e.map(({info:e})=>(0,$.jsxs)(`option`,{value:e.uuid,children:[e.name,` (`,e.rdns,`)`]},e.uuid))]})})}),e.length===0&&(0,$.jsx)(`p`,{children:`No wallets found.`}),u&&!d&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-connect`,onClick:ne,disabled:n,children:`Connect Wallet`}),u&&d&&!n&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-confirm`,onClick:re,disabled:!d||p==null,children:`Confirm Connection`}),u&&d&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Connected`}),(0,$.jsx)(`pre`,{className:`box`,children:`\ + `);let t=e.state[s];if(t==null)return;JSON.stringify(i.getState())!==JSON.stringify(t)&&p(t);return}Tq(i)&&i.dispatch(e)});case`DISPATCH`:switch(e.payload.type){case`RESET`:return p(m),s===void 0?u?.init(i.getState()):u?.init(Dq(c.name));case`COMMIT`:if(s===void 0){u?.init(i.getState());return}return u?.init(Dq(c.name));case`ROLLBACK`:return Mq(e.state,e=>{if(s===void 0){p(e),u?.init(i.getState());return}p(e[s]),u?.init(Dq(c.name))});case`JUMP_TO_STATE`:case`JUMP_TO_ACTION`:return Mq(e.state,e=>{if(s===void 0){p(e);return}JSON.stringify(i.getState())!==JSON.stringify(e[s])&&p(e[s])});case`IMPORT_STATE`:{let{nextLiftedState:t}=e.payload,n=t.computedStates.slice(-1)[0]?.state;if(!n)return;p(s===void 0?n:n[s]),u?.send(null,t);return}case`PAUSE_RECORDING`:return f=!f}return}}),m},Mq=(e,t)=>{let n;try{n=JSON.parse(e)}catch(e){console.error(`[zustand devtools middleware] Could not parse the received json`,e)}n!==void 0&&t(n)},Nq=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};function Pq(e,t){let n;try{n=e()}catch{return}return{getItem:e=>{let r=e=>e===null?null:JSON.parse(e,t?.reviver),i=n.getItem(e)??null;return i instanceof Promise?i.then(r):r(i)},setItem:(e,r)=>n.setItem(e,JSON.stringify(r,t?.replacer)),removeItem:e=>n.removeItem(e)}}var Fq=e=>t=>{try{let n=e(t);return n instanceof Promise?n:{then(e){return Fq(e)(n)},catch(e){return this}}}catch(e){return{then(e){return this},catch(t){return Fq(t)(e)}}}},Iq=(e,t)=>(n,r,i)=>{let a={storage:Pq(()=>window.localStorage),partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1,s=0,c=new Set,l=new Set,u=a.storage;if(!u)return e((...e)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),n(...e)},r,i);let d=()=>{let e=a.partialize({...r()});return u.setItem(a.name,{state:e,version:a.version})},f=i.setState;i.setState=(e,t)=>(f(e,t),d());let p=e((...e)=>(n(...e),d()),r,i);i.getInitialState=()=>p;let m,h=()=>{if(!u)return;let e=++s;o=!1,c.forEach(e=>e(r()??p));let t=a.onRehydrateStorage?.call(a,r()??p)||void 0;return Fq(u.getItem.bind(u))(a.name).then(e=>{if(e)if(typeof e.version==`number`&&e.version!==a.version){if(a.migrate){let t=a.migrate(e.state,e.version);return t instanceof Promise?t.then(e=>[!0,e]):[!0,t]}console.error(`State loaded from storage couldn't be migrated since no migrate function was provided`)}else return[!1,e.state];return[!1,void 0]}).then(t=>{if(e!==s)return;let[i,o]=t;if(m=a.merge(o,r()??p),n(m,!0),i)return d()}).then(()=>{e===s&&(t?.(r(),void 0),m=r(),o=!0,l.forEach(e=>e(m)))}).catch(n=>{e===s&&t?.(void 0,n)})};return i.persist={setOptions:e=>{a={...a,...e},e.storage&&(u=e.storage)},clearStorage:()=>{u?.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>h(),hasHydrated:()=>o,onHydrate:e=>(c.add(e),()=>{c.delete(e)}),onFinishHydration:e=>(l.add(e),()=>{l.delete(e)})},a.skipHydration||h(),m||p},Lq=e=>{let t,n=new Set,r=(e,r)=>{let i=typeof e==`function`?e(t):e;if(!Object.is(i,t)){let e=t;t=r??(typeof i!=`object`||!i)?i:Object.assign({},t,i),n.forEach(n=>n(t,e))}},i=()=>t,a={setState:r,getState:i,getInitialState:()=>o,subscribe:e=>(n.add(e),()=>n.delete(e))},o=t=e(r,i,a);return a},Rq=(e=>e?Lq(e):Lq);function zq(e){return new Promise((t,n)=>{e.oncomplete=e.onsuccess=()=>t(e.result),e.onabort=e.onerror=()=>n(e.error)})}function Bq(e,t){let n,r=()=>{if(n)return n;let r=indexedDB.open(e);return r.onupgradeneeded=()=>r.result.createObjectStore(t),n=zq(r),n.then(e=>{e.onclose=()=>n=void 0},()=>{}),n};return(e,n)=>r().then(r=>n(r.transaction(t,e).objectStore(t)))}var Vq;function Hq(){return Vq||=Bq(`keyval-store`,`keyval`),Vq}function Uq(e,t=Hq()){return t(`readonly`,t=>zq(t.get(e)))}function Wq(e,t,n=Hq()){return n(`readwrite`,n=>(n.put(t,e),zq(n.transaction)))}function Gq(e,t=Hq()){return t(`readwrite`,t=>(t.delete(e),zq(t.transaction)))}function Kq(e){return e}function qq(){let e=typeof indexedDB<`u`?Bq(`porto`,`store`):void 0;return Kq({async getItem(t){let n=await Uq(t,e);return n===null?null:n},async removeItem(t){await Gq(t,e)},async setItem(t,n){await Wq(t,LB(n),e)},sizeLimit:1024*1024*50})}function Jq(){let e=new Map;return Kq({getItem(t){return e.get(t)??null},removeItem(t){e.delete(t)},setItem(t,n){e.set(t,n)},sizeLimit:1/0})}var Yq=typeof window<`u`&&typeof document<`u`,Xq={announceProvider:!0,chains:qF,mode:Yq?Sq({host:WB.prod}):xq(),relay:$v(tG.prod.http),storage:Yq&&typeof indexedDB<`u`?qq():Jq(),storageKey:`porto.store`};function Zq(e={}){let t=e.chains??Xq.chains,n=Object.fromEntries(t.map(t=>[t.id,e.transports?.[t.id]??$v()])),r={announceProvider:e.announceProvider??Xq.announceProvider,authUrl:e.authUrl,chains:t,feeToken:e.feeToken,merchantUrl:e.merchantUrl,mode:e.mode??Xq.mode,relay:e.relay??Xq.relay,storage:e.storage??Xq.storage,storageKey:e.storageKey??Xq.storageKey,transports:n},i=Rq(jq(Nq(Iq(e=>({accounts:[],chainIds:r.chains.map(e=>e.id),feeToken:r.feeToken,requestQueue:[]}),{merge(e,t){let n=e,i=r.chains.find(e=>e.id===n.chainIds[0])?.id??r.chains[0].id,a=[i,...r.chains.map(e=>e.id).filter(e=>e!==i)];return{...t,...n,chainIds:a}},name:r.storageKey,partialize:e=>({accounts:e.accounts.map(e=>LB(e)),chainIds:e.chainIds}),storage:r.storage,version:5})))),a=r.mode,o={config:r,getMode(){return a},id:zB(),setMode(e){return c?.(),a=e,c=e.setup({internal:o}),c},store:i},s=hK(o),c=a===null?()=>{}:a.setup({internal:o});return{_internal:o,config:r,destroy(){c(),s._internal.destroy()},provider:s}}var Qq=`http://127.0.0.1:9545`,$q=Object.freeze(Object.values(KF)),eJ=e=>$q.find(t=>t.id===e),tJ=e=>{if(typeof e==`number`)return Number.isFinite(e)?e:void 0;if(typeof e!=`string`)return;let t=e.trim();if(/^0x[0-9a-fA-F]+$/.test(t)){let e=Number.parseInt(t,16);return Number.isNaN(e)?void 0:e}if(/^\d+$/.test(t)){let e=Number.parseInt(t,10);return Number.isNaN(e)?void 0:e}},nJ=(e,t,n)=>{let r=tJ(e);if(t(r),r!=null){let e=eJ(r);e||={id:r,name:`Chain ${r}`,network:`chain-${r}`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[]},public:{http:[]}}},n(e)}else n(void 0)},rJ=async(e,t=`GET`,n)=>{let r={"Content-Type":`application/json`},i=typeof window<`u`?window.__SESSION_TOKEN__:void 0;i&&(r[`X-Session-Token`]=i);let a=await fetch(`${Qq}${e}`,{method:t,headers:r,body:n===void 0?void 0:JSON.stringify(n)});if(!a.ok)throw Error(`API request failed: ${a.status} ${a.statusText}`);try{return await a.json()}catch{throw Error(`Invalid JSON response`)}},iJ=e=>JSON.stringify(e,(e,t)=>typeof t==`bigint`?t.toString():t,2),aJ=e=>{if(e==null)return iJ(e);if(typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`){let t=e;try{let n=JSON.parse(t.message);return iJ({...e,message:n})}catch{return iJ(e)}}return iJ(e)},oJ=e=>!!e&&e.status===`ok`,sJ=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),$=s(((e,t)=>{t.exports=sJ()}))();function cJ(){(0,y.useEffect)(()=>{window.__PORTO__||(window.__PORTO__=Zq())},[]);let[e,t]=(0,y.useState)([]),[n,r]=(0,y.useState)(!1),[i,a]=(0,y.useState)(null),[o,s]=(0,y.useState)(null),[c,l]=(0,y.useState)(null),u=e.find(e=>e.info.uuid===c)??null,[d,f]=(0,y.useState)(),[p,m]=(0,y.useState)(),[h,g]=(0,y.useState)(),[_,v]=(0,y.useState)(null),[b,x]=(0,y.useState)(null),[S,ee]=(0,y.useState)(null),C=(0,y.useRef)(null),te=(0,y.useRef)(null),ne=(0,y.useRef)(null),re=async()=>{if(!(!u||n)){f((await u.provider.request({method:`eth_requestAccounts`}))?.[0]??void 0);try{nJ(await u.provider.request({method:`eth_chainId`}),m,g)}catch{m(void 0),g(void 0)}}},ie=async()=>{if(!(!d||p==null)){try{await rJ(`/api/connection`,`POST`,[d,p])}catch{return}r(!0)}},ae=async()=>{if(!u||!o)return;let{id:e,signType:t,request:n}=o,r=n.address,i=n.message;try{let n;switch(t){case`PersonalSign`:n=await u.provider.request({method:`personal_sign`,params:[i,r]});break;case`SignTypedDataV4`:n=await u.provider.request({method:`eth_signTypedData_v4`,params:[r,i]});break;default:throw Error(`Unsupported signType: ${t}`)}await rJ(`/api/signing/response`,`POST`,{id:e,signature:n,error:null}),ee(n),s(null)}catch(t){let n=typeof t==`object`&&t&&`message`in t&&typeof t.message==`string`?t.message:String(t);try{await rJ(`/api/signing/response`,`POST`,{id:e,signature:null,error:n})}catch{}ee(null),s(null)}},oe=async()=>{if(!u||!i?.request)return;let e=Kv({transport:Jv(u.provider),chain:h});try{let{from:t,input:n,to:r,...a}=i.request,o=await e.sendTransaction({...a,account:t||(await e.getAddresses())[0],...n?{data:n}:{},...r?{to:r}:{},chain:h});x(o),await rJ(`/api/transaction/response`,`POST`,{id:i.id,hash:o,error:null}),v(await wv(e,{hash:o}))}catch(e){let t=typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`?e.message:String(e);console.error(`send failed:`,t);try{await rJ(`/api/transaction/response`,`POST`,{id:i.id,hash:null,error:t})}catch{}}},se=(0,y.useCallback)(()=>{C.current&&=(window.clearInterval(C.current),null),te.current&&=(window.clearInterval(te.current),null),a(null),s(null),x(null),v(null),f(void 0),m(void 0),g(void 0),r(!1),rJ(`/api/connection`,`POST`,null)},[]);return(0,y.useEffect)(()=>{ne.current&&c&&ne.current!==c&&se(),ne.current=c},[c,se]),(0,y.useEffect)(()=>{e.length===1&&!u&&l(e[0].info.uuid)},[e,u]),(0,y.useEffect)(()=>{let e=e=>{let{info:n,provider:r}=e.detail;t(e=>e.some(e=>e.info.uuid===n.uuid)?e:[...e,{info:n,provider:r}])};return window.addEventListener(`eip6963:announceProvider`,e),window.dispatchEvent(new Event(`eip6963:requestProvider`)),()=>window.removeEventListener(`eip6963:announceProvider`,e)},[]),(0,y.useEffect)(()=>{if(!u)return;let e=e=>{n||f(e[0]??void 0)},t=e=>{n||nJ(e,m,g)};return u.provider.on?.(`accountsChanged`,e),u.provider.on?.(`chainChanged`,t),()=>{u.provider.removeListener?.(`accountsChanged`,e),u.provider.removeListener?.(`chainChanged`,t)}},[u,n]),(0,y.useEffect)(()=>{if(!n||i||o)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await rJ(`/api/transaction/request`);oJ(n)&&(window.clearInterval(t),e&&a(n.data))}catch{}},1e3);return C.current=t,()=>{e=!1,window.clearInterval(t),C.current===t&&(C.current=null)}},[n,i,o]),(0,y.useEffect)(()=>{if(!n||o||i)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await rJ(`/api/signing/request`);oJ(n)&&(window.clearInterval(t),e&&s(n.data))}catch{}},1e3);return te.current=t,()=>{e=!1,window.clearInterval(t),te.current===t&&(te.current=null)}},[n,o,i]),(0,$.jsx)(`div`,{className:`wrapper`,children:(0,$.jsxs)(`div`,{className:`container`,children:[(0,$.jsx)(`div`,{className:`notice`,children:`Browser wallet is still in early development. Use with caution!`}),(0,$.jsx)(`img`,{className:`banner`,src:`banner.png`,alt:`Foundry Browser Wallet`}),e.length>1&&(0,$.jsx)(`div`,{className:`wallet-selector`,children:(0,$.jsx)(`label`,{children:(0,$.jsxs)(`select`,{value:c??``,onChange:e=>l(e.target.value||null),disabled:n,children:[(0,$.jsx)(`option`,{value:``,disabled:!0,children:`Select wallet…`}),e.map(({info:e})=>(0,$.jsxs)(`option`,{value:e.uuid,children:[e.name,` (`,e.rdns,`)`]},e.uuid))]})})}),e.length===0&&(0,$.jsx)(`p`,{children:`No wallets found.`}),u&&!d&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-connect`,onClick:re,disabled:n,children:`Connect Wallet`}),u&&d&&!n&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-confirm`,onClick:ie,disabled:!d||p==null,children:`Confirm Connection`}),u&&d&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Connected`}),(0,$.jsx)(`pre`,{className:`box`,children:`\ account: ${d} chain: ${h?`${h.name} (${p})`:p??`unknown`} -rpc: ${h?.rpcUrls?.default?.http?.[0]??h?.rpcUrls?.public?.http?.[0]??`unknown`}`})]}),u&&d&&n&&!i&&!o&&!b&&!S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction To Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:`No pending transaction or signing request`})})]}),u&&d&&n&&!b&&i&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction to Sign & Send`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:UV(i.request)})}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:ae,children:`Sign & Send`})]}),u&&d&&n&&!i&&o&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Message / Data to Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:WV(o.request)})}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:ie,children:`Sign`})]}),u&&d&&b&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction Hash`}),(0,$.jsx)(`pre`,{className:`box`,children:b}),(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Receipt`}),(0,$.jsx)(`pre`,{className:`box`,children:_?UV(_):`Waiting for receipt...`})]})]}),u&&d&&n&&S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Signature Result`}),(0,$.jsx)(`pre`,{className:`box`,children:S})]})]})})}var JV=document.getElementById(`root`);if(JV)(0,v.createRoot)(JV).render((0,$.jsx)(y.StrictMode,{children:(0,$.jsx)(qV,{})}));else throw Error(`Root element with id "root" not found`); \ No newline at end of file +rpc: ${h?.rpcUrls?.default?.http?.[0]??h?.rpcUrls?.public?.http?.[0]??`unknown`}`})]}),u&&d&&n&&!i&&!o&&!b&&!S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction To Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:`No pending transaction or signing request`})})]}),u&&d&&n&&!b&&i&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction to Sign & Send`}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:oe,children:`Sign & Send`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:iJ(i.request)})})]}),u&&d&&n&&!i&&o&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Message / Data to Sign`}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:ae,children:`Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:aJ(o.request)})})]}),u&&d&&b&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction Hash`}),(0,$.jsx)(`pre`,{className:`box`,children:b}),(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Receipt`}),(0,$.jsx)(`pre`,{className:`box`,children:_?iJ(_):`Waiting for receipt...`})]})]}),u&&d&&n&&S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Signature Result`}),(0,$.jsx)(`pre`,{className:`box`,children:S})]})]})})}var lJ=document.getElementById(`root`);if(lJ)(0,v.createRoot)(lJ).render((0,$.jsx)(y.StrictMode,{children:(0,$.jsx)(cJ,{})}));else throw Error(`Root element with id "root" not found`); \ No newline at end of file diff --git a/crates/wallets/src/wallet_browser/app/assets/styles.css b/crates/wallets/src/wallet_browser/app/assets/styles.css index b2c9cd369d41f..3e00c2bb172d8 100644 --- a/crates/wallets/src/wallet_browser/app/assets/styles.css +++ b/crates/wallets/src/wallet_browser/app/assets/styles.css @@ -1,2 +1,2 @@ -*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;color:inherit;letter-spacing:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;color:inherit;letter-spacing:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html,body,#root{color:#f8f8f8;background-color:#13151b;width:100%;height:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}button{color:#f8f8f8;cursor:pointer;background-color:#3a3f51;border:1px solid #e1e4e8;border-radius:4px;padding:8px 12px}button:hover{background-color:#50566e}select{color:#f8f8f8;cursor:pointer;background-color:#1e2026;border:1px solid #e1e4e8;border-radius:8px;margin-bottom:16px;padding:8px}option{color:#f8f8f8;background-color:#1e2026}pre{white-space:pre-wrap;word-break:break-all;overflow-wrap:anywhere}.wrapper{flex-direction:column;justify-content:center;align-items:center;min-height:100vh;padding:24px;display:flex}.container{background-color:#3b3b3b;border-radius:8px;flex-direction:column;align-items:flex-start;max-width:600px;padding:16px;display:flex}.notice{color:#333;text-align:center;background-color:#fc0;border-radius:8px;width:100%;margin-bottom:16px;padding:8px;font-size:13px;font-weight:700}.banner{border-radius:8px;width:100%;height:auto}.wallet-selector,.wallet-connect,.wallet-send,.wallet-confirm{align-self:center;margin-top:16px}.title,.section-title{color:#f8f8f8}.title{margin-bottom:24px;font-size:36px}.section-title{margin-bottom:16px;font-size:24px}.box{border:1px solid #e1e4e8;border-radius:8px;margin-bottom:16px;padding:8px 12px;font-size:13px} +*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;color:inherit;letter-spacing:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;color:inherit;letter-spacing:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html,body,#root{color:#f8f8f8;background-color:#13151b;width:100%;height:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}button{color:#f8f8f8;cursor:pointer;background-color:#3a3f51;border:1px solid #e1e4e8;border-radius:4px;padding:8px 12px}button:hover{background-color:#50566e}select{color:#f8f8f8;cursor:pointer;background-color:#1e2026;border:1px solid #e1e4e8;border-radius:8px;margin-bottom:16px;padding:8px}option{color:#f8f8f8;background-color:#1e2026}pre{white-space:pre-wrap;word-break:break-all;overflow-wrap:anywhere}.wrapper{flex-direction:column;justify-content:center;align-items:center;min-height:100vh;padding:24px;display:flex}.container{background-color:#3b3b3b;border-radius:8px;flex-direction:column;align-items:flex-start;max-width:600px;padding:16px;display:flex}.notice{color:#333;text-align:center;background-color:#fc0;border-radius:8px;width:100%;margin-bottom:16px;padding:8px;font-size:13px;font-weight:700}.banner{border-radius:8px;width:100%;height:auto}.wallet-selector,.wallet-connect,.wallet-send,.wallet-confirm{align-self:center;margin-top:16px}.wallet-send{margin-bottom:16px}.title,.section-title{color:#f8f8f8}.title{margin-bottom:24px;font-size:36px}.section-title{margin-bottom:16px;font-size:24px}.box{border:1px solid #e1e4e8;border-radius:8px;margin-bottom:16px;padding:8px 12px;font-size:13px} /*$vite$:1*/ \ No newline at end of file diff --git a/crates/wallets/src/wallet_browser/opts.rs b/crates/wallets/src/wallet_browser/opts.rs index f175a887df475..b8a30727115a5 100644 --- a/crates/wallets/src/wallet_browser/opts.rs +++ b/crates/wallets/src/wallet_browser/opts.rs @@ -9,10 +9,10 @@ use crate::wallet_browser::signer::BrowserSigner; /// Browser wallet options #[derive(Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "Browser wallet options")] +#[command(next_help_heading = "Wallet options - browser wallet")] pub struct BrowserWalletOpts { /// Use a browser wallet. - #[arg(long, help_heading = "")] + #[arg(long)] pub browser: bool, /// Port for the browser wallet server. diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs index 62770e8a944c9..5a7ad7fe4bee9 100644 --- a/crates/wallets/src/wallet_browser/signer.rs +++ b/crates/wallets/src/wallet_browser/signer.rs @@ -3,7 +3,7 @@ use std::{ time::{Duration, Instant}, }; -use alloy_network::{Ethereum, Network, TransactionBuilder}; +use alloy_network::{Network, TransactionBuilder}; use alloy_primitives::{Address, B256, ChainId}; use alloy_signer::Result; use tokio::sync::Mutex; @@ -15,7 +15,7 @@ use crate::wallet_browser::{ }; #[derive(Clone, Debug)] -pub struct BrowserSigner { +pub struct BrowserSigner { server: Arc>>, address: Address, chain_id: ChainId, diff --git a/crates/wallets/src/wallet_multi/mod.rs b/crates/wallets/src/wallet_multi/mod.rs index 03d5855ea873e..3ad8e9edbc70d 100644 --- a/crates/wallets/src/wallet_multi/mod.rs +++ b/crates/wallets/src/wallet_multi/mod.rs @@ -4,7 +4,7 @@ use crate::{ utils, wallet_browser::signer::BrowserSigner, }; -use alloy_network::{Ethereum, Network}; +use alloy_network::Network; use alloy_primitives::map::AddressHashMap; use alloy_signer::Signer; use clap::Parser; @@ -15,35 +15,19 @@ use serde::Serialize; use std::path::PathBuf; /// Container for multiple wallets. -#[derive(Debug)] -pub struct MultiWallet { +#[derive(Debug, Default)] +pub struct MultiWallet { /// Vector of wallets that require an action to be unlocked. /// Those are lazily unlocked on the first access of the signers. pending_signers: Vec, /// Contains unlocked signers. signers: AddressHashMap, - /// Browser signer - browser: Option>, } -impl Default for MultiWallet { - fn default() -> Self { - Self { - pending_signers: Default::default(), - signers: Default::default(), - browser: Default::default(), - } - } -} - -impl MultiWallet { - pub fn new( - pending_signers: Vec, - signers: Vec, - browser: Option>, - ) -> Self { +impl MultiWallet { + pub fn new(pending_signers: Vec, signers: Vec) -> Self { let signers = signers.into_iter().map(|signer| (signer.address(), signer)).collect(); - Self { pending_signers, signers, browser } + Self { pending_signers, signers } } fn maybe_unlock_pending(&mut self) -> Result<()> { @@ -54,18 +38,14 @@ impl MultiWallet { Ok(()) } - pub fn signers( - &mut self, - ) -> Result<(&AddressHashMap, Option<&BrowserSigner>)> { + pub fn signers(&mut self) -> Result<&AddressHashMap> { self.maybe_unlock_pending()?; - Ok((&self.signers, self.browser.as_ref())) + Ok(&self.signers) } - pub fn into_signers( - mut self, - ) -> Result<(AddressHashMap, Option>)> { + pub fn into_signers(mut self) -> Result> { self.maybe_unlock_pending()?; - Ok((self.signers, self.browser)) + Ok(self.signers) } pub fn add_signer(&mut self, signer: WalletSigner) { @@ -260,10 +240,9 @@ pub struct MultiWalletOpts { impl MultiWalletOpts { /// Returns [MultiWallet] container configured with provided options. - pub async fn get_multi_wallet(&self) -> Result> { + pub async fn get_multi_wallet(&self) -> Result { let mut pending = Vec::new(); let mut signers: Vec = Vec::new(); - let browser = self.browser_signer().await?; if let Some(ledgers) = self.ledgers().await? { signers.extend(ledgers); @@ -300,7 +279,7 @@ impl MultiWalletOpts { )); } - Ok(MultiWallet::new(pending, signers, browser)) + Ok(MultiWallet::new(pending, signers)) } pub fn private_keys(&self) -> Result>> { diff --git a/deny.toml b/deny.toml index b62a49e7297a9..641b5cd5175bd 100644 --- a/deny.toml +++ b/deny.toml @@ -110,4 +110,5 @@ allow-git = [ "https://github.com/tempoxyz/tempo", # Transitive dependency of Tempo "https://github.com/paradigmxyz/reth", + "https://github.com/stevencartavia/reth", ] diff --git a/flake.lock b/flake.lock index 550ffb9d26ef9..7f516069e6e23 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1773471952, - "narHash": "sha256-kIRggXyT8RzijtfvyRIzj+zIDWM2fnCp8t0X4BkkTVc=", + "lastModified": 1774076307, + "narHash": "sha256-v8axK9HGgVERw9oG3SKdsuE+ri0GPUZDyRBN4GLqQ1c=", "owner": "nix-community", "repo": "fenix", - "rev": "a1b770adbc3f6c27485d03b90462ec414d4e1ce5", + "rev": "556198cc6c69c0a13228a15e33b2360f333b0092", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1773476965, - "narHash": "sha256-Laaj25PvGeoP5SPhMfMGxvWqM6ZjZ6LdUeEhP6b3czY=", + "lastModified": 1773840656, + "narHash": "sha256-9tpvMGFteZnd3gRQZFlRCohVpqooygFuy9yjuyRL2C0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f82ce7af0b79ac154b12e27ed800aeb97413723c", + "rev": "9cf7092bdd603554bd8b63c216e8943cf9b12512", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1773326183, - "narHash": "sha256-tj3piRd9RnnP36HwHmQD4O4XZeowsH/rvMeyp9Pmot0=", + "lastModified": 1774036669, + "narHash": "sha256-EWhsBSh/h1VcyLKXuTEyH8lNVB2ktuKVkqx8dkQ6hxk=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "6254616e97f358e67b70dfc0463687f5f7911c1a", + "rev": "0cf3e8a07f0e29825f5db78840e646a4bb519742", "type": "github" }, "original": { diff --git a/foundryup/foundryup b/foundryup/foundryup index 2dcbb5c2f6d0b..5aadb284917bb 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -3,7 +3,7 @@ set -eo pipefail # NOTE: if you make modifications to this script, please increment the version number. # WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date. -FOUNDRYUP_INSTALLER_VERSION="1.5.0" +FOUNDRYUP_INSTALLER_VERSION="1.6.1" BASE_DIR=${XDG_CONFIG_HOME:-$HOME} FOUNDRY_DIR=${FOUNDRY_DIR:-"$BASE_DIR/.foundry"} @@ -334,7 +334,7 @@ main() { ensure mkdir -p "$FOUNDRY_VERSIONS_DIR" # Download and extract the binaries archive - say "downloading forge and cast for $FOUNDRYUP_TAG version" + say "downloading forge, cast, anvil, and chisel for $FOUNDRYUP_TAG version" if [ "$PLATFORM" = "win32" ]; then tmp="$(mktemp -d 2>/dev/null)" || err "failed to create temp dir" tmp="$tmp/foundry.zip" diff --git a/testdata/default/cheats/CurrentFilePath.t.sol b/testdata/default/cheats/CurrentFilePath.t.sol new file mode 100644 index 0000000000000..6628c5f455fc2 --- /dev/null +++ b/testdata/default/cheats/CurrentFilePath.t.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "utils/Test.sol"; + +contract CurrentFilePathTest is Test { + function testCurrentFilePath() public { + string memory filePath = vm.currentFilePath(); + // The path should be relative to the project root and point to this test file. + assertEq(normalizePath(filePath), "default/cheats/CurrentFilePath.t.sol"); + } + + function testCurrentFilePathIsNotEmpty() public { + string memory filePath = vm.currentFilePath(); + assertTrue(bytes(filePath).length > 0, "currentFilePath() should not return an empty string"); + } + + function normalizePath(string memory path) internal pure returns (string memory) { + return vm.replace(path, "\\", "/"); + } +} diff --git a/testdata/default/cheats/Json.t.sol b/testdata/default/cheats/Json.t.sol index c187e07465bb1..44bba4025e82d 100644 --- a/testdata/default/cheats/Json.t.sol +++ b/testdata/default/cheats/Json.t.sol @@ -510,4 +510,19 @@ contract WriteJsonTest is Test { vm.removeFile(path); vm.writeJson("{\"a\": 123, \"b\": \"0x000000000000000000000000000000000000bEEF\"}", path); } + + function test_writeJson_createFile() public { + string memory path = "fixtures/Json/write_test_nonexistent.json"; + + // Write to a file that does not exist using the 3-arg overload + vm.writeJson(vm.toString(uint256(99)), path, ".x.y"); + + // Verify the file was created with the correct content + string memory json = vm.readFile(path); + uint256 value = abi.decode(vm.parseJson(json, ".x.y"), (uint256)); + assertEq(value, 99); + + // Clean up + vm.removeFile(path); + } } diff --git a/testdata/utils/Vm.sol b/testdata/utils/Vm.sol index e4b11e8d42e64..d9f9b52821f52 100644 --- a/testdata/utils/Vm.sol +++ b/testdata/utils/Vm.sol @@ -192,6 +192,7 @@ interface Vm { function createWallet(string calldata walletLabel) external returns (Wallet memory wallet); function createWallet(uint256 privateKey) external returns (Wallet memory wallet); function createWallet(uint256 privateKey, string calldata walletLabel) external returns (Wallet memory wallet); + function currentFilePath() external view returns (string memory path); function deal(address account, uint256 newBalance) external; function deleteSnapshot(uint256 snapshotId) external returns (bool success); function deleteSnapshots() external; From e4788762ab2c6fe89d4cd8fe4199b10fee8ff557 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:50:42 +0700 Subject: [PATCH 267/374] Potential fix for pull request finding 'CodeQL / Cleartext logging of sensitive information' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/fmt/src/ui.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index 36407b3f0c9f2..83144a8714870 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -1743,8 +1743,8 @@ yParity 0" ); let pretty = pretty_generic_header_response(block.header()); - assert!(pretty.contains("difficulty 1"), "{pretty}"); - assert!(pretty.contains("totalDifficulty 163591"), "{pretty}"); + assert!(pretty.contains("difficulty 1")); + assert!(pretty.contains("totalDifficulty 163591")); } #[test] From 363b5b008e823cc7522d0295f72b407772ce7756 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:51:30 +0700 Subject: [PATCH 268/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/forge/src/cmd/install.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/forge/src/cmd/install.rs b/crates/forge/src/cmd/install.rs index b56d774a2b2ca..26088a5263f0c 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -359,6 +359,20 @@ impl Installer<'_> { } fn remove_nested_git_dirs_inner(root: &Path, dir: &Path) -> Result<()> { + // Ensure we never recurse outside of the original root directory. + // If canonicalization fails or dir is not under root, stop recursing. + let root_canon = match root.canonicalize() { + Ok(p) => p, + Err(_) => return Ok(()), + }; + let dir_canon = match dir.canonicalize() { + Ok(p) => p, + Err(_) => return Ok(()), + }; + if !dir_canon.starts_with(&root_canon) { + return Ok(()); + } + let entries = match std::fs::read_dir(dir) { Ok(entries) => entries, Err(_) => return Ok(()), From 72d68d8329437586dc7f21fc5e7fcc08d4e81fa9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:52:13 +0700 Subject: [PATCH 269/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/forge/tests/cli/install.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/forge/tests/cli/install.rs b/crates/forge/tests/cli/install.rs index 838d791c5d0e7..ae9582b9b1fa0 100644 --- a/crates/forge/tests/cli/install.rs +++ b/crates/forge/tests/cli/install.rs @@ -619,19 +619,19 @@ forgetest!(flaky_install_no_git_cleans_nested_submodules, |prj, cmd| { } // There should be no .git file or directory anywhere under the installed dependency. - fn assert_no_git(dir: &Path) { + fn assert_no_git(dir: &Path, root: &Path) { for entry in fs::read_dir(dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); if path.file_name() == Some(".git".as_ref()) { panic!("found leftover .git at {}", path.display()); } - if path.is_dir() { - assert_no_git(&path); + if path.is_dir() && path.starts_with(root) { + assert_no_git(&path, root); } } } - assert_no_git(&dep_dir); + assert_no_git(&dep_dir, &dep_dir); }); forgetest_init!(sync_on_forge_update, |prj, cmd| { From 23c90411943f361df75001ca0f14129b7f3f3924 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:56:21 +0700 Subject: [PATCH 270/374] Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/evm/traces/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 2c5965b1141f7..ebdbc6265e1fd 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -375,7 +375,7 @@ impl TraceMode { 3..=4 => std::cmp::max(self, Self::Call), // Enable step recording and state diff recording when verbosity is 5 or higher. // This includes backtraces (JUMP/JUMPDEST steps) and storage changes. - _ => std::cmp::max(self, Self::RecordStateDiff), + _ => if self == Self::Debug { self } else { std::cmp::max(self, Self::RecordStateDiff) }, } } From 786a917874122a5a89848472d58554c776e701f3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 9 Apr 2026 06:57:23 +0700 Subject: [PATCH 271/374] Update crates/anvil/src/eth/backend/executor.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/anvil/src/eth/backend/executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index d583933717567..6ebbd4a786b31 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -228,7 +228,7 @@ where } let receipt = if tx_type == FoundryTxType::Deposit { - let deposit_nonce = state.get(&sender).map(|acc| acc.info.nonce); + let deposit_nonce = state.get(&sender).map(|acc| acc.info.nonce.saturating_sub(1)); let receipt = alloy_consensus::Receipt { status: Eip658Value::Eip658(result.is_success()), cumulative_gas_used: self.gas_used, From 75e51091803fcd419e115df014ba65edcd634fcb Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:21:09 +0700 Subject: [PATCH 272/374] Change branch trigger from 'main' to 'master' (#403) https://github.com/Dargon789/hardhat-project/issues/2073 https://github.com/Dargon789/hardhat-project/pull/2078/commits/a98e69d5803ec208947f592c91749f248e2dd07c Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .github/workflows/Docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index 5a2330e7d5d62..7b85ca2ae00c8 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -4,7 +4,7 @@ on: push: tags: ["*"] branches: - - "main" + - "master" pull_request: branches: ["**"] From 98cd6697e3a2ed41d9cdb7a5a2f117bda69f9c78 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:34:55 +0700 Subject: [PATCH 273/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/util.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 015a127525767..c1f56b38d9d5d 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -4,7 +4,7 @@ use std::{ env, fs::{self, File}, io::{Read, Seek, Write}, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf}, process::Command, sync::LazyLock, }; @@ -257,7 +257,33 @@ fn copy_dir_filtered_inner( "attempted to access path outside of allowed source base directory", )); } - let dst_path = dst.join(entry.file_name()); + let relative_src_path = canonical_src_path.strip_prefix(base_src).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "failed to derive relative path within source base directory", + ) + })?; + for component in relative_src_path.components() { + match component { + Component::Normal(name) => { + let name = name.to_string_lossy(); + if name.contains("..") || name.contains('/') || name.contains('\\') { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "invalid path component in source entry", + )); + } + } + Component::CurDir => {} + Component::ParentDir | Component::RootDir | Component::Prefix(_) => { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + } + } + let dst_path = base_dst.join(relative_src_path); if ty.is_dir() { // Skip build artifact directories at the root level From 0a1121d66a1b62298ac20f739bd7982749ee4962 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:30:54 +0700 Subject: [PATCH 274/374] Create apisec-scan.yml (#404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol * Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#345) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 110: Uncontrolled data used in path expression (#347) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 102: Artifact poisoning (#351) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md (#350) * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 109: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> … * Potential fix for code scanning alert no. 102: Artifact poisoning (#354) * Potential fix for code scanning alert no. 102: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#357) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * refactor(common): make ProviderBuilder generic over Network #13250 (#361) * refactor(common): make `ProviderBuilder` generic over `Network` (#13250) * refactor(common): make `ProviderBuilder` generic over `Network` - Updated `ProviderBuilder` helper to be generic, which will facilate `FoundryNetwork` rollout - Adjusted the instantiation of `ProviderBuilder` in various locations to use `AnyNetwork`. - Enhanced the `build` and `build_with_wallet` methods to accommodate the new generic structure. * fix: relax trait bound on `N::TransactionRequest` * fix: comment * fix: comment * fix(anvil): return error instead of empty vec for out-of-range log queries (#13251) * fix(config): respect user-configured etherscan URL over chain defaults (#13239) fix(config): Respect user-configured etherscan URL over chain defaults (#13238) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` (#13218) * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` * fix: after `TransactionBuilder4844` impl * feat(common): introduce generic `ProviderBuilder::from_config` method (#13268) - keep the existing helpers that erase generic to avoid breaking change * fix(cast): remove redundant chain() call in explorer_client (#13272) Co-authored-by: tefyosL-sol * fix(verify): respect user-configured etherscan URL over chain defaults (#13275) Co-authored-by: tefyosL-sol * Update flake.lock (#13279) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/93523fa' (2026-01-24) → 'github:nix-community/fenix/b2344f3' (2026-01-31) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) → 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) → 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) Co-authored-by: github-actions[bot] * fix(invariant): remove unused cloned calldata (#12893) * fix(invariant): prune calldata to bound memory usage in long runs * style: fix formatting in invariant executor * chore: remove vyper files from testdata to fix CI * chore: trigger ci update * fix(invariant): remove unused FuzzCase.calldata field to prevent OOM The calldata field in FuzzCase was stored but never read after construction. Removing it entirely eliminates memory accumulation during long invariant runs. Changes: - Remove FuzzCase.calldata field (unused after construction) - Remove prune_calldata() methods (no longer needed) - Restore vyper test files that were incorrectly deleted Fixes #12397 Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(debugger): display actual gas usage alongside refund counter (#13271) * feat(debugger): display actual gas usage alongside refund counter * fix(forge-test): fix flamegraph gas inaccuracy issues * reverse `--decode-internal` not default with `--flamechart` * make gas unsigned as `inferno` doesn't support anyway * replace revm-inspectors patch * fix clippy * update tests * update tests * fmt * fix(config): handle decimal string in U256 deserialization (#13284) * fix: adjust numerical cells in gas report to be right aligned (#12883) * Adjust Cells to be Right Aligned in Gas Report * fix test and fmt --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): weekly `cargo update` (#13280) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v1.5.2 -> v1.5.4 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-json-abi v1.5.2 -> v1.5.4 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-primitives v1.5.2 -> v1.5.4 Updating alloy-sol-macro v1.5.2 -> v1.5.4 Updating alloy-sol-macro-expander v1.5.2 -> v1.5.4 Updating alloy-sol-macro-input v1.5.2 -> v1.5.4 Updating alloy-sol-type-parser v1.5.2 -> v1.5.4 Updating alloy-sol-types v1.5.2 -> v1.5.4 Updating annotate-snippets v0.12.10 -> v0.12.11 Updating aws-smithy-async v1.2.7 -> v1.2.10 Updating aws-smithy-http-client v1.1.5 -> v1.1.8 Updating aws-smithy-observability v0.2.0 -> v0.2.3 Updating aws-smithy-query v0.60.9 -> v0.60.12 Updating aws-smithy-runtime-api v1.10.0 -> v1.11.2 Updating aws-smithy-types v1.3.6 -> v1.4.2 Updating bytemuck v1.24.0 -> v1.25.0 Updating cc v1.2.54 -> v1.2.55 Updating clap v4.5.54 -> v4.5.56 Updating clap_builder v4.5.54 -> v4.5.56 Updating clap_derive v4.5.49 -> v4.5.55 Updating cliclack v0.3.7 -> v0.3.8 Updating find-msvc-tools v0.1.8 -> v0.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating iana-time-zone v0.1.64 -> v0.1.65 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating keccak-asm v0.1.4 -> v0.1.5 Unchanged matchit v0.8.4 (available: v0.8.6) Updating notify-types v2.0.0 -> v2.1.0 Updating portable-atomic v1.13.0 -> v1.13.1 Updating portable-atomic-util v0.2.4 -> v0.2.5 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating revm-inspectors v0.34.0 -> v0.34.2 Updating sha3-asm v0.1.4 -> v0.1.5 Updating siphasher v1.0.1 -> v1.0.2 Updating slab v0.4.11 -> v0.4.12 Updating syn-solidity v1.5.2 -> v1.5.4 Removing tiny-keccak v2.0.2 Updating zerocopy v0.8.33 -> v0.8.37 Updating zerocopy-derive v0.8.33 -> v0.8.37 Updating zmij v1.0.16 -> v1.0.18 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: update RPC URLs from ithaca.xyz to reth.rs (#13261) * chore: update RPC URLs from ithaca.xyz to reth.rs Co-authored-by: Tim Beiko Amp-Thread-ID: https://ampcode.com/threads/T-019c0a51-3f0a-76eb-ba4a-bfb6a697d9ba Co-authored-by: Amp * fix * fmt * Update rpc.rs * Update rpc.rs * test: update test --------- Co-authored-by: Tim Beiko Co-authored-by: Amp Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg * ci(bench): use depot runner for benchmarks (#13288) * feat(cli): cli markdown docs (#13291) * feat: add foundry-cli-markdown crate Add a new crate for generating Markdown documentation from clap CLIs. This is a fork of clap-markdown with the following enhancements: - Support for grouped options by help heading (PR #48) - Show environment variable names for arguments (PR #50) - Add version information to generated Markdown (PR #52) * feat(cli): add hidden --markdown-help flag Add a hidden --markdown-help flag to forge, cast, anvil, and chisel that prints CLI reference documentation as Markdown and exits. This uses the new foundry-cli-markdown crate to generate the output. * chore(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#13298) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/5e57cd118135c172c3672efd75eb46360885c0ef...c94ce9fb468520275223c153574b00df6fe4bcc9) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.13 to 2.67.18 (#13297) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.13 to 2.67.18. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/710817a1645ef40daad5bcde7431ceccf6cc3528...650c5ca14212efbbf3e580844b04bdccf68dac31) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 (#13299) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/727cc5b0b19bc265bd5ef28fc66bccb284473b5d...5adeaaaf36f64df54f62adb34aa5fbfdb0109d34) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.0.1 to 6.1.0 (#13300) chore(deps): bump mikepenz/release-changelog-builder-action Bumps [mikepenz/release-changelog-builder-action](https://github.com/mikepenz/release-changelog-builder-action) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/mikepenz/release-changelog-builder-action/releases) - [Commits](https://github.com/mikepenz/release-changelog-builder-action/compare/439f79b5b5428107c7688c1d2b0e8bacc9b8792c...6faf020194b7c8853f9e55c4fd92e40b02122a04) --- updated-dependencies: - dependency-name: mikepenz/release-changelog-builder-action dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: fix typos CI (#13303) - Add consts to typos ignore list (valid Rust std::env::consts) - Fix LintCotext -> LintContext typos in linter docs * chore(deps): bump crate-ci/typos from 1.42.2 to 1.43.0 (#13296) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.2 to 1.43.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/a1d64977b4aa1709d6328d518aa753f4899352d8...93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * sec: bump to `bytes` `^1.11.1` for `RUSTSEC-2026-0007` (#13306) bump to 1.11.1 for patch: https://rustsec.org/advisories/RUSTSEC-2026-0007 * chore(anvil,cast): remove unnecessary `populate_blob_hashes()` (#13308) Both `set_blob_sidecar`/`set_blob_sidecar_7594` implement a call to `populate_blob_hashes()` * feat(forge): generate random fuzz seed if none provided (#13309) * feat(forge): generate random fuzz seed if none provided Generate a random seed for fuzz/invariant tests when no seed is explicitly configured. This ensures reproducibility by always having a seed available that can be passed via --fuzz-seed to reproduce test runs. * feat(forge): print fuzz seed on fuzz failure (#13310) * feat(forge): print fuzz seed on test failure When a fuzz or invariant test fails, print the seed used so users can reproduce the failure with --fuzz-seed. * test: update snapshots for fuzz seed output Add [SEED] redaction pattern to match 'Fuzz seed: 0x...' output. Update all test snapshots that have fuzz/invariant failures to include the new seed line. * fix: use [SEED] placeholder in issue_3055 test snapshot Amp-Thread-ID: https://ampcode.com/threads/T-019c26cb-9d21-74f9-9e49-7ea59885e827 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(anvil): cache block timestamp in mined receipts (#13311) * fix: use shared `display_chain` helper in CLI error handler (#13314) * Update handler.rs * Update handler.rs * Add --enable-tx-gas-limit CLI flag for EIP-7825 support (#13307) add --enable-tx-gas-limit * fix(anvil): use consistent chain_id fallback in fork setup (#13276) Co-authored-by: tefyosL-sol * fix(test-utils): skip build artifacts when copying to temp workspace (#13266) * feat(lint): add common uppercase abbreviations to mixedCase exceptions (#13305) Co-authored-by: onbjerg * fix(fmt): correct indentation for closing brace in empty contracts with comments (#13319) Co-authored-by: onbjerg * feat(anvil): add `trace_replayBlockTransactions` endpoint for block txs tracing (#13098) Co-authored-by: onbjerg * fix(eip712): write diagnostics to stderr instead of stdout (#13293) Co-authored-by: onbjerg --------- Signed-off-by: dependabot[bot] Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Matt D Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Tim Beiko Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#373) * Potential fix for code scanning alert no. 108: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Forge/master (#376) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar Co-authored-by: googleworkspace-bot * chore(deps): bump the cargo group across 1 directory with 9 updates (#377) Bumps the cargo group with 3 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing), [keccak](https://github.com/RustCrypto/sponges) and [slab](https://github.com/tokio-rs/slab). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `time` from 0.3.41 to 0.3.47 - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.41...v0.3.47) Updates `alloy-dyn-abi` from 1.3.0 to 1.5.7 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.3.0...v1.5.7) Updates `bytes` from 1.10.1 to 1.11.1 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.1...v1.11.1) Updates `keccak` from 0.1.5 to 0.1.6 - [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6) Updates `lru` from 0.12.5 to 0.16.3 - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.12.5...0.16.3) Updates `protobuf` from 3.3.0 to 3.7.2 Updates `ruint` from 1.15.0 to 1.17.2 - [Release notes](https://github.com/recmo/uint/releases) - [Changelog](https://github.com/recmo/uint/blob/main/CHANGELOG.md) - [Commits](https://github.com/recmo/uint/compare/v1.15.0...v1.17.2) Updates `slab` from 0.4.10 to 0.4.12 - [Release notes](https://github.com/tokio-rs/slab/releases) - [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.12) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: time dependency-version: 0.3.47 dependency-type: direct:production dependency-group: cargo - dependency-name: alloy-dyn-abi dependency-version: 1.5.7 dependency-type: direct:production dependency-group: cargo - dependency-name: bytes dependency-version: 1.11.1 dependency-type: direct:production dependency-group: cargo - dependency-name: keccak dependency-version: 0.1.6 dependency-type: indirect dependency-group: cargo - dependency-name: lru dependency-version: 0.16.3 dependency-type: indirect dependency-group: cargo - dependency-name: protobuf dependency-version: 3.7.2 dependency-type: indirect dependency-group: cargo - dependency-name: ruint dependency-version: 1.17.2 dependency-type: indirect dependency-group: cargo - dependency-name: slab dependency-version: 0.4.12 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create static.yml (#381) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Hardhat project (#389) * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot * fix: update gas_params when changing spec via set_spec (#14210) * Change branch trigger from 'main' to 'master' (#403) https://github.com/Dargon789/hardhat-project/issues/2073 https://github.com/Dargon789/hardhat-project/pull/2078/commits/a98e69d5803ec208947f592c91749f248e2dd07c Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat(evm): add Tempo labels/fns/events to `CallTraceDecoder` (#14213) * fix(cast): resolve correct hardfork for historical tx execution (#14207) * fix(cast): resolve correct hardfork for historical tx execution * chore: add TODOs --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * perf(traces): skip precompile addresses in identify_addresses (#14198) * perf(traces): skip precompile addresses in identify_addresses Filter precompile addresses before sending to external identifiers (Sourcify/Etherscan), matching what prefetch_signatures already does. Also cache unknown fetch errors so they aren't re-fetched on subsequent trace arenas, and fix the cache merge logic to never downgrade a successful metadata lookup to None. Fixes #14196 Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * perf(traces): add Tempo precompiles to is_known_precompile Extend is_known_precompile to also cover Tempo precompile addresses and TIP20 fee token addresses. These are system contracts that will never resolve on Sourcify/Etherscan and should be skipped during external identification. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * refactor(traces): make is_known_precompile chain-aware Thread chain_id through CallTraceDecoder so is_known_precompile can gate network-specific precompiles by chain. Tempo precompiles and TIP20 tokens now only match on Tempo chains (4217, moderato, testnet), not on Ethereum mainnet or other chains. Add with_chain_id to CallTraceDecoderBuilder and wire it up in forge script, forge test, cast, and chisel. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * feat(traces): add Celo transfer precompile to is_known_precompile Add CELO_TRANSFER (0x...fd) to foundry-evm-core precompiles and gate it on Celo/CeloSepolia chain IDs in is_known_precompile. Optimism has no unique precompile addresses beyond P256VERIFY (0x100) which is already covered by the standard EVM precompile list. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * perf(traces): use raw chain ID matches instead of config parsing Replace foundry_config::Chain::from_id() calls with direct integer matches in is_known_precompile to avoid config parsing overhead on every trace node. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * refactor(traces): revert to Chain::from_id for readability Chain::from_id() is a cheap enum lookup, not a config parse. Revert to the more readable/maintainable approach. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * chore(traces): import Chain and NamedChain at top level Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 * chore: fix nightly rustfmt formatting Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d6da9-deee-77ac-a6fb-263872687ac6 --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(script): streamline `EvmOpts` resolution (#14217) * feat(evm): add `OpEvmNetwork` (#14214) fix(cast): decouple provider network from EVM network in `cast run` * feat(cli): `--tempo.fee-token` token id parsing (#14219) * fix(evm): use `AnyNetwork` for fork backend (#14220) fix(evm): use `AnyNetwork` for fork backend RPC layer * feat(forge): propagate `fee_token` to `deploy_tokens` in `CreateArgs::deploy` (#14221) * fix(evm): proper `fee_token` handling in `InspectorHandler` & `Cheatcodes` (#14225) * feat(cast): full `cast keychain` suite (#14222) * feat(evm): `FromAnyRpcTransaction` helper trait (#14223) * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: googleworkspace-bot Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: figtracer --- .cargo/config.toml | 6 +- .circleci/cargo.yml | 32 + .circleci/ci-web3-gamefi.yml | 26 + .circleci/ci.yml | 31 + .circleci/ci_cargo.yml | 37 + .circleci/ci_deploy.yml | 34 + .circleci/ci_v1.yml | 31 + .circleci/config.yml | 32 + .circleci/dev_stage.yml | 70 ++ .circleci/web3_defi_gamefi.yml | 26 + .codesandbox/tasks.json | 7 + .deps/remix-tests/remix_accounts.sol | 39 + .deps/remix-tests/remix_tests.sol | 225 +++++ .github/ISSUE_TEMPLATE/bug_report.md | 39 + .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/workflows/Docker.yml | 62 ++ .github/workflows/apisec-scan.yml | 29 + .github/workflows/codeql.yml | 92 ++ .github/workflows/deploy.yml | 27 + .github/workflows/docker.yml | 62 ++ .github/workflows/google.yml | 117 +++ .github/workflows/npm.yml | 37 + .github/workflows/snyk-container.yml | 55 + .github/workflows/static.yml | 43 + .gitmodules | 6 + Cargo.lock | 7 + Cargo.toml | 2 + benches/LATEST.md | 71 +- benches/src/lib.rs | 16 +- counter/.github/workflows/test.yml | 43 + counter/.gitignore | 14 + counter/README.md | 66 ++ counter/foundry.toml | 6 + counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 + counter/src/Counter.sol | 14 + counter/test/Counter.t.sol | 24 + crates/anvil/src/config.rs | 22 +- crates/cast/src/cmd/call.rs | 12 +- crates/cast/src/cmd/keychain.rs | 544 ++++++++-- crates/cast/src/cmd/run.rs | 23 +- crates/cast/src/debug.rs | 3 +- crates/cast/tests/cli/main.rs | 106 +- .../cheatcodes/assets/cheatcodes.schema.json | 2 +- crates/cheatcodes/spec/src/lib.rs | 4 +- crates/cheatcodes/src/config.rs | 16 +- crates/cheatcodes/src/inspector.rs | 26 +- crates/chisel/src/dispatcher.rs | 9 +- crates/chisel/src/executor.rs | 1 + crates/cli/src/opts/tempo.rs | 25 +- crates/cli/src/utils/mod.rs | 3 + crates/cli/src/utils/suggestions.rs | 3 +- crates/cli/src/utils/tempo.rs | 15 + crates/common/Cargo.toml | 3 + crates/common/src/contracts.rs | 2 +- crates/common/src/transactions/builder.rs | 16 + crates/common/src/transactions/receipt.rs | 7 + crates/config/Cargo.toml | 4 + crates/config/assets/config.schema.json | 6 + crates/config/spec/Cargo.toml | 28 + crates/config/spec/src/lib.rs | 64 ++ crates/config/src/lib.rs | 62 +- crates/doc/src/parser/comment.rs | 6 + crates/doc/src/writer/buf_writer.rs | 20 +- crates/evm/core/Cargo.toml | 5 +- crates/evm/core/src/backend/mod.rs | 78 +- crates/evm/core/src/env.rs | 271 ++++- crates/evm/core/src/evm.rs | 28 +- crates/evm/core/src/precompiles.rs | 5 + crates/evm/evm/src/executors/builder.rs | 18 +- crates/evm/evm/src/executors/trace.rs | 2 +- crates/evm/hardforks/Cargo.toml | 1 + crates/evm/hardforks/src/lib.rs | 70 +- crates/evm/traces/Cargo.toml | 3 + crates/evm/traces/src/decoder/mod.rs | 176 +++- crates/evm/traces/src/decoder/precompiles.rs | 40 +- crates/evm/traces/src/identifier/external.rs | 22 +- crates/forge/Cargo.toml | 1 + crates/forge/src/cmd/config.rs | 2 +- crates/forge/src/cmd/create.rs | 12 +- crates/forge/src/cmd/test/mod.rs | 17 +- crates/forge/src/multi_runner.rs | 1 + crates/forge/tests/cli/test_optimizer.rs | 1 - crates/lint/src/linter.rs | 129 +++ crates/lint/src/sol/gas/keccak.rs | 98 +- crates/script/src/execute.rs | 10 +- crates/script/src/lib.rs | 68 +- crates/script/src/runner.rs | 29 +- crates/test-utils/src/rpc.rs | 1 + crates/test-utils/src/script.rs | 29 +- crates/test-utils/src/util.rs | 104 +- crates/verify/src/utils.rs | 2 +- npm/package.json | 2 +- npm/scripts/stage-from-artifact.mjs | 28 +- npm/src/const.mjs | 30 +- sleep.json | 955 ++++++++++++++++++ testdata/default/repros/Issue14212.t.sol | 56 + testdata/foundry.toml | 1 + 100 files changed, 4379 insertions(+), 427 deletions(-) create mode 100644 .circleci/cargo.yml create mode 100644 .circleci/ci-web3-gamefi.yml create mode 100644 .circleci/ci.yml create mode 100644 .circleci/ci_cargo.yml create mode 100644 .circleci/ci_deploy.yml create mode 100644 .circleci/ci_v1.yml create mode 100644 .circleci/config.yml create mode 100644 .circleci/dev_stage.yml create mode 100644 .circleci/web3_defi_gamefi.yml create mode 100644 .codesandbox/tasks.json create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/Docker.yml create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/google.yml create mode 100644 .github/workflows/snyk-container.yml create mode 100644 .github/workflows/static.yml create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol create mode 100644 crates/cli/src/utils/tempo.rs create mode 100644 crates/config/assets/config.schema.json create mode 100644 crates/config/spec/Cargo.toml create mode 100644 crates/config/spec/src/lib.rs create mode 100644 crates/lint/src/linter.rs create mode 100644 sleep.json create mode 100644 testdata/default/repros/Issue14212.t.sol diff --git a/.cargo/config.toml b/.cargo/config.toml index 1ca035a75d78c..ca844fb33e15b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,12 @@ [alias] -cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-config = "test -p foundry-config-spec --features schema tests::" test-debugger = "test -p forge --test cli manual_debug_setup -- --include-ignored --nocapture" bless-lints = "test -p forge --test ui -- --bless" +# Backwards compatibility alias for `spec-cheats` +cheats = "spec-cheats" + # Increase the stack size to 10MB for Windows targets, which is in line with Linux # (whereas default for Windows is 1MB). [target.x86_64-pc-windows-msvc] diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..32b65e6a23cc5 --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,32 @@ +version: 2.1 +# +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..82c6de5b42b73 --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..4168efef0971f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..5ba351727d22b --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,70 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..53a505774ac88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml new file mode 100644 index 0000000000000..7b85ca2ae00c8 --- /dev/null +++ b/.github/workflows/Docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "master" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 766beae8cc8d7..96d83592a4cc0 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -233,9 +233,46 @@ jobs: PLATFORM='${{ matrix.os }}' ARCH='${{ matrix.arch }}' + # Basic validation of matrix-derived values to avoid path manipulation + case "$TOOL" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid TOOL value: $TOOL" >&2; exit 1 ;; + esac + case "$PLATFORM" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid PLATFORM value: $PLATFORM" >&2; exit 1 ;; + esac + case "$ARCH" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid ARCH value: $ARCH" >&2; exit 1 ;; + esac + PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" + echo "Preparing to publish package from: $PACKAGE_DIR" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 + exit 1 + fi + + # Resolve to an absolute path and ensure it stays within ./@foundry-rs + ABS_PACKAGE_DIR="$(realpath "$PACKAGE_DIR")" + ABS_EXPECTED_ROOT="$(realpath "./@foundry-rs")" + case "$ABS_PACKAGE_DIR" in + "$ABS_EXPECTED_ROOT"/*) ;; + *) + echo "ERROR: Resolved package directory is outside expected root:" >&2 + echo " ABS_PACKAGE_DIR=$ABS_PACKAGE_DIR" >&2 + echo " ABS_EXPECTED_ROOT=$ABS_EXPECTED_ROOT" >&2 + exit 1 + ;; + esac + ls -la "$PACKAGE_DIR" + # Minimal sanity check: require a package.json before publishing + if [[ ! -f "$PACKAGE_DIR/package.json" ]]; then + echo "ERROR: package.json not found in $PACKAGE_DIR; refusing to publish." >&2 + exit 1 + fi + bun ./scripts/publish.mjs "$PACKAGE_DIR" echo "Published @foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000000000..0ba82305f82b2 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/Cargo.lock b/Cargo.lock index f66ee73901ee1..03b5ad35d23ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4975,6 +4975,8 @@ dependencies = [ "itertools 0.14.0", "jiff", "num-format", + "op-alloy-network", + "op-alloy-rpc-types", "path-slash", "regex", "reqwest 0.13.2", @@ -5257,6 +5259,8 @@ dependencies = [ "futures", "itertools 0.14.0", "op-alloy-consensus", + "op-alloy-network", + "op-alloy-rpc-types", "op-revm", "parking_lot", "revm", @@ -5320,6 +5324,7 @@ dependencies = [ name = "foundry-evm-hardforks" version = "1.6.0" dependencies = [ + "alloy-chains", "alloy-hardforks", "alloy-op-hardforks", "alloy-rpc-types", @@ -5371,6 +5376,8 @@ dependencies = [ "serde_json", "solar-compiler", "tempfile", + "tempo-contracts", + "tempo-precompiles", "tokio", "tracing", "yansi", diff --git a/Cargo.toml b/Cargo.toml index 73efb0b87a05a..64a4f6617548c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/cli/", "crates/common/", "crates/config/", + "crates/config/spec/", "crates/debugger/", "crates/doc/", "crates/evm/core/", @@ -297,6 +298,7 @@ foundry-cli-markdown = { path = "crates/cli-markdown" } foundry-common = { path = "crates/common" } foundry-common-fmt = { path = "crates/common/fmt" } foundry-config = { path = "crates/config" } +foundry-config-spec = { path = "crates/config/spec" } foundry-debugger = { path = "crates/debugger" } foundry-evm = { path = "crates/evm/evm" } foundry-evm-abi = { path = "crates/evm/abi" } diff --git a/benches/LATEST.md b/benches/LATEST.md index 7ea1049a2ac41..00776abc94003 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,73 +1,28 @@ # Foundry Benchmark Results -**Date**: 2025-10-02 12:14:23 +**Date**: 2026-01-27 03:38:34 -## Repositories Tested +## Summary -1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -2. [Vectorized/solady](https://github.com/Vectorized/solady) -3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) -4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) - -## Foundry Versions - -- **v1.3.6**: forge Version: 1.3.6-v1.3.6 (d241588 2025-09-16) -- **v1.4.0-rc1**: forge Version: 1.4.0-v1.4.0-rc1 (bd0e4a7 2025-10-01) - -## Forge Test - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 3.17 s | 2.94 s | -| solady | 2.28 s | 2.10 s | -| Uniswap-v4-core | 7.27 s | 6.13 s | -| sparkdotfi-spark-psm | 43.04 s | 44.08 s | +Benchmarked 2 Foundry versions across 1 repository. -## Forge Fuzz Test +### Repositories Tested -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------ | ---------- | -| ithacaxyz-account | 3.18 s | 3.02 s | -| solady | 2.39 s | 2.24 s | -| Uniswap-v4-core | 6.84 s | 6.20 s | -| sparkdotfi-spark-psm | 3.07 s | 2.72 s | - -## Forge Test (Isolated) - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| solady | 2.26 s | 2.41 s | -| Uniswap-v4-core | 7.22 s | 7.71 s | -| sparkdotfi-spark-psm | 45.53 s | 50.49 s | +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -## Forge Build (No Cache) +### Foundry Versions -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 9.16 s | 9.08 s | -| solady | 14.62 s | 14.69 s | -| Uniswap-v4-core | 2m 3.8s | 2m 5.3s | -| sparkdotfi-spark-psm | 13.17 s | 13.14 s | +- **stable**: forge Version: 1.5.0-dev (6e718be 2025-12-07) +- **nightly**: forge Version: 1.5.0-dev (6e718be 2025-12-07) ## Forge Build (With Cache) -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 0.156 s | 0.113 s | -| solady | 0.089 s | 0.094 s | -| Uniswap-v4-core | 0.133 s | 0.127 s | -| sparkdotfi-spark-psm | 0.173 s | 0.131 s | - -## Forge Coverage - -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | -------- | ---------- | -| ithacaxyz-account | 14.91 s | 13.34 s | -| Uniswap-v4-core | 1m 34.8s | 1m 30.3s | -| sparkdotfi-spark-psm | 3m 49.3s | 3m 40.2s | +| Repository | stable | nightly | +|------------|----------|----------| +| ithacaxyz-account | 0.345 s | 0.279 s | ## System Information -- **OS**: macos +- **OS**: linux - **CPU**: 8 -- **Rustc**: rustc 1.90.0-nightly (3014e79f9 2025-07-15) +- **Rustc**: rustc 1.93.0 (254b59607 2026-01-19) diff --git a/benches/src/lib.rs b/benches/src/lib.rs index b4b3de2c3ddf3..810980d70ad72 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -130,10 +130,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 4863f5a82296e..03f4fcd05178c 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -37,10 +37,7 @@ use foundry_config::Config; use foundry_evm::{ backend::{BlockchainDb, BlockchainDbMeta, SharedBackend}, constants::DEFAULT_CREATE2_DEPLOYER, - hardfork::{ - FoundryHardfork, OpHardfork, ethereum_hardfork_from_block_tag, - spec_id_from_ethereum_hardfork, - }, + hardfork::{FoundryHardfork, OpHardfork}, utils::{ apply_chain_and_block_specific_env_changes, block_env_from_header, get_blob_base_fee_update_fraction, @@ -1269,16 +1266,8 @@ impl NodeConfig { let chain_id = if let Some(chain_id) = self.fork_chain_id { Some(chain_id) } else if self.hardfork.is_none() { - // Auto-adjust hardfork if not specified, but only if we're forking mainnet. let chain_id = provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?; - if alloy_chains::NamedChain::Mainnet == chain_id { - let hardfork: EthereumHardfork = - ethereum_hardfork_from_block_tag(fork_block_number); - - evm_env.cfg_env.spec = spec_id_from_ethereum_hardfork(hardfork); - self.hardfork = Some(FoundryHardfork::Ethereum(hardfork)); - } Some(U256::from(chain_id)) } else { None @@ -1344,6 +1333,15 @@ latest block number: {latest_block}" chain_id }; + // Auto-detect hardfork from chain activation data if not explicitly set. + if self.hardfork.is_none() + && let Some(hardfork) = + FoundryHardfork::from_chain_and_timestamp(chain_id, block.header.timestamp()) + { + evm_env.cfg_env.spec = SpecId::from(hardfork); + self.hardfork = Some(hardfork); + } + // if not set explicitly we use the base fee of the latest block if self.base_fee.is_none() && let Some(base_fee) = block.header.base_fee_per_gas() diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index df90641982630..6b0b1740da0ba 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -36,7 +36,7 @@ use foundry_config::{ use foundry_evm::{ core::{ FoundryBlock, FoundryTransaction, - evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork}, + evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork}, }, executors::TracingExecutor, opts::EvmOpts, @@ -224,7 +224,15 @@ impl CallArgs { if self.tx.tempo.is_tempo() { self.run_with_network::().await } else { - self.run_with_network::().await + let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); + let mut evm_opts = figment.extract::()?; + evm_opts.infer_network_from_fork().await; + + if evm_opts.networks.is_optimism() { + self.run_with_network::().await + } else { + self.run_with_network::().await + } } } diff --git a/crates/cast/src/cmd/keychain.rs b/crates/cast/src/cmd/keychain.rs index 6fdc1b1605606..c6a9646a6b2ab 100644 --- a/crates/cast/src/cmd/keychain.rs +++ b/crates/cast/src/cmd/keychain.rs @@ -1,10 +1,16 @@ -use alloy_network::TransactionBuilder; -use alloy_primitives::Address; -use alloy_provider::Provider; +use alloy_ens::NameOrAddress; +use alloy_network::EthereumWallet; +use alloy_primitives::{Address, U256, hex, keccak256}; +use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; +use alloy_signer::Signer; +use alloy_sol_types::{SolCall, sol}; use chrono::DateTime; use clap::Parser; use eyre::Result; -use foundry_cli::{opts::RpcOpts, utils::LoadConfig}; +use foundry_cli::{ + opts::{RpcOpts, TransactionOpts}, + utils::LoadConfig, +}; use foundry_common::{ FoundryTransactionBuilder, provider::ProviderBuilder, @@ -12,13 +18,57 @@ use foundry_common::{ tempo::{self, KeyType, KeysFile, WalletType, read_tempo_keys_file, tempo_keys_path}, }; use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; -use tempo_contracts::precompiles::IAccountKeychain::{KeyInfo, SignatureType}; +use tempo_contracts::precompiles::{ + ACCOUNT_KEYCHAIN_ADDRESS, IAccountKeychain, + IAccountKeychain::{KeyInfo, SignatureType, TokenLimit}, +}; use yansi::Paint; -use crate::{ - cmd::{erc20::build_provider_with_signer, send::cast_send}, - tx::{CastTxSender, SendTxOpts}, -}; +use crate::tx::{CastTxBuilder, CastTxSender, SendTxOpts}; + +// Extended AccountKeychain ABI for functions not yet in the pinned tempo-contracts. +// These types mirror the T3+ precompile interface. +sol! { + #[derive(Debug)] + struct SelectorRule { + bytes4 selector; + address[] recipients; + } + + #[derive(Debug)] + struct CallScope { + address target; + SelectorRule[] selectorRules; + } + + #[derive(Debug)] + struct ExtTokenLimit { + address token; + uint256 amount; + } + + #[derive(Debug)] + struct KeyRestrictions { + uint64 expiry; + bool enforceLimits; + ExtTokenLimit[] limits; + bool allowAnyCalls; + CallScope[] allowedCalls; + } + + function authorizeKeyWithRestrictions( + address keyId, + uint8 signatureType, + KeyRestrictions calldata config + ) external; + + function setAllowedCalls( + address keyId, + CallScope[] calldata scopes + ) external; + + function removeAllowedCalls(address keyId, address target) external; +} /// Tempo keychain management commands. /// @@ -37,6 +87,7 @@ pub enum KeychainSubcommand { }, /// Check on-chain provisioning status of a key via the AccountKeychain precompile. + #[command(visible_alias = "info")] Check { /// The wallet (account) address. wallet_address: Address, @@ -49,31 +100,123 @@ pub enum KeychainSubcommand { }, /// Authorize a new key on-chain via the AccountKeychain precompile. + #[command(visible_alias = "auth")] Authorize { /// The key address to authorize. key_address: Address, /// Signature type: secp256k1, p256, or webauthn. - #[arg(long, default_value = "secp256k1", value_parser = parse_signature_type)] + #[arg(default_value = "secp256k1", value_parser = parse_signature_type)] key_type: SignatureType, /// Expiry timestamp (unix seconds). Defaults to u64::MAX (never expires). - #[arg(long)] - expiry: Option, + #[arg(default_value_t = u64::MAX)] + expiry: u64, /// Enforce spending limits for this key. #[arg(long)] enforce_limits: bool, + /// Spending limit in TOKEN:AMOUNT format. Can be specified multiple times. + #[arg(long = "limit", value_parser = parse_limit)] + limits: Vec, + + /// Call scope restriction in `TARGET[:SELECTORS[@RECIPIENTS]]` format. + /// TARGET alone allows all calls. `TARGET:transfer,approve` restricts to those selectors. + /// `TARGET:transfer@0x123` restricts selector to specific recipients. + #[arg(long = "scope", value_parser = parse_scope)] + scope: Vec, + + /// Call scope restrictions as a JSON array. + /// Format: [{"target":"0x...","selectors":["transfer"]}] or + /// [{"target":"0x...","selectors":[{"selector":"transfer","recipients":["0x..."]}]}] + #[arg(long = "scopes", value_parser = parse_scopes_json, conflicts_with = "scope")] + scopes_json: Option>, + + #[command(flatten)] + tx: TransactionOpts, + #[command(flatten)] send_tx: SendTxOpts, }, /// Revoke an authorized key on-chain via the AccountKeychain precompile. + #[command(visible_alias = "rev")] Revoke { /// The key address to revoke. key_address: Address, + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Query the remaining spending limit for a key on a specific token. + #[command(name = "rl", visible_alias = "remaining-limit")] + RemainingLimit { + /// The wallet (account) address. + wallet_address: Address, + + /// The key address. + key_address: Address, + + /// The token address. + token: Address, + + #[command(flatten)] + rpc: RpcOpts, + }, + + /// Update the spending limit for a key on a specific token. + #[command(name = "ul", visible_alias = "update-limit")] + UpdateLimit { + /// The key address. + key_address: Address, + + /// The token address. + token: Address, + + /// The new spending limit. + new_limit: U256, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Set allowed call scopes for a key. + #[command(name = "ss", visible_alias = "set-scope")] + SetScope { + /// The key address. + key_address: Address, + + /// Call scope restriction in `TARGET[:SELECTORS[@RECIPIENTS]]` format. + #[arg(long = "scope", required = true, value_parser = parse_scope)] + scope: Vec, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Remove call scope for a key on a target. + #[command(name = "rs", visible_alias = "remove-scope")] + RemoveScope { + /// The key address. + key_address: Address, + + /// The target address to remove scope for. + target: Address, + + #[command(flatten)] + tx: TransactionOpts, + #[command(flatten)] send_tx: SendTxOpts, }, @@ -112,6 +255,149 @@ fn wallet_type_name(t: &WalletType) -> &'static str { } } +/// Parse a `--limit TOKEN:AMOUNT` flag value. +fn parse_limit(s: &str) -> Result { + let (token_str, amount_str) = s + .split_once(':') + .ok_or_else(|| format!("invalid limit format: {s} (expected TOKEN:AMOUNT)"))?; + let token: Address = + token_str.parse().map_err(|e| format!("invalid token address '{token_str}': {e}"))?; + let amount: U256 = + amount_str.parse().map_err(|e| format!("invalid amount '{amount_str}': {e}"))?; + Ok(TokenLimit { token, amount }) +} + +/// Parse a `--scope TARGET[:SELECTORS[@RECIPIENTS]]` flag value. +/// +/// Formats: +/// - `0xAddr` — allow all calls to target +/// - `0xAddr:transfer,approve` — allow only those selectors (by name or 4-byte hex) +/// - `0xAddr:transfer@0xRecipient` — selector with recipient restriction +fn parse_scope(s: &str) -> Result { + let (target_str, selectors_str) = match s.split_once(':') { + Some((t, sel)) => (t, Some(sel)), + None => (s, None), + }; + + let target: Address = + target_str.parse().map_err(|e| format!("invalid target address '{target_str}': {e}"))?; + + let selector_rules = match selectors_str { + None => vec![], + Some(sel_str) => parse_selector_rules(sel_str)?, + }; + + Ok(CallScope { target, selectorRules: selector_rules }) +} + +/// Parse comma-separated selectors, each optionally with `@recipient1,recipient2,...`. +/// +/// Example: `transfer,approve` or `transfer@0x123` or `0xd09de08a` +fn parse_selector_rules(s: &str) -> Result, String> { + let mut rules = Vec::new(); + + for part in s.split(',') { + let part = part.trim(); + if part.is_empty() { + continue; + } + + let (selector_str, recipients_str) = match part.split_once('@') { + Some((sel, recip)) => (sel, Some(recip)), + None => (part, None), + }; + + let selector = parse_selector_bytes(selector_str)?; + + let recipients = match recipients_str { + None => vec![], + Some(r) => r + .split(',') + .filter(|s| !s.trim().is_empty()) + .map(|addr_str| { + let addr_str = addr_str.trim(); + addr_str + .parse::
() + .map_err(|e| format!("invalid recipient address '{addr_str}': {e}")) + }) + .collect::, _>>()?, + }; + + rules.push(SelectorRule { selector: selector.into(), recipients }); + } + + Ok(rules) +} + +/// Parse a selector string: either a 4-byte hex (`0xd09de08a`) or a function name +/// (computed as the first 4 bytes of keccak256 of `name()`). +fn parse_selector_bytes(s: &str) -> Result<[u8; 4], String> { + let s = s.trim(); + if s.starts_with("0x") || s.starts_with("0X") { + let hex_str = &s[2..]; + if hex_str.len() != 8 { + return Err(format!("hex selector must be 4 bytes (8 hex chars), got: {s}")); + } + let bytes = hex::decode(hex_str).map_err(|e| format!("invalid hex selector '{s}': {e}"))?; + let mut arr = [0u8; 4]; + arr.copy_from_slice(&bytes); + Ok(arr) + } else { + let sig = if s.contains('(') { s.to_string() } else { format!("{s}()") }; + let hash = keccak256(sig.as_bytes()); + let mut arr = [0u8; 4]; + arr.copy_from_slice(&hash[..4]); + Ok(arr) + } +} + +/// Represents a single scope entry in JSON format for `--scopes`. +#[derive(serde::Deserialize)] +struct JsonCallScope { + target: Address, + #[serde(default)] + selectors: Option>, +} + +/// A selector entry can be either a plain string or an object with recipients. +#[derive(serde::Deserialize)] +#[serde(untagged)] +enum JsonSelectorEntry { + Name(String), + WithRecipients { selector: String, recipients: Vec
}, +} + +/// Parse `--scopes` JSON flag value. +fn parse_scopes_json(s: &str) -> Result, String> { + let entries: Vec = + serde_json::from_str(s).map_err(|e| format!("invalid --scopes JSON: {e}"))?; + + let mut scopes = Vec::new(); + for entry in entries { + let selector_rules = match entry.selectors { + None => vec![], + Some(sels) => { + let mut rules = Vec::new(); + for sel_entry in sels { + let (selector_str, recipients) = match sel_entry { + JsonSelectorEntry::Name(name) => (name, vec![]), + JsonSelectorEntry::WithRecipients { selector, recipients } => { + (selector, recipients) + } + }; + let selector = parse_selector_bytes(&selector_str) + .map_err(|e| format!("in --scopes JSON: {e}"))?; + rules.push(SelectorRule { selector: selector.into(), recipients }); + } + rules + } + }; + scopes.push(CallScope { target: entry.target, selectorRules: selector_rules }); + } + + Ok(scopes) +} + impl KeychainSubcommand { pub async fn run(self) -> Result<()> { match self { @@ -120,10 +406,44 @@ impl KeychainSubcommand { Self::Check { wallet_address, key_address, rpc } => { run_check(wallet_address, key_address, rpc).await } - Self::Authorize { key_address, key_type, expiry, enforce_limits, send_tx } => { - run_authorize(key_address, key_type, expiry, enforce_limits, send_tx).await + Self::Authorize { + key_address, + key_type, + expiry, + enforce_limits, + limits, + scope, + scopes_json, + tx, + send_tx, + } => { + let all_scopes = + if let Some(json_scopes) = scopes_json { json_scopes } else { scope }; + run_authorize( + key_address, + key_type, + expiry, + enforce_limits, + limits, + all_scopes, + tx, + send_tx, + ) + .await + } + Self::Revoke { key_address, tx, send_tx } => run_revoke(key_address, tx, send_tx).await, + Self::RemainingLimit { wallet_address, key_address, token, rpc } => { + run_remaining_limit(wallet_address, key_address, token, rpc).await + } + Self::UpdateLimit { key_address, token, new_limit, tx, send_tx } => { + run_update_limit(key_address, token, new_limit, tx, send_tx).await + } + Self::SetScope { key_address, scope, tx, send_tx } => { + run_set_scope(key_address, scope, tx, send_tx).await + } + Self::RemoveScope { key_address, target, tx, send_tx } => { + run_remove_scope(key_address, target, tx, send_tx).await } - Self::Revoke { key_address, send_tx } => run_revoke(key_address, send_tx).await, } } } @@ -181,7 +501,7 @@ fn run_show(wallet_address: Address) -> Result<()> { Ok(()) } -/// `cast keychain check` — query on-chain key status. +/// `cast keychain check` / `cast keychain info` — query on-chain key status. async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) -> Result<()> { let config = rpc.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; @@ -195,7 +515,7 @@ async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) "wallet_address": wallet_address.to_string(), "key_address": key_address.to_string(), "provisioned": provisioned, - "signature_type": signature_type_name(&info.signatureType), + "signatureType": signature_type_name(&info.signatureType), "key_id": info.keyId.to_string(), "expiry": info.expiry, "expiry_human": format_expiry(info.expiry), @@ -245,76 +565,166 @@ async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) Ok(()) } -/// `cast keychain authorize` — authorize a key on-chain. +/// `cast keychain authorize` / `cast keychain auth` — authorize a key on-chain. +#[allow(clippy::too_many_arguments)] async fn run_authorize( key_address: Address, key_type: SignatureType, - expiry: Option, + expiry: u64, enforce_limits: bool, + limits: Vec, + allowed_calls: Vec, + tx_opts: TransactionOpts, send_tx: SendTxOpts, ) -> Result<()> { - let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; + let enforce = enforce_limits || !limits.is_empty(); + + let calldata = if !allowed_calls.is_empty() { + // Use the T3+ authorizeKey overload with KeyRestrictions when scopes are provided. + let sig_type_u8 = match key_type { + SignatureType::Secp256k1 => 0u8, + SignatureType::P256 => 1u8, + SignatureType::WebAuthn => 2u8, + _ => eyre::bail!("unknown signature type"), + }; + let restrictions = KeyRestrictions { + expiry, + enforceLimits: enforce, + limits: limits + .into_iter() + .map(|l| ExtTokenLimit { token: l.token, amount: l.amount }) + .collect(), + allowAnyCalls: false, + allowedCalls: allowed_calls, + }; + authorizeKeyWithRestrictionsCall { + keyId: key_address, + signatureType: sig_type_u8, + config: restrictions, + } + .abi_encode() + } else { + // Use the legacy authorizeKey when no scopes are needed. + IAccountKeychain::authorizeKeyCall { + keyId: key_address, + signatureType: key_type, + expiry, + enforceLimits: enforce, + limits, + } + .abi_encode() + }; - let config = send_tx.eth.load_config()?; - let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); - let provider = ProviderBuilder::::from_config(&config)?.build()?; + send_keychain_tx(calldata, tx_opts, &send_tx).await +} - let keychain = provider.account_keychain(); - let mut tx = keychain - .authorizeKey(key_address, key_type, expiry.unwrap_or(u64::MAX), enforce_limits, vec![]) - .into_transaction_request(); +/// `cast keychain revoke` / `cast keychain rev` — revoke a key on-chain. +async fn run_revoke( + key_address: Address, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + let calldata = IAccountKeychain::revokeKeyCall { keyId: key_address }.abi_encode(); + send_keychain_tx(calldata, tx_opts, &send_tx).await +} - if let Some(ref access_key) = tempo_access_key { - let signer = signer.as_ref().expect("signer required for access key"); - tx.set_from(access_key.wallet_address); - tx.set_key_id(access_key.key_address); +/// `cast keychain rl` — query remaining spending limit. +async fn run_remaining_limit( + wallet_address: Address, + key_address: Address, + token: Address, + rpc: RpcOpts, +) -> Result<()> { + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; - let raw_tx = tx - .sign_with_access_key( - &provider, - signer, - access_key.wallet_address, - access_key.key_address, - access_key.key_authorization.as_ref(), - ) - .await?; + let remaining: U256 = + provider.get_keychain_remaining_limit(wallet_address, key_address, token).await?; - let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); - let cast = CastTxSender::new(&provider); - cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await?; + if shell::is_json() { + sh_println!("{}", serde_json::to_string(&remaining.to_string())?)?; } else { - let signer = signer.unwrap_or(send_tx.eth.wallet.signer().await?); - let provider = build_provider_with_signer::(&send_tx, signer)?; - cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) - .await?; + sh_println!("{remaining}")?; } Ok(()) } -/// `cast keychain revoke` — revoke a key on-chain. -async fn run_revoke(key_address: Address, send_tx: SendTxOpts) -> Result<()> { +/// `cast keychain ul` — update spending limit. +async fn run_update_limit( + key_address: Address, + token: Address, + new_limit: U256, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + let calldata = IAccountKeychain::updateSpendingLimitCall { + keyId: key_address, + token, + newLimit: new_limit, + } + .abi_encode(); + send_keychain_tx(calldata, tx_opts, &send_tx).await +} + +/// `cast keychain ss` — set allowed call scopes. +async fn run_set_scope( + key_address: Address, + scopes: Vec, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + let calldata = setAllowedCallsCall { keyId: key_address, scopes }.abi_encode(); + send_keychain_tx(calldata, tx_opts, &send_tx).await +} + +/// `cast keychain rs` — remove call scope for a target. +async fn run_remove_scope( + key_address: Address, + target: Address, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + let calldata = removeAllowedCallsCall { keyId: key_address, target }.abi_encode(); + send_keychain_tx(calldata, tx_opts, &send_tx).await +} + +/// Shared helper to send a keychain precompile transaction. +async fn send_keychain_tx( + calldata: Vec, + mut tx_opts: TransactionOpts, + send_tx: &SendTxOpts, +) -> Result<()> { let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; let config = send_tx.eth.load_config()?; let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); let provider = ProviderBuilder::::from_config(&config)?.build()?; - let keychain = provider.account_keychain(); - let mut tx = keychain.revokeKey(key_address).into_transaction_request(); + // Inject key_id for correct gas estimation with keychain signature overhead. + if let Some(ref ak) = tempo_access_key { + tx_opts.tempo.key_id = Some(ak.key_address); + } + + let builder = CastTxBuilder::new(&provider, tx_opts, &config) + .await? + .with_to(Some(NameOrAddress::Address(ACCOUNT_KEYCHAIN_ADDRESS))) + .await? + .with_code_sig_and_args(None, Some(hex::encode_prefixed(&calldata)), vec![]) + .await?; - if let Some(ref access_key) = tempo_access_key { + if let Some(ref ak) = tempo_access_key { let signer = signer.as_ref().expect("signer required for access key"); - tx.set_from(access_key.wallet_address); - tx.set_key_id(access_key.key_address); + let from = ak.wallet_address; + let (tx, _) = builder.build(from).await?; let raw_tx = tx .sign_with_access_key( &provider, signer, - access_key.wallet_address, - access_key.key_address, - access_key.key_authorization.as_ref(), + ak.wallet_address, + ak.key_address, + ak.key_authorization.as_ref(), ) .await?; @@ -322,10 +732,22 @@ async fn run_revoke(key_address: Address, send_tx: SendTxOpts) -> Result<()> { let cast = CastTxSender::new(&provider); cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await?; } else { - let signer = signer.unwrap_or(send_tx.eth.wallet.signer().await?); - let provider = build_provider_with_signer::(&send_tx, signer)?; - cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) - .await?; + let signer = match signer { + Some(s) => s, + None => send_tx.eth.wallet.signer().await?, + }; + let from = signer.address(); + let (tx, _) = builder.build(from).await?; + + let wallet = EthereumWallet::from(signer); + let provider = AlloyProviderBuilder::<_, _, TempoNetwork>::default() + .wallet(wallet) + .connect_provider(&provider); + + let cast = CastTxSender::new(provider); + let pending_tx = cast.send(tx).await?; + let tx_hash = *pending_tx.inner().tx_hash(); + cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await?; } Ok(()) diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 19b3b0daf0566..6a21db65026f6 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -32,9 +32,10 @@ use foundry_config::{ use foundry_evm::{ core::{ FoundryBlock as _, - evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork, TxEnvFor}, + evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, }, executors::{EvmError, Executor, TracingExecutor}, + hardforks::FoundryHardfork, opts::EvmOpts, traces::{InternalTraceMode, TraceMode, Traces}, }; @@ -139,6 +140,8 @@ impl RunArgs { if evm_opts.networks.is_tempo() { self.run_with_evm::().await + } else if evm_opts.networks.is_optimism() { + self.run_with_evm::().await } else { self.run_with_evm::().await } @@ -209,12 +212,18 @@ impl RunArgs { if let Some(block) = &block { evm_env.block_env = block_env_from_header(block.header()); - // TODO: we need a smarter way to map the block to the corresponding evm_version for - // commonly used chains + // Resolve the correct spec for the block using the same approach as reth: walk + // known chain activation conditions to find the latest active fork. Falls back + // to a blob-gas heuristic for unknown 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::Prague); + if let Some(hardfork) = FoundryHardfork::from_chain_and_timestamp( + evm_env.cfg_env.chain_id, + block.header().timestamp(), + ) { + evm_env.cfg_env.set_spec(hardfork.into()); + } else if block.header().excess_blob_gas().is_some() { + // TODO: add glamsterdam header field checks in the future + evm_version = Some(EvmVersion::Cancun); } } apply_chain_and_block_specific_env_changes::( @@ -242,7 +251,7 @@ impl RunArgs { None, )?; - evm_env.cfg_env.set_spec(executor.spec_id()); + evm_env.cfg_env.set_spec_and_mainnet_gas_params(executor.spec_id()); // Set the state to the moment right before the transaction if !self.quick { diff --git a/crates/cast/src/debug.rs b/crates/cast/src/debug.rs index 9af69a4167e38..823dedde99059 100644 --- a/crates/cast/src/debug.rs +++ b/crates/cast/src/debug.rs @@ -56,7 +56,8 @@ pub(crate) async fn handle_traces( let mut builder = CallTraceDecoderBuilder::new() .with_labels(labels.chain(config_labels)) .with_signature_identifier(SignaturesIdentifier::from_config(config)?) - .with_label_disabled(disable_label); + .with_label_disabled(disable_label) + .with_chain_id(Some(chain.id())); let mut identifier = TraceIdentifiers::new().with_external(config, Some(chain))?; if let Some(contracts) = &known_contracts { builder = builder.with_known_contracts(contracts); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index a04ee9a6d9e45..f76a33eac5c3d 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1568,6 +1568,27 @@ Transaction successfully executed. "#]]); }); +// Regression test: pre-Berlin tx (block 12243999) must use Istanbul gas costs. +// Without correct hardfork resolution, this tx OOGs under Prague/Berlin cold SLOAD costs. +casttest!(run_pre_berlin_tx_uses_correct_spec, |_prj, cmd| { + let rpc = next_http_archive_rpc_url(); + cmd.args([ + "run", + "-v", + "0xbb4dece05b8d41a2f79475f76daccf7abdd816f6813897cd02ef8509205ebecb", + "--quick", + "--rpc-url", + rpc.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +... +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that `cast --to-base` commands are working correctly. casttest!(to_base, |_prj, cmd| { let values = [ @@ -3075,7 +3096,7 @@ Executing previous transactions from the block. Traces: [33841] FiatTokenProxy::fallback(0x111111125421cA6dc452d289314280a0f8842A65, 164054805 [1.64e8]) ├─ [26673] FiatTokenV2_2::approve(0x111111125421cA6dc452d289314280a0f8842A65, 164054805 [1.64e8]) [delegatecall] - │ ├─ emit Approval(owner: 0x9a95Af47C51562acfb2107F44d7967DF253197df, spender: 0x111111125421cA6dc452d289314280a0f8842A65, value: 164054805 [1.64e8]) + │ ├─ emit Approval(owner: 0x9a95Af47C51562acfb2107F44d7967DF253197df, spender: 0x111111125421cA6dc452d289314280a0f8842A65, amount: 164054805 [1.64e8]) │ └─ ← [Return] true └─ ← [Return] true ... @@ -3083,6 +3104,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { @@ -3825,9 +3874,9 @@ Traces: │ └─ ← [Return] 0x0000000000000000000000000b55b053230e4effb6609de652fca73fd1c29804 ├─ [9750] 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913::balanceOf(0xA12384c5E52fD646E7BC7F6B3b33A605651F566E) [staticcall] │ ├─ [2553] 0x2Ce6311ddAE708829bc0784C967b7d77D19FD779::balanceOf(0xA12384c5E52fD646E7BC7F6B3b33A605651F566E) [delegatecall] - │ │ └─ ← [Return] 0x000000000000000000000000000000000000000000000000000000000000f3b9 - │ └─ ← [Return] 0x000000000000000000000000000000000000000000000000000000000000f3b9 - ├─ [65442] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::fulfillBasicOrder_efficient_6GL6yc() + │ │ └─ ← [Return] 62393 [6.239e4] + │ └─ ← [Return] 62393 [6.239e4] + ├─ [65442] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::selfCallPayVerifyCall537021665() │ ├─ [25070] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::unwrapAndValidateSignature(0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00) [staticcall] │ │ ├─ [22067] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::unwrapAndValidateSignature(0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00) [delegatecall] │ │ │ ├─ [2369] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::pauseFlag() [staticcall] @@ -3846,22 +3895,22 @@ Traces: │ │ └─ ← [Return] │ ├─ [3250] 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913::balanceOf(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312) [staticcall] │ │ ├─ [2553] 0x2Ce6311ddAE708829bc0784C967b7d77D19FD779::balanceOf(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312) [delegatecall] - │ │ │ └─ ← [Return] 0x000000000000000000000000000000000000000000000000000000000000968b - │ │ └─ ← [Return] 0x000000000000000000000000000000000000000000000000000000000000968b + │ │ │ └─ ← [Return] 38539 [3.853e4] + │ │ └─ ← [Return] 38539 [3.853e4] │ ├─ [16411] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::pay(1551, 0x1bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b, 0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a12384c5e52fd646e7bc7f6b3b33a605651f566e000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f0000000000000000000000000000000000000000000000000000000000036cd000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000327a25ad5cfe5c4d4339c1a4267d4a83e8c93312000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000b55b053230e4effb6609de652fca73fd1c2980400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000221000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) │ │ ├─ [15711] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::pay(1551, 0x1bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b, 0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a12384c5e52fd646e7bc7f6b3b33a605651f566e000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f0000000000000000000000000000000000000000000000000000000000036cd000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000327a25ad5cfe5c4d4339c1a4267d4a83e8c93312000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000b55b053230e4effb6609de652fca73fd1c2980400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000221000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) [delegatecall] │ │ │ ├─ [12963] 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913::transfer(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312, 1551) │ │ │ │ ├─ [12263] 0x2Ce6311ddAE708829bc0784C967b7d77D19FD779::transfer(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312, 1551) [delegatecall] - │ │ │ │ │ ├─ emit Transfer(param0: 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E, param1: 0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312, param2: 1551) - │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001 - │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001 + │ │ │ │ │ ├─ emit Transfer(from: 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E, to: 0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312, amount: 1551) + │ │ │ │ │ └─ ← [Return] true + │ │ │ │ └─ ← [Return] true │ │ │ └─ ← [Stop] │ │ └─ ← [Return] │ ├─ [1250] 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913::balanceOf(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312) [staticcall] │ │ ├─ [553] 0x2Ce6311ddAE708829bc0784C967b7d77D19FD779::balanceOf(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312) [delegatecall] - │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000009c9a - │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000009c9a - │ ├─ [5675] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::multicallN2M_001Taw5z() + │ │ │ └─ ← [Return] 40090 [4.009e4] + │ │ └─ ← [Return] 40090 [4.009e4] + │ ├─ [5675] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::selfCallExecutePay1395256087() │ │ ├─ [4148] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::execute(0x0100000000007821000100000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b) │ │ │ ├─ [3693] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::execute(0x0100000000007821000100000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b) [delegatecall] │ │ │ │ ├─ [435] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::fallback() @@ -4971,3 +5020,36 @@ casttest!(cast_decode_tx_tempo, |_prj, cmd| { casttest!(cast_decode_tx_invalid, |_prj, cmd| { cmd.args(["decode-tx", "0xinvalid"]).assert_failure(); }); + +// Test that `cast run --evm-version` correctly updates gas parameters for historical blocks. +// Mainnet tx 0xb856d9...d05d9647 is a Homestead-era tx (block 1,625,693). +// EXP gas pricing differs between Homestead (10 gas/byte) and Spurious Dragon+ (50 gas/byte). +// Without the fix, `set_spec()` only updated the spec discriminant but not the gas_params table, +// so the executor would use stale (latest) gas pricing even when `--evm-version homestead` is set. +casttest!(run_evm_version_updates_gas_params, |_prj, cmd| { + let rpc = next_http_archive_rpc_url(); + let tx = "0xb856d9c8dffeaa317d89ed6abba861d007a708c54971da91233abcd2d05d9647"; + + // Run with --evm-version homestead: gas must match on-chain gasUsed (166651). + let homestead_output = cmd + .args(["run", tx, "--quick", "--rpc-url", rpc.as_str(), "--evm-version", "homestead"]) + .assert_success() + .get_output() + .stdout_lossy(); + assert!( + homestead_output.contains("Gas used: 166651"), + "expected Homestead gas (166651), got: {homestead_output}" + ); + + // Run with --evm-version spuriousDragon: higher gas due to EXP repricing (50 vs 10 gas/byte). + let sd_output = cmd + .cast_fuse() + .args(["run", tx, "--quick", "--rpc-url", rpc.as_str(), "--evm-version", "spuriousDragon"]) + .assert_success() + .get_output() + .stdout_lossy(); + assert!( + sd_output.contains("Gas used: 177241"), + "expected Spurious Dragon gas (177241), got: {sd_output}" + ); +}); diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index c98cfb69357bd..af1a09c38d109 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Cheatcodes", - "description": "Foundry cheatcodes. Learn more: ", + "description": "Foundry cheatcodes. Learn more: ", "type": "object", "properties": { "cheatcodes": { diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 29e968aeb5bc6..202b590769857 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -19,7 +19,7 @@ mod vm; pub use vm::Vm; // The `cheatcodes.json` schema. -/// Foundry cheatcodes. Learn more: +/// Foundry cheatcodes. Learn more: #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] @@ -178,7 +178,7 @@ interface Vm {{ eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); if std::env::var("CI").is_ok() { - eprintln!(" NOTE: run `cargo cheats` locally and commit the updated files\n"); + eprintln!(" NOTE: run `cargo spec-cheats` locally and commit the updated files\n"); } if let Some(parent) = file.parent() { let _ = fs::create_dir_all(parent); diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 79d7d29869784..3435617cdc45e 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,6 +1,6 @@ use super::Result; use crate::Vm::Rpc; -use alloy_primitives::{U256, map::AddressHashMap}; +use alloy_primitives::{Address, U256, map::AddressHashMap}; use foundry_common::{ContractsByArtifact, fs::normalize_path}; use foundry_compilers::{ArtifactId, ProjectPathsConfig, utils::canonicalize}; use foundry_config::{ @@ -56,6 +56,8 @@ pub struct CheatsConfig { pub seed: Option, /// Whether to allow `expectRevert` to work for internal calls. pub internal_expect_revert: bool, + /// Fee token to use for Tempo transactions. + pub fee_token: Option
, } impl CheatsConfig { @@ -65,6 +67,7 @@ impl CheatsConfig { evm_opts: EvmOpts, available_artifacts: Option, running_artifact: Option, + fee_token: Option
, ) -> Self { let rpc_endpoints = config.rpc_endpoints.clone().resolved(); trace!(?rpc_endpoints, "using resolved rpc endpoints"); @@ -92,12 +95,19 @@ impl CheatsConfig { assertions_revert: config.assertions_revert, seed: config.fuzz.seed, internal_expect_revert: config.allow_internal_expect_revert, + fee_token, } } /// Returns a new `CheatsConfig` configured with the given `Config` and `EvmOpts`. pub fn clone_with(&self, config: &Config, evm_opts: EvmOpts) -> Self { - Self::new(config, evm_opts, self.available_artifacts.clone(), self.running_artifact.clone()) + Self::new( + config, + evm_opts, + self.available_artifacts.clone(), + self.running_artifact.clone(), + self.fee_token, + ) } /// Attempts to canonicalize (see [std::fs::canonicalize]) the path. @@ -222,6 +232,7 @@ impl Default for CheatsConfig { assertions_revert: true, seed: None, internal_expect_revert: false, + fee_token: None, } } } @@ -237,6 +248,7 @@ mod tests { Default::default(), None, None, + None, ) } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 11b3e4f0d0aab..58d7c924622e9 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -768,7 +768,7 @@ impl Cheatcodes { ) -> Option { // Apply custom execution evm version. if let Some(spec_id) = self.execution_evm_version { - ecx.cfg_mut().set_spec(spec_id); + ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id); } let gas = Gas::new(call.gas_limit); @@ -1025,7 +1025,9 @@ impl Cheatcodes { } tx_req.set_authorization_list(active_delegations); } - + if let Some(fee_token) = self.config.fee_token { + tx_req.set_fee_token(fee_token); + } self.broadcastable_transactions.push_back(BroadcastableTransaction { rpc, transaction: TransactionMaybeSigned::new(tx_req), @@ -1715,7 +1717,7 @@ impl Inspector> for Cheatcode ) -> Option { // Apply custom execution evm version. if let Some(spec_id) = self.execution_evm_version { - ecx.cfg_mut().set_spec(spec_id); + ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id); } let gas = Gas::new(input.gas_limit()); @@ -1792,16 +1794,18 @@ impl Inspector> for Cheatcode let rpc = ecx.db().active_fork_url(); let account = &ecx.journal().evm_state()[&broadcast.new_origin]; + let mut tx_req = TransactionRequestFor::::default() + .with_from(broadcast.new_origin) + .with_kind(TxKind::Create) + .with_value(input.value()) + .with_input(input.init_code()) + .with_nonce(account.info.nonce); + if let Some(fee_token) = self.config.fee_token { + tx_req.set_fee_token(fee_token); + } self.broadcastable_transactions.push_back(BroadcastableTransaction { rpc, - transaction: TransactionMaybeSigned::new( - TransactionRequestFor::::default() - .with_from(broadcast.new_origin) - .with_kind(TxKind::Create) - .with_value(input.value()) - .with_input(input.init_code()) - .with_nonce(account.info.nonce), - ), + transaction: TransactionMaybeSigned::new(tx_req), }); input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create)); diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index a1497677aee18..411c599aad22b 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -158,17 +158,18 @@ impl ChiselDispatcher { result: &mut ChiselResult, // known_contracts: &ContractsByArtifact, ) -> eyre::Result { + let chain_id = session_config.evm_opts.get_remote_chain_id().await; + let mut decoder = CallTraceDecoderBuilder::new() .with_labels(result.labeled_addresses.clone()) .with_signature_identifier(SignaturesIdentifier::from_config( &session_config.foundry_config, )?) + .with_chain_id(chain_id.map(|c| c.id())) .build(); - let mut identifier = TraceIdentifiers::new().with_external( - &session_config.foundry_config, - session_config.evm_opts.get_remote_chain_id().await, - )?; + let mut identifier = + TraceIdentifiers::new().with_external(&session_config.foundry_config, chain_id)?; if !identifier.is_empty() { for (_, trace) in &mut result.traces { decoder.identify(trace, &mut identifier); diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 4aa22ffbf7a63..f5deb369ff963 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -227,6 +227,7 @@ impl SessionSource { self.config.evm_opts.clone(), None, None, + None, ) .into(), ) diff --git a/crates/cli/src/opts/tempo.rs b/crates/cli/src/opts/tempo.rs index a24d764b79f81..a071e6774c21a 100644 --- a/crates/cli/src/opts/tempo.rs +++ b/crates/cli/src/opts/tempo.rs @@ -5,6 +5,8 @@ use clap::Parser; use foundry_common::FoundryTransactionBuilder; use std::str::FromStr; +use crate::utils::parse_fee_token_address; + /// CLI options for Tempo transactions. #[derive(Clone, Debug, Default, Parser)] #[command(next_help_heading = "Tempo")] @@ -16,7 +18,7 @@ pub struct TempoOpts { /// /// If this is not set, the fee token is chosen according to network rules. See the Tempo docs /// for more information. - #[arg(long = "tempo.fee-token")] + #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] pub fee_token: Option
, /// Nonce key for Tempo parallelizable nonces. @@ -135,3 +137,24 @@ impl TempoOpts { fn parse_signature(s: &str) -> Result { Signature::from_str(s).map_err(|e| format!("invalid signature: {e}")) } + +#[cfg(test)] +mod tests { + use super::*; + use foundry_evm::core::tempo::{ALPHA_USD_ADDRESS, BETA_USD_ADDRESS}; + + #[test] + fn parse_fee_token_id() { + let opts = TempoOpts::try_parse_from([ + "", + "--tempo.fee-token", + "0x20C0000000000000000000000000000000000002", + ]) + .unwrap(); + assert_eq!(opts.fee_token, Some(BETA_USD_ADDRESS),); + + // AlphaUSD token ID is 1u64 + let opts_with_id = TempoOpts::try_parse_from(["", "--tempo.fee-token", "1"]).unwrap(); + assert_eq!(opts_with_id.fee_token, Some(ALPHA_USD_ADDRESS),); + } +} diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 6323288bec2b4..bfd722a5ac104 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -30,6 +30,9 @@ pub use abi::*; mod allocator; pub use allocator::*; +mod tempo; +pub use tempo::*; + // reexport all `foundry_config::utils` #[doc(hidden)] pub use foundry_config::utils::*; diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..8f6d7f3cde092 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -16,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/cli/src/utils/tempo.rs b/crates/cli/src/utils/tempo.rs new file mode 100644 index 0000000000000..2c9e5d9604884 --- /dev/null +++ b/crates/cli/src/utils/tempo.rs @@ -0,0 +1,15 @@ +use std::str::FromStr; + +use alloy_primitives::{Address, hex}; + +/// Parses a fee token address. +pub fn parse_fee_token_address(address_or_id: &str) -> eyre::Result
{ + Address::from_str(address_or_id).or_else(|_| Ok(token_id_to_address(address_or_id.parse()?))) +} + +fn token_id_to_address(token_id: u64) -> Address { + let mut address_bytes = [0u8; 20]; + address_bytes[..12].copy_from_slice(&hex!("20C000000000000000000000")); + address_bytes[12..20].copy_from_slice(&token_id.to_be_bytes()); + Address::from(address_bytes) +} diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 1486b8ef43fb2..610b1382156e5 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -45,6 +45,9 @@ alloy-transport.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } alloy-network.workspace = true +op-alloy-network.workspace = true +op-alloy-rpc-types.workspace = true + revm.workspace = true solar.workspace = true diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 06b2c29945417..f079d0f4fbf3d 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) .map(|(_, data)| data) } diff --git a/crates/common/src/transactions/builder.rs b/crates/common/src/transactions/builder.rs index 938d95e4395b7..935c30f20bfe9 100644 --- a/crates/common/src/transactions/builder.rs +++ b/crates/common/src/transactions/builder.rs @@ -7,6 +7,8 @@ use alloy_primitives::{Address, B256, Signature, TxKind, U256}; use alloy_provider::Provider; use alloy_signer::Signer; use eyre::Result; +use op_alloy_network::Optimism; +use op_alloy_rpc_types::OpTransactionRequest; use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; use tempo_primitives::{ TempoSignature, @@ -343,6 +345,20 @@ impl FoundryTransactionBuilder for ::Transact } } +impl FoundryTransactionBuilder for OpTransactionRequest { + fn reset_gas_limit(&mut self) { + self.as_mut().gas = None; + } + + fn authorization_list(&self) -> Option<&Vec> { + self.as_ref().authorization_list.as_ref() + } + + fn set_authorization_list(&mut self, authorization_list: Vec) { + self.as_mut().authorization_list = Some(authorization_list); + } +} + impl FoundryTransactionBuilder for ::TransactionRequest { fn reset_gas_limit(&mut self) { self.gas = None; diff --git a/crates/common/src/transactions/receipt.rs b/crates/common/src/transactions/receipt.rs index 363eaedca8cea..9ca6cb02b10ee 100644 --- a/crates/common/src/transactions/receipt.rs +++ b/crates/common/src/transactions/receipt.rs @@ -7,6 +7,7 @@ use alloy_provider::{ use alloy_rpc_types::{BlockId, TransactionReceipt}; use eyre::Result; use foundry_common_fmt::{UIfmt, UIfmtReceiptExt, get_pretty_receipt_attr}; +use op_alloy_rpc_types::OpTransactionReceipt; use serde::{Deserialize, Serialize}; use tempo_alloy::rpc::TempoTransactionReceipt; @@ -22,6 +23,12 @@ impl FoundryReceiptResponse for TransactionReceipt { } } +impl FoundryReceiptResponse for OpTransactionReceipt { + fn set_contract_address(&mut self, contract_address: Address) { + self.inner.contract_address = Some(contract_address); + } +} + impl FoundryReceiptResponse for TempoTransactionReceipt { fn set_contract_address(&mut self, contract_address: Address) { self.contract_address = Some(contract_address); diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 39c281e81be25..4c6978e0d9750 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -49,6 +49,9 @@ walkdir.workspace = true yansi.workspace = true clap = { version = "4", features = ["derive"] } +# schema +schemars = { version = "1.0", optional = true } + [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2" @@ -60,3 +63,4 @@ tempfile.workspace = true [features] isolate-by-default = [] +schema = ["dep:schemars"] diff --git a/crates/config/assets/config.schema.json b/crates/config/assets/config.schema.json new file mode 100644 index 0000000000000..00a381eaf66ff --- /dev/null +++ b/crates/config/assets/config.schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Config", + "description": "Foundry configuration. Learn more: ", + "type": "object" +} \ No newline at end of file diff --git a/crates/config/spec/Cargo.toml b/crates/config/spec/Cargo.toml new file mode 100644 index 0000000000000..89d40cf5606ce --- /dev/null +++ b/crates/config/spec/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "foundry-config-spec" +description = "Foundry configuration specification" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +foundry-config.workspace = true +serde.workspace = true + +# schema +schemars = { version = "1.0", optional = true } + +[dev-dependencies] +serde_json.workspace = true + +[features] +schema = ["dep:schemars", "foundry-config/schema"] diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs new file mode 100644 index 0000000000000..5a362e963d956 --- /dev/null +++ b/crates/config/spec/src/lib.rs @@ -0,0 +1,64 @@ +//! Config specification for Foundry. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use foundry_config::Config; +use serde::{Deserialize, Serialize}; + +// The `config.json` schema. +/// Foundry configuration. Learn more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct ConfigSchema { + #[serde(flatten)] + pub config: Config, +} + +#[cfg(test)] +#[expect(clippy::disallowed_macros)] +mod tests { + use super::*; + use std::{fs, path::Path}; + + #[cfg(feature = "schema")] + const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/config.schema.json"); + + /// Generates the configuration JSON schema. + #[cfg(feature = "schema")] + fn json_schema() -> String { + serde_json::to_string_pretty(&schemars::schema_for!(ConfigSchema)).unwrap() + } + + #[test] + #[cfg(feature = "schema")] + fn schema_up_to_date() { + ensure_file_contents(Path::new(SCHEMA_PATH), &json_schema()); + } + + /// Checks that the `file` has the specified `contents`. If that is not the + /// case, updates the file and then fails the test. + fn ensure_file_contents(file: &Path, contents: &str) { + if let Ok(old_contents) = fs::read_to_string(file) + && normalize_newlines(&old_contents) == normalize_newlines(contents) + { + // File is already up to date. + return; + } + + eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); + if std::env::var("CI").is_ok() { + eprintln!(" NOTE: run `cargo spec-config` locally and commit the updated files\n"); + } + if let Some(parent) = file.parent() { + let _ = fs::create_dir_all(parent); + } + fs::write(file, contents).unwrap(); + panic!("some file was not up to date and has been updated, simply re-run the tests"); + } + + fn normalize_newlines(s: &str) -> String { + s.replace("\r\n", "\n") + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 92156f39a0aa8..e77033c75844a 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -163,6 +163,7 @@ pub use semver; /// /// Note that these behaviors differ from those of [`Config::figment()`]. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Config { /// The selected profile. **(default: _default_ `default`)** /// @@ -2755,7 +2756,7 @@ impl BasicConfig { "\ [profile.{}] {s} -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options\n", +# See more config options: https://getfoundry.sh/config/reference/default-config\n", self.profile )) } @@ -4244,7 +4245,6 @@ mod tests { out = "myout" verbosity = 3 evm_version = 'berlin' - [profile.other] src = "other-src" "#, @@ -4876,28 +4876,48 @@ mod tests { } #[test] - fn test_parse_with_profile() { - let foundry_str = 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 - "; - assert_eq!( - parse_with_profile::(foundry_str).unwrap().unwrap(), - ( - Config::DEFAULT_PROFILE, + fn test_extract_basic() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + src = "mysrc" + out = "myout" + verbosity = 3 + evm_version = 'berlin' + [profile.other] + src = "other-src" + "#, + )?; + let loaded = Config::load().unwrap(); + assert_eq!(loaded.evm_version, EvmVersion::Berlin); + let base = loaded.into_basic(); + let default = Config::default(); + assert_eq!( + base, BasicConfig { profile: Config::DEFAULT_PROFILE, - src: "src".into(), - out: "out".into(), - libs: vec!["lib".into()], - remappings: vec![] + src: "mysrc".into(), + out: "myout".into(), + libs: default.libs.clone(), + remappings: default.remappings.clone(), } - ) - ); + ); + jail.set_env("FOUNDRY_PROFILE", r"other"); + let base = Config::figment().extract::().unwrap(); + assert_eq!( + base, + BasicConfig { + profile: Config::DEFAULT_PROFILE, + src: "other-src".into(), + out: "myout".into(), + libs: default.libs.clone(), + remappings: default.remappings, + } + ); + Ok(()) + }); } #[test] diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 431bf70df88f5..69f529c3eb6a2 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -155,6 +155,12 @@ impl From> for Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index a62dff3f21582..e5dfbbaadce25 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,4 +1,4 @@ -use crate::{AsDoc, CommentTag, Comments, Deployment, Markdown, writer::traits::ParamLike}; +use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; use solang_parser::pt::{ EnumDefinition, ErrorParameter, EventParameter, Parameter, VariableDeclaration, @@ -89,17 +89,6 @@ impl BufWriter { writeln!(self.buf, "{}", Markdown::Italic(text)) } - /// Writes dev content to the buffer, handling markdown lists properly. - /// If the content contains markdown lists, it formats them correctly. - /// Otherwise, it writes the content in italics. - pub fn write_dev_content(&mut self, text: &str) -> fmt::Result { - for line in text.lines() { - writeln!(self.buf, "{line}")?; - } - - Ok(()) - } - /// Writes bold text to the buffer formatted as [Markdown::Bold]. pub fn write_bold(&mut self, text: &str) -> fmt::Result { writeln!(self.buf, "{}", Markdown::Bold(text)) @@ -202,7 +191,8 @@ impl BufWriter { params: &EnumDefinition, comments: &Comments, ) -> fmt::Result { - let comments = comments.include_tags(&[CommentTag::Param]); + let comments = + comments.include_tags(&[CommentTag::Param, CommentTag::Custom("variant".to_string())]); // There is nothing to write. if comments.is_empty() { @@ -215,7 +205,7 @@ impl BufWriter { self.write_piped(&VARIANTS_TABLE_HEADERS.join("|"))?; self.write_piped(&VARIANTS_TABLE_SEPARATOR)?; - for value in ¶ms.values { + for value in params.values.iter() { let param_name = value.as_ref().map(|v| v.name.clone()); let comment = param_name.as_ref().and_then(|name| { @@ -223,7 +213,7 @@ impl BufWriter { }); let row = [ - Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + Markdown::Code(param_name.as_deref().unwrap_or("")).as_doc()?, comment.unwrap_or_default().replace('\n', " "), ]; self.write_piped(&row.join("|"))?; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 7bd5bc525330f..2517893fa32b0 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -23,7 +23,7 @@ foundry-evm-networks.workspace = true alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } -alloy-evm = { workspace = true, features = ["rpc"] } +alloy-evm = { workspace = true, features = ["rpc", "op"] } alloy-genesis.workspace = true alloy-hardforks.workspace = true alloy-json-abi.workspace = true @@ -53,6 +53,8 @@ revm = { workspace = true, features = [ "blst", ] } revm-inspectors.workspace = true +op-alloy-consensus = { workspace = true, features = ["k256"] } +op-alloy-network.workspace = true op-revm.workspace = true tempo-revm.workspace = true tempo-alloy.workspace = true @@ -77,4 +79,5 @@ url.workspace = true alloy-op-evm.workspace = true alloy-serde.workspace = true op-alloy-consensus.workspace = true +op-alloy-rpc-types.workspace = true foundry-test-utils.workspace = true diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index e999bb8a7d78a..e7c1345792aa3 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1,21 +1,22 @@ //! Foundry's main executor backend abstraction and implementation. use crate::{ - FoundryBlock, FoundryInspectorExt, FoundryTransaction, + FoundryBlock, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction, constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS}, evm::{ - BlockEnvFor, BlockResponseFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, - FoundryEvmFactory, FoundryEvmNetwork, HaltReasonFor, PrecompilesFor, SpecFor, - TransactionResponseFor, TxEnvFor, + BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory, + FoundryEvmNetwork, HaltReasonFor, PrecompilesFor, SpecFor, TxEnvFor, }, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, utils::get_blob_base_fee_update_fraction, }; use alloy_consensus::{BlockHeader, Typed2718}; -use alloy_evm::{Evm, EvmEnv, EvmFactory, FromRecoveredTx}; +use alloy_evm::{Evm, EvmEnv, EvmFactory}; use alloy_genesis::GenesisAccount; -use alloy_network::{BlockResponse, Network, TransactionResponse}; +use alloy_network::{ + AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse, +}; use alloy_primitives::{Address, B256, TxKind, U256, keccak256, uint}; use alloy_rpc_types::BlockNumberOrTag; use eyre::Context; @@ -441,7 +442,7 @@ pub trait DatabaseExt: #[must_use] pub struct Backend { /// The access point for managing forks - forks: MultiFork, BlockEnvFor>, + forks: MultiFork, BlockEnvFor>, // The default in memory db mem_db: FoundryEvmInMemoryDB, /// The journaled_state to use to initialize new forks with @@ -499,7 +500,7 @@ impl Backend { /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory /// database. pub fn spawn(fork: Option) -> eyre::Result { - Self::new(MultiFork::, BlockEnvFor>::spawn(), fork) + Self::new(MultiFork::, BlockEnvFor>::spawn(), fork) } /// Creates a new instance of `Backend` @@ -509,7 +510,7 @@ impl Backend { /// /// Prefer using [`spawn`](Self::spawn) instead. pub fn new( - forks: MultiFork, BlockEnvFor>, + forks: MultiFork, BlockEnvFor>, fork: Option, ) -> eyre::Result { trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend"); @@ -548,7 +549,7 @@ impl Backend { /// as active pub(crate) fn new_with_fork( id: &ForkId, - fork: Fork>, + fork: Fork>, journaled_state: JournaledState, ) -> eyre::Result { let mut backend = Self::spawn(None)?; @@ -613,7 +614,7 @@ impl Backend { &self, ) -> &StateSnapshots< BackendStateSnapshot< - BackendDatabaseSnapshot>, + BackendDatabaseSnapshot>, SpecFor, BlockEnvFor, >, @@ -671,7 +672,7 @@ impl Backend { pub(crate) fn update_fork_db( &self, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork>, + target_fork: &mut Fork>, ) { self.update_fork_db_contracts( self.inner.persistent_accounts.iter().copied(), @@ -685,7 +686,7 @@ impl Backend { &self, accounts: impl IntoIterator, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork>, + target_fork: &mut Fork>, ) { if let Some(db) = self.active_fork_db() { merge_account_data(accounts, db, active_journaled_state, target_fork) @@ -710,22 +711,22 @@ impl Backend { } /// Returns the currently active `Fork`, if any - pub fn active_fork(&self) -> Option<&Fork>> { + pub fn active_fork(&self) -> Option<&Fork>> { self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx)) } /// Returns the currently active `Fork`, if any - pub fn active_fork_mut(&mut self) -> Option<&mut Fork>> { + pub fn active_fork_mut(&mut self) -> Option<&mut Fork>> { self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx)) } /// Returns the currently active `ForkDB`, if any - pub fn active_fork_db(&self) -> Option<&ForkDB>> { + pub fn active_fork_db(&self) -> Option<&ForkDB>> { self.active_fork().map(|f| &f.db) } /// Returns the currently active `ForkDB`, if any - pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB>> { + pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB>> { self.active_fork_mut().map(|f| &mut f.db) } @@ -748,7 +749,7 @@ impl Backend { /// Creates a snapshot of the currently active database pub(crate) fn create_db_snapshot( &self, - ) -> BackendDatabaseSnapshot> { + ) -> BackendDatabaseSnapshot> { if let Some((id, idx)) = self.active_fork_ids { let fork = self.inner.get_fork(idx).clone(); let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed"); @@ -894,7 +895,7 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(u64, BlockResponseFor)> { + ) -> eyre::Result<(u64, AnyRpcBlock)> { let fork = self.inner.get_fork_by_id(id)?; let tx = fork.backend().get_transaction(transaction)?; @@ -924,7 +925,7 @@ impl Backend { evm_env: EvmEnvFor, tx_hash: B256, journaled_state: &mut JournaledState, - ) -> eyre::Result>> { + ) -> eyre::Result> { trace!(?id, ?tx_hash, "replay until transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); @@ -959,7 +960,7 @@ impl Backend { let mut evm = FEN::EvmFactory::default().create_evm(replay_db, evm_env); for tx in &txs_to_replay { - let tx_env = TxEnvFor::::from_recovered_tx(tx.as_ref(), tx.from()); + let tx_env = TxEnvFor::::from_any_rpc_transaction(tx)?; trace!(tx=?tx.tx_hash(), "committing transaction"); evm.transact_commit(tx_env).wrap_err("backend: failed committing transaction")?; } @@ -1352,7 +1353,7 @@ impl DatabaseExt for Backend { let fork = self.inner.get_fork_by_id_mut(id)?; fork.backend().get_transaction(transaction)? }; - let tx_env = TxEnvFor::::from_recovered_tx(tx.as_ref(), tx.from()); + let tx_env = TxEnvFor::::from_any_rpc_transaction(&tx)?; // This is a bit ambiguous because the user wants to transact an arbitrary transaction in // the current context, but we're assuming the user wants to transact the transaction as it @@ -1719,12 +1720,12 @@ pub struct BackendInner { pub created_forks: HashMap, /// Holds all created fork databases // Note: data is stored in an `Option` so we can remove it without reshuffling - pub forks: Vec>>>, + pub forks: Vec>>>, /// Contains state snapshots made at a certain point #[allow(clippy::type_complexity)] pub state_snapshots: StateSnapshots< BackendStateSnapshot< - BackendDatabaseSnapshot>, + BackendDatabaseSnapshot>, SpecFor, BlockEnvFor, >, @@ -1809,14 +1810,14 @@ impl BackendInner { /// Returns the underlying fork mapped to the index #[track_caller] - fn get_fork(&self, idx: ForkLookupIndex) -> &Fork> { + fn get_fork(&self, idx: ForkLookupIndex) -> &Fork> { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].as_ref().unwrap() } /// Returns the underlying fork mapped to the index #[track_caller] - fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork> { + fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork> { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].as_mut().unwrap() } @@ -1826,35 +1827,32 @@ impl BackendInner { fn get_fork_by_id_mut( &mut self, id: LocalForkId, - ) -> eyre::Result<&mut Fork>> { + ) -> eyre::Result<&mut Fork>> { let idx = self.ensure_fork_index_by_local_id(id)?; Ok(self.get_fork_mut(idx)) } /// Returns the underlying fork corresponding to the id #[track_caller] - fn get_fork_by_id( - &self, - id: LocalForkId, - ) -> eyre::Result<&Fork>> { + fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork>> { let idx = self.ensure_fork_index_by_local_id(id)?; Ok(self.get_fork(idx)) } /// Removes the fork - fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork> { + fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork> { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].take().unwrap() } - fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork>) { + fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork>) { self.forks[idx] = Some(fork) } /// Returns an iterator over Forks pub fn forks_iter( &self, - ) -> impl Iterator>)> + '_ { + ) -> impl Iterator>)> + '_ { self.issued_local_fork_ids .iter() .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id]))) @@ -1863,7 +1861,7 @@ impl BackendInner { /// Returns a mutable iterator over all Forks pub fn forks_iter_mut( &mut self, - ) -> impl Iterator>> + '_ { + ) -> impl Iterator>> + '_ { self.forks.iter_mut().filter_map(|f| f.as_mut()) } @@ -1873,7 +1871,7 @@ impl BackendInner { id: LocalForkId, fork_id: ForkId, idx: ForkLookupIndex, - fork: Fork>, + fork: Fork>, ) { self.created_forks.insert(fork_id.clone(), idx); self.issued_local_fork_ids.insert(id, fork_id); @@ -1885,7 +1883,7 @@ impl BackendInner { &mut self, id: LocalForkId, fork_id: ForkId, - db: ForkDB>, + db: ForkDB>, journaled_state: JournaledState, ) -> ForkLookupIndex { let idx = self.forks.len(); @@ -1901,7 +1899,7 @@ impl BackendInner { &mut self, id: LocalForkId, new_fork_id: ForkId, - backend: SharedBackend>, + backend: SharedBackend>, ) -> eyre::Result { let fork_id = self.ensure_fork_id(id)?; let idx = self.ensure_fork_index(fork_id)?; @@ -1926,7 +1924,7 @@ impl BackendInner { pub fn insert_new_fork( &mut self, fork_id: ForkId, - db: ForkDB>, + db: ForkDB>, journaled_state: JournaledState, ) -> (LocalForkId, ForkLookupIndex) { let idx = self.forks.len(); @@ -2102,7 +2100,7 @@ fn commit_transaction( evm_env: EvmEnvFor, tx_env: TxEnvFor, journaled_state: &mut JournaledState, - fork: &mut Fork>, + fork: &mut Fork>, fork_id: &ForkId, persistent_accounts: &HashSet
, inspector: &mut dyn for<'db> FoundryInspectorExt< diff --git a/crates/evm/core/src/env.rs b/crates/evm/core/src/env.rs index 27df53343c7ba..bcae56125d818 100644 --- a/crates/evm/core/src/env.rs +++ b/crates/evm/core/src/env.rs @@ -1,10 +1,17 @@ use std::fmt::Debug; +use alloy_consensus::Typed2718; pub use alloy_evm::EvmEnv; +use alloy_evm::FromRecoveredTx; +use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse}; use alloy_primitives::{Address, B256, Bytes, U256}; +use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit}; use op_revm::{ OpTransaction, - transaction::{OpTxTr, deposit::DEPOSIT_TRANSACTION_TYPE}, + transaction::{ + OpTxTr, + deposit::{DEPOSIT_TRANSACTION_TYPE, DepositTransactionParts}, + }, }; use revm::{ Context, Database, Journal, @@ -574,14 +581,111 @@ impl< } } +/// Trait for converting an [`AnyRpcTransaction`] into a specific `TxEnv`. +/// +/// Implementations extract the inner [`alloy_consensus::TxEnvelope`] via +/// [`as_envelope()`](alloy_network::AnyTxEnvelope::as_envelope) then delegate to +/// [`FromRecoveredTx`]. +pub trait FromAnyRpcTransaction: Sized { + /// Tries to convert an [`AnyRpcTransaction`] into `Self`. + fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result; +} + +impl FromAnyRpcTransaction for TxEnv { + fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { + if let Some(envelope) = tx.as_envelope() { + Ok(Self::from_recovered_tx(envelope, tx.from())) + } else { + eyre::bail!("cannot convert unknown transaction type to TxEnv") + } + } +} + +impl FromAnyRpcTransaction for OpTransaction { + fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { + if let Some(envelope) = tx.as_envelope() { + return Ok(Self { + base: TxEnv::from_recovered_tx(envelope, tx.from()), + enveloped_tx: None, + deposit: Default::default(), + }); + } + + // Handle OP deposit transactions from `Unknown` envelope variant. + if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner + && unknown.ty() == DEPOSIT_TX_TYPE_ID + { + let mut fields = unknown.inner.fields.clone(); + fields.insert("from".to_string(), serde_json::to_value(tx.from())?); + let deposit_tx: TxDeposit = fields + .deserialize_into() + .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?; + let base = TxEnv::from_recovered_tx(&deposit_tx, tx.from()); + let deposit = DepositTransactionParts { + source_hash: deposit_tx.source_hash, + mint: Some(deposit_tx.mint), + is_system_transaction: deposit_tx.is_system_transaction, + }; + return Ok(Self { base, enveloped_tx: None, deposit }); + } + + eyre::bail!("cannot convert unknown transaction type to OpTransaction") + } +} + +impl FromAnyRpcTransaction for TempoTxEnv { + fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { + use alloy_consensus::Transaction as _; + if let Some(envelope) = tx.as_envelope() { + return Ok(TxEnv::from_recovered_tx(envelope, tx.from()).into()); + } + + // Handle Tempo transactions from `Unknown` envelope variant. + if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner + && unknown.ty() == tempo_alloy::primitives::TEMPO_TX_TYPE_ID + { + let base = TxEnv { + tx_type: unknown.ty(), + caller: tx.from(), + gas_limit: unknown.gas_limit(), + gas_price: unknown.max_fee_per_gas(), + gas_priority_fee: unknown.max_priority_fee_per_gas(), + kind: unknown.kind(), + value: unknown.value(), + data: unknown.input().clone(), + nonce: unknown.nonce(), + chain_id: unknown.chain_id(), + access_list: unknown.access_list().cloned().unwrap_or_default(), + ..Default::default() + }; + let fee_token = + unknown.inner.fields.get_deserialized::
("feeToken").and_then(Result::ok); + return Ok(Self { inner: base, fee_token, ..Default::default() }); + } + + eyre::bail!("cannot convert unknown transaction type to TempoTxEnv") + } +} + #[cfg(test)] mod tests { use super::*; + use alloy_consensus::{Sealed, Signed, TxEip1559, transaction::Recovered}; use alloy_evm::{EthEvmFactory, EvmFactory}; + use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction}; use alloy_op_evm::OpEvmFactory; + use alloy_primitives::Signature; + use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo}; + use alloy_serde::WithOtherFields; use foundry_evm_hardforks::TempoHardfork; + use op_alloy_consensus::{OpTxEnvelope, transaction::OpTransactionInfo}; + use op_alloy_rpc_types::Transaction as OpRpcTransaction; use op_revm::OpSpecId; use revm::database::EmptyDB; + use tempo_alloy::primitives::{ + AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope, + transaction::PrimitiveSignature, + }; use tempo_evm::TempoEvmFactory; #[test] @@ -652,4 +756,169 @@ mod tests { let evm_env = evm.ctx().evm_clone(); evm.ctx_mut().set_evm(evm_env); } + + fn make_signed_eip1559() -> Signed { + Signed::new_unchecked( + TxEip1559 { + chain_id: 1, + nonce: 42, + gas_limit: 21001, + to: TxKind::Call(Address::with_last_byte(0xBB)), + value: U256::from(101), + ..Default::default() + }, + Signature::new(U256::ZERO, U256::ZERO, false), + B256::ZERO, + ) + } + + #[test] + fn from_any_rpc_transaction_for_eth() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + let rpc_tx = RpcTransaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + TransactionInfo::default(), + ); + + let any_tx = >::from(rpc_tx); + let tx_env = TxEnv::from_any_rpc_transaction(&any_tx).unwrap(); + + assert_eq!(tx_env.caller, from); + assert_eq!(tx_env.nonce, 42); + assert_eq!(tx_env.gas_limit, 21001); + assert_eq!(tx_env.value, U256::from(101)); + assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB))); + } + + #[test] + fn from_any_rpc_transaction_for_op() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + + // Build the eth TxEnv to compare against op base + let rpc_tx = RpcTransaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + TransactionInfo::default(), + ); + let any_tx = >::from(rpc_tx); + let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap(); + + let op_tx_env = OpTransaction::::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(op_tx_env.base, expected_base); + } + + #[test] + fn from_any_rpc_transaction_unknown_envelope_errors() { + let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope { + hash: B256::ZERO, + inner: UnknownTypedTransaction { + ty: AnyTxType(0xFF), + fields: Default::default(), + memo: Default::default(), + }, + }); + let from = Address::random(); + let any_tx = AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction { + inner: Recovered::new_unchecked(unknown, from), + block_hash: None, + block_number: None, + transaction_index: None, + effective_gas_price: None, + block_timestamp: None, + })); + + let result = TxEnv::from_any_rpc_transaction(&any_tx).unwrap_err(); + assert!(result.to_string().contains("unknown transaction type")); + } + + #[test] + fn from_any_rpc_transaction_for_op_deposit() { + let from = Address::random(); + let source_hash = B256::random(); + let deposit = TxDeposit { + source_hash, + from, + to: TxKind::Call(Address::with_last_byte(0xCC)), + mint: 1111, + value: U256::from(200), + gas_limit: 21000, + is_system_transaction: true, + input: Default::default(), + }; + + // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as AnyRpcTransaction. + let op_rpc_tx = OpRpcTransaction::from_transaction( + Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from), + OpTransactionInfo::default(), + ); + let json = serde_json::to_value(&op_rpc_tx).unwrap(); + let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap(); + + let op_tx_env = OpTransaction::::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(op_tx_env.base.caller, from); + assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC))); + assert_eq!(op_tx_env.base.value, U256::from(200)); + assert_eq!(op_tx_env.base.gas_limit, 21000); + assert_eq!(op_tx_env.deposit.source_hash, source_hash); + assert_eq!(op_tx_env.deposit.mint, Some(1111)); + assert!(op_tx_env.deposit.is_system_transaction); + } + + #[test] + fn from_any_rpc_transaction_for_tempo_eth_envelope() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + let rpc_tx = RpcTransaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + TransactionInfo::default(), + ); + let any_tx = >::from(rpc_tx); + + let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(tx_env.inner.caller, from); + assert_eq!(tx_env.inner.nonce, 42); + assert_eq!(tx_env.inner.gas_limit, 21001); + assert_eq!(tx_env.inner.value, U256::from(101)); + assert_eq!(tx_env.fee_token, None); + } + + #[test] + fn from_any_rpc_transaction_for_tempo_aa() { + let from = Address::random(); + let fee_token = Some(Address::random()); + let tempo_tx = TempoTransaction { + chain_id: 42431, + nonce: 42, + gas_limit: 424242, + fee_token, + nonce_key: U256::from(4242), + valid_after: Some(1800000000), + ..Default::default() + }; + let aa_signed = AASigned::new_unhashed( + tempo_tx, + TempoSignature::Primitive(PrimitiveSignature::Secp256k1(Signature::new( + U256::ZERO, + U256::ZERO, + false, + ))), + ); + + // Build a concrete Tempo RPC transaction, serialize to JSON, deserialize as + // AnyRpcTransaction. + let rpc_tx = RpcTransaction::from_transaction( + Recovered::new_unchecked(TempoTxEnvelope::AA(aa_signed), from), + TransactionInfo::default(), + ); + let json = serde_json::to_value(&rpc_tx).unwrap(); + let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap(); + + let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(tx_env.inner.caller, from); + assert_eq!(tx_env.inner.nonce, 42); + assert_eq!(tx_env.inner.gas_limit, 424242); + assert_eq!(tx_env.inner.chain_id, Some(42431)); + assert_eq!(tx_env.fee_token, fee_token); + } } diff --git a/crates/evm/core/src/evm.rs b/crates/evm/core/src/evm.rs index acf1bcc088519..814f6e58d6fe1 100644 --- a/crates/evm/core/src/evm.rs +++ b/crates/evm/core/src/evm.rs @@ -6,6 +6,7 @@ use std::{ use crate::{ FoundryBlock, FoundryContextExt, FoundryInspectorExt, FoundryTransaction, + FromAnyRpcTransaction, backend::{DatabaseExt, JournaledState}, constants::{CALLER, DEFAULT_CREATE2_DEPLOYER_CODEHASH, TEST_CONTRACT_ADDRESS}, tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner}, @@ -23,6 +24,7 @@ use alloy_rlp::Decodable; use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt}; use foundry_config::FromEvmVersion; use foundry_fork_db::{DatabaseError, ForkBlockEnv}; +use op_alloy_network::Optimism; use op_revm::OpHaltReason; use revm::{ Context, @@ -113,6 +115,14 @@ impl FoundryEvmNetwork for TempoEvmNetwork { type EvmFactory = TempoEvmFactory; } +// TODO: use `OpEvmFactory` once the FoundryEvmFactory impl is available for it. +#[derive(Clone, Copy, Debug, Default)] +pub struct OpEvmNetwork; +impl FoundryEvmNetwork for OpEvmNetwork { + type Network = Optimism; + type EvmFactory = EthEvmFactory; +} + /// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`]. pub type EvmFactoryFor = ::EvmFactory; pub type FoundryContextFor<'db, FEN> = @@ -134,7 +144,7 @@ pub trait FoundryEvmFactory: EvmFactory< Spec: Into + FromEvmVersion + Default + Display + Copy + Unpin + Send + 'static, BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin, - Tx: Clone + Debug + FoundryTransaction + Default + Send + Sync, + Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync, HaltReason: IntoInstructionResult, Precompiles = PrecompilesMap, > + Clone @@ -1119,6 +1129,22 @@ impl<'db, I: FoundryInspectorExt Result, Self::Error> { + self.inner.load_fee_fields(evm)?; + + match self.inspect_run_without_catch_error(evm) { + Ok(output) => Ok(output), + Err(e) => self.catch_error(evm, e), + } + } + /// Delegates to [`TempoEvmHandler::inspect_execution_with`], injecting the CREATE2 factory /// redirect exec loop. AA multi-call dispatch and gas adjustments are handled by the inner /// Tempo handler. diff --git a/crates/evm/core/src/precompiles.rs b/crates/evm/core/src/precompiles.rs index fd569dc30dc07..7ee9c66106de7 100644 --- a/crates/evm/core/src/precompiles.rs +++ b/crates/evm/core/src/precompiles.rs @@ -54,6 +54,11 @@ pub const BLS12_MAP_FP2_TO_G2: Address = address!("0x000000000000000000000000000 /// The P256VERIFY precompile address. pub const P256_VERIFY: Address = address!("0x0000000000000000000000000000000000000100"); +/// The Celo transfer precompile address. +/// +/// See +pub const CELO_TRANSFER: Address = address!("0x00000000000000000000000000000000000000fd"); + /// Precompile addresses. pub const PRECOMPILES: &[Address] = &[ EC_RECOVER, diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index 5dd3e550d766b..587d58bd2af74 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -19,8 +19,8 @@ pub struct ExecutorBuilder { stack: InspectorStackBuilder>, /// The gas limit. gas_limit: Option, - /// The spec. - spec: SpecFor, + /// The spec override. When `None`, the spec from `EvmEnv::cfg_env` is preserved. + spec: Option>, legacy_assertions: bool, } @@ -30,7 +30,7 @@ impl Default for ExecutorBuilder { Self { stack: InspectorStackBuilder::new(), gas_limit: None, - spec: Default::default(), + spec: None, legacy_assertions: false, } } @@ -52,10 +52,16 @@ impl ExecutorBuilder { /// Sets the EVM spec to use. #[inline] pub fn spec_id(mut self, spec: SpecFor) -> Self { - self.spec = spec; + self.spec = Some(spec); self } + /// Optionally sets the EVM spec. When `None`, the spec from `EvmEnv::cfg_env` is preserved. + #[inline] + pub fn spec_id_opt(self, spec: Option>) -> Self { + if let Some(spec) = spec { self.spec_id(spec) } else { self } + } + /// Sets the executor gas limit. #[inline] pub fn gas_limit(mut self, gas_limit: u64) -> Self { @@ -86,7 +92,9 @@ impl ExecutorBuilder { stack.gas_price = Some(tx_env.gas_price()); } let gas_limit = gas_limit.unwrap_or(evm_env.block_env.gas_limit()); - evm_env.cfg_env.set_spec(spec); + if let Some(spec) = spec { + evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec); + } Executor::new(db, evm_env, tx_env, stack.build(), gas_limit, legacy_assertions) } } diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 8d296dd1f1a06..54a77399fa2c9 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -37,7 +37,7 @@ impl TracingExecutor { .inspectors(|stack| { stack.trace_mode(trace_mode).networks(networks).create2_deployer(create2_deployer) }) - .spec_id(evm_spec_id::>(version.unwrap_or_default())) + .spec_id_opt(version.map(evm_spec_id::>)) .build(env.0, env.1, db); // Apply the state overrides. diff --git a/crates/evm/hardforks/Cargo.toml b/crates/evm/hardforks/Cargo.toml index a80a86b1a1e0b..9bf318028487a 100644 --- a/crates/evm/hardforks/Cargo.toml +++ b/crates/evm/hardforks/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true workspace = true [dependencies] +alloy-chains.workspace = true alloy-hardforks = { workspace = true, features = ["serde"] } alloy-op-hardforks = { workspace = true, features = ["serde"] } alloy-rpc-types.workspace = true diff --git a/crates/evm/hardforks/src/lib.rs b/crates/evm/hardforks/src/lib.rs index f85b74ad69048..73cfffe2077eb 100644 --- a/crates/evm/hardforks/src/lib.rs +++ b/crates/evm/hardforks/src/lib.rs @@ -5,6 +5,7 @@ use std::str::FromStr; +use alloy_chains::Chain; use alloy_rpc_types::BlockNumberOrTag; use foundry_compilers::artifacts::EvmVersion; use op_revm::OpSpecId; @@ -98,6 +99,22 @@ impl FoundryHardfork { Self::Tempo(h) => format!("{h}"), } } + + /// Auto-detect the active hardfork for a given chain at a specific timestamp. + /// + /// Tries Ethereum, then Optimism. Returns `None` for unknown chains. + pub fn from_chain_and_timestamp(chain_id: u64, timestamp: u64) -> Option { + let chain = Chain::from_id(chain_id); + if let Some(fork) = EthereumHardfork::from_chain_and_timestamp(chain, timestamp) { + return Some(Self::Ethereum(fork)); + } + if let Some(fork) = OpHardfork::from_chain_and_timestamp(chain, timestamp) { + return Some(Self::Optimism(fork)); + } + // TODO: add tempo support after https://github.com/tempoxyz/tempo/pull/3514 release + // providing TempoHardfork::from_chain_and_timestamp + None + } } impl From for FoundryHardfork { @@ -155,6 +172,15 @@ impl From for SpecId { } } +impl From for OpSpecId { + fn from(fork: FoundryHardfork) -> Self { + match fork { + FoundryHardfork::Optimism(hardfork) => spec_id_from_optimism_hardfork(hardfork), + _ => Self::default(), + } + } +} + /// Map an `EthereumHardfork` enum into its corresponding `SpecId`. pub fn spec_id_from_ethereum_hardfork(hardfork: EthereumHardfork) -> SpecId { match hardfork { @@ -203,7 +229,7 @@ pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId { } /// Trait for converting an [`EvmVersion`] into a network-specific spec type. -pub trait FromEvmVersion { +pub trait FromEvmVersion: From { fn from_evm_version(version: EvmVersion) -> Self; } @@ -313,4 +339,46 @@ mod tests { EthereumHardfork::London ); } + + #[test] + fn test_from_chain_and_timestamp_ethereum_mainnet() { + assert_eq!( + FoundryHardfork::from_chain_and_timestamp(1, 0), + Some(FoundryHardfork::Ethereum(EthereumHardfork::Frontier)) + ); + // Shanghai activated at timestamp 1681338455 on mainnet + assert_eq!( + FoundryHardfork::from_chain_and_timestamp(1, 1_681_338_455), + Some(FoundryHardfork::Ethereum(EthereumHardfork::Shanghai)) + ); + } + + #[test] + fn test_from_chain_and_timestamp_sepolia() { + let sepolia_chain_id = 11155111; + assert!(FoundryHardfork::from_chain_and_timestamp(sepolia_chain_id, u64::MAX).is_some()); + } + + #[test] + fn test_from_chain_and_timestamp_op_mainnet() { + let op_chain_id = 10; + assert!(matches!( + FoundryHardfork::from_chain_and_timestamp(op_chain_id, u64::MAX), + Some(FoundryHardfork::Optimism(_)) + )); + } + + #[test] + fn test_from_chain_and_timestamp_base() { + let base_chain_id = 8453; + assert!(matches!( + FoundryHardfork::from_chain_and_timestamp(base_chain_id, u64::MAX), + Some(FoundryHardfork::Optimism(_)) + )); + } + + #[test] + fn test_from_chain_and_timestamp_unknown_chain() { + assert_eq!(FoundryHardfork::from_chain_and_timestamp(999999, 0), None); + } } diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 0d31cfca6a7ae..90d2db724cebc 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -32,6 +32,9 @@ alloy-primitives = { workspace = true, features = [ alloy-sol-types.workspace = true revm-inspectors.workspace = true +tempo-contracts.workspace = true +tempo-precompiles.workspace = true + async-trait.workspace = true eyre.workspace = true futures.workspace = true diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 9ac46918a5372..bf53210aebbb4 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -22,10 +22,19 @@ use foundry_evm_core::{ BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256, }, + tempo::{ALPHA_USD_ADDRESS, BETA_USD_ADDRESS, THETA_USD_ADDRESS}, }; use itertools::Itertools; use revm_inspectors::tracing::types::{DecodedCallLog, DecodedCallTrace}; use std::{collections::BTreeMap, sync::OnceLock}; +use tempo_contracts::precompiles::{ + IAccountKeychain, IFeeManager, IStablecoinDEX, ITIP20Factory, ITIP403Registry, IValidatorConfig, +}; +use tempo_precompiles::{ + ACCOUNT_KEYCHAIN_ADDRESS, NONCE_PRECOMPILE_ADDRESS, PATH_USD_ADDRESS, STABLECOIN_DEX_ADDRESS, + TIP_FEE_MANAGER_ADDRESS, TIP20_FACTORY_ADDRESS, TIP403_REGISTRY_ADDRESS, + VALIDATOR_CONFIG_ADDRESS, nonce::INonce, tip20::ITIP20, +}; mod precompiles; @@ -94,6 +103,13 @@ impl CallTraceDecoderBuilder { self } + /// Sets the chain ID for network-specific precompile detection. + #[inline] + pub fn with_chain_id(mut self, chain_id: Option) -> Self { + self.decoder.chain_id = chain_id; + self + } + /// Sets the debug identifier for the decoder. #[inline] pub fn with_debug_identifier(mut self, identifier: DebugTraceIdentifier) -> Self { @@ -151,6 +167,9 @@ pub struct CallTraceDecoder { /// Disable showing of labels. pub disable_labels: bool, + + /// The chain ID, used to determine network-specific precompiles. + pub chain_id: Option, } impl CallTraceDecoder { @@ -192,6 +211,18 @@ impl CallTraceDecoder { (BLS12_MAP_FP_TO_G1, "BLS12_MAP_FP_TO_G1".to_string()), (BLS12_MAP_FP2_TO_G2, "BLS12_MAP_FP2_TO_G2".to_string()), (P256_VERIFY, "P256VERIFY".to_string()), + // Tempo + (TIP_FEE_MANAGER_ADDRESS, "FeeManager".to_string()), + (TIP403_REGISTRY_ADDRESS, "TIP403Registry".to_string()), + (TIP20_FACTORY_ADDRESS, "TIP20Factory".to_string()), + (STABLECOIN_DEX_ADDRESS, "StablecoinDex".to_string()), + (NONCE_PRECOMPILE_ADDRESS, "Nonce".to_string()), + (VALIDATOR_CONFIG_ADDRESS, "ValidatorConfig".to_string()), + (ACCOUNT_KEYCHAIN_ADDRESS, "AccountKeychain".to_string()), + (PATH_USD_ADDRESS, "PathUSD".to_string()), + (ALPHA_USD_ADDRESS, "AlphaUSD".to_string()), + (BETA_USD_ADDRESS, "BetaUSD".to_string()), + (THETA_USD_ADDRESS, "ThetaUSD".to_string()), ]), receive_contracts: Default::default(), fallback_contracts: Default::default(), @@ -200,11 +231,29 @@ impl CallTraceDecoder { functions: console::hh::abi::functions() .into_values() .chain(Vm::abi::functions().into_values()) + // Tempo + .chain(IFeeManager::abi::functions().into_values()) + .chain(ITIP20::abi::functions().into_values()) + .chain(ITIP403Registry::abi::functions().into_values()) + .chain(ITIP20Factory::abi::functions().into_values()) + .chain(IStablecoinDEX::abi::functions().into_values()) + .chain(INonce::abi::functions().into_values()) + .chain(IValidatorConfig::abi::functions().into_values()) + .chain(IAccountKeychain::abi::functions().into_values()) .flatten() .map(|func| (func.selector(), vec![func])) .collect(), events: console::ds::abi::events() .into_values() + // Tempo + .chain(IFeeManager::abi::events().into_values()) + .chain(ITIP20::abi::events().into_values()) + .chain(ITIP403Registry::abi::events().into_values()) + .chain(ITIP20Factory::abi::events().into_values()) + .chain(IStablecoinDEX::abi::events().into_values()) + .chain(INonce::abi::events().into_values()) + .chain(IValidatorConfig::abi::events().into_values()) + .chain(IAccountKeychain::abi::events().into_values()) .flatten() .map(|event| ((event.selector(), indexed_inputs(&event)), vec![event])) .collect(), @@ -216,6 +265,8 @@ impl CallTraceDecoder { debug_identifier: None, disable_labels: false, + + chain_id: None, } } @@ -249,6 +300,12 @@ impl CallTraceDecoder { identifier: &'a mut impl TraceIdentifier, ) -> Vec> { let nodes = arena.nodes().iter().filter(|node| { + // Skip precompile addresses, they will never resolve externally. + if node.is_precompile() + || precompiles::is_known_precompile(node.trace.address, self.chain_id) + { + return false; + } let address = &node.trace.address; !self.labels.contains_key(address) || !self.contracts.contains_key(address) }); @@ -391,7 +448,7 @@ impl CallTraceDecoder { return DecodedCallTrace { label, ..Default::default() }; } - if let Some(trace) = precompiles::decode(trace, 1) { + if let Some(trace) = precompiles::decode(trace, self.chain_id) { return trace; } @@ -794,7 +851,7 @@ impl CallTraceDecoder { // Ignore known addresses. if n.trace.address == DEFAULT_CREATE2_DEPLOYER || n.is_precompile() - || precompiles::is_known_precompile(n.trace.address, 1) + || precompiles::is_known_precompile(n.trace.address, self.chain_id) { return false; } @@ -1392,4 +1449,119 @@ mod tests { assert_eq!(result, expected, "Output case failed for: {function_signature}"); } } + + // A mock identifier that records which addresses it was asked to identify. + struct RecordingIdentifier { + queried: Vec
, + } + impl TraceIdentifier for RecordingIdentifier { + fn identify_addresses(&mut self, nodes: &[&CallTraceNode]) -> Vec> { + self.queried.extend(nodes.iter().map(|n| n.trace.address)); + Vec::new() + } + } + + #[test] + fn test_identify_addresses_skips_evm_precompiles() { + use foundry_evm_core::precompiles::SHA_256; + + let decoder = CallTraceDecoder::new(); + + let mut arena = CallTraceArena::default(); + let regular_addr = Address::from([0x42; 20]); + arena.nodes_mut()[0].trace.address = regular_addr; + + // Standard EVM precompile flagged by the inspector. + arena.nodes_mut().push(CallTraceNode { + trace: CallTrace { + address: SHA_256, + depth: 1, + maybe_precompile: Some(true), + ..Default::default() + }, + idx: 1, + ..Default::default() + }); + + // Standard EVM precompile NOT flagged, caught by is_known_precompile. + arena.nodes_mut().push(CallTraceNode { + trace: CallTrace { + address: SHA_256, + depth: 1, + maybe_precompile: None, + ..Default::default() + }, + idx: 2, + ..Default::default() + }); + + let mut identifier = RecordingIdentifier { queried: Vec::new() }; + decoder.identify_addresses(&arena, &mut identifier); + + assert_eq!(identifier.queried, vec![regular_addr]); + } + + #[test] + fn test_identify_addresses_skips_tempo_precompiles() { + use foundry_evm_core::tempo::TEMPO_PRECOMPILE_ADDRESSES; + + // Decoder with Tempo chain ID (4217). + let mut decoder = CallTraceDecoder::new().clone(); + decoder.chain_id = Some(4217); + + let mut arena = CallTraceArena::default(); + let regular_addr = Address::from([0x42; 20]); + arena.nodes_mut()[0].trace.address = regular_addr; + + // Tempo precompile — not flagged by inspector, caught by is_known_precompile + // only when chain_id is a Tempo chain. + let tempo_precompile = TEMPO_PRECOMPILE_ADDRESSES[0]; + arena.nodes_mut().push(CallTraceNode { + trace: CallTrace { + address: tempo_precompile, + depth: 1, + maybe_precompile: None, + ..Default::default() + }, + idx: 1, + ..Default::default() + }); + + let mut identifier = RecordingIdentifier { queried: Vec::new() }; + decoder.identify_addresses(&arena, &mut identifier); + + // On a Tempo chain, the Tempo precompile should be filtered out. + assert_eq!(identifier.queried, vec![regular_addr]); + } + + #[test] + fn test_identify_addresses_does_not_skip_tempo_precompiles_on_other_chains() { + use foundry_evm_core::tempo::TEMPO_PRECOMPILE_ADDRESSES; + + // Decoder with Ethereum mainnet chain ID (1). + let mut decoder = CallTraceDecoder::new().clone(); + decoder.chain_id = Some(1); + + let mut arena = CallTraceArena::default(); + let regular_addr = Address::from([0x42; 20]); + arena.nodes_mut()[0].trace.address = regular_addr; + + let tempo_precompile = TEMPO_PRECOMPILE_ADDRESSES[0]; + arena.nodes_mut().push(CallTraceNode { + trace: CallTrace { + address: tempo_precompile, + depth: 1, + maybe_precompile: None, + ..Default::default() + }, + idx: 1, + ..Default::default() + }); + + let mut identifier = RecordingIdentifier { queried: Vec::new() }; + decoder.identify_addresses(&arena, &mut identifier); + + // On Ethereum, Tempo precompile addresses are regular contracts — should NOT be filtered. + assert_eq!(identifier.queried, vec![regular_addr, tempo_precompile]); + } } diff --git a/crates/evm/traces/src/decoder/precompiles.rs b/crates/evm/traces/src/decoder/precompiles.rs index fdbe744641cd7..5b0a320477fad 100644 --- a/crates/evm/traces/src/decoder/precompiles.rs +++ b/crates/evm/traces/src/decoder/precompiles.rs @@ -1,10 +1,14 @@ use crate::{CallTrace, DecodedCallData}; use alloy_primitives::{Address, B256, U256, hex}; use alloy_sol_types::{SolCall, abi, sol}; -use foundry_evm_core::precompiles::{ - BLAKE_2F, BLS12_G1ADD, BLS12_G1MSM, BLS12_G2ADD, BLS12_G2MSM, BLS12_MAP_FP_TO_G1, - BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, - MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256, +use foundry_config::{Chain, NamedChain}; +use foundry_evm_core::{ + precompiles::{ + BLAKE_2F, BLS12_G1ADD, BLS12_G1MSM, BLS12_G2ADD, BLS12_G2MSM, BLS12_MAP_FP_TO_G1, + BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, CELO_TRANSFER, EC_ADD, EC_MUL, EC_PAIRING, + EC_RECOVER, IDENTITY, MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256, + }, + tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS}, }; use itertools::Itertools; use revm_inspectors::tracing::types::DecodedCallTrace; @@ -50,8 +54,9 @@ interface Precompiles { } use Precompiles::*; -pub(super) fn is_known_precompile(address: Address, _chain_id: u64) -> bool { - address[..19].iter().all(|&x| x == 0) +pub(super) fn is_known_precompile(address: Address, chain_id: Option) -> bool { + // Standard EVM precompiles (all chains). + let is_standard = address[..19].iter().all(|&x| x == 0) && matches!( address, EC_RECOVER @@ -72,12 +77,29 @@ pub(super) fn is_known_precompile(address: Address, _chain_id: u64) -> bool { | BLS12_MAP_FP_TO_G1 | BLS12_MAP_FP2_TO_G2 | P256_VERIFY - ) + ); + if is_standard { + return true; + } + // Tempo precompiles and TIP20 fee tokens (only on Tempo chains). + if chain_id.is_some_and(|id| Chain::from_id(id).is_tempo()) + && (TEMPO_PRECOMPILE_ADDRESSES.contains(&address) || TEMPO_TIP20_TOKENS.contains(&address)) + { + return true; + } + // Celo transfer precompile (only on Celo chains). + if chain_id.is_some_and(|id| { + matches!(Chain::from_id(id).named(), Some(NamedChain::Celo | NamedChain::CeloSepolia)) + }) && address == CELO_TRANSFER + { + return true; + } + false } /// Tries to decode a precompile call. Returns `Some` if successful. -pub(super) fn decode(trace: &CallTrace, _chain_id: u64) -> Option { - if !is_known_precompile(trace.address, _chain_id) { +pub(super) fn decode(trace: &CallTrace, chain_id: Option) -> Option { + if !is_known_precompile(trace.address, chain_id) { return None; } diff --git a/crates/evm/traces/src/identifier/external.rs b/crates/evm/traces/src/identifier/external.rs index e3ca8567cd95e..841c77c33f0de 100644 --- a/crates/evm/traces/src/identifier/external.rs +++ b/crates/evm/traces/src/identifier/external.rs @@ -191,12 +191,20 @@ impl TraceIdentifier for ExternalIdentifier { .map(|metadata| self.identify_from_metadata(address, metadata)); match self.contracts.entry(address) { Entry::Occupied(mut occupied_entry) => { - // Override if: - // - new is from Etherscan and old is not - // - new is Some and old is None, meaning verified only in one source - if !matches!(occupied_entry.get().0, FetcherKind::Etherscan) - || value.1.is_none() - { + let old = occupied_entry.get(); + // Only override when the new result is strictly better: + // - new has metadata and old doesn't, OR + // - both have metadata but new is from Etherscan and old is not. + // Never downgrade a successful lookup to None. + let should_replace = match (&old.1, &value.1) { + (None, Some(_)) => true, + (Some(_), None) => false, + _ => { + matches!(value.0, FetcherKind::Etherscan) + && !matches!(old.0, FetcherKind::Etherscan) + } + }; + if should_replace { occupied_entry.insert(value); } } @@ -317,6 +325,8 @@ impl Stream for ExternalFetcher { } Err(err) => { warn!(target: "evm::traces::external", ?err, "could not get info"); + // Cache the failure so we don't re-fetch on subsequent arenas. + return Poll::Ready(Some((addr, (pin.fetcher.kind(), None)))); } } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index c24dc0ba484d7..457eb1fc3222a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -61,6 +61,7 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true tempo-alloy.workspace = true diff --git a/crates/forge/src/cmd/config.rs b/crates/forge/src/cmd/config.rs index 49716146c456b..fac66727c9d99 100644 --- a/crates/forge/src/cmd/config.rs +++ b/crates/forge/src/cmd/config.rs @@ -53,8 +53,8 @@ impl ConfigArgs { } else { config.to_string_pretty()? }; - sh_println!("{s}")?; + Ok(()) } } diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 2d013ef2a6c68..a6337a266c9a8 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -349,7 +349,7 @@ impl CreateArgs { let is_args_empty = args.is_empty(); let mut deployer = - factory.deploy_tokens(args.clone()).context("failed to deploy contract").map_err(|e| { + factory.deploy_tokens(args.clone(), self.tx.tempo.fee_token).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") } else { @@ -681,7 +681,11 @@ impl + Clone> DeploymentTxFactory { pub fn deploy_tokens( self, params: Vec, - ) -> Result, ContractDeploymentError> { + fee_token: Option
, + ) -> Result, ContractDeploymentError> + where + N::TransactionRequest: FoundryTransactionBuilder, + { // Encode the constructor args & concatenate with the bytecode if necessary let data: Bytes = match (self.abi.constructor(), params.is_empty()) { (None, false) => return Err(ContractDeploymentError::ConstructorError), @@ -699,7 +703,9 @@ impl + Clone> DeploymentTxFactory { // create the tx object. Since we're deploying a contract, `to` is `None` let mut tx = N::TransactionRequest::default(); tx.set_input(data); - + if let Some(fee_token) = fee_token { + tx.set_fee_token(fee_token); + } Ok(Deployer { client: self.client.clone(), tx, confs: 1, timeout: self.timeout }) } } diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index d7902b37efe65..c0a2da8cd528b 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -41,7 +41,8 @@ use foundry_config::{ use foundry_debugger::Debugger; use foundry_evm::{ core::evm::{ - BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, SpecFor, TempoEvmNetwork, TxEnvFor, + BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, SpecFor, TempoEvmNetwork, + TxEnvFor, }, opts::EvmOpts, traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth}, @@ -353,6 +354,17 @@ impl TestArgs { decode_internal, ) .await? + } else if evm_opts.networks.is_optimism() { + self.build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + ) + .await? } else { self.build_and_run_tests::( config, @@ -596,7 +608,8 @@ impl TestArgs { let mut builder = CallTraceDecoderBuilder::new() .with_known_contracts(&known_contracts) .with_label_disabled(self.disable_labels) - .with_verbosity(verbosity); + .with_verbosity(verbosity) + .with_chain_id(remote_chain.map(|c| c.id())); // Signatures are of no value for gas reports. if !self.gas_report { builder = diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 37b058ba1a67e..fd9b4b889c2e2 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -372,6 +372,7 @@ impl TestRunnerConfig { self.evm_opts.clone(), Some(known_contracts), Some(artifact_id.clone()), + None, )); ExecutorBuilder::default() .inspectors(|stack| { diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index cb942510bbb49..7316f4c4239b7 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -1,103 +1,27 @@ use super::AsmKeccak256; use crate::{ - linter::{LateLintPass, LintContext}, + declare_forge_lint, + linter::EarlyLintPass, sol::{Severity, SolLint}, }; -use solar::{ - ast::{self as ast, Span}, - interface::kw, - sema::hir::{self}, -}; +use solar_ast::{Expr, ExprKind}; +use solar_interface::kw; declare_forge_lint!( ASM_KECCAK256, Severity::Gas, "asm-keccak256", - "use of inefficient hashing mechanism; consider using inline assembly" + "hash using inline assembly to save gas" ); -impl<'hir> LateLintPass<'hir> for AsmKeccak256 { - fn check_stmt( - &mut self, - ctx: &LintContext, - hir: &'hir hir::Hir<'hir>, - stmt: &'hir hir::Stmt<'hir>, - ) { - let check_expr_and_emit_lint = - |expr: &'hir hir::Expr<'hir>, assign: Option, is_return: bool| { - if let Some(hash_arg) = extract_keccak256_arg(expr) { - self.emit_lint( - ctx, - hir, - stmt.span, - expr, - hash_arg, - AsmContext { _assign: assign, _is_return: is_return }, - ); - } - }; - - match stmt.kind { - hir::StmtKind::DeclSingle(var_id) => { - let var = hir.variable(var_id); - if let Some(init) = var.initializer { - // Constants should be optimized by the compiler, so no gas savings apply. - if !matches!(var.mutability, Some(hir::VarMut::Constant)) { - check_expr_and_emit_lint(init, var.name, false); - } +impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 { + fn check_expr(&mut self, ctx: &crate::linter::LintContext<'_>, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(expr, _) = &expr.kind { + if let ExprKind::Ident(ident) = &expr.kind { + if ident.name == kw::Keccak256 { + ctx.emit(&ASM_KECCAK256, expr.span); } } - // Expressions that don't (directly) assign to a variable - hir::StmtKind::Expr(expr) - | hir::StmtKind::Emit(expr) - | hir::StmtKind::Revert(expr) - | hir::StmtKind::DeclMulti(_, expr) - | hir::StmtKind::If(expr, ..) => check_expr_and_emit_lint(expr, None, false), - hir::StmtKind::Return(Some(expr)) => check_expr_and_emit_lint(expr, None, true), - _ => (), } } } - -impl AsmKeccak256 { - /// Emits lints (when possible with fix suggestions) for inefficient `keccak256` calls. - fn emit_lint( - &self, - ctx: &LintContext, - _hir: &hir::Hir<'_>, - _stmt_span: Span, - call: &hir::Expr<'_>, - _hash: &hir::Expr<'_>, - _asm_ctx: AsmContext, - ) { - ctx.emit(&ASM_KECCAK256, call.span); - } -} - -/// If the expression is a call to `keccak256` with one argument, returns that argument. -fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Expr<'hir>> { - let hir::ExprKind::Call( - callee, - hir::CallArgs { kind: hir::CallArgsKind::Unnamed(args), .. }, - .., - ) = &expr.kind - else { - return None; - }; - - let is_keccak = if let hir::ExprKind::Ident([hir::Res::Builtin(builtin)]) = callee.kind { - matches!(builtin.name(), kw::Keccak256) - } else { - return None; - }; - - if is_keccak && args.len() == 1 { Some(&args[0]) } else { None } -} - -// -- HELPER FUNCTIONS AND STRUCTS ---------------------------------------------------------------- - -#[derive(Debug, Clone, Copy)] -struct AsmContext { - _assign: Option, - _is_return: bool, -} diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 68df630bd1127..1da825accded9 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -340,6 +340,8 @@ impl ExecutedState { &self, known_contracts: &ContractsByArtifact, ) -> Result { + let chain_id = self.script_config.evm_opts.get_remote_chain_id().await; + let mut decoder = CallTraceDecoderBuilder::new() .with_labels(self.execution_result.labeled_addresses.clone()) .with_verbosity(self.script_config.evm_opts.verbosity) @@ -348,12 +350,12 @@ impl ExecutedState { &self.script_config.config, )?) .with_label_disabled(self.args.disable_labels) + .with_chain_id(chain_id.map(|c| c.id())) .build(); - let mut identifier = TraceIdentifiers::new().with_local(known_contracts).with_external( - &self.script_config.config, - self.script_config.evm_opts.get_remote_chain_id().await, - )?; + let mut identifier = TraceIdentifiers::new() + .with_local(known_contracts) + .with_external(&self.script_config.config, chain_id)?; for (_, trace) in &self.execution_result.traces { decoder.identify(trace, &mut identifier); diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index b5f8d9fb55097..4ba84333874bc 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -29,7 +29,7 @@ use forge_script_sequence::{AdditionalContract, NestedValue}; use forge_verify::{RetryArgs, VerifierArgs}; use foundry_cli::{ opts::{BuildOpts, EvmArgs, GlobalArgs}, - utils::LoadConfig, + utils::{LoadConfig, parse_fee_token_address}, }; use foundry_common::{ CONTRACT_MAX_SIZE, ContractsByArtifact, SELECTOR_LEN, @@ -47,8 +47,9 @@ use foundry_config::{ use foundry_evm::{ backend::Backend, core::{ - Breakpoints, - evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork}, + Breakpoints, FoundryTransaction, + evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, + tempo::PATH_USD_ADDRESS, }, executors::ExecutorBuilder, inspectors::{ @@ -138,7 +139,7 @@ pub struct ScriptArgs { pub batch_size: usize, /// Tempo fee token address for paying transaction fees. - #[arg(long = "tempo.fee-token", value_name = "ADDRESS")] + #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] pub fee_token: Option
, /// Skips on-chain simulation. @@ -244,12 +245,24 @@ pub struct ScriptArgs { } impl ScriptArgs { - pub async fn preprocess(self) -> Result> { + /// Loads config, resolves evm_opts (including network inference from fork), and returns them. + async fn resolved_evm_opts(&self) -> Result<(Config, EvmOpts)> { + let (config, mut evm_opts) = self.load_config_and_evm_opts()?; + + // Auto-detect network from fork chain ID when not explicitly configured. + evm_opts.infer_network_from_fork().await; + + Ok((config, evm_opts)) + } + + async fn preprocess( + self, + config: Config, + mut evm_opts: EvmOpts, + ) -> Result> { let script_wallets = Wallets::new(self.wallets.get_multi_wallet().await?, self.evm.sender); let browser_wallet = self.wallets.browser_signer::().await?; - let (config, mut evm_opts) = self.load_config_and_evm_opts()?; - if let Some(sender) = self.maybe_load_private_key()? { evm_opts.sender = sender; } else if self.evm.sender.is_none() { @@ -265,8 +278,13 @@ impl ScriptArgs { } } - let script_config = ScriptConfig::new(config, evm_opts, self.batch, self.fee_token).await?; + let fee_token = if evm_opts.networks.is_tempo() && self.fee_token.is_none() { + Some(PATH_USD_ADDRESS) + } else { + self.fee_token + }; + let script_config = ScriptConfig::new(config, evm_opts, self.batch, fee_token).await?; Ok(PreprocessedState { args: self, script_config, script_wallets, browser_wallet }) } @@ -274,10 +292,7 @@ impl ScriptArgs { pub async fn run_script(self) -> Result<()> { trace!(target: "script", "executing script command"); - let (_, mut evm_opts) = self.load_config_and_evm_opts()?; - - // Auto-detect network from fork chain ID when not explicitly configured. - evm_opts.infer_network_from_fork().await; + let (config, evm_opts) = self.resolved_evm_opts().await?; let is_tempo = evm_opts.networks.is_tempo(); @@ -287,7 +302,7 @@ impl ScriptArgs { if is_tempo { let batch = self.batch; - let bundled = match self.prepare_bundled::().await? { + let bundled = match self.prepare_bundled::(config, evm_opts).await? { Some(bundled) => bundled, None => return Ok(()), }; @@ -298,16 +313,22 @@ impl ScriptArgs { broadcasted.verify().await?; } Ok(()) + } else if evm_opts.networks.is_optimism() { + self.run_generic_script::(config, evm_opts).await } else { - self.run_generic_script::().await + self.run_generic_script::(config, evm_opts).await } } /// Prepares the bundled state (compile, simulate, bundle) and returns it /// for broadcasting, or returns `None` if there's nothing to broadcast /// (e.g., debug mode, no transactions, missing RPCs). - async fn prepare_bundled(self) -> Result>> { - let state = self.preprocess::().await?; + async fn prepare_bundled( + self, + config: Config, + evm_opts: EvmOpts, + ) -> Result>> { + let state = self.preprocess::(config, evm_opts).await?; let create2_deployer = state.script_config.evm_opts.create2_deployer; let compiled = state.compile()?; @@ -396,8 +417,12 @@ impl ScriptArgs { Ok(Some(bundled)) } - async fn run_generic_script(self) -> Result<()> { - let bundled = match self.prepare_bundled::().await? { + async fn run_generic_script( + self, + config: Config, + evm_opts: EvmOpts, + ) -> Result<()> { + let bundled = match self.prepare_bundled::(config, evm_opts).await? { Some(bundled) => bundled, None => return Ok(()), }; @@ -724,7 +749,7 @@ impl ScriptConfig { debug: bool, ) -> Result> { trace!("preparing script runner"); - let (evm_env, tx_env, fork_block) = self.evm_opts.env().await?; + let (evm_env, mut tx_env, fork_block) = self.evm_opts.env::<_, _, TxEnvFor>().await?; let db = if let Some(fork_url) = self.evm_opts.fork_url.as_ref() { match self.backends.get(fork_url) { @@ -766,6 +791,7 @@ impl ScriptConfig { self.evm_opts.clone(), Some(known_contracts), Some(target), + self.fee_token, ) .into(), ) @@ -774,6 +800,10 @@ impl ScriptConfig { }); } + // Propagate fee token to the transaction environment so that internal EVM calls + // (e.g. script deployment, setUp) use the correct fee token for Tempo networks. + tx_env.set_fee_token(self.fee_token); + Ok(ScriptRunner::new(builder.build(evm_env, tx_env, db), self.evm_opts.clone())) } } diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 468514b0646f0..f07b4b7a3168d 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -6,7 +6,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; -use foundry_common::TransactionMaybeSigned; +use foundry_common::{FoundryTransactionBuilder, TransactionMaybeSigned}; use foundry_config::Config; use foundry_evm::{ constants::CALLER, @@ -77,10 +77,14 @@ impl ScriptRunner { traces.push((TraceKind::Deployment, deploy_traces)); } - let mut tx_req = TransactionRequestFor::::default(); - tx_req.set_from(self.evm_opts.sender); - tx_req.set_input(code.clone()); - tx_req.set_nonce(sender_nonce + library_transactions.len() as u64); + let mut tx_req = TransactionRequestFor::::default() + .with_from(self.evm_opts.sender) + .with_input(code.clone()) + .with_nonce(sender_nonce + library_transactions.len() as u64); + + if let Some(fee_token) = script_config.fee_token { + tx_req.set_fee_token(fee_token); + } library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), @@ -110,11 +114,16 @@ impl ScriptRunner { traces.push((TraceKind::Deployment, deploy_traces)); } - let mut tx_req = TransactionRequestFor::::default(); - tx_req.set_from(self.evm_opts.sender); - tx_req.set_input(calldata); - tx_req.set_nonce(sender_nonce + library_transactions.len() as u64); - tx_req.set_to(create2_deployer); + let mut tx_req = TransactionRequestFor::::default() + .with_from(self.evm_opts.sender) + .with_input(calldata) + .with_nonce(sender_nonce + library_transactions.len() as u64) + .with_to(create2_deployer); + + if let Some(fee_token) = script_config.fee_token { + tx_req.set_fee_token(fee_token); + } + library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), transaction: TransactionMaybeSigned::new(tx_req), diff --git a/crates/test-utils/src/rpc.rs b/crates/test-utils/src/rpc.rs index 229e48cd871a5..ca787105418b3 100644 --- a/crates/test-utils/src/rpc.rs +++ b/crates/test-utils/src/rpc.rs @@ -101,6 +101,7 @@ pub fn rpc_endpoints() -> RpcEndpoints { ("mainnet2", RpcEndpointUrl::Url(next_http_archive_rpc_url())), ("sepolia", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Sepolia))), ("optimism", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Optimism))), + ("base", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Base))), ("arbitrum", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Arbitrum))), ("polygon", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Polygon))), ("bsc", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::BinanceSmartChain))), diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 9f5f502c241d1..8345a1c2bf975 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,9 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); - fs::copy(file, to_dir.join(name))?; + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } else { + continue; + } + fs::copy(&file, to_dir.join(name))?; } Ok(()) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 75c68b278fcf1..c1f56b38d9d5d 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -4,7 +4,7 @@ use std::{ env, fs::{self, File}, io::{Read, Seek, Write}, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf}, process::Command, sync::LazyLock, }; @@ -231,15 +231,59 @@ pub fn read_string(path: impl AsRef) -> String { /// copied to temporary test workspaces. pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { fs::create_dir_all(dst)?; - copy_dir_filtered_inner(src, dst, true) + // Canonicalize the source once and treat it as the base directory for all traversal. + let base = src.canonicalize()?; + // Canonicalize the destination once and treat it as the base directory for all writes. + let dst_base = dst.canonicalize()?; + copy_dir_filtered_inner(src, dst, &base, &dst_base, true) } -fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Result<()> { +fn copy_dir_filtered_inner( + src: &Path, + dst: &Path, + base_src: &Path, + base_dst: &Path, + is_root: bool, +) -> std::io::Result<()> { for entry in fs::read_dir(src)? { let entry = entry?; let ty = entry.file_type()?; let src_path = entry.path(); - let dst_path = dst.join(entry.file_name()); + // Ensure that any path we operate on stays within the original source base directory. + let canonical_src_path = src_path.canonicalize()?; + if !canonical_src_path.starts_with(base_src) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + let relative_src_path = canonical_src_path.strip_prefix(base_src).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "failed to derive relative path within source base directory", + ) + })?; + for component in relative_src_path.components() { + match component { + Component::Normal(name) => { + let name = name.to_string_lossy(); + if name.contains("..") || name.contains('/') || name.contains('\\') { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "invalid path component in source entry", + )); + } + } + Component::CurDir => {} + Component::ParentDir | Component::RootDir | Component::Prefix(_) => { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + } + } + let dst_path = base_dst.join(relative_src_path); if ty.is_dir() { // Skip build artifact directories at the root level @@ -249,10 +293,58 @@ fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Re { continue; } + // Ensure that the destination directory stays within the allowed destination base. + let canonical_dst_dir = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // Directory does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_dir.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } fs::create_dir_all(&dst_path)?; - copy_dir_filtered_inner(&src_path, &dst_path, false)?; + copy_dir_filtered_inner(&src_path, &dst_path, base_src, base_dst, false)?; } else { - fs::copy(&src_path, &dst_path)?; + // Ensure that the destination file path stays within the allowed destination base. + let canonical_dst_path = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // File does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_path.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + fs::copy(&canonical_src_path, &canonical_dst_path)?; } } Ok(()) diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index b1bc0835779c2..a02dadf563922 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -304,7 +304,7 @@ pub fn deploy_contract( to: Option, ) -> Result { let mut evm_env = evm_env.clone(); - evm_env.cfg_env.set_spec(spec_id); + evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id); if to.is_some_and(|to| to.is_call()) { let TxKind::Call(to) = to.unwrap() else { unreachable!() }; diff --git a/npm/package.json b/npm/package.json index 2362816053f8c..d596a7930b609 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.0.2", "bun": "^1.3.1", "typescript": "^5.9.3" }, diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix diff --git a/npm/src/const.mjs b/npm/src/const.mjs index 6b3dcf3f9fbed..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** diff --git a/sleep.json b/sleep.json new file mode 100644 index 0000000000000..5b430e1e663f6 --- /dev/null +++ b/sleep.json @@ -0,0 +1,955 @@ +{ + "results": [ + { + "command": "sleep 0.020", + "mean": 0.023726515413333333, + "stddev": 0.004602014051751124, + "median": 0.02267755758, + "user": 0.0013185473333333334, + "system": 0.0020899164444444446, + "min": 0.02109890308, + "max": 0.05602819808, + "times": [ + 0.02856005608, + 0.02346135008, + 0.02202502208, + 0.02139558708, + 0.02265920408, + 0.02121691608, + 0.02272505608, + 0.02114247908, + 0.02157142808, + 0.021514666079999998, + 0.02161920108, + 0.02335035008, + 0.02224331408, + 0.02228639708, + 0.02152537208, + 0.021732302079999998, + 0.02273370308, + 0.02115513608, + 0.02268494308, + 0.02244547308, + 0.023943647079999998, + 0.02324528508, + 0.02152617908, + 0.023991903079999998, + 0.02250884108, + 0.02342551708, + 0.02113216608, + 0.02168223108, + 0.02222267508, + 0.02273532108, + 0.02273995308, + 0.05602819808, + 0.02501500608, + 0.03121396008, + 0.02424400108, + 0.02459129108, + 0.02633760708, + 0.02377406808, + 0.02365474708, + 0.02406064008, + 0.02300910408, + 0.02437339208, + 0.02317403908, + 0.02257532008, + 0.02267017208, + 0.02356714508, + 0.02367204808, + 0.02258227108, + 0.02330384008, + 0.02225645108, + 0.02478414908, + 0.02484724308, + 0.02270765708, + 0.02339114708, + 0.02450795908, + 0.02348840008, + 0.044674490080000004, + 0.028041754080000002, + 0.022940745079999998, + 0.02259975308, + 0.022112378079999998, + 0.02271348408, + 0.02320266708, + 0.02284982108, + 0.02244050908, + 0.02238655808, + 0.022084648079999998, + 0.02241669808, + 0.02523103408, + 0.02256237908, + 0.03532525108, + 0.02232798408, + 0.02173793008, + 0.021903001079999998, + 0.02288046308, + 0.02368652508, + 0.02211418708, + 0.02265551308, + 0.02187778308, + 0.02191395108, + 0.02182523808, + 0.02185612208, + 0.02109890308, + 0.02294132008, + 0.02191512608, + 0.02264461208, + 0.02227651108, + 0.02307147508, + 0.02227169708, + 0.02177434208 + ], + "memory_usage_byte": [ + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.021", + "mean": 0.022889189941111117, + "stddev": 0.0007161191938371117, + "median": 0.02280623708, + "user": 0.0009166992592592593, + "system": 0.0016941181481481477, + "min": 0.02132554808, + "max": 0.02453766808, + "times": [ + 0.02311599608, + 0.02274468508, + 0.02193879008, + 0.02158843608, + 0.02329398008, + 0.02379494508, + 0.02260801308, + 0.02439507908, + 0.02448522508, + 0.02403379508, + 0.02298143008, + 0.02263027308, + 0.02229235308, + 0.02335063508, + 0.02377098008, + 0.02269184108, + 0.023631199079999998, + 0.02338021508, + 0.02198521708, + 0.02251586208, + 0.022295963079999998, + 0.02226397608, + 0.02453766808, + 0.02184453408, + 0.02289659908, + 0.02382663208, + 0.02347397108, + 0.02225926308, + 0.02207640608, + 0.02243237108, + 0.02278192608, + 0.02270514808, + 0.02245069008, + 0.023018867079999998, + 0.02399866208, + 0.02236840708, + 0.02366382208, + 0.02294188908, + 0.02155127708, + 0.02294999808, + 0.02132554808, + 0.02242025908, + 0.02202766108, + 0.02182175108, + 0.02272186608, + 0.02211805308, + 0.02319764908, + 0.022308045079999998, + 0.02345400908, + 0.022437877079999998, + 0.02273417808, + 0.02217370908, + 0.02254318408, + 0.023269922079999998, + 0.02384951108, + 0.02419476108, + 0.02439866908, + 0.02354840508, + 0.02304219108, + 0.02354960608, + 0.02382648708, + 0.02345751208, + 0.02367913708, + 0.02253067208, + 0.02215132608, + 0.022603942079999998, + 0.02284062808, + 0.02252907808, + 0.02220393508, + 0.023291509079999998, + 0.02399456908, + 0.02407123208, + 0.02279175108, + 0.02300624708, + 0.02309500408, + 0.023036532079999998, + 0.02303833108, + 0.02316846908, + 0.02228349608, + 0.02247140608, + 0.022482600079999998, + 0.02370720808, + 0.02220123708, + 0.02230588608, + 0.02333678708, + 0.02153336008, + 0.02203071908, + 0.02279195108, + 0.02353659108, + 0.02267460708, + 0.022536274079999998, + 0.022769262079999998, + 0.02314857808, + 0.02194885908, + 0.02355038408, + 0.02320035308, + 0.02307451408, + 0.02379926408, + 0.02330480208, + 0.02257055708, + 0.02330320308, + 0.02303003208, + 0.02327859908, + 0.02171311608, + 0.02282052308, + 0.02170123708, + 0.02254831308, + 0.02235855408 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.022", + "mean": 0.02415569324504855, + "stddev": 0.0009830972994273135, + "median": 0.02409406108, + "user": 0.001165289514563107, + "system": 0.001767603883495146, + "min": 0.02243173808, + "max": 0.02755932908, + "times": [ + 0.02456728108, + 0.02650439708, + 0.02480475408, + 0.02452974808, + 0.02300978308, + 0.02521451608, + 0.02543841408, + 0.02538411108, + 0.02475773908, + 0.02403843308, + 0.02426362708, + 0.02326921708, + 0.02447185308, + 0.02361749008, + 0.02410661008, + 0.02371481508, + 0.02327300908, + 0.02430165908, + 0.02328269108, + 0.02315262608, + 0.02380195808, + 0.02283639508, + 0.02491355808, + 0.02401717008, + 0.02556049408, + 0.02350359508, + 0.02400529208, + 0.02533555808, + 0.02467923308, + 0.02478442308, + 0.02422068708, + 0.02352175108, + 0.02481882108, + 0.02456148108, + 0.02314905108, + 0.024188183079999998, + 0.02483985908, + 0.02289141308, + 0.02364977308, + 0.02354907008, + 0.02379135508, + 0.026812933079999997, + 0.023360627079999998, + 0.02331436308, + 0.02504176308, + 0.02358805508, + 0.02409406108, + 0.02350689508, + 0.02303628508, + 0.02430972408, + 0.02516170908, + 0.02352843108, + 0.02274564308, + 0.02345165808, + 0.02429327308, + 0.02252948108, + 0.02445868508, + 0.02755932908, + 0.02522621808, + 0.02491753008, + 0.022858510079999998, + 0.02401968108, + 0.02409596908, + 0.02390450108, + 0.02373108808, + 0.027211489079999998, + 0.02537487108, + 0.02319182608, + 0.02390569508, + 0.02490164708, + 0.02384732708, + 0.02243173808, + 0.02367003008, + 0.02494288308, + 0.02436298308, + 0.02390639308, + 0.02423030808, + 0.02430082908, + 0.02320845908, + 0.02421546708, + 0.02530823508, + 0.02368935308, + 0.02306283708, + 0.023536658079999998, + 0.02359881208, + 0.02438320308, + 0.02477724008, + 0.02362231908, + 0.02419465008, + 0.02596891608, + 0.02307578608, + 0.02459456508, + 0.02384055408, + 0.02421387408, + 0.02510733208, + 0.02473580508, + 0.02243970708, + 0.02253156008, + 0.02550018108, + 0.02440877608, + 0.02281331608, + 0.02354148408, + 0.02352098308 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] +} diff --git a/testdata/default/repros/Issue14212.t.sol b/testdata/default/repros/Issue14212.t.sol new file mode 100644 index 0000000000000..0135e072bb72f --- /dev/null +++ b/testdata/default/repros/Issue14212.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "utils/Test.sol"; + +// https://github.com/foundry-rs/foundry/issues/14212 +// EthEvmNetwork uses Ethereum as its Network type, which cannot deserialize +// OP Stack deposit transactions (type 0x7e). These tests verify that the fork +// backend can handle blocks and transactions containing deposit txs. + +contract Issue14212Test is Test { + // Base block 30434326 contains a deposit tx at index 0: + // tx: 0x6fc82bcdcdeba0385c3910cd8e92074e51aaa9f21528dbc4c242f560a2f27bab + // type: 0x7e (deposit) + // from: 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001 + // to: 0x4200000000000000000000000000000000000015 + // + // A regular tx in the same block: + // tx: 0xe2f4bffbcc88dd94cabf9b15e2318df0afc2ec895012274d0ecec3d27d6da3e2 + + /// vm.transact on an OP deposit tx should not revert with a deserialization error. + /// This exercises the fork backend's get_transaction codepath. + function test_transactDepositTxOnBase() public { + // Fork Base at the block before the deposit tx + vm.createSelectFork("base", 30434325); + + // Transact the deposit tx from the next block. + // This calls fork.backend().get_transaction() which uses FEN::Network + // to deserialize the response. With Network = Ethereum, this fails: + // "deserialization error: data did not match any variant of untagged enum BlockTransactions" + vm.transact(0xe2f4bffbcc88dd94cabf9b15e2318df0afc2ec895012274d0ecec3d27d6da3e2); + } + + /// vm.rollFork to a tx hash in a block containing deposit txs should work. + /// This exercises the fork backend's get_full_block codepath. + function test_rollForkToTxOnBase() public { + vm.createSelectFork("base", 30434325); + + // Roll to a regular tx in block 30434326 which also contains deposit txs. + // This calls get_full_block internally which must deserialize the entire + // block including the deposit tx. + bytes32 txHash = 0xe2f4bffbcc88dd94cabf9b15e2318df0afc2ec895012274d0ecec3d27d6da3e2; + vm.rollFork(txHash); + } + + /// vm.transact on an OP deposit tx on Optimism mainnet. + function test_transactDepositTxOnOptimism() public { + // Optimism block 127867197 contains deposit tx at index 0: + // tx: 0x56b04a2e66cba482270c6e68244a8faaa59a1e1878a04086a12514be2d6e14f9 + vm.createSelectFork("optimism", 127867196); + + // Transact a regular tx from the block containing deposit txs. + // The regular tx at index 1: + vm.transact(0xa003e419e2d7502269eb5eda56947b580120e00abfd5b5460d08f8af44a0c24f); + } +} diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 8c60909ff66c9..6307668074860 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -54,6 +54,7 @@ mainnet = "${RPC_MAINNET}" mainnet2 = "${RPC_MAINNET2}" sepolia = "${RPC_SEPOLIA}" optimism = "${RPC_OPTIMISM}" +base = "${RPC_BASE}" arbitrum = "${RPC_ARBITRUM}" polygon = "${RPC_POLYGON}" bsc = "${RPC_BSC}" From 88997dc3ae1b1c4d2df5754625a1c1bae2cfd985 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 10 Apr 2026 12:55:32 +0700 Subject: [PATCH 275/374] Revise Foundry benchmark results and system info (#408) Updated benchmark results for Foundry versions v1.3.6 and v1.4.0-rc1, including new test data and system information. https://github.com/foundry-rs/foundry/commit/1c4d334e95bc1cad3a390cc735d0f3ec59eea07f Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- benches/LATEST.md | 71 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/benches/LATEST.md b/benches/LATEST.md index 00776abc94003..7ea1049a2ac41 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,28 +1,73 @@ # Foundry Benchmark Results -**Date**: 2026-01-27 03:38:34 +**Date**: 2025-10-02 12:14:23 -## Summary +## Repositories Tested -Benchmarked 2 Foundry versions across 1 repository. +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [Vectorized/solady](https://github.com/Vectorized/solady) +3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) +4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) -### Repositories Tested +## Foundry Versions -1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +- **v1.3.6**: forge Version: 1.3.6-v1.3.6 (d241588 2025-09-16) +- **v1.4.0-rc1**: forge Version: 1.4.0-v1.4.0-rc1 (bd0e4a7 2025-10-01) + +## Forge Test + +| Repository | v1.3.6 | v1.4.0-rc1 | +| -------------------- | ------- | ---------- | +| ithacaxyz-account | 3.17 s | 2.94 s | +| solady | 2.28 s | 2.10 s | +| Uniswap-v4-core | 7.27 s | 6.13 s | +| sparkdotfi-spark-psm | 43.04 s | 44.08 s | + +## Forge Fuzz Test -### Foundry Versions +| Repository | v1.3.6 | v1.4.0-rc1 | +| -------------------- | ------ | ---------- | +| ithacaxyz-account | 3.18 s | 3.02 s | +| solady | 2.39 s | 2.24 s | +| Uniswap-v4-core | 6.84 s | 6.20 s | +| sparkdotfi-spark-psm | 3.07 s | 2.72 s | -- **stable**: forge Version: 1.5.0-dev (6e718be 2025-12-07) -- **nightly**: forge Version: 1.5.0-dev (6e718be 2025-12-07) +## Forge Test (Isolated) + +| Repository | v1.3.6 | v1.4.0-rc1 | +| -------------------- | ------- | ---------- | +| solady | 2.26 s | 2.41 s | +| Uniswap-v4-core | 7.22 s | 7.71 s | +| sparkdotfi-spark-psm | 45.53 s | 50.49 s | + +## Forge Build (No Cache) + +| Repository | v1.3.6 | v1.4.0-rc1 | +| -------------------- | ------- | ---------- | +| ithacaxyz-account | 9.16 s | 9.08 s | +| solady | 14.62 s | 14.69 s | +| Uniswap-v4-core | 2m 3.8s | 2m 5.3s | +| sparkdotfi-spark-psm | 13.17 s | 13.14 s | ## Forge Build (With Cache) -| Repository | stable | nightly | -|------------|----------|----------| -| ithacaxyz-account | 0.345 s | 0.279 s | +| Repository | v1.3.6 | v1.4.0-rc1 | +| -------------------- | ------- | ---------- | +| ithacaxyz-account | 0.156 s | 0.113 s | +| solady | 0.089 s | 0.094 s | +| Uniswap-v4-core | 0.133 s | 0.127 s | +| sparkdotfi-spark-psm | 0.173 s | 0.131 s | + +## Forge Coverage + +| Repository | v1.3.6 | v1.4.0-rc1 | +| -------------------- | -------- | ---------- | +| ithacaxyz-account | 14.91 s | 13.34 s | +| Uniswap-v4-core | 1m 34.8s | 1m 30.3s | +| sparkdotfi-spark-psm | 3m 49.3s | 3m 40.2s | ## System Information -- **OS**: linux +- **OS**: macos - **CPU**: 8 -- **Rustc**: rustc 1.93.0 (254b59607 2026-01-19) +- **Rustc**: rustc 1.90.0-nightly (3014e79f9 2025-07-15) From 6dc7d961171a13e9f6b11a96cbce2127bd8b92fc Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:02:37 +0700 Subject: [PATCH 276/374] Revert "benches\LATEST.md (#350)" (#409) This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. --- sleep.json | 955 ----------------------------------------------------- 1 file changed, 955 deletions(-) delete mode 100644 sleep.json diff --git a/sleep.json b/sleep.json deleted file mode 100644 index 5b430e1e663f6..0000000000000 --- a/sleep.json +++ /dev/null @@ -1,955 +0,0 @@ -{ - "results": [ - { - "command": "sleep 0.020", - "mean": 0.023726515413333333, - "stddev": 0.004602014051751124, - "median": 0.02267755758, - "user": 0.0013185473333333334, - "system": 0.0020899164444444446, - "min": 0.02109890308, - "max": 0.05602819808, - "times": [ - 0.02856005608, - 0.02346135008, - 0.02202502208, - 0.02139558708, - 0.02265920408, - 0.02121691608, - 0.02272505608, - 0.02114247908, - 0.02157142808, - 0.021514666079999998, - 0.02161920108, - 0.02335035008, - 0.02224331408, - 0.02228639708, - 0.02152537208, - 0.021732302079999998, - 0.02273370308, - 0.02115513608, - 0.02268494308, - 0.02244547308, - 0.023943647079999998, - 0.02324528508, - 0.02152617908, - 0.023991903079999998, - 0.02250884108, - 0.02342551708, - 0.02113216608, - 0.02168223108, - 0.02222267508, - 0.02273532108, - 0.02273995308, - 0.05602819808, - 0.02501500608, - 0.03121396008, - 0.02424400108, - 0.02459129108, - 0.02633760708, - 0.02377406808, - 0.02365474708, - 0.02406064008, - 0.02300910408, - 0.02437339208, - 0.02317403908, - 0.02257532008, - 0.02267017208, - 0.02356714508, - 0.02367204808, - 0.02258227108, - 0.02330384008, - 0.02225645108, - 0.02478414908, - 0.02484724308, - 0.02270765708, - 0.02339114708, - 0.02450795908, - 0.02348840008, - 0.044674490080000004, - 0.028041754080000002, - 0.022940745079999998, - 0.02259975308, - 0.022112378079999998, - 0.02271348408, - 0.02320266708, - 0.02284982108, - 0.02244050908, - 0.02238655808, - 0.022084648079999998, - 0.02241669808, - 0.02523103408, - 0.02256237908, - 0.03532525108, - 0.02232798408, - 0.02173793008, - 0.021903001079999998, - 0.02288046308, - 0.02368652508, - 0.02211418708, - 0.02265551308, - 0.02187778308, - 0.02191395108, - 0.02182523808, - 0.02185612208, - 0.02109890308, - 0.02294132008, - 0.02191512608, - 0.02264461208, - 0.02227651108, - 0.02307147508, - 0.02227169708, - 0.02177434208 - ], - "memory_usage_byte": [ - 3014656, - 3014656, - 3014656, - 3014656, - 3014656, - 3014656, - 3014656, - 3014656, - 3014656, - 3014656, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3141632, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3268608, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680 - ], - "exit_codes": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - }, - { - "command": "sleep 0.021", - "mean": 0.022889189941111117, - "stddev": 0.0007161191938371117, - "median": 0.02280623708, - "user": 0.0009166992592592593, - "system": 0.0016941181481481477, - "min": 0.02132554808, - "max": 0.02453766808, - "times": [ - 0.02311599608, - 0.02274468508, - 0.02193879008, - 0.02158843608, - 0.02329398008, - 0.02379494508, - 0.02260801308, - 0.02439507908, - 0.02448522508, - 0.02403379508, - 0.02298143008, - 0.02263027308, - 0.02229235308, - 0.02335063508, - 0.02377098008, - 0.02269184108, - 0.023631199079999998, - 0.02338021508, - 0.02198521708, - 0.02251586208, - 0.022295963079999998, - 0.02226397608, - 0.02453766808, - 0.02184453408, - 0.02289659908, - 0.02382663208, - 0.02347397108, - 0.02225926308, - 0.02207640608, - 0.02243237108, - 0.02278192608, - 0.02270514808, - 0.02245069008, - 0.023018867079999998, - 0.02399866208, - 0.02236840708, - 0.02366382208, - 0.02294188908, - 0.02155127708, - 0.02294999808, - 0.02132554808, - 0.02242025908, - 0.02202766108, - 0.02182175108, - 0.02272186608, - 0.02211805308, - 0.02319764908, - 0.022308045079999998, - 0.02345400908, - 0.022437877079999998, - 0.02273417808, - 0.02217370908, - 0.02254318408, - 0.023269922079999998, - 0.02384951108, - 0.02419476108, - 0.02439866908, - 0.02354840508, - 0.02304219108, - 0.02354960608, - 0.02382648708, - 0.02345751208, - 0.02367913708, - 0.02253067208, - 0.02215132608, - 0.022603942079999998, - 0.02284062808, - 0.02252907808, - 0.02220393508, - 0.023291509079999998, - 0.02399456908, - 0.02407123208, - 0.02279175108, - 0.02300624708, - 0.02309500408, - 0.023036532079999998, - 0.02303833108, - 0.02316846908, - 0.02228349608, - 0.02247140608, - 0.022482600079999998, - 0.02370720808, - 0.02220123708, - 0.02230588608, - 0.02333678708, - 0.02153336008, - 0.02203071908, - 0.02279195108, - 0.02353659108, - 0.02267460708, - 0.022536274079999998, - 0.022769262079999998, - 0.02314857808, - 0.02194885908, - 0.02355038408, - 0.02320035308, - 0.02307451408, - 0.02379926408, - 0.02330480208, - 0.02257055708, - 0.02330320308, - 0.02303003208, - 0.02327859908, - 0.02171311608, - 0.02282052308, - 0.02170123708, - 0.02254831308, - 0.02235855408 - ], - "memory_usage_byte": [ - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680 - ], - "exit_codes": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - }, - { - "command": "sleep 0.022", - "mean": 0.02415569324504855, - "stddev": 0.0009830972994273135, - "median": 0.02409406108, - "user": 0.001165289514563107, - "system": 0.001767603883495146, - "min": 0.02243173808, - "max": 0.02755932908, - "times": [ - 0.02456728108, - 0.02650439708, - 0.02480475408, - 0.02452974808, - 0.02300978308, - 0.02521451608, - 0.02543841408, - 0.02538411108, - 0.02475773908, - 0.02403843308, - 0.02426362708, - 0.02326921708, - 0.02447185308, - 0.02361749008, - 0.02410661008, - 0.02371481508, - 0.02327300908, - 0.02430165908, - 0.02328269108, - 0.02315262608, - 0.02380195808, - 0.02283639508, - 0.02491355808, - 0.02401717008, - 0.02556049408, - 0.02350359508, - 0.02400529208, - 0.02533555808, - 0.02467923308, - 0.02478442308, - 0.02422068708, - 0.02352175108, - 0.02481882108, - 0.02456148108, - 0.02314905108, - 0.024188183079999998, - 0.02483985908, - 0.02289141308, - 0.02364977308, - 0.02354907008, - 0.02379135508, - 0.026812933079999997, - 0.023360627079999998, - 0.02331436308, - 0.02504176308, - 0.02358805508, - 0.02409406108, - 0.02350689508, - 0.02303628508, - 0.02430972408, - 0.02516170908, - 0.02352843108, - 0.02274564308, - 0.02345165808, - 0.02429327308, - 0.02252948108, - 0.02445868508, - 0.02755932908, - 0.02522621808, - 0.02491753008, - 0.022858510079999998, - 0.02401968108, - 0.02409596908, - 0.02390450108, - 0.02373108808, - 0.027211489079999998, - 0.02537487108, - 0.02319182608, - 0.02390569508, - 0.02490164708, - 0.02384732708, - 0.02243173808, - 0.02367003008, - 0.02494288308, - 0.02436298308, - 0.02390639308, - 0.02423030808, - 0.02430082908, - 0.02320845908, - 0.02421546708, - 0.02530823508, - 0.02368935308, - 0.02306283708, - 0.023536658079999998, - 0.02359881208, - 0.02438320308, - 0.02477724008, - 0.02362231908, - 0.02419465008, - 0.02596891608, - 0.02307578608, - 0.02459456508, - 0.02384055408, - 0.02421387408, - 0.02510733208, - 0.02473580508, - 0.02243970708, - 0.02253156008, - 0.02550018108, - 0.02440877608, - 0.02281331608, - 0.02354148408, - 0.02352098308 - ], - "memory_usage_byte": [ - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680, - 3399680 - ], - "exit_codes": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - } - ] -} From b3d01ec026f66567cbe77f421768d3704bc28706 Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Fri, 10 Apr 2026 13:06:16 +0700 Subject: [PATCH 277/374] Update Docker.yml --- .github/workflows/Docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index 7b85ca2ae00c8..5a2330e7d5d62 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -4,7 +4,7 @@ on: push: tags: ["*"] branches: - - "master" + - "main" pull_request: branches: ["**"] From dcaffd59af1bb4e4815abc28f417fe60bec41f08 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:25:45 +0700 Subject: [PATCH 278/374] Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/evm/evm/src/executors/invariant/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index db992516850a1..5f54436da2ce7 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -486,7 +486,7 @@ impl<'a> InvariantExecutor<'a> { is_last_call } else { self.config.check_interval == 1 - || (current_run.depth + 1).is_multiple_of(self.config.check_interval) + || (current_run.depth + 1) % self.config.check_interval as usize == 0 || is_last_call }; From d045cf9af29eb540c89b1306f29b45a26623979e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:07:07 +0700 Subject: [PATCH 279/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 07413fef5495c..fe448a5462bab 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -118,35 +118,37 @@ impl ScriptTester { fn copy_testdata(root: &Path) -> Result<()> { let testdata = Self::testdata_path(); let from_dir = testdata.join("utils"); + let from_dir_canon = from_dir.canonicalize()?; let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); - // Only operate on regular files to avoid following symlinks or directories - let metadata = fs::symlink_metadata(&file)?; + + // Resolve each entry and ensure it stays within the trusted source directory. + let canonical_file = match file.canonicalize() { + Ok(path) if path.starts_with(&from_dir_canon) => path, + _ => continue, + }; + + // Only operate on regular files to avoid following symlinks or directories. + let metadata = fs::symlink_metadata(&canonical_file)?; let ftype = metadata.file_type(); if !ftype.is_file() { continue; } + let name = match file.file_name() { Some(name) => name, None => continue, }; - // Validate file name to avoid path traversal and absolute paths + // Validate file name to avoid path traversal and absolute paths. let name_str = name.to_string_lossy(); if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { - // Skip invalid (potentially dangerous) file names + // Skip invalid (potentially dangerous) file names. continue; } - // Verify canonicalized file is in from_dir to avoid symlink traversal - if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&from_dir) { - continue; - } - } else { - continue; - } - fs::copy(&file, to_dir.join(name))?; + + fs::copy(&canonical_file, to_dir.join(name))?; } Ok(()) } From 3d1f0e61a4ca5ee86fedb6425137557e71adfe65 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:17:05 +0700 Subject: [PATCH 280/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index fe448a5462bab..6c7d50ce1d0e4 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -119,6 +119,7 @@ impl ScriptTester { let testdata = Self::testdata_path(); let from_dir = testdata.join("utils"); let from_dir_canon = from_dir.canonicalize()?; + let from_dir_canon = from_dir.canonicalize()?; let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { @@ -138,15 +139,16 @@ impl ScriptTester { } let name = match file.file_name() { - Some(name) => name, - None => continue, + // Verify canonicalized file is in canonicalized from_dir to avoid traversal + let canonical_file = match file.canonicalize() { + Ok(path) => path, + Err(_) => continue, }; - // Validate file name to avoid path traversal and absolute paths. - let name_str = name.to_string_lossy(); - if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + if !canonical_file.starts_with(&from_dir_canon) { // Skip invalid (potentially dangerous) file names. continue; - } + + fs::copy(&canonical_file, to_dir.join(name))?; fs::copy(&canonical_file, to_dir.join(name))?; } From 722b9b888c07d713c7d3fc91aeeba17af5e45b77 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:18:19 +0700 Subject: [PATCH 281/374] Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } From dc8de27bd4e366fb8844c00bc51e34831082cdd9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:19:34 +0700 Subject: [PATCH 282/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index f079d0f4fbf3d..06b2c29945417 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From 570d96224ffc0f11c2f45901dc1ce9d97ed17c66 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:56:32 +0700 Subject: [PATCH 283/374] Wagmi (e604566) (#413) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol * Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update docker.yml * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> … * feat(anvil): add Tempo gas estimation and tx type tests (#14197) * ci: add missing ARBITRUM_RPC and ETH_SEPOLIA_RPC secrets to flaky/isolate workflows (#14233) * refactor(cli): introduce `RpcCommonOpts` (#14224) * ci: remove unused HTTP_ARCHIVE_URLS and WS_ARCHIVE_URLS secrets (#14234) * feat(invariant): show best value realtime in optimization mode (#14226) * feat(evm): implement `FoundryEvmFactory` for `OpEvmFactory` (#14228) * clippy: warn on `if_then_some_else_none` (autofix) (#14235) clippy: warn on if_then_some_else_none Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `explicit_into_iter_loop` (autofix) (#14236) clippy: warn on explicit_into_iter_loop Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(clippy): remove `literal_string_with_formatting_args` allow, no longer required (#14238) chore: remove literal_string_with_formatting_args allow The upstream clippy bug (rust-lang/rust-clippy#13885) was fixed in rust-lang/rust-clippy#13953 (merged Feb 2025). The workaround is no longer needed. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `single_char_pattern` (autofix) (#14237) clippy: warn on single_char_pattern Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(script): Tempo direct signer + gas calc logic (#14242) * clippy: warn on `unnested_or_patterns` (autofix) (#14239) clippy: warn on unnested_or_patterns Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `enum_glob_use` (clippy) (#14240) clippy: warn on enum_glob_use Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `from_iter_instead_of_collect` (autofix) (#14241) clippy: warn on from_iter_instead_of_collect Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `if_not_else` (autofix) (#14092) * clippy: warn on if_not_else Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor: use contains_key + remove instead of if-let remove Simpler branch flip that preserves the original structure. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: resolve new if_not_else and redundant_else findings after merge Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: flip if_not_else in keychain authorize Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: use multi-line format for blob_hashes if-else Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: rustfmt prefers single-line if-else here Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(cli): restore short flag for `--rpc-url` (#14246) * fix(cli): restore short flag for `--rpc-url` * fix: requirements * fix: short flag collision * fix: restore long * fix: help message * fix(cast): `batch-send` handling by clearing `to`/`value` fields (#14250) * chore(cast): remove redundant batch-mktx clearing (#14252) * fix(anvil): use <= for pending pool replacement underprice check (#14254) * fix(invariant): preserve delay semantics during shrinking (#14218) * fix(invariant): preserve delay semantics during shrinking * fix tests --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): extract `transaction_at_block_index` in fork (#14271) * chore(deps): weekly `cargo update` (#14270) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * chore(deny): ignore RUSTSEC-2026-0097 (#14272) * Update flake.lock (#14269) Co-authored-by: github-actions[bot] * Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Addresses scfuzzbench/scfuzzbench#112. (#14266) * fix(forge): `script`/`test` Tempo selection when used w/ Anvil (#14258) * fix(anvil): parse Tempo expiring nonce fields from hex strings (#14259) * fix(ci): fix flaky_testdata vyper regression and p256 snapshot (#14278) * chore(evm): reorganize `foundry-evm-core::evm` mod (#14277) * refactor(anvil): extract `build_block_info` helper (#14251) refactor(anvil): extract build_block_info helper * feat(evm): auto-detect Tempo network on Anvil (#14279) * feat(evm): auto-detect Tempo network on Anvil * test(evm): add integration tests for network auto-selection via infer_network_from_fork Amp-Thread-ID: https://ampcode.com/threads/T-019d85e2-2767-70a1-a1bd-659d72a156f4 Co-authored-by: Amp --------- Co-authored-by: Amp * clippy: warn on `derive_partial_eq_without_eq` (autofix) (#14243) clippy: warn on derive_partial_eq_without_eq Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `flat_map_option` (autofix) (#14284) clippy: warn on flat_map_option Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): wildcard P256VERIFY gas in osaka snapshot (#14287) The P256VERIFY precompile gas cost was reduced from 6900 to 3450, but the flaky_osaka_can_run_p256_precompile snapshot still had the old value hardcoded. Replace with [..] for consistency with the rest of the snapshot. Closes #14282 Closes #14283 Amp-Thread-ID: https://ampcode.com/threads/T-019d8681-ae7d-712f-b4e7-eb6f595f82e3 Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `equatable_if_let` (manual) (#14285) * clippy: warn on flat_map_option Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on equatable_if_let Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `large_stack_frames`(manual) (#14289) clippy: warn on large_stack_frames Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `needless_continue` (manual) (#14288) clippy: warn on needless_continue Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on 6 small lints (1 finding each, fixed) (manual) (#14286) clippy: warn on trailing_empty_array, trait_duplication_in_bounds, trivial_regex, tuple_array_conversions, unnecessary_struct_initialization, unused_peekable Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * evm: only classify skip reverts from cheatcode address (#14264) * evm: require cheatcode origin for skip * evm/core: stop decoding magic skip in maybe_decode * evm/fuzz: guard skip decode by cheatcode reverter --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: warn on `unnecessary_self_imports` (manual) (#14293) clippy: warn on unnecessary_self_imports Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): auto-detect seconds vs milliseconds in evm_setTime (#14292) * fix(anvil): convert evm_setTime input from millis to seconds before use evm_setTime accepts a JavaScript-style millisecond timestamp for Ganache compatibility, but the offset was computed by subtracting current_call_timestamp (seconds) from the raw millisecond input. Convert the input to seconds first, then compute the offset correctly. Co-Authored-By: cui <1579517+cuiweixie@users.noreply.github.com> Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: move millis-to-seconds conversion to RPC dispatch layer The internal evm_set_time API accepts seconds (matching internal tests), while the JSON-RPC evm_setTime method accepts milliseconds for Ganache compatibility. Move the Duration::from_millis conversion to the RPC handler so both code paths are correct. Co-Authored-By: cui <1579517+cuiweixie@users.noreply.github.com> Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: auto-detect seconds vs milliseconds in evm_setTime Timestamps above 1e12 are treated as milliseconds (Ganache compat) and converted to seconds; values at or below that threshold are treated as seconds directly. This avoids silently downcasting a seconds timestamp to a nonsensical value. Co-Authored-By: cui <1579517+cuiweixie@users.noreply.github.com> Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * style: fix rustfmt Co-Authored-By: cui <1579517+cuiweixie@users.noreply.github.com> Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: cui <1579517+cuiweixie@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): add 7 day dependency cooldown (#14294) Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: enable `string_lit_as_bytes` (manual) (#14296) clippy: enable `string_lit_as_bytes` Amp-Thread-ID: https://ampcode.com/threads/T-019d8707-07ff-77cf-b7f2-5dc0f64200ec Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: enable `option_as_ref_cloned` (manual) (#14295) clippy: enable `option_as_ref_cloned` Amp-Thread-ID: https://ampcode.com/threads/T-019d8707-07ff-77cf-b7f2-5dc0f64200ec Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): guard deposit tx parsing in Tempo mode (#14261) * fix(anvil): guard deposit tx parsing in Tempo mode * use the ensure_op_deposits_active check * clippy: enable `missing_const_for_fn` (autofix) (#14297) * clippy: enable `missing_const_for_fn` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8707-07ff-77cf-b7f2-5dc0f64200ec * fix: revert const on feature-gated non-const functions Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8707-07ff-77cf-b7f2-5dc0f64200ec --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * clippy: enable `useless_let_if_seq` (manual) (#14300) clippy: enable `useless_let_if_seq` Amp-Thread-ID: https://ampcode.com/threads/T-019d8707-07ff-77cf-b7f2-5dc0f64200ec Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: cui Co-authored-by: Karl Yu <43113774+0xKarl98@users.noreply.github.com> Co-authored-by: Arsh Co-authored-by: cui <1579517+cuiweixie@users.noreply.github.com> Co-authored-by: googleworkspace-bot --- .circleci/cargo.yml | 32 + .circleci/ci-web3-gamefi.yml | 26 + .circleci/ci.yml | 31 + .circleci/ci_cargo.yml | 37 + .circleci/ci_v1.yml | 31 + .circleci/config.yml | 32 + .circleci/dev_stage.yml | 70 + .circleci/web3_defi_gamefi.yml | 26 + .codesandbox/tasks.json | 7 + .deps/remix-tests/remix_accounts.sol | 39 + .deps/remix-tests/remix_tests.sol | 225 ++++ .github/ISSUE_TEMPLATE/bug_report.md | 39 + .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/dependabot.yml | 2 + .github/workflows/Docker.yml | 62 + .github/workflows/apisec-scan.yml | 29 + .github/workflows/codeql.yml | 92 ++ .github/workflows/deploy.yml | 27 + .github/workflows/docker.yml | 62 + .github/workflows/google.yml | 117 ++ .github/workflows/npm.yml | 37 + .github/workflows/snyk-container.yml | 55 + .github/workflows/test-flaky.yml | 9 +- .github/workflows/test-isolate.yml | 9 +- .github/workflows/test.yml | 2 - .gitmodules | 6 + Cargo.lock | 191 +-- Cargo.toml | 25 +- benches/src/lib.rs | 22 +- counter/.github/workflows/test.yml | 43 + counter/.gitignore | 14 + counter/README.md | 66 + counter/foundry.toml | 6 + counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 + counter/src/Counter.sol | 14 + counter/test/Counter.t.sol | 24 + crates/anvil/core/src/eth/mod.rs | 2 +- crates/anvil/core/src/eth/transaction/mod.rs | 10 +- crates/anvil/rpc/src/error.rs | 2 +- crates/anvil/rpc/src/request.rs | 4 +- crates/anvil/rpc/src/response.rs | 2 +- crates/anvil/server/src/config.rs | 2 +- crates/anvil/server/src/ipc.rs | 2 +- crates/anvil/src/config.rs | 73 +- crates/anvil/src/eth/api.rs | 20 +- crates/anvil/src/eth/backend/cheats.rs | 2 +- crates/anvil/src/eth/backend/db.rs | 4 +- crates/anvil/src/eth/backend/executor.rs | 8 +- crates/anvil/src/eth/backend/fork.rs | 31 +- crates/anvil/src/eth/backend/info.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 282 ++-- crates/anvil/src/eth/backend/mem/storage.rs | 23 +- crates/anvil/src/eth/fees.rs | 20 +- crates/anvil/src/eth/pool/mod.rs | 10 +- crates/anvil/src/eth/pool/transactions.rs | 16 +- crates/anvil/src/filter.rs | 2 +- crates/anvil/src/lib.rs | 18 +- crates/anvil/src/logging.rs | 2 +- crates/anvil/src/pubsub.rs | 2 +- crates/anvil/src/server/rpc_handlers.rs | 4 +- crates/anvil/src/tasks/block_listener.rs | 2 +- crates/anvil/src/tasks/mod.rs | 2 +- crates/anvil/tests/it/tempo.rs | 485 +++++++ crates/cast/src/args.rs | 7 +- crates/cast/src/base.rs | 10 +- crates/cast/src/cmd/batch_mktx.rs | 6 +- crates/cast/src/cmd/batch_send.rs | 8 +- crates/cast/src/cmd/create2.rs | 1 + crates/cast/src/cmd/erc20.rs | 4 +- crates/cast/src/cmd/interface.rs | 2 +- crates/cast/src/cmd/keychain.rs | 40 +- crates/cast/src/cmd/run.rs | 153 +-- crates/cast/src/cmd/storage.rs | 2 +- crates/cast/src/cmd/tip20.rs | 2 +- crates/cast/src/cmd/wallet/mod.rs | 2 +- crates/cast/src/lib.rs | 14 +- crates/cast/src/tx.rs | 9 +- crates/cast/tests/cli/main.rs | 48 +- crates/cheatcodes/src/error.rs | 4 +- crates/cheatcodes/src/evm/prank.rs | 4 +- crates/cheatcodes/src/fs.rs | 2 +- crates/cheatcodes/src/inspector.rs | 109 +- crates/cheatcodes/src/inspector/analysis.rs | 4 +- crates/cheatcodes/src/json.rs | 2 +- crates/cheatcodes/src/test/assert.rs | 14 +- crates/cheatcodes/src/test/expect.rs | 8 +- crates/cheatcodes/src/test/revert_handlers.rs | 8 +- crates/cheatcodes/src/utils.rs | 5 +- crates/chisel/src/dispatcher.rs | 6 +- crates/chisel/src/executor.rs | 30 +- crates/chisel/src/runner.rs | 2 +- crates/chisel/src/solidity_helper.rs | 9 +- crates/chisel/tests/it/repl/session.rs | 2 +- crates/cli-markdown/src/lib.rs | 10 +- crates/cli/Cargo.toml | 1 + crates/cli/src/opts/evm.rs | 80 +- crates/cli/src/opts/mod.rs | 2 + crates/cli/src/opts/rpc.rs | 58 +- crates/cli/src/opts/rpc_common.rs | 114 ++ crates/cli/src/opts/tempo.rs | 2 +- crates/cli/src/utils/cmd.rs | 7 +- crates/cli/src/utils/mod.rs | 18 +- crates/cli/src/utils/suggestions.rs | 3 +- crates/common/fmt/src/console.rs | 24 +- crates/common/fmt/src/dynamic.rs | 4 +- crates/common/fmt/src/ui.rs | 6 +- crates/common/src/comments/comment.rs | 14 +- crates/common/src/comments/inline_config.rs | 4 +- crates/common/src/comments/mod.rs | 10 +- crates/common/src/compile.rs | 18 +- crates/common/src/contracts.rs | 8 +- crates/common/src/io/shell.rs | 24 +- crates/common/src/io/stdin.rs | 4 +- crates/common/src/provider/curl_transport.rs | 2 +- crates/common/src/provider/mod.rs | 25 +- .../common/src/provider/runtime_transport.rs | 8 +- crates/common/src/retry.rs | 4 +- crates/common/src/selectors.rs | 6 +- crates/common/src/slot_identifier.rs | 4 +- crates/common/src/traits.rs | 2 +- crates/common/src/transactions/broadcast.rs | 6 +- crates/common/src/transactions/builder.rs | 15 + crates/config/src/error.rs | 6 +- crates/config/src/etherscan.rs | 4 +- crates/config/src/extend.rs | 6 +- crates/config/src/filter.rs | 8 +- crates/config/src/fix.rs | 4 +- crates/config/src/fmt.rs | 18 +- crates/config/src/fs_permissions.rs | 10 +- crates/config/src/fuzz.rs | 4 +- crates/config/src/inline/mod.rs | 6 +- crates/config/src/inline/natspec.rs | 13 +- crates/config/src/invariant.rs | 5 + crates/config/src/lib.rs | 32 +- crates/config/src/lint.rs | 4 +- crates/config/src/providers/ext.rs | 2 +- crates/config/src/providers/remappings.rs | 13 +- crates/config/src/providers/warnings.rs | 12 +- crates/debugger/src/debugger.rs | 2 +- crates/debugger/src/node.rs | 2 +- crates/debugger/src/tui/draw.rs | 6 +- crates/debugger/src/tui/mod.rs | 2 +- crates/doc/src/builder.rs | 4 +- crates/doc/src/document.rs | 10 +- crates/doc/src/parser/comment.rs | 28 +- .../doc/src/preprocessor/infer_hyperlinks.rs | 12 +- crates/doc/src/solang_ext/ast_eq.rs | 6 +- crates/doc/src/writer/as_doc.rs | 39 +- crates/doc/src/writer/buf_writer.rs | 22 +- crates/evm/core/Cargo.toml | 2 + crates/evm/core/src/backend/cow.rs | 2 +- crates/evm/core/src/backend/mod.rs | 14 +- crates/evm/core/src/backend/snapshot.rs | 4 +- crates/evm/core/src/buffer.rs | 21 +- crates/evm/core/src/bytecode.rs | 8 +- crates/evm/core/src/decode.rs | 13 +- crates/evm/core/src/evm.rs | 1170 ----------------- crates/evm/core/src/evm/eth.rs | 358 +++++ crates/evm/core/src/evm/mod.rs | 301 +++++ crates/evm/core/src/evm/op.rs | 424 ++++++ crates/evm/core/src/evm/tempo.rs | 526 ++++++++ crates/evm/core/src/fork/database.rs | 8 +- crates/evm/core/src/opts.rs | 79 +- crates/evm/core/src/utils.rs | 4 +- crates/evm/coverage/src/analysis.rs | 6 +- crates/evm/coverage/src/lib.rs | 8 +- crates/evm/evm/src/executors/builder.rs | 8 +- crates/evm/evm/src/executors/corpus.rs | 10 +- crates/evm/evm/src/executors/fuzz/mod.rs | 20 +- crates/evm/evm/src/executors/invariant/mod.rs | 178 ++- .../evm/evm/src/executors/invariant/result.rs | 10 +- .../evm/evm/src/executors/invariant/shrink.rs | 175 ++- crates/evm/evm/src/executors/mod.rs | 74 +- crates/evm/evm/src/executors/trace.rs | 79 +- crates/evm/evm/src/inspectors/chisel_state.rs | 2 +- .../evm/src/inspectors/revert_diagnostic.rs | 6 +- crates/evm/evm/src/inspectors/stack.rs | 30 +- crates/evm/fuzz/src/invariant/mod.rs | 10 +- crates/evm/fuzz/src/strategies/int.rs | 2 +- crates/evm/fuzz/src/strategies/invariants.rs | 6 +- crates/evm/fuzz/src/strategies/state.rs | 4 +- crates/evm/fuzz/src/strategies/uint.rs | 2 +- crates/evm/hardforks/src/lib.rs | 6 +- crates/evm/networks/src/lib.rs | 8 +- crates/evm/traces/src/backtrace/mod.rs | 6 +- crates/evm/traces/src/debug/mod.rs | 4 +- crates/evm/traces/src/decoder/mod.rs | 26 +- crates/evm/traces/src/folded_stack_trace.rs | 2 +- crates/evm/traces/src/identifier/external.rs | 2 +- crates/evm/traces/src/identifier/local.rs | 4 +- crates/evm/traces/src/identifier/mod.rs | 2 +- .../evm/traces/src/identifier/signatures.rs | 2 +- crates/evm/traces/src/lib.rs | 8 +- crates/fmt/src/lib.rs | 10 +- crates/fmt/src/pp/convenience.rs | 6 +- crates/fmt/src/pp/mod.rs | 2 +- crates/fmt/src/pp/ring.rs | 2 +- crates/fmt/src/state/common.rs | 76 +- crates/fmt/src/state/mod.rs | 36 +- crates/fmt/src/state/sol.rs | 68 +- crates/fmt/src/state/yul.rs | 6 +- crates/forge/Cargo.toml | 16 +- crates/forge/src/cmd/bind.rs | 18 +- crates/forge/src/cmd/bind_json.rs | 12 +- crates/forge/src/cmd/build.rs | 2 +- crates/forge/src/cmd/compiler.rs | 12 +- crates/forge/src/cmd/coverage.rs | 5 +- crates/forge/src/cmd/create.rs | 20 +- crates/forge/src/cmd/doc/mod.rs | 2 +- crates/forge/src/cmd/doc/server.rs | 4 +- crates/forge/src/cmd/eip712.rs | 2 +- crates/forge/src/cmd/fmt.rs | 2 +- crates/forge/src/cmd/inspect.rs | 11 +- crates/forge/src/cmd/install.rs | 2 +- crates/forge/src/cmd/selectors.rs | 4 +- crates/forge/src/cmd/snapshot.rs | 8 +- crates/forge/src/cmd/test/filter.rs | 10 +- crates/forge/src/cmd/test/mod.rs | 24 +- crates/forge/src/cmd/test/summary.rs | 2 +- crates/forge/src/coverage.rs | 4 +- crates/forge/src/lockfile.rs | 8 +- crates/forge/src/multi_runner.rs | 24 +- crates/forge/src/result.rs | 18 +- crates/forge/src/runner.rs | 15 +- crates/forge/tests/cli/lint.rs | 2 +- crates/forge/tests/cli/script.rs | 7 +- .../tests/cli/test_cmd/invariant/common.rs | 117 +- crates/forge/tests/cli/test_optimizer.rs | 1 - crates/lint/src/linter.rs | 129 ++ crates/lint/src/linter/mod.rs | 14 +- .../sol/codesize/unwrapped_modifier_logic.rs | 2 +- crates/lint/src/sol/gas/custom_errors.rs | 2 +- crates/lint/src/sol/gas/keccak.rs | 98 +- crates/lint/src/sol/high/incorrect_shift.rs | 2 +- crates/lint/src/sol/high/unchecked_calls.rs | 2 +- crates/lint/src/sol/info/interface_naming.rs | 2 +- .../lint/src/sol/info/multi_contract_file.rs | 2 +- .../lint/src/sol/info/screaming_snake_case.rs | 4 +- crates/lint/src/sol/med/unsafe_typecast.rs | 5 +- crates/lint/src/sol/mod.rs | 12 +- crates/macros/src/cheatcodes.rs | 2 +- crates/macros/src/console_fmt.rs | 4 +- crates/primitives/src/transaction/envelope.rs | 4 +- crates/script-sequence/src/reader.rs | 2 +- crates/script-sequence/src/sequence.rs | 85 +- crates/script-sequence/src/transaction.rs | 10 +- crates/script/Cargo.toml | 1 + crates/script/src/broadcast.rs | 16 +- crates/script/src/build.rs | 38 +- crates/script/src/execute.rs | 15 +- crates/script/src/lib.rs | 15 +- crates/script/src/receipts.rs | 2 +- crates/script/src/runner.rs | 18 +- crates/script/src/simulate.rs | 68 +- crates/script/src/verify.rs | 12 +- crates/sol-macro-gen/src/sol_macro_gen.rs | 16 +- crates/test-utils/src/ext.rs | 4 +- crates/test-utils/src/prj.rs | 8 +- crates/test-utils/src/script.rs | 40 +- crates/test-utils/src/ui_runner.rs | 2 +- crates/test-utils/src/util.rs | 2 +- crates/verify/src/etherscan/flatten.rs | 6 +- crates/verify/src/etherscan/mod.rs | 6 +- crates/verify/src/etherscan/standard_json.rs | 11 +- crates/verify/src/provider.rs | 4 +- crates/verify/src/retry.rs | 2 +- crates/verify/src/utils.rs | 18 +- crates/wallets/src/error.rs | 8 +- crates/wallets/src/utils.rs | 6 +- crates/wallets/src/wallet_browser/server.rs | 6 +- crates/wallets/src/wallet_browser/signer.rs | 2 +- crates/wallets/src/wallet_browser/state.rs | 2 +- crates/wallets/src/wallet_browser/types.rs | 6 +- crates/wallets/src/wallet_multi/mod.rs | 6 +- deny.toml | 2 + flake.lock | 18 +- npm/package.json | 2 +- npm/scripts/stage-from-artifact.mjs | 28 +- npm/src/const.mjs | 30 +- 282 files changed, 5929 insertions(+), 2936 deletions(-) create mode 100644 .circleci/cargo.yml create mode 100644 .circleci/ci-web3-gamefi.yml create mode 100644 .circleci/ci.yml create mode 100644 .circleci/ci_cargo.yml create mode 100644 .circleci/ci_v1.yml create mode 100644 .circleci/config.yml create mode 100644 .circleci/dev_stage.yml create mode 100644 .circleci/web3_defi_gamefi.yml create mode 100644 .codesandbox/tasks.json create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/Docker.yml create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/google.yml create mode 100644 .github/workflows/snyk-container.yml create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol create mode 100644 crates/cli/src/opts/rpc_common.rs delete mode 100644 crates/evm/core/src/evm.rs create mode 100644 crates/evm/core/src/evm/eth.rs create mode 100644 crates/evm/core/src/evm/mod.rs create mode 100644 crates/evm/core/src/evm/op.rs create mode 100644 crates/evm/core/src/evm/tempo.rs create mode 100644 crates/lint/src/linter.rs diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..32b65e6a23cc5 --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,32 @@ +version: 2.1 +# +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..82c6de5b42b73 --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..4168efef0971f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..5ba351727d22b --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,70 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..53a505774ac88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 54106f083501d..e32978eaded37 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,5 +4,7 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 7 ignore: - dependency-name: "softprops/action-gh-release" diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/Docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 766beae8cc8d7..96d83592a4cc0 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -233,9 +233,46 @@ jobs: PLATFORM='${{ matrix.os }}' ARCH='${{ matrix.arch }}' + # Basic validation of matrix-derived values to avoid path manipulation + case "$TOOL" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid TOOL value: $TOOL" >&2; exit 1 ;; + esac + case "$PLATFORM" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid PLATFORM value: $PLATFORM" >&2; exit 1 ;; + esac + case "$ARCH" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid ARCH value: $ARCH" >&2; exit 1 ;; + esac + PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" + echo "Preparing to publish package from: $PACKAGE_DIR" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 + exit 1 + fi + + # Resolve to an absolute path and ensure it stays within ./@foundry-rs + ABS_PACKAGE_DIR="$(realpath "$PACKAGE_DIR")" + ABS_EXPECTED_ROOT="$(realpath "./@foundry-rs")" + case "$ABS_PACKAGE_DIR" in + "$ABS_EXPECTED_ROOT"/*) ;; + *) + echo "ERROR: Resolved package directory is outside expected root:" >&2 + echo " ABS_PACKAGE_DIR=$ABS_PACKAGE_DIR" >&2 + echo " ABS_EXPECTED_ROOT=$ABS_EXPECTED_ROOT" >&2 + exit 1 + ;; + esac + ls -la "$PACKAGE_DIR" + # Minimal sanity check: require a package.json before publishing + if [[ ! -f "$PACKAGE_DIR/package.json" ]]; then + echo "ERROR: package.json not found in $PACKAGE_DIR; refusing to publish." >&2 + exit 1 + fi + bun ./scripts/publish.mjs "$PACKAGE_DIR" echo "Published @foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index 00eb791f1c211..b1d0bc51e69a8 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -36,13 +36,20 @@ jobs: - uses: taiki-e/install-action@cf39a74df4a72510be4e5b63348d61067f11e64a # v2 with: tool: nextest + - uses: actions/setup-python@v6 + with: + python-version: "3.14" + - name: Install Vyper + # Also update vyper version in .devcontainer/Dockerfile.dev + run: pip --version && pip install vyper==0.4.3 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: Test flaky tests env: SVM_TARGET_PLATFORM: linux-amd64 - HTTP_ARCHIVE_URLS: ${{ secrets.HTTP_ARCHIVE_URLS }} ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} + ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} + ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} run: cargo nextest run --profile flaky --no-fail-fast # If any of the jobs fail, this will create a normal-priority issue to signal so. diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index 89bf27be4050e..baf21395303ff 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -40,13 +40,20 @@ jobs: - uses: taiki-e/install-action@cf39a74df4a72510be4e5b63348d61067f11e64a # v2 with: tool: nextest + - uses: actions/setup-python@v6 + with: + python-version: "3.14" + - name: Install Vyper + # Also update vyper version in .devcontainer/Dockerfile.dev + run: pip --version && pip install vyper==0.4.3 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 - name: Test flaky tests with isolation env: SVM_TARGET_PLATFORM: linux-amd64 - HTTP_ARCHIVE_URLS: ${{ secrets.HTTP_ARCHIVE_URLS }} ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} + ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} + ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} run: cargo nextest run --profile flaky --features=isolate-by-default --no-fail-fast # If nextest fails, create a high-priority issue for isolation failures. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e67498f679bf..2bf4ba9fc8cd2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -111,8 +111,6 @@ jobs: - name: Test env: SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} - HTTP_ARCHIVE_URLS: ${{ secrets.HTTP_ARCHIVE_URLS }} - WS_ARCHIVE_URLS: ${{ secrets.WS_ARCHIVE_URLS }} ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/Cargo.lock b/Cargo.lock index 03b5ad35d23ea..fce125e17c4ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,14 +468,14 @@ dependencies = [ "foldhash 0.2.0", "getrandom 0.4.2", "hashbrown 0.16.1", - "indexmap 2.13.1", + "indexmap 2.14.0", "itoa", "k256", "keccak-asm", "paste", "proptest", "proptest-derive", - "rand 0.9.2", + "rand 0.9.3", "rapidhash", "ruint", "rustc-hash", @@ -885,7 +885,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.13.1", + "indexmap 2.14.0", "proc-macro-error2", "proc-macro2", "quote", @@ -1056,9 +1056,9 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.12.14" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b669bf35e50f130e98212b486b0df78d93e285963344e58937692705e1a21a" +checksum = "92570a3f9c98e7e84df84b71d0965ac99b1871fcd75a3773a3bd1bad13f64cf7" dependencies = [ "anstyle", "memchr", @@ -1196,7 +1196,7 @@ dependencies = [ "clap", "clap_complete", "ctrlc", - "ethereum_ssz 0.10.1", + "ethereum_ssz 0.10.3", "eyre", "fdlimit", "flate2", @@ -1215,7 +1215,7 @@ dependencies = [ "op-revm", "parking_lot", "rand 0.8.5", - "rand 0.9.2", + "rand 0.9.3", "reqwest 0.13.2", "revm", "revm-inspectors", @@ -1252,7 +1252,7 @@ dependencies = [ "foundry-common", "foundry-evm", "foundry-primitives", - "rand 0.9.2", + "rand 0.9.3", "revm", "serde", "serde_json", @@ -2350,7 +2350,7 @@ dependencies = [ "boa_interner", "boa_macros", "boa_string", - "indexmap 2.13.1", + "indexmap 2.14.0", "num-bigint", "rustc-hash", ] @@ -2382,7 +2382,7 @@ dependencies = [ "futures-lite", "hashbrown 0.16.1", "icu_normalizer", - "indexmap 2.13.1", + "indexmap 2.14.0", "intrusive-collections", "itertools 0.14.0", "num-bigint", @@ -2391,7 +2391,7 @@ dependencies = [ "num_enum", "paste", "portable-atomic", - "rand 0.9.2", + "rand 0.9.3", "regress", "rustc-hash", "ryu-js", @@ -2428,7 +2428,7 @@ dependencies = [ "boa_gc", "boa_macros", "hashbrown 0.16.1", - "indexmap 2.13.1", + "indexmap 2.14.0", "once_cell", "phf 0.13.1", "rustc-hash", @@ -2711,7 +2711,7 @@ dependencies = [ "op-alloy-flz", "op-alloy-network", "rand 0.8.5", - "rand 0.9.2", + "rand 0.9.3", "rayon", "regex", "revm", @@ -2739,9 +2739,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.59" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "jobserver", @@ -2915,9 +2915,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb" +checksum = "406e68b4de5c59cfb8f750a7cbd4d31ae153788b8352167c1e5f4fc26e8c91e9" dependencies = [ "clap", ] @@ -4263,9 +4263,9 @@ dependencies = [ [[package]] name = "ethereum_ssz" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2128a84f7a3850d54ee343334e3392cca61f9f6aa9441eec481b9394b43c238b" +checksum = "368a4a4e4273b0135111fe9464e35465067766a8f664615b5a86338b73864407" dependencies = [ "alloy-primitives", "ethereum_serde_utils", @@ -4290,9 +4290,9 @@ dependencies = [ [[package]] name = "ethereum_ssz_derive" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd596f91cff004fc8d02be44c21c0f9b93140a04b66027ae052f5f8e05b48eba" +checksum = "f2cd82c68120c89361e1a457245cf212f7d9f541bffaffed530c8f2d54a160b2" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -4329,7 +4329,7 @@ checksum = "61e332670cb19161dee3f15ae86bfb5e4361bb1716d7c1995bd309b5360cb630" dependencies = [ "alloy-dyn-abi", "alloy-primitives", - "indexmap 2.13.1", + "indexmap 2.14.0", ] [[package]] @@ -4350,9 +4350,9 @@ checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" [[package]] name = "fastrand" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fastrlp" @@ -4571,7 +4571,7 @@ dependencies = [ "path-slash", "proptest", "quick-junit", - "rand 0.9.2", + "rand 0.9.3", "rayon", "regex", "reqwest 0.13.2", @@ -4680,6 +4680,7 @@ dependencies = [ "foundry-config", "foundry-debugger", "foundry-evm", + "foundry-evm-networks", "foundry-linking", "foundry-wallets", "futures", @@ -4851,7 +4852,7 @@ dependencies = [ "p256", "parking_lot", "proptest", - "rand 0.9.2", + "rand 0.9.3", "revm", "revm-inspectors", "semver 1.0.28", @@ -4903,6 +4904,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm", + "foundry-evm-networks", "foundry-wallets", "futures", "indicatif", @@ -5037,7 +5039,7 @@ dependencies = [ "fs_extra", "itertools 0.14.0", "path-slash", - "rand 0.9.2", + "rand 0.9.3", "rayon", "semver 1.0.28", "serde", @@ -5246,6 +5248,7 @@ dependencies = [ "alloy-rpc-types", "alloy-serde 2.0.0-rc.0", "alloy-sol-types", + "anvil", "auto_impl", "eyre", "foundry-cheatcodes-spec", @@ -5312,7 +5315,7 @@ dependencies = [ "itertools 0.14.0", "parking_lot", "proptest", - "rand 0.9.2", + "rand 0.9.3", "revm", "serde", "solar-compiler", @@ -5386,7 +5389,7 @@ dependencies = [ [[package]] name = "foundry-fork-db" version = "0.24.1" -source = "git+https://github.com/foundry-rs/foundry-fork-db?branch=main#767dcd5e6e773dde1dd10faef677bb9291cc4bf7" +source = "git+https://github.com/foundry-rs/foundry-fork-db?branch=main#a29e143c9c44cf09525fc453f40ec1414fa36b51" dependencies = [ "alloy-chains", "alloy-primitives", @@ -5481,7 +5484,7 @@ dependencies = [ "foundry-config", "idna_adapter", "parking_lot", - "rand 0.9.2", + "rand 0.9.3", "regex", "reqwest 0.13.2", "serde_json", @@ -5850,7 +5853,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.13.1", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -5919,6 +5922,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "heck" version = "0.5.0" @@ -6363,13 +6372,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -6687,9 +6696,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.94" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ "cfg-if", "futures-util", @@ -6877,9 +6886,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ "libc", ] @@ -7033,7 +7042,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a229930b29a9908560883e1f386eae25d8a971d259a80f49916a50627f04a42d" dependencies = [ "anyhow", - "indexmap 2.13.1", + "indexmap 2.14.0", "mdbook-core", "mdbook-html", "mdbook-markdown", @@ -7063,7 +7072,7 @@ dependencies = [ "handlebars", "hex", "html5ever", - "indexmap 2.13.1", + "indexmap 2.14.0", "mdbook-core", "mdbook-markdown", "mdbook-renderer", @@ -7732,8 +7741,8 @@ dependencies = [ "alloy-rpc-types-engine", "alloy-serde 2.0.0-rc.0", "derive_more", - "ethereum_ssz 0.10.1", - "ethereum_ssz_derive 0.10.1", + "ethereum_ssz 0.10.3", + "ethereum_ssz_derive 0.10.3", "op-alloy-consensus", "serde", "sha2 0.10.9", @@ -7969,7 +7978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.13.1", + "indexmap 2.14.0", ] [[package]] @@ -8273,7 +8282,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.10+spec-1.1.0", + "toml_edit 0.25.11+spec-1.1.0", ] [[package]] @@ -8314,7 +8323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" dependencies = [ "futures", - "indexmap 2.13.1", + "indexmap 2.14.0", "nix 0.31.2", "tokio", "tracing", @@ -8331,7 +8340,7 @@ dependencies = [ "bit-vec", "bitflags 2.11.0", "num-traits", - "rand 0.9.2", + "rand 0.9.3", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -8466,7 +8475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ee9342d671fae8d66b3ae9fd7a9714dfd089c04d2a8b1ec0436ef77aee15e5f" dependencies = [ "chrono", - "indexmap 2.13.1", + "indexmap 2.14.0", "newtype-uuid", "quick-xml 0.38.4", "strip-ansi-escapes", @@ -8522,7 +8531,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.2", + "rand 0.9.3", "ring", "rustc-hash", "rustls", @@ -8599,9 +8608,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -8662,7 +8671,7 @@ version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ - "rand 0.9.2", + "rand 0.9.3", "rustversion", ] @@ -9612,7 +9621,7 @@ dependencies = [ "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.2", + "rand 0.9.3", "rlp", "ruint-macro", "serde_core", @@ -9776,9 +9785,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4" dependencies = [ "aws-lc-rs", "ring", @@ -9969,7 +9978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.2", + "rand 0.9.3", "secp256k1-sys 0.11.0", ] @@ -10130,7 +10139,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.13.1", + "indexmap 2.14.0", "itoa", "memchr", "serde", @@ -10180,7 +10189,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.1", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -10507,7 +10516,7 @@ source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138d dependencies = [ "bumpalo", "index_vec", - "indexmap 2.13.1", + "indexmap 2.14.0", "parking_lot", "rayon", "rustc-hash", @@ -10519,7 +10528,7 @@ name = "solar-interface" version = "0.1.8" source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ - "annotate-snippets 0.12.14", + "annotate-snippets 0.12.15", "anstream 0.6.21", "anstyle", "derive_more", @@ -11221,9 +11230,9 @@ dependencies = [ [[package]] name = "thin-vec" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" +checksum = "da322882471314edc77fa5232c587bcb87c9df52bfd0d7d4826f8868ead61899" [[package]] name = "thiserror" @@ -11450,7 +11459,7 @@ version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.13.1", + "indexmap 2.14.0", "serde_core", "serde_spanned", "toml_datetime 0.7.5+spec-1.1.0", @@ -11483,7 +11492,7 @@ version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.13.1", + "indexmap 2.14.0", "serde_core", "serde_spanned", "toml_datetime 0.7.5+spec-1.1.0", @@ -11498,7 +11507,7 @@ version = "0.24.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01f2eadbbc6b377a847be05f60791ef1058d9f696ecb51d2c07fe911d8569d8e" dependencies = [ - "indexmap 2.13.1", + "indexmap 2.14.0", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", @@ -11507,11 +11516,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.10+spec-1.1.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.13.1", + "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "winnow 1.0.1", @@ -11588,7 +11597,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.13.1", + "indexmap 2.14.0", "pin-project-lite", "slab", "sync_wrapper", @@ -11785,7 +11794,7 @@ dependencies = [ "http 1.4.0", "httparse", "log", - "rand 0.9.2", + "rand 0.9.3", "rustls", "rustls-pki-types", "sha1", @@ -11795,9 +11804,9 @@ dependencies = [ [[package]] name = "turnkey_api_key_stamper" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4fc991208d20002cc9d05f21d5b6646078cc4809fb74c9dcc720c58ff16f060" +checksum = "34f72e05a07cb04163efff0c766521ebacbb268a851db83d419b7c56df90d046" dependencies = [ "base64 0.22.1", "hex", @@ -11811,9 +11820,9 @@ dependencies = [ [[package]] name = "turnkey_client" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0587a85462ebb2ceda8e01c38081fefb43d0819e652d59bbfc86ac3a1731f2" +checksum = "0c3b2cb46984c849dbd514bc9d5271620d3e52267ebd1cafd1c95d3fa3b4cf6d" dependencies = [ "mime", "prost 0.12.6", @@ -12233,9 +12242,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ "cfg-if", "once_cell", @@ -12246,9 +12255,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.67" +version = "0.4.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" +checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" dependencies = [ "js-sys", "wasm-bindgen", @@ -12256,9 +12265,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12266,9 +12275,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" dependencies = [ "bumpalo", "proc-macro2", @@ -12279,9 +12288,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" dependencies = [ "unicode-ident", ] @@ -12303,7 +12312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.1", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -12342,7 +12351,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap 2.13.1", + "indexmap 2.14.0", "semver 1.0.28", ] @@ -12419,9 +12428,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.94" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" dependencies = [ "js-sys", "wasm-bindgen", @@ -12936,7 +12945,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.1", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -12967,7 +12976,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap 2.13.1", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -12986,7 +12995,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.1", + "indexmap 2.14.0", "log", "semver 1.0.28", "serde", @@ -13203,7 +13212,7 @@ dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.13.1", + "indexmap 2.14.0", "memchr", "zopfli", ] diff --git a/Cargo.toml b/Cargo.toml index 73efb0b87a05a..0d328597b95c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,33 +48,56 @@ clear_with_drain = "warn" cloned_instead_of_copied = "warn" collection_is_never_read = "warn" dbg_macro = "warn" +derive_partial_eq_without_eq = "warn" empty_line_after_doc_comments = "warn" empty_line_after_outer_attr = "warn" +enum_glob_use = "warn" +equatable_if_let = "warn" +explicit_into_iter_loop = "warn" explicit_iter_loop = "warn" +flat_map_option = "warn" +from_iter_instead_of_collect = "warn" +if_then_some_else_none = "warn" implicit_clone = "warn" +if_not_else = "warn" imprecise_flops = "warn" iter_on_empty_collections = "warn" iter_with_drain = "warn" iter_without_into_iter = "warn" +large_stack_frames = "warn" manual_assert = "warn" manual_clamp = "warn" manual_string_new = "warn" +missing_const_for_fn = "warn" mutex_integer = "warn" naive_bytecount = "warn" needless_bitwise_bool = "warn" +needless_continue = "warn" nonstandard_macro_braces = "warn" +option_as_ref_cloned = "warn" path_buf_push_overwrite = "warn" read_zero_byte_vec = "warn" redundant_clone = "warn" +single_char_pattern = "warn" redundant_else = "warn" +string_lit_as_bytes = "warn" string_lit_chars_any = "warn" suboptimal_flops = "warn" suspicious_operation_groupings = "warn" +trailing_empty_array = "warn" +trait_duplication_in_bounds = "warn" transmute_undefined_repr = "warn" +trivial_regex = "warn" +tuple_array_conversions = "warn" uninhabited_references = "warn" +unnecessary_struct_initialization = "warn" uninlined_format_args = "warn" +unnecessary_self_imports = "warn" +unnested_or_patterns = "warn" +unused_peekable = "warn" unused_rounding = "warn" use_self = "warn" +useless_let_if_seq = "warn" while_float = "warn" zero_sized_map_values = "warn" @@ -92,8 +115,6 @@ significant_drop_tightening = "allow" too_long_first_doc_paragraph = "allow" # Specific allows. -# until is fixed -literal_string_with_formatting_args = "allow" result_large_err = "allow" [workspace.lints.rust] diff --git a/benches/src/lib.rs b/benches/src/lib.rs index b4b3de2c3ddf3..ab3bec614e3cd 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -130,10 +130,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } @@ -175,13 +185,13 @@ impl BenchmarkProject { .status() .wrap_err("Failed to run npm install")?; - if !status.success() { + if status.success() { + sh_println!(" ✅ npm install completed successfully"); + } else { sh_println!( " ⚠️ Warning: npm install failed with exit code: {:?}", status.code() ); - } else { - sh_println!(" ✅ npm install completed successfully"); } } Ok(()) diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index aee27d15c600e..1b5e231124a78 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -31,7 +31,7 @@ use self::serde_helpers::*; /// Wrapper type that ensures the type is named `params` #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] -pub struct Params { +pub struct Params { #[serde(default)] pub params: T, } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index f967dcd6ddd61..056d7a9602855 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -33,17 +33,17 @@ impl Typed2718 for MaybeImpersonatedTransaction { impl MaybeImpersonatedTransaction { /// Creates a new wrapper for the given transaction - pub fn new(transaction: T) -> Self { + pub const fn new(transaction: T) -> Self { Self { transaction, impersonated_sender: None } } /// Creates a new impersonated transaction wrapper using the given sender - pub fn impersonated(transaction: T, impersonated_sender: Address) -> Self { + pub const fn impersonated(transaction: T, impersonated_sender: Address) -> Self { Self { transaction, impersonated_sender: Some(impersonated_sender) } } /// Returns whether the transaction is impersonated - pub fn is_impersonated(&self) -> bool { + pub const fn is_impersonated(&self) -> bool { self.impersonated_sender.is_some() } @@ -137,11 +137,11 @@ pub struct PendingTransaction { } impl PendingTransaction { - pub fn hash(&self) -> &TxHash { + pub const fn hash(&self) -> &TxHash { &self.hash } - pub fn sender(&self) -> &Address { + pub const fn sender(&self) -> &Address { &self.sender } } diff --git a/crates/anvil/rpc/src/error.rs b/crates/anvil/rpc/src/error.rs index 2e1d90e5be997..2e5877d5f2b8b 100644 --- a/crates/anvil/rpc/src/error.rs +++ b/crates/anvil/rpc/src/error.rs @@ -94,7 +94,7 @@ pub enum ErrorCode { impl ErrorCode { /// Returns the error code as `i64` - pub fn code(&self) -> i64 { + pub const fn code(&self) -> i64 { match *self { Self::ParseError => -32700, Self::InvalidRequest => -32600, diff --git a/crates/anvil/rpc/src/request.rs b/crates/anvil/rpc/src/request.rs index 5cb8510b80f2b..eade8b25fe3cd 100644 --- a/crates/anvil/rpc/src/request.rs +++ b/crates/anvil/rpc/src/request.rs @@ -84,7 +84,7 @@ impl From for serde_json::Value { } } -fn no_params() -> RequestParams { +const fn no_params() -> RequestParams { RequestParams::None } @@ -113,7 +113,7 @@ impl fmt::Display for Id { } } -fn null_id() -> Id { +const fn null_id() -> Id { Id::Null } diff --git a/crates/anvil/rpc/src/response.rs b/crates/anvil/rpc/src/response.rs index ff47ac77e7fdf..b7f2d9f478092 100644 --- a/crates/anvil/rpc/src/response.rs +++ b/crates/anvil/rpc/src/response.rs @@ -49,7 +49,7 @@ impl ResponseResult { Self::Success(serde_json::to_value(&content).unwrap()) } - pub fn error(error: RpcError) -> Self { + pub const fn error(error: RpcError) -> Self { Self::Error(error) } } diff --git a/crates/anvil/server/src/config.rs b/crates/anvil/server/src/config.rs index dd15959b113a6..d2a2d4430131e 100644 --- a/crates/anvil/server/src/config.rs +++ b/crates/anvil/server/src/config.rs @@ -27,7 +27,7 @@ impl ServerConfig { } /// Whether to enable CORS. - pub fn set_cors(mut self, cors: bool) -> Self { + pub const fn set_cors(mut self, cors: bool) -> Self { self.no_cors = !cors; self } diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index 4a9133ee6dd58..757cdb05b2a2e 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -23,7 +23,7 @@ pub struct IpcEndpoint { impl IpcEndpoint { /// Creates a new endpoint with the given handler - pub fn new(handler: Handler, path: String) -> Self { + pub const fn new(handler: Handler, path: String) -> Self { Self { handler, path } } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 03f4fcd05178c..c9b63f08cb2f5 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -511,7 +511,7 @@ impl Default for NodeConfig { impl NodeConfig { /// Returns the memory limit of the node #[must_use] - pub fn with_memory_limit(mut self, mems_value: Option) -> Self { + pub const fn with_memory_limit(mut self, mems_value: Option) -> Self { self.memory_limit = mems_value; self } @@ -579,13 +579,13 @@ impl NodeConfig { /// Sets a custom code size limit #[must_use] - pub fn with_code_size_limit(mut self, code_size_limit: Option) -> Self { + pub const fn with_code_size_limit(mut self, code_size_limit: Option) -> Self { self.code_size_limit = code_size_limit; self } /// Disables code size limit #[must_use] - pub fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { + pub const fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { if disable_code_size_limit { self.code_size_limit = Some(usize::MAX); } @@ -636,7 +636,7 @@ impl NodeConfig { /// Sets the gas limit #[must_use] - pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { + pub const fn with_gas_limit(mut self, gas_limit: Option) -> Self { self.gas_limit = gas_limit; self } @@ -645,7 +645,7 @@ impl NodeConfig { /// /// If set to `true` block gas limit will not be enforced #[must_use] - pub fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self { + pub const fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self { self.disable_block_gas_limit = disable_block_gas_limit; self } @@ -654,14 +654,14 @@ impl NodeConfig { /// /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825) #[must_use] - pub fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self { + pub const fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self { self.enable_tx_gas_limit = enable_tx_gas_limit; self } /// Sets the gas price #[must_use] - pub fn with_gas_price(mut self, gas_price: Option) -> Self { + pub const fn with_gas_price(mut self, gas_price: Option) -> Self { self.gas_price = gas_price; self } @@ -685,7 +685,7 @@ impl NodeConfig { /// Sets the max number of transactions in a block #[must_use] - pub fn with_max_transactions(mut self, max_transactions: Option) -> Self { + pub const fn with_max_transactions(mut self, max_transactions: Option) -> Self { if let Some(max_transactions) = max_transactions { self.max_transactions = max_transactions; } @@ -704,14 +704,14 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub fn with_base_fee(mut self, base_fee: Option) -> Self { + pub const fn with_base_fee(mut self, base_fee: Option) -> Self { self.base_fee = base_fee; self } /// Disable the enforcement of a minimum suggested priority fee #[must_use] - pub fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { + pub const fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { self.disable_min_priority_fee = disable_min_priority_fee; self } @@ -757,7 +757,7 @@ impl NodeConfig { /// Sets the hardfork #[must_use] - pub fn with_hardfork(mut self, hardfork: Option) -> Self { + pub const fn with_hardfork(mut self, hardfork: Option) -> Self { self.hardfork = hardfork; self } @@ -811,21 +811,21 @@ impl NodeConfig { /// If set to `true` auto mining will be disabled #[must_use] - pub fn with_no_mining(mut self, no_mining: bool) -> Self { + pub const fn with_no_mining(mut self, no_mining: bool) -> Self { self.no_mining = no_mining; self } /// Sets the slots in an epoch #[must_use] - pub fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self { + pub const 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 { + pub const fn with_port(mut self, port: u16) -> Self { self.port = port; self } @@ -850,7 +850,7 @@ impl NodeConfig { } #[must_use] - pub fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { + pub const fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { self.no_storage_caching = no_storage_caching; self } @@ -886,7 +886,7 @@ impl NodeConfig { /// Sets the `fork_chain_id` to use to fork off local cache from #[must_use] - pub fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { + pub const fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { self.fork_chain_id = fork_chain_id; self } @@ -900,7 +900,7 @@ impl NodeConfig { /// Sets the `fork_request_timeout` to use for requests #[must_use] - pub fn fork_request_timeout(mut self, fork_request_timeout: Option) -> Self { + pub const fn fork_request_timeout(mut self, fork_request_timeout: Option) -> Self { if let Some(fork_request_timeout) = fork_request_timeout { self.fork_request_timeout = fork_request_timeout; } @@ -909,7 +909,7 @@ impl NodeConfig { /// Sets the `fork_request_retries` to use for spurious networks #[must_use] - pub fn fork_request_retries(mut self, fork_request_retries: Option) -> Self { + pub const fn fork_request_retries(mut self, fork_request_retries: Option) -> Self { if let Some(fork_request_retries) = fork_request_retries { self.fork_request_retries = fork_request_retries; } @@ -918,7 +918,7 @@ impl NodeConfig { /// Sets the initial `fork_retry_backoff` for rate limits #[must_use] - pub fn fork_retry_backoff(mut self, fork_retry_backoff: Option) -> Self { + pub const fn fork_retry_backoff(mut self, fork_retry_backoff: Option) -> Self { if let Some(fork_retry_backoff) = fork_retry_backoff { self.fork_retry_backoff = fork_retry_backoff; } @@ -929,7 +929,10 @@ impl NodeConfig { /// /// See also, #[must_use] - pub fn fork_compute_units_per_second(mut self, compute_units_per_second: Option) -> Self { + pub const fn fork_compute_units_per_second( + mut self, + compute_units_per_second: Option, + ) -> Self { if let Some(compute_units_per_second) = compute_units_per_second { self.compute_units_per_second = compute_units_per_second; } @@ -938,35 +941,35 @@ impl NodeConfig { /// Sets whether to enable tracing #[must_use] - pub fn with_tracing(mut self, enable_tracing: bool) -> Self { + pub const fn with_tracing(mut self, enable_tracing: bool) -> Self { self.enable_tracing = enable_tracing; self } /// Sets whether to enable steps tracing #[must_use] - pub fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self { + pub const fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self { self.enable_steps_tracing = enable_steps_tracing; self } /// Sets whether to print `console.log` invocations to stdout. #[must_use] - pub fn with_print_logs(mut self, print_logs: bool) -> Self { + pub const fn with_print_logs(mut self, print_logs: bool) -> Self { self.print_logs = print_logs; self } /// Sets whether to print traces to stdout. #[must_use] - pub fn with_print_traces(mut self, print_traces: bool) -> Self { + pub const fn with_print_traces(mut self, print_traces: bool) -> Self { self.print_traces = print_traces; self } /// Sets whether to enable autoImpersonate #[must_use] - pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self { + pub const fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self { self.enable_auto_impersonate = enable_auto_impersonate; self } @@ -985,7 +988,7 @@ impl NodeConfig { } #[must_use] - pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self { + pub const fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self { self.transaction_order = transaction_order; self } @@ -1024,14 +1027,14 @@ impl NodeConfig { /// Sets whether to disable the default create2 deployer #[must_use] - pub fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { + pub const fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { self.disable_default_create2_deployer = yes; self } /// Sets whether to disable pool balance checks #[must_use] - pub fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self { + pub const fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self { self.disable_pool_balance_checks = yes; self } @@ -1045,7 +1048,7 @@ impl NodeConfig { /// Enable features for provided networks. #[must_use] - pub fn with_networks(mut self, networks: NetworkConfigs) -> Self { + pub const fn with_networks(mut self, networks: NetworkConfigs) -> Self { self.networks = networks; self } @@ -1066,12 +1069,12 @@ impl NodeConfig { /// Makes the node silent to not emit anything on stdout #[must_use] - pub fn silent(self) -> Self { + pub const fn silent(self) -> Self { self.set_silent(true) } #[must_use] - pub fn set_silent(mut self, silent: bool) -> Self { + pub const fn set_silent(mut self, silent: bool) -> Self { self.silent = silent; self } @@ -1541,7 +1544,7 @@ pub enum ForkChoice { impl ForkChoice { /// Returns the block number to fork from - pub fn block_number(&self) -> Option { + pub const fn block_number(&self) -> Option { match self { Self::Block(block_number) => Some(*block_number), Self::Transaction(_) => None, @@ -1549,7 +1552,7 @@ impl ForkChoice { } /// Returns the transaction hash to fork from - pub fn transaction_hash(&self) -> Option { + pub const fn transaction_hash(&self) -> Option { match self { Self::Block(_) => None, Self::Transaction(transaction_hash) => Some(*transaction_hash), @@ -1579,12 +1582,12 @@ pub struct PruneStateHistoryConfig { impl PruneStateHistoryConfig { /// Returns `true` if writing state history is supported - pub fn is_state_history_supported(&self) -> bool { + pub const fn is_state_history_supported(&self) -> bool { !self.enabled || self.max_memory_history.is_some() } /// Returns true if this setting was enabled. - pub fn is_config_enabled(&self) -> bool { + pub const fn is_config_enabled(&self) -> bool { self.enabled } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index af27b01dca34e..1909e50548d4a 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -418,7 +418,7 @@ impl EthApi { } }) .unwrap_or_default(), - network: if self.backend.is_tempo() { Some("tempo".to_string()) } else { None }, + network: self.backend.is_tempo().then(|| "tempo".to_string()), }) } @@ -477,6 +477,10 @@ impl EthApi { /// Sets the specific timestamp and returns the number of seconds between the given timestamp /// and the current time. /// + /// The `timestamp` is in seconds. The `evm_setTime` JSON-RPC method accepts both seconds and + /// milliseconds (values above 1e12 are treated as milliseconds); the RPC handler normalises + /// the input to seconds before calling this function. + /// /// Handler for RPC call: `evm_setTime` pub fn evm_set_time(&self, timestamp: u64) -> Result { node_info!("evm_setTime"); @@ -485,7 +489,7 @@ impl EthApi { // number of seconds between the given timestamp and the current time. let offset = timestamp.saturating_sub(now); - Ok(Duration::from_millis(offset).as_secs()) + Ok(offset) } /// Set the next block gas limit @@ -625,6 +629,7 @@ impl EthApi { } /// Handler for RPC call: `anvil_getBlobByHash` + #[allow(clippy::large_stack_frames)] pub fn anvil_get_blob_by_versioned_hash( &self, hash: B256, @@ -1480,6 +1485,7 @@ impl EthApi { } /// Executes the [EthRequest] and returns an RPC [ResponseResult]. + #[allow(clippy::large_stack_frames)] pub async fn execute(&self, request: EthRequest) -> ResponseResult { trace!(target: "rpc::api", "executing eth request"); let response = match request.clone() { @@ -1753,7 +1759,15 @@ impl EthApi { "The timestamp is too big", )); } - let time = timestamp.to::(); + // evm_setTime accepts either seconds or milliseconds for Ganache compatibility. + // Timestamps above 1e12 are interpreted as milliseconds and converted to + // seconds; below that threshold they are treated as seconds directly. + let raw = timestamp.to::(); + let time = if raw > 1_000_000_000_000 { + Duration::from_millis(raw).as_secs() + } else { + raw + }; self.evm_set_time(time).to_rpc_result() } EthRequest::EvmSetBlockGasLimit(gas_limit) => { diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index c529b96dd81b3..d5d1638ef3b71 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -97,7 +97,7 @@ pub struct CheatsState { } impl CheatEcrecover { - pub fn new(cheats: Arc) -> Self { + pub const fn new(cheats: Arc) -> Self { Self { cheats } } } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index b27ee942c87bb..de9ad434252f3 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -226,7 +226,7 @@ pub trait Db: /// Deserialize and add all chain data to the backend storage fn load_state(&mut self, state: SerializableState) -> DatabaseResult { - for (addr, account) in state.accounts.into_iter() { + for (addr, account) in state.accounts { let old_account_nonce = DatabaseRef::basic_ref(self, addr) .ok() .and_then(|acc| acc.map(|acc| acc.nonce)) @@ -250,7 +250,7 @@ pub trait Db: }, ); - for (k, v) in account.storage.into_iter() { + for (k, v) in account.storage { self.set_storage_at(addr, k, v)?; } } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 70d3599a765f1..3df14f6ea86f3 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -436,13 +436,11 @@ where trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", pool_tx.hash(), out); trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", pool_tx.hash(), exit_reason, gas_used); - let contract_address = if pending.transaction.to().is_none() { + let contract_address = pending.transaction.to().is_none().then(|| { let addr = sender.create(nonce); trace!(target: "backend", "Contract creation tx: computed address {:?}", addr); - Some(addr) - } else { - None - }; + addr + }); // TODO: replace `TransactionInfo` with alloy receipt/transaction types let transaction_index = tx_info.len() as u64; diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 09871d32c0120..bd3d1292f01b5 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -370,23 +370,8 @@ impl ClientFork { number: u64, index: usize, ) -> Result, TransportError> { - if let Some(block) = self.block_by_number(number).await? { - #[allow(clippy::collapsible_match)] - match block.transactions() { - BlockTransactions::Full(txs) => { - if let Some(tx) = txs.get(index) { - return Ok(Some(tx.clone())); - } - } - BlockTransactions::Hashes(hashes) => { - if let Some(tx_hash) = hashes.get(index) { - return self.transaction_by_hash(*tx_hash).await; - } - } - BlockTransactions::Uncle => {} - } - } - Ok(None) + let block = self.block_by_number(number).await?; + self.transaction_at_block_index(block, index).await } pub async fn transaction_by_block_hash_and_index( @@ -394,8 +379,16 @@ impl ClientFork { hash: B256, index: usize, ) -> Result, TransportError> { - if let Some(block) = self.block_by_hash(hash).await? { - #[allow(clippy::collapsible_match)] + let block = self.block_by_hash(hash).await?; + self.transaction_at_block_index(block, index).await + } + + async fn transaction_at_block_index( + &self, + block: Option, + index: usize, + ) -> Result, TransportError> { + if let Some(block) = block { match block.transactions() { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index def04501abaa8..c739cd26d1de6 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -18,7 +18,7 @@ pub struct StorageInfo { } impl StorageInfo { - pub(crate) fn new(backend: Arc>) -> Self { + pub(crate) const fn new(backend: Arc>) -> Self { Self { backend } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index f8dd6012a4295..be57f40a8fd0a 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -141,7 +141,8 @@ use tempo_chainspec::hardfork::TempoHardfork; use tempo_evm::evm::TempoEvmFactory; use tempo_primitives::TEMPO_TX_TYPE_ID; use tempo_revm::{ - TempoBlockEnv, TempoHaltReason, TempoTxEnv, evm::TempoContext, gas_params::tempo_gas_params, + TempoBatchCallEnv, TempoBlockEnv, TempoHaltReason, TempoTxEnv, evm::TempoContext, + gas_params::tempo_gas_params, }; use tokio::sync::RwLock as AsyncRwLock; @@ -182,7 +183,7 @@ impl fmt::Debug for BlockRequest { } impl BlockRequest { - pub fn block_number(&self) -> BlockNumber { + pub const fn block_number(&self) -> BlockNumber { match *self { Self::Pending(_) => BlockNumber::Pending, Self::Number(n) => BlockNumber::Number(n), @@ -351,12 +352,12 @@ impl Backend { } /// Returns the `TimeManager` responsible for timestamps - pub fn time(&self) -> &TimeManager { + pub const fn time(&self) -> &TimeManager { &self.time } /// Returns the `CheatsManager` responsible for executing cheatcodes - pub fn cheats(&self) -> &CheatsManager { + pub const fn cheats(&self) -> &CheatsManager { &self.cheats } @@ -368,12 +369,12 @@ impl Backend { } /// Returns the `FeeManager` that manages fee/pricings - pub fn fees(&self) -> &FeeManager { + pub const fn fees(&self) -> &FeeManager { &self.fees } /// The EVM environment data of the blockchain - pub fn evm_env(&self) -> &Arc> { + pub const fn evm_env(&self) -> &Arc> { &self.evm_env } @@ -407,7 +408,7 @@ impl Backend { } /// Returns the genesis data for the Beacon API. - pub fn genesis_time(&self) -> u64 { + pub const fn genesis_time(&self) -> u64 { self.genesis.timestamp } @@ -482,17 +483,17 @@ impl Backend { } /// Returns true if op-stack deposits are active - pub fn is_optimism(&self) -> bool { + pub const fn is_optimism(&self) -> bool { self.networks.is_optimism() } /// Returns true if Tempo network mode is active - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { self.networks.is_tempo() } /// Returns the active hardfork. - pub fn hardfork(&self) -> FoundryHardfork { + pub const fn hardfork(&self) -> FoundryHardfork { self.hardfork } @@ -581,7 +582,7 @@ impl Backend { } /// Returns an error if op-stack deposits are not active - pub fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { + pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { if self.is_optimism() { return Ok(()); } @@ -589,7 +590,7 @@ impl Backend { } /// Returns an error if Tempo transactions are not active - pub fn ensure_tempo_active(&self) -> Result<(), BlockchainError> { + pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> { if self.is_tempo() { return Ok(()); } @@ -636,7 +637,7 @@ impl Backend { } /// Returns whether the minimum suggested priority fee is enforced - pub fn is_min_priority_fee_enforced(&self) -> bool { + pub const fn is_min_priority_fee_enforced(&self) -> bool { self.fees.is_min_priority_fee_enforced() } @@ -1222,7 +1223,7 @@ impl Backend { I: Inspector>>, WrapDatabaseRef<&'db DB>: Database, { - let hardfork = TempoHardfork::from(evm_env.cfg_env.spec); + let hardfork = TempoHardfork::from(self.hardfork); let tempo_env = EvmEnv::new( evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)), TempoBlockEnv { inner: evm_env.block_env.clone(), timestamp_millis_part: 0 }, @@ -1292,7 +1293,7 @@ impl Backend { let mut evm = OpEvmFactory::default().create_evm_with_inspector(db, op_env, inspector); run!(evm) } else if self.is_tempo() { - let hardfork = TempoHardfork::from(evm_env.cfg_env.spec); + let hardfork = TempoHardfork::from(self.hardfork); let tempo_env = EvmEnv::new( evm_env .cfg_env @@ -1383,11 +1384,7 @@ impl Backend { gas_priority_fee: max_priority_fee_per_gas, max_fee_per_blob_gas: max_fee_per_blob_gas .or_else(|| { - if !blob_hashes.is_empty() { - evm_env.block_env.blob_gasprice() - } else { - Some(0) - } + if blob_hashes.is_empty() { Some(0) } else { evm_env.block_env.blob_gasprice() } }) .unwrap_or_default(), kind: match to { @@ -1415,8 +1412,10 @@ impl Backend { evm_env.cfg_env.disable_base_fee = true; } - // Deposit transaction? - if let Ok(deposit) = get_deposit_tx_parts(&other) { + // Deposit transaction? (only valid when op-stack deposits are active) + if self.ensure_op_deposits_active().is_ok() + && let Ok(deposit) = get_deposit_tx_parts(&other) + { tx_env.deposit = deposit; } @@ -1432,9 +1431,59 @@ impl Backend { ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { let mut inspector = self.build_inspector(); + // Extract Tempo-specific fields before `build_call_env` consumes `other`. + let tempo_overrides = self.is_tempo().then(|| { + let fee_token = + request.other.get_deserialized::
("feeToken").and_then(|r| r.ok()); + let nonce_key = request + .other + .get_deserialized::("nonceKey") + .and_then(|r| r.ok()) + .unwrap_or_default(); + let valid_before = request + .other + .get_deserialized::("validBefore") + .and_then(|r| r.ok()) + .map(|v| v.saturating_to::()); + let valid_after = request + .other + .get_deserialized::("validAfter") + .and_then(|r| r.ok()) + .map(|v| v.saturating_to::()); + (fee_token, nonce_key, valid_before, valid_after) + }); + let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let ResultAndState { result, state } = - self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)?; + if let Some((fee_token, nonce_key, valid_before, valid_after)) = tempo_overrides { + use tempo_primitives::transaction::Call; + + let base = tx_env.base; + let mut tempo_tx = TempoTxEnv::from(base.clone()); + tempo_tx.fee_token = fee_token; + + if !nonce_key.is_zero() || valid_before.is_some() || valid_after.is_some() { + // For gas estimation we don't have a signed tx, so generate a + // unique hash for expiring-nonce replay protection. The nonce + // manager needs a non-zero hash; the actual value doesn't matter + // because the state is discarded after estimation. + let estimation_hash = keccak256(base.data.as_ref()); + tempo_tx.tempo_tx_env = Some(Box::new(TempoBatchCallEnv { + nonce_key, + valid_before, + valid_after, + aa_calls: vec![Call { to: base.kind, value: base.value, input: base.data }], + tx_hash: estimation_hash, + expiring_nonce_hash: Some(estimation_hash), + ..Default::default() + })); + } + self.transact_tempo_with_inspector_ref(state, &evm_env, &mut inspector, tempo_tx)? + } else { + self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)? + }; + let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); inspector.print_logs(); @@ -1797,6 +1846,7 @@ impl Backend { })) } + #[allow(clippy::large_stack_frames)] pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result> { let storage = self.blockchain.storage.read(); for block in storage.blocks.values() { @@ -2135,7 +2185,7 @@ impl Backend { } // Clear all storage and reinitialize with genesis - let base_fee = if self.fees.is_eip1559() { Some(self.fees.base_fee()) } else { None }; + let base_fee = self.fees.is_eip1559().then(|| self.fees.base_fee()); *self.blockchain.storage.write() = BlockchainStorage::new( &self.evm_env.read(), base_fee, @@ -2328,12 +2378,12 @@ where // get the range that predates the fork if any if let Some(fork) = self.get_fork() { - let mut to_on_fork = to; - - if !fork.predates_fork(to) { + let to_on_fork = if fork.predates_fork(to) { + to + } else { // adjust the ranges - to_on_fork = fork.block_number(); - } + fork.block_number() + }; if fork.predates_fork_inclusive(from) { // this data is only available on the forked client @@ -2403,6 +2453,56 @@ where self.do_mine_block(pool_transactions).await } + /// Builds a [`BlockInfo`] from the EVM environment, execution results, and transactions. + fn build_block_info( + evm_env: &EvmEnv, + parent_hash: B256, + number: u64, + state_root: B256, + block_result: BlockExecutionResult, + transactions: Vec>, + transaction_infos: Vec, + ) -> BlockInfo { + let spec_id = *evm_env.spec_id(); + let is_shanghai = spec_id >= SpecId::SHANGHAI; + let is_cancun = spec_id >= SpecId::CANCUN; + let is_prague = spec_id >= SpecId::PRAGUE; + + let receipts_root = calculate_receipt_root(&block_result.receipts); + let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used); + let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| { + b.accrue_bloom(r.logs_bloom()); + b + }); + + let header = Header { + parent_hash, + ommers_hash: Default::default(), + beneficiary: evm_env.block_env.beneficiary, + state_root, + transactions_root: Default::default(), + receipts_root, + logs_bloom: bloom, + difficulty: evm_env.block_env.difficulty, + number, + gas_limit: evm_env.block_env.gas_limit, + gas_used: block_result.gas_used, + timestamp: evm_env.block_env.timestamp.saturating_to(), + extra_data: Default::default(), + mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(), + nonce: Default::default(), + base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(evm_env.block_env.basefee), + parent_beacon_block_root: is_cancun.then_some(Default::default()), + blob_gas_used: cumulative_blob_gas_used, + excess_blob_gas: if is_cancun { evm_env.block_env.blob_excess_gas() } else { None }, + withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), + requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), + }; + + let block = create_block(header, transactions); + BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts } + } + async fn do_mine_block( &self, pool_transactions: Vec>>, @@ -2457,18 +2557,6 @@ where evm_env.block_env.timestamp = U256::from(self.time.next_timestamp()); let spec_id = *evm_env.spec_id(); - let is_shanghai = spec_id >= SpecId::SHANGHAI; - let is_cancun = spec_id >= SpecId::CANCUN; - let is_prague = spec_id >= SpecId::PRAGUE; - let gas_limit = evm_env.block_env.gas_limit; - let difficulty = evm_env.block_env.difficulty; - let mix_hash = evm_env.block_env.prevrandao; - let beneficiary = evm_env.block_env.beneficiary; - let timestamp = evm_env.block_env.timestamp; - let base_fee = - if spec_id >= SpecId::LONDON { Some(evm_env.block_env.basefee) } else { None }; - let excess_blob_gas = - if is_cancun { evm_env.block_env.blob_excess_gas() } else { None }; let inspector_tx_config = self.inspector_tx_config(); let gas_config = self.pool_tx_gas_config(&evm_env); @@ -2489,50 +2577,17 @@ where let included = pool_result.included; let invalid = pool_result.invalid; let not_yet_valid = pool_result.not_yet_valid; - let transaction_infos = pool_result.tx_info; - let transactions = pool_result.txs; let state_root = db.maybe_state_root().unwrap_or_default(); - - // 7. Build block header - let receipts_root = calculate_receipt_root(&block_result.receipts); - let cumulative_gas_used = block_result.gas_used; - let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used); - let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| { - b.accrue_bloom(r.logs_bloom()); - b - }); - - let header = Header { - parent_hash: best_hash, - ommers_hash: Default::default(), - beneficiary, + let block_info = Self::build_block_info( + &evm_env, + best_hash, + block_number, state_root, - transactions_root: Default::default(), - receipts_root, - logs_bloom: bloom, - difficulty, - number: block_number, - gas_limit, - gas_used: cumulative_gas_used, - timestamp: timestamp.saturating_to(), - extra_data: Default::default(), - mix_hash: mix_hash.unwrap_or_default(), - nonce: Default::default(), - base_fee_per_gas: base_fee, - parent_beacon_block_root: is_cancun.then_some(Default::default()), - blob_gas_used: cumulative_blob_gas_used, - excess_blob_gas, - withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), - requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), - }; - - let block = create_block(header, transactions); - let block_info: BlockInfo = BlockInfo { - block, - transactions: transaction_infos, - receipts: block_result.receipts, - }; + block_result, + pool_result.txs, + pool_result.tx_info, + ); // update the new blockhash in the db itself let block_hash = block_info.block.header.hash_slow(); @@ -2700,17 +2755,6 @@ where let parent_hash = self.blockchain.storage.read().best_hash; let spec_id = *evm_env.spec_id(); - let is_shanghai = spec_id >= SpecId::SHANGHAI; - let is_cancun = spec_id >= SpecId::CANCUN; - let is_prague = spec_id >= SpecId::PRAGUE; - let gas_limit = evm_env.block_env.gas_limit; - let difficulty = evm_env.block_env.difficulty; - let mix_hash = evm_env.block_env.prevrandao; - let beneficiary = evm_env.block_env.beneficiary; - let timestamp = evm_env.block_env.timestamp; - let base_fee = - if spec_id >= SpecId::LONDON { Some(evm_env.block_env.basefee) } else { None }; - let excess_blob_gas = if is_cancun { evm_env.block_env.blob_excess_gas() } else { None }; let inspector_tx_config = self.inspector_tx_config(); let gas_config = self.pool_tx_gas_config(&evm_env); @@ -2726,49 +2770,20 @@ where &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env), ); - let transaction_infos = pool_result.tx_info; - let transactions = pool_result.txs; - // Extract inner CacheDB (which implements MaybeFullDatabase) let cache_db = cache_db.0; let state_root = cache_db.maybe_state_root().unwrap_or_default(); - - let receipts_root = calculate_receipt_root(&block_result.receipts); - let cumulative_gas_used = block_result.gas_used; - let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used); - let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| { - b.accrue_bloom(r.logs_bloom()); - b - }); - - let header = Header { + let block_number = evm_env.block_env.number.saturating_to(); + let block_info = Self::build_block_info( + &evm_env, parent_hash, - ommers_hash: Default::default(), - beneficiary, + block_number, state_root, - transactions_root: Default::default(), - receipts_root, - logs_bloom: bloom, - difficulty, - number: evm_env.block_env.number.saturating_to(), - gas_limit, - gas_used: cumulative_gas_used, - timestamp: timestamp.saturating_to(), - extra_data: Default::default(), - mix_hash: mix_hash.unwrap_or_default(), - nonce: Default::default(), - base_fee_per_gas: base_fee, - parent_beacon_block_root: is_cancun.then_some(Default::default()), - blob_gas_used: cumulative_blob_gas_used, - excess_blob_gas, - withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS), - requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH), - }; - - let block = create_block(header, transactions); - let block_info = - BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts }; + block_result, + pool_result.txs, + pool_result.tx_info, + ); f(Box::new(cache_db), block_info) } @@ -3680,11 +3695,8 @@ impl> Backend { let best_number = self.blockchain.storage.read().best_number; let blocks = self.blockchain.storage.read().serialized_blocks(); let transactions = self.blockchain.storage.read().serialized_transactions(); - let historical_states = if preserve_historical_states { - Some(self.states.write().serialized_states()) - } else { - None - }; + let historical_states = + preserve_historical_states.then(|| self.states.write().serialized_states()); let state = self.db.read().await.dump_state( at, diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 3cb4c869db14e..a80f6bfd1f5e8 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -85,7 +85,7 @@ impl InMemoryBlockStates { } /// Configures no disk caching - pub fn memory_only(mut self) -> Self { + pub const fn memory_only(mut self) -> Self { self.max_on_disk_limit = 0; self } @@ -112,7 +112,7 @@ impl InMemoryBlockStates { } /// Returns true if only memory caching is supported. - fn is_memory_only(&self) -> bool { + const fn is_memory_only(&self) -> bool { self.max_on_disk_limit == 0 } @@ -197,7 +197,7 @@ impl InMemoryBlockStates { } /// Sets the maximum number of stats we keep in memory - pub fn set_cache_limit(&mut self, limit: usize) { + pub const fn set_cache_limit(&mut self, limit: usize) { self.in_memory_limit = limit; } @@ -605,14 +605,15 @@ impl MinedTransaction { CallKind::Create2 => OperationType::OpCreate2, _ => return None, }; - let mut from = node.trace.caller; - let mut to = node.trace.address; - let mut value = node.trace.value; - if node.is_selfdestruct() { - from = node.trace.address; - to = node.trace.selfdestruct_refund_target.unwrap_or_default(); - value = node.trace.selfdestruct_transferred_value.unwrap_or_default(); - } + let (from, to, value) = if node.is_selfdestruct() { + ( + node.trace.address, + node.trace.selfdestruct_refund_target.unwrap_or_default(), + node.trace.selfdestruct_transferred_value.unwrap_or_default(), + ) + } else { + (node.trace.caller, node.trace.address, node.trace.value) + }; Some(InternalOperation { r#type, from, to, value }) }) .collect() diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index c6a8660e02425..e5cfa9ef7a08a 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -84,7 +84,7 @@ impl FeeManager { } /// Returns the base fee params used for EIP-1559 calculations - pub fn base_fee_params(&self) -> BaseFeeParams { + pub const fn base_fee_params(&self) -> BaseFeeParams { self.base_fee_params } @@ -93,11 +93,11 @@ impl FeeManager { } /// Returns true for post London - pub fn is_eip1559(&self) -> bool { + pub const fn is_eip1559(&self) -> bool { (self.spec_id as u8) >= (SpecId::LONDON as u8) } - pub fn is_eip4844(&self) -> bool { + pub const fn is_eip4844(&self) -> bool { (self.spec_id as u8) >= (SpecId::CANCUN as u8) } @@ -110,7 +110,7 @@ impl FeeManager { if self.is_eip1559() { *self.base_fee.read() } else { 0 } } - pub fn is_min_priority_fee_enforced(&self) -> bool { + pub const fn is_min_priority_fee_enforced(&self) -> bool { self.is_min_priority_fee_enforced } @@ -120,7 +120,7 @@ impl FeeManager { } pub fn excess_blob_gas_and_price(&self) -> Option { - if self.is_eip4844() { Some(*self.blob_excess_gas_and_price.read()) } else { None } + self.is_eip4844().then(|| *self.blob_excess_gas_and_price.read()) } pub fn base_fee_per_blob_gas(&self) -> u128 { @@ -210,7 +210,7 @@ impl FeeHistoryService where N::ReceiptEnvelope: TxReceipt, { - pub fn new( + pub const fn new( blob_params: BlobParams, new_blocks: NewBlockNotifications, cache: FeeHistoryCache, @@ -226,7 +226,7 @@ where } /// Returns the configured history limit - pub fn fee_history_limit(&self) -> u64 { + pub const fn fee_history_limit(&self) -> u64 { self.fee_history_limit } @@ -392,7 +392,7 @@ pub struct FeeDetails { impl FeeDetails { /// All values zero - pub fn zero() -> Self { + pub const fn zero() -> Self { Self { gas_price: Some(0), max_fee_per_gas: Some(0), @@ -402,7 +402,7 @@ 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 { + pub const fn or_zero_fees(self) -> Self { let Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas } = self; @@ -415,7 +415,7 @@ impl FeeDetails { } /// Turns this type into a tuple - pub fn split(self) -> (Option, Option, Option, Option) { + pub const 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) diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 666ffb1771dac..d6a99f50cd08f 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -124,11 +124,11 @@ impl Pool { }; trace!(target: "txpool", "Dropped transactions: {:?}", removed.iter().map(|tx| tx.hash()).collect::>()); - let mut dropped = None; - if !removed.is_empty() { - dropped = removed.into_iter().find(|t| *t.pending_transaction.hash() == tx); + if removed.is_empty() { + None + } else { + removed.into_iter().find(|t| *t.pending_transaction.hash() == tx) } - dropped } /// Notifies listeners if the transaction was added to the ready queue. @@ -510,7 +510,7 @@ pub enum AddedTransaction { } impl AddedTransaction { - pub fn hash(&self) -> &TxHash { + pub const fn hash(&self) -> &TxHash { match self { 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 98c16fed375c9..df65822e1eab3 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -91,7 +91,7 @@ pub struct PoolTransaction { // == impl PoolTransaction == impl PoolTransaction { - pub fn new(transaction: PendingTransaction) -> Self { + pub const fn new(transaction: PendingTransaction) -> Self { Self { pending_transaction: transaction, requires: vec![], @@ -101,7 +101,7 @@ impl PoolTransaction { } /// Returns the hash of this transaction - pub fn hash(&self) -> TxHash { + pub const fn hash(&self) -> TxHash { *self.pending_transaction.hash() } } @@ -276,7 +276,7 @@ impl PendingTransactions { .and_then(|hash| self.waiting_queue.get(hash)) { // check if underpriced - if tx.transaction.max_fee_per_gas() < replace.transaction.max_fee_per_gas() { + if tx.transaction.max_fee_per_gas() <= replace.transaction.max_fee_per_gas() { warn!(target: "txpool", "pending replacement transaction underpriced [{:?}]", tx.transaction.hash()); return Err(PoolError::ReplacementUnderpriced(tx.transaction.hash())); } @@ -466,11 +466,11 @@ impl ReadyTransactions { self.ready_tx.read().get(hash).cloned() } - pub fn provided_markers(&self) -> &HashMap { + pub const fn provided_markers(&self) -> &HashMap { &self.provided_markers } - fn next_id(&mut self) -> u64 { + const fn next_id(&mut self) -> u64 { let id = self.id; self.id = self.id.wrapping_add(1); id @@ -515,11 +515,7 @@ impl ReadyTransactions { if let Some(idx) = tx2.unlocks.iter().position(|i| i == &hash) { tx2.unlocks.swap_remove(idx); } - if tx2.unlocks.is_empty() { - Some(tx2.transaction.transaction.provides.clone()) - } else { - None - } + tx2.unlocks.is_empty().then(|| tx2.transaction.transaction.provides.clone()) }; // find previous transactions diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index b96ac52b799d9..98db8a5455df9 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -74,7 +74,7 @@ impl Filters { } /// The duration how long to keep alive stale filters - pub fn keep_alive(&self) -> Duration { + pub const fn keep_alive(&self) -> Duration { self.keepalive } diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 6b2345aa3685f..26e587e8b5123 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -310,7 +310,7 @@ impl Drop for NodeHandle { impl NodeHandle { /// The [NodeConfig] the node was launched with. - pub fn config(&self) -> &NodeConfig { + pub const fn config(&self) -> &NodeConfig { &self.config } @@ -387,7 +387,7 @@ impl NodeHandle { } /// Native token balance of every genesis account in the genesis block. - pub fn genesis_balance(&self) -> U256 { + pub const fn genesis_balance(&self) -> U256 { self.config.genesis_balance } @@ -397,14 +397,14 @@ impl NodeHandle { } /// Returns the shutdown signal. - pub fn shutdown_signal(&self) -> &Option { + pub const fn shutdown_signal(&self) -> &Option { &self._signal } /// Returns mutable access to the shutdown signal. /// /// This can be used to extract the Signal. - pub fn shutdown_signal_mut(&mut self) -> &mut Option { + pub const fn shutdown_signal_mut(&mut self) -> &mut Option { &mut self._signal } @@ -423,7 +423,7 @@ impl NodeHandle { /// /// # } /// ``` - pub fn task_manager(&self) -> &TaskManager { + pub const fn task_manager(&self) -> &TaskManager { &self.task_manager } } @@ -465,15 +465,15 @@ pub fn init_tracing() -> LoggingManager { let manager = LoggingManager::default(); let _ = if let Ok(rust_log_val) = std::env::var("RUST_LOG") - && !rust_log_val.contains("=") + && !rust_log_val.contains('=') { // Mutate the given filter to include `node` logs if it is not already present. // This prevents the unexpected behaviour of not seeing any node logs if a RUST_LOG // is already present that doesn't set it. - let rust_log_val = if !rust_log_val.contains("node") { - format!("{rust_log_val},node=info") - } else { + let rust_log_val = if rust_log_val.contains("node") { rust_log_val + } else { + format!("{rust_log_val},node=info") }; let env_filter: EnvFilter = diff --git a/crates/anvil/src/logging.rs b/crates/anvil/src/logging.rs index 9f0a8d85e9920..31737aa4a3e07 100644 --- a/crates/anvil/src/logging.rs +++ b/crates/anvil/src/logging.rs @@ -22,7 +22,7 @@ pub struct NodeLogLayer { impl NodeLogLayer { /// Returns a new instance of this layer - pub fn new(state: LoggingManager) -> Self { + pub const fn new(state: LoggingManager) -> Self { Self { state } } } diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index 6ada777082bd1..4ceb8be0bfc64 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -81,7 +81,7 @@ pub struct EthSubscriptionResponse { } impl EthSubscriptionResponse { - pub fn new(params: EthSubscriptionParams) -> Self { + pub const fn new(params: EthSubscriptionParams) -> Self { Self { jsonrpc: Version::V2, method: "eth_subscription", params } } } diff --git a/crates/anvil/src/server/rpc_handlers.rs b/crates/anvil/src/server/rpc_handlers.rs index 321970bd03bcf..87e993c4ba75a 100644 --- a/crates/anvil/src/server/rpc_handlers.rs +++ b/crates/anvil/src/server/rpc_handlers.rs @@ -22,7 +22,7 @@ pub struct HttpEthRpcHandler { impl HttpEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` - pub fn new(api: EthApi) -> Self { + pub const fn new(api: EthApi) -> Self { Self { api } } } @@ -45,7 +45,7 @@ pub struct PubSubEthRpcHandler { impl PubSubEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` - pub fn new(api: EthApi) -> Self { + pub const fn new(api: EthApi) -> Self { Self { api } } diff --git a/crates/anvil/src/tasks/block_listener.rs b/crates/anvil/src/tasks/block_listener.rs index d729c6b63686d..073368a8afdb9 100644 --- a/crates/anvil/src/tasks/block_listener.rs +++ b/crates/anvil/src/tasks/block_listener.rs @@ -20,7 +20,7 @@ where St: Stream, F: Fn(::Item) -> Fut, { - pub fn new(on_shutdown: Shutdown, block_stream: St, task_factory: F) -> Self { + pub const fn new(on_shutdown: Shutdown, block_stream: St, task_factory: F) -> Self { Self { stream: block_stream, task_factory, task: None, on_shutdown } } } diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index 3d0b38bbb64cd..10def5d64c04e 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -26,7 +26,7 @@ pub struct TaskManager { impl TaskManager { /// Creates a new instance of the task manager - pub fn new(tokio_handle: Handle, on_shutdown: Shutdown) -> Self { + pub const fn new(tokio_handle: Handle, on_shutdown: Shutdown) -> Self { Self { tokio_handle, on_shutdown } } diff --git a/crates/anvil/tests/it/tempo.rs b/crates/anvil/tests/it/tempo.rs index 29615d0cf8806..08c48aaee7716 100644 --- a/crates/anvil/tests/it/tempo.rs +++ b/crates/anvil/tests/it/tempo.rs @@ -2170,3 +2170,488 @@ async fn test_tempo_aa_nonce_too_high_rejected() { tokio::time::sleep(std::time::Duration::from_millis(100)).await; } } + +// ============================================================================ +// Gas Estimation: Tempo AA Transaction +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_estimation_tempo_aa_transaction() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let transfer_call = token.transfer(recipient, U256::from(1000)); + let calldata: Bytes = transfer_call.calldata().clone(); + + let tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default().from(accounts[0]).to(PATH_USD).with_input(calldata), + other: [("feeToken".to_string(), serde_json::json!(PATH_USD.to_string()))] + .into_iter() + .collect(), + }; + + let gas_estimate = provider.estimate_gas(tx).await.unwrap(); + + assert!( + gas_estimate > 21000, + "Tempo AA gas estimate should be greater than 21000, got: {gas_estimate}" + ); +} + +// ============================================================================ +// Gas Estimation: Tempo AA with 2D Nonce +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_estimation_tempo_aa_with_2d_nonce() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let transfer_call = token.transfer(recipient, U256::from(1000)); + let calldata: Bytes = transfer_call.calldata().clone(); + + // Baseline: plain AA tx (no 2D nonce) + let baseline_tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()), + other: [("feeToken".to_string(), serde_json::json!(PATH_USD.to_string()))] + .into_iter() + .collect(), + }; + let baseline_gas = provider.estimate_gas(baseline_tx).await.unwrap(); + + // 2D nonce tx with nonce=0 (new nonce key) + let tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata) + .with_nonce(0), + other: [ + ("feeToken".to_string(), serde_json::json!(PATH_USD.to_string())), + ("nonceKey".to_string(), serde_json::json!("0x64")), + ] + .into_iter() + .collect(), + }; + + let gas_estimate = provider.estimate_gas(tx).await.unwrap(); + + // New 2D nonce key (nonce=0) charges COLD_SLOAD + SSTORE_SET = 22100 gas + let nonce_key_delta = gas_estimate - baseline_gas; + assert!( + nonce_key_delta >= 22_100, + "2D nonce should add >= 22100 gas, got delta: {nonce_key_delta} \ + (baseline: {baseline_gas}, 2d_nonce: {gas_estimate})" + ); +} + +// ============================================================================ +// Gas Estimation: Tempo AA with Expiring Nonce +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_estimation_tempo_aa_expiring_nonce() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let transfer_call = token.transfer(recipient, U256::from(1000)); + let calldata: Bytes = transfer_call.calldata().clone(); + + // Baseline: plain AA tx (no expiring nonce) + let baseline_tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()), + other: [("feeToken".to_string(), serde_json::json!(PATH_USD.to_string()))] + .into_iter() + .collect(), + }; + let baseline_gas = provider.estimate_gas(baseline_tx).await.unwrap(); + + let max_nonce_key = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + + let tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata) + .with_nonce(0), + other: [ + ("feeToken".to_string(), serde_json::json!(PATH_USD.to_string())), + ("nonceKey".to_string(), serde_json::json!(max_nonce_key)), + ] + .into_iter() + .collect(), + }; + + let gas_estimate = provider.estimate_gas(tx).await.unwrap(); + + // At T0, expiring nonces are treated as 2D nonces (22100 gas). + // At T1+, this charges EXPIRING_NONCE_GAS = 13000 instead. + let expiring_delta = gas_estimate - baseline_gas; + assert!( + expiring_delta >= 22_100, + "Expiring nonce should add >= 22100 gas at T0, got delta: {expiring_delta} \ + (baseline: {baseline_gas}, expiring: {gas_estimate})" + ); +} + +// ============================================================================ +// Gas Estimation: T1 Hardfork Nonce Gas Costs +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_estimation_t1_nonce_costs() { + use tempo_chainspec::hardfork::TempoHardfork; + + let (_api, handle) = + spawn(NodeConfig::test_tempo().with_hardfork(Some(TempoHardfork::T1.into()))).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let transfer_call = token.transfer(recipient, U256::from(1000)); + let calldata: Bytes = transfer_call.calldata().clone(); + + // Baseline: plain AA tx (no nonce key) + let baseline_tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()), + other: [("feeToken".to_string(), serde_json::json!(PATH_USD.to_string()))] + .into_iter() + .collect(), + }; + let baseline_gas = provider.estimate_gas(baseline_tx).await.unwrap(); + + // TIP-1000: nonce=0 pays 250k for account creation + assert!( + baseline_gas > 250_000, + "T1 baseline should include 250k (TIP-1000), got: {baseline_gas}" + ); + + // Existing 2D nonce key (nonce=1) charges 5,000 gas + let nonce_2d_tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()) + .with_nonce(1), + other: [ + ("feeToken".to_string(), serde_json::json!(PATH_USD.to_string())), + ("nonceKey".to_string(), serde_json::json!("0x64")), + ] + .into_iter() + .collect(), + }; + let nonce_2d_gas = provider.estimate_gas(nonce_2d_tx).await.unwrap(); + + let baseline_nonce1_tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()) + .with_nonce(1), + other: [("feeToken".to_string(), serde_json::json!(PATH_USD.to_string()))] + .into_iter() + .collect(), + }; + let baseline_nonce1_gas = provider.estimate_gas(baseline_nonce1_tx).await.unwrap(); + let nonce_2d_delta = nonce_2d_gas - baseline_nonce1_gas; + + assert!( + nonce_2d_delta >= 5_000, + "T1: existing 2D nonce key should add >= 5000 gas, got: {nonce_2d_delta}" + ); + + // TIP-1000: nonce=0 should cost 250k more than nonce=1 + let tip1000_delta = baseline_gas - baseline_nonce1_gas; + assert!( + tip1000_delta >= 250_000, + "T1: TIP-1000 delta should be >= 250000, got: {tip1000_delta}" + ); + + // Expiring nonce (nonce_key=MAX) at T1 should charge ~13K for ring buffer ops + // (2*COLD_SLOAD + WARM_SLOAD + 3*WARM_SSTORE_RESET), NOT 22K like at T0. + let max_nonce_key = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + let block = provider.get_block(BlockNumberOrTag::Latest.into()).await.unwrap().unwrap(); + let valid_before = block.header.timestamp + 25; + + let expiring_tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()) + .with_nonce(0), + other: [ + ("feeToken".to_string(), serde_json::json!(PATH_USD.to_string())), + ("nonceKey".to_string(), serde_json::json!(max_nonce_key)), + ("validBefore".to_string(), serde_json::json!(valid_before)), + ] + .into_iter() + .collect(), + }; + let expiring_gas = provider.estimate_gas(expiring_tx).await.unwrap(); + + // Compare against baseline_nonce1 (nonce=1, no nonce key) since both the baseline (nonce=0) + // and expiring tx (nonce=0) include the 250k account creation cost. + let expiring_delta = expiring_gas - baseline_nonce1_gas; + assert!( + expiring_delta >= 13_000, + "T1: expiring nonce should add at least ~13K gas for ring buffer ops, got delta: {expiring_delta}" + ); +} + +// ============================================================================ +// Gas Estimation: 2D Nonce Estimate Sufficient for Real Transaction +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_estimation_2d_nonce_converges() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let recipient = accounts[1]; + let signer = dev_key(0); + + let token = IERC20::new(PATH_USD, &provider); + let transfer_call = token.transfer(recipient, U256::from(1000)); + let calldata: Bytes = transfer_call.calldata().clone(); + + // Estimate gas with 2D nonce + let nonce_key = U256::from(0x64); + let tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()) + .with_nonce(0), + other: [ + ("feeToken".to_string(), serde_json::json!(PATH_USD.to_string())), + ("nonceKey".to_string(), serde_json::json!(format!("{nonce_key:#x}"))), + ] + .into_iter() + .collect(), + }; + + let gas_estimate = provider.estimate_gas(tx).await.unwrap(); + + // Send the actual transaction with the estimated gas to verify it's sufficient + let chain_id = provider.get_chain_id().await.unwrap(); + let base_fee = provider.get_gas_price().await.unwrap(); + + let tempo_tx = TempoTransaction { + chain_id, + fee_token: Some(PATH_USD), + max_priority_fee_per_gas: base_fee / 10, + max_fee_per_gas: base_fee * 2, + gas_limit: gas_estimate, + calls: vec![Call { to: TxKind::Call(PATH_USD), value: U256::ZERO, input: calldata }], + access_list: Default::default(), + nonce_key, + nonce: 0, + fee_payer_signature: None, + valid_before: None, + valid_after: None, + key_authorization: None, + tempo_authorization_list: vec![], + }; + + let sig_hash = tempo_tx.signature_hash(); + let signature = signer.sign_hash(&sig_hash).await.unwrap(); + let tempo_sig = TempoSignature::Primitive(PrimitiveSignature::Secp256k1(signature)); + let signed_tx = AASigned::new_unhashed(tempo_tx, tempo_sig); + let envelope = TempoTxEnvelope::AA(signed_tx); + + let mut encoded = Vec::new(); + envelope.encode_2718(&mut encoded); + + let tx_hash = provider.send_raw_transaction(&encoded).await.unwrap(); + let receipt = tx_hash.get_receipt().await.unwrap(); + assert!( + receipt.status(), + "2D nonce transaction should succeed with estimated gas: {gas_estimate}" + ); + assert!( + receipt.gas_used() <= gas_estimate, + "Gas used ({}) should be <= estimate ({}) for 2D nonce tx", + receipt.gas_used(), + gas_estimate + ); +} + +// ============================================================================ +// Gas Estimation: Converges for Tempo Intrinsic Gas +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_gas_estimation_converges_for_tempo_intrinsic_gas() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let transfer_call = token.transfer(recipient, U256::from(1000)); + let calldata: Bytes = transfer_call.calldata().clone(); + + let tx: WithOtherFields = WithOtherFields { + inner: TransactionRequest::default() + .from(accounts[0]) + .to(PATH_USD) + .with_input(calldata.clone()), + other: [("feeToken".to_string(), serde_json::json!(PATH_USD.to_string()))] + .into_iter() + .collect(), + }; + + let gas_estimate = provider.estimate_gas(tx).await.unwrap(); + + // Send the actual transaction with the estimated gas to verify convergence + let signer = dev_key(0); + let chain_id = provider.get_chain_id().await.unwrap(); + let base_fee = provider.get_gas_price().await.unwrap(); + + let tempo_tx = TempoTransaction { + chain_id, + fee_token: Some(PATH_USD), + max_priority_fee_per_gas: base_fee / 10, + max_fee_per_gas: base_fee * 2, + gas_limit: gas_estimate, + calls: vec![Call { to: TxKind::Call(PATH_USD), value: U256::ZERO, input: calldata }], + access_list: Default::default(), + nonce_key: U256::ZERO, + nonce: 0, + fee_payer_signature: None, + valid_before: None, + valid_after: None, + key_authorization: None, + tempo_authorization_list: vec![], + }; + + let sig_hash = tempo_tx.signature_hash(); + let signature = signer.sign_hash(&sig_hash).await.unwrap(); + let tempo_sig = TempoSignature::Primitive(PrimitiveSignature::Secp256k1(signature)); + let signed_tx = AASigned::new_unhashed(tempo_tx, tempo_sig); + let envelope = TempoTxEnvelope::AA(signed_tx); + + let mut encoded = Vec::new(); + envelope.encode_2718(&mut encoded); + + let tx_hash = provider.send_raw_transaction(&encoded).await.unwrap(); + let receipt = tx_hash.get_receipt().await.unwrap(); + assert!(receipt.status(), "Transaction should succeed with estimated gas: {gas_estimate}"); + + assert!( + receipt.gas_used() <= gas_estimate, + "Gas used ({}) should be <= estimate ({})", + receipt.gas_used(), + gas_estimate + ); +} + +// ============================================================================ +// EIP-1559 Transaction +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_eip1559_transaction() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let sender = accounts[0]; + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let recipient_balance_before = token.balanceOf(recipient).call().await.unwrap(); + + let transfer_amount = U256::from(500_000); + let transfer_call = token.transfer(recipient, transfer_amount); + let calldata: Bytes = transfer_call.calldata().clone(); + + let base_fee = provider.get_gas_price().await.unwrap(); + + let tx = TransactionRequest::default() + .from(sender) + .to(PATH_USD) + .with_input(calldata) + .with_gas_limit(TIP20_TRANSFER_GAS) + .max_fee_per_gas(base_fee * 2) + .max_priority_fee_per_gas(base_fee / 10); + + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert!(receipt.status(), "EIP-1559 transaction should succeed"); + + let recipient_balance_after = token.balanceOf(recipient).call().await.unwrap(); + assert_eq!( + recipient_balance_after, + recipient_balance_before + transfer_amount, + "Recipient should receive transfer amount" + ); +} + +// ============================================================================ +// Legacy Transaction +// ============================================================================ + +#[tokio::test(flavor = "multi_thread")] +async fn test_legacy_transaction() { + let (_api, handle) = spawn(NodeConfig::test_tempo()).await; + let provider = handle.http_provider(); + + let accounts: Vec
= handle.dev_accounts().collect(); + let sender = accounts[0]; + let recipient = accounts[1]; + + let token = IERC20::new(PATH_USD, &provider); + let recipient_balance_before = token.balanceOf(recipient).call().await.unwrap(); + + let transfer_amount = U256::from(250_000); + let transfer_call = token.transfer(recipient, transfer_amount); + let calldata: Bytes = transfer_call.calldata().clone(); + + let gas_price = provider.get_gas_price().await.unwrap(); + + let tx = TransactionRequest::default() + .from(sender) + .to(PATH_USD) + .with_input(calldata) + .with_gas_limit(TIP20_TRANSFER_GAS) + .with_gas_price(gas_price); + + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert!(receipt.status(), "Legacy transaction should succeed"); + + let recipient_balance_after = token.balanceOf(recipient).call().await.unwrap(); + assert_eq!( + recipient_balance_after, + recipient_balance_before + transfer_amount, + "Recipient should receive transfer amount" + ); +} diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index c4ffcdf810d8d..125c613b49969 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -55,6 +55,7 @@ pub fn setup() -> Result<()> { } /// Run the subcommand. +#[allow(clippy::large_stack_frames)] pub async fn run_command(args: CastArgs) -> Result<()> { match args.cmd { // Constants @@ -195,10 +196,10 @@ pub async fn run_command(args: CastArgs) -> Result<()> { print_tokens(&tokens); } CastSubcommand::AbiEncode { sig, packed, args } => { - if !packed { - sh_println!("{}", SimpleCast::abi_encode(&sig, &args)?)? - } else { + if packed { sh_println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?)? + } else { + sh_println!("{}", SimpleCast::abi_encode(&sig, &args)?)? } } CastSubcommand::AbiEncodeEvent { sig, args } => { diff --git a/crates/cast/src/base.rs b/crates/cast/src/base.rs index 239dbdfb1010c..085df0b55e89a 100644 --- a/crates/cast/src/base.rs +++ b/crates/cast/src/base.rs @@ -314,7 +314,7 @@ impl NumberWithBase { } /// Creates a copy of the number with the provided base. - pub fn with_base(&self, base: Base) -> Self { + pub const fn with_base(&self, base: Base) -> Self { Self { number: self.number, is_nonnegative: self.is_nonnegative, base } } @@ -336,17 +336,17 @@ impl NumberWithBase { /// Returns a copy of the underlying number as an unsigned integer. If the value is negative /// then the two's complement of its absolute value will be returned. - pub fn number(&self) -> U256 { + pub const fn number(&self) -> U256 { self.number } /// Returns whether the underlying number is positive or zero. - pub fn is_nonnegative(&self) -> bool { + pub const fn is_nonnegative(&self) -> bool { self.is_nonnegative } /// Returns the underlying base. Defaults to [Decimal][Base]. - pub fn base(&self) -> Base { + pub const fn base(&self) -> Base { self.base } @@ -356,7 +356,7 @@ impl NumberWithBase { } /// Sets the number's base to format to. - pub fn set_base(&mut self, base: Base) -> &mut Self { + pub const fn set_base(&mut self, base: Base) -> &mut Self { self.base = base; self } diff --git a/crates/cast/src/cmd/batch_mktx.rs b/crates/cast/src/cmd/batch_mktx.rs index ec8725ae8c1ab..e2793f7de7571 100644 --- a/crates/cast/src/cmd/batch_mktx.rs +++ b/crates/cast/src/cmd/batch_mktx.rs @@ -10,7 +10,7 @@ use crate::{ use alloy_consensus::SignableTransaction; use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; use alloy_signer::Signer; use clap::Parser; @@ -115,9 +115,7 @@ impl BatchMakeTxArgs { // Set dummy "to" from first call let first_call_to = call_specs.first().map(|s| s.to); let builder = builder.with_to(first_call_to.map(Into::into)).await?; - let mut tx_builder = builder.with_code_sig_and_args(None, None, vec![]).await?; - tx_builder.tx.clear_kind(); - tx_builder.tx.set_value(U256::ZERO); + let tx_builder = builder.with_code_sig_and_args(None, None, vec![]).await?; if raw_unsigned { if eth.wallet.from.is_none() && !has_nonce { diff --git a/crates/cast/src/cmd/batch_send.rs b/crates/cast/src/cmd/batch_send.rs index 4f88913d929da..c381e5f7a1e2a 100644 --- a/crates/cast/src/cmd/batch_send.rs +++ b/crates/cast/src/cmd/batch_send.rs @@ -9,8 +9,8 @@ use crate::{ cmd::send::cast_send, tx::{self, CastTxBuilder, CastTxSender, SendTxOpts}, }; -use alloy_network::{EthereumWallet, TransactionBuilder}; -use alloy_primitives::{Bytes, U256}; +use alloy_network::EthereumWallet; +use alloy_primitives::Bytes; use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::Signer; use clap::Parser; @@ -119,9 +119,7 @@ impl BatchSendArgs { let builder = builder.with_to(first_call_to.map(Into::into)).await?; // Use empty sig/args since we're using calls directly - let mut builder = builder.with_code_sig_and_args(None, None, vec![]).await?; - builder.tx.clear_kind(); - builder.tx.set_value(U256::ZERO); + let builder = builder.with_code_sig_and_args(None, None, vec![]).await?; let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); diff --git a/crates/cast/src/cmd/create2.rs b/crates/cast/src/cmd/create2.rs index a531c95856666..f7e5aae1f2d9e 100644 --- a/crates/cast/src/cmd/create2.rs +++ b/crates/cast/src/cmd/create2.rs @@ -209,6 +209,7 @@ impl Create2Args { let found = Arc::clone(&found); handles.push(std::thread::spawn(move || { // Read the first bytes of the salt as a usize to be able to increment it. + #[repr(C)] struct B256Aligned(B256, [usize; 0]); let mut salt = B256Aligned(salt, []); // SAFETY: B256 is aligned to `usize`. diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index 39f09a6426f81..07e7df1b67517 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -299,7 +299,7 @@ pub enum Erc20Subcommand { } impl Erc20Subcommand { - fn rpc_opts(&self) -> &RpcOpts { + const fn rpc_opts(&self) -> &RpcOpts { match self { Self::Allowance { rpc, .. } => rpc, Self::Approve { send_tx, .. } => &send_tx.eth.rpc, @@ -314,7 +314,7 @@ impl Erc20Subcommand { } } - fn erc20_opts(&self) -> Option<&Erc20TxOpts> { + const fn erc20_opts(&self) -> Option<&Erc20TxOpts> { match self { Self::Approve { tx, .. } | Self::Transfer { tx, .. } diff --git a/crates/cast/src/cmd/interface.rs b/crates/cast/src/cmd/interface.rs index 634fc96d83c31..2e2051822dafc 100644 --- a/crates/cast/src/cmd/interface.rs +++ b/crates/cast/src/cmd/interface.rs @@ -80,7 +80,7 @@ impl InterfaceArgs { }; // Build config for to_sol conversion. - let config = if flatten { Some(ToSolConfig::new().one_contract(true)) } else { None }; + let config = flatten.then(|| ToSolConfig::new().one_contract(true)); // Retrieve interfaces from the array of ABIs. let interfaces = get_interfaces(abis, config)?; diff --git a/crates/cast/src/cmd/keychain.rs b/crates/cast/src/cmd/keychain.rs index c6a9646a6b2ab..d14c043a1620a 100644 --- a/crates/cast/src/cmd/keychain.rs +++ b/crates/cast/src/cmd/keychain.rs @@ -231,7 +231,7 @@ fn parse_signature_type(s: &str) -> Result { } } -fn signature_type_name(t: &SignatureType) -> &'static str { +const fn signature_type_name(t: &SignatureType) -> &'static str { match t { SignatureType::Secp256k1 => "secp256k1", SignatureType::P256 => "p256", @@ -240,7 +240,7 @@ fn signature_type_name(t: &SignatureType) -> &'static str { } } -fn key_type_name(t: &KeyType) -> &'static str { +const fn key_type_name(t: &KeyType) -> &'static str { match t { KeyType::Secp256k1 => "secp256k1", KeyType::P256 => "p256", @@ -248,7 +248,7 @@ fn key_type_name(t: &KeyType) -> &'static str { } } -fn wallet_type_name(t: &WalletType) -> &'static str { +const fn wallet_type_name(t: &WalletType) -> &'static str { match t { WalletType::Local => "local", WalletType::Passkey => "passkey", @@ -546,7 +546,9 @@ async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) // Expiry: show human-readable date and whether it's expired. let expiry_str = format_expiry(info.expiry); - if info.expiry != u64::MAX { + if info.expiry == u64::MAX { + sh_println!("Expiry: {}", expiry_str)?; + } else { let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() @@ -556,8 +558,6 @@ async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) } else { sh_println!("Expiry: {}", expiry_str)?; } - } else { - sh_println!("Expiry: {}", expiry_str)?; } sh_println!("Spending Limits: {}", if info.enforceLimits { "enforced" } else { "none" })?; @@ -579,7 +579,17 @@ async fn run_authorize( ) -> Result<()> { let enforce = enforce_limits || !limits.is_empty(); - let calldata = if !allowed_calls.is_empty() { + let calldata = if allowed_calls.is_empty() { + // Use the legacy authorizeKey when no scopes are needed. + IAccountKeychain::authorizeKeyCall { + keyId: key_address, + signatureType: key_type, + expiry, + enforceLimits: enforce, + limits, + } + .abi_encode() + } else { // Use the T3+ authorizeKey overload with KeyRestrictions when scopes are provided. let sig_type_u8 = match key_type { SignatureType::Secp256k1 => 0u8, @@ -603,16 +613,6 @@ async fn run_authorize( config: restrictions, } .abi_encode() - } else { - // Use the legacy authorizeKey when no scopes are needed. - IAccountKeychain::authorizeKeyCall { - keyId: key_address, - signatureType: key_type, - expiry, - enforceLimits: enforce, - limits, - } - .abi_encode() }; send_keychain_tx(calldata, tx_opts, &send_tx).await @@ -783,10 +783,10 @@ fn print_key_entry(entry: &tempo::KeyEntry) -> Result<()> { if let Some(key_address) = entry.key_address { sh_println!("Key Address: {key_address}")?; - if key_address != entry.wallet_address { - sh_println!("Mode: keychain (access key)")?; - } else { + if key_address == entry.wallet_address { sh_println!("Mode: direct (EOA)")?; + } else { + sh_println!("Mode: keychain (access key)")?; } } else { sh_println!("Key Address: (not set)")?; diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 6a21db65026f6..aece03350df60 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -1,11 +1,6 @@ -use crate::{ - debug::handle_traces, - utils::{apply_chain_and_block_specific_env_changes, block_env_from_header}, -}; -use alloy_consensus::{BlockHeader, Transaction, transaction::SignerRecoverable}; - -use alloy_evm::FromRecoveredTx; -use alloy_network::{BlockResponse, TransactionResponse}; +use crate::{debug::handle_traces, utils::apply_chain_and_block_specific_env_changes}; +use alloy_consensus::Transaction; +use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{ Address, Bytes, U256, map::{AddressSet, HashMap}, @@ -18,9 +13,7 @@ use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, utils::{TraceResult, init_progress}, }; -use foundry_common::{ - SYSTEM_TRANSACTION_TYPE, is_known_system_sender, provider::ProviderBuilder, shell, -}; +use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_impersonated_tx, is_known_system_sender, shell}; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{ Config, @@ -30,17 +23,14 @@ use foundry_config::{ }, }; use foundry_evm::{ - core::{ - FoundryBlock as _, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, - }, + Env, executors::{EvmError, Executor, TracingExecutor}, - hardforks::FoundryHardfork, opts::EvmOpts, traces::{InternalTraceMode, TraceMode, Traces}, + utils::configure_tx_env, }; use futures::TryFutureExt; -use revm::{DatabaseRef, context::Block}; +use revm::{DatabaseRef, primitives::hardfork::SpecId}; /// CLI arguments for `cast run`. #[derive(Clone, Debug, Parser)] @@ -132,22 +122,6 @@ impl RunArgs { /// /// Note: This executes the transaction(s) as is: Cheatcodes are disabled pub async fn run(self) -> Result<()> { - let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); - let mut evm_opts = figment.extract::()?; - - // Auto-detect network from fork chain ID when not explicitly configured. - evm_opts.infer_network_from_fork().await; - - if evm_opts.networks.is_tempo() { - self.run_with_evm::().await - } else if evm_opts.networks.is_optimism() { - self.run_with_evm::().await - } else { - self.run_with_evm::().await - } - } - - async fn run_with_evm(self) -> Result<()> { let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); let evm_opts = figment.extract::()?; let mut config = Config::from_provider(figment)?.sanitized(); @@ -160,7 +134,7 @@ impl RunArgs { let compute_units_per_second = if self.no_rate_limit { Some(u64::MAX) } else { self.compute_units_per_second }; - let provider = ProviderBuilder::::from_config(&config)? + let provider = foundry_cli::utils::get_provider_builder(&config)? .compute_units_per_second_opt(compute_units_per_second) .build()?; @@ -182,52 +156,56 @@ impl RunArgs { )); } - let tx_block_number = tx - .block_number() - .ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))?; + let tx_block_number = + tx.block_number.ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))?; // we need to fork off the parent block config.fork_block_number = Some(tx_block_number - 1); let create2_deployer = evm_opts.create2_deployer; - let (block, (mut evm_env, tx_env, fork, chain, networks)) = tokio::try_join!( + let (block, (mut env, fork, chain, networks)) = tokio::try_join!( // fetch the block the transaction was mined in provider.get_block(tx_block_number.into()).full().into_future().map_err(Into::into), - TracingExecutor::::get_fork_material(&mut config, evm_opts) + TracingExecutor::get_fork_material(&mut config, evm_opts) )?; let mut evm_version = self.evm_version; - evm_env.cfg_env.disable_block_gas_limit = self.disable_block_gas_limit; + env.evm_env.cfg_env.disable_block_gas_limit = self.disable_block_gas_limit; // By default do not enforce transaction gas limits imposed by Osaka (EIP-7825). // Users can opt-in to enable these limits by setting `enable_tx_gas_limit` to true. if !self.enable_tx_gas_limit { - evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); + env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); } - evm_env.cfg_env.limit_contract_code_size = None; - evm_env.block_env.set_number(U256::from(tx_block_number)); + env.evm_env.cfg_env.limit_contract_code_size = None; + env.evm_env.block_env.number = U256::from(tx_block_number); + + let mut parent_beacon_block_root = None; if let Some(block) = &block { - evm_env.block_env = block_env_from_header(block.header()); + env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); + env.evm_env.block_env.beneficiary = block.header.beneficiary; + env.evm_env.block_env.difficulty = block.header.difficulty; + env.evm_env.block_env.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); + env.evm_env.block_env.basefee = block.header.base_fee_per_gas.unwrap_or_default(); + env.evm_env.block_env.gas_limit = block.header.gas_limit; + + if env.evm_env.cfg_env.spec >= SpecId::CANCUN { + parent_beacon_block_root = block.header.parent_beacon_block_root; + } - // Resolve the correct spec for the block using the same approach as reth: walk - // known chain activation conditions to find the latest active fork. Falls back - // to a blob-gas heuristic for unknown chains. + // 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 let Some(hardfork) = FoundryHardfork::from_chain_and_timestamp( - evm_env.cfg_env.chain_id, - block.header().timestamp(), - ) { - evm_env.cfg_env.set_spec(hardfork.into()); - } else if block.header().excess_blob_gas().is_some() { - // TODO: add glamsterdam header field checks in the future - evm_version = Some(EvmVersion::Cancun); + // 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::Prague); } } - apply_chain_and_block_specific_env_changes::( - &mut evm_env, + apply_chain_and_block_specific_env_changes::( + &mut env.evm_env, block, config.networks, ); @@ -241,8 +219,8 @@ impl RunArgs { InternalTraceMode::None }) .with_state_changes(shell::verbosity() > 4); - let mut executor = TracingExecutor::::new( - (evm_env.clone(), tx_env), + let mut executor = TracingExecutor::new( + env.clone(), fork, evm_version, trace_mode, @@ -251,7 +229,17 @@ impl RunArgs { None, )?; - evm_env.cfg_env.set_spec_and_mainnet_gas_params(executor.spec_id()); + if let Some(parent_beacon_block_root) = parent_beacon_block_root { + let timestamp: u64 = env.evm_env.block_env.timestamp.try_into().wrap_err("failed to convert block timestamp to u64")?; + executor.process_beacon_block_root(timestamp, parent_beacon_block_root)?; + } + + let mut env = Env::new_with_spec_id( + env.evm_env.cfg_env.clone(), + env.evm_env.block_env.clone(), + env.tx.clone(), + executor.spec_id(), + ); // Set the state to the moment right before the transaction if !self.quick { @@ -260,10 +248,10 @@ impl RunArgs { } if let Some(block) = block { - let pb = init_progress(block.transactions().len() as u64, "tx"); + let pb = init_progress(block.transactions.len() as u64, "tx"); pb.set_position(0); - let BlockTransactions::Full(ref txs) = *block.transactions() else { + let BlockTransactions::Full(ref txs) = block.transactions else { return Err(eyre::eyre!("Could not get block txs")); }; @@ -282,26 +270,22 @@ impl RunArgs { break; } - let tx_env = TxEnvFor::::from_recovered_tx(tx.as_ref(), tx.from()); + configure_tx_env(&mut env, tx); - evm_env.cfg_env.disable_balance_check = true; + env.evm_env.cfg_env.disable_balance_check = true; if let Some(to) = Transaction::to(tx) { trace!(tx=?tx.tx_hash(),?to, "executing previous call transaction"); - executor.transact_with_env(evm_env.clone(), tx_env.clone()).wrap_err_with( - || { - format!( - "Failed to execute transaction: {:?} in block {}", - tx.tx_hash(), - evm_env.block_env.number() - ) - }, - )?; + executor.transact_with_env(env.clone()).wrap_err_with(|| { + format!( + "Failed to execute transaction: {:?} in block {}", + tx.tx_hash(), + env.evm_env.block_env.number + ) + })?; } else { trace!(tx=?tx.tx_hash(), "executing previous create transaction"); - if let Err(error) = - executor.deploy_with_env(evm_env.clone(), tx_env.clone(), None) - { + if let Err(error) = executor.deploy_with_env(env.clone(), None) { match error { // Reverted transactions should be skipped EvmError::Execution(_) => (), @@ -310,7 +294,7 @@ impl RunArgs { format!( "Failed to deploy transaction: {:?} in block {}", tx.tx_hash(), - evm_env.block_env.number() + env.evm_env.block_env.number ) }); } @@ -327,18 +311,17 @@ impl RunArgs { let result = { executor.set_trace_printer(self.trace_printer); - let tx_env = TxEnvFor::::from_recovered_tx(tx.as_ref(), tx.from()); - - if tx.as_ref().recover_signer().is_ok_and(|signer| signer != tx.from()) { - evm_env.cfg_env.disable_balance_check = true; + configure_tx_env(&mut env, &tx); + if is_impersonated_tx(tx.as_ref()) { + env.evm_env.cfg_env.disable_balance_check = true; } if let Some(to) = Transaction::to(&tx) { trace!(tx=?tx.tx_hash(), to=?to, "executing call transaction"); - TraceResult::from(executor.transact_with_env(evm_env, tx_env)?) + TraceResult::try_from(executor.transact_with_env(env))? } else { trace!(tx=?tx.tx_hash(), "executing create transaction"); - TraceResult::try_from(executor.deploy_with_env(evm_env, tx_env, None))? + TraceResult::try_from(executor.deploy_with_env(env, None))? } }; @@ -361,8 +344,8 @@ impl RunArgs { } } -pub fn fetch_contracts_bytecode_from_trace( - executor: &Executor, +pub fn fetch_contracts_bytecode_from_trace( + executor: &Executor, result: &TraceResult, ) -> Result> { let mut contracts_bytecode = HashMap::default(); diff --git a/crates/cast/src/cmd/storage.rs b/crates/cast/src/cmd/storage.rs index b2a2c32251f6f..1295b49c19551 100644 --- a/crates/cast/src/cmd/storage.rs +++ b/crates/cast/src/cmd/storage.rs @@ -382,7 +382,7 @@ fn add_storage_layout_output>(project: }) } -fn is_storage_layout_empty(storage_layout: &Option) -> bool { +const fn is_storage_layout_empty(storage_layout: &Option) -> bool { if let Some(s) = storage_layout { s.storage.is_empty() } else { true } } diff --git a/crates/cast/src/cmd/tip20.rs b/crates/cast/src/cmd/tip20.rs index 24428aef36cf0..fc1cb6c9bec5e 100644 --- a/crates/cast/src/cmd/tip20.rs +++ b/crates/cast/src/cmd/tip20.rs @@ -145,7 +145,7 @@ impl Tip20TxOpts { } impl Tip20Subcommand { - fn rpc_opts(&self) -> &RpcOpts { + const fn rpc_opts(&self) -> &RpcOpts { match self { Self::Create { send_tx, .. } => &send_tx.eth.rpc, } diff --git a/crates/cast/src/cmd/wallet/mod.rs b/crates/cast/src/cmd/wallet/mod.rs index 9381454f9b57b..8e0dd8dd3ed8c 100644 --- a/crates/cast/src/cmd/wallet/mod.rs +++ b/crates/cast/src/cmd/wallet/mod.rs @@ -313,7 +313,7 @@ impl WalletSubcommands { Self::New { path, account_name, unsafe_password, number, password, force } => { let mut rng = thread_rng(); - let mut json_values = if shell::is_json() { Some(vec![]) } else { None }; + let mut json_values = shell::is_json().then(std::vec::Vec::new); let path = if let Some(path) = path { match dunce::canonicalize(&path) { diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 2cc51a04d1ee9..ce5572acebc13 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -93,7 +93,7 @@ impl + Clone + Unpin, N: Network> Cast { /// # Ok(()) /// # } /// ``` - pub fn new(provider: P) -> Self { + pub const fn new(provider: P) -> Self { Self { provider, _phantom: PhantomData } } @@ -155,11 +155,9 @@ impl + Clone + Unpin, N: Network> Cast { } let res = call.await?; - let mut decoded = vec![]; - - if let Some(func) = func { + let decoded = if let Some(func) = func { // decode args into tokens - decoded = match func.abi_decode_output(res.as_ref()) { + match func.abi_decode_output(res.as_ref()) { Ok(decoded) => decoded, Err(err) => { // ensure the address is a contract @@ -185,8 +183,10 @@ impl + Clone + Unpin, N: Network> Cast { "could not decode output; did you specify the wrong function return data type?" ); } - }; - } + } + } else { + vec![] + }; // handle case when return type is not specified Ok(if decoded.is_empty() { diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 42c6fe843e921..50c142000c14c 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -171,7 +171,7 @@ where N::ReceiptResponse: UIfmt + UIfmtReceiptExt, { /// Creates a new Cast instance responsible for sending transactions. - pub fn new(provider: P) -> Self { + pub const fn new(provider: P) -> Self { Self { provider, _phantom: PhantomData } } @@ -484,6 +484,11 @@ where let sender = sender.into(); self.prepare(&sender); + // For batch transactions with calls, clear `to` and `value` so the node correctly + // identifies this as an AA batch transaction. The `calls` field determines the actual + // targets. If `to` is set, `build_aa()` would add a spurious extra call. + self.tx.clear_batch_to(); + // resolve let tx_nonce = self.resolve_nonce(sender.address(), fill).await?; self.resolve_auth(&sender, tx_nonce).await?; @@ -663,7 +668,7 @@ where /// Skips gas, fee and nonce filling. Use for read-only calls /// (eth_call, eth_estimateGas, eth_createAccessList). - pub fn raw(mut self) -> Self { + pub const fn raw(mut self) -> Self { self.fill = false; self } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 5b44e0a89e4a9..1267d7dcc4397 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -3104,6 +3104,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { @@ -3841,28 +3869,28 @@ casttest!(flaky_osaka_can_run_p256_precompile, |_prj, cmd| { .assert_success() .stdout_eq(str![[r#" Traces: - [91537] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::execute(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a12384c5e52fd646e7bc7f6b3b33a605651f566e000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f0000000000000000000000000000000000000000000000000000000000036cd000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000327a25ad5cfe5c4d4339c1a4267d4a83e8c93312000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000b55b053230e4effb6609de652fca73fd1c2980400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000221000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) + [..] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::execute(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a12384c5e52fd646e7bc7f6b3b33a605651f566e000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f0000000000000000000000000000000000000000000000000000000000036cd000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000327a25ad5cfe5c4d4339c1a4267d4a83e8c93312000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000005a00000000000000000000000000b55b053230e4effb6609de652fca73fd1c2980400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000221000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) ├─ [2241] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::fallback(00) [staticcall] │ └─ ← [Return] 0x0000000000000000000000000b55b053230e4effb6609de652fca73fd1c29804 ├─ [9750] 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913::balanceOf(0xA12384c5E52fD646E7BC7F6B3b33A605651F566E) [staticcall] │ ├─ [2553] 0x2Ce6311ddAE708829bc0784C967b7d77D19FD779::balanceOf(0xA12384c5E52fD646E7BC7F6B3b33A605651F566E) [delegatecall] │ │ └─ ← [Return] 62393 [6.239e4] │ └─ ← [Return] 62393 [6.239e4] - ├─ [65442] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::selfCallPayVerifyCall537021665() - │ ├─ [25070] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::unwrapAndValidateSignature(0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00) [staticcall] - │ │ ├─ [22067] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::unwrapAndValidateSignature(0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00) [delegatecall] + ├─ [..] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::[..]() + │ ├─ [..] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::unwrapAndValidateSignature(0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00) [staticcall] + │ │ ├─ [..] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::unwrapAndValidateSignature(0x290a4c4039f102eceba2147e1fcc46f994a46d1229faf43ffff26a058e7378ff, 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006cdd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f92500000000000000000000000000000000000000000000000000000000000000244242424242424242424242424242424242424242424242424242424242424242010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000827b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d0000000000000000000000000000000000000000000000000000000000001bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b00) [delegatecall] │ │ │ ├─ [2369] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::pauseFlag() [staticcall] │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000 │ │ │ ├─ [120] PRECOMPILES::sha256(0x7b226368616c6c656e6765223a224b51704d51446e7841757a726f68522d483878472d5a536b625249702d76515f5f5f4a714259357a655038222c2263726f73734f726967696e223a66616c73652c226f726967696e223a2268747470732f2f6974686163612e78797a222c2274797065223a22776562617574686e2e676574227d) [staticcall] │ │ │ │ └─ ← [Return] 0xc13089327d3c20c0ce35f2f058c423de29977e6950e406c095e366a8fabd463f │ │ │ ├─ [96] PRECOMPILES::sha256(0x424242424242424242424242424242424242424242424242424242424242424201000000c13089327d3c20c0ce35f2f058c423de29977e6950e406c095e366a8fabd463f) [staticcall] │ │ │ │ └─ ← [Return] 0xc544bd9a4ea526dda3a008f43c21b6f0be3031b1ff71832b9876915dc91deea0 - │ │ │ ├─ [6900] P256VERIFY::c544bd9a(4ea526dda3a008f43c21b6f0be3031b1ff71832b9876915dc91deea0dd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f925bf54fa13f88658092efa36c51b1e3c4db31d3afb92812fb852dac7cf9614bc479bf5da7241d9c4ab1b431b57ec3369587b4c831d7a564438990da053708c3289) [staticcall] + │ │ │ ├─ [..] P256VERIFY::c544bd9a(4ea526dda3a008f43c21b6f0be3031b1ff71832b9876915dc91deea0dd519280ec730727f07aa36550bde31a1d5f3097818f3425c2f083ed33a91f080fa2afac0071f6e1af9a0e9c09b851bf01e68bc8a1c1f89f686c48205762f925bf54fa13f88658092efa36c51b1e3c4db31d3afb92812fb852dac7cf9614bc479bf5da7241d9c4ab1b431b57ec3369587b4c831d7a564438990da053708c3289) [staticcall] │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001 │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000011bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000000000000000011bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b - │ ├─ [5994] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::checkAndIncrementNonce(23) - │ │ ├─ [5608] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::checkAndIncrementNonce(23) [delegatecall] + │ ├─ [..] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::checkAndIncrementNonce(23) + │ │ ├─ [..] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::checkAndIncrementNonce(23) [delegatecall] │ │ │ └─ ← [Stop] │ │ └─ ← [Return] │ ├─ [3250] 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913::balanceOf(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312) [staticcall] @@ -3882,9 +3910,9 @@ Traces: │ │ ├─ [553] 0x2Ce6311ddAE708829bc0784C967b7d77D19FD779::balanceOf(0x327a25aD5Cfe5c4D4339C1A4267D4a83E8c93312) [delegatecall] │ │ │ └─ ← [Return] 40090 [4.009e4] │ │ └─ ← [Return] 40090 [4.009e4] - │ ├─ [5675] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::selfCallExecutePay1395256087() - │ │ ├─ [4148] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::execute(0x0100000000007821000100000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b) - │ │ │ ├─ [3693] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::execute(0x0100000000007821000100000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b) [delegatecall] + │ ├─ [..] 0xc2FF493F28e894742b968A7DB5D3F21F0aD80C6c::[..]() + │ │ ├─ [..] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::execute(0x0100000000007821000100000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b) + │ │ │ ├─ [..] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::execute(0x0100000000007821000100000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201bde17b8de18819c9eb86cefc3920ddb5d3d4254de276e3d6e18dd2b399f732b) [delegatecall] │ │ │ │ ├─ [435] 0xA12384c5E52fD646E7BC7F6B3b33A605651F566E::fallback() │ │ │ │ │ ├─ [55] 0x0B55b053230E4EFFb6609de652fCa73Fd1C29804::fallback() [delegatecall] │ │ │ │ │ │ └─ ← [Stop] diff --git a/crates/cheatcodes/src/error.rs b/crates/cheatcodes/src/error.rs index fdc3260cc7638..fbdf2592891b8 100644 --- a/crates/cheatcodes/src/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -153,12 +153,12 @@ impl Error { } /// Returns the raw data of this error. - pub fn data(&self) -> &[u8] { + pub const fn data(&self) -> &[u8] { unsafe { &*self.data } } /// Returns `true` if this error is a human-readable string. - pub fn is_str(&self) -> bool { + pub const fn is_str(&self) -> bool { self.is_str } diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index 578728c4a4c41..0669dfa076822 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -26,7 +26,7 @@ pub struct Prank { impl Prank { /// Create a new prank. - pub fn new( + pub const fn new( prank_caller: Address, prank_origin: Address, new_caller: Address, @@ -49,7 +49,7 @@ impl Prank { /// Apply the prank by setting `used` to true if it is false /// Only returns self in the case it is updated (first application) - pub fn first_time_applied(&self) -> Option { + pub const fn first_time_applied(&self) -> Option { if self.used { None } else { Some(Self { used: true, ..*self }) } } } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 9f175c94d2f30..3f8bd91b0e8d9 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -549,7 +549,7 @@ fn get_artifact_code( // Try filtering by profile as well filtered.retain(|(id, _)| id.profile == running.profile); - if filtered.len() == 1 { Some(filtered[0]) } else { None } + (filtered.len() == 1).then(|| filtered[0]) }) .ok_or_else(|| fmt_err!("multiple matching artifacts found")), ) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 58d7c924622e9..8d86fb30f2431 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -304,12 +304,12 @@ pub struct GasMetering { impl GasMetering { /// Start the gas recording. - pub fn start(&mut self) { + pub const fn start(&mut self) { self.recording = true; } /// Stop the gas recording. - pub fn stop(&mut self) { + pub const fn stop(&mut self) { self.recording = false; } @@ -902,7 +902,7 @@ impl Cheatcodes { // Apply delegate call, `call.caller`` will not equal `prank.prank_caller` if prank.delegate_call && curr_depth == prank.depth - && let CallScheme::DelegateCall = call.scheme + && call.scheme == CallScheme::DelegateCall { call.target_address = prank.new_caller; call.caller = prank.new_caller; @@ -912,21 +912,23 @@ impl Cheatcodes { } if curr_depth >= prank.depth && call.caller == prank.prank_caller { - let mut prank_applied = false; - // At the target depth we set `msg.sender` - if curr_depth == prank.depth { + let prank_applied = if curr_depth == prank.depth { // Ensure new caller is loaded and touched let _ = journaled_account(ecx, prank.new_caller); call.caller = prank.new_caller; - prank_applied = true; - } + true + } else { + false + }; // At the target depth, or deeper, we set `tx.origin` - if let Some(new_origin) = prank.new_origin { + let prank_applied = if let Some(new_origin) = prank.new_origin { ecx.tx_mut().set_caller(new_origin); - prank_applied = true; - } + true + } else { + prank_applied + }; // If prank applied for first time, then update if prank_applied && let Some(applied_prank) = prank.first_time_applied() { @@ -1060,19 +1062,12 @@ impl Cheatcodes { if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { // Determine if account is "initialized," ie, it has a non-zero balance, a non-zero // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code - let initialized; - let old_balance; - let old_nonce; - - if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) { - initialized = acc.data.info.exists(); - old_balance = acc.data.info.balance; - old_nonce = acc.data.info.nonce; - } else { - initialized = false; - old_balance = U256::ZERO; - old_nonce = 0; - } + let (initialized, old_balance, old_nonce) = + if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) { + (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce) + } else { + (false, U256::ZERO, 0) + }; let kind = match call.scheme { CallScheme::Call => crate::Vm::AccountAccessKind::Call, @@ -1548,7 +1543,7 @@ impl Inspector> for Cheatcode }, }; - if count != expected.count { Some((expected, count)) } else { None } + (count != expected.count).then_some((expected, count)) }) .collect::>(); @@ -1743,21 +1738,23 @@ impl Inspector> for Cheatcode && curr_depth >= prank.depth && input.caller() == prank.prank_caller { - let mut prank_applied = false; - // At the target depth we set `msg.sender` - if curr_depth == prank.depth { + let prank_applied = if curr_depth == prank.depth { // Ensure new caller is loaded and touched let _ = journaled_account(ecx, prank.new_caller); input.set_caller(prank.new_caller); - prank_applied = true; - } + true + } else { + false + }; // At the target depth, or deeper, we set `tx.origin` - if let Some(new_origin) = prank.new_origin { + let prank_applied = if let Some(new_origin) = prank.new_origin { ecx.tx_mut().set_caller(new_origin); - prank_applied = true; - } + true + } else { + prank_applied + }; // If prank applied for first time, then update if prank_applied && let Some(applied_prank) = prank.first_time_applied() { @@ -2214,13 +2211,14 @@ impl Cheatcodes { // Try to include present value for informational purposes, otherwise assume // it's not set (zero value) - let mut present_value = U256::ZERO; // Try to load the account and the slot's present value - if ecx.journal_mut().load_account(address).is_ok() + let present_value = if ecx.journal_mut().load_account(address).is_ok() && let Some(previous) = ecx.sload(address, key) { - present_value = previous.data; - } + previous.data + } else { + U256::ZERO + }; let access = crate::Vm::StorageAccess { account: interpreter.input.target_address, slot: key.into(), @@ -2241,12 +2239,13 @@ impl Cheatcodes { let address = interpreter.input.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; - if ecx.journal_mut().load_account(address).is_ok() + let previous_value = if ecx.journal_mut().load_account(address).is_ok() && let Some(previous) = ecx.sload(address, key) { - previous_value = previous.data; - } + previous.data + } else { + U256::ZERO + }; let access = crate::Vm::StorageAccess { account: address, @@ -2272,18 +2271,12 @@ impl Cheatcodes { }; let address = Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0)))); - let initialized; - let balance; - let nonce; - if let Ok(acc) = ecx.journal_mut().load_account(address) { - initialized = acc.data.info.exists(); - balance = acc.data.info.balance; - nonce = acc.data.info.nonce; - } else { - initialized = false; - balance = U256::ZERO; - nonce = 0; - } + let (initialized, balance, nonce) = + if let Ok(acc) = ecx.journal_mut().load_account(address) { + (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce) + } else { + (false, U256::ZERO, 0) + }; let curr_depth = ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"); let account_access = crate::Vm::AccountAccess { @@ -2532,7 +2525,7 @@ fn disallowed_mem_write( } /// Returns true if the kind of account access is a call. -fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool { +const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool { matches!( kind, crate::Vm::AccountAccessKind::Call @@ -2602,7 +2595,7 @@ fn append_storage_access( } /// Returns the [`spec::Cheatcode`] definition for a given [`spec::CheatcodeDef`] implementor. -fn cheatcode_of(_: &T) -> &'static spec::Cheatcode<'static> { +const fn cheatcode_of(_: &T) -> &'static spec::Cheatcode<'static> { T::CHEATCODE } @@ -2610,11 +2603,11 @@ fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str { cheat.func.signature.split('(').next().unwrap() } -fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str { +const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str { cheat.func.id } -fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str { +const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str { cheat.func.signature } @@ -2676,7 +2669,7 @@ fn apply_dispatch( } /// Helper function to check if frame execution will exit. -fn will_exit(action: &InterpreterAction) -> bool { +const fn will_exit(action: &InterpreterAction) -> bool { match action { InterpreterAction::Return(result) => { result.result.is_ok_or_revert() || result.result.is_error() diff --git a/crates/cheatcodes/src/inspector/analysis.rs b/crates/cheatcodes/src/inspector/analysis.rs index 1e73fc3df783a..806caa50a2080 100644 --- a/crates/cheatcodes/src/inspector/analysis.rs +++ b/crates/cheatcodes/src/inspector/analysis.rs @@ -52,7 +52,7 @@ impl std::fmt::Debug for CheatcodeAnalysis { } impl CheatcodeAnalysis { - pub fn new(compiler: Arc) -> Self { + pub const fn new(compiler: Arc) -> Self { Self { compiler, struct_defs: OnceLock::new() } } @@ -80,7 +80,7 @@ struct StructDefinitionResolver<'gcx> { impl<'gcx> StructDefinitionResolver<'gcx> { /// Constructs a new generator. - pub fn new(gcx: Gcx<'gcx>) -> Self { + pub const fn new(gcx: Gcx<'gcx>) -> Self { Self { gcx, struct_defs: TypeDefMap::new() } } diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 24cfa5474a5ba..9af337e223eea 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -503,7 +503,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: pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> { - if !path.starts_with('$') { format!("${path}").into() } else { path.into() } + if path.starts_with('$') { path.into() } else { format!("${path}").into() } } /// Converts a JSON [`Value`] to a [`DynSolValue`] by trying to guess encoded type. For safer diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index db0399d78b0ec..437d45e6b41f7 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -29,7 +29,7 @@ enum AssertionKind { } impl AssertionKind { - fn inverse(self) -> Self { + const fn inverse(self) -> Self { match self { Self::Eq => Self::Ne, Self::Ne => Self::Eq, @@ -40,7 +40,7 @@ impl AssertionKind { } } - fn to_str(self) -> &'static str { + const fn to_str(self) -> &'static str { match self { Self::Eq => "==", Self::Ne => "!=", @@ -450,11 +450,11 @@ impl_assertions! { (assertApproxEqRelDecimal_2Call, assertApproxEqRelDecimal_3Call), } -fn assert_true(condition: bool) -> Result<(), ()> { +const fn assert_true(condition: bool) -> Result<(), ()> { if condition { Ok(()) } else { Err(()) } } -fn assert_false(condition: bool) -> Result<(), ()> { +const fn assert_false(condition: bool) -> Result<(), ()> { assert_true(!condition) } @@ -467,10 +467,10 @@ fn assert_eq<'a, T: PartialEq>(left: &'a T, right: &'a T) -> ComparisonResult<'a } fn assert_not_eq<'a, T: PartialEq>(left: &'a T, right: &'a T) -> ComparisonResult<'a, T> { - if left != right { - Ok(()) - } else { + if left == right { Err(ComparisonAssertionError { kind: AssertionKind::Ne, left, right }) + } else { + Ok(()) } } diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 851500e938c06..5ef5b46ff4919 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -155,7 +155,7 @@ impl From for CreateScheme { } impl CreateScheme { - pub fn eq(&self, create_scheme: Self) -> bool { + pub const fn eq(&self, create_scheme: Self) -> bool { matches!( (self, create_scheme), (Self::Create, Self::Create) | (Self::Create2, Self::Create2 { .. }) @@ -1244,10 +1244,10 @@ pub(crate) fn get_emit_mismatch_message( { let (expected_name, expected_value) = &expected_params[param_idx]; let (_actual_name, actual_value) = &actual_params[param_idx]; - let param_name = if !expected_name.is_empty() { - expected_name - } else { + let param_name = if expected_name.is_empty() { &format!("param{param_idx}") + } else { + expected_name }; return format!( "{param_name}: expected={expected_value}, got={actual_value}", diff --git a/crates/cheatcodes/src/test/revert_handlers.rs b/crates/cheatcodes/src/test/revert_handlers.rs index 3b0610ebf07a4..6ba333f63f402 100644 --- a/crates/cheatcodes/src/test/revert_handlers.rs +++ b/crates/cheatcodes/src/test/revert_handlers.rs @@ -198,7 +198,10 @@ pub(crate) fn handle_expect_revert( // If we expect no reverts with a specific reason/reverter, but got a revert, // we need to check if it matches our criteria - if !matches!(status, return_ok!()) { + if matches!(status, return_ok!()) { + // No revert occurred, which is what we expected + Ok(success_return()) + } else { // We got a revert, but we expected 0 reverts // We need to check if this revert matches our expected criteria @@ -266,9 +269,6 @@ pub(crate) fn handle_expect_revert( } } } - } else { - // No revert occurred, which is what we expected - Ok(success_return()) } } else { ensure!(!matches!(status, return_ok!()), "next call did not revert as expected"); diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 90e93f63999ad..fcaf6e9c6c3bb 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -225,11 +225,10 @@ impl Cheatcode for resumeTracingCall { impl Cheatcode for interceptInitcodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - if !state.intercept_next_create_call { - state.intercept_next_create_call = true; - } else { + if state.intercept_next_create_call { bail!("vm.interceptInitcode() has already been called") } + state.intercept_next_create_call = true; Ok(Default::default()) } } diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 411c599aad22b..b4bf1c738ee85 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -72,12 +72,12 @@ impl ChiselDispatcher { } /// Returns the [`SessionSource`]. - pub fn source(&self) -> &SessionSource { + pub const fn source(&self) -> &SessionSource { &self.session.source } /// Returns the [`SessionSource`]. - pub fn source_mut(&mut self) -> &mut SessionSource { + pub const fn source_mut(&mut self) -> &mut SessionSource { &mut self.session.source } @@ -512,7 +512,7 @@ fn preprocess(input: &str) -> (bool, Cow<'_, str>) { let mut only_trivia = true; let mut new_input = Cow::Borrowed(input); for (pos, token) in solar::parse::Cursor::new(input).with_position() { - use RawTokenKind::*; + use RawTokenKind::{BlockComment, LineComment, Literal, Whitespace}; if matches!(token.kind, Whitespace | LineComment { .. } | BlockComment { .. }) { continue; diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index f5deb369ff963..da2c7f4caff02 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -549,9 +549,8 @@ impl Type { pt::Expression::Multiply(_, lhs, rhs) | pt::Expression::Divide(_, lhs, rhs) => { match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) { - (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) | - (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) | - (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => { + (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_))) | +(Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => { Some(Self::Builtin(DynSolType::Int(256))) } _ => { @@ -822,7 +821,7 @@ impl Type { custom_type: &mut Vec, contract_name: Option, ) -> Result> { - if let Some("this") | Some("super") = custom_type.last().map(String::as_str) { + if let Some("this" | "super") = custom_type.last().map(String::as_str) { custom_type.pop(); } if custom_type.is_empty() { @@ -1081,12 +1080,12 @@ impl Type { match self { Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => { match inner.try_as_ethabi(intermediate) { - Some(DynSolType::Array(inner)) | Some(DynSolType::FixedArray(inner, _)) => { + Some(DynSolType::Array(inner) | DynSolType::FixedArray(inner, _)) => { Some(*inner) } - Some(DynSolType::Bytes) - | Some(DynSolType::String) - | Some(DynSolType::FixedBytes(_)) => Some(DynSolType::FixedBytes(1)), + Some(DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_)) => { + Some(DynSolType::FixedBytes(1)) + } ty => ty, } } @@ -1096,7 +1095,7 @@ impl Type { /// Returns whether this type is dynamic #[inline] - fn is_dynamic(&self) -> bool { + const fn is_dynamic(&self) -> bool { match self { // TODO: Note, this is not entirely correct. Fixed arrays of non-dynamic types are // not dynamic, nor are tuples of non-dynamic types. @@ -1108,23 +1107,22 @@ impl Type { /// Returns whether this type is an array #[inline] - fn is_array(&self) -> bool { + const fn is_array(&self) -> bool { matches!( self, Self::Array(_) | Self::FixedArray(_, _) - | Self::Builtin(DynSolType::Array(_)) - | Self::Builtin(DynSolType::FixedArray(_, _)) + | Self::Builtin(DynSolType::Array(_) | DynSolType::FixedArray(_, _)) ) } /// Returns whether this type is a dynamic array (can call push, pop) #[inline] - fn is_dynamic_array(&self) -> bool { + const fn is_dynamic_array(&self) -> bool { matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_))) } - fn is_fixed_bytes(&self) -> bool { + const fn is_fixed_bytes(&self) -> bool { matches!(self, Self::Builtin(DynSolType::FixedBytes(_))) } } @@ -1143,7 +1141,7 @@ fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option _ => None, }); match vis { - Some(pt::Visibility::External(_)) | Some(pt::Visibility::Public(_)) => { + Some(pt::Visibility::External(_) | pt::Visibility::Public(_)) => { match custom_type.first().unwrap().as_str() { "address" => Some(DynSolType::Address), "selector" => Some(DynSolType::FixedBytes(4)), @@ -1607,7 +1605,7 @@ mod tests { T: AsRef + std::fmt::Display + 'a, I: IntoIterator + 'a, { - for (input, expected) in input.into_iter() { + for (input, expected) in input { let input = input.as_ref(); let ty = get_type_ethabi(s, input, true); assert_eq!(ty.as_ref(), Some(expected), "\n{input}"); diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index 783f48031c2f0..1b2a530378701 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -62,7 +62,7 @@ impl ChiselRunner { /// ### Returns /// /// A new [ChiselRunner] - pub fn new( + pub const fn new( executor: Executor, initial_balance: U256, sender: Address, diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index 1002b5533f116..c3212f8e89962 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -147,8 +147,8 @@ impl SolidityHelper { /// Validate that a source snippet is closed (i.e., all braces and parenthesis are matched). fn validate_closed(&self, input: &str) -> ValidationResult { - use RawLiteralKind::*; - use RawTokenKind::*; + use RawLiteralKind::Str; + use RawTokenKind::{BlockComment, CloseDelim, Literal, OpenDelim}; let mut stack = vec![]; for token in Cursor::new(input) { match token.kind { @@ -282,7 +282,10 @@ impl Helper for SolidityHelper {} fn token_style(token: &Token) -> Style { use solar::parse::{ interface::kw::*, - token::{TokenKind::*, TokenLitKind::*}, + token::{ + TokenKind::{Arrow, Comment, FatArrow, Ident, Literal}, + TokenLitKind::{HexStr, Str, UnicodeStr}, + }, }; match token.kind { diff --git a/crates/chisel/tests/it/repl/session.rs b/crates/chisel/tests/it/repl/session.rs index 92de13a012fd8..5076b00996ae7 100644 --- a/crates/chisel/tests/it/repl/session.rs +++ b/crates/chisel/tests/it/repl/session.rs @@ -68,7 +68,7 @@ impl ChiselSession { &self.project } - pub fn is_repl(&self) -> bool { + pub const fn is_repl(&self) -> bool { self.is_repl } diff --git a/crates/cli-markdown/src/lib.rs b/crates/cli-markdown/src/lib.rs index 732e22bfd193d..7702fcf15b9a7 100644 --- a/crates/cli-markdown/src/lib.rs +++ b/crates/cli-markdown/src/lib.rs @@ -24,7 +24,7 @@ pub struct MarkdownOptions { impl MarkdownOptions { /// Construct a default instance of `MarkdownOptions`. - pub fn new() -> Self { + pub const fn new() -> Self { Self { title: None, show_footer: true, show_table_of_contents: true, show_aliases: true } } @@ -35,19 +35,19 @@ impl MarkdownOptions { } /// Whether to show the default footer advertising `clap-markdown`. - pub fn show_footer(mut self, show: bool) -> Self { + pub const fn show_footer(mut self, show: bool) -> Self { self.show_footer = show; self } /// Whether to show the default table of contents. - pub fn show_table_of_contents(mut self, show: bool) -> Self { + pub const fn show_table_of_contents(mut self, show: bool) -> Self { self.show_table_of_contents = show; self } /// Whether to show aliases for arguments and commands. - pub fn show_aliases(mut self, show: bool) -> Self { + pub const fn show_aliases(mut self, show: bool) -> Self { self.show_aliases = show; self } @@ -456,7 +456,7 @@ fn get_alias_string(aliases: &[&str]) -> Option { Some(aliases.iter().map(|alias| format!("`{alias}`")).collect::>().join(", ")) } -fn pluralize<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str { +const fn pluralize<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str { if count == 1 { singular } else { plural } } diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 172ad81d4858b..79b15381607aa 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -18,6 +18,7 @@ foundry-cli-markdown.workspace = true foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true +foundry-evm-networks.workspace = true foundry-wallets.workspace = true foundry-compilers.workspace = true diff --git a/crates/cli/src/opts/evm.rs b/crates/cli/src/opts/evm.rs index 7af7c20fb021a..f818878b897e1 100644 --- a/crates/cli/src/opts/evm.rs +++ b/crates/cli/src/opts/evm.rs @@ -10,8 +10,10 @@ use foundry_config::{ value::{Dict, Map, Value}, }, }; +use foundry_evm_networks::NetworkConfigs; use serde::Serialize; +use crate::opts::RpcCommonOpts; use foundry_common::shell; /// `EvmArgs` and `EnvArgs` take the highest precedence in the Config/Figment hierarchy. @@ -39,31 +41,29 @@ use foundry_common::shell; #[derive(Clone, Debug, Default, Serialize, Parser)] #[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. - #[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, + /// Common RPC options (URL, timeout, rate limiting, etc.). + #[command(flatten)] + #[serde(flatten)] + pub rpc: RpcCommonOpts, /// Fetch state from a specific block number over a remote endpoint. /// - /// See --fork-url. - #[arg(long, requires = "fork_url", value_name = "BLOCK")] + /// See --rpc-url. + #[arg(long, requires = "rpc_url", value_name = "BLOCK")] #[serde(skip_serializing_if = "Option::is_none")] pub fork_block_number: Option, /// Number of retries. /// - /// See --fork-url. - #[arg(long, requires = "fork_url", value_name = "RETRIES")] + /// See --rpc-url. + #[arg(long, requires = "rpc_url", value_name = "RETRIES")] #[serde(skip_serializing_if = "Option::is_none")] pub fork_retries: Option, /// Initial retry backoff on encountering errors. /// - /// See --fork-url. - #[arg(long, requires = "fork_url", value_name = "BACKOFF")] + /// See --rpc-url. + #[arg(long, requires = "rpc_url", value_name = "BACKOFF")] #[serde(skip_serializing_if = "Option::is_none")] pub fork_retry_backoff: Option, @@ -73,7 +73,7 @@ pub struct EvmArgs { /// /// This flag overrides the project's configuration file. /// - /// See --fork-url. + /// See --rpc-url. #[arg(long)] #[serde(skip)] pub no_storage_caching: bool, @@ -108,27 +108,6 @@ pub struct EvmArgs { #[serde(skip_serializing_if = "Option::is_none")] pub create2_deployer: Option
, - /// Sets the number of assumed available compute units per second for this provider - /// - /// default value: 330 - /// - /// See also --fork-url and - #[arg(long, alias = "cups", value_name = "CUPS", help_heading = "Fork config")] - #[serde(skip_serializing_if = "Option::is_none")] - pub compute_units_per_second: Option, - - /// Disables rate limiting for this node's provider. - /// - /// See also --fork-url and - #[arg( - long, - value_name = "NO_RATE_LIMITS", - help_heading = "Fork config", - visible_alias = "no-rate-limit" - )] - #[serde(skip)] - pub no_rpc_rate_limit: bool, - /// All ethereum environment related arguments #[command(flatten)] #[serde(flatten)] @@ -140,6 +119,11 @@ pub struct EvmArgs { #[arg(long)] #[serde(skip)] pub isolate: bool, + + /// Network selection. + #[command(flatten)] + #[serde(skip)] + pub networks: NetworkConfigs, } // Make this set of options a `figment::Provider` so that it can be merged into the `Config` @@ -181,8 +165,27 @@ impl Provider for EvmArgs { dict.insert("no_storage_caching".to_string(), self.no_storage_caching.into()); } - if self.no_rpc_rate_limit { - dict.insert("no_rpc_rate_limit".to_string(), self.no_rpc_rate_limit.into()); + // Merge serde-skipped fields from the common RPC options. + if self.rpc.no_rpc_rate_limit { + dict.insert("no_rpc_rate_limit".to_string(), true.into()); + } + if self.rpc.accept_invalid_certs { + dict.insert("eth_rpc_accept_invalid_certs".to_string(), true.into()); + } + if self.rpc.no_proxy { + dict.insert("eth_rpc_no_proxy".to_string(), true.into()); + } + + // Only insert network flags when explicitly set via CLI to avoid overriding + // values from foundry.toml (NetworkConfigs is flattened in Config). + if self.networks.is_tempo() { + dict.insert("tempo".to_string(), true.into()); + } + if self.networks.is_optimism() { + dict.insert("optimism".to_string(), true.into()); + } + if self.networks.is_celo() { + dict.insert("celo".to_string(), true.into()); } Ok(Map::from([(Config::selected_profile(), dict)])) @@ -297,7 +300,10 @@ mod tests { #[test] fn compute_units_per_second_present_when_some() { - let args = EvmArgs { compute_units_per_second: Some(1000), ..Default::default() }; + let args = EvmArgs { + rpc: RpcCommonOpts { compute_units_per_second: Some(1000), ..Default::default() }, + ..Default::default() + }; let data = args.data().expect("provider data"); let dict = data.get(&Config::selected_profile()).expect("profile dict"); let val = dict.get("compute_units_per_second").expect("cups present"); diff --git a/crates/cli/src/opts/mod.rs b/crates/cli/src/opts/mod.rs index ce1234528a94b..fc619f482d2fb 100644 --- a/crates/cli/src/opts/mod.rs +++ b/crates/cli/src/opts/mod.rs @@ -5,6 +5,7 @@ mod evm; mod global; mod network; mod rpc; +mod rpc_common; mod tempo; mod transaction; @@ -15,5 +16,6 @@ pub use evm::*; pub use global::*; pub use network::*; pub use rpc::*; +pub use rpc_common::*; pub use tempo::*; pub use transaction::*; diff --git a/crates/cli/src/opts/rpc.rs b/crates/cli/src/opts/rpc.rs index 15a5de678272a..3467ccc13f697 100644 --- a/crates/cli/src/opts/rpc.rs +++ b/crates/cli/src/opts/rpc.rs @@ -1,4 +1,4 @@ -use crate::opts::ChainValueParser; +use crate::opts::{ChainValueParser, RpcCommonOpts}; use alloy_chains::ChainKind; use clap::Parser; use eyre::Result; @@ -19,24 +19,9 @@ const FLASHBOTS_URL: &str = "https://rpc.flashbots.net/fast"; #[derive(Clone, Debug, Default, Parser)] #[command(next_help_heading = "Rpc options")] pub struct RpcOpts { - /// The RPC endpoint, default value is http://localhost:8545. - #[arg(short = 'r', long = "rpc-url", env = "ETH_RPC_URL")] - pub url: Option, - - /// Allow insecure RPC connections (accept invalid HTTPS certificates). - /// - /// When the provider's inner runtime transport variant is HTTP, this configures the reqwest - /// client to accept invalid certificates. - #[arg(short = 'k', long = "insecure", default_value = "false")] - pub accept_invalid_certs: bool, - - /// Disable automatic proxy detection. - /// - /// Use this in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) where - /// system proxy detection causes crashes. When enabled, HTTP_PROXY/HTTPS_PROXY environment - /// variables and system proxy settings will be ignored. - #[arg(long = "no-proxy", alias = "disable-proxy", default_value = "false")] - pub no_proxy: bool, + /// Common RPC options (URL, timeout, rate limiting, etc.). + #[command(flatten)] + pub common: RpcCommonOpts, /// Use the Flashbots RPC URL with fast mode (). /// @@ -58,14 +43,6 @@ pub struct RpcOpts { #[arg(long, env = "ETH_RPC_JWT_SECRET")] pub jwt_secret: Option, - /// Timeout for the RPC request in seconds. - /// - /// The specified timeout will be used to override the default timeout for RPC requests. - /// - /// Default value: 45 - #[arg(long, env = "ETH_RPC_TIMEOUT")] - pub rpc_timeout: Option, - /// Specify custom headers for RPC requests. #[arg(long, alias = "headers", env = "ETH_RPC_HEADERS", value_delimiter(','))] pub rpc_headers: Option>, @@ -90,13 +67,11 @@ impl figment::Provider for RpcOpts { impl RpcOpts { /// Returns the RPC endpoint. pub fn url<'a>(&'a self, config: Option<&'a Config>) -> Result>> { - let url = match (self.flashbots, self.url.as_deref(), config) { - (true, ..) => Some(Cow::Borrowed(FLASHBOTS_URL)), - (false, Some(url), _) => Some(Cow::Borrowed(url)), - (false, None, Some(config)) => config.get_rpc_url().transpose()?, - (false, None, None) => None, - }; - Ok(url) + if self.flashbots { + Ok(Some(Cow::Borrowed(FLASHBOTS_URL))) + } else { + self.common.url(config) + } } /// Returns the JWT secret. @@ -110,25 +85,16 @@ impl RpcOpts { } pub fn dict(&self) -> Dict { - let mut dict = Dict::new(); - if let Ok(Some(url)) = self.url(None) { - dict.insert("eth_rpc_url".into(), url.into_owned().into()); + let mut dict = self.common.dict(); + if self.flashbots { + dict.insert("eth_rpc_url".into(), FLASHBOTS_URL.into()); } if let Ok(Some(jwt)) = self.jwt(None) { dict.insert("eth_rpc_jwt".into(), jwt.into_owned().into()); } - if let Some(rpc_timeout) = self.rpc_timeout { - dict.insert("eth_rpc_timeout".into(), rpc_timeout.into()); - } if let Some(headers) = &self.rpc_headers { dict.insert("eth_rpc_headers".into(), headers.clone().into()); } - if self.accept_invalid_certs { - dict.insert("eth_rpc_accept_invalid_certs".into(), true.into()); - } - if self.no_proxy { - dict.insert("eth_rpc_no_proxy".into(), true.into()); - } if self.curl { dict.insert("eth_rpc_curl".into(), true.into()); } diff --git a/crates/cli/src/opts/rpc_common.rs b/crates/cli/src/opts/rpc_common.rs new file mode 100644 index 0000000000000..05b98582fa88f --- /dev/null +++ b/crates/cli/src/opts/rpc_common.rs @@ -0,0 +1,114 @@ +//! Common RPC options shared between `RpcOpts` and `EvmArgs`. + +use clap::Parser; +use eyre::Result; +use foundry_config::{ + Config, + figment::{ + self, Metadata, Profile, + value::{Dict, Map}, + }, +}; +use serde::Serialize; +use std::borrow::Cow; + +/// Common RPC-related options shared across CLI commands. +/// +/// This struct holds fields that both [`super::RpcOpts`] (cast) and +/// [`super::EvmArgs`] (forge/script) need, eliminating duplication and +/// making the two structs composable. +#[derive(Clone, Debug, Default, Serialize, Parser)] +pub struct RpcCommonOpts { + /// The RPC endpoint. + #[arg(short, long, visible_alias = "fork-url", env = "ETH_RPC_URL")] + #[serde(rename = "eth_rpc_url", skip_serializing_if = "Option::is_none")] + pub rpc_url: Option, + + /// Allow insecure RPC connections (accept invalid HTTPS certificates). + /// + /// When the provider's inner runtime transport variant is HTTP, this configures the reqwest + /// client to accept invalid certificates. + #[arg(short = 'k', long = "insecure", default_value = "false")] + #[serde(skip)] + pub accept_invalid_certs: bool, + + /// Timeout for the RPC request in seconds. + /// + /// The specified timeout will be used to override the default timeout for RPC requests. + /// + /// Default value: 45 + #[arg(long, env = "ETH_RPC_TIMEOUT")] + #[serde(rename = "eth_rpc_timeout", skip_serializing_if = "Option::is_none")] + pub rpc_timeout: Option, + + /// Disable automatic proxy detection. + /// + /// Use this in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) where + /// system proxy detection causes crashes. When enabled, HTTP_PROXY/HTTPS_PROXY environment + /// variables and system proxy settings will be ignored. + #[arg(long = "no-proxy", alias = "disable-proxy", default_value = "false")] + #[serde(skip)] + pub no_proxy: bool, + + /// Sets the number of assumed available compute units per second for this provider. + /// + /// default value: 330 + /// + /// See also + #[arg(long, alias = "cups", value_name = "CUPS")] + #[serde(skip_serializing_if = "Option::is_none")] + pub compute_units_per_second: Option, + + /// Disables rate limiting for this node's provider. + /// + /// See also + #[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rate-limit")] + #[serde(skip)] + pub no_rpc_rate_limit: bool, +} + +impl figment::Provider for RpcCommonOpts { + fn metadata(&self) -> Metadata { + Metadata::named("RpcCommonOpts") + } + + fn data(&self) -> Result, figment::Error> { + Ok(Map::from([(Config::selected_profile(), self.dict())])) + } +} + +impl RpcCommonOpts { + /// Returns the RPC endpoint URL, resolving from CLI args or config. + pub fn url<'a>(&'a self, config: Option<&'a Config>) -> Result>> { + let url = match (self.rpc_url.as_deref(), config) { + (Some(url), _) => Some(Cow::Borrowed(url)), + (None, Some(config)) => config.get_rpc_url().transpose()?, + (None, None) => None, + }; + Ok(url) + } + + /// Builds a figment-compatible dictionary from these options. + pub fn dict(&self) -> Dict { + let mut dict = Dict::new(); + if let Ok(Some(url)) = self.url(None) { + dict.insert("eth_rpc_url".into(), url.into_owned().into()); + } + if let Some(rpc_timeout) = self.rpc_timeout { + dict.insert("eth_rpc_timeout".into(), rpc_timeout.into()); + } + if self.accept_invalid_certs { + dict.insert("eth_rpc_accept_invalid_certs".into(), true.into()); + } + if self.no_proxy { + dict.insert("eth_rpc_no_proxy".into(), true.into()); + } + if let Some(cups) = self.compute_units_per_second { + dict.insert("compute_units_per_second".into(), cups.into()); + } + if self.no_rpc_rate_limit { + dict.insert("no_rpc_rate_limit".into(), true.into()); + } + dict + } +} diff --git a/crates/cli/src/opts/tempo.rs b/crates/cli/src/opts/tempo.rs index a071e6774c21a..5220b7ee80fe1 100644 --- a/crates/cli/src/opts/tempo.rs +++ b/crates/cli/src/opts/tempo.rs @@ -76,7 +76,7 @@ pub struct TempoOpts { impl TempoOpts { /// Returns `true` if any Tempo-specific option is set. - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { self.fee_token.is_some() || self.nonce_key.is_some() || self.sponsor_signature.is_some() diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index eb4cf477d39f9..f88346949d1b6 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -112,7 +112,12 @@ pub fn init_progress(len: u64, label: &str) -> indicatif::ProgressBar { /// True if the network calculates gas costs differently. pub fn has_different_gas_calc(chain_id: u64) -> bool { - if let Some(chain) = Chain::from(chain_id).named() { + let chain = Chain::from(chain_id); + // Is either Tempo | TempoModerato | TempoTestnet | TempoDevnet + if chain.is_tempo() || chain.id() == 31318 { + return true; + } + if let Some(chain) = chain.named() { return chain.is_arbitrum() || chain.is_elastic() || matches!( diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index bfd722a5ac104..f1c6c435217b7 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -373,16 +373,16 @@ impl<'a> Git<'a> { .map(drop) } - pub fn root(self, root: &Path) -> Git<'_> { + pub const fn root(self, root: &Path) -> Git<'_> { Git { root, ..self } } - pub fn quiet(self, quiet: bool) -> Self { + pub const fn quiet(self, quiet: bool) -> Self { Self { quiet, ..self } } /// True to perform shallow clones - pub fn shallow(self, shallow: bool) -> Self { + pub const fn shallow(self, shallow: bool) -> Self { Self { shallow, ..self } } @@ -738,7 +738,7 @@ pub struct Submodule { } impl Submodule { - pub fn new(rev: String, path: PathBuf) -> Self { + pub const fn new(rev: String, path: PathBuf) -> Self { Self { rev, path } } @@ -746,7 +746,7 @@ impl Submodule { &self.rev } - pub fn path(&self) -> &PathBuf { + pub const fn path(&self) -> &PathBuf { &self.path } } @@ -771,11 +771,11 @@ impl FromStr for Submodule { pub struct Submodules(pub Vec); impl Submodules { - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.0.len() } - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.0.is_empty() } } @@ -857,10 +857,10 @@ mod tests { let mut cwd_file = File::create(cwd_env).unwrap(); let mut prj_file = File::create(nested.join(".env")).unwrap(); - cwd_file.write_all("TESTCWDKEY=cwd_val".as_bytes()).unwrap(); + cwd_file.write_all(b"TESTCWDKEY=cwd_val").unwrap(); cwd_file.sync_all().unwrap(); - prj_file.write_all("TESTPRJKEY=prj_val".as_bytes()).unwrap(); + prj_file.write_all(b"TESTPRJKEY=prj_val").unwrap(); prj_file.sync_all().unwrap(); let cwd = env::current_dir().unwrap(); diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..8f6d7f3cde092 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -16,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index 43c2a4621528b..d751ddba245ba 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -222,10 +222,10 @@ impl ConsoleFmt for U256 { let integer = amount / exp10; let decimal = (amount % exp10).to_string(); let decimal = format!("{decimal:0>log$}").trim_end_matches('0').to_string(); - if !decimal.is_empty() { - format!("{integer}.{decimal}e{log}") - } else { + if decimal.is_empty() { format!("{integer}e{log}") + } else { + format!("{integer}.{decimal}e{log}") } } FormatSpec::Exponential(Some(precision)) => { @@ -234,10 +234,10 @@ impl ConsoleFmt for U256 { let integer = amount / exp10; let decimal = (amount % exp10).to_string(); let decimal = format!("{decimal:0>precision$}").trim_end_matches('0').to_string(); - if !decimal.is_empty() { - format!("{integer}.{decimal}") - } else { + if decimal.is_empty() { format!("{integer}") + } else { + format!("{integer}.{decimal}") } } } @@ -266,10 +266,10 @@ impl ConsoleFmt for I256 { 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(); - if !decimal.is_empty() { - format!("{sign}{integer}.{decimal}e{log}") - } else { + if decimal.is_empty() { format!("{sign}{integer}e{log}") + } else { + format!("{sign}{integer}.{decimal}e{log}") } } FormatSpec::Exponential(Some(precision)) => { @@ -279,10 +279,10 @@ impl ConsoleFmt for I256 { let integer = (amount / exp10).twos_complement(); let decimal = (amount % exp10).twos_complement().to_string(); let decimal = format!("{decimal:0>precision$}").trim_end_matches('0').to_string(); - if !decimal.is_empty() { - format!("{sign}{integer}.{decimal}") - } else { + if decimal.is_empty() { format!("{sign}{integer}") + } else { + format!("{sign}{integer}.{decimal}") } } } diff --git a/crates/common/fmt/src/dynamic.rs b/crates/common/fmt/src/dynamic.rs index 6388871c15c24..1882d7b3d49b0 100644 --- a/crates/common/fmt/src/dynamic.rs +++ b/crates/common/fmt/src/dynamic.rs @@ -107,7 +107,7 @@ struct DynValueDisplay<'a> { impl<'a> DynValueDisplay<'a> { /// Creates a new [`Display`](fmt::Display) wrapper for the given value. - fn new(value: &'a DynSolValue, raw: bool) -> Self { + const fn new(value: &'a DynSolValue, raw: bool) -> Self { Self { value, formatter: DynValueFormatter { raw } } } } @@ -242,7 +242,7 @@ impl From for StructDefinitions { } impl StructDefinitions { - pub fn new(map: TypeDefMap) -> Self { + pub const fn new(map: TypeDefMap) -> Self { Self(map) } diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index 12948f896421c..3ab26593766e3 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -59,7 +59,9 @@ impl UIfmt for Option { impl UIfmt for [T] { fn pretty(&self) -> String { - if !self.is_empty() { + if self.is_empty() { + "[]".to_string() + } else { let mut s = String::with_capacity(self.len() * 64); s.push_str("[\n"); for item in self { @@ -71,8 +73,6 @@ impl UIfmt for [T] { } s.push(']'); s - } else { - "[]".to_string() } } } diff --git a/crates/common/src/comments/comment.rs b/crates/common/src/comments/comment.rs index 40b9452cb4aaf..c19ade41ce9c8 100644 --- a/crates/common/src/comments/comment.rs +++ b/crates/common/src/comments/comment.rs @@ -5,7 +5,7 @@ use solar::parse::{ interface::BytePos, }; -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum CommentStyle { /// No code on either side of each line of the comment Isolated, @@ -18,16 +18,16 @@ pub enum CommentStyle { } impl CommentStyle { - pub fn is_mixed(&self) -> bool { + pub const fn is_mixed(&self) -> bool { matches!(self, Self::Mixed) } - pub fn is_trailing(&self) -> bool { + pub const fn is_trailing(&self) -> bool { matches!(self, Self::Trailing) } - pub fn is_isolated(&self) -> bool { + pub const fn is_isolated(&self) -> bool { matches!(self, Self::Isolated) } - pub fn is_blank(&self) -> bool { + pub const fn is_blank(&self) -> bool { matches!(self, Self::BlankLine) } } @@ -46,7 +46,7 @@ impl Comment { self.span.lo() } - pub fn prefix(&self) -> Option<&'static str> { + pub const fn prefix(&self) -> Option<&'static str> { if self.lines.is_empty() { return None; } @@ -58,7 +58,7 @@ impl Comment { }) } - pub fn suffix(&self) -> Option<&'static str> { + pub const fn suffix(&self) -> Option<&'static str> { if self.lines.is_empty() { return None; } diff --git a/crates/common/src/comments/inline_config.rs b/crates/common/src/comments/inline_config.rs index 16ffd6993bfef..cf736d1ad1f33 100644 --- a/crates/common/src/comments/inline_config.rs +++ b/crates/common/src/comments/inline_config.rs @@ -46,7 +46,7 @@ impl InlineConfigItem> { vec!["all".to_string()] } else { match relevant.split_once(')') { - Some((id_str, _)) => id_str.split(",").map(|s| s.trim().to_string()).collect(), + Some((id_str, _)) => id_str.split(',').map(|s| s.trim().to_string()).collect(), None => return Err(InvalidInlineConfigItem::Syntax(s.into())), } }; @@ -331,7 +331,7 @@ struct NextItemFinder { } impl NextItemFinder { - fn new(offset: BytePos) -> Self { + const fn new(offset: BytePos) -> Self { Self { offset } } diff --git a/crates/common/src/comments/mod.rs b/crates/common/src/comments/mod.rs index a3e4a8f3ec743..812590b126e71 100644 --- a/crates/common/src/comments/mod.rs +++ b/crates/common/src/comments/mod.rs @@ -94,7 +94,7 @@ impl Comments { // Stop when we find a trailing or a non-mixed comment match cmnt.style { - CommentStyle::Mixed => continue, + CommentStyle::Mixed => {} CommentStyle::Trailing => return Some((cmnt, i)), _ => break, } @@ -317,9 +317,7 @@ impl<'ast> CommentGatherer<'ast> { res.push(line); continue; } - if !pos.is_last { - res.push(format_doc_block_comment(&line, self.tab_width)); - } else { + if pos.is_last { // Ensure last line of a doc comment only has the `*/` decorator if let Some((first, _)) = line.split_once("*/") && !first.trim().is_empty() @@ -327,6 +325,8 @@ impl<'ast> CommentGatherer<'ast> { res.push(format_doc_block_comment(first.trim_end(), self.tab_width)); } res.push(" */".to_string()); + } else { + res.push(format_doc_block_comment(&line, self.tab_width)); } } res @@ -380,7 +380,7 @@ fn format_doc_block_comment(line: &str, tab_width: Option) -> String { return (" *").to_string(); } - if let Some((_, rest_of_line)) = line.split_once("*") { + if let Some((_, rest_of_line)) = line.split_once('*') { if rest_of_line.is_empty() { (" *").to_string() } else if let Some(tab_width) = tab_width { diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 5078fc10f7f41..03bebb0583df6 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -89,14 +89,14 @@ impl ProjectCompiler { /// Sets whether to print contract names. #[inline] - pub fn print_names(mut self, yes: bool) -> Self { + pub const fn print_names(mut self, yes: bool) -> Self { self.print_names = Some(yes); self } /// Sets whether to print contract sizes. #[inline] - pub fn print_sizes(mut self, yes: bool) -> Self { + pub const fn print_sizes(mut self, yes: bool) -> Self { self.print_sizes = Some(yes); self } @@ -104,21 +104,21 @@ impl ProjectCompiler { /// Sets whether to print anything at all. Overrides other `print` options. #[inline] #[doc(alias = "silent")] - pub fn quiet(mut self, yes: bool) -> Self { + pub const fn quiet(mut self, yes: bool) -> Self { self.quiet = Some(yes); self } /// Sets whether to bail on compiler errors. #[inline] - pub fn bail(mut self, yes: bool) -> Self { + pub const fn bail(mut self, yes: bool) -> Self { self.bail = Some(yes); self } /// Sets whether to ignore EIP-3860 initcode size limits. #[inline] - pub fn ignore_eip_3860(mut self, yes: bool) -> Self { + pub const fn ignore_eip_3860(mut self, yes: bool) -> Self { self.ignore_eip_3860 = yes; self } @@ -132,7 +132,7 @@ impl ProjectCompiler { /// Sets if tests should be dynamically linked. #[inline] - pub fn dynamic_test_linking(mut self, preprocess: bool) -> Self { + pub const fn dynamic_test_linking(mut self, preprocess: bool) -> Self { self.dynamic_test_linking = preprocess; self } @@ -163,10 +163,10 @@ impl ProjectCompiler { let files = std::mem::take(&mut self.files); let preprocess = self.dynamic_test_linking; self.compile_with(|| { - let sources = if !files.is_empty() { - Source::read_all(files)? - } else { + let sources = if files.is_empty() { project.paths.read_input_files()? + } else { + Source::read_all(files)? }; let mut compiler = diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 06b2c29945417..e9f203ebf7539 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) .map(|(_, data)| data) } @@ -484,7 +484,7 @@ pub fn bytecode_diff_score<'a>(mut a: &'a [u8], mut b: &'a [u8]) -> f64 { /// # Safety /// /// `a` must be at least as long as `b`. -unsafe fn count_different_bytes(a: &[u8], b: &[u8]) -> usize { +const unsafe fn count_different_bytes(a: &[u8], b: &[u8]) -> usize { // This could've been written as `std::iter::zip(a, b).filter(|(x, y)| x != y).count()`, // however this function is very hot, and has been written to be as primitive as // possible for lower optimization levels. @@ -616,7 +616,7 @@ pub fn find_matching_contract_artifact( // If all artifact_ids in `possible_targets` have the same name (without ".", indicates // additional compiler profiles), it means that there are multiple contracts in the // same file. - if !target_id.name.contains(".") + if !target_id.name.contains('.') && possible_targets.iter().any(|(id, _)| id.name != target_id.name) { eyre::bail!( @@ -628,7 +628,7 @@ pub fn find_matching_contract_artifact( // same but `id.path` is different. let artifact = possible_targets .iter() - .find_map(|(id, artifact)| if id.profile == "default" { Some(*artifact) } else { None }) + .find_map(|(id, artifact)| (id.profile == "default").then_some(*artifact)) .unwrap_or(target_artifact); Ok(artifact.clone()) diff --git a/crates/common/src/io/shell.rs b/crates/common/src/io/shell.rs index e462be70c75aa..f067ebfa95cd8 100644 --- a/crates/common/src/io/shell.rs +++ b/crates/common/src/io/shell.rs @@ -51,7 +51,7 @@ pub fn is_markdown() -> bool { /// The global shell instance. static GLOBAL_SHELL: OnceLock> = OnceLock::new(); -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] /// The requested output mode. pub enum OutputMode { /// Default output @@ -74,7 +74,7 @@ impl OutputMode { } /// The requested output format. -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum OutputFormat { /// Plain text output. #[default] @@ -153,7 +153,7 @@ enum ShellOut { } /// Whether messages should use color output. -#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize, Deserialize, ValueEnum)] +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, ValueEnum)] pub enum ColorChoice { /// Intelligently guess whether to use color output (default). #[default] @@ -204,7 +204,7 @@ impl Shell { } /// Creates a shell that ignores all output. - pub fn empty() -> Self { + pub const fn empty() -> Self { Self { output: ShellOut::Empty(std::io::empty()), output_format: OutputFormat::Text, @@ -264,22 +264,22 @@ impl Shell { } /// Gets the output format of the shell. - pub fn output_format(&self) -> OutputFormat { + pub const fn output_format(&self) -> OutputFormat { self.output_format } /// Gets the output mode of the shell. - pub fn output_mode(&self) -> OutputMode { + pub const fn output_mode(&self) -> OutputMode { self.output_mode } /// Gets the verbosity of the shell when [`OutputMode::Normal`] is set. - pub fn verbosity(&self) -> Verbosity { + pub const fn verbosity(&self) -> Verbosity { self.verbosity } /// Sets the verbosity level. - pub fn set_verbosity(&mut self, verbosity: Verbosity) { + pub const fn set_verbosity(&mut self, verbosity: Verbosity) { self.verbosity = verbosity; } @@ -287,7 +287,7 @@ impl Shell { /// /// If we are not using a color stream, this will always return `Never`, even if the color /// choice has been set to something else. - pub fn color_choice(&self) -> ColorChoice { + pub const fn color_choice(&self) -> ColorChoice { match self.output { ShellOut::Stream { color_choice, .. } => color_choice, ShellOut::Empty(_) => ColorChoice::Never, @@ -295,7 +295,7 @@ impl Shell { } /// Returns `true` if stderr is a tty. - pub fn is_err_tty(&self) -> bool { + pub const fn is_err_tty(&self) -> bool { match self.output { ShellOut::Stream { stderr_tty, .. } => stderr_tty, ShellOut::Empty(_) => false, @@ -489,7 +489,7 @@ impl ShellOut { impl ColorChoice { /// Converts our color choice to [`anstream`]'s version. - fn to_anstream_color_choice(self) -> anstream::ColorChoice { + const fn to_anstream_color_choice(self) -> anstream::ColorChoice { match self { Self::Always => anstream::ColorChoice::Always, Self::Never => anstream::ColorChoice::Never, @@ -498,7 +498,7 @@ impl ColorChoice { } } -fn supports_color(choice: anstream::ColorChoice) -> bool { +const fn supports_color(choice: anstream::ColorChoice) -> bool { match choice { anstream::ColorChoice::Always | anstream::ColorChoice::AlwaysAnsi diff --git a/crates/common/src/io/stdin.rs b/crates/common/src/io/stdin.rs index feba958c18e47..f9da53f305f28 100644 --- a/crates/common/src/io/stdin.rs +++ b/crates/common/src/io/stdin.rs @@ -101,9 +101,9 @@ pub fn read_bytes(read_line: bool) -> Result> { let mut buf = String::new(); stdin.read_line(&mut buf)?; // remove the trailing newline - if let Some(b'\n') = buf.as_bytes().last() { + if matches!(buf.as_bytes().last(), Some(b'\n')) { buf.pop(); - if let Some(b'\r') = buf.as_bytes().last() { + if matches!(buf.as_bytes().last(), Some(b'\r')) { buf.pop(); } } diff --git a/crates/common/src/provider/curl_transport.rs b/crates/common/src/provider/curl_transport.rs index ca43e8b34f2b3..f2f3a44361f52 100644 --- a/crates/common/src/provider/curl_transport.rs +++ b/crates/common/src/provider/curl_transport.rs @@ -66,7 +66,7 @@ pub struct CurlTransport { impl CurlTransport { /// Create a new curl transport with the given URL. - pub fn new(url: Url) -> Self { + pub const fn new(url: Url) -> Self { Self { url, headers: vec![], jwt: None } } diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 764a44db55baa..1d4c0ec9bfeaa 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -190,19 +190,19 @@ impl ProviderBuilder { /// response body has finished. /// /// Default is no timeout. - pub fn timeout(mut self, timeout: Duration) -> Self { + pub const 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 { + pub const 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 { + pub const fn max_retry(mut self, max_retry: u32) -> Self { self.max_retry = max_retry; self } @@ -221,7 +221,7 @@ impl ProviderBuilder { } /// The starting backoff delay to use after the first failed request - pub fn initial_backoff(mut self, initial_backoff: u64) -> Self { + pub const fn initial_backoff(mut self, initial_backoff: u64) -> Self { self.initial_backoff = initial_backoff; self } @@ -229,7 +229,7 @@ impl ProviderBuilder { /// 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 { + pub const fn compute_units_per_second(mut self, compute_units_per_second: u64) -> Self { self.compute_units_per_second = compute_units_per_second; self } @@ -237,7 +237,10 @@ impl ProviderBuilder { /// 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 { + pub const 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; } @@ -247,7 +250,7 @@ impl ProviderBuilder { /// Sets the provider to be local. /// /// This is useful for local dev nodes. - pub fn local(mut self, is_local: bool) -> Self { + pub const fn local(mut self, is_local: bool) -> Self { self.is_local = is_local; self } @@ -255,7 +258,7 @@ impl ProviderBuilder { /// Sets aggressive `max_retry` and `initial_backoff` values /// /// This is only recommend for local dev nodes - pub fn aggressive(self) -> Self { + pub const fn aggressive(self) -> Self { self.max_retry(100).initial_backoff(100).local(true) } @@ -279,7 +282,7 @@ impl ProviderBuilder { } /// Sets whether to accept invalid certificates. - pub fn accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self { + pub const fn accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self { self.accept_invalid_certs = accept_invalid_certs; self } @@ -288,7 +291,7 @@ impl ProviderBuilder { /// /// This can help in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) /// where system proxy detection via SCDynamicStore causes crashes. - pub fn no_proxy(mut self, no_proxy: bool) -> Self { + pub const fn no_proxy(mut self, no_proxy: bool) -> Self { self.no_proxy = no_proxy; self } @@ -297,7 +300,7 @@ impl ProviderBuilder { /// /// When enabled, the provider will print equivalent curl commands to stdout /// instead of actually executing the RPC requests. - pub fn curl_mode(mut self, curl_mode: bool) -> Self { + pub const fn curl_mode(mut self, curl_mode: bool) -> Self { self.curl_mode = curl_mode; self } diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 8725a4fbe2fe7..5c4ff2a8fa7e5 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -97,7 +97,7 @@ pub struct RuntimeTransportBuilder { impl RuntimeTransportBuilder { /// Create a new builder with the given URL. - pub fn new(url: Url) -> Self { + pub const fn new(url: Url) -> Self { Self { url, headers: vec![], @@ -121,13 +121,13 @@ impl RuntimeTransportBuilder { } /// Set the timeout for the transport. - pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self { + pub const fn with_timeout(mut self, timeout: std::time::Duration) -> Self { self.timeout = timeout; self } /// Set whether to accept invalid certificates. - pub fn accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self { + pub const fn accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self { self.accept_invalid_certs = accept_invalid_certs; self } @@ -136,7 +136,7 @@ impl RuntimeTransportBuilder { /// /// This can help in sandboxed environments (e.g., Cursor IDE sandbox, macOS App Sandbox) /// where system proxy detection via SCDynamicStore causes crashes. - pub fn no_proxy(mut self, no_proxy: bool) -> Self { + pub const fn no_proxy(mut self, no_proxy: bool) -> Self { self.no_proxy = no_proxy; self } diff --git a/crates/common/src/retry.rs b/crates/common/src/retry.rs index a8e8188e43246..ec102d3ef0446 100644 --- a/crates/common/src/retry.rs +++ b/crates/common/src/retry.rs @@ -23,12 +23,12 @@ pub struct Retry { impl Retry { /// Creates a new `Retry` instance. - pub fn new(retries: u32, delay: Duration) -> Self { + pub const fn new(retries: u32, delay: Duration) -> Self { Self { retries, delay } } /// Creates a new `Retry` instance with no delay between retries. - pub fn new_no_delay(retries: u32) -> Self { + pub const fn new_no_delay(retries: u32) -> Self { Self::new(retries, Duration::ZERO) } diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index 27bb2d7ef4369..c8cf736d69e95 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -371,7 +371,7 @@ pub enum SelectorKind { impl SelectorKind { /// Returns the function selector if it is a function OR custom error. - pub fn as_function(&self) -> Option { + pub const fn as_function(&self) -> Option { match *self { Self::Function(selector) | Self::Error(selector) => Some(selector), _ => None, @@ -379,7 +379,7 @@ impl SelectorKind { } /// Returns the event selector if it is an event. - pub fn as_event(&self) -> Option { + pub const fn as_event(&self) -> Option { match *self { Self::Event(hash) => Some(hash), _ => None, @@ -444,7 +444,7 @@ pub struct RawSelectorImportData { } impl RawSelectorImportData { - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.function.is_empty() && self.event.is_empty() && self.error.is_empty() } } diff --git a/crates/common/src/slot_identifier.rs b/crates/common/src/slot_identifier.rs index 231a4478c293e..e737bc478bb62 100644 --- a/crates/common/src/slot_identifier.rs +++ b/crates/common/src/slot_identifier.rs @@ -124,7 +124,7 @@ impl SlotInfo { } /// Slot is of type [`DynSolType::Bytes`] or [`DynSolType::String`] - pub fn is_bytes_or_string(&self) -> bool { + pub const fn is_bytes_or_string(&self) -> bool { matches!(self.slot_type.dyn_sol_type, DynSolType::Bytes | DynSolType::String) } @@ -377,7 +377,7 @@ pub struct SlotIdentifier { impl SlotIdentifier { /// Creates a new SlotIdentifier with the given storage layout. - pub fn new(storage_layout: Arc) -> Self { + pub const fn new(storage_layout: Arc) -> Self { Self { storage_layout } } diff --git a/crates/common/src/traits.rs b/crates/common/src/traits.rs index 2f3540c453595..89d4d4c8f8575 100644 --- a/crates/common/src/traits.rs +++ b/crates/common/src/traits.rs @@ -221,7 +221,7 @@ impl TestFunctionKind { /// Returns `true` if this function is a unit test. #[inline] - pub fn is_unit_test(&self) -> bool { + pub const fn is_unit_test(&self) -> bool { matches!(self, Self::UnitTest { .. }) } diff --git a/crates/common/src/transactions/broadcast.rs b/crates/common/src/transactions/broadcast.rs index f44076f366d20..45a3ea038ff42 100644 --- a/crates/common/src/transactions/broadcast.rs +++ b/crates/common/src/transactions/broadcast.rs @@ -23,15 +23,15 @@ pub enum TransactionMaybeSigned { impl TransactionMaybeSigned { /// Creates a new (unsigned) transaction for broadcast - pub fn new(tx: N::TransactionRequest) -> Self { + pub const fn new(tx: N::TransactionRequest) -> Self { Self::Unsigned(tx) } - pub fn is_unsigned(&self) -> bool { + pub const fn is_unsigned(&self) -> bool { matches!(self, Self::Unsigned(_)) } - pub fn as_unsigned_mut(&mut self) -> Option<&mut N::TransactionRequest> { + pub const fn as_unsigned_mut(&mut self) -> Option<&mut N::TransactionRequest> { match self { Self::Unsigned(tx) => Some(tx), _ => None, diff --git a/crates/common/src/transactions/builder.rs b/crates/common/src/transactions/builder.rs index 935c30f20bfe9..b022db33eadaa 100644 --- a/crates/common/src/transactions/builder.rs +++ b/crates/common/src/transactions/builder.rs @@ -249,6 +249,14 @@ pub trait FoundryTransactionBuilder: TransactionBuilder { /// No-op for non-Tempo networks. fn convert_create_to_call(&mut self) {} + /// Clears the `to` and `value` fields for batch transactions that use `calls`. + /// + /// In Tempo AA batch transactions, targets are specified in the `calls` field, not in `to`. + /// If `to` is set, `build_aa()` would add a spurious extra call. Must be called after + /// `prepare()` sets `kind`/`to` but before gas estimation. + /// No-op for non-Tempo networks. + fn clear_batch_to(&mut self) {} + /// Signs the transaction using an access key (keychain mode). /// /// If `key_authorization` is provided and the key is not yet provisioned on-chain, @@ -440,6 +448,13 @@ impl FoundryTransactionBuilder for ::Tran } } + fn clear_batch_to(&mut self) { + if !self.calls.is_empty() { + self.inner.to = None; + self.inner.value = None; + } + } + fn sign_with_access_key( mut self, provider: &impl Provider, diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 4f9eddc5e3ad6..9881d29bd0aab 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -13,7 +13,7 @@ pub struct ExtractConfigError { impl ExtractConfigError { /// Wraps the figment error. - pub fn new(error: figment::Error) -> Self { + pub const fn new(error: figment::Error) -> Self { Self { error } } } @@ -22,7 +22,7 @@ impl fmt::Display for ExtractConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut unique_errors = Vec::with_capacity(self.error.count()); let mut unique = HashSet::with_capacity(self.error.count()); - for err in self.error.clone().into_iter() { + for err in self.error.clone() { let err = if err .metadata .as_ref() @@ -152,7 +152,7 @@ impl SolidityErrorCode { /// The textual identifier for this error /// /// Returns `Err(code)` if unknown error - pub fn as_str(&self) -> Result<&'static str, u64> { + pub const fn as_str(&self) -> Result<&'static str, u64> { let s = match self { Self::SpdxLicenseNotProvided => "license", Self::VisibilityForConstructorIsIgnored => "constructor-visibility", diff --git a/crates/config/src/etherscan.rs b/crates/config/src/etherscan.rs index 25772a8171303..314ec53d87d89 100644 --- a/crates/config/src/etherscan.rs +++ b/crates/config/src/etherscan.rs @@ -135,11 +135,11 @@ impl ResolvedEtherscanConfigs { self, chain: Chain, ) -> Option> { - for (_, config) in self.configs.into_iter() { + for (_, config) in self.configs { match config { Ok(c) if c.chain == Some(chain) => return Some(Ok(c)), Err(e) => return Some(Err(e)), - _ => continue, + _ => {} } } None diff --git a/crates/config/src/extend.rs b/crates/config/src/extend.rs index 671b391450493..5b4eb23322272 100644 --- a/crates/config/src/extend.rs +++ b/crates/config/src/extend.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; /// Strategy for extending configuration from a base file. -#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum ExtendStrategy { /// Uses `admerge` figment strategy. @@ -26,7 +26,7 @@ pub enum ExtendStrategy { /// Supports two formats: /// - String: `extends = "base.toml"` /// - Object: `extends = { path = "base.toml", strategy = "no-collision" }` -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum Extends { /// Simple string path to base file @@ -35,7 +35,7 @@ pub enum Extends { Config(ExtendConfig), } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ExtendConfig { pub path: String, #[serde(default)] diff --git a/crates/config/src/filter.rs b/crates/config/src/filter.rs index 97b2ef48d7509..d92badb424a06 100644 --- a/crates/config/src/filter.rs +++ b/crates/config/src/filter.rs @@ -138,11 +138,11 @@ 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 { + if matcher.is_match_exclude(file) { file.strip_prefix(&self.project_root) .map_or(true, |stripped| matcher.is_match_exclude(stripped)) + } else { + false } }) } @@ -180,7 +180,7 @@ impl SkipBuildFilter { } /// Returns the pattern to match against a file - pub fn file_pattern(&self) -> &str { + pub const fn file_pattern(&self) -> &str { match self { Self::Tests => ".t.sol", Self::Scripts => ".s.sol", diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index d3f3d96ee8584..ef7f1660525a3 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -21,11 +21,11 @@ impl TomlFile { Ok(Self { doc, path }) } - fn doc(&self) -> &toml_edit::DocumentMut { + const fn doc(&self) -> &toml_edit::DocumentMut { &self.doc } - fn doc_mut(&mut self) -> &mut toml_edit::DocumentMut { + const fn doc_mut(&mut self) -> &mut toml_edit::DocumentMut { &mut self.doc } diff --git a/crates/config/src/fmt.rs b/crates/config/src/fmt.rs index bea82b9a45449..ac5227e1c059c 100644 --- a/crates/config/src/fmt.rs +++ b/crates/config/src/fmt.rs @@ -78,19 +78,19 @@ pub enum NumberUnderscore { impl NumberUnderscore { /// Returns true if the option is `Preserve` #[inline] - pub fn is_preserve(self) -> bool { + pub const fn is_preserve(self) -> bool { matches!(self, Self::Preserve) } /// Returns true if the option is `Remove` #[inline] - pub fn is_remove(self) -> bool { + pub const fn is_remove(self) -> bool { matches!(self, Self::Remove) } /// Returns true if the option is `Remove` #[inline] - pub fn is_thousands(self) -> bool { + pub const fn is_thousands(self) -> bool { matches!(self, Self::Thousands) } } @@ -178,15 +178,15 @@ pub enum MultilineFuncHeaderStyle { } impl MultilineFuncHeaderStyle { - pub fn all(&self) -> bool { + pub const fn all(&self) -> bool { matches!(self, Self::All | Self::AllParams) } - pub fn params_first(&self) -> bool { + pub const fn params_first(&self) -> bool { matches!(self, Self::ParamsAlways | Self::ParamsFirstMulti) } - pub fn attrib_first(&self) -> bool { + pub const fn attrib_first(&self) -> bool { matches!(self, Self::AttributesFirst) } } @@ -224,15 +224,15 @@ pub enum PreferCompact { } impl PreferCompact { - pub fn calls(&self) -> bool { + pub const fn calls(&self) -> bool { matches!(self, Self::All | Self::Calls) } - pub fn events(&self) -> bool { + pub const fn events(&self) -> bool { matches!(self, Self::All | Self::Events | Self::EventsErrors) } - pub fn errors(&self) -> bool { + pub const fn errors(&self) -> bool { matches!(self, Self::All | Self::Errors | Self::EventsErrors) } } diff --git a/crates/config/src/fs_permissions.rs b/crates/config/src/fs_permissions.rs index f8fba0c97a10e..e38e6040ac902 100644 --- a/crates/config/src/fs_permissions.rs +++ b/crates/config/src/fs_permissions.rs @@ -90,7 +90,7 @@ impl FsPermissions { } } - if max_path_len > 0 { Some(highest_permission) } else { None } + (max_path_len > 0).then_some(highest_permission) } /// Updates all `allowed_paths` and joins ([`Path::join`]) the `root` with all entries @@ -112,12 +112,12 @@ impl FsPermissions { } /// Returns true if no permissions are configured - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.permissions.is_empty() } /// Returns the number of configured permissions - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.permissions.len() } } @@ -158,7 +158,7 @@ impl PathPermission { } /// Returns true if the access is allowed - pub fn is_granted(&self, kind: FsAccessKind) -> bool { + pub const fn is_granted(&self, kind: FsAccessKind) -> bool { self.access.is_granted(kind) } } @@ -197,7 +197,7 @@ pub enum FsAccessPermission { impl FsAccessPermission { /// Returns true if the access is allowed - pub fn is_granted(&self, kind: FsAccessKind) -> bool { + pub const fn is_granted(&self, kind: FsAccessKind) -> bool { match (self, kind) { (Self::ReadWrite, _) => true, (Self::Write, FsAccessKind::Write) => true, diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index d5bb4baa980ab..03808cc15cc9a 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -132,12 +132,12 @@ impl FuzzCorpusConfig { } /// Whether edge coverage should be collected and displayed. - pub fn collect_edge_coverage(&self) -> bool { + pub const fn collect_edge_coverage(&self) -> bool { self.corpus_dir.is_some() || self.show_edge_coverage } /// Whether coverage guided fuzzing is enabled. - pub fn is_coverage_guided(&self) -> bool { + pub const fn is_coverage_guided(&self) -> bool { self.corpus_dir.is_some() } } diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index b463c1555ef9d..270df14a6c291 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -97,7 +97,11 @@ impl InlineConfig { /// Returns a [`figment::Provider`] for this [`InlineConfig`] at the given contract and function /// level. - pub fn provide<'a>(&'a self, contract: &'a str, function: &'a str) -> InlineConfigProvider<'a> { + pub const fn provide<'a>( + &'a self, + contract: &'a str, + function: &'a str, + ) -> InlineConfigProvider<'a> { InlineConfigProvider { inline: self, contract, function } } diff --git a/crates/config/src/inline/natspec.rs b/crates/config/src/inline/natspec.rs index f75d32913fb15..f1d5844101379 100644 --- a/crates/config/src/inline/natspec.rs +++ b/crates/config/src/inline/natspec.rs @@ -58,14 +58,15 @@ impl NatSpec { warn!(?abs_path, %contract, "could not parse natspec with solar"); } - let mut used_solc = false; - if !used_solar + let used_solc = if !used_solar && let Some(ast) = &artifact.ast && let Some(node) = solc.contract_root_node(&ast.nodes, &contract) { solc.parse(&mut natspecs, &contract, node, true); - used_solc = true; - } + true + } else { + false + }; if !used_solar && !used_solc { warn!(?abs_path, %contract, "could not parse natspec"); @@ -129,7 +130,7 @@ struct SolcParser { } impl SolcParser { - fn new() -> Self { + const fn new() -> Self { Self { _private: () } } @@ -222,7 +223,7 @@ struct SolarParser<'a> { } impl<'a> SolarParser<'a> { - fn new(sess: &'a Session) -> Self { + const fn new(sess: &'a Session) -> Self { Self { sess } } diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 591af88efdee4..15f8d4608ac5f 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -79,4 +79,9 @@ impl InvariantConfig { pub fn new(cache_dir: PathBuf) -> Self { Self { failure_persist_dir: Some(cache_dir), ..Default::default() } } + + /// Returns true if generated invariant calls may advance block time or height. + pub const fn has_delay(&self) -> bool { + self.max_block_delay.is_some() || self.max_time_delay.is_some() + } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 92156f39a0aa8..8b11087aaefb3 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -162,7 +162,7 @@ pub use semver; /// the "default" meta-profile. /// /// Note that these behaviors differ from those of [`Config::figment()`]. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Config { /// The selected profile. **(default: _default_ `default`)** /// @@ -648,7 +648,7 @@ impl From for DenyLevel { impl DenyLevel { /// Returns `true` if the deny level includes warnings. - pub fn warnings(&self) -> bool { + pub const fn warnings(&self) -> bool { match self { Self::Never => false, Self::Warnings | Self::Notes => true, @@ -656,7 +656,7 @@ impl DenyLevel { } /// Returns `true` if the deny level includes notes. - pub fn notes(&self) -> bool { + pub const fn notes(&self) -> bool { match self { Self::Never | Self::Warnings => false, Self::Notes => true, @@ -664,7 +664,7 @@ impl DenyLevel { } /// Returns `true` if the deny level is set to never (only errors). - pub fn never(&self) -> bool { + pub const fn never(&self) -> bool { match self { Self::Never => true, Self::Warnings | Self::Notes => false, @@ -1009,7 +1009,7 @@ impl Config { /// Normalizes optimizer settings. /// See - pub fn normalized_optimizer_settings(mut self) -> Self { + pub const fn normalized_optimizer_settings(mut self) -> Self { self.normalize_optimizer_settings(); self } @@ -1023,7 +1023,7 @@ impl Config { /// - with default settings, optimizer is set to false and optimizer runs to 200 /// - if optimizer is set and optimizer runs not specified, then optimizer runs is set to 200 /// - enable optimizer if not explicitly set and optimizer runs set to a value greater than 0 - pub fn normalize_optimizer_settings(&mut self) { + pub const fn normalize_optimizer_settings(&mut self) { match (self.optimizer, self.optimizer_runs) { // Default: set the optimizer to false and optimizer runs to 200. (None, None) => { @@ -1067,7 +1067,7 @@ impl Config { /// Cleans up any duplicate `Remapping` and sorts them /// /// On windows this will convert any `\` in the remapping path into a `/` - pub fn sanitize_remappings(&mut self) { + pub const fn sanitize_remappings(&mut self) { #[cfg(target_os = "windows")] { // force `/` in remappings on windows @@ -1150,7 +1150,8 @@ impl Config { paths: &ProjectPathsConfig, ) -> Result>, SolcError> { - let mut map = BTreeMap::new(); + let mut map: BTreeMap> = + BTreeMap::new(); if self.compilation_restrictions.is_empty() { return Ok(BTreeMap::new()); } @@ -1170,9 +1171,7 @@ impl Config { }) { let res: RestrictionsWithVersion<_> = res.clone().try_into().map_err(SolcError::msg)?; - if !map.contains_key(source) { - map.insert(source.clone(), res); - } else { + if map.contains_key(source) { let value = map.remove(source.as_path()).unwrap(); if let Some(merged) = value.clone().merge(res) { map.insert(source.clone(), merged); @@ -1187,6 +1186,8 @@ impl Config { ); map.insert(source.clone(), value); } + } else { + map.insert(source.clone(), res); } } } @@ -1335,7 +1336,7 @@ impl Config { /// /// Returns `false` if `solc_version` is explicitly set, otherwise returns the value of /// `auto_detect_solc` - pub fn is_auto_detect(&self) -> bool { + pub const fn is_auto_detect(&self) -> bool { if self.solc.is_some() { return false; } @@ -2182,9 +2183,8 @@ impl Config { } if let Ok(entries) = cache_dir.as_path().read_dir() { for entry in entries.flatten().filter(|x| x.path().is_dir()) { - match Chain::from_str(&entry.file_name().to_string_lossy()) { - Ok(chain) => cache.chains.push(Self::list_foundry_chain_cache(chain)?), - Err(_) => continue, + if let Ok(chain) = Chain::from_str(&entry.file_name().to_string_lossy()) { + cache.chains.push(Self::list_foundry_chain_cache(chain)?); } } Ok(cache) @@ -2329,7 +2329,7 @@ impl Config { // Normalize `deny` based on the provided `deny_warnings` value. if figment.extract_inner::("deny_warnings").unwrap_or(false) - && let Ok(DenyLevel::Never) = figment.extract_inner("deny") + && figment.extract_inner("deny") == Ok(DenyLevel::Never) { figment = figment.merge(("deny", DenyLevel::Warnings)); } diff --git a/crates/config/src/lint.rs b/crates/config/src/lint.rs index 840a226f280fe..9494fe18d9f8b 100644 --- a/crates/config/src/lint.rs +++ b/crates/config/src/lint.rs @@ -120,7 +120,7 @@ pub enum Severity { } impl Severity { - fn to_str(self) -> &'static str { + const fn to_str(self) -> &'static str { match self { Self::High => "High", Self::Med => "Med", @@ -131,7 +131,7 @@ impl Severity { } } - fn to_str_kebab(self) -> &'static str { + const fn to_str_kebab(self) -> &'static str { match self { Self::High => "high", Self::Med => "medium", diff --git a/crates/config/src/providers/ext.rs b/crates/config/src/providers/ext.rs index a6404da946cd3..6423e37a4f2fa 100644 --- a/crates/config/src/providers/ext.rs +++ b/crates/config/src/providers/ext.rs @@ -56,7 +56,7 @@ pub(crate) struct TomlFileProvider { } impl TomlFileProvider { - pub(crate) fn new(env_var: Option<&'static str>, default: PathBuf) -> Self { + pub(crate) const fn new(env_var: Option<&'static str>, default: PathBuf) -> Self { Self { env_var, env_val: OnceCell::new(), default, cache: OnceCell::new() } } diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 0f308aa1ba523..88c0255217908 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -24,12 +24,12 @@ pub struct Remappings { impl Remappings { /// Create a new `Remappings` wrapper with an empty vector. - pub fn new() -> Self { + pub const fn new() -> Self { Self { remappings: Vec::new(), project_paths: Vec::new() } } /// Create a new `Remappings` wrapper with a vector of remappings. - pub fn new_with_remappings(remappings: Vec) -> Self { + pub const fn new_with_remappings(remappings: Vec) -> Self { Self { remappings, project_paths: Vec::new() } } @@ -267,8 +267,7 @@ impl RemappingsProvider<'_> { // if the configured _src_ directory is set to something that // `Remapping::find_many` doesn't classify as a src directory (src, contracts, // lib), then we need to manually add a remapping here - let mut src_remapping = None; - if ![Path::new("src"), Path::new("contracts"), Path::new("lib")] + let src_remapping = if ![Path::new("src"), Path::new("contracts"), Path::new("lib")] .contains(&config.src.as_path()) && let Some(name) = lib.file_name().and_then(|s| s.to_str()) { @@ -280,8 +279,10 @@ impl RemappingsProvider<'_> { if !r.path.ends_with('/') { r.path.push('/') } - src_remapping = Some(r); - } + Some(r) + } else { + None + }; // Eventually, we could set context for remappings at this location, // taking into account the OS platform. We'll need to be able to handle nested diff --git a/crates/config/src/providers/warnings.rs b/crates/config/src/providers/warnings.rs index 9113fcd76fffa..930066b29cf73 100644 --- a/crates/config/src/providers/warnings.rs +++ b/crates/config/src/providers/warnings.rs @@ -89,14 +89,10 @@ impl WarningsProvider

{ // 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 - } + (key == *deprecated_key).then(|| Warning::DeprecatedKey { + old: deprecated_key.to_string(), + new: new_value.to_string(), + }) }) }; let profiles = data diff --git a/crates/debugger/src/debugger.rs b/crates/debugger/src/debugger.rs index ab0b66f658dc7..95d836ada6856 100644 --- a/crates/debugger/src/debugger.rs +++ b/crates/debugger/src/debugger.rs @@ -27,7 +27,7 @@ impl Debugger { } /// Creates a new debugger. - pub fn new( + pub const fn new( debug_arena: Vec, identified_contracts: AddressHashMap, contracts_sources: ContractSources, diff --git a/crates/debugger/src/node.rs b/crates/debugger/src/node.rs index e74ae58d66323..9e84b847c5329 100644 --- a/crates/debugger/src/node.rs +++ b/crates/debugger/src/node.rs @@ -22,7 +22,7 @@ pub struct DebugNode { impl DebugNode { /// Creates a new debug node. - pub fn new( + pub const fn new( address: Address, kind: CallKind, steps: Vec, diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index daf2d01d1da84..5b6dceac4c890 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -290,11 +290,11 @@ impl TUIContext<'_> { lines.push(u_num, line, u_text); } - let first = if !last_has_nl { + let first = if last_has_nl { + 0 + } else { lines.push_raw(h_num, &[Span::raw(last), Span::styled(actual[0], h_text)]); 1 - } else { - 0 }; // Skip the first line if it has already been handled above. diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index e3aa94e09aab8..9f4a445a6fb45 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -34,7 +34,7 @@ pub struct TUI<'a> { impl<'a> TUI<'a> { /// Creates a new debugger. - pub fn new(debugger_context: &'a mut DebuggerContext) -> Self { + pub const fn new(debugger_context: &'a mut DebuggerContext) -> Self { Self { debugger_context } } diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index 30b0f9cd81179..7ecada9d33e72 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -65,7 +65,7 @@ impl DocBuilder { } /// Set `should_build` flag on the builder - pub fn with_should_build(mut self, should_build: bool) -> Self { + pub const fn with_should_build(mut self, should_build: bool) -> Self { self.should_build = should_build; self } @@ -393,7 +393,7 @@ impl DocBuilder { Some(self.config.book.clone()) } else { let book_path = self.config.book.join("book.toml"); - if book_path.is_file() { Some(book_path) } else { None } + book_path.is_file().then_some(book_path) } }; diff --git a/crates/doc/src/document.rs b/crates/doc/src/document.rs index cabc230e13b14..bcf80089cfe56 100644 --- a/crates/doc/src/document.rs +++ b/crates/doc/src/document.rs @@ -89,7 +89,7 @@ pub enum DocumentContent { } impl DocumentContent { - pub(crate) fn len(&self) -> usize { + pub(crate) const fn len(&self) -> usize { match self { Self::Empty => 0, Self::Single(_) => 1, @@ -101,13 +101,7 @@ impl DocumentContent { pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut ParseItem> { match self { Self::Empty => None, - Self::Single(item) => { - if index == 0 { - Some(item) - } else { - None - } - } + Self::Single(item) => (index == 0).then_some(item), Self::Constants(items) => items.get_mut(index), Self::OverloadedFunctions(items) => items.get_mut(index), } diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 431bf70df88f5..ee0175a5e21b1 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -66,7 +66,7 @@ pub struct Comment { impl Comment { /// Create new instance of [Comment]. - pub fn new(tag: CommentTag, value: String) -> Self { + pub const fn new(tag: CommentTag, value: String) -> Self { Self { tag, value } } @@ -86,21 +86,17 @@ impl Comment { /// Returns [None] if the word doesn't match. /// Useful for [CommentTag::Param] and [CommentTag::Return] comments. pub fn match_first_word(&self, expected: &str) -> Option<&str> { - self.split_first_word().and_then( - |(word, rest)| { - if word == expected { Some(rest) } else { None } - }, - ) + self.split_first_word().and_then(|(word, rest)| (word == expected).then_some(rest)) } /// Check if this comment is a custom tag. - pub fn is_custom(&self) -> bool { + pub const fn is_custom(&self) -> bool { matches!(self.tag, CommentTag::Custom(_)) } } /// The collection of natspec [Comment] items. -#[derive(Clone, Debug, Default, PartialEq, Deref, DerefMut)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Deref, DerefMut)] pub struct Comments(Vec); /// Forward the [Comments] function implementation to the [CommentsRef] @@ -151,12 +147,24 @@ impl Comments { impl From> for Comments { fn from(value: Vec) -> Self { - Self(value.into_iter().flat_map(Comment::from_doc_comment).collect()) + Self(value.into_iter().filter_map(Comment::from_doc_comment).collect()) + } +} + +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) } } /// The collection of references to natspec [Comment] items. -#[derive(Debug, Default, PartialEq, Deref)] +#[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); impl<'a> CommentsRef<'a> { diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index cb8d9d1bcf5f4..925a7cc8a2259 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -203,7 +203,7 @@ struct InlineLinkTarget<'a> { } impl<'a> InlineLinkTarget<'a> { - fn borrowed(section: &'a str, target_path: PathBuf) -> Self { + const fn borrowed(section: &'a str, target_path: PathBuf) -> Self { Self { section: Cow::Borrowed(section), target_path } } } @@ -261,12 +261,8 @@ impl<'a> InlineLink<'a> { self.exact_identifier().split('-').next().unwrap() } - fn exact_identifier(&self) -> &str { - let mut name = self.identifier; - if let Some(part) = self.part { - name = part; - } - name + const fn exact_identifier(&self) -> &str { + if let Some(part) = self.part { part } else { self.identifier } } /// Returns the content of the matched link. @@ -275,7 +271,7 @@ impl<'a> InlineLink<'a> { } /// Returns true if the link is external. - fn is_external(&self) -> bool { + const fn is_external(&self) -> bool { self.part.is_some() } } diff --git a/crates/doc/src/solang_ext/ast_eq.rs b/crates/doc/src/solang_ext/ast_eq.rs index e19d3469ce829..1252ac4eb32bf 100644 --- a/crates/doc/src/solang_ext/ast_eq.rs +++ b/crates/doc/src/solang_ext/ast_eq.rs @@ -100,10 +100,10 @@ where T: AstEq, { fn ast_eq(&self, other: &Self) -> bool { - if self.len() != other.len() { - false - } else { + if self.len() == other.len() { self.iter().zip(other.iter()).all(|(left, right)| left.ast_eq(right)) + } else { + false } } } diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 1502322ac87f4..e3358b2f437dd 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -1,12 +1,11 @@ use crate::{ - CONTRACT_INHERITANCE_ID, CommentTag, Comments, CommentsRef, DEPLOYMENTS_ID, Document, - GIT_SOURCE_ID, INHERITDOC_ID, Markdown, PreprocessorOutput, - document::{DocumentContent, read_context}, - helpers::function_signature, + document::{read_context, DocumentContent}, parser::ParseSource, - solang_ext::SafeUnwrap, writer::BufWriter, + CommentTag, Comments, CommentsRef, Document, Markdown, PreprocessorOutput, + CONTRACT_INHERITANCE_ID, DEPLOYMENTS_ID, GIT_SOURCE_ID, INHERITDOC_ID, }; +use forge_fmt::solang_ext::SafeUnwrap; use itertools::Itertools; use solang_parser::pt::{Base, FunctionDefinition, VariableAttribute}; use std::path::Path; @@ -63,7 +62,7 @@ impl AsDoc for CommentsRef<'_> { // Write dev tags let devs = self.include_tag(CommentTag::Dev); for d in devs.iter() { - writer.write_dev_content(&d.value)?; + writer.write_italic(&d.value)?; writer.writeln()?; } @@ -88,7 +87,7 @@ impl AsDoc for CommentsRef<'_> { impl AsDoc for Base { fn as_doc(&self) -> AsDocResult { - Ok(self.name.identifiers.iter().map(|ident| ident.name.clone()).join(".")) + Ok(self.name.identifiers.iter().map(|ident| ident.name.to_owned()).join(".")) } } @@ -107,7 +106,16 @@ impl AsDoc for Document { for item in items { let func = item.as_function().unwrap(); - let heading = function_signature(func).replace(',', ", "); + let mut heading = item.source.ident(); + if !func.params.is_empty() { + heading.push_str(&format!( + "({})", + func.params + .iter() + .map(|p| p.1.as_ref().map(|p| p.ty.to_string()).unwrap_or_default()) + .join(", ") + )); + } writer.write_heading(&heading)?; writer.write_section(&item.comments, &item.code)?; } @@ -253,7 +261,15 @@ impl AsDoc for Document { writer.write_subtitle("Enums")?; enums.into_iter().try_for_each(|(item, comments, code)| { writer.write_heading(&item.name.safe_unwrap().name)?; - writer.write_section(comments, code)?; + + let filtered_comments: Comments = (*comments) + .iter() + .filter(|c| c.tag != CommentTag::Custom("variant".to_string())) + .cloned() + .collect::>() + .into(); + + writer.write_section(&filtered_comments, code)?; writer.try_write_variant_table(item, comments) })?; } @@ -319,10 +335,9 @@ impl Document { comments: &Comments, code: &str, ) -> Result<(), std::fmt::Error> { - let func_sign = function_signature(func); - let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.clone()); + let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned()); let comments = - comments.merge_inheritdoc(&func_sign, read_context!(self, INHERITDOC_ID, Inheritdoc)); + comments.merge_inheritdoc(&func_name, read_context!(self, INHERITDOC_ID, Inheritdoc)); // Write function name writer.write_heading(&func_name)?; diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index a62dff3f21582..085664f85fa81 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,4 +1,4 @@ -use crate::{AsDoc, CommentTag, Comments, Deployment, Markdown, writer::traits::ParamLike}; +use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; use solang_parser::pt::{ EnumDefinition, ErrorParameter, EventParameter, Parameter, VariableDeclaration, @@ -40,7 +40,7 @@ impl BufWriter { } /// Returns true if the buffer is empty. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.buf.is_empty() } @@ -89,17 +89,6 @@ impl BufWriter { writeln!(self.buf, "{}", Markdown::Italic(text)) } - /// Writes dev content to the buffer, handling markdown lists properly. - /// If the content contains markdown lists, it formats them correctly. - /// Otherwise, it writes the content in italics. - pub fn write_dev_content(&mut self, text: &str) -> fmt::Result { - for line in text.lines() { - writeln!(self.buf, "{line}")?; - } - - Ok(()) - } - /// Writes bold text to the buffer formatted as [Markdown::Bold]. pub fn write_bold(&mut self, text: &str) -> fmt::Result { writeln!(self.buf, "{}", Markdown::Bold(text)) @@ -202,7 +191,8 @@ impl BufWriter { params: &EnumDefinition, comments: &Comments, ) -> fmt::Result { - let comments = comments.include_tags(&[CommentTag::Param]); + let comments = + comments.include_tags(&[CommentTag::Param, CommentTag::Custom("variant".to_string())]); // There is nothing to write. if comments.is_empty() { @@ -215,7 +205,7 @@ impl BufWriter { self.write_piped(&VARIANTS_TABLE_HEADERS.join("|"))?; self.write_piped(&VARIANTS_TABLE_SEPARATOR)?; - for value in ¶ms.values { + for value in params.values.iter() { let param_name = value.as_ref().map(|v| v.name.clone()); let comment = param_name.as_ref().and_then(|name| { @@ -223,7 +213,7 @@ impl BufWriter { }); let row = [ - Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + Markdown::Code(param_name.as_deref().unwrap_or("")).as_doc()?, comment.unwrap_or_default().replace('\n', " "), ]; self.write_piped(&row.join("|"))?; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 2517893fa32b0..3e1a485eea09e 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -54,6 +54,7 @@ revm = { workspace = true, features = [ ] } revm-inspectors.workspace = true op-alloy-consensus = { workspace = true, features = ["k256"] } +alloy-op-evm.workspace = true op-alloy-network.workspace = true op-revm.workspace = true tempo-revm.workspace = true @@ -80,4 +81,5 @@ alloy-op-evm.workspace = true alloy-serde.workspace = true op-alloy-consensus.workspace = true op-alloy-rpc-types.workspace = true +anvil.workspace = true foundry-test-utils.workspace = true diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index b6afcaafce3ec..87a92e9d97460 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -72,7 +72,7 @@ impl Debug for CowBackend<'_, FEN> { impl<'a, FEN: FoundryEvmNetwork> CowBackend<'a, FEN> { /// Creates a new `CowBackend` with the given `Backend`. - pub fn new_borrowed(backend: &'a Backend) -> Self { + pub const fn new_borrowed(backend: &'a Backend) -> Self { Self { backend: Cow::Borrowed(backend), pending_init: None } } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index e7c1345792aa3..5c5fe29c3154c 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -610,7 +610,7 @@ impl Backend { /// Returns all snapshots created in this backend #[allow(clippy::type_complexity)] - pub fn state_snapshots( + pub const fn state_snapshots( &self, ) -> &StateSnapshots< BackendStateSnapshot< @@ -650,7 +650,7 @@ impl Backend { } /// Returns the set caller address - pub fn caller_address(&self) -> Option

{ + pub const fn caller_address(&self) -> Option
{ self.inner.caller } @@ -659,12 +659,12 @@ impl Backend { /// If an error occurs in a restored state snapshot, the test is considered failed. /// /// This returns whether there was a reverted state snapshot that recorded an error. - pub fn has_state_snapshot_failure(&self) -> bool { + pub const fn has_state_snapshot_failure(&self) -> bool { self.inner.has_state_snapshot_failure } /// Sets the state snapshot failure flag. - pub fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) { + pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) { self.inner.has_state_snapshot_failure = has_state_snapshot_failure } @@ -696,7 +696,7 @@ impl Backend { } /// Returns the memory db used if not in forking mode - pub fn mem_db(&self) -> &FoundryEvmInMemoryDB { + pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB { &self.mem_db } @@ -1669,7 +1669,7 @@ pub struct Fork { impl Fork { /// Returns a reference to the underlying [`SharedBackend`]. - pub fn backend(&self) -> &SharedBackend { + pub const fn backend(&self) -> &SharedBackend { &self.db.db } @@ -2006,7 +2006,7 @@ pub(crate) fn merge_account_data, ) { - for addr in accounts.into_iter() { + for addr in accounts { merge_db_account_data(addr, active, &mut target_fork.db); merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state); } diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index df47882befb4f..915657ccde868 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -27,7 +27,7 @@ pub struct BackendStateSnapshot { impl BackendStateSnapshot { /// Takes a new state snapshot. - pub fn new(db: T, journaled_state: JournaledState, evm_env: EvmEnv) -> Self { + pub const fn new(db: T, journaled_state: JournaledState, evm_env: EvmEnv) -> Self { Self { db, journaled_state, snap_evm_env: evm_env } } @@ -57,7 +57,7 @@ pub enum RevertStateSnapshotAction { impl RevertStateSnapshotAction { /// Returns `true` if the action is to keep the state snapshot. - pub fn is_keep(&self) -> bool { + pub const fn is_keep(&self) -> bool { matches!(self, Self::RevertKeep) } } diff --git a/crates/evm/core/src/buffer.rs b/crates/evm/core/src/buffer.rs index e600708f43173..16655e382da0f 100644 --- a/crates/evm/core/src/buffer.rs +++ b/crates/evm/core/src/buffer.rs @@ -2,7 +2,7 @@ use alloy_primitives::U256; use revm::bytecode::opcode; /// Used to keep track of which buffer is currently active to be drawn by the debugger. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum BufferKind { Memory, Calldata, @@ -11,7 +11,7 @@ pub enum BufferKind { impl BufferKind { /// Helper to cycle through the active buffers. - pub fn next(&self) -> Self { + pub const fn next(&self) -> Self { match self { Self::Memory => Self::Calldata, Self::Calldata => Self::Returndata, @@ -83,17 +83,12 @@ pub fn get_buffer_accesses(op: u8, stack: &[U256]) -> Option { -2 => Some(1), -1 => Some(32), 0 => None, - 1.. => { - if (stack_index as usize) <= stack_len { - Some(stack[stack_len - stack_index as usize].saturating_to()) - } else { - None - } - } + 1.. => ((stack_index as usize) <= stack_len) + .then(|| stack[stack_len - stack_index as usize].saturating_to()), _ => panic!("invalid stack index"), }; - if buffer_access.0.is_some() || buffer_access.1.is_some() { + (buffer_access.0.is_some() || buffer_access.1.is_some()).then(|| { let (read, write) = buffer_access; let read_access = read.and_then(|b| { let (buffer, offset, len) = b; @@ -103,8 +98,6 @@ pub fn get_buffer_accesses(op: u8, stack: &[U256]) -> Option { let (offset, len) = b; Some(BufferAccess { offset: get_size(offset)?, len: get_size(len)? }) }); - Some(BufferAccesses { read: read_access, write: write_access }) - } else { - None - } + BufferAccesses { read: read_access, write: write_access } + }) } diff --git a/crates/evm/core/src/bytecode.rs b/crates/evm/core/src/bytecode.rs index 1d5bfea4f7e38..4e571e209d2fd 100644 --- a/crates/evm/core/src/bytecode.rs +++ b/crates/evm/core/src/bytecode.rs @@ -33,25 +33,25 @@ impl<'a> InstIter<'a> { /// Returns a new iterator that also yields the program counter alongside the opcode and /// immediate data. #[inline] - pub fn with_pc(self) -> InstIterWithPc<'a> { + pub const fn with_pc(self) -> InstIterWithPc<'a> { InstIterWithPc { iter: self, pc: 0 } } /// Returns the inner iterator. #[inline] - pub fn inner(&self) -> &slice::Iter<'a, u8> { + pub const fn inner(&self) -> &slice::Iter<'a, u8> { &self.iter } /// Returns the inner iterator. #[inline] - pub fn inner_mut(&mut self) -> &mut slice::Iter<'a, u8> { + pub const fn inner_mut(&mut self) -> &mut slice::Iter<'a, u8> { &mut self.iter } /// Returns the inner iterator. #[inline] - pub fn into_inner(self) -> slice::Iter<'a, u8> { + pub const fn into_inner(self) -> slice::Iter<'a, u8> { self.iter } } diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 0e28d92d91cae..7e4fe8d4dc15c 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -127,10 +127,6 @@ impl RevertDecoder { /// /// See [`decode`](Self::decode) for more information. pub fn maybe_decode(&self, err: &[u8], status: Option) -> Option { - if let Some(reason) = SkipReason::decode(err) { - return Some(reason.to_string()); - } - // Solidity's `Error(string)` (handled separately in order to strip revert: prefix) if let Some(ContractError(Revert(revert))) = RevertReason::decode(err) { return Some(revert.reason); @@ -274,4 +270,13 @@ mod tests { ); assert_eq!(decoder.decode(data, None), "ValidationFailed(0x756688fe)"); } + + #[test] + fn maybe_decode_magic_skip_is_not_skip_marker() { + let decoder = RevertDecoder::new(); + let reason = decoder.maybe_decode(crate::constants::MAGIC_SKIP, None).unwrap(); + + assert_eq!(reason, "FOUNDRY::SKIP"); + assert!(SkipReason::decode_self(&reason).is_none()); + } } diff --git a/crates/evm/core/src/evm.rs b/crates/evm/core/src/evm.rs deleted file mode 100644 index 814f6e58d6fe1..0000000000000 --- a/crates/evm/core/src/evm.rs +++ /dev/null @@ -1,1170 +0,0 @@ -use std::{ - fmt::{Debug, Display}, - marker::PhantomData, - ops::{Deref, DerefMut}, -}; - -use crate::{ - FoundryBlock, FoundryContextExt, FoundryInspectorExt, FoundryTransaction, - FromAnyRpcTransaction, - backend::{DatabaseExt, JournaledState}, - constants::{CALLER, DEFAULT_CREATE2_DEPLOYER_CODEHASH, TEST_CONTRACT_ADDRESS}, - tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner}, -}; -use alloy_consensus::{ - SignableTransaction, Signed, constants::KECCAK_EMPTY, transaction::SignerRecoverable, -}; -use alloy_evm::{ - EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, eth::EthEvmContext, - precompiles::PrecompilesMap, -}; -use alloy_network::{Ethereum, Network}; -use alloy_primitives::{Address, Bytes, Signature, U256}; -use alloy_rlp::Decodable; -use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt}; -use foundry_config::FromEvmVersion; -use foundry_fork_db::{DatabaseError, ForkBlockEnv}; -use op_alloy_network::Optimism; -use op_revm::OpHaltReason; -use revm::{ - Context, - context::{ - BlockEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContextTr, TxEnv, - result::{EVMError, ExecResultAndState, ExecutionResult, HaltReason, ResultAndState}, - }, - handler::{ - EthFrame, EvmTr, FrameResult, FrameTr, Handler, ItemOrResult, instructions::EthInstructions, - }, - inspector::{InspectorEvmTr, InspectorHandler}, - interpreter::{ - CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, - FrameInput, Gas, InstructionResult, InterpreterResult, SharedMemory, - interpreter::EthInterpreter, interpreter_action::FrameInit, return_ok, - }, - primitives::hardfork::SpecId, - state::Bytecode, -}; -use serde::{Deserialize, Serialize}; -use tempo_alloy::TempoNetwork; -use tempo_chainspec::hardfork::TempoHardfork; -use tempo_evm::evm::TempoEvmFactory; -use tempo_precompiles::storage::StorageCtx; -use tempo_revm::{ - TempoBlockEnv, TempoHaltReason, TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, - gas_params::tempo_gas_params, handler::TempoEvmHandler, -}; - -/// Converts a network-specific halt reason into an [`InstructionResult`]. -pub trait IntoInstructionResult { - fn into_instruction_result(self) -> InstructionResult; -} - -impl IntoInstructionResult for HaltReason { - fn into_instruction_result(self) -> InstructionResult { - self.into() - } -} - -impl IntoInstructionResult for OpHaltReason { - fn into_instruction_result(self) -> InstructionResult { - match self { - Self::Base(eth) => eth.into(), - Self::FailedDeposit => InstructionResult::Stop, - } - } -} - -impl IntoInstructionResult for TempoHaltReason { - fn into_instruction_result(self) -> InstructionResult { - match self { - Self::Ethereum(eth) => eth.into(), - _ => InstructionResult::PrecompileError, - } - } -} - -/// Foundry's supertrait associating [Network] with [FoundryEvmFactory] -pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static { - type Network: Network< - TxEnvelope: Decodable - + SignerRecoverable - + From::UnsignedTx>> - + for<'d> Deserialize<'d> - + Serialize - + UIfmt, - UnsignedTx: SignableTransaction, - TransactionRequest: FoundryTransactionBuilder - + for<'d> Deserialize<'d> - + Serialize, - ReceiptResponse: FoundryReceiptResponse, - >; - type EvmFactory: FoundryEvmFactory::TxEnvelope>>; -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct EthEvmNetwork; -impl FoundryEvmNetwork for EthEvmNetwork { - type Network = Ethereum; - type EvmFactory = EthEvmFactory; -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct TempoEvmNetwork; -impl FoundryEvmNetwork for TempoEvmNetwork { - type Network = TempoNetwork; - type EvmFactory = TempoEvmFactory; -} - -// TODO: use `OpEvmFactory` once the FoundryEvmFactory impl is available for it. -#[derive(Clone, Copy, Debug, Default)] -pub struct OpEvmNetwork; -impl FoundryEvmNetwork for OpEvmNetwork { - type Network = Optimism; - type EvmFactory = EthEvmFactory; -} - -/// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`]. -pub type EvmFactoryFor = ::EvmFactory; -pub type FoundryContextFor<'db, FEN> = - as FoundryEvmFactory>::FoundryContext<'db>; -pub type TxEnvFor = as EvmFactory>::Tx; -pub type HaltReasonFor = as EvmFactory>::HaltReason; -pub type SpecFor = as EvmFactory>::Spec; -pub type BlockEnvFor = as EvmFactory>::BlockEnv; -pub type PrecompilesFor = as EvmFactory>::Precompiles; -pub type EvmEnvFor = EvmEnv, BlockEnvFor>; - -pub type NetworkFor = ::Network; -pub type TxEnvelopeFor = as Network>::TxEnvelope; -pub type TransactionRequestFor = as Network>::TransactionRequest; -pub type TransactionResponseFor = as Network>::TransactionResponse; -pub type BlockResponseFor = as Network>::BlockResponse; - -pub trait FoundryEvmFactory: - EvmFactory< - Spec: Into + FromEvmVersion + Default + Display + Copy + Unpin + Send + 'static, - BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin, - Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync, - HaltReason: IntoInstructionResult, - Precompiles = PrecompilesMap, - > + Clone - + Debug - + Default - + 'static -{ - /// Foundry Context abstraction - type FoundryContext<'db>: FoundryContextExt< - Block = Self::BlockEnv, - Tx = Self::Tx, - Spec = Self::Spec, - Db: DatabaseExt, - > - where - Self: 'db; - - /// The Foundry-wrapped EVM type produced by this factory. - type FoundryEvm<'db, I: FoundryInspectorExt>>: Evm< - DB = &'db mut dyn DatabaseExt, - Tx = Self::Tx, - BlockEnv = Self::BlockEnv, - Spec = Self::Spec, - HaltReason = Self::HaltReason, - > + Deref> - + IntoNestedEvm - where - Self: 'db; - - /// Creates a Foundry-wrapped EVM with the given inspector. - fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( - &self, - db: &'db mut dyn DatabaseExt, - evm_env: EvmEnv, - inspector: I, - ) -> Self::FoundryEvm<'db, I>; - - /// Creates a Foundry-wrapped EVM with a dynamic inspector, returning a boxed [`NestedEvm`]. - /// - /// This helper exists because `&mut dyn FoundryInspectorExt` cannot satisfy - /// the generic `I: FoundryInspectorExt>` bound when the context - /// type is only known through an associated type. Each concrete factory implements this - /// directly, side-stepping the higher-kinded lifetime issue. - fn create_foundry_nested_evm<'db>( - &self, - db: &'db mut dyn DatabaseExt, - evm_env: EvmEnv, - inspector: &'db mut dyn FoundryInspectorExt>, - ) -> Box + 'db>; -} - -impl FoundryEvmFactory for EthEvmFactory { - type FoundryContext<'db> = EthEvmContext<&'db mut dyn DatabaseExt>; - - type FoundryEvm<'db, I: FoundryInspectorExt>> = EthFoundryEvm<'db, I>; - - fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( - &self, - db: &'db mut dyn DatabaseExt, - evm_env: EvmEnv, - inspector: I, - ) -> Self::FoundryEvm<'db, I> { - let eth_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); - let mut inner = eth_evm.into_inner(); - inner.ctx.cfg.tx_chain_id_check = true; - - let mut evm = EthFoundryEvm { inner }; - evm.inspector().get_networks().inject_precompiles(evm.precompiles_mut()); - evm - } - - fn create_foundry_nested_evm<'db>( - &self, - db: &'db mut dyn DatabaseExt, - evm_env: EvmEnv, - inspector: &'db mut dyn FoundryInspectorExt>, - ) -> Box + 'db> { - Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_nested_evm()) - } -} - -/// Get the call inputs for the CREATE2 factory. -fn get_create2_factory_call_inputs( - salt: U256, - inputs: &CreateInputs, - deployer: Address, -) -> CallInputs { - let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat(); - CallInputs { - caller: inputs.caller(), - bytecode_address: deployer, - known_bytecode: None, - target_address: deployer, - scheme: CallScheme::Call, - value: CallValue::Transfer(inputs.value()), - input: CallInput::Bytes(calldata.into()), - gas_limit: inputs.gas_limit(), - is_static: false, - return_memory_offset: 0..0, - } -} - -type EthRevmEvm<'db, I> = RevmEvm< - EthEvmContext<&'db mut dyn DatabaseExt>, - I, - EthInstructions>>, - PrecompilesMap, - EthFrame, ->; - -pub struct EthFoundryEvm< - 'db, - I: FoundryInspectorExt>>, -> { - pub inner: EthRevmEvm<'db, I>, -} - -impl<'db, I: FoundryInspectorExt>>> Evm - for EthFoundryEvm<'db, I> -{ - type Precompiles = PrecompilesMap; - type Inspector = I; - type DB = &'db mut dyn DatabaseExt; - type Error = EVMError; - type HaltReason = HaltReason; - type Spec = SpecId; - type Tx = TxEnv; - type BlockEnv = BlockEnv; - - fn block(&self) -> &BlockEnv { - &self.inner.block - } - - fn chain_id(&self) -> u64 { - self.inner.ctx.cfg.chain_id - } - - fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { - (&self.inner.ctx.journaled_state.database, &self.inner.inspector, &self.inner.precompiles) - } - - fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { - ( - &mut self.inner.ctx.journaled_state.database, - &mut self.inner.inspector, - &mut self.inner.precompiles, - ) - } - - fn set_inspector_enabled(&mut self, _enabled: bool) { - unimplemented!("FoundryEvm is always inspecting") - } - - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, Self::Error> { - self.inner.set_tx(tx); - - let mut handler = EthFoundryHandler::::default(); - let result = handler.inspect_run(&mut self.inner)?; - - Ok(ResultAndState::new(result, self.inner.ctx.journaled_state.inner.state.clone())) - } - - fn transact_system_call( - &mut self, - _caller: Address, - _contract: Address, - _data: Bytes, - ) -> Result, Self::Error> { - unimplemented!() - } - - fn finish(self) -> (Self::DB, EvmEnv) - where - Self: Sized, - { - let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx; - - (journaled_state.database, EvmEnv { block_env, cfg_env }) - } -} - -impl<'db, I: FoundryInspectorExt>>> Deref - for EthFoundryEvm<'db, I> -{ - type Target = EthEvmContext<&'db mut dyn DatabaseExt>; - - fn deref(&self) -> &Self::Target { - &self.inner.ctx - } -} - -impl<'db, I: FoundryInspectorExt>>> DerefMut - for EthFoundryEvm<'db, I> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner.ctx - } -} - -/// Trait for converting a Foundry EVM wrapper into its inner `NestedEvm` implementation. -/// -/// Both [`EthFoundryEvm`] and [`TempoFoundryEvm`] wrap an inner revm EVM that implements -/// [`NestedEvm`]. This trait provides a uniform way to unwrap them. -pub trait IntoNestedEvm { - /// The inner type that implements [`NestedEvm`]. - type Inner: NestedEvm; - - /// Consumes the wrapper, returning the inner revm EVM. - fn into_nested_evm(self) -> Self::Inner; -} - -impl<'db, I: FoundryInspectorExt>>> - IntoNestedEvm for EthFoundryEvm<'db, I> -{ - type Inner = EthRevmEvm<'db, I>; - - fn into_nested_evm(self) -> Self::Inner { - self.inner - } -} - -/// Object-safe trait exposing the operations that cheatcode nested EVM closures need. -/// -/// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.) -/// so that cheatcode impls can build and run nested EVMs without knowing the concrete type. -pub trait NestedEvm { - /// The spec type. - type Spec; - /// The block environment type. - type Block; - /// The transaction environment type. - type Tx; - - /// Returns a mutable reference to the journal inner state (`JournaledState`). - fn journal_inner_mut(&mut self) -> &mut JournaledState; - - /// Runs a single execution frame (create or call) through the EVM handler loop. - fn run_execution(&mut self, frame: FrameInput) -> Result>; - - /// Executes a full transaction with the given tx env. - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, EVMError>; - - fn to_evm_env(&self) -> EvmEnv; -} - -impl<'db, I: FoundryInspectorExt>>> NestedEvm - for EthRevmEvm<'db, I> -{ - type Spec = SpecId; - type Block = BlockEnv; - type Tx = TxEnv; - - fn journal_inner_mut(&mut self) -> &mut JournaledState { - &mut self.ctx_mut().journaled_state.inner - } - - fn run_execution(&mut self, frame: FrameInput) -> Result> { - let mut handler = EthFoundryHandler::::default(); - - // Create first frame - let memory = - SharedMemory::new_with_buffer(self.ctx().local().shared_memory_buffer().clone()); - let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; - - // Run execution loop - let mut frame_result = handler.inspect_run_exec_loop(self, first_frame_input)?; - - // Handle last frame result - handler.last_frame_result(self, &mut frame_result)?; - - Ok(frame_result) - } - - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, EVMError> { - self.set_tx(tx); - - let mut handler = EthFoundryHandler::::default(); - let result = handler.inspect_run(self)?; - - Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone())) - } - - fn to_evm_env(&self) -> EvmEnv { - self.ctx_ref().evm_clone() - } -} - -/// Closure type used by `CheatcodesExecutor` methods that run nested EVM operations. -pub type NestedEvmClosure<'a, Spec, Block, Tx> = - &'a mut dyn FnMut( - &mut dyn NestedEvm, - ) -> Result<(), EVMError>; - -/// Clones the current context (env + journal), passes the database, cloned env, -/// and cloned journal inner to the callback. The callback builds whatever EVM it -/// needs, runs its operations, and returns `(result, modified_env, modified_journal)`. -/// Modified state is written back after the callback returns. -pub fn with_cloned_context( - ecx: &mut CTX, - f: impl FnOnce( - &mut CTX::Db, - EvmEnv, - JournaledState, - ) - -> Result<(EvmEnv, JournaledState), EVMError>, -) -> Result<(), EVMError> { - let evm_env = ecx.evm_clone(); - - let (db, journal_inner) = ecx.db_journal_inner_mut(); - let journal_inner_clone = journal_inner.clone(); - - let (sub_evm_env, sub_inner) = f(db, evm_env, journal_inner_clone)?; - - // Write back modified state. The db borrow was released when f returned. - ecx.set_journal_inner(sub_inner); - ecx.set_evm(sub_evm_env); - - Ok(()) -} - -pub struct EthFoundryHandler< - 'db, - I: FoundryInspectorExt>>, -> { - create2_overrides: Vec<(usize, CallInputs)>, - _phantom: PhantomData<(&'db mut dyn DatabaseExt, I)>, -} - -impl<'db, I: FoundryInspectorExt>>> Default - for EthFoundryHandler<'db, I> -{ - fn default() -> Self { - Self { create2_overrides: Vec::new(), _phantom: PhantomData } - } -} - -// Blanket Handler implementation for FoundryHandler, needed for implementing the InspectorHandler -// trait. -impl<'db, I: FoundryInspectorExt>>> Handler - for EthFoundryHandler<'db, I> -{ - type Evm = RevmEvm< - EthEvmContext<&'db mut dyn DatabaseExt>, - I, - EthInstructions>>, - PrecompilesMap, - EthFrame, - >; - type Error = EVMError; - type HaltReason = HaltReason; -} - -impl<'db, I: FoundryInspectorExt>>> - EthFoundryHandler<'db, I> -{ - /// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2 - /// factory. - fn handle_create_frame( - &mut self, - evm: &mut ::Evm, - init: &mut FrameInit, - ) -> Result, ::Error> { - if let FrameInput::Create(inputs) = &init.frame_input - && let CreateScheme::Create2 { salt } = inputs.scheme() - { - let (ctx, inspector) = evm.ctx_inspector(); - - if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) { - let gas_limit = inputs.gas_limit(); - - // Get CREATE2 deployer. - let create2_deployer = evm.inspector().create2_deployer(); - - // Generate call inputs for CREATE2 factory. - let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer); - - // Push data about current override to the stack. - self.create2_overrides.push((evm.journal().depth(), call_inputs.clone())); - - // Sanity check that CREATE2 deployer exists. - let code_hash = evm.journal_mut().load_account(create2_deployer)?.info.code_hash; - if code_hash == KECCAK_EMPTY { - return Ok(Some(FrameResult::Call(CallOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: Bytes::from( - format!("missing CREATE2 deployer: {create2_deployer}") - .into_bytes(), - ), - gas: Gas::new(gas_limit), - }, - memory_offset: 0..0, - was_precompile_called: false, - precompile_call_logs: vec![], - }))); - } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { - return Ok(Some(FrameResult::Call(CallOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: "invalid CREATE2 deployer bytecode".into(), - gas: Gas::new(gas_limit), - }, - memory_offset: 0..0, - was_precompile_called: false, - precompile_call_logs: vec![], - }))); - } - - // Rewrite the frame init - init.frame_input = FrameInput::Call(Box::new(call_inputs)); - } - } - Ok(None) - } - - /// Transforms CREATE2 factory call results back into CREATE outcomes. - fn handle_create2_override( - &mut self, - evm: &mut ::Evm, - result: FrameResult, - ) -> FrameResult { - if self.create2_overrides.last().is_some_and(|(depth, _)| *depth == evm.journal().depth()) { - let (_, call_inputs) = self.create2_overrides.pop().unwrap(); - let FrameResult::Call(mut call) = result else { - unreachable!("create2 override should be a call frame"); - }; - - // Decode address from output. - let address = match call.instruction_result() { - return_ok!() => Address::try_from(call.output().as_ref()) - .map_err(|_| { - call.result = InterpreterResult { - result: InstructionResult::Revert, - output: "invalid CREATE2 factory output".into(), - gas: Gas::new(call_inputs.gas_limit), - }; - }) - .ok(), - _ => None, - }; - - FrameResult::Create(CreateOutcome { result: call.result, address }) - } else { - result - } - } -} - -impl<'db, I: FoundryInspectorExt>>> - InspectorHandler for EthFoundryHandler<'db, I> -{ - type IT = EthInterpreter; - - fn inspect_run_exec_loop( - &mut self, - evm: &mut Self::Evm, - first_frame_input: <::Frame as FrameTr>::FrameInit, - ) -> Result { - let res = evm.inspect_frame_init(first_frame_input)?; - - if let ItemOrResult::Result(frame_result) = res { - return Ok(frame_result); - } - - loop { - let call_or_result = evm.inspect_frame_run()?; - - let result = match call_or_result { - ItemOrResult::Item(mut init) => { - // Handle CREATE/CREATE2 frame initialization - if let Some(frame_result) = self.handle_create_frame(evm, &mut init)? { - return Ok(frame_result); - } - - match evm.inspect_frame_init(init)? { - ItemOrResult::Item(_) => continue, - ItemOrResult::Result(result) => result, - } - } - ItemOrResult::Result(result) => result, - }; - - // Handle CREATE2 override transformation if needed - let result = self.handle_create2_override(evm, result); - - if let Some(result) = evm.frame_return_result(result)? { - return Ok(result); - } - } - } -} - -// Will be removed when the next revm release includes bluealloy/revm#3518. -type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt, I>; - -/// Tempo counterpart of [`EthFoundryEvm`]. Wraps `tempo_revm::TempoEvm` and routes execution -/// through [`TempoFoundryHandler`] which composes [`TempoEvmHandler`] with CREATE2 factory -/// redirect logic. -/// -/// Uses [`TempoEvmFactory`] for construction to reuse factory setup logic, then unwraps to the -/// raw revm EVM via `into_inner()` since the handler operates at the revm level. -pub struct TempoFoundryEvm< - 'db, - I: FoundryInspectorExt>>, -> { - pub inner: TempoRevmEvm<'db, I>, -} - -/// Initialize Tempo precompiles and contracts for a newly created [`TempoFoundryEvm`]. -/// -/// In non-fork mode, runs full genesis initialization (precompile sentinel bytecode, -/// TIP20 fee tokens, standard contracts) via [`StorageCtx::enter_evm`]. -/// -/// In fork mode, warms up precompile and TIP20 token addresses with sentinel bytecode -/// to prevent repeated RPC round-trips for addresses that are Rust-native precompiles -/// on Tempo nodes (no real EVM bytecode on-chain). -fn initialize_tempo_evm< - 'db, - I: FoundryInspectorExt>>, ->( - evm: &mut TempoFoundryEvm<'db, I>, - is_forked: bool, -) { - let ctx = &mut evm.inner.inner.ctx; - StorageCtx::enter_evm(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx, || { - if is_forked { - // In fork mode, warm up precompile accounts to avoid repeated RPC fetches. - let mut sctx = StorageCtx; - let sentinel = Bytecode::new_legacy(Bytes::from_static(&[0xef])); - for addr in TEMPO_PRECOMPILE_ADDRESSES.iter().chain(TEMPO_TIP20_TOKENS.iter()) { - sctx.set_code(*addr, sentinel.clone()) - .expect("failed to warm tempo precompile address"); - } - } else { - // In non-fork mode, run full genesis initialization. - initialize_tempo_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER) - .expect("tempo genesis initialization failed"); - } - }); -} - -impl FoundryEvmFactory for TempoEvmFactory { - type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt>; - - type FoundryEvm<'db, I: FoundryInspectorExt>> = - TempoFoundryEvm<'db, I>; - - fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( - &self, - db: &'db mut dyn DatabaseExt, - evm_env: EvmEnv, - inspector: I, - ) -> Self::FoundryEvm<'db, I> { - let is_forked = db.is_forked_mode(); - let spec = *evm_env.spec_id(); - let tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); - let mut inner = tempo_evm.into_inner(); - inner.ctx.cfg.gas_params = tempo_gas_params(spec); - inner.ctx.cfg.tx_chain_id_check = true; - - let mut evm = TempoFoundryEvm { inner }; - let networks = Evm::inspector(&evm).get_networks(); - networks.inject_precompiles(evm.precompiles_mut()); - - initialize_tempo_evm(&mut evm, is_forked); - evm - } - - fn create_foundry_nested_evm<'db>( - &self, - db: &'db mut dyn DatabaseExt, - evm_env: EvmEnv, - inspector: &'db mut dyn FoundryInspectorExt>, - ) -> Box + 'db> - { - Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_nested_evm()) - } -} - -impl<'db, I: FoundryInspectorExt>>> Evm - for TempoFoundryEvm<'db, I> -{ - type Precompiles = PrecompilesMap; - type Inspector = I; - type DB = &'db mut dyn DatabaseExt; - type Error = EVMError; - type HaltReason = TempoHaltReason; - type Spec = TempoHardfork; - type Tx = TempoTxEnv; - type BlockEnv = TempoBlockEnv; - - fn block(&self) -> &TempoBlockEnv { - &self.inner.block - } - - fn chain_id(&self) -> u64 { - self.inner.ctx.cfg.chain_id - } - - fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { - let evm = &self.inner.inner; - (&evm.ctx.journaled_state.database, &evm.inspector, &evm.precompiles) - } - - fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { - let evm = &mut self.inner.inner; - (&mut evm.ctx.journaled_state.database, &mut evm.inspector, &mut evm.precompiles) - } - - fn set_inspector_enabled(&mut self, _enabled: bool) { - unimplemented!("TempoFoundryEvm is always inspecting") - } - - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, Self::Error> { - self.inner.set_tx(tx); - - let mut handler = TempoFoundryHandler::::default(); - let result = handler.inspect_run(&mut self.inner)?; - - Ok(ResultAndState::new(result, self.inner.inner.ctx.journaled_state.inner.state.clone())) - } - - fn transact_system_call( - &mut self, - _caller: Address, - _contract: Address, - _data: Bytes, - ) -> Result>, Self::Error> { - unimplemented!() - } - - fn finish(self) -> (Self::DB, EvmEnv) - where - Self: Sized, - { - let revm_evm = self.inner.inner; - let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = revm_evm.ctx; - (journaled_state.database, EvmEnv { block_env, cfg_env }) - } -} - -impl<'db, I: FoundryInspectorExt>>> Deref - for TempoFoundryEvm<'db, I> -{ - type Target = TempoContext<&'db mut dyn DatabaseExt>; - - fn deref(&self) -> &Self::Target { - &self.inner.ctx - } -} - -impl<'db, I: FoundryInspectorExt>>> DerefMut - for TempoFoundryEvm<'db, I> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner.ctx - } -} - -impl<'db, I: FoundryInspectorExt>>> - IntoNestedEvm for TempoFoundryEvm<'db, I> -{ - type Inner = TempoRevmEvm<'db, I>; - - fn into_nested_evm(self) -> Self::Inner { - self.inner - } -} - -/// Maps a Tempo [`EVMError`] to the common `EVMError` used by [`NestedEvm`]. -/// -/// This exists because [`NestedEvm`] currently uses Eth-typed errors. When `NestedEvm` gains -/// an associated `Error` type, this mapping can be removed. -fn map_tempo_error(e: EVMError) -> EVMError { - match e { - EVMError::Database(db) => EVMError::Database(db), - EVMError::Header(h) => EVMError::Header(h), - EVMError::Custom(s) => EVMError::Custom(s), - EVMError::Transaction(t) => match t { - TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth), - t => EVMError::Custom(format!("tempo transaction error: {t}")), - }, - } -} - -impl<'db, I: FoundryInspectorExt>>> NestedEvm - for TempoRevmEvm<'db, I> -{ - type Spec = TempoHardfork; - type Block = TempoBlockEnv; - type Tx = TempoTxEnv; - - fn journal_inner_mut(&mut self) -> &mut JournaledState { - &mut self.ctx_mut().journaled_state.inner - } - - fn run_execution(&mut self, frame: FrameInput) -> Result> { - let mut handler = TempoFoundryHandler::::default(); - - let memory = - SharedMemory::new_with_buffer(self.ctx().local().shared_memory_buffer().clone()); - let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; - - let mut frame_result = - handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_tempo_error)?; - - handler.last_frame_result(self, &mut frame_result).map_err(map_tempo_error)?; - - Ok(frame_result) - } - - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, EVMError> { - self.set_tx(tx); - - let mut handler = TempoFoundryHandler::::default(); - let result = handler.inspect_run(self).map_err(map_tempo_error)?; - - let result = result.map_haltreason(|h| match h { - TempoHaltReason::Ethereum(eth) => eth, - _ => HaltReason::PrecompileError, - }); - - Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone())) - } - - fn to_evm_env(&self) -> EvmEnv { - self.ctx_ref().evm_clone() - } -} - -/// Tempo counterpart of [`EthFoundryHandler`]. Wraps [`TempoEvmHandler`] and injects CREATE2 -/// factory redirect logic into the execution loop. Delegates all [`Handler`] methods to -/// [`TempoEvmHandler`] for proper Tempo validation, fee collection, AA dispatch, and gas -/// handling. -/// -/// Will be removed when the next revm release includes bluealloy/revm#3518. -pub struct TempoFoundryHandler< - 'db, - I: FoundryInspectorExt>>, -> { - inner: TempoEvmHandler<&'db mut dyn DatabaseExt, I>, - create2_overrides: Vec<(usize, CallInputs)>, -} - -impl<'db, I: FoundryInspectorExt>>> Default - for TempoFoundryHandler<'db, I> -{ - fn default() -> Self { - Self { inner: TempoEvmHandler::new(), create2_overrides: Vec::new() } - } -} - -impl<'db, I: FoundryInspectorExt>>> Handler - for TempoFoundryHandler<'db, I> -{ - type Evm = TempoRevmEvm<'db, I>; - type Error = EVMError; - type HaltReason = TempoHaltReason; - - #[inline] - fn run( - &mut self, - evm: &mut Self::Evm, - ) -> Result, Self::Error> { - self.inner.run(evm) - } - - #[inline] - fn execution( - &mut self, - evm: &mut Self::Evm, - init_and_floor_gas: &revm::interpreter::InitialAndFloorGas, - ) -> Result { - self.inner.execution(evm, init_and_floor_gas) - } - - #[inline] - fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { - self.inner.validate_env(evm) - } - - #[inline] - fn validate_against_state_and_deduct_caller( - &self, - evm: &mut Self::Evm, - ) -> Result<(), Self::Error> { - self.inner.validate_against_state_and_deduct_caller(evm) - } - - #[inline] - fn reimburse_caller( - &self, - evm: &mut Self::Evm, - exec_result: &mut <::Frame as FrameTr>::FrameResult, - ) -> Result<(), Self::Error> { - self.inner.reimburse_caller(evm, exec_result) - } - - #[inline] - fn reward_beneficiary( - &self, - evm: &mut Self::Evm, - exec_result: &mut <::Frame as FrameTr>::FrameResult, - ) -> Result<(), Self::Error> { - self.inner.reward_beneficiary(evm, exec_result) - } - - #[inline] - fn validate_initial_tx_gas( - &self, - evm: &mut Self::Evm, - ) -> Result { - self.inner.validate_initial_tx_gas(evm) - } - - #[inline] - fn execution_result( - &mut self, - evm: &mut Self::Evm, - result: <::Frame as FrameTr>::FrameResult, - result_gas: revm::context::result::ResultGas, - ) -> Result, Self::Error> { - self.inner.execution_result(evm, result, result_gas) - } - - #[inline] - fn catch_error( - &self, - evm: &mut Self::Evm, - error: Self::Error, - ) -> Result, Self::Error> { - self.inner.catch_error(evm, error) - } -} - -/// CREATE2 factory redirect execution loop for Tempo. -fn create2_exec_loop< - 'db, - I: FoundryInspectorExt>>, ->( - create2_overrides: &mut Vec<(usize, CallInputs)>, - evm: &mut TempoRevmEvm<'db, I>, - first_frame_input: FrameInit, -) -> Result> { - let res = evm.inspect_frame_init(first_frame_input)?; - - if let ItemOrResult::Result(frame_result) = res { - return Ok(frame_result); - } - - loop { - let call_or_result = evm.inspect_frame_run()?; - - let result = match call_or_result { - ItemOrResult::Item(mut init) => { - if let Some(frame_result) = handle_create2_frame(create2_overrides, evm, &mut init)? - { - return Ok(frame_result); - } - - match evm.inspect_frame_init(init)? { - ItemOrResult::Item(_) => continue, - ItemOrResult::Result(result) => result, - } - } - ItemOrResult::Result(result) => result, - }; - - let result = handle_create2_result(create2_overrides, evm, result); - - if let Some(result) = evm.frame_return_result(result)? { - return Ok(result); - } - } -} - -/// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2 factory. -fn handle_create2_frame< - 'db, - I: FoundryInspectorExt>>, ->( - create2_overrides: &mut Vec<(usize, CallInputs)>, - evm: &mut TempoRevmEvm<'db, I>, - init: &mut FrameInit, -) -> Result, EVMError> { - if let FrameInput::Create(inputs) = &init.frame_input - && let CreateScheme::Create2 { salt } = inputs.scheme() - { - let (ctx, inspector) = evm.ctx_inspector(); - - if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) { - let gas_limit = inputs.gas_limit(); - let create2_deployer = evm.inspector().create2_deployer(); - let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer); - - create2_overrides.push((evm.journal().depth(), call_inputs.clone())); - - let code_hash = evm.journal_mut().load_account(create2_deployer)?.info.code_hash; - if code_hash == KECCAK_EMPTY { - return Ok(Some(FrameResult::Call(CallOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: Bytes::from( - format!("missing CREATE2 deployer: {create2_deployer}").into_bytes(), - ), - gas: Gas::new(gas_limit), - }, - memory_offset: 0..0, - was_precompile_called: false, - precompile_call_logs: vec![], - }))); - } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { - return Ok(Some(FrameResult::Call(CallOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: "invalid CREATE2 deployer bytecode".into(), - gas: Gas::new(gas_limit), - }, - memory_offset: 0..0, - was_precompile_called: false, - precompile_call_logs: vec![], - }))); - } - - init.frame_input = FrameInput::Call(Box::new(call_inputs)); - } - } - Ok(None) -} - -/// Transforms CREATE2 factory call results back into CREATE outcomes. -fn handle_create2_result< - 'db, - I: FoundryInspectorExt>>, ->( - create2_overrides: &mut Vec<(usize, CallInputs)>, - evm: &mut TempoRevmEvm<'db, I>, - result: FrameResult, -) -> FrameResult { - if create2_overrides.last().is_some_and(|(depth, _)| *depth == evm.journal().depth()) { - let (_, call_inputs) = create2_overrides.pop().unwrap(); - let FrameResult::Call(mut call) = result else { - unreachable!("create2 override should be a call frame"); - }; - - let address = match call.instruction_result() { - return_ok!() => Address::try_from(call.output().as_ref()) - .map_err(|_| { - call.result = InterpreterResult { - result: InstructionResult::Revert, - output: "invalid CREATE2 factory output".into(), - gas: Gas::new(call_inputs.gas_limit), - }; - }) - .ok(), - _ => None, - }; - - FrameResult::Create(CreateOutcome { result: call.result, address }) - } else { - result - } -} - -impl<'db, I: FoundryInspectorExt>>> - InspectorHandler for TempoFoundryHandler<'db, I> -{ - type IT = EthInterpreter; - - /// Overrides `inspect_run` to load Tempo fee fields before delegating to the default - /// execution pipeline. This is necessary because the default `inspect_run` calls - /// `validate` → `validate_against_state_and_deduct_caller` directly, bypassing - /// `Handler::run` where `load_fee_fields` is normally invoked. - fn inspect_run( - &mut self, - evm: &mut Self::Evm, - ) -> Result, Self::Error> { - self.inner.load_fee_fields(evm)?; - - match self.inspect_run_without_catch_error(evm) { - Ok(output) => Ok(output), - Err(e) => self.catch_error(evm, e), - } - } - - /// Delegates to [`TempoEvmHandler::inspect_execution_with`], injecting the CREATE2 factory - /// redirect exec loop. AA multi-call dispatch and gas adjustments are handled by the inner - /// Tempo handler. - #[inline] - fn inspect_execution( - &mut self, - evm: &mut Self::Evm, - init_and_floor_gas: &revm::interpreter::InitialAndFloorGas, - ) -> Result { - let overrides = &mut self.create2_overrides; - self.inner.inspect_execution_with(evm, init_and_floor_gas, |_handler, evm, init| { - create2_exec_loop(overrides, evm, init) - }) - } - - fn inspect_run_exec_loop( - &mut self, - evm: &mut Self::Evm, - first_frame_input: <::Frame as FrameTr>::FrameInit, - ) -> Result { - create2_exec_loop(&mut self.create2_overrides, evm, first_frame_input) - } -} diff --git a/crates/evm/core/src/evm/eth.rs b/crates/evm/core/src/evm/eth.rs new file mode 100644 index 0000000000000..fae182ae639b6 --- /dev/null +++ b/crates/evm/core/src/evm/eth.rs @@ -0,0 +1,358 @@ +use super::*; + +pub type EthRevmEvm<'db, I> = RevmEvm< + EthEvmContext<&'db mut dyn DatabaseExt>, + I, + EthInstructions>>, + PrecompilesMap, + EthFrame, +>; + +pub struct EthFoundryEvm< + 'db, + I: FoundryInspectorExt>>, +> { + pub inner: EthRevmEvm<'db, I>, +} + +impl<'db, I: FoundryInspectorExt>>> Evm + for EthFoundryEvm<'db, I> +{ + type Precompiles = PrecompilesMap; + type Inspector = I; + type DB = &'db mut dyn DatabaseExt; + type Error = EVMError; + type HaltReason = HaltReason; + type Spec = SpecId; + type Tx = TxEnv; + type BlockEnv = BlockEnv; + + fn block(&self) -> &BlockEnv { + &self.inner.block + } + + fn chain_id(&self) -> u64 { + self.inner.ctx.cfg.chain_id + } + + fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { + (&self.inner.ctx.journaled_state.database, &self.inner.inspector, &self.inner.precompiles) + } + + fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { + ( + &mut self.inner.ctx.journaled_state.database, + &mut self.inner.inspector, + &mut self.inner.precompiles, + ) + } + + fn set_inspector_enabled(&mut self, _enabled: bool) { + unimplemented!("FoundryEvm is always inspecting") + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, Self::Error> { + self.inner.set_tx(tx); + + let mut handler = EthFoundryHandler::::default(); + let result = handler.inspect_run(&mut self.inner)?; + + Ok(ResultAndState::new(result, self.inner.ctx.journaled_state.inner.state.clone())) + } + + fn transact_system_call( + &mut self, + _caller: Address, + _contract: Address, + _data: Bytes, + ) -> Result, Self::Error> { + unimplemented!() + } + + fn finish(self) -> (Self::DB, EvmEnv) + where + Self: Sized, + { + let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx; + + (journaled_state.database, EvmEnv { block_env, cfg_env }) + } +} + +impl<'db, I: FoundryInspectorExt>>> Deref + for EthFoundryEvm<'db, I> +{ + type Target = EthEvmContext<&'db mut dyn DatabaseExt>; + + fn deref(&self) -> &Self::Target { + &self.inner.ctx + } +} + +impl<'db, I: FoundryInspectorExt>>> DerefMut + for EthFoundryEvm<'db, I> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner.ctx + } +} + +impl<'db, I: FoundryInspectorExt>>> + IntoNestedEvm for EthFoundryEvm<'db, I> +{ + type Inner = EthRevmEvm<'db, I>; + + fn into_nested_evm(self) -> Self::Inner { + self.inner + } +} + +impl FoundryEvmFactory for EthEvmFactory { + type FoundryContext<'db> = EthEvmContext<&'db mut dyn DatabaseExt>; + + type FoundryEvm<'db, I: FoundryInspectorExt>> = EthFoundryEvm<'db, I>; + + fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: I, + ) -> Self::FoundryEvm<'db, I> { + let eth_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); + let mut inner = eth_evm.into_inner(); + inner.ctx.cfg.tx_chain_id_check = true; + + let mut evm = EthFoundryEvm { inner }; + evm.inspector().get_networks().inject_precompiles(evm.precompiles_mut()); + evm + } + + fn create_foundry_nested_evm<'db>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: &'db mut dyn FoundryInspectorExt>, + ) -> Box + 'db> { + Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_nested_evm()) + } +} + +impl<'db, I: FoundryInspectorExt>>> NestedEvm + for EthRevmEvm<'db, I> +{ + type Spec = SpecId; + type Block = BlockEnv; + type Tx = TxEnv; + + fn journal_inner_mut(&mut self) -> &mut JournaledState { + &mut self.ctx_mut().journaled_state.inner + } + + fn run_execution(&mut self, frame: FrameInput) -> Result> { + let mut handler = EthFoundryHandler::::default(); + + // Create first frame + let memory = + SharedMemory::new_with_buffer(self.ctx().local().shared_memory_buffer().clone()); + let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; + + // Run execution loop + let mut frame_result = handler.inspect_run_exec_loop(self, first_frame_input)?; + + // Handle last frame result + handler.last_frame_result(self, &mut frame_result)?; + + Ok(frame_result) + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, EVMError> { + self.set_tx(tx); + + let mut handler = EthFoundryHandler::::default(); + let result = handler.inspect_run(self)?; + + Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone())) + } + + fn to_evm_env(&self) -> EvmEnv { + self.ctx_ref().evm_clone() + } +} + +pub struct EthFoundryHandler< + 'db, + I: FoundryInspectorExt>>, +> { + create2_overrides: Vec<(usize, CallInputs)>, + _phantom: PhantomData<(&'db mut dyn DatabaseExt, I)>, +} + +impl<'db, I: FoundryInspectorExt>>> Default + for EthFoundryHandler<'db, I> +{ + fn default() -> Self { + Self { create2_overrides: Vec::new(), _phantom: PhantomData } + } +} + +// Blanket Handler implementation for FoundryHandler, needed for implementing the InspectorHandler +// trait. +impl<'db, I: FoundryInspectorExt>>> Handler + for EthFoundryHandler<'db, I> +{ + type Evm = RevmEvm< + EthEvmContext<&'db mut dyn DatabaseExt>, + I, + EthInstructions>>, + PrecompilesMap, + EthFrame, + >; + type Error = EVMError; + type HaltReason = HaltReason; +} + +impl<'db, I: FoundryInspectorExt>>> + EthFoundryHandler<'db, I> +{ + /// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2 + /// factory. + fn handle_create_frame( + &mut self, + evm: &mut ::Evm, + init: &mut FrameInit, + ) -> Result, ::Error> { + if let FrameInput::Create(inputs) = &init.frame_input + && let CreateScheme::Create2 { salt } = inputs.scheme() + { + let (ctx, inspector) = evm.ctx_inspector(); + + if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) { + let gas_limit = inputs.gas_limit(); + + // Get CREATE2 deployer. + let create2_deployer = evm.inspector().create2_deployer(); + + // Generate call inputs for CREATE2 factory. + let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer); + + // Push data about current override to the stack. + self.create2_overrides.push((evm.journal().depth(), call_inputs.clone())); + + // Sanity check that CREATE2 deployer exists. + let code_hash = evm.journal_mut().load_account(create2_deployer)?.info.code_hash; + if code_hash == KECCAK_EMPTY { + return Ok(Some(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Bytes::from( + format!("missing CREATE2 deployer: {create2_deployer}") + .into_bytes(), + ), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: vec![], + }))); + } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { + return Ok(Some(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 deployer bytecode".into(), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: vec![], + }))); + } + + // Rewrite the frame init + init.frame_input = FrameInput::Call(Box::new(call_inputs)); + } + } + Ok(None) + } + + /// Transforms CREATE2 factory call results back into CREATE outcomes. + fn handle_create2_override( + &mut self, + evm: &mut ::Evm, + result: FrameResult, + ) -> FrameResult { + if self.create2_overrides.last().is_some_and(|(depth, _)| *depth == evm.journal().depth()) { + let (_, call_inputs) = self.create2_overrides.pop().unwrap(); + let FrameResult::Call(mut call) = result else { + unreachable!("create2 override should be a call frame"); + }; + + // Decode address from output. + let address = match call.instruction_result() { + return_ok!() => Address::try_from(call.output().as_ref()) + .map_err(|_| { + call.result = InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 factory output".into(), + gas: Gas::new(call_inputs.gas_limit), + }; + }) + .ok(), + _ => None, + }; + + FrameResult::Create(CreateOutcome { result: call.result, address }) + } else { + result + } + } +} + +impl<'db, I: FoundryInspectorExt>>> + InspectorHandler for EthFoundryHandler<'db, I> +{ + type IT = EthInterpreter; + + fn inspect_run_exec_loop( + &mut self, + evm: &mut Self::Evm, + first_frame_input: <::Frame as FrameTr>::FrameInit, + ) -> Result { + let res = evm.inspect_frame_init(first_frame_input)?; + + if let ItemOrResult::Result(frame_result) = res { + return Ok(frame_result); + } + + loop { + let call_or_result = evm.inspect_frame_run()?; + + let result = match call_or_result { + ItemOrResult::Item(mut init) => { + // Handle CREATE/CREATE2 frame initialization + if let Some(frame_result) = self.handle_create_frame(evm, &mut init)? { + return Ok(frame_result); + } + + match evm.inspect_frame_init(init)? { + ItemOrResult::Item(_) => continue, + ItemOrResult::Result(result) => result, + } + } + ItemOrResult::Result(result) => result, + }; + + // Handle CREATE2 override transformation if needed + let result = self.handle_create2_override(evm, result); + + if let Some(result) = evm.frame_return_result(result)? { + return Ok(result); + } + } + } +} diff --git a/crates/evm/core/src/evm/mod.rs b/crates/evm/core/src/evm/mod.rs new file mode 100644 index 0000000000000..4dc6339706491 --- /dev/null +++ b/crates/evm/core/src/evm/mod.rs @@ -0,0 +1,301 @@ +use std::{ + fmt::Debug, + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + +use crate::{ + FoundryBlock, FoundryContextExt, FoundryInspectorExt, FoundryTransaction, + FromAnyRpcTransaction, + backend::{DatabaseExt, JournaledState}, + constants::{CALLER, DEFAULT_CREATE2_DEPLOYER_CODEHASH, TEST_CONTRACT_ADDRESS}, + tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner}, +}; +use alloy_consensus::{ + SignableTransaction, Signed, constants::KECCAK_EMPTY, transaction::SignerRecoverable, +}; +use alloy_evm::{ + EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, eth::EthEvmContext, + precompiles::PrecompilesMap, +}; +use alloy_network::{Ethereum, Network}; +use alloy_op_evm::OpEvmFactory; +use alloy_primitives::{Address, Bytes, Signature, U256}; +use alloy_rlp::Decodable; +use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt}; +use foundry_config::FromEvmVersion; +use foundry_fork_db::{DatabaseError, ForkBlockEnv}; +use op_alloy_network::Optimism; +use op_revm::{ + DefaultOp, OpBuilder, OpContext, OpHaltReason, OpSpecId, OpTransaction, handler::OpHandler, + precompiles::OpPrecompiles, transaction::error::OpTransactionError, +}; +use revm::{ + Context, + context::{ + BlockEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContextTr, TxEnv, + result::{EVMError, ExecResultAndState, ExecutionResult, HaltReason, ResultAndState}, + }, + handler::{ + EthFrame, EvmTr, FrameResult, FrameTr, Handler, ItemOrResult, instructions::EthInstructions, + }, + inspector::{InspectorEvmTr, InspectorHandler}, + interpreter::{ + CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, + FrameInput, Gas, InstructionResult, InterpreterResult, SharedMemory, + interpreter::EthInterpreter, interpreter_action::FrameInit, return_ok, + }, + primitives::hardfork::SpecId, + state::Bytecode, +}; +use serde::{Deserialize, Serialize}; +use tempo_alloy::TempoNetwork; +use tempo_chainspec::hardfork::TempoHardfork; +use tempo_evm::evm::TempoEvmFactory; +use tempo_precompiles::storage::StorageCtx; +use tempo_revm::{ + TempoBlockEnv, TempoHaltReason, TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, + gas_params::tempo_gas_params, handler::TempoEvmHandler, +}; + +pub mod eth; +pub mod op; +pub mod tempo; + +pub use eth::*; +pub use op::*; +pub use tempo::*; + +/// Foundry's supertrait associating [Network] with [FoundryEvmFactory] +pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static { + type Network: Network< + TxEnvelope: Decodable + + SignerRecoverable + + From::UnsignedTx>> + + for<'d> Deserialize<'d> + + Serialize + + UIfmt, + UnsignedTx: SignableTransaction, + TransactionRequest: FoundryTransactionBuilder + + for<'d> Deserialize<'d> + + Serialize, + ReceiptResponse: FoundryReceiptResponse, + >; + type EvmFactory: FoundryEvmFactory::TxEnvelope>>; +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct EthEvmNetwork; +impl FoundryEvmNetwork for EthEvmNetwork { + type Network = Ethereum; + type EvmFactory = EthEvmFactory; +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct TempoEvmNetwork; +impl FoundryEvmNetwork for TempoEvmNetwork { + type Network = TempoNetwork; + type EvmFactory = TempoEvmFactory; +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct OpEvmNetwork; +impl FoundryEvmNetwork for OpEvmNetwork { + type Network = Optimism; + type EvmFactory = OpEvmFactory; +} + +/// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`]. +pub type EvmFactoryFor = ::EvmFactory; +pub type FoundryContextFor<'db, FEN> = + as FoundryEvmFactory>::FoundryContext<'db>; +pub type TxEnvFor = as EvmFactory>::Tx; +pub type HaltReasonFor = as EvmFactory>::HaltReason; +pub type SpecFor = as EvmFactory>::Spec; +pub type BlockEnvFor = as EvmFactory>::BlockEnv; +pub type PrecompilesFor = as EvmFactory>::Precompiles; +pub type EvmEnvFor = EvmEnv, BlockEnvFor>; + +pub type NetworkFor = ::Network; +pub type TxEnvelopeFor = as Network>::TxEnvelope; +pub type TransactionRequestFor = as Network>::TransactionRequest; +pub type TransactionResponseFor = as Network>::TransactionResponse; +pub type BlockResponseFor = as Network>::BlockResponse; + +pub trait FoundryEvmFactory: + EvmFactory< + Spec: Into + FromEvmVersion + Default + Copy + Unpin + Send + 'static, + BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin, + Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync, + HaltReason: IntoInstructionResult, + Precompiles = PrecompilesMap, + > + Clone + + Debug + + Default + + 'static +{ + /// Foundry Context abstraction + type FoundryContext<'db>: FoundryContextExt< + Block = Self::BlockEnv, + Tx = Self::Tx, + Spec = Self::Spec, + Db: DatabaseExt, + > + where + Self: 'db; + + /// The Foundry-wrapped EVM type produced by this factory. + type FoundryEvm<'db, I: FoundryInspectorExt>>: Evm< + DB = &'db mut dyn DatabaseExt, + Tx = Self::Tx, + BlockEnv = Self::BlockEnv, + Spec = Self::Spec, + HaltReason = Self::HaltReason, + > + Deref> + + IntoNestedEvm + where + Self: 'db; + + /// Creates a Foundry-wrapped EVM with the given inspector. + fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: I, + ) -> Self::FoundryEvm<'db, I>; + + /// Creates a Foundry-wrapped EVM with a dynamic inspector, returning a boxed [`NestedEvm`]. + /// + /// This helper exists because `&mut dyn FoundryInspectorExt` cannot satisfy + /// the generic `I: FoundryInspectorExt>` bound when the context + /// type is only known through an associated type. Each concrete factory implements this + /// directly, side-stepping the higher-kinded lifetime issue. + fn create_foundry_nested_evm<'db>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: &'db mut dyn FoundryInspectorExt>, + ) -> Box + 'db>; +} + +/// Trait for converting a Foundry EVM wrapper into its inner `NestedEvm` implementation. +/// +/// Both [`EthFoundryEvm`] and [`TempoFoundryEvm`] wrap an inner revm EVM that implements +/// [`NestedEvm`]. This trait provides a uniform way to unwrap them. +pub trait IntoNestedEvm { + /// The inner type that implements [`NestedEvm`]. + type Inner: NestedEvm; + + /// Consumes the wrapper, returning the inner revm EVM. + fn into_nested_evm(self) -> Self::Inner; +} + +/// Object-safe trait exposing the operations that cheatcode nested EVM closures need. +/// +/// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.) +/// so that cheatcode impls can build and run nested EVMs without knowing the concrete type. +pub trait NestedEvm { + /// The spec type. + type Spec; + /// The block environment type. + type Block; + /// The transaction environment type. + type Tx; + + /// Returns a mutable reference to the journal inner state (`JournaledState`). + fn journal_inner_mut(&mut self) -> &mut JournaledState; + + /// Runs a single execution frame (create or call) through the EVM handler loop. + fn run_execution(&mut self, frame: FrameInput) -> Result>; + + /// Executes a full transaction with the given tx env. + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, EVMError>; + + fn to_evm_env(&self) -> EvmEnv; +} + +/// Closure type used by `CheatcodesExecutor` methods that run nested EVM operations. +pub type NestedEvmClosure<'a, Spec, Block, Tx> = + &'a mut dyn FnMut( + &mut dyn NestedEvm, + ) -> Result<(), EVMError>; + +/// Clones the current context (env + journal), passes the database, cloned env, +/// and cloned journal inner to the callback. The callback builds whatever EVM it +/// needs, runs its operations, and returns `(result, modified_env, modified_journal)`. +/// Modified state is written back after the callback returns. +pub fn with_cloned_context( + ecx: &mut CTX, + f: impl FnOnce( + &mut CTX::Db, + EvmEnv, + JournaledState, + ) + -> Result<(EvmEnv, JournaledState), EVMError>, +) -> Result<(), EVMError> { + let evm_env = ecx.evm_clone(); + + let (db, journal_inner) = ecx.db_journal_inner_mut(); + let journal_inner_clone = journal_inner.clone(); + + let (sub_evm_env, sub_inner) = f(db, evm_env, journal_inner_clone)?; + + // Write back modified state. The db borrow was released when f returned. + ecx.set_journal_inner(sub_inner); + ecx.set_evm(sub_evm_env); + + Ok(()) +} + +/// Get the call inputs for the CREATE2 factory. +pub(crate) fn get_create2_factory_call_inputs( + salt: U256, + inputs: &CreateInputs, + deployer: Address, +) -> CallInputs { + let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat(); + CallInputs { + caller: inputs.caller(), + bytecode_address: deployer, + known_bytecode: None, + target_address: deployer, + scheme: CallScheme::Call, + value: CallValue::Transfer(inputs.value()), + input: CallInput::Bytes(calldata.into()), + gas_limit: inputs.gas_limit(), + is_static: false, + return_memory_offset: 0..0, + } +} + +/// Converts a network-specific halt reason into an [`InstructionResult`]. +pub trait IntoInstructionResult { + fn into_instruction_result(self) -> InstructionResult; +} + +impl IntoInstructionResult for HaltReason { + fn into_instruction_result(self) -> InstructionResult { + self.into() + } +} + +impl IntoInstructionResult for OpHaltReason { + fn into_instruction_result(self) -> InstructionResult { + match self { + Self::Base(eth) => eth.into(), + Self::FailedDeposit => InstructionResult::Stop, + } + } +} + +impl IntoInstructionResult for TempoHaltReason { + fn into_instruction_result(self) -> InstructionResult { + match self { + Self::Ethereum(eth) => eth.into(), + _ => InstructionResult::PrecompileError, + } + } +} diff --git a/crates/evm/core/src/evm/op.rs b/crates/evm/core/src/evm/op.rs new file mode 100644 index 0000000000000..397744cab9d72 --- /dev/null +++ b/crates/evm/core/src/evm/op.rs @@ -0,0 +1,424 @@ +use super::*; + +pub type OpRevmEvm<'db, I> = op_revm::OpEvm< + OpContext<&'db mut dyn DatabaseExt>, + I, + EthInstructions>>, + PrecompilesMap, +>; + +/// Optimism counterpart of [`EthFoundryEvm`]. Wraps `op_revm::OpEvm` and routes execution +/// through [`OpFoundryHandler`] which composes [`OpHandler`] with CREATE2 factory redirect logic. +pub struct OpFoundryEvm< + 'db, + I: FoundryInspectorExt>>, +> { + pub inner: OpRevmEvm<'db, I>, +} + +impl FoundryEvmFactory for OpEvmFactory { + type FoundryContext<'db> = OpContext<&'db mut dyn DatabaseExt>; + + type FoundryEvm<'db, I: FoundryInspectorExt>> = OpFoundryEvm<'db, I>; + + fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: I, + ) -> Self::FoundryEvm<'db, I> { + let spec_id = *evm_env.spec_id(); + let mut inner = Context::op() + .with_db(db) + .with_block(evm_env.block_env) + .with_cfg(evm_env.cfg_env) + .build_op_with_inspector(inspector) + .with_precompiles(PrecompilesMap::from_static( + OpPrecompiles::new_with_spec(spec_id).precompiles(), + )); + inner.ctx().cfg.tx_chain_id_check = true; + + let mut evm = OpFoundryEvm { inner }; + let networks = Evm::inspector(&evm).get_networks(); + networks.inject_precompiles(evm.precompiles_mut()); + evm + } + + fn create_foundry_nested_evm<'db>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: &'db mut dyn FoundryInspectorExt>, + ) -> Box> + 'db> + { + Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_nested_evm()) + } +} + +impl<'db, I: FoundryInspectorExt>>> Evm + for OpFoundryEvm<'db, I> +{ + type Precompiles = PrecompilesMap; + type Inspector = I; + type DB = &'db mut dyn DatabaseExt; + type Error = EVMError; + type HaltReason = OpHaltReason; + type Spec = OpSpecId; + type Tx = OpTransaction; + type BlockEnv = BlockEnv; + + fn block(&self) -> &BlockEnv { + &self.inner.ctx_ref().block + } + + fn chain_id(&self) -> u64 { + self.inner.ctx_ref().cfg.chain_id + } + + fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { + let (ctx, _, precompiles, _, inspector) = self.inner.all_inspector(); + (&ctx.journaled_state.database, inspector, precompiles) + } + + fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { + let (ctx, _, precompiles, _, inspector) = self.inner.all_mut_inspector(); + (&mut ctx.journaled_state.database, inspector, precompiles) + } + + fn set_inspector_enabled(&mut self, _enabled: bool) { + unimplemented!("OpFoundryEvm is always inspecting") + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, Self::Error> { + self.inner.ctx().set_tx(tx); + + let mut handler = OpFoundryHandler::::default(); + let result = handler.inspect_run(&mut self.inner)?; + + Ok(ResultAndState::new(result, self.inner.ctx_ref().journaled_state.inner.state.clone())) + } + + fn transact_system_call( + &mut self, + _caller: Address, + _contract: Address, + _data: Bytes, + ) -> Result>, Self::Error> { + unimplemented!() + } + + fn finish(self) -> (Self::DB, EvmEnv) + where + Self: Sized, + { + let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.0.ctx; + (journaled_state.database, EvmEnv { block_env, cfg_env }) + } +} + +impl<'db, I: FoundryInspectorExt>>> Deref + for OpFoundryEvm<'db, I> +{ + type Target = OpContext<&'db mut dyn DatabaseExt>; + + fn deref(&self) -> &Self::Target { + &self.inner.0.ctx + } +} + +impl<'db, I: FoundryInspectorExt>>> DerefMut + for OpFoundryEvm<'db, I> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner.0.ctx + } +} + +impl<'db, I: FoundryInspectorExt>>> + IntoNestedEvm> for OpFoundryEvm<'db, I> +{ + type Inner = OpRevmEvm<'db, I>; + + fn into_nested_evm(self) -> Self::Inner { + self.inner + } +} + +/// Maps an OP [`EVMError`] to the common `EVMError` used by [`NestedEvm`]. +fn map_op_error(e: EVMError) -> EVMError { + match e { + EVMError::Database(db) => EVMError::Database(db), + EVMError::Header(h) => EVMError::Header(h), + EVMError::Custom(s) => EVMError::Custom(s), + EVMError::Transaction(t) => EVMError::Custom(format!("op transaction error: {t}")), + } +} + +impl<'db, I: FoundryInspectorExt>>> NestedEvm + for OpRevmEvm<'db, I> +{ + type Spec = OpSpecId; + type Block = BlockEnv; + type Tx = OpTransaction; + + fn journal_inner_mut(&mut self) -> &mut JournaledState { + &mut self.ctx().journaled_state.inner + } + + fn run_execution(&mut self, frame: FrameInput) -> Result> { + let mut handler = OpFoundryHandler::::default(); + + let memory = + SharedMemory::new_with_buffer(self.ctx_ref().local.shared_memory_buffer().clone()); + let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; + + let mut frame_result = + handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_op_error)?; + + handler.last_frame_result(self, &mut frame_result).map_err(map_op_error)?; + + Ok(frame_result) + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, EVMError> { + self.ctx().set_tx(tx); + + let mut handler = OpFoundryHandler::::default(); + let result = handler.inspect_run(self).map_err(map_op_error)?; + + let result = result.map_haltreason(|h| match h { + OpHaltReason::Base(eth) => eth, + _ => HaltReason::PrecompileError, + }); + + Ok(ResultAndState::new(result, self.ctx_ref().journaled_state.inner.state.clone())) + } + + fn to_evm_env(&self) -> EvmEnv { + EvmEnv::new(self.ctx_ref().cfg.clone(), self.ctx_ref().block.clone()) + } +} + +/// Optimism handler that composes [`OpHandler`] with CREATE2 factory redirect logic. +pub struct OpFoundryHandler< + 'db, + I: FoundryInspectorExt>>, +> { + inner: OpHandler< + OpRevmEvm<'db, I>, + EVMError, + EthFrame, + >, + create2_overrides: Vec<(usize, CallInputs)>, +} + +impl<'db, I: FoundryInspectorExt>>> Default + for OpFoundryHandler<'db, I> +{ + fn default() -> Self { + Self { inner: OpHandler::new(), create2_overrides: Vec::new() } + } +} + +impl<'db, I: FoundryInspectorExt>>> Handler + for OpFoundryHandler<'db, I> +{ + type Evm = OpRevmEvm<'db, I>; + type Error = EVMError; + type HaltReason = OpHaltReason; + + #[inline] + fn run( + &mut self, + evm: &mut Self::Evm, + ) -> Result, Self::Error> { + self.inner.run(evm) + } + + #[inline] + fn execution( + &mut self, + evm: &mut Self::Evm, + init_and_floor_gas: &revm::interpreter::InitialAndFloorGas, + ) -> Result { + self.inner.execution(evm, init_and_floor_gas) + } + + #[inline] + fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + self.inner.validate_env(evm) + } + + #[inline] + fn validate_against_state_and_deduct_caller( + &self, + evm: &mut Self::Evm, + ) -> Result<(), Self::Error> { + self.inner.validate_against_state_and_deduct_caller(evm) + } + + #[inline] + fn reimburse_caller( + &self, + evm: &mut Self::Evm, + exec_result: &mut <::Frame as FrameTr>::FrameResult, + ) -> Result<(), Self::Error> { + self.inner.reimburse_caller(evm, exec_result) + } + + #[inline] + fn reward_beneficiary( + &self, + evm: &mut Self::Evm, + exec_result: &mut <::Frame as FrameTr>::FrameResult, + ) -> Result<(), Self::Error> { + self.inner.reward_beneficiary(evm, exec_result) + } + + #[inline] + fn validate_initial_tx_gas( + &self, + evm: &mut Self::Evm, + ) -> Result { + self.inner.validate_initial_tx_gas(evm) + } + + #[inline] + fn execution_result( + &mut self, + evm: &mut Self::Evm, + result: <::Frame as FrameTr>::FrameResult, + result_gas: revm::context::result::ResultGas, + ) -> Result, Self::Error> { + self.inner.execution_result(evm, result, result_gas) + } + + #[inline] + fn catch_error( + &self, + evm: &mut Self::Evm, + error: Self::Error, + ) -> Result, Self::Error> { + self.inner.catch_error(evm, error) + } +} + +impl<'db, I: FoundryInspectorExt>>> + InspectorHandler for OpFoundryHandler<'db, I> +{ + type IT = EthInterpreter; + + fn inspect_run_exec_loop( + &mut self, + evm: &mut Self::Evm, + first_frame_input: <::Frame as FrameTr>::FrameInit, + ) -> Result { + let res = evm.inspect_frame_init(first_frame_input)?; + + if let ItemOrResult::Result(frame_result) = res { + return Ok(frame_result); + } + + loop { + let call_or_result = evm.inspect_frame_run()?; + + let result = match call_or_result { + ItemOrResult::Item(mut init) => { + // Handle CREATE/CREATE2 frame initialization + if let FrameInput::Create(inputs) = &init.frame_input + && let CreateScheme::Create2 { salt } = inputs.scheme() + { + let (ctx, inspector) = evm.ctx_inspector(); + if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) { + let gas_limit = inputs.gas_limit(); + let create2_deployer = evm.inspector().create2_deployer(); + let call_inputs = + get_create2_factory_call_inputs(salt, inputs, create2_deployer); + + self.create2_overrides + .push((evm.ctx_ref().journal().depth(), call_inputs.clone())); + + let code_hash = evm + .ctx() + .journal_mut() + .load_account(create2_deployer)? + .info + .code_hash; + if code_hash == KECCAK_EMPTY { + return Ok(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Bytes::from( + format!("missing CREATE2 deployer: {create2_deployer}") + .into_bytes(), + ), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: vec![], + })); + } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { + return Ok(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 deployer bytecode".into(), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: vec![], + })); + } + + init.frame_input = FrameInput::Call(Box::new(call_inputs)); + } + } + + match evm.inspect_frame_init(init)? { + ItemOrResult::Item(_) => continue, + ItemOrResult::Result(result) => result, + } + } + ItemOrResult::Result(result) => result, + }; + + // Handle CREATE2 override transformation if needed + let result = if self + .create2_overrides + .last() + .is_some_and(|(depth, _)| *depth == evm.ctx_ref().journal().depth()) + { + let (_, call_inputs) = self.create2_overrides.pop().unwrap(); + let FrameResult::Call(mut call) = result else { + unreachable!("create2 override should be a call frame"); + }; + let address = match call.instruction_result() { + return_ok!() => Address::try_from(call.output().as_ref()) + .map_err(|_| { + call.result = InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 factory output".into(), + gas: Gas::new(call_inputs.gas_limit), + }; + }) + .ok(), + _ => None, + }; + FrameResult::Create(CreateOutcome { result: call.result, address }) + } else { + result + }; + + if let Some(result) = evm.frame_return_result(result)? { + return Ok(result); + } + } + } +} diff --git a/crates/evm/core/src/evm/tempo.rs b/crates/evm/core/src/evm/tempo.rs new file mode 100644 index 0000000000000..4edc40a0973dd --- /dev/null +++ b/crates/evm/core/src/evm/tempo.rs @@ -0,0 +1,526 @@ +use super::*; + +// Will be removed when the next revm release includes bluealloy/revm#3518. +pub type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt, I>; + +/// Tempo counterpart of [`EthFoundryEvm`]. Wraps `tempo_revm::TempoEvm` and routes execution +/// through [`TempoFoundryHandler`] which composes [`TempoEvmHandler`] with CREATE2 factory +/// redirect logic. +/// +/// Uses [`TempoEvmFactory`] for construction to reuse factory setup logic, then unwraps to the +/// raw revm EVM via `into_inner()` since the handler operates at the revm level. +pub struct TempoFoundryEvm< + 'db, + I: FoundryInspectorExt>>, +> { + pub inner: TempoRevmEvm<'db, I>, +} + +/// Initialize Tempo precompiles and contracts for a newly created [`TempoFoundryEvm`]. +/// +/// In non-fork mode, runs full genesis initialization (precompile sentinel bytecode, +/// TIP20 fee tokens, standard contracts) via [`StorageCtx::enter_evm`]. +/// +/// In fork mode, warms up precompile and TIP20 token addresses with sentinel bytecode +/// to prevent repeated RPC round-trips for addresses that are Rust-native precompiles +/// on Tempo nodes (no real EVM bytecode on-chain). +pub(crate) fn initialize_tempo_evm< + 'db, + I: FoundryInspectorExt>>, +>( + evm: &mut TempoFoundryEvm<'db, I>, + is_forked: bool, +) { + let ctx = &mut evm.inner.inner.ctx; + StorageCtx::enter_evm(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx, || { + if is_forked { + // In fork mode, warm up precompile accounts to avoid repeated RPC fetches. + let mut sctx = StorageCtx; + let sentinel = Bytecode::new_legacy(Bytes::from_static(&[0xef])); + for addr in TEMPO_PRECOMPILE_ADDRESSES.iter().chain(TEMPO_TIP20_TOKENS.iter()) { + sctx.set_code(*addr, sentinel.clone()) + .expect("failed to warm tempo precompile address"); + } + } else { + // In non-fork mode, run full genesis initialization. + initialize_tempo_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER) + .expect("tempo genesis initialization failed"); + } + }); +} + +impl FoundryEvmFactory for TempoEvmFactory { + type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt>; + + type FoundryEvm<'db, I: FoundryInspectorExt>> = + TempoFoundryEvm<'db, I>; + + fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: I, + ) -> Self::FoundryEvm<'db, I> { + let is_forked = db.is_forked_mode(); + let spec = *evm_env.spec_id(); + let tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); + let mut inner = tempo_evm.into_inner(); + inner.ctx.cfg.gas_params = tempo_gas_params(spec); + inner.ctx.cfg.tx_chain_id_check = true; + + let mut evm = TempoFoundryEvm { inner }; + let networks = Evm::inspector(&evm).get_networks(); + networks.inject_precompiles(evm.precompiles_mut()); + + initialize_tempo_evm(&mut evm, is_forked); + evm + } + + fn create_foundry_nested_evm<'db>( + &self, + db: &'db mut dyn DatabaseExt, + evm_env: EvmEnv, + inspector: &'db mut dyn FoundryInspectorExt>, + ) -> Box + 'db> + { + Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_nested_evm()) + } +} + +impl<'db, I: FoundryInspectorExt>>> Evm + for TempoFoundryEvm<'db, I> +{ + type Precompiles = PrecompilesMap; + type Inspector = I; + type DB = &'db mut dyn DatabaseExt; + type Error = EVMError; + type HaltReason = TempoHaltReason; + type Spec = TempoHardfork; + type Tx = TempoTxEnv; + type BlockEnv = TempoBlockEnv; + + fn block(&self) -> &TempoBlockEnv { + &self.inner.block + } + + fn chain_id(&self) -> u64 { + self.inner.ctx.cfg.chain_id + } + + fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { + let evm = &self.inner.inner; + (&evm.ctx.journaled_state.database, &evm.inspector, &evm.precompiles) + } + + fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { + let evm = &mut self.inner.inner; + (&mut evm.ctx.journaled_state.database, &mut evm.inspector, &mut evm.precompiles) + } + + fn set_inspector_enabled(&mut self, _enabled: bool) { + unimplemented!("TempoFoundryEvm is always inspecting") + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, Self::Error> { + self.inner.set_tx(tx); + + let mut handler = TempoFoundryHandler::::default(); + let result = handler.inspect_run(&mut self.inner)?; + + Ok(ResultAndState::new(result, self.inner.inner.ctx.journaled_state.inner.state.clone())) + } + + fn transact_system_call( + &mut self, + _caller: Address, + _contract: Address, + _data: Bytes, + ) -> Result>, Self::Error> { + unimplemented!() + } + + fn finish(self) -> (Self::DB, EvmEnv) + where + Self: Sized, + { + let revm_evm = self.inner.inner; + let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = revm_evm.ctx; + (journaled_state.database, EvmEnv { block_env, cfg_env }) + } +} + +impl<'db, I: FoundryInspectorExt>>> Deref + for TempoFoundryEvm<'db, I> +{ + type Target = TempoContext<&'db mut dyn DatabaseExt>; + + fn deref(&self) -> &Self::Target { + &self.inner.ctx + } +} + +impl<'db, I: FoundryInspectorExt>>> DerefMut + for TempoFoundryEvm<'db, I> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner.ctx + } +} + +impl<'db, I: FoundryInspectorExt>>> + IntoNestedEvm for TempoFoundryEvm<'db, I> +{ + type Inner = TempoRevmEvm<'db, I>; + + fn into_nested_evm(self) -> Self::Inner { + self.inner + } +} + +/// Maps a Tempo [`EVMError`] to the common `EVMError` used by [`NestedEvm`]. +/// +/// This exists because [`NestedEvm`] currently uses Eth-typed errors. When `NestedEvm` gains +/// an associated `Error` type, this mapping can be removed. +pub(crate) fn map_tempo_error( + e: EVMError, +) -> EVMError { + match e { + EVMError::Database(db) => EVMError::Database(db), + EVMError::Header(h) => EVMError::Header(h), + EVMError::Custom(s) => EVMError::Custom(s), + EVMError::Transaction(t) => match t { + TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth), + t => EVMError::Custom(format!("tempo transaction error: {t}")), + }, + } +} + +impl<'db, I: FoundryInspectorExt>>> NestedEvm + for TempoRevmEvm<'db, I> +{ + type Spec = TempoHardfork; + type Block = TempoBlockEnv; + type Tx = TempoTxEnv; + + fn journal_inner_mut(&mut self) -> &mut JournaledState { + &mut self.ctx_mut().journaled_state.inner + } + + fn run_execution(&mut self, frame: FrameInput) -> Result> { + let mut handler = TempoFoundryHandler::::default(); + + let memory = + SharedMemory::new_with_buffer(self.ctx().local().shared_memory_buffer().clone()); + let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; + + let mut frame_result = + handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_tempo_error)?; + + handler.last_frame_result(self, &mut frame_result).map_err(map_tempo_error)?; + + Ok(frame_result) + } + + fn transact_raw( + &mut self, + tx: Self::Tx, + ) -> Result, EVMError> { + self.set_tx(tx); + + let mut handler = TempoFoundryHandler::::default(); + let result = handler.inspect_run(self).map_err(map_tempo_error)?; + + let result = result.map_haltreason(|h| match h { + TempoHaltReason::Ethereum(eth) => eth, + _ => HaltReason::PrecompileError, + }); + + Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone())) + } + + fn to_evm_env(&self) -> EvmEnv { + self.ctx_ref().evm_clone() + } +} + +/// Tempo counterpart of [`EthFoundryHandler`]. Wraps [`TempoEvmHandler`] and injects CREATE2 +/// factory redirect logic into the execution loop. Delegates all [`Handler`] methods to +/// [`TempoEvmHandler`] for proper Tempo validation, fee collection, AA dispatch, and gas +/// handling. +/// +/// Will be removed when the next revm release includes bluealloy/revm#3518. +pub struct TempoFoundryHandler< + 'db, + I: FoundryInspectorExt>>, +> { + inner: TempoEvmHandler<&'db mut dyn DatabaseExt, I>, + create2_overrides: Vec<(usize, CallInputs)>, +} + +impl<'db, I: FoundryInspectorExt>>> Default + for TempoFoundryHandler<'db, I> +{ + fn default() -> Self { + Self { inner: TempoEvmHandler::new(), create2_overrides: Vec::new() } + } +} + +impl<'db, I: FoundryInspectorExt>>> Handler + for TempoFoundryHandler<'db, I> +{ + type Evm = TempoRevmEvm<'db, I>; + type Error = EVMError; + type HaltReason = TempoHaltReason; + + #[inline] + fn run( + &mut self, + evm: &mut Self::Evm, + ) -> Result, Self::Error> { + self.inner.run(evm) + } + + #[inline] + fn execution( + &mut self, + evm: &mut Self::Evm, + init_and_floor_gas: &revm::interpreter::InitialAndFloorGas, + ) -> Result { + self.inner.execution(evm, init_and_floor_gas) + } + + #[inline] + fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> { + self.inner.validate_env(evm) + } + + #[inline] + fn validate_against_state_and_deduct_caller( + &self, + evm: &mut Self::Evm, + ) -> Result<(), Self::Error> { + self.inner.validate_against_state_and_deduct_caller(evm) + } + + #[inline] + fn reimburse_caller( + &self, + evm: &mut Self::Evm, + exec_result: &mut <::Frame as FrameTr>::FrameResult, + ) -> Result<(), Self::Error> { + self.inner.reimburse_caller(evm, exec_result) + } + + #[inline] + fn reward_beneficiary( + &self, + evm: &mut Self::Evm, + exec_result: &mut <::Frame as FrameTr>::FrameResult, + ) -> Result<(), Self::Error> { + self.inner.reward_beneficiary(evm, exec_result) + } + + #[inline] + fn validate_initial_tx_gas( + &self, + evm: &mut Self::Evm, + ) -> Result { + self.inner.validate_initial_tx_gas(evm) + } + + #[inline] + fn execution_result( + &mut self, + evm: &mut Self::Evm, + result: <::Frame as FrameTr>::FrameResult, + result_gas: revm::context::result::ResultGas, + ) -> Result, Self::Error> { + self.inner.execution_result(evm, result, result_gas) + } + + #[inline] + fn catch_error( + &self, + evm: &mut Self::Evm, + error: Self::Error, + ) -> Result, Self::Error> { + self.inner.catch_error(evm, error) + } +} + +/// CREATE2 factory redirect execution loop for Tempo. +fn create2_exec_loop< + 'db, + I: FoundryInspectorExt>>, +>( + create2_overrides: &mut Vec<(usize, CallInputs)>, + evm: &mut TempoRevmEvm<'db, I>, + first_frame_input: FrameInit, +) -> Result> { + let res = evm.inspect_frame_init(first_frame_input)?; + + if let ItemOrResult::Result(frame_result) = res { + return Ok(frame_result); + } + + loop { + let call_or_result = evm.inspect_frame_run()?; + + let result = match call_or_result { + ItemOrResult::Item(mut init) => { + if let Some(frame_result) = handle_create2_frame(create2_overrides, evm, &mut init)? + { + return Ok(frame_result); + } + + match evm.inspect_frame_init(init)? { + ItemOrResult::Item(_) => continue, + ItemOrResult::Result(result) => result, + } + } + ItemOrResult::Result(result) => result, + }; + + let result = handle_create2_result(create2_overrides, evm, result); + + if let Some(result) = evm.frame_return_result(result)? { + return Ok(result); + } + } +} + +/// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2 factory. +fn handle_create2_frame< + 'db, + I: FoundryInspectorExt>>, +>( + create2_overrides: &mut Vec<(usize, CallInputs)>, + evm: &mut TempoRevmEvm<'db, I>, + init: &mut FrameInit, +) -> Result, EVMError> { + if let FrameInput::Create(inputs) = &init.frame_input + && let CreateScheme::Create2 { salt } = inputs.scheme() + { + let (ctx, inspector) = evm.ctx_inspector(); + + if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) { + let gas_limit = inputs.gas_limit(); + let create2_deployer = evm.inspector().create2_deployer(); + let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer); + + create2_overrides.push((evm.journal().depth(), call_inputs.clone())); + + let code_hash = evm.journal_mut().load_account(create2_deployer)?.info.code_hash; + if code_hash == KECCAK_EMPTY { + return Ok(Some(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Bytes::from( + format!("missing CREATE2 deployer: {create2_deployer}").into_bytes(), + ), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: vec![], + }))); + } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { + return Ok(Some(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 deployer bytecode".into(), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + was_precompile_called: false, + precompile_call_logs: vec![], + }))); + } + + init.frame_input = FrameInput::Call(Box::new(call_inputs)); + } + } + Ok(None) +} + +/// Transforms CREATE2 factory call results back into CREATE outcomes. +fn handle_create2_result< + 'db, + I: FoundryInspectorExt>>, +>( + create2_overrides: &mut Vec<(usize, CallInputs)>, + evm: &mut TempoRevmEvm<'db, I>, + result: FrameResult, +) -> FrameResult { + if create2_overrides.last().is_some_and(|(depth, _)| *depth == evm.journal().depth()) { + let (_, call_inputs) = create2_overrides.pop().unwrap(); + let FrameResult::Call(mut call) = result else { + unreachable!("create2 override should be a call frame"); + }; + + let address = match call.instruction_result() { + return_ok!() => Address::try_from(call.output().as_ref()) + .map_err(|_| { + call.result = InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 factory output".into(), + gas: Gas::new(call_inputs.gas_limit), + }; + }) + .ok(), + _ => None, + }; + + FrameResult::Create(CreateOutcome { result: call.result, address }) + } else { + result + } +} + +impl<'db, I: FoundryInspectorExt>>> + InspectorHandler for TempoFoundryHandler<'db, I> +{ + type IT = EthInterpreter; + + /// Overrides `inspect_run` to load Tempo fee fields before delegating to the default + /// execution pipeline. This is necessary because the default `inspect_run` calls + /// `validate` → `validate_against_state_and_deduct_caller` directly, bypassing + /// `Handler::run` where `load_fee_fields` is normally invoked. + fn inspect_run( + &mut self, + evm: &mut Self::Evm, + ) -> Result, Self::Error> { + self.inner.load_fee_fields(evm)?; + + match self.inspect_run_without_catch_error(evm) { + Ok(output) => Ok(output), + Err(e) => self.catch_error(evm, e), + } + } + + /// Delegates to [`TempoEvmHandler::inspect_execution_with`], injecting the CREATE2 factory + /// redirect exec loop. AA multi-call dispatch and gas adjustments are handled by the inner + /// Tempo handler. + #[inline] + fn inspect_execution( + &mut self, + evm: &mut Self::Evm, + init_and_floor_gas: &revm::interpreter::InitialAndFloorGas, + ) -> Result { + let overrides = &mut self.create2_overrides; + self.inner.inspect_execution_with(evm, init_and_floor_gas, |_handler, evm, init| { + create2_exec_loop(overrides, evm, init) + }) + } + + fn inspect_run_exec_loop( + &mut self, + evm: &mut Self::Evm, + first_frame_input: <::Frame as FrameTr>::FrameInit, + ) -> Result { + create2_exec_loop(&mut self.create2_overrides, evm, first_frame_input) + } +} diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index da0f212072d22..6f539d356e5a9 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -56,15 +56,15 @@ impl ForkedDatabase { } } - pub fn database(&self) -> &CacheDB> { + pub const fn database(&self) -> &CacheDB> { &self.cache_db } - pub fn database_mut(&mut self) -> &mut CacheDB> { + pub const fn database_mut(&mut self) -> &mut CacheDB> { &mut self.cache_db } - pub fn state_snapshots(&self) -> &Arc>>> { + pub const fn state_snapshots(&self) -> &Arc>>> { &self.state_snapshots } @@ -92,7 +92,7 @@ impl ForkedDatabase { } /// Returns the database that holds the remote state - pub fn inner(&self) -> &BlockchainDb { + pub const fn inner(&self) -> &BlockchainDb { &self.db } diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 28df92da912e9..ee3ee216bcbab 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -4,11 +4,12 @@ use crate::{ fork::CreateFork, utils::{apply_chain_and_block_specific_env_changes, block_env_from_header}, }; +use alloy_chains::NamedChain; use alloy_consensus::BlockHeader; use alloy_network::{AnyNetwork, BlockResponse, Network}; use alloy_primitives::{Address, B256, BlockNumber, ChainId, U256}; use alloy_provider::{Provider, RootProvider}; -use alloy_rpc_types::BlockNumberOrTag; +use alloy_rpc_types::{BlockNumberOrTag, anvil::NodeInfo}; use eyre::WrapErr; use foundry_common::{ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, provider::ProviderBuilder}; use foundry_config::{Chain, Config, GasLimit}; @@ -142,7 +143,17 @@ impl EvmOpts { && let Ok(provider) = self.fork_provider_with_url::(fork_url) && let Ok(chain_id) = provider.get_chain_id().await { - self.networks = self.networks.with_chain_id(chain_id); + // If Anvil's chain, request anvil_nodeInfo to determine if the network is Tempo. + if chain_id == NamedChain::AnvilHardhat as u64 { + if let Ok(node_info) = + provider.raw_request::<_, NodeInfo>("anvil_nodeInfo".into(), ()).await + && node_info.network.is_some_and(|network| network == "tempo") + { + self.networks = NetworkConfigs::with_tempo(); + } + } else { + self.networks = self.networks.with_chain_id(chain_id); + } } } @@ -350,7 +361,7 @@ impl EvmOpts { /// - u64::MAX, if `no_rpc_rate_limit` if set (as rate limiting is disabled) /// - the assigned compute units, if `compute_units_per_second` is set /// - ALCHEMY_FREE_TIER_CUPS (330) otherwise - fn get_compute_units_per_second(&self) -> u64 { + const fn get_compute_units_per_second(&self) -> u64 { if self.no_rpc_rate_limit { u64::MAX } else if let Some(cups) = self.compute_units_per_second { @@ -450,6 +461,68 @@ mod tests { use super::*; + #[tokio::test(flavor = "multi_thread")] + async fn infer_network_default_anvil_selects_ethereum() { + let (_api, handle) = anvil::spawn(anvil::NodeConfig::test()).await; + + let config = Config::figment(); + let mut evm_opts = config.extract::().unwrap(); + evm_opts.fork_url = Some(handle.http_endpoint()); + assert_eq!(evm_opts.networks, NetworkConfigs::default()); + + evm_opts.infer_network_from_fork().await; + + // Plain anvil (chain id 31337) without tempo flag -> Ethereum (no network flags set). + assert!(!evm_opts.networks.is_tempo()); + assert!(!evm_opts.networks.is_optimism()); + assert!(!evm_opts.networks.is_celo()); + assert_eq!(evm_opts.networks, NetworkConfigs::default()); + } + + #[tokio::test(flavor = "multi_thread")] + async fn infer_network_tempo_anvil_via_node_info() { + let (_api, handle) = anvil::spawn(anvil::NodeConfig::test_tempo()).await; + + let config = Config::figment(); + let mut evm_opts = config.extract::().unwrap(); + evm_opts.fork_url = Some(handle.http_endpoint()); + // Networks not set -> should query anvil_nodeInfo to discover tempo. + assert_eq!(evm_opts.networks, NetworkConfigs::default()); + + evm_opts.infer_network_from_fork().await; + + assert!(evm_opts.networks.is_tempo(), "should detect tempo via anvil_nodeInfo"); + } + + #[tokio::test(flavor = "multi_thread")] + async fn infer_network_tempo_anvil_skips_rpc_when_already_set() { + // Use a URL that would fail if any RPC call were attempted (connection refused). + // This proves the early-return guard prevents all network requests. + let config = Config::figment(); + let mut evm_opts = config.extract::().unwrap(); + evm_opts.fork_url = Some("http://127.0.0.1:1".to_string()); + // Explicitly set tempo before calling infer (simulates --tempo CLI flag). + evm_opts.networks = NetworkConfigs::with_tempo(); + + evm_opts.infer_network_from_fork().await; + + // Should still be tempo, the early-return guard skips the RPC call. + assert!(evm_opts.networks.is_tempo()); + } + + #[tokio::test(flavor = "multi_thread")] + async fn flaky_infer_network_tempo_moderato_rpc() { + let config = Config::figment(); + let mut evm_opts = config.extract::().unwrap(); + evm_opts.fork_url = Some("https://rpc.moderato.tempo.xyz".to_string()); + assert_eq!(evm_opts.networks, NetworkConfigs::default()); + + evm_opts.infer_network_from_fork().await; + + // Tempo Moderato has a known Tempo chain ID -> should be inferred via with_chain_id. + assert!(evm_opts.networks.is_tempo(), "should detect tempo from Moderato chain ID"); + } + #[tokio::test(flavor = "multi_thread")] async fn get_fork_pins_block_number_from_env() { let endpoint = foundry_test_utils::rpc::next_http_rpc_endpoint(); diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 0f6b75022f00a..76e1df1bd1778 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -16,7 +16,7 @@ pub use revm::state::EvmState as StateChangeset; /// Hints to the compiler that this is a cold path, i.e. unlikely to be taken. #[cold] #[inline(always)] -pub fn cold_path() { +pub const fn cold_path() { // TODO: remove `#[cold]` and call `std::hint::cold_path` once stable. } @@ -49,7 +49,7 @@ pub fn apply_chain_and_block_specific_env_changes< block: &N::BlockResponse, configs: NetworkConfigs, ) { - use NamedChain::*; + use NamedChain::{BinanceSmartChain, BinanceSmartChainTestnet, Mainnet}; if let Ok(chain) = NamedChain::try_from(evm_env.cfg_env.chain_id) { let block_number = block.header().number(); diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 9f25d2e28baec..231da8a4b1d62 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -57,7 +57,7 @@ impl<'gcx> SourceVisitor<'gcx> { } } - fn checkpoint(&self) -> SourceVisitorCheckpoint { + const fn checkpoint(&self) -> SourceVisitorCheckpoint { SourceVisitorCheckpoint { items: self.items.len(), all_lines: self.all_lines.len(), @@ -181,7 +181,7 @@ impl<'gcx> SourceVisitor<'gcx> { first.line_index as u32 + 1..last.line_index as u32 + 2 } - fn next_branch_id(&mut self) -> u32 { + const fn next_branch_id(&mut self) -> u32 { let id = self.branch_id; self.branch_id = id + 1; id @@ -543,7 +543,7 @@ impl SourceAnalysis { } /// Returns all the mutable coverage items. - pub fn all_items_mut(&mut self) -> &mut Vec { + pub const fn all_items_mut(&mut self) -> &mut Vec { &mut self.all_items } diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index c1a9e0957d495..25ecebed244a8 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -225,7 +225,7 @@ impl HitMap { /// Returns the bytecode. #[inline] - pub fn bytecode(&self) -> &Bytes { + pub const fn bytecode(&self) -> &Bytes { &self.bytecode } @@ -480,7 +480,7 @@ impl fmt::Display for SourceLocation { impl SourceLocation { /// Returns the byte range as usize. - pub fn bytes(&self) -> Range { + pub const fn bytes(&self) -> Range { self.bytes.start as usize..self.bytes.end as usize } @@ -530,7 +530,7 @@ impl CoverageSummary { } /// Adds another coverage summary to this one. - pub fn merge(&mut self, other: &Self) { + pub const fn merge(&mut self, other: &Self) { let Self { line_count, line_hits, @@ -552,7 +552,7 @@ impl CoverageSummary { } /// Adds a coverage item to this summary. - pub fn add_item(&mut self, item: &CoverageItem) { + pub const fn add_item(&mut self, item: &CoverageItem) { match item.kind { CoverageItemKind::Line => { self.line_count += 1; diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index 587d58bd2af74..a49716ca4fbc1 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -51,27 +51,27 @@ impl ExecutorBuilder { /// Sets the EVM spec to use. #[inline] - pub fn spec_id(mut self, spec: SpecFor) -> Self { + pub const fn spec_id(mut self, spec: SpecFor) -> Self { self.spec = Some(spec); self } /// Optionally sets the EVM spec. When `None`, the spec from `EvmEnv::cfg_env` is preserved. #[inline] - pub fn spec_id_opt(self, spec: Option>) -> Self { + pub const fn spec_id_opt(self, spec: Option>) -> Self { if let Some(spec) = spec { self.spec_id(spec) } else { self } } /// Sets the executor gas limit. #[inline] - pub fn gas_limit(mut self, gas_limit: u64) -> Self { + pub const fn gas_limit(mut self, gas_limit: u64) -> Self { self.gas_limit = Some(gas_limit); self } /// Sets the `legacy_assertions` flag. #[inline] - pub fn legacy_assertions(mut self, legacy_assertions: bool) -> Self { + pub const fn legacy_assertions(mut self, legacy_assertions: bool) -> Self { self.legacy_assertions = legacy_assertions; self } diff --git a/crates/evm/evm/src/executors/corpus.rs b/crates/evm/evm/src/executors/corpus.rs index 9e70c42e7aafb..850d3cdeaad90 100644 --- a/crates/evm/evm/src/executors/corpus.rs +++ b/crates/evm/evm/src/executors/corpus.rs @@ -230,7 +230,7 @@ impl fmt::Display for CorpusMetrics { impl CorpusMetrics { /// Records number of new edges or features explored during the campaign. - pub fn update_seen(&mut self, is_edge: bool) { + pub const fn update_seen(&mut self, is_edge: bool) { if is_edge { self.cumulative_edges_seen += 1; } else { @@ -239,7 +239,7 @@ impl CorpusMetrics { } /// Updates campaign favored items. - pub fn update_favored(&mut self, is_favored: bool, corpus_favored: bool) { + pub const fn update_favored(&mut self, is_favored: bool, corpus_favored: bool) { if is_favored && !corpus_favored { self.favored_items += 1; } else if !is_favored && corpus_favored { @@ -707,15 +707,15 @@ impl WorkerCorpus { self.evict_oldest_corpus()?; - let tx = if !self.in_memory_corpus.is_empty() { + let tx = if self.in_memory_corpus.is_empty() { + self.new_tx(test_runner)? + } else { let corpus = &self.in_memory_corpus [test_runner.rng().random_range(0..self.in_memory_corpus.len())]; self.current_mutated = Some(corpus.uuid); let mut tx = corpus.tx_seq.first().unwrap().clone(); self.abi_mutate(&mut tx, function, test_runner, fuzz_state)?; tx - } else { - self.new_tx(test_runner)? }; Ok(tx.call_details.calldata) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 2b620eb65cd46..33152b73dda3c 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -196,10 +196,8 @@ impl FuzzedExecutor { config: FuzzConfig, persisted_failure: Option, ) -> Self { - let mut max_workers = Ord::max(1, config.runs / MIN_RUNS_PER_WORKER); - if config.runs == 0 { - max_workers = 0; - } + let max_workers = + if config.runs == 0 { 0 } else { Ord::max(1, config.runs / MIN_RUNS_PER_WORKER) }; let num_workers = Ord::min(rayon::current_num_threads(), max_workers as usize); Self { executor_f: executor, runner, sender, config, persisted_failure, num_workers } } @@ -437,7 +435,7 @@ impl FuzzedExecutor { self.config.corpus.clone(), strategy.boxed(), // Master worker replays the persisted corpus using the executor - if worker_id == 0 { Some(&self.executor_f) } else { None }, + (worker_id == 0).then_some(&self.executor_f), Some(func), None, // fuzzed_contracts for invariant tests )?; @@ -598,7 +596,15 @@ impl FuzzedExecutor { }) => { inc_runs(); - let reason = rd.maybe_decode(&outcome.1.result, status); + // Only classify magic skip payloads when the revert originates from the + // cheatcode address. + let reason = if outcome.1.reverter == Some(CHEATCODE_ADDRESS) { + SkipReason::decode(&outcome.1.result) + .map(|reason| reason.to_string()) + .or_else(|| rd.maybe_decode(&outcome.1.result, status)) + } else { + rd.maybe_decode(&outcome.1.result, status) + }; worker.logs.extend(outcome.1.logs.clone()); worker.counterexample = outcome; worker.failure = Some(TestCaseError::fail(reason.unwrap_or_default())); @@ -648,7 +654,7 @@ impl FuzzedExecutor { } /// Determines the number of runs per worker. - fn runs_per_worker(&self, worker_id: usize) -> u32 { + const fn runs_per_worker(&self, worker_id: usize) -> u32 { let worker_id = worker_id as u32; let total_runs = self.config.runs; let n = self.num_workers as u32; diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index b3b08fd0ebcca..ebe3c29a005ca 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -41,7 +41,7 @@ use serde_json::json; use std::{ collections::{HashMap as Map, btree_map::Entry}, sync::Arc, - time::{Instant, SystemTime, UNIX_EPOCH}, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; mod error; @@ -122,6 +122,68 @@ pub struct InvariantMetrics { pub discards: usize, } +/// Campaign-level throughput metrics for invariant progress reporting. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +struct InvariantThroughputMetrics { + total_txs: u64, + total_gas: u64, +} + +impl InvariantThroughputMetrics { + const fn record_call(&mut self, gas_used: u64) { + self.total_txs += 1; + self.total_gas += gas_used; + } + + fn tx_per_sec(self, elapsed: Duration) -> f64 { + rate_per_sec(self.total_txs as f64, elapsed) + } + + fn gas_per_sec(self, elapsed: Duration) -> f64 { + rate_per_sec(self.total_gas as f64, elapsed) + } +} + +/// Converts a cumulative campaign total into an average per-second rate. +/// +/// Returns `0.0` during the initial zero-elapsed startup window to avoid +/// dividing by zero while progress reporting is warming up. +fn rate_per_sec(total: f64, elapsed: Duration) -> f64 { + let elapsed_secs = elapsed.as_secs_f64(); + if elapsed_secs > 0.0 { total / elapsed_secs } else { 0.0 } +} + +/// Builds the machine-readable invariant progress payload emitted during a +/// campaign. +/// +/// This keeps the existing corpus progress metrics together with cumulative and +/// derived throughput fields so downstream benchmark tooling can consume a +/// single JSON event shape. +fn build_invariant_progress_json( + timestamp_secs: u64, + invariant_name: &str, + corpus_metrics: &M, + optimization_best: Option, + throughput: InvariantThroughputMetrics, + elapsed: Duration, +) -> serde_json::Value { + let mut payload = json!({ + "timestamp": timestamp_secs, + "invariant": invariant_name, + "metrics": corpus_metrics, + "total_txs": throughput.total_txs, + "total_gas": throughput.total_gas, + "tx_per_sec": throughput.tx_per_sec(elapsed), + "gas_per_sec": throughput.gas_per_sec(elapsed), + }); + + if let Some(best) = optimization_best { + payload["optimization_best"] = json!(best.to_string()); + } + + payload +} + /// Contains data collected during invariant test runs. struct InvariantTestData { // Consumed gas and calldata of every successful fuzz call. @@ -190,12 +252,12 @@ impl InvariantTest { } /// Returns number of invariant test reverts. - fn reverts(&self) -> usize { + const fn reverts(&self) -> usize { self.test_data.failures.reverts } /// Whether invariant test has errors or not. - fn has_errors(&self) -> bool { + const fn has_errors(&self) -> bool { self.test_data.failures.error.is_some() } @@ -370,6 +432,8 @@ impl<'a, FEN: FoundryEvmNetwork> InvariantExecutor<'a, FEN> { let mut runs = 0; let timer = FuzzTestTimer::new(self.config.timeout); let mut last_metrics_report = Instant::now(); + let campaign_start = Instant::now(); + let mut throughput = InvariantThroughputMetrics::default(); let continue_campaign = |runs: u32| { if early_exit.should_stop() { return false; @@ -478,6 +542,7 @@ impl<'a, FEN: FoundryEvmNetwork> InvariantExecutor<'a, FEN> { current_run .fuzz_runs .push(FuzzCase { gas: call_result.gas_used, stipend: call_result.stipend }); + throughput.record_call(call_result.gas_used); // Determine if test can continue or should exit. // Check invariants based on check_interval to improve deep run performance. @@ -527,9 +592,12 @@ impl<'a, FEN: FoundryEvmNetwork> InvariantExecutor<'a, FEN> { invariant_test.test_data.failures.error = Some(InvariantFuzzError::Revert(case_data)); result::RichInvariantResults::new(false, None) - } else if !invariant_contract.is_optimization() { - // In optimization mode, keep reverted calls to preserve - // warp/roll values for correct replay during shrinking. + } else if !invariant_contract.is_optimization() + && !self.config.has_delay() + { + // Delay-enabled campaigns keep reverted calls so shrinking can + // preserve their warp/roll contribution when building the final + // counterexample. current_run.inputs.pop(); result::RichInvariantResults::new(true, None) } else { @@ -589,21 +657,33 @@ impl<'a, FEN: FoundryEvmNetwork> InvariantExecutor<'a, FEN> { if let Some(progress) = progress { // If running with progress then increment completed runs. progress.inc(1); - // Display metrics in progress bar. - if edge_coverage_enabled { - progress.set_message(format!("{}", &corpus_manager.metrics)); + // Display current best value and/or corpus metrics in progress bar. + let best = invariant_test.test_data.optimization_best_value; + if edge_coverage_enabled || best.is_some() { + let mut msg = String::new(); + if let Some(best) = best { + msg.push_str(&format!("best: {best}")); + } + if edge_coverage_enabled { + if !msg.is_empty() { + msg.push_str(", "); + } + msg.push_str(&format!("{}", &corpus_manager.metrics)); + } + progress.set_message(msg); } } else if edge_coverage_enabled && last_metrics_report.elapsed() > DURATION_BETWEEN_METRICS_REPORT { - // Display metrics inline if corpus dir set. - let metrics = json!({ - "timestamp": SystemTime::now() - .duration_since(UNIX_EPOCH)? - .as_secs(), - "invariant": invariant_contract.invariant_function.name, - "metrics": &corpus_manager.metrics, - }); + // Display corpus metrics inline as JSON. + let metrics = build_invariant_progress_json( + SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(), + &invariant_contract.invariant_function.name, + &corpus_manager.metrics, + invariant_test.test_data.optimization_best_value, + throughput, + campaign_start.elapsed(), + ); let _ = sh_println!("{}", serde_json::to_string(&metrics)?); last_metrics_report = Instant::now(); } @@ -1085,18 +1165,16 @@ fn collect_data( run_depth: u32, ) { // Verify it has no code. - let mut has_code = false; - if let Some(Some(code)) = + let has_code = if let Some(Some(code)) = state_changeset.get(&tx.sender).map(|account| account.info.code.as_ref()) { - has_code = !code.is_empty(); - } + !code.is_empty() + } else { + false + }; // We keep the nonce changes to apply later. - let mut sender_changeset = None; - if !has_code { - sender_changeset = state_changeset.remove(&tx.sender); - } + let sender_changeset = if has_code { None } else { state_changeset.remove(&tx.sender) }; // Collect values from fuzzed call result and add them to fuzz dictionary. invariant_test.fuzz_state.collect_values_from_call( @@ -1174,3 +1252,53 @@ pub(crate) fn execute_tx( .call_raw(tx.sender, tx.call_details.target, tx.call_details.calldata.clone(), U256::ZERO) .map_err(|e| eyre!(format!("Could not make raw evm call: {e}"))) } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn invariant_progress_json_includes_throughput_fields() { + let mut throughput = InvariantThroughputMetrics::default(); + throughput.record_call(20); + throughput.record_call(30); + + let payload = build_invariant_progress_json( + 123, + "invariant_balance", + &json!({ "corpus_count": 7 }), + Some(I256::try_from(42).unwrap()), + throughput, + Duration::from_secs(10), + ); + + assert_eq!(payload["timestamp"], json!(123)); + assert_eq!(payload["invariant"], json!("invariant_balance")); + assert_eq!(payload["metrics"]["corpus_count"], json!(7)); + assert_eq!(payload["total_txs"], json!(2)); + assert_eq!(payload["total_gas"], json!(50)); + assert!((payload["tx_per_sec"].as_f64().unwrap() - 0.2).abs() < 1e-12); + assert!((payload["gas_per_sec"].as_f64().unwrap() - 5.0).abs() < 1e-12); + assert_eq!(payload["optimization_best"], json!("42")); + } + + #[test] + fn invariant_progress_json_zero_elapsed_reports_zero_rates() { + let mut throughput = InvariantThroughputMetrics::default(); + throughput.record_call(21_000); + + let payload = build_invariant_progress_json( + 456, + "invariant_zero_elapsed", + &json!({ "corpus_count": 1 }), + None, + throughput, + Duration::ZERO, + ); + + assert_eq!(payload["tx_per_sec"], json!(0.0)); + assert_eq!(payload["gas_per_sec"], json!(0.0)); + assert!(payload.get("optimization_best").is_none()); + } +} diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 75e4bd2cc1130..8c38dc854a7c8 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -51,7 +51,7 @@ pub(crate) struct RichInvariantResults { } impl RichInvariantResults { - pub(crate) fn new(can_continue: bool, call_result: Option>) -> Self { + pub(crate) const fn new(can_continue: bool, call_result: Option>) -> Self { Self { can_continue, call_result } } } @@ -183,10 +183,10 @@ pub(crate) fn can_continue( invariant_data.failures.error = Some(InvariantFuzzError::Revert(case_data)); return Ok(RichInvariantResults::new(false, None)); - } else if call_result.reverted && !is_optimization { - // If we don't fail test on revert then remove last reverted call from inputs. - // In optimization mode, we keep reverted calls to preserve warp/roll values - // for correct replay during shrinking. + } else if call_result.reverted && !is_optimization && !invariant_config.has_delay() { + // If we don't fail test on revert then remove the reverted call from inputs. + // Delay-enabled campaigns keep reverted calls so shrinking can preserve their + // warp/roll contribution when building the final counterexample. invariant_run.inputs.pop(); } } diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 2badd12300432..9dc21e24eb7fc 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -35,7 +35,7 @@ impl CallSequenceShrinker { } /// Advance to the next call index, wrapping around to 0 at the end. - fn next_index(&self, call_idx: usize) -> usize { + const fn next_index(&self, call_idx: usize) -> usize { if call_idx + 1 == self.call_sequence_len { 0 } else { call_idx + 1 } } } @@ -87,6 +87,37 @@ fn apply_warp_roll_to_env( } } +/// Builds the final shrunk sequence from the shrinker state. +/// +/// When `accumulate_warp_roll` is enabled, warp/roll from removed calls is folded into the next +/// kept call so the final sequence remains reproducible. +fn build_shrunk_sequence( + calls: &[BasicTxDetails], + shrinker: &CallSequenceShrinker, + accumulate_warp_roll: bool, +) -> Vec { + if !accumulate_warp_roll { + return shrinker.current().map(|idx| calls[idx].clone()).collect(); + } + + let mut result = Vec::new(); + let mut accumulated_warp = U256::ZERO; + let mut accumulated_roll = U256::ZERO; + + for (idx, call) in calls.iter().enumerate() { + accumulated_warp += call.warp.unwrap_or(U256::ZERO); + accumulated_roll += call.roll.unwrap_or(U256::ZERO); + + if shrinker.included_calls.test(idx) { + result.push(apply_warp_roll(call, accumulated_warp, accumulated_roll)); + accumulated_warp = U256::ZERO; + accumulated_roll = U256::ZERO; + } + } + + result +} + pub(crate) fn shrink_sequence( config: &InvariantConfig, invariant_contract: &InvariantContract<'_>, @@ -108,6 +139,7 @@ pub(crate) fn shrink_sequence( return Ok(vec![]); } + let accumulate_warp_roll = config.has_delay(); let mut call_idx = 0; let mut shrinker = CallSequenceShrinker::new(calls.len()); @@ -125,6 +157,7 @@ pub(crate) fn shrink_sequence( target_address, calldata.clone(), CheckSequenceOptions { + accumulate_warp_roll, fail_on_revert: config.fail_on_revert, call_after_invariant: invariant_contract.call_after_invariant, rd: None, @@ -144,7 +177,7 @@ pub(crate) fn shrink_sequence( call_idx = shrinker.next_index(call_idx); } - Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) + Ok(build_shrunk_sequence(calls, &shrinker, accumulate_warp_roll)) } /// Checks if the given call sequence breaks the invariant. @@ -153,7 +186,25 @@ pub(crate) fn shrink_sequence( /// persisted failures. /// Returns the result of invariant check (and afterInvariant call if needed) and if sequence was /// entirely applied. +/// +/// When `options.accumulate_warp_roll` is enabled, warp/roll from removed calls is folded into the +/// next kept call so the candidate sequence stays representable as a concrete counterexample. pub fn check_sequence( + executor: Executor, + calls: &[BasicTxDetails], + sequence: Vec, + test_address: Address, + calldata: Bytes, + options: CheckSequenceOptions<'_>, +) -> eyre::Result<(bool, bool, Option)> { + if options.accumulate_warp_roll { + check_sequence_with_accumulation(executor, calls, sequence, test_address, calldata, options) + } else { + check_sequence_simple(executor, calls, sequence, test_address, calldata, options) + } +} + +fn check_sequence_simple( mut executor: Executor, calls: &[BasicTxDetails], sequence: Vec, @@ -179,9 +230,59 @@ pub fn check_sequence( } } - // Check the invariant for call sequence. + finish_sequence_check(&executor, test_address, calldata, &options) +} + +fn check_sequence_with_accumulation( + mut executor: Executor, + calls: &[BasicTxDetails], + sequence: Vec, + test_address: Address, + calldata: Bytes, + options: CheckSequenceOptions<'_>, +) -> eyre::Result<(bool, bool, Option)> { + let mut accumulated_warp = U256::ZERO; + let mut accumulated_roll = U256::ZERO; + let mut seq_iter = sequence.iter().peekable(); + + for (idx, tx) in calls.iter().enumerate() { + accumulated_warp += tx.warp.unwrap_or(U256::ZERO); + accumulated_roll += tx.roll.unwrap_or(U256::ZERO); + + if seq_iter.peek() != Some(&&idx) { + continue; + } + + seq_iter.next(); + + let tx_with_accumulated = apply_warp_roll(tx, accumulated_warp, accumulated_roll); + let mut call_result = execute_tx(&mut executor, &tx_with_accumulated)?; + + if call_result.reverted { + if options.fail_on_revert && call_result.result.as_ref() != MAGIC_ASSUME { + return Ok((false, false, call_failure_reason(call_result, options.rd))); + } + } else { + executor.commit(&mut call_result); + } + + accumulated_warp = U256::ZERO; + accumulated_roll = U256::ZERO; + } + + // Unlike optimization mode we intentionally do not apply trailing warp/roll before the + // invariant call: those delays would not be representable in the final shrunk sequence. + finish_sequence_check(&executor, test_address, calldata, &options) +} + +fn finish_sequence_check( + executor: &Executor, + test_address: Address, + calldata: Bytes, + options: &CheckSequenceOptions<'_>, +) -> eyre::Result<(bool, bool, Option)> { let (invariant_result, mut success) = - call_invariant_function(&executor, test_address, calldata)?; + call_invariant_function(executor, test_address, calldata)?; if !success { return Ok((false, true, call_failure_reason(invariant_result, options.rd))); } @@ -190,7 +291,7 @@ pub fn check_sequence( // declared. if success && options.call_after_invariant { let (after_invariant_result, after_invariant_success) = - call_after_invariant_function(&executor, test_address)?; + call_after_invariant_function(executor, test_address)?; success = after_invariant_success; if !success { return Ok((false, true, call_failure_reason(after_invariant_result, options.rd))); @@ -201,6 +302,7 @@ pub fn check_sequence( } pub struct CheckSequenceOptions<'a> { + pub accumulate_warp_roll: bool, pub fail_on_revert: bool, pub call_after_invariant: bool, pub rd: Option<&'a RevertDecoder>, @@ -279,23 +381,7 @@ pub(crate) fn shrink_sequence_value( call_idx = shrinker.next_index(call_idx); } - // Build the final shrunk sequence, accumulating warp/roll from removed calls. - let mut result = Vec::new(); - let mut accumulated_warp = U256::ZERO; - let mut accumulated_roll = U256::ZERO; - - for (idx, call) in calls.iter().enumerate() { - accumulated_warp += call.warp.unwrap_or(U256::ZERO); - accumulated_roll += call.roll.unwrap_or(U256::ZERO); - - if shrinker.included_calls.test(idx) { - result.push(apply_warp_roll(call, accumulated_warp, accumulated_roll)); - accumulated_warp = U256::ZERO; - accumulated_roll = U256::ZERO; - } - } - - Ok(result) + Ok(build_shrunk_sequence(calls, &shrinker, true)) } /// Executes a call sequence and returns the optimization value (int256) from the invariant @@ -347,3 +433,48 @@ pub fn check_sequence_value( Ok(None) } + +#[cfg(test)] +mod tests { + use super::{CallSequenceShrinker, build_shrunk_sequence}; + use alloy_primitives::{Address, Bytes, U256}; + use foundry_evm_fuzz::{BasicTxDetails, CallDetails}; + use proptest::bits::BitSetLike; + + fn tx(warp: Option, roll: Option) -> BasicTxDetails { + BasicTxDetails { + warp: warp.map(U256::from), + roll: roll.map(U256::from), + sender: Address::ZERO, + call_details: CallDetails { target: Address::ZERO, calldata: Bytes::new() }, + } + } + + #[test] + fn build_shrunk_sequence_accumulates_removed_delay_into_next_kept_call() { + let calls = vec![tx(Some(3), Some(5)), tx(Some(7), Some(11)), tx(Some(13), Some(17))]; + let mut shrinker = CallSequenceShrinker::new(calls.len()); + shrinker.included_calls.clear(0); + + let shrunk = build_shrunk_sequence(&calls, &shrinker, true); + + assert_eq!(shrunk.len(), 2); + assert_eq!(shrunk[0].warp, Some(U256::from(10))); + assert_eq!(shrunk[0].roll, Some(U256::from(16))); + assert_eq!(shrunk[1].warp, Some(U256::from(13))); + assert_eq!(shrunk[1].roll, Some(U256::from(17))); + } + + #[test] + fn build_shrunk_sequence_does_not_move_trailing_delay_backward() { + let calls = vec![tx(Some(3), Some(5)), tx(Some(7), Some(11))]; + let mut shrinker = CallSequenceShrinker::new(calls.len()); + shrinker.included_calls.clear(1); + + let shrunk = build_shrunk_sequence(&calls, &shrinker, true); + + assert_eq!(shrunk.len(), 1); + assert_eq!(shrunk[0].warp, Some(U256::from(3))); + assert_eq!(shrunk[0].roll, Some(U256::from(5))); + } +} diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index eaf98d3dd8fdc..8f377e899bfd4 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -171,42 +171,42 @@ impl Executor { } /// Returns a reference to the EVM environment (block and cfg). - pub fn evm_env(&self) -> &EvmEnvFor { + pub const fn evm_env(&self) -> &EvmEnvFor { &self.evm_env } /// Returns a mutable reference to the EVM environment (block and cfg). - pub fn evm_env_mut(&mut self) -> &mut EvmEnvFor { + pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor { &mut self.evm_env } /// Returns a reference to the transaction environment. - pub fn tx_env(&self) -> &TxEnvFor { + pub const fn tx_env(&self) -> &TxEnvFor { &self.tx_env } /// Returns a mutable reference to the transaction environment. - pub fn tx_env_mut(&mut self) -> &mut TxEnvFor { + pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor { &mut self.tx_env } /// Returns a reference to the EVM inspector. - pub fn inspector(&self) -> &InspectorStack { + pub const fn inspector(&self) -> &InspectorStack { &self.inspector } /// Returns a mutable reference to the EVM inspector. - pub fn inspector_mut(&mut self) -> &mut InspectorStack { + pub const fn inspector_mut(&mut self) -> &mut InspectorStack { &mut self.inspector } /// Returns the EVM spec. - pub fn spec_id(&self) -> SpecFor { + pub const fn spec_id(&self) -> SpecFor { self.evm_env.cfg_env.spec } /// Sets the EVM spec. - pub fn set_spec_id(&mut self, spec_id: SpecFor) { + pub const fn set_spec_id(&mut self, spec_id: SpecFor) { self.evm_env.cfg_env.spec = spec_id; } @@ -214,24 +214,24 @@ impl Executor { /// /// This is different from the gas limit imposed by the passed in environment, as those limits /// are used by the EVM for certain opcodes like `gaslimit`. - pub fn gas_limit(&self) -> u64 { + pub const fn gas_limit(&self) -> u64 { self.gas_limit } /// Sets the gas limit for calls and deployments. - pub fn set_gas_limit(&mut self, gas_limit: u64) { + pub const fn set_gas_limit(&mut self, gas_limit: u64) { self.gas_limit = gas_limit; } /// Returns whether `failed()` should be called on the test contract to determine if the test /// failed. - pub fn legacy_assertions(&self) -> bool { + pub const fn legacy_assertions(&self) -> bool { self.legacy_assertions } /// Sets whether `failed()` should be called on the test contract to determine if the test /// failed. - pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) { + pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) { self.legacy_assertions = legacy_assertions; } @@ -957,7 +957,9 @@ impl RawCallResult { /// Converts the result of the call into an `EvmError`. pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError { - if let Some(reason) = SkipReason::decode(&self.result) { + if self.reverter == Some(CHEATCODE_ADDRESS) + && let Some(reason) = SkipReason::decode(&self.result) + { return EvmError::Skip(reason); } let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason); @@ -965,7 +967,7 @@ impl RawCallResult { } /// Converts the result of the call into an `ExecutionErr`. - pub fn into_execution_error(self, reason: String) -> ExecutionErr { + pub const fn into_execution_error(self, reason: String) -> ExecutionErr { ExecutionErr { raw: self, reason } } @@ -1150,7 +1152,7 @@ impl FuzzTestTimer { } /// Whether the fuzz test timer is enabled. - pub fn is_enabled(&self) -> bool { + pub const fn is_enabled(&self) -> bool { self.inner.is_some() } @@ -1192,3 +1194,45 @@ impl EarlyExit { self.inner.load(Ordering::Relaxed) } } + +#[cfg(test)] +mod tests { + use super::*; + use foundry_evm_core::constants::MAGIC_SKIP; + + #[test] + fn cheatcode_skip_payload_is_classified_as_skip() { + let raw = RawCallResult:: { + result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"), + reverter: Some(CHEATCODE_ADDRESS), + ..Default::default() + }; + + let err = raw.into_evm_error(None); + assert!(matches!(err, EvmError::Skip(_))); + } + + #[test] + fn forged_skip_payload_from_non_cheatcode_is_execution_error() { + let raw = RawCallResult:: { + result: Bytes::from_static(MAGIC_SKIP), + reverter: Some(CALLER), + ..Default::default() + }; + + let err = raw.into_evm_error(None); + assert!(matches!(err, EvmError::Execution(_))); + } + + #[test] + fn skip_payload_without_reverter_is_execution_error() { + let raw = RawCallResult:: { + result: Bytes::from_static(MAGIC_SKIP), + reverter: None, + ..Default::default() + }; + + let err = raw.into_evm_error(None); + assert!(matches!(err, EvmError::Execution(_))); + } +} diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 54a77399fa2c9..ac506855355dc 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -1,28 +1,26 @@ -use crate::executors::{Executor, ExecutorBuilder}; -use alloy_primitives::{Address, U256, map::HashMap}; +use crate::{ + Env, + executors::{Executor, ExecutorBuilder}, +}; +use alloy_primitives::{Address, FixedBytes, U256, address, map::HashMap}; use alloy_rpc_types::state::StateOverride; use eyre::Context; use foundry_compilers::artifacts::EvmVersion; -use foundry_config::{Chain, Config, evm_spec_id}; -use foundry_evm_core::{ - backend::Backend, - evm::{BlockEnvFor, EvmEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor}, - fork::CreateFork, - opts::EvmOpts, -}; +use foundry_config::{Chain, Config, utils::evm_spec_id}; +use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use foundry_evm_networks::NetworkConfigs; use foundry_evm_traces::TraceMode; -use revm::{context::Transaction, state::Bytecode}; +use revm::{primitives::hardfork::SpecId, state::Bytecode}; use std::ops::{Deref, DerefMut}; /// A default executor with tracing enabled -pub struct TracingExecutor { - executor: Executor, +pub struct TracingExecutor { + executor: Executor, } -impl TracingExecutor { +impl TracingExecutor { pub fn new( - env: (EvmEnvFor, TxEnvFor), + env: Env, fork: CreateFork, version: Option, trace_mode: TraceMode, @@ -33,12 +31,12 @@ impl TracingExecutor { let db = Backend::spawn(Some(fork))?; // configures a bare version of the evm executor: no cheatcode and log_collector inspector // is enabled, tracing will be enabled only for the targeted transaction - let mut executor = ExecutorBuilder::default() + let mut executor = ExecutorBuilder::new() .inspectors(|stack| { stack.trace_mode(trace_mode).networks(networks).create2_deployer(create2_deployer) }) - .spec_id_opt(version.map(evm_spec_id::>)) - .build(env.0, env.1, db); + .spec_id(evm_spec_id(version.unwrap_or_default())) + .build(env, db); // Apply the state overrides. if let Some(state_overrides) = state_overrides { @@ -73,7 +71,7 @@ impl TracingExecutor { } /// Returns the spec id of the executor - pub fn spec_id(&self) -> SpecFor { + pub fn spec_id(&self) -> SpecId { self.executor.spec_id() } @@ -81,31 +79,56 @@ impl TracingExecutor { pub async fn get_fork_material( config: &mut Config, mut evm_opts: EvmOpts, - ) -> eyre::Result<(EvmEnvFor, TxEnvFor, CreateFork, Chain, NetworkConfigs)> { + ) -> eyre::Result<(Env, CreateFork, Chain, NetworkConfigs)> { evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); evm_opts.fork_block_number = config.fork_block_number; - let (evm_env, tx_env, fork_block) = - evm_opts.env::, BlockEnvFor, TxEnvFor>().await?; + let env = evm_opts.evm_env().await?; - let fork = evm_opts.get_fork(config, evm_env.cfg_env.chain_id, fork_block).unwrap(); - let networks = evm_opts.networks.with_chain_id(evm_env.cfg_env.chain_id); + let fork = evm_opts.get_fork(config, env.clone()).unwrap(); + let networks = evm_opts.networks.with_chain_id(env.evm_env.cfg_env.chain_id); config.labels.extend(networks.precompiles_label()); - let chain = tx_env.chain_id().unwrap().into(); - Ok((evm_env, tx_env, fork, chain, networks)) + let chain = env.tx.chain_id.unwrap().into(); + Ok((env, fork, chain, networks)) + } + + /// Processes the beacon block root by storing it in the appropriate storage slots. + pub fn process_beacon_block_root( + &mut self, + block_timestamp: u64, + beacon_root: FixedBytes<32>, + ) -> eyre::Result<()> { + const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); + const HISTORY_BUFFER_LENGTH: u64 = 8192; + + let timestamp_index = block_timestamp % HISTORY_BUFFER_LENGTH; + let root_index = timestamp_index + HISTORY_BUFFER_LENGTH; + + let timestamp_slot = U256::from(timestamp_index); + let root_slot = U256::from(root_index); + + self.set_storage_slot(BEACON_ROOTS_ADDRESS, timestamp_slot, U256::from(block_timestamp))?; + + self.set_storage_slot( + BEACON_ROOTS_ADDRESS, + root_slot, + U256::from_be_bytes(beacon_root.into()), + )?; + + Ok(()) } } -impl Deref for TracingExecutor { - type Target = Executor; +impl Deref for TracingExecutor { + type Target = Executor; fn deref(&self) -> &Self::Target { &self.executor } } -impl DerefMut for TracingExecutor { +impl DerefMut for TracingExecutor { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.executor } diff --git a/crates/evm/evm/src/inspectors/chisel_state.rs b/crates/evm/evm/src/inspectors/chisel_state.rs index 1e776d4bfcdfa..1388f3c18140f 100644 --- a/crates/evm/evm/src/inspectors/chisel_state.rs +++ b/crates/evm/evm/src/inspectors/chisel_state.rs @@ -16,7 +16,7 @@ pub struct ChiselState { impl ChiselState { /// Create a new Chisel state inspector. #[inline] - pub fn new(final_pc: usize) -> Self { + pub const fn new(final_pc: usize) -> Self { Self { final_pc, state: None } } } diff --git a/crates/evm/evm/src/inspectors/revert_diagnostic.rs b/crates/evm/evm/src/inspectors/revert_diagnostic.rs index fae1f3d22b6a1..e26a4ee9aaa9a 100644 --- a/crates/evm/evm/src/inspectors/revert_diagnostic.rs +++ b/crates/evm/evm/src/inspectors/revert_diagnostic.rs @@ -15,7 +15,7 @@ use std::fmt; const IGNORE: [Address; 2] = [HARDHAT_CONSOLE_ADDRESS, CHEATCODE_ADDRESS]; /// Checks if the call scheme corresponds to any sort of delegate call -pub fn is_delegatecall(scheme: CallScheme) -> bool { +pub const fn is_delegatecall(scheme: CallScheme) -> bool { matches!(scheme, CallScheme::DelegateCall | CallScheme::CallCode) } @@ -68,12 +68,12 @@ pub struct RevertDiagnostic { impl RevertDiagnostic { /// Returns the effective target address whose code would be executed. /// For delegate calls, this is the `bytecode_address`. Otherwise, it's the `target_address`. - fn code_target_address(&self, inputs: &mut CallInputs) -> Address { + const fn code_target_address(&self, inputs: &mut CallInputs) -> Address { if is_delegatecall(inputs.scheme) { inputs.bytecode_address } else { inputs.target_address } } /// Derives the revert reason based on the cached data. Should only be called after a revert. - fn reason(&self) -> Option { + const fn reason(&self) -> Option { if let Some((addr, scheme, _)) = self.non_contract_call { let reason = if is_delegatecall(scheme) { DetailedRevertReason::DelegateCallToNonContract(addr) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 374dfca8febec..dcbb5a109ae9f 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -129,7 +129,7 @@ impl InspectorStackBuilder { /// Set the gas price. #[inline] - pub fn gas_price(mut self, gas_price: u128) -> Self { + pub const fn gas_price(mut self, gas_price: u128) -> Self { self.gas_price = Some(gas_price); self } @@ -157,28 +157,28 @@ impl InspectorStackBuilder { /// Set the Chisel inspector. #[inline] - pub fn chisel_state(mut self, final_pc: usize) -> Self { + pub const fn chisel_state(mut self, final_pc: usize) -> Self { self.chisel_state = Some(final_pc); self } /// Set the log collector, and whether to print the logs directly to stdout. #[inline] - pub fn logs(mut self, live_logs: bool) -> Self { + pub const fn logs(mut self, live_logs: bool) -> Self { self.logs = Some(live_logs); self } /// Set whether to collect line coverage information. #[inline] - pub fn line_coverage(mut self, yes: bool) -> Self { + pub const fn line_coverage(mut self, yes: bool) -> Self { self.line_coverage = Some(yes); self } /// Set whether to enable the trace printer. #[inline] - pub fn print(mut self, yes: bool) -> Self { + pub const fn print(mut self, yes: bool) -> Self { self.print = Some(yes); self } @@ -196,20 +196,20 @@ impl InspectorStackBuilder { /// Set whether to enable the call isolation. /// For description of call isolation, see [`InspectorStack::enable_isolation`]. #[inline] - pub fn enable_isolation(mut self, yes: bool) -> Self { + pub const fn enable_isolation(mut self, yes: bool) -> Self { self.enable_isolation = yes; self } /// Set networks with enabled features. #[inline] - pub fn networks(mut self, networks: NetworkConfigs) -> Self { + pub const fn networks(mut self, networks: NetworkConfigs) -> Self { self.networks = networks; self } #[inline] - pub fn create2_deployer(mut self, create2_deployer: Address) -> Self { + pub const fn create2_deployer(mut self, create2_deployer: Address) -> Self { self.create2_deployer = create2_deployer; self } @@ -459,13 +459,9 @@ impl CheatcodesExecutor for InspectorStackInner { fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option
) { self.in_inner_context = enabled; - self.inner_context_data = if enabled { - Some(InnerContextData { - original_origin: original_origin.expect("origin required when enabling inner ctx"), - }) - } else { - None - }; + self.inner_context_data = enabled.then(|| InnerContextData { + original_origin: original_origin.expect("origin required when enabling inner ctx"), + }); } } @@ -541,13 +537,13 @@ impl InspectorStack { /// Set whether to enable call isolation. #[inline] - pub fn enable_isolation(&mut self, yes: bool) { + pub const fn enable_isolation(&mut self, yes: bool) { self.inner.enable_isolation = yes; } /// Set networks with enabled features. #[inline] - pub fn networks(&mut self, networks: NetworkConfigs) { + pub const fn networks(&mut self, networks: NetworkConfigs) { self.inner.networks = networks; } diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index 9c47a37680254..2d0e62ed4ec60 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -198,7 +198,7 @@ pub struct TargetedContract { impl TargetedContract { /// Returns a new `TargetedContract` instance. - pub fn new(identifier: String, abi: JsonAbi) -> Self { + pub const fn new(identifier: String, abi: JsonAbi) -> Self { Self { identifier, abi, @@ -225,15 +225,15 @@ impl TargetedContract { /// 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()) - } else { + if self.targeted_functions.is_empty() { Either::Right(self.abi.functions().filter(|&func| { !matches!( func.state_mutability, alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View ) && !self.excluded_functions.contains(func) })) + } else { + Either::Left(self.targeted_functions.iter()) } } @@ -274,7 +274,7 @@ pub struct InvariantContract<'a> { impl<'a> InvariantContract<'a> { /// Creates a new invariant contract. - pub fn new( + pub const fn new( address: Address, invariant_function: &'a Function, call_after_invariant: bool, diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index 68ff4e033da14..c96d7c2da6420 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -23,7 +23,7 @@ impl IntValueTree { /// # Arguments /// * `start` - Starting value for the tree /// * `fixed` - If `true` the tree would only contain one element and won't be simplified. - fn new(start: I256, fixed: bool) -> Self { + const fn new(start: I256, fixed: bool) -> Self { Self { lo: I256::ZERO, curr: start, hi: start, fixed } } diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 0f7ba181be7ac..8ff473d4ebc93 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -119,9 +119,7 @@ fn select_random_sender( senders: Rc, dictionary_weight: u32, ) -> impl Strategy + use<> { - if !senders.targeted.is_empty() { - any::().prop_map(move |index| *index.get(&senders.targeted)).boxed() - } else { + if senders.targeted.is_empty() { assert!(dictionary_weight <= 100, "dictionary_weight must be <= 100"); proptest::prop_oneof![ 100 - dictionary_weight => fuzz_param(&alloy_dyn_abi::DynSolType::Address), @@ -142,6 +140,8 @@ fn select_random_sender( addr }) .boxed() + } else { + any::().prop_map(move |index| *index.get(&senders.targeted)).boxed() } } diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 39f53e0c81444..177fc7696ce4f 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -460,7 +460,7 @@ impl FuzzDictionary { } } - pub fn values(&self) -> &B256IndexSet { + pub const fn values(&self) -> &B256IndexSet { &self.state_values } @@ -499,7 +499,7 @@ impl FuzzDictionary { } #[inline] - pub fn addresses(&self) -> &AddressIndexSet { + pub const fn addresses(&self) -> &AddressIndexSet { &self.addresses } diff --git a/crates/evm/fuzz/src/strategies/uint.rs b/crates/evm/fuzz/src/strategies/uint.rs index ad6eae502fa42..ebf21ad0d1687 100644 --- a/crates/evm/fuzz/src/strategies/uint.rs +++ b/crates/evm/fuzz/src/strategies/uint.rs @@ -23,7 +23,7 @@ impl UintValueTree { /// # Arguments /// * `start` - Starting value for the tree /// * `fixed` - If `true` the tree would only contain one element and won't be simplified. - fn new(start: U256, fixed: bool) -> Self { + const fn new(start: U256, fixed: bool) -> Self { Self { lo: U256::ZERO, curr: start, hi: start, fixed } } diff --git a/crates/evm/hardforks/src/lib.rs b/crates/evm/hardforks/src/lib.rs index 73cfffe2077eb..d4372e8072633 100644 --- a/crates/evm/hardforks/src/lib.rs +++ b/crates/evm/hardforks/src/lib.rs @@ -79,15 +79,15 @@ impl FromStr for FoundryHardfork { } impl FoundryHardfork { - pub fn ethereum(h: EthereumHardfork) -> Self { + pub const fn ethereum(h: EthereumHardfork) -> Self { Self::Ethereum(h) } - pub fn optimism(h: OpHardfork) -> Self { + pub const fn optimism(h: OpHardfork) -> Self { Self::Optimism(h) } - pub fn tempo(h: TempoHardfork) -> Self { + pub const fn tempo(h: TempoHardfork) -> Self { Self::Tempo(h) } diff --git a/crates/evm/networks/src/lib.rs b/crates/evm/networks/src/lib.rs index 7f8e04e02bc92..fed98398c4f27 100644 --- a/crates/evm/networks/src/lib.rs +++ b/crates/evm/networks/src/lib.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; pub mod celo; -#[derive(Clone, Debug, Default, Parser, Copy, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Default, Parser, Copy, Serialize, Deserialize, PartialEq, Eq)] pub struct NetworkConfigs { /// Enable Optimism network features. #[arg(help_heading = "Networks", long, conflicts_with_all = ["celo", "tempo"])] @@ -53,11 +53,11 @@ impl NetworkConfigs { Self { tempo: true, ..Default::default() } } - pub fn is_optimism(&self) -> bool { + pub const fn is_optimism(&self) -> bool { self.optimism } - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { self.tempo } @@ -88,7 +88,7 @@ impl NetworkConfigs { self.bypass_prevrandao } - pub fn is_celo(&self) -> bool { + pub const fn is_celo(&self) -> bool { self.celo } diff --git a/crates/evm/traces/src/backtrace/mod.rs b/crates/evm/traces/src/backtrace/mod.rs index b2232b42b410c..1b479954b8d59 100644 --- a/crates/evm/traces/src/backtrace/mod.rs +++ b/crates/evm/traces/src/backtrace/mod.rs @@ -302,7 +302,7 @@ impl<'a> Backtrace<'a> { } /// Returns true if the backtrace is empty. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.frames.is_empty() } } @@ -346,7 +346,7 @@ struct BacktraceFrame { impl BacktraceFrame { /// Creates a new backtrace frame. - fn new(contract_address: Address) -> Self { + const fn new(contract_address: Address) -> Self { Self { contract_address, contract_name: None, @@ -379,7 +379,7 @@ impl BacktraceFrame { } /// Sets the byte offset. - fn with_byte_offset(mut self, offset: usize) -> Self { + const fn with_byte_offset(mut self, offset: usize) -> Self { self.byte_offset = Some(offset); self } diff --git a/crates/evm/traces/src/debug/mod.rs b/crates/evm/traces/src/debug/mod.rs index 4dda5ff720444..c3e0cca2cf80c 100644 --- a/crates/evm/traces/src/debug/mod.rs +++ b/crates/evm/traces/src/debug/mod.rs @@ -18,7 +18,7 @@ pub struct DebugTraceIdentifier { } impl DebugTraceIdentifier { - pub fn new(contracts_sources: ContractSources) -> Self { + pub const fn new(contracts_sources: ContractSources) -> Self { Self { contracts_sources } } @@ -65,7 +65,7 @@ struct DebugStepsWalker<'a> { } impl<'a> DebugStepsWalker<'a> { - pub fn new( + pub const fn new( node: &'a mut CallTraceNode, sources: &'a ContractSources, contract_name: &'a str, diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index bf53210aebbb4..907b096c435a5 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -84,7 +84,7 @@ impl CallTraceDecoderBuilder { /// Sets the verbosity level of the decoder. #[inline] - pub fn with_verbosity(mut self, level: u8) -> Self { + pub const fn with_verbosity(mut self, level: u8) -> Self { self.decoder.verbosity = level; self } @@ -98,14 +98,14 @@ impl CallTraceDecoderBuilder { /// Sets the signature identifier for events and functions. #[inline] - pub fn with_label_disabled(mut self, disable_alias: bool) -> Self { + pub const fn with_label_disabled(mut self, disable_alias: bool) -> Self { self.decoder.disable_labels = disable_alias; self } /// Sets the chain ID for network-specific precompile detection. #[inline] - pub fn with_chain_id(mut self, chain_id: Option) -> Self { + pub const fn with_chain_id(mut self, chain_id: Option) -> Self { self.decoder.chain_id = chain_id; self } @@ -362,7 +362,7 @@ impl CallTraceDecoder { self.revert_decoder.push_error(error); } - pub fn without_label(&mut self, disable: bool) { + pub const fn without_label(&mut self, disable: bool) { self.disable_labels = disable; } @@ -482,7 +482,9 @@ impl CallTraceDecoder { && !contract_selectors.contains(&selector) && (!cdata.is_empty() || !self.receive_contracts.contains(&trace.address)) { - let return_data = if !trace.success { + let return_data = if trace.success { + None + } else { let revert_msg = self.revert_decoder.decode(&trace.output, trace.status); if trace.output.is_empty() || revert_msg.contains("EvmError: Revert") { @@ -493,8 +495,6 @@ impl CallTraceDecoder { } else { Some(revert_msg) } - } else { - None }; return if let Some(func) = functions.first() { @@ -598,20 +598,12 @@ impl CallTraceDecoder { "broadcast" | "startBroadcast" => { // Redact private key if defined // broadcast(uint256) / startBroadcast(uint256) - if !func.inputs.is_empty() && func.inputs[0].ty == "uint256" { - Some(vec!["".to_string()]) - } else { - None - } + (!func.inputs.is_empty() && func.inputs[0].ty == "uint256").then(|| vec!["".to_string()]) } "getNonce" => { // Redact private key if defined // getNonce(Wallet) - if !func.inputs.is_empty() && func.inputs[0].ty == "tuple" { - Some(vec!["".to_string()]) - } else { - None - } + (!func.inputs.is_empty() && func.inputs[0].ty == "tuple").then(|| vec!["".to_string()]) } "sign" | "signP256" => { let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?; diff --git a/crates/evm/traces/src/folded_stack_trace.rs b/crates/evm/traces/src/folded_stack_trace.rs index 2b4e58b8665a7..4fc43594e1b39 100644 --- a/crates/evm/traces/src/folded_stack_trace.rs +++ b/crates/evm/traces/src/folded_stack_trace.rs @@ -189,7 +189,7 @@ impl FoldedStackTraceBuilder { } /// Exit execution of a function call. - pub fn exit(&mut self) { + pub const fn exit(&mut self) { self.exits += 1; } diff --git a/crates/evm/traces/src/identifier/external.rs b/crates/evm/traces/src/identifier/external.rs index 841c77c33f0de..6230c53a57fcd 100644 --- a/crates/evm/traces/src/identifier/external.rs +++ b/crates/evm/traces/src/identifier/external.rs @@ -360,7 +360,7 @@ struct EtherscanFetcher { } impl EtherscanFetcher { - fn new(client: foundry_block_explorers::Client) -> Self { + const fn new(client: foundry_block_explorers::Client) -> Self { Self { client, invalid_api_key: AtomicBool::new(false) } } } diff --git a/crates/evm/traces/src/identifier/local.rs b/crates/evm/traces/src/identifier/local.rs index 3e066c9511a28..05dab2ba44edd 100644 --- a/crates/evm/traces/src/identifier/local.rs +++ b/crates/evm/traces/src/identifier/local.rs @@ -29,14 +29,14 @@ impl<'a> LocalTraceIdentifier<'a> { Self { known_contracts, ordered_ids, contracts_bytecode: None } } - pub fn with_bytecodes(mut self, contracts_bytecode: &'a HashMap) -> Self { + pub const fn with_bytecodes(mut self, contracts_bytecode: &'a HashMap) -> Self { self.contracts_bytecode = Some(contracts_bytecode); self } /// Returns the known contracts. #[inline] - pub fn contracts(&self) -> &'a ContractsByArtifact { + pub const fn contracts(&self) -> &'a ContractsByArtifact { self.known_contracts } diff --git a/crates/evm/traces/src/identifier/mod.rs b/crates/evm/traces/src/identifier/mod.rs index 521c42010f25b..40dd968527d68 100644 --- a/crates/evm/traces/src/identifier/mod.rs +++ b/crates/evm/traces/src/identifier/mod.rs @@ -102,7 +102,7 @@ impl<'a> TraceIdentifiers<'a> { } /// Returns `true` if there are no set identifiers. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.local.is_none() && self.external.is_none() } } diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index ef557a10683ea..3481620fa7c75 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -181,7 +181,7 @@ impl SignaturesIdentifier { /// - `cache_dir` is the cache directory to store the signatures. /// - `offline` disables the OpenChain client. pub fn new_with(cache_dir: Option<&Path>, offline: bool) -> Result { - let client = if !offline { Some(OpenChainClient::new()?) } else { None }; + let client = if offline { None } else { Some(OpenChainClient::new()?) }; let (cache, cache_path) = if let Some(cache_dir) = cache_dir { let path = cache_dir.join("signatures"); let cache = SignaturesCache::load(&path); diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 2c5965b1141f7..f9bf9c5e8471a 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -216,7 +216,7 @@ pub fn render_trace_arena_inner( String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8") } -fn convert_color_choice(choice: shell::ColorChoice) -> revm_inspectors::ColorChoice { +const fn convert_color_choice(choice: shell::ColorChoice) -> revm_inspectors::ColorChoice { match choice { shell::ColorChoice::Auto => revm_inspectors::ColorChoice::Auto, shell::ColorChoice::Always => revm_inspectors::ColorChoice::Always, @@ -237,7 +237,7 @@ impl TraceKind { /// /// [`Deployment`]: TraceKind::Deployment #[must_use] - pub fn is_deployment(self) -> bool { + pub const fn is_deployment(self) -> bool { matches!(self, Self::Deployment) } @@ -245,7 +245,7 @@ impl TraceKind { /// /// [`Setup`]: TraceKind::Setup #[must_use] - pub fn is_setup(self) -> bool { + pub const fn is_setup(self) -> bool { matches!(self, Self::Setup) } @@ -253,7 +253,7 @@ impl TraceKind { /// /// [`Execution`]: TraceKind::Execution #[must_use] - pub fn is_execution(self) -> bool { + pub const fn is_execution(self) -> bool { matches!(self, Self::Execution) } } diff --git a/crates/fmt/src/lib.rs b/crates/fmt/src/lib.rs index c73e4c9553c18..cf973d4d99dff 100644 --- a/crates/fmt/src/lib.rs +++ b/crates/fmt/src/lib.rs @@ -62,7 +62,7 @@ impl DiagnosticsResult { } /// Returns any result produced. - pub fn ok_ref(&self) -> Option<&T> { + pub const fn ok_ref(&self) -> Option<&T> { match self { Self::Ok(s) | Self::OkWithDiagnostics(s, _) | Self::ErrRecovered(s, _) => Some(s), Self::Err(_) => None, @@ -70,7 +70,7 @@ impl DiagnosticsResult { } /// Returns any diagnostics emitted. - pub fn err_ref(&self) -> Option<&E> { + pub const fn err_ref(&self) -> Option<&E> { match self { Self::Ok(_) => None, Self::OkWithDiagnostics(_, d) | Self::ErrRecovered(_, d) | Self::Err(d) => Some(d), @@ -78,12 +78,12 @@ impl DiagnosticsResult { } /// Returns `true` if the result is `Ok`. - pub fn is_ok(&self) -> bool { + pub const fn is_ok(&self) -> bool { matches!(self, Self::Ok(_) | Self::OkWithDiagnostics(_, _)) } /// Returns `true` if the result is `Err`. - pub fn is_err(&self) -> bool { + pub const fn is_err(&self) -> bool { !self.is_ok() } } @@ -234,7 +234,7 @@ pub fn format_ast<'ast>( gcx.sess.source_map(), true, config.wrap_comments, - if matches!(config.style, IndentStyle::Tab) { Some(config.tab_width) } else { None }, + matches!(config.style, IndentStyle::Tab).then(|| config.tab_width), ); let ast = source.ast.as_ref()?; let inline_config = parse_inline_config(gcx.sess, &comments, ast); diff --git a/crates/fmt/src/pp/convenience.rs b/crates/fmt/src/pp/convenience.rs index 65e66d5d42097..a4a2d693c6149 100644 --- a/crates/fmt/src/pp/convenience.rs +++ b/crates/fmt/src/pp/convenience.rs @@ -76,7 +76,7 @@ impl Printer { return true; } - self.out.ends_with(" ") + self.out.ends_with(' ') } pub fn is_beginning_of_line(&self) -> bool { @@ -138,14 +138,14 @@ impl Printer { } impl Token { - pub(crate) fn is_neverbreak(&self) -> bool { + pub(crate) const fn is_neverbreak(&self) -> bool { if let Self::Break(BreakToken { never_break, .. }) = *self { return never_break; } false } - pub(crate) fn is_hardbreak(&self) -> bool { + pub(crate) const fn is_hardbreak(&self) -> bool { if let Self::Break(BreakToken { blank_space, never_break, .. }) = *self { return blank_space == SIZE_INFINITY as usize && !never_break; } diff --git a/crates/fmt/src/pp/mod.rs b/crates/fmt/src/pp/mod.rs index 66b5a0170ea1e..89783f05f5bb8 100644 --- a/crates/fmt/src/pp/mod.rs +++ b/crates/fmt/src/pp/mod.rs @@ -215,7 +215,7 @@ impl Printer { { for i in self.buf.index_range().rev() { let token = &self.buf[i].token; - if let Token::End = token { + if matches!(token, Token::End) { // It's safe to skip the end of a box. continue; } diff --git a/crates/fmt/src/pp/ring.rs b/crates/fmt/src/pp/ring.rs index f958a0dd2fefc..198f3c0a1baee 100644 --- a/crates/fmt/src/pp/ring.rs +++ b/crates/fmt/src/pp/ring.rs @@ -11,7 +11,7 @@ pub(crate) struct RingBuffer { } impl RingBuffer { - pub(crate) fn new() -> Self { + pub(crate) const fn new() -> Self { Self { data: VecDeque::new(), offset: 0 } } diff --git a/crates/fmt/src/state/common.rs b/crates/fmt/src/state/common.rs index e3e37c5169329..b2b2767ad1e9a 100644 --- a/crates/fmt/src/state/common.rs +++ b/crates/fmt/src/state/common.rs @@ -37,12 +37,12 @@ impl<'ast> State<'_, 'ast> { let quote_pos = span.lo() + kind.prefix().len() as u32; self.print_str_lit(kind, quote_pos, symbol.as_str()); } - if !pos.is_last { + if pos.is_last { + self.neverbreak(); + } else { if !self.print_trailing_comment(span.hi(), None) { self.space_if_not_bol(); } - } else { - self.neverbreak(); } } self.end(); @@ -93,10 +93,10 @@ impl<'ast> State<'_, 'ast> { let config = self.config.number_underscore; let is_dec = !["0x", "0b", "0o"].iter().any(|prefix| source.starts_with(prefix)); - let (val, exp) = if !is_dec { - (source, "") - } else { + let (val, exp) = if is_dec { source.split_once(['e', 'E']).unwrap_or((source, "")) + } else { + (source, "") }; let (val, fract) = val.split_once('.').unwrap_or((val, "")); @@ -243,11 +243,12 @@ impl<'ast> State<'_, 'ast> { self.print_inside_parens(|state| { let span = get_span(&values[0]); state.s.cbox(state.ind); - let mut skip_break = true; - if state.peek_comment_before(span.hi()).is_some() { + let skip_break = if state.peek_comment_before(span.hi()).is_some() { state.hardbreak(); - skip_break = false; - } + false + } else { + true + }; state.print_comments(span.lo(), CommentConfig::skip_ws().mixed_prev_space()); print(state, &values[0]); @@ -601,9 +602,10 @@ impl<'ast> State<'_, 'ast> { let enabled = !self.inline_config.is_disabled(Span::new(block_lo, block_lo + BytePos(1))) && !self.handle_span(self.cursor.span(block_lo), true); - match self.peek_comment().and_then(|cmnt| { - if cmnt.span.hi() < block_lo { Some((cmnt.span, cmnt.style)) } else { None } - }) { + match self + .peek_comment() + .and_then(|cmnt| (cmnt.span.hi() < block_lo).then_some((cmnt.span, cmnt.style))) + { Some((span, style)) => { if enabled { // Inline config is not disabled and span not handled @@ -656,14 +658,15 @@ impl<'ast> State<'_, 'ast> { print(self, stmt); let is_disabled = self.inline_config.is_disabled(get_block_span(stmt)); - let mut next_enabled = false; - let mut next_lo = None; - if !is_last { + let (next_enabled, next_lo) = if is_last { + (false, None) + } else { let next_span = get_block_span(&block[i + 1]); - next_enabled = !self.inline_config.is_disabled(next_span); - next_lo = - self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo()); - } + ( + !self.inline_config.is_disabled(next_span), + self.peek_comment_before(next_span.lo()).is_none().then_some(next_span.lo()), + ) + }; // when this stmt and the next one are enabled, break normally (except if last stmt) if !is_disabled @@ -759,10 +762,7 @@ impl<'ast> State<'_, 'ast> { if has_braces { self.word("{"); } - let mut offset = 0; - if let BlockFormat::NoBraces(Some(off)) = block_format { - offset = off; - } + let offset = if let BlockFormat::NoBraces(Some(off)) = block_format { off } else { 0 }; self.print_comments( pos_hi, self.cmnt_config().offset(offset).mixed_no_break().mixed_prev_space().mixed_post_nbsp(), @@ -824,27 +824,27 @@ impl Default for ListFormat { impl ListFormat { // -- GETTER METHODS ------------------------------------------------------- - pub(crate) fn prev_symbol(&self) -> Option<&'static str> { + pub(crate) const fn prev_symbol(&self) -> Option<&'static str> { if let ListFormatKind::Yul { sym_prev, .. } = self.kind { sym_prev } else { None } } - pub(crate) fn post_symbol(&self) -> Option<&'static str> { + pub(crate) const fn post_symbol(&self) -> Option<&'static str> { if let ListFormatKind::Yul { sym_post, .. } = self.kind { sym_post } else { None } } - pub(crate) fn is_consistent(&self) -> bool { + pub(crate) const fn is_consistent(&self) -> bool { matches!(self.kind, ListFormatKind::Consistent) } - pub(crate) fn is_compact(&self) -> bool { + pub(crate) const fn is_compact(&self) -> bool { matches!(self.kind, ListFormatKind::Compact) } - pub(crate) fn is_inline(&self) -> bool { + pub(crate) const fn is_inline(&self) -> bool { matches!(self.kind, ListFormatKind::Inline) } - pub(crate) fn breaks_with_comments(&self) -> bool { + pub(crate) const fn breaks_with_comments(&self) -> bool { self.breaks_cmnts } @@ -880,35 +880,35 @@ impl ListFormat { } } - pub(crate) fn without_ind(mut self, without: bool) -> Self { + pub(crate) const fn without_ind(mut self, without: bool) -> Self { if !matches!(self.kind, ListFormatKind::Inline) { self.no_ind = without; } self } - pub(crate) fn break_single(mut self, value: bool) -> Self { + pub(crate) const fn break_single(mut self, value: bool) -> Self { if !matches!(self.kind, ListFormatKind::Inline) { self.break_single = value; } self } - pub(crate) fn break_cmnts(mut self) -> Self { + pub(crate) const fn break_cmnts(mut self) -> Self { if !matches!(self.kind, ListFormatKind::Inline) { self.breaks_cmnts = true; } self } - pub(crate) fn with_space(mut self) -> Self { + pub(crate) const fn with_space(mut self) -> Self { if !matches!(self.kind, ListFormatKind::Inline) { self.with_space = true; } self } - pub(crate) fn with_delimiters(mut self, with: bool) -> Self { + pub(crate) const fn with_delimiters(mut self, with: bool) -> Self { if matches!(self.kind, ListFormatKind::Compact | ListFormatKind::Consistent) { self.with_delimiters = with; } @@ -947,14 +947,14 @@ pub(crate) enum BlockFormat { } impl BlockFormat { - pub(crate) fn with_braces(&self) -> bool { + pub(crate) const fn with_braces(&self) -> bool { !matches!(self, Self::NoBraces(_)) } - pub(crate) fn breaks(&self) -> bool { + pub(crate) const fn breaks(&self) -> bool { matches!(self, Self::NoBraces(None)) } - pub(crate) fn attempt_single_line(&self) -> bool { + pub(crate) const fn attempt_single_line(&self) -> bool { matches!(self, Self::Compact(_)) } } diff --git a/crates/fmt/src/state/mod.rs b/crates/fmt/src/state/mod.rs index e98eac6f4e5c1..89a9bf152c8c2 100644 --- a/crates/fmt/src/state/mod.rs +++ b/crates/fmt/src/state/mod.rs @@ -44,19 +44,19 @@ pub(super) struct CallContext { } impl CallContext { - pub(super) fn nested(size: usize) -> Self { + pub(super) const fn nested(size: usize) -> Self { Self { kind: CallContextKind::Nested, size, has_indent: false } } - pub(super) fn chained(size: usize, has_indent: bool) -> Self { + pub(super) const fn chained(size: usize, has_indent: bool) -> Self { Self { kind: CallContextKind::Chained, size, has_indent } } - pub(super) fn is_nested(&self) -> bool { + pub(super) const fn is_nested(&self) -> bool { matches!(self.kind, CallContextKind::Nested) } - pub(super) fn is_chained(&self) -> bool { + pub(super) const fn is_chained(&self) -> bool { matches!(self.kind, CallContextKind::Chained) } } @@ -201,11 +201,7 @@ impl<'sess> State<'sess, '_> { Self { s: pp::Printer::new( config.line_length, - if matches!(config.style, IndentStyle::Tab) { - Some(config.tab_width) - } else { - None - }, + matches!(config.style, IndentStyle::Tab).then(|| config.tab_width), ), ind: config.tab_width as isize, sm, @@ -440,8 +436,8 @@ impl State<'_, '_> { // - ends with ',' a line break or a space are required. // - ends with ';' a line break is required. prev_needs_space = match line.chars().next_back() { - Some('[') | Some('(') | Some('{') => self.config.bracket_spacing, - Some(',') | Some(';') => true, + Some('[' | '(' | '{') => self.config.bracket_spacing, + Some(',' | ';') => true, _ => false, }; } @@ -482,10 +478,10 @@ impl<'sess> State<'sess, '_> { } fn cmnt_config(&self) -> CommentConfig { - CommentConfig { ..Default::default() } + Default::default() } - fn print_docs(&mut self, docs: &'_ ast::DocComments<'_>) { + const fn print_docs(&mut self, docs: &'_ ast::DocComments<'_>) { // Intetionally no-op. Handled with `self.comments`. let _ = docs; } @@ -1065,12 +1061,12 @@ impl CommentConfig { Self { skip_blanks: Some(Skip::Trailing), ..Default::default() } } - pub(crate) fn offset(mut self, off: isize) -> Self { + pub(crate) const fn offset(mut self, off: isize) -> Self { self.offset = off; self } - pub(crate) fn no_breaks(mut self) -> Self { + pub(crate) const fn no_breaks(mut self) -> Self { self.iso_no_break = true; self.trailing_no_break = true; self.mixed_no_break_prev = true; @@ -1078,28 +1074,28 @@ impl CommentConfig { self } - pub(crate) fn trailing_no_break(mut self) -> Self { + pub(crate) const fn trailing_no_break(mut self) -> Self { self.trailing_no_break = true; self } - pub(crate) fn mixed_no_break(mut self) -> Self { + pub(crate) const fn mixed_no_break(mut self) -> Self { self.mixed_no_break_prev = true; self.mixed_no_break_post = true; self } - pub(crate) fn mixed_no_break_post(mut self) -> Self { + pub(crate) const fn mixed_no_break_post(mut self) -> Self { self.mixed_no_break_post = true; self } - pub(crate) fn mixed_prev_space(mut self) -> Self { + pub(crate) const fn mixed_prev_space(mut self) -> Self { self.mixed_prev_space = true; self } - pub(crate) fn mixed_post_nbsp(mut self) -> Self { + pub(crate) const fn mixed_post_nbsp(mut self) -> Self { self.mixed_post_nbsp = true; self } diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index f46455b2a6b5d..dc53558a20f33 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -93,7 +93,7 @@ impl<'ast> State<'_, 'ast> { let cmnts = self .comments .iter() - .filter_map(|c| if c.pos() < span.lo() { Some(c.style) } else { None }) + .filter_map(|c| (c.pos() < span.lo()).then_some(c.style)) .collect::>(); if let Some(first) = cmnts.first() @@ -364,7 +364,16 @@ impl<'ast> State<'_, 'ast> { self.print_word("{"); self.end(); - if !body.is_empty() { + if body.is_empty() { + if self.print_comments(span.hi(), CommentConfig::skip_ws()).is_some() { + // Adjust the offset of the trailing break from comment printing + // so the closing brace is not indented + self.s.offset(-self.ind); + } else if self.config.bracket_spacing { + self.nbsp(); + }; + self.end(); + } else { // update block depth self.block_depth += 1; @@ -405,15 +414,6 @@ impl<'ast> State<'_, 'ast> { // restore block depth self.block_depth -= 1; - } else { - if self.print_comments(span.hi(), CommentConfig::skip_ws()).is_some() { - // Adjust the offset of the trailing break from comment printing - // so the closing brace is not indented - self.s.offset(-self.ind); - } else if self.config.bracket_spacing { - self.nbsp(); - }; - self.end(); } self.print_word("}"); @@ -699,16 +699,17 @@ impl<'ast> State<'_, 'ast> { for cmnt in inner_cmnts.into_iter().rev() { self.comments.push_front(cmnt); } - let mut enabled = false; - if !self.handle_span(span, false) { + let enabled = if self.handle_span(span, false) { + false + } else { if !self.is_bol_or_only_ind() { self.space(); } self.ibox(0); print_fn(self); self.cursor.advance_to(span.hi(), true); - enabled = true; - } + true + }; // Print subsequent comments. for cmnt in post_cmnts { let Some(cmnt) = self.handle_comment(cmnt, false) else { @@ -1566,14 +1567,15 @@ impl<'ast> State<'_, 'ast> { } // Trailing comment handling. - let mut is_trailing = false; - if let Some(style) = self.print_comments( + let is_trailing = if let Some(style) = self.print_comments( span.hi(), CommentConfig::skip_ws().mixed_no_break().mixed_prev_space(), ) { skip_break = true; - is_trailing = style.is_trailing(); - } + style.is_trailing() + } else { + false + }; // Adjust indentation and line breaks. match (skip_break, end.is_some()) { @@ -1714,11 +1716,11 @@ impl<'ast> State<'_, 'ast> { self.call_stack.push(CallContext::chained(callee_size, chain_has_indent)); } - if !chain_has_indent { + if chain_has_indent { + self.s.ibox(self.ind); + } else { self.skip_index_break = true; self.cbox(0); - } else { - self.s.ibox(self.ind); } } @@ -2190,7 +2192,9 @@ impl<'ast> State<'_, 'ast> { self.nbsp(); } - if !args.is_empty() { + if args.is_empty() { + self.end(); + } else { self.print_word("returns "); self.print_word("("); self.zerobreak(); @@ -2206,8 +2210,6 @@ impl<'ast> State<'_, 'ast> { ); self.print_word(")"); self.nbsp(); - } else { - self.end(); } if block.is_empty() { self.print_block(block, *try_span); @@ -2678,20 +2680,20 @@ enum MemberOrCallArgs { } impl MemberOrCallArgs { - fn size(&self) -> usize { + const fn size(&self) -> usize { match self { Self::CallArgs(size, ..) | Self::Member(size) => *size, } } - fn member_size(&self) -> usize { + const fn member_size(&self) -> usize { match self { Self::CallArgs(..) => 0, Self::Member(size) => *size, } } - fn has_comments(&self) -> bool { + const fn has_comments(&self) -> bool { matches!(self, Self::CallArgs(.., true)) } } @@ -2750,7 +2752,7 @@ impl<'ast> AttributeCommentMapper<'ast> { let before_limit = self.attributes[a].span.lo(); let inner_limit = self.attributes[a].span.hi(); let after_limit = - if !is_last { self.attributes[a + 1].span.lo() } else { self.limit_pos }; + if is_last { self.limit_pos } else { self.attributes[a + 1].span.lo() }; let mut c = 0; while c < self.comments.len() { @@ -2834,7 +2836,7 @@ impl<'ast> AttributeCommentMapper<'ast> { } } -fn stmt_needs_semi(stmt: &ast::StmtKind<'_>) -> bool { +const fn stmt_needs_semi(stmt: &ast::StmtKind<'_>) -> bool { match stmt { ast::StmtKind::Assembly { .. } | ast::StmtKind::Block { .. } @@ -2879,7 +2881,7 @@ fn item_needs_iso(item: &ast::ItemKind<'_>) -> bool { } } -fn is_binary_expr(expr_kind: &ast::ExprKind<'_>) -> bool { +const fn is_binary_expr(expr_kind: &ast::ExprKind<'_>) -> bool { matches!(expr_kind, ast::ExprKind::Binary(..)) } @@ -2899,7 +2901,7 @@ fn has_complex_successor(expr_kind: &ast::ExprKind<'_>, left: bool) -> bool { } } -fn is_call(expr_kind: &ast::ExprKind<'_>) -> bool { +const fn is_call(expr_kind: &ast::ExprKind<'_>) -> bool { matches!(expr_kind, ast::ExprKind::Call(..)) } @@ -2907,7 +2909,7 @@ fn is_call(expr_kind: &ast::ExprKind<'_>) -> bool { /// Used to determine if `.field` after such a call should avoid breaking. /// E.g., `_lzSend({_dstEid: x, ...}).guid` → true (named args call) /// E.g., `someFunc(a, b).field` → false (positional args) -fn is_call_with_named_args(expr_kind: &ast::ExprKind<'_>) -> bool { +const fn is_call_with_named_args(expr_kind: &ast::ExprKind<'_>) -> bool { if let ast::ExprKind::Call(_, args) = expr_kind { matches!(args.kind, ast::CallArgsKind::Named(_)) } else { diff --git a/crates/fmt/src/state/yul.rs b/crates/fmt/src/state/yul.rs index 0f973624bf9ec..b67fdd5a26263 100644 --- a/crates/fmt/src/state/yul.rs +++ b/crates/fmt/src/state/yul.rs @@ -253,7 +253,9 @@ impl<'ast> State<'_, 'ast> { |s, stmt| { s.print_yul_stmt(stmt); s.print_comments(stmt.span.hi(), CommentConfig::default()); - if i != n_args { + if i == n_args { + s.print_trailing_comment(stmt.span.hi(), Some(span.hi())); + } else { let next_span = block[i + 1].span; s.print_trailing_comment(stmt.span.hi(), Some(next_span.lo())); if !s.is_bol_or_only_ind() && !s.inline_config.is_disabled(stmt.span) { @@ -270,8 +272,6 @@ impl<'ast> State<'_, 'ast> { } } i += 1; - } else { - s.print_trailing_comment(stmt.span.hi(), Some(span.hi())); } }, |b| b.span, diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index c24dc0ba484d7..3be249dd24b1a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -29,6 +29,7 @@ foundry-common.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true foundry-evm.workspace = true +foundry-evm-networks.workspace = true foundry-linking.workspace = true comfy-table.workspace = true @@ -50,19 +51,19 @@ forge-script.workspace = true forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, optional = 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-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } +alloy-rpc-types.workspace = true +alloy-serde.workspace = true alloy-signer.workspace = true alloy-transport.workspace = true - -tempo-alloy.workspace = true +alloy-hardforks.workspace = true revm.workspace = true @@ -104,7 +105,6 @@ quick-junit = "0.5.2" [dev-dependencies] alloy-hardforks.workspace = true anvil.workspace = true -foundry-evm-networks.workspace = true forge-script-sequence.workspace = true foundry-test-utils.workspace = true @@ -122,7 +122,7 @@ asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] tracy-allocator = ["foundry-cli/tracy-allocator"] -aws-kms = ["foundry-wallets/aws-kms"] -gcp-kms = ["foundry-wallets/gcp-kms"] -turnkey = ["foundry-wallets/turnkey"] +aws-kms = ["dep:foundry-wallets", "foundry-wallets/aws-kms"] +gcp-kms = ["dep:foundry-wallets", "foundry-wallets/gcp-kms"] +turnkey = ["dep:foundry-wallets", "foundry-wallets/turnkey"] isolate-by-default = ["foundry-config/isolate-by-default"] diff --git a/crates/forge/src/cmd/bind.rs b/crates/forge/src/cmd/bind.rs index 9ef8edc88f5d7..e356dde7a2ead 100644 --- a/crates/forge/src/cmd/bind.rs +++ b/crates/forge/src/cmd/bind.rs @@ -202,7 +202,7 @@ impl BindArgs { .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 } + dup.insert(name.clone()).then(|| SolMacroGen::new(path, name)) }) .collect::>(); @@ -235,7 +235,14 @@ impl BindArgs { let mut solmacrogen = self.get_solmacrogen(artifacts)?; sh_println!("Generating bindings for {} contracts", solmacrogen.instances.len())?; - if !self.module { + if self.module { + trace!(single_file = self.single_file, "generating module"); + solmacrogen.write_to_module( + bindings_root, + self.single_file, + !self.skip_extra_derives, + )?; + } else { trace!(single_file = self.single_file, "generating crate"); solmacrogen.write_to_crate( &self.crate_name, @@ -248,13 +255,6 @@ impl BindArgs { self.alloy_rev.clone(), !self.skip_extra_derives, )?; - } else { - trace!(single_file = self.single_file, "generating module"); - solmacrogen.write_to_module( - bindings_root, - self.single_file, - !self.skip_extra_derives, - )?; } Ok(()) diff --git a/crates/forge/src/cmd/bind_json.rs b/crates/forge/src/cmd/bind_json.rs index f29ba6f131409..113a32ba5fb7e 100644 --- a/crates/forge/src/cmd/bind_json.rs +++ b/crates/forge/src/cmd/bind_json.rs @@ -157,13 +157,13 @@ impl BindJsonArgs { let mut target_files = HashSet::new(); for (path, source) in &input.input.sources { - if !include.is_empty() { - if !include.iter().any(|matcher| matcher.is_match(path)) { + if include.is_empty() { + // Exclude library files by default + if project.paths.has_library_ancestor(path) { continue; } } else { - // Exclude library files by default - if project.paths.has_library_ancestor(path) { + if !include.iter().any(|matcher| matcher.is_match(path)) { continue; } } @@ -398,7 +398,7 @@ struct PreprocessorVisitor { } impl PreprocessorVisitor { - fn new() -> Self { + const fn new() -> Self { Self { updates: Vec::new() } } @@ -452,7 +452,7 @@ impl<'ast> Visit<'ast> for PreprocessorVisitor { var: &'ast ast::VariableDefinition<'ast>, ) -> ControlFlow { // Remove `immutable` attributes. - if let Some(VarMut::Immutable) = var.mutability { + if var.mutability == Some(VarMut::Immutable) { self.updates.push((var.span, "")); } diff --git a/crates/forge/src/cmd/build.rs b/crates/forge/src/cmd/build.rs index 2812f4d319b7b..55eff1174853e 100644 --- a/crates/forge/src/cmd/build.rs +++ b/crates/forge/src/cmd/build.rs @@ -215,7 +215,7 @@ impl BuildArgs { } /// Returns whether `BuildArgs` was configured with `--watch` - pub fn is_watch(&self) -> bool { + pub const fn is_watch(&self) -> bool { self.watch.watch.is_some() } diff --git a/crates/forge/src/cmd/compiler.rs b/crates/forge/src/cmd/compiler.rs index 39d38b8b249ba..29c2e30cbb0c5 100644 --- a/crates/forge/src/cmd/compiler.rs +++ b/crates/forge/src/cmd/compiler.rs @@ -92,15 +92,9 @@ impl ResolveArgs { }) .collect(); - let evm_version = if shell::verbosity() > 1 { - let evm = EvmVersion::default() - .normalize_version_solc(version) - .unwrap_or_default(); - - Some(evm) - } else { - None - }; + let evm_version = (shell::verbosity() > 1).then(|| { + EvmVersion::default().normalize_version_solc(version).unwrap_or_default() + }); ResolvedCompiler { version: version.clone(), evm_version, paths } }) diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index e5ed66215b0bd..ea034bce87185 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -55,7 +55,6 @@ pub struct CoverageArgs { /// If not specified, the report will be stored in the root of the project. #[arg( long, - short, value_hint = ValueHint::FilePath, value_name = "PATH" )] @@ -316,11 +315,11 @@ impl CoverageArgs { Ok(()) } - pub fn is_watch(&self) -> bool { + pub const fn is_watch(&self) -> bool { self.test.is_watch() } - pub fn watch(&self) -> &WatchArgs { + pub const fn watch(&self) -> &WatchArgs { &self.test.watch } } diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index a6337a266c9a8..122020571ad24 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -426,7 +426,14 @@ impl CreateArgs { } if dry_run { - if !shell::is_json() { + if shell::is_json() { + let output = json!({ + "contract": self.contract.name, + "transaction": &deployer.tx, + "abi":&abi + }); + sh_println!("{}", serde_json::to_string_pretty(&output)?)?; + } else { sh_warn!("Dry run enabled, not broadcasting transaction\n")?; sh_println!("Contract: {}", self.contract.name)?; @@ -439,13 +446,6 @@ impl CreateArgs { sh_warn!( "To broadcast this transaction, add --broadcast to the previous command. See forge create --help for more." )?; - } else { - let output = json!({ - "contract": self.contract.name, - "transaction": &deployer.tx, - "abi":&abi - }); - sh_println!("{}", serde_json::to_string_pretty(&output)?)?; } return Ok(()); @@ -504,7 +504,7 @@ impl CreateArgs { sh_println!("Starting contract verification...")?; let num_of_optimizations = if let Some(optimizer) = self.build.compiler.optimize { - if optimizer { Some(self.build.compiler.optimizer_runs.unwrap_or(200)) } else { None } + optimizer.then(|| self.build.compiler.optimizer_runs.unwrap_or(200)) } else { self.build.compiler.optimizer_runs }; @@ -672,7 +672,7 @@ impl + Clone> DeploymentTxFactory { /// 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: P, timeout: u64) -> Self { + pub const fn new(abi: JsonAbi, bytecode: Bytes, client: P, timeout: u64) -> Self { Self { client, abi, bytecode, timeout, _network: PhantomData } } diff --git a/crates/forge/src/cmd/doc/mod.rs b/crates/forge/src/cmd/doc/mod.rs index 602284ce94ebf..957636111a302 100644 --- a/crates/forge/src/cmd/doc/mod.rs +++ b/crates/forge/src/cmd/doc/mod.rs @@ -133,7 +133,7 @@ impl DocArgs { } /// Returns whether watch mode is enabled - pub fn is_watch(&self) -> bool { + pub const fn is_watch(&self) -> bool { self.watch.watch.is_some() } diff --git a/crates/forge/src/cmd/doc/server.rs b/crates/forge/src/cmd/doc/server.rs index f8d447d52b1fb..d9a83adda8721 100644 --- a/crates/forge/src/cmd/doc/server.rs +++ b/crates/forge/src/cmd/doc/server.rs @@ -38,13 +38,13 @@ impl Server { } /// Set the port to serve on. - pub fn with_port(mut self, port: usize) -> Self { + pub const fn with_port(mut self, port: usize) -> Self { self.port = port; self } /// Set whether to open the browser after serving. - pub fn open(mut self, open: bool) -> Self { + pub const fn open(mut self, open: bool) -> Self { self.open = open; self } diff --git a/crates/forge/src/cmd/eip712.rs b/crates/forge/src/cmd/eip712.rs index 74bbb039cd507..a635820e66a9c 100644 --- a/crates/forge/src/cmd/eip712.rs +++ b/crates/forge/src/cmd/eip712.rs @@ -100,7 +100,7 @@ pub struct Resolver<'gcx> { impl<'gcx> Resolver<'gcx> { /// Constructs a new [`Resolver`] for the supplied [`Hir`] instance. - pub fn new(gcx: Gcx<'gcx>) -> Self { + pub const fn new(gcx: Gcx<'gcx>) -> Self { Self { gcx } } diff --git a/crates/forge/src/cmd/fmt.rs b/crates/forge/src/cmd/fmt.rs index 159f8d87c2a2e..b476cf97a130e 100644 --- a/crates/forge/src/cmd/fmt.rs +++ b/crates/forge/src/cmd/fmt.rs @@ -216,7 +216,7 @@ impl FmtArgs { } /// Returns whether `FmtArgs` was configured with `--watch` - pub fn is_watch(&self) -> bool { + pub const fn is_watch(&self) -> bool { self.watch.watch.is_some() } } diff --git a/crates/forge/src/cmd/inspect.rs b/crates/forge/src/cmd/inspect.rs index 139fd17b0cb96..4eedd0f8f6b08 100644 --- a/crates/forge/src/cmd/inspect.rs +++ b/crates/forge/src/cmd/inspect.rs @@ -251,15 +251,15 @@ fn print_abi(abi: &JsonAbi, should_wrap: bool) -> Result<()> { for func in abi.functions.values().flatten() { let selector = func.selector().to_string(); let state_mut = func.state_mutability.as_json_str(); - let func_sig = if !func.outputs.is_empty() { + let func_sig = if func.outputs.is_empty() { + format!("{}({}) {state_mut}", func.name, get_ty_sig(&func.inputs)) + } else { format!( "{}({}) {state_mut} returns ({})", func.name, get_ty_sig(&func.inputs), get_ty_sig(&func.outputs) ) - } else { - format!("{}({}) {state_mut}", func.name, get_ty_sig(&func.inputs)) }; table.add_row(["function", &func_sig, &selector]); } @@ -370,7 +370,7 @@ fn print_method_identifiers( headers, |table| { for (method, identifier) in method_identifiers { - table.add_row([method, identifier]); + table.add_row([method.as_str(), identifier.as_str()]); } }, should_wrap, @@ -679,8 +679,7 @@ impl PartialEq for ContractArtifactField { type Eos = EvmOutputSelection; matches!( (self, other), - (Self::Abi | Self::Events, Cos::Abi) - | (Self::Errors, Cos::Abi) + (Self::Abi | Self::Events | Self::Errors, Cos::Abi) | (Self::Bytecode, Cos::Evm(Eos::ByteCode(_))) | (Self::DeployedBytecode, Cos::Evm(Eos::DeployedByteCode(_))) | (Self::Assembly | Self::AssemblyOptimized, Cos::Evm(Eos::Assembly)) diff --git a/crates/forge/src/cmd/install.rs b/crates/forge/src/cmd/install.rs index 81fa24312640f..3fcad958a6cc0 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -567,7 +567,7 @@ impl Installer<'_> { sh_println!("[{i}] {c} selected")?; return Ok(c.clone()); } - _ => continue, + _ => {} } } } diff --git a/crates/forge/src/cmd/selectors.rs b/crates/forge/src/cmd/selectors.rs index a0c319fe9c947..252544e7ea5e8 100644 --- a/crates/forge/src/cmd/selectors.rs +++ b/crates/forge/src/cmd/selectors.rs @@ -214,7 +214,7 @@ impl SelectorsSubcommands { .filter_map(|(k1, v1)| { second_method_map .iter() - .find_map(|(k2, v2)| if **v2 == *v1 { Some((k2, v2)) } else { None }) + .find_map(|(k2, v2)| (**v2 == *v1).then_some((k2, v2))) .map(|(k2, v2)| (v2, k1, k2)) }) .collect(); @@ -286,7 +286,7 @@ impl SelectorsSubcommands { .collect() }; - let mut artifacts = artifacts.into_iter().peekable(); + let mut artifacts = artifacts.into_iter(); #[derive(PartialEq, PartialOrd, Eq, Ord)] enum SelectorType { diff --git a/crates/forge/src/cmd/snapshot.rs b/crates/forge/src/cmd/snapshot.rs index 1a37bd02aa4eb..c8dc2ba72aae1 100644 --- a/crates/forge/src/cmd/snapshot.rs +++ b/crates/forge/src/cmd/snapshot.rs @@ -89,7 +89,7 @@ pub struct GasSnapshotArgs { impl GasSnapshotArgs { /// Returns whether `GasSnapshotArgs` was configured with `--watch` - pub fn is_watch(&self) -> bool { + pub const fn is_watch(&self) -> bool { self.test.is_watch() } @@ -181,7 +181,7 @@ enum DiffSortOrder { } impl GasSnapshotConfig { - fn is_in_gas_range(&self, gas_used: u64) -> bool { + const fn is_in_gas_range(&self, gas_used: u64) -> bool { if let Some(min) = self.min && gas_used < min { @@ -355,7 +355,7 @@ impl GasSnapshotDiff { /// /// `> 0` if the source used more gas /// `< 0` if the target used more gas - fn gas_change(&self) -> i128 { + const fn gas_change(&self) -> i128 { self.source_gas_used.gas() as i128 - self.target_gas_used.gas() as i128 } @@ -418,7 +418,7 @@ fn diff( let mut diffs = Vec::with_capacity(tests.len()); let mut new_tests = Vec::new(); - for test in tests.into_iter() { + for test in tests { if let Some(target_gas_used) = snaps.get(&(test.contract_name().to_string(), test.signature.clone())).cloned() { diff --git a/crates/forge/src/cmd/test/filter.rs b/crates/forge/src/cmd/test/filter.rs index f39535a97bb92..c9219ec504d6a 100644 --- a/crates/forge/src/cmd/test/filter.rs +++ b/crates/forge/src/cmd/test/filter.rs @@ -46,7 +46,7 @@ pub struct FilterArgs { impl FilterArgs { /// Returns true if the filter is empty. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.test_pattern.is_none() && self.test_pattern_inverse.is_none() && self.contract_pattern.is_none() @@ -176,22 +176,22 @@ pub struct ProjectPathsAwareFilter { impl ProjectPathsAwareFilter { /// Returns true if the filter is empty. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.args_filter.is_empty() } /// Returns the CLI arguments. - pub fn args(&self) -> &FilterArgs { + pub const fn args(&self) -> &FilterArgs { &self.args_filter } /// Returns the CLI arguments mutably. - pub fn args_mut(&mut self) -> &mut FilterArgs { + pub const fn args_mut(&mut self) -> &mut FilterArgs { &mut self.args_filter } /// Returns the project paths. - pub fn paths(&self) -> &ProjectPathsConfig { + pub const fn paths(&self) -> &ProjectPathsConfig { &self.paths } } diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index c0a2da8cd528b..b9c1588b45072 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -505,10 +505,11 @@ impl TestArgs { let num_filtered = runner.matching_test_functions(filter).count(); if num_filtered == 0 { - let mut total_tests = num_filtered; - if !filter.is_empty() { - total_tests = runner.matching_test_functions(&EmptyTestFilter::default()).count(); - } + let total_tests = if filter.is_empty() { + num_filtered + } else { + runner.matching_test_functions(&EmptyTestFilter::default()).count() + }; if total_tests == 0 { sh_println!( "No tests found in project! Forge looks for functions that start with `test`" @@ -827,14 +828,9 @@ impl TestArgs { .iter() .filter_map(|(k, v)| { previous_snapshots.get(k).and_then(|previous_snapshot| { - if previous_snapshot != v { - Some(( - k.clone(), - (previous_snapshot.clone(), v.clone()), - )) - } else { - None - } + (previous_snapshot != v).then(|| { + (k.clone(), (previous_snapshot.clone(), v.clone())) + }) }) }) .collect(); @@ -958,7 +954,7 @@ impl TestArgs { } /// Returns whether `BuildArgs` was configured with `--watch` - pub fn is_watch(&self) -> bool { + pub const fn is_watch(&self) -> bool { self.watch.watch.is_some() } @@ -1051,7 +1047,7 @@ fn persist_run_failures(config: &Config, outcome: &TestOutcome) { let mut failures = outcome.failures().peekable(); while let Some((test_name, _)) = failures.next() { if test_name.is_any_test() - && let Some(test_match) = test_name.split("(").next() + && let Some(test_match) = test_name.split('(').next() { filter.push_str(test_match); if failures.peek().is_some() { diff --git a/crates/forge/src/cmd/test/summary.rs b/crates/forge/src/cmd/test/summary.rs index 2b3e29ff0ccc1..f8a72272af53c 100644 --- a/crates/forge/src/cmd/test/summary.rs +++ b/crates/forge/src/cmd/test/summary.rs @@ -17,7 +17,7 @@ pub struct TestSummaryReport { } impl TestSummaryReport { - pub fn new(is_detailed: bool, outcome: TestOutcome) -> Self { + pub const fn new(is_detailed: bool, outcome: TestOutcome) -> Self { Self { is_detailed, outcome } } } diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 040f51d587bf3..f7a20443b2dd3 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -115,7 +115,7 @@ pub struct LcovReporter { impl LcovReporter { /// Create a new LCOV reporter. - pub fn new(path: PathBuf, version: Version) -> Self { + pub const fn new(path: PathBuf, version: Version) -> Self { Self { path, version } } } @@ -270,7 +270,7 @@ pub struct BytecodeReporter { } impl BytecodeReporter { - pub fn new(root: PathBuf, destdir: PathBuf) -> Self { + pub const fn new(root: PathBuf, destdir: PathBuf) -> Self { Self { root, destdir } } } diff --git a/crates/forge/src/lockfile.rs b/crates/forge/src/lockfile.rs index e64cab6ff53a1..4f0f091076bb9 100644 --- a/crates/forge/src/lockfile.rs +++ b/crates/forge/src/lockfile.rs @@ -39,7 +39,7 @@ impl<'a> Lockfile<'a> { } /// Set the git instance to be used for submodule operations. - pub fn with_git(mut self, git: &'a Git<'_>) -> Self { + pub const fn with_git(mut self, git: &'a Git<'_>) -> Self { self.git = Some(git); self } @@ -302,7 +302,7 @@ impl DepIdentifier { } /// Marks as dependency as overridden. - pub fn mark_override(&mut self) { + pub const fn mark_override(&mut self) { match self { Self::Branch { r#override, .. } => *r#override = true, Self::Tag { r#override, .. } => *r#override = true, @@ -311,7 +311,7 @@ impl DepIdentifier { } /// Returns whether the dependency has been overridden. - pub fn overridden(&self) -> bool { + pub const fn overridden(&self) -> bool { match self { Self::Branch { r#override, .. } => *r#override, Self::Tag { r#override, .. } => *r#override, @@ -320,7 +320,7 @@ impl DepIdentifier { } /// Returns whether the dependency is a branch. - pub fn is_branch(&self) -> bool { + pub const fn is_branch(&self) -> bool { matches!(self, Self::Branch { .. }) } } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index fd9b4b889c2e2..a3d105af848eb 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -246,11 +246,11 @@ impl MultiContractRunner { progress: Option<&TestsProgress>, ) -> SuiteResult { let identifier = artifact_id.identifier(); - let mut span_name = identifier.as_str(); - - if !enabled!(tracing::Level::TRACE) { - span_name = get_contract_name(&identifier); - } + let span_name = if enabled!(tracing::Level::TRACE) { + identifier.as_str() + } else { + get_contract_name(&identifier) + }; let span = debug_span!("suite", name = %span_name); let span_local = span.clone(); let _guard = span_local.enter(); @@ -440,12 +440,12 @@ impl MultiContractRunnerBuilder { } } - pub fn sender(mut self, sender: Address) -> Self { + pub const fn sender(mut self, sender: Address) -> Self { self.sender = Some(sender); self } - pub fn initial_balance(mut self, initial_balance: U256) -> Self { + pub const fn initial_balance(mut self, initial_balance: U256) -> Self { self.initial_balance = initial_balance; self } @@ -455,27 +455,27 @@ impl MultiContractRunnerBuilder { self } - pub fn set_coverage(mut self, enable: bool) -> Self { + pub const fn set_coverage(mut self, enable: bool) -> Self { self.line_coverage = enable; self } - pub fn set_debug(mut self, enable: bool) -> Self { + pub const fn set_debug(mut self, enable: bool) -> Self { self.debug = enable; self } - pub fn set_decode_internal(mut self, mode: InternalTraceMode) -> Self { + pub const fn set_decode_internal(mut self, mode: InternalTraceMode) -> Self { self.decode_internal = mode; self } - pub fn fail_fast(mut self, fail_fast: bool) -> Self { + pub const fn fail_fast(mut self, fail_fast: bool) -> Self { self.fail_fast = fail_fast; self } - pub fn enable_isolation(mut self, enable: bool) -> Self { + pub const fn enable_isolation(mut self, enable: bool) -> Self { self.isolation = enable; self } diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 37388e65f5bcd..66be289ef252d 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -51,7 +51,7 @@ pub struct TestOutcome { impl TestOutcome { /// Creates a new test outcome with the given results. - pub fn new( + pub const fn new( known_contracts: Option, results: BTreeMap, allow_failure: bool, @@ -68,7 +68,7 @@ impl TestOutcome { } /// Creates a new empty test outcome. - pub fn empty(known_contracts: Option, allow_failure: bool) -> Self { + pub const fn empty(known_contracts: Option, allow_failure: bool) -> Self { Self::new(known_contracts, BTreeMap::new(), allow_failure, None) } @@ -391,19 +391,19 @@ pub enum TestStatus { impl TestStatus { /// Returns `true` if the test was successful. #[inline] - pub fn is_success(self) -> bool { + pub const fn is_success(self) -> bool { matches!(self, Self::Success) } /// Returns `true` if the test failed. #[inline] - pub fn is_failure(self) -> bool { + pub const fn is_failure(self) -> bool { matches!(self, Self::Failure) } /// Returns `true` if the test was skipped. #[inline] - pub fn is_skipped(self) -> bool { + pub const fn is_skipped(self) -> bool { matches!(self, Self::Skipped) } } @@ -784,7 +784,7 @@ impl TestResult { } /// Returns `true` if this is the result of a fuzz test - pub fn is_fuzz(&self) -> bool { + pub const fn is_fuzz(&self) -> bool { matches!(self.kind, TestKind::Fuzz { .. }) } @@ -877,7 +877,7 @@ impl fmt::Display for TestKindReport { impl TestKindReport { /// Returns the main gas value to compare against - pub fn gas(&self) -> u64 { + pub const fn gas(&self) -> u64 { match *self { Self::Unit { gas } => gas, // We use the median for comparisons @@ -924,12 +924,12 @@ impl Default for TestKind { impl TestKind { /// Returns `true` if this is a fuzz test. - pub fn is_fuzz(&self) -> bool { + pub const fn is_fuzz(&self) -> bool { matches!(self, Self::Fuzz { .. }) } /// Returns `true` if this is an invariant test. - pub fn is_invariant(&self) -> bool { + pub const fn is_invariant(&self) -> bool { matches!(self, Self::Invariant { .. }) } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 862fea4b6a22c..41cbaec88264e 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -32,6 +32,7 @@ use foundry_evm::{ invariant::{InvariantContract, InvariantSettings}, strategies::EvmFuzzState, }, + revm::primitives::hardfork::SpecId, traces::{TraceKind, TraceMode, load_contracts}, }; use itertools::Itertools; @@ -87,7 +88,7 @@ impl<'a, FEN: FoundryEvmNetwork> Deref for ContractRunner<'a, FEN> { } impl<'a, FEN: FoundryEvmNetwork> ContractRunner<'a, FEN> { - pub fn new( + pub const fn new( name: &'a str, contract: &'a TestContract, executor: Executor, @@ -358,10 +359,10 @@ impl<'a, FEN: FoundryEvmNetwork> ContractRunner<'a, FEN> { if setup.reason.is_some() { // The setup failed, so we return a single test result for `setUp` - let fail_msg = if !setup.deployment_failure { - "setUp()".to_string() - } else { + let fail_msg = if setup.deployment_failure { "constructor()".to_string() + } else { + "setUp()".to_string() }; return SuiteResult::new( start.elapsed(), @@ -500,7 +501,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { } } - fn revert_decoder(&self) -> &'a RevertDecoder { + const fn revert_decoder(&self) -> &'a RevertDecoder { &self.cr.mcr.revert_decoder } @@ -799,6 +800,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { invariant_contract.address, invariant_contract.invariant_function.selector().to_vec().into(), CheckSequenceOptions { + accumulate_warp_roll: invariant_config.has_delay(), fail_on_revert: invariant_config.fail_on_revert, call_after_invariant: invariant_contract.call_after_invariant, rd: Some(self.revert_decoder()), @@ -1096,7 +1098,8 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { address, &ITest::beforeTestSetupCall { testSelector: func.selector() }, ) { - debug!(?calldata, spec=%self.executor.spec_id(), "applying before_test_setup"); + let spec_id: SpecId = self.executor.spec_id().into(); + debug!(?calldata, spec=%spec_id, "applying before_test_setup"); // Apply before test configured calldata. match self.executor.to_mut().transact_raw( self.tcfg.sender, diff --git a/crates/forge/tests/cli/lint.rs b/crates/forge/tests/cli/lint.rs index 05e6288d96f0e..fd69907be2f09 100644 --- a/crates/forge/tests/cli/lint.rs +++ b/crates/forge/tests/cli/lint.rs @@ -1155,7 +1155,7 @@ async fn ensure_lint_rule_docs() { let mut missing_lints = Vec::new(); for lint in REGISTERED_LINTS { let selector = lint.id().to_lowercase(); - let selector_with_space = selector.replace("-", " "); + let selector_with_space = selector.replace('-', " "); if !content.to_lowercase().contains(&selector) && !content.to_lowercase().contains(&selector_with_space) { diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index c2f92b9e95962..c026a09f69e8b 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1050,9 +1050,8 @@ forgetest_async!(check_broadcast_log, |prj, cmd| { let run_log = re.replace_all(&run_log, ""); // Clean up carriage return OS differences - let re = Regex::new(r"\r\n").unwrap(); - let fixtures_log = re.replace_all(&fixtures_log, "\n"); - let run_log = re.replace_all(&run_log, "\n"); + let fixtures_log = fixtures_log.replace("\r\n", "\n"); + let run_log = run_log.replace("\r\n", "\n"); similar_asserts::assert_eq!(fixtures_log, run_log); }); @@ -3201,7 +3200,7 @@ contract CounterScript is Script { error: the following required arguments were not provided: --broadcast -Usage: [..] script --broadcast --verify --fork-url [ARGS]... +Usage: [..] script --broadcast --verify --rpc-url [ARGS]... For more information, try '--help'. diff --git a/crates/forge/tests/cli/test_cmd/invariant/common.rs b/crates/forge/tests/cli/test_cmd/invariant/common.rs index 9f8450817eba8..8f71030bc0a7c 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/common.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/common.rs @@ -1653,46 +1653,39 @@ contract InvariantWarpAndRoll { } function invariant_warp() public view { - require(block.number < 200000, "max block"); + require(block.timestamp < 500000, "max timestamp"); } /// forge-config: default.invariant.show_solidity = true function invariant_roll() public view { - require(block.timestamp < 500000, "max timestamp"); + require(block.number < 200000, "max block"); } } "#, ); cmd.args(["test", "--mt", "invariant_warp"]).assert_failure().stdout_eq(str![[r#" -[COMPILING_FILES] with [SOLC_VERSION] -[SOLC_VERSION] [ELAPSED] -Compiler run successful! - -Ran 1 test for test/InvariantWarpAndRoll.t.sol:InvariantWarpAndRoll -[FAIL: max block] - [Sequence] (original: 6, shrunk: 6) - sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=6280 roll=21461 calldata=setNumber(uint256) args=[200000 [2e5]] +... +[FAIL: max timestamp] + [Sequence] (original: 5, shrunk: 5) + sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=6280 roll=21461 calldata=setNumber(uint256) args=[500000 [5e5]] sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=92060 roll=51816 calldata=setNumber(uint256) args=[0] sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=198040 roll=60259 calldata=increment() args=[] sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=20609 roll=27086 calldata=setNumber(uint256) args=[26717227324157985679793128079000084308648530834088529513797156275625002 [2.671e70]] sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=409368 roll=24864 calldata=increment() args=[] - sender=[..] addr=[test/InvariantWarpAndRoll.t.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f warp=218105 roll=17834 calldata=setNumber(uint256) args=[24752675372815722001736610830 [2.475e28]] invariant_warp() (runs: 0, calls: 0, reverts: 0) ... "#]]); cmd.forge_fuse().args(["test", "--mt", "invariant_roll"]).assert_failure().stdout_eq(str![[r#" -No files changed, compilation skipped - -Ran 1 test for test/InvariantWarpAndRoll.t.sol:InvariantWarpAndRoll -[FAIL: max timestamp] - [Sequence] (original: 5, shrunk: 5) +... +[FAIL: max block] + [Sequence] (original: 6, shrunk: 6) vm.warp(block.timestamp + 6280); vm.roll(block.number + 21461); vm.prank([..]); - Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).setNumber(200000); + Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).setNumber(500000); vm.warp(block.timestamp + 92060); vm.roll(block.number + 51816); vm.prank([..]); @@ -1709,6 +1702,10 @@ Ran 1 test for test/InvariantWarpAndRoll.t.sol:InvariantWarpAndRoll vm.roll(block.number + 24864); vm.prank([..]); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); + vm.warp(block.timestamp + 218105); + vm.roll(block.number + 17834); + vm.prank([..]); + Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).setNumber(24752675372815722001736610830); invariant_roll() (runs: 0, calls: 0, reverts: 0) ... @@ -2069,3 +2066,89 @@ contract InvariantOptimizeWarpTest is Test { ... "#]]); }); + +// Regression test for delay-aware shrinking in check mode. +// Removed calls may still contribute warp/roll, so the final shrunk sequence must preserve those +// values in the remaining call. +forgetest_init!(invariant_shrink_preserves_warp_roll, |prj, cmd| { + prj.add_test( + "InvariantRollWarpShrink.t.sol", + r#" +import "forge-std/Test.sol"; + +contract Roll { + uint256 public number; + + function increment() public { + require(block.number > 50000, "wrong block"); + number++; + } +} + +contract Warp { + uint256 public number; + + function increment() public { + require(block.timestamp > 500000, "wrong timestamp"); + number++; + } +} + +contract InvariantRoll is Test { + Roll public roll; + + function setUp() public { + roll = new Roll(); + } + + /// forge-config: default.fuzz.seed = "119" + /// forge-config: default.invariant.max_block_delay = 60480 + /// forge-config: default.invariant.show_solidity = true + function invariant_roll() public view { + require(roll.number() == 0, "number is not zero"); + } +} + +contract InvariantWarp is Test { + Warp public warp; + + function setUp() public { + warp = new Warp(); + } + + /// forge-config: default.fuzz.seed = "119" + /// forge-config: default.invariant.max_time_delay = 604800 + /// forge-config: default.invariant.show_solidity = true + function invariant_warp() public view { + require(warp.number() == 0, "max time"); + } +} +"#, + ); + + cmd.args(["test", "--mt", "invariant_roll"]).assert_failure().stdout_eq(str![[r#" +... +[FAIL: number is not zero] + [Sequence] (original: 3, shrunk: 1) + vm.roll(block.number + 52068); + vm.prank([..]); + Roll(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); + invariant_roll() (runs: 0, calls: 0, reverts: 2) +... + +"#]]); + + cmd.forge_fuse().args(["test", "--mt", "invariant_warp"]).assert_failure().stdout_eq(str![[ + r#" +... +[FAIL: max time] + [Sequence] (original: 3, shrunk: 1) + vm.warp(block.timestamp + 656868); + vm.prank(0x00000000000000000000000000000000000012d2); + Warp(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); + invariant_warp() (runs: 0, calls: 0, reverts: 2) +... + +"# + ]]); +}); diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/lint/src/linter/mod.rs b/crates/lint/src/linter/mod.rs index 355e61f3284ed..fc140643eb68a 100644 --- a/crates/lint/src/linter/mod.rs +++ b/crates/lint/src/linter/mod.rs @@ -62,7 +62,7 @@ pub struct LinterConfig<'s> { } impl<'s, 'c> LintContext<'s, 'c> { - pub fn new( + pub const fn new( sess: &'s Session, with_description: bool, with_json_emitter: bool, @@ -77,7 +77,7 @@ impl<'s, 'c> LintContext<'s, 'c> { if self.with_json_emitter { diag.help(help) } else { diag.help(hyperlink(help)) } } - pub fn session(&self) -> &'s Session { + pub const fn session(&self) -> &'s Session { self.sess } @@ -211,14 +211,14 @@ pub struct Suggestion { impl Suggestion { /// Creates a new [`SuggestionKind::Example`] suggestion. - pub fn example(content: String) -> Self { + pub const fn example(content: String) -> Self { Self { desc: None, content, kind: SuggestionKind::Example } } /// Creates a new [`SuggestionKind::Fix`] suggestion. /// /// When possible, will attempt to inline the suggestion. - pub fn fix(content: String, applicability: Applicability) -> Self { + pub const fn fix(content: String, applicability: Applicability) -> Self { Self { desc: None, content, @@ -231,13 +231,13 @@ impl Suggestion { } /// Sets the description for the suggestion. - pub fn with_desc(mut self, desc: &'static str) -> Self { + pub const fn with_desc(mut self, desc: &'static str) -> Self { self.desc = Some(desc); self } /// Sets the span for a [`SuggestionKind::Fix`] suggestion. - pub fn with_span(mut self, span: Span) -> Self { + pub const fn with_span(mut self, span: Span) -> Self { if let SuggestionKind::Fix { span: ref mut s, .. } = self.kind { *s = Some(span); } @@ -245,7 +245,7 @@ impl Suggestion { } /// Sets the style for a [`SuggestionKind::Fix`] suggestion. - pub fn with_style(mut self, style: SuggestionStyle) -> Self { + pub const fn with_style(mut self, style: SuggestionStyle) -> Self { if let SuggestionKind::Fix { style: ref mut s, .. } = self.kind { *s = style; } diff --git a/crates/lint/src/sol/codesize/unwrapped_modifier_logic.rs b/crates/lint/src/sol/codesize/unwrapped_modifier_logic.rs index 64f7b27134732..65d195e47ada5 100644 --- a/crates/lint/src/sol/codesize/unwrapped_modifier_logic.rs +++ b/crates/lint/src/sol/codesize/unwrapped_modifier_logic.rs @@ -79,7 +79,7 @@ impl UnwrappedModifierLogic { let (mut res, mut has_valid_stmt) = (false, false); for stmt in stmts { match &stmt.kind { - hir::StmtKind::Placeholder => continue, + hir::StmtKind::Placeholder => {} hir::StmtKind::Expr(expr) => { if !self.is_valid_expr(hir, expr) || has_valid_stmt { res = true; diff --git a/crates/lint/src/sol/gas/custom_errors.rs b/crates/lint/src/sol/gas/custom_errors.rs index e7dfa4ae1af56..b1b7a68310885 100644 --- a/crates/lint/src/sol/gas/custom_errors.rs +++ b/crates/lint/src/sol/gas/custom_errors.rs @@ -48,6 +48,6 @@ fn should_lint_require(args: &solar::ast::CallArgs<'_>) -> bool { } /// Checks if an expression is a string literal. -fn is_string_literal(expr: &Expr<'_>) -> bool { +const fn is_string_literal(expr: &Expr<'_>) -> bool { matches!(&expr.kind, ExprKind::Lit(lit, _) if matches!(lit.kind, solar::ast::LitKind::Str(..))) } diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index cb942510bbb49..7316f4c4239b7 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -1,103 +1,27 @@ use super::AsmKeccak256; use crate::{ - linter::{LateLintPass, LintContext}, + declare_forge_lint, + linter::EarlyLintPass, sol::{Severity, SolLint}, }; -use solar::{ - ast::{self as ast, Span}, - interface::kw, - sema::hir::{self}, -}; +use solar_ast::{Expr, ExprKind}; +use solar_interface::kw; declare_forge_lint!( ASM_KECCAK256, Severity::Gas, "asm-keccak256", - "use of inefficient hashing mechanism; consider using inline assembly" + "hash using inline assembly to save gas" ); -impl<'hir> LateLintPass<'hir> for AsmKeccak256 { - fn check_stmt( - &mut self, - ctx: &LintContext, - hir: &'hir hir::Hir<'hir>, - stmt: &'hir hir::Stmt<'hir>, - ) { - let check_expr_and_emit_lint = - |expr: &'hir hir::Expr<'hir>, assign: Option, is_return: bool| { - if let Some(hash_arg) = extract_keccak256_arg(expr) { - self.emit_lint( - ctx, - hir, - stmt.span, - expr, - hash_arg, - AsmContext { _assign: assign, _is_return: is_return }, - ); - } - }; - - match stmt.kind { - hir::StmtKind::DeclSingle(var_id) => { - let var = hir.variable(var_id); - if let Some(init) = var.initializer { - // Constants should be optimized by the compiler, so no gas savings apply. - if !matches!(var.mutability, Some(hir::VarMut::Constant)) { - check_expr_and_emit_lint(init, var.name, false); - } +impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 { + fn check_expr(&mut self, ctx: &crate::linter::LintContext<'_>, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(expr, _) = &expr.kind { + if let ExprKind::Ident(ident) = &expr.kind { + if ident.name == kw::Keccak256 { + ctx.emit(&ASM_KECCAK256, expr.span); } } - // Expressions that don't (directly) assign to a variable - hir::StmtKind::Expr(expr) - | hir::StmtKind::Emit(expr) - | hir::StmtKind::Revert(expr) - | hir::StmtKind::DeclMulti(_, expr) - | hir::StmtKind::If(expr, ..) => check_expr_and_emit_lint(expr, None, false), - hir::StmtKind::Return(Some(expr)) => check_expr_and_emit_lint(expr, None, true), - _ => (), } } } - -impl AsmKeccak256 { - /// Emits lints (when possible with fix suggestions) for inefficient `keccak256` calls. - fn emit_lint( - &self, - ctx: &LintContext, - _hir: &hir::Hir<'_>, - _stmt_span: Span, - call: &hir::Expr<'_>, - _hash: &hir::Expr<'_>, - _asm_ctx: AsmContext, - ) { - ctx.emit(&ASM_KECCAK256, call.span); - } -} - -/// If the expression is a call to `keccak256` with one argument, returns that argument. -fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Expr<'hir>> { - let hir::ExprKind::Call( - callee, - hir::CallArgs { kind: hir::CallArgsKind::Unnamed(args), .. }, - .., - ) = &expr.kind - else { - return None; - }; - - let is_keccak = if let hir::ExprKind::Ident([hir::Res::Builtin(builtin)]) = callee.kind { - matches!(builtin.name(), kw::Keccak256) - } else { - return None; - }; - - if is_keccak && args.len() == 1 { Some(&args[0]) } else { None } -} - -// -- HELPER FUNCTIONS AND STRUCTS ---------------------------------------------------------------- - -#[derive(Debug, Clone, Copy)] -struct AsmContext { - _assign: Option, - _is_return: bool, -} diff --git a/crates/lint/src/sol/high/incorrect_shift.rs b/crates/lint/src/sol/high/incorrect_shift.rs index 2ad60b9a793f7..8e7fcbf868109 100644 --- a/crates/lint/src/sol/high/incorrect_shift.rs +++ b/crates/lint/src/sol/high/incorrect_shift.rs @@ -29,7 +29,7 @@ impl<'ast> EarlyLintPass<'ast> for IncorrectShift { // TODO: come up with a better heuristic. Treat initial impl as a PoC. // Checks if the left operand is a literal and the right operand is not, indicating a potential // reversed shift operation. -fn contains_incorrect_shift<'ast>( +const fn contains_incorrect_shift<'ast>( left_expr: &'ast Expr<'ast>, right_expr: &'ast Expr<'ast>, ) -> bool { diff --git a/crates/lint/src/sol/high/unchecked_calls.rs b/crates/lint/src/sol/high/unchecked_calls.rs index df9747e1ff100..900a3793496a9 100644 --- a/crates/lint/src/sol/high/unchecked_calls.rs +++ b/crates/lint/src/sol/high/unchecked_calls.rs @@ -168,7 +168,7 @@ impl<'ast> Visit<'ast> for UncheckedCallChecker<'_, '_> { /// - `target.delegatecall(...)` /// - `target.staticcall(...)` /// - `target.call{value: x}(...)` -fn is_low_level_call(expr: &Expr<'_>) -> bool { +const fn is_low_level_call(expr: &Expr<'_>) -> bool { if let ExprKind::Call(call_expr, _args) = &expr.kind { // Check the callee expression let callee = match &call_expr.kind { diff --git a/crates/lint/src/sol/info/interface_naming.rs b/crates/lint/src/sol/info/interface_naming.rs index 06625a808f745..ba4bf13c48a81 100644 --- a/crates/lint/src/sol/info/interface_naming.rs +++ b/crates/lint/src/sol/info/interface_naming.rs @@ -3,7 +3,7 @@ use crate::{ sol::{Severity, SolLint, info::InterfaceFileNaming}, }; -use solar::ast::{self as ast}; +use solar::ast; declare_forge_lint!( INTERFACE_FILE_NAMING, diff --git a/crates/lint/src/sol/info/multi_contract_file.rs b/crates/lint/src/sol/info/multi_contract_file.rs index 34a0bc8f67904..7be1aaa983d71 100644 --- a/crates/lint/src/sol/info/multi_contract_file.rs +++ b/crates/lint/src/sol/info/multi_contract_file.rs @@ -3,7 +3,7 @@ use crate::{ sol::{Severity, SolLint, info::MultiContractFile}, }; -use solar::ast::{self as ast}; +use solar::ast; declare_forge_lint!( MULTI_CONTRACT_FILE, diff --git a/crates/lint/src/sol/info/screaming_snake_case.rs b/crates/lint/src/sol/info/screaming_snake_case.rs index a0d56c5a3cf34..ff3d5982b2fb3 100644 --- a/crates/lint/src/sol/info/screaming_snake_case.rs +++ b/crates/lint/src/sol/info/screaming_snake_case.rs @@ -56,9 +56,9 @@ pub fn check_screaming_snake_case(s: &str) -> Option { // Handle leading/trailing underscores like `heck` does let expected = format!( "{prefix}{name}{suffix}", - prefix = if s.starts_with("_") { "_" } else { "" }, + prefix = if s.starts_with('_') { "_" } else { "" }, name = heck::AsShoutySnakeCase(s), - suffix = if s.ends_with("_") { "_" } else { "" } + suffix = if s.ends_with('_') { "_" } else { "" } ); if s == expected { None } else { Some(expected) } } diff --git a/crates/lint/src/sol/med/unsafe_typecast.rs b/crates/lint/src/sol/med/unsafe_typecast.rs index 7ef4d44ea03e3..860e87b38a2f2 100644 --- a/crates/lint/src/sol/med/unsafe_typecast.rs +++ b/crates/lint/src/sol/med/unsafe_typecast.rs @@ -134,7 +134,7 @@ fn infer_source_types( } /// Checks if a type cast from source_type to target_type is unsafe. -fn is_unsafe_elementary_typecast( +const fn is_unsafe_elementary_typecast( source_type: &ElementaryType, target_type: &ElementaryType, ) -> bool { @@ -159,8 +159,7 @@ fn is_unsafe_elementary_typecast( } // Dynamic bytes to fixed bytes (potential truncation) - (ElementaryType::Bytes, ElementaryType::FixedBytes(_)) - | (ElementaryType::String, ElementaryType::FixedBytes(_)) => true, + (ElementaryType::Bytes | ElementaryType::String, ElementaryType::FixedBytes(_)) => true, // Address to smaller uint (truncation) - address is 160 bits (ElementaryType::Address(_), ElementaryType::UInt(target_size)) => target_size.bits() < 160, diff --git a/crates/lint/src/sol/mod.rs b/crates/lint/src/sol/mod.rs index f9afac2b00443..ba2e7f0d697bf 100644 --- a/crates/lint/src/sol/mod.rs +++ b/crates/lint/src/sol/mod.rs @@ -97,22 +97,22 @@ impl<'a> SolidityLinter<'a> { self } - pub fn with_description(mut self, with: bool) -> Self { + pub const fn with_description(mut self, with: bool) -> Self { self.with_description = with; self } - pub fn with_json_emitter(mut self, with: bool) -> Self { + pub const fn with_json_emitter(mut self, with: bool) -> Self { self.with_json_emitter = with; self } - pub fn with_lint_specific(mut self, lint_specific: &'a LintSpecificConfig) -> Self { + pub const fn with_lint_specific(mut self, lint_specific: &'a LintSpecificConfig) -> Self { self.lint_specific = lint_specific; self } - fn config(&'a self, inline: &'a InlineConfig>) -> LinterConfig<'a> { + const fn config(&'a self, inline: &'a InlineConfig>) -> LinterConfig<'a> { LinterConfig { inline, lint_specific: self.lint_specific } } @@ -147,7 +147,7 @@ impl<'a> SolidityLinter<'a> { .fold((Vec::new(), Vec::new()), |(mut passes, mut ids), (pass, lints)| { let included_ids: Vec<_> = lints .iter() - .filter_map(|lint| if self.include_lint(*lint) { Some(lint.id) } else { None }) + .filter_map(|lint| self.include_lint(*lint).then_some(lint.id)) .collect(); if !included_ids.is_empty() { @@ -198,7 +198,7 @@ impl<'a> SolidityLinter<'a> { .fold((Vec::new(), Vec::new()), |(mut passes, mut ids), (pass, lints)| { let included_ids: Vec<_> = lints .iter() - .filter_map(|lint| if self.include_lint(*lint) { Some(lint.id) } else { None }) + .filter_map(|lint| self.include_lint(*lint).then_some(lint.id)) .collect(); if !included_ids.is_empty() { diff --git a/crates/macros/src/cheatcodes.rs b/crates/macros/src/cheatcodes.rs index b6ac95df7120c..363c03c73c9e4 100644 --- a/crates/macros/src/cheatcodes.rs +++ b/crates/macros/src/cheatcodes.rs @@ -263,7 +263,7 @@ enum StructKind { } impl StructKind { - fn as_str(self) -> &'static str { + const fn as_str(self) -> &'static str { match self { Self::Struct => "struct", Self::Error => "error", diff --git a/crates/macros/src/console_fmt.rs b/crates/macros/src/console_fmt.rs index e8a8e6319bd09..0c95841d1e653 100644 --- a/crates/macros/src/console_fmt.rs +++ b/crates/macros/src/console_fmt.rs @@ -81,9 +81,7 @@ fn derive_enum(e: &DataEnum) -> TokenStream { let fields: Punctuated = fields .enumerate() - .map(|(i, field)| { - field.ident.as_ref().cloned().unwrap_or_else(|| format_ident!("__var_{i}")) - }) + .map(|(i, field)| field.ident.clone().unwrap_or_else(|| format_ident!("__var_{i}"))) .collect(); if fields.len() != 1 { diff --git a/crates/primitives/src/transaction/envelope.rs b/crates/primitives/src/transaction/envelope.rs index 64b15d50318a1..721973d29badc 100644 --- a/crates/primitives/src/transaction/envelope.rs +++ b/crates/primitives/src/transaction/envelope.rs @@ -78,7 +78,7 @@ impl FoundryTxEnvelope { } } - pub fn sidecar(&self) -> Option<&TxEip4844WithSidecar> { + pub const fn sidecar(&self) -> Option<&TxEip4844WithSidecar> { match self { Self::Eip4844(signed_variant) => match signed_variant.tx() { TxEip4844Variant::TxEip4844WithSidecar(with_sidecar) => Some(with_sidecar), @@ -107,7 +107,7 @@ impl FoundryTxEnvelope { } /// Returns `true` if this is a Tempo transaction. - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { matches!(self, Self::Tempo(_)) } diff --git a/crates/script-sequence/src/reader.rs b/crates/script-sequence/src/reader.rs index 839ab77664202..4be6b42e4a35d 100644 --- a/crates/script-sequence/src/reader.rs +++ b/crates/script-sequence/src/reader.rs @@ -62,7 +62,7 @@ impl BroadcastReader { { // 1. Recursively read all .json files in the broadcast directory let mut broadcasts = vec![]; - for entry in walkdir::WalkDir::new(&self.broadcast_path).into_iter() { + for entry in walkdir::WalkDir::new(&self.broadcast_path) { let entry = entry?; let path = entry.path(); diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index fb0c01eb4fd1e..37c59fc47f17c 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -1,6 +1,7 @@ use crate::transaction::TransactionWithMetadata; -use alloy_network::{Network, ReceiptResponse}; +use alloy_network::ReceiptResponse; use alloy_primitives::{TxHash, hex, map::HashMap}; +use alloy_rpc_types_eth::TransactionReceipt; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{SELECTOR_LEN, TransactionMaybeSigned, fs, shell}; use foundry_compilers::ArtifactId; @@ -11,7 +12,7 @@ use std::{ path::PathBuf, time::{Duration, SystemTime, UNIX_EPOCH}, }; - +# pub const DRY_RUN_DIR: &str = "dry-run"; #[derive(Clone, Serialize, Deserialize)] @@ -19,29 +20,13 @@ pub struct NestedValue { pub internal_type: String, pub value: String, } - -/// Sensitive values from the transactions in a script sequence -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveTransactionMetadata { - pub rpc: String, -} - -/// Sensitive info from the script sequence which is saved into the cache folder -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveScriptSequence { - pub transactions: VecDeque, -} - +# /// Helper that saves the transactions sequence and its state on which transactions have been /// broadcasted -#[derive(Clone, Serialize, Deserialize)] -#[serde(bound( - serialize = "N::TransactionRequest: Serialize, N::TxEnvelope: Serialize", - deserialize = "N::TransactionRequest: for<'de2> Deserialize<'de2>, N::TxEnvelope: for<'de2> Deserialize<'de2>" -))] -pub struct ScriptSequence { - pub transactions: VecDeque>, - pub receipts: Vec, +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct ScriptSequence { + pub transactions: VecDeque, + pub receipts: Vec, pub libraries: Vec, pub pending: Vec, #[serde(skip)] @@ -54,24 +39,20 @@ pub struct ScriptSequence { pub commit: Option, } -impl Default for ScriptSequence { - fn default() -> Self { - Self { - transactions: Default::default(), - receipts: Default::default(), - libraries: Default::default(), - pending: Default::default(), - paths: Default::default(), - returns: Default::default(), - timestamp: Default::default(), - chain: Default::default(), - commit: Default::default(), - } - } +/// Sensitive values from the transactions in a script sequence +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveTransactionMetadata { + pub rpc: String, +} + +/// Sensitive info from the script sequence which is saved into the cache folder +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveScriptSequence { + pub transactions: VecDeque, } -impl From<&ScriptSequence> for SensitiveScriptSequence { - fn from(sequence: &ScriptSequence) -> Self { +impl From<&ScriptSequence> for SensitiveScriptSequence { + fn from(sequence: &ScriptSequence) -> Self { Self { transactions: sequence .transactions @@ -82,7 +63,7 @@ impl From<&ScriptSequence> for SensitiveScriptSequence { } } -impl ScriptSequence { +impl ScriptSequence { /// Loads The sequence for the corresponding json file pub fn load( config: &Config, @@ -90,10 +71,7 @@ impl ScriptSequence { target: &ArtifactId, chain_id: u64, dry_run: bool, - ) -> Result - where - N::TxEnvelope: for<'d> Deserialize<'d>, - { + ) -> Result { let (path, sensitive_path) = Self::get_paths(config, sig, target, chain_id, dry_run)?; let mut script_sequence: Self = fs::read_json_file(&path) @@ -114,10 +92,7 @@ impl ScriptSequence { /// Saves the transactions as file if it's a standalone deployment. /// `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<()> - where - N::TxEnvelope: Serialize, - { + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { self.sort_receipts(); if self.transactions.is_empty() { @@ -166,10 +141,10 @@ impl ScriptSequence { Ok(()) } - pub fn add_receipt(&mut self, receipt: N::ReceiptResponse) { + pub fn add_receipt(&mut self, receipt: TransactionReceipt) { self.receipts.push(receipt); } - + # /// Sorts all receipts with ascending transaction index pub fn sort_receipts(&mut self) { self.receipts.sort_by_key(|r| (r.block_number(), r.transaction_index())); @@ -185,7 +160,7 @@ impl ScriptSequence { pub fn remove_pending(&mut self, tx_hash: TxHash) { self.pending.retain(|element| element != &tx_hash); } - + # /// Gets paths in the formats /// `./broadcast/[contract_filename]/[chain_id]/[sig]-latest.json` and /// `./cache/[contract_filename]/[chain_id]/[sig]-latest.json`. @@ -196,8 +171,8 @@ impl ScriptSequence { chain_id: u64, dry_run: bool, ) -> Result<(PathBuf, PathBuf)> { - let mut broadcast = config.broadcast.clone(); - let mut cache = config.cache_path.clone(); + 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.")?; @@ -229,7 +204,7 @@ impl ScriptSequence { } /// Returns the list of the transactions without the metadata. - pub fn transactions(&self) -> impl Iterator> { + pub fn transactions(&self) -> impl Iterator { self.transactions.iter().map(|tx| tx.tx()) } @@ -240,7 +215,7 @@ impl ScriptSequence { .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); } } - +# /// 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-sequence/src/transaction.rs b/crates/script-sequence/src/transaction.rs index 3e4b46d3fa327..da4448228e117 100644 --- a/crates/script-sequence/src/transaction.rs +++ b/crates/script-sequence/src/transaction.rs @@ -43,15 +43,15 @@ pub struct TransactionWithMetadata { pub is_fixed_gas_limit: bool, } -fn default_string() -> Option { +const fn default_string() -> Option { Some(String::new()) } -fn default_address() -> Option
{ +const fn default_address() -> Option
{ Some(Address::ZERO) } -fn default_vec_of_strings() -> Option> { +const fn default_vec_of_strings() -> Option> { Some(vec![]) } @@ -71,11 +71,11 @@ impl TransactionWithMetadata { } } - pub fn tx(&self) -> &TransactionMaybeSigned { + pub const fn tx(&self) -> &TransactionMaybeSigned { &self.transaction } - pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { + pub const fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { &mut self.transaction } diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 381867dde73dd..4146e07d2248b 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -19,6 +19,7 @@ foundry-cli.workspace = true foundry-config.workspace = true foundry-common.workspace = true foundry-evm.workspace = true +foundry-evm-networks.workspace = true alloy-evm.workspace = true foundry-debugger.workspace = true foundry-cheatcodes.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 2fd7ab6537a39..045da94168e34 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -345,14 +345,18 @@ impl BundledState { .chain(self.browser_wallet.as_ref().map(|b| b.address())) .collect(); - // For addresses without an explicit signer, try Tempo access key lookup. + // For addresses without an explicit signer, try Tempo keys.toml fallback. let mut access_keys: AddressHashMap<(WalletSigner, TempoAccessKeyConfig)> = AddressHashMap::default(); + let mut direct_signers: AddressHashMap = AddressHashMap::default(); let mut missing_addresses = Vec::new(); for addr in &required_addresses { if !signers.contains(addr) { match lookup_signer(*addr) { + Ok(TempoLookup::Direct(signer)) => { + direct_signers.insert(*addr, signer); + } Ok(TempoLookup::Keychain(signer, config)) => { access_keys.insert(*addr, (signer, *config)); } @@ -372,8 +376,11 @@ impl BundledState { } let signers = self.script_wallets.into_multi_wallet().into_signers()?; - let eth_wallets = + let mut eth_wallets: AddressHashMap = signers.into_iter().map(|(addr, signer)| (addr, signer.into())).collect(); + for (addr, signer) in direct_signers { + eth_wallets.insert(addr, signer.into()); + } SendTransactionsKind::Raw { eth_wallets, browser: self.browser_wallet, access_keys } }; @@ -486,8 +493,9 @@ impl BundledState { }) .collect::>>()?; - let estimate_via_rpc = - has_different_gas_calc(sequence.chain) || self.args.skip_simulation; + let estimate_via_rpc = has_different_gas_calc(sequence.chain) + || self.script_config.evm_opts.networks.is_tempo() + || 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 diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index bf8cfcddf6f64..f1462bb868f58 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -107,7 +107,7 @@ pub enum ScriptPredeployLibraries { } impl ScriptPredeployLibraries { - pub fn libraries_count(&self) -> usize { + pub const fn libraries_count(&self) -> usize { match self { Self::Default(libs) => libs.len(), Self::Create2(libs, _) => libs.len(), @@ -292,7 +292,15 @@ impl CompiledState { }; let (args, build_data, script_wallets, browser_wallet, script_config) = - if !self.args.unlocked { + if self.args.unlocked { + ( + self.args, + self.build_data, + self.script_wallets, + self.browser_wallet, + self.script_config, + ) + } else { let mut froms = sequence.sequences().iter().flat_map(|s| { s.transactions .iter() @@ -305,7 +313,15 @@ impl CompiledState { .signers() .map_err(|e| eyre::eyre!("Failed to get available signers: {}", e))?; - if !froms.all(|from| available_signers.contains(&from)) { + if froms.all(|from| available_signers.contains(&from)) { + ( + self.args, + self.build_data, + self.script_wallets, + self.browser_wallet, + self.script_config, + ) + } else { // IF we are missing required signers, execute script as we might need to // collect private keys from the execution. let executed = self.link().await?.prepare_execution().await?.execute().await?; @@ -316,23 +332,7 @@ impl CompiledState { executed.browser_wallet, executed.script_config, ) - } else { - ( - self.args, - self.build_data, - self.script_wallets, - self.browser_wallet, - self.script_config, - ) } - } else { - ( - self.args, - self.build_data, - self.script_wallets, - self.browser_wallet, - self.script_config, - ) }; // Collect libraries from sequence and link contracts with them. diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 1da825accded9..162d061462b66 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -232,8 +232,7 @@ 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::>(); + let total_rpcs = txs.iter().filter_map(|tx| tx.rpc.clone()).collect::>(); Self { total_rpcs, missing_rpc } } @@ -379,10 +378,10 @@ impl ExecutedState { ty: "unknown".to_string(), }); - let label = if !output.name.is_empty() { - output.name.clone() - } else { + let label = if output.name.is_empty() { index.to_string() + } else { + output.name.clone() }; returns.insert( @@ -477,10 +476,10 @@ impl PreSimulationState { ty: "unknown".to_string(), }); - let label = if !output.name.is_empty() { - output.name.clone() - } else { + let label = if output.name.is_empty() { index.to_string() + } else { + output.name.clone() }; sh_println!( "{label}: {internal_type} {value}", diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 4ba84333874bc..d8d83913edb40 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -59,6 +59,7 @@ use foundry_evm::{ opts::EvmOpts, traces::{TraceMode, Traces}, }; +use foundry_evm_networks::NetworkConfigs; use foundry_wallets::MultiWalletOpts; use serde::Serialize; use std::path::PathBuf; @@ -249,8 +250,13 @@ impl ScriptArgs { async fn resolved_evm_opts(&self) -> Result<(Config, EvmOpts)> { let (config, mut evm_opts) = self.load_config_and_evm_opts()?; - // Auto-detect network from fork chain ID when not explicitly configured. - evm_opts.infer_network_from_fork().await; + if self.fee_token.is_some() { + // If fee token is set directly select tempo + evm_opts.networks = NetworkConfigs::with_tempo(); + } else { + // Auto-detect network from fork chain ID when not explicitly configured. + evm_opts.infer_network_from_fork().await; + } Ok((config, evm_opts)) } @@ -289,6 +295,7 @@ impl ScriptArgs { } /// Executes the script + #[allow(clippy::large_stack_frames)] pub async fn run_script(self) -> Result<()> { trace!(target: "script", "executing script command"); @@ -323,6 +330,7 @@ impl ScriptArgs { /// Prepares the bundled state (compile, simulate, bundle) and returns it /// for broadcasting, or returns `None` if there's nothing to broadcast /// (e.g., debug mode, no transactions, missing RPCs). + #[allow(clippy::large_stack_frames)] async fn prepare_bundled( self, config: Config, @@ -532,7 +540,6 @@ impl ScriptArgs { bytecodes.push((format!("Unknown{unknown_c}"), init_code, deployed_code)); unknown_c += 1; } - continue; } let mut prompt_user = false; @@ -588,7 +595,7 @@ impl ScriptArgs { } /// We only broadcast transactions if --broadcast, --resume, or --verify was passed. - fn should_broadcast(&self) -> bool { + const fn should_broadcast(&self) -> bool { self.broadcast || self.resume || self.verify } } diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index dc6f8755d042b..c066a026e0a0f 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -27,7 +27,7 @@ pub enum TxStatus { impl From for TxStatus { fn from(receipt: R) -> Self { - if !receipt.status() { Self::Revert(receipt) } else { Self::Success(receipt) } + if receipt.status() { Self::Success(receipt) } else { Self::Revert(receipt) } } } diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index f07b4b7a3168d..015a411c8bd97 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -29,7 +29,7 @@ pub struct ScriptRunner { } impl ScriptRunner { - pub fn new(executor: Executor, evm_opts: EvmOpts) -> Self { + pub const fn new(executor: Executor, evm_opts: EvmOpts) -> Self { Self { executor, evm_opts } } @@ -177,10 +177,7 @@ impl ScriptRunner { traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces))); // Optionally call the `setUp` function - let (success, gas_used, labeled_addresses, transactions) = if !setup { - self.executor.backend_mut().set_test_contract(address); - (true, 0, Default::default(), Some(library_transactions)) - } else { + let (success, gas_used, labeled_addresses, transactions) = if setup { match self.executor.setup(Some(self.evm_opts.sender), address, None) { Ok(RawCallResult { reverted, @@ -221,6 +218,9 @@ impl ScriptRunner { } Err(e) => return Err(e.into()), } + } else { + self.executor.backend_mut().set_test_contract(address); + (true, 0, Default::default(), Some(library_transactions)) }; Ok(( @@ -395,9 +395,11 @@ impl ScriptRunner { self.executor.tx_env_mut().set_gas_limit(mid_gas_limit); let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?; match res.exit_reason { - Some(InstructionResult::Revert) - | Some(InstructionResult::OutOfGas) - | Some(InstructionResult::OutOfFunds) => { + Some( + InstructionResult::Revert + | InstructionResult::OutOfGas + | InstructionResult::OutOfFunds, + ) => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 2d942670af066..05834ed8bb036 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,21 +9,15 @@ use crate::{ execute::{ExecutionArtifacts, ExecutionData}, sequence::get_commit_hash, }; -use alloy_chains::NamedChain; -use alloy_evm::revm::context::Block; use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, U256, map::HashMap, utils::format_units}; +use alloy_primitives::{Address, TxKind, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{ContractData, shell}; -use foundry_evm::{ - core::{FoundryBlock, evm::FoundryEvmNetwork}, - traces::{decode_trace_arena, render_trace_arena}, -}; -use foundry_wallets::wallet_browser::signer::BrowserSigner; +use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ @@ -37,24 +31,23 @@ use std::{ /// /// Can be either converted directly to [BundledState] or driven to it through /// [FilledTransactionsState]. -pub struct PreSimulationState { +pub struct PreSimulationState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, - pub execution_result: ScriptResult, + pub execution_result: ScriptResult, pub execution_artifacts: ExecutionArtifacts, } -impl PreSimulationState { +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> { + pub async fn fill_metadata(self) -> Result { let address_to_abi = self.build_address_to_abi_map(); let mut transactions = self @@ -71,7 +64,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if to.is_some() { + if let Some(TxKind::Call(_)) = to { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, @@ -95,7 +88,6 @@ impl PreSimulationState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data, execution_artifacts: self.execution_artifacts, transactions, @@ -108,8 +100,8 @@ impl PreSimulationState { /// Collects gas usage and metadata for each transaction. pub async fn simulate_and_fill( &self, - transactions: VecDeque>, - ) -> Result>> { + transactions: VecDeque, + ) -> Result> { trace!(target: "script", "executing onchain simulation"); let runners = Arc::new( @@ -129,7 +121,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = tx.to(); + let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( tx.from() @@ -147,8 +139,7 @@ impl PreSimulationState { // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { - let block_number = runner.executor.evm_env().block_env.number() + U256::from(1); - runner.executor.evm_env_mut().block_env.set_number(block_number); + runner.executor.env_mut().evm_env.block_env.number += U256::from(1); } let is_noop_tx = if let Some(to) = to { @@ -235,7 +226,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::is_json() { @@ -257,23 +248,22 @@ impl PreSimulationState { /// 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 struct FilledTransactionsState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, - pub transactions: VecDeque>, + pub transactions: VecDeque, } -impl FilledTransactionsState { +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(mut self) -> Result> { + pub async fn bundle(mut 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() { @@ -284,7 +274,7 @@ impl FilledTransactionsState { // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); - let mut manager = ProvidersManager::::default(); + 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 @@ -292,7 +282,7 @@ impl FilledTransactionsState { let mut txes_iter = mem::take(&mut self.transactions).into_iter().peekable(); while let Some(mut tx) = txes_iter.next() { - let tx_rpc = tx.rpc.clone(); + let tx_rpc = tx.rpc.to_owned(); let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; if let Some(tx) = tx.tx_mut().as_unsigned_mut() { @@ -307,7 +297,7 @@ impl FilledTransactionsState { // only estimate gas for unsigned transactions if let Some(tx) = tx.as_unsigned_mut() { trace!("estimating with different gas calculation"); - let gas = tx.gas_limit().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. // @@ -361,12 +351,6 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); - // Get the native token symbol for the chain using NamedChain - let token_symbol = NamedChain::try_from(provider_info.chain) - .unwrap_or_default() - .native_currency_symbol() - .unwrap_or("ETH"); - // 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 { @@ -390,7 +374,7 @@ impl FilledTransactionsState { sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; + sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; sh_println!("\n==========================")?; } else { sh_println!( @@ -400,7 +384,6 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, - "token_symbol": token_symbol, }) )?; } @@ -423,7 +406,6 @@ impl FilledTransactionsState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data, sequence, }) @@ -434,14 +416,14 @@ impl FilledTransactionsState { &self, multi: bool, chain: u64, - transactions: VecDeque>, - ) -> Result> { + 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( + Some(ScriptSequence::get_paths( &self.script_config.config, &self.args.sig, &self.build_data.build_data.target, diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 0a2b171b379ba..ef10a1ce94082 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -78,7 +78,7 @@ impl VerifyBundle { cache_path: Some(project.paths.cache.clone()), lib_paths: project.paths.libraries.clone(), hardhat: config.profile == Config::HARDHAT_PROFILE, - config_path: if config_path.exists() { Some(config_path) } else { None }, + config_path: config_path.exists().then_some(config_path), }; let via_ir = config.via_ir; @@ -201,14 +201,14 @@ async fn verify_contracts( for (receipt, tx) in sequence.receipts.iter_mut().zip(sequence.transactions.iter()) { // create2 hash offset - let mut offset = 0; - - if tx.is_create2() + let offset = if tx.is_create2() && let Some(contract_address) = tx.contract_address { receipt.set_contract_address(contract_address); - offset = 32; - } + 32 + } else { + 0 + }; // Verify contract created directly from the transaction if let (Some(address), Some(data)) = (receipt.contract_address(), tx.tx().input()) { diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs index 6d98a26d79cb3..5921f020e2337 100644 --- a/crates/sol-macro-gen/src/sol_macro_gen.rs +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -29,7 +29,7 @@ pub struct SolMacroGen { } impl SolMacroGen { - pub fn new(path: PathBuf, name: String) -> Self { + pub const fn new(path: PathBuf, name: String) -> Self { Self { path, name, expansion: None } } @@ -53,7 +53,7 @@ pub struct MultiSolMacroGen { } impl MultiSolMacroGen { - pub fn new(instances: Vec) -> Self { + pub const fn new(instances: Vec) -> Self { Self { instances } } @@ -238,7 +238,12 @@ edition = "2021" for instance in &self.instances { let name = instance.name.to_snake_case(); - if !single_file { + if single_file { + // Single File + let mut contents = String::new(); + write!(contents, "{}\n\n", instance.expansion.as_ref().unwrap())?; + write!(mod_contents, "{contents}")?; + } else { // Module write_mod_name(&mut mod_contents, &name)?; let mut contents = String::new(); @@ -249,11 +254,6 @@ edition = "2021" let contents = prettyplease::unparse(&file); fs::write(bindings_path.join(format!("{name}.rs")), contents) .wrap_err("Failed to write file")?; - } else { - // Single File - let mut contents = String::new(); - write!(contents, "{}\n\n", instance.expansion.as_ref().unwrap())?; - write!(mod_contents, "{contents}")?; } } diff --git a/crates/test-utils/src/ext.rs b/crates/test-utils/src/ext.rs index b14cf32a9dd3f..ba4f8421353b3 100644 --- a/crates/test-utils/src/ext.rs +++ b/crates/test-utils/src/ext.rs @@ -34,13 +34,13 @@ impl ExtTester { } /// Sets the path style. - pub fn style(mut self, style: PathStyle) -> Self { + pub const fn style(mut self, style: PathStyle) -> Self { self.style = style; self } /// Sets the fork block. - pub fn fork_block(mut self, fork_block: u64) -> Self { + pub const fn fork_block(mut self, fork_block: u64) -> Self { self.fork_block = Some(fork_block); self } diff --git a/crates/test-utils/src/prj.rs b/crates/test-utils/src/prj.rs index 47227a4298428..96fa065317bec 100644 --- a/crates/test-utils/src/prj.rs +++ b/crates/test-utils/src/prj.rs @@ -79,13 +79,13 @@ impl RemoteProject { } /// Whether to run `forge build` - pub fn set_build(mut self, run_build: bool) -> Self { + pub const fn set_build(mut self, run_build: bool) -> Self { self.run_build = run_build; self } /// Configures the project's pathstyle - pub fn path_style(mut self, path_style: PathStyle) -> Self { + pub const fn path_style(mut self, path_style: PathStyle) -> Self { self.path_style = path_style; self } @@ -545,7 +545,7 @@ pub struct TestCommand { impl TestCommand { /// Returns a mutable reference to the underlying command. - pub fn cmd(&mut self) -> &mut Command { + pub const fn cmd(&mut self) -> &mut Command { &mut self.cmd } @@ -768,7 +768,7 @@ impl TestCommand { } /// Does not apply [`snapbox`] redactions to the command output. - pub fn with_no_redact(&mut self) -> &mut Self { + pub const fn with_no_redact(&mut self) -> &mut Self { self.redact_output = false; self } diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 9f5f502c241d1..07413fef5495c 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,9 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); - fs::copy(file, to_dir.join(name))?; + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } else { + continue; + } + fs::copy(&file, to_dir.join(name))?; } Ok(()) } @@ -224,11 +247,12 @@ impl ScriptTester { trace!(target: "tests", "STDOUT\n{stdout}\n\nSTDERR\n{stderr}"); - assert!( - !(!stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str())), - "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", - expected.as_str() - ); + if !stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str()) { + panic!( + "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", + expected.as_str() + ); + } self } diff --git a/crates/test-utils/src/ui_runner.rs b/crates/test-utils/src/ui_runner.rs index 8ae0af75fd458..c23d4b5ae08c9 100644 --- a/crates/test-utils/src/ui_runner.rs +++ b/crates/test-utils/src/ui_runner.rs @@ -116,7 +116,7 @@ fn config<'a>( } let stdout_filters: &[(&str, &str)] = - &[(&env!("CARGO_PKG_VERSION").replace(".", r"\."), "VERSION")]; + &[(&env!("CARGO_PKG_VERSION").replace('.', r"\."), "VERSION")]; for &(pattern, replacement) in stdout_filters { config.stdout_filter(pattern, replacement); } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 75c68b278fcf1..d22e12736d832 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -174,7 +174,7 @@ pub fn get_vyper() -> Vyper { let path = VYPER.as_path(); let mut file = File::create(path).unwrap(); if let Err(e) = file.try_lock() { - if let fs::TryLockError::WouldBlock = e { + if matches!(e, fs::TryLockError::WouldBlock) { file.lock().unwrap(); assert!(path.exists()); return Vyper::new(path).unwrap(); diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index e603540a4c336..731779714776f 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -111,9 +111,9 @@ Diagnostics: {diags}", /// sanitized variant of the specific version so that it can be installed. This is merely /// intended to ensure the flattened code can be compiled without errors. fn strip_build_meta(version: Version) -> Version { - if version.build != BuildMetadata::EMPTY { - Version::new(version.major, version.minor, version.patch) - } else { + if version.build == BuildMetadata::EMPTY { version + } else { + Version::new(version.major, version.minor, version.patch) } } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index bc074a3fe4c2f..7f0afd972c5bf 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -281,11 +281,11 @@ 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('/'); - let base_url = if !is_etherscan { + let base_url = if is_etherscan { + base_url.unwrap_or(api_url) + } else { // If verifier is not Etherscan then set base url as api url without /api suffix. api_url.strip_suffix("/api").unwrap_or(api_url) - } else { - base_url.unwrap_or(api_url) }; builder.with_api_url(api_url)?.with_url(base_url)? } else { diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index 848d5808fd77b..bb0549b9ad19e 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -2,6 +2,8 @@ use super::{EtherscanSourceProvider, VerifyArgs}; use crate::{provider::VerificationContext, verify::ContractLanguage}; use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; +use foundry_compilers::artifacts::{Source, vyper::VyperInput}; +use std::path::Path; #[derive(Debug)] pub struct EtherscanStandardJsonSource; @@ -24,7 +26,14 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { serde_json::to_string(&input).wrap_err("Failed to parse standard json input")? } ContractLanguage::Vyper => { - let input = context.get_vyper_standard_json_input()?; + let path = Path::new(&context.target_path); + let sources = Source::read_all_from(path, &["vy", "vyi"])?; + let input = VyperInput::new( + sources, + context.compiler_settings.vyper.clone(), + &context.compiler_version, + ); + serde_json::to_string(&input).wrap_err("Failed to parse vyper json input")? } }; diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index e59d712d6811c..01c1f6b829195 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -232,11 +232,11 @@ impl VerificationProviderType { ) } - pub fn is_sourcify(&self) -> bool { + pub const fn is_sourcify(&self) -> bool { matches!(self, Self::Sourcify) } - pub fn is_etherscan(&self) -> bool { + pub const fn is_etherscan(&self) -> bool { matches!(self, Self::Etherscan) } } diff --git a/crates/verify/src/retry.rs b/crates/verify/src/retry.rs index 4bc934149175f..7df2826ed8714 100644 --- a/crates/verify/src/retry.rs +++ b/crates/verify/src/retry.rs @@ -37,7 +37,7 @@ impl Default for RetryArgs { impl RetryArgs { /// Converts the arguments into a `Retry` instance. - pub fn into_retry(self) -> Retry { + pub const fn into_retry(self) -> Retry { Retry::new(self.retries, Duration::from_secs(self.delay as u64)) } } diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index a02dadf563922..9d4cacc97591a 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -43,12 +43,12 @@ pub enum BytecodeType { impl BytecodeType { /// Check if the bytecode type is creation - pub fn is_creation(&self) -> bool { + pub const fn is_creation(&self) -> bool { matches!(self, Self::Creation) } /// Check if the bytecode type is runtime - pub fn is_runtime(&self) -> bool { + pub const fn is_runtime(&self) -> bool { matches!(self, Self::Runtime) } } @@ -107,15 +107,15 @@ pub fn print_result( config: &Config, ) { if let Some(res) = res { - if !shell::is_json() { + if shell::is_json() { + let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; + json_results.push(json_res); + } else { let _ = sh_println!( "{} with status {}", format!("{bytecode_type:?} code matched").green().bold(), res.green().bold() ); - } else { - let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; - json_results.push(json_res); } } else if !shell::is_json() { let _ = sh_err!( @@ -397,10 +397,10 @@ pub fn is_host_only(url: &Url) -> bool { /// assert_ne!(version.build, BuildMetadata::EMPTY); /// ``` pub async fn ensure_solc_build_metadata(version: Version) -> Result { - if version.build != BuildMetadata::EMPTY { - Ok(version) - } else { + if version.build == BuildMetadata::EMPTY { Ok(lookup_compiler_version(&version).await?) + } else { + Ok(version) } } diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index 029da52cfdf86..a0f9628956469 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -57,19 +57,19 @@ pub enum WalletSignerError { } impl WalletSignerError { - pub fn aws_unsupported() -> Self { + pub const fn aws_unsupported() -> Self { Self::UnsupportedSigner("AWS KMS") } - pub fn gcp_unsupported() -> Self { + pub const fn gcp_unsupported() -> Self { Self::UnsupportedSigner("Google Cloud KMS") } - pub fn turnkey_unsupported() -> Self { + pub const fn turnkey_unsupported() -> Self { Self::UnsupportedSigner("Turnkey") } - pub fn browser_unsupported() -> Self { + pub const fn browser_unsupported() -> Self { Self::UnsupportedSigner("Browser Wallet") } } diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs index f5282496ed0ca..6849125e3f084 100644 --- a/crates/wallets/src/utils.rs +++ b/crates/wallets/src/utils.rs @@ -124,9 +124,7 @@ pub fn create_keystore_signer( (Some(password), _) => Ok(Some(password.to_string())), (_, Some(password_file)) => { let password_file = Path::new(password_file); - if !password_file.is_file() { - Err(eyre::eyre!("Keystore password file `{password_file:?}` does not exist")) - } else { + if password_file.is_file() { Ok(Some( fs::read_to_string(password_file) .wrap_err_with(|| { @@ -135,6 +133,8 @@ pub fn create_keystore_signer( .trim_end() .to_string(), )) + } else { + Err(eyre::eyre!("Keystore password file `{password_file:?}` does not exist")) } } (None, None) => Ok(None), diff --git a/crates/wallets/src/wallet_browser/server.rs b/crates/wallets/src/wallet_browser/server.rs index ae5aaefb29444..0361416640c3a 100644 --- a/crates/wallets/src/wallet_browser/server.rs +++ b/crates/wallets/src/wallet_browser/server.rs @@ -87,17 +87,17 @@ impl BrowserWalletServer { } /// Get the server port. - pub fn port(&self) -> u16 { + pub const fn port(&self) -> u16 { self.port } /// Check if the browser should be opened. - pub fn open_browser(&self) -> bool { + pub const fn open_browser(&self) -> bool { self.open_browser } /// Get the timeout duration. - pub fn timeout(&self) -> Duration { + pub const fn timeout(&self) -> Duration { self.timeout } diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs index 5a7ad7fe4bee9..55f3bad5a4394 100644 --- a/crates/wallets/src/wallet_browser/signer.rs +++ b/crates/wallets/src/wallet_browser/signer.rs @@ -86,7 +86,7 @@ impl BrowserSigner { Ok(tx_hash) } - pub fn address(&self) -> Address { + pub const fn address(&self) -> Address { self.address } } diff --git a/crates/wallets/src/wallet_browser/state.rs b/crates/wallets/src/wallet_browser/state.rs index 0fa38da9cc110..c7c9859ebdcf4 100644 --- a/crates/wallets/src/wallet_browser/state.rs +++ b/crates/wallets/src/wallet_browser/state.rs @@ -52,7 +52,7 @@ impl BrowserWalletState { /// This relaxes certain security restrictions for local development. /// /// **WARNING**: This should only be used in a development environment. - pub fn is_development(&self) -> bool { + pub const fn is_development(&self) -> bool { self.development } diff --git a/crates/wallets/src/wallet_browser/types.rs b/crates/wallets/src/wallet_browser/types.rs index da4ac924a2b41..3e37498bb1e19 100644 --- a/crates/wallets/src/wallet_browser/types.rs +++ b/crates/wallets/src/wallet_browser/types.rs @@ -17,14 +17,14 @@ pub(crate) enum BrowserApiResponse { impl BrowserApiResponse<()> { /// Create a successful response with no data. - pub fn ok() -> Self { + pub const fn ok() -> Self { Self::Ok(()) } } impl BrowserApiResponse { /// Create a successful response with the given data. - pub fn with_data(data: T) -> Self { + pub const fn with_data(data: T) -> Self { Self::Ok(data) } @@ -121,7 +121,7 @@ pub struct Connection { impl Connection { /// Create a new connection instance. - pub fn new(address: Address, chain_id: ChainId) -> Self { + pub const fn new(address: Address, chain_id: ChainId) -> Self { Self { address, chain_id } } } diff --git a/crates/wallets/src/wallet_multi/mod.rs b/crates/wallets/src/wallet_multi/mod.rs index 3ad8e9edbc70d..a6d85581e19f0 100644 --- a/crates/wallets/src/wallet_multi/mod.rs +++ b/crates/wallets/src/wallet_multi/mod.rs @@ -292,14 +292,14 @@ impl MultiWalletOpts { pks.push(pk); } } - if !pks.is_empty() { + if pks.is_empty() { + Ok(None) + } else { let wallets = pks .into_iter() .map(|pk| utils::create_private_key_signer(pk)) .collect::>>()?; Ok(Some(wallets)) - } else { - Ok(None) } } diff --git a/deny.toml b/deny.toml index 641b5cd5175bd..a78f212fc8720 100644 --- a/deny.toml +++ b/deny.toml @@ -9,6 +9,8 @@ ignore = [ "RUSTSEC-2024-0436", # https://rustsec.org/advisories/RUSTSEC-2025-0141 bincode is unmaintained "RUSTSEC-2025-0141", + # https://rustsec.org/advisories/RUSTSEC-2026-0097 rand is unsound with a custom logger + "RUSTSEC-2026-0097", ] # This section is considered when running `cargo deny check bans`. diff --git a/flake.lock b/flake.lock index 4663480be03f1..3b3ca69bf658b 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1775287186, - "narHash": "sha256-hYntDpbh8MuiYRmBf/6uHMpDOP2m7L7bXQXboWPg6WM=", + "lastModified": 1775891769, + "narHash": "sha256-EOfVlTKw2n8w1uhfh46GS4hEGnQ7oWrIWQfIY6utIkI=", "owner": "nix-community", "repo": "fenix", - "rev": "2517c7fb1eafc7259bb631267f1e1f813cf5f3bc", + "rev": "6fbc54dde15aee725bdc7aae5e478849685d5f56", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1775126147, - "narHash": "sha256-J0dZU4atgcfo4QvM9D92uQ0Oe1eLTxBVXjJzdEMQpD0=", + "lastModified": 1775888245, + "narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8d8c1fa5b412c223ffa47410867813290cdedfef", + "rev": "13043924aaa7375ce482ebe2494338e058282925", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1775228522, - "narHash": "sha256-+6eTD6EAabjow5gdjWRP6aI2UUwOZJEjzzsvvbVu8f8=", + "lastModified": 1775843361, + "narHash": "sha256-j53ZgyDvmYf3Sjh1IPvvTjqa614qUfVQSzj59+MpzkY=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "f4b77dc99d9925667246e2887783b79bdc46a50d", + "rev": "9eb97ea96d8400e8957ddd56702e962614296583", "type": "github" }, "original": { diff --git a/npm/package.json b/npm/package.json index 2362816053f8c..d596a7930b609 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.0.2", "bun": "^1.3.1", "typescript": "^5.9.3" }, diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix diff --git a/npm/src/const.mjs b/npm/src/const.mjs index 6b3dcf3f9fbed..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** From 571d2e84fdb7e9a894e19ed4acd737b7f96a6ccd Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 00:09:18 +0700 Subject: [PATCH 284/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/forge/tests/cli/install.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/forge/tests/cli/install.rs b/crates/forge/tests/cli/install.rs index ae9582b9b1fa0..d61f134c5a6a5 100644 --- a/crates/forge/tests/cli/install.rs +++ b/crates/forge/tests/cli/install.rs @@ -620,14 +620,20 @@ forgetest!(flaky_install_no_git_cleans_nested_submodules, |prj, cmd| { // There should be no .git file or directory anywhere under the installed dependency. fn assert_no_git(dir: &Path, root: &Path) { + let canonical_root = root.canonicalize().unwrap(); for entry in fs::read_dir(dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); if path.file_name() == Some(".git".as_ref()) { panic!("found leftover .git at {}", path.display()); } - if path.is_dir() && path.starts_with(root) { - assert_no_git(&path, root); + + let metadata = fs::symlink_metadata(&path).unwrap(); + if metadata.is_dir() && !metadata.file_type().is_symlink() { + let canonical_path = path.canonicalize().unwrap(); + if canonical_path.starts_with(&canonical_root) { + assert_no_git(&path, root); + } } } } From e14aa93fa7b5e981a6647b39d2c47ceaa183022a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 05:33:56 +0700 Subject: [PATCH 285/374] chore(deps): bump DeterminateSystems/update-flake-lock (#419) Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from e80a657d7603606be0c69b117cfdc240f1e6af88 to ff43f160ef7014ae1a1fd85699fb6a44f436135b. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/e80a657d7603606be0c69b117cfdc240f1e6af88...ff43f160ef7014ae1a1fd85699fb6a44f436135b) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: ff43f160ef7014ae1a1fd85699fb6a44f436135b dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index e3541cd07c446..f6c01d65adfcc 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: DeterminateSystems/update-flake-lock@e80a657d7603606be0c69b117cfdc240f1e6af88 # main + - uses: DeterminateSystems/update-flake-lock@ff43f160ef7014ae1a1fd85699fb6a44f436135b # main with: pr-title: "Update flake.lock" pr-labels: | From 780a8c8a987da2a94bd6a3d58e2269334a429a9a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 07:25:14 +0700 Subject: [PATCH 286/374] Delete .circleci/config.yml (#420) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 4168efef0971f..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello - From f76d97b48b7d6b4da35fa8a8c1c7e8f88a33fc83 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 07:37:11 +0700 Subject: [PATCH 287/374] Delete .circleci directory (#421) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 32 ---------------- .circleci/ci-web3-gamefi.yml | 26 ------------- .circleci/ci.yml | 31 --------------- .circleci/ci_cargo.yml | 37 ------------------ .circleci/ci_deploy.yml | 34 ----------------- .circleci/ci_v1.yml | 31 --------------- .circleci/dev_stage.yml | 70 ---------------------------------- .circleci/web3_defi_gamefi.yml | 26 ------------- 8 files changed, 287 deletions(-) delete mode 100644 .circleci/cargo.yml delete mode 100644 .circleci/ci-web3-gamefi.yml delete mode 100644 .circleci/ci.yml delete mode 100644 .circleci/ci_cargo.yml delete mode 100644 .circleci/ci_deploy.yml delete mode 100644 .circleci/ci_v1.yml delete mode 100644 .circleci/dev_stage.yml delete mode 100644 .circleci/web3_defi_gamefi.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index 32b65e6a23cc5..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2.1 -# -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml deleted file mode 100644 index ad53a8e498202..0000000000000 --- a/.circleci/ci-web3-gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml deleted file mode 100644 index 1b5df6d6e668e..0000000000000 --- a/.circleci/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml deleted file mode 100644 index 46a18d45a5fca..0000000000000 --- a/.circleci/ci_cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 82c6de5b42b73..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml deleted file mode 100644 index 5ba351727d22b..0000000000000 --- a/.circleci/dev_stage.yml +++ /dev/null @@ -1,70 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- - - jobs: - my-job: - steps: - - run: echo "Hello, world!" - - run: - command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" - max_auto_reruns: 3 - auto_rerun_delay: 10s - - workflows: - dev_stage_pre-prod: - jobs: - - test_dev: - filters: # using regex filters requires the entire branch to match - branches: - only: # only branches matching the below regex filters will run - - dev - - /user-.*/ - - test_stage: - filters: - branches: - only: stage - - test_pre-prod: - filters: - branches: - only: /pre-prod(?:-.+)?$/ - - - build-test-deploy: - jobs: - - build: - filters: # required since `test` has tag filters AND requires `build` - tags: - only: /^config-test.*/ - - test: - requires: - - build - filters: # required since `deploy` has tag filters AND requires `test` - tags: - only: /^config-test.*/ - - deploy: - requires: - - test - filters: - tags: - only: /^config-test.*/ - branches: - ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml deleted file mode 100644 index edb6605e3f101..0000000000000 --- a/.circleci/web3_defi_gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- From c6eb37f9d99d15bfbda4cc6308a10b20c6026d3c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 07:50:46 +0700 Subject: [PATCH 288/374] Delete .circleci directory (#423) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 32 ---------------- .circleci/ci-web3-gamefi.yml | 26 ------------- .circleci/ci.yml | 31 --------------- .circleci/ci_cargo.yml | 37 ------------------ .circleci/ci_deploy.yml | 34 ----------------- .circleci/ci_v1.yml | 31 --------------- .circleci/config.yml | 32 ---------------- .circleci/dev_stage.yml | 70 ---------------------------------- .circleci/web3_defi_gamefi.yml | 26 ------------- 9 files changed, 319 deletions(-) delete mode 100644 .circleci/cargo.yml delete mode 100644 .circleci/ci-web3-gamefi.yml delete mode 100644 .circleci/ci.yml delete mode 100644 .circleci/ci_cargo.yml delete mode 100644 .circleci/ci_deploy.yml delete mode 100644 .circleci/ci_v1.yml delete mode 100644 .circleci/config.yml delete mode 100644 .circleci/dev_stage.yml delete mode 100644 .circleci/web3_defi_gamefi.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index 32b65e6a23cc5..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2.1 -# -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml deleted file mode 100644 index ad53a8e498202..0000000000000 --- a/.circleci/ci-web3-gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml deleted file mode 100644 index 1b5df6d6e668e..0000000000000 --- a/.circleci/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml deleted file mode 100644 index 46a18d45a5fca..0000000000000 --- a/.circleci/ci_cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 82c6de5b42b73..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 4168efef0971f..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello - diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml deleted file mode 100644 index 5ba351727d22b..0000000000000 --- a/.circleci/dev_stage.yml +++ /dev/null @@ -1,70 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- - - jobs: - my-job: - steps: - - run: echo "Hello, world!" - - run: - command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" - max_auto_reruns: 3 - auto_rerun_delay: 10s - - workflows: - dev_stage_pre-prod: - jobs: - - test_dev: - filters: # using regex filters requires the entire branch to match - branches: - only: # only branches matching the below regex filters will run - - dev - - /user-.*/ - - test_stage: - filters: - branches: - only: stage - - test_pre-prod: - filters: - branches: - only: /pre-prod(?:-.+)?$/ - - - build-test-deploy: - jobs: - - build: - filters: # required since `test` has tag filters AND requires `build` - tags: - only: /^config-test.*/ - - test: - requires: - - build - filters: # required since `deploy` has tag filters AND requires `test` - tags: - only: /^config-test.*/ - - deploy: - requires: - - test - filters: - tags: - only: /^config-test.*/ - branches: - ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml deleted file mode 100644 index edb6605e3f101..0000000000000 --- a/.circleci/web3_defi_gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- From 1bf6b7acf63d47a580fffd831b838318be981fd8 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 11:30:07 +0700 Subject: [PATCH 289/374] Delete .circleci directory (#422) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 32 ---------------- .circleci/ci-web3-gamefi.yml | 26 ------------- .circleci/ci.yml | 31 --------------- .circleci/ci_cargo.yml | 37 ------------------ .circleci/ci_v1.yml | 31 --------------- .circleci/config.yml | 32 ---------------- .circleci/dev_stage.yml | 70 ---------------------------------- .circleci/web3_defi_gamefi.yml | 26 ------------- 8 files changed, 285 deletions(-) delete mode 100644 .circleci/cargo.yml delete mode 100644 .circleci/ci-web3-gamefi.yml delete mode 100644 .circleci/ci.yml delete mode 100644 .circleci/ci_cargo.yml delete mode 100644 .circleci/ci_v1.yml delete mode 100644 .circleci/config.yml delete mode 100644 .circleci/dev_stage.yml delete mode 100644 .circleci/web3_defi_gamefi.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index 32b65e6a23cc5..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2.1 -# -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml deleted file mode 100644 index ad53a8e498202..0000000000000 --- a/.circleci/ci-web3-gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml deleted file mode 100644 index 1b5df6d6e668e..0000000000000 --- a/.circleci/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml deleted file mode 100644 index 46a18d45a5fca..0000000000000 --- a/.circleci/ci_cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 82c6de5b42b73..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 4168efef0971f..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello - diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml deleted file mode 100644 index 5ba351727d22b..0000000000000 --- a/.circleci/dev_stage.yml +++ /dev/null @@ -1,70 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- - - jobs: - my-job: - steps: - - run: echo "Hello, world!" - - run: - command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" - max_auto_reruns: 3 - auto_rerun_delay: 10s - - workflows: - dev_stage_pre-prod: - jobs: - - test_dev: - filters: # using regex filters requires the entire branch to match - branches: - only: # only branches matching the below regex filters will run - - dev - - /user-.*/ - - test_stage: - filters: - branches: - only: stage - - test_pre-prod: - filters: - branches: - only: /pre-prod(?:-.+)?$/ - - - build-test-deploy: - jobs: - - build: - filters: # required since `test` has tag filters AND requires `build` - tags: - only: /^config-test.*/ - - test: - requires: - - build - filters: # required since `deploy` has tag filters AND requires `test` - tags: - only: /^config-test.*/ - - deploy: - requires: - - test - filters: - tags: - only: /^config-test.*/ - branches: - ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml deleted file mode 100644 index edb6605e3f101..0000000000000 --- a/.circleci/web3_defi_gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- From d5c743f6fbcd40c3ed4e8f38b33246b91e0130be Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 12:42:58 +0700 Subject: [PATCH 290/374] Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/cargo.yml | 32 ---------------- .circleci/ci-web3-gamefi.yml | 26 ------------- .circleci/ci.yml | 31 --------------- .circleci/ci_cargo.yml | 37 ------------------ .circleci/ci_v1.yml | 31 --------------- .circleci/config.yml | 32 ---------------- .circleci/dev_stage.yml | 70 ---------------------------------- .circleci/web3_defi_gamefi.yml | 26 ------------- 8 files changed, 285 deletions(-) delete mode 100644 .circleci/cargo.yml delete mode 100644 .circleci/ci-web3-gamefi.yml delete mode 100644 .circleci/ci.yml delete mode 100644 .circleci/ci_cargo.yml delete mode 100644 .circleci/ci_v1.yml delete mode 100644 .circleci/config.yml delete mode 100644 .circleci/dev_stage.yml delete mode 100644 .circleci/web3_defi_gamefi.yml diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml deleted file mode 100644 index 32b65e6a23cc5..0000000000000 --- a/.circleci/cargo.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: 2.1 -# -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml deleted file mode 100644 index ad53a8e498202..0000000000000 --- a/.circleci/ci-web3-gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml deleted file mode 100644 index 1b5df6d6e668e..0000000000000 --- a/.circleci/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml deleted file mode 100644 index 46a18d45a5fca..0000000000000 --- a/.circleci/ci_cargo.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.88.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 82c6de5b42b73..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 4168efef0971f..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/reference/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello - diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml deleted file mode 100644 index 5ba351727d22b..0000000000000 --- a/.circleci/dev_stage.yml +++ /dev/null @@ -1,70 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- - - jobs: - my-job: - steps: - - run: echo "Hello, world!" - - run: - command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" - max_auto_reruns: 3 - auto_rerun_delay: 10s - - workflows: - dev_stage_pre-prod: - jobs: - - test_dev: - filters: # using regex filters requires the entire branch to match - branches: - only: # only branches matching the below regex filters will run - - dev - - /user-.*/ - - test_stage: - filters: - branches: - only: stage - - test_pre-prod: - filters: - branches: - only: /pre-prod(?:-.+)?$/ - - - build-test-deploy: - jobs: - - build: - filters: # required since `test` has tag filters AND requires `build` - tags: - only: /^config-test.*/ - - test: - requires: - - build - filters: # required since `deploy` has tag filters AND requires `test` - tags: - only: /^config-test.*/ - - deploy: - requires: - - test - filters: - tags: - only: /^config-test.*/ - branches: - ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml deleted file mode 100644 index edb6605e3f101..0000000000000 --- a/.circleci/web3_defi_gamefi.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference - -version: 2.1 -executors: - my-custom-executor: - docker: - - image: cimg/base:stable - auth: - # ensure you have first added these secrets - # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables - username: $DOCKER_HUB_USER - password: $DOCKER_HUB_PASSWORD -jobs: - web3-defi-game-project-: - - executor: my-custom-executor - steps: - - checkout - - run: | - # echo Hello, World! - -workflows: - my-custom-workflow: - jobs: - - web3-defi-game-project- From 88968f8d792e3612fa777011ceccef7c2cf65775 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 14 Apr 2026 12:50:00 +0700 Subject: [PATCH 291/374] Revert "benches\LATEST.md (#350)" (#425) This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. From c06c8603ef040a3eb89aab84fffbf979324add66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:35:15 +0000 Subject: [PATCH 292/374] chore(deps): bump actions/upload-pages-artifact from 3 to 4.0.0 Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4.0.0. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- .github/workflows/static.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d15e9ca56b220..e8c0425176f25 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -42,7 +42,7 @@ jobs: uses: actions/configure-pages@v6 - name: Upload artifact if: github.ref_name == 'master' && github.event_name == 'push' - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 with: path: ./target/doc diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 0ba82305f82b2..4218958d38db5 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -34,7 +34,7 @@ jobs: - name: Setup Pages uses: actions/configure-pages@v5 - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: # Upload entire repository path: '.' From 5345641085a448758322a15942b495593a9900a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 12:54:31 +0700 Subject: [PATCH 293/374] chore(deps): bump google-github-actions/get-gke-credentials (#417) Bumps [google-github-actions/get-gke-credentials](https://github.com/google-github-actions/get-gke-credentials) from 2.2.1 to 3.0.0. - [Release notes](https://github.com/google-github-actions/get-gke-credentials/releases) - [Changelog](https://github.com/google-github-actions/get-gke-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/google-github-actions/get-gke-credentials/compare/6051de21ad50fbb1767bc93c11357a49082ad116...3da1e46a907576cefaa90c484278bb5b259dd395) --- updated-dependencies: - dependency-name: google-github-actions/get-gke-credentials dependency-version: 3.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/google.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml index 1295e430ca96a..569c7b00a4b7f 100644 --- a/.github/workflows/google.yml +++ b/.github/workflows/google.yml @@ -83,7 +83,7 @@ jobs: # Get the GKE credentials so we can deploy to the cluster - name: 'Set up GKE credentials' - uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + uses: 'google-github-actions/get-gke-credentials@3da1e46a907576cefaa90c484278bb5b259dd395' # google-github-actions/get-gke-credentials@v2 with: cluster_name: '${{ env.GKE_CLUSTER }}' location: '${{ env.GKE_ZONE }}' From 59e9169c268e2f2742a4c1905da61684376ff6a5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:27:28 +0700 Subject: [PATCH 294/374] Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } From e9dc73115b745638824972729feee41c108e2fdd Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 11:30:23 +0000 Subject: [PATCH 295/374] Revert 350 dargon789/gamefi (#424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> … * Forge/master (#376) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar Co-authored-by: googleworkspace-bot * chore(deps): bump the cargo group across 1 directory with 9 updates (#377) Bumps the cargo group with 3 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing), [keccak](https://github.com/RustCrypto/sponges) and [slab](https://github.com/tokio-rs/slab). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `time` from 0.3.41 to 0.3.47 - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.41...v0.3.47) Updates `alloy-dyn-abi` from 1.3.0 to 1.5.7 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.3.0...v1.5.7) Updates `bytes` from 1.10.1 to 1.11.1 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.1...v1.11.1) Updates `keccak` from 0.1.5 to 0.1.6 - [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6) Updates `lru` from 0.12.5 to 0.16.3 - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.12.5...0.16.3) Updates `protobuf` from 3.3.0 to 3.7.2 Updates `ruint` from 1.15.0 to 1.17.2 - [Release notes](https://github.com/recmo/uint/releases) - [Changelog](https://github.com/recmo/uint/blob/main/CHANGELOG.md) - [Commits](https://github.com/recmo/uint/compare/v1.15.0...v1.17.2) Updates `slab` from 0.4.10 to 0.4.12 - [Release notes](https://github.com/tokio-rs/slab/releases) - [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.12) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: time dependency-version: 0.3.47 dependency-type: direct:production dependency-group: cargo - dependency-name: alloy-dyn-abi dependency-version: 1.5.7 dependency-type: direct:production dependency-group: cargo - dependency-name: bytes dependency-version: 1.11.1 dependency-type: direct:production dependency-group: cargo - dependency-name: keccak dependency-version: 0.1.6 dependency-type: indirect dependency-group: cargo - dependency-name: lru dependency-version: 0.16.3 dependency-type: indirect dependency-group: cargo - dependency-name: protobuf dependency-version: 3.7.2 dependency-type: indirect dependency-group: cargo - dependency-name: ruint dependency-version: 1.17.2 dependency-type: indirect dependency-group: cargo - dependency-name: slab dependency-version: 0.4.12 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create static.yml (#381) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Hardhat project (#389) * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot * Change branch trigger from 'main' to 'master' (#403) https://github.com/Dargon789/hardhat-project/issues/2073 https://github.com/Dargon789/hardhat-project/pull/2078/commits/a98e69d5803ec208947f592c91749f248e2dd07c Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revise Foundry benchmark results and system info (#408) Updated benchmark results for Foundry versions v1.3.6 and v1.4.0-rc1, including new test data and system information. https://github.com/foundry-rs/foundry/commit/1c4d334e95bc1cad3a390cc735d0f3ec59eea07f Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "benches\LATEST.md (#350)" (#409) This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. * Update Docker.yml * chore(deps): bump DeterminateSystems/update-flake-lock (#419) Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from e80a657d7603606be0c69b117cfdc240f1e6af88 to ff43f160ef7014ae1a1fd85699fb6a44f436135b. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/e80a657d7603606be0c69b117cfdc240f1e6af88...ff43f160ef7014ae1a1fd85699fb6a44f436135b) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: ff43f160ef7014ae1a1fd85699fb6a44f436135b dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/config.yml (#420) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory (#421) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "benches\LATEST.md (#350)" This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. * Update crates/config/spec/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update benches/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/test-utils/src/script.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Desant pivo Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: zerosnacks Co-authored-by: googleworkspace-bot --- .circleci/ci_deploy.yml | 34 ----------------------------- .github/workflows/nix.yml | 2 +- benches/src/lib.rs | 1 + crates/cli/src/utils/suggestions.rs | 2 +- crates/common/src/contracts.rs | 2 +- crates/config/spec/src/lib.rs | 3 +-- crates/lint/src/linter.rs | 2 +- crates/test-utils/src/script.rs | 1 + 8 files changed, 7 insertions(+), 40 deletions(-) delete mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index e3541cd07c446..f6c01d65adfcc 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - uses: DeterminateSystems/update-flake-lock@e80a657d7603606be0c69b117cfdc240f1e6af88 # main + - uses: DeterminateSystems/update-flake-lock@ff43f160ef7014ae1a1fd85699fb6a44f436135b # main with: pr-title: "Update flake.lock" pr-labels: | diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ab3bec614e3cd..516d73531007a 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -127,6 +127,7 @@ impl BenchmarkProject { let root = root_path.to_str().unwrap(); // Remove all files in the directory + let root_path = root_path.canonicalize()?; for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs index 5a362e963d956..77a3bb7ab11c2 100644 --- a/crates/config/spec/src/lib.rs +++ b/crates/config/spec/src/lib.rs @@ -40,8 +40,7 @@ mod tests { /// Checks that the `file` has the specified `contents`. If that is not the /// case, updates the file and then fails the test. fn ensure_file_contents(file: &Path, contents: &str) { - if let Ok(old_contents) = fs::read_to_string(file) - && normalize_newlines(&old_contents) == normalize_newlines(contents) + if fs::read_to_string(file).map(|old| normalize_newlines(&old) == normalize_newlines(contents)).unwrap_or(false) { // File is already up to date. return; diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..ca543dbc1fe83 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index c1a6cb53bdbff..21d5c36272670 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -120,6 +120,7 @@ impl ScriptTester { let from_dir = testdata.join("utils"); let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; + let from_dir = from_dir.canonicalize()?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); // Only operate on regular files to avoid following symlinks or directories From 0732d4cb24df139bffa7c7c2bb27e24c25447240 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:35:33 +0700 Subject: [PATCH 296/374] Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- 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 c133fa01291c4..f6a753ebfd727 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -648,7 +648,7 @@ impl From for DenyLevel { impl DenyLevel { /// Returns `true` if the deny level includes warnings. - pub fn warnings(&self) -> bool { + pub const fn warnings(&self) -> bool { match self { Self::Never => false, Self::Warnings | Self::Notes => true, From 4706fb0079a6566758755f1912b308c96f3cdee3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:36:03 +0700 Subject: [PATCH 297/374] Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- 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 f6a753ebfd727..41b73b24d98c8 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -657,7 +657,7 @@ impl DenyLevel { /// Returns `true` if the deny level includes notes. pub fn notes(&self) -> bool { - match self { + pub const fn notes(&self) -> bool { Self::Never | Self::Warnings => false, Self::Notes => true, } From 48f901c1dc7d22fd1f5f8c546ef97178e27114f3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:36:23 +0700 Subject: [PATCH 298/374] Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- 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 41b73b24d98c8..96e0d78680ade 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -664,7 +664,7 @@ impl DenyLevel { } /// Returns `true` if the deny level is set to never (only errors). - pub fn never(&self) -> bool { + pub const fn never(&self) -> bool { match self { Self::Never => true, Self::Warnings | Self::Notes => false, From 2b0d78659a5f65c14c473c90e27ae304d1e1657c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:36:51 +0700 Subject: [PATCH 299/374] Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- 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 96e0d78680ade..a061745771171 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1335,7 +1335,7 @@ impl Config { /// /// Returns `false` if `solc_version` is explicitly set, otherwise returns the value of /// `auto_detect_solc` - pub fn is_auto_detect(&self) -> bool { + pub const fn is_auto_detect(&self) -> bool { if self.solc.is_some() { return false; } From fd2db65e4854577895afa2486eee4fd5246c9494 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:37:17 +0700 Subject: [PATCH 300/374] Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- 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 a061745771171..2c7a6b4876de0 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2329,7 +2329,7 @@ impl Config { // Normalize `deny` based on the provided `deny_warnings` value. if figment.extract_inner::("deny_warnings").unwrap_or(false) - && let Ok(DenyLevel::Never) = figment.extract_inner("deny") + && matches!(figment.extract_inner("deny"), Ok(DenyLevel::Never)) { figment = figment.merge(("deny", DenyLevel::Warnings)); } From 7e67b2b86437fce8571d29e7a1c0825f4d270bf3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 19:27:58 +0700 Subject: [PATCH 301/374] Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail From e525ac18f963a1b0f0641f46f1c59afffbb32643 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:32:16 +0000 Subject: [PATCH 302/374] fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#435) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: googleworkspace-bot From b05f4c4f5eadad2dd38876b0a4254bd3a5ccfa6a Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Tue, 21 Apr 2026 21:35:52 +0700 Subject: [PATCH 303/374] doc-script.js --- doc/doc-filelist.js | 1 + doc/doc-script.js | 228 +++++++++++++++++++++++++ doc/doc-style.css | 403 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 632 insertions(+) create mode 100644 doc/doc-filelist.js create mode 100644 doc/doc-script.js create mode 100644 doc/doc-style.css diff --git a/doc/doc-filelist.js b/doc/doc-filelist.js new file mode 100644 index 0000000000000..c2a398ff94c23 --- /dev/null +++ b/doc/doc-filelist.js @@ -0,0 +1 @@ +var tree={}; \ No newline at end of file diff --git a/doc/doc-script.js b/doc/doc-script.js new file mode 100644 index 0000000000000..7fa122605e7cb --- /dev/null +++ b/doc/doc-script.js @@ -0,0 +1,228 @@ +// # res/script.js +// +// This is the script file that gets copied into the output. It mainly manages the display +// of the folder tree. The idea of this script file is to be minimal and standalone. So +// that means no jQuery. + +// Use localStorage to store data about the tree's state: whether or not +// the tree is visible and which directories are expanded. Unless the state +var sidebarVisible = (window.localStorage && window.localStorage.docker_showSidebar) ? + window.localStorage.docker_showSidebar == 'yes' : + defaultSidebar; + +/** + * ## makeTree + * + * Consructs the folder tree view + * + * @param {object} treeData Folder structure as in [queueFile](../src/docker.js.html#docker.prototype.queuefile) + * @param {string} root Path from current file to root (ie `'../../'` etc.) + * @param {string} filename The current file name + */ +function makeTree(treeData, root, filename) { + var treeNode = document.getElementById('tree'); + var treeHandle = document.getElementById('sidebar-toggle'); + treeHandle.addEventListener('click', toggleTree, false); + + // Build the html and add it to the container. + treeNode.innerHTML = nodeHtml('', treeData, '', root); + + // Root folder (whole tree) should always be open + treeNode.childNodes[0].className += ' open'; + + // Attach click event handler + treeNode.addEventListener('click', nodeClicked, false); + + if (sidebarVisible) document.body.className += ' sidebar'; + + // Restore scroll position from localStorage if set. And attach scroll handler + if (window.localStorage && window.localStorage.docker_treeScroll) treeNode.scrollTop = window.localStorage.docker_treeScroll; + treeNode.onscroll = treeScrolled; + + // Only set a class to allow CSS transitions after the tree state has been painted + setTimeout(function() { document.body.className += ' slidey'; }, 100); +} + +/** + * ## treeScrolled + * + * Called when the tree is scrolled. Stores the scroll position in localStorage + * so it can be restored on the next pageview. + */ +function treeScrolled() { + var tree = document.getElementById('tree'); + if (window.localStorage) window.localStorage.docker_treeScroll = tree.scrollTop; +} + +/** + * ## nodeClicked + * + * Called when a directory is clicked. Toggles open state of the directory + * + * @param {Event} e The click event + */ +function nodeClicked(e) { + // Find the target + var t = e.target; + + // If the click target is actually a file (rather than a directory), ignore it + if (t.tagName.toLowerCase() !== 'div' || t.className === 'children') return; + + // Recurse upwards until we find the actual directory node + while (t && t.className.substring(0, 3) != 'dir') t = t.parentNode; + + // If we're at the root node, then do nothing (we don't allow collapsing of the whole tree) + if (!t || t.parentNode.id == 'tree') return; + + // Find the path and toggle the state, saving the state in the localStorage variable + var path = t.getAttribute('rel'); + if (t.className.indexOf('open') !== -1) { + t.className = t.className.replace(/\s*open/g, ''); + if (window.localStorage) window.localStorage.removeItem('docker_openPath:' + path); + } else { + t.className += ' open'; + if (window.localStorage) window.localStorage['docker_openPath:' + path] = 'yes'; + } +} + + +/** + * ## nodeHtml + * + * Constructs the markup for a directory in the tree + * + * @param {string} nodename The node name. + * @param {object} node Node object of same format as whole tree. + * @param {string} path The path form the base to this node + * @param {string} root Relative path from current page to root + */ +function nodeHtml(nodename, node, path, root) { + // Firstly, figure out whether or not the directory is expanded from localStorage + var isOpen = window.localStorage && window.localStorage['docker_openPath:' + path] == 'yes'; + var out = '
'; + out += '
' + nodename + '
'; + out += '
'; + + // Loop through all child directories first + if (node.dirs) { + var dirs = []; + for (var i in node.dirs) { + if (node.dirs.hasOwnProperty(i)) dirs.push({ name: i, html: nodeHtml(i, node.dirs[i], path + i + '/', root) }); + } + // Have to store them in an array first and then sort them alphabetically here + dirs.sort(function(a, b) { return (a.name > b.name) ? 1 : (a.name == b.name) ? 0 : -1; }); + + for (var k = 0; k < dirs.length; k += 1) out += dirs[k].html; + } + + // Now loop through all the child files alphabetically + if (node.files) { + node.files.sort(); + for (var j = 0; j < node.files.length; j += 1) { + out += '' + node.files[j] + ''; + } + } + + // Close things off + out += '
'; + + return out; +} + +/** + * ## toggleTree + * + * Toggles the visibility of the folder tree + */ +function toggleTree() { + // Do the actual toggling by modifying the class on the body element. That way we can get some nice CSS transitions going. + if (sidebarVisible) { + document.body.className = document.body.className.replace(/\s*sidebar/g, ''); + sidebarVisible = false; + } else { + document.body.className += ' sidebar'; + sidebarVisible = true; + } + if (window.localStorage) { + if (sidebarVisible) { + window.localStorage.docker_showSidebar = 'yes'; + } else { + window.localStorage.docker_showSidebar = 'no'; + } + } +} + +/** + * ## wireUpTabs + * + * Wires up events on the sidebar tabe + */ +function wireUpTabs() { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Each tab has a class corresponding of the id of its tab pane + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + children[i].addEventListener('click', function(c) { + return function() { switchTab(c); }; + }(children[i].className)); + } +} + +/** + * ## switchTab + * + * Switches tabs in the sidebar + * + * @param {string} tab The ID of the tab to switch to + */ +function switchTab(tab) { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Easiest way to go through tabs without any kind of selector is just to look at the tab bar + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + + // Figure out what tab pane this tab button corresponts to + var t = children[i].className.replace(/\s.*$/, ''); + if (t === tab) { + // Show the tab pane, select the tab button + document.getElementById(t).style.display = 'block'; + if (children[i].className.indexOf('selected') === -1) children[i].className += ' selected'; + } else { + // Hide the tab pane, deselect the tab button + document.getElementById(t).style.display = 'none'; + children[i].className = children[i].className.replace(/\sselected/, ''); + } + } + + // Store the last open tab in localStorage + if (window.localStorage) window.localStorage.docker_sidebarTab = tab; +} + +/** + * ## window.onload + * + * When the document is ready, make the sidebar and all that jazz + */ +(function(init) { + if (window.addEventListener) { + window.addEventListener('DOMContentLoaded', init); + } else { // IE8 and below + window.onload = init; + } +}(function() { + makeTree(tree, relativeDir, thisFile); + wireUpTabs(); + + // Switch to the last viewed sidebar tab if stored, otherwise default to folder tree + if (window.localStorage && window.localStorage.docker_sidebarTab) { + switchTab(window.localStorage.docker_sidebarTab); + } else { + switchTab('tree'); + } +})); diff --git a/doc/doc-style.css b/doc/doc-style.css new file mode 100644 index 0000000000000..2019a1b7659c6 --- /dev/null +++ b/doc/doc-style.css @@ -0,0 +1,403 @@ +/* + +Original highlight.js style (c) Ivan Sagalaev + +*/ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +/* Base color: saturation 0; */ +.hljs, +.hljs-subst { + color: #444; +} +.hljs-comment { + color: #888888; +} +.hljs-keyword, +.hljs-attribute, +.hljs-selector-tag, +.hljs-meta-keyword, +.hljs-doctag, +.hljs-name { + font-weight: bold; +} +/* User color: hue: 0 */ +.hljs-type, +.hljs-string, +.hljs-number, +.hljs-selector-id, +.hljs-selector-class, +.hljs-quote, +.hljs-template-tag, +.hljs-deletion { + color: #880000; +} +.hljs-title, +.hljs-section { + color: #880000; + font-weight: bold; +} +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #BC6060; +} +/* Language color: hue: 90; */ +.hljs-literal { + color: #78A960; +} +.hljs-built_in, +.hljs-bullet, +.hljs-code, +.hljs-addition { + color: #397300; +} +/* Meta color: hue: 200 */ +.hljs-meta { + color: #1f7199; +} +.hljs-meta-string { + color: #4d99bf; +} +/* Misc effects */ +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: bold; +} +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + margin: 0; + padding: 0; + background: #ffffff; + color: #4d4d4d; +} +p, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0 0 15px 0; +} +h1 { + margin-top: 40px; +} +a { + color: #880000; +} +a:visited { + color: #880000; +} +#tree, +#headings { + position: absolute; + top: 30px; + left: 0; + bottom: 0; + width: 290px; + padding: 10px 0; + overflow: auto; +} +#sidebar_wrapper { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 0; + overflow: hidden; + background: #e7e7e7; +} +#sidebar_switch { + position: absolute; + top: 0; + left: 0; + width: 290px; + height: 29px; + border-bottom: 1px solid; + background: #e2e2e2; + border-bottom-color: #d6d6d6; +} +#sidebar_switch span { + display: block; + float: left; + width: 50%; + text-align: center; + line-height: 29px; + cursor: pointer; + color: #4b4b4b; +} +#sidebar_switch span:hover { + background: #e7e7e7; +} +#sidebar_switch .selected { + font-weight: bold; + background: #ededed; + color: #444; +} +.slidey #sidebar_wrapper { + -webkit-transition: width 250ms linear; + -moz-transition: width 250ms linear; + -ms-transition: width 250ms linear; + -o-transition: width 250ms linear; + transition: width 250ms linear; +} +.sidebar #sidebar_wrapper { + width: 290px; +} +#tree .nodename { + text-indent: 12px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAg0lEQVQYlWNIS0tbAcSK////Z8CHGTIzM7+mp6d/ASouwqswKyvrO1DRfyg+CcRaxCgE4Z9A3AjEbIQUgjHQOQvwKgS6+ffChQt3AiUDcCqsra29d/v27R6ghCVWN2ZnZ/9YuXLlRqBAPBALYvVMR0fHmQcPHrQBOUZ4gwfqFj5CAQ4Al6wLIYDwo9QAAAAASUVORK5CYII="); + background-repeat: no-repeat; + background-position: left center; + cursor: pointer; +} +#tree .open > .nodename { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAlElEQVQYlWNIS0tbCsT/8eCN////Z2B49OhRfHZ29jdsioDiP27evJkNVggkONeuXbscm8Jly5atA8rzwRSCsG5DQ8MtZEU1NTUPgOLGUHm4QgaQFVlZWT9BijIzM39fuHChDCaHohBkBdCq9SCF8+bN2wHkC+FSCMLGkyZNOvb9+3dbNHEMhSDsDsRMxCjEiolWCADeUBHgU/IGQQAAAABJRU5ErkJggg=="); + background-position: left 7px; +} +#tree .dir, +#tree .file { + position: relative; + min-height: 20px; + line-height: 20px; + padding-left: 12px; +} +#tree .dir > .children, +#tree .file > .children { + display: none; +} +#tree .dir.open > .children, +#tree .file.open > .children { + display: block; +} +#tree .file { + padding-left: 24px; + display: block; + text-decoration: none; + color: #444; +} +#tree > .dir { + padding-left: 0; +} +#headings .heading a { + text-decoration: none; + padding-left: 10px; + display: block; + color: #444; +} +#headings .h1 { + padding-left: 0; + margin-top: 10px; + font-size: 1.3em; +} +#headings .h2 { + padding-left: 10px; + margin-top: 8px; + font-size: 1.1em; +} +#headings .h3 { + padding-left: 20px; + margin-top: 5px; + font-size: 1em; +} +#headings .h4 { + padding-left: 30px; + margin-top: 3px; + font-size: 0.9em; +} +#headings .h5 { + padding-left: 40px; + margin-top: 1px; + font-size: 0.8em; +} +#headings .h6 { + padding-left: 50px; + font-size: 0.75em; +} +#sidebar-toggle { + position: fixed; + top: 0; + left: 0; + width: 5px; + bottom: 0; + z-index: 2; + cursor: pointer; + background: #dfdfdf; +} +#sidebar-toggle:hover { + width: 10px; + background: #d6d6d6; +} +.slidey #sidebar-toggle, +.slidey #container { + -webkit-transition: all 250ms linear; + -moz-transition: all 250ms linear; + -ms-transition: all 250ms linear; + -o-transition: all 250ms linear; + transition: all 250ms linear; +} +.sidebar #sidebar-toggle { + left: 290px; +} +#container { + position: fixed; + left: 5px; + right: 0; + top: 0; + bottom: 0; + overflow: auto; +} +.sidebar #container { + left: 295px; +} +.no-sidebar #sidebar_wrapper, +.no-sidebar #sidebar-toggle { + display: none; +} +.no-sidebar #container { + left: 0; +} +#page { + padding-top: 40px; +} +table td { + border: 0; + outline: 0; +} +.docs.markdown { + padding: 10px 50px; +} +td.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; +} +.docs pre { + margin: 15px 0 15px; + padding: 5px; + padding-left: 10px; + border: 1px solid #d6d6d6; + background: #F0F0F0; + font-size: 12px; + overflow: auto; +} +.docs pre.code_stats { + font-size: 60%; +} +.docs p tt, +.docs li tt, +.docs p code, +.docs li code { + border: 1px solid #d6d6d6; + font-size: 12px; + padding: 0 0.2em; + background: #e7e7e7; +} +.dox { + border-top: 1px solid #dddddd; + padding-top: 10px; + padding-bottom: 10px; +} +.dox .details { + padding: 10px; + background: #F0F0F0; + border: 1px solid #d6d6d6; + margin-bottom: 10px; +} +.dox .dox_tag_title { + font-weight: bold; +} +.dox .dox_tag_detail { + margin-left: 10px; +} +.dox .dox_tag_detail span { + margin-right: 5px; +} +.dox .dox_type { + font-style: italic; +} +.dox .dox_tag_name { + font-weight: bold; +} +.pilwrap { + position: relative; + padding-top: 1px; +} +.pilwrap .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + color: #555555; +} +.pilwrap .pilcrow:before { + content: '\b6'; +} +.pilwrap:hover .pilcrow { + opacity: 1; +} +td.code { + padding: 8px 15px 8px 25px; + width: 100%; + vertical-align: top; + border-left: 1px solid #d6d6d6; + background: #F0F0F0; +} +.background { + border-left: 1px solid #d6d6d6; + position: absolute; + z-index: -1; + top: 0; + right: 0; + bottom: 0; + left: 525px; + background: #F0F0F0; +} +pre, +tt, +code { + font-size: 12px; + line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; + padding: 0; + white-space: pre-wrap; + background: #F0F0F0; +} +.line-num { + display: inline-block; + width: 50px; + text-align: right; + opacity: 0.3; + margin-left: -20px; + text-decoration: none; + color: #888888; +} +.line-num:before { + content: attr(data-line); +} From 37ca1c2c9a44a3cc1804ccc029773edbd56b93fc Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:52:02 +0000 Subject: [PATCH 304/374] doc-script.js (#438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol * Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#345) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 110: Uncontrolled data used in path expression (#347) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 102: Artifact poisoning (#351) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md (#350) * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 109: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> … * Potential fix for code scanning alert no. 102: Artifact poisoning (#354) * Potential fix for code scanning alert no. 102: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#357) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * refactor(common): make ProviderBuilder generic over Network #13250 (#361) * refactor(common): make `ProviderBuilder` generic over `Network` (#13250) * refactor(common): make `ProviderBuilder` generic over `Network` - Updated `ProviderBuilder` helper to be generic, which will facilate `FoundryNetwork` rollout - Adjusted the instantiation of `ProviderBuilder` in various locations to use `AnyNetwork`. - Enhanced the `build` and `build_with_wallet` methods to accommodate the new generic structure. * fix: relax trait bound on `N::TransactionRequest` * fix: comment * fix: comment * fix(anvil): return error instead of empty vec for out-of-range log queries (#13251) * fix(config): respect user-configured etherscan URL over chain defaults (#13239) fix(config): Respect user-configured etherscan URL over chain defaults (#13238) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` (#13218) * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` * fix: after `TransactionBuilder4844` impl * feat(common): introduce generic `ProviderBuilder::from_config` method (#13268) - keep the existing helpers that erase generic to avoid breaking change * fix(cast): remove redundant chain() call in explorer_client (#13272) Co-authored-by: tefyosL-sol * fix(verify): respect user-configured etherscan URL over chain defaults (#13275) Co-authored-by: tefyosL-sol * Update flake.lock (#13279) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/93523fa' (2026-01-24) → 'github:nix-community/fenix/b2344f3' (2026-01-31) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) → 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) → 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) Co-authored-by: github-actions[bot] * fix(invariant): remove unused cloned calldata (#12893) * fix(invariant): prune calldata to bound memory usage in long runs * style: fix formatting in invariant executor * chore: remove vyper files from testdata to fix CI * chore: trigger ci update * fix(invariant): remove unused FuzzCase.calldata field to prevent OOM The calldata field in FuzzCase was stored but never read after construction. Removing it entirely eliminates memory accumulation during long invariant runs. Changes: - Remove FuzzCase.calldata field (unused after construction) - Remove prune_calldata() methods (no longer needed) - Restore vyper test files that were incorrectly deleted Fixes #12397 Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(debugger): display actual gas usage alongside refund counter (#13271) * feat(debugger): display actual gas usage alongside refund counter * fix(forge-test): fix flamegraph gas inaccuracy issues * reverse `--decode-internal` not default with `--flamechart` * make gas unsigned as `inferno` doesn't support anyway * replace revm-inspectors patch * fix clippy * update tests * update tests * fmt * fix(config): handle decimal string in U256 deserialization (#13284) * fix: adjust numerical cells in gas report to be right aligned (#12883) * Adjust Cells to be Right Aligned in Gas Report * fix test and fmt --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): weekly `cargo update` (#13280) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v1.5.2 -> v1.5.4 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-json-abi v1.5.2 -> v1.5.4 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-primitives v1.5.2 -> v1.5.4 Updating alloy-sol-macro v1.5.2 -> v1.5.4 Updating alloy-sol-macro-expander v1.5.2 -> v1.5.4 Updating alloy-sol-macro-input v1.5.2 -> v1.5.4 Updating alloy-sol-type-parser v1.5.2 -> v1.5.4 Updating alloy-sol-types v1.5.2 -> v1.5.4 Updating annotate-snippets v0.12.10 -> v0.12.11 Updating aws-smithy-async v1.2.7 -> v1.2.10 Updating aws-smithy-http-client v1.1.5 -> v1.1.8 Updating aws-smithy-observability v0.2.0 -> v0.2.3 Updating aws-smithy-query v0.60.9 -> v0.60.12 Updating aws-smithy-runtime-api v1.10.0 -> v1.11.2 Updating aws-smithy-types v1.3.6 -> v1.4.2 Updating bytemuck v1.24.0 -> v1.25.0 Updating cc v1.2.54 -> v1.2.55 Updating clap v4.5.54 -> v4.5.56 Updating clap_builder v4.5.54 -> v4.5.56 Updating clap_derive v4.5.49 -> v4.5.55 Updating cliclack v0.3.7 -> v0.3.8 Updating find-msvc-tools v0.1.8 -> v0.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating iana-time-zone v0.1.64 -> v0.1.65 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating keccak-asm v0.1.4 -> v0.1.5 Unchanged matchit v0.8.4 (available: v0.8.6) Updating notify-types v2.0.0 -> v2.1.0 Updating portable-atomic v1.13.0 -> v1.13.1 Updating portable-atomic-util v0.2.4 -> v0.2.5 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating revm-inspectors v0.34.0 -> v0.34.2 Updating sha3-asm v0.1.4 -> v0.1.5 Updating siphasher v1.0.1 -> v1.0.2 Updating slab v0.4.11 -> v0.4.12 Updating syn-solidity v1.5.2 -> v1.5.4 Removing tiny-keccak v2.0.2 Updating zerocopy v0.8.33 -> v0.8.37 Updating zerocopy-derive v0.8.33 -> v0.8.37 Updating zmij v1.0.16 -> v1.0.18 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: update RPC URLs from ithaca.xyz to reth.rs (#13261) * chore: update RPC URLs from ithaca.xyz to reth.rs Co-authored-by: Tim Beiko Amp-Thread-ID: https://ampcode.com/threads/T-019c0a51-3f0a-76eb-ba4a-bfb6a697d9ba Co-authored-by: Amp * fix * fmt * Update rpc.rs * Update rpc.rs * test: update test --------- Co-authored-by: Tim Beiko Co-authored-by: Amp Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg * ci(bench): use depot runner for benchmarks (#13288) * feat(cli): cli markdown docs (#13291) * feat: add foundry-cli-markdown crate Add a new crate for generating Markdown documentation from clap CLIs. This is a fork of clap-markdown with the following enhancements: - Support for grouped options by help heading (PR #48) - Show environment variable names for arguments (PR #50) - Add version information to generated Markdown (PR #52) * feat(cli): add hidden --markdown-help flag Add a hidden --markdown-help flag to forge, cast, anvil, and chisel that prints CLI reference documentation as Markdown and exits. This uses the new foundry-cli-markdown crate to generate the output. * chore(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#13298) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/5e57cd118135c172c3672efd75eb46360885c0ef...c94ce9fb468520275223c153574b00df6fe4bcc9) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.13 to 2.67.18 (#13297) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.13 to 2.67.18. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/710817a1645ef40daad5bcde7431ceccf6cc3528...650c5ca14212efbbf3e580844b04bdccf68dac31) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 (#13299) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/727cc5b0b19bc265bd5ef28fc66bccb284473b5d...5adeaaaf36f64df54f62adb34aa5fbfdb0109d34) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.0.1 to 6.1.0 (#13300) chore(deps): bump mikepenz/release-changelog-builder-action Bumps [mikepenz/release-changelog-builder-action](https://github.com/mikepenz/release-changelog-builder-action) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/mikepenz/release-changelog-builder-action/releases) - [Commits](https://github.com/mikepenz/release-changelog-builder-action/compare/439f79b5b5428107c7688c1d2b0e8bacc9b8792c...6faf020194b7c8853f9e55c4fd92e40b02122a04) --- updated-dependencies: - dependency-name: mikepenz/release-changelog-builder-action dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: fix typos CI (#13303) - Add consts to typos ignore list (valid Rust std::env::consts) - Fix LintCotext -> LintContext typos in linter docs * chore(deps): bump crate-ci/typos from 1.42.2 to 1.43.0 (#13296) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.2 to 1.43.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/a1d64977b4aa1709d6328d518aa753f4899352d8...93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * sec: bump to `bytes` `^1.11.1` for `RUSTSEC-2026-0007` (#13306) bump to 1.11.1 for patch: https://rustsec.org/advisories/RUSTSEC-2026-0007 * chore(anvil,cast): remove unnecessary `populate_blob_hashes()` (#13308) Both `set_blob_sidecar`/`set_blob_sidecar_7594` implement a call to `populate_blob_hashes()` * feat(forge): generate random fuzz seed if none provided (#13309) * feat(forge): generate random fuzz seed if none provided Generate a random seed for fuzz/invariant tests when no seed is explicitly configured. This ensures reproducibility by always having a seed available that can be passed via --fuzz-seed to reproduce test runs. * feat(forge): print fuzz seed on fuzz failure (#13310) * feat(forge): print fuzz seed on test failure When a fuzz or invariant test fails, print the seed used so users can reproduce the failure with --fuzz-seed. * test: update snapshots for fuzz seed output Add [SEED] redaction pattern to match 'Fuzz seed: 0x...' output. Update all test snapshots that have fuzz/invariant failures to include the new seed line. * fix: use [SEED] placeholder in issue_3055 test snapshot Amp-Thread-ID: https://ampcode.com/threads/T-019c26cb-9d21-74f9-9e49-7ea59885e827 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(anvil): cache block timestamp in mined receipts (#13311) * fix: use shared `display_chain` helper in CLI error handler (#13314) * Update handler.rs * Update handler.rs * Add --enable-tx-gas-limit CLI flag for EIP-7825 support (#13307) add --enable-tx-gas-limit * fix(anvil): use consistent chain_id fallback in fork setup (#13276) Co-authored-by: tefyosL-sol * fix(test-utils): skip build artifacts when copying to temp workspace (#13266) * feat(lint): add common uppercase abbreviations to mixedCase exceptions (#13305) Co-authored-by: onbjerg * fix(fmt): correct indentation for closing brace in empty contracts with comments (#13319) Co-authored-by: onbjerg * feat(anvil): add `trace_replayBlockTransactions` endpoint for block txs tracing (#13098) Co-authored-by: onbjerg * fix(eip712): write diagnostics to stderr instead of stdout (#13293) Co-authored-by: onbjerg --------- Signed-off-by: dependabot[bot] Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Matt D Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Tim Beiko Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#373) * Potential fix for code scanning alert no. 108: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Forge/master (#376) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar Co-authored-by: googleworkspace-bot * Hardhat project (#378) * docs(config): complete foundry.toml configuration reference (#13198) * complete config * docs: mark optional fields in config README * use foundry book for full config * docs(config): point to Foundry Book for configuration reference * feat(invariant): add optimization mode for invariant testing (#13196) * feat(invariant): add optimization mode for invariant testing Adds optimization mode for invariant testing, similar to Echidna. This mode maximizes an `int256` return value from a function prefixed with `optimize_`. **Usage:** - Define an invariant function returning `int256` with `optimize_` prefix - Foundry will fuzz to find the sequence that maximizes this value - The best value and sequence are tracked and reported - Sequence shrinking is applied to find the minimal reproducing sequence **Example:** ```solidity function optimize_maxRoundingError() external view returns (int256) { return int256(pool.totalShares()) - int256(pool.expectedShares()); } ``` **Bug fix during implementation:** Fixed a bug in shrink logic where `vm.warp` and `vm.roll` values were not correctly accumulated when replaying shrunk sequences. This caused the "best" sequence to be non-reproducible because time/block values depended on removed calls. The fix: - Keeps reverted calls in sequence during optimization to preserve warp/roll - Accumulates warps/rolls from removed calls into kept calls during shrinking - Updates inspector's cheatcodes.block alongside executor.env Closes #12190 Amp-Thread-ID: https://ampcode.com/threads/T-019bea21-149c-728c-9556-850778b70ea3 Co-authored-by: Amp * refactor: isolate optimization shrinking logic from check mode - Keep shrink_sequence and check_sequence unchanged for regular invariant checks - Add shrink_sequence_value and check_sequence_value for optimization mode - Optimization mode handles warp/roll accumulation from removed calls - replay.rs dispatches to correct shrinking function based on target_value * refactor: cleanup shrink optimization code, extract helpers --------- Co-authored-by: Amp * fix: only classify setUp as test setup if it has no parameters (#13204) Contracts with `setUp(bytes memory)` (common in Gnosis Safe/Zodiac modules) were incorrectly classified as dev/test contracts and excluded from `forge build --sizes` output. Now `setUp` is only classified as a test `Setup` function when it has no parameters, matching Forge's actual test setup behavior. Fixes #11126 * chore: fix clippy lints (#13207) * feat(cast): add --flatten flag to cast interface (#13201) * feat(cast): add --all-in-one flag to cast interface Adds a new `--all-in-one` flag to `cast interface` that inlines inherited/library struct types directly into the generated interface. This addresses the issue where `cast interface` generates a separate `library` block for struct types that originate from inherited interfaces, making the generated interface less usable for some workflows. With `--all-in-one`, all types are consolidated into a single interface: ```solidity // Before (default): library IBase { struct TestStruct { address asset; } } interface Contract { function test(IBase.TestStruct memory) external; } // After (with --all-in-one): interface Contract { struct TestStruct { address asset; } function test(TestStruct memory) external; } ``` Uses alloy-json-abi's `ToSolConfig::one_contract(true)` option introduced in alloy-core 0.8.24. Closes #9960 * chore: cargo fmt * refactor: rename --all-in-one to --flatten per review * chore: fix rustfmt * Update flake.lock (#13212) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/edd5602' (2026-01-17) → 'github:nix-community/fenix/93523fa' (2026-01-24) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/adbff8b' (2026-01-15) → 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/be5afa0' (2026-01-16) → 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) Co-authored-by: github-actions[bot] * chore(deps): weekly `cargo update` (#13213) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 62 packages to latest compatible versions Updating alloy-chains v0.2.29 -> v0.2.30 Updating alloy-consensus v1.4.3 -> v1.5.2 Updating alloy-consensus-any v1.4.3 -> v1.5.2 Updating alloy-contract v1.4.3 -> v1.5.2 Updating alloy-eip5792 v1.4.3 -> v1.5.2 Updating alloy-eip7928 v0.3.0 -> v0.3.2 Updating alloy-eips v1.4.3 -> v1.5.2 Updating alloy-ens v1.4.3 -> v1.5.2 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-genesis v1.4.3 -> v1.5.2 Updating alloy-json-rpc v1.4.3 -> v1.5.2 Updating alloy-network v1.4.3 -> v1.5.2 Updating alloy-network-primitives v1.4.3 -> v1.5.2 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-provider v1.4.3 -> v1.5.2 Updating alloy-pubsub v1.4.3 -> v1.5.2 Updating alloy-rpc-client v1.4.3 -> v1.5.2 Updating alloy-rpc-types v1.4.3 -> v1.5.2 Updating alloy-rpc-types-anvil v1.4.3 -> v1.5.2 Updating alloy-rpc-types-any v1.4.3 -> v1.5.2 Updating alloy-rpc-types-beacon v1.4.3 -> v1.5.2 Updating alloy-rpc-types-debug v1.4.3 -> v1.5.2 Updating alloy-rpc-types-engine v1.4.3 -> v1.5.2 Updating alloy-rpc-types-eth v1.4.3 -> v1.5.2 Updating alloy-rpc-types-trace v1.4.3 -> v1.5.2 Updating alloy-rpc-types-txpool v1.4.3 -> v1.5.2 Updating alloy-serde v1.4.3 -> v1.5.2 Updating alloy-signer v1.4.3 -> v1.5.2 Updating alloy-signer-aws v1.4.3 -> v1.5.2 Updating alloy-signer-gcp v1.4.3 -> v1.5.2 Updating alloy-signer-ledger v1.4.3 -> v1.5.2 Updating alloy-signer-local v1.4.3 -> v1.5.2 Updating alloy-signer-trezor v1.4.3 -> v1.5.2 Updating alloy-signer-turnkey v1.4.3 -> v1.5.2 Updating alloy-transport v1.4.3 -> v1.5.2 Updating alloy-transport-http v1.4.3 -> v1.5.2 Updating alloy-transport-ipc v1.4.3 -> v1.5.2 Updating alloy-transport-ws v1.4.3 -> v1.5.2 Updating alloy-tx-macros v1.4.3 -> v1.5.2 Updating aws-lc-rs v1.15.3 -> v1.15.4 Updating aws-lc-sys v0.36.0 -> v0.37.0 Updating cc v1.2.53 -> v1.2.54 Updating clearscreen v4.0.2 -> v4.0.3 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating libm v0.2.15 -> v0.2.16 Unchanged matchit v0.8.4 (available: v0.8.6) Removing nix v0.29.0 Updating num-conv v0.1.0 -> v0.2.0 Updating opener v0.8.3 -> v0.8.4 Updating openssl-probe v0.2.0 -> v0.2.1 Updating proc-macro2 v1.0.105 -> v1.0.106 Updating process-wrap v8.2.1 -> v9.0.1 Updating quote v1.0.43 -> v1.0.44 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating socket2 v0.6.1 -> v0.6.2 Updating time v0.3.45 -> v0.3.46 Updating time-core v0.1.7 -> v0.1.8 Updating time-macros v0.2.25 -> v0.2.26 Updating uuid v1.19.0 -> v1.20.0 Updating watchexec-signals v5.0.0 -> v5.0.1 Updating watchexec-supervisor v5.0.1 -> v5.0.2 Updating web_atoms v0.2.1 -> v0.2.3 Updating windows v0.61.3 -> v0.62.2 Updating windows-collections v0.2.0 -> v0.3.2 Removing windows-core v0.61.2 Updating windows-future v0.2.1 -> v0.3.2 Removing windows-link v0.1.3 Updating windows-numerics v0.2.0 -> v0.3.1 Removing windows-result v0.3.4 Removing windows-strings v0.4.2 Updating windows-threading v0.1.0 -> v0.2.1 Updating zmij v1.0.15 -> v1.0.16 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * chore(anvil): clean up remaining dead code after Odyssey sunset (#13211) * fix(coverage): correct BRDA hit values for LCOV consistency (#13151) * fix(coverage): correct BRDA hit values for LCOV consistency Per the LCOV tracefile format specification, BRDA hit values should be: - "-" when the expression was never evaluated (line not executed) - "0" when the branch exists and was evaluated but never taken - "N" when the branch was taken N times Previously, we were outputting "-" for all branches with 0 hits, which caused genhtml to fail with "inconsistent" errors when a line was hit (DA shows hits > 0) but branches on that line showed "-". This fix tracks line hits in a first pass, then uses that information to determine whether to output "-" (line never hit) or "0" (line hit but branch not taken) for branches with 0 hits. Fixes foundry-rs/foundry#11548 * fix: clippy explicit_iter_loop warning * test(coverage): add brda_lcov_consistency test for BRDA hit values Verifies that BRDA outputs follow LCOV spec: - "0" when line was executed but branch not taken - "-" when line was never executed This catches the inconsistency that caused genhtml to fail. --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks * feat(forge): add browser wallet support for `forge script` (#12952) * feat(script): add support for browser wallet * fix: browser wallet opts defaults * chore: bump foundry-browser-wallet v0.1.0 * ci: use shared cache mounts for parallel builds (#13231) perf(docker): use shared cache mounts for parallel builds Change cache mount sharing mode from `locked` to `shared` for cargo registry, git, and sccache directories. With `sharing=locked`, parallel builds must wait for exclusive access to each cache, causing builds to queue up even when compilation itself is fast. Both cargo and sccache handle concurrent access correctly, so `shared` is safe and allows parallel builds to proceed without blocking. Amp-Thread-ID: https://ampcode.com/threads/T-019bfc1d-3cee-70ca-9caa-01e33acdff46 Co-authored-by: Amp * fix(anvil): preserve withdrawals in SerializableBlock for state dump/load (#13227) * fix(anvil): preserve withdrawals in SerializableBlock for state dump/load * add: test * fix ci * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.1 to 3.15.2 (#13236) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.1 to 3.15.2. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/1d699fc25db3f9e079cd2f168ca007a4183389be...89ab342bd48ff7318caf8d39d6a330c7b1df8f2f) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.66.2 to 2.67.13 (#13235) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.66.2 to 2.67.13. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.66.2...710817a1645ef40daad5bcde7431ceccf6cc3528) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.13 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 8.0.0 to 8.1.0 (#13234) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 8.0.0 to 8.1.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/98357b18bf14b5342f975ff684046ec3b2a07725...c0f553fe549906ede9cf27b5156039d195d2ece0) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.42.1 to 1.42.2 (#13233) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.1 to 1.42.2. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/65120634e79d8374d1aa2f27e54baa0c364fff5a...a1d64977b4aa1709d6328d518aa753f4899352d8) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.42.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(docker): expose VERGEN_GIT_SHA to build environment (#13237) * docs(coverage): link to stack-too-deep guide in warnings (#13240) Update coverage warnings to point to the new Foundry Book guide instead of the GitHub issue, providing users with actionable techniques to resolve the error. Amp-Thread-ID: https://ampcode.com/threads/T-019bff7a-8502-72b7-af76-794f258e8c67 Co-authored-by: Amp * Polkadot/Kusama/PolkadotTestnet for RPC gas estimation (#12537) * adds polkadot testnet * adds polkadot testnet * bump alloy chains * fmt * Remove kluster * update rpc url * update rpc url * fmt * adds polkadot kusama * bump alloy chains * perf(anvil): avoid redundant block queries in ots_getBlockTransactions (#13243) * fix(anvil): use consistent chain_id fallback for blob params (#13241) * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * feat(primitives): introduce `NetworkWallet` impl for `EthereumWallet` (#13248) - Seamless support for eth/op/tempo txs - This aims to replace `WalletSigner`'s impl once `FoundryNetwork` will be used everywhere * refactor(common): make `ProviderBuilder` generic over `Network` (#13250) * refactor(common): make `ProviderBuilder` generic over `Network` - Updated `ProviderBuilder` helper to be generic, which will facilate `FoundryNetwork` rollout - Adjusted the instantiation of `ProviderBuilder` in various locations to use `AnyNetwork`. - Enhanced the `build` and `build_with_wallet` methods to accommodate the new generic structure. * fix: relax trait bound on `N::TransactionRequest` * fix: comment * fix: comment * fix(anvil): return error instead of empty vec for out-of-range log queries (#13251) * fix(config): respect user-configured etherscan URL over chain defaults (#13239) fix(config): Respect user-configured etherscan URL over chain defaults (#13238) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` (#13218) * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` * fix: after `TransactionBuilder4844` impl * feat(common): introduce generic `ProviderBuilder::from_config` method (#13268) - keep the existing helpers that erase generic to avoid breaking change * fix(cast): remove redundant chain() call in explorer_client (#13272) Co-authored-by: tefyosL-sol * fix(verify): respect user-configured etherscan URL over chain defaults (#13275) Co-authored-by: tefyosL-sol * Update flake.lock (#13279) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/93523fa' (2026-01-24) → 'github:nix-community/fenix/b2344f3' (2026-01-31) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) → 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) → 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) Co-authored-by: github-actions[bot] * fix(invariant): remove unused cloned calldata (#12893) * fix(invariant): prune calldata to bound memory usage in long runs * style: fix formatting in invariant executor * chore: remove vyper files from testdata to fix CI * chore: trigger ci update * fix(invariant): remove unused FuzzCase.calldata field to prevent OOM The calldata field in FuzzCase was stored but never read after construction. Removing it entirely eliminates memory accumulation during long invariant runs. Changes: - Remove FuzzCase.calldata field (unused after construction) - Remove prune_calldata() methods (no longer needed) - Restore vyper test files that were incorrectly deleted Fixes #12397 Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(debugger): display actual gas usage alongside refund counter (#13271) * feat(debugger): display actual gas usage alongside refund counter * fix(forge-test): fix flamegraph gas inaccuracy issues * reverse `--decode-internal` not default with `--flamechart` * make gas unsigned as `inferno` doesn't support anyway * replace revm-inspectors patch * fix clippy * update tests * update tests * fmt * fix(config): handle decimal string in U256 deserialization (#13284) * fix: adjust numerical cells in gas report to be right aligned (#12883) * Adjust Cells to be Right Aligned in Gas Report * fix test and fmt --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): weekly `cargo update` (#13280) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v1.5.2 -> v1.5.4 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-json-abi v1.5.2 -> v1.5.4 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-primitives v1.5.2 -> v1.5.4 Updating alloy-sol-macro v1.5.2 -> v1.5.4 Updating alloy-sol-macro-expander v1.5.2 -> v1.5.4 Updating alloy-sol-macro-input v1.5.2 -> v1.5.4 Updating alloy-sol-type-parser v1.5.2 -> v1.5.4 Updating alloy-sol-types v1.5.2 -> v1.5.4 Updating annotate-snippets v0.12.10 -> v0.12.11 Updating aws-smithy-async v1.2.7 -> v1.2.10 Updating aws-smithy-http-client v1.1.5 -> v1.1.8 Updating aws-smithy-observability v0.2.0 -> v0.2.3 Updating aws-smithy-query v0.60.9 -> v0.60.12 Updating aws-smithy-runtime-api v1.10.0 -> v1.11.2 Updating aws-smithy-types v1.3.6 -> v1.4.2 Updating bytemuck v1.24.0 -> v1.25.0 Updating cc v1.2.54 -> v1.2.55 Updating clap v4.5.54 -> v4.5.56 Updating clap_builder v4.5.54 -> v4.5.56 Updating clap_derive v4.5.49 -> v4.5.55 Updating cliclack v0.3.7 -> v0.3.8 Updating find-msvc-tools v0.1.8 -> v0.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating iana-time-zone v0.1.64 -> v0.1.65 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating keccak-asm v0.1.4 -> v0.1.5 Unchanged matchit v0.8.4 (available: v0.8.6) Updating notify-types v2.0.0 -> v2.1.0 Updating portable-atomic v1.13.0 -> v1.13.1 Updating portable-atomic-util v0.2.4 -> v0.2.5 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating revm-inspectors v0.34.0 -> v0.34.2 Updating sha3-asm v0.1.4 -> v0.1.5 Updating siphasher v1.0.1 -> v1.0.2 Updating slab v0.4.11 -> v0.4.12 Updating syn-solidity v1.5.2 -> v1.5.4 Removing tiny-keccak v2.0.2 Updating zerocopy v0.8.33 -> v0.8.37 Updating zerocopy-derive v0.8.33 -> v0.8.37 Updating zmij v1.0.16 -> v1.0.18 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: update RPC URLs from ithaca.xyz to reth.rs (#13261) * chore: update RPC URLs from ithaca.xyz to reth.rs Co-authored-by: Tim Beiko Amp-Thread-ID: https://ampcode.com/threads/T-019c0a51-3f0a-76eb-ba4a-bfb6a697d9ba Co-authored-by: Amp * fix * fmt * Update rpc.rs * Update rpc.rs * test: update test --------- Co-authored-by: Tim Beiko Co-authored-by: Amp Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg * ci(bench): use depot runner for benchmarks (#13288) * feat(cli): cli markdown docs (#13291) * feat: add foundry-cli-markdown crate Add a new crate for generating Markdown documentation from clap CLIs. This is a fork of clap-markdown with the following enhancements: - Support for grouped options by help heading (PR #48) - Show environment variable names for arguments (PR #50) - Add version information to generated Markdown (PR #52) * feat(cli): add hidden --markdown-help flag Add a hidden --markdown-help flag to forge, cast, anvil, and chisel that prints CLI reference documentation as Markdown and exits. This uses the new foundry-cli-markdown crate to generate the output. * chore(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#13298) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/5e57cd118135c172c3672efd75eb46360885c0ef...c94ce9fb468520275223c153574b00df6fe4bcc9) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.13 to 2.67.18 (#13297) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.13 to 2.67.18. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/710817a1645ef40daad5bcde7431ceccf6cc3528...650c5ca14212efbbf3e580844b04bdccf68dac31) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 (#13299) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/727cc5b0b19bc265bd5ef28fc66bccb284473b5d...5adeaaaf36f64df54f62adb34aa5fbfdb0109d34) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.0.1 to 6.1.0 (#13300) chore(deps): bump mikepenz/release-changelog-builder-action Bumps [mikepenz/release-changelog-builder-action](https://github.com/mikepenz/release-changelog-builder-action) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/mikepenz/release-changelog-builder-action/releases) - [Commits](https://github.com/mikepenz/release-changelog-builder-action/compare/439f79b5b5428107c7688c1d2b0e8bacc9b8792c...6faf020194b7c8853f9e55c4fd92e40b02122a04) --- updated-dependencies: - dependency-name: mikepenz/release-changelog-builder-action dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: fix typos CI (#13303) - Add consts to typos ignore list (valid Rust std::env::consts) - Fix LintCotext -> LintContext typos in linter docs * chore(deps): bump crate-ci/typos from 1.42.2 to 1.43.0 (#13296) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.2 to 1.43.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/a1d64977b4aa1709d6328d518aa753f4899352d8...93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * sec: bump to `bytes` `^1.11.1` for `RUSTSEC-2026-0007` (#13306) bump to 1.11.1 for patch: https://rustsec.org/advisories/RUSTSEC-2026-0007 * chore(anvil,cast): remove unnecessary `populate_blob_hashes()` (#13308) Both `set_blob_sidecar`/`set_blob_sidecar_7594` implement a call to `populate_blob_hashes()` * feat(forge): generate random fuzz seed if none provided (#13309) * feat(forge): generate random fuzz seed if none provided Generate a random seed for fuzz/invariant tests when no seed is explicitly configured. This ensures reproducibility by always having a seed available that can be passed via --fuzz-seed to reproduce test runs. * feat(forge): print fuzz seed on fuzz failure (#13310) * feat(forge): print fuzz seed on test failure When a fuzz or invariant test fails, print the seed used so users can reproduce the failure with --fuzz-seed. * test: update snapshots for fuzz seed output Add [SEED] redaction pattern to match 'Fuzz seed: 0x...' output. Update all test snapshots that have fuzz/invariant failures to include the new seed line. * fix: use [SEED] placeholder in issue_3055 test snapshot Amp-Thread-ID: https://ampcode.com/threads/T-019c26cb-9d21-74f9-9e49-7ea59885e827 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(anvil): cache block timestamp in mined receipts (#13311) * fix: use shared `display_chain` helper in CLI error handler (#13314) * Update handler.rs * Update handler.rs * Add --enable-tx-gas-limit CLI flag for EIP-7825 support (#13307) add --enable-tx-gas-limit * fix(anvil): use consistent chain_id fallback in fork setup (#13276) Co-authored-by: tefyosL-sol * fix(test-utils): skip build artifacts when copying to temp workspace (#13266) * feat(lint): add common uppercase abbreviations to mixedCase exceptions (#13305) Co-authored-by: onbjerg * fix(fmt): correct indentation for closing brace in empty contracts with comments (#13319) Co-authored-by: onbjerg * feat(anvil): add `trace_replayBlockTransactions` endpoint for block txs tracing (#13098) Co-authored-by: onbjerg * fix(eip712): write diagnostics to stderr instead of stdout (#13293) Co-authored-by: onbjerg * foundryup: tempo now distributes all binaries (#13337) Amp-Thread-ID: https://ampcode.com/threads/T-019c2ea2-963a-744a-8b1d-57709bc295be Co-authored-by: Amp * fix(config): handle vyper section with skip_serializing_if fields (#13318) * fix(config): handle vyper section with skip_serializing_if fields The vyper config section uses skip_serializing_if = Option::is_none on all fields, causing the default serialization to produce an empty dict. This led to all vyper keys being flagged as unknown. Add explicit VYPER_KEYS constant and special-case the vyper section in collect_standalone_section_warnings to use these known keys instead of deriving them from the (empty) default serialization. Fixes #13316 * test(config): add regression tests for vyper config warnings Tests for #13316: - no_false_warnings_for_vyper_config_keys: valid vyper keys in standalone section - no_false_warnings_for_nested_vyper_config_keys: valid vyper keys in profile - warns_on_unknown_vyper_keys: unknown vyper keys should still warn Amp-Thread-ID: https://ampcode.com/threads/T-019c28b9-9c8c-76bf-96a5-ff5a504c0507 Co-authored-by: Amp * fix build issues * fix(config): handle nested vyper section with skip_serializing_if fields The VyperConfig struct uses skip_serializing_if on all Option fields, causing the default serialization to produce an empty dict. This caused false warnings for valid vyper keys like optimize, path, and experimental_codegen when used in profile nested sections like [profile.default.vyper]. Uses the existing VYPER_KEYS constant for nested vyper sections, matching how standalone [vyper] sections are already handled. Fixes #13316 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c297e-a282-7188-8f79-5080d3e451a9 --------- Co-authored-by: Amp Co-authored-by: zerosnacks Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: broken config test, currently blocking CI (#13340) fix broken config * skip checksum hash in create2 mining when case-insensitive (#13331) * kip checksum hash in create2 mining * fix the clippy * fix: avoid setting FOUNDRY_PROFILE: ci in template workflows, profile does not exist (#13339) avoid encoding FOUNDRY_PROFILE: ci, profile does not exist * perf(evm): wrap Executor.backend in Arc for copy-on-write cloning (#13327) * perf(evm): wrap Executor.backend in Arc for copy-on-write cloning During parallel fuzzing, each worker clones the Executor. Previously this deep-cloned the entire Backend (CacheDB, JournaledState, state snapshots), which could be 10-50MB per clone with 16 workers = 160-800MB wasted memory. This change wraps Backend in Arc and uses Arc::make_mut() for copy-on-write semantics. When workers only read state, they share the same backend. When a worker mutates, it gets its own copy. Expected impact: - ~80% memory reduction for parallel fuzz runs - Faster executor clone (pointer copy instead of deep clone) - No behavioral change: mutations still get isolated copies Amp-Thread-ID: https://ampcode.com/threads/T-019c2af1-f00b-723a-a3c3-25cbd6f3e92b Co-authored-by: Amp * test: update config test expectations for new mixed_case_exceptions Fix test expectations after 1bd687f0d added new values (ID, URL, API, JSON, XML, HTML, HTTP, HTTPS) to lint.mixed_case_exceptions defaults. Amp-Thread-ID: https://ampcode.com/threads/T-019c2af1-f00b-723a-a3c3-25cbd6f3e92b Co-authored-by: Amp * Update config.rs * Update config.rs * fix: restore "URI" in config test JSON expectations Amp-Thread-ID: https://ampcode.com/threads/T-019c2f68-f9df-76bc-ba4c-94fbe1789c9c Co-authored-by: Amp --------- Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks * chore(ci): update time crate (#13348) * test(cast): ignore flaky_run_celo_with_precompiles (Celo RPC no longer supports debug_traceTransaction) (#13347) * fix(test-utils): create destination directory in copy_dir_filtered (#13350) * chore(wallets): Remove `NetworkWallet` impl for `WalletSigner` (#13343) - superseeded by `EthereumWallet`'s one, which is integrated in `TransactionBuilder` flow * fix(anvil): return error when querying future block number in with_database_at (#13267) * return error when querying future block number * fix test --------- Co-authored-by: onbjerg Co-authored-by: Matthias Seitz * chore: remove stale `tiny-keccak` references (#13358) * chore: remove stale tiny-keccak profile override * chore: remove stale tiny-keccak deny exception * chore(script): typo (#13353) * perf(cheatcodes): loop invariant code motion by hand (#13357) * chore(anvil): remove unnecessary clone operations (#13330) * perf(linking): replace double hash mpa lookup contains_key + [] with single get (#13361) * fix(verify): correct Sourcify API URL construction for custom chains (#13360) Update verify.rs * chore(common): remove dead `with_spinner_reporter` function (#13366) * resolve absolute and relative paths on Windows (#13364) * fix: unittest failed (#13371) * perf(anvil): reuse storage root from prove_storage instead of recompu… (#13363) perf(anvil): reuse storage root from prove_storage instead of recomputing * chore(deps): weekly `cargo update` (#13384) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 94 packages to latest compatible versions Updating alloy-consensus v1.5.2 -> v1.6.1 Updating alloy-consensus-any v1.5.2 -> v1.6.1 Updating alloy-contract v1.5.2 -> v1.6.1 Updating alloy-eip5792 v1.5.2 -> v1.6.1 Updating alloy-eips v1.5.2 -> v1.6.1 Updating alloy-ens v1.5.2 -> v1.6.1 Updating alloy-evm v0.26.3 -> v0.26.4 (available: v0.27.2) Updating alloy-genesis v1.5.2 -> v1.6.1 Updating alloy-json-rpc v1.5.2 -> v1.6.1 Updating alloy-network v1.5.2 -> v1.6.1 Updating alloy-network-primitives v1.5.2 -> v1.6.1 Updating alloy-op-evm v0.26.3 -> v0.26.4 (available: v0.27.2) Updating alloy-provider v1.5.2 -> v1.6.1 Updating alloy-pubsub v1.5.2 -> v1.6.1 Updating alloy-rlp v0.3.12 -> v0.3.13 Updating alloy-rlp-derive v0.3.12 -> v0.3.13 Updating alloy-rpc-client v1.5.2 -> v1.6.1 Updating alloy-rpc-types v1.5.2 -> v1.6.1 Updating alloy-rpc-types-anvil v1.5.2 -> v1.6.1 Updating alloy-rpc-types-any v1.5.2 -> v1.6.1 Updating alloy-rpc-types-beacon v1.5.2 -> v1.6.1 Updating alloy-rpc-types-debug v1.5.2 -> v1.6.1 Updating alloy-rpc-types-engine v1.5.2 -> v1.6.1 Updating alloy-rpc-types-eth v1.5.2 -> v1.6.1 Updating alloy-rpc-types-trace v1.5.2 -> v1.6.1 Updating alloy-rpc-types-txpool v1.5.2 -> v1.6.1 Updating alloy-serde v1.5.2 -> v1.6.1 Updating alloy-signer v1.5.2 -> v1.6.1 Updating alloy-signer-aws v1.5.2 -> v1.6.1 Updating alloy-signer-gcp v1.5.2 -> v1.6.1 Updating alloy-signer-ledger v1.5.2 -> v1.6.1 Updating alloy-signer-local v1.5.2 -> v1.6.1 Updating alloy-signer-trezor v1.5.2 -> v1.6.1 Updating alloy-signer-turnkey v1.5.2 -> v1.6.1 Updating alloy-transport v1.5.2 -> v1.6.1 Updating alloy-transport-http v1.5.2 -> v1.6.1 Updating alloy-transport-ipc v1.5.2 -> v1.6.1 Updating alloy-transport-ws v1.5.2 -> v1.6.1 Updating alloy-trie v0.9.3 -> v0.9.4 Updating alloy-tx-macros v1.5.2 -> v1.6.1 Updating anyhow v1.0.100 -> v1.0.101 Updating async-compression v0.4.37 -> v0.4.39 Updating aws-config v1.8.12 -> v1.8.13 Updating aws-runtime v1.5.18 -> v1.6.0 Updating aws-sdk-kms v1.98.0 -> v1.99.0 Updating aws-sdk-sso v1.92.0 -> v1.93.0 Updating aws-sdk-ssooidc v1.94.0 -> v1.95.0 Updating aws-sdk-sts v1.96.0 -> v1.97.0 Updating aws-sigv4 v1.3.7 -> v1.3.8 Updating aws-smithy-async v1.2.10 -> v1.2.11 Updating aws-smithy-http v0.62.6 -> v0.63.3 Updating aws-smithy-http-client v1.1.8 -> v1.1.9 Updating aws-smithy-json v0.61.9 -> v0.62.3 Updating aws-smithy-observability v0.2.3 -> v0.2.4 Updating aws-smithy-query v0.60.12 -> v0.60.13 Updating aws-smithy-runtime v1.9.8 -> v1.10.0 Updating aws-smithy-runtime-api v1.11.2 -> v1.11.3 Updating aws-smithy-types v1.4.2 -> v1.4.3 Updating clap v4.5.56 -> v4.5.57 Updating clap_builder v4.5.56 -> v4.5.57 Updating flate2 v1.1.8 -> v1.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating hyper-util v0.1.19 -> v0.1.20 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating interprocess v2.2.3 -> v2.3.1 Updating jiff v0.2.18 -> v0.2.19 Updating jiff-static v0.2.18 -> v0.2.19 Unchanged matchit v0.8.4 (available: v0.8.6) Updating memchr v2.7.6 -> v2.8.0 Updating nybbles v0.4.7 -> v0.4.8 Updating pest v2.8.5 -> v2.8.6 Updating pest_derive v2.8.5 -> v2.8.6 Updating pest_generator v2.8.5 -> v2.8.6 Updating pest_meta v2.8.5 -> v2.8.6 Updating proptest v1.9.0 -> v1.10.0 Unchanged rand v0.8.5 (available: v0.9.2) Updating rapidhash v4.2.1 -> v4.2.2 Updating regex v1.12.2 -> v1.12.3 Updating regex-automata v0.4.13 -> v0.4.14 Updating regex-lite v0.1.8 -> v0.1.9 Updating regex-syntax v0.8.8 -> v0.8.9 Unchanged reqwest v0.12.28 (available: v0.13.2) Updating schemars v1.2.0 -> v1.2.1 Updating schemars_derive v1.2.0 -> v1.2.1 Updating sval v2.16.0 -> v2.17.0 Updating sval_buffer v2.16.0 -> v2.17.0 Updating sval_dynamic v2.16.0 -> v2.17.0 Updating sval_fmt v2.16.0 -> v2.17.0 Updating sval_json v2.16.0 -> v2.17.0 Updating sval_nested v2.16.0 -> v2.17.0 Updating sval_ref v2.16.0 -> v2.17.0 Updating sval_serde v2.16.0 -> v2.17.0 Updating system-configuration v0.6.1 -> v0.7.0 Updating webbrowser v1.0.6 -> v1.1.0 Updating webpki-roots v1.0.5 -> v1.0.6 Updating zerocopy v0.8.37 -> v0.8.39 Updating zerocopy-derive v0.8.37 -> v0.8.39 Updating zlib-rs v0.5.5 -> v0.6.0 Updating zmij v1.0.18 -> v1.0.19 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * Update flake.lock (#13383) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/b2344f3' (2026-01-31) → 'github:nix-community/fenix/e1b28f6' (2026-02-07) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) → 'github:rust-lang/rust-analyzer/d2a00da' (2026-02-05) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) → 'github:NixOS/nixpkgs/ae67888' (2026-02-06) Co-authored-by: github-actions[bot] * perf(verify): reuse transaction from earlier RPC call instead of fetching twice (#13391) * perf(verify): reuse transaction from earlier RPC call instead of fetching twice * fix ci * fix(cast): --json support for erc20 cmds (#12727) * refactor(anvil): using is_ok since it's more robust (#13377) * fix: may div by zero (#13369) * refactor(primitives): turn `FoundryTransactionRequest` into an enum (#13278) - Combines Eth's, Op's, and Tempo's transaction requests to inherit Op/Tempo tx building * perf: avoid checksum (#13374) * docs: slim readme (#13393) * fix: correct trace message in dynamic linking preprocessor (#13394) * perf(invariant): avoid cloning state changeset in fuzz runs (#13398) Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore(deps): bump depot/build-push-action from 1.16.2 to 1.17.0 (#13405) Bumps [depot/build-push-action](https://github.com/depot/build-push-action) from 1.16.2 to 1.17.0. - [Release notes](https://github.com/depot/build-push-action/releases) - [Commits](https://github.com/depot/build-push-action/compare/9785b135c3c76c33db102e45be96a25ab55cd507...5f3b3c2e5a00f0093de47f657aeaefcedff27d18) --- updated-dependencies: - dependency-name: depot/build-push-action dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.18 to 2.67.27 (#13406) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.18 to 2.67.27. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/650c5ca14212efbbf3e580844b04bdccf68dac31...1e67dedb5e3c590e1c9d9272ace46ef689da250d) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.27 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.0 to 1.43.4 (#13407) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.43.0 to 1.43.4. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c...78bc6fb2c0d734235d57a2d6b9de923cc325ebdd) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ci: use dedicated template for isolate flaky test failures (#13409) * chore(deps): bump depot/setup-action from 1.6.0 to 1.7.1 (#13408) * fix(primitives): `FoundryTransactionRequest` conversion w/ tempo variant (#13401) - Fix `TempoTransactionRequest` variant, as inner req was always set to default. - Added unit tests to assess `FoundryTransactionRequest` proper variant routing * return error instead of empty array when filter not found (#13415) * chore(config): remove unused enum accessor methods (#13414) * fix(cast): clean up temp dir in `cast storage` when etherscan cache is unavailable (#13418) * perf(primitives): avoid cloning receipts (#13396) * fix: constructor params and args check (#13375) * fix: correct path format in get_paths doc comment (#13388) * ci: replace merge_group with push on master (#13419) * ci(release): pin action-gh-release to v2.4.2 (#13420) v2.5.0 introduced a draft→finalize flow that races in matrix jobs, causing 'Too many retries' failures in the Create release step. See: https://github.com/softprops/action-gh-release/issues/704 Amp-Thread-ID: https://ampcode.com/threads/T-019c4c6d-c55a-752a-8b27-25413f485bed Co-authored-by: Amp * fix(anvil): handle disk cache write failures in state eviction (#13332) * feat(forge,chisel): realtime `console.log` (#13321) * fix(cast): remove duplicate receipt handling in Tempo transactions (#13378) * perf(traces): deduplicate addresses before external fetching (#13320) * fix: prevent panic on etherscan client creation failure in test command (#13395) * perf(config): skip redundant remapping detection in _with_root (#13389) * fix(common): remove trailing space in `state_root` match pattern (#13426) * chore(config): `curl` mode as config key (#13260) Co-authored-by: onbjerg * fix(config): normalize deny_warnings from env vars (#13434) * fix: correct dead condition in command error formatting (#13427) * add missing JSON output support for `erc20 decimals` (#13438) * fix(anvil): variable shadowing bug in ReadyTransactions::remove_with_markers (#13436) Update transactions.rs * Update flake.lock (#13448) * chore(deps): weekly `cargo update` (#13449) * feat(evm): `ForkDatabase`/`MultiFork` generic over `Network` (#13459) * feat(evm): `ForkDatabase`/`MultiFork` generic over `Network` bump `foundry-fork-db` * fix: typo * fix(cheatcodes): fix vm.expectRevert for direct precompile calls (#13460) Precompile calls don't create an interpreter frame, so `initialize_interp` never fires and `max_depth` never gets bumped beyond the cheatcode call depth. This causes the depth check in `handle_expect_revert` to fail with "call didn't revert at a lower depth than cheatcode call depth". Track `max_depth` in the `call` hook as well, accounting for the callee depth (`curr_depth + 1`). Amp-Thread-ID: https://ampcode.com/threads/T-019c63a2-2c36-7334-ab55-2931a174b59c Co-authored-by: Amp * fix(lint): remove unreachable macro arm in declare_forge_lint (#13452) * chore(flake): use nightly rustfmt (#13441) * chore(flake): use nightly rustfmt * chore(flake): update flake * feat: add `executeTransaction` cheatcode (#13437) feat: add executeTransaction cheatcode Port the executeTransaction cheatcode from tempoxyz/tempo-foundry. Executes RLP-encoded signed transactions in an isolated EVM context with full semantics (like --isolate mode). OP deposit and Tempo AA transactions return errors for now (marked with TODOs). * fix(forge): don't reset snapshot diff result on missing file (#13442) * fix(traces): check HTTP status before JSON parsing in Sourcify fetcher (#13446) * Update external.rs * chore: fmt * test: rm useless tests --------- Co-authored-by: Oliver Nordbjerg * feat(cheatcodes): add Ed25519 crypto cheatcodes (#13450) * feat(cheatcodes): add Ed25519 crypto cheatcodes Add four new cheatcodes for Ed25519 cryptography: - createEd25519Key(bytes32 salt) - deterministic key generation - publicKeyEd25519(bytes32 privateKey) - derive public key - signEd25519(namespace, message, privateKey) - sign with domain separation - verifyEd25519(signature, namespace, message, publicKey) - verify signatures Uses ed25519-consensus crate. Includes comprehensive unit tests for determinism, namespace separation, edge cases, and invalid inputs. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c5f04-a6ed-7015-9b4d-4464a35bc26c * chore: solidity test * test: fix assertions --------- Co-authored-by: Amp Co-authored-by: Oliver Nordbjerg * feat(lint): add missing visit methods to EarlyLintVisitor (#13454) Update early.rs * notify subscribers for txs promoted after block mining (#13464) * notify subscribers for txs promoted after block mining * refactor: extract notify_ready helper to deduplicate notification logic Amp-Thread-ID: https://ampcode.com/threads/T-019c6840-d225-723a-bf92-46e4e29c7ad1 Co-authored-by: Amp --------- Co-authored-by: Matthias Seitz Co-authored-by: Amp * chore(deps): bump taiki-e/install-action from 2.67.27 to 2.68.0 (#13465) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.27 to 2.68.0. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/1e67dedb5e3c590e1c9d9272ace46ef689da250d...f8d25fb8a2df08dcd3cead89780d572767b8655f) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.68.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/cache-cargo-install-action from 3.0.1 to 3.0.2 (#13466) Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases) - [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/34ce5120836e5f9f1508d8713d7fdea0e8facd6f...2bfc3cedaf2ee5e7fa5d0ae034ccd5fb50cf8e1f) --- updated-dependencies: - dependency-name: taiki-e/cache-cargo-install-action dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.4 to 1.43.5 (#13467) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.43.4 to 1.43.5. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/78bc6fb2c0d734235d57a2d6b9de923cc325ebdd...57b11c6b7e54c402ccd9cda953f1072ec4f78e33) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 to a135ea602656a8348c5c34887131dd9f7a28bd8c (#13468) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 to a135ea602656a8348c5c34887131dd9f7a28bd8c. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/5adeaaaf36f64df54f62adb34aa5fbfdb0109d34...a135ea602656a8348c5c34887131dd9f7a28bd8c) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: a135ea602656a8348c5c34887131dd9f7a28bd8c dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.4.2 to 2.5.0 (#13469) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.4.2 to 2.5.0. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/5be0e66d93ac7ed76da52eca8bb058f665c3a5fe...a06a81a03ee405af7f2048a818ed3f03bbf83c7b) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * test: mark clone CLI tests as flaky (#13472) These tests hit real Etherscan/Sourcify APIs and fail intermittently due to rate limiting and network issues. They will now run in the nightly flaky test workflow with retries instead of blocking every PR. Amp-Thread-ID: https://ampcode.com/threads/T-019c6840-d225-723a-bf92-46e4e29c7ad1 Co-authored-by: Amp * fix: bind TempDir guard in clone test to prevent premature cleanup (#13471) Amp-Thread-ID: https://ampcode.com/threads/T-019c6840-d225-723a-bf92-46e4e29c7ad1 Co-authored-by: Amp * prevent balance overflow in anvil_addBalance (#13457) Co-authored-by: Matthias Seitz * chore(tests): bump forge-std version (#13482) * move `sccache --show-stats` into build RUN to show actual stats (#13483) * fix(anvil): correct blob_gas_used_ratio calculation in fee history (#13491) Update fees.rs * fix(cheatcodes): make vm.executeTransaction work in isolation mode (#13475) * fix(test): exclude ExecuteTransactionTest from isolation mode vm.executeTransaction already performs its own isolated execution (fresh EVM, cloned state, state merging). When isolation mode is enabled, the inspector's transact_inner intercepts CALLs at depth==1 inside the cheatcode's inner EVM, causing double-isolation that results in 'transaction reverted: 0x'. Amp-Thread-ID: https://ampcode.com/threads/T-019c6ad3-d3f0-70d3-8d78-38ccd8444e9e Co-authored-by: Amp * fix(cheatcodes): make vm.executeTransaction work in isolation mode Two bugs prevented vm.executeTransaction from working with --isolate: 1. Double isolation: executeTransaction creates its own inner EVM at depth=1, but the isolation inspector also intercepts CALLs at depth=1, causing a nested transact_inner. Fix: add set_in_inner_context() to CheatcodesExecutor trait and set it before/after the inner EVM run, matching how transact_inner already handles this. 2. Corrupted cfg env: executeTransaction modified env.cfg (disabled nonce checks, set initcode size limit) but never restored it. Subsequent isolated calls then failed nonce validation (NonceTooHigh). Fix: restore env.cfg from the cached copy alongside env.tx and basefee. Amp-Thread-ID: https://ampcode.com/threads/T-019c6ad3-d3f0-70d3-8d78-38ccd8444e9e Co-authored-by: Amp --------- Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test-utils: remove unused `IS_TTY` helper (#13492) Update util.rs * chore: update LATEST_SOLC to 0.8.34 (#13489) Amp-Thread-ID: https://ampcode.com/threads/T-019c7441-de53-7338-86cb-6d84f755016a Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(anvil): add --max-transactions CLI flag (#13495) * remove unimplemented anvil_enableTraces endpoint (#13499) * feat(fmt): pretty printing for generic block/transaction responses (#13497) feat(fmt): pretty printing for generic block/transaction reponses * Refactor `locked_read_to_string` to reuse `locked_read (#13494) Update fs.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(script-sequence): extract duplicated filter logic (#13500) * chore(broadcast): cleanup avg gas price calculation (#13509) * chore(deps): weekly `cargo update` (#13513) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * feat(common): generic `TransactionReceiptWithRevertReason` + pprinting (#13503) * refactor: Use `fs::write_pretty_json_file` in `MultiChainSequence::save` (#13510) * Update flake.lock (#13511) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/d0555da' (2026-02-14) → 'github:nix-community/fenix/6d86ae5' (2026-02-21) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/bbc84d3' (2026-02-13) → 'github:rust-lang/rust-analyzer/46a214b' (2026-02-20) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/2343bbb' (2026-02-11) → 'github:NixOS/nixpkgs/d1c15b7' (2026-02-16) Co-authored-by: github-actions[bot] * fix(sol-macro-gen): correct identifier check in write_mod_name (#13508) * chore(deps): bump DeterminateSystems/update-flake-lock from a135ea602656a8348c5c34887131dd9f7a28bd8c to 5909792a83875ddb5dd4b18734534a98a74a709c (#13524) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.68.0 to 2.68.8 (#13523) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.2 to 3.16.1 (#13522) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): prevent panic in `utc_from_secs` for out-of-range timestamps (#13520) * ci(release): revert action-gh-release to v2.4.2 (#13527) * ci: ignore softprops/action-gh-release in dependabot (#13528) * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.16.1 to 3.16.3 (#13529) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.16.1 to 3.16.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/681d8e8bfdb5d7af56f113ba2425b1fb00ec9edc...73327eb48f028efaaf5013656ba216ca3cdeca7b) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.16.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(primitives): add `FoundryTransactionBuilder` trait (#13512) Co-authored-by: figtracer <1gusredo@gmail.com> * fix(wallets): use turnkey_unsupported() instead of hardcoded error (#13535) * chore(deps): bump taiki-e/install-action from 2.68.8 to 2.68.9 (#13534) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.68.8 to 2.68.9. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/cfdb446e391c69574ebc316dfb7d7849ec12b940...7f491e26f71f4ec2e6902c7c95c73043f209ab79) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.68.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): preserve original error in update_url (#13531) Co-authored-by: Oliver Nordbjerg * fix: swap incorrect doc comments for archive RPC URL functions in rpc. (#13480) * Allow verifier-url for unknown Etherscan chains (#13079) Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: onbjerg * fix: remove unused `no_storage_caching()` method from `NodeConfig` (#13487) * fix: remove code duplication in `get_runtime_codes` (#13502) * forge: avoid repeated selector decoding in find command (#13516) * fix(cli): Git::is_repo_root always returns false (#13505) * fix(verify): remove unused functions from `VerificationContext` (#13481) Co-authored-by: onbjerg * add missing doc section keys to config validation (#13447) * fix(anvil): use EIP-2718 encoding for OP enveloped_tx (#13537) * fix: correct flag name in error message from --compiler-version to --… (#13539) * chore(ci): unblock ci, fix clippy lint (#13543) * add lint-fix * fix: resolve nightly clippy warnings - collapsible_match: collapse plain if into match guards, allow for if-let - iter_kv_map: use .values()/.keys() instead of .iter().flat_map(|(_, v)| v) - useless_conversion: remove unnecessary .into_iter() Amp-Thread-ID: https://ampcode.com/threads/T-019c9930-51be-760a-b2c7-9a029f851fee Co-authored-by: Amp * fix: add missing match arm for Occupied entry in remappings Amp-Thread-ID: https://ampcode.com/threads/T-019c99a5-39f3-72be-ad16-e7d041662ea9 Co-authored-by: Amp * fix: revert incorrect .values() call on Vec in runner Amp-Thread-ID: https://ampcode.com/threads/T-019c99a5-39f3-72be-ad16-e7d041662ea9 Co-authored-by: Amp * fix: resolve irrefutable let pattern warning in MultiForkHandler Amp-Thread-ID: https://ampcode.com/threads/T-019c99a5-39f3-72be-ad16-e7d041662ea9 Co-authored-by: Amp --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Amp * refactor(wallets): browser wallet generic `Network` (#13550) * fix(anvil): use individual tx gas instead of cumulative in fee history (#13552) * fix(anvil): clear tx pool on anvil_reset (#13544) * add EIP-3860 initcode size check in txpool validation (#13473) * fix: clarify Anvil storage caching builder naming (#13546) Co-authored-by: Amp Co-authored-by: Oliver Nordbjerg * refactor(evm): remove dead BackendError::Other variant (#13553) * fix(script): actually skip Vyper contract verification (#13484) Co-authored-by: onbjerg * feat(doc): Adding new 'Constants' section for constants and immutables (#13116) Co-authored-by: onbjerg Co-authored-by: Oliver Nordbjerg * fix(evm): avoid wrong CowBackend initialization in load_allocs and clone_account (#13554) Co-authored-by: Amp Co-authored-by: Matthias Seitz * chore: use `fs::write_pretty_json_file` in `ScriptSequence::save` (#13562) * fix(forge): apply --access-list in forge create (#13557) Co-authored-by: onbjerg * Update flake.lock (#13564) Co-authored-by: github-actions[bot] Co-authored-by: onbjerg * chore(deps): weekly `cargo update` (#13565) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * fix(cast): correct max_int boundary check for uint255 (#13568) * fix(fmt): correct total_difficulty attribute name (#13578) * chore(evm): use `AnyRpcTransaction` in `configure_tx_env`/`commit_transaction` (#13572) * feat(evm): add `AsEnvMut::set_env` (#13573) * fix(fmt): don't inline while/for/if blocks with multiple statements (#13566) * refactor(cheatcodes,evm): use ContextTr read accessors for env fields (#13582) Co-authored-by: Amp * chore(evm): simplify `CowBackend` init logic by using `Option` (#13584) * feat(anvil): add EIP-2935 blockhash histo… * chore(deps): bump the cargo group across 1 directory with 9 updates (#377) Bumps the cargo group with 3 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing), [keccak](https://github.com/RustCrypto/sponges) and [slab](https://github.com/tokio-rs/slab). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `time` from 0.3.41 to 0.3.47 - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.41...v0.3.47) Updates `alloy-dyn-abi` from 1.3.0 to 1.5.7 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.3.0...v1.5.7) Updates `bytes` from 1.10.1 to 1.11.1 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.1...v1.11.1) Updates `keccak` from 0.1.5 to 0.1.6 - [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6) Updates `lru` from 0.12.5 to 0.16.3 - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.12.5...0.16.3) Updates `protobuf` from 3.3.0 to 3.7.2 Updates `ruint` from 1.15.0 to 1.17.2 - [Release notes](https://github.com/recmo/uint/releases) - [Changelog](https://github.com/recmo/uint/blob/main/CHANGELOG.md) - [Commits](https://github.com/recmo/uint/compare/v1.15.0...v1.17.2) Updates `slab` from 0.4.10 to 0.4.12 - [Release notes](https://github.com/tokio-rs/slab/releases) - [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.12) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: time dependency-version: 0.3.47 dependency-type: direct:production dependency-group: cargo - dependency-name: alloy-dyn-abi dependency-version: 1.5.7 dependency-type: direct:production dependency-group: cargo - dependency-name: bytes dependency-version: 1.11.1 dependency-type: direct:production dependency-group: cargo - dependency-name: keccak dependency-version: 0.1.6 dependency-type: indirect dependency-group: cargo - dependency-name: lru dependency-version: 0.16.3 dependency-type: indirect dependency-group: cargo - dependency-name: protobuf dependency-version: 3.7.2 dependency-type: indirect dependency-group: cargo - dependency-name: ruint dependency-version: 1.17.2 dependency-type: indirect dependency-group: cargo - dependency-name: slab dependency-version: 0.4.12 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create static.yml (#381) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Hardhat project (#389) * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot * foundry-rs#13763 (#398) * fix: update EtherlinkTestnet -> EtherlinkShadownet for alloy-chains v0.2.31 (#13763) Co-authored-by: Matthias Seitz <19890894+mattsse@users.noreply.github.com> * chore(evm): make `FoundryCfg` generic over `Spec` (#13757) * chore(deps): weekly `cargo update` (#13760) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/alloy-rs/evm.git` Updating git repository `https://github.com/foundry-rs/optimism` Updating git submodule `https://github.com/flashbots/op-rbuilder` Updating git submodule `https://github.com/foundry-rs/forge-std` Updating git submodule `https://github.com/runtimeverification/kontrol-cheatcodes` Updating git submodule `https://github.com/ethereum-optimism/lib-keccak` Updating git submodule `https://github.com/dapphub/ds-test` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts` Updating git submodule `https://github.com/a16z/erc4626-tests.git` Updating git submodule `https://github.com/safe-global/safe-contracts` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/transmissions11/solmate` Updating git submodule `https://github.com/ethereum-optimism/superchain-registry` Updating git submodule `https://github.com/flashbots/rollup-boost` Updating git repository `https://github.com/foundry-rs/foundry-fork-db` Updating git repository `https://github.com/paradigmxyz/revm-inspectors.git` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git submodule `https://github.com/foundry-rs/forge-std` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/tempoxyz/tempo-std` Updating git repository `https://github.com/stevencartavia/reth` Locking 31 packages to latest compatible versions Updating alloy-chains v0.2.30 -> v0.2.31 Updating alloy-trie v0.9.4 -> v0.9.5 Adding anstream v1.0.0 Unchanged anstream v0.6.21 (available: v1.0.0) Updating anstyle v1.0.13 -> v1.0.14 Updating anstyle-lossy v1.1.4 -> v1.1.5 Adding anstyle-parse v1.0.0 Updating bon v3.9.0 -> v3.9.1 Updating bon-macros v3.9.0 -> v3.9.1 Updating c-kzg v2.1.6 -> v2.1.7 Updating cc v1.2.56 -> v1.2.57 Updating clap v4.5.60 -> v4.6.0 Updating clap_builder v4.5.60 -> v4.6.0 Updating clap_complete v4.5.66 -> v4.6.0 Updating clap_complete_nushell v4.5.10 -> v4.6.0 Updating clap_derive v4.5.55 -> v4.6.0 Updating clap_lex v1.0.0 -> v1.1.0 Updating colorchoice v1.0.4 -> v1.0.5 Updating console v0.16.2 -> v0.16.3 Removing darling v0.21.3 Removing darling_core v0.21.3 Removing darling_macro v0.21.3 Updating derive-where v1.6.0 -> v1.6.1 Updating evmole v0.8.2 -> v0.8.4 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating kasuari v0.4.11 -> v0.4.12 Unchanged matchit v0.8.4 (available: v0.8.6) Updating once_cell v1.21.3 -> v1.21.4 Unchanged op-revm v15.0.0 (available: v17.0.0) Updating portable-atomic-util v0.2.5 -> v0.2.6 Unchanged quick-junit v0.5.2 (available: v0.6.0) Unchanged rand v0.8.5 (available: v0.10.0) Unchanged rand v0.9.2 (available: v0.10.0) Unchanged revm v34.0.0 (available: v36.0.0) Updating schannel v0.1.28 -> v0.1.29 Updating serde_with v3.17.0 -> v3.18.0 Updating serde_with_macros v3.17.0 -> v3.18.0 Unchanged snapbox v0.6.24 (available: v1.1.0) Unchanged soldeer-commands v0.10.0 (available: v0.10.1) Unchanged soldeer-core v0.10.0 (available: v0.10.1) Unchanged strum v0.27.2 (available: v0.28.0) Updating tempfile v3.26.0 -> v3.27.0 Updating tinyvec v1.10.0 -> v1.11.0 Unchanged toml v0.9.12+spec-1.1.0 (available: v1.0.6+spec-1.1.0) Unchanged toml_edit v0.24.1+spec-1.1.0 (available: v0.25.4+spec-1.1.0) Updating tracing-subscriber v0.3.22 -> v0.3.23 Updating zerocopy v0.8.41 -> v0.8.42 Updating zerocopy-derive v0.8.41 -> v0.8.42 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * feat(cheatcodes): bubble-up `Network` generic to `Wallets` (#13768) * feat(script): generic `TxStatus` receipt type (#13770) feat(script): generic `TxStatus` * chore(script): idiomatic `BroadcastReader::into_tx_receipts` (#13771) * refactor(evm): simplify `Backend::initialize` and `CowBackend::backend_mut` (#13755) `initialize` now takes `(SpecId, Address, TxKind)` instead of `&Env`, since those are the only fields it reads. This removes the need for `backend_mut` to clone `EvmEnv` — it only needs `&TxEnv` now, because the `SpecId` already comes from `self.spec_id`. Moves towards aligning with alloy-evm's BlockExecutor ownership model where `EvmEnv` is block-scoped and `TxEnv` is tx-scoped. * feat(cheatcodes): make `Cheatcodes` context-generic (#13767) * feat(cheatcodes): make `Cheatcodes` context-generic * fix: merge conflict --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): add `--network` flag to `cast tx` for network-specific raw encoding (#13745) * feat(cast): add --network flag to `cast tx` for multi-network raw encoding Replace the `FoundryNetwork`-based `transaction_raw` workaround with proper network selection via a new `--network` / `-n` CLI flag. This moves raw tx encoding back into `Cast::transaction` dispatching to the correct provider (Ethereum, Optimism, or Tempo) based on the flag. - Add `NetworkVariant` enum (Ethereum/Optimism/Tempo) in foundry-cli opts - use it w/ `--network` through CastSubcommand::Tx and provider construction - Add test for Tempo raw tx encoding (`tx_raw_tempo`) * fix: network num args * feat(cast): `block --raw` network selection (#13754) * rebase PR 13745 * feat(cast): `block` for multi-network raw encoding Co-authored-by: figtracer <1gusredo@gmail.com> * fix: doctest --------- Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): make mined_receipts generic (#13761) * chore(evm): split `Executor::env` into `evm_env` and `tx_env` fields (#13773) Split the getters/setters accordingly, and update all call sites. * refactor(cheatcodes): `CheatcodesExecutor` generic (#13774) refactor(cheatcodes): make `CheatcodesExecutor` use generic env types from `ContextTr` Replace concrete `EvmEnv`/`TxEnv` in `with_fresh_nested_evm` with `EvmEnv<::Spec, CTX::Block>` and `CTX::Tx` to support non-Eth networks. Add `FoundryContextTr` trait as fully generic counterpart to the concrete `FoundryContextExt`. Remove unused `clone_to_cfg_env`/`apply_cfg_env` from `FoundryCfg`. * fix(anvil): flaky `test_trace_filter()` (#13764) fix * chore(cast): granular bounds on `Cast` (#13776) * refactor(evm): `FoundryContextExt` generic types (#13778) * fix(cheatcodes): create file in writeJson/writeToml 3-arg overload (#13777) Closes foundry-rs/foundry#13775 Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): make EthApi generic over `N: Network` (#13751) * refactor(anvil): make EthApi generic over N: Network * rm todo comments --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): move `CheatsCtxExt` trait to `foundry-evm-core` (#13781) Previously `CheatsCtxExt` was defined in `foundry-cheatcodes`. Move it to `foundry-evm-core`, and rename it to `EthCheatCtx` to make clear it pins the context to Ethereum-specific env types (`BlockEnv`/`TxEnv`/`CfgEnv`) as a temporary alias during the transition to fully generic EVM and cheatcodes. * refactor(evm): make `NestedEvm` trait generic with associated types (#13782) Add `Tx`, `Block`, and `Spec` associated types to `NestedEvm` so each network can specify its own environment types. The trait remains object-safe when used as `dyn NestedEvm`. Update `NestedEvmClosure` to pin the associated types to Eth types, and set the concrete types in the `FoundryEvm` implementation. * refactor(anvil): propagate `EthApi` to all holders (#13783) refactor(anvil): propagate EthApi to all holders * refactor(evm): rename `NestedEvmClosure` and move to `foundry-evm-core` (#13785) refactor(evm): rename `NestedEvmClosure` to `EthNestedEvmClosure` and move to `foundry-evm-core` * refactor(evm): remove `Env` abstraction from `Executor` impl (#13790) * refactor(anvil): remove redundant param (#13792) * refactor(cheatcodes): tighten verbose bounds to `EthCheatCtx` (#13791) * refactor(evm): remove `eth_*_mut()` from `FoundryContextExt` (#13789) * feat(script): generic `TransactionWithMetadata` + generic pprinting `TransactionMaybeSigned` (#13795) * refactor(evm): `DatabaseExt` generic over env types (#13797) refactor(evm): make DatabaseExt generic over environment types Add generic parameters with defaults to DatabaseExt: `DatabaseExt` This makes the trait generic over EVM environment types while remaining fully backwards-compatible — all existing code uses the defaults. Non-Ethereum networks (e.g. Tempo) can now implement DatabaseExt with their own environment types. 9 method signatures updated: snapshot_state, revert_state, create_select_fork, create_select_fork_at_transaction, select_fork, roll_fork, roll_fork_to_transaction, transact, transact_from_tx. * test(cast): mark flaky revert_reason_from and wildcard RPC-dependent tail (#13796) * fix(anvil): reject invalid versioned_hashes in beacon blobs endpoint (#13787) * fix(cheatcodes): prevent panic in expectRevert with empty bytes (#13769) * fix(cheatcodes): prevent panic in expectRevert with empty bytes When vm.expectRevert(bytes('')) catches a revert with non-empty data, decode_error in alloy-dyn-abi panics on the empty expected_reason (slice index out of range). Guard the decode_error call with a length check. Closes #13766 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test: add regression test for expectRevert empty bytes panic Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test: fix snapshot for expectRevert empty bytes regression test Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): add `DB` associated type to `FoundryJournalExt` (#13799) * refactor(evm): make DatabaseExt generic over environment types Add generic parameters with defaults to DatabaseExt: `DatabaseExt` This makes the trait generic over EVM environment types while remaining fully backwards-compatible — all existing code uses the defaults. Non-Ethereum networks (e.g. Tempo) can now implement DatabaseExt with their own environment types. 9 method signatures updated: snapshot_state, revert_state, create_select_fork, create_select_fork_at_transaction, select_fork, roll_fork, roll_fork_to_transaction, transact, transact_from_tx. * refactor(evm): replace dyn DatabaseExt in FoundryJournalExt with associated type Replace `&mut dyn DatabaseExt` return type in `FoundryJournalExt` with an associated type `type DB: DatabaseExt`. This removes 3 uses of `dyn DatabaseExt` while remaining backwards-compatible — `&mut DB` auto-coerces to `&mut dyn DatabaseExt` at call sites that still need it. `FoundryJournalExt` is never used as a trait object itself, so adding the associated type has no object-safety impact. --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(anvil): add `AnvilBlockExecutor` and `FoundryReceiptBuilder` (#13788) feat(anvil): add AnvilBlockExecutor and FoundryReceiptBuilder * fix(anvil): swap param order in get_next_block_blob_excess_gas to match callers (#13740) * feat(script): `Network`-generic `ScriptSequence` (#13803) * fix(config): add symmetric serialization for FuzzDictionaryConfig usize fields (#13723) * chore(evm): remove `Env::new_with_spec_id()` method (#13806) * fix(install): clean up nested submodules when using --no-git (#13779) * fix(install): clean up nested submodules when using --no-git Fixes #13688 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019cf6ab-6839-70a9-98a9-289974db717b * test: mark regression test as flaky_ instead of ignored Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * style: fix fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019cf6ab-6839-70a9-98a9-289974db717b --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): use associated types in `with_cloned_context` (#13802) refactor(evm): use associated types in `with_cloned_context` closure signature * refactor(evm): propagate env types through `FoundryJournalExt` (#13808) * refactor(evm): simplify `FoundryCfg` to marker trait (#13810) * feat(anvil): add `AnvilBlockExecutorFactory` (#13811) * feat(script): `Network`-generic `ScriptSequenceKind` (#13809) * feature(evm): owned `Tx`/`Evm` getters and `Evm` setter for `FoundryContextExt` (#13812) * chore(evm): remove `Env::{clone_evm_and_tx,apply_evm_and_tx}` methods (#13813) * chore(evm): rename `InspectorExt` to `EthInspectorExt` (#13815) * refactor(evm): add `Fork::backend()` accessor (#13817) * refactor(evm): remove `Env` from `commit_transaction` and `replay_until` (#13816) * feat(script): generic `BundledState` impl (#13825) * chore: bump alloy chains (#13827) * chore(evm): remove `Env` abstraction (#13826) * chore(evm): remove `Env` abstraction completely * fix: nit Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(macros): use correct index for tuple struct fields in ConsoleFmt (#13829) * chore(evm): fix stale `Env` references in doc comments (#13828) The combined `Env` wrapper struct was removed in #13826. Update remaining doc comments that still reference it to use the current `EvmEnv`/`TxEnv` names instead. * refactor(anvil): wire `AnvilBlockExecutorFactory` into `Backend::mine_block` (#13814) refactor(anvil): wire AnvilBlockExecutorFactory into Backend::mine_block * feat(script): generic `ScriptTransactionBuilder` (#13830) Remove hardcoded `Ethereum` default from `TransactionWithMetadata`, making `ScriptTransactionBuilder`, `simulate_and_fill`, and broadcast reader calls `Network`-generic. Cheatcode broadcast helpers now explicitly use Ethereum. * fix(`foundryup`): bump foundryup version (#13832) bump foundryup version * fix(foundryup): tempo-foundry now ships all binaries (#13834) nit * chore(deps): bump taiki-e/install-action from 2.68.17 to 2.68.35 (#13821) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump oven-sh/setup-bun from 2.1.2 to 2.2.0 (#13819) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump Swatinem/rust-cache from 2.8.2 to 2.9.1 (#13818) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.5 to 1.44.0 (#13820) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(evm): backend env helpers generic (#13836) Update `update_env_block` and `update_current_env_with_fork_env` to use generic `FoundryBlock`/`FoundryTransaction` bounds and `BlockHeader` trait methods via setters, replacing direct field access on `AnyRpcBlock`. * refactor(anvil): rm `TransactionExecutor`, mv revm callers to `AnvilBlockExecutorFactory` (#13835) refactor(anvil): rm , mv remai callers AnvilBlockExecutorFactory * refactor(script): extract `BrowserSigner` from `MultiWallet` (#13839) * refactor(anvil): mv `Backend` methods to generic impl, thread N through NodeConfig/spawn (#13840) * refactor(anvil): extract `block_env_from_header` utility (#13838) * chore(evm): clean-up `FoundryEvm` impl (#13844) * feat(cheatcodes): add `currentFilePath` cheatcode (#13735) * feat(cheatcodes): add `currentFilePath` cheatcode Add `vm.currentFilePath()` that returns the source file path of the currently running test or script contract, relative to the project root. This enables contracts to locate sibling files (calldata JSONs, markdown descriptions, configs) without hardcoding paths. A common pattern in proposal/script repos is overriding a `dirPath()` function with a hardcoded string — this cheatcode eliminates that boilerplate. Implementation leverages `CheatsConfig::running_artifact` which already tracks the entry-point `ArtifactId`. The `source` field is stripped of the project root prefix to return a consistent relative path. * fix: rustfmt Amp-Thread-ID: https://ampcode.com/threads/T-019cfb9f-8fce-717d-b9de-fedd8ee7d555 Co-authored-by: Amp * fix: remove view from test functions, fix forge-fmt Amp-Thread-ID: https://ampcode.com/threads/T-019cfba7-4986-77c6-9630-574261e9d580 Co-authored-by: Amp --------- Co-authored-by: Alex Netto Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(evm): simplify nested Evm handling (#13846) refactor(evm): simplify nested EVM handling - Replace `to_env()` (returning cfg+block+tx tuple) with `to_evm_env()` returning only `EvmEnv` (cfg+block), since tx is not needed by callers - Remove unused `with_cloned_context` generic type parameter `R` - Drop `set_tx` call in `with_cloned_context` since nested tx modifications should not propagate back to the outer context - Remove `Evm` useless `db_mut`, `precompiles`, `precompiles_mut`, `inspector`, `inspector_mut` provideds methods reimplementations * refactor(cheatcodes): extract fork env helper to reduce duplication (#13848) Extract `fork_env_op` helper that handles the common pattern shared by all fork-switching cheatcodes: clone EVM/tx env → run db operation → write env back. Deduplicates 7 call sites (rollFork × 4, selectFork, createSelectFork × 2). * refactor(cheatcodes): `BroadcastableTransaction` network-agnostic (#13849) refactor(cheatcodes): make BroadcastableTransaction network-agnostic Remove the `Network` type parameter from `BroadcastableTransaction` by storing raw EVM data (from, to, value, input, nonce, gas) as primitive fields instead of wrapping `TransactionMaybeSigned`. This prevents the `Network` generic from leaking through `Cheatcodes`, `RawCallResult`, and `ScriptResult`. The conversion to network-specific types (`TransactionMaybeSigned`) now happens in the script layer (`simulate.rs`) at the point where transactions are handed off to `ScriptTransactionBuilder`. * feat(evm): generic `NestedEvmClosure` (#13850) refactor(evm): generalize NestedEvmClosure with generic type params Replace `EthNestedEvmClosure` (pinned to Eth types) with generic `NestedEvmClosure` to support non-Eth networks. * feat(evm): `FoundryContextExt` generic impl (#13857) * feat(evm): wire Inspector and DatabaseExt Context generics (#13856) * refactor(anvil): make `PendingTransaction` generic over tx type (#13854) * chore(cheatcodes): remove `Cheatcodes` context generic (#13861) * refactor(evm): delegate to alloy's `EthEvmFactory` in `new_evm_with_inspector` (#13860) * chore: add `id` attributes to issue templates (#13864) * chore: add `id` attributes to issue templates Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0b22-cf39-75b7-b3d7-9280780eecd5 * chore: add `id` attributes to required issue template fields only Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0b22-cf39-75b7-b3d7-9280780eecd5 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * refactor(evm): merge `FoundryJournalExt` into `FoundryContextExt` (#13863) * refactor(evm): merge `FoundryJournalExt` into `FoundryContextExt` * fix: failed merge * fix(traces): fix verbosity trace mode and unify verbosity handling (#13859) * fix(traces): use max instead of min for verbosity trace mode Add regression tests for TraceMode::with_verbosity to ensure verbosity 5 raises the trace mode to at least Steps rather than lowering it. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: James Niken <155266991+dizer-ti@users.noreply.github.com> * fix(traces): unify verbosity handling in TraceMode with_verbosity now raises to RecordStateDiff at verbosity 5, matching the documented behavior of -vvvvv (storage changes + backtraces). This removes the separate with_state_changes(verbosity() > 4) call in trace_mode() which used the global shell verbosity instead of the local evm_opts.verbosity — these two values can diverge when --gas-report or --flamegraph bumps evm_opts.verbosity to 3. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(forge): only show backtraces at verbosity 5 Backtraces require opcode-level step recording which is expensive. Gate backtrace display at verbosity >= 5 (-vvvvv) instead of >= 3, matching the documented behavior and the step recording threshold. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): comprehensive unit tests for TraceMode verbosity levels Tests every verbosity level (0-5) × every TraceMode variant: - verbosity 0-2: noop across all modes - verbosity 3-4: raises to Call, never downgrades - verbosity 5: raises to RecordStateDiff, never downgrades - into_config correctness at each level (record_steps, record_state_diff) - monotonicity invariant: with_verbosity never lowers any mode Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): add integration test for backtrace verbosity levels Runs the same failing test at verbosity 1, 3, 4, and 5 with snapshot assertions to verify backtraces only appear at -vvvvv. Removes the monotonicity unit test in favor of concrete integration coverage. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(traces): keep backtraces at verbosity >= 3, add source locations at 5 Backtraces are useful even without source locations — they show contract/function names at -vvv/-vvvv. Source file locations are only added at -vvvvv when step recording is enabled. Updates integration test to assert all three behaviors: - -vvv: backtrace with function names only - -vvvv: same, plus setup traces - -vvvvv: backtrace with source file:line:col locations Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: fix rustfmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): handle compiler warnings in snapshot Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): add [staticcall] to snapshot for pure function Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * perf(traces): RecordStateDiff should not enable debug-level recording RecordStateDiff now behaves as Steps + state diff, not Debug + state diff. This avoids recording full stack snapshots (memcpy per opcode), memory snapshots, and unfiltered opcode recording at -vvvvv. Before: 50k opcodes → 50k step records with full stack copies (~16MB) After: 50k opcodes → ~500 step records (JUMP/JUMPDEST only), no copies Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): assert Debug mode config is unchanged Locks in that Debug mode still enables full memory/stack snapshots, returndata, immediate bytes, and unfiltered opcode recording — ensuring the RecordStateDiff optimization doesn't affect the debugger or cheatcode recording paths. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(traces): state diff needs unfiltered opcodes for SLOAD/SSTORE record_state_diff captures storage changes in the step() callback, which only fires for recorded opcodes. With a JUMP/JUMPDEST filter, SSTORE steps are skipped and state diffs are lost. RecordStateDiff now disables the opcode filter (like before) but still skips memory/stack snapshots — saving the expensive per-opcode memcpy. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): update JSON fixture for RecordStateDiff config Stack and memory snapshots are no longer recorded at -vvvvv since RecordStateDiff now uses Steps-level config. These fields are null in the JSON output instead of populated. Full debugger-level data is still available via --debug. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> * refactor(anvil): make Block generic over tx type (#13865) * refactor(evm): `FoundryContextExt` bound, use generic `Spec` in `EthCheatCtx` (#13866) * refactor(evm): use `TxEnv` directly in `DatabaseExt` instead of `TransactionRequest` (#13867) * chore(deps): weekly `cargo update` (#13878) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * Update flake.lock (#13877) Co-authored-by: github-actions[bot] * fix(deps): bump to Foundry browser wallet version 0.2.0 (#13890) bump to https://github.com/foundry-rs/foundry-browser-wallet/releases/tag/v0.2.0 * revert: "BroadcastableTransaction network-agnostic" (#13849) (#13891) * perf(anvil): remove redundant clone in create_access_list (#13887) * perf(evm): make `NestedEvm::to_evm_env` consuming to avoid useless clone (#13893) Change `to_evm_env(&self)` to `to_evm_env(self)` so the implementation can use `Evm::finish()` to destructure the EVM without an extra clone of `cfg_env` and `block_env`. Update `with_fresh_nested_evm` to return `EvmEnv` after consuming the EVM post-closure, so callers no longer need to capture the env inside a `NestedEvmClosure` (which only has `&mut dyn NestedEvm` and cannot call a consuming method). Fix the `sub_inner` / `sub_evm_env` ordering in `with_nested_evm` impls so the journal is cloned before `to_evm_env` consumes the EVM. * chore(common): consistency fix on `TransactionMaybeSigned::to()` (#13895) * feat(cheatcodes): Network/Evm generic `Cheatcodes` (#13894) * feat(cheatcodes): Network/Evm generic `Cheatcodes` * fix: tx create detection * fix(ci): adapt to `TransactionMaybeSigned::to()` return type change (#13896) * chore(cheatcodes): remove unused `cheats` param from `CheatcodesExecutor::console_log` (#13897) * refactor(evm): remove `tx_env` param from EVM constructor helper (#13903) Remove the `tx_env` parameter from `new_eth_evm_with_inspector`, `with_cloned_context`, and `with_fresh_nested_evm`. Instead of setting the transaction environment at EVM construction time (where it may become stale or be set redundantly), callers now pass the tx env directly to `evm.transact()` at execution time. This avoids some clones. * chore: remove dead code `paths_config` and `log_status` from inspector stack (#13905) chore: remove dead code in InspectorStackInner Remove two unused methods: - `InspectorStack::paths_config()`: zero callers across the codebase - `InspectorStackInner::log_status()`: zero callers across the codebase Also removes the now-unused `foundry_compilers::ProjectPathsConfig` import. * chore: remove no-op `spec_id` reassignment in `Executor::clone_with_backend` (#13906) chore: remove no-op spec_id reassignment in clone_with_backend After cloning `self.evm_env`, `evm_env.cfg_env.spec` already equals `self.spec_id()` (which simply returns `self.evm_env.cfg_env.spec`). The reassignment is a no-op. * feat(evm): `Backend` generic network (#13579) * feat(evm): `Backend` generic over `Network` * fix(evm): use `AnyNetwork` for fork env init to support non-standard chains When forking non-standard EVM chains (e.g. Moonbeam, Arbitrum), their block headers may lack standard Ethereum fields like `mixHash`. Using the generic `N`-typed provider (defaulting to `Ethereum`) caused deserialization failures for these chains. * feat(evm): add `TryAnyIntoTxEnv` trait for `AnyTxEnvelope` to `TxEnv` conversion Introduces TryAnyIntoTxEnv trait in foundry-evm-core to replace the TxEnv: FromRecoveredTx bound on Backend. This allows Backend to default to AnyNetwork: - TxEnvelope (Ethereum): delegates to FromRecoveredTx directly - AnyTxEnvelope: extracts inner TxEnvelope via as_envelope(), then delegates to FromRecoveredTx; returns error for unknown tx types Co-authored-by: Amp * chore: fix `cargo deny` failure --------- Co-authored-by: Amp * refactor(evm): `NestedEvm` based on revm's `Evm` instead of alloy-evm (#13908) * Add feature: `forge inspect linearization` to see a Solidity contract's linearized inheritance (#13704) * feat: Tempo wallet access key support for cast (#13909) * refactor(script-sequence): rename misleading `opcode` field to `call_kind` (#13907) * fix(fmt): swap valid_before/valid_after in TempoTransaction pretty print (#13910) * fix(fmt): prefer header total_difficulty for totalDifficulty (#13919) * feat(evm): `FoundryContextExt` impls for OP (#13925) * refactor(evm): simplify `NestedEvm` trait by removing `Block` and `Spec` (#13933) * refactor(evm): make `FoundryInspectorExt` generic over `CTX` and rename traits (#13922) * refactor(evm): make `FoundryInspectorExt` generic over `CTX` and rename traits Restructure the inspector extension traits for clarity and genericity: - Rename FoundryInspectorExt -> InspectorExt: the context-free base trait providing Foundry-specific inspector methods (console logging, network config) without any Inspector supertrait. - Rename EthInspectorExt -> FoundryInspectorExt: the combined trait that unifies Inspector + InspectorExt. Generic over any CTX that implements FoundryContextExt, rather than being hardcoded to specific BLOCK/TX/SPEC type parameters with for<'a> HRTB baked in. - Introduce EthEvmCtx<'db> type alias for the concrete Eth context (Context), keeping usage sites concise. This makes the trait hierarchy composable for multi-network support while keeping the Eth-specific default path ergonomic via the type alias during refactor. Co-authored-by: Amp * fix: use `EthEvmContext` instead of EthEvmCtx as default --------- Co-authored-by: Amp * fix(evm): restore `code_size_limit` config in `CfgEnv` (#13912) `cfg_env()` unconditionally set `limit_contract_code_size = Some(usize::MAX)`, ignoring the user's `code_size_limit` setting from foundry.toml. Use the configured value when present, falling back to `usize::MAX` (disabled). * perf(evm): remove unnecessary clones in do_call_end/do_create_end (#13913) perf(evm): remove unnecessary clones in `do_call_end`/`do_create_end` Both methods returned `CallOutcome`/`CreateOutcome` but all callers discarded the return value — the outcome is already mutated in-place via `&mut`. This removes the final `outcome.clone()` and changes the early-return in the `#[ret]` macro from `then_some(outcome.clone())` to `then(|| ())`, eliminating up to 2 clones per call/create end. * fix(wallets): browser wallet CLI help heading formatting (#13876) fix: browser wallet CLI help heading formatting - Set proper help_heading on BrowserWalletOpts to use 'Wallet options - browser wallet' consistently - Add next_help_heading to Erc20TxOpts to prevent transaction options from leaking into the browser wallet section Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(forge-script): Tempo access key support for forge script (#13917) * refactor(anvil): encapsulate per-tx inspector lifecycle in `finish_transaction` (#13932) * refactor(anvil): encapsulate per-tx inspector lifecycle in `finish_transaction` Extract the repeated ~20-line drain-and-reset block from 3 locations in the block mining loop into `AnvilInspector::finish_transaction()`. The method prints traces/logs, drains the tracer into `Vec`, and reinstalls fresh tracer + log collector for the next transaction. Introduces `InspectorTxConfig` to carry the print/tracing settings, replacing direct field access into the inspector internals. * style: fix rustfmt * refactor(evm): generalize `TryAnyToTxEnv` trait over TxEnv + simplification (#13924) * refactor(evm): generalize `TryAnyToTxEnv` trait over TxEnv + simplification The old trait converted `TxEnvelope` (the consensus-layer type) into `TxEnv`, requiring callers to separately pass the sender address. This was a leaky abstraction: the sender is already carried by the RPC transaction wrapper (TransactionResponse::from()), so callers had to extract it themselves before calling `try_into_tx_env`. The new trait `TryAnyToTxEnv` is generic over the output TxEnv type and takes the full RPC transaction as receiver, which means: - Implementations for `alloy_rpc_types::Transaction` and `AnyRpcTransaction` can call ToTxEnv / FromRecoveredTx internally without leaking address handling to callers. - A new impl for `op_alloy_rpc_types::Transaction` produces `OpTransaction`, enabling OP-stack fork replay without a separate code path. - `Backend` now constrains `N::TransactionResponse: TryAnyToTxEnv` instead of `N::TxEnvelope: TryAnyIntoTxEnv`, which is both more accurate and unlocks multi-network support. * fix: docs --------- Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(evm): simplify `replay_until` to use single `ForkDB` clone (#13931) * refactor: simplify replay_until to use single ForkDB clone Replace the per-transaction Backend + EVM creation in replay_until with a single cloned ForkDB and one persistent EthEvm instance. Before: for each tx in the block, commit_transaction would clone Fork + JournaledState, spawn a new Backend (including MultiFork), create a new FoundryEvm, transact, then apply_state_changeset. In a block with N preceding transactions, this meant N full clones. After: clone the fork's CacheDB once (SharedBackend is Arc-backed, so only the local cache layer is duplicated), create one EthEvm via EthEvmFactory, call transact_commit for each tx, then replace the fork's DB and refresh journaled states once at the end. Also: - Remove unused tx_env parameter from replay_until - Extract Fork::refresh_journaled_states helper to deduplicate the paired update_state calls in both replay_until and apply_state_changeset - Remove unused NoOpInspector import Amp-Thread-ID: https://ampcode.com/threads/T-019d2512-8074-72ac-92d8-e8f887911219 Co-authored-by: Amp * style: fix rustfmt * refactor: simplify tx collection loop and remove stale comments --------- Co-authored-by: Amp Co-authored-by: zerosnacks * fix(evm): use try_any_to_tx_env in replay_until (#13938) The merge of #13931 introduced a call to the old `try_into_tx_env` method which was renamed to `try_any_to_tx_env` in #13924. This fixes the docs CI build. Amp-Thread-ID: https://ampcode.com/threads/T-019d2952-9ec2-76f9-8f90-b7b3735d4ce3 Co-authored-by: Amp * chore(deps): bump taiki-e/install-action from 2.68.35 to 2.69.8 (#13915) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.68.35 to 2.69.8. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/94a7388bec5d4c8dd93e3ebf09e0ff448f3f6f4d...7bc99eee1f1b8902a125006cf790a1f4c8461e63) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.69.8 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.17.0 to 3.17.1 (#13914) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/131015bad844610e5e6300f8a143bf625d3e74f4...a18f73c54ca8525de051e73c31512a67f44df919) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.17.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(evm): simplify `FoundryContextExt` spec handling (#13935) - Introduced `FoundryContextExt::Spec` type for convenience - Removed complex bounds on `ContextTr::Cfg` using instead the new `Spec` type - Simplified accordingly the rest of the code Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(anvil): remove some intermediate `Env` wrappers, pass `EvmEnv` directly (#13941) refactor(anvil): remove some intermediate Env wrapper, pass EvmEnv directly Replace usage of the `Env` struct wrapper with direct `EvmEnv` references in `new_eth_evm_with_inspector`, `validate_pool_transaction_for`, and `validate_for`. The optimism flag is now passed explicitly as `is_optimism: bool` instead of being extracted from `env.networks`. This eliminates unnecessary `Env::new(...)` construction in several hot paths (block mining, pending block simulation, tx replay) and simplifies the function signatures. * refactor(anvil): remove Env from storage, call env, and executor APIs (#13942) Continue the cleanup of the Env wrapper by passing EvmEnv directly in remaining call sites: - BlockchainStorage::new / Blockchain::new: drop the redundant spec_id parameter, derive it from evm_env.spec_id() instead - build_call_env: return (EvmEnv, OpTransaction) instead of Env, consumers destructure and use directly - new_eth_evm_with_inspector_ref: accept &EvmEnv instead of &Env, derive is_optimism from self.is_optimism() - build_tx_env_for_pending: take is_optimism: bool instead of NetworkConfigs + &EvmEnv - inspect_tx / replay_block_transactions_with_inspector: extract only evm_env from next_env(), avoid cloning the whole Env - api.rs: use spec_id() / chain_id() helpers instead of digging into env.evm_env.cfg_env fields directly - Various minor cleanups: inline single-use env write guards, use self.chain_id() / self.spec_id() helpers consistently --------- Signed-off-by: dependabot[bot] Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Matthias Seitz <19890894+mattsse@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: Nikki Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Edgar Richards Co-authored-by: Red Swan Co-authored-by: onbjerg Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Cleartext logging of sensitive information' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/anvil/src/eth/backend/executor.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Change branch trigger from 'main' to 'master' (#403) https://github.com/Dargon789/hardhat-project/issues/2073 https://github.com/Dargon789/hardhat-project/pull/2078/commits/a98e69d5803ec208947f592c91749f248e2dd07c Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revise Foundry benchmark results and system info (#408) Updated benchmark results for Foundry versions v1.3.6 and v1.4.0-rc1, including new test data and system information. https://github.com/foundry-rs/foundry/commit/1c4d334e95bc1cad3a390cc735d0f3ec59eea07f Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "benches\LATEST.md (#350)" (#409) This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. * Update Docker.yml * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock (#419) Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from e80a657d7603606be0c69b117cfdc240f1e6af88 to ff43f160ef7014ae1a1fd85699fb6a44f436135b. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/e80a657d7603606be0c69b117cfdc240f1e6af88...ff43f160ef7014ae1a1fd85699fb6a44f436135b) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: ff43f160ef7014ae1a1fd85699fb6a44f436135b dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/config.yml (#420) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory (#421) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "benches\LATEST.md (#350)" (#425) This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. * chore(deps): bump google-github-actions/get-gke-credentials (#417) Bumps [google-github-actions/get-gke-credentials](https://github.com/google-github-actions/get-gke-credentials) from 2.2.1 to 3.0.0. - [Release notes](https://github.com/google-github-actions/get-gke-credentials/releases) - [Changelog](https://github.com/google-github-actions/get-gke-credentials/blob/main/CHANGELOG.md) - [Commits](https://github.com/google-github-actions/get-gke-credentials/compare/6051de21ad50fbb1767bc93c11357a49082ad116...3da1e46a907576cefaa90c484278bb5b259dd395) --- updated-dependencies: - dependency-name: google-github-actions/get-gke-credentials dependency-version: 3.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry (#14347) * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry When an access key is refreshed but not yet provisioned on-chain, the first charge payment strips key_authorization (assuming provisioned), the server rejects with verification-failed, and the retry must obtain a fresh 402 challenge — the server consumes challenge IDs on first use, so reusing the original challenge would fail again. Amp-Thread-ID: https://ampcode.com/threads/T-019d8bde-8cf7-778c-9c9c-727632e62bd4 Co-authored-by: Amp * refactor(mpp): extract select_challenge helper, improve retry robustness - Extract shared select_challenge() for consistent 402 diagnostics on both initial and verification-failed retry paths - Don't restore key_provisioned(true) on non-402 fresh probe responses to avoid bad state on transient server errors - Add rollback_pending() to key-not-provisioned retry to prevent dirty local state from producing wrong credential shapes Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019d9619-6c24-75af-b1e8-7d02d77e0561 --------- Co-authored-by: Amp * refactor(evm): remove useless Eth/Tempo EVM wrapper structs (#14350) * chore(mpp): add built-in rpc url mapping (#14353) add built-in rpc url mapping * fix(evm): skip isolation for CREATE2 factory redirect calls (#14360) * fix(evm): preserve CREATE2 redirect state across isolated transactions (#14363) * feat(init): add `SignatureVerifier` to tempo example (#14351) * feat(init): add `SignatureVerifier` to tempo example * chore: also call `recover()` * style: typo --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(config): validate `optimizer_runs` does not exceed u32::MAX (#14354) * fix(config): validate `optimizer_runs` does not exceed u32::MAX * fix clippy * ci: remove softprops/action-gh-release dependabot ignore (#14364) The draft-finalize race that caused `Too many retries` in matrix jobs (v2.5.0) was fixed in v2.5.2. We're already on v2.6.1, so the ignore rule is no longer needed. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): mine TIP20 virtual address master salt (#14365) * chore(deps): bump actions/github-script from 8.0.0 to 9.0.0 (#14367) * chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (#14368) * chore(deps): bump taiki-e/install-action from 2.75.0 to 2.75.5 (#14369) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.75.0 to 2.75.5. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/cf39a74df4a72510be4e5b63348d61067f11e64a...a2352fc6ce487f030a3aa709482d57823eadfb37) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.75.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump anstream from 0.6.21 to 1.0.0 (#14329) Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.21 to 1.0.0. - [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.21...anstream-v1.0.0) --- updated-dependencies: - dependency-name: anstream dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * Update flake.lock (#14375) Co-authored-by: github-actions[bot] * fix(ci): handle stale branches in bump-tempo workflow (#14377) * fix(config): surface cleanup failures as warnings instead of hard errors (#14379) * fix(config): surface cleanup failures as warnings instead of hard errors Cache cleanup is best-effort by design. Instead of silently swallowing errors or making them fatal, return warnings from Config and emit them via sh_warn! at the CLI layer. All cleanup steps now run even if some fail. Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * fix: address CI - collapse nested ifs, remove disallowed eprintln Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * chore(tempo): bump rev + use extension traits (#14378) * feat(tempo): bump tempo rev and use TempoProviderExt::is_hardfork_active Bumps tempo crates from 6f4f5cc to bb08bb9 (latest main) and replaces the local is_hardfork_active helper with the new trait method on TempoProviderExt, which queries the tempo_forkSchedule RPC directly. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: propagate is_hardfork_active error instead of unwrap_or Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * refactor: use TempoAddressExt in tempo_labels and fee token parsing Replace is_tip20_prefix() with addr.is_tip20() in the TempoLabels inspector and use Address::TIP20_PREFIX instead of hardcoded hex bytes in token_id_to_address. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: enable reth-codec feature on tempo-primitives and fix rustfmt The rev bump split tempo crates into two copies (6f4f5cc for mpp-rs, bb08bb9 for foundry). The bb08bb9 copy lost the reth-codec feature that was previously unified from other workspace members. Explicitly enable it to restore Compact derives on TempoTxEnvelope/TempoHeader. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * chore: remove cargo-update workflow (#14382) Dependabot is now configured for weekly cargo updates, making this workflow redundant. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): return error for eth_getLogs with unknown blockHash instead of empty (#14371) Co-authored-by: Claude Opus 4.7 (1M context) * refactor(cast): cleanup `cast send` (#14385) * refactor(cast): cleanup `cast send` * docs: clarify `TempoNetwork` requirement * style: clippy * fix: review feedback * fix(anvil): refetch full fork blocks with missing tx cache (#14384) * docs: correct Tempo TIP-1022 documentation URL (#14387) * feat(verify): clearer error when `ETHERSCAN_API_KEY` selects etherscan (#14383) * feat: make asm-keccak a default feature in all binaries (#14389) * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(wallets): move `wallets` crate to `foundry-core` (#14348) * fix(cheatcodes): read broadcasts with the active network (#14388) * fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#435) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: googleworkspace-bot * doc-script.js --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: googleworkspace-bot Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: cui Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Louis Peter Sitoe Co-authored-by: o-az --- .cargo/config.toml | 6 +- .codesandbox/tasks.json | 7 + .deps/remix-tests/remix_accounts.sol | 39 + .deps/remix-tests/remix_tests.sol | 225 ++++ .github/ISSUE_TEMPLATE/bug_report.md | 39 + .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/dependabot.yml | 2 - .github/workflows/Docker.yml | 62 ++ .github/workflows/apisec-scan.yml | 29 + .github/workflows/benchmarks.yml | 6 +- .github/workflows/bump-tempo.yml | 11 +- .github/workflows/codeql.yml | 92 ++ .github/workflows/crate-checks.yml | 2 +- .github/workflows/dependencies.yml | 19 - .github/workflows/deploy.yml | 27 + .github/workflows/docker.yml | 62 ++ .github/workflows/google.yml | 117 +++ .github/workflows/npm.yml | 37 + .github/workflows/release.yml | 8 +- .github/workflows/snyk-container.yml | 55 + .github/workflows/static.yml | 43 + .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 2 +- .gitmodules | 6 + Cargo.lock | 40 +- Cargo.toml | 29 +- benches/src/lib.rs | 16 +- counter/.github/workflows/test.yml | 43 + counter/.gitignore | 14 + counter/README.md | 66 ++ counter/foundry.toml | 6 + counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 + counter/src/Counter.sol | 14 + counter/test/Counter.t.sol | 24 + crates/anvil/Cargo.toml | 4 +- crates/anvil/core/src/eth/block.rs | 45 +- crates/anvil/core/src/eth/transaction/mod.rs | 22 +- crates/anvil/src/cmd.rs | 17 +- crates/anvil/src/config.rs | 231 ++--- crates/anvil/src/eth/backend/db.rs | 99 +- crates/anvil/src/eth/backend/fork.rs | 17 +- crates/anvil/src/eth/backend/info.rs | 15 +- crates/anvil/src/eth/backend/mem/mod.rs | 2 +- crates/anvil/src/eth/error.rs | 7 + crates/anvil/src/eth/pool/transactions.rs | 50 +- crates/anvil/src/filter.rs | 113 +- crates/anvil/src/pubsub.rs | 52 +- crates/anvil/src/server/beacon/handlers.rs | 42 +- crates/anvil/src/server/beacon/mod.rs | 12 +- crates/anvil/src/server/rpc_handlers.rs | 11 +- crates/anvil/src/service.rs | 71 +- crates/anvil/tests/it/fork.rs | 25 + crates/anvil/tests/it/logs.rs | 18 + crates/cast/Cargo.toml | 4 +- crates/cast/src/call_spec.rs | 34 + crates/cast/src/cmd/access_list.rs | 17 +- crates/cast/src/cmd/batch_mktx.rs | 45 +- crates/cast/src/cmd/batch_send.rs | 73 +- crates/cast/src/cmd/create2.rs | 92 +- crates/cast/src/cmd/erc20.rs | 130 +-- crates/cast/src/cmd/keychain.rs | 64 +- crates/cast/src/cmd/miner.rs | 52 + crates/cast/src/cmd/mod.rs | 1 + crates/cast/src/cmd/send.rs | 65 +- crates/cast/src/cmd/tip20.rs | 243 ----- crates/cast/src/cmd/tip20/create.rs | 113 ++ crates/cast/src/cmd/tip20/mine.rs | 249 +++++ crates/cast/src/cmd/tip20/mod.rs | 111 ++ crates/cast/src/lib.rs | 568 +++++----- crates/cast/src/tempo.rs | 21 + crates/cast/src/tx.rs | 53 +- crates/cast/tests/cli/main.rs | 28 + .../cheatcodes/assets/cheatcodes.schema.json | 2 +- crates/cheatcodes/spec/src/lib.rs | 4 +- crates/cheatcodes/src/config.rs | 3 + crates/cheatcodes/src/evm/fork.rs | 234 +++-- crates/cheatcodes/src/fs.rs | 122 ++- crates/cheatcodes/src/lib.rs | 31 +- crates/cheatcodes/src/test.rs | 52 +- crates/cheatcodes/src/utils.rs | 98 +- crates/chisel/Cargo.toml | 2 +- crates/chisel/src/executor.rs | 46 +- crates/cli/Cargo.toml | 2 + crates/cli/src/opts/network.rs | 16 - crates/cli/src/utils/suggestions.rs | 3 +- crates/cli/src/utils/tempo.rs | 5 +- crates/common/Cargo.toml | 1 + crates/common/fmt/src/ui.rs | 383 ++++--- crates/common/src/contracts.rs | 2 +- crates/common/src/provider/mpp/transport.rs | 340 +++--- crates/common/src/transactions.rs | 281 +++++ crates/config/Cargo.toml | 4 + crates/config/assets/config.schema.json | 6 + crates/config/spec/Cargo.toml | 28 + crates/config/spec/src/lib.rs | 64 ++ crates/config/src/endpoints.rs | 12 + crates/config/src/lib.rs | 130 ++- crates/doc/src/parser/comment.rs | 6 + crates/doc/src/writer/buf_writer.rs | 20 +- crates/evm/core/Cargo.toml | 1 - crates/evm/core/src/backend/cow.rs | 163 +-- crates/evm/core/src/backend/mod.rs | 676 ++++++------ crates/evm/core/src/evm/eth.rs | 146 +-- crates/evm/core/src/evm/mod.rs | 45 +- crates/evm/core/src/evm/op.rs | 52 +- crates/evm/core/src/evm/tempo.rs | 161 +-- crates/evm/core/src/tempo.rs | 17 +- crates/evm/core/src/utils.rs | 46 +- crates/evm/evm/Cargo.toml | 2 + crates/evm/evm/src/executors/builder.rs | 71 +- crates/evm/evm/src/executors/trace.rs | 79 +- crates/evm/evm/src/inspectors/stack.rs | 5 + crates/evm/evm/src/inspectors/tempo_labels.rs | 6 +- crates/evm/traces/src/lib.rs | 136 +-- crates/forge/Cargo.toml | 5 +- crates/forge/assets/tempo/MailTemplate.sol | 53 + crates/forge/assets/tempo/MailTemplate.t.sol | 121 ++- crates/forge/src/args.rs | 6 +- crates/forge/src/cmd/cache.rs | 21 +- crates/forge/src/cmd/config.rs | 2 +- crates/forge/src/cmd/inspect.rs | 175 +--- crates/forge/src/cmd/install.rs | 49 +- crates/forge/src/cmd/test/mod.rs | 159 +-- crates/forge/src/multi_runner.rs | 107 +- crates/forge/tests/cli/install.rs | 45 - crates/forge/tests/cli/test_optimizer.rs | 1 - crates/lint/src/linter.rs | 129 +++ crates/script-sequence/src/transaction.rs | 35 +- crates/script/Cargo.toml | 1 + crates/script/src/build.rs | 113 +- crates/script/src/execute.rs | 87 +- crates/script/src/lib.rs | 242 +---- crates/script/src/receipts.rs | 37 +- crates/script/src/runner.rs | 92 +- crates/script/src/sequence.rs | 58 +- crates/script/src/simulate.rs | 86 +- crates/script/src/verify.rs | 41 +- crates/test-utils/src/script.rs | 29 +- crates/test-utils/src/util.rs | 104 +- crates/verify/src/provider.rs | 4 + crates/verify/src/utils.rs | 140 ++- crates/verify/src/verify.rs | 44 +- crates/wallets/Cargo.toml | 69 -- crates/wallets/src/error.rs | 75 -- crates/wallets/src/lib.rs | 31 - crates/wallets/src/opts.rs | 330 ------ crates/wallets/src/signer.rs | 348 ------- crates/wallets/src/tempo.rs | 160 --- crates/wallets/src/utils.rs | 164 --- .../src/wallet_browser/app/assets/banner.png | Bin 559043 -> 0 bytes .../src/wallet_browser/app/assets/index.html | 14 - .../src/wallet_browser/app/assets/logo.png | Bin 49040 -> 0 bytes .../src/wallet_browser/app/assets/main.js | 71 -- .../src/wallet_browser/app/assets/styles.css | 2 - crates/wallets/src/wallet_browser/app/mod.rs | 7 - crates/wallets/src/wallet_browser/error.rs | 28 - crates/wallets/src/wallet_browser/handlers.rs | 195 ---- crates/wallets/src/wallet_browser/mod.rs | 976 ------------------ crates/wallets/src/wallet_browser/opts.rs | 50 - crates/wallets/src/wallet_browser/queue.rs | 83 -- crates/wallets/src/wallet_browser/router.rs | 106 -- crates/wallets/src/wallet_browser/server.rs | 218 ---- crates/wallets/src/wallet_browser/signer.rs | 103 -- crates/wallets/src/wallet_browser/state.rs | 139 --- crates/wallets/src/wallet_browser/types.rs | 127 --- crates/wallets/src/wallet_multi/mod.rs | 621 ----------- crates/wallets/src/wallet_raw/mod.rs | 62 -- deny.toml | 1 + doc/doc-filelist.js | 1 + doc/doc-script.js | 228 ++++ doc/doc-style.css | 403 ++++++++ flake.lock | 18 +- npm/scripts/stage-from-artifact.mjs | 28 +- npm/src/const.mjs | 30 +- 178 files changed, 6161 insertions(+), 8035 deletions(-) create mode 100644 .codesandbox/tasks.json create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/Docker.yml create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/codeql.yml delete mode 100644 .github/workflows/dependencies.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/google.yml create mode 100644 .github/workflows/snyk-container.yml create mode 100644 .github/workflows/static.yml create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol create mode 100644 crates/cast/src/cmd/miner.rs delete mode 100644 crates/cast/src/cmd/tip20.rs create mode 100644 crates/cast/src/cmd/tip20/create.rs create mode 100644 crates/cast/src/cmd/tip20/mine.rs create mode 100644 crates/cast/src/cmd/tip20/mod.rs create mode 100644 crates/cast/src/tempo.rs create mode 100644 crates/common/src/transactions.rs create mode 100644 crates/config/assets/config.schema.json create mode 100644 crates/config/spec/Cargo.toml create mode 100644 crates/config/spec/src/lib.rs create mode 100644 crates/lint/src/linter.rs delete mode 100644 crates/wallets/Cargo.toml delete mode 100644 crates/wallets/src/error.rs delete mode 100644 crates/wallets/src/lib.rs delete mode 100644 crates/wallets/src/opts.rs delete mode 100644 crates/wallets/src/signer.rs delete mode 100644 crates/wallets/src/tempo.rs delete mode 100644 crates/wallets/src/utils.rs delete mode 100644 crates/wallets/src/wallet_browser/app/assets/banner.png delete mode 100644 crates/wallets/src/wallet_browser/app/assets/index.html delete mode 100644 crates/wallets/src/wallet_browser/app/assets/logo.png delete mode 100644 crates/wallets/src/wallet_browser/app/assets/main.js delete mode 100644 crates/wallets/src/wallet_browser/app/assets/styles.css delete mode 100644 crates/wallets/src/wallet_browser/app/mod.rs delete mode 100644 crates/wallets/src/wallet_browser/error.rs delete mode 100644 crates/wallets/src/wallet_browser/handlers.rs delete mode 100644 crates/wallets/src/wallet_browser/mod.rs delete mode 100644 crates/wallets/src/wallet_browser/opts.rs delete mode 100644 crates/wallets/src/wallet_browser/queue.rs delete mode 100644 crates/wallets/src/wallet_browser/router.rs delete mode 100644 crates/wallets/src/wallet_browser/server.rs delete mode 100644 crates/wallets/src/wallet_browser/signer.rs delete mode 100644 crates/wallets/src/wallet_browser/state.rs delete mode 100644 crates/wallets/src/wallet_browser/types.rs delete mode 100644 crates/wallets/src/wallet_multi/mod.rs delete mode 100644 crates/wallets/src/wallet_raw/mod.rs create mode 100644 doc/doc-filelist.js create mode 100644 doc/doc-script.js create mode 100644 doc/doc-style.css diff --git a/.cargo/config.toml b/.cargo/config.toml index 1ca035a75d78c..ca844fb33e15b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,12 @@ [alias] -cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-config = "test -p foundry-config-spec --features schema tests::" test-debugger = "test -p forge --test cli manual_debug_setup -- --include-ignored --nocapture" bless-lints = "test -p forge --test ui -- --bless" +# Backwards compatibility alias for `spec-cheats` +cheats = "spec-cheats" + # Increase the stack size to 10MB for Windows targets, which is in line with Linux # (whereas default for Windows is 1MB). [target.x86_64-pc-windows-msvc] diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..53a505774ac88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a5a1184e9ac0f..1cd9930619634 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,8 +6,6 @@ updates: interval: "weekly" cooldown: default-days: 7 - ignore: - - dependency-name: "softprops/action-gh-release" - package-ecosystem: "cargo" directory: "/" diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/Docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index bebe02d5c50d4..96af1f4ab9a1e 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -131,7 +131,7 @@ jobs: run: ./.github/scripts/commit-and-read-benchmarks.sh benches "${{ github.event_name }}" "${{ github.repository }}" - name: Upload benchmark results as artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: benchmark-results path: | @@ -172,7 +172,7 @@ jobs: - name: Create PR for manual runs if: github.event_name == 'workflow_dispatch' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const branchName = '${{ needs.run-benchmarks.outputs.branch_name }}'; @@ -200,7 +200,7 @@ jobs: - name: Comment on PR if: github.event.inputs.pr_number != '' || github.event_name == 'pull_request' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const prNumber = ${{ github.event.inputs.pr_number || github.event.pull_request.number }}; diff --git a/.github/workflows/bump-tempo.yml b/.github/workflows/bump-tempo.yml index e8f0ed8e4533f..ffbd68d772d81 100644 --- a/.github/workflows/bump-tempo.yml +++ b/.github/workflows/bump-tempo.yml @@ -34,15 +34,22 @@ jobs: run: | BRANCH_NAME="deps/bump-tempo-${LATEST_REV:0:7}" + # Skip if a PR already exists for this branch + EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number') + if [[ -n "$EXISTING_PR" ]]; then + echo "PR #${EXISTING_PR} already exists for ${BRANCH_NAME}, skipping." + exit 0 + fi + # Configure git git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - # Create and push branch + # Create and push branch (force-push in case a stale branch exists from a prior failed run) git checkout -b "$BRANCH_NAME" git add Cargo.toml Cargo.lock git commit -m "chore(deps): bump tempo dependencies to ${LATEST_REV:0:7}" - git push origin "$BRANCH_NAME" + git push --force-with-lease origin "$BRANCH_NAME" # Create PR gh pr create \ diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index 4567d8da156e0..781d63759045e 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -29,7 +29,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@cf39a74df4a72510be4e5b63348d61067f11e64a # v2.75.0 + - uses: taiki-e/install-action@a2352fc6ce487f030a3aa709482d57823eadfb37 # v2.75.16 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml deleted file mode 100644 index 374b1781cac98..0000000000000 --- a/.github/workflows/dependencies.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Runs `cargo update` periodically. - -name: dependencies - -permissions: {} - -on: - schedule: - - cron: "0 0 * * SUN" # Run weekly on Sundays at midnight UTC - workflow_dispatch: # Needed so we can run it manually - -jobs: - update: - uses: tempoxyz/ci/.github/workflows/cargo-update-pr.yml@268b3ce142717ff86c58fbbcc3abc3f109f0fb8d # main - permissions: - contents: write - pull-requests: write - secrets: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..569c7b00a4b7f --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@3da1e46a907576cefaa90c484278bb5b259dd395' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 9b9b798435c90..007ef5f4286aa 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -233,9 +233,46 @@ jobs: PLATFORM='${{ matrix.os }}' ARCH='${{ matrix.arch }}' + # Basic validation of matrix-derived values to avoid path manipulation + case "$TOOL" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid TOOL value: $TOOL" >&2; exit 1 ;; + esac + case "$PLATFORM" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid PLATFORM value: $PLATFORM" >&2; exit 1 ;; + esac + case "$ARCH" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid ARCH value: $ARCH" >&2; exit 1 ;; + esac + PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" + echo "Preparing to publish package from: $PACKAGE_DIR" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 + exit 1 + fi + + # Resolve to an absolute path and ensure it stays within ./@foundry-rs + ABS_PACKAGE_DIR="$(realpath "$PACKAGE_DIR")" + ABS_EXPECTED_ROOT="$(realpath "./@foundry-rs")" + case "$ABS_PACKAGE_DIR" in + "$ABS_EXPECTED_ROOT"/*) ;; + *) + echo "ERROR: Resolved package directory is outside expected root:" >&2 + echo " ABS_PACKAGE_DIR=$ABS_PACKAGE_DIR" >&2 + echo " ABS_EXPECTED_ROOT=$ABS_EXPECTED_ROOT" >&2 + exit 1 + ;; + esac + ls -la "$PACKAGE_DIR" + # Minimal sanity check: require a package.json before publishing + if [[ ! -f "$PACKAGE_DIR/package.json" ]]; then + echo "ERROR: package.json not found in $PACKAGE_DIR; refusing to publish." >&2 + exit 1 + fi + bun ./scripts/publish.mjs "$PACKAGE_DIR" echo "Published @foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15bb33f30f01b..2ad324e7ef9c1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,7 +58,7 @@ jobs: # the changelog. - name: Create build-specific nightly tag if: ${{ env.IS_NIGHTLY == 'true' }} - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: TAG_NAME: ${{ steps.release_info.outputs.tag_name }} with: @@ -232,7 +232,7 @@ jobs: printf "foundry_attestation=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.attestation.txt" >> "$GITHUB_OUTPUT" - name: Upload build artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: retention-days: 1 name: ${{ steps.artifacts.outputs.file_name }} @@ -321,14 +321,14 @@ jobs: # Moves the `nightly` tag to `HEAD` - name: Move nightly tag if: ${{ env.IS_NIGHTLY == 'true' }} - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const moveTag = require('./.github/scripts/move-tag.js') await moveTag({ github, context }, 'nightly') - name: Delete old nightlies - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const prunePrereleases = require('./.github/scripts/prune-prereleases.js') diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000000000..0ba82305f82b2 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index 6b32d5172c357..af4415b1e8ba1 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -33,7 +33,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@cf39a74df4a72510be4e5b63348d61067f11e64a # v2.75.0 + - uses: taiki-e/install-action@a2352fc6ce487f030a3aa709482d57823eadfb37 # v2.75.16 with: tool: nextest - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index 982b470064582..afd91d3d72c05 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -37,7 +37,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@cf39a74df4a72510be4e5b63348d61067f11e64a # v2.75.0 + - uses: taiki-e/install-action@a2352fc6ce487f030a3aa709482d57823eadfb37 # v2.75.16 with: tool: nextest - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d6f2ccdf56bc1..f9ccb850dbc5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,7 +73,7 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: taiki-e/install-action@cf39a74df4a72510be4e5b63348d61067f11e64a # v2.75.0 + - uses: taiki-e/install-action@a2352fc6ce487f030a3aa709482d57823eadfb37 # v2.75.16 with: tool: nextest diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/Cargo.lock b/Cargo.lock index 9adfcb569b8bd..d15f7c092e642 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4954,6 +4954,7 @@ dependencies = [ "strsim", "strum", "tempfile", + "tempo-primitives", "tikv-jemallocator", "tokio", "tracing", @@ -4993,7 +4994,7 @@ dependencies = [ "alloy-transport", "alloy-transport-ipc", "alloy-transport-ws", - "anstream 0.6.21", + "anstream 1.0.0", "anstyle", "axum", "chrono", @@ -5248,6 +5249,7 @@ dependencies = [ "serde", "serde_json", "tempo-precompiles", + "tempo-primitives", "thiserror 2.0.18", "tokio", "tracing", @@ -5308,7 +5310,6 @@ dependencies = [ "serde", "serde_json", "tempo-alloy", - "tempo-chainspec", "tempo-contracts", "tempo-evm", "tempo-precompiles", @@ -5542,7 +5543,8 @@ dependencies = [ [[package]] name = "foundry-wallets" -version = "1.6.0" +version = "0.1.0" +source = "git+https://github.com/foundry-rs/foundry-core?rev=7f401c1397af90a0a94ef7424a48bbf3dc0248cc#7f401c1397af90a0a94ef7424a48bbf3dc0248cc" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -5565,9 +5567,6 @@ dependencies = [ "dirs", "eth-keystore", "eyre", - "foundry-common", - "foundry-config", - "reqwest 0.13.2", "rpassword", "serde", "serde_json", @@ -11296,7 +11295,7 @@ dependencies = [ [[package]] name = "tempo-alloy" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", "alloy-contract", @@ -11314,6 +11313,7 @@ dependencies = [ "derive_more", "futures", "serde", + "tempo-chainspec", "tempo-contracts", "tempo-primitives", "tracing", @@ -11322,7 +11322,7 @@ dependencies = [ [[package]] name = "tempo-chainspec" version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-eips 2.0.0", "alloy-evm", @@ -11340,8 +11340,8 @@ dependencies = [ [[package]] name = "tempo-consensus" -version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +version = "1.6.0" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11357,7 +11357,7 @@ dependencies = [ [[package]] name = "tempo-contracts" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-contract", "alloy-primitives", @@ -11367,8 +11367,8 @@ dependencies = [ [[package]] name = "tempo-evm" -version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +version = "1.6.0" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11394,8 +11394,8 @@ dependencies = [ [[package]] name = "tempo-precompiles" -version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +version = "1.6.0" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy", "alloy-evm", @@ -11414,8 +11414,8 @@ dependencies = [ [[package]] name = "tempo-precompiles-macros" -version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +version = "1.6.0" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy", "proc-macro2", @@ -11426,7 +11426,7 @@ dependencies = [ [[package]] name = "tempo-primitives" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", "alloy-eips 2.0.0", @@ -11456,8 +11456,8 @@ dependencies = [ [[package]] name = "tempo-revm" -version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=39b9262#39b92627745eb1b0dbb99650d990ef9168376ded" +version = "1.6.0" +source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", "alloy-evm", diff --git a/Cargo.toml b/Cargo.toml index f83c8594b4a89..70d6eba6a6aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/cli/", "crates/common/", "crates/config/", + "crates/config/spec/", "crates/debugger/", "crates/doc/", "crates/evm/core/", @@ -326,6 +327,7 @@ foundry-cli-markdown = { path = "crates/cli-markdown" } foundry-common = { path = "crates/common" } foundry-common-fmt = { path = "crates/common/fmt" } foundry-config = { path = "crates/config" } +foundry-config-spec = { path = "crates/config/spec" } foundry-debugger = { path = "crates/debugger" } foundry-evm = { path = "crates/evm/evm" } foundry-evm-abi = { path = "crates/evm/abi" } @@ -338,7 +340,6 @@ foundry-evm-sancov = { path = "crates/evm/sancov" } 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" } foundry-primitives = { path = "crates/primitives" } @@ -355,6 +356,9 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features "rustls", ] } +## foundry-core +foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "7f401c1397af90a0a94ef7424a48bbf3dc0248cc", default-features = false } + ## alloy alloy-consensus = { version = "2.0.0", default-features = false } alloy-contract = { version = "2.0.0", default-features = false } @@ -419,7 +423,7 @@ revm-inspectors = { version = "0.38.1", features = ["serde"] } op-revm = { version = "18.0.0", default-features = false } ## cli -anstream = "0.6" +anstream = "1.0" anstyle = "1.0" dialoguer = { version = "0.12", default-features = false, features = [ "password", @@ -513,15 +517,16 @@ mpp = { git = "https://github.com/tempoxyz/mpp-rs", rev = "310c9a1f3fe485fa9c7a8 "client", "reqwest-rustls-tls", ] } -tempo-chainspec = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262", default-features = false } -tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262", default-features = false, features = [ +tempo-chainspec = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9", default-features = false } +tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9", default-features = false, features = [ "serde", + "reth-codec", ] } -tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262", default-features = false } -tempo-evm = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262", default-features = false } -tempo-revm = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262", default-features = false, features = ["serde"] } -tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262" } -tempo-precompiles = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262" } +tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9", default-features = false } +tempo-evm = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9", default-features = false } +tempo-revm = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9", default-features = false, features = ["serde"] } +tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9" } +tempo-precompiles = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9" } ## Pinned dependencies. Enabled for the workspace in crates/test-utils. @@ -608,9 +613,9 @@ alloy-op-hardforks = { git = "https://github.com/foundry-rs/optimism", branch = # foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "b139c57c2b54bc06a9e4c9783941f5bbd4bd3a1f" } ## tempo — unify crates.io versions (pulled by mpp) with git rev -tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262" } -tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262" } -tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "39b9262" } +tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9" } +tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9" } +tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "bb08bb9" } # solar solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ca429168c2095..ab3bec614e3cd 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -130,10 +130,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 0d0111c8f5636..b664266450d07 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -124,8 +124,8 @@ op-alloy-rpc-types.workspace = true tempo-alloy.workspace = true [features] -default = ["cli", "jemalloc"] -asm-keccak = ["alloy-primitives/asm-keccak"] +default = ["cli", "jemalloc", "asm-keccak"] +asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] tracy-allocator = ["foundry-cli/tracy-allocator"] diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 60a51e7dda28b..6460971d0d2fb 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -4,33 +4,32 @@ use alloy_consensus::{ }; use alloy_eips::eip2718::Encodable2718; use alloy_network::Network; -use foundry_primitives::FoundryTxEnvelope; +use foundry_primitives::FoundryNetwork; +use std::fmt::Debug; use crate::eth::transaction::MaybeImpersonatedTransaction; -/// Type alias for a block containing potentially impersonated transactions. -pub type Block = alloy_consensus::Block>; +/// Type alias for Ethereum Block with Anvil's transaction type +pub type Block = alloy_consensus::Block; + +/// Anvil's concrete block info type. +pub type BlockInfo = TypedBlockInfo; /// Container type that gathers all block data, generic over a [`Network`]. #[derive(Clone, Debug)] -pub struct BlockInfo { - pub block: Block, +pub struct TypedBlockInfo { + pub block: alloy_consensus::Block>, pub transactions: Vec, pub receipts: Vec, } -/// Helper function to create a new block with Header and Anvil transactions, generic over the -/// transaction envelope with a default of [`FoundryTxEnvelope`]. +/// Helper function to create a new block with Header and Anvil transactions /// /// Note: if the `impersonate-tx` feature is enabled this will also accept /// `MaybeImpersonatedTransaction`. -pub fn create_block( - mut header: Header, - transactions: impl IntoIterator, -) -> Block +pub fn create_block(mut header: Header, transactions: impl IntoIterator) -> Block where - Tx: Encodable2718, - T: Into>, + T: Into, { let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect(); let transactions_root = calculate_transaction_root(&transactions); @@ -42,6 +41,24 @@ where Block::new(header, body) } +/// Generic helper function to create a block with any transaction type that supports encoding. +pub fn create_typed_block( + mut header: Header, + transactions: impl IntoIterator, +) -> alloy_consensus::Block +where + T: Encodable2718, +{ + let transactions: Vec<_> = transactions.into_iter().collect(); + let transactions_root = calculate_transaction_root(&transactions); + + header.transactions_root = transactions_root; + header.ommers_hash = EMPTY_OMMER_ROOT_HASH; + + let body = BlockBody { transactions, ommers: Vec::new(), withdrawals: None }; + alloy_consensus::Block::new(header, body) +} + #[cfg(test)] mod tests { use alloy_primitives::{ @@ -202,7 +219,7 @@ mod tests { let data = hex::decode("f9034df90348a0fbdbd8d2d0ac5f14bd5fa90e547fe6f1d15019c724f8e7b60972d381cd5d9cf8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c9577e7945db22e38fc060909f2278c7746b0f9ba05017cfa3b0247e35197215ae8d610265ffebc8edca8ea66d6567eb0adecda867a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018355bb7b871fffffffffffff808462bd0e1ab9014bf90148a00000000000000000000000000000000000000000000000000000000000000000f85494319fa8f1bc4e53410e92d10d918659b16540e60a945a573efb304d04c1224cd012313e827eca5dce5d94a9c831c5a268031176ebf5f3de5051e8cba0dbfe94c9577e7945db22e38fc060909f2278c7746b0f9b808400000000f8c9b841a6946f2d16f68338cbcbd8b117374ab421128ce422467088456bceba9d70c34106128e6d4564659cf6776c08a4186063c0a05f7cffd695c10cf26a6f301b67f800b8412b782100c18c35102dc0a37ece1a152544f04ad7dc1868d18a9570f744ace60870f822f53d35e89a2ea9709ccbf1f4a25ee5003944faa845d02dde0a41d5704601b841d53caebd6c8a82456e85c2806a9e08381f959a31fb94a77e58f00e38ad97b2e0355b8519ab2122662cbe022f2a4ef7ff16adc0b2d5dcd123181ec79705116db300a063746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365880000000000000000c0c0").unwrap(); - let block = ::decode(&mut data.as_slice()).unwrap(); + let block = Block::decode(&mut data.as_slice()).unwrap(); // encode and check that it matches the original data let mut encoded = Vec::new(); diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 056d7a9602855..02bcc749bcb82 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -20,7 +20,7 @@ use std::ops::Deref; /// This is a helper that carries the `impersonated` sender so that the right hash /// can be created. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct MaybeImpersonatedTransaction { +pub struct MaybeImpersonatedTransaction { transaction: T, impersonated_sender: Option
, } @@ -33,17 +33,17 @@ impl Typed2718 for MaybeImpersonatedTransaction { impl MaybeImpersonatedTransaction { /// Creates a new wrapper for the given transaction - pub const fn new(transaction: T) -> Self { + pub fn new(transaction: T) -> Self { Self { transaction, impersonated_sender: None } } /// Creates a new impersonated transaction wrapper using the given sender - pub const fn impersonated(transaction: T, impersonated_sender: Address) -> Self { + pub fn impersonated(transaction: T, impersonated_sender: Address) -> Self { Self { transaction, impersonated_sender: Some(impersonated_sender) } } /// Returns whether the transaction is impersonated - pub const fn is_impersonated(&self) -> bool { + pub fn is_impersonated(&self) -> bool { self.impersonated_sender.is_some() } @@ -93,14 +93,14 @@ impl Encodable for MaybeImpersonatedTransaction { } } -impl From> for FoundryTxEnvelope { - fn from(value: MaybeImpersonatedTransaction) -> Self { +impl From for FoundryTxEnvelope { + fn from(value: MaybeImpersonatedTransaction) -> Self { value.transaction } } -impl From for MaybeImpersonatedTransaction { - fn from(value: T) -> Self { +impl From for MaybeImpersonatedTransaction { + fn from(value: FoundryTxEnvelope) -> Self { Self::new(value) } } @@ -127,7 +127,7 @@ impl Deref for MaybeImpersonatedTransaction { /// Queued transaction #[derive(Clone, Debug, PartialEq, Eq)] -pub struct PendingTransaction { +pub struct PendingTransaction { /// The actual transaction pub transaction: MaybeImpersonatedTransaction, /// the recovered sender of this transaction @@ -137,11 +137,11 @@ pub struct PendingTransaction { } impl PendingTransaction { - pub const fn hash(&self) -> &TxHash { + pub fn hash(&self) -> &TxHash { &self.hash } - pub const fn sender(&self) -> &Address { + pub fn sender(&self) -> &Address { &self.sender } } diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 68bd83a1e6e89..9dac717e1f8a2 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -4,7 +4,6 @@ use crate::{ eth::{EthApi, backend::db::SerializableState, pool::transactions::TransactionOrder}, }; use alloy_genesis::Genesis; -use alloy_network::Network; use alloy_primitives::{B256, U256, utils::Unit}; use alloy_signer_local::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; @@ -14,7 +13,6 @@ use foundry_common::shell; use foundry_config::{Chain, Config, FigmentProviders}; use foundry_evm::hardfork::{EthereumHardfork, OpHardfork}; use foundry_evm_networks::NetworkConfigs; -use foundry_primitives::FoundryReceiptEnvelope; use futures::FutureExt; use rand_08::{SeedableRng, rngs::StdRng}; use std::{ @@ -29,7 +27,6 @@ use std::{ task::{Context, Poll}, time::Duration, }; -use tempo_chainspec::hardfork::TempoHardfork; use tokio::time::{Instant, Interval}; #[derive(Clone, Debug, Parser)] @@ -231,8 +228,6 @@ impl NodeArgs { Some(hf) => { if self.evm.networks.is_optimism() { Some(OpHardfork::from_str(hf)?.into()) - } else if self.evm.networks.is_tempo() { - Some(TempoHardfork::from_str(hf)?.into()) } else { Some(EthereumHardfork::from_str(hf)?.into()) } @@ -642,17 +637,17 @@ impl AnvilEvmArgs { } /// Helper type to periodically dump the state of the chain to disk -struct PeriodicStateDumper { +struct PeriodicStateDumper { in_progress_dump: Option + Send + Sync + 'static>>>, - api: EthApi, + api: EthApi, dump_state: Option, preserve_historical_states: bool, interval: Interval, } -impl> PeriodicStateDumper { +impl PeriodicStateDumper { fn new( - api: EthApi, + api: EthApi, dump_state: Option, interval: Duration, preserve_historical_states: bool, @@ -676,7 +671,7 @@ impl> PeriodicStateDumper, dump_state: PathBuf, preserve_historical_states: bool) { + async fn dump_state(api: EthApi, dump_state: PathBuf, preserve_historical_states: bool) { trace!(path=?dump_state, "Dumping state on shutdown"); match api.serialized_state(preserve_historical_states).await { Ok(state) => { @@ -694,7 +689,7 @@ impl> PeriodicStateDumper> Future for PeriodicStateDumper { +impl Future for PeriodicStateDumper { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index c9b63f08cb2f5..56a5e4ddcfa81 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -3,6 +3,7 @@ use crate::{ eth::{ backend::{ db::{Db, SerializableState}, + env::Env, fork::{ClientFork, ClientForkConfig}, genesis::GenesisConfig, mem::fork_db::ForkedDatabase, @@ -37,18 +38,19 @@ use foundry_config::Config; use foundry_evm::{ backend::{BlockchainDb, BlockchainDbMeta, SharedBackend}, constants::DEFAULT_CREATE2_DEPLOYER, - hardfork::{FoundryHardfork, OpHardfork}, - utils::{ - apply_chain_and_block_specific_env_changes, block_env_from_header, - get_blob_base_fee_update_fraction, + hardfork::{ + FoundryHardfork, OpHardfork, ethereum_hardfork_from_block_tag, + spec_id_from_ethereum_hardfork, }, + utils::{apply_chain_and_block_specific_env_changes, get_blob_base_fee_update_fraction}, }; -use foundry_primitives::FoundryTxEnvelope; +use foundry_primitives::FoundryNetwork; use itertools::Itertools; +use op_revm::OpTransaction; use parking_lot::RwLock; use rand_08::thread_rng; use revm::{ - context::{BlockEnv, CfgEnv}, + context::{BlockEnv, CfgEnv, TxEnv}, context_interface::block::BlobExcessGasAndPrice, primitives::hardfork::SpecId, }; @@ -60,7 +62,6 @@ use std::{ sync::Arc, time::Duration, }; -use tempo_chainspec::hardfork::TempoHardfork; use tokio::sync::RwLock as TokioRwLock; use yansi::Paint; @@ -424,12 +425,6 @@ impl NodeConfig { Self { enable_tracing: true, port: 0, silent: true, ..Default::default() } } - /// Returns a test config with Tempo network enabled. - #[doc(hidden)] - pub fn test_tempo() -> Self { - Self { networks: NetworkConfigs::with_tempo(), ..Self::test() } - } - /// Returns a new config which does not initialize any accounts on node startup. pub fn empty_state() -> Self { Self { @@ -511,35 +506,20 @@ impl Default for NodeConfig { impl NodeConfig { /// Returns the memory limit of the node #[must_use] - pub const fn with_memory_limit(mut self, mems_value: Option) -> Self { + pub fn with_memory_limit(mut self, mems_value: Option) -> Self { self.memory_limit = mems_value; self } - - /// Returns the base fee to use. - /// - /// In Tempo mode, uses the hardfork-specific base fee (10 gwei pre-T1, 20 gwei T1+). + /// Returns the base fee to use pub fn get_base_fee(&self) -> u64 { - let default = if self.networks.is_tempo() { - TempoHardfork::from(self.get_hardfork()).base_fee() - } else { - INITIAL_BASE_FEE - }; self.base_fee .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64))) - .unwrap_or(default) + .unwrap_or(INITIAL_BASE_FEE) } - /// Returns the gas price to use. - /// - /// In Tempo mode, defaults to the hardfork-specific base fee. + /// Returns the base fee to use pub fn get_gas_price(&self) -> u128 { - let default = if self.networks.is_tempo() { - TempoHardfork::from(self.get_hardfork()).base_fee() as u128 - } else { - INITIAL_GAS_PRICE - }; - self.gas_price.unwrap_or(default) + self.gas_price.unwrap_or(INITIAL_GAS_PRICE) } pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice { @@ -571,21 +551,18 @@ impl NodeConfig { if self.networks.is_optimism() { return OpHardfork::default().into(); } - if self.networks.is_tempo() { - return TempoHardfork::default().into(); - } EthereumHardfork::default().into() } /// Sets a custom code size limit #[must_use] - pub const fn with_code_size_limit(mut self, code_size_limit: Option) -> Self { + pub fn with_code_size_limit(mut self, code_size_limit: Option) -> Self { self.code_size_limit = code_size_limit; self } /// Disables code size limit #[must_use] - pub const fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { + pub fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { if disable_code_size_limit { self.code_size_limit = Some(usize::MAX); } @@ -636,7 +613,7 @@ impl NodeConfig { /// Sets the gas limit #[must_use] - pub const fn with_gas_limit(mut self, gas_limit: Option) -> Self { + pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { self.gas_limit = gas_limit; self } @@ -645,7 +622,7 @@ impl NodeConfig { /// /// If set to `true` block gas limit will not be enforced #[must_use] - pub const fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self { + pub fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self { self.disable_block_gas_limit = disable_block_gas_limit; self } @@ -654,14 +631,14 @@ impl NodeConfig { /// /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825) #[must_use] - pub const fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self { + pub fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self { self.enable_tx_gas_limit = enable_tx_gas_limit; self } /// Sets the gas price #[must_use] - pub const fn with_gas_price(mut self, gas_price: Option) -> Self { + pub fn with_gas_price(mut self, gas_price: Option) -> Self { self.gas_price = gas_price; self } @@ -685,7 +662,7 @@ impl NodeConfig { /// Sets the max number of transactions in a block #[must_use] - pub const fn with_max_transactions(mut self, max_transactions: Option) -> Self { + pub fn with_max_transactions(mut self, max_transactions: Option) -> Self { if let Some(max_transactions) = max_transactions { self.max_transactions = max_transactions; } @@ -704,14 +681,14 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub const fn with_base_fee(mut self, base_fee: Option) -> Self { + pub fn with_base_fee(mut self, base_fee: Option) -> Self { self.base_fee = base_fee; self } /// Disable the enforcement of a minimum suggested priority fee #[must_use] - pub const fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { + pub fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { self.disable_min_priority_fee = disable_min_priority_fee; self } @@ -757,7 +734,7 @@ impl NodeConfig { /// Sets the hardfork #[must_use] - pub const fn with_hardfork(mut self, hardfork: Option) -> Self { + pub fn with_hardfork(mut self, hardfork: Option) -> Self { self.hardfork = hardfork; self } @@ -811,21 +788,21 @@ impl NodeConfig { /// If set to `true` auto mining will be disabled #[must_use] - pub const fn with_no_mining(mut self, no_mining: bool) -> Self { + pub fn with_no_mining(mut self, no_mining: bool) -> Self { self.no_mining = no_mining; self } /// Sets the slots in an epoch #[must_use] - pub const fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self { + 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 const fn with_port(mut self, port: u16) -> Self { + pub fn with_port(mut self, port: u16) -> Self { self.port = port; self } @@ -850,7 +827,7 @@ impl NodeConfig { } #[must_use] - pub const fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { + pub fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { self.no_storage_caching = no_storage_caching; self } @@ -886,7 +863,7 @@ impl NodeConfig { /// Sets the `fork_chain_id` to use to fork off local cache from #[must_use] - pub const fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { + pub fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { self.fork_chain_id = fork_chain_id; self } @@ -900,7 +877,7 @@ impl NodeConfig { /// Sets the `fork_request_timeout` to use for requests #[must_use] - pub const fn fork_request_timeout(mut self, fork_request_timeout: Option) -> Self { + pub fn fork_request_timeout(mut self, fork_request_timeout: Option) -> Self { if let Some(fork_request_timeout) = fork_request_timeout { self.fork_request_timeout = fork_request_timeout; } @@ -909,7 +886,7 @@ impl NodeConfig { /// Sets the `fork_request_retries` to use for spurious networks #[must_use] - pub const fn fork_request_retries(mut self, fork_request_retries: Option) -> Self { + pub fn fork_request_retries(mut self, fork_request_retries: Option) -> Self { if let Some(fork_request_retries) = fork_request_retries { self.fork_request_retries = fork_request_retries; } @@ -918,7 +895,7 @@ impl NodeConfig { /// Sets the initial `fork_retry_backoff` for rate limits #[must_use] - pub const fn fork_retry_backoff(mut self, fork_retry_backoff: Option) -> Self { + pub fn fork_retry_backoff(mut self, fork_retry_backoff: Option) -> Self { if let Some(fork_retry_backoff) = fork_retry_backoff { self.fork_retry_backoff = fork_retry_backoff; } @@ -929,10 +906,7 @@ impl NodeConfig { /// /// See also, #[must_use] - pub const fn fork_compute_units_per_second( - mut self, - compute_units_per_second: Option, - ) -> Self { + pub fn fork_compute_units_per_second(mut self, compute_units_per_second: Option) -> Self { if let Some(compute_units_per_second) = compute_units_per_second { self.compute_units_per_second = compute_units_per_second; } @@ -941,35 +915,35 @@ impl NodeConfig { /// Sets whether to enable tracing #[must_use] - pub const fn with_tracing(mut self, enable_tracing: bool) -> Self { + pub fn with_tracing(mut self, enable_tracing: bool) -> Self { self.enable_tracing = enable_tracing; self } /// Sets whether to enable steps tracing #[must_use] - pub const fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self { + pub fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self { self.enable_steps_tracing = enable_steps_tracing; self } /// Sets whether to print `console.log` invocations to stdout. #[must_use] - pub const fn with_print_logs(mut self, print_logs: bool) -> Self { + pub fn with_print_logs(mut self, print_logs: bool) -> Self { self.print_logs = print_logs; self } /// Sets whether to print traces to stdout. #[must_use] - pub const fn with_print_traces(mut self, print_traces: bool) -> Self { + pub fn with_print_traces(mut self, print_traces: bool) -> Self { self.print_traces = print_traces; self } /// Sets whether to enable autoImpersonate #[must_use] - pub const fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self { + pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self { self.enable_auto_impersonate = enable_auto_impersonate; self } @@ -988,7 +962,7 @@ impl NodeConfig { } #[must_use] - pub const fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self { + pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self { self.transaction_order = transaction_order; self } @@ -1027,14 +1001,14 @@ impl NodeConfig { /// Sets whether to disable the default create2 deployer #[must_use] - pub const fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { + pub fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { self.disable_default_create2_deployer = yes; self } /// Sets whether to disable pool balance checks #[must_use] - pub const fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self { + pub fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self { self.disable_pool_balance_checks = yes; self } @@ -1048,33 +1022,19 @@ impl NodeConfig { /// Enable features for provided networks. #[must_use] - pub const fn with_networks(mut self, networks: NetworkConfigs) -> Self { + pub fn with_networks(mut self, networks: NetworkConfigs) -> Self { self.networks = networks; self } - /// Enable Tempo network features. - #[must_use] - pub fn with_tempo(mut self) -> Self { - self.networks = NetworkConfigs::with_tempo(); - self - } - - /// Enable Optimism network features. - #[must_use] - pub fn with_optimism(mut self) -> Self { - self.networks = NetworkConfigs::with_optimism(); - self - } - /// Makes the node silent to not emit anything on stdout #[must_use] - pub const fn silent(self) -> Self { + pub fn silent(self) -> Self { self.set_silent(true) } #[must_use] - pub const fn set_silent(mut self, silent: bool) -> Self { + pub fn set_silent(mut self, silent: bool) -> Self { self.silent = silent; self } @@ -1093,13 +1053,7 @@ impl NodeConfig { /// [Backend](mem::Backend) /// /// *Note*: only memory based backend for now - pub(crate) async fn setup(&mut self) -> Result> - where - N: alloy_network::Network< - TxEnvelope = foundry_primitives::FoundryTxEnvelope, - ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope, - >, - { + pub(crate) async fn setup(&mut self) -> Result> { // configure the revm environment let mut cfg = CfgEnv::default(); @@ -1122,13 +1076,20 @@ impl NodeConfig { } let spec_id = cfg.spec; - let mut evm_env = EvmEnv::new( - cfg, - BlockEnv { - gas_limit: self.gas_limit(), - basefee: self.get_base_fee(), + let mut env = Env::new( + EvmEnv::new( + cfg, + BlockEnv { + gas_limit: self.gas_limit(), + basefee: self.get_base_fee(), + ..Default::default() + }, + ), + OpTransaction { + base: TxEnv { chain_id: Some(self.get_chain_id()), ..Default::default() }, ..Default::default() }, + self.networks, ); let base_fee_params: BaseFeeParams = @@ -1146,7 +1107,7 @@ impl NodeConfig { let (db, fork): (Arc>>, Option) = if let Some(eth_rpc_url) = self.eth_rpc_url.clone() { - self.setup_fork_db(eth_rpc_url, &mut evm_env, &fees).await? + self.setup_fork_db(eth_rpc_url, &mut env, &fees).await? } else { (Arc::new(TokioRwLock::new(Box::::default())), None) }; @@ -1156,16 +1117,16 @@ impl NodeConfig { // --chain-id flag gets precedence over the genesis.json chain id // if self.chain_id.is_none() { - evm_env.cfg_env.chain_id = genesis.config.chain_id; + env.evm_env.cfg_env.chain_id = genesis.config.chain_id; } - evm_env.block_env.timestamp = U256::from(genesis.timestamp); + env.evm_env.block_env.timestamp = U256::from(genesis.timestamp); if let Some(base_fee) = genesis.base_fee_per_gas { - evm_env.block_env.basefee = base_fee.try_into()?; + env.evm_env.block_env.basefee = base_fee.try_into()?; } if let Some(number) = genesis.number { - evm_env.block_env.number = U256::from(number); + env.evm_env.block_env.number = U256::from(number); } - evm_env.block_env.beneficiary = genesis.coinbase; + env.evm_env.block_env.beneficiary = genesis.coinbase; } let genesis = GenesisConfig { @@ -1188,8 +1149,7 @@ impl NodeConfig { // only memory based backend for now let backend = mem::Backend::with_genesis( db, - Arc::new(RwLock::new(evm_env)), - self.networks, + Arc::new(RwLock::new(env)), genesis, fees, Arc::new(RwLock::new(fork)), @@ -1215,6 +1175,10 @@ impl NodeConfig { .wrap_err("failed to create default create2 deployer")?; } + if let Some(state) = self.init_state.clone() { + backend.load_state(state).await.wrap_err("failed to load init state")?; + } + Ok(backend) } @@ -1227,10 +1191,10 @@ impl NodeConfig { pub async fn setup_fork_db( &mut self, eth_rpc_url: String, - evm_env: &mut EvmEnv, + env: &mut Env, fees: &FeeManager, ) -> Result<(Arc>>, Option)> { - let (db, config) = self.setup_fork_db_config(eth_rpc_url, evm_env, fees).await?; + let (db, config) = self.setup_fork_db_config(eth_rpc_url, env, fees).await?; let db: Arc>> = Arc::new(TokioRwLock::new(Box::new(db))); let fork = ClientFork::new(config, Arc::clone(&db)); Ok((db, Some(fork))) @@ -1244,7 +1208,7 @@ impl NodeConfig { pub async fn setup_fork_db_config( &mut self, eth_rpc_url: String, - evm_env: &mut EvmEnv, + env: &mut Env, fees: &FeeManager, ) -> Result<(ForkedDatabase, ClientForkConfig)> { debug!(target: "node", ?eth_rpc_url, "setting up fork db"); @@ -1269,8 +1233,16 @@ impl NodeConfig { let chain_id = if let Some(chain_id) = self.fork_chain_id { Some(chain_id) } else if self.hardfork.is_none() { + // Auto-adjust hardfork if not specified, but only if we're forking mainnet. let chain_id = provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?; + if alloy_chains::NamedChain::Mainnet == chain_id { + let hardfork: EthereumHardfork = + ethereum_hardfork_from_block_tag(fork_block_number); + + env.evm_env.cfg_env.spec = spec_id_from_ethereum_hardfork(hardfork); + self.hardfork = Some(FoundryHardfork::Ethereum(hardfork)); + } Some(U256::from(chain_id)) } else { None @@ -1312,12 +1284,17 @@ latest block number: {latest_block}" let gas_limit = self.fork_gas_limit(&block); self.gas_limit = Some(gas_limit); - evm_env.block_env = BlockEnv { + env.evm_env.block_env = BlockEnv { + number: U256::from(fork_block_number), + 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, // Keep previous `coinbase` and `basefee` value - beneficiary: evm_env.block_env.beneficiary, - basefee: evm_env.block_env.basefee, - ..block_env_from_header(&block.header) + beneficiary: env.evm_env.block_env.beneficiary, + basefee: env.evm_env.block_env.basefee, + ..Default::default() }; // Determine chain_id early so we can use it consistently @@ -1332,25 +1309,17 @@ latest block number: {latest_block}" // need to update the dev signers and env with the chain id self.set_chain_id(Some(chain_id)); - evm_env.cfg_env.chain_id = chain_id; + env.evm_env.cfg_env.chain_id = chain_id; + env.tx.base.chain_id = chain_id.into(); chain_id }; - // Auto-detect hardfork from chain activation data if not explicitly set. - if self.hardfork.is_none() - && let Some(hardfork) = - FoundryHardfork::from_chain_and_timestamp(chain_id, block.header.timestamp()) - { - evm_env.cfg_env.spec = SpecId::from(hardfork); - self.hardfork = Some(hardfork); - } - // if not set explicitly we use the base fee of the latest block if self.base_fee.is_none() && let Some(base_fee) = block.header.base_fee_per_gas() { self.base_fee = Some(base_fee); - evm_env.block_env.basefee = base_fee; + env.evm_env.block_env.basefee = 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( @@ -1369,7 +1338,7 @@ latest block number: {latest_block}" // Derive blob params using the fork block timestamp regardless of explicit base fee. let blob_params = get_blob_params(chain_id, block.header.timestamp()); - evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( + env.evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( blob_excess_gas, blob_params.update_fraction as u64, )); @@ -1396,13 +1365,13 @@ latest block number: {latest_block}" 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::( - evm_env, + apply_chain_and_block_specific_env_changes::( + &mut env.evm_env, &block, self.networks, ); - let meta = BlockchainDbMeta::new(evm_env.block_env.clone(), eth_rpc_url.clone()); + let meta = BlockchainDbMeta::new(env.evm_env.block_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 { @@ -1434,7 +1403,7 @@ latest block number: {latest_block}" 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().map(|g| g as u128), - blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price, + blob_excess_gas_and_price: env.evm_env.block_env.blob_excess_gas_and_price, force_transactions, }; @@ -1482,7 +1451,7 @@ latest block number: {latest_block}" async fn derive_block_and_transactions( fork_choice: &ForkChoice, provider: &Arc, -) -> eyre::Result<(BlockNumber, Option>>)> { +) -> eyre::Result<(BlockNumber, Option>)> { match fork_choice { ForkChoice::Block(block_number) => { let block_number = *block_number; @@ -1544,7 +1513,7 @@ pub enum ForkChoice { impl ForkChoice { /// Returns the block number to fork from - pub const fn block_number(&self) -> Option { + pub fn block_number(&self) -> Option { match self { Self::Block(block_number) => Some(*block_number), Self::Transaction(_) => None, @@ -1552,7 +1521,7 @@ impl ForkChoice { } /// Returns the transaction hash to fork from - pub const fn transaction_hash(&self) -> Option { + pub fn transaction_hash(&self) -> Option { match self { Self::Block(_) => None, Self::Transaction(transaction_hash) => Some(*transaction_hash), @@ -1582,12 +1551,12 @@ pub struct PruneStateHistoryConfig { impl PruneStateHistoryConfig { /// Returns `true` if writing state history is supported - pub const fn is_state_history_supported(&self) -> bool { + pub fn is_state_history_supported(&self) -> bool { !self.enabled || self.max_memory_history.is_some() } /// Returns true if this setting was enabled. - pub const fn is_config_enabled(&self) -> bool { + pub fn is_config_enabled(&self) -> bool { self.enabled } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index de9ad434252f3..14691862a225e 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -8,7 +8,6 @@ use std::{ use alloy_consensus::{BlockBody, Header}; use alloy_eips::eip4895::Withdrawals; -use alloy_network::Network; use alloy_primitives::{ Address, B256, Bytes, U256, keccak256, map::{AddressMap, HashMap}, @@ -22,7 +21,7 @@ use foundry_common::errors::FsPathError; use foundry_evm::backend::{ BlockchainDb, DatabaseError, DatabaseResult, MemDb, RevertStateSnapshotAction, StateSnapshot, }; -use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope}; +use foundry_primitives::{FoundryNetwork, FoundryReceiptEnvelope, FoundryTxEnvelope}; use revm::{ Database, DatabaseCommit, bytecode::Bytecode, @@ -91,79 +90,6 @@ pub trait MaybeForkedDatabase { fn maybe_inner(&self) -> Result<&BlockchainDb, String>; } -/// `dyn Db` satisfies all `alloy_evm::Database` requirements via its supertraits, but the -/// blanket impl has an implicit `Sized` bound. Provide an explicit impl. -impl alloy_evm::Database for dyn Db {} - -/// A wrapper around [`CacheDB`]. -#[derive(Debug)] -pub struct AnvilCacheDB(pub CacheDB); - -impl> AnvilCacheDB { - pub fn new(inner: T) -> Self { - Self(CacheDB::new(inner)) - } -} - -impl> std::ops::Deref for AnvilCacheDB { - type Target = CacheDB; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl> std::ops::DerefMut for AnvilCacheDB { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl + fmt::Debug> Database for AnvilCacheDB { - type Error = DatabaseError; - - fn basic(&mut self, address: Address) -> Result, Self::Error> { - self.0.basic(address) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.0.code_by_hash(code_hash) - } - - fn storage(&mut self, address: Address, index: U256) -> Result { - self.0.storage(address, index) - } - - fn block_hash(&mut self, number: u64) -> Result { - self.0.block_hash(number) - } -} - -impl> DatabaseRef for AnvilCacheDB { - type Error = DatabaseError; - - fn basic_ref(&self, address: Address) -> Result, Self::Error> { - self.0.basic_ref(address) - } - - fn code_by_hash_ref(&self, code_hash: B256) -> Result { - self.0.code_by_hash_ref(code_hash) - } - - fn storage_ref(&self, address: Address, index: U256) -> Result { - self.0.storage_ref(address, index) - } - - fn block_hash_ref(&self, number: u64) -> Result { - self.0.block_hash_ref(number) - } -} - -impl + fmt::Debug> DatabaseCommit for AnvilCacheDB { - fn commit(&mut self, changes: revm::state::EvmState) { - self.0.commit(changes) - } -} - /// This bundles all required revm traits pub trait Db: DatabaseRef @@ -226,7 +152,7 @@ pub trait Db: /// Deserialize and add all chain data to the backend storage fn load_state(&mut self, state: SerializableState) -> DatabaseResult { - for (addr, account) in state.accounts { + for (addr, account) in state.accounts.into_iter() { let old_account_nonce = DatabaseRef::basic_ref(self, addr) .ok() .and_then(|acc| acc.map(|acc| acc.nonce)) @@ -250,7 +176,7 @@ pub trait Db: }, ); - for (k, v) in account.storage { + for (k, v) in account.storage.into_iter() { self.set_storage_at(addr, k, v)?; } } @@ -504,7 +430,6 @@ impl TryFrom for BlockEnv { basefee: legacy.basefee.and_then(|v| v.to_u64()).unwrap_or(0), difficulty: legacy.difficulty.and_then(|v| v.to_u256()).unwrap_or(U256::ZERO), prevrandao: legacy.prevrandao.or(Some(B256::ZERO)), - slot_num: 0, blob_excess_gas_and_price: legacy .blob_excess_gas_and_price .map(|v| BlobExcessGasAndPrice::new(v.excess_blob_gas, v.blob_gasprice)) @@ -651,7 +576,7 @@ where #[serde(untagged)] pub enum SerializableTransactionType { TypedTransaction(FoundryTxEnvelope), - MaybeImpersonatedTransaction(MaybeImpersonatedTransaction), + MaybeImpersonatedTransaction(MaybeImpersonatedTransaction), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -683,13 +608,13 @@ impl From for Block { } } -impl From> for SerializableTransactionType { - fn from(transaction: MaybeImpersonatedTransaction) -> Self { +impl From for SerializableTransactionType { + fn from(transaction: MaybeImpersonatedTransaction) -> Self { Self::MaybeImpersonatedTransaction(transaction) } } -impl From for MaybeImpersonatedTransaction { +impl From for MaybeImpersonatedTransaction { fn from(transaction: SerializableTransactionType) -> Self { match transaction { SerializableTransactionType::TypedTransaction(tx) => Self::new(tx), @@ -706,10 +631,8 @@ pub struct SerializableTransaction { pub block_number: u64, } -impl> From> - for SerializableTransaction -{ - fn from(transaction: MinedTransaction) -> Self { +impl From> for SerializableTransaction { + fn from(transaction: MinedTransaction) -> Self { Self { info: transaction.info, receipt: transaction.receipt, @@ -719,9 +642,7 @@ impl> From> From - for MinedTransaction -{ +impl From for MinedTransaction { fn from(transaction: SerializableTransaction) -> Self { Self { info: transaction.info, diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index bd3d1292f01b5..b1bff4e66cd21 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -442,8 +442,10 @@ impl ClientFork { &self, hash: B256, ) -> Result, TransportError> { - if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { - return Ok(Some(self.convert_to_full_block(block))); + if let Some(block) = self.storage_read().blocks.get(&hash).cloned() + && let Some(block) = self.convert_to_full_block(block) + { + return Ok(Some(block)); } self.fetch_full_block(hash).await } @@ -479,8 +481,9 @@ impl ClientFork { .get(&block_number) .copied() .and_then(|hash| self.storage_read().blocks.get(&hash).cloned()) + && let Some(block) = self.convert_to_full_block(block) { - return Ok(Some(self.convert_to_full_block(block))); + return Ok(Some(block)); } self.fetch_full_block(block_number).await @@ -509,15 +512,15 @@ impl ClientFork { } /// Converts a block of hashes into a full block - fn convert_to_full_block(&self, mut block: N::BlockResponse) -> N::BlockResponse { + fn convert_to_full_block(&self, mut block: N::BlockResponse) -> Option { let storage = self.storage.read(); let transactions = block .transactions() .hashes() - .filter_map(|hash| storage.transactions.get(&hash).cloned()) - .collect(); + .map(|hash| storage.transactions.get(&hash).cloned()) + .collect::>>()?; *block.transactions_mut() = BlockTransactions::Full(transactions); - block + Some(block) } } diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index c739cd26d1de6..72acc5e62a39e 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -1,10 +1,10 @@ //! Handler that can get current storage related data use crate::mem::Backend; -use alloy_consensus::TxReceipt; use alloy_network::{AnyRpcBlock, Network}; use alloy_primitives::B256; use anvil_core::eth::block::Block; +use foundry_primitives::{FoundryNetwork, FoundryReceiptEnvelope}; use std::{fmt, sync::Arc}; /// A type that can fetch data related to the ethereum storage. @@ -18,7 +18,7 @@ pub struct StorageInfo { } impl StorageInfo { - pub(crate) const fn new(backend: Arc>) -> Self { + pub(crate) fn new(backend: Arc>) -> Self { Self { backend } } @@ -39,17 +39,16 @@ impl StorageInfo { } } -impl StorageInfo -where - N::ReceiptEnvelope: TxReceipt, -{ +impl StorageInfo { + // TODO: receipts methods require N::ReceiptEnvelope generalization + /// Returns the receipts of the current block - pub fn current_receipts(&self) -> Option> { + pub fn current_receipts(&self) -> Option> { self.backend.mined_receipts(self.backend.best_hash()) } /// Returns the receipts of the block with the given hash - pub fn receipts(&self, hash: B256) -> Option> { + pub fn receipts(&self, hash: B256) -> Option> { self.backend.mined_receipts(hash) } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 991d48c7fd6a6..ed928ef25213d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2371,7 +2371,7 @@ where return Ok(fork.logs(&filter).await?); } - Ok(Vec::new()) + Err(BlockchainError::UnknownBlock) } /// Returns the logs that match the filter in the given range of blocks diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index d693590ab0b41..3b2ada43d7731 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -73,6 +73,8 @@ pub enum BlockchainError { BlockOutOfRange(u64, u64), #[error("Resource not found")] BlockNotFound, + #[error("unknown block")] + UnknownBlock, /// Thrown when a requested transaction is not found #[error("transaction not found")] TransactionNotFound, @@ -649,6 +651,11 @@ impl ToRpcResponseResult for Result { message: "filter not found".into(), data: None, }, + err @ BlockchainError::UnknownBlock => RpcError { + code: ErrorCode::ServerError(-32000), + message: err.to_string().into(), + data: None, + }, } .into(), } diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index df65822e1eab3..81059c7b48d20 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,16 +1,12 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; -use alloy_consensus::{ - Transaction, Typed2718, - crypto::RecoveryError, - transaction::{SignerRecoverable, TxHashRef}, -}; +use alloy_consensus::{Transaction, Typed2718}; use alloy_network::AnyRpcTransaction; use alloy_primitives::{ Address, TxHash, map::{HashMap, HashSet}, }; -use alloy_rlp::Encodable; use anvil_core::eth::transaction::PendingTransaction; +use foundry_primitives::FoundryTxEnvelope; use parking_lot::RwLock; use std::{cmp::Ordering, collections::BTreeSet, fmt, str::FromStr, sync::Arc, time::Instant}; @@ -77,7 +73,7 @@ pub struct TransactionPriority(pub u128); /// Internal Transaction type #[derive(Clone, PartialEq, Eq)] -pub struct PoolTransaction { +pub struct PoolTransaction { /// the pending eth transaction pub pending_transaction: PendingTransaction, /// Markers required by the transaction @@ -91,7 +87,7 @@ pub struct PoolTransaction { // == impl PoolTransaction == impl PoolTransaction { - pub const fn new(transaction: PendingTransaction) -> Self { + pub fn new(transaction: PendingTransaction) -> Self { Self { pending_transaction: transaction, requires: vec![], @@ -101,7 +97,7 @@ impl PoolTransaction { } /// Returns the hash of this transaction - pub const fn hash(&self) -> TxHash { + pub fn hash(&self) -> TxHash { *self.pending_transaction.hash() } } @@ -132,15 +128,10 @@ impl fmt::Debug for PoolTransaction { } } -impl TryFrom for PoolTransaction -where - T: SignerRecoverable + TxHashRef + Encodable + TryFrom, - >::Error: Into, - RecoveryError: Into, -{ +impl TryFrom for PoolTransaction { type Error = eyre::Error; fn try_from(value: AnyRpcTransaction) -> Result { - let typed_transaction = T::try_from(value).map_err(Into::into)?; + let typed_transaction = FoundryTxEnvelope::try_from(value)?; let pending_transaction = PendingTransaction::new(typed_transaction)?; Ok(Self { pending_transaction, @@ -155,7 +146,7 @@ where /// /// Keeps a set of transactions that are waiting for other transactions #[derive(Clone, Debug)] -pub struct PendingTransactions { +pub struct PendingTransactions { /// markers that aren't yet provided by any transaction required_markers: HashMap>, /// mapping of the markers of a transaction to the hash of the transaction @@ -276,7 +267,7 @@ impl PendingTransactions { .and_then(|hash| self.waiting_queue.get(hash)) { // check if underpriced - if tx.transaction.max_fee_per_gas() <= replace.transaction.max_fee_per_gas() { + if tx.transaction.max_fee_per_gas() < replace.transaction.max_fee_per_gas() { warn!(target: "txpool", "pending replacement transaction underpriced [{:?}]", tx.transaction.hash()); return Err(PoolError::ReplacementUnderpriced(tx.transaction.hash())); } @@ -298,7 +289,7 @@ impl PendingTransactions { /// A transaction in the pool #[derive(Clone)] -pub struct PendingPoolTransaction { +pub struct PendingPoolTransaction { pub transaction: Arc>, /// markers required and have not been satisfied yet by other transactions in the pool pub missing_markers: HashSet, @@ -346,7 +337,7 @@ impl fmt::Debug for PendingPoolTransaction { } } -pub struct TransactionsIterator { +pub struct TransactionsIterator { all: HashMap>, awaiting: HashMap)>, independent: BTreeSet>, @@ -403,7 +394,7 @@ impl Iterator for TransactionsIterator { /// transactions that are ready to be included in a block. #[derive(Clone, Debug)] -pub struct ReadyTransactions { +pub struct ReadyTransactions { /// keeps track of transactions inserted in the pool /// /// this way we can determine when transactions where submitted to the pool @@ -466,11 +457,11 @@ impl ReadyTransactions { self.ready_tx.read().get(hash).cloned() } - pub const fn provided_markers(&self) -> &HashMap { + pub fn provided_markers(&self) -> &HashMap { &self.provided_markers } - const fn next_id(&mut self) -> u64 { + fn next_id(&mut self) -> u64 { let id = self.id; self.id = self.id.wrapping_add(1); id @@ -515,7 +506,11 @@ impl ReadyTransactions { if let Some(idx) = tx2.unlocks.iter().position(|i| i == &hash) { tx2.unlocks.swap_remove(idx); } - tx2.unlocks.is_empty().then(|| tx2.transaction.transaction.provides.clone()) + if tx2.unlocks.is_empty() { + Some(tx2.transaction.transaction.provides.clone()) + } else { + None + } }; // find previous transactions @@ -689,8 +684,9 @@ impl ReadyTransactions { { warn!(target: "txpool", "ready replacement transaction underpriced [{:?}]", tx.hash()); return Err(PoolError::ReplacementUnderpriced(tx.hash())); + } else { + trace!(target: "txpool", "replacing ready transaction [{:?}] with higher gas price [{:?}]", to_remove.transaction.transaction.hash(), tx.hash()); } - trace!(target: "txpool", "replacing ready transaction [{:?}] with higher gas price [{:?}]", to_remove.transaction.transaction.hash(), tx.hash()); } unlocked_tx.extend(to_remove.unlocks.iter().copied()) @@ -708,7 +704,7 @@ impl ReadyTransactions { /// A reference to a transaction in the pool #[derive(Debug)] -pub struct PoolTransactionRef { +pub struct PoolTransactionRef { /// actual transaction pub transaction: Arc>, /// identifier used to internally compare the transaction in the pool @@ -745,7 +741,7 @@ impl Ord for PoolTransactionRef { } #[derive(Debug)] -pub struct ReadyTransaction { +pub struct ReadyTransaction { /// ref to the actual transaction pub transaction: PoolTransactionRef, /// tracks the transactions that get unlocked by this transaction diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 98db8a5455df9..9c4de9ae7b519 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -4,8 +4,6 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::ToRpcResponseResult}, pubsub::filter_logs, }; -use alloy_consensus::TxReceipt; -use alloy_network::Network; use alloy_primitives::{TxHash, map::HashMap}; use alloy_rpc_types::{Filter, FilteredParams, Log}; use anvil_core::eth::subscription::SubscriptionId; @@ -13,6 +11,7 @@ use anvil_rpc::{ error::{ErrorCode, RpcError}, response::ResponseResult, }; +use foundry_primitives::FoundryNetwork; use futures::{Stream, StreamExt, channel::mpsc::Receiver}; use std::{ pin::Pin, @@ -23,34 +22,23 @@ use std::{ use tokio::sync::Mutex; /// Type alias for filters identified by their id and their expiration timestamp -type FilterMap = Arc, Instant)>>>; +type FilterMap = Arc>>; /// timeout after which to remove an active filter if it wasn't polled since then pub const ACTIVE_FILTER_TIMEOUT_SECS: u64 = 60 * 5; /// Contains all registered filters -pub struct Filters { +#[derive(Clone, Debug)] +pub struct Filters { /// all currently active filters - active_filters: FilterMap, + active_filters: FilterMap, /// How long we keep a live the filter after the last poll keepalive: Duration, } -impl Clone for Filters { - fn clone(&self) -> Self { - Self { active_filters: self.active_filters.clone(), keepalive: self.keepalive } - } -} - -impl std::fmt::Debug for Filters { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Filters").field("keepalive", &self.keepalive).finish_non_exhaustive() - } -} - -impl Filters { +impl Filters { /// Adds a new `EthFilter` to the set - pub async fn add_filter(&self, filter: EthFilter) -> String { + pub async fn add_filter(&self, filter: EthFilter) -> String { let id = new_id(); trace!(target: "node::filter", "Adding new filter id {}", id); let mut filters = self.active_filters.lock().await; @@ -58,6 +46,26 @@ impl Filters { id } + pub async fn get_filter_changes(&self, id: &str) -> ResponseResult { + { + let mut filters = self.active_filters.lock().await; + if let Some((filter, deadline)) = filters.get_mut(id) { + let resp = filter + .next() + .await + .unwrap_or_else(|| ResponseResult::success(Vec::<()>::new())); + *deadline = self.next_deadline(); + return resp; + } + } + warn!(target: "node::filter", "No filter found for {}", id); + ResponseResult::error(RpcError { + code: ErrorCode::ServerError(-32000), + message: "filter not found".into(), + data: None, + }) + } + /// Returns the original `Filter` of an `eth_newFilter` pub async fn get_log_filter(&self, id: &str) -> Option { let filters = self.active_filters.lock().await; @@ -68,13 +76,13 @@ impl Filters { } /// Removes the filter identified with the `id` - pub async fn uninstall_filter(&self, id: &str) -> Option> { + pub async fn uninstall_filter(&self, id: &str) -> Option { trace!(target: "node::filter", "Uninstalling filter id {}", id); self.active_filters.lock().await.remove(id).map(|(f, _)| f) } /// The duration how long to keep alive stale filters - pub const fn keep_alive(&self) -> Duration { + pub fn keep_alive(&self) -> Duration { self.keepalive } @@ -98,32 +106,7 @@ impl Filters { } } -impl Filters -where - N::ReceiptEnvelope: TxReceipt, -{ - pub async fn get_filter_changes(&self, id: &str) -> ResponseResult { - { - let mut filters = self.active_filters.lock().await; - if let Some((filter, deadline)) = filters.get_mut(id) { - let resp = filter - .next() - .await - .unwrap_or_else(|| ResponseResult::success(Vec::<()>::new())); - *deadline = self.next_deadline(); - return resp; - } - } - warn!(target: "node::filter", "No filter found for {}", id); - ResponseResult::error(RpcError { - code: ErrorCode::ServerError(-32000), - message: "filter not found".into(), - data: None, - }) - } -} - -impl Default for Filters { +impl Default for Filters { fn default() -> Self { Self { active_filters: Arc::new(Default::default()), @@ -138,26 +121,14 @@ fn new_id() -> String { } /// Represents a poll based filter -pub enum EthFilter { - Logs(Box>), +#[derive(Debug)] +pub enum EthFilter { + Logs(Box), Blocks(NewBlockNotifications), PendingTransactions(Receiver), } -impl std::fmt::Debug for EthFilter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Logs(_) => f.debug_tuple("Logs").finish(), - Self::Blocks(_) => f.debug_tuple("Blocks").finish(), - Self::PendingTransactions(_) => f.debug_tuple("PendingTransactions").finish(), - } - } -} - -impl Stream for EthFilter -where - N::ReceiptEnvelope: TxReceipt, -{ +impl Stream for EthFilter { type Item = ResponseResult; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -183,11 +154,12 @@ where } /// Listens for new blocks and matching logs emitted in that block -pub struct LogsFilter { +#[derive(Debug)] +pub struct LogsFilter { /// listener for new blocks pub blocks: NewBlockNotifications, /// accessor for block storage - pub storage: StorageInfo, + pub storage: StorageInfo, /// matcher with all provided filter params pub filter: FilteredParams, /// existing logs that matched the filter when the listener was installed @@ -196,16 +168,7 @@ pub struct LogsFilter { pub historic: Option>, } -impl std::fmt::Debug for LogsFilter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LogsFilter").field("filter", &self.filter).finish_non_exhaustive() - } -} - -impl LogsFilter -where - N::ReceiptEnvelope: TxReceipt, -{ +impl LogsFilter { /// Returns all the logs since the last time this filter was polled pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { let mut logs = self.historic.take().unwrap_or_default(); diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index 4ceb8be0bfc64..c62427eb9a4c8 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -3,11 +3,12 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::to_rpc_result}, }; use alloy_consensus::{BlockHeader, TxReceipt}; -use alloy_network::{AnyRpcTransaction, Network}; +use alloy_network::AnyRpcTransaction; use alloy_primitives::{B256, TxHash}; use alloy_rpc_types::{FilteredParams, Log, Transaction, pubsub::SubscriptionResult}; use anvil_core::eth::{block::Block, subscription::SubscriptionId}; use anvil_rpc::{request::Version, response::ResponseResult}; +use foundry_primitives::FoundryNetwork; use futures::{Stream, StreamExt, channel::mpsc::Receiver, ready}; use serde::Serialize; use std::{ @@ -18,27 +19,16 @@ use std::{ use tokio::sync::mpsc::UnboundedReceiver; /// Listens for new blocks and matching logs emitted in that block -pub struct LogsSubscription { +#[derive(Debug)] +pub struct LogsSubscription { pub blocks: NewBlockNotifications, - pub storage: StorageInfo, + pub storage: StorageInfo, pub filter: FilteredParams, pub queued: VecDeque, pub id: SubscriptionId, } -impl std::fmt::Debug for LogsSubscription { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LogsSubscription") - .field("filter", &self.filter) - .field("id", &self.id) - .finish_non_exhaustive() - } -} - -impl LogsSubscription -where - N::ReceiptEnvelope: TxReceipt, -{ +impl LogsSubscription { fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { loop { if let Some(log) = self.queued.pop_front() { @@ -81,7 +71,7 @@ pub struct EthSubscriptionResponse { } impl EthSubscriptionResponse { - pub const fn new(params: EthSubscriptionParams) -> Self { + pub fn new(params: EthSubscriptionParams) -> Self { Self { jsonrpc: Version::V2, method: "eth_subscription", params } } } @@ -95,28 +85,15 @@ pub struct EthSubscriptionParams { } /// Represents an ethereum Websocket subscription -pub enum EthSubscription { - Logs(Box>), - Header(NewBlockNotifications, StorageInfo, SubscriptionId), +#[derive(Debug)] +pub enum EthSubscription { + Logs(Box), + Header(NewBlockNotifications, StorageInfo, SubscriptionId), PendingTransactions(Receiver, SubscriptionId), FullPendingTransactions(UnboundedReceiver, SubscriptionId), } -impl std::fmt::Debug for EthSubscription { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Logs(_) => f.debug_tuple("Logs").finish(), - Self::Header(..) => f.debug_tuple("Header").finish(), - Self::PendingTransactions(..) => f.debug_tuple("PendingTransactions").finish(), - Self::FullPendingTransactions(..) => f.debug_tuple("FullPendingTransactions").finish(), - } - } -} - -impl EthSubscription -where - N::ReceiptEnvelope: TxReceipt, -{ +impl EthSubscription { fn poll_response(&mut self, cx: &mut Context<'_>) -> Poll> { match self { Self::Logs(listener) => listener.poll(cx), @@ -159,10 +136,7 @@ where } } -impl Stream for EthSubscription -where - N::ReceiptEnvelope: TxReceipt, -{ +impl Stream for EthSubscription { type Item = serde_json::Value; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/crates/anvil/src/server/beacon/handlers.rs b/crates/anvil/src/server/beacon/handlers.rs index 73d533fa606c3..9310a7960293c 100644 --- a/crates/anvil/src/server/beacon/handlers.rs +++ b/crates/anvil/src/server/beacon/handlers.rs @@ -1,7 +1,6 @@ use super::{error::BeaconError, utils::must_be_ssz}; use crate::eth::EthApi; use alloy_eips::BlockId; -use alloy_network::Network; use alloy_primitives::{B256, aliases::B32}; use alloy_rpc_types_beacon::{ genesis::{GenesisData, GenesisResponse}, @@ -21,8 +20,8 @@ use std::{collections::HashMap, str::FromStr as _}; /// This endpoint is deprecated. Use `GET /eth/v1/beacon/blobs/{block_id}` instead. /// /// GET /eth/v1/beacon/blob_sidecars/{block_id} -pub async fn handle_get_blob_sidecars( - State(_api): State>, +pub async fn handle_get_blob_sidecars( + State(_api): State, Path(_block_id): Path, Query(_params): Query>, ) -> Response { @@ -33,9 +32,9 @@ pub async fn handle_get_blob_sidecars( /// Handles incoming Beacon API requests for blobs /// /// GET /eth/v1/beacon/blobs/{block_id} -pub async fn handle_get_blobs( +pub async fn handle_get_blobs( headers: HeaderMap, - State(api): State>, + State(api): State, Path(block_id): Path, Query(versioned_hashes): Query>, ) -> Response { @@ -44,31 +43,12 @@ pub async fn handle_get_blobs( return BeaconError::invalid_block_id(block_id).into_response(); }; - // Parse versioned hashes from query parameters - // Supports comma-separated format: ?versioned_hashes=0x...,0x... - let versioned_hashes: Vec = match versioned_hashes.get("versioned_hashes") { - Some(s) => { - let mut hashes = Vec::new(); - for hash in s.split(',') { - let hash = hash.trim(); - if hash.is_empty() { - continue; - } - match B256::from_str(hash) { - Ok(h) => hashes.push(h), - Err(_) => { - return BeaconError::new( - super::error::BeaconErrorCode::BadRequest, - format!("Invalid versioned hash: {hash}"), - ) - .into_response(); - } - } - } - hashes - } - None => Vec::new(), - }; + // Parse indices from query parameters + // Supports both comma-separated (?indices=1,2,3) and repeated parameters (?indices=1&indices=2) + let versioned_hashes: Vec = versioned_hashes + .get("versioned_hashes") + .map(|s| s.split(',').filter_map(|hash| B256::from_str(hash.trim()).ok()).collect()) + .unwrap_or_default(); // Get the blob sidecars using existing EthApi logic match api.anvil_get_blobs_by_block_id(block_id, versioned_hashes) { @@ -94,7 +74,7 @@ pub async fn handle_get_blobs( /// Only returns the `genesis_time`, other fields are set to zero. /// /// GET /eth/v1/beacon/genesis -pub async fn handle_get_genesis(State(api): State>) -> Response { +pub async fn handle_get_genesis(State(api): State) -> Response { match api.anvil_get_genesis_time() { Ok(genesis_time) => Json(GenesisResponse { data: GenesisData { diff --git a/crates/anvil/src/server/beacon/mod.rs b/crates/anvil/src/server/beacon/mod.rs index f0138747c8b00..9100186d436d5 100644 --- a/crates/anvil/src/server/beacon/mod.rs +++ b/crates/anvil/src/server/beacon/mod.rs @@ -3,20 +3,16 @@ use axum::{Router, routing::get}; use crate::eth::EthApi; -use alloy_network::Network; mod error; mod handlers; mod utils; /// Configures an [`axum::Router`] that handles Beacon REST API calls. -pub fn router(api: EthApi) -> Router { +pub fn router(api: EthApi) -> Router { Router::new() - .route( - "/eth/v1/beacon/blob_sidecars/{block_id}", - get(handlers::handle_get_blob_sidecars::), - ) - .route("/eth/v1/beacon/blobs/{block_id}", get(handlers::handle_get_blobs::)) - .route("/eth/v1/beacon/genesis", get(handlers::handle_get_genesis::)) + .route("/eth/v1/beacon/blob_sidecars/{block_id}", get(handlers::handle_get_blob_sidecars)) + .route("/eth/v1/beacon/blobs/{block_id}", get(handlers::handle_get_blobs)) + .route("/eth/v1/beacon/genesis", get(handlers::handle_get_genesis)) .with_state(api) } diff --git a/crates/anvil/src/server/rpc_handlers.rs b/crates/anvil/src/server/rpc_handlers.rs index c531ba2705713..060cd682a4277 100644 --- a/crates/anvil/src/server/rpc_handlers.rs +++ b/crates/anvil/src/server/rpc_handlers.rs @@ -11,18 +11,17 @@ use alloy_rpc_types::{ use anvil_core::eth::{EthPubSub, EthRequest, EthRpcCall, subscription::SubscriptionId}; use anvil_rpc::{error::RpcError, response::ResponseResult}; use anvil_server::{PubSubContext, PubSubRpcHandler, RpcHandler}; -use foundry_primitives::FoundryNetwork; /// A `RpcHandler` that expects `EthRequest` rpc calls via http #[derive(Clone)] pub struct HttpEthRpcHandler { /// Access to the node - api: EthApi, + api: EthApi, } impl HttpEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` - pub const fn new(api: EthApi) -> Self { + pub fn new(api: EthApi) -> Self { Self { api } } } @@ -40,12 +39,12 @@ impl RpcHandler for HttpEthRpcHandler { #[derive(Clone)] pub struct PubSubEthRpcHandler { /// Access to the node - api: EthApi, + api: EthApi, } impl PubSubEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` - pub const fn new(api: EthApi) -> Self { + pub fn new(api: EthApi) -> Self { Self { api } } @@ -134,7 +133,7 @@ impl PubSubEthRpcHandler { impl PubSubRpcHandler for PubSubEthRpcHandler { type Request = EthRpcCall; type SubscriptionId = SubscriptionId; - type Subscription = EthSubscription; + type Subscription = EthSubscription; async fn on_request(&self, request: Self::Request, cx: PubSubContext) -> ResponseResult { trace!(target: "rpc", "received pubsub request {:?}", request); diff --git a/crates/anvil/src/service.rs b/crates/anvil/src/service.rs index 88a47b55a1418..c0071505157ae 100644 --- a/crates/anvil/src/service.rs +++ b/crates/anvil/src/service.rs @@ -3,7 +3,6 @@ use crate::{ NodeResult, eth::{ - backend::validate::TransactionValidator, fees::FeeHistoryService, miner::Miner, pool::{Pool, transactions::PoolTransaction}, @@ -11,9 +10,7 @@ use crate::{ filter::Filters, mem::{Backend, storage::MinedBlockOutcome}, }; -use alloy_consensus::TxReceipt; -use alloy_network::Network; -use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope}; +use foundry_primitives::{FoundryNetwork, FoundryTxEnvelope}; use futures::{FutureExt, Stream, StreamExt}; use std::{ collections::VecDeque, @@ -29,35 +26,28 @@ use tokio::{task::JoinHandle, time::Interval}; /// 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 -where - N::ReceiptEnvelope: TxReceipt, -{ +pub struct NodeService { /// The pool that holds all transactions. - pool: Arc>, + pool: Arc>, /// Creates new blocks. - block_producer: BlockProducer, + block_producer: BlockProducer, /// The miner responsible to select transactions from the `pool`. - miner: Miner, + miner: Miner, /// Maintenance task for fee history related tasks. - fee_history: FeeHistoryService, + fee_history: FeeHistoryService, /// Tracks all active filters - filters: Filters, + filters: Filters, /// The interval at which to check for filters that need to be evicted filter_eviction_interval: Interval, } -impl NodeService -where - Backend: TransactionValidator, - N: Network, -{ +impl NodeService { pub fn new( - pool: Arc>, - backend: Arc>, - miner: Miner, - fee_history: FeeHistoryService, - filters: Filters, + pool: Arc, + backend: Arc>, + miner: Miner, + fee_history: FeeHistoryService, + filters: Filters, ) -> Self { let start = tokio::time::Instant::now() + filters.keep_alive(); let filter_eviction_interval = tokio::time::interval_at(start, filters.keep_alive()); @@ -72,11 +62,7 @@ where } } -impl Future for NodeService -where - Backend: TransactionValidator, - N: Network, -{ +impl Future for NodeService { type Output = NodeResult<()>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -115,35 +101,27 @@ where } } -type MiningResult = (MinedBlockOutcome<::TxEnvelope>, Arc>); +type MiningResult = (MinedBlockOutcome, Arc>); /// A type that exclusively mines one block at a time #[must_use = "streams do nothing unless polled"] -struct BlockProducer { +struct BlockProducer { /// Holds the backend if no block is being mined - idle_backend: Option>>, + 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>>>, + queued: VecDeque>>>, } -impl BlockProducer -where - Backend: TransactionValidator, - N: Network, -{ - fn new(backend: Arc>) -> Self { +impl BlockProducer { + fn new(backend: Arc>) -> Self { Self { idle_backend: Some(backend), block_mining: None, queued: Default::default() } } } -impl Stream for BlockProducer -where - Backend: TransactionValidator + Send + Sync + 'static, - N: Network + 'static, -{ - type Item = MinedBlockOutcome; +impl Stream for BlockProducer { + type Item = MinedBlockOutcome; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); @@ -179,8 +157,9 @@ where panic!("miner task failed: {err}"); } }; + } else { + pin.block_mining = Some(mining) } - pin.block_mining = Some(mining) } Poll::Pending diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 1a81b79a9a3c3..e802d8a19be7b 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1449,6 +1449,31 @@ async fn test_immutable_fork_transaction_hash() { } } +#[tokio::test(flavor = "multi_thread")] +async fn test_block_by_number_full_refetches_missing_cached_transactions() { + let (api, _) = spawn(fork_config()).await; + + let block = + api.block_by_number_full(BlockNumberOrTag::Number(BLOCK_NUMBER)).await.unwrap().unwrap(); + let block_txs = block.transactions.as_transactions().unwrap(); + let original_len = block_txs.len(); + let missing_hash = *block_txs[0].tx_hash(); + + let fork = api.backend.get_fork().unwrap(); + { + let mut storage = fork.storage.write(); + assert!(storage.transactions.remove(&missing_hash).is_some()); + } + + let refreshed = + api.block_by_number_full(BlockNumberOrTag::Number(BLOCK_NUMBER)).await.unwrap().unwrap(); + let refreshed_txs = refreshed.transactions.as_transactions().unwrap(); + + assert_eq!(refreshed_txs.len(), original_len); + assert_eq!(refreshed_txs[0].tx_hash(), &missing_hash); + assert!(fork.storage.read().transactions.contains_key(&missing_hash)); +} + // #[tokio::test(flavor = "multi_thread")] async fn test_fork_query_at_fork_block() { diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index bea3fdec96ad6..df5086e505f9a 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -10,6 +10,7 @@ use alloy_provider::Provider; use alloy_rpc_types::{BlockNumberOrTag, Filter}; use anvil::{NodeConfig, spawn}; use futures::StreamExt; +use std::str::FromStr; #[tokio::test(flavor = "multi_thread")] async fn get_past_events() { @@ -195,3 +196,20 @@ async fn watch_events() { assert_eq!(log.1.block_hash.unwrap(), hash); } } + +#[tokio::test(flavor = "multi_thread")] +async fn get_logs_unknown_block_hash_returns_error() { + let (_api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let unknown_hash = + B256::from_str("0x0000000000000000000000000000000000000000000000000000000000000001") + .unwrap(); + let filter = Filter::new().at_block_hash(unknown_hash); + + let err = provider.get_logs(&filter).await.unwrap_err(); + assert!( + err.to_string().contains("unknown block"), + "expected `unknown block` in error, got: {err}" + ); +} diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 16a23081c87c1..dbd89ce88827d 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -99,8 +99,8 @@ anvil.workspace = true foundry-test-utils.workspace = true [features] -default = ["jemalloc"] -asm-keccak = ["alloy-primitives/asm-keccak"] +default = ["jemalloc", "asm-keccak"] +asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] tracy-allocator = ["foundry-cli/tracy-allocator"] diff --git a/crates/cast/src/call_spec.rs b/crates/cast/src/call_spec.rs index dcd6d0980fcfd..b39e024f38af5 100644 --- a/crates/cast/src/call_spec.rs +++ b/crates/cast/src/call_spec.rs @@ -8,9 +8,14 @@ //! - `0x123::transfer(address,uint256):0x789,1000` - Contract call with signature //! - `0x123::0xabcdef` - Contract call with raw calldata +use alloy_network::Network; use alloy_primitives::{Address, Bytes, U256, hex}; +use alloy_provider::Provider; use eyre::{Result, WrapErr, eyre}; +use foundry_cli::utils::parse_function_args; +use foundry_config::Chain; use std::str::FromStr; +use tempo_primitives::transaction::Call; /// A parsed call specification for batch transactions. #[derive(Debug, Clone)] @@ -98,6 +103,35 @@ impl CallSpec { Ok(Self { to, value, sig, args, data }) } + + /// Resolves this spec into a [`Call`], encoding function arguments if needed. + /// `i` is the 0-based index of this call; displayed as `i + 1` in error messages. + pub async fn resolve>( + &self, + i: usize, + chain: Chain, + provider: &P, + etherscan_api_key: Option<&str>, + ) -> Result { + let input = if let Some(data) = &self.data { + data.clone() + } else if let Some(sig) = &self.sig { + let (encoded, _) = parse_function_args( + sig, + self.args.clone(), + Some(self.to), + chain, + provider, + etherscan_api_key, + ) + .await + .map_err(|e| eyre!("Failed to encode call {}: {e}", i + 1))?; + Bytes::from(encoded) + } else { + Bytes::new() + }; + Ok(Call { to: self.to.into(), value: self.value, input }) + } } impl FromStr for CallSpec { diff --git a/crates/cast/src/cmd/access_list.rs b/crates/cast/src/cmd/access_list.rs index 9776502145afd..ae533483fb820 100644 --- a/crates/cast/src/cmd/access_list.rs +++ b/crates/cast/src/cmd/access_list.rs @@ -1,9 +1,10 @@ use crate::{ Cast, + rlp_converter::TryIntoRlpEncodable, tx::{CastTxBuilder, SenderKind}, }; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, Network}; +use alloy_network::{AnyNetwork, Network}; use alloy_rpc_types::BlockId; use clap::Parser; use eyre::Result; @@ -11,8 +12,13 @@ use foundry_cli::{ opts::{RpcOpts, TransactionOpts}, utils::LoadConfig, }; -use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; +use foundry_common::{ + fmt::{UIfmt, UIfmtHeaderExt, UIfmtSignatureExt}, + provider::ProviderBuilder, +}; +use foundry_primitives::FoundryTransactionBuilder; use foundry_wallets::WalletOpts; +use serde::Serialize; use std::str::FromStr; use tempo_alloy::TempoNetwork; @@ -62,13 +68,18 @@ impl AccessListArgs { if self.tx.tempo.is_tempo() { self.run_with_network::().await } else { - self.run_with_network::().await + self.run_with_network::().await } } pub async fn run_with_network(self) -> Result<()> where + N::TxEnvelope: Serialize + UIfmtSignatureExt, + N::Header: TryIntoRlpEncodable, N::TransactionRequest: FoundryTransactionBuilder, + N::TransactionResponse: UIfmt, + N::HeaderResponse: UIfmtHeaderExt, + N::BlockResponse: UIfmt, { let Self { to, mut sig, args, data, tx, rpc, wallet, block } = self; diff --git a/crates/cast/src/cmd/batch_mktx.rs b/crates/cast/src/cmd/batch_mktx.rs index 42596fe6cff4d..759d8ef507f20 100644 --- a/crates/cast/src/cmd/batch_mktx.rs +++ b/crates/cast/src/cmd/batch_mktx.rs @@ -10,18 +10,17 @@ use crate::{ use alloy_consensus::SignableTransaction; use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumWallet, NetworkTransactionBuilder}; -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::Address; use alloy_provider::Provider; use alloy_signer::Signer; use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, LoadConfig, parse_function_args}, + utils::{self, LoadConfig}, }; use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; use tempo_alloy::TempoNetwork; -use tempo_primitives::transaction::Call; /// CLI arguments for `cast batch-mktx`. /// @@ -75,28 +74,10 @@ impl BatchMakeTxArgs { let chain = utils::get_chain(config.chain, &provider).await?; let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); - // Build Vec from specs let mut tempo_calls = Vec::with_capacity(call_specs.len()); for (i, spec) in call_specs.iter().enumerate() { - let input = if let Some(data) = &spec.data { - data.clone() - } else if let Some(sig) = &spec.sig { - let (encoded, _) = parse_function_args( - sig, - spec.args.clone(), - Some(spec.to), - chain, - &provider, - etherscan_api_key.as_deref(), - ) - .await - .map_err(|e| eyre!("Failed to encode call {}: {}", i + 1, e))?; - Bytes::from(encoded) - } else { - Bytes::new() - }; - - tempo_calls.push(Call { to: spec.to.into(), value: spec.value, input }); + tempo_calls + .push(spec.resolve(i, chain, &provider, etherscan_api_key.as_deref()).await?); } sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?; @@ -144,23 +125,9 @@ impl BatchMakeTxArgs { Some(s) => s, None => eth.wallet.signer().await?, }; - let from = if let Some(ref access_key) = tempo_access_key { - access_key.wallet_address - } else { - Signer::address(&signer) - }; - - if tempo_access_key.is_none() { - tx::validate_from_address(eth.wallet.from, from)?; - } - - let (tx, _) = if tempo_access_key.is_some() { - tx_builder.build(from).await? - } else { - tx_builder.build(&signer).await? - }; let signed_tx = if let Some(ref access_key) = tempo_access_key { + let (tx, _) = tx_builder.build(access_key.wallet_address).await?; let raw_tx = tx .sign_with_access_key( &provider, @@ -172,6 +139,8 @@ impl BatchMakeTxArgs { .await?; alloy_primitives::hex::encode(raw_tx) } else { + tx::validate_from_address(eth.wallet.from, Signer::address(&signer))?; + let (tx, _) = tx_builder.build(&signer).await?; let envelope = tx.build(&EthereumWallet::new(signer)).await?; alloy_primitives::hex::encode(envelope.encoded_2718()) }; diff --git a/crates/cast/src/cmd/batch_send.rs b/crates/cast/src/cmd/batch_send.rs index c381e5f7a1e2a..5fd18b0261897 100644 --- a/crates/cast/src/cmd/batch_send.rs +++ b/crates/cast/src/cmd/batch_send.rs @@ -6,23 +6,21 @@ use crate::{ call_spec::CallSpec, - cmd::send::cast_send, - tx::{self, CastTxBuilder, CastTxSender, SendTxOpts}, + cmd::send::{cast_send, cast_send_with_access_key}, + tx::{self, CastTxBuilder, SendTxOpts}, }; use alloy_network::EthereumWallet; -use alloy_primitives::Bytes; use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::Signer; use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::TransactionOpts, - utils::{self, LoadConfig, parse_function_args}, + utils::{self, LoadConfig}, }; -use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; +use foundry_common::provider::ProviderBuilder; use std::time::Duration; use tempo_alloy::TempoNetwork; -use tempo_primitives::transaction::Call; /// CLI arguments for `cast batch-send`. /// @@ -79,25 +77,8 @@ impl BatchSendArgs { // Build Vec from specs let mut tempo_calls = Vec::with_capacity(call_specs.len()); for (i, spec) in call_specs.iter().enumerate() { - let input = if let Some(data) = &spec.data { - data.clone() - } else if let Some(sig) = &spec.sig { - let (encoded, _) = parse_function_args( - sig, - spec.args.clone(), - Some(spec.to), - chain, - &provider, - etherscan_api_key.as_deref(), - ) - .await - .map_err(|e| eyre!("Failed to encode call {}: {}", i + 1, e))?; - Bytes::from(encoded) - } else { - Bytes::new() - }; - - tempo_calls.push(Call { to: spec.to.into(), value: spec.value, input }); + tempo_calls + .push(spec.resolve(i, chain, &provider, etherscan_api_key.as_deref()).await?); } sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?; @@ -139,38 +120,22 @@ impl BatchSendArgs { Some(s) => s, None => send_tx.eth.wallet.signer().await?, }; - let from = if let Some(ref access_key) = tempo_access_key { - access_key.wallet_address - } else { - Signer::address(&signer) - }; - - if tempo_access_key.is_none() { - tx::validate_from_address(send_tx.eth.wallet.from, from)?; - } - - let (tx_request, _) = if tempo_access_key.is_some() { - builder.build(from).await? - } else { - builder.build(&signer).await? - }; if let Some(ref access_key) = tempo_access_key { - let raw_tx = tx_request - .sign_with_access_key( - &provider, - &signer, - access_key.wallet_address, - access_key.key_address, - access_key.key_authorization.as_ref(), - ) - .await?; - - let cast = CastTxSender::new(&provider); - let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); - cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout) - .await?; + let (tx_request, _) = builder.build(access_key.wallet_address).await?; + cast_send_with_access_key( + &provider, + tx_request, + &signer, + access_key, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await?; } else { + tx::validate_from_address(send_tx.eth.wallet.from, Signer::address(&signer))?; + let (tx_request, _) = builder.build(&signer).await?; let wallet = EthereumWallet::from(signer); let provider = AlloyProviderBuilder::<_, _, TempoNetwork>::default() .wallet(wallet) diff --git a/crates/cast/src/cmd/create2.rs b/crates/cast/src/cmd/create2.rs index f7e5aae1f2d9e..056dbd73fcc83 100644 --- a/crates/cast/src/cmd/create2.rs +++ b/crates/cast/src/cmd/create2.rs @@ -3,13 +3,7 @@ use clap::Parser; use eyre::{Result, WrapErr}; use rand::{RngCore, SeedableRng, rngs::StdRng}; use regex::RegexSetBuilder; -use std::{ - sync::{ - Arc, - atomic::{AtomicBool, Ordering}, - }, - time::Instant, -}; +use std::time::Instant; // https://etherscan.io/address/0x4e59b44847b379578588920ca78fbf26c0b4956c#code const DEPLOYER: &str = "0x4e59b44847b379578588920ca78fbf26c0b4956c"; @@ -195,71 +189,27 @@ impl Create2Args { sh_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(); - - // Loops through all possible salts in parallel until a result is found. - // Each thread iterates over `(i..).step_by(n_threads)`. - for i in 0..n_threads { - // Create local copies for the thread. - let increment = n_threads; - let regex = regex.clone(); - let regex_len = regex.patterns().len(); - let found = Arc::clone(&found); - handles.push(std::thread::spawn(move || { - // Read the first bytes of the salt as a usize to be able to increment it. - #[repr(C)] - struct B256Aligned(B256, [usize; 0]); - let mut salt = B256Aligned(salt, []); - // SAFETY: B256 is aligned to `usize`. - let salt_word = unsafe { - &mut *salt.0.as_mut_ptr().add(32 - usize::BITS as usize / 8).cast::() - }; - // Important: add the thread index to the salt to avoid duplicate results. - *salt_word = salt_word.wrapping_add(i); - - // Use checksum format only when case_sensitive is enabled. - // This avoids an extra keccak256 call per iteration when not needed. - let mut checksum_buf = [0u8; 42]; - let mut hex_buf = [0u8; 40]; - loop { - // Stop if a result was found in another thread. - if found.load(Ordering::Relaxed) { - break None; - } - - // Calculate the `CREATE2` address. - #[expect(clippy::needless_borrows_for_generic_args)] - let addr = deployer.create2(&salt.0, &init_code_hash); - - // Check if the regex matches the calculated address. - // When case_sensitive is true, use EIP-55 checksum format (requires keccak256). - // Otherwise, use lowercase hex to avoid the extra hash computation. - let s = if case_sensitive { - let _ = addr.to_checksum_raw(&mut checksum_buf, None); - // SAFETY: stripping 2 ASCII bytes ("0x") off of an already valid UTF-8 - // string is safe. - unsafe { std::str::from_utf8_unchecked(checksum_buf.get_unchecked(2..)) } - } else { - // SAFETY: hex::encode_to_slice always produces valid UTF-8 (hex digits). - let _ = hex::encode_to_slice(addr.as_slice(), &mut hex_buf); - unsafe { std::str::from_utf8_unchecked(&hex_buf) } - }; - if regex.matches(s).into_iter().count() == regex_len { - // Notify other threads that we found a result. - found.store(true, Ordering::Relaxed); - break Some((addr, salt.0)); - } - - // Increment the salt for the next iteration. - *salt_word = salt_word.wrapping_add(increment); - } - })); - } - - let results = handles.into_iter().filter_map(|h| h.join().unwrap()).collect::>(); - let (address, salt) = results.into_iter().next().unwrap(); + let regex_len = regex.patterns().len(); + let mut checksum_buf = [0u8; 42]; + let mut hex_buf = [0u8; 40]; + let (address, salt) = super::miner::mine_salt(salt, n_threads, move |salt| { + #[expect(clippy::needless_borrows_for_generic_args)] + let addr = deployer.create2(&salt, &init_code_hash); + // Use checksum format only when case_sensitive is enabled — it requires an extra + // keccak256 call, so we fall back to plain hex when case sensitivity is off. + let s = if case_sensitive { + let _ = addr.to_checksum_raw(&mut checksum_buf, None); + // SAFETY: stripping 2 ASCII bytes ("0x") off of an already valid UTF-8 string. + unsafe { std::str::from_utf8_unchecked(checksum_buf.get_unchecked(2..)) } + } else { + // SAFETY: hex::encode_to_slice always produces valid UTF-8 (hex digits). + let _ = hex::encode_to_slice(addr.as_slice(), &mut hex_buf); + unsafe { std::str::from_utf8_unchecked(&hex_buf) } + }; + (regex.matches(s).into_iter().count() == regex_len).then_some((addr, salt)) + }) + .ok_or_else(|| eyre::eyre!("create2 salt mining failed: all threads panicked"))?; sh_println!("Successfully found contract address in {:?}", timer.elapsed())?; sh_println!("Address: {address}")?; sh_println!("Salt: {salt} ({})", U256::from_be_bytes(salt.0))?; diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index 07e7df1b67517..e629b8eca0d5a 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -1,17 +1,21 @@ use std::{str::FromStr, time::Duration}; -use crate::{cmd::send::cast_send, format_uint_exp, tx::SendTxOpts}; +use crate::{ + cmd::send::{cast_send, cast_send_with_access_key}, + format_uint_exp, + tx::{SendTxOpts, TxParams}, +}; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::BlockId; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder}; -use alloy_primitives::{U64, U256}; +use alloy_network::{Ethereum, EthereumWallet, Network}; +use alloy_primitives::U256; use alloy_provider::{Provider, fillers::RecommendedFillers}; use alloy_signer::Signature; use alloy_sol_types::sol; -use clap::{Args, Parser}; +use clap::Parser; use foundry_cli::{ - opts::{RpcOpts, TempoOpts}, + opts::RpcOpts, utils::{LoadConfig, get_chain, get_provider}, }; use foundry_common::{ @@ -42,32 +46,6 @@ sol! { } } -/// Transaction options for ERC20 operations. -/// -/// This struct contains only the transaction options relevant to ERC20 token interactions -#[derive(Debug, Clone, Args)] -#[command(next_help_heading = "Transaction options")] -pub struct Erc20TxOpts { - /// Gas limit for the transaction. - #[arg(long, env = "ETH_GAS_LIMIT")] - pub gas_limit: Option, - - /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. - #[arg(long, env = "ETH_GAS_PRICE")] - pub gas_price: Option, - - /// Max priority fee per gas for EIP1559 transactions. - #[arg(long, env = "ETH_PRIORITY_GAS_PRICE")] - pub priority_gas_price: Option, - - /// Nonce for the transaction. - #[arg(long)] - pub nonce: Option, - - #[command(flatten)] - pub tempo: TempoOpts, -} - /// Creates a provider with a pre-resolved signer. pub(crate) fn build_provider_with_signer( tx_opts: &SendTxOpts, @@ -86,32 +64,6 @@ where Ok(provider) } -impl Erc20TxOpts { - /// Applies gas, fee, nonce, and Tempo options to a transaction request. - fn apply(&self, tx: &mut N::TransactionRequest, legacy: bool) - where - N::TransactionRequest: FoundryTransactionBuilder, - { - if let Some(gas_limit) = self.gas_limit { - tx.set_gas_limit(gas_limit.to()); - } - - if let Some(gas_price) = self.gas_price { - if legacy { - tx.set_gas_price(gas_price.to()); - } else { - tx.set_max_fee_per_gas(gas_price.to()); - } - } - - if !legacy && let Some(priority_fee) = self.priority_gas_price { - tx.set_max_priority_fee_per_gas(priority_fee.to()); - } - - self.tempo.apply::(tx, self.nonce.map(|n| n.to())); - } -} - /// Interact with ERC20 tokens. #[derive(Debug, Parser, Clone)] pub enum Erc20Subcommand { @@ -152,7 +104,7 @@ pub enum Erc20Subcommand { send_tx: SendTxOpts, #[command(flatten)] - tx: Erc20TxOpts, + tx: TxParams, }, /// Approve ERC20 token spending. @@ -173,7 +125,7 @@ pub enum Erc20Subcommand { send_tx: SendTxOpts, #[command(flatten)] - tx: Erc20TxOpts, + tx: TxParams, }, /// Query ERC20 token allowance. @@ -277,7 +229,7 @@ pub enum Erc20Subcommand { send_tx: SendTxOpts, #[command(flatten)] - tx: Erc20TxOpts, + tx: TxParams, }, /// Burn ERC20 tokens. @@ -294,7 +246,7 @@ pub enum Erc20Subcommand { send_tx: SendTxOpts, #[command(flatten)] - tx: Erc20TxOpts, + tx: TxParams, }, } @@ -314,7 +266,7 @@ impl Erc20Subcommand { } } - const fn erc20_opts(&self) -> Option<&Erc20TxOpts> { + const fn erc20_opts(&self) -> Option<&TxParams> { match self { Self::Approve { tx, .. } | Self::Transfer { tx, .. } @@ -383,8 +335,9 @@ impl Erc20Subcommand { ) => {{ let timeout = $send_tx.timeout.unwrap_or(config.transaction_timeout); if let Some(ref access_key) = tempo_keychain { - let signer = - pre_resolved_signer.as_ref().expect("signer required for access key"); + let signer = pre_resolved_signer + .as_ref() + .ok_or_else(|| eyre::eyre!("signer required for access key"))?; let $provider = ProviderBuilder::::from_config(&config)?.build()?; let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); @@ -393,8 +346,7 @@ impl Erc20Subcommand { &mut tx, get_chain(config.chain, &$provider).await?.is_legacy(), ); - apply_tempo_access_key::(&mut tx, Some(access_key)); - send_tempo_keychain( + cast_send_with_access_key( &$provider, tx, signer, @@ -551,49 +503,3 @@ impl Erc20Subcommand { Ok(()) } } - -/// Applies Tempo access key fields (from, key_id) to a transaction request. -/// -/// Note: `key_authorization` is intentionally not set here. It is only included -/// if the key is not yet provisioned on-chain (checked in [`send_tempo_keychain`]). -fn apply_tempo_access_key( - tx: &mut N::TransactionRequest, - config: Option<&TempoAccessKeyConfig>, -) where - N::TransactionRequest: FoundryTransactionBuilder, -{ - if let Some(config) = config { - tx.set_from(config.wallet_address); - tx.set_key_id(config.key_address); - } -} - -/// Sends a Tempo transaction using access key (keychain V2 mode). -/// -/// Signs the transaction with the access key and sends it via `send_raw_transaction`, -/// bypassing `EthereumWallet`. Only includes `key_authorization` if the key is not yet -/// provisioned on-chain. -async fn send_tempo_keychain>( - provider: &P, - tx: ::TransactionRequest, - signer: &WalletSigner, - access_key: &TempoAccessKeyConfig, - cast_async: bool, - confirmations: u64, - timeout: u64, -) -> eyre::Result<()> { - let raw_tx = tx - .sign_with_access_key( - provider, - signer, - access_key.wallet_address, - access_key.key_address, - access_key.key_authorization.as_ref(), - ) - .await?; - - let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); - - let cast = crate::tx::CastTxSender::new(provider); - cast.print_tx_result(tx_hash, cast_async, confirmations, timeout).await -} diff --git a/crates/cast/src/cmd/keychain.rs b/crates/cast/src/cmd/keychain.rs index 086d64e7c73e7..8b7d80786dfad 100644 --- a/crates/cast/src/cmd/keychain.rs +++ b/crates/cast/src/cmd/keychain.rs @@ -1,7 +1,7 @@ use alloy_ens::NameOrAddress; use alloy_network::EthereumWallet; use alloy_primitives::{Address, U256, hex, keccak256}; -use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; +use alloy_provider::ProviderBuilder as AlloyProviderBuilder; use alloy_signer::Signer; use alloy_sol_types::SolCall; use chrono::DateTime; @@ -12,7 +12,6 @@ use foundry_cli::{ utils::LoadConfig, }; use foundry_common::{ - FoundryTransactionBuilder, provider::ProviderBuilder, shell, tempo::{self, KeyType, KeysFile, WalletType, read_tempo_keys_file, tempo_keys_path}, @@ -29,7 +28,10 @@ use tempo_contracts::precompiles::{ }; use yansi::Paint; -use crate::tx::{CastTxBuilder, CastTxSender, SendTxOpts}; +use crate::{ + cmd::send::{cast_send, cast_send_with_access_key}, + tx::{CastTxBuilder, SendTxOpts}, +}; /// Tempo keychain management commands. /// @@ -582,7 +584,7 @@ async fn run_authorize( let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; - let calldata = if is_hardfork_active(&provider, TempoHardfork::T3).await { + let calldata = if provider.is_hardfork_active(TempoHardfork::T3).await? { // T3+ authorizeKey(address,SignatureType,KeyRestrictions) let restrictions = KeyRestrictions { expiry, @@ -632,7 +634,7 @@ async fn run_remaining_limit( let config = rpc.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; - let remaining: U256 = if is_hardfork_active(&provider, TempoHardfork::T3).await { + let remaining: U256 = if provider.is_hardfork_active(TempoHardfork::T3).await? { provider.get_keychain_remaining_limit(wallet_address, key_address, token).await? } else { // Pre-T3: use the legacy getRemainingLimit(address,address,address) @@ -693,22 +695,6 @@ async fn run_remove_scope( send_keychain_tx(calldata, tx_opts, &send_tx).await } -/// Returns `true` when the connected Tempo chain has the given hardfork active. -async fn is_hardfork_active>( - provider: &P, - hardfork: TempoHardfork, -) -> bool { - let Ok(chain_id) = provider.get_chain_id().await else { return true }; - let block_ts = provider - .get_block(Default::default()) - .await - .ok() - .flatten() - .map(|b| b.header.inner.inner.inner.timestamp) - .unwrap_or(0); - TempoHardfork::from_chain_and_timestamp(chain_id, block_ts).is_none_or(|h| h >= hardfork) -} - /// Shared helper to send a keychain precompile transaction. async fn send_keychain_tx( calldata: Vec, @@ -734,23 +720,19 @@ async fn send_keychain_tx( .await?; if let Some(ref ak) = tempo_access_key { - let signer = signer.as_ref().expect("signer required for access key"); - let from = ak.wallet_address; - let (tx, _) = builder.build(from).await?; - - let raw_tx = tx - .sign_with_access_key( - &provider, - signer, - ak.wallet_address, - ak.key_address, - ak.key_authorization.as_ref(), - ) - .await?; - - let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); - let cast = CastTxSender::new(&provider); - cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await?; + let signer = + signer.as_ref().ok_or_else(|| eyre::eyre!("signer required for access key"))?; + let (tx, _) = builder.build(ak.wallet_address).await?; + cast_send_with_access_key( + &provider, + tx, + signer, + ak, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await?; } else { let signer = match signer { Some(s) => s, @@ -764,10 +746,8 @@ async fn send_keychain_tx( .wallet(wallet) .connect_provider(&provider); - let cast = CastTxSender::new(provider); - let pending_tx = cast.send(tx).await?; - let tx_hash = *pending_tx.inner().tx_hash(); - cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await?; + cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) + .await?; } Ok(()) diff --git a/crates/cast/src/cmd/miner.rs b/crates/cast/src/cmd/miner.rs new file mode 100644 index 0000000000000..346efb9a503a0 --- /dev/null +++ b/crates/cast/src/cmd/miner.rs @@ -0,0 +1,52 @@ +use alloy_primitives::B256; +use std::sync::{ + Arc, + atomic::{AtomicBool, Ordering}, +}; + +/// Mines a salt by iterating B256 values in parallel until `check` returns `Some`. +/// +/// Each of the `n_threads` threads starts at `salt + thread_index` and steps by `n_threads`, +/// ensuring non-overlapping coverage. Returns `None` only if all threads panicked. +pub(crate) fn mine_salt(salt: B256, n_threads: usize, check: F) -> Option +where + T: Send + 'static, + F: FnMut(B256) -> Option + Clone + Send + 'static, +{ + let found = Arc::new(AtomicBool::new(false)); + let mut handles = Vec::with_capacity(n_threads); + + for i in 0..n_threads { + let increment = n_threads; + let found = Arc::clone(&found); + let mut check = check.clone(); + + handles.push(std::thread::spawn(move || { + #[repr(C)] + struct B256Aligned(B256, [usize; 0]); + + let mut salt = B256Aligned(salt, []); + // SAFETY: `B256` is aligned to `usize`. + let salt_word = unsafe { + &mut *salt.0.as_mut_ptr().add(32 - usize::BITS as usize / 8).cast::() + }; + // Important: offset by thread index to avoid duplicate work across threads. + *salt_word = salt_word.wrapping_add(i); + + loop { + if found.load(Ordering::Relaxed) { + break None; + } + + if let Some(result) = check(salt.0) { + found.store(true, Ordering::Relaxed); + break Some(result); + } + + *salt_word = salt_word.wrapping_add(increment); + } + })); + } + + handles.into_iter().find_map(|h| h.join().ok().flatten()) +} diff --git a/crates/cast/src/cmd/mod.rs b/crates/cast/src/cmd/mod.rs index 66f88a68f40a1..6a9d11f5dc61d 100644 --- a/crates/cast/src/cmd/mod.rs +++ b/crates/cast/src/cmd/mod.rs @@ -22,6 +22,7 @@ pub mod find_block; pub mod interface; pub mod keychain; pub mod logs; +pub(crate) mod miner; pub mod mktx; pub mod rpc; pub mod run; diff --git a/crates/cast/src/cmd/send.rs b/crates/cast/src/cmd/send.rs index 1080286605099..08c535bef8a18 100644 --- a/crates/cast/src/cmd/send.rs +++ b/crates/cast/src/cmd/send.rs @@ -2,7 +2,7 @@ use std::{path::PathBuf, str::FromStr, time::Duration}; use alloy_consensus::{SignableTransaction, Signed}; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network}; +use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder}; use alloy_primitives::Address; use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::{Signature, Signer}; @@ -271,24 +271,17 @@ impl SendTxArgs { Some(s) => s, None => send_tx.eth.wallet.signer().await?, }; - let from = ak.wallet_address; - - let (tx_request, _) = builder.build(from).await?; - - let raw_tx = tx_request - .sign_with_access_key( - &provider, - &signer, - ak.wallet_address, - ak.key_address, - ak.key_authorization.as_ref(), - ) - .await?; - - let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); - - let cast = CastTxSender::new(&provider); - cast.print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout).await + let (tx_request, _) = builder.build(ak.wallet_address).await?; + cast_send_with_access_key( + &provider, + tx_request, + &signer, + &ak, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await // Case 4: // 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 @@ -354,3 +347,37 @@ where Ok(()) } + +/// Signs a transaction with a Tempo access key and sends it via `send_raw_transaction`. +/// +/// Sets `from` and `key_id` on the transaction before signing, making it idempotent for txs built +/// with [`CastTxBuilder`] (fields already set) and also with sol!-bindings (fields not yet set). +/// +/// NOTE: The default implementation returns an error. Only `TempoNetwork` supports this. +pub(crate) async fn cast_send_with_access_key>( + provider: &P, + mut tx: N::TransactionRequest, + signer: &WalletSigner, + access_key: &TempoAccessKeyConfig, + cast_async: bool, + confirmations: u64, + timeout: u64, +) -> Result<()> +where + N::TransactionRequest: FoundryTransactionBuilder, + N::ReceiptResponse: UIfmt + UIfmtReceiptExt, +{ + tx.set_from(access_key.wallet_address); + tx.set_key_id(access_key.key_address); + let raw_tx = tx + .sign_with_access_key( + provider, + signer, + access_key.wallet_address, + access_key.key_address, + access_key.key_authorization.as_ref(), + ) + .await?; + let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); + CastTxSender::new(provider).print_tx_result(tx_hash, cast_async, confirmations, timeout).await +} diff --git a/crates/cast/src/cmd/tip20.rs b/crates/cast/src/cmd/tip20.rs deleted file mode 100644 index fc1cb6c9bec5e..0000000000000 --- a/crates/cast/src/cmd/tip20.rs +++ /dev/null @@ -1,243 +0,0 @@ -use crate::{ - cmd::{erc20::build_provider_with_signer, send::cast_send}, - tx::{CastTxSender, SendTxOpts}, -}; -use alloy_ens::NameOrAddress; -use alloy_network::{Network, TransactionBuilder}; -use alloy_primitives::{B256, U256}; -use alloy_provider::Provider; -use alloy_sol_types::sol; -use clap::{Args, Parser}; -use foundry_cli::{ - opts::{RpcOpts, TempoOpts}, - utils::{LoadConfig, get_chain}, -}; -use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; -use std::str::FromStr; -use tempo_alloy::TempoNetwork; -use tempo_contracts::precompiles::{TIP20_FACTORY_ADDRESS, is_iso4217_currency}; - -sol! { - #[sol(rpc)] - interface ITIP20Factory { - function createToken( - string memory name, - string memory symbol, - string memory currency, - address quoteToken, - address admin, - bytes32 salt - ) external returns (address token); - } -} - -/// Returns a warning message for non-ISO 4217 currency codes used in TIP-20 token creation. -pub(crate) fn iso4217_warning_message(currency: &str) -> String { - let hyperlink = |url: &str| format!("\x1b]8;;{url}\x1b\\{url}\x1b]8;;\x1b\\"); - let tip20_docs = hyperlink("https://docs.tempo.xyz/protocol/tip20/overview"); - let iso_docs = hyperlink("https://www.iso.org/iso-4217-currency-codes.html"); - - format!( - "\"{currency}\" is not a recognized ISO 4217 currency code.\n\ - \n\ - If the token you are trying to deploy is a fiat-backed stablecoin, Tempo strongly\n\ - recommends that the currency code field be the ISO-4217 currency code of the fiat\n\ - currency your token tracks (e.g. \"USD\", \"EUR\", \"GBP\").\n\ - \n\ - The currency field is IMMUTABLE after token creation and affects fee payment\n\ - eligibility, DEX routing, and quote token pairing. Only \"USD\"-denominated tokens\n\ - can be used to pay transaction fees on Tempo.\n\ - \n\ - Learn more:\n \ - - Tempo TIP-20 docs: {tip20_docs}\n \ - - ISO 4217 standard: {iso_docs}" - ) -} - -/// TIP-20 token operations (Tempo). -#[derive(Debug, Parser, Clone)] -pub enum Tip20Subcommand { - /// Create a new TIP-20 token via the TIP20Factory. - #[command(visible_alias = "c")] - Create { - /// The token name (e.g. "US Dollar Coin"). - name: String, - - /// The token symbol (e.g. "USDC"). - symbol: String, - - /// The ISO 4217 currency code (e.g. "USD", "EUR", "GBP"). - /// This field is IMMUTABLE after creation and affects fee payment - /// eligibility, DEX routing, and quote token pairing. - currency: String, - - /// The TIP-20 quote token address used for exchange pricing. - #[arg(value_parser = NameOrAddress::from_str)] - quote_token: NameOrAddress, - - /// The admin address to receive DEFAULT_ADMIN_ROLE on the new token. - #[arg(value_parser = NameOrAddress::from_str)] - admin: NameOrAddress, - - /// A unique salt for deterministic address derivation (hex-encoded bytes32). - salt: B256, - - /// Skip the ISO 4217 currency code validation warning. - #[arg(long)] - force: bool, - - #[command(flatten)] - send_tx: SendTxOpts, - - #[command(flatten)] - tx: Tip20TxOpts, - }, -} - -/// Transaction options for TIP-20 operations. -#[derive(Debug, Clone, Args)] -#[command(next_help_heading = "Transaction options")] -pub struct Tip20TxOpts { - /// Gas limit for the transaction. - #[arg(long)] - pub gas_limit: Option, - - /// Gas price or max fee per gas for the transaction. - #[arg(long)] - pub gas_price: Option, - - /// Max priority fee per gas (EIP-1559). - #[arg(long)] - pub priority_gas_price: Option, - - /// Nonce for the transaction. - #[arg(long)] - pub nonce: Option, - - #[command(flatten)] - pub tempo: TempoOpts, -} - -impl Tip20TxOpts { - /// Applies gas, fee, nonce, and Tempo options to a transaction request. - fn apply(&self, tx: &mut N::TransactionRequest, legacy: bool) - where - N::TransactionRequest: FoundryTransactionBuilder, - { - if let Some(gas_limit) = self.gas_limit { - tx.set_gas_limit(gas_limit.to()); - } - - if let Some(gas_price) = self.gas_price { - if legacy { - tx.set_gas_price(gas_price.to()); - } else { - tx.set_max_fee_per_gas(gas_price.to()); - } - } - - if !legacy && let Some(priority_fee) = self.priority_gas_price { - tx.set_max_priority_fee_per_gas(priority_fee.to()); - } - - self.tempo.apply::(tx, self.nonce.map(|n| n.to())); - } -} - -impl Tip20Subcommand { - const fn rpc_opts(&self) -> &RpcOpts { - match self { - Self::Create { send_tx, .. } => &send_tx.eth.rpc, - } - } - - pub async fn run(self) -> eyre::Result<()> { - let (signer, tempo_access_key) = match &self { - Self::Create { send_tx, .. } => { - if send_tx.eth.wallet.from.is_some() { - send_tx.eth.wallet.maybe_signer().await? - } else { - (None, None) - } - } - }; - - let config = self.rpc_opts().load_config()?; - - match self { - Self::Create { - name, - symbol, - currency, - quote_token, - admin, - salt, - force, - send_tx, - tx: tx_opts, - } => { - if !is_iso4217_currency(¤cy) && !force { - sh_warn!("{}", iso4217_warning_message(¤cy))?; - let response: String = foundry_common::prompt!("\nContinue anyway? [y/N] ")?; - if !matches!(response.trim(), "y" | "Y") { - sh_println!("Aborted.")?; - return Ok(()); - } - } - - let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); - let provider = ProviderBuilder::::from_config(&config)?.build()?; - let quote_token_addr = quote_token.resolve(&provider).await?; - let admin_addr = admin.resolve(&provider).await?; - - let mut tx = ITIP20Factory::new(TIP20_FACTORY_ADDRESS, &provider) - .createToken(name, symbol, currency, quote_token_addr, admin_addr, salt) - .into_transaction_request(); - - tx_opts.apply::( - &mut tx, - get_chain(config.chain, &provider).await?.is_legacy(), - ); - - if let Some(ref access_key) = tempo_access_key { - let signer = signer.as_ref().expect("signer required for access key"); - tx.set_from(access_key.wallet_address); - tx.set_key_id(access_key.key_address); - - let raw_tx = tx - .sign_with_access_key( - &provider, - signer, - access_key.wallet_address, - access_key.key_address, - access_key.key_authorization.as_ref(), - ) - .await?; - - let tx_hash = *provider.send_raw_transaction(&raw_tx).await?.tx_hash(); - let cast = CastTxSender::new(&provider); - cast.print_tx_result( - tx_hash, - send_tx.cast_async, - send_tx.confirmations, - timeout, - ) - .await? - } else { - let signer = signer.unwrap_or(send_tx.eth.wallet.signer().await?); - let provider = build_provider_with_signer::(&send_tx, signer)?; - cast_send( - provider, - tx, - send_tx.cast_async, - send_tx.sync, - send_tx.confirmations, - timeout, - ) - .await? - } - } - }; - Ok(()) - } -} diff --git a/crates/cast/src/cmd/tip20/create.rs b/crates/cast/src/cmd/tip20/create.rs new file mode 100644 index 0000000000000..3417674f1d470 --- /dev/null +++ b/crates/cast/src/cmd/tip20/create.rs @@ -0,0 +1,113 @@ +use crate::{ + cmd::{ + erc20::build_provider_with_signer, + send::{cast_send, cast_send_with_access_key}, + }, + tx::{SendTxOpts, TxParams}, +}; +use alloy_ens::NameOrAddress; +use alloy_primitives::B256; +use alloy_sol_types::sol; +use foundry_cli::utils::{LoadConfig, get_chain}; +use foundry_common::provider::ProviderBuilder; +use tempo_alloy::TempoNetwork; +use tempo_contracts::precompiles::{TIP20_FACTORY_ADDRESS, is_iso4217_currency}; + +sol! { + #[sol(rpc)] + interface ITIP20Factory { + function createToken( + string memory name, + string memory symbol, + string memory currency, + address quoteToken, + address admin, + bytes32 salt + ) external returns (address token); + } +} + +/// Returns a warning message for non-ISO 4217 currency codes used in TIP-20 token creation. +pub(crate) fn iso4217_warning_message(currency: &str) -> String { + let hyperlink = |url: &str| format!("\x1b]8;;{url}\x1b\\{url}\x1b]8;;\x1b\\"); + let tip20_docs = hyperlink("https://docs.tempo.xyz/protocol/tip20/overview"); + let iso_docs = hyperlink("https://www.iso.org/iso-4217-currency-codes.html"); + + format!( + "\"{currency}\" is not a recognized ISO 4217 currency code.\n\ + \n\ + If the token you are trying to deploy is a fiat-backed stablecoin, Tempo strongly\n\ + recommends that the currency code field be the ISO-4217 currency code of the fiat\n\ + currency your token tracks (e.g. \"USD\", \"EUR\", \"GBP\").\n\ + \n\ + The currency field is IMMUTABLE after token creation and affects fee payment\n\ + eligibility, DEX routing, and quote token pairing. Only \"USD\"-denominated tokens\n\ + can be used to pay transaction fees on Tempo.\n\ + \n\ + Learn more:\n \ + - Tempo TIP-20 docs: {tip20_docs}\n \ + - ISO 4217 standard: {iso_docs}" + ) +} + +#[allow(clippy::too_many_arguments)] +pub(super) async fn run( + name: String, + symbol: String, + currency: String, + quote_token: NameOrAddress, + admin: NameOrAddress, + salt: B256, + force: bool, + send_tx: SendTxOpts, + tx_opts: TxParams, +) -> eyre::Result<()> { + let (signer, tempo_access_key) = if send_tx.eth.wallet.from.is_some() { + send_tx.eth.wallet.maybe_signer().await? + } else { + (None, None) + }; + + let config = send_tx.eth.rpc.load_config()?; + + if !is_iso4217_currency(¤cy) && !force { + sh_warn!("{}", super::iso4217_warning_message(¤cy))?; + let response: String = foundry_common::prompt!("\nContinue anyway? [y/N] ")?; + if !matches!(response.trim(), "y" | "Y") { + sh_println!("Aborted.")?; + return Ok(()); + } + } + + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let quote_token_addr = quote_token.resolve(&provider).await?; + let admin_addr = admin.resolve(&provider).await?; + + let mut tx = ITIP20Factory::new(TIP20_FACTORY_ADDRESS, &provider) + .createToken(name, symbol, currency, quote_token_addr, admin_addr, salt) + .into_transaction_request(); + + tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); + + if let Some(ref access_key) = tempo_access_key { + let signer = signer.as_ref().ok_or_else(|| eyre::eyre!("access key requires a signer"))?; + cast_send_with_access_key( + &provider, + tx, + signer, + access_key, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await?; + } else { + let signer = signer.unwrap_or(send_tx.eth.wallet.signer().await?); + let provider = build_provider_with_signer::(&send_tx, signer)?; + cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) + .await?; + } + + Ok(()) +} diff --git a/crates/cast/src/cmd/tip20/mine.rs b/crates/cast/src/cmd/tip20/mine.rs new file mode 100644 index 0000000000000..a5f9062482a01 --- /dev/null +++ b/crates/cast/src/cmd/tip20/mine.rs @@ -0,0 +1,249 @@ +use crate::{ + cmd::{ + erc20::build_provider_with_signer, + send::{cast_send, cast_send_with_access_key}, + }, + tx::{SendTxOpts, TxParams}, +}; +use alloy_primitives::{Address, B256, keccak256}; +use alloy_signer::Signer; +use eyre::Result; +use foundry_cli::utils::{LoadConfig, get_chain}; +use foundry_common::provider::ProviderBuilder; +use rand::{RngCore, SeedableRng, rngs::StdRng}; +use std::time::{Duration, Instant}; +use tempo_alloy::{ + TempoNetwork, + contracts::precompiles::{ADDRESS_REGISTRY_ADDRESS, IAddressRegistry}, +}; +use tempo_primitives::{MasterId, TempoAddressExt, UserTag}; + +const POW_BYTES: usize = 4; + +pub(super) struct Output { + pub(super) salt: B256, + pub(super) registration_hash: B256, + pub(super) master_id: MasterId, + pub(super) zero_tag_virtual_address: Address, +} + +pub(super) fn run( + master: Address, + salt: Option, + threads: Option, + seed: Option, + no_random: bool, +) -> Result { + if !master.is_valid_master() { + eyre::bail!( + "invalid master address {master}; see https://docs.tempo.xyz/protocol/tips/tip-1022" + ); + } + + if let Some(salt) = salt { + let output = derive(master, salt); + if !has_pow(&output.registration_hash, POW_BYTES) { + eyre::bail!( + "provided salt does not satisfy TIP-1022 proof of work: {}", + output.registration_hash + ); + } + print_output(&output, None)?; + return Ok(output); + } + + let mut n_threads = threads.unwrap_or(0); + if n_threads == 0 { + n_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); + } + + let mut salt = B256::ZERO; + if !no_random { + let mut rng = match seed { + Some(seed) => StdRng::from_seed(seed.0), + None => StdRng::from_os_rng(), + }; + rng.fill_bytes(&mut salt[..]); + } + + sh_println!("Mining TIP-1022 salt for {master} with {n_threads} threads...")?; + + let timer = Instant::now(); + let output = mine(master, salt, n_threads, POW_BYTES)?; + print_output(&output, Some(timer.elapsed()))?; + Ok(output) +} + +pub(super) async fn register( + master: Address, + salt: B256, + send_tx: SendTxOpts, + tx_opts: TxParams, +) -> Result<()> { + let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; + let signer = signer.ok_or_else(|| { + eyre::eyre!( + "--register requires a signer or Tempo keychain identity (for example --private-key or --from)" + ) + })?; + + let sender = + tempo_access_key.as_ref().map(|ak| ak.wallet_address).unwrap_or_else(|| signer.address()); + + if sender != master { + eyre::bail!( + "registration sender mismatch: mined salt is for {master}, but the configured signer would register as {sender}" + ); + } + + let config = send_tx.eth.load_config()?; + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + let mut tx = IAddressRegistry::new(ADDRESS_REGISTRY_ADDRESS, &provider) + .registerVirtualMaster(salt) + .into_transaction_request(); + tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); + + sh_println!("Submitting registerVirtualMaster({salt}) on Tempo...")?; + + if let Some(ref access_key) = tempo_access_key { + cast_send_with_access_key( + &provider, + tx, + &signer, + access_key, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await?; + } else { + let provider = build_provider_with_signer::(&send_tx, signer)?; + cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) + .await?; + } + + Ok(()) +} + +fn mine(master: Address, salt: B256, n_threads: usize, pow_bytes: usize) -> Result { + let mut packed = [0u8; 52]; + packed[..20].copy_from_slice(master.as_slice()); + + crate::cmd::miner::mine_salt(salt, n_threads, move |salt| { + packed[20..].copy_from_slice(salt.as_slice()); + let registration_hash = keccak256(packed); + + has_pow(®istration_hash, pow_bytes).then(|| { + let master_id = MasterId::from_slice(®istration_hash[4..8]); + let zero_tag_virtual_address = Address::new_virtual(master_id, UserTag::ZERO); + Output { salt, registration_hash, master_id, zero_tag_virtual_address } + }) + }) + .ok_or_else(|| eyre::eyre!("virtual master mining failed: all threads panicked")) +} + +fn derive(master: Address, salt: B256) -> Output { + let registration_hash = registration_hash(master, salt); + let master_id = MasterId::from_slice(®istration_hash[4..8]); + let zero_tag_virtual_address = Address::new_virtual(master_id, UserTag::ZERO); + + Output { salt, registration_hash, master_id, zero_tag_virtual_address } +} + +fn registration_hash(master: Address, salt: B256) -> B256 { + let mut packed = [0u8; 52]; + packed[..20].copy_from_slice(master.as_slice()); + packed[20..].copy_from_slice(salt.as_slice()); + keccak256(packed) +} + +fn has_pow(registration_hash: &B256, pow_bytes: usize) -> bool { + registration_hash[..pow_bytes].iter().all(|byte| *byte == 0) +} + +fn print_output(output: &Output, elapsed: Option) -> Result<()> { + let header = if let Some(elapsed) = elapsed { + format!("Found salt in {elapsed:?}\n") + } else { + String::new() + }; + + sh_println!( + r#"{header}Salt: {} +Registration hash: {} +Master ID: {} +Zero-tag address: {}"#, + output.salt, + output.registration_hash, + output.master_id, + output.zero_tag_virtual_address, + )?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::{address, b256}; + + #[test] + fn derives_master_id_and_zero_tag_address() { + let master = address!("0x1234567890123456789012345678901234567890"); + let salt = b256!("0x0000000000000000000000000000000000000000000000000000000000000001"); + let output = derive(master, salt); + + assert_eq!( + output.registration_hash, + b256!("0x661db5481211842e0330ea3e4cf0b4e7e5abd2314161ce16e9a99e7460480f21"), + ); + assert_eq!(output.master_id, MasterId::from([0x12, 0x11, 0x84, 0x2e])); + assert_eq!( + output.zero_tag_virtual_address, + address!("0x1211842efdfdfdfdfdfdfdfdfdfd000000000000"), + ); + assert_eq!(output.master_id, MasterId::from_slice(&output.registration_hash[4..8])); + assert_eq!( + output.zero_tag_virtual_address, + Address::new_virtual(output.master_id, UserTag::ZERO), + ); + } + + #[test] + fn mines_pow_with_reduced_difficulty() -> Result<()> { + let master = address!("0x1234567890123456789012345678901234567890"); + let output = mine(master, B256::ZERO, 1, 1)?; + + assert_eq!( + output.salt, + b256!("0x000000000000000000000000000000000000000000000000f301000000000000"), + ); + assert_eq!(output.registration_hash[0], 0); + assert_eq!(output.master_id, MasterId::from_slice(&output.registration_hash[4..8])); + assert_eq!( + output.zero_tag_virtual_address, + Address::new_virtual(output.master_id, UserTag::ZERO), + ); + Ok(()) + } + + #[test] + fn has_pow_checks_leading_zero_bytes() { + let mut hash = B256::ZERO; + assert!(has_pow(&hash, 4)); + assert!(has_pow(&hash, 0)); + + hash[3] = 1; + assert!(!has_pow(&hash, 4)); + assert!(has_pow(&hash, 3)); + assert!(has_pow(&hash, 0)); + } + + #[test] + fn rejects_invalid_master_addresses() { + assert!(!Address::ZERO.is_valid_master()); + assert!(!address!("0x00000000fdfdfdfdfdfdfdfdfdfd000000000001").is_valid_master()); + assert!(!address!("0x20c0000000000000000000000000000000000001").is_valid_master()); + } +} diff --git a/crates/cast/src/cmd/tip20/mod.rs b/crates/cast/src/cmd/tip20/mod.rs new file mode 100644 index 0000000000000..e3c39b2c9bb18 --- /dev/null +++ b/crates/cast/src/cmd/tip20/mod.rs @@ -0,0 +1,111 @@ +use crate::tx::{SendTxOpts, TxParams}; +use alloy_ens::NameOrAddress; +use alloy_primitives::{Address, B256}; +use clap::Parser; +use std::str::FromStr; + +mod create; +pub(crate) use create::iso4217_warning_message; +mod mine; + +/// TIP-20 token operations (Tempo). +#[derive(Debug, Parser, Clone)] +pub enum Tip20Subcommand { + /// Create a new TIP-20 token via the TIP20Factory. + #[command(visible_alias = "c")] + Create { + /// The token name (e.g. "US Dollar Coin"). + name: String, + + /// The token symbol (e.g. "USDC"). + symbol: String, + + /// The ISO 4217 currency code (e.g. "USD", "EUR", "GBP"). + /// This field is IMMUTABLE after creation and affects fee payment + /// eligibility, DEX routing, and quote token pairing. + currency: String, + + /// The TIP-20 quote token address used for exchange pricing. + #[arg(value_parser = NameOrAddress::from_str)] + quote_token: NameOrAddress, + + /// The admin address to receive DEFAULT_ADMIN_ROLE on the new token. + #[arg(value_parser = NameOrAddress::from_str)] + admin: NameOrAddress, + + /// A unique salt for deterministic address derivation (hex-encoded bytes32). + salt: B256, + + /// Skip the ISO 4217 currency code validation warning. + #[arg(long)] + force: bool, + + #[command(flatten)] + send_tx: SendTxOpts, + + #[command(flatten)] + tx: TxParams, + }, + + /// Mine a TIP-1022 salt for virtual address' master registration on Tempo. + #[command(visible_alias = "m")] + Mine { + /// Address that will call `registerVirtualMaster(bytes32)`. + #[arg(value_name = "ADDRESS")] + master: Address, + + /// Salt to validate directly instead of mining one. + #[arg(long, conflicts_with_all = ["seed", "no_random"], value_name = "HEX")] + salt: Option, + + /// Number of threads to use. Specifying 0 defaults to the number of logical cores. + #[arg(global = true, long, short = 'j', visible_alias = "jobs")] + threads: Option, + + /// The random number generator's seed, used to initialize the salt search. + #[arg(long, value_name = "HEX")] + seed: Option, + + /// Don't initialize the salt with a random value, and instead use the default value of 0. + #[arg(long, conflicts_with = "seed")] + no_random: bool, + + /// Submit `registerVirtualMaster(bytes32)` on Tempo after finding or validating the salt. + #[arg(long, conflicts_with_all = ["seed", "no_random"])] + register: bool, + + #[command(flatten)] + send_tx: SendTxOpts, + + #[command(flatten)] + tx: TxParams, + }, +} + +impl Tip20Subcommand { + pub async fn run(self) -> eyre::Result<()> { + match self { + Self::Create { + name, + symbol, + currency, + quote_token, + admin, + salt, + force, + send_tx, + tx, + } => { + create::run(name, symbol, currency, quote_token, admin, salt, force, send_tx, tx) + .await?; + } + Self::Mine { master, salt, threads, seed, no_random, register, send_tx, tx } => { + let output = mine::run(master, salt, threads, seed, no_random)?; + if register { + mine::register(master, output.salt, send_tx, tx).await?; + } + } + } + Ok(()) + } +} diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index ce5572acebc13..dedd15e460728 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -7,11 +7,7 @@ extern crate foundry_common; #[macro_use] extern crate tracing; - -use alloy_consensus::{ - BlockHeader, - transaction::{Recovered, SignerRecoverable}, -}; +use alloy_consensus::BlockHeader; use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; use alloy_eips::Encodable2718; use alloy_ens::NameOrAddress; @@ -22,7 +18,7 @@ use alloy_primitives::{ utils::{ParseUnits, Unit, keccak256}, }; use alloy_provider::{PendingTransactionBuilder, Provider, network::eip2718::Decodable2718}; -use alloy_rlp::{Decodable, Encodable}; +use alloy_rlp::Decodable; use alloy_rpc_types::{ BlockId, BlockNumberOrTag, BlockOverrides, Filter, FilterBlockOption, Log, state::StateOverride, }; @@ -35,12 +31,14 @@ use foundry_common::{ compile::etherscan_project, flatten, fmt::*, - fs, shell, + fs, + provider::ProviderBuilder, + shell, }; -use foundry_config::Chain; +use foundry_config::{Chain, Config}; use foundry_evm::core::bytecode::InstIter; +use foundry_primitives::{FoundryNetwork, FoundryTxEnvelope}; use futures::{FutureExt, StreamExt, future::Either}; -use op_alloy_consensus as _; use rayon::prelude::*; use serde::Serialize; @@ -62,7 +60,6 @@ pub mod cmd; pub mod opts; pub mod base; -pub mod call_spec; pub(crate) mod debug; pub mod errors; mod rlp_converter; @@ -70,6 +67,8 @@ pub mod tx; use rlp_converter::Item; +use crate::rlp_converter::TryIntoRlpEncodable; + // TODO: CastContract with common contract initializers? Same for CastProviders? pub struct Cast { @@ -77,7 +76,14 @@ pub struct Cast { _phantom: PhantomData, } -impl + Clone + Unpin, N: Network> Cast { +impl + Clone + Unpin, N: Network> Cast +where + N::TxEnvelope: Serialize + UIfmtSignatureExt, + N::Header: TryIntoRlpEncodable, + N::TransactionResponse: UIfmt, + N::HeaderResponse: UIfmtHeaderExt, + N::BlockResponse: UIfmt, +{ /// Creates a new Cast instance from the provided client /// /// # Example @@ -93,7 +99,7 @@ impl + Clone + Unpin, N: Network> Cast { /// # Ok(()) /// # } /// ``` - pub const fn new(provider: P) -> Self { + pub fn new(provider: P) -> Self { Self { provider, _phantom: PhantomData } } @@ -155,9 +161,11 @@ impl + Clone + Unpin, N: Network> Cast { } let res = call.await?; - let decoded = if let Some(func) = func { + let mut decoded = vec![]; + + if let Some(func) = func { // decode args into tokens - match func.abi_decode_output(res.as_ref()) { + decoded = match func.abi_decode_output(res.as_ref()) { Ok(decoded) => decoded, Err(err) => { // ensure the address is a contract @@ -183,10 +191,8 @@ impl + Clone + Unpin, N: Network> Cast { "could not decode output; did you specify the wrong function return data type?" ); } - } - } else { - vec![] - }; + }; + } // handle case when return type is not specified Ok(if decoded.is_empty() { @@ -288,6 +294,160 @@ impl + Clone + Unpin, N: Network> Cast { Ok(res) } + /// # Example + /// + /// ``` + /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; + /// use cast::Cast; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let block = cast.block(5, true, vec![], false).await?; + /// println!("{}", block); + /// # Ok(()) + /// # } + /// ``` + pub async fn block>( + &self, + block: B, + full: bool, + fields: Vec, + raw: bool, + ) -> Result { + let block = block.into(); + if fields.contains(&"transactions".into()) && !full { + eyre::bail!("use --full to view transactions") + } + + let block = self + .provider + .get_block(block) + .kind(full.into()) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; + + Ok(if raw { + let encoded = block.header().as_ref().try_rlp_encode()?; + format!("0x{}", hex::encode(encoded)) + } else if !fields.is_empty() { + let mut result = String::new(); + for field in fields { + result.push_str( + &get_pretty_block_attr::(&block, &field) + .unwrap_or_else(|| format!("{field} is not a valid block field")), + ); + + result.push('\n'); + } + result.trim_end().to_string() + } else if shell::is_json() { + serde_json::to_value(&block).unwrap().to_string() + } else { + block.pretty() + }) + } + + async fn block_field_as_num>(&self, block: B, field: String) -> Result { + Self::block( + self, + block.into(), + false, + // Select only select field + vec![field], + false, + ) + .await? + .parse() + .map_err(Into::into) + } + + pub async fn base_fee>(&self, block: B) -> Result { + Self::block_field_as_num(self, block, String::from("baseFeePerGas")).await + } + + pub async fn age>(&self, block: B) -> Result { + let timestamp_str = + 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 { + Self::block_field_as_num(self, block, "timestamp".to_string()).await + } + + pub async fn chain(&self) -> Result<&str> { + let genesis_hash = Self::block( + self, + 0, + false, + // Select only block hash + vec![String::from("hash")], + false, + ) + .await?; + + Ok(match &genesis_hash[..] { + "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { + match &(Self::block(self, 1920000, false, vec![String::from("hash")], false) + .await?)[..] + { + "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { + "etclive" + } + _ => "ethlive", + } + } + "0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9" => "kovan", + "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" => "ropsten", + "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" => { + "optimism-mainnet" + } + "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" => { + "optimism-goerli" + } + "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5" => { + "optimism-kovan" + } + "0x521982bd54239dc71269eefb58601762cc15cfb2978e0becb46af7962ed6bfaa" => "fraxtal", + "0x910f5c4084b63fd860d0c2f9a04615115a5a991254700b39ba072290dbd77489" => { + "fraxtal-testnet" + } + "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442" => { + "arbitrum-mainnet" + } + "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" => "morden", + "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177" => "rinkeby", + "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" => "goerli", + "0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4" => "kotti", + "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b" => "polygon-pos", + "0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697" => { + "polygon-pos-amoy-testnet" + } + "0x81005434635456a16f74ff7023fbe0bf423abbc8a8deb093ffff455c0ad3b741" => "polygon-zkevm", + "0x676c1a76a6c5855a32bdf7c61977a0d1510088a4eeac1330466453b3d08b60b9" => { + "polygon-zkevm-cardona-testnet" + } + "0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756" => "gnosis", + "0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a" => "chiado", + "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", + "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", + "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { + match &(Self::block(self, 1, false, vec![String::from("hash")], false).await?)[..] { + "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { + "avalanche-fuji" + } + _ => "avalanche", + } + } + "0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee" => "ink", + "0xe5fd5cf0be56af58ad5751b401410d6b7a09d830fa459789746a3d0dd1c79834" => "ink-sepolia", + _ => "unknown", + }) + } + pub async fn chain_id(&self) -> Result { Ok(self.provider.get_chain_id().await?) } @@ -550,6 +710,67 @@ impl + Clone + Unpin, N: Network> Cast { Ok(code.len().to_string()) } + /// # Example + /// + /// ``` + /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; + /// use cast::Cast; + /// + /// # async fn foo() -> eyre::Result<()> { + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; + /// let cast = Cast::new(provider); + /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; + /// let tx = cast.transaction(Some(tx_hash.to_string()), None, None, None, false).await?; + /// println!("{}", tx); + /// # Ok(()) + /// # } + /// ``` + pub async fn transaction( + &self, + tx_hash: Option, + from: Option, + nonce: Option, + field: Option, + to_request: bool, + ) -> Result { + let tx = if let Some(tx_hash) = tx_hash { + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; + self.provider + .get_transaction_by_hash(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))? + } else if let Some(from) = from { + // If nonce is not provided, uses 0. + let nonce = U64::from(nonce.unwrap_or_default()); + let from = from.resolve(self.provider.root()).await?; + + self.provider + .raw_request::<_, Option>( + "eth_getTransactionBySenderAndNonce".into(), + (from, nonce), + ) + .await? + .ok_or_else(|| { + eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::()) + })? + } else { + eyre::bail!("tx hash or from address is required") + }; + + Ok(if let Some(ref field) = field { + get_pretty_tx_attr::(&tx, field.as_str()) + .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? + } else if shell::is_json() { + // to_value first to sort json object keys + serde_json::to_value(&tx)?.to_string() + } else if to_request { + serde_json::to_string_pretty(&Into::::into(tx))? + } else { + tx.pretty() + }) + } + /// Perform a raw JSON-RPC request /// /// # Example @@ -885,264 +1106,6 @@ impl + Clone + Unpin, N: Network> Cast { } } -impl, N: Network> Cast -where - N::HeaderResponse: UIfmtHeaderExt, - N::BlockResponse: UIfmt, -{ - /// # Example - /// - /// ``` - /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; - /// use cast::Cast; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = - /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; - /// let cast = Cast::new(provider); - /// let block = cast.block(5, true, vec![]).await?; - /// println!("{}", block); - /// # Ok(()) - /// # } - /// ``` - pub async fn block>( - &self, - block: B, - full: bool, - fields: Vec, - ) -> Result { - let block = block.into(); - if fields.contains(&"transactions".into()) && !full { - eyre::bail!("use --full to view transactions") - } - - let block = self - .provider - .get_block(block) - .kind(full.into()) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; - - Ok(if !fields.is_empty() { - let mut result = String::new(); - for field in fields { - result.push_str( - &get_pretty_block_attr::(&block, &field) - .unwrap_or_else(|| format!("{field} is not a valid block field")), - ); - - result.push('\n'); - } - result.trim_end().to_string() - } else if shell::is_json() { - serde_json::to_value(&block).unwrap().to_string() - } else { - block.pretty() - }) - } - - async fn block_field_as_num>(&self, block: B, field: String) -> Result { - Self::block( - self, - block.into(), - false, - // Select only select field - vec![field], - ) - .await? - .parse() - .map_err(Into::into) - } - - pub async fn base_fee>(&self, block: B) -> Result { - Self::block_field_as_num(self, block, String::from("baseFeePerGas")).await - } - - pub async fn age>(&self, block: B) -> Result { - let timestamp_str = - 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 { - Self::block_field_as_num(self, block, "timestamp".to_string()).await - } - - pub async fn chain(&self) -> Result<&str> { - let genesis_hash = Self::block( - self, - 0, - false, - // Select only block hash - vec![String::from("hash")], - ) - .await?; - - Ok(match &genesis_hash[..] { - "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { - match &(Self::block(self, 1920000, false, vec![String::from("hash")]).await?)[..] { - "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { - "etclive" - } - _ => "ethlive", - } - } - "0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9" => "kovan", - "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d" => "ropsten", - "0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" => { - "optimism-mainnet" - } - "0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" => { - "optimism-goerli" - } - "0x02adc9b449ff5f2467b8c674ece7ff9b21319d76c4ad62a67a70d552655927e5" => { - "optimism-kovan" - } - "0x521982bd54239dc71269eefb58601762cc15cfb2978e0becb46af7962ed6bfaa" => "fraxtal", - "0x910f5c4084b63fd860d0c2f9a04615115a5a991254700b39ba072290dbd77489" => { - "fraxtal-testnet" - } - "0x7ee576b35482195fc49205cec9af72ce14f003b9ae69f6ba0faef4514be8b442" => { - "arbitrum-mainnet" - } - "0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303" => "morden", - "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177" => "rinkeby", - "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" => "goerli", - "0x14c2283285a88fe5fce9bf5c573ab03d6616695d717b12a127188bcacfc743c4" => "kotti", - "0xa9c28ce2141b56c474f1dc504bee9b01eb1bd7d1a507580d5519d4437a97de1b" => "polygon-pos", - "0x7202b2b53c5a0836e773e319d18922cc756dd67432f9a1f65352b61f4406c697" => { - "polygon-pos-amoy-testnet" - } - "0x81005434635456a16f74ff7023fbe0bf423abbc8a8deb093ffff455c0ad3b741" => "polygon-zkevm", - "0x676c1a76a6c5855a32bdf7c61977a0d1510088a4eeac1330466453b3d08b60b9" => { - "polygon-zkevm-cardona-testnet" - } - "0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756" => "gnosis", - "0xada44fd8d2ecab8b08f256af07ad3e777f17fb434f8f8e678b312f576212ba9a" => "chiado", - "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", - "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", - "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { - match &(Self::block(self, 1, false, vec![String::from("hash")]).await?)[..] { - "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { - "avalanche-fuji" - } - _ => "avalanche", - } - } - "0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee" => "ink", - "0xe5fd5cf0be56af58ad5751b401410d6b7a09d830fa459789746a3d0dd1c79834" => "ink-sepolia", - _ => "unknown", - }) - } -} - -impl, N: Network> Cast -where - N::Header: Encodable, -{ - /// # Example - /// - /// ``` - /// use alloy_provider::{ProviderBuilder, RootProvider, network::Ethereum}; - /// use cast::Cast; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = - /// ProviderBuilder::<_, _, Ethereum>::default().connect("http://localhost:8545").await?; - /// let cast = Cast::new(provider); - /// let block = cast.block_raw(5, true).await?; - /// println!("{}", block); - /// # Ok(()) - /// # } - /// ``` - pub async fn block_raw>(&self, block: B, full: bool) -> Result { - let block_id = block.into(); - - let block = self - .provider - .get_block(block_id) - .kind(full.into()) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block_id))?; - - let encoded = alloy_rlp::encode(block.header().as_ref()); - - Ok(format!("0x{}", hex::encode(encoded))) - } -} - -impl, N: Network> Cast -where - N::TxEnvelope: Serialize + UIfmtSignatureExt, - N::TransactionResponse: UIfmt, -{ - /// # Example - /// - /// ``` - /// use alloy_provider::{ProviderBuilder, RootProvider, network::AnyNetwork}; - /// use cast::Cast; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = - /// ProviderBuilder::<_, _, AnyNetwork>::default().connect("http://localhost:8545").await?; - /// let cast = Cast::new(provider); - /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; - /// let tx = cast.transaction(Some(tx_hash.to_string()), None, None, None, false, false).await?; - /// println!("{}", tx); - /// # Ok(()) - /// # } - /// ``` - pub async fn transaction( - &self, - tx_hash: Option, - from: Option, - nonce: Option, - field: Option, - raw: bool, - to_request: bool, - ) -> Result { - let tx = if let Some(tx_hash) = tx_hash { - let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; - self.provider - .get_transaction_by_hash(tx_hash) - .await? - .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))? - } else if let Some(from) = from { - // If nonce is not provided, uses 0. - let nonce = U64::from(nonce.unwrap_or_default()); - let from = from.resolve(self.provider.root()).await?; - - self.provider - .raw_request::<_, Option>( - "eth_getTransactionBySenderAndNonce".into(), - (from, nonce), - ) - .await? - .ok_or_else(|| { - eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::()) - })? - } else { - eyre::bail!("tx hash or from address is required") - }; - - Ok(if raw { - let encoded = tx.as_ref().encoded_2718(); - format!("0x{}", hex::encode(encoded)) - } else if let Some(ref field) = field { - get_pretty_tx_attr::(&tx, field.as_str()) - .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.clone()))? - } else if shell::is_json() { - // to_value first to sort json object keys - serde_json::to_value(&tx)?.to_string() - } else if to_request { - serde_json::to_string_pretty(&Into::::into(tx))? - } else { - tx.pretty() - }) - } -} - pub struct SimpleCast; impl SimpleCast { @@ -2352,22 +2315,15 @@ impl SimpleCast { /// # Example /// /// ``` - /// use alloy_network::Ethereum; /// use cast::SimpleCast as Cast; /// /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8"; - /// let tx_envelope = Cast::decode_raw_transaction::(&tx)?; + /// let tx_envelope = Cast::decode_raw_transaction(&tx)?; /// # Ok::<(), eyre::Report>(()) - pub fn decode_raw_transaction>( - tx: &str, - ) -> Result { + pub fn decode_raw_transaction(tx: &str) -> Result { let tx_hex = hex::decode(tx)?; - let tx: N::TxEnvelope = Decodable2718::decode_2718(&mut tx_hex.as_slice())?; - if let Ok(signer) = tx.recover_signer() { - Ok(serde_json::to_string_pretty(&Recovered::new_unchecked(tx, signer))?) - } else { - Ok(serde_json::to_string_pretty(&tx)?) - } + let tx = Decodable2718::decode_2718(&mut tx_hex.as_slice())?; + Ok(tx) } } @@ -2402,6 +2358,44 @@ fn explorer_client( builder.build().map_err(Into::into) } +// Temporary workaround to handle tx raw encoding through FoundryNetwork +// TODO: Once the Network selection UI will be finalized, bring back --raw +// handling to `Cast::transaction` +pub async fn transaction_raw( + config: &Config, + tx_hash: Option, + from: Option, + nonce: Option, +) -> Result { + let provider = ProviderBuilder::::from_config(config)?.build()?; + let tx = if let Some(tx_hash) = tx_hash { + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; + provider + .get_transaction_by_hash(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))? + } else if let Some(from) = from { + // If nonce is not provided, uses 0. + let nonce = U64::from(nonce.unwrap_or_default()); + let from = from.resolve(provider.root()).await?; + + provider + .raw_request::<_, Option<::TransactionResponse>>( + "eth_getTransactionBySenderAndNonce".into(), + (from, nonce), + ) + .await? + .ok_or_else(|| { + eyre::eyre!("tx not found for sender {from} and nonce {:?}", nonce.to::()) + })? + } else { + eyre::bail!("tx hash or from address is required") + }; + + let encoded = tx.as_ref().encoded_2718(); + Ok(format!("0x{}", hex::encode(encoded))) +} + #[cfg(test)] mod tests { use super::{DynSolValue, SimpleCast as Cast, serialize_value_as_json}; diff --git a/crates/cast/src/tempo.rs b/crates/cast/src/tempo.rs new file mode 100644 index 0000000000000..3c64491aaea63 --- /dev/null +++ b/crates/cast/src/tempo.rs @@ -0,0 +1,21 @@ +use alloy_primitives::Address; +use alloy_provider::Provider; +use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; + +pub use foundry_wallets::tempo::sign_with_access_key; + +/// Checks whether an access key is already provisioned on-chain. +/// +/// Queries the AccountKeychain precompile's `getKey` function. A key is considered +/// provisioned if the returned `keyId` is non-zero (i.e. the key exists and has not +/// been revoked). +pub async fn is_key_provisioned>( + provider: &P, + wallet_address: Address, + key_address: Address, +) -> bool { + match provider.get_keychain_key(wallet_address, key_address).await { + Ok(info) => info.keyId != Address::ZERO, + Err(_) => false, + } +} diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 50c142000c14c..131c96e6f6c17 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -4,7 +4,7 @@ use alloy_dyn_abi::ErrorExt; use alloy_ens::NameOrAddress; use alloy_json_abi::Function; use alloy_network::{Network, TransactionBuilder}; -use alloy_primitives::{Address, B256, Bytes, TxHash, TxKind, U256, hex}; +use alloy_primitives::{Address, B256, Bytes, TxHash, TxKind, U64, U256, hex}; use alloy_provider::{PendingTransactionBuilder, Provider}; use alloy_rpc_types::{AccessList, Authorization, TransactionInputKind}; use alloy_signer::Signer; @@ -12,7 +12,7 @@ use alloy_transport::TransportError; use clap::Args; use eyre::{Result, WrapErr}; use foundry_cli::{ - opts::{CliAuthorizationList, EthereumOpts, TransactionOpts}, + opts::{CliAuthorizationList, EthereumOpts, TempoOpts, TransactionOpts}, utils::{self, parse_function_args}, }; use foundry_common::{ @@ -57,6 +57,55 @@ pub struct SendTxOpts { pub browser: BrowserWalletOpts, } +/// Transaction options shared across cast commands that submit on-chain transactions. +#[derive(Debug, Clone, Args)] +#[command(next_help_heading = "Transaction options")] +pub struct TxParams { + /// Gas limit for the transaction. + #[arg(long, env = "ETH_GAS_LIMIT")] + pub gas_limit: Option, + + /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. + #[arg(long, env = "ETH_GAS_PRICE")] + pub gas_price: Option, + + /// Max priority fee per gas for EIP1559 transactions. + #[arg(long, env = "ETH_PRIORITY_GAS_PRICE")] + pub priority_gas_price: Option, + + /// Nonce for the transaction. + #[arg(long)] + pub nonce: Option, + + #[command(flatten)] + pub tempo: TempoOpts, +} + +impl TxParams { + pub(crate) fn apply(&self, tx: &mut N::TransactionRequest, legacy: bool) + where + N::TransactionRequest: FoundryTransactionBuilder, + { + if let Some(gas_limit) = self.gas_limit { + tx.set_gas_limit(gas_limit.to()); + } + + if let Some(gas_price) = self.gas_price { + if legacy { + tx.set_gas_price(gas_price.to()); + } else { + tx.set_max_fee_per_gas(gas_price.to()); + } + } + + if !legacy && let Some(priority_fee) = self.priority_gas_price { + tx.set_max_priority_fee_per_gas(priority_fee.to()); + } + + self.tempo.apply::(tx, self.nonce.map(|n| n.to())); + } +} + /// Different sender kinds used by [`CastTxBuilder`]. pub enum SenderKind<'a> { /// An address without signer. Used for read-only calls and transactions sent through unlocked diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 0df824f3588da..7d3b913b8780c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -3106,6 +3106,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index c98cfb69357bd..af1a09c38d109 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Cheatcodes", - "description": "Foundry cheatcodes. Learn more: ", + "description": "Foundry cheatcodes. Learn more: ", "type": "object", "properties": { "cheatcodes": { diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 29e968aeb5bc6..202b590769857 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -19,7 +19,7 @@ mod vm; pub use vm::Vm; // The `cheatcodes.json` schema. -/// Foundry cheatcodes. Learn more: +/// Foundry cheatcodes. Learn more: #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] @@ -178,7 +178,7 @@ interface Vm {{ eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); if std::env::var("CI").is_ok() { - eprintln!(" NOTE: run `cargo cheats` locally and commit the updated files\n"); + eprintln!(" NOTE: run `cargo spec-cheats` locally and commit the updated files\n"); } if let Some(parent) = file.parent() { let _ = fs::create_dir_all(parent); diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 3435617cdc45e..a5e3d35a985ed 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -186,6 +186,9 @@ impl CheatsConfig { pub fn rpc_endpoint(&self, url_or_alias: &str) -> Result { if let Some(endpoint) = self.rpc_endpoints.get(url_or_alias) { Ok(endpoint.clone().try_resolve()) + } else if let Some(builtin_url) = foundry_config::builtin_rpc_url(url_or_alias) { + let url = RpcEndpointUrl::Url(builtin_url.to_string()); + Ok(RpcEndpoint::new(url).resolve()) } else { // check if it's a URL or a path to an existing file to an ipc socket if url_or_alias.starts_with("http") || diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 3fa95c2a3a298..295fb05517818 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,6 +1,6 @@ use crate::{ - Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, DatabaseExt, Result, Vm::*, - json::json_value_to_token, + Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxExt, CheatsCtxt, DatabaseExt, Result, + Vm::*, json::json_value_to_token, }; use alloy_dyn_abi::DynSolValue; use alloy_evm::EvmEnv; @@ -10,13 +10,14 @@ use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use foundry_common::provider::ProviderBuilder; -use foundry_evm_core::{ - FoundryContextExt, backend::JournaledState, evm::FoundryEvmNetwork, fork::CreateFork, -}; +use foundry_evm_core::{Env, FoundryContextExt, backend::FoundryJournalExt, fork::CreateFork}; use revm::context::ContextTr; impl Cheatcode for activeForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self {} = self; ccx.ecx .db() @@ -27,103 +28,122 @@ impl Cheatcode for activeForkCall { } impl Cheatcode for createFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { urlOrAlias } = self; create_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { urlOrAlias, blockNumber } = self; create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { urlOrAlias, txHash } = self; create_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for createSelectFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias } = self; create_select_fork(ccx, urlOrAlias, None) } } impl Cheatcode for createSelectFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, blockNumber } = self; create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to())) } } impl Cheatcode for createSelectFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { urlOrAlias, txHash } = self; create_select_fork_at_transaction(ccx, urlOrAlias, txHash) } } impl Cheatcode for rollFork_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { blockNumber } = self; persist_caller(ccx); - fork_env_op(ccx.ecx, |db, evm_env, _, inner| { - db.roll_fork(None, (*blockNumber).to(), evm_env, inner) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.roll_fork(None, (*blockNumber).to(), &mut evm_env, &mut tx_env, inner)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(Default::default()) } } impl Cheatcode for rollFork_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { txHash } = self; persist_caller(ccx); - fork_env_op(ccx.ecx, |db, evm_env, _, inner| { - db.roll_fork_to_transaction(None, *txHash, evm_env, inner) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.roll_fork_to_transaction(None, *txHash, &mut evm_env, &mut tx_env, inner)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(Default::default()) } } impl Cheatcode for rollFork_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId, blockNumber } = self; persist_caller(ccx); - fork_env_op(ccx.ecx, |db, evm_env, _, inner| { - db.roll_fork(Some(*forkId), (*blockNumber).to(), evm_env, inner) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.roll_fork(Some(*forkId), (*blockNumber).to(), &mut evm_env, &mut tx_env, inner)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(Default::default()) } } impl Cheatcode for rollFork_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId, txHash } = self; persist_caller(ccx); - fork_env_op(ccx.ecx, |db, evm_env, _, inner| { - db.roll_fork_to_transaction(Some(*forkId), *txHash, evm_env, inner) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.roll_fork_to_transaction(Some(*forkId), *txHash, &mut evm_env, &mut tx_env, inner)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(Default::default()) } } impl Cheatcode for selectForkCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { forkId } = self; persist_caller(ccx); check_broadcast(ccx.state)?; - fork_env_op(ccx.ecx, |db, evm_env, tx_env, inner| { - db.select_fork(*forkId, evm_env, tx_env, inner) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + db.select_fork(*forkId, &mut evm_env, &mut tx_env, inner)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(Default::default()) } } impl Cheatcode for transact_0Call { - fn apply_full( + fn apply_full>( &self, - ccx: &mut CheatsCtxt<'_, '_, FEN>, - executor: &mut dyn CheatcodesExecutor, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Self { txHash } = *self; transact(ccx, executor, txHash, None) @@ -131,10 +151,10 @@ impl Cheatcode for transact_0Call { } impl Cheatcode for transact_1Call { - fn apply_full( + fn apply_full>( &self, - ccx: &mut CheatsCtxt<'_, '_, FEN>, - executor: &mut dyn CheatcodesExecutor, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Self { forkId, txHash } = *self; transact(ccx, executor, txHash, Some(forkId)) @@ -142,7 +162,10 @@ impl Cheatcode for transact_1Call { } impl Cheatcode for allowCheatcodesCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { account } = self; ccx.ecx.db_mut().allow_cheatcode_access(*account); Ok(Default::default()) @@ -150,7 +173,10 @@ impl Cheatcode for allowCheatcodesCall { } impl Cheatcode for makePersistent_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { account } = self; ccx.ecx.db_mut().add_persistent_account(*account); Ok(Default::default()) @@ -158,7 +184,10 @@ impl Cheatcode for makePersistent_0Call { } impl Cheatcode for makePersistent_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { account0, account1 } = self; ccx.ecx.db_mut().add_persistent_account(*account0); ccx.ecx.db_mut().add_persistent_account(*account1); @@ -167,7 +196,10 @@ impl Cheatcode for makePersistent_1Call { } impl Cheatcode for makePersistent_2Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { account0, account1, account2 } = self; ccx.ecx.db_mut().add_persistent_account(*account0); ccx.ecx.db_mut().add_persistent_account(*account1); @@ -177,7 +209,10 @@ impl Cheatcode for makePersistent_2Call { } impl Cheatcode for makePersistent_3Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { accounts } = self; for account in accounts { ccx.ecx.db_mut().add_persistent_account(*account); @@ -187,7 +222,10 @@ impl Cheatcode for makePersistent_3Call { } impl Cheatcode for revokePersistent_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { account } = self; ccx.ecx.db_mut().remove_persistent_account(account); Ok(Default::default()) @@ -195,7 +233,10 @@ impl Cheatcode for revokePersistent_0Call { } impl Cheatcode for revokePersistent_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { accounts } = self; for account in accounts { ccx.ecx.db_mut().remove_persistent_account(account); @@ -205,14 +246,20 @@ impl Cheatcode for revokePersistent_1Call { } impl Cheatcode for isPersistentCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { account } = self; Ok(ccx.ecx.db().is_persistent(account).abi_encode()) } } impl Cheatcode for rpc_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { method, params } = self; let url = ccx.ecx.db().active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; @@ -221,7 +268,7 @@ impl Cheatcode for rpc_0Call { } impl Cheatcode for rpc_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { urlOrAlias, method, params } = self; let url = state.config.rpc_endpoint(urlOrAlias)?.url()?; rpc_call(&url, method, params) @@ -229,7 +276,10 @@ impl Cheatcode for rpc_1Call { } impl Cheatcode for eth_getLogsCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { fromBlock, toBlock, target, topics } = self; let (Ok(from_block), Ok(to_block)) = (u64::try_from(fromBlock), u64::try_from(toBlock)) else { @@ -271,7 +321,10 @@ impl Cheatcode for eth_getLogsCall { } impl Cheatcode for getRawBlockHeaderCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { blockNumber } = self; let url = ccx.ecx.db().active_fork_url().ok_or_else(|| fmt_err!("no active fork"))?; let provider = ProviderBuilder::::new(&url).build()?; @@ -293,22 +346,24 @@ impl Cheatcode for getRawBlockHeaderCall { } /// Creates and then also selects the new fork -fn create_select_fork( - ccx: &mut CheatsCtxt<'_, '_, FEN>, +fn create_select_fork( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, ) -> Result { check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, block)?; - fork_env_op(ccx.ecx, |db, evm_env, tx_env, inner| { - db.create_select_fork(fork, evm_env, tx_env, inner) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + let id = db.create_select_fork(fork, &mut evm_env, &mut tx_env, inner)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(id.abi_encode()) } /// Creates a new fork -fn create_fork( - ccx: &mut CheatsCtxt<'_, '_, FEN>, +fn create_fork>( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, ) -> Result { @@ -318,22 +373,25 @@ fn create_fork( } /// Creates and then also selects the new fork at the given transaction -fn create_select_fork_at_transaction( - ccx: &mut CheatsCtxt<'_, '_, FEN>, +fn create_select_fork_at_transaction( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, transaction: &B256, ) -> Result { check_broadcast(ccx.state)?; let fork = create_fork_request(ccx, url_or_alias, None)?; - fork_env_op(ccx.ecx, |db, evm_env, tx_env, inner| { - db.create_select_fork_at_transaction(fork, evm_env, tx_env, inner, *transaction) - }) + let (mut evm_env, mut tx_env) = Env::clone_evm_and_tx(ccx.ecx); + let (db, inner) = ccx.ecx.journal_mut().as_db_and_inner(); + let id = + db.create_select_fork_at_transaction(fork, &mut evm_env, &mut tx_env, inner, *transaction)?; + Env::apply_evm_and_tx(ccx.ecx, evm_env, tx_env); + Ok(id.abi_encode()) } /// Creates a new fork at the given transaction -fn create_fork_at_transaction( - ccx: &mut CheatsCtxt<'_, '_, FEN>, +fn create_fork_at_transaction>( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, transaction: &B256, ) -> Result { @@ -343,8 +401,8 @@ fn create_fork_at_transaction( } /// Creates the request object for a new fork request -fn create_fork_request( - ccx: &mut CheatsCtxt<'_, '_, FEN>, +fn create_fork_request>( + ccx: &mut CheatsCtxt<'_, CTX>, url_or_alias: &str, block: Option, ) -> Result { @@ -363,33 +421,16 @@ fn create_fork_request( enable_caching: !ccx.state.config.no_storage_caching && ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), url, + evm_env: EvmEnv { + cfg_env: ccx.ecx.cfg_mut().clone(), + block_env: ccx.ecx.block_mut().clone(), + }, evm_opts, }; Ok(fork) } -/// Clones the EVM and tx environments, runs a fork operation that may modify them, then writes -/// them back. This is the common pattern for all fork-switching cheatcodes (rollFork, selectFork, -/// createSelectFork). -fn fork_env_op( - ecx: &mut CTX, - f: impl FnOnce( - &mut CTX::Db, - &mut EvmEnv, - &mut CTX::Tx, - &mut JournaledState, - ) -> eyre::Result, -) -> Result { - let mut evm_env = ecx.evm_clone(); - let mut tx_env = ecx.tx_clone(); - let (db, inner) = ecx.db_journal_inner_mut(); - let result = f(db, &mut evm_env, &mut tx_env, inner)?; - ecx.set_evm(evm_env); - ecx.set_tx(tx_env); - Ok(result.abi_encode()) -} - -fn check_broadcast(state: &Cheatcodes) -> Result<()> { +fn check_broadcast(state: &Cheatcodes) -> Result<()> { if state.broadcast.is_none() { Ok(()) } else { @@ -397,9 +438,9 @@ fn check_broadcast(state: &Cheatcodes) -> Result<() } } -fn transact( - ccx: &mut CheatsCtxt<'_, '_, FEN>, - executor: &mut dyn CheatcodesExecutor, +fn transact>( + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, transaction: B256, fork_id: Option, ) -> Result { @@ -411,7 +452,7 @@ fn transact( // 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 -fn persist_caller(ccx: &mut CheatsCtxt<'_, '_, FEN>) { +fn persist_caller>(ccx: &mut CheatsCtxt<'_, CTX>) { ccx.ecx.db_mut().add_persistent_account(ccx.caller); } @@ -427,11 +468,7 @@ fn rpc_call(url: &str, method: &str, params: &str) -> Result { .map_err(|err| fmt_err!("failed to parse result: {err}"))?, ); - let payload = match &result_as_tokens { - DynSolValue::Bytes(b) => b.clone(), - _ => result_as_tokens.abi_encode(), - }; - Ok(DynSolValue::Bytes(payload).abi_encode()) + Ok(result_as_tokens.abi_encode()) } /// Convert fixed bytes and address values to bytes in order to prevent encoding issues. @@ -443,6 +480,9 @@ fn convert_to_bytes(token: &DynSolValue) -> DynSolValue { DynSolValue::Bytes(bytes.as_slice()[..*size].to_vec()) } DynSolValue::Address(addr) => DynSolValue::Bytes(addr.to_vec()), + // Convert tuple values to prevent encoding issues. + // See: + DynSolValue::Tuple(vals) => DynSolValue::Tuple(vals.iter().map(convert_to_bytes).collect()), val => val.clone(), } } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 3f8bd91b0e8d9..5e762ec62a035 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; -use alloy_network::{Ethereum, Network, ReceiptResponse}; +use alloy_network::{Network, ReceiptResponse}; use alloy_primitives::{Bytes, U256, hex, map::Entry}; use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; @@ -792,7 +792,7 @@ impl Cheatcode for getBroadcastCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId, txType } = self; - let latest_broadcast = latest_broadcast( + let latest_broadcast = latest_broadcast::<::Network>( contractName, *chainId, &state.config.broadcast, @@ -810,7 +810,7 @@ impl Cheatcode for getBroadcasts_0Call { let reader = BroadcastReader::new(contractName.clone(), *chainId, &state.config.broadcast)? .with_tx_type(map_broadcast_tx_type(*txType)); - let broadcasts = reader.read::()?; + let broadcasts = reader.read::<::Network>()?; let summaries = broadcasts .into_iter() @@ -830,7 +830,7 @@ impl Cheatcode for getBroadcasts_1Call { let reader = BroadcastReader::new(contractName.clone(), *chainId, &state.config.broadcast)?; - let broadcasts = reader.read::()?; + let broadcasts = reader.read::<::Network>()?; let summaries = broadcasts .into_iter() @@ -849,7 +849,7 @@ impl Cheatcode for getDeployment_0Call { let Self { contractName } = self; let chain_id = ccx.ecx.cfg().chain_id(); - let latest_broadcast = latest_broadcast( + let latest_broadcast = latest_broadcast::<::Network>( contractName, chain_id, &ccx.state.config.broadcast, @@ -864,7 +864,7 @@ impl Cheatcode for getDeployment_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { contractName, chainId } = self; - let latest_broadcast = latest_broadcast( + let latest_broadcast = latest_broadcast::<::Network>( contractName, *chainId, &state.config.broadcast, @@ -883,7 +883,7 @@ impl Cheatcode for getDeploymentsCall { .with_tx_type(CallKind::Create) .with_tx_type(CallKind::Create2); - let broadcasts = reader.read::()?; + let broadcasts = reader.read::<::Network>()?; let summaries = broadcasts .into_iter() @@ -929,19 +929,22 @@ fn parse_broadcast_results( .collect() } -fn latest_broadcast( +fn latest_broadcast( contract_name: &String, chain_id: u64, broadcast_path: &Path, filters: Vec, -) -> Result { +) -> Result +where + N::TxEnvelope: for<'d> serde::Deserialize<'d>, +{ let mut reader = BroadcastReader::new(contract_name.clone(), chain_id, broadcast_path)?; for filter in filters { reader = reader.with_tx_type(filter); } - let broadcast = reader.read_latest::()?; + let broadcast = reader.read_latest::()?; let results = reader.into_tx_receipts(broadcast); @@ -957,7 +960,9 @@ fn latest_broadcast( mod tests { use super::*; use crate::CheatsConfig; - use std::sync::Arc; + use alloy_primitives::{address, b256}; + use foundry_evm_core::evm::TempoEvmNetwork; + use std::{env, fs as stdfs, sync::Arc}; fn cheats() -> Cheatcodes { let config = CheatsConfig { @@ -1031,4 +1036,99 @@ mod tests { let err = result.unwrap_err().to_string(); assert!(err.contains("expected bytecode, found unlinked bytecode with placeholder")); } + + fn unique_temp_dir(prefix: &str) -> PathBuf { + env::temp_dir().join(format!( + "foundry-cheatcodes-{prefix}-{}", + SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() + )) + } + + #[test] + fn test_latest_broadcast_reads_tempo_sequences() { + let root = unique_temp_dir("tempo-broadcast"); + let broadcast_path = root.join("broadcast"); + let sequence_dir = broadcast_path.join("Counter.s.sol").join("31337"); + stdfs::create_dir_all(&sequence_dir).unwrap(); + + let tx_hash = "0x04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f"; + let block_hash = "0x860f788b251ece768e63b0d3906d156f652d843848b71c7fe81faacd49139d66"; + let from = "0xa70ab0448e66cd77995bfbba5c5b64b41a85f3fd"; + let contract_address = "0x20c0000000000000000000000000000000000000"; + let zero_bloom = format!("0x{}", "0".repeat(512)); + + let sequence = serde_json::json!({ + "transactions": [{ + "hash": tx_hash, + "transactionType": "CREATE", + "contractName": "Counter", + "contractAddress": contract_address, + "function": serde_json::Value::Null, + "arguments": serde_json::Value::Null, + "transaction": { + "type": "0x76", + "from": from, + "to": serde_json::Value::Null, + "data": "0x", + "value": "0x0", + "gas": "0x5208", + "nonce": "0x0", + "accessList": [], + "calls": [], + "nonceKey": "0x0", + "feePayerSignature": serde_json::Value::Null, + "validBefore": serde_json::Value::Null, + "validAfter": serde_json::Value::Null, + "keyAuthorization": serde_json::Value::Null, + "aaAuthorizationList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }], + "receipts": [{ + "type": "0x76", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logs": [], + "logsBloom": zero_bloom, + "transactionHash": tx_hash, + "transactionIndex": "0x0", + "blockHash": block_hash, + "blockNumber": "0x7", + "gasUsed": "0x5208", + "effectiveGasPrice": "0x1", + "from": from, + "to": serde_json::Value::Null, + "contractAddress": contract_address, + "feePayer": from + }], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1, + "chain": 31337, + "commit": serde_json::Value::Null + }); + + fs::write_json_file(&sequence_dir.join("run-1.json"), &sequence).unwrap(); + + let latest = latest_broadcast::<::Network>( + &"Counter".to_owned(), + 31337, + &broadcast_path, + vec![CallKind::Create], + ) + .unwrap(); + + assert_eq!( + latest.txHash, + b256!("04548a0ea27e2cccc1479af3c2ff02da4d4d3ea46af8e8d7edaa49f6ea27073f") + ); + assert_eq!(latest.blockNumber, 7); + assert!(matches!(latest.txType, BroadcastTxType::Create)); + assert_eq!(latest.contractAddress, address!("20c0000000000000000000000000000000000000")); + assert!(latest.success); + + stdfs::remove_dir_all(root).unwrap(); + } } diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index f3e922e5c14b4..7775d61841b2c 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -16,18 +16,15 @@ pub extern crate foundry_cheatcodes_spec as spec; extern crate tracing; use alloy_primitives::Address; -use foundry_evm_core::{ - backend::DatabaseExt, - evm::{FoundryContextFor, FoundryEvmNetwork}, -}; +use foundry_evm_core::backend::DatabaseExt; use revm::context::{ContextTr, JournalTr}; pub use Vm::ForgeContext; pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; -pub use foundry_evm_core::evm::NestedEvmClosure; pub use inspector::{ BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, CheatcodesExecutor, + CheatsCtxExt, NestedEvmClosure, }; pub use spec::{CheatcodeDef, Vm}; @@ -71,7 +68,7 @@ pub(crate) trait Cheatcode: CheatcodeDef { /// Applies this cheatcode to the given state. /// /// Implement this function if you don't need access to the EVM data. - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let _ = state; unimplemented!("{}", Self::CHEATCODE.func.id) } @@ -80,7 +77,7 @@ pub(crate) trait Cheatcode: CheatcodeDef { /// /// Implement this function if you need access to the EVM data. #[inline(always)] - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { self.apply(ccx.state) } @@ -88,10 +85,10 @@ pub(crate) trait Cheatcode: CheatcodeDef { /// /// Implement this function if you need access to the executor. #[inline(always)] - fn apply_full( + fn apply_full( &self, - ccx: &mut CheatsCtxt<'_, '_, FEN>, - executor: &mut dyn CheatcodesExecutor, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let _ = executor; self.apply_stateful(ccx) @@ -99,19 +96,19 @@ pub(crate) trait Cheatcode: CheatcodeDef { } /// The cheatcode context. -pub struct CheatsCtxt<'a, 'db, FEN: FoundryEvmNetwork + 'db> { +pub struct CheatsCtxt<'a, CTX> { /// The cheatcodes inspector state. - pub(crate) state: &'a mut Cheatcodes, + pub(crate) state: &'a mut Cheatcodes, /// The EVM context. - pub(crate) ecx: &'a mut FoundryContextFor<'db, FEN>, + pub(crate) ecx: &'a mut CTX, /// The original `msg.sender`. pub(crate) caller: Address, /// Gas limit of the current cheatcode call. pub(crate) gas_limit: u64, } -impl<'a, 'db, FEN: FoundryEvmNetwork> std::ops::Deref for CheatsCtxt<'a, 'db, FEN> { - type Target = FoundryContextFor<'db, FEN>; +impl std::ops::Deref for CheatsCtxt<'_, CTX> { + type Target = CTX; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -119,14 +116,14 @@ impl<'a, 'db, FEN: FoundryEvmNetwork> std::ops::Deref for CheatsCtxt<'a, 'db, FE } } -impl<'db, FEN: FoundryEvmNetwork> std::ops::DerefMut for CheatsCtxt<'_, 'db, FEN> { +impl std::ops::DerefMut for CheatsCtxt<'_, CTX> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { self.ecx } } -impl CheatsCtxt<'_, '_, FEN> { +impl CheatsCtxt<'_, CTX> { pub(crate) fn ensure_not_precompile(&self, address: &Address) -> Result<()> { if self.is_precompile(address) { Err(precompile_error(address)) } else { Ok(()) } } diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 06e8cbdb66ab1..c6441007ab0ce 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -5,7 +5,11 @@ use alloy_chains::Chain as AlloyChain; use alloy_primitives::{Address, U256}; use alloy_sol_types::SolValue; use foundry_common::version::SEMVER_VERSION; -use foundry_evm_core::{constants::MAGIC_SKIP, evm::FoundryEvmNetwork}; +use foundry_evm_core::{ + backend::{DatabaseExt, FoundryJournalExt}, + constants::MAGIC_SKIP, + env::FoundryContextExt, +}; use revm::context::{ContextTr, JournalTr}; use std::str::FromStr; @@ -15,28 +19,28 @@ pub(crate) mod expect; pub(crate) mod revert_handlers; impl Cheatcode for breakpoint_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { char } = self; breakpoint(ccx.state, &ccx.caller, char, true) } } impl Cheatcode for breakpoint_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { char, value } = self; breakpoint(ccx.state, &ccx.caller, char, *value) } } impl Cheatcode for getFoundryVersionCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self {} = self; Ok(SEMVER_VERSION.abi_encode()) } } impl Cheatcode for rpcUrlCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { rpcAlias } = self; let url = state.config.rpc_endpoint(rpcAlias)?.url()?.abi_encode(); Ok(url) @@ -44,21 +48,21 @@ impl Cheatcode for rpcUrlCall { } impl Cheatcode for rpcUrlsCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.config.rpc_urls().map(|urls| urls.abi_encode()) } } impl Cheatcode for rpcUrlStructsCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; state.config.rpc_urls().map(|urls| urls.abi_encode()) } } impl Cheatcode for sleepCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { duration } = self; let sleep_duration = std::time::Duration::from_millis(duration.saturating_to()); std::thread::sleep(sleep_duration); @@ -67,21 +71,20 @@ impl Cheatcode for sleepCall { } impl Cheatcode for skip_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { skipTest } = *self; - 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.ecx.journal().depth() <= 1, "`skip` can only be used at test level"); - Err([MAGIC_SKIP, &[]].concat().into()) - } else { - Ok(Default::default()) - } + skip_1Call { skipTest, reason: String::new() }.apply_stateful(ccx) } } impl Cheatcode for skip_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { skipTest, reason } = self; if *skipTest { // Skip should not work if called deeper than at test level. @@ -95,14 +98,14 @@ impl Cheatcode for skip_1Call { } impl Cheatcode for getChain_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { chainAlias } = self; get_chain(state, chainAlias) } } impl Cheatcode for getChain_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { chainId } = self; // Convert the chainId to a string and use the existing get_chain function let chain_id_str = chainId.to_string(); @@ -111,12 +114,7 @@ impl Cheatcode for getChain_1Call { } /// Adds or removes the given breakpoint to the state. -fn breakpoint( - state: &mut Cheatcodes, - caller: &Address, - s: &str, - add: bool, -) -> Result { +fn breakpoint(state: &mut Cheatcodes, caller: &Address, s: &str, add: bool) -> Result { let mut chars = s.chars(); let (Some(point), None) = (chars.next(), chars.next()) else { bail!("breakpoints must be exactly one character"); @@ -133,7 +131,7 @@ fn breakpoint( } /// Gets chain information for the given alias. -fn get_chain(state: &mut Cheatcodes, chain_alias: &str) -> Result { +fn get_chain(state: &mut Cheatcodes, chain_alias: &str) -> Result { // Parse the chain alias - works for both chain names and IDs let alloy_chain = AlloyChain::from_str(chain_alias) .map_err(|_| fmt_err!("invalid chain alias: {chain_alias}"))?; diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index fcaf6e9c6c3bb..491a321525d9e 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -8,7 +8,7 @@ use alloy_rlp::{Decodable, Encodable}; use alloy_sol_types::SolValue; use foundry_common::{TYPE_BINDING_PREFIX, fs}; use foundry_config::fs_permissions::FsAccessKind; -use foundry_evm_core::{constants::DEFAULT_CREATE2_DEPLOYER, evm::FoundryEvmNetwork}; +use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; use foundry_evm_fuzz::strategies::BoundMutator; use proptest::prelude::Strategy; use rand::{Rng, RngCore, seq::SliceRandom}; @@ -33,7 +33,7 @@ pub struct IgnoredTraces { } impl Cheatcode for labelCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { account, newLabel } = self; state.labels.insert(*account, newLabel.clone()); Ok(Default::default()) @@ -41,7 +41,7 @@ impl Cheatcode for labelCall { } impl Cheatcode for getLabelCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { account } = self; Ok(match state.labels.get(account) { Some(label) => label.abi_encode(), @@ -51,7 +51,7 @@ impl Cheatcode for getLabelCall { } impl Cheatcode for computeCreateAddressCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { nonce, deployer } = self; ensure!(*nonce <= U256::from(u64::MAX), "nonce must be less than 2^64"); Ok(deployer.create(nonce.to()).abi_encode()) @@ -59,28 +59,28 @@ impl Cheatcode for computeCreateAddressCall { } impl Cheatcode for computeCreate2Address_0Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { salt, initCodeHash, deployer } = self; Ok(deployer.create2(salt, initCodeHash).abi_encode()) } } impl Cheatcode for computeCreate2Address_1Call { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { salt, initCodeHash } = self; Ok(DEFAULT_CREATE2_DEPLOYER.create2(salt, initCodeHash).abi_encode()) } } impl Cheatcode for ensNamehashCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; Ok(namehash(name).abi_encode()) } } impl Cheatcode for bound_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { current, min, max } = *self; let Some(mutated) = U256::bound(current, min, max, state.test_runner()) else { bail!("cannot bound {current} in [{min}, {max}] range") @@ -90,7 +90,7 @@ impl Cheatcode for bound_0Call { } impl Cheatcode for bound_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { current, min, max } = *self; let Some(mutated) = I256::bound(current, min, max, state.test_runner()) else { bail!("cannot bound {current} in [{min}, {max}] range") @@ -100,27 +100,27 @@ impl Cheatcode for bound_1Call { } impl Cheatcode for randomUint_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { random_uint(state, None, None) } } impl Cheatcode for randomUint_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { min, max } = *self; random_uint(state, None, Some((min, max))) } } impl Cheatcode for randomUint_2Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bits } = *self; random_uint(state, Some(bits), None) } } impl Cheatcode for randomAddressCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { Ok(DynSolValue::type_strategy(&DynSolType::Address) .new_tree(state.test_runner()) .unwrap() @@ -130,27 +130,27 @@ impl Cheatcode for randomAddressCall { } impl Cheatcode for randomInt_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { random_int(state, None) } } impl Cheatcode for randomInt_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bits } = *self; random_int(state, Some(bits)) } } impl Cheatcode for randomBoolCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let rand_bool: bool = state.rng().random(); Ok(rand_bool.abi_encode()) } } impl Cheatcode for randomBytesCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { len } = *self; ensure!( len <= U256::from(usize::MAX), @@ -163,24 +163,24 @@ impl Cheatcode for randomBytesCall { } impl Cheatcode for randomBytes4Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let rand_u32 = state.rng().next_u32(); Ok(B32::from(rand_u32).abi_encode()) } } impl Cheatcode for randomBytes8Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let rand_u64 = state.rng().next_u64(); Ok(B64::from(rand_u64).abi_encode()) } } impl Cheatcode for pauseTracingCall { - fn apply_full( + fn apply_full( &self, - ccx: &mut CheatsCtxt<'_, '_, FEN>, - executor: &mut dyn CheatcodesExecutor, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Some(tracer) = executor.tracing_inspector() else { // No tracer -> nothing to pause @@ -200,10 +200,10 @@ impl Cheatcode for pauseTracingCall { } impl Cheatcode for resumeTracingCall { - fn apply_full( + fn apply_full( &self, - ccx: &mut CheatsCtxt<'_, '_, FEN>, - executor: &mut dyn CheatcodesExecutor, + ccx: &mut CheatsCtxt<'_, CTX>, + executor: &mut dyn CheatcodesExecutor, ) -> Result { let Some(tracer) = executor.tracing_inspector() else { // No tracer -> nothing to unpause @@ -223,18 +223,19 @@ impl Cheatcode for resumeTracingCall { } impl Cheatcode for interceptInitcodeCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self {} = self; - if state.intercept_next_create_call { + if !state.intercept_next_create_call { + state.intercept_next_create_call = true; + } else { bail!("vm.interceptInitcode() has already been called") } - state.intercept_next_create_call = true; Ok(Default::default()) } } impl Cheatcode for setArbitraryStorage_0Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target } = self; ccx.state.arbitrary_storage().mark_arbitrary(target, false); @@ -243,7 +244,7 @@ impl Cheatcode for setArbitraryStorage_0Call { } impl Cheatcode for setArbitraryStorage_1Call { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { target, overwrite } = self; ccx.state.arbitrary_storage().mark_arbitrary(target, *overwrite); @@ -252,7 +253,10 @@ impl Cheatcode for setArbitraryStorage_1Call { } impl Cheatcode for copyStorageCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful>( + &self, + ccx: &mut CheatsCtxt<'_, CTX>, + ) -> Result { let Self { from, to } = self; ensure!( @@ -276,7 +280,7 @@ impl Cheatcode for copyStorageCall { } impl Cheatcode for sortCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { array } = self; let mut sorted_values = array.clone(); @@ -287,7 +291,7 @@ impl Cheatcode for sortCall { } impl Cheatcode for shuffleCall { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { array } = self; let mut shuffled_values = array.clone(); @@ -299,7 +303,7 @@ impl Cheatcode for shuffleCall { } impl Cheatcode for setSeedCall { - fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result { + fn apply_stateful(&self, ccx: &mut CheatsCtxt<'_, CTX>) -> Result { let Self { seed } = self; ccx.state.set_seed(*seed); Ok(Default::default()) @@ -308,11 +312,7 @@ impl Cheatcode for setSeedCall { /// Helper to generate a random `uint` value (with given bits or bounded if specified) /// from type strategy. -fn random_uint( - state: &mut Cheatcodes, - bits: Option, - bounds: Option<(U256, U256)>, -) -> Result { +fn random_uint(state: &mut Cheatcodes, bits: Option, bounds: Option<(U256, U256)>) -> Result { if let Some(bits) = bits { // Generate random with specified bits. ensure!(bits <= U256::from(256), "number of bits cannot exceed 256"); @@ -345,7 +345,7 @@ fn random_uint( } /// Helper to generate a random `int` value (with given bits if specified) from type strategy. -fn random_int(state: &mut Cheatcodes, bits: Option) -> Result { +fn random_int(state: &mut Cheatcodes, bits: Option) -> Result { let no_bits = bits.unwrap_or(U256::from(256)); ensure!(no_bits <= U256::from(256), "number of bits cannot exceed 256"); Ok(DynSolValue::type_strategy(&DynSolType::Int(no_bits.to::())) @@ -356,7 +356,7 @@ fn random_int(state: &mut Cheatcodes, bits: Option< } impl Cheatcode for eip712HashType_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { typeNameOrDefinition } = self; let type_def = get_canonical_type_def(typeNameOrDefinition, state, None)?; @@ -366,7 +366,7 @@ impl Cheatcode for eip712HashType_0Call { } impl Cheatcode for eip712HashType_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bindingsPath, typeName } = self; let path = state.config.ensure_path_allowed(bindingsPath, FsAccessKind::Read)?; @@ -377,7 +377,7 @@ impl Cheatcode for eip712HashType_1Call { } impl Cheatcode for eip712HashStruct_0Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { typeNameOrDefinition, abiEncodedData } = self; let type_def = get_canonical_type_def(typeNameOrDefinition, state, None)?; @@ -388,7 +388,7 @@ impl Cheatcode for eip712HashStruct_0Call { } impl Cheatcode for eip712HashStruct_1Call { - fn apply(&self, state: &mut Cheatcodes) -> Result { + fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { bindingsPath, typeName, abiEncodedData } = self; let path = state.config.ensure_path_allowed(bindingsPath, FsAccessKind::Read)?; @@ -399,7 +399,7 @@ impl Cheatcode for eip712HashStruct_1Call { } impl Cheatcode for eip712HashTypedDataCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { jsonData } = self; let typed_data: TypedData = serde_json::from_str(jsonData)?; let digest = typed_data.eip712_signing_hash()?; @@ -410,9 +410,9 @@ impl Cheatcode for eip712HashTypedDataCall { /// Returns EIP-712 canonical type definition from the provided string type representation or type /// name. If type name provided, then it looks up bindings from file generated by `forge bind-json`. -fn get_canonical_type_def( +fn get_canonical_type_def( name_or_def: &String, - state: &mut Cheatcodes, + state: &mut Cheatcodes, path: Option, ) -> Result { let type_def = if name_or_def.contains('(') { @@ -501,7 +501,7 @@ fn get_struct_hash(primary: &str, type_def: &String, abi_encoded_data: &Bytes) - } impl Cheatcode for toRlpCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { data } = self; let mut buf = Vec::new(); @@ -512,7 +512,7 @@ impl Cheatcode for toRlpCall { } impl Cheatcode for fromRlpCall { - fn apply(&self, _state: &mut Cheatcodes) -> Result { + fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { rlp } = self; let decoded: Vec = Vec::::decode(&mut rlp.as_ref()) diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 4d130416b949c..e9a1936013272 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -65,7 +65,7 @@ foundry-test-utils.workspace = true rexpect = "0.6" [features] -default = ["jemalloc"] +default = ["jemalloc", "asm-keccak"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index da2c7f4caff02..8ebeca5ee2549 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -199,23 +199,20 @@ impl SessionSource { } async fn build_runner(&mut self, final_pc: usize) -> Result { - let (evm_env, tx_env, fork_block) = self.config.evm_opts.env().await?; + let env = self.config.evm_opts.env().await?; let backend = match self.config.backend.clone() { Some(backend) => backend, None => { - let fork = self.config.evm_opts.get_fork( - &self.config.foundry_config, - evm_env.cfg_env.chain_id, - fork_block, - ); + let fork = + self.config.evm_opts.get_fork(&self.config.foundry_config, env.evm_env.clone()); let backend = Backend::spawn(fork)?; self.config.backend = Some(backend.clone()); backend } }; - let executor = ExecutorBuilder::default() + let executor = ExecutorBuilder::new() .inspectors(|stack| { stack .logs(self.config.foundry_config.live_logs) @@ -227,7 +224,6 @@ impl SessionSource { self.config.evm_opts.clone(), None, None, - None, ) .into(), ) @@ -235,7 +231,7 @@ impl SessionSource { .gas_limit(self.config.evm_opts.gas_limit()) .spec_id(self.config.foundry_config.evm_spec_id()) .legacy_assertions(self.config.foundry_config.legacy_assertions) - .build(evm_env, tx_env, backend); + .build(env, backend); Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone())) } @@ -549,8 +545,9 @@ impl Type { pt::Expression::Multiply(_, lhs, rhs) | pt::Expression::Divide(_, lhs, rhs) => { match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) { - (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_))) | -(Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => { + (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) | + (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) | + (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => { Some(Self::Builtin(DynSolType::Int(256))) } _ => { @@ -821,7 +818,7 @@ impl Type { custom_type: &mut Vec, contract_name: Option, ) -> Result> { - if let Some("this" | "super") = custom_type.last().map(String::as_str) { + if let Some("this") | Some("super") = custom_type.last().map(String::as_str) { custom_type.pop(); } if custom_type.is_empty() { @@ -958,7 +955,7 @@ impl Type { Ok(None) } } - other_expr => Ok(Self::ethabi(other_expr, intermediate)), + ty => Ok(Self::ethabi(ty, intermediate)), }; // re-run everything with the resolved variable in case we're accessing a builtin member // for example array or bytes length etc @@ -1080,12 +1077,12 @@ impl Type { match self { Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => { match inner.try_as_ethabi(intermediate) { - Some(DynSolType::Array(inner) | DynSolType::FixedArray(inner, _)) => { + Some(DynSolType::Array(inner)) | Some(DynSolType::FixedArray(inner, _)) => { Some(*inner) } - Some(DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_)) => { - Some(DynSolType::FixedBytes(1)) - } + Some(DynSolType::Bytes) + | Some(DynSolType::String) + | Some(DynSolType::FixedBytes(_)) => Some(DynSolType::FixedBytes(1)), ty => ty, } } @@ -1095,7 +1092,7 @@ impl Type { /// Returns whether this type is dynamic #[inline] - const fn is_dynamic(&self) -> bool { + fn is_dynamic(&self) -> bool { match self { // TODO: Note, this is not entirely correct. Fixed arrays of non-dynamic types are // not dynamic, nor are tuples of non-dynamic types. @@ -1107,22 +1104,23 @@ impl Type { /// Returns whether this type is an array #[inline] - const fn is_array(&self) -> bool { + fn is_array(&self) -> bool { matches!( self, Self::Array(_) | Self::FixedArray(_, _) - | Self::Builtin(DynSolType::Array(_) | DynSolType::FixedArray(_, _)) + | Self::Builtin(DynSolType::Array(_)) + | Self::Builtin(DynSolType::FixedArray(_, _)) ) } /// Returns whether this type is a dynamic array (can call push, pop) #[inline] - const fn is_dynamic_array(&self) -> bool { + fn is_dynamic_array(&self) -> bool { matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_))) } - const fn is_fixed_bytes(&self) -> bool { + fn is_fixed_bytes(&self) -> bool { matches!(self, Self::Builtin(DynSolType::FixedBytes(_))) } } @@ -1141,7 +1139,7 @@ fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option _ => None, }); match vis { - Some(pt::Visibility::External(_) | pt::Visibility::Public(_)) => { + Some(pt::Visibility::External(_)) | Some(pt::Visibility::Public(_)) => { match custom_type.first().unwrap().as_str() { "address" => Some(DynSolType::Address), "selector" => Some(DynSolType::FixedBytes(4)), @@ -1605,7 +1603,7 @@ mod tests { T: AsRef + std::fmt::Display + 'a, I: IntoIterator + 'a, { - for (input, expected) in input { + for (input, expected) in input.into_iter() { let input = input.as_ref(); let ty = get_type_ethabi(s, input, true); assert_eq!(ty.as_ref(), Some(expected), "\n{input}"); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 79b15381607aa..165154da88f63 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -21,6 +21,8 @@ foundry-evm.workspace = true foundry-evm-networks.workspace = true foundry-wallets.workspace = true +tempo-primitives.workspace = true + foundry-compilers.workspace = true solar.workspace = true diff --git a/crates/cli/src/opts/network.rs b/crates/cli/src/opts/network.rs index ffb63ad7048d4..f78af85842c95 100644 --- a/crates/cli/src/opts/network.rs +++ b/crates/cli/src/opts/network.rs @@ -1,6 +1,3 @@ -use alloy_chains::Chain; -use alloy_primitives::ChainId; - /// Network selection, defaulting to Ethereum #[derive(Clone, Debug, Default, clap::ValueEnum)] pub enum NetworkVariant { @@ -12,16 +9,3 @@ pub enum NetworkVariant { /// Tempo Tempo, } - -impl From for NetworkVariant { - fn from(chain_id: ChainId) -> Self { - let chain = Chain::from_id(chain_id); - if chain.is_tempo() { - Self::Tempo - } else if chain.is_optimism() { - Self::Optimism - } else { - Default::default() - } - } -} diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..8f6d7f3cde092 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -16,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/cli/src/utils/tempo.rs b/crates/cli/src/utils/tempo.rs index 2c9e5d9604884..647f52d316a6c 100644 --- a/crates/cli/src/utils/tempo.rs +++ b/crates/cli/src/utils/tempo.rs @@ -1,6 +1,7 @@ use std::str::FromStr; -use alloy_primitives::{Address, hex}; +use alloy_primitives::Address; +use tempo_primitives::TempoAddressExt; /// Parses a fee token address. pub fn parse_fee_token_address(address_or_id: &str) -> eyre::Result
{ @@ -9,7 +10,7 @@ pub fn parse_fee_token_address(address_or_id: &str) -> eyre::Result
{ fn token_id_to_address(token_id: u64) -> Address { let mut address_bytes = [0u8; 20]; - address_bytes[..12].copy_from_slice(&hex!("20C000000000000000000000")); + address_bytes[..12].copy_from_slice(&Address::TIP20_PREFIX); address_bytes[12..20].copy_from_slice(&token_id.to_be_bytes()); Address::from(address_bytes) } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index ac7a8c12eae71..706527093cc8e 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,6 +17,7 @@ foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-common-fmt.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true +foundry-primitives.workspace = true alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index 964b26b978fbe..fd208b18da5a8 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -3,13 +3,13 @@ use std::num::NonZeroU64; use alloy_consensus::{ - BlockHeader, Eip658Value, Signed, Transaction as TxTrait, TxEip1559, TxEip2930, - TxEip4844Variant, TxEip7702, TxEnvelope, TxLegacy, TxReceipt, Typed2718, + BlockHeader, Eip658Value, Receipt, ReceiptWithBloom, Signed, Transaction as TxTrait, TxEip1559, + TxEip2930, TxEip4844Variant, TxEip7702, TxEnvelope, TxLegacy, TxReceipt, Typed2718, transaction::TxHashRef, }; use alloy_network::{ - AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTransactionReceipt, AnyTxEnvelope, - BlockResponse, Network, ReceiptResponse, primitives::HeaderResponse, + AnyReceiptEnvelope, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTransactionReceipt, + AnyTxEnvelope, BlockResponse, Network, ReceiptResponse, primitives::HeaderResponse, }; use alloy_primitives::{ Address, Bloom, Bytes, FixedBytes, I256, Signature, U8, U64, U256, Uint, hex, @@ -18,6 +18,7 @@ use alloy_rpc_types::{ AccessListItem, Block, BlockTransactions, Header, Log, Transaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; +use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope, FoundryTxReceipt}; use op_alloy_consensus::{OpTxEnvelope, TxDeposit}; use revm::context_interface::transaction::SignedAuthorization; use serde::Deserialize; @@ -61,9 +62,7 @@ impl UIfmt for Option { impl UIfmt for [T] { fn pretty(&self) -> String { - if self.is_empty() { - "[]".to_string() - } else { + if !self.is_empty() { let mut s = String::with_capacity(self.len() * 64); s.push_str("[\n"); for item in self { @@ -75,13 +74,15 @@ impl UIfmt for [T] { } s.push(']'); s + } else { + "[]".to_string() } } } impl UIfmt for String { fn pretty(&self) -> String { - self.clone() + self.to_string() } } @@ -178,10 +179,37 @@ impl UIfmt for Signature { } } -/// Pretty-prints the common fields of any `TransactionReceipt`. -fn pretty_receipt>(receipt: &TransactionReceipt, tx_type: u8) -> String { - let mut pretty = format!( - " +impl UIfmt for AnyTransactionReceipt { + fn pretty(&self) -> String { + let Self { + inner: + TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + from, + to, + gas_used, + contract_address, + 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!( + " blockHash {} blockNumber {} contractAddress {} @@ -198,41 +226,31 @@ transactionIndex {} type {} blobGasPrice {} blobGasUsed {}", - receipt.block_hash.pretty(), - receipt.block_number.pretty(), - receipt.contract_address.pretty(), - receipt.inner.cumulative_gas_used().pretty(), - receipt.effective_gas_price.pretty(), - receipt.from.pretty(), - receipt.gas_used.pretty(), - serde_json::to_string(receipt.inner.logs()).unwrap(), - receipt.inner.bloom().pretty(), - receipt.state_root().pretty(), - receipt.inner.status_or_post_state().pretty(), - receipt.transaction_hash.pretty(), - receipt.transaction_index.pretty(), - tx_type, - receipt.blob_gas_price.pretty(), - receipt.blob_gas_used.pretty() - ); - - if let Some(to) = receipt.to { - pretty.push_str(&format!("\nto {}", to.pretty())); - } - - pretty -} - -impl UIfmt for TransactionReceipt { - fn pretty(&self) -> String { - pretty_receipt(self, self.transaction_type() as u8) - } -} + block_hash.pretty(), + block_number.pretty(), + contract_address.pretty(), + cumulative_gas_used.pretty(), + effective_gas_price.pretty(), + from.pretty(), + gas_used.pretty(), + serde_json::to_string(&logs).unwrap(), + logs_bloom.pretty(), + self.state_root().pretty(), + status.pretty(), + transaction_hash.pretty(), + transaction_index.pretty(), + transaction_type, + blob_gas_price.pretty(), + blob_gas_used.pretty() + ); + + if let Some(to) = to { + pretty.push_str(&format!("\nto {}", to.pretty())); + } + + // additional captured fields + pretty.push_str(&other.pretty()); -impl UIfmt for AnyTransactionReceipt { - fn pretty(&self) -> String { - let mut pretty = pretty_receipt(&self.inner, self.inner.inner.r#type); - pretty.push_str(&self.other.pretty()); pretty } } @@ -509,8 +527,8 @@ validAfter {}", self.nonce_key.pretty(), self.nonce.pretty(), self.fee_payer_signature.pretty(), - self.valid_before.pretty(), self.valid_after.pretty(), + self.valid_before.pretty(), ) } } @@ -715,39 +733,114 @@ impl UIfmt for SignedAuthorization { } } +impl UIfmt for FoundryReceiptEnvelope +where + T: UIfmt + Clone + core::fmt::Debug + PartialEq + Eq, +{ + fn pretty(&self) -> String { + let receipt = self.as_receipt(); + let deposit_info = match self { + Self::Deposit(d) => { + format!( + " +depositNonce {} +depositReceiptVersion {}", + d.receipt.deposit_nonce.pretty(), + d.receipt.deposit_receipt_version.pretty() + ) + } + _ => String::new(), + }; + + format!( + " +status {} +cumulativeGasUsed {} +logs {} +logsBloom {} +type {}{}", + receipt.status.pretty(), + receipt.cumulative_gas_used.pretty(), + receipt.logs.pretty(), + self.logs_bloom().pretty(), + self.tx_type() as u8, + deposit_info + ) + } +} + +impl UIfmt for FoundryTxReceipt { + fn pretty(&self) -> String { + let receipt = &self.0.inner; + let other = &self.0.other; + + let mut pretty = format!( + " +blockHash {} +blockNumber {} +contractAddress {} +cumulativeGasUsed {} +effectiveGasPrice {} +from {} +gasUsed {} +logs {} +logsBloom {} +root {} +status {} +transactionHash {} +transactionIndex {} +type {} +blobGasPrice {} +blobGasUsed {}", + receipt.block_hash().pretty(), + receipt.block_number().pretty(), + receipt.contract_address().pretty(), + receipt.cumulative_gas_used().pretty(), + receipt.effective_gas_price().pretty(), + receipt.from().pretty(), + receipt.gas_used().pretty(), + serde_json::to_string(receipt.inner.logs()).unwrap(), + receipt.inner.logs_bloom().pretty(), + self.state_root().pretty(), + receipt.status().pretty(), + receipt.transaction_hash().pretty(), + receipt.transaction_index().pretty(), + receipt.inner.tx_type() as u8, + receipt.blob_gas_price().pretty(), + receipt.blob_gas_used().pretty() + ); + + if let Some(to) = receipt.to() { + pretty.push_str(&format!("\nto {}", to.pretty())); + } + + // additional captured fields + pretty.push_str(&other.pretty()); + + pretty + } +} + pub trait UIfmtHeaderExt { fn size_pretty(&self) -> String; - fn total_difficulty_pretty(&self) -> String; } impl UIfmtHeaderExt for Header { fn size_pretty(&self) -> String { self.size.pretty() } - - fn total_difficulty_pretty(&self) -> String { - self.total_difficulty.unwrap_or_else(|| self.difficulty()).pretty() - } } impl UIfmtHeaderExt for AnyRpcHeader { fn size_pretty(&self) -> String { self.size.pretty() } - - fn total_difficulty_pretty(&self) -> String { - self.total_difficulty.unwrap_or_else(|| self.difficulty()).pretty() - } } impl UIfmtHeaderExt for TempoHeaderResponse { fn size_pretty(&self) -> String { self.inner.size.pretty() } - - fn total_difficulty_pretty(&self) -> String { - self.inner.total_difficulty.unwrap_or_else(|| self.inner.difficulty()).pretty() - } } pub trait UIfmtSignatureExt { @@ -783,6 +876,12 @@ impl UIfmtSignatureExt for OpTxEnvelope { } } +impl UIfmtSignatureExt for FoundryTxEnvelope { + fn signature_pretty(&self) -> Option<(String, String, String)> { + self.clone().try_into_eth().ok().and_then(|envelope| envelope.signature_pretty()) + } +} + impl UIfmtSignatureExt for TempoTxEnvelope { fn signature_pretty(&self) -> Option<(String, String, String)> { let sig = match self { @@ -814,39 +913,31 @@ pub trait UIfmtReceiptExt { fn tx_type_pretty(&self) -> String; } -fn receipt_logs_pretty>(receipt: &TransactionReceipt) -> String { - serde_json::to_string(receipt.inner.logs()).unwrap_or_default() -} - -fn receipt_logs_bloom_pretty>(receipt: &TransactionReceipt) -> String { - receipt.inner.bloom().pretty() -} - -impl UIfmtReceiptExt for TransactionReceipt { +impl UIfmtReceiptExt for AnyTransactionReceipt { fn logs_pretty(&self) -> String { - receipt_logs_pretty(self) + serde_json::to_string(&self.inner.inner.inner.receipt.logs).unwrap_or_default() } fn logs_bloom_pretty(&self) -> String { - receipt_logs_bloom_pretty(self) + self.inner.inner.inner.logs_bloom.pretty() } fn tx_type_pretty(&self) -> String { - self.transaction_type().to_string() + self.inner.inner.r#type.to_string() } } -impl UIfmtReceiptExt for AnyTransactionReceipt { +impl UIfmtReceiptExt for FoundryTxReceipt { fn logs_pretty(&self) -> String { - receipt_logs_pretty(&self.inner) + serde_json::to_string(self.0.inner.inner.logs()).unwrap_or_default() } fn logs_bloom_pretty(&self) -> String { - receipt_logs_bloom_pretty(&self.inner) + self.0.inner.inner.logs_bloom().pretty() } fn tx_type_pretty(&self) -> String { - self.inner.inner.r#type.to_string() + (self.0.inner.inner.tx_type() as u8).to_string() } } @@ -971,7 +1062,7 @@ where "size" => Some(block.header().size_pretty()), "stateRoot" | "state_root" => Some(block.header().state_root().pretty()), "timestamp" => Some(block.header().timestamp().pretty()), - "totalDifficulty" | "total_difficulty" => Some(block.header().total_difficulty_pretty()), + "totalDifficulty" | "total_difficulty" => Some(block.header().difficulty().pretty()), "blobGasUsed" | "blob_gas_used" => Some(block.header().blob_gas_used().pretty()), "excessBlobGas" | "excess_blob_gas" => Some(block.header().excess_blob_gas().pretty()), "requestsHash" | "requests_hash" => Some(block.header().requests_hash().pretty()), @@ -1060,7 +1151,7 @@ requestsHash {}", header.timestamp().pretty(), fmt_timestamp(header.timestamp()), header.withdrawals_root().pretty(), - header.total_difficulty_pretty(), + header.difficulty().pretty(), header.blob_gas_used().pretty(), header.excess_blob_gas().pretty(), header.requests_hash().pretty(), @@ -1088,9 +1179,9 @@ fn fmt_timestamp(timestamp: u64) -> String { #[cfg(test)] mod tests { use super::*; - use alloy_network::Ethereum; use alloy_primitives::B256; use alloy_rpc_types::Authorization; + use foundry_primitives::FoundryNetwork; use similar_asserts::assert_eq; use std::str::FromStr; @@ -1488,47 +1579,51 @@ yParity 0" #[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: ::BlockResponse = serde_json::from_str(block).unwrap(); + let block: ::BlockResponse = + 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!(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::(&txs[0], "from") + get_pretty_tx_attr::(&txs[0], "from") ); - assert_eq!(Some("90000".to_string()), get_pretty_tx_attr::(&txs[0], "gas")); + 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") + get_pretty_tx_attr::(&txs[0], "gasPrice") ); assert_eq!( Some("0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067".to_string()), - get_pretty_tx_attr::(&txs[0], "hash") + get_pretty_tx_attr::(&txs[0], "hash") ); - 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("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::(&txs[0], "r") + get_pretty_tx_attr::(&txs[0], "r") ); assert_eq!( Some("0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e".to_string()), - get_pretty_tx_attr::(&txs[0], "s") + get_pretty_tx_attr::(&txs[0], "s") ); assert_eq!( Some("0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e".into()), - get_pretty_tx_attr::(&txs[0], "to") + get_pretty_tx_attr::(&txs[0], "to") ); assert_eq!( Some("0".to_string()), - get_pretty_tx_attr::(&txs[0], "transactionIndex") + get_pretty_tx_attr::(&txs[0], "transactionIndex") ); - assert_eq!(Some("27".to_string()), get_pretty_tx_attr::(&txs[0], "v")); - assert_eq!(Some("0".to_string()), get_pretty_tx_attr::(&txs[0], "value")); + assert_eq!(Some("27".to_string()), get_pretty_tx_attr::(&txs[0], "v")); + assert_eq!(Some("0".to_string()), get_pretty_tx_attr::(&txs[0], "value")); } #[test] @@ -1551,7 +1646,7 @@ yParity 0" "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", - "difficulty": "0x1", + "difficulty": "0x27f07", "totalDifficulty": "0x27f07", "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", "size": "0x27f07", @@ -1564,77 +1659,83 @@ yParity 0" } ); - let block: ::BlockResponse = serde_json::from_value(json).unwrap(); + let block: ::BlockResponse = + serde_json::from_value(json).unwrap(); - assert_eq!(None, get_pretty_block_attr::(&block, "")); + assert_eq!(None, get_pretty_block_attr::(&block, "")); assert_eq!( Some("7".to_string()), - get_pretty_block_attr::(&block, "baseFeePerGas") + get_pretty_block_attr::(&block, "baseFeePerGas") + ); + assert_eq!( + Some("163591".to_string()), + get_pretty_block_attr::(&block, "difficulty") ); - assert_eq!(Some("1".to_string()), get_pretty_block_attr::(&block, "difficulty")); assert_eq!( Some("0x0000000000000000000000000000000000000000000000000000000000000000".to_string()), - get_pretty_block_attr::(&block, "extraData") + get_pretty_block_attr::(&block, "extraData") ); assert_eq!( Some("653145".to_string()), - get_pretty_block_attr::(&block, "gasLimit") + get_pretty_block_attr::(&block, "gasLimit") ); assert_eq!( Some("653145".to_string()), - get_pretty_block_attr::(&block, "gasUsed") + get_pretty_block_attr::(&block, "gasUsed") ); assert_eq!( Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), - get_pretty_block_attr::(&block, "hash") + get_pretty_block_attr::(&block, "hash") ); - assert_eq!(Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), get_pretty_block_attr::(&block, "logsBloom")); + assert_eq!(Some("0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331".to_string()), get_pretty_block_attr::(&block, "logsBloom")); assert_eq!( Some("0x0000000000000000000000000000000000000001".to_string()), - get_pretty_block_attr::(&block, "miner") + get_pretty_block_attr::(&block, "miner") ); assert_eq!( Some("0x1010101010101010101010101010101010101010101010101010101010101010".to_string()), - get_pretty_block_attr::(&block, "mixHash") + get_pretty_block_attr::(&block, "mixHash") ); assert_eq!( Some("0x0000000000000000".to_string()), - get_pretty_block_attr::(&block, "nonce") + get_pretty_block_attr::(&block, "nonce") + ); + assert_eq!( + Some("436".to_string()), + get_pretty_block_attr::(&block, "number") ); - assert_eq!(Some("436".to_string()), get_pretty_block_attr::(&block, "number")); assert_eq!( Some("0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5".to_string()), - get_pretty_block_attr::(&block, "parentHash") + get_pretty_block_attr::(&block, "parentHash") ); assert_eq!( Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".to_string()), - get_pretty_block_attr::(&block, "transactionsRoot") + get_pretty_block_attr::(&block, "transactionsRoot") ); assert_eq!( Some("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".to_string()), - get_pretty_block_attr::(&block, "receiptsRoot") + get_pretty_block_attr::(&block, "receiptsRoot") ); assert_eq!( Some("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".to_string()), - get_pretty_block_attr::(&block, "sha3Uncles") + get_pretty_block_attr::(&block, "sha3Uncles") + ); + assert_eq!( + Some("163591".to_string()), + get_pretty_block_attr::(&block, "size") ); - assert_eq!(Some("163591".to_string()), get_pretty_block_attr::(&block, "size")); assert_eq!( Some("0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff".to_string()), - get_pretty_block_attr::(&block, "stateRoot") + get_pretty_block_attr::(&block, "stateRoot") ); assert_eq!( Some("1424182926".to_string()), - get_pretty_block_attr::(&block, "timestamp") + get_pretty_block_attr::(&block, "timestamp") ); assert_eq!( Some("163591".to_string()), - get_pretty_block_attr::(&block, "totalDifficulty") + get_pretty_block_attr::(&block, "totalDifficulty") ); - - let pretty = pretty_generic_header_response(block.header()); - assert!(pretty.contains("difficulty 1"), "{pretty}"); - assert!(pretty.contains("totalDifficulty 163591"), "{pretty}"); } #[test] @@ -1823,24 +1924,33 @@ to 0x20C0000000000000000000000000000000000000 } #[test] - fn test_ethereum_receipt_uifmt() { + fn test_foundry_tx_receipt_uifmt() { + use alloy_network::AnyTransactionReceipt; + use foundry_primitives::FoundryTxReceipt; + + // Test UIfmt implementation for FoundryTxReceipt let s = r#"{"type":"0x2","status":"0x1","cumulativeGasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x1234567890123456789012345678901234567890123456789012345678901234","transactionIndex":"0x0","blockHash":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd","blockNumber":"0x1","gasUsed":"0x5208","effectiveGasPrice":"0x3b9aca00","from":"0x1234567890123456789012345678901234567890","to":"0x0987654321098765432109876543210987654321","contractAddress":null}"#; - let receipt: TransactionReceipt = serde_json::from_str(s).unwrap(); + let any_receipt: AnyTransactionReceipt = serde_json::from_str(s).unwrap(); + let foundry_receipt = FoundryTxReceipt::try_from(any_receipt).unwrap(); - let pretty_output = receipt.pretty(); + let pretty_output = foundry_receipt.pretty(); + // Check that essential fields are present in the output assert!(pretty_output.contains("blockHash")); assert!(pretty_output.contains("blockNumber")); assert!(pretty_output.contains("status")); assert!(pretty_output.contains("gasUsed")); assert!(pretty_output.contains("transactionHash")); assert!(pretty_output.contains("type")); + + // Verify the transaction hash appears in the output assert!( pretty_output .contains("0x1234567890123456789012345678901234567890123456789012345678901234") ); - assert!(pretty_output.contains("1 (success)")); - assert!(pretty_output.contains("0x0987654321098765432109876543210987654321")); + + // Verify status is pretty printed correctly (boolean true for successful transaction) + assert!(pretty_output.contains("true")); } #[test] @@ -1862,35 +1972,38 @@ to 0x20C0000000000000000000000000000000000000 "contractAddress": null }); - let receipt: ::ReceiptResponse = + let receipt: ::ReceiptResponse = serde_json::from_value(receipt_json).unwrap(); // Test basic receipt attributes assert_eq!( Some("0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd".to_string()), - get_pretty_receipt_attr::(&receipt, "blockHash") + get_pretty_receipt_attr::(&receipt, "blockHash") ); assert_eq!( Some("1".to_string()), - get_pretty_receipt_attr::(&receipt, "blockNumber") + get_pretty_receipt_attr::(&receipt, "blockNumber") ); assert_eq!( Some("0x1234567890123456789012345678901234567890123456789012345678901234".to_string()), - get_pretty_receipt_attr::(&receipt, "transactionHash") + get_pretty_receipt_attr::(&receipt, "transactionHash") ); assert_eq!( Some("21000".to_string()), - get_pretty_receipt_attr::(&receipt, "gasUsed") + get_pretty_receipt_attr::(&receipt, "gasUsed") ); assert_eq!( Some("true".to_string()), - get_pretty_receipt_attr::(&receipt, "status") + get_pretty_receipt_attr::(&receipt, "status") + ); + assert_eq!( + Some("2".to_string()), + get_pretty_receipt_attr::(&receipt, "type") ); assert_eq!( - Some("EIP-1559".to_string()), - get_pretty_receipt_attr::(&receipt, "type") + Some("[]".to_string()), + get_pretty_receipt_attr::(&receipt, "logs") ); - assert_eq!(Some("[]".to_string()), get_pretty_receipt_attr::(&receipt, "logs")); - assert!(get_pretty_receipt_attr::(&receipt, "logsBloom").is_some()); + assert!(get_pretty_receipt_attr::(&receipt, "logsBloom").is_some()); } } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 895b16b3b4532..e9f203ebf7539 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) .map(|(_, data)| data) } diff --git a/crates/common/src/provider/mpp/transport.rs b/crates/common/src/provider/mpp/transport.rs index bba15ebce4f90..aea72174c9756 100644 --- a/crates/common/src/provider/mpp/transport.rs +++ b/crates/common/src/provider/mpp/transport.rs @@ -228,59 +228,11 @@ where // held until the retry response is fully handled. let _pay_guard = self.provider.lock_pay().await; - let www_auth_values: Vec<&str> = resp - .headers() - .get_all(WWW_AUTHENTICATE_HEADER) - .iter() - .filter_map(|v| v.to_str().ok()) - .collect(); - - if www_auth_values.is_empty() { - return Err(TransportErrorKind::custom(std::io::Error::other( - "402 response missing WWW-Authenticate header", - ))); - } - - let challenges: Vec<_> = parse_www_authenticate_all(www_auth_values) - .into_iter() - .filter_map(|r| r.ok()) - .collect(); - - // Try each challenge until we find one with a matching key (chain + currency) - // in keys.toml. This handles servers that offer multiple chains and currencies - // (e.g. mainnet + testnet) — we pick the first one the user has a key for. - let mut last_resolve_err: Option = None; - let resolved_pair = challenges.iter().find_map(|c| { - let (chain_id, currency) = extract_challenge_chain_and_currency(c); - let currency = currency.and_then(|s| s.parse().ok()); - match self.provider.resolve_for(DiscoverOptions { chain_id, currency }) { - Ok(provider) => { - provider.supports(c.method.as_str(), c.intent.as_str()).then_some((provider, c)) - } - Err(e) => { - last_resolve_err = Some(e); - None - } - } - }); - - let (resolved, challenge) = resolved_pair.ok_or_else(|| { - // Surface the real config error (invalid key, bad key_authorization, etc.) - // instead of a generic "no supported challenge" message. - if let Some(err) = last_resolve_err { - return err; - } - let offered: Vec<_> = - challenges.iter().map(|c| format!("{}.{}", c.method, c.intent)).collect(); - TransportErrorKind::custom(std::io::Error::other(format!( - "no supported MPP challenge; server offered [{}]", - offered.join(", "), - ))) - })?; + let (resolved, challenge) = Self::select_challenge(&resp, &self.provider)?; debug!(id = %challenge.id, method = %challenge.method, intent = %challenge.intent, "received MPP 402 challenge, paying"); - let credential = resolved.pay(challenge).await.map_err(|e| { + let credential = resolved.pay(&challenge).await.map_err(|e| { TransportErrorKind::custom(std::io::Error::other(format!("MPP payment failed: {e}"))) })?; @@ -316,33 +268,7 @@ where self.provider.commit_topup_and_track_voucher(); let resolved = self.provider.resolve()?; - let credential = resolved.pay(challenge).await.map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(std::io::Error::other(format!( - "MPP payment failed: {e}" - ))) - })?; - let auth_header = format_authorization(&credential).map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(std::io::Error::other(format!( - "failed to format MPP credential: {e}" - ))) - })?; - - let voucher_resp = self - .client - .post(self.url.clone()) - .timeout(MPP_RETRY_TIMEOUT) - .headers(headers.clone()) - .header("content-type", "application/json") - .header(AUTHORIZATION_HEADER, &auth_header) - .body(body.clone()) - .send() - .await - .map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(e) - })?; + let voucher_resp = self.pay_and_retry(&challenge, &resolved, &headers, &body).await?; let result = Self::handle_response(voucher_resp).await; if result.is_ok() { @@ -388,31 +314,8 @@ where debug!("MPP voucher stale, retrying with fresh voucher"); let resolved = self.provider.resolve()?; if resolved.supports(challenge.method.as_str(), challenge.intent.as_str()) { - let credential = resolved.pay(challenge).await.map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( - "MPP payment failed: {e}" - ))) - })?; - let auth_header = format_authorization(&credential).map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( - "failed to format MPP credential: {e}" - ))) - })?; - - let final_resp = self - .client - .post(self.url.clone()) - .timeout(MPP_RETRY_TIMEOUT) - .headers(headers.clone()) - .header("content-type", "application/json") - .header(AUTHORIZATION_HEADER, auth_header) - .body(body.clone()) - .send() - .await - .map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(e) - })?; + let final_resp = + self.pay_and_retry(&challenge, &resolved, &headers, &body).await?; let result = Self::handle_response(final_resp).await; if result.is_ok() { @@ -425,110 +328,42 @@ where } // Retry with key_authorization when the error explicitly indicates - // the access key is not provisioned on-chain. Unconditionally - // retrying caused "access key already exists" when the 402 was for - // a different reason (e.g. wrong currency, insufficient balance). + // the access key is not provisioned on-chain, or when verification + // failed and the key appears provisioned (first-time provisioning + // where key_auth was stripped but not yet provisioned on-chain). + // + // We fetch a fresh challenge because the server may have consumed + // the original challenge ID on first use. let needs_key_provisioning = problem_type.ends_with("/key-not-provisioned") || detail.contains("access key does not exist") || detail.contains("key is not provisioned"); - if needs_key_provisioning { - self.provider.set_key_provisioned(false); - let resolved = self.provider.resolve()?; - - if resolved.supports(challenge.method.as_str(), challenge.intent.as_str()) { - debug!( - "MPP 402 indicates key not provisioned, retrying with key_authorization" - ); - - let credential = resolved.pay(challenge).await.map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( - "MPP payment failed: {e}" - ))) - })?; - let auth_header = format_authorization(&credential).map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( - "failed to format MPP credential: {e}" - ))) - })?; - - let final_resp = self - .client - .post(self.url.clone()) - .timeout(MPP_RETRY_TIMEOUT) - .headers(headers) - .header("content-type", "application/json") - .header(AUTHORIZATION_HEADER, auth_header) - .body(body) - .send() - .await - .map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(e) - })?; - - let result = Self::handle_response(final_resp).await; - if result.is_ok() { - self.provider.set_key_provisioned(true); - self.provider.flush_pending(); - } else { - self.provider.rollback_pending(); - } - return result; - } - } - - // Retry with key_authorization when verification failed and key appears - // provisioned — handles first-time provisioning where key_auth was stripped - // but the key was not yet provisioned on-chain. let needs_verification_retry = (problem_type.ends_with("/verification-failed") || detail.contains("verification-failed")) && self.provider.is_key_provisioned(); - if needs_verification_retry { + if needs_key_provisioning || needs_verification_retry { + debug!( + problem_type, + "MPP 402 key not provisioned/verification-failed, retrying with key_authorization" + ); self.provider.set_key_provisioned(false); - let resolved = self.provider.resolve()?; + self.provider.rollback_pending(); - if resolved.supports(challenge.method.as_str(), challenge.intent.as_str()) { - debug!( - "MPP 402 verification-failed with key provisioned, retrying with key_authorization" - ); + let (resolved, fresh_challenge) = + self.fetch_fresh_challenge(&headers, &body).await?; - let credential = resolved.pay(challenge).await.map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( - "MPP payment failed: {e}" - ))) - })?; - let auth_header = format_authorization(&credential).map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( - "failed to format MPP credential: {e}" - ))) - })?; - - let final_resp = self - .client - .post(self.url.clone()) - .timeout(MPP_RETRY_TIMEOUT) - .headers(headers) - .header("content-type", "application/json") - .header(AUTHORIZATION_HEADER, auth_header) - .body(body) - .send() - .await - .map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(e) - })?; + let final_resp = + self.pay_and_retry(&fresh_challenge, &resolved, &headers, &body).await?; - let result = Self::handle_response(final_resp).await; - if result.is_ok() { - self.provider.set_key_provisioned(true); - self.provider.flush_pending(); - } else { - self.provider.rollback_pending(); - } - return result; + let result = Self::handle_response(final_resp).await; + if result.is_ok() { + self.provider.set_key_provisioned(true); + self.provider.flush_pending(); + } else { + self.provider.rollback_pending(); } + return result; } self.provider.rollback_pending(); @@ -548,6 +383,125 @@ where result } + /// Pay a challenge and send the authenticated retry request. + async fn pay_and_retry( + &self, + challenge: &mpp::protocol::core::PaymentChallenge, + provider: &P::Provider, + headers: &reqwest::header::HeaderMap, + body: &[u8], + ) -> TransportResult { + let credential = provider.pay(challenge).await.map_err(|e| { + self.provider.rollback_pending(); + TransportErrorKind::custom(std::io::Error::other(format!("MPP payment failed: {e}"))) + })?; + + let auth_header = format_authorization(&credential).map_err(|e| { + self.provider.rollback_pending(); + TransportErrorKind::custom(std::io::Error::other(format!( + "failed to format MPP credential: {e}" + ))) + })?; + + self.client + .post(self.url.clone()) + .timeout(MPP_RETRY_TIMEOUT) + .headers(headers.clone()) + .header("content-type", "application/json") + .header(AUTHORIZATION_HEADER, auth_header) + .body(body.to_vec()) + .send() + .await + .map_err(|e| { + self.provider.rollback_pending(); + TransportErrorKind::custom(e) + }) + } + + /// Fetch a fresh 402 challenge from the server (unauthenticated request). + /// + /// Returns `Ok(Some((provider, challenge)))` if the server returns a 402 + /// with a matching challenge. Returns `Ok(None)` with the response handled + /// if the server returns a non-402 status. Errors on network or parse failures. + async fn fetch_fresh_challenge( + &self, + headers: &reqwest::header::HeaderMap, + body: &[u8], + ) -> TransportResult<(P::Provider, mpp::protocol::core::PaymentChallenge)> { + let fresh_resp = self + .client + .post(self.url.clone()) + .timeout(MPP_RETRY_TIMEOUT) + .headers(headers.clone()) + .header("content-type", "application/json") + .body(body.to_vec()) + .send() + .await + .map_err(TransportErrorKind::custom)?; + + if fresh_resp.status() != StatusCode::PAYMENT_REQUIRED { + // Non-402 → return whatever the server sent (could be success or error). + let result = Self::handle_response(fresh_resp).await; + return Err(result.err().unwrap_or_else(|| { + TransportErrorKind::custom(std::io::Error::other( + "unexpected success on unauthenticated fresh probe", + )) + })); + } + + Self::select_challenge(&fresh_resp, &self.provider) + } + + /// Parse `WWW-Authenticate` challenges from a 402 response and resolve + /// the first one matching a locally configured key (chain + currency). + fn select_challenge( + resp: &reqwest::Response, + provider: &P, + ) -> TransportResult<(P::Provider, mpp::protocol::core::PaymentChallenge)> { + let www_auth_values: Vec<&str> = resp + .headers() + .get_all(WWW_AUTHENTICATE_HEADER) + .iter() + .filter_map(|v| v.to_str().ok()) + .collect(); + + if www_auth_values.is_empty() { + return Err(TransportErrorKind::custom(std::io::Error::other( + "402 response missing WWW-Authenticate header", + ))); + } + + let challenges: Vec<_> = parse_www_authenticate_all(www_auth_values) + .into_iter() + .filter_map(|r| r.ok()) + .collect(); + + let mut last_resolve_err: Option = None; + let resolved_pair = challenges.iter().find_map(|c| { + let (chain_id, currency) = extract_challenge_chain_and_currency(c); + let currency = currency.and_then(|s| s.parse().ok()); + match provider.resolve_for(DiscoverOptions { chain_id, currency }) { + Ok(p) => p.supports(c.method.as_str(), c.intent.as_str()).then_some((p, c.clone())), + Err(e) => { + last_resolve_err = Some(e); + None + } + } + }); + + resolved_pair.ok_or_else(|| { + if let Some(err) = last_resolve_err { + return err; + } + let offered: Vec<_> = + challenges.iter().map(|c| format!("{}.{}", c.method, c.intent)).collect(); + TransportErrorKind::custom(std::io::Error::other(format!( + "no supported MPP challenge; server offered [{}]", + offered.join(", "), + ))) + }) + } + async fn handle_response(resp: reqwest::Response) -> TransportResult { let status = resp.status(); debug!(%status, "received response from MPP transport"); diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs new file mode 100644 index 0000000000000..893e7806d1dbc --- /dev/null +++ b/crates/common/src/transactions.rs @@ -0,0 +1,281 @@ +//! Wrappers for transactions. + +use alloy_consensus::{Transaction, TxEnvelope, transaction::SignerRecoverable}; +use alloy_eips::eip7702::SignedAuthorization; +use alloy_network::{ + AnyTransactionReceipt, Ethereum, Network, TransactionBuilder7702, TransactionResponse, +}; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_provider::{ + Provider, + network::{AnyNetwork, ReceiptResponse, TransactionBuilder}, +}; +use alloy_rpc_types::{BlockId, TransactionRequest}; +use eyre::Result; +use foundry_common_fmt::{UIfmt, UIfmtReceiptExt, get_pretty_receipt_attr}; +use serde::{Deserialize, Serialize}; + +/// Helper type to carry a transaction along with an optional revert reason +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TransactionReceiptWithRevertReason { + /// The underlying transaction receipt + #[serde(flatten)] + pub receipt: N::ReceiptResponse, + + /// The revert reason string if the transaction status is failed + #[serde(skip_serializing_if = "Option::is_none", rename = "revertReason")] + pub revert_reason: Option, +} + +impl TransactionReceiptWithRevertReason +where + N::TxEnvelope: Clone, + N::ReceiptResponse: UIfmtReceiptExt, +{ + /// 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: &dyn Provider) -> Result<()> { + self.revert_reason = self.fetch_revert_reason(provider).await?; + Ok(()) + } + + async fn fetch_revert_reason(&self, provider: &dyn Provider) -> Result> { + // If the transaction succeeded, there is no revert reason to fetch + if self.receipt.status() { + return Ok(None); + } + + let transaction = provider + .get_transaction_by_hash(self.receipt.transaction_hash()) + .await + .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() { + let mut call_request: N::TransactionRequest = transaction.as_ref().clone().into(); + call_request.set_from(transaction.from()); + match provider.call(call_request).block(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") + } +} + +impl From for TransactionReceiptWithRevertReason { + fn from(receipt: AnyTransactionReceipt) -> Self { + Self { receipt, revert_reason: None } + } +} + +impl From> for AnyTransactionReceipt { + fn from(receipt_with_reason: TransactionReceiptWithRevertReason) -> Self { + receipt_with_reason.receipt + } +} + +impl UIfmt for TransactionReceiptWithRevertReason +where + N::ReceiptResponse: UIfmt, +{ + fn pretty(&self) -> String { + if let Some(revert_reason) = &self.revert_reason { + format!( + "{} +revertReason {}", + self.receipt.pretty(), + revert_reason + ) + } else { + self.receipt.pretty() + } + } +} + +impl UIfmt for TransactionMaybeSigned { + fn pretty(&self) -> String { + match self { + Self::Signed { tx, .. } => tx.pretty(), + Self::Unsigned(tx) => format!( + " +accessList {} +chainId {} +gasLimit {} +gasPrice {} +input {} +maxFeePerBlobGas {} +maxFeePerGas {} +maxPriorityFeePerGas {} +nonce {} +to {} +type {} +value {}", + tx.access_list + .as_ref() + .map(|a| a.iter().collect::>()) + .unwrap_or_default() + .pretty(), + tx.chain_id.pretty(), + tx.gas_limit().unwrap_or_default(), + tx.gas_price.pretty(), + tx.input.input.pretty(), + tx.max_fee_per_blob_gas.pretty(), + tx.max_fee_per_gas.pretty(), + tx.max_priority_fee_per_gas.pretty(), + tx.nonce.pretty(), + tx.to.as_ref().map(|a| a.to()).unwrap_or_default().pretty(), + tx.transaction_type.unwrap_or_default(), + tx.value.pretty(), + ), + } + } +} + +fn extract_revert_reason>(error_string: S) -> Option { + let message_substr = "execution reverted: "; + error_string + .as_ref() + .find(message_substr) + .map(|index| error_string.as_ref().split_at(index + message_substr.len()).1.to_string()) +} + +/// Returns the `UiFmt::pretty()` formatted attribute of the transaction receipt with revert reason +pub fn get_pretty_receipt_w_reason_attr( + receipt: &TransactionReceiptWithRevertReason, + attr: &str, +) -> Option +where + N: Network, + N::ReceiptResponse: UIfmtReceiptExt, +{ + // Handle revert reason first, then delegate to the receipt formatting function + if matches!(attr, "revertReason" | "revert_reason") { + return Some(receipt.revert_reason.pretty()); + } + get_pretty_receipt_attr::(&receipt.receipt, attr) +} + +/// Used for broadcasting transactions +/// A transaction can either be a [`TransactionRequest`] waiting to be signed +/// or a [`TxEnvelope`], already signed +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum TransactionMaybeSigned { + Signed { + #[serde(flatten)] + tx: N::TxEnvelope, + from: Address, + }, + Unsigned(N::TransactionRequest), +} + +impl TransactionMaybeSigned { + /// Creates a new (unsigned) transaction for broadcast + pub fn new(tx: N::TransactionRequest) -> Self { + Self::Unsigned(tx) + } + + /// Creates a new signed transaction for broadcast. + pub fn new_signed( + tx: N::TxEnvelope, + ) -> core::result::Result + where + N::TxEnvelope: SignerRecoverable, + { + let from = tx.recover_signer()?; + Ok(Self::Signed { tx, from }) + } + + pub fn is_unsigned(&self) -> bool { + matches!(self, Self::Unsigned(_)) + } + + pub fn as_unsigned_mut(&mut self) -> Option<&mut N::TransactionRequest> { + match self { + Self::Unsigned(tx) => Some(tx), + _ => None, + } + } + + pub fn from(&self) -> Option
{ + match self { + Self::Signed { from, .. } => Some(*from), + Self::Unsigned(tx) => tx.from(), + } + } + + pub fn input(&self) -> Option<&Bytes> { + match self { + Self::Signed { tx, .. } => Some(tx.input()), + Self::Unsigned(tx) => tx.input(), + } + } + + pub fn to(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.kind()), + Self::Unsigned(tx) => tx.kind(), + } + } + + pub fn value(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.value()), + Self::Unsigned(tx) => tx.value(), + } + } + + pub fn gas(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.gas_limit() as u128), + Self::Unsigned(tx) => tx.gas_limit().map(|g| g as u128), + } + } + + pub fn nonce(&self) -> Option { + match self { + Self::Signed { tx, .. } => Some(tx.nonce()), + Self::Unsigned(tx) => tx.nonce(), + } + } + + pub fn authorization_list(&self) -> Option> + where + N::TransactionRequest: TransactionBuilder7702, + { + match self { + Self::Signed { tx, .. } => tx.authorization_list().map(|auths| auths.to_vec()), + Self::Unsigned(tx) => tx.authorization_list().map(|auths| auths.to_vec()), + } + .filter(|auths| !auths.is_empty()) + } +} + +impl From for TransactionMaybeSigned { + fn from(tx: TransactionRequest) -> Self { + Self::new(tx) + } +} + +impl TryFrom for TransactionMaybeSigned { + type Error = alloy_consensus::crypto::RecoveryError; + + fn try_from(tx: TxEnvelope) -> core::result::Result { + Self::new_signed(tx) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_extract_revert_reason() { + 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), None); + } +} diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 39c281e81be25..4c6978e0d9750 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -49,6 +49,9 @@ walkdir.workspace = true yansi.workspace = true clap = { version = "4", features = ["derive"] } +# schema +schemars = { version = "1.0", optional = true } + [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2" @@ -60,3 +63,4 @@ tempfile.workspace = true [features] isolate-by-default = [] +schema = ["dep:schemars"] diff --git a/crates/config/assets/config.schema.json b/crates/config/assets/config.schema.json new file mode 100644 index 0000000000000..00a381eaf66ff --- /dev/null +++ b/crates/config/assets/config.schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Config", + "description": "Foundry configuration. Learn more: ", + "type": "object" +} \ No newline at end of file diff --git a/crates/config/spec/Cargo.toml b/crates/config/spec/Cargo.toml new file mode 100644 index 0000000000000..89d40cf5606ce --- /dev/null +++ b/crates/config/spec/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "foundry-config-spec" +description = "Foundry configuration specification" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +foundry-config.workspace = true +serde.workspace = true + +# schema +schemars = { version = "1.0", optional = true } + +[dev-dependencies] +serde_json.workspace = true + +[features] +schema = ["dep:schemars", "foundry-config/schema"] diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs new file mode 100644 index 0000000000000..5a362e963d956 --- /dev/null +++ b/crates/config/spec/src/lib.rs @@ -0,0 +1,64 @@ +//! Config specification for Foundry. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use foundry_config::Config; +use serde::{Deserialize, Serialize}; + +// The `config.json` schema. +/// Foundry configuration. Learn more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct ConfigSchema { + #[serde(flatten)] + pub config: Config, +} + +#[cfg(test)] +#[expect(clippy::disallowed_macros)] +mod tests { + use super::*; + use std::{fs, path::Path}; + + #[cfg(feature = "schema")] + const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/config.schema.json"); + + /// Generates the configuration JSON schema. + #[cfg(feature = "schema")] + fn json_schema() -> String { + serde_json::to_string_pretty(&schemars::schema_for!(ConfigSchema)).unwrap() + } + + #[test] + #[cfg(feature = "schema")] + fn schema_up_to_date() { + ensure_file_contents(Path::new(SCHEMA_PATH), &json_schema()); + } + + /// Checks that the `file` has the specified `contents`. If that is not the + /// case, updates the file and then fails the test. + fn ensure_file_contents(file: &Path, contents: &str) { + if let Ok(old_contents) = fs::read_to_string(file) + && normalize_newlines(&old_contents) == normalize_newlines(contents) + { + // File is already up to date. + return; + } + + eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); + if std::env::var("CI").is_ok() { + eprintln!(" NOTE: run `cargo spec-config` locally and commit the updated files\n"); + } + if let Some(parent) = file.parent() { + let _ = fs::create_dir_all(parent); + } + fs::write(file, contents).unwrap(); + panic!("some file was not up to date and has been updated, simply re-run the tests"); + } + + fn normalize_newlines(s: &str) -> String { + s.replace("\r\n", "\n") + } +} diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index af25127beb585..64ee7e05fc75f 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -453,6 +453,18 @@ impl DerefMut for ResolvedRpcEndpoints { } } +/// Returns the URL for a built-in RPC alias, if one exists. +/// +/// Built-in aliases act as fallbacks: they are only used when the alias has **not** been +/// defined by the user in `[rpc_endpoints]` or resolved via MESC. +pub fn builtin_rpc_url(alias: &str) -> Option<&'static str> { + match alias { + "tempo" => Some("https://rpc.mpp.tempo.xyz"), + "moderato" => Some("https://rpc.mpp.moderato.tempo.xyz"), + _ => None, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 0cc28c93057dd..c0f8249f37dfd 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -45,7 +45,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; use std::{ borrow::Cow, collections::BTreeMap, - fs, + fs, io, path::{Path, PathBuf}, str::FromStr, }; @@ -59,6 +59,7 @@ pub use utils::*; mod endpoints; pub use endpoints::{ ResolvedRpcEndpoint, ResolvedRpcEndpoints, RpcEndpoint, RpcEndpointUrl, RpcEndpoints, + builtin_rpc_url, }; mod etherscan; @@ -856,6 +857,17 @@ impl Config { config.normalize_optimizer_settings(); config.normalize_hardfork_settings().map_err(ExtractConfigError::new)?; + // Validate optimizer_runs does not exceed u32::MAX (Solidity compiler limit) + if let Some(runs) = config.optimizer_runs + && runs > u32::MAX as usize + { + return Err(ExtractConfigError::new(Error::from(format!( + "`optimizer_runs` value {} exceeds maximum allowed value of {}", + runs, + u32::MAX + )))); + } + Ok(config) } @@ -1257,7 +1269,9 @@ impl Config { let project = builder.build(self.compiler()?)?; if self.force { - self.cleanup(&project)?; + // Warnings are intentionally dropped here because `sh_warn!` is a circular + // dependency. Callers that need warnings should call `cleanup()` directly. + let _ = self.cleanup(&project); } Ok(project) @@ -1286,21 +1300,40 @@ impl Config { } /// Cleans the project. + /// + /// Returns a list of warning messages for any non-fatal cleanup failures. Cleanup is + /// best-effort: all steps are attempted even if some fail. pub fn cleanup>( &self, project: &Project, - ) -> Result<(), SolcError> { - project.cleanup()?; + ) -> Result, SolcError> { + let mut warnings = Vec::new(); + + if let Err(err) = project.cleanup() { + warnings.push(format!("failed to clean project artifacts: {err}")); + } // Remove last test run failures file. - let _ = fs::remove_file(&self.test_failures_file); + if let Err(err) = fs::remove_file(&self.test_failures_file) + && err.kind() != io::ErrorKind::NotFound + { + warnings.push(format!( + "failed to remove test failures file {}: {err}", + self.test_failures_file.display() + )); + } // Remove fuzz and invariant cache directories. - let remove_test_dir = |test_dir: &Option| { + let mut 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); + if let Err(err) = fs::remove_dir_all(&path) + && err.kind() != io::ErrorKind::NotFound + { + warnings.push(format!( + "failed to remove test cache directory {}: {err}", + path.display() + )); } } }; @@ -1309,7 +1342,7 @@ impl Config { remove_test_dir(&self.invariant.corpus.corpus_dir); remove_test_dir(&self.invariant.failure_persist_dir); - Ok(()) + Ok(warnings) } /// Ensures that the configured version is installed if explicitly set @@ -1529,6 +1562,10 @@ impl Config { return Some(Ok(Cow::Owned(mesc_url))); } + if let Some(builtin) = crate::endpoints::builtin_rpc_url(maybe_alias) { + return Some(Ok(Cow::Borrowed(builtin))); + } + None } @@ -2138,63 +2175,108 @@ impl Config { } /// Clears the foundry cache. - pub fn clean_foundry_cache() -> eyre::Result<()> { + /// + /// Returns warnings for any non-fatal deletion failures. + pub fn clean_foundry_cache() -> eyre::Result> { if let Some(cache_dir) = Self::foundry_cache_dir() { let path = cache_dir.as_path(); - let _ = fs::remove_dir_all(path); + if let Err(err) = fs::remove_dir_all(path) + && err.kind() != io::ErrorKind::NotFound + { + return Ok(vec![format!( + "failed to remove foundry cache at {}: {err}", + path.display() + )]); + } } else { eyre::bail!("failed to get foundry_cache_dir"); } - Ok(()) + Ok(vec![]) } /// Clears the foundry cache for `chain`. - pub fn clean_foundry_chain_cache(chain: Chain) -> eyre::Result<()> { + /// + /// Returns warnings for any non-fatal deletion failures. + pub fn clean_foundry_chain_cache(chain: Chain) -> eyre::Result> { if let Some(cache_dir) = Self::foundry_chain_cache_dir(chain) { let path = cache_dir.as_path(); - let _ = fs::remove_dir_all(path); + if let Err(err) = fs::remove_dir_all(path) + && err.kind() != io::ErrorKind::NotFound + { + return Ok(vec![format!( + "failed to remove foundry cache for chain {chain} at {}: {err}", + path.display() + )]); + } } else { eyre::bail!("failed to get foundry_chain_cache_dir"); } - Ok(()) + Ok(vec![]) } /// Clears the foundry cache for `chain` and `block`. - pub fn clean_foundry_block_cache(chain: Chain, block: u64) -> eyre::Result<()> { + /// + /// Returns warnings for any non-fatal deletion failures. + pub fn clean_foundry_block_cache(chain: Chain, block: u64) -> eyre::Result> { if let Some(cache_dir) = Self::foundry_block_cache_dir(chain, block) { let path = cache_dir.as_path(); - let _ = fs::remove_dir_all(path); + if let Err(err) = fs::remove_dir_all(path) + && err.kind() != io::ErrorKind::NotFound + { + return Ok(vec![format!( + "failed to remove foundry cache for chain {chain} block {block} at {}: {err}", + path.display() + )]); + } } else { eyre::bail!("failed to get foundry_block_cache_dir"); } - Ok(()) + Ok(vec![]) } /// Clears the foundry etherscan cache. - pub fn clean_foundry_etherscan_cache() -> eyre::Result<()> { + /// + /// Returns warnings for any non-fatal deletion failures. + pub fn clean_foundry_etherscan_cache() -> eyre::Result> { if let Some(cache_dir) = Self::foundry_etherscan_cache_dir() { let path = cache_dir.as_path(); - let _ = fs::remove_dir_all(path); + if let Err(err) = fs::remove_dir_all(path) + && err.kind() != io::ErrorKind::NotFound + { + return Ok(vec![format!( + "failed to remove foundry etherscan cache at {}: {err}", + path.display() + )]); + } } else { eyre::bail!("failed to get foundry_etherscan_cache_dir"); } - Ok(()) + Ok(vec![]) } /// Clears the foundry etherscan cache for `chain`. - pub fn clean_foundry_etherscan_chain_cache(chain: Chain) -> eyre::Result<()> { + /// + /// Returns warnings for any non-fatal deletion failures. + pub fn clean_foundry_etherscan_chain_cache(chain: Chain) -> eyre::Result> { if let Some(cache_dir) = Self::foundry_etherscan_chain_cache_dir(chain) { let path = cache_dir.as_path(); - let _ = fs::remove_dir_all(path); + if let Err(err) = fs::remove_dir_all(path) + && err.kind() != io::ErrorKind::NotFound + { + return Ok(vec![format!( + "failed to remove foundry etherscan cache for chain {chain} at {}: {err}", + path.display() + )]); + } } else { eyre::bail!("failed to get foundry_etherscan_cache_dir for chain: {}", chain); } - Ok(()) + Ok(vec![]) } /// List the data in the foundry cache. diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 3e915b1b7f479..3d4c57fe5dc3e 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -151,6 +151,12 @@ impl From> for Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index 8081de7dac660..085664f85fa81 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,4 +1,4 @@ -use crate::{AsDoc, CommentTag, Comments, Deployment, Markdown, writer::traits::ParamLike}; +use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; use solang_parser::pt::{ EnumDefinition, ErrorParameter, EventParameter, Parameter, VariableDeclaration, @@ -89,17 +89,6 @@ impl BufWriter { writeln!(self.buf, "{}", Markdown::Italic(text)) } - /// Writes dev content to the buffer, handling markdown lists properly. - /// If the content contains markdown lists, it formats them correctly. - /// Otherwise, it writes the content in italics. - pub fn write_dev_content(&mut self, text: &str) -> fmt::Result { - for line in text.lines() { - writeln!(self.buf, "{line}")?; - } - - Ok(()) - } - /// Writes bold text to the buffer formatted as [Markdown::Bold]. pub fn write_bold(&mut self, text: &str) -> fmt::Result { writeln!(self.buf, "{}", Markdown::Bold(text)) @@ -202,7 +191,8 @@ impl BufWriter { params: &EnumDefinition, comments: &Comments, ) -> fmt::Result { - let comments = comments.include_tags(&[CommentTag::Param]); + let comments = + comments.include_tags(&[CommentTag::Param, CommentTag::Custom("variant".to_string())]); // There is nothing to write. if comments.is_empty() { @@ -215,7 +205,7 @@ impl BufWriter { self.write_piped(&VARIANTS_TABLE_HEADERS.join("|"))?; self.write_piped(&VARIANTS_TABLE_SEPARATOR)?; - for value in ¶ms.values { + for value in params.values.iter() { let param_name = value.as_ref().map(|v| v.name.clone()); let comment = param_name.as_ref().and_then(|name| { @@ -223,7 +213,7 @@ impl BufWriter { }); let row = [ - Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + Markdown::Code(param_name.as_deref().unwrap_or("")).as_doc()?, comment.unwrap_or_default().replace('\n', " "), ]; self.write_piped(&row.join("|"))?; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 3db3c082fff14..03d569c17f500 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -59,7 +59,6 @@ op-alloy-network.workspace = true op-revm.workspace = true tempo-revm.workspace = true tempo-alloy.workspace = true -tempo-chainspec.workspace = true tempo-contracts.workspace = true tempo-evm.workspace = true tempo-precompiles.workspace = true diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 87a92e9d97460..8c80b21121c37 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -2,32 +2,29 @@ use super::BackendError; use crate::{ - FoundryInspectorExt, + Env, InspectorExt, backend::{ Backend, DatabaseExt, JournaledState, LocalForkId, RevertStateSnapshotAction, diagnostic::RevertDiagnostic, }, - evm::{ - EvmEnvFor, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork, HaltReasonFor, SpecFor, - TxEnvFor, - }, fork::{CreateFork, ForkId}, }; -use alloy_evm::Evm; +use alloy_evm::{Evm, EvmEnv}; use alloy_genesis::GenesisAccount; -use alloy_primitives::{Address, B256, TxKind, U256}; +use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::TransactionRequest; use eyre::WrapErr; use foundry_fork_db::DatabaseError; use revm::{ Database, DatabaseCommit, bytecode::Bytecode, - context::{ContextTr, Transaction}, + context::TxEnv, context_interface::result::ResultAndState, database::DatabaseRef, - primitives::AddressMap, + primitives::{HashMap as Map, hardfork::SpecId}, state::{Account, AccountInfo, EvmState}, }; -use std::{borrow::Cow, collections::BTreeMap, fmt::Debug}; +use std::{borrow::Cow, collections::BTreeMap}; /// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called. /// @@ -45,35 +42,21 @@ use std::{borrow::Cow, collections::BTreeMap, fmt::Debug}; /// 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. -pub struct CowBackend<'a, FEN: FoundryEvmNetwork> { +#[derive(Clone, Debug)] +pub struct CowBackend<'a> { /// The underlying `Backend`. /// /// No calls on the `CowBackend` will ever persistently modify the `backend`'s state. - pub backend: Cow<'a, Backend>, - /// Pending initialization params for the backend on first mutable access. + pub backend: Cow<'a, Backend>, + /// The [SpecId] to initialize the backend with on first mutable access. /// `None` means the backend has already been initialized for the current call. - pending_init: Option<(SpecFor, Address, TxKind)>, -} - -impl Clone for CowBackend<'_, FEN> { - fn clone(&self) -> Self { - Self { backend: self.backend.clone(), pending_init: self.pending_init } - } + spec_id: Option, } -impl Debug for CowBackend<'_, FEN> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CowBackend") - .field("backend", &self.backend) - .field("pending_init", &self.pending_init) - .finish() - } -} - -impl<'a, FEN: FoundryEvmNetwork> CowBackend<'a, FEN> { +impl<'a> CowBackend<'a> { /// Creates a new `CowBackend` with the given `Backend`. - pub const fn new_borrowed(backend: &'a Backend) -> Self { - Self { backend: Cow::Borrowed(backend), pending_init: None } + pub fn new_borrowed(backend: &'a Backend) -> Self { + Self { backend: Cow::Borrowed(backend), spec_id: Some(SpecId::default()) } } /// Executes the configured transaction of the `env` without committing state changes @@ -81,26 +64,25 @@ impl<'a, FEN: FoundryEvmNetwork> CowBackend<'a, FEN> { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect FoundryInspectorExt>>( + pub fn inspect( &mut self, - evm_env: &mut EvmEnvFor, - tx_env: &mut TxEnvFor, + env: &mut Env, inspector: I, - ) -> eyre::Result>> { + ) -> 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.pending_init = Some((evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind())); + self.spec_id = Some(env.evm_env.cfg_env.spec); - let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector( + let mut evm = crate::evm::new_evm_with_inspector( self, - evm_env.clone(), + env.evm_env.clone(), + env.tx.clone(), inspector, ); - let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?; + let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?; - *tx_env = evm.tx().clone(); - *evm_env = evm.finish().1; + *env = Env::from(evm.cfg.clone(), evm.block.clone(), evm.tx.clone()); Ok(res) } @@ -115,42 +97,40 @@ impl<'a, FEN: FoundryEvmNetwork> CowBackend<'a, FEN> { /// Returns a mutable instance of the Backend. /// /// If this is the first time this is called, the backed is cloned and initialized. - fn backend_mut(&mut self) -> &mut Backend { - if let Some((spec_id, caller, tx_kind)) = self.pending_init.take() { + fn backend_mut(&mut self, evm_env: &EvmEnv, tx_env: &TxEnv) -> &mut Backend { + if let Some(spec_id) = self.spec_id.take() { let backend = self.backend.to_mut(); - backend.initialize(spec_id, caller, tx_kind); + let mut env = Env { evm_env: evm_env.clone(), tx: tx_env.clone() }; + env.evm_env.cfg_env.spec = spec_id; + backend.initialize(&env); return backend; } self.backend.to_mut() } /// Returns a mutable instance of the Backend if it is initialized. - fn initialized_backend_mut(&mut self) -> Option<&mut Backend> { - if self.pending_init.is_none() { + fn initialized_backend_mut(&mut self) -> Option<&mut Backend> { + if self.spec_id.is_none() { return Some(self.backend.to_mut()); } None } } -impl DatabaseExt for CowBackend<'_, FEN> { - fn snapshot_state( - &mut self, - journaled_state: &JournaledState, - evm_env: &EvmEnvFor, - ) -> U256 { - self.backend_mut().snapshot_state(journaled_state, evm_env) +impl DatabaseExt for CowBackend<'_> { + fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256 { + self.backend_mut(evm_env, &TxEnv::default()).snapshot_state(journaled_state, evm_env) } fn revert_state( &mut self, id: U256, journaled_state: &JournaledState, - evm_env: &mut EvmEnvFor, - caller: Address, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, action: RevertStateSnapshotAction, ) -> Option { - self.backend_mut().revert_state(id, journaled_state, evm_env, caller, action) + self.backend_mut(evm_env, tx_env).revert_state(id, journaled_state, evm_env, tx_env, action) } fn delete_state_snapshot(&mut self, id: U256) -> bool { @@ -182,56 +162,81 @@ impl DatabaseExt for CowBackend<'_, FEN fn select_fork( &mut self, id: LocalForkId, - evm_env: &mut EvmEnvFor, - tx_env: &mut TxEnvFor, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - self.backend_mut().select_fork(id, evm_env, tx_env, journaled_state) + self.backend_mut(evm_env, tx_env).select_fork(id, evm_env, tx_env, journaled_state) } fn roll_fork( &mut self, id: Option, block_number: u64, - evm_env: &mut EvmEnvFor, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - self.backend_mut().roll_fork(id, block_number, evm_env, journaled_state) + self.backend_mut(evm_env, tx_env).roll_fork( + id, + block_number, + evm_env, + tx_env, + journaled_state, + ) } fn roll_fork_to_transaction( &mut self, id: Option, transaction: B256, - evm_env: &mut EvmEnvFor, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - self.backend_mut().roll_fork_to_transaction(id, transaction, evm_env, journaled_state) + self.backend_mut(evm_env, tx_env).roll_fork_to_transaction( + id, + transaction, + evm_env, + tx_env, + journaled_state, + ) } fn transact( &mut self, id: Option, transaction: B256, - evm_env: EvmEnvFor, + evm_env: EvmEnv, + tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn for<'db> FoundryInspectorExt< - ::FoundryContext<'db>, - >, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { - self.backend_mut().transact(id, transaction, evm_env, journaled_state, inspector) + self.backend_mut(&evm_env, &tx_env).transact( + id, + transaction, + evm_env, + tx_env, + journaled_state, + inspector, + ) } fn transact_from_tx( &mut self, - tx_env: TxEnvFor, - evm_env: EvmEnvFor, + transaction: &TransactionRequest, + evm_env: EvmEnv, + tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn for<'db> FoundryInspectorExt< - ::FoundryContext<'db>, - >, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { - self.backend_mut().transact_from_tx(tx_env, evm_env, journaled_state, inspector) + self.backend_mut(&evm_env, &tx_env).transact_from_tx( + transaction, + evm_env, + tx_env, + journaled_state, + inspector, + ) } fn active_fork_id(&self) -> Option { @@ -300,7 +305,7 @@ impl DatabaseExt for CowBackend<'_, FEN } } -impl DatabaseRef for CowBackend<'_, FEN> { +impl DatabaseRef for CowBackend<'_> { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -320,7 +325,7 @@ impl DatabaseRef for CowBackend<'_, FEN> { } } -impl Database for CowBackend<'_, FEN> { +impl Database for CowBackend<'_> { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { @@ -340,8 +345,8 @@ impl Database for CowBackend<'_, FEN> { } } -impl DatabaseCommit for CowBackend<'_, FEN> { - fn commit(&mut self, changes: AddressMap) { +impl DatabaseCommit for CowBackend<'_> { + 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 5c5fe29c3154c..61f03d8608412 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1,35 +1,36 @@ //! Foundry's main executor backend abstraction and implementation. use crate::{ - FoundryBlock, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction, + Env, InspectorExt, constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS}, - evm::{ - BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory, - FoundryEvmNetwork, HaltReasonFor, PrecompilesFor, SpecFor, TxEnvFor, - }, + evm::new_evm_with_inspector, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, utils::get_blob_base_fee_update_fraction, }; use alloy_consensus::{BlockHeader, Typed2718}; -use alloy_evm::{Evm, EvmEnv, EvmFactory}; +use alloy_evm::{Evm, EvmEnv, FromRecoveredTx, rpc::TryIntoTxEnv}; use alloy_genesis::GenesisAccount; use alloy_network::{ - AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse, + AnyNetwork, AnyRpcBlock, AnyRpcTransaction, AnyTxEnvelope, TransactionResponse, }; use alloy_primitives::{Address, B256, TxKind, U256, keccak256, uint}; -use alloy_rpc_types::BlockNumberOrTag; +use alloy_rpc_types::{BlockNumberOrTag, Transaction, TransactionRequest}; use eyre::Context; use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender}; -pub use foundry_fork_db::{BlockchainDb, ForkBlockEnv, SharedBackend, cache::BlockchainDbMeta}; -use itertools::Itertools; +pub use foundry_fork_db::{BlockchainDb, SharedBackend, cache::BlockchainDbMeta}; use revm::{ - Database, DatabaseCommit, JournalEntry, + Database, DatabaseCommit, Journal, JournalEntry, bytecode::Bytecode, - context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction}, - context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState}, - database::{CacheDB, DatabaseRef, EmptyDB}, - primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log}, + context::{JournalInner, TxEnv}, + context_interface::{ + block::BlobExcessGasAndPrice, journaled_state::account::JournaledAccountTr, + result::ResultAndState, + }, + database::{CacheDB, DatabaseRef}, + inspector::{JournalExt, NoOpInspector}, + precompile::{PrecompileSpecId, Precompiles}, + primitives::{HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId}, state::{Account, AccountInfo, EvmState, EvmStorageSlot}, }; use std::{ @@ -54,7 +55,7 @@ mod snapshot; pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot}; // A `revm::Database` that is used in forking mode -type ForkDB = CacheDB>; +type ForkDB = CacheDB; /// Represents a numeric `ForkId` valid only for the existence of the `Backend`. /// @@ -81,19 +82,13 @@ pub type JournaledState = JournalInner; /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut)] -pub trait DatabaseExt: - Database + DatabaseCommit + Debug -{ +pub trait DatabaseExt: Database + DatabaseCommit + Debug { /// Creates a new state snapshot at the current point of execution. /// /// A state snapshot is associated with a new unique id that's created for the snapshot. /// State snapshots can be reverted: [DatabaseExt::revert_state], however, depending on the /// [RevertStateSnapshotAction], it will keep the snapshot alive or delete it. - fn snapshot_state( - &mut self, - journaled_state: &JournaledState, - evm_env: &EvmEnv, - ) -> U256; + fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256; /// Reverts the snapshot if it exists /// @@ -103,16 +98,16 @@ pub trait DatabaseExt: /// **N.B.** While this reverts the state of the evm to the snapshot, it keeps new logs made /// since the snapshots was created. This way we can show logs that were emitted between /// snapshot and its revert. - /// This will also revert any changes in the `EvmEnv` and `TxEnv` and replace them with the - /// captured values from `Self::snapshot_state`. + /// This will also revert any changes in the `Env` and replace it with the captured `Env` of + /// `Self::snapshot_state`. /// /// Depending on [RevertStateSnapshotAction] it will keep the snapshot alive or delete it. fn revert_state( &mut self, id: U256, journaled_state: &JournaledState, - evm_env: &mut EvmEnv, - caller: Address, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, action: RevertStateSnapshotAction, ) -> Option; @@ -131,8 +126,8 @@ pub trait DatabaseExt: fn create_select_fork( &mut self, fork: CreateFork, - evm_env: &mut EvmEnv, - tx_env: &mut F::Tx, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result { let id = self.create_fork(fork)?; @@ -146,8 +141,8 @@ pub trait DatabaseExt: fn create_select_fork_at_transaction( &mut self, fork: CreateFork, - evm_env: &mut EvmEnv, - tx_env: &mut F::Tx, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, transaction: B256, ) -> eyre::Result { @@ -168,7 +163,7 @@ pub trait DatabaseExt: /// Selects the fork's state /// - /// This will also modify the current `EvmEnv` and `TxEnv`. + /// This will also modify the current `Env`. /// /// **Note**: this does not change the local state, but swaps the remote state /// @@ -178,8 +173,8 @@ pub trait DatabaseExt: fn select_fork( &mut self, id: LocalForkId, - evm_env: &mut EvmEnv, - tx_env: &mut F::Tx, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -194,7 +189,8 @@ pub trait DatabaseExt: &mut self, id: Option, block_number: u64, - evm_env: &mut EvmEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -210,7 +206,8 @@ pub trait DatabaseExt: &mut self, id: Option, transaction: B256, - evm_env: &mut EvmEnv, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -219,18 +216,20 @@ pub trait DatabaseExt: &mut self, id: Option, transaction: B256, - evm_env: EvmEnv, + evm_env: EvmEnv, + tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn for<'db> FoundryInspectorExt>, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; /// Executes a given TransactionRequest, commits the new state to the DB fn transact_from_tx( &mut self, - tx_env: F::Tx, - evm_env: EvmEnv, + transaction: &TransactionRequest, + evm_env: EvmEnv, + tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn for<'db> FoundryInspectorExt>, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()>; /// Returns the `ForkId` that's currently used in the database, if fork mode is on @@ -387,6 +386,40 @@ pub trait DatabaseExt: fn set_blockhash(&mut self, block_number: U256, block_hash: B256); } +struct _ObjectSafe(dyn DatabaseExt); + +/// Extension trait for [`Journal`] providing borrow splitting and state replacement. +/// +/// Generic code accesses the journal via `ctx.journal_mut()` which returns `&mut impl JournalTr`. +/// This trait adds the ability to split the journal into its database and inner state components, +/// enabling direct [`DatabaseExt`] method calls with zero-copy borrow splitting. +pub trait FoundryJournalExt: JournalExt { + /// Returns mutable references to the database and journal inner state. + /// + /// Enables calling [`DatabaseExt`] methods directly, e.g.: + /// ```ignore + /// let (journal, env) = ctx.journal_and_env_mut(); // FoundryContextExt + /// let (db, inner) = journal.as_db_and_inner(); // FoundryJournalExt + /// db.select_fork(id, &env, inner)?; // DatabaseExt + /// ``` + fn as_db_and_inner(&mut self) -> (&mut dyn DatabaseExt, &mut JournaledState); + + /// Replaces the journal inner state. + /// + /// Used by sub-EVM execution to write back modified state after running a closure. + fn set_inner(&mut self, inner: JournaledState); +} + +impl FoundryJournalExt for Journal { + fn as_db_and_inner(&mut self) -> (&mut dyn DatabaseExt, &mut JournaledState) { + (&mut self.database, &mut self.inner) + } + + fn set_inner(&mut self, inner: JournaledState) { + self.inner = inner; + } +} + /// Provides the underlying `revm::Database` implementation. /// /// A `Backend` can be initialised in two forms: @@ -419,7 +452,7 @@ pub trait DatabaseExt: /// Multiple "forks" can be created `Backend::create_fork()`, however only 1 can be used by the /// `db`. However, their state can be hot-swapped by swapping the read half of `db` from one fork to /// another. -/// When swapping forks (`Backend::select_fork()`) we also update the current `EvmEnv` of the `EVM` +/// When swapping forks (`Backend::select_fork()`) we also update the current `Env` of the `EVM` /// accordingly, so that all `block.*` config values match /// /// When another for is selected [`DatabaseExt::select_fork()`] the entire storage, including @@ -439,10 +472,11 @@ pub trait DatabaseExt: /// **Note:** State snapshots work across fork-swaps, e.g. if fork `A` is currently active, then a /// snapshot is created before fork `B` is selected, then fork `A` will be the active fork again /// after reverting the snapshot. +#[derive(Clone, Debug)] #[must_use] -pub struct Backend { +pub struct Backend { /// The access point for managing forks - forks: MultiFork, BlockEnvFor>, + forks: MultiFork, // The default in memory db mem_db: FoundryEvmInMemoryDB, /// The journaled_state to use to initialize new forks with @@ -467,40 +501,16 @@ pub struct Backend { /// If this is set, then the Backend is currently in forking mode active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>, /// holds additional Backend data - inner: BackendInner, + inner: BackendInner, } -impl Clone for Backend { - fn clone(&self) -> Self { - Self { - forks: self.forks.clone(), - mem_db: self.mem_db.clone(), - fork_init_journaled_state: self.fork_init_journaled_state.clone(), - active_fork_ids: self.active_fork_ids, - inner: self.inner.clone(), - } - } -} - -impl Debug for Backend { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Backend") - .field("forks", &self.forks) - .field("mem_db", &self.mem_db) - .field("fork_init_journaled_state", &self.fork_init_journaled_state) - .field("active_fork_ids", &self.active_fork_ids) - .field("inner", &self.inner) - .finish() - } -} - -impl Backend { +impl Backend { /// Creates a new Backend with a spawned multi fork thread. /// /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory /// database. pub fn spawn(fork: Option) -> eyre::Result { - Self::new(MultiFork::, BlockEnvFor>::spawn(), fork) + Self::new(MultiFork::spawn(), fork) } /// Creates a new instance of `Backend` @@ -509,10 +519,7 @@ impl Backend { /// database. /// /// Prefer using [`spawn`](Self::spawn) instead. - pub fn new( - forks: MultiFork, BlockEnvFor>, - fork: Option, - ) -> eyre::Result { + pub fn new(forks: MultiFork, fork: Option) -> eyre::Result { trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend"); // Note: this will take of registering the `fork` let inner = BackendInner { @@ -549,7 +556,7 @@ impl Backend { /// as active pub(crate) fn new_with_fork( id: &ForkId, - fork: Fork>, + fork: Fork, journaled_state: JournaledState, ) -> eyre::Result { let mut backend = Self::spawn(None)?; @@ -602,23 +609,16 @@ impl Backend { storage: Map, ) -> Result<(), DatabaseError> { if let Some(db) = self.active_fork_db_mut() { - db.replace_account_storage(address, storage.into_iter().collect()) + db.replace_account_storage(address, storage) } else { - self.mem_db.replace_account_storage(address, storage.into_iter().collect()) + self.mem_db.replace_account_storage(address, storage) } } /// Returns all snapshots created in this backend - #[allow(clippy::type_complexity)] - pub const fn state_snapshots( + pub fn state_snapshots( &self, - ) -> &StateSnapshots< - BackendStateSnapshot< - BackendDatabaseSnapshot>, - SpecFor, - BlockEnvFor, - >, - > { + ) -> &StateSnapshots> { &self.inner.state_snapshots } @@ -630,8 +630,8 @@ 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"); - self.inner.persistent_accounts.insert(acc); - self.inner.cheatcode_access_accounts.insert(acc); + self.add_persistent_account(acc); + self.allow_cheatcode_access(acc); self } @@ -639,18 +639,19 @@ impl Backend { pub fn set_caller(&mut self, acc: Address) -> &mut Self { trace!(?acc, "setting caller account"); self.inner.caller = Some(acc); - self.inner.cheatcode_access_accounts.insert(acc); + self.allow_cheatcode_access(acc); self } /// Sets the current spec id - pub fn set_spec_id(&mut self, spec_id: impl Into>) -> &mut Self { - self.inner.spec_id = spec_id.into(); + pub fn set_spec_id(&mut self, spec_id: SpecId) -> &mut Self { + trace!(?spec_id, "setting spec ID"); + self.inner.spec_id = spec_id; self } /// Returns the set caller address - pub const fn caller_address(&self) -> Option
{ + pub fn caller_address(&self) -> Option
{ self.inner.caller } @@ -659,12 +660,12 @@ impl Backend { /// If an error occurs in a restored state snapshot, the test is considered failed. /// /// This returns whether there was a reverted state snapshot that recorded an error. - pub const fn has_state_snapshot_failure(&self) -> bool { + pub fn has_state_snapshot_failure(&self) -> bool { self.inner.has_state_snapshot_failure } /// Sets the state snapshot failure flag. - pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) { + pub fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) { self.inner.has_state_snapshot_failure = has_state_snapshot_failure } @@ -672,7 +673,7 @@ impl Backend { pub(crate) fn update_fork_db( &self, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork>, + target_fork: &mut Fork, ) { self.update_fork_db_contracts( self.inner.persistent_accounts.iter().copied(), @@ -686,7 +687,7 @@ impl Backend { &self, accounts: impl IntoIterator, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork>, + target_fork: &mut Fork, ) { if let Some(db) = self.active_fork_db() { merge_account_data(accounts, db, active_journaled_state, target_fork) @@ -696,7 +697,7 @@ impl Backend { } /// Returns the memory db used if not in forking mode - pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB { + pub fn mem_db(&self) -> &FoundryEvmInMemoryDB { &self.mem_db } @@ -711,22 +712,22 @@ impl Backend { } /// Returns the currently active `Fork`, if any - pub fn active_fork(&self) -> Option<&Fork>> { + pub fn active_fork(&self) -> Option<&Fork> { self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx)) } /// Returns the currently active `Fork`, if any - pub fn active_fork_mut(&mut self) -> Option<&mut Fork>> { + pub fn active_fork_mut(&mut self) -> Option<&mut Fork> { self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx)) } /// Returns the currently active `ForkDB`, if any - pub fn active_fork_db(&self) -> Option<&ForkDB>> { + pub fn active_fork_db(&self) -> Option<&ForkDB> { self.active_fork().map(|f| &f.db) } /// Returns the currently active `ForkDB`, if any - pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB>> { + pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB> { self.active_fork_mut().map(|f| &mut f.db) } @@ -747,9 +748,7 @@ impl Backend { } /// Creates a snapshot of the currently active database - pub(crate) fn create_db_snapshot( - &self, - ) -> BackendDatabaseSnapshot> { + pub(crate) fn create_db_snapshot(&self) -> BackendDatabaseSnapshot { if let Some((id, idx)) = self.active_fork_ids { let fork = self.inner.get_fork(idx).clone(); let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed"); @@ -785,21 +784,18 @@ 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, - spec_id: impl Into>, - caller: Address, - tx_kind: TxKind, - ) { - self.set_caller(caller); - self.set_spec_id(spec_id); + pub(crate) fn initialize(&mut self, env: &Env) { + self.set_caller(env.tx.caller); + self.set_spec_id(env.evm_env.cfg_env.spec); - let test_contract = match tx_kind { + let test_contract = match env.tx.kind { TxKind::Call(to) => to, TxKind::Create => { - let nonce = - self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default(); - caller.create(nonce) + 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); @@ -810,29 +806,29 @@ impl Backend { /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] - pub fn inspect FoundryInspectorExt>>( + pub fn inspect( &mut self, - evm_env: &mut EvmEnvFor, - tx_env: &mut TxEnvFor, + env: &mut Env, inspector: I, - ) -> eyre::Result>> { - self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind()); - let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector( + ) -> eyre::Result { + self.initialize(env); + let mut evm = crate::evm::new_evm_with_inspector( self, - evm_env.to_owned(), + env.evm_env.to_owned(), + env.tx.to_owned(), inspector, ); - let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?; - *tx_env = evm.tx().clone(); - *evm_env = evm.finish().1; + let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?; + + *env = Env::from(evm.cfg.clone(), evm.block.clone(), evm.tx.clone()); Ok(res) } /// Returns true if the address is a precompile pub fn is_existing_precompile(&self, addr: &Address) -> bool { - self.inner.precompiles().addresses().contains(addr) + self.inner.precompiles().contains(addr) } /// Sets the initial journaled state to use when initializing forks @@ -856,10 +852,7 @@ impl Backend { .fork_init_journaled_state .state .iter() - .filter(|(addr, _)| { - !self.is_existing_precompile(addr) - && !self.inner.persistent_accounts.contains(*addr) - }) + .filter(|(addr, _)| !self.is_existing_precompile(addr) && !self.is_persistent(addr)) .map(|(addr, _)| addr) .copied() .collect::>(); @@ -897,20 +890,20 @@ impl Backend { transaction: B256, ) -> eyre::Result<(u64, AnyRpcBlock)> { let fork = self.inner.get_fork_by_id(id)?; - let tx = fork.backend().get_transaction(transaction)?; + 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.backend().get_full_block(tx_block)?; + if let Some(tx_block) = tx.block_number { + 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 - 1; Ok((fork_block, block)) } else { - let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?; + let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; - let number = block.header().number(); + let number = block.header.number(); Ok((number, block)) } @@ -922,69 +915,52 @@ impl Backend { pub fn replay_until( &mut self, id: LocalForkId, - evm_env: EvmEnvFor, + mut env: Env, tx_hash: B256, journaled_state: &mut JournaledState, - ) -> eyre::Result> { + ) -> eyre::Result>> { trace!(?id, ?tx_hash, "replay until transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); + let fork_id = self.ensure_fork_id(id)?.clone(); let fork = self.inner.get_fork_by_id_mut(id)?; let full_block = - fork.backend().get_full_block(evm_env.block_env.number().saturating_to::())?; + fork.db.db.get_full_block(env.evm_env.block_env.number.saturating_to::())?; - // Collect non-system transactions up to and including the target. - let txs = full_block - .transactions() - .txns() - .filter(|tx| !is_known_system_sender(tx.from()) && tx.ty() != SYSTEM_TRANSACTION_TYPE); - - let mut txs_to_replay = Vec::new(); - let mut target_tx = None; - for tx in txs { - if tx.tx_hash() == tx_hash { - target_tx = Some(tx.clone()); - break; + for tx in full_block.inner.transactions.txns() { + // 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.inner().inner.signer()) + || tx.ty() == SYSTEM_TRANSACTION_TYPE + { + trace!(tx=?tx.tx_hash(), "skipping system transaction"); + continue; } - txs_to_replay.push(tx.clone()); - } - - // Replay all preceding transactions using a single EVM + cloned ForkDB. - if !txs_to_replay.is_empty() { - let now = Instant::now(); - // Clone the fork's CacheDB once. The underlying SharedBackend is Arc-backed, - // so only the local cache layer is actually duplicated. - let replay_db = fork.db.clone(); - let mut evm = FEN::EvmFactory::default().create_evm(replay_db, evm_env); - - for tx in &txs_to_replay { - let tx_env = TxEnvFor::::from_any_rpc_transaction(tx)?; - trace!(tx=?tx.tx_hash(), "committing transaction"); - evm.transact_commit(tx_env).wrap_err("backend: failed committing transaction")?; + if tx.tx_hash() == tx_hash { + // found the target transaction + return Ok(Some(tx.inner.clone())); } - - // Extract the DB back and replace the fork's database with the replayed state. - fork.db = evm.into_db(); - - // Refresh journaled states from the updated database, preserving persistent - // accounts (cheatcode address, CREATE2 deployer, test contract, etc.). - fork.refresh_journaled_states(journaled_state, &persistent_accounts)?; - - trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions"); + trace!(tx=?tx.tx_hash(), "committing transaction"); + + commit_transaction( + tx, + &mut env, + journaled_state, + fork, + &fork_id, + &persistent_accounts, + &mut NoOpInspector, + )?; } - Ok(target_tx) + Ok(None) } } -impl DatabaseExt for Backend { - fn snapshot_state( - &mut self, - journaled_state: &JournaledState, - evm_env: &EvmEnvFor, - ) -> U256 { +impl DatabaseExt for Backend { + fn snapshot_state(&mut self, journaled_state: &JournaledState, evm_env: &EvmEnv) -> U256 { trace!("create snapshot"); let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new( self.create_db_snapshot(), @@ -999,8 +975,8 @@ impl DatabaseExt for Backend { &mut self, id: U256, current_state: &JournaledState, - evm_env: &mut EvmEnvFor, - caller: Address, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, action: RevertStateSnapshotAction, ) -> Option { trace!(?id, "revert snapshot"); @@ -1032,6 +1008,7 @@ impl DatabaseExt for Backend { // there might be the case where the snapshot was created during `setUp` with // another caller, so we need to ensure the caller account is present in the // journaled state and database + let caller = tx_env.caller; journaled_state.state.entry(caller).or_insert_with(|| { let caller_account = current_state .state @@ -1050,7 +1027,7 @@ impl DatabaseExt for Backend { } } - *evm_env = snap_evm_env; + update_current_env_with_fork_env(evm_env, tx_env, snap_evm_env); trace!(target: "backend", "Reverted snapshot {}", id); Some(journaled_state) @@ -1097,6 +1074,7 @@ impl DatabaseExt for Backend { Some(id), transaction, &mut evm_env, + &mut TxEnv::default(), &mut self.inner.new_journaled_state(), )?; @@ -1108,8 +1086,8 @@ impl DatabaseExt for Backend { fn select_fork( &mut self, id: LocalForkId, - evm_env: &mut EvmEnvFor, - tx_env: &mut TxEnvFor, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, active_journaled_state: &mut JournaledState, ) -> eyre::Result<()> { trace!(?id, "select fork"); @@ -1123,8 +1101,8 @@ impl DatabaseExt for Backend { if let Some(active_fork_id) = self.active_fork_id() { self.forks.update_block( self.ensure_fork_id(active_fork_id).cloned()?, - evm_env.block_env.number(), - evm_env.block_env.timestamp(), + evm_env.block_env.number, + evm_env.block_env.timestamp, )?; } @@ -1140,8 +1118,8 @@ impl DatabaseExt for Backend { if let Some(active) = self.active_fork_mut() { active.journaled_state = active_journaled_state.clone(); - let caller = tx_env.caller(); - let caller_account = active.journaled_state.state.get(&caller).cloned(); + let caller = tx_env.caller; + let caller_account = active.journaled_state.state.get(&tx_env.caller).cloned(); let target_fork = self.inner.get_fork_mut(idx); // depth 0 will be the default value when the fork was created @@ -1206,11 +1184,11 @@ impl DatabaseExt for Backend { // another edge case where a fork is created and selected during setup with not // necessarily the same caller as for the test, however we must always // ensure that fork's state contains the current sender - let caller = tx_env.caller(); + let caller = tx_env.caller; fork.journaled_state.state.entry(caller).or_insert_with(|| { let caller_account = active_journaled_state .state - .get(&caller) + .get(&tx_env.caller) .map(|acc| acc.info.clone()) .unwrap_or_default(); @@ -1229,8 +1207,7 @@ impl DatabaseExt for Backend { self.active_fork_ids = Some((id, idx)); // Update current environment with environment of newly selected fork. - tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id)); - *evm_env = fork_evm_env; + update_current_env_with_fork_env(evm_env, tx_env, fork_evm_env); Ok(()) } @@ -1241,7 +1218,8 @@ impl DatabaseExt for Backend { &mut self, id: Option, block_number: u64, - evm_env: &mut EvmEnvFor, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { trace!(?id, ?block_number, "roll fork"); @@ -1256,7 +1234,7 @@ impl DatabaseExt for Backend { if active_id == id { // need to update the block's env settings right away, which is otherwise set when // forks are selected `select_fork` - *evm_env = fork_env; + update_current_env_with_fork_env(evm_env, tx_env, fork_env); // we also need to update the journaled_state right away, this has essentially the // same effect as selecting (`select_fork`) by discarding @@ -1305,7 +1283,8 @@ impl DatabaseExt for Backend { &mut self, id: Option, transaction: B256, - evm_env: &mut EvmEnvFor, + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { trace!(?id, ?transaction, "roll fork to transaction"); @@ -1317,10 +1296,10 @@ impl DatabaseExt for Backend { // roll the fork to the transaction's parent block or latest if it's pending, because we // need to fork off the parent block's state for tx level forking and then replay the txs // before the tx in that block to get the state at the tx - self.roll_fork(Some(id), fork_block, evm_env, journaled_state)?; + self.roll_fork(Some(id), fork_block, evm_env, tx_env, journaled_state)?; // we need to update the env to the block - update_env_block(evm_env, block.header()); + update_env_block(evm_env, &block); // after we forked at the fork block we need to properly update the block env to the block // env of the tx's block @@ -1328,8 +1307,10 @@ impl DatabaseExt for Backend { .forks .update_block_env(self.inner.ensure_fork_id(id).cloned()?, evm_env.block_env.clone()); + let env = Env { evm_env: evm_env.clone(), tx: tx_env.clone() }; + // replay all transactions that came before - self.replay_until(id, evm_env.clone(), transaction, journaled_state)?; + self.replay_until(id, env, transaction, journaled_state)?; Ok(()) } @@ -1338,11 +1319,10 @@ impl DatabaseExt for Backend { &mut self, maybe_id: Option, transaction: B256, - mut evm_env: EvmEnvFor, + mut evm_env: EvmEnv, + tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn for<'db> FoundryInspectorExt< - ::FoundryContext<'db>, - >, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { trace!(?maybe_id, ?transaction, "execute transaction"); let persistent_accounts = self.inner.persistent_accounts.clone(); @@ -1351,9 +1331,8 @@ impl DatabaseExt for Backend { let tx = { let fork = self.inner.get_fork_by_id_mut(id)?; - fork.backend().get_transaction(transaction)? + fork.db.db.get_transaction(transaction)? }; - let tx_env = TxEnvFor::::from_any_rpc_transaction(&tx)?; // This is a bit ambiguous because the user wants to transact an arbitrary transaction in // the current context, but we're assuming the user wants to transact the transaction as it @@ -1363,12 +1342,13 @@ impl DatabaseExt for Backend { // So we modify the env to match the transaction's block. let (_fork_block, block) = self.get_block_number_and_block_for_transaction(id, transaction)?; - update_env_block(&mut evm_env, block.header()); + update_env_block(&mut evm_env, &block); + let mut env = Env { evm_env, tx: tx_env }; let fork = self.inner.get_fork_by_id_mut(id)?; - commit_transaction::( - evm_env, - tx_env, + commit_transaction( + &tx, + &mut env, journaled_state, fork, &fork_id, @@ -1379,24 +1359,29 @@ impl DatabaseExt for Backend { fn transact_from_tx( &mut self, - tx_env: TxEnvFor, - evm_env: EvmEnvFor, + tx: &TransactionRequest, + evm_env: EvmEnv, + tx_env: TxEnv, journaled_state: &mut JournaledState, - inspector: &mut dyn for<'db> FoundryInspectorExt< - ::FoundryContext<'db>, - >, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { - trace!("execute signed transaction"); + trace!(?tx, "execute signed transaction"); self.commit(journaled_state.state.clone()); + let mut env = Env { evm_env, tx: tx_env }; let res = { + env.tx = tx.clone().try_into_tx_env(&env.evm_env)?; + let mut db = self.clone(); - let depth = journaled_state.depth + 1; - let mut evm = - FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector); - evm.journal_inner_mut().depth = depth; - evm.transact_raw(tx_env)? + let mut evm = new_evm_with_inspector( + &mut db, + env.evm_env.to_owned(), + env.tx.to_owned(), + inspector, + ); + evm.journaled_state.depth = journaled_state.depth + 1; + evm.transact(env.tx)? }; self.commit(res.state); @@ -1570,7 +1555,7 @@ impl DatabaseExt for Backend { } } -impl DatabaseRef for Backend { +impl DatabaseRef for Backend { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -1606,8 +1591,8 @@ impl DatabaseRef for Backend { } } -impl DatabaseCommit for Backend { - fn commit(&mut self, changes: AddressMap) { +impl DatabaseCommit for Backend { + fn commit(&mut self, changes: Map) { if let Some(db) = self.active_fork_db_mut() { db.commit(changes) } else { @@ -1616,7 +1601,7 @@ impl DatabaseCommit for Backend { } } -impl Database for Backend { +impl Database for Backend { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { if let Some(db) = self.active_fork_db_mut() { @@ -1653,26 +1638,21 @@ impl Database for Backend { /// Variants of a [revm::Database] #[derive(Clone, Debug)] -pub enum BackendDatabaseSnapshot { +pub enum BackendDatabaseSnapshot { /// Simple in-memory [revm::Database] InMemory(FoundryEvmInMemoryDB), /// Contains the entire forking mode database - Forked(LocalForkId, ForkId, ForkLookupIndex, Box>), + Forked(LocalForkId, ForkId, ForkLookupIndex, Box), } /// Represents a fork #[derive(Clone, Debug)] -pub struct Fork { - db: ForkDB, +pub struct Fork { + db: ForkDB, journaled_state: JournaledState, } -impl Fork { - /// Returns a reference to the underlying [`SharedBackend`]. - pub const fn backend(&self) -> &SharedBackend { - &self.db.db - } - +impl Fork { /// Returns true if the account is a contract pub fn is_contract(&self, acc: Address) -> bool { if let Ok(Some(acc)) = self.db.basic_ref(acc) @@ -1682,22 +1662,11 @@ impl Fork { } is_contract_in_state(&self.journaled_state.state, acc) } - - /// Refreshes the given journaled state and the fork's own journaled state from the - /// database, preserving persistent accounts. - fn refresh_journaled_states( - &mut self, - journaled_state: &mut JournaledState, - persistent_accounts: &HashSet
, - ) -> Result<(), BackendError> { - update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?; - update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?; - Ok(()) - } } /// Container type for various Backend related data -pub struct BackendInner { +#[derive(Clone, Debug)] +pub struct BackendInner { /// Stores the `ForkId` of the fork the `Backend` launched with from the start. /// /// In other words if [`Backend::spawn()`] was called with a `CreateFork` command, to launch @@ -1720,16 +1689,9 @@ pub struct BackendInner { pub created_forks: HashMap, /// Holds all created fork databases // Note: data is stored in an `Option` so we can remove it without reshuffling - pub forks: Vec>>>, + pub forks: Vec>, /// Contains state snapshots made at a certain point - #[allow(clippy::type_complexity)] - pub state_snapshots: StateSnapshots< - BackendStateSnapshot< - BackendDatabaseSnapshot>, - SpecFor, - BlockEnvFor, - >, - >, + pub state_snapshots: StateSnapshots>, /// Tracks whether there was a failure in a snapshot that was reverted /// /// The Test contract contains a bool variable that is set to true when an `assert` function @@ -1749,48 +1711,12 @@ pub struct BackendInner { /// instead the use only one that's persistent across fork swaps. pub persistent_accounts: HashSet
, /// The configured spec id - pub spec_id: SpecFor, + pub spec_id: SpecId, /// All accounts that are allowed to execute cheatcodes pub cheatcode_access_accounts: HashSet
, } -impl Clone for BackendInner { - fn clone(&self) -> Self { - Self { - launched_with_fork: self.launched_with_fork.clone(), - issued_local_fork_ids: self.issued_local_fork_ids.clone(), - created_forks: self.created_forks.clone(), - forks: self.forks.clone(), - state_snapshots: self.state_snapshots.clone(), - has_state_snapshot_failure: self.has_state_snapshot_failure, - caller: self.caller, - next_fork_id: self.next_fork_id, - persistent_accounts: self.persistent_accounts.clone(), - spec_id: self.spec_id, - cheatcode_access_accounts: self.cheatcode_access_accounts.clone(), - } - } -} - -impl Debug for BackendInner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BackendInner") - .field("launched_with_fork", &self.launched_with_fork) - .field("issued_local_fork_ids", &self.issued_local_fork_ids) - .field("created_forks", &self.created_forks) - .field("forks", &self.forks) - .field("state_snapshots", &self.state_snapshots) - .field("has_state_snapshot_failure", &self.has_state_snapshot_failure) - .field("caller", &self.caller) - .field("next_fork_id", &self.next_fork_id) - .field("persistent_accounts", &self.persistent_accounts) - .field("spec_id", &self.spec_id) - .field("cheatcode_access_accounts", &self.cheatcode_access_accounts) - .finish() - } -} - -impl BackendInner { +impl BackendInner { pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> { self.issued_local_fork_ids .get(&id) @@ -1810,58 +1736,51 @@ impl BackendInner { /// Returns the underlying fork mapped to the index #[track_caller] - fn get_fork(&self, idx: ForkLookupIndex) -> &Fork> { + fn get_fork(&self, idx: ForkLookupIndex) -> &Fork { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].as_ref().unwrap() } /// Returns the underlying fork mapped to the index #[track_caller] - fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork> { + fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].as_mut().unwrap() } /// Returns the underlying fork corresponding to the id #[track_caller] - fn get_fork_by_id_mut( - &mut self, - id: LocalForkId, - ) -> eyre::Result<&mut Fork>> { + fn get_fork_by_id_mut(&mut self, id: LocalForkId) -> eyre::Result<&mut Fork> { let idx = self.ensure_fork_index_by_local_id(id)?; Ok(self.get_fork_mut(idx)) } /// Returns the underlying fork corresponding to the id #[track_caller] - fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork>> { + fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork> { let idx = self.ensure_fork_index_by_local_id(id)?; Ok(self.get_fork(idx)) } /// Removes the fork - fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork> { + fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork { debug_assert!(idx < self.forks.len(), "fork lookup index must exist"); self.forks[idx].take().unwrap() } - fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork>) { + fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork) { self.forks[idx] = Some(fork) } /// Returns an iterator over Forks - pub fn forks_iter( - &self, - ) -> impl Iterator>)> + '_ { + pub fn forks_iter(&self) -> impl Iterator + '_ { self.issued_local_fork_ids .iter() .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id]))) } /// Returns a mutable iterator over all Forks - pub fn forks_iter_mut( - &mut self, - ) -> impl Iterator>> + '_ { + pub fn forks_iter_mut(&mut self) -> impl Iterator + '_ { self.forks.iter_mut().filter_map(|f| f.as_mut()) } @@ -1871,7 +1790,7 @@ impl BackendInner { id: LocalForkId, fork_id: ForkId, idx: ForkLookupIndex, - fork: Fork>, + fork: Fork, ) { self.created_forks.insert(fork_id.clone(), idx); self.issued_local_fork_ids.insert(id, fork_id); @@ -1883,7 +1802,7 @@ impl BackendInner { &mut self, id: LocalForkId, fork_id: ForkId, - db: ForkDB>, + db: ForkDB, journaled_state: JournaledState, ) -> ForkLookupIndex { let idx = self.forks.len(); @@ -1899,7 +1818,7 @@ impl BackendInner { &mut self, id: LocalForkId, new_fork_id: ForkId, - backend: SharedBackend>, + backend: SharedBackend, ) -> eyre::Result { let fork_id = self.ensure_fork_id(id)?; let idx = self.ensure_fork_index(fork_id)?; @@ -1924,7 +1843,7 @@ impl BackendInner { pub fn insert_new_fork( &mut self, fork_id: ForkId, - db: ForkDB>, + db: ForkDB, journaled_state: JournaledState, ) -> (LocalForkId, ForkLookupIndex) { let idx = self.forks.len(); @@ -1952,19 +1871,15 @@ impl BackendInner { self.issued_local_fork_ids.is_empty() } - pub fn precompiles(&self) -> PrecompilesFor { - let evm = FEN::EvmFactory::default().create_evm( - EmptyDB::default(), - EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()), - ); - evm.precompiles().clone() + pub fn precompiles(&self) -> &'static Precompiles { + Precompiles::new(PrecompileSpecId::from_spec_id(self.spec_id)) } /// Returns a new, empty, `JournaledState` with set precompiles pub fn new_journaled_state(&self) -> JournaledState { let mut journal = { let mut journal_inner = JournalInner::new(); - journal_inner.set_spec_id(self.spec_id.into()); + journal_inner.set_spec_id(self.spec_id); journal_inner }; journal @@ -1974,7 +1889,7 @@ impl BackendInner { } } -impl Default for BackendInner { +impl Default for BackendInner { fn default() -> Self { Self { launched_with_fork: None, @@ -1986,7 +1901,7 @@ impl Default for BackendInner { caller: None, next_fork_id: Default::default(), persistent_accounts: Default::default(), - spec_id: SpecFor::::default(), + spec_id: SpecId::default(), // grant the cheatcode,default test and caller address access to execute cheatcodes // itself cheatcode_access_accounts: HashSet::from([ @@ -1998,15 +1913,25 @@ impl Default for BackendInner { } } +/// This updates the currently used env with the fork's environment +pub(crate) fn update_current_env_with_fork_env( + evm_env: &mut EvmEnv, + tx_env: &mut TxEnv, + fork_evm_env: EvmEnv, +) { + tx_env.chain_id = Some(fork_evm_env.cfg_env.chain_id); + *evm_env = fork_evm_env; +} + /// Clones the data of the given `accounts` from the `active` database into the `fork_db` /// This includes the data held in storage (`CacheDB`) and kept in the `JournaledState`. -pub(crate) fn merge_account_data( +pub(crate) fn merge_account_data( accounts: impl IntoIterator, active: &CacheDB, active_journaled_state: &mut JournaledState, - target_fork: &mut Fork, + target_fork: &mut Fork, ) { - for addr in accounts { + for addr in accounts.into_iter() { merge_db_account_data(addr, active, &mut target_fork.db); merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state); } @@ -2033,10 +1958,10 @@ fn merge_journaled_state_data( } /// Clones the account data from the `active` db into the `ForkDB` -fn merge_db_account_data( +fn merge_db_account_data( addr: Address, active: &CacheDB, - fork_db: &mut ForkDB, + fork_db: &mut ForkDB, ) { trace!(?addr, "merging database data"); @@ -2073,51 +1998,55 @@ fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool { } /// Updates the evm env's block with the block's data -fn update_env_block( - evm_env: &mut EvmEnv, - header: &impl BlockHeader, -) { +fn update_env_block(evm_env: &mut EvmEnv, block: &AnyRpcBlock) { let block_env = &mut evm_env.block_env; - block_env.set_timestamp(U256::from(header.timestamp())); - block_env.set_beneficiary(header.beneficiary()); - block_env.set_difficulty(header.difficulty()); - block_env.set_prevrandao(header.mix_hash()); - block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default()); - block_env.set_gas_limit(header.gas_limit()); - block_env.set_number(U256::from(header.number())); - - if let Some(excess_blob_gas) = header.excess_blob_gas() { - evm_env.block_env.set_blob_excess_gas_and_price( + block_env.timestamp = U256::from(block.header.timestamp()); + block_env.beneficiary = block.header.beneficiary(); + block_env.difficulty = block.header.difficulty(); + block_env.prevrandao = Some(block.header.mix_hash().unwrap_or_default()); + block_env.basefee = block.header.base_fee_per_gas().unwrap_or_default(); + block_env.gas_limit = block.header.gas_limit(); + block_env.number = U256::from(block.header.number()); + + if let Some(excess_blob_gas) = block.header.excess_blob_gas() { + evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( excess_blob_gas, - get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, header.timestamp()), - ); + get_blob_base_fee_update_fraction(evm_env.cfg_env.chain_id, block.header.timestamp()), + )); } } /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an inspector. -fn commit_transaction( - evm_env: EvmEnvFor, - tx_env: TxEnvFor, +fn commit_transaction( + tx: &AnyRpcTransaction, + env: &mut Env, journaled_state: &mut JournaledState, - fork: &mut Fork>, + fork: &mut Fork, fork_id: &ForkId, persistent_accounts: &HashSet
, - inspector: &mut dyn for<'db> FoundryInspectorExt< - ::FoundryContext<'db>, - >, + inspector: &mut dyn InspectorExt, ) -> eyre::Result<()> { + if let Some(tx_envelope) = tx.as_envelope() { + env.tx = TxEnv::from_recovered_tx(tx_envelope, tx.from()); + } + let now = Instant::now(); let res = { let fork = fork.clone(); let journaled_state = journaled_state.clone(); let depth = journaled_state.depth; - let mut db: Backend = Backend::new_with_fork(fork_id, fork, journaled_state)?; + let mut db = Backend::new_with_fork(fork_id, fork, journaled_state)?; - let mut evm = - FEN::EvmFactory::default().create_foundry_nested_evm(&mut db, evm_env, inspector); - evm.journal_inner_mut().depth = depth + 1; - evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")? + let mut evm = crate::evm::new_evm_with_inspector( + &mut db as _, + env.evm_env.to_owned(), + env.tx.to_owned(), + inspector, + ); + // Adjust inner EVM depth to ensure that inspectors receive accurate data. + evm.journaled_state.depth = depth + 1; + evm.transact(env.tx.clone()).wrap_err("backend: failed committing transaction")? }; trace!(elapsed = ?now.elapsed(), "transacted transaction"); @@ -2146,30 +2075,30 @@ pub fn update_state( /// Applies the changeset of a transaction to the active journaled state and also commits it in the /// forked db -fn apply_state_changeset( - state: EvmState, +fn apply_state_changeset( + state: Map, journaled_state: &mut JournaledState, - fork: &mut Fork, + fork: &mut Fork, persistent_accounts: &HashSet
, ) -> Result<(), BackendError> { // commit the state and update the loaded accounts fork.db.commit(state); - fork.refresh_journaled_states(journaled_state, persistent_accounts) + + 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(()) } #[cfg(test)] mod tests { - use crate::{backend::Backend, evm::EthEvmNetwork, opts::EvmOpts}; + use crate::{backend::Backend, opts::EvmOpts}; use alloy_primitives::{U256, address}; use alloy_provider::Provider; use foundry_common::provider::get_http_provider; use foundry_config::{Config, NamedChain}; use foundry_fork_db::cache::{BlockchainDb, BlockchainDbMeta}; - use revm::{ - context::{BlockEnv, TxEnv}, - database::DatabaseRef, - primitives::hardfork::SpecId, - }; + use revm::database::DatabaseRef; #[tokio::test(flavor = "multi_thread")] async fn can_read_write_cache() { @@ -2182,12 +2111,11 @@ mod tests { evm_opts.fork_url = Some(endpoint.to_string()); evm_opts.fork_block_number = Some(block_num); - let (evm_env, _, fork_block) = evm_opts.env::().await.unwrap(); + let env = evm_opts.env().await.unwrap(); - let fork = - evm_opts.get_fork(&Config::default(), evm_env.cfg_env.chain_id, fork_block).unwrap(); + let fork = evm_opts.get_fork(&Config::default(), env.evm_env.clone()).unwrap(); - let backend = Backend::::spawn(Some(fork)).unwrap(); + let backend = Backend::spawn(Some(fork)).unwrap(); // some rng contract from etherscan let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3"); @@ -2201,7 +2129,7 @@ mod tests { let meta = BlockchainDbMeta { chain: None, - block_env: evm_env.block_env, + block_env: env.evm_env.block_env, hosts: Default::default(), }; diff --git a/crates/evm/core/src/evm/eth.rs b/crates/evm/core/src/evm/eth.rs index bef827c6690f2..71313e9e34df8 100644 --- a/crates/evm/core/src/evm/eth.rs +++ b/crates/evm/core/src/evm/eth.rs @@ -1,111 +1,43 @@ -use super::*; - -type EthEvmHandler<'db, I> = - MainnetHandler, EVMError, EthFrame>; +use alloy_evm::{ + EthEvm, EthEvmFactory, Evm, EvmEnv, EvmFactory, eth::EthEvmContext, precompiles::PrecompilesMap, +}; +use foundry_fork_db::DatabaseError; +use revm::{ + context::{ + BlockEnv, ContextTr, Evm as RevmEvm, LocalContextTr, TxEnv, + result::{EVMError, ResultAndState}, + }, + handler::{ + EthFrame, EvmTr, FrameResult, Handler, MainnetHandler, instructions::EthInstructions, + }, + inspector::InspectorHandler, + interpreter::{ + FrameInput, SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit, + }, + primitives::hardfork::SpecId, +}; + +use crate::{ + FoundryContextExt, FoundryInspectorExt, + backend::{DatabaseExt, JournaledState}, + evm::{FoundryEvmFactory, NestedEvm}, +}; + +type EthEvmHandler<'db, I> = MainnetHandler, EVMError, EthFrame>; pub type EthRevmEvm<'db, I> = RevmEvm< EthEvmContext<&'db mut dyn DatabaseExt>, I, EthInstructions>>, PrecompilesMap, - EthFrame, + EthFrame, >; -pub struct EthFoundryEvm< - 'db, - I: FoundryInspectorExt>>, -> { - pub inner: EthRevmEvm<'db, I>, -} - -impl<'db, I: FoundryInspectorExt>>> Evm - for EthFoundryEvm<'db, I> -{ - type Precompiles = PrecompilesMap; - type Inspector = I; - type DB = &'db mut dyn DatabaseExt; - type Error = EVMError; - type HaltReason = HaltReason; - type Spec = SpecId; - type Tx = TxEnv; - type BlockEnv = BlockEnv; - - fn block(&self) -> &BlockEnv { - &self.inner.block - } - - fn chain_id(&self) -> u64 { - self.inner.ctx.cfg.chain_id - } - - fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { - (&self.inner.ctx.journaled_state.database, &self.inner.inspector, &self.inner.precompiles) - } - - fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { - ( - &mut self.inner.ctx.journaled_state.database, - &mut self.inner.inspector, - &mut self.inner.precompiles, - ) - } - - fn set_inspector_enabled(&mut self, _enabled: bool) { - unimplemented!("FoundryEvm is always inspecting") - } - - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, Self::Error> { - self.inner.set_tx(tx); - - let result = EthEvmHandler::::default().inspect_run(&mut self.inner)?; - - Ok(ResultAndState::new(result, self.inner.ctx.journaled_state.inner.state.clone())) - } - - fn transact_system_call( - &mut self, - _caller: Address, - _contract: Address, - _data: Bytes, - ) -> Result, Self::Error> { - unimplemented!() - } - - fn finish(self) -> (Self::DB, EvmEnv) - where - Self: Sized, - { - let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx; - - (journaled_state.database, EvmEnv { block_env, cfg_env }) - } -} - -impl<'db, I: FoundryInspectorExt>>> Deref - for EthFoundryEvm<'db, I> -{ - type Target = EthEvmContext<&'db mut dyn DatabaseExt>; - - fn deref(&self) -> &Self::Target { - &self.inner.ctx - } -} - -impl<'db, I: FoundryInspectorExt>>> DerefMut - for EthFoundryEvm<'db, I> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner.ctx - } -} - impl FoundryEvmFactory for EthEvmFactory { type FoundryContext<'db> = EthEvmContext<&'db mut dyn DatabaseExt>; - type FoundryEvm<'db, I: FoundryInspectorExt>> = EthFoundryEvm<'db, I>; + type FoundryEvm<'db, I: FoundryInspectorExt>> = + EthEvm<&'db mut dyn DatabaseExt, I, Self::Precompiles>; fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( &self, @@ -113,13 +45,10 @@ impl FoundryEvmFactory for EthEvmFactory { evm_env: EvmEnv, inspector: I, ) -> Self::FoundryEvm<'db, I> { - let eth_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); - let mut inner = eth_evm.into_inner(); - inner.ctx.cfg.tx_chain_id_check = true; - - let mut evm = EthFoundryEvm { inner }; - evm.inspector().get_networks().inject_precompiles(evm.precompiles_mut()); - evm + let mut eth_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); + eth_evm.cfg.tx_chain_id_check = true; + eth_evm.inspector().get_networks().inject_precompiles(eth_evm.precompiles_mut()); + eth_evm } fn create_foundry_nested_evm<'db>( @@ -128,7 +57,7 @@ impl FoundryEvmFactory for EthEvmFactory { evm_env: EvmEnv, inspector: &'db mut dyn FoundryInspectorExt>, ) -> Box + 'db> { - Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).inner) + Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_inner()) } } @@ -148,7 +77,7 @@ impl<'db, I: FoundryInspectorExt Result, EVMError> { + fn transact_raw(&mut self, tx: Self::Tx) -> Result> { self.set_tx(tx); let result = EthEvmHandler::::default().inspect_run(self)?; diff --git a/crates/evm/core/src/evm/mod.rs b/crates/evm/core/src/evm/mod.rs index 1dcdeed34c6bf..708226be003a2 100644 --- a/crates/evm/core/src/evm/mod.rs +++ b/crates/evm/core/src/evm/mod.rs @@ -1,64 +1,39 @@ -use std::{ - fmt::Debug, - ops::{Deref, DerefMut}, -}; +use std::{fmt::Debug, ops::Deref}; use crate::{ FoundryBlock, FoundryContextExt, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction, backend::{DatabaseExt, JournaledState}, - constants::{CALLER, TEST_CONTRACT_ADDRESS}, - tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner}, }; use alloy_consensus::{SignableTransaction, Signed, transaction::SignerRecoverable}; use alloy_evm::{ - EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, eth::EthEvmContext, - precompiles::PrecompilesMap, + EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, precompiles::PrecompilesMap, }; use alloy_network::{Ethereum, Network}; -use alloy_op_evm::{OpEvmFactory, OpTx}; -use alloy_primitives::{Address, Bytes, Signature, U256}; +use alloy_op_evm::OpEvmFactory; +use alloy_primitives::{Address, Signature, U256}; use alloy_rlp::Decodable; use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt}; use foundry_config::FromEvmVersion; use foundry_fork_db::{DatabaseError, ForkBlockEnv}; use op_alloy_network::Optimism; -use op_revm::{ - L1BlockInfo, OpEvm, OpHaltReason, OpSpecId, OpTransaction, handler::OpHandler, - precompiles::OpPrecompiles, transaction::error::OpTransactionError, -}; +use op_revm::OpHaltReason; use revm::{ - Context, Database, Journal, MainContext, + Database, context::{ - BlockEnv, CfgEnv, ContextTr, Evm as RevmEvm, JournalTr, LocalContextTr, TxEnv, - result::{ - EVMError, ExecResultAndState, ExecutionResult, HaltReason, InvalidTransaction, - ResultAndState, - }, - }, - handler::{ - EthFrame, EvmTr, FrameResult, Handler, MainnetHandler, instructions::EthInstructions, + JournalTr, + result::{EVMError, HaltReason, ResultAndState}, }, - inspector::{InspectorEvmTr, InspectorHandler}, + handler::FrameResult, interpreter::{ CallInput, CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, InstructionResult, - SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit, }, primitives::hardfork::SpecId, - state::Bytecode, }; use serde::{Deserialize, Serialize}; use tempo_alloy::TempoNetwork; -use tempo_chainspec::hardfork::TempoHardfork; use tempo_evm::evm::TempoEvmFactory; -use tempo_precompiles::storage::StorageCtx; -use tempo_revm::{ - TempoBlockEnv, TempoHaltReason, TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, - gas_params::tempo_gas_params, handler::TempoEvmHandler, -}; - -// Modified revm's OpContext with `OpTx` -pub type OpContext = Context, DB, Journal, L1BlockInfo>; +use tempo_revm::TempoHaltReason; pub mod eth; pub mod op; diff --git a/crates/evm/core/src/evm/op.rs b/crates/evm/core/src/evm/op.rs index e98ee785ab705..1b9db85cf2781 100644 --- a/crates/evm/core/src/evm/op.rs +++ b/crates/evm/core/src/evm/op.rs @@ -1,10 +1,40 @@ -use super::*; - -type OpEvmHandler<'db, I> = OpHandler< - OpRevmEvm<'db, I>, - EVMError, - EthFrame, ->; +use std::ops::{Deref, DerefMut}; + +use alloy_evm::{Evm, EvmEnv, precompiles::PrecompilesMap}; +use alloy_op_evm::{OpEvmFactory, OpTx}; +use alloy_primitives::{Address, Bytes}; +use foundry_fork_db::DatabaseError; +use op_revm::{ + L1BlockInfo, OpEvm, OpHaltReason, OpSpecId, OpTransaction, OpTransactionError, + handler::OpHandler, precompiles::OpPrecompiles, +}; +use revm::{ + Context, Journal, MainContext, + context::{ + BlockEnv, CfgEnv, ContextTr, LocalContextTr, + result::{ + EVMError, ExecResultAndState, ExecutionResult, HaltReason, InvalidTransaction, + ResultAndState, + }, + }, + handler::{EthFrame, EvmTr, FrameResult, Handler, instructions::EthInstructions}, + inspector::{InspectorEvmTr, InspectorHandler}, + interpreter::{ + FrameInput, SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit, + }, +}; + +use crate::{ + FoundryContextExt, FoundryInspectorExt, + backend::{DatabaseExt, JournaledState}, + evm::{FoundryEvmFactory, NestedEvm}, +}; + +// Modified revm's OpContext with `OpTx` +pub type OpContext = Context, DB, Journal, L1BlockInfo>; + +type OpEvmHandler<'db, I> = + OpHandler, EVMError, EthFrame>; pub type OpRevmEvm<'db, I> = op_revm::OpEvm< OpContext<&'db mut dyn DatabaseExt>, @@ -13,8 +43,8 @@ pub type OpRevmEvm<'db, I> = op_revm::OpEvm< PrecompilesMap, >; -/// Optimism counterpart of [`EthFoundryEvm`]. Wraps `op_revm::OpEvm` and routes execution -/// through [`OpHandler`]. +/// Wraps [`op_revm::OpEvm`] and routes execution through [`OpHandler`]. +/// It uses foundry's custom [`OpContext`] as op-revm's one is not compatible with [`OpTx`]. pub struct OpFoundryEvm< 'db, I: FoundryInspectorExt>>, @@ -188,7 +218,7 @@ impl<'db, I: FoundryInspectorExt::new(); let memory = - SharedMemory::new_with_buffer(self.ctx_ref().local.shared_memory_buffer().clone()); + SharedMemory::new_with_buffer(self.ctx_ref().local().shared_memory_buffer().clone()); let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; let mut frame_result = @@ -217,6 +247,6 @@ impl<'db, I: FoundryInspectorExt EvmEnv { - EvmEnv::new(self.ctx_ref().cfg.clone(), self.ctx_ref().block.clone()) + self.ctx_ref().evm_clone() } } diff --git a/crates/evm/core/src/evm/tempo.rs b/crates/evm/core/src/evm/tempo.rs index 65194b50ddfe7..98c8cef9fac07 100644 --- a/crates/evm/core/src/evm/tempo.rs +++ b/crates/evm/core/src/evm/tempo.rs @@ -1,21 +1,36 @@ -use super::*; +use alloy_evm::{Evm, EvmEnv, EvmFactory}; +use alloy_primitives::Bytes; +use foundry_evm_hardforks::TempoHardfork; +use foundry_fork_db::DatabaseError; +use revm::{ + context::{ + ContextTr, LocalContextTr, + result::{EVMError, HaltReason, ResultAndState}, + }, + handler::{EvmTr, FrameResult, Handler}, + inspector::InspectorHandler, + interpreter::{FrameInput, SharedMemory, interpreter_action::FrameInit}, + state::Bytecode, +}; +use tempo_evm::{TempoBlockEnv, TempoEvmFactory, TempoHaltReason, evm::TempoEvm}; +use tempo_precompiles::storage::StorageCtx; +use tempo_revm::{ + TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, gas_params::tempo_gas_params, + handler::TempoEvmHandler, +}; + +use crate::{ + FoundryContextExt, FoundryInspectorExt, + backend::{DatabaseExt, JournaledState}, + constants::{CALLER, TEST_CONTRACT_ADDRESS}, + evm::{FoundryEvmFactory, NestedEvm}, + tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner}, +}; // Will be removed when the next revm release includes bluealloy/revm#3518. pub type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt, I>; -/// Tempo counterpart of [`EthFoundryEvm`]. Wraps `tempo_revm::TempoEvm` and routes execution -/// through [`TempoEvmHandler`]. -/// -/// Uses [`TempoEvmFactory`] for construction to reuse factory setup logic, then unwraps to the -/// raw revm EVM via `into_inner()` since the handler operates at the revm level. -pub struct TempoFoundryEvm< - 'db, - I: FoundryInspectorExt>>, -> { - pub inner: TempoRevmEvm<'db, I>, -} - -/// Initialize Tempo precompiles and contracts for a newly created [`TempoFoundryEvm`]. +/// Initialize Tempo precompiles and contracts for a newly created EVM. /// /// In non-fork mode, runs full genesis initialization (precompile sentinel bytecode, /// TIP20 fee tokens, standard contracts) via [`StorageCtx::enter_evm`]. @@ -27,10 +42,10 @@ pub(crate) fn initialize_tempo_evm< 'db, I: FoundryInspectorExt>>, >( - evm: &mut TempoFoundryEvm<'db, I>, + evm: &mut TempoEvm<&'db mut dyn DatabaseExt, I>, is_forked: bool, ) { - let ctx = &mut evm.inner.inner.ctx; + let ctx = evm.ctx_mut(); StorageCtx::enter_evm(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx, || { if is_forked { // In fork mode, warm up precompile accounts to avoid repeated RPC fetches. @@ -52,7 +67,7 @@ impl FoundryEvmFactory for TempoEvmFactory { type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt>; type FoundryEvm<'db, I: FoundryInspectorExt>> = - TempoFoundryEvm<'db, I>; + TempoEvm<&'db mut dyn DatabaseExt, I>; fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt>>( &self, @@ -62,20 +77,18 @@ impl FoundryEvmFactory for TempoEvmFactory { ) -> Self::FoundryEvm<'db, I> { let is_forked = db.is_forked_mode(); let spec = *evm_env.spec_id(); - let tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); - let mut inner = tempo_evm.into_inner(); - inner.ctx.cfg.gas_params = tempo_gas_params(spec); - inner.ctx.cfg.tx_chain_id_check = true; - if inner.ctx.cfg.tx_gas_limit_cap.is_none() { - inner.ctx.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap(); + let mut tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector); + tempo_evm.cfg.gas_params = tempo_gas_params(spec); + tempo_evm.cfg.tx_chain_id_check = true; + if tempo_evm.cfg.tx_gas_limit_cap.is_none() { + tempo_evm.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap(); } - let mut evm = TempoFoundryEvm { inner }; - let networks = Evm::inspector(&evm).get_networks(); - networks.inject_precompiles(evm.precompiles_mut()); + let networks = tempo_evm.inspector().get_networks(); + networks.inject_precompiles(tempo_evm.precompiles_mut()); - initialize_tempo_evm(&mut evm, is_forked); - evm + initialize_tempo_evm(&mut tempo_evm, is_forked); + tempo_evm } fn create_foundry_nested_evm<'db>( @@ -85,90 +98,7 @@ impl FoundryEvmFactory for TempoEvmFactory { inspector: &'db mut dyn FoundryInspectorExt>, ) -> Box + 'db> { - Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).inner) - } -} - -impl<'db, I: FoundryInspectorExt>>> Evm - for TempoFoundryEvm<'db, I> -{ - type Precompiles = PrecompilesMap; - type Inspector = I; - type DB = &'db mut dyn DatabaseExt; - type Error = EVMError; - type HaltReason = TempoHaltReason; - type Spec = TempoHardfork; - type Tx = TempoTxEnv; - type BlockEnv = TempoBlockEnv; - - fn block(&self) -> &TempoBlockEnv { - &self.inner.block - } - - fn chain_id(&self) -> u64 { - self.inner.ctx.cfg.chain_id - } - - fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) { - let evm = &self.inner.inner; - (&evm.ctx.journaled_state.database, &evm.inspector, &evm.precompiles) - } - - fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) { - let evm = &mut self.inner.inner; - (&mut evm.ctx.journaled_state.database, &mut evm.inspector, &mut evm.precompiles) - } - - fn set_inspector_enabled(&mut self, _enabled: bool) { - unimplemented!("TempoFoundryEvm is always inspecting") - } - - fn transact_raw( - &mut self, - tx: Self::Tx, - ) -> Result, Self::Error> { - self.inner.set_tx(tx); - - let mut handler = TempoEvmHandler::new(); - let result = handler.inspect_run(&mut self.inner)?; - - Ok(ResultAndState::new(result, self.inner.inner.ctx.journaled_state.inner.state.clone())) - } - - fn transact_system_call( - &mut self, - _caller: Address, - _contract: Address, - _data: Bytes, - ) -> Result>, Self::Error> { - unimplemented!() - } - - fn finish(self) -> (Self::DB, EvmEnv) - where - Self: Sized, - { - let revm_evm = self.inner.inner; - let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = revm_evm.ctx; - (journaled_state.database, EvmEnv { block_env, cfg_env }) - } -} - -impl<'db, I: FoundryInspectorExt>>> Deref - for TempoFoundryEvm<'db, I> -{ - type Target = TempoContext<&'db mut dyn DatabaseExt>; - - fn deref(&self) -> &Self::Target { - &self.inner.ctx - } -} - -impl<'db, I: FoundryInspectorExt>>> DerefMut - for TempoFoundryEvm<'db, I> -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner.ctx + Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_inner()) } } @@ -206,7 +136,7 @@ impl<'db, I: FoundryInspectorExt Result, EVMError> { + fn transact_raw(&mut self, tx: Self::Tx) -> Result> { self.set_tx(tx); let mut handler = TempoEvmHandler::new(); diff --git a/crates/evm/core/src/tempo.rs b/crates/evm/core/src/tempo.rs index 5e0cbefec56d3..eb29696858ff2 100644 --- a/crates/evm/core/src/tempo.rs +++ b/crates/evm/core/src/tempo.rs @@ -11,11 +11,14 @@ use tempo_contracts::{ ARACHNID_CREATE2_FACTORY_ADDRESS, CREATEX_ADDRESS, CreateX, MULTICALL3_ADDRESS, Multicall3, PERMIT2_ADDRESS, Permit2, SAFE_DEPLOYER_ADDRESS, SafeDeployer, contracts::ARACHNID_CREATE2_FACTORY_BYTECODE, + precompiles::{ + ACCOUNT_KEYCHAIN_ADDRESS, ADDRESS_REGISTRY_ADDRESS, NONCE_PRECOMPILE_ADDRESS, + SIGNATURE_VERIFIER_ADDRESS, STABLECOIN_DEX_ADDRESS, TIP_FEE_MANAGER_ADDRESS, + TIP20_FACTORY_ADDRESS, TIP403_REGISTRY_ADDRESS, VALIDATOR_CONFIG_ADDRESS, + VALIDATOR_CONFIG_V2_ADDRESS, + }, }; use tempo_precompiles::{ - ACCOUNT_KEYCHAIN_ADDRESS, NONCE_PRECOMPILE_ADDRESS, STABLECOIN_DEX_ADDRESS, - TIP_FEE_MANAGER_ADDRESS, TIP20_FACTORY_ADDRESS, TIP403_REGISTRY_ADDRESS, - VALIDATOR_CONFIG_ADDRESS, VALIDATOR_CONFIG_V2_ADDRESS, error::TempoPrecompileError, storage::{PrecompileStorageProvider, StorageCtx}, tip20::{ISSUER_ROLE, ITIP20, TIP20Token}, @@ -25,12 +28,6 @@ use tempo_precompiles::{ pub use tempo_contracts::precompiles::PATH_USD_ADDRESS; -// TODO: remove once we can re-export from tempo_precompiles instead. -pub const SIGNATURE_VERIFIER_ADDRESS: Address = - address!("0x5165300000000000000000000000000000000000"); -pub const ADDRESS_REGISTRY_ADDRESS: Address = - address!("0xFDC0000000000000000000000000000000000000"); - /// All well-known Tempo precompile addresses. pub const TEMPO_PRECOMPILE_ADDRESSES: &[Address] = &[ NONCE_PRECOMPILE_ADDRESS, @@ -92,7 +89,7 @@ pub fn initialize_tempo_genesis_inner( // Create PathUSD token: 0x20C0000000000000000000000000000000000000 let path_usd_token_address = create_and_mint_token( - address!("20C0000000000000000000000000000000000000"), + PATH_USD_ADDRESS, "PathUSD", "PathUSD", "USD", diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 76e1df1bd1778..826384b5ea259 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,4 +1,4 @@ -use crate::{EvmEnv, FoundryBlock}; +use crate::EvmEnv; use alloy_chains::Chain; use alloy_consensus::{BlockHeader, private::alloy_eips::eip7840::BlobParams}; use alloy_hardforks::EthereumHardfork; @@ -16,40 +16,22 @@ pub use revm::state::EvmState as StateChangeset; /// Hints to the compiler that this is a cold path, i.e. unlikely to be taken. #[cold] #[inline(always)] -pub const fn cold_path() { +pub fn cold_path() { // TODO: remove `#[cold]` and call `std::hint::cold_path` once stable. } -/// Constructs a generic [`FoundryBlock`] from a block header. -pub fn block_env_from_header(header: &impl BlockHeader) -> BLOCK { - let mut block = BLOCK::default(); - block.set_number(U256::from(header.number())); - block.set_beneficiary(header.beneficiary()); - block.set_timestamp(U256::from(header.timestamp())); - block.set_difficulty(header.difficulty()); - block.set_prevrandao(header.mix_hash()); - block.set_basefee(header.base_fee_per_gas().unwrap_or_default()); - block.set_gas_limit(header.gas_limit()); - block -} - /// Depending on the configured chain id and block number this should apply any specific changes /// /// - 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), works with any -/// [`FoundryBlock`] type. -pub fn apply_chain_and_block_specific_env_changes< - N: Network, - SPEC: Into + Copy, - BLOCK: FoundryBlock, ->( - evm_env: &mut EvmEnv, +/// Should be called with proper chain id (retrieved from provider if not provided). +pub fn apply_chain_and_block_specific_env_changes( + evm_env: &mut EvmEnv, block: &N::BlockResponse, configs: NetworkConfigs, ) { - use NamedChain::{BinanceSmartChain, BinanceSmartChainTestnet, Mainnet}; + use NamedChain::*; if let Ok(chain) = NamedChain::try_from(evm_env.cfg_env.chain_id) { let block_number = block.header().number(); @@ -58,9 +40,8 @@ pub fn apply_chain_and_block_specific_env_changes< Mainnet => { // after merge difficulty is supplanted with prevrandao EIP-4399 if block_number >= 15_537_351u64 { - evm_env - .block_env - .set_difficulty(evm_env.block_env.prevrandao().unwrap_or_default().into()); + evm_env.block_env.difficulty = + evm_env.block_env.prevrandao.unwrap_or_default().into(); } return; @@ -72,7 +53,7 @@ pub fn apply_chain_and_block_specific_env_changes< // (`mixHash`) is always zero, even though bsc adopts the newer EVM // specification. This will confuse revm and causes emulation // failure. - evm_env.block_env.set_prevrandao(Some(evm_env.block_env.difficulty().into())); + evm_env.block_env.prevrandao = Some(evm_env.block_env.difficulty.into()); return; } c if c.is_arbitrum() => { @@ -85,23 +66,22 @@ pub fn apply_chain_and_block_specific_env_changes< serde_json::from_value::(l1_block_number).ok() }) { - evm_env.block_env.set_number(l1_block_number); + evm_env.block_env.number = l1_block_number.to(); } } _ => {} } } - if configs.bypass_prevrandao(evm_env.cfg_env.chain_id) - && evm_env.block_env.prevrandao().is_none() + if configs.bypass_prevrandao(evm_env.cfg_env.chain_id) && evm_env.block_env.prevrandao.is_none() { // - evm_env.block_env.set_prevrandao(Some(B256::random())); + evm_env.block_env.prevrandao = Some(B256::random()); } // if difficulty is `0` we assume it's past merge if block.header().difficulty().is_zero() { - evm_env.block_env.set_difficulty(evm_env.block_env.prevrandao().unwrap_or_default().into()); + evm_env.block_env.difficulty = evm_env.block_env.prevrandao.unwrap_or_default().into(); } } diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 60a2c1433817a..6bf7163d6e023 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -28,6 +28,7 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true +alloy-network.workspace = true alloy-primitives = { workspace = true, features = [ "serde", "getrandom", @@ -48,6 +49,7 @@ revm = { workspace = true, default-features = false, features = [ ] } revm-inspectors.workspace = true tempo-precompiles.workspace = true +tempo-primitives.workspace = true eyre.workspace = true parking_lot.workspace = true diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index a49716ca4fbc1..d3336fcc9013c 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -1,9 +1,6 @@ use crate::{executors::Executor, inspectors::InspectorStackBuilder}; -use foundry_evm_core::{ - backend::Backend, - evm::{BlockEnvFor, EvmEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor}, -}; -use revm::context::{Block, Transaction}; +use foundry_evm_core::{Env, backend::Backend}; +use revm::primitives::hardfork::SpecId; /// The builder that allows to configure an evm [`Executor`] which a stack of optional /// [`revm::Inspector`]s, such as [`Cheatcodes`]. @@ -14,36 +11,40 @@ use revm::context::{Block, Transaction}; /// [`InspectorStack`]: super::InspectorStack #[derive(Debug, Clone)] #[must_use = "builders do nothing unless you call `build` on them"] -pub struct ExecutorBuilder { +pub struct ExecutorBuilder { /// The configuration used to build an `InspectorStack`. - stack: InspectorStackBuilder>, + stack: InspectorStackBuilder, /// The gas limit. gas_limit: Option, - /// The spec override. When `None`, the spec from `EvmEnv::cfg_env` is preserved. - spec: Option>, + /// The spec ID. + spec_id: SpecId, legacy_assertions: bool, } -impl Default for ExecutorBuilder { +impl Default for ExecutorBuilder { #[inline] fn default() -> Self { Self { stack: InspectorStackBuilder::new(), gas_limit: None, - spec: None, + spec_id: SpecId::default(), legacy_assertions: false, } } } -impl ExecutorBuilder { +impl ExecutorBuilder { + /// Create a new executor builder. + #[inline] + pub fn new() -> Self { + Self::default() + } + /// Modify the inspector stack. #[inline] pub fn inspectors( mut self, - f: impl FnOnce( - InspectorStackBuilder>, - ) -> InspectorStackBuilder>, + f: impl FnOnce(InspectorStackBuilder) -> InspectorStackBuilder, ) -> Self { self.stack = f(self.stack); self @@ -51,50 +52,42 @@ impl ExecutorBuilder { /// Sets the EVM spec to use. #[inline] - pub const fn spec_id(mut self, spec: SpecFor) -> Self { - self.spec = Some(spec); + pub fn spec_id(mut self, spec: SpecId) -> Self { + self.spec_id = spec; self } - /// Optionally sets the EVM spec. When `None`, the spec from `EvmEnv::cfg_env` is preserved. - #[inline] - pub const fn spec_id_opt(self, spec: Option>) -> Self { - if let Some(spec) = spec { self.spec_id(spec) } else { self } - } - /// Sets the executor gas limit. #[inline] - pub const fn gas_limit(mut self, gas_limit: u64) -> Self { + pub fn gas_limit(mut self, gas_limit: u64) -> Self { self.gas_limit = Some(gas_limit); self } /// Sets the `legacy_assertions` flag. #[inline] - pub const fn legacy_assertions(mut self, legacy_assertions: bool) -> Self { + pub fn legacy_assertions(mut self, legacy_assertions: bool) -> Self { self.legacy_assertions = legacy_assertions; self } /// Builds the executor as configured. #[inline] - pub fn build( - self, - mut evm_env: EvmEnvFor, - tx_env: TxEnvFor, - db: Backend, - ) -> Executor { - let Self { mut stack, gas_limit, spec, legacy_assertions, .. } = self; + pub fn build(self, env: Env, db: Backend) -> Executor { + let Self { mut stack, gas_limit, spec_id, legacy_assertions } = self; if stack.block.is_none() { - stack.block = Some(evm_env.block_env.clone()); + stack.block = Some(env.evm_env.block_env.clone()); } if stack.gas_price.is_none() { - stack.gas_price = Some(tx_env.gas_price()); - } - let gas_limit = gas_limit.unwrap_or(evm_env.block_env.gas_limit()); - if let Some(spec) = spec { - evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec); + stack.gas_price = Some(env.tx.gas_price); } - Executor::new(db, evm_env, tx_env, stack.build(), gas_limit, legacy_assertions) + let gas_limit = gas_limit.unwrap_or(env.evm_env.block_env.gas_limit); + let env = Env::new_with_spec_id( + env.evm_env.cfg_env.clone(), + env.evm_env.block_env.clone(), + env.tx, + spec_id, + ); + Executor::new(db, env, stack.build(), gas_limit, legacy_assertions) } } diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 93e02cdba870f..d2f9858981df8 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -1,28 +1,26 @@ -use crate::executors::{Executor, ExecutorBuilder}; -use alloy_primitives::{Address, U256, map::HashMap}; +use crate::{ + Env, + executors::{Executor, ExecutorBuilder}, +}; +use alloy_primitives::{Address, FixedBytes, U256, address, map::HashMap}; use alloy_rpc_types::state::StateOverride; use eyre::Context; use foundry_compilers::artifacts::EvmVersion; -use foundry_config::{Chain, Config, evm_spec_id}; -use foundry_evm_core::{ - backend::Backend, - evm::{BlockEnvFor, EvmEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor}, - fork::CreateFork, - opts::EvmOpts, -}; +use foundry_config::{Chain, Config, utils::evm_spec_id}; +use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use foundry_evm_networks::NetworkConfigs; use foundry_evm_traces::TraceMode; -use revm::{context::Transaction, state::Bytecode}; +use revm::{primitives::hardfork::SpecId, state::Bytecode}; use std::ops::{Deref, DerefMut}; /// A default executor with tracing enabled -pub struct TracingExecutor { - executor: Executor, +pub struct TracingExecutor { + executor: Executor, } -impl TracingExecutor { +impl TracingExecutor { pub fn new( - env: (EvmEnvFor, TxEnvFor), + env: Env, fork: CreateFork, version: Option, trace_mode: TraceMode, @@ -33,12 +31,12 @@ impl TracingExecutor { let db = Backend::spawn(Some(fork))?; // configures a bare version of the evm executor: no cheatcode and log_collector inspector // is enabled, tracing will be enabled only for the targeted transaction - let mut executor = ExecutorBuilder::default() + let mut executor = ExecutorBuilder::new() .inspectors(|stack| { stack.trace_mode(trace_mode).networks(networks).create2_deployer(create2_deployer) }) - .spec_id_opt(version.map(evm_spec_id::>)) - .build(env.0, env.1, db); + .spec_id(evm_spec_id(version.unwrap_or_default())) + .build(env, db); // Apply the state overrides. if let Some(state_overrides) = state_overrides { @@ -73,7 +71,7 @@ impl TracingExecutor { } /// Returns the spec id of the executor - pub const fn spec_id(&self) -> SpecFor { + pub fn spec_id(&self) -> SpecId { self.executor.spec_id() } @@ -81,31 +79,56 @@ impl TracingExecutor { pub async fn get_fork_material( config: &mut Config, mut evm_opts: EvmOpts, - ) -> eyre::Result<(EvmEnvFor, TxEnvFor, CreateFork, Chain, NetworkConfigs)> { + ) -> eyre::Result<(Env, CreateFork, Chain, NetworkConfigs)> { evm_opts.fork_url = Some(config.get_rpc_url_or_localhost_http()?.into_owned()); evm_opts.fork_block_number = config.fork_block_number; - let (evm_env, tx_env, fork_block) = - evm_opts.env::, BlockEnvFor, TxEnvFor>().await?; + let env = evm_opts.env().await?; - let fork = evm_opts.get_fork(config, evm_env.cfg_env.chain_id, fork_block).unwrap(); - let networks = evm_opts.networks.with_chain_id(evm_env.cfg_env.chain_id); + let fork = evm_opts.get_fork(config, env.evm_env.clone()).unwrap(); + let networks = evm_opts.networks.with_chain_id(env.evm_env.cfg_env.chain_id); config.labels.extend(networks.precompiles_label()); - let chain = tx_env.chain_id().unwrap().into(); - Ok((evm_env, tx_env, fork, chain, networks)) + let chain = env.tx.chain_id.unwrap().into(); + Ok((env, fork, chain, networks)) + } + + /// Processes the beacon block root by storing it in the appropriate storage slots. + pub fn process_beacon_block_root( + &mut self, + block_timestamp: u64, + beacon_root: FixedBytes<32>, + ) -> eyre::Result<()> { + const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); + const HISTORY_BUFFER_LENGTH: u64 = 8192; + + let timestamp_index = block_timestamp % HISTORY_BUFFER_LENGTH; + let root_index = timestamp_index + HISTORY_BUFFER_LENGTH; + + let timestamp_slot = U256::from(timestamp_index); + let root_slot = U256::from(root_index); + + self.set_storage_slot(BEACON_ROOTS_ADDRESS, timestamp_slot, U256::from(block_timestamp))?; + + self.set_storage_slot( + BEACON_ROOTS_ADDRESS, + root_slot, + U256::from_be_bytes(beacon_root.into()), + )?; + + Ok(()) } } -impl Deref for TracingExecutor { - type Target = Executor; +impl Deref for TracingExecutor { + type Target = Executor; fn deref(&self) -> &Self::Target { &self.executor } } -impl DerefMut for TracingExecutor { +impl DerefMut for TracingExecutor { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.executor } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 0631905c1b33f..addaddc180efd 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -898,12 +898,17 @@ impl InspectorStackRefMut<'_, FEN> { .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone()))); let mut inner = std::mem::take(self.inner); + // Save pending CREATE2 redirects so frame_end in the nested EVM doesn't consume them. + // These belong to the outer EVM's frame lifecycle and must be restored after. + let saved_create2_redirects = std::mem::take(&mut inner.pending_create2_redirects); + let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner }); if let Some(cheats) = self.cheatcodes.as_deref_mut() { *cheats = cheatcodes.unwrap(); } + inner.pending_create2_redirects = saved_create2_redirects; *self.inner = inner; out diff --git a/crates/evm/evm/src/inspectors/tempo_labels.rs b/crates/evm/evm/src/inspectors/tempo_labels.rs index d2726772b13f7..f0e2e2279fb17 100644 --- a/crates/evm/evm/src/inspectors/tempo_labels.rs +++ b/crates/evm/evm/src/inspectors/tempo_labels.rs @@ -6,7 +6,7 @@ use revm::{ inspector::JournalExt, interpreter::{CallInputs, CallOutcome, interpreter::EthInterpreter}, }; -use tempo_precompiles::tip20::is_tip20_prefix; +use tempo_primitives::TempoAddressExt; /// Inspector that labels TIP20 token precompile addresses with their on-chain names. /// @@ -25,9 +25,7 @@ where CTX::Journal: JournalExt, { fn call(&mut self, ctx: &mut CTX, inputs: &mut CallInputs) -> Option { - if is_tip20_prefix(inputs.target_address) - && !self.labels.contains_key(&inputs.target_address) - { + if inputs.target_address.is_tip20() && !self.labels.contains_key(&inputs.target_address) { let bytes = ctx .db_mut() .storage(inputs.target_address, tempo_precompiles::tip20::slots::NAME) diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index f9bf9c5e8471a..5e8c452c8695a 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -216,7 +216,7 @@ pub fn render_trace_arena_inner( String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8") } -const fn convert_color_choice(choice: shell::ColorChoice) -> revm_inspectors::ColorChoice { +fn convert_color_choice(choice: shell::ColorChoice) -> revm_inspectors::ColorChoice { match choice { shell::ColorChoice::Auto => revm_inspectors::ColorChoice::Auto, shell::ColorChoice::Always => revm_inspectors::ColorChoice::Always, @@ -237,7 +237,7 @@ impl TraceKind { /// /// [`Deployment`]: TraceKind::Deployment #[must_use] - pub const fn is_deployment(self) -> bool { + pub fn is_deployment(self) -> bool { matches!(self, Self::Deployment) } @@ -245,7 +245,7 @@ impl TraceKind { /// /// [`Setup`]: TraceKind::Setup #[must_use] - pub const fn is_setup(self) -> bool { + pub fn is_setup(self) -> bool { matches!(self, Self::Setup) } @@ -253,7 +253,7 @@ impl TraceKind { /// /// [`Execution`]: TraceKind::Execution #[must_use] - pub const fn is_execution(self) -> bool { + pub fn is_execution(self) -> bool { matches!(self, Self::Execution) } } @@ -321,10 +321,7 @@ pub enum TraceMode { /// /// Used by debugger. Debug, - /// Step trace with storage change recording. - /// - /// Records JUMP/JUMPDEST steps (like `Steps`) plus storage diffs on SLOAD/SSTORE. - /// Does not enable memory/stack snapshots or unfiltered opcode recording. + /// Debug trace with storage changes. RecordStateDiff, } @@ -373,9 +370,9 @@ impl TraceMode { match verbosity { 0..3 => self, 3..=4 => std::cmp::max(self, Self::Call), - // Enable step recording and state diff recording when verbosity is 5 or higher. - // This includes backtraces (JUMP/JUMPDEST steps) and storage changes. - _ => std::cmp::max(self, Self::RecordStateDiff), + // Enable step recording for backtraces when verbosity is 5 or higher. + // We need to ensure we're recording JUMP AND JUMPDEST steps. + _ => std::cmp::min(self, Self::Steps), } } @@ -383,128 +380,23 @@ impl TraceMode { if self.is_none() { None } else { - // RecordStateDiff is Steps + state diff recording, not Debug + state diff. - // It should not enable memory/stack snapshots. - // State diff recording requires all opcodes (no filter) since it needs - // SLOAD/SSTORE steps, not just JUMP/JUMPDEST. - let effective = if self.record_state_diff() { Self::Steps } else { self }; TracingInspectorConfig { record_steps: self >= Self::Steps, - record_memory_snapshots: effective >= Self::Jump, - record_stack_snapshots: if effective > Self::Steps { + record_memory_snapshots: self >= Self::Jump, + record_stack_snapshots: if self > Self::Steps { StackSnapshotType::Full } else { StackSnapshotType::None }, record_logs: true, record_state_diff: self.record_state_diff(), - record_returndata_snapshots: effective.is_debug(), - // State diff needs all opcodes recorded to capture SLOAD/SSTORE. - record_opcodes_filter: if self.record_state_diff() { - None - } else { - (effective.is_steps() || effective.is_jump() || effective.is_jump_simple()) - .then(|| { - OpcodeFilter::new().enabled(OpCode::JUMP).enabled(OpCode::JUMPDEST) - }) - }, + record_returndata_snapshots: self.is_debug(), + record_opcodes_filter: (self.is_steps() || self.is_jump() || self.is_jump_simple()) + .then(|| OpcodeFilter::new().enabled(OpCode::JUMP).enabled(OpCode::JUMPDEST)), exclude_precompile_calls: false, - record_immediate_bytes: effective.is_debug(), + record_immediate_bytes: self.is_debug(), } .into() } } } - -#[cfg(test)] -mod tests { - use super::*; - - // -- TraceMode::with_verbosity level tests -- - - #[test] - fn verbosity_0_through_2_is_noop() { - for v in 0..=2 { - assert_eq!(TraceMode::None.with_verbosity(v), TraceMode::None, "v={v}"); - assert_eq!(TraceMode::Call.with_verbosity(v), TraceMode::Call, "v={v}"); - assert_eq!(TraceMode::Debug.with_verbosity(v), TraceMode::Debug, "v={v}"); - } - } - - #[test] - fn verbosity_3_and_4_raises_to_call() { - for v in 3..=4 { - assert_eq!(TraceMode::None.with_verbosity(v), TraceMode::Call, "v={v}"); - // Already above Call — must not downgrade. - assert_eq!(TraceMode::Debug.with_verbosity(v), TraceMode::Debug, "v={v}"); - assert_eq!( - TraceMode::RecordStateDiff.with_verbosity(v), - TraceMode::RecordStateDiff, - "v={v}" - ); - } - } - - #[test] - fn verbosity_5_raises_to_record_state_diff() { - assert_eq!(TraceMode::None.with_verbosity(5), TraceMode::RecordStateDiff); - assert_eq!(TraceMode::Call.with_verbosity(5), TraceMode::RecordStateDiff); - assert_eq!(TraceMode::Steps.with_verbosity(5), TraceMode::RecordStateDiff); - assert_eq!(TraceMode::Debug.with_verbosity(5), TraceMode::RecordStateDiff); - // Already at the top — stays the same. - assert_eq!(TraceMode::RecordStateDiff.with_verbosity(5), TraceMode::RecordStateDiff); - } - - // -- into_config at each verbosity level -- - - #[test] - fn config_at_verbosity_0_is_none() { - let mode = TraceMode::None.with_verbosity(0); - assert!(mode.into_config().is_none()); - } - - #[test] - fn config_at_verbosity_3_records_calls_only() { - let cfg = TraceMode::None.with_verbosity(3).into_config().unwrap(); - assert!(!cfg.record_steps, "verbosity 3 should not record steps"); - assert!(!cfg.record_state_diff, "verbosity 3 should not record state diff"); - assert!(cfg.record_logs, "verbosity 3 should record logs"); - } - - #[test] - fn config_at_verbosity_5_records_steps_and_state_diff() { - let cfg = TraceMode::None.with_verbosity(5).into_config().unwrap(); - assert!(cfg.record_steps, "verbosity 5 must record steps for backtraces"); - assert!(cfg.record_state_diff, "verbosity 5 must record state diff"); - assert!(cfg.record_logs, "verbosity 5 must record logs"); - // RecordStateDiff should NOT enable expensive debug-level features. - assert!(!cfg.record_memory_snapshots, "verbosity 5 should not record memory snapshots"); - assert_eq!( - cfg.record_stack_snapshots, - StackSnapshotType::None, - "verbosity 5 should not record stack snapshots" - ); - // State diff requires all opcodes to capture SLOAD/SSTORE, so no filter. - assert!( - cfg.record_opcodes_filter.is_none(), - "verbosity 5 needs unfiltered opcodes for state diff" - ); - } - - #[test] - fn config_debug_mode_unchanged() { - // Debug mode must still enable full recording for the debugger. - let cfg = TraceMode::Debug.into_config().unwrap(); - assert!(cfg.record_steps); - assert!(cfg.record_memory_snapshots, "Debug must record memory snapshots"); - assert_eq!( - cfg.record_stack_snapshots, - StackSnapshotType::Full, - "Debug must record full stack snapshots" - ); - assert!(cfg.record_returndata_snapshots, "Debug must record returndata"); - assert!(cfg.record_immediate_bytes, "Debug must record immediate bytes"); - assert!(cfg.record_opcodes_filter.is_none(), "Debug must record all opcodes (no filter)"); - assert!(!cfg.record_state_diff, "Debug alone should not record state diff"); - } -} diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 51335b2fe84cd..9a7a237855b97 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,6 +62,7 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true tempo-alloy.workspace = true @@ -117,8 +118,8 @@ tempfile.workspace = true alloy-signer-local.workspace = true [features] -default = ["jemalloc"] -asm-keccak = ["alloy-primitives/asm-keccak"] +default = ["jemalloc", "asm-keccak"] +asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] tracy-allocator = ["foundry-cli/tracy-allocator"] diff --git a/crates/forge/assets/tempo/MailTemplate.sol b/crates/forge/assets/tempo/MailTemplate.sol index 92911c3f562a9..2caed92d47517 100644 --- a/crates/forge/assets/tempo/MailTemplate.sol +++ b/crates/forge/assets/tempo/MailTemplate.sol @@ -2,23 +2,76 @@ pragma solidity ^0.8.13; import {ITIP20} from "tempo-std/interfaces/ITIP20.sol"; +import {StdPrecompiles} from "tempo-std/StdPrecompiles.sol"; +/// @title Mail +/// @notice Send mail with TIP-20 token attachments on Tempo. +/// +/// Supports two modes: +/// 1. Direct — call `sendMail()` yourself (uses `msg.sender`). +/// 2. Relayed — sign a mail off-chain and let anyone deliver it on-chain. +/// +/// Relayed mode uses the [TIP-1020] `SignatureVerifier` precompile to verify the +/// sender's Tempo signature. Unlike Ethereum's `ecrecover`, this precompile: +/// - Supports secp256k1, P256, and WebAuthn signature types +/// - Reverts on invalid signatures instead of returning `address(0)` +/// - Maintains forward compatibility with future Tempo account types +/// +/// [TIP-1020]: contract Mail { + /// @notice Emitted when a mail is sent, either directly or via a relayer. event MailSent(address indexed from, address indexed to, string message, Attachment attachment); + /// @notice A TIP-20 token transfer bundled with a mail. struct Attachment { uint256 amount; bytes32 memo; } + /// @notice The TIP-20 token used for mail attachments. ITIP20 public token; + /// @notice Per-sender nonce to prevent signature replay on relayed mails (requires T3). + mapping(address => uint256) public nonces; + constructor(ITIP20 token_) { token = token_; } + /// @notice Send mail directly (sender = msg.sender). function sendMail(address to, string memory message, Attachment memory attachment) external { token.transferFromWithMemo(msg.sender, to, attachment.amount, attachment.memo); emit MailSent(msg.sender, to, message, attachment); } + + /// @notice Send mail on behalf of `from` using their off-chain Tempo signature (requires T3). + /// @dev The sender must have pre-approved this contract to spend their tokens. + function sendMail( + address from, + address to, + string memory message, + Attachment memory attachment, + bytes calldata signature + ) external { + bytes32 hash = getDigest(from, to, message, attachment); + + // `verify()` returns `false` on signer mismatch, reverts on malformed signatures. + require(StdPrecompiles.SIGNATURE_VERIFIER.verify(from, hash, signature), "invalid signature"); + + // `recover()` returns the signer address directly, reverts on malformed signatures. + require(StdPrecompiles.SIGNATURE_VERIFIER.recover(hash, signature) == from, "invalid signature"); + + nonces[from]++; + token.transferFromWithMemo(from, to, attachment.amount, attachment.memo); + emit MailSent(from, to, message, attachment); + } + + /// @notice Compute the digest a sender must sign to authorize a relayed mail. + function getDigest(address from, address to, string memory message, Attachment memory attachment) + public + view + returns (bytes32) + { + return keccak256(abi.encode(address(this), block.chainid, from, to, message, attachment, nonces[from])); + } } diff --git a/crates/forge/assets/tempo/MailTemplate.t.sol b/crates/forge/assets/tempo/MailTemplate.t.sol index f5a9976715026..b1749db5df0bf 100644 --- a/crates/forge/assets/tempo/MailTemplate.t.sol +++ b/crates/forge/assets/tempo/MailTemplate.t.sol @@ -8,6 +8,7 @@ import {StdPrecompiles} from "tempo-std/StdPrecompiles.sol"; import {StdTokens} from "tempo-std/StdTokens.sol"; import {Mail} from "../src/Mail.sol"; +/// @notice Tests for direct mail sending (no signature verification). contract MailTest is Test { ITIP20 public token; Mail public mail; @@ -15,7 +16,7 @@ contract MailTest is Test { address public constant ALICE = address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); address public constant BOB = address(0x70997970C51812dc3A010C7d01b50e0d17dc79C8); - function setUp() public { + function setUp() public virtual { address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.ALPHA_USD_ADDRESS); StdPrecompiles.TIP_FEE_MANAGER.setUserToken(feeToken); @@ -35,11 +36,10 @@ contract MailTest is Test { Mail.Attachment memory attachment = Mail.Attachment({amount: 100 * 10 ** token.decimals(), memo: "Invoice #1234"}); - vm.prank(ALICE); + vm.startPrank(ALICE); token.approve(address(mail), attachment.amount); - - vm.prank(ALICE); - mail.sendMail(BOB, "Hello Alice, this is a unit test mail.", attachment); + mail.sendMail(BOB, "Hello Bob, here is your invoice.", attachment); + vm.stopPrank(); assertEq(token.balanceOf(BOB), attachment.amount); assertEq(token.balanceOf(ALICE), 100_000 * 10 ** token.decimals() - attachment.amount); @@ -62,3 +62,114 @@ contract MailTest is Test { assertEq(token.balanceOf(ALICE), mintAmount - sendAmount); } } + +/// @notice Tests for relayed mail using the TIP-1020 SignatureVerifier precompile (requires T3). +/// forge-config: default.hardfork = "tempo:T3" +contract MailRelayTest is MailTest { + // secp256k1 keys (used by vm.sign / vm.addr) + uint256 internal constant ALICE_PK = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + uint256 internal constant BOB_PK = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d; + + // P256 key (used by vm.signP256 / vm.publicKeyP256) + uint256 internal constant CAROL_P256_PK = 0x1; + address internal CAROL; + bytes32 internal carolPubX; + bytes32 internal carolPubY; + + uint256 internal constant P256_ORDER = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; + uint256 internal constant P256N_HALF = 0x7FFFFFFF800000007FFFFFFFFFFFFFFFDE737D56D38BCF4279DCE5617E3192A8; + + function setUp() public override { + super.setUp(); + + // Derive P256 public key and Tempo address for Carol + (uint256 x, uint256 y) = vm.publicKeyP256(CAROL_P256_PK); + carolPubX = bytes32(x); + carolPubY = bytes32(y); + CAROL = address(uint160(uint256(keccak256(abi.encodePacked(x, y))))); + } + + /// @notice Relayed send with a secp256k1 signature — Alice signs, Bob delivers. + function test_SendMailWithSecp256k1Signature() public { + token.mint(ALICE, 100_000 * 10 ** token.decimals()); + + Mail.Attachment memory attachment = + Mail.Attachment({amount: 100 * 10 ** token.decimals(), memo: "Invoice #1234"}); + + vm.prank(ALICE); + token.approve(address(mail), attachment.amount); + + string memory message = "Hello Bob, here is your invoice."; + bytes32 digest = mail.getDigest(ALICE, BOB, message, attachment); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_PK, digest); + + vm.prank(BOB); + mail.sendMail(ALICE, BOB, message, attachment, abi.encodePacked(r, s, v)); + + assertEq(token.balanceOf(BOB), attachment.amount); + assertEq(mail.nonces(ALICE), 1); + } + + /// @notice Relayed send with a P256 signature — Carol signs, Bob delivers. + function test_SendMailWithP256Signature() public { + token.mint(CAROL, 100_000 * 10 ** token.decimals()); + + Mail.Attachment memory attachment = + Mail.Attachment({amount: 100 * 10 ** token.decimals(), memo: "Invoice #1234"}); + + vm.prank(CAROL); + token.approve(address(mail), attachment.amount); + + string memory message = "Hello Bob, signed with P256."; + bytes32 digest = mail.getDigest(CAROL, BOB, message, attachment); + (bytes32 r, bytes32 s) = vm.signP256(CAROL_P256_PK, digest); + s = _normalizeP256S(s); + + bytes memory sig = abi.encodePacked(uint8(0x01), r, s, carolPubX, carolPubY, uint8(0)); + + vm.prank(BOB); + mail.sendMail(CAROL, BOB, message, attachment, sig); + + assertEq(token.balanceOf(BOB), attachment.amount); + assertEq(mail.nonces(CAROL), 1); + } + + /// @notice Replaying the same signature fails (nonce incremented). + function test_ReplayReverts() public { + token.mint(ALICE, 100_000 * 10 ** token.decimals()); + + Mail.Attachment memory attachment = Mail.Attachment({amount: 50 * 10 ** token.decimals(), memo: "tip"}); + + vm.prank(ALICE); + token.approve(address(mail), attachment.amount * 2); + + string memory message = "tip"; + bytes32 digest = mail.getDigest(ALICE, BOB, message, attachment); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_PK, digest); + bytes memory sig = abi.encodePacked(r, s, v); + + mail.sendMail(ALICE, BOB, message, attachment, sig); + + vm.expectRevert(); + mail.sendMail(ALICE, BOB, message, attachment, sig); + } + + /// @notice Submitting Bob's signature as Alice's fails. + function test_WrongSignerReverts() public { + Mail.Attachment memory attachment = Mail.Attachment({amount: 100, memo: "fake"}); + + string memory message = "spoofed"; + bytes32 digest = mail.getDigest(ALICE, BOB, message, attachment); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(BOB_PK, digest); + + vm.expectRevert("invalid signature"); + mail.sendMail(ALICE, BOB, message, attachment, abi.encodePacked(r, s, v)); + } + + /// @dev Normalize P256 s to low-s form (required by the precompile). + function _normalizeP256S(bytes32 s) internal pure returns (bytes32) { + uint256 sVal = uint256(s); + if (sVal > P256N_HALF) return bytes32(P256_ORDER - sVal); + return s; + } +} diff --git a/crates/forge/src/args.rs b/crates/forge/src/args.rs index 4f55f028a3690..c5779aebbcbc7 100644 --- a/crates/forge/src/args.rs +++ b/crates/forge/src/args.rs @@ -6,7 +6,7 @@ use clap::{CommandFactory, Parser}; use clap_complete::generate; use eyre::Result; use foundry_cli::utils; -use foundry_common::shell; +use foundry_common::{sh_warn, shell}; use foundry_evm::inspectors::cheatcodes::{ForgeContext, set_execution_context}; /// Run the `forge` command line interface. @@ -99,7 +99,9 @@ pub fn run_command(args: Forge) -> Result<()> { ForgeSubcommand::Clean { root } => { let config = utils::load_config_with_root(root.as_deref())?; let project = config.project()?; - config.cleanup(&project)?; + for warning in config.cleanup(&project)? { + let _ = sh_warn!("{warning}"); + } Ok(()) } ForgeSubcommand::Snapshot(cmd) => { diff --git a/crates/forge/src/cmd/cache.rs b/crates/forge/src/cmd/cache.rs index 432d7c11c19dd..0c1f22f2e6de6 100644 --- a/crates/forge/src/cmd/cache.rs +++ b/crates/forge/src/cmd/cache.rs @@ -4,6 +4,7 @@ use clap::{ builder::{PossibleValuesParser, TypedValueParser}, }; use eyre::Result; +use foundry_common::sh_warn; use foundry_config::{Chain, Config, NamedChain, cache}; use std::{ffi::OsStr, str::FromStr}; use strum::VariantNames; @@ -63,10 +64,13 @@ impl CleanArgs { clean_chain_cache(chain, blocks.clone(), etherscan)? } ChainOrAll::All => { - if etherscan { - Config::clean_foundry_etherscan_cache()?; + let warnings = if etherscan { + Config::clean_foundry_etherscan_cache()? } else { Config::clean_foundry_cache()? + }; + for warning in warnings { + let _ = sh_warn!("{warning}"); } } } @@ -128,17 +132,24 @@ impl FromStr for ChainOrAll { fn clean_chain_cache(chain: impl Into, blocks: Vec, etherscan: bool) -> Result<()> { let chain = chain.into(); + let mut warnings = Vec::new(); if blocks.is_empty() { - Config::clean_foundry_etherscan_chain_cache(chain)?; + warnings.extend(Config::clean_foundry_etherscan_chain_cache(chain)?); if etherscan { + for warning in warnings { + let _ = sh_warn!("{warning}"); + } return Ok(()); } - Config::clean_foundry_chain_cache(chain)?; + warnings.extend(Config::clean_foundry_chain_cache(chain)?); } else { for block in blocks { - Config::clean_foundry_block_cache(chain, block)?; + warnings.extend(Config::clean_foundry_block_cache(chain, block)?); } } + for warning in warnings { + let _ = sh_warn!("{warning}"); + } Ok(()) } diff --git a/crates/forge/src/cmd/config.rs b/crates/forge/src/cmd/config.rs index 49716146c456b..fac66727c9d99 100644 --- a/crates/forge/src/cmd/config.rs +++ b/crates/forge/src/cmd/config.rs @@ -53,8 +53,8 @@ impl ConfigArgs { } else { config.to_string_pretty()? }; - sh_println!("{s}")?; + Ok(()) } } diff --git a/crates/forge/src/cmd/inspect.rs b/crates/forge/src/cmd/inspect.rs index 4eedd0f8f6b08..9c1fb32775019 100644 --- a/crates/forge/src/cmd/inspect.rs +++ b/crates/forge/src/cmd/inspect.rs @@ -9,7 +9,6 @@ use foundry_common::{ find_matching_contract_artifact, find_target_path, shell, }; use foundry_compilers::{ - ProjectCompileOutput, artifacts::{ StorageLayout, output_selection::{ @@ -19,11 +18,9 @@ use foundry_compilers::{ }, solc::SolcLanguage, }; -use path_slash::PathExt; use regex::Regex; use serde_json::{Map, Value}; -use solar::sema::interface::source_map::FileName; -use std::{collections::BTreeMap, fmt, ops::ControlFlow, path::Path, str::FromStr, sync::LazyLock}; +use std::{collections::BTreeMap, fmt, str::FromStr, sync::LazyLock}; /// CLI arguments for `forge inspect`. #[derive(Clone, Debug, Parser)] @@ -79,13 +76,8 @@ impl InspectArgs { // Build the project let project = modified_build_args.project()?; - let target_path = find_target_path(&project, &contract)?; - if field == ContractArtifactField::Linearization && !is_solidity_source(&target_path) { - eyre::bail!( - "linearization inspection is only supported for Solidity contracts (.sol targets)" - ); - } let compiler = ProjectCompiler::new().quiet(true); + let target_path = find_target_path(&project, &contract)?; let mut output = compiler.files([target_path.clone()]).compile(&project)?; // Find the artifact @@ -166,20 +158,16 @@ impl InspectArgs { .collect(); if shell::is_json() { return print_json(&all_libs); + } else { + sh_println!( + "Dynamically linked libraries:\n{}", + all_libs + .iter() + .map(|v| format!(" {v}")) + .collect::>() + .join("\n") + )?; } - sh_println!( - "Dynamically linked libraries:\n{}", - all_libs.iter().map(|v| format!(" {v}")).collect::>().join("\n") - )?; - } - ContractArtifactField::Linearization => { - print_linearization( - &mut output, - project.root(), - &target_path, - contract.name(), - wrap, - )?; } }; @@ -251,15 +239,15 @@ fn print_abi(abi: &JsonAbi, should_wrap: bool) -> Result<()> { for func in abi.functions.values().flatten() { let selector = func.selector().to_string(); let state_mut = func.state_mutability.as_json_str(); - let func_sig = if func.outputs.is_empty() { - format!("{}({}) {state_mut}", func.name, get_ty_sig(&func.inputs)) - } else { + let func_sig = if !func.outputs.is_empty() { format!( "{}({}) {state_mut} returns ({})", func.name, get_ty_sig(&func.inputs), get_ty_sig(&func.outputs) ) + } else { + format!("{}({}) {state_mut}", func.name, get_ty_sig(&func.inputs)) }; table.add_row(["function", &func_sig, &selector]); } @@ -370,7 +358,7 @@ fn print_method_identifiers( headers, |table| { for (method, identifier) in method_identifiers { - table.add_row([method.as_str(), identifier.as_str()]); + table.add_row([method, identifier]); } }, should_wrap, @@ -418,111 +406,6 @@ fn print_table( Ok(()) } -fn print_linearization( - output: &mut ProjectCompileOutput, - root: &Path, - target_path: &Path, - target_name: Option<&str>, - should_wrap: bool, -) -> Result<()> { - let mut chain = Vec::new(); - let mut lowered = false; - let compiler = output.parser_mut().solc_mut().compiler_mut(); - compiler.enter_mut(|compiler| -> Result<()> { - let Ok(ControlFlow::Continue(())) = compiler.lower_asts() else { return Ok(()) }; - lowered = true; - - let hir = &compiler.gcx().hir; - let matching_contracts = hir - .contract_ids() - .filter(|id| { - let contract = hir.contract(*id); - if let Some(target_name) = target_name - && contract.name.as_str() != target_name - { - return false; - } - - matches!( - &hir.source(contract.source).file.name, - FileName::Real(path) if path == target_path - ) - }) - .collect::>(); - - let target_contract = match matching_contracts.as_slice() { - [id] => *id, - [] => { - if let Some(target_name) = target_name { - eyre::bail!( - "Could not find contract `{target_name}` in `{}`", - target_path.display() - ); - } - eyre::bail!("Could not find contract in `{}`", target_path.display()); - } - _ => { - eyre::bail!( - "Multiple contracts found in the same file, please specify the target : or " - ); - } - }; - - for (order, base_id) in hir.contract(target_contract).linearized_bases.iter().enumerate() { - let contract = hir.contract(*base_id); - let source = hir.source(contract.source); - let FileName::Real(path) = &source.file.name else { continue }; - let path = path.strip_prefix(root).unwrap_or(path); - chain.push(( - order, - path.to_slash_lossy().into_owned(), - contract.name.as_str().to_string(), - )); - } - - Ok(()) - })?; - - // `compiler.sess()` inside of `ProjectCompileOutput` is built with `with_buffer_emitter`. - let diags = compiler.sess().dcx.emitted_diagnostics().unwrap(); - if compiler.sess().dcx.has_errors().is_err() { - eyre::bail!("{diags}"); - } else { - let _ = sh_eprint!("{diags}"); - } - if !lowered { - eyre::bail!( - "unable to inspect linearization: failed to lower Solidity ASTs for `{}`", - target_path.display() - ); - } - - if shell::is_json() { - let contracts = chain - .into_iter() - .map(|(order, source, contract)| { - serde_json::json!({ - "order": order, - "source": source, - "contract": contract, - }) - }) - .collect::>(); - return print_json(&contracts); - } - - let headers = vec![Cell::new("Order"), Cell::new("Source"), Cell::new("Contract")]; - print_table( - headers, - |table| { - for (order, source, contract) in &chain { - table.add_row([order.to_string(), source.clone(), contract.clone()]); - } - }, - should_wrap, - ) -} - /// Contract level output selection #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ContractArtifactField { @@ -545,7 +428,6 @@ pub enum ContractArtifactField { Events, StandardJson, Libraries, - Linearization, } macro_rules! impl_value_enum { @@ -630,9 +512,6 @@ impl_value_enum! { Events => "events" | "ev", StandardJson => "standardJson" | "standard-json" | "standard_json", Libraries => "libraries" | "lib" | "libs", - Linearization => "linearization" | "linearizedInheritance" - | "linearized-inheritance" | "linearized_inheritance" - | "linearizedBases" | "linearized-bases" | "linearized_bases", } } @@ -666,9 +545,6 @@ impl TryFrom for ContractOutputSelection { Err(eyre!("StandardJson is not supported for ContractOutputSelection")) } Caf::Libraries => Err(eyre!("Libraries is not supported for ContractOutputSelection")), - Caf::Linearization => { - Err(eyre!("Linearization is not supported for ContractOutputSelection")) - } } } } @@ -679,7 +555,8 @@ impl PartialEq for ContractArtifactField { type Eos = EvmOutputSelection; matches!( (self, other), - (Self::Abi | Self::Events | Self::Errors, Cos::Abi) + (Self::Abi | Self::Events, Cos::Abi) + | (Self::Errors, Cos::Abi) | (Self::Bytecode, Cos::Evm(Eos::ByteCode(_))) | (Self::DeployedBytecode, Cos::Evm(Eos::DeployedByteCode(_))) | (Self::Assembly | Self::AssemblyOptimized, Cos::Evm(Eos::Assembly)) @@ -708,11 +585,7 @@ impl ContractArtifactField { pub const fn can_skip_field(&self) -> bool { matches!( self, - Self::Bytecode - | Self::DeployedBytecode - | Self::StandardJson - | Self::Libraries - | Self::Linearization + Self::Bytecode | Self::DeployedBytecode | Self::StandardJson | Self::Libraries ) } } @@ -759,10 +632,6 @@ fn get_json_str(obj: &impl serde::Serialize, key: Option<&str>) -> Result bool { - path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| ext.eq_ignore_ascii_case("sol")) -} - fn missing_error(field: &str) -> eyre::Error { eyre!( "{field} missing from artifact; \ @@ -793,14 +662,6 @@ mod tests { .to_string() .eq("Libraries is not supported for ContractOutputSelection") ); - } else if field == ContractArtifactField::Linearization { - let selection: Result = field.try_into(); - assert!( - selection - .unwrap_err() - .to_string() - .eq("Linearization is not supported for ContractOutputSelection") - ); } else { let selection: ContractOutputSelection = field.try_into().unwrap(); assert_eq!(field, selection); diff --git a/crates/forge/src/cmd/install.rs b/crates/forge/src/cmd/install.rs index 3fcad958a6cc0..b7aa4ca294098 100644 --- a/crates/forge/src/cmd/install.rs +++ b/crates/forge/src/cmd/install.rs @@ -197,7 +197,7 @@ impl DependencyInstallOpts { let rev = git.get_rev(tag_or_branch, &path)?; dep_id = Some(DepIdentifier::Branch { - name: tag_or_branch.clone(), + name: tag_or_branch.to_string(), rev, r#override: false, }); @@ -327,10 +327,8 @@ impl Installer<'_> { dep.tag = self.last_tag(path); } - // checkout the tag if necessary, using recursive checkout to properly clean up - // nested submodules that may exist on the default branch but not on the target tag. - // See: https://github.com/foundry-rs/foundry/issues/13688 - self.git_checkout(&dep, path, true)?; + // checkout the tag if necessary + self.git_checkout(&dep, path, false)?; trace!("updating dependency submodules recursively"); self.git.root(path).submodule_update( @@ -341,51 +339,12 @@ impl Installer<'_> { std::iter::empty::(), )?; - // remove nested .git directories from submodules before removing the top-level .git - Self::remove_nested_git_dirs(path)?; - // remove git artifacts fs::remove_dir_all(path.join(".git"))?; Ok(dep.tag) } - /// Recursively removes `.git` files/directories from nested submodules within `root`. - /// - /// Submodules typically have a `.git` file (not a directory) pointing to the parent's - /// `.git/modules/` directory. This cleans those up so the result is a plain folder tree. - fn remove_nested_git_dirs(root: &Path) -> Result<()> { - Self::remove_nested_git_dirs_inner(root, root) - } - - fn remove_nested_git_dirs_inner(root: &Path, dir: &Path) -> Result<()> { - let entries = match std::fs::read_dir(dir) { - Ok(entries) => entries, - Err(_) => return Ok(()), - }; - for entry in entries { - let entry = entry?; - let ft = entry.file_type()?; - - // never follow symlinks - if ft.is_symlink() { - continue; - } - - let path = entry.path(); - if path.file_name() == Some(".git".as_ref()) && path.parent() != Some(root) { - if ft.is_dir() { - fs::remove_dir_all(&path)?; - } else { - fs::remove_file(&path)?; - } - } else if ft.is_dir() { - Self::remove_nested_git_dirs_inner(root, &path)?; - } - } - Ok(()) - } - /// Installs the dependency as new submodule. /// /// This will add the git submodule to the given dir, initialize it and checkout the tag if @@ -567,7 +526,7 @@ impl Installer<'_> { sh_println!("[{i}] {c} selected")?; return Ok(c.clone()); } - _ => {} + _ => continue, } } } diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index da300c429e37e..8698adaddacfa 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -23,7 +23,7 @@ use foundry_cli::{ use foundry_common::{EmptyTestFilter, TestFunctionExt, compile::ProjectCompiler, fs, shell}; use foundry_compilers::{ ProjectCompileOutput, - artifacts::{Libraries, output_selection::OutputSelection}, + artifacts::output_selection::OutputSelection, compilers::{ Language, multi::{MultiCompiler, MultiCompilerLanguage}, @@ -40,16 +40,11 @@ use foundry_config::{ }; use foundry_debugger::Debugger; use foundry_evm::{ - core::evm::{ - BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, SpecFor, TempoEvmNetwork, - TxEnvFor, - }, opts::EvmOpts, traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth}, }; use rand::Rng; use regex::Regex; -use revm::context::Transaction; use std::{ collections::{BTreeMap, BTreeSet}, fmt::Write, @@ -320,11 +315,14 @@ impl TestArgs { let should_debug = self.debug; let should_draw = self.flamegraph || self.flamechart; - // Determine executor verbosity. + // Determine print verbosity and executor verbosity. + let verbosity = evm_opts.verbosity; if (self.gas_report && evm_opts.verbosity < 3) || self.flamegraph || self.flamechart { evm_opts.verbosity = 3; } + let env = evm_opts.env().await?; + // Enable internal tracing for more informative flamegraph. if should_draw && !self.decode_internal { self.decode_internal = true; @@ -339,44 +337,23 @@ impl TestArgs { InternalTraceMode::None }; - // Auto-detect network from fork chain ID when not explicitly configured. - evm_opts.infer_network_from_fork().await; - - // Dispatch based on network type. - let (libraries, mut outcome) = if evm_opts.networks.is_tempo() { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? - } else if evm_opts.networks.is_optimism() { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? - } else { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? - }; + // Prepare the test builder. + let config = Arc::new(config); + let runner = MultiContractRunnerBuilder::new(config.clone()) + .set_debug(should_debug) + .set_decode_internal(decode_internal) + .initial_balance(evm_opts.initial_balance) + .evm_spec(config.evm_spec_id()) + .sender(evm_opts.sender) + .with_fork(evm_opts.get_fork(&config, env.evm_env.clone())) + .enable_isolation(evm_opts.isolate) + .networks(evm_opts.networks) + .fail_fast(self.fail_fast) + .set_coverage(coverage) + .build::(output, env, evm_opts)?; + + let libraries = runner.libraries.clone(); + let mut outcome = self.run_tests_inner(runner, config, verbosity, filter, output).await?; if should_draw { let (suite_name, test_name, mut test_result) = @@ -450,43 +427,10 @@ impl TestArgs { Ok(outcome) } - /// Build the test runner and execute tests for a specific network type. - #[allow(clippy::too_many_arguments)] - async fn build_and_run_tests( - &self, - config: Config, - evm_opts: EvmOpts, - output: &ProjectCompileOutput, - filter: &ProjectPathsAwareFilter, - coverage: bool, - should_debug: bool, - decode_internal: InternalTraceMode, - ) -> eyre::Result<(Libraries, TestOutcome)> { - let verbosity = evm_opts.verbosity; - let (evm_env, tx_env, fork_block) = - evm_opts.env::, BlockEnvFor, TxEnvFor>().await?; - - let config = Arc::new(config); - let runner = MultiContractRunnerBuilder::new(config.clone()) - .set_debug(should_debug) - .set_decode_internal(decode_internal) - .initial_balance(evm_opts.initial_balance) - .sender(evm_opts.sender) - .with_fork(evm_opts.get_fork(&config, evm_env.cfg_env.chain_id, fork_block)) - .enable_isolation(evm_opts.isolate) - .fail_fast(self.fail_fast) - .set_coverage(coverage) - .build::(output, evm_env, tx_env, evm_opts)?; - - let libraries = runner.libraries.clone(); - let outcome = self.run_tests_inner(runner, config, verbosity, filter, output).await?; - Ok((libraries, outcome)) - } - /// Run all tests that matches the filter predicate from a test runner - async fn run_tests_inner( + async fn run_tests_inner( &self, - mut runner: MultiContractRunner, + mut runner: MultiContractRunner, config: Arc, verbosity: u8, filter: &ProjectPathsAwareFilter, @@ -505,11 +449,10 @@ impl TestArgs { let num_filtered = runner.matching_test_functions(filter).count(); if num_filtered == 0 { - let total_tests = if filter.is_empty() { - num_filtered - } else { - runner.matching_test_functions(&EmptyTestFilter::default()).count() - }; + let mut total_tests = num_filtered; + if !filter.is_empty() { + total_tests = runner.matching_test_functions(&EmptyTestFilter::default()).count(); + } if total_tests == 0 { sh_println!( "No tests found in project! Forge looks for functions that start with `test`" @@ -527,7 +470,7 @@ impl TestArgs { } sh_warn!("{msg}")?; } - return Ok(TestOutcome::empty(Some(runner.known_contracts.clone()), false)); + return Ok(TestOutcome::empty(Some(runner), false)); } if num_filtered != 1 && (self.debug || self.flamegraph || self.flamechart) { @@ -569,19 +512,17 @@ impl TestArgs { } } sh_println!("{}", serde_json::to_string(&results)?)?; - let kc = runner.known_contracts.clone(); - return Ok(TestOutcome::new(Some(kc), results, self.allow_failure, fuzz_seed)); + return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed)); } if self.junit { let results = runner.test_collect(filter)?; sh_println!("{}", junit_xml_report(&results, verbosity).to_string()?)?; - let kc = runner.known_contracts.clone(); - return Ok(TestOutcome::new(Some(kc), results, self.allow_failure, fuzz_seed)); + return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed)); } let remote_chain = - if runner.fork.is_some() { runner.tx_env.chain_id().map(Into::into) } else { None }; + if runner.fork.is_some() { runner.env.tx.chain_id.map(Into::into) } else { None }; let known_contracts = runner.known_contracts.clone(); let libraries = runner.libraries.clone(); @@ -609,8 +550,7 @@ impl TestArgs { let mut builder = CallTraceDecoderBuilder::new() .with_known_contracts(&known_contracts) .with_label_disabled(self.disable_labels) - .with_verbosity(verbosity) - .with_chain_id(remote_chain.map(|c| c.id())); + .with_verbosity(verbosity); // Signatures are of no value for gas reports. if !self.gas_report { builder = @@ -742,9 +682,7 @@ impl TestArgs { } } - // Extract and display backtrace for failed tests when verbosity >= 3. - // At verbosity 3-4 backtraces show contract/function names only. - // At verbosity 5 backtraces include source file locations. + // Extract and display backtrace for failed tests when verbosity >= 3 if !silent && result.status.is_failure() && verbosity >= 3 @@ -828,9 +766,14 @@ impl TestArgs { .iter() .filter_map(|(k, v)| { previous_snapshots.get(k).and_then(|previous_snapshot| { - (previous_snapshot != v).then(|| { - (k.clone(), (previous_snapshot.clone(), v.clone())) - }) + if previous_snapshot != v { + Some(( + k.clone(), + (previous_snapshot.clone(), v.clone()), + )) + } else { + None + } }) }) .collect(); @@ -920,10 +863,7 @@ impl TestArgs { // Reattach the task. match handle.await { - Ok(result) => { - let runner = result?; - outcome.known_contracts = Some(runner.known_contracts); - } + Ok(result) => outcome.runner = Some(result?), Err(e) => match e.try_into_panic() { Ok(payload) => std::panic::resume_unwind(payload), Err(e) => return Err(e.into()), @@ -954,7 +894,7 @@ impl TestArgs { } /// Returns whether `BuildArgs` was configured with `--watch` - pub const fn is_watch(&self) -> bool { + pub fn is_watch(&self) -> bool { self.watch.watch.is_some() } @@ -993,7 +933,7 @@ impl Provider for TestArgs { if let Some(etherscan_api_key) = self.etherscan_api_key.as_ref().filter(|s| !s.trim().is_empty()) { - dict.insert("etherscan_api_key".to_string(), etherscan_api_key.clone().into()); + dict.insert("etherscan_api_key".to_string(), etherscan_api_key.to_string().into()); } if self.show_progress { @@ -1005,10 +945,7 @@ impl Provider for TestArgs { } /// Lists all matching tests -fn list( - runner: MultiContractRunner, - filter: &ProjectPathsAwareFilter, -) -> Result { +fn list(runner: MultiContractRunner, filter: &ProjectPathsAwareFilter) -> Result { let results = runner.list(filter); if shell::is_json() { @@ -1022,7 +959,7 @@ fn list( } } } - Ok(TestOutcome::empty(Some(runner.known_contracts), false)) + Ok(TestOutcome::empty(Some(runner), false)) } /// Load persisted filter (with last test run failures) from file. @@ -1047,7 +984,7 @@ fn persist_run_failures(config: &Config, outcome: &TestOutcome) { let mut failures = outcome.failures().peekable(); while let Some((test_name, _)) = failures.next() { if test_name.is_any_test() - && let Some(test_match) = test_name.split('(').next() + && let Some(test_match) = test_name.split("(").next() { filter.push_str(test_match); if failures.peek().is_some() { diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 88bbc6156c812..12e2ce39691fe 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -10,6 +10,7 @@ use eyre::Result; use foundry_cli::opts::configure_pcx_from_compile_output; use foundry_common::{ ContractsByArtifact, ContractsByArtifactBuilder, TestFunctionExt, get_contract_name, + shell::verbosity, }; use foundry_compilers::{ Artifact, ArtifactId, Compiler, ProjectCompileOutput, @@ -17,8 +18,8 @@ use foundry_compilers::{ }; use foundry_config::{Config, InlineConfig}; use foundry_evm::{ + Env, backend::Backend, - core::evm::{EvmEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor}, decode::RevertDecoder, executors::{EarlyExit, Executor, ExecutorBuilder}, fork::CreateFork, @@ -27,13 +28,13 @@ use foundry_evm::{ opts::EvmOpts, traces::{InternalTraceMode, TraceMode}, }; - +use foundry_evm_networks::NetworkConfigs; use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; +use revm::primitives::hardfork::SpecId; use std::{ borrow::Borrow, collections::BTreeMap, - ops::{Deref, DerefMut}, path::Path, sync::{Arc, mpsc}, time::Instant, @@ -50,7 +51,7 @@ 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. #[derive(Clone, Debug)] -pub struct MultiContractRunner { +pub struct MultiContractRunner { /// Mapping of contract name to JsonAbi, creation bytecode and library bytecode which /// needs to be deployed & linked against pub contracts: DeployableContracts, @@ -71,24 +72,24 @@ pub struct MultiContractRunner { pub fork: Option, /// The base configuration for the test runner. - pub tcfg: TestRunnerConfig, + pub tcfg: TestRunnerConfig, } -impl Deref for MultiContractRunner { - type Target = TestRunnerConfig; +impl std::ops::Deref for MultiContractRunner { + type Target = TestRunnerConfig; fn deref(&self) -> &Self::Target { &self.tcfg } } -impl DerefMut for MultiContractRunner { +impl std::ops::DerefMut for MultiContractRunner { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.tcfg } } -impl MultiContractRunner { +impl MultiContractRunner { /// Returns an iterator over all contracts that match the filter. pub fn matching_contracts<'a: 'b, 'b>( &'a self, @@ -240,17 +241,17 @@ impl MultiContractRunner { &self, artifact_id: &ArtifactId, contract: &TestContract, - db: &Backend, + db: &Backend, filter: &dyn TestFilter, tokio_handle: &tokio::runtime::Handle, progress: Option<&TestsProgress>, ) -> SuiteResult { let identifier = artifact_id.identifier(); - let span_name = if enabled!(tracing::Level::TRACE) { - identifier.as_str() - } else { - get_contract_name(&identifier) - }; + let mut span_name = identifier.as_str(); + + if !enabled!(tracing::Level::TRACE) { + span_name = get_contract_name(&identifier); + } let span = debug_span!("suite", name = %span_name); let span_local = span.clone(); let _guard = span_local.enter(); @@ -284,7 +285,7 @@ impl MultiContractRunner { /// /// This is modified after instantiation through inline config. #[derive(Clone, Debug)] -pub struct TestRunnerConfig { +pub struct TestRunnerConfig { /// Project config. pub config: Arc, /// Inline configuration. @@ -293,11 +294,9 @@ pub struct TestRunnerConfig { /// EVM configuration. pub evm_opts: EvmOpts, /// EVM environment. - pub evm_env: EvmEnvFor, - /// Transaction environment. - pub tx_env: TxEnvFor, + pub env: Env, /// EVM version. - pub spec_id: SpecFor, + pub spec_id: SpecId, /// The address which will be used to deploy the initial contracts and send all transactions. pub sender: Address, @@ -309,11 +308,13 @@ pub struct TestRunnerConfig { pub decode_internal: InternalTraceMode, /// Whether to enable call isolation. pub isolation: bool, + /// Networks with enabled features. + pub networks: NetworkConfigs, /// Whether to exit early on test failure or if test run interrupted. pub early_exit: EarlyExit, } -impl TestRunnerConfig { +impl TestRunnerConfig { /// Reconfigures all fields using the given `config`. /// This is for example used to override the configuration with inline config. pub fn reconfigure_with(&mut self, config: Arc) { @@ -321,7 +322,7 @@ impl TestRunnerConfig { self.spec_id = config.evm_spec_id(); self.sender = config.sender; - self.evm_opts.networks = config.networks; + self.networks = config.networks; self.isolation = config.isolate; // Specific to Forge, not present in config. @@ -338,7 +339,7 @@ impl TestRunnerConfig { } /// Configures the given executor with this configuration. - pub fn configure_executor(&self, executor: &mut Executor) { + pub fn configure_executor(&self, executor: &mut Executor) { // TODO: See above let inspector = executor.inspector_mut(); @@ -350,7 +351,7 @@ impl TestRunnerConfig { inspector.tracing(self.trace_mode()); inspector.collect_line_coverage(self.line_coverage); inspector.enable_isolation(self.isolation); - inspector.networks(self.evm_opts.networks); + inspector.networks(self.networks); // inspector.set_create2_deployer(self.evm_opts.create2_deployer); // executor.env_mut().clone_from(&self.env); @@ -365,16 +366,15 @@ impl TestRunnerConfig { known_contracts: ContractsByArtifact, analysis: Arc, artifact_id: &ArtifactId, - db: Backend, - ) -> Executor { + db: Backend, + ) -> Executor { let cheats_config = Arc::new(CheatsConfig::new( &self.config, self.evm_opts.clone(), Some(known_contracts), Some(artifact_id.clone()), - None, )); - ExecutorBuilder::default() + ExecutorBuilder::new() .inspectors(|stack| { stack .logs(self.config.live_logs) @@ -382,14 +382,14 @@ impl TestRunnerConfig { .trace_mode(self.trace_mode()) .line_coverage(self.line_coverage) .enable_isolation(self.isolation) - .networks(self.evm_opts.networks) + .networks(self.networks) .create2_deployer(self.evm_opts.create2_deployer) .set_analysis(analysis) }) .spec_id(self.spec_id) .gas_limit(self.evm_opts.gas_limit()) .legacy_assertions(self.config.legacy_assertions) - .build(self.evm_env.clone(), self.tx_env.clone(), db) + .build(self.env.clone(), db) } fn trace_mode(&self) -> TraceMode { @@ -397,6 +397,7 @@ impl TestRunnerConfig { .with_debug(self.debug) .with_decode_internal(self.decode_internal) .with_verbosity(self.evm_opts.verbosity) + .with_state_changes(verbosity() > 4) } } @@ -409,6 +410,8 @@ pub struct MultiContractRunnerBuilder { pub sender: Option
, /// The initial balance for each one of the deployed smart contracts pub initial_balance: U256, + /// The EVM spec to use + pub evm_spec: Option, /// The fork to use at launch pub fork: Option, /// Project config. @@ -421,6 +424,8 @@ pub struct MultiContractRunnerBuilder { pub decode_internal: InternalTraceMode, /// Whether to enable call isolation pub isolation: bool, + /// Networks with enabled features. + pub networks: NetworkConfigs, /// Whether to exit early on test failure. pub fail_fast: bool, } @@ -431,64 +436,75 @@ impl MultiContractRunnerBuilder { config, sender: Default::default(), initial_balance: Default::default(), + evm_spec: Default::default(), fork: Default::default(), line_coverage: Default::default(), debug: Default::default(), isolation: Default::default(), decode_internal: Default::default(), + networks: Default::default(), fail_fast: false, } } - pub const fn sender(mut self, sender: Address) -> Self { + pub fn sender(mut self, sender: Address) -> Self { self.sender = Some(sender); self } - pub const fn initial_balance(mut self, initial_balance: U256) -> Self { + pub fn initial_balance(mut self, initial_balance: U256) -> Self { self.initial_balance = initial_balance; self } + pub fn evm_spec(mut self, spec: SpecId) -> Self { + self.evm_spec = Some(spec); + self + } + pub fn with_fork(mut self, fork: Option) -> Self { self.fork = fork; self } - pub const fn set_coverage(mut self, enable: bool) -> Self { + pub fn set_coverage(mut self, enable: bool) -> Self { self.line_coverage = enable; self } - pub const fn set_debug(mut self, enable: bool) -> Self { + pub fn set_debug(mut self, enable: bool) -> Self { self.debug = enable; self } - pub const fn set_decode_internal(mut self, mode: InternalTraceMode) -> Self { + pub fn set_decode_internal(mut self, mode: InternalTraceMode) -> Self { self.decode_internal = mode; self } - pub const fn fail_fast(mut self, fail_fast: bool) -> Self { + pub fn fail_fast(mut self, fail_fast: bool) -> Self { self.fail_fast = fail_fast; self } - pub const fn enable_isolation(mut self, enable: bool) -> Self { + pub fn enable_isolation(mut self, enable: bool) -> Self { self.isolation = enable; self } + pub fn networks(mut self, networks: NetworkConfigs) -> Self { + self.networks = networks; + self + } + /// 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, output: &ProjectCompileOutput, - evm_env: EvmEnvFor, - tx_env: TxEnvFor, + env: Env, evm_opts: EvmOpts, - ) -> Result> { + ) -> Result { let root = &self.config.root; let contracts = output .artifact_ids() @@ -551,7 +567,8 @@ impl MultiContractRunnerBuilder { dcx.set_flags_mut(|f| f.track_diagnostics = false); // Populate solar's global context by parsing and lowering the sources. - let files: Vec<_> = output.output().sources.as_ref().keys().cloned().collect(); + let files: Vec<_> = + output.output().sources.as_ref().keys().map(|path| path.to_path_buf()).collect(); analysis.enter_mut(|compiler| -> Result<()> { let mut pcx = compiler.parse(); @@ -584,15 +601,15 @@ impl MultiContractRunnerBuilder { tcfg: TestRunnerConfig { evm_opts, - evm_env, - tx_env, - spec_id: self.config.evm_spec_id(), + env, + spec_id: self.evm_spec.unwrap_or_else(|| self.config.evm_spec_id()), sender: self.sender.unwrap_or(self.config.sender), line_coverage: self.line_coverage, debug: self.debug, decode_internal: self.decode_internal, inline_config: Arc::new(InlineConfig::new_parsed(output, &self.config)?), isolation: self.isolation, + networks: self.networks, early_exit: EarlyExit::new(self.fail_fast), config: self.config, }, diff --git a/crates/forge/tests/cli/install.rs b/crates/forge/tests/cli/install.rs index 35b785cb111de..f95f865f92bfb 100644 --- a/crates/forge/tests/cli/install.rs +++ b/crates/forge/tests/cli/install.rs @@ -591,51 +591,6 @@ async fn correctly_sync_dep_with_multiple_version() { assert_eq!(solday_v_245.rev(), submod_solday_v_245.rev()); } -// Regression test: `forge install --no-git` should clean up nested submodule contents -// when installing a tag that does not use submodules for its dependencies. -// https://github.com/foundry-rs/foundry/issues/13688 -forgetest!(flaky_install_no_git_cleans_nested_submodules, |prj, cmd| { - cmd.git_init(); - - // Install openzeppelin-contracts-upgradeable at v4.7.3 with --no-git. - // The default branch has submodules in lib/ (e.g. openzeppelin-contracts, erc4626-tests), - // but v4.7.3 does not use submodules for dependencies. - cmd.forge_fuse() - .args(["install", "--no-git", "OpenZeppelin/openzeppelin-contracts-upgradeable@v4.7.3"]) - .assert_success(); - - let dep_dir = prj.root().join("lib").join("openzeppelin-contracts-upgradeable"); - assert!(dep_dir.exists(), "dependency should be installed"); - - // The nested lib/ directory should either not exist or be empty — v4.7.3 does not use - // submodules so there should be no leftover submodule contents from the default branch. - let nested_lib = dep_dir.join("lib"); - if nested_lib.exists() { - let entries: Vec<_> = fs::read_dir(&nested_lib).unwrap().collect(); - assert!( - entries.is_empty(), - "nested lib/ should be empty after --no-git install at v4.7.3, found: {entries:?}" - ); - } - - // There should be no .git file or directory anywhere under the installed dependency. - fn assert_no_git(dir: &Path) { - for entry in fs::read_dir(dir).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - assert!( - path.file_name() != Some(".git".as_ref()), - "found leftover .git at {}", - path.display() - ); - if path.is_dir() { - assert_no_git(&path); - } - } - } - assert_no_git(&dep_dir); -}); - forgetest_init!(sync_on_forge_update, |prj, cmd| { let git = Git::new(prj.root()); diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/script-sequence/src/transaction.rs b/crates/script-sequence/src/transaction.rs index da4448228e117..cf3c92d7eb5e2 100644 --- a/crates/script-sequence/src/transaction.rs +++ b/crates/script-sequence/src/transaction.rs @@ -1,4 +1,3 @@ -use alloy_network::Network; use alloy_primitives::{Address, B256, Bytes}; use foundry_common::TransactionMaybeSigned; use revm_inspectors::tracing::types::CallKind; @@ -8,24 +7,18 @@ use serde::{Deserialize, Serialize}; #[serde(rename_all = "camelCase")] pub struct AdditionalContract { #[serde(rename = "transactionType")] - pub call_kind: CallKind, + pub opcode: CallKind, pub contract_name: Option, pub address: Address, pub init_code: Bytes, } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde( - rename_all = "camelCase", - bound( - serialize = "N::TransactionRequest: Serialize, N::TxEnvelope: Serialize", - deserialize = "N::TransactionRequest: for<'de2> Deserialize<'de2>, N::TxEnvelope: for<'de2> Deserialize<'de2>" - ) -)] -pub struct TransactionWithMetadata { +#[serde(rename_all = "camelCase")] +pub struct TransactionWithMetadata { pub hash: Option, #[serde(rename = "transactionType")] - pub call_kind: CallKind, + pub opcode: CallKind, #[serde(default = "default_string")] pub contract_name: Option, #[serde(default = "default_address")] @@ -36,31 +29,31 @@ pub struct TransactionWithMetadata { pub arguments: Option>, #[serde(skip)] pub rpc: String, - pub transaction: TransactionMaybeSigned, + pub transaction: TransactionMaybeSigned, #[serde(default)] pub additional_contracts: Vec, #[serde(default)] pub is_fixed_gas_limit: bool, } -const fn default_string() -> Option { +fn default_string() -> Option { Some(String::new()) } -const fn default_address() -> Option
{ +fn default_address() -> Option
{ Some(Address::ZERO) } -const fn default_vec_of_strings() -> Option> { +fn default_vec_of_strings() -> Option> { Some(vec![]) } -impl TransactionWithMetadata { - pub fn from_tx_request(transaction: TransactionMaybeSigned) -> Self { +impl TransactionWithMetadata { + pub fn from_tx_request(transaction: TransactionMaybeSigned) -> Self { Self { transaction, hash: Default::default(), - call_kind: Default::default(), + opcode: Default::default(), contract_name: Default::default(), contract_address: Default::default(), function: Default::default(), @@ -71,15 +64,15 @@ impl TransactionWithMetadata { } } - pub const fn tx(&self) -> &TransactionMaybeSigned { + pub fn tx(&self) -> &TransactionMaybeSigned { &self.transaction } - pub const fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { + pub fn tx_mut(&mut self) -> &mut TransactionMaybeSigned { &mut self.transaction } pub fn is_create2(&self) -> bool { - self.call_kind == CallKind::Create2 + self.opcode == CallKind::Create2 } } diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 4146e07d2248b..65b75b8b39bb1 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -56,6 +56,7 @@ alloy-primitives.workspace = true alloy-eips.workspace = true alloy-consensus.workspace = true thiserror.workspace = true +tempo-alloy.workspace = true tempo-alloy.workspace = true tempo-primitives.workspace = true diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index f1462bb868f58..7c5ea9e14d54a 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -18,9 +18,8 @@ use foundry_compilers::{ info::ContractInfo, utils::source_files_iter, }; -use foundry_evm::{core::evm::FoundryEvmNetwork, traces::debug::ContractSources}; +use foundry_evm::traces::debug::ContractSources; use foundry_linking::Linker; -use foundry_wallets::wallet_browser::signer::BrowserSigner; use std::{path::PathBuf, str::FromStr, sync::Arc}; /// Container for the compiled contracts. @@ -41,10 +40,7 @@ impl BuildData { /// 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 { + pub async fn link(self, script_config: &ScriptConfig) -> Result { let create2_deployer = script_config.evm_opts.create2_deployer; let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { let provider = ProviderBuilder::::new(fork_url).build()?; @@ -107,7 +103,7 @@ pub enum ScriptPredeployLibraries { } impl ScriptPredeployLibraries { - pub const fn libraries_count(&self) -> usize { + pub fn libraries_count(&self) -> usize { match self { Self::Default(libs) => libs.len(), Self::Create2(libs, _) => libs.len(), @@ -157,18 +153,17 @@ impl LinkedBuildData { } /// First state basically containing only inputs of the user. -pub struct PreprocessedState { +pub struct PreprocessedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, } -impl PreprocessedState { +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, browser_wallet } = self; + pub fn compile(self) -> Result { + let Self { args, script_config, script_wallets } = self; let project = script_config.config.project()?; let mut target_name = args.target_contract.clone(); @@ -188,11 +183,12 @@ impl PreprocessedState { } }; + #[expect(clippy::redundant_clone)] let sources_to_compile = source_files_iter( project.paths.sources.as_path(), MultiCompilerLanguage::FILE_EXTENSIONS, ) - .chain([target_path.clone()]); + .chain([target_path.to_path_buf()]); let output = ProjectCompiler::new().files(sources_to_compile).compile(&project)?; @@ -236,33 +232,31 @@ impl PreprocessedState { args, script_config, script_wallets, - browser_wallet, build_data: BuildData { output, target, project_root: project.root().to_path_buf() }, }) } } /// State after we have determined and compiled target contract to be executed. -pub struct CompiledState { +pub struct CompiledState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: BuildData, } -impl CompiledState { +impl CompiledState { /// Uses provided sender address to compute library addresses and link contracts with them. - pub async fn link(self) -> Result> { - let Self { args, script_config, script_wallets, browser_wallet, build_data } = self; + pub async fn link(self) -> Result { + let Self { args, script_config, script_wallets, build_data } = self; let build_data = build_data.link(&script_config).await?; - Ok(LinkedState { args, script_config, script_wallets, browser_wallet, build_data }) + 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> { + pub async fn resume(self) -> Result { let chain = if self.args.multi { None } else { @@ -291,49 +285,35 @@ impl CompiledState { } }; - let (args, build_data, script_wallets, browser_wallet, script_config) = - if self.args.unlocked { + 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)) { + // IF we are missing required signers, execute script as we might need to collect + // private keys from the execution. + let executed = self.link().await?.prepare_execution().await?.execute().await?; ( - self.args, - self.build_data, - self.script_wallets, - self.browser_wallet, - self.script_config, + executed.args, + executed.build_data.build_data, + executed.script_wallets, + executed.script_config, ) } else { - 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)) { - ( - self.args, - self.build_data, - self.script_wallets, - self.browser_wallet, - self.script_config, - ) - } else { - // IF we are missing required signers, execute script as we might need to - // collect private keys from the execution. - let executed = self.link().await?.prepare_execution().await?.execute().await?; - ( - executed.args, - executed.build_data.build_data, - executed.script_wallets, - executed.browser_wallet, - executed.script_config, - ) - } - }; + (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 { @@ -348,17 +328,12 @@ impl CompiledState { args, script_config, script_wallets, - browser_wallet, build_data: linked_build_data, sequence, }) } - fn try_load_sequence( - &self, - chain: Option, - dry_run: bool, - ) -> Result> { + fn try_load_sequence(&self, chain: Option, dry_run: bool) -> Result { if let Some(chain) = chain { let sequence = ScriptSequence::load( &self.script_config.config, diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 162d061462b66..326960c23ac66 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -6,13 +6,13 @@ use crate::{ }; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; -use alloy_network::{AnyNetwork, Network, TransactionBuilder}; +use alloy_network::AnyNetwork; use alloy_primitives::{ Address, Bytes, map::{HashMap, HashSet}, }; use alloy_provider::Provider; -use alloy_rpc_types::TransactionInputKind; +use alloy_rpc_types::TransactionInput; use eyre::{OptionExt, Result}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; @@ -24,7 +24,6 @@ use foundry_common::{ use foundry_config::NamedChain; use foundry_debugger::Debugger; use foundry_evm::{ - core::evm::FoundryEvmNetwork, decode::decode_console_logs, inspectors::cheatcodes::BroadcastableTransactions, traces::{ @@ -33,7 +32,6 @@ use foundry_evm::{ render_trace_arena, }, }; -use foundry_wallets::wallet_browser::signer::BrowserSigner; use futures::future::join_all; use itertools::Itertools; use std::path::Path; @@ -41,11 +39,10 @@ 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 struct LinkedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, } @@ -62,11 +59,11 @@ pub struct ExecutionData { pub abi: JsonAbi, } -impl LinkedState { +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, browser_wallet, build_data } = self; + pub async fn prepare_execution(self) -> Result { + let Self { args, script_config, script_wallets, build_data } = self; let target_contract = build_data.get_target_contract()?; @@ -80,7 +77,6 @@ impl LinkedState { args, script_config, script_wallets, - browser_wallet, execution_data: ExecutionData { func, calldata, @@ -94,19 +90,18 @@ impl LinkedState { /// Same as [LinkedState], but also contains [ExecutionData]. #[derive(Debug)] -pub struct PreExecutionState { +pub struct PreExecutionState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, } -impl PreExecutionState { +impl PreExecutionState { /// Executes the script and returns the state after execution. /// Might require executing script twice in cases when we determine sender from execution. - pub async fn execute(mut self) -> Result> { + pub async fn execute(mut self) -> Result { let mut runner = self .script_config .get_runner_with_cheatcodes( @@ -128,7 +123,6 @@ impl PreExecutionState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data.build_data, }; @@ -139,7 +133,6 @@ impl PreExecutionState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data, execution_data: self.execution_data, execution_result: result, @@ -147,10 +140,7 @@ impl PreExecutionState { } /// Executes the script using the provided runner and returns the [ScriptResult]. - pub async fn execute_with_runner( - &self, - runner: &mut ScriptRunner, - ) -> Result> { + 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(), @@ -190,7 +180,7 @@ impl PreExecutionState { /// them instead. fn maybe_new_sender( &self, - transactions: Option<&BroadcastableTransactions>, + transactions: Option<&BroadcastableTransactions>, ) -> Result> { let mut new_sender = None; @@ -230,9 +220,10 @@ pub struct RpcData { impl RpcData { /// Iterates over script transactions and collects RPC urls. - fn from_transactions(txs: &BroadcastableTransactions) -> Self { + 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.clone()).collect::>(); + let total_rpcs = + txs.iter().filter_map(|tx| tx.rpc.as_ref().cloned()).collect::>(); Self { total_rpcs, missing_rpc } } @@ -281,33 +272,30 @@ pub struct ExecutionArtifacts { } /// State after the script has been executed. -pub struct ExecutedState { +pub struct ExecutedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, - pub execution_result: ScriptResult, + pub execution_result: ScriptResult, } -impl ExecutedState { +impl ExecutedState { /// Collects the data we need for simulation and various post-execution tasks. - pub async fn prepare_simulation(self) -> Result> { + pub async fn prepare_simulation(self) -> Result { let returns = self.get_returns()?; let decoder = self.build_trace_decoder(&self.build_data.known_contracts).await?; - let mut txs: BroadcastableTransactions = - self.execution_result.transactions.clone().unwrap_or_default(); + let mut txs = self.execution_result.transactions.clone().unwrap_or_default(); // Ensure that unsigned transactions have both `data` and `input` populated to avoid // issues with eth_estimateGas and eth_sendTransaction requests. for tx in &mut txs { - if let Some(req) = tx.transaction.as_unsigned_mut() - && let Some(input) = req.input().cloned() - { - *req = req.clone().with_input_kind(input, TransactionInputKind::Both); + if let Some(req) = tx.transaction.as_unsigned_mut() { + req.input = + TransactionInput::maybe_both(std::mem::take(&mut req.input).into_input()); } } let rpc_data = RpcData::from_transactions(&txs); @@ -326,7 +314,6 @@ impl ExecutedState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data, execution_data: self.execution_data, execution_result: self.execution_result, @@ -339,8 +326,6 @@ impl ExecutedState { &self, known_contracts: &ContractsByArtifact, ) -> Result { - let chain_id = self.script_config.evm_opts.get_remote_chain_id().await; - let mut decoder = CallTraceDecoderBuilder::new() .with_labels(self.execution_result.labeled_addresses.clone()) .with_verbosity(self.script_config.evm_opts.verbosity) @@ -349,12 +334,12 @@ impl ExecutedState { &self.script_config.config, )?) .with_label_disabled(self.args.disable_labels) - .with_chain_id(chain_id.map(|c| c.id())) .build(); - let mut identifier = TraceIdentifiers::new() - .with_local(known_contracts) - .with_external(&self.script_config.config, chain_id)?; + let mut identifier = TraceIdentifiers::new().with_local(known_contracts).with_external( + &self.script_config.config, + self.script_config.evm_opts.get_remote_chain_id().await, + )?; for (_, trace) in &self.execution_result.traces { decoder.identify(trace, &mut identifier); @@ -378,10 +363,10 @@ impl ExecutedState { ty: "unknown".to_string(), }); - let label = if output.name.is_empty() { - index.to_string() + let label = if !output.name.is_empty() { + output.name.to_string() } else { - output.name.clone() + index.to_string() }; returns.insert( @@ -402,7 +387,7 @@ impl ExecutedState { } } -impl PreSimulationState { +impl PreSimulationState { pub async fn show_json(&self) -> Result<()> { let mut result = self.execution_result.clone(); @@ -476,10 +461,10 @@ impl PreSimulationState { ty: "unknown".to_string(), }); - let label = if output.name.is_empty() { - index.to_string() + let label = if !output.name.is_empty() { + output.name.to_string() } else { - output.name.clone() + index.to_string() }; sh_println!( "{label}: {internal_type} {value}", diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index d8d83913edb40..684bd42baae89 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -2,7 +2,6 @@ //! //! Smart contract scripting. -#![recursion_limit = "256"] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg))] @@ -12,11 +11,10 @@ extern crate foundry_common; #[macro_use] extern crate tracing; -use crate::{broadcast::BundledState, runner::ScriptRunner}; +use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; -use alloy_network::Network; use alloy_primitives::{ - Address, Bytes, Log, U256, hex, + Address, Bytes, Log, TxKind, U256, hex, map::{AddressHashMap, HashMap}, }; use alloy_signer::Signer; @@ -29,7 +27,7 @@ use forge_script_sequence::{AdditionalContract, NestedValue}; use forge_verify::{RetryArgs, VerifierArgs}; use foundry_cli::{ opts::{BuildOpts, EvmArgs, GlobalArgs}, - utils::{LoadConfig, parse_fee_token_address}, + utils::LoadConfig, }; use foundry_common::{ CONTRACT_MAX_SIZE, ContractsByArtifact, SELECTOR_LEN, @@ -46,11 +44,7 @@ use foundry_config::{ }; use foundry_evm::{ backend::Backend, - core::{ - Breakpoints, FoundryTransaction, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, - tempo::PATH_USD_ADDRESS, - }, + core::Breakpoints, executors::ExecutorBuilder, inspectors::{ CheatsConfig, @@ -59,7 +53,6 @@ use foundry_evm::{ opts::EvmOpts, traces::{TraceMode, Traces}, }; -use foundry_evm_networks::NetworkConfigs; use foundry_wallets::MultiWalletOpts; use serde::Serialize; use std::path::PathBuf; @@ -124,25 +117,12 @@ pub struct ScriptArgs { #[arg(long)] pub broadcast: bool, - /// Batch all broadcast transactions into a single Tempo batch transaction. - /// - /// When enabled, all vm.broadcast() calls are collected and sent as a single - /// atomic type 0x76 transaction instead of individual transactions. - /// This provides atomicity (all-or-nothing execution) and gas savings. - #[arg(long)] - pub batch: bool, - - /// Number of calls per Tempo batch transaction. + /// Batch size of transactions. /// - /// When `--batch` is enabled, splits the collected calls into multiple batch - /// transactions of at most this many calls each. - #[arg(long, requires = "batch", default_value = "100")] + /// 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, - /// Tempo fee token address for paying transaction fees. - #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] - pub fee_token: Option
, - /// Skips on-chain simulation. #[arg(long)] pub skip_simulation: bool, @@ -246,97 +226,34 @@ pub struct ScriptArgs { } impl ScriptArgs { - /// Loads config, resolves evm_opts (including network inference from fork), and returns them. - async fn resolved_evm_opts(&self) -> Result<(Config, EvmOpts)> { - let (config, mut evm_opts) = self.load_config_and_evm_opts()?; - - if self.fee_token.is_some() { - // If fee token is set directly select tempo - evm_opts.networks = NetworkConfigs::with_tempo(); - } else { - // Auto-detect network from fork chain ID when not explicitly configured. - evm_opts.infer_network_from_fork().await; - } - - Ok((config, evm_opts)) - } - - async fn preprocess( - self, - config: Config, - mut evm_opts: EvmOpts, - ) -> Result> { + pub async fn preprocess(self) -> Result { let script_wallets = Wallets::new(self.wallets.get_multi_wallet().await?, self.evm.sender); - let browser_wallet = self.wallets.browser_signer::().await?; + + let (config, mut evm_opts) = self.load_config_and_evm_opts()?; if let Some(sender) = self.maybe_load_private_key()? { evm_opts.sender = sender; } else if self.evm.sender.is_none() { - // If no sender was explicitly set via --sender, auto-detect it from available signers: - // use the sole signer's address if there's exactly one, or fall back to the browser - // wallet address if present. + // If no sender was explicitly set via --sender and there's exactly one signer + // (e.g. from --account or --keystore), use that signer's address as the sender. + // This makes --account behave consistently with --private-key. if let Ok(signers) = script_wallets.signers() && signers.len() == 1 { evm_opts.sender = signers[0]; - } else if let Some(signer) = browser_wallet.as_ref().map(|b| b.address()) { - evm_opts.sender = signer } } - let fee_token = if evm_opts.networks.is_tempo() && self.fee_token.is_none() { - Some(PATH_USD_ADDRESS) - } else { - self.fee_token - }; + let script_config = ScriptConfig::new(config, evm_opts).await?; - let script_config = ScriptConfig::new(config, evm_opts, self.batch, fee_token).await?; - Ok(PreprocessedState { args: self, script_config, script_wallets, browser_wallet }) + Ok(PreprocessedState { args: self, script_config, script_wallets }) } /// Executes the script - #[allow(clippy::large_stack_frames)] pub async fn run_script(self) -> Result<()> { trace!(target: "script", "executing script command"); - let (config, evm_opts) = self.resolved_evm_opts().await?; - - let is_tempo = evm_opts.networks.is_tempo(); - - if self.batch && !is_tempo { - eyre::bail!("--batch mode is only supported on Tempo networks"); - } - - if is_tempo { - let batch = self.batch; - let bundled = match self.prepare_bundled::(config, evm_opts).await? { - Some(bundled) => bundled, - None => return Ok(()), - }; - let bundled = bundled.wait_for_pending().await?; - let broadcasted = - if batch { bundled.broadcast_batch().await? } else { bundled.broadcast().await? }; - if broadcasted.args.verify { - broadcasted.verify().await?; - } - Ok(()) - } else if evm_opts.networks.is_optimism() { - self.run_generic_script::(config, evm_opts).await - } else { - self.run_generic_script::(config, evm_opts).await - } - } - - /// Prepares the bundled state (compile, simulate, bundle) and returns it - /// for broadcasting, or returns `None` if there's nothing to broadcast - /// (e.g., debug mode, no transactions, missing RPCs). - #[allow(clippy::large_stack_frames)] - async fn prepare_bundled( - self, - config: Config, - evm_opts: EvmOpts, - ) -> Result>> { - let state = self.preprocess::(config, evm_opts).await?; + let state = self.preprocess().await?; let create2_deployer = state.script_config.evm_opts.create2_deployer; let compiled = state.compile()?; @@ -358,8 +275,8 @@ impl ScriptArgs { if pre_simulation.args.debug { return match pre_simulation.args.dump.clone() { - Some(path) => pre_simulation.dump_debugger(&path).map(|_| None), - None => pre_simulation.run_debugger().map(|_| None), + Some(path) => pre_simulation.dump_debugger(&path), + None => pre_simulation.run_debugger(), }; } @@ -381,7 +298,7 @@ impl ScriptArgs { sh_warn!("No transactions to broadcast.")?; } - return Ok(None); + return Ok(()); } // Check if there are any missing RPCs and exit early to avoid hard error. @@ -390,7 +307,7 @@ impl ScriptArgs { sh_println!("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; } - return Ok(None); + return Ok(()); } pre_simulation.args.check_contract_sizes( @@ -414,7 +331,7 @@ impl ScriptArgs { "\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more." )?; } - return Ok(None); + return Ok(()); } // Exit early if something is wrong with verification options. @@ -422,19 +339,6 @@ impl ScriptArgs { bundled.verify_preflight_check()?; } - Ok(Some(bundled)) - } - - async fn run_generic_script( - self, - config: Config, - evm_opts: EvmOpts, - ) -> Result<()> { - let bundled = match self.prepare_bundled::(config, evm_opts).await? { - Some(bundled) => bundled, - None => return Ok(()), - }; - // Wait for pending txes and broadcast others. let broadcasted = bundled.wait_for_pending().await?.broadcast().await?; @@ -507,9 +411,9 @@ impl ScriptArgs { /// /// If `self.broadcast` is enabled, it asks confirmation of the user. Otherwise, it just warns /// the user. - fn check_contract_sizes( + fn check_contract_sizes( &self, - result: &ScriptResult, + result: &ScriptResult, known_contracts: &ContractsByArtifact, create2_deployer: Address, ) -> Result<()> { @@ -540,6 +444,7 @@ impl ScriptArgs { bytecodes.push((format!("Unknown{unknown_c}"), init_code, deployed_code)); unknown_c += 1; } + continue; } let mut prompt_user = false; @@ -559,13 +464,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 == create2_deployer { // Size of the salt prefix. offset = 32; } else { continue; } + } else if let Some(TxKind::Create) = to { + // Pass } // Find artifact with a deployment code same as the data. @@ -595,7 +502,7 @@ impl ScriptArgs { } /// We only broadcast transactions if --broadcast, --resume, or --verify was passed. - const fn should_broadcast(&self) -> bool { + fn should_broadcast(&self) -> bool { self.broadcast || self.resume || self.verify } } @@ -608,12 +515,12 @@ impl Provider for ScriptArgs { fn data(&self) -> Result, figment::Error> { let mut dict = Dict::default(); - if let Some(etherscan_api_key) = + if let Some(ref etherscan_api_key) = self.etherscan_api_key.as_ref().filter(|s| !s.trim().is_empty()) { dict.insert( "etherscan_api_key".to_string(), - figment::value::Value::from(etherscan_api_key.clone()), + figment::value::Value::from(etherscan_api_key.to_string()), ); } @@ -625,9 +532,8 @@ impl Provider for ScriptArgs { } } -#[derive(Serialize, Clone)] -#[serde(bound = "")] -pub struct ScriptResult { +#[derive(Default, Serialize, Clone)] +pub struct ScriptResult { pub success: bool, #[serde(rename = "raw_logs")] pub logs: Vec, @@ -635,30 +541,14 @@ pub struct ScriptResult { pub gas_used: u64, pub labeled_addresses: AddressHashMap, #[serde(skip)] - pub transactions: Option>, + pub transactions: Option, pub returned: Bytes, pub address: Option
, #[serde(skip)] pub breakpoints: Breakpoints, } -impl Default for ScriptResult { - fn default() -> Self { - Self { - success: Default::default(), - logs: Default::default(), - traces: Default::default(), - gas_used: Default::default(), - labeled_addresses: Default::default(), - transactions: Default::default(), - returned: Default::default(), - address: Default::default(), - breakpoints: Default::default(), - } - } -} - -impl ScriptResult { +impl ScriptResult { pub fn get_created_contracts( &self, known_contracts: &ContractsByArtifact, @@ -673,7 +563,7 @@ impl ScriptResult { .find_by_creation_code(init_code.as_ref()) .map(|artifact| artifact.0.name.clone()); return Some(AdditionalContract { - call_kind: node.trace.kind, + opcode: node.trace.kind, address: node.trace.address, contract_name, init_code, @@ -687,34 +577,24 @@ impl ScriptResult { } #[derive(Serialize)] -#[serde(bound = "")] -struct JsonResult<'a, N: Network> { +struct JsonResult<'a> { logs: Vec, returns: &'a HashMap, #[serde(flatten)] - result: &'a ScriptResult, + result: &'a ScriptResult, } #[derive(Clone, Debug)] -pub struct ScriptConfig { +pub struct ScriptConfig { pub config: Config, pub evm_opts: EvmOpts, pub sender_nonce: u64, /// Maps a rpc url to a backend - pub backends: HashMap>, - /// Whether to batch all broadcast transactions into a single Tempo batch transaction. - pub batch: bool, - /// Tempo fee token address for paying transaction fees. - pub fee_token: Option
, + pub backends: HashMap, } -impl ScriptConfig { - pub async fn new( - config: Config, - evm_opts: EvmOpts, - batch: bool, - fee_token: Option
, - ) -> Result { +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, evm_opts.fork_block_number).await? } else { @@ -722,7 +602,7 @@ impl ScriptConfig { 1 }; - Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default(), batch, fee_token }) + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default() }) } pub async fn update_sender(&mut self, sender: Address) -> Result<()> { @@ -736,7 +616,7 @@ impl ScriptConfig { Ok(()) } - async fn get_runner(&mut self) -> Result> { + async fn get_runner(&mut self) -> Result { self._get_runner(None, false).await } @@ -746,7 +626,7 @@ impl ScriptConfig { script_wallets: Wallets, debug: bool, target: ArtifactId, - ) -> Result> { + ) -> Result { self._get_runner(Some((known_contracts, script_wallets, target)), debug).await } @@ -754,16 +634,15 @@ impl ScriptConfig { &mut self, cheats_data: Option<(ContractsByArtifact, Wallets, ArtifactId)>, debug: bool, - ) -> Result> { + ) -> Result { trace!("preparing script runner"); - let (evm_env, mut tx_env, fork_block) = self.evm_opts.env::<_, _, TxEnvFor>().await?; + let env = self.evm_opts.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, evm_env.cfg_env.chain_id, fork_block); + let fork = self.evm_opts.get_fork(&self.config, env.evm_env.clone()); let backend = Backend::spawn(fork)?; self.backends.insert(fork_url.clone(), backend.clone()); backend @@ -777,7 +656,7 @@ impl ScriptConfig { }; // We need to enable tracing to decode contract names: local or external. - let mut builder = ExecutorBuilder::default() + let mut builder = ExecutorBuilder::new() .inspectors(|stack| { stack .logs(self.config.live_logs) @@ -798,7 +677,6 @@ impl ScriptConfig { self.evm_opts.clone(), Some(known_contracts), Some(target), - self.fee_token, ) .into(), ) @@ -807,18 +685,13 @@ impl ScriptConfig { }); } - // Propagate fee token to the transaction environment so that internal EVM calls - // (e.g. script deployment, setUp) use the correct fee token for Tempo networks. - tx_env.set_fee_token(self.fee_token); - - Ok(ScriptRunner::new(builder.build(evm_env, tx_env, db), self.evm_opts.clone())) + Ok(ScriptRunner::new(builder.build(env, db), self.evm_opts.clone())) } } #[cfg(test)] mod tests { use super::*; - use alloy_network::Ethereum; use foundry_config::{NamedChain, UnresolvedEnvVarError}; use std::fs; use tempfile::tempdir; @@ -872,7 +745,7 @@ mod tests { ScriptArgs::parse_from(["foundry-cli", "Contract.sol", "--disable-code-size-limit"]); assert!(args.disable_code_size_limit); - let result = ScriptResult::::default(); + let result = ScriptResult::default(); let contracts = ContractsByArtifact::default(); let create = Address::ZERO; assert!(args.check_contract_sizes(&result, &contracts, create).is_ok()); @@ -1097,19 +970,4 @@ mod tests { ]); assert!(args.with_gas_price.unwrap().is_zero()); } - - #[test] - fn test_priority_gas_price_cannot_exceed_gas_price() { - let args = ScriptArgs::parse_from([ - "foundry-cli", - "--broadcast", - "--with-gas-price", - "100", - "--priority-gas-price", - "200", - "Script", - ]); - // priority (200) > max_fee (100) — broadcast should reject this at runtime - assert!(args.priority_gas_price.unwrap() > args.with_gas_price.unwrap()); - } } diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index c066a026e0a0f..afa4e8ab03e4d 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,9 +1,10 @@ use alloy_chains::{Chain, NamedChain}; -use alloy_network::{Network, ReceiptResponse}; +use alloy_network::{Ethereum, ReceiptResponse}; use alloy_primitives::{TxHash, U256, utils::format_units}; use alloy_provider::{ PendingTransactionBuilder, PendingTransactionError, Provider, RootProvider, WatchTxError, }; +use alloy_rpc_types::TransactionReceipt; use eyre::{Result, eyre}; use forge_script_sequence::ScriptSequence; use foundry_common::{retry, retry::RetryError, shell}; @@ -19,25 +20,25 @@ pub struct PendingReceiptError { } /// Convenience enum for internal signalling of transaction status -pub enum TxStatus { +pub enum TxStatus { Dropped, - Success(R), - Revert(R), + Success(TransactionReceipt), + Revert(TransactionReceipt), } -impl From for TxStatus { - fn from(receipt: R) -> Self { - if receipt.status() { Self::Success(receipt) } else { Self::Revert(receipt) } +impl From for TxStatus { + fn from(receipt: TransactionReceipt) -> Self { + if !receipt.status() { Self::Revert(receipt) } else { Self::Success(receipt) } } } /// Checks the status of a txhash by first polling for a receipt, then for /// mempool inclusion. Returns the tx hash, and a status -pub async fn check_tx_status( - provider: &RootProvider, +pub async fn check_tx_status( + provider: &RootProvider, hash: TxHash, timeout: u64, -) -> (TxHash, Result, eyre::Report>) { +) -> (TxHash, Result) { let result = retry::Retry::new_no_delay(3) .run_async_until_break(|| async { match PendingTransactionBuilder::new(provider.clone(), hash) @@ -88,10 +89,10 @@ pub async fn check_tx_status( } /// Prints parts of the receipt to stdout -pub fn format_receipt( +pub fn format_receipt( chain: Chain, - receipt: &N::ReceiptResponse, - sequence: Option<&ScriptSequence>, + receipt: &TransactionReceipt, + sequence: Option<&ScriptSequence>, ) -> String { let gas_used = receipt.gas_used(); let gas_price = receipt.effective_gas_price(); @@ -181,9 +182,7 @@ pub fn format_receipt( #[cfg(test)] mod tests { use super::*; - use alloy_network::Ethereum; use alloy_primitives::B256; - use alloy_rpc_types::TransactionReceipt; use std::collections::VecDeque; fn mock_receipt(tx_hash: B256, success: bool) -> TransactionReceipt { @@ -199,11 +198,7 @@ mod tests { .unwrap() } - fn mock_sequence( - tx_hash: B256, - contract: Option<&str>, - func: Option<&str>, - ) -> ScriptSequence { + fn mock_sequence(tx_hash: B256, contract: Option<&str>, func: Option<&str>) -> ScriptSequence { let tx = serde_json::from_value(serde_json::json!({ "hash": tx_hash, "transactionType": "CALL", "contractName": contract, "contractAddress": null, "function": func, @@ -233,7 +228,7 @@ mod tests { #[test] fn format_receipt_without_sequence_omits_metadata() { let hash = B256::repeat_byte(0x42); - let out = format_receipt::(Chain::mainnet(), &mock_receipt(hash, true), None); + let out = format_receipt(Chain::mainnet(), &mock_receipt(hash, true), None); assert!(!out.contains("Contract:")); assert!(!out.contains("Function:")); diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index de80b1bf98c47..4b8b3bbeba3e9 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -1,19 +1,13 @@ use super::{ScriptConfig, ScriptResult}; use crate::build::ScriptPredeployLibraries; use alloy_eips::eip7702::SignedAuthorization; -use alloy_evm::revm::context::Transaction; -use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_rpc_types::TransactionRequest; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; -use foundry_common::{FoundryTransactionBuilder, TransactionMaybeSigned}; use foundry_config::Config; use foundry_evm::{ constants::CALLER, - core::{ - FoundryTransaction, - evm::{FoundryEvmNetwork, TransactionRequestFor}, - }, executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, opts::EvmOpts, revm::interpreter::{InstructionResult, return_ok}, @@ -23,13 +17,13 @@ use std::collections::VecDeque; /// Drives script execution #[derive(Debug)] -pub struct ScriptRunner { - pub executor: Executor, +pub struct ScriptRunner { + pub executor: Executor, pub evm_opts: EvmOpts, } -impl ScriptRunner { - pub const fn new(executor: Executor, evm_opts: EvmOpts) -> Self { +impl ScriptRunner { + pub fn new(executor: Executor, evm_opts: EvmOpts) -> Self { Self { executor, evm_opts } } @@ -39,9 +33,9 @@ impl ScriptRunner { libraries: &ScriptPredeployLibraries, code: Bytes, setup: bool, - script_config: &ScriptConfig, + script_config: &ScriptConfig, is_broadcast: bool, - ) -> Result<(Address, ScriptResult)> { + ) -> Result<(Address, ScriptResult)> { trace!(target: "script", "executing setUP()"); if !is_broadcast { @@ -79,18 +73,15 @@ impl ScriptRunner { traces.push((TraceKind::Deployment, deploy_traces)); } - let mut tx_req = TransactionRequestFor::::default() - .with_from(self.evm_opts.sender) - .with_input(code.clone()) - .with_nonce(sender_nonce + library_transactions.len() as u64); - - if let Some(fee_token) = script_config.fee_token { - tx_req.set_fee_token(fee_token); - } - library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), - transaction: TransactionMaybeSigned::new(tx_req), + transaction: TransactionRequest { + from: Some(self.evm_opts.sender), + input: code.clone().into(), + nonce: Some(sender_nonce + library_transactions.len() as u64), + ..Default::default() + } + .into(), }) }), ScriptPredeployLibraries::Create2(libraries, salt) => { @@ -116,19 +107,16 @@ impl ScriptRunner { traces.push((TraceKind::Deployment, deploy_traces)); } - let mut tx_req = TransactionRequestFor::::default() - .with_from(self.evm_opts.sender) - .with_input(calldata) - .with_nonce(sender_nonce + library_transactions.len() as u64) - .with_to(create2_deployer); - - if let Some(fee_token) = script_config.fee_token { - tx_req.set_fee_token(fee_token); - } - library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), - transaction: TransactionMaybeSigned::new(tx_req), + transaction: TransactionRequest { + from: Some(self.evm_opts.sender), + input: calldata.into(), + nonce: Some(sender_nonce + library_transactions.len() as u64), + to: Some(TxKind::Call(create2_deployer)), + ..Default::default() + } + .into(), }); } @@ -179,7 +167,10 @@ impl ScriptRunner { traces.extend(constructor_traces.map(|traces| (TraceKind::Deployment, traces))); // Optionally call the `setUp` function - let (success, gas_used, labeled_addresses, transactions) = if setup { + let (success, gas_used, labeled_addresses, transactions) = if !setup { + self.executor.backend_mut().set_test_contract(address); + (true, 0, Default::default(), Some(library_transactions)) + } else { match self.executor.setup(Some(self.evm_opts.sender), address, None) { Ok(RawCallResult { reverted, @@ -220,9 +211,6 @@ impl ScriptRunner { } Err(e) => return Err(e.into()), } - } else { - self.executor.backend_mut().set_test_contract(address); - (true, 0, Default::default(), Some(library_transactions)) }; Ok(( @@ -242,11 +230,7 @@ impl ScriptRunner { } /// Executes the method that will collect all broadcastable transactions. - pub fn script( - &mut self, - address: Address, - calldata: Bytes, - ) -> Result> { + pub fn script(&mut self, address: Address, calldata: Bytes) -> Result { self.call(self.evm_opts.sender, address, calldata, U256::ZERO, None, false) } @@ -258,7 +242,7 @@ impl ScriptRunner { calldata: Option, value: Option, authorization_list: Option>, - ) -> Result> { + ) -> Result { if let Some(to) = to { self.call( from, @@ -314,7 +298,7 @@ impl ScriptRunner { value: U256, authorization_list: Option>, commit: bool, - ) -> Result> { + ) -> Result { let mut res = if let Some(authorization_list) = &authorization_list { self.executor.call_raw_with_authorization( from, @@ -378,7 +362,7 @@ impl ScriptRunner { /// it might be problematic when using `ffi`. fn search_optimal_gas_usage( &mut self, - res: &RawCallResult, + res: &RawCallResult, from: Address, to: Address, calldata: &Bytes, @@ -387,21 +371,19 @@ impl ScriptRunner { let mut gas_used = res.gas_used; if matches!(res.exit_reason, Some(return_ok!())) { // Store the current gas limit and reset it later. - let init_gas_limit = self.executor.tx_env().gas_limit(); + let init_gas_limit = self.executor.env().tx.gas_limit; let mut highest_gas_limit = gas_used * 3; let mut lowest_gas_limit = gas_used; let mut last_highest_gas_limit = highest_gas_limit; while (highest_gas_limit - lowest_gas_limit) > 1 { let mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; - self.executor.tx_env_mut().set_gas_limit(mid_gas_limit); + self.executor.env_mut().tx.gas_limit = mid_gas_limit; let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?; match res.exit_reason { - Some( - InstructionResult::Revert - | InstructionResult::OutOfGas - | InstructionResult::OutOfFunds, - ) => { + Some(InstructionResult::Revert) + | Some(InstructionResult::OutOfGas) + | Some(InstructionResult::OutOfFunds) => { lowest_gas_limit = mid_gas_limit; } _ => { @@ -422,7 +404,7 @@ impl ScriptRunner { } } // Reset gas limit in the executor. - self.executor.tx_env_mut().set_gas_limit(init_gas_limit); + self.executor.env_mut().tx.gas_limit = init_gas_limit; } Ok(gas_used) } diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 7b9f21ae71f1e..c0a7e9deddca7 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -1,32 +1,23 @@ use crate::multi_sequence::MultiChainSequence; -use alloy_network::Network; use eyre::Result; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cli::utils::Git; -use foundry_common::{FoundryTransactionBuilder, fmt::UIfmt}; +use foundry_common::fmt::UIfmt; use foundry_compilers::ArtifactId; use foundry_config::Config; -use serde::{Deserialize, Serialize}; use std::{ fmt::{Error, Write}, path::Path, }; /// Format transaction details for display -fn format_transaction( - index: usize, - tx: &TransactionWithMetadata, -) -> Result -where - N::TxEnvelope: UIfmt, - N::TransactionRequest: FoundryTransactionBuilder, -{ +fn format_transaction(index: usize, tx: &TransactionWithMetadata) -> Result { let mut output = String::new(); writeln!(output, "### Transaction {index} ###")?; writeln!(output, "{}", tx.tx().pretty())?; // Show contract name and address if available - if !tx.call_kind.is_any_create() + if !tx.opcode.is_any_create() && let (Some(name), Some(addr)) = (&tx.contract_name, &tx.contract_address) { writeln!(output, "contract: {name}({addr})")?; @@ -54,20 +45,12 @@ pub fn get_commit_hash(root: &Path) -> Option { Git::new(root).commit_hash(true, "HEAD").ok() } -pub enum ScriptSequenceKind -where - N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, - N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, -{ - Single(ScriptSequence), - Multi(MultiChainSequence), +pub enum ScriptSequenceKind { + Single(ScriptSequence), + Multi(MultiChainSequence), } -impl ScriptSequenceKind -where - N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, - N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, -{ +impl ScriptSequenceKind { pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { match self { Self::Single(sequence) => sequence.save(silent, save_ts), @@ -75,14 +58,14 @@ where } } - pub fn sequences(&self) -> &[ScriptSequence] { + pub fn sequences(&self) -> &[ScriptSequence] { match self { Self::Single(sequence) => std::slice::from_ref(sequence), Self::Multi(sequence) => &sequence.deployments, } } - pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] { + pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] { match self { Self::Single(sequence) => std::slice::from_mut(sequence), Self::Multi(sequence) => &mut sequence.deployments, @@ -97,28 +80,19 @@ where ) -> Result<()> { match self { Self::Single(sequence) => { - sequence.paths = Some(ScriptSequence::::get_paths( - config, - sig, - target, - sequence.chain, - false, - )?); + sequence.paths = + Some(ScriptSequence::get_paths(config, sig, target, sequence.chain, false)?); } Self::Multi(sequence) => { (sequence.path, sequence.sensitive_path) = - MultiChainSequence::::get_paths(config, sig, target, false)?; + MultiChainSequence::get_paths(config, sig, target, false)?; } }; Ok(()) } - pub fn show_transactions(&self) -> Result<()> - where - N::TxEnvelope: UIfmt, - N::TransactionRequest: FoundryTransactionBuilder, - { + pub fn show_transactions(&self) -> Result<()> { for sequence in self.sequences() { if !sequence.transactions.is_empty() { sh_println!("\nChain {}\n", sequence.chain)?; @@ -133,11 +107,7 @@ where } } -impl Drop for ScriptSequenceKind -where - N::TxEnvelope: for<'d> Deserialize<'d> + Serialize, - N::TransactionRequest: for<'d> Deserialize<'d> + Serialize, -{ +impl Drop for ScriptSequenceKind { fn drop(&mut self) { if let Err(err) = self.save(false, true) { error!(?err, "could not save deployment sequence"); diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 5c5155aa1678b..05834ed8bb036 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,21 +9,15 @@ use crate::{ execute::{ExecutionArtifacts, ExecutionData}, sequence::get_commit_hash, }; -use alloy_chains::NamedChain; -use alloy_evm::revm::context::Block; use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, U256, map::HashMap, utils::format_units}; +use alloy_primitives::{Address, TxKind, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{ContractData, shell}; -use foundry_evm::{ - core::{FoundryBlock, evm::FoundryEvmNetwork}, - traces::{decode_trace_arena, render_trace_arena}, -}; -use foundry_wallets::wallet_browser::signer::BrowserSigner; +use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ @@ -37,24 +31,23 @@ use std::{ /// /// Can be either converted directly to [BundledState] or driven to it through /// [FilledTransactionsState]. -pub struct PreSimulationState { +pub struct PreSimulationState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, - pub execution_result: ScriptResult, + pub execution_result: ScriptResult, pub execution_artifacts: ExecutionArtifacts, } -impl PreSimulationState { +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> { + pub async fn fill_metadata(self) -> Result { let address_to_abi = self.build_address_to_abi_map(); let mut transactions = self @@ -71,7 +64,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if to.is_some() { + if let Some(TxKind::Call(_)) = to { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, @@ -95,7 +88,6 @@ impl PreSimulationState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data, execution_artifacts: self.execution_artifacts, transactions, @@ -108,8 +100,8 @@ impl PreSimulationState { /// Collects gas usage and metadata for each transaction. pub async fn simulate_and_fill( &self, - transactions: VecDeque>, - ) -> Result>> { + transactions: VecDeque, + ) -> Result> { trace!(target: "script", "executing onchain simulation"); let runners = Arc::new( @@ -129,7 +121,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = tx.to(); + let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( tx.from() @@ -147,8 +139,7 @@ impl PreSimulationState { // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { - let block_number = runner.executor.evm_env().block_env.number() + U256::from(1); - runner.executor.evm_env_mut().block_env.set_number(block_number); + runner.executor.env_mut().evm_env.block_env.number += U256::from(1); } let is_noop_tx = if let Some(to) = to { @@ -235,12 +226,12 @@ 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::is_json() { let n = rpcs.len(); - let s = if n == 1 { "" } else { "s" }; + let s = if n != 1 { "s" } else { "" }; sh_println!("\n## Setting up {n} EVM{s}.")?; } @@ -257,23 +248,22 @@ impl PreSimulationState { /// 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 struct FilledTransactionsState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, - pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, - pub transactions: VecDeque>, + pub transactions: VecDeque, } -impl FilledTransactionsState { +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(mut self) -> Result> { + pub async fn bundle(mut 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() { @@ -284,7 +274,7 @@ impl FilledTransactionsState { // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); - let mut manager = ProvidersManager::::default(); + 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 @@ -292,7 +282,7 @@ impl FilledTransactionsState { let mut txes_iter = mem::take(&mut self.transactions).into_iter().peekable(); while let Some(mut tx) = txes_iter.next() { - let tx_rpc = tx.rpc.clone(); + let tx_rpc = tx.rpc.to_owned(); let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; if let Some(tx) = tx.tx_mut().as_unsigned_mut() { @@ -307,7 +297,7 @@ impl FilledTransactionsState { // only estimate gas for unsigned transactions if let Some(tx) = tx.as_unsigned_mut() { trace!("estimating with different gas calculation"); - let gas = tx.gas_limit().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. // @@ -361,12 +351,6 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); - // Get the native token symbol for the chain using NamedChain - let token_symbol = NamedChain::try_from(provider_info.chain) - .unwrap_or_default() - .native_currency_symbol() - .unwrap_or("ETH"); - // 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 { @@ -384,7 +368,15 @@ impl FilledTransactionsState { .unwrap_or_else(|_| "[Could not calculate]".to_string()); let estimated_amount = estimated_amount_raw.trim_end_matches('0'); - if shell::is_json() { + if !shell::is_json() { + sh_println!("\n==========================")?; + sh_println!("\nChain {}", provider_info.chain)?; + + sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; + sh_println!("\nEstimated total gas used for script: {total_gas}")?; + sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; + sh_println!("\n==========================")?; + } else { sh_println!( "{}", serde_json::json!({ @@ -392,17 +384,8 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, - "token_symbol": token_symbol, }) )?; - } else { - sh_println!("\n==========================")?; - sh_println!("\nChain {}", provider_info.chain)?; - - sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; - sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; - sh_println!("\n==========================")?; } } } @@ -423,7 +406,6 @@ impl FilledTransactionsState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, - browser_wallet: self.browser_wallet, build_data: self.build_data, sequence, }) @@ -434,14 +416,14 @@ impl FilledTransactionsState { &self, multi: bool, chain: u64, - transactions: VecDeque>, - ) -> Result> { + 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( + Some(ScriptSequence::get_paths( &self.script_config.config, &self.args.sig, &self.build_data.build_data.target, diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index ef10a1ce94082..1bfac6d9564bc 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -3,29 +3,28 @@ use crate::{ build::LinkedBuildData, sequence::{ScriptSequenceKind, get_commit_hash}, }; -use alloy_network::{Network, ReceiptResponse}; +use alloy_network::ReceiptResponse; use alloy_primitives::{Address, hex}; use eyre::{Result, eyre}; use forge_script_sequence::{AdditionalContract, ScriptSequence}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs, provider::VerificationProviderType}; use foundry_cli::opts::{EtherscanOpts, ProjectPathOpts}; -use foundry_common::{ContractsByArtifact, FoundryReceiptResponse}; +use foundry_common::ContractsByArtifact; use foundry_compilers::{Project, artifacts::EvmVersion, info::ContractInfo}; use foundry_config::{Chain, Config}; -use foundry_evm::core::evm::FoundryEvmNetwork; 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 struct BroadcastedState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub build_data: LinkedBuildData, - pub sequence: ScriptSequenceKind, + pub sequence: ScriptSequenceKind, } -impl BroadcastedState { +impl BroadcastedState { pub async fn verify(self) -> Result<()> { let Self { args, script_config, build_data, mut sequence, .. } = self; @@ -38,7 +37,7 @@ impl BroadcastedState { ); for sequence in sequence.sequences_mut() { - verify_contracts::(sequence, &script_config.config, verify.clone()).await?; + verify_contracts(sequence, &script_config.config, verify.clone()).await?; } Ok(()) @@ -78,7 +77,7 @@ impl VerifyBundle { cache_path: Some(project.paths.cache.clone()), lib_paths: project.paths.libraries.clone(), hardhat: config.profile == Config::HARDHAT_PROFILE, - config_path: config_path.exists().then_some(config_path), + config_path: if config_path.exists() { Some(config_path) } else { None }, }; let via_ir = config.via_ir; @@ -166,7 +165,7 @@ impl VerifyBundle { evm_version: Some(evm_version), show_standard_json_input: false, guess_constructor_args: false, - compilation_profile: Some(artifact.profile.clone()), + compilation_profile: Some(artifact.profile.to_string()), language: None, creation_transaction_hash: None, }; @@ -180,8 +179,8 @@ impl VerifyBundle { /// Given the broadcast log, it matches transactions with receipts, and tries to verify any /// created contract on etherscan. -async fn verify_contracts( - sequence: &mut ScriptSequence, +async fn verify_contracts( + sequence: &mut ScriptSequence, config: &Config, mut verify: VerifyBundle, ) -> Result<()> { @@ -201,14 +200,12 @@ async fn verify_contracts( for (receipt, tx) in sequence.receipts.iter_mut().zip(sequence.transactions.iter()) { // create2 hash offset - let offset = if tx.is_create2() - && let Some(contract_address) = tx.contract_address - { - receipt.set_contract_address(contract_address); - 32 - } else { - 0 - }; + let mut offset = 0; + + if tx.is_create2() { + receipt.contract_address = tx.contract_address; + offset = 32; + } // Verify contract created directly from the transaction if let (Some(address), Some(data)) = (receipt.contract_address(), tx.tx().input()) { @@ -269,8 +266,8 @@ async fn verify_contracts( Ok(()) } -fn check_unverified( - sequence: &ScriptSequence, +fn check_unverified( + sequence: &ScriptSequence, unverifiable_contracts: Vec
, verify: VerifyBundle, ) { diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 06f83886d0fd2..c1a6cb53bdbff 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,9 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); - fs::copy(file, to_dir.join(name))?; + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } else { + continue; + } + fs::copy(&file, to_dir.join(name))?; } Ok(()) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index d22e12736d832..a6df9cda5e976 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -4,7 +4,7 @@ use std::{ env, fs::{self, File}, io::{Read, Seek, Write}, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf}, process::Command, sync::LazyLock, }; @@ -231,15 +231,59 @@ pub fn read_string(path: impl AsRef) -> String { /// copied to temporary test workspaces. pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { fs::create_dir_all(dst)?; - copy_dir_filtered_inner(src, dst, true) + // Canonicalize the source once and treat it as the base directory for all traversal. + let base = src.canonicalize()?; + // Canonicalize the destination once and treat it as the base directory for all writes. + let dst_base = dst.canonicalize()?; + copy_dir_filtered_inner(src, dst, &base, &dst_base, true) } -fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Result<()> { +fn copy_dir_filtered_inner( + src: &Path, + dst: &Path, + base_src: &Path, + base_dst: &Path, + is_root: bool, +) -> std::io::Result<()> { for entry in fs::read_dir(src)? { let entry = entry?; let ty = entry.file_type()?; let src_path = entry.path(); - let dst_path = dst.join(entry.file_name()); + // Ensure that any path we operate on stays within the original source base directory. + let canonical_src_path = src_path.canonicalize()?; + if !canonical_src_path.starts_with(base_src) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + let relative_src_path = canonical_src_path.strip_prefix(base_src).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "failed to derive relative path within source base directory", + ) + })?; + for component in relative_src_path.components() { + match component { + Component::Normal(name) => { + let name = name.to_string_lossy(); + if name.contains("..") || name.contains('/') || name.contains('\\') { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "invalid path component in source entry", + )); + } + } + Component::CurDir => {} + Component::ParentDir | Component::RootDir | Component::Prefix(_) => { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + } + } + let dst_path = base_dst.join(relative_src_path); if ty.is_dir() { // Skip build artifact directories at the root level @@ -249,10 +293,58 @@ fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Re { continue; } + // Ensure that the destination directory stays within the allowed destination base. + let canonical_dst_dir = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // Directory does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_dir.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } fs::create_dir_all(&dst_path)?; - copy_dir_filtered_inner(&src_path, &dst_path, false)?; + copy_dir_filtered_inner(&src_path, &dst_path, base_src, base_dst, false)?; } else { - fs::copy(&src_path, &dst_path)?; + // Ensure that the destination file path stays within the allowed destination base. + let canonical_dst_path = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // File does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_path.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + fs::copy(&canonical_src_path, &canonical_dst_path)?; } } Ok(()) diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 01c1f6b829195..773430c4bf785 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -223,6 +223,10 @@ impl VerificationProviderType { // 4. If no `--verifier` is specified but `ETHERSCAN_API_KEY` is set, default to Etherscan. if has_key { + sh_eprintln!( + "ETHERSCAN_API_KEY is set, defaulting to Etherscan verifier. \ + Unset it or pass `--verifier sourcify` (or another provider) to override." + )?; return Ok(Box::::default()); } diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 9d4cacc97591a..c6344dd9d2731 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -1,7 +1,7 @@ use crate::{bytecode::VerifyBytecodeArgs, types::VerificationType}; +use alloy_consensus::BlockHeader; use alloy_dyn_abi::DynSolValue; -use alloy_evm::EvmEnv; -use alloy_primitives::{Address, Bytes, TxKind}; +use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_provider::{ Provider, network::{AnyNetwork, AnyRpcBlock}, @@ -18,16 +18,13 @@ use foundry_common::{abi::encode_args, compile::ProjectCompiler, ignore_metadata use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}; use foundry_config::Config; use foundry_evm::{ - constants::DEFAULT_CREATE2_DEPLOYER, - core::{decode::RevertDecoder, evm::EthEvmNetwork}, - executors::TracingExecutor, - opts::EvmOpts, - traces::TraceMode, - utils::{apply_chain_and_block_specific_env_changes, block_env_from_header}, + Env, constants::DEFAULT_CREATE2_DEPLOYER, core::decode::RevertDecoder, + executors::TracingExecutor, opts::EvmOpts, traces::TraceMode, + utils::apply_chain_and_block_specific_env_changes, }; use foundry_evm_networks::NetworkConfigs; use reqwest::Url; -use revm::{bytecode::Bytecode, context::TxEnv, database::Database, primitives::hardfork::SpecId}; +use revm::{bytecode::Bytecode, database::Database, primitives::hardfork::SpecId}; use semver::{BuildMetadata, Version}; use serde::{Deserialize, Serialize}; use yansi::Paint; @@ -43,12 +40,12 @@ pub enum BytecodeType { impl BytecodeType { /// Check if the bytecode type is creation - pub const fn is_creation(&self) -> bool { + pub fn is_creation(&self) -> bool { matches!(self, Self::Creation) } /// Check if the bytecode type is runtime - pub const fn is_runtime(&self) -> bool { + pub fn is_runtime(&self) -> bool { matches!(self, Self::Runtime) } } @@ -107,15 +104,15 @@ pub fn print_result( config: &Config, ) { if let Some(res) = res { - if shell::is_json() { - let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; - json_results.push(json_res); - } else { + if !shell::is_json() { let _ = sh_println!( "{} with status {}", format!("{bytecode_type:?} code matched").green().bold(), res.green().bold() ); + } else { + let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; + json_results.push(json_res); } } else if !shell::is_json() { let _ = sh_err!( @@ -268,16 +265,16 @@ pub async fn get_tracing_executor( fork_blk_num: u64, evm_version: EvmVersion, evm_opts: EvmOpts, -) -> Result<(EvmEnv, TxEnv, TracingExecutor)> { +) -> Result<(Env, TracingExecutor)> { fork_config.fork_block_number = Some(fork_blk_num); fork_config.evm_version = evm_version; let create2_deployer = evm_opts.create2_deployer; - let (evm_env, tx_env, fork, _chain, networks) = - TracingExecutor::::get_fork_material(fork_config, evm_opts).await?; + let (env, fork, _chain, networks) = + TracingExecutor::get_fork_material(fork_config, evm_opts).await?; - let executor = TracingExecutor::::new( - (evm_env.clone(), tx_env.clone()), + let executor = TracingExecutor::new( + env.clone(), fork, Some(fork_config.evm_version), TraceMode::Call, @@ -286,25 +283,31 @@ pub async fn get_tracing_executor( None, )?; - Ok((evm_env, tx_env, executor)) + Ok((env, executor)) } -pub fn configure_env_block(evm_env: &mut EvmEnv, block: &AnyRpcBlock, config: NetworkConfigs) { - let number = evm_env.block_env.number; - evm_env.block_env = block_env_from_header(&block.header); - evm_env.block_env.number = number; - apply_chain_and_block_specific_env_changes::(evm_env, block, config); +pub fn configure_env_block(env: &mut Env, block: &AnyRpcBlock, config: NetworkConfigs) { + env.evm_env.block_env.timestamp = U256::from(block.header.timestamp()); + env.evm_env.block_env.beneficiary = block.header.beneficiary(); + env.evm_env.block_env.difficulty = block.header.difficulty(); + env.evm_env.block_env.prevrandao = Some(block.header.mix_hash().unwrap_or_default()); + env.evm_env.block_env.basefee = block.header.base_fee_per_gas().unwrap_or_default(); + env.evm_env.block_env.gas_limit = block.header.gas_limit(); + apply_chain_and_block_specific_env_changes::(&mut env.evm_env, block, config); } pub fn deploy_contract( - executor: &mut TracingExecutor, - evm_env: &EvmEnv, - tx_env: &TxEnv, + executor: &mut TracingExecutor, + env: &Env, spec_id: SpecId, to: Option, ) -> Result { - let mut evm_env = evm_env.clone(); - evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id); + let env = Env::new_with_spec_id( + env.evm_env.cfg_env.clone(), + env.evm_env.block_env.clone(), + env.tx.clone(), + spec_id, + ); if to.is_some_and(|to| to.is_call()) { let TxKind::Call(to) = to.unwrap() else { unreachable!() }; @@ -313,7 +316,7 @@ pub fn deploy_contract( "Transaction `to` address is not the default create2 deployer i.e the tx is not a contract creation tx." ); } - let result = executor.transact_with_env(evm_env, tx_env.clone())?; + let result = executor.transact_with_env(env)?; trace!(transact_result = ?result.exit_reason); @@ -343,14 +346,14 @@ pub fn deploy_contract( Ok(Address::from_slice(&result.result)) } else { - let deploy_result = executor.deploy_with_env(evm_env, tx_env.clone(), None)?; + let deploy_result = executor.deploy_with_env(env, None)?; trace!(deploy_result = ?deploy_result.raw.exit_reason); Ok(deploy_result.address) } } pub async fn get_runtime_codes( - executor: &mut TracingExecutor, + executor: &mut TracingExecutor, provider: &impl Provider, address: Address, fork_address: Address, @@ -386,6 +389,35 @@ pub fn is_host_only(url: &Url) -> bool { matches!(url.path(), "/" | "") } +/// Wraps a failed verification error with guidance when `--verifier-url` looks misconfigured for +/// the Etherscan provider. Returns `err` untouched when no hint applies. +/// +/// The hint only fires when the Etherscan verifier is active: it requires an API endpoint +/// (typically `/api`). Sourcify, Blockscout, etc. accept host-only URLs, so we leave their +/// errors alone. +pub fn wrap_verifier_url_error( + err: eyre::Error, + verifier_url: Option<&str>, + using_etherscan: bool, +) -> eyre::Error { + let Some(verifier_url) = verifier_url else { return err }; + let url = match Url::parse(verifier_url) { + Ok(url) => url, + Err(url_err) => { + return err.wrap_err(format!("Invalid URL {verifier_url} provided: {url_err}")); + } + }; + if is_host_only(&url) && using_etherscan { + return err.wrap_err(format!( + "Verifier `etherscan` requires an API endpoint, but `--verifier-url` is host-only: `{verifier_url}`.\n\ + Fixes (pick one):\n\ + - Append the API path, e.g. `--verifier-url {verifier_url}/api`\n\ + - Switch verifier, e.g. `--verifier sourcify` (works with host-only URLs)" + )); + } + err +} + /// Given any solc [Version] return a [Version] with build metadata /// /// # Example @@ -397,10 +429,10 @@ pub fn is_host_only(url: &Url) -> bool { /// assert_ne!(version.build, BuildMetadata::EMPTY); /// ``` pub async fn ensure_solc_build_metadata(version: Version) -> Result { - if version.build == BuildMetadata::EMPTY { - Ok(lookup_compiler_version(&version).await?) - } else { + if version.build != BuildMetadata::EMPTY { Ok(version) + } else { + Ok(lookup_compiler_version(&version).await?) } } @@ -414,4 +446,38 @@ mod tests { assert!(is_host_only(&Url::parse("https://blockscout.net/").unwrap())); assert!(is_host_only(&Url::parse("https://blockscout.net").unwrap())); } + + #[test] + fn wrap_verifier_url_error_passes_through_when_no_url() { + let err = eyre::eyre!("upstream failure"); + let wrapped = wrap_verifier_url_error(err, None, true); + assert_eq!(wrapped.to_string(), "upstream failure"); + } + + #[test] + fn wrap_verifier_url_error_adds_hint_for_host_only_etherscan_url() { + let err = eyre::eyre!("upstream failure"); + let wrapped = wrap_verifier_url_error(err, Some("https://contracts.tempo.xyz"), true); + let msg = format!("{wrapped:#}"); + assert!(msg.contains("host-only"), "message: {msg}"); + assert!(msg.contains("--verifier-url https://contracts.tempo.xyz/api"), "message: {msg}"); + assert!(msg.contains("--verifier sourcify"), "message: {msg}"); + } + + /// Sourcify and other non-etherscan verifiers accept host-only URLs; we must not emit the + /// hint for them, otherwise we would mislead the user into editing a correct URL. + #[test] + fn wrap_verifier_url_error_does_not_hint_for_non_etherscan_provider() { + let err = eyre::eyre!("upstream failure"); + let wrapped = wrap_verifier_url_error(err, Some("https://contracts.tempo.xyz"), false); + assert_eq!(wrapped.to_string(), "upstream failure"); + } + + #[test] + fn wrap_verifier_url_error_reports_invalid_url() { + let err = eyre::eyre!("upstream failure"); + let wrapped = wrap_verifier_url_error(err, Some("not a url"), true); + let msg = format!("{wrapped:#}"); + assert!(msg.contains("Invalid URL"), "message: {msg}"); + } } diff --git a/crates/verify/src/verify.rs b/crates/verify/src/verify.rs index cab8462ebb25e..27f7975d283c2 100644 --- a/crates/verify/src/verify.rs +++ b/crates/verify/src/verify.rs @@ -4,7 +4,7 @@ use crate::{ RetryArgs, etherscan::EtherscanVerificationProvider, provider::{VerificationContext, VerificationProvider, VerificationProviderType}, - utils::is_host_only, + utils::wrap_verifier_url_error, }; use alloy_primitives::{Address, TxHash, map::HashSet}; use alloy_provider::Provider; @@ -20,7 +20,6 @@ use foundry_config::{ Chain, Config, SolcReq, figment, impl_figment_convert, impl_figment_convert_cast, }; use itertools::Itertools; -use reqwest::Url; use semver::BuildMetadata; use std::path::PathBuf; @@ -282,25 +281,28 @@ impl VerifyArgs { { sh_println!("Constructor args: {args}")? } - self.verifier.verifier.client(self.etherscan.key().as_deref(), self.etherscan.chain, self.verifier.verifier_url.is_some())?.verify(self, context).await.map_err(|err| { - if let Some(verifier_url) = verifier_url { - match Url::parse(&verifier_url) { - Ok(url) if is_host_only(&url) => { - return err.wrap_err(format!( - "Provided URL `{verifier_url}` is host only.\n Did you mean to use the API endpoint`{verifier_url}/api` ?" - )) - } - Err(url_err) => { - return err.wrap_err(format!( - "Invalid URL {verifier_url} provided: {url_err}" - )) - } - _ => {} - } - } - - err - }) + // `client()` picks Etherscan when `--verifier etherscan` is passed, or when + // `ETHERSCAN_API_KEY` is set and no other provider was explicitly chosen. This mirrors + // that selection closely enough to decide whether the host-only URL hint applies. + let etherscan_key = self.etherscan.key(); + let using_etherscan = self.verifier.verifier.is_etherscan() + || (etherscan_key.as_deref().is_some_and(|k| !k.is_empty()) + && !matches!( + self.verifier.verifier, + VerificationProviderType::Blockscout + | VerificationProviderType::Oklink + | VerificationProviderType::Custom + )); + self.verifier + .verifier + .client( + etherscan_key.as_deref(), + self.etherscan.chain, + self.verifier.verifier_url.is_some(), + )? + .verify(self, context) + .await + .map_err(|err| wrap_verifier_url_error(err, verifier_url.as_deref(), using_etherscan)) } /// Returns the configured verification provider diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml deleted file mode 100644 index 0f30346c72039..0000000000000 --- a/crates/wallets/Cargo.toml +++ /dev/null @@ -1,69 +0,0 @@ -[package] -name = "foundry-wallets" - -version.workspace = true -edition.workspace = true -rust-version.workspace = true -authors.workspace = true -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-local = { workspace = true, features = ["mnemonic", "keystore"] } -alloy-signer-ledger = { workspace = true, features = ["eip712"] } -alloy-signer-trezor.workspace = true -alloy-network.workspace = true -alloy-consensus.workspace = true -alloy-sol-types.workspace = true -alloy-dyn-abi.workspace = true - -# browser wallet -axum.workspace = true -foundry-common.workspace = true -serde_json.workspace = true -tokio = { workspace = true, features = ["macros"] } -uuid.workspace = true -webbrowser = "1.0.6" - -# aws-kms -alloy-signer-aws = { workspace = true, features = ["eip712"], optional = true } -aws-config = { version = "1", default-features = true, optional = true } - -# gcp-kms -alloy-signer-gcp = { workspace = true, features = ["eip712"], optional = true } - -# turnkey -alloy-signer-turnkey = { workspace = true, features = ["eip712"], optional = true } - -tempo-primitives.workspace = true -alloy-rlp.workspace = true - -async-trait.workspace = true -clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } -derive_builder = "0.20" -dirs.workspace = true -eyre.workspace = true -rpassword = "7" -serde.workspace = true -thiserror.workspace = true -toml.workspace = true -tower.workspace = true -tower-http = { workspace = true, features = ["cors", "set-header"] } -tracing.workspace = true -eth-keystore = "0.5.0" - -[dev-dependencies] -reqwest = { workspace = true, features = ["json"] } - -[features] -aws-kms = ["dep:alloy-signer-aws", "dep:aws-config"] -gcp-kms = ["dep:alloy-signer-gcp"] -turnkey = ["dep:alloy-signer-turnkey"] diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs deleted file mode 100644 index a0f9628956469..0000000000000 --- a/crates/wallets/src/error.rs +++ /dev/null @@ -1,75 +0,0 @@ -use alloy_primitives::hex::FromHexError; -use alloy_signer::k256::ecdsa; -use alloy_signer_ledger::LedgerError; -use alloy_signer_local::LocalSignerError; -use alloy_signer_trezor::TrezorError; - -#[cfg(feature = "aws-kms")] -use alloy_signer_aws::AwsSignerError; - -#[cfg(feature = "gcp-kms")] -use alloy_signer_gcp::GcpSignerError; - -#[cfg(feature = "turnkey")] -use alloy_signer_turnkey::TurnkeySignerError; - -use crate::wallet_browser::error::BrowserWalletError; - -#[derive(Debug, thiserror::Error)] -pub enum PrivateKeyError { - #[error("Failed to create wallet from private key. Private key is invalid hex: {0}")] - InvalidHex(#[from] FromHexError), - #[error( - "Failed to create wallet from private key. Invalid private key. But env var {0} exists. Is the `$` anchor missing?" - )] - ExistsAsEnvVar(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum WalletSignerError { - #[error(transparent)] - Local(#[from] LocalSignerError), - #[error("Failed to decrypt keystore: incorrect password")] - IncorrectKeystorePassword, - #[error(transparent)] - Ledger(#[from] LedgerError), - #[error(transparent)] - Trezor(#[from] TrezorError), - #[error(transparent)] - #[cfg(feature = "aws-kms")] - Aws(#[from] Box), - #[error(transparent)] - #[cfg(feature = "gcp-kms")] - Gcp(#[from] Box), - #[error(transparent)] - #[cfg(feature = "turnkey")] - Turnkey(#[from] TurnkeySignerError), - #[error(transparent)] - Browser(#[from] BrowserWalletError), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error(transparent)] - InvalidHex(#[from] FromHexError), - #[error(transparent)] - Ecdsa(#[from] ecdsa::Error), - #[error("foundry was not built with support for {0} signer")] - UnsupportedSigner(&'static str), -} - -impl WalletSignerError { - pub const fn aws_unsupported() -> Self { - Self::UnsupportedSigner("AWS KMS") - } - - pub const fn gcp_unsupported() -> Self { - Self::UnsupportedSigner("Google Cloud KMS") - } - - pub const fn turnkey_unsupported() -> Self { - Self::UnsupportedSigner("Turnkey") - } - - pub const fn browser_unsupported() -> Self { - Self::UnsupportedSigner("Browser Wallet") - } -} diff --git a/crates/wallets/src/lib.rs b/crates/wallets/src/lib.rs deleted file mode 100644 index 435da79ace3d8..0000000000000 --- a/crates/wallets/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! # foundry-wallets -//! -//! Utilities for working with multiple signers. - -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(docsrs, feature(doc_cfg))] - -#[macro_use] -extern crate foundry_common; - -#[macro_use] -extern crate tracing; - -pub mod error; -pub mod opts; -pub mod signer; -pub mod tempo; -pub mod utils; -pub mod wallet_browser; -pub mod wallet_multi; -pub mod wallet_raw; - -pub use opts::WalletOpts; -pub use signer::{PendingSigner, WalletSigner}; -pub use tempo::TempoAccessKeyConfig; -pub use wallet_browser::opts::BrowserWalletOpts; -pub use wallet_multi::MultiWalletOpts; -pub use wallet_raw::RawWalletOpts; - -#[cfg(feature = "aws-kms")] -use aws_config as _; diff --git a/crates/wallets/src/opts.rs b/crates/wallets/src/opts.rs deleted file mode 100644 index 49a15a6365979..0000000000000 --- a/crates/wallets/src/opts.rs +++ /dev/null @@ -1,330 +0,0 @@ -use crate::{signer::WalletSigner, tempo::TempoAccessKeyConfig, utils, wallet_raw::RawWalletOpts}; -use alloy_primitives::Address; -use alloy_signer::Signer; -use clap::Parser; -use eyre::Result; -use serde::Serialize; - -/// The wallet options can either be: -/// 1. Raw (via private key / mnemonic file, see `RawWallet`) -/// 2. Keystore (via file path) -/// 3. Ledger -/// 4. Trezor -/// 5. AWS KMS -/// 6. Google Cloud KMS -/// 7. Turnkey -/// 8. Browser wallet -#[derive(Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "Wallet options", about = None, long_about = None)] -pub struct WalletOpts { - /// The sender account. - #[arg( - long, - short, - value_name = "ADDRESS", - help_heading = "Wallet options - raw", - env = "ETH_FROM" - )] - pub from: Option
, - - #[command(flatten)] - pub raw: RawWalletOpts, - - /// Use the keystore in the given folder or file. - #[arg( - long = "keystore", - help_heading = "Wallet options - keystore", - value_name = "PATH", - env = "ETH_KEYSTORE" - )] - pub keystore_path: Option, - - /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename - #[arg( - long = "account", - help_heading = "Wallet options - keystore", - value_name = "ACCOUNT_NAME", - env = "ETH_KEYSTORE_ACCOUNT", - conflicts_with = "keystore_path" - )] - pub keystore_account_name: Option, - - /// The keystore password. - /// - /// Used with --keystore. - #[arg( - long = "password", - help_heading = "Wallet options - keystore", - requires = "keystore_path", - value_name = "PASSWORD" - )] - pub keystore_password: Option, - - /// The keystore password file path. - /// - /// Used with --keystore. - #[arg( - long = "password-file", - help_heading = "Wallet options - keystore", - requires = "keystore_path", - value_name = "PASSWORD_FILE", - env = "ETH_PASSWORD" - )] - pub keystore_password_file: Option, - - /// Use a Ledger hardware wallet. - #[arg(long, short, help_heading = "Wallet options - hardware wallet")] - pub ledger: bool, - - /// Use a Trezor hardware wallet. - #[arg(long, short, help_heading = "Wallet options - hardware wallet")] - pub trezor: bool, - - /// Use AWS Key Management Service. - /// - /// Ensure the AWS_KMS_KEY_ID environment variable is set. - #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "aws-kms"))] - pub aws: bool, - - /// Use Google Cloud Key Management Service. - /// - /// Ensure the following environment variables are set: GCP_PROJECT_ID, GCP_LOCATION, - /// GCP_KEY_RING, GCP_KEY_NAME, GCP_KEY_VERSION. - /// - /// See: - #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "gcp-kms"))] - pub gcp: bool, - - /// Use Turnkey. - /// - /// Ensure the following environment variables are set: TURNKEY_API_PRIVATE_KEY, - /// TURNKEY_ORGANIZATION_ID, TURNKEY_ADDRESS. - /// - /// See: - #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "turnkey"))] - pub turnkey: bool, - - /// Tempo access key private key. - /// - /// When set, the transaction is signed with this access key on behalf of - /// `--tempo.root-account`. - #[arg( - long = "tempo.access-key", - help_heading = "Wallet options - Tempo", - value_name = "PRIVATE_KEY", - env = "TEMPO_ACCESS_KEY" - )] - pub tempo_access_key: Option, - - /// Tempo root account address (the `from` address for keychain transactions). - /// - /// Required when `--tempo.access-key` is set. - #[arg( - long = "tempo.root-account", - help_heading = "Wallet options - Tempo", - value_name = "ADDRESS", - requires = "tempo_access_key", - env = "TEMPO_ROOT_ACCOUNT" - )] - pub tempo_root_account: Option
, -} - -impl WalletOpts { - /// Attempts to resolve a signer from the configured wallet options. - /// - /// Returns the signer and, for Tempo keychain mode, an [`TempoAccessKeyConfig`] describing the - /// root wallet and provisioning data. - /// - /// Returns `Ok((None, None))` if no wallet option was configured and no Tempo fallback - /// matched. - pub async fn maybe_signer( - &self, - ) -> Result<(Option, Option)> { - trace!("start finding signer"); - - // If a Tempo access key is provided on the CLI, use it directly. - if let Some(ref access_key) = self.tempo_access_key { - let root_account = self.tempo_root_account.ok_or_else(|| { - eyre::eyre!("--tempo.root-account is required when --tempo.access-key is set") - })?; - let signer = utils::create_private_key_signer(access_key)?; - let key_address = signer.address(); - let config = TempoAccessKeyConfig { - wallet_address: root_account, - key_address, - key_authorization: None, - }; - return Ok((Some(signer), Some(config))); - } - - let get_env = |key: &str| { - std::env::var(key) - .map_err(|_| eyre::eyre!("{key} environment variable is required for signer")) - }; - - let signer = if self.ledger { - utils::create_ledger_signer(self.raw.hd_path.as_deref(), self.raw.mnemonic_index) - .await? - } else if self.trezor { - utils::create_trezor_signer(self.raw.hd_path.as_deref(), self.raw.mnemonic_index) - .await? - } else if self.aws { - let key_id = get_env("AWS_KMS_KEY_ID")?; - WalletSigner::from_aws(key_id).await? - } else if self.gcp { - let project_id = get_env("GCP_PROJECT_ID")?; - let location = get_env("GCP_LOCATION")?; - let keyring = get_env("GCP_KEY_RING")?; - let key_name = get_env("GCP_KEY_NAME")?; - let key_version = get_env("GCP_KEY_VERSION")? - .parse() - .map_err(|_| eyre::eyre!("GCP_KEY_VERSION could not be parsed into u64"))?; - WalletSigner::from_gcp(project_id, location, keyring, key_name, key_version).await? - } else if self.turnkey { - let api_private_key = get_env("TURNKEY_API_PRIVATE_KEY")?; - let organization_id = get_env("TURNKEY_ORGANIZATION_ID")?; - let address_str = get_env("TURNKEY_ADDRESS")?; - let address = address_str.parse().map_err(|_| { - eyre::eyre!("TURNKEY_ADDRESS could not be parsed as an Ethereum address") - })?; - WalletSigner::from_turnkey(api_private_key, organization_id, address)? - } else if let Some(raw_wallet) = self.raw.signer()? { - raw_wallet - } else if let Some(path) = utils::maybe_get_keystore_path( - self.keystore_path.as_deref(), - self.keystore_account_name.as_deref(), - )? { - let (maybe_signer, maybe_pending) = utils::create_keystore_signer( - &path, - self.keystore_password.as_deref(), - self.keystore_password_file.as_deref(), - )?; - if let Some(pending) = maybe_pending { - pending.unlock()? - } else if let Some(signer) = maybe_signer { - signer - } else { - unreachable!() - } - } else { - // No explicit wallet option was provided. Try Tempo wallet as a fallback - // if `--from` is set. - if let Some(from) = self.from { - match crate::tempo::lookup_signer(from)? { - crate::tempo::TempoLookup::Direct(signer) => { - return Ok((Some(signer), None)); - } - crate::tempo::TempoLookup::Keychain(signer, config) => { - return Ok((Some(signer), Some(*config))); - } - crate::tempo::TempoLookup::NotFound => {} - } - } - - return Ok((None, None)); - }; - - Ok((Some(signer), None)) - } - - pub async fn signer(&self) -> Result { - self.maybe_signer().await?.0.ok_or_else(|| { - eyre::eyre!( - "\ -Error accessing local wallet. Did you pass a keystore, hardware wallet, private key or mnemonic? - -Run the command with --help flag for more information or use the corresponding CLI -flag to set your key via: - ---keystore ---interactive ---private-key ---mnemonic-path ---aws ---gcp ---turnkey ---trezor ---ledger ---browser - -Alternatively, when using the `cast send` or `cast mktx` commands with a local node -or RPC that has unlocked accounts, the --unlocked or --ethsign flags can be used, -respectively. The sender address can be specified by setting the `ETH_FROM` environment -variable to the desired unlocked account address, or by providing the address directly -using the --from flag." - ) - }) - } -} - -impl From for WalletOpts { - fn from(options: RawWalletOpts) -> Self { - Self { raw: options, ..Default::default() } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::{path::Path, str::FromStr}; - - #[tokio::test] - async fn find_keystore() { - let keystore = - Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../cast/tests/fixtures/keystore")); - let keystore_file = keystore - .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); - let password_file = keystore.join("password-ec554"); - let wallet: WalletOpts = WalletOpts::parse_from([ - "foundry-cli", - "--from", - "560d246fcddc9ea98a8b032c9a2f474efb493c28", - "--keystore", - keystore_file.to_str().unwrap(), - "--password-file", - password_file.to_str().unwrap(), - ]); - let signer = wallet.signer().await.unwrap(); - assert_eq!( - signer.address(), - Address::from_str("ec554aeafe75601aaab43bd4621a22284db566c2").unwrap() - ); - } - - #[tokio::test] - async fn illformed_private_key_generates_user_friendly_error() { - let wallet = WalletOpts { - raw: RawWalletOpts { - interactive: false, - private_key: Some("123".to_string()), - mnemonic: None, - mnemonic_passphrase: None, - hd_path: None, - mnemonic_index: 0, - }, - from: None, - keystore_path: None, - keystore_account_name: None, - keystore_password: None, - keystore_password_file: None, - ledger: false, - trezor: false, - aws: false, - gcp: false, - turnkey: false, - tempo_access_key: None, - tempo_root_account: None, - }; - match wallet.signer().await { - Ok(_) => { - panic!("illformed private key shouldn't decode") - } - Err(x) => { - assert!( - x.to_string().contains("Failed to decode private key"), - "Error message is not user-friendly" - ); - } - } - } -} diff --git a/crates/wallets/src/signer.rs b/crates/wallets/src/signer.rs deleted file mode 100644 index ddbd5a47fe978..0000000000000 --- a/crates/wallets/src/signer.rs +++ /dev/null @@ -1,348 +0,0 @@ -use crate::error::WalletSignerError; -use alloy_consensus::SignableTransaction; -use alloy_dyn_abi::TypedData; -use alloy_network::TxSigner; -use alloy_primitives::{Address, B256, ChainId, Signature, hex}; -use alloy_signer::Signer; -use alloy_signer_ledger::{HDPath as LedgerHDPath, LedgerSigner}; -use alloy_signer_local::{MnemonicBuilder, PrivateKeySigner, coins_bip39::English}; -use alloy_signer_trezor::{HDPath as TrezorHDPath, TrezorSigner}; -use alloy_sol_types::{Eip712Domain, SolStruct}; -use async_trait::async_trait; -use std::{collections::HashSet, path::PathBuf}; -use tracing::warn; - -#[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::{ - GoogleApi, - google::cloud::kms::v1::key_management_service_client::KeyManagementServiceClient, - }, -}; - -#[cfg(feature = "turnkey")] -use alloy_signer_turnkey::TurnkeySigner; - -pub type Result = std::result::Result; - -/// Wrapper enum around different signers. -#[derive(Debug)] -pub enum WalletSigner { - /// Wrapper around local wallet. e.g. private key, mnemonic - Local(PrivateKeySigner), - /// Wrapper around Ledger signer. - Ledger(LedgerSigner), - /// Wrapper around Trezor signer. - Trezor(TrezorSigner), - /// Wrapper around AWS KMS signer. - #[cfg(feature = "aws-kms")] - Aws(AwsSigner), - /// Wrapper around Google Cloud KMS signer. - #[cfg(feature = "gcp-kms")] - Gcp(GcpSigner), - /// Wrapper around Turnkey signer. - #[cfg(feature = "turnkey")] - Turnkey(TurnkeySigner), -} - -impl WalletSigner { - pub async fn from_ledger_path(path: LedgerHDPath) -> Result { - let ledger = LedgerSigner::new(path, None).await?; - Ok(Self::Ledger(ledger)) - } - - pub async fn from_trezor_path(path: TrezorHDPath) -> Result { - let trezor = TrezorSigner::new(path, None).await?; - Ok(Self::Trezor(trezor)) - } - - pub async fn from_aws(key_id: String) -> Result { - #[cfg(feature = "aws-kms")] - { - let config = - alloy_signer_aws::aws_config::load_defaults(BehaviorVersion::latest()).await; - let client = AwsClient::new(&config); - - Ok(Self::Aws( - AwsSigner::new(client, key_id, None) - .await - .map_err(|e| WalletSignerError::Aws(Box::new(e)))?, - )) - } - - #[cfg(not(feature = "aws-kms"))] - { - let _ = key_id; - Err(WalletSignerError::aws_unsupported()) - } - } - - 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::Gcp(Box::new(GcpSignerError::GoogleKmsError( - e, - )))); - } - }; - - let specifier = KeySpecifier::new(keyring, &key_name, key_version); - - Ok(Self::Gcp( - GcpSigner::new(client, specifier, None) - .await - .map_err(|e| WalletSignerError::Gcp(Box::new(e)))?, - )) - } - - #[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_turnkey( - api_private_key: String, - organization_id: String, - address: Address, - ) -> Result { - #[cfg(feature = "turnkey")] - { - Ok(Self::Turnkey(TurnkeySigner::from_api_key( - &api_private_key, - organization_id, - address, - None, - )?)) - } - - #[cfg(not(feature = "turnkey"))] - { - let _ = api_private_key; - let _ = organization_id; - let _ = address; - Err(WalletSignerError::turnkey_unsupported()) - } - } - - pub fn from_private_key(private_key: &B256) -> Result { - Ok(Self::Local(PrivateKeySigner::from_bytes(private_key)?)) - } - - /// Returns a list of addresses available to use with current signer - /// - /// - for Ledger and Trezor signers the number of addresses to retrieve is specified as argument - /// - 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 - /// - errors when retrieving addresses are logged but do not prevent returning available - /// addresses - pub async fn available_senders(&self, max: usize) -> Result> { - let mut senders = HashSet::new(); - - match self { - Self::Local(local) => { - senders.insert(local.address()); - } - Self::Ledger(ledger) => { - // Try LedgerLive derivation path - for i in 0..max { - match ledger.get_address_with_path(&LedgerHDPath::LedgerLive(i)).await { - Ok(address) => { - senders.insert(address); - } - Err(e) => { - warn!("Failed to get Ledger address at index {i} (LedgerLive): {e}"); - } - } - } - // Try Legacy derivation path - for i in 0..max { - match ledger.get_address_with_path(&LedgerHDPath::Legacy(i)).await { - Ok(address) => { - senders.insert(address); - } - Err(e) => { - warn!("Failed to get Ledger address at index {i} (Legacy): {e}"); - } - } - } - } - Self::Trezor(trezor) => { - for i in 0..max { - match trezor.get_address_with_path(&TrezorHDPath::TrezorLive(i)).await { - Ok(address) => { - senders.insert(address); - } - Err(e) => { - warn!("Failed to get Trezor address at index {i} (TrezorLive): {e}",); - } - } - } - } - #[cfg(feature = "aws-kms")] - Self::Aws(aws) => { - senders.insert(alloy_signer::Signer::address(aws)); - } - #[cfg(feature = "gcp-kms")] - Self::Gcp(gcp) => { - senders.insert(alloy_signer::Signer::address(gcp)); - } - #[cfg(feature = "turnkey")] - Self::Turnkey(turnkey) => { - senders.insert(alloy_signer::Signer::address(turnkey)); - } - } - Ok(senders.into_iter().collect()) - } - - pub fn from_mnemonic( - mnemonic: &str, - passphrase: Option<&str>, - derivation_path: Option<&str>, - index: u32, - ) -> Result { - let mut builder = MnemonicBuilder::::default().phrase(mnemonic); - - if let Some(passphrase) = passphrase { - builder = builder.password(passphrase) - } - - builder = if let Some(hd_path) = derivation_path { - builder.derivation_path(hd_path)? - } else { - builder.index(index)? - }; - - Ok(Self::Local(builder.build()?)) - } -} - -macro_rules! delegate { - ($s:ident, $inner:ident => $e:expr) => { - match $s { - Self::Local($inner) => $e, - Self::Ledger($inner) => $e, - Self::Trezor($inner) => $e, - #[cfg(feature = "aws-kms")] - Self::Aws($inner) => $e, - #[cfg(feature = "gcp-kms")] - Self::Gcp($inner) => $e, - #[cfg(feature = "turnkey")] - Self::Turnkey($inner) => $e, - } - }; -} - -#[async_trait] -impl Signer for WalletSigner { - /// 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_message(&self, message: &[u8]) -> alloy_signer::Result { - delegate!(self, inner => inner.sign_message(message)).await - } - - fn address(&self) -> Address { - delegate!(self, inner => alloy_signer::Signer::address(inner)) - } - - fn chain_id(&self) -> Option { - delegate!(self, inner => inner.chain_id()) - } - - fn set_chain_id(&mut self, chain_id: Option) { - delegate!(self, inner => inner.set_chain_id(chain_id)) - } - - 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_dynamic_typed_data( - &self, - payload: &TypedData, - ) -> alloy_signer::Result { - delegate!(self, inner => inner.sign_dynamic_typed_data(payload)).await - } -} - -#[async_trait] -impl TxSigner for WalletSigner { - fn address(&self) -> Address { - Signer::address(self) - } - - async fn sign_transaction( - &self, - tx: &mut dyn SignableTransaction, - ) -> alloy_signer::Result { - delegate!(self, inner => TxSigner::sign_transaction(inner, tx)).await - } -} - -/// Signers that require user action to be obtained. -#[derive(Debug, Clone)] -pub enum PendingSigner { - Keystore(PathBuf), - Interactive, -} - -impl PendingSigner { - pub fn unlock(self) -> Result { - match self { - Self::Keystore(path) => { - let password = rpassword::prompt_password("Enter keystore password:")?; - match PrivateKeySigner::decrypt_keystore(path, password) { - Ok(signer) => Ok(WalletSigner::Local(signer)), - Err(e) => match e { - // Catch the `MacMismatch` error, which indicates an incorrect password and - // return a more user-friendly `IncorrectKeystorePassword`. - alloy_signer_local::LocalSignerError::EthKeystoreError( - eth_keystore::KeystoreError::MacMismatch, - ) => Err(WalletSignerError::IncorrectKeystorePassword), - _ => Err(WalletSignerError::Local(e)), - }, - } - } - Self::Interactive => { - let private_key = rpassword::prompt_password("Enter private key:")?; - Ok(WalletSigner::from_private_key(&hex::FromHex::from_hex(private_key)?)?) - } - } - } -} diff --git a/crates/wallets/src/tempo.rs b/crates/wallets/src/tempo.rs deleted file mode 100644 index e0a05d1212eae..0000000000000 --- a/crates/wallets/src/tempo.rs +++ /dev/null @@ -1,160 +0,0 @@ -use alloy_primitives::{Address, hex}; -use alloy_rlp::Decodable; -use eyre::Result; -use std::path::PathBuf; -use tempo_primitives::transaction::SignedKeyAuthorization; - -use crate::{WalletSigner, utils}; - -/// Wallet type: how this wallet was created. -#[derive(Clone, Copy, Default, serde::Deserialize)] -#[serde(rename_all = "lowercase")] -enum WalletType { - #[default] - Local, - Passkey, -} - -/// Cryptographic key type. -#[derive(Clone, Copy, Default, serde::Deserialize)] -#[serde(rename_all = "lowercase")] -enum KeyType { - #[default] - Secp256k1, - P256, - WebAuthn, -} - -/// A single entry from Tempo's `keys.toml`. -#[derive(serde::Deserialize)] -#[allow(dead_code)] -struct KeyEntry { - #[serde(default)] - wallet_type: WalletType, - #[serde(default)] - wallet_address: Address, - #[serde(default)] - chain_id: u64, - #[serde(default)] - key_type: KeyType, - #[serde(default)] - key_address: Option
, - #[serde(default)] - key: Option, - #[serde(default)] - key_authorization: Option, - #[serde(default)] - expiry: Option, - #[serde(default)] - limits: Vec, -} - -/// Per-token spending limit stored in `keys.toml`. -#[derive(serde::Deserialize)] -struct StoredTokenLimit { - #[allow(dead_code)] - currency: Address, - #[allow(dead_code)] - limit: String, -} - -/// The top-level structure of `~/.tempo/wallet/keys.toml`. -#[derive(serde::Deserialize)] -struct KeysFile { - #[serde(default)] - keys: Vec, -} - -/// Configuration for a Tempo access key (keychain mode). -/// -/// When a Tempo wallet entry uses keychain mode (`wallet_address != key_address`), the signer -/// is an access key that signs on behalf of the root wallet. This struct carries the metadata -/// needed to construct the correct transaction. -#[derive(Debug, Clone)] -pub struct TempoAccessKeyConfig { - /// The root wallet address (the `from` address for transactions). - pub wallet_address: Address, - /// The access key's address (derived from the private key that actually signs). - pub key_address: Address, - /// Decoded key authorization for on-chain provisioning. - /// - /// When present, callers should check whether the key is already provisioned on-chain - /// (via the AccountKeychain precompile) before including this in a transaction. - pub key_authorization: Option, -} - -/// Result of looking up an address in Tempo's key store. -pub enum TempoLookup { - /// A direct (EOA) signer was found — `wallet_address == key_address`. - Direct(WalletSigner), - /// A keychain (access key) signer was found — `wallet_address != key_address`. - Keychain(WalletSigner, Box), - /// No matching entry was found. - NotFound, -} - -/// Returns the path to Tempo's keys file. -/// -/// Respects `TEMPO_HOME` env var, defaulting to `~/.tempo`. -fn keys_path() -> Option { - let base = std::env::var_os("TEMPO_HOME") - .map(PathBuf::from) - .or_else(|| dirs::home_dir().map(|h| h.join(".tempo")))?; - Some(base.join("wallet").join("keys.toml")) -} - -/// Decodes a hex-encoded, RLP-encoded [`SignedKeyAuthorization`]. -fn decode_key_authorization(hex_str: &str) -> Result { - let bytes = hex::decode(hex_str)?; - let auth = SignedKeyAuthorization::decode(&mut bytes.as_slice())?; - Ok(auth) -} - -/// Looks up a signer for the given address in Tempo's `keys.toml`. -/// -/// Returns [`TempoLookup::Direct`] if a direct-mode (EOA) key is found, -/// [`TempoLookup::Keychain`] if a keychain-mode access key is found, -/// or [`TempoLookup::NotFound`] if no entry matches. -pub fn lookup_signer(from: Address) -> Result { - let path = match keys_path() { - Some(p) if p.is_file() => p, - _ => return Ok(TempoLookup::NotFound), - }; - - let contents = std::fs::read_to_string(&path)?; - let file: KeysFile = toml::from_str(&contents)?; - - for entry in &file.keys { - if entry.wallet_address != from { - continue; - } - - let Some(key) = &entry.key else { - continue; - }; - - // Direct mode: wallet_address == key_address (or key_address is absent). - let is_direct = - entry.key_address.is_none() || entry.key_address == Some(entry.wallet_address); - - let signer = utils::create_private_key_signer(key)?; - - if is_direct { - return Ok(TempoLookup::Direct(signer)); - } - - // Keychain mode: the key is an access key signing on behalf of wallet_address. - let key_authorization = - entry.key_authorization.as_deref().map(decode_key_authorization).transpose()?; - - let config = TempoAccessKeyConfig { - wallet_address: entry.wallet_address, - // SAFETY: `is_direct` was false, so `key_address` is `Some` and != wallet_address - key_address: entry.key_address.unwrap(), - key_authorization, - }; - return Ok(TempoLookup::Keychain(signer, Box::new(config))); - } - - Ok(TempoLookup::NotFound) -} diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs deleted file mode 100644 index 6849125e3f084..0000000000000 --- a/crates/wallets/src/utils.rs +++ /dev/null @@ -1,164 +0,0 @@ -use crate::{PendingSigner, WalletSigner, error::PrivateKeyError}; -use alloy_primitives::{B256, hex::FromHex}; -use alloy_signer_ledger::HDPath as LedgerHDPath; -use alloy_signer_local::PrivateKeySigner; -use alloy_signer_trezor::HDPath as TrezorHDPath; -use eyre::{Context, Result}; -use foundry_config::Config; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -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: &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 PrivateKeySigner::from_bytes(&private_key) { - Ok(pk) => Ok(WalletSigner::Local(pk)), - Err(err) => { - ensure_pk_not_env(private_key_str)?; - eyre::bail!("Failed to create wallet from private key: {err}") - } - } -} - -/// Creates [WalletSigner] instance from given mnemonic parameters. -/// -/// Mnemonic can be either a file path or a mnemonic phrase. -pub fn create_mnemonic_signer( - mnemonic: &str, - passphrase: Option<&str>, - hd_path: Option<&str>, - index: u32, -) -> Result { - let mnemonic = if Path::new(mnemonic).is_file() { - fs::read_to_string(mnemonic)? - } else { - mnemonic.to_owned() - }; - let mnemonic = mnemonic.split_whitespace().collect::>().join(" "); - - Ok(WalletSigner::from_mnemonic(&mnemonic, passphrase, hd_path, index)?) -} - -/// Creates [WalletSigner] instance from given Ledger parameters. -pub async fn create_ledger_signer( - hd_path: Option<&str>, - mnemonic_index: u32, -) -> Result { - let derivation = if let Some(hd_path) = hd_path { - LedgerHDPath::Other(hd_path.to_owned()) - } else { - LedgerHDPath::LedgerLive(mnemonic_index as usize) - }; - - WalletSigner::from_ledger_path(derivation).await.wrap_err_with(|| { - "\ -Could not connect to Ledger device. -Make sure it's connected and unlocked, with no other desktop wallet apps open." - }) -} - -/// Creates [WalletSigner] instance from given Trezor parameters. -pub async fn create_trezor_signer( - hd_path: Option<&str>, - mnemonic_index: u32, -) -> Result { - let derivation = if let Some(hd_path) = hd_path { - TrezorHDPath::Other(hd_path.to_owned()) - } else { - TrezorHDPath::TrezorLive(mnemonic_index as usize) - }; - - WalletSigner::from_trezor_path(derivation).await.wrap_err_with(|| { - "\ -Could not connect to Trezor device. -Make sure it's connected and unlocked, with no other conflicting desktop wallet apps open." - }) -} - -pub fn maybe_get_keystore_path( - maybe_path: Option<&str>, - maybe_name: Option<&str>, -) -> Result> { - let default_keystore_dir = Config::foundry_keystores_dir() - .ok_or_else(|| eyre::eyre!("Could not find the default keystore directory."))?; - Ok(maybe_path - .map(PathBuf::from) - .or_else(|| maybe_name.map(|name| default_keystore_dir.join(name)))) -} - -/// Creates keystore signer from given parameters. -/// -/// If correct password or password file is provided, the keystore is decrypted and a [WalletSigner] -/// is returned. -/// -/// Otherwise, a [PendingSigner] is returned, which can be used to unlock the keystore later, -/// prompting user for password. -pub fn create_keystore_signer( - path: &PathBuf, - maybe_password: Option<&str>, - maybe_password_file: Option<&str>, -) -> Result<(Option, Option)> { - if !path.exists() { - eyre::bail!("Keystore file `{path:?}` does not exist") - } - - if path.is_dir() { - eyre::bail!( - "Keystore path `{path:?}` is a directory. Please specify the keystore file directly." - ) - } - - let password = match (maybe_password, maybe_password_file) { - (Some(password), _) => Ok(Some(password.to_string())), - (_, Some(password_file)) => { - let password_file = Path::new(password_file); - if password_file.is_file() { - Ok(Some( - fs::read_to_string(password_file) - .wrap_err_with(|| { - format!("Failed to read keystore password file at {password_file:?}") - })? - .trim_end() - .to_string(), - )) - } else { - Err(eyre::eyre!("Keystore password file `{password_file:?}` does not exist")) - } - } - (None, None) => Ok(None), - }?; - - if let Some(password) = password { - let wallet = PrivateKeySigner::decrypt_keystore(path, password) - .wrap_err_with(|| format!("Failed to decrypt keystore {path:?}"))?; - Ok((Some(WalletSigner::Local(wallet)), None)) - } else { - Ok((None, Some(PendingSigner::Keystore(path.clone())))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_private_key_signer() { - let pk = B256::random(); - let pk_str = pk.to_string(); - assert!(create_private_key_signer(&pk_str).is_ok()); - // skip 0x - assert!(create_private_key_signer(&pk_str[2..]).is_ok()); - } -} diff --git a/crates/wallets/src/wallet_browser/app/assets/banner.png b/crates/wallets/src/wallet_browser/app/assets/banner.png deleted file mode 100644 index 2a3752b97fc0c082483daaa3e94a29cec296599b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 559043 zcmXt9RaDdu6aDQjv2-^`x3nNqyCB^y(h|}k-MfT>bV+xKA}JyztO`g-cO%{1vHSgB zzNb5{bIzQZd+xpSURPU%2%jDw001I2RYiROfc{rvL7|ZUpeA;J;Xgp>Y8on0&@y4r z1@SRKn4Q;x9LzmAK^V*s0Wl@F&{HN>9#(D<^hFROC3$hE73Lux27tWWLiUrr?t3GfkrW`9}5g~4>}&jw@eO>=Ve zrZusm)N#_|?J*BuZ;{lCjyP?p-s3HfbK?bhmaLfzRF43F1yECzHw>6-H`6()dQ2hS zgPkf^z(6zIh)x|-rg^HhaQgt89k_p$1#%5u?k+bTEnx$1{mpioPOz$|S<~{r%~iL2 zh~duX@O5tET5O~Dea&s<^EmOv^e={w-1N&vbpu<0s%uYmJaXXqlQIwItxd@9Lh{QK zd+VRO?_bAWD}P%3)BKA=so!OnImDIETrH9&sfT1$CpBm)WZfXu=2G^0lOgZ{7!t{iq$YxytBB@oOt1DvSJHi;kIGeQ9EBP0yD@h4^p@PL?O|?wQ<*`zPHZac+f7WKioS>R+&OMOv8VS3T(k1 z9JV+UlTgkHOen39@2Iaitb`dZF_uIZnI>%VRzgsdl?47>My^CBueDi=oojofp@)rW z(ZivU9jEEtlT;vGmg$_DO8m(H0xJ@!{PH&po6iJ;6f5{~q|E}m7dvv*V7?ZCMlvk@ zN#AV;sx~K%Vn2R598**K}CWBQ=i-%(O1KWU80K!Go*Sg(lys!X-Fz%U$yaUGX z8EiI?Ud`f>!O&#+K9=2SG0%=p2e-GeW2C5Sf||mahZ+#@sDu}5i~Qq!^_#nqngGe{ z&eNUAFUe52$VZKLbv;!rA~Sx{PBy#&`rGTsfo~IWXJBdDcg)$+Kkni&mOB2$c@Ku; zBdZtM96-30hF~srt*KFXr^~`>sFYZ}N{4mY%7$}?y}^*QTeAdA3HOsw`ui6@e<}aW z7h^F>w(?CQjW~Q+EZ&N*8X7qeHW8~v&b^v*L`5cb{93uahr~Q+(@fRvl!3%4K*~rN zv6;2-IGAMhakLbyZaVd?PIpLhZfw+_vG^0j7qZ}<|K zjd@LlOt5DA2TO|}#Ar>JY7=W(Z!=2+!HO%r;E_|9A}*X7bxpFr!UC>al!pX#97Ywq zp~{ALjRkmiF5{*HL5+Pk?EG>*EW0{8Dl%gOLA&D~inzp5ZnsIS2>mB4*~{>Uu~`R38g4bp(F!|%IQNxvWWn>;f2V80 zVZrT##6wC23vN;d+ao0GBJIJ&5|R;33_r4&0_?ZW5{YDG^#8Ib*}h8}8=f^2#|>Fr z!KrGueWh{?GtwbLQkMnV3QL#{w?+7?%GX+V>@}8`_Zi&Keci~5b2Kj1v=OS@&sy`b z4)k4n`+@i`)Gz<Yl#Lv43Wz9iihv@V{_ zzdaWxlM)cS8(mDxTr8U&DX^wRQq#62Y)UNLMI+snbOw=NUgCTdJMNIT96bO%X6C-5 z+Q$eepx8?U=I+cIFdlYtOXMMT*Raelcx@S6Wvp||9H4!Q@UR{Ooj4@mnEyTNb2L}zW0-c@mHy2Cfpyr5MA~t zG`jo?Vgl^<_lv6OSaml2OXi+3836GSaQr+Xbf=C#>^5k!?1=~LMP_z};{b##mvP~= zsFi$inMnHg<;V8D7t5)@px_*xM|*ys=#w?6CkNgRIF{EKMqIate4z>p7hCWPpL_Ao zl#MvYk8H#TawtNDNk*Ob2{HY@>`U=BPc;e_oZi$JzKm>b!@z4gA}|^8tN99ZWtywO z8+aZ3l*o#cW#Oa;-04W?{E&C#ZKmYvsiQi* z5fQOef*$(*Z5awN?dvAo$?-=*E>cwS-i6CxSbzmubnSd?xD6md4I;hNyHtUp)Kx&} z?^}51(;|R99Tr9{Dl8nck1kx{Kp{r(ECFjBPs~xREZ**DjP}$y!QY*j3OZy`DNPnN z0@-tDDg9vICQQIWo;n()CjBF?UellgqDU9O?1G8aI*-i2)L;R{pjD7b@9l89{f-#X zOdJ&1$p&cD%7UjE35qCz1=m=tuGjZ(nZm)KEG$5eaiK_Rm*~=6FljTszW5;M&ix_5 zMeq;0EU#9CJFnr&QzB%cdRrmxCNU_{-W^_0U4>wvb!RWgFva`KBh4 zb|}#QTM(F^yu>)X?fTGBJas1%sP0zHzc#}i&NjHOCNzzY{1%)JKWec3Tx0XTDg%^E zVEq`XzM*t7_sjpCRiD;G)V@Y7)}C-F^^8#8k^~1^Za{~%99_?RiASQmr@h`YsZAHo zKPSn%StVfaryYih`wN9I%K6LYKo|S(>csYc&TPrS2D0M)d^23l6@%t#8M}VovPpKo zwHnO=c&{U(d{?;myebDo@p*^noYcv}Be24dSDDvgwx(++PciF*AKOxHCCC zY(PdKXoWH#*jL%ImJ+Qvf_{YY0!7m}h}13Lyh7o(-Lw~MYrlEdh2eHbRdujxJUh!` zzaSrV#WPr#RVWAOFP4`o5DXN(L_Y@$*VC{#0cp+})Bx!~Cl_#u3bRN7bNqz;>`VnW zQ9?X_fboFnVb8R%Q`*$3{Y_nsh~Ysa5RjuVe5g2bXT58j=plQb8YdYznM%Ua{$@NA z=EC3c-kJN6_5iB_o`4dE53%ND^f*Kbe~sf$Ah-AqUOZcO}$2RBY68uv;h zzVjQrZ-dv4ToR`XY!AvP3nH~4$z7#JXz*M2)5;GRNkYO$Z(WU~x^{9Q5+MhcOkYWp z>EIRQr@9@-{Isj!KPh{ay+m7ph<1--}V(C4t_e--n2Bz4*oRge8u! zi~__fueO>h)gSPxY5NCT|J%vK`Hp+t0d;|=FsB_ikAjgzd3BRe3v6&uT;5lMu4fRx zz`_xH+AmmAT;#l@q)_Z0YLmiHn-!z|hu}*JQ+aVesdI@gr4*gow_L0#!jWzN{#k3) zF`w1R6Ohc8?_Cp(kYYxFSE7xr0o4N>bcWr`Sj^Qu5xA()#N4j9K}yi$|AhMPt8< zzT7tu?q)Hk|f+Ep&C@1Z9yD>g{_owv8=%^SmZ*dLAOKO}m`R;!3KZC3m`3 z?nfW0l*N%%R1(ASlm{EWr!?{DfhHg6nCY$ku*hj*+q#mt^!X@@IX+eQLXB)rYAu98 z+5_S~^4j4njMyzHTT-b1gzhwkqy+fu&AVCeZ9VlbYKQ<-#DdL4{A3&0)n(wy(1;59 zb;{ki5T^;8i4BcaV%T=sNdk^{!+Yd3FJ4I8B?E+vJJG#x44%0fZnGN}#{bNrqdOLe z!@n^Tjo*O*k7xna8YEsdLOBe}Q#guu-EFIL=LZmz49N5y{~=jHc;U&|{wQGl`?2(j zxA&-56#0I054%0nzCQ>vdZvKDZnV!P=K+t#J%`tj=n=$RJUk%2SeSY2_7f||MB7aW z)DJu_RpR@{R%yt3l7qnvXul4jn_p9Vxs^BO<>JK4TK}-P>A-{`03)|r?ECP0?xp0? z#llIEEh3`o4<6-c#MMdtup%n^gsB@$%dpbfLQ_Wl-lwafn9 zLS_0Og(XY~dc<|b`R}&3ysM7BktL!)H#pVDzix3e@%wum@V(K&vKqvinK_Fr3{&cr zm%R|R8m@9IiZuO7KOSfloY0yV6`IVNXMmh*6~U+1-1gU&Gs>`8NHGZqIkNw{5{Bz1 zXw0i~VHYaTkQ@&#N$@3Wg_T%`Jfk>xM^u3)1?%Ww|tKVKwnclL! zOlHiB)+Hb{V80#bf(AZ(Znqqu@LS+k-er#22UZ-rj~10+y(XQ8x!OCb@nyI6NCA4# z^}odCLra)VFBL&~CZv*9ab$H_K=!TB(k*u^=u|P(Qss~$ZdV(O`8t_Y5r_i~2-}J) zHM*v`kp~ewRupResJ$}Ol5dIZG~+;OIDx!qjw7cplL}?^fGc0=kkhp4+fF`QO8!XA zdnjnXoF-tFyhyIVzXZgGuGIv}LCvqE1O8C-gVbp};a$)+p#7mcbmIjiRVOl?_eG|) z=|oKlp5Bhf_ldjl}PTYyL5~(iFH*|Ik^_`fQhYz$w%4;Z|xFjtY4X=kv=i$d93*nI$Bawj@Ev{RX{FY1#IdOU=mfC$vy$Bejs} zPauX`p^Vq1C5)!5EF!@34KO1P5NRZST@5FM_so1_eQupK@eMJrNRO-7MbSc`E!l!t z@lYL$;-x@x+3IIDL^exL#ae&tOP~+UZekZjq1ac^^K|Q4ib!XnjZ-0mJ`Nl7_2R2< z0X5h{g+5n?7)5a`;#Lr?IF!^ZNvcgSYK~`{4w2AV&Y0)qZjd3zc<0f3i$KdSz=kma zZ@4RHEr6$r;7UAHNY?Nc|Av(dz$jlQywzF90S~;|x-qH4L;#fP)(@P2T08;D(%;5G zs~C-hP)oT1#X%pb3K>($iE5R@~jl=xZds_ z(^4??4obg}NZ#YuHZ=J@`7y;Y0QD3ntVC+!^IsDxMr*D_N8X1D@6w=TaGjyCc>ErFopAv);wqEy=$^aoOZ&MlQU+ zXJJinZ?UQoDRCtljvG)Y{t63_r?Y?k&_@78G5`77SsRzh`^=;Cvn&+0_lV_3B^c}b zj#+E>m46~cRrK@bsw90#JAJ!stxNe%E(>Q1yMUyUeu<#_S z@5fjU&Cz5_GrYs9cARy3-kTL(G(Ovj6>+-bom!YtQ^-5cc*8f`P4iQ?pnNuGZY1?( zM6nvJQ@6;Q$RrUJuo2H+0arzQ1(?(8qEch0#_+TljC_|?)a8Jf0fkQJp#q2_oG*`V z^69gYoy=nHSC_O@w(?d{C(Il}(G9^3*I$rr2=z%Tp{28ptt$s799`^ykN+?|BA#OJM?wz|Nh zfV?+{-1mh>4=vax@kG`_>ln6+5V>MUYpa$}PU<`&u<*DM8&3c8j8t&*6y`!~8m)2& z5(OXsPDQSoh140=Hvu0F#z{xJFvWRD`H9s%=1c}02;L>4)}fmuqG;qPD-cCWCA{Lt z#q&N7iyGHa1*s+pX#Sx!?@DU(+(k%J9tfo%Z+(;PHtVY}96TyJK8`9qKCZIi< z4-_e?(E6Fwi=%^V;#I3>W7XHgw&*Sb6yzFSJfj%^Eh#R3c)W2?yzUmjLTLl%agJ#2 zRE3!F{P~`*%S&kBNQ6#SfWap#-0C!p*hZhcey@ZiovV&6+xAY}7_T*&_X+F+vjL@C zpk8d@-4QJkEwUrw898{unf%l72D4G`v&>cfh2ksb3#1(=#9Q=jj~#=n`%g%9;m}@$ z>ufa9xl_zzzbJx4DO})jsBx9ghtqHUZDqX!ue<=j6zIm4Oqo&(3u@^9Q|2AuM2UP& zvsEBl)DI^Su(lFk%$<8G|Jos?N;cimO7pah{Vm3b4H4I0g~T9^_etOV~96W zK4X}zFlp?OD+t!{+I12bQ2h30zuMQGDF7z-8h>%%X{}PwadX3Nrz)Vqj%q?Ku&K@t z5yAV``@~>RK?Cp8BD804d60$p_bDCJ`%HvdMPW!wk;>0jejdCrZrThwq#p57Vg}pPs2@z2i@t-x`6u>}ke};KZn~$jxEHvh6rWbd z{3(qTB>+cKBx{UTq?;i4i`}Q%4?ehQWfYEGAr<3G%I^w&BSta~ng`xrXX5@s4-Hqa z9sASyeB%3&O0cDuahZoKQHuYf0I60P&p>KBshL7n30KVKHI z6BRhARtORhe<(fP`^y$aiOy_5C{bF=B8C%C`60-3KodK{aBX_)Xr$8)GrTEh@4m(n z*6#PW2$NzOZGN$1C>KXow2&)*aragPK@8@Z9=JycG_(?o-+R|gq))UNa$AGI89Brz ztoQ6!ps$ISW{-az$z4iuSNaj;ft!c!?!Fy5oMT>oMjV0)k_x$B(-bm`MLcj*v4wnQ zM7l$Q0`c!K%DBnDL7nB>%UYzmPQc+_?&=R8P&wKPmVymWTY0&K_ui|HM}zX$XL_5b zn+k1h;*U&b8jH(YFLCGhBU{II(IsS_w%3?kVQVTZ4!?CKjdVX#ELs+z6K_kJUUB5N z+6&lM#4BVkmbmN(K`mCD#6j&eD^z~(&hGS-~IJ`X(@ zjb!^d{Jc>Qf`v+6Qzn%BY#92xx{VrD4~qKT{*3yay6eXoUI-!{D5D8_ISmdVEC&;o zj$2PO@|1#Tf9XD+cYi9AnKBivW*q(?xGSjNJ;fO$EdmC9kIdLP?Z21{4G2#2^QoTuMgl4Ym!kuPl9e93%dAr&9(!}5c9<7RPWS_O`3i2=L2v~+eD zELwGTOlz-5JAewiaby16ifv*nYlpX72Bv19$YcT&Rl?+|`DlyIioa;)uHda_VLjR` zu>66GK-M!W5lDvI%IPrn`wKJesh5qmoz$3BiLUM-YRx^1H z8^#R=he;MvV*!uu?3o@iFV4+7FOxP-zse0RfMsd|Pa-zfp~#@sO|>fU)(vI?O!@d< zU^N+u5gyV7RL@}-3Ja+{O4u{3WHUtORaqxxu956AvdFl=ae<3;O(+LZq9qw z^;%Yhew5L*+qG85iNUb(X6@ub750s(8LY5eg`l znhNBWn(=8zWpF`Fl7j`PG~2P4x&WT(9^zPLzH7Vp;3Mp)kzu-7!T9T84l0*8HVBnR zTaNKQ_w~peNsgKW)Vi|d_7$yRRtIn6XhsA8f@VC@^jiiN!GvSjn)QInk)*-%Hm#5BlDeqv#vvj!C2^&{VPoagHGkqq4}3ntVimus2~zA zoTp9TbO1TOWlwQULvAGxcuRgCGG7c>l6`{{R-?(?1IS@PST64LBfJH-D{RT&gOjrw z^wt-E{#a-H1NqGN%qAo>@>%9hcFe;|fwLFMQMQB75ewh}74oG9?o8zc)y)6|U3RS0 zag)#DHCRT5<fH7$H@4zH(Ky4i8^%RCF5E*ik zuz`8E)@i1s`Y5m5NrS95Hc2hWl5%p+fpk_!=T>9jQuR z`g6=6C>MeZc`sQfVOUQBaa~B15{07@Kdn$zubi2E(COgSD0c*KI&CzQYLl7_ju7F- ziz!d2lRsmj`d4o>BxB2{f7GW)>k~5Q75KX`2APs2032)EB#xmkEo(o3B%DJ_Su6h` zN@KzF7dg=aZ<%XUS862CN$FuvEs5O)7V@H-tMV|t^km=!{Vz=k* z8^XK}V^0b%Te`OTe9MgG2_|ubeS>~}YTr+L`cGb*5~=)j5K_fTI)Vcq!4fhUIb592 z!uOfzWs#y@dS~~*|51zrXpa|?z8%Q7;<#!Z`;s}_aaKJn{TzY6%p*MNqCdmUgm6|&ra4-JBzd@xx>QA zfEHN;?n&y*J`FSdXNVo(^2PT@fFO243+Y~Pao$oV?8r9|_4Pf8p_78c7_J>3s;Mq* zo}xHcX!;kLlqZJQyehWRkMeGm9&ZghLZYwxye);)CLz3%;%`^K@8j+&4k8BPqUH9O zN9qTTEQ(f&_bb{rdz0#Xd#^%2iUSgswb`75)y6sy1Vh85?%vieOlkV88U|t$XI7n(C)-3h+vK#(Ioj#4eeW zelYx{bgWjTzOaeGG>`-jT}mFfzcAw|=hU@+T@XmjtyTNc4d@4(Ma_&5+d!_tID}SnA6qAR^3~+CDw8C0Ak1z_=z3D@ByDiTmkt_ z8JPO4lkkwwK=bg;o$1_+K9=ZTj@~yeYcq3?2^+V?d8wCD8hS}UvH)WA^IIiEVWy5A zkP`)C@uqpe+~Ml~z_k5P?g-u_YQTfbdEe1pTE#fyE{8RIA?b57|3giSF7DCm`?HHs zmRLzns)A#DH->j_8(t#Jd;Sn@Al`1i*p+*Yrs4%SFN-D+dK~~1Yuf_BdvnGn4LtK$ zz)*8RhHg?9KFjp4V}kz>1L4RBs9hu2jB10q9gp3Uoe_9LNsYc>K_Z%>A0{b9#1`RDAM?&CPK)iNDkldB^4Y zG%)bTpNQi%2^REqbew#ZFy*s}iZL{!BS9Eiub94Xxra+ELw<&7I$zhQn6!jh(DEnR zRC}|pQXi41=4M%JwD5NpsnJG5FPrv6}R8-{{pBY#(lW1&EW< z$!q=6o01u!lV}-IvO0yg=2U)`*a?35x_|>wU>l;`@o$*&c zuVEyiy*YU%Sjd#OQPRi|#p4<(A_mpZh%b9#If>L|8?B{w?7F$Vt%zMfP6(Ho1&3b{ znRI2g2~aAm{9!5qkpaEDA;Ygbj_NV3nUAyfOHJIq+Tgl3q8;#8UiK|*t>T}jRD0JP z9$O7P+_ZKaPTXiM=1^TPTJ1|Ig6}-IvJ)5^<_I5(Gi0iHAi_eE}XFz#IcCJ;F z;l8GS+a@S2Mej|~5)*Dd8s@G``2X=X+%Nx&G;&k0czJ;QPuSveevTuiW ztu!`?f?2z`(j3eTBqQw;Jz`H|XtMsbOC{2BJ3y=hS2FO6EQ)f#pC4oYb!Nxu zzM(0MDO$x(op<|K*W@mPrswU5?tnA8ld8Rr`OqDNd!zl2+6 zbMY4SUIg2iQL~ZDMqW3MFS^Rtwllks}a2$V9XU|zlBMExa4nokE_PtS)B<_%plc>O_%00YFnh%F}I3KYBq z#J7XsDaG5Ec;Vz!Aad$ENqx^mGzkaa~O z*rFg$;#(KmI0S62aI4W|^_V2wJDuYh)jt8K>-1{cj7Wukon^r&*n1j3y;F+_AjPuI zWfB2bNM933Ji7Ha0V{84%;f3)RGmwZkdRdMYC+BknDk)2>y9fODF6JJTR~4|`k|Eg zh1v&tWvpPExpKP#DffOwMHeQK*j@tUuNK4M`$@n1s(P+9vC$tY?JaPx<(zZl@i%f- zvrnK$R_Vlmgj>ew&)Ujf=ikqlO1*hctbZnNMEt_kKfPX_?BhNJEiE%ko`iGetUdSKx8~(DHMFojOI)8w+79fU2C2CJR@+1WTJL?(dnQQ(#)N;+FgV&#ar7??i?h z^cY>Pf+zp%L(lWj1uHJFxHCS9wCma2=oeCz-J%T2PHk>IP;)RK04?iV{+*Gw^i(pj zdojvCKZ`Sy#IIq zO1)GH^Rf|1s~BS*1ixnh$}zA}X@!qURv&Z`7C&i5@FrGrO&IXb=8n}a7-r|P=+Xo2 zOR}-akR}6Gr4HBsH2IyJt&CEpFwc%_`5vxxq;F_=R>D6c<5=3VvEdc)1)#m-&GY$= ztr16nA6}%4_4op@R@bJa_29tcguPnGfK_ykQ!cT!*Ri>uiVG)#^P+>_Ob!2raUCky zE77mik(|GGL=Z?~AKH<@tL_3Lu@S`&^5lTw1Ca^Pdj!$2uDsB^$310f1E?s9He5ja zFdB@I0@#tlbBzdjNo6C8s(UYjQaO`M{GoqsQ(m=XH-X}IdfOUtJX zFhVI++{26I?*O$&zG-#`WBq$Sc0#H4f3~yL4+`oA{GMD3lODlp%dNSdpW^{j8+v}8 z8U248sTvahDF{FU6qkswYgK9Cc)uc_*;=O8C+=U??olmuAMUk{EODxM3Od+mfC-o ze2k2w?v+gK7Gb$NG8lf9arRNDqv`{&4|;U;7;+!bX%(Z0YQhX}o$aXI7FM$#Hxc?4 z5oz7rM#uF%(7krI_pd!i@K9M(qwYWKQ5a%Q5j+42c!Jt0bZzqwWxDYN{q78Ph|Uky zokB~u-5wGcT)k#Rvme5&Xa5@mAQyWBkUXQ>+PIbKcUc1NH%Acxp{*H0*8ImQJMlbH zdEc^0VWx4%?700P$>(skafs#vESGpi?O|3&1_UQeeu?bfy#W>IHlzQn^I$)~1nua3 zw&0)!taS%E4(%P9pci~g>rKzVNcC5ggq?z21&r0X(0!Ma+j3az{M`in4aY6s&z$sk zmR-62Wr!r;8Wo-R(clF4U)NO4`h{R{Tx+HBgEAHW`?fhy^Kk=Z@o4G$tA@f)Xo44d zmy{^|x^6?R%I)1%Hcp~zm6w4KVLV-@FEZ1w2ClvrcZtxS*>B=~#*$Y6mb9m0^_=!U z$8^jnQb-*jZwDl?mzsc2?rrC5uL(M#B9L2=RkyNF17-D~$uC(3U@0+yTAQHU#=z}= z(VlLK5TpmRFPGW)&ck9*fK$4w%BpS-lJ7jO@elpX$AS1Uv;s7;IvpusAG!IAdHe2a z_80nUi5mVLP{oOIMb6(<2Y|qv!4zopSN;PRZ;+qf&5IMH%$y(t-F?xm6(JAIrSUqKi)wBD;pKJ{<<`*ulb8Qc2%T zq2Xr@Q2AsX^=cK1V|N#vwu^t% zrbMk9_|sFihEeQkZwL^R8Z9PiK~~~cx&0XrJh1oYl|^sV9tv$xBR%b>*BBfwp94Y* zmF`EM_fxqanpo9+$z7evaUdPKxLDad zKE7)KUKZJGsn!Mv>7G&4Y3_p3s~u4BE$a4Glx+fjBcK{eS%{6?ZTW^Vp^ zvhyG>TP);vSA40Q&$Lcg`N~&j2?6-7n4h=b%`ktOAk7qdq|J~kay&vaWbkH$z4^+g zufd6Y7{L)#Nm=M`woN2~G?rilpj#mj^I(0g(jLqjjGXr09HV;tRt6zJfw6piVablT zF~_>h#yZCSEnbJY%8v&IZr*aC>3AZr3E;0WIJOu-M4@K^Q6K;Y&N{|$hsMa;>5&f? z)>Z`Pg&LS6LdABun!9OW9J7bH(31cdQh;-MWF{!)Jko$oPU;tU#ubWsX$v4`0UkmY z2#gf!8w`*eLOK1J9%=sS41%zbqErO{ZmeqYM@MtreNS>_+}g>L(!%%#oSVxNqj?Zs zJy+`wCLS-CA9Acnj$|H68F#v3-x61Iw`2rjaBu?rzcF!1en#4E+y9xjo1WzLtLQf( z3J+~d9PO64jpy>sHPU{ya=t}cLe`yB4|zZBuDmS%#t+-NiPA_)fB*VWf&udx8(Q|2 zz|N1BkSMt!>E25E{SCaD^|uPU;wwfzo#-2BKW8X=6wA zKFy>9_24?u51;v~h~5S-`Y8@2Jm%^09~T73e0gj!IY?0QoYC{M9EA#`EZ^_e?mdo4 zEme(2k&HCF8LJ%Ob7>`R`s@-+-kGJZD7m3Ftf$K$9AV25LfWncWwIDMRFc^V;tzql zKu@jG!_bD|Hwg;RleKBF#0wN^K%77cxeA25{s-HHyduyIir{|&0HE}7Oq$;}+4qmN zd_=R?wf&!DbDx1465s#8aktStXUAYNWqqdSKjq-e{@8Afs6+}I`R=wqVJUjK7w^k1 z9A+q1^dc2)@ppf#gbVp(BJB(|eO!6DT#2eRnWo|ysT%jCzA}!JaspsZXb&ya^=KVt zVE#;10A-dSZu-0*sRrVFg{%bF@7Vh?$^;ZAT=aybeH4MDJ4iyO|HsX03z>PV1af9O`aHRh@Tz}blUIKSg`MPOGexdF5Mb>yBm zo1*hsPxw%0cCc`xN$l71KKSrCaBly%V6@9$Q1M-bO3yR(j1S-}kK~7~EG@HNOzhz~ zh4+0NoLaFk?v;FA{6Tw9FLKn*RO~_|SAmzOh7S`xGVn6$d2JZs{e9)tZ%&Xh9!sal z4qp*BZGN!vq-BPh^Y53g$3BCkQUH=&YL4gyCZTNQIdDxD>2%$*2uOeKXpSi*;^x9`l%j=I~vxq}w?v)Mul z+Kz!7W$SWX=2^nWYb~7M(_KuYzTX@dlKhJAr?!os%dA!Q1BoRp?)V+>DX1ZBA0zbFaDkeJS-2)keS3xix8R5b31PJRFJp>>^aOei zbnZdRmd_6KnpqT~D=RQ@d@rg_FLe?1=YIVsUaP5?%l7myu{r+|fwDIr$d2pi@_Wi`yt{tx5zn*e`n$8;pB0CIEC;1#Dzna4V_*RON8xmTFvqL zt#RseU?uQxie@t9KR;4w5~7#}UTi$CVrD{+@dmM#L_9e1CBP95@yl!77F0aO)Vo9c zq~tMiNk53CTN5aSsiD2L+C|OcJDIm%wguHY$F$Z(|?ww@!d=KXhodr$RVGN!TjHRf#|{Zv<2#w zJ&(WPD(Kv&r^|Ojcxdl;oe_71BC@Km(9!E{{Z1C>uk*HS@ZW#m4E);X$0%z2#g4?F zGx{q4T9`Sypj5^V&^=`L>JFyl@SU}_6lE^EyzJTtCq$hx(?FzEki4I&`SB0vz}t6! z6_fJspF5rkq^SIy$&5JdT79<@gyo>peV+c;FvTf0E#~eSIII0Q{-a|%P)E;}5a!D` zx7cxepZ9tg6jSwv2lFhONw0siT#SG9Yw()OcJ}%pZOCZfeIg1C<<7RT{cVpj+>u^v;2o>DGOxdu4^6jlqbOh!3mx zJMVWD2-PR^%u*@x3}VIb^eC@5l)>g~RbE|qwpWCTPU=8i@ZTn^l``jPWh&>~+-Fz= z3`*T|1srE1#9mV1dxq)d$T2L-^Cv644kJig*lXNgQS$Bdyl6$=xns6%iwA0svX^4@ zH|e-(o|Y?ad%q?vawiC2sMbXG(L(@VuEt03cMjPLqm+g~Pr-oACh_i26ev*eGwNg$ zN7al}TXlYfn;ldB5%f68^r=SDJL%B2r~b|x??vv{1CW;TJhxhLJi2fGqZuzD+2mZ5 zr4+z${f|8M+y4|#@-T3~+K>6#k}-R%D>C3>Ci6?ai&d(DdazVM2}UKjnkjM%j>X*1 z*uhAPjNwtNH%RFjFKNp9G!xl*+Xi%gl4q{Ee1?kwZV_`NcU$vjfEiX^CH9IqYEQ@p z)P8k}4mw0s(cOQU$5Dq7Jh((+lyCgmuxIFTYfkR}6Qq>kM7JM(+btCc+q^CAo=#y@ z2Buxc5fqDLgu0;tF((eTolU@p^)2%dQj$t~7giyq&oFoO3Kg-l^3_ue7a zn%D~dEGCfq0{1F24WWKPzT5We=!c7EfkIosXTb&1`z1B+j>wb&mN!fu4S8vtGeqh- z>Yp{|x>qxBC2?iz70@?+mllfThFp+>-0?$-G~ji9^}KZM-y>&4Paw(J>t;qH>-2%U zlq$K!159;*q0&v>p=Ehdgm&~nZ;%e1z|4uxBk!6 zpa^^oN-Ot&7a&tKo%+*%`dZYg=}0DH|2X}5vQEnHfvWa{C6c&Iot5d zP+{&?Lwy9|uDYY-ZmaM;(Dt=Q=3cqn)E*Ih$91FdtWIc$1hDsGUUUNmobE>ov%!-h*IM^s%9HmAnN;f`afuHD ztLY(e5kS=_8hPBgvhkYgafN*0^!ybs19EEy+!zP592%#i#M;KyEF@p4_tF-1lUF2B zohKj0>*_`=QLp@@LS?sh@`Z9(tdM|uVj;Dc)TK=cPv^?QkGL~Iom5pm3|AEYBQi`AAKAiCWFo5Rg z7bASHGII4C_!LME)1knKb*JjVUcndMmGjKB81V3y#m~Z?knlZ)mA#B{OvR%g*B|Wd zE|~ps=Ak{8-*$z@Xh-&S;#{8JjN;ZAW0W{RlU#L!T^`e8=ceLFw++mz!`9)#oc2HN zNI2MB39Jf5R^KmrA>@sV)-bd9D2q>Lw!1jUfd{cgoNqv~#LC63O2F^^qDcxMhrmnV zmS0wrEu%{rkN-#QMKml9^5nmN1fX3Ea^Ag*9_Z5ttSZ?@v`_?L7XKn>?68Hw!r{iZ zVX8Zab}fAvPWwGE-F>DyMCj_=_*>Pa>OyS!xl2 zePt&rPwM_n^?TNGT$~md8CweMIfi(i{<#Chj*`iEU#sLFR96g2S&c&6FA|(hpP4d` z)C?0iG5jLZ(orQ-tXLrp-}`CRBsA#m4U4Lm&G6+q!h7>e@qSSEWj<9M3Z14X1 z`kRx0{HMNbLJo4bUMD`9i%S@ za@;E=Z^XZ*Ep}Q-5}Rm?3hhd+ODSEhu1dH5ju+VGrQ19Ez*c0f@51^`B2YN);I6nk zeGl@nblmo3=sSgiWSQ_1>=4wu#UQ6+C)(FM_&Vz!-}hlw=gq!gOUhPYSp!AiAPbD< z2$Q#fDz;xx962CXx*SmtE7({>%qw{xAMv&NxHCGo!chAjalip?7e$>s_J#CRy*|^j z;QF0dDtj`=q4@OT#T;PnakIsCjoFSs7#o0VI=0t7))O7$!;OLMaxGnxVap5+i=(sq zPD{ak`7!`yN))k+vtke)J*fFs1q4Wx$b6}qVM$*1u-D+QMYGbNRFMcrh~lz08_kM3 z@b8`I(2WBXI_0cW4A=cFvpiB6$Ig}|6>G>KB%4qoT;w}GKY{rnG3)KEW35ImzQc8a zXL5ZeLZ#t=pP>c?iXG~!;eE{eol1LF!7y4^glMgtoY8$eOy9tD@`LAoUX*^uK?V_V zU60R`_DqTQCI|jTf-R=Vw-iKL$4m05uA93@)RSL{8nA?>|6woP7kBC}1s)7q0NI~x zLKU_bb8$!1F`36Tzs$|o8JmgVo)(Of`WxA~|9r0b z$hGWa8A)0?x=a9mxn}$PXzQ07Kq1io<-|L&d+VC_IqFj#zvq8F+GwC#d9ShVJQx+O zwqMbg^%6ekIsdn7slJuS0g<^8+jf6N!|_Usccb0oxtF7=&pB}?-=D}>vx??HC(&{=`0 zdI2#ZYql(yzx!J|*A<@imKd##f3|@4iHKZ^`qgA||2(*E@`&gc67vqj-mn@zVoQ^p zg4*gtH!PrEjUXaAV6zUwH{Z9A;>VK}5ZTV)=rzWnx`bb1+)Q`YCb*WSSZL-so9PK$ zLg=gagmsyO5hOAuDY~BMv4K2Ze4Y2YzZ83~+9uH=(Qn9t%N_^g(Ou(ovx&T6kt9=6 zO1acLeA0uhEu~iXntQ5)y&;W^aqtbiM#=O;5>ga2sgu3;Cj4^*TQ`L3wG6;n9z18Q-xHp05vJDdW5rA2{m zkO1u4&);sh{G*i5ejR%m{sdJx!HB$Acpj?<2jwfcFOr3=-3KGoK$Ta^p?tVJX9&cC zd*;6@1jW-CJ?v&3C8PkuNDq?>6lA;(B{rLx@^CT1s-|XE#d3d0b{A2~6cprbcW6nD@$U}%^95O7^=c-4n(vsZV;3AjZA?q|Uk1J0 zmk?o^BRUg-Vfk}Ie3}wL5nyRGQab(q1>Nd&eKRTG^qk-oUQ`z{&N!a)q$ys)_)%(D z^aZuq!3JkHt}~=}BfT>o`9@fxf-MCt5g+jVVvI=r6q_$QGTiecSaS>7^7AL1Y=gJW zvVO&0ZOzOzL$4WASi=8kI_tM4|27Oi+t^08fOI1Wf|8P>OHvdBkx-G8l8%j#E-4Y| zmPSC}i?{(wmz2^2l#uS;^YY94Cp@3yc%7lu_Cp9iM^&9>)g)pl;f z)`g=BP4mq&&U;nj;kV&C*0~jq%$hY!0NSHeVw<9(RnbX#w*pBV_^}yKWcaTKsT*rA;U7HEA$LUqaj zsqTJ5dS%^Y8MTu6l*G&O=W0c|Wa&ZHpsCK$dyTrlSPIg6a1r%eB9HEsr1B5l6p3Xf z&2dh`|7MG=dJ&_040{P|*Q5oLp4^{7@4eZ+ltZ(J8wcB7;GkYae?Fj=5DmpEpeQ8Z z=-=QsuZs5MCsQ7R)^q71h*?t9dMd{eBr6U8E*A1`-H`++fC1MX9{%mx14Ik@8-W9t zWW;I4X(^;HpTRr?R60MeZz3N6ujD;w%kiNfnB0ZpaBI&*CEuM8nWlkJbHQ!B-<+wYG6ZuYgOM69={#Yo(OtFhvM zU2Vl(oKK6FO_G-(NIQdWM~8re!l2YcqC0Ek_;u`~leuGLGb{=rL&S6gGq!%&7+gh6 zaQY5gUisT{HT(?1WHZREjdmk5JWcZLfrpN2*E!_=y+tkjQ4^>JPRH1BHnsA?c|%Pf z>4UdF$T!yt<6HpCol-#%rU#_BYKWK`~!is@eICyCui>~bD?&h@U~1wkLbr} zpza%gZDmP&_gZ}*EkZ?u9vGXo7dRAjASR(LxT6)L!kMPVWSavkMLyXpH>l14Kca-U zEABUF&u`7)OI5rh3W(%g7F)7H#hO?rgC+wnu&OIme%Q>EXA5Uk-a=SMd5oF*LlVy2 zIHyI+7^tNq^od0G+7xDiDm}DdTmL0kBVbRLv;_-{?7uktmQzescOuVmieTt_IARS4 z&)Vzu=8q0Is{AE05~kxJ7*^1c<#Wr-#mR%iCmmcTd-UiT`V@8bo-IAEIj`&Q5Dq)9jqPhXNU;NY)${!jH zJa)Pr_dZDn!7av>@r`qNZ{-8U>Tt zV4b{7dF4}^6JD;j(96`;DD;lE9urI}j1%EQq#i%@zT332pdf!W;!Eg1Ky&x~eha(n zNa;6_`cgHP(-@pCy6!1hVe@vyGo?xL-_+V2<)Xhyh*nDw7VX-1XO2;@&{vKxi638$Yg6Gj{J2~V4V-e=d-2O%Jh)ES z5dlgn=&{h{|GK`SjitAE4s$9G52GYh2%zLsrXq5u+l+=h&bscG8sHWIFHmYdBbLSQ zsR-D2VSR=ON=lH?C)9V2@H48yiw4rBmo6C>uCEED#^^c-Yodtb!Kx%Q+sBo%sTyor zFudS!a482)w>qD~lEm_2D1keuy_9nz#xaPkZr(3q z85!SsN#@IMsStcFO`JUiDwQ~l*Ej2QO!)jAFR>r&CUA^;UHP<@0HhoHyYlD~A;)O9 zQBqIO4E8|-I$-uS_8AR;q<|FM--Wcla+Fb)1B@WEZ;7Ie(jFn;CmC}T)VEc*)*|D- zw+>c)F6&o4ja1i5mNWUb5&gZbljQA5@n#gvMdrKy+$cXwszwT2>>lJoTwOU`tS&dr z!)U}K6H4kt%A+LYz)gCdP$aG$qh@>|KltyCD`dy$M}mIzF&!C!QrDD4*S~@E_|Yrn zTaMWhq%v)oyH0SX59pnUxCv?xE$FR%drIh6Rf)-B{~?Mykwt>S$M!|RlKwZ&Q}I`O zrc%WJat=Pq7|vgPtf&2ADdKiN8G+0iQcy|{#d>mWZkk_c^Ay{SN7LLU-Hz{t#4@Z& zinvYa0G`vDHHh6f|MBgkdy+}t@=mkAZ1XWg_@k|d9NvLBcGBZZDa2RZ40m-x;#t=B$!_m^PLB zFYVOBRiO7mYWbQDP)Nh>kX~p2;Y9d753ybQlU4_nVF=I-Ss{_*hf$H@2R|C}M zYXJiUk@feVv1c>&5KLgp0eJlp_L>3dONMv6#GHZ_ZnbgOIhc|(RO2W5^1PZGaQSjU zt4Vm&2I_5#zV%K6gkwOXnUir4DFX{^amCFf`$K=Spse z0x^srps&kYO#D>quC4@@^T5bX6i#wR4zQ-<87@I(z$fO+1Y8KtN`s8N0%rVkS=(xc zc}vPg*VQ04l#&e)rnM?%^7pb!VumM%$ulR*a(ACOe{20nZ0rywd;iC1Te@W_d)=SB z3Bh6FpmWjj1Z%^zuO`c6Yh;r-EWlAfd-J?9yJQl%QIBQgh)Dh#V91r+Qg<2fGyW*_ zCHG$b9f~djSKraG^8SduEL9eCO^?cK%S8O&w1Qbx%NwdV=;gyxSJQ{f2?>RWp{5M_%4alze!R$vAo^giI#*xEq zlCsB*$^I~FBvTgY3Vwba*Ac6D<5Pv7tZemmZNb$JW!w9)zvpTs_G}m@feX_+lk1Zv zXj;OK$mPP$Q+f6tvb>+baIQ1iOcbX4j>YD*vj$fDHyE{)*z4$gEZTTz`+SFYvc8F< z1LQtOH<`MdB%(Y7A)S~Yh7Y= zsr#?3`3<6a2wtY)@UJI4@2#a+O*oza++*!+4UwzElBBSaW=&F|6d(z0u zK8ZTK_p#==VFn!umna{37%^jZ&J)mm6?+BJhMC4Xi*}GOP4WNM3!di9cDDHjCp_Xo zw+6jidtog)mKhAgZ0qbvgYx1M9?fCVY={r(eTf>2!FHxcn6Io{A8-5m3W?>gCCz z#Hst*dMP@??Bz4%JWQVPi5DdrmrsCM~$&?-eXP@&4=$7clY!Yn!})APPbh5Pt1uf(zFcB|w>EWN>e_*lLHueB!#8`w!g*d?6-GJ&WY-NQX5D z)|GBV4jsM3qu0qpUP%IGvt&tW=!ep12X)ud!U1TdL zsEinZD+Vh<09!cD=!8oTsNR3yJAHmYv_h~x`z1>lm6M6-T2O7P{ zQb}>;R|aHvT_XiE_~$n(u>(Hu4gH#tmzD=rQJ5J*5g@WQdOHK-pj710f&dQy%65KV zi!*)V(W;=|9J$xzD-Wm~_x4YE34p#SBbX9uKnzsc{Me8_LsWA=fw~yPtcywYIBm8l zNK@YW+pt6-_P$d5cPH5FevdW7bCsWu9dC5Fqg@QK5vLEo90+kL z-{~+Cb@cu<5UL~5sBONjt8X2Mk%-Aol_C0S<+whu!5Y0ona`f@@sDk%Kp*$=^5x-I zod?0sXwAvok%7W(X`w7wiRMsQ@DlF4!}If^b{?y88AidMu1vG>axS=s&(9^P-`j6$ zJ9$h003Q*P9g+mI-vLbCy3G8F9b_SAkL6t&UMG1e@)x@D^Zt>ZjAn%=Rl8ckv#iuD z=ikq4!#n0iZv+h1zR%)h*m4{5hIYI;z5{)+NkinBc1)0Oi3jbM>sa@n7S&7n@3 zURUmrza%CXpF(%1kT7-5;z-sIi;gXHWnnc6x~2v;VTYiP7dh^NJF{26)(k{2-nF2u z94;LyL4I9$#-FtccWug*P1h)RdXVICPlQ5h0hBf*g!RiAr+ykac1(&apDkG3Oz^V4*mL7AHpzIwTveE5B%8z1vtuDuT?zE|4@ro zR+TRynIY6fILpx9wIEJO-%b*E%56yH8d{8s(;hm-h|>@}jsaf5tx<`T7&W*6e#R8lT#|nd9u&F(Ga)k_Y$vaOGc7u860OBM-kk7MB&C z3HTJk!V*>ZLx$ngn9Xqx`x^Ln+kz>~Q#n{a<41=7&OAGWSRYM$e&+36tFL!euOC-= zBT+zwP>2>X)oTQk$%^I-Ln$jZHoM)tA;Fr*1MY`| zTQ!#>QJ}gWrZojRXE{1<>w-O>x*jNU zEk$?VJu`6HjO`M1;VOPpV3y-3QZo`FrKdyC>|T@H0eu8*IM|NBk!gJOdDYEArV z)4eH!O?H{7=c)d&`ZH(QJQK&#k11@tqhk{qBO#C{Wd;m{8#kG{-y6w6=)coyop|)D zkzy1J13395bap@WEUqxEfJHQZA-~x;<_H2FExwiQc~xi|@qYns$4ag`(+|Gb1b;)9 z8iGMh&L@pX{h7qSu$g3-1u%x0_z(G`2a*IMmz28tS&P7f!j2)&_=qzfiVTl^do~0& zajBCtWQC%j6jq;p1hq1i)i522v)(L^^#7u=|0MJWJ;AGNKTymMV*-b7ZCzNJ(q}gzS3wC$c8*}7kPPFif_hwyLf#_Y2Bdz^lk|WJU zh*p7HRyZ+ggZ(1{?_Ka-ZYUJ`)t_-OZ4B}cFuO#nigO#ogjYnqK1cwX4SsT)1GL6> z>5qXt2;cHUjsh@Dve*9b?>aM;8GltwLgs0JKJ{0uNywH_v!!4 zWLMZKgpxKSVU2&5A0IybZhS45`EuBcOE)I>$UqWv+WaZ{tA6J7wA))zX#96m#k;v3 zpLF~9*_&tlS|pXbqJ)zia-4fp9{+o>6)T;RSGx#+>9R^5|6Y2eHT=rTGA0Slo;>4L&bRfnA_0U*2@V&uLB4@a?-0AsNhGZYD&a=+<^t zzojaZe7o`5p4U}eC_-0|In8!GPfRx@c3;5Pe-g6RUwd2Ps^((X;0v1JY^NMjM1;`- zg!$=20U`2X4rdbkhrx_Hr?L0j;-O2|2}Y{q%IP3KP&@X~^J1OZlTaFs2gCN)9_ifu4%B4V!s?dArq#a`e~?TJKq?CJpGk9KgT%&mfCh%h!i1Pic#|D)e~7-8j%% zSNHX*w-T0p5K;Ryuj*LGp?95p_nQr|1e4LHki*e9bVJr!9y!3oE-&J$sH(h&{sX)r z#?4@v0HYJ^QNVO}V4LU#QQj)WXr}xsLQG%v#%1JJhvIV`iocWF96Fk1Rz^d5``tI}B)I2QRTV9)-0&`<=p)I%y{JXmP<^-bR;F14Zudn3i1sSou#~h*v3x!* z&suoRR?AmPjUG4_IEP-G8#kzzZszvx7!FskVw!hzPYk-il?xvmTy8r${^Bk*&SUuu zpvJl1hdcZ7C*ONd!9G<`)PwU;#IpCzhk#R90%QjNP3{uoOGXF~y6^UF_s z1*!v(wX?`ab8{Jnwb*vM# zlSAfnH^`t2+z;9yp;)t^#RvXu{Ys}&Fn7Mw!@;z3Vh;gS!0vWd$@j$riMgKvJ`p?% zEbnG>4ho7Q$y;Dzn#60^5OKkbnK6^-*~?Y8-bzd03#5-C<8%9v8sq!WpCtzZx4XE- zrUpMOuJ};_lZ(Ym0>SIVSu3B&Le}Sh)>pd#&t;u^uA|sLj7;+=^^-?-l_H4Q+NaYnL!D2o19ew)# z9d)xA_4)cW~wYZ)Bwm%q#x%BhccH+suD8s=tr7Il8-0V|y;}1-({#r9*^s zu7ekYrbm=e4kyNo?ttW{g6muK%3l=!^5Z(KL!!j0q%3|&a1g@esVD zrzP=Im)aWKn!R@_TBsy)nxDmXN4DoqF=^~1MFnGyYwi-C;Q%4b*84tdUi#Ej1HBrc zriPTjB$$NMB4s8VKR?%bBmPFws2sCwi0Y@{yh+4&<}LfLLKz?HOe8x-8P#e1Y3)(b z7PfX`V6e$U6OdN-6EE-2P@%=p*5I{dS-=NBOV2lJ&<6LWndt$v}cMZ`UMld0i_ zu>@q{w)foc8pr(U@Nat|!tY`o{8n=3LB*`x@@J7@+17W3j3K6)6yUZu2u zO{Bhbe1b`fxQJuJ-M)2IVRkiQ@JWSPVhAG@R zuAK>{oHx}l0MjAI$BKCbf4L1e7lEbvq!nuiLsJt#$gN?dKftD%V*O2@pdll8%CQOb z?Csp&GDr#g)(ZcIDi(g!c8>XlhnP(`R1h({@UHxNbZDP?dBJJhI&Jbj-=h04-FkLW z&);-T-SF~$r^@Qpr&dZMs^1)sOp2`^$1q%IC8#AmuI~FwlC>cXy$opk>=4AW@-p;z z#$<~B>hYm~iEqbB%T0w5(tuBws~i&h2TU`FscyYUGhb}g!l#>NUDM!eqSvCun^&uH znyZ+Xlnj?c-|~g20=*X#BR|LKezAX0T7>=Vp{O%CRl0`A)6$b}=(+RZDZW4sLI+oy z0ZCtDD=lAe^E5MyythuAz$9O1uL_g@X90FgJJsRrih=Fm4CB|uNMtUB<^fsaTo^7j ztN?Ps;oOD$h?bzt;J>|WuZPoK1AYW>M%v5%GScRSiF0^Zs}b!8_3Hbt;MmMbt%B;~ zSE`E5!q9?~lCy~Az$mubn2V~V8#w>GulD?R;hWw0zpgv>fI`a=PEHECd$kRFvSREz93fys3niuCY=DfAUOJkG@WaI9 z6$jxekO85qo~c?SH&9XT}tMIxN(J_y-s% zY4uS5h8->CFqM7e1EHO7S zLgn{NS{{b%WKYbSaVN0otF#$A#k`s9?hN}u(3k*M^lO!U@yg*;TVmV^`+oIir~qC8 zUtT}DkvwS|Pm>rHZXLYqu5i+`@y+IY!ey}NDxxdQ^C$M?X+&o0DYHy|PySN*vgr$o9w?b9NIoth2!Sfs<)r`)LJ#8$%NKDR1SoMhTPd*~YLuVBKVd*}HNHXz&K6$LG>6=)!4 zpa+J(Hq==i1K!4iLpCB+_kjOwHv>iPj%-L}4{|m_)Hy?k&ZsdjN26Q7-F}AU%Q3Hb zONcxP^VOo8eA)Oy^R8|hSnv+iO(?svHifwie!lF~2kL--kl?Sm0=15P5nNi@s$J4a z7MK&$Ps7=J@ZH`~f}b^SRWIMP9HNq^W0IDr{PF6J{Tq#B)2Z)PtO4btdlj!&1kYOG{ z5j24e0H_Vaz^T3WF6a&@aQ}HFseIG>V;Bt3+=s~+2enfI6YI+wf$Jl?NKZ%N8g}<} zgIzBJ&rSQqjc0H7Su}v{7mH3%pPh`YU#jlI2QS}Vjcpomu-xAH9S2l?#hTLO>L0hc zL+QO2(l-ffCa!(4;Z@v3L7CSO1U%9$po0u1VK4L8q0GFR*A^VYbVNi0JChA{GDIQICiUXjKC zvv7GQ!<#VQWa7Nv!CtAShu;DDw}`yQ&lf%hK1-tNK1vVXY~WCk&3;~EdSU*!-jgMn zb~M^5=73RW5@>hinuHL4BC0XB(KbOdW=G+sSl@W>W%IT~Ox`wwm_x(|mYt`9=@e=W zJJQe5ijBSC0@-k#%l&xOPY)^?DhbK2@rc#eQ6%S$gb7%<}2$KX7{xpbh~hi zkoVrw=46Ij8%(^$u6W2uk~ zz~-+9qmZkyvnGq7C}Y8+N$1n+Co1YU-$ceI2XL*zeK5JtDn4+y`!5g}K$#-|4aJ0XeaIAtac!1(x?&bsp3KS#Ajs53cvFyf*|sL)W6& zP=0`;w>W^vlVtS){>-T!p*;n5u8J=u3quQ)0rE40uV@w`H+*lMpBk-cyp&Zs{|NVlXgBhTy>NK@VxvMw4UM~4^F77VX zH$Lt>wmc%@zbzVi>ZFiePg@&%WFE>FU5`vqMVXTTF?)KF>7{pMCi)`T;`=(-hmepdKGTNiAa);0p7_x`(_1UZQ*w3Q z({VJKTuO#DRI-M7zVu~%d;>G$E>Ix9C!Iwog#5VCa$WHzz|REMz-le9wBsW8a{#|~ zyIYMGBmx+=>ztsCh;UI@##86tb?SC3Kpn*_cpf zkj#JZwOwtkER!-m_4&sA%7jk^bjM1mmtTfFNZqodt72v+gr=E-ZXk z>Gk#E0BW3>0DE_?311Eq)GUP^BUSoDB`MXxY;GYdnD@9QAe%htX&#a}FJLE%GA|O{ z3%n7+GNSShfdq!*SOn&LtL_$OK5`Tm<-QOJYOQyd5>6S}Xh&?({Yvu6zt<)#`dRuc zyQT@v2p<##GJ>*yXb(-H1mf^*J&4piHeW-COf zVKCF(ZMnmgY0`#``M30_lpD6hSs$3Rkk`(Yo3!>8p5%^Oyq~X9lEWVwM621FM(pOb z{mJ!bYF;O@srzsGI<=czpA?tQ^fl_4N{SVOk)>XXq?^LP%^IC6CBl|U`e30BE~>5vbM{G#Tm?EtUX%9xLD32 z>j4Pxg++``s7Vw?0IfYyCB%Xhr%Q~%g}gSVuYeS%KCPm6019Tk&e-;wj~S$*23RVo ziSn$@={buL9Gb@7e|pfi%s`l2e{czMcTX60i{q9zvYF_=g@Mwa)g_S6`&G=)*?ZJJ zdEUYGQ}f|mK@h&Dp+8iIn@y^C_ch1wKQYm0|ITeeq@fb?{6W;eU!f?=7yBjC6s-R- z-Fm@pUh(W+ezwtf4hAjYr-Uq4|MGux9pWrSc)-Z7@V*4?UPzN7JK&+l!^%_M0roH+U_9>ohQ(K&l{Fg&s?LTiw#s8 z1vWU2k=c|Z34L-f$Z_rR>%do>D2I?@zcIVVZt}W^O0A=?+jQcrFKCS9W5jySP1AVz zN{N~i-gkM%`fG7Dc=9g_QaMB_@gSZ~Tof0pQk8Z;)@D7_PkB`QNR+hM<(@QuKbpCK zO=xS_Hb`eH_w~2iotj2$+?33JYs#857)^N}TC&>$%P0Mj2>+@1r=V#xKu3iC^7NT# z5gaFoa)7Z~X~`X{7U*FaJn`Rrj5u<)q@R4uaeB(#aMWf5;QmJHL50s5xpC&3u9GYW z9gk=h20xq3s*}5Uj+LWfyd57;LF07l)$@j%wQsYmD^}2?TXIlL8|<4$m@1LQ#chhy zfd6nO7arZ1eWE5ugf;E)qKZN+6dZ0KpWw@{nM*F9vzBDy#NqarQIx~?2;B-fvBV04 z+(18J??^(-!|=Uz6Xf*(@h&$FI!3=vi+Vw~4C4h&T7FsCO)XWQYMF8=wX*MfFwJd{ z*7K%nN9ZbrUd}%|orh^Sg`oUKt5J)@zjfYF|JdB(VOu;2<`tvez%Re-x6_?J#l03g z)<|?qn;+McWzMP{5zM9DFS@(0T%_T0$%etKUf;l6tCNDq0tz7M9tVDb_eMq$(U8ZO zoM7%?4*+{OLya&T%uHB9A2jJhQ$+^>faakO@Q}2R921Y2$b|QD ziRp#st0{nYfW6sgH>_xD!U9tyI(Mgap>%>!_zz3yr)FO{u=Hc<5as>JN%K@ln|>HE zlG^=*%{GZ$OYgp&5X11_fz|Z;(${36l9izi-PMq5vQTQPOJ`C7{|c{8l6Y4S17(KV zneTTA0YNYm%@OYD>+1dTtiw;a0x%anK6~;oHac8X{d6xgJm|NR-T<$0MZ^g5p&|0{ z&^GMXul~RJlCAeD9ZrW0(Ub(*y+W?tQ4(N!qSOeL5Bc_$3z5gJXmYs|$}M72a8=Ya zmvZBEB0p9IYF59Sh}en0iJ$T^rh9MI?eSj^tuwb1-flbKUKpC!U6K6c?rT@&Sm_5> zaxJ2GAOT>Cb`!aW`z~#QSUiqxgci7!oGmwT0WI^-UlH#oe#I9UxV|m2d z*f={b0|tnQe+wRg>%4-fXppa_LK0MmyG>ockJIJpP$F<61cDC79AEyjB1^)|IlQI= z9vB$fabL=!`K@@FxstK3#KuQeBhh~PqMKFk@4Nly@6*B1(OuKAND>O>wAlwS^b)&; zTZRY8eRUAgvVW-QY4G#AC7pSK&N5>oM>$mToFg?faHxFu;5^9cH-Y<4>FzhWQGO$I zM*K@vEktb=o3G3+`wi$Hi-(vU^+ta8tA%D^dqu0L4{@q`l-FnstTRuYyr3RiA zCO)CclmD%AWKKa{dmLs6KCP>j0B#NXi+mUpBR&-kLJx$L6k!*Bi(R7q5B?rlrT`A4 zQJdTnuzi9vf9UJ2GXIb`vN|UEZ1x|$bsrdNXb>l30*z4-x1;b5YLP1Mvj&qT3TD(w zdP^sVew?d;69v>2F|!>NB(br@uyo*5Oq7S2&dI2 z_QLuhKRM=k%l2P~CiESM7+#AW!rZSv%>M5HB1&H)Ye5%_Bo=hB{|bAeSrfPFl9^-L zLJ58{r|l4&gmZ{R=OmEK_@|BX5=u1%d=k#IZ%lQ@;$sI~s~%W&H$4s3yCquWtA6WY zgOudY(eWQa>1UtoZo5r2n3g?OfKyAom3a&5{v5uo9n4nvgDS^jbq$TqjaAEK4zYt^ zy49-;7bD~f0H0S-AsUnipaGdqSjL*eKJcUNakEPlW(P~@eK$_K-miDZVmuN5g$yA( zH@c1Qmha_Zh)ADhr}SUXnKN{unYh} zeaLy+C|EDaVr(HmJ#jlLc%C+o*!`7cRfbDuOy9(@e_Cp7HU*s0N@* zx_i91qNGTg$2Vu(bJ^CsO)r_p0|PQ463Gy?oBt7O$gu&gD&f~|L^-0`)EF>AX;Al9 z2!!-xW9Hot;BOa>{WmQf1NS3~wyCF-!pcxEaRyrhoCa=+G#EeJ9vHND+Tt(^ntv?X zWt1OPGLQoxbZb5zqqCCwVcXt4%tW>DM#wlfN(ik>{45pp@%Ix);NE`C$q@jSgqoj5 zEUI)R`}0GySk?})LR2GTkViE#0WiG37o5wT$#GOXYp4~1?KvI0{HL$mpPH}hvS4^< zz??m13mQT9Z`JO?B$C8moW64>aVWqo=p=P(4#O&(URPxDcL%iZf0*(nlV;;imep@j%HPEW+d6Hk?*4ODp>sJxDGw=mR@5Xv>~j!Nq9f|$>j%=XX`F;qJ+ zJ^m=Ta$3`mN!6Fnl*k$Ztf{0BKcZ(C2N&v3ccedno>igWQ_#id9Q0S%1)dr7Y5m$T zmFc~DgWNW9@r>lh6d}}x5@9C`Q!~oZx#T1b1r%<_i%CUo?wfGmfcc^KLo>_wQ~QX& z^m7K&3HYrsTnG(D{q^)(S#5d#A(#=`;}S2r6rEyi-Cfq%D;^z=Aqr*j`WaHiU&fPF zW8YeD17I{utGSD+H+T(kS5R*kiZ@hnX21gpVFd%_v46N{uwpd_WnU8$Dyy z6MUTo?7oL0&~m9CewK!HV?g=)zex2;Z?kg#tI8-^eV#V->@ zL9dUnn_S2kf}#S;$Q-Pw~bXzJrq!0e*P~nvidvq$s1uaiJL`G9|!sL@g-zW+?ogX6(3LeT&2x z)a{>dxp>GOFK~IwJYdW$`Id?nyZN`#>s|$^uio>)*;bnQmcVFgvtAH1m9yOWx7|J@ z^h8O_{c`EC5Mu~P5*Hj#Xc^`*fU*$3XForM~v%bf+R>1v0e6eOel zbbZXdeTD509WEB~k&(CUpA@N9jd^sI%Q7WNWU4rQH!Z55C=dED_isLC=ys&dr5G=D zNn&yahnmGE#e*;5&Hf)I^JmrnP#BcGBV-0X=UzzdQ;G%G=p7CxgbSpG9RJm>d`5S@ zd!2RA<%q^iqw^N6zJe=vXxw_J0g}^p6I2GH)!M$>+Bg+1zUm_G%G3l5{N31C!AFjX z2&w|R_%eRMK7Fb2mx4Xr!(&T&u#%oYk}vi@={}aBV5jPrt>CI$8{h!kIUaL;W8)lV zYH+o^u6hcx`vA9jc*Z4AC14}n8idg~h1S_07zI_glyH=s3S}f%(Do{C-xBMlwaNf6 ztsoSbYrei=lhOB1MwpS5M){eEc{R^|H`dU8(o$HJ^pNf};CB11itd3HpD*M$i||RH zSmSnL8XSj=*7UF#m6ZLs|3S3unLpQa=bj9myGq#83!Y5vk|H&=E`r~&fa2-T4C(kV zrSynDyBD3@V3XCuLdOcoQO8C4d@bC%CZOfobB6(?xW( zV2rzo>=V7RF!YT1fB@f-DOR$Du7dBB+sQZ6+%6gaF8rC>)os0&uLm|#10bALC{7Ev z{cs6wd!ac$EG4h?JnW|Mf=-&00_u4ar0z?0tX*pjZx*M1@UnEb9ZQA5hG7`N_HFx# zI1$d?5C~XPQz3ix7|;sL&&p%k$ErTzO%JF0+=l)FyRPxrewxS1Udn|$N<96viS$=9 zZaY(A0?Br)#4st`K&N^PNP zK=#WYeXHDUICZU%xQOTcwO9L(E|ABFP$13{nzrKg_N>Me?c_p;{u=3pPCKF)o{FPV z#9f)q%=A8j{0GfZ{0!Np#)RK^{bn6e3GTdic3}R0HD-QLRI|X_`83lYcv-t=>Ej z^I?7IlAQB3ll1Scabg9ygUq*9WRIbi3#{lzbo|OyRFYVB2>AdW{qV2}& zjnXF>*`nEC9Xv|x6m*CIH@5t*^xo=}V>y?l^NBKV0B3VNck$hcbj{GDhgUrJcjP&5 zx-npqG^wwaljoQWg})L1mHEevd1=@rb9+uI#?8G)J!C`DJYH*6~FC6 zmF99ZVl54Kt$MgsTCs+$UU+^~Mn-6pXH-*kj8%j)(5RTa$qCN)$mzAme5o{b)oj1p zMTwa}C!pqhxNHCYdDuDxuR2CR%$Qa=DlY_RK~GxFy{5=V=jAC!Q0FI$6Sm0%WTy~r z^9QwetpT}B;KjcIK8Fjvzv9>IZ&Z6u#Qfu|%d9WMZ83Ux{2-9^#B@UHx09~>U$_2o z;=g)a{;FDmyx0h7+&~_pCw-_fUIA__80nL2*Ma*o^vsw5VZe@(6wKvI+NIe_olZXr zx)j4xVJsEsVBeXTHbBaM;3+ZXI-3cg@L9G6Y_%Y`+{1C|7(f1yuwzgH*J`wiUyl8% z6YKOr;O;huh`R!P%2xI$bIeE#2RAN!34nG6ye}ft9=cq=^v1L!egK#@}D4qb~P9UHLLVnkQa z;;iwqV=7IXCkr$y?(7@8I}*{wv>7UH#q+!cjVZ}u`B&wWAxlr;tzoTN`GDHOvg>9m z)r)1jfVO?|m*dorgu=`j^2rU;Q<|guo5~a%imBIj%8<3k6&gWrThN{zS8uPns{kFs zi=ciY4B1jEi&uj3sHr)1A_a~_`?;!0eD@vBI+!~d@K{MIUn*#&RrF|q&1g^#FjK`n z4h$5vYrvMNj=bIX&es^D#4rMgv+fEJ!<`g1w+(?SFTs6Rwnw$M_HID)&XCi9>$EM2 zHG1Hol{=(739{=HsAF@R7}xjI_It-SYnbuXLaZ!|1jqwlU7`bD<8)pH>X{FV9+2!< zJt2Ga6jQu8Ou>OKzWoHOKGZCMX9CKDG{3p%#B2b)FRaoq@3fZx4`ja(9#P=YrE%g6rI`e<1-uI8+XJ(AC@B23PC0kju%^)fi6(MBG zUfHvhn6VVCM7AP?qP#=)EHko(P!cKGcOv^TGw18`!}mWpkH3H@xwHNqVyBUz zr=>KiW0D!!5;3}2yraU>bHk=FT*`lGDf?oIp&tSBU7 zJCjXN*t}4y`ZW0qC*6hLnHm6q1*cCK+tOvu>{&3A3Xj5lC2+)>y(O!Ou-kYnvWeBR z83!1~TdBm%?SCx+PqqHub(?M;%y3 z&wn6zabYTnN$O%mh0ZJFFu93d*|f%CS5ov*_4wFRug_Bm2%d|>7JrpN)Xer%9c`Dsi3-f z>f;pgT)G0?>r1=p`BR47v{JZ3z#Gz(?OXsrX)fei5F4# zy>R*Jq_N5KFgcoz&#~pvqHRQysh~TZ$a#KJx*yJ^U*99oJfRPSz!lHv74NpZ7%^F5 zQURrEr~aexjWW8H>YNcbzd=&wr$4|*KfEqq4d&0r_kuOvb(gxGxvRWqULgIlTtsr@ z*CwSvllwc%dspck$II7LwY06oOUjEmug`dN>w{EqF|pNVQY23|GETTgp=Jh;$Q8*R zR<2s@WG%a}gwrrynP2|d$g1Y*J&aS>bLAzS+D~u8RfFL0HZh84sq#7-S4UYu!9MQC z6CT?`KUb2Q!?ixijMpkB_Zt=IA?>$+RsYq$v;-wpfhLJlzYjLUWUaw7`VmA~8fXgL zN>#o++(ER9-LhXASzgtIf7%@+vqqh@;40!lN6TmU&5-{%(qnbh-BGV*T)Pn0j`Mcz zBH!?l91zPI8l=Zg5al3x4(6f2_mN57r0<=dr(WuSjk-h>Z#*{SiTlHaXel|yVI@Jb z#V<%SRm@wkd=xCb4z+u7y4 zV~?M5J`pSt7l7(cyTv3)gFHpfhmTxDALXm7f>1fy&^CfE($4=9NxU1E6DPEp^Ke@t zl5{q0mnfx7&{bq~{_J)HAFC7m2;4qC&W+VVto;Z+#yNYSD?(~S4UU~_uq%Svw`P++ z9|X(cM3)>U!nULFtPh!_fV4f0yPhdzsOQm(%omiEr<=-vC#5SINDf0&E%z` z7CzvLXY{wRE}yoKFD^bhjnmz>_d%8`cHOky+c?hyD!DQoj)t@46^!uCNL;wgRPU?q zJ+1J7c<06w#p#oyQBh5cIfaNw`cMAxYa0L7)0BF)qE6AKuv z1cCZS?Cq@-j8+P(^!t*OXdNK(91eljl!sJlW1T#AauCCQ z-x|xVSjRN#@vYVwT1f2tcRlAA3jY`?&lmajnoK+{8IQz+VL)P9!x|nwn@$cEm1bhR zq+`fkX&?&Gg}KTj|va&rF*u*=p01{OxQR6jTosw~ynf2e0UX-2DN#L&@epI=p?@zkjh^Y_n0Z-=}jt=S#~S z)l$UYP7ZfY?7gG8zSy^hP1<>k4l76@cW>xoX{n%K*;_;Oi9tyY;q=oz3p=vf_z|=E zXNnS2Z>uiwtcB(r(L)&joH(TwaY7UB7ikJclIg0Wi$JIrRmoh^)aat z14l)WUOJ|71p&7e5CdpZmjm$WD(o+ajt=0UP1yi6KpgT~iu- zqwpI`JJC=y3YFd@(|A$^kz6a$Fxz-)|A;1PWrCncTHRd{Q^L_r6;F6k4`ta(;30|%>s5(&L^2aYQErY@_~Pvpn?@%D)SycN&4Nk zqm@|~>#*^ws)pZJW^~Js5|>w>9>j(}1Vvypb7ZkF0vv&@??%%VzmWsg)Na6oYpaT^ z&TL{2Z)*=cJn3q#PyH>hcE&^l+VlG9dWO4HEE&EWxQb(i$9AH3!l>sT!Uy@E$PFI9 zdi>QE6fv&5ngHh+8INd9US>FIahuoD!1XhA(wid=Fm_;Pq_ zJI9@*c9d)6xH`DQ1-t$LJ^ZzB$9ms8xclbb+$TfFy@xsFk_loCO;4}f$14dov^u=P zAmbWxM^(QQ@3bv9oItPg=E&IPJrk`AW(U(gif`W~tz@688LpMH9~ljQx)LEheZ&_j zFJr2jC-T+s2m^S`UdSX<1FIS$RIfv`dQN4R2e%Tc;HcF7VzG-ytvop15_$IBjNH}r z+cw8|jzfSt_Hx8R(*r}d$1qeby*I&(SDLYd-lSG!k-Zb^e1&bL){jujoxUFEuTJ4pO;^u^+qiv3(OKu1u0|Wll@EO(oKe!=bPccEeYMcPGp>X*#lPK zNfYVJQR~{w2|~C>+O~aPnE1U-f%vPbtG3A8C_+!k4b`NG2iMbUr+1SXpSVMD=K?$x zK#Kox!-smV#^wa>x|?dn+)187xb!4k`+r}3Lf_wy`D2mU?RhHPLy#V!cRqhP2SJ!1 z@CcI(Td999!?$m5d3P+Dqw1e%mou&;dMU zS&JX9MueSXLI}8{l5qXkVc`{zQ&N~^F10)OF)!xP$+glFfri`iZ+{Y{nq91@#3ZfdNPndlbJcd)#`EPPG_dS3OCLElkp{1#vo^smmFJx%Ph4LR`qMZG zmq-rT0l6M}BFX>if4rhGaU|J;iO^p*KZK9bd~9~(?ckLQN}Bc1y}x)wNNSTcYea=C z?4;4C8*X|_qG#L1c{hxU{^o41bROk`43ImC;No=WKH<$OP^43~lVU(wG!RBIP>H(- zCyS_?e3znm3V^@TA1W`)9V_IcL){Pmsb9#W=&gTav?#4_-?(q`9Uw-z2)sXsxkjvq zp*U%|M1pFk8w>vSYi`(6GVZW=?8hZ3q`1)Q@E8Xkz zLUq7KVgnr27D{d!+xf#W0M7<)4Lzh+xLi1*F5J`;aP8;9T*q@fYc;vhoFc>oRPXpE zFmj#?KZBeXI|8wy#?2LkJE8tvG9I4hY~`=k@B{p6W5)pD=&B=3yyDn`Ty_=jYS^C2 zoN>%n?%O|_=mJ&|DAO{QOcUtvr5yoH5u-0TiA& zrR}oz<>(Vd@uNG{}PZVZ@3#7%OU%>2fvvq0u5_scUrY0Sls>= zS02Cb;nn;tqV-zeO#Xu=p6SX^iY`JYSUe0Sb5h5iPQmK! zTPHeMSz=b3|AtlU^=ZuDdNUqmpS|!dCeQyk$yW$5*I^4Zdxm;o2kPLinizgQt3Nu- zTnvATfq=o=039}TEDxavad|qB^62xO6bvCAK@vDBg+Nf&C?k8sSH-UwOEkCD7M9Pr zUizkGX+*Y-j8UT9jVH$;sivyXvvc7Bc6buBJ$5jRSPnJDe?)AZqCowk3O6zz5lcsI z7y3(@{uKz!t_}J)y!gN?SLEqHf@bY%rdf(8;fS#d7mKLvP7+8kOd%O`CF(ggnm|{sq{1Z(t$kCdodN&IG(qY2J(9N;8E_ z8T9^M|CU7Lq{2s^JD`&q3K*__h@*;CZincq))D|HqTG21Ok@O~j(l zH6-#Iuzf_H?y{zA5Bf=5zv&G+70d4O8PBc_EY9EqH&@qvDffhci)injxtIqsbSRJU z=OhZ(NLO?9YvT)_))~s&KfeX!p9Qj8>s!%xnZ~qR^us06=^vliyWJ=4%TwR#x3%MM zS+A`xOzAs4l;b2NqubL6r5}_>1wZ?JqW!{2_J%$1KOtTHP)Y8MZuc!@j+k)}%hmK4 z+F$G8(=kBZ+c1T-e)5B~AHyA!2b=H1dT6E{L{{fNPk)MBqB9hVa==8KD*aMwD1Bh% zW|u$)H#|; z1GUy_*3q15*5s*%Q<`X?hjq8u_6Y3_?=bUbRvpT&h2E_U7QHmrXW4187Zvt>`;@+| zfbeQ~U=Rw9sVYRpB!Lb^@R}}v*XWQi`!>&61*CTGK^ zztFO%C&!$I5zfc=8RsbIKE@ZB7I!>{Ui4==FoT!UI4}-=CzLA& zUeGGJk-w8_U@cDsd4e5KLrg~`R_Xty!KZ!2{uXp_fYTC_kV)&6xPP?Dhh)-eF$ zB}8nN8p9m^6|&4vuCl9iF3P_`jgn135>&=NLHxMecP`BV-pPe=QdffwCcNe$N~M3r zlUa^(dZH&H=aEQ&RqAUr$rPF&tZfsR>M$;vyM~i^^jvB32B29$W{&+0AM(ZJa)1e_Nu6=NrFH0 zV=|6>-z!|i*jgtUu+?;|J-ox-J!lIbJf~$!>Y#=NtssV3ZIHmf3%B-V9XTzc!TQ2sTVI=@ zfMs>!V*HyRAQuX*(5x3hHOr|L-XB6j>Wgq)+?H2zM5L!+mM)ghomUEg4kk=%`|Exbi zbZ%R(MuD-a`)iIa__~_dg>JS6J<-Dn%RP~v%D1`7=tf$Vx;^+rbe6nGNp)=1s|RBb(&+aGiB-K%i9bk57vhwDkG z*1YtQ0|8^G^}`=ssNuK--W{B{Fz#1Wh$L9!u?PgJpJB9kx;raL3S9q!@O~sh*hEdE zKo(34%>_5*NE`8zpsBIZ<@*Ic%NF93Eq_|DvVpJ5_B=LNKmAprh(C;CtPvA0>+&}VxINkr-YPX6Q0Q)2G725=Y@Gb8 z>2Bf-q3!d#^Jnqy@S(Ub8E`t1{_~2Ox|G}YEA70;_Mwd&Gk%{5NV7lfv!Qi-Er!|( zwH0Z5Yh(3dI#_m*&-H=AKzUQ;5{-p1K;S5-+?O~$?C)ipU{HIKm#XvcJhV4FA(tV^RVm8DpRkiN)GbvSm!@7QOREy;FVV*kv`MJtt#S+ZG;mXTljh7CKn=i+F1$a z&EHAB4Q`o|xYEHHwtx30nIA=g+!))xm)(399-Ix10pVP^57AQ0gz+B;`0VoW@x+_` zPP9V??=vhZWeq<2&oR3``oGNO4MT8oZ!hQ*@pOLDdBo_7cB+80Hv7$soLvm_;$Qel zCNih(KD-XSpm7sD_AH&fJ(m+dK74%yrK#7+A#49%QhTgT4puZyFvjCp4ck!y(|OUL z4`tzeLTrbD@Dz4K#sJ^n9Ht-tame*zb>DCv!&OKwW+8Hi6-vOG%<1e;1L=4oKOL*o`=lAjU+BCniS+3>?Y zALIN+KI%du9CShB?Svt^$&AFx_^QK{{BP=x6**C<;mzLBjj0|A`IjI+C3b%${PH;R z`axK=$gm?AF9|pJb7Ps!aF=3CBGM#M;mMulakcWUMt8tXx88s?pTN;wxGrG;!2HO~ z-LakRziqbTt+bO#;2Stw!jzFEE!!v$-)R^yO!o~qgq?>%?q~I@q9?Nqy_V)iafo4Vu153gJS+`1~BB2X@ zdi%dA^*e3rMED?iEa&vg?j^Xo?D3!HA3e&v6A|G82UoYQh+&21On8!5z*vu#DNrMm z$4FZ+wINV_T)qWK6*7rCf1dx1^%H(n?&)pt_|L0BiF<64$bL=m3;-5V9X%gGxwdT4 z@PNgF*t}Cyv%%;*|6EOe1GjL2yp;tC5e~A(58Cg{_idW10A1A>yUn5IV5nbizVkMz z*J^U904?yu-I#Xxy4|2xTsAs)#z)-bAe;J|fBi#af6l~j@x5STo5JLc+vciJuJM_) zgxF#R;JBxJKrP*5JcH~u~TbREkg35q)yy(j^?;}zSN0CwhMg=g3BQ z4K5ke#AUaVGmZJKH)d)1=?U+6IR@BOb)S)eMV>dFSZaMuDsaR$KA-rid?LP>?cg?ky5_R_N&jYK-8y77;TnJG67Tk!zMlXsMzTwp% zoNi+1a&7lks$*fiYQ}_%&g-ywZnOOOBNKv+ISfWRK1}kqRJ}2&XJ7R21Mz5qn$Ndj zzme)PmSN9n5{t2R;oE#+;Klgt9(2N#l0HVf>H6=pDF8R1&jR6QDYskPB20F>l&nqw zXYQm0#$CHB0DLBX(xX}}P+a*yc;pevk#?=)9yI@y5Zq(x8;5o4O9$5P`8aI|OWnk? z9LnDiM!epwh9DU#fON(6kEO*$ijgY{*4P=cd&ZeGnjM1zmHQ|2oR2Z`ZCmPuPfP4@ zbv-(LY1Zc1hC>p8^Q5cWJ~sCXc5W=M09DpPw%?0Sx+T-?T51|u@=%-Pyzk4eUcy{N z6}bQ_3YXjdV7F<20xZ?fTEQ>ABM!^&Vh<_2nFv@wsq=)tpA86Wc?v8L;j{JAj5hiF zr1_A@WV`fB-&Ic`bhv|p(+qMeu$zd3fv~75Rw&DdF~Bc)86K^I@IOvsa<>AbR^Bgf z46ILuw)IN6fif*?Q7RiB&<30*TQ^|W^sA)zOYFB@mt7iS&bSLY!eQcpOVVq&5Ricg z$B*RrLfaBoz=bOex#*Y+l}z78Q@GxIa!S5LI#f_5?^|OiV!mx@k6;y^fU*z7;1acW zKt}dr=n4}eX)X9iU3vWB;GOi>ujn5$w{xmd%?xoRsmR=q`R2tP;=2JDQZK#sm)DF$ z_CmiE_y^o z9qBdOZlpCQ{>vd#6#0OYGRHTRUY0bdOXmN<)PHDrP;s~cSpmwM!+YMdfQjS`kMWCv z^?ze2RDbKhN;Oxj3**9&BYQ^bp-#2*sqDPja|ttT0%1+!JJLqW#2D*^W)Vqpd87BG zcKMjypIncO0^&PQ55Z9C*;NZ+lGzm^LLm2vzr#`#;j@LhlT!G|6`QchbRaH7f5CiG67=c)1!8TBkUu`iM9PeUYS0`jGCexP8a zMugtZ-cD>7DcXUldj>f9ohB=?JSU~a`4Y!Qa2Np)1jwV+_hX5B>q)<>R4JFJg~)z1 z{_kktmyTJb_i$ALK1cS*?rR|Oo@)mV-os6;;f(C#0JxsNk2tw3zz-}oD^#cl$|!XX z#Pe5&{|=}wr`XS$ZwDs&^aXA|uB^=O0?_JYH7Uy+%O76b&ZRs0n8Wg3{ZjjM#KP@T zN+$=46RGO%PS*!7oz--07PF8K0jyup_^nwcggR?Gx>JTZ>?*ZJynu?_OLQSh&Q~35 zD2)9dd1% zO=#-h6A z1VrM^*(%lC&6#R5hI1c~UUY~k&=Pgx#v~~xxc=~Rs^4_{=HbJ`nI%8>Km&^P0Llrw z!S;`OE3W8sGe#*OF;V5Z4%g3=z#sNqixW?z@UKagc)g~-o@eH@#%irDTr&l_x7ope zP?2T$^W=U1t4_9=hG$cb|9fI*i)?+XMt(K6X|S79PH&M=RS{>BoLoG`GBDz z3+@oyZ)F`(4h3_UU9MAp44noR3|0cbgfprxhK=$He-2H*Mhs-6ND*Zh8lAq< z>-qgPksnSqn?BgW)sk{M53Yw#tw?A3GCd7wN!Ggz_p$U~( z$Pxwm`ALE#Hg(`12RYb++Bu!f!7rUTykQqst9Hj;-iZ%zLL-BU$A4f@$8OICxouA| z{z57VjH4rVrF{?-Pm3Z@aCQal!NC4m`^oI}b>|OzSy#jifyrmbcjGhZ*^Eh#=ec7( znJD1{Mn(%<)r0c9yM0oPuivHL5l$9hIb_Q7R~}7*1=#pQUjqYvgMm~8n1X4NIC$~6 zt{g(0ss30c+54c6Bkoh8D7oov0RxsFgh7P$L{8Av&t9}6!iG_I?F~9f1-F^s$r5uS zoBykJre5uFJ<>(Ks0sABq2&xz*~I3UYrklK@am_^vrZ9l;P1Aa8v3dv7AIVoy;oRd&n-pt=F9!`($IH{}9lV>N; z@RtYI@NMBR{A>R%wQ8H+wr?aRqK@bw-}>h&C8vYc89b0>jkCN*+I{HL<9#ty^IGfp zQ1Uy*oF~A*TZ>+NT~EV}ktA@!6Q-=gQ>!d5E6uDw4&AIFtJHqgRV92p5+3q^WKw>V zsY>k__3$JtCY2zmVk_{bxgKeCp0VgMI1l6gLq`K6p5YWFwf{U-K$Pb`@Uowz0yTMI<9b@0}BI15j zks1(xqoV`6ty~HpXirl&vAvsrG3W=UezWG$r=u>Y!c#Sk7tfDU55Z%Rvmu_n$K~!< z_FuJF@N8?**!Z%On*esRR2(>gVANyJC>b{Il(xNVZF>(U8JEij&&U9OA1L1saFVo5 z4Ne(HyjiKgbOd7)OLn?T?NA@}n$)qXO3XCXlOrAO*hOK-ctW%>Bn6Vw=J|z}w(HEQ zE*D;2#6ahnAaMrDG;W>FxGF$dd=aA@&4_~YSV((0x->RbsH{Cg_va9d+yES9SEGLW z*5oo!P!Z?QNYv=zC50}y#z?vb2*PFDxuW|`zxYCtYlr<}I`)>tG{Q6i zrThjEjhRfUhT;Q=YuiRGI?9{ZSZr*svN~T`iWVk?Re{0y6ESDdY?P_5#31pGeWLj6 zNdm-5LEz=^Nnml&RZD5V)FRBvN+dG?{Zi+PerudN$M4Tw?|2%Hu(SVB>zDE7@>*(Y z+q)jT|5B7m9VCvFYhXf3By-t!g1R#>x$>vIRBY~_PTD$lW||}nsi)r&PkyAUwND?6 zKA7|U^{=2IK1K66-F1lO%KeS_dZbiqjOhFRK~3oCkm$^Dy%Z;wL6~=W{uXjg^d0;k z1Gr3Q@g=EQ<)UOP#n~BR0vkhHU-vxMRpibct}v$(ZxOUjt&D|9haqQz5w_CN9a`h| z2dcc^1h|e&o7tk^`|qwHZX5}Cqm6=C3EI($@RF|SxM4YTjcfc1M6(a2x`*gEb+V4+ zztLeqL~*NF56x+FLB~qCjn)Oxwl%lk%>H?Vy#M{)G|L)VaoFYarj|DT-z$?t3OXnH z0+Yt{yGT4IM%Ph7)dsGSC5_F}Z&or4@3-QT=(y~@ApVQIC34U;S&$GO)y16A2!Gw` zm;y6dQqi;HRr-IVr27W|3-7|gn_J@Jq4lPJ$C`Bzu8DV!i$*)31;N!hyS$VrwrCX- zh<;L(lGmy`ZnORQKd;Rh`c-FL=J znE>M^AnVsJ6(R;bMAF(VgM7bP~y#46Y21KZ+ExUT_z))+=C55XDo=z(EopzgzdQn9W$=p1zGr?963LgIK$@2F+ofNsrBhX3DpTPyf>KoRV zZL#oR6BRe-0;^uN)KJ+?F&aO+J&=yK(y1c)cEFFslZdmzpSeIgYB9Xa?W;#yx?!sm>of2v71Hx0Xz6XhrPWIZy5Ly;^Hg!3(w17%TJ?m;iwT zw1MfdtIYF-hz^cD*lG%V;YHgFlO-Ay+`>fX&xo~0G|~Gevh}izE55pYwqGJ57dtSN zTSkcxDbe+rKi^I8=+?@aJ+Ck@#&=L1uI$~rN z#Fg$ntUd^PecmxQ^H;QDDd}Noqs~YLkE`qa>`dHQ(5Lo*$`R)3`g)gC3O`aqgaTGX zlE7h|rPSLq`^$L6MSaF6vv= z8pGN`5dqSZ52V`x(Duf8qISXJvRNkVl#3q$#zB!^=Qe_bF2Wx3(DTml32(3KK;oahkC(zygj)hb2CbH4S zjCdTblD;^B~1k?bNg#KtZ^2f2&oPoFxR2+ zCx`<}E@#>Oa*w3ej+`IVgUW<#^qVRY;0j63JqRwz4jxL%D4YZe8)F1WX3medp)LWD zaFpUk+Ytd}>RsdT4ZzvhS^eVMRFnd}=KQ3;f3c9D1S>~}A<2(ZS&H+i_G5zSw1hoV z^fT_wY(3JS0cb?z8v}v|oPjC@NsPf+y zMqWtc{CUpxI>j_;&4j0lMP>&rjGg_U*3{v>I+R;aOh)6|Kqxm@*2x=fE!@iW{ zYN^krd2WL4YA4;ki6rum&NN_j45?F#R5oWGw;Y+vPB_N0IlIEsdF9C9e*FFB!L(C|kb$e!y%PoG|x!$(=zd!>vZSc;W*Z$a-f`nVs@TSwYe}yM`km5^uM8n-^6!%pl<(#(<1V(6-!yK7AkPh_2k7*OP%A# zrBKdtCDEb8(VJsrq}9q0&`~h~f5eMij`5I;-iBm!XNG;&rd+0VIQ)L#wOaz0c8^3T zUM*p9Wi4cVbj&0~nxKeM72@~Q$QC>6YIb+2;}kaP?iqz#Gthqpp^#JyOLW8Gucoq# zt(H@}cOMDIyu}pY&(_@62ZKNzlyT9=X47N9e4>%EH;}}JvT;Ns zFy;mdZ7oj#mZbD(%OjV~ww%qz(cLy$m`!)Ho_}Fw2{xA=0Z5v__PG#y#xLRbxa$(8`+Zkja}kPKcM2z=V`zu(U~^~3 z@EZCzFJ@RyrTK)EP;BJcm_~cakfAn7;^!g#(+oD&H&u6t8cZCm-#p(S&HAssWCLE& zoSbhm{%P~XpWB^^vVJ~k_#E8G+fS7B+0k3s4?g!E_geKo4f@8%1ft zvzJ1}JQF%8{arJg8%w~Z0{$fOdSu0+z?8ycGb~V~WnLe0#Pvj_ov`b-y9ywy6{>Np zdELo0+z*_!_f0pLMr3qK4%gxq(!)6SP|X&Y@KHdZ^xzj3*?*VVCh-D5Ndvs35JMfn zBK?rv%M>p8o7LtvcV_!*8KUT_!cZyIlHP$@d${J$$xabfGCCZ(+}+<*1ymsM*$!8iO`zB=_5RfdBJkOME7dwL9G*KZK^H!~g{-fHaz`M=^Kg#4gL@STq;H zNc}gAT_f5h$c{C?fY|P#Yfl%%+Fjb!xot-I5tDe?)+RIA6)>+A-|jcZe}Hzkqr~z5 zj0|&o8CgYA+h0iUB)Zf z)n~s}FFhTI*(u?A>0|!H_ zk0vT`cRE4k`V!i}rgG`}>Bk=-hq{D)wG-zu;PigbW5mb|+Cga=FA^L#?|Wml?b>f@ z;Z^yBM7(2cde=Vw-w?6=`ulwi1-w)AulrS`ztC<-qDR)P=62aR7Vwbt;_vGVW0Qun zw_Y77Tm@OIBU{JZcY5;1O=> zSNxe~d1O&QD%VsHADNb>?asX3%kKhjZf@>welOOzGR?plMC|*S3d;!K%^E8{C2~QU z`*)>mdcTC=b+K{#%_RxH>E+1z&m8?raJ;lNozdV)A^KbN!3RZI_Qb&uwpa+osriV9 zP#*ULKS;SMO@-l#P%Y>G#lpG5QbMzkXfrG{$*3{a{N>oZm1xe?;iz@#QhxnAJu$D@ z>~s`s=g0co;$Jm>qrZ&ISn*sa$h))Rx1Phqh21z)azu|3Dbu;@p(>@sZA(6{`BElJ z83HiZzoR}!2FTl0QDbEYZ>eN;R1gM?QOcL!QGVUW+~{cj?8R^>;L^mK^5W}Fv(cry zE}?{Q^`%a@e_kz{eb@;WNoN@6hQyX6xGtWla0#7^))h(+B$AO4#DB=V?R3`&`f8z)%7>l6!K-SG-VG*(21z}X%Bi!7zm8HVZYBDR z4YJ#VH$Q%&xsyMr;eP(2#uQ-A8&=1F)8{4BtDSFrby_x0EC{`=6J5LZ}d~_5!-ez(`IQc#o_`GuR zC%$~T?-VD&8}whEB+y((Z-@bmBi_kiyzh4NYoka8@DDT31$!h%br#eo)G?v*VV?IQOj5*^w)CCdunn%4Fo);6_ ztO^jHhORnIEQQm^`)!(pg;Nh*-_w~+AH`sTNC;7(=0|yEDD@NUhYNSFEJyG=EOK(M z{^%oh%=iIfm2H2)A-8`jJ=3^l{KTlHaI!@UvlZ>BFj&18-E#C-+n1~5!+)OsC-w-D zVbsP^TCh~2|1UpBXcvQzUw-;yK#@GUU+M&u5SkhLC(UFn*;hqUMKgr*^O1Z=%3tYQ zSx-%pugN)1Fn8aYl$M|&S;{_7Qyc8}R-Jc~-@#a`UvGw}Ty`qMi*dFDy*WF>@8R}~ z|NNfl{I+I4Qs2k1D=J=Eib8Lf#f>ua9`$G_-9>v(Jr^nKfRBbF44MW|ar z30j3g*O*!HY4_(P>JnOHWgalC&?ukbEH-Q{phet8QFP9L%|!Ui!hh@j5`G=w6$kml zdHXGAv7k?xxvWqE+w6viOca1S9t(KU8SNWT_WZT|d96c>1$E0-0ybV8x*8<>_#?`DkO z{igab@}%w6%KUA4yAZrqI?FyDsDgw_17Zjrn92azKm_v;1v%|_F1;=%h7 zX?_Adho8QS@IpGj!eLW-Y;(V|b6)&s#7)uzwlt^u{hB4BdbTf@)C~(Yq(a$Z@byOC zxrtwgbUv|zGscZipxPu$Itz|-t@X%O=)tYJz;5D6D%_FNtNMGu3L<=V?T93025{T7 zU2gcT#qjd2uNgCmPy7%P5`!6!%lWXhQoy$Mw&nmfLMgO#GmllSS?5no1l3=^53p#k zi1PvWb|;2ii@(^6V1l=D_81`+g=hj%H7Q5p*^>duM_nO^R@^aG;~zSfKG7j6jEHgu z0)($3u}Y5DoZ0qr%KpIGd$%ecrxkQ~8Hk+MVmf<$FGV4@_nS#`NxsantBa>(pEB76 zf7!m)d%)NR%j}z)p1T2lpOKr3+Je8$?XJ!cBfi2pAB!s^TleJIe573U26-|D`Z9kV z<6w-;<4!CTug(b=t{*_+ZtCZd=>{dVF_oDoL5C$H28;U~-_Gj`3*h{Z`#%ewhhn7j8Ecv`c>CU7eNE z5+^S&A8*#%B)23*8h0;Yg97M%m$&8m>0@{n0+%2tr_1jXZw;WfHp}j}f4J}p@KYa_ z@r!O5IaFj`o~`@5EE9^8vxt-#jOJ|u*^hGm-XVk6y-9ioYQ89hRb$Rd2SA!IXvuV<{#i4HV!Q zi{t6v@t@;I6J)R_AT|Z|B+o4&@ZAOE4tq=q0(wdCaZHj1z{?mPcK^}1ByP@Tb8|4x z7EF@hWl&$?1eAWhH;qe`5Kop{>X`=s4^?QsOr(hj^3LqZb+; zGMpC^NnOA$5?39tlHlMVJWS_j(DQ6drnNoWCcVpC97*w#B+*ooE-PWZHp2gQ1xceW ztMq5MgUeUAao;#*q2k0Ys7|)gk>^NOJQ8lQUf{kCwqN&xFD$HMU3ReNeP#HblVJdRh)wlw(5xax(KhA$yL&OX7yK|km+5!FI z=MU)?I|nrEmV6dmy$c&{ehauALr$MGV8F~s@B!^atRmx#{yXpxA;BT?6|dgDy=Vugs^hFm*E&8c0xc%|QI-Tg zDP*o}oOpFB+yd}d5;dHuHEcG}3wFb z{`r5CH;gBkbxStYDGJo}3oL+sii89g4~Ft^iitIs79tIj1-5WHp9VC9b?3zxI&CdZ z&$AYMAw5&7dveFr9qUVxjZHG0eFPdtJ}2!-l$2yG!_QyCMO*uy&(O(Z)Wq9xSF-2F z^w&3C@-s77PSySp|729y1^>hERR6_Ei2b|`5B@R!Srh+z=JSTe0JMD%3kYBX@FTD= zJX#0EGIMSY#&WemQHS%CuQ#tf?%h0j!ifAgSW~k^%=S=uXOqHXV-$b}!~x#%`Ig*! zSnM%q{u;2WmTLM+-R?T1nHP`xTl-sEPlPUQ2VCWXOrxDRPze6RxNjcGP+$az3-JP# zYRquhKx|aPMM{&7wxD-rb_L_Tixl^VRodtOKMTM;X|#02f|2mL(DNPq{0^yTGcjnc zK56cwAK43I8wPKR3D|gzu>2oQXZ{b>|Ni0EIkVXJeVviDkR`G=Qxr;;QfOf)Q52Gp zm}84~6s1LrOeGW{6iUs=S}L+{WkSfluQPML^ZnuTKb*%o=f3af^SVl;9jVtPDqsB_ z8dL)e@_s-W^OAfi5q1-382>wEQ_EMxeN|NuqiZIqQ&8YxT_oT#D)Hyj#7040`6KUV z*38?-rWcfd0RBF&c2Kk!RN3br zF8~f=QqQUpU1KW7_4tFzpC`>GxW@o|-Eh7C;(9h9u=}t6+`L}zjg-U<%@~S;t zm?cknorrF2XBcx#q)lNaXxJthVfj~cE^7K7K(=uT&_+=*zf5@!8ZJz3Z81|-0)0@t z5(QV1A?hXfE&c#~CHp@Y&6 z;w6G^dmfIfJL?6&Pi1DGEX~GJQ8h?q;q2Yk{4y> zP9V>#fXjb_f*L=~)J}Lxt{2-) zg~;f|&PLfGdSe+*ZrL+48iAq?A%m){L>n1A-97170O-YOo!U$8&h4(2;Jt3Eq{ljp zd^q!vxWVV`U{DVdER>G;N@dwZxocw`^6-)H0OlE`=J*V{Z;F>%3{d!0A-ls&ft zrFRwH_fZG=UwhzAA}9YSa2z-ErdF>D6K^D z!CgU3=JTnarIe(#n#3u|sz@zUendPWUbiFzbLVBzUS5++<#k=*?yCBbpy1D@)dwFf9*YKiqY;JEe%>8$ z&rS9}B=P$FI*Lv>lAKn};vZECh$U)0yyXy1Wm{1;(ENV%{%{@j`DR7PF1f{^p3|3e z1 zklbW>jj|l~ZNU;%h^;FI^?q;kk3Xg?Q2r@D{-gS_IS;CxKm$DdG3m)5S7JXrLw$UI zfTsz7jx$z2w$F1z&v9Qx5()e<)aYp+dAN>;0}~m#Jln^w@R{sj@mj>$s8f0$oxM}C zNX^MdzA2A;w+OF8e|Agi0W9ueF$^Rcfzue;_xlIH=myHG+xEfM+`;+F)1}%Mq@$X_ zg-a_ugJxYk)fExsE^M%Bu(vDR0j&)(eZTU2yAhY2p_r$Lk09h9C*6blI=x0EPshimLN2`L6t-Vb^dX)T z0~pb}-nS<+;>Iv|M{2e8C!)#@9x}|wLene(IT{>p^#q2UNPqc2(&-8DIA$ta_8zE_Dg}aeY!~B@}tipu68Xqy z1dpMsnpTP|mlfLt?T(XT>!@$$1}Wt&YGyFj?oW>e8xLfEzWI{+LB4)Nbw>~3>B8z^ z-J`~e8=GxZyGC}cUKsSw!!%QwIe#k{dC-Sp8L1)vP8*2>ilhSak_;wlM$CZ+WHTxK z?!i4FpaXK*vqRdUKnT-H1SBtUEal83$z3HM4{M-@mDcCUf8JYP*r?qXL|VFi30tb1 zzMWhG+Tr|tj3UgO$u@x4OI$iy<04HF#}J+*3Bq)L8&PcU?-eBm^(CfKTp83%U#!%W z@YqayCX~JxT(bsN-{{H9Ts4|X4J>V)#Xy#dl{OrwB%kfTaDsFW1T4v1HDm@X=>y$3~h?E7Zs{M*CeEM9&A~>p3VsoWIb0$E&#iM7JX2~K1wmD-SmC~UN!NO z{pxiy*@3eL=bkj6H>0@LE!Iz^UWoVXNiB{8B~7Gbu^N|5CM$(f)g_YML5G0ELeMlr&a_jFrXEFYFQ{fBve&VrU z8V>x4lGF&a$UXmg%_{{U#{eE&e;wm{s{O(6ehPyxIPg1FXXUhNd_C$HIK^4LrIcyo z?rVw6U;a}6s{{mzZP5QHq+@h`s^oWAk$uXuuZ80LwzX}0<2NJ*x;qKWO9qKs)}*N7 zBBFS2M#r^Y?P!M5B2*ErDpqvh51T zoO2+gQlD>0=A4UW82XvNhbffOx7ltxvVL}+6ukBRZ4MqVUJE(;h|unk%V9$yvO(5$ zkJ7=i>vW2{B#B?)5d_fG{ zZkITp&LrAroJK$LL|gQvT*kmMb~1CncS80ub4C>T5iSc|lv2xIZC&CEzgGr@5NV|h z%{!j{^hwtAkK`W5I>{@YrGAdPad(Ac5A^cn@G@NZ$&bw0I^uF(cD!*_!)fL`<&h*& zp9{&K!9%>{)ptSVSb4h#H%TTV4?Vqlr|IRur1^M2@-^)E>pf_cb?JqL1$tT`{aQ3< zF0lFTrw|9k@hZ2MmXsIuM)0A@+m?IhCt_c9h>3`5q83|#^N8RrKmy|qUt*1~fhK++ zo3z}@@FEC|`M&o$Zw3a3_=!7#X^&gn@+x{Q^bpvlNwvmXJ8QN0@=;y@g&>rEOB&3U z5`(S9kt0|jI-f=)os<*E-@r3x?EdpYXCBh8gz2@`t{zzUJ}k1+l@W z;dDOdEUg2lc9D083Xa%-s8dTHoKOuv_!v&*EjTLLq=-mKqEd7f-r(qy(t=4zJMq(I zEGxl0_$)@><$~UmYx`wdEp+BZwm;FS)b%P~{!+ca-ST3ijdJC~?{srmQq?fOc84%O zn6GMDUwt^;UCdZt9UjxOeT*uXPLOtBk>1w6K_VwX-YaKk=q&Tz-GEE+M(+;G`BkkD zejlA*oS^EjG8G;phF~XjT8^a@H*pq+@Pc>11+NcP{Fb(kb+}XDwdE&2qxe$n3r}%O z34vb@T06XsPMJE}VbVu|<=hY4O_Sq9+BC$ck~2rp*>LFY1>Q!?-YR5(pxzBx^~R7|}{s(A!H+ ztH2ST+=U*<+W0RaF{#A=}eCN)COMI;^Ga`pL_1u68y4xq04@hG3GoEId zn~BptT9&5OyXS+S3Suv4$|ao9$F6L z_-TNn&JNi8RB_tH*T^~8btSu;r=;5tP+oV%3<%cA-$=6+qYZ+bO=P*hAO0sy#{mHnF^JZxBYQHmLWHbHy$CD@j@CCwdIbMmgH{=R4@ z{O@Etc6>kcV>ys{!;`*~OapOXt$VUiSx~*T1bXt?ny8K+3q*JTB%Mtb=QTop$%(@r zS*2^Q%pJC~)~?J_y@!6t9WyBq>0gp-AsN;y&MnM4mDmZ@2)dV0>WC5Q2FAnlMo-O# z8u0n$JLxq=8MpEHkORpFtN)v#H^Fm%#y=<+9|&im(Ko;4Dg(Ct6+$>C>cpza+x?J5 zBHSRq8K1WE1f6ooq~jt04;7;uwA^Tqp`Nrwk}Lazh$7k65(H;Wro47Q-#?)G4i%S{ zo|v5R>I14cB9>elqq|H!^#yH^^#8v76Ee616Rf(6%qM~zfyb`%FgHI)F*o=(EVhwP z@Y1%?(2nyj(sGvBc5kSMMZjAn@yv69kUMbh4lBn_Ol)>aB^tG!YczyhmksmB z;E!Sk2fbRiJ{#MM{#zsb9p8ClpD9}-yz9|dgzuO7qxPW@)XkHH)?cKqDI6tk;GXGF zR{lPgN49vJrBb)$-G3?8ATK??r?6Xjtsp`&0-q1;>PeD$r|z z;pE}ARGphn9oRqT|57v6Lz*4}2K3mRxyL5X$*D}U9qTW`G$Hv6XKLW=96&pC;#E!) zbhHFVbOI+Ztr`d!!Qoc#ny=fM2wxg;#xjox(#{{QXxVukUM`S%;WoHEbHaR{mXHc8$%|seKdac2T zQG_=j&W>uI#CH5tl-2uATnaLVxpVYOuXf;~s+A~j7#v=3X;v>3eNQbu68USowcgJv z?KfTFooJsceswV2nEW>s@6x;an))v6t!KedkYh8l?I<1Hz5SwyQsdMA#Q=dd~@n5P!(n{T|7PSLg&iOXWbXhGd!1!phul^Od>)JW_k^XCvCh1C@VO0aTS zUHX3~^p5P=Z;#6JwN1i2j=e8k51*&H zu8CRXZcuADOHM(%F3wXIlK>j$PvSIx<(+bhw(a=V!_vN>oW;YWwl8KbUFy}K|qYEY=W!$4Zpqy&n z#WL?Oq{>!;lxpfkXq`yUG=8pNob(+%82acGKJ;k-sDaFH<1S$vZKg<(gPM1Jpn=`5 zt{HvX{CgmpQFoDZiBD7!KJFxI6o7dv)Ic~FU=TyqW)6rVTm2p-`XUoRh+6B?dt}yJ7PY}Z-hB8`geHZ z$?2;w_t9 zzWrBn@w#_F#JvPqUN22E$*N)8Hql~uvvd)9s0FP7FTD= zs9UX?e%JTs&aPQ&pgU-@WnSOqTtp#tVATj`2pB zC<9?uP~Wn`z_f3{J(TSKZ5by{yZ&Q(l34$J+dP zqNvXo^Wx@4_f-G2cXmt}F>!Lz->6+`X)t~=x4LpS7tKn{P>iopre*!I#t@G(hPni- zqxSzs+CUH4Z5npJP_shsGMwRj!f$#-kn*93Jacdc`pQq_g6-B(E`%yseqnGK=BQJb ztPmI?WaD!fp`KWqfgen<@!>cJ8D?8t<3KE7W`=_yg3mz6w%><`%vX~xlKlEY1A}A@ z=5}@*rbDu?*>7KT{K(jl16wsRF%D-uh+<(*W!+{*SA)rfv*lm!TPM8- z7A@dzvAosDiMz>1Jg)$9#BkFH#wD_B%RiD1YSMU}qsmKWEIytBrXy!af2pk^lD}US zmwzUo!toyYkZD4Fy^x8cvAFsV_dpp*e!HEV^#_&Ed{E-c&rKV8^#cA|c^TpmQgE<( zU2}*ObBZwd&CN8Vf8o=w8`F9*HF$0%H1xXZkOHCVA>=HAxTgwf6sn1kXPcswQh{~| zw4G;HO_TvRxg3?jco!BBdl`hCy~;dxQ9pmluO5$uxkfje*ACLb7OvpI z;|}jh-qj(2z0#X&XJ59IssPFXyVE$MqwIk5ge@?ogdjCw;0+VoUVLE(ng9KiR7zD@ zD?T$#(nV3_}XHSH3uj#l^s2=4N8Ax*rdW1^6@AWKp-W>{25Ec3S6?+e;)YFoD6Y2j|Uq|V<){& zL&qCPt8=#V>8f_3Px(46hE*{AWgv1d-z7~MsF7dUtqb- zS^D-`dWW2r9W-|V0^~P6dm%wG3Lh0J(b3}Rq+0C9pP1T#9|(6~;N?_>E#d(GG|Y>k zC31(jal}TIRGkzjbocbCtCU4h)YG`kb>xA8 ze1@1<hUGm=b?gjh%Rp&Xa-z0er?shi=q6L`k3Of9njTdR1Xpzx)f9K@c z7DpL9t>y2&CESnc*{-!=h0wc!H(H#yqN`pTF!^@h!0YSkAZrVXvx+m3MY(VcX5%+j zi?#v2h~J)X+j(={8>wbGJlCZJxX4U35Zy`D5a!0zZhkoEcpTAq&>nZPYh~{nzbQfn zoa~U6%PrBqbMm;bA>!_KQJ=63#ntO=Ei-Xz=Wwb*V?jcyGyVW=#~-Z!0iq)LG_Xtb zy5q^m#R@&$iH$BRnYehF@3$OkV#v~qIR+{x;@&)iU|2!)3GSc6&2=8(v@-(E58N4s zm!PjAg8jKAd|J9nZyyRSZtL%wVXH2aCqw8UYA zuiP+dT!x-q6mbdYrd0aGp`T{mf$ZPGm)-1>ndFVbswIq<%xV)SZl!;Ro%E&IP~bhSa=_oSWXxNHx$y79Uupaq|54!(0; zm{sTbjwkrL%=O`i9XxjFnpNpRQkHmX6- zD)+zoZ^$hO_qwWHxOHLCx6ovOw0&I-W?ogUJiM|Pyrc0q!v?Veppsp_cMOPtTrgGBc`U^6#70XE zjJ)G~EBM>~Z`;=&b;{r%{F*`vJ!O*Jer$cc<45(eCUf>_<-+WS(T@-QqKzdB+f_r$ zj0Lj*>M6hL;h8PuT-k>)*w;8G>=I!Deg7nj_1_e#vX&;}BZ4O7gr8!m-jC2vFd*vF z&_lAnA2%-r6;O#R^hqE4_{~pHF}Jo5=rpC)V#y< zp27lPZ8pylM~mXBgJxzvNvzLGR-pZQhdAh9QX3pz3V+-yI&bv?TH4*>BS5(Ub*ShE zLnk=UD~N{kZ&J#9%$)@D%N*)ad-Ef1GxAC6SKgC^D!Z0ZhYFAiyF-*@SlN8>ed-Sb zL9&$D*Jd#+@y0_ByCpxs0Vh-*J4dr-wY@(qA!zd|JYDx~Q>&k)80`WQwsw5=3g&1c! zB4H!e@^B(~d+TrM64#qv9uo`M1=U?^K1mO}s=EkNCHq!zKTIR)4>G>Xx1?VSq_%=_ zi6iJ_jXiGrbC-mM<&Mdx~(O$ zou2z|+Xe!GYorbTxvd-{cK4(Q`z(GLO&rM9T3MxjJ1KOyuTOgKwZ8OM5(-;fiE0rN z7Q*{uuV}8kW1-p0rKFZ!nl&pQEfytTA9?le(05P2>E#-k1^bF|w_H*&=lehO!w7%``v94@?BEIHiXo)LjkHmNHMXVL^_c~97p8&q-lY)pjk)p)O)=&-nj=(oatg$IUxOXH_`dyAJxnEmX1Q< zX>1Qa64HyB{*I6;F?{jqF#no?IAU{raZw~--^so?kfX=*7XV6a$uNA|o zLZl<5A36)eTds~7A}6|jo6ZFA@}+Hd5oL8|KLpjgh#rt#6$5Yl@Tmvo(w{3es(}Q7 z-27x-_)d4QA-md4j5YfMC7lqO4rri;FJ=5`(eq`YiNo}a<;8cJ9AQwI_1%9-Bv4p% z2T*+q@+GvoFu!`9a_?K!7)Jb1Va~RvKNGz{4iBRq66veyf|2G%g3|}h=WJ?ZWPp2l z_jyK2wnWEEL58L}d?8S++)vQxM$PVhCC6b)cElbNuoXg}R+F&Q( zg>Nn3q49#q=!T%VHtHyhJeP>##K=-sF1;uX*?zYfcd>-p{mI}=OwsJSd-qi%AB6xi zfT8e;)k=lRP+#p`Z{Gj9yv22#)4+?ZupF+qaS(luC#Rw{Wh7D5x)%zL&^ZT*iD>O0 zM??Sp!;9XmR5Ej4uL#^J4);`kDHgx9R-Fv^196#8jsn?;)@;6HRQf4stE=EqOUISp ziltvny5QDRcI@pinqI4yEEExArtCJIwZ5%6c+_tx>fUK_eY?=dESmPW2TFbtO1%aeraR_~#7 z^IIPI=taW*S~+?+++te=_xer~f%5ri{w?S;)a#HSvs9xto5TC@VnHSrF=m|Y(bKj% z(x0Yo&6t(>_hmAT{TOP7_r=gs?+VxL{!ISolk%&_;4Jx#DsZPt-p@hvEX5-9^*4(j zzAi2%Wnbr1ETKP)ejcvwzcAFzj-J1SnpCss>oYl2YV=SU+DvU>p(JC-_R{2h&OID5 z3R;DfJA)E{&&!;Yw^_S1Fqs9|)L?YWB0)C6zu_c!B1QrAtKquDr~xbQ>6T(l5V|@$ z?ES2j(ugq)W!|vGI>(lb@y$JqyL=te_!Ugcn&rQmU`)QjsgUo6$WWmWjMSJ_GDplQ3wmx!iYL>603stTebG7{mjog9l{UEM((dT7##6+2jy6tC|2?=$0mKPxX5m% zg=BGLw!>?Z;2J1+BH$$De&>Crfd;Asw4QU?ep5a@QhIg{_++oRN$m+d5AXsJk80hM zgo2*my*xry-b9~Y)V(j`3>}>|mMle4vs)}ZA*0c?7A*2)WbOU-4^(9|b6KGkwdBlC z(Fe0~q~&@I`u!ZJ#~1~g~e1i4|&6RDJVQOs+a zId_``Fv0yWGIh41S<|cyOoC{$tKoV|Aw zZQ!SZqGMEJ`c7U-s(H6T-X!f0><(Ul4gM%UPH(hyWHhOa2G%+kQTs#0>-*#zo*rey z_Hq=qh@2Jtsac#w?$trVHWoi#a;e=5>>;8;e(pBrv$0CwiKC7>ymR6o532LD2BURA zu!@JbF(zYTJI&ji_EeIoX0HRKA96*$W5~ZuZRXO5<4LEG?{hqs=@Y_0^2+loPcF)W zM9A+?b@s}SXtTjHx!B})VC7?;EPm~+qc>8NtNoxXzX$V#isL~s(tbm$G5E4Y+?(Ji zJ=)T+L6D|ZDcHUqj`JrrGee0`N!<1snKi%wf97Pcb1FQ1N9K(zm1CIWi%qr3hf-PPFI&U1A`T5+zncUDR; ziWt|0{y^!4FE?8Pcx_&1e`9|g!)|li(i)R|=jJ}opDC_a(@weJ7~AxBtR6@!TvdqL z9I=5WgBD{QbdW66r4!m(kUeu6eq|0yKfn+IugkBJKQg0jheu`fX9AA>(>j*Kj z!@mK>Ds)2dSudMCx-kDn35{Tpu1@4|KE0NuP%k`;FS>&V0h%0Y*}&lsuq4i5k2K>i z9PtEb;zX61w?xUEp4W0WkzNQ4piX`8!jtr}q{+fSM zjrmLgH!cedH-jE|)7H8}NOw#QNn;L~>sNoGF$U_=_`qp|KSqH^&=BN;wte-~85WmI z@(q=Y^S_mN&mjAKK7q=~^B7#x+$1Hz3IfcpIZJuQiNd4pFGWFBFdDp;H!t$k*UgU% z>D=#CE~uP;8U0piBFS3#A~1bteZ2LR5~p_?kI1aS@$D&Ns&D+}pjOi7xuIHDUuX}U zlMdH;Wf$UaT7JkN$u6x2b;Dcb!J!}Sj>|P4?&yMX7~&0_=o14YnBX@zQ|gWGgxgup z`Dm@UVD#SV(pKkYz1!REp?+^s*}9!z3Ukfgagv(ODYkw7bF!S;yZQWRoH*@p|M2aV z)UrmK^p*Q*^_MA)nk(zO_$V3BsYeQ}FTv7Ro43O56KKO}^JmJPT({lO^3`Y|#dFJ% zy#!9htKG{rILyFg+{#sGU?U=c9I(X4m$b`p+9gxfDF* z6BR>j;6D$>E?Wq~M^1WPp(<%Bz~s%OYv^kX^v~IIJU{8a%~Gf?Scj^LE9SR?ANX{C zkbApdVT45!7R^IL+cV?o2cEq!MJ}HRI~TFp21;9T`Q@bdOYKF;l*H3k2~a63yBs}@ zb^nR=`zNp)DZ5{7MUt_NnM`dopM#PVj{>qhQK|I@>NK~#G0DecWX*F+?7l6;mXiVu zo;+Uv>qEOk@vM;S1YY9mcP?mPFWwm-mg1y;-CX~!+GBpfsb;q(JMmglftAeB&Og3D zHvuDSh;2QD{BbTsKQ48Ycui6HfsXkD=xqVO@)3Q!P5QKBqDO+dQ;W{j8F5qJb-RYr zPBvBqPh#$ZwX@QaPM`Qd(uga|oK2c5i$HqKuw;b7Y-|{msEsMRl z9u_rSEwhS}^19I2HBu_adV*1sX@lb;HMj>8o^b?U|&5L z5o=z%Ax1s|SW~Y7Q)K^DTNQx+49Wqc7sz6m>_^#zV*q^kZ0)o($cZk5Edyu`So}d~ zf>+;&A!CTwlAzp8!*}wdEcTIcsEzDy-@n_NIOt*&nJd z+kEB7~3Lb~B+OjuKYi=Z~5IFZm{8MNTU7#wJ<&--%0}4-9?NfM%lp33Yg!w}8o>{*0GG@y_jUn~t6)rDuuTH4sF z;nejs0>@(h8A|tOp)!1|O_8wfzPs-pCZ0Ola9d|GZXZ@V0Z2@F_cpxx7Pmur6h#ZZ ze41SqD!0mZtPe1#EWADSF`Ip&kcYP!eR~gWUJZV9b%x){g2M+R+FZ_H(QSV|Xa5C4 zIHZA*Jj=KPkc57oapmhTh`<4NdMhvjixsanCW}=8kfHap2ruDkEk+qMkP-*p_;i@r zue^@EG7p%%`iXrc*ojz-#B^oo+Bu++`BAb zxd+Y)>RC%U>|gZeguP?B^NiT1$KxosX@32jl~=Z52Bt8R-B{p+z+IH+K4VB>N_yvH ztL@wK{Su+lUTMd-E{`u|e|=Bt*_aIotO^~n=VsNPTP8V zg;^TTi@bU#c|pxkk6~SLrE0W|>P?6p;rYI4T){JU%<9Kv1US=d5wQ zAmF_7TuRhio*awjrV)~rkaLz8n&eHMG!q}tLPdJ1eS1vumA@(uW$zu87s~Ry{7yxQ zM-{pkOYqulPTmK^t42zM=dco5`>+4^{1n@lR*VT|lpff6v4q@WS56~{0KoYp%1K%# zHK9M*;T_S!EYcT_g&P}9{YagyUeR-4N8SBPmtoTnwGAeAvrtaO%FBW-oZBS!`35f( z!OiANDARNx{zaN9mN@^}VXe&J))As02-Ln9035JD1^UI#ehM)TH2opmK~Zs0%F!AG zPS#*dLEojBT`q9YW0O{;@H=^JP@D#{KjOzZaxj+cMe&8DkDY&z7VosNwHVwRn_}IM zMn7qh=LB)i&k6BWMA}-Zc6lnt{=#W;(wx+mQ{Q$hZuP+3-4!ZMo)u2cC)sDW-O)#` z-GIE;|MMK5+<6F7{=>w|*JNiuoax#NGl9|`FPe!iR>C{^eS(Pzb*yZK2(kC z$+I5gOK8~T-aR`u0lDv&0eXc0l5Y;ihyRr9iQgNrlA*>-{0v@Loc7+y7Pvibe?>2U zs76S3az3tcHHGHWKK(*_{=@l?P%lsnd2M@QbDqOEj){nlJx%b3O0wn#1>a%74%gA~ z_Gr(`Lg1=RCCQN^kc57HzS*g_1wOv^hs|mcK%RaE&-k!VL$%I>NYTzs#!A|ZeY;8| z_TJ~sq!@0^DRxI|B*aX!KvrxDaaAZ_(RXi>ni}zdW`SuXLesW4*+=48Z zuSr(9bbARt{+O4Le$mUzX6>18oJb%_aAxsfDwNfK5+Uwt^Yv-$6MTGvcBg!NzLQD{A=h@V!>H za>RSo0rWVA=@8ZWGg}4750AH`H|@6zuTI~B6$HPvBa~tG#D@N+Oj8PtMTlAS z>I!Y*HIihm3_jr(eFYoo_4Hz{YcTg!X}r;7tOj`zR1|oJs`UvGsVxc}_5N3^@$tP^){|B0V==*1>qWys+f(!q==Zg1 zey!awFUKzS!6^!AArVF7z(<4Dn!nqunmkGtq0MAz^ube6oz>^5*P+&JDQ#T(@!I+m zU4vc4CYc-E&Wh>sC%5b^`GQz=7|o0vYx7WFtqRqxy~?0OF!?OzS)>q*gNM}vq=od~$F@fd} z7dOy5n|d~_5tCk(TLwMW$9f3&xmLOu)>`o+boDStFHOX>XiV-b@U=nHN2V~6yMdG9 ze%l|-X0{z?#wWgp`R^A3p*(9CBVgYmP!bi`4RV}0T>kE2eGrNPOgK_O{Eoi72=}8nF79Bxsp>@j5w{A2GRzWh5kQ%EOPJ_mTM2v$j6KIO{O6 z=jfHFff5aSgyzI6ehV38*-#j1MC9HieOJE5#FLRzVY6@0?+>L#&Z!@-#JiIjF;~Cs z4~S`h4pH1`*{sMnj}ClsVpZt|l6gc%_Fk<@UmUdMOEO6jX~|eK&&bS+(2v4iw>dzt zc5|&Xh0*Le>xbwE@x^~)T_|d`V{`MT*BCXcbLJ9HaN|C`Vna>5wS|vZ5GWA6N6OrPSm6;4Dtrv_ zQ^ktQF+w5Jt@^jIcHHEsikoV(H=$kKg2&wupC z#Y+3YOZhJ@chbWj?(fNMI6wc=T}v;sYPbE6ThA*6t5yb0tvd0I$E)FMb-teouur$)(=zcgGOzCDfX{|vvk&`dkI zdr%cE7XMBVLCM{Opt17EiC3Hw?XGlrIAh5X8@C@@{Vo?$eqVcAq>-R4xL(_>YY5Bo zm_ECoo&pzDSQkv+>L6vVrcqn)ycua@Yv|(yz&H+YCF(>w+^0J>JyR;+-aNJqoWbYp zFo?Hlr6$(_0Z8%@`6N8m@*aDAe`gCP6<{OX<_6qW362eWOj4yfs1n>-TlEaLq=yDo zA0-FAJL-Mqb@w6W;fEe z@@s;hmlBppYt$Q*vwfGs+4J;bFKW#uI_fZ^SBsw6lsSrqb9?4`#}7?K0dV%p`0rv~ z#2$LNRJp~)w0jl1?aPAwDP;Z47EIImS_%h_|5rYWWBmOS&H91GoBkD6^Gp}SWgcI8 zeHl1PMfkOtXJG6!=Wb*`$`n4JLq3hY=`Lt08t^ODG}J?*>>I1JuN=&b0iZeyr5U{0r3em(jF)mwa~_FmRlSb(S%bU0v%FC6+O zZ^1eff1j~_`ujP=*y4DPJ3y@sjb*()va_;`Yc-WXdhns^u*Gvwxc;OV_u^6Wa?ZP*un-dd=K z)Iqg8B21AjueYe^PaG0t5*a&zL=Ffv=8OqqC~b#vYxij8{X%yV&}{KT`q11#saSP} zQW!6q#0wGaUOnPGVR`W~7ni=YQWKkRn_rdq5#s0{X{iS(X{1>}dYziByJ*aGXr`bL@ zp+e3ujKemDE5Vc@0ZNBa3ir-me5%U%Uq|L>;t0k{S#kw`B#=# zY}V1n*KE{N#iFQY&W&e9>T>&CuTuBQkHd=%UH zwy;r_S5pF(+hH#tJ$KVN#=Z5D*J$x85hoQI64+UZYdSJ2##Ko2S=z(Gh|0yPTj<>M z*Dp!)FDBQTBEHGmd6-THDA1%q!D#4o0I6)FOO2y?m=Pw_z2{+S*RBKH_^YxK&!Zj4 zp+pd|I6DpGWVyY5F-)=;aS}6d&^NkGCc;>5{i}%PrbhojOHwsXAB!Z8i#uYvsGGLm zUi4~XgW*Qc$2&2SM!+`aB0guqDGFTWRfFSPNKKrguytzTdpYG-RJXfncFjMVdqM>K z&4Z!fw`oH$5XSR;av5(t7yg$8049LEF5yR3{$;GLv&yq@ETx*c6_d_`jpne1L4dx#QskRy*7AHQA+>r0F}+u#@e z)TGeadjGHWAdI>4B(JrPPT-&D|GGjN4NEo%#ALBGVk!Ve}9+z>`WBBHalSW!-#>nz+3jN|%M97FL=j5Nh@w>V4oQj@B@#;Z z%%Mc7Xhn$$k;+mOvdk!=>`Mq`LiT0s+svHre17=;1@pt4$2r%1-LKd4N$p??z22w_ zod48Y?1t!z{X}c2B(A}Wcl^7E1L;cAtnUY-t$fI%X0_+29J;^u4(z+G9`xgX-4dBm zf1ciVN@8DXkqZCmnJw}3bj0)hcLGJI`2V)9RAQOdV^zyLimtt=Xt5G*-4XR4FQ@V^ zbob?vg0nc-f$x7?kaUGGqghM)qwSMBgR`4>0*4MZ=vC7v1Rj&cnR1R7t~!{RDa&l| zj7}aSZ=>Fmgp9Q(nsHQF8_%%Q+L~4YhT6iq^NfRM_N9vb$rlVDec}axowy z=>myha!l-Fj+Guj_1wC6_I>XP2cBI5ih{OQv=xW|e%JXzi9_+fPSu9hv<-C2?RDrZ zcjo7R;zYnnvq@KY$3s*}_gdN>s1@Te;54U~gzOb+*=+n+?j@S&P|;OztbJ2q5_un1 zZxb7u5kP{{eV6;p%n*ABmjAG>aQ?_*1pF^}%ZjD!_bsq#efbW6N9(x(`ZBn*F}ywc zg5stuHsvS6iQK~~ls8QuYK54_C@QjBLtQjb)(J6|Aa1$Na>J_+uk%g}3v4jT|B zAkT+6TpDqD+N07|oL1uaxDn|5IHT-wGD=g>$1QnEJkQQ{6$KsHowe)3#G5ZU-xR&&&@G&y$%KN!GfUBq)ANcp4PFsSUTmHNs0(a|Jzu#VS zdj^UZPn^G4zc5b#owS1xd7dFiP*sJj+uaX3sU-)#uxyinSaYI%7&!uUhWST^$|rq1 z40;RaFeP87i-ByIDCYKli-8+Jg`B8T(rBhdiC_P=#FFdK`P=sEAx7CT`}voUjj| zl@*$>`cp7QBLOExH^gt=+>t#5frA1t-n_%coaPwo@ z6aFDVf6>BietJ{*Qvp%rA5k(!QAM9$@6XV8Md*?}dNOEVj}4nX%3EXy=a})W7dQ7% z)ln=N`WD+@dAhOXM|aFa>NPx$P5bn#w3$EE;6{#?EeLQ7F|8&--;x$nPO+pP({^i% z*3L{&mP#)?Vzk~%O8aRnO}h-F?mXJ48&2M7o)8Z`wft>{zK~`l73$#c{hZS&j#sS{ zXXg-p>z~_tpx+twH&pY?JQ-h+bIX0eDiwO|p%YX}{PxE;sx)6#b z3U3Q{on6q|A$X?N_s18i)^e+n+V0<)ce#&t>KCEB^b)V&qpt6U(4QMF%wOnzyLf-) zY<;RKYE?UBkfX1eHFTt*LV@$Qy1n#!&Q*U~sa-ZN^S8*`eunQXw(V7P$zA7C8aGCF z2Yqz!Lhg$u*Byk2)9KFFi=i{e$ck%aXex$Tb3LLO2a1NTHZMhOkyscJJP!5Dq-S}N zKI530Mo+q4ous2(8gEI#l|CZ=NP0{1MzCi%wZiQ^1(>iVdV95z>ta0=>PTL3Nmydu z&LHf`pV>cV#T>cfr@K+HQ6mN3?wXinffM*-^ zHnHJ{qRK~$HWXtU=foB_Hrwwx82(N*&&rFEfT8LklUDoZC=5PCc*{mh1tNuI9WB=L zjoCn^UeRRMu1;nmVjZaBhLRGabHE=>Ritj+@W#jOfz|aOxyY1BvxY&nT2%@4SGfe_d{ip%pTham6W{BN4HUm(#$K-wUWe7;3OBh#n)L2 z7t8Q3S3Cl*rFMao;IT6)BsI$nN7Qp*%|7GdsSJCM>pdK54D` z`6V4;H?mlxO+J?(*MBIc{hLrK1HUnQSDsC|$6eMkkS|%=6Z3ff^mE$EHe_(?0q8IdW}-eVOUfsS_~kRr4O{!*@70{%lFe8?kyE9OPz?md)QMXAvm=zCZh@N)mG&A?5{F_7c}Zji%M9H7j}gi zPXxU_Gt=*&m~X#_qcG2?w@{H~hd_i29%Qkvh}@^XT89pHN?Z9}uSiOeSwDDk*Fhq= z6BB${p#Pm}_XHuPcWs$0a)R?MU?KN^t-K6=dlz-k+l3Ov$mZv4Th}&z>D&U27 zXo~qZFj(sLvs|HT_Q7M`gZu2wE9AKiODenBJ$Kz_EqGui-R?R3*$3(gV>K{(eU7*Z z^!H#zX?bsWm!Q^VX4EIjN_i%IEj>x7sP^EiRojp>&r~> zI?oYoyDZ6Kieyf@7c^VSt5p@dTXHGx;sir_y5iwP|3KnxNJASs;0yJ*O5OK1CkzaW z!o?tC%DG{i04h-1Li(o7DWUU2v`s&1Gt%x4_zK^fu+dD=x)yC!LW9k2Q^ea=Q%=jd zhMH<$e{E(}r5MenF*tnCP$1v)G>n|%XuhQN15REQ<=m-_^w&Ei53qFRS==Un)r?cgi_m6UUF+w{ z`w0|giT9V#%E-*5SD$eoo3h?X5H*xlki_Bvd; z0jspOZOMSprs5Be%fIpd#4MXAOitNB1zx!Wgxqu|+WU1VZWDP_hB=Nj5$JOb+>}5_ zC=+8;Gt;665yRkw2o2fuIZXiv@~YGG3GZN2NwOw^+!6y7lH_nVwi)OkV>qU8_s$v! zpr{p`%c3@doR8SYi0Y#XCysJ5txV3!;~WxLc27#J6tCPW+k*+zgLO%NMt2y2ngsSv z%hM-IYH2#%<@PM_0x8-1HHJi?u$N<_0O=N*HETL*9u@4LriYVbYLtEyxQaG@E#yC9 zp~BUlokmPfo5Dv{i!~ETM<1-Gue&)`UEaIy$}S`Y{=i}BSB*U{<~;!hsYr?-y!B*q z-lG_0l~YrF?u45tmZSNeOPm(RV~Jtyg&hi*t*VE^hShHHAS!AV;&&-0#4e2|{nK1P z#9s1Zmig}ogd^pJ=&rZ?;=P}o&2**(KzD?PWS^w<0`(5ITu}z4A@EbCG)SLXyEv3a zFi3GFvqC1&5K$pn*WEa??Q99yJPneCsDE4cU=5_0TTp3fd(P5HkbL6CQ73ej-Ss$OJH@69V}onUNa=Q!Fa5kz8HbL#`-^Fs{N}tH6zjaHo>naeCwu0j*6AZ{1-Pf zD;G)1J=3%kN|6a^>53NAPip^iOV$!L9O0u|>_9TFO^j9Sd9v6n&-l8bN5=Vo)&dR+ zEEhN4kXtBCP>8?aoPw_WK^5i|oI&nm`I+)o z`K(IbvaQ&{3IHWPTKNQS2e%`%kZ?xVj`QMeh)Y4??4`9?VKCE!+!95_|I#s#-1Lld zK@db}fxU!FvILziE#f)`6F`Y;1%4+!6ozFw6N{|&jsvJ+Y9_itTbs&+V zcWF{wT7M?>0Av5cqN*zO$z0jyhs6WbO?Rl&TlsrB9MH*3dszwC4$Tt_9{uxp-?Nyx zrxWz1!lQY5(95#d=>10;3vapnoN}33L7e+WBb)nt`K7@_8QZ?)y&#V+?`GKefmVi+ zKFOZiW{y$21sCK>kv~8;ROp!)2Lm$av8YFIchy!~LMd0nnyzr(&n_A_L^Gla%^-=( zdW*hcCKp;ru9zMvezW|aK+UW#4whZ6yb!$8hxnnj*i5$7v;;8w!?~Zh0L9BS!ry@VXmUe3}(P^C(Y5A|~8rs9jTz7$+NIUEd{XIpz305hsyqHf@ zq&VTgU$ZMY4gdbjm;5y_(T>I)FoT_1g?lgTAUk+@!TQ%kn6A`I=n4^75lGh|PyZFH zMPKv#4}8K*5MDaawVMdb4e;kMii(y)bYHCQ?#A@APo;muG(?z6y{BX`!1UHaRJmrW zjEB{6=zH+5E+$xi`9c|Ta5cHI^>hMSS0Xiif+OKvkhzBSran!?{+8X2iXujy9QwOY zR1e8ifB@w$n^B52Y#VwP%##gvYUC~arCDs)Pa$H}8yD(mmR}=_1D@!EY2huF%x+7J z1A)I?LAt-7{ImVa*I5v&cY0;*MXKV>#T`~;vN6FIz4FozWaFfC;RVbc;Y1~<@ZfgI`_Oxsj=v(0d!pC1Di*yQn`DzmcM1qJSLA59ju8Dr6-W0#hHTFCQ$3bRx74-)v3 z?W;gQ#h88`qeK|%9XX;`BlC)DjK0U8D=0!6rF*?L?FY(Zo^Wb(r*_Cv`aHPHj^9eX zzm)k>`AU?X1aOr8GB#klTJVS=@Kdz>_sVqDFpe2@xvPOviDzH(NdFC%bpxhBcs`JDuSP55pO_14*FKD@Dv@O0 ziGxKG^ADPiALFpzUN`bEe@*)*l1L{f&Qszi`v%7;cCN=PIw~D`gKjzi{Niy`Ly8bNCrpz>MBVkNpoh z@^>vC`v(G&rjKHxCsFx3iA1916}0~Y=8)n!_(sA2BwLTC$jJ0km(VMidN@Qc$0(ajUzj{+`C6#yT)z0pPf&=U=?Bds99I`t@HgMZ+d# z0J}l7JnlDcj#f9qtfKw!ev)eZr^Q%wLvxCBeD2Yp`@YDoB@wro^m1;WIsGDv$X_si zeACiOgiBxC=oV;QLh_^_I@3Ex>UTFUbM~T@$6NL_`CHe$al}QF+iSpQRELi9*na%Q z;X=r?Kt+1YshOubzi=2n?Fd?}et#4k^&eT0rpZZR2A@?kf^q81$?=HZ*%hXg+Ae>> zhDf8k=6ZRYDqG2nDR?E$Sme7#9-g(AsZeqKtS*tKx$j7}HfSqqGgToeRZqZxn)MSNEQ0i~5w@bdTQf+o_pnx4{%+QMmc7^ZJUyn%F58?Tahy z2gWd3`=_y*4a5F*d;!~Lufeo=xfbXQt~BK+Fnh|FhV>9` zl6lQk(JfxMkm~~k?mu8=>02wZbG$&R4D&RUD$jp#D$%=lm)@lp@)r)&d1w|{m&|}h z$FOkhT5~9LaXu-exfI3Pq5KwmHNL;%WpqIu4T8opnqNg+ljT*_%UUeQE<_SKJVi;u zd@eRO>8io;v^bs~#{|z|NODN{?=q}0Yx`#+6_4k|;$ij3l-mF5k!hb8CHUOO?P*Sz zIVjXQaS)D#@-7G$ZUyzYu{`MB5O6w$YE8t@+l8oye5vTP`-eIndo`IXkzPQ$1BE2+#2*yfkm_21Z(r0z&o zNBait#}`hQRfsE*TgHVwl1Z`SB07vOfe|VdIc(>u`uKQds*QZh?gKTmVtb@=^>?l= z-fD^ZCU&BKD;2Cxi;g0Ufq3NhGGz{UpNbCx%|DbrAtb@ojr$n?%jk`dQSdhyN}a=c zq}FmN;y2m^>c7xbDz>4?3YxOuB$<9}LBo(xcpQpvV_c~F%PuMgLHI;NW=p!V@Xau) zb5d0Srceo3xFKOFikB}CFrR(!pRh)t&u-Mm#&@sKrL5UOi%-k@76R8J*;TiHc^&*_V3h;d+14u8LAfVl7dV8Gb4b5 zzak3Oi0yaUBcI5{C0`g{sQ;Y&U;5{k6G>rXvTp2NG-_J3wF_AiL|yOg9nejRZ;DYa z`Z-cg*<)bKUM5$x7~VMM4f4s`$rC(PV#rSw6uyZ*u9_{aDsb(u-&;gK zOh7B=FrkM^a5(z(;(c~`H?r%ba3=~q*p^Pq(<$4SWb1pe-cYcw>&W#A`DJbSy>-i} z5cO&sVOLRn9CAoh%NouTgszC;Tmru{i!t@O<8|n@{;&^YzVNzt`~8JlxupG6HR*br zX8>LsNaH3b0!e(<8wGpT{9J0lfS)0#g**j8$2Y9;^ zn*{W;8k!zjgO)Dn5_=2Vp!Mpz18;G{_-r*+)PC+J<8?8w(Sf*ygdv!KsW$>8GdT+I z1hfW#0aa#Yi&*l_(ShIwt1bIcS^N{lCMDLuJ1pEX;2W{4&A9dh2bVl6e zD-T)e@Y5{j%^D|NUL&nn(zMpTlD^(oV<+jr9+<-4(0L*1(tw{A+P}69MCQ!a(qxZg z-FEGV4`UqKxwL_l)g6bU`oTUN2Fto~bk{QNcM*3s6*OYu8nRAjq>? z@@NG=gz7;;`YSZk5SB;js<(azRgw2@!Leh6DSFhnxUe(7BIzN+iHxt@dK}u9kRS!= z{-o6uDB@~jp;O$+Y-Hbm=zZFtQBS%x0+jXDdTt}@F3v#H-;XQdz(a$Fnk3};b9O>2 z6|8*pJRkDQS`glWvYXlzRKXpHQ}^GW8fZ(nHB36hlTPTO3iCwbi?FLL-(mD=-Ulc6 z6>_hK_x002ARqBcRxET`*4bpOv!zpdPoz#ZIPDoZ^aIhjyAJby!^f_>^G(o!EH$|} zHteLS;qPk~y(~R}{Pbg3p$kt;=?A4E5B_jEO1@wIpTX|4BXylJ~-8cJd zn=fRO`6VE{<03hpv{kXBCG%_Mc7yD=HJ9U0lrJ1#&)Lgq6vl#qOFVb>)j>&lz~k-U z9&?3blb8sf8~BThpB(UCmEQz%Kq~<}1$Pri3oNPQBCKalV3{Z!vaCTc@7Cl9dWsZo{$f&!2zAN(BuA2cOF7oz-SK^~PB z%6Ne;wHYpczovn#COEX;F7%BQg;j0c?l|F7hV5BmyjP|Y0UP~0@}4kPGmP@Z z6|{)8PGww~t5p8R@p^%7S5C(hYw_?AeEpEq`BM z*IhcH1kx@U^n1t7%LJ9b8;da?&|UQSFz{f~9Dn+N5jhX)lpzX3p}c`_e7PuU#Zp-7 zUymoWc(;KujR`y4E73@kCCCRAkSGb9Qa^!{}Xto#R<&5P-_Jh7zX zTX{deg%LUK*~m&X&z-qW0(>*4H)cW`ayzYx*ir8#q@fqzBz@%@*RHCrp6gBq( zw7tOJ{Q7~dxoD4YR-cv%Sc9!~PiwfEePnN{3t}EiPaV-I+|lz~uR6<6 zB*gPLBzX}Ry~m^dG2(zcZ(dw>!_B(e4&G^`?Q-EB#<6xr{h7+SHW>~4N5SmFpMMKS ztp~gRyGWC4#JTLW@HmHZSr24wAJ%m=KsK!Z_AjDmulxsjh&kK@S=xyDUz*R3LVA0P z&DI_ygR9tV=OT%kFbRM+ryz6jYk*{UA0+#*VnixUU0dCAZ}y88>a!FN*1p2`eB1=r zwSLSo`GPjc^7zO-aJC`Mwum&!{pFsUBUyj+{Kb|W^v3h!+p>sIMWk>S(!^ox1s+Db zDxM%K{ZEk_JW@L{vlF7OAu?P>MrS%7A{PaLXV-loqeL~2Y_Oagx8Vdw0n_nH+4^Mp z4FT5&>G>ee%tI;`1@$l9>d5)!xdUhuLRthM3w82i+DS}LDplguShW#c-qMgLTzXEA zY1{*m#SJ_m>Nojnx9M6nt;UV1mCYLIDKZ@0j{(eEsQ!|u{;G)_K9tlUWNq==J)|q- zAQ~ti-(d8$4y(Q#dEv-ZbH%Fhjke_@^DcA$9!5gWy#u~IN_iR>aJ^UX2mauMYZUyf zJpJ4b-OJCDADr{phtV*~(ZBNXN$qdWFp#{Sr-R5X%i@3z0W#|0zhV6^ zL20vCj(gkaoHe58JrE+ighYfd>D40z_H)aW{82wDP{Q)ogNa}}E<%2GMdv>LlNy{V z4mzYvy_r4@gi_sQm8AM7$bUuP*$4eXlpks$QO?mmGEA2^v+|b$^|hodxev4PAy0I# zOp3{s;XJrGDU~6Hn}~lAc1Av-O+tnoh-jSBz_%#cM*)ZAZXRz zMr3k)EWO43Pbz}U4$@BZ>cn2~W@oiHVg)~&S7%jO2;1F-xQTXk=s7&%*u(L~(uHT@ zMppRRP;;PL?&onlvd5bF%%i9Lb|s*jc%`vJjwr!}P~Hw!Wf3lHRoe~zw22uH)l0F< z#=)m@`F1{kqzAi&DLf6|l3TQyK<<{HY{jM;K2s)F-s>~}q;Y`ZebitCv$2Y?bCWH$ z{O~B+h&Uv5{#|tVJ^+Ni?U>%Qadop091Ab`T`@8`F9saH&Fr3&u~fSfP465D3+>U8 zYkk}}vggwY&N<9HWaIr1frKFg^UX~hi#uJ0;tj+LuGyb- zgo6qbx))C#RTPw6BB_GxTKYMdiMgr7ucW-{y`3~6klNZlFfZU~MU6UsLKOe-yoX7!0k!A1{BZ-jg`EQXKJeQl)7Rc(L^iih zuO3QEzO?L%g8xZn1qFCS$Av-$F_ZgCW$v>FEco=F^q(6^B3+)J!a|IH(O5hhf6edi z{)B5khK%pI(=pk0Zee^?%$TV)KluTAx=x|@kOaa;am-S|UwgqUSUi%Ag&Ar_py)fv ze3ns-WoLtx!%}g+KoetLd>aTlZ^N)>B`$&*bWhaCjtY^QC3`A-aFX&I^X84F)6zie zYa~}Ead&m+3^Y}4a7u8~czUe8-jQA`YQ2iJiB9aBKzRD&eN}M-LcJ-N?h-i$bsnl?8Moz6nch zYzl?5#8+n;s*Vp9nJMi=eU~AR?xSm!qFr|GCp&X*pAbuJdC5ulYZ-W2J}0y`U*T55 z-LTJb#!z##*}I%YIgWB;M0oONq&o`Ze+^|q_gZt`^@cWH8MT+MpZQwVzD9ePbeBBb zjvZbfo#lhV+;BJ#o8Pl+)DNy$07P--tCZP>`*Z;Wi%!r_{xbZR)raxF^^0bA95E^J ze6@fqCzs-wStqpwiK9ZwYYk>QaQz=-JaXb-byTq;<){eS`2qb2uSeQq94~TZj2n5h zPS(IU+FSCHTigrv0Q!CVoG1VmC_S09ejEx&^I8HV*;6QRoP%8AAf!@WCTI3kH=?ik zmb>v)A*#F9QD99RP>m^ilH`)#Brs|(173chZ1eM{S41m+7VfyZ6or#3hnA&Lg?4a< zCk&#-tu3oRWBf~4M$^MU?1Qj@B0M7OUhU4~8VM4qEI(RL6STCx`rtQz1(J9Tyuo$C zKzZpGF*y%PG3MuA4LZICa~H5&`Fpro zxbb^!%RUrvq~4`&%f!zyA2}Q;~H+CF1eJ<`GJCO(-x=C5VHajaFB~BhPfXh zE4WBR->Jzya$+HUsH+fpBWUJ2{K?UvYF6Uty5uBh@~wG0`hK0QTU?g>Bj8a6KTu43 zYhLv#Tv4oMA^N)@I}1F*%*=KLs92SYCz%W$0{lF2Cc2%!X(>HrlEhdWJNg>&y4!vj zM0}c}J>uDnF?x_X9!->+FoE!ER1SjhN!}wBA^??XB-V-DOv?h3T5A~k3@GM@QjXN; zSx;f%Xr%CzTp$q(tZd!>RV40%9l$iG3flpKzz2NkkUF#I1jg31HHnHQSdb4XQBA;o zT-Vu3<@~jh5sZcP7UpFphWj;ECeTS-^yg9LYoS|tH_(LQlk}?wveqd02zWQ@yFwY0 zFy8h1ZSL`Z4cF(Nn()slr`rcE$KXP!9a7*>wXv_LH@q2(x++9=qikDynaohNebZ1VKkdGex;)`a+Q ze`q*^_K&U+(>eXD*}H}3cj~)ch`_OW9{)VEa{<})5N#w%Xkj0bkg@gw z^=-vl6=&G9%{GCU#&;d1p#|~F^#iZGGws5Ij*?VWgmh^R1uNXj9F`^{fbg=Ha%(2pMHdo_rW^rG&DJz$J6zefqKK}R_4 z0FfG7J?{RG=6(FsmncH%aTKO?5wsRt8L^+;p3v$V@>X$SXf1 z1E=LRs45B$@T`TXp|HRSAiap4APDMXUkhaqse7ZST}WX5*2S%7aNm%a-d0&wn;&}O z_42~WeT-YK@Q-{TxDV2rH4&SOKAR&-=!4iu`R5qZM^}&Q1#iec?~^?ag4H)TGXdAV zPY_C^u>;E+0SjVQM3$l}yx)DwW4PJ=kw4ENj|*(`bXerfwzU*2 zeT?_c#$Xe;)!y>BF}gk7O17~6ETm!sytdPC6D+QH??^!eIQlW9-d}fY^NNDF6xSw3 z2+E(Unbmjz#r}Op7ix^Y{MPdKOLNT{2iNQQ%T~01>?HhsjisYEVx%*NNls798mldy z9Xjwu{&o7kzQQkZZM?yu;zNM&bKnQkxQ^sML1Js&AJS4+XQQ>-Gjeg2X*l32%bdFS zd1iTGAqr?n%bkRa(#GX*#yBb&wrE_N3^edRPdzO_tj}&zY}n20NKg8(s&kHs$e*Cc zhQGv~!IRy<1>vcos4u=tE3s`7di@qg*Jnj@3k=7ASC`*}`JXR2FH@_nRu64irYWkj z;>(5wZ;hV2^|ET9dmD=R%_`+S;WW?YLVDFcWcPofOjkarmNW2!^GGNu2E56v70{LQ zXZbvnLn%Gp>4Y7W%0l}F&bi7FpVlBd`Zl^Hh}Q3E6SKFzg9;Q&k9uf8=TjsGAGcV* z(sey++Ombx@Y$b3X&7#}zSNR}C${^vS!f9(L@i+=vvgI);v4^n{PE<)%o9e39wgl@ z(UH?~lpyT7eRS*7Be5U-XLQ#Qbbcik^?mO+Reu1(vC5ys5cf15&y^Dme zp(wFUlQ~I;j!2FBn@$Oj7S>KB|-z+U9Y zQF4ict90T5Dy`>@yKKgVYEKy*Xd6^`=SCy*#xdaXfIk_KWvIJiS&@;iD|W{IL{2>Y z#~(WVO$<)Anz|X?zqdoNJp4L7&euQc@5?_Qh<)wkaRb3>^Znoqdh-Er31TyhYDxOpDjDo6yJy`yhBdr3>*}D-H_wgWxMlg09Ez0@8wa7Uv|k}l}eB*z~9aN z-6#oK#_s}kv>WJ86&l1CO3@-aaFeEA`bOO+F|V%?c!7s3LCX;1FL5iNG)v9R0YhwDPJe^YnBnvLA6svr_04?nse zX^qNbQO4)vuT7?p-*$kQ865^Txy+_+={Qi_U55uuiS*4rZeb$27`ixgDzrD%VWEJbF8P;t%gHK9~3Rs@Ro~t{57kO4q;W6_-b?8%mLf z?K_3De#5B*7Ko~(Og82AKPf*NCzk)kfEjZDqP-uD;L(|O`)5{jK7Fjw&EU- zgif@QAA%`4IE&1;RHRrtglaPbVFFq(;-O99rlJ3PM|5()9vj>^MZ@iIH#$lma!94Qs?pnh44K4PY?HE6rg_2A6I z)jo9qwr^!ElSYuQ^XHM(q4$!!*-l#1bmJQGbBz~|F{NcP-40D#0n>N}|GPrAt{(q;Z! z@+`LhXnz;_yJ@HBAa9^)BWjE{i+jdBcAHl)w74Qn)!oDFu|&N@MhEK=`E)ikNVb%#9MMPGT!Yu_I+*J z<Bi`sSO;nO86CV{`TCb# zmX0FG!J-m!ks(lP1GqUJ-K75-<5DEdlD-<{?%w=uDd%FLfR`PB5&RnMTRJ1!e!Bp3 zI~dh6I>F5KI`x4)H7gGXKFB5&;=kY*b_bV=t$>X6oP#XI1t?^)W|($)!F?O`_~w0E zUDh>iXc(yCdpq#pUz$52f;iEWf#iEu?589L;}h?t6E9SWLr*5IMxp(;Y#vg3tsviU zg_Y-YX8Vk~Xh2NAH!jUu$|Bh%Yh3MtW062Z@*~Wa7X0oF`v=_g!%={M1*g|$)3M{mcl`oUHigE;g}($KGj^no0cF)T)t=|?1B z0PATID8w=)Dca~~X7itw-tHpBaMAgK5=rzVF4h87vf*Qet>gVtxGEH!cBbM{L_oeu zz~RlwNWfGxcc1sN0pPti{bQyNBJTET>;vlCK=|kTGM@W=;5(`i29qq{kOgOR&_ZaM!{aGD$qnwtUUw?6s<5x9#*>cfQ3~Mmu4gREXzA|hezjyMhn{~0h6 z`%lu+`KD|5_`vsGCMY{$BJ1YndSuimh38YCRBTOu8{>mLTu^9KF9|}mFAHd{+o8p& zI^w3mk<_IdrCM-IG5XaA`TLm>)axxhfgtC`c|_KD*)AgUnvC~xo$;ceXS^Hxz*`*o z5~}bwyccimM>Y;@NAkj5|6qw8D zX4VP3#2h;D(5kZ&u&q0&DnH)#k#X-!kGsp#(FYF0GH7-sPbgiXPl7c_WG*Dyp(<5c z4@S{eqv?t0kZ1D#>QZc%{)4j*&|XBoV~hZmN}An^umlmL^vuO``_7pY#XThMhWn{a z33F;F<&rkoTR!Et3E;E78!#f`=-T05kdNJJk8vTw^x5Tz+CCG_*WrTL^Rm?2_}uH? z`JcrYT27cgUOxc(fJ&5uOFcP~MT{fJmlYELwV$(}|M-{_D@5zX7XC@dJ5wFB)AI;C z29)rH0c{jPGzR}}+?)*L+&|!m#Kh*kqJ$gor#y-Wr%uH6X73#@j}3Ey z|JLCUwvsH>%*}uGGY;c_PKEpuuz>&P^1Ym}w#)ow+rZ^xs@q*QH2&_L7z8HMO%^V| zlb7?YjZWFTKn2A2N6p z^Mz}*P>Xj(mB!FoiYbd9q1t?Ndgxo+Tc;E2?mI(H1!x(#K!Hesg~zC%gGk{C!Xo=l zAJzD=S7qT+yj^{3vdQiLE`9mo`Axp*Jqf;JGWJOm$B~27^-MB#^a*$^Y+LF{ITYil6YlZt-!f#6jp27S8AVX0Hf?UwHP0 zot(eg9%gloI?7#n@u4m3eq3mEsk^{(FY%4N7mqgm3Eli!kw|+=+bz@a{Iez4+jbWj zFpvt_uL!<_iy~k%$q5W?yA1+g8Xwl^!q<3O?hmgmxVC5CoMA(Eoc%&J4DkGhJP%%%OZ5OfT)m5I6o7ltpP$QU2n2W5QUq30~Ssuzn}zHQ1RL-=vlx zaswHQBVG6cT7L{2fNLeFUn%G165oh()M6tQxnGcuzYdo+PKF-;L^ptIw}_y_ZL3#~ zF;U5h*Q-lU=l^6ZtnL1|3tEpfl?vr_oa4@q|MSH62bV>P;O6R{T~FKC!L5G0vifo~ zK{1E`-eGT^VU^i{^iwUPAI%oC!%WHnM6@u?edoezg1S!S+rNcYOWmzVw! z%Z-EU+u9=>+8(JN6!te~?!%i3`;Xlec8zcl-9x>PnkQ7c=JndQJ7qk{czL!5SF9wj zws}}-loop9uY$Q!w40H@vQ7Oq?tQ)X)p9gg1@IeKWb^CP*A`#ZlF=B8F4fyIbo*uP7tq(Y+Ep*w!eLBoTQI+85^53YZ zU=YhMc;ALf?4m&yRHzCrV1<8Hd`8hVx5W_Do@#a43H2CC6$lt0__t31APFktvGjyx z6XznQb=j34KrHORW>tC=2p7h@`MB#I8O%atS>napVF}aJZO0JSIHI=5u;+dcoVFK) zGiQ(?${}UeU zU^d9F3MsB%Xj$)=qh0S>K(?MRd*K0O`^!5O&<`*>AIKXHxoKox_^@~VrN74?rE~Hf zyETM7d#L77DH^ziSb0Baad(y1eZ-4*r{Yu^LWw-`~m+V3Ar<%hpyNOW=5+y zb-1YARZpB@r&=52fD&ARd#!yNVjsgYcBCixEh3*6DWZ^9E~3US;KDk*%0?`B&EDKRbWN0bm@oU4 z4yz{TG_u5FAj@$7bA3G9KPPRvyMIqGpm5y2BXT$@_RH8zW324DgfXU9f*FHSMYATM z3x@>^43(He%7L)|tu4+hTkPwgA7T=5Mi2{PnjZ zjGYSQ*@r2>HF%aDymEMIwEe&hRgYuBQaa?G=Z@4`2eRBdb*WMo*s=Sd_#;e52OLN$?b9xE)ri9+&Fj?&UV+MW!f|4c|;Reb;#U!JIiU9@uer7xTC3 z$1sZMj1ZCPFFWQt2#h3q8o^CqOvaI?2lTZ#Hm9y1MP1e{S}1Bn4ANHap?MU!ob$*GyX8 zy&KPb5C0I3Tag^KP+B>97kCavh> zW~Kg=9FAG>k*K|>+R!-AD&$hOgUKucZ;`9PdIv8S*_qyZ0IuyiQtTB&%%V~>eZ6xH zO|Kq65XP=w8&!VMzN2Qx3in`&Kb{F%`8E1MIvRLG=IgV?U;azE@hHj&Gn6@_atD-F zh?o7{karJ(|3Yz70}W99wWd=E9HF6XFV)(Z8>t`3HinoRSRQS9Mqo+rig{ zTb#wv%h7SCO9KkT>AQ{RWthVolZUs81>HG~XBFEae$S5K5En69MeSWHKh^vs6r|6i zR8tQe0s?u)S@?6CogeTvW9fQq!{^g0TOo%OU_CuJo*-B*86ALPS;vQ{BuMS`*jM*_ z$a~@9bO@Y^>$K#F2v7^kCANWOwEqyr7bP=Yje5N#$FVYOdM?q5Y^)jHLnP2ft4~;d ztxKHgtAw$a1mSgyO*QgRCr(kO6(SRuXP739SlxyXR;b|*7S;JLJGl%GKt)QVspHZB z+l#Y8(|6N;x>gLLh7D+&L36#B+*Q7x0wz8vxf6ERgvh$epAhMa0}7wt z==7az3OLbKH<{qT(=fo(>CAmPv>s4Q%4@Zd%!u+8FLVs2I`^D@yS0Be7HSG~i(Q~f z>UL$;nSmgVLP8BMSl8tK;d6XQjV|R=arJ|+DC5fkZ1fiIUN0cOVTZdATN zS}Y=WORge)ehQOZ(#iH|y>)in;(!+Hg4}K*^?{Ki5{aF`XCSpfMQeN8=$7q8)pt{P zEF5O<#mA$e#Y$v#s@aD$7hR9l!PDCG*HDjK>nnbDhhB?vE5Ahb29c+Z;NgSJ4LhSR z{gFLyloAUN1Fobr@dBd4Mt;?rNqXPA!>w!X)elSa)I z!fhdFVEe)UNEjA@murQd()N8u9Dlc0u+VaNps{lTOBymaTyI5k?hYeeQ0i3@ z{7N+Sk{r4;AeuUbl`-ZqVwAy$<;&XrJbSSlX}iqF-c8|Y=Yi=e_ku|DA4}d2zeFM1 zY-(E+>DB*d0rcBZ%uOCgaLE?x;B;->y8Gj;+mk)vb%7ClW4fEiC89p4IIysy0s{i` z17-6(eQR_l)x&t`SX(3G&G}&ggTHH(7XyagPCxG%0D$xA?biL2UvnupOuOnBr`DV4 z_%He(XJyGf6qXRFIR2kdp=e&zdjYB$8H=s6vX__eO`AsunBsjN9Bq`x&a)GOa-MgE zD|sHKH#_>nzHcB6OHSc}Ztkae!d7s>&=PHoS8!fub_aL%T^{0#_m>z%Hm_1V!Ek8g zgIEn_DS(~kY)!!J6C5xbO0^Y)|AIa*xvhDl%HPB^Lr>% z{Pg~-v$W8pG--nmPtqR|P;cnOzorJj<_!!u8`f%oN8vA8BP2=rH z*`t4zKW0dJW8Y1@Q9Vju5-g*It72x3-ophw)4RQHr!McnggYJnNWBN<8ztC~ACKA5 zSzvl8^2;+zlZ)+qMG%+0xnF3JL5Qcd9NLdp_k*&VrQ`--APUbRO81c-h^cjGnDPAo zNIDaDsNTPi-)DCFzLae&A$yi6%SDrBT({|KeLt6%lHOp|2(qXDo7 zMdovgK0s+ks5)Ody5msbi~e_hNmb&a!EcA6X#RuSeoiR70%yY2eic%HK}W(>F}>wD zg8|l({nhi3HK$P5_4EN=InCjbGJ$TPQB}G`%57)`0S%V zQ`j+&U&R)dxq-(lDs;zHOoyAQB00K-RCv-$jOWm zZw7sV$6V`JUL*_M(VC@+PHmJmUx-E6TgJ|-P~Y0fo4bh@+YS2#tB=q7nj4Z&VZ`@? zysQEA5mqmjTnz%&^V9i-li^Vjw^yIHN?uQdU0o(c{@5J=IXNGwy6S;!odS*P-7h&e ze^HY#9Rjmp_Y_gsqz>rCGZhV%>8Zbhr{W8aBz%kN59OMA$UfkeSm}aSjDQE4zuesq z{Qgb7nJAP=EC2d+!?%pXEl1E6k>xWRRPF(fBD>@PGE`$;yMh>(0BN=OHM$O1eA57A zKxRWLl|FoE2>!8t!AF(+`QWXjPX|Pwar9dA_4@R>u`p9`8yS?V*`tYvz%jI&8F9?9 zc>=~k{pY3l%o!_V^diVDOM&@o^Oho4)ALr72$1sE5)=#wg}33E@N0NOVP-RT^MZEM zsmH=7=}In-4cNKZy+fYv=HfQV++V2zezITIrtd=+s5ADU?ub+JyWU}m@!p?A#o=)m zJ@18EIoSA>q|+d3*wG3oq3+x4r~To4-KErIGBI}@Vu--Wf$S5)l<%;7#Bmb}iqzP= zGX_|hQtxr3eq+S7E~m~w@x6v?rzM%8rpR2&a{l1p=XEdHiXM8y9vIp9N)sa`B``$0 z!F{1LZ3dq{6lBH^Z(21VQcuNLIWIU%XX%%rgdN-P-!<1sF=5~5o}gMQT|oF1pJ!@i zoZqh3qX>yMnXeJFGclZ(fcUC#Udlwi9KU{;dccmX^1j$-pBLA|H^=$;Cy^TY{O#5j z=ZAtv_*|9(S6N;^O?W~&m%0F?x#o%k^U8;~;g2MiS`_{94YABVO~$uW?Ykfmy`2bX zNuEP(>CpVs3y72|h~|(Mxs^uB9Mu`2nr7utpMZX)joC-1q3jGEruxNVM9U5%yy-RD znz)ZAFH(wGeSugcO$~ZTg=MeEsYPWHvUf=cAH^L{Elu2`eSwmL78W6&jyDsdwTZRM zoG^=xw}x{>A>-VcIV}Q&VY}|7$tNl@oYE0PmQXU*FNWhysXBWNQRgw#!``^Ut&7o~ zleBcd@S_-!HE#3W^&Gx0pW673zdFj4vyOPJ-^zJz>GJ;3Vw)ZCLjz1Cyfk^ub<>BB zvKRQH&lodw00Elj88_<57hHXY?Y!z`O)3?tj&BURp$MB>vStc3u$c>pg3F@REpNxL zV&E%Nt<5JXqVx|rUB2a{A%YhVW!Z+|tYBgC)EY-ijX9zVV)MN-_SRKK`$66%^D0L{z8 z-*g&AoO#P0x`xF-IQSg|qdEn)pHR9Gwi3M&ymzfFZtm7InNBF}DJS~R2dH*X=j#nC zkf1|F#O zJi5a_p;|s^KC`O|**buuZLhX%y^lu`XyGH(e84{LOMn*92wopJMC|24p15o8V&n<- z%mHS>3n*|KdG3D8^_}DwIN6<0dq+)*T(Ot^$Mv;PlN%lh1juiS&NT)q9nFGyORs*Z z{;C~VnHV=0dP2CzH((9fo1yLUieJF#eiQ5gT0o|==9L@vt>qOlh(anYci1*XPTGln&K|a z3&U?Q=AmigpT$)*M@XxH-vw|oekc`~*T*m3GzS*1#_kEZXMwk@wx>ummYBc41bgC> z)r((p!oZCaq2GrTq2WIV>Lo0Mj>6B;($0@`4Zy8GJ3(cS?6~>hbJBjklGXPB{P+}v>-5!>R z!!e)$#W_a?66pR5X20J1;u*IY1Bx|BN;sSV0Rhkt6II=n;-9RRoaG}o(JS#pLm&)? zQU015!8Luz)|>n39dXNwCU&agtO0{8aEI{ zJvs3Yk1F*Mp0pr?sN27Hz;7X)^L*r=^f;gEiO<*D<)9T#f*bYH25XxPN*eLrqDL!l ze%9sPyg`~4Z6ew7s08qT<7raD_wZrhb5_#lF(=}i+5WEbSB=NSO^h|-Z68Zjyvri5 zQz{e385@*NujuHV!uug>cCKbgI4oN6O$9C+dgKtS0euE87JqCa((6MIQe}Y==1A-;h(t016EHkoxP^k$YXp(li*n z&8N4qP>o!BDpa}DE6sFN^MO^Ze%}mFusEr=u!L-uNR(yhZ9uC3Wu4!agoEW6OG_ig zPeF;N1#JOyWIH)epvVxu@C~y$HFeChQWA&&aa8}rqL=CH`)iY>>o>jay5%XB0`Srd z;XE+#SX8rO{@MFK*^d~rLQ2Eo5j6v2W)@^TIj^u0Nbc;>qO75lIm#rxmR>OO>e`j~ z!-kG+y>{1UU~v>2M6=Q%QcB;41Fmz^iFgEv1sb>5ypk;JD^{RT$PbAxvNy8o-AP}^3JEudSnTUT;3;C2yfzd!BO&69oW|p~Efo zD?ltmx}U%0s7p#W#U=q^9=&l=9}0>FwhAT!A*`EU_P!9oFox-pN|ZnRpg6tl%C;bM zu^Q#^2e@E#Zd8<_7|z&D2pMok*`40CC~>JYOelY4Q2bmOOwbDpf;xf9>*b%v?t_)5 z)t$9xccZuvZH*dDrUR(RaB>f}!lBNQ^QPo%eZo$$v?$oqMff9B7Zx6|R(;~(9*my+B&gsC zkz>5-hSbGMP3zVWNE1HEf&m0f8HJ{!TU_>%;Uv#_goiz*N;&6Kc}~7?=vE{;M#n}x z#J=(guvirVKAdLNAeEF&h4Wu-YR{c(n#rED!vGHMa^uEx?IqBoT zW%xxxzmFSQ>llGnM}E&Pb1u1yR}o~~7qV;fe`-YY+}9slRU-c5UdoEsFh^4O+wm2u zrWeReQC-8{?W=dXV&*9h7Kf(SsE?+G4r$z$ijhYm4jOY5F3v5B{fcA>*>4`=BeMKN zemszE^p4=J`h6D-Fo)}FdUC4j<=?;=<>kgrY`)~J2YAL)8`Q`H9(|CSL)|G6n9Rvl z>jQG^6H6c8i6ksSj_%PhNcIhlj##3zYU*1d5y`4q(?oFg@k>}>@to~4zuV$50DH=l z|1^TlAJidZblPrjBF!|>-m=sha@2!7T8oTOhJWJMGzZ*oyhW_;@>pk+=LDLaz%wSq z!c36-;Nw{$lfTMy^;mN^r8Mu~mVj}^{eu}8Ca1a#dBO=!3!7V5mqiN`z-O(?z-(6o zUp)Ek6^oUpD0acn7(T%>#PIA6Mj<3m{D&G0{P)LzZlVNtL+cOWhtf`O<1c<&(MFC`&=WrToKQ`1SjI`_zN*ao+5tUxUS>GY2ohNqE{Y*c-smj*VA2L4we< z-^UMw7ENm?^5P^(4nZh$q$y+!1-C%+=ndq4;A;MJBtkhjs*Rs(2XPRS`eN+DQ7hnq zugE+|;)|X}dN`3u)%6X<5-oVR5hkOhFJZ2j8}l=`!4^)20x(7K2LI(W-bJAW<`0L1 zCzzxi^3%X=D_8`6Zz@tS+3bU|e&PO(CvR0a(Mdcr&x8H}qz3}--n4xYskre;m{=}F zX3^8HOb>m93{zm$CEd3Ukd9`~KG1ze$5B61NzrGny z-3_^1VqQs~+WDJftgQ}|w<8yxp*jj|dUK+(v0tgd-9s4|MxCxH?3X7tK2T416{xql zwirKQcq2xY`I?U=K_gXc@qu8?&jJ7sZt?@+XOL!fBwmN?hR_#T`MRfg$rT*>@ik-$ zKtwDt5VWx8%*v>qa!}@O)_>_nJM?|^35R-H!~kek06UXkgSac&CgbGA9xJ9<$mKFF3lajJhBp zz%-b&76uhvY0&5sjFSNH2Fnr(mR^k17oNXcj_*W@H~#a{-HOxI?cqTu^Y!o^C+mRw zf|gG}(LoxXJjD6<`T4%R^rJwSzf=k9et779sa!L{r-2MSs`j4JFwFgt>0W~3y%D?P!Km_2_LFAQf% zB3XWrEF-T~l)a(?0dgl|vO2o)GnczGVQxNE;9TGeosRwOnnun-^U*b2J~vmtlJ%bb zCIg%TX(+Bo^7Iga(YAKcj`8_?2bGI*>BSH*dOV1Wpg@8)Bf%8zF1yg`8RG*FQP-9r zdmYBBORi^mi1;-xS62^_udn1XGr0&P_K@PmBaa&)0u9WT3pfbZfwD|OW`)d^gzxVV zH6C)nVd}K-!A3EKvz)bJ>gFoRx$`I6RE+p|`;!M$l}n73dV6^J5O%S$+GAp zO*Ce$LND@ij|rQ(?AY5;0l^#e3(%#m>1Yh?b&8(=bA68jO+Oo2XXtZ_*^XG z(@pj8(8Mv!VHoUPjF~4XLyfFw7eIL6WHN8QKC&f<&wQ-Vii2@6;Ss|3eBZGZQ-GY^ zeFZr1{IRY;SC+dv!hpzavX+w6kYxIx%^IKW(V3;(W3|(s#B|it@486CHcisa^+_p(505uz{)cOUwj^_&J z?lbB&WQ({;9dk$SVOtTi*_dnQ&cLF?H~r0|gaL(mH{#e(6HD+lz!kt*97yalc6Kkk zl9ruRsSXS%WMI))wo>K;d{bCc^h1l#1KIYQxXMG06V-co^G?LqO=Vz&G7r)XctyV| z1cLc5h8I0IhQ=xpVLXk?%fF=z-U>OMn+z?W304Wf#YC7Yn6>mr=n7jv049nzdzhAZ7+KJ z^p+{_h>@EWF5q8uHm{w2&SoulYy6@Kg{Ub+b1q1M%YHBthl>=F#$UA z4?QLjUBK&nIH8eZAtGt+96RcF$sCI|SQ0vhWG*ggEUpiSs?U7Ao5UeLL_1t@-v0pofaTziT;7-yirt!9 z<~@&`j)AlDhvwo}*VsNqEN+>*#<5qGxi#=IW}5@IEcUyCruh=punZ6D+AEU72fSD< zg5;gDPq{-YQdN3B-UmZ8XQ1B1KRUss*2$QpwxbxtBclQvpmDQr77ey5~Z|P(|2To7rOc(s^*^gkCL{-=juL z(P<4l(JBSxOH;l`yUp3Bn@G~M0Wr37^gD*ap8f-phs7@!fdP+fABAROLf8xK z!!USpBp2LRK2$a{AQ8lgxzE|7BXylKIrFf1M^V5kn@5WM@E3JL==$_Tfy+?+MD4^d zch&0DLm$HC`OYJ}(`uM~NEw&fmQr{aghRkHhhS;92zRJhBT&RCz^UIoA zeLK$Qkh3O1A1_tJgsD$T2;7fNq3>$^bDjrXqI)4AW~N1Dn**wKC*al;*>IhtonxB zk2zB_RJ3(m2E0auBm#gZC;HA?gy$JzYmt7detRWS#+f6qk?o6WZ;g5ns zMqW&#*BdmSk;}`Er6$ugcQzRxKS$p)kbCln?VYv8iMBj6Z@caW{EkDXRcT42~}Ql z7ErB`3<>WM$_#$wd_06EZ@t{S&4bpUpoH>#j1>tKJ@;&bXhgpK>UBQ7p}7d#v*80f z2}C}(BcCb05eXvUpMrpUFpaH$?@GL)spQ(Og*l(Rlarn`RhD-?n2o z5Vi(EZ#M54q@4uCS2&1elVbon?sS`|-m_5!T?5*|;{4g+uQyJDIw&L>^;x1FoYdL$ zdy|q(KgYZ!6TX@Nep~Bzt zA5pIY;AS#KCTEw@W%gEA)Vh2wn9ofEmrXXyRj_1XSnzkH%kGR%JQ{JPi|FM9^nE^B zRO!lc(yK^<`MNkGX9d0c4y@_8;p>2Vh9}5c{);b={+F`w#j6^4Xwc5eT+l5Q1&mm; z06#R7YnQ*$`2GW*vn75%|n*5yKvD_(^!OOJR!(vb?dDvqd~FMNiO!)E}*k&{38~e zVsCi)$7uhi(@fjNvL*PNx*+M`?TS`EXX#7r9eZtd+nn_Shml}HELZftBdGnp5wwTm z>QiTmhfWOSi>}9d(6l!M`6xYPYpRrzn58t?1WImv{KnGIRZv=QVb!*oCB@2bqVjLp zf7)~37eq%M;+C_apGXFQw&tD{H$tCC?<^H7q!r*p>_GMGQW4Mt?p%^PFHh$4|*xmaG8WN`uKCiC$M<)RHIGe})t+;e8iW+SD9^^pBKAf({1g0Vji=siGg_Fw_O?;^q=cOqFPk(Ne4+6iC zfTqX@ge~!`=GgFzYeShuH(L%V%#V>#fgcyaHi)@{@#&%Eaekaiz01+Qw?kcG>aaJa;hFOM!Fc_7!*e2xLOl$$ z*9ElL{MYpy!{Rz8j#K-qn%pO$lv=(;Xs3Mp#Kum(BFbsT{>0c`!0@h(F07)y622=) z9F6Cj20E4|-&Y9y@mXAg6n)2A@J391cGh#3ba!BN))w7kHEv+&sY35pYVmv_a%vW- z#XUxLR>X^<&4ihwIUP`3)o*1SS;`s*0PV*-)@!SrHO`^dijp@DJenDD8XD$pQ|B$! zdjQ_zF32J;abY_m{lfG0E}(AgJBecca5EnDJ~2@}w4e&4b>tS@ZN{V9B^bvxpL3_E zuU|o|kT+iM2zYT9e+F#yhazv1h^Hm)zEPww1oK|r$uohj%8~yi#Pff1yB9B!2Q)Z0 z3#;3Fy%0hx@(XZ7jvgzAE=ah&wUEzX1+N`vQ@<+@Ff!KirBM`in^Jjp{wnBHIL4uG z*#9um?%4xWc8}Z)1%Wxu4&*dg-Wkrv3lmfQ$g^Pcu@UBoDzm&fOFGjXd?~d!bby>1 z8p}Sq9ljd{Pqj^{8KNH^m~k}*ZzjJM%_j1|Pay0=xuqxy(If{t{3`a@?frZ3(7+ah zE<_wmS*P~j=!LED!hI9@m5EIU(f1r^?-NBgP)@4CJ?I7j-WM825Yrh2F_8ME^dN@f zhpUVprv-gP6Tf)%e-z1o$9dfpA+Jn65&uH;vI{BB^Q!rEj=TXGZ?}w6f8DUU7WDbd zY5|Adv3ygT5yxXi4f{CvetGLU*^NaFYDG?bq;A?eB|lsRqkIj~jP4yZ=VID0A(2lK zJt5C^)Ay9+nE0Dfz=L~vwOamo53hoZ{gHCX>~w52>Il08VyCVkAr#2^*G>UoQ6ARa zDc9lLCGZuo)nWG-@u!AwGxcHY3nZH@ZY_y`K!A#<#eaZ&jxv8`^D{PCCvOc(WAgf5 z(K8`&C{316FEkEhy#V@C#&@033^{UWS7!?(yB%h5+H{doZiyXvhZ08e|70KoQJeGv z{?QYc{m*W73FZ~Azjb=(+qBYqp15L6iksX6{eJ6M45#IXI>d{VAYtt{t@)XIL7>d7 zqj!-1V%}lQ9ON01e0ukppT*`E@A*T)PJGN$#n-nn_WXuiLw*Be8DBiXkg=yT?iBFD zlY^gpTnB%PQE9Bs8NK)Qe^~!45MyIuhO8y*utE*q1;4kI+}4aV{kctUffpjxh+GUb z^5M9!{UTj53UDglVQj+QM(Ld-TY+ox~>4CGXRi~K+ad2pZQAi&_n)Q{3T+y4Y8FsW>z)s>0V zK$yCLQk1BoU?8Tz^5h55B<`193o`)oX$D!x4Ur-hpwv76-ieaq;bxuCy`6_W7fXx(E0^x z{d4g#qc>_W5Lj{2i;?K9&5 zUT;ew%Jswj-ML9dS9vp=SO<%R1InbQDQ&{`J3WttR&Y0R?$@K|LNOnfnq3?C2zJg6 z{FAdK^v0R(TGG-YXES+Ey{8_=(>{}T3Be-p*RS$COjh+vuMZf81jD?ZlLndCl6P+G zW#x{bo%a0vu0HY@4{K@|L3|MEx`7{&)uY-zK6*)KiVCCYE&Y%c)UrFw2=cjjuo!W04%d*bLk0~g&C@C{Lra=e3z7@5Hahx@r6w&u!OGS1d zm-@jrM>xlp+e2j`6QbK-Nf7yH`O-2XlXK@D>yK={;$H3nl-@}oJ|RdnwT+_w5IgHD z!k}dCyDxgB%NQ-%Tq|lS!lU^v)>2Vn0KJ=}!$}jQl*qwn(Wb_*t~^Fql)R?u7xLoo zCRZcNeR!=)El2H}jr)hBranPy#rls~W2%oSY)EwreE|Ofa>}HY-*BWs1jO*uxb%4U zqouxv&2saH$i4D~?_L($4{%ABJ(E`Ns+1PJ=ARVt=pN%i>9G|a26^+qLoEb;C=Em` z8O?B(giXXv#_RGAQWbw}+*1_}>Mc5PNJ8x@>i$Wq(!8Gzs(F)Q=k5YK7S(~SyoflO zuxH3yYR^6@OfAHen$T~n8=4g3SkggE=6f%39Zx%v3fDc9HLo>PJb1_JR)8TRs-2;) zz;}}T#-A&wN5Y33!ZJ5YWF3_ggbhFdkeXJ97##y12E)bAcQx7lvxFEUB%GH2wUR@Y z!#^OuUGWoCXWd8XB+S(1{~D29t)xIm@(>X)`7_Z45wrcR#&4y_{^5U>V9^U9KWC%< zo3~Uds)Acnq1^2}**O=PX&5N**VOhYDq05!?MChqn6{$GTJjj;tAiBEa5=J%a#s^B z_+R5>Na(d8$DVb>p#Nrc7qcJx?#cKGVmI1ErOBI#t?BHiid;U3wdQ48Fnf3%?FXoK z`1#x^U=f?+M^r8GY%*lb{^5B5!fo9k!!M}nN?$&*A1LCS=3{&zy+C|WPJ4~1(d9S9 zd5Q#ekQw|#-tj;XJiZnB&iwvR)Er`#zB$ngSw2HPZo}?TeAkX#*c+RCyiln(`_>Mp z2(l8j8A)}ZZ9~rwU0Vu^m<^23E_zdeu5eF1DFted1Ln56G&RQ*pq zzDWi>a@xrA94dujtB2t_*5NKhHOzg6I5$=q^G5IEP}>$gepM(=U{0a><*X2A_TR9* zOF|S(ZQdRbjj^@76uLq5a&`j(SCRTZfv+| z0R=O@_S;CVB**rLfBwUKM=pk#MZM0Ve&CoN6(YX@liT6fk2umf-`Q%Pr?h=Ghvvx} z%~v0+WTQlcoV?KywE^J4@>kJ&S^6hci+I5rc>FED9b(S^`31LGNQ}Qccp@%J?+^oF zNhQI=Gu>yHZGBrWGp-A*?Gpw!4!`N55NRET#CsE;d|gKd_H;CVO#Lc${GzCyM{L(; z=#u^BB2rDwE0l&e*iEjN)cKIz#-5j3LW2uLrCqVc@cjvd02Rlx=}p47$(P8Cqal2y z4FR;@hv_$=u5h@Pf`=^m;{Ddz-qvipNqfSpasYhhO^T`7v^Ye7HKl(bd4H^a(^mv# zFCJ~{cfAya|8#KQ8R_;imaPqJF3!x^hz-fKS`R9vApXJn*3^R??bVc%%*k|Npoif) zQMcJZ&-g*BDi06STm~`3w!D+uybLde>Q>~X=?jZNRfll>h;Fx$&0*LXm7MPlGq}g7 zD8PgLa4e#A$Ta{Jf(bf1pgFh(oLYV)6JAT+;OJ`I>Gr4Zg~n_6hu%*Qy}1({t5kXh zb!aK}#7a?2I)|F=h7;j&>f(ICG+bG<^f>iMs5B7x{p~dZU!S+JcX5Ui!+<}qUP4@( zkk4+5hQ^@G=r3}vp0FH|d_QGY|G)F5rc=Gh${z6DlJU^MG!q%4#Sc6Cz&04ZQz%$k z&-O7POu*^XWucGeQT&t;h7fEH=^BYKL?DQ}T_7@lf3;!((hwgXxeTUo=zswBu4bu0 znGf?X#=f620j)woV+ig3sc z0z3-|ujU$^Ct_&NLbaGh7kIIZ&-pPR30c@~(QLv|7NS8);3TRszjlj$kKgGVqkvJq9`wdNbQI)WBFm;5Sxdvf~yBR*nnv~s#ZZnRW8B$yFy?3iTb$aN2KmL4y z#jF@BTEBLellPnvuO$w%2i`zMB$8gQxW44k;ZfCsDju#g>GhxC529&)sFY5cw?Qzg zie6NYBcO^Jb^V-mFLDYW?rPp3DJ1rZ=TN1Qg?pHIrbKCA$hef6qSn~^+f$j{pB9I7 zDrVQ#Sg7X!8Th*O#b{w-Njb{fpMeZjFkA;4wBt zWapf+KKVx|YW@g01A9#v_70HClxx9WUXu_NwmuBL~X?> zrAIC`?gRiBRz0_yzRfVZ_ib=Dls1Lf)!&yAtK5-|+;O2+QjQb*kXJJ4xvyVgrBC;a zO80?fY!C`{?7AKM;saGp-Yi^-#T-@D%LbMCXzQ|L3{ltGtbE>n%#{1(+XI#UXudwi zf9$-qwGw)??5dQ&qMtWJZbeRWd2WljC0jGTe~?#ESReprg7X`}3#7fQ47QLRHyO5e zv(RMY=!RE+kWSh_e$ndX$hO@sh}-~YIuFl0u_zmq#UPOH;_~hsYaezgD7Xj=YBUZ> zjo7G2!>OR=KS^eD@)U3=jgOJ_+s!QZ#4{`zCAqB0rkAOyYRjlo`|+h`L>ZUT1;9CI z!!Q5(EugWXVOdK$tXQdUzWrGGK3Wu?wIGp+gp8<{PD>rx+Gu#=G4@di>R=*bT_&KE7cmS-kGUg=DmKS3tqXEh$kbv&;j3 zaZ4@Bi?Hu3h?kAUY|p#9p*yf6oOD&CMD?~{%MtQ5-2J!a^Ot`c0mec;?K(H=;WzTl zAHPC#*hvaYn(Z@Mc{WmTJhWDjm80vtZm2O17fZG>1r}poYeem92sG!flS_Y1)wPa*1 zCTqPmMuEe&ph)BAwu4RZHLji)Gyw7~Bf&P~M>yJvk^2V(7<(>(Uz}9_j&Gc?3;ck= zwKf5VcdP?!Jez7s4~+pTBZ^Rn%fsHzw*9g0Xv-!}z4w09vSYXwW*ttY4SW}MBzB@k zB$=(-M7bB%v>sr$qX;6*iy?P~zbi+fI7{xWm?S}l5_hs8Mv)OK4CTBQ$c8Sjcm=@P zqHdYgAczu#S7o#i;j~w#mx>-ZQ#I*`%&sxx#7+8u1|Ku!2`tGUskvh?IrFc1I+YiS zWH2Uceo^OejH~2CD=B@wAN#?15NZQ8t%qHP8YbHuwC?{H^dRdEUHK>|I&&`wy}u4+ z8BSaSM^h5taNUiJ^xIS{r|=qrChCPbF~qgw(AsZoz`AQU%Ip=Bldt=>93kID@L#vq z5{II?=ob_XiKiJ;YF)OoYa;~sVqLeU@j}VwI5IgP04t-uBs0)<0>$sqez(7taD>WEjQm1DwllQzqbi(DaufC;PpFwD}N8NIZs&`u+9bI z!~5jO5!(k7tZ8XB4CfFpm#RZN>FRHBtE;4^FChKT8tK#M%V;o9;)|j|>m%ZD5khTW zY{P-Q2=y$Axw8v7l!JJMsl_d=y#G{vc+aCcU z)?{qZqILNt`1p2{hopL1gA*ji$}mHdpy4ZYeEti z9(^WOF6t*U3&s^|Ya++Ti0{dkVBPP7U+MlbMn(`%*& zt|P$e@N-q{uT#h|nWh``_fij+PBM{E55A)#Zjug#M-IE?U2P(KJCMN7EWLB-(7oSJ z{I4QN+3LeF^EKR-cSKv49z~hfJN6jtvyFX@_$-S>6q(_=A{Kbq|x&gAw5m88$J66wHNNWFJEIW5n>Y&o#7Bhs|z zEIf?2tHvf>*vn(Bv1iYVz@1)|az}>?oj~>EUN|~K1P+iVnAPn6hv?gzmxPJDgnV`| z_V_Xe+jVj1wtu%|;(G)Mw?P`t>|CFm4l2djE>a$s;-95J$T680k$O%`23K@5oB6P- zkmb69E0~}4*j-L%$4PAm>&ql_yFsz7{CVmNB)1%D-$cx|+Ae=jhV;Ri@F4I8$sRKj~Trj?gG@%b$?n!FQGHF$>*dKu0%Jeg2@x+6My8T ztT8I8v|Sk01B{#gf#39DS2Ac4_}iN+S3apS{&CKCd@SI5vT)}p*v8{`LgUrZBbxikl^+Ty zWNS=PufhOT&<}dTZ6t$}`k2VO_4*ba&^u>MS~2J!7vrOiV5#1voHW362m8_47~o~D zlN;)Us$6VaK_uppv!5nwq7cr%YlCb*ce=v*Qe_0^5)&nVQ|qp!5xgbiyPd&eXUSuB#wF0N8`h9hx#3K;>DOx)3hj(B^%7F#cvOO${ zx|EL&2mk=`6NZTRl5;i!U72|fZjwFOH2`^vTvoWlSt$VNZrWw)>m{FOWUPbuKX4S%LB>`dqhDI zoAwLNcl)yWqZBY3bp^nCVA}_r#2GFh;U>PGN=P-T^s#{kFj{O)z0D&W0@givxexX2 zu-zX-UI3KN%&nR4DE$ye@)oD%s!!x<^R%m@jvPigTo(!HeHOjk@N(z=;wpi|94DxU zf`Q|a1toI^w2dEnsQ#(Z7P!xV%X`34i@}*PQ1rs}7E~kywuye--;$7HjnbAXB})Rh ztI##2Oaxi`OhO`T2VWYyp!Me9VY2L@g*oPSTxGX7! zHYCxkj0VMfAgBSJN`<_3KO=M4JJg$kk8ag-{Xm>@224%+2JdGkeGUV83)1EDB(^|8I}sz%)S z+VW06=&%ZEmUt2C0&dXKH#P+JqQ?Z!b*g@pS?^HzVKIh1&F8Im~`uz0XPF zOq)xwK(R`0iU`=W^5<`#%w;24h|nTZIsUq0B#b(7d0^|oKzAlGxRF;G9Fr$3JY5Fu z?S|4cX)VwX4e0<0HOlQhyU@{9eDH4_0az$W>#*2A?o6%@Q8uXVJ%9(gNyAHv z;o(vEY*|u-gNIJhF}Lvwuk4!Dn0~6r5#+lX>V6+zgBVFF?x!f@{mB-pJVZ2Z8^9@o z=s6iz1^3b=#-<~&euZIx7vW(NX_g0|M&S_Y=RBbSfU@4q)&7DG(Eog`9W94bslW^63+qh1mX zs5f%$nR{WQpd@LI>rp1v0HxQg&EVvBLab=ya{$yJChZLZqNvDR@VCe-dMa?=-*4aK zr$}cZ`*dvS?Jp#nv0M&fc?~Wu_NC;(=&Gv*!Tx=q;@pS`fTPR4>Hoo2NNkmQRCfs3 z#*PYH>i}X{@vEFVErxG8yHS?%19MoswATK)7%a#TSZP6<(8T5q?6x@kFSIK0_uw5z zAeq~cxZ$SzcCx#?y8!ZX`b>+;!c{3qU*-6)=!pjthw12$F=e+06*$^f@;~I~0&^1i z+==tZ&d~j=$QiuvAc*6Q zf|EdhEWa}J1OhBCrTC!WpcW`I9j=6!$@HfvQIy^_fvV82f8@3o2eRH15(-JvX(Lcg}TO~UmJzz&n~cS&29837_CSA@H4wSqSNAyQEIT=WTop zb(!_O{fnu$88%F4R0M8VQ`m*e?6rrLgnL`e=UUM%Vvp_hG=U54yFvGOg&@=N#YevP zp;spC^l5(34vG1}X4KFpAGFi;I~|?}4u3EN0SMY1l%XgXVix=VXgc#~DBu78U-!(I z8T-C(V~etj$TmZYNK^`?j8xW8lFBl-rKpIK5M{hmq^xCEGm@wjiYQ`)Y?ZMyX6F9R z=bYc~f9E*IjQf6F*X#LwJZPQZvwY0NH?fG)4qEZoM4n>i25`8uVXp ze??MAMs1xZM(6}}ouI3hU7B}`q;5Ur;>M{4KGhvph*$+e71R-7cDC{M1;ZD|>x!E)G^c|MyHdxsVG=D#X&9_~<*B!&6S!aR? zwJ0wc&=hDYPB%sw;TPeQq}X&}pfn-}S(yF+!&f2F+6~m;3eo*33?hQ~lG>0hX5`ln ze>PvJ}Vzw*63K=%kq1v!JLTn~5?k1`^_>HwDvriByn^l~l4Wec;Xn{!@Ar2OoK`ZkHHY~yMsLu^L)pCXz{(W#hDR6_>3~`C#FB99KcZ*=+D;&q~Y`6 zM=4*XM!?o5^h-&WaMKx8(imdQ={~N~lj~Xv&i;KcY%%M&7o3-wBlfRt_RTUyFZKA!K-gP z{x^F5Z-+~CcX>>IBW-fRK{#o%knQ}JOwRoWPQ}ffSBfGLsUtj~T;n??}~9ah)ntbDS%b-Z7G)3TnMS8bqk8^Y$H{LEs(l zBFu61)2EStKc6_@@R#bJ*YQetUNIuq4jfbMMd7eQR4hY0Fg}c}EkXCuaA3S>HuPBR zAX6;ZL#c2dSKCZCDBZ!m7b;jjW{iHa{Q~e{yLi)1{tdh6jV-l(ieR9?x7jw2T}kir z&N}hM{%8!USn;Dj?7CXWbsVYP@wi(5fJuO}3O4vcL7>>R6x{Mbhx0~T2M=So2(9UB z_s^Ja>cg_l-GE#Q)ajuIicBJ7LMUFFPK6#M=*&58O2r+pv0Y z&)0$zZE0{zw!*JC{?#Q*;#gsM(`N%=xh?dpG$y>+#-PNxz972th5*x%-o1(m&z^-? z><@69x}X+u?`2h-rzm6O=B3=iAIB|nS(L2A8iz>2)GbL(=^;P%WtuBafYaYqab?+~k~lFOoO8<8TeDvf&$@bn-yz|EferWonEN92Jur-N zXph=&-xBVF8L%h(C!~~6{iiL!43XrIfF=tGyzXEs8O%)_%8-zT>|D+{y$~W}sFLmi zJa}hX$yezQ4+dl)F)V|)4YU}eNKRmVHD?>JJ+m?T+Hyw4{A2Kz{muPS`t%r=0i>Yj z<`VG_67$n^!{BiA)qIqvyQG+_-RE0nUWH_E;sD%mSSzjKaI!kY)AIDNY4@0fU=(BfxD7nY=T0=LX(AmEP`FC(R6vj3dQ(hYE13Sd zLUsTI?7nAfOAW!`p6JI&Jjr|u+H`<&6D)fnqrXm26`yO&1gY{vRAG>YaBA&<5T{TG zK8&p0tnr|{N0B%A)y8`xs&hXz>pv=}` zDksRC2P~L#r#d*D6C}yeyB}Ig2XCSKG=aO1$T;TpesIpT>Kf{*6-WYvN5c0fYE6M1 zj%U)?F+$ZMeXK-XsV?hQzw)!^jxJO13C3>y0fS7^BN?I_eD!R;60fhvs;@p9eGBmt zUV~mk5-$ESSjL`H+?W@f4ws!8B^tXS^2;);=|1f6d^#V=eu(lPvUg#A`+#U{LT?YR z*-M?HR0n7#g~Fug~xI+8kT<>5u!1df2Ez-vP^@s)Q}>I(qPDUL+~rd~%!P zV?!Z&5-uG*i01wlffeA^oSgHht8oXfO%j=WtEO-RJKzaKGR7h2lS)Z7(61HZX-v=D zcxF(>V1NXn#ePPwghl-1D7W$PE0L1QUloSlW66;eB^PJ=+Q9qNXmO7gwFAP&;$tp% zALG&38Lr!(k4%n_i8a^;>3ysBY&sJlcL{Xn_8KdJL~yVbT-jm=7v56iLfk(2js+^3yRTNUe(lm!dO|nHJnmGJ_BM#i<;~uE zPJmGe8eF*Sy!MhuvYu`oD`>3cZHHa)kwFpmiGTJd$S}3*jLK%ERmQ`*bZBJND zCVKSE$s}{;S0j4yy=DK}gd&ZqR+jlg`RRUVr5iN|I*3?s{vEiBNiEox{BO+3^0dM}2l$7X} za*2T>l!s2T5no;e?BTW5g4W(z&@UbTGGc!ybrw&0NsVZwedapV4h`?z|0vcT$83&m zO{Egi&Nxh|t>Aeudx6e7yqwpHJT-QWu(lK4qAPY{+ehPaWab2JRXEdzMz^)lJR;li>v9VV#XW%VJ}BJ^=H6F;zq%@cE`GQM zDbb5s1u|&38wRBtqZg_#74!=H446xfu6b+VEmuvY(EInx^{iQ%)ZI{whoHKBv5)DhZF6A*Y*LLL4G%5JL|aUQRLx26|m zPLDCVnR*X+w(YJc-5(o~g6{4(!U*v$ng=bI#GFwP=+ws%l7vAG@)!cf^kSV8WmqyZ z!19FhZGPsa6XuHS^RIFGm0ve~zPgv!elsiHWaXbeRe5HblY2Sdhf{d&1M%r7u?<9UWkbuwrP!bUujUo$tm zwM&(Ww5u3(pHwFud4%utNq%AW91-WPB_s%;Cf@QXP1>UgLS(mdcKv_7x3~hCrDN|g zom;UwZe8lIUZQ;*Bd?m)tI`>bzCMQ0JVF3#y;}4;LN+PM%fdxak6!PfKqm_t&;w`; zDHLuR!RPLUi`b?0UL-WBUsVPq1M3H2e^9(da<~AuYwSVM%l{_a!U7hWD()pIE` zNAAA!%tmDNzNJ_r6k#>EYI*Z&obCy3#|rJNIDApEEBpcYYJsL>x$^zGL=p z)6rseeD3Zu;%T9Y4}Ri%V7*tN_B~Gf60GxmZ^nIMrAQ`*roo=#W$uK%QV>a;F0q?; zJMIy_blF@8giWJn*C}p|z(#+=_wr(RuW)%7W7iGQWFzt(Ajb7=g~Hi|KPTR4gN>wL z<|~LNARDa@ioiopLOO)+A|icA*sM+5uQA1%i|-Nh(}2Jer@coDus7Ie&yI6LT7cc$ z09V0^`||BYoj!E&(*f<=6B;Y$lVw7<5rPrgA#h)obh?(&o6k$n%Ftc~Xvj82iWCGW zc{Xqn#C{Qzh^6wVM=e&V=!kMW92@oTjKi2jp8HeK*IJV zq8E-{kzt%y*mA3E$jf~-%k`52y#w|3y|$tTdUz<5ypynPkFyjoL@O;YMW+rg>^J%o z{%1G555c*~XhF+6H*#Jd3H-a`#6~eKO$XXtkqUbzjkD)J#R=X^t{?2{_#CMCj?cI`+Ue^B;)l{Fy8D zrn#K2blhN_Sb3+Q1sRDG-U-fz@Sc}w{UtAT?P;36@bh?!EpQK9=2moypoZejs5vr> zcmrj6f9OI3Pu6M)wj!hY!pzN2pOSg9T~tMCmyR=2G^aR>Q|@?WP#&!e1Rfm6{1J@*$DtUZA|7atusubx zbkNCINGXh*Yf( zv?H`qZXN&h+G$y%*ET2QwvmP}w=7oP#Bnoc7_%cC2D9Qu1UU=v7L}Vij7;~!2h$Sv z<`wM$m+4TKx(F%hV&(u6vnXiIbCw*?qx)UxIZiS-L$vrPDbc<_L?(IlTZxXEh^{lo zBwC%jo*WQLJ(s9=vpucOtoXwh1gYFHwem1VvS7m!MyX%eWghYQ^5>;DFeSZK)s0`0 zY)5^B$~7Z(@qpLdj@X2`L4g3;j6kp`I@JyzF)F~vYst$fPV+wTWwX7fo+OU3h%m68 z6MKXbWBO_&JnCzcVpiy@g9g;#xsA$;n|~>)Ez88cpo)bQ5|IT!pPLpfTv%F){Mxf# znA*l`4&CYe`@5AJT|GTTdBBnMZ?}qi8pEHD_r{Z}eyHj}{}FTt_n=rqkeaqdK1q~S zUqL)@Cr^ZMJ`Hl^`$OXnbU#>gjXd`9Y_qMW7=1g&K?C;f!>~`)2u1)}AlDHOB2B1J zU{bAZ6)<@Tb$!wFU(>(KAfSxuF!(LpK*mzun5~++I31M^3SmqS0oC&|q>^csbKsGX zMQ8Bn9?a}J;vmSCBH05xdtjONsHxbQs~VfRQ@j6u)M2MGY$0Xv)xX`W4i=IJBiqCI z8Q*x2@k>)m{GNWTCA}5rmuMw_a;j*(KuwwIuq&xkrd(ck#} z(rOadbM+>Q68}zqW~uW8#l1aXw2OOe$>s+}&Jtuo5$yT>yh5lwB8(9;wHZAobfE@E z@Fk~Veaekw;HlUQZim^!iryN-%=Af+riGu1Ssoi{Orzhw56U^EeC*DCfl^I~e#YBA zJzDo_K^YMRm=d2G-+mP|-%$jcu$zT>^`IUFqqj)gaaf*xRh&LhJ``iTTucBbR{Z`2 z+!yIZ`f9H3yV8&@RurkX;l6(P0pS>sa5#Uv$I>lV^1zsgHa6mU9>!2EM`RVawx zi=|)I<7TrxD`o$Vmp|`#bv3@&4v2Rk2e`gYh^Glv`9=CRf_yMAh|?hX@;gU z=Ztpvr>awz4gb zYt5(JwF>Xju8dpC8b0}_<|+rTc^-^XEtjU^I}5y{>2AnBGD z_Z&NC13{M{_aCdO1`FA(sti0Yv!%GRJsvaq(TflSX7t|-ngxiW1&jL)c7*Cc9-xjG2LnA9nn zx3oG2^L*rCTROfk{=Ug^H$?_y0hvRT6(v9NTW4Ea%L*ZzC!1#aW*|Ry;qZt-Sol^X zAh>?U&XE4%vWajAg|Skp2`a~qKMJ|=)2lflLY7jj8};) z%1_F1-vo9gyLVxA^z_KJ%|!#AdMPJK-ip-wNr8%ZdH+4I68%*ad6CtL-1~qWnWDTl z9g%idw2MUrLh&sZcL_dnpO=l0MD2W3iLq8rBm_u%K`ze^uB~D{;OuV1_j}{RuZc^y zcM+uJcm~CBp8;e7Ix+cf7N3nvWV$FQ@K;A;YdEYxK8Y2)fgPqwRWjAw{x- zTj=Dz)f|K&qM1{njhv+nt99$;m>_x-~AS7)S2!By;ez9zgTa3Pt%Lwlwj^MBH((?_vPQ;LGiEkU|-thi}V^)Lvx!j-H}g@ZdJ`ou1_n% zLtVPRYp!N(5M5UO<}3=(uUtIT%z(hwdG4W%IkcgkGs;I$n5-sDKUxxR_-7!SWzn^E zD1yEjTPAH$g(c~*A}^ElAZrC_a5$cYkLsKAl7d9g7B}$j69~_JsL`>*nFC4juz?qw z{Xg^5|E#(^gPrk|>|+GaCRAzHgfH){)`!eO8t>H?{Xyw4OfeQaMRjUeQ<=lmI zuH~{&Rn=xjg&`8iR24y}MuPCi=rhIbl1o`qoX$b;0#ClP!Ff|WhJ`g@3$AA;D)#n|@Kpo@#4uuBpkO!0 zSN79VWUuLUJ|Hm;l4aEk5Q;B5e$%4p=?`8Kc;;55u#@!^a>^1rmy!-t%LV8^e|?eR z5{?rVu2F0=BvMeCp-1o;DyyJ@kMWkIif-tzr6M^xEEy_u5>z6?Ve>1Rf(%~?ejPkG z1j^QAKcbVRN&bp}Z!DFookh7@CJLhFGL8r`Hew!^VP9SW7JJ122}RHV`es0pk11Lt zNOj)S9}aggI@3&neURU!cd|EcvCBT4w7G1TSQiLwZ82bOxCNxHg;2$mvoIG9W9piYaL#cH&3 z9VX{dPbnpzt?Ss&X6NTH|7f+IXV2D-a^+>>GlXHDM*;oU6FOv^%ZZbFq!_G(+)~V( zv|E-PF_r}v0W&=2+``5xv+C$hb2z(~`>9rg;9KZpcE1kIq{G9lNTlWG?-;qqiIamuos3BVFgg+b;HP$oXOhz$gjDk3je+Q1|hppR1uOCD8RZ~hLdOiqY?YK z_ep+NwFNM6z3KA069jJ|d4rxFdK(q26u}*c`&~k$koS1On?p@cH zNw8IM7N0z1%qy~&#>^apIj*B%crPcXwsafF1`+xKFePm7rP|Mw{5t(XIrD|BE4L3o z_8ea_H~dcNL8FC6${sT@S6ACn9`A(AJ6}FW&a=!#Y!9_YsyP*ms>*J>_I>XlVlfs= zRut~!VKuxyulnvDiU#>5tk^X8+C@?en&CkB0b(IO>TCz)6jUQi=RWZ=&APp7i#X}_ z($6#EpbDtECNebBFpTKvVqs!0KOIe8&`hZvf?)p4O4Lin>xGRMP=ei-#`;}7PbZy9 z646dF@DFI`60z^dN2Y3wELAXdX)*Ozyfk#rXC>eU%SxPgjOcV;S7|$u3h6{We2g^x z>2`fBx_?vjLeyl%DO312?|wf4NAUXAGC$DVKbLVcOs{52F{|KT)P*;zc?%~y9gkhi zy~S%v5thP@*h>xbWQyL3-S(;#bIee+3}f=99%`*nc~SUn*rjRD*hTNF>;$OPc$E#I z2IoioxU|>*i7?Ktz}4_WNyavKEnR~=)0yuqb618+6G>Fe;jCBEAhxO>NA$Jf1Ik3$ zp}UbB;_t}cn>qW|J2Ys)GeTWpvOnYkFnyf!4p06(U}Rjy72)z8KDLo!KfzeH)yu!v zwgR_OAcZHR7Mqk#Si34lP)p;FP^zMPx(cg|r30w7*p@8Sg{dtvma>B~{qCp*wO@|utjcK9_x z)w#YIq=YWAneQH%hj9V17w3%!eSd zI%pXg1t9mZ>I;EaFqMy_#&E3;QnA6pE{uJLp+FtLlgAj+a89rGz?4MRC~sYHM24{Y zD#S-Bj}>_`(R`K^I>W_oHM$1*E7KDi3}nNqpnk zwW=?p-a^#k71}N2jiSSO)pZ1tBsY4id>2ji7(e}<_GC80j8^>1?eU4@lSRup9!Rw@ zbpD9M zdo1ZJL7XWA0rWEEqeNDE{H3mIMG|Dvq9C8}aRN)VOu6?vmGa+Rk+i?kc)h1+uTPWH z&Peb-4Zzbg9#aTHb@G);y&=6m_Q=2OKwx6Te@qIG$^M)J(6JGUd*2;&Jifz1f`6B1 z3$CY~7|uS8bAJ(7rT3UGlkCgCTs}^lMg`NjF%yu#tdkYIWozb4+ls0|{hAYI-=`-= zu4ff8zfXSLM?7FixC1#PW!?RYn&4iToBD5XZh5rOqghrRYH9iT6sr0N+`ljH)B$Af z{3SN!iowI7C&s6HvX?s5|B(G9y+@)0G*HA0DY3us zV@NLX%AdSXU@so%!b2lxJ0VRG9>n6gbf)Q48Pp>~=!DLn1J#GXb1{jxvotLwa&ooW zo^kCEq#_4Xnux#~^@9BH?@Ln?09S%M+Vqhi94ij~o3n5|?TC`NVnx#>WrO-NNTGmO zX4%%N{2{r2#ID=?V1X3d@8%&ASoiLYxRfFx_I;@q=@0xCiIGFZsn?*D&GGo=t!pJ8 zQ^t)wpoa@zDE+1bUq5=!fVh>9hfGxc-h{U;2K;RUZ2L;~=Q^P80@qgJNm68+(T7#y zjY0|BdmZ2Dtb9Ovakk0^rAbeeh$yuAVBQdK`#{D{fqAk>9VSS^GNzDXRc0LN=m5nF z*`=lZhw}UmG>dUUk#ykd>u)wFA$Y#6ijY><=D-KHlqaj>;%(UDS-h)5&?OwWm3w&_ zqZ6O7gU=#RU@^8`2{^ELV9EjD@&ol2f_iefJA~c|tM}ZDny?i-S8u#oQCUI1#JB-nd z>=BZiMRu`A8zIK=2Xt&txZHguV0gugF0c!03VGoXeh5M zL6*xo?(uL}=tcr7)1%KxHr|Ztwa9MPTW``KNX~~GfDC6V&q_zQ42507j*3YHeoYwC745l7DkdJ)Yx8uOs%K&OC7t3Tc8>> z0_{`{KWVA(3cGTOQno*eQ`u~>49KZ1-ZP)m*#R?@CSTcocK%oVHi%^%i0nn?*#sni zQq>}^PBv52_6)-uTA?vT%TN$T$3nAI-mj;awHcg%1rf2rC`PlISYTGKx%}%6$J;15 zMZ!PSARqZT@ks36Pe)TeT45mVtwPA@#=Vy~;W2P!gJ7l%d+p1PZ4T#!5nUomkThd;$|O4C))zgsWs$5 zLeW+Q0hH2CC3?=rZW}4|zEi@a62}t;V)s!V+b?2D!v~huwxU#R-O<1W_~X{E65xDR zFzb#VnxqS}zS{}YSBeJ&)j<1nEL)W;<{nlZ)ez-Cdb+VJBgn2qfn9*Gm3}7^s$ClL zRvPVQ&#*SZE@<#otCubF4@K-GyoAhIZu;(py3G7+ivH3}RtYko9o{;Q7y$ZNz+g{# zG{g;}LY)a{^%cPxe#UAn@$f4CplnWWX z$Bw)#R@|0Qy#aQO!G=*Sys4FjyE|!>hf?ttsA8T>ko}B{(EAMs1($HUy4K%AW0W7< z?VB0ble&HWPFl#SgFFZ29jU5X*MqqdDvL#YPA|PbeeX1hMjheDyx}9Abb?QgDn4Hv zsz%tMCWIO2Y-z;x(<-i@K!72l4ka)Z4_B}78-G#rVZug8`*_Yfk?>r3pEfQa|R%GfMX z9CZc{y-jx@YcZc;yyqIhos;MZO&$rmDiuF-Xj>>S=?DMjQ}|wjfw>aOg>~9FarhmI z;g13d=URiTnkrI`Jx4xEj&6r~zT5nBdqOL5N5-ARVXw4R+%2rwb5wya31ED7UiQUf zFJSfOC~DXX_tlcv13%J)6poW3-_94wkrx%err}hajIttt54ux^p|A={27a?sGFI|Q z%CI-Ycp~TuZtRBiN%wxvzZbfMmx2#SM+S#?Yq8odLIfU1J25L}^UUJ`&q{;&?Hl_E zrwqJ-g)|<%YHzRjuxVQLP@j!5)yDnb=;tror0=7h`GNcqjO`LDjydg=hYG)m-N;V# zMwg(26@0$-oe1Hu2)(BVG5+ni6Fhy}qT}*!x#R*S@j}zfjI? zWV8``kQc5t9$~B6@@w7qI4^#!GtB;=4BmQ+zFrTqz%3(B&$2y73US=#p{$sgqnl!2 z1ru^nnmLpxTg#Vgcq=DI^x+?*i0^k`v2qH>{-#w=wc3MNi-$F?KhaXtFH}NH z(TFYc3-glTM|B5+v4>2_LSkYWh@k!^PwRXeVrCn|1&h$k0A3h4pUkMj80|JB=dmS= zz=XPQHcXg?^r{MP439>&vNSEMI4KFOb~y4ODEiRFRiFJOx^~>-xCm{YtVDT`Koo_h zm-bheI28+{Q?0P*)R@<+7Pt10w_M-8-)4P4)@t3|&34IbD->>l5t~EOR{%E`(03Ve zyZvg8qXx?F2Tk4#Z7Dp%D%ucGcyCc-){388WG*PMKKLb?hG1ij9;10UmW$5xizA6xMz5>@XHJXMXEqQ?!r3Z z=_26reFAnk8^viRkv%8N`7^Bs`Bo9G@I-F`Cy6rm_`hc;g7he5yHjg>wjcBsh{-wC z$-S>zFoDxI3w607Q`)nXro2a}SPcxc7dYLPAW0L&8wyVxssqJ*bdBuzZAcUcClhe- z3bQ8usN;sGX!q!h3omhTF4QZ6K2)ZvyXG0&E4P_4>2cqL*4h(%)F?z@_e}5}eX#s|oK$)y zAAX5qGezRiaGa$j<^XH&NjMNE<_?Gi!0aI*AGOkI9gzIXpkch<#V`Fz){K;Qqz}hG z>6z92!fnu?b*07sIW<+8!hKoWcCb6LMeo?FiLcCwjE&yTpg`JQXx; zi6F*P5F~DtBm+Jm!KVM63G$9t2_f+o`4GILz5)Rtmg5V#z^Dt=4j7UuRoo=<xo0#o?$-;~ zwXEJuYqC4vwQVHLw31K>okzr8RH-D@N@r7h>;4QMjyI?NJBqEUz-SR!qxGXP0n8@l zPQ>UjZqoO9Y>xsvxQi3ujAR|a_7z6CE)mDhY1qDxTab0e0~gq31z4+${yn$O+WAjc z;gsD|IEH~*lwouRlcM;FZ^!!WLSh9=?kW(?xdHxXkc@1U&NK9FVC^DE61NLFpJ~Bw zX;xDGkPk_em`KElsSa7u@^D$b~%z~4UBoBo1{c0=_hZsVz zDiL9`HT0Qagp0=US5NriFTp zP)1c024?y+U&}|)K=N*i46)BLW=oYFc+N011Wbz97Dn|a&`b?bXX`D9MgoInwIt}X`X9eeHsi<{nN~V9)%Ps zI2bajZH6~4)cObrQ1_5T`l7|*<5_~kPNIvmG^J`6`A9_c;+yx)q3cub@vq|r%D1)N zES~|HLOfAQ>|8l_GF1<0|C^BqZvCbu$WRNy2f2kU2`0uU_YUF+%W92=+Mv)kH;K`n zLwEPoC5vOD@S(!{y(9|!E_hwHYdO;T7XkF{qnKzg);&u5$j&IqiFoo+wo5R+5rby@ zY>VsYd=Ew6ikSYQLa4*~bXT@dHSqnck5;10FN6=`|C+O;tnaDY>&fF>CP@nt5QCD~o%>G9ADf;|5AN#Uj~Z?r_0>K`>1j#-k?3+z#8scv zP2Q6BiXxAuCaHQg4@SwI2-QV*#LN;Q zsO3*a1uajtXDCq;RI{5|h6Z35d-ug6|CBQ%eRf?o?{+pvZvUTk0;w_Ru3)j99W1CJ zC^q@Ivbk6H6EfDPYD1*O|EuN!-heYtWWv)&D7~k+`74ETR%u~BfN&9AG-6-;-QMrc z@qPO1LB6hVS4RDT4y$UG?-juM@e90F0S`U`(udhy9e$zvRtEV36%Rd9&#VI(NyQHY zbB(aD+o3=Rj|Op(d4;3TII7dx)z^g*irTcWj2VnB?D-qIBStp^@99Ul=(%Pbdw+Jc zUJ<7##Z73+?X0-itAxf7M*&8cE8Az&RotmU&29PoZh!JKR5x`qAEhV*j2IXu z6I27$duX@Vzc|NE@8|CvL5AGddCA!L`}0Qt;?^B69X8l-FV6FGe!mTCq-gfQ96e;g z9Z6slQF^b(2A9y;Ece#J$9!;Qgi1zX?V=)}d(J4>JWT92On7h$|_ZRdBz+(x~(vVXp~ zb?O;uI1t$)FVNfW+tURV6E2O-N5VytUQFyfcP8k`i`TlyJ$|En{Sf2yPm$?LCcdgvS6fTsI7rXW*&53)Ebrl#rjkr+k1VRVOW~I&`y2BuJFDK;>a6JLGYC!Dr5Xnp4i^zcvR_8ex9%9Np{vMDKJ&kaWN!M*=bpPLU)*OvX2PT+y3 zd31W20P+naUjdt$MbrcAk&7v(SEPjR;1<{RHWJf~9zYZ!wDc`rMT?;~eImK^+) zCMFB&(4;}fJDg?-@>dk|QB84ojQD{*@Qo&;lL7cg9H`8N`sO&_58(*e zAk6mS2JP!Y;VfmYSQowk&mkR-$~SOcheGO6ayts5Q%+oy)kUAg!Y|M*#u{`*7xt^h z0z%&~s?&E^jZNv)dnY2pwamU@>r@bZPQQY*q9F+h^_6CeB-n%DEX|MZ$b3#81YbID z!)ef`Kk>uK9R)4xeM4bxL*1z$Qw)g1{5ydE!QbvDGfF{bqoFXVO~}IRjq0KBG<@c- zd!Hq-yKt}!DHJfNV)Qht#Pike?GZcuw}BFLOTc0Xuf4&O3;m?E-=)j&^8y`(n{6w! zA+ud(Wba72^KC^%@QPz3pJ{>$<(l&W`v$+|LQ`u$Kl@XTgVI;HuFHw7@eRkXzNT#y z+b@jWm3pUN=?oE0@2`p*|~88sg0Xa%!AF0nTU0saf))`Ju7AJ|E`*$l!GF* zN>3l-G58m%DVx=3R52@qwG?65J=5oSPhgpMju_56EGCCWi8C%W)TK=PUY}M|;G+|9 z>Ds@|Pwt%+{Y` z4F-yXNnsbVgV2O+pqWs4J#g<~qm){ggCO`+b1om_Q1Z`W-&`%Su6RQOgX?QCL(HZe zTu|>8}rc?3yVzgx&@$`;zEj|zr5)-lkwC?u0^bTd`CK0?{oD5;$xqR+-qMKTy}m2)GNN0LevXhyf4J(2=R#S z-|+3W@Kd4_HDGi^)OeQ+edLz^j@3wA@{<`J#q}LumjWJc+I%AePC_4W6&xdEzP%ww zAM9-{-2Bx|n!mdWicFJ#{5MSrE6fh%t5O9UyoEE3w^Uvoy>j?LYa{oD>lNi6%5Ch| zJ+Ej|snqiWGIXcp71ce=!W@0%VF>u+H+P$TXsNLU6PLYalTBqg~cRr7I(ax+k z$!^p;u6;%ePsoZTNMWT=>VZZCUnyg0Mxsj3U!cA+ddKqaz^MB^5Z`HX0RbvQ-vh#0M6{(u@mt71Aj}yg?;$W0mE}TN@M-p>%;!Gc7kw<*97zn%h)dt+nKm)t7;Bgp0BV2m&aMF|33?G zzccSX%kXR&P}&vHDF`02RX^;$eioL*=*ip@$M^t=kYVC0c|MY4q62=qCecj0^Om># zB(j@o_s=Y$(F_I?OqlZBb4~%s4J@7S}G4Rj5vi%$vMjUpK!OdulL@nrM3fIBaB7J zj)f<#iy?Qp*uJrgD*0_%{)}&x79?9Ecc?y?KUtr?^Cegx-MR7{prF$CNfWA7K_g=~ zV8!Iudh@&SojxS)wIa%ojVV#yb9*UmCzn@XtS#8sy1PQ>u(`R*Tq0Uu@H@`^Z^|OE zKN^-oewi;&zLr7^Vp22@e?2%J^Q7s7t(7sM9_4ZrC1k}FyZyvqv~t`NEj}=+9L2VK zck2NEsI|BRJ?y2RK3$}Ycq}kwyX)@G+i$io3|9av6;I!2F?`BS5#7dR+5z)K9<1$zE~se^B>*wxG7Vo!{wrTxJhOKhIMYe)#v%uMd_0_Parrw__F<6 z4pnwf03lqbZfL!na;65P>qlgQ9Rrv|&}GA|TzznYkbV-3Y#hQ@F~Bv>6a+Gul33>K zQK)J4*NIFt9RquG`GlJ8v)iuDD>w<|UYVp>KvHt7l=;&e^C!YD-xxtUj%L*SJ^5`* zYji2F&#QNQTv7S?=8DuLMSqHTVWp7!Zlu~9Xm^c>#(iSqjPa`9XQ8+` zX(LdI4Di`26QNd#82iyCb*Bmf2*H~i}-J;08N$8Z5EA$#f z!)DY?E3GXmPP_Ru9ZQjay<@NM={zKmAyI`5HyJ-}fEwXM@&@nydP_(EZGBmUy5Gop z$M#1aD`WTae71L@5(wiSRJ%KQr|3tN+^vL2yFmC{`6wsGCu6rw-!H>~LGdb0dK~>P zFodo>NzB)$9Tg1Z79Pf=%6mV5Cqi+R%5b5fbCohw?3Nynx^cYN8(r&tJD}&bzWl2T z-7PjwDStm8GE-hE!JZ>HvetaVe;TGD<3vTiyq7=`JIFiT3QN*EUxIs#a5)%8=}5y- z!okXV0zJfId2&Hc#JcOG>U~IV;)E4EWw=F>#Qr~;&cqwa|NZ~3dlqBg_jT+lYqC@_ zL)IkP6=e$VB%ufqbE7ONYegtR_ADh!WJZW66r!xzx0vk9%-rAkob&tr19Q%tb1$#^ zx}ML+1Dp@#C=Z|JI0=ZXy{(hi44;kndLd_+#df~)^zn6s;~IG!(2mOPf|`bga10FK z>k^FYE#%MH_^q(%e4|4prumll$xa>Dj{^AOGt7!s^%CQBzz)({8VvMe>`%WxE)Yl9-8gKR6MUH?iRJ00rQ%9lqi z_>Kbj^#jE1+eMEr70?tp`H7>7%TSyWT0MaSUL&8;IJvU?K2J!P-e*DF8Yu^iHg%R$3j30c!<=P%g#5p6r2MJlNet@0{0Dt@^y8xc z;9t?vq1DityKq|D=#8L9%@rQ36p4Edp<6tcTfUSyW-VRc$q9G9EZ?%R+A+{Tnq+|# z9N-BTS4}KktD)<6Am+69!(U%K%43}ge6}eMYtZiJ{6Dp1RuhwBABB%3VITj8_eez4 z8OTY^o+64hDzH8Z_;n*^NGE#_KtCz`jPTbN7x)yzkdE#_R=8-vSK_@bdR-8F6{1T5 zMm1&t3K^knCc*(S3f5L-Mvqw7zk z&U;GU-T;v|nbX)4fcyB<8r?H%1QcJ21s*twDu}%qeIjh%$4n3yx{gl0%g(AEiO;L5 zjWvtzY3HU2AzzJJUKX?AtgPe%O^;@s{-gwR;pm;eI`r6q=0l0Rd!$6}+0SO;8w}1Y z+FP_LBRxfxU}3YQ#eOOYOKAgtZD+1QO`%Q?5OD=09wv)Z|B% z!nGWX_H!wf*g&U7#Um5T6&vKeLhi=y>_8prH-xfYk{^^J4P(Ji;-|?p)IG?CD7d+Q zf%5|^#(lqCX_s-!^A7VOms($lIA_Bv9!%8!DXY&)@UJVUvTpsH9UXs|&DV$ap{){* zvH1i$oMt#5-Og-u!fJq<;G8GMlLK~OiL{;P?`(B%%}H$5=kZY;uAwVMU=1Bo69?^I zXEWkny7a3m{4M0V!}r{xf(R=1)8#n1;2ro!1bD3gJ^8Vxq)mvw7v2{R{AE$F=-7xt z)=u4fAY#ir!)QV?9s8SPV%tR`OO9NFcw0bfAdrV@#2nAYy+Y-o((dSE0}L!Q8gmO_ z;Rb)%EB8>z{2b*S6lbVLmlnp&;x-fPy3x@Mif2oG>UVHVvVhU(cdIxe+~0p!fQ~pB z*!RB=4k(^mJR8qk$4J4b>F$VhGmXXJ)K#?Th$M|%a%SE_{(%O4GWhW7roky>8DikJ zN1M;y_JF`Yij%D7x1!M$J1G+=_UJ^sRxdWF*IZ3GB^KlEzgTHY#Rhre`!)h@&l@THKpAZV= zBxo&yX>I#fcy?EJQDnad#1B~wl!O}{?<-$AqaOswHNhNAicRYG7W{ z6N=r2R8VnGh3s8#A?`zeK*_%C92Ky4Pu5w;sG)e5j~Cq-q(*}t*JkqRs7>q-WRmyf zI4g11s^@ogIbu;y!uHJ`g^alP`$1cNt|sY_uLiZ2r-S}+$IM8qXk1H_NC+^V=D-1d zAZyZ-^tNi^kJt~0s7TEt9(8Lqx1(Mu@vNJ8`FiEPSyteq>98fc$dS zHPZCUEug0yp*B5b8I>@=j@1Bthb3}T@4VR~SoK%qI!gNNLh^hLGrvPErZDW_2;=%C zoG6j!6x__3yKSD%^jGJ!V@9Olq?D!ag$dGdRwyZ;b&_FskqgA}X1rtYH6ju?%nh1H zR>{8d{;7QU86%T^TU}~^2mV{2YfX3iGs>mwS3oa2+Of32JcT(ze{q%4ZgV~8a(rtV zBGybM9-6-%Ky*QFL<~3N@(C+lAGGlT<2;{8)E^ z2>8)N49QK%r-qK~7~RRIzkM#`q~C^|>@hZkp%3B#yE{jIcO-ES1!pqFgaJRII07?g z*0aYj(w4Rw;3U)`&H)5^K+V#VuG9P34g`Z~%V$q`)HQ$*^pm};1Y%1a^5e4)nJ;m^ z^Nf69SGRR)3H>q~8rb^Kkj)W%fF3@GxU;HvUr|I|yd8ALuE`sbVXn^ zw?Ao~@-SC#ILX~%9y#R-*{tB4yzT}LXc~O73GJDqJS^-r*>07e&izHMM_S|0tmDGh zB3wK^NdW6;piiTyl{Hoj+j<;s^KiKXtAIRP09PSmn)I#ElUDX6#Tr;4J4UTrY(O=d%7r@s=p*cNuXhOME6Aju!}4a-6O)hvWT&Q^YaJyN zI3?yGMEjfMEWlf{SDzSn0QeMPp8mP)0wy_95kF?(4()S;@64HRyA@Ns?mzg8eQoKn zrk>EQVn3AY@U}17`D-D)?jON&khJ~q1}d6u>IgMTdrc5!mUPqloVR`ax(DP8u+m(6_Xs{7-1Dfh3spel~gJ=?mcY!lj@=NVG zFggBPkkximRbUWqadnq5=$V@Y%^-3GVb(Z-H?`|Oen9)E$B5V}%MhRw2D^B}{~64a zcdgPh^Y#=#U%18^wLs~4lK1OUtYJz15VSQqzCdnZ9vqbOO5&iIOq_o1UAPpw9C8Wc z0`7#~S5bFU0g~uxB3ez*1c?={Lb_G)8VR2nxDE_a3(k0se~=Ar9nae4$cYe(k)Z40 z@K@RhHqAwx>F4B*wUvn2L5BaLv}bk2ebR8|o_XrJc0U9#9QZ-zo0d(>BII*0y*_#s z393DMRMLo2TGa~xPjs2^_Q5!I`86${IJdk_Y3i#I#lgbdP(_ItbE@I*zt{GdXAcvE z=B>YEidts=$Kk*j*is!J1i9C3qw|nH?h4inL9x9ihnM)czGv>)Uea9DB(gt1<9LjV zjd;NpF+#+;qEn9#pQhgQ`8{W)=DCVR1UbLCwz_6b)A6+oh{tKt%#$7mN&phcVQ9bz#oCEb0e|?;>;JgeV>%>|JBJi4a?C z-~!VrMR&QqH^dz760#oNAP7M0r}v2Cs53j0Q+LAJrZa^edeUi#A2KNQj*#yh^p#5+ z{!$sUHw&cEonIglv8VfadY+_@X*#dFh2RLU-CoQ!5Ok{}b>I(;?)II$%WN2q!mShU z!vENb#It&JPZaXUIyg-UJL@iErM}EJ0QjUr@X0 z&wEltrD4|R=lO@<|7-?593mSq#e^t-$kh=02k>nNi+$f?@_Xlq-|EEGr;E|xf zYu3Uz@&Tv!KXLtKU38t@pS*Xb<>4_+s~4XG#{*baIVM%V^ZP<|#_9B`6VpbkDIvVn zm`L2%W$GkKi5qRt;G>Z>msa|QvN+K&9$e?IQMUReM*LX%9GU-EeyS_R`lv2^z6Wqw ztj?RJ-+8)T#LLgyxn3}j_2l-zfGq6Te|VwLw8M6avu3JPyvATD& zn+rQ>2X1r6$16^Vn8}UDu@gD9<40~KaENn4o%K+{73N^I2|dh^IC_S=a+EzhX50DP zV;*;tBj5{T4{uvY(jIktlcxb)R0E^?m|2;yJu>2k!u|UOa#-qi*2?1In2CGG;9Dvg zrJXdd^@YIpGiW!mEMyjlo>b~4H&2mE{=z)d8|7po-M}A=)8l`UB|Ov%e57Wb^THe* zCOb4M8cuI&k8*774xy;$M~*}SxCN|qzK+cSmW5Z)3ERtRKFqVr!v9X|(2I-`d(V!7 zXenTOu^SRD#R3)5r*7B)$=m~Jr!s_c!IR@S()X?GOIk0lanvvc|Ba4T>8h(w*>$ZF zdQmyk}4Qf!d`4i(o{dE3mLc=(u5wk z3#e1oD?Nxo!M#4ge576MbHT$y?9`L&)MlimEEak#Kof>fXm*dyzQwCE(FjkDy-2}L zt83d`8jtK{$HdRSk^x~*InL)2e-3!7R*;H|J_eLg`lk;LCDT9R#oqCKK!-@Ic5x#~ z^xeeyGA-tA@y%am7ni#}O2DO{ULIb?dET3$UIi1n?bi!xRxA5PhoAZVZc-+hTn`P< zlZyGmOmHxNsG@mairPs`RKXZ+IFVQAB62-Lt)PRh``da;YnPeGMm2+WRj5|GSYS1o zgQ4r&R;XkWFG7KmV_&1{zG5J&Gr&Jb;TDUCS3i{fAc6$S+2KXX`XWtWcn~?i5&0if zZo)_HMg=PGk_C~nSI3s$%z9H;mPJyykx37P7wNtOREZiyO1tQ(6a1*aax?0NwlpuH zj!W{QFyQ^l=8&UXz7JU}USA9Xn>|AUHp#ij>@7SyaSzNIC{3ZVxT$rhra@k-S0PO2 zmkv16b+g3SpeFrB1KIaiuiXg$XeL;*>UQ~Y1^xN=Rn$-10Ji%@iEwWp{|VJ8@}G#0 zH38lQCkvLha-9zN#2M(7fmDGk4Ul~Q>Xd{8ca{xZPAZ&=|H{da6FjCA^KRx(Upk9l zWwJX@97WwbrB9u{)a|TX%5JW$2)!a#8h&1JZd=QgXL$dP<^U<=2_#~c@U_8-S$N_l z?z5rO+17@)JBvpo8k!!`)7Xe6VD~}^JF!=8H)2^$n)rZAeb-(b{((9p@B2zJcyvPf zCkwE|&}P=H*onh8pwfK;9OJkNoSHNUG!|f?$x%KUa1_K;ZXpyfKre8g_)dc5Z6NbGka4);}ws?C#9}W%t1+tlm5g1nCAgp{y8mx z(+V57=nm@60Nu8&_tRE?kzP*zvWG?@b3ZaMXX{;_s^Iko$$Ze*;eapxHx2#3o?l2V zv@17*)Y>C+HP0a=KG{{sNw$jv<3NxruOj)OcaTer2NQ%qgnWN!qa!WsTjSHQmpJ^a zMWbZaCA7IW=Q<%_`_5r_Rbd&F(WGR!q3qUJ^;U{lFOJgy;bu_ENH-*+l3v zuM#`v8Y%ZtZKi-WG#z7(@Oh09W_h#f=cI32N<(y=qv))bLkBI8l*86uFZSpt!yj-# z91Ay$0&Pw!Qx@yxKcCRp|3jPIuzy725+-Ih>YB%C_uTVP*yRS_Yxpvg73Zm-J9@mZ z20V0M0RIurv~aPRzn!2b%ai-1@#w7~cucFQMBHa9KlPysgU-q^Rfd_AZ& z#MMCRsbBIOCg@}%&ADw+Yn_lI3QpcgyX-cZ^!ed;WM@-yfp7(}!G4R`l`GyqGKm#; z9vd)2wciiT<%s@tV2t_VU859@EOv^fPK9D2TstV4jVNO{Y_}d;cC_fL?S{H8a7Nn_ zRn%AuM+|S<@@>9yL0LE)^g=`~sx5I>l$)lUm6$@8dc43_34#!&hXX5@&7H-}oVegG zFGD;(YoO0e{~R?~a93yBp#4xVIgR;hYvPNuhIPN42KWn6)n|WFk?KfJH3m^*E$nbQ zJ^wlRBc`W!sI}NhAuOM{*E;!>X574W`+lH_?05U6gcm?5#f+HrpIZd~{Ra_$vkuc4 zLR!MCS$Gnc4V6~!*UCNX^0#E@6xzppg;0R}^{pNDG=8Mvhe z)B3P3nk+xItP}>UfJO(kaXWFR?;N3B)yRST`r|jYV=BBecj8WT8UO|J}yYhLJ zocPX%I%NZMzefizEc(Y6>U?ySn|| z0r$Z;6N1xH;NR@s*0wnwo71^o3ZppzW>g$9t=&i*qPagR5$nbK;(rsa) zXv|PzAuK_}KZ-nFiv{`kW_A|o?=m}Vc{-IPZ~3KZ8*6>n@I73@7TxoAUpvK%Ne1i7 zt6-_DDmHp>A=bBOTkIK*Uv!rW0!fU6vsZpHqN;x{u}XYpV)65HgzO{4vu^KyDet6$ zh6Z774Z&Np8@F6@$R7ftyE1ljdtUStdZeES{c@fJN-s{aGA;Ck?%#*N;9;ZL{7S6P z@YY3>Fo_hVL@FXE2sE{P|AJ{XbSjt0*R#m>PZ7cFgf8uhM|Xgc4>R@Os9hD<;SWVg z5)J&LxB!3Lhi%HGYLcQkopO6rV4HGyr-x2a-6U0DEtG*S@Db3yQFgxEJ+-7!h+iJ2 zwD8Ve(Rxbs055MEGYSN=FeP07FckHIf@`FgriJ)rZ^!bVWd6hZp~a;vPhol1t_rvMx&z}Xj;Eqy zg-7}8DLYe4SgP~JLLFCeeDNE`8RXir-KJm8|H$VVTx`gds~+@P?VD3-xE?f%X zc}J_wohm;U#}B`}Q2qqDC5&w`FwVx^3*jPtWlklU(5JttKpx2g5?k`H3O5JU3gQ^C zS0@^R=R`1Cq^y=oc{OPFI@{y{DA?)W=K)E+y%4@bn3}n?mV#qk`C{wvC36tdHtj5i z$jLF|Kkk%8`pQEYHdB-_=Oag{Tzt6Mqg!s_qRoqZ9>t*+XGix!vJqC4@8kMHjuj8w zp;|7r10)QY%NO-$=k}m2J8uCZV@&E?BlHmwsu8K*7FG$H&z=aS;@pL`@_iWb z^#tsWa_(&GC_7z8>IZUUkEG z&ykC4y*qc!rr`*9)Goq$)K7_@iAx0BXFhO!-lq zk|H12nC|}hTFmU2LY|*T+IM#!pjpUA_0PX3gA1lyM9XYG!3Nv=dq_bhTW9)ZJ}M0{ zy=_tI24I8y`aVHt&E*Jq8+m&Wm7B-yuvac?o~KuYRQ4Yne2u?;vzL;<)t$Ia(Ut%F z_o8Fc{62H@6c|{biL%sx*Ul|k z>`{mE<_3;}vRusZ;!K-Komiv<+wBPg`}q0QWXF{6#l@-qVY)qMy_;{(nS(T0?cBj4 zozyWp!Lt*o9Le9~Y@Zi%AN-0tX24V~M{E?)-OC{y*@V(hnXi!yh5e^yt2aw|ws(gVQ_|jTX%h;bzZ8pagJ>b6s z7=~D$vEZ)on`XcfV{bo338VW41(PODv@+m#v5jo5^6%v|pWrPtfCYG6%L!E<&7`%+Iv;eQ=&!@0YCil~ zJ&=b&`D4U-_HwUp1KZZnPg4ukmvTwq-vBWu)jFOzU#g>= zAYiBaO5DeJ6t#GG`rcj*>b(>3-|x6-N&|o96-}1$?vhs4(P#cBubmmb8Z=1)+=Vo1 zaKw+JtPyOIAbvtL>%&1=oS;s`6LK^?IOytXreZ%r?z|iOn-)()e?qcB8(WrxMVsgNUqnE)W@h%uT(h$(bV`WC)2Mw~2RO9}-A;YK zh_nRFpHpEvsve(!{ zwVZm47@`y2BW+;kZRWy`+#xXEc?6W-ktRZzf{t<#SHUW%#CH5wuFXt->AimsbOoCg z<5ZVfViOJGQJygoh~mcMW8YeXe#VF>XS~@;W=7rM{Q0zqtLD`_LiGNqeA!3k^>0E) z3|pH%l`}x-$KyfrR?WWh!0=+w(d>KYohCDOcvfo)8PDLbPguzD0@EC<{~tJrP%LnM zQmhYKCr3dGXy0_%4J0t{nB_UBWKI>$3afkOV#NiFsr*z=1IY*bsnTymTQ$E)mz`bi zx>9%VRlf)ExuB$kf1VusInz>e_Ip^V>2RDKeOE&UB!Kp!;CZ%WBj*nT9NoA_TuMi7 zpmNc(C%LE;=eU(o7V)caa$&@YZa6F!$MffrqXJq}WV?Es{G|psJi2O`=)ED+idyW= z0WSs*MrQjri(zNah^FXYlfk#fGk1?pp*|Vh3gDn@gEj2TzwG zFBT-SH(#;maT>kV%V{{S#?u_Y_{VMoe|?I4-G=T0yo)UU4LWTLNk|U!dh;h^0R4L> z#kh*o4N*pc7hlYpOAoB*s>1TDCqX;^Kdly`)LeKoBVtwU8M$EFfu7Qh8Q2RO+!;#s z-ZVBGIJEXUI}Ej}sOUaj8D?0!?#C#|&uE5{{4DcT4qxdD>nPIBV2jwylxsxO{xwwK zNN#$TSV3N&E%D7=8X+JwWk8>@$TFBtSK9<078BW`kVmLeOCpZ2^3Ci!T;;y21b*Oq z4pP{nyq>4z3AXr7^7zoQ=9F27+L7qD1W%38qM9m8M?e&OesZaGF42v?}$`% z19VTLc43>`my2iNO9F%f4Pe7?@61=G232BB#sUvKlLf0F0YmKn_bdc{fMKs5Et1kiME)^2zwrw6w-`dFz4i!ct)0 z^cmW^W(jl2A{YZf<1kCYIyX&rD+g-ZSin!9Fk~-;IkySpXAWt7CFo=LgW;*jzhQrF z^w{298O7m2=HRifrfz@aVwq9?Kh4m@ucsPyV&)KEo{JN0B4wyEGAy&8Z6Ufq({3-? z$(@&KiiiKaIRu^S=av9t;$-QBYV}@Zs!6A3e5KOR9Pt|`v-Xw0#2*>VmReF?Nu&V> zG?5oJ1!_d7`{nBSvSlq(r||Crap@cPN%b?lL{I45T;P)vVoF#Har|(|Q6Gdy5E2)U znYTM!ghu@ZFe?-Xg&i{D@1v~ohsh??lFue@R4VzxPyj3R<~o8QJ`6p_hixyavp(y% zus$l2YbbL}Fl#NxY2{aLQHJ5_=qWL99H;(t`@Dx?WIvi6ga7$!m&ZJ?lbYuebN&$L z1@xJeCt+dtEG9FiLcIWdZ9eo4JNys|XCqaao_{NQEfF)DLrbb;2Gp6L6U2Qvq+t4* z7obW*zYq4QaC%Olq(?sa@g3Uep9zuKdb7fM>V}C5?_|as(4IvY45RBGKZ`>}w-CO) zXU-fviTJGZeL^&P^?i`%*Y~U?QSN+0w|PScd<+i!ss2WG>Oo(|=E2`eK?oQx-mGTP zC9RYmYZ7})>i??|;#U;Qn6Hs@zlk%2!d_&fJYLCU?;vYibLab!V<>S~E0oU!S!-*e zA2Q)eaNuh&#gJrLOLw}O*@nJ{6!ZGoC_D4RC$iX;HtkO%>7Ud@p6a{^3LF&o?qT6e z4s5bqWJM<)__jh+hqY4~u*xy4__483v&SIvc)7miSm7&F(0_S}nRkKRUX+6mTlc3j zz1xjyh`{rim=Z%JQkRt1b`7g}wu$cWj z{x64o*VYdP3E??p^{HXc?wG#21yA@TiOrd5HV!~kQ5+tA^acDx4=7MigFtM8tsC=v zSe6#kVOtqXa(~s!3*n~`vmI~-9sLEX2$W4g^4n6}ST%3nwUoX~^7GPFj7#%w?=JjU zi?4q0J0SVIYMNgS8$8m#=F=2QeZvnh{B2LMVA=iPN2PlYt%GEt1kPnz``PLnGkdmM z|1}*pgV!C#c3X9IihiKFRmNxnnkl`Zp}rE7<+K^bZfn+3$MZ|Z!Gs#)PqSS&H-Gda z7mo3-v+KgQ1yNsr+Be}lroE1jA<^sJPP7Z>t_i|=Q?qw@vIZ8FE=OLAyI1iLO5Z}x z)~P0Z0ged~HKR1KCrjhm&K;1-G1F7;G%iXBb*rMUi^G4n3x5a+`BXl*KrY4;?;Pe5 z46E6RkYVN72_&YR6w1Er6C0aasP7R{QPwT&i%%1OBPi~~Uy4QyGT1^qdypAl>461y zD`2!0aC5xh9DWbQc((|RK5DA#c0^iA%qxbGi>a_w23=%3pO@$l9lQ`dd4_g+SbIej zuLte7MqT^Lrv7U)VA+?_w7t#P{M%Eq_)H}qT*6iuVG)%yR@u|TMzn%{C^$7Y zxzo08ojvgK{a|NBr%b|AJkKQ3eR-E*)Ot9ykp6H3S8@NG`BEX|Al&Co{&#GZ(hqWV zu}C6_wjk;c?0*~5%1sq%o%Gq`CP}<1P81AoC)|Y|?u>eF?S{!~Kcg9{GfXDE-LZk+ zmywUWL|G?oE`D3MUW!DqK%LP<>mo#C^z2{c_mP?=TS5TH5n-0t^ATM^yM4AOD03M{ zQO^QcZiVwW?#+Ioo0G!4_{gj^&_5)gra$%pLK@ezUOQ_KBi`c7xf)7*wp+%UI6Z&` zXL!uv;0)SiiBc>lm2U%5{5%k~hrL{3{^ie5G}yQ*p|9AU2!P~)%fpM$eU2X?cE%_O z3$?wMdzPYfb-MPyWheOhu^)-#(zxrs(@fi5E*~$!1ti&D&nV`GZD7ZnvXxhmq@PG3 z{KV#XfVJ@3Bo^Wq=^NaB=^JFfS&CQqaKZfd^Scdfzra6K`v9fx_s$Q7Y6yC0x| z;(`G}zyYyb|D_X-#OE(byJ5Ah!6{c02@I;+g5-mezNJicgZ*e zxf_%v&T?^LOyO8#W;c9ZrT9=0hikLkgD{!v7@EgP;_;_?{U0Qd1y71_-N!6|6Af|4CA+Bn%1OX682P#;yY zkrac006I&Lh+&q=@8ItKeKA+cj)NSjeBskdDA3kkqx3*=1FFJCL-6$b_jr9*4m;b^jD34|L@p;Tc^^_? z2^2ZCBLg@wdEej?EG(Y)f!Ta*K02c>ZWtBvhpm#j&|pw?uc*xavL+)-xjUsZlSa?9 zcTQj%`g=+3Z&*E%j?j>{LZlBpxPq`Pvl`bq%MA4#7{f1Cn_LY zRLy;Dyd*;c+EU(26oQ(c4kMqVqSt*Wf=$pZ_F^pa>x8Uv&t7>FXA}lbx&w4 zH_YIfP;@#zZO85kpr88SZHIUuzI)DSV5sGgKf_VUUNst}){CefW5pADlR6Jx+P6c< zSAlha1*$-*dzKO>j)b78o9SFrP)G24sw@5mKuM5p{Fwc)qP629Aa`l4K0-nQ{^j@+ z;X9g?4T}3ZcxHtto;>wOhGreN&wlq`&rhQo*_&o;R6P6| ziV+&y>}$2?865tr>4n|u!@g!5%f5Vlv)E})XD?$&vqaobd&GS+1;E~-X>3_-zMqkg zD|Wp=EKIajz)v18W&tKyW1x1(K7}lHck@8&wS9j=9Y=Og567TromFuU zqQ=6);8&et{9tV({FY$CW^LIf9t3*vWc?Cz0)KBAbX>fnorg~1yenOBqYKo#yf?>z zeZVs3f-pokIlFnYQRn*}j2aMS3f_Uoutqo75E~!VF*HmN3Jo0f{4CVgax6^Zg418! z_}yz^D+oqB!R$Ed9kC_@S^=9{T92mdpSqE-kMvRZ8>zMoa**V+Y_dX&Q{wq`ga@hju~Ad{ETL-&T2iTX2c@ghe10tLNT2l zy6F{BKB{z&Oks#^fOb=GA9at>e*iB_{P1e4EZrWpaRyBdRqg|w?+87>Vw;dVHv(zttrv1OrqRr|$uxQ6*N6Dknv}Q@7A?-M-JQ4%Xi7Ue5 z?`0nsJT%#`)7|aa;+rD3Up2glIY6tby)2$SgIfsuZ^`~Uk|zaG9`qsf2)|KZOKk2L z^xX{l%spg>aCcw0l1S}hxN@u1q0?EZvM&VTmz?@2&5DB;_j41gn@^ue|4oq8;US{9 z1X1D`;t9CJ&e`S9=-q4Nfu}y`%!WNd<@@y^IphhZhVeUs#iKv&JM(6}fl@_LF$2v{ z2BqPSA%1x1H8_5D7mce;L}Y}ZXcX)SBIOQ>*_(qwiI=yRrj>X}=1C`?!cHGobI z-OrmbH35H7O6=^c_*}-XJ*Wueqf=BIM(u!H^dypODhgPLtEgI3J0Xps5_ndWXa%o? z0iY(HD-sg6llkDOvd}5x!*{<4`i5|JbzU3?I}KUPYp5(rUn;7Zhsx&#{*yF2BpGss z)^);xG5OCW9IJNJfb?ZAT%89MT+#61F$U5Masy(jLEZhYzLq}fdu%Y}f{;%%WyBQy zcJ{i>Pek3E$MyRM_{eAIKucEqG2#r0Jq9LnI6P}&&^cxh!CN`${@+<=2l&-}d(lVi zv&G*xOCP+e@f#4P{T2uqD~?4ij-5YL)`>WG1wN)r7BNIM@mn`I2}1^ocx!O&9{m+7 ztT1792fR33#OOtr6l41t?aEQvoW#c=mr|@H`2p|UN;YbJ_`&M{QBVj`&A(LbVSXDr zGZW$_ST^?U#ek3z<=Vq!cTE`~O;n^YSl z;>NEov2a%5r#ndo?wC5`33K^F$16^LQhg(Niza6jFob?&xxh1IwT+l7fCK2Cb_~1A zE?ZH@HRd_)puP{j?}_*j8+>--{2MG>-Pk%vcn7&^<)V5MN5jD$%e6)1T0%#-v1 zDC;9E9-8M0Et*A;_jLsr_qda)x!Xn@f|@HDZ(I;!*P#9shW*d&>y^PPz`LKUp@9wW zfY?tODCxH|7jr`&)aj9*r6Jz@X!v#c6L8>O`P;G$C7A?@os_HkeQHZ^&GD~Mm%NqUxe;3M^Q7~Pn2HgH4F9>2|_fZ5_<2_(4I z>O7fcK`TCYW6IG3AWd#wMgW$}Qo9!{x?n0w_xQeikKMTIqVws$N9m9L)4800y-5^3 z0K9u4npLzoH9ZD8YKISP&UR*JIkQr-p``;ce<&WW{P%>>3C>!lGABMx5q?Z^r`ZHU%mRX)S@`_W<=cFd|kpt(77S zR7|Yc+pZA5VU?3b7F70mLAf}RPSz|i(A7hS3z66q;NjAbE?V~l=3?cyb{8m(U9bjC zBU?Y?WK-5Hbe+};sCIS-I|<`$Rzvl?Ba4*}v;Eez{MM3jkPjXScg*`BB&` zF}AufW=ES* zVPHeczWWkY6#oNYIEn87ijxR|5N;6ko1!erR60JV;pY7H?6nvUgVKmEcMm4ZUfs|8 zY?)Yr1+27#8gyJ4D9!KaFnx^rWS(dWfpHLnHVo$>y~Xj8DkT(Avr87A z`L*cFj_j~Z6gbG1U=Dd5)E2_P%=4aao*x;f@jWFU&YzE8MKZVu(su`250C%7z8c@d zz^cJFz|U-G(zof;3;6@wZCAjV^}!stGEt8`^z+4Q3IW&U#)8kEKTm0485_VfWF~-Tp;mWiKAja1?p^~uZf7<4S7kMx zg|6N#y?dAc<>1XS6t^gfr+(EL4OZ6nX8Afo1IMu3d*C5G4vuwlf`RlDzd`jkyTcBV zf<#eBayy=~{J;z<=CS7nYH<$Y>?9=?dhg_;S%Gt;Izb*p4Ojld^`xAn)rH;K_ zOEy1xlTnGoa&3qgp&jLv%!99~VZ)n3pZ?2Oy2u6>GM@$My4^%vDjQob0jlNz!tEj&M?a+Bcas)~qoKS~2$oGSeVXC_$(2_Wr35;%1e*?OJF%f7{{| z6!s-SaactOse|-jK5+jBp8-2iyTk4XA3Kk{YxUgqvPxG4_PZShtN(kYNf* z&sVsBk?(%-Jm5d@(}e`$##ikIk>RKbRhSsO4_t??AJ#5G;{}MmMwhSRi09c+;wfDl zp7;4JjYi3@aM911H52V`1DRdfdY{xVxySPtY3D37VUh&i04o2>F-^ z!&pPd_Hy=Q=xO`7KCE5F`-VLd0&dcS+QGxh;KoC6tZx^a;rRn6EExRI?EH_vft<8ciqJEu<=%dd z)!?D{8Zo?jl2MzEP!X6bbOzHSOtRwz0N0h&Hk*%EI$tsaR?`55sP}{bi+* znC|b34mV5NYqR??jU2M70%M!r$Dl29VWPicS#Vecjvn!k=@W!zEepK3CH)`|sF^o# z{~qS$O?w>R0VT?nSa2=8X(Hz-!WcgwvG1^0JF!-IsI%0K_K zyha_bM-jc|;~Rz4Ss55k?OEr?AVkRLaBe%H^LTo87(C9G>h=(nb7ciyI&zpnDPq<# zXS#Ha$ItCk+YsSq`qVv_M^O)h6->fW&DZ}%6B+TQU~ARl0J1J2|H6Q1`WnhBSqgYS zS*UFdRldH9O1JniE@ux+RY@wyKE<&UC+5E2buyzLmKwbGE~{l!7wzS5nTTWdq!1y} zBqdD(>x1c&Yk0nDFw5mLY}QBrV{fXg@_PG3I%s@<}O+-thu7{|H$@WbBk7=-yMo z@cwd~8m13I0=n$V#4!%BGWfsXvMO|F=<=p{s9o>C`l#7Lj-m$j+tO+L>+7?=fep||E75}`4dvHrppUF;2i;Jua>0j zBMp8^8`X)Ry8xMpBOx7a3iRRyw9m1=T4O5x&(h+!LKWsVFEEP#I`SHd4g-aw%}ov5 zhM{sp)h8QbglT^?Rj@BlsQfs;sF|F}_dpQ5rbnpAXn4FoAs_Wu=w6ENnVPPJUsaj` zzVALs+>$rC=4*5t#hL2&U4fVbXd8>|@~G=USJ(K;5hBJX9Gws}M>&Q{74q?)N{->d z%`h%G%=}#DJJP7T@XsazP>{|kwqaFqlA@`Ra zkpyWL{&Cjf?BC^V=*IsOE^41%N!Zz0UQ+T`oMpSpNvw`Nz%{X+%OedRidc(1%HH|{ z&{Nu%@!^l#pmPpPg9i%0*)ZhFwJr7>O_&Nj(e)3cG>v5k0FPTJu-S(-uuif!>FNK5N5HCtZ;@L z$iA}bQsGihi=w!M!d;|PLOYsc;*`P0?Whakb6bj>=K^oc%m#UQKVRrI)E9c4k+>O%z*{YW|N z$ic`Y!pCc9AK#qyA@xg313jweZvV0oUwIt5dof(frEb2*xOvQ01e_Cw97skl^dN?E z4?>eo0X0)gDF=4qd<^r=%i5-W zH9?#9lqO_!pvgsTPFxPf-vw_J^=8+koczeZ7Bd|0k>)Pq@+RXZuhsPnje&k zb?;)Q22|CvYF9ZCtD`(gA$@WsFW6~^Z-b|(nmp;OhzU>=Bzp%NmD9s~_4llh7Dh>z zSitPL!nX>ujW$TJL}7pgeYT8;r{L%@N34D{ya2$ zo&JOxb>BSf^y36=grdf$7XQKS1#_y&BTKX#`MhDR%Cbv_>4~DxzI}JqJW6}5S7tAr zg#xeS0o+*%hvA(p$E5F@9=g2sNE$EvO4eS15KsF*n$E->syFWc_nF;3*6fCaLiV*Z zBN0(Zp@=DpB6}e+N7j@zQLkCn7Ph5*L{CJ z@AvB!yD48z{5)JsO7?PORbS?U4v8NUq;Knkql@_;KDtOp4&ER7cJ=!t@<$ItIUPOw zRrxpDDHqsCf{@;yTZcO9!rxD5H|<|H5&~o`u5z2h#U3qOTeQGm4?`ZjIYaJ9UZFkB zMpxw^PBAD(Cvbm*s?erg{oR?cA1g18d=!LTPzxMVk051bhnOhnQ21&T2rP=jfFXu@ zb9WM}9LrFms$ZoL05j|goly*DTb+h4pvzpKL9^`ciRzE3dV4Ocb>gDU!3>D}IMR8y@_W13WMa{Cbu9A~=8ei5fp=OK{DOm*T=v zxwSxP3SfrAX9Nzn-U$mxDQ3Mh>@xq#*7w|QsFJ$cnSMaBdh=(B3j2nj2efmI+ z;09TmkK6E-oF;*x9EX$VUx5~@q$OCCD=`PK3r8*px#!)dts3f7tL{V} zctC;;z{zN>+|@;+2M{xLoNa*tD%idgIzLcq7v?@Cvmz5**{2QI->ggC94|}Ta<%`C zzTJ@p6fyS({m5yUt7uLZwgG3zWB8uAP^JrBym1ZV`#%1fuPWRptlA7vFy()L3<((M z94m^Gsuro*Mh5WyjY6&DDrDu<#1n10;O&OKiM!9UOP;=8F#;E*D?*D$ogm&03y7)e z$vPYc$l+q#7+r3a%Y-O`Af-e9tl|Q# zASUzTDs4U+is_-65OW*`Q@KUWBcDqtoHlc+1u0B0W>j;HWrj^YSO?yc(*^IwN=SB-3jOr zz;f#M4tA;JiujJW#wOd<7qi&UYYo#T92kvP!87w}4cXW^F0cz)#Bn5aq1PpMzY8<7@7danPU z6k&?7p7!RVa-oj&VvKJg0aZx)rF(scAl>Ip?#|6df!yt{|5hK4vYO|ajLPa7_U;Er zE0cDErF-%}sGGHw6kcHsfQ9Bn%AdDe2Q*gC?cHo*s^+0dy-dGue5@=zr65#|m%>8M zm@PO}3l|o6bzHN`UWK2SOxpc81F5hv*HZG8TiOUAjxbGFIJs}X80NG z{B1A<1>h-3^l?RabQYgu@`rYU)lUF>w3e=*(b&dFnFB}k|| z;AQ&j+q*L6c*0R?x8h!s?r$6>fc%H$czQx2ioW0BcitYk$!EA0A{18ft#n`bA2nRJ zIA>wiDMnGI#Ua7%KPAUj%A6X2tZe?Pg#;q!Tkfkj_i8VO0f+-_G6n0l&$m5{BONBP zs?LsD(waaag7pSbP!roDS%KmUxZ4yS9-U|&m6&-586Mh>uKy)s5`fxUpTQmb6t0(j z1A2AYv`Jm~(}zl$&C6XaE%LzhzX?75zZ%9{DHYJ3v)2v^bwQP@&RrObXwijU%@|N7$D1I9sCd}zbegOJ&W0v9tk!$V7Q z|AEkX%nqg>>DTxF&@(@L2kXDk9GME}(-XkX=tF{wn&XvU?&@_4VaY!K1a~9b+N)14 zp)`d;q$S{DanvO|@d1?dJ7sd*CD{+&{B{zyhtT|#0e)7ARxRTk=xX8Vn}=Kt>V4q! zr?PiZ3s$%46u>=1RT>DQI07N(rz#6>>F_FS3D#9`8$Tj;@iP$Qb^U=mlAs-__Pz)0 z8|2$H^~lPr_Y|=Dk-c}c7>xo%|E+#P-vlzoHJ5ioMk4Ju&{>mzQG|8YJjfNkONFo@ z7wq(?TfgE^`hx27z9%)Avhqv(LC%?hqWYq)=XE%}T-%Q$|5Vc{M>7ocTl&Pc(ayuy zx71>-3ROAC@6HfIQ8DT>!I?Hxu0>UyQACnVdG3{9K}cxRKjZ7qg8oy^n_L_qrT_yH zbQ58H;{Xa%M4(JMTW$vk<_F%vPp+6=RW}UrDD^*ez51-2e*x#e%)w>$(xx@f3gZ{b z{5t_v+Uh#jp4xckC^_hmP~in|ZH8g5dBejOL(K)^XlDjLb-x1b;D6}c9`XmpoXI=5 znn3S6ha1Fc%ikoFb)O+pPD$9~yI_Cl1Xopypu9pBK_3jE)NX9ew}t?x^c}FVolP6J zwY>U+OGXpJ2u~1jsAri~^SR#d`Hd7G`ETXn0bkQVoHVF;(Fed~SW$@@EX-?C>fpc3 zUVcgE-M+3BAYTclxR^aT&=iYxgCdM}>f9^%o!ysqc2*)5F zIbEk2=l&t)jo9*NN;7Kc*~59W#GACVQ%)hQ2MRG1I8fa|Ohy)vs>;beb$VvL+2#!sXuzQ+>nv2bt z`;R67Y}`=r-!Yn$KJ;*^0T~(Pq1~ z3erUfxqA|gMK{UU4c|Y~Clz@2w|ks&gjvf&Z3x9RMldxI)VwLUGAGz|UPDmTF!_9MG8d|FFV&pArn*+OONA=x z3wkqq$C8RYItNWCO~=4quD-C<+>iG$LsTgCJmnTQg%@7kSFnoaqO%?};Y;u1EjMUQ zc_0H!(cV&V6JCDtv1rO!K)I(DjxYI5aj4Sa3Ad}Q>cSmN~ z#jdw5SAAv6i;+{|>MwxfnN#052LyFiOETgku+USNpdpD!{fTlIUo9V&41$NAPIL=X z;A_U8%C#*(fSvq*uhrjz*LdeA2e%(^ucg)k4aqs3ldYM*AlKc!^OwAZotG@JY0K5C zSS1`M!*`owax9oeNH_)#NW)j^Jta?)X~7;Zh?MHCQTb=E*tWo;(Hkw( zzoTcd3&~SgJ3;sAr{^oFFJUj2?q#KHpB zV~GlX%c3UJ=>NrOp*ckn1+n}o6sZ%Vc~bABEhi1!vXmOBX(U8wrI}VBqPUA^dD1xE zZDSw=?Y+sW!+IWlUvoCNSux*HiQ1|Zoz)*vFZJ>G5t@Vs=7^{N^)ZK|zJ#>vNy@Zr z`<&~Xpbd*NkyVsUj6Zx+p1-=kW-kIh3_HEq-tPZM?DD#|>EIUT*ne*6h$%28?r8dbK$D#b-P=P< zyR^U16L;hdeB%5F8WtsbWvYtNUE$)Qtnb|S{gmDkNPD_N&TYZJ8v38&*J*6;ndea+ z+XVqXi}gtf&kDXu7S`SoD3Cx`9ml|DfDuO19!q~{QZn*)c{J-yBl|$pc)M~|vlQ)c z!Q_LI7W{%n(cu4=N@)(6sTF=aF~l#OJc<0EaAFvA);N9tPEPe{IRnHj% zBN828e<<&Adok*~*59UB|MQ357mLGJ!dO|0dkSNCDGuzr+a)a62~W_cdZP*n6VY)? zmJJ0B8=2IQ>8oE}fdB65KRBO*GkvQB+0;z=KXaQgQ-(Tte?G*4_a^jMA9kz-?KiLq z;9F3mlo^9h8{}*v>}fy?bF1m(yoL7FIK&K1jRX%N zaV`{?*M24NYzw<=cr7xrVea|9>4Mo5iTo!Vw;@=Fz7!zcU6#MKkLn^HphPpFg*cI; zk4FolU>OQaU=If16`>mL&`(zlqhyH58_CI*ofUss=aa~)4hlK9fu(pC2>@HL;m@G@in#Xgm$yjB;eHNkU(a}Wm5bkG}9-7ua1zS6Mg z;Ac0?BwOn;kUcBN&KD%grSH`)Zh23CMl3O3MS=9qk{l@Dn}!5VbA(>{D?^lisV9WQ znvl8StIbsk$gl6Sij!tTkF&0QTppediMq%kj<91?TSnsFwyEI5^mVT3-m@S-Ua-wZ zem^O4u>k7Vm$b2tB22=Dc+xq3=GLEkxQNmuXVSPW_1y(OPg5UokR86EQ5r1yRfy^-l=WAZ zvJccmmhD}&<(naYFrB0ATnS<2m%Y1ieBKA_T|SnT&bHl#{f5vXM|mrkE|ad^5`?e( zYB7-)A*i$XD&|kY@yy8{aKQ}T4}d`bBp#1woa1?$S%3WaSHouC_kt`+-=+hG0+56Q z6YY!O{SK-K>990O;3TcD5ILHNqx8^jSc3d%j-Q7EKMoa9baTSB0ul0H11G3Eui>^_ z`LO3GrSAMpWg`94S?HQhL9&?$$qry3(oTv#WOz`iW2fuSRZ)!rOd)^xiGu$YzEsI- z62OqT)-QVyyU$y3W;~}(Z186Dk9(y+xeeQZW2AV)2Qe54x^paS#XB#TqnO}*l)3^Z z81DJ?g-z%8C#ZM?QiBMm>61S=ya^UmH?ER9#LcdIaVfXY#|k5veAr9tqGn*rYGqgSrJ=hO}sSNyT- z>FTu6K4AWgZMhNp>p5`ogoV}zcL*1z=;8it?5B+FYSM>4u@i<{gi2iy}xa3|c zm_`D{X4<>YPcLuGCce%x;@1p;hM>rTylW4ksWbV#CPRP!YA_01k%?Ec1)Pt{_9lUW z(Vb6xdg0bad)-nu4`Cf}1M#2k_56uqKz7RD7+^n!fp2ttkq+iKrOE+ply@o=6wf z!-T)ghk1H&6C*q%F9mAh(~Zs1c=x)1{hD!X6z4{!0T@UfW1`@|Jx(xQfrR#Ct5x`mEa!XXaxk_0Mg z{FtivjdyNm*vtMgd13;&$PR(8=Hev(i0l}Nm$}Kihn-4~iaQ9rZ+(qZ(bsJWa)*iZ zoD^w-r8q?p6@9`Wq2|4s3b?V|{$YG*efxzGXvNnoxWKovT;ZR(bpBS>RKOo+PCC(> zLCa@v>&|&y_)|9jVj4DvZ^#I=;D9;YI1mpRvS<&Nqjt|B8#5XxXrj~Spi5;0?%#a% zkCM?>;R8r$9b#$67|{pA7x0WN6HjNr?~s46n_-2ZW6d_ix}YR=pY8fX#Ill&MYLKL{!c}qqf^ZEzzFxSaXdmld| zB8dCPT9@^|S#u5fo;AQtX`Juhba1!%K`qPQxq-v&)o*nrE4EYp0=-(b znGwsTEz(Ld^zbtxjyUga;EC_3YYDCjO)+FDpWQRoFfY8~8AYxXGT!BNIchq%6^+3J zgm430AX3Vu?#3?7VPl}bBC~`bCkXOyVFs9 z#v1Sr zORdXPf~tlBi{6Mg4GHHq`LA^(5Gjcb^)M8`15T4d(s-Sd!~)r9+nQRcf=YZYo?dI`xv-wwplY2Nk-W})BhlF7W zT+IGeWg$rU$!N*vYhNtnQH-JFpA_Py@POs$BLvu)rL3hzI0Siar+9*Un1JjtS`5nk z0?6kDeFa;AEnNavxAzcQybdX8sO9(-;wn!gh}Na@AI73Pl7puen=r zZ;&NP##WE#zjtn+E%jo0l$FE2f2Jn-CT3mn7r-AMv3#qkQBhu&*rbUHNW&M-<|RW1 z|J7lX8T?89m3(@ryKNaBC;7*s8#r{Wl}HVblQQq`lyj>PAUppv{23r-oU9Fwjg^?h z`3aDavBowYkom5z9Z_~mlaG?)bi@a(2VfE&woZTkY7d;pi{$kn^?!3U61NNzDMa`* zCQ?C&>Mm2#6=Bu=;=gARM`#ZE^aqEK%Cb!zM^#wv{8-LA#VxYy`(38{l-K<96d`~k zJ?RS9;p8-Us3Jz9h5wK%Psy!(jPLB>PvCaw{hJ4Vs6aH->b(^_ue0;EGM4hg4K7R6?fJY zDdRB0^amfn?w<&a#^F3YsXC_Bul>S&MZnRQz;fV>vfyVaPrd~*9()mqn6A5DUbvU=J09?}nE|716v@_vfv@WadrIRUnJ)k2%GTFJu)Ot04Uo>ZaJ0tx-WCyK5BB z3(soqwNHnJ-Fo@Wwx?@z9l*0r^rM6JdI#9^v>B_-A5AyMkc8cE)OPAeeCJ;K%%~VX zicP3zE0D}(3)F7g76Sn;@CwcGdW7SNXg^o^4nA&VgK^N{A|Hc9i7-=xEfJ0qTeD0~ z?5rHU^9=-7q`sm3Dza^+Ep3lCRa{sfJ@_r^!?m;oX3hlS;L0Z_uT`<8dOm;mWBcYd zI7?R6iuFZTEN)O>ef-~AWvzyHgRyX7CUig^XBo&Im%UsN8gQ?~a_KoI$5+0GUZV{( z>7S(kIBa*DzN5qg(i0O@G-3PgVNa+`^f2n+OCc1viuxc!$=`8(zL~j|9nBZiCOK7& zkG$O2^13+mQ|q0PUTWWZ)a1r6yy@K%q@T2L;RER zwVu2|38tM%p0RZ26;%_ebkK$tefSk1L`L8*3uRXOQ%p8(CX#q250u{>N{t7zj zhjQSo*&f7bGVVK|l=krTZLSogf*RgTn4dRRf$pr#Zj+X-DqovR0 z6HhB<27idbsw2!>0BAUy{ct9x&4)3^`|2`WE-}$Upm7v3~Z5;YbI9M5B?gEW5lq)755;JZ=B z2YwovE(QKTStMlDM{`Q)<23$GUdv#)JaED%J{}mhzWs}1MyW1t_jd_7mG-S~B&#~E zR)%o-e&0I08@!WY^6cA^YttEdvcK@kb&+kk@3iGwE4dGBWsOkgn%liPazpbFczlY^ z>exkol^%M%Y)7NX~MJO#}8$a2hrMEc^cTkB>#dxY^LHq{=lcJUtJ zb*5?*uA&%1KuUpo0LMAOfPi53Gl=L=V*~7_C7w4B7<$OHNg=PE=7+g};4jf@B`WX!8@KrxcR3#Ds2rM{kM4Mc`H( z1}gL3DicfE&!-Pi_@rId$R$ODXN1>|gNOt`q+AKyLM?Q!QblIwc!pP zq_0wYa}K-u)!Pjc@fcM&N?T$Bn}_~&IDa3YC|}M10o;^5e@vvOu9lADgOz@+-B(xq z>x~A0?g1ET8Z}Ulzte_5RI}G@2|Ch#054IvdH+cRpyvf4Q$+gX&7k$hAK6>@ZT1%x z68HHVEX9SRDgNmAPoMXgS|&h8?QnK-{amm%xSbHcO!i_E5qFJ504&Q@oJAL+=K#+R zJV_Pg0fxnatrOXPYdgzLeu$Ay@3rFbNI|3+*nlk;ZqkvnqI5#xaw@W`hg};&cOp=t zfzx(i`L2S4sEt#=#R^-rT=xhla=~Sz0wl_qiDEi#;mTe*LsOAeOS>Qn^HTyqk?r(w z*RZ`fJpDH6^RHiypt4Jc_ulhH9pxiPuu?J7^qKqxl(+LWA!n6I`7G;_C>rMltbhn^ zqs_TE3deK44p4*(Zi;l9H(-DeCQ*fG6#R#!f4$Z39cvf)YYptkK`$&N)DQr%1=Tnz!R0(9Sq>T8{FdT85%@pSMvY6#U8dCQ32+pDIkT z1p+O{hsdlS>?_EBd0qL0v^TJ@?L@)tdr$_X$`A(Cuk9be&f@S-!ObD*KT9zG+NXNA z4I@f^ApK7Y=ZI`cEEHlR7MTcsMpt=<@LA9RVW2i>>h0KOU1mptz^%AoBu<|83`HEx z;=Ss+vfNQ}(Y^i%I5hm0t5AjHhr^6#|BjjL()muy-cXf-(~r`~ZD7hQs9NsUHs2k; zEou#4Bl_Oy%1|TNHlxx*4;Fp0sJpe_)(}=;`t2EV+;b@T^{2fUurNU~5cPF3aMq;; z$02C_=-Vnlhh6uT8(ak<_{0TFC;0D53*go4n5rDIRT_Vq*zQpj<^7)>FFe2%ph5D3 zRD{E#Tz877xypkyB!J=W;UuyEM4;F~conaz$9W5mVePyPZk;*s^9?oa20!UoOY@|TH{G=&(xDquXX+r&RBCZf5T!{FYysEK)F?36_vQU zGWuGW(TG6=@l#TAQn&s^ZKspsuMkTU(r<@^ZY zfvt+_(i5*`PZl)pD@K{TA}mC|UvS=iQ^dLHRW^aa)->AZO8Y(h zpt_nQTzCgs*b5`SzsrLGqlTuf_6pHUBI2QA;IN4r#S23z#rc4*$FBpq1}?*N3)uY5 zg4*}Z&9?{WPc_x5(0!#c9rzO-Pkj2(BG?QtkiBgN@zK||Cy+Gr#tSsV4LMf*YS_v7 z-dTQ|((b+bTeX8eK`@DCb`DrJOKtx{J_z@Dn;Uq18W}+TnzeLn-s0PzKziTzR^z_n z{Xic28zc$S=8Y{^76KhPzsxu`?pW0G*KL6%jL$|7j;bqb>L`HRe-ewzTXZ2Q=-Pg- z<w`*_b4&bpbaaWWWL3R%va=Jp{wcyX!0)5E z6ln$0&Dx$rJI^Iue)PL8ONMgEQaAH-ewwSqS(YRRQFb1nr?{Sd9{y>^ekE8~m+?59 zQ9R;TCz9QW&Hvnc*)*JT8O^z*;~=+atleW7#`O)?L-xJAPs^|iEOec@>2kL~U5Km5 zoJP{TdnvkbJ~=}!)WM2%N{s;0Hr;grKv7h|G%TY>ZNf7$Q!U%^cKoAFEgUhVdq*Jo z@C@zFZZ?@J1GJ!rSdukdasEl)X!t9nF^M)>jUrT|QwL&5qe>~&{9_L~a}qzFXI=F6 zcN6I-mBJFr(y$x0f_;yh@nKTC7?Y1jcAA6RgU896#+zHG>u1llIR)mfkt=%?v;A3y zDPo3!jtz+Q6&@-t`1|&BmWYpz?U5`D<7#!tMfg&t@Ydu|mdyTPQKuLX77mBX9;fLM z7Kf)VtVtK)^TvSR;r_A zogHYEdm8!LXTZA25+{{*$UT2t_w*Mx=zB@uKq5Bb3iHz6WS1`SS5LY5Aq5p658|Oj zY?V}Z_4NoZJHTb%d`V#U^Cnh%DbN|csAn*sTD4_7NpoV$;{$eofo<^43W5unso{tK z3s`_P%awgi%XzJ$t%KFIp$`i#cmEZ>RvyG`cu4x6kqiNp_qiFuR~E5Vv>iI%cf|gx zEWw`gRXW{K+`v&r6~#FrJY{UBNTDa@>gt@>Lr(|vb{r|XjAQ&~hK6z2vV$0A?dA5y zFV=QT*J%a$S12O4nUk_Re>03`(uuCOa9K~F{n55x(N}9BqI3AXeZsAZhBrmKd=^Um1-2gfbr8$t&FRz|yqr`w6-`m2ulb!c zfZ6raE-!q7SYKX>(FF3BRiI0(cLI_T-OG_3w-N@B{o(r#Ed{2EqQWFOVdf8)PUw%} z7`DOvR5c+I{PJZpx$crM1TG+7W@mjCx9+B$>jM+mT#ic(9KsOQf5}he{_@!+ONm9n zd?d4%ookNF4mU-u8~G>YMRAMvPF&V`GF20wR+RDf&teJ%L&|z+R!ig| ztWB~EH2NThG4ynUJtAce52{}VZciIN zC$g|#gG^WDDnRzFrxWFJDk{xh*c^a`FulsL1)pyyy<6e6ly+TKb}(ig;fEMv;CQsg zl^hgAYM<2rW{Qb`a*UJob-#@7Sl$_5i;q6H&`Re1shsbGibKOzRyLn&KVT$t{GPBn^b!^^4mCmeG( zT>K}$sAzak-+rn~$C6GDG}$1_4e}_!97iVwS@(xt3^NvS_$JLI-0>K-r=I=20p+~k zjSz8ZtoJMa#9Dz4cIq9fqKl@iK-ZBE=%R&5X@x>=`C~?tAV7?y176@cUH{ftN7h|= z(0OMwO|h19yjG-a+>Jb^PZ2=D{H7NJ!_3eN%bMgP&kSeq9hpJDcHEv>7x?C}@bcO* zdo&~OLVd@!zxX{=vc(ugMS=ZWZGXiE&Ph@@Xmozo`EOx>m z34*mqnXF{~Yw>Nk81+{10X8FjFlmAR*GC zQS&m}m@q?zhHNkB+sIQC0s5eP(_QVtzx4idN8$?9IV)W{4vv9>eYm}s;5f*zSV184 z+uO_GT+4gP#~UeyRu-Qq<#;DBtdOF`fa3o0p;R;qvW|&%vKv9usESXghxE zx|QsaER%9ZvWY6C`bX-vI?TL4IS*@@h!38iR|)fqjAHl?HF5XdMl9M&mEaI%{uY5b zRLfLi8p3IP+U5#@{NumCiIKSi!XNkdmQP}77oVejY40;7T_cZW|Jc~{SA&Mq4=W;G z@ywXo6?Fv^nkD7)OeOX5rkv#Py>C#yNe&I4os>=ZP%7oo z`>J+U?DNP;y0yG^#51JIV>AgzbW{1(KD{~c`QvLYip{(#Y2UpB=J(_S8kHi5@E?)4 z!&t}7939LKDmra4+nV1I#o(NQ=~*`1L}){x6O^MF1#!dc|F)f=NuPzo&HHvIqUQX3 zuGM7io8m2l#1gWlCy}_U&}YnhLPl?7IL3sXI8BdX36w71JD?{7;YzX2{)wmG#0TGf^mh6=t8|_Z)jHZ7b*AtEl3e5Da-(PHf!w)S z*$P2I;cfo}GdT?1gkCz1Apw{KQfbg@TKk*qrRN145n*NJH6W+t!q`f$rv-}dR11(! zRjzS-dCbEc0MXJ?Jn7b!0M{XidVVX@`*-hB>LR&nF#ZElIDmwox+Lk=hBI1p?Vz2& zq=;`DSg)p6fu@O?`R$d>+>h1&v>9h8+@30oC!y@@D|L@C_ptB|8E@C5qaxI~AUFZH zEnvVg%UT`T33VG%Q{)@f==hm;7*Klr0k?C17$pX9dJxs^Pb7S6N04iTDJP;6Y+q!)3f#(mIC3^R< zrgOV1P;*k4H^`&EkT(|o0|9}eL|~}SaQX|1HVPj5NGJGxA&1V1Jd!?%`H5#qe@v9Mo9y6%|ySYFX7*Yt(26&7{obbJfJ)**nyBZK_Bd}2MU-^%? zj)L7KXuJJD1<@8mB}x!$h*R7hb)?)rwv9Xw_F25innvF5BK zpC;$H@PI(*h8IhIbvGw1p;(h;br+qcjfVw65FzX)JD$k!epc7XifY^-pN7f02>G-& zh+ct%4w4(5=VHe(!Vki1gRb8A(@n9!&S_x~S-+X`^6PJowDQ$Lel96QR9Z zulZ+UaC6{aNbg<+(%YZ6IZfW$Ws?9w%5mk0gc_AOsO{H;6;f;lw0AI-`RPUi%!AEJ zJd~%1R3}>yLr-N~JdNpVe$yWa`HkXZk?Q>SZ9aS7n4KH7%lSz&Is`t{w#|!*523-d zo7J2zYt#W9K;na6Y>WKMe$7>QU1+tS8?F09;cs2e;zfuWrnR8#=1?gMmfQ!p@034l zI7?59v*XWrZ|w?^2B;l)0)S1@K_4;A7+y1MV$F_RO1{QwKi4TYd$=uDS?E(?)a3Yq z@BeJ!Z8>Ks8l8Crhq;ArhE$knJj$1Q1X4@qv%?5>OC~(Bgok**G5ofYK?Pd#MVgCt z&gQP2pcDw2f?NV+Sl2mpYOKKg+8U>Abi$i*>WDD$&^3!^L#jF&PmsApEYW9j;hvoL zK1O?tSfLlb&`*O1-D6#oKpZF^vt|<%5;((w^@limN>XsHkZIgUt+!|Wsvx>3ygNd%GM~A$S3;!&%^wtv-ZxTZ zJ1pk_m73E5v>|d0avQoW&f8!zMx#O@Iw9!WdG7`1ip_Z_5vh8Q<-2#hkiZ3~fL*{y z1}H<3zsE*h(7H*890{l%1ch5>p`iwwC2H{9lI>qD?Z&hO2AQ?;f!4vlA8P{iz=LwA z>d$zaFlx(%Q%YKiA9?47@IZ8X@L0PA8zXE!9T;_9P;A=4f&gJKJ5|^rU%M3@nkSr( zT;OOH-?jlmUjZ1Avzv^~@uEP|de^2sqIC+&_GOz80}h zYybQz?#Xx^LNzbe&2D+H!oub~8LkE^Y&lEJ2QAy7Mailulk^f#G&674Rq(b1d>rQn zC$DLUv}PYnuUM^nn)4he8yJ3&jL#y0M6~95 zMy!$XlU?I*c6bzLsjpCF{vGDqV3D`C_HT2rF#(tp8AJ74BD_a6*vFE6k?uqtaa2wPTSkrD#Aowqsa3SyX zzyxGri8}OvI*8RoQ6pesMeL`u)}4s<&heBCN9cYI`}~N;b+lcF=|BD}c~kwBYk$0a zzA2>il!&1mZ$m>qdi2$>%@SS4D#=vwoif9!1cflMLiWK-a_ zsj*d;uRNL@j7ixRfwi1SQIUZ#S^;8M^STsp`j5P_q})zljCgDUHbtZ zsqaYDra!xSe)K*#S7|NEH50%7V*T;uO6ElIwThT_pl}K=fuUf=fAdRu?>QO zW90Rj5~v@?_+Sf7?G8YrzIRW8mp<;uY?LHj41z6>L`~cUDF#K{AIWD*gDp#S&IyA_ zg$C&SYrLlJHu?A?iHqQu?7zMRrpQ|mlH_SXk;rsyx|S7emR3!opQ~3t_)iS%pH&rO zWauL;Xn1fZ0Eb&{RhUtG>;F~TKSflEeYJT@c%IURs~hK&OHzmPt|LJ|M?zmBph{VN zn<>#W6L{qCarf6F`DNc%KfF(!E&4Frv>M*u@a32$xBv}YrLvcT!r#9=pX-ejvEFEa zIL&v5&%5@nYsuy%qJ8^~30Z(cV5d^vdI^Frz}w+dK;tO=Fs>8l=hg`n`Qd{_ohEM?%`zjA zb!W;}g|zrpKu=EB&^1Re$Gu+$0|oK~2u|MWpZPkkwIl_dB4a?ns`6T2e0LI!vAhr~ zvsiG`j>CpKIDy1xcROdaaFZ_QQir?1&322#&=*7g4ZLMbSKoieG6w4MkG;!tvqWZ} zE}s0B>hG2SP@8h&5L%h;EQM;=gQBP-aS^3KTpMJ6$(bEi-FCb8Q7kwnYzES*3Wm3#k}1?c+~U~r%J^KXuWd9AnSEpKG|RLCZd`O1xbZ$}*n2|4X#x*mwtcETJ@XImYjyF0e#x#6Kpdiz4Qi;zVqq z?e34SghoHQZy(l#t)^tUkGh%Sop+ZauR~i{l(yetD?DHjBa$O%cUuobCD^T~f%^|K zgjpITpJ-opMOE+OiCKjA%elK8BFOA_X;+-}GdU(!#fv zexpTvL$)91oDGXqtVWeX`o3DdD*T+P`b%yTy8~w)J0tJZUiSL39JJ63pT+^&Y3R8X z|N6VJSXa?puR*xI+@?%Xz*!2y^E2}kcc>z=e1o(lVWb2SU3gN4suCP@Uyofvla3bd zB*hZxy0TW7j8@x|bdl8*+a*p7TU8vEKI)l zgt%vInNTE4xsZ1A2YCoZlAzY}!ry#NjM)3%hoQ5n_J3U#&|LRaTubbEFzXzQ20R|J+5d817=-~?S?#9Ddl z@wxmTFP70y&b6<)8=ya+7hbxtJOyZV!OH1MfDz@M-K>(>wz=*;%j+85=h2-SWK?;=lrC>MET@Tum#$A8q zO5jmu-5y@d$}RZ8rJ2{@;K5q2-)Dw4MyQF<%8lrE0u*UGyuYi6-Tn;BkPfC}gw|E7 zQN9k6`gsh+2p|mj^h1fn@?+0rxinwoG3C^wySK|>7&&W*A+-z#M1dTk0GWH>M6E+v9z<1tITB#mYphsw`}0k9p0&3a@oA^+bvoAr znBehE$I$Bhi>DXECUG@{cSi5U9ey+c8r`gLFHYc*KSXZnFdjo z+$cs6Y*0k#3j@?>59*{r{m!!==d%#M+nv20&mW7ujHp}+=LQG6Ca>BG0mt5*-)p_( zD4I+`2l6?kPm9s3$jl|4p!TWAy&2EwW&gFlJw8!MS@F>@?GkW-!?|TBNBB+Y3gybW z+YUC&g^jS$A+gEfYY|uCpHq$z+a{OM&rzoxC5?p`2FKK>KNW}&NuotIau|%>tJ(Lr zZd|4#*l7x9ogFw&n&ye3&L{jXFa|iF-vY$5kcA5lLz~W&aDhMU$fr>Ag;cFtPzmkmjOr8h|Rk^rFGYC2#cLQ~&roM|ewYI_rgX1y}h zf9mi=OPJ_`pl^Zxr?Pua?Mk7X3$zWOBs=VV2r{umVTkdjwe`K25$np(SKPg=aL2Ok!hAKvhc%+8!?-druJOb zP|H)OYJGFC_wn`hZZz=xe>9zmJCtwOhM#vf!`S!TAWKq|J=-fzC+J3fEGm}BOh=eh6eJg*;z#!YC5RJ6rN z+U{JZq#AbZ98UHHlBp(dvx1C22x8U^q!)MOF_-ohr|a^V0gfk#ya@Yq`4~_ae#jqm z;#c#`+H*lN9LsO2BXIIc^T?Tri!iHeRv&n-6s*wra+6>xL??s+TyWUwgU!M8KR;cd z^a^C8^!%s|5H5b%xsi{y;eD%Ko6HMZnI+#jFeE9YyKoq(Klq^Rb@RpCRdyU61!lI7 z-DOItBN&L0-0i*@d+_U_Q-8TX!CHZ!{iux-#%} zZ%Qlm#huRvlLp<^GVL><`g2iZI^TTY`2kRWE<8-KYtNwCusKK8SdAA6=hT+OmdX)R zvA+1lkX3b8NGvM|QVC(~);_n+lBrkHL=5xi|S5o6759e}!hevM@dF(0yK- zF4i7Wy~w9|TPH^x<*QhEeQCtlPBnDR3u?rrh-1jY#J{d|^m*j-crR1-OiXr*H^+{I z^!>c%o8aO;v=mN%Y6yibrA4Ij^610nLl7X)efp1~2FaudNQ6CArMMCnr|wfqd0qAI z(u96yb3SnIydnY{QtDdoJHL|Bd;!((GG0=HdlQr}_|GQdsBVd0`wF+)LELTW(64=_ zmnZJ(vaCM=YH71>)mn`T_VS*1A$-pg)+8cZo95HG?Z|*{;(O5C6B?Yq-;2Vh5(){L zd8EfECmC=fXVM-+RfWaT!i{|JE0iefj4EbKlvsBh&f$lxm!Zx3l*o|7x0-H9(K~Oj ze%Zw&oGELhojk(}uR$?RH4YvU`rBhtfOkcZR)B@R8Ib}0NJqs~7hr7;R|$Z10zNEc%4jsy;yH4A5P)u zp&a&X=LWkV5}K6JZ14|7#}eYtJ?<3pD$&0e7S5@%h%Q|zSrXakcj8`%x;yO()rAPh(q6606;>n>l8uTgde1G9IZlMho?m{WC$d2 zeZKc8p{1`s^8CTs>2yPwv+a<>z9Wr@2G?5#Uj>^xbcj2UC5fmh!936gyP{G~VaucB z)wMRy`l1wh;ECnV5(+Kop0sCucwF$Gl*nd4aDfp*&X?Mm3Lb)7^;uSU?>~gppW&(>(3Q3oqseP z*ot@YF`?hFMysQU&*OzSN|r!v-(nTw;E$a%hsO%!b(2dp?%*-H7HMv{jv~aoD-~{9 z@9P#32L-7Fej0KXO+8Fo^|Z3W+1PhWQhAbp_*~Vr9xh|2qiOl1#xJ=gMuuZa z_G8lMsoxMY#HnwhS(cy635|sh0Ui4Y?}*$PE~1hEE&UXddSUzyJcF^v!%vVj0&s)= z{xH9`Xi9&lZY};?W8i}(uC-1gtu+*?%eDc{2xERX*mE8buWOXXNdn7l zg(|rbS`%Mv;;XjFO%Z=V(lKN>?)_2+5ADp0Ik3SocE%`TT$YT^JyG_D?RRg!-=`Lp z+16^*N^rwfbZ}+y!wcQZr_v-rE+F?^8qf|Y+kU5f=u78NLhx_1zE9qoM801~=09lw zHWJUrF9UiukqjN^p9!^ts}oN}qD(l64=**(T{pGznCC3;u1+469Ya{tz+X})XoMed8|Jt_Y`(_p;s_V^0z07eR`*QQ z_C^$3g4*okhufE);M?2$rX}G_c=eg5YnGoVd(tQV>^>In4tOD7N7yzoFqN4pOL8;| z=jeLyTP=Wn>2kZVhmY3IHnLe*e~?Xo7ubdTvFD=SNCTdb=e2^sFNg_dAMB${%CapP zc_a_z#RexLqIHFSg@0m-(6|tw3UP3XW zYBMr#5`I-gmej3>)Z%GZmsxU8CX(AozMLdH>6-!&{9iO97f5V3>2V1tz=PL6z3&Oy zj(e0M!_*sNJ&&fL2WdP9;Zn(a!;1Jy&jUF6S!4}24F5@4M*?+8-XAn1h+NV0ILeJ3H1Sf3VWtSDN8gkr@yWWMQk5gdQDBaP;W2Gu;Y2Z`yHGt`r zJ3Y}Lkt;(Y?>cz=B)F?~8pq)G1p_Ahxx5X~W|b<)#DlSedhe6)JhagyUG)C1x9oxe z{h;hKL3C@{MKz>)WAb71rO_CV9~a-gZ+}fPYH=ftH;$}#*Ff?4P4o0wg>AkeI0Ls> z*3LQg`8BA}wk)xf$x?z_u-AVJ(!}}PeG+3cCH-SASZ4U5^u>fo1DHHbvU_&d$HV`z zwY=O!55{R7i00AL`Ax_-;{0QDISAKh?o)?+pM_Rz!d)meT@^1-d})+xxwv&Wo}A6 z+_8nK&NT4dA5RQ>*2O{tvP=}9KfqbQ!!>~88=Ct3yxL882ok-P3O#*nS~G8YC00?4 zs=#c)ke&jPpF`h1q1vcwKjfS{tQS<-eE|8_kg7Re+X4J`3JMz0w8IZB!s5NweTb4i z%EG!2BMy(`9}J%fd0kd|nBJaol)X_86-gW^{#Ix7{-hZhg-rU~5O_scvNZsx9n&f$ zAccM<3tS0d$3}ECSUcOnCJesQeXn*s|wN)eme5a80IH=0YRy4F zT}*RD6b`R-{O`0>Yy`Wop6st^`DgQ!8u}9f-^nufC=|A5rD0)#9*M+$Qx4$yq#-)B za93XJmHm(SBZdiG&VN9JDQ9|V|5n>xqzb2Y1EO}$I|fixXArS|%d zo6R))Aa_pb(WLBHdhg*XKs(XROS=UO`EY%%w$!z=TM z;T|U6QP#RfBGLvRamTa$Aoi98-Ncd@hzet|NsVsC8v>Qj2#{Ss86R&wxZ$Ka_#GwA zNw$%(kf*hb-TCv@QWt%I{u<>am-OK6C%GqVZmM3Ci$>1;NLhF}|1p&B6iu=LrWKe8 z!u8J`t{TBP`(2i|M$1gcJLFo}!HFu||GuP9V0Z*^(3l@auj@U`YW6M-I54}yv~UL( z%Np!*p%iBw+0Zjeyr4hG570cD6p*H2l{m)d<YE7ci$v@e%{}T z?GgV$=r?xx`nCtS;$T&SX~^_WAv>ADDSiMX%R=}MaQdYYw+ZhQknb(Z?XgXhKZDx* zYJ4+}BAeLo)9vokqETt%RVzZinB=##*HfCm>gLTd3C#MM0D$zX> z6tWqm@pf+Mrl4$f0Q;LJ(9$BeZ!G=FulIx1=DZ`&@fGt^RaoK>xIHbAj;wWkMQpC4 z_=(BJH22%!Bd=-=Cu!Z?{?od(8<;~JFVlPrSmK#=nXW7qQ!-(Aa$Cwi$N19P_r;2l z*5*q$8=`Pr{{BQI990uEwtD%&oB@Cz0->hu>agmJHi$_LpFmk)Ao{={j(*^o3`r*7 zA>ULCf1jp4_8&9J7T{)+Yb3*e4}SyhcH9yAsFU33p*|8 zNVBVoT=AQw&p7c9K?{~_;RbxzezqF`{z*}r)9gcyO`mdqjs+qjhtaWqy>y|gAl8zX z^bwGjEzD=7j|Bx#A9nx1!}jo=awPwHr!c8B#oPhYxX=8Y%w>x6<<3(=Xzj*VC5=Bg zFwb|qVARZf=$CWKRI*a5wE2Z~^ou8fZzeUV%$`}6RndPX1Kh6B z=nn=VAeN8BPeT{13DcUM^n9V}%t(E&e028|EW$d9(~P%}0)l7|WiUQ=Y~wZN=`3;O)r&pF68jBtcPnfRkHW`(0&}?)WmAt z`-n%&<1qPGUbx$Ep0Hqxl;8Aq_r*={M-GG+Nf5I*ID3A1Q}}APp-jMy%3mBrjE?Hk z3cCp){VIi3W&oypNA{^qy0|gJb&-~9(Xs`9aAem?44ktzlIxRc2mezv5!2`2G#fPe zxaJk~&GZ(#YW0%c1J7i=x>UA#=?XlUN%Z_RYzZFtFujw?NXE3g z^XM0l6Hm|Ys4-LitlXIwUk`_o^;!m|lrdK)D|k`*>YV6YbwLa%1f_Yb?=YJ*IceD! ze^;dp`Qmb}f*KG=fIlGoM>NiJd%CzHVLOg;(}#$n?9m2?6HbxHc2O8_YADkr1?eU4 z0x+~eSb-;0=zVKqO^Oxj>+7!T?8X_`PW4?MjwbP#^;`zrXMR7n&9*c^h40aqL7IMO z?>P*$JAgKac5+gn29*x9x{0ZQb3+yTxwBq_6hcY>>2wrkY`;Y3V{!27zBcWU8c$Ed z;?LaQv>~n1c+Nd=f&+F&f5ZD-&r%dkW(_BKQGJ6#e+V4b?vaWnSSb+csRH18>bz7QH9U6v>Idf&h_BE@8q#ddSZ1C;fxLn$KX zU%tDiNsVhOYkqqdSvchig=Ug=L@dD|SMD@if`&N;&BePXzjNE1iodekk{SR!6-k!J zt||%%x}3%wRT}g$B!uIueKT?MIVDup{ z2&uEz;mZ8L8pm(K83L7bID7OM>F*~?_Pr9G?`at`OJW# z`0^*TEAC3ka`CAh$1~f}JPe~V6LE2?lgMA(l&^)!Q0)GDoQ>fMsbAN`x`v@_o~S5s|HJjqf^MF6_N^8gVJLh4Rh(e0~Y2Ww4_qj^$+)1Akm z_WLol9}l(UADkX}B8qf@u8Zs42dsbHILvUng;Wz8hse<8<8!SOVM< zpZ|OAt>92edJzWF;_xju1a~1#miH7_v8(g@~u>9`+8O@rW)nH-EJ&(oFxN6 z-f2E!{&2gu^I~Sj9Flo-Zsb2ntc;>E(Eww7O^#5b+6#t%?dsJ#WlM_KqYIHf7FdI7 zIlx8U!TQMhGEJTx1a~>V8k;Q|SfqCz-W`0#XNGF`Wsf-5(+7xB3?o5_l9H3aqGb_ekAjhzv6&rI1&`U?U-)ijm`~#qU zmuZ~?>#J$?-HfYq?r6&I3C7zQj)?tFfHR&(v1D#I5ZMQb#P@PKAaSBE=8*!5_xbw` zeDG0Xom_-p=s#Wby^;r63!woA!7h|=2SlrB6$^0UMpg3I!$_7+XdguCw!CKA?GfFy zvoZ@OagG{5W4n@ocR^rEBFT9wI6lVhmLN$ep*M_VuY1at_Y8Of-wuDjj8rC%n@VEU ze`s(1^@l$ky_7IxIwlG?A}I`wP1AX-4Tme2L}hX?yy7(8`RFb?Py0aS4)S|pk1Z7N z4}8yub`}1$C6KjfC#s=(XG5Y8_Y)pc;Lw)L8j`CEVS?(*9`DNAZ-}C=dp{X}z{#9) za!@-2^b2I|D6gKr7p_Ih(bfCm(;q+wW`*x}OjUHt)}(;%#SrJua1Up0P?LJlWEJ^O z!Ks@N*C`Sm`28DiK7*P$K&GAZl_4AQ!;#$K6&NT*7mQj;qbC*DBdK!U*((E;58PeFM4K^81-8`S8|lK{qq)G&bzDvXEqO#^$uzD+BSumVlZ+ zIzR{!$1ZZw=;=rHk}eG?;z&gH2sfWopHm*#Z%a8Cp@<&fB+880y~EUt^8#~xUx~GW z#ea9-Ze9`D8sU4qGk@PG7{464rRm2<`oK7yA(J{!FKtPsnVWZ4sguICU(Cw?w?$rW z-_Sk5&ksvFW9w|t_0?gR#*WM8`I#y@KLj%C#xRRK=VC=M!B;QHCxCLC|D)uezNs6% zSBYDWCs=;e%Mv6t*^va3tNdnr{@V(fNr4ajIah)#U(8p2 z0E>gr6gUQbAxO1SHPwQfXiH1)M9PY>_?iN<&tz+6zQF7aLk`ew~T3SU8UzXdb zToJ0CeTq=OA+~&ADcV!rDW9y?$1FVD6} z-%lCQZW@Ov7q0mo_P)2oHc($S>dn6lB7#GCoC5iKyo|u3t;N{C!fQhAtQH9yXS8_@ zGfCRTcATY!A{{o>U)J~tIoUNg>J7+Hw@p(~T^ix%g$zK1cQz(p^_VSp&ZX0Odr&aKmg z%gI7vD6%9`QpBHwh5=iDnr%pQ-V(SwdA{2WaiM$(a3obV9ZoS?O+)(~GTMkJgc{XT zI6^OMCySC=DTACgOWp@WD<}0-xBEEvHd{G=DlSj|{4w=;aft=0N$B|US01J7nA}lIoBz)O zu=8ti*$f9fY5V7}TsGuXqX!oLtiBVu-h4B7k$2#u1tIBP@;uFMhWyfB;- zBN_Ui0LVtvtIm^lr%Mw;i%dxI)v__{JpuEeTS@7Ib-DIpsHanb6|cA@ z>TCwDO-Plx7AMFfpmUAOutX2&Q>q55-m>ufXJ=4OAf_?Y1fh89KHvfzZvKl-9+rRw zXzZEHBUYF)J-R}9)YPCU(Q=9An0aCf%rD6)fIALo&liFhY8QbfN6H0ug)yW36pd?N za4Uu^%Y8^&L4BUV6Il%x(xsghSmO^V zv5Ndx^w>QUA!d{dNeRjv<;bObDR9s3@PncE(UpE&^jkkvP#Ca5X|8z1&@kAab)903 zdeXo?Q)#kHiPIT$VnQ+fW>EuHy-2q=vKOu7%` zsmSJm$_(y=(9<&EwkKc2gzKQ2-oBGDYQ@v~mg?q^+dP23T$u~_yQiz%x%t4|3p66 zR)Kj>_=zBAe%uiy^S?!zi^2qfDQ*Q*`MNVx+_>OrenXI_RiE>9UVB2n2UO)f1t!;d zLA=Z8oi)_ieV`Ql6ab^-?@oDZrZUef8c`$+^{;XQ5oT$mV{F|$hE+D4Hf+V}L)_5W zJ96@%3!0ns*2`miGXyV7%>zxmQ#d%Q%gVHe@&woio`(KeO=8zKrPV4S-c)+?v!Q0M zka;hUDeP&#Q0sgLEA5Br{tyhA!=%Ke#uOTpcAZw2Tlm`lrn`z2%KPlfl-2Jf%DTQWboU*9Fy@6 zsi;b2J{lr%?w8C(U|*HYLyIy`r4?`)uGS?ZKF_z&&WnO4Vw|AQcXWaUdoP>)gG6tb z&RjUekNFP-psC!js7Pp_eL^j^0X6ZPvoKIbV7cHgRLfFNjrwvHDqGT0h1Tt!D6YO` zvIvoBMP#(*O~VAGw+^=4I=4`EF|o$ZTNK%&Yt~$_IoMYXsangAyfM0Ir4xSoY&nx- zT#o%;-!6fGi{c@k(?Pun_Zp$x^EOV$S!|-k;4I@*GWZ_M!QY15sX>3$E><6MaMvQ! z!7#ThFM{Ks=Q4ELi)=xef-Z`-c>dtx0|hV)Y}lVuRFKmnt6GvYF6iW-4f%@LSx?o= zdwd(UHo8w*-NnBDgQL7N;nYMyzT1@V%X2zK%%Al_DSu%Ze{wA=Z&+d0)skJk+5d+^ zcY~D)xve)d*+71q2~7c=dzhD|9);rGOY+^+FqtaDza^ToM_BPO{vSl&1D9N0eagRWyPu7L=2@TA`R^0= zN_?k70y`NA(Jp~pi^#^OSf4cMlI2}~z>SIDV><}chope}eBG&jZ0_V09H=ei z5a)eh1tuwYMrO1*x6-~rE|Mx-Tm;;?>EjOHK_uE-hVD;Es!#csJ&Lv zS~O+Uk2ETOpAyD9%8hRPY}>#Anl0e^iaNeWVv?_3fR{E(t}vfED?DFGlHWq!0^k>L zqc`dgYro*4X5V)AylK^=LKMBv!wxs)v4X@%&|h7u6Rccg*-nc|kSo3zF19srV#Hu6 z0SWi*0tO{kL9@YfH8@~q!EGPC{0xD%b4xu9MSeB@rU9kdkGyALYDW@B0mtdw?QB{6 z3OmsB!I*aR8*+ZT!ZV=aw-$JL@$cj}_z7l~ixho)5NJX$TY<^*ZSNrvT9V4y(=`q{ zd)beRfwYBolRqmjC1ezER7q$d>X4-BfzziYlNMfC7>t)3)3=ldr19qGeV`GsD8wX*oPR5aelX?d@3$j$mQ{W zZiZ^B!4)s5Nrx;T^PP{Fzcy~q=Y3x-Tu*9^N}e)IP3lFvc87}f7ky2WiN zva&9zi`8yhbC(w`1%iad9-%5JpXS2F5#_N(#Yc>gGuzGIw5eC@pyOK9EKGmk8RjVh zhm#^zpR+>&?+b#e5pIA{K`la!(Jw8JZ||t0@EFp>!KC-y?~dvg1@=I62ctWY5S5$u zpK1g*h}zyeILC2Q=593#HlnLUp~~clJXF^x93=z^P}g|+%}^jAfiq>EVsj)v-iM16 zKK%<@;XMs}1&B(}FRTC7b(srAj)c0eUzZvKAR%07BcNl;NJrrP2Co6A2FYo`(|*(Fab^43ONCCvV!-wt;!^UN~IW@ z5&iMLrjHFZW_Ek%GnZ87K17Jmc5{R3_{GmCa1l(&kV;$gfn#K2?rLWD zt4MZ~b`B`Jv${W{;M!VQWI&%4(r{S0sazX1!?BAfHOQ`txn{bb^1!aC5PjK$BsL=x zh(zD~PHl?51ol`%yGjJQ*PlP->UnZIPn24>S~;6c1ZSV7Ie;&Xt{6r=8IxB8eh9(( zpc;i&s8;{EsQ*UUfbOF6qTuKc1uS`sgA4_`Ti zr!(Q)7D~iNg6m@-jIR%v6$^e`;!`YHk5L#!g$~$jagksfa21fx)`2`%C?aLKs~*bY zA>YR2S{5~{Bi0>jS6^Nr?SYN8!Rqrk1qHG}`7G%K^FH@tMN@6#blrM$pOv7~+DAk5 zEj(Q)Tj&~lVp#%BJy0hRY9$1S<>2fsbmiHf>}!jAg#q4wT|-4M{M&DJT<(|27p@bBp9GXl?I2-9P-0gX362DVG$Zy2RKS|9l!p0;j^hT zgnW`A9rl3nQ;rA2Zu@!J{AM?Y|K=p0=fbT;P}Bsns^N5qJTA9VJHn4{*+VYvb#}*M zR^^VN4(+9Fn*IaAoHp`Ku-uB~shqV8Gmbmrc=S9+} zq;|*|NPC)6D8JdpzScwQ_RLBDuuQPox=iLsS!>oi5`?!qVH_{aZls5X#l}!wcM>*7 zYaa#KaMS{PD+F_q$I+{15N_u494D|HDNrYKh`G3H!%2Uw>!XK(7)a7DKjq z-lZJ;^DdXWuv3<`v^H$d`4x+XNpZ*oqiO+leCXbrk#NXSJc@;rnfhgMlD4t&PP8G> z4tlCe{A0vkmn(ui9BNyis@}&>J;?>5!ym1#xAFjY$mA8@qL5e5!lf)D){9hdM~(6G z03}FDljrQAL}333M>Jqxzui_V66v*aasknEs8%i0Rj}vM_fY75v@y1)4x`Lvv(T8)f^$qx+X|-RGsX4IJkT%t#(o=pFx~+Yk5jqJcOI}Z@I^frQ-*Wjo_A@v+w7D))T2CDz?$V#pG3;HvSaFmfQ7*0O^n*h+qGYKdLTsG|ky_=0gk8^4*zQIuK*Z;qqG3mqZ{2y@i5 z6r@K~$PMgNZhO1!rpw5Kz#~i%DVWC5ieuGXni~?*1K+Ls-~HB+h8qe|QI>k-ubL4r zc20SLiz}Kfn43bq1%xFGb=lO9i;8Ds7>*m+Tg|+lqp!4376)n%Ad8J(Q1JK}Z!uQ8 zBOPcC%GKI~x#6`myoIn|gZj%LdD> z_jpo^Ni3U?{rc*PTq&qs+RQwUylCP2dI(j?%oc&8q^r?19R1om);Bx}g7KWx@iw`~ zSbu#*uMu2hA2$QmIz1=ufBK=SFu74IO_lAwc*IO4DfqFUb;rbsl!39Ic&qn9HiG4W zOa&{TeA|_P=e?(optt30UYDkUY#fOt5i2dQoGJMr_NC9y*P!Djil#ly^WHmfZNv>j z9ylRGbB{u1X^>~CSkHxF)T&NVV+aabm|yuyGy^6{s6h)rAKnhKwH(|@jwFfNj2GQ{ z4d@FzZFuInKs94C+^%Pm+8kNgC-?zer8h`$(hY5Uv>{Q9#z}#6jPOfo`_5=>lM$>e zl@Bfau3%|8(xo9TxD&)md3~qH2+& zoXAClax_Nk#P~Ohez6@~CX$1fQ3>)iylA*OmTuqFp)MbAq3E}RbK`Ge?Og>!Jo#Q5 zix=F>Y%o&nHl=(0hkMWX3MMEI%G+#pJOAYg_aat<+c~Ll8o;wmv)QX3`Z6NE&MaMm zh$qRPFT;wG=lNwpAt{_utlkucIrSbtv6Ouw04%tp|Iy1Y_<<;fcPYQqrBBb}gB<4% zBwT)s&bLJ`?w5qiS{lo4jU~#0YYK=XFMC{Y?V2&_dXkvS)i-yixZfn)w~$}^Vbw4* z^5fn0UBElC;^7za(5X?rXy!KEGhAAOmWx`yDZ2Q33z}7YHp?8=jNIfL~#I9`UwP8%Hn>q z%I3d{?ev-v^-NIv!=`g1`zrDtWsk*FMY{bZ(_TH)*Zjp?+aP+TyMXo!u?y5SK zc~QKFDHFiv(X$8;Nsi2pyFP9H{H1QO3&}y#2f}DrVKcafkafp&9t7d}qfyt24v75r zx;^Je=3u|+=qd!eBZj9Id$r)8V_Y6L?>FYxOc=}Xx)~ehS z%7nZYYg9@etQE;HF3BxUy-uM0lJwnoXh-jKzg;~=fxntWyA=EYto{(K`m!|qBjgL= z-RG6#O(8pA-2laww()aSzsez5fW}`>#5k#$&kx%lqc?4mgel|qQ^yzs zuT*F*!L*~QXbVxQ*y`j*Xyy^M08Llob|?zaT(mwYZIjOV9j#6pAvo!?0l~!tfoW(t za$gIra9iUEWU^qS2$G;%ieZ2Xk;K4jl)41U#>$wVHqG3Jv@n`4zN7VWKu07c^{2a! zjeL#zSS{`mUtfqLEg>~DQPDuzKR8K?OOXor%3rEnL=}j#`%NGzWY)X!m2kg#(g+k& zg}sV=N37LMp4^L3BfaAs6cPcvX3Y!@3qDA~scGwZ&kv5>2Z-D0Zku_aL#1 zb*um|c5tPXj})8UjM{v~{>?Fa-yDMs-l>ZOIe2Pg?2E3{oZOj^`|$Iv7+w+G&fOfU zogS*K7rsgT9@sueavA$VpFg@Y^u%F18oc|Db|*+ijm($ZoE!jbS1eaBsgRO6P)E)A zpen^d9jKua6|o@6w3?SIi*>?;w9)Lc5LS zpgb^ats0snP19b=*K*oF=7R*8fkPy>%oDZUX`mQuEzZR0Q98 zl)ZhXi4rdHp~yEu)C5x0rNbA>5oKEML!pWQ9+u%Qh)AHKcGJ6!Iq!Kfy)M5+ZtHFv zMjnGssM33{LI1h94dFn@Exw+2S1!aUf0oM&{Jq-;4h4F9{(~y{;d{|fpux=unoBdJ z+K8jsVbv46fXmvAN)FX0j1`dwK;T-yyatL3f@t)Rx8qst!AG|O3eLay*>1F<`C-Gb z+(qBQQN0#pnteO8JQQW6PTRKK4h}{+fyK)W7ngm_Uknys-Q~^tzAE>!;l3{t5(@~p{?9I`}dcFSts8u$c? zOdHVD?T~|uvx)5Tk6R+30daLChOw@c9+q5m{r2cI@esJtZavA-x`N~@yp;caY_YPc z8g!v@Tt2&c8BV@R!3bdBr}tO&ZV`+kq<|ODFVZ-Jl6`#H(v)Yh&$bUpZ0OY;XhG=< zR7SI+ZNZz?z9I!Lk2uyc;(-*r(UJ{Xma6+xch~f!)%Al*E;^yQgzOxbd<;>mG`BD4 z^<;hnGUkyfCbmCADzMpbZM5Ev&qr|M`k9Uv36_j?}DPP#2Z*o1j~^6OkUmHY<)f0 z9H(IcaQ3 zj8clRHfW8v{T@i*`3KiPh9~kLz-yd|sQy{G~MzuDncC61Mjwc0m{UPte;(~r2-hxfopedqq0 zW?P?46M4l#gO0)d@9Gc7`2YI2-vHW+xMAQ3Sjr6_xb6D&5RHCh zscbJw&{ZX((sRD=zpBhjR;vh)i2B59^dCOE{4Z~xMZq`Z|NHyO=I;Sw)Mxz~Jr#M{ zL0LTR`z(<7K04iBht5Flrh1?mc6Xs{>{^lR4z=STwnL6AN?ifDZ8Cy%$$MgdR=bg| zK{UvPa$gB#g`<6CsXEZ=ccd}xQ-KU6V$uRuU3V-94t~_jp#a^r!1&1mb*K`(>EUQ@ z{72)&jH4^#p4@Tuo&!_jbCVjM2!q3}zw;l-THOf%%0lg%=zV*QGz&Z54W}p{6@JbL zPKCUF>>~GomrXI^beFx$8@uB^>ZcRtI)&HHU9YOfe00^pM(jr=1r+CAF#|tArTiOB zXuW##94Arw+COP)Vh3y{o|?-opMk8VLs1c5Lsss3si z>mD85v#iE$==ceVP~+}}qO%}qQ8cZ28k7xSA3)kn08WE3u3_nnmz=gb8zDsP-T&H8 zZp*-Z@%YJJ^M51-A{#Cc6`7GU{#l83&5~tTwRvNhb=4#S=t|*r-6KME#^i5E2XWT0 z(lg+kEtO)q5aE}L5hL79@C2Wjt`JEx{&L}0BfW|qu8ylq7dbRHmsyYb;V+->rw8Zo z_T(=MN!Yal8;P7ZVM-a^$If-`*u?_JID7Lx(EoMk+@vco)H{B%=)XYcvj^OCcan2H zz*4Ej8*D9;^JyM%37q>ouMtdJeLBkO8ax|Zo+AjX{>EImlb}r+`Iv!$0UQOTpU&Kv z>_PnTD1037Oy%>%yel0M07?<6_YWST?~-lQziMuWs|4Vuq6Zq(b8dGGA3>xNfH3Ge zjdPkf`F^(%p-0PIT?}oT-kNUztTI0 zk8MZ!X2crB=AH5bmCZ9ogC+&_cHQ%vk8M9^>Q7Dcix7EUAal!xGBN_Q8$xi?cnTP< z)f3iP0pR~S0&bv#q2P)a0pO&>bI_@5=jrfryJfo;JyVxzFrxit7D8uf!hh6rYGAngcdGGBLC1KE!sjuyk?|C(>|Hjd9^o`u+q>|<2rlxe%W?=as*AkBDw5>z#; zeKo1#BgS(>@T)P@!FgnOh-H!?6RfxIa@V`&^KlDKS2J;R89e>RDCB$iv0^Hp+9QYI zgh-7lTW#i(1B3An=pGRWg{Ijj-Q<7&5RlJlL3VvNGcs!5lfn!h$Kl60=-3yaHcIv5 zP;*Vc=~DG6XFX!{rIx+=uv&6KHqu^vbZR;JGm36PWLJ}^!$$ z3-Zu8QaYnF;IH+7(K$xJIZ6-*dl}SNxv>l!S}K(1+@O_9s(;FxMjxdlH#4{~vWZ@_~Kwf*=cGeL$r$xXC6lpx|XrF1f- zcjP0w{^vPK)-}AU1)S%Be1P;swJzR=`7!>3;-NT`118o&%I?8Tg7RxjazD zW)w3B53hUWiej?#j|4z%;Wx3Y{}LG=>$O-{hy~3UaDo=1>Ds{vk%w;zqK^Dk4NuON zv6KTk<*^^(ob7>~M=lO#r@q`IIuD*evDne)>AJi}|K(;q zf0r*0@0Wi|c8E4*HqIFa(qx|_os|26{3{S{FXh=&OWw2>jjIK{Mj|7AG$VKy`35fg z)R{m&g+2zP_H9G!2G8K$D2E21bLppoan=qH#p_>?&fI+N9s?Wc-6p_i`kuX+-+2V5 zR?XR=6p>2S2&R;zctJ{a!5Tu*KXPOHX)W8{A?i8Z0tm!cF2?sjxhT zS`MB+UW_lc*iuXg(~Iv^=Wz1+!A-{=Omf@{W!(D@8GVvU6MHf64jwXeG9woOlB#41cvc232|={E0a6(R|7Y7SPKzS&}<2s(L%{ zH6vWRJCgnInvO*f>O_I5gvQ2=@&f-e;i=6AE5A$T!k7gDEDaRfrTe?95Tm#9WM1z9 z_lfn1^8w8Q7^k&?CK0kfmcGfnaCU{+)#X+FTESl`9o1^R6S#Tsr{-$*m&7k9vpM=R ziTi3s>s);x=yC`CcYgPa0(?JrF`NQr$&j4wyN?h;3$LU>U`yYOYcpmK{{i%H+hnZC z174!J0+~1E=($H)74{fE=hD5;?E@I?U8L7+eIfAzO7t&2MH4)S4t-8@J2~R+8VOZk zM?%%$I&mRUT0s1<05Ch9qqcoqLGKf9$226$yWwrqdT-zfpSh_@7&lED+H*slCB6$) zDUubf`9ml`xKZlY)6@&#E(J#8!veEEFk2=*$n8>W6KD_6a%!Ou{veY!hZS8t=Qq5iad`eKsgHXRxFs$IX&wI1l1)((LF^uGIwZV?>`x1Wl}}Qcg>t=X zzPRQ;CgdkPh_Te6cC7U7b5J+O;xS2*Ozd0~NCfpn;u0t+o*CeUh*E%16SLar(2!yxF^=dZc|13A~gI z4+6t$Pj&*316m=@T{kQD%lKlaq#>8*%~$WR{ETTVb?=Vb9Bq;BHwUMu6VGJjLR}9f zohI@k>5*;f>{_<2qZ>9~Rc$h?2(;2r#D636D5wTgbxKFhC@I%=6^MxO^PYER7-q4a zi&8sFSs^1`hbXevcwciV+MpLbdIuifsQ#~%@;iwz*Ch5Ad1rSM(xqP%OUe0f4EiS|a^fx7D00RcLg2_}Ob#YW~Sb$sjbg@0!K#5px z85M;mtt__)(no}2Jca)~m`zO-CHkGTNQ9PCcK%vF1+rp`<&=*h9`j%-54}XAf2j63 zI(_#beC`}$p@(t<)qx`0NwZx(N>dQ=t7N$PqcFy~?t*DgFFaDfWoC5=RyoY!{z0%M zwnFD!`9OZt;ct|APq>j_Bs=jxG@bcBRPX=B?{j9e@B5O8${s~Xm?FY+x}Mjo9us|W z$3X1^c$3*Ec_a`Pkta%S^d17a?(9xeCBv-Y=FroY2hvcg9@yKWz>^~PIsuu#e6jgs zb;H!h41LM|?CG*6hcX6YVtCN>$SIap7P4A)h>N24J_Smp);*F0IdH#FQ8pKG6&AB+ zXhlQ490RH}?HoKgHhl1}%}WuoBtchX6m2ZW28TpKm_1j+{>>j#K;rwa$p*dTZG=Yx<-}UWhfP*6Jse|xD^a(1oqltCvgI~%p zM@!K@g~R>eC^xrD`%L$7&H+AU17%NjeTuqH2A4&vmP7ehy|W<5`|SsIpZYJ>0Zn>D zxlW=9zxmAuJSDJRa@}GgdRnnCjCv!q9idxC>tq z%x~>re!=xg>r2&^GY_8hz{$T}RpJADzW1tMg>_5UE*$hhybJ#A<1W%~F#aAo0~nic z)bfX(3=0uqnqiEInFfRU_6T6eS{dPVK{n~)kFwn1t|uLEoHaW45u4?RD4MEg^-q$R zk=Me=+x9qbD+3dEH_7*K=4u>?z1XOo+xXh7UKx{=w^)}Xjywdu5T^f2M@%!-Gmrc< zgi~6-h+Td9%G;$0Op4{eySfhh7{3bncn!)!KSZO)Em))z2Zs##IJcGj3^+W|gpdw% zkssZSapVvoa>c^!Hx>kk#f?^O$1C$1E{x`iyG6ft`n@v!O&Je#U=Mmy`JZUW zOLN4K3sEa9WcOj1$9v$x!(5R{-}bEwk@r3py>FyU?-dpyy+G3nwwr)qkQ| zSS^D%3!bE(_|z~A;}xn3F&gbbph~+-H9Md5qx`GIxf92>eq)itaB92i^Z@KfU&%F4Q{17qvu=iS_uo zSbFIN!5)2|zjx_I-YoLpao`Le(j^+em%A5(;!mQ36SjX&SP{kC*dGPRc~JLL5p#L^ zblN3|xKn#BaEjCVRyTCn@HtbHEf@iQA~?`(z2tc_o6j2QpicxAxX(Az<|X}Q7V z+Jn6FA-lNs)z3k_`yOD-6^JyTKK|(ECMU9lU~kmB0Dl7IIP$PD5ggkci5^s8+LZ<#1UZ5k!L191fN3mR{Vj|J`ZuN*vi)1;;9-(}Mk_oFBu zABYu3CgK+7V9A>_ir`TPS=J-xg$yGxEND$0tQPA`xd?tt7teG|7vGA?xp?X@>|J+4 z6e!(L*aM`(pz_oW%!QQPRlnPMGJjfzyhP`hZg+d#9N!L~z;oOM%cq^l!ZN<#T7X9T zv7$3?`?)8_qxbCz8{ZPvOQ9T&_Naevcg zkyoQ&H(Czp+63(pm5O!BPLJP8{x2>HvFtI7J(#<10`n%G|9;tt{dP2LU_iEMkeZRuMvfFQ?Vjv+QR+imD|%!}Oxy+dEQ$bN4v z526LqNxtCw=bZ>iAo}Sq9?6nnvVuW;x}2tMBHK-k2I%&W`}O$-=S2wF=%JV5_4Ly8wCzw|^7q@$tg z7lBb%az7m2WG3dPYCjdbWQ+n|KjJ$MsxU`_6{xnz)!QHjfAePscT9L@1lk zgSyu*6!=pAyx3^`N(zNnjSjyS^M$MfC~D%gH1xXN`76AL1O1+K_ue0j-w$q{-=KyL z#jK&|uUV&*uVznkn$(^IZqM6sMf@oOL_MMkTuivsGI+!}8SaP-Sr>t?OXgzI((CC)t!&C3?UP!6D`EBSB6Hwrc) z2bXGlhxd774G37|jz1=(T5YTCryz9y3z$<=n-8sEjyyh&UO#?&;wX7Re-Mx}$XVn> zoYWcEZBx3ge&^wlJx2c+Q3Ii^lgh2>>xL zSs&_WOz(lb(68URmHXkbx3QjT+&l4^tBDg4=OJ4u3V zjxQGyrSaZJr3=92_Ztr#k@x>~7?ap$SPBJ4zRYe1Sxfo@(xU>L8j1UYt{!RUW!t#$ zEoi&c=P-+X^DD|Oo|levj(p!3^s_vMl^8$p?GP8<&M&pIliPmfoGiJarS8gbH9YpZ zR+lIrhhrKg;l1Z0CO|8z@wlfxBMg*d?8vf#-VcBx!~@fsxz}Lu=^lZ6+ee}SsPNH*Xt!l2(WHNFUEI%q3?e6T^>1tz6PiyU-9oOoSAQMbcDP!4*7)UmA@~?nWVhD zqE|WZ^W=9b-%N@U`-}-4n)gL}(Zt!gQ(zhmgplEj?l%oH=48A@*(Ty&n zSH*9QkKTLCeF*y^+j3jAIMM{t+rRw8IGWI(j)551b`LMO2xNc5BGTX)kbRG*P;OW| zh11Yc^hbDd-h?WoE7_{QWudfvaE-c{x$cdNCAnJ3@K%Xu==y35bE!sabpF|ns1i#!@96yB^b|{U;4tv8mH>i zG;ow78UQ$-F2On0Udf$H92)v5^@>wb>Ryc61oy5DA;T;8;_dz}s(ja9xaG6R8AaQH z`nf_^KuJKO__QsCDeSuc#FokwP&2ZZ)EMtIKTk&-an`CRgxhA55&9I>nlBWh0xF8kYfHoi1 zG`5YhZ3!(4_gqsX;FT`&@#bNd14uh5LW+fC(>K*=zwgRW)XdRakD)FhTfbXoSGl!m zDEDIRaW%%-l3}k@o!XFGvH*?-M}Hei2A+G@zHW|Ofr>+JMItTz1n*T@8Dv2mP~5|R zQ%m7S)#5^YiLC|lyM{@W&YPTq$ae1{uG~ZS&OOlpl%=?s8$7puQ6KTtW(t86sM9hF zecm{x&DUcN+R5DgVQO79Sy00Z_iItdM8S*<`Lf<1#Js6UiG$^04I@ZK^g;4Izw6^Ij`XN@waPx2t1pzYr6r<*DY zE^YM!OC)A+6*Zo{{qupMa!6^fjTnD{w180ba`bc6*%x2FfBq|Jj;Nq1 zcIN6%1-C?s+}=)&JG+ymvI`dGdC!1C`9G+U*2qVrthN5EYt88{KZRLvzHr> zH-|NRL+5@mZS=13OZ7%#X^_?TS9(6nUAD8_FOV49^PM77(ZF_vj6sx(X5yP2nj_CK-SlE^zmT@2eB zC&l1R>+dbT`iInb`N7K5zktV-M@7RMe+YrO5!@)--cU(Id+%d*ZujE1BS<;x03ZH_ zT>f0kuM@^#uECRb?>xlwkE*1etn^N7xHb-=B1Fxf<%q90rUK>$5N1%0CBDhKD zk&GEnwLq#j^&!AaF7mzn0F(R$X1M)9TQom_%jMH6XM@F|fV=fVC~2})D;DnJ3Tj48 z7P@i38zw(yoeon)+SwPfM-B_bUK?$!{aa+*v_P#a7NR?-082i!`r_&D2-}WFnD2B2eL4m(n$I4*Tvka4qxOx6MQ|`WFtfhq1^VzvamKtS2fA0Q4nJQc1z9Id2$av*CqbNRmWWFIn}nt-xZ}tL@TNe#8xKNVjR;p%ZJj! z1?$&sR79%V3Wn?UhJrl@+a z4^xK0r&^{p2Y`G90n0%(I~v{)kjFf)9+uLf)=Wss1@1R;Qbl)5^pnwSY05BZ_=lb8 z{9$XlR7}UC`N1aW%OPYH)Hl29fMn*gN~3Mv?Q^>Zd#he%;Em}Qu7W$2*f~j7Kf7M}ornH7l~W6k|Zn2P9||D$#V6qPb!mGP#M|`zv9Q*#2Om$7v{~%Qe-oH5-ql_Av=Xc*M zL=T=$*|En2v~l=&97_KQ!J@6tJ1+PEVr!v4a8nVaWl5)6MSjlDI=`miUeouFuE7;d z9ozV6XvbOeg-MAj0~ZP1^%=_`X$J{6M|NUItk=-7Jb@&RALvvsFu+pQ-pbPM;%+(6 z*g)fmdy+_D?L^xXc%z4yz{WZtXdf4;cSA2}D9UsG2ZVf9VE2akK6QT=i@ zvl*3DMAvrgu`{JSfwYGTNfMt264-d5Ny}EAkEhLX-p_5{qf|LE=^sH1`Og=~9N6(f z0?`>ma=9);5}SPwmpn%&gHPSex*gKAO1gX}EhU;!P%$hjo#YMO=jG@pA&`X8(IARw zg^~)nd7IJm+u+6lgxEC^#K18J-fwmqT^vESX_G;5UIY5)k_c^+1MB9ucm(tYF%D`p zGM=I;i?n;Vs|fY1+i?J#=ADwszk4P|%KhkuPJ@y!E>$ zJOAGcP$k~46OHbey8?GL=m`l4#HH3fI#95wf3n3wz+(m+hANhE5Q3>#P-v&3_Y2N! zb|3Eh@zAy8ZbU-_z)kjdZiWiM@{;7 z5$LvL+|lc?fKh3;ojQ#JNAvg`5f3Nm@cD?Cnz`@G{ohbGZiEnpp^%`gDbSo2!X!8&7$Bk*WdC#r1BG@N$kWj zPIHiw^T$I)#rfn;)9+~^VCQyT?hn14l=HbT3q_}_>#RdFbpHGI-Axzq)_rica%+0- zbHUfp`w{Pil4cQ-+x{}T_wT(3AC?k^Yzn?eaI1>;#8kl*l5ZPo7GOI;+7|LsOpmW3 zt^kxSYh}d;sUt&&qyTR->+WHk&^2V19oXQ51Xq(*53r{RPu{ax1xh01D9E>IO+Olt zML33_geTK2e9na*q(!*{vSi3#aXomt6nkYms6-1N9ikcpQ($U{HwfdWvr*G!_AjaT z`5LTs1yytfYar>^n@(JCxnm6MAal?v$H7erW4$j&pAuAin|(1LjrZq|jpBvex;>-R zr%zd5Q@rXV7RrT!2|#FRAUC}N7rxWjt9xo94l1fgF`|0_MUKBg<6ir5~Nx?ii6b_WF9d{UMxZ7_DG|f15eR;C^~W zZI|pOVMqwifpK_XeQ!|B*IHD+y>dJgSNdeRV&?<*lKBf=e756I$WI8FwlSL_Eo1oZ zasiu9J|*#aUxN+a{r_;5R0Ci>kyC z^5%n>LR~9WuJd=@u`=|o9UYq4&pHC%#f_oAoL<{>!d<+gP71By2pw(X4bU7V&0!;y zr<`d`6H1;SAwR6k!S1w=vJ5xPV@suAGhUSzQ?It8))HB zVmuQ72VaOZeTls4<;$N(-kWp2v#?|zPNzp)ojx!3Ksm`MEhFW8^ByxVzWG+=$(jrEgi^Cld^>`f}u@9^+0Yps2r-_M+?~EH>Zpfs9TEDt_9t!U$6EpX_scy|E#DbGa zHWuDo5WTQC>n97^p~F8=wuTARFP(Iq5g}uMT=^qI~u#+fhUJLSM)ykZc==vqX{X-kKPwk%~C zdYTAi*o>Aj8zA2s`O&aY0x02_j(t^m$WExeA*k}4KUARkCrKN-k}e6^{eDkd%iA~9 zAcTos(GG#8b6BNUU?~BGc`HRWN!G*A@En$6v~cm&a%&?3(Yb*-t=kdJD_K2_ZX?p` zr&=5_bTRU~czs1s_TY#-uiVd{23~I+nd!=Bf_J4fKc*^SxRO!hJwx78M?_Qa{TYEJ z87HJb>Wk|(!jWpgC2_FTSXhVtfE%;zgYHPYGlQCNlv)!i-o#~PxC47jUweTvCLQ^1 zq3$fyY({>8Z#+)k7Qz4F9b|@P-ST^!FndlA)AJBWE?WK5z|n<)=y8fRmKMgC8p%_sL=4CmGYe=-~IQnm3r?Z3>3P)2_rE%6*wd`5D>+HBnUf) z7YCP_8@)rCt3eD@)jDoYQ#x&FJb$87fEi`Z7R+f_a^pkX-L5rLwJ~l8X7)yHi!P&M zQKk}4fySz_v~7&D?*y+igZMB)Y(c?8n+k5JC+N~9EzY1+aClU|Sc3dmEbazIS9p*V zz{#HjhN6Mr1(i#HX9x=Z4BSO@5Htd1!-aDfng>d6PHl8vOFl0oi>>aKnCzWEgZH*z z%3=EMQYl~oNiq8lllPyp?8&zkB!s|l+SwP{4?!Cj z$Uz)8=6adWULj8H9hq_9U#}?bek%1+->SkFSB}M7uX3I0AVPebr(B<8<#n?ZE0=-| z%CVR%juHdHaDB28w$%EKbPo!fm`?Q zOJF5;pxy)ZNx=#ofwX52E>T;tN#31e|INHa-0op?tH3C`iuX(1AwEb`kl2R%C_uRq zDYLl$58UjPf${c*@g4cx#*MCxwI3Y_`eT>2dHISE1kY=iHPxyq(QA3c9|&k9hrF z7L8Y_=%3})+YWyUx;J43L{=+cgS6GLQ_EiiK$*n?HU1fxayPwir0SRM^oGf17DHB)UpYZC#lMRwygT@o0 zke+Az^GqoY+@x5Z^Nj>RNo_J@ZH~VRi}&v`4BVPM$GTdxTrl18`uY`UFif8K_I2S7 zTtI$3U5tm~gc&?({q+4n>~0=Ic?r0K2yiO!n5`Fjh%X>tf*FAeG4y1Vg6Z8YW zzPZy^jyg~e2Y$+D6g9P`vp0gMCq+5fbmr5@gXF7W%b?;XV{=;1Hwh5w-clpmC%w|m(~ z+$HB9$w8Oz$5W|K9AUE!U^Y)(Hahs|`WaOmX9Xg!uEQQV^+Xp~;(_j|Am3||Wi5JX zh%gkri|kgAVmjMEQql?t@-hf6VY}W1M*+vUINF*y7|Z-}mWc9S`K14o8nVCRm<=jw z?qF^QnYi8-J9Q4`WJVCE~#VcGk_kYA1gt-`N8)AZl3JT>~-t^IQI!4xvZ>$BV8u< zR=!A$HR(nB3fb7!imr&tsFf&h&?*7#z(lYiDM~hA%Tq(rKM=S*w52&egx}&ZL(Fck z_ih1Au>>>Egy+vLRsX^avL3f^E`jtDQx~lR?f-#O-FX*wVJ24<_^>(a7lOs_&wikT zaiha$yDhtL5+q*_*hR$LOcmh*z0x>v-ki;AP4B`4cz>kcIy&z@%t5{wAsNdcf`4yXBKl(VTl4wGQ8&%1dSS;Px-Y>t^b&OKFGx=eF}gM81xt^K6(BO6F@+;)yl#3hZxU}gx{z3wn8t1_U>YvXoj;$ z0pRVYt0b584o=iP&^kiL-%Oz}&vY^4#w>q?bRTMfYK6ewvA^;8;A~4ojc?w(^y}#@ zx$VdeVFMuO#9Fg=Wu(B}?|F8BrIL{degSk$3#*CjAJx32pHzs(?VR~bDCeiYs8!Ga zao8R`N*^u=48Mfb_>kvd*1i87Y&%w=U!a|KU5#=rH6t5 z<{pT6U1X}R@}xE_F#vvN*&8VZecd~3FJh~NA$kR6AQH96@-xC#+$J!ZdW;j0PEj9heb>-}(%h|qbY zE>4ULExWT6Ddz@&DF4I?8$G?P4o%)flL>Ek?OqCfxt$IZ5@v4ItH69*@BS@8+FtY- z98<~@@%aY7^^-ylt3GN8eAo6L)=ZFStUGXy-I_PKfPSSB_5Fy+um8Iy$ z$#BVyQq)7isTtPwU|wK~so2TIz`&qWQf0MnFK>?>RM<}l)2Kui3YGqYx%90tH-r@D zds?Y}Tw#ft{l8}A$ptg0-RixazrbVH?rfVJaSg9k5q)?lh=-S;PjK4MCs89cJk{=*>Ug1G;eWLRM6RoiULPCJp?8@;b zuf}}~k^@>0CjyH+udT%}jOmXLcOI5r-}S7_tD&TZHdWVI_xE+}P3OD`y?9=i1!XQ; zzvOaI&@Sj4gGdBl!-EVZZvpd#k?ehFQ@Qa78ubADJQrdoimdN7-@0XzLK~pWOJL`Z z*;54XJSSQx>vG-7r$i|Fzj)OKQPy(n4sCQdR{ucGoF{%DZ)5~Xn5kMPpLgWx=i*Og z?}xy}(tSfpI~ZMIpu;a)gT%Tuxzj~fhVpyaPX!W`HU40vR%`CI;?b~{=nbNstq&o8 zbEFyf#s8qroFve*agd7#QlRsrLu{I=#CH+%69|HicB1B068}mb$R~(aa(ADLv#jVB z@@{?c`0+6B{xcb0>72@=P&1i}SqsTo7x;Oze9M1goxi-EJ8S(oFkcR25w!EZ>vcy2b^zsb>L0#j(NCqtp90RI=||LrtVO_)a#oiNtE|hZ1)3 zQa*{%e~Vy?bTr9VCwH0?jE)siB`DB)rEg*Un_IGit9x1g5~5I-1EEJ&z-H-cUY>vr zp?C0wHKDfK>wBzD;P|PK9oT+eQj&V-W{1z+`Wo5QwR_pbeSIVscz$u@1RAK!&KOHL z_ve1!%#ny{Olvj&rCl7qyuN;h;mVuz+gG&q$>Nsxf;-o>%)_9EXFgsC$KanK;{tAJkh>ujQVGU{AcSZv z|5|(3%+>P;F^pLdgyBnKJw-k{9#NL|m~zo3N)hFhBW>LQIdNN4m zMi2a{nBjk1Z*@ZBO&;Br9PU#QqZ*Ldho!mtWIV)4Mo&MF^w z?%_4ktK)C_CEl6Pgofy=TY8*8rXnQ|7x{+YV9S_3;MBg7ETH1ZdcRAZEWCQXHv&4k z$mCY9vx6uLbwN8o2jm-CSzhjXU8hBJ@cHSV4;1M?ul|&V+KL2@;%|t^lFkZ5Kl2V<7B8ovof)PLkFaFgX|v`X8>3KR!S3{x7(5<<}DE z*jEXe5M`;#uc>Pv)$vi@rcFMk*@BC_Hof?!OXsyqug1CJf#J!4jMh-1CJG? zARZ}%KT`6R=6v;}Y34Vveuw9%27OhXp;*ZpX zx)uey#1R|pX{L@A*Vtk5MGixqyArip2-j`$Bzl*}k7Z1J6eCJDWh_RT+6P=4)K7ul zi6g^W*F+H6rRR^Vev|B3LruI%R!hB?wL~~RK`we4Rk(2LJs7CdD<2(QSy}jxNSuW| zhgh+FbTOOnD|Ize;2ZR6a$ag8Y zqWMC|5>I7O|LVq>cPk4Ljax&@Tfz^}dxwETq0l^ZtG6y<5-&9Z#i8LBp@c~^)m6}5 zm7~ui%P`L$>hIlui`^5%Gd(^DAm=vMgpgC{(Qx1#bg(^tyD9>XlKQT1rhjL0^AZ!+ zMBxAqF!eo=Wi$LQhp?_^iY0R5#I&E|0$|&LR7k~Fe+ExajagD&oSff7XTV~X;7&L+ zlk$#TQzo*i(-;RuT3G)?|8l#hGY4Aggv9_Ug3#4RO3RDYmiK3exst5Fs?N9{>_x8<$yu#R zbB35Ik<`R&qbi5@pui$SGyBh8!Z997#yc_86v9IOb!9^&eHYv z>$NV0^MOy`3V#adZy><=da}PTMVEEpo^$OA^}s7Svccs;rC$Y7>h6aDef3plT(rzL z?rWb~+Dlk>eZB%YEfMTTJmu;P|HxgA09y(uf&hMShToP;@6+2i;QFH%)Q{|MhKLiQ zoPK_y7yn6r=6cmR98+#GGDvVsbW>{xLGb-g&LZ*P5i-(NVah*Iz(|>g3YO zCJ9TBI|ahz?_rnS$d6+g20;BGX3|6FZ7whsA>!cQ-Iw#kKsvDX)#?D_Cu+r`LhnLG z-*s;7zOm0kaJ!R;?eFdr6!u}3;5Cp4lFzOBMhus`hSwcYDY#7Ny*;Oz_8{*b8R+HR z-1Zl|eVI;61&s&k?@Fm}$B&e8x7Nr7jD3G}|ILiw@t{S6ZVZu3VgLJ*yum>%xFF-f zV_N?WU%>T&KVA@Wc8;|Xa)MWXu^wC`Sn3d_t<}@)(YhVE#C8x?bSvq z*cr0i;MFMfbU;z@}-jvnnq0Qro)bM0=8TMJFwdhzQlJ=`;F=cGFSfS z5g0<&3Vgk-QZJ9lCZKDESX&RIiw9AP)E_3$pJrXo6Ay#v&FFVMV9-=?Fgr?v z{2ko*?-W@V!_Pn$Hs|N(JyGWCmtn15EgfPAk!!Nw5`inuE0Is5cm0gat%bp-78w~Z z2WFTT?WPEm;SK(_LOllOwTRd2UWe<4tY{s7rMt~*zb}4f?dtg<@I55PHKUW7s+wrc zzEaooP_^#(2}GYI1ofC7tde)awwloP@R;XA2E2$bGC1-r2-GWWUonX9;~1$VXrBpj zHK2MaQFHS^B6|yqybNbbz6Jghp*d<5Hg|3ed%-fcAM2IoShHhu{`G&{|Dqf-~nN#2B|FPg5rKD?2NQg?*Y9mYN4*6_UUScX;xLDEgEgvpPma)mj~+# z`fSmUY-Q{GMweY*_HAM5pBrsh8V^^$er}v)pTQdhR;mRxq>>J_mXFBW6 zM~#d-edQ6^d53<7uIz87S~;7@UHTrL<0`uu?R-h_VS@k`Xp!8z{1xOJ$t_L4DTava zgZqR!ojlCp?X;%J3GWW~tYS#Jp+cHDV&!3rAH7+n_vdR4da?oBT(}zVf~|7_n>4=z z+B`j}sa@uj`ST0U_8Ek!zY^|w#X`>%gMV&r8vNRM73`xQ#3JQ-lr@wyZHv744VdOz z!sVDR#xFP4SCX(v0A`e<_FJl@2I3eXeT!`w>vg>NfnfFgWkj?-e4)3ltyL9d3rWSn zfPgWOVYEWv?dO&_V8fSsNi->s`DS3iVP9^}w&rHTaw?y#q{P zuUgn?Z}>NqmE<~dum2oT^@ikXkE?(ysLO6`6<{ZZ%ObO5AAUHu%=|9V^RqNH_dxK4 zS?Wlgtw01HrsTIKG6OsPfNWnw%SuMT-;|+>q#5wp^6sN;j5HC;YW6=Ah4asEW0;L; zy6wLyI>ViY$vvu~NwL0v>T7Uyl#50*)|^oAGNS{ES5hdQ7M44JZA4MUh^V zKChGi3ha{HF_qx4kr%NVA|M_inG-+!BA9O{&qRN7A#pT%VzYrOgPjpqh4;j=x3P~kaC3P3erGDoLSy@{v_Ng4i( z_X*OezuUA*)?h#2(@v{g^zY`?#RXhOa!Q);6h)hvTt_U|rhk$|PsvhY2yW!e`56oF z=Z(iS$t`*Fm*hdMX#=ijc#_cF0Cv_82dct z-j)~w=s&<$Ch7aJJvf4xqM~_VNAZq^EM;K*h|3lz&LF-p%R(vsxP&6TvyS(7kbh9wr*@pZ6S&A^}Xs80M zUoR{mc>o|2`C(Ek>|Nq<5Rwrv$9gt8%{8_$`4N zyNr0hW2o)Zw=#fs6-SQtE5V>N%qMgGP%rxBJL>m$;`+XSDWdn>B3vz{DDwWe_TM*l z4;$ervS6O5k#7fN5Y+zaKBsu4<6=9y;)u%Fy&Ag^vw8-j_(GIIU{~B6ddj^rqB74= z-b;@d_iv_4&En)xC8_Rh=a>2&d@~|vSW$9_S)bBX;C_C;o5RqFyOPK=UfMoM*ShWA z*33$7gHle|y2u&qgC;5mcIXmbDl^iOKi=WFLmJe?^cX_fj~rg1O=Vl+ry>t&TXK=_ z?;?xtg<84MSZmOUoJ)YnhZu)9&{1wuN7SKVX6P(3g!SF4iV0XJ%`-pa=r{Q1?*Hmt9}4l`c;NkX zUGIbgeMk7iO-`@8!_dE%V$z^J>y#BL8zJ(6PKK>CY6?_*mB}gKKJL$Pa`0E(uj*kv z|13YbWUy z)h4Of69=-3r$!#3zozO;vd3_7JLs}5%%#7Wqb=b+$&Y5M!p|D$2`*d>LX%%MtQlP4+}`f|`0$7@+xAJv;g{_`dX z73OOa4KZJSl8#r{g{`Xy&hI6$PxB#j#K>iR9m zjo2#aEO*X9^uYu#Q`UxIFRNu+azAMg_h))5>{2^d!7jA(wLZ-SCuWF#%i%STYlv zum1s@#gT1jsXqOb`ls#VZ!UwY@nv7+)ZHlq!bt4B&N7=+8KSJSGN=f`TjR@HZhZ~H zEseDQ^|5_ktj}C;8ujgF7iY63OMDL4p@N~yjjw9;=ZPaJo{b|V4)rD`|u`88q|A)LU$c`#A6&UUK zTfCN#t{VB#_G%q79CF$bOM85ROs&QsWaRS^o1I-w!S~30@6Yw@I%pF3^%Cv|k1X6~ z(6$*F@;{)$wZxxlP5_)OjnF&uL|qZxj!nA6YgGj-N8?YjgAUl94$p$d_I$mV`|pt; z@i3-%O`(dVy*VcZ__>Jwrdv@l$d>X;wqG(FC&4Myo#ab9!rlL5$WdO$A6)(~D3p^~ zJb>hmz&YpXa^@_-5SIQobeugt`t#ybKeZ8)Nk&-zxeJzwQT4Ag=NncOc&leHgPV>? z@V65(L6=l{Y##>CmAU;qs3)y%NBoQS>1hJ#d0q6v4!o@*IY09;pZ5#InHQS;IClWj ze~0GW2a1#?cO+d~p1$-DT9&j5n`^dxi{9{L4bG!>!A3Y>Wj7G*L zi!|r1)l_+?3Lzv4)QV>a)PSCexevnH8>Z5ZFIk0r8^Xfy@D$Y$JzED@bU6!PPlS{4 zM4bbm0QOV{u^>1H((Wi#Jj6%819X&Nhj?n|x_{~X$X#_h`_Oo~7~SBR1UP+_o3Tzk z4WPz!2njI$_7wa}-mgn6)gZrgs6QvZ26`b1`RD64V3ND6H_|gJwkx#Uj1@EJp)o%v zgnU0zlIq^1Km>F%5G@NjQaPEhz?&voU(+rEtKE!#Zm5N;rSC#(7iZMb*n(|i-B_+~ zq)qvK-z{9X^s5rv#?G3as+UmfNr_sV?mP#2fDwcflI0+yd->`AHYE*sz0Pq51PjiZ z0QVpK0o4XRlb%^i5KG}o`Dgx;^Lh!piD0;r6b{X;c9zaXbP(|S3@S&RU;wk)`^P>D08sq77>e^prY>Ogzl^j*)iM3+wN{DxLy!Uoc;uarqI zJ1IE{ia-WLK+ywwqNjS;O?xTto;(L`qPFgoH@EYQe~W5ZPG9*bL)AE&>N&j#J$lzs zeWpQ?eh9>1wY>-oS&cP15InT-GW^}%P|t!yCtIpDCg37ss%4=vwO&SQ$aGNP z6JJ&kg2iq>G3ffM7Dt0E$fanG#^u?P)-s51m$oP0sfMwe3g5U6#kqAg)p6EnJ zu5RrjM_$sN6mC;szS{YQDx^#P<^8vcEf3?78_P4XANGh*gpf<3EAoKhsGH7fJF*X~ z*gUg0{0uX$u!{RWjK~o<4QA$CJ&Itfxl+iSIVg&8&_{f#&@OQk|8^Ex~rD zYf2U-w}Y_^xbKt>t3{o+q!uGNKXe#+6$3Lww`?-y7`t+}iif&$rg1&`+mXxqzEHn8 zq?iBhi0Td`n_XAB1?*>aLqb1PyScp%!B7IBjy*6ST_hA0@q@U9X(xFRH1fRvhV}vK z*6Qqy`wHZGhuv4#Z?8vUk7KcX#{Y=H;HOA063sCx)AK=lB~?t3 zDtQZ%q|XwO^QQxp$GeMe!>x6=`f4s`*i8cK!o_~ z@r`CY-qISIuGTK(wU7Yp%OoiKqs+{X8Sou>-3Crb} zUu{Z@VAU1Z?r@pY5Cik!yLk}t$$46(XWV0c0u#pvb^v0b7$^b}7g?HJq5>pm9YI@F(VCsqtxorPvfdTzRg^CX1IKtiezsh< zU|OTc6bLWbU5oiPd%q%}L(}Q%@wLuVaZ`A;E8yH<_6~}V@AlmY+-rAI9aFoL7*yBC zPdL5e1#ko+`B!!R>*p3bw^QT?#~7%`S5Q-Off^xv!X4PKoYv9#L%g&|O$Ux-u>jVD z0rk!l@)KfQT&UQ6Z@)M&iof(YU4aI*vrth^Mt5v37`6MC#~x3VFKPbl$$&Q^;KW+}1PiF1r@!)AGEAYSWjd|MyG<^`ON+HCyd|Is&3n-e!TSer`?eiGd{{iJA+sGogo|m^ z6{=YGqiX|lkq4l#7b0{%)&fhF%O?kIHH7Ag^UR~3KNL0!z|clfej$2B&EAJHD7TOO z_tDMd;YB(i;L8#6(OrYc$4m3s&U6Niy#;{VAMgTNW&Nf?^av$(PrS9Ad{589=(NxN zdJ{%W)2BCt9rMomU}d9Bn~!vb&GF_VW9-q^w+nOQx#0b3TCEEdQ_N2_>AovypRote zpsXyV)rvn-BIHq=0t__oZa*hp8-f}947oq(F8s-ij?5~4;OM}QWBk(<|1Q4o*47RQ zPMIq=9lm^@40%Hg<1ZE>9DqjkB1JqZT~K1@bEKp?o;jheO0UU+=&di~rCiePrDfLK z05qYHF5(Q>d;>Y(ONO=BSOX_!G!IB-vUe#4p2M9RI|FsVx^l&axd`s;nYsXw>0L(>1X|kAy2b4jBd9}%g(92k6-6otRXsIg($cXmM-(6hb~>dKb`}yzQ3^$250=D6AOSy%A78!?K>YLwYXoP`<@bVzg8! zorZj0Yxy0qks8mw-)o{6uEpoGCZ^8nCaC4FI1I+2Z^CY`gb@|eI2qZ+P^U#p!wsOu)hmZ?`B9IIwm;J7m7T`VQ9R9=aCS-qv%i|-&T@c&IaS*M5&+l zgkZUHt6k(??$t*VWIh~F{c{7({6VUf5L^7cKY`bE_wkL+B&zmHv905>8X3+Qw58% z*QBJ`9(zvvL&M$D^s{$zUA9{)4ZjW5BM@Tw>h?nkAdu@adW)rfnx?JB*{oQ?sdbyu zcPZEz(rOgh-#scdftpvqD`mpuk;KKP0pQ4pQC1z_aZ+K;VbEL<99=~@1&_W2Vq6Xi z5c`R^AJ_cy=%!)f-ixp93*Rv}TvAG15hMrMp}z)?!$?AtS5f8$D!mgT-VtD2yznX3 zIqh^t+qZ?}_<-fbBJNOtJ1h^XnQ0Z5$I~ol*peyNnx;-wzeiddTtVzePSS z@Qe*n0RmnY=pG|vaY&1}nsSrAK9}(Zl~T#~A(g6~>HT2( zGi6h3O>>(thc2-Sd(uHHo>A8TpE?jPA31VO9T}5> zk@Kk>5{27MBb0gg(e;hVpZw3>Q9XL&vJE7q&EJ3JYqr{#y6}30@!{@WSoo#87|0u_ttsLRa|_39R6cSO>?VdJPYo&VH{AJM3y3=@gmyxq8-CA&eBZ(VwNNqe-QEjw0v zTRY}OkIM#wmt}Z4ULBM|16Ag@W|zmK@XX0U@rocrK;>ySeqlkt1M|(MY2vBT(3^|(pfZFJ1Ya!YI|GlZ4<#Xx5714`@Nw5*vd*LVB~0aSse zN-nUUEL3_m(jDV|%Gs^+(TCq7gJ+<574z1-zOwa=iMVd)PDQ>2*u@6Y*HUx?QR^%6 zwjCRc{^5@eK(urzhPD^M96YXdTn59AhSK^bwNU43G(za?J1c!Gmuia9PeH=HJW)27 zl`ui)ru+F|VzyR4d%3-SfbdU$*pTIlaTWp+aP+@gZVY2Zv#;q%nRKQqRAe&bwb5N{ z*|ENt!Z^A|_ZEk4Tm45b27`SPS(ayzN7qHa(93BHoPI%SWz$vb_H~BsrcdUxC zaPjlx{GnjoUZ|kc?mAcbw+{ir$&S$SGyh@N-N=ToaFbwVaX4Mq9BDYC>Rta*6?$1I zj*%7fEt&mJ2Wm2s$cBVDn8o;Wd^oDUxi=ft=&hscTh%n4-gm7yvjn0jV}=B`YiIAK zsC_hC``Yw#<5>39^PuAWAT)3+XZ7Q$-sPKI>)$!Sd8AKGMVs&e%neL>)vj&LSjOop zg+yW&6YmTC7_e&;BwwYZqkfxSjcC?Gwrm)hMP&p6-yxzH`tlwd=B{bT!r^WKNl0|| zCD^|@J&ykL{8;4mnwfCkPG4g=Zda`51J)xv*@?gTZ=l7!V9nvmDrnul96cN^hG_0n zM1qyiCN&YyZl!l0JxMWqHC0)&uY+=hGo{lmwK@9C1Fro@fi|a%$uomqFXZYGu&YW7 zAnJ&kh8dnL=)>E-R96gLsz>Kt)CEb3TJYzkmvU7NwVnl)v|~nEzg!=@I0bY);wdNi zdf%S092RD9FLo-@4<6Ye4XSL+u&M_TE~W?%{9eH#QNsCo@qbd|xOp`5gfC z&f5Lk3JU40o(Pijj9*muz}%lb8UO79y()HcME}_n1@ERdB7XZ>vR(@T3veAI8#qTU zE66Pj{n&-k;xKRk)X&M{hPQx>$kRYE@Cbyeg4G;h*lD5n70R2NX_&GBYOzI9K4muOEby-GkUg@9Z;1vD^Z9*OXkPkx3V zv=gV}WnvN`2^!GLydV$Yd-Rr}y3*`uRKv}`r2DALjI<=HjM5RwQ7g4^qx}S#v3AZa zuIp0G48lqEs19o+*7MS(6;aVoH>T{mWb#73W}dBi=54Ct7yh5$EVFaH)_msV13aSz z?lgL^)8yq6F&tRIjAv!g?~ETsc@*<5`zM~gk9`XiZt0xd*WQtUKEb?`zPXF@W3rON~F4)Wrk`>s; z%`d8x=!}`yOIRG9c)Sbwz-bUujzJW@$43BDqp(fU{>^7ib(7;b&73-$b73NItr~X_ z%t?#sj}RpvW!n0t5x}m)%M)_odX6mD=|%oxlSaTu;Q zeHQ!I2tHStN4%AT0MIZhf!hO?o2AflQJ@Z7&V)fXAd7FCnHv-bV9%Pnu?lXXD!-~L zjxQWo8w}tpa&2*PgFco}JT%EIHV}2IaC+k1q58MBn1Y*-!ybTx9F(-;D&BC?EucGi z9$}0dD|^R zMQE{P7fTEnP=a6+{nHpXpmag;a0F}x3%-AN4R7&LoZ7Y5K<d;kR^R50V zvypz|AyQ}L2?Y>*Rlj{R2O-bbC5v51$rj&bIkvV zgnbUdr95Q|Q69(PayedVeEbJx{||i7y}O-BT-;AC$G<%ligZE7>X^#w&~YHSakQSI zSVhuaJhC{k9h4;dXQ;tgp&8Yeeshb1CJOpl&gUK+U_LS4O0dF{qv=HX?QpmFrw)jy zVQMf0Xx#Qf01sFiv)XgR#ZlimbdBkxNtix{nC>+6KD0kHc!O2 zYz24>U!9#Ta;x$ZfyqGd)67pi{WG&RHAP{<+*8jB4}j8|eun|!R(?FAh ziLTD1K$mlPWkn`4gP!L#KmshWL|A0WC>a1<4Izmc6GuZ zZ7Dhj9VoF_iG2}56g&2Jfap6qa=GJX+#A2`S4O|>l$#j1W2^^O=-cgA4UbGYlE;X4 zr!eioZ51h@fXqQ-GsfIcM+U&Jb&&3=Y2bhX#X;w%Ywn{miJ4Ac&>eCyZ}dP1CTcfr z$y~Q~=L_~NEF|9d3K%!gu7C;qCqM2*Z6PuQ)X{fr{l}i~m``8+%02+`O(#M(pY`j$ zLJK_!K^!-0b+yTZzb3M_qhL~h%iErpSV2Uf4E!X3xUA71@2$Y@x0vznT?{PZv0Ej! zI_m5G1cQUv?Zoq693q^CIkt2zlp{I{{ zSTuHi{?!-UJoUPl{#Mn}`Bawg%)* z<{*2SJo!^Q0coitHzC`Dgpj!DNPVVhMpriL`EJ4;4mB*!nQS1~2%qlO6s};|1~dM6 z-2wA|?Oburd-xI)X7|o5&qc*Vj1f68w;rx!j}3;xdV4^Xyd*B34b2MqxZSTshM*!| zoP{#L->N=w>79s8`?0f%@g~v!dlOvSifR?&LNLjf*>GP+{`s1Koyt=WzRhJ-`>m!I zl`y}_E+p|4{l>IZ|A1zcfRX$9QH`EC4=w1ayb#XJ`|$yczo8(xEed$oh>HbA(VxFC z$Bh3OK$n8hb_6Qivt4J(4v^q_O((pvLlypk?~=}+)^@u<>F;;c&TIz`XPyM&zcmmB zMM5@5BSfq!_sYLZdDG~W86P6Beo#Nuv*sb|q&Bo(X-K^G<_pX?rE2<%3*YMs3i(;* zWI@(1Gz_*E7C`v>-fXLU|LZi&Pv>tTy6>aqIsMa2vf=x+$XLWWMW6nM&|^ zzg4{u2mso@%e}R!49d5;i;ph#tj%~ez#T8VJybys^1wX&XJT)zwJaju2)PEodx>1I zet(3YonpF&-xgHET;hrsXb05{ejxWmxn!a9UDU?M=-BxOh~Dkf8Fe0_2Bs4mJ8hxF zj-N>r{|0<^u_XRK3y}6zr}tr86ZW(ysgk!cDIbp*gPTVo3;57oEB0WVou*y}xuxMi z?SHQ!M1nehf1!bQ5r5UY&`gBv}O%) zt0D>=z_{n*VwK&TE7H&kdT+4Lgew{T5Myl(W52M5Ik}cwZ6rqx5L#s&-B?dbnwDNs=yFQtka6KoP+czhg;m ztg{yM4br@_Oz>GHWbWHFjFKY>K#}RcTazjt^9!zmgk*ur?+ecrBR5ZfP%akQ1;#*# z2;nc{d;NXWaeB6iE-(gBjKyFQ_=*V=r^jTZ;AmCHBYvx#ViG^rZ0;_ge2iGLNAvYw zG&fD4&xRDzez2|y)87oqXGqc3>(iMdFMzQuImm$Y7NXgZ?r+{I-Rx?7@5o@)Rsr?# zR5=LehZA{-Q2|m%U@5lrJDwC7Y0ga})fm0PI4hE0D`lpJWAGXXnFkR9>P9-8hZ0~G ziR_@m!pY#RDTe1Afc1fQE{9Kayt9TnMzuxlZJ?<2g$tKiUJqEEYL~y>v#~XKHsF*c zO?UV^^7=TaRD>$^>NJ3C;ePwrq;nfl2mbJqrDL-_T=jA5-(CBkeHmon$+}rBiz#nF zGd59@-#Gf*_YH%XD%e^fcFBOw&)iu~%G(Z(5JG&Ae{9l8r%P=X`0_3E^%7cKlO+QV zA3So`e8-vaeOC?^#3}r1RPmZ8>TjXny^Pj5-cpWjp0#+Dba>S3gXaSplsNQ}i#}8* zOw|Skylht;-G{sV86KjJ;t7RSw@z%-8s1qD`c2FUU?NJ$?Y+xtxTspwjkX^;_OVB* zj$P0Tu{^mdmO20?@zfb8l}hyYJK-uB;?e{h(L0REB5e=`eNxfl)e6!-PKh~8-J#x8 z=0FO><%lTMe8U_F477Z4!ryqd1Uv7hg)HHSMi-Q|p5}{@y?ud?%0-ByU4wq-#@gXs zK)_=~q4P_o*JoMCU}RI153ywr97$<=ymEsxIp7;TwY|5NE%cuaZ`58Im%Z-vQ4(vsR?1bzQ@FaUEY{`YU5AH+*meiL!G<4O1hnL%AkP)GtHg@?}UIHW8vS+zY^ zs9s>bhZdyk&&#liV-pf=c4m8KIv6~@!j`<=^#rgkMU;&R2b;D1Yz=B6;e9K^FB-SBAkZy2I|sh(Y|l z^4)>I;E#J9x8>Cvr0NI09>cgEvBFMoh$A18S^bOE6y=G8G9)#ZiJlz=s@V4X=oNLg zm1p^72)E!UD7s=m2!h^JUmZP-I9|alt0A7hJIa>O5<84|%EX96c*bC35ycy|`|&F+ zW{^sj^$ z*k_ws9z!BH81-I|+W8HA0}o+fB@Wa$$k`?!aTS9jDRf8IR{7hvHgz9##nD9Ki5f4A z1RM2)`B=RZn0L6>-Wb|q&A#oxT}JAx53bTAy1_T^fgi>YU5ZX5VKgKzjJ)#bWbtvS zQnkyj{s-DN&@p#<3DN51MBmgA8*mL~fAOVpv>o=LSCshuU)u%0U7v=@h+YunY)kza zd%r8^dx>cXo=-0rYi~SnEdz9L>E^6HC3~AF0$h`~h(lj@XKOrui3$X3Klma# zf@n=j%pHwX*HvlxV)hcbH00L5wt52x3Q&JM^^!tzypArj=-=FIiYStOdXW|9Y$;E<^a#Hh!o6#ys;b8O9VQnoZJZYOcVEj!KiizmPn|w%{8Is+W@zL z8v8ss2|bmg+N&6IjGJ|F(}jY*D3Vl5`VBnO`b7+RLyldAj_`*3q^r`Q(rr~lHH`Q7 z#&GREXaEb||MwY$G=u_aP!O}()`c31X$WX>S*P_Z0UaqxX@o;lKnulkKeXMy8x0)k za#OfkvUTBN3q(jj=~(*FSRXh^^ zz}gl%2YyGq*r}BZ*uwkdn?kgFx;z$PTa6(K1 zx8*7Q1Y&)W>SeN;gzV(wUU4H~K#Pg_r#VIR_iimT)kc%(7^L5$ktLIX+3%Y+Gty4; z6O>E7Lm0r&_t4Lx?Bg>d^zH(19!Ls#oN&I2m@g3ZX?jSaSK()({o)__%Fh-sL7zMd zmTQ}Zkr_*NFSEz%y(;IXGnDG1V!ctWREZ;z9qS)S!FOn=ZR-FRDHLi; z2h#hH;eGG+YQ=+3%n>t&1-^N+yj_ zNN0=GClV?_J%gB!a0(*6Ml}`A&`l{uhAy%nH)*B^Ze87>j`V?;D^UAXjG!3r0R$HX zjc@}3QrD4;{@DL*3)-<3tRv_`sCa)Uv5^n?*^npNYA6Vr9HkQXARCDGmBh`q>?pb# z`@|kqGvYP)zU4?}yt~sDPj#|t;&BTQgd+d{s=HTU| zcQZ?Iko`zb5$h{o$gB6rYv_zQc=>w(ChU@5LR4lR9d@9*{mNYH`5j!8rgY=M(RfPU z{1z`=sbS6S4sr;Pc5yAw?x$Y`9~(pj+x*}nkuiugLJWr9m8k$4f5>?)@GE`YZ)IDY zoNb0|Uk&FYEnT@3M={F8iE4nTS;|DlrAIFP9M9-463O=4%CE_nX4MIxc$gJ(#-vjvi_ZFrWo2cfv9yAM?x9!F#{oNst?_=q~3{&^CU?odBw57Ivfc=QMaEOV55{#Uup(K0DuiKZ%(%Ym2?MsD)5 zj}raN7WZm-5%W#yS_3Dee^x(J+>H*x=Tp)njhJAV##Jy>^0 zgT9bE1#X;%ff|tB+srK~*?jfA9z4Tt=syPUu(DV+B>uwDrPbP&sPze|m{>V%!cOvT z$rvEl z8mV*xP@O?4r}Iy73@w`UozI{p?HMP_u3n|zA9N&&2#UapZGIS@?c(LFw4OuN-~|-I zw>vD$MnuaqhiwP>a&Kc->kn`mUvO~WZfWO};Ot#J`MhMpnu#R{;R557piK@DQVxpw zH&@J+uF!fxpfN6g*8F(+AnxV7Hk+mx^v+LhPA{3`wIFD;o6f}{JbU|}hhXLh+t3c2 zwa+my2|srdlge|9%vo9C}~#vy|#24D%y?n`?)8>ztv^T9PXASCyN5egl{a0#ztv(kqmguoGx! z=6)NnN`=N1{GmvrRxH3$qsy34M(lJ_79s_KBjkaiNR-3lmq5y^%x~LbJ(r?UGJ<}k z#EuOD_Xn{C4tw=`Bg9)vT6DlWXkF2kelT^wUYOw+P;(SL=z6pOjAILqE4Ih{=Sas# zMl5OE2?`IAMqJIKB39#1)TZ{@bPF1MUHth4ZPr^)Yk@lvJrBR~$e-&&~seRWTW zz)&FjLvDczKIX9BBvDvZobjO#0$dDUE-i-R=PK}*xA)CJA}@qql|(#`6=14stE%iY z{l9y?Qzp9l+F-PgVeaMriD?nR;AK?cgXKLFf&!?&ilNIAg2T!inrtsKn?)$LOFoEuB zX8H(|0tbn05J7-0E=oGWpW*(Ab6cQ~V#0;&VXF?4Ikk2gU_XGs3`EDu3@#P17xx1j zlH{6KV#pVa8gW0Ys*)fHsVWK$z2Ib3(Fmp_D5uNG@rd^pb{N>Z)%k)4vrEBg`^sl2 zLOdWCgLulsUI#jMllTwIpY*4DON*8}_z|PeP`huH;dIu8&EOU2z*pweOQ4uJyN8Rc zJ>SMrj{y25dWterpnsTh>Lg88vZ~h3$*p5Fi9f#?vM##ANpQu)2&2Jk=fxat;N3%fiAKl~$1xO$!*S5ykh#=-W-A_cJz=wmlTcqd{= zN^CVKarvG|*Trt#-$)vfeQZGLxh2U3&Soy&aZRzu*!P(CG?#gPLq5rYW?;{0VdoJL z2|cqH^bZAmykr^7&5P5$g>;oX${C)JOR-UshG!fI_c_yzXRGSS@)c<++fATuS zO*|nQ(4cy|V;1%$VU`_{Pq@kBR=X>J<+}y~xWdd98c_%v5u1mB$q#SPr zQ?(c+qV%#G@Iw)58ocKy!J1uKAqC^Bzsx+y%UnrHWS>}aq>)7*ETK*L3J58_Cx_nh z-=c7KGU02a&y6s!O;`zN7ctDQpSu12OG$N>u*cE zIm`)#gw3QJVeK6J$-$!JZD*SU|2s%WYICN}Mbzl9>#H`GVLVca&Mh;T>UlTH+S-=3 zD0eW;@3$XFCT9V4K2m7^h7DRaAg>TDrsbAzjHp|0H8lk`32zVSep^}KBOtD`a_v~c z1V6HdUXY+)tz~)ryP`xe1?^k}%0H|NU7c?A*u2o?VW({J0iEICdU;d?Pw=I$30jo7 zt7R=Gnc@tFoAdUui2+=a$9k{ZeRSWm3(&kj?n*O!T1|*kceg=E6J@BeRhEi`bg1F9*lU{D z>gO6c2R61ghho5emCR|M)0e+#py${Ma-R2^>p3N+SpTjxzhirM8E59NiX}GYM^VQq z)|h{Z-EYxQ4d(^5zW&)GN)w0ju{O5V&&-3p}^c_&fSvb{OVcxuzehsx&O&kyc z%Kk_HU9q%ii_t$WOnRT5B^!TV?;7Zw?zFMgvuXX1-{RYS1UifX_A{4_M!mE-B=sKG z1$0)1T=l94FWHTWT(I&(!Ag*%JIET5LEK*LyC;*e#tjR~0Y}$iK$w%as#O_z#mRW% z0`=AIz>R|G}j_L+O}<0m7u1 zlgu+La}{J%xJ(l|WaWECojtxIUoS}hGAYv7=Dl;()gA=n@3NxkIE8t1})0h>!Z1j!$&kn`Vy+WYPK z1$DtFKv$K<@T4lvpB@mN-54$`u*|qzw<%z8xX&%X9A$nsU?FuJ#yi-`$7y9{j z!TWSf*Xb3%J$7B6O>f_*?Q^}#*WP+_;4dH20pyMSYid33ifMJBhm-@$wj==K8XsxL ze_J_=vEFrUazKu=Um{u3UG9A;2iK{k8~lt*^#%k1u4-Wp!K7rIY;Mz22Z?L#dO@Kdv`q+@t1>Qq)9y)(A)KLerw=ZL&=KT z+rzyjM|ZvWt`Z8F8<8b@<2!CPuGc1Fk~wG&F6_f!!^xk=Ve^{xi5vgC8gw?C9uXxz z4Y?QJ!k$zzCEbvCGQDx#6r9l$;sfKA<;KL`F3;2fPz`!~IndYl=( z!OKTq$|80u(WPYYS9uvXIZyp($owW@Lsb9%MLX;LKaMlqS4zbV(*9&)IE&Z$msdH%fS1oNsB&P| zR+9!d;7R2X?eLj<6>NpYHyX4ejmZDkX7qJkNekRS9?LUq4(_Y+8O>}(8#vPebdsf_ z(`bF-S?qXkeEcrtE4+V}glySuT&()6#4atSU?fj$54l-_`k~{H^_2f2Yo!$N{@C1= zjryx*wHybmR6N}r`nQ&mhx+o70n(6!UPI>$g}}FUZxj7fM&1*dlqAXRksP$?RS@tj zh777kc8t6jFIymR(yJxV2_Ff(gR?Nzni(asmf3G34Rq0=FajZ_Q{eo;Q(cUA3_fW3 z-ILuK?;3)XV=Tb;Thq92;KHicZcW6&5d`HP;Uro8NbT;ke zMC+>4YHh9=00dxZX6g{RYBL_k#m5#)T4T&hso#j%PYmWF>Tv$+$5*hl89XqLU%hbYG}qWuR>`NGr2*LpB{0nWzsSz1Jmyc|Yhw4k|DY{`;w1Y)={__4& zJVQ8xKwlHrW<+X_@L~ED_;{`bA~)WNaQ^7$1hHv2tvimrfO8FPxPhd?$Y#_P%&H!r z){9TI3Gile@xR|C3;d_T&ACZ+OP5xZQdiX;+!h-KP18MIgTmc_AxvMvj=cGkS;JtN z@sSn+I1`I}Hk+ceheNoME%@1kA=ZqP0-X^mvj3GUW~yj^@MZjQlwP0F5;w?48%$WQW@33z@Ybv{}vIDO%qq(T(G<)RvLm!#!swe)QGcq85nUX%% zWA=eWoqG5X98-o5NRUOr#RKGU{p{hk{;|gLOJZ6xnG>AQXk2>wU%6rTLJ{q0;Lp+i zU{)#N(E~B|%Ju5tWwYb-B>C0MVqQ`{EC^3}%C`^9-7(B1&3xGU8&uL)7w07dnW~726p+y@%4;OH;E|f@0i#)0#RnOV|Y$gqNDv5tNQo z#l2rvN9xvB)YZ0r>h_L;=QjQHeppjdtr*j5OXoBA>N5;J)xrJo+`p$0Tv+kSHU(x$ zm9)5LWnSKwRIo7KHNDrCex(fJ8O6%Z-E-cYntL(|ecF|vhA1n+Sb1QWYNvdfY?87_ zT&VFSg?$z$soLjev)C@+W{)E$uQNrq1tfl!wz<&HKAEz6w%PL!BX|Sc3beku68wOZ z;14MthI8Z#pK{J;;>F126RuGPi2-r!Huz;B$e}2(fR)`@uik%In|u>Kpo%38Y##Z+ zyA0*59Ib_@553kUbI`<#_s)+gU^#)AA0c>;6L<9?U2AT)HCI5a>5rCaiALxn`N0|_hx~;c1DCAi3Js+Y zJd=VZu+4E!(F@g>lvn#ItbCM)p}g zt-oAr_DG z(K`CqzFk#j3VjNy;F1K@`R^5u$~-$66!)`&UGymk_;Yf{K7^+bG<=Fy(3ghRU)1Wo zIyR_>uRU)IeknP7Z@=8?@%kYQUk3*MGw>~ga)-}{ZbB~WPBfK6`^59H=Fm)Q3z*r% zAm`VjcmEYtB(*f1MH7_hS9fxy0?@Fp2w3&?rSW~MC9D?Vp)Of?oEtxH3vTV06z3h? z8)HJ0(;S?>Uv_V#=o*RvTGXz>cn%2v(LjDKEZ9UvDk%i6e3q7?(1SJn0dxMlDo-zD z6TdiZ=nI8G0*Sorjq1fk1qg_`+`{iVE-jlL zPu@ubwm61!27UXlx*~(6e*j?3>3lnuY{XHU=e6pJ;38yTSo(y`>L`ZTxS+ZZ6OYBf zON@RFz2e~9pGShU0Fbw~t8V#TL0xgk=FNpPfF*SQ4hPPxwbpiunwPCEJCvKlimq^r zAe0;$hddu~BGq?AT4JiNdy=jJPA;m#^_Z_WGC$-k9rx!FWtbf34Cq#3jAeCw));#=hSD9)4}Q=ZzrrY%)oY`5dctS`t{Xg!Mt37fp#p z+#f;18Ej@zOt016iC~2oP1{m8+k}E|Eha<}gIJMnT4JHfrrqIwP7bG#vlL9Y%k=X6 z`m`BBgVJw8?XMJJu6x)>{?Y#0x8n)MCdDLz#Zq=#pU|iJuXK9^n6I+8E zy}*f&KB3EC{U;93L#8*)u~(v~32Qu@QE`8}>6jzb!OhszKL!ef>3I0Gla_j!CL<{AWPG+Qhqe?J zdaz{IA3w!61#(#@73hHA2{pnb#a4C3U{9cj5%0ImBL0CO%vC-EuzWI8_c(|)n@RY8lH z(G@2DN7I@AL-oFY{64cYcCs&1B$b#-me7n+v{(`eVWcFx%9dpgDT>fyskbspB`Km1 zGE-8bP_ktkB6}w5U}nyDK0kc_g?Zd_&V8=y`Fg(6zOHB}!jHa>UwSpG&Z5}dec#?Z zX_}0+K-nwfPpAPewVjKsNk_1%LEx8x&1DQVg6Jzg*LISB zV;!l|0B%Xhx`AQU@S@d$ZA^hC>+j;?S81IBZ~>j+F(4w!mU84PDspI3wQpyGQH_J2 z7BRf6h=+jF#N_=-+UJ}`3+9<$ZY8-wp*b^l3pT^|SFntItXZ$8Ovj^@Gn=D6I-82(Gi-p} zRdj(k_{rVtKHuwH4zX4xJCF&d*6G@-tq1W01$fgnRB1=e;_3JwKxkIKY`y1-2eSAA zd7r%`1}lT;O^y9qXvA}goBWP%2}ZNa;IE@}m0v(klsnu0;vxL9RoDckYQIB0(MW44 z_73+H6?u0bGnXOxABeNq0HgpFO+K&=h^?hu`COcvEDMyt(2W!~=+Fn(ds=KjNZp2# z;5zbxbuh_I%%j95%3gPgn3kp6^oHvckF`e~=_$z0EP6LE5HCR&;C*wJh;j6j>xQ?% z4ycvwb`|e;^%cB9SG75@GS@?=#qB{Z#=fbJ3r?uPBTH+)3v6fE(p6>YLpEXag2%xi z?#H4=IVvc4ZIW(Q_p?^NJD{_FA=arQ06Kx;^S_K+8kf^>ANrqVULSd8!Whk7T zkAI<-TqL&h+M99tDb zQzG4doGZm)gaZ8KSv4Fk2qn$(3FrKyzQV%Da~4AU1F0%ZWR@BTd@!Z{XgzA(_b2Y( z6k`15_c!4KSw~dO%3Wtc_%3_XHlr!JaxcbFm%#7`GL~w5Pd(%-c%ZVVk;U7*IWx?A zLQ306Z=_gG)pfJo-u^qb#w$;?lvuaqA!3B%ekr!%BjA}faKR+TA@3{_85G0lZze&q8~=$M1kH95 zTesW6U9nL~s&<6&UJ8O2%fKC0b-_?~fQu*@jN);Sa=t8QcstEvy+z7n@v7mMSrc&V zkmq?NqceG9vA0@`B{+`TspJ(+oKUUIz~(Mf_BSwH+*tZ%Qna~2@5RAmc$O?Afvov- zoM_SpGL#51@ak8tbFZ1u29fxQ>UOeX zR~M2aMOtPv_p?!sx>V;#F{<);(}VASOECm%HJzJ*nFV9xUwNg^cM{w*3L9s7FF68aI#Paf8sN8x5mcB?h4k8BAg>S)^`Xd<5a^?`ymM=ay z;k4;CrNb|#=e#7a0kfQYcZ-1X+1Y1rzmIjjcP5#0ME?U;gcqxyKR>Vm_8cJ#zKNa& zo!6zBI}C_zy6X@Ps|@hwd7ide4>0gfr(N!08*rcka*!vTf{R<|ys6uZf%XElSl)G>eNEBW(9S-yFPuG1-hXV*%|g5M){cwzo; zxfmOHT!ni1xy4_M)?ov=NBK7x8_ak6-~K^VJ0^$Dcrp075vmin1TCT=c87pDvTwt4 zX`2^}*LLlGA_}nE8PLZpelFjafRCIJ-vN9icX1N_GWi%;w%*nF5^po$w&rwP`vuh_ zW(4{g`!7!uUBr<*-2JOV&@}V5#!!I>wvKE$n-@GPmLY+PNR`$nAcxUkkcOVEEYBiH zaT0T^5?OZH2PGKSg4gjl&R2mjR#=6WGKEj0xZ5}0zX{!27#)&eje-u-IEn+&o^HH$ zOcp4z>$p#c(Z<}ecPCyMt>5!$tCSDs-hzgwc?jn&YDkpUsk&Y^l4l-v)3x}eSKhXF zC9bOBFUPn4Gyj7n{ZPrz?__}jmN#Ohy3sc@>@>=)y*P=M?kX%q0|BHb-E-_Oa>3L z1tXdSsTHs`I8=27M|}j_+O=RB^3)(e2v%hCy+2clkY{P4%gRr^?aISy5Kk! z%tp$swJVv(2O6-oP$%6d$w^c_rl{yc9@|8$IaeLI{_8Lg_Nr!lghm#l=bi~D0q^!| zF(M>F6j1J5%dG;g?D>!S+;JIA(o4YbqUePPJgG$XS1=cvq7A)R`Zc>-j&u{1i7xD= zmwvMqmx3z7f+@LlgYw}v`OWwSOr;aHr5K3)ffnmdl%O!mq{!TKe?rc)K# zeuM6Pa5l!#MA?2ylzv5mnq?QXPBdUZI_e14S_O23%hym=9ROBQ4rEJCJoW=0X82xz z#Ak88{g?Q2vuQFT-|2h*F+#2;cyh_lUqRMcnY{%j%M=f=!|(kulP>34Vqta1PRr<; zH((9rw;kEplJbfEcWIPc`PnYc9B;7o6xQj5fpV-BGzR36+iFpIcYc_3#a!Pajbo(j zzU%ub>dTi+r{cEEa6B)`P}5KzWzmOb;Ksvs2R4QmPv-@rV#KI20arg;_~knH-qw;a zRTvWBDAagwV7hHYb|MNFdJi}tl3V`U{nbE>@x0DkzHQrgcy@MH zq5u9H-!T5x7kXWv*YYZVf?S-^|15ct6RbUQz6vVa>p$FH`-N<%6&y}=e`#PE`ZnyO z;0yQa8s`uhlr0+#zWik&Lw1gS4@EZaDVMS~*RfYlU9m-Hh>u$cs)B;=OYQ9ji zppcL)@t0!*>!lT^rN^0lul}`t2Q>|ZV6f|bg&KjQf3o7tpAwg|^unL9vLs#RS+Zb~ zpKc0F=8n#MUZGc zF?}^?84g6KAXPNv`=)dQOPdv$&Dn>3I6lXPSjd5@cmjtL+PS!#Fgf@d9Q8%Xef*O6 zl?n3)u`77&R+J{qr*dADbrcjDq(vAgnM$#AK)9Kc>3?c=fL08j!*gt=!u#G}$y0)= zZA&SpaU;2Ky#bYpZIb{v?yG)KHD0gw!i-k_>n$K%KRtiwhE?wtWy@8+3LbbkZvn;I zgp3?SrI!3Av%ppQVU%xLy~7+#KqX6@;N4%(`qAy&thV@ybNGSQDL8(e=VfqzMZX5I z`6U15SZ+@xTW855DZWJP|QQQWz?HH+)g8f*24&t}DZv{6K<3@Q!G{ z9_uya`Z-4~&N@D`r zQtsh?pm#*uJUO(mMb2rw}Rt`KYL{Pl#c@U|WJ>90OwzW*(4n;C(` z&}UfW(&WiRNPgW6h!c;4q)7>Cz#ANLav4Waf9e`=D0jvPEdAQTHuW)b!QSm$|Lkma4C|Kj3Xk!pue)%W;Z2jJMtYa>xH+2J^#T?~} z2px7KKgK?Er(M7U>H{@u5IH(lO(J`?e}UN|9NEeuv#IaCE~V(vM%D?AHm@_*h%&RV z9Xy$CxoEqNJlf<7UATrOZ-w2Q!|sc*8s}=i488(bztoy%|`uemv^nv2P|5B~`DM0_1JCX~%zA+m*|cY`+?dljwm zG~dGA{n_eLrNPg5d-ucQl0P&u=d-+FA||KClEr&vGzC4$@pyIbKtaVThiKY1&jLsO zsHVPu85lDwb**B3#kcj*P5?(09~4T){>2@sBh|D^T1g?(LUAO6#?{|8;)}b{akTu9 zJ3mkBh)^oWYyyR7@)!8aty@QBDn;3f>mZ>1buP~pDgY8uXwJWXk3*srwUDrn(3MeY z5&h?W!Z=A_B~*|c)j`oPunr7oq0QAN^UHyZ8fa-N6VPwUGxkFyi3g9Mt4?O}=Lg>^ zYvid3`m<-LfFM};o>Cx9z_9E;&PE)C5)}azjPEvxn>9oqAOLiH#WhXH($m0A&?~i~ z4m!zIHx@3+t$CRWdmcp0%r!CaVGxst{{WrP)j|RF(3^{}4a84CH}B^na;s=*7IiJU zz;4Ux;iy#x3eNk0iODHuCtkd5Y$Iq~zWfDQjr`bDQqQ~$l8KGEuo#j4O$w}D%juyc zO$Pt4n7VfkV7=2em!KMzTcS`L33d_ExnR|a={8&2ehkYR8t9JD!WG&YJx@ae`C%`(+0E zr7!J~i=-O##5)HO!XD>F=t4W5$Un_fP^k*zcWX)OJ>WMgq>bzj=bgRJ+jovJ0go%sa5pw`$FyBNr3-obZC4NMud2g=&To?VIwm9DsPsP52UjXzsI3TRCphwM?oO@z zCRzh0go0WbIre_A6&mAM?HP3(78%b|?4>9ove$8^_8Al*3+xT;uetsxRzmL2Zd^tT zEU8c-xZlEzBLqmv4gTOoul;7Sc}cq_vx`8!;4nIaXP6VYQ|j{1Mm+e5WBsJJ`Xdd2 zD8*Eb_Hlk(5Ik107Fd#3#dqHA2nJBBS@T?SXv!|$+IQ&&HQ?%O-Ps=+#;6{3 zcCIA+O)&qPzT)P5-vZvfGC0Rql3{$tj3>q644EgR3TW#HRtLvE4h#{6E#@1*ZOel|Bn%^$am)BGGY4d-W&{9PTNEZn zD@s$GTGK{%Ia|7ed)HRVTeU;tV$a4NWmRN#z<9OkB(^>Y3)MpGtYSNHI$U$WT*;nuMGD> zTmv9x7($@d;Hk>&O}tkh4FP(OUIjQh6+6Ef7WDpgP~T@~EvYy81cF5u#l<#Ti+1xQ zPic{EgYJsG8Vxx1QOS?sp67W>!4MnJ)K?W81q}*SCO6Z-YJHz4K`Lp?<0k44Jzz5K z7(e~njUP2^jtOv@u?9cUzD5plxKDsQRZU~QuzON zr^?S3&9_twGX<-aj8Y`rfmXGYX`Mg;&SBJ7eI_8OsMgEiq!^tlh!u;8rvRZO;zpX3MBbPIjf6wl>Z zMB_c=F9ndKGQz=AL5JE=KLW(&{XLno=RxZJmP!*eczLEIRIM$y^1@}b+=Ux_%TQ_q zfz_mxIDZoE!g;KlyY09C%d0+IfiSX&k9m2#wd0@h-5eJ4+VQwh{*{eF;9b^se^L-N zIzfxzAg$!|P-N>DltmY%b=eoq4ZX`Z61y;1pQLw`UbtcQdhWFrG5(*oKc8Qh22TJD z&2VAW`o}D@>HQF++HREoL^9+k%WV;bLQgZjp@mi&W=vS()<{yU^g*%}%G244iIo`g zUTQN+3d2}C&@b}}Y-n{_(TCyVQt(qTLJWk$G*)7jJzk?8nt|lUs<+fYHMEWE9nXB` zD$tPsvNsvdm9k!NAj2zu91gNCGB9Q^goRcjTb-9sB#ccb##4-0{^Xa?#oUF_a z`{zT$iT_J4C@Go4;YSDt$d+1_khNzy|Qu;Q^K zUl20Q;kAxW2Z{hSH7Q}<$#+u-Jz48$D3{*!GSTE{pompcQ_9iJ*~W^}8+VhuYWEiv zKilqPw2B}dk18*$sg#{`juAUC9_-VFt(Y6yMdK(61+? z6vhvJ$p=qwIMRR zuYsf=>5{qpDLuCC!3R~?!VDrw4je;5-{V`!_|v%6^zs5ffaqbU8!1n33w@#CBo(1} z`K7?_vIS0$DuZEbqYm%mT>4cIoI0jInik!-bi118sm}H>3xW_C8U826ac?U<6S&v>q!mP@Y2HIcd2nHsy{I~Q&*nZt9S(dihl7gLJV!J zw`%9}M17)(!g&E*v`w$6jHnf;xWRZykRj{{uUrGKWdR>U3OvVJpgc!0p0d*T=VjI$ zKn-vCAnbmP`aqQQwgHo<3WR_GkTRnjlpnQtj8fjI(K>BMEv%m`b$ED3a?}1&X=Te( z^!bnY`!ANJ{8qxvyngKOPC<1)6(>N{Sq<}lTXh&g*s{z3ASc~+9yO^>azFS;0=XW_ zH@+p-R)~nnl`4D+X-M|s>!g2{ag8rZw92c7?>xDQ3Ob4r1qu@Z7va52V;!syov|YV z7hWE!y@8YGcq}}=I)Ctb$jqwKAqnd5rQ$@O+#gu<&Mj%5-P~YI*x>fp?n2M&NeM>T zyL>mB+km73W{}SYtPeXt?jf`N9KLIeQS0n=RFIR5jB)j0sSGOIT6~m`jVGG}dZM$J z9xu=fRc9*9V7`JL8g+{m{sp%0@>dchW({%SCg2h$*cfME6R#pg?S79o+l~R(K==K` zKA>q=6A{=)!%G=|LAijcMo?{?zVFV z$Nte<8Yu~gC7PyA9o-BKiC)##Xryw{9DG!BO zI_EJ^$Fou(g7VS&my%TSq&51u2GjeT4i5ZtTm%WRZH6CzfmSVx&_5fYj@BzbJZeo% zsD}U>Pr0Z;Ld9H?K$Ss+X$g2o^l8n}WZr#ukYoQ+qT1cq_at>6zq*fJ_(78CQ^Wouq$8f1NvTo=msR(7YXxXTRG{qae&}u=u@4Op3KIP)@Hgc z5~Z6pBAL8iHgvOM;NDUwCJYV2v^cF@8YyBlJN4Ww`_6J+OjK?0lwD+2D%19<#t3Bn z{{e|7F*4E~sZRX!aY2Ceb5o4g%KQD4r)u_A>yc`j&H}n^R6cN9iMHjR1 zN>Fn3Hy`MjOhy6-e;MPU3%kYu$58bXrl70ZC?s5PRlIu`-9|$h3W;|5V3V||H4t~E z*#O6UmaTE=^}|$7-WB+6sME)fbGANLRVLZJ;90vcf+@%0N@|R^|3`#WtNZw)_%=DZ zIM085zRO>_ZvPL-%~=l1Q|Ja`Cr3o?<`l-)Xj)2fT{)_lCGb%>Fvc{dcT3 zJ-mFwS9@^rmF6Z!Cl+kOs{%&I*6H9(f{q16E}_48L{ncjM}pSPT^IYa!;mBbmezff zrxm?`->hd-Zh7~$p*D!H-6aKk#5j^s(9dP|{%)0l2}0jFsp-dtNq>z7t*)Njz_z)- z!0rY64r2e_FLqw1jkX+jhkk*%Qu2S>l7*}cW16pd6DKo2doqjhz2Q;a9hvUp%(DkN zjy%MLfn-76n0F+aNXbU#rA>hYDkopVMp&u^Bv2bPeZLBv{8vg=qyMP?D>-Qf?T7~l z?qKbWKn|KyL=>cypd$H)T%x|YbzQnOXqA2VPF+KkJD<+FpRs~yQ#e)V!i#5g9VNbu z&X;-mxCH^*--dwo`|`KfuDt%Ekla07$T5`hVjuk^g?1SE&&6r&CxQK9okXhOG{JXh zK0Xktx+)>|JI>%oZ$t%FYoKn^K!h9|pM|R43bS6LCxa*CKZHUHUdiDaR4-(+2`r>j z)+Harl&XL%o{cy%7;`Q~UY|P39w{_sE~2cnj-#cfA6#bNy$BBBs2=9L@~bCUKg2~{ zqY7bW=Q1vCZrJ@`k7wvmQl=`e`lmP*cQ?x#H6A!ADPjVTKr9t7`Sz}u)VwWCzoTw& zSp~dA_+eP*&^LqS=5Pb#a9h5(63`S4|6rtb`0K&|-@8YDYSmNSOA`jHo3NGfLmefNJdNJAtNs&wH-NnuIb06qoJ~!Gk%zfTgiYsjrzq-1WJCVst`6H6xa4 zhA9DUP^<0(eW?k@-MRG?c-YtO`R~cMCFqN7>xDspsbM z_hL@}G6Tqc=&p*c-&DL&Km?KeTm)BUwSrKg*#?^&g13)VL_2b%M|7sp;R6ue|% zKUlsHQ=>lDJm!r^Ar|6717u$YsCXufIMV*yW)60sjOD>xo(!6#geBuP2s`#@>CeSxXap|ym( zl1DWS?DNeyR*olI9{dBzZ#gHul=W@GvjM6(!us#C(=p-Tk;Q#nxk=@qSEBYQLAj$Q zVzD0=YkUh-t~RTXXH*K9zYh18RL!-!@vET|fAzbU1>A;kRx(IEPz+@xw3eJ!p#u0d znCe8&n0@bXb4S1y#S~#uUYjlSXQyY1%j}Y@&HkCG0l_@!?#)W!EfOFsbRJfiaSJoN4{tRyeg#`BDn*1 z!idyx7yxS>j^Pdm>QU?w;fW}5l%;lNOFss>f9MzgeSS5#``l~C03qZfb^Rr^o>vyT=q83&nO-_$qvjm&|#JUGZvd%G$%LzIG z4^q7_a{Y63D?a7I*$e->p^q2Tj|2mM-s_HK1*Y*QE6A`WjQ%B6bJN2(G1VR@%cWvz>PA= zHsCJAJr`7OM69z77ZKVPIroRmf+EL%rVY3QR@(=#@6O9@F{1E;nA0@}{Hcbi-HDqJN2 z9zN}<9n^A}?ELz47^IW$S> zxuP`-RBy0xV7)D>b)+a;oc@dPq2td@*>~V46Sn z7MZl9E_;QdWj0agFr@^L)o^-f^$+$B-BY5?Pz4TRpzZAiHY`jdmR}Q;QF>dFaY-~; z@#<8C%=*q-BdD4Q|xu zbW6Yx2_b>cZwKEEKSLg+EqOEE^v^OUm4gomoSEz3nx21#NKPsP^4(*BtJdPN%e+~tXPS^`icM@)M`3`_0qzeb#-cw z`wVr4xzj+q4#?Qw`Zf4iP(SQ>6KG+rP0+FmZ3Up?7 zAr+zxBxy4ChX%qQESooj`Uh2|xD&sn-Yr^#H}X_7z>1x9rlKRGc)=aXsKHjgvAtN= zE+b&8#n}sO7m&pu4n10pfOgRT9vt~iKr6B@A?yWXVetCAKaH9wV?$`SRMRVncOB1N zj@p+G!-vBp9M)!UB6hElvz?H2_pbj?{W~4t#O|XmK>>?{ZC^yc4T^w_FHOmX4S2Y!?^Cc=#|c(z=v^o zBW~KibVvG0F%n=D9~;Pir1=%eOM3EaB}LGB5!Ddz;7Dp1HLQo%KdW)uX|b4&%v>}k z&@T00_E@79PzkM((8n-!*J20Q{1SVIk9m#gJ2b@Gk@i9yoCk^`WL4JEhd+=vujW<1 z|6v)%SzP4ro}~d}RVqb-m)&>L#Lj7+^EsE>aYK7~Z-)BN#$FVIl`PD{8#*kM}jX#rj}Z76ML9!3{OK@W~-(~w0;r2_k9bbbbvsWd*Tgc{ED1HvI>EXnhb0SzgyW284}$eHy9a zA&UZMkMlw^@$P4EA5m9a7y*lW)>8ERY>n^uz%KkS~8+B4vM8_Cu`&Jw# zB_vdJo%4k|3zkjs`Gd0rhAE2p+!yu#TIX6vUwVSPr zIk`4;!GNld_Vs!Jb8Pp@g94140^`a9-pn3amAwe@{SlgxT;m*~_X^m_LpeGa_zTdN zRT1UpdxO`vf=@ZcRP|M!@s#8VN+}A}o-=31-@gy+0JYgqpu)+|RUPjxs|hF08$U%K z+YH%bL)Hnqf~l87V0VxEN`Mf#Yma(}2hD=|$;8WK6Glazc3^>-8UP3jt-4DkuE2QP zV&l`|zBY&3u3&d_rUU01n$}F20x#rQxu(SH9^1ZSy3N&Q^^Xb`II{E8iJi` zlngh?WuIq{0md4fV{NvQf?pf}r{3Wh+N3|W;K{iHqM$mVIYOL6|7-GZsd78!q_Hy9 z8B3c6b`x!fF?%MCP_01ZoTqB!cO5xjg4djlZzCII_9N~K&R5q!Bj*GLJ-BA{>jV5X& zbl@N+M(5A0Y)}3MKsFb=^VIISxeaMB8&7&9bmD3E;)Ro{9k6$qs<0I;9p5 z4E#oe@2wl(&PCzvH>2zK3aP|j?C7mfs*7mJ;R4A=rfZ8q%@LT8k*HmQyo21StEsRc z^j*AvDEKQuC5qRay#f>65e0d%QV(jVE{C4NY_R&-a0VqYqep$Uqce!e4iF^m>6XPM zVjyc*eM>A^ifaD0>-JyMUcS}Sg6Q^l<($@azXzuziYUbcpF2GHsd8Wmwfm)OpfE)H zu=l*6SRh$PM1l3&sE^i13vHXHJ35l1L6hP~cW?_NZ$BbO?UX!PM(74->;+dKmJM~$ zN!h>5{R;qJmqTtTFE`yt6lp8CJ=7y%1BvkSo|(o3&cL6`(b$p&$vdc@6gTeLdwJHI zOA#{Osz!{)%PIS8Euanel-9nKE#2^U+OFonM`TZiUDk?InS=UXhFsnASQH|UGZSa6 zB}jq-H)KkVHJhCb^%HOGJFE{C?eV_WtpqyIyj7mmKH`X!JQAaAXrGR(NLQ)qY z5n8z7^j!pp32V{bwcleXeRXj#j;WDxt<#}k^^MW5J$p@Agu%P~VU{iIJ%=*nQcTc< zTf5lbnOnH#dc2=9>Cy^SPoKvh#3@F)0cAl05lEH{SVyBigi1{2;#gtH^Bc9TFE(6f z-hUW+bhQ$7^3t3{ThHFkp1qIdhzkBU^2BSyKU0$xQf_SgrvFa@_{8{Vgmpsk; z)t#SDT)3V0_z#kofb10d`%KxAO5d|C^94DzO!;q>m2sif!aI5O2g)h_<2XIwg1({% z*OPhrMO06Sl||he3T&tQ^#2Rq3QqW{u;gDN*Pv3V#XMAh%`R0&Zx|IaBD4v49FZ5m zbq|KE5TiznIgVnNA!nr7m!KU)`!7JYT}fJzfxZ){!Y+lx_wRf>*l|f%{3-oQX zbKJq3H8OwkWg`#$&md!)!9Ld{6=}x(uzzu`j{xn=$@)G(H3JWjgk-`3Wrb>WnA)XyiX|VP7y-R;pcT@`Om$gWJPfiTA7yk7oO+5}`pd1Ot zzSs^oYl*g%9Z$B4ZU7Z47tXC0ROno^wGjn=k`O8a>v==#CFP6$cscnFTU-$c&??zkstAd zci{!S`ZV(U7HqU8{YgA670PhJq^aMfn))q8@^tX!%M)Oub< zwrju5G;3_5Zuj_Q$yz?z0dzgpQ?#eOTFQ{8X`)mj)Ui9t{Gv6;UItmKW09Q)6K`xs zU2nh=W_3^fhFhDAlgOU$u0{}T3YY!jdKW?X4wSn(y~`53uFg-{hpPQ24)b)_$8}Vw zl~CB4M)crML1M`%6_MOR=Z`N|-psxyz%L!Xvo%fLF)q1uCnq*Huf6eJim4v^cM+tpt{~n zLyT?4m*v%q;v%H8YP29_U?w|Vq$2cq8}1}bL){{gb@kd{9O=f5Q~|0y%jfALy+%D| ziR)uI0h^v)Qz@cw!42QhyJvhBdNLm$1rH%oCUSQ_S^^vWk_cGyU14QH4npUAJ8jZ~ z+Ev1v+5o=clCSHY5oZP4EyfM+&gsPddlm}q#n7fQzz@%tqEd^R#h#Bd`Bw0XAVBqgaM-#z&e!Zan?o6I&U!@JloV&k6_iE~j@_RQg|z-|gC# zAEtU!8!9ZcdP7W(6d9>YUk!srPQ^DrY_((!lb#^00fJ2~@~2T8GnMSelB=yBDH%!h z4}$F*!FJg0>4I=8NN$^_Fan*3rD$bZ`g%|(_|c_#nH$!!UsYf>I9iR3toA`9vokX_ zH)>15vov?V%IEXrug96Y_G7q=oxsLRe@D;n%MUNj2_osyCpY?zpM8s_yujESVLHl< z#@AbG!D7WiNwcsO^Zvp?#8DPh5#Xitur5UTbQ*IGvhSg~Zeow3UF1oQbrIiz))sxM zeSkPx{5$JcMI?ATP+QQ>oLHSgRL=Y>*$%5@tF$>hr1qo8%Iqtsw^3ARD>%z_He;O_ zg?22>PCyM3WT(F(uWsYDzztWCL@l_!q>ph8!*T&_5qEK5;4Z08Qg?YQ45cAIH+fSS z@H#3D6d_Is{SDc80EMjx5Jkb^tcKS_0G_V+5vjLPj%vg6zv_@&F|tvG`djnje<$r1 zfszX5b+>lLF|czzREE%<1TIj+=%_--Y%+bd$2QBU5-uH zz6R#Z@6a_%E^m@Y9!mtE!ZN?D$|&% zuy)pvE>)zue%;xE6{R6IwJe>}*m3h&2O=$xZ@ql&MmuGJp`nA(xrQI#4P(dKX~RPWM4yHwX!@Q>#za*|(olHU`hDgCaWd6vQ_6QKqH&(5IKSQyeBV z*xv=VHq^kmyf<}w(Vd|mdg=ZU)gSmkADVWgwL0Bi@?93(S{;*uSM>K)pzIdEiTug? zR%+0PWS_;8az4GkN{GM4KF>=*$9H9?r*}qPo|U<40n$9zK;gro*CU5yxclWwacC5m zG72r65f%~@qgTVp4_@Z_wm3kqp^OFeLIC1TdDoi4xv9mX+!1BJ-!sRa<<)-&4|ydQ z@r-2Rr(WSp(78n{IUBTRh8}Rx68Vm88(~UsfgLYEKO99&GGAQ~4lK7Q(|&CtfTBHY zy+IuX+FRZmpHxpLzb!x4b07SjtEL6qDhj7+pQzE_t%M;_u2d%<7ha(CDv82;xj09x zR@pdl?W({iOkM~hIwrXfh}sK2YyneKVLQ}VpmkeygE`m*_0H_3l!-`P0_-hpSd1lm z4|&LytN`+;!pS`+oi`%)n~)IBL1&${eK{J3qr(Dy@-=If!WBhGa-`q3q(>8jgcsE_ z*n?n?;24o{{H(oDdmvl08$=tzA69%cM!<$LC-XK7?%Inw ztds{FivcewDQ-*5{F{4{7oV5>>`$oSZ(mV-8q$7j_eltn5*p*T zf?Fu5O9^i98p<@gAA47Ynt=N9_W8x*_1M) zKlQ-l8{!YZaINmHMb7{Uk0S`xl9ieT+RSP_1hMzF8A-``ga@4mV}~G})1YDCK-27- z<^Zx>sfS<}j?JSx-1Pq(T512!Ft(Gq_*Tbul&inxoVDNCURW!uIqJr5{!HD)U_aMQ<}cCwJa!>mO2Ny_vHP$>1lYh#phXxL(Z;~nwvp98Mfcr zZAD;g5zrGbAuEAbzOl~pCbxtaI^yMGp2}_$2gZ#ir_bKl#(MrpFji>{qGl(~8G5ud zTRlLc@F6|glBZFu=kzpCX3R1eDCB5dNBQ{z4A<^HE43B<7e&D5d^+}9F1LO_<2(JA zz?)wZRJRiYI+$0sQKwSPfjub2!j+Fbl8ys&MFHM=u5vx4arMG%idF9Q7EVi~dKn6S zj~HemlMl@_T9W1lIC-w`OU^J&uAgA3v7dbNH2sN@G7O}`h5G=Daveo792yUs>O$_w zk4?DsEYhfo_6uy7{>{5 za&SAc(MSy(>Bvq1Yrk##aV%HOV??-#vK>))s3GLK|5OD2ur7I(U7c3)jr{dN#d-?7&eym`{PDbs1%Lv z?OkO_poVc)Ho(Wxn$&4zzoz{$;4~}25m=Ccc6uHen{x)XN)rBbKp6@fXiY@t^yJy1 zqn~d2ibVg!(G1utAyTR1`^JQ4>Lp74@!jGgO8S}O?`}M3R?%kl_ud0ef*MH6_ym9S z07LDN;9OUe#L2ArqNP_)@8^V<+#-%GVjI*o7urZUi3`DpL<;Z2bRPNkl5=M5xxaQn zmvEQpnudE}S3TO`L!?PO=M>#+13U8!*uN9s_8rwInYLbv|(N5QFYh%S@vpiU-4CcUkj>i1c?*kd8HYN?Drgni#!UXEwYM|s!1 z4_a6_x(l531VB4lr^9#QIf z*~~0ggW2HHWqR)TNw~`6&AO7={A(EB*#?Vkw3BG)mc1P7&=W`~IopUDFA0#?@E^L< z4013yTIK@pxNuFMxMmiGV@<>=!sozo2?zj|$r6UW3WQ56W|woM@Mv8G9Z~n4)xasR za?lq{x$h5)s^vxZ+{h*t07VQ42R%PA(7j>GItwaWqOH0aN3e!pPH&Z9r6wUtzZ1zC zA(C(+bp(9Czen)C=z(v~M(=?_zWx8vbl(3|{_+2RUDp}*-uoDpWM%JjNyA7~Dk4tw zjv^Gzt^RgqD$YvP1Ufoa=i&-_PyyADkb~d7bC;`51S$ISQ4U zb%#Pn=k|Rc&-rGn6VWrn^F$~Km&)gn(M*2${DH^_peD>?-*mBPc_6?O@iVx@e)q~p zY(4w6wBaylHLn+-bwN}9dl|NT&tAImf96tu4`?Au`oQ9UAem{K#uZ-k9|ZsC%x*LA z1ec80{f#M$kb1E~Ci{nXN+QT+bCcK0PvgdC2*u-EyDh*hE@3nIOVPa288%f*#r8jP zWUe~z3w(Z-DEY`8DMqS?MolIU(D4^q;)+NlQj0OwYFba=YSTBWtr_E>&_Q(nE}$Wm zx9^wIMPSlJA9H2@IahhMe_+p_SYJV~2=$6)9%pBub6ngLfD?xhK8U>F(TxOio1*1d zmg{f!o77o%ZsyK`Z-9P9ozb^B|MT~`R76q)Anv*D$doe5kLO^OMbq zlG&{PK}1}rkLC(gf9?8QTqthbOdV*#Ml=|VPPw=<=0Bn(a2AH#43`n8NnkIs!*DVB z8Y{}X5|161NB6-ov%he!`r-G}8{5T6JR@Mzg5(d=R3HVe)IY+9myaz*+g}wWIVy0K zI#=*t4GvJ}-^_jytpxi`;|hg^O~AlWapJ81CEmeJ@5Hun_jn%W1a|-a{EOAb$PZWZqqoK9crhEo`d)IC*xSg4fxWiZ<)v6m?wi@(wTq4fkm*@YDH|P-Ee>zyu?DO;s>~y!oUxptqD(v z5>a?_)LUFtnRM1e?$<(hZH}V6QdPCll~)_KdvknZIikq%q!_+5Lq; z1U%T;bNpHiR1JhC6707wH66ti32bi?*#2DVS&FCgZuB3l8&Iw(;@H-rYp6NHF(HeC zw#hwzKwK=N*Ka@&yItewemnJ+%QJa=;T`lt6=!DD4quHb*4Ucz=^AeKg)&Hl7MF;m zvKc1>wN!&6OT$fRt8@cg^~+mNbpts)7PFhKCTzNZB}Efoy>k3Bomh6~Y7gu}f2?X0 zfBS4xyyk3dG_v%}-_>^4z7yxR^|kvxNb-XC!Er+Mz^jeIlc$fef2~!@Z4kIX;E{{e zlWu4!QQCV+De^R9irD-2Pt-Xil;g`dzI&tT4>hY&SD`&Cb3@35Wz9X1{h7}O%6xp< ziZ@k%%jW>7qQ&-i;AdWvr%T(X_xGrBT#JL!F~6?RJAHg``0ZfrM*vO{NE%0E?a%rz zmbtr+!v>t0eH^}TFXs*(6N))?mj`btR{P_a)PFwGLs8`l1glHS_f8#nKxmWsgJA`Z zM=oAGc5z!m<0qQ_&^OhbmvrCB<2>jdw#;Hl*ca2}jb1IVmIkA;!VCedyZ~pE2om9= z{xz_xK5Zw=%g^e!o9rouX+^)lIgxLZ+g3Kbks=5E22amFZt!B1c5H=&iLQ`U(b>ShB-~%`(5QIE-o?MRRnJ$teIF*TK8}pMxD@2d+g|i=)m|<396(6BqIU)r zO*`xsDo7J6n`e#nZ3nG6y=`~#ha5d`wUdc|ZDlB4OVBRGASDO1bt{f`!nhY>XMG!} zr{7U(KZEi@<6m?U0jR}8N?1d#B9P~|h6rLKNxHkGYIoc=QS^74y7$pfxgOdZ&?Z4= zzB>E97)?$MGE9fICqL0#<1t&;Ecox@O2=Q5yW|%39jDEOxozw$>UXYGu1}+tA=PlO zK-JZ`pf_mr3vtFNI3-C4-H#dxm0mBVi{HS$4w_^h_%rc*3u87a^s^E{Rk-sYaMS=_ zq{RJyIJ`EpL#`zosG!UIYG}eA@^KN4V=QDPPKGjX`o<`>3m&ZTe)}*@EMq%{V|6h@5-l)5t>sAkcMgg_mIkRe7(E3$Q8q(}IGv#(r|6e|rq1o*0dSa* zD#};tQ4|`geGy)>M~!IUGTX@_zW&9hrWL4yQ&h5B9ljIx)tB!CdXhiY9@oL)x_<$E z8maStvhSm(`JCO*3SkNgA-s>2b9d&06XjR#N43ZnpyFKU1bIImP5N71QGX34>tXNK zsNrP`)NmsMe@;H8rl1XQ4EYoe+hpCvXl z`X9q{N*KOyua}t`hMtt4kLo>2oeUw0vBmISuJQ`i$mU86)%eVQE_EH-ZZfLkdB&rCbUg{l)S zebt631rAU!uKhE0%q(~Koe|DzGqL)%uPX7kJM9I$9PFx2*nHLfKK{_IsmO%GsaM+XKMm0k%V@vzXIEvXN&x<7%ha*paq zW4f%A(t7>Z#%dD-ywI)V-N*3r*clr=-M_f3KZMWf*fZ@L@r`zp<(`3C#f)pVEyqdV z+F`~P|B{Tkzmb;aj=MROCN8&0`b*LDQX)79%soM1e@PH5Y>l*=x@iH^p5}6y;OH&u zB}Hcg7x|P(Nv4Hdp6BIs9}Yfv&EtG%>?FZi`18!2$hawbP-UH>mk{7qM|ZQ%J?V+N zf(Ikqg+HK(tHg3qBpG_fkIS1P%VmLjkWpgN9*5lG?$VHSoPAQZ}-?ghAQ16me=*$xHs?SYbWNG5^vG8ihi&>m^jT~UH zmls_oXR?POc}|^)At15j8s6@?03OzT{d+DjaI|#_s7gtcDt-MjKQv^9WvhT*jrLzZ zgN#j}jzH+!hn9zFlu??bygzCPF@5{b^Mc%{I*Q7>s#?3nXt~MQ z-qNR6)I8%1E zEIrNGq>`}rESESpGHe6ci*^;|6K}>2@iM~bt+Cnfa_CV{<$OTy6uxcx?mJe~Jsbyk zepZUc**_tEm|}vy3WYdIxAHW-dO)k?=B;Y_#7%-d(C%CHtB8E_UFkzDnYe>d_L<8> z%R(90fc$T&3l3u1AVYK)C+rYW_QHxI^r~|#{k2w)UqV(pwjIF8w-e|N;;)+-*X`_1 zz$H$#pmxs*U)aQ}9khg!gm0y4-fWKf_)2d;jlF>jBIpiNvxBknyI9YxJ1^|oZNii= zgIok@;p_CYkdAZU9#K*EJoH1YmzFnozq~y7@F|eITENA0dU$Ems_5{3NKcXz>rf1$iJ55l2EbjMc=uU0 zzYWNk1xF!CXnjT+d;~2!KImo*y1}0IvxcYdGI&uJ#y1J*+okK?z+4@Ezd(L@7g|pH zL>>i(1xM)}DSaA}@F}+G3L$<1vY)z}%#p@JFv|)@EVX^s(-Fi!c{G z%YU$r?!70s|2CfFLXtn8hz8tR*$NesLJaKT*m0bnI_F~u0_anQBh_mZs=Tpfc1xV@ zAGy@awMOC23E;z~v4dkEz476M#qZ`JZ0(TsYz-e?8gNr$Z`B6z7*nd?-N~nrXOFWk zY+1Yj>h=O9A;hV9MhJ(SI_XC6L{eFcRq@+a&ICqcrguT8zAXCIAE$Q^q00aN(zmBW z-WAGs1cj?7Ez4%Xx8~g`DHhqyDn2{4e;u1I}_$XyDe? z&7das)9CPdiU_B^DYiO8dnYmd@8x|Z=Iu9NH21<`VCCnR-) zpeK^LV!IHtP256CBf0xoqJqbZeK&E)qF_MW&PEVeS1Cc7Uf0nMhuR&ku=ZV@*lMFs z(!%|cIq748?q5w5W!wmcVzl{TZ~`r93;)r%^h8_{+*d6Wh8pm;ct7cQf;Old*N|ei zNI^87()Bp&KrRlQa=pz35tt8{Kl9-)T}K)GEM4NtpRTmy5fPp>Zi05KT5y&Ep?lPG z3S5gR??a%9BbKFU{HtjtkZl<^>5K*;j2r2>6uCo*$Mv&4$7M5XJ+KU=BczaNA=x-X zaoTrk1lFLXW&A!_1IJy6USFAK30qi_io@-=85(YESJ{UC51eu%bQhqo$v?X3swN!0 zpg}zrloZB zaUjddI4v>-Poe^WfPqio1w^o40Bi?)rvPm+o4l8!e8G(SPY(N>E$5cjzxN!FJhn`n z4thFobBEo>N$nl0JJnNPN#gCe!g@LrilzU-E}Nh`;JtCj%T&1-SNx#)pVRu?_=HW2 z!Fdl(@}mtPHqs7>B$GMR55rOV@tw?p^ahU9F<{9F(eKO0Li9-ac$e#O zXD(?nWRHNDtvfFwKZUAvYWgy6lwopv2QF?fXRab3E9)h6U=Q+wcl_I}s1ZU$Oz>Jm zcfn`T*yjX_Wy0A;45rXUbpi8oz(XzyfpS);YI#*C z&kBQVV~SW1^gug$=+K8nkM6hvkdC_nd(N zfzg)h4K>n-wqk+dd$~v`_ywa@*5-5n9n{UlhW@OEz8s})R`;9oF;p9Yv58Z^QGPJc z1z>;X&c7ZOtfm8aQZMzY+}b04>1uO%tWG%}0ta`$63GGianP++fSLjt%luZv;*U8j zMW5sT;ZWL|{6KsNwM`T}10;En-+uu!O0k-lkF~149d&;Crs%N~ll{q;SF=B5Y<&Bm z=SHn9t5qd``<7&F!v%Oi|G+>7QT95>{Ga92Z_6#MZ-RQ3mT67W!XkDNy!cSr+>jHD zLjTwTgzo2zt294rr^G>E|&!@OqU3ByugUsYXv_R%;WYErIvHQPY ze;den{34k7Y(S{Oy1zh0B$YIG6k?5jIBa?)fV7&y15@~EE|N$H7i(;d8i0U#SX2Z3 z1Edw036ZGDrc=IB+sTCiJ#*|$FI=}$7*QwqvEnT!_VmAufVq_^12|h5^rxkYrsm$> zZhp7%e$L=JHRx_krYm}56|}G3l1FETxb}joNL?A^ZuEWV)Lfk1LmjWb+bk@KCyA7| z1)_KH!7KxQq~ylCwcJlRP(%)qGBe-u*b#XYwUcpZDy=%Jn` zG+eDh#>k}ttAFOP>fglDe}cmY6rF`f#kRo_9Nn}|9F#l`4u_sNgpRS>o4TyQu5Gk@ z9=w6Vok!TQ@3?^72Sk{Xg6&eaJ0g$&#(eVD(UzS=>C-Oj0(dk7C3uBROt!wO1}dwor#G=AXx3R4`VYUD zQA-Wx3iek6A-S7k#MxWNmcmQfe6KKvD?=$_%-=#RPjOa{4tAS!@XHMb-QU9)1j)iP zv$-u7njogEVbv=U(_?C2Js;WgO$mTm@+=A^=X+GX4%$!*sHP!NiZ5pAHjZtGX0C8aL9=fZdw&dZ|y{ zv}OD}4dqCoKEOiv4CBn%x51}~1|xnH{XN)o7vQNNYd?`1`J!Nc-6ln_b%9{$!N-uBj6)-WVI0?h2 zh7BHla}3M4J<81kmGP!tNdoWg%j<$sKw8G+YFT?AU6{bsGJzThPLA08QF>a$NUVA7 zmNbXE-6co;R_bbE2ln9Cwg1qSreLd+thxH`KC?~*WXz6)3L*^C(~E=6X4ir?POsvD z2hNFE@P~sNLiOOwHJ3ai)a&SUtXjX+6MmMgiF$j*fA9(ci=xbDu4omWV*G4Jtg}HO ztC3SIBTwPet@aF=(jK)_`AN@Zqv=f6qA>?yH1(2w>?tZ==^C>rm^+hTiW)dc=7^Q1LHE{24Fn13K&~jBUlF|q)a8-F zWN)Hc3;D*6WLv2ET$5nCH56k*tI%6mXaSa@v>Hvn@S0dLktlF+=z|v@BzFlt)D7A4 zF~0#~tK}7)KxwoLM{s~vq$kD0z=yvBH9T~y0ACXdH~0tAG6!lB#z2*5{knsv66w8f zyc{@!O%77a1JM*Ayr#UD=W*S|#`9}SlbkQj`97rp<9}aIzcsyQJQA;k-j^Y1LU$_v zJ-CF%hr1ht4AnMFeB$AMzgxeg;YuGjeY1RXeVTxl2vtG4LVD*+;oqt>MH{}<5I&U1 z@Z++8zp}NNci2jgHI(sAMZH@k>|L8G# zH){oF!QcbWDxYOSw`>>aTfns+0m??`#iD_`kJ{WJ>G9s3xH8T`YfiFF8!kPY*+;)4 z{H!@j#F?+h3}2;7J(KeoXF+73v5{|Zq$^M3w5DQrA1272DkG{U~)&^qQxT4SS!YZT} zFk>~S7VKp%du4|avSP36pTm{TK9vjhPayUMkA9bA1Vg*FGez#8y8P-P39&i$>hbWc zefE669PIEw5)X98ob1`VISP%hqqHytQYP(HfoDSLYv`WSy*F3a*iD^%#Yc`b^}Ses zDU_NQc~}A+Oi1O5)E^1^LUy9!jdgQ zKCu%?*!m~Z=a*TVX^a=$+WiNiVB>>s$m(?xTJ*VpD?dX$@VEcArX4;chIE*p5pa** zr9#RY9PRCtLE|?QwMv6;?C<)ddsu<-PfIxU=6277UK_9P!=(d#~v%5$iF zDH}Hi*Vf*uKjB6-u+_GP|E^nu3x1)p4EE|aEgAHC+};~eXV(?JwvXdss8Y-JGKC~0 z>&yooVQ*Ni4D>lbE*J9!Fa9{I43dHKpX`+TVyH4^x>wpKmn%I(g3-l~nHuyv2MGC! z@fbYg1dTZOapH<`5A{d8y9Q#*-!iy^Lq`)}zB+f6lgdtS6~lq?OZ3RJ*MUF4WMws* zJXKKz=WP|6#6<*A@D9plm%ySB0J^a5rsuf_HLtN)mI`2ge$VR%vL=W$bywnQxfe~Uj*ThigbH$COZtoSeDX_8^x@o*4 zD9Z#dJ$+SxJ4Jz;QXmXkpwOi1hilnyAD~)%g06zhoo89M%gdBWqe+MV8Sh4DI2z8* zGF;wIqI63l5+Wih3bcSQOX)UDVatVqOhUqll6KfrnZuw_skN`|-BpjxpYGzm79= z`W^rGiYuyi4UW*c{=WRGZJCQfMDm^#zEK?gp$?9ha-=h26i~*4_Bc@afb$pgQ9tVOn_XI}UBMRK(9C!LVRY@pMX+3!o^Eyvt*o#5G=*>l}*Ymc=Cg|^ru{zw4*#4(S z@{DM~lAHuS8h$mh-f2`&7EO10kIJIIg!tk1YjuKf>8w7Q@C#cHF1&MrEh@kE$DU+>JUG*N zYpV_jgv9@i_0CJ8yVyz<_oBIbZivXGT7&9w#&bfkErA5}d>9Olz$sFkLNHw}X4reG z^ZNA{TM>BmN*K_8iYeU{V~15Rv}P1F>Xbvc_XKjy1xHv<4M30-=#Y@+#eZh_*etw>MfG`!hb(|~-Rb>t zyE&kQJ<9-WQmwD z&+o`*jG`x*jsRGs1Kr!|lsLRJgs5is`5e(=aR0(2<*?`yH(H-f0E=J+Q!vxA`=*MkTMPrdw}sPOVCY+EXGu^ zCjHD=yl5cKM3^L^g1{dJ^zjry#$8LB3mIIcx4MX*+Mr@JD8Nz)owM4ZjhSEt;=#8x zz-k0V0XaBpy;!M5WV50wJ%@&=CoXrM*x^`{-?1?`93-R0sfyaBC4J#R$x&Y0tzetv zddOUfA5^w(yyP&#x>OxCGim*!8!DihEP%!3q~)PDZ)@IjARoS&DC+U(=pH`6@e=L^ zmLXDtBs~zlA97XUYlk>hUGak4zhqIA+PCyVk4V_n)_LcT(~$XbtYSbaLg&X6&#}$6 zC#x$rmTqw{*Vw{KKe^&)uA$Ytwe&O?$4v@8zzANI5Qgr0jqvojgH3V)wS-HZb0=>e zbb54dw@lEbcu%eD z+aifnHVZL3&^Hg9^wn1%>H(42wk`q6&OE%XZYcyLk23O+*!Bd20(AwX{(bY->f?7R1B>m z!j#3}<88P8?&i7TBxcQT;DUC?f1v&y3ed4bK7`XxyMvCu;Lljd@B1<-k+y6vDW8hvu?smMS9T&cwaBB02wzjuhZVg$lj zu{CkqGW>ZOy0Q=P22^IU+z)Q_oLtQ|0jj2|EDv$)<6xxC$Uw(JuiWV~e^V4ugU-u; z$yL20TO?=!ZT<~_(%h<}%tQI%R4Y?2&flJYo-2=i@Ar+?G=N<8>hVpx{5}ZMe#E2s zlYZqlc3H`r%(JYvA_+=0*B(F33_g=AZMjKTxMtYz#b+RUQub6SiQD^fb-6a;O$heX zTHA*caCQgYc+JCh95O7*@lgm@MaaIu2!^0^r@75q6Opnf5mSzaneNW%SU<~t157|1 z2QdmHah{QUl=(PEv)R8{5?}_{roGqVcy_&dYrS@X_wv%k0b?>+b7utFV@&=nPvPTr zcFcHi!ej*vX;cmO~fBU*UJ`y;G6+uAnaTFxC5W?A);j z4B4Yf?xLjp&LSD-01uS;^wD5TIzMG|VYn+_7wqD%_YqiY_}PDl?WG*EGAy)>+u5At zux5OESBHD&(l>5F_un4SMtEDcuRG6o|&jW8yE?9uVE$t(9 zCfVOnLP8vJsjJCGrQJ(6cjGKMZK&oxX`rZy2bfPxb*wHPK|>C~!CRfrUm-GmPW)v5 z>zU&ZV3a+6p}m@Rgj&0lolXF`aAi$0AsVvP)HMKJy8)k8`wQ6ROmu)`svoQg+U3)G zcP^ll#O^lRT)iXBatX*Kt>61tV z4Z|JZm+9ggqI&cheazwegWmYag0n&C{szM-V|6h|X)S|E#7^e^#CuK_^=#c{Y_acD#?|s(EbSb%)9)=alz@wV)Yo5~xx1k8N&V6L|a#gKJq>1p&yWct3+0|~!1tX{NKgO4F^ zlo8A&Y_7oED&h{jP_1Sf1(`l(CPHZ+-Y59b^VcWX zGYQl~tdaK1S6A15bps#a_SWlb4jtrW!h&rQeT!U|-jzOf_7>E&=^`IVB0B=$ruqED zA}t%jL^~ke7nySx30T_vc{^Dk0AD*xz9)&UwR^+ur~uy0$@vSK_F$j@>2fI?g4$*&s|XOizGc<=HR9uOQ!*nh%B1xH4`4u1m*p`3+*^RNuUx6u~D>urizO!OZl*94Rr;YWqKkA(_A?DcSRQvI_FUEUXI2oPhbtOS< zB14jj!;h<=a;Q)o@O*Q9`->ba9+;UK|C28vc5gEtbjdqE2%gEQ;&*xz>RJ)iHo3{k zOSV^-yk%gOzfPPj3)pk95Awh<_%XDeISZB22jmgoOXmwO1nxiOswI%TZ@u(sE z&$&culNdo-8R7WXco~;`xIK7rH`T=u8cI6;X%KsCCI*JsLoR3*FS3_9fqQ}ybPFiZ zwCH3>K$5g2p2_e8pZ?CaDCY5e`sppgQapcomqz%EW7E>M`g7dGsM_V(ND5KdrNn(NT4qf z%5#JQuO-K&aR*-%p!339x5PeasMJPy93hOifT){z{!G*DXYbuKFNJQ6t=Gs!Iy2p%0Z|JHAt=BBbIQLL1Y57mrr%5CNKI+tb-wgHmS3 zH?H(!kyqm!i$~~whwI{8HLvJM9DrK6NOP{JiX7uk7R|}6 zz+K#7ySGGf#KXAuvRar_s$r?5{7QJ({GZEBi>+`fzwn_r{oNanl~UEAv|2vbTTzW~ z9$>1KKNG;B-`kJsnRh@5?mAu=MqV&DMH;8fNV~u@+(!x2ko~rU3-?$<82A!BdPOfl znO!+#6uU2D@=%i>cK$g(_9UU?Shff2zMk&w)9BUsJ!7+`+X##Z`aTiHq|uj(A=ZM{ zrJqFRe7RVvm3nJ$${dlHc+GMWw^+4ar3Y?OLoWy2+*Gq(6}?L?U2`+tl$+0X!>e=a>U$#I zye4;j3U47>6i`jqbx+u5Z~>d8Tt<`RobMb~jm%errs4>Q0Hb+yxws;JDf`iEg$+*0 zXP(wQ;7> z?>B!=EVS15?JH}R^6Z&)6aL--L!ZP_*6 zHlSRvTq(nz91ghg0lO`T07Mb)Z_aYcRMIVyHMzjm0PiqCdD_MwPqbvW8Yctu=IseH zMZ(@cA{RFv5%1xRzyln($+f94?j>O=a{Y^PD*WIH2yWaJ$N20skrpe*bj zX7P7J7UKw&x_(TUf-uRxI5RyE7#7agYZWhBIUd;^pc-Tj|Y@kFjYh`j!r9~XwG~b|>->{>dwW#pQ%JR&E zOw^W^EVn;}`c=2$rVKtDJ%|d#ZjCke*MrNn5xEgCJM3|3a<^ds()NNrlnd|uCCo_C z6);&f>wt)-k??X529@%VfIp7=F4@>xLSGqrVcT4c)_EPZC@i%1gok{L&+&uR@ z68uP+bARBS?3Dka(q(=|m-<(bU33&|YMxGPdD^J|S%n?d($aVzF`KxptQxs=Y zHILwv_uYiXwQX_1HL6fvGMGNvUgcmno9wK}zOO+$_Y{4#eu`6w-@Zs^f9d+An_Hyz zjyv?gJjuc#9hdYDT@y~vjL3b;I0CUS>P2&}nm0Rm`hU)sK#BIF3~E4rlSQtT&3?A!CVt;+6LKqCS@pgxr6ets7;*XhpWPuXG^0UdnCb6 zQKaAdDaUZZFCKCGFw)Ok1Z=(rPZy$Fhwb-f*kznK2|yZ~g4d0mAGze?H!P(I?-pS! z{8Pquc2}~Ny|{2H;_9AbVpG^{BgPi=>#XvCc7LE!lvtP(v^yug4GB<=&~Bx6~3|d zq7a?rzU*oTGz#!$mk?z%@CuP3E2mZGFtrni+2ap+GQQBTN{uh?b2J-#roXJgr_Db{ z4BH9KS@47Am%PEoj>3@U-lzW(^OpiT5?O4V%*x3%u8n$SDsgnQkL$ZNMSvlod-HTF zPrDt|Y`O;?HED2m8W#5eA6C6Dw%O+kH^ivnyhUr9QwU> zeF)Q&#RnJgZAD)cTmEtc_oNy2;7~q0LGG-6I&=xO z&tQFeSNw+^{t-yGkTcZ<(M$Hv*)|w?L1aMc=zOP0>wq*juSln<6Va+{v zopsFm`D(`=2@nX|D*~7B49;O4R359uA^ixFY`sKAX6Q{5PO-;7K_01qA;d&4f=juZ zPsPDi&h>ZPL6-!_O5sV1UW&u8=W#1JXR=s2N15zXbJ^x(lq+}uWQDHGe1_r)B+gEV zxJHo9-R;cKyUmXjUP#=10hDeg@|9iyt{)AT=#zV3 zEU9P*QaFf;%Q0&&8{$_-&w%!$sREG|$1|CCEDXwgv@Yf8ahYfIU1lRcpz$-Id`z7X3~bP2Be+k}%(JbJtJbQh ztSTvdJ6{ee#q8(1-!GxXka|e%Tq>!ed;B#G+3EN;<@zp9RqvjI{GN_!{u7GT#QR}7 zt=Vv%mKhPe=5`eYwiA`0I{74HW`%*=S7TT*rhT=MZgWBV0sS#?T=J|r{XBnp$j8{GI>c(D9&|lh}J8M>UTM93;6lK`{jh$7A4e;$!4|Sal zpwso@$31*SxttTY+t)Cm$5NmN@bB*?Pt9FuXikYXa9;jzX6E1`W)NMoY6I-x(8~AX z?d!lZ0!Cn3@61ey_WIii)`F})n%Mzf$-GmdMfvR@Qse`$p5oXkfELc*mTLF2^h^1d z2ZAE)7#{-cS!5BeJVjT!McJ*V+ZQR_(*;x5fvX=Mna%wc5dcZ!dXu2+&DTp03>>gS zI(Rf3>Kw#R@pd1I2>kD=j)loQ7JElKhE+HYGB^%;pSG9euHJ%VJiDXF80LN)HzAD{ zFRL7~rTo1O?7x@(%s|6LQ`MgExayo6nsEgchrTm3`0*9(NuRNM2sjW{CZpR8lCooc zF9MQ`2tFdDkDh-gDD#MAl+~atIcnum5DuOl9hIr+)vAV;iJs!6OZfC3ip-v#u$_;* z;vl~bf2vO+A#^fm3G0vrp{V|9sn*VL^IM%o7HvDsSw>=?1R-%S>vI{M3ty%k$5Ec5 z>tRB(A8ud?5ut0Ul0se5lv5f`W%Z#zmbv>Rnu6m;E1BHUC6&M01IL$bHoL2Er3@bQ+7pJ6|~8p0Zx}eYRD1ovoE) zJz~ibK_{2?B{j+tFyiYs;fVu#aL3zU%?T_2*a z+Gj{4wOx=prJ82I#`X+hjY)!iH+RJBdm?QqK%t1tK2$<%c9oUL-%xohDE!PxT~6a( zNv4SicfZmuh<}R=jnYiD)c|_9_kYQUFb~#eGY12MDY_}`9l5Q~Ha_IwKE!m zP?I0@mCZ3)`It{8V*B5N*^pra3yxl&-+SfHkmvuHXyDZ0?O7q19 ztB*ZD0>2CiDEXjYpF@N+!t-BnkevDW$ zqTelJ2Oq-j{IgHEK&wMbZxnwR^f0`Zn_rrH%tTm~W|g15!gUR_aJs~1IoBNNi?6w6 zXMeqc9!Q`Mm$5oIqWBr(w7jwZpi9=XQii^u>ifX?5$cwq|7_vuzSx~y&N~V#2WIWXKxn2}?AnT? zX+|?$NzheQZV7(Dx{o%?U8op1>;k<60qX6kE}&H+AqTXAs!f{>s+mC7-n+%F2OQkN zrQg-B-k9XCgbAhoj*SCFi3)3~0?0k=!y#xF4ljy)AsDPeBMWx>4+4%y%Y@sc9pJ^$ z3@%+9<7(y<>PRN5e-vG9d`Axp46X~1?8OfAGem+LhiqcywTPofuq7GHYafo%m;z|W zHZLE4{e}nFop|b%!P#4m6>m7*uPgdh-n}@mdt=aC3H@(8FGz>FU58Z3Gy6jf@`DGy zEb(P*2K9aXVyw-Mts=SdxCQ2X=Az_t7R$k=SGSFO*VBI|_dU3gj4?z||DNnDQ!1+~LiUHrvF)c#H;~liOC#whLv|unC>pv26Tj<#q zd`uAY!5C0Gd}ZG`-e*L}lNbGmxyKa0QbCdd?PtMj>cruvmq*Fl`cFOjVVZ1#V$OI{ z2(TwG4*8XJ(x!RbVr&c)iv*2a2Y%{-E>Ah&d^%28$!u}`W&hHSh$o*f*L-ALjK3T?rO9V`S*#z* z`-9mK!5h)n`aMu}ZgQ9U{aooyt7c4K)6^sS_Zoeu2)-uCSjs}O_Yn4F9Yn6~nU``X zTY5GoGz-OnPW1`rt;{2P;MP-fPBaeXoGUjm72DST=>EFXWojmlDGM$H+`)HIU0gGl z+1go>sTbm8_!s|4q7{VtcyGE#0hlL-GX=~c+D_9EkgWApX&wBHKx82K?1#7YZziDu6$YHS>f+HO_B7*5Pv?AIc+qAS6SQA;yHN zN?}s9(FJJpI)rq;hce5FC~pAoFa5@TbK&9t>LY_HUsU?uAr)ju;(buHd;*}1O{yl} z+Y4rz3r*gz6Z!!v3AGhoS7+r~Zu#(jt#;ux*2UWPZrSU!p288NEjakz zmR~eLct%{AWv5YDMc^*T;$hN>)Cb0!%@hd8u66|86VuJ;eXpFw2e z@1HunE*X-=!?{8`_A2nI+m@HZ%jZy2>Gn40QKAUxLEH0V_U2VoN$%494E5gLXi<9N z|D)-=>Au4jgpf1ep zNBv%dJ_jEVD&DrY(wAyqvX;<08WedV~%D_j8LWqW$ zt8*uG0g_Lm^CrE5vVSKUFJq>Q8~<&ir@?c4NC|d67Kr`Cnp@Hq?Ed7My&v%7oiH~9 zdM)&lKQg!X|74GBDEAM= zdo|RddXxM6EDs-?Ap?;B2n^e$K@|*7)Wj-wY_imjhWxSQbz17u3n6x0GUL6$#s*_c z#!1d5&B~qsKEVH0l|Ns98C|DFmiPNX(0k+{Po9Q9v?i$L(|B^OoT=4Zb6&aPp6~cG zB@YZ=??EH8-Z2LFNf0Z3*XJ$hx1@yM9C_1GtkIAwc$u^=MQw%DxHCOw!dn4 zV`;=lyBzA1wx<8wcZKN&QS~9;(f@e-wA41Cs*sI~P>&e$6s-CUeXocjBhGBw8&|5v z&!4+b`*Lk*z0eLFZZ4tl{iLl;iG&ef7@xnZu7}HYL1K({L;}Y>bjGvO%4wp=AE+<* zfN|3OF`oKux0rIe0(y`rGVj0E9}GCNM^M77l}T9G{v1zd9I}*~n%tPz-xIW>AM5%8 z4shSjlwb(uSur_c?t%+mvygr&n`9|X{4mFzn1Soa~5Rr z@Vnp}wExHk?Fv|ESuSIkDs=BRh3G8j&Tw?<|1L|| z4$mB96s}%B_y`OO@Q^biW6ql{hsW#Y%)fmV-m0sfqwB}64M8oFSPSt%I6FbG4!-RG z4_m`s) z*BNVCFk%DV#U9Xym;n&pia8&gz$>Is?#c2oSEH#(>u^ z3)E8a_3YaHhZVrAF^S@MawO}UNcJFjL%eyNQ+1ndSyd3l4p12MYn?1!KY?grS?4_H zWzEf{Ezm*@xYNn*&F3hI8O93%t|X=C))i+s@-EC=w1@z|MR{^tEfvs!`vO59JkVQ4 z(YWJ6i2a;Uhd|}6TF$>EXyvC{6b>QZc;h9-;2S@{Bi!EG%TaW|rM$){p$J!G6xt*d z|9qEAb+?ojnv`gwPE=5!W+$CX1*`9eL3B~snPhM?=SDu{{zvT-iB;AG4h2DYLcOmX z{Z#~%&HwSr+sn~gv;i}x&GUWA>&XI8NbLN}P3pD#LW6)l>A;yGh2ur1!@3{gI%DMX zAiUj^I8Q8k(PCjNv4)@?-*Ysac=;m!&as8CV18;G=O`987e8-9kV3tJ_tEt%SAVa9 zu9Jl)Ykzfol0XNc=vwID@;2l6gJ|E1|y>KjJr=0DhK*K1M8qWR3(Lc5FRvU#|}c;-Q9 z=w0&nO`8g?sC*Z4Ys8l=h;1Yi*3(;PZ=1~v^c=8a%Kfws-Cf-Wufh($ykQd~SXvwP zz|y~oVqgG!+W}-gw41u$oj6;V$XyiT)ed)*t_=SYXCcb z*~EG?!1Q};Uw9-9%bw)-*OCbzu6|)Ub)#nSY2+5pe~XLzO!8Z@_dA?&b?vd`wPEBR zv|%r>hd+P7YW#&6}HtuA8fVpTnY+b zB^LNojMMnRVjr31$ehDc1p-AOK}i<2&5SkNj^iSQdT<%AvltX5Ch$Sqg;Gb}LcLWc zUAbGS^s>)tix4R94cND#Mq-wn0DMB_u_Z1$3AK$Mj~Evv$x%!E(Tk4{L+3?1pF?*x zRmy_$nJ^@Wc6&$DB>Bz9bB7cP+>Ckrc+F<#BM*JWU;`CIjApP>>f|_bXadT> zg?~wU?F1nUiq|pQ7sLpgRlEccJ^X$NLr=~%MiEkwwq5H@I$SZwXP$CA5~OMp`=5*FOl}rV}6ilR8Q2^j@{z-|+SQzujn?+}Urbx01Z+ zbgqHyjwxt;gPpKLr*=^Ur?g*64=b-eJ*573zZ5TXm(#6X5FxJ-B*=i+T@aori1Mzs zL@3hE;To^34*w`}!**P1(t^HQ7*%xr{hQ`fha=h25^#UyoEQ_iWyf{eBQtKHKZq;V zOw7TNcyyydNag|U`av0B&o{bxJt4P zt(d_2D}A~9_PB!YP&)OS+PUNT;HFyDe{QFb^Uneo3b3VB;q4yHD~Z87$Krw4-QPgw@2y`|^^yX|H#gg@wvl;<0)!fv%nX9I7`Ll82 z)sI1}XeWrXj2d!-$(q#20B=%EqEiDBDDZDXnQkhhaY-nf0Brq%W67cWYUM%px7$xz zO=1QTkLO4F7Jz7CWXBF5lL7Qd^;4kpRbO6oEm#*;@-k1x(x;1~=Ed*jXaHeP1@&qep-F7K_cewI8~oz3z=; znG&88DPi7bCdmXN#bfFpqI-_a;9VY&5d+Xr=)>lE3CmS*H#|>GFz?=I*Zv1% zZxfDUQvUOb6Ci|EQpq&R#zzN3u+n)wQ+-xW{5P@DOe(h*K>*h!#|aCg-b5OU^WQ2X z$oweVHhd)J_O(*iBnr)@9lf74{} z$=F3<<>OfuuiC)V)FuPG%AuxxB?-l+%P(P77huy~sOkwss`qXh=-A7A?hnS-#hU%fv8Z0}|!toxA(=$^H!iu{7;e~k=EnqtYClu=}{tj4RLa!;IvAyRh)&x>gr(OJHnhE zlPg~B3O%X?7C&CJ;6!cF^Mv-)1;0QQ_mL6z7;G+BoO9R9<0^RBR^KcMmJ}Ng}*% zC5Q*i?O-?L@*j4}v?7!@G>`;}Bills%l-SQ_&gO{uvpuC2}dkf*5dZLG77M2^&X;DoTpWK3OvB$>h;?iqE{dN?8Dh1({5Dhy*jQo6G(&8d|TB>dp6dph*=GSr+>EMzHHPiMq{%ohLN> zD(*D2615MHe&A`5)=dy3w}NRZf%8ThH2`lb*>$l@O zgRa>y9+QI7L=e2-)5E_^i#hqUo?7<0Y&UF}glRra*$@lX-j3zh=;Jy&_1|6zZ$EJiC`9q&FXfe&rhv;pSRZDhr@WN!Ns#DF{b%jFes?3|mg||beQx5(h1Y6xx{R)|= zqUBs^Yo{b|Ws2m1t)ifrTuaH=GptP}OEL$t;p1J{^WQn1d{m8bBORTEhXtuXNG;v8 zI5fc(7(in1%o(;!-w0xi$>fp(@DPhV@K%+1QR&6~kagKjZEqOGzoo`G^7?6$$t_;U>!$-a0^b$zFsQu+MDA2JxRlnm+lboQq;TLV*S@`j5=h;2aX#0?CGfh zoT*yuyP5H%;x$^oPYZuNM-ng2Tqrib>?l`XAQT9Bc>Rd3LHSBfOY>exmjLF3Q3Y49 zDy@M!w2fU9MUL>=*jzHlS> zao*T^{xUlxp}6<`zTS5bSh3~L`x$ou7c zk396=ZOGHs6pHBstyv-dA>xV}~X>!v7x_TZfO^p3$?|@-SkjZ8|i+k?bi@yg3 z{LYyXh7w^1VkE;!8?6waH>1b?-RjyFT_s~zx&t0APRy>l8IoXL_DOM)L=|Cqw&0lX zZ5LD=FEB>?z%Vc9U$yjb;!3#irUi@|nKUkD zS_<{?;VbX&r#2LFOky(Dc&3N1*WsG^|hDSZ5^f zrtZWJ`2&_TLu)f&^m^mSsu0t00i9qBq+(!1R8xp9x?CrLMikgNHV-VBusew&KY@z@ zPWEGYqKm2o32+9ELFgtRgoT)IC`||#c^Iv?3n7dpZl=^=gCMg8dnu$DR;ldlio03+ zN$Ckhfp;GUr%)3YIAe(s|8mQCc$5L@SPP#XpDWsI^Y=190o)nEocJ_6CW59GPtyOz z`Id9O+`Re&dv_6E4l@(RqG#h}pML=LTN;`@L@PpLKp+wM2H^yeAxArW{fDjmNgvC7 z&O`q>ToIpf{q`+k^UNjlYyH3xZ@~{@tJeMFMMa z)3=ZrOmWSx#6pcn{?*0c^BLd-gc!6Fjx0O*_;iG*;J$1_)ZuV*)cprIPUu{vNGnw2NI+!pMrQi*amVT zR{8zj18fzsa~%6_yg2aw>nuP~4V>oTa?hU>cB$|&nNNuGbs^IS;?J&^>b zi`(0FzGi$;h7QQG1ZW*2)p+`55;&>sdlYiZfPR|rlGp;qTHyIRxP*d6_3_#zqQ^EW z!Z{5wWCzJ8UaX5YGFMkDZJ9RB*U5kGBk_W+F%muRqb@4`*xuy_elLkR)B`a$EJkAc zbJ9&6*t`cYaFth*#gD#tX%^Pql7BuB(m0c!Y}7E6{;=rYZcffRqyN`~>APQ`JF2Yu zZ=KIrZ6}%g>wbKm|3Ly0u=r3k*IERL@=^b)nI^AyzMgaWQ1Yr9w;;uTS+xE;wZ6wG z>mp~N{^B$%=ekKU8#{tQg(@;(i(h z9R4XGxq158icjlrN!{g|w|iC@5GP+EJ^DMydt6B_gML`t#Mp_CV}%XlmY;o(5km!e zW~P$eb3iUXvIpWRL^@MG{BCpke${flq8ZiSL@3xQO)->4K8d1d?URt_2ROfO+(0eb z71>GY-Vq0y43`))szf|c=oy~Y;bZ;XcL#h*B!sJi`dv@-TOskR?}glM=KYyN+EBzv z7~=6a4)?ojE6xsCIA;Dsi0Xs$SYBWha1wM;Tc{z%k9}jg!42-aLIzIykvPvR3Sg>M z@z5W}INS>^%Ep60rwStF2vyFEyD={v8$Za8M^)nuNt~?rceMF=2lP4C>eNpj5;=7U6#c+-N8 zQ@mhhGhe44n;T#FP@Wjgj8^CkA9-t`=Jm*#NS6UOeiUmzGyu=b@iyJ~3u>rN(pcoW z`0NG`hxSRDxvQ_~5;iP38KTNAb=F80}2Ap9jBynM@#+Fd$?!Ot^*?d3A78dsQRk4IF*lt1`A4S zmvr}N~VrinDuWyugp`}9OnsGo1hE=?3 zjAOjxcZ~S&fQgxC5^sX|92P!~oeK#mZz<%tzIjP?IuJ&lTZjlDoW!{Z zM6HTrX1`ETiP1OGs}e681Kxh!-9BUu%#r`*%|P_$XHh1f-OnVqyb_UX}GBB08mfzw|IM0`#yAM;QFbZz7AY30aDy!Ie13y%Pt<9MJqe< zaU32`l}c1dqZO#p^n$;xubKWmftD0G>LTCrkq4f&bV5xbj3* z9#v}$c#T$;{3^iTQwAe%4PVm)*^CNRB;f{JS)J8a{ZXtjefeRz;sws=epgOKMFlTz z-0Pb%lE1~p8oSIN9!02}_?i#A{mT@y?aefpZ7qqm&O#k zffY}9@2rDm5bxKd5KatUg%%*oMQyIxFZMo+?O%f5@&%HrkX>t0$Hpx!QJG@rv`jpD zU$GZ7ic;06+zzlKSV8xl1J}>`p4<(s16<0p6QqOZdpJ)Oux@>ssb^jx?IK{5gowsq zBGdKHwsG9KNc(mFR_o@VXWkXvky@vbJnXN>PE)A*SoXlV;hn|-_-*(~h)DUh&#h2- z3(&p6i=Nz|nG_xa2==RVHhC;9t@xC#3OnB`uHdjNqHATA=rESJnd3U~ug zwXGMY(k_ z=wS&}>a1n#T`qWYX;bM#MNN-WN@&Lm#5~uzt75a6({`VnJ zqTfAt)6`D}=^;8qSerjP2Xyg9v_H=;Or|Y8OdOuao;*1%MtVN&+G1eGtSIpo06Rzs6Fd^5 z5AnbF3;AK zb{S^maTIMnE8q6HU-lwPy8%A8MNe`mUyKKROI^7Jtlh-s-^~%h6@he-pL@e`&ISfx zu#L#N_I1^(8ywYRPvvbw?S;m7lF>@2OlaY)U%NLU6$V!%zym-6M)uCHeJaSA0-?6a zS5;{W9PXju!bjQf&IM!U4IybCWNrfk;cU0|SXe{^(Tvdt0gf^{impvz&#q=wrE^4J zxr0)aHj7n2b!TQWD`7nnNT9s839|~s3%%vbVSTsX#3ed>_#1nz7RsXj^? zY3MK3(NpdDx6!lgq~LytvgWODf-vJBpU4L}C@>mt6(1@Nwqpz|GSYAuzC}hJT=5!)3mOLc zJ}Yj;_J1MNA9=`fe`)(@xpx2@J+cLKf(W%Q z-63Yhy*H(ACG1#I6N%XZ{k}?aa1r_pByHAL`9qkgW=z}nTFA#VJ!0Y1uRrZn`4 zj=On9WxQ;^B*No}%hR~iCz)7vFSGkkK-LvRU^&YLPj^!#anZ8p$1H3>ws4Cc(gVAc zzSr01le2R)~tsPmf~=s z*HkL9{Y#59C$)V|w|r zAX0|qI8RnQgIGeWeOWKziUI;+hGV{k@>)_h6weqYj~JSe&G@@#x{1iX8k0e;e&K64S0eIs6zBsb`WJq0db& zweEp42GtWgw?sOv+wIUynwKnkbUjkvS_RyP=|m7m-{-}1(-rY3;uy7p&K>lkVTEtM z4ok*8-cU1CMYI=M>%gS;7;%3M+ZIs%YdV}Ewdq4wH%|*FJ=|9@w(c86$)t^6Z`}ka zR|j0&2r;;QYd=)}!S$z&t)a?CjNy*8jTd8n+wM>SX~&+SGHkSoRcEQ=1`4E|!+D&g z@xTrhhua*_*IBM-^<$Ql4rpHS8lkDON6CrGT0078rA~>UEE}dzAWVcv7z%kUAWAtIiJ@r)Oj*eUTl|| z)xN5D95+o7JE`me=0R-n6=_ zv5tm{)^NVUtbOA9Mhx6u<|c1c^U?K&qcm|^3q}6#ey&!Z7Gd+z|6q!PxSPtX0WG*h zfbb}uOQ)Zf*a2b>?r;P#SqX<0zpYA5p>BgFAsxq8xk$SnOzaVMlbCTMUycbvpFC1T ze~IiSqZ}C}dg#uB>j{$J8FY&u@8Nfqc+-!|XLa)Q_epuZ)!+oXc#POBBF8#viDC-G z_+v8MtG*uRtcgV5QI~SXh3gt1mH1ufCD2#^!S%Xg&(@|qZVubQy-kA5PaL8K_pp076FN(=#76mGw$13SA|=2~gV$-(fRSpsb}2;LE!J zyU%K`@hfUCcs-8dvCFer{M@+J)$Ms_VtH6+vFL!4bF>clqersGy;}cxlU>0YyFnWi zZ)gInfXhsrRW*(NmC#U(ZW{P}h(-4YAQnUwbh`2cUPyGy=%=l;CUM)_X(VK6Zr_c` zUvIpde`|Pg0$@=2ZtU`eq8?ftr3%f!ly+QyRiB3_S_bgzwo}8JH)v~eX%{JA#@6OP zqC~>lJCmh}K5qXhskgQ!N;zrYTk!a|Rk@IP&t(kQ%&qq8Gp@Q4uj#unjc&Ca$<)xzih0#JF;3O|otwZr?w}KmpY=-4DfYxFWS7^9 zr1}DYsJB^rTzyULE8qFt_o7`-8+&jRy_x zDZ=7Upg<-LDhm_^Z5ME)NsCg_%h}mD1N@qAbOvF^J*nhYnkV&%YJkFM?l*M>e&u`*|wdILt}ExNYiH znT&U&gXAA@-63z}j)m;@3dt#~Q-XXRvp$Z$H1niqk^-SCCiK~nvgs>$xb~K?!#nh( zkTOA${Z>Ge8sMlqm7dOu`QV#9xOm2n{tuR~?D)0JYYSP_bSSQ~$^6_ZKWsu|;b;#Q zYxw#}t)lbNyNjD#(bDC8V{vj2-FmaPMBe81Zr>hXnv3oz(;tMI*Wr~_QD>-BCjGmE z%9!dIf#UFDFHow(7q23OJgo6#?iATdj|7E_j1RMBLyNxOrk5+Mq^P|BQ=1B^|NOSL z?7$1}TiLlOzeV=EIpz|6?6#)$$YYgeF0WO0`Rs5${gdp3%cH5MHmu2p4$cY`idt4$ z>RDM?N`uyrhyKjn0i&)roUJCFLDeGR*T&4cX)ihI zB6?WD4(ae>gA;pH98d{tnFSqD3ra#QYa+<3riLU&tXLdSfwsJSZK1m?z~0%S%} z3GUL~R%kB(zCxE(2m~Kav`S^54_nS7e@87zed^7l==*(Mt+X$K;KI3q;xBe@4(hJi zaTEPui2b{6f6tX{;#HPCy*PqYCdZCWvSK=ucx%#GYT{ygf+ zD;R%i7@My>iiu2P~L$;0&~D`{nz|GTX-c6 zAg9s)iqyh?{EM#kx>RR@i2*{h(^M}p|C2AfKU@BSN zz?7k~e08=SsZ1D7DdpJKv=-gUHm!xUXxX|$#ka&uEfyA|hb9$QpP@>kgv@g;tffz# zU05tz_$~%dvqIX=1F4~n8x&}(kR-k{WUDvvAoD)^-3+#`LYN$jwQIF+*s(tFL!E(p zJ|3w~L$nF!Ol3Tc=N8}RnRCu_rsJk{cu#@?7>PlLWkz;rrFvH8=q$@*43G+Y=J;L;v9L|H<(aO0Lf#dK(;#`iJwb{^9{0h~FlF(W+hMY?Pd zRXO3v4R;va*{dcFT%@JM-T}4fdm4SP;600Nz&9~De_vwofacgXz8u$sV)e2Qo`l%! z>wP!qJ}v7Ewneskb7;SYjumVCzD(;h(%fyh{xy7Uchx*@P64HWzu|IWO zIbSd37ETsGmS?ifuLxEqY%pb+W^KzA?}mywI07(!6?Z*?*%>wi%pwYid61YQ}VJb3e>O5`@HaSNQ8i>~?*~Nqd)o9e$ng>PW;75)0Xj3?aQW~cFrzrLWK`zMzd zLJ7+kX<7|O&?|f3!Fvilb6y#pS^gNw0GMz#@q$_A8;E=^I}a%7O@4u*^$_aupEeY( z(z`|zSRn=&&#hQkf4%rjH1J|c48$q>s%`;V4%XXkL(@1s|AE!%4hft>_rl)&kK2G< zn`>0OPCM@*5Iy9k)7@IQ^!A<)xj( zT9(@<0uBH@tW-y8+|AGO3nfy+h8E3{i=5?_=q?no|9!3ae^1h-Ppc@Y(JPCC(j|?l z(C-TWv5TPb4NZ}_($M#GY=#!g4vVBMTxttKJPh3PWR$w9w`F1VSIzXDG_I6iJg7~y zpbBlD?tSQm%O-XD;i=s|uPQDod8J96WG9<3gZ0&0H>kT&U)d9AVogRFENa%J3^AEf&-7 zyo^Jh=xr2SRiGFik$S%R9kIt{8#?*GZ#-?OU%n~s`5`))3wg@294COU^oB!Xm#9Ms zI7uY$VhKDmy#h(5e*B7%&{htd@I1H8@Ro-zMI}ZddMQ89sS=)8X>GyMu0cE%(&16% z(!dQ~Sc|ksuXu8Bm65VhfLUsQk3WcM4e=Xwl`B$B7G(f{lf2@)hEML$=8J!n^)%(B z8|XCekzDT}KzaVm0adt~lO^?D0O;Qar~83nmRlkEyvX_4_gIZ!koht{LPqBswr;gh zE*dBu2;0{3g_o7ijxcSO55B>`0H(aqJI~MqNn5=c zE8oP?;98JzuVtyinM|KXVmP2DlodHIXZB;qBX;kO-X+Oh<|Y;1%aZ(=IsatbAw6E1%@Dopj6x>~S<-;*Q0f_h5U_glcGug|2dA$j`CMvYPk-JXmy0v9`K@>x^@sM` zxo>Y061p(DRVc5=GHTcZ)DI>{y}B>#umdU}g}=Lx+^`}Y{a#Bw_3l=0ToB~aHT*1q zvojH4FD(Ck$a@PEpZGocz4$IP+g#zpxc*c*oJ=pol1m4>o>sZ#whWnzUtpIypoLA8 z3ECRe(eeR8)1=W4D7bgy%pnMwtKhO}gI%jWKEfHdfWB{8J_T|W( z);YT?7c12zFlRCA&nsSS5wzV~kY-@C6I=!2yq){O4s*0f0--{cBxVqNo%2rdK<7z& zeT!$%`}==$xgt_kJbDl9!48OLcWGfvKMxKobLSup?X4P?z-q|mJZ5F{q3^)3UYA9xhYGfITkW-3g+X?+v%~hnEyIqCLIRn^eG)t zJ%6A|Pvlk1-@ze%>3k$Qf>$29BOuprXn%%|1Bg`ZQ}ti1b)^Z3Ah-oVXM8e`bQG~Z+Pqp z@ibv@uJ|gJ0%f;YRAXmD{@igt`#oUv{#5iaAAeaRxR1xVF!|JXq3O3tzA{ zjT{75@#L(GJaAZK#S<(q>pT_H28Aow^vFhlGt-NiE*h1n{!7spJGz0n0ZU$URJ#=a zv^e1;wm0(_4?rpyuHTi{JMhpLByzH@`TP1#<%#|w(zr8f1N}k(=xzHaq)mI@Tm13D zctlPClgF-FY&Y}LuIN5nSa7I+`jC~9Tussy9M{A1T?6W^+?qaW;S8Sg#6O(<@Ig4Vv^uVJ(xgeF zk!E-wRV4s!MX!K%PYn8DXX?8D;C0WMLtGZ)&?=)@o$}PYqKv^z7|l`o@LczzF5&E+ z;7jZfk=I-79dl(1dRs~L5{DPu&(>$Xd`gfwTsd_|Uq)c4Y`J!$s`YmnnPshuh?l{4 zG9N3q-PYg910*$edF1&b(X$*MB~Fs#B`Zv-X~iAy zEDDp1fCO~u(@rRUltwmzQ(&e#vYZ(Y6xQ|?u5IUffDbf-gppKxSQMr*Jn9u9L-89s zc~LbL+=Oao#k1kH7Y|Op{`IUscS|I`PTo!Sj>iXg$Pn`qmSE|FYqK$xn~Swej+La@BqrhLM#o!oH@OmG*Imw?n4I zaiOA>a+>hcm-n{*zi*8tOQS_w5KpFY6?S9gogm`)eDftB;4&80x92KpT!brW|0(YA zBzUj>w)p{zMcDfQAL{|>Hm!ymhI~9>A_BHMw=o>0Xj!y=f5xJHBTHNR(nUBcquM2H z^rdH0K|@e{mFBNv0o2#@`kX&Heg2M^#JtaUh&<;rcgezk%bOF6%?V^K1pTS=jVJ-P z?sNF%mCHW=B`Ff+QQkm8vG5IPnzj(LEJuliwb+*db^8Z;=ILOPx2U=9{zJ^-6gNBL z;GTNojNbk3~L5%n~e<9DkKTYL(^OW~Yd`(N>}(wIL?zSc$rKgS|yTp2W3 zx{#O0{34D*p!RY-!9@Bw+s#v95J~CGcGS~R9RvqP371Dqrp<-oCvFTf3?~=94dOIO zc6&t2?&qoDriMBUQg7(z!=41H<(4M%5kxzQPP9=rn@KW|h7q3i3$_wh>b zjKpD3%~NDC>2z7WQUam}haOk|n`O_{L6OJ@q38V0-)SNe_L1*{Wb}1i68q)DM~0>4 zkWtuS-;`++B%qmSXMj8`Dz#dR>DGtptAgXK+vL6(QJ#EpXL6E@CjLE4$5II^w&6F9 z1~S5XK0(r{`?!P2$R9pUu)e{C1PDTZUk94oYMlQJA-}J_TX0@dQ!=! zoHum+U{2H@?+DnWp2%c?{Wzpz+J8#+tIB84Iss>>{`&%six*vR11Icoxj({up*8`k zoI00KXiYiq-te;Y@6#zz4rLEe6_!>L3aqDij!|l%WXn(lH5WxYr0u_*d=jqbA>hSo@H-U!?I#e*OsR^K-^Y{Cc&Kp_-92m%ZrYR2pQc%4?Q=G{^>! z2NZc_dz21a?kU2l*g)3)$=Toh%YFKcw`~B1~TL85uQFDa=VQxhGcH zGCU2=?&Nk0K(46P1@wk*9Y^@f*#*CsiOt%*G2<(}RUF;Kk`K0+AeQ_Dq{ z!31c7=c`Xc{;vAv`P@eUdvk;3)~H>%xnAcLDm*px&;CfA`HaPz1&V%Fq zj@aoYzr_9{9F6ULg?^d3=C6Feoq!%q3vcGs3nJ@^=5sPpz;9=p^2iQeJ(X_}58jNr zG$!%oIA7KDGhWn2dGT(4Xh$%$3!o2Dj!07L$Yuw8{Q7-gFYBlLGsi)D;Zake6SJ=F7Pxh+iaEPJ_ykH@u&2Mu^2GNK4980+R?bl?;GI>zO4!Q3aM>Aedp#C~sljX7fKjMT?HLMiVWx@l>bdDg5aRKUF5M z3r?=C2NuU9=~rUf;y+L|HL!2?CxYuZvu>L(Q3AC4M=?o#d)l&uh#1+1Dhpvb5tAaD zMg`>SchRW)13~IIKJ<2B+653NCWk6t;MzHt;OslXDi+$(E2Y63zei$`0*L=9e)l)+ z0B}KO=E_H4CPwMVvTfklI#t|X#5)V3)#sqqI<;K4t$LY!9@z3PS$xA=m}v?;_W_#k zR(nL6FmU!pxz9M30(R+GbVTz2K{Oar7<W+pQ`pW$oaSP*f5UT{dd=WiX*1Xvtn{f+Q75XmY0B zflhCxPlCNmFiK>_a1wfs%4N|i2&7B2Tw6yy-?KYxZ39MCc0*QmBmG_S|)0z3OS6picHq~ypk&$*vr_#xwwvAjYk~|7VLx9 z=PEqmk}Ul%R(}i3vt|<=!oSx`xqLeL#xG=SS6{=|j7y>y!}rY(bDfLs&JVSK#SnGI zc3j1!7@f7hX#y0w?hG_b)?EtyB&fS7b!FuCPhHp+@J_aq}+^#Z-!RQQay(&OX^9--a4ORL*V`#d~_ zs-c;asKL3XT?Vet$0e_&pFh{KHS45X?ZbCNmkAn09Gz@mE%V2RtllC47w|0iP&h6lrQ-2 zq7eucC0U`?>YMZ{cBVx5R`nFI`pgLGE+%`{zoxH<|>CUB&oc zK>wb4la8fchf3IA*n~fd(WooSqMoe9Ndl~qxzj;F2&StdMw&Ba{~chytoNsmV zL-4rrdmuz052SU;op}waY0dLP7fgm|ut{GB};I6cffhTmA*fc~lt8^MhW%z$6UuTzP=g z*lV8?$F3fpb@CG9K{myERg$e&QxZ#fbWjtcVmB90$m1E`s9tIfN9{n zs*^*3ID~tsg~6%IWo$%oMntO%+I{KUp?nZbNT1b29k%W!$hAhX&@(ps7+MqT*;;6a+h_X;=TFOG1Q!J_bkAk=LeB!xfl~<73N;Myd4|f*oD|;;l4gX zO%F&FZ`kX%;#7Dh=X+sY1*ye6PT>YK37XJM1@2 z%5LljNs`8#qQ~U$uw2W``MsQti|^C;r^b=jYRJ1q6N#YGOsUggES+YnL0N4aDsX}- zWQOP^DM?6%wx6Y;LUNPKzTD7B$9~gMJA?{$sUIuUKRa+_+643y9HzKkZ3D=UtCbMp zWqLft@EAszFlpAj((fmmX)*GW-`;g>T9^W9L8a1_I=()XBzQX)d$%o$H_84hCeADU z)YGJY=66B2ov=)5+|v#-iZ!I+rOn|g0mQ)*_VPfA+{W^A9fv?ee8Efs3^q%0p%e7I6Z5n?1v(1tO{m9G-bX$rSQjnvftLBlDx z8(!t$Z8lma%*A=M(0|w6osJ!w;pIgp3q@x0LkOx{&N7+rrBfU3BK6fn20bu!lZykNIJNx6kMzCm?e877;~aCWye7aHI@)^gIvpWp2YkB^Y`O*J zk7t5tEaT)5hLXDA_EsqlTj&Jr`5hFI-1`yuug1Dy+aC|&v7W`Uu7(VjMwe9I3W*9c zKH5raG8U0*k`!w~&B6ytkC2NNFh`~F@Zwo}65W`Ku3tXEurkpTr@X$5IP<$aulgRY zcotmohqMUvLOkUW@(kl-K+>(rD?$@xOHr@Emy?>50mys&*Q@ov&v>uUdgKi|2&X!hFVWtGCa+(ET>+nL(#pjX)>l^7IaUswlHQ;-U3^5P%Wh z@y_}77L{!;C;Ib$&C-IB=$wit?mH`bZ9RSLsE(8%1|#U%Im+vvP3e??h!?`E(AqMe~3j^!YGYdxU-@WFhR8+V3E5zb{}pkT$+m zLa}9BF0~;eM9|i$3uZFzHPvvYXT%McoD&+ZqaT`ATKe(4dJPkUPGd{IUOdh>(mETc zSYy(?4cGnk@vbUtRG)o>u_|HyK;f3J1&{Cja++?z*ox1qNCh`y6o1ZIYsa`4qi%ML zwi|~rCYNhZ)I*H@hzSyhXZC>S-`dZk?63atirVG~Yfj`hdPpbiGv=P_lXCY?_0z5) z3JlNLvWvX*J*K6+Q*D{&2U!|0$YT_-7udbxX*AF47xp0vS&;4A(sh5GSJ9vb&s@IN zHl>W*NZx)!t%GvL43UqWc^>zmOWRJ&exvAm!xv|~puaWa^6p=5M+ej>Lz`WLXJooJ zzwrVyNouIwImO4|!9s&C>KEN`d6WHCZuP|{kJ^Ilh2c^t2S56H{Rbl8TZ@$JFF$V1 z7JYk{BTCYR122QE&}9wpW1^D@XwE}KK$?z4&M|L6_m}Nufy(ubej&w5xWE-|_%5P& z)&J1H=}GtQy|qwJF;sb&avKX9F-Apv5g|*=atxap_8W8ymscrJlqr>@0}QE4*vc!E z(ODZF{we0ylww*eAKT_~<5n%=S=PjkbV2&BYrp|j{czcR>7!bTjZ1{XM1{>W>&A7( zB2K8vX2%Ur=|~>|hhH*s*MJru{YT}-{e~wWv1=}zU^_fB&a-DTrRfG^A!*ubERNRN zed?xL3eURa$DB>PNKr)`Wi0B7vy)=OXuOxhG2Rt%_1(aQ^>O!ZK=_+TZ<7k;5G0`v z!uAK4wVS&bcfToedCTwpFoEpPr46)^*d$ORNdFG}btusM5%m78_iziX1ENC7f8+~Z zk98Oul$E{XhH=EZ10&guTh)Cpnk6-O<+gv$h?LtI!qnSpMpDx_=hx?TVXD5Ea+)Yj z6DNxM9#*R3&P1RX%)G*Q>p~sXq4?T-!ey~lO*mXL{57PhM_BvqSc@=a^3fheS zDJF{uGXzb+`Jefo{A4-xahm-hO5CjDY~~WolmrBxRCp?v+n? z6>H(*)UWCHi}&(qA^l}MjIQe_XTRT${k0g|hSh+#2I>AmrQ&O-s#y6}fY`q$!D>UC z9yspY+XDb(f>jwzi0=%*wFR4U?OZZYeBX z^OUDk$tAFs1alx{@i)@;i@Y7ZylmglzCe4tmQLhaJHjml=^uR~7*YuYU3W?fvG1@( z_4ncP7sMB8qzH)V+K8-5lx@uZjk!?xfmFF0)>jJIk0b!6v#bJ+seFez3ECyV;JdFD zRsXuI2jb@I;A>#6(>ap>h>R{t3tAt#6RVqF`lnQ2^Zc>ZA8x%6U5-MLXx1@zbldng zD3!fwgs(X&2_h^m`X9zv2+dCjkU#q>>B@OxgoQ!5k#wy&BTqW&ssX{q2aZgxx_jQWSkd8n0L3#W-QzzY+=2y!#+yF2Sy02&R2}Gq<9G(zR*YxF_J%IR8;**2{yW5 z{b&U5Gz|d0Q$k>0N1f9Wt#VdCAz|=fA8=A6fm%T{o^><`OiosFx&%SNDvulD*~Rn1 z@1+*1QTk(k`D>?nxopDO4wcW7*L^Jy;GgRj@1Bfb@%~ATzfK+R0$%aqw=r_XE#u^u zK1Zd5K}|%a06;Cy?Laq_clKrSfipNptxVfy<|OR_VM3_@qOKzyu*+)0SAAf`jq@oc zp)FFNPola89Hni!lE$#)V)O}_|NvD+}hes-ddtUI8R)r(i# zI64#{j(UV~{xwCaVu)VIm$b>TZ}#nv+6host|X(A3kMfOo_=!Sc(NMA{tQQ;B~^bK z7KH&ne+U)a1jA)z4V5h9dZtq3^*7fRxY!&m$ct(SR&OfQp+7eTk%-38oC}fVm|`BN zm}N{g13Lt<8A7JzAC$%UsYneAAahY1@k>=uAn`Y z&{0`sHE2??792r4Y0D>S&D`U{U7a@?{?>E^Y+O+;P@ze{c58osW^DRT_`l2DdtZIQ zl8^M6_Hh;5|c%)FgxiG%zpOZ9{5n?oXO(3J*nd<%r-Pe7Ug% z8CN`lKB)XIpI61Uxh`_l71pN3P=$#!}Vdk?&8aSmsMHVI^@E!6C6Mwk!#Bf58;?@=9sW*GnwrNMw57 zE4)u@t;ki)HH+hnWz`pF3vLT)kcGB3i+;Bpi0-*yE#KPxX{V+P$sCMIp4>jq^1BK@ zDI#2n^)}zn0N`29E4A0a1*T{6Jlm6uh2Ey}Td+Kw0dO%8A|8Gizoi^34Y-NXBB<{lehA=ZQhqieA1 z=+b)Dn}&9V_?q9Ihj<_jT2diHA039OTBb z0~b6(w^HuH#bYQ$_}L|BmpwpN=4mC)Zbko>^5B7VbN|yGLb^Q6{>cWHz=ra zNAh=gu|$uo1dLN9L=OiV`_YIEDMl%VhHM}z7gNHBU3kj6)e zU^IEHJ>A=!xbN+SA~wDSaXazv<6wur%NzKp6WlkZ{g9Kcj9kI6_Oy-to#AF7*$3_v zv=Tkz+O}lJ0`DoaoK6MOLn2conNxcWYJ01BdV+lK8*{tirNClsFoqvYmJqW9{3=k4 zD6s$Co5m^l4YhSZ&XZ%v_Q8)HWdHIbO|{xbCTjzU-qEks_fapeS~hdMjL4(S1G~cH zymplrFg%;OX}&wQ^&k3Z%t^U~qyO_v^e!apu04ddz5snhU3(7oj=>H@(FujPuK`O; zf{Gs9%7|}Hu&mfEKv^(dxVRMvV|I~Uc&IlF11E37{B)S|F~jZ;-nC>VH`PWc-632*26DpZP)=zP5GuzgJ7FH=ED*A&;T8tE_8S z&hTK@SV*iUBlh>Qo~l@6!DB+;Hm+i0<0s+MCFseT^QXLl6z|81St>)qWaWe~iW)r=8*nJk3VRAa<cpWtY~63G zS}@7H;q7Pc!2+{N+JiSgis*!k;b)W}PD5!v))y0XLzv1u0x63k&(mjSy$yA{6Yk#! z#P8hR(SCr;48W&x_KNZUWZw$tou#2j3?aj%IOPG&OUi*&nU*#eW9uvfC=(4 zxLEuxBoeR>?z(#1?U;uVaq4)B2`C(tGzdNg2R|$8D}{M3mm}L+K7XB=$)kX-6_+$9E(Fb!ns;EFMln*3rVJ4Of)!W`Z*`+ z795*XFJNhBsbF@EP2G!(!7Pc%u*>L$SxaX6hjZDe3}jjIMFt2Ivv$JZ(0=m%6N?@@ z*d_%SiWY^pl5t_=KTvHdimZUP7oZL}KvXe@oxG$6*98`c(F$uTQMJ+BDOdRD7@Bk7p zgM+>%D^u=;jiI+HaP+~3qtN1*y(}eaGu#@ez|y=7j8|R#_1SCk+3m40;OZ5_gVY4L2`CWrYVHZY zw2KCX7eiD7k1D#~tXR={(;!XUs%$HW+$4+-PdoW_T;CI|Q6#u-ph+FNEx! zmy+Y1%8P9;14&oz6I%iP!Ht=bCDRX=a|XJx;-2rb;O4M46D0?n)uv#rSJ!U8QXpWj*5P4w0U z&x6F2%3a|iFHGa(FW?n}uh(4b5nV^obFYW4+8KP15fyVhEuLC(v}*MgZ3y%8uo5H% zLAuWt`J*|JWA7p8f*0}v`L_oNCX?%~QLeAs?0OUQ4s62~`&b&j1`|PPxI?-kH`><;eD@r~2>SeRk@B1mX>y-tpQWx@-nbG#=!YMRpqiAKcV7w#@^1#zR04 zYz&hcfCm#Vg;_l+P`hRq(|YU}@0FpqF`jTe)sRczyzT3Bt=|mc_7eDb_R7XsVX<&@ zlU@{ZynZvbFS1G+Tmr(CK-Yv^j784q;ZAmJ?Wj+eVh`^b>_a;VPK-2FaPXb&wI#9Y z_qX7)U@6Y9>Mhf$Z9S5Jj5JhjYORsa;^L(`ztmiz|WWdu(S#- z5VCt{%O#R$;~n+~@-qVY&VfZ|UD=#!n*`fX$7^xW9qY-Dy+`BAU2_1nY2;Pg`rI>G z8^nAC-kCFv??QP?`jjeEjiZSBPG%`l6NuY1u!9BYS#jdGoyKWP=eZ4sEtF&x(8o^~NIlq8^(e9EC-82B_n@=@ukCj@zdDt7VsX>oQibjB)5yLG;ar^Z;ymYiKPX&egweZz~K`vrXIX5UcvB;6NRx z`F%&+fux3x>F`@Ye??udzkW0HWwLvGcMx6z!-W>yNUVGMc>eyesMzL$@s!p6*pm+qo8?3VF-=6^`@F1KUzmzUm!o^X%hxMNv z@AH19vAmYpBZO+Z{I$rtMI9*%o)VtBlTe8>&WL>Lb#V3c60R#b!k}AjXbHrq;AJAt z38qGWW^7G8tbn0FUgH++Ce6-m3u&7V<^b3j&^E9V=vmLN{5}Scd6OW}MA9+XUVaxH z*a|)gge0i4a;(5loVW36S;0;MQQF_INHOmWBcb$0sPB6Cn?|r|~;D zFr14#CRSH{p_&5Pi0!+}uP#~vbR+CikpN}sKwUVyu-Lkpx{afAY-5$Q1#)aHux)plYPb{CWuY@_xqO4Y3u*qTrg9o(z?>VzV~}QDJO(6?v>=iCREE=Tq}}9 z7<%aTY_B6al9K-4f06BNjhB$!dJRMt2jlnICCGMfFY&c$duXCX|ZicVv)!5kI3ds-~Wo0 z_I}hgP%pI|wu>6oF=1?vF0Ud%e*rM~cedaj{nry&<+*6K)~lz7IYK3JmNQT43!Xb) z`qyi_s{j&PncAJHw+hze^-4<$FYL44(e>$Fa#WIOb&RW@^iv#SMLF91dtknx9UUGq zxP?~&#j|H5N!d@Pp`q8;>ThkXxS!@p8<3$LorFZe) zzd+>Ti;&n~SG-(QAO2I0I~Dci;-Z4nX_W9;Q3?y4~~7i zX91VvQYc72_XrfvyiY@>PUN`hmfHgTG|UVUm$8%Rt9r`)AUv7Y<~MlV97kO$u;cg* zuiA_k3>7;vdifwpW#)>{9ch@#t=UB7wEK!XS}aujT70>;&*1%eU8*7@@isVBYFCgO z)Ts4Vcx^Q@S6Ksm$sbWSmd16d=5{1b&LiaHixdyCZOOtR5AMg&# z>`S8-E`V1$CHU?xG}LnIOmHNh`)f}o&gGCkxO+%bXU_n(Vm*&e$U z%chD6JB6j{{fp^D5Z6et8y}w78|jSAJeQcs{!bM6g!#psq8Lk&AmM4+p`CzN*!~QL z;z(I$r9JqZBEv8$<;XaKkWh85&ucvYp=6-lGJ5Ab>&li$FdttZI%_ULb(m9Pap~pN z7afz8ADLagHI$#)j{N)2uf9T5#34-1n-ve<$WcLythXP#P>pB@;l#ZJ`; zfXb(CpA@!DO)Q7V0)EI#gdz9m$Ld`^hhwiZV$(Om-sVfLRc!J(zH1#v-gI?AD!}_+ zZglIZ#v;z`I!qYOPjUy-@I_Wd4lxI3&p^dhW4*G(wP(L*!=Mi) zm5CCeSzHu)d^^*BCe+@PQD zB%@9uRtqDwG+9;1zE*qQ98pE%xQO#mJ*y7{Ur>F7NgRsVhLollAHdyj#xS^HT%>Y?Jn7l& zC{JxTT{8klMq}x}(AHaHu+9%Zuq6}W8=IB(2a+je{t$Nn{B#=iIDzzDS`vRJ%V-EB zP`75SIm~I5JPvx3gBpFVT^p9)CDHL2BtGy15zk8Q;ULX@@!%X@T%n^e7tHTAK3IC* zWFai$)ls~0?*~};nEfFP;Mp`4CiR0O>F6IoQvOHoPdZ}7eS+YU6|>SlHE0IEyjSbY zlb|nGX=g0EjgMPE1iD3saXzr|>i39)X8C~4ZC;=fDWq$e6x-~vnEUXSvECEvjkSw+ z^3P3ysMc5mLM&#Sv)`nxJm(#W>Gk@go<>g;R@EEB#DPJDF&y>InDqJP+;h_%aJkm9 zNV@>Yj5wiZ`ufPx$26Kd17Bv9uDvYrUUvK& zBxw90jxo`fQiby77FkG-ylZp~#__V15Z%8|PX~YK$lx3pgN3&cyOEMf2k{Qo7PRor zj{M5k_N#$5DwQzF;gVtgHRi|)NLOl zCkZL?2l}cclat;1Kskxj34`3zqG~`Y5~cOX2y~|#%w2SN5OasB3u-r7z6XnPb16K0 zS8$h+%|ANrf&T~xkh5C}Aw5gnZIT6H9ApJUHiYE~*7rE%4t(~$$je6AzL@zA3CNdO zpbP8nd9HJ;k%o0QJd(3g6$<#Leb9ZWrMT3Y5Pw$HsrnUQgZb~FV>E+N%!kIcZj zgg;;qirotQ)ZnQS4k4I@PVz{W|4icx9MOl)YC_^|XRd87Uj8K-83;Lu8@OR1CK24` zRyT_^6ybW`UmWA@S)r7b`-{_Cw4p`K(tl}7p{<_7?O6c%7{Q0J{EP>4P3z=tnlakK z8}-Gw9X%SE_&xhOy4MzSt&-zWq^@{GlR&yNUGSyY~w4&Z$(A(472UZTihewvdZ?gv_+UDxotS(kk0f8dN!oE8-L>_#29hVKVHnp zDXZ=gI**3<*O(1pnbU+D#PdR!!>I)nv5mCG>qgyy@f~*4*r--E0f=FuF%^p7n0c`NmACL zs5pYXM+|oM;eEG*U1I||y`qHT+gtSTg(2LKtFYR6WM75E{d?^7?CllmAdx#Ih=4l0 zfaW&Sj86~Nw!GO9`yQosbjK*0a?bE0u83{;`t2C51;Bf*$RRMI5zv#N2Y8AhWpi|D zrl5$e&p9hi-v_KA$SqH^IQV1w$eM^O;&CQalT(%?14gaIuK(TjsoM3Fe^U6__?rFv zlwj~qo+@@`rbL+9e4PtsKYc+VuOKgUba<){i`Z?dwk-x%T1!8%cZdPSI9*jrhMoKe z_`*hHW#@6geWrLZ(8h7x&dj)`TKteD@&wB3*|x`BU-o*WRB{-A5B{)mdo;R*`4SLu#TtuZRBZfWe6!;3h9al*D?gF^lPV z)n$Bk1LzcD%N&~4qIr0_yy=k&fDp6PFqlp|4J1|#bds? z9iNKavz^Dap~?pg@Oug7@;Kf^+lz7=d(jdu55=9~DlhJ`*99Q&$UU$R#Jvd>aGrG) z=AKpmiR2-77R=W18l^f@F?PH8#&zA=LtMSbEiiqe$NSu~@(>ASQV~ zgZnd)Ov2P}lL5D&3`zE9PO|#^S#g4srDDg7bfxiB`Du}!U zsezOcAUU>4Hu%yIm5c=G@E48>F~}c4;jNT|ElTIahhK~K=3zbq96a>}nmOhl{vV)< zQk(lNx~F0`xPp|G$Nk~QuG9I8OSHDl>kPoU4+Qo;lqb|&`gj=4`EnNc5_MDJl%((V zJGaaI^~a?W+F#r`$*CX;+aaSgE$q})Otc@XdaQoSTJ4X&!4M_ntw0fNjpaLlzzvJ6 zuX}@6?kUkC(>JSsj6U3VPsBS)C>`}xD1Qu*M#h7c8_tVRtmXSK%&tVexxA&XNfb$} zu}kD9y3kHOk;mJ=@(Rw`l}x;M3&Aq_ov+V^gM^=9p5wL3Uqh_v7$b~?=*!j_w8_a7 zahJTb`w;x;o^~6y~nFjwjb z)dcLfgWyyO^5^_Bb{=oECh~h>^55w4O4DsL2A(IE!Sbr1>t}~bA&I<}1}C05m0@?C z52_tt*9D5}q4mTFGc)rS!;0+N5|vWG`b_kA57 zTBTVIkxd0s)IlXIEofAUnDSE(L`Aqui!)>QL4rn^;18vf8k(Ggk?-L=>I1Gi6r-iH zX%#HP1#KYDL%E?A@XMQ9sr`-?v34#D2VX*Ll}t4$R*!`vOO7?)8*5nE?0wkB4KK|< zufmwWIlgqH;fK=}J+ca~E)R}URGB3osJ!Fdf?j^woX64CSJMQ;%+LnGsrsaa7dxdp zSrb88tKRBqk0%sd_bf)@I%yOhK3`n)s)uhJZ!lb&WE^y7(`c{4>z{V!+C+?*4)s4s z_TI7cv#Y$OJ$!1vwNT6)U&|7?mjI?xUTz4rG;F?ICXC14*a$W%@r$76Cq)#+-!m<=5lA8PA~OA@VnAk}(M(Mh-GOtqAy{SyNu%3IuL>LK7djxYi@nrP*?d zR8Mw=7^8CKJZ+o{kykx{13Mf|K+4}%l#IZkd%*W&&XwQy~`A(oD zA%_k2_lFPQsozZD%55N>z%umfhb;fS;*hPIqR+QA?xN^G^&}~h8|y`q;C~yzm)g1- z9-#NuR=*z^{s;yCLAPzA@8au&XI4jv=HER`;x<``-J7l#Fqpn6Y^*!5T}uSdjZ+$Vx0@7v-mDPXf?wt?{`|(f&ho5oRLx3uJDedY+aLYrtw3tfJKtHd|+hWt6CN z0*nL4!NG2@UGkjmz)_n1>=Go~EC+t@F_!I5DwQmEpG}pI&`{Sct($iG`&+C;Xx~DH z<=IS0>dMQ|mX}&zK*_dY#vZ0$-RG2s^kGKK(j!lc$!6e0K!TMa2eH~S+Poc{S!WG? z%(E=qJS$!rI4ytsy53awkLHdJivyQ4^E)rTl3X3WhJ(jA=~{`=co&9Z@@0v(Sgvx2k zc?rF#XDrj1LI~3=sJqt;qdNiOszHaa@r~{DHK0(qTrq_*-au)<1hp3dyRjMOOuuuV zXjKq;{=r7CsGx(GSZXTx#ePTxzhL8rYz6nUK)i$Qz-lFM?$Ma{zINH&lTE&$f(DI2 zaiXazXqYhSC{*6y;ogp3e}toiu~Ouak|HQh+C!B0VY6-UbfIWqD)!*|< zdjmb@+^hX)8XHwlEL==}1CqBeZIqx|O!3@GV(+jn-oFY%;Xy164&&PH^$FH&0pdnb zjxyuG@|(99-`kgBf7?>nQcDdTy*tlHl1_73sDXbi6cP1z?4-3Hf+W=q@-_$y#IKZ| zrj2v~EEg(Xa2W|&fozrE#{BJH^>Q+&f1jehw$?9AkMQpo1s}Np1yjWdt@uxe1Ygr_QrEs%q8}) z44(8#YV~gdclk=k;}Iod&058axjn(UYtE;>Wg=GJEJe8g#AH*!KKUENkI>62Ry^Bc zPAU>94=DcvODSIsU7z1R=H-ve_N3ZC4Lqrq1DlVxALb)v6Syf9A1L8pfi2^u5eR>9 z(BP=$XL51o@zSuWpRs7X1bq!UAWvTmK8APc{mLi&lzI;S#3}MwMV-w6Gw#2kkRh^q z40Bulz6vmP0KAq*`bf&*?lW`c^TUH8*g$Pf#`z(P!**f!P!rD8H!l0?&{xigNb7S3 zkVT%&1i{)rTqqxWkS|1MKJd~p9HT4EPasj)s#+Tbmx2DTW4V56~2@qKzlqx-*8LE%1eu#>mPI21gSUrjQifuS|wvY8T_!_72cF$UB+8qGo=U5U!q*H zIBjoTx8V_1=ik#;lx(N4^l>u%R?gLL&kA)+< zfExR=F({acA6NW|1Zq=Chg$`@*poAOVgTwe<)==~ps5R?OI#TyAKdp-ac^~J{!(Lr znTe?I2k0^eDZ)%b7F@okX!(Z`ku_@_#MGOm@wkD9WAx?8+4Hh@*n4r{fqPhu`blf| z(Aye@3H4j&3eNmB?hJ4RJoE4x{^CXFv1&|8_9g{Kr&%6y;yJW_yM?w?^=^@7@0B*O zoSV`Ysgb{k3 z6q&x?4sij5pH5$3`u0yHs*BrlSjl2^XYeVzs?Tg#;4t=qBx$u2%wUBlz<1U{MJfAg z0;YJcT=@ix%quD+1|0SCB==x^eVjQvgPtoX@UW4Rls~vV;7`j@bD(XnV@m>gl1|)o z=5M@lq_g6-sswfL7NST{U46nitQXrd@S({$CaVEoQyCYE%ea`?_r0i#lwg#WTTw z`QgI|A3SR*5;zdfmm_nYP@Lbc&jbnl%;u+{z9=o=_~ExAvU)9Ih70>C17NL|Jd5FE z;pyMebs29UWX9^1B<*S}36M&B-%3mOx+!|Df>3kNh;#z>e(I6<{-GRqTYp9<-@%zO zV<9JafMBZ^VYx&cDey_j)K^D-A-b55{Y944$PE(=b;sj|a#70*7>B|mOYykWrRPyb zh!gkh+TLFk_~^Nor_zZZ+kOh$LVlv=CLeNw27)T;M^8v?py>C(rJp-GDD^9qiN>5{>L=K>vZgd+W~4gR(fcoWmXeWOl}=3r)wE}_Hk;mDiI zv=?bPn1ij7-apBQZ>B4m}PtV2p9R=VNRrEf=B1!#Tty7k^nur zw`d!*F+IM8u_p@nFD1haEmfFD2uQ{`zf{T#3aQxzfLlJi+7!x+ph(Z0zwRRtww^zBwG$6rnqM2e89&80Sp?$2QSbFsEb0kMpN=D;y>S*6zIm+XzJ3VuTO= z*U2xvS=f4bo0yO%Xmj;X?DwCzESs!dp>^sBRKrQ+wvNM8NF56%44R+|AfM)^Xfhsk zfttyEn)L|2W~kxscLI`)e9u8mw+9|G9!ix!G!}nfCns=R`9tPf8eZ@<+i<(a67-tT zUjt|z^Lo$_F7+R(UJHIX2njHre2c^WIom{~Z4)2`zzCnqIVDOdjk5GK8{=?Vn3@wC z(BUwN`N~Cvd(~645{m||%#ibS@;cg}V28K!|aobM6vOdYHOud@GgiHuBN95V?Dn#4)}Ms3)?8r%QS zcH{bTl<)hqCtp*D776TbBj6r3ic+?B>^_gv!`84)LjsHmrMom?C@(x({rC)MVm8!_ z$fczGVaLwBdNrtB1=hThkSgx9b}Rc4y)N+3{7=mMBdzTc!rN&V9B;pXvh1E#SM479 z18%dLb_Vt+_$yGF*|jTWyS7-WjDG*X%Sl!(PXjuB8?lSD18-@kUIdSXMV=;t?ZkWZ zK!z)(zDv*jP%Cl~3TV=~l82>gzy8%{ZZvjh4%4_c$CbiL70nCRMNTy}XZFmT9caRn z4nmyHX%BHE4j@+8T|m}W`4FhaDVT2SXypuh3dR|6sT?=Igel$bP0Ok=^;%UIcF!0Orhnb zRE|g63WBV5g}}++1Ju=4kU*YbI|?!W(9Q|DYy(YroMWQ%a{UrGL{zU7=i;Wdem5raBxx_V|w^vLS(|4Pgel@%`81`9@yzjvR7KVB?ZZx0C_%{^!i&P94{TT}cm z`#RM$2!S1xY7e}#==`hS&#LHTh+vuJyq^RO!BGD79j6#0wKwejkgMq~(D`1M5+eP@ zH(KeY2!=7sOgIK~#CbmfMq(p6_3gzSpZvXb&hV5ggbZRyVx%2hcO%Cj(>mn&&BD=$ zY!c93HfY6kzwoV`+5CqQ=kG(05|EW@#biOCLYv2ixKl9mp0o-c0mc!2S|cPbNACdM z0s$Z<1l-a=wVu8^F#(Q{9DZ``UC1E{0+x@R7oa706|9LlQMx z(=H&>79EjN0o^Bu5^z&*JggK*Ft~?xk#FXF%_9choz`=0k�RvS!A(2hLV7oRUGp z#P>QpD4u>OQ%*&X^VGuAtCO@6K~d_y6bP3`x@Fd7^#ZM z0tXyS0rBPYSKBxhOAVT9#9k=_4i_uy#MPG3l|Jp>mvNF-#sfLCcfUhnR}OJ zuyiyszCx{jj!NvsbpJE>?{!t#0xUo=`u$mhl6CfH2$R?NR?Eea&kF;`^;83nc+mwj zX(jAn63fk*bLDJ4=oX}^~0nV1zy=63MH7PJ6e|!n+G14Ir8G`xAqvxto<2 zFGC(_CSBi%piu4}!dyz^78MqicLAjU@U#1(46^Z|6j&1HA5;y0gVel@&aPhIn`kS; zav$MbH~OvZysU578*tBbMXTTVbr8ttyYU+LOb^Mg7HsZnIq^$w%CZ*%m;xoa!_-d*w=BwlM5wirE zhPM8dr_&YWra2Sb-V`g2oIW#1 zfbt36_&oo2zzih5UmYrfbxsPES874uz$IQ?yD2D1FnXKWI|g;l+Q{S@eD_(3mMr{W zUZ)68k|#2szMt1Qdloz*PR{In`~%7J1bg7N_ptClWT?`e;md5l#?R+n!>4wb7Q{7U<@ zV2iW{gvC;xMwT9LBJr6tOHwsu_Q>oVvp{ng z8ZHFr@|VY#MIPxyqD9ucOu%;mV2Duhnhf%zD}lPggK@5uG+xa`$`F)_pFd}PA1NgC z1V4z5dj;)11TL}9h7EUmwl*ltqYukUHPqbhL!_kwhlYTQTzN0Q1%_w>te{mzJE_Fj zm~GB8%jhfatNjB|+2n1wkVPURio1uRIt85z3-LKfNx=Y~@)J#f6sRNLfX}V_Pdf!E z4-luZ>b>d0)N7x_MCcM<*5$W6=A0f?v_n%{J~q}Xy=%zyQxCoz;jmQDu@>P~i0GM+ z&R~gWart@X+LRDOo#z|FALZ8T#%~DwYK%4_-GY6c>ipFhhpFN5osyTng|fv8RB7n= zF+k8m-p^eGrv%{g+!Fl16Yfa9faDD58F5UAr|_|ddGQp&ABk^ZQ~bBoM#H;R0TZz8 z(4lw=@)ig>5~0*ssVXoE*{mq|uiD0uLm`OUML`@o)CF9$L#8N1#4IvyD{WXv z*?t2ytm{7-V-pS6glA6;?ntI@=1vXTa$C%}LSnA)eGyY=&k_9WlEPD|OW^y_qQY5` zFJx^_LCLINH~>G7DPTd>lS{hqhoLoI@>6ZmJU1(`yY&S-c1!BM8vYFq3*aQ@%yiL; z!zMqh)ypq)o4<$@gg%fvxh0WiOIsVzf>=kGnI#mTQ%8&eoqW0qBOkOUb-+A@*6-l6 zUx<+k?n7DiGv(0K<_9w{VO182jN+sV4#nV8<<1JU;bW$fHAHY@GlD!ieMgj$+5yP_ z%$C42yCQ!aJ;5$W*vkBVtP=nBTOetO;J+CSC>F(m^>P%9AX$IfgWG_qG2u25W{}^_ z`xxBTE;j-=Fn3Wh@t^idPOdJqv+4!ptGx1NiwmgifggvTQ(Dp4g-G*S8Vo!r*wY87 z`#U`48Jh86hgS!+o*KK~z0=iyKFANK$E=WP4fGme#! zRaxJx6ABF!l@P}$L`K8NI3J~G84bzEsU$0e5OPi^I|?C{V}!EFKF;TN9>4p(|AuqU z96f0`NPY4mC# zE#|fk`8LHV=0zvw8=3K_5=bvXZZ<2_?vJf`lheg6i>ozaol%?Z-9~-USWfe~qo#aO{`*lA}Or%uE2`5kl*b z>B*d*ye-p|ym(AxT|T7tZWs9k%2v&{_4RqLAx__nA{FPi{{{7FWAJwgC3 zY%ioiQz2;b4=Rdd`K;LP^5$mwhPd3)y3eszGcw&j4%JBMBvUUa$tDnxrT%*u`+V^G z=m9=*4Hp6Vv-8)M^h9=b-%_-uc=s0IH{lDM+VyOZRdO=*&~WX{N-@xUGWyT1lA0|R zxgU69kULHr)y&g?|-KiwC($N{AK4tPz*CGRQ|-LVFSz_Y4&K7LNB(ACZ9H z(wn~)3qdZ5=Et}bIBtU`b5FLv6dZ-B@>ZAt8tfM*$vCPl>#*TiW&P%fbv3J+5X0|)QJtZ+xLE80dN3mkfF}a;_n#V-X$%7 zV$wUMI;Q2#XzpdE{AmQ%+siy$eUiLS{UR+T7}o}n7L;qUJTqDZPt*MHULVNK6`C?u zbV94PJ=|djrnVz@On};E*x{f*+jSe}F7QuxL#TRbxbqEq!J%%yL(OJU=caG{nzn&xlZPT)W?oY%2@ z-CHT&ke{9@r}3Zx@xKNCHarMEJ%!ez46gj%E{K<8lme0kMZ-^u5MG!8uf*wtv*m>D zi{Jx(ilZAG2%?&!t*czJ7W+1X3W)2&v_d3^D| zSfVNEzL6^rQAzqnJ7|Qemy24A%CSz*Bq(PEAL@R_*_RyvBel7Tw9 zcSJ787iY{P2D~h=cGQ!(DNROcIC!KA)O%YE=ILedxEmF#v#M>Nrg-@OSXf6^xk z&KVz$t2;b~6mo4CbJrW7xF{}v%`&m}C>@Wkg%<ie;4yyM(4M5FM=i=H!mDvHbu7(H-1A9+pQ?BC4zXEx7e6#VixSDD93c4U{?b=!82 zxkxC9xB9#;83XDKRKst?4L`dqS9pg4t~LrP8wpb!JadB07UXUsHhVzIjs!2nwh9q@ zN>4f+8G1hce*NHF;q}_PRLXYzki>UT?HE6UK9C?kldb-i#1Q{H3+$rbfyJ6a^~^)JC^rF9<=37a>QOWq5<@J_^jy~ z2^Nbk#Muw}KG(4H>=IiVBP~(MpcGD=U)Z#575B@iX7}l9x{r~y{#JIYO9A8#}5ej4varC z`iQP(Xqu0GH2fmHNY6p>D;m&@ydvnwPcX8=Ah#R*j5-^7&A)Q`PWHjHsAwlMVSL^g zV#g%9y1F06!z9KPYh1#63_a5eZSMkQ!1@1x$rxtJ>)xqCXBpb7gTFSMD0As2>ny)a z_DG|`B{?H@@I!Pm;Qs*vG*FZ$$e-1&6gYn;5Q=p4h2D-($*xyC$~WGd#|RvGYxf zA}V@^P;;hZ5ZFw*Q7^3vt&cx@DHn>eteE20X&<}MjFi;zhR3>3L1NosXLi+%-E)D9 zu|7-cLWImm7@CESjwUW0X?e#O#r^9(f}ZNX7%RO;-TA&Y7x5%s5ZA(K%xVe10DU z`RITEC~li>qlY5>ysFMPNQgzB2L-fUdHo2-Q;|wY`xL?m`?Pqo6M-uxo&SbQf6x;zN6NOY?Lv*rXnru35Q^5;VrN~|VrK7&Q%K&gltA1yE<45P0I-@) zzjv*H5?3yWsgTS;Wqt^Xe;rUI*p0LF zg(C)J!Iy@#pp#sNviP=s+yBC%zx|;L`NCa?(5~S-ND+|9Puo{j5q^f$D=y?90a_HTj$af`Atd~HqC?@N~`zCx+gD8(m?rX1ic zoX2`#oGmeJmbdJSS0a%=?Iu5Yx;#ZPm^a3k_RRm|s8@C_QEjIDO5Kt=;OPcLXL3h1 zwv??suwlob4(>hmHexskbuE?Dtlnh0-O(G_Y>@8Q!^c;2^c@S(v+ zNhmRIW_8;nB1mfA#_NN%&G6f((rVIKOS0*)F7%QI z*&g}hd}a0#E6PKYk-a~WkthxFS(l^tV@O^OBkvZ5A4MOCPJ1l|w2Cl3A z|3dZm)RSLWrUP5dk%POZwN()S(QxpYShtsuM96)^7dM+%mr;55!^=1zfcPUZEt5g4 zTVl!Q_{RIBqC;FwIaZQK7`wb$1M0qEeV>YkrG!XVrxa^lBR zZh7K-D;?H}PAJ>E}QQz^MFj1H@^(FP3v>%w`T5c8ufZI}^uPov&VSePbhoxp|HSD7-ei_JZRpTfFO z=W&$N@UAg*-SOvfQ#zaNe{jog`zL2f8#sI);rmMe61K|y__Dp~B9~G867fTY*|}{+mbMgm=e9BzF0*#5 zV;|CUlEK@eyWQUeZp2|I(mIO#6gjAuVZ77Ujn4H0Ha!cOu4BQ|<;i!|U*1gH75Bv% z#kK`^)qKMcQhe4<|94m*PbTD?oa*ZEhj;VQFZODlp!#7bh1&+V8VW3!hc;}h_XXas z9LF80r8agh`++rmIferVfZoE#irI{RVe<3T4HA$=CA7w^L2{*BXq_>7WO~geZ)o&; zD@XV=IO8-I_6!074UffJHJa8W=683?DEXEBitZ)0qL%|z2k)-2BP!&&@3rUmwYC1a zU@x_od<7Fl(6_#qMJ}vNJ?_nI14Q3BhZXFsXuk&S6e}e>g3|^69h89uYq7Vm zFz;g8B2^M+P{;Do2iIGc+4nMXosIf%O_3mjAk+9oZ-JTz;C@u{@V(Rz1Athi+>w%2 zHrLfRkanl*dcDV7e1*w_)y5&}!fc4Z6&nIrT;Eu(r>_fr;g)K+;aLH@fJ)VyH?dQk z|5Ut;1ZqQ<^46-CM8k=YqID0E?>Qfe-@mi;yI#-}Oeu08#m=u9MR*7Q@tLFAv!jDr z@zTD)@a3qq3e*=6Yjk!+H_J4Oy&THO!BXVFAe7_ZU7K07=1xXu4l?DugceA~j3=V# z1R;vT$*09xX66F0^!ro4SF1OZ3Zs8O>&t+Pm1t__n1)!gd~o11tA;O4V$(fU4F|s} zYuW{I`^O4BWnUZnp5?15tEmf#*!}EWd}xB7;UtLXv*zC!d^jV5@joWGXK$CW-0851 zQ*Q9DwohG7x6Z$*i~s(w?Kf8-t$g;*)Sz1q9u!2kZcW>vZwMsbNYE4hOG=*);97mG z!AvL$y0ZC!DpqW|-=!mxD#`kz_%qKBOHyi35C!HXQm@fh+Yzx{d*5)PIn7GI4bvh7 z#~4i6y15nS&s33mHQ#5=k&`GJ$h86k4t0noN@5B|0vcxMhA%27(=*=A=KklI1$);P6I@I%y@ zf5^X@VGeU}hhiFD_3yJkUB?x{5fpO~4tn2aX2yLqyA*O7KHe?nonXAni3 z9EfT-CTt1Y#V4}nYj1%Zz3`?Mx1EC`OTUc1gvfvhl-Or+@&z~!W32=?7mk=$wwYK^ z<8RI#fcfdE8`+P)7v4&MCK7q`HVtFqt7$=6I95_$-v;bW?Sa{o<+kG6-e@wkBE zZTi9iIVo7KY2CrWerdb5HS8+0KGz`s@ZI6MSn~Xh*-fY})3Jg&U$x4D1n) zdCB_K(yI<~BvvVVBV=!eW;T5X8bqNv^@cV;<_61~QNOjrSHt$wC|FO3q7NG$Ne#a1 z`%%KH=MJLsi<*h`Mi&^pCUlAIqTSjD%Jdkn=FqM6u+W;i=1wH5n$3$dP3^Z{kKqa) zoFp{8g3#O6YytO(s}XIx0H-z9~-|0R`PlN13Y(BlbQXbr* zJCC%#8CzIQ{yDxB!!v2Vv&@bzEz7XjD4%Esi-n<}BdaW{L?UT~LW zOizFM<;L8~djq>oanoEiNX=@OaC|#B{5M9Q;J35k`5Mgm<5DNa-^Ak=BJjy+P^lzw zCu;d14@$OBNksJ+!%z%H7&@>3kHSE9EbQm@mxb7FQ9Zz1sbnyKC$X6X4AUB(5z1|4 z^mN~7EZMP_odON8Yqtd+JY%2<583T|uL0Yx&lYE#E|@ug=DB*!oO8KxRs-}VTxk~; z1XD{;8V@mfB)A!H^Wje_P!x569~v`0?EBo+@RK{Az(?`!()E`$LE10e4?%#!|Of=CJw zs}UfXo`Ckjuafi+fKe0jd1A1UrvYqDu-1q2u5^V>OukVB6f2i{VO_RYcDAq|SL-j{Ovxh+_-I9^4|K&CCl&|1~vW zxqqo8(AFe&XH_D{*@Z`>A8R z8G^#hGuJuwQs{`y4ieV~hYEr|hV(d`WPm*Q6>o&Pf=b_aVd3)+@QgkGbXO?2Eec)? z72`+>*y>I94=CpBNMI)qyxdc+Bd;a?wijvakyeIY|Hn&N85hJEY+|PS`vG z*+XuI`738sd*_x%h)xsm$Aeeh=2LDSk> zLeQNGmiBsPBEqM=eF01EdW-%8ID;|gxnU$i>zwcR#V*MFrQ_Zi=k4kqA{UYn}D_sv)=7X zyX?yVFhX5}REtFlscMU68eSZOxKrQD=I!Mv9aTR+Pc%jg$my>OWDRfb#xO7K<@kF= zjSc?FpM@&pop;ffkQadrMO2#r{1v-w*3z^3H0#_MfLd|vA#|pi(x9_0Ujh#Q^m&n~ zS@9cD$=bNKv&w|bY;mbmQ!>iq85y36`MAbnenAcK$4_N&dra-0FxeEo+*N50^FnII| zs~{P?E^rwnBhLfL+=#o(%EP(cn5Aj8>h*9HG15h6P7SMAQA^RouLG5r&qJA`S3pv{ zM@;tTZ3k&xsOVp^EPjsm>So06yj6Jbb)^AbYSYBz&AcO@ix&Ia1< zq3dZ>+R>jr7w--@F^U3ARwy5Vk zU6cshY|l6C!*Fh#YSkw$VDMgv(FAA-7Bjaa#xX_Q;1>R=*W%+PDz4G&BvSRn967nA zZ8G)ibc+fN9s)X>$m*jx+k@=o50d$iubd9a9eFYfX?ZoqKHWAC;3>lGzr4@HDw33< zqt%tEWN-Ii>)kVj9a(YHP=x^H)yr!kAPB>ZteEJUp3WYY!pXchQfI1Uz{5?NKb6+a zDzT!kpdYt+yKg|>d#L{w;{xGov_z2NUhwteh!CyXr7P8JXjjV8aH41LupkuVWe>lF zcl5)}Rurha&`{ibKJg(uazlWLv~n2%c`49Gz1lENHH}F|1R{|iX{y~#v3fm_5_h%Z z$85DHHx{$46zw!^Kk)PB^C@#Ui$l3vRHvLK`hoM<0c#n1#W&=rEbBc{K?2`V#+0<~rhk>*em}F2kxN@0=gpGX z9k`Ywt5(IPx=nX{NbA6jt`szqB?toZe^_(~M6D|Voc&4lv^)jjSH9xOdo(aSzC+ZT zmV8imza;8R6HiVB%l+RNWKmUl-VaaSF*_n`!2B)^g1?lEk>Rk%BCex2=6gpiiralG z5sTquPxZ;RYeA{xyXZ_+bXpbUjO;!Xpu$gn5f16>OWZIDWF{Q{(v4OXSyg_Hn^T*2-@W$uN@Ff5Ia9PU?j&k2OA_y{C6+>QNdWrLYerI{08zEj~p zp#ZanriDFE{%Ig8Dd-C`Sc*CPE(E;iW4sRrDu+Nd-ThZr!|$1xolSHDGuD<~Bnov| zDR#%Sne%+(j+!L_G%^UUz^}Nuqkhk8-6q%eYpb=EdI_+U*^A8Y%*-9AH+%$oiU2-Y zpW^uh-uXDKe7nRKnk+7PUN#@8^~NKhgD($0t6~L-daez4q3i?pl_PQn+lK=zg+n0y zqxbfJm6w~jcfc$-*n%3kF?p3gl;PEicFU6^aYI~1e0+?bR?#4zX{s7e&@P%GyCFqzU}Ls-Bs5k-6{W!mO!88;H+<kSRDThz)5%`k91(hV}FogzD(t}-3%%N36m(9 z2X+;RGv{Whr1P$?DR8Mj!)mqm*xo3p;e&ug3RR^0g4V6V`Dci$8{IYHCiT=v@vo~$ z<2wD>U;p#lrIH9e_m6nNiuA%S`lrInTpkocn zt7f~%QMjC{M9ny!l;}f%%*8Ca<}ClmT=esL-Co&S#BSk&FYzZBCzmG^+a`+ag#bnJ z@A2oky1k>0TaVEdA@qB?VI)X3dq>s<+a{&Fdo!F?k$2Wuftk>RtDsxEQ=lQt|Bt>V zxt(zPz5c07t<(v6H=a3J#X_G3F+IWanbf0;+SfnQc753Up;di{dwV2WQpIsD#Sx0@Cmih;Dc zUt{dM&YvtnanYXcum0Vbv(F!w=_^DU12b5WaOi~qkq7$Ki%8LPp~7{ z)bKy$a>Nw4Nl4-l5_SmXCesIXA7NF4^#?o)m{;_%+UP?vR0%}ClyK9scD9FX_fSbF zd_;moL#^&dGo(Qfo9-tGY0I5^U#7}!(CnfQ{sG*mrlmk}9OJqN6za{s;Y%ff3crd} zf_0x?;U0bINX2=DoutDH4?EbDdHSpONZPwi+@`_AR@Uz5Q{op?XgvE@3{a~MS#s{z z&K+>b2xJ&uxzl0bZ`CSlBp-i?>&c@?r>nlpvoa*8LhfiQ@})fnUEs zZQg=qkBw1I>fipc~HyAn`=L`KThB2V6O+s%7anf zwMW}_T#R14HA{PleR$l?{5$y~OTPB2>upm%(7W9%03E7%MaluXKJ9pEtjt5UThPSD z>us)MwFdJ01v-%c_T&YY4T{P7lNoN(V}{*`;@n5#m~gZzqwo$&!5d7$ z=flDv6QO%^`H2UEj%+3km?dBO#A1>Vji z@RBImbqv6(Nt8(mtPiX#z=>F44AzX^Sq5B;O~J3{Ezq%NJk4^xC?IbhKv%OEDq`S9 zI?~jOwdP?w0YtF_e!xIWfU(7a|1hJn12?Is{==ajp2W870Prg#0``~HVofpbLJvKa z-Q|o6(W0F}bxV`qFV`yWL}z$wJ7W@f40%xbOcyp{exm6+-pQT9d1uLRblh<{M@aB@ zl-^ny1D!D3epf;Lm?GoqwG}x7@aPocB1xvAw*4JA4F;Jp3;oHnrATWIBQ8^`jg z;r%WBA$)c$Bz*Scuc^X#bmpO-{zEvX)a->@7LQBIvTj_zl|J;2-&=_{?#QlbLRwH; zgIAJT!q+i6Tq(Y=?R)^p{nvZd3&*;%BQ`_)Ah6s+GGS~m%`}l<%V5@|Puk#6&!2}* zS95xn|16>~9~emIvFG|A3-0{g>CNxIE2Ke_tTuP8Z9wPYp)Oe+o>gc;mQbIJvw!&L z2}`a>qdOO!{Rs2#+|DX)VSL6xu`!@3ydJRqIJ~H-9sf@Tnj}EU%H7f{fM*dsoF1v*&lQCM+hRX)=e^T2cN@s3S6@kAITL~I?xkiTPaxk~_?tRRg#Fde z@ekIs52sz0eXVI@Ig+Tg)_YeqCXfURSAiQrP4V79I#F;PwO5+O<((YoMzX9m=97_H$UK*{5#;CXfqV}5N6BikYeF3 zRL~#Y-y`+;<~nQvZF=C>&V7Xi%F?e+{eGbY&2TPz{FJ?VuM79?B=-W1dj!f+M=>kt zat3%O{Dhwo0qwZmwZLw1OwN-QNide@e4;4?ow9MN9++r7X@DHryhCIS6PJ<>q@7#Y z<#5`}%**8HSg@L6K2z<-lYu!wJrzTtmXYs%xa z_(H=y#<}vrKtoB@uN6&?cu?c#$ozgq&5uPMCspth9Mg{ltbzFDU9tK&M(V8hEtg&ydSm0Zu<2;q+l-!S>+a_!^@{|9w&^qkeUY~RKIKDKPzzDmDO6%&& zcZ`Nf0#4?iCwAhTIzY~XkB11U28@5h5A1}f#T!BYA77L)jwris?Mw4t)JI|9;uyUiS%A5z%Q8}apI=anol0vC zQLztps9QDOCXciBe?msl9U*|89ts-(TXP}`nVrg3OLcQ&C;jNwUPjaw|BEyY7Z6xb zvQl5FLfRmw?07leJHdQqcT+TVSyH7ugh+vE&*UzkG{AOoNH~fYQ*u#Uske!`{oDlV$ zp(b@cp&?OKkuv;Yl6eYv2)&RE1cS?{5Abo1qV1zalC~c5#ke!@630hVpftxvD6nGU z09pu?Y!sGx_L3D~bV>>`zRH7*pj_lwrn%KX-CM~+}+|_n*dHiGqosDHX8^3yk0={T%OJYyH2OU!}GU?SKh*| z+ZQD4dne8h$W6=Zp9G1-fqUD{3MuDYY@{*7!rx65|7n)ul8eCO3pl_*dHr4eyQVPoU+Lc%`=)0=t%9pW|g4dr5+cw34 zi&M>Sa)=}yte98uD%VFH#>c3U3d@U$&!$4epa~ez_(*&^M=_ai#7Ra#czpP_>9Pc< z=K(?B>U;j|6YfIm+PvV<)sl=&`in=X%!mG021=~Hjh9q@r(P-P%6y~Q`uWPmqu0I% z^+SlyjC1u@Txuyn<^b6S_SZQIpF|D(Oa0aI>?!*Hr-&nFKL?`pNcSpDZ+lM;J(=GR z$b1zqYZiH7pXrzGt+{RUsY{pp*x846T_~@$IEuCJFqYLlYs}kt6Vx?S#sPkM&L-0T zi;3PS>AJ21V0$OrYosie?ZYT~r*L^=-dUbBE!uc({;KlrAv+ORrGzzB3qbKh>e{0> z0>}37F_n@!Afiir%??VSAKZdwk<%nji3n-#fKM%AX8PZ~{Lo_d5Ph3S^u!TSnSVoo zp8DO+?x%$kBo*j#Da~X(m$Ov6h}VaC@bk)U#S~jW|iMJ$=JmI&i2_dWY4E45ii{!K>q?|^OVW59|DAo z?a#n?DGcvlB6Ch?$D3!XVHIB)*WfHNSe8i;Ti6QoPj5O-UhBBV1JibuVOEPkqx%E) zEcP1XAdWNIyxyPF2wa}-vC)Q|U@cz8Rp`7Kvoa1<(k16Jgd*-`99M|lo%fOQ`)9jm z@t5S}!Be-XC0sg%DNvSj>F|GW*8Eug6bVC?NG|5SjuN|be}>*Xj`7r_RtR*r%&~V9 z$=^fJU^=8O=o7}7rX=d5x^ zq69T}N&MsaM~5zEoPJ$iP>*DLthPLwKrA8Xdd(?qqt6>IC4)E#F}A1MyMy~z!rqsw zk-OEL==Q|!-&~CG?d>c2!h!$={5FI7iruZ@@T6pZr|GRS?clnRL^IYHtv5(J85uQ9 zX1}vxeA;*JVt@LD+0+DZ++joU$HrJJe}ui9(fNg8E+Z&_Jli*gc5 z7?_uAkNo_F{@+=Y!+W_6KKRF3Xw)Odn?=w3fgzQ$U+aO*g27=+1h1!0T)R1W^3KWS zLyp_;?&K)4&Ag!h-iU?%h|qpXQsW*Hy($w50!+62crqS(P4Zj_Yj!xJt$d>@m#gCIOX<#z~a|gqObsVeM8);|d-K_@_UGDki){>bA{LeRfGu&FIO} z{+`YJs-Q4cFmTK4#7`vuY5pCg_B~rj*fl3L*FK6ddW%H+&Z<#JiCHy6y1~#zAFpq?0ZVfAp5{@MPVV7v`g1a+ zVGj^Gwf*87)sExIxoW3koRng|W`C?(aS~?T!Yp`h_y~j1)sKR_Ak<6(Tuz<1DNhc1 zv~=WkTazCMWk5QB^4PoYBLpr3U3_WIHF}>d<2dqCn0TR7l0z|!<4!a({*1e?D|vO` z)8h@~5TJbog?WGMzpVPj2~#zYV`X*JsVkX+cCX&BA`kSP+UO78aAXUMi5RCz1-kI) z%;R|u7B7clz>g2YH$Z<02+#}zvoiT*cI~dEu z91&OB?%6d(@m+Ao4QND*kI29g;dduLcUefGGu8DL=i|57rQ+lma(U>>-(Udk`}_l3?8Ny45=hE{s@R$XV^6qB5$qk!lVp%A$xU9hYb_C_*u=BAG)ta`!J8 zwNK@80A^9A5QpuOj`Vjy8#dDg?cZxgo9DE@Mvo&Fd*&B3{4ZX)KWIAr*S2ok;MJuI z9$iGze^%imj&i;JBK-cUi?r`KTNhi27;)-r96rD&pZnB()!z}euV@RFR_5t|#v zFJ6BcA%~E27p}W}yV*u(`eyud*gSfJvK2)B7?MYo>dym>o`1II|B@R(r^dY#0!MuRE2Ocjq8pL(7v%+L`<~R?$uN?d*Zu&Nmb;qH+*b%Xyq$fd zKt=`BHnoBN;g}51;G&)V`@m;H^@$yhc=9@Ot}n4Px_yYM%GPmA%oiYHuqk`tpE`k3 zgxHj9wvQ3GIz6xcwXJ^F$LUD(M{@q0_?agoEPTt1DNAk^g3@n7iQ5Lg)_+4fspD+< z_F+$+VD@3_6{-&!HT!``vMG{b=udwXB}cn08wlLHbzXvLGj|P7#2EijBrs03B}$|m zGhbKzu=`NWZ34a&6D<0lIOEadk4#1QLg{jlHL(9ZT(ad>iV=o9L$Kr{!D;DdP_x@A zdna*IIk4~$YQ6Tzw=Tp8??(R24NidFD0pc3-5prp=tT75=CPAVdF8y2@g)m5w03xv zb)6FqHt08UYDhCdZfji)e^T3+3TyGEn#jK?36q)fl9` zct2-c6#~m}>txsZG8kK@o`G`X=i7($4DOtpb2^dqx;dR#+UL4ykt%kXH97v*#^Ovd z5>Z^?nX_2sKy|PSu(|YB{P&ux1l^vUZ+Xz}YOdx-O)(*cLVLBe;O+o>O#5KPF-q!IXiYVs%ZaxuYMuWSYY;kK;0uJCmwV_ z31uZD@Kxwshc(3lZ`9a&%r{)(kSB$KZMlIZN2JhP?|evnj{H3Wt?(35-?REn)9CMK zUo8tOfMxEWQ_JnnK$M_5M18W+r71wMi;~V;nK-kXDX~|I=tJ7Y{LpE7GP-=&Ks=(^ z_;R&$TIlh`a~}m>S8G?3vCQenwCUx)q}b7YN7G$Fp+@4jM?joHi4UB5*yt%eCE0c(^i}stCywnlTRX8g(=&a984t6 z5->`6T&}0h&SsTXFReVj_j}lj<^C76)LHj!U%<44NRMQXYItmeL4JxuB>)}g_b}3> z$&X6zkEdD#-=EhSG>mu?S_P_AJM@xSF>|3^76A?Xv0OSl0o1<%U|6VnLJ$~_-!Neg zS@$Geg)k6Bo{X_45><&@b-RhQm)@3AU=AX1>ug0AAYR;6Yf<0ls}Lx0haZAlRiE;_aCC(Pfy1IU4d?FBO_nIfFb^GmxVgz|^gcLqbX z&pNu^UBWDtqr3?qe9;v(WB7$Yz6wed@PsUQQ#=p2@lZTSpt5Sg_tbJ>D%=zC8X`v{ zDZ$Xjd9*si>00bsZ&BkjgsmL`25Yjv9Ow+2*?yTnwDPJ+@N*1nolhGcR-OpPV#qGY z(9vJW2_kSkVNt$^KI2_OcgqN>th3TiJ8+`4hAaEOBmG2l(|&kjw%H&JWS;B%Np-z2 zJR}B;D<=}CaO_T1X0Oir6qz+CU;8H$kamZgx7$~>P*eDblIpN8e)Nb(NfDQj|+(?cb!gcXWcEm`7A z9a+>ZXB7iQYh-%uz8b93Kt1zy+6RZ;Kl)Z(gKgJ@Ds@~aQ9@X~@1KoJH2>o#e8|y% zoL=II)GLC4KL9v|cB2^trk?%Km+Nz*ufsxn+TfoKTO&Oh;^fiE&%a+e{2q?GL4A=f zJ+hE(g+#-pU#+{QQ%?R73~?F#uQvs;OgsM8S*S~T-J+=!`)Ik zU_!gq&3-ysd7LiVO-kd*q0r)lYhr?S*Zn3i2RfmI*Rfeb_O1fSb7r-u+t$Dm6nY^(%>!e?_fjxiK^gv&Sw# zQm{Z!AcKl!QET6)Fp~V!DleoMo6+k6^mPf8D%?GI^FJ8*k>rX8aq!-JpZ8Y<$*05B7ntxxaYaQ4ucsdNnQxoNK? zQ;g@sMJwAK$9al#+3~amvnz-w%l$aQiGyfK{C!3UR(1qp{9;;+whc_^c|o^NHP{^* z@a~7eu;GbaG&=4OG$80S*|pKm^Xc%&+^D(~KK$go`PVO7Np;9&UdAL>mcV9!O^aBY zjpe1)E*Xa)eoB9Wv^N#!Kf1x&LK5K{2O!nFo1c?hfP) zs=KVJSZKXJtW9WOTH@h}QGsjzc_x~-wa3iHoP)4r_-Gp-5>y{Xiu<2=Srn$vi3lC; zJpJh@<-c!pn!j$p!wrZk)yxjNuL``R%6}1K(@iWtf~yJFL3Z-aBUA{eE4M!8tj%VF zXSKt@H7OaALz5U!^IVn%P={cj%eSwRKZ_*Xf*+)Dbvs;N`l)VQp_$dwoSJu|1=C?` z1-(}*eSoo7hFv}(Mm~h_>CRZL6@T0H$5=%ql_3{eNBg7oc4PINc19YME*}Rmgj&2& zWUlQ7p`K$PS&Cup0xGN?>}=-~1)y)aFZ{v?^({wwJ^NltGT^WwU;ofK!vOuz&MJ4K zfTgO(9e2RNvoL+0nwW!L5V!=~v;KA!oBmSQw+M?Ey$6>_|Db6_H3 z`PiOP%GjxR;G%+ENHs_*y&Pk2A)IMDq^}QMb|yav7hVn%))ox5!b{}ZEAs#dC&5gDlm zI+*}VDoT)#$CpI?jT)C3mgHk!gEW$=EYb)*WKK&v+!`Hn7cSurVH{a@h`&0ERxQo} z@`YDuL5(pIG4A&qGe}mzd~+o4VbD?w`79d#3I;j;$hjBNSP{Ls;0L7aAN};{Ana0n z3tbR-;GHRXnorLnx(Iq%BE4Vgzv`4Pzhjn>87Gjxx(0sCB%R4sGWM#p@f@d?wNdrtTWpG_rd1~RjyF2o$qnPZc)nnTxdW1W_^*}Al;T31m95^On|A1qP!QbrO) zmkEGbP-F*rp$IR7&T^BV8*p{cH_bEMJ$f9BQKQvH++5E}lX$cDbe~sl{o}XXibwqCa1R?jVvfY~5$rkV9PBSht$v zibY#4c-C@`tAXp6n|ae|1rBorti9!K%MX_*b0b$2iy2dNFHrw?_hCp2j}n>1Jd1XD z*YT){oX!@sGCQ8gEh>p@ingvLey?;F1IIt&F&Y%%NhWz^VG2a4f+La_0|Mn%#xuW@ z)3jT*LJr*pzx*6*1Q?^jzg8{NrL^aN7hDcRCn5qJ^lzp5)z#peq|_0BI}#;?&MsVq z=z0~oJ3?7@CszIw+KDU5+)|og-zSNoke4OhwHySz;&yo70*XujGRC!9ndE>e5FWL?Foi4sx6?d-VbI-zi*JukzD>MAw85vyyp!+{=Yqf?VH6 z{(UN=i3V&ac0PHBD|{=Qo4>hV1fE`yO2+*_xEQ*7pDy%Vbar}+j@daeP}nH%DuQBL zVjTO&WpsCnj=r0DhKRp*Rz&?+q@%2@=-pYYJA629!Mv#>+4|?=)DD8}qoQToPg^Z; zI&t(vFJ1FefYA}sr%VuWIc9H2hR^eTm<2)1&ZrM+4jkZnAFh2EI#@tJU1+=y_Uy$` z)B$h9DLR>}?{@iDkE{B;%(}0==s8_gB#ZaH&-K_!9ZC51``uFd=YZ3Xw(FJ9bap*!g}BdX`1Vj#D5^EFYU+)6A?4V0MD~Z;!)E?| z10z)MEx~I+DUbT8A;4-Q37yTeQQ&%}%BJuJte!j0>=Z}INfNkelv!C3le;*Jqwgub zc33}%d~kPL)Zi2zX`JEwrobF@pcB*f?Z$Rd0gh@jdUXN>wt$}vChJ;4(k zMkN?~tS~f}y_}*!K^ZBTWhBw8qO6*)`BpiVS#^-7RkWiNDw@EXRx8z10sML+><;@OV>0^>#99|pDb}6Xp}vZ z;ZzU_eP5!6F;e+q6^$yp5~nwSq1f6)H6J|7N+K)~EFmugj2DV#xByl2>v1!cBs?iOlmEOXuH{z5ySNc-_s1|q5$pWWOLA%1Q zKC{oxsiY_|fBFqZ|KewCX-K}5GcE^vP`ot`idc#Y$d1}Flk=H-^WohWF6Wc2hu`P| zbz}G!*1vRN6%x(+(0p=xSKw`9*tZzsnhpJ9MDL>P@b5@hbC(x&)PMq%fG!~AzI$T_ zZ%35RXu6Hb)8*bRdI#Q)YP9(+djgRr8>JFzaDIFrfOHP9I?4VY4*AcS}T4FiqSEw2V>pV`h@SpMt=M*AIrA~vK?aK ziWW7Jz+81m+(N3umaPLcVGeWu;@oVbtjnGRdW(4Nxx_U@0g6G)MvF9s>c%jD zLPRy$DL?RZR+j>Yc4Cr!M2txKavKNmNbktX-+I$e)(7BsJf)qtIycd8d<#F>_B8z& z7?0|9yNd>k+%#kONY#*VgXYJDBW~2*D;!?OF14j%-*6?TXBD`tceuL~PLJQ^<3dJ$ zDw{R4gWh>`l@3#1rFFH1uMY=(xoj(ju`5I?B)4IYkg^Og@LAZ9rNr>H?o8Ya-+T3G z*a@jh4s_y<`Zje*4isWSJ{*Jjfzwx!{lj6Rz6JnC96tCCwbCFqNvK-E=dCs&H;lYbT%~qv=M6}t7 zl5fB$icR2|MDN}w_l+bD9y-Qp_Nrz>6*i7uzb=gVpG*=dM{OhIx_Zxz^{?nh?S$$V z`U0tW3q8eMr#MsdIA~U;{n|$QsJp(F29!ViqP?Tzz=MmVGf-f3m+n12E7~g*@TCnp zDXlrsJ?czjRK?ZZ#QS~=a?b$UcabN21uyLAG#7E=St_7fuLP$)>Yx9rpmTk|U{17F zJa@B4v?evwIkLN0(8%=AB#y*#XzR!*H`pOHFq2bX#xcKfy{ zegwm;p_5|@c=Na?CNy2wT}%hry)2^5NAnn3Shh>??7QtC z?dE#(=AXUUm&bp79b}*V@5|2F8FnJuxuX=-BO^53xffB z6vaOXa^h-5%13z9frSX6x`8-6g51B&8^{)BU&{VZt^gZMSARop^kmvNOn|{%S+;6i zeNQlO=cjM_XX>_ol6OPN&D^BHK3ug6U^_|-$cFlfp*xR}bfsQ~+)5+XQg10#2(A9x z^ZpElY56j@dGNXjC3=cU79&t|UKMaNUo3X6iGwG!jopBCEk5Z^jT!yR_$#-o;5`!Do7vUr}(Jn2&S!qER~WyQbV>6 zZ;R1>-#Z|~K3}IjE3UBmE@^*YZ19~)Cy^NPj5Z-S{gRbpl1U%hDAO_W$45Rjrgybg z{>RcMU}0oZnR43u70TK?$kiPq{k}thAKpM8KZCZ_0XS(#P?!Olv*wX5j^3?$N@8~F zF{+PBcTDRN48+>m!nHA`=liMi##d)UqIh6xx-t!Ok&nr>HN>6Fvq!4w_X{OvSL4Y) zzc~r3qEv-|g#)G2hC?58v+@PBq6&4ennA4B_E3A=uqe6Y8GT3A=qq46Gc(OUcHXhT zY5Q8-^Xs73pb(b7WfHg5pbI8;XU~81=I}$FAXglajd11=5_h@b=L&un3**9)tEJQF-uU;EmOXLOk>_gD=h&8fOR6inJUlSknrjm-C+I zkess5&W-Y7A8Yv;CwRiH$<&neah5b>-raT*iBb1m9ZbD1L zj@*u&c(!dGvmfQ8pYYatIovfGI66Y(OT zNQlsE`;gON8?55~l-kPLKL6j=CgK@hIrV0HBgnMG=`Rp}q@S>096R`yYu^KG7}J^|*xNm(vgk=W+RW5oY2K;`8^4|Nh+NIpEnPZ3vzC5k z_QBT!o8y#Yw|^8^H6!cOBeKxi81gWIP2yKS)vSyY@`N`wC>Y19AKpk^=mHkKPc;^? z%Ywu|D4)-BL}Hl2_UNa=V(A$%Z73!28D;cfv}4kSBzKc9M4viwxY|RWNDNr2DKD!$ zKr52q6j|#Z^QG~}T--39M`Uv$uSH}%Rv8v~h)7=7kQZ;Cb{L}e*I4OgLLz2uzf>=# z*DhX|2>ta;|L(Skv)9L2YK#D~hJiDo5Js0DYXqEV-o-vqq2*XD{-OE5TG6&bTG{#w zq>A%FzL`0cvYE+~CX{Bqu(%Hi>w25xK=JEEk!IHCrPuB_Luth|D?@+v(6x?n;ub>( z(o3j$r-XP(SlX3Y%cN091KFdIBz#*Cqf87B7FeKiI5UYmMI5J+u@89aU zOy01B<#pvV&bNC7=L){Rx~D2u%8N(6`8$N-lR#yT4?Mk4he58~huZf1Jb8*VDg;Z@ zW4U2e5Nfi=dw$|vg2F+brNw5?6HQ0yU6pIVPB@j7SeQj@*pFgW49jhel*pBtsu=pCW z6h4uzi(6i|AocdXIFj?K-t4j^9|xi$vW)O(fIW?`l;YgoY1s{5L z6~*uR|1JQk0x(=p7Fgjm98~3(#-5xP^{Nmf%lm*cdHxFuG#>n6b{w0)!0w=Tv}~gJ zp>}<-+d%&;*CDi*&-OzHC0`ME5Y~VkwPVbC3rGS&eUMrp>hJ_`F?cKwH2sl}>4kLyptu+r@zk+Use!@(tY`b-%cqs&1CQ*#tE}j$AGLr(-e{ zqE$YeblUd3`jmW545IbGy~f}E0xpgxPF4LWik^8O=9sxAiyfhr50Bfk$#S*F&~-Sv z4LpkvM!p+#oYhV|kbYS9h3n(#^9w-@ns7O4Ggi~f1I^`VBH%SS33P^4)IkD7Qlg(} zAfGb-)HAqodp@3b#*P4T~yBwbl@_Kv9-Fc%e zmWPmZiF-+Gb*1KQJY%QPUaObeQ_JAo{9@5Jq8@q~Ly|LCTYaA9wAGa6C6ick>Eh1! z0w;iF6ZWgdDq`?L=QV~hPxSYdAJaJO_G&CvVLt(CS`sAXKHv6U9NEtcm?H`Qy1t&e zvO?u}+$rRn8!>KD*qiXjfOT=PNQFOejFgvw{{=CtM**CFuUSdZC!K&ylp-c_$1EIj zia-L6z{oNuJw5{OeVdfLT6uxj7qd%=;+D=&60^EiIaV0MCzR@!kr>T%c`pOoqFuK% z3HY|A5@&EY0WqrHWnSry>(qRWRoO!u8Qgs;@U!|81N$;ug|2RnCAbmMfrUqKgjC9V zueBvqE*vbF!hwz)08fxh7*ZqY2y|gxUj(v#lU&2eSsPdb^$8@VJjVE&#tlzL zFmmka;sJ<$dBNl$Fd8~6rEPnHJT%&Sw4X>kew+HWq!qO|__w6!$tAv%P}+d6c8Cd& z6aP7O&%YOY$zyK{giF3vW%(EI6QQ>!zsp3y`_v48_mlwvCXYM8Rj>IHG&;VZLU)6M z5DDU5B=GuhY7Sq@!B9T!W+sk6dEWkO?8*|iD!9t#KKk*8Uh=b=P$wSFaFcaLi@CQL zdHGptJxrXW45T3f#CM!|t%;4>)_8zD#^%*QoDuqV`)52B9pG&O%*nDu`_5%bbp zzZQApATI?Q2WX{wy?0b!yXO`i{#rBVJH8gwusTon+)@3q>fUJHRmmVFo=WR21hC@Q zu?Y#%BA4%Og=D#cXxLn#=KQYL$Mw1apZM$PsY%2dQ(?ZTD+97wjji!E?|TrXq!G3F zmY>}U#rnK(mDP{j5tA83UWD5z_~3SNGP!e03S34j9DOEal$-bXjps?gdsQ;q(Sl&Z z_f0WP|HiO}SGAvDUx;qtF34_k*y%=E(Gzr@@nEeaVcW=lWIO! zE16ARtGT6?XdY5-iIY*A)3L*kt5iinEzk157)_EqzV+XO7?tir5BkcC4&4lG$`&QD9xBKF3Ae((Kwg z*C{xL;@{o4R&^Chdw$Lu8>6Dqw3 zuPhZT*3Z_?I(knZ=kE?4DU%p1|3VH>TusmEsWU*5Suncu3CG7F0%hSR8I4Cwxrd+h=2D4zK^ zCBpc=q-K}x)Xv09C5lzyKeJfGevn8ks6~Y)Em)6{o26ki^cW>1!Shzhk(tSay@(yeg!>VdL{#Gg zN_LuD!acbeMM#weS4dK?8}G~?z5t0CSy*c!!l#<4M^E) zEIfHaJ6XHTA?}tI^yp{EjX=Ar0wwgEH*PreZ`T^|3xhC>kB1^q0VcuNKF(XX6A|Tg z3N+mJ%ALjx=DmfK2Ko@{#*#oEP|)p-O!DuT41q6Gj{5mS`6vH|m$#kPCc{ z29DqscP0r9|BA2Bb?2;h`0dDMe+`2cs{CYKCC}^HA(G3kD_QWX5%lRx(D3_B#d9|o zabg?>D+c8zBFurWe^S1`>B-OWB=W%5N#xRN)4E}6qurdle3B}O3cNmjN$sd?8=m3+ z@o2SN>ejw)=tBJ^lmB@Ads0VE>?5JM+;-d%sMuB2gy~bJ1ZE@SLY}*!<(ZKbmiwQb zR^B9Xj_MgGvpyEl$C6$lNeAHy+>a?9M!+lf=r`)|7iX-KQ`aDwqw;Gk&M6gft}Do` zdMWU#RxzS`zJVwzM$6a_2h=b>aUD>AGh);wOU43evXXq1tLS^Kp}U=SC;c0VrrwXQ zJ|0SX$*0wi>N9Up)htxLfc1FpDn@$6d)!@9l*2)3P`rlizW%F~SSlB%2O>UQUCrRc z*y|XoF@_$NugA4C@evA;0(69S?dPGX?CwZ|2>GL@P4F#|%yox9ZOhlb$yImf8^RPR zy@McMRcPtccH?X4z8*WBX(a=GYyt2i8+}lpoT@`I+a9^IwJ4uXHZ_~r*vh*itf7qx zU7wnuvMmfa@l1$+;qlkDFHp1(5`u0lR7kRFmFK_@I|-5yUg|Swa-_3Gy~!@~f`^HH z1(ly;DdexLNA;W4kJonm(B;}11&_}z{rH4gAWJgxfCcV6t1 zBS0cU4_7@?ZJpE%OV~UyhChVL7H;s$^r2+&Xih`)W0XqQ-59pFXj*Y)oNvUEoH#~Z z>YP5Ed?%U|L8v@)7m7s%+oRxmYup7TcuOIjdYu3yK5Y;NH$J}vDX!qT>yn{FFmrr4 z9Ro|Dn9T8xEM2{O!+H$N>+YMnakqGGYriu6hTHmOYqIpw6<9;h5oVv3A`LwH`@?f7*Q{f6xM6p!^~QLpe&iMeNa)fH>=4KKAGHz;)uR9*9Ne z7c=7C-@$iJntw?AgkpB2q9W*Aj(IUsZ}p?+?;6xI7`wgEMI-r&yZIe0owlA{616;c zc>fr7=~MK3P$}`)g1#NTfQ?3r0}bGBARYDgKS^4#B82&cY=<92Y|y0dG0DWsp99;_jS==5j8w69vuWByZYWYVk)|gYSG^ zA0S$OzR6F|X!c`jJ^^TpENSqPBF7>I3fNu8+4bkO-XAm|#FU(xwEtSC0}i9~Wc{IX zc1FUn2bMVpR^A}LH>J?s_LidaUf!bx0w2Mn!Sr``C&7>6Z=lM>MglV3uD^h1eA=Wn zVZ;iH(bF2apJwZUC|hHpGx?HUe;*95}p4{vbg@ZxEC3 zfXYge;eH}>Hh(upsDrhYC0QBifZPhxTof3(QGLZX_m^!-EaRSF2Fi?S6?}`Abd&z$ zuhz$R^Y^(O9m6FPz!&A}4=>0cLdZ!9z{V#Zucn9%YR}rCoA=mA_l9cjX z8@jIm#8BGh1ko-Lps&Shq>bfEK6=R#3@chG_=xPXMc4$^h9R1cWc6~MRnL-L@Ddr1 z!W@HTP}U*po^$a(7)c6gKY@_JYqwjEY577SJGEfZ1KB_FbT@j<{eEt3?n0sl3K-_n_rb5>N^JbR9`auQQWw?2iNx3>j@rUFAw^AZyD> zJ6bO!X@m47WSFr*LTsi-;(Z)U*miH?%<1o ziO_PCt-FcSR^7^#68Kx!&*_>8{Ih+uAQMgRaVvyf;KWKjiF(9DHC(DG zSS)rGnM1Aa1vR={MutN0N2&~Tnu1DE7tk)$X#WNt#oB$VAy>2A4G%&kN`&1(@S#09 zHhwKYg<5c#N)%}YG$-QUQ~|Wm=Ykrz?gQY7W%ow1B?0Xw2YbF1ewJBvRLrZxjOJ_t zF|}!3wh`y4x}r(IY5lu87_`t6LSIHXo*hmL&&?*LUgSzTSB9alw9l1z?~6L}rOfW3Cnaia8&H#`w9;bMU_JZ(O( zEf$W}gRZ}=h)8*!FzxYmuKUd90a7%c5If)7nZ+?T1cz@;-6LK`>eXM~VE(&&U1Cd- z0Hii<8qn%|`Q3El`Gl0=Wt_W@ZihfTx0fb;>~}DBRUL4YwrqyyZbI$%w?!iVMlLtL z91s(R&e&iH&;K|x;W0etFhdE_Brfh=0q_?F6yAh>eNbZ`D$+rg4;>v@Ed*;}Z*gH= zJ+?O&(~H(ZU+=H4>ko_YSeKT*3M>byonJWHbTN({>`;za70)x$ zF1i~!zA3$V{)TZL)3!i}@wVn|bLDfxI|HIO$k+Xk`dK|qo^*SY{5ebGDMnBFVV`R8 zN@AN!ofnqCNBOgt@T=F`-tgK?4r&|S5Cr&Pn^Sq(5;{B)XNBN+O9*!c9`V^h?M?to zs5H6UabXGYdcTAKie}Y*3?H+Weiv-1UpA&G+d<8$6n3Gz2_D3PbDdE8R~|5$UjRdc zjbKGKdhB|w_Z*hIbp9Ww@~nKi+pLk;0n2QgS>Mi_zD1jsYM)qIhN*t zd)742Zhz}IYZk`#{kdBSJmk_O@zVt0DI>H4dmBne)5`$}&V%d#t@t?Wc0Gs z&mlzS89C_7Q^h|BCsXV76+nnqNM9-y;;3pjxOoU{%kK^*hbazH=-Ba%YI(hm@5dHK z7Qou%-hrQInTwtI$bf!q;z#OT^VHq9c?&(5!POv*x7y6}=I$MskVM!Ttk#FFt9g>7 zPn-~<8f2)&4AEHV#q5KxWL!m|;sKySY}UM-NR!_MmIM#-S{GXgd$proF>pUQ!jf20 zaoWb2Gz9{=xxCbX4z%$EIw%Yzpbs7jdP|&u;yQX*4DF6RIfY}B{R!324jdeE;Ur2C z(KA&;NUT=i&{dYNn(^L&rQQauefI*ueiECs1NW!3 zI8$X`(=TBjCp{5k_! zQi7v0TsahWXuMoY?*mE7y>DuE?I~7$r-#@HF2RkemD)Q3w*|~=7=7B6IANOU><&%! zORDg;mOu7}iX?#quFpbOQQ$j3Eh(bc5e0ozh{JxMjc$(zLXxCTadLPG;uR?5PR*_6r8 zL;1k(&joDtLm&4`;NIl1VYdWiQF4My9@d(8k*QDSI6b|hsx+y?Bx~_sj1=a#<||EWz6)`16}Ro_A1&^pc2;niY7)KX%MDEnUj5Ahq7FPf z6)Eg*ctW(#)+1*NQ5e+%y8Yolp3jjPc|l~1ySge@CI=p`UDApv6oGUsWrS#OQsHyt z@Fti}-vcG$r6wSEV&AK0Z9;U$g;YL@IpZf`C;n+HZm(3Z+Pd)}ULXaUo^W@2-2Zp~ zB!`CFrSUQ3=;;hB(OmSvi=!z=@>3PVTy}PMy%!bOcAIMyoIa~W8DMwS@e)E&nff2M z`i-?|&Oh1G2QuB}e7_AA3@3uL%bQdG49XFAAt42?$N9VeIoE=sd_;2pAFsW-DAPc| zZeAuHsbd7+PqE(QYNL9VBN&JtMO+6DEl?MZSBFB(A)!d4>=Dt}>3b(IPvk&c{6#&l zyO5Y7?1^qY3s*c9)Ag3=hWE|A%`5Y;s{^Yyuk3*@&YXfH1qmuZoAn+0dI!nZarH(D zKfD4lv_T&ijXC0BEYN^{yeWRc;bP36>14ZJ=Ex`?uzy8SGo4kUX-WWf1-fQC-$4bs z`@5DOjvv86ArOZ;&Y})0i8Eu|far0kd3((L86xPob~hJkSskI9SOb`yrGn`m7JL9m zS|Igu@M1eQ!m#fqyH5!$w1Nmpiql@DY84?P>6XNcZ)xT8u0pWYLC_807v#B7@}-C0 zM^ZDN?h5^RAT7F>n^v#Cv%aj{BZoH>xF1C=irb0tImU~^&itf?lvAVt1F`JK(jMQl zLw`f3CDE$R)AxWLmtH1lmA2X~Q9WCL))VXNK2FLAzZ1`c4>2VT_OuW`KYJ(IrfPzE zD-$v(cW6fbWJU=@lP^LojM}8y?w4YjNW;e4CPZ3BC~P!I98;&zx#8l#Lmv=@a^h1k zDB@)?MQ#R0vT!-F*of7K&*Nz)+Ob=M)}T$n%aM+v7xXL-sN4qMF!XCT*GA0l*cUhT zxtwM^i!Ry8lO~gsV+c-kS-B#UoJT^N%tM;YQKJS6?#zTb=veNVE$w!=7@2zUu#x%2l& zKN-u95CwPO*c_0AH_C8`dkNG$%!xZvMQQ05To=4+)LQ|?mX?p zPkYStGi1~h7GzNnE8mNiCxLz73%=n%*=o)hv=42XCP;e$j5q^t>_ASSd8*g~8_0Q#3AUCB(f_*FS& zW-RBJ<{U;$@4@>vDNotGmoGa6jNR)s#!wy#>fNdvI?<#`P$2AJc<7_xF>;xFla7`@ zRJnUA>k6!vfF?Agp~2t@3HsYUBcw~x!xm7vEWr*OPy;@f|BX`X*EhDlYnf878gkC|4TdaN?_3233bSKQ zyfaBOB3-PX|H$S2GUCMNUq6w3h18h|F;8n9ClPpEot7z1+BY*@Xg!PKXAN$=HPP6+1erYKpSeyMvStxW9ZBD58Ykx6O>S| zdyU6Waq_q_EH0mt18K^g`M~`58$Q~!DJiFS7b=kvMw859chcr-{GJ~OOSs<8{L4qW zp1l4+k!M;fpJlZ*SkO-#`ZaK-lI_(+-F!6gz>L0q4B_7kVQ{Ul=+5&zp}er)dv)&B zdj1U%92_Yr^b*wIyFV!VHXqg*4Jbs*JqYB|KEP*<2db#PYLV*yw7d=mO$2f|L>yOv zpD&|=++_))vUF&HUPs9pTUYz%B*2mG)rE zk=G_|4@l}mdQa4s+y44;Qtp6ySOP>oiB~f_FMfmcv)i4e`@LcV0{UyY^6-tR&U92sIIe44^r*+-DrP#z~W` zFC@nG)2@xBATp3a(8*`G*^_m&*whr&`ty=NEXo|3h!I#y&=uvltr`3tN7QtXBsBdq zkq7h(kz(M5CEe3BuG5H$n4>n_E4a9QfEizq6Nucxdi^MU75h zA)1+v7oK#VzIyU+ZQD|zeY?~x8Q_Xyy4DGRZVVoalHofJG&MjfGzpD|{jFNd;afZ( zC9bz~bvvM=V1@Y=bKu_hFVxVt;GUlm&asot6HNTG$81Me<9ge``0D(;n$`C8mf3=R zLG>vu+a)&+^S^TW|1JQT`WuI5E*{7IyWres3<>xxa)KyAhw0f^dIRYgp?l|6-X9>MegGN1 zlpDNc5?y)-Lve+lVHq_;&WY!N76w$$7^U;}`yov|*gS`aekr z+@=EouY|i91vlU_bo%dJaO#Exk>KB$9CI%5c03IH!}w3avk* z8VQyf(ouccEI}rVlJB^>?stu~6)(prBix93g#9GuEx3q?j@dm+kh+D3ySt6Z4o)6qlT&leQc7F??T-+o@g6X*^PX$JARkE6+uv@Ldw z&(@OX`Yy+AmuwU_rIyJbDQ?x>OLvNXUcC1a_;DTz0qVodj=%oI8&en$g)6Z za+7B6L56Rdv&yy%%inom(-y02x2(nyvBP`){AFQCl0`JTu0e_7?+?&O_;v*w`SC-@ z4oI)4=fq)2n+0$ICdtEZV<)7J?J388Gox9+BsT4M8VbiU&%)QG4pwp`*tyY+Ly7k;X3fpk%{V zp6QB0>Pul?IX7o+#T);rt@ep4FinIh4Nc1hlDL=lJlPo2k39j1aPP~A=`a0T{+*KN zRT?x`oH-;m9(tnznC>pOt5>Vpe3bJ`8ut6hB_jI5MfohR1s8tFB~bbZ&M|R8F%=OL z`>z{AD<)ryR2IstNI|14g$i@%0sg=u-=VCDLVIm7mKqIUOYS?fSz+iP(v7dw88{YJ?L0dRA{ zoZr#Du_*B&`|)qXW^{4|Ouxe>lu583x@k)cVs4CK;cF~etR}tH)m})4#xaecOaVB{)UXgeZ0r*|J!pk^)XQ4DzP(K zvOW>RN%#1{xOKvzo3gZYR>2#{O>zJ6+_3`boAiq7*UFwj(=;^5MBMK&psQh`MnKDg zd5s%%zz_W@@69?09>Z3j!@~*S9FD1Z1nxEQWJ=z-_zpWf=b%p?Ise{QkV4}##Q`(J z6>;tzxh)sgXJm_jGG>V*tn{vt^LVnq_%J&@FZmod-e#Je$fXZQ>%yC1SNrVMi}TIl z<=wMTtSUvFxA8B3V)gY0Kx(_7IpL$>+5O#;C?SP^LOhR&gF>zYTWNs~qVsp}lcixB zYx0hwttXoU6Q8D2e0)P6$%A|Hv=n~Izm-}= zNNS9QJPs)D^O6u@%Q#e{@wRY-GUzH%=oENhH!C?KH=@VXXwb%BojlDC?v&0icJ%Bu3p zIb~EXtNo{P|9wU2UGAH~j%KDv!~&U3NdT6&npr(()H-W5|ktq>YKdd(Xx09 z{Ft=sUyzOU*7`3UbKV3)w?UPKR+q`J-=eJ>=lv6Yl9V3m=TXdt z6||#^z3JLPWUZAm_xw0TR7DVX>LkC(Sw+?^rH%TK*avCCCQn`fF-(%)Bt}$rJW?z*qnY9!6kGN zm0?GC&wj97|MK~c6r|y`THsy*@EpF$SqV>mGsr-F$?$JraP!gH#C6!OBlH0Me%nRT z?t`CQC^&dU{V~XP@a7US!8)qbe}%K|V9g5pFh-*w2Mf-}^)yOKN%vu-`Vl9_CoBoT zXJQcFHXMg%sL9iURCcb;@^%0pzt&1EytI-(qwG`JtYtxb1+GFhIz&B zW+Sq@Yt%dkqJsEtL`=S=S} zK$0ci&IQ*dkIxku!?cwH=C?3$g5NEdW^O%ejvBaVYsA%(aF{a^&^>bD1J_U?`e{D< zbFs9^Y;?7L4>@%t)g19-X5swy{HuD@Wz6OR^x=ZSpSg?c->?+y!INm+(~ z3oe`+*z@c~#@QQxO+eD?uiX+MQ)3|%sd+&UFxjOc46Y(X3U8l|mo$B`H^2=VJP@x= zqCoaF`~X7{5hrOy!P(F~VfwBoDQg#>A$MZo=I5e-vibSy1sY^9L;W`F$vWZh>D177 z;)Cu(hsQUljBxLB0nwzCdCa+N15P5XD325dQ9%bwz%l zswQ#wro?QNY5o-vy%CB+in-l$>~XPvytT8rT9px~~zN`OBI zc`WOhLSc6uMz%&%*A+(;-YM}c@vb91uee@+bJ6<8uHd5Gmq9Z$-&~8WvPu;I2jNpM z_@|Z*r9|KSr|ji-8m@hLoc5$ziHKX&wGg5Yp9X#N)H-%CL-E6-jW4!6{RnPkV**H- z!iB<)@<^~ITktHyP>Zbd*d@Cx`R9rM$daDyDt=PyGF46){Y(c);-hn~PoE9F{;zx7 z8B>3$Gmm6}lH#Q)&@J-dbx}*4Q+?bG&aDQlT7myYYDUwYyiU`^M7}rmt8UA_ht?qh z9(LLvjbX(rh&1c=t9u#u$^le@6*4_2?DcEZhAXSi+$j~-i~BstNh#ui|NVZ21nsGg z$Cg&G0w9?=a_Kpo0=;bJwYQBonO%GLLQ9Hrn7?a(142+3VN(2`g3Mcv$biWwi1NEj znRX@xYWAS`$dF!wX=l4LWiHC7zz8&*OcDS7Ej>^yK5+e@c+8l?@p4hYYM2dJ7=nKq-6NKA|>WxhxBeT~_D&hpPGiywrlc@X2AEDiWxYT zV3;Z3(L1xM;@Jm9IdMLt^c+CZZA@zaljnq-0nJ= zp+#KX{^EQ$mc|Rq7)qmpLDU=bBgyD7#S%m|NG$q=FmkAib8amM%Fbt9ww+)XP3Bs? zp4;d$LLO&!^EwV$LvWt)s5-KCfsO)uaTM=3);*?#=OfrvBd1vp<$(C96 zIOlxM_lNKOAKbs(_jSFV&)4Gt8(Rwf@e2O4I>nG4qE;5Yr>2~gB2f@W8~@JdmMXO; zcY;M@M&(?6>Zl@_F)yCNqxybuckMPmIwL&z%+lu;4vkS5kuA)nmAx-2{k8C4PG^_f zVBww<(Gd&$sjU`RY}B|>(@s$fTn3hs*@ER>xK4b14^}Kmx1WBAc9v2u2L;FDd^C7$ z9wT@Q2#mx4lpgdh%b}E+iNhyVz>>HKXy^=H$IMWZ=K-a{2+*86RacDuBRcMR|4sPQJj+{;|6o9F zzQRgun=pYvM>(7NUxD(9>zwob9?-!CtOQAzX`nC;aLd*v#fvHnfdVs$egzSj%~bpu z9dGcInRugps9HC{>AKUr5xZIxJ%3LhY}XKs7>R(u3k|=KAiO(F7uJeNyA0w62!hXG zZ9&_Cs-ufT@L=8aKh#ZTMw(`^`JPDjMu@*i>gY{sRK!m7^t6!!SxIkz*|AMQex7<= zldMCxpC|bw4BAj&avh#|Z`cg-agMjzHa^xS6bnn;tHa%XdH*5ZWf}_zRw_@Bf>&5R zsCf7+OJTKY6vgWjOlHX>Ut1Sl5&V1gL?yxwE39$~@jQuBtGZh`*TdpMq{6%k*X}J_ z)v{KG!;_Ig$|Aow6tfrBmWYaA`v>kqT}=osijz(U$~9r5F;!sj;Kn2Wgvj3`2!)!{ zEi}(Ya32rQ`nHe#NQp<4!#TJw0Ix@g><>@b2xA;Dok8F)++WZlbmlAd+nGSPuP!;m z=gNaMyu>akfJ<~jc}`#t?OY;T{GaHw-R$v+$kizWEo$sM^)LSUb$XleHV$LAif$8g zg$k!}BgvRb#;-XagyeXwPJ~gZ&7u8^9ZQwUty+%s=WK)UWZ~&vCM|O43#9Vb`J(H0 zOfD|5gjnbbbZwOtd!&>mqtx^0I2wap2Q|n zs#2N_U8O+{(ysz|bRw;&tDSm#h^So{J(gw=xB=wd5jWv%y6vo~R^&CC$-ra&fWzGb z@*1THL9=qez^LRs`>yDmY0> zYK9YdpxEctXsxY^Pgqu>5Gk*D}2V6gg~u#IJ%dY_#%YCo9{jF%dTNrFMK^J5hE63n)$HxaB1x7 zpL_7rEgYmAaf4Mis0LQc2K8dwiDG>Az!YisfesYQ!qSq#DRhrFa5>sg0GgXxdybGU zGXeQ3=fBI|FfQl}vxS)BFGlE3npo={zv{!=eCR`;76FoRBDBP?Ham^`fn5Be(&HQ` zdt{z#u%&F@6T1@2^q$vo>0|Pj_H86#z*rT%moSdQiq&k2+Z1 z97=lsXGMUfwGBTFTaVw^~`BHAftG_t(hqZ74Cs zwR>k+jbum<_Hu6cZue0a+?mc;34%$_gcD8I_ZqjcQIrExx0}XpDN~LWFWWk7BJ!3) zj|be-ke;m?X|08GgpPeT3pNq(0F0ljycA6iEW*-r{DJ2Wx#!xks-^YUANm8{_uEew z84-`Ax@d(98%~@jl7jGBl`*-Bx3uyWget?(sJHjhk-3iyo_bz$8}_=7GNg0q-9}up zC8PqEn?-!^t4H5-PQ-6PkdI4zM$80CSA?Yj1eOjl5b-A9;5wxy1TCU+XToj$MsU19!QW@f_UrvRW9?j%q5>RsxC zo$}w)Yc4W9pN87fkrVLzVIHMhBT(TPYLX_GgW=m!uE^tSl}OjKKpKJQtG3fR(QIcK ziq@xNa6I{W`*FFwxAaxBjYkiOkHBs5awSyh09twa_EGp~LY7)8ibxap%E`f@3lJxX z!kQ)PBbYU-jTT4Fr`Z|WWa`iF*NDrN%xLh(Mf{6 zuj^mbqHUzr)M%-cj8cPQ`{xJ@YV#k{!2Rm%f-%*+c9nay6wTE6+9ziE;z8@rdu;h!W3_K)QqnQzAj4nS{;Ou^L&s^ zF1j`}67lXZwGbW4Ox$12WkUEL>7Q8F4+Im)rMte=#Rot3k_Z?Q4~agtHJuhC8m@jw z>uf%h!m_Yorid&cjRmKhImm+b)Pk2_*y!}aXfN3Cw_P74Wcp2YmW4P(&DuZUAUBSG zF?FdeyNgJ>9(h&l$eA6|OXlc0Bp|GKT(k@ZwU|s22J|%jP^59#S~v%Ejn&*=6|fQO z6)9{2b@mbU9{N@|hO+|Z{$PwlWGaqoq6is+2N)b9@PywFfNj>$l&&l{fz^0**=}t} zEdP=rGYzT!q`p*qn0+KAgzN7MyWayk8I>vf`p&O-lYa1klN~3YR7hc>Sh$Exhg7Dg zJ!7dOU)>)f)8@ul2u;FnTbvs70luG@hftKX7fflO2i2An0o%AC^UGJrqC&K;?W2N; z%c+HDz+!sx{^*YzA5l6(pHe8dd&NZ5?w~Vu39i3GBkgTFBkru>n=z6)98DM@6WQ3@zGRUF9#JK}$!sqCy%*8l z9WcK)k-Pf@11>vGc|uSPp&RaU>Gkw4g;YH~IbD8w;3o@Q;4Xgi55HbA;Gnuqn!NTd zymd?R&ds8NY76*?;=11YY}T?a-?w8^yNFJvuGAx z7xRZe04~FX8_GXHpFvqvd_%E2{%m)cG$R(6d*S;%xx1(N$sQsf@ow~32XX}+arFTb zg18gCcy;&lR~M&S!WXon^rOa=bAO}^k5rq=G4yJv@Zl2fTu6{knVh`pW-LS-1=a81 z#dyr)noM+~LK0>>KvdJX>R!==8u}~MF5TR5FJSLR>a~WHyUc}us2O9R@ddgJW%hJX z=$>_gdgJ?ig2Zm$_b;w*m~aBFVT${SGIHMzU|)WTj=|*ViML?c#R!0evd^yWxWoy! zFgyVY)NX!ll<>tpd1Zt#3abT%toHtMyPIkaX;BaesE0bf+ABzZ&T| zGP_Of&GqrknY^O@3@~~T*-|JUW#c#CSoQDeF8uQ&-iG4D_i=7q0;gD%wk=_9uQQ{u_-lOPt198#SRU#YVgpZVHdmRo2SgyS7+bR4Dq zpebQExLWQnUyYdDf@9;b(9QCPC##>+DMW*wj|BP6>vM+W$X4e$T7CX`B|}$j0ZjUl za_#wMl$~?VX&cBDEKXd7WnZ5!dS}TR{b$3*Yd;PPzQT$R_3!`voVr@Y1n}Z}m7JtA zaKQc&T(w{(w4*cqHGo&8_UytT<{PyQ?5CWBYVz}t3@2o6Na4bel@aF{etL+S~I7_%p_zXVkpZ+WL<+Is| zyub{WrjgbI9X)NjYr{w8Jo0OgF#PM{y!63%;s->odZXFe2;&Dd7in-6D5HFZy1%=e@Vf(@X!Eys~x(guVZiPI$Or(8^g&EI0jR4;|z zq>dwM%zg>%Pwk*MNyo8F%e1eZL)M-}`!{%Kz|58f8CMby2;}QxZnhd%1grUW7ssEJ?%|7xA5W#F5kVoKEs$?y|b; zYSoQ~A!a0H7Sqt>|20-Niy4D)#C(omkFglQ0T#?hvY&5~K2PK?Kx85Wt-&uIokT+m zz@qMeS_@Zpt224XB2?$w0m56jpxibZc$+nx?yH4r8*(X8KWtCtU130|V2zVqf;+MP zKk>0|R#M-JoU%fgm}>zXweOE>e2@)2|3M|nHJOI96Q07fxS?dgc^xKM85C%!dae2H zsAY~#7;B#Q41~Y7^;vCCKk?z~D@OOp0APWc;n~X()BTr+DQd^vjx@$b%;a7{9)|cg zS6?lfzL=R9&V^R?I*mEJ1crfD0LfqeF_S;6oVDD7cv&loH5sM5Db*^%u7y)_q~?C^ z{x98`#GMuOwc@z>J`2?F@C46Nn;tF)&KCJ;8Mlm|?{9fedFf53y!DeR7Kb6qOS_!w zqWmYuX37TYsJvG0`AE%-nnH8T3WNEM@yUZ?(WT7qJsfRAI$;Y8tmT97a?#D!Ai6*{ zqK+c$?%$&8zz5heF%U07cHdYVbdXrm>vXVpnG!%*lmc@42jB^B8ZV4(ANG_LdVQY` z*iBfo;pR}sRyrW&GK18u$D{4d=q)3DYs!NY**;3_G$X}O!yQM)9{%~^SzXmtX148n zPxDnx;h^}4)0X;LlrNu@5PMpv#6b$I!dzDHV~1vXXK|$_m0SlI_q-9{1LG*{RnLQ7 zre~TRboYyy4WBoJYpx;XE!A2)l^u(-gC|J9i1C0f2X}4 z>c0fr^HdTV0uUVxJO`B+>|`Uv4*jlD{_a|`;7coQTl zvgA>Zdu0~)qup?g*E|n>=|(b-<`LnO_y8Bk4jx7utaI$+yFMD7ek@7QF>fAUa;p zMBi=Se-xnm3rZDEMfe~NayHf)Z~Okl(SL!Siy8)Bp7f~ATbs|)$fXZCS6QfYdAdjR zPt;MRxCq%f&fxK!?XCzM4r*Y-{oki&;E8-LNe6H21%8&aKYaGG#gTsxg~{`jy*ckA z_)gv*~ zA5TF<@--D0mppKzB6x`pzM;~LUwH)WS0G8%nLp^j9qnYx%El7I_zz9*f?Cq3DW#+L zB$D>NobMzHmtLoXvAAbn)VLl(ymiUnhEVuWfN%q(jh9b=Xyz+OwrE{Wbeca% z!4P@Ji(=rLhpp#NibKo7MOWfhU#!6XRem)1{%E}f{Mm_uPH^Mx(N;=00kVFdU?I;{ z;=c}8a28C!{WV>rf^<1HsOI@pYmMMj{eo_~ceX6Bq|0%qAXZghM8`>@usoz%*L}7~ zNR4&BoE(LuO~gXj%FROBU(kJiBAfLH%(9LNYn6T_?lklE|9cqyAV%2V`gWKcmey!= znb<=t=^zy#|48QWG~?YUd&^jb!J~+wqQp?y34!>bQk47mIZo(8KQowb~l zd2)3kIIEr_oK2OykIYPthp`{!PDpjmOtGS#O40jeA>Z>cf#+yKC$K+AxJMkcATWf7 z(4Qr8QWixd?;OG44fJXEGt7H|xMKKi)i2d8lUAKYP5R|Y$#Hn?!_-#ljf0|GzeRQY00eOE(Yokhe+*IyBXou?xX^m$l`Ytie+VxgVW^#xB>te;lyLKit0gbr zP^jzStyrie$R_t3sQnii>(hUcE3?z0lrS->I!ywWb<3L3;%xU6|*P* zvZ&E~=YF5s(R{c!Dd>a?yc{t6i&dm@#;d+u-5@ICJfLl#Rm(!_S5QazyV6d|Fg6A| z04+bf!6yM1Xno9lqwTz{@3Oi9+tZI)n6ymrkikWg=BTJLn%h!+JY+X6Uvj#mqVVMuIZvPujO|)I}zs(nvL5DlP|RD-R(HKWa`GVe+LsRi*kvixp|WpHn= zekWDapYbj*#C*2S2ru2c*(O@_?_`+NaeFz*@?;Op@0Y08VSPL>>s}k-*P^$8#qksV zC3drvpfun9#q{S1?~91B5f3dNq)D?G`y>iJxsj9%ROaBD_c~);mcxy2e)%cpWlLEq zp{3n5l-_5lgSi|6oU*h5+`k=B{A=@^^O(2XZ+%u0M4Nj*{|G$M_)J~CX?wc9OW{Iw zzSx^;5hz5$ds19guM;Ld5b*q9Q9a56?IMVT*FGS<-`ICk99aQFTta`EX2@hy8G?(` z-`gogHXlWxJ#G^~-~_7AXq0-cP}u7Wo*uWE4^JjInf4!zAj@0DF54pmahFad9*M#?^a*oxK6eC^?sRd|O9KKKQnVgVC6KC*My?75>t{c63YcB*ac9q73b zetFR>e3_LWu+mS&1O^z&Ly8G7TYW;7+}&=zVHT6VnJ=GBaH}cJok2St$(EOr+XDxC zNNm`^V`fcQvpPTY0RuXyw;G#M`M?k?4aRnun3H01+C|17Qzu#ICshmdC}7Jh*Tv9>EsRUL+*I{Y?sHVZ}4K zTpY>u?-=7MP)OwF`lCPBigKkNA%Ca?26(F(k^ep3Le2g|4BngS#e~8Jfu6%fyQ3R( z8&l~&JrIxeJD2b8thdqw$7!-rCWM3A>HE)tYA=0i6a0nhF*cnJVlJbuh!eXzdChSZ z$f|e~t@Rgo7Hgz!etl#T2iErZYvz_$IXBKp+v1$^KAiNn3huF3C@?}W@g|rm25;Vp z;!~=oFubz~HhVK@nV26qoH1`?U9@f*}=mcov(`cLS?$5PLm zGy@Xm7S@v1&wei|fBj6tkN(`3cR7Pq_CGa)WmSs}}`eFu#d88BFj16LgysYX#>xqPE5UiB^N=t=~c~L_b_DKVvwI6E4O}Fpt{e){aX)mx%vdr^3PtDymnM9tzGi9+FXtV^w~=mN1R{p; zo_CZ_uE5*F!K*%OcZ zHv(}<#gb2rK*uXBwi80X`rj8JbO*ljN09*j^B=7`0c!QBttX4z!g%FOnU>61!^ilg zjQ_lo5P<&sAbI=Z1Q3lR2=5nN>ivLy9;{jaV1WS*NX!ifGwiq+hfZ0g`uKud2!Z(z zxo|@!TrH~B4GoNF!d*mXQwJC_JSf{})B*4qAr;s~uWG9PDF&j%;wTb}E#f2m1hCeJ z;Oy1e^QYwiPe95AsN>Cy2Vai6%cL3PYd2(nMb+8wq~ zf;xMzj~rfjni1A4L7yg(?SV{FdMgasmm?Dw-mspz00Nr28PYT(qrdNpI&0`(IX1Zf zkboGI3y#@H+Vq} zu^)5c3=9ezV|JLu0ee`(Oh|Oi%qx_kjfqxRiXZ&T${)i@@R}0F0NSZ911|h35JxD# zpocQ{ub@nlFE*}=qN!H`3TrjY{8|h`^oC`LCmNf*&+reGc925tb5}e3RnfOYE0x~j z3;EFCdE{NoOHrx>SOiAcg`HyhWJ zubDZw;|HPmytUgT4?pAQHNq(AYz*6XL$fFkDOha0_InQ(Kf#1+HfAiDLnoTDnPp&b zFPCBMZ%ib7_I1VwW2PKDr?_}vnE&42H@!H+fyvshe(}Mk)77ID{H-I47=3lpHH9@JF)RpxZ6~=$ zUV`6UK8*Y>1i^rdAvnc}L&GjMKiH-n)B6*gspUxGES1D?YRHmF#qPFL z*IL%Xnxlt4l?S-kc3nwkE-FLA`6;=cuyO_Kx#R4sze3WT@3sGXc+Z*fk=Pp#@}bg_ z{#UA1dmBAz>#XK)_|cZuM-9ruplwTC5yDav2kfa1E?ulvg!IoV31B*u8&M37f@1Jcm2Ew;L}hrY+2eCLOdSfPT~8 z<3KlUKWG}Qn|OZI9hNIY5AwPNybv-XcY=I9E>oWFb$0|8Ycg%+%87Z{ckO+kMOUk`*O+i-1?1cCvTw!f za1nqn*LB(A!(ZrZ$@DbPwd)^vdrM98otHVgoy!b=Kc!7Fd@q9nrfNNE;tsFbdD&D& zyB?3&{|*ywD?>)#<0Cl0ukbNo(a_WGkSSAKbF*8+3^XM<;z?eFc}fh#O(kjch?<+gCD$`oFvvD?1Yl z54^r33In?j_*HET_Bk5^?{NA&Q=UO*z)b1d-ueKsw;Q?7U;ca`v6Jyde^y1Z66b?# z3VqOm5F6}vT=Sc^jMd-T*czvt&9o$7B-wCBar6G#BqUv$Lc)dPBoV0bsGX8RROpPO zOWXEmvOQBiQ~PcG#KG6+bbZvINe)h#<@x!mf4!v2G|7EAD`riMnA);@*ago0)5TP^ z1H4HWRY-xlq=?wa4N=iFeD;EyT6QR7aw#x_V-!X?z3{z*wxKmHI-y>{w?m-#Zjz3e zn28cw;58#^G#`olmi^H+=T%nucGB8(s+CZ=jXbssx$Yy{#>Z4ZRa3?xpg^FeV#=gE zNQwT>$iF#aUJt5=Ixon3Lyuv*dO%vhuWe{A)(m<|*7VZ&S;R3)?KnD;p(#Z8M;X85 z-6r0=e6%RO9$;{M`1Ymw>hHhXF9m4V@NWNyNUJ)=>_|c9CJ{O-c1`dDT-W%XT*HDH+@Fz06uh<+9topN$eHi|ff$$Ub!$ywkl1weQo1<>f4ej$%}wQ8K+jS zQXF;e0heTTJ`DpQHY!C>_RcPBr6Wh+~TS&`C zYB!-wnd#E*yx10p)G3?SaewA9vLT`utoqJ6Dcwf+ADb*u34)M$l756dOW>SloPO0r zb5pI2G`{cHjBH8A04p7)2 ze4OTK=RLaFQa5ETKvQnB0&oAj;nG!j>TdJvAUKB@VUgyE_WQg+b7I}4lj?FA53{BQ zEMnfn7$?Q2RGUrfLBks*zwsIdOt3N`EOQ*Bs7XixzZ^?Gl>Z+4GZ11MnssIV?{aWy z_nj=M%93?Q$0aV?Q*bN$zFqNZ;Fz4Z+&CL4WS0`(Imr72zKLHF+dJUafhtgkaoo#v z>+3%%m>oJo(L^+W?*Do3Uojkz?0jlaPOBMKI|p}t<+cn8owh@xb8lqh>4LwBc&oaV zPFzLGb@{9CLDp!b4z$i6rormIe<$zn?#;kcvxYb&1Qq~({0Li5Wo}6%;otj*ix?aI zOFj%PaGqrHxCZ1>XZz9N4s5CJ#3@qx6`+Lj0*CX>Uk-YcFn6Pm)K!3o@8Y_8*#71y z0%RgON^op*d_#CjQ|;r`8=FJp-gQxQqz-1HZ#uaA!1lwncOk#p!38jIGyRa;|GY9t z2{UjYq&z$Aow~(lx~`9P!(>LD&LAhh1jmU3ryjx#uZ7zjXSCzCK3|4Ip@8j2nF#Lk zAvyQIn zHhjFcKa+H5O=G9t^$ui{yZ7�+7tSwE!fhATkCci&OxCU|7RqZqSV&>(n5_6-z0s zncZ_YV?JE=dD~QZWEjt@6gHUJ?Dq23Gv`;Hrv?RO{4wv^WN~T;an9RIGsw4$OAfuf zrF|^WYhX$*e;P!_CD3=ahas`*Wf; zg5E#!{W^T0p_IJ3(VY5_pe64>AFd-Kw0V30WY12Z{pO2K%3u8!hlmI@8!7p1`BYBE zmxKDlz9^Dc+}L51~Qall)?J<9Sd!tXt*EZR_PSAu?44AfKL3y& zOtDG%TPtJk+O&AwH{_ml4RDphJzm~@k;)072`!&8_9^SV{TO*_{71pxL)F^5d1Q@r zs^Vu5N@j;+*FvrZ%YsX=q@xdN`vYL&^V-4s>`yxuwwXkU4Z!lxMDl2=k4$cUZDYzL&(p zUL8Jvck}go=n`U!T^Gk58AtuFDcTbq;7WCHW}jp;=!B}?yUN#;b$Z!9Pw4M zA&vhy82+x=@7QmwyL9iIA?g8#ic7OS+byIdeLIy9XY=Ojy**R zrSx%s6r2NHY7$i$erxS`k*1@aAFXqp)`Ec0ubXaZj#v`5x~!Xpl7$IWtxk`sL9e@! zyOK~w#xUy=vV8J2&eE1U*@rGO$jS_gS>KC{{#oIRbZg-8;>C6+4~TeO3?u-aiQCV z;-$WCmL7?5(YG{z;DK1x#6>&8Ft0L&d%T_3+xZ$l_#woKqVxH>=IMkN zB9hxabwR7;FlhUw`I%FHHf{TUW?Sn2q{F4(KtOBNCt(UB7h?P$#2-V`Ul9|dJcJDN z90F!JDYkX~4M9(M^#%BPbQR6W*v0YtOYSTvAo;2bWU>}CPL-I8;y61u(a0#?hj8SV z%jv#df9Pc7aB9@Str`b~hGYFrpA~Q7tSPQTd3fj1@!VH@=4qQf8|Yu>CSLUfk||?q zqjluxWU#a+BWO~A9!9OJ|Fqp+4G;$uT7ukV@;{d!T(N`F|Wiwm`(p_aTQ7a9Svp<(C{*2rj$o{Z4s^CAYQOd!MEg)p~Yr0ddwC2;A*0`pz;Z+^!?t2PTL* z&v?wOQYx5##16>27;I*p`<|t^9srvrl9Cj9ihZSn+j&q zt&6Mr?H=G8V)OK_h~?)ZDiG46acHUHP<%0MP5hL@@tWDBxewpdF?d(5SyRs z@%G#e5pfQp3;1eR*)Is?N?77TU2OLca?EG;^l`_TKQGZ~H#;i!So^!vUUiXFRN6a! zF|quL8W+`fTBd8>EbmbO^PI(c!Q2eWZm8@}aJ7D;X~|EMhFE~Wo*5RK8!4Ib+O8^b z6f&P{zyv+GjUkBH-6W8nP9k8kcJYa4!+8!?(!sn^#{Hs7iPH^jNQY=RqP8Ik4xOj= zH5(KmW7u4F{;h>`s)LIwIoOeIqkhw=VsB}#g1DHWf83ss466Ff2}r_|egCol@WnLv zo$=hC&uPnTL|@;ce%|mair^OPg#uzpp{_^{U#hWp7Sx^j4kz-C3Y<{S zlfkC%dDQ;O>jzpZXUNIdpdQZ6Xvh|k=2XVOqBBH!7=YGu*;FMp)xR)ezl||Yt-qzZ z?ady4NIdBO2R2Y_OVIa#S?X=93;tMhIGO6Mz}wb(4PsL%&c(z~V09h5ho67UdFK2` z+W4ejcFOuvYJx~z{fP_g6}!}!N1A~^Tet5sHm8)tpEn_?a9~OA4Y^u{?s*>nX>NKg z?~wM!$d-7@zHP3%yfNKk>J6DE8a=9W^r+4#TnC> zp5amdSe48FA+%0#h+7LjL0JukwdlF@ZzlH{F51rG)t~Z(dnzHJrp6EQ7e?`#t1nKX zag~aVu`wFS6oWs_o|TTNvYE5Z^$qsVgt@Y}o_($wjXM}&0pEPHlYUZ4Mh6~y{AJlU zw_l@U+M#w&TA^{rc>knsEPD)dMm?IP;(#h(&-Yyss&=W#C_e4JH+TO-2RHhM1D~lD zRDQ|u?Tn4w$(;B1*;!o@%h4_CEs@!K`_G!=Pw>SuX;^pqK4rmOp`w41<)X*A5D@IN z1A$*EbRq}o>(dFJ;2>7)v{Ub^_sH7dzGV1x(-QBQ!NyReTGYU?wX8ve)4-(#{4&_)z>!^U zze@CFLBJqtavKlM4|&VyD&G6<-U9}P(ZFvUn0dah;?~ggrUSA^Z17L?~HQ6Vz5yvw=KWp^hBKfcS6y-^iZ}DM|U`gCA}J{$_adV5AoMH_i56?K9o=9^d#`or(FZhN6~knkB3zfG`S-8DiJ3``w}ya z)gltZqRx}>jYp}WAbUGPf_POXAuR9~#jLU+ABD@qwe2yPL97yd&^nv@+Wg+{J(Pk@ z+w$r%S?4FrUXDe1exUG9;h+#2{vbVp7N2zB+n zAJ#hdqQlBs3c%R3Gyl^BU(tICU%w2zI(@F!T2ap6h<$V>S%I2IQ)Ge0d(Kbx8+K9! zR?5_2d|p6J+V}pj?~nG#Y5}^Ejz0$SRgWIT{JQVDK78dmLa$Cj*^R;&?czM>!-@)) zIa^`C5fPoC((`(ZqLd*P_f#R3{pe*`WK$voQIs?=FtqX)=aN+oYB|b+#xsUR{mr7T z5p68f*LFuza>utQL>+Jwgb$X0#zNA{_tS*BV|`QEF7$E4iV=^64npnCHsJo z-WM2MlIlCzN|qJ<6DN5U6VkIZs(@er6#IFnrxlog*02mZP`i4(CFm!-p>eqdgV`4{OwsOzza+V z&OY7B5Z!R1?sdVTjWWw%hnA=U^N&K^RKyPp>(B1@ZiJFvmb^fd$|>br+$nCl(+|i+ zoy`*i#>6dfO9z{G6-;`hllMV^rBeQAqSbA6*M)ah`(XoG{8ca?2|eQZ4stjX*x2Ph zRi1b*l4?nZlj@-roi_tPe=T!=W3TfEo0r^hl3*(F?S`y&vO{ks>Co{{Ao02NKLe}z zmA`tf##>7%8i)(8#rV$WBytX;JbeSP*&+{Qs1%b? zOO?Pzkq>g|sF?B+G7SA+naukS4?l2wW%V#GnZLoU@QB0QJ(bxWQ^HT&3ft_E#3@i| zzwl{TX^WT;fS#4InDMr)7>FvfR7fbmtlV+tlnaIU5H4*npzG}3UA4aX9?RcE8JY^7 z&8^=x_5{~H!P4j;Bff7FnY$g#KbKN?+I!r-n16HM`dg(D7vji)*DOSg40X*DsMf)^ zYNNlDs3Cj+F7=G@z3OLSTRH5T`F3I$aD1Md9q~cX+B3u~{g1L^@|w#%DHuF=pJ32 z8NC8B=B5lXRGM&pi89`OBhR2`ZmO%iePYD9h0hnh)&DZhQ2(+3HBQPt90??8fi}622YL5?whFQa%N86 zFZp@SY1VNm;o(QVktcDLpYKZ7$(}gDUcBqt??PJF8pr#)QNz*x>{5*WSWl15u|Y~r zH#pW@1|={yK;nB2h$!p=TeHvYk!IGWTQ{|M+CbFHYR`Sm$I@^aDMCDO@17hTwlJJ@ zf!sxdvR~5bmNKYq4k#`b+!r84R(5@!2Qw3C1X*edr@Jc=L6`k13}yJ8^oM&*hcWTfcQ&+T6S&UA|uy7{#f)dt-HH) zRh34XYGO1UrcN=KYX8gg?SJ?w=2P=@(@EycNTmLPDNae`a0ViSILveb?4F1;GP)WN5m-xf@0ut^L)DS{a^$k8`?>k z(m}KK9NK&N6x#`SZ$mB?vHTTeR~= zWK7(oXg5VXN=)(zfa<#Z0SgNum3lu=(cF91VZ{GGWPRp>HS460x9XbkpG%X9syQtG zCEuh_n;l*!pCPte=Hg@{Gqv5b2wN*iQ;Vp@y!I$m$2O>g(O zpl7!z<89L+^pIQFm}8MccPn%y`$>qV$ zPee^&Omwi3lH|8d4);+*p1;Bw&WlbmH}8LgBME~c09oQGSCBtv0uF~|ch(%8il)_w ziATaBtfsRvj}C`KYah_JJp2i|HtTG#F+~?868*{DlC${H9{i&pR6W#fTAPUOEqJQ3o^^3c^A^B#0s|3(mUc9FjA9 zb8{3AHtJPj;aD{Bb9%wZWj^Y&sWG|x?nB}zaDS7X$T_|?`zbEK92bieT%8F`Px%slssCkYGY3uPn%Je3yugprAgw8WcY2GLkK(t!5BkH5J4l zf0dqX5OftckTMb*vfSqMag}R17r*f1e>9! zBRgd!=UAbvQ1;G9L^87uMc&B{*+ll<`+R+V`2G|3eZ8*h`FuQhfC4!&wg=t3TH?N| z>aAibdGAZdZ5Fl0+krJUtTNV6J z$&MNqpvWaaZ%*wkv`&w%w(+nVLrs$}`4K^^BD z!=EfgGQ$!Cj!&pfVa+nz`+Mg6D2cJl+xQ3=wsu6tw%@d(V#D>!P8cq$k*fD-Ik5j9Aq}_& zIRdN>2(GazLurs%;C|yd>?&OhkASa+Yir`hOX9J=q9uWu$z!IszmYdQZ1;{+y(53x zICkic)%EGDdymRlqRiC_zigWB zGpEN&LS{an4a`yI?;+Tl#6He&oG<9K7LIcpFFPUfC!0 zAGi<-eh-Q3qOA1y4jnyN7iTz@UP2+k?>Pgh@7P|$x-02*Rlf{u(od+hmCE$Xjbzh* z<0TEtDIw?&lmgxhj>C|y?+;;?WQa_efzel_a>y@A`=uff3P<5L*nVEf-T+tKf_DUE zE*~RWfypT{=>AsLtn7UJN~dQVraZtuN5Er*KJwnZ zQo*2X41cUduz$Y$^u|(r*3I?hlVg=U%ojztRyEkg1peGXRqS#OnIz4Gf;{aEf0jTv z!~J4%s4C$V)!UL8ei)SjxV9O^IJ}ZBtL81EP7tEG^pr-tAo&a5HwmNTo|Sb$gK2)o z%Zd2luTpks*Bs`T3)zW6INoIl(F{ar5i;Z7F?=GcZ=acaFyvfnWR%#<%xp0s(GR>m z-=*F^m$K38#~)M6M)R(=Hm!?J?n$do-w?8DS!D@4v{9yp%{C50`Nw!vBW;1yhXVVm zp1ht$cT}Enai5mEn8}9kS)XaegsBBx+E2<@QWd&Nza^7ZShxif&EOqww4;a+Ir3)P zjU~sb1(3YQC1+}A{|Iw+{l8PunotJfiG-%#pJLt-GLpkG?YHEI&TUe5gu*tH8uXc- zA(v5gw~27?hC-852+_Zg(7?HhA(Bc7&v`o`DTt4l^G*7ldtE{C3gY`$k=H1fvEC+qS=b)^aP2dF zf1)aIOUudPOMFtC!nMDV^xQed@Lyx=%LHpP9r-dpEKJ@Jh^{YuD(e-64hEy_8W?i> zcCTYR{A@+c_;kE{^#>W!cs@v1QvdeUq7nB3gwfnFkY1(FGxmI%PIc#5$R6peY)L5yi71DkyGsQi@5;jsoE-Mae>*6 zV13D8DnnNGelvFa*UP`tQWTDQw9`t_KylouubJ(dcOb5s+LRGmpvR!i_Pvo zs1aHqDN0SLB@)dcOxB)kMhB*NWQuC$GKd|N@pHc&@2681=Z#xEH#q9nS`oSVIDzqLviLs3zw%M#rW<(=Gc>27v>v`3kBex$ zRmw=tsEZDAOmKthd9i*1)$M@iVoi?gEoa=HA17oTEs*<^=k*AJ<`j=M2j*}8JQlq_ z5Kx!i51;~XA3k2v8~-DxRt@rh!6?Ydu^#G6F(17{yR|6n$qNVC2oh=Q75Tx%Z3@&E zU;-1LYhAnxn;`N~!)bsU5Ro&_VF)t%roND#Ks$K#cM+kB4P5$#xB6WN2Oh|nva-kS z7%OqAJD9Bg%f`udy<*rSg=*4g`6E^Ka!1#-ewQ~s62M+JMf|&I{DKfxzs>%!0nTZN zb$a2u|5vAhjOZzeS!I%l6!@JssCZJk6n*Z+9Dy$AEm2*ZI6rM-_PNU}nhDGNPK#|Q ze|ScN+7wMv$hw0+p>wdrAED=-<%Z4({nXUziQXH!E(+^v;#lw=b&Ad?7G#?=wvm$c#yJ^kz# zM4XA7zqFxGQ!lO_W^fF337`u-U`T`&BC# zHs>mwlP~gqL*OoN4_wh@@V(lWQ_RtdxZ#(bzzyf!q{Dz(s-TR3egk z?FQ@bT)@0Fh5yo`=G#qr8i~4PdmTRRFW)+#6PCTa#kFzb5916(zK$bdszmKtKFp=! zFNDeh2$~}_|2E9#uUlkdZtvR&hHta0{J3pFUjG*bBGqG;5t!D6%y&Xs=vgi;F-m(;<<2^>bKxro(%do_h3X0o zIB%?gV3n>M>A|1hn%Pek(y!f8B{7kF@pwK@Y zPS6z1Z~S~$EylF$a?N9#DJFQC1B+K+x;P*Re|jaW9EyI^Kq6jQ_O;(A$E=tI<~4{3 zmURvFiHrKw_j6}Cu#E^coX1AxcGw_f+h%!W^sh-aD#sWykuVI&hJd4!Qbq#y&U_CtWm=rJW?B+RoQ$uxKFO=i+LcoR5;VG?Eeu@yDYo{`4v%uS z`4X7H-Y#W@F8|i9!tULy#`#hR`|co4F0fo@_n`o`QD?}{i*Z~=!z7^FwJ|+SpUj*E9ocJw7Nr?=RSvp8Vqw4Eu-C&Ywdd-Zjgu-HtbVv z&i+dtY-u9v6^FYbv07C?mirl&^n-T2Z-46(!sG6a4ym}#h(1qANUHSN6ABlC5JjQWL_xT zU?LRMPG2g0wKnpaS3*RLf*Hf(B{HCe<#qHmT_8-ZK($F~Jd*s^uqmVS!zEi4w!Q2G zw6*9_qouxXqWJRIJNd99~h{ zWOQEj&UHl~f59$TVrJwk`x$=ft>>8y!4O7VSK%Hs9MD}e2{?Bw)?UkORnLpi{#Vno zj!bi@d2Mza>wD(Nh^8P(SR(r6z4c~z#`qR5=z=qjpH^MRwxEMQIgPz*B1$dicy}Ee zNLP0vqARkfa_`3;w76CNx}|9E^Qc~pYqWf0((z#Ro0UBpX5)T6+=$f-nDTz{qgMZ%94 zD;`b>&=axjd9Z!Vp*c%3{>9+r5m};j$M@X55%z9UR_V^58QYv*-A?RNp~*v+;9D zh??^O0dm8Jg#_I>S|^s|NlW1ry$UuoVvJH2i;Tzn4Ve=o(fc3qYsYYz%cA3q&#ikYCsR^AwM)e;%{BzWjuDY5@__JCLgO+u%A}^s5!!q3ij;JoIr0(C8#yl|d z|5<=n0W$Y2OP-GP`ItXvlp^!&g;#o;pv2CK1-(3E>5PMW6o^9UzH{ZiY69F6xDg2e z7i!QR{x3ZqUnG>hnhIYg2npG56`X*weJj9f*Kpn9J7WE7DOF@{cJVk}d#_hX*wW7K z-AqQ&Hw5c0Df_{Ehm-(|V*Gt}zcR8*L<6LEH?R>DPxx(Eu%(fe3IZ~-AGV*@b8DI|5iMXpJ|2&|MV{+qx(XW*^IeahE(?AC&4dIy z&3mz?%AMc>@1n&;($9o`jj#`+F6}9-1B=i)KbC1DdV)2f&@);PA-N$!jVM9rqM7L_$LmLHDB1t z1h%xb9?8no`;~FP*V>P4;yZQ-Gi7bE1>DNa-gy{3Fw0d?srjRb!x5tipMVkkH_`z- zMI3C|bDtLGtTbZIN0kbAeZg1ti`TV$a+(@iM;GLyEX{ey)LLh(Q@PBhHD7;JrP-=v z>37d8b2y5rQ7|476~V^xWYMB~Jq#?18<`lcW=U^Nf z_oeQWUBJj}p-vRDFEA$B#xs5Z8afwW)cyC`XoMFjV*c6}9USzH?jCqO#B5bbojfua z6=EayV(?={dd+)I6C|?e#iqvP7911@-vGFLJi`Ng-Xi6L>gwR3)e`?mVplwBx%d^s zY82(4AQr-XclXrLOoFama+ERg_Gxkg+$6NBFP%{N(zG|RXjdKVm$+NzoziGXwG(0E z&tsiBbP&0T%HWyo<`?fNP574m8J#S2`@~VrE2{?o`cB9*=D)L^Ua*%3#nldx8l}72 zV+-nWf|W4zWS{gdJVE5zu+8O=nY)UzB}Vk`%!emj`YsZGlzrGk3W>~2^XRDnNq3PX zZqVD&M2aW&ugAYv6W!OHMp0?#uP5Xi;BAy5W!hrt{IcYKh3*`)MB&pjUf&na{-(j6 zV)w7ze;TZG(LP-lv+(?>@#Ei{`+uMCp8Zrz|I#a)7fWPtyr$%~;*51wv3qu3V*bIM zj#g2jwQK0isNUFttv&P3vm0#w&q_Vsx3GU7^C-jF6J}mEpQx~T@SI`)GXl{BvjoNF zBe}QojN^VtuQDwX+7lwN(@FOf)uOobi(y!)~=vq=nfZdAd+Bg*V9<& zmmsbI){2(Tor4%3x+SFkv$m`d7GE&OuvAd4zuSm#OAdMA0`Db!Uo#So1aYlzWOKGHsZLy+Z@^6S)6_2YI(O!{ifY}z&#l)oi1tNzg-Vq=w zrBE!JK#{6{I|_`zDj8K(`57O5PnVP8GBgYY`;LEsbTzDAuNKnE?|TyvO}{8UZPVG= zJxvSFzD-Qnc&3on4nr{977Yuyoo}(_|C^rN>`#o>QtTBHCHln2>sN?c-($?y^iP)P z2>KYtd3XAs6%`UJXpK=)Gg1@*JHLm!z-r5e7(D=gI3G;`BPG=IxZaj+`I0T8vHJcd z-4VS@(?mEZdg1-_zZ|Qx*q4df8UqGWRA5`okSIY69<>lcK%Lzih7XLVO7!Nm6Ugp8 zufOy4N}X3?LWV2aP%Zm6e4`Op;Bb}p1_q>=@uMAneSXWp!}uK$cFBp8Na`b{X4maa|9mzaqGc@O#xU#ADqhUciS3ntXez2!4 zKzhaQpOw7+rN%ypFYB9b;!D$QSl`Y097X?7vwwOHcQ)Cb0FOL&yrSwt^Z89qcEn^S zR1;lq@dMC)#(j{T;stR34V3`G+=;`z->t8p!@QHe&L+%w9&DRJ``xLjQw%2ksV`Nq zBZZvTC1G*;ZRpe2z%T4OtR}?xe}o4?Zwuis3tNdbFZ*wz8x}a)*toLo>ZfF2s zXk#5najTvzH|-qXe9;QPV(e6X??%DAtrB%cL6epBThs055)MZLpz+#rIq7xOHRGNq zr_>hAm6w!<|IDL|NbbE=s7z_+uCa|+V0UR|*z9*IK6Wr6cLD5oIOV%9JQ0f@-*0e}{kRupAP^p#QQ<0vYJ7&d4=3&4 z!Iv&nwU)lF$K2;jAu1%ytu1Pq+^f^Sb<83X=b8S#G*7sMT|a-z_9pNs5&E7QLu4q| zl|w@?u^;<~4K)a#5^npfRVH{_UL$RWhw1$H&vO+KO5EFapCPhu?w!||nQDoWRO}rh z5WAJ6GN^IBE;sZ?-b@fyDAquh=&O5MPZ0c`?B9<%UzGYYU|U-V^CSJyl)-GBE?5dQ1wYu6 z%}4#<#J^6O6{2TX7`%U-0O?5XrN}%rYrk{V6c&b2t%ULZMgC}8|CiG1AV>Y&_Q_cs zCRoCWTK5X2U*DvnbQ^&!$IpmSqC!x_dpcj2X(eq`{i*^;L(-Z0-bYgN*BF(F)X%@| zdq$q=%pGb`xmOV$VWa5xK=18qvTK2DBRBqx=!xsr#SP@{_d z<|GYtW4Pp(|HE&}vZz9*G>GkWBLXCYnaa9-{WJc`n_it#WrO|oEUIU;^3VLnrJZTUjkV5#D=<+X7#TpA8z0Pi9KZe*|ig?M^zrN{7GcI=DyW&PF_A`;=v4%4J64|cI0 zvHJoUf=9XU`zOA%`%$6h=6JIY0=U_eB&6+#j>+`+hA}BS(xK!ge+d)Jc~WOqUg$dO z|9C~6??RcjRrh=s^-;*C{@c!Fn9T5;iskV~$~Sudeny*BL_yW>33B87T*L6+mn#Jj zk7kR=ENaSQq4AkU%Z~Ed$u^14R{`KwE=6M6vNMl0K?UG&KL0EzYgWE9UgkN|RW27Q!&EfqT_F=a`JqLfE*Lhq4SmGADgbO9UT3%xha-QAVkA4<Q@`udS7J5lpUz=JEVO4q>4aaCn&k)@b87-K{;)zy|!Mr+s}7?k;I{r zc5P{XILhPP=}Jk@t!Nzvzumaa4qoNn^fyFbVo~ysGYn`unc-;V&0wpx7dTJ!oPNev zElBjFV!L5V=QF7T1$}Kub0RlT{A}tO((nx6c1N4;XNxX4jeI|?8(@?%J*n8-Bgqfh z-O%{BOp=w%1BV3zVq0*&G!jd9i+HjnD4==|KP0gOa%{y=Z@Jz=h=dst}v>*&*i@18o0G5@yUBiuSQ7Y_Ge?T zLE{G8ikY(H1q^zqzJ=#ZNX|wgbRxO_l8|Vgl9XVZjDqM(7IrJrTMQeZO1QCH`yenV0JhcRltC~%{%G?7(ZQXw7|16< zv}a?Til+YWn{N-pUG}1?HPqJNZV>MT*`Gr0w=RPW^TM;z*V$(880gX-e|@osIbLY> z91@@5=Q&_y#m>g;_6ofE489B0shlvNu;(Wa#qEF7wk+UWd?d*@W9+k^;~8Nl~>Y`vP%WpP$WhM9>p ztbkuFWDEURR$Y*EK^D})I9yYEqKResvE9FYKb#vR2cjTZ47fp-l84r;JX3QRBI#Qm>GE*9pMV9m>8jM5}M4>}fd zWPd)6#c7?nR$(YV{+V4eUIkw>ACk)iFCE!`hLMY`zd(_c9rhbT1kXeXI&N?ZKf$MW z5!HLY-8I=hQ)nTSO(pQMyKGHV9NQs>7=Au$+rl=l+R+2dnVk$9-wg0q%9ik_Qb^_P zhF1;*>1~NK28IC9W-x#LnzgNkrVPFhZL5OIi>Hd7xSh+aFrbOQ^%1m#5QNv!rPvKS z(+tI^%A%u}PJ@5J4 zp7Vj-?We?@gACt{Uy*ZEMw8;jcGGb6g=#@WrMG z)@$EWEQ)PWi_Ui5RZuFW3+VY=b9cui_-!a}r=Dxjcekg!ck`uhJSRSfq2UB45;dwdl1oG5MUP6S?MHOxX_CvY*m2ZxWF?GBwFitUz^q^j zv)>Q!|ApHS6S~FaLvkf58i!xP0Y`~YV1}_5V)@XHDwLXv8%XMXZ+8p7m4NRsTWKzU z8F6z;!`ch)$r5(+>GqAqq-gP7Gvr7E3)I+NhG3_gWI1ahTu|BLSK;O(0+AM0kE10b z_$(3{TnRU-bJ#I?EV)YKwPK!~$L!y>^_uGmQT;%f`%z@kb2?!lo4E8@^xYEOKD&N5$Z|FRxJM1j+ zOU!t_O6PjDKoOa9YOQdfXpLsaW1o`Q(cunXJ891I)j6(vLpR6ZpFSRahw1%#D?)ev zI?)Fcs&E}$mp|BNWiGA_gMKbeP*sF2mtXDVv&)Ep6li6~C?z4b`EEeJD1G6gt!^_z z=;f5HT@48tO3S>7&r|WIud%G8!|)SuM@~t(ufp#OEB$n~0w^vGYJ<^gfiNi0MWibz z45864!?uT?Q%2>7%ll4!m|JXBB#qL9B#GguM5xpP*S$mq!{hnR_(p zZJOJqf@=;#k$WPpxFpB&8iOFi42iBeGHH3vM9t3(d91J+MFeCgO)Av0;R^Sj5K)W# z%Ev}XV5MN;inH$Rru8s1@{4BcBI+5_Pdj(kALkFyc`tAy_wbc>Q5(lALe7@bfE_M+ z|Ni|$vKz58GJMR|_Xp0b&~d^Pu zCFh18ezQH~rqg!OudnJNkKT-JR+1$ps`l)hRg)`Vv~^mFNb^7pVb^~zK1-MDyx17L z0(;MKqMDegXustGPNA>F-Z&_u4Llx1l4gk zRmFa>&U_u}eV7`2JGk_&#%Yb~{AQ_!&yY$g*KaC{?1H-1+2G zW}LH|T<$JC>=~J?7S^G%kNs_;p;>q+ZggwIT@r>)nM+$91a0E$eGJp8_ z81EMdIs$=ZlS&EP47!vL8{VjLE;KwSz`rOs#aOp7YAujD_~SpH08cq`F(_f(Q;-We zw8^dIY15)WgMJMel8EL$qWmK`kBK2bR=qRIkyGE-?L&K`z$LR~3}3J&H7bd5r^8O< zUMu&#@j@M=^`!OO{ha)c&~+qphGvZHQ#6olWc_|M!ey&-^%w4ZZuQ>XvQ4zvlcT^z zAAyI-HjHDJ+1DrvD+yL%)Y~Sw%M3Kpuirt<33?h0WYJXLjCbsd7c0r4hA|Hj@O|-y zx%S{S#Y5~=^HNCrg;D`T=meJS(L`QnOF0>l)n4FbL+^T$+p}<3r7)W3UG2eiB`IvF z?uKG5;WmZ7zro@kTejYZ!_#Fej9&PjycLaJmRFJlv?iY%B_+hh<$+~vNT@sUcOVfz z$TI%^xN1wl__5QajKUA|dxslAiMj(427PV?X)j@E$@_ew7AH|*o_U|X0Nsx1{&l|H zB8ADP6&2-t+nMzQE7#}~Y~Q5vq!)`Ce0zD8KwwwXEGC!2vr`bY8mrG9Og$*0a5LD) znE?H``??5uwX*UEB^au-RT&Az`m%e-=#1ejl`)$XOf8rUh~RH=;eW;V8T^wAlLu{& zchA?vu0X8R=reqAu@i!Ed1yR5lBQ z(-D1(R*w)tWVfw255~}(8G_O=ceAu9yI0l86;sIzXyyo+Z^TgH@AwYE< zG;a&aEOl+_Nt-GnR($C0p-&i4DapW2h0WB4HdT9zK7$w#$B zO*RuI?>UEGJNl|(4XN>QMYco1r9Ry|?2)H>lut~p6%UEBk_Ixql6)iT24Su?Ff>Wt zUCm`|J_D0#(`PTW(g6AqdKjHP&h|CpMicaj<0i2?ni#y3l_>JA;3+JuQEAuCgwr&u z=S-U1PGZAy%itsVzEAkwgwO!z%X{I++E9t94&+@Ttz0M773W2DHWap9Rqyb{8x~I| zG<=sOqe}1A_}W4mmf6R=?YVb$0PWmz-UF!Ic(SKipNH*p>7}M`T3|`<(g~*r%25C_0TsZ!UeYs z7~X?Yr#mfM;_fJpl*<>tXyYsh&$isRE&q5Sux9l6+=h|lxD;o72WG4-dr8@&1Scs< z*^K_fnc7!)UMJ#=1#-l!3$_+6Ypm4s-2@}hLr5<%eA}v0o@=)JDPQoX?OxX`CbV!} z`{o9jV&UkmbB-U~HqMYkPD^;m3`=oj8@}B92~5ZVjL^+|7$wdMQ36z=*bQGnJlV>u z6t~<>N|8+OJI&6~#`8lJQ0F(xA+^Fj$><@e!<PM zRI#PJgsw7-dOe}DN+fz!QeS>_^k$r=$B)jrWlhLKKAMB&t@h^gRlXeW zE)!Ml?iWBlF@)vlbZ0Hw6W?g|q08r&u;H_0p)PXoClr7bj8nZp`-|O!op;N;0Vu_t z#!uDlS`VSpY*Lo@K;GeMJg83q_u2@AlyH152D!=3yc;xf^ z<6SW}vs&y<^~~27N>PiTbFc1Xh^)z>0reZ!qUh}>#L94ey4ef7qxbJ5bLXsn4waRikx$YhI|!L~alHH$VDfs^ z1o^k#s*Bd(3kSE)ee+&YMM>I{jPBG;fpsu_MaMqQPiqW;+i?JD8~m4jb|Rx zm$sL!!}!Sg?#rj!mr5hEqaGrBbtlzDSf_6}<(>+z2>Yl*XoBO2#j%TJ&fbdKm``pT z=CJ;>!}QEtU(}e!H~P)p&F25l0(gqT_b(;|?M5g0BPsD^z4j4q^RoudU{$RYbDJ=g z@$w!vH>nZ(YC)v!p>%oMxC})gxSMC7Xi63gA(w+ew$djmshy!@>lWN)(sdb=xT-_h1Q6z?!kkwV;=WJB%mlO9!C z4xa13qIK%|GlX^IR5DS|DawJxkt(X z8FKT^l&hR^LIcH}$sD42>Z6=@Bv3wPI%gAl#|-{!s4EaBcC`5OKor!(K&j-Y7zCOY z6`i2X&)B?bq<|BN0{d?`a!+}LxE?{`tvKOO)~tsYmP9Bw$k1+_Mh5&PUSB}kuyVA? zK_56QmsCkyQy>3ZIEda$&7_!hQCvR~`QbTNwKOT!PmdcT|4)#`%sWjm?%i(8zA%TC z+HGkzTtNM<|7{M_Qvn2MmYR#R1Ak$R#|KPAoE*dG+YHFJNX}%yKF^F={LrCogL8CJ z{??(hS$_>?HsGV3D2>jTBmp0_?XnfmT(jYSnp}y(ORgTi!0l4{EQ$6F!LEjzU*tW8!zDNeJ?PJ zf@FLvm#=3Y&zaRXD%c_Wcf914l!98Gk8!YXdoGIeTH*_O8RpD+VQz^*SGX%Lq7M2YPQ7+qFCE>6nA2%cOv}NmTMTIslqX^flpQ7w+x6 z8Wk>ZMbJq43VSO9%1je%jy!)p*|6IDe3o+*ea$8pO2ifR)1k$DWp-Qc`@$WNbA*h^-zBu&uDS;5MHSK7Yt% zs5l==mvC!0%=2oqnWtUx>OqL(?d}Vr3EvZ8CZL7w3e{&!2NnVA|Up^_< zb_Yux=B}SFU?s#e>-Pmx)65Y#!VKMrHbXo6J}p8mRtuPJGY;0D4{-_^%f41M&g#WS z5MuF68{Sj-vXl!K^rb?hLZB#5d91=ye*J@ZAsdQ4zQ($%u8RR%ctwt5_nFDyhFKNa z^uqfTE(P z)(s2E_sAD*0nCpDbvz%C1U6pu9cGusH6{yb)L@BW{f+`MY<@NWsqo*f{4YxOFTR=p-B-JRLo?@03xNv1`hOT+S_+_YDPWA$Sa+zs^=I97A`unEr3n}I zsp9+KTt3lvr_S^1J+J{h-Eq}?BADyR_ctla=&tB>qvRN^1N?95$_^8h&E^M+S^G|W zt$R{*^G)L$bOZN)YKJ@=f%A|d)u5;3xR-TcP$d-!d2{E|<^j%I1}A-`-Q|!WYpOdW z8Y1f>61PieCox>y*8WfzPN9xFn>biHI5-fmxTO#52vmzz6k=nQ9fnXKGG!jFkX6$? z_|$4Vv>;25&Sq`_R`Uj0H7WUOoEuP}Km?OLYHg(gUgjL0d-Lm&7nO)@daRLlPsOKx z+IOH5c40ugH}qXqFRpj^*TnD_zg3YQ?Tef*i`WSsq1(XHNx8{mKZ0;!Mo1_P(i^5j ziL1cRnXa3dx>H`VOFSVci>gfAfP~=aC)w5VJBrv$6#-riRJ0j!VKcV>uG)QfnjZ&V zoauU4>dQY{zu&uVGJoPPj8xu0j{Lpc&E|U4WmfWnNBr(fxZ)NYKyTnFRJN2=){ppz z05-EUZ{rD3(Y+LXX(DOu@@r@J`r_+8{B+LKf}=E55zoZt?(i1Y=JV|FFctbd6ZvyCkXYcM~yM+hVxNDo;jwMJ%R}9c6d}rrNA` z0B|J&gq+;qz;N!4fKpjm73qK~ih#+9VePa1+i478=4n!Hjw6A{gWOYwE?=7TZAZNw zwm@Lcj9c+<4xZWiPxP9+YfrpLHHMXkvbu!elk9em)J1m_CoXZmsYql1#|5gqmLX5aCI6~?KPyW%Bq?vcsuLR)`;YI zCPmA+0;IFATGWEt`kM#@b_sThD8kLXCVUa6gJQ$dGE0`S%RsfBhGMEwI~rl2#WpjDwe`){)$Zsmfcop{k^?P0sja%% zRw`QU_qqRH`&zHB1U*?iqw%I3))Cs)C|<(hNp*qCd8A)Z7-tOr>#SZ;u{)V$MrU*s z1Pxw~^KiYI-jhOse*@tXLFGfxS!cQNIVhOr&(sStP7}`@p|_J7PjdQo?7s80Yu3jb z%kHmpGO}-G4=dEfp-}8Jj<7-~7p|M_!dkh$eqd`EFaZMC>^!tGV0{ z*-VNRv9s}1p%*C|sAE~$@-kwE*y5?u}!p9n%$miV)> z`|!(%g*9;>5^E8-$<{uJpdA?Q0nZz5sABJ4`1yw!L&tBm)~C!aMd={JAq_$Nx~QJ0 z*Fjb~r-Q_y5TbMXwyX_z%(dMd~ zvfqJh8Mm{oXrCWSs(}Y{a>+a&cafPMjcj>Ogq5mntz7ySuMUs5BSJHT3BTI>`=$Tk zcz?lv@;TloIm%al^w|{Iw(JKFyuJzU&x~NIKqVD!V+eDg)w%0Wz`v3X8GAi%>Yo^zt!kcmtV$ zx6E?{cLz{XO1Dhb(&fDBE|~%iG{`H$@2D6xUELn7$8+nb$&kqfdTtqs#INkZraNqQ z$G?IJt7)K_E^#&I#RVnQGK>F4zARh1xqdQ+jGVTfd+GuVA6Ka7m0!#FnHyv3!N_*8 zDuF;1`AQOJhzeddekxdh_Mh3s&!=NC8+d*eL(Wc&<_Shw*)yPPtA-p)H8w=AE_{!r zBubqW*G$8e%9gQ%*;4X*K`5G)u@D!-&yCchP<81=;?2mgURXw<%A$eZ;~R(mioYZN zDT^`X*5YazDuu8HDL&J3o**@k8xi_ZHRzj#D&36!`M z&C(#gy?o_bR-5O1Ujz3UXmM_cuIX~x6Xe3N(_$MOm<~x)JGMoi02_ueyLXKZ#MGa2 zE8r5A0hALvy~+PkiTl<|@7t61MUo>OJ1RaW)Odwj*Nd)82i=K?9h(|Xo2#%!Y3b)K zQ@ynh1Yq)C#hpheu|rZXY=z@Cjp)hxLjJ*{4aSmB(m2z`zi8oGgtak1e#>=**zdG zf{uT`F|$@Ah;mxwNtC&HAxj#yIHRpWm*YL>1;-kBKMH6T`kD-(zCt0*#76h99JI)9 z`FJ!EYdP?`DfYuP!4ijD90AodX_q&Ef&Lp;pQVfctbnFy7mCoTVvm1uwl`)EZ%9)~ zG0`iPJ3?ysT?n}GvgU{PA9Hv^%4^g($Jfk&e=JHEMabGZ;pAfTs`XNS(`J8x{O|28 zAuJsgQvWUMW|jf9)ic$Pm#mMqZbg9Fmafv398(Sw=fCxWFztZG>|=aRq&D1q2gqs=m5*H3Um0ZL%vMpa6aniQr@K=~~NtCPtYH2sAQ zxBA3hMp~$I6=|)KYsG{}Xm(|z)xK9JCat+*wGC;L67LCWZq>T zbOsj(RKasKc$1D8!4ljs%7>2&-i@5>58^_5Vfed%e{f(%HTidE&+)HFPJ2N6CU}DQ zI1X)1XV*{a<`#VKHf^HRbBiBA3QfHN<`p?-ftnkJWN!1g7R3}yvtb2C;j*% z-;k7<)6Gv>3d=3ejvAh4Csd10Kl)@~BWP(qdr~=9SyOo(4{z<@d(bZ%cRyji`-m1` zs|fBC&=VPUY?k-K6CBXkYbFR%`u)nk^Tp_4Eed|-NgP`A)qg?M&3`8=Db2LbD|FVH zI;II}cuCd>-C^rOK$j5J3lQxGx2bRmnFFaz{In>BA-9S8?>aQ16NI^IgO4zpFQGLl zYeGjlGkOy1s#!i#{Yin@V@`qpX1~^2Erxj$Q(-ehBXDBBZj$j~&|g-8=kA`#v=4s?az4C0VbNeadB{>T*;9n=&Qr*-Yv>8A_;_$!u#* z9r=h6RHB{w!y1?VkEXM1i|TvZ@SYjEOFD)W1?iR;qy?md$_>ls_fRvI7 z(!$V4Du--@Q=tl%z8x3 zB!*jVnrmLy5c3EVCF#KdK`8;x3po6U(Uv=$7&fMKbx==nw8NFE4B~5VvgFjsO_su$ z$RAw9TL2(#Q7gAM6vnk7$r+(ok@d8rc>=3|i4I2gwwaXcJ&>L)D(yGVFa44uAo#=Z z+Rl!0D^4k`2IELQ0Llf-sZlGK%fm){;G6uM`i%RySktSpq?R|LFqHe4x3ng0g4Sc!5vFmNkN;@P!@zk9S&n?`sN|?yTPm9?e9A|j$NERz5e!J zgekH60UKbuL--SIYMUg$bF|j%D~~yGCQEwu)1ab;0}$&ECYuV7u1fCHVj{Gie&J_F zD|dbJfbEa6AePgr9eB$1kC>_B;Bi3TZ{a_F;9WYw_+gLALAH-YcFF_9BFM=fXMmCP zmXHM2maviDtW8_#M}VxR=9Ssm-tjWPn&TiuAQbThF!as|g%~M57?h|!)lfTzP|-^ZG^?Vp;=}8eWgDBZ zEV=;_6e)0R?TBmPsuw~34Y$e^)B%z|GRx2 zk>P{q>rq?uyu1Ib-pc(;<9$VCi!UGXn;a@1qxC!iaN&dL-yylF@qDF}Dt|fgxV@ly zs03t}#%SySRunie+9RbcUZ<9jwbA#k2-kLtq$-GtQpWB(8SOa5qtmgsm~&G6&6cH( zItVNbkqrU3Bco+!=G~?%hTtmvfCmJzicQ7wR`^igU=($>F`%2bkz$pdj%6roe z>1oTZVgYTk+IW*9hF^O5s<&BvN|a7Cx!7tm%hU}QXgsowS9_V?wFyJS zT7u$ncEg^pHIjf*xIO}@kjItb1J5^cJRhWF6e7F9dmslHgq6^m$sw7Nk#Ld2Z9LV( z4S`1czG~c85IT9@uG#yc&6b4hcoy-ZMazbX06h}!3pu=g>(ggJ=ea~4Mi47DtIAdu zalS)?@B6q?L$EQ4b7Qf%8<+=d>Mn>FP^Z94k5a>-ju6V|RJo_`vMQ+5bTz}?x+^P} z1e1$T+<>X$L)$Z}@xPX=bA9;_puq$14bNF>=JPA!s3>kQzsoc}0z_H16YHS&I`C7# z1_X@5GBXPu(NP(xn3!@z;bfp@H||SVm8bo8t~V{hhLDd}Rrs0&fbMgn$fxUkh40c^ zZT@VeDQw4xIA{BU#_$cW*8Gq|y^mvADg!6eZ<@f-Kq6c1AXn9T3AshS7^B%H9!fQ) z?Qt=9(`AY0J< zbx9>Lfyc9YezMIdtjFo68GI8`B`21sNjf*_&YDoLWLd0cASJ#mNe<_h zy(>9_wHajOME-0Y(_g)D%l`z7jFg*IU9vIIry7~0C+fKC_>Aex`8NQZPH}-3jvbqq z?gmeRH1a(h)hp}AcjGA-A3=FH;I!g*J0g-Vy-o*1tcf*Jo}_a@GL&UG9Mv>wQ5EAH z?qHGJb2Ad?H$~UBOk#9|tB!HmpXlj9<$^ZDh*)>a1MI{1p%;ShfR3MQpY^Z>r&5kh zgWYk3+x^G3eWK;NH``u{h}WNDX_4jkfC3Huic&Ss%I!+nL^Mg5gQ9#~;Pz>VWvBI5 zPZf%|7&AOE?WzeJTc9m~9J%mM1o%$1<+k>U@~MrsltgEVmGc{!q}GpmoPVL^W?1|U zrYgGjz}&ONuy~Cs28z6oxlDiSOO88AL+T81Q4joo)(BPjMMYpf9FvRV99=I_09DwJ zqD-fok><-6)i;}Z0e~vWGvaYh15gBjgemz~&}k^wA=|angHuJsx&HW2yi@h63+WnB zr|)S?WhM6rQa%1K?CsD${H~Uj-J9~y-DSY_`C@LaNBDoNz1UH7|Im-!C1Jd^EL-)z zy&XzGB8iQRDGN$2u+qDp>#cVQ%w&Eu7hi$<3xa#LS2aX)_KDUjo>&per>yKGKjBt{ zo3>{cMw9rpW~d_OuWKe1$KH9bBSH6TXQV_6aqSL6;;sG9F6KMpM}Db}ueP{tY_8$V zr09!s>?6qGBV&R9cPWF42im2BmVSqT6qH)+;^4Ib0IC*hMEgJr6{!)=6{?cqH@8%D z+e@zJY~F7a${Ka>{01*IGjOqS3FUF1F!?qdYUC$dz$zzvBL&=(!)Z5?`cHV}();gP z>+3cP!CZy?w`z^cU-5VXGBp<8lsyO%#UCn5-?GFzYMoy$t!bGNqunQY&tH*-^H07} zi)fhTe)m`lJ?pp;(nI>J`}8iXko;IE)jkk?0*e74BEL2{1%BNq5GI8agHA^x*Mw$a zHd&-xB1ydC+q6ExN;RIOWda3!@qqigQrus5H9hX%%6QLfvf7#OwR% z`+YzGd(3|kX)!#lt=iPRXWBU*#CO2=E^oOc@QbU&6g!9X5eG*I_Hn=hqMN$zz-73rLx4Kizhp~^?JI80=Lvtr6#EHh;^1U5Eb}vKq4Tqiq%I1cy&xsy= z_O_6{M-Md`k`Ww{w z#L0MUa`4a)9P%X^<1=zVi%tXxl>76-Fu!^dYsmCB_f`*FQF2h<9@f>FVs2+wUUs zGH}xlq=Qh{&6@m`BMAiP`ul4`Nm>a{GKBRivFPqZZx=aPg{p!rGvz_CO-+~%Qbg=2 zSnXE={4A{{R=(E=cQ2AY9}7uS>{&qAq;m0s!_Z!OO`lFEnc;edG(HrsFjDwNOlQ`L zeVYp5LW)sj%CqJ@5@dMvEj@rhImWYz+D`hko{xz@>p1!&Bt~O1gR-o?qv3k~Kil(0 z0LB-JioF z6~wekw1tlgEO%e4ngBU?Fzrv9>8i5u6-~NqkLDbfRnUy_IH9W&zEE0)u|;H(DVFz} zxw_?}x$pf~<+pi_9(QVXB+E)?z9!PPu^D*+3Eir>z$`5)8gOsXtlHF$8+@T;YAw;N z?Q)K=VrM3Aj7eyqZ0nmCjY#hKG~-5v9tY3I5U*eJW|li0SK^2kRd$altE~M!-r|zA z`VT)*Wy6sPtg3}ErKecMF$^ol zj>{|c2dH*@5=k58Tq**hq6@cR^Bo8e*WqS?h*W^X1JxmTG2<%X6#_!Q{z~CLfyi(! z2v27B_tu3)&zlVW)ACBOeUG9UZf4;#=a#?wWPcd78Wb!bXj1?WL?6~w3%vFH{c*S# zJ^>DZ4g24}ST)k-dh2Bvo>ux!J0*Zn{4j;nj;_#P##&7hm5Dk9ZGVYaEfMV8^qRX> zaij`Rsrtz(994A3y4XE!x{pi53;J`4w@{DARtG_lz&41rWZPPHam&`f02VQ?R72uI znC$4r0}N2&n}BM`W_-@+27^=iFI67dyjd{N&GOWNa^%dx=E6KpCegs@yvjLIFo zD(g?$({wgN9L(KxuXS`^R@>i}9xB40qEua6K<(I@LvQ-(Hwj~kN$&3obAlRJt>Ld8 zqJtNh0O;qMJoUv-9=r6#K1U$#vIIimb2J1K>Qa9SyGdPIP*!*#&F=Yl-yK6xKKVb8dd=G|;bAiyX{y|MBH2=*DoM#Gu zER$&1t`1IASnmvL;`Md_^h&Oy2iHIt;q7ry-@kf&0wuy12TDX`u(12Tegq! zo^jX7ntUnDu6+H}5fkTjNGV3&b#z`k!Gyql!CSXrdi}mB_j-}>`>?(cg;Doon)QAW z78?^IA1UK+!W{pi5)>YqAlh==Fk`RlIz^O_M2`RlRX~@t>Yp~biL@tMi(~lxGPFVl z&CWxHoB3ih=4QaOvh6<#SCS%qgxUn8%aeWrtbbOD`2NxMA@qH&;(I7A%_Vaa9fZi(-q}TnY5hDGOUQcvkmTQ=@q(AkeEswTQr(;_R+lq%2pTfGx zh5HqFKMME6{mJ$-Jd5)QOuGk{kLcmYCUw^*RL%F6BBeRd=@}7X+lr(q0&^{mtbX|m z9nv-0R_ZglM`zZAA=iNY&cUi*7Cg|Z=ZnHbp4v?}UXB7YAlmv3RYC+iqR<9|5e+%* zHeU;z)yE1}W5>F8f>>F99$PJl@fI9igM=#@Z390tphwbWQC*yB{b{sr`D&Z4D7|7cTdkV<+m9E(GkLRV;^Y+HZR?F;J6J>SbWm?qab_S`i^oeZ z)1d+SCHyR)%x?g$X=5CVy-My6(T0m`KcATWw3pi-WB@AyJ(2A=|HB1&$yZ} z+si-y)qfd|uWB?Ff5GK?e?`69O>M=vAt7BK7zHPZE3v<;3mW9UBek+IerJc?hz2{C zHlG&ev7KKF;AI~6FI@KYrvj(PQ1E6=?;5!vY{(6;-9Z@FGkIzb+g3oDQz*i zQ#PscAt-Hg8S8rMIx2~4!=f33^OZ>-rufR3qX_PCx93a-@X4mDDc_b<5YV>bV3jz> zYR}1Ct~e4${oxj=6M}{2-yb@to23=+4sz<}{#UzXIUHO$aO@Xp#0&LWxls6T`3S+7 zXf3g(mlrrl;uUbn&FReL^;q42+`D*r-#AcQ3u1cAq;R`VqyN%Xgd_R#DeqOH^8ln9 z)LA)5_@)^|VS?Y5K!g%K9q!6sl40En)>0M(u%UXmNk}E)6hcHl9gZ*47c`M#Pa^7a z(so_pZ}sbP8WQdgM!2u>@E^$AUPk|B&BEpQ!IW}LxBtPL4;YXIzCr8xeN47A@(EyAX%9IQe>n`@$wUS`6!xxg+p_2eUiPmv>1INEbUr$Yx;zw7j z1-%Isfmy0l(39t=Qx(+((oM zF(I-rL^;GIr_hDW@no!^?qhC&!pq07;wSRt)BB4GXOUME_%z*e*(vt~JU-yt>;cAG z07X{)y1+yhNnD8{n*}!*M4*1pC?q3w9x^^^1H1vCv%N0PAYSC>6Z1Q#X8QQdZuJ(n zcXiJ~^qC>Gb>DA%Mc9G4gf=HJqHELN7mBYKIum#(w}lEPj>3XvJQ|4YCQ+rjN_W$R zkFDpH&dlJ%CkinVVPR(L4i=U?I5eF96l{e*GWVCthFxl5k@4OfP6U7rFEex<%!R~0 zy#Z!e5PSz+g3N6dMi<~(2a*GK;{s)9eQkl5u*i#aoOLR^qmz`5^`x0s>+6I7&z8MF1fKpmaPu3Pq zB>fRK#Fk(K9$@WH36Ju{Oowh#gV5&$7z;P>gyGLZauC<&E!JYgJLhtYw~oU*{{(D7 zLs`Q8d{0}jl*;{C*+uJZDQHFp2kkAX-80(f+r0Qo{I`uraz!Bq-m;*Od$l-?#c~mf zcn7|x0|5s8K~MFJqKI|LR1${I0_C@R zHw{rx#G?n8p_lmauA4yS;&cmypzl+hnfim#u3qnWzl%ah?JjBt62Nxv-*F#y^~}mg zs4ziiiFMmS42E@^`th;ElVZg%5LX89as4mki4Db6v-mZ(y!FpNu}m%*6Z9YDB!A~3 z%7H9~8@wkrt(@kH6%X`9udrCxvPGjQavo!h-7dv4MVHT)?a4sc)B6x-T1Cj&m^8b- zJ=d?BBRCrcVA~B%l5hvz3SnYZ4?GbO*2AQ-fzbdFz8NDQc`> zOc2n-ijV?L6Ns5p5fg(_5fqcmb5hfLE6JNucL@?-VY>pz&dg2=4t5iItw+5oPcuam0!Jp!eczNVXOc8 zI$^X(?$_Eny$H2l4%u7tD}s_qtWZdH&@~1Il>MgElz*R2Y~Tye5e%@Me5yM7aQa4YuYyrS{Q6^MP`8rjL zwWmi9q3{RM_FwYUwT1jlA=b$%*fs1z`lJVGpKu&;w^p_$hAE_8pI&Ym(Wk5K%d3 zA1C)Flb+VdF-;=>Y_X!h`#*@$0-WiFwzW=m&h`^HeAZzv9WV*`lsNE)tW|ypB*cX1i*`E8tSbcNIk$odG8XM+Ur|5}Qaop{T=1;G z756OZDKH&W5z-RXgR57)npeujiy6Fb_f=w+qkax9X9A2`ll?EA;sWmOiyuX$Ig*3? z%k*%lxi8C=5fe%C50dm9r;~`V3JDV$_y4Q3dSFGZG0F0vbS7J!h_5^)n6 z{f%|m@JfY9e~OXRS@Jq2MR1pBQz=5hixT}<!UiA?lq<7<##MgzmD_)9984t69KeZ+*4l++Y= z8F)S1ms3KVy*%7*&B~gOV*w^mz_1N}RWb+da(ExH{X{!GjQr`FZ6=*!tO;BXKVH6> z?Prl8fIgp`YPiY9M_OcRY2?@KQ2hPV{Rd7DWxK@uVY_q)U@|y6U?v)OAwY@qq1|FB z5xkvV$&aLVbaAcJp-@*3U1;}fY zqQtP%;!m#MzEutbNSwxn+qxgRi#~kOtsB8c6*x&fDgNbXmq~tqEcQ$CWDGA5Q?jG* zZJXE^F~$ioMB!ySTq~nsFyeXdgMA`@iSW@!LiM(uJISZ;3`y{N1B6t%J8HV^g^8rm zCBlg(r8nGf&CQS4)E>48)do2xh|P2R*S<{CUH!jiWiQTnewM(qI+#yoa7P248* z&Ijj%L^enFZB;_v-PECC5HATV27F&lp$UAe!$?9mz-C;W{mk?gx-9_q*@t}gcSEPA zCwpy}C24ue?<<80Pb%COoPm##R|WV<=9*2lG0XpY1~hPiY{8umySWdft|D1bet?xC z!wLB0KWz_=^BcFx^$;0THb6`QE=&MhI7+L=mOgSpoV-qpg=9)^=V=K63n#AkVjDqF z)R2d6D9Xpn9GpU4-`I|bA_?G&Q_n=+NeODM(*RcBY|^y{=gp~b3S1;ngT+|Mg(@2{ zLk0cY1M@jx6Wrdq$ns#Dmsny11wlyys%qY11gHhako=a|q~cJTkY6!+OIV$kn8wPE zUNNCIIsADARpnLw&yO1G{b^5*)>nzwR9?lFnUvY%m4l$a!OE7vY{!abK?=FWl}g9W z8_gj5S?AxF#|PUhrl5IBT!{EMF`;D_(vr-3oFl5-W?v3u+BN*a)*p(;BudM4J;4p) z6r{}9`pYL|BaG^nFTz8leP#Uf6#POxp1+W=ml51^_bTD7zk&afj9^p;W>`GQYWJ;VtP*&GG6RXvDc_zlAi8>fE%X zEDNBRFB}_H0a(LGp_b0rG$x8jJr}QOX#@zLy9Dq zN6?2d2pUA=`MI^saDMQa+TNT5?SFXQvq-s>a6z;dNLbPLFLRF7dwYG{%+&+lgQVoW zFbi3W=m6RtV)dt>`abd*E^w#$aK>wpbn7bvZ{aaUv0>ul2E}-vR=Mw?uYVrSa(XUM zZ2H(@4_tuttX%>Ow!f_QZu$gja-$^ovSJBtefI>9!sgHoU?$BA zx|*)?0$}(yJ5DQsJ|o6&z!9S=>xJ2^HEi~fDX?8;nE*F7_|UrJL22Xk=d7?at_YvB zU!(hTa^ATABCT^mQuS~K?Ovcrj@;e}AT7&wXJ6GYAy&7apJm^fRdYPhD?L(Ahrf=b z<4`NfgY`-8bx5rU7xT3*!or^alqkH!$Pjo`uJfH+5JfbIfv0b6-D zmdyP+Zpn5t!u3fe7?*yw9+JTFA|x_kw8`sbll8|fS`*R@)0wXn=v>G4h&fM{-5Ui1 zyaDJIn+Qmn285%;_QEyThAc`OWAhk9+bsc`nhebxx`4dksHq8MUdljJv+~E zmR|}y#zT%CW>8YtiiK=%N+de_F~)S>MuUKS1zCLb(PapB=j;G&!|`bbyn^vzn2wj2 z71Inw_fvUil1w-<)UpcinI1n(Tt6Lg067UvYIMWQuH&H42_l>@tBkKewY&;0>Z1+Y z%ZvDJaLSbzE`3DjVt+5#aJZ9B@jK#Y*uM(G1~rmwf-9MA(+k-@VTKw9?eVS`Q^Bz} zQ(;Z*K1WIDD8L6`2h7MWrxgr4j1CBw=lw)4b4xj=B4BbHGjA<-E&f4;Cr}3 zTT4cZ^h`emOkhLS3Lx0HTj^8LzkQlJxcmnTfP@zDio^+l?z50k{eg~l_tCC%_y9M3 zARSPC8a!k;A()Lj&P7ee^ybM2zA6zujK4{|DZYkn-iH{YGON<>JDDD_k@8-)Q)-0a z0vx)rrVX$YxB%yIfiDh%cHo6FLs1T6aoz%w3`3Kt1|1|0)1$zn3;>-9o3(pQ-MIR) z6*SJRkiomk_WjSqLizq;q+D0pht|H)cbbP@Yy!X<*Wn$eUBPp)-$-?STwC(ZmEW(e z4($_rnUL+st^Wvqm=2~kjtZOeu6iDI%?fOEiLIdsKHHe z(F^TM1vT9JAX?}5#g_r^e}@p;_N2s{;&wh;^CwVNPKHvTF72gQVoP?x6#HL^&SoLzlEv12){N%H&!iR!ml|OQc0~sW0FYr!uzXZr!U0JVgUH$;bp7Nn_ z=@UVbP}B<6GOiOU(y&q35FEliHA8>ak&BhP^~ynvX0R9?oK4zyVk;K#fTsux*4ofc zcC-A`Td#H-Mw{(IF+I+Sl$l)9dH6W)W|=X2Z`qkx5Sfm_UDn5yS2@wB1dqeo38hwi zR**7zZDz^2QqdOy=NX7HfKbfm=hQ3CF{YHzO}g^c7^b2UP^i&Uret4#<4;wT&)mmh zoO|IB3zB7}fp}M}sG01K^aoK@t6T!6G<>!ndQDQ5Z zH+eO`ZoIZR4LZLvz5JzLLj5HcTiGglBhmWrduY%x_bPLa7%cE?SoWk1t#}?ktn#(t zvZD%f`S3C5^(}t)w-+_o*S8%vda9Yt*$>?jvd8np@p3A(vj2^ z7nK?H;8++n@+T0ClQUTJ`{cCj{RvH)b0u*@Qgm!0_FWscln#goUGAYfB;Kfp7j^t~ zKh)vHGVKDMUvMc}k^}Tj!C7}(4N@pPG{sjqjbHU2?A>fLc4r%lW;y(zfO)WV<@%1) zi&6;BMa3yK^jYdZ!paC}R|Gd^;j+!FI10gl+XH#p?(Yr|ef>j?X_8JXi+4}wyp*{d zMK!96V>Z4!Mx}_T8QZ8adh!%lU`W17Aih1^vv%~igr6(}Vili%%OcWl)%SH`SZ1C4 zzh};J&x8;@o`BJz`hB~<%7y=T$TVxVQk;-PB)=LG?{GN>14x~UiB=BZ`19Pn_bkXI za_7CG!AGaPaQh&n5C8++`FRoVpCo~y>4;QZpb+4i z2~&_~1f`7LtcTUuonR-)p!z;?Ix1K5gJsPBE!S9t8A>F-p)gve2>INO+Bv^qgo)m* zyE1wf*N?z|WFxb=ydV2`UEgH)_45OXD>!V6t*<|(8gJ`TD|u z-mUiF*GY#R=otZu$rkv63&7l0>+hW(3qyf0?0Bs(JKVD}TKcwp-qX z7JJAcoO?&X=ZGH?bYXLKFf_p7V@Y|oLW;WU;v&VL3l%g;aePgZLwZ4(8nhvd?K1-o^+r(jZ9hBF#o>$vq2uziUYjQrvP*n+w5y!Q*ACl3guP5y z$&K~tmhIMmQ2SNG%yW?z%+8P#=85By|78XL(YsD>)KvHMNwe$mT$S70R>?Biv+&pW zC_+jK3hft_002%|IQ5)vU~zC#3+;vUSt%#Q&hhGc&8KXdISl;#pu- zx%Flp?n&n7E><@76A|_16w@;XE~HY7@Kl{%kgDgiXuv3glT*vi-gEt7nQo=X^FY$4 zL99l{*Z$4D;>BQK5{#asqP~6G$y0nlx6MTikNE*>3<#Nrav*gP9~(gcC3*t0btV+W z8GeU99dU$>-%=pwe^z4up`cGj->p2=Js^O!;u#~~2!J`N(ukL%JL*OqlMkWQ;{n6VPpNun!-i1clySGzXL@cBJO zn<0jufrEYvj#QE_Hd{^Hveu}~68J$%e*8MFyY1=_aN)-leFpKXiiwHYcp?O%odFYA z|71G$CXZ<>J9v6Uc-<@Ai>=IKc=(}=niphE-r8X9d)wln3dZ&c<5ND#d97KP$_h$4 z5pOpQq$&ILnJMFJl%)KpMo;z0WVk7#Z=Ydm%^boS%fIcr`%FB78=?640#1HyUGYTp z&4^GZeiU#)Mh4Fvg<-42ahqD35ZROz%r}2hBXsaES>)!}(ETon7Y+e>&rs~+0vs)H zI&4`y+jt>{;Im6fI$i_(U9E2^|#9O}-gs4j1K>TbJKOy)fkpuraDN>|($-D*$Zyiw0xDJ9NEK!Hd>iqFDZ`Ay#xnBVeo!H8aH&=_!W>^m)MAeS&`5yQ4 zH)Hx|)>x0v0@ijAy>HMjT@#cuWH!Y4KtEFvaJ<54XKU6X#B8&~L+;k!=L6{pFsrO? z!jqD-M3GX&*7L4gGn+le<@BQh%l{ElD*g2NA z1#soghp7Uc&rF6&;e)si0=mlu>zFCmBa(l82z}AloAm$70=#sa0^9+xt{5=Yn!*C( zCz_c9+KQ#&Z2tl37YuVY*bYW9!X*0CCelH;CM{6qUTPjKcEtL6Q#%>0SrrGI)wHPg z<854YKP>f_uIuBd-N@@1C*m6h#x>hYmruTaRuF5Ay$&U!lp_cjyDKY`eA;ZC-+{nK zin7wh0|9MTHB*abjn3*o&;z6}V(z%MF*j;xZSKw0E3B`~YWto|MYh=68|+HI($@RN zilu<|=MRoJRCerGtt;u{h@S6YP2KHNNa)_H&^YIhuvp&ErJrv61sr}cvhD910YA7&5CaDz#SuP5M9Z%Di8pF!wg_=jgC#F|L{DL0NnqttMYnxlSufwwg`a=Ub-RBr3Hn|+;_OP`-n%?c5V#u==o}9C zR%amk$aTlQ1hV$ft12Uezs?WgbKmQl7BwTVS${yW=@-e|hRbf=vgZq9WDzt7is^dB z@ID?bK{vExx5ubas^N6nx}I!(9~~!In_Z@Xka*AUe+ClB*ptA|A5Z3Gu8J1~lh@h$ zXAvw>N$npEiN;R|p8@A(k#(#okXG5vfrps*gtV6ZwJ|6lMhsH!MHWY99u`mFjKQDW z)D;2)N-ScN{q}Q9$JP>*&CmPZ9oLXm?yH+Q$fqdGeV1H687-|=u#l}a<@2QdbP?F< zqi`|}K5Q3b-vr7Dp=%h&zc=(C$5HF?S)lS8NENL>GDs9#eXL=wOCp=YOj8i}-G|xq zQOHXtC`QsP*vYl-(QWB-=Y8u>1PD*6=Rb&q_QDq-o92V_%hm_ZKz*1zPCWS?Mo#GgI_1Tgcv^eduguHNfsW(ur4{|!l zWyq)1BpRsG3vAv3cQG^$tgqtGm`+{^!T=5$1pff2=o#XI5W;c%V}=Kj^YB40>Ukl3 z{N6jyMBClM5|D#uHLDO`_GNS*0+iMfr7l6k*UI`6L_x1sP zxWCzXE*f4q&M z7B~ns;LF2gI#u$DoVtxRK|JJ62m*fpo4dI*JV0Radd;?@V>d|p zbSMF>r^~vJxSjR`hcXzQC%*zJhlp~U+jX+Y*Z%;Cj(X-8gfD$gKlunKUdSSwk_CD) z9BbeS7rPjHa^Vr}e+sykIq&$L%M&2TI)z6SK6o9P2c(59C7-I$Qh*ZZv#lNU{z4Z% z*O?x^Z{9O@2g zehrl82@C7&_g|8{9x7jOjCz^UAWZ|Iw&}jFx`pm%)ut$~=L=wug}$5mHnLjKYhBfx z(&g7i%ClAx-}fxXtUZS&F7Dj0@bl{L(jK3PiRVnLU@wq=5SE%~A&Z>KhxsltJ)#N6 zSnO2mM&6Hu=e!>6^`Y#-?JpMYC1d$aad>J`^vGx!rSy+VFW@||Gq*Tt!K!t{<*FDfPICFAS?TZ z50ykcNb&sz)A6OhiTcNr`k%&Wbo|*Y@ktTqE5W$7hlK%4JYdiC-MHkcoP1VI(Ij(Y zR*{e^^R-0*0NP8UdiIO~$aaM6;?l^}$rqT5J&J@vulyKcuW`MT=bSwtz7inCeLUza zm?jf^*t-LYB{)p3!PfU|-gFQ!0;!XCOvfExUuITiwmF&Imj&MZEmVByXMwUynxQAc z(|rQ_eCAIq&_eNvRq+P}i}9e8M%f;36)q*v=*g@IH`>TT)>+jI3H-(4{*KUn1moiX zp8aii&Czei{()QkPWjr)*y=3GA@>hscdOy^ zDx0X5}JyiYkxaJY6C`yj8F5J9|PG{HuD$YuXx`>CX` z!`+PS?M@-ao(azs0thjFbPP`=rElj!;3Mo1!M=|FjrEv?1zA=a4e|22#>WU#Ek6D7 zu!A1H5QEdhgJ1>0S}Be+mp>%jg#IJ+K5AEar~Z(Y&;xwyFdR(Q`o%-ii)+CkR_@DP zPAQ&e?~&mE+_Tn}KYrWS7X^ffW}UGWM-|_THl7%J@o_ST!13p)dB1ye2)s4HzRJ(p zEcvX^-27Ph57SKA!-qYZ268)BTQ5QuWB(n8Mdmu3joFPYqVl9zZgcO73}I@xDaW@{ z9x-ai$wf3&o?0I|_6`>!SaROAI$|F=JUsY!H1o=;5kkElsPUH-k?N{jb9^A?KOQDp z$9#W)@U-9$q>ll)yu+@`Jk%qJYzSx79#7A=u*KX@d$G_at;MCcp`>uu+?~>YRb-lh zg&8uRt3~($Bv@dew|I8Q?M%07$$5I`Yit(KM)5;~`%&k&T@IWM8vzL}c+>^M$7@lk zE5IWK*ZV%S{tg=+zA+W*PpbQyw($0f3@MfY)439t0lYtAAci866J@W)OC0p#TDZ)h zqZ!ei_dZejilu_(aEl$YMtq8|#N#pWNZdo~Lc-YenSasRTB7Ch%~qW5iNT>BW3enS zBLljms%&@ApC$-}TZqhSG43KQ5v?3Lx+5Q9Wh#2J`DJndwKtBXz(Fq@<+fGipM;rI zDM7ee>SU-pG)eC4Ei<+Ha~Xm>}NfK zzLaoiSF!nhSAHWpya=kDJju1D2&fI{T@eHDs>kO4+o!x1*`X^ghsaZ)y+NP%b+n9a zW$};R_S^mXsTGWu@)P_{Gq5a_q}l6T{OQ9Lw0({%uil&a|DW+ojefG6GXWa zqnP{iK;2k=jdkz5Py9$PXkZzWXPyB`T%OL?{`!Fn->h3!LKK*F@#GMo zs|OtxRq3B^Igcy7Z|j)hkKJeb#E&S29{~9^Me>{hpYBh1T=cFnW>%ia7i4xz7ayXa z?cJ_!4`g1IWho9q&~5TMXJDH`7si^yL_;B;>fF@ZZJI3L8wH5m=I-HJ0D6Gf_ux;A zi)z|rd2kGxoQk>&I3A2wkL+Rx>?oH0IDk%udQ35CPPByoB@t}J7h$hEegi-d@V(Jk zu%f%}zl(*5{dTDcGA?nV;t3LN|Vs7a(E*)ZOXkWdE33Rt7gybLM@I*Q*W~-YG81(hBzf6hA4VO)~l&boNXn)*jiBEj` z;eB$4(w*@yz$!aRaLb-mXVDi#y(lt(#8#7_WWcUP;oKEM3o0T9=Y)(v;GBC!*_ZzW zgDf!@KH5d>&#T;192-Np)o!{LfNW)e1n2V87o;V8E~Ms?$|d?!X=*q5=3n+Ax`SuF zmggMorswUSD7L2L4;D60h(8qnD#H}x7;^_G$9KQ(#FdKP1a+JiT{1)ISfXAP`Q!!P z48gai;pP~}@TIkElIOmwm9q+ws?@?dQ1kL*?BhuynAphOk8Qwp1cz$`nS}Fx58!)9 zc&HGZAPlGpFKJ=$V)fRC4rYR1f5GrxP_K^ZO1LOXs<^4TfLtKJQvh9=RbA+yR*_9R zYno*f{(m%`^+S{2-^S0qjqa3YpmZt-D6t_RC@J0X5d@_L0jb?ciHcw&IYLPV0g+G` zp|l_&ok~h~ZF~6s@a#{xW9NPD^SZ9r?cTo~rB|feoRXBFFal;4R;S#wR=) z28qpo;-Q^bxT|{HgA%O=>L;t>n>z<(x=3b&h)TiYv%9v{CqwFRt@?0qT9WE_kz`yj z8WDc~{@eAL@Z6@Pg~{+Q$qa1i!Uc(Z z7295!eUO;DrGK~>%8glzcSOT>PaSxtomwC~7XP;W@O0rfmG4wP6J33F1M^I`^Z0AC zblXLWm6xm`J^u_o0Q1qiYjPpsQuyTlH~0ObiK$m0gCIi7bLy@ftUh31O0G2@RhG}S zKEFBmakTM7Qn?xmc^(W9iwyM@rGGh&|LkZ@8g2L;-Pm01T&5W}I&e25(-kG-{5ZrI z^J$YoklzgsnN1qrG9J!|P;&Oj`z!R;T8`|;4g_#L5%Rl9_0TdrHt`-ytj3=u$3^o% zKG7m!d8(j44@&IIq#p_QGQCzL*?o+Zc`}$D87*S}JDq0^r{nOa_t2J;*5-8sqBI)` zGqdMqWq5PJY_xoXxK9Vh_!A@#n3N!XnVZVHQrg4C6V0yp84xZBPU=Me$blqfG|u#b z6iz%}^sn$JV~@fo2&(IPv)I#27lT-sul>*gx#=9UDmB*0s=MwsPW_W$Y!(o%?M`6z zqTD?3>JMX#FY<2q$T+q)R=2rt@wcH!#kh3yeHHmV38SV~AUl0Re8GUYd=hl(Ty?W* zw{eS##feE_*dy=9r-h@a0Txz8L5hK@kNYE7C2$ggqiS=Lym5OX9i%)}FM0hDnhOfa zM@V0RY#_0QJ6McasHLOF{Uz&A!!P>&wL79~1mvu25X>)o|j$^*W?5tH!$9D>kjQvk(Z@~@KvP_Q^tA25 zFA0B1?ViAw(~_GXOC184Ob}Eb@593zixP?bHs1T_>-St2a0`lGT0;wi>7OkN3dZ_n z#4T5FD)#H2z$?J02Ufn5%z7 z#7WM8UbI%b$JzCQq7;(H) z9?uL9`W!w@cyoYo%3Ys*mbIR*c;s|EN4{Z!8=B7v93BxvxwIj@54Hc6Ny|GFXbk>CZeCOg(1=y&qqe3eT&JjUw@;M2^fQzP$RkXt!T8NriCGsX&5;gXUvQnjRr_ylj$D8iDe(MJ+u?N+^BMoCSQW3|X}{_Z<rj z>S!ygy4aGqACLXRlnk`d+&o<{9o)tt{7%8e?b17P%tD|tM@JR^907!=nA@R){3}3b zw1;%I4Y$Oa{T>ZqO63>2{JT7<3sx%FR0&Tw;L@+c$|AwyC!F$9I&WXc1Vk2}> zeu3#Ebl}h`uYfp2e+svhk&x77kp*}Eh^6=%AFq{<_&hd!SQ4Tk5a2)_JaG~<*7X7d z$$2H9`cYuTn}gUYXjUV;>qV4xmflUQZm?3V{g0a(N&;61>%|scFOTvIVE*qJ)Pzm) z54M!>eWpi%X50A-^r9Y(<|4dh0?Z>1eEIp9DgBvSDUT+_iVQdf_GX0u$~@(^OQsF# zFU=?ZoS%(VyeXH~aW}X12N0AL)M_yjucl*xBzR=TfZZyP01o8ro%M_S$%2pmI7%-# z1^xT{5B#1jQGwq@fuEevX%E=Ra>%fc@tC2Q3W$L3gzSrk zKX1VB?%GU0ClS?1fnUlbJ!gPJm<=IS=ze6&5$-{q4(O$U?ZOAU$!gB4_mdU+1F~GA zV(A4t*B=jwm8Lm2<27B!rrzu(X#y9@vH--^%8~t{*d(EWo60K!&{H$F(Dw;XIW4+> zI-GhPnd+`6-_2V{`E*%!b4f|KjJi(E=pDFDtrDOLor{D$$nIAa76ruA=0#qf8$ZJs z`r_LN^ItBgTkA?wif#Lw;E1k!gUW)*bmA9aP=+)y7&S8A-P^ga$r`06TlO_(k-w>b z&pQn8Miu>1N`H5r+y2qVO3&**7FF-uR+6Oi;HQ^&b^Ja4l!>QFR$&1J2N6cu2v5vv zXQc$+aNH_TEL2!FZjW=mu8eT#?oo?%p7%VZdESohWcRU?%OzFsvr7@~Ws~x6%_?>& zh>7u<=Q=GWwdGF^rt4=KZK!$`}ChZP<`xbRiP7!4MT<#ymX_0wZorjviU#efs zyo8fK6hXlBlm27G9r99$8eft%DM?HRg+@6WjNBTaBrvW^3)FOm+h1a}rkYCGWJWtb ztISNhprvv7M4@!$lU;DqQGS5N#8JSr2+L1oF)SQYb>^|_Ol90-`4DkVT|{g*Ua=!f03x~r|aCwls^(9c3U%@*Ngqk|nmln0@y zmZ8T4xp824E#1>L%3A-|E(k#qJ*&N)|(9KJcwG%pb*c?`ICa^Xf=c zJwu=OhR*AKl#3V_YT_|C`T@&WAgdb6Xi_Hm@2#o8-@GCEl2LZY^<4I$$p1Zoj+e1l z=SX=J=*0$we-foi;avDzFUc}QS7nUbG?IuBajCOnW~ zhw2poBO=7{T_~$e*eGtRMBG4!2I##1&m&QYJ@CJykYd`YB1Y_9|ET5mqw*(PJT(@J z2Wns0{Kj16z?y^CzOwl46d$5YI152JogYL@^O>-v7N@U z?2AKf^VsDUIvdibqui6J6`3Q#)6%@oNu19~@3dzdZh*Y}tGK>~Vg$?mq zMU0^5Y=Yj&M|_W@!+Jfj9;P-CiZWYclbdpBq^vlV_SU0iPEd1SxUoOp^(fE3HZwVU@f9V&qZqGnAzHbW{rfo?#bo+8!l~|QXPh3@`CAEGB-60!2 zWw9e=9Hm-dkCUQo{UDZ@^4uK%+rEJq2`eIhD9gVSKzw-iQuTAzeS4RbtR@JMtmY`= z27YpPeYxzb^zGJV7HlxkXHdQQ?bPOw`D)cci9pt4#IGKbZD+{rgD;1!7aZYk#B>$i z?xuzL*7dDs^qt+%-daK65e4(7zS79>&<{_(DAM5Ssp1+2Q6Z_A0g~AnLMGeBhnR-> z$&?ROiM%9wJ;dVcjm}$gVx%w8MoY+oC%Cx@KTo|$ z$THb~GOU1^VqE=O8zr`_3QGfwYeYci94{8^ZYtabf?d}+9gWX?mHW1S+_m!>{MWnv zL4CVXFySth^jdJM+KgTLuAtLZEMN^}7FT!6X)8}e*EP&@?wv&^S>=~QEumr$EZC&Vq9Y(4I4nr$1 zIkA>`MG7`{XRPT1D~J*LQ4wSPz)6ye_5WD_ifY_=gOnJ$zvOm>95dp;#SX4Wr1j!4 zYi{1m%BgufM4eY3kxaXvd-J_Mzh6s(q;mNc>F+vu%N|v=&IVFX4lgz-*l507v1bq6O$}qB)^4f|QD&lMj9k zK5>FPz!9n+!khC)Y53RuZ#0TgRhs#uWTXq_-mmSv8l<9U+$Ws=-go155-a=D|=YdL8k3!kRK)2prx@>l)&<>M!gRUXxl!TTUs({Sc}!$MS#gW;^b7M zO2+ch4NV~UL`rq&f}9Wk@8o1-`Ph)l3*C6<~>j&G4=i8Al=sYAAf!9=cidz7}TRi zQxp~pgz@|n6kb0Rx+trP>Tf3c(3VqgAW7MnR0TDSe;5trrAyywCV!EF4ty)R%%G!h zaM*a?lbK=;;;X9p=^dxWyJYP_+_ZUz)a4UH|KBkk*_}z{d0|y`upYug%ek&oV>b;B zW`jM>;Z;rEkvC~Oxvs>vY7(g?-mT3C(w*vKh_@g8gC9}YHAQ;6o+zJ|4h!3jq3BH@ z7P`#MN{yQ%{)F=NCvYWtdiout{O6NRcZ7=e*q*^dpHw)_?)Pcz^mA3d*xsKTQb5_* zq3A_~YrN_i{rjF1WM&&LLEeN$xj&2%`E z0dIji7g;jpTLP7%Sm2gQC}@Phw%xiy{`!vmi~etmlI*n}#$F*Xjs(^r1!n}l%2&_5 zRdG1Q+EtSD4x|zC6>p&HK$YI&vxZ`DvpnR-L&1nSP`0 ztE0MySmI|NNZZoasg7*aQnTp_LRHszkDxnJaD>>7=}&v=?1~%pN*{XFKmkHX()rO3 z{3`z)AWY?K>TeZfg{(1R22Hv%trDvUFv`gQ>paVe;59u7s79d1ZnM6QL7;o73A{FUUuT{o%||Rt7}JO%s-?3ueI)15vooM zkKlB7{&Tfy6>Xv`R`G6wo*fiCX#8Kq4!#ULSj2x z`ARDVc}KgSl{Q~Lll8mY9ex25GRa@|)hG74Iqai^`#tDHo+|qryr=M#WEpH_$-Zqh zb(rH*ypJ2SyPf-BR+&C_l|gyRlSpOuAG6mFxt%?MfSw^%-a^{bGm*G#&&z6zVY+ zlAS4%VJPML^fztLq~5aCkaJ2R?6KWQ_m@s$&!)NC$1LxaWB=UA=r%~uS|xIhL>c+q zq^{}Z-kVP+>b}HruI*#)8WT|tmjMM7Zu?3Ec{$~JHBR#Qnl+?odGh6=46Jc>`UA2T zB))iG)4^B*I3GvpAeut$*C7CzywE{jCs0ZFIz-pUabkJr{yVhg5;Pu1b@dTNbFUn^ zU6zp7xOC4q{bi8}l?Nr6-&Sz=7wNH>mtEL2mTtKJ($9w7Dhq4V!l~yUPYms*eRy&Oj#=38I;5p***Y;;@h;BG}!7v>%PI`pQUJJNy8<(v7IoS z`O@N>v*yJcYX%y;7%Abc%bw?2^3G55k&Kl|HnU^bUN}20UK0jM>tS!$@W#j>6?hJH zSD{Jzq2x3@_BRkYl4prlJUTCQBhsvx+Zd+bq4IY;`~B%%ALf;iLz}A|MD@ozLb=}z zj}_KWC6V!l^7Bu5l2j$fc|W|oFv+a zz`@I#x4{8r0nX;#Us7N}>63gMhc{8e79UKG+g{fu**ZB6JvsiO*eu#hZcJugBGUca z`ZtgJw~IuBHk7~V=wL5cWW*u?@;st|$U*`@Sk+kQN3E8D<$&=n5XDNhA&@9u|GAY zOpjtoym~;J6|O-bPIE0?vE`Q>LX0fP_Yn(oS72)n?1D`lf|=$YhJ`>S{MZ|*-Sw?5VpilQgeO6d2asOBR*Yr4{qSl zr(IFU_F84Km@LoW+C4}ghwTav@l;{XUtvk(*?&LouMy7wL0pC5ugbM$v+@xruPOMJ^?{~WL>Wr2fFgf&UWc=>dz@P? z527wo`}+JqFd2G|igM-hJJ~Zrp>wGL8}j2)if$G%Tbb03+pAg;KDXy)FU$wb()|nlu6~_kFH+2do zB`voz$zUlg!TY^`1~N7gRB}dds9Vr*#x>ON#j&96Ty+nAcyH-S0pl=?$*O!XR0%xT zqz$c^Ly>z4_i*J@i~Z)X{!@_&os+4bb}`#l8mc~+jOCe|#`8zd&zwxIQ1Y=mNB#~Y zwJu_|iG$l`-*>N^pI-<#yc6APZa5W|B-jun@Oql^fct_|R_0Ev^9L)+A56*I(5%af zh9Yo5;)USLjBa3%LR3bY0GNYp^}fKfrT0>V0NjD;avM$y#AeW&us^=n#|BFWQvOBq zP~)%XQ#kwyx|ADszVoa8u;%RN?dSAjvSyVTZ-+7acd4!zsLy_+^Jx0h$i9*&IYIzx z0?LM8l&2d$o~*@D0c#>T$ryONtY?;IS`1N!s~z(F_Wti zC+iFha3D}(65oEP`C8X9h9pPGIxh>v%9`Rv$?@N&KWrDOp3tcRQ*sn#^+e^AqYD)w z2GnJ^{SP0w0=ggSO!a~RcL3F|X|fkwwVB?OJ*9<*2?S=U#W!!}$r`_q_8r&XrV%Ib zyW=)BPVLU*$wbn}z5S|sDg@k%Zu>xDXvZuA`SKovB@^PiOaUidS8`PD_`fFUJ@4Km zBs8uY11_^W3A^Csi4Qm#fBOqF10cu)?n10?#_8anh<7KR^bh4+Kb2c_9Fv8aAV82rO%_cqy)i=D4a_nH#bKdp+_p-E%VkNQB3S&5G$QC}$ zku)f1*qr^xf(^4w-Z=6L?iUzKQx75OE8G~5V}^f29%sPq$wVd~nc`h!GL|Vsvk^kN zU`geyCNv^=D zR`^4Ao9O_fd*2(X8&FpfCZI9uclaohrHi%gGl&xCC686e)3zz_8M30iK$RVR{qIsR zOsjb3LA)4gsaHK|Sz9L&B)VptG6hGcI;B~R0Bc1|n-yD~HTZnOF-;M?z_A{5QX&689EU`f1WqJBxnDju(v)?XP^9$VC z$U4st^972_M}yE+!~q1(-G*fkhVFT&kegp{E@RGtAZp+tcohI^B$^84O!_S|mZQa} zT8;RR4lgj4ll_LtP`gUL3=6eHgH3kLnQq@5ua5Oy^CQG}7)Rzi4s z=0w}L*73`PN>$n)xrdG7iU?=y_ofURwIf`o!>RBZ{eh{>NAgFVZ|hBk-*eEDo=>yQ z_f3MKsR%Z|DkHE+*&^Avd>Mm@`%z~YfY(|m`P`Cf!|Zp-N8B_fXqJrQXG%6()@g(2 zKO>sl5Ft?lji5ZHdCHC%6rZis08;ap(85QP=Zep}ni^D7RcE5znc*)HKNq%BO=!j6 zo5QY6J=e}L|Ju9}hfYMi_lq(kz z=;-6fD3?D!xAD)Uxc}uvm&k~CZo{HgJ!Z8CCO zPNRJvUY%4XH{bJ(!)1i!Wb|}=(pG*OI|-yYEvGRd{P5=ULj2Y~e;mghdcqxua5-5V zER&j1=ub#_OW=wuQ%vfc>?Lz@1#H@=zc3fXE4=NZ#LuI!5gYfI;?kqUIw_=|oGVMd zt7i(`>7W8~ZSO)UDP*Hqxa8$V*WW)0tGmyPPwVM5G9$AlVkfAC2C$?N%!=&FtNRnx>v%{dqetxCsM%2e z?^%4v74roxb>0bgqZhH}lDBGbI{$fb=IjY4%%ApI2-DF82FeK=u+`Qt=MO|qCSy8T zw=g`cEzF>g=j$2Dhbq8zZrU73WFRi}Id)^uMU&mvZ9_&a%Qs?@k9b;dXnJxqCGw-0 zeEiaujef~I-z1Z5{%xncRY*$4TGjOxF|HR?-u*8tpnhBZii=xNfpsx}XCuGXEGY++A-F80QT8F{04pS|#G3;xUwMtI ztCDKtXBI6eu3B<(92RWb0~V_>&_n{sC8L4LBPldhml2*3#s9ZB_=zmZHnDI(aueT@ z;x~0ZJ6RY({e{?dz{U(TcNk2`We3fXd71@>SyvlgdWdR`Myfc(?osNvW0-y@Jf1NJ zl<+0R73vrnaC!8x3VwL-p(1-C62!0EC(TM?TfURQun+X0tGN$*7){h!LOe$vTQ(T{W_j zIn0!K<2M$4bIs@BLKm$HrpH_UvHf_FSLg#e-`A4rLKPSeWXeuPVmiwF&WgbS!fm-= z6IZfrW#d6AW|hY5ezGA`QxV$0%h?W$g{EYg-X+uPwCn>e$5Sn=qFC^k6&s^5?`tjX z&gYbFSWy`ruokf+1k*Jtxq8BM?$j&71rgL(dC1`^UJhyUUo(0e_Ogp&Yv3oXN1bey zVN|1~vcGq_gm~f*ktc(9Kr)5A{q?cP6gWp4{pM2S=V3Lk2HaHUt`DX)oZs6VLf>iD z0k?)yJ8>;GQ0?jAOdo9eTGPRlJp@5V5ZQ@KiJ*C^K?xSGpYN%lDI27C*~Ds%rcnIU zxa?$(iw5%C1Oza5t3EjsA~ilUK;2HS(7Qi)Rv02o=7~E-_xYN-6=kbg5GZ#p!z{tz zDFi>GWtKgu+c;K*b-y6Xgy3`JW>$-{xq=VyoP;wmgSZjJ^T1h~OG=Fh|1}6z3_$Tn zLu%GLy?nzoh<|Oo_}8w+cORuPn_32q=Ls}dKONdcrIo3B-|o#29cK7Kb{HJ^_8lDR zbO5lm3qucCu_hdLH_xxL6oOWN&W&y<6WWH{_s*(Q;fDjI>%X}b?7lZ}bR@@NZ1!8_bv(8w!wU)1O#&&`sxUu=3-&JNpxp&<8ioFTz8oM(l5z-#CJ;!neT+lBsa&_E`C2KWmrvWa9es4IT*)z zep$lM|JUMRh*FqolDN9mJ}g$!ix_J#OO4{`#Z?>%au?F>ea`(g zMGv}UkQ<@LHg#{OPT$fE=IAp*r+$l#?+q*B{dm16=KO|bDGZr9Nu-d%z>8z|*>wUJf9*5wiAGxK9 zex+cdrQ$F=2%?#O5SD&F$%bA$ia}iN-Ebj9kMi>uS_OHP^2*Y1;UapVW>$Sj-3o^Q z2E?gtS?8&yje{c#VW1r9(YoJ!aqQqeh(iJy!eE?M6vk5!a6J|fpjKyR1rAtGXOM$T z5x}#h=a`SIKc~4Oa0aigafC_REWC~f6DED?g}AwD@>f!@BK-3%(cyA3FEMsYjuX1e zFl2v`AbaD*-ukD(?_QsIN*$k3w9$dTyWPSPm$reBLUpQvqunt>^X+K9BEMNJS?O%x9Yr$pIe>1q?@uMVL4(8^KdEC_^+S$*9cT<>@yXsSn zbG2Da!Re8rU&yxMe+0iGdVkY$@xcBR4B#wCt1!-*5B}#yqz7OZgbeZ4XO?CkOwnFI z^ZOu^(`3>6G-R>bgRpzz&rH%p{ve~7kEOqp^-uI|lFhcSt~m+sh@Ir+N%%@546G@? z;Uv&ARA&*+|IM)TLh<|h%ucB%asOcj**8~NOx@M@88W|y5LV!k*_ULieq{U8$<($O zC`}J=lpKG9(Ia+`!V$%kC(+(Ta$k?+kPHfGNRQw2W*pRxdM6lrl>SF@H4Q9781bpr zN^E#^L@rJ9zh(&{*lFmQ32_N9@9?z4x{zrK8qO(;0iAT>Kk{e@H>$MySvfhus9?w5Qi}7~v(G`K8DNtk^!E?WS&Ut4 zpyVPu8SuBhk>YbkX#c_Wqhw(o<7&rxR*C>c#7cvNB__`CWA@bM@b9y<)RK6rMSi?l zXSrNQLp!eE(i?I9HMDR=lJUlBygrWh1S2t7S13Ac^v$oCN&6vpGp?3=ttP_m0jLkZ z9PRpy`x%+8gcSQ1unQdb#6lhC@Oz9xu`=WzkzolzBJQ_A!=^z8x--{p2!sgsDoY&jh52;x_x>dfA!y32WXOOX#C;fV1#rxmDWuR#&B(TMat5DUi8{welE9u93? zO?r(h`>2bH$!=__DEEk~h}p$iu-EC1Ln`!GRk_ntGEx*k=7XMt!b3R&w|Y8cus6kl z)%s}%D{-oae6Xj-40IG;NZ)idc`Kz@NhvGPd*Sv@s}j}}^g_;g!AaC3>;o#&ftxo~ zEMwody>Bi4{YcSIGq?W-_QAr8SC+l2tApyJj48^%`2Q|o%vX)eh^hnl7ay*~x8ZJP ztoaD&>GNGZv%PtU=bk^?+f<=4qG{$zvhVJ0igB*zZ&@>;D!M94c6zN)VE8Yp1`u# zP?VQ#O%0I>fuN>6$SLcG8oD%xSxj)-NU zzU`BGE@fsFlIUfu`lYw-f9>^*exWX(e>FyYbK{}$?Xxx^<@-dkx+eZG^uPCEj5`@+ zshv-Qr^kDO0}Xd$LrgqQlICz835vf-92WrtePXC<`%U)UGX`X7Mr~^6g6n!)$SD!3 z@(0{n4mYSa1OuS(rbh0kVd2IavU2Nk@kuj1GrqHS8wY|_06*;8&d8M|VNXtCCQSX6H?ofq{*^NHpGv;aWC1vo}qHWDg z^Hj$sNo3MJ4TmT6hO3HD;S1#{w)#1)q=4I_(bvQi@R&8!~TWanCCZwszSX zI_)G>TF6cPClVqjQD-mbDAH|&fD-O$PBeT@4@q6>5_)5*juF(sBH84xWaSCM8ux{$ zhD!?Q>h#oI{!!?S1|1v&9yhen`BguUf9&(iGi}J?AdkP13d;_M4{ENUrG%le2y_BX6bc34$!kAw( zd8G5l(#R^Y#MN@I6*&bY#NjYbk|*ha2_F4GVndP@PJR_f-l-icnLmB$4*etRMn6Up zwh0oXAApdE5V$($v;egq-9Unh)%h(xJ&G?xyD_EE9US@+nk!d}3abdKo~8L3pM9I} zkj&*XSeFBp%vJYJ0Ceqs9dg3v#n z5}F6n6qO(0+Gz0;abaSX2*CccOJ43+8)`c}tOR?&@VMlou`ymXIQ^7nv@W@YA<#Or z%tK`yy@FhHic})gGTHayO8?j)ZyUk2J4F=M*h9juWKVN=Xqjc>x-oZx0DgIttrAyv z_+^jm+h!^@zowW!BN<;If6 zZooyVgbbCKL+IYZCgDAp9hv5BFaxwIAQxbM4PcH&m`64`_C$1dqN? z`H_Scbr-iV&rZhU?C$J~&^ivV{GcGD8^WpezLsP0jvamJ>N`u#&Io}M%#JwVqtLrS zaFs4wVj>19Suuc3vLFwmwhpy3sjTr6nM5)|Li;g*y@-O#F6|<NrQ7#Q)DD*U%5D?BmtFM;&=BY%=p zSi_mM+zbE@qv#kUxZ^RY3FLxyyIWdaPs@?<{kn5m=Kom@vJ|@5ANvQWNq_&@3vM$w zpG1@#Jh?GjA6=MLef!?yq~aolz6(&OrY!7~0O|D{+8^X+)uk`9Y0!%pEZvdY?y)A2GE7Ida0&GsS>B$gexJVjVU+>b3cL?M$K&c~dskzIm6 z>U-^ev|hZ_8>(69{n{T}Hur>j=Iis^O22;%Rxj(E>`1XwRDqicY&`oSXK;p_zg}Q4 zbrQ=wRu=BKJHWYaVtfx{xhP?zikpJ)($QPKIzUUA`Jirmk%8bs}4Gnj|EZqSJ5ZF}t!RrzH>y0nwi z)JM5&s$uUks)FO_ONMO;d0ps-bk>hVP|KMyJQ-%pv`n;6k$isu5Q~!U-|sQNkk@v6bfs zCG;d^Zq!V3;zxy0FhBOY%Yz(DO8xamBAx)_Af0bjHNCdq;KzoUNYjYoWXKwk-`Fqc zboT~$XxJZ3cplDOc{ZT)yWj&HCjd{Zp#o*_&H5zm1VOpVh}9Rm{zw?jO}sgyUu~QF2qp*-UK*z755nEQOTUy8z5U>s7vUEui*Ph=+D3mF zIk|Fkf6XIU@Vjo>T*HmyFu{zgOteFci?dBW$jFKr_^m!dk(~cYgv?Uub_Prde0hq% zHiI&1l`x;uMuvm=ALSs5a#*d%J^2#?ufJ{e_%cW(2h-g-{q)us6KujHxBC_~5L#&K!KNY3SPD@{wh+@S}J&$tob|rsarnc<_1N309>9fc)nKN~%PlF_i zGmZ%A?++-eKaHm~_Gs{EsJ&Hw{ZyJj^MtwSJWfgAc?<|F64CXg?`NVWq7FK~Ygp`1 z1?{i*kS^O^pQ3%C1Px{#??-c!XpT*Bwfeh(S*MYCOMMC7g}mV@pQ^O`OrJyh+13Sl zVe><|vw(00K|ghC0D!Wx8j7r$)zw40ehPqeBz5`QIa3$?7)ju@* zJGqTuEDGTpntvC88g^XTgZ|x%OP5 zPN;|Fd`4n2^QD6z97Mdp^y1BP-JesO!{_p_cT8fqzuuqbb@Ti-L!mO)pcg~KDa1gC zpT#RSW`K z#*9Kphh`G>D+rIpL6Mw^o59zjBV5G&`!q6yr+p7bYcYI`sc12m`)!Vm)zE6lPnNU3 zfBnSS$^UqdS~Up9s`clYl`@D(%^t=s`WA6_()PuVKcF}zx^jyMVOP2#_j@MToY z=v9i__YTZ3@r~kvCxEdq&7)o^K1q>Nx>e}K>63Z|87wZ}%!vRK7W=uD>y{vbcb?S?f)^LF}e!DX<_4-UiM!UYncW zXK`k7zKhJ%gPhXs2fFH~sW)BzlH%C(ukutIu|oXANcs_4WJ|2E2ZN%7;bZDD79~i( zXY=`HalSO`4l*2MKr9l{!mZc1zj18mQM6WI!OTz=B{VVR&RXQ=qy* z)oVYBws172-h~MO@bl;K)GhfdCH#F2rO<{4TmtdMX@b*CITktYbF^(I&$*Vxvx>-b z^gHrJ^IA;!tgSqHdBOA>e9DhLsvcjseD`ADmPD6>&b(CL$?SUP;Wpbd@W})1a_YtoGY_(9Wz^hlrp5-a_J4EyTniHgvvK z+$lj!uNWMI7Xr*Sa5^PE7-$_*TY@Uc&l=`Izy{NYdp#o)A1YY{d=$&{DXdGgA88^@ zoUDc>+=6MUnp~L&o*qRD@O4DJEfJGuV9vj!XLN>Hhjhtzys_af)$wTe?)i;|Y>4L` zSdqJSD}T-&wammtFYhlu+7#?O9(i4zW~RnZx0uQ1`A%u&Fq!UKu2BpMnr^^PexSMd zx`=%+o6ls29wKn0rM?z0+rq1(3m86cZqscqkrVEtKWt;hKdyN5B$G7okU-c=x8y_2 zGtwIa$e9fun%i%jafk+_lw8A=AEW4|IW%S*pnBqLuRXR(cmz^Gdeq5PQ*&Aa?jOGA@xcjytuP1 zq^7asEIp7urQ`|-xtLctYZQE1|1FSwg%w@}KLjgDs?T@@JN7Op!^-x8t}}~o-6u1T zUqU{%NalRHA8Y|D8ElUzkaZ1e`z_HNw`N>b}9mnNDoY*E}w zz+WC4gu%e>>CbJ5g%`&E;><;K%_rZ}(x!W!j&t(=wMs;oFF5HkUGhGI1T)|Wy7+~> za+D=>s**ZGcrHNxdGhNyfF+LR9xme)bcb(=0Uu_#@2TP|d-|2B&F|-42*o{QKZAFH zw7@b&0Q$^(1#CVT?$0mQ_ zzBq7_dz4*prsjTE7x|e1oF(vygsV9!udQ=Z@(_Kr(E9XxLjVPBIY7_={nsn3Xi+Gq zV&hG*jYT$#iO4a|&K6NUW=9J@A~(A^GjzQMjp-^pnY1mRf~Z2@Ko2icFlRxKyLv>J zMzEHPmVbd9m)9n8UC{RgV=~|4jy&N)_O72;a5eKbwOl}h% zu>CR9UD9OyGO-t{kaccG1HMS1CMe<>3|P~Snh!M_sy~AS`Fc_9=-sJZl26rlW*`b^ zA?r)%a7%vYM>TcdKF^$9=hnGUif3@QW+#x^K{Q#wQ|+`sD~hF&?atJ*!yca!8u%qQ zyfl9g8pQaXkugA&@7Z-fj+$SQQFTAF)}wJhn`>*%f+e2$qhRh7|y1eL=J5%9Jlk4*f2q4%ZrMuwXl6jS2 zFtjn+RFyh_^E!@}$2&*Hx`LhI(=JJZ8RP%abe{24{{J8UTxZ)OJL3pZ2xTRlBMM2B zWR|0pnVCJ#Wv@h((Xh&@tZ!E4K^dtiitHpK>tv7PoWI|V|NZN6-MSvvXS`p}=POt4 z_5d@Am~~-qWnN?Q?LYRb2=K*S9skx%ecf4d*YrY92nkZet6xVaC7OB!in6z!q@NWKYKk? zknXu1zbD61wK(AZiu~}cUVv3JLeZ=+0#02ks z!r4shV*>L&p(US{(-QW0rvu)iAH9xDBl~=raa8{CZLq}y- z60!f|^~35iEvFbn36qPO*zNE;x0)ky4-0T4?Fx!1E-ZZrvxTKi^`T5?{-{Vazi4C-&J2 zB8E05MG51fm@cnO6hfYV_J(zZhYQu2S6PHZ#c)D<#~^XuBfZvdENO})ypSS?D0gto z>4)(b1mc40D1X2ipGk2p;*>B##X5xq+tuwmvJ-Vd&D*ou3-(J(fbyF5@EN)8?(W9$9tV7d{A|t=M2xB1rDh3A z;j`&C){cUJ?g6tqOUrYC&IxS^D3jp^CL`}h*RiFZQfizdZe)DNZ^-@o>DVjgPUB*3 zv_^oK6VuBavS2M{It0V=sT(!-=iHD8%c=u=h3*5f^Ezr&q~8>ELtA90FLgpm4N-hA z#GeWRE7{o+T`jZZuU~-`0_>%L-~JATmD1BWvnh7s=vsgihRD#HYPsw*G$!SBkfd

rBeAOeU8Pju{{gJ*;Wj-4$EeX;&5q$;RNBQ0`-f~iEA%T+!?b2@aq^v&6XleF| zan;8+5Pg2YKRS@yI9+!&5rsq82B!mEsPc+wYID@$eN{aB&Aa;pViSiI<9GL6g`cp; zP7s$m8ix570>TwF9lHRY`D_@*Vk2MdJ-Ec+f6j2%U>u|o0%oAu=j^}3@swYm?s#M$ zs+V^@))bb&b2ox5GWL z`jw0gS3(@1Hx|pP_ja@jQR+s^vkfo-bNYySwJL<=H&$Y2fUJbcyL@B$@=f#oyGlrA zO5?#xdsjo^z2Vad|A{LU|6Yz@7xepD@Ec78cg7wESUaC4E~sd}vsjNT)t+5mNkhCi#fQ)gCcj>0gDM!YITej2@5w#YE%$ifw}948 z0?#@k&58QYE^6n9#eGog9ZCv!yE^xrMi4+wrapbglY$8-EIluv`KanF;lKT=r{X2G zS5bww?n(w43-xK+rfuIzS+C{R;w!SX@msLhnXml)?+Z8#IUhn#3btt~|82Q!k#e`& zZECZ4uI?NEo8b3th*~C}I<-N*=Ke`+>3s#qu%|85*bV^^k)*efrx6fG_6-6OkD@VX z*47FQf7cd@1-YgTWG7~)45)Ub>G^hflO@T9uJ9t69lFVwX3to=rG-oRPE(i64Q8$i zQ-0ne+&6J+Eo}bloH)XCsjl;B&Y_tt`nO~NXLG@|&vT90^Sc^eL`K2^q~!?Pm4p1Atunv>izb|zd4HK`e{&IM0=7x<-7UsaZFOMiYK z`U*?of@e|1&RWLmDBNl6OJ+D?u}Rzzt#;-dyt|dZ&2bWt(4Jpk>fDPJ1zp#2 zPs`odTF|-)qpyaoy>-A-1mHmgKn_V<-rkrWz>MX}z`H!6tX0LxPssV4A0X&ii!|1) za^?_6pG&6a*_nN1-}w-4YC(z6=!I`a9}h9ivW!NOKeX`mv^?LYw>aTQO0ZOt*_`y{ z8|RP14<4Cg?oXz-h_O4o`0IO-OJqewzDtZyjovnc;E84y@Zbql(R#jDzsliL;8sqU z!|1=)tfT~JG*f^4(e+1O7nMHGzc&fF!+azC!X9Hv>xIJKW3!(+F_nU43R7V;;mVn~ zF7Pxp{P0WLt9xm(;649;-$fVQ~x=(j(0X-W7nr+?t zW-h>WKjukqW^MY7iSrQ;?I?eAHge>9*tFwjUI=G4CO!tB{}u9pK64%2?)w83ea@t$ zeir`yv#aaZzuLk_!S#%uY4U;zLXwAiS^lTw5a@9XESe|)}8yyjSwfU>h&SWrBIoC3Hx#TG$n@(7M%UCzDjn&ru!ld|P zFY&UIW8Wz>PB`l~^meAMw(9FGp!_YL3q6+Rsvzhz#HPsZ6gpTc-7UyOnqZUBV^S3| zc*Q7p!QJk(a+N|<9C%-dF%9L025*)3qE(>3f0rn7uJ(DkR zT_`t|VRaVVhKp|A<)qXX-{68C?M(4j0}PP!x|g&r!+OV1^0r9C8+*lQ91=$fr#Ph6 z3QN>E^H!F8CO<;MuKQ!l1YcTdB(AiIacs6OCbr@>^itY&+Wkp0;g%s%GjeWv* z@Ly4H>{?RtB$DEh!erU^S|^=RvOZxgf3WZWg~~;)oI4PzSj2u7Cg{m2&I*(4_Qlc< zq;*?{`d-UKAcmy(#^7s>wWEjzdbZp4zWp z`eb!P?a{u28%r6S zdqe$%UlpYIK{w)#!^KCFv(3DU`j9wqnywO3OLjq+`daXQ;_XA{R?u61u;H@6wy(!I z@p>W_8?&{sTPsI}xx>M781T2nls;?4peHxFt{5NwId!zk z^&N)!CEufgOi5JZd-HtXK0m30qN9nwUjjcr6Xd9M4r0WoS22yanOpxpojk!pwulaC z(y{w19e%=(?!h~5_l*`A65}1REF+D1$lzC7>Io=*2sm(Ey)c!J!ntta26RAr=@I9^ zy6#_|%TQIRSLCgKHme(=6E;Yy^|!VccVhs9Ve5lbY{GtM;;Y>&Yh3}uA zd=~t?#%vf=!J?QU#g8Ihw|8ZcJzTddsaX+J>Q z=*(2Jw}umbBRH|STg`ziwpb$Uf|c7YAFJ*f$BuVVYmh?mJY>~I;i2JPsAaXI=tN8a zTTwrvj_EsMfpBk&p!%oqT}#7 z#Dfw-aGV?BW}-54!Si;{%^1PPpaOhu)sF|t`wf+hOO@a=`5hDCwG%+E)kj zt^ERDf!~L$)b-cg_g+(Pyz(5@?=brw_A=h2-6X=4bgpYf1=+o#1~R5&u90*8h8l5? zIHJg%FmFoK$#~MwRCLSeA+P(XBpdt6jEnudd!=@Mtr}Oj_O#E@W95G64-xa^-s0eC z#M+i&=aDi#+C?S=L9@=A{O3YTBrwG&(*9b5Nx(YN?!BXT4cux#Y;(*&b9$ieU;0)Z zB3CbY^aEn%3ol^T^6@N9oag9A4&czVx}&1t9+=5h&+47ob`hF~TqIFdKcwPJvaf9d z>cVr}gnf()9uqgZ$K1Q^_(ry68sa-HNcqb6_mnrP$?5>%*-J0D`{xp?;nk?EerIiD zg#XuHlQ||ex_9VzzjIHXd&WR;z!c89NCMV7(tf^?_ZV0L${11)gQ=BQx$k!I;q=DiQ0L%*p+$9`!AJ zwdoMGA{-584cb(3)Q&KOKY-99pd|5@Az0d}Am8|g03k5LQd7=ifvmxcbTfpXp+S+T z3K)OC5-OPG_yYGSr^9dgpU{-_N2!utOq|)k3GpBCx2RYF9icu3Qg>1`@Y1=zk~ijz z<*?Lm9~oN)aEar8hYba(8ac`vY9bGfDId!4WumN0^t~A3-(8uSsjeGFow1_ib883doI_ipVfh88-Vt4h8|CCGM?`~*C`;8<-eN_t)F9IYKYj+?^TBs;> z?`GdN-8>b3I0d2^J|r6DL+tI6$$1Hwwh`fLzzM0R-}ae37YNWFvJmg(IahD1i!_Ml zW~&wb&_I6-pG`%6TCfpIHg3PtcDhkhWAYL+^7|i#WC26+HzPjF$J;&Pa=EVXQ6xu} zc@{dd>^lFgRK+f)an5InM~OAdEV~OV^qTZtu^5Egn!T08o`Uo$SuM9o0mIbWdH2Bd zhi=ghwGhQdB@&udnC8ZGKzY!Itvh)t***)MbjJtT2Ne94Hi)q5Bi-$+4;9x^cOKbmXg7fo9JqRr*|ruN@?fSu(|W zmw*7ybHJj7|3`S{g&BV#qggg5J@j*Z*OA3mVz1 zma@V@tdCx1Op9jzg4i`@nSty6)wX4LqY$In_ffqZzYvy}@s!x@Un9V>`-+VQ*CewW zAOQc87fNKGmVbU=h!s9XNl=8(WHGKEuRx;CU(V6a37h>qVdeJ3M6)?K)oCnf*oQHH zci*?_dej{0Z=K4M41v&u`UG!bY_&&73!ZBjqwh2!JI`6`dw(>Jp z`Hf7L51l?g{a+fVVJjVZ%x6MwerKnI51&{L{=4{gJKu#;5uCyf+XC8Wq%))VM?-Ha zh9tUmm=|3&{}4RUy?qxnuzgbB;|Y(d}N++bycuH;=)` zc?Dj=irc;Rt)mF49cw08ktks;@MV-cw?b|+p+QJD`&_74VH=0R(IZA&!{~_OqOLDjA&?(6hI8A z{g@a$PODboABWDhJMmxezf-quXX>Kl4lXXKnRsruKwo-)S;{plL8O*pf~{}jzBl=M z^@Hu&&k{$7Ufi)e_6zx7fSok7oPE2pVwAA|RcoxR@h8wLdyAB^qlr>-SGuk$kkUrLz%_s>0vE%ZpHBntx>yqGCg=smx){;uuj>gbK^vF|lUxzaCs| zR|Z@=b@dzJPZ@01klGxoO7=5blkJkm=CZ)>UgovnFC6L|fI)bA$r^{naMt=j6k9c3TJ&M0@Z^V#YNG_Ck6`E~AO#`b28)imb`Y*zRoITZsO zPlh@5nPB;2DO0J*>I^^DKQ8pZ%|c$vo*S4c9|xWsKojoNcRFQMN4qnwpt;fV0&z8^ z>Hb=a)5f662avfH_s*1@Bdu$_(XuWDKkU=56&z--X{<>A1eYDC9c`(s)Ob5L&p2JySG>c4Ok{u-d%6_kl`FAb&pvCqilv5$YCa& z06*&Jb_&J2Pk%AM$BB8~D4;pyJGW2;{$ST3gu2_qm17Pkn$;qD6&jpw5-rxie>|Cos@FK@AX+Yh*Ls8;y2 z@WY3&T;H;<-xYE{)VAbT9%`Ufc~+0q*Iz4ZnOhDnPCbAA@v7urOsc-qQUkIK*aEW)>BhoTni5_GAq{Ai&AgFq7%i4026KEi$MWM zvp(nGI3`CdD^7Y0S+c<=k+1Mk87HvzN;|n(;bOV-1lh(*Z6L&p#t^{e8baFgQBT1( z>PrjE>^8^#!hh`k=3)&SToYYfIKZ2>;Gq95JX4tJm1HaM@_kAl@0C)Wfu0dlhcf;mIQ2UQHgrR4B^4MK5X?9Uc;(vI5qoO{}X7F#627ff@!SZgrqx{#B^fznhd zMuPgR$51LWMH!GPch-AJR~`pAxCu!iWsY&iA)t#a?D53YuLCttk+K-?&ex#TKry3* zUa6x&VgFlP!eFrmnaSwT5||UONwAJbXqC8bh!S_GHU^Y)zrSx*OvRqWYS)hueeZZa z{%p+UUVdBHOr78l;7?Eb6^isSZ1X?Wln2ax5IaV$*9r@$gH>%z$=BqI5 zy?9KnFn{spy_4G~$SSH(5bdCBQoF?)Ns@?;Mp-@;6UYmhVudS7K>s!dRrt{fCof5J z*Vcb9^U{@4`|k5?|Nb?^)f&n#RpLX`U0tGoywfk4V*+yViS0v%4l}+>xrhh+4gx>| zDJ#REJ~oD&TBC2b9J`Coh~aw`@b#EWxc6_r^JI}L*O{_7$%*})zETATb*Oa=JJLpH zLy(VT{>u7D|6eOkkOG!lZ#%U4+J#>3wQ**OQ#?H&Nz7mliMf~Oc%T2qvXLVHu*at? z--^@3tzk}*`8UL5v+ZT%o-y5(i7J9H@Xea3whE5*dJEfGHLC$a@G21~z*^t8e$t~QtUH2ux^7AN7E8+;M5AE~yt zB|`3>du)W>t#wCR|530GwJBg2ja91_-MweemFo0Jh3D6t$_GBgeI`>C)DeQaxx#k{ED$Kj7IL9gM)iMenmiY;?wcrT};eMBQUTeofI^ z#O?(dT~ZXbI}8rEL-qf*FzP~X4>1*XOxGaAtz#)kn{fel?*jj|Kbr|Owr<$mdB>)P zI4_kpyq40Gwenp|xS^e0BujQaEP6iq38ISLZ||tMi4CQ_AX93Sw#@ED*E+E&>OS0q z5SCJaqQ0Lqfs+17aE=tt{D2)`9zr(#Ed1*=qR4YJr}e>d2DoYWdbxOxz>0jnKCI^>j9#oCnbJ`m2uG4h+;+FH#3ZB5r3VM-!(_dv$?GY!joaT-0(rw+7P%XN*-FV*WOxYSfc=Ozub^Xv2?H>`-fro z>YoA%09bQDdFWiEq*2YPePf8{f7fb4N(T}z#^}U)re_YVYP#^D1T7HPBhfso&(>$J9@*IoB zt%9M(mP6OK2|jkH0IMwaK1>V(Aw(6`_q$KX&$ z)SQ?UnshVo!{^zxo2#UU)@S_xl21TWj&l$|Q-rc5*zi&iUl|9<@C-anB#Spmuk@uy zi)hWeH|a)Y7oui0!cOq)`|Hv6UqOmJYh_{KGDORN%=}~IpJyQ8zIqFl60O>bPf$qY zPCRwjA2ke+_KDh~0ZvOAA)ZRYkbv4ZLBP@4{A&BObkb9~|6Q!OMD5fh>eFxGR9`t0g>zpz-8Nv)epROuV?wJ#C@H2cgzC3XZ z3SdL#I+6`pDIR*zVicm{ASUz{3Ol5PH`Q6odT4+PXg>yTmhO9WHc!!BC{pIIBZQau zNlqkWjN6Xo`vpODIMi^Bmn~4aHpS|pgMs_Sq>dnNgnw4=xs6xiYdw5I)sz3yK?l3s z837+nxJjdTQa5j2!3gaAno}=&T{j-thRtu5AE=$YchBGCX;2@y~Pzz0TR z$X4tmG>L*so7CR@__h?02#%V4f^NwDa17SzZTeS!9%UNL>8Zduxp_+fQ70FNBw}E5 zAQG_`z?9-Y(0%8*R_Fr+hl}ffWoI??ZDy^0RC{*yvrnp@mbvul^NT20vxMW?d`lS3 zv2waMC$W4S4VXQkMcUctIxGh!spGg5$IJcRqS9i%v{UHk&`$I6HT=sL^ranT_N@Tb2Pl4xF2;k7>&L z0G>%iB*IoNJX9WCx$ya~sm2uzp(=zUkB6dfSW82T*6&}&2)dr;lCw#3!xpLDefsJ- zZ0P9)j`%tMQ#qUTqks0>d$S(wlkdrBq8e@vBS1@IEpAfJBZ(yndkTL3FEafqc!nIP zyV4%I1X1=>K*6a3B{9>#9FLg966}m{gYnI*VKtFPl-{=Tb+TKn?EH>ORs1%bbDSP}>unI);quMzVU6Y1VR%zMDNY7b)yb17#lvS{gTRI5q{l_Bg^{?J zIK#tuz81!3;qU*c765&UHOO9Tafqa`HmSDcsXox!w#?C0mtPb_op>`JqT_jJ-JI}_$*hs=@s@Ak@p1cP=ms+`-&) z1ygr*xWA2N{hrH8mgTj0v+%6ow{$VGu5b1AE$C#DP@8|q>)iLObp<+kMBo8rE^umr zQ77mvd48KA^%JCvc^un08xC{m9O7I?)+m94F-Fyf!)hx6zF-EFce4QH9EFD&GU)G5 z9&2m^eZHD{yrd7uDa;m8&v2~HZfim~ye@|d%*xNl9*V7f|1nyY);h(!}ivNsZG3^}j8v?=l zfk==9>+SY;GqP)d*;S}4a*ZsmdLXaLas*}h!GQuzVvajeMV?6_RX@ayoOCaVs0tGfO4A)6ZwIGM)a$%l>9l zCTJpOMuuOb(J)cP+&hnDX(NbsMFMSPJH>U3*8Ey1`Ejq38mU4^kHc4p1l=#hhFIQS%}mHGNAN z2|s)TUL_pxpsYA=z{B^YR^JqSioEn=uskL!4w*`Z?|V_PpJ5WAB9Zm zvhlzHi%*Yv1Og7@`efnjJOw#^EQ?N;Cf^%O?R1Dlw23fg9vvPEbOS9#ixF(Q$&V3d za><+3CqNPKJPcAG_f3Ujh&H1Pwl=us*3({a)on|@2}A-oDE2N zc!Xk!Qf~{eXGb*egq(V=`3VIkxAl2*ZvjrknRSa@VmYI$ku*TZ-WbJNzZBwhy1EM0#<+3eOgWU*|xij?An1hP-#PHD2M zDh$njSpS`M8;-;?kxUT8gGfKm_VTFf25t7Ff>72twollL-p?XJFQ2a?j#I>7$bjAR z<*$iP?>X@vJo|Ab-*d+Y2THHy7axv-*yb3+H@4RomjmxDtDW4BW)ZpD_hp4o zIx($zT+>yc7xmIdH!@;C8TUH@{tixbU)$E|YDigU!>({un{GqBK1fBtXTV1=v@|Y{ z2npt=5DM93c1nKBrq}&Dh8zZixQ4-9zW3ahPhu&o2PC#Q$M&Jmp+^`}Sn9o-BDg$- zi?@VzFRq=sIOc`Kz`A{VY0TjH8o`)seJ%!sr{7mq(As%p9EqEhJQm?Aap#%@$DvrC z0tfZ@s*Y>&5lM?{X6G3(cL=|h%@%&s7iIS@`-~o0Gw~wxKBvXg#KQj!((Bt<@#>K# zVyy`E=O?niXs8U_lL1!`!Cv7vzd$lah+R;2m-jEa!t(!mQRU(jCM|Uh+XVD^WAt#J zPFb4$wANX=!mE5hsg~~gdTc%J>Uk^1-FftV2a|g0?)rTHtn$`^U}kC!e^!AjWAFyb zfE`%USt=xk@0xE-r@==sPypdVaB7&u+w>n+v&p4K$s6FonV=0(l^b}o+B>SxmEp25 z{18noTU%=nbT|+sp|)bm+;>(=u^HpVr5%4~&6!3&!74_W-t%}g|!j4J0Fv023#v(HOE1J}_j)QB# zb9@Ta`X35V-DMJCT|b0Cgovl_JJyeFt{~x~AQWSw^9ln#V%R7JDJFQQw#7l*j zevkMTMJWxK}(&3<|Oj8p&n75IGzV@-i+eTQEU9%^xiO#Odp4Unn*|N1vCJD4YZd^GXN zR&uPDSsbA&emj~oKjJbLaAM+wk-2PWWd4x{2i~OFPXX;mJRL?O^z(peAf!=3=`ttR zK=peRL(og^k|9Qf+cR;>z}3kJMQx&MCM0Kw9?Jf80V2-Y>{nUeeY$64W}bkY6bHjg zZJnu{2a8(T`-p%-FnmgQPkNE%=oQ+Z^Y_3@$1^M_swOOms;c!gkEoeWklk7GE!7n5 z3HtY8WrdJnt`M}T&Z5)5pZN<+H${vy2~Sjvv#|6SlWjI{iy8^Q0;x8)t^Pj?06YAa zM*KQ?uwgWLI$lamD&x03AB7l_TDH>fR`yIn* zyb|+NQW_|hBi3J)+6jx<5c30@Vk;9K77g5hB8USAP{@u)nH>C+#VHvX-gF&t4+pOl zQh+D!nbgZj1gSdYx_u~*Z~cl5NnBuit9bA1hrAgM#QqJ-frf9y>MYJWaIT)@+R{X+ zne9x1^Ou+HOh`nx9`D!_rYNGmUddwc!01^8)qqT{VY$G@bia_N{g*m?C>i%^&_}pV zz5m!j;!)RJ&tQyPKy;?OpMUNEB_76GA$qP(F1us*wJt&F9ZUws2Om1#@Z)eCP~sp} z+T10K+hMG94o1FV+W~80$NOF5rfDI0|DK;j^@)pR?x^q`AQO9r%)~(!8qwxVV-&zx zp2Tz+6=`1aFr|;Y?yv4Jn%oI^e`MmugbZ^px@1xE4G-?KM!+p2I0kWPIQB&OEs>d` zVfp^Q(yeSpRp*$7=IK&=xvq@f5EzM&-syqwR!qK^WO#bmRG@>kDm!#t2@K}xLG=Mr_rz}5mC z^_r8Pfa=r3MT$J8?OF`R=y!0>J5N&Jo0!#uT}Qg}o9S>;CG-V2EIn;aXvtn`7az~& z)&H@wT2kqA@u$QY*GWAE-M0I?G}+BvQ+nlQi=#k=rKb7N-tYN%f?CXS;IiKpFq_kj z#Obf^osI#Ejy;8&4}Tq&f~X(GR}oRzcqZTljZR)n;(kQBRMhjZRz2V!7U}^z{N)2Qk8F%A+R^ipt0g&CWacop3(~ zo@>2aKoWuKR`daq6z!T`4nfQn{+$AelbU}BXOl6-Vglffx@#2lXrw)g^5D5iH}kos z(B3#X(nq?fujHycc(fc0e%b3mRDh-dHkR4hK`7(l_<5)xqA>l_Yu zD@EV3$m~gzm=8ivI``n=$sBYB<6z7|S!FPdCtlp5pWa}^fwIYRiCmTrr8$M4*~*9c z4_X(NI_~IKAV^pc1K}hkf(+aTiVTSH8jo{EHoEk^Dgop5`5(85Z(LWSMMwI>L-z~0 zPi2PJ9+jgxyeHf5nVf>pCCwnBFnscwS5Vh)toBXt(^KOBam(Z^$i-4Ojn}+dhA+8% z0jvCRlWj`tPXX@zPfUs_Y-iyz$~`Ii9i3w_k^sN}Jt7oTuFA?2D&Ka^{-L$qx@kDsqZFY~6#L7U+Xd6ZjY3lE=o2Ru=4DuT@4%UPqBeeD*jRzc5ip zyma;rB>s-vq?#J4?5a(^FP+Wvqp#cLY@+ZRD8-mzG#YzT{x+^O>W#puM~Q{koQ5}K zj{?rIcUFwIb{I)DJfb!?X8-;>fu8E8JFb8j1Q1|lARwu|_HRhTuo^>jz?{nx635~T zs;tY27)`k1F-LVPIlTo2OLEp_v#VJ_Xk;ZP9q!9-~)yK&vX>Y^%bSFbf-fn8%3TLXNtFv0hp(@V(8Q6Pqt5dL%Y~-+Xw7|{yLPhE+ z=xEk>Sgrh%JI!|cTluy7z5*%MnBp-&x_=Z*p)?0#{vqqlAeMx6|V!bi#%zCvs4}u<+?5O=Y zY=&O{)-}hyekVML+i6H*vn|zy=gW1CZ=LGP#u=QpiV0bery-FYvAyMZ4}!HQeCWv) zYqsLLa4DJRAN_|Np56i7H{e$h%r#e$u*jtaOwhzSpcYRrRd=;Q7_ZQ z8pxYaW|uG=1;0dc9tFE>Fw}GVLpxByzMB6p>J;3%5%5W}?6Jk(g3|4RYQ@}ve?)8s z^*5?_8fzo{C0rzRxbs#!EqEzs;#0lS>!lY!L(aoQF2xQ?sXyt)1?2Bsrrp06oPYbG z47j6n3RVy-2g0IZ)(GlCG(;QXhSwlY%w$^K{OubkB7(Sqc`oIa^r%XZTpY(x`^L65 z(w(3c6V5Or;X>QDiqd55EsV8JOyckBgnnj;Z<>5!vi$?=ke^0CyW2jsUg@rqc4VnD z(xk^)a51=k#QY+-`Y678G4+^GO#g3vb{3>PQ*Y|-pB0P&@LXA@vX~?3d!mkhNXd}Q z?+M91pmCTIB?!zI4B?q+1XV#18ft3sV62Ya>;k!^jOaxWv5+jwAgLg{^}3`+g`|Nb zXgsDEhWULEXH7XBGGKH^|GY3?)cpEm8eH-~LpVYOs%xrt^j(?QG1}^HrIRTPeXW=0 z*Xy~617h4EXSBCpTS_on(#JR21`>8}t+||0xcnJVdrH1Z6`v=l?&X$vCsaM@8zK<- zH!Uj(=aq;v?argGai;%0R8IPKMtnbqU?7ug2HM0MKkPPlIK7dYhqMsXlknd<_y^5p zfiOPs-DUF~Z~POPrF^!@^o=0qVV>>q---o20J-rf;{)& zW)}QnXvel12b4fpQcrZRxECuWS6ei>S~ByMu#Y&>(;NhRtqr`!v~QQ9oV&kOR!3b* zr^l62_NtIXSMWQ@soZ>b;;urjv7Ovv$96|ayaKt~r~B$T8UA8<4ZFE$9kZ8>qbZ1h z28vv+TsaR!_2iU=bCeO=cO^l`kL`y^K&%R5dKEmvu7CN*cmM=aJMUdLzATsX{r4da z5V)Un2^x(Who%4dqWB!bH~21hWb4nc5oM1%Z}UajJ8@v< z1U62Va2xzX;7OqFt2^bPHyUtHW^oaB34hC~OYS?^TT{cDPu(_wW2ieaXbq?gOup`uYWqOApQFP5Zj4u}0>OY#WuYtXkP zG_P5$Vu}L(OvM{cDta%;|S33VewPDy+E4U^m;^CP>c{CXILNGGBea8 z#v6cnW{0@&EPW#D)dVZPAR(+@#~mcqIrHyZi&aTu&4*(IrztQlc!uR3B7~EN68m=K zNhLXxkq1%Pzz|zq@T*ow_clp5-X7vaf#&CKShrfBFo4MH@pNT++FYMuf5Vu>`d;{e zTYfKY@6rbv5SCpF=x5hFd5Ke$pmA>wA`z6NF+nn`c~G%!OrZGc#cqwFinf;Qo3MBN z$I{Cea?Uh~J)k{cICjz7*bUqnzgr1cq1~euh5{7(PQ-c4ks(wS(EZBQidr}O!+ERk z7H!Wi6O9rD6AGN8G2Hsi{#T$ag7M%!qZ`p!r^(BZ7J`)!(_8uyC>di6{yRgIVCfw3 z`IjhNem!33MsCYtX=wCghI{b9oYJ)@g9lx&xnWI*mzJmCwr>a}DbV4XP=BQ5`XqG z78a^K`cy1nQ%x|hwNBBi0sP1QP!-lgD05Q6nC1{~fomWCn;y}0Oz0W??VKwhZ?n7$ z2?}5mx4n} zx{_|YUP)y+k^2rQ4FB6MaW(o1Ml0s|Ho=ZPlWGe=zoq`if8g_(esH3p)14jrTPSto z{$bNoSy3sXm%d3jfil*iUTrue`93IofUVftDU^1y+UMz0 zoB}hU?z+kPo7f(CjtaTU2=L9Xt8vWDLAbBM5Qw=Vsj7}TFCiugN)iM_^PstpA2@JG znb?WBL9O_UJE=YwB~K)g7=(X#MhePPlP6%|C*+}v!Vg2!d=?)|ktu7?Npf}{O8p#s zu6+A}SITkQXL_O?#>J$(_Q&<|ps#IPb>sZd}?Jx#=-9m2a`te^h!GB9#NcFsWWW;)L5&c)QzOw=z?R{d=ML1B||EKZR*2j>UF z`-B<#2;ZbLO?kQFKok^CuEQKm{GHH>NTJDtlBo(rKuI_AP$f`U7#aDw7!;K-{*2b( zlXIvm>Q83KFW{4~U+wc4jWM=M3f4;nL zbmBgljDqMe;H`AvUvK`+hNRBV>K8GnpJ`fr7g>199!JO3T-twJlr4p6^|V}Ki3GM5-yhV_RZi?1 zCcO3RBan}LA%v+O0e!odk`4-=*WEd0H}-d#AXr;k0-ugGZYv;~+@NTl%>1ih0B2sS zZ$Hqkv6l6AZoU2PiE&Ms>Z@0#*jz+RtAMLv6cC5N-GwC}&FxZn<}v(3nI12GMXb6a zl>g9)Ld1nbyAGFTs5;77Q=6;$KoXdM-6C%X^P0n}G?apmWNSu@`U2HR>EG*^xgG3Qt@y@!X6-@2vq94#L+gCXWvb&6%v z?#dG1P&@)S`#zHAKR}lHBqxyFp`r}-F9|`4GwEC3gJc#-*iSg(*3CsY9vi?UNBM<< zZV-DS;l0BXoLxr|n`iEW<|)ox*;Bi3`6#cTA(Q*(S=>>_-4XTmhc#gKOamvQm$MPeW^Ya1ykfZCNfOZ~d>5gK+1kV{^`6*zdQ^^)boG(S&6bPW3b#E> zzE5dMqeycgOvh94;Yej0lkDW@2FJgN9OoeQd9rI5*BoN|Ljrh*4tPKOWs~sT;w__m z^Cr@?b&&N>3D-(7g%am-({+O60x0|tnM(Iv?HD@MTFRlICf zvv~g=?ooT3NlY4L$EK>QG5j^VKI7GYhq--@2{L;oo|!~k{5tESAyQt7rPL~=u3H{w zWg{Nyh)s)j`h`F^vpE9zFbhP*gDQN7I=t$M3Gcv(4tagPnbg`MV72ZOhu`PWNR~1Dn22SP_`kvEZNsN z&pbbT|Aw>N_xpWa*Xy!}JR||)%$c}LX1;~EZrmi%TPts38hg1Zc7tBe%>LbojKf&i5m-rn@3e-8imBnxq2Ax1}j(mDcjSwsO183-$(1JYyKEe z3Wl|)H0|kt^`%Sg5A1j1Ff~xiQV&k-SC>PsvAM7djUwc%E3}t|J=Sd>9n@htO}xIC z5f*oNKTdl$xCpy_!y|U!yYwy~YXAxu6-`;Dy=}b3Z{@i+{3-)wkk>63KtwhvsaWIz zbKij4f@$wS$Q$iJlS+|b{Z73iu8|NlJ6CQ%R^724$WWbKd`IWYejHG3Ojq7Tg4w%2M@Aw>Y zZDON6rB18a0`Kr>KyDj*hooSR6~GEIXHYJ7Lp1nt1fx$EYyydVwPL^S*zb!zULzV0 zk8SZg8l5v3Qe9&{3JUNQUW?l_PwaGj^6BsUdnbN=DL(W5qwLn!<{67Ig7WHu@xz>m zB6H3Ux)CbJBGPF%?bj14N^qX{RUNsyt@XisQixm)+KY8w9=KzSsQEx8M;1p*5VNBj z=AYw{V`LEPKeftRa=@Z7x-{B7+!sq(+aF_tN%TNG#!J<1>GT(DkA_TlSj?VTth;(^ zCp2pfLfj)mv+h6B>bR+O&@TS^x0A(X17n37dacD4T%(MhjVqe<-*^*v!s9Xr$HWL> zjt?_`e5G+^99>$t(6#4i5#8VvqpI|4RicLZ zprrMlCNDqNSaG%7F~obGzzQ-tTeXq4Xd(Yu`^?P6^#bVzE|yyU^Q76b#M9^d_kxpu z&%;o{$)P*D#I`{lkOd~QuDC$agI#FA?Xz^-!CLzN3&N!-+h5yNYUtBWBgB{P@L z2gV>Ce$axB@JMK82yV8DaiDG!u|6+bYho7|vyh0@9&0-7mbB0r*kBt>QU@j?05!Sq zUZm{-_RJ@~(RvHYw#nUIzVN=D+J@7;c&WAeRh5_3b`6gSFXw+_-wQ^7!#~-wGk@WC z#~fQ5Uj2&vEwIFRAV&XJy=9#PU(+A@xCY{h6h^HcV9q&h2J?$qit2;Q2Y*&?KH?(v zfL2ceRS>jtH#WPh|8;oFPm$XCBC7jx+{sNnGzpMQhdwM^dY|^K>S*HC@RAGz)(Q*E&CR#eZ?%U4tzA>Vm?V*kY?ql%UFdTtX*3)LbgHB@$k@8{ zbB8AD@XkEM#R3{(tn*Bn9|Bqi`(P?3aITHn zq^L7*H2oOlz6$8<0N*fxg*P|;G%)I;1|$dMe*rL}AkV&MuMJH~cc6B|9LQgiG3Z}p zEOlAsb694F&_gmvZ~8+LH#Ho5~luXD@<5m+8$y9d3g{!MTL(gOQvhUs~NIoVrqe?T(?>;CDtJ7|$>m$e?AekHoJs z-d=W%eR_z8FSD00ZMF*JVE4pZyP^rlD!s|W4dA7 zg}x5JVMpX=dS4V2bfJsr$jrIi-}~;&N!%DeBo%TZ_?eM<^7LrQeGBgtJ{@^V0cYnXQcCEgXGbxV`hn-*>ht0kxEICb z21)ad7M>YU3%^^ed6BI-v3I|<^C4-^FI9}gZJ;u9L((4%^=5j)A`t2R9ZWrQ;d{e?;i?!BA@o33gE3ikXkudUsd-;z|j7LDl;;&p0og z#YL=9=yIL8c(5;IY12WOITAIX1!{Jg{GxTX^xlr?f&#A&()_I4`{@`&&TR`HxKz^& zSBMa(N-&w1bx2@UuHE=4(7*hG`6&)j8hz6CU-b2x^S(U3Lw^_tgi=+PYTL_Gi$3q$ zH=L7)%m~ndq6XN}K)V(>DB&EkC=pA;P42@MYOT{lPk z9<>qzqQJt*w(DKq8K(w9)V;(Ngg( zidD5<3{5bWi;H&Ex*?U;!HZPQKC3Z9C4p%|p^lZ+o zFmQEv;0MRMLhOJ25lhY9YQ-TYTYYkYVWVHcX{NH_fRHq^hOOG$x+4eg{$ufsG;=*h zl?v2xC!$9N?;1p99EdWxY5*Ld_FbTZXmwfPFGSL%6cO@v9;OEWn+t^YP_9lqJ~U?1 z=tTPvUwt^JhL`Y60&?DuXrAxEi&Fpo?H`o_F2WRb4DXYs!AdW%{H`>l@K7)wS?F8y z=Zl718vOV0(D#A^JeW;w9_EG#NKxgH@RA&XDS}i6L?H+EZC@xjm*WjZO#i-6vhD&= zdJ5Zi_7DWGSoL3oMt8ULmp37|FBeSDbz-?=*Zj11y2PNQ#aR;=8`JTS%$fkC=)JEz`Pf?DE}Uxu5+?DJ-Xk7 z6m*5>8&>3-`Aa3+c$Ka}Ux(F=uX|M>q$PMHcCJLPl zn8qvcePc7Kra1Co2-IcvjfYkIc99~dg9nZR*2wu9;DC%i|Fq95bK6YQ2r!Z-D+0x8 z^zwJ}r{Lc3X6s(&INuQ1kamyOxv`y9G@TMvkgQnL8@@8ZD2X4=5%^bu^d_x}! z&2_w7?`lW`?^YHbQ9s#ywRA7h;IJoK`iw->6xnD?EcqXlkbl_K(*0rf6L_2;-c7OzMIRf3d3F%P*pl4 zI9@u~v z9)hfn%V--WVPSv^Y%pFah#(Dh(wkVwRuIvA#Ah{kRwFzuW)5L!hO(qJ2>RCN$mzC} zUbw`Dp{n;ycTM@ne`;%B;qA8fv2ZzOIV7NovFFQS>#Rw37n?K=bC7wr4;J#=vLzp8 ze+n}bg*`-cAEfX<$s68Et3R+ICj&}G1?XmiJ-K> z{om|1X`_YgT(GfVz^tWQjSRY8aoLbctJ2(&{;Jjdq|980=|cgk<3Yf}MxB`1lht;a zNcRa6kPdsZJ~9#%V*GG|W(Og1fc3rgKKK}!*Gzlk5}LE*wMUm#G~=P*d~=P4aSV6+ zo5*jY(3+Oj`%=mS)PbLZU;A_xRd;bq}}3i{MPp!;pVUO(}L zrwi(SYz%I38#%9JjDg|dfz#)T>_z!Vb-3tl~HKVV8x{h&N`wq(Yo1^N7cH$LjhD1ss@U<+&W z9%kml%q%DaY&L8I9Io7q3&`e&$%_M~WKbRy3O;iC?Sz@SII3brchGY4&Q%nGS(N9F z!+mNM59-c#I#r&^aTTV|Z2UJyJFr#8Wr0rJB<_sP`y;p)paUe%v% z$Bbo5!$n#glv{|Twf7ZjI(P7qqSS+{W7ucZq9F1(UQ%yWVQPn!0(CI@sxA)E2O-Ph z^iH8iwVC=RO-*g>q{DY$UysH~_cdh4kC&*9=FccjMfHwCMDcga-To06ptnbJ@wA(VRm8CcDw=yi{ou)XNlRN$!lG&4bqIe4 zn7>eaGWfB58tm_cD8k?cNObzRBp^m<2D}Os-;|vjF$G-a!`@v#EdD>0My6K#-De6H@ihQbAhq&C<5p7fiFSy8s;F=qqgzYY4NO#A1_bYM* zjxtfcQcOOUVgkgF2hT8zb|4ks-K(km2^!ZX%WP_+J0qok0K!cSnHUux*n0K^8Oy2= z<3d7KHmKJO!0Ym^J|HO4Hvxt+r3Vc&JW9bb3gg*h- zx?(es49r|H=YxPx{HK$WrqA;8 zFXi65k|L4yL`m}uj`f~{%vcwtZ=A6JOzuPRD`#5MFB@LN0hn)4d+0d$zU8!?V#Gc& z43oK8%jri@2upE53bFo&#LhU$qsoHNy-Fh^1N`L4HNVF;0y-?OlLb!C!4SQGXl;}y z;sQt99wN4SEVh2?QRBGEkWFGse7((GqO^`1=6jkiM{rq12jLcKt*aAnFwtSSn zaDqUEwd7(m4O4eizN#XCPTfXDUKHabj>Z1tdjmLX^cqbGHu)n&{RZLX$?Ta2St4LR z$m4KESB@n9cb5ch9&-@~+ab*-tlEj}8TLbu?=V@mjvamG(3jox??pV&H9|7vW2ti- zwYJi-`Ee=eZF`pN@Efw$aJSyT?Jc*Glf^$p_Cp@L==YjW}ZQ zyNR-g9p#6PVKRxS5OT3$+tu{vKU-oLk@-|^iaW?#^12DKn*5d(3G|)dk02Nz`IRQ! z*P*q=pGkJbOQlERjav-u|8^Y?QXb%!;grHZ``E0Ix@V{TmW))B(Eyy`KwP2W%Lq85!oOvmXQ zVV6q7E)g{>mrq*VZo}O*-)z@W^k>V=+V8&Lp)+IOL`3#J&wO?=ydc`_n&{3>S{HWl zhg81i0~}%v^spouq+6iz2S>K2-k|Yd=H`}^HLwm4^xa~ZPsPG_bf@^xY2|Tjpl9+v zGd_veJ@JHykl3XGLtud=xeWx2fKAy;l(O^SqLC<|2npj!u86q`ww%5+us&>>MS=X* zQm%i7z8pYWcSB0aNA>7y7V5A*{_q2frOY}zE9jitik%E~qm8kCT&?Zuie*HoM$yo| zVUXF+#mt48PjAF{Apd0aBAD1!|F`eQxt%Z-Zb<2$wLMtG2g1vgFQK`ua9)HUQ%VZq z_wW&Y*Yaxc$NMQA)DdlJ1wgRT~yqdpUQ9^3pByubGO9MC%A%y#4{e=190 zN~03{$vzm@x7R==xNo~&Kb+mPlQDBoegXGx^j;BESDG{C zb-;yb4YU1e`PeJ!YgB}9KZH+$kV#IqmS z*tVCd#B=it`^t=_MKw_3XZ}E?blI;wkw6Z*ixhi75|HwHCOgdtZKLuZ$7u=2kh=*` z#Zj# zN%{Z0ya#1T;D{C!(qId9`x}lu=pFNy)vjs*FK@_8SpQT@6#v&w)7h`BYWZRRK6RAK zMqM~9)@PRVkGtTdfgI`Qew#nxB_Q5^i`~54$!K%fgAGJ9Ja`2a)N_J{uDF+r6=FUZ(v(oHI83j69CP%yAZ1 zi`T5tgR10LJn7O%fEge2KK(Z#R~JYjp(~e9Tu4rX6Zn`(ZRqGAKOb^ev<=_i>ex3H zkKCF8!`zhUz||xF9a9^OB}zyHuK1on+J6<4O7f01+W334M0@ofe9J&rjdBuXlCd&Io9a7MR1B;I=(Y1iBeb)p{a;{VFYc z^hBWXp#v0s?zlu$L4*1Jv^EHbsiu^JL>`LrR${7F1>8L?R~g+$zW${y2QP&n*o+T^ zh_kZqPwREJ$;X8p8N`W72W{ZOfoi&<&mV6p0Zd^{DrvQ176!5#O;FV zLuMa?3B|ecUhh?A7N5jxZc|P*wU*wRVhEwxkNwrC_2B8K0KW+diD3pC{yjb*?+-fF zxoa6E=dVK4_jz;k@;DJccX$;sco)wMqV`O`Ny1KO=c zHktsg2?r{1yevLf6PusS){T7vAB?EHA5hnY( zEY%h!Ya)({-~O(&P>tvz@ag8D-5v((ZQ1ngZ=`cfk=JKqp>Y6gpmJ}@Y!$S6B?;+A z04tpJsT+7mb907=7_1st)r1A+A_t5su6tq^Z;N$f-%YJj+)UPTDo8$0tN9u*b^Bh)lHx7E((l+@u5K|82cw>Mi<0Wh$ zN~^0<1Fje;j-LH9Oc4WpCm`<7$Ytdp18iG^{9C3IM-Saj7(0B^ima;Tt7~zhBa>cp zo$?^*Ke&CcR;b+h7oimKVk0IFA0_*4pZTttXS;b=S^9cZ>b0jQA0$VXb=2f4K^2Eg z4x6OL?lw56HMUa^R?e~c=KuJx6$Sy!r>2K~u-gYl+~&rN&`uIjF3(pe)vmr`&Adtx zUZiPzZLH6x$MV9Ydw;V4(CAO9`T6EbRNhe`3ZJc)`DSYXqd->H)O0Dp$z~v+=J~?W zCR^mTE#_0~IVgfg>x>B02lw#9;&0@n9&c}HpEea zvlfHREjVr<`>F05p#Wlqnu|s4us0V-bxdqRuz}H@)s>}^vF47KV>he*6m(&s+J6U_ zdAQYQ24vb%&j1hndee3fC;W@-(Xwfq!G;ggj86qO3|A$!%m&lDG}JD7tc6Q3cU|Ya z0#e9Tkne$^(!AFL^z^}-v-q7LPrSRZ0WDORGqaH3(*_4|+*o9%D~GB*y4b?ku27b;3HQ!*B(59nKJ471htSw-k{5fM)215G1=(t(u5 z#qu80%6n%Egf9b}3#h%VHG^*1e%^^-IA!OULqyP;Fd~Hbv7PV`>uskv#WI{5%sMm7 zckNqd1jyi!b)d`7ED(a|ki0(E6EvmizyIWkm_@$So#;5WF$vr!F>9gNKk(X;xIB_YL}bwq!rf)SgP*Je_cAk!AcyGb8=@4sA@GxopZlUhwkvv4AMu4J z|JJ9CTMycGc~Tu8*k7_?ohit%@BU*|bmE+t>*Df1Jo(J&^;^Rt&yv1QW`6yYW$O1Y zUXr`{h)&$-P4$%Ync9`T$i|bql;yMa`tp6sxZqE6q5eTnIw{YUM(-OJ5sf#4i6G~=+TXRs9OUrZOOc$~HQ zlac&#*?j3IgR@Sr`&}ZgaTC_GY803AmSSE_!LTRG(No$OkKZcfCLHNBVrM&F9-@St zewfWVe<#8Gj~=NBrypl7|M1~`VTy}3fnL0+*A!;c-+75l{Yua#)nhTKlFe&9`)1cA zD_=R)?Mg(x`(5^?PYry7fzHLZlkC0q;9{-v3`QEs=0>@u`44>hvr-A)_UM_{MaX09wm7g4&i3|j;LAzC~aSyHa`B&JvI74eG1Qbs$TX#+*PSKamM@eDfn zm}@W_vT8r|UhN=md>Oq##)Onr)GMUnMLxdu6c3!5Lf6kN6F=R2K`TEdqKS*RQ(Ij* zH>E^iUI96}wAw&TuqFO>tM|h5=Oc`|%N!-RZq*Bbwt}h74_Jvl)Pt9Vha|O-;7JTM z2r(ORDG(^GHQuxBJmb`}=r97)Z)}^jX5y2}cl4frOiRUl^`OpdiSGv@UShm{o5Gby zcq!;e+}z4@U}cCsardQ<&*v4%^BWNY_Z3LI6m>l8dNkuhhR!>t(vpKD(b7?Br&(Ij z=}2F^gTq7DW4N4de2(;pKj75r3J?%c=H_zeN$6k+gK>1vNWuH$mSk#IJf6-c0s?UK z9Hn`KcVv33 zoi^Ox^M-LowtjiDWJ4uJHE3m`c(yn4VALl?nUNw2g#Kc;I;_k^_N$t~6u2PjL245c=F-belI9|%5LUTdLN(z<~o!kOG)vly3prHg9J$y59tXh&)>cKaS9w< zZXo*A>5&5D+TBH*l?rY1mKyfgH-FUAu^Icce5AnOnT`^Vz_&z^e9$_E^EeIL}(`9l&kbMIg@ZI?)okQq8vHA;(EDn`9{hQ;cH}s{}1{K~CiHJIhw#GNQC$ z_=Wi@n~bIZhlc4OstAC_jrM?0GlQmg5oXFG{+(`Ul%X8z{0hAxFu8l~&y9KZx3T9h zUqZY`5l(%wge+YUygB|jI93BrJt#%Jj0Y+ZMkP}md7~~RNGX>X;YXe=A34TNRdjVE zA?Xm(XP*Jv8Xmt8|QY2BzAH+HvuYPE|cRf6A9FNE__7iun2V{bayR8 z#q$ZO(}!G{-EH!PR@d-{3_RD#cI4DqYj2Kh#@t&PyIlvTYbT`7Qya6jtU2|} zZ5M&QxQW#62shX+$SlS>|>H}<6F#Lbz&+S}Pd&ADbWo4$3lqzj~{jS9EjF^`ANh;7DQ znAoV6S;2^VWmPrB$aU>o{1G@2!xcQ(3K5|3hZm(?*`r#jZ_xqu21 z6LK8GqJZk!AWcAS*?3$4J~T|mDdL|XS>HsuC;u54W-+XhoOt{ zia|)nP57Ad{jX!2IA!L8msh>mTA#44FXNFbP{oCeKd_V{h=;AV0lULsvSngvNt9kv zZ@2P23zi50uJKbO<8*wEs1%e_dHwY@+~FSvU`1QZRaBad*+Kq@p&9_M1Yzwugvg^ZBJc1(9DKnR2hN$}As>ag`J20eJ47$%r1wQ{aM&L8A8+y{?XVVZPRSB8 zlk00xc20Rcvl}Ov!-qd;pkVswQOfJlMAyyDpn^Aj1qDB@SG7N>pd2}UwDID-^<|^I z9$^AAKX`}7znYGaK_5#m!Q2s-?HN(Jw~;VM#_PkAJl;NBTK;TFn2zQw7Kk^Yh5+b7 zFXx0M!GVp$>wWAge=rWjUft0LvbSI+m%G63rHI#B`ESD))*`#~?G&6IeNS2bgzCtq z6o`K|YZd?FtE)Z8s9X$>+fG?a<^sE6vab>|EmqVvfuBYgpP^_fm0n>W@9C?MU%=N%$k#o02Q z7_lhhv|9odk4IjgSJ{LwJjS0=?zRyVYN7A0+`)PFC)nG&U{^r_@C=jD2pN5-V<;ui zB!%GEhb{*^9csQUd}Y$ZAE-{9YcEz@oPIscmG6q-*bq+MAOrPPt*C4=_`=tw4^;_4 zKJ9HMB>6_x?7PtAtJ5i~7c2hbW};Ovhy{9EY7m(Mp1)3m7GF+2;zHhRgL-h8ndG$-;fm)EwOuFP zUw6UhJ^A==N^6bF$jfunD>D1qMr@z%m1oxUl?7Q?W6f#?j>1E2jG@^7c>&tGPIGs=$qb+JV{Th7A1b*-!yqY2 zc34+$$}Ol7Ah&m3RGyo0Ng@RD-~z7{S+2V9QBWy3&;)4HO0Dp7Z0Z zh?Je0NwY!o-nEh!59qqBOa3b0M@__$>gc-VR}Y^aT&1{u;zgc7+Gn`saFl3(R~L$4 z1|u2-%5{j&U;a^sE<{0|;xQ6f>fhI^s+2OQW9?njiu=8;=l>nN1nw;dgi!nlcj9X& zoEnZnA>+jqQ78*84n?N*bwfoEALD)8NIoa%6mh?@%Pa$K?=vp~bRKdL$lC3=g*y|Y z0h5lAA_k^5?x_!sOt9mK2MS{kYOSo=MaSGNSg(J!bEm$ARiJ zRw`xg<{c0<)Y~ijS4f3l{`A&zbhO1l8u8lm<}<5E($5B(z-E_adX{a5z-N))*>W%E zZJwBGU`RTTcPjEZ%YxoT3!cBUy!BFPB(k~Bswm!+ZTf(=n#nQ-c9&Gh7 zq`JgRGkDjydHCA^WTt9t<@gW%aO*lRhQrr4)#n#;Kxz>9eLgu0H8*>+Mzc=<@zj%! z4RG%zsU~$xex}{s+@YGZ?%5sgIA=?8%e`t1%cAYzH(}0Wi@~{jS&yO|4(6lO!%=Hq zI9Z?!CkvHgI=HHC0lEp$9_(}m@06I+)2ERxMbtSQ^pSq6HsA9~GFpATPrY zLSR?6u0?54A8_}?PyA5L7yM*(e;ejgFw7~MmP70nZdY2l$-sf@4pO2| z@?gNYD5cf&{ZY@=U;6KOqIL`EILS^(t~eB4svXe7C3MohTv+*mxJU_-mJJlzF$u-# z{f@Z;Z_YBL*gJ-5q5XC~k~x9) z)t*fbXjty7{DC?c(^?aGC0z1HwYV{^?_J!cuwL@cl0HuYLa3!DV#J*RlL7l_vb^t1$P@0`l;Dg< zUB5)^IKLT_OIj@5>Hx)28D;SZ{T4Z%1gwh&({rWtO4Nz5wJ5lmp3INQikTR@M7a|J z#)+$+SHXlFeV;t_f?=iKy~}J@nli`S$f-WMbq$>5qIyp;CP2q?yMHH%U~d`3E6gd= z-C`*?Q3eM>J_vf`ek+~6HoL!Jeycr06_q5CS9jkl?!_q%-QWT-y#sfPtRwkxwe6U^ ze{Y0q?hsPdyPd9vSPea9IiPmp+e~y7YnG<0pPSFVe>ag|L-ZC4ox(!KbL`Y+cZfOG38L1#{#QsJV-vokMw3GI3henvba zA;0e>{>-=7y~8feh@t>XjDPeWDI@MEn`)BS^r|!b=77c z`1;>zA`bwdk)PZQz463H$b!`$e9Ti123lefH$m@q5Q`}<(g$Y<#myi-H^q;jUu@2Q zBX{--p}U5Ca};({A6t_VK>h&E@;EF*pO@(iq~4y>`ttEO?24)8A-mnV@ImHj{L!P3 zTPLx&GM=gG-|h>N?QL2v$${zDxu?Q$9jNuoi*NdrkJn)8&*kwZy$-PXnw<0-`h9>( z{Rk{YIV$+MK*(JUOZDpA)f!v~nB(o=`IzuLjV#pog>$(Et0+Z|?xQC1IsGRXn)mt- zl7ZXa3hW$`Yna(#6qubfTWjB%+QvRLu7s%S5?a2B^S`%Shtx)BFAwJt}FH%P^__%Z+M*+6ZlE_|K%~PUeVwstp(an*>IVc zaGn&(NT7n9{7DK9nv3bP=vY;|W3o?&Hi#kGf6m_Q-p(AO6oTbIX2AT{ir3WH{-XZYsHpv5Ui~L8Q66x5# z^UE!oK>hU>Z{!1xq5)PHss=9g_=1SmFXl4f1T6vtnl%a@1SVXpp0szm`+*wAU!Zm# z?jRw5_IDnFJ@(x~Mv`sVyC~vhWepq$c7r(n8-RjJ)cNMlW`2KPXvJY~r z@DInCRLb0|B8H!L1azhkuLecvTSyPu2m;n>Ir9bg)=d zc+ZD8r(Mt@ACo&(@u8s@2E62AG?)jJVY_eG)xBc$y2ia*5B}7Q{gFWI1Jhsj0SxWu`wd(~& z7$f)buRQYRgp(-_muDU}pOt9qw*4Xh%*ods7(uL`lju6Z8#LO}56Dwe78u59HnSxl zWGDZh+{f0Pc>l47iawwiV!ofZ1G1;p?%5@B0}HeO!R(_mKNOfA7x)~F_8zvM1UYzc zfO5#gaeryAcIqW@#EVOxme$P8%z%{oH^Xm+Lq5Vg)G$F$?+?x;zQ6=Iz53<-TpVPL zXQzoEdYeb&xU4pimRhm}rJ8GQ_V!Mhb^~VpdZej;zS}p99|5`kdUJLfPaOw`9_}AyYgPNzg2Hk+2_MJJ;VgYE?f_Vuv_XI&KJAZ6zvhsx!xQNkc=2)kcgfe%* zsR|E%@H24fkmLfUVCA4VN1RW> z&$&f&=77{Bg{??e-%woa)#pTu5W(@BExyAcY6G(9HsXdJ@2-U(AL<8w6v z<2V-|ZOS<8HwzDsF?K+XHw4muMBx>gf6H1n^}m;T6qUln#h7a0#?>`}Qe=id?&EOi zo>$lFTh_0c=UimU7&NC{bw-=>bGcE$xu>%*7hsHowA~`;)Q!2JGa>y3+R4N!Et5YV~&XN zKK6yl6f!8~j2e?{u6nS8oBhGdoyPoK3clLVwi*u6uO)!aFF#C9>pQh@6?*AAaRSvR zLbH95DJ;0*dC=qWX@Dp*-&7xAKhO~K`trHbE6+H2P)E|t@5i^MTY4S*Lf%PxcFYe;}`oMYo zYWJVrrt1QDL{1s$G7|wRI##^Q=SmQ*DiH}Fm&l|9-%9d{%9N=wwM$fHiL{`NM1*rQB4NNmbq13B{QB(H#lAn z*%H3{K}je_oeRht(bXF~K1+bJO6&%23LTi}iG%M@HG7m6^o60Z2mLpGL+1l8c*tk_ z0tlsxb_z(14pCdX6&}@J;n_NpfjKYCJZWTwqF6W12U7N=j?nm?WDS}`TU1{cA37z1 z>^uyFOldjK`E#B3x`a$9a9-q78lBA;Bw)cWEQH%g&lp@AaNn-ois5FL)p|`nQ{=#a}z$Uz}eK06hroDOw68HJt8;c*i>$>15>VDf{}tQe?pdA`DCWM^35Qrw=a54_xr)Gy3Rml;l>Aqn*nJ%P$e(YCdW>hIAz_{w(iHOj;eGqYnD z`a(+qr#g$759Ef|f16$TIi{ZNeD$IzMdvS{q`zNRoXT;Zo6JT+^M6-wrtM+oG$I`1ihO6y$t*> zaj{-(?GT0+cc3>vf&EVKdPz3;?@<{R#xWy<7dsjAJ=PN|PMo^|*ZADeO1a-x2PB{; zbs~X{d%kQE$?s?$m{e|Nk*ZK+8hH=soqnZ48C8huN3&mNWrpF%kJNes3)If_E6fD& zL&I!F;}7{r_<64H#E&PqeAEpvysN*?jk45RceC z@(GN5)rsUKL6e;$@_g9Wf4J~*JZfBbPJnL^Kb`SZ1;_2oobgE!Mym7^NMz%GujjSN z{J82>nt=?(A7>zmXc$1Te5~PeMvPFo37x|kH{m6HI9`rp{xJWi%aCkuda*@QP=+~0 zT$Xf@BW=ZZju|-Fb*qr8+rlQ>+d?uB%6h@;oZh0D`BKJsGy*RKbg=i{3LVFbq@u0G zJj~uQCCKPeO7mWbc(v~gKeE6xNaarGfER+lL^T&)Ox}J5%)!jYouJGCqG~cP^3hvQ zt@ja-4Kob0I0a_O0EaE{eUy#gzrnqO=L`8jz;BKx`7^lDgX)F=;>wMaa2qdmoUoXG zrwx<((gZmL8SeqoCuMmPRKVSU_nlgr@p)&%c(Zw)cFy;47zNr(6*7Kdr+vy=%v$Z? z^ke3p6h#N!y+F)`!>AOog%1e9aF8|S@TtK1Hf+TDbkpcsX;8LU8OT{2$Xf;#3c!fH z#7%t_%#DbG3|hT9_Fj0Q!q3e0$Lld4g9jialzr*t-6?Qs$*kSG0KYF*rP*m~PT5j~ zFFZ!Zx+z-p_(hehYNzefUW;p=+bkzHP>EW(y{R!LiLB=l&!%6!WmsuhX)Z>@62>g} zKVS=ZWUigEa$VUmiXDQYf{ud?`xM+2%dKB~v8i)Ipjqfi;zN;K(-6_J{m#CHn%~eC3<&Wn+UK`Mbd8Ot32{c6FOLo`B<|13fY2uyrIcnk(5&E( zS!*rM_13<^gSzJ}AObh#C^j=|CP42#-9ZLK zdtZ)*aY8fj8-EC#yCjf%>CFlpR;Bt(TTjk zc;9e~PfN!=0UxhK*T!9+?c`L(jaq<8Z8#E|eIl4MV87|>7hbr)a9~$IL=^g6_vX)9 z~@aIPQ`*t%WH!0o^qk@_Ls%=TRU;v_N`=g0UB)99OK8*wb|KTeH zdH%Wyr=D~|zZgjOB8UTaM#LNe=v4)~XRXsGGId+(D1gMVImOEL7Uo}K2gWa*h=EA; z`v<*+#2_s`l&2;-Ys=8#1~_E_(_sDm8bKvLi7k{eotep6%zAVNb3akPDoS3o z^nl6huF&{;1}7&`^TqUe`&lUQAXvnMjqHc^Da?!)+@$&zz8r}L(Yp|Bz5~o})!U20 zkCdp&WuSR$bX*2Be6T&q-+HMqxVcg=|sz)kKE{V--}V79|23B<;J zWH9w}=%=#Kt8+IVX}E7^<8k80_;r$w9kRT2bNs@gyjlAlqBT?zJI-FHX*hkBUF{C^cJOt0_;&ZkKJ@(o5$f^X*1+o(99qjX4u8`r8+|Lg*%> zMCNM<_%n#eDUNRRt%izw?4`N#a2&L`nvlKwVl8!LmHzNj9)G~5o|80Ldd75s!@8qI zXi>}|4wXrfzuo%esJcP$h2Y50P*X1ODPTwX!uuD5obQ&OPo$6f~MsEbTGX;^=_%5X!yTH$uR9k9u>X;fLA_R6jL?WDo>I@%q2u(OCxt&8z~DMFTn6)HrN0;%SjM~ zc+`0zHXu$KfH2I21b`y|JQV1h!m*O&WcbZ=1d8jMk2NVLqvWSPoMhALHJz7xqM*O( z93G)v#Y{L7t9e}zp0@115&20Wc`vZ^1Ai_dbq=Bm3x^(SV&5a`TBOF|k1lfQ@;v(5 z`ad&Kj<0^)F^A}?;2Ky8Rixdt#zh!zLeyg*3K1!SNeFt?ln3}q{0BE-LWb@gq8g=O z{<%D5iXS?jm0M7y9QYD5E!E1k08)2TjVr zv;Y@=98Gi|+=B{`H1JAoe1@gHm<%v^O;Fkm%TD=@4mh34^hicn+spEDsOINimuAK8 zmqV1EKc3fJ;fKeXY_KQurj7ALU6i#T{8rotQ{bpV+;g(#GjxwDzOHKLZzbQnp@NlE!-drn!v!n zhbSAoB^Gw7{^{)9oZvg?{=;|{b#xxkkHk~1fH za$DqMeuw1RDXtvZ)n`@9XzCYN_%I;qpxE|l#)Ape zKzuB6f>VEoKpGQ-g-mqoF?Ip`p|^0!$3R@3rTtteKiT4mYEV5Ju@m$9$IE!~6O8C} z^l6XOqZ|LNksx!3c25?x=9)uV!t`aUuJuuWpx-$F?N9Yl`^e4GR!$pGkAml7`c2>* zt08e-qENv|C7i;4o;vV4{swfK17~9hl<@12B!)(qs(pNNj(p_8HqR+d3~;*CkL-BH(VJz!R7cb3?@k4t~Lx=n!=1fDZ%bedl~_dN7mPUa`gj(4sery-wT5Yb(L^tB1Fx6`|8$WgqZE7xCkACYf&} zWR@6@R7pv?)Fcz+Z^TT1erT@YIe>y_UjK=bI>Gy_?>G(bI%Ld63>a}yLisnVg`mm{t(Ri)>dlPN84w9PKeGFqSlL)JpZr55q6m|i)V~&l zQd6YvNi>(gH%lCE}M?rG6v+LTsShbU{uANQW||ErFiE{7orv9&A=WC*q+DnI z^@sZ?pK&Bo*h$W_lULODd#o`1PY;nt=KOn9oA!1fIzr@=tiw3v(Qx74U(w6gnwQK~ zB_h4=v1^qh_%cm=R@#2WoscqH;Cd}v>L~|thl^`gOqus00qt}dkn~X+y{1HHi5*X$ zdFPB~bg@IMLbE7Q8lqu=9UiX|6^l|30()U2HAqmbq1bRd!x*pM5J>yo`J1^@iX~!7 zb)XjRTqWs8zbG{?GhI2lufi{amphLFY$$Z?A@)o@_-FFZ$6lV654t&@A+--Or%}f4 zmr>z8lEm4=A(|yrVL;s4hcVU0z5XfZ$Air0%ZwrRiW=?V-6IF zXB$%H;oJQprXaK6MRVzb!Q(33p-di98jm5evsoSfHy()`I>7Bb>)SSY>5x?iwE5S; znm||p?-x}Cp%5NgZZ!~WLfAx7IdFio=Kp>7E*H#tTCDj%Nd&jJNmbB?=wq;%Xiks6 zt2OV&_eqa=pyQYbT^_X1eETY?oytXbuod}-4s5tfjSFFZbu&s#CyC4Zpk2$RNpdT3 z@^RPq$XcOYPO56{AthY`w|`LEczfK8!a9=Ax7~TCvy=S@u}^7Awyfh7snwf6QO7AV z{BWs2fEcXn#ym$r2gQJ4EC($<1$9xj8%@%Chph={QZI1QvMU8oksCLoSH7_SKMSxV zL!~iRTM_P$FrQl0rybD%+yFWDW$p_j2@eFN9 z>Cn;1}ghK^6IVsg$w z_sItOPc3KNb`zbW_!{IW_s>0#niSOfd8okpTB3pg+HrzsSxb@4`nRPPx~Aabk-jS} zNB6$|1f-KiUHkYlM3m2QY<1={I;z;SDdz!pPi0kS-MIdbYgiNVf!`*3!#v7`C3 zri&aEtXD)?PM?L-P*k?|-jq+kfzMFEx9tj=b{I65zYV`C3LDjI1urv4h3r3KXwh&! zH;)pj{`|v#5T2jq117H+JQR;XQ2A&Y%VxqWV+%PH!%3@}E%a6$#Qo zL9IRLL=tI%;kpOhhXxeZ7_9-t$Z<{uq*O`nQ1^u^EWpo)oZZCDQc- znGK>q_|np=-(74DKss@v<}9e6RZ#U-RS=rm8tnj>5fYtC$3(Q?VKmK_p@FJ^QI>S+ zdFYlHRRs!f#0)gLijrp0IwSZ^zeip%y?b<5qlMvr+rBn$o>1hvMDFd5pQzBPo>V{T z%&QUlKzGy~TVEuT{IcRpxk-cHKkfG;iP6xN$PACsr{^tUDPu`RC5qzD_6sYLm+!ED zRZY9Elo`UN&#AvDCdYHp7_NyGfOn=2ra+AkNN^6eXy^cy65NRrorgOYctDnYTx=B_7}DbD zk&xTv*Y}Y&2kLIMiVl`r?O~+oz#%);N|~39f${EZC%As_*xmZ_#{tF?6#;6A=A6IDk^rqew!= zJcKGx%tJ2Yre>}9!tKNy97%{X?$m^%M8IWHAx%((>Fc0yDjOSZHqZRxcG0Ty+ngwC z!hqKe6Mp_$2C(liM8wiK1YwC-6cOKjZFY2EdC35Nj!w*l$L6Z%-mmU77H_yTRz+gz z%XE6{(8W<%+(4t6C`X8tHf#+Ac`)T9840DF^nG9h?F7LJWPw3==}K0c3VXLhCwX*N zKrUyW5Y$aKsr*h5cg8Di4DNSN?Vo=5XES}7)qR`e7M0=1h#n7<;PT=M8D$?utHPcDq{o&{34$~$2s#FP6xk$fQy;$(%+c<15M8k8pT?r|w zH$7rVYZrkSQpL!a=L>Nv6>?O@k@?YSPLNXt%O89bPyUQW4oQKBpub6JnKIkisPfQ_ zz1jD3`E`Bm$D(5xx+Mu`=13XllJsX%BM|?<*M$Xjg$3cbOJ(hr4&TI+MB8la9X>3r z7T00|B<>>*Oc~9b#y=C(Btp&wwnA(~Qu1d>$fuV`%2`AT+RRu(GGU%L}xoN!QtgWK3hy0d1v1o z6vIa~I02J3?{**~p{z7>Aogi8eMxa2IBz-g(eu@16svuK!aX+J#Z)RP&wNf3IQ+m? z0aINIhL;?A!jrP1MIRp0aMo>!e?v)+jOlP1RF>l=U0Lw|;~sZ#;sF(QdV@j3-9{ou zCO4;fP`JJ1(9SM|h@Qtv!HUdt?9@w5v&jAs1dpRVi<)7FKLdSkQcxx{BQR2!^<6-U zNf^)8+mW#K71^^p_M-IUD9A6(rS?wloj|BB&y~8pbOUCEr#0nnakWLw_@&H3Dw)&* z1#tT`Y97N=`i($MZBdq}vG-|EDdQI2`x=DE^MDRio*zksdDH?N5kA`S!Q`c=KP40$ zu!ZJ^d7&J(m_08K)Pkkn!R%nvs1t)S?*lF!(eOvz;2<0F=s!otzJ)EGl%n*p)vJz7V~^Y4lA>fLO>{*gn~b4LytMB-ay5a!f9_Rgp;%Z zrF=Pc$4?FT3#)Vh$rJpv-zzhLy4g0~#^qdscqKZLj!FCoI}~%nZ>SLl*W^LBm^TSKqMrrHx2Bq%&RbEZy0phZd z`)due8e9m!omhhMuY$(RA&Dm(&&%R+C5e^n^v0xz88Lm+Fa=1XiCH`*pQ6Tx6S2`PP zwwneXIe#T{;45%y28uL3S`@P*}011G)hkiy$xff0Nx^_Z}2qb1(aJE+@Qo{ znugAHWg6a5TiL!;^)3_+iVBL|LiAM4$rpbvJJ2JK2a%junN-bo$M0P+?2m@Py>oJ~XVI*YR~_>)Y3cYipCbck!l!>}RV9Rd(y>FVLCH948Mf zTL;N25iJ@5J~&1UoO(`LUq~ro_>c8I1b!tq0hQ|qYjsT=H0_l1WXYuui6-)*xsJj< z#%K1xG3kq2_B2p){>l{;G51_Wvv(>U&9#dt^CbMP^g(9KLmlM+wF@~bO0=iQ zKp|cKf%7x5M{NOf8b>P&N72|I?*!d6updxAGWf_`DFml%2#&Sve)S*OOFI~ zlo44D4RH|5${w<|HynMk&0PgajmHOWbG9^Obm~g@0IfZ2IyP(&Mk_TLTpA1(U^PGJ z?4(cL2kX-5O_#oVxT08zP4H&KrGyH|)kyGz;Oq-7F06_$RsfFr4eUe-2S6v#sC5;l z{{6y+BJdOD!Rp=?C^I`8S6U5H+|c{4U%a4l-RlT4=u1*_XdZ4aVc_p`$%o#)U_dP0 z4m~93`Rn+k|8_^^80yfP-m53Wr|!3=2&A11#VX$PcgJWca9^1J&?fMi(#&M;WF+$k7f=NBbnG6+yG1!ZxCXWn$3H+UtkGlv@CKq= z!kV1@GuiO&xA!#o7&@SI1yKx91m)~5H3}}J!&-J<({lydovBwo5J|CE@a}*8r zPXKtJ-9%YDLVaOe$d@o=UC$cC>w%$hBCLv&g>Y~WNXOH%+@TR|ksKFD$F#O2{s(Mf zOrl>m#{;vNo}Cxwf2Xc1?z4J?=;r3-6rFUXoVz#Jami-NqYhmf3a@*6-vTAmRy>hseKVGNbP?hO= zU+vxG^Yp(LCN;+E3m=za6!c(jqjdQ{=&K(na|BJSE_V`PEJ7u&H;V7`v^K zvAFM&T0S*fWT1EZX{X+L^gpfBd6qp2mUK+b%f_j~PXa9cV#TZ{(Ep$r+M_t%a)1&?MIt#P5!8GQdpJKI206Fm- zUS``+;|2(;)SxVes!!_LXY%Rx3#3&5cK*dwv;;>ul=Og*%TCg?%Nr)$FyO3H}DxTU*|=XzSjPkQmzIy=MD-$ zo@MXrw!?z@{LjIkJR9CfVOj95W9ZV>Z^#q@1FNvE`GrT))FhaUA)0}yI~wrIG@xKN#yxx~&_sF)r=5t2~xqdR^#4~JH;(|ERH!+)+63O$*68=k7Q?3>;r zzX|VKPv8@~b7`%FrNBtXkmYyUkPGaAvTuRx0%gw$D15I6atoI;^)TufTBG?X?^L7{ zo+#)!`Q<|fFm6g9tJsC}(MX(7El$Hob>3uWs^KsBG#60aACzXkm29Kg^<41_-m zpSRzBx$VE80~C*e8~mgg$R;<3UmR5N(4Hqr$-zo5IgT^_Mc?9PwnqNT4(&_*aPsM| zvr8FOwWQ{QhqQgv=o)6jq8vC{@*ud4BRH7^0pq*V#_Tzv9FWFTS$oe9$)k%ex}4_LDFrBXm8?J=C$H0 zS(uj55FWdx5l&%n7~~bg`x#bltZlDbb}c7;tm!sHpR;7)dhDjzFYL+06h0kb)uc`n)s$$L>{et4! zuxvw!s?`VmOjZ%|Od;hdqu=l5!4O!AG&Xl5>Ix~S_TBeJe~8dm%VLp8vl&bN!4L!B zXpThn?eas1-WL|pc$zfN0)?(FKU8T*<9t!AQgc?|WUo?2c-L>ypYT4@doy3lSN^dFDVN)ov)y zl6Kb!geEexa7sPnjf|_RwR^ftO-TH$ie9ER~wx9$1Dorf=b)RgS*ap}o5S#eI5b&|&*@)#I6| zJrwMJycH>w5vGJU2ME9)S^+oX{Z)ORd%&>U@naS;H}h%oqb@kcknX-_7lsm&8Mq4R zpZ|88ImkntcW@UieG24vhfm;h?%x>`2ll2dR|X^Zt9`um>mF#R)u8#V>^AmJXRFCj z*>&O39xxnfyqA|=royg4hMk3Tx`DMesAMfO0+v_aJwOw*qxgxm7o0eGci6&-sd8aL z!1~Cpm6wWsCQ{zEY4zfQVtVkW(F;o7+*da)dro;0a(o@U#I~W2%!jAIUCExk`+*OR zJbH|?2ZW52$fK0>h*3c9f|-f)K~p(&$j{-f3ymn7`Ht69?Q72`&yvP94}f?~42kyl zSrT$q9SH93O`&X)u9E(t`0Z9v*es!&_c2))@zh&NQJmSFcvb2haqyIbXE-jAC>a^W!!CP& zo${0=rhRt;<0%1(kfJ|Jx8)LdfU&Tr;Y3HfgfXIbS0sF@;PuY8(PNB~ix@&)Q=sEz zbB4{6+rPc^kqxm6s)pV|R}OaMMyZU=*{&dkv6y@on&8Vld6<*3|L;ZM*y;J@!^jaK zqS0pIk50YnpX2oTtcO`rLD+0<2p+x;_L$?~#?YKCWN4CvPNez$3k3=`QPmNQ z<5J^Qg!ACUU(RnJ|F5&<&&I1t45nRGM=3FLiHAQtCwXK1+Q`?ZzdijE{2XOM9LQMY zl<7f-lxJ~uoRYwbv!+D}^;MdzZF(4$n4rydCJNQyX{Fb|bg5CIy_UAp{WsO>PjMPv z3>AidZ*Ad#4E)y@24L+jkyU}K7byJ$SR5u#fQzOMF^3+Qj_2>=qQ<^$cm!2{adzNf zg#V3{9Ey*ktOo7AXvI%I#FL8JD5p@(aPtC0m-gg_A0c+#pB3&pL3d{CddK9R=TD^M{sB8;XA^D32v_VQWysRwkHMty_R}g0{c~YEpOuL~;{rzpD7!u`4VDA1+cpzp4=I3S`G@kyx zqn{%&hfwCMfjP#JyyhYc0?NsfU_1qBW#J|S;Y^Iv*67VK?S@ihd^V!pwqNJHd{#*= z8e8mJ!8O+0C%(^Mv}4UQb@*=f*`@%lJ-0E@7!)y&VW}YYSvAVdbNW6j`gw?Fh2$w! z!A55#nIjJE2QwHAlRF8n4pY7ycGzoR1G7o#H|A-pM(u$-?^;OF&FqoZYgd_HY8$8K z0!vZGRy?%$PN|FW0~f6|^dzGPqpv5O3y&Y{I)1!vwSRT;#<+)gczL6hTWQ|)QO5?+ z9PxWOy`}5YCj|+`XI}D>9vuRL7;1;H(Ok!todGiIWFW5m92y7!EM_waatxKD;vwrc z)X3T2$RixhU5wg+2PYs#-2@+Oe+Wyf0=%94X>ss;-EL`#%SoWDo3r`-zAd>h_j%QS zC{mM=O)bXS0J^w!cXUp3kRiKTV%`wJUPsY)T;KQ`QSOzM5!ibG3NQWZFSkUw+!%H7 z*>25Cc@~q_(nv*Z#Vy8A<;GT*<3WNINb}aT4j$?l>|xa>FabL^bR?@Nk^IUa09||& z9q{l!py#%w7z_5`>6N25ZN9Jbmf$8St)R{H(#~$@-`aaEvyyWnf8b>Ek-XToEfx>6 zKYaD-qq`9&BiA;Pc9u4ts_}emGqv_t6+{tRIPML68klIMGZijDSMJ;%lYpTDu*O3( zH;01t42*fSV3n{C>_N}~krqcW;UDle}T?>idzGM2d3W~PvfBu zzz2@v?Ko7jYA#qGu6mznN!lj_AHmK)M=N2-653F+gFXtB?|TCl6EUI$(6}iq2}d7+ zDZXfW4+m{lgNkH#QnH2Em zjcMtL`%kZ!W*}GpS^(0Ub%d1Mgm!m54+r*J=zYsKD&P>|HkN*v2N7-qIUZ8$Y!CB8cY!`X&28Py9nQ{5c_6X0z2K4*9`#!Dc)(3rPu4U` z!%hT&$D!Za5Q}FMAa4cs_GP+wS2;@J<+V|>VF`E=03I>s0 zy4@N;3x;87R}n5ncNa#T!q&*>jcjJ(ppR|FS$FjmLh124;I1k)qQ%1K5J&7qxEsKD zR*AP}$&?5z9tl0I^OS+Dj~t)DCxYJm!C4H3VlTET07h771PrU4*rdFj6j zmW1urd0S^0EcyN!*g=@`Qrr0%-8y~FX0&SIo6aRE?yP`w?YqVh3rmZ+PG0= zB4;o$0I(>4Q6E%@uc1ZDT`k3P#9N8Z3oa#fNlzAl?VIh~AEF^6}h_MR-aT^I7t^YDMHUsu~K>yJ!{!+Ohl zk$#J?LqtF-U>>FKnhm;yHg3=xjhyDquKP<@{EZmrdGnBm`TOTgn#*;+;tCc129B%(a z8YU?{r?Pz@DJT1TLo30m0QXMhVzKAuJ|0phTBHWAqlPS{r;G7>yDH za$ag*6z+*?yNX#mWg%GeSbysN%SKCvKoO!NQs0V$)7!5&z&1J{280NKLDn9XKz@ZY zHb?K4=mNH87!Og2Fbxevsr`0a20}z4a7NQhJ9+j(?dGj9gNL7e!NuK%%-Cz}LxDvM zyW>*FqMx^_Mn0OxH*%V({h~d1O&VzVJX%^_`F8MGiO1_z=TqOFrZ1rS8rVfGE`cj; z`r0lA|HvznB_lAoYA82g5PqvcyRB zPRb&0NGGh_5%||aE)dyJcxTdmt7$-p7$v+b2#C4y7hKv+<6tk`mE8n=Xt-Q&bA9Rs zyqX>F$gXsXHrj3X8A4d8TJo0d(ZCP(5!bY_SVaAY1&pkQTaE8<&BB?fN+_=VTE zB?bZe%2jSs`#5OlCRw63u&DJR!1H%=X*#g^($XwrTCxQ`$Tuxn&Ga~QF~)|JE$HUm z`0PaAL#dONXVCi9Op4$AD6c!ZQ2dmnLX5BoREEtF`||9R>!y7TLnLr?@5#`$A*SzK zL!ms|{jw8o-e1ro7%}2covr|+=o&cFgY*1kaCIpWir7;81DiH>u#wD(zi&Tl<494+ z9^XO=OEKm`IlO3NRelMiqM&@^_0vRb-f~ohV!$80F5y1a!{Ek5S6U|Gb8r8%$&wgu z5<4sw1lD5*jk_!t9{u=PR2ThK6?6GDxx;|>l@kifdLI34x@aqf2{7PdHUxfqhM4n! zRaC(PoBa^{*{-6u*PzyHDCl^~bLyc+G`CR$^8mL)z#2SA{P;qn-w zJ`RItE*+U@gkFKO%tkqY>8MG_Kg1@%em$+SBrY~im@d{^kXEy)qTlpyzf4?38<3qZNJ*4O>(8HqSqnJx_9H4UN*|!?h!4L{Aop!C#Z?_G8|c z)bKW9NE|t%kd30-`Y9ITXq8glTlQ$R47K@IeTjP%eE<33Pgm8 zwA;`=zs3GUOfAk=(AI!oAqDrZ@wcrXl)vt9rrfQtkrSGKe{ZsOYm#omlGy%8)>b=u zS%{;}a&8<-Rwm(#1C0qgN$2*WUWLNL9K@&Wb9oM5%>qJ)7;_JOfhU^gcT>1}rgb>W}zcimNOuk&JXi0fW0!wg07UTtV*;EvEva^YK10%x z)jbVofoSGI)~7k1`w6is#Re!oy6%OK;mM609qo;*&YHJ9I`_E;J(Zg3>n#E1K>Z-8 ziOnnNJZgUV>;n@jGFT@*SrIC~|BzNo8o`PDViTr`2B^1lEp8cFd7K+TkP_Q-Z}Xhb z+V;fM{}{1v&tC8z6FGZ<4A% zHZ^$p)9kmjW#qw?6XF7*=Yn!i=nC?Y@qW!*8ajGcF+L65GsAV@)^~(_1Mn1Ho%Pw# z^YPsHOldhryaooQM;$3FcbG69exH2u*sp5d;D_Zt|DFQIH~pJHo_Lq(0PQxe;!CkC zs1rK>K8I4&s$1U4O98OeNBwiIrIm_W=!m{dJFc2L*`8h~^N4@3DeT#?3Bl+|=ll<; zhFtur^Em>_2611h9rrkwUtnm0NY^odRm`&#Jf&H&`ua2T#epS7^QKsDJ`SETYc9yR zKx6rEa*QtgUyl~bI!8Px&RDQ}Z?c*hs}u=5IpIH`;Gf5l@NUlTYp}h)vBIlEki5p+ zsUGIs8rXK;YXKx1ZG}%+pe0}Z*mpztmjcN@DgmB};J};xI!kj~0Z-e#PuoR?q}lkL z(NmGS5&1K1FRz0`aNp+AQP;}`+`mvdx?!U*nQI^|XPS*qbQ`>e*-4uP)MD zPZDLDH1;GsOE5dG)h(JM7ZB_4Cy@0{TEF#9b#Y_W^hap-ih4S{txXKhm zE$-)O!&9b`4~y$wWO>iY9aO3k_$e+0Z$d$)1YpK6kdvd`bDKQto-jCNvsWbWg&KsQ z>xGYcstQ4X!*|kRAhp*d?EIgu$8lcsj5<$;g0(fG|AC@CKi>yw_uW$8Pion3QSG3n z%(2y1!Aw8@h`0INSK@#4@hcygzo?4z+^DX(kn7s%l6`l$OPx|?B_cwe+Ss>fRHmPM zWh}|MeRl#~O)GE#L%V$^-j3*e{D&m3(P32nvgy#bjL)I!8GNbmec)KwWLi=&PLs?* z_WI-(X=Id=PFq+&N+_C^rb$6>-H|(23V#q6T2h#~Q(F_P{v&qf6R`Z&dg|g ziOwMDw*M`IAxAbn@6C64jlB5anp9R7IqIZvvxV5-Gc14-f9CRBGWg=aT%jyRBm5>tnsqDx0} zkJEZZ@$jJPTH#nLGSoi=t=G+Mf0eRZ4Ve5W_*mVR*U%_Kt+Vr|`=Q3%7nA3Ux;ho~ z%p9c>THCj0X8-(Iyi{CX$0>A1wH|3t)bGS7hVKcqKk3{b*mLmXZnh!GdGkZ9 z;&3k0sOjl-Cj3vigQjN;lWE!MFK!VFrgrUL)_%zEe*wxrIH98f9oyob3X&rxfMDM{TZa2YWv*_0WAD6=3CMo~KZh;c4P&1ZnjI_HjaD#ofnwLn z*{u#)m&zVgxc+SiuL7$^3=2P!OB0nTkhBhprHYd^rWIsIltyExCBy9MYeX*krPDi) z63kJgZ=lE_hg!>PwI(2QkIM2-6pc@tA5e^azwb>xZZcL^x|+dO%j`DyxoTD;|FA+- z`2CXtO_zd;jWwrhbvUbnq%-%C)9dhmf3stvuR8D z+Uyr&tt7XXjk!B-tB6By1}|6rSST6Zf*n_Dv6&stzba4dkzAU)zEsWQ+dp>0zW7zC zW&GP#jc<*eD%yp&^RGP2KEE(M(pLSjH~e-+ZR1J7o6#DQepc>eDc*u*qx0^IHws^W z(zrG|Q@#VP_HiY!$CoA)UwZUmpZy7+*VG2mqG{!pVmbR15AoO!9Xgs&qjg4yny~fc z&c2>6_2p+DKpH>ehit;*it4?Y@_YJ)wOm*X);oYevzx zFG*Z2a$HhUSdXvT{Xn(1!EVgyM%X_g-v-kubG9Cr3s;bhOpwwI0=L=}uWx zYo+ws?H$Ut(1>a8J2MA@gm$H6Uti-cil^NC{_eoanfr%%@nDuSO`LgG5*9Q7`^=z< zFz{Jj%z6=4p$DU+NOI$);>)$Gr z&N|6%7qDm)jh*k-CyHo>P5sp{&pELff#-Fj-emAgMB(eD!#6cD`pW}Ew`88)>h$tZ zi+*l?#~G^@>Wh!6AFsPfQAvFYtnU>Q%+LywaJ~_|m`(vmYr+c*^=`!3wQPFd)cVvQ z!=+)J7yqe(jVDqUvy)(eyW|74Le{lLCl8@69{~dNb)BrCci^SuX`YZO}*)hC%L-iE}6Io`|=X?9h$~gV^YFy z(L^3l7fSA42^dGZZJ5^?&P`O_=4u&em}BNzCGYxC<8DdsNc@8qj;*Uw*Wc~x>S9Pk z5~OL~*tGeocP!k`cCoAOIZWx3##8=Erg^E!C2?!5y0GaO<<5)bMt-U4XDBvuvj%dw z1a8o+-2`9cDoGMY8@8<)DsI8C~ zsv1(MNQBJUxWKqcON3PBXWm?VIwYHnBX~1JC2pmoHKe0#t`cjLQ#M@At7+OWSASh! zGxNLG0v8nSs+sPN?@D{3_RFZ~l&lKqHeGs%p~f9SPViGN!gw}d>gch8)Z=rG*Rw@Q z(K7I0uxTLA#^IyG)#%qA^#_gq9l2)M-sPY@CdF{+d_NZF-0pt=Epkzd7%Z}gS5xX{ zd+&`1Fyp@q{b*K<`~8a4h2p~r``n0o3gpV3|I)@ivo6ICnH4TPS{Hm`*t`BQ zKl9zBYP#M?Qd`PI$~BNV)tKf=M#?s^w_uY_pAoa2*xy-+55>yrBM&r6kM@^G;^6Ml3#tS{;%M*fT8 z366{m{$u{~oK?IZU{?PKBx9_f@pkDMpkK(sflst6TZr!`PGj#G?kUij%;Tv<^?UxU z(ZyTi)`(o*4`9zG{)eiVKe7S@i;A?0sRy4bhsuMlx>TLgSv-pVzI(MpQK>D(mK zmNNqT7LGc~6{Up!h|RC9PB{Qd-oEu(CY{nLsjS#jd(7`cz_0zWJArj!zEUA$rpX7R z8#Q@M-k$IemVaCA6t0*v(KjZ%TJ|hLI5OM4bz`pCkG~(H^TYXgSc7X@a3Jj()8psv zXup>~>BHS4lT{hUnp5*nxazWZtkiL$8v9L~zI(AX-EeGCos8aB9!n#9u04>G6Ox&Go`3;6HBpDvk z=p!&pC`GEqMj?D$_&2C-?~uXM3jvLadp*wPH8+V%NqxO^=12J()&_aK)gw){;zOCX zqr$~2sh@rhsJk*`jq@3GL9#DzpA}A$u=$bEep*QQmGIZsnun^oua2Er&~eUGJ^fKs z^a3Nb(%aYT+02V~46?MU>`xMR$Wyxm1P=B#(!mc1;_)GUu)5OYJxeQ}|?1z?j?~z*i|M7%H@K`>y@?$&k zH_pAZ(&J%YW6|9cQ-&?Wf=8#0ILCfG@$$THiu&z_qKcH74bn_ebY5lme}>PozXBNIm#AiL*LXSPw-M$w%NEh>743Tu!^A7#aJpGR~WI;4E^(6J~7O*>q?zYLgw%Yh}x4!YsXm|=K>;cY(FUsT-_J|L;^Hs>w zg_YsUE(-&%D&kwUf?Mm{Z?^XT^-TXU+a_^PzX$76i%Hx!MYxkHf-BJI_j&ROD%x46 zoV%()!M}R5^)Zyr77F~zuf$y78XSFp>27tSXq@Bn>W12Gsn!^&5OccW2ti}pc90zObOprZ?Zij>H-A1SnqrxL%PWC5?1u&*ciV0x^a7cF6s z|FUTqXI!<1M3k42^1e$N;kIvNMSY_3JViB1n!O_0ryo`GQTwi+8n&5xI_{!-v+ZY* z&90T5(3A9O7O842^*;I3k+{v0y06V#Kg{aVCSLM;EhK(9ZG5u#Nd8R;{on)s&n5S~ zE{oeRi+b>K*}+#z*Z-ekXhYwph-9rnw)X1t*Ja-R^b}Bfxo-0*#Ope(K0;@8Oosd* zwWrMH@OE~~=%~Bn`j4MI3EJ()o72`UJNH`}Sh^R!keU)dkAZgtarn~4Gkj^DpYDb) z$>pelF2}*{;e0JIWPpHrI&*t!_LH8$aS{lfexV#-e{7X*%dfRt|$BTAO0hJRSo)Vt3eT)UA*PHLx z=AnL%JYY%B9zE2wwRCgw<^JHr>l^C%pptvHT5jiNuBKDY^6@WBt6WOv|7beze=7g~ zk6&lln~buKk-Z{29Nq}Y-t!AYDhx@Oq<2sPm2#Dr6*fF)ANL&)KSKcH$m4Q zdlqENRj>SZGtrdgUwrH-NdyNUbIlZ|3p~{OWeuk^Vhhf*m`?qIhOuxxB>)SIu7W=EZIqmW)KYFcD^Ddl=@Azv|2I;9j(}_ud z1%(uD`IqX4umi0FtI`f?zu4a^GSjixZ|`ca=QP~VEfLcmY?68&!`*l4n?te0xc+7C z!M*@XSs|LRaHVSz@^$qq5qLCva^z2}VS-nc$Dk`tH=ER=zMcg!o4Vog4~h%hi{DkuIvfh63VOrkIf{1|s zEyhBz%oo<4Lu#X&m!X}Ag3niU!RnQTR&i4pGcE9Z7-z`_dWrw7wYg<$W^LcYZ3&yL zJ!3HQj*Ir+jH5c0sV6yreA!i#RjJmBK4B6-C@RGxZ`r#(*AiLhcX|H}h(D4`$T!3p zcEexd1(*(R%ky*bXNCL@CC-2Tyt&!X@Il4V36OG|3ddu7DMQlCuymyg5AB?v60IgB zU&##mh3opf)C7`-DjHGDx$6EZquYvtqf_QYD5f0|L^GO zzaJ{?aM|%(kDQ!(HQ$)}MN^sUe#nlCsuyU`mKWqj%G3%Q4w_)k?%dcLrmS0jW)R<1 zGcFh`{i+NlB!?vRn^?I$hx&GcVn=sFl zXGAB4Y&q5m{82%Lzw7RLh1QODnc3FsX?{RKJwhTE`IOqVd34Sh4R|{vQcGQdef2D| zRK{6a%nw-SoN6h)hGX54NU2*(C3LI4q?sVq} zeIo*Qp!=H0)i4;a^v z`S9Y~UwKC5v67zlZjZ(Ct2ejv{c~sRev>wfUyC8!d9rrusPxq`o&HpB`p-<|M3mh- zkoP*S_5v`Q?;l&^+&O8+Sf`P9(67L@e^B9$S!jT;+qGA4{40WGM16bkHHa7|OHh@= z7rkwd>=i9G2Beoc)QbcUy!ZqF=C{O-137dO=b=G&JCK=!`LxVC>6XA|Cy~|-LB?cKQS{j zSNN>LS#9~gWhx8TchbyTB=d`i6oIbloD&@c)h2}bB%cdt6>&E6!yO&%!^W+7h9Eyt_-((9nP{L^n7kdWmfe=w17 zr*p&M8k~*bnU{*FJ@I}>%e~Vfi2N$(Q=McDu^OCYf>jZMgCF0bC=^LpRiu(ZMQ-#s zrqW&x{luix7A9&6nbKcMv0=^~_At__+}^8%)F`kphVnB+oa5b)a*O;6O|cd;;RcQ0 z=z;r=c9#}Hz@SR}YM3kwIDwXyLyB2^eKtvfy;voGp!yhkcTb9+0g?+D62O{h6kiO! zE&br@g0HPMg#2TKq&)^^%yZ`>^O#S~p-^AKu95Vos&hxP4OR$n<0UkJ<1g)RHV+p+ zsi2{U9|eotLQhFJ6A_O6fDE+1uMF_*4wjU>t;kIK)A8V(yAe@Jt(1CHo9C=M>G z9Je{CLOg?a{#LrU}?Irv>DR^;(V-x_wv#UynpojKLpSes4 z_N_M6tYkI&nC6q4H}dd~V?^>TjZ-ysN=qn#_P{NyH#x#c;=UId;sx-87R3r-Ara^f zb72#R4S5Z{@sfky7V{Q9s#_R`t_k&4tL4`aFw6JhKUq`(BW~^+sd^dW z{Aw5$16R*pXhk*C(MA0gpr=R~C)RBIVl8AMVhqXvUau?p?5bIqG^HjBO7IZ?C6-g) zgNA7n`7()G^CQ{QaF3rH^TF%;4u5LW@a3aEs>u+nGt~UU;YFZYLC9BkIF@;}^IRCV zyVZy%QL>y*zO+4i_THl|_)}Cx;eS>9A4Q4YD^Oq4V@HZU$-$(%@ z>?nEtvcd_FN5{hPpNl?Aw4ulQb$AlK5d~5lC;gBtIK+PkQ4gRRT3mQ8ybc>kMgAdy zWEXNRwZ>`SCRl40e!zO$39y_qL*$bweV4ILeCpyCE?q|a`>|6@*u>`to-g`~qVu#i ztnY0P`jPAvO=Q_A9;`Y{Ir-^wfKOcw7^0y!Orwn$rc~Z#-J0Kdu<@D8Z*})s!%Ijp zNxzFngg-EACl~OOP)uHjC6aakzZS2(9}N96_C4p)yJ>c<-;Qdb_hQd`imT9`FVp*F z5Tg|vk?IOHkN;Z9)7@#X{6~J_!K}vrX93FK0Q)V7ohFME3yST{fum>-uuDwgfxOBh z2{D8S%qlwcytoBeX!epox|A@y11zZ%utgr71>%x0A0%Jj$GR_FZKs^8P#VyHam*+G zBV<@j2tfmR$wMyHC<%h;^SQt?2)!fl4%#cbn^^+#^^$S~Tjs&WB~bjJC=JfIHX44I znJrhkrB;4yndStvkU2e{NV8L)$Ko96fn%-=QTo_OB;?=JoUS$a(XV(mj0uqDXDM#Q zsgV-dX9d{_tZBpWrQ|m}MnMs`j)(>Am7AU%)iBaN>7JAUSehpLzXTI&N*Al5Oy1s!Dr zjURAe2Qf^>WW@MMwaVnq1}G^d_GqPC?8NZ>uUcNJaC1IzXY=>-Ml}D0t?t&wpV(a@ z+{VH1e<9iR%h}g<3EThf=vB4|=2r#X@)ksW{T(dJUJ=xNJ|c1s0pR$5A;579moP9|gzp*4T~f%eW4wFY_H^bosOUb-2+He++M5f=T0w!mJwWGd)mkE|@9 zBMWHW*#LGw1|i#W3PjD8j|?K~&~siyqs{*)c|ddG^HiEqTKylak&{!qmud>|Od*Qo zNkzoD&dlvd+&ro!Bp#!tI-jUGG2SZkh)N=yxH7vb4bo;&BjQ0SNdT*j{StUS^u0lx z%w&9f=7bpnvH>cE{D=aw%;%6#B+$`EzUV|PAQ~8w`9?VEDg5U)%>xCaK_Ne6#c_fu zlm#LFrh=0iwbb4OA^nrl#Lcy%y4(eScuqq4O5toqC)jWEPmR)~`dRZC@%+=fb3-+$ zJc4{1Tq;MSjqiT%)LZERB5d!Xhi2x>&%^sxPqO5T?e+|~k1v-bi=p~D<3TZ$EY_d; z1GN~XM!S)BFALa94zkER;UNbFk(xR88G(l^4LQI18@#^K0(M8i#-H?=`-uq5t!-%L zzU19Igy3Ua%Ws~&?y0Kk*Y1r9R(hHnG1{ahe2eBq>-PK>#*A+E_~w&IS?!%%syP7q zuK?`6nikY5;Cc&HNERCe`>a5WwSN}fY&dgAio6Y&1I9tU0@6Ud646!>hy`+c>9EFt z8D&ckfdB1

L|DLh0i6${yFiFXulW?%2RMFZNE?&md(C02eZy?xp8#gnEEV)WHw$ z_~k$C?fwxdn=~quk6@1iGECuw`&=W2et?%!_t#opAi0@q2FW^l$HEg59O~?JT>O|5 z%XdW*9LQpALa%iZtC)Zh?kOmFj3m8Ba+kRXeWQT7PK5MnLbsNY5rX+?^*N*w5_JLA zRRGO1mYB;c&Jk)HSZBobbr2^~u;YNf=eZ>usSN%}{ZP;8J@;!_)2r(pWOE}lE}wtP zV1gI+GXLpH7+^!zkBclzIc{!|}Y_i8(mEU!2eeI%>y|`@I&wkkd%903!xxKj2V#589hdcG4V4LvZ zC7pvx<|Ub%#1;ve(Z5)?I#HeUk!L{4)(9?~7;DWbFtvfUy8hO*2PiwOHI#FeFxftLFjnp}GxfBIM4v@7H~~_}>fId++1ahGQ+K2x1T<6Te(%eb9L& z4boCES-otJG1U>rBu?3B-?*;Sll4r7HhO(v7L+LN-+;DC2F!s@kYVZr-fq;Usq% zPaz-l8>VR*D3L-LO{(VnAuR{YUp7>eLf5E>lYf$YqF)0{&|TF~YE%^8zF+_&I7{ls zP&6j7+Ozf0*bByNU_q8SR~&OnS2n#`UvL2T&w+#$Y|$x+!u~)sqgun?Id2vP3z2}g zUVAr9k22;w0Om9zr7E8ud$T`4eqTFp%gnzP?#f+Q@Sx-ZaRG5&COd-bv3)LmtLo_6 z|0*Ruk{rQ79|n0Wp1Ts`SEB5 zoyKt08s8?4DdPyYm{4v7wBga7d?{ZoaMh1}0EYs8x?fEY+a0mFHZf4VhW$SEx;ap- z2wWHR>m+WtgeWJ_Aq$HUufV(J5U%;}xAENNoV#pb95ng+wf=aOMu7T|+uKjgh_DP< z;p6h7N#THa35C4P`myem&NsRof6j3tnF^{E{g%g24H0|(B&%|i-KISslN`2mqAVh< zI^uR&f2Qf2rbxCh@tZ6NjhbfvM}sH);$ANzMMs~HeO>;9C9ygPfP?MPft!c>>NME7 zkHkh6aG&H}^=PaTf_pS3SOD2Flg7=XrYX*i4tSn6kpd#=&PdTm|)~0zm!x22c2a2Ej z$FXg5$B}$x|E(Ts%`zo@gWK=6WG5rATLp~Hj6GC!NaZ*BeLA&?mgm|d9fls;D4M#&74A}Gm_?V_)(bjauvtUFBqklubg*;k(R-wR9t z6C}{!X;Ew{KPz_4^A%K~eX!?Eoy3)3bV(%JzKDf5P@zUN7neJ8kIg;kl>p1hGRfNV z)u|LJXi}shy-Y$8aC%Uj_hP1pOz1zrp949S5*7#vY`Zxj<;V_h1l|}aHAUS*lK+M7 zK9SqvW`}XfP=JoWRqS&ZAAMZB<>)(u?(F-3RfHBk_EYln20w7Q?x+Jt#iB0=6_Gg8 z$UT;^Ud;I&RIJOzpy&?pjNL@j-!0vYYCs*LPVQ4aKkM$P%63 z2@VLqt_QBRET)!79bSKL-Xa~o zb)5F)3d}Xs0!NH|G`qO>@#h=A!ROx{lrE{dC?vDqN+>`$^c0bixmgF=b<2D#!7rj~ zXdB^;T7EXQ25I&tSBg}t=g1GNx$XE6cV6(-DlreqH!C$v2^k9DqI9=@nZK)6K?(lH zJP33*-5~Q@RNq6Wtwt|GYU#;2$WX{`nrkSQ-#nZ{q&s9>XUqt4H~kV#$o&fnXEo(9 zQtTj4sIYYDGB+=T|4g$afe{$uF3`<*Pf^v8}8eNEg56m7VdO1*M3T z%W==q)PgxMw=q8*RlNkXtYBJG{^gPHUJn5DDN=3^8gjpQs|ESJlB5VsLPkZ)@ZGQ$ zj3vH68nXR^(&`=2p#mdMx&3Ds?Z}B2x5D0noWJ;>jS5f-AFXjn7&6Lg8=gOn4Gfist@vZusLE$i(`!hQOBwNeQNSg)Z?>u ziHzWws!g02oXAWaQ?LVA**XxB_BOgdbK zUF}Lig_@eXb4d$~g+zN9E-~#a`IjPvaLBSpR)QG~-hh~7M+aG+NA3!AZ| zMW&9u#R|`4LWmNdRub-FHbaKf>Bgm?ne*ZO0kv+M+9ty{Az)RzH(VuF6id0f^XHs$d7E$QSO89L*^7(5YH0L z>4%JvX^?Jfiqmn>D;Uw(W1LFb@SH(O2cff$2ljpzTk~zW${KV%0d{uARkdP(+i<^y zNB6P-b2m53-NQw}l*^<1Mq$kL48t9TyoaxZx~*`1w>YUnx;VWa&rJAz9&WC54m&*cM0g*zeKNYI?jh8zi#XU zYii1>Sgo}P&dtHUwMvk->+7*y#}+4HicXuq&{3urA*vCjq}VghXmaE%SP_RU_y_#G z6(s_*V*r2u(}V*!P_Bj;cv$mv#q7ZIC67_SK%gOglJm;E3A9)mqEuq9HR^0BcF`L@ ze0)qx@e;K#9lCZ>bKCPmbHlOj>eK`8n($~E6g_a18_-pMzwNo}s&WY&s?`una!d<| zg-0qL&y>bb5=4(Lr7E}QRQt0j`I*fYpPm_)a1MpTyV0Ymu#W$X{E!@I;>c{%y1=Uk z#)7ZLRcI1ypxzV=Y(1afXpw5a;h^)%fk!sXsxF6T<;3*kCOC9ms;1s zDx6V4%j=9nZcp-8eDM3CN?^W!!a0;A`16?f&pzfq5wAgc@u2EG876DydxXO0R;M}c^5ul@h9!Szbb6C3f60P+ z=*;Ul(Ma|A=-vBuOLhMQ{sC+v0BT0$KU!RVHlS_OImnN{_m+wcMK(?gOyg6|)XuRn zK|qOEs3%R>Z>{s}>G4x{J3yQknKwmwT1xq;YE?vjoW9TdWA|>G%`NJgOvfWi3lq+5 znb>#9-G6jF`r}_{KyjiItpA{JIIs_d=#r3(GD7WGp!9~wR={l9@|K#ToF8+Zj|9@E z#4OsTAXro(=G_2D@q+GsK$p0TpL1$4nR@hRK1I58;+ScI7&1RlWE^?np-y{2#)xm^gUxQ8J1N?Pr1Y^s($?c`J4 zE;hPpQcF>No;>R4^_6J>usKIfL1+tO2^?J_lZ43BP21|jim zRs*nnOvj4|$v63DFJ~oR6XNzyUb`WU!*7@|e!GlE&q3HQ#F)KN^`(=Ms!X77@2oGp zKSb!#{h0d=F^6n{OK*-n3Zwl75EhUchqvgDvPGkz7qYMU=oO#{T&c#CUB_Ij96r$0 z@)5u}b)~{oyoUfPs1U~RNJq-6vH+Vhu*vcs4F9<&6x5%ycT zwE2-Q(zTSW{IGf(+s#o56>Ylx#JhWe-yMO2QtcXUpiziO1<}7q^4>`DUBL= z1+y3nD+>*HoZA4g6ICNI%BzOorqJEB)*^nR`z688SvSl;CE3WDlD3(2q9u(Uc&wWR z%#cQZSEnZ`lW>D)k)gXoJZRz9-ADB$i1lmy#QhtJT*id&>T)rT3KRDW1u6hwxDOB*43kFXmS7r>tE zdcLI@^q250th63UKMjdujPwJLU)QY_aQS0$471Bz&0NpVS&VRs#%(O5^$8a?3Taa{tr0;vTW)5&Jt;$oL|0&U?B}Bq`GyCG<2N2w_4Up z#CV;i9AHYHa%vW89aWjRzY69pYu(UT8n5=1C^ z6)O8&&FI(n1sVaJ8V6Q#me+%RlYQt)p6A9MY9Qk{Jru?pA{v2EjkW}=9^Tx;Ok_9e zQ+Jp{gvlZKNcGqCGi9U?WYgn3Ol%;GUgaVb-;-{b)jkEvSv7xOuvOgHyqtc9o_D9` zjmYNIks&h`=JXU6CVefn`&eX=%)}Pz3tSgq97A`1V4}NoWJS4Z9xvG?6+nuz{L7-Y z2^V5T@pD4-=)e$Qn`|AdW&~}EV58(rSFL!eOLWasAawffZ6pN`eIrD>3SD5$hU^98 zvLPu75Nkyp6TD(9(D`35?9w=#pK;_pW&E9QlEeFGy(QxEvoED)j=Lvmz}zlLpPCz? zdbUnwB;=Pd`PZmZ?k`05?Z&Bx?L33Y9!R;>;T}ea$(|bOv^=VS0v~QsAa=&VeIURY z7TU5=H_uJ`V#U98in;EaOb4J7vE(YnY}xMXVy&)&xI2Mnt68E@4CkL zDjXd69S9CS2nQ`Z-WDd9NLfECrFXU%Ajoh75{A)`(NFXCs$ngGe(PR8S}6*#GLyik zk8kKdrQY86vL)*eqULARnjt|o<8=3!!o*mk+nmH$J6i4Pu#i$<@uOV5?53PDVv$)8 zdCS0J%+4cUcTxvmT0c&_q3X=lhok$fR%D{S-(DQau!($`tzO1uoD;)4iVVIx?S0K$ z3eD$JiqpEYU%WqB9^{^+6?M#?v82}qm<$z^!|Sqjgn-1wt9#i!DDP>(4>&(HHWIUE zDIj#R(GQ{aZzz77`?qZ}U@teE>&rw92|b7lh*k}^Hec+6w{jq06{1g=N^5stNO+dDPv zGzJ}uljQH2jJ>}-kJ&(9)Za-@q zeDiN1MyAbs&FzHa18pRaX4qKyZ1t_HAf@;&M5=+tL;eI)Ygu`ND+qJ^om;x;IZG>3 zH}KMi&NEoO)u+|s`HSP0V=*rt!dlWbS)Ises^()dxsTs(&(N@;Wy**4lY4WHC_1*ItM@)hfS>aq6btxNn1oDzhBqwbJe z$yFHrDJ!TSAjS`ul-qhevFi!Q`N8mwZjmnb+(E^O7g&L5DgwqZdKH|p9fbS=Kn)p^ zg&DD55lq@vK7am9i26Eb=!JXr;>Bm!9a=g!EEi^Hd*M5c^<(egUe4< z$32&5@Jq75->>s0^?S8`40HU~XcyFaPuzX}1k|IcJczbK4dI~X_*W4KRUoX+eIh5k z@j&&%SHy>mT9l#YX=Pe7BJy-+<^k`Rc~>y2jSkV#NN<| zblQmjHe)!r8`d+}=?GpQ$`-$?tods#@@Fifs%z14(cu*)nRR1`>qUrQ-Oq{s2nE+R zn1z%Yzf}xj+6KPzz`MS9Ela`m_zyRRKlm@_l0rX^2Gzg z_br>ra%Sfry-n}llCwI+zuU3=Gu(TU+QM|XHb0~NjUwbt0PRY1pu?N6f#Kn#cK?Nm zT^0c+PWcHr!Y--9=evuVwuJ21xRcW$DXo@ROYY0oVsDaE#yOZrxqIaFmFH-Kl?h%- zbzb`?s4=0h;mBZ*yRt@MPmD|;NZ`!tAMWQ(ON$!I%$?fpq;S`I9e?FgJ>~czHEVwz zI3gY1STY;6os3*N*ea(YJNrUe`l!i&kXh`pf5U<5!ouCH+VTPT7O|fZNc>j94w7g! z2muywIax64K5^-j`?ZR&ZI1ZVl%D7}91(xzNzIJ=+N~w&<~WfV*wQ16hp5Cipsf=f z^AvD2`^NT^8yLm_VLkB`($pRp$^-mHw@ye*{3h?a zO%Atuvh5Sv{6|G8^Rj)JEH1L&ESsVYyc~4j)gXvEUAnSLw{5GkhPzLE{Y>}!-R`!@ zA2L>i?wW{u;d5@gABsHP0pE-|Uemms`}}=gx|aL_oHXMT(UKdCD6`|Q3{{^xW0VyOalcQm4{F0j_Vnl&CW%D>HLhjrduBnIK-ms-e| z9(%C-#|MzuplL4TXLMuBQf+n(>Dq8+Z9<&(D~bYP0+1Gc5(wgntk>ZtPcz>mQOW$K z+8g16d3#R5&e$aP%Au#3E7rsh(h8*QHOUvy-z1qP5`ZwIG<}i0;_^zZ*K_wD=Uavv zT!T9!Jne>e+)QO;jN87P;}VR5^zW7gsI7akY*PmXwL`9|52GSUBGcK9A_FnY_V2vRVJY2os&MrCc;L*|5alVi) z8CjZ*Q3;AALEJIRu5NsQ{X|vA(J4r3Wq@#^mxT>oDhDpb&CL&PzMc1@YTl>xepGbE zOenO>zg#5Y+THh+yd?VpeWHfXXscYfZ);st!?9tKTO>L$%Ubqwm%xNgO&w?IMHvq0$P0~Pa-P|` z&sEdN*=BCkoGY+hOIG5wopR*3&mY3ac4R4yG3n`bNJ*#>sm}5V$j>=v{qQvI!jyll8xxWU$M3V{;n6 zjbX=4y^8d0hj=o8jx-(&rNGm>pB}!=n0g#v_fu7jv4CPvriI&_Fag5py$tMt*ki zz=NNzE-wZcQ167I(rXR>{a)4L5;G&OA53Eqk)kbRS`38dv5XxYw+Entm*3|SDPLWC zNZ1noQg&%TGC109X-oMwW^5>atY1-t(0ntrYUre3JAGR20CuBpd7y}qc0+Y)&e)%mu{$)?$ruF*Slq9vp@Or z`VcJ!OrE%^V5T7ZcPahZ3*K#xsuVLk<3M>=L^^rV0R1k{K~{B6Y$2^t$856y>!HcS zHOM0=o7cpeCc@{p{o*9Jl}8gcY=tMf7q$Ol!rV0fnZ4`gwm&d5^lT$R09!=5Pv*AP z*(_Uh>3~OgdzSmxG~ZKERw(k-fGIp#HiF3gut37+j@a?Vdu7qlc#>yNP73PBt3GB=BVJL|+jO3P0dVl2hHS^iY%qrcQ0=$p?D} z+-Fw`kM2FDWGOyL8eZCUZ-yw!d$N?nmcJ3J@7;2lfD}{f-S_~l?_J3a1VtYF?3lf- zaLaC^Gu~i1LvgS5hPTjMEM#_ zJQuDi8gA-HnVGxeoT0gY(Q}8`;!U(HnQ-uDQNP0n)tB%jeWSKaaQyhP(1jQ@fMx;8 zz6(L5EWldGSTY`RC{ST&`MG1g=^AMyjH>Qby!H5LIP;>`(#&8D!JzF?>Q#LU{YXcy zufx8KB-ekCTL@-a1J#N9H~5uKZlJrWd5(zc1^roPE7yXe+Nl(82M(_%UiMT~Inh5! zrFdbDfLtLFjVcbmJaCDqwP!?LMOhRdc&>5p(QSa$Qq9CDFO4h{`jVv?jhsfM@IB9e zZfa>%=)0j{c#+=NbdlvuTSk6IfUwB9Qx^=5G=Em{Es#;lRVcxFo-kN?G=7`YL|o4J zRU0%@drYZ0A5q|3JtJ`YBx9BQb_j_V>nv9m>zb!;RGAO(Y=CI^$#d0`$~#caQwz|! zn6EG1$CcK7%I73(&L*0^b>_j2^FtKxM`h7s&7j{j>}iV;_C(Bx3Kt;W5T3K@6?uh9 zi-S#n@^0t24ly!0vF5o81t8w5yA9VM?QORh0d^APU$k^*7B2;Y0g$9j)m-s-Rm&Yh z&>f{$sA5a0n||W{_wuIdQA)=F{S|lU#Y{t;lhU7n3rEiYE^`Q}vd?Q2jB;|bNgDri z&vGHs#Hd@AE-IXdZM&4dIb?>)MwePH9U^58?yI5zmPBaCuu8uoi>1CNEZkssLMOwX#ORZ*~i$^n`VgQ`-W8mv` zdO*!?=*_T2Wu6;=@`=~@UF~K0z!Ts%^qJfd$$Mk-daO1~Z(dlL_Dd-b)K6_Q=HT~H zL~G@CRrd?Q8E5FlO3l9pf(lZZ`Ju4a!_3O1wW?%^kwde}q~3e`Uk8SYgGoEX7d>WACB6&`6cbK=z;7-uA^JPO(L*Tubn z1W-I;wyeO!+!0mM1yZBv$)!rb9~bO2g#v^hr-r`2xCE(pbG7cXQJ@N=xF-zX&|lM} zB+DCNO4OvT`3B57NZ#JngS@*-bg6vlwe^PR@E>CP(l!%Z5yI#$sa?bWW3nciX`Q9* zu^M2vvd8H3pjNw#*e*&?5GuW{darX4y>WeKUP4N#E-L&WnG85js{g`&P*D6)5K-vf zRH`%95xOa))Br`N2n7|vbVz^X-(V2ZZ%}o4*=njmOEKAvwgA*u>eP}PFBJU|SHA>O_%k>E; z4CqtkT3#%)sC%+TzO&@4dT_)2*3TY{l99QWM5?yNeWfbH*N)nUqY_EM44MZq$+W~$If}xyvq86})<@v$ zZvJj{WQM3T@;#zVEb5|d_Rs9xT;Y6eF+p$B_mIdGP4<=|01ts7H~~=slFMApdOVZ& zStmVmQB6GDX;cb3LaDEzg*{6I`HvTNq^>B7?F6;@t0r>1C*JJ){2S(rV7EwHiK)%4 z?U{;vZkgdQ^+rc>r?F#a%9? zrx3!K1<3iG53bNGy(!0Q#Zcp7^1W2VuQV7h`Z-aQ5EJ&mgjBl%C@PZt1z;etp$ zeF49dLi~28?PMDtyjBMJu~4a>j7&%b&NwOTYovq87lh|-8rIM@l<%^59PNKDIF=Ll z3JCAxG|i_@d8QSS=iC+d5jXJs7>tZ|WLyPWXn?mEURe?e>vQyTNQ=WUrjginnf&Lt zgH>wcbU_83o+hy`nnY~gdSuS};PQfGdEwQo#mOX098o4%w!_oq-nfl#n%wsy!3mxB z<~emyhd`5~r zMdwi|dH2!|NGyl|;pO%qV32@5C6q zLZ9H?N$5L9FO^CD2LwmM`7d3F#LNqq-`sRZ>G}!+5>j95G3iC*q&~EV*Ul+?wLxH9 zLdQ`1aaytR@wN^hIekN`x}9#B;gq6RvxRQunh$Wt5B-`%su>XP=oE#5$Yr=A#eXo( zh2ic+9#hWCHX}w(|GYafhva{Ledg|X1r)kbAmWH89Oqe7sK;2wiICIQB=jmz&-0yA zSoDjfDV#(gC+X9)Esl9e+jv1gwu7x%x{nw}4@8o%pmr2amtnobdDeSd0*^T=A;EZk z8A7nof}^kD*{K`*OyrNMo`+YN+uu*P2X{x_$IR0KVr9?&>s?|K=l0r)OR-9hFrUAx_U1a?EIrN4SqP&zF_=Lzy{7=4J!p&meIytf zUGXn0M2iB|M_zVI8RZJxP$MrK=m6HLKr`5=+wXh!M2)16rVpfDyvso%&N*p_fFgk2y?iE3InVK&@rw8uT}AIaB$sOP?LSa`D-a=+zutW>|j z?04PNC=FSc%83u#S{G|}KPLB*WFA0i5OGt|p<3>Jma#R0+$^B-5Bk0;_UIM@9 zPaTrLTSTHBC(tY}-&qy%-_L17;G~EBMs`9Zp0R$ofDVFvLKmpt+RwAJ0KsmTNaC)n z9{68ev1?-+8>T= zU|DIpJ-H2C>4HF)2R&wOy&&QDpMa^TBwnRQaJvfky<&4flVUvwFeVC8V<}mq)hk}E z5p)%_G@f_~sEW$;^9@ih#n4-X^h^zx04W>5AE`oNSl#{;Y!UR%waNDqXisz;IVv_M z?H}=_@X+HzYLY1?oa4qNNhNSdrz&=$puO(<@%+Ctj0eR1I@mmUEktn-F2YGX5LR6= z!c??nR_{7qgGE=g&h>bGQ!-Oy9I3Q?uxg2UkMPsrO&K6chjCW`Z1`FjLDj7o9n9@l zG{45BT~p|y%s}j#aZhp4Q^H2Sx%`-#=eAhEzLJk| zV^#n#=;N?;<;DTP9Ij~H?8ouCqm&_n+8}|Z>oqC3%xG|Vo?Gy}M5kCKFm6uBs>ATQ zzZq8?EssP@_lq9Ji^3eB!%zA!L7kHfvM} zdA#j^YcBkc)(&XgbL>$~hDY*8e7?f(yrdwh5&7?I6WheDurx~ij8JoKnJDIdeoG@~ zVwt34fDL@wb~P8w$Y+tZh(rMQu3PP3xNya5;Ux)Ul2Cl<8`FOA|vYuf7I@e@6 z=GBpBdR#Xfu({ z_Th_(Q8Mup)&>T9@$wP4#g+$Sr+{t?SLes3(aL{;$PycMZO3kY@Z@&m&b0cnd$} zNw|DMahlQ}^`M>tAic(ts?0;91#fQ|W+Dsc0uc3AX$Bn&B;CJ$_VCCN(I_;1yq}s? z{|IK9?4RngDtI^Vs*%NkRbhY_k^=PCjejK!Upe9Tzi$P;qPuz2-X8HEfOJ;tGe*)4 zw!43T)V%~CNK}RLg%>*;1;{`=E*2$0@V3~>RibDc`?3n~Z!$gpZhB z0_j}3_&cZwKbKnl5KvsK0_Y$G@&F?6#CHuj|3}kVctzEPYkZ%XVP@!&ZiY??rIZ>a z6%-T|B?JKhrIi+#LyLf*bO@s$0v0Mr!ys6EB4L2!07`eq#O1EL?muwWUhC}j#(v-D z_h9@#e)i;yebR8WIB>P7{aEN1e=q9Oc`skaSTZZTN86-j(ikNk?IqXQ0l38C%5VRR z_gY`?5ftJ3q1u(R73PkSJMPq)^1m`{b|O%xORsm?L~C9GIsAu4lv@uQ>aqfp^$>y3 z@=rz99gy!S@%}SR`WCEyUrqYda0W~H4#Y5@@RBgu)L@p13CByf)?OTW}J1N&xoQBUbpa?@nEN+gyYJOQpBNLxWI=wmgti6UgxRZs=7naR*)ur=6HW^w<=?*VYfGPAsH$ zTq|0OvhYHm-d{x}KaL;LE?sHoW(&dd1CPJL$MuoC2%S9-HV}Y#NaMx|q(r2AF+Z>W zLDp;kja>|Mvmaxfva0hBSX+4^(NHNXTta-iW~@g485$XR`e;NeYw>4Cq`uS_d_atx zGJv58eTwo&bMbLG7yNPjHAG3eLQ?q2m|%e)_q`iMkxEQ{!NmfGkxZAOROG27=R$p$010@t?*En3Qw!Rm4*v9Prm= z;>^RQ0|E>8(}B=9QG?|adUqlG9)J(M5a&8UPvO77K}+e*`zP94$saRUPpM#0*Z$n_ znLch+TEQ-=qq1`&`D03`9HM+^I<(;Ex9`fJ=VAByG68ewwa`eYR*Y509Tn@HvQ`5m z^aZgHzy~fu9GqW(IeGaBTP$03w1u@2O8TYNgFc)Bq0?%dg-2JPqU(6u8d>%A6Qo(s z)e{Xw-Sv+$k%`m0jJAT@wY8dYu9E9a1{s|1NIMtI5BXmwpr% zFYT%3svKQcofo)qO6FhN#Zw%LdJ;)!&qbW~t{(fy5p-0Nu4g<~4_@_KD=}%QL~f-t zL)}hk3=`_<^$QyKs{IT6?6N3@efe--K{?8~)Vt?)c{=KE1KebaOAabJss9d>AhyuK z|G}B`0eVL@PjrhjRQC9)d4ar`n`FO#0FK|Hi1ynosiIH(hbcsWru(0wibvyj236ev z{RlIKChm^*N8-Q>a1>48D^X991C$Spv*?ZU+CZ;P6aqgd9M7iuO# z(RcTIO=uK6{K8n&2=&eIbY(C>C}YJ4o%7K<>1_t~!Pv{negroE z-7Qgn#mH z_*kP*bT}HrHO2Q5zmUxmczBVk{N-*7vLgBmA`x9@;BgWbc)J9zHu4IS-uwT^R|D~f zU^y_Ck;i!Q9`lL|KMBXWJ-^aSRNEUnwKL-QWIeBw9x7PKWBtK^cXRkw_w}edVQx1G zaBR_Ln+d(N`ZUdd<`=@8E&3;ixjSMr$jB8oH_UzBaj$OW?b9d}u*wX-e{I&FfQ4Lq)GZzR9?p)#J`)u}JQ**9ewbqEJY3bz!BFK()y2lmRl%csR* zAI|LKr<8ylE7m5%;Tk&P=~j7b6%(EE_yqRswIjJ|8vj@fI-f;z*dM9hQ}yW|EYNvt z+&q+&fKqYlyTZ=Rl*kHw6|#S$NBV+YJQ$SL&j0!4Qy|?K;?0>5mpA?=Xzpi)O&ONl z^xvwy2qCCrZdW65jA*=Mg+n_13HKF!Re)PK9)Ju_D`88s+kU;$T^uef09FulJ6Ea| zP!#;KOux)Y{O~I&&7|i)T-)tR?f#l88ct|FgAU=@k^V^wR_%8rQ9akdCEtZ3QiQ{Z zKqFp)Ln{_oPrqwg>_8FFgqX30TAMT}Tg7CLw{>gVIrAVlr>5)arq|_nx;lDOH`#Hi z*RIdLLEgm?bo@zbSqQSce@hfI*7?vw#(r0VKGFAyq_grCrzq5tS~hX2Qx~_wUlI_d ze2M71r>$wVbMuu`M1}40{6zCd62NN$mYrnD2C2`%pD(kg^ZTgJ(pw)aFz$Rpd9z_D z4diRLkw|)jVK!`U&rJ8;Qqgh}pzfD)O$%P=Kiuov`Al8Zy0(5}z?_<2y4l_NAG=DA z>4{#j_tQ-30T4htYg#b_?LKC;ZZpJP3D^RSRh}N@IQ3fUOvtNu8^jlHtk#cN&dR@8 zHW8Me)mPk~ee&!c_(xnZz59*v!s*UrF&QJ@AuZwgfxp)6iMTnVGZ4Da%U-S+;`XH- zMd)0azjzKRG>nD>KyCo}2z%AU6~Zy3b+Ht(Cr^vu!=yKGpS+kx3rGsMyyo|t^d1G? zpBf(HsDKy8t?M7XVP_onZ+b_{Aq>wy-bj6^%?)}p!1>8@M$cSgvJKa`WnV+Lr zIuEbyn ztRw?;S!U!|f2HtN{Q`wAE4c*V=BHpnj}XL!5QqN_f4WwLUA8$y;XSAe;Vg4bt1E{n z3+=I?BfdnDx3a)f+zm~UUW>M4249y1dF55p??NxdddwlOc1$Vz(|dSfBZP)tBvN|w zf$K4$IfmgII_LBf3sOyNaGqGozk`GB+Ljs_k`pnrC~4dZPLAo*NpJ>10BRm+xa3X& zGq6wHe@UzH%k}#96FFGG!wWiqa*h!v?0_V1x!k@j)r)w+rAvCjPcC?BaY;pN;4Uke zOE5=DI{+C>syU)A_HxSupAL1!P|7_sosswc)h(Z-768=nzF93^2@j3|1LyWN7V9Pg zGLEf0qJDR|%bPET4Z!>R9n2J!g?bSfN4B3}&ddXLf@JOSCYxZqE4ZiLXYi40?3EJY zp4C2*ml0Ak=JGI6o`sw8ltV4%nyDK|{d0AI6<|X%k*#dta9^tpv z@j2Xic<=IpYhP>s`&QZ0RB5r#K}xDmVd1poV}^0zj}cXPT-W8tW+IdReis?8%#Ji( zc9^G3Vb4|%J#&4ZTL!jw27@+%v3xA#Zrwt31SN*i_8sw)TVPi+ztuJOWj)Hd5R92l z17}%P0RK|X~SHsFjzje0ZVg({Vp(zj7R%SQZOoFjk1Bm`S~bDLb^oSyRD12aDE z^X=d7fsY(&VhhX;w+@Wb276amh3y$7J7b)}5DHpwNIDBIUtlI^6Y}@h={Q{oLi$@# zkVsIBV^nb953}sZCnR%7YiZe;cv(KP?Hewhp?PbPbCYkgI~Q$3i1l)I;9HKc=EBRW zHHMk0-@neBpqzynW-AoUm;Udein3+rZ)-$dT+g!VV*V3D<*pX5`ph9N;h(kENYZga zGAr*bo^`2U)NlZe`2MRVxM4#eFoiHgI( zvi1$|6|62xR4 z0lUihADuY^P+bI@bLqb0s7#%7VvqOy)GU6bW5X*;k07*nVn}r$;5IZ&9c-%^ae8LUiVRrSWX{^b9)1AAAL9@C|4%mkAs~{4Pd7bR9BO|U=wa{8o}6G_~#i2@HvPKqVQtqagpi$4l5TNWhNS z?+i111NmyL*hcvJkaXz%V~kTKv2ssoFK@fO{3q!Po|`BPn&-T~MEuSMrcgC>>Hh1I zAYJ3x97E~`AR8~U;(RCW({dQ5pP;*A99Rf~?#fYgGXSUADubUB#DHH$Oy=W~Yy?-{ z2(?Cw4bULyp6Z;#V+6%XAo>;CgE&+pVSR?emPOpTvL!_Ss#qyE(fX#jw6?3E z*(71i=I`eY1Jj{LdrK$lKA%C-9)55)`%QK1ccjz>hQ}ob1t++Q`o3&`_*jeZ{6W_q zNFe}it)<67#p6a}7y0OM|R1LHM+=@fubuS^~Y|jaiCzZbANa2!uPA{T8 z?2zW_@D{Ul5PWC;w{7lV>*8(I1muCv3t_s(7*<fo+VgWcaHc=Jc07SkozDS% z*9VbY?S13`&AokzW_YxB^y-1;N~g=5PFxS<{$RLC)vieiKDIghJ|*tRX9k~R6i$h2 zGOe)Xw$RM6D3xQ5pZ>d^_@JefJ-=5;@aopuTGNk|;F<$lTf@0};t_@&67g5WoiinEDx>2~k!ed)(=@ zMuC_VAvl9&>lO>a4pP{9S46?jwP#eiGGna|OJzk3_Pl1I{UEVc8T=Q=rs996aeaqf znMM0%Cu<~WMaPy6jNXtY)i6#CE*K!dVpZiw;~&N{r0*6cMn2~H@nGc)Mi*xN9C?O1 zmN9c+Rp&W)ZFWj&PTwWKPU$B2=B7)BO~d@3{rdFXaj0k8PfjLs=OOz(O;Rwu0OA}% zCT4?E^w;ZvErhv^_xHD_){nv;Mcwr2yb(!5a!koi2P!$tBxK6;Wm_~H9m!IxfUfrx zL_uW!(w+i|&^-~YJ8b;4Yly{!+;?c83MqLy+Ln|y1B^ix!84j4 z($Xo(f)pF_DWhn!=C<|;r9xgSHaLJ+LZ0$i@A1!gn^!TuPs}<(JL~pp zbnS>j6%5zE(<1;3v0&640HKujMxdJq6mH!y?|9d;Z*!BWsVffl6Z#Kh05<*o9bWRl zT2jv15cPBFO^hY$2S#Wk-$+N6^xTVg`JRO}m+W2$a|;f&dtGO``zs)h>%l&QSmXHm z)lI{tZ>M#=u(Bv~MA`g73;V1zR`vVvozB_|Grh%D&jR++IPC&<+Kyl08|};N(|R29 za!;6Xbti2%v>~`pL#{%DIIhbs8~$HvVO5n!ECKJC~~poi4%0{3V%+VvVPTbY%l4ZTR6nD=mgw)OD@c zv$?p-Nh}mu9QbaGO3^X}jo*G5*z7O4vKfbpa0(y{Q`tQ8L`mWuSjy)9cKHPDa5>|Q zs`Fbt4d)6B{)+de1s@f9SZt4y>wCVvqZ<>oaD&9l`ki4Sxmx#^xZ^42Jwtp!GumaG zdP;oHUX^8fz!;1D-U@B{KAvSDJ;H4MFqD6vQ<@RK;xj|gk;p)6Xc&v$qo#k`XFne- zKF{c?*r(^YMm-;Dx#)<9YBWQhl##tqkEO@mMfm%k+jpoRHV2tV+=WygJ0StM?|7(j z4I}W=j`j;&W5hRZJUIY2t;FIqbo!)Vp;D6n#gcko{+Z}wB5LIxtc%XlV+ut~WHo1| zdlfEgfBF}`s|waJ)2!D|pG#*anG#R$#DLVA;Ok;?{GgOt}O;xjqu%MUQsv>K!=O{hvGHjj%8;0-G-mNWoYjDc9YtENmx<5 zVpk-W^sT7W`jhzgqapEclbfp{f;a^Z4zqQA!aq32^6e4)gaaCZGLd75Z&xC zp>*wVb7skG9%f0H%{pgrmN~G|?`b~g)&mLrsutzKqu-Xc-Vf@z&0bi|7@cNc8I2p54R~Sr6In z(e-{i-eQby3Zr4#+%ldkx<&6Bzq4cP!1C=ot)dc6eiDxTrBP#IX?lU?3G4+AP+~Bd zd1-!ICkdA0ia!^>C%~v4d87rsSg>XntE^Y!7~p}<_wIUm;Az-nINavH)8MicO#Oah z=ku4e$Ku^@FG}o%jGt_s&AtqfSiA(CZO(8N1AiC4DtD^hujTR&ZA;sud7HZBOz^9~ zJvcniD;X)af$0&A8U`E2DiTp|#clu+I+JU%1zKd89xwdNunYZ#UK` zq7$h91#=Q9NaJP0*!v3Gt>_~Z^k?C9`uTEM`26YqQ##)R#b=(Kb-J?u=B0cNAJ$w zwbncExA_)c`JPe3YBr{Z{8a@?)7v<-TnIOsoK?!9SG=E#!6!W+F2A$CO&mL2$BD-m zinAS122ni*#$6z3Zy${*?9f1|aO^u44i;E?VC;$G=2z3H)tq=ib@sSeHbfm{;zR```A@ACgR=|mF~4#bn-_9mUlZ`VHO6tIzkI_i(ev*> zq16GpZ)^Lnob;PLbKCp#xW)eC-q!7g-$gPpr(7pKY&9f6OvK@9OytgaDRrM2guI6a zxCiysCy~R8_0sO4jtN13cWsdshTd$DP&Bw${hl137k%IgUCe~Yjr^%bxr#;>ydmUr zk6m`_35wMWeX!TUwwH0L)oyGWSYy3$w&W8lC)9QR1LZ6*%`SN+gf09d4?J@E6DMa@hcu692JGa(94fzjZTb!RVtbyxdl3Ih>%O}{o&tJ9ypC;F z#-QlERNg7u&=eIBxUzlqjL}hNamm-jMlKE6ej%;##xW;b=jJ@4^yrnla=m|8RGF+ zu9H`vyLV^{?#|A1h)-!cQC2UCR*v6)1kCzc9|#cy;0R;T6Jyg?a|^{Z5e?1>lkWlW z&q89ocYGm2^hWQ;puSgYh1=>Qr&CntKiC_ix)VROw%_*7M9ZfF%#wcXpMIMQ05 zFNteImqlZbNFVAKXB4Eppr`E|c$aSp*8(9aXFZWY8s+kS$duraj`&{OUepY`$$!{c zDt8ToqhU32VoVcUelTJvmiNZc)F}R+S6AZ3ywKsq`&QN4Waq=%JgHl~Vk_4$KJ5F` z?KuqpvW%&QP_CXASh6W2LT^p=CfDCv&TedX839Y?&8B&+D$wT#y zfc;~Jaqj&kB6gM=q_e*mk@XScL`KwW@HY%Af}OjXz##8nBt3MJI(h_DDMqY9>E?&A z@N3`xoPO4&S=nn~$F`OD{TvvPXKWKWKl%HJ8NU->lhAp&<~X8h5GPW3({a}s!S7zZ zI!*wDNX8Bk4XUD%7RRNHYK?D4=Tt&b^7eURD^_`EybrK_mBqP`FYW+86|-LMGvTEO z@2&Cbi7RTPk|*^M_>AN{;u+ zJQ1V>`>jhc!>^b3ZT>>TNFXo2SCA@a<)^uWTxyEDbm2jXWYGa>&fsFduJyMo|BVHH zK*+P<&}0+Yw)afd35e6N57U9tbCg4`2Iwc)$yyXUA`bfqy%O`t-p;mf)!3+gEcha; z#lO^j`4H-Y=-G(Rdp!9S1UgZvxAx<5cto2k7r;^ib4qlp9`BtQuGJpQ7zZswCql+v z@Hl^8n&MBOOP^yVEYf9<#JIUntT*)qXBk;Oo&wlh^VKx-gW^NE{x!zG)E@8Z~;Gonan9dteiK z+~q_7lLU>I;~M(CnLP`s4{qgr@3AkCCP45)xd*FD!nBsX&f7<^c;DK7ij&>m8BLJk z+xp-Sk-)6X!g9F{2|IEecs>{mW~u1Yg0#nR@CU*O#OfX7E5=gou}`$ z@(S5XFHJ{v2?&3=OVSm#CS~>VEcrydvZJ+(8~{vS)|W68vkE-S;Me${2eB zq~@!ijR5uUI-1kE)s4M zj)Wt4HpL}mAZhpr!#=jvvfHZnoq`%CdSWlz^A9>6x@6J^r#vXC(j3WivwFu9~nUX4U)2JO{le8&N&YInvlOg=v*>xEl06d*hNEkL_qGWaOe7C)7R%=>zXvbi~_$aVJuv5&j^#| zjrFEU9;>1ipW5Sx0SE|apCLB4>?HHE(fQ9K*iIZs8z4Nme0xhg^Nv0HQyx2|#2-Ii zepbl_q%bQZM)-XKC5Y}64c$FpJG0=0w}xDQUi8=VKC&Pdnzk4jsj+_U3sL3%O*`*T zf<3|Ib*L4wRA`UWuYa(xF3Z@Y)Q;OLVX)F%kgSw0sa$yhZRvI{<(WU7(Ks_xVXH!asBwxNOWqnbEac@`iQ0JVQpdEyWT^2` z^}^2Umg_B0+=+e0yKRP1j-52*n>F`|cJw{f?~09*|3fW&%D&86SSWkXmv<$X`43A1 zS1exi5swg_8QpM$iKK-P*o|X41g8Top+UvP@gu8mTSLZ2#b-t;Y=2>MnXP}Je_m-j z4ziq$SpzSMD;ko)l2QMochN#Hi6W{jSMXP1+>xkR#;CCQiM(t}HdhC+{AV}R)JqwW z982j+uRk%qCK-nB{N&LI56zV)GhW{lAF)T2j~m=G_;tKz*KCr0G-u9`(+6e-(&6AO zqciQ}QfeeIVDIx9`eyKYTlvq43O=BlE)eb!xN_th)di&+DbGmn4>y+lqx+fIZ1bM;+eEEF z)ozgBcT6)}(N#7VYZP7iub!@DE8v-}Aw=Hc0Awwn!=f|iPU>t$wxLOz#=Z^^WvC*4 zlQGM5|8w&Tv6%J4QXt-Ocj)NGS$6Pa`=so<=WYF;QWm%gi3~p`PN#kw+v4wbg_*cKhGQ&ORLnSjlJMI#Yxo3P7Hfw zcsbo_>9K6J)4&~6Z>(F|^yai0o_B7J zP-(~;1a916;CYrY#1R2p%Xa2xiH{fe1GTHvUJ#DAu@jCtKx8d@Jj~63zd+qhA?})( z3Z@0G^<{JRC2x?6bRNpCeCL5RmY19V8ci`ek{lv0@vlh;ZSP=Y5ESLaI-BfhQ)LsR z3}?qI#uUmm)|>*Zv#Kd-M1_?13lP$VB6 zHERs$Z+CXihBzP?V#QnV)7D3mIWHn{zBR+>fk{7MDP8O2*culUT3rKipMC#{y&ji3V{0Zt+|Evgfc)^#*Xg_U zYy+&iy~qTtxC`RIy5O0>O%9m)gG{_od&mA16?-S0TTty_XJbK=C;vp)!>v<(7d>~L z@s0W=Mz9`aguFnYh0xKgfDQ0)8vaoaKxvRPV9EicalLUpUYP=dMq2fIEOvaD)TgBC~5jE8&+4z=BRLa(zI zblWbv>)x0H5bj&eW}H zrXFS@KZN*3r<_zwQO-6um(nwdksTE^7(x%^Np%t}zeDU;s)y=Go8nJFfFwzmy#8im zzPazUI+2OhvJF*#9@+PuUkDt6;sA-Q_CqzF*3zw)J_~hE{(l$1C8xgoCrB}eO|#-d z4&Et6@qAy9Z~t8q0)wE6sim>8-~+$Er6X=?007K#h|W;tYSRIB)%_2kh`MrXyeIHADT(T}jxWCi4AEF}^d<24w0d`DD6Gj|Q|nlmB-nF8 z8$3!seCH}+N0hMq6-YkOwS2sLg{tMLY`=?Aie{D}L-Si_ax30ra5ROWTv++@X%6!y zeQ|cIk3E|P|4-Ux+wZBHbdTZo8diH^5w-gT^A`tX&)#ftRfbLa?9UG`c|=Ir7B}M% zze&1>O*GGEet-r(PKv?Y@0KXCdf z!~kUsS*ZA1n;33PUWFVPx*^z>_URDCwr2MJH`ME+2=SlV#>E2LCQ_b^mVT>^isp3A z$=$CR#Em4J`aLnzKZ%#7buKVek!}}OuPigo{K785bbb4rssGgarb-OorHfwFEt?^4)RxviO7FBB_j90 z0V{sdkP8|cX^-&__fuT)9PV5uAlr6T-P)&uEr*tR(3H=O90-&O-Lv6`HX$6O0r}J& z3FM`EZ*aAm71mWOE)5x^=yRMUi$Mdog#4Rxj0uK6SXC&tz=6majkM-Mtk@;?wibOs z`^K;k#u-Lf%D~~kCN(W!-w#kM;LaiIT7M;Po;Q(ZbBNb~V~_>BZmdDr_^O{$TRmUm zjp(9AaKSeLwesnYJ&vpCieC9feN^HW?_2BXoWQ8+w%D`vIdf^MJ;IFZzz?#6r0~#fqjN+q}T(drg+T z$>+_RS9Y_)FDe9RTdrFV7w30vFBeEwOoda6F~oxlc6qU+h8@S2akZzkD)4hF>;#PmXtx2@2cP!4Ld+&titky_npj3#>EoA%q-UVwdty?X zJvMRo&CF&Oe5VVlLG4j)eZCtxIcE@s#JIiZmGrIGX=jG&_$Wnq-W}Mge`&v1T+@Mh zmEMoPG2&G}mY*IqBBz{FrbJ#2Q!&^;AFzHd3&SKt6&pEJ1Snr(1dlJbliA#=E(hwD zH>NO?c;3A&O!u3YbtWJ$AUXWJb*rGdDk|ek5_#ruPFaKwZI1KxP5JI2^`*glbwgWQ z;ku*`{nyl_rJQ{h?z0ng_qHgxrgC;8KqQba-Pd=e1qp%s-KrnD4zAZFEUW>-j^1PR zJ^-2Ma$BIt-lGjRNmKn^$=~+T6z8zG-<;NX@P)I|X*I{tPaolLZF>M*vOo4w_d@G0 zP4L{A>03x%KgQ&66(gxZddGNJvn;nkGBlF}ZTCm<3lhHl>T3km&Dw~qN~_BuFF zkr{s(3fy*y9$>~-pb#s+oLKb42hUvWpEjSy6!gz~YwZ^qR&aplhLg0wX40=jRhI)$ zCct>w#X+Mz((+3O!Q6lOjUOa$<{2dGO1|PG?Nq+Eu2c(noj!>>5KpMV?AWaexYu~3 zU!IOvTDb~|ItBoah)+O`{i|UKqHfZhC(_;3dzMJ=wdqc%w-aU>_H230g**4(PFf|g zkY4ew@!UTTEHELizE=f%ucZn2F#48RU}1@`;GS9~hFj z^9E-sXuo=cM|y+)ot+$PjC*8^dau69e_x_`J3`tFU(M9P7pDf!eRk+Vvv0(y$AhpP zCy{9QaU2hvVO6kZX(+YLzg!&Tm~U2Iez9Qcd$xE3gq3k@hho7Yb%el2sDp03;WY88 zMms<`DPR5k4;`;mo*(T-9Km?}6(8(c`qS7i^n2O5^qVc!XZdPVB(`Ej#6T!&q|pFI_MqUu72J!q+0i~E>V`TdPFbnn56s` zYR}!qk4tLc$CWy#`t6~hXz3*FJ+As4FBVD&1a}kK+F@a?(U3*D_A`h;^HwxSf_9d_ ze-!@7_`AZKVSMWmbLCkcngj~}4pSJ{I3?~z>>F-<;hX8@h{$){GU{u z4K3Uu;24p;$Z)?o54q4K(pH7FrhBz?&IM#{9y~jd$`+sKQQ|K`sBFV+ht^Fr{?q|M z;9tE9qxn4U5v+ZVji86NUU2oNKR3quceC^~KaduyCvrTH5+?|)PRu$9AvNZ(Lu~V} zzsa~@hEZy8@o_-O0n)!;vOmX694|j)Tvx4)Ah6@(poFupPSV7Slq3Pg@XZB4a0hH) z?*(mCV0T`VAhCi(R=qJGpFRuM!tP>s$hDVIJH^^| zCkDJzDhp-YoBXdvkyVPnFz!YfZ8@i29Lr?jHU8Ca{n3}`eLX}=D{+g>x+j1+K)y1A zvp<^>D))=eG*9oHA%` zsPb1B*H1P6d+8U0Az7K)|A$<`;AoHFQVtnegC z^zi#6Up@Q%b4_!Mp>~A!J9JqiLQIGYDzf{UJK5cXRr^x+hqWSR6MG6#S^2CfLxYli zY;`ML9SoFfMqO;*b#PZlQfk57_%YAvVx1lgaZRfF$IA;V6dC6@nBi-)G~fIRCje9p zFPw^dmFxl3DE0zusUmKSkaBPj^;Fe1w9?r2m6?>;>S+4 z%^0d36?%0DneK7rW(P4ZV9&-2I)Uu5NAmF9x+WQYUD*He;WI0Q>}m0Tm0Ai*`Cm7s zqLZAKwxy_H*6T+7(*T6#r1+x=XgxY+U{P=3&owQ>DVxwcWv2*G=rUe#*5{D+vgd<9*sZZ|klP10Rxw%zMH`f31wW_=XM3 zMYRfsH71mj8G8SYzL^5B8`Ia|^xSienHrvC&wp17)y?^b0N}MQJn1wWZ>P}N7yf%K z?<<0u#3>-ApyKh)U|+!okfi_wXa-kbuigQK&3?asOrVM?%>#7%;NmV?rZb1Rt%)(`>^4pHLS za#g!_U)+Mk>yL!c&KVS}!5l={ibF@=SsyVnYL#>c1(;3uD?(z)#f}!C%u2 z9*6_!G({PFohg$7`QtNCD!pTPu6NrblL-O!94w^rNXY5nzag@P8S1wZf9||xlY$#f zoRpC5(IxM$5rVlWyXMCYg5IKWVfhUuir>HmwVPc^)Qo!X5GyyMs^6dQpdfB%K=`}J zai#H%++OU&SS?+zk~7`8l5gux^s`3?pa~dQGlOM3i-Y7VzZCHPkIk_tQyWtL4&Cl9 z*n1Nwcnd7)@C_af^xs=Bd zWJtp`6Rp@+^yE&}wGgY57?7@i`JpWw0u5Nqw=Vb6gAX}dDaGyo%8!M^Pmr~k@s1mh zz;(C8*B7FpbMjT@;OC~fK0IuI0j zFyUiKf6b2cUAS3vf&S-M--5PX=NMKJCk!L~a@&ucVL$B|=ZJMciIv^~Bz89PmCu0g z9eQS8;C_z~sFq-}7682~W~YzqnR_TBB5VxkV!%74PHB#zJ%PH6vW?P2dQO^vTe2+1 zCy%Wkd_kEkjD)FN2lBo{;JSbhrzvXSA(~|F*F2i#b5J~z1j$0aXU>9ZW)QN8n}Aj1 z)!RNW%&=(bY1y5+OMt-W8ijy&zYE_>dcZ@H_&DY{Cgmkqd=ylWJNx3aP7?QcYF~*x z^J-g2DQW|YZ|k@c=Pr|V)~sZp zP=xcmAgF?HXwuL3i>E!kJwVn~WYsv~oEG7pDcKE4F&eY$^YAz!`1IqBDYnP>TU3b` z9oSjhqN8n}%K}Se>5UC9W$2KKr7l}Vjm*E#+1GDutSFu`**Kxx0eH{=i?r_Sh)A;L-%VPuu?C6d_5 zdRai?jt-czE`4O1Db|&7@1~IbaCCaB%rp5N-DamScG!`h{uTzz#Xr4?HHV| z{WD6!d2ScTps}MuZU$R4_&?Sdg8-WF19@?cJNc%5f3$p}N8TP(Qop&W4H^%!U2An(*@l=%i%T z5hT`*(1#$Ly5vIteIGp0C(LWBONgoKq;A$nNt+}#jBO_GOWaRAmzyJ(~XZ^_=DU?RN_@<+hYKZGfW%(L*L zn4-(b^gq&M=G4e}2}X3rpHT7BECHP}A~V7xD|KF{4UqG?IX_j+P>KC^7s2 zJjtX5kzN-gHF2k)pk1r*QG+)|gohDj&ywHMADW|dL^L!qMRphzAojCb>LJ2-$(aiB=_{778BA@}G+eA9=zU4e0Dc{vu6 z>c8h9^Lho*=W%N(O5Dw#uBilQX4WLkuGM5QP+6AF=~WM2k}BKy9~`OWA0UEjZ8&ULPHojI@P{kR`b z-ZVS7EOTq8CI1{+44Om)BlnsFs~tBi-n;M9$rplc8ia?v4nB6r%*aYj*iZjE;$A$Z zJK1_;;8bzH-TU1p+=E?{IdjqcbPU=2wrGF9)~p-@mV3uizQxdKKvJrG8s!)lVqOM0 z3MlKi*vH-!)QST!ePzmsLOTYnvpVL&!;oA;nn22v?@fN`m3 z$DSug%gZ31Bf&?xnfk3%+ln9@0F5+=xMel*k>ul%WLS9|79LK?CEvwt+Gsm*#C+IR z0L-K3J8G?6$ZBV^vp7(m4 zINy3Uz-jo(c31`;=>opTqdVHUS~!YA;gbZcnfkjK>~C`i zd8$Xt2K`tpd^R@66+O+?ilF%pkH70Ij>LHOlRCLCzy%Dtx$c#`A~n95WJs-GVQ z%qWx1e@IRz=vDC!R$dMX1;iu8HJa=WnyC8@HsHiiF z#5{zb;#xo;&+V+`CtXo=meV)f@Mcks_VwytlTaYpE(;NgiIH)ZpzeXzWlkiG|LLi3 z+_KmQ#W-er9enK)WnS3pInWv55MG1_-}||Go|(`vmOa_xk6`21ZlyX!K`vXJ(wFR< zp?9_4YQo(n7f+XVQL{HI%QjNy&%l!HLr?E&dR>n$VYq@p^nH1Y0$ol<&*_)CX8UQC z5JnX~bt+a5e2=T$<9rvi9Jd&lP9djvZe5N;)F=3!WR3R4-`*Ng{a3Bff}}4nbkluG zw(k17Y|5G{L^WMB@l|^Pl6fy?BO)hBy`MKMco^4?-j$#zOpaXnR`)4>_8~EuB#Bze zuF;uVDwL+%&e!%C%xW*1K3EDfOMtoKHZ3+q4Z+)uxtr^g(PRo{H64IckeC{o2P(lZ z>MlLpd}@nzl_HCSPkegdFwU^+ZHa&pU(wBDDWbXHdKOeCLuN2`-_N6qilAZqeT05! z+5;=@&pAS}zo|B7W?yz`hW`~S^}Io~bR(FOB4WVJIYt%lf;1od80ei2pWnl2_)A)* z2_H%$l5+z+&|ny)#&W*Ti6-oV9@Vp-H78~)R8(C}k9ksDLswEliuQuPEtabAnnv+5 zuPXTqx3!J-9WgM#?H=jiETf`GjG6uK0Ry%gikg)eW$a&+sgg3okXP$d*sHYCPPa~u znbP)jkmjlaO>ixO_A17woa&1v1DJ;#JLmR=9=LB0zE0TwX~}PM9j}`k0iwi1 zjzx+pO>4I-zpoEn#DU)($z0?BFa=2snNQD8z}M0X(d+huJ4z)eKJU91?)gx%w@fb! zjNrK;25v=M98CW?@)ow`BR2;#T=gG_JTPfmo>1`ys`U_M{)&;KQFf&TCqobJ+0o`9 znsjV>kXXBC3LZT*4fDJ>MOFC~RDa9s-&7e-8*~ ztwIj?UF|D%AQu~$d&IQOy3u<3o#K~AC2>F6$9N-nF#mu=yF1!r^iUg=lWK%4v*8jS~h zm%bOCUX1>XWk@hG%Wql(w%?H+^VhOP*&HdFxDi@a8Azr$Oex-QE(K!PGKq|BP zmNdI|m}8nY%I)r)7pvAePgw|Odt}pgeJgcxd4wyBQ7VayU#o2QFrC2L z#U3iSKRVPMC5H#jZ#XELXQB0`6+KPw8;jMS?P%t#r-NR|m;EON8N#1r4#yX9G^JkK zVJ}3XAxbu!f};uZQy%Dm0&iS|craFqJ+hW`M5nx=zWa}taDBi@<*MYegr_#ZyP$~H zF>L_?h*>LLYLNhZ)l9$U-Bu5|$5`vy35iB-rn1?O<%QjBKae8S3ELm< zro%^*ffo2|RW)@pfix&p#pV1?5dt+b2c+3v#pIx6*BE}ju8(sVQ1hAXuy0)ABt$iQ z;^ASW2LN_+rH?UmpO2s#IejP1a^#w@1aQVJNRHmPdP8H&V%=^w;!S%IqP zae&;!=bO>r9nhQ2yCWHM`bok77>rady1uGggm7@=(rp=IDc8F7?`V zWn6kyUgWnIoi&N@o1=wa;JDMu?wn#o@ErZA?BIP^5tV(@1L&;3+`V8B-nmHzk)5i; z?d1^TBx#R>wtn9l^eFqtubOC@C2~gPaZ>SU54*twX9so zT^z3WWqUbibYnbbqjsO@CJo&PrMzdgKpiLNNJI`=))4|c`#0#iNZC*AoqNnm0yq88 zqP0HJ>-B9ZYRv^-i3}p|AyCvaFK*+zX28yD{lr$^KirNLa@WXoyM;-+Kr4C?vP*j9 zTj-;Q{C19;@7x0(eEbvn9TW2kleH7b3FD0CC+LdP)br~yi@@YsDqIPzKf;7+_Ym{uFKv#Lbs||Jrv;1Xkr}nhd$zeCOS2V>k6E*E4%Cyo^>M ze}yX#ZCEQK!A#IQF&y=Ce>HGa5@egZj@?Id@Blg3jR%F}1W9jjF3XnT9|!YImo^#? z#ALock-vv?eJ=+n<__>X883n7m<`pmGDtVH-2H;r&&DhN3 zbE-qTE}g#Aer+!2_JcYNA!EzP4Joz9FQ6Ze>N zDF?Ar)u^zrDDTY|CQPSIm^u40*Xe!H%D4A2R{Pbx2X@R}&h1@8AF3k#z#2U)=i)%` zvSgffyp4QfwzKLPo(kj0j^QL_f86CfqJXLWaxxFLViZ2K1Spf!EN`y9OEMg9(5;cO={lX32PHWa z=*2qM{e<87TFg+R6l)Iy2Ls0K?$x2eK?`5HAy)nt?1l}FM2;MW*S(UhTOUB><0DU2 z$IMo}e6WKV@yA7*xPT{H%GO}Txbh<9i$ezxZ9PliHUq&9%P$^X|nVNkNo?UaVj{Tm9Q+ zWFUvdgxr6T1$uc&k)bg?-E%cS<~w%no}{7hWyP=fDY@IxRElgi?gh_ z6*L!!i;1vn0ea^DyA20pmdqI_B8ZyjfoDiW&?{^=CpqN{?9xq#w1FWvn*20T<=>RZ zima@D>AB}a61Em{uvMbLl1culfXK(~3ggWVMG#q&0zj}^>bg_FBp2f7zWWJLM`Op@ z1;tnjdmh+Yj6B$4Eig^w4>hC76Zd7~Ih&d=<-&~$jbDo0v9D$n{_#~^NATnQb-uP#992Cl}i1Dz7HQC&T&|lqMle*&j2z(}p@^~_}G8eCe+`}+b zCEp`d?M39_h;YGu<(mn@DB|QO`;4@PSSznr`&W+aYh%T3(x0&S`;kimWqcK(K6ccJ zvdaPG<^Y7qu6%uht+~3O9ZwD4oIHBm0wW6^YKy| z2D&mnbC54>;LT`%nSx0-CmiDPn0#I?E#WH4B{k|t_!WLYc^@tV;%(l@)D{_bL8dKy zjEnATC+Ww0GKF3-b5-jW>P`*^CO7&$Wbxnmc6fHgGHE47jelwvwxjYiuN?oMkJWa*&XPv9t? zXv*xNpR&`T8a#~7oP~X7pSEA-?mtQnM;jXg+HG`k64f8aAn(cuYULq!1&n%+kAVTf zi+@)8-`H@X7UF+C79vPMr|!f_6U&QqS%;r}oO0bz=IHxqWgrIIA+5)=6G=@L9XCO{ z3|QX>uW2cM4c~#pJ7Y_o!i9G;Y=w8MPd#rCg)T@W<&@cecZ+_wk$*=94^TuDC@64x z^3j_Xm-x1PNF+LF$|2rc7?-$&P%<* z)Cn&`u$AytbNjEChy}E;_StX$?f&4+n;sk7A)mZrdGZ}C@S*l0mfTwzd$zba7p~tm zt(aaiWJ#ctuRnDmLMPtx5zm4SsEhrhhf_1?r2KZ#@DoKX>mp|0^h-E)@0`Ljpfoy2 z4Zvw}N=|nOGZT--EG8>%MV5mTqf>A0@$SqWiQ#e56Dlae0Opp-OIswewP==2LJ?`- zI54!$GNrw;TtI5U>-Ig+v{p7-h`Hw19^!ViO?N#_R5rkBXF=F5)RN7%GCR=j>1fS6 znlLr|?G{@9V%&1RIuH7SAS}Q|Zl83$HQB*ST=_{~iGlfmp)9TLcrwlkZo5@eBhE*W zi__+I_Y9NN2_)(Q^+d^^At@&hUoiW@ zS=)Jlk3T(3(jQouW6pBtt;Aj#Ura5D=tO+Kfh6{{5YC%j-xop;H-|ohny{7g8jfi4 z4^*P;=B<2ooHba`_Z@M#4W>jpD&h)(#1jy@$?yOnUq~=_SaHnc^~(Sw!`0%MB(l2! zQt})9@B35DtuBU6cS-E6Mx(oj&}442m=E~@BFyF#WC-)eSPwSA>Zk@P)`hlU{P#HXP`EFW#Q!!#|DMO(Ah2H((k z#8Yy@Iv@Or5!`ML=fmvy#a34j;|OgT-%%`D;6b4zu-&WqYQW~6%N%u@lOY6m8KNz< zP@&ne_;Kvio7h@VeyyByl8Id=&3li^8=r;``2s@wcekr-kP_eRu|o?wsY&nHPLbM8wE zI&RS)?lKtB8Uv`aJX%x#GK640ZW;CFL^L%=T5sbanf6$mL7E<<3Sr4&jPpoKk#f7* z#O$w{-OW64-yn+OGsTUNA3n_?JtaH-vP7NG{>LZF3HX;QWzi#LJjE9=AQxYL>d$`d z3o0bCQKILujQxc)$Fd9k?j~ zPK$-&XNamDnL0T44=xO1)Zl|1YffBfLY0p|QsSh7H!pEQSCAlfoLoLc6oy~;+{hOL z(R48(83G{+P*h25ON&PD)?VO;6V?G0E?<8dWUAU1L_M_zA6ZmUb+%Z>-r(wY*>}z6 zPv`n-7PMH8*+cg0LffC(yPGTlqf-phQy@0os~Dah_QFA9Zbz6w|6z~Bu#1okiLT!= z+Ag~!{{6bmVfu2QC%t&b|Lu?b!Jg|Ig33$0(wMeqQWO6U?zQVwQ+j(<;C+wNZFD>b z0X(>P3vZGq@f(ElbV$hxE*hLN-^onK--YDK%xA4!3z$S683xyVT0ku*7etZ{@zL&_ zNn;^l%|DU58QIhK>wrChmX4};juejioYl3)7sIN&jH?;9r9L%vR+J5FMLnqgbN=P_ zwk1?#1dNWx{M5vWM2y5;n>{T9X(9BqF4;<9<_F|q#LV$l9=R;B@ zx*`tb?q&q>e_PeQ3C+GnEo1b6B$Bga8;1kcfIR>KA{Q6NH z&fsnzn#U8C`!`rRXzYrNeD}K#&i(nX2=k5uFdj$nAK%Tsd@&knkMb++(@f(Etjb&U zav@GIi{x$jM1OK74>rz~-1ESqhx^zIBT`~nt71+>>NpN!Oq4G0Fa#3H@4i@!C`!(s zd-lc`P;g*puBP_CpVmD)nNI?q+7~#ga}*2wE-(b3zz?Y3H0=WOfLZ!JfR~Z{NNy?? z)dKAU^U@o8g0j!J;-dcWU)bjiuHx9#0L`&R5x{S#bT0BO@R9wDHM=y*>Y(R$6aXv^ z?&d7^+csN(=bYbnF)V9U-12b{+2E|^P_rN`1HqvH`cD}& zL?oR$bqLEO1Kr~}m><_lb~EMnp?9NtU76ih zD7Nm)Y3BgM;EuVq)qZm$Gcp%dwNoqjy?0AlWmo5j&%5%+Kv#KV=`1N8ni38&Ay1M~ z;V#r@6qhOa|fGyNPJ)Pg}-37jt( zxxVQXS6G`!djk%CjGLA8Nx|INyy|o{ZBO5IKHU%xzqYf?*>yu z*)ua%I!PwJ{6PPzj?6GB=~-1}#6Z4jK`h&f#7>uD)xCG~05*9N2jW*zQUaK)0FaOM zRF!4uCIbo{Tv<%Y{^J<`w0gXl9-w+j`sHTNb$4>S+zD@Bg77<-^zje(jla7lAMAdk znk#@vT$@d<%lOIr*E_Sf?#XuQqjC+uwbPY6iHh17kQjQ$v8PcCcHxC>@F@nVK44qn z(XozxNpnC0-B`24w{7TE7F4C?JeLyF`2&ge4q1ecrBRoc)l(fqS+C-k=oA_w=sP}@&6&Pc)P)Fr{#QzL<6n|?vza-*T zgLKQKOdan%YXy?))%TS;QK+w)`bWG0YH7rm$?wyL35FA}mFhLX7@({;o(Cy|<#45gq#u(d}6|qF!Eo1_QWB63zo# zY{f_lSqZYy-mSpi%s(??g9CmtfxuuVzxX4y&26$uiy{r}N^T~Oh8WiJ8$zcA^f+~( z1bL)+By+S`vvM|l1=3wHX6Cm?9UcHh_=qON zqYe^O;aJ}CwPige_A&-0R31>klVC$=O14Ep{r778X;8I3E)49l3g4 zYv)+wyO$h3)&ynuC?{cQ3#^BEthQ%p*pFdnsAryvC}_hzJ8pjgxOd$hS|9omvfMcy zeoOq(Ta6hNh{?UEWD5Y zl?hfNl-09eK}i_+DCGNcbZ)F#eFf70VWNkMxxwNgg9og$-wH-jpmOW$k!Fx|sM~J8N#aDYvjWLtR>XhC-@RA%_>n4>e}uH&s=|uxM1TDq*1JmQ zw}Hp^Mz8(s1NnWmS!$#;mdFaS&rIpZtaj=$%rStd3z3}zU??zT5FQKz?Wqu&0 z@*jN{tEeSX0$k%)o0?2Nj;k<-=R5w(thd{$@|_~C$L<4^sS(Nfd-Gx(`x&Xz(HFY$ zQVKADcQbT(pB3*@6p>tgb?I%;si!OVw1DBGqDvE9v$;xm$IQbU+5aMq{Uvz7F70^1} zjkHqW_$y0BJL8Ea_(1XV=y1CR@T=>&|L>9PiI<9PRBf-QvHU|wT@mEYNm+uv=`a+& z{k?dZ>?kmG0qvjFgnn;e3P1T6x!=fxR0Rkp!eP1I) zzO3OlStc|>ABe9-s^w20FME&pyzQ^{W}Rxfx_8I5WGVCC{pAHFw&ujm8MrCVVe7PU z-Yk>*Ma6dEu-Wx6P{947n87Nzje(n?L$Bc>L1q?8>*T-o+)<~=2k0lZ#26m4M0q}L z4$M7(_AccN@$65v?0Ywmi zW>$OD>@M7s6)Spv%K?mqQ zb8Qhk=lL~U86E^KeZ0zRpH0uKU#K;BP5PiBOrVckW(l_C{0I)13;5c82KM~ACfC{X zo}P&MQtB29eSlgw-jwr{D>xAu$Fb(%pgs|;vblXQduxNKv!n%A#aVEqHLUf09BxKx zwC8;r#F%&JBTj}}%_Cyawxu|;-f1@f=U?tK)h9meI}Fmss?@c9<8RS}kXQgK z-WgXaJV?5@uEA=c zG`O>x^$z!D?}e4OB9p1vI-5>UA%=|MCh#|96Y4y^APYqy5dQXl&`*y2Fqt5su!nWU z=f4yJ`6qaL2BJR}o{d)AlyzKC#J(i;--wu8BOT8BBRs%g0kJAdaf>iKi`zyiC8rKoqo;GF3Ag#0pEakC<^{?kjzl7FS|{FW(4lA1a+ z2RHaGrj)PiqG9!PoBqyk39Z`-g!Qk0iMknOq4JY+1(`j=)~upg_7YbHYpcVZPIe#E zKPhg0s@?zj(>6X-^miGiZJz4CtftH>b6DZWj8YYwoM@vTXJs|9xx%PiJNHvcJzm|c zKgNLIa0sa2%E#F`1t4@a&OhCHxx2+@D^Z2{FcBr9t1&7K*I!|P-hK4CIaE?@(L<3m zCFX2Pl_(pcneQusEojmE2KY7fMPZBB$E%Z1P(A!w9}8IvuPj8EXB{J84xWxS{OZP9 z@_QFN4{qne%Fk((O&30L$vL=LRr+>1jitH&osp_-OYhMQlYEZ}*)rBfPrM@M@0-dU z3{n58Ck8i9kd?P{?3Fn4SS^~=7=ih+$Yp+Hf)kkhJ^GqeY2dN@dH+C40Ep}DG*?w9 z3U;0Ed4(lx0l%j2ldo1H?@={(D&5v?`7pRQ^vv7OvGb(98|HIQ+m?JUF)Vpf9k4jV z-DKFII%Cs|Zx8IwY7xWIT*%iIVMw531Ewh&A^x^Lq3Z{awu16qX{+FgE=4gu|19Yx zk;IKFx{svtFkD<1w*#Uaf3ZKh2$)Bj$D^G&PM+enXL4yab$o5%D~qT+sVPQtdci76 zN@XK~kcBpNsedG0y6R#T8kC@`YS`}ynSZn9!oc-4^V9)j99eIHV(Y=%kFaUYflD_a zfG-}gjcr_cBn29HREAK_yu@c?XRz|zA6VfDjFyvI-@DlX!EJCG7^47wY2Fe;?l-sM zKXcnz9F~+hf#)@&wdKe7Hfh8a{OfN;7QD5a;E_ z&-teT#ijgAqeI2E!HeffS?{KfOSU;(D{Z+i#SOpBPXn(Y8({JVx72a06)60p-$ps> zfoHY?C($|||7=iyur7MOZ^260VpJ(Y9qwC~|Bk%G##rvA+z}1mX>1PV6DE_vk3ZU( zKk3-+JC|Ae%7#Z$g8{VFM;U2P*={cBal{62!7Eot`M=63U)n!rJrShr=>#-vYxbE; zQtQQmRn1&M;t8zFl9O-j0_KE+)sdaUd33*3AzOxTgJnlG=b#s~?v)CZ*4$w{w3mC5 zWl+RY8{Zb4CaFPgY(gaE!6B+!!Yyw-Q27!ao*S}yDqeJgwY3w<>zusdCXZ6UG0x%# zsMqW(#ZVM1ymhc;4uxy<18;bXE{_J@FR*S=#YgG9fPLQk-GJlM=l^cKOoAW{u5xiX z06=f{+m2iTOw?~bM)q9*!`Y{f^JZ~Csml=+1aO|LF-yP94W!|+EL?O(@9#TjV|qjc z?KIB&&adBn^|_I!^1Kd*O{?4e$19Z)SfxeJlTX_J*rz9Iu7;a_)u)thhyQ-9i~%DJ zfan9?Iw{n5@RJUBV5bb25v=aS@Mj-oj-y?98Qi`0_i&+>zHk+Izh7bj0w8o+vWX+< z^$m9fp>UA_MQFq>vI#77IMggz|BE1|$ zVe=G^TNB7m*1Gmnyb}P0t|)RF%eUVDSfj@Ou>g}p{LCZawsdoG_)MV(HN5=V5gFCb z(A-UhS8Auc>|UYFtU)%8y(YOEw6OLdYo1nyfD0}u3u-Ap;6ZQxFzkp1O`$kOR=PNN z0H##IMPSlrUi^EPIQ-=%zy;s90iDTn5owWuQwb6hsFe56radF{ykm0hPw4Dx&DNdf zKcc!+S1gALk))AHKTmv+XaK>R?Xvy<8XFtmUCCD4RV^-E zo5Uo&!OV+V!u(WoXr6fnO7FVU8-j$jke>R{MG6kGU4Xm6FD}bV`$DGue%u)O><~y5 z!=L+O4tKaCYXYc}bLsiL$Yz`LcO_>nO~BB>UfvoX$9EwQ)mHZi_LkLzJ5M4JyTCN= zhZvgJc!Beb!#Qu{U;7; zPR1jmyAX|kBtDRWW1{4NJ}|r@Y4H|HP-9a9vVh~Wtr&3IaqB%LdrAsD|6G;bD8m-e z7q7_LEd08z+J`da1V!~>r}uz49440j6?m5ILa6}56bG#vQEXE1P&eQN!be+j{DBVvdok*)p1a&A*y#n}kfkol5K-`& z>CtCWb5aQh(6MV`JQiPLve5JT4DqG%P!XDm+JvTTAP*(7cntb?hvi^Ot%)E;L0?ImVqAG zX7Q@b%WFgI6}%+CIrr^PcgKpsxNQKGUpFc)v15CG2OW!?j}Rr0O(R~@Z`SI6sr1Qx zkwdrIn(+OdECwcqN7q#dfOjA31D+@ zN`Z~r8*AfU{_W=j9}d5Uo5w|PU@rWQM#V?z{TIvhr@GpJ2zmddeGW01i-@t)Z1wA1 zPBYQs$I~+B)*j%8+INAOhnRH@D>&3w|0n&2e`#ODDE?<17gNdz{B6kmwyRWaBMwqf zm9XA&u)A}mn}dVf(3SEUsUFETVFbt#UvK}XiT5$Z5dNV~zZo;|GmkaP8Gd`z=qYPx zC@*W-wHKJ^K%J}xBy5tNoFV7u3OF{T}5A$Ld@MfYh> z(WzEH6MsOeq7>>o0_=5WIqZ|inkXHrl5)Y#-_e18q|NwSqt5zt{u54)o)BlG)W^Q6 z8D}In0AKuaj7j#(kw@P~Ub6+F-%Y$u>RgnqZ_Kg_T45NiD)K%RP(<3F}t9N&I1N-bsF%exS>ad9>! z1|?Hu`tgS55iJbEQ5L_Z__gx+S#CpjOh@%`kYdD8HeOG)6+~P#axvb z-*x#pUGfl8x!R&1q|1 zqhCyGB%e^3eIy5zkCOA2>-j7C+xQmbk*PCxIf~C=*uB(C;3oJ+pkGuB<=~ zA_wzG;oAETb$Pjt9TJ;g>h%n30>nbuCbO3(BL_bD8A1f1(EvHj2&`@t@o}e2Rq?GF z=%q`l%Any=$ByFMBTnrXqYc@tf-ZKxM2C^uojteCbMTS?2YVefg_-XW1HdINUY{M3 zEaiyT%A2QYYbiATUr~}6@c3TEfL?N%on2`d9D@oqJ^%IWkSavHf@ty z?|CHV;i8iryfdLPOMkVw1b~#zcBrifmGf^*4f^4U^`|lash&Apa&xUv2*iSQ887M5 zszKi`v+dO=YbnOTr(tvLc^1cpeUZ;A&jKK3ZKW3Z21w;M;*I)KWOtM$PA01(?+*g4 zB~cQS(>uWg`PUM%S7GVNIO*Ox0WeN2%}$%&M>!r=BHjj}yjmJhGO4~C6iHbkx$(&N zm@nyook4^0=D*RHUGRHDmcUm2#~DMPlvmQ0aB%EFknUbD6oeeilU0;6eIvGhzF1~w z?(F8sZeIHk(tmx|)pLBtf*&ZRV0F&-(jbzIDb|3(6X(qPxp3A#nZ~ujinCq!KF`=rAjo;Z~Rf7poAuj}`(Ew0h z>9*f}WW*rm-Xd~T@%z`aCr7~SbRwWMCm6R5Hge=EI)SFckrVyl;lBzw;XmtMcp)|~ zfP3V&=T-FYPIJjS7CY}9W-iLZ$NP>}eTm#v*MPzR=exY2)o&+A8ai^I_o_T?$r~L= z&QSQ_f_T}D`Up*2+Hw78D?>>^*Ok|uEG@Tt4mE$5X6K`K%*C1UzB`WxGUNC~HFVB@ zVE-3e5y&;M2Ov1Tr(pO=UR6aMwfGFyzEVNlc@VBMvh1F^J;J>j-A2FWujH8?lfo4=RIEl0z4Mr;5EwojRbPV!7Flgr#Q~1i%Fo=zH?a zzY#?EJDB?qfT;kEKV`jL3sSVs4Dw^WgKgUrN_WSoFn)bv&C>`l3HN&##a}l=U0+yT z|4`=6{y=zc)i_jpD)uHTJUjo!^XSGP*#6rcYJoiv;J;JQKfeDd4?lYTfr3n^kvL)P zh&D}vmGU7%% zu4w}}og)tP@lhWo&OiZDjJ@*YlWi+)2JVU*1)$Tkz&=jG2Gu4%N=FFDr--u4Uzgb( z_AOfl80ee`2%RA17&5!p!I1F+h87_N6rqGIlIyvuva45?(58UD0|h3<%KPuz{5rBa zvN?`9F;jq`@r zaecZsRu}*$)~nU=M$4qw%q1AjVF!3He1O?Z;B~y?J!YYjb$I<~FURIBPu~hGjT7tz zcanhfLz{lEmLLf8@&Mz@-j?aM27K-qOxj&h!d@gW^FrAmb$G{Z?PyBSlZwD}e1JZ1 z;S~EsmI4H6tv2-7Fl7QAt2=q({4r>0I9%Ts=bJ5OdT6l|dH4qkZEbH}Ronz&HJWf- z^xA?f079>Um^>iHjL&%FIpLP+b?KmJXOb7*To<|bJMl`LrELa%#y@h5t995aWP&Fy zwvk8Xiy)7U*Il|Po^Pq<;9h(yCVDY_30c!W@bp;^{QzV$c-@Nnn1L)E2S2)Q1sLZ& zpQYMOBJyUiAI~jD1l?Jb$hG3J>#{UW?EKd4EzOT#UNWETxW__iujYc!4SLm-hP7;1 zIUwANlbnF|I~aL@IKA3gFRHhTyqexOV&O#D`Ceu6#Zh~j<-sm!lYvsR_u-~Dog7cqul^=Ftd^jtD4n<^LKxXs{oP|84 z_=Y;0Y!H>1i;+NMFy&%*rXwHZ6l}qU5PLqcb0`;fBI%$8FS+p)(k=Ljiv*SGwtnd% z977U1F!UST=Ku{h$gtvi;4Vr)CkL<)A{!wbVyy&FP_$R=G;(?<7SC6QI$ z7FK?X&VR-c_RMQ)Y+&yZzznnb(KteKmBIqS*p$>a%A5y#RJT$``cO%Q0H7V;RA#t9 zUrZR&TIYSN7Z}YQXEf-g$<3=q#K((rYSd~RIgs(W4e6|veTNgp1ILk*yYjxfwfC%F zi3_DHqX{=jmv>|rjJ|ZzErAW>Nw{%pYl7s2Y?SeO+;=d#`b(ONW%G&^Bth#1rKJ|n zei{aqL12n+~9jd1R)BG$Be#LP;4{gbYy30NLfWZ;cxjwXX?)7s|quJ>U-hiwAKAj1^F5a8MZp;sJyDLrXLj7BsaQEhj^lL;7+gH3Ffx~ zT4*x_z`7qk479aZ&OeX{{i-96>QH+0-LE>fGTAdD`+EpkOY|C;ZzG^y>w-oX(4;Az z7c6^u-dp|BnhvBT0d(sd>-7%8!JY;AYc3o%Qo!S%7yf7d2D`$0^$Rx#0JIM>gI89M zJ(s0^===R|Zx>~tZVAWSZqzmh<<`BLxxjDOAqa!(VXh*VQIh~9gY z2FHX%-zH6gH4HG8G=P&)QiwtTjw>jy@(LHRKKd0k^z^sm`hSMyaj;i3HGT{J{9q^B#xnCY*hl$*s+Hjzpo0P z?nH435zc(uDfC3%Zo}@|r-dlzM;m<(#6I8SKO4t0p9)P-f9(K4Rc5;bV*QjlJdEso z89j0zaq>qmQAN3Hv&5w+WA?C(0tYv|PY0aRLrq~tAv;0v;IqKVD@vXJ48oA(7OIRh zqYhge528=5&^ly$f~uv+Jm6J)B7R&=Y!6c1v|{~y1YP!i-rK2;%BL@r%v<{L7yyGC zUuDCn3*Y{pZg|%`U{eQszM4Bp%T&0MR2Vs`SWCdJOH0B-5i7MHWa?9;=n2PPo%p<$ zT`Bg?;pnsyXqq!ayXu23)`4Vny@J#pP^wUyce`n;k{y>Tcf*fi}+c9b>Y zaCqBIXVu`!e>8|9KqSqbJG}lnvg+_J)VeP*`yJkl%(L#`kLbQpYbS3~m(MVsp-mcb z%xfQ;TvS;XE$*EY4NZ@oL~_T#>Um-6x)soe^~nxbK@+|BJ}2W4 z@I-dU?zPl{W5I zYOoxPh1`$!prDF9{PU$h*%D_xkUh3Gkf*uB1$7jU;TTaI>}H@gmC)D6OYONBhfK~@ z)$QvA;c*Z{T((>A=)#uG#|lsK)?bl(GeMQWXCgG_7H`HYcfHtq3NT!{ogfUW)_h*pp{@4+(MD{3D;ahWX`uKV^?75l%XP=6IG#cV%UA6(O$! z7X9q4x9n|9o4CQ&_Ukb?_d&XUf&Sxn7tmlA=NT+>AAH2TF{eBre`ixrg2D3*wJ0e} zGQ&jg4g!Bc3`Jw!)e@FcQ4v7fBTuWdTYd04(j}168^fNfN9YZ>b^ac1&tYi$(oF+a z9inT%fEvKs6uZ+y@mjnU=^VM-1)W8oN7-XaVIuF8qHf(Q$DHiD52*292Oh9b~6)#f#a*~dd2A@sNFi1xF?K~sYfS-&^J^WvQ=== z+SMQwCF+EF1_8s!{!>wc>b>yk+%_R3F#0i+rG}bs@Dk!s^>k*8dEVfAC9(u*v<`qz4YG#ji=md`!?58U~t99g11HrTc;!Ip<# z%K+~hespseR?lK4RiaznT-++LaVO?h=3f6gw#Romb8452p--7Y&hYDf*;~iaq}||P zPRk{LO_bsW#qSVZGs1ru)^0PT${akaFUM?pPO^meP;ZyFf51|oAh*%b zBp?!qPy6C-gFskjvzovOP}$ zj1<;Fv@9&Y3y2h1r&INW{y)nHq-eVy(~%vkp)DeK(zGG*2@(WQXfg#=1Py`q_aOb_*@{EgGAGz*E)3`eDW@?-}5UUelA&%x)*iu z>8FaNw?jvFbWu8I_(2sQDnJML6PrZg-(RYFIKhl|#?&!El(G?ULV5YmoWO`c)q_Xt zOy5|xf)I_1>@;fHYcTOe<2Wu<^;eLD>*ed%qEdy@v^!5%lkMfm9Q&?{Kt;mjCw>C~ z)sTfgDCRI0R1ds12@^o-OJ1;i{|$61I31_H@TmVC#Ccxb^%g&2lpnT6s;3`sYo7yt z0A6a}|8=vfgLUsqwMJe32QIVko7dZ}oFhZ9u1GMjepT;ccO^Z-RduWWE~`y{EsG=L z;y3*s_?r;z$Iio|VjL;pi}fe8aU^ql+^W$^klFP4(2x$#F~%+(?2BQ$@7eI#W&RN@ z($UMEP~7g1buT`ST+KrJBw$Gntf8Ygko2}=;n54&AGPxFf|%n)|!|y?of*b&)Iql=hWCY zX<+um;y?I?nHxKhtBov~!7y*of4>i8O@DmB_3}a5Wpu0F2hMA9n9L|sVC}K^Q<|}@ zfP{^6^`~ky-r(E*bYTW z;h@^Oq6w#jAutKpnM<;Zi}USKJra0O4HYWEW9WMtR1c8y9i{P>ga1~VA-rFpleZ^9 zr9J*2{4)&@1@1N!j>W`|fcHH_lVrT1dh>hktSWK8sQhcv=@T9>3&IR)UMG%W<$E@+%>%>n{U^ zO)D|U8L4iY=z8`T8Pz{XfZtj5WKZvm1}0xd(A=!lWfH%e*{3hLro5?#4mQWg2WED& znk>-IdHf#TRV?5PT*malcxRB|>!t$_(nu@gsqte=Wd4>VYoLZC3GR@_X`d86?j^dTkI_e~$ zwJM#*l>b;is_Np>i6Sh0;|5sNq&9;egx+8hbx=|OV3BFJKC}}bDMqOMhj&>1n!KNf zt}mz%`UqukR6pQjFXyVNvAz!6PJcq_U%!FWysSWUU}x<1R5CC8kax=WqoZR_*VYXO zuuxs@jDe51g)anM0TO#d@8W20%s@$^iXOOxV!zyU8BewYsr~kE4l;=$hmvY$@7d#w zQ7|9uobz~?Il>KF>J#-|K)ym89{Up|4~T9dine-vypDLMPF92!uoY~@* z>8yNCdhi=mUt#F}$Ot2c2Qo(Tap$p{7u{|~Fi+nc@hFjj6CltyBW{X%K}DO##w|Ut zhoqFi=;r6o#NP0n!EyUB&P5@>#@O~{p?t_3!Dk%8OjN3fSgB-Ls-H}T!krsMOSFeL zAOZ3j+ouV7EZK9qYu+M+HC{R9a|UJL??&cll*tt3>s>_Pap6LQn#jRUAi_=X81g`s zax?-hc|DFhzu=a32f5yvls^kb+VJM@r~n!t??&~Y(#$g#=|}VnZz)l@)J2(F>@hFt`WqZNz-Eh(@dmgAT3G7XFBs2D#DYf_M+Vj z^&iji1ock~-m>h%jh+85Za;kNsD#px&OLj@Q3ah4w(zECy;dWA|D3;(!GWk-jSBrM zPYwxug3hZY86Fl751M~{@3-Sa*A{517xE3>K}ISV@}+;kCsi5*9)D^DIwU+d zJ&~LKC1m51>;A7`mSOE2#tUDEGsZ^w?l^Gbyg|MWI4j8%f=FLrou7B#c>Uggw>-}> zAHy9TJ6*qemG-uR7#8(~Bk-LWOW_o_Ne&FuWvjfa7Y&cz=W1cunxeBmV#;_?q$zE! z40M`0wPD?T2+RFO#scx)ao2G+H8RZFZkkV9K7GwD{`4M^SLcV6;{mO{tL}k1HBIpz zFCWw@Q2myYv!h94Sni?zd*CEwC!~F4&zv3?v>3x<2edKP*y`%~+ROE+Lq!h%!MivJ z4^&7Gr^5bg3U{gfApuq;U^gKcgR)U8!IM1>nux4lZL$pyYP>|hIykbLyM(=-;0)Nk zIpGs{yO)QbgJ99U>C2?uLW>FDG)9DjHTj=#Stf^$R7$lW-92@^1IdCU*3PDs79(%Y z3qOy?2;uz_fEV8hd$K`|8s0cP15H3tAG`kA6M@|~Bk00wzoTFNEMS-kcP6j4D-%ZD#Smq$^F8uy%)ZEtPj9`N-@E;HKn+FHN2a#qUn5$~p+u1Rt<4pj zS)n||u;a6F7v^OzbV8^}j?Y3|FuS({pDpG*#SL2kEly=6@bRyhsnhmtax12LH|^hX z8)n+t6(hH-mHDqe_4~eJa3t`aPtm(! z2W;E~L8veo2ZCr)w^q;FfR<9XXDQmUz`zMlJq0+?;OsGaK!gT^A1DVs89K$O%uDOB zrR2W4aRf8D3;Y7bWhDj^Wy8_JBn{pe-ZXRgBkIniXUiwr0~C8aOW^*57U6R?O<6$| z-9+UW|J~4*=KLVVg_^XJW_3RqGSM2go&9TldlV6$Kw=Aku{3+Vbo@(4$n?ToLhyWM zB#W3J!e(RgXpk{Flxw%^_HmyvF6C>YRqJr=pQD%-md|XlYpFz zb>~WHDc2M)q_fv}@aq<=-ySvJ7b-MWlmsuODwp>-sB3-BcaWr&dQfSFQ-W@FGBT~J z+avurtli6GJFZ3EDR9ST?Pv(xT{XxAIW^!bRMmzMxLOC6 z!pJ$cS1nvg5Z1Q_fl+o@NYAp@1nb9xaNi?fzeB>e1wbp1IRJ3%tP_>ZKRDEvoBueA z(bU9f?d9G!r%B*757hlAxn1V3h3*WlpWb_4yPtRGrteMCg7K&Cxi+jHEmnmJ(c*>g zlu$HX%EZ!U>Vg$dAa1}l9C>|gQKDy$w$02jU3g$W3MJHx!3`4@U-du3khju?L?w92 zicY(s3E1dLPzS{L(a6nu1li0iuo<>;S&9308qi&m!W{C{l^Gg!Wl zb5uaOm%r$r8X&|S_WyWA_Q<){5D>$Cm>7 zksp@|v2hP7w--z8z*#j^wxKv_^c*mVIfL)WW>XZ3@a-_7 zI}yf*pN4kc!VMyu^M6TPr)^kG% zfF^2eyz5#gvz(FY42xZ=d=;@r_yya$J`m=dy&h`=BG5NNBs%v!uAMvZ!K#|s@@L}G zsPrxI?s<*JuafgM0ug3Bs*0(t{qUg2hLqpP_4mXTH_k=lWuyg7B!DyoBL{7mSgZ%;9kjJMJn$G znP)LgbK=Y>Wo-|<@Yb*0kCU2&n63)VV&RdOYoUzOr>&8spZCTNanvGj%o(sAPXSPrH0Uin&Hd@8xG1+Bl~{&YW5Ly->eqift_Zi_p6`c)1(IH#F=kY8Dx=(9V(^-9l^+4@ zHu0Wwe^Fe22Ri<&No^~ofVJiLUGsPK-;?MikD!`ARigoGufKr>E%H&$jXWXsWx!zPEkmwFTpau*`Lcns2 znr4tY7lsHFYnh`yX%un}R`8NZy+Wju=-@(3F(US@9O0a}@K zbNKJA+3JY>YeTkA;oVq}qTEY*ZU+E!lSca=rPoVJ9|a_|Xp-%6&%zQ!Xuk*073u^P z;zP*q#Fgrb@y`zfZ~cP)yFD6n$ZY0QL3g7y=_}MO$Hjld?3@Q0%pGCAm1bNg0kwLF zwpb89c%i;(sA>*@jKVjAd~`6zhL^#Gqg9>gNU3V;)XIYgFYY?;FRc=Xec`NLKmUy< z@EDGMac~0&VQ3Mwy%%|@`%n+BFvRQ3^3>OF3=NuuAjegf&ny^MVDVYT{b&n0^7U<# zqg>bKxuaKlY&N-S3-q8$?cfrnjX~Qd;3!A+zWO1EOqPGHf8S7|C+bu`#+;U^O<`Qs zKYFs$FEX4`PaVn9%pb`8ar0oFF?Bz4bA3t70VO<(Yb=SC8 z`v8OaOEgC1vUp^WY?E+Rza$-y>Qc$~bEWag7AFK&~7T#rZ-DBYO(NrABu-KS@=Tr7&x~#xJlxSwT`cA+5mf0 z5(1t=q)36E1xMyJAz)(3X_pJZQG)DXpB8L>1pfK83DAAchSm&0z#7HtX5tCb_6&MT zr6n+bnsJb%A%j$(95rvzugJR_`#|9PtVZh2V^TubhqU7X+r%ZCdkeKYYX>8;tT5mj zFc4C&mz;=t{rNP3b}?Wj?1u?$#cpg!7{V`6*emDbP-8?;p~#{2L_~xm?h@7B54|pX z+JN?Tw&?B3d>Avd}_s=n2BxB5QXl@k~M1Y z-+eokb3VXxQlZy*p$m~!YTw?h|LDUL90uLSK8!jxwZG*%8*jq_i$6Cs*X!E1lsuPw zinTR*?8MXO-X^Tjh*Yr|tl_4^+7KV}_>F(a`srtr6m&2%Cy8Cs@4ISzpJrmo8c)j^ z>~w1z-_+Z+3(!X|u2~)=(XX5h-GifV86v?}`Itv7Kyi&Ubc_D49*0uRR`flI>#EL` zPh?TPOd?Uzi%0fNnI)mnk=1_}5P9hQ*Fx?5DDKm3?=Mub9oF9>!jjd8U{xVmPltnEYQxxOKGkQTg#38)Gjk0NXpExB1K^#+q7ol0#fH?=)|Ifdho=ErgARjDS<+Tk zZ_w)v=~<9L$%}*Frk+Gi?=VJhoC#iHzM369j4qaFZmW;f&7?kY2)mI=fN>Gx?~;@c z%PE3s%eT2Xhv!jpG5m}BKY@&1_re|YL8<^ISWm5H_0S2V>8b$DD&FlasQ;Z)c}fN> zr(A3D!gR>5NbdsOym0!K%BI!nyl<-s$3H>1`0R-2sQyepM(OdqvpSHk@gT;{{p`=Q z@TjObiHh(_kxDP{MAYf>KVo#Ez6#jQqtl4;)fB#NtH53?15$&*6Y`1XNRshD|AK#n zg7fm(Lvu;mx`pYIgbnDiGRL^QI#wu{$NI1e$NlY_+0Reonk|Vgz5A54M;Hct7wW9J z?g&S7oJz(JwBcQ9=m`=>GC{QZ@|~;A+X&u({GIaO<`L4<<=dwk3 zX6)@@v%FkQiFp=tIbe#Sz?u--3GhWplthc-V~0bV_(`o0ygU@m6)4%1B@3_DJa+mN z*T@GyMwex(58NWf{Qe}&J83l#|3$B@0sMytn`0}ZM#dWp-b=<*#0s~l-^YmUOBG|i z-pz_UdPsp6F9w3VAzvIYTd`JxsuKW)mmq}#*U6xJ_!d9s%BqW^2JR={3A7^3oBx7d zDAZN`;`br9f^#?KVx?)jaVV(~sBgQ?g(RUXkb@#Q{$1FfVnp%xF~}zo1tGX(F}7p( zSh?g;@`N;4!?hnaH+g$#tUAl;ZNw21!;Hn#HMkGQD6!;i*`G*+KicV=oOF;g4ZL)8 z22mMqJ$8qyMxGcIjf+7N%F7ZMat+e}@1zf2AXH6oZSGcg{A9krqZaiD=qI3*VLB&} z@K$G>VxYy!`ngbKO!k%k9tWZAwtmz%y>m|)opWJl*^Te;Qu`O&HcIU_!?1zu9vr=} zGS}+d1hIdMT?f8)HT4Fm$C@ll!>TLlQ0WU69eX9MyJ#DT)aA2~)R2{}_-?JZjrtxh72NAUJ zo{>YVZKh>61{q0j$$&qV?hm$*tBpU%p>48H#8b+}Y6T*V1P zHfcqs8m&J+z5FN-OtOg{-Ehvde&F(|{v!S=^5|F0`-U^U8Qs;dZjtH#xO$MA`dGSW zc&+vkKTTIy?GC4(1&(|N7}Sw+?k1xA&|Gi|WzbpfWD`=u@B7EJU4@GPQ&1yajf?{|6$vPG9HHvI>B`oI*=t zdS&&&*|R(Tdiolm6cvaFrUJhv99$d6L5f%oMAIcb#{c^Z_g_T^o_ZWl$MW`U=F|A1 zYa~8Ey^cCuW!|^*&OnSsKH6?CRmV%8fBY(OVF!0F!mc=7WoUkVhk5caSmn61B*=4Z z=x{c@@T&$lElClQ5GMsrnYjSRY)zL9bI3p!MHT~fn;S-sn;WO*)|Q-)hnn9~z7;Mh z13v%R`e;Pwrt5N}x`Sz_7O;$fii(SHwCL`-7M9$GOH*-IbuilI%5FAv@D785LT89m zw=HU*p>{OKF*sdBClnQ(mtfQ{&FbPmGznhJ8B=`_R3ua>gddM#$F9V zSSC+Q*7<&OoSak+V@5`+p1<+q@(`dkJ+2Nv*7`BX_BsLa@A&Ty|M#}sdc}3*ul9DE z15;Gt!wC&+bt|=mY|NQXh zpz{R(`1J3H1by$W8YNlTSen22DTvI7?>pNAjLQWV?JdwTZ+c^^Ae3&EPR-39nTJXj zAr1-n9o^6q(P>NOqYtLNeid!xCwu*M6@;fY=z|@FuretsZl>gNeXgijiUxde`Z#Ly zgW6{OsZrv*SI5gEdf*+Z_gsE>m%`Vxcvl_neobH^gkmmyHq!oPbOqrl5tA_K2f;Rq7whQ=M^>xF57P zz^B(st%wOnCsS)mF7l8(>mzEuRz2y(934Ry?t76TSVjXHFHnKC0jCG;oqgrJaIJCR z1KoM=iTU}F8HckF??lw0Gg*>KZ19b$mTx1Hw7N`TbKG#i2*d$A&LE<3ir3Z%4Ss*& zLD!D`cbkb}>63X|_<$ojOWu6V7kq3B*u9r{qui+DJ&F~@>~6e=l2C+w6guKV!K`5R zS2{`Hahle~BY5z{x4(ZHxq5Ftntj`?z4j+67CCzW{)PV8`E6(~{jpOglOVOYfQO=f zZ&Oq=1q^}1to<;eApnR4O+P$u<7i*(0oywHzR^Q=-%s1ldOS}30y4R>KKK{o%SFDh3tTSi`@~NZrP_A!?tvx7DA#UQ zJVth{C(1TWF}fVGO-#A8MqE6^Gk>XgLAkd>P54(|;}KqKBI8tjUHltQqQlvgSJhsh z+kSI7P4T!l9`CCHO${c|ZND>H^?@a)#IJL0>8rLCQ;Q}cQTjF8IEbba^;T5t9mJ|i z6l&k(pdDM%WqbW->#AI&sGclz`z1-L)1p%Kee53-;h$rF(at0ijwWykoDN?;Bgz>t zZ|EKlANvN>JF8rF?GWD=$F%L41pB0h%{cGy(G<{}FPLdi*~%e!KSWjB1S|5<2+PX# zR#?!Pz6Z6xys|pZ$(4;Qgm5gXVFdwyOn5qKeI1x_!`qiH zRzb18gjIBK{WYAWK_Dm10T5{Htvl^X6?0*tji=nj`={R2*2s$>AA4UgFVT|PYnSs9 zN(;;RN+Iw91bX=XuhxrUw=igOj9|GMeb^-b$Kj4VT^XCgCjuObMC!qX(Hi>Febr0f z+PskcC(lK*EKH1Q#@V=WG9vmN$#jf*io?_=vkKA>0a;+qhrfd);}*||if)3g{vkcc z0MRyzOQ@)$I^;-RW}5yJC_r_@P|6c{Zm@|~6H*PM`!=n^M+gRd@5AJ!;r>^^)!uHk z9gN{~&iIK5ts9Mg77Rn8#@qBuwtG6o0%x;L;Ywz%JThF{JkQEJcxL9E0Cn%wPYw^7 z%O7x~pHm0T$?h@SX@I&=4C!XlgApF#_F{l~ z)TWn{3<%bC55-VAe6+B@NnNhMznCBA(~tz!GR>C|d5jcQxpQH|spi@wN_pJfH+pf} zq%tw$xF(66;s_hQ1sYJz9zYXq;44W#zgpsfQXC%o#dI0CQqwX%oF`BrbUc&WL>Mjb zZ3u(u`*WdEsEK+fvVu7 zJuIM<*vxW=~;w44${ptR>^l7RSe3c-k^73yvJf2^66B z*WqYFfj2HTs4ULN3soed^Yx*^7mtjXw(|2hsPFkz0i8q1mv%{GbYIF_GZzGRz$@C6 zqa?PSgW!j`aGX4^HgK^q6$4qn$(DtV;L@&tIahRm-(xLPy(X6o3J=FL4)`C0k;@hc zrx=2Spz(AhAgSAvy`8ZA>)8(-!8RVWsfnP^KM0$*H0cARxq7M~J>TrW@o(>HAE&(D z8DyhtnLg5IX6-n5{>Z>F+_uNzCXhuQI`JOz^4+u=Z1A#&UUNYLAH6MW26P+tDJm3#NSVE|$)& zhu$p;FfXEkhG#N$X62PIq=w)jF!rmX3`wmuCx2Lv@c9hK+4h@jzzQfb?>4QYqi`vi zx2)vTrrXxlv)wVRt2OI*z`vI+3cQxbI5w;F?r2%bm92|hQd}&zNH`pNXAdljPWx}7 zW%T{t?@2xs&Peui_AiW84}nKezB}OQq~TArv9x_XZiUzsK313+!l!zS zefiLGU<fqqM4oI* z3hy-)Ut4$6Q2e=Ya4)LvJE_eA*ez}1PyN@GleD!{Yp$p`i6Duy*2jHU`c7P&s^!c7s)!ZSVrX6H89`XZs zk8((eN6ld?Av<~M?KhYdVfxhp-Zaaa{UBv&^oXe9x6RF0MI#U29}zZT(>(|P- zh^6t7yo@z*U_tabRDA$K5ir0nE!Ki>5*$%S`uXAD3~A5-S;~>m3a9*h4q^7tj>}nG z0h(O29%hip)~iTT|DNMt7W$SZevO%hygi8UJ}I=lo^0`(Nzvp|S68|p1|P-{avNf2 z4>mkh_c8m9M4t)t%eOl&K)ol#&BE?bW>^9@fn4;&n;J>O3a$GE#N~%CocC>{wgV4; zs)j#_++&5YR+1!1Nmz+B@ct%=u`w)BRe(no3(+41alB%FNel_g5DnZGE(DjeKH5c% zztT1R#FNIUV#_7Da}~XqrQ84k!<&!)OWmp5bBhE1yK})_cub$CpJXkRm9$$Y;5Y|? z7e1W>wc%35a2jL+n^H!_PZ*-+@v0KVIF6hBCZ_z?a zO}m#_hN%yjH2m`c`Jth~4>_6UYU?orJgfcYKUgV88Cjd|E$!}oE!a?<^S=XrL3r&! zcF#cwI9uPQ=f0&P2DA5NCI}U-#zBGN6S|i;s7UgQ@%N2C%TGVLeCA?hzvN#R&M-${Il<9AumO|MtHhq9vP+96QDb`z~JSkha>Y7Qls=5cETrhO#!0N^@1K zOx-_&YKQyO7M@^tQpNe9doK{fpvlOsf_j6NPS&RUc+&cE^phkyO7_6fYtf#n1ZQZ?7|Iq!msFyw zvjwrVLEc?3|3OQ(gH?*IRPYj;+R&x^P0h=F-KHWctFMET@L1%y`Vd(hBjIz@GnW8X z-WQ=IXD3$c=p&z19t8v0AmEFXHqb~47bPKyvx^&jQR!msZp1SmXzHSi^9zI1Lrl-r z`dYiKg4*Jty;4*NB;S(X+BoD7i1E|fmNe15P?B2^t@nuQ(@2oJ8foyM<{v(RN z>)JUHq-I*>a`oXYTl>cx&1J5d8*Lsa*i(z(R{v>o&w!Vo8?y24FF9jGh%-J?zX90i zfo?-JXxkb6bpSO@d6<_~-db_j4`kI~2(T&?N0l#M<@D6&<`pB!9&s~&ZPu{ya$elk_?TUiz;-?ph8z`g7aIy$xCJ$6C8{Shj+&sp9iXq27i3AQ=7Kh9JgaNS%P60|s{MRGoQdc5N((D#JzSjG-3}@tD9cV9zJES_ zfYZIc<0^$A6AWDtCntb66iN@8QP;fi$`CAVyRWkBZvhF+ok)j$TAnzE>TK7cthxgy43wK^DKf9xRU*Xl=S0g4Hw(?GL z5T*?Bpx?8m+}x^PoOfTMXl{vzYv*kckTz>f`yF1LQ_ZV5z+9G3(+`;rIrL8c<878HA<4CLQ(2|E|zX#IUccd(j~pNidB=mRu$%v4nNIDI{1}!qz%jGz_u*pBA2r zUH2%;Oy7ryo^`>XLL#Qix9UhV9dqxG&7O zy`7m#nHv+y5MgD0SLP&`*}`1%o^R$&_QtWc(&mU?nf8Ey=TJV>7mm7l@@3r)=OQxR z9k<==izKO&(Zbdn7j|sd7`wi`gsR^nKJ%9zcOAVKSlz){7FlG#a|30mgTeM*6qNoe zQHs8`f*`+6)c;sb?yLDz^!!pvuf(5FS^RWCO|G~iYCwV~l+7-oktAtGHeC`5)mq9Y z3u8%YO?xV}*GcmeRM3iY3Q!e(9{KT^uwCs=#jQ@bY*VW*i*iiHQ&))AsmxBH|3D=lqU6u962`h-RFG}~OE<7n#lAw@<3kG}oGd9L zV@G3sDiDbfv2Z2}llRIF>4)sg6NMzMKan;*pXY9v&Mv&eZ4)CK0vf$oedH1I%m?a8YjUOBQ3tAewyQDpfkWG`g#51@c4mpDJ^J@wsQr!VZlDiU%Y z3i}8t3^n$-^Uh^g|lH8<9mHK&9WGhMuB0;nJ|2`RK`|{N0s#>jT1L zV|)jsy#a5g>a96DFM^jG9`BlEiBxYkSaR|Zi41YXl8>E&%dir+z5jf=MB`O7O-Foj za8OH|Egd4sjGHw~fiK}nCZq||f18tIls@a#i}>3$6CSr^q9tt@_m3DyT~%G8ooW;W zBpG~9R5mJ2iC`?2^wdrUjuY5SyN90dgVeIf4;A3>^Pal{9>qGqJzuM1eY@Uu+RUcxnHq|! zeQ`CiG_&?I-7dHw%%6U{;Yic#d+C30_j|uPc@gpW`X^kDUB7 zIpLnO%iltf-(zdSfZcyeFya4fbEf*uVGYt=tFl+cjFwm~`mXbCX_AZJ#?WVl*fo6} zXzYEGdfF(w%8rME&h^|Qu*3P1=6a%4Un%B@%!0DbpT?L^U%B8DIF%1u0y;2F(`6uF zLV<&R`wYJ-u(0qtz6Z8Cf+C^kUzGm|7#h@K1R1v`U?StK30^M}&C6Fr^#lG4pI z)9)0ptF`0k)BV==_asAY2fT)Y6Z8WwvIvp}-*nDuHQgos^eqpXv2AL_tlB08OkH6W zo2Z3ajw6cJ@EJw42{XLJV)tatmZ{j91${lfWz>wm_#@GSr?}MH`26-k{-whL`wl>@ z+1nTPN)yyKPjY}8#ma)z;znK^W;FGEL+XDE#OIo@-P~syp@1{_3wZwoalekD_8{&B z9?kVF*_3CC+5!wuX`0JTA?H|Dm^mQnf&nmq=O^KT$n;+=t?h!VN>rA>>Rql==Bu1a zYP8tLX3@JEly{yTxreNK*~91NOD1(ZPy^7b=@7<~X3C`n+g4&-LcaVJH;c{NTlF-= zKDtlU4m$dcc-LK<3nH7p=Blj=-Qj+vpf@XX&esQ98e=FZvBNrWRg(+kHnveSUuOXw z&53cZ;Td9n$~{crP^fX7a13R$$y4c}N=oQx40roQZjyX4vbS@h_4yW6H@F;=0x?1oZ`tV_;MUAR_yBgHfCoSp+g;y{ z%^kBIH3MWkFfN@BSrX&}KqCBy{2g6djeosV(?YpPuEr76tSIP{JAFK-6ghkD_cc|! zPmI*u)e6+6iB6J^9@Gx84OAJ%kU@gz;`nzPlAL+T$WLOV>)eAkwb(^=)^f9T7)Sml zWR3L6>uno#@)G8O?=4WYTs*rn=PIU?9XdDc{`5k+mT31=TnS5zSNZOHx?9TTCKx}f9vg<;6G4aRt zN>gO_QXb|Z*-Gs=H&OC<0~m>9yjjk#M0n%ZC)i?Q(tF$w*@J1^m$ zcuS=0f6VaRd+NE7mwG$7Z+A|$eQS@0NXn2d4^5)gOYA=DVwUl&?&x6`oo)f6#ni%# zts{C@efA5Lgsm)ke+IPQ*N2g~5gt{{=kqR(^F5qXlx)cL(_06G=Q`r+sm4H((d0^0 zpdVBi2aoHjLC?AV9$pZyKkfLNe$Fg%bx+q+f_Dpv40|%lSjcu*wrXNihFK^Cl5&K93pnDBj>+Idv`NOjc+%lhnk)`2==UN z7;-jyIl8ay`2SYDLZe7cAq{VJjpr7q`VM-Um(9ev&xfpMyZ@Z}$0%pDxzoCqO@AnbOgJGws(Vtu0o4iQ)tMBH6Jl7+b2p`h zKdfdZe|rjC&hvaoJPKF?6L$pe6p`pXS5!TqIP|;MD*5S+$p=#6e{+1VZ@3W0uvBNG zPvLY;a{02FHkC&=Ex=FQ&)K+8JYN@$)0}?3Bp-(KM}$Z7uTDNkeav={Yj=%=?>@@X z@!+$PVW;M6PeZNl-%}1gdsQUr>74aw+}5>}U4@hav-wj)sk01)hg%t6mlR&hL9`Xw z{(9B*Zy566%=#fLhH^fwV;E8Rx-Nv@#oO;Wf&mb1 zw^j-cXZkuhq9)i*{^-%Kje7+-ct!<_fNWI z2@v>EjfZf80Knc!_0m;_UVNd$cj#vhgHqF&XL@%HCt!kS4E$e2DS4B6G0vTK{v;~z9;~-E*jy*1vUzT{)=G%^&2-0*v9Wd@!%r ze=U)B5xKEmlV0rVMknI=S?iML0ojYi|3}lAxI^`Zef&AI+n4O?*kuVNdra0!h3wfz zh)VcHmdG4?iYz56GM1vUwc3gaMNw3g5F@f>8T&rxo%g!l_b-_1I&;o*-OqjB-_M7l z*7ISoR`z!h+xKJg5M$qZsRsxXg89rOZBgO+Rt+zH4QT&+oqb1Dn!lg7-qdY_L>f0T zc>t8Rs3&yUS2$iP8(&|hgz?3C&&3~_uVhCBh9NeWHm2grKWvn+(XL8U`-HJTI{`qX zKbV)&SAruo`r%_aI4VoZr5IXhys` zaZb%uueS?N=FBN2=f<5ECATGZm_JDpf7ZjoB87i(Th~A2GykK??Nz}TfV%g=&Zy!5Bnmil z%4^zvG_A;_KeU*7xszUuK*Qek()E(`R{kHBm$^o~*bxgk@&^K$ZZ=lWi-ZMcjZ*5| zBO|)(b;q<8qPr1KN8jAL7s0;mZ(y$Fq#I~`!@~!h2(~x0>Bj^}A@IAJWOw*;kl=n` zE#FVXbj;~R23m6ivXx@Z=ZFBV{^tZ(EPkk|0|Nq1=;|!>X&&|7C{u;awO-lCkaDJ= zhKHmjskEhsLb7w{iW~MF$kWJ_wCM3;UUca-XT28bG)XRPpdH_X@A(M&lUeOq46sZd zDFsxDB(DgZz{SrA=!ftm+X*euLtqh^Z9lXsJR8i!-Ih)6fHJwaD?ch`Px#mJ+M(%2 zFE{_=4>!v!=7Zi1KPFNwERNBRv1+gb)>udr4Hul8YxSRVmJ*BGm_^8Kh2C-@_W(TQ z%api_6e_lXH$0nu@~*s)!x?45kNDl{8mRo#SiqSxa}4HHBiQh$62ndaSj8k2p2qp6 z9N+^RXv<$GNXJ_ycrWWuO_4@NZkoph0QwUNgWceO?Tr6?x$Lr=$Vf9kByY7_?8bFl z2RHNQnG}wwzvaL19%W9?od9m&Fao%?i)fbl3p*;b&gGkByQLvZ-cqoA^K0*PR zm0CQM`7tvnEq9Kk^92s;(fIwaj4NcJ0EFH(j*g98X$SDa+~RG;z0LdSA*&Rs(FF@y(bm=P6Rvs)6yf#0J5l=9ya%9uar&U@6~&3*Z0(m;iM7Ea z5*izWndwjP!k;L#R~Vwq5FG1$lsVxUH4l zyMX4>yPo2#2|>O9Jn_Wl@<~RLG)|j<-dX#%KM&Ksp}QnlPeY{53J@*93L2;*8Gl)1 z{23E1@EGxW2%d9kqQIf{#RcIS-lP~2LYe@}r&D^?Z`YIeX5q+}IeYJkZ?lI5u>{Un zDH-pTkWSX0d1Tp8mn7Y`SX~U45U8f>1Uy7IQ>p{Y`CILr;wZMAB%h|%xLAIUNJ3@y zoEKnugaiMkOTbkdj!7oXW9+1Kvj7=rz}@6OIy$uP-?0L`zcH7sU7NWr1C{ zw+OG86AN~$5BHOj z1T4k)(t0c9_e|M4cdU=(@0tjKw6tx?Aqj4TyzcyxsmU@WInWb{bO#~|D5D}RIOsY2 zai_=i?%KWBWs$=qoM(~rns)n&wD9BmZYJC+l~A*7&?O^JqnGxPy+Rwe^>F= z!R9N0rjJ`$UfpD@)rcM8psz5Z7%?|S&u~I2O?r3#e;0ru8+Jpe|EFj^Q!@89ef9dV z6L-WrcWn8k-&$-nSnzzaYzCa?W6y_gdZxItm8`KQ|eDVI1WhVXl_7?B6ywxA%%&A#j=oU zQQ+}W6_D>IZVqbf~NP zO*8qR;V#c90@2V*Kf5~^v&&bD^uHWzQu&1zC}_3otoEZ=Scy%I?bE6P>?N{0;u<7ZG3z}k zkSJ{X!YnQuLgV2^$LAj&=u2et?6m^m{^yvtLzQsadgjQ3TRYMpX1MbG&*xkw%xS2b ziabpouDPu%vD5#2Z8QBAC(&ptRF;?w_1Rz{_+$&k7pRF+^N^--^6Jt4<5X6OVeEpY zc2XuDO)4GToOA1{_7Q*;u*qjIyz5c`R#j(K-$o}ko8gGL+X~lYXP}7-Z1Nkvre zb=%k&4c*K27@ohKlq(x!Y++cn(7?N2Rw6%x=U&R3wjU_1X@XS}Q9JgCIbluXMGy++ zsW-8M$oj~0H_Yu{ZC-vKr*B}D!&v7cR(W$VC1L#Y13q>M+8!D|ZEREn*>Qi?(DAMl zmDE9v&zpP44UkZC-q)Cr6R`l5=B;n$G2r(;K;5Njx-2r*K62F|9;E#gVJ}`1JwhjTh-{x);g~B@2|E% z0aoy!cMb2Ms?lYHV`^iBz#1tQrlam?g940zBg_e6r#)?oQVZ%oW7+pR876E=yp;5;u8N z_X|MEg2V}HyC1SE3dOs)fIt$sjU=$ab1Ute8uiHih2XFqE2lDqPd$f?uc&CqSfL+KWVfn z9CR*coDw6+pj9!~QUMYy2K(+-mj&(rmvR>06OkKaMNC0G!%`ocbA~zhByMfuf}(lxEALP?Hx3!^ddV zDCt7frQC&5u%*o8A-Jvv5+bc8n?e# zM8L1`k8U`%)}I%n2%Q0(TLI4nrFR#z_@|xr?wZ8BN!jyy^YL^BT}BgJ$MCX|kJQCo zcr_(0OR+f`GsoHJ^BU1Qv~DHQgM!&E&paWL&w!w;KPMie&7?DNRAn*8B`dRC;^ii- zPZ6DFeonp*Lw4VMy*~8ruoiH^k&~PL?$LCjA9-Xt&cjfz0;d7}x9nNy02V$4pXa*; zE8n-(0=9%}oL~UN9_6N)B?7^_Aoj+Noc$HbSLWE~rN3C zYi@ON9K=;7x7|+d@D8>7(ty2An76?lO{DM73(Xu#%yq_T#-zfIRpHXn)bgwF`{-1f zqClQEe0)3duKlFsbB#{j>xL+e{&Jk&9H+m4ippWo7kjCY!rb{Hyo{=kJijh_CRW1< zs9yJ3degm}L%iGfL7K_nZ%=jf+N-7?fOwfTQu^JyAtQyd3=xf&Z_`$%zdZ%iqWoDJ!iiO>1 zqzeY0Wx1rkAPxA=+^cFNp*`UEve&t3*nBSKq<|nae!+wDHXJS}OgePWN=#6=f&CAa zE<_!BxtQQw`(O{crVcG(TWOR*seK<#(!s?)mN;M?(f8%EIf?}jBIUL9MhK-ee;*r3 zx*y$e-oTG%R^sI;BO)iQg2t6N4zPyOXgE*I_cAtKIRHykvNgW`!vSmi--Q!I5OB}& zR4T`2153%wPGnK-w`^pgb1{2><`he$m?^(Qh=}IQxH{|0{(9tz{ktzsQxpEe@os8q zS%_y>G(RCGD`k|?lo`nx)MCQgXMg({`Pm4oR{&xw_)D3>(C}WKm_vb=rEB&B7z+;q z(m=Y`BF9^Iv3c2jfwpZ4bBWnE1C}7%FE0lmarncc_Gc8Hiirny0UQjJ()cWf(ziq;5YYimh_4(805D3Eb3`5=Lb{TV$5>wA)UMWSVGr4RmmgM5n#ib) zl6_8m@LlKcW3`Wb$`VGIE7!8t2b_3rmY6;dcUFDxs`$sm&2P^SA&+X=Rt=ur14D=& z}*6~X9=e+QtH7IYP z>Sh)HS_UskdAo=a%vZO8{hV*zZg5kOqxVC+I98T?RQKvtyAwqRA3L2l37z%(;?#Z)3y+l+ zYMu;{AZkIP5DfscYr(&*e9IqF)J=lUcK1iL=5ltc%E-F8{-2wAjS$2y3as2-Ejf$*! z>RWrgee39@dcZElO=PrhNhw+qE+0ik7*S2`x_@WA2kueqEO)#Tpe{(+P21gX8mW;N zCVhrF-o{#`Hp?-alcAMEv!QCYn`4uqE+!=s$vp_}r~ikve?H_$e}8*Vck8u+;Dv)t zW^YVcYzzpNA(`V?8}|7RI`^*&FpYjk-(D2$JiBfw%bonr)C+I3;%@qgHhGD)Di?y6 z{)lRcUu|jDVDDvAuy&=nlAbV)k8Kverf8l=2yA)cj{+!{`kIDLX*$bd1x`b@f2;dd zmN#64b|1>g662_&{vW4K1HI$bc~Hy+=DU9E<6+`O%nx5Qm5e_RGLK4``FU9uU`z&;+BEhJ zm=Sp4Lw17ZFBAzho%bw48I=$2MP&*j(=k@>b9-4jySe~XYCf8GEb&W;A~o*FgQ+>Q zmDnA8ZxIymTE`f)LSkZ#8ZE>2c1^nT_+dwM&F|BA`ZxwNm9+4lonx;^2exjMV6&aT zavaE$u49Q9TIH|LUPT&=i1i@=_!uVl-fR}jM0Nb99P`^Enet@ukj{V7KY4s}9~x>J zkWx&-)yVCXbdj&xOWNLR-*3jKS@H}2ywu&93L9z`}Gi;VGmn1A$4=g#8M7HMoQ%Im{d^mu1 zjMbOnEm6LB^OZ*7{@jntG@ENqoW8pU3hf1mmr!prBw%+WxU=ctj;IXyaX@*1?E)|P z#a&kvZFVvr{0scwwx5>;r+;sJn@SF!^d7jG*DwUGk#k#CqW5wcNGJ5gpnx?m;giV)5X5Gz-gJUM( zByGF(u>H&Z9e3Q|T$?GhxhrX@e|Y=C8DNPAWr3^xr+M@uJ(i&C2{E?6XX_62oRV%v;Vlo++8Kaz@tp!KeGsSf;@Gj+ zbKp)dkhnG8GmE}=HiJM*VuM$1SZx5y4Kq1lK8)u@0cS~(zhwl)A=b{lxO~2=d#{6l zKn-{nH`SNc9Xx6^bwei%^vfuRP?ZSuf_hDl@=EE-qD@f~_Qw6+9yHH+L_HmQm6Zvd z{_~4Yw;_-=zVESXj=t)Vd@>FKu_c;8VQwFAR6K#q#tTc>BQ>{-|I2)<(Pl}q-TcLX!op9-BZ6yShZi{%o=PB=hx*9G zfBTc7OrYWDr|)E}V<|p{**&$r6QoqEX@75{%LzWL%RZk`uj_SAf)qa#mC9_;DFq5ikC(>_4a$(cVf(IJQu(xd9=WlYuI=Zm3Sw-(@!UL@Z5igEOp3fVSfnIi$22^=Yv|5( zd7*1;+wgY&YrEB0fulWenwJyUFqaf1nW1eRQUoj{q~J7oPR3H1m)dfYbPP-N0|GL4 zlXMjbpqi5n($rzqvb$$&9g5C@tH-csIV*VP)|x*5G_UMi|F_wEFv#f&J-F`}y~CWu zFhuP~j)fyjuV-wRe(bJZUmUb*@2AY7=Jp`n*?4mIE##s zA=~8uO>_Q{Q>@)v>yhJw&Z7KuUM1?M4&;fnE2_`p-pYmh{N>(|*ZZ$~)en4a#+Q3V z4lF&lQ1^U0xraFRLS$Lo66t}69-;~vN%8>Qxt;oHuJz3LKrY0HI=b)ox8{}yc}s9^ z=rClvZTHH?uXlU8X;iC(nGu5=z|nHskMr{OUB5#ho@;X0^glH*7Qj>6V-Y0j5&M11 z>slo4O06i&6VbKx2p9ex=)LUS7E#7UQa33iA+m^5=-YQKYtkiQ#Fs)L+ul;`8}T#x zjG>g>*lX{NI-B-wI1(*JLY3+q-k3J5N*wzDJ?4F}K*G?(Hn&h*}IT6ysnq(+kVlvvD|^&)mCt z1!DC)C^KvM64%~wT_eJShEeNb0yEZ+n%4Ok>lRDE5a)p;@opBau^ljPlCcQcgHAKu+znI z+0))Fv%0~if~gN3QyzD?zvl<}&&gjc32-L35(ut@6iDUYMf6vA-q#uOP5&su&81@C zX$nt>?hqh%WPR@|R*^9PtNb;T9jsa?O4k?EiEBDciY?I)mIl0_k{?NjhT`#!xd{o=PyEc&LDL? z3qSbC-hA-u795!@M?ZWCOl&OZn`7u&HX z#wHq+$Ggp7CA5JZhWF&(g`+7c(fv;kgKTbMbI>aEY`j(0(J0|`S?+U{ z6!k@lHio7}iS(o4NcDto6Mw|w+)rM}Xrox=2+O(JxKM8KkZ!Y1FGw6t|25XW&Sk%G zatOFcQa1>TKF!}~)r~0|f$2fuWr@Z?5D&h5ZVt zuSry+oX4yDoyleB(DQaMI3n1j6XAW6mXxO{Tzq$qN9B%z*MW2%s-4n|Wh1~fhV1Dv zMZ?B0x*C*2;E^XH_9@l+VWF`E8x&A{!kMe+^&5154+{K4OpxteQ!;JY0FWM7u z9_y;$YKOd|(VFjoXyihqS1330ZDYVcld%I_hWxBb_ zFli^gG$T1kmnOH=p$2{=x&h4359tq8xJSwc6P6@j6(*yvD*@ zk=7OFK>3=6D4~37Pe{aIyehJCx9(cPXEU5~Plo#SaHPkl-it2dc!q=4aq|W)yWhw^ z&E@LtT(b^qeb1?VMCqN?8ZZ3|D2H?ucqUxS4^9B(7twk4_})-4LtqrFaSKn1RN2=U zj96HI)$bA#$3dA~9pa##5h(D|Vjs?FolYd6P?;{@1nj(UrNv0*9Ivi7s>mcW7 zqls^&MzP8Ng|kgqzGfzXQOR_i`I$&sWK@GzgC2b@{__huKHe+w#hh`mU|>|ZWmJ~-sH<#VTsQE`CT@OTqYqP%eLPt>g}`USC}h`W1vYoVs#a;T2M*%2SJ zA?V2(1zR^AeqaEj>ml#coH?=aOl((kW`Ty{)>qw68-;1tBFG78Rq?PCfiq-QlSeaV z`TG8c-M-**+)5mH z4S@x^4N+2=wJ?A~PgoItuC*4uzEDyJ7)#Hp)i3?UW8iDpS+;XGrYIZEd??;|jh%;Y zy7|D&i~sOMN!p+9h2@x`#KjwVfBsEsP%ax3OLQWC!}L$>dWlRn<%tBf#+D!%Y`C*k z)Oj|chs8qmZCYZ75o=X{r-vh#3QO-eul=qZE*5Yhe#d7?sOs{99aCdqYf*qy zwfo^dzFHtcU57{n@}fk4NQ)DmMh$ER8=v7gp4)v_f=uHRk>lcSjZ?hkmdw1>{cP9` zkUW^|{PeDJq|ChJgl!Z#~Ag!#ZG)-Gy2Tqctm7xr-N*R+FnysuE=PmC0`LU3oZ;12lKJ^#$7 zKjl*46&?-${{ql-HAF-Vt`>sy!l!ype!^;n8zAu*LdJ@M^b2pW>VSHmlZzBO{$o@*%284WOgiN-xs*N*n6yZEZE%PB`%Q0$GM_l9m^WN9h4rn(*(9c3{Duwc=Ou~3Z9vbI zyzujavRAZcF~YNyx7Hd-SVFth*FMw&0@T9e<(bMGo=xhTw$<>Sy3{U0#8M>er@}GiT#^({$2`SI}3!^Y*)-9Yif_ zln=b4YMDI_QnT=gCw>a}#(&vHbJyg`(SMy6uS_PCH|cFyNz!6avb5w@n1w$Mc^6_H zDKR|Qn3dmrR|Y!oOpluhcF@U-;A2PAB56mTOx)_g)m%e-VG#4>NSw*Gtd#kSpUz`U zi=zocP~WW_wAWR4>P zvwR0nD;s#UefZS&I`oTyKab(C%6hYdRn-7Iy;My@!rtWSW~?NR9(<^<^XwbNy!O`M z)g!lh))l(m=sjG^M%g9}<2Qy1Qsm~(u@Dhl{7~y)m4!U4N{L5_aM`-ja!>Msn{5zh zE7vJ(ZJs<$9iBsXnbI!YrLH?UetuslfL^V=mlpGB-#R3}w{Wm*@K8@9+EYduhOx)^ z8$4J3{Mo>BQ=5cRbg(_I&v9k&d}{my%ELSYKzL)iBPi!4tE=uj(2x1K@2cS?&|yh# zzk#s!Y(|9Sz&OxQiwz^gTk4^PpBZQ722rDen4;P9SbGWvW#9>g?Hhs}p;_%c#IPi( zK|&CuxmDyNIl@;779k%Tsd3TmDIUxjTJ8}6^lPq+en1u|(M^a#O(~3jE#iZhwsZTN zxl1ZXJHJ%LJWW&x;a8+36@)4aQumIm=NqM&%uog7qYbwf1ivhloW{Nd-v*uTLGChq zY^OOXk7 zyWd-zuxleRI^y<-_^+!nj?UF9Ww#6&)V+>>+eySAsHL^;&tRc{EK|-H3B9#dVtlZ0 zQly2Rj9Y|n8PS8!cs569Z6?iAoN zF6}R0Bz*8MR2ogbjH_So(Jm5FmN@tCKp-l!S2$|5xFR?&_+HZ4_FU$p`GZdvb#rRW z*vUCXWS!{2zu_hi|6339neadxYy7!V^xcXswDUt2(C@e;G#$o+Wvn}m8+<@mI4wR_ zd=9*Z0+ReKz*lyv4>z#^*GH8B2np`JaGITS3smbsXQk=qZVUUt6*zj^zmJ{BQ$HY! zP=~}%jdpa$lTJr8SYoKwf3W6$i^khWItMhxQHQf6|2|KXWkpCC8fjpb|EiZ9IE&5H zPLz}bsu$(!zKesm0+~3Hdt`sVl?-@^_^Xl>|Gt_#Kl)?}A6di=OF;kK*B>iM0srR+ zusOaXc>2q%!xAa`V6hEGmsR009A%+3Ue!vE#L8TjpJaE8<^JNwW13A$CSQg3^_j*D z;Ofrq&7yRgqiJl?vx#dd=<8UIoLO*6ldSX(md7P-dAZ#s3`5mE6j|+88NJN|L+zE^ zS&2T91wd^73&Yh<>>G*uVTA!drM1G9yGLG~I}{07$k0+)4iQe+Y?s2kzlsDxk!`P9 zECgT_7kgE;oLOIeQN))-R6r&9tonfREy-LNpo@-oT&xv%r+BLTNta;&QK2G;3>I@~ z7x#5EAW`KD3)&=;ZWz3A>I#}#@4!%D5ye-Ka-S6NT(j_w-_n?p6rbK z`e_2mYJDr=fhrfEtIgQxqy2OW^xq1bL@vBDTrYQrRS6vOkYNuXri#vja10>r;)Q?; z%2$xr>&&zVkW!un9zl6%`?WInVIImnXP@xzVj^(TxX}Mq(5Cty7YA_Nl-V+w z-K(;sCXU%s>pD&IO*xBvDw4>I|DyiHh1tUyP=~b*Doa-|)7Lqi>T_y(zuRps0?ZU= zq+1}1gIC{;Hc#|vGOd(1Ld_=dHX_4HF@FXMZv$dQzyi)7_CA6R?na-W2j1a;(m81@ z*aH(#MUDu4nIPKJyZLgb+dJ-KwGU#02Q(L6`u7L}%nf|r`>O*#5-xicy0A8aNm4{@ z0H9Kheb*P;yiaf)G1(WRhnJu?9ik8^rCLLqw(F{G?|&Vv=@vrJB2?{kIOwdZ-?Cxa z2c0z&T^gnAh0*b0>4=hj?M&;*Pcf3VinSSk8(T51sr^Ppzs|Oweu=&*l=u3L*^aa% zM8HMz-Jl=Atpp?~anWAcvy;SHoH2kJt;zY_Wo4`}Mv^LvIBR&MQUB`N#rF+Q-awK#@SHyxkOeE6-s-=#6i^2tKwQQ&ozxg7e53O7jVC@ z?!MTb=-&rj)QMqykD(r|&B+K7!QjFIr&$zo<8s_t=I{cQ`o4u6%|>*qEnfRmYTaq1QNvUF^>PEz~uH-%4|! zSRos_v-?Gl zx1g-ii+}JhVv*iYG>VUW<`a^>IWmhkwqg3^)6JhEKbOa!A#G;Sbs}q+u>t02jBzht zsT8%&OWjocrUAHuml7NO$Z)C}kgejmUO#M_Hd0rLNr+zDI+y%~q~33p3g6~gO72@M z`WUdKT)KO5&C$orwRP_4lP=eFw z`oGiE;?%&$Z8v+G1i!Z493lnIDm|sj=FaZpn`7a4|LGqHT^RazKd7(vkwU3@+|t1V z{$JW}EcZXc(o#k3mi>>uPzcmgG9jvjV9<4kXooLQ<_w^{v`F-a|XlTn&23A7Lx;X3M2RnoY@ZxeP4@vnk2M6q zsp8O2$_B#@t@-vT7yNuf1e%Hiy$%~P_);h8ovrTAa4_IM*AxJnF3mi3LE*l{XR*VF z!79oDL&`zS@}>=Y@hy6~N_LDJD&`D$vp8UJQY`4=H8Z8w!{XrSTaw0p6N%&Tvr*e$ zf66|f=S4f{o>u%^*|#I$0O9E|Ioj~i2d`dpO$3#p1@AF$nx)4bmUf$8E zf9KGc`;N8ev7xYDmWT>|5`6pO3x&(#0szV0qOc|-b}~Ayc{~4zUEc=p+~rByn*)_f zOWsN%HeNpvQ|ZgfA0AD7dHoI)JH*$#Y9381taDuZ8t(o4Pzt-me6z{iQo9wl|Axg{ za9x#6;J;Ts9nv2YgrsZNxpxa$H!5yr{q)$Pd7Ho93fy2 zEC3Djk=8(ZFrYuWFt%|m$?@hfC_sMM-5E*e!^zh!pO_MQ$)SsCD#oiB@4+9Bi`2a# zmFITGgXY)5y*>9i1u~d#;Xs2L!urunqlLn{|-L#qj>QNtmipAb& zT}DVC*}G>*8jZ_SVR47t45!(VLoSdDhD5QnmN6Awm~-7zY>tX4BhYw&$!|I=#zkKVrCSl2J~)VL&%$B z?Hk0g3il_-MIE{T=4MBTgWb|&E1;>m=oO2BZaWIFPi@BsL42iu!MO|CKZS{QAy<(M zMT+@ncTv{tW+PaE5_P;uj-6gI01Vk^0|y{$`L9MeXV*MLT_Xi`_&#BUQR7ZDzkj_P zU3bGCDqdRfqd0MG+~=Ydcp$=~tXCKd!*9K&FYtzZTT(hKL3Odx21#L%MH8BhlnZq0 zP~HA3++P}2{XZ?Km+$>3ns}r_X^pL+Ihf)v>PD|!ZvAsNNzw>#A; zuRFfnvPnf7lj@5BHBj<1qL@Nz*s$0R`h}>4h6OXYE}8x;xRRFR??>&Hq~)Hive{H! z0AzgIE%$!ZOB7x^6f$nak!H$iEbDLCN6SZ)myn*lEZIja9-tY2uL!Gb-0Qa_cZF=H zfvLD04J!ZW=TGe8Ow579UBya7+ewpZgNBP zD;e?Kx#v-s^Kn>K;+zCBzmim9AMd!mhK`A{9l%rf9Ec@sw%uBPIsGHINtYaF#dCTF zb48Z!Y_KZ#b$tqoQ&rkJxJwSW@d3`fn0D`|6UYZe7~AvGp((YP6jw zpJ&eHV=c(5*5`%vhEwlXjW>S!tu1#zQY{VjJCvZjJI;wu*Yqlt%HLRPNyXB99yC`p z2CqDe?N(*oYuYr&iEvdSp@vX~yl)!WiG3CL4l9z?!K)lt$p0Qae6_(kVJqT+WDN!# z`;#ywV~0q(X&6)v>F-;-o3HUP?x{tX^zPwN!T4wys18THcbkW_K)=3!a9JWV)~+qq zWSjBhjzpsRAC8WVa0^Lzxhq5jJRN(-op*{icfN_xJ~I?ewwqK%Z{Ry9e(yCLpI|!z zQ9ZH@;ScW5R=q-AJv>x%DZU;ZBII-bR%Ay!{|R33pUUeYNMb?=Sc7?t0I(G!g@&q- z3W|Ok7^7dN_L*??vc@;XHkB|m?skBRFWzm7CB|dwAij;!DP7)7{fe@|@8ADUr+(SD zbECOwsAFL<#+Rc+_W%-#)}#;w6kNFI(y^RWBWI}x4IvZ~I&H#CihXh)O9Q`?V=NNH%s=WL5V{gd3Q8K1WC1egtc zX>}wqkNEg2UPoTF1H~dx5nSJmdTkv0{?u%458=a)!~Z=A$(Iq~gRZxTq0}BBSX9V* zEia58xf1hmH2=K%iAk63`vV!%Ui&on5@!iJg#(*L0{n`fK`I6ak#v6V??V?SC~+)^ zCmr68CKtM#NNk~9EEc6s-9I#mIq=4QCNe#C*UkBkqWXpZY&YA-GnORMxBB)I2ZNw9 zHv=~h2vMVLlnhpmAkOT0;j`0oo;wZHAF)}^$gQ|%^`XIiyMIv7iXkl;3e z*TOyup48>g1*eQ8STWoc;Yh5kY0cz}&^2-lM0o_UcpKl#hIM(cVT zUs+7g=dO4b83}JN6!D{F56~fJPKP)DtK7*D1SsSHHbhVR8^9PfSl1tw`Z+|UN6@KU-DB`2JH;n!}GOw5SV9*7p zhvVPXQhoZu41{4x=Sa&G1-Tm|c4Eo7*@hqiqH)77L055ULEmRhWJ>0bp=F+5;L2m1QJqZ?5J|CV#9|Tc}Rv-?+z4 z;LJm$E7)Z3zavW7dc+PH)8|>0g3>L;mBE$fYOFMi9k#%6sA#s)Yr_-Xi7H;7f=TFb z2OhpHM^3u%c7C`&3Zin5<>BG^#GY>H{rao&(I0?7kW1yM*w(`aS*DmaeE<9;H+}do z=IQgQmaw7syOmcDhd&VxVZW;;w1OX|WYrw(ZEKT)6;UqJu|*>k0hGXfpZsADToVF{q*+r3EpCp&QuL514`W;8O{k@w((k`Brg^?D@gVbDNNbvb z2>P0K&H6oH!*S#wicOPr(dF1V@FqQD2zy=0*v3@!`oy(Oi!jud&38p7;H*k6#lgA) zZnlG5FdCD@E-Az!|-F+s%socJ(7Dq?M8e>MnJfn_95`ejAN!Tu!Nug$l9obV8=qdXXw&i|RwD_YRGX>9v0=wBWi@vvo^Tu;Noi`z7!UhY4o z`l#u9=ka*nwD_E3zq7Mmo&zl6wkmDRVHjf^T-ou($xmQfgTcRI3_dO4%M1NRJHaR(k7qgdNJZ{=%hfuyc7u6wHF-$ctk>(K)~T-LF3HVp)(Z7S=bzD0nYwWAbtJZs&kz-J`uPqrA_2opuZp^@ zP<+iL)$JTQw0~?+LlwfU-b;sc8tSL3EV>3>=Aa-LN-ylOKiTq)Ae{8S^F)0`S{h*+s7B zv+r9((aZZuvS>y@a}hA!WjV|8d@jguJ^R=G$(om%7rNcOYn;weCoQ}e{D=9v+A_m z#P_2VWt#_pYvK<@fi2j6dx!EK5Kx{t5O=vZli{y}`a*nXl!+(W?0OSO3evyfcx7g> zScp$xLK42e{u#w5DJ}yAx-ek7$4!mFm}6cJ{=b(aBtB{ zNxwgDa`@eZW8T}|g8vZVzQQAJG;6U3JsEzkhB#R9E)4(>IXY8qb8so2<}V%+)UAdt z*XT@XUf#_`o}++VK~)>s(e>Q#s5w5_`8g{q&F@3&#!>;}c;W#7fgQ>-3zdQ@y5SM> zr4L>g!h3AU*Y@Xtmw#JLnp27zY-bCn-zYt4bgh-e)e z!vl!%VDiNoA$!c2?p8ME`VFnOCdaVh(a=7T{!6R`DD2uhZLJOaqyPBQbIswKOeOCo zBKWhO(_7g6aG?9u#&Rhs4|NuqKA)w$Ryo@pwNc#MEprcAyT?InG!kAxx7pGQITc+L z_5*Dt_lqg$rP2GNpvR5B*7GA*i0tm^8IyVTn|C~v*x-Tt9zVYC1s_#2g_#3Op0vVi zSHDkF?*7U+k8AmE73Q?;YI%A&&)%2nwUv%#DVcg50SQu%KKSk5GZjj_uE1Z zt?#^WxlBUJeKp^OAEpRv5m=0il>bLM7zHAGo9BF2GxphX)%_1m=lw|K|G)9qea_(= z``9Dn*rcq4GS0Dyj7pS5l2P`EGR|!)3Q;8EkRoMOiZYJ9R|&nXLyCsI*E!$w`QiHy z+&|r~`*lB`*L6KEQBN?-L-qtCt-O_CsrhXRfFg78cQ^yMwF#}%Mz=6M%2D#bgwHPJ z4j_e58Vy_*Ipd&(;TU@v(6X&dN~<`i|EW7>x)WFUmp-WMx<`%whlJO=>V7ztck8PA zGm>7yB@{3&WEUjiLnYl{zW z=dqL#oA;b{lk(InMH?35Gc&dZamdPFM8#rR$8XyYwpphVqThKX>^XhpIW#c`i42|m zU`}Wjsr`1>T%}Fo#rE~;0m;w#UxQDm`PD^BLUr_=zAQKT-y|`7lSVrUP8^{oxrU4D zYaLs0df)r(IC!Xx5Pfs>^7qC)B3c=yAEY}k!X4jmOmRv08;xY+xZ`WBvAopv5bwU zk_{25a^TOtD*xXuvx-ZC+nM*yYxw?s($!8IzVGXW;Q<$jA9y<0jpc@|D0&PK2!-m) z%n+5bqRv)gwoD+QH06=T?}~PFw34{XgL5%agn7)2$N(}C0naLh15coSm8DXU32|VI zQ+`c9=L|!UwAbA^?*95zDkwmV4&@2D1OG2aX_;j$l1^Q+k0P zX+Y8noUTnNe|J5$@*2Qi0($}3Gw3NKt4)}x`2AT%x@r;CF)*8U;!qGvcOFs)OZ+#M z9`gqK&s%P-)3H}E#2bKOcUMNK#O?u4`~@KQnfD^mp8=qhGiVBD%&rNpP%cs5FHZ}8 z#(%#0pI6DB6G&JYhZXQ(1eL9VgtP8J3{s5r^~qZH12u7yP4u->-ehy z0IXbS;-wol^Y$Md68EJ(_fBN^Ar$9b-*YRgT)4mp`#3W?f0qsHS=LzFY<;}8$Q7NLr|n}%B$hVEb23Rh65Lx*f*k4lR4P)56?SthZ63S zK#gVV^&!tO5)R&XrXfOOxrFDfW~jT@YJ)!T^QcNQ30N*fj}vX30(ZsY5X0LaFCVNoDjl+2+!H&DOeYk&2D+=w z=2vb%VR?x6xmPFW&(AIoD@b=6G2)5|xK~sZv>;Uu@B1yBxcuU|(9LhoyzALow$R{H zqbM6RIUIip)vbchhKgkTU#3DM#*h-m2)7IXQP) zAB?@w8b5~9&5b|WY)rUGd&j5bT(sQ9gei+1xG!SbQUOTMH}N@$Q0e>tpeF!xpM^TR z`FAtdI0fM>v5D_yx_m3Y$fwEAH?7@HIIsNRxG&O5BV(9O&n!>O`reg@rV$p z$!fxV9c`_G_G>8K1Hs4EJTUUFq(?7J28m~xiI~K7yv}zVjN9c0x_8hG=-(4&V-0_Ig zeCJ7gp@N|JOx*+Btp)QYVNsi{dx3{B{f+Mx?7!dmPMfU17Q1T=R-%ulSJ9jjm8_dt zcxKspnWJ1vDtG4r0>=M6tx$%<=9W-1m-G3nbWJ{O?MMH=7ob7RVNYWI^Rcyz7MjEn zXjeZLnB`d4-BEeMS8|;60G-n((dcVOy*@xox&`+iq7@pK7d$0!w$nie_6s+TQJw&k zCy*{?@Fu9adcKY$6uKc;3=ONHO2P!EA%u}2*jyX@@Te<=O~ksaiP@FMe)YFWn8Uqt zgwNl|Vk3AYV>;OgNHf?8?6R7pE^oeTXmNgU8f%X6D9^6|2nJ$sd^K0#yQ zA=%L5Xi_YcY8dx3a^3`bzT>>7F^mhZ&wGO`@EAH@N>lGcPYkzW8`!0QppVj<$AI+s zUnhW-xrBBiU^TsWC~R3c8mzSL6VGj(WOuHfJr4Vr14>Y!DnJ8|3-BL&;M0eommN=& z5dcAo;mXaJHx1X;56IG2Ms{~C8&mbFzXAHuOj9w1y%EYO0{TuY=YktjX+lKmgi ze4(6h@X}c;K=CpLrs5B}p5XP3LeaOnQe?)>EgPlS!6Zx%{UY+*1?dkKb_CgW|R?)G-pU z1VAsIj{LZ*M7%gobmClem6&GQrjn{Gy@f)eAJo;#?XJDk{b|>`L zUViO=)93lfE0Lc2zt)eLEu~$~|D2sy1WvT2)}W5_T@`kIiA)+=1NBDo6n&kjobhF^ zFRad6e>d~ZKQ-0_K3c_l?)-zvd5&Q|2B6*yTv$I~%Dd{t03%_0+Ker0q+(PdJZGNN zim`42L3?X~t6$?RgRCPL?~EueU1SA8y+2u_95$M6_IR4Gh$Fg-mNHdQuNztJTxzB%6rk2T2 zA)m8JH(;fVqdZCKd52=-h?52kx}qt%y;jw8<+N=(Uq+4+PG?r6OntN@7_n!2egyY~ z9O43G-h}5%())Law-IDhm=j4Brat&~06>rfmK1;dV1&sYBHybHNBnOIyy{!`LTp+W z2)v&Swz+Q#h1VDG`=v@7^6GZG`+bd!Ina@SzuHnaS^>=KOmX6>urO8O){sw>X75WV zhZ5825#(g#(~>vF`MGxT-8Iib=&hGES^4+BMvALnSv>sSM2mTJCzzKI1e6gqvmE|z zTHxowb$#dOIW)^lQ6hNlh?1=dj08N^TM+)18RWo!K9$!{TWXGtAEZ>@vavj3Ug&kN z6ka-3-TNpx)|}f_Dq~HkUGe*?OXvHkG^fSIkZFZe{YW9bt-fAY$T3Xqi z!#hq>66R>AE5NCx4?lj!k|Xrjd53G0YifXlxu)<>yI1V9C_uh%CJC8}68Mi)zqkp$ zNJpG{w`O%Iy#Khz(+Atun@QkjBeX8CiBFVP#}olTr^<1hsn#RuLOoePA=~%nrx;CU#t~z|qW~79ReOpAa?4 zKD&31;I%2W-uoNymEHc~?c4}obk&HO1@1EcIn>1(M)l zejBte0-{1(9wJ9z_ii3NC7eC|{8;0-PQPKhwkmEzgNg!$(loa295<++)HV}5BXtq!U<#%_=4@nkictsa5OIkNmWNGy{5S9Xz=|HgqVFbYpf%Iqah@n8Bn@br* zcG-OcEr>8sJo0p#e4N^mQ#E7W&`weekU`W zL5z`-_bZhs2ZrdKPItrQ>n2QhU_%nWUVS;JqO+14ZP?`xmklE^^2T_)9Uie%MU_4FPho(hQ_#e(=v{37$1eXC*=`jj&BQ3 zPCv4}dzD%r@!#jZ=xMjZpLm|Tn>408(~kz#_4V~-l-F?vpFTheOe0Z=lzWSoXIQSl zNWvRhzOD_D+>EcGr$t3|)8@^RkT_Aa%u;EQu$kXq zafBI3xhkfcG6uadpAsYIHy+D0Ro%3(`;5FPaZq@)J^4 zsd&cwY0n#f*I{^hIpk}rw+y(;m!G*E+dS#|8cr>UxGmre!1ttIO0jaF3!*hVW~9tS z^}$sPemp>paVbo*#IfNtJ4I5gL!Kp+XK9)`Y#&*fhL94+=>4-XD)Q?nDD)1>z$w@7v~OV4Y6uce-f_2Ox^FgqF(>Zrl;LMy08ZT&9_f9k5`q16Gbvr+}`u z-%ZMi8M^u7-L%8YztJZvB-eah>W!a3#RY@#A#Z;qcn>aTb#D zBnyx6A(NC7DgU*>DdeW67FW@E0kV*A$LoXwBS6w%#QlXVF$e%?-_{qr*-~_2`<16u ztNC^ng#Q7)K!VT)S~=$uj(^@Bt8o(6Hd%R?`0rwjVlueG`#1j>^?VBanoHn2%2`TX zB8|W3VBPgsH=cE+0a89GX3wODtd#vti^TGfR4)U4@C8(INS@xy9cX~aUn&&fGXw@R z0U^toSCwkI+ih%wlbY-F$hWoDV26!zjoD{iD?!*LF+n8d5YKQ`2)#hWoMdrKnHHmaM1o za~}$sJ}qee&#fylMbz4_v+Vj z%Res4p)j05ZG7aXd(mG1dnUWHK9luQzBpptFzVY;2=wZtHbJu7WOhod$S*l3XLRL> z6U!{?z~{*$wz`c!2=`;GS`y<22H$Db|4p^Vt3=-P8BZuZ^hQ0G&3|LEdr6%0n3H!_J`{g4!q(6Xw@K@7eiBItchxkqUAJ zu|E)tls9l!7j4!Iery65=M_8{kvctK;EvE+ad75);IQ5c>=rxZ7erO4SbY)a*Fqzw zT$qco$NHa>208Qm?#p9f!w{lF2{7JnuVSxp6(NJ15<$)#bgI3cf7ZI*KvlU$g;5zj zwAX_~9Fwvfk$j2fvM0Akim}`uJ*x$qcsX6|9KaNTft!Sk+5`2DiL!IYh`dN=Vfgp4 zurJjM?651)3K59GFOz+{$QZDlty5sR5;=Xa>)j$k@H+ov(SCtXsmOZTb&$iey#mcs z&En_bf;~!_W*}{PE|RU^nsdk_zlUYXIj$&0g9F7AbfyC!^rdvHmSuUdIh+odDahL) z7O|Qdf;{0wzi@+fqR2o~X2b+F#hS_a!fCRqIRBD#PJ`+*T2Ht`>fJ+&U|NuGnogzZ zIrjCFrC2*rGPPm^^ZbtY*%lG4UE2pxN8iYrFJJ9tAN8nozSGah=xYV|H}{yVyhxzFOG z+1d%j=igrH;s-m=9UC*3xz+&OTpM_npXU1_bRZR)jFZr-eUOPXO2C)u3$1EI`h55n zS{taA7W?Su(twPuD`hal|jVL0X| zS~RcCeCgj>cz@;niy5@+ROoFx&L4`__;uo===(a&vu>)-;Bil+-xDS}*@=uHUoCAQ zY=><@9tr6v}N!a3eb<06H5U(_gFBB$sR@%4p92T>y_Tjk5B7!iUy+zW|39USR834 z4SkhSo`F{Kw2tssK@DYn>-6b;P!Py7O;|(%KBlwp_U+QUHfi$Vp3jiJqj$(0sIG%Zli{hn5 zKAnV8l#Mz3a)wx&caLdh|NfWt4ZfsSs*(!w>H*!MqfX7dx%cknxrUcAz321`Q?+`8KQ7>B!nPYkX?S8 zr9TIlo=}N1o=I%L50v5?7^aerusFAfY%M(bt(UxeU&3PU)$}r>{-ZpRsdJyD7T4c5 zd@j`e{_pH%P>to{hAsI)oOB%bG;|>)vhciPD2x(t@HtU6K|ixW+ta$3(IBj(2Gpk#`T&x2+~X zxxQ0}(ES*(N1WO1(vWDd!mXI^utIo%1oiGoZiY+H#Aff#S2?FNgmo#$(Ec+{?%klM zl|oC{v|xJKR>MPe<)N`3?9*4Wl9Ut&`7hH)G;OC6Nb@M?FbcQLR3&x; zXsIHC{);T?T>dn>n|f)l{p(-G^@iWKGW9_@GSzU_Akeod(xiA-3#0aUkZKixOvk5! ztr~=fa!TcsHLEj(5lH)-Fiync`dEIeP>ZE>WjNk{?9>$y~%Uv4&*7BXnu%)w~s$u&w*xR80)by^X z=qVigcVadQi(#I%3y;E&i4aUNJ1dHUq*B^r-=%j$&+CrP-DFcEyWGT+ zAs%2lu+`V==1|KLW9DKjotz53fn%x69;adML}*%>dUtW^x~G5ER7(LeApbQ|iDxfMxcJQ+Mup z#Bi?TZn~CrUl{aes}4(07YN4^DBo1+UHfqdA1VK=v>W4@9gJMR?nnlQfS64C{ zp?w%i<3f~~>Bpu1NcN8&1eHkA*0n#cAi~RN^pESi6?4AyhwLk4=y2~AK&0DI)auI+ zPYsd$KdM15RAY?Or~sARzGaw}Ku2zA9bBL&d}5*#pn;4*2>-OD)LvautI7 zZ$gKa_(~;h)E~Gv`PfDk8pnkTgtUp|b8FGo2-Ee(F$C!t>3v=lkC=6U3wEXAQ^0nB z_OFD>gC7BVUy?dRHz2jGAIm(s9bfY@8Yg;_5)CM{b|L7I$>&y}`mP?w3#$S9!=X$z zzCD+pua5!2?SVIsl}hR5RS+qZSwi~g@C$ov7-2M#n%IGs_I#a241f409e>*#-SF=?@FUCpDiIthv#7{H{=w(VY7c?Pq=TwgQ)i*=gYtr<2EgzftIwfhZ_l@jhWM>7d<`Ok7;{Aqv! zsFRgyV23?b1P%hRBEylVgB|Y+6AWmdSW0U5elU}S&-rm2Nu|!ypD#S5-Xcr$bzq=_ z378m9WU@|J+t)f8$rOlH=v-p8;li#PPeyZ}_D{n8N^%gSNZ1uW&8fmT8I#82!i%18 z*)_eqBdxnbdmppaLlz!!WOb&=?C<`bK9So$pz-T&gJAsMiV86nF8~j=sseWg++)7j z=#W);)t|H)fWi$lrgKzwK&EMk)VzP(aqo4Nc0(jQ{fhKh@tixzvqem(B4j>>gox7B zN+84`!b7zV2E2I@lC;q7|E8nCiRnW-;#_3ybSu)i9EJSj`7gcALKWutArb^^mJksC znPr1kr!k{L#Cd|{@ed^}$yaE9U(0XK|NS;m9#dP|Gw?zGi^rD#VD-h@;FZtABOk}k zO!fG;eboPI;S37CD3ONh1b3cENisL_2$IZ%0k-OK)XnK@G?fG8dBJs&=|N_o1rqCr zo)bHU;7P1M*Py63V4@bE$+?aktlCR~z$HY@7vFS*;NQoSuqH}RMFs`55u?JwWAdV~ z;KIeBRRNJ4Oq9~VR?x%e@QQJ6TWh=a#JENHTj99}%w z0UJ5R!^`!``DN!Jdl~#V(uc(pC?24~x?dDClDH&s?y|?kRMC~HG^91;*Ef1KkuTcr zvDl0-N%X=~Qnz>Umz^up?h?eeEybNR8}7OpKpn$W46Sm|BoyH30nTTFT-klvx4oeE z1G%+17J@Ud?rX;P81M{Yes#5W>9s3t>IzO{6UE`WJ2t%I-pF40#%uqG1#hEPLuanH zt8uj4KPx+_P#QJ;fcEK6$jwcwj}MzUY9O2#p>~WBhd&pJG?rW4e#4DiV%O7jb!#I| z;$os)OogVmm7cEk)46Ep-4k3{m5;KKPROJ&5`IDfI0`m^$!j*$weQ^H41B>R`PonW zs>op39&id>{c`O@cz$pSzJFf9X<+AXd-83(DU)>{4@SB0M~5~w28nY_oeQSp^hTH~ zJ>!=w@88xA;K5vp_%$;XDRCr}=j_I{Gv`VK<3>J?abO~|3uIG+X@|lWXm~n)caP?& z`xQg>qGr>kd!@Ny8OPo-4HO8S#nYu8d+Q)x1Q0=wTD<`1jb|ytEp<(x$##LbIq{KT-R9 z@C)%D^~m0#jAU6O*OH8GGASkwH#?{=plML^kvII?DA5nO^3ef$Uq&Zu8qsY=Fu}O; zv=+F|O*l~^2*kAPv?sTmY=AJMb>KPpAL4Y$nhQtG#S!@A45UU23H8xs^Y9v0U?w#_ zl1V*Xx0Ok9a%LkFM8I&QH-mlUJvNrHg&hb5gkX2|S@dk}!}SQ4ZLx09E5`^=M5y%1 zzEdT<4=UybH3)sAwXK8g0g?$I5WIXV*8>MVwVxn?r}hW8bT@JttuLjT3x(srf{d1H zqRL#muaCb2uda8-GFtO~0dlKxKS35F&hg{zh~wu7|3QgUWh6#Q*0vBJC=mu%xDl1* zr)&mN65qZ&NU1TdIig&NV zok%aqqMJZM=kmW8tHECwYDCCGL0*mtW7)gE#`?@YR>NnZF|UJ4i#CoD&#{-o=2*eF zsI!{CZ>rh|{L=7N^Vw}FM80PO*h-mW@xR5yu;MG{5n~?+F2n6Ltwk}Z+fRjwu04DZ zynk$*a;aP2+|iM?*UD9`Z!W8ybrTi}Fpgm8vn>LL_7gKws0zKu}mZp3Yyd_!=&{d3u`&LPtbc4Z;HNEYDh7%27f_uCUKx596UGwxJyiVgRg`yUKm zA(mf2SQhbH zp`Y4#!JQn6j2ISs6L!0+DZ(UmVYPqsU(~0eIw_q zIlKb=e}7uVr}7(u8i>@#o)eULcTbSzYI2|8{<-2ZEZ)s=W@JF@ z+%xy?0nbG)DX})|P?LD+tbf9t;9cbIN&EZ6+uze`KG&E>q| zD`fW-7}f%S)Ph#pShJ)9ZoT?A$75+s>*7K5_kRqTbQXbzN+y)HB3}qIMDJw%zGrvd3O@DchVC{ec=cg*vW0o$aQY6_iOfjfKhN%Q=#d@Y99<8uLt?n%C*6nZ zI~XSx44ox-uZt^-VsxTV$ShR^<|y^==JfW;Q{naCII$QcEeVl)|G=V$;i%xm zQ*wTq(u%j&{j=O@V#=ma;Q^^e>j{eo`-6=eA{|HWDw+cvk`&f)xq=7K^D}0KdKYpw zhW0qh1x3{f;x|}c%`$y)L^qCodO+m*b7ksppq`e>-4=^fTKXXC*lnTHR9S(o^ugs2 zm$S~rh)VUBRHT@xWjn@i@O1ep;}-t)yzI7+)m$UMv(sp6P1fP5V7RxaVlc1{sX5J^ z;R&kN2gASe1&9wd&pvLruITJ|^Abo$Cgu$Y!6pk#qIRw?eUJ8ExYk$_Om@gT{J9q9 zE7R_A&;6DL?MLY+KjfvVZooku@lQhgSoj2bi5x*a%8&1o0n4vMdUPTjj&l zApjrIM+mm;eE#HnzZ$tY8+soNgf|xTFE#w?xH9|}X}=!;ZbGU5ZF3cr_zsQ3VqA!T z57XzqZ7l1{_u&BR`xcA^`JxB1WRs z8twd%Zp#xe1vH!6$3NY>Zl;!2{|@Hlx@IyBF{Z$Z+Te@cbDF>vo*3lQm_70 z@R6m4RFMI>y!8CM9`lhgw$+L=_whb16gWqxsOJ#&0XiYz<;AIVh)tZRSh-v6q#?-e z+#f#(HsVfQ^F-pxV(_##bXE?piKvOK(i*{-cZRL2#IuTOuo6`N#<5ZI;VCA=&~B*SOf;g3jIn5-u9d24(of)mYN61OZtZH!>o zJ8g6XCr_4B()@G_hH9o(nlx!0T5!VtPdqDas9RS<%p^`nXwT#tdAf{Y<<`wUWR@E0 zDjnyWgy#Rpm|-eGR^AxdGJm)~`|i>W2YY1E!WnuwY9~NAE?EB2Q3FAjKHgeEG-sx2 zHGG=)5)gm}JDJ3xM2}$MyFZ=UVq|MA003E=bW)Vi=N;=aM;)&M~x0}J6Er(H-yn6|LzR=3zF!3VobcsXx<5POV+QTB$ z0v+I7O6+R5yMU@m<0Z%a3$K4T{lKeMV~K5a$8af~5za#LKVtyawqx2m)F^(iR5q!< z%i4D^@CE#H5TZE<%+zvw+{vogy=ii^&MzVW5}&gAz|~oMuQN_(VVh|&^9D9lgE$Q` zSZQMBuTeO5AaBSEtr(KcIT|g!!O{GR*Bdk(&rO2zv_81pY4hN%9XIYCL%VNO9yysx zcY$gFK4du=+xdlRv@LrGhfuWy0D(;0XnGm>_RrS%uXlsL#o9nzUY#5}i1?85LT%FI znl(uTnf%uC(-nj?^J|6R^}a;&oU`u{_NQ1L)lRB#J>|lVpOP>Hs>Xmhz=|!C^`Na@ z0z4I@o$wJ}vOjpJGF&LvyqA(0lJ&3TU*j2Wiej*(8(D6J&w%`0La{OO(21PB*2)Hq zAbh}Y6AUFy9T;RP#vBvx!e%Gf#7K%evvOO6r<~%TjTp(*xYZDKg`7FPp+NkE1JNC$w%!*qi|lRZ-5)0clL2 z(oE(td}*%zahqY*?`@l04OHIXRtyuIF9o5rm)}ut<2WT($B7Pf!rZC$O>j~IXc~?@ zLd6<`_dc(EzLT$N$@(HN1TvG$7-8hi)Gss>3a6QIK;w991UxA2_i$;4{(S?MWG_~W zLg+FV%;6{D-#Yp==mt%J-yhMCN^@kqMt;FV9C;I%cQ_RG=TkO2FIEVYP8T6--ac$Z za5-Iy-prVsGv@>4i~;EkWQ0EX=tRmRwz50HqIm5foYK_gr4)x9g#_^u*){nB(b$(; zPd;q#B#1)-f11whFw}<^p$kt(MiD4~J?ZJG2J^BVh@0up2Wc^J(y9 z&&C#Tz2c!5%h5lgt!yOOiwn#dtp&DEox91`gyk%Yk zdzMu0bc7LDl3KpIeq(PF<0I;YuIzs6&&w7Pe?jAgAD{ROpJrnzb&$Fv;t4ifZM|BV z<(5QLsZ29GUtODO(=20kEJX5?g~NXw*sTndvBw5gi{&-lI?_WEngq78F&)fLB1@^( zql9JtPv8?5bog0|MOVjc(L~Id=dgnDDOJoL1U3HBNakTi#1>|YMkoqx^QV7wh$K*i z>DWct21pi}#pkSONGL$QMNmW$a6X4c+`s>(?Wu*^)42taDrwA`##G5zatLEIbgWgX zazw4^GLmtS!;e97$N2^PfkAU!9?(h05%=%HLN4B4e_Z`S42VFZyRJ9%pJV}8IHfLJ zl<{J(!M%hBAf?mH^_Jk9?>ovW>$Ec)F~-~a&lEb>+3Vj)>Mvn$uU^_Zd#KSQ;?HBk zcY%0cU1}ApT^|3S{)J9RN1@h{`U`aWkqZ)FfI(`IxEgE-I~k0;E2kDoQ(CpaXzAjs z&_|4wJOXq9{OZ2lY5<%VIdTN%A&NYsUWnw<#+*7JYRKgm{ly=?I74I#i$1?|Dg4;d z*tDI?C5j_>!>|0#wCB_0b#${J+3pcPHkLN>q1<>hV=wcTBJRe)nWKc)#st5{uTNKE zxIFK@IG-n!&lPOFH6`gbEnTLyzLwg5ma-k1sz8=l7SX0heD1f6RgqLe{3K zuTk(yY&H|a$~iJ43IBIt;Jh}Cxdg(s8_H*^UoEP!;s1&|;8uENYi%+@QgyRB1A2W_yMlByRIjMW(l()Kg zY4%g+LjAk~{asqnVSQWe10hLGnVnXuxTn%eeEy+*ulo~Qv?yT-wXJTwr<6?N`C@nq zSZ|6sa*zeclE*<_6OwH4E9stBl)s8ovQFa8@!S#Q-OT3Chf#+ngY??yJp{Ui=)or( zgy6V%nE%Xvx+XS3icIHCm!b6S$j*ae+u$9=*9MDxMJ#lApT4k%^13`1=$Ad64 zhOv;-Y%X`Ghqc{6F}4Nzwq$Y9z zI8b+0e4!G>=J%r}5|nsZ;`Y)fc-&Yds-)V#qcdssV0D=2U-G`Gu{7A`%dL4Q;~O7| zS8b34KyWbwI%<8^>o1nfQP}#?WI#Z!?7tNXeHj0h=}3A?DD0wB`0g~mSHi5F?>Xnt z_idz0^wH*qhLr%M)y-~+aerf$17@;p4)ZB%SCIDNE*}!Cp~Tm49M{i#je?1#kx(J0 z6K{K7(Bv$aYl{K)M0ooXvgc**wu52<61Rsw>zm@kI;ZWt1^i^=g5+JApL+c|Z_uYf zZYbNhE`YlL#cpqY5L4%*F&$H9*M$T8&mm$8TNZQ`w zM=_JxI5LA7$PjCLP^k5OWG&znRY0Qu%oo?RAxl;IXloyJZ2OiAiX7C+ot^pFEOHtN zByT!<%z6JydKT~f**b8#yFMH`nXR`=}E+)o2H_}RbJvRki^IFtNG-FUu8ABHOs ztr_>L=W#apq;;CBF?h3|_wJir=j-2^KmSEjWJG7&wKkrt@6iMxIN;pt{ggjoj!Htp zLU~#0rr1XSqT~{NHc=aA#$=;`fC5o%y+gF}xr=eI6F1KWk_^7Fm0{Qpl-R%?ww9kI zIPquGINsK)C#f~O7csUmkEOXg;di=(q}J@DAd<59J>EA{Rmd8fdz`HBAQE5|b;6SC zLxte~thv;rZ`k7oYewcmbMq{&iY1xO$y7_J^IfA$w8L&yX6(V4D%Cu-&PaXMJ zI1}e91^FV^nZClGX53CqwA!y`FYQhIv7YbGZgf*Q5!Q(nS+xoMTDr$g|2C!~(?Sg? z2HeeAj01yFVp@Q>xJ5l<29I4v#{GS4Hr4bqm~r=U;3a6#FZbnAXCSxkQ<|=~X5qa+ z5d04}iBN^P=2 ziW_9oq4&bCp)M}rybd>`$p<>k!}@f!tqUH`lKfH z-0)WKfX&=%&P#9PKXsWh3Ea6ko?PL38|Ycjyyw*PK4?RjMheTIy7LyrZ2$Hv2ZluM zMs%?eCYNr{p~kw^5H}r0bt-D8PNy&g>Xj_yPO=T`vd!lQx*tv2blDhG)rzA*gA2c_DmVA=0NG&r+8xzKDTPJ&<#HP%1%{y zONXfTs4=|-TRXNnbxd2rjVUW)eEHyuXn`jC<&qzJEQ zKLiK^#|TfBD{d8RaG%7QKs9Tl6bUuZ?M`5K=AP#mPM8gA6DIf|5>qxq#Osd!;C-s> z99bu0PBDc{=>WKH+?BN=GXD3;b4$Ti#{q_BitKdk1(h?H8-FSFjkC%x4{6b7cFbOJ zN(0&BVBA)@FZ6fEw!T!ry?gfw3NOk-SS6HI2h$&>^@=of+`C`#St*tcroB=+-VdTN z? zh{wZEcjF@l7asObdAi&^D(Ren;R?pxmd>lNJE46(AO|I!YT{t=5}H{w-{Ja>%yE17 z{bk(7?iSxaiIu(R{NK^C-T7+b0-~FO!*Gt;kkDw&1>29c=jz5!37ZA@8=I)Y?Lk0@ zxVSifFr3k{&v69Y^VyF5B01f)``fW)y{>P_EX>m5^buB*3}P- zS^J|O{`xpZZS@n>35~88Qi?xdEU`te>|?NPl^kt4{6ViYl+zCqrA=?(3y_Y-TgjH9 z=e+}pS81DG#bYM_8fj@Pp~iAE*3m3i-%xGApv8?YXUEUFO>(Vy^R$75UomOnE4v07 zzL%iH=0;_!-G__9u6}7?rPL8do?^=Rbkb|xZ}N*MyN)whE12xBieNVZT;Tbvc~Bh} z7Z4f0{tdA2JIElMSHv9VLKbHRq%&s@n-##;V6uz7%Ku733Jlr zCI?KSTALZ`RqCBq%63Bg@&hi16?~_7x7* z-OywkOUGF|9=Q(2FE~H!4t;l%?`Euss*K%#di3gPO8!LPS3?t@ymLqT!)`)@(=Pb5bF8S^4MayDk`y$3Us6)xvE$%76b*vp80?7 zeNO#wM27M-r!}U~DSDGbBEkl{{q1$=C@&`|&2ZEsn-<~-2SpR4KE@k9`I(g6=w6_S#4sVef8mRPO#osxjX4Vu@tMzJ3#Mu@<#Z9q+u{O9d1 z9E_QCGC1+%U!cn_?TniyCH}n|FYWkUoA~Zrhuv!lATQ>rUm|fQrmzLwx|u7ySY3Dj z;z=i!kRV2{QK1!)!_Gnd3i3kzLsthmannklXFiryPi z>bXR_7_Wa2Pw}?UHIW4@Jh&?WDVxd{+d8G3W-yJtX_q|%8-HUozSwT;M$)Lb@+E66 z8jy3}2VL8Zti7Q{upgdL*zpTNd#mPGz~ubnEm00g*VyMY%?;XP2DtI&VSf4!hd%9H z6b5!aah>JRt9y4q_sv1axu#1J>>1KG8lNg>=ou&|2K{_`mXSXCX8d+vs@c^*f_&p2 zL>7IT5fWP!_k!=R=U5vgdNy@n=d^*RA_+@ikb5Jx#rCv-Aod!@cl)j7fLK&aGDBV%VB32CqiAcAWx`R!W)?)U#;dJ9?C^{0c`VYfJruwEdBr*k6meUZBtdMJi3s2h zLKeTOO91<37X%Q1x7WIRo@*P;_Wmf*a2o6DAC`4{?&GDZdijPLWNNY&wuw|tDsZ*+ zht9%LD<{sG?_TKWtTMf%T2nHZEQL~N9SQmwIaG+(2Lo#2_Kh=-9KU{Iq}=EqZY)f$ z5;FmurUixhb#TEEfY0UF3@>Mm{&_BlVg9>WECkE=b-I|VgpnXl&c*5YKmiuBjvK<+YFORiw;P4 z5L=XtiiZps38$_n8l0jtsw6~E)=AFf{1VRm`DZ8Z(PqC($lg2U>GRVynw2s?-ibBc zAez=44PNkQm*)DZml zx>!@#^@!B}j;Wo#@+PT+@If*tieC2wrk+9zH2h_XAW?*sGxl;mIHWN7Gq&vd^Ry6B!V@_j>u9#Q;?X^cr znd`Q%UeP@%$?{VC(X*E)vo8H*9HlqG`}K0QQ;|oyyGs;7unc!U7kXMe0C1qN@XHZJ zQJufwpH~moy)$XQJh$_I)!D^)C^oBPg0BHUbJ+1$cZsm!0({{$I(4A-PUOzuOs4-c zYvE^xxApm-m|VHqGFMDu9$tnG+rgRFy-0I!37Zt3;Qj;wSUugZxWRgysG=sc@iOw zXc-kxFba(z#(zQPlJp@Sli?M_-bH+GR6&rUlq~-G z1TsgJn{F5FZlg=?+*~{LpV&X`j{YKRo$t00;A>*vH3fL~yY%dGGa3pi4BI*7(KAm3 z{Z&u8w1&LZQ_nt&YaiKKPf0QF6M}m#SICeO(?ctXoSTH^**fMuo^2tBGbh}+;|is- zuhH-F&@SFE98WsPfwk?{qmR>TxxwVo=J-RoJ4DHCcD8k&X}`$gz4RpSv1~Yfo0FJ^&lw zs^`}LKo{C30_cYgdo_1eaZ=z0HoD8-Y8A|pxO_7Qfr115RnR~V=xL}Ga6g%LEjJuZfyLAXTL#jNu>fV^bez%sSAG58<~K` zRI&FJ*DqG=ryjqCPyo%K0N)pAjsU9c4Ipp>;1+IP^zc1xQX_gBK3i9Q#KP@H&nNX! z;0DCTeGn4W2UvX@U9$a1X45q4%Ist`<@OIy{I7HZ_{B3mp;#}F=}7)7CR|#HkHa7* z+&tx3njwG!-XcW+i06+-l6L`=4#z+)sWl+5%dxn-n68ZJq@0-!GzO@?LwXWmBB>s1 zg&GD&=ey?-uK$k3{qKr4JqG(7G|F`*u2>u|X?|Wrlk!KHgvxBi-tLf{SchoZsR44M zMS9ZcBnZHY=7B3J?t(9(azEN4%@exx9E0@#H4HrqaQpzgCqG*-B7YqFgy#+aKNvT5 z7Yuo{$oySmy4bO&&hA&u|FZzlGN7JeST;At(8S(cGz&U!UCtWM`FFr(zOUehe+R(gFdX`3sd$w2JO*-y;UFjEzk~1>n2|H^KA^&D1hn(-Qsxa| zu3k_nr30%%SUMn}fzk>3Q{p!y^BE1WLyUcx@Zch#;jscv**un~Zhw&jN4p0U{h&i; zW&JvaaL6ZosQJ;qZd|D6o1zwm!=qq)HKo z#I9=dZok|7JU#gb+YAyJZWU?=d<0o12r#{$Dvrg7O1SgdH73 zlhLenA#$#vi7a|-w~h>!&y7`X6WBIu^5747FQ>@kkI)1sDrMn#^^G+={pLX8&R(`q=Nyyklj>&W(El5S%QE_w+;% zt!lK)=HRLRL?9Droij#!FgX(z!m^_P!*7uX?-+xZ#syoIlYn-eWWh7JV7UW2H7Y>; zeTd+3;cJ66Gi`j~`*1sx*R~Y5Vlu24Xo6W5t9e~4NW}zXPbfaBuI$nTqfW=Hb!Ky_ znMK<9@`3b~T>6AWSK?H~$pm&z?Oeptr#Tr!s}Ks5!;PNt|Kw~( zbB0hx0g6FyojMX|2KHZxgw*8jxc{*lnb=%Gk1^3w`~<{)A||6{6QDx4mgmQ zn8P>zF#-3<6)!jIlP8tWF)xN^_Re`R6v6*_$@BBUD{?3PcS zRvg$lgpqxQ<$<8Qeo_L*2Qap4z^nK!|TMhl#dhF?W zy5`|vB_3DIP+9QxhzGjr_HGW}5G|AHf!px%*(4!>j@ z*qgAVAGQbk5%AZZz+v7%;ylKzPx#feXgn~{@eZF5pWHKgnK{&dIi3zAf>D8`D^AN~gK z??-C+gI0S@57l~115f>aGRGm9Fk#FZRfYhw^U}6{t~~I9}P{3t$$F=q{yW{zEC7JJ8WH?mmdii z_61T;pJbSM_(&^Uvx}b!jJ|8e^Hi&x|Iy>5CT2g&HZcw8ku&4&%Ne53zl$^o7$?B; zxIB~@Bvxg3#9QYW$KOB+*!k=$4^V>1?8 z3`#^ozGw~#eW3IBOK6)vWK;=SZ8%7!{=OFfjc8aK-(~!nyO^|TmzQLKR-q{(|J1WG z5*jT?X?-{~t|0|eO}y8cXF(w~Z)@PBh; zMWojwgjPd9(mds+o=_Ska1^VNUkR}|{70KSo>{jJeEu(TiX`(78GG8&j-+DwZ~2cXeOVO)4|Kl{~|uw(_u!C6Dme$M#{uQTO9sy#nn&EbW*knk0P$iLpT%; zhX>L2{{6jr^x-*f{p|)zB<4G-*WUmgV~fW-m>QL{&e;W8$xA#cWpJ5yo!0MTQyt)A zfaTSh|C>|_(WooQ$M3HVjU2GD zLjkPrkgfysXVzxHxsQ>HBcI+Je{uab73SZP+Y7pvxZGMOE+=&s3;%f7whi~4GfWF1 zIVg_!Y;}LXq&P0y*AxQ#7VJ-&QVI@c^wa<-*y>wZJ0PW0H1PDec`k-J$A|P8meU77 zz?@%uOO2_|=kEOIOb~p29Qjr5IE`vfV9OW*yr3UF{#}>3@3-mzWpE{>h>mk!Zi>O9 z?%SuOk?Z|d;L)ILL#3hE3R?IFQr{0tY% zf=Lqx9`2b6-!zezD6w|EBi-8j>i~^zd^PFnMno^QY%#V0GPJ8tFDj6wgCYyx%|&_R zfH%VD>RCtyh~3egE8BOU7Xb?;crRGhz@cMZa!FH`^t#n~W3$f?BcCqu?xH@fzYpq$ z!*!6^Yd}bU|3>f(09bx1(&9{udVQcb%tRgZM%WEQo^`5Twzjkm)se~A4;T^!igWyF z=ni0TL~T1j^(03ErLB*0geHIE16xhs7B4u;hjmhN=zc{{(@EQ*JI_!Rx~~N80s*jG z%2_1x;IrANerifav+)Bi=g>RTq^V@R4diVfGbr$0F#Qw6VM2G0_e-F|UbT44M=)z8 zmEeyLl?gI!F|EG6?NFyno}y5nx=#g&02<%7R`o54jx--W6xK3k1cX}DC(zd^P_Id^ zg>Z&^=yKm)q#dBi`eaRPA(g|TX|Y3hRk)4%2@P!EwKIWL z->1TLUonkG{o7ZHd&`V*PbBzOt8PeMSEGQe6X_#Q%`nP&61w~z5q|iH&nv>E4L!_% z$(RB1HU!|w53@0lX?7CF-Is>wzOVAxT~QAOynunMProRvelS?x{lEYKGz+&on+oWj$Q{E0c_0%2>hgv z>@%%cE>Au+Nes+xKKt!8Ro;S36v#$);Z&a`8!Zj@Ltqhr?BTtZ$D&X$wO!YIc|oV@ zdv3|nx9fN90OuPV=f7))Q)wQ3*e?_Vq$i+GCByMLMj!HwvZhY`ekp@HY5V*cXmiN> zbC1-OO-PX#a7cR4=Dj5kSkes-nxGr=vWwQd%db(KT`WE!B7c?r6!p3lz3_`(kh6HQ zL|V&}lg0kSkdU`2CxPMZ1qnS`FxQan&=^A%MW-mTwmrNe#Cd)YK&tXY=_$8AS4)E3 zfZ|rs{SikSlePtcbxx1ZIjM6frcq_n)~g0eIizOG@^D#f4VgYvgl#$kV@A%FOyx|WEuw()aQ0vsOrGb9ku5#xM+KqNf7ki$U!lT8z?%A5Rhoq3wJ{$shH~$Ws;3|ZO zlh5q;Enbpb?Q(5x#U#;*c;D#(T_oa*=1dLy-qe{i%6Gs@4st0(?4ozQ=yl$>Pc%{Z`zPByG7 zrED*+iH9NI+KQ%E?N+YViuXDtD@dG)ROf_%RzT@y`$D|nCB;Y{qD4^NTgj&d61QNq ze;%CQB*#f4qtCH+Sm9OxqzEue&-8$tQgVLOZ#6ib;U7o7u*WIG>kKS_Qg|2QCQK7L zmlSZ{*e>kiV<;8a==z!rU_UuKf2s6is^pjhHN3kg@{%z2K#rk?gIB6f;`d0>^fSs$ zo^?;_!RGKjEl(?|dv5HJJuEjwNbeMsIW^B~u*9*AqpfdRh9}GucnbGG^ZLvr#=*O>&XnkJZ*hV2~vx~8G zhA+by4V~x^K#Csqovh|+M!8GXw8(^jk{sm8SAp5v=`pMYV^7*%a6S~(WN^PuYwfwJ zABOH}r|D{aP3dElTjo0wY)$g;`tq~jj5Oo=tH9eGuq2bd0?$dS;KmWqovFL4S?7Tz zs^uTfwl9E6G9_O32f=mMr--!_o`MPzgaRcBpLQ{GAX_J%mO{I$=9?enTYKeqnTt?w z*SzS*NSmZ!&ClHUy7yC0aIN3NNZ7fgZFP>=SiZvupw^gFUqR1-WMw{Uj5W^v_3!Q# zARMSoQ2BZVn6E5Gc{L6jKv5TjnDWt?{Vi%6@t1FTHCQ2H%Oyl(K{^S9#4c6+$6su^ZdjdDBR(zB59y0AA=qbA9zxs28O;R5wh7{RH2Fc?VAJ3Ghv#sX$ z(}v}Yv%)pshm<*yNF;umN@Ywy`D{XPyKGV&A;KqYy4VcwQ}u1sZ7pH))ydfMqb- z?Na}_HRj39K~maUxx>OyxM$4)t5agHdv}KiN>*ISER(}v&zA(x=;e#1ztPxqnQ%aT zqYJ&SZF#Z1aq;h=uGR*Uh3XAJyb>NCv z^T@`Q(x$B&(FqEY1L{{*9T|Ds-BUM+cbF#W)(sn3g+LDo2$$ZU-m3GvS@}d=-%i;E zfV|pgAwrMnPvlDC7ew6Qm6Y?w*;AmH1_Up-=eFp3_@z^Az~Zt9}O zby*H|MM?m;wG7#z1$56MILIx1y~X~-oe~fSQ&;$I`?f<$8`C|cC{V4?&EjswFZki_bwQw{o?1J$MW>}NiM_zm ztMNRzV1k23BxSTg9y*HXuhV+zZ$K67IG#FV?u4N}h2aO=YIaj@KRoJx7v>ZIeqH#w z*0)2pKO(K@CX@OeQmR1_2e?vxh}^|w>1?Sp(~bRHlFEYRu==O)nRF&p*?1=QWJ?KA zE~`n3q@9=lBRbeX-ZSL5Lpr)cfs?Zlh^vCozq|bOU__`kZ=w)D+g3!@dt>a1^z++z zg03OO(B0X)$?%45V5}8heUwN|b)8`T|qw)YVpAz6-0t0~~F0JOQ zL2XP5z_Cs-i&YvkxR6`2@hvdy4xQJ}fIiT*ZZFb$KmiB3a^l|*gNi5MlKUe-FRfw@EMW*KcGi5_1|Xk< za1#sNf_f#K+n$9D;gEfy$N#7!eHOK-o;xzL>{b*x_%j>`!ZhqP)ph5|Yt> z=^3aQRHf2pKrvU~_@ZhDa+n^8uzN}aL~P<-{OB1oB&icp8S%Yi# z>rx!14|%4)|NBbBNjll-qZp(SNjLKn{hxV5Yu^HV0AuW9j*(}vPkF^5F}KyE5Ab5C z=aGVXx;~KaG89{vSqGy4|2%#o-(5N25GcNg*FTosVMm-_$f%Vq!YEPWVR-}gF{n9M zEx^|Y$=Lc`x;eI*_)i_eTQw6(iVvepT=;JFSc3D!(#IhTmfi;|@F^c0#Gn5O< zg~rw`Uv-?UR~V-ZN;cqS1o^fa5OH_*CAO8N>sDlyNUSfv{@yGOc4(h#jk@(dMFHmV zZ|B}yj0S*jyD2MkuZgF4Td6XSIMRyC*CPH~52gwdIyr1g%z+hVYhJ}I@F?n&h%akP z1eu@TVk5+r`KD-GCN*l2R_5QIhx{js(P3PLpK0^0WNG)Swo-V$YVlXPqtXiT^42diZ6{F1Qw?7O|Yx3WLJ|HPjGWj&CzyN|TKhv*6yxOBNZAxP0| zOPQsmmHT3A7}O+oUOlr%e|mcs#$Hi?*e{zSCo2qTf#TW2;Fhj@Zgg-eqj!7`%G?jk z3ts`p2w7Xck~(~5+1GSqBQjc$Pj6X#FV1*K_>e9{)zw3Tz?xb1b5H}1&XA2M4R!jfwjb$~Rpehwc<8to%lp6}I$ zx=KpVpXG2U{u~Szq5#sM$f(bzC-rgc6$ zUxb{w+>PDxDIoig=T{YjX1MhF`%jE7dE2~m)mq_n`NQ(32V#00O8nKoQ(_d(z8mQ7 zN9?IB=ds~aDtEiQvhm;7seyCpimC+cFXDi=9Dw>OWf zn=Bo#JY-8Ig=@54S~O060a$;<@J8>wP-b{e!8`Z1f3~G)T^U_>-pu)LS^~)_4-fmP zgTgi&-e;}ojS!Kai9!PC#1BvjZ(8e{35SeYa)@JYkmUN!OpR`aRRTPjP2&!whE-|; z8wn3yL7Tc#{fHiVEa1sBGuwNo4;AgAt%z`pWEG8I+;sDvmQD*$F#-p;!z!Ib`zB= zA9zOrJ8Dr(YpU}mR*Zk-zpDPXCsO>tYrw9VkRJP}{19Owb*J9< zj>$hV9Ye~nnnDBrz=NCl{tyQ5T*aq+(>*ScVzPAYfdE(NhOei52c0tbCMYU*+(R_Z zGZS}tSe${+K|>9pmVm{NpxP3~EJf<@+a&zm4m6+nJRdihLuS!NflUpwW+7C`M_g5V zVO#K!9@%hBpGea`{ZjYzL>(GrtLAUC!24x&V=eow-pVjdl@$S*4~ASVZK zwCCmpt@ACd?9)qtxkpK9hOP^XioQEc>uN8=tlvL~#r!-Jn&p7=JqIF4q>>(_Jm5g~ zc@nrGq#|z}w&uk>5y#ZQJ9mIQa!KSV{*SVCloDtJ?~v_s5K8#zP`gkwbX5&!zeaik z-Df@bp|h{glHZAx&bqbuh7MX7?YZ+;Vq_sb#~hU+WHRd4xZwq`g*_#0!XrE@2g}1? z^te|9H77~t0X5x_Ek1df5mbMU+{e2}pUsQ%<^#27xqs306UY$+ieL+#%&l;;Sxo>O zESR48vj>c*6!6%z4P$1JbqQqsUoU{@P7A`>Huc##Don(ERX*9FG*f5?}+X^^Oe|?fWBl`u0`4Pc)5P@MNuP}dm%SCcg+}wvvm-6^%(|iWdjpJCOyJqaC z7-r2eT`?E1G~mDU@RQ9+pm68y>QK041RYiZm{sF?PK4k{o=kLDZf4)zLt$Ig2Y)x# z&_5k8y5OCx*osg@f=8;;X zmgy%ln~>@ktSD17EuMDwoUG`QtCA@AaVh3Bd~QQ9>;rdv8jp4|SBU-M&$9=QzBnRZ zzOaG!T3n~+qdYSB4}a)({GY4yozULv>Q_TAlg_kYe9+&wYBKAIuDq{@(j5L4Dnsq3 zbWD}rZCu#Vfr~-#?O}^DS((taANLOE<_T&lA8y}*I8Yr{Yc{ERLfBMgAx-Uw8^>hR zfm0k%zUj*5xy2a0ye}|jloA-ZIGL%3M&D<9Cm$pz2WVYk`@8^5k*#=fsH4kIfzfiY z>qMP9fTpabeVE8r|HL-W!{<0+`CSu1(}-8`8o$mvw?iEI9JV%ZFV%FX+kwz4;3GfE zickcwZlCk|i+1_1E!Ogn%#UlC5!rtBDmrNegv?N?dIaE-<9xk zetPndL|4IJToYPCTjd%gR3jfW4$bPIh}?d)tbl)oMO)C^dvkM%xj#dhU2O3Vy8`1K z({R?^5@ntQ{n6LwyuV&HhMZb9D*Ra3{TmANbO9RQ1LDtU7(v?SDp1gsIdt2cdN;Fh zAUAHZcdkw)mOwJI(w`H6I3G6!P!v5BYnu1z&!PMf%-=(T8P|E z%+|ao4NG56!}J|CthIpOKuLBH`L2TTpWo+?^+dp!$9jc#f=ah<{{7ZzmSeiaR7y5YuWe2TZ$Le14Xe{FiJp0{Y8O69gFiHan^wzuUy2=9-9 zYAQa(9KLOv#0f|Q0;{ZtbzF1E`vRx2W3dayZW>suQ0VkTxU_hxe0;?S0hRnhIc4vx zd%UmqdJG(gK4{Hu60Z>#kjfJ+X$^><#vD!OlV0s&hSg95WGGmIh_I`tUND`cC-Fn@ zG3#l!0KniOsS{Qd5(sF$|Yu~jg(i%~6azMQKA zf?LX_00t~-q&#x$ECw(oe~quv>sEbRS)LqoTo{S0mrBQ;R#!`0QiS^ot$jZ(#X8?N z?qA-BC{U3lj?mA-H>|CHdHeSforaep5ST0L2k*jKZ`^8J2sptAD2+x>OekgqE0b}9 z{_b0!uG~8<4yOmMx~*YXZ&6CTC8)Iwp|9Drc^z6mb&)kc)r^eXMQWL zI@nma!f+QHxU$K7#a5HA;&a>3G1Q#%8^;oafkHa}+>rie@BGu!QTr+MgBN=7FUlAg@JCFxDgF{f0-TP+Urz>P^a zGlU88k>ABi3i@QKEiRQ*Wp@#%OyYE;dIqh4NZX@%`$&_mtOOvahU_Z#;~E6VW*dqu zf>3C#EoG!w=_uGH-^$#x@93eXiIAwLR~=X-0gEKIN71(PynKL`{mVzp+hx(3vz9HK z_s2T|_y@OgH~+H#ncTDXDw$r*;Ybd+wb5bzg=WKdg~mEtueh*sQgUuUf7D_FM|oOw zH7Vj{HJp(RgOE87S=2%71XFe^0Nb z1`bgSPF8ZttAJ_cD709ri&?Pb z*TPK9P^RMJbH=yf^O1XGS3uR{Bzp9k8i34d=jh4@4%&VbI3mro(Hyoh(6zD~4=Yp5 zGvHMAFW%h8y1VYhSAuzOu)5Hwa?{VVwvf!#?3$YSxp-2;TNAB1;*>eS7$X7Ac|5UH zN=WfQep~^zKakn^Lox^X0Nn=|=_pSb^U>vR!r#9p;7f^Q(F?^Bhms?|Ebk?Wa8WthRSn(-^WLSfP-J$+R zfCTOQB&-G>N>xCT#f%o`@|jT$)*`m_D$nDlZbj;;={5^CP2ZoBlL8)tI!mz8V0^28 zffyj@o-wJ!uyTPfLIrOi=nIVS#se9iK!jouZO~oAlXR>3KZd5R=wW6 ze(Rc{%IkvOyotD8LQd;4% zJeS77lMC~Wpy1yOXIB5EoSOdEGCFq~9uqZZq6G1_1j`AfyJKc86dQ&Kq1FWdX*CQR z1r-ps`}ft+5LJv@IfTLpDj*j zfR}De9u>Q$%wWy^sX%V3W*k(~0Kc53M-uJ~C5tk&>4B~*v)7NDlVQzoLvD<^ z?;nG|PzldwRn#)Q7+;>jznqnvRCX&=+Nw;49J%d2P~?7WaJL>)RYkzOWCAUmA`@vX zfCI||TmS1#uAFC`-{Zf9{M^)A?I5)i#Qc<5h>zFjdf0dWqbHcB+#`N<(8=D1?%i3D zy>TpKRgugBwu@OR!yLpxy-`-M8Z5I9$33qadJthw0hBtFLT*!hXGayoVU>66RO}X& zVIJw?t`{m)30TMzQNFpRJxt-5cjrLs)2d~zho{4D$a}NX5*QATKfb%JU5G#h^2rAB z!%^v}A1GUQKV>zxOQp1r8*UVEscrm$!>k8+b_t4Fv>r;Yq^_DO$*bO#y753c%4gpW zeU+w2L(a9Juk**|-bb5D(Y7&_*!NqBq~>CGtPJelb08Myx%RQ_@BBTwm!E_Ug`uuD zO#o+LT}00yWxpEdQjHuXZs@YX8XD_{khglMqTT^?&Qz#$5D1RxxJcNLD*gND7vKEL z{L2u11BZ2o|FP7Q)v--x1(EiJq5Fk@jf}@oDF4i%0ygbwVfIP&TmB8H8nVV`NGz$pH_Alz?soHJpAG^==O2Twz!9H zan9_XY5&{}u0K@+P{C%ke;F7qR)TNuLJS_pfBQ~}zAg>r!ao2X^PiHKt{Lg=BG6;^ zLLVAsz?yOrv`K$L&eUq*hfbNK)>TIfIS}UGsd)6G$Niq49$q zxCx^%A^K~&G^SA?+N^)|VC(l>u>`&Uy>M=PiQwcU{eMH}sb7f}uapddiX%4s2i5WZ zFAYgD-ac!yzaDMroL2O;*sjnS!?&;IN%I*oWm&`Q1g?w?{q!H|9}s<(^8=?ZaGu*c zR=*#^jgpAP@1m<}V?7FaksmfLKV!)gP-#aV5DtX9MiQ%<9%$}s9r3z!9t^Y3P(_uS ze+6IpaQf}m&jn}y<{b@wNbs*F?No4Lk3MPuLG(Ee4OeIaEbr(VP5tC+I_p2__at1M zANnN^jrlTjy`8qfN3wb?yuA>0vEzB{(NA}QoP+CYoYR-@H|erXkAar#Pd1fU-#h=xl!7Wz7WwUr zNjPJC{bk_BS+v};R8E|yUgM{76VjYX-_D(RDc6v%Cd>rEqWITbju!@hwbN+-2Cb9r zvXu}$uS)JeZ-zYtq+e=Lfa+oXaWL@4OB`Ic&G@FTq}_X8QY$&u8&YvAAegBSvfy%& z7P1Y&Q}#QaNy%rYF)Bh&_=gtW;OFgVtlJqwhdM59zS%mw>}(-CR`XR{$Zo~AaoYE} zVh5O|p$XTb?yhHB^1|A@6h59eQ+FV1fiOWJ&uRMvEGT51xz69BFDxxsqs094w;#{7 z9EjrKr=9XmFbp~Hj726OyxOn-`*t;okMH#Q=6Vy$3!GzX#7}jm>qEYnThVm<&!N30 zP4O(8e~cU@XS{_qJ1(&QNb5){2GZoooIlVbViW0A){}(@&{c`heZY4G5IDI=43k>t zHMqx89YV+!sHl~St_LPy?O!4LJ6@#&rr}3T*KPoRLnMh6lsRI2kZE%YRG&j|emddz zE^z_&tNyw+{1P$La>(W!vV!M(^ zlAAyfIlzq6Bv#%UI1H^P-EbM?<-)Jf2KRKGDcrnBoBb;_geZFPNs^KO0MlWK70k-% z37{@*U&8F|p&yfDBfnhT4?Z-sL?qj&a!kYWP=L@jdjrwL{p=BEp?{-MQ|E28Kulxv zi<wnczt9Hompck{2y;gT+dC2H|xAO(yaXj%oFfimKRAUEK%oUD+om@keCnjOd} z34Ddlx-L=j-S+WgHC-bmuPMc6RxNra|xaY zeO$JI#Tf`8mCB-IdQWHQa=yl55^$d8ILTUhV+9_4U7cIRt?Ptqp0|v;Q9}_?AG5vm z4#jYoPLo<5e#4{)l}3t);(T@atj<6$s+p?w;w{pp164OeX}>pJP;FusrvU~sa4Ebj zc$J`las$SdrcHHfyR9zj0(g}tH`P86DNO~}PwYc1kEWD(E?kuK(*g&6rH{Bn`=B$95Zikqytw7OG?&nhL#aF{KV(Ce$4@3lN`P z4gYA6phq-jBD~0d6wX>;xUHIZ9GS7*02Xy!g#Y&rzp&68AqBoDU!lv%mb}W?C?E!g zCGjfWW~dJJHs=b3?I!&PI2|>*iVcwt2VED_zma7r`j&CblHlv!2zLIK&MYiJ^-!h9+&U6 zNP;%x9G;-dn8M<=$@Dv?*555w|Do*{`AZpLNu|xtCNn)d&{!c3nTa*0PMCZe&7wBIy*v1{iD4nljS_1dLFIP*|>+i1;>mqpRGCKWN z>z7wkz&zHE%G}G0mo$-WGyOaG?2yrbSn!Ny41sd^`nRRiLOpJocTY$s+qZB^v8_JypE@dyHH>rvk6}fAgVNs{(H{nnW4tj+&sM*;fz$Zx(5FM@Q zJUg4ngL)dCW9S^wW3bK3ePR#EYW$)c^98`dGN8Ru7vuwE?$%{%4c?SLkCUR2rd@8M zTj(`?$AdNK#LWdcgMN_HCec>4pZNF?9sJh%f$#m8LwErKtR9>4T6sEm88#9Im7fBH zUz=ZtgWhNU=A`Kq+v?z2*_ZfxG16^VdS<$axj}ZAvSVw$9E4two)_oKv5Gf)Tl=B~ z(^$sdOEgjDmZnENfJX9{vl53&En3cBMD|An*NU2UT)r)uJkI#l9`*NoomSXjLV5rA zv6uil1U%B)Vk)cTeMA$Pxz4ravX$p-KPcXd6pSb-h(G*c+eIQhP$tN#s!CnI?TZo5 zm!;t6*T<+54)cxSoL4DSS{Ud!vg3<@A~_%zI30d_aiCsqFNkd|x?(0ZK3$|;(7V#7 zD7j+~*=`>?XMs7D4^cpiF*X-DD{AJi?FrMRSXy*MxWL-Z6e)4THayBz6^yR?Drp}z z5y$X`|8@m~i78jZFeQ;K=tDaMuDUe$QjWh(V?>l|^g+|TX}uqJ`o!1+nq;t_a!HWB zkbq0c8iHg?Ms8%=+wtA!8m-~sTgOU$duSTZP8@VIa-iK-CMJ<(b0dGK^0%6ZMe*91 zy4*M_bw@Ckz_m#n!dSqrRfU`5DPUr&34K`RFA@NGo@#38j}~i2v~tGFeJ(5{Efy- zkmMplBJ}F_%bvbP7!~s#oOOI4QX9R*js0-c{Zfii&?dgzERD-ZNvirEP3Qej<^TWj z>zw1*TlP4|R%UitCnGeFvXYUqWs|IPA**4pcfu(nA{1pihe#q$+4~q}kIaKJzP>+v z{)X%Jx?RuL^YwV#@0x1t2EfQ1A{BSY_7P1BEaWLZ0bQ@&!)3ve=y3u0yhgW5pobi6 zemM9&39qAs?kAB_MPd*47!HMoDE*e)bWz&(^F+&8(3C=kKk8?p{@;(=kxdEEq_TS1V3gr>ofRJzVGg^G9@t{4JU)@U*>;#@P@= zLDkuzE7s22@~6Y?O^u?w0j?H$W19y_iLc*&YPDCUT2S#Uf3jVp^d^U+ zWy!|x1Z+dGAEgtc5xtM97N+)^f3;Ru;1to}WK%{km#JK1I`?NOk39lGLwpxa=Yn`m zB`VxgwBX#ia0N)*by}-x`x7qQ?%`7;PRFNRjP_Ka3)}t_RMmRQJZ|^n*qebCWN8KH z#Ar>N0g;k(U?7}i5diGKZs2)$VDrGXt<|=>j`YTFogK$m*oyBKx`>`zf{}UO@gijJ zcfa|;AnPL;@so56vlvwoTF<#oy&({j;Oc38)}!6fRT?X5qwNDf>zJ99UT zkF7=u#E=s=&4fyduI9C!PmanV6$u`F>WqPq%CT(h#LkP(BA2=V9K1G80?b&408phq z;$`5QbmU;wg(u|FKoP)!%LG;C-lrzIRaW;Zj?uaLJ~J&}&?(;GZ{>c z@6obvaxC8oGM8PP&|8VR{|}FIG`R!>meD9Nq9O!@{RLk(%(m;l3*e?rpYtEL6Q}Lg z9j6dikTp#zhi}u>3iuz0?sY86k&M-xoQ=)Q~ zI{MUFeow2}MmNV!F>j!i5B}RTu7~`C1bdK+iF7Q^-w*$0{!*xzw|TXVZT|8_2Q9^< z?4aUzv9GZIq9X?rv#)o1$lZGCtq3Uqxe9qrv*SolOCVu>KgI?^ZkWgtV?N)1|Da{Y z*E{#?5|?~*A}6th0gSR$eM!gq{l@#2O(yR5_s6hCl>CkZc$1wx|8psbV^|^D?!jRY zY0M(+5aRe>7Jz;9fjm(6C{vHm^wPf@REZY~e?YdnUAZBuoC5cq zryExoyr^Q=4%ss4_sYjD?%B$?(0+Q(wb{aEbSiABmrXk5dsFi$^2bWzCZPjyUgEl9 z#_Wm1%lRWIS!xs+NiX1`N~qV@ac;?&#IeGNGX81(zhSkHG1c{SI0xUAhnG)2M^cg_n6*YKuImQYmMo?gn|viBELzX@Hvm~X0zaNHw_-lr~q z6iBmo0VA8A)cBU8HUm~HiC^1Z1ljOG0@!5}Z?_pSBKhi@i-o&yPlY&lCX;_eQ_0 zqF|~h>a+V5Ms?T2VB|l(jMUAKpOUm}BEpkf_Ophm9Lq?;kqSr`gE@D+4mR zQVNzZ#`A3e#@zwB>f0uP(&R_Lf3VyS5Lj+v>1CQ1^b26i)H;8 z<-K6LWZap%D>t=gA1SrLie7H|%bSX~zE#oVXE9U$Ma`!)6#a#-uUMN=bo2bg29d_G*n?`0-=#{c; zK0c>dtKessLARqTz5dR#&JhBc^6WGs$OAOauMD1Wz5%-41N;>+#CI*+k|$8g-VYH_ z<0aX6damf?f)Mh?C)`EfC)y$+qiR;lelPN*q-Z|&gJIlSL#BMa8K{wr$V$lg z3*3;$CH5at{G(=abq|jQB#KngJA3dmKlbiiFf$4LM+y7^Gt9=!e#m>)p%<%6{CUzd z#lM%-Xzr3ac(Zpo`PFOLsq9C6D-lQU8^($h+uGtIY$C$sOkXE;E$=GR^{hk$NA4MF z!B+b1Rlbj#EUv8L!vl*| zkl{M;^%@~cj?A&2D5WO>MZKFHR!cL`*K{_>?;N>AgEp0tJ|2*~L@es)9u&M4jym2= z<#r!X-YS9t+<&N_cMjheT_C*AdrCq(T>_$sYrrn7pON(*)EMVFqWZuhNHhh$&!J^O zb@+q&neP@NDj0tQ9YjN48g8e=Do8+Qz?~VufkGz2PccaSBiiDvxxqn$uc|1RXuAk( z0W0fy&z6ln=f~~B#CueR9P0O%H4g);uOD|G!`fF1_v-Q_z3BE5j3}2jQ;QDu{#{B^ zWx*72X>!O#uX7LWO+2|ZZoc>_O1;|ePosOW{jDuk+TVFa*YGVFFF_a^0^_T2IBNMQ2=U6}{P!1NUj+xed{>fP^*d^0hhc3(t8} z7MO%xhag+^)R~cf2+yxGk5q?nY#xu_eJi&k)_(9T%)jbLMVARiWXiHk4G95jdSBnBWf9%U0YK&Z*w2H4fIB*0-u&LP{;i8MWNQ%+~Txm%tZf8rB8*nnm7K({&E&r@O@xc0B+T~jFQTXo9 zJhd_ojlVQ}ZRZRqf=ZdQkotmRl9OB)WM?6XT0b}vQ5d3+n@9j=W9SqRANwyXN3s$9 zB`4~coX?i>fg8KiaSIWDBhm>6LA!^N{UeH7t}HS)J2E$${s;zp!ZOo{8qO4}*yshd z+2q4J-<9tJRgLDuqsfpMql-5!jvLMb$8K0|vYF)+9`rw)=2#xu22P`l-rU5%_Zf$^ zc3FS-SFIz00Odd`=B0-?6~@a%T72+;8;svxAJYWuvgi*l(DZ#PKTGoPf*>H*GqUhA zq{%-4u*@{XSJ#N0rhjU?S`(XYwRh}lqiFHpoDU)T)qsc3_hWOm0JP)I+&hlF_ke{1 z(ge0qo3>V+aOXXtY3gGAkKks7xme7c?+2p0x^vj2FACB-VdG%L(uZP);oon!F@JMN zN1eL;>jR4v_f)EE3q**Hkf3GE$Yb$FGbZ`?w!1w_x zShv-m}WCP%NS!N~t^@ZR! z;yBhArlNm^eenq3(|PQwh!$xk*lvLQ`s3Kk0f7pOvae`=$4vAIL$cMXuWkTkUJnUq zozYsQE!?^@-w5>Fj&@)fSsc>hzmOqCIYuX)ehB~qP|+$EldlV*$jKjmCV6UJrzkFA zE#bh=$q{Gn+&1fwySdfB+zk+&Jy^byD{Q!e1n5;*y12t2kxqWuHez65`Vu{9-)jUJi|(YyU0!cY%>YDGgZg8@oF z`?L-zVjdv4WP&gDmcxKjl)qXT!S1b<3EcxPc$}>aEx}fLyMgkpSF9~M^4$%~eL8{8 zBew-J*|83d-h-jr+B0=QE)!`IAnYh;&32Am;?wj>{8I-2{zrMv4e9DzspyHN%6dAhvdMj5`Si;yDWsp*i1*_RXPuT4E zeX0a#nyx%4J&2P&gb&lKzVb*2{CJ;btIDSG@!>Dt^n;RE#pmv&hj!W%c zU;k&f;=$Ln$moSRFIL>9E`;!9QsyEM3k38n-bnCz39~jhaLCHKFp*mJXJy%pi(r1; zVr8HBBlMX7S@XQ*xZYD0Gn>Zfj~I99p1@0Xp>Wfj>)B5nBI$7}^meTzUsu)L)9pzX znKiT&+&|F_m)OHj+Oao#ReD&RL7FEv?V`tC%v?$+km~R?ru8tOo&K}Cxz88e!fY5o ztva+BWV#UW?Wg-obip>0rqE#}EM>o@8yxTzw8Lx0UgKVprQ?1P!hGves3?o3$D6;x zlYGG7wR+js^^5!FY=)4fb57N^94FmBP`)S}FK5cTfxK={a{kIg2vxBVX=@lW7+>KM z16+og{pMHt7Ps^0X|VV+;#Yqvk=QfD)dR&|lw)~sc;=T^zrVa)1ofV!h7BBdeX}dF zkrty3CEeXLzFS**Y6IXjX7@k%s4Nfi;bMT!fQ|E4@;t9pt~Ed7U+=az9N3Nc==K%> z!*E_u9g?03Tv^W|b9VO6N39BZr`P}FLz^^rAa4wRciqdU)^dCk7y)`q99XgWw-xWU z6R79!-j5s1`0ry#8TEP0-MhP;1Lws2%h~OwLxL&M!DFsk`jBK=myl!`mi)V|k%GUB zA$6A@aBSu0S~K7q0R6wYKuni!xQ!i=P?v%6C!6scN1sA2Olsr_YB^aJ1XGA|r? zc;Abj!yD!BoAFb^Eq?1p1eq=gPM3j~mJar40N@JD&k>Q+NDuI!Xope$>CSqr>aV`+ zu*rN_n`c5U1qjf^k$XoCr{SUrwj~)(N!7_ z*)+lUF*&}O{(bS>j|E@X$xdkUzkRY6bGCql?S-d+$CD@SuZtKx?nORn#Uhp-w{_8v zng2KX>KHM8eDvzb`$Vx2dk$u1?KqOIH!I>)puKLS2?z;);3q78W%^rXJ4!c19iRv6 zA!$@J8u`|nb!q_}urM{Dm+fKJJ;#(?MtJg7Y;7hgaIs`XL4(5eYXeFB+Vq zA@4Zbvn9TVT`7u)`W!>___6oS4g2^KyAZ=p2LN5Tq(T$A8-6>tOvGQF84xEoHD%en z9;Do0hv4Op9opKmWg=dilmD}b8ABXJhL>QQ|C$O0&ym>@@a5lhQ}M#=hd$a#X

< ziL!x5@7Gd!z%q){WB8~cM4mqKG<+xkKvlFISBaT(N;V<7amIO1mJdg^6$j!z8EG?! zrq#l`fQ01jFfKB2>GN5JL=rvV?;(hK-4!x9)^6i@P!JVl21!pz_qCWC0vV zk5$xd3;f5lo%JF9=JIruk9wEw1K4GLg~>=;B)zBV5#rDDJge&puGu@+vxlhl9~4Ie zuOu^3Rn%Wr+~I0H=X^&Ul^$7sCHX-90))dL`COIl59_=1^l7RB->pDU4rRy+hUOe;kz-X z({ii?cj$}A{P)Lkb3@+~bHmV#=l>)17>{Ur0fa2DA#0Yp7VOP&1)mPlxW|Ef#l8FN zJ7jC^o%w(IF{!7isRv?G$u~#*@Twx+X1{c{(5>y&h?FdSD@42lRgjltTH#>kPg=fh zN{SLf{oTLIP-E?heYH|qH^LlS-4q!Xt#oYzcINmtVj>s~gSW)O>>iWpomvd+eUB{B z<6Ye7`h&CXHzw@LJZXeGb`*{1i<@MmO0)Tz0y9^S{NAPFKf`CoONuhK5NuLg?r+;K z*AFHfSsBHA9nUVuXpMWcvx-a>({&#WsXU#0E{@jN1moyfgbMxr8#~9vn62!^+TP*O z{y~Wki84UmISJ#Zqe8fr!aOLuqKzI5!w-L0Af3y21_H#b(`9|-*ZHulvb?l^zIX81 zPc6RM*MiV%PTTo=Mb>}%zsrv5WCdr6mD98A6pITWo-T_fWAAJ-L2(88V_u_9I|rSk z!Nl8m%7U3b&!xc!5}8VtJpG})kz)KR{FDY~c<2XQN5&hVbNx- z>eczxw?~n=o73FR*B=<&rV&59~HCNK(zOQcfr_H+={;qHNle*6cLD z{Av9AK$qr?zC|T;dE3uo4y6v)?n_yau9@PJp%dzMmsUduv^V?9!W}<}POWWSES#wg>)q(? zH74(V`;eM?lg7d?d&JeffNSHlWe+G+ZO^SOBEe`e!;b1u>I$Rz(L2`+nVYJDz++ z^mVNs6L0OgOxoN{wye!}FF1a8>3dwhmNAy~DR$&_cI?Og@Qrfj(YMIo%oLNK3inAx z7o+PTD}&G8K58;FgMOzM(rIkaV%`ZgrX!5E!KV`x^nrKj0vs-V_BiKeO zd%0ptjEHYkk7l`_;reQv9uJDLvz*Q8vnDXVu@>Pc)WyO9r-oZBx?85l32ya+ZqS$u z^uhK~F*BZB6rDNM+b1&r9=l*)BDPsyN)Yrkw;$0v52}QgR^saz9A4wqh3_)Neg!XY zDKSD#AiE{IV(Yo)=`*Li^VFgEIBf23mT)>K;M6v)BX34uD?@cOFS|6Kcq_-jMgzI? zY%M$0ZL>z`pQ!T_dYVLkCD3Vbr5GH4ef8VG=mV|b^>l&hRqqgk`e=Rm!@8h|h>BXs z^!kq+%pG|i8)6D4ZTZo%Es!3=r|G69&#IfK_VfbM-Vk~}tRO-m9X0ua{wAALtkb|y_KUDYa~4GIZ){4u)3*$0-^1CJ~;*P|skRDK6B;hcE2Oq*ALE z|7*88!kdjgd*ra`lj9O%V`IfM;ODAFWZR~xc_!-eC|>JX(zUB6Ken3~KYutQPE{w& zZrvnbXT$YX>+f_k#4oK49ryNot@``FD+#qO&+esnukjRFt{5P)d49<$n%7HSDw;ws z0#GV?Y^d56QY+sw?JS@6w{D=}da<#9t|K-2wPx1*ft5agFu#x$FsO_qE^lr|s6ci3 z1@-n*P8$mct^Z2&3>xQP&$7jz=YeuIQM76I)0TDUVm+!bZ|Sq?COYL&7<9I%f?DiF zDKUM_jN4&oM7NvcM2TjlpjRhGbtl819O9#l^fI_0B;}+~pRYz!ZmIPFijfC*(IR4) zWRY@7+|=r6%so)WC$E_pKXln|_Y~^uY&}BqD)4+ll>Wuk%EXyV)5X*;I^-W1o%rl@ z^MQJO(a->0$Y$8pq=sfs6f;*x2kJ5+wQ9OTT0Fg;W3nMAD=(`oB*b0yj6vag5ER_> zp$TL;X1aOpISXlqh%j$*baNGYl#pI^cw$pCn!)=}`!U_H-wjn5_1pW;BPEa2vJ0+K zVtWi}SL)GEMN{{8Vty=Jo1&s$)Z@yJj6OLpLzz*t!rMXjuq?o}v-e183#H3LyGY{$ zI`+YCSw_eR?>YXv=>UdJG3I^DKWs923TA(*N*sJj9FycantK>=J)u+=ztv~d1If1q zj{E)=N*UsQ+s4beimTjsJ`9g+!9(&2CfV3Us$Odna|cbmz!2Au8%rUjs*Zb)Z^)+m ztI_Y2i_JVV#EDRj-lMTdTrO>NslTn@)pxw`!uKJ9Yqpt_d@kw?M{K{hrFGWHCyTMl zxRwCwo1w=H^0iFFvRj~djN{3_+x*~IPLE+9WKLi~FDP)r)EJuTJvoKdAnqW}RP8>4 z?pn}Rai_xnkUxvs!cQKpd_`MYa0mCskVRr0EL}9;G7*xHQS^0YJj@6qS*FLhzP=BunfUNIBCEHu13wAZP!! z;+mhY25!dGV#^UZP6UrfSE$d0`h_7%d#C!;dPdCK)3nKmcWqIGKmwg0Ww0fTZSKLl z?Fb5u8g8OAaqBoKQ=QK^N5D*Wd>mhPH5*LIwQkeDR~B;Fg+b=(A=d(LSGZqR&(`|l zQmaX-Uq1O$IO#rXnkXf=sB@<#5IE0DIFhNT<%HC}EP(>6MR|u(NuHu>PIeK4B*Cnf z49xiAUjVaHgtfGhR;+dNIl}b;p{W&FJ??9gadKuj98Ss}ku0br|v$Tmr95>pI zRPrLwS0Ffgf@=|CCB8#b%g3@z-9>vhq-oO3a`R#7%b%nv1aCar0^lrue`no+=Tnd> z;b+!lc#eoE4JtnjOU`O95~7wtJ)S6eabkiA4MR={Du?+1OiAb`3ve1xV1(>HH9%wQ zK0o%0*{Q_}cp$GWj6Xm5YVL1tveR-~gC^`U-)FSHur9Pga3}wQzlJ>SIwUe1d5!98 ziyJ0O#6?f&s>UCp!R)wBTAH6uk5WEX5rJRZQoF^E^yFb_2`Fht&KdeMYMZvm)VxCw zsQ!mOQG*4mkcr z+@tmy=$?Z|>*9B)CVSY?_!k2+x&g3$GP6oPml7cM*p~qi7l8IPr)VCzj|Lz7M%Pif zAFI3R>oKYLJAANcR3288kv2&3OLfwV7W>YyB9O)7;;lh=r`wW2x|wZMts8@;Gyt+i zC_Owia=i^dAj7vm)bKcy=0)eZ;FY5Pjs^j;-9fbG??E%xG_4sunIq!C^zrqrBJ}hz zz3^j#WR$M$_d!-M7)~E#j>OUaL6ILsq8~uiMd0-&pinr~Xoo`gh&Xd@yL)BOR9|}; zdzk~3LrA&-FXdy;ri?+l)?}dx)C5Ym2N>H5ZghTg?*CPyT7Bd<_s!ZSwW{uo20{f` z{P^`$@2G{U^Ae#-ak>S3d}0!8Y3k4TdnkQhO+}kWnu3)8?lEA@aDs=&NBQO+EnOpW zcL3Go=x~p`&o_Z#W|Qk;D_17z?-YO+dasMD-u%nEdorVsTS5wJFtW^q7qy4XA^3wu z4KAvz@Et)B_j#1c-EJ{Gd*i-`-Pj9J@L%DNYI!8AeTTL!6|V~avBBoDp|{crae{>X z!KLWpIN?%`TBEj^YCb0qWF}I%c22)k9f%Ij5yGSz z1Y7fi=f0Q`-<|_ddP*=QWSTNW6is11`UoALIDmCx>ob`*ZW=Ne1Ihe=;Eh*|W;1?e z0k3e<(kj3qO;zwaDX1W+hTWU)2bS;MyUk`6cis^C>R+hp-bkj05I((H=q?u;DIR;z zd>L_bHhvs^QOSh6_9SxD8h);e92<;F@@Z!_&xVwb%@(j?vLRj&<~1-~I8U5Nf1i_} ze)z|iy*3}BbhmIW&uA%Ek^`3wn5k)A7QYhu3KT9ye;GzZ@aVQIP_jkl1dbJ7qG9CL z7%rqiD_3@8eg(ku<)nAyW;aCp#OV$UQ-#&N(TaZylQ2YEi7Q-CEH!}tMle;{m-E;* zc*@Qi|JZ(N>bSHY zq-Olt=|p~~=MX)r&9g>Q#K61$zOV%XhKB-;&Yp~!ituSo|*X;7u!_(Dq(X$p9pp1EX=6e}PZS?~c zM>>^=_WiKwXEZYWy7|09KAQfxVc-lo^$C~Da-z=-*}b~Z5j{C90fy9F=K`YdJq60= z(P9t@Zh|ZuWbI34mlUXFaxF6EiTmeX;T*WPb!qSI?N<-e(;z4)?n(4zb85Z@p61WK3UCCGE2&8A*y|2$Cb#xhu>ZV-;YZK-ed5Yo0G>^gGYI@jR2>&BPgWi0)>h5qBw`8RUlNtmYe; zc^JkeF$Z;&ww$6J-x1I4D*d$H%5cF00T1Nl6f9*LC zrg2i%S=L$*a}V$~;{6xy!od%74hk5G>H@QAMjKD<|Z+pTL=vKA#sCT5Y#* zvN>x`wSw*FsvDp$Q_G_$3|~~*bi-E6pYG(#03K31zvv1bP&HF5t-SMf>wKh#4+d+c zC0@jxq>+|Y7#37gBk~y%2GpF<`{tFJ_zugDyl{7Qz0<+Sa8tMEZBw?ya%ic zaQfQtTDM&8Ls-0d@Gi!YX%chKVvY9KdC6f&+J}VnsCIh)Suljj($_!xKa$>5C}?zz zdDqkFntyOjYl+c1Z~^k7n74&WPe$`&iR_VsCd9E$jM2s?R%8N9^EtR=c1f&3Gi_ z-B4=q;V!nYpY1x--~&773FaD~kS&|;qGCuBHbM&LL0lhW+Zu<)-v8(YtYOXKro^MJ zOm9ma!*+>|X$|gEdtC8tH(^TsW7SSMw8EVmR6MuXcc~j&ev(3+^J$c?qNrv%96j*y zsdg8X7}i=qX)N>l@Pv2G(@&HzlMW4+P=+YwJL04v-#BqMdt142?Z*G%UieIn#ev|F z)&^E_Z)kuxn4g@X3}hKt>ziDneoVmzS4(G|@~$Ms>Rl6f_c9&*!1c2`R5L^^58meE z-`zyL=C$dnBK>&QStLzVPG*>mHzd$LZbV*Zj>)IG^TAZp zjTGEYmQU}UgK0vlf3o1a&cH>g(x4&Hsit$=$#tqofND=k9i{yQLp=kSk}O$(K_2Bh z$xMc(CahMYurd+pT&No0VY~t&8a(6KofY#coY9`5TFSJTRq7cbZB8N~VqXAMv?mt` z5K5gG$H!=+?aQw-KJpX~Izf^4db;oC1}|G;3cI&BHixjTfSa1VgTmxa+}zwx3R&0L zF-bXc*d#T9<^_4TR|fMDr_3825g{o}U^gJ8X4PHZFoq+FeRufsjuc=@Ys8d*n<&P0 z=mO7z#dT)mK@`ywbaKK80M{s7;<)q~FoPLyL^7l?F5l8!5W*!g{?qZD!}L?%yeD3G z0z@b93aJVpqfVV9qfMY;3+$lr%cout6gRb~=n1d08lx^&_lw`x2w9-yUGCT9azKt2 znK~)Lewjeca_}}^zsg?fBIzOxEwwDQvbg)9BlHfsB5SsWOx3FlTMlsxHHe7G=gZ7L zMTWTzyzZej1HVHy{2)?;(4B6ld>R~`{vW>p;H307QiIx(gM6dTE^L(Xj|xB3DwX~V z^!r@9aClUCqm!AI9*)6%HrMyWGnnM83h<~11~*?mk7V9uyM5qRzCZg^2MS{!Gwyl2 z(K&Z>bgTa7hi()9XWTEq!$7|cbTSmmiu%HWQrXtihoI~W>e6WNT*$aUTm_K)GX2d( zunZNKsQh|aMAY40rabExCsph>;S86m!M>5s#4CNt!dy&&oIulcWVHt`F|^ zIK?P;qF;$E3~>82dEpyX+P?NU?fNh7GvK(>LmJ}Qb>L5nEiMNjyoe(LEXee!Rq{#~-*42veYHxy@mB+r+I z0(F?vj)1|Uq9Xyu*Z390^QcsM{9L0kQ{i?jbZRx)BR|PZ<(C^*nKS!~P6KjW(eOV% zaCeZ-l1fKMpT`L#p7vyP;AK!u1a`n>XGjX$$4{@_MVA6CUSzglB}iL^c|i;}rSlO} zENQem#M2B$4M4*`PicSdwX~lXR~0+%CkL!dr3+Fo2*8f-576P~SWX7iW#*xPH+3`< z66{6kl}*Usu}Fq9hA-Ut|EhS>Q3`{kB-9)>Ppe4Bye_(J|KAG4mPu{4(nNi%Mk%hXbcElq~&W=gWoK2CMIn za6lkk7IBaR5-zIcV(=u%Q`k;5=w1%P@2;0B%`9Jo#^5;Jf5ScgQO$5X^XN}nd3mxs1B3d| z=ub_c#rR6I<-p*-;{i>aY!9@SleuZWg>&b?_?rH5DqlMnwPhCcvvl4rrbmWTwNhiu z4pBaGq)gSOF1Fo-<|tz$fYtyQhw;7F_(DgtGUpKLIkOGp_gJ^K4g>?K9W(yYzltR^ z)jIu6w$ZK8TP(P`exe!+Ac5J9Ut_P`gGQIcSpcv1x)N$s%z?)L*pOOqU%9&lzV5-F zeIfy|m~RGTsW<#(!$_+9mYxv}#t+8+Fxo^Ur5WW-*7yjukj6E=N`JLi6bc=o0iL2o z^?DTXuCL0tv^?%k99}J&l|CN>uYGIJhkpyiSX0&9pu}mU@UD#-OTB^%+*-qFkLC9x z7f_Q97YUTZ@xrLlC1Yud)BOpjPh8XkniwrfOg92eXe7_)dApDpB5MlW+YY7Pwj|G(Uq@r8uJd{M48Z9@=&^?YV>^k5I3S~ znfwbntOOHf$B&)vIc9MQs5%0O{ZGdsX!yt^_QJ8Avco+_ARk&23r)Ixl^mvM2nl;P zjTQ3-Tp&x~vR6?I%)pm#^u${NwQ5kmN!tC%_;VgH{c0xfkWiCGIG|6U`G%%?vz>{T z^a0!?cIJycp5}7Bb>e3owmh$nv>deK#MZX&DW%w-dWjds6+@v!eqcv=e;TrdHU6jD zyYq5BCfd?CAEfA_SBlTh4QKE)Io2-V22b!!ARZ!2TS@$h{~!zf2CL!*PLb10T`CU0 z3(q=H1(1o;kn*^I9XdA@>B7WP2CaV$wPdd*%Bl3kd!H)?>chEnLje40Vus7d-!!!i zgk8h2Y=Sa^pbd2QxP%`!7X~p0AYol|=RjKE#ci*CXgddFoV1-E>IWGYWhx~aY&SoZ z4u&q(+c)2m#Wm5>Sgfs>+bC3>ia;>y3;#vMG;y%+qeJKciT4c;_1fUls9Tiy`1V>7 zpjDd?E3&&DJ2%ZqhBKb4uD)HNyVGINu+ntV_k<+w~WcvlHp%x_qad9hl{Wf-QAFrY%l?W zccX*{w4w&++C4Y{_EU#5(&wxe)#-kf&7wzL5)T(D==I$Xi>le@>#XTd^kk{#ney=j zXe)0A@Sf{xj1Ovu93f4f(k{ zuAwnU@z-={pS%%g+sT=|d79mfCRiZ;!Im-;9q9mLXSjrJ>}k-(+-}YG@t8)&-{@V4 z_Erk6|5Q_in=FmMv|VZ~Gw8hl^>6$0GuiB-qzBhk&>u3=++1CKq6`!tnPZNb*e9cd zfWJ<+*S<3JQ8JYP8A|G7plt$spJQw=loikbB1GnWCYEHs07jgU@qdZc5q3W2u$>y- zPNN?0uV%dUGzkH7jZsL7Mk0diP_b^1`8Dr-rmD342HWR&AC@d~1D{bkODahE?ufeE z`+TXSmUYMegbI6ub9&SVeAcbxc(Y3Jzpps|w3$ODga6(`d@fDg!g9nrDIdOA~4l;d=si5D&H8Z`+{1-Os)AH`&kh97uZ_+WU$-dBxQ;%2g}M++fbQcBqw_P z+SfN{`>=G4$8n0>tT@wYrh1!*0~J zV3)JcO#`~^HoU&l-r73C#Ox4h2KoA0iVZTp>NgCmLg3CVH{X{x$4H~v0PKheAt7jD z^Xi_!F>oh+%0m9WDH+a=8(>5hhv~klz@7eW{0)4J%pb-k)APl+@CJly^U+DrrxY;g zgfoX|Kkbn+R8psTu-D+5fc6ElF8Ab9AT|_Twnn?2(^bd9_p_zA{dH|!JVSinU`qR| zo%IRunvhJ%3GY03m&FrF7}L;Zc@6-N;Xu}!-7@+QJdJ*bcrum2`rjRZ4-!`AfxGzy z@a0GSahkj>T3sHg7GedKZ=3((OS40#3$M%oB1CEu*Ul)MI`t6Dy1RuHxj5Qm!|MQ9 z>g<&evef_MlowN}qSo69BS?Wa0rH=KfC3N)yRl_IA(p3Gb86Lo@V$MCCK8{PU@c$g z1#$o7kCtmUN{ZFz5i7Krh79AZ>#6hH08A{tgPJk*+iAakMpPcNL=BhSxcJ?8;k!(Z z{-L9P@EqbJZ5GznbkNHa?1$zZ+X(K|{XsO0A2l4vV@I>;An7eFeLAOQ)};+^2MXdY2@2 z^}K<#^mxDLB7i2qvnTu;G%p}Jy(T(|NWxFA0y;)9cSu)rUup1NWv4X!b9)ut$*dFz z$l1Zg#+((sRrIa_zOg{kB1MF8FR}!(6RmxhZ+iV#E5s3f>6#(oUt`Irp8JX>@#V|w zs26_)r(__j$cLUFcByq8iebr{G;ukJo7yLQ^E)Hv4f8AI=$T!h5!&ztO3-2TO^AqK zor>cR6u*59lf&F&>}No!3bheCe3@?p{KJr-TBetZM(fr=@aubes%r?>#k##KnuuC4 z8VO1jJ!)X{`uc`inZ{CaBk!Nh$gA?0wyZWroX;nrzibYwO3>R%@MmAC=R6_r+SxGc zFth%R$C>EMIv83n0p|nL_XoN`63%7*y8;%D=fOw@d!|&)_y<2{c!s~5lO~>wJV|=C z`PR^_WBVxp>+GMmZfLLQj=*!lA`sd$!;j0`#;8pW@Dy)Yhb%#SCyw3)Cesnxg68z4F{$8rG z$-BfAc@+BQ5l-Yr<1+8mxA@5z$$H2%6bD5;Jmn35-s#tcQpf1l0T4K3d;U^_zT#9j zk(c1*fO~dGb$@--OWa;xloP`p6;l1qwDWAJpCP>=TstFhi3OMl2um~kR2RbC8uf_K zXK)FWC3i62L4hy%K)-|PibJkZa?u`?RR~O0Y#g9qW85}xf260S!DJjqpZ)6ks0FfA za##ZoVTc{9VI?FQ0Ih{sZdw9i<|A#2EJT7cAuXn3z<3N;Wow_6E)xvzc_R^HC`RLt zfY61x?4SuZQ8V;Ap-za;Jz_1@KwVs{lQZ@r{SPcHdy=~0;`m*_7LEHtt=hN2fR)rM zUw{ai?(GPef2+z*b3=^P;qjB2kEo+!85%Fnoq;oQA+BtLbELqxoY8P?_3F8in=yb| zRUQcEvjTg&AvXqCfyZP9`M6iE`BM;|yJTrzSl;FHz!q)v!0mDB={@JGz<6$n%L#;Zthw+PO~NQ+BCIlv&Y7Q;Z_+Dw=;ufG+q zUe4~RzM?O>Y@-DeNq9fO`Fu)hHKpQc_ra?qTDx8z=R(Nh`=VXEV&c8i<)dQyf0N8;l=3%!xbNLnRa?H`MawjBY=w{&Xc$?0XF~E zAy$ma3ETbBtwG{WQ6ZZXo_b8D@iQ(M>bXMCT2;?hu_^~fDXF7>HADGof#-tkpP-&9 zAQhg7KCd-3`z1h0flU zAYw$+cB^d~5qYdNoaQ|ZU5NpG?<~c`>*s z6Z7jtO;ua_u?lEUVVnD>G%A@)LyLRO7hfdIiT%EGi+wr2Fl505bqU%felz=vu5;;m zK(Mk|7W_+#Zu%12?hoeU?zaXh%$?78-tX^PaZ4RBzcctDO$;#NfLZ<)2juBW&Z%i6 zRi9^wq4rf#7rPKozPhYuuBDzb+JX_{`E-~kfji{DEf`<7izXYg@-(2)*7h{ZQ zCPu)GzmSQ}WDhG=d+;|+qyf{nYbuaJDB)t8_1Wn0L;`|KBKdt_$DB=ZJN9l=pGxjz z>km?{`SMn7w|DekJ%Q&qX++K%L;XLTc=3QmV@sP0h1YF=^z@7p$I(fQvuCSPwaN zE1esUf#fK_P&EvBXKyweb32jF)H-kUl@LfrDP&j^Cy-5J;QN|p$e`KQM!?ao;aP)Iml{-kgd@`Vn%Pa61zfgxlN+iJgjOwN9jG`kHr z$~^0l=$xI5%RNo}n5}yMPCM71mATKur~F-KJWaYm`pYDuk<+ubZpz@wY0Gb3M#}X@ zxXI5ekL1LMm0g@D_0|8LmUB@!j6cV}QO9*I*-gD1%m*vj-r^Dw*0~2#N-LtZ_)w)qkbqROodnfqS<2SGaCFhz(qGjeVNt}{UVK?WP!QaJ z_^gcHbJent8Mt#wz1U5CWW`8rzE+A>VZ#hjdAHQdr*KfdF-r+bU+@%ZON6 zVM<@++OtehwwTJh51e`7L~4-``gznDw=CMo7YIc#k}lDwSy9L0L~y>)sd$u5=)Fij zg3DpP848@|qQkunt@LVIwg8MD+>#>*#@KSe?|qw8W5Z-P&D{dRTJeW^48Uhjl;H+B zXD*(WDD*96Z6!E5d|Q%qLnjWMD(bV|R6WNu;&4VB=;(SZm>0RFaZ z@CP8Hm{@aUvz}KAX$)BCdNk?=&TP=lN-(6+TEs#e`gSy493gT}(kg1f}iil3H>>b@_E>L0lEdyxC|#v_PCD6Faz zo~QBgw${dWcsYEjI(x2<&LOMTmGylcR%kM5y8YJa?NpaYRh#ALh$Qp(?SfY=*1iMD z5zt<)G-+`aC6qN^bjXyc z2~oNIboVa+`L#m1pu_kDVsnRF#|*ZoiR_s7rseW!VCvhK~!u2e)^BO~OBL<5yiRv}qg_l<-W5mDi4 zSji|##JwV+j54B(OKBl1nfJcGe);|j=kYkNb6(H+PH#b#iC*5ubPI&O`L|x6k1)g` ziuv#FzyP_Z_J-aj{DD;>?{Wacwq@#|4rMF;Q)F*AWIMIsE!I<3Hwd_MET8VsjS^)u z6QQmFV&!8_to-tcG)y+4GFIZE!b5}pqkLp528RTzqxz}AkTOAvCYm*3HJHA-uh!i$8VJ0s-S`n|k z<-<1j?C0rCM|<8$S@X)#mSUSlO3zuR(AD|DeNI+n1rD+5!>I9AdhO5R+1$1F&U=T4 zAtM2V;V?gvknQ;lF=Pi}(}RD4fL!DLC&t+Hx_#9im_w>%K%| z#A0}%?P~ZXc(1mi6nm9?1loD-j7VY8w&@Ad zwSVzt@jrKXJvW_ZgEoK9yt|zVt{a*2l=K=C3mk8$7Zh_?g+%AYc7Qnk7l9Im!_ed{y$Dj_^cg*#jj921hWkRA zO7mD>0MXk4BSeO|-yFlbitHj$#Sqc~m~{f-BZ3yJ`c;Jfq+2Vq zdmOO}KNHTg?+FO%5gTNB|K%}jC_g17V+e9NMTHw=4X`POHiaw&33n`3i)N@r!1^8{ zPv)0a*L6G3Iuvs^^tGO>oI_Dw1X);l z>LK8~Cbo3%vk)bVE>5lLuHzZh;^~P5ug&Mllf&NqH+$nN1OPC+S zT_G1-=HoZWv)fn>?|z=XPdUBGx}p9!%J`FnJpRFsG0%it392Go%+=fcn=)B)>@z3L z9nI#j>SaT6bD3_`&)AzV?TBYD_1mM@YL%vi0*oe#bQtW^iTPmnPnXS0E`uR8OJ~j_-{DQJ zU=6*-#5{Wsf<)O@7SS#KXl4|$;Lsq168_SC2pxQn)j_j)20L^w6l8#8Vm3n5eI6YA zzZQT93##x)4Wr%NO`Yh>4kSh2&;he%E{aqRguj6c{q+)eousxw$r!PolaWZ!%JR{b zZPJ4YKwEMKu6?z^U$B4PzIV`SK~{9-#`lHXl*T5)_0v|Pv-^2!LKxkdU=XHd8OssQ ze#tUbLzMWa$0~bjnjJZVN4c$d93u-Sx6enq>pjLeUurkzPw1-_FlR%~0@{P4{YD5c zw|QVTc6QQ)`R=oKcE^`r6@ol-14rR7J^)OvM4nvu-Ery|Ame?8(HjK2a{677ze!Qx z6-MYDzDE;;9b(>qoBS|{So-m?tJXYil*`_a@LsNdzr&zy-Z6Zkcn$f?E4L#@t!X27 zMAwEX&_bl=df;gN^)}{jVe^19b&pmmj#l)P&t%_1D;|LCFzR)lvlmkz78Sp>m{bwF zjnwk0ofAS`hAn7k>|*|<7XIm7HJ(}^c_=)RhWlJqpcD;5k(3u#_J-Ur|0~5B3z)%s z?bKU=0&MlO)vjtV@Ky(cM-RLPskq-S^Mmhc)y{U>06lo6fX#~dbbGB+a#E&cwdwne z!ijFR&)lioZd2Uf^-7|^814O&e~Np;R{-3NTVU+}3ze>~oO}877M}7H7K)T$39jGH zjp>7B%lSW-zs?4pl7RaPPtbL%=3{SO`@^7`;OSU0oz5C4L&xl7b(bL9i;ve5|18y6 zzThq$Y7YvM_L3F4g>ykKin2$+hMd=OfNaz*CeLU=PD9Cn3n5#O5!2CYqCU*F`N!^y ziM7>aC)H3GaW!z*l0znz+AUym$bMi;QpyO7Nr8JLSGLggONaChN?l7xE9A3^OYpA8 zN$eC$HWGV%G^(L=qgl1y*o3lWvv#AB2=K*~`Mu6In_O+--hZ>Z{Z~EsA;T}Ym1*V$ z7nXuN^6~?Ey?tyRMA?{jX!ab0vkoc+O>uW(kgk99xpl-a0Gb}aARQ*S?1TlomK_$i zB}0dU?UlIqu5No}8lXg&+qH-LV{v^q-bjESWlMPXG-yYD(6U@1Ch9&z*lJRncC#Aw7QD7(8 zP0yD{%Y*CE!2bc{PlLxgdZ;d zyI$4j%Yxr>TyLP0QwqG)hL~aw^-b1kjOQt^c8(>Q3gz8VW;yXw=e8?U@(4vsAks&r zRpyx7W_gfVa$vT!!KTb$nXk4+50ng?^MPJ>^R6-xL)j2S(i*mqWbY5eNgQ60-nc_g z>N2@*{JDbg{hTgr{K86ZUAb{jvUX4%HG-|DFHVw=w*OC8C<`^MkqU8BM~mx`suNSQ z?-z?qi+DNtRsLVj$y(jt;!0H-vStB{kpDx1vz?6;Q!g#&69?@+nh~mVs4MK_jZ^Qs z7;ha9=kAq!uzk)9KEjUbymUZQ>O>1I6G(_MK29|>)uNWyOw4ej>VVb*z7dB5UN8p= zUL{+CFq4JZ>)Vkte50l$HC4^}Fx;P3C=$c??`IDxA&RTjTVOtVi=zlo4Od7Ft_&5G zpxWv_DjT#UC%T)Wb2jHEQ!w^BJMLZ^P7#J{L>N*N2ax}$SK-RZ>hSOo5^|3aZ{zna~Sm9MDCv|5J>GbGsIUS2!vonvz zrT-K5RD9s_7h*rSCQFsT#_o~_=yqdnzPykw3v_S?Qm@G%hj}dO?lBMVdiYWEkW9{0 zd9pQX>RCQhIyPTJq7lJkbZRDZ%x^M}_zb1_f;Tw!+RD*GPytk1iDx~TA0-{;Rt)pK zGJXN+9z(+Q;FirA*PQ_Gu^zP-A)7(s1Zp(UlUyIAfod_@N1y>yCWAG@STA~K?jDM3 z+$Ii=A%P2cO;N=jfR<@eAAJlzzqfm1CEp3E-{5SP*L)W&rk`D&P2lPOP|QdwIS>Ek z=eT`3KD5&7za6F>jmx=lLNbVjT2;lFLRhFriT+4Qiu4|PTt( zxji7f5c4xKP#x{D73?RnqBI0Uh#(whCE9ukXNx*QDP6L|5)?-e>9XwAk5Q_PE!^d^ ztG{%)>|3L{Zfu)GZ(#Yjf$PTD0`K2cw+}4k?TE%aKKlHP6d}Yqex?oGD_yr|xUnU% zF>i-T{lD-+Dp1HS`tr+T82G;ltOnJk6_*na4b)44ZJa|7|3jK!k((gBh4;WHZ8ttn z>Q?e^%&?;8hty$s?z74K2bC9Dn<+hD2Tt&3p6tiZc?d3_^ClBEsk0qSuPlnmvrZGBdiqwEi1snGYoR;K-O<~@B z2e-d)wBUtUIP~FJFzp_jo&}x&)nFm)1jt;pk^Irri`M%?Eh2k&AihRj@|ChcQbP9z zW?|pE;CwA)Y?J~(1SCcPBuPou;t$$*LOf^@sC}4t^fQ~=@pnwbDNLdCsju=17=5`G zr3#!l=cLk7u*a))Dj#n|LP5UzAa{~p&;9gb6BfV7K7ajU6_NTdJ-@9C+wz;m+ospS zetVdoc3$8Pjn_Wir?@t5J9zvk3l4mG#D$r0!dJ&%KmPIzp(uh+(m1)cCG$7QKIP0# z#y@Bs3D#{@G6JQ)rukZoOtmNfS?B=kkmg@zgC5z7nS`6pD?X)07vSg5zkP9uOS;$! z6pFYC=9K3m32N&J?=6t`6R#Hz3#=D96{Egof@L0uMtA>5Dv5tEM2URl+4^J#QiIkK zSR9h!G`)0`jUgWnGcnENhVf8t_6=jLzt-lMRlGps0?IFA6q#G}7ZoAhs_j&ZIIW|w zKrJysRdL4$Q9lddn1+tc0*QMYgEVldFfEr`&kRH zZ<5I@+77K$1y**zO;Fx;w1L-C;a09q?E8WIXd^}rQ*~d_q6({(=irV-84SDqmG#Wl z?*VspaPDhH4Ccf(Q=qN`E;Qg>*mv6$S0F=FScubk}?F8gfb$eYDdvuPcig?D`tGg zniTY&J^uF0GP>qzST{C@?Snz-J}W+2AR{>b)@^FvRtsT2PTd2*$@HD z{~^DlbNIx-95;?D<%MqDE?U{OrjLG_r-Okp!1O&btf!U8LkkHwg`0MM#YwM7iqVf` z%C3Ogh)Ub9I;9q%i?3}jG;%wCCx}!BnNX!hZ%yt_$cHcLlif_N_7GE<9~i&$t93zp ze?_M+c)US2br&nb><~50 z=#K<3GRSFBO>A{|xUkazIW$q^I1C>ZVLd#uE-srbyI1!jA96A5108*l$nX1e*Mf$0g!x%~Zw zX4i@4%W(F6x~$;zD{l0HL$B=dJGm^-m7l)-pAVRrJvx_yWtpRvkLRo}QJwc~AySAx zOqA`bY}L=|V5HK5*9ogGrq3bVV~=NAoG^mWCCQ$Prv1=d=v}&=so15XYWZPi#yd9r ziHc@+ao6|s5XEE8s&JLQn`|a+NBKBABN&Oz;)AyLQ03&<7pS^3?$km7mbN*pDb9OM z{rF!sfg4=$U2SckWxv_?G71*WQEDp%lX#0_+rE^WvRi8od43yMGe0>=Qkpz=Sg|Jv zeq>1eraZs0{$cJw^?#RyuPm&q7f*Nyl`*zGoWt1xOxUXy*_4UnsRSgAZONIn?Y2?=RA&%$6j zN>zx`!_m9mAFBKoGB(z^&Fq#EPMx6tfr+rbn+C(A;`Y#w4Oe4w<=@LTGWdh`zw@7r z$+^%)w+?C&KL`J@#%SBjr4WEihonF&<(U8u9kmUl%)2_*+ZNmSsu&?6f8y zf=yI@=yRmh@PhL#n|_fx8L*wxzceNazc0ASA?3>5bBB%(^7Tm4W#&J8Mg`Ol9rv(d z5F)nVAY{sUew*zTN>U{2)b)2KzgupE&m_Z+db#=%l@oKJXU6ixXG(jW78)W$LNjBR z9R5V$yu!DT^c%NH?zyz1tt7F4RK9WAMU#p3)#`(;l?q5*e@IeUcRXlwGf!4$yoEg^)@YIy+GrAZ_dYJTjdqdF zYC|KQ?25BPBB8&>C9by>!duz{1D_mkYMfqFyj!Xhj0V4z)FH%QU2H$VC9eg19Nx?` zjo(pw)7o-)o%j&3<~X!Kwdu|(N+UR4sm|AU_Xv7i0%&{rbc1&zMljiZ+y6Dhk(gMRF?z5MjF5eUJ2A z@l~*_TsC77wu@rzZZf`_BF3pYjJ+eOsI&1#iI%aRUd$CyhEyHJlFKR5=HLub#h2&) z0wt@-#P2&<3%X@baRXEZw&fPbyE@!M?L3&3`}fOx=pzE2o_&M`ZZdvwSLR%vu>ZN; zhI4e!6IWY+#J{m!8QPa+A)k_bw{gOG@KO`V2R_AZM@#vxg)UUQLn8H@+jo=!{HHeC zB@_8S^>!=_7TMk$xlw)(=!ynyKcc<-;nv1Djq`~D^tsJT5k&^?QmR+V*BfW+6`&UV zUORWeQgfpgC|3+0^i&UcrHpZ3avZ&Y)9G~&mDnA>0-w@3lri$sF`KdBDd_X3{+N~z zkL^#CPzg|wiH8~5>f1+Be?$HPQZPvQo4p?ivv16pK{_<|JlrKwEdPgrld&qQb5v7N zVW4AN#_SSWJmQA*ujcd6rHDZ)5sX&zMsZ2%oCF@rnsmSk>>Y7ie>;lSd%*>E|813H zO*DW|3Dzk>40p!&JN>Re#3mB|HyZ41G`X$)EAXcQs%QAd3Nv?%=vVpR;d_zq*klrN z3?<9H4iIbmKxMdZH+A(cl{(khv|;!)_BQl0@A;ka&N%+|yS3Iq{*CEd4D}2Y?^D(s zwB}O}dRE&aVpW9X54V<;SWMYdcNrOpM{j#dC{#|TcGQKT8Cv@e52zRu`7St%(A7~{ zN#t|mH={UvP3l4wb;n|$82Q#zQQ^AkZkS!4@`sr&s=Ee1vB2s@#UAcshG^5c_me7x ziznqHd#bPfyN$cXMd|142bQip%sptBl&YUob-y%nNpR=MhPuH} zBlG$O)CQeQL82dpy|RmJ)czK zAb2OLm`wk+5u@8=+iP#vn*j=nFQGi2VC~F3e)eI{_U?u2I-A6nEEBLBy@IdduNhj@ zn7)v}#Zfn`^-DT=&p%A#%_U4AF#vse7|(jU9(W%}Y@tX)2ihvu)(*)8g-)sB_{v!r zXR_z@8fjlp_IHmMa{0%P82a1%FfyJ7)w!&abtmf+fb>9MxO8!NpE#>yDb=Q9+P))} zYIdFJG4gN^W9TBJ(_nfCu|t3K^~`qdi_Mq0o$ua5NHqloA4Cf;eBBfBZ$sPoH0D^8 z)7b}oBi37HN*<%US1sxjHR_^i?@cT@uOF;E4(A8@rW2rjU9vY?32&XTk$om#cdWa- zR_6##aQfusVb%ANmIEN0_h+g?6nm|?hSWCa_BA1B?g$?U!Wrx1k!Mm;5*>F)ZthKv z{9+feCH~)TYoM#P+u^PJn$2b8{H?zPi*N7}W#|U6aR)XHgab{kovIc5r?{E(`CGu! zE*&1KDX+;Y5E7z2hbL3dFW5&)c}4_|f=YWrgn2CM3kFcl!0@)j*C4zCo_>#9$F7_+ z_ME~zJcArDdi->3?7uArP`qm8CCF~)vCkt{yZ_C`Jf%OGVjtNLjk-x5GzQETLh)d8 zel6Ro>|5!eOyP>y^KtVvxk0d09?J&0r=J2qkpGuYc#V9}A~}P?w0q4Dl+35y)V8vO zkdnjvyUUplX3?86p)+P{(-*p79|&>Jp$J&>k9#XaBEQhXPk|J%7z@6mBL|GWH9#xp zwPg?yCEa>8#I||I2gNCcE#PW8sQ4&z{Gv?dF)?ryS``4#!Kf4os;2uOd^lJVa2vR^ZoIx0=0zOvg^Y8W=sNCoLUX0 zhR-is{)@H!B1~~&j9{f+RyKn^3`Hi?@s&XEt%gmF(_MK=`X$57y=c8womc4OwbV9o ziV~$AbIHEv0{WC6{r(;^WE^_?AYybH9!iUgeyA-(-NkJuaGl)?u5R(_HeKQn_KtOU z#Zn}^zHWGD@f=MdJQ<4%r&?_~lWcApZA>L8u>>1T>Ki~Z?iR!vMcv0gg~5tRHfPmy z1H2p+G%lbvxVC-BPaw5+`*}J)x*G;zJ8=BJVcCbmgBM9GBK7M)Z@|Ln@fy0JfObL% zgOGsSGcV7@Q>rZslR>ok8eJM!o0fGT!%a7C3BCvmQ4zWp!-Z*w|eD{P(Sn z=}UnW1kW=(h2{rM{A=3H^Sev3+L-WiS409sHENZR#|=pAY2ios$KxVA^I8WI7LCNj z8t3S(5B6Jpv9xFu69J(XY3qjT-||1S8rLz%6^mCLniD?ElKJ5WqNKjh9_QoDmWubX zQjgehx4i=@QGG2*zwxd|66+5$^;Dr78}|E-n=9!6V{CA8iO`q#y`Ni1xp z>Ry2IrgD2nFTqJ1Lo!P@1jTSsH3H)WSpGd056a5yiazU@PCk8N_Z!2CICI8?@u&vE z8h{V?UFFpZkwREsv6LQ=wb3QxBh`Q4823Xt5CQUkpurc}XLfHrdw9ILl}McyM%wlp z*4;f-y@OnJ-PzI)Dn4Ii?A2+HLu)TGIr%G5-KL_OqY1WPr88BC`!Yx zEX|d~zvYbvzm2X*`N)|WS9|zyR~?cPbyb8@6iBpQh54FUjXkG<$CWvA!!(@mPiaEK zn+(f>AA<`%+#}q!k7ZapyF-qsoeXjS(^uy}0UZ0P;^&$}!M~Jd-*&sC?)t@hs5no# zw>@l8yY7b}FA(E&2ihU`T!|Bi9uLD5NNZ5Nv3~{NN>6Eo269fvyzKa%xv8o0s*t<| z4HjDpP(x%koN8k>*5246%N=}9pP62;lR51arn|_qh8|Y9?}C5pB8A}{EYU<+&zF7) zv|%YB;uW6vqiU?qb*CwUZs>XFG(USXC8^R%%$nSzr)KVYQvWBPIeBO7cY>yI`|>Xu z?%UekIdUv7-CNy-MK?~y&GGsIFVta!{vGea$4EWa4rO^i#Crx(VsYpsa_a`w2_37BZ6IUU#7Zw<+ynHgt_%rA%Jj}QpnUsR(v1d?m%2(v?kZzvzZ*jSK-RM@p zUQuFNt8W+I{bdhtSI+HD^hn`#>h7+5G&J?i1TH{!XECrtp%l3NqsR-bB*oc;4@nGi>fRSzO=LR=;`FuygZ4~wb4SF$|R)U(C|2(_V@ReRM5_oIF{=l43 z=oaL{-L33Hmpk1&_(SBUE7lJyGk^PaKd=ySz z7_Tl%oEZOZN!@hdq#cZ%V3Jp3JSwdMSAX!w5fV%yLaqrTt^FGM44r%OcQ8|;;#kB0 z4Rza+bF1ajWZ5lNNYB6E#EiA?zl|S)jvw~Qy*K*vBketGem%=&r4z0pk;>Pv#=aA- zcAdbgY3J6x;iJZ`+lvk>_DBD^l>9(0Ja75PniK@s`{+r)T9O?B%wRh;C8-tOXIy3^ z=er6YyWrQ@)^XE^a_^IBd4gZhHR)6>7=FU?Si2=84bs_&M(_(>lCq_N8)G7_=}JzN zOWf*1O|TTNpCaJR0VGE2r=O4ocf7>J|;zCVaC-*LB}3A8(OD-&^j( zwS>if{XLf~PY4Sgi{FVkU?5l{+kN6+AU1!zWPH)QiLA!8)IuEON)LkZ@Kxp5GbraR^)#q^`)wdJY{^^!x??naw}j#`}=&&{Oxb0q|d9`fWM}aHf?lDMW73>^-!V2tI;NsciWz zuU8#*t*G)d*l5MV$B%!C)Zqhq-%OTQO_auk-nT)tY;vdS`299?2|gnxSyfNj!w&%F zhK+@(Gg^`3@`-bkm-d1qsEct&Qt?6L6IXS0v0`_%zX(Q3c$)J`86;mG<+C#Y@t_#( zzQd9xomx!4yY4w_avb_ul5QWBVg4tVtJCxir_^}ivBbuob7`VKPa#9xw_+f=^k5n8 z(tQ5ZhWi*FKTYx2*Ny?L$&gM(E(4Q{z!p3C546I6*Wf;0=YoAlfmBuZSanz@%TILg zET)(>WrmFIG~;M&!dJKt67f+``Qq?x>%gWr9+-zwip0Accin|I-wt0v2casr28p0b zJl>z2vmsf)ozz~hm|qX9*}7%A@cH5j?F$9x`IzwQIS)?v=7LVbU;2aB%)4t9vC2eN zVMPq(kI5n&zWFGxjPzSYvD7d5LRXGy|CxHiU2CWN zl}0Vv=LzW^ZA%@%QMt`6%p@L60$^XvYjlF_*n9Ls8}>6N^3PbVt4?F@+o}Y8I&8vz zh+WeTj#nKmsjc*G`F~n*qNJ>D9jE;u zc*o)~VQjE%q>olb0VlOcQ_W}Kt zprLN*Ztt?_7cHMoG1r2&-iUA&Uj20Y=19Iv`Qp$bACcwZ+sSZ-_a1VwuiIt#Uxd5k z<%a`03Zmi2VNeqDrZ!rq^Xjn!97$Vl9H$a?D)jqJj(0MSiNxuPfl;E^xduF`QqPg%@4X zqlmYOfiiB=2tP3Z)=d!P-~As%0*wAysde;x|3q=oYS8)nvSQa3v#1t{sl^$N5`ban l<5_i2G&9banYsf8V83UP4*tby(M?_u*z7-OQEuiL|3BTs8$bX6 diff --git a/crates/wallets/src/wallet_browser/app/assets/index.html b/crates/wallets/src/wallet_browser/app/assets/index.html deleted file mode 100644 index 052f6448dadde..0000000000000 --- a/crates/wallets/src/wallet_browser/app/assets/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Foundry - - - - -

- - diff --git a/crates/wallets/src/wallet_browser/app/assets/logo.png b/crates/wallets/src/wallet_browser/app/assets/logo.png deleted file mode 100644 index 1f29796cdda68977f0dae3817c35cba6dc30b116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49040 zcmW)nb6BKp8^*J(-HgpP-fY{pU7KAqakK3ka{djQ#Hc36Yia z8Mp}Pt|l!8Q9Dg^1pEMFEvhIA0nreT{Avmd0ntk(Da}@?i6%?iXK)X$Df^kOaZB43RW=MG1UtKHM9Pc$`1@ zObZ98r_ zYUtCva$?uV`%BYNhNf}$bss$G2z7|3v<_He@86O1~1RN0*B(uihbQw!Nc=i zVslFimF<#DQc0})Uyp15o2iE(ntcE3eWtdxq^`%E82{HZ6pqQuKE|fW>t74%%Inq# znbz+rA3??=IJqAaN8j_`&AS76M~K`npJ?ETzs??Zq}}QrW!uZfr{Zeq6y_VL2&v2C zE8hH)CY`$6Pd8R#DqV0M#<$LK>V3P<_PE{~yP4r$b!htQ@w=id%Z~Xdx8wQ#jDLV< z(?9vIpSu#r6sP+!k^1dB1RZ6H8k(d#@cIXKxP~m>{U(mqWP1bOgyvhG6?oRaCVzFl zkTVV7H~ASn<54iVF)!FdGj#W>X-0@&HNV$u&2=Qe6?kEVG^`grxyzgPx0fI)@MCP= zMa(B7?``{Bp|O^d5n26Os&&=BZAI8>$!(j04{IDtK_4%hU>e0N&Q9v9*BRgvg_x{+ z&+~B=vj5xN67)fi`*fffX8_mO_oUEchW%g4(N+0@tMHOVy)V~6)8DmwlEqDrZVU`b zirx%pndxLxD1w5DdufUXZy#{p>vr`wb~8LzY-vtDjC&t8{EUxDHZ!dAd^QZ-#;Ed% zd0n;$f&bv;MV_VmHin_LeJA@lh>lXL5mkTguB5d7M-NV}PKJK(`&}J;&-FNiyFB?6 zg9hh7S+>)EhxMid2Z@)%5CS z{yV9@kbeq%4I_QsawM;PusG-;Z*otK?ypUS-$l1qAv~$Si)*H9R)qW%^SPB);rDknizXS}srN-~V?`#IMH=nm zj3lgxl;nz3>?h)P7Kvo+{1j|2klLs$o0h*(O0vRaPULIOIX^88M*g`l4d>@+-6NlQ zkl$iJVDVCL4`@(m(PJao?L%hfdTpyOEMMO&ippqK>~hdK=w-DISi9GA6zlxTb1BJh zy)(eA@hx>;^ze&N-YB>ORn>(}t)L@vFx{B7hHQJybay?$GQ-@QP$i*)$DSB^ExLWl zgCuL=r4-H56~X@NDJ*FxfLbu4&;7Gh?C@9-qoK3H63L?F~*nhvib3z zKHn5E^VpN(at;&`^2N>g#}gsd$foZ2*>1e>L23Rpze*kSn;=4FVCmBLyDpZ<1yUOK z=f!_@w)n5?z9c@Aj#c~X6K(B%8?niZ%cW!jE&A-hci)La>Aug&vrGj%?Wfnh-Oeks z2w6>LELuP@(_j2)7UP_^DZ0me5^RyV>1ohSw=B!Vk0`<%M~L9fC!+pE5yO~-{gjvd zgd>#R#!R$9x!2}N^6qS9Rx_t`x*B$<*#GE^lR3Fou;sn~QNY$BuOwi24(wi|6@u3f zpWEcKo*2OVl4nmXdARH!_6UJ=$59lQq)F_4zEw4?KVC+J->>YSkJEoOxizNsA5+@W zRUgP5iwGZNm>;yC)OO-w&NL1pF;v$lj9!VFkHi^X=Xz~q#`NClaf`+^hJ4EldVdN# zqW+4ff`x=62`dIA8qx@&44vB%#8B796G&l?3Y&uhU$rT0nuv{4q9LJ3SO)dJKc9#e zhV+l);>^2EFg=_bxKG{vjt{&oww74vErc~MQaM*=C#FFP=}xhi{VpeO%4NLr+sN7G zwa(MV38D8GT zr`aj_EegYMvht>j+G_B}%0bWb_$@0eWJVV6np(R1(&)mkmsrJ8v3e0EN|}CCEr?cx zRzwH6lc|?Tq5Y>{6yem-VHcAeAVUhkhR6I{-E_3d-p1rM|&J|>zS8^sSb z7E?w~5vk0(r0TkaQ9$L18{t69lBtPjAt;Ueo3fR)l=1fJfI7($%A4A|tbkyaL(KoLtoDT7v zxkkIpbVI+REi}JgPaHm#!+tVgC1afYX=Z?fT}86NpL{RS`}#rNanb#-v4*WIfGOig zEl|Q!_9?jwt8}bmd;8mEggv2vw~NZC?vis#aPy@=v@lB97nzfPO}k-gYy`Fh2Eh!!0=ljM!;l~`%uo@F=RM#V!*c{OLTJQI zG0`<+bVk9GRcqr6bveluxGNCnHBBHL7Mz=Z&x&?u?~PsxCw-t&3g+FD47gwXaZ>v- zo2KvNGY@>8JnzCEPc!O3`Q-O3oP6)Vo2%3XTGfAaBARhrF-WR4e!H zy>O4OVwCfCYSwUv?5d)8|Ne9UluAPnA}ttXOlWF?wUmT7)V0n{O@=}fz@YsVDrG+< z%_$+XAQ0i*lOymQ(zIxhU)-Dd%bBVQtvxTxougAf2vJg;%A!&k^Z8Y-On@&o*#& z=b8ZY1?b@+LeD$0N?&5U8a+TT@bxi%ju}vL^2baA5$G3R>eg<3b1zy~omTgGdY`6x z%Mr+6IWnO<&m>Q;;VsoTO|Z$!B%p(A&qKQZkC~1G7buGn-ztpax16Qvk)V-NBQcFw<2%8rQiMT z45+N$kJpB8bGoIZ=#o@<9fNB+j{ET0UjllLOWiN$ol9wm?O3Up%|%N2 z78z15DwnFj9xukLQ=!{JzJO|}5v`O&KYAw@H1HrDOMpA8$v5aOI5=k)sY8=isb*`@ z`Pc&mn>~`Io#_{{^iN`k;^XHV!$;d51A5E~jLg#ILZS*g5e3z*eb}LXp*-4B;sCUk zxE&wx<+vO#u&oS%;||h~9ynZa#6h)bO0GxnO%Uh=Pd?s{pBIE*{>(tpgcTiMhS5`J z2IPOH2qmxZ6}jbJ0>A8JSf=TQJ|Xg*2dUR?F})lLf8Z;Ky#yrf?9_cAmr|%Gc5jvX?{XpV_bt56cnxU{_st}zvK%rovq$>Ti zkdHjxY@iwQCz>Y5rQhR|flHsn`n>uz&cH61F6u;#O;!+c|dyrRt<|LZYv zuB||r5iEwPd&Ko&3-hghMO5PdIP>Sz_s^>EFbk?LEew73Q#)LDhha7PVM499MMV4C zMD_+oG~9@%-$uX0gCOLC)~yg{LauA1{t}*l022t>jEG(wZ*f7_rQJAaq6QGp9mXHe zOQxb%(G3ebiFf$X@%IP*1|!elS%efVJ+W4tRfh;-&t*IU!U>l4tx)qgf$LG^2o(J5 zV4ytcHB>h=u^_Bt*dC!(pdZ&PK#ZQb+v}ThTY~=JL;JaedH(FZd&V0282>L$4Wuv^( zqy9JTWAYMI)`@?Eib?;R(x7&s2!brDXl5h|x=(mBs?~p1#7J{(vQ%s)uV`3#VM9@# z)bYtZ_3d2eG(R?b)|$>85$U>ooPHdYiRyWuC-^*N2$UADA|v?@EsE(g9nlgns*j-x zUS1BM8y{wv$8|_p;U3}T$>f>^i8qfYr&PD^M30e25G6&<%Qg4Y+~IXBLqjriaPn;h zLnn!b!IdU;cHlq}&KX zv;U?vmI}=$LU^fZll;a_etn|A&}*G*VcvP{VQzJRV8tzw|EatJMD(U`CPgVWM9cSN(!Mk!ntp1=wlYo|oRP~y>njjoAj0|h z&aC4QN6lq{kst-jB*MzbSC~%dR{@XB{b=MJM_uj`LmVTH4oN^Gl zEcQo0J9?M$lA)L3<-(QR2BDRnTA_7J6vEZFF@!qFN@agRmcih{W>&$>$tfJQWY+nN zEnenm#IxWg(GrjIBza~!gujf7Lic<~FtyX8Zlk`HxkWbBb6}bqhBNiR_#0?Rgc(U+ zTxu3a1=3E1kOZ$$ZU^tYQjzrJPgn5#7hUez(`;ufK~EEH65}*i=6iZ`CnXmbfe)Rq zL)veW*p^+7Fs&)*u+wiU03%KzIhHu4rMKQ&{ds|`XO2C4uNOA;M_yYn3Rz0Do;%=pxjr#qc zVvmGT2DxGR;GN)o8S(kdv9p*2r~vX`W}kL$=FSZtu#&34?8chH1{CvdxVc3G!xSAx z)ZK`t9>Me0xgNh6FS>4~mBB)RFOwWI4$K-Y8E_ieRRtIcxK;GH*fNBF9NX62b_Ag+ z^yDkHC@ovy78Bk>B8~vHJMNchR}*PscG#C66LTOwfLzpC_s}%N(t{9hqe3XPr0I; z5B2&yl&_)4!4m{i?ZqhOUN?F7hQeu-+HB!KI4U7osQ)(b-rrmTnB^i0p?mKaF3b@W zGGoH)v34&~;*=UKEQ3||EG|PWZ`0o6ZoD$+8cH=UM(f>xcoz8(R#XHkog)3|6@;yX z;5hs3uP6WB3i@C17@0B8TW6@L4QkNQgUBaq8GlTjM*-!A?lsR4Lpj zvPM6oR#GskDnf%g$25XxC!pvgS#?VNqEWeQQyeRHx1iQnJg65-#Mek>RJ|eQr|KDQ zf5n53Sy_Rjn<@(ZFD_IQT?WYAo^&HWK@WjH_DSKr+JqvN-Zd2F2r3rpl7 zr%oE_uAr{(kN)d2;=C@OmMq7@RJS(!+72uED^3nR%hctEbbY6ewP8Z%9PC##)#IO@ zH`SW|iKsiv^;(-0p|IMPgIs_$2-^gQh2Qs8=J<2<(FOic%v`^HYAH@TcXaem`J}uU zpUq=BEwk|3bjIxs6^fKjOf@tkm6G3qS7x1Xu1wFc^9gdOw9*^mu7#KObRXvCQ8yyj zyBOquKdEP2h4Zur6e5_bFXhTTu*c@TMLfSlIb<=Y--!pzh&f-k<=e})rT&Dq@z7H% zn-vB0z@>XaW(UGEb}#f3otLj0fTqGTN~NOP3-{W3-yDk|wHGLD2Sbjo(tBU21`#X~ z4{CXnEgmnoqqN@3);>jawNL>pLJ@&4BP4*K*qB;<$6|NNiRD!QzXHERHm#VvK3>TWSw>+N zCATe`#pFCjs~p2#x2YDf;m?Mg(~aO6JrO$FPxm931wR_vmZn0f)%!))Ia^%ze!N}y zFZdN-U>**`5jrI38I7DL?gCN|ISw(8%GPWJqa2|rH-meUSH-Ry?4Dd2?1M5#K?II` z^BmKM(gS@@1+%J*I@8hpQsxF)PWpcH)nov&t%vE+EZ@iDi10zu*ku*5*V@&+!8Q_C z=^FwAXW}tU?pHMmp{fBnBj;aa@+B~0nfUtM=1JZ75o`P8UyD*OT$Nrpp95{6X9w9v zh_J=@qY+I>uYX$=6-aFq8zL|G0Ml#4WVmk{T=WPLZJdmM;_2xIcKr71$G!jojq}mP zJ}sgj+#b@QWW1_@AgWk+93)B1w_K^JTPuo5^q%9&s_+tj77a}uk3~&)cp?`Or^8$? zw7vi%0@b|FaY!UE8$*x9B~b`{FiJ>qpcgtkZpc1|vpJcslAmGALoY+jmbL>c$hH%0 zn^tDDb@oZd_DfJt=&mxtW>!2`9o>UocS)h^Xx2-K8Wt{YlJA%vpd@+Xv? zZC~kQd)u)D>w8THwH0}VF|WJ}LgJGM7_D~}AytVAehmHJp7EOGJ;Oz>ZRAiosUEB9Q zZV^fr*Di*1I6GDB?6*u+Cq3gy z-|Xd1VOi&4o?q+i;L3H+%lWFttT+9G4;gI6*Q)A9ITfvvKyTS`g{E@Pe1VtT4?(%m zv54;&^ONVry8Yc4$Wk@b3WDuP&Dauo7w``X1X;Rh zM(u#T5Y=%QtmDGXic^IZGN1?KNSdZy)hd&o@<$NwG<^Mi!) z6+VzH-Nau#PNoby1tUtQ@yXNz{$ZfrL#`!*n=c;I8ct$&rm%x+WEQ3`XKm;zLTPUB zSuEH&EkmjK3yx0K2QC~+f7sYMNKFI<)ab4|RmIw&O0Boz8&lXEWiA=(8W7c3`Y4C? z$dCEYUYpTt15z}to3TUbW*$us38N`|ugph>Wp?D`UaZ-j#)l(fQ_Yg@`ApIO@!0~d zEy+Ustwx8O(4v3ctd!LEZz12%$j!#k^J$F%4>x*easRlup2zGrZR3DDXQ^0%JN25b zsuehh;|zhuB6VF%*}4ku*71ZyRD$uI`ffh|Xvq?eOwlh1^BG(vx)_RRg^v9eZEvrS z#71mdc1ezP0J1~l`k75`kd5F}RBhfgQ+MniE+%TqDQ2phO>Ucv4Te1&G5rHEg{L60 zb1Os4E-OAmrp?P;w7dQGS9_LoH~GTl<5n;-XaJSxbc?ALrlMi?^*X&5<}j zuPNH7L5`Hs(bLe&n;lSd*|Io;n+wxZe>6+d^*NVPwT*lt6*8kb`ZH24c@`;` z^qsr+*4$?lIZfDi4d^kkLZ#z~GG(5?lsg7;)o8AUlnUym*XTiy;*q0xwuZM|S)egA z{!m(;8}Ou@f1URMm!{fp0W)N8GQr2vsaVnb^~^80uMrs~ShV2# zS0}E;Yn19}0*nrh83B&Hy1o~SfEl|Y^WzW%2M#6us9nFGM4|Cy&NGjhR+8QBy&2fm z_XUiUvfJvBtNo%PX3XW)MrKQWFJN&>=3O(f>`*eBmy$78`&&{3!x*_y*U4kT=j`odL_rp3z-foL%0HG{$glSATq)QAd-g>g{=01dI36UCu z7{zj95v_@JifNycbfNjjcCqP#LtmnJdSQpuZ{rnBVg&7(OV=g7Q2v9z-oti`!H+yR zB(L-N?-G0jol>G2UDaXez|7oh#14Q4%jAoDg;Vai8k0f-FOnLg&KQw3bz1wl@`hEi zr8Yq7i%6(heJZh3i}gaEMttaSTtr(k@5?I{unfHWvWW^Z5m0&Ucq2uNSj_ovH@rCs zt_U#*9i)emB2zA{XOM+?4&cuq5#2kiXYg6W7I=$r8`3-k*^GzCu63R}7SG=OTr$Qu za;bRp_4Bm^pDwb-dci@DrcOBwIU6^^B1FqTNWUL7t~c@SMM$#?QgGgthMOFoYUrJOuMHn!dNHk?MNv~zCD8<|-sC%dgBLBrcff^2lVF;Q#3_6vQN3Ry-I%Br zgnSZ0icyL5MVtwzLwZus5ns7Cq8$YY8Xx0lEx9tx&?0V2NjF|gCl#HH8>6U^U6|5f z{L$a`fuamlJs=EoWJ?sLVCl5hNWu7hde-R}Y-k#6WDIkjXno{TI}3v8s{0&6yr1&_ ztV8~eGz}(LL1=3*e!1)r%Du~f`9D!$@_1d%RLc!&6ADm#egJ46E{P`{>SRLD-xgy$ z$z}(9P2S*r6S}uCuf$(7z^xxkxid6Hd94#Z{#{pWXz@!NF9k2>v1R3)5HA}myzK)g z`B;YDg{`I5vFo6fAT=L#7LKYFY33H|c4uIY#vC`$U;z%DY5-^%OHD&2?l_EL0{vFR zp|am`+S!5brwt=U)z*dzIWG444fIs`rnTJpy8v`}DwY|GB9M7(LMF(;YVCblzYuf$ zt~ary7g1cj{DH1)Qj`Do?E6g`YdH_mzT>K*drav$1*jfxY}yt5?AMEeANAR)H} zOvIX2_E@HwbKpoQIy9ijaX zS|&M&?OWMSfQcL!Uk80W1VwrGSN8;o%bk52A@OqvF{s@&aFCUzV})KE=0L>zFk1Pr zL`8hS;~?tg!Ny#YS1$Er8DOG3mufZaC-?JKVbno&FyGK8Brhh+$x4`s{mt_`@6ezDk8(wN^$RYVwXdCIicLpDV2N}j>gRG`<#233f< z-2)6C9*;zI`1r6GZwJBU>5uoj9dsNLu;>8$K{LA4QRC{TspAnNn{fS)6>c==k}!f2 z_kd+C-#hF`ywX|1afKSp+k!X2x95bV zBTrolS+SKB#HPEMUD*i^Zo|JYy(%6{32Vz5AIZ5zVb6lyfuFtSLLe6PP>vG+@N zOfNTB(ZtZQK{(c}z|{VQ)K&~PWEXl6NLtCQ6{LduWI%>vyi%Ms>awFT3Qy^qyV&@P z&n_yaf?^>g($!8CGXh6G2Sa`tawc9s|F4ZW&)v7k!*^a520qn8p|~kz#CbC0q6+&z ziV+w{^w#z2aaARmpBiB#kU1Iek($l+!`tD-@D&LCC)R8S`D&DOVx~uJ>PdY)m%YTrn3NT9|)BGORy}Z&I zSM-aCoqGE;s%1^r+SJ&%u40 z-YD zM9xqw@5+5kMS_lxv`9%s*!+q9O+~Sburmip|H(rwI@5x;xeVC<4MfRv=Yu1JXZ;xpsWf#g&HRB`+4?i1A)-oaWLdB z&)6{d6QDQu(5~NDu!_@9BrL&SqXdfni35U60rCC9WrMQtI-He=G0{H;T4wTO+!dbn z!Du>kqB?lp8z_>NN$mu(3q|xa`-8$fCA>=T(d|#8m%fT5DP&bnzXb~iMNCVN>(a3j zpvs&W$)vfDf1XxrBih;%t|Uq-$K`I_UgBexQVToo@Hk0kaXGzuh#Y(~{`*Z2JS3ZU zrBYPZ4dvW}*_@%o0MSr}v+65W4DEq_do!FUfDj_`p_E zP|(YwfaOgF=T8MiU!vUYbMX?wP4?YsXE4fVzDlp5wW#;s1hD4Rd4|W!;CyTqOVdfS z0<{sXHn<^iQ9TsrI`OvD=p!d^uIj(1sP?0i`12m&KH#wA?E^!@TZ_uwaJwXm`X2Nep?x#X26%bBzs4 zVgs~aM7fE`lfknDgIJU6U`qe>s^&4oc(J06Obg7mxL>Wg=ASwl$lOxe3yXabNAo0M04k0j zImB^iC796Wc+j>?)&_hrFpp%%?D|Xc)7d{4(}8)^@aO4!LBNa%(xt9wo8FWN3nwo& zY+QKM_WJykmVs5HiwQx|R}p#JwK!RY!Bs6o6bqHa)!$vmhkcl$mWbYY)i)VMV!uKo z$8l3|H;jztsfkCUS+NDobeE>F)W4PeRnY=aqorhXsI&3-N)CWtVD%fh{Jwc>#F|vz zFuHV>ra1HMO!vKpXJc5R34(i|1 zlt(|w2K5%4efjnfpWl_J^Cm^bDq6sFJ8PtS7WxGD9Q;BnoncZSO07@ecF;2tM@-0D z4NSjW+7*v45_}=mI**Y}*O;gKw;TZ>J9{?wB#}k}?4>Ljq`ZbI26qC&{h^5YVEjp| znk%bbHG_G1*Qu`nHRxt6wPIruR5Q(Nwyk#}pYtQ_P)m19jw;{f_6`T|0q=}6jy_@) zs{Iy^f=mijUrf8Z?qc`TFWoBI^7yFm#(KA|p}hGvbW?E@A$53Z*=+3o^qB6W+%f=tcNyd=Q}_$7f?5cjnr3!Cp6Tw~`o}87 zL|RmDQmw-gG2Uxlkq7L!%=lK;G~0%FAe_TYm|kD5=_jmf-k+EMK2RwB()8$qEFoeJ zN4^O=RZMiYGY(dKgFv}ktEyV+Y%SiTAmZKqL#1jV*{)F zR}IHDW3p*##&#rN9V+j&>ez-#{rLhrrXR$HU6X0Hw99uH7XpEwLH6AYdVXcZEK;Pj znf!o2b*BI~=-uri%s!MpqkYJma>XMIl^dZ8o=qLa1*epc^ECC)#I?Y)%ZFrsC$Ad4 zYP3%wM`#UuDfm2oAR_SIogDQ( zo~7O(4!#$+#Y8_$OdB3V(E|lmN*-$J!t&U=+O9AQC~E5bi&Er#k^1&nN_Ic{$T5pH z9>(4+(-2~bG(?b*K}=0uM(oLBiX8aD^k2?R{}@KW5%^+T?>h${JmT!&s&^D0ND2u$ zEa~T9q$#(h<2Z)A1If;rsHpzeudnNmJ`Q0h*zZ!dSaz!P_!&1L=Z_?3r-FXJr4?}E z)CfZ{B%(*?sdojh8SWWr?N)!s!q(5(xX3GqrVm_UWJ|=RH6Q-*-i?JmgzpD}P3%oM zuwnEawEq0)=9>xZq9c=#G3!l^hmA1F*wV26cxR?!0>6QPX=Ag(K`iHYQv0%gyXHOV zJs@PkH31~CeA)}BOLdU3_+{Ow3;AlWtQm#qQrXFXZes4X6#^e_mzT0raEgLhLwS8I zhmUHg1y$)TbgI-1sZtk=v+zx;X<9vxs92?bkjtrw`R(Gi>pf!=^nFSJa}cl3O~gV6yCx zg_0Rcx-yyK!92`(d>0Z9WT>9CcEZiT42cZ;pMJ>Vk@mc%cqa4^ABd2Te`W&l#N_P| z6{;n@l~#y3!Uqh_GTN@FaWZ5s|MR@ieTvsj;>994k0oyZ^D!$h5_^8A!#XflH;63r z7s{NqhV;^D7o)%pri;O>ulmaLf^c1AIfl>EThZN#ia8CPgE#^SA6JN2X7lLFO^|QV z7MHcrFa0`uqb$_*gmC-ENF3KL5ZwCx4mhwjyk(1`*`1J*F>z&&J(d3KS6ULtcim$N z7pGZX&Y{zCQ`EN4Lzh-$^4FzdJKmSbBb)#J$V?d|zEnpH+VZdQtYsetjlT)@!yNFr zSh4nz+xVctF(5&r#q&~8-4=NQ7GEAt`5Z;l3b9Xh87KC3L+5*O%t$oLGTE$;nE1-BUrBH7Gc1W z!n34n{EiZS1(6*OdY{m7c%!4ARbt~hqN6w-IrL#CIEV1K#WqKVQE#B(>e*+SYR-26UYO5hx zrWP%1JP;XktjD{w6_Ep-h>)}X4UbUvbEff`V2U3rFPZ6|`-(gX?^DfFX0?l#;Xa6p zZ_wqc5}jUHE?aLF5hOTNF{X5;99RPWlev#MEpb6zhrEQ1+>%$J5xIW%ovyhYf_IRS-+`RsmOoT%cUON zOTJCcr-%g~9}5jpJ5YsDfvONLeyNE{&}R?XTX8Du*XOR$ROON7ZB)!J+kKJ!nR?V- zW*>MT1MJArck>CHU2Ux>Wut~iIwE;{GzBGtVc;UmNafG9T1n;aSc@E(DR>6TD<$=Q;D`Q`@N`%Tq6^%7E;B+8VOo5m zgbPZ3>qUKM*(b4lHeHq-g3y(R%mS{* zXy9Q(7W`g0Mz%`ZrMvER`&vEY%bGu5Xb9ionzneOXRw;~`?EJ~0yx((&wRDn;8UtU z!^HZJn1u08yX99WBBImJ{y^Xf9>IXQ#2VSXRR#XXu⪙0PES-FemGH=wF0v+kS@F z&ukPKsd@Sni;%4&y%C)Wjmn&4Nofnrx0}G8POy{l8YkOA;+ii`T-`h;M^2`xB^}AZ z;Pu+%j=}$5>CnvLO}&UF`IJDA{xQjr>UXEfO7{Q{fG47T;<*o4FZ@qd5JSsIwLvmn ztBPuGam8|PG*MWohoNFzytyz_R%{4cELub#AF(_`ABcfyodbzZ1tw~3W!V*Vb{WT? z>++}ZnMS&_35$srTYdco1FOUIQ{B(&7RQ!7E=K3^{Q;D{ttH%j@^aokf!?UT*6pTWIXCJiiAUS56(<6&hAm zwG>*bACoTk?}tTvFs%P^Bv5#DYl1%C4m^q+RvTY#Uw4{4MFA)ZxiOAf1YNz!NOJR( zBzP0Ww`LhK%gd@_eMO!U-8UKnww{8D2j!aqv6_ml~7jc<5c37u$*~ zyM)u1pDhS|&<0av;tl&9E9m^gs!yup>g~R3;x%H&;1DB7u;%;?c4~9U-6QD-eKo*` z%`WejjCyTZEh4h}F=teF$-MVn7uLlZ5&*1lJ-7 z+awEf#q=6p?v{^Yz$CpB1WmB@A8oChC!Le5G`Z>{kkCPv?Z)#ru^s5LLztUHCI`UA8HZEZ>$V$J9w)VDEN#&+m$~l#<&+Q` z+n4n!n5#rNKoN-<|)jskIl2~)H zomq@(hfy1&8gYLXnT4E|In<8FKo+g_sV@vFYN#}ZSm!d=37rr&Xovs=-~Q7~Mjt1j zl4v)VZQ6O|fd|FicqAA&{}J%KsHJ~~OUD%bw$GDZp>T<3dZAWL=7Gg$syK8qcSgN=HQgO;ii5X=9h$K>3Q5?)ygtl zp^j?BG8ZcP+Q4phH8TtqZB4A~=VxU8E`9`l8XW3oT969Js&nUpOa47Xxdu3=hYPXi zv=1k6XXErNKzj?6abl)9h`xj{x%_zB{Met^<<2g?A~tO)K`;;AyYJ+nvDBz-)-x#4 zpFK*rS$;#Avdiq@w9N zrZGb0JymH8_-rtL-RCfuybynHNnxU2o=B~a!_0J6OSj)kL>Cm9aBU^_T8d*Vd}V6v z+;ugqEChrKxt69XwM_T(<<4Jq7gdFEKZ)R~GR(zhx1%P5L3))yI*su+B*r2}>_77< z#OyCh!wZ!ZEC~{&_;+0>H#%kpp>$m3V#O`a%g3v3-opRz`~s;VebOoE)VG_!)s>&r zac;vhUlf#vjp+(WMU)wZ(m0hoU?=-Ht=0H;Vd9dRHdtX*wWJz(#=q#)vfj%kg^!2K z(BCsQBj3VA{c_REYfHt-_pE98eatX2aN;(a>a<6p*3>^I&cwjd=M3|bwm5ZQzY{E# zVd5|kD@GULFueNn-9I*xw^p(pIF&>BXNQlIt~7WaB;z{`$tK_Fa;}CAdjm@~wD)uH zlztKCN7bW{xNTy$NOr$u^C6a*E0hxjM%GpH_F@ej#p4Q3B#fO;((@pP2WObws*?n{?w`r zy4KH0Hu9)Wxu&3)DA602Yb66yEN%2iQLy$=inIOE z;m=pE(Vh0&IeGEN&^vxk5rgdRCQ4i7O*(0jw{NPI6$So^oZnw;99SxZmu8=&j-GsG zD@khY(Bz2)YTC=d3%2?bRXpv2GMormX^Rn2xmscW$zLkf+fq>p{fK@PjqJ?4=1$=Z zsVZM&G}n~2#Vj7S2-ap(PeBZ8G>gD@1@-xCBhP!>AFr-cISHrW9{gt|yhbGAsFeVI~YHjWZ8^$glJNa1T#IPbvN@_X7v5aS*=qP9-D{@H*%t3LP`(kqG$-Ee$s_@5URm|c`PibtHflD&?PiKc zTk}i4n#x|!@JvfT5`(~(yc6pV`ci`Adc}u%X`aSbgN$sMzP-2HpLQ6K2V`}rNDRbz za0+r-oGNf6KdhvFq<^XAc3>KQBk(br=peXSPt%~DI)!)pqzpt5MOwXD8e%a*VQ@o| z;lFBD?kQwpI7iBkZXj(NoZm*{UR@ropcW^_8e z1~pzs7IVOYfjn3{p@yUjZHbTI2-8MCaLAWkM_SfzIQ&*BJ7tG5Gk13h$ZE=_;yhK! zn4ST}#BmH|FK_QuI^gC=HnJMoTR;H)w=yYUJ330)wQuN~T7fEJDukOJLNL`Q9(DgB zM%nzY2-IxEam^{3@(w7h+z5ujZ4`?Pu55uoenvpdcB^di_RDbmlY~-%tRS9&2#P6Y zFtS*xOXYsg3|pR6re)gG!M_tcjsOeaFxPp_=|I{8daRxTMjTV8Rv=KHv9?Z4j?vyO zWL8|v-r29ESYcDYsY`;j86rYDmKD(5z3loC|5Y-Jx^-aCT$AmdDCZnDN#Vg*0YKg0XnnfY1-v()P*GaSx z6jUQGn-y6JOGW%;(X_D6!*KGN1JLy6_y@7i_I6b1!--2L)^FPg=(vXqRF{bfRYZTo zAl~l5v&X*%f`+D(QxZ?}smK&2#d>!ife&d;dGAHxN1bcD@cU!CL8cmWKSfHRB5KbQ z(0to6RTPb=23Rm1qzU3!`qUJ@iO@L5pn)^K%zh)#$na9|{BKEnVRF0x-VIAkw3#<3 z`9Aa-;?6ZrD)~mX`G{_2$fUmDPg>(~fEfbPEqY_ovkX;s-fwzZ3OwfnXe9>EL(}xU ze`m&1-dhRmWXEe&?uF#SVKWM}JaLO=$=y@=?Ja%iA1{FIhh-z=*#w$c27A$f7uF+LGR zq7ulh3bIY+nrg`vz9X{0uuLG9NEp5%u&^lB#FM-kT5OVzt<@se%4RP+gEibvq;ASs zAIvS*s36a4bO2S3B#kQ)`Y^tq)}Ue`3HBGc)`!HRrAt90mr9Y}>4Lz0WM#2UOIidM zlYLk2-0l2#?o%z;vi{&?z2oB85;xThfuRnw`M*ipb2|pbVPgcz$!g^N3j`ME6@DgE znyAJ(AMXSlzn`k9knxD9;sj{01CcEhh#AYMNFkUyBQM@N2u4lg5xVzW&l@;}* z+|8)o*wq8H;v3sIf7;sz(vb9LjnfpY&y?V=A=KMAy&j+R(TQCLG098IGA{Y_)z1JQ zF{&$){5JoK{Rzc;t6^#jJEfR{X$&Qo+Ok&8*EBUcrh8(~%Kepphv@pX_JC9P>M~EZ zx2>`OhE~?)KWZ(p9tTh@zUN8pLwjSx_~SkqSKQgna`)4}=ok!bKeyq&1J&>=S&M>! z8iljrb%gLMxx?L1n)Re`oVU>m!_t9Y$tA)Vm6*Ay*FEEQ-aj}i@#?hPI3t{lb8^Pg z`+Ds~n3|T*$XYad-%;xNDGfDCJHG(G%d7yuuG?Iq&o4u^-Ggpj)XbSq)<70{7MBFB zSs(#xPr_1q+`ptSy@c5DkM;7&E8GkM#_vaV6(WMoKyx*8bTkIk^68aQBH3{=E`9(1 zQFPXiaQ|@}*KykEJatY@ZkRmX-As2(jbZY1cXu)X+}oZReg0pe$au&UG(svoGrxxFmFc_AJO}O87vub>)2r=^A6ze4HaS zVjPr*pyK~d3yem_42(tgpuCRWLh>W-Nq$`%94|^Jl4j6r(hi*NWJ}5v0C$pfcv4VC zNo|Tiu(a`?R|89aB+E=$DFeeC_betRWS|wkX1IAwP2+g9E_dK1=jK@?eE zupXv>D*j}K55+0yQQ^duM7i&Hgj1=y2ID-*p!h>P&}B6L?dwp#VB@AnnolQB8|MyL zo}7ys{7BgQte5jNsC%3V&z=Q%+C(8MQks<)Hd*0W1%Cl#5YF!Lr_{gxs^Ig`BnW4jf&y10B7BI$$-{xV&Tq2Dp3zUV*VLDhir4B^akO^|FCmJ;ANq73V z8S7|TOE!+pI4tfseCpMOebEDhj4m0La*X#JYqL(;$JthQ5^XEqxj0%3kh!d{%mL!);@5Fh~T}3TJR3d z4Sf=}5_`rU%abKLO^_9NMLy|yL3cTiS;e?L{HPrLPva1udx7aD)kJAb?Pp5z8D6#~ z^{U$do(RsnNi|jKPxM?bHD>=lTSFwGus+!2D(P^n?*T#*Dr`Ndv9XR(l4p|Jm|`;F zgW18g_M&fTTEbDJ**5dh#`v)onM)QiG92J6`xgkDnXof=@J+0&=)=xBk9u{Y%${Nz zD>MS)H=>4tbh4_YHwn~0D!bfxzWHkY%3^3U1Vbz8WoO0WN=>)^Ab+v!r`+K{Y2KN?Kk38P;5*vHkjo)IA3E8|)15|Ct#^itMzM|6xr{ zR`Mk8R=T`l`cBMj{CiAcZq;;^X-N^j$5(lIa zFHucFOt(3N@<1J5y7O44fAiA+W6*w0WS8&jI*-zESQ1N|-AKFtN4|K8bS5 zaR>lw8{X$WF!4s}GF$>-MsqVA#M}x=)HqYM98jHOXSwb%Cq67teo!!HxOf}5qXN{k znPHknV+=FDR^V`uozrJ=IKk)%ZASwzV2ldomx-B?8lmb8+(9xmD(fjEp74R392eQV zVeRr+g!qgzB=_yR`#IR8rA4*ftQE;xJO8s1D2~Gp;fBy(iJ zO@fO9ON}Bl&|jgbP*|9Cic>iNOy9;IT9RfscyR7PbFKj~N&`$wYbH_k2CIc7{Zvg>^&A5Um1^_fE(|xSg%mPKmNClz&7Qmjj^n< z(KXeoidvKux3^KZnkkeaQbVaH%!66h$yN-OrpPFBC<#A?v{fpkI~p{A4N6ZWxlnjE zodeylAK)_4+>OL#c{{#(q;uQQ0jFMu9sv!qTtDJ1JlC$$SLRD-TR2Efl|}c2fkz}? zx=$FjVziNyd&ddqW)5kcf6Tdp9+$+ZP^e|11jGG~#HZ_NwmO8!HGE7W(=*iiOyv!W zTZHZMGIW%Z-GbgU_&<_R%Rp57_JhB9BUUqnwfdvYExRI(qv}B(4dj~$MGf5z#+3*e zL!fi9PL}gtkvBXMSp(hTb)0NX362M19hc}<(01{@+S9l3!7U6NM-TM&pvWTas)Hg?0zg**{1s+uYrBP zb~s795p|=9q!0Aj>~5OcNSP}^2gP9yVSFO8j<5cIlX_mtxCLbR6LWlL7aytB_j50b zFlf`6K`mAwaQ{HEignd5$`FpO0D2!1uZ) zvQYi~*kFJ&F%3X*Xz-6V&{ChLm{YOIFr^GF#Dky+n1J}!ahCQteC*eb*b6o39nj`9Vhgu6C#^&4rKP#`|p=bwF$M+I9awXIA^ zv=eyMO=xM#H)Ot=j8YLS{EV~eBoq%m!YpZukNme7UC88M`HIU$`Fs9aDt&4alIP)mN2Z@(<8nx;hfg2((k!GQ{)_qJ# zROU13jsur}vV4Vl4QQH%7~Y0u4Q64oeo&&>cxn=j`aUGBX<{)tbgEn|&Q@U)4g#R$ zM6E`^UxhcE*GZ}k*~E!xPYg&M5VK5&e3=fAs_5sIoEp$xyrpI!JOFVBVaASEGxLx( z=7$KAD}KJ=BzytEGMXMtYq>O3(3DH6&P|%l@6IR+!#9umlTlDu%>}sX2rTkK7onmP zIGtk(T|)_LA~-ZVX+`Q}hQjc-ZZ7M>GlocgL%f)Dt~4@-aV-~7{r$O7bFebOS(H`v z>2}!AjrqTiX$dg%5qyBMQbi%*NdNaksR#Fv${=q%`42~CYqy(6ScI@v&FbEYXg!j=E=g*-;Cds73UqYZ>P zf;B7=t_H&4FXrOfi<_j#ayA@`@l0`XpVyQ}C(i%7X|2vf^jnj0b%xX`99uOCWeRyH zp5UgX@dv3!2L14=pcV136wT6mDdR_#?A;E(vj8){BbGTmGE!Yn!Gnu!Qmwmmm5SPy#`kvs3sKtq-eXbgBD;he`qbsk9tyU|`ei=)b^j z=nhF|SBTBBMOvXtpi#+NbXtM{zj7!VT?rrFBk~#ewjf|Z+n6Ix=Ln;JD7FHIOvW~Y zfAFHP6li4Eyx4qoQw)xiRlXae=&zoan#TJ%ow!^r3T2Fa|MU0HGZ+y6*fDir0s6a; zR*Qd9U1AqT{|3jqeGr+4_3Aeff}?>NUdwq@=j6moF6-!H3L5=V`8s-|X$V6TNG6ax z7WYg^{*P?(zB+*#& zIR){=eoJJ;ehz>5gCzo~W~Ad!t~Qt-AIBf@`54|!+`cPv6@D1Di+J|p(*1QjGge~r z;m&sYpRCVZCiZRVseZ z){A?}#J+(AGt1*AROI zK#NX~%-qz=J2$X1xaA#GlU7BNSYoFosg^q2xh6y7RHgCL-ny^?F^eM|x?G7MY9mRI zz6vqsqkA@xH=YuTM7s}2<#0FstmK2O?$b}(WM0&gg|-j5&}Hyr?1rz}`do`HzRyc?xx{tIw4;^f}l%vz}DpzqTY&q8)=onTx za4NZ(fH*F3rVJFBRUf&jzBb#Ra`6L_CPrN0nLFMA^{o&>e7><*hSi$*b zW)k%vy6N=i2>6X8#nn0HJPnFXI`v*2*nvYZw)gq*jJ{;Aoz5O-Ue}?cd`;vDpb%`= zyE3@B*<4Ujs{!InIj?XNZz&Fsg9G!?BQKmAb>R($Hu22t&m$$Wd4%oZ2y2gnB(g)b zHd(Qn&*-a;KPk6ldW?5 z8Qna$D*TgI(k;0`h3Tk2O6C)$2zx#_>Mt#|nQBevtUAC(+Y$Si@GO!f@r$VC&^Kve^{Q7N_!F}DN2i@+n6G^rs_jRL&BNUUFXjU(!y7fPDM;ue0syM zfn-FOdPw>>BpGRw{>Oj%PK>y??Ik%FBdzg1;80xl`G`T|T*o}7e=^zz67u!$Bb&rV z!(|xkzmnP5*s>%K<6=w=XQ+p{rZtOc>yrdcgV@LvM7<%t^;w*Hlj{RcN|Y8Fg!cR{ znO52Y=NVZLnH1nb1mDHMl~O7XJxp0MI4g7k@Go&mO-sOj_OwO>$7=eG@GA`OO{1+R zEMC~KQg`eQ6=;)r!1lZ|EU`t|81Vy;%7Fsx-mn#jYND>9Bj1IjyqG#p(!R#U) zBYq#J%lWE#o4%wYYKWGE{Ct-kO=^Q;SH9 zQm6ZQI`%adng;#5mOdw2#^do-Ad6_=DsQ_H>ZYK*f(jN23bljO z-~q-0bbW50Z18lYY3|d-hP>fcvi00w6O|FUqG7+NwXY{FFcel3Wb5#w{T?gJn8JA~ z#-C6mjWdjd zfqAex?bp1NFV+Uu#O^*R zEdNNPP$%3hsuifO!d5EqMm4;5IaCpK+>p{lV@NdFMoBM+U8 zqIsSQDsTq*CpstNTBJEMNf(F=ceemhzqM3jDHe8k)U+W%-&>1Lq68^+O*@5@WcCO? z8)vt~7Jem+LHLW|q66w7aEQ&Hn^a?lTA}*QHvfcNjmGh5vFdlLBC;6tRt2%@mmmtn zuW~UOCs5O({mpj_!jJ822F3Y^w9J3xLr~8cO2{An;z|4Lnl7ek**zy^ zG*A&8c~K7k6#d5)gFRhtOWZNK>$BXP3e9p#AMz~j-1++(C1$!L5a$f~zh0PB5aq#) zp{Y#l$gluoQp(%k5Zx7WkPC0A%iCr4Pt1(Uu@h`q8(7sf0SR>Axs= z=e3JV(TYqr*P?td(TDeJ{)jijxxLFzxxCAkX!y}BpTK9uwMg806iDMZgJ?of37!pT z#2atXK@Bx(cb6$tsvBL_vQ&&JS3B*ENFYWy%O1~{w=UvrX|U{Q@ZQkpknRe1EyZ^=nvsUYGi7Isg(dd? zzY=e9jfSyfaWSVI|A8DGyhAd4Tg=f}NJug;{@@~PiGNBQlYB+DrV=@py1u1lQFj@P zMq9!>N-qxuW{|3o%1JpmyX^o8XXMl-7?17rdN_K7Z-JgA9P`SzCOFf<XBn@de7llwMenVW$NVt`S&)ZMz6Ku@W z?OpuOg$o-&jY6Ju`z&W(Z(O5OB9sR|J0?P%f0T%puAAxDK3~l+Ktt_|u2Sz`M|Nj@ z^qqm?M#!Nl>v61VZy&$7x%PU&KNjtwQhjG<@yFbgk@uVldFSGZDUoGxbiN8RXjdmu zZ6t0VWR}x__)hQu8bUY~6dfaqY9&SI11vr9ReJBl|G{@$RU}DBT{Oo4NUcP8>PZfr z`9?;@7pqyByHrE?mA2Vq5TZ2jZRFSQs}P@3L5lH4k7jQ)-X(-{?_*n$2{P_gx$avG z|0>#j(VIo9M_55}-`2i!i>GlX%odZctmcpD|0w;lhl)j4tNRM((r zVqvf?ilrrNnUiOh+1n`it)7YdVA2M5$XR=qZ6fCB4)T3QD2GF=kMu;}MWJF>q3 zgtn-mY^j1g@wI0jGXVbnL$9@d24X##)bhvx#^NKn3+h8B4yyA6PiZoUw&{yY7rwa*Pn>Z%-2G7 zBaHSe%p$F$>OJM}B|Oofi0U-O!eqVeRE2T8PYOUVXVtLMTWRM<$x)9-VZ7{3bnt9$ zlvi4GeYiFFdm<<1k;VmCBvLV7fw8KH#4U~^lOyds*|7c~Aoc+Em5HXG7;X&s+>R3M z3N9vRTyJ%oszP`9-hDh@jXtoS>GX-czTr?3EcNvx^FU5W(iIm2Y~U*A zc1;WxG+9UJjAkCl=_z+2weOQQ=3foVz=4=OJJ=^$+pOYY>`H_vr0SLW zbx~{v+Ir{FZeHg)VPxhgIq-dYlxgX1W)~!K`PP^_@ohp%70C&SB<8w@j1LJ&2{Xct z_ch`ttlzC}5TZ%9pWReE{KtveuX%9(0^+HwGd{>ec_|FVw)BEue84N>$nlN#bJ~)Sp zwjP97rB}#~d%vH8qpZ!`R11y!mFMoH#G_bD0SWb-g3;J#|52kS$NYMvvc*w`cYF(o zppRe;4J+!y7&U*hUh@+ASAZP7QrJ{&ML+lgTBboLJ%D66$73*?mvpyE7&5@J1pEiMdt_J#?_#r*u&~WvYrD4Bk|T)veLPz20n>rfi_t5(`!4(|Y@6eEUTL zae6}TY)#I;-ujfgXxdyT=qR@uSvX8P^;1~6hY_H@gZwPBe8TuHC~0&&g~djbq5f9mfqJ3ma*9K&IM^hxB) zg+HtcdCf2gM|)LU57Xp2LzGaEp_ZK}M4FBnIsG3gCdefE;R1qR+C4rov0rMgib@E( z`uyPOP;@Fb`@Glm^u_>JBMpQVjjSBN^}zDp^HCxu6dhMbcFC4F{@=&#JJyF(dyhNz zB+~c)1egyB$;%@6WDJvk5^sbO#MuWsXl9+9$HjFip(-6Y6*@o*3HR!proM!KOz3{F(+l7se+;>& z|2wEOOM2>X6Igm^SibVf(40OMNhbKH4>h-4A@n>5j46s|l-J`cM8a`)5mWNpkymGu zgqp@yjp!<>#y~B7M&jaQ4b&9ZGKn7m>)xhvzzUa?C!~btl;!wv=lAFb5ugo8)oOob zS2Y||LZ2Uw0A>Y?&j_4!VC`UuJ0m1gfM3VhBVAOoVn=HO8cq0Hesjc(wi|GkP z9JPwGFCJ0)J{4J2b4tYb2)Zmo{vmz661`*D^~0BLgbCxVdkKGNL&Yz}$KRna!vsD^ zX6a@z1jikbQjJ_#buBV-5GBN<>Y%ulVloGNBzBgxaN6RA4{j9ks3?gaNM;v5`Gquf2W5iK%>;Ib}H+?sOo03x6g9hvHn7{ z8?xMZG7UKG`Nx0Q;4Xq$AgreMdCxMMx*gbj2bLBBBywgI=pXH(N&lgK$;Q`sL8{$c z!n|4tRh>_>=q^4kcTa91c0O-Spw8j&)ENl zRsJTRJ*uh$2BHeXry?=8+eeEB7R;32CX$y7K^Guw(p(Up8FK%0eSyaN$cDRy%?7F+ zo?ffWjHeTHn^FEvG^C@!G4?NF-!K?(WX>LGJ?5r1(g)MrYU4EDfS5RedG99h$N>A` znCL*M?E8BMe^Y-obz~im`L?%PVL&M}3qNCAGq$boWUA(RMvr-?Og1Y&ZxOMS66U{o zRJ{Qw(rPBBdMEbp6?F7gY)NOhBV(b(#e31q7`~M41P?Ik9WfHhed3Bpw#EXu)g3!Y z*21||5iUqjfr16dROvBzfwKjAj$ODe3dIMUeE)yl&fABk^qBuHGwl7?Yw3m^=BP8o z+EfZnJSJ%{S%*$y-*RpPjgdkJy?v_7OYp*qI9!^Vr%iEHj;J_qGfUJ+O4C5hD@lnd zSemmjCA0%>jAv;HWv^Uhz{xUH8~WJv*r~E`H2>?z^#bY?yUUwMcLTjI$@J+1=oXh! zhkSP(FgT0UHwk>I>7YnEYf!3!$_IX6+~}$k!AaF`gjS=&PjI^yAtJJ#F5+=o)wd_)xiqAT^*IVsqwr12ga~yBeU{-@_o^AQd6E`cDDBmlyfKS^*kh4UCS}_d zL9vSu@j_}vIsAdsV?|%z#c^C8mSKm+Efly9NJ(bffpZqhvm~Y%V^i5vsn8Q1#&(dmz=}Dj$Xlh(YT{RLy8P$NCBOOht*`z%9&?3!}2i} zer`(XunGeQ+GgUgSA~Zhk3nSlZow|17TB5Yn0C?09c#&$JYP=`-bT`pD*yDoE$R@^GB<02w)+CbF?>xd= zU$(#BIE>E5ne~%a5dvar$~r&lBcz2VF~g)%okm)n{AfQC&||x=0t&!a9fQ_>>I^YX z2-#e2Tm9y4tshgpi%M=W=_f#@whFF`#n@GuDrYnN(~;yG!dW5a&2Nj5)0x$EGM1Hi z>mvjBBAi*Dk}if^EMP!A!pMoT?2s%G2pg8ahQD*$P(4;mauYwoP-Lc&VOs5FXMx;H zb(mn>Jo^i&$fLjPkhOcf1bCZGR@@zcO%ac+kHJ@_@2Y6t2B=)X(DN3&08+@KkrEy{ zaR9Q&Rt@X!&a6Cuev+&BRWT%4EvndaBmgkTpA?@q&20IeNW$ANI~sc+{rwpG@254@ zV~e_v88gphSw;!gVwL;$FWoXJ#;Q*6RMRe53(C)b@S=z>bcMcfa9%e#Og2zao`v$ zC?}lONttcl#|}h%4>kWXd$1?eGI4YeDz6G}Rr^6sU8qC#f*Q+t#s&OVlbR8q;v;F^ zWYNvrP*oOu`<3i+1}AzEjt(7ZF4dYyros$29IG1_avxj!fR^fAnePqoPfu(-R>Y}u z8ACBTc@GnBTs|v@3)Kqrmc6hB*gSZ-p_UMRQud2gMH7Op_gatwF-S0<2$X`cJpR6L zTKDD}lr!Uh>K&K0$6g{t^CSLb<5ek%moy4@(Ik!_p$>}AG7$`!wixHqAf;2ZJW=s8 zQhg)ZUbIk0EUptBT<2z+-u%g*x3X|(G_Bi%!Mm!<(OtMpF}SEp7;_c}6_-2LSIgxf zM`?1PG38@S8;b4rLOexb#}xWrrYk9)Jl?c6~Fi9bZ=lm8C9h~_WyyPrs){r?)_Oh zN~G`u*3G<*;SH9QZo_oc%}}t5EVc$_dUi(qe1~ zr-GY!J$gW{J^_#@6<8K7nCupbiZ29mK33{XKGAo$b%1Fj-VzA^hTVt==Z&bCViXXL zswAQDu*A-O1%B2AI6ZVL#;Z>pM+Gd8tT}y7&%(SY*Eg8VjzZ&U+CnOrUEd~seNiBo zhVki!NERwGI*f#$uYTAOa=wej7;yx7)2M(8S14xxbWL=+( zsKzK6yzq+Wny?r5O6tp}ykp9W&xYfRb&c<2Es6&btj|(%BmJp`^riDr|y^0lLk9z-&N#phP64W-(re45wyjxj|8!lpt zZ$ZE;TiRPyIT(z&cVX(vX+x8!I3a~%C5&JMby=8@1`}C{<_D#awDww?lga-L)6}BM zuj-L=p+0Wtws{RZo9mNryKf391hZ6UhZ(0CxW;VWZYMK^k~7O@lO^FLpOjk`EC%eu zf$F#$HeYSpNJ~}O0#qbR70XHKJA-eqoRD}msGa`T5C&lVvajc2BboRxBofPOpN91A z#uA=pM+D290Q_|rhGwVx|^Za4=Mcnm1q@seXNtt zQgJb(YkEV4a0Up735lzg=OkDq$saO*BdeoCd0n9ph)*1~6(K2$Yh5##pCRKc)$ya^ zoCPjQRA>W0eHC41%Pvub5Xk9ipE6ITDu-iCXoy7eYFDXu0<1JpNG;1!2Di>id3?6L?i|5WaOOtQvc+Q|vm z#uu$J(hVRmZUz(aw?vUq7T&}mR+LYclPJ^3$CehuvuOSO{>+z1gW=dy-$0Pof?M-{ z2HZNe;)SdFtTl$^9{fH@i@p-yPR0L($F7_4rTK{RMWNk2!wQ#B0MH99d) zoI8R`g258Lfh8Z@`zCs_N9c2&_fTiW9c?k1`w4r`ufp`|KAPfbtoUHuI{8krvGnDW ztk-EvEVzShADk5L`#WLcTRg?}Uob?gVplbvBf!1PcE!Ohg3Nl+KD51eRq{dA8$B{7 zs#6OPT#yQesF+WlD0VK3DAG)Nbg%~s^g_@!LU*7n8TuX~Q>U4bae6W~hGv6^b6}z5 zkgsqg9pF0Jr_CkA8lJ4(k;* z4(=-VNGxyY0fjnK)cBh+mJf&-kVjX1HzAW}hSR*XMfLQ{Fa4^$*cKc;59BoL8V+8* zEG+nC1EUe|D9ti7nd5LU{K zpoD#?SIhBwhrv^BrC?vdjB7HAC~ctdKJ1$q&O<%ASA~p`8qfagk{0Q8v8YHY7(lU0 zEq?v#nAJe+kG-|jBal2Yda57J@vnCq%TGRutH)#hhGhg_#?A)T zm)7g_Usap$YLs2)kyOWvqzqd{#jaBgvPBU>VTO&w9Ie`5!&o({ zF{q&?POR*3T6%z>vRl6N?gk>x_rK0P{I`W%!!5&o_8*xKh(3bIv!s{(de>XCNq&Ce z&0R_3-QcF#aK+d-*SM@!88xQhP;0NKPiZ8vm^ds9KpF_mT%?8p62)VHu+&R2+R;v$ zWxPv(YzvFMbd4{Z!whU&N}RUGAi8hrs?2ruU! zc)@Mkj-R=z-#ztX$@V5^xc7dOwkU>gmejDZO84*GYlTI;51AQ#_%WO-~&b(&`+ng3|R-1t$Mep0LfT6 zYaEy9=W+ETS|#5^B+mUR5vZ$g*^{U0)JE`LvEpP3_NdJ)@8m7v!BV~LA=R>rC?+Y2 zIitf;yQR4dwY)1lsMx*Nwth5i<}gbnmd04Ts44xPxEh#8Z?Q$eU$NsaR^1ltMJ7w; z&4~L7r^ilm2g-avWQtx7PM~-7ZX?n6EHhpC*XmlZycHnq-F?b2b;RT_zzx4suR)%C z_0|IY>N?73?%onLWHb>WYC1yjCAczQU(`AmefR;$yro5)6loqThQFHhF$mB&jzsl- zi;4Hdr~6E})q{9pWYv5P`#DY+&A>XmGQH9F><<0cU*LR7N$$$Jm?J@toh)ne6U=c| zJruD`;O^s@1{k;#*5AMPr<{ZxF$Q>W6DQpEdH(}m zN?aGsh@`uQDAS=WDOQ#p^Hd$tS5ecTQ6R7$Zq*bZ`F*)lJLkj24F@Do;4LFyX_HH? zBh!8Za}(*;ckVOda^PE1&HLg%&x(KV2Cg9SPkN2#On7a=OOUoSB4e&HeQwi) z+Su_eK%HR$$k>9tqRq!t6@*64-Tl%{c3Iv zmt8ZO^SLk~rQ_~L(a<9tGo712EL7QN<`JS?YIa)qUQnn0Gzr$JbwB>%NZP|`U^<7~ z;mRinxGtJaZp{k?cgN|05q1-lMrGsZQ+|cVYl^%0|1fwl56xjVLY_Y015Y(?IJSLT ziY(WWpHz7qPHZoOfB#SdOJRs#?fnrIjC}02(K#%Q}f*m#DGIP)K`Hf!QHl% zS&a$8F*V8z%Ew&ioRo1}OHh0fqU65_v9dOY1*AYx79m6Uo?T zuvW8NlMB&IFcL>AnkER6D&&6S{TpI3%p~t?0ec-VRiUcJABk(ztBD{kTN5bL_giC= z)AOgF)h#4a%6ZXimYjRFBqT#2+yK1oi)NB|qdDI@v1%o2lwc}sYjl^{H{D|YAL=p@ z%K)zM>-Z1z9mn;zkB&+HWiOrdOq`-J%)(MxdRlq)Ea^D*LvP4g94K|tBVNr-NYHCJ^Zoa1dL5wr-xc{!3!yc0BW9m|^3fIf09U_+WK{zMNX?2g&| z;ZdXd;AH8@chnBvBEJ6H?_MYa*A`9E7fs&<4{cP-Mf27v&f8iPi2{VvAZr;P@cNd> zfBhJE7$BZsd@MUd>^olCMWMjz*E>C7B~AU;T;aNA7n;8zI5!&uBv)QFa|)+-{Ms~W zmr!>?LdVwTuJ(O1A{0*Ec8!#><-5zF{X#_=UV>t^Z%Akz1T(dRoiUUAE5Rth)Lvbz z5988T387lVl}I6V8b8~h19YGM5Rq6R&Vpz7JyOmMn^_cR=V&Mfo}aPk@3I+bMg>;I zSW?d4wK6{O+uN>O)rmY8kU@{1hIZ(QCVAPW6I)xXY-1?pP*OFsLy35WeJWeBVkAo) zUA1k2U$!2h`y(r*``wvFlQnm@Gei(Mn1%y~cEf^)p((}2xe>@nNJ3?W40*#BrX7aP ztd=nRz!a`A^1HdH;r}v~`gvdUbooiKb*g0M0`_rjDKH3Zs@Z~#Y|cVx`V2Y2d-f}9x= zyx8TlBxRhfz+0-N_}z**!l)BNyYQ=G8dc$_CCt>EB%BelYafPyINfGd$ej-3jb@RX zMqAOc*spC>>n4rhguuS}YaHmfj&o!Mhr$G zoz9A07*a~T3#`gT-v&ANvGNR0|HN&Su_GZXrcS!Rb(_gpc=-G}KQiY6B9-)0j@pGJ zrP5eeC70=tN~`&av1LXI=byUL??FV4ScvBWYN3h%eO`Vj{fkiG(%L6)0yfNkG^Mvu*SZrdbvdf}HG;Dfing8S#Z;RP!9HzsWUE4*=$rZY}A_b;VA?NLGLhI_A z#@Ja(7pS?O{U96@7Cxdwta;|IJJf~`4sJCVT4+V-Ph!|i>bF1a#avstY9*bgXySV^ z%7`h(?mPLtIEmwwz5{RlLZm?}Km7Pqcn6#@FG6uvng}N2tTVNj?u0>7jBtMP8Zs3A zAR_{ESB2(@D%>Ely$-~YnuNJwtTV(}9oz6?S_qbXf$~h*G_*#RhhXbbh`!ON=u0j0 zsh`5~z2Ay|=@%=4;;Sh;#97f;!32xP`4DBBjXhqLbU&&26Pr_2pmP{HiQFBdO=E^1 z`!{}fR3*pY6~*86tZe{YR{vHTL2387IGd|GASzlTkAi)BrSPvVC&y-=GWog;omU*1 z4{yoVHP>?KBx4p`(AsK+0KOqM_In?B>%Jl)U!~tUVyWp=I(tOP)i6(^#VZ|axrxHg z=^_oK!$KOWyvQw}f{}&M%gjXI{gg?1m92*2uyZ)$eEN=F)(}AAai$=6khR|*nXwtx zN}}i0@2HB%GCz87Rp6|Y-;+>a@a^NIE4bh95XF_}{pQ8qjO0OHQfcKlNFItxR8+O( zPbKqAK!Q86yyx&qM^p#C&f3%|w$}l@{>74qb$~_pZl5*-Z)7Ts)b{ORW(&*831CYM zf5H-is^Zi6d9AN(H=DBOFeh!k{&pY1Oa8*dqq-t3>*Si9A9&C@Jr=~8dwBR=f7u)Y zoV7rMG=2VB=285B@i{w`(-f%Fjp*kKp^1RVY4+*+22KR#me-2j zPgzSULia_Pa2Pjzzt}5csA=JMP}`Y&4P-rO46;}6 zz*M3n0llA3S+jL7TQ0S`v!daybE1iZ;3_tbDS2@aa`7&Mk#J63U2W3cGf$|-x0c^* zm3hQkh1-9}_Y`tcm`-EcY_}SZzeKb-y0EGUCy~xRA!y&R1rBYX3VR-hGkdH)Ir9*s zoDhyZ4qXCaR-HXdvGE9Lt8j{wzMkI{+ruB)uKiRTL`D(g*zWjHF$WT+s;(N!gDsSN z%jp#vvWiuG)gHZQlN74)qm4^SQzIc9%Si*sQ5MdrahVtO!H0w!gslxJ;sGqET_fudn^yF%tpO_dW=TyDiWvGua~zA|R1Tc?-Z`Az+XHy_3oD{74{vXD%6@ zCFZ@xS)T5724NxrUicQh*J_}k4r@7ox^mZFWyx(v)<2!K7LmALKxB5)@~I)JolSSK zu{H%o$HXEs_F!j+jg&Ll{CtJzNPRX$X`HZTC@XAhinB9LjdjyrhA|nUZA%NlHLFC^&BQ2lKgb=J{9=-C;)WM=Zu}C|%^}54 zDemu6LfW;tEUN#J>;W#r@v&(m@R3DpNOq+(kLfvnU-{5%7W}`0HnGR1jfHQAy_-I0 zBsG@?fk;NzmhK`rBK5$(sf8r|YJRaQOS}8Iu4-rsDRs<$ZCO3?NLJ5!Lr{B65G`mg zrM`c)>q`NI8A6{G`IJqQu$yC^bEnH%mkny!Yr$Q}?^r0oq%P(ifRG}d+1iLi7gaPP z#!ViF#G}49iKWY1hUY^zCxHTyK3+XpP1> z^}1c8mk{5=_%a63lgg1UF;QgXCsL<_WOlO_{+RPcw7?yr*8^MYLEbZGq}#E=Qn?)d5NypQAZb$zOk5AJ;LMn=p$(3RFuy6MGQ{ zreKEov+9V%_NqnGni#=*B5QZlF(-6Z`ZJ!h~XB0vek#$OEjzO5)8 zfty878s34RA2&sODM9VrJ_KowT48zDg}g%54M^>3dAcUFEvyU&LirJPtPZ(tU^Lj1 z(vX+lWTPc9~7BvSJFhj16MX7^BQAMc4Ktp5x4Q53tkB0e&?yd~}2B^%pB+7=COtYh> zGiQA1ES9aj(HQwGb)*OUvEnF;Ig$I>ngj-F+8G zDUr4SHH6{r@Wk6h5SdP8M&_iKy3N26Qi0W;G@ZYQ_QdwhOB0!@$CvSc1@?OcyS7hH z1xTmWDK~LR7gFHp8QI9^FJVdQk98!99*B}G7A-p3I^{>4H5NoU@O#E*{^n_=>uRz= z8M+x6fM9YqIStZthv76~Ats+G*v12)P-WRRS8g*F3wM9|e=tOJo&52Y;o~@iSID<6 z{Ne{%XOS=B+W)hB1=6cSjN9_I3yy>uSyS{TCuL9r8jX)6J!43+Y(SNFo^!S)Gzts* zL^ZTLxukidGK#ic-dmMEPl{EhT#q zQ+Q-1fW|21L|u@4!~2xR%|qlvuLM8Qxq?Qbmep(LRZtkeD2}vBYVJ^V@hm~EV^f-S zNZ}*f2qvqW%YabQ=-jA9B({lLTU*uGu#o3oG2Y_Bg2)O+tcQ$J`cfOA-`<}W8oG)= zW%~p81EI_A;Zu$XwbU!ZsEL34zo0kqRNO_AyjDYDgXDzPd;_@r{?+IYYQD?$nS-TA z{MC{U<)rO7O$ZeBS)Hy=f9FdN=s4@o2lkqPk0b>lq|u}-v4r({hxT)?H7IbkZ~_!k zwSGyDCfO_Jk>H?((x68*{iUw0$j7wt>P5yxBkxEwaV0H$H1#*0F@v)Z9YkD9hR7-( zRLl7n^5aJZZTrukF_vT2=j9nA>ymp~4ypmkxSAMwRuD#nvxapPWVQSEoijeE?x5F} zt64urw2)NGJS*&xeHHUglyeGGi(LTXiBqBMcwEc`{WY*~;9f!D8Wmo`0m5^FX9}j@ z-dGsSl_U^7mo!NCN3t-gCdYUMq~njjBQq1E>3hZ?UGlbnDkP1DhNl!sZ%-+t-Yxr_ z@z6`^eyhc{p%C-?u(|vSq2R;c57jqtqux!n*yllR-VQSQ+hx!(V>kb^pG1lwxHnrv z(V{&yhU^VL2PKX(*7(a7=*X;dp!1;1ukp^wFxqj5xeBe|$4lGXCuJaYY@FR8v5?+j z*#C0zdo8(ZDtU<0P^-Lt)bej>w!0T4hl>52K>)`KoKH;L$bFD>xGLm;Uc~#4$&Gl0 zTDAFL9r%rV$WyO|x$Cw^YiGZCdbv4m#aaaQdx|9dzVr&n9}@_>ijbb#&G-(f8oaGb zs=e}LohDX43?fHdyu~_#xg&N;dMjd+(2m)?GbxF%j_-&Y_C?pj?5AU192AS;C;iXp zR}wqHipm>C8~UFKIiE)Z_$7NoKy8)k|%pEgn*TRB`-&L27*_V|X& zvhq;+g8rkZlTqins~1aFfV0ssBnk=eWGzw~a5{aoM)*#bw)C zwzZbsvhQr$wq47sRo{%&vfcCP_w;|g>h8L(Q^$F{b%Mrp2{mp_UXWy_FLVKwf^Jw- zy3O3@-B^jw6#aB})p_n&Aw*D(e5=Zza z7_)mgeCwN-Je6BsC%rHSZ6c$sm(%2mqLrQDV!f7nA?};pM z(T8fJBdcsuGWG&f0(a-HZ%&5&bxY+-UOS+Xt<7^lg9IKxH_hJ%Jn0DkfaRBT#+9c?hdTwx zc1rs@;iA8&p-AX|nGvFvAYHkCA9D)}tLTC!%s!hj<1+KklAkV4^T zB$>1Be?P!4=#l>hP*%P@0PQIRFzxvoLctna!PTqn^jA)~L5EHUa;K%?t4~~(*Z{MS zg&0Mn;*KTs{<3M+cHppUByxlV;`_gO-d(9^+yt4olvRX7{|^g!18kn_a=1M_km>cB zzp=lnXMHCeQYdd)GJ;4JdY49ZClp&=nWdSPqw)nTx?%C0=hWh|&G>r`4%TX=@=U)w0OEvR>)Lzr7=$;rcRxJR(ecS%rKvtO z#&W30lynJ-X+SRRz7EWK5%4Ds-f!OyZSR|uJX@c2qM3Z!cs!Zdl~o~l=fXAk0`rTM zGXC7Ykl#cuEQ`PdLL;V`gi{$)Q}nVVJp&0uT;KRk{ZGAWDOwB*E;z(t^X1vDXWeVZ z0_9~_fRNp)T%nFpx;OQ(4e+@d1Zd(WbC}EC16~!HnrqcIl0h6y+^`A(n|-%l2}}Dq zVdrQ<5bbNL_w}}sh61F0)nG)$cvTI#moIt#I$6qY(PB#U-ZYWUmZXdm28C5s8Dbnm zn;bK8c;pXDUg}mNo*3+C%jK37mJbYbBw+4(0%-}6VodCaeilc-9r}m$ciLI`KAkN# z@6GS9{S3RdIhhwcFIbg^-4A&#g~5-=8-hk(6DFCCI!vqrF3`$QvpCGqG1o*!XR!dC zPpr#|$Ae@np-w!M&1Uc>-^<0ISS;xV?{Vj~Z(I+7`SeO!^P!mldfVsL3Ew3zU%xOs zLdTIHX_?+ufSbM1FixE*pnEJ}&h)S0KSOLzpKty(R_73w#!qB3tc@LfqDG;08srXc z@*-f(#z%6s6hdGa(M-j?AR_GXL*#HVnnrph(&$R6F+@BQdGK5?gvIxgG*9-BIZD1RDoXm=$FyUE$vq=dVyZ^8$ z$VKptHgp-MTK0N-JhYrYX2r@5o)4PbX=dp7E$BJ@T1-dD7l1qvs*iFOex2M|%^QsG z8jsaRBb()Pwkg%2*OivLc#}>>F3=90{xp$B%TWLf1bpJ^rEIV|35T~oXC=L;?No0G zmD@^)c5Rkd5GS1Ipu^@y!XVr$_!xIXD?=T|3=V+gXjC8URgGA(Ja!@+Q(SXY0iU)0 zahlwhVHuiiPEn%9BJi>WE#ot!0QRMVxD}60@VL#e5~kf@lPaSddI>pHF|ir-;V$34 z*^`*n6INAe!Ln;3)+&@Wo!C77hZSvqStt!%Scv-M~ z+54B^{rbG8T|2vi=(rXD#?2(1fpO>$nv)C{tc0kUsVS;^*|Ri)kZ5dJ4ZL=8(McFh zh9e4srQA?r#n!Zv*6xLuP>NQTGG-GBpSLh!N9)ilj^l)&N?4FW;%wp}-OR@_?->FbyfDJ}+j!rqiu=;^Xp+6yg` zw9pv}tca!^<}5+6Mg^_`?||&&ciB*`fSN7t@WJEpfxTe?6IOI3@qAtcv>@S*1>$hF zr+r>HN-BF;j0t5pM3UFIAQ+v3fQAXyYyK%=@DT6;CGi7t-GOuY3^Ye`$gAQD%1ccc zf#dYQQ*ZIkvI)Ol_uHIziC2L^J3*2yrSw;A`L{>>fZ3Nv_DWIs@i{3HqKRhrP{Gq8 z5A^(ULt3cJN%Qj=n!m&oc$LOUx}kz^DWr#1uBJA#RT#Wlkbk|>@H z-Op!XvUuvV$BU07U zMJafitPaJES2?Sl2S1y{$Nze@j@(AyXhnY)GYu2tHl; zmo{!)&*2x^g5Di}_Mc87`y`rk;(d>Uao*y(ktu7qtNIzpVVaWn!iNnr-O0T=_^&kOr7_v5vp1LP(k`mNz1u(1 zU_P7Z-#ZD)>lb2l=z2dq_xBI21PQm!i)dcc)739!@EYTqicgw) zcGoit=ckOrYHZ3O7QqOBoXGAlygXCv*np-R2S#e=?_CVi$}Y_nE5ga67f?toI9&a` zcAH1!mSo67X2U8~F_>SaXSd=zJ9_Ij2_zP}%#a{=a<|mBwuA+n*AasgS>sHW=<&8_ zr7PuEvnj1>i6o05+VJG2>7|^Q;~C?2*aO!BmJ2p;c1v`86pBrNK7O@y@}+54qBr#s z9t$BE)E8phQc8`?D+ZkE-*U>Tv`QoX6oCDD8LSuCK^WH?O%31-t(4<=kp5n|mz7Lf zg_^|Wyfah_A|pwhe#`wp;ZSk8`ymp+uOn-qvIpUstil2wx>BAVnwYP<^ev}=Ji5Kz*?7p9)ZZ}h_i(K0W zeDC%yrHjTAE#RIrJ{+#bfr)hH6z76`I<)1BzqH4n*>ONI8Vji?$YWLqcGkHMY&4!q zV;cuLS{g2f{C7r!B4p4jRD_txaEcLt2S4-8b)E5G#jzzBxUMv3Gbs*gKI5$qelB@c z!cEHMc1epyceaAMrC!{wRx~{@0@WPX-yZA4UwabG5SC`a@a-y9JW68lju>1Z3(&-B z#UtytRz)=($NRrWPW6YnYG`|CMCrHAD~OE_#S9u*xDI*At=GN01HDS*4XiGXVLsZs zj<4Pl(8nci3F8GNh(q6h=;7iV5v&ml~0YG5l{RcPkT=AGxa0*wv+&0 zM4BE;!0w6@)=_s|N}f`nls4iQW-aSEu!x+OwSQczYaDK-^btzLLwv{^+3ob{-^%DF z=!xGwpnt4dmx@dCS{H?jDW>SVj3Xl^q23{~qxq*-s?s;hT%|8wAqa5+?lUT)2#9i- z2Rs#|dB_XVs)LlbzZoX^slUo}Sj6!_&UZbmU_GzBDIp-EJ;wK@1z8ge4EUMupfO4LlCw7_kuTEu6>!HG>M`02SLcrC{(G?L#*()Eu_kk%E(ry zB@Hd4uAj9z#_PDgkr6@VCa()sNSr5p?o3%w0vyEsMUW2T_q?tFmm`vG(5x7zis`#e z=fCxK1QAL9w4*?3$PloX|9CEd8Ve2~znX!W&$7(%e7goX3SfDcUx~6AItB=4X%376Y%$_erq^oyjl^7TFN$2K&8W%H1;hLfqz2XxBLm6uTq@~t z{V@a#s+IK$O>CXVM*cLX$59;H1>#o z$qh&JQii%m6MTSaclQQM=6%6yF4d9w*X&P5468}qr6yiI%C)cnlk}QP$kftf0$D`I z5F%;eBpTAtorHFe`8xP-)J8H$a7|Ynsa0hK>X9;xoymP5_T{Ru z$k2XUusu06ZC#N5MgZZbuGVP0cxrb+Fx(hFH(sJ}{Vu=e(sVVV>?|C4M&j1JqSv9k zngwiL-u)7+4G*%a5#W&{=y9ABDCvhfz>@#(wKPLdPG6r(#{Xfp*+()2i2{=l-Cf-; zao{mcD8d{n7Q7bJ3XBpBJ3CR&0Q9@)$4mk13s;rB^|Fn4Fc%PFzwZD2F9;Xv39xJkp-xBOYt$assql(R!Hx`Q?T9}E+bh|^g}!<%s)|)N2g?rT-BR0LKeR=hwz@C@_o7>8mZ%+3KIEh5w;eA+#~;UNCh7ba&! zqV<}kJ!kXkKGEz5>0cLw*{6>cnr7rUc~I|FVZ>fm8tnldmLjCZtLTnrzzCl?8j5CkJ+plj|-(#~@jP z;B$JlJExVlh_KM;j*LC^<1g}fp_Sl7oSjGwwu(t7x2fSc_j`6+wHkq~L{Q5_iFkur zCcyw!1BW%bmI_Ng5#uy%r8tWZNM#fCD7k7?=J^+^PHuW1(LmU2%8CAh71>I<>YpO( z{%_>P#~F{8kOc)h5IPvzP(}asMg3-Pp~pHW8^!o_fEI&1qOjWfX8JB_hz8o9@`ckE z;9k~ujj`WEDlH+M^9t4VZw5f6>-hxl^<80l1$C>A6-a5)$DlJi*J90olEu4t>Zpgp z`guImP$lsEn|Xc8k#?w`#&L1<#qtwO$KD*$GWH#2)FYG$;MFi)FhZ@%Y5!xhjF*w) zH>Zl*?HRcg&Q(wRD!Z9#s@QWq5P624&hc z$b`S{2-XrlFdhm3L?_t;pe(!}NUmca9-;CypSI6N8%!^b#B#%0}N z&!5f&$|?Qe3NGvOj*1PyxXGwsYZI3)-IH_WnhFuVfwC_tIB%RW)t%8N4)qgeDA$+% z%&t-1-DC=Gjt3+VjJ5pw1SurSrCha-JaP>cixu1)XtiNzQx9nO_ODwZTyhrl@gQ~)bry~79FRC3Wp(pREArM zu+0JN9T*5-W~*34JQu{Ug1Uo8g(_RMA91lUpT_R-@K`cPSRyLY^Vrl|N6NwmCf17B zCv*4!q7VyaB%T|vDT-qM?i0yZixQvkK%gw$j)15J7220qk1s~aSI8+%+Hy^)@`(^> z+${KO+wE^Xv1&5I94FEqp7M2$AQ7j#{8}(Kjt88 z+z*5$V>ae?7%)^6l_{OQ@ve9LwcUylRV;j5M6y1`BZI$#yp9#xoe-zFh9d{+#~K8QktT(t;|mh>^Zho@BS>MrEEFa08(Bk&rP+~EqW>0 zOZhx`Oij&dAB@e>&kplyJMy7`j|G$t4+nKrD0(PnR16OGLbm%V-9zXaIz=}@Htm1atp*l^3WO+PCrCPv zVDxtHzl@31j>^%D(w?yU8n(1^49(#9u4p zj}RfLU}e+2wQT=0GU)!Z_A^ zI#rx7_BD4i2~(lLwmyw&OotZ2Wol}wYDgdAG@1O^Ho5IUs0<_(iSvb-7O9Q=3^tCG zDz?4X0{kU5v>n1yqI`GgvNi_&2^kg+OQ$Yv$5O*v0X4>@GE1MNtu;wMy9kYn1q!}4 z*2fkId7bhI#tFTzr;cK41RU`$vovLKo|b= zvtE@GH({xOA29go`&V6)`l?x2)=i*Z-5NzW_%Q$Q|q9$;ea&`$Qdi z^kO3?m!V9!l^;{h3X+gDZe0~UESDAN$U&VXj!dTn-qukOzF`vKg*pR*>^aH^`oP|; zVKZQY{u6;RggJf}!QXs7dYa+{KiU<%1f0j^i&vuVdm`kFSKRCcO1BdE1(e12R5
KwB-&;7~Gwqq4WT36H|1MgF;E@NwJ z_9ncfpq2r-%#u2vOjUyDzYg&k_BH|Iv57(P)SZ2)rRC~VLyjeBr5TIZ%9ymo;)<%= z6BBXu7VA;X1eh??bcpti?q!f>7wf=kT{cOESkk_Zuh&pG8a|AYPtL!eiOYPH0tiP{ zKEN<>9V|zNL^dLWew!$_6W*epYz?w25u8iu*)}4LAsUlW){*2@&Y9!=Mc(hBL@@j{*|x17osU`z7YrY~;Tw z4F1acUK@TpnRl}j7u1wK%&O%4XKkisMCuiD<-KovNlMf^sI<))=d=0|$fHU1i>h)| zALi%eX1^J_{HSS(FyU^Ss};)J$Y90DuQ;!N+<;DpM&Q|_WLxSTZK;!qH1qRP=s|YM zm(QE;LN~sRptwf+XptqPIk~B5e31oVs!iIi1K0nf^XF^cXC%QB_5SZ(LVS@XxiSX zMsrGts2@gTow=+@Upzt2qnh`53~Q)8miR7SJQzOQvPt~`2WpypEFy9k4vZ@9K$LtKO_Qf*+z+{au6>O({+ zk=)lZAD##7PlP)E^MP7D&TPlcRh^6#1FVMRJ7d5XlbGV;Y^IW*wsr;CU z#UOEOKV0sYV~GWyi1TMk?C&=gtHaLQ4|7^2(hOOoyW_#X3tZ}F)@z!AjskEXYd=#X z%8qQ@Q)j)BT*pJH1wvtrnnWqb+ZSkhcd-c&nMU<&R6j_X(Im+CZBSL(J{Q^vHY6(Scue3Wi6{S5>L zT{uH7D55wx=62p=!rv1ZYT9-rCILok=2>AzPkQuFtapj+sJ=VOPFifAaym*ps(3KZd!Ih{eedz7WL|R)i2}fK@ zHKj@`M`E9d2C4njvrDK48Jx>k_hT}@&&JrQ`63~%tXfgNt*ifL4YLSzDO%8vnf&au zE3gty@^0=p~+i;@L+QO)@`4b9u9(uf%(|b$z-ReKc?=Q9>Lz~U$WH@V1 z-)=2n0$CMF8dC)?MlHfa>-qzRSyIs$t^7wwUKS%i{Ixb)5f;>EYt+_;&|R z`g}OU@&w7VhDl}|o925^V#%!0P*MbTEeWP()sF zM%`2n1W&189`~e%MGb&r*ZWR)*t2}l7Cb*>xOt1*r1f;@z#&v2T zy^!#Ft?;e8s~>o>@@d#fH(CNy9mR+~gEMI5Z6-96vItW{FHhzA1rxuGgX}c_VAeQd z2~J9AtrL{61~!n6TEwBnlWIt??v7nHKHm}K454dL4HHD}P>e3QpINJCKC6E5SpIYU zDKwPJ)HU9JOg=%zFMD=(GY7B|7=590$tG1mvUS>7;vXIrLd9`fZdr497V`}GJx9;` z&6`FM&3XM0Xzn8UKtQ#vm;Fn@#L-kSX%YrJ`efJe2=aenjnD6n#Msg|fZEk3phPI* zLy-8+Khr>jhj-ZM6G(t+&0}L!XCM<$O5x@lwh^TZ+x5=kvWv9Aj6q)?dn(3T5t*81 z5qHwhQEOk)418gHO4E21kO&pfyeKF&5k$J1R!pLN%OW^4w_Z$68RWwM&3>{APWoib z*6$Mrm|De)29`Fj#Bw*WP?HrKUR1C-RXYfs7nYTk^iLqhjX~SV(b4?}w~enKhpz|5 zmaUYI7iZIw=RQkbA%jke)U}-!)z##NH4BXx!<7i!e#t{gq&1jCv`PdMbj1oZN_~y# z^3P(?(s!096M)!>#X1H)3c+f*3vUKSz?s0II7&>MEo{iU1zY7^&T+P6bw^1T!WyT8Q4 zM#Zbio6De9cW8s;LpM8qMeLt9yyL}-RkP5z<*(68&$vhy zesXlg^?;oGcrRPSTIPU|BSF`=2mE%5O$FG{owqGpvyAyfV;C=hY8kD9a4!;?<|%l6 z5`?WysskX9M8Cthz`h<%Pf;|P0=h&6MZw#}cpM%1F_B{8UX7nyc;Xap5o}~d8HD~X zml0KAi#9bsE&jy#h@x0&F~X|4F#b{D?*?$6&6gN0r3f%$c8xRx zp)cxm>IHj2UK~UB6h~xfRb3|7iM^wNsTk_RlBJ|#SO3wTj0rX%-U88=L|)WImETm~ zLk;;?@^hd#js53F0*r*2d>&S)ag3WRJ&N=6dI%by50vINKf+sdar?U(fgr7aU+jU| zCbk3Sfkh3Xv}I<9!4(oY=56fQogAmMWjX*tMOhqQpgH9YgIkjL;P!@nAtDdV{1(x` zfEb+ZK}szvxLTY}Ku>DGHc*;hcVUp9Cha~-5XLKp`E{GwDU%iwL!LFrn4meqiWfc8 zWfoV@q21ggw+)he z6$}SWo#XAI$q#&$WmjvMNU^~9$yj`P=%W&7XuXdInPFuf9->bcmDMda873f|{3^w- zuWoTwSL6r>7))xkN$TO@GoVtWU)L7s`Ti(%?trX&bI?I$Yn z9TnBhka_Pm@A2pJl}g`~Tq|Zfdnh_wZ1s;L@vP69^D3N06}jrbE0muP-%7*K=;%Rv z!7{7bOJ%I5@&l(K^h4Ft!!VM|j?x+F_6RMAHs%OOZetIL075Q>)%t^L5Gb%s__Xb_ z>aGC<9`U^}j(yc1mHp=e)o=PRZ?Cp{8}1LKYQXiJK)~^3GV{xa`>Q`WMPRaO2Ct^X=45 zrl8Cpy|1?C>3B=VQA?Ay>;-2?=T#}vW^p!OOvw-6J&b%N5n4~(u^NV5(klXEuK{9E z#or-5Vnj=U;g$p?!0Hea9>2St$lTioEqTVUoOWdX09ET-RqD}JIHa$UcRufa3_&CkCZIyPQhp<-qB?~jrY3{Xb3&|T z!4E|7-Wc=bvnkrtSs2q5)=PHzU)f-J*6bQlE0^>ri6_;+k^06QQ>UozgVdaW=4`tT zalhIb0F8aOIlGyHnA4E3*d|Sg9JTK_Dwz>KTMg(S&HNLQwg@{w>{WQNzHI343vs!M zXU%;p^uiIZ>IOLahbluPZGbC6$A18JPGLwtVL#&Fa*Kx7guZ~4Tv_2pIuKv!6j1rbx+J|=(BLz(er$ODH{q?WWQ;NjBtn(e-ha7v zFJ>A(?pRj$25#6r&}a%FD*w}&fTX|EVjM=${r>oIky_&K8X_if$G?k7ES*jg5qj<= z;q*-bJtkBNX zHvZxhLai*W%V5mso&k*bpF-QB>RrtJ`GAjHp>muz_km$P_KGN|!3)4Gi;^=LP+p-j zeJ?I`)xG#ZYlDg7gZ=VT=7*nqo6JN*ypoMbgP`xFh_=tg&q1vO%ST{B`A`>nae=>< zw5)4eeR-8(z;3rvxA1X+mWOn{r)TwT(1R${hW-V7l*X1MnUW0yP5uwut6wul>(|${%;!L8=Lm6|B3pRSH*C2G!B^}54!=c=+C|Vk6=Js z!)Z1}T?+gg&OKeeeDecNU$n;N6B30kwJ}bLz)f=Jz3-*YE-;9g;~BP|CVLR*?lJ?@ zHX~<=@}UHODjwbgS+k|twEn0b35gt;iz$uCDR3DWYVkr-czIMR_T~%+r`BoiofP22TYGtT41-r(+8GI zO@^$xb3u{+8}smOIYq{G|MwwVk+I2tm^->3SF4By5gAsMmlID^xg2Ha1VOy37xt}$ zny+~-f^NY~(W^mu(|5hic^nY$;Dp~Em1O>Ejp2c_b4{)3Z8xJFCV=V|TZy$Q4_x1)5S>!{$Ae?n~db=--B+?=zzKF4&08_)1 zKZ*=hbpu1!Ghg3NJ_tPDnD{vMBBNh(D7(#btAwt=le!+?b3`m{(U z=>RMLkJc{qUqU`STI5!!dic>$oS*pHs8?XtBWnxtlG;@Yu=>w+4ZVN zjpiJE9^x|xz=U`z;v6vx`&am{E7Ae2dacb58{vW#F1ldIf8{$ooaX)#6N61G*dU+e zwo#T3ZOWRaq3|l%aN#95A0aP}+keb_W=qwe+B+ zucu-07j~q5nmg<`gVd0M=8$-iSDi?^$tG&5146_=YMa-UQSXi1NmW=DUO!(nWe*Uf zi4ZQS{sS8+bvo`;oB@Bx`bxd2tEf!7mZ8G(S5b{k2LKisheRyku?H;YNlhf&)B)SR zzmjZa;xI|#mP$iuA)>5Wvnoub} zj3>y&GnI5>QC6iD3+_KQ7@r-1FldMnG5y8o;X=sTw7pf@@rK$Bt{};C0FBN-wRx#a{n)2|g<9U;1b}`IKUNS!mU9AH5;38L{qQsE(a*?=iMiQ~ zl*TT1M`0U_u_2lP^hSfRTupI`E`941`(#Ko)GD{6MqcNc_8DKb0^RO(32K_(N%>!P zWNk`BV%6P$I?a>Ct-xE3afSJ&N4Q)%N983iY}G!$_FpcDs3vfM+PpbIe`w8ym5FA* zIsBn=@f)*>YNkVnwL|vz-{TQuC_@4hRW!>9G@7S?;_%lv2i8t2t1)e2cp+p^nhivc z`xgu}OOOWU3fWI?F+4Oe5KY@oBm+!?A*D;415XyN8CG5qF_2XXS6QBpRo|soai`M? zU5@Nc{W1Xw*$U0jn-tCzfC zEmb>%6_qvk;AQGeJ5|)#6+i@IrdNGE@t*F{QY@gi-Z?KMVw_*)q}lgeZ%r$JRW4$N zoi}u$dX=4nU&L@6@abS3$c|CD*^7Ox8aWl?eR=lrd>;WvI^G|zx634SGj~HtihS@| z#+4JIolTg(f6N;%q35z!dt}9ke`0wD4!%97$xyhtM4|<@J~|n`yDfzCwzZyhaWZ7b zU-(3@h}&+;Xr@g*TFU}wWQ0@57s~un#7=cnML>7d8_5z#titA&2V(!W8!&e>SLJ_(=TL*Pyy9N7yI!C1yK4;la4>0svyvBMx3}Em z*Cj)=xL9-SB@(b%q#jVPKZr}0NE7%EO9Je=nMa9A^XU5~tn&CWl|vsA5RfCBnKos2 z;thzP4?nW=^`Qt1wN@RBfvl+&^c})pK)04t*?eh7*Z!{Wb2$v7<~!Kh70`}7Ae)42 zC1=opCn3Pn4dhfTlb_$R!xOyj;!!SlZ}Xv-9fr#&kb;Yy;Ub(oi9sB_7Fx5Y7Z*si;C+SsJ_>|0NQ!& zSHOix-!R-ngRG2X!j-!<&yU(v-W>X&8rc42rX(EP&ztR-l+0z4!9m56MHmigMM0^s z3RYGiW3WIf;iT{`$b!4u2%FvwbkbrAgj}}2Xa#q-zPx!SCe>jzlf!!Z#+@K^=Tqr zlOma>oM8hH#B|>P$ELduW)C08hF=kQ{&QQXa51z*=l-m#|Bx0%%KUaf+Q1)mDAQqW z_CpEdDG&t9P9zR8lqupnFjSYX6)uA$uf5(az!*6uya&*8SE1%9ItK~cgJGkrRT34c zWYy`)?QYVcs1q66K;R~*fDn`~I#O=0ww$%$9|ge1LJDlxpRVKGNRa++=}@MksG+rf zWqwa_%h{R(!tu-i18*_v;g5A^DPh&TN|SQg9bOs~!Y@wf&j?fe=Ut{lhOt5qTftHl zy}MRxDMwoQH|x>!BQg-4kycKAWI?ZiA7cC6k9?Cqq$vewrg<0&MW7BWAZ2getF$`F ziGk2ZuMmw6{R$+Ec(XWU=uAff{r}`*Nj#HLkD&cT3-my7CgiJIr~m!fe9d)f5g?D- zE~LqF8lXvf(UjPflhe_lWeU;gbkn8+-5(}IwEcwplU(G1o$&8TN>$d?x|n&QY1dd> zC$PnX%7u85s(?ELcIcU|rAP>JMw*i^;hE4$CCv}@Z%Fx&xq(SSP)0$ImnBdqpHQ%> zyuO5c)Odfi=y6<_$}5{NHGPM{S{L{e3}0S>zFlWH=ClQV7el0vp~{#Ib@i<8p84^h zZGT)Zh1KAucz`MKfMY$!=L3rVGVz$T5k#Nm*c33fGE)d|tz-^Hgklb*NKT&=BC|Eb$FFy?bY`W`!Q4~QjeWjk<=tV zFd~gxrmCD95l_U@dRzMORDJ`>gy{lyJgxa6?PK73447Kb?L^6{jtK$EpEBu>(0Lc# zZn1B!qEw{+L<}hrTZ;rl!ccz-SGy&~dvf_941+o*CN8fFK!x@|t3QiW{PyHSrrrVE zvNs0Meah6>p(F4{ybq^NHy1g~+f0qfR4iLAmZ=1DmdR9YC`0+9Ia6F)<;{=*jC9PE z&oX!#Lff61+Qy;E2}$&@Cjs(TT(Ex;_azs3=MKBkd;?>*G8ox1|GFZBF>-`3F|h=< z%Yky!G=cZ6dG&=O-S@oGeiI$FOa?X4*(U%y?6~pkWIo7Qew)0O@mdQ((7+M zM}5q~E3NubaaLX|sy$hoZ z0R+krwN`0FJU568t_dz(7PlW#R#08Y4nxOdx5vdK*FQkKIOpZ%JA1f{^B3>~M8dwiz)Sd=Vv8P}ENAGU8(ASUFRJ}bX%+B#;4T!< za*Ya3zib603i@APkCbpozWTkqkZ)ZXMd=F^Fo!@ zVv4V4)En=%m|X?j9ELd$D_T%n17OM)<@|v@8qD0Jxic79f0bsfCIcovbTqo(~b9cUtoX1tN#WaCSGrgeIoeD+9@Kc=+k zKyvh;FY8dCyb$G8qZLvzG9LU~&{bn-o=v?hs`5<~DDZetsk6I+7WeR}uI+CC^~Rzl zPzhzomFyOX8puV|>V&)e$~a+=#+xdWQPgZ!sH>xGW&pRiU?S3vd?D9=wLo{#_GRH9 zsPs%(p_E^}7v@x(?M-nN^45Ikc`ptsom#K`Fm+k{sF|Y)(PqSQP=J2qBhUr=d#k3% zYu+$>yazzcuL+v%*?t(y>3m1CCaZE_w96sGWWNCP;+e0&bU6K+_@xh!p1jJlFTvQ5 z|DqXU@BEU}eoKV9Vt18+bU_vE9n?4T>hEn?o5YIw)7URkl~Pg~UuLmbMN$$*+i1I> z?Gtkl{}whb{Exn^-ykFeGSXyaUFh6ptAZ#M3|QITBI`i4`GxK2=j&!zl3!thM}Guy z7JSTfei!L8qon_%K%W6cpUD-^r?HwqZhdW9J9YXm1gZkA?yj*XnU)o!aN%gZkFbVt zv_8R*cYZ|fZuvn)7l}Izw#d9Gag42fdgM7JF@NU%=L$kE)mLn_*BGVSQg~mlV)o46 zPX6AbyrU3|)`bfH?c?tC+)bH|HU|zz;WhlKw|_=X=cAf-jU3)Vh4Gs7uQ62#tsw71 zuwJrh=KN$%j=?d;BGzq~EIVx1tV_2d2?6yE36ls~WW?1aZhR(GDh)uffPl0NkO;b7 zcrcTKjC(f#>AI+~qxv6(f*r=WMlZ+oap%H0T|d(*Kos#(x8p8$=~l10ak_?w)|3{mT|14GkWghd!-CL!IF^E`f9f?-qZUq<;?9v}hg8Nc@k1tIMC6zG z{@wa?ec>{4_*9G+P-Ia0#WXj;3$lXFY83Q;tpzyB diff --git a/crates/wallets/src/wallet_browser/app/assets/main.js b/crates/wallets/src/wallet_browser/app/assets/main.js deleted file mode 100644 index 6d89aa9e303f1..0000000000000 --- a/crates/wallets/src/wallet_browser/app/assets/main.js +++ /dev/null @@ -1,71 +0,0 @@ -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(e&&(t=e(e=0)),t),s=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),c=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},l=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},u=(n,r,a)=>(a=n==null?{}:e(i(n)),l(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),d=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function ee(){}var C={H:null,A:null,T:null,S:null},te=Object.prototype.hasOwnProperty;function ne(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function re(e,t){return ne(e.type,t,e.props)}function ie(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function ae(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var oe=/\/+/g;function se(e,t){return typeof e==`object`&&e&&e.key!=null?ae(``+e.key):t.toString(36)}function ce(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(ee,ee):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function le(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,le(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+se(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(oe,`$&/`)+`/`),le(o,r,i,``,function(e){return e})):o!=null&&(ie(o)&&(o=re(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(oe,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=d()})),p=s((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,ie());else{var t=n(l);t!==null&&se(x,t.startTime-e)}}var S=!1,ee=-1,C=5,te=-1;function ne(){return g?!0:!(e.unstable_now()-tet&&ne());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&se(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?ie():S=!1}}}var ie;if(typeof y==`function`)ie=function(){y(re)};else if(typeof MessageChannel<`u`){var ae=new MessageChannel,oe=ae.port2;ae.port1.onmessage=re,ie=function(){oe.postMessage(null)}}else ie=function(){_(re,0)};function se(t,n){ee=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(ee),ee=-1):h=!0,se(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,ie()))),r},e.unstable_shouldYield=ne,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),m=s(((e,t)=>{t.exports=p()})),h=s((e=>{var t=f();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=h()})),_=s((e=>{var t=m(),n=f(),r=g();function i(e){var t=`https://react.dev/errors/`+e;if(1he||(e.current=me[he],me[he]=null,he--)}function ve(e,t){he++,me[he]=e.current,e.current=t}var ye=ge(null),be=ge(null),xe=ge(null),Se=ge(null);function Ce(e,t){switch(ve(xe,t),ve(be,e),ve(ye,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?sf(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=sf(t),e=cf(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}_e(ye),ve(ye,e)}function we(){_e(ye),_e(be),_e(xe)}function Te(e){e.memoizedState!==null&&ve(Se,e);var t=ye.current,n=cf(t,e.type);t!==n&&(ve(be,e),ve(ye,n))}function Ee(e){be.current===e&&(_e(ye),_e(be)),Se.current===e&&(_e(Se),hp._currentValue=pe)}var De,Oe;function ke(e){if(De===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);De=t&&t[1]||``,Oe=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{Ae=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?ke(n):``}function Me(e,t){switch(e.tag){case 26:case 27:case 5:return ke(e.type);case 16:return ke(`Lazy`);case 13:return e.child!==t&&t!==null?ke(`Suspense Fallback`):ke(`Suspense`);case 19:return ke(`SuspenseList`);case 0:case 15:return je(e.type,!1);case 11:return je(e.type.render,!1);case 1:return je(e.type,!0);case 31:return ke(`Activity`);default:return``}}function Ne(e){try{var t=``,n=null;do t+=Me(e,n),n=e,e=e.return;while(e);return t}catch(e){return` -Error generating stack: `+e.message+` -`+e.stack}}var Pe=Object.prototype.hasOwnProperty,Fe=t.unstable_scheduleCallback,Ie=t.unstable_cancelCallback,Le=t.unstable_shouldYield,Re=t.unstable_requestPaint,ze=t.unstable_now,Be=t.unstable_getCurrentPriorityLevel,Ve=t.unstable_ImmediatePriority,He=t.unstable_UserBlockingPriority,Ue=t.unstable_NormalPriority,We=t.unstable_LowPriority,Ge=t.unstable_IdlePriority,Ke=t.log,qe=t.unstable_setDisableYieldValue,Je=null,Ye=null;function Xe(e){if(typeof Ke==`function`&&qe(e),Ye&&typeof Ye.setStrictMode==`function`)try{Ye.setStrictMode(Je,e)}catch{}}var Ze=Math.clz32?Math.clz32:et,Qe=Math.log,$e=Math.LN2;function et(e){return e>>>=0,e===0?32:31-(Qe(e)/$e|0)|0}var tt=256,nt=262144,rt=4194304;function it(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function at(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=it(n))):i=it(o):i=it(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=it(n))):i=it(o)):i=it(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function ot(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function st(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ct(){var e=rt;return rt<<=1,!(rt&62914560)&&(rt=4194304),e}function lt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function ut(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function dt(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),xn=!1;if(bn)try{var Sn={};Object.defineProperty(Sn,`passive`,{get:function(){xn=!0}}),window.addEventListener(`test`,Sn,Sn),window.removeEventListener(`test`,Sn,Sn)}catch{xn=!1}var Cn=null,wn=null,Tn=null;function En(){if(Tn)return Tn;var e,t=wn,n=t.length,r,i=`value`in Cn?Cn.value:Cn.textContent,a=i.length;for(e=0;e=nr),ar=` `,or=!1;function sr(e,t){switch(e){case`keyup`:return er.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function cr(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var lr=!1;function ur(e,t){switch(e){case`compositionend`:return cr(t);case`keypress`:return t.which===32?(or=!0,ar):null;case`textInput`:return e=t.data,e===ar&&or?null:e;default:return null}}function dr(e,t){if(lr)return e===`compositionend`||!tr&&sr(e,t)?(e=En(),Tn=wn=Cn=null,lr=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=Nr(n)}}function Fr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Fr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Ir(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=qt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=qt(e.document)}return t}function Lr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t===`input`&&(e.type===`text`||e.type===`search`||e.type===`tel`||e.type===`url`||e.type===`password`)||t===`textarea`||e.contentEditable===`true`)}var Rr=bn&&`documentMode`in document&&11>=document.documentMode,zr=null,Br=null,Vr=null,Hr=!1;function Ur(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Hr||zr==null||zr!==qt(r)||(r=zr,`selectionStart`in r&&Lr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Vr&&Mr(Vr,r)||(Vr=r,r=Gd(Br,`onSelect`),0>=o,i-=o,Fi=1<<32-Ze(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),Wi&&Li(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),Wi&&Li(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return Wi&&Li(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),Wi&&Li(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===ie&&Ra(l)===r.type){n(e,r.sibling),c=a(r,o.props),Ga(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=Si(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=xi(o.type,o.key,o.props,null,e.mode,c),Ga(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=Ti(o,e.mode,c),c.return=e,e=c}return s(e);case ie:return o=Ra(o),b(e,r,o,c)}if(de(o))return h(e,r,o,c);if(ce(o)){if(l=ce(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,Wa(o),c);if(o.$$typeof===ee)return b(e,r,fa(e,o),c);Ka(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=Ci(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Ua=0;var i=b(e,t,n,r);return Ha=null,i}catch(t){if(t===Ma||t===Pa)throw t;var a=_i(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Ja=qa(!0),Ya=qa(!1),Xa=!1;function Za(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Qa(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function $a(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function eo(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,eu&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=mi(e),pi(e,null,n),t}return ui(e,r,t,n),mi(e)}function to(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,pt(e,n)}}function no(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var ro=!1;function io(){if(ro){var e=Ca;if(e!==null)throw e}}function ao(e,t,n,r){ro=!1;var i=e.updateQueue;Xa=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,m=f!==s.lane;if(m?(M&f)===f:(r&f)===f){f!==0&&f===Sa&&(ro=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=p({},d,f);break a;case 2:Xa=!0}}f=s.callback,f!==null&&(e.flags|=64,m&&(e.flags|=8192),m=i.callbacks,m===null?i.callbacks=[f]:m.push(f))}else m={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=m,c=d):u=u.next=m,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;m=s,s=m.next,m.next=null,i.lastBaseUpdate=m,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),lu|=o,e.lanes=o,e.memoizedState=d}}function oo(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function so(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=w.T,s={};w.T=s,qs(e,!1,t,n);try{var c=i(),l=w.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?Ks(e,t,Ea(c,r),ju(e)):Ks(e,t,r,ju(e))}catch(n){Ks(e,t,{then:function(){},status:`rejected`,reason:n},ju())}finally{fe.p=a,o!==null&&s.types!==null&&(o.types=s.types),w.T=o}}function Is(){}function Ls(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=Rs(e).queue;Fs(e,a,t,pe,n===null?Is:function(){return zs(e),n(r)})}function Rs(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:pe,baseState:pe,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jo,lastRenderedState:pe},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function zs(e){var t=Rs(e);t.next===null&&(t=e.alternate.memoizedState),Ks(e,t.next.queue,{},ju())}function Bs(){return da(hp)}function Vs(){return Uo().memoizedState}function Hs(){return Uo().memoizedState}function Us(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=ju();e=$a(n);var r=eo(t,e,n);r!==null&&(Nu(r,t,n),to(r,t,n)),t={cache:va()},e.payload=t;return}t=t.return}}function Ws(e,t,n){var r=ju();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Js(e)?Ys(t,n):(n=di(e,t,n,r),n!==null&&(Nu(n,e,r),Xs(n,t,r)))}function Gs(e,t,n){Ks(e,t,n,ju())}function Ks(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Js(e))Ys(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,jr(s,o))return ui(e,t,i,0),tu===null&&li(),!1}catch{}if(n=di(e,t,i,r),n!==null)return Nu(n,e,r),Xs(n,t,r),!0}return!1}function qs(e,t,n,r){if(r={lane:2,revertLane:Ad(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Js(e)){if(t)throw Error(i(479))}else t=di(e,n,r,2),t!==null&&Nu(t,e,2)}function Js(e){var t=e.alternate;return e===A||t!==null&&t===A}function Ys(e,t){Do=Eo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Xs(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,pt(e,n)}}var Zs={readContext:da,use:Ko,useCallback:No,useContext:No,useEffect:No,useImperativeHandle:No,useLayoutEffect:No,useInsertionEffect:No,useMemo:No,useReducer:No,useRef:No,useState:No,useDebugValue:No,useDeferredValue:No,useTransition:No,useSyncExternalStore:No,useId:No,useHostTransitionStatus:No,useFormState:No,useActionState:No,useOptimistic:No,useMemoCache:No,useCacheRefresh:No};Zs.useEffectEvent=No;var Qs={readContext:da,use:Ko,useCallback:function(e,t){return Ho().memoizedState=[e,t===void 0?null:t],e},useContext:da,useEffect:Ss,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),bs(4194308,4,Os.bind(null,t,e),n)},useLayoutEffect:function(e,t){return bs(4194308,4,e,t)},useInsertionEffect:function(e,t){bs(4,2,e,t)},useMemo:function(e,t){var n=Ho();t=t===void 0?null:t;var r=e();if(Oo){Xe(!0);try{e()}finally{Xe(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Ho();if(n!==void 0){var i=n(t);if(Oo){Xe(!0);try{n(t)}finally{Xe(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=Ws.bind(null,A,e),[r.memoizedState,e]},useRef:function(e){var t=Ho();return e={current:e},t.memoizedState=e},useState:function(e){e=is(e);var t=e.queue,n=Gs.bind(null,A,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:As,useDeferredValue:function(e,t){return Ns(Ho(),e,t)},useTransition:function(){var e=is(!1);return e=Fs.bind(null,A,e.queue,!0,!1),Ho().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=A,a=Ho();if(Wi){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),tu===null)throw Error(i(349));M&127||$o(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,Ss(ts.bind(null,r,o,e),[e]),r.flags|=2048,vs(9,{destroy:void 0},es.bind(null,r,o,n,t),null),n},useId:function(){var e=Ho(),t=tu.identifierPrefix;if(Wi){var n=Ii,r=Fi;n=(r&~(1<<32-Ze(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=ko++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[bt]=t,o[xt]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch($d(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Kc(t)}}return Zc(t),qc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Kc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=xe.current,Zi(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Hi,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[bt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Zd(e.nodeValue,n)),e||Ji(t,!0)}else e=of(e).createTextNode(r),e[bt]=t,t.stateNode=e}return Zc(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Zi(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[bt]=t}else Qi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Zc(t),e=!1}else n=$i(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(bo(t),t):(bo(t),null);if(t.flags&128)throw Error(i(558))}return Zc(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=Zi(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[bt]=t}else Qi(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Zc(t),a=!1}else a=$i(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(bo(t),t):(bo(t),null)}return bo(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Yc(t,t.updateQueue),Zc(t),null);case 4:return we(),e===null&&Vd(t.stateNode.containerInfo),Zc(t),null;case 10:return aa(t.type),Zc(t),null;case 19:if(_e(xo),r=t.memoizedState,r===null)return Zc(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)Xc(r,!1);else{if(cu!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=So(e),o!==null){for(t.flags|=128,Xc(r,!1),e=o.updateQueue,t.updateQueue=e,Yc(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)bi(n,e),n=n.sibling;return ve(xo,xo.current&1|2),Wi&&Li(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&ze()>yu&&(t.flags|=128,a=!0,Xc(r,!1),t.lanes=4194304)}else{if(!a)if(e=So(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Yc(t,e),Xc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!Wi)return Zc(t),null}else 2*ze()-r.renderingStartTime>yu&&n!==536870912&&(t.flags|=128,a=!0,Xc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(Zc(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=ze(),e.sibling=null,n=xo.current,ve(xo,a?n&1|2:n&1),Wi&&Li(t,r.treeForkCount),e);case 22:case 23:return bo(t),po(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(Zc(t),t.subtreeFlags&6&&(t.flags|=8192)):Zc(t),n=t.updateQueue,n!==null&&Yc(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&_e(Oa),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),aa(_a),Zc(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function $c(e,t){switch(Bi(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return aa(_a),we(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Ee(t),null;case 31:if(t.memoizedState!==null){if(bo(t),t.alternate===null)throw Error(i(340));Qi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(bo(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Qi()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return _e(xo),null;case 4:return we(),null;case 10:return aa(t.type),null;case 22:case 23:return bo(t),po(),e!==null&&_e(Oa),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return aa(_a),null;case 25:return null;default:return null}}function el(e,t){switch(Bi(t),t.tag){case 3:aa(_a),we();break;case 26:case 27:case 5:Ee(t);break;case 4:we();break;case 31:t.memoizedState!==null&&bo(t);break;case 13:bo(t);break;case 19:_e(xo);break;case 10:aa(t.type);break;case 22:case 23:bo(t),po(),e!==null&&_e(Oa);break;case 24:aa(_a)}}function tl(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){ld(t,t.return,e)}}function nl(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){ld(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){ld(t,t.return,e)}}function rl(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{so(t,n)}catch(t){ld(e,e.return,t)}}}function il(e,t,n){n.props=ac(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){ld(e,t,n)}}function al(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){ld(e,t,n)}}function ol(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){ld(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){ld(e,t,n)}else n.current=null}function sl(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){ld(e,e.return,t)}}function cl(e,t,n){try{var r=e.stateNode;ef(r,e.type,n,t),r[xt]=t}catch(t){ld(e,e.return,t)}}function ll(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&_f(e.type)||e.tag===4}function ul(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||ll(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&_f(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function dl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=dn));else if(r!==4&&(r===27&&_f(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(dl(e,t,n),e=e.sibling;e!==null;)dl(e,t,n),e=e.sibling}function fl(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&_f(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(fl(e,t,n),e=e.sibling;e!==null;)fl(e,t,n),e=e.sibling}function pl(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);$d(t,r,n),t[bt]=e,t[xt]=n}catch(t){ld(e,e.return,t)}}var ml=!1,hl=!1,gl=!1,_l=typeof WeakSet==`function`?WeakSet:Set,vl=null;function yl(e,t){if(e=e.containerInfo,rf=wp,e=Ir(e),Lr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(af={focusedElem:e,selectionRange:n},wp=!1,vl=t;vl!==null;)if(t=vl,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,vl=e;else for(;vl!==null;){switch(t=vl,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),$d(o,r,n),o[bt]=e,Mt(o),r=o;break a;case`link`:var s=ip(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=Pr(s,h),v=Pr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,w.T=null,n=Du,Du=null;var o=Cu,s=Tu;if(Su=0,wu=Cu=null,Tu=0,eu&6)throw Error(i(331));var c=eu;if(eu|=4,Yl(o.current),Vl(o,o.current,s,n),eu=c,Cd(0,!1),Ye&&typeof Ye.onPostCommitFiberRoot==`function`)try{Ye.onPostCommitFiberRoot(Je,o)}catch{}return!0}finally{fe.p=a,w.T=r,ad(e,t)}}function cd(e,t,n){t=Di(n,t),t=dc(e.stateNode,t,2),e=eo(e,t,2),e!==null&&(ut(e,2),Sd(e))}function ld(e,t,n){if(e.tag===3)cd(e,e,n);else for(;t!==null;){if(t.tag===3){cd(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(xu===null||!xu.has(r))){e=Di(n,e),n=fc(2),r=eo(t,n,2),r!==null&&(pc(n,r,t,e),ut(r,2),Sd(r));break}}t=t.return}}function ud(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new $l;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(ou=!0,i.add(n),e=dd.bind(null,e,t,n),t.then(e,e))}function dd(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,tu===e&&(M&n)===n&&(cu===4||cu===3&&(M&62914560)===M&&300>ze()-_u?!(eu&2)&&Bu(e,0):du|=n,pu===M&&(pu=0)),Sd(e)}function fd(e,t){t===0&&(t=ct()),e=fi(e,t),e!==null&&(ut(e,t),Sd(e))}function pd(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),fd(e,n)}function md(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),fd(e,n)}function hd(e,t){return Fe(e,t)}var gd=null,_d=null,vd=!1,yd=!1,bd=!1,xd=0;function Sd(e){e!==_d&&e.next===null&&(_d===null?gd=_d=e:_d=_d.next=e),yd=!0,vd||(vd=!0,kd())}function Cd(e,t){if(!bd&&yd){bd=!0;do for(var n=!1,r=gd;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ze(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,Od(r,a))}else a=M,a=at(r,r===tu?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||ot(r,a)||(n=!0,Od(r,a));r=r.next}while(n);bd=!1}}function wd(){Td()}function Td(){yd=vd=!1;var e=0;xd!==0&&df()&&(e=xd);for(var t=ze(),n=null,r=gd;r!==null;){var i=r.next,a=Ed(r,t);a===0?(r.next=null,n===null?gd=i:n.next=i,i===null&&(_d=n)):(n=r,(e!==0||a&3)&&(yd=!0)),r=i}Su!==0&&Su!==5||Cd(e,!1),xd!==0&&(xd=0)}function Ed(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&tf(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function Bf(e,t,n){var r=zf;if(r&&typeof t==`string`&&t){var i=Yt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),Pf.has(i)||(Pf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),$d(t,`link`,e),Mt(t),r.head.appendChild(t)))}}function Vf(e){If.D(e),Bf(`dns-prefetch`,e,null)}function Hf(e,t){If.C(e,t),Bf(`preconnect`,e,t)}function Uf(e,t,n){If.L(e,t,n);var r=zf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Yt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Yt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Yt(n.imageSizes)+`"]`)):i+=`[href="`+Yt(e)+`"]`;var a=i;switch(t){case`style`:a=Jf(e);break;case`script`:a=I(e)}Nf.has(a)||(e=p({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),Nf.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(Yf(a))||t===`script`&&r.querySelector(Qf(a))||(t=r.createElement(`link`),$d(t,`link`,e),Mt(t),r.head.appendChild(t)))}}function Wf(e,t){If.m(e,t);var n=zf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Yt(r)+`"][href="`+Yt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=I(e)}if(!Nf.has(a)&&(e=p({rel:`modulepreload`,href:e},t),Nf.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Qf(a)))return}r=n.createElement(`link`),$d(r,`link`,e),Mt(r),n.head.appendChild(r)}}}function Gf(e,t,n){If.S(e,t,n);var r=zf;if(r&&e){var i=jt(r).hoistableStyles,a=Jf(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(Yf(a)))s.loading=5;else{e=p({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=Nf.get(a))&&tp(e,n);var c=o=r.createElement(`link`);Mt(c),$d(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,ep(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Kf(e,t){If.X(e,t);var n=zf;if(n&&e){var r=jt(n).hoistableScripts,i=I(e),a=r.get(i);a||(a=n.querySelector(Qf(i)),a||(e=p({src:e,async:!0},t),(t=Nf.get(i))&&np(e,t),a=n.createElement(`script`),Mt(a),$d(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function qf(e,t){If.M(e,t);var n=zf;if(n&&e){var r=jt(n).hoistableScripts,i=I(e),a=r.get(i);a||(a=n.querySelector(Qf(i)),a||(e=p({src:e,async:!0,type:`module`},t),(t=Nf.get(i))&&np(e,t),a=n.createElement(`script`),Mt(a),$d(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function F(e,t,n,r){var a=(a=xe.current)?Ff(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Jf(n.href),n=jt(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Jf(n.href);var o=jt(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(Yf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),Nf.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},Nf.set(e,n),o||Zf(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=I(n),n=jt(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Jf(e){return`href="`+Yt(e)+`"`}function Yf(e){return`link[rel="stylesheet"][`+e+`]`}function Xf(e){return p({},e,{"data-precedence":e.precedence,precedence:null})}function Zf(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),$d(t,`link`,n),Mt(t),e.head.appendChild(t))}function I(e){return`[src="`+Yt(e)+`"]`}function Qf(e){return`script[async]`+e}function $f(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Yt(n.href)+`"]`);if(r)return t.instance=r,Mt(r),r;var a=p({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),Mt(r),$d(r,`style`,a),ep(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Jf(n.href);var o=e.querySelector(Yf(a));if(o)return t.state.loading|=4,t.instance=o,Mt(o),o;r=Xf(n),(a=Nf.get(a))&&tp(r,a),o=(e.ownerDocument||e).createElement(`link`),Mt(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),$d(o,`link`,r),t.state.loading|=4,ep(o,n.precedence,e),t.instance=o;case`script`:return o=I(n.src),(a=e.querySelector(Qf(o)))?(t.instance=a,Mt(a),a):(r=n,(a=Nf.get(o))&&(r=p({},n),np(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),Mt(a),$d(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,ep(r,n.precedence,e));return t.instance}function ep(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function op(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function sp(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function cp(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Jf(r.href),a=t.querySelector(Yf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=dp.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,Mt(a);return}a=t.ownerDocument||t,r=Xf(r),(i=Nf.get(i))&&tp(r,i),a=a.createElement(`link`),Mt(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),$d(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=dp.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var lp=0;function up(e,t){return e.stylesheets&&e.count===0&&pp(e,e.stylesheets),0lp?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function dp(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)pp(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var fp=null;function pp(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,fp=new Map,t.forEach(mp,e),fp=null,dp.call(e))}function mp(e,t){if(!(t.state.loading&4)){var n=fp.get(e);if(n)var r=n.get(null);else{n=new Map,fp.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=_()}))(),y=f(),b,x=o((()=>{b=`1.2.3`})),S,ee=o((()=>{x(),S=class e extends Error{constructor(t,n={}){let r=n.cause instanceof e?n.cause.details:n.cause?.message?n.cause.message:n.details,i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=[t||`An error occurred.`,``,...n.metaMessages?[...n.metaMessages,``]:[],...i?[`Docs: https://abitype.dev${i}`]:[],...r?[`Details: ${r}`]:[],`Version: abitype@${b}`].join(` -`);super(a),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`metaMessages`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiTypeError`}),n.cause&&(this.cause=n.cause),this.details=r,this.docsPath=i,this.metaMessages=n.metaMessages,this.shortMessage=t}}}));function C(e,t){return e.exec(t)?.groups}var te,ne,re,ie=o((()=>{te=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,ne=/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/,re=/^\(.+?\).*?$/}));function ae(e){let t=e.type;if(oe.test(e.type)&&`components`in e){t=`(`;let n=e.components.length;for(let r=0;r{ie(),oe=/^tuple(?(\[(\d*)\])*)$/}));function ce(e){let t=``,n=e.length;for(let r=0;r{se()}));function ue(e){return e.type===`function`?`function ${e.name}(${ce(e.inputs)})${e.stateMutability&&e.stateMutability!==`nonpayable`?` ${e.stateMutability}`:``}${e.outputs?.length?` returns (${ce(e.outputs)})`:``}`:e.type===`event`?`event ${e.name}(${ce(e.inputs)})`:e.type===`error`?`error ${e.name}(${ce(e.inputs)})`:e.type===`constructor`?`constructor(${ce(e.inputs)})${e.stateMutability===`payable`?` payable`:``}`:e.type===`fallback`?`fallback() external${e.stateMutability===`payable`?` payable`:``}`:`receive() external payable`}var de=o((()=>{le()}));function w(e){return we.test(e)}function fe(e){return C(we,e)}function pe(e){return Te.test(e)}function me(e){return C(Te,e)}function he(e){return Ee.test(e)}function ge(e){return C(Ee,e)}function _e(e){return De.test(e)}function ve(e){return C(De,e)}function ye(e){return Oe.test(e)}function be(e){return C(Oe,e)}function xe(e){return ke.test(e)}function Se(e){return C(ke,e)}function Ce(e){return Ae.test(e)}var we,Te,Ee,De,Oe,ke,Ae,je,Me,Ne,Pe=o((()=>{ie(),we=/^error (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)$/,Te=/^event (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)$/,Ee=/^function (?[a-zA-Z$_][a-zA-Z0-9$_]*)\((?.*?)\)(?: (?external|public{1}))?(?: (?pure|view|nonpayable|payable{1}))?(?: returns\s?\((?.*?)\))?$/,De=/^struct (?[a-zA-Z$_][a-zA-Z0-9$_]*) \{(?.*?)\}$/,Oe=/^constructor\((?.*?)\)(?:\s(?payable{1}))?$/,ke=/^fallback\(\) external(?:\s(?payable{1}))?$/,Ae=/^receive\(\) external payable$/,je=new Set([`memory`,`indexed`,`storage`,`calldata`]),Me=new Set([`indexed`]),Ne=new Set([`calldata`,`memory`,`storage`])})),Fe,Ie,Le,Re=o((()=>{ee(),Fe=class extends S{constructor({signature:e}){super(`Failed to parse ABI item.`,{details:`parseAbiItem(${JSON.stringify(e,null,2)})`,docsPath:`/api/human#parseabiitem-1`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiItemError`})}},Ie=class extends S{constructor({type:e}){super(`Unknown type.`,{metaMessages:[`Type "${e}" is not a valid ABI type. Perhaps you forgot to include a struct signature?`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownTypeError`})}},Le=class extends S{constructor({type:e}){super(`Unknown type.`,{metaMessages:[`Type "${e}" is not a valid ABI type.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownSolidityTypeError`})}}})),ze,Be,Ve,He,Ue,We,Ge=o((()=>{ee(),ze=class extends S{constructor({params:e}){super(`Failed to parse ABI parameters.`,{details:`parseAbiParameters(${JSON.stringify(e,null,2)})`,docsPath:`/api/human#parseabiparameters-1`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiParametersError`})}},Be=class extends S{constructor({param:e}){super(`Invalid ABI parameter.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidParameterError`})}},Ve=class extends S{constructor({param:e,name:t}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`"${t}" is a protected Solidity keyword. More info: https://docs.soliditylang.org/en/latest/cheatsheet.html`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SolidityProtectedKeywordError`})}},He=class extends S{constructor({param:e,type:t,modifier:n}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`Modifier "${n}" not allowed${t?` in "${t}" type`:``}.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidModifierError`})}},Ue=class extends S{constructor({param:e,type:t,modifier:n}){super(`Invalid ABI parameter.`,{details:e,metaMessages:[`Modifier "${n}" not allowed${t?` in "${t}" type`:``}.`,`Data location can only be specified for array, struct, or mapping types, but "${n}" was given.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidFunctionModifierError`})}},We=class extends S{constructor({abiParameter:e}){super(`Invalid ABI parameter.`,{details:JSON.stringify(e,null,2),metaMessages:[`ABI parameter type is invalid.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidAbiTypeParameterError`})}}})),Ke,qe,Je,Ye=o((()=>{ee(),Ke=class extends S{constructor({signature:e,type:t}){super(`Invalid ${t} signature.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidSignatureError`})}},qe=class extends S{constructor({signature:e}){super(`Unknown signature.`,{details:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`UnknownSignatureError`})}},Je=class extends S{constructor({signature:e}){super(`Invalid struct signature.`,{details:e,metaMessages:[`No properties exist.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidStructSignatureError`})}}})),Xe,Ze=o((()=>{ee(),Xe=class extends S{constructor({type:e}){super(`Circular reference detected.`,{metaMessages:[`Struct "${e}" is a circular reference.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`CircularReferenceError`})}}})),Qe,$e=o((()=>{ee(),Qe=class extends S{constructor({current:e,depth:t}){super(`Unbalanced parentheses.`,{metaMessages:[`"${e.trim()}" has too many ${t>0?`opening`:`closing`} parentheses.`],details:`Depth "${t}"`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`InvalidParenthesisError`})}}}));function et(e,t,n){let r=``;if(n)for(let e of Object.entries(n)){if(!e)continue;let t=``;for(let n of e[1])t+=`[${n.type}${n.name?`:${n.name}`:``}]`;r+=`(${e[0]}{${t}})`}return t?`${t}:${e}${r}`:`${e}${r}`}var tt,nt=o((()=>{tt=new Map([[`address`,{type:`address`}],[`bool`,{type:`bool`}],[`bytes`,{type:`bytes`}],[`bytes32`,{type:`bytes32`}],[`int`,{type:`int256`}],[`int256`,{type:`int256`}],[`string`,{type:`string`}],[`uint`,{type:`uint256`}],[`uint8`,{type:`uint8`}],[`uint16`,{type:`uint16`}],[`uint24`,{type:`uint24`}],[`uint32`,{type:`uint32`}],[`uint64`,{type:`uint64`}],[`uint96`,{type:`uint96`}],[`uint112`,{type:`uint112`}],[`uint160`,{type:`uint160`}],[`uint192`,{type:`uint192`}],[`uint256`,{type:`uint256`}],[`address owner`,{type:`address`,name:`owner`}],[`address to`,{type:`address`,name:`to`}],[`bool approved`,{type:`bool`,name:`approved`}],[`bytes _data`,{type:`bytes`,name:`_data`}],[`bytes data`,{type:`bytes`,name:`data`}],[`bytes signature`,{type:`bytes`,name:`signature`}],[`bytes32 hash`,{type:`bytes32`,name:`hash`}],[`bytes32 r`,{type:`bytes32`,name:`r`}],[`bytes32 root`,{type:`bytes32`,name:`root`}],[`bytes32 s`,{type:`bytes32`,name:`s`}],[`string name`,{type:`string`,name:`name`}],[`string symbol`,{type:`string`,name:`symbol`}],[`string tokenURI`,{type:`string`,name:`tokenURI`}],[`uint tokenId`,{type:`uint256`,name:`tokenId`}],[`uint8 v`,{type:`uint8`,name:`v`}],[`uint256 balance`,{type:`uint256`,name:`balance`}],[`uint256 tokenId`,{type:`uint256`,name:`tokenId`}],[`uint256 value`,{type:`uint256`,name:`value`}],[`event:address indexed from`,{type:`address`,name:`from`,indexed:!0}],[`event:address indexed to`,{type:`address`,name:`to`,indexed:!0}],[`event:uint indexed tokenId`,{type:`uint256`,name:`tokenId`,indexed:!0}],[`event:uint256 indexed tokenId`,{type:`uint256`,name:`tokenId`,indexed:!0}]])}));function rt(e,t={}){if(he(e))return it(e,t);if(pe(e))return at(e,t);if(w(e))return ot(e,t);if(ye(e))return st(e,t);if(xe(e))return ct(e);if(Ce(e))return{type:`receive`,stateMutability:`payable`};throw new qe({signature:e})}function it(e,t={}){let n=ge(e);if(!n)throw new Ke({signature:e,type:`function`});let r=ut(n.parameters),i=[],a=r.length;for(let e=0;e{ie(),Re(),Ge(),Ye(),$e(),nt(),Pe(),mt=/^(?[a-zA-Z$_][a-zA-Z0-9$_]*(?:\spayable)?)(?(?:\[\d*?\])+?)?(?:\s(?calldata|indexed|memory|storage{1}))?(?:\s(?[a-zA-Z$_][a-zA-Z0-9$_]*))?$/,ht=/^\((?.+?)\)(?(?:\[\d*?\])+?)?(?:\s(?calldata|indexed|memory|storage{1}))?(?:\s(?[a-zA-Z$_][a-zA-Z0-9$_]*))?$/,gt=/^u?int$/,_t=/^(?:after|alias|anonymous|apply|auto|byte|calldata|case|catch|constant|copyof|default|defined|error|event|external|false|final|function|immutable|implements|in|indexed|inline|internal|let|mapping|match|memory|mutable|null|of|override|partial|private|promise|public|pure|reference|relocatable|return|returns|sizeof|static|storage|struct|super|supports|switch|this|true|try|typedef|typeof|var|view|virtual)$/}));function yt(e){let t={},n=e.length;for(let r=0;r{ie(),Re(),Ge(),Ye(),Ze(),Pe(),vt(),xt=/^(?[a-zA-Z$_][a-zA-Z0-9$_]*)(?(?:\[\d*?\])+?)?$/}));function Ct(e){let t=yt(e),n=[],r=e.length;for(let i=0;i{Pe(),St(),vt()}));function Tt(e){let t;if(typeof e==`string`)t=rt(e);else{let n=yt(e),r=e.length;for(let i=0;i{Re(),Pe(),St(),vt()}));function Dt(e){let t=[];if(typeof e==`string`){let n=ut(e),r=n.length;for(let e=0;e{Ge(),Pe(),St(),vt()})),kt=o((()=>{de(),le(),wt(),Et(),Ot()}));kt();function T(e,t,n){let r=e[t.name];if(typeof r==`function`)return r;let i=e[n];return typeof i==`function`?i:n=>t(e,n)}function At(e,{includeName:t=!1}={}){if(e.type!==`function`&&e.type!==`event`&&e.type!==`error`)throw new sn(e.type);return`${e.name}(${jt(e.inputs,{includeName:t})})`}function jt(e,{includeName:t=!1}={}){return e?e.map(e=>Mt(e,{includeName:t})).join(t?`, `:`,`):``}function Mt(e,{includeName:t}){return e.type.startsWith(`tuple`)?`(${jt(e.components,{includeName:t})})${e.type.slice(5)}`:e.type+(t&&e.name?` ${e.name}`:``)}var Nt=o((()=>{cn()}));function Pt(e,{strict:t=!0}={}){return!e||typeof e!=`string`?!1:t?/^0x[0-9a-fA-F]*$/.test(e):e.startsWith(`0x`)}var Ft=o((()=>{}));function It(e){return Pt(e,{strict:!1})?Math.ceil((e.length-2)/2):e.length}var Lt=o((()=>{Ft()})),Rt,zt=o((()=>{Rt=`2.47.6`}));function Bt(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause!==void 0?Bt(e.cause,t):t?null:e}var Vt,E,D=o((()=>{zt(),Vt={getDocsUrl:({docsBaseUrl:e,docsPath:t=``,docsSlug:n})=>t?`${e??`https://viem.sh`}${t}${n?`#${n}`:``}`:void 0,version:`viem@${Rt}`},E=class e extends Error{constructor(t,n={}){let r=n.cause instanceof e?n.cause.details:n.cause?.message?n.cause.message:n.details,i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=Vt.getDocsUrl?.({...n,docsPath:i}),o=[t||`An error occurred.`,``,...n.metaMessages?[...n.metaMessages,``]:[],...a?[`Docs: ${a}`]:[],...r?[`Details: ${r}`]:[],...Vt.version?[`Version: ${Vt.version}`]:[]].join(` -`);super(o,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`metaMessages`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.details=r,this.docsPath=i,this.metaMessages=n.metaMessages,this.name=n.name??this.name,this.shortMessage=t,this.version=Rt}walk(e){return Bt(this,e)}}})),Ht,Ut,Wt,Gt,Kt,qt,Jt,Yt,Xt,Zt,Qt,$t,en,tn,nn,rn,an,on,sn,cn=o((()=>{Nt(),Lt(),D(),Ht=class extends E{constructor({docsPath:e}){super([`A constructor was not found on the ABI.`,`Make sure you are using the correct ABI and that the constructor exists on it.`].join(` -`),{docsPath:e,name:`AbiConstructorNotFoundError`})}},Ut=class extends E{constructor({docsPath:e}){super(["Constructor arguments were provided (`args`), but a constructor parameters (`inputs`) were not found on the ABI.","Make sure you are using the correct ABI, and that the `inputs` attribute on the constructor exists."].join(` -`),{docsPath:e,name:`AbiConstructorParamsNotFoundError`})}},Wt=class extends E{constructor({data:e,params:t,size:n}){super([`Data size of ${n} bytes is too small for given parameters.`].join(` -`),{metaMessages:[`Params: (${jt(t,{includeName:!0})})`,`Data: ${e} (${n} bytes)`],name:`AbiDecodingDataSizeTooSmallError`}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`params`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`size`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=e,this.params=t,this.size=n}},Gt=class extends E{constructor({cause:e}={}){super(`Cannot decode zero data ("0x") with ABI parameters.`,{name:`AbiDecodingZeroDataError`,cause:e})}},Kt=class extends E{constructor({expectedLength:e,givenLength:t,type:n}){super([`ABI encoding array length mismatch for type ${n}.`,`Expected length: ${e}`,`Given length: ${t}`].join(` -`),{name:`AbiEncodingArrayLengthMismatchError`})}},qt=class extends E{constructor({expectedSize:e,value:t}){super(`Size of bytes "${t}" (bytes${It(t)}) does not match expected size (bytes${e}).`,{name:`AbiEncodingBytesSizeMismatchError`})}},Jt=class extends E{constructor({expectedLength:e,givenLength:t}){super([`ABI encoding params/values length mismatch.`,`Expected length (params): ${e}`,`Given length (values): ${t}`].join(` -`),{name:`AbiEncodingLengthMismatchError`})}},Yt=class extends E{constructor(e,{docsPath:t}){super([`Arguments (\`args\`) were provided to "${e}", but "${e}" on the ABI does not contain any parameters (\`inputs\`).`,`Cannot encode error result without knowing what the parameter types are.`,`Make sure you are using the correct ABI and that the inputs exist on it.`].join(` -`),{docsPath:t,name:`AbiErrorInputsNotFoundError`})}},Xt=class extends E{constructor(e,{docsPath:t}={}){super([`Error ${e?`"${e}" `:``}not found on ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`].join(` -`),{docsPath:t,name:`AbiErrorNotFoundError`})}},Zt=class extends E{constructor(e,{docsPath:t,cause:n}){super([`Encoded error signature "${e}" not found on ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`,`You can look up the decoded signature here: https://4byte.sourcify.dev/?q=${e}.`].join(` -`),{docsPath:t,name:`AbiErrorSignatureNotFoundError`,cause:n}),Object.defineProperty(this,`signature`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.signature=e}},Qt=class extends E{constructor(e,{docsPath:t}={}){super([`Function ${e?`"${e}" `:``}not found on ABI.`,`Make sure you are using the correct ABI and that the function exists on it.`].join(` -`),{docsPath:t,name:`AbiFunctionNotFoundError`})}},$t=class extends E{constructor(e,{docsPath:t}){super([`Function "${e}" does not contain any \`outputs\` on ABI.`,`Cannot decode function result without knowing what the parameter types are.`,`Make sure you are using the correct ABI and that the function exists on it.`].join(` -`),{docsPath:t,name:`AbiFunctionOutputsNotFoundError`})}},en=class extends E{constructor(e,{docsPath:t}){super([`Encoded function signature "${e}" not found on ABI.`,`Make sure you are using the correct ABI and that the function exists on it.`,`You can look up the signature here: https://4byte.sourcify.dev/?q=${e}.`].join(` -`),{docsPath:t,name:`AbiFunctionSignatureNotFoundError`})}},tn=class extends E{constructor(e,t){super(`Found ambiguous types in overloaded ABI items.`,{metaMessages:[`\`${e.type}\` in \`${At(e.abiItem)}\`, and`,`\`${t.type}\` in \`${At(t.abiItem)}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`],name:`AbiItemAmbiguityError`})}},nn=class extends E{constructor({expectedSize:e,givenSize:t}){super(`Expected bytes${e}, got bytes${t}.`,{name:`BytesSizeMismatchError`})}},rn=class extends E{constructor(e,{docsPath:t}){super([`Type "${e}" is not a valid encoding type.`,`Please provide a valid ABI type.`].join(` -`),{docsPath:t,name:`InvalidAbiEncodingType`})}},an=class extends E{constructor(e,{docsPath:t}){super([`Type "${e}" is not a valid decoding type.`,`Please provide a valid ABI type.`].join(` -`),{docsPath:t,name:`InvalidAbiDecodingType`})}},on=class extends E{constructor(e){super([`Value "${e}" is not a valid array.`].join(` -`),{name:`InvalidArrayError`})}},sn=class extends E{constructor(e){super([`"${e}" is not a valid definition type.`,`Valid types: "function", "event", "error"`].join(` -`),{name:`InvalidDefinitionTypeError`})}}})),ln,un,dn,fn=o((()=>{D(),ln=class extends E{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset "${e}" is out-of-bounds (size: ${n}).`,{name:`SliceOffsetOutOfBoundsError`})}},un=class extends E{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (${e}) exceeds padding size (${t}).`,{name:`SizeExceedsPaddingSizeError`})}},dn=class extends E{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} is expected to be ${t} ${n} long, but is ${e} ${n} long.`,{name:`InvalidBytesLengthError`})}}}));function pn(e,{dir:t,size:n=32}={}){return typeof e==`string`?mn(e,{dir:t,size:n}):hn(e,{dir:t,size:n})}function mn(e,{dir:t,size:n=32}={}){if(n===null)return e;let r=e.replace(`0x`,``);if(r.length>n*2)throw new un({size:Math.ceil(r.length/2),targetSize:n,type:`hex`});return`0x${r[t===`right`?`padEnd`:`padStart`](n*2,`0`)}`}function hn(e,{dir:t,size:n=32}={}){if(n===null)return e;if(e.length>n)throw new un({size:e.length,targetSize:n,type:`bytes`});let r=new Uint8Array(n);for(let i=0;i{fn()})),_n,vn,yn,bn,xn=o((()=>{D(),_n=class extends E{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number "${i}" is not in safe ${r?`${r*8}-bit ${n?`signed`:`unsigned`} `:``}integer range ${e?`(${t} to ${e})`:`(above ${t})`}`,{name:`IntegerOutOfRangeError`})}},vn=class extends E{constructor(e){super(`Bytes value "${e}" is not a valid boolean. The bytes array must contain a single byte of either a 0 or 1 value.`,{name:`InvalidBytesBooleanError`})}},yn=class extends E{constructor(e){super(`Hex value "${e}" is not a valid boolean. The hex value must be "0x0" (false) or "0x1" (true).`,{name:`InvalidHexBooleanError`})}},bn=class extends E{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed ${t} bytes. Given size: ${e} bytes.`,{name:`SizeOverflowError`})}}}));function Sn(e,{dir:t=`left`}={}){let n=typeof e==`string`?e.replace(`0x`,``):e,r=0;for(let e=0;e{}));function wn(e,{size:t}){if(It(e)>t)throw new bn({givenSize:It(e),maxSize:t})}function Tn(e,t={}){let{signed:n}=t;t.size&&wn(e,{size:t.size});let r=BigInt(e);if(!n)return r;let i=(e.length-2)/2;return r<=(1n<{xn(),Lt(),Cn()}));function kn(e,t={}){return typeof e==`number`||typeof e==`bigint`?O(e,t):typeof e==`string`?Mn(e,t):typeof e==`boolean`?An(e,t):jn(e,t)}function An(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(wn(n,{size:t.size}),pn(n,{size:t.size})):n}function jn(e,t={}){let n=``;for(let t=0;ta||i{xn(),gn(),On(),Nn=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),Pn=new TextEncoder}));function Fn(e,t={}){return typeof e==`number`||typeof e==`bigint`?zn(e,t):typeof e==`boolean`?In(e,t):Pt(e)?Rn(e,t):Bn(e,t)}function In(e,t={}){let n=new Uint8Array(1);return n[0]=Number(e),typeof t.size==`number`?(wn(n,{size:t.size}),pn(n,{size:t.size})):n}function Ln(e){if(e>=Hn.zero&&e<=Hn.nine)return e-Hn.zero;if(e>=Hn.A&&e<=Hn.F)return e-(Hn.A-10);if(e>=Hn.a&&e<=Hn.f)return e-(Hn.a-10)}function Rn(e,t={}){let n=e;t.size&&(wn(n,{size:t.size}),n=pn(n,{dir:`right`,size:t.size}));let r=n.slice(2);r.length%2&&(r=`0${r}`);let i=r.length/2,a=new Uint8Array(i);for(let e=0,t=0;e{D(),Ft(),gn(),On(),k(),Vn=new TextEncoder,Hn={zero:48,nine:57,A:65,F:70,a:97,f:102}}));function Wn(e,t=!1){return t?{h:Number(e&qn),l:Number(e>>Jn&qn)}:{h:Number(e>>Jn&qn)|0,l:Number(e&qn)|0}}function Gn(e,t=!1){let n=e.length,r=new Uint32Array(n),i=new Uint32Array(n);for(let a=0;a>>0)+(r>>>0);return{h:e+n+(i/2**32|0)|0,l:i|0}}var qn,Jn,Yn,Xn,Zn,Qn,$n,er,tr,nr,rr,ir,ar,or,sr,cr,lr,ur,dr=o((()=>{qn=BigInt(2**32-1),Jn=BigInt(32),Yn=(e,t,n)=>e>>>n,Xn=(e,t,n)=>e<<32-n|t>>>n,Zn=(e,t,n)=>e>>>n|t<<32-n,Qn=(e,t,n)=>e<<32-n|t>>>n,$n=(e,t,n)=>e<<64-n|t>>>n-32,er=(e,t,n)=>e>>>n-32|t<<64-n,tr=(e,t,n)=>e<>>32-n,nr=(e,t,n)=>t<>>32-n,rr=(e,t,n)=>t<>>64-n,ir=(e,t,n)=>e<>>64-n,ar=(e,t,n)=>(e>>>0)+(t>>>0)+(n>>>0),or=(e,t,n,r)=>t+n+r+(e/2**32|0)|0,sr=(e,t,n,r)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0),cr=(e,t,n,r,i)=>t+n+r+i+(e/2**32|0)|0,lr=(e,t,n,r,i)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0)+(i>>>0),ur=(e,t,n,r,i,a)=>t+n+r+i+a+(e/2**32|0)|0})),fr,pr=o((()=>{fr=typeof globalThis==`object`&&`crypto`in globalThis?globalThis.crypto:void 0}));function mr(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`}function hr(e){if(!Number.isSafeInteger(e)||e<0)throw Error(`positive integer expected, got `+e)}function gr(e,...t){if(!mr(e))throw Error(`Uint8Array expected`);if(t.length>0&&!t.includes(e.length))throw Error(`Uint8Array expected of length `+t+`, got length=`+e.length)}function _r(e){if(typeof e!=`function`||typeof e.create!=`function`)throw Error(`Hash should be wrapped by utils.createHasher`);hr(e.outputLen),hr(e.blockLen)}function vr(e,t=!0){if(e.destroyed)throw Error(`Hash instance has been destroyed`);if(t&&e.finished)throw Error(`Hash#digest() has already been called`)}function yr(e,t){gr(e);let n=t.outputLen;if(e.length>>t}function wr(e){return e<<24&4278190080|e<<8&16711680|e>>>8&65280|e>>>24&255}function Tr(e){for(let t=0;te().update(Dr(t)).digest(),n=e();return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=()=>e(),t}function Ar(e){let t=(t,n)=>e(n).update(Dr(t)).digest(),n=e({});return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=t=>e(t),t}function jr(e=32){if(fr&&typeof fr.getRandomValues==`function`)return fr.getRandomValues(new Uint8Array(e));if(fr&&typeof fr.randomBytes==`function`)return Uint8Array.from(fr.randomBytes(e));throw Error(`crypto.getRandomValues must be defined`)}var Mr,Nr,Pr,Fr=o((()=>{pr(),Mr=new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68,Nr=Mr?e=>e:Tr,typeof Uint8Array.from([]).toHex==`function`&&Uint8Array.fromHex,Pr=class{}}));function Ir(e,t=24){let n=new Uint32Array(10);for(let r=24-t;r<24;r++){for(let t=0;t<10;t++)n[t]=e[t]^e[t+10]^e[t+20]^e[t+30]^e[t+40];for(let t=0;t<10;t+=2){let r=(t+8)%10,i=(t+2)%10,a=n[i],o=n[i+1],s=Yr(a,o,1)^n[r],c=Xr(a,o,1)^n[r+1];for(let n=0;n<50;n+=10)e[t+n]^=s,e[t+n+1]^=c}let t=e[2],i=e[3];for(let n=0;n<24;n++){let r=Wr[n],a=Yr(t,i,r),o=Xr(t,i,r),s=Ur[n];t=e[s],i=e[s+1],e[s]=a,e[s+1]=o}for(let t=0;t<50;t+=10){for(let r=0;r<10;r++)n[r]=e[t+r];for(let r=0;r<10;r++)e[t+r]^=~n[(r+2)%10]&n[(r+4)%10]}e[0]^=qr[r],e[1]^=Jr[r]}xr(n)}var Lr,Rr,zr,Br,Vr,Hr,Ur,Wr,Gr,Kr,qr,Jr,Yr,Xr,Zr,Qr,$r,ei,ti=o((()=>{dr(),Fr(),Lr=BigInt(0),Rr=BigInt(1),zr=BigInt(2),Br=BigInt(7),Vr=BigInt(256),Hr=BigInt(113),Ur=[],Wr=[],Gr=[];for(let e=0,t=Rr,n=1,r=0;e<24;e++){[n,r]=[r,(2*n+3*r)%5],Ur.push(2*(5*r+n)),Wr.push((e+1)*(e+2)/2%64);let i=Lr;for(let e=0;e<7;e++)t=(t<>Br)*Hr)%Vr,t&zr&&(i^=Rr<<(Rr<n>32?rr(e,t,n):tr(e,t,n),Xr=(e,t,n)=>n>32?ir(e,t,n):nr(e,t,n),Zr=class e extends Pr{constructor(e,t,n,r=!1,i=24){if(super(),this.pos=0,this.posOut=0,this.finished=!1,this.destroyed=!1,this.enableXOF=!1,this.blockLen=e,this.suffix=t,this.outputLen=n,this.enableXOF=r,this.rounds=i,hr(n),!(0=n&&this.keccak();let a=Math.min(n-this.posOut,i-r);e.set(t.subarray(this.posOut,this.posOut+a),r),this.posOut+=a,r+=a}return e}xofInto(e){if(!this.enableXOF)throw Error(`XOF is not possible for this instance`);return this.writeInto(e)}xof(e){return hr(e),this.xofInto(new Uint8Array(e))}digestInto(e){if(yr(e,this),this.finished)throw Error(`digest() was already called`);return this.writeInto(e),this.destroy(),e}digest(){return this.digestInto(new Uint8Array(this.outputLen))}destroy(){this.destroyed=!0,xr(this.state)}_cloneInto(t){let{blockLen:n,suffix:r,outputLen:i,rounds:a,enableXOF:o}=this;return t||=new e(n,r,i,o,a),t.state32.set(this.state32),t.pos=this.pos,t.posOut=this.posOut,t.finished=this.finished,t.rounds=a,t.suffix=r,t.outputLen=i,t.enableXOF=o,t.destroyed=this.destroyed,t}},Qr=(e,t,n)=>kr(()=>new Zr(t,e,n)),Qr(6,144,224/8),Qr(6,136,256/8),Qr(6,104,384/8),Qr(6,72,512/8),Qr(1,144,224/8),$r=Qr(1,136,256/8),Qr(1,104,384/8),Qr(1,72,512/8),ei=(e,t,n)=>Ar((r={})=>new Zr(t,e,r.dkLen===void 0?n:r.dkLen,!0)),ei(31,168,128/8),ei(31,136,256/8)}));function ni(e,t){let n=t||`hex`,r=$r(Pt(e,{strict:!1})?Fn(e):e);return n===`bytes`?r:kn(r)}var ri=o((()=>{ti(),Ft(),Un(),k()}));function ii(e){return ai(e)}var ai,oi=o((()=>{Un(),ri(),ai=e=>ni(Fn(e))}));function si(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;o{D()})),li,ui=o((()=>{ci(),li=e=>si(typeof e==`string`?e:ue(e))}));function di(e){return ii(li(e))}var fi=o((()=>{oi(),ui()})),pi,mi=o((()=>{fi(),pi=di})),hi,gi=o((()=>{D(),hi=class extends E{constructor({address:e}){super(`Address "${e}" is invalid.`,{metaMessages:[`- Address must be a hex value of 20 bytes (40 hex characters).`,`- Address must match its checksum counterpart.`],name:`InvalidAddressError`})}}})),_i,vi=o((()=>{_i=class extends Map{constructor(e){super(),Object.defineProperty(this,`maxSize`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&(super.delete(e),super.set(e,t)),t}set(e,t){if(super.has(e)&&super.delete(e),super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=super.keys().next().value;e!==void 0&&super.delete(e)}return this}}}));function yi(e,t){if(xi.has(`${e}.${t}`))return xi.get(`${e}.${t}`);let n=t?`${t}${e.toLowerCase()}`:e.substring(2).toLowerCase(),r=ni(Bn(n),`bytes`),i=(t?n.substring(`${t}0x`.length):n).split(``);for(let e=0;e<40;e+=2)r[e>>1]>>4>=8&&i[e]&&(i[e]=i[e].toUpperCase()),(r[e>>1]&15)>=8&&i[e+1]&&(i[e+1]=i[e+1].toUpperCase());let a=`0x${i.join(``)}`;return xi.set(`${e}.${t}`,a),a}function bi(e,t){if(!Ci(e,{strict:!1}))throw new hi({address:e});return yi(e,t)}var xi,Si=o((()=>{gi(),Un(),ri(),vi(),Ei(),xi=new _i(8192)}));function Ci(e,t){let{strict:n=!0}=t??{},r=`${e}.${n}`;if(Ti.has(r))return Ti.get(r);let i=wi.test(e)?e.toLowerCase()===e?!0:n?yi(e)===e:!0:!1;return Ti.set(r,i),i}var wi,Ti,Ei=o((()=>{vi(),Si(),wi=/^0x[a-fA-F0-9]{40}$/,Ti=new _i(8192)}));function Di(e){return typeof e[0]==`string`?ki(e):Oi(e)}function Oi(e){let t=0;for(let n of e)t+=n.length;let n=new Uint8Array(t),r=0;for(let t of e)n.set(t,r),r+=t.length;return n}function ki(e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}var Ai=o((()=>{}));function ji(e,t,n,{strict:r}={}){return Pt(e,{strict:!1})?Fi(e,t,n,{strict:r}):Pi(e,t,n,{strict:r})}function Mi(e,t){if(typeof t==`number`&&t>0&&t>It(e)-1)throw new ln({offset:t,position:`start`,size:It(e)})}function Ni(e,t,n){if(typeof t==`number`&&typeof n==`number`&&It(e)!==n-t)throw new ln({offset:n,position:`end`,size:It(e)})}function Pi(e,t,n,{strict:r}={}){Mi(e,t);let i=e.slice(t,n);return r&&Ni(i,t,n),i}function Fi(e,t,n,{strict:r}={}){Mi(e,t);let i=`0x${e.replace(`0x`,``).slice((t??0)*2,(n??e.length)*2)}`;return r&&Ni(i,t,n),i}var Ii=o((()=>{fn(),Ft(),Lt()})),Li,Ri,zi=o((()=>{Li=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,Ri=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/}));function Bi(e,t){if(e.length!==t.length)throw new Jt({expectedLength:e.length,givenLength:t.length});let n=Ui(Vi({params:e,values:t}));return n.length===0?`0x`:n}function Vi({params:e,values:t}){let n=[];for(let r=0;r0?Di([t,e]):t}}if(i)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:Di(a.map(({encoded:e})=>e))}}function Ki(e,{param:t}){let[,n]=t.type.split(`bytes`),r=It(e);if(!n){let t=e;return r%32!=0&&(t=mn(t,{dir:`right`,size:Math.ceil((e.length-2)/2/32)*32})),{dynamic:!0,encoded:Di([mn(O(r,{size:32})),t])}}if(r!==Number.parseInt(n,10))throw new qt({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:mn(e,{dir:`right`})}}function qi(e){if(typeof e!=`boolean`)throw new E(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:mn(An(e))}}function Ji(e,{signed:t,size:n=256}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function Zi(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}var Qi=o((()=>{cn(),gi(),D(),xn(),Ei(),Ai(),gn(),Lt(),Ii(),k(),zi()})),$i,ea=o((()=>{Ii(),fi(),$i=e=>ji(di(e),0,4)}));function ta(e){let{abi:t,args:n=[],name:r}=e,i=Pt(r,{strict:!1}),a=t.filter(e=>i?e.type===`function`?$i(e)===r:e.type===`event`?pi(e)===r:!1:`name`in e&&e.name===r);if(a.length===0)return;if(a.length===1)return a[0];let o;for(let e of a)if(`inputs`in e){if(!n||n.length===0){if(!e.inputs||e.inputs.length===0)return e;continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===n.length&&n.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?na(t,r):!1})){if(o&&`inputs`in o&&o.inputs){let t=ra(e.inputs,o.inputs,n);if(t)throw new tn({abiItem:e,type:t[0]},{abiItem:o,type:t[1]})}o=e}}return o||a[0]}function na(e,t){let n=typeof e,r=t.type;switch(r){case`address`:return Ci(e,{strict:!1});case`bool`:return n===`boolean`;case`function`:return n===`string`;case`string`:return n===`string`;default:return r===`tuple`&&`components`in t?Object.values(t.components).every((t,r)=>n===`object`&&na(Object.values(e)[r],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>na(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function ra(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return ra(i.components,a.components,n[r]);let o=[i.type,a.type];if(o.includes(`address`)&&o.includes(`bytes20`)||(o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`))&&Ci(n[r],{strict:!1}))return o}}var ia=o((()=>{cn(),Ft(),Ei(),mi(),ea()}));function aa(e){return typeof e==`string`?{address:e,type:`json-rpc`}:e}var oa=o((()=>{}));function sa(e){let{abi:t,args:n,functionName:r}=e,i=t[0];if(r){let e=ta({abi:t,args:n,name:r});if(!e)throw new Qt(r,{docsPath:ca});i=e}if(i.type!==`function`)throw new Qt(void 0,{docsPath:ca});return{abi:[i],functionName:$i(At(i))}}var ca,la=o((()=>{cn(),ea(),Nt(),ia(),ca=`/docs/contract/encodeFunctionData`}));function ua(e){let{args:t}=e,{abi:n,functionName:r}=e.abi.length===1&&e.functionName?.startsWith(`0x`)?e:sa(e),i=n[0];return ki([r,(`inputs`in i&&i.inputs?Bi(i.inputs,t??[]):void 0)??`0x`])}var da=o((()=>{Ai(),Qi(),la()})),fa,pa,ma,ha=o((()=>{fa={1:"An `assert` condition failed.",17:`Arithmetic operation resulted in underflow or overflow.`,18:"Division or modulo by zero (e.g. `5 / 0` or `23 % 0`).",33:`Attempted to convert to an invalid type.`,34:`Attempted to access a storage byte array that is incorrectly encoded.`,49:"Performed `.pop()` on an empty array",50:`Array index is out of bounds.`,65:`Allocated too much memory or created an array which is too large.`,81:`Attempted to call a zero-initialized variable of internal function type.`},pa={inputs:[{name:`message`,type:`string`}],name:`Error`,type:`error`},ma={inputs:[{name:`reason`,type:`uint256`}],name:`Panic`,type:`error`}})),ga,_a,va,ya=o((()=>{D(),ga=class extends E{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`,{name:`NegativeOffsetError`})}},_a=class extends E{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`,{name:`PositionOutOfBoundsError`})}},va=class extends E{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`,{name:`RecursiveReadLimitExceededError`})}}}));function ba(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(xa);return n.bytes=e,n.dataView=new DataView(e.buffer??e,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var xa,Sa=o((()=>{ya(),xa={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new va({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new _a({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new ga({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new ga({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}}}));function Ca(e,t={}){return t.size!==void 0&&wn(e,{size:t.size}),Tn(jn(e,t),t)}function wa(e,t={}){let n=e;if(t.size!==void 0&&(wn(n,{size:t.size}),n=Sn(n)),n.length>1||n[0]>1)throw new vn(n);return!!n[0]}function Ta(e,t={}){return t.size!==void 0&&wn(e,{size:t.size}),Dn(jn(e,t),t)}function Ea(e,t={}){let n=e;return t.size!==void 0&&(wn(n,{size:t.size}),n=Sn(n,{dir:`right`})),new TextDecoder().decode(n)}var Da=o((()=>{xn(),Cn(),On(),k()}));function Oa(e,t){let n=typeof t==`string`?Rn(t):t,r=ba(n);if(It(n)===0&&e.length>0)throw new Gt;if(It(t)&&It(t)<32)throw new Wt({data:typeof t==`string`?t:jn(t),params:e,size:It(t)});let i=0,a=[];for(let t=0;t48?Ca(i,{signed:n}):Ta(i,{signed:n}),32]}function Fa(e,t,{staticPosition:n}){let r=t.components.length===0||t.components.some(({name:e})=>!e),i=r?[]:{},a=0;if(La(t)){let o=n+Ta(e.readBytes(za));for(let n=0;n{cn(),Si(),Sa(),Lt(),Ii(),Cn(),Da(),Un(),k(),Qi(),Ra=32,za=32}));function Va(e){let{abi:t,data:n,cause:r}=e,i=ji(n,0,4);if(i===`0x`)throw new Gt({cause:r});let a=[...t||[],pa,ma].find(e=>e.type===`error`&&i===$i(At(e)));if(!a)throw new Zt(i,{docsPath:`/docs/contract/decodeErrorResult`,cause:r});return{abiItem:a,args:`inputs`in a&&a.inputs&&a.inputs.length>0?Oa(a.inputs,ji(n,4)):void 0,errorName:a.name}}var Ha=o((()=>{ha(),cn(),Ii(),ea(),Ba(),Nt()})),Ua,Wa=o((()=>{Ua=(e,t,n)=>JSON.stringify(e,(e,n)=>{let r=typeof n==`bigint`?n.toString():n;return typeof t==`function`?t(e,r):r},n)}));function Ga({abiItem:e,args:t,includeFunctionName:n=!0,includeName:r=!1}){if(`name`in e&&`inputs`in e&&e.inputs)return`${n?e.name:``}(${e.inputs.map((e,n)=>`${r&&e.name?`${e.name}: `:``}${typeof t[n]==`object`?Ua(t[n]):t[n]}`).join(`, `)})`}var Ka=o((()=>{Wa()})),qa,Ja,Ya=o((()=>{qa={gwei:9,wei:18},Ja={ether:-9,wei:9}}));function Xa(e,t){let n=e.toString(),r=n.startsWith(`-`);r&&(n=n.slice(1)),n=n.padStart(t,`0`);let[i,a]=[n.slice(0,n.length-t),n.slice(n.length-t)];return a=a.replace(/(0+)$/,``),`${r?`-`:``}${i||`0`}${a?`.${a}`:``}`}var Za=o((()=>{}));function Qa(e,t=`wei`){return Xa(e,qa[t])}var $a=o((()=>{Ya(),Za()}));function eo(e,t=`wei`){return Xa(e,Ja[t])}var to=o((()=>{Ya(),Za()}));function no(e){return e.reduce((e,{slot:t,value:n})=>`${e} ${t}: ${n}\n`,``)}function ro(e){return e.reduce((e,{address:t,...n})=>{let r=`${e} ${t}:\n`;return n.nonce&&(r+=` nonce: ${n.nonce}\n`),n.balance&&(r+=` balance: ${n.balance}\n`),n.code&&(r+=` code: ${n.code}\n`),n.state&&(r+=` state: -`,r+=no(n.state)),n.stateDiff&&(r+=` stateDiff: -`,r+=no(n.stateDiff)),r},` State Override: -`).slice(0,-1)}var io,ao,oo=o((()=>{D(),io=class extends E{constructor({address:e}){super(`State for account "${e}" is set multiple times.`,{name:`AccountStateConflictError`})}},ao=class extends E{constructor(){super(`state and stateDiff are set on the same account.`,{name:`StateAssignmentConflictError`})}}}));function so(e){let t=Object.entries(e).map(([e,t])=>t===void 0||t===!1?null:[e,t]).filter(Boolean),n=t.reduce((e,[t])=>Math.max(e,t.length),0);return t.map(([e,t])=>` ${`${e}:`.padEnd(n+1)} ${t}`).join(` -`)}var co,lo,uo,fo,po,mo,ho,go,_o=o((()=>{$a(),to(),D(),co=class extends E{constructor({v:e}){super(`Invalid \`v\` value "${e}". Expected 27 or 28.`,{name:`InvalidLegacyVError`})}},lo=class extends E{constructor({transaction:e}){super(`Cannot infer a transaction type from provided transaction.`,{metaMessages:[`Provided Transaction:`,`{`,so(e),`}`,``,`To infer the type, either provide:`,"- a `type` to the Transaction, or","- an EIP-1559 Transaction with `maxFeePerGas`, or","- an EIP-2930 Transaction with `gasPrice` & `accessList`, or","- an EIP-4844 Transaction with `blobs`, `blobVersionedHashes`, `sidecars`, or","- an EIP-7702 Transaction with `authorizationList`, or","- a Legacy Transaction with `gasPrice`"],name:`InvalidSerializableTransactionError`})}},uo=class extends E{constructor({storageKey:e}){super(`Size for storage key "${e}" is invalid. Expected 32 bytes. Got ${Math.floor((e.length-2)/2)} bytes.`,{name:`InvalidStorageKeySizeError`})}},fo=class extends E{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d}){let f=so({chain:r&&`${r?.name} (id: ${r?.id})`,from:t?.address,to:u,value:d!==void 0&&`${Qa(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${eo(o)} gwei`,maxFeePerGas:s!==void 0&&`${eo(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${eo(c)} gwei`,nonce:l});super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Request Arguments:`,f].filter(Boolean),name:`TransactionExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},po=class extends E{constructor({blockHash:e,blockNumber:t,blockTag:n,hash:r,index:i}){let a=`Transaction`;n&&i!==void 0&&(a=`Transaction at block time "${n}" at index "${i}"`),e&&i!==void 0&&(a=`Transaction at block hash "${e}" at index "${i}"`),t&&i!==void 0&&(a=`Transaction at block number "${t}" at index "${i}"`),r&&(a=`Transaction with hash "${r}"`),super(`${a} could not be found.`,{name:`TransactionNotFoundError`})}},mo=class extends E{constructor({hash:e}){super(`Transaction receipt with hash "${e}" could not be found. The Transaction may not be processed on a block yet.`,{name:`TransactionReceiptNotFoundError`})}},ho=class extends E{constructor({receipt:e}){super(`Transaction with hash "${e.transactionHash}" reverted.`,{metaMessages:[`The receipt marked the transaction as "reverted". This could mean that the function on the contract you are trying to call threw an error.`,` `,`You can attempt to extract the revert reason by:`,"- calling the `simulateContract` or `simulateCalls` Action with the `abi` and `functionName` of the contract","- using the `call` Action with raw `data`"],name:`TransactionReceiptRevertedError`}),Object.defineProperty(this,`receipt`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.receipt=e}},go=class extends E{constructor({hash:e}){super(`Timed out while waiting for transaction with hash "${e}" to be confirmed.`,{name:`WaitForTransactionReceiptTimeoutError`})}}})),vo,yo,bo=o((()=>{vo=e=>e,yo=e=>e})),xo,So,Co,A,wo,To,Eo=o((()=>{oa(),ha(),Ha(),Nt(),Ka(),ia(),$a(),to(),cn(),D(),oo(),_o(),bo(),xo=class extends E{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d,stateOverride:f}){let p=so({from:(t?aa(t):void 0)?.address,to:u,value:d!==void 0&&`${Qa(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${eo(o)} gwei`,maxFeePerGas:s!==void 0&&`${eo(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${eo(c)} gwei`,nonce:l});f&&(p+=`\n${ro(f)}`),super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Raw Call Arguments:`,p].filter(Boolean),name:`CallExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},So=class extends E{constructor(e,{abi:t,args:n,contractAddress:r,docsPath:i,functionName:a,sender:o}){let s=ta({abi:t,args:n,name:a}),c=s?Ga({abiItem:s,args:n,includeFunctionName:!1,includeName:!1}):void 0,l=s?At(s,{includeName:!0}):void 0,u=so({address:r&&vo(r),function:l,args:c&&c!==`()`&&`${[...Array(a?.length??0).keys()].map(()=>` `).join(``)}${c}`,sender:o});super(e.shortMessage||`An unknown error occurred while executing the contract function "${a}".`,{cause:e,docsPath:i,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],u&&`Contract Call:`,u].filter(Boolean),name:`ContractFunctionExecutionError`}),Object.defineProperty(this,`abi`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`args`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`contractAddress`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`formattedArgs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`functionName`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`sender`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.abi=t,this.args=n,this.cause=e,this.contractAddress=r,this.functionName=a,this.sender=o}},Co=class extends E{constructor({abi:e,data:t,functionName:n,message:r,cause:i}){let a,o,s,c;if(t&&t!==`0x`)try{o=Va({abi:e,data:t,cause:i});let{abiItem:n,errorName:r,args:a}=o;if(r===`Error`)c=a[0];else if(r===`Panic`){let[e]=a;c=fa[e]}else{let e=n?At(n,{includeName:!0}):void 0,t=n&&a?Ga({abiItem:n,args:a,includeFunctionName:!1,includeName:!1}):void 0;s=[e?`Error: ${e}`:``,t&&t!==`()`?` ${[...Array(r?.length??0).keys()].map(()=>` `).join(``)}${t}`:``]}}catch(e){a=e}else r&&(c=r);let l;a instanceof Zt&&(l=a.signature,s=[`Unable to decode signature "${l}" as it was not found on the provided ABI.`,`Make sure you are using the correct ABI and that the error exists on it.`,`You can look up the decoded signature here: https://4byte.sourcify.dev/?q=${l}.`]),super(c&&c!==`execution reverted`||l?[`The contract function "${n}" reverted with the following ${l?`signature`:`reason`}:`,c||l].join(` -`):`The contract function "${n}" reverted.`,{cause:a??i,metaMessages:s,name:`ContractFunctionRevertedError`}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`raw`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`reason`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`signature`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=o,this.raw=t,this.reason=c,this.signature=l}},A=class extends E{constructor({functionName:e,cause:t}){super(`The contract function "${e}" returned no data ("0x").`,{metaMessages:[`This could be due to any of the following:`,` - The contract does not have the function "${e}",`,` - The parameters passed to the contract function may be invalid, or`,` - The address is not a contract.`],name:`ContractFunctionZeroDataError`,cause:t})}},wo=class extends E{constructor({factory:e}){super(`Deployment for counterfactual contract call failed${e?` for factory "${e}".`:``}`,{metaMessages:[`Please ensure:`,"- The `factory` is a valid contract deployment factory (ie. Create2 Factory, ERC-4337 Factory, etc).","- The `factoryData` is a valid encoded function call for contract deployment function on the factory."],name:`CounterfactualDeploymentFailedError`})}},To=class extends E{constructor({data:e,message:t}){super(t||``,{name:`RawContractError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:3}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=e}}})),Do,Oo,ko,Ao=o((()=>{Wa(),D(),bo(),Do=class extends E{constructor({body:e,cause:t,details:n,headers:r,status:i,url:a}){super(`HTTP request failed.`,{cause:t,details:n,metaMessages:[i&&`Status: ${i}`,`URL: ${yo(a)}`,e&&`Request body: ${Ua(e)}`].filter(Boolean),name:`HttpRequestError`}),Object.defineProperty(this,`body`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`headers`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`status`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.body=e,this.headers=r,this.status=i,this.url=a}},Oo=class extends E{constructor({body:e,error:t,url:n}){super(`RPC Request failed.`,{cause:t,details:t.message,metaMessages:[`URL: ${yo(n)}`,`Request body: ${Ua(e)}`],name:`RpcRequestError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.code=t.code,this.data=t.data,this.url=n}},ko=class extends E{constructor({body:e,url:t}){super(`The request took too long to respond.`,{details:`The request timed out.`,metaMessages:[`URL: ${yo(t)}`,`Request body: ${Ua(e)}`],name:`TimeoutError`}),Object.defineProperty(this,`url`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.url=t}}})),jo,Mo,No,Po,Fo,Io,Lo,Ro,zo,Bo,Vo,Ho,Uo,Wo,Go,Ko,qo,Jo,Yo,Xo,Zo,Qo,$o,es,ts,ns,rs,is,as,os,ss=o((()=>{D(),Ao(),jo=-1,Mo=class extends E{constructor(e,{code:t,docsPath:n,metaMessages:r,name:i,shortMessage:a}){super(a,{cause:e,docsPath:n,metaMessages:r||e?.metaMessages,name:i||`RpcError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.name=i||e.name,this.code=e instanceof Oo?e.code:t??jo}},No=class extends Mo{constructor(e,t){super(e,t),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.data=t.data}},Po=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ParseRpcError`,shortMessage:`Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.`})}},Object.defineProperty(Po,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700}),Fo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InvalidRequestRpcError`,shortMessage:`JSON is not a valid request object.`})}},Object.defineProperty(Fo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600}),Io=class e extends Mo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`MethodNotFoundRpcError`,shortMessage:`The method${n?` "${n}"`:``} does not exist / is not available.`})}},Object.defineProperty(Io,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601}),Lo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InvalidParamsRpcError`,shortMessage:[`Invalid parameters were provided to the RPC method.`,`Double check you have provided the correct parameters.`].join(` -`)})}},Object.defineProperty(Lo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602}),Ro=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InternalRpcError`,shortMessage:`An internal error was received.`})}},Object.defineProperty(Ro,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603}),zo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`InvalidInputRpcError`,shortMessage:[`Missing or invalid parameters.`,`Double check you have provided the correct parameters.`].join(` -`)})}},Object.defineProperty(zo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3}),Bo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ResourceNotFoundRpcError`,shortMessage:`Requested resource not found.`}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`ResourceNotFoundRpcError`})}},Object.defineProperty(Bo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001}),Vo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`ResourceUnavailableRpcError`,shortMessage:`Requested resource not available.`})}},Object.defineProperty(Vo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002}),Ho=class e extends Mo{constructor(t){super(t,{code:e.code,name:`TransactionRejectedRpcError`,shortMessage:`Transaction creation failed.`})}},Object.defineProperty(Ho,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003}),Uo=class e extends Mo{constructor(t,{method:n}={}){super(t,{code:e.code,name:`MethodNotSupportedRpcError`,shortMessage:`Method${n?` "${n}"`:``} is not supported.`})}},Object.defineProperty(Uo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004}),Wo=class e extends Mo{constructor(t){super(t,{code:e.code,name:`LimitExceededRpcError`,shortMessage:`Request exceeds defined limit.`})}},Object.defineProperty(Wo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005}),Go=class e extends Mo{constructor(t){super(t,{code:e.code,name:`JsonRpcVersionUnsupportedError`,shortMessage:`Version of JSON-RPC protocol is not supported.`})}},Object.defineProperty(Go,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006}),Ko=class e extends No{constructor(t){super(t,{code:e.code,name:`UserRejectedRequestError`,shortMessage:`User rejected the request.`})}},Object.defineProperty(Ko,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001}),qo=class e extends No{constructor(t){super(t,{code:e.code,name:`UnauthorizedProviderError`,shortMessage:`The requested method and/or account has not been authorized by the user.`})}},Object.defineProperty(qo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100}),Jo=class e extends No{constructor(t,{method:n}={}){super(t,{code:e.code,name:`UnsupportedProviderMethodError`,shortMessage:`The Provider does not support the requested method${n?` " ${n}"`:``}.`})}},Object.defineProperty(Jo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200}),Yo=class e extends No{constructor(t){super(t,{code:e.code,name:`ProviderDisconnectedError`,shortMessage:`The Provider is disconnected from all chains.`})}},Object.defineProperty(Yo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900}),Xo=class e extends No{constructor(t){super(t,{code:e.code,name:`ChainDisconnectedError`,shortMessage:`The Provider is not connected to the requested chain.`})}},Object.defineProperty(Xo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901}),Zo=class e extends No{constructor(t){super(t,{code:e.code,name:`SwitchChainError`,shortMessage:`An error occurred when attempting to switch chain.`})}},Object.defineProperty(Zo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902}),Qo=class e extends No{constructor(t){super(t,{code:e.code,name:`UnsupportedNonOptionalCapabilityError`,shortMessage:`This Wallet does not support a capability that was not marked as optional.`})}},Object.defineProperty(Qo,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700}),$o=class e extends No{constructor(t){super(t,{code:e.code,name:`UnsupportedChainIdError`,shortMessage:`This Wallet does not support the requested chain ID.`})}},Object.defineProperty($o,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710}),es=class e extends No{constructor(t){super(t,{code:e.code,name:`DuplicateIdError`,shortMessage:`There is already a bundle submitted with this ID.`})}},Object.defineProperty(es,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720}),ts=class e extends No{constructor(t){super(t,{code:e.code,name:`UnknownBundleIdError`,shortMessage:`This bundle id is unknown / has not been submitted`})}},Object.defineProperty(ts,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730}),ns=class e extends No{constructor(t){super(t,{code:e.code,name:`BundleTooLargeError`,shortMessage:`The call bundle is too large for the Wallet to process.`})}},Object.defineProperty(ns,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740}),rs=class e extends No{constructor(t){super(t,{code:e.code,name:`AtomicReadyWalletRejectedUpgradeError`,shortMessage:`The Wallet can support atomicity after an upgrade, but the user rejected the upgrade.`})}},Object.defineProperty(rs,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750}),is=class e extends No{constructor(t){super(t,{code:e.code,name:`AtomicityNotSupportedError`,shortMessage:`The wallet does not support atomic execution but the request requires it.`})}},Object.defineProperty(is,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760}),as=class e extends No{constructor(t){super(t,{code:e.code,name:`WalletConnectSessionSettlementError`,shortMessage:`WalletConnect session settlement failed.`})}},Object.defineProperty(as,`code`,{enumerable:!0,configurable:!0,writable:!0,value:7e3}),os=class extends Mo{constructor(e){super(e,{name:`UnknownRpcError`,shortMessage:`An unknown RPC error occurred.`})}}}));da(),cn(),D(),Eo(),Ao(),ss();var cs=3;function ls(e,{abi:t,address:n,args:r,docsPath:i,functionName:a,sender:o}){let s=e instanceof To?e:e instanceof E?e.walk(e=>`data`in e)||e.walk():{},{code:c,data:l,details:u,message:d,shortMessage:f}=s;return new So(e instanceof Gt?new A({functionName:a,cause:e}):[cs,Ro.code].includes(c)&&(l||u||d||f)||c===zo.code&&u===`execution reverted`&&l?new Co({abi:t,data:typeof l==`object`?l.data:l,functionName:a,message:s instanceof Oo?u:f??d,cause:e}):e,{abi:t,args:r,contractAddress:n,docsPath:i,functionName:a,sender:o})}Si(),ri();function us(e){return yi(`0x${ni(`0x${e.substring(4)}`).substring(26)}`)}var ds,fs,ps,ms,hs=o((()=>{ds=(function(){let e=typeof document<`u`&&document.createElement(`link`).relList;return e&&e.supports&&e.supports(`modulepreload`)?`modulepreload`:`preload`})(),fs=function(e){return`/`+e},ps={},ms=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=fs(t,n),t in ps)return;ps[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:ds,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})}}));function gs(e,t,n,r){if(typeof e.setBigUint64==`function`)return e.setBigUint64(t,n,r);let i=BigInt(32),a=BigInt(4294967295),o=Number(n>>i&a),s=Number(n&a),c=r?4:0,l=r?0:4;e.setUint32(t+c,o,r),e.setUint32(t+l,s,r)}function _s(e,t,n){return e&t^~e&n}function vs(e,t,n){return e&t^e&n^t&n}var ys,bs,xs,Ss,Cs=o((()=>{Fr(),ys=class extends Pr{constructor(e,t,n,r){super(),this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.blockLen=e,this.outputLen=t,this.padOffset=n,this.isLE=r,this.buffer=new Uint8Array(e),this.view=Sr(this.buffer)}update(e){vr(this),e=Dr(e),gr(e);let{view:t,buffer:n,blockLen:r}=this,i=e.length;for(let a=0;ar-a&&(this.process(n,0),a=0);for(let e=a;el.length)throw Error(`_sha2: outputLen bigger than state`);for(let e=0;e{Cs(),dr(),Fr(),ws=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),Ts=new Uint32Array(64),Es=class extends ys{constructor(e=32){super(64,e,8,!1),this.A=bs[0]|0,this.B=bs[1]|0,this.C=bs[2]|0,this.D=bs[3]|0,this.E=bs[4]|0,this.F=bs[5]|0,this.G=bs[6]|0,this.H=bs[7]|0}get(){let{A:e,B:t,C:n,D:r,E:i,F:a,G:o,H:s}=this;return[e,t,n,r,i,a,o,s]}set(e,t,n,r,i,a,o,s){this.A=e|0,this.B=t|0,this.C=n|0,this.D=r|0,this.E=i|0,this.F=a|0,this.G=o|0,this.H=s|0}process(e,t){for(let n=0;n<16;n++,t+=4)Ts[n]=e.getUint32(t,!1);for(let e=16;e<64;e++){let t=Ts[e-15],n=Ts[e-2],r=Cr(t,7)^Cr(t,18)^t>>>3;Ts[e]=(Cr(n,17)^Cr(n,19)^n>>>10)+Ts[e-7]+r+Ts[e-16]|0}let{A:n,B:r,C:i,D:a,E:o,F:s,G:c,H:l}=this;for(let e=0;e<64;e++){let t=Cr(o,6)^Cr(o,11)^Cr(o,25),u=l+t+_s(o,s,c)+ws[e]+Ts[e]|0,d=(Cr(n,2)^Cr(n,13)^Cr(n,22))+vs(n,r,i)|0;l=c,c=s,s=o,o=a+u|0,a=i,i=r,r=n,n=u+d|0}n=n+this.A|0,r=r+this.B|0,i=i+this.C|0,a=a+this.D|0,o=o+this.E|0,s=s+this.F|0,c=c+this.G|0,l=l+this.H|0,this.set(n,r,i,a,o,s,c,l)}roundClean(){xr(Ts)}destroy(){this.set(0,0,0,0,0,0,0,0),xr(this.buffer)}},Ds=Gn(`0x428a2f98d728ae22.0x7137449123ef65cd.0xb5c0fbcfec4d3b2f.0xe9b5dba58189dbbc.0x3956c25bf348b538.0x59f111f1b605d019.0x923f82a4af194f9b.0xab1c5ed5da6d8118.0xd807aa98a3030242.0x12835b0145706fbe.0x243185be4ee4b28c.0x550c7dc3d5ffb4e2.0x72be5d74f27b896f.0x80deb1fe3b1696b1.0x9bdc06a725c71235.0xc19bf174cf692694.0xe49b69c19ef14ad2.0xefbe4786384f25e3.0x0fc19dc68b8cd5b5.0x240ca1cc77ac9c65.0x2de92c6f592b0275.0x4a7484aa6ea6e483.0x5cb0a9dcbd41fbd4.0x76f988da831153b5.0x983e5152ee66dfab.0xa831c66d2db43210.0xb00327c898fb213f.0xbf597fc7beef0ee4.0xc6e00bf33da88fc2.0xd5a79147930aa725.0x06ca6351e003826f.0x142929670a0e6e70.0x27b70a8546d22ffc.0x2e1b21385c26c926.0x4d2c6dfc5ac42aed.0x53380d139d95b3df.0x650a73548baf63de.0x766a0abb3c77b2a8.0x81c2c92e47edaee6.0x92722c851482353b.0xa2bfe8a14cf10364.0xa81a664bbc423001.0xc24b8b70d0f89791.0xc76c51a30654be30.0xd192e819d6ef5218.0xd69906245565a910.0xf40e35855771202a.0x106aa07032bbd1b8.0x19a4c116b8d2d0c8.0x1e376c085141ab53.0x2748774cdf8eeb99.0x34b0bcb5e19b48a8.0x391c0cb3c5c95a63.0x4ed8aa4ae3418acb.0x5b9cca4f7763e373.0x682e6ff3d6b2b8a3.0x748f82ee5defb2fc.0x78a5636f43172f60.0x84c87814a1f0ab72.0x8cc702081a6439ec.0x90befffa23631e28.0xa4506cebde82bde9.0xbef9a3f7b2c67915.0xc67178f2e372532b.0xca273eceea26619c.0xd186b8c721c0c207.0xeada7dd6cde0eb1e.0xf57d4f7fee6ed178.0x06f067aa72176fba.0x0a637dc5a2c898a6.0x113f9804bef90dae.0x1b710b35131c471b.0x28db77f523047d84.0x32caab7b40c72493.0x3c9ebe0a15c9bebc.0x431d67c49c100d4c.0x4cc5d4becb3e42b6.0x597f299cfc657e2a.0x5fcb6fab3ad6faec.0x6c44198c4a475817`.split(`.`).map(e=>BigInt(e))),Os=Ds[0],ks=Ds[1],As=new Uint32Array(80),js=new Uint32Array(80),Ms=class extends ys{constructor(e=64){super(128,e,16,!1),this.Ah=Ss[0]|0,this.Al=Ss[1]|0,this.Bh=Ss[2]|0,this.Bl=Ss[3]|0,this.Ch=Ss[4]|0,this.Cl=Ss[5]|0,this.Dh=Ss[6]|0,this.Dl=Ss[7]|0,this.Eh=Ss[8]|0,this.El=Ss[9]|0,this.Fh=Ss[10]|0,this.Fl=Ss[11]|0,this.Gh=Ss[12]|0,this.Gl=Ss[13]|0,this.Hh=Ss[14]|0,this.Hl=Ss[15]|0}get(){let{Ah:e,Al:t,Bh:n,Bl:r,Ch:i,Cl:a,Dh:o,Dl:s,Eh:c,El:l,Fh:u,Fl:d,Gh:f,Gl:p,Hh:m,Hl:h}=this;return[e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h]}set(e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h){this.Ah=e|0,this.Al=t|0,this.Bh=n|0,this.Bl=r|0,this.Ch=i|0,this.Cl=a|0,this.Dh=o|0,this.Dl=s|0,this.Eh=c|0,this.El=l|0,this.Fh=u|0,this.Fl=d|0,this.Gh=f|0,this.Gl=p|0,this.Hh=m|0,this.Hl=h|0}process(e,t){for(let n=0;n<16;n++,t+=4)As[n]=e.getUint32(t),js[n]=e.getUint32(t+=4);for(let e=16;e<80;e++){let t=As[e-15]|0,n=js[e-15]|0,r=Zn(t,n,1)^Zn(t,n,8)^Yn(t,n,7),i=Qn(t,n,1)^Qn(t,n,8)^Xn(t,n,7),a=As[e-2]|0,o=js[e-2]|0,s=Zn(a,o,19)^$n(a,o,61)^Yn(a,o,6),c=Qn(a,o,19)^er(a,o,61)^Xn(a,o,6),l=sr(i,c,js[e-7],js[e-16]);As[e]=cr(l,r,s,As[e-7],As[e-16])|0,js[e]=l|0}let{Ah:n,Al:r,Bh:i,Bl:a,Ch:o,Cl:s,Dh:c,Dl:l,Eh:u,El:d,Fh:f,Fl:p,Gh:m,Gl:h,Hh:g,Hl:_}=this;for(let e=0;e<80;e++){let t=Zn(u,d,14)^Zn(u,d,18)^$n(u,d,41),v=Qn(u,d,14)^Qn(u,d,18)^er(u,d,41),y=u&f^~u&m,b=d&p^~d&h,x=lr(_,v,b,ks[e],js[e]),S=ur(x,g,t,y,Os[e],As[e]),ee=x|0,C=Zn(n,r,28)^$n(n,r,34)^$n(n,r,39),te=Qn(n,r,28)^er(n,r,34)^er(n,r,39),ne=n&i^n&o^i&o,re=r&a^r&s^a&s;g=m|0,_=h|0,m=f|0,h=p|0,f=u|0,p=d|0,{h:u,l:d}=Kn(c|0,l|0,S|0,ee|0),c=o|0,l=s|0,o=i|0,s=a|0,i=n|0,a=r|0;let ie=ar(ee,te,re);n=or(ie,S,C,ne),r=ie|0}({h:n,l:r}=Kn(this.Ah|0,this.Al|0,n|0,r|0)),{h:i,l:a}=Kn(this.Bh|0,this.Bl|0,i|0,a|0),{h:o,l:s}=Kn(this.Ch|0,this.Cl|0,o|0,s|0),{h:c,l}=Kn(this.Dh|0,this.Dl|0,c|0,l|0),{h:u,l:d}=Kn(this.Eh|0,this.El|0,u|0,d|0),{h:f,l:p}=Kn(this.Fh|0,this.Fl|0,f|0,p|0),{h:m,l:h}=Kn(this.Gh|0,this.Gl|0,m|0,h|0),{h:g,l:_}=Kn(this.Hh|0,this.Hl|0,g|0,_|0),this.set(n,r,i,a,o,s,c,l,u,d,f,p,m,h,g,_)}roundClean(){xr(As,js)}destroy(){xr(this.buffer),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}},Ns=class extends Ms{constructor(){super(48),this.Ah=xs[0]|0,this.Al=xs[1]|0,this.Bh=xs[2]|0,this.Bl=xs[3]|0,this.Ch=xs[4]|0,this.Cl=xs[5]|0,this.Dh=xs[6]|0,this.Dl=xs[7]|0,this.Eh=xs[8]|0,this.El=xs[9]|0,this.Fh=xs[10]|0,this.Fl=xs[11]|0,this.Gh=xs[12]|0,this.Gl=xs[13]|0,this.Hh=xs[14]|0,this.Hl=xs[15]|0}},Ps=kr(()=>new Es),Fs=kr(()=>new Ms),Is=kr(()=>new Ns)})),Rs,zs,Bs=o((()=>{Fr(),Rs=class extends Pr{constructor(e,t){super(),this.finished=!1,this.destroyed=!1,_r(e);let n=Dr(t);if(this.iHash=e.create(),typeof this.iHash.update!=`function`)throw Error(`Expected instance of class which extends utils.Hash`);this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;let r=this.blockLen,i=new Uint8Array(r);i.set(n.length>r?e.create().update(n).digest():n);for(let e=0;enew Rs(e,t).update(n).digest(),zs.create=(e,t)=>new Rs(e,t)}));function Vs(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`}function Hs(e){if(!Vs(e))throw Error(`Uint8Array expected`)}function Us(e,t){if(typeof t!=`boolean`)throw Error(e+` boolean expected, got `+t)}function Ws(e){let t=e.toString(16);return t.length&1?`0`+t:t}function Gs(e){if(typeof e!=`string`)throw Error(`hex string expected, got `+typeof e);return e===``?lc:BigInt(`0x`+e)}function Ks(e){if(Hs(e),dc)return e.toHex();let t=``;for(let n=0;n=pc._0&&e<=pc._9)return e-pc._0;if(e>=pc.A&&e<=pc.F)return e-(pc.A-10);if(e>=pc.a&&e<=pc.f)return e-(pc.a-10)}function Js(e){if(typeof e!=`string`)throw Error(`hex string expected, got `+typeof e);if(dc)return Uint8Array.fromHex(e);let t=e.length,n=t/2;if(t%2)throw Error(`hex string expected, got unpadded hex of length `+t);let r=new Uint8Array(n);for(let t=0,i=0;tlc;e>>=uc,t+=1);return t}function oc(e,t,n){if(typeof e!=`number`||e<2)throw Error(`hashLen must be a number`);if(typeof t!=`number`||t<2)throw Error(`qByteLen must be a number`);if(typeof n!=`function`)throw Error(`hmacFn must be a function`);let r=gc(e),i=gc(e),a=0,o=()=>{r.fill(1),i.fill(0),a=0},s=(...e)=>n(i,r,...e),c=(e=gc(0))=>{i=s(_c([0]),e),r=s(),e.length!==0&&(i=s(_c([1]),e),r=s())},l=()=>{if(a++>=1e3)throw Error(`drbg: tried 1000 values`);let e=0,n=[];for(;e{o(),c(e);let n;for(;!(n=t(l()));)c();return o(),n}}function sc(e,t,n={}){let r=(t,n,r)=>{let i=vc[n];if(typeof i!=`function`)throw Error(`invalid validator function`);let a=e[t];if(!(r&&a===void 0)&&!i(a,e))throw Error(`param `+String(t)+` is invalid. Expected `+n+`, got `+a)};for(let[e,n]of Object.entries(t))r(e,n,!1);for(let[e,t]of Object.entries(n))r(e,t,!0);return e}function cc(e){let t=new WeakMap;return(n,...r)=>{let i=t.get(n);if(i!==void 0)return i;let a=e(n,...r);return t.set(n,a),a}}var lc,uc,dc,fc,pc,mc,hc,gc,_c,vc,yc=o((()=>{lc=BigInt(0),uc=BigInt(1),dc=typeof Uint8Array.from([]).toHex==`function`&&typeof Uint8Array.fromHex==`function`,fc=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),pc={_0:48,_9:57,A:65,F:70,a:97,f:102},mc=e=>typeof e==`bigint`&&lc<=e,hc=e=>(uc<new Uint8Array(e),_c=e=>Uint8Array.from(e),vc={bigint:e=>typeof e==`bigint`,function:e=>typeof e==`function`,boolean:e=>typeof e==`boolean`,string:e=>typeof e==`string`,stringOrUint8Array:e=>typeof e==`string`||Vs(e),isSafeInteger:e=>Number.isSafeInteger(e),array:e=>Array.isArray(e),field:(e,t)=>t.Fp.isValid(e),hash:e=>typeof e==`function`&&Number.isSafeInteger(e.outputLen)}}));function bc(e,t){let n=e%t;return n>=Ic?n:t+n}function xc(e,t,n){let r=e;for(;t-- >Ic;)r*=r,r%=n;return r}function Sc(e,t){if(e===Ic)throw Error(`invert: expected non-zero number`);if(t<=Ic)throw Error(`invert: expected positive modulus, got `+t);let n=bc(e,t),r=t,i=Ic,a=Lc,o=Lc,s=Ic;for(;n!==Ic;){let e=r/n,t=r%n,c=i-o*e,l=a-s*e;r=n,n=t,i=o,a=s,o=c,s=l}if(r!==Lc)throw Error(`invert: does not exist`);return bc(i,t)}function Cc(e,t){let n=(e.ORDER+Lc)/Bc,r=e.pow(t,n);if(!e.eql(e.sqr(r),t))throw Error(`Cannot find square root`);return r}function wc(e,t){let n=(e.ORDER-Vc)/Hc,r=e.mul(t,Rc),i=e.pow(r,n),a=e.mul(t,i),o=e.mul(e.mul(a,Rc),i),s=e.mul(a,e.sub(o,e.ONE));if(!e.eql(e.sqr(s),t))throw Error(`Cannot find square root`);return s}function Tc(e){if(e1e3)throw Error(`Cannot find square root: probably non-prime P`);if(n===1)return Cc;let a=i.pow(r,t),o=(t+Lc)/Rc;return function(e,r){if(e.is0(r))return r;if(Ac(e,r)!==1)throw Error(`Cannot find square root`);let i=n,s=e.mul(e.ONE,a),c=e.pow(r,t),l=e.pow(r,o);for(;!e.eql(c,e.ONE);){if(e.is0(c))return e.ZERO;let t=1,n=e.sqr(c);for(;!e.eql(n,e.ONE);)if(t++,n=e.sqr(n),t===i)throw Error(`Cannot find square root`);let r=Lc<(e[t]=`function`,e),{ORDER:`bigint`,MASK:`bigint`,BYTES:`isSafeInteger`,BITS:`isSafeInteger`}))}function Oc(e,t,n){if(nIc;)n&Lc&&(r=e.mul(r,i)),i=e.sqr(i),n>>=Lc;return r}function kc(e,t,n=!1){let r=Array(t.length).fill(n?e.ZERO:void 0),i=t.reduce((t,n,i)=>e.is0(n)?t:(r[i]=t,e.mul(t,n)),e.ONE),a=e.inv(i);return t.reduceRight((t,n,i)=>e.is0(n)?t:(r[i]=e.mul(t,r[i]),e.mul(t,n)),a),r}function Ac(e,t){let n=(e.ORDER-Lc)/Rc,r=e.pow(t,n),i=e.eql(r,e.ONE),a=e.eql(r,e.ZERO),o=e.eql(r,e.neg(e.ONE));if(!i&&!a&&!o)throw Error(`invalid Legendre symbol result`);return i?1:a?0:-1}function jc(e,t){t!==void 0&&hr(t);let n=t===void 0?e.toString(2).length:t;return{nBitLength:n,nByteLength:Math.ceil(n/8)}}function Mc(e,t,n=!1,r={}){if(e<=Ic)throw Error(`invalid field: expected ORDER > 0, got `+e);let{nBitLength:i,nByteLength:a}=jc(e,t);if(a>2048)throw Error(`invalid field: expected ORDER of <= 2048 bytes`);let o,s=Object.freeze({ORDER:e,isLE:n,BITS:i,BYTES:a,MASK:hc(i),ZERO:Ic,ONE:Lc,create:t=>bc(t,e),isValid:t=>{if(typeof t!=`bigint`)throw Error(`invalid field element: expected bigint, got `+typeof t);return Ic<=t&&te===Ic,isOdd:e=>(e&Lc)===Lc,neg:t=>bc(-t,e),eql:(e,t)=>e===t,sqr:t=>bc(t*t,e),add:(t,n)=>bc(t+n,e),sub:(t,n)=>bc(t-n,e),mul:(t,n)=>bc(t*n,e),pow:(e,t)=>Oc(s,e,t),div:(t,n)=>bc(t*Sc(n,e),e),sqrN:e=>e*e,addN:(e,t)=>e+t,subN:(e,t)=>e-t,mulN:(e,t)=>e*t,inv:t=>Sc(t,e),sqrt:r.sqrt||(t=>(o||=Ec(e),o(s,t))),toBytes:e=>n?Qs(e,a):Zs(e,a),fromBytes:e=>{if(e.length!==a)throw Error(`Field.fromBytes: expected `+a+` bytes, got `+e.length);return n?Xs(e):Ys(e)},invertBatch:e=>kc(s,e),cmov:(e,t,n)=>n?t:e});return Object.freeze(s)}function Nc(e){if(typeof e!=`bigint`)throw Error(`field order must be bigint`);let t=e.toString(2).length;return Math.ceil(t/8)}function Pc(e){let t=Nc(e);return t+Math.ceil(t/2)}function Fc(e,t,n=!1){let r=e.length,i=Nc(t),a=Pc(t);if(r<16||r1024)throw Error(`expected `+a+`-1024 bytes of input, got `+r);let o=bc(n?Xs(e):Ys(e),t-Lc)+Lc;return n?Qs(o,i):Zs(o,i)}var Ic,Lc,Rc,zc,Bc,Vc,Hc,Uc,Wc=o((()=>{Fr(),yc(),Ic=BigInt(0),Lc=BigInt(1),Rc=BigInt(2),zc=BigInt(3),Bc=BigInt(4),Vc=BigInt(5),Hc=BigInt(8),Uc=[`create`,`isValid`,`is0`,`neg`,`inv`,`sqrt`,`sqr`,`eql`,`add`,`sub`,`mul`,`pow`,`div`,`addN`,`subN`,`mulN`,`sqrN`]}));function Gc(e,t){let n=t.negate();return e?n:t}function Kc(e,t){if(!Number.isSafeInteger(e)||e<=0||e>t)throw Error(`invalid window size, expected [1..`+t+`], got W=`+e)}function qc(e,t){Kc(e,t);let n=Math.ceil(t/e)+1,r=2**(e-1),i=2**e;return{windows:n,windowSize:r,mask:hc(e),maxNumber:i,shiftBy:BigInt(e)}}function Jc(e,t,n){let{windowSize:r,mask:i,maxNumber:a,shiftBy:o}=n,s=Number(e&i),c=e>>o;s>r&&(s-=a,c+=nl);let l=t*r,u=l+Math.abs(s)-1,d=s===0,f=s<0,p=t%2!=0;return{nextN:c,offset:u,isZero:d,isNeg:f,isNegF:p,offsetF:l}}function Yc(e,t){if(!Array.isArray(e))throw Error(`array expected`);e.forEach((e,n)=>{if(!(e instanceof t))throw Error(`invalid point at index `+n)})}function Xc(e,t){if(!Array.isArray(e))throw Error(`array of scalars expected`);e.forEach((e,n)=>{if(!t.isValid(e))throw Error(`invalid scalar at index `+n)})}function Zc(e){return il.get(e)||1}function Qc(e,t){return{constTimeNegate:Gc,hasPrecomputes(e){return Zc(e)!==1},unsafeLadder(t,n,r=e.ZERO){let i=t;for(;n>tl;)n&nl&&(r=r.add(i)),i=i.double(),n>>=nl;return r},precomputeWindow(e,n){let{windows:r,windowSize:i}=qc(n,t),a=[],o=e,s=o;for(let e=0;e12?c=s-3:s>4?c=s-2:s>0&&(c=2);let l=hc(c),u=Array(Number(l)+1).fill(o),d=Math.floor((t.BITS-1)/c)*c,f=o;for(let e=d;e>=0;e-=c){u.fill(o);for(let t=0;t>BigInt(e)&l);u[a]=u[a].add(n[t])}let t=o;for(let e=u.length-1,n=o;e>0;e--)n=n.add(u[e]),t=t.add(n);if(f=f.add(t),e!==0)for(let e=0;e{Wc(),yc(),tl=BigInt(0),nl=BigInt(1),rl=new WeakMap,il=new WeakMap}));function ol(e){e.lowS!==void 0&&Us(`lowS`,e.lowS),e.prehash!==void 0&&Us(`prehash`,e.prehash)}function sl(e){let t=el(e);sc(t,{a:`field`,b:`field`},{allowInfinityPoint:`boolean`,allowedPrivateKeyLengths:`array`,clearCofactor:`function`,fromBytes:`function`,isTorsionFree:`function`,toBytes:`function`,wrapPrivateKey:`boolean`});let{endo:n,Fp:r,a:i}=t;if(n){if(!r.eql(i,r.ZERO))throw Error(`invalid endo: CURVE.a must be 0`);if(typeof n!=`object`||typeof n.beta!=`bigint`||typeof n.splitScalar!=`function`)throw Error(`invalid endo: expected "beta": bigint and "splitScalar": function`)}return Object.freeze({...t})}function cl(e,t){return Ks(Zs(e,t))}function ll(e){let t=sl(e),{Fp:n}=t,r=Mc(t.n,t.nBitLength),i=t.toBytes||((e,t,r)=>{let i=t.toAffine();return ec(Uint8Array.from([4]),n.toBytes(i.x),n.toBytes(i.y))}),a=t.fromBytes||(e=>{let t=e.subarray(1);return{x:n.fromBytes(t.subarray(0,n.BYTES)),y:n.fromBytes(t.subarray(n.BYTES,2*n.BYTES))}});function o(e){let{a:r,b:i}=t,a=n.sqr(e),o=n.mul(a,e);return n.add(n.add(o,n.mul(e,r)),i)}function s(e,t){let r=n.sqr(t),i=o(e);return n.eql(r,i)}if(!s(t.Gx,t.Gy))throw Error(`bad curve params: generator point`);let c=n.mul(n.pow(t.a,yl),bl),l=n.mul(n.sqr(t.b),BigInt(27));if(n.is0(n.add(c,l)))throw Error(`bad curve params: a or b`);function u(e){return rc(e,_l,t.n)}function d(e){let{allowedPrivateKeyLengths:n,nByteLength:r,wrapPrivateKey:i,n:a}=t;if(n&&typeof e!=`bigint`){if(Vs(e)&&(e=Ks(e)),typeof e!=`string`||!n.includes(e.length))throw Error(`invalid private key`);e=e.padStart(r*2,`0`)}let o;try{o=typeof e==`bigint`?e:Ys($s(`private key`,e,r))}catch{throw Error(`invalid private key, expected hex or `+r+` bytes, got `+typeof e)}return i&&(o=bc(o,a)),ic(`private key`,o,_l,a),o}function f(e){if(!(e instanceof h))throw Error(`ProjectivePoint expected`)}let p=cc((e,t)=>{let{px:r,py:i,pz:a}=e;if(n.eql(a,n.ONE))return{x:r,y:i};let o=e.is0();t??=o?n.ONE:n.inv(a);let s=n.mul(r,t),c=n.mul(i,t),l=n.mul(a,t);if(o)return{x:n.ZERO,y:n.ZERO};if(!n.eql(l,n.ONE))throw Error(`invZ was invalid`);return{x:s,y:c}}),m=cc(e=>{if(e.is0()){if(t.allowInfinityPoint&&!n.is0(e.py))return;throw Error(`bad point: ZERO`)}let{x:r,y:i}=e.toAffine();if(!n.isValid(r)||!n.isValid(i))throw Error(`bad point: x or y not FE`);if(!s(r,i))throw Error(`bad point: equation left != right`);if(!e.isTorsionFree())throw Error(`bad point: not in prime-order subgroup`);return!0});class h{constructor(e,t,r){if(e==null||!n.isValid(e))throw Error(`x required`);if(t==null||!n.isValid(t)||n.is0(t))throw Error(`y required`);if(r==null||!n.isValid(r))throw Error(`z required`);this.px=e,this.py=t,this.pz=r,Object.freeze(this)}static fromAffine(e){let{x:t,y:r}=e||{};if(!e||!n.isValid(t)||!n.isValid(r))throw Error(`invalid affine point`);if(e instanceof h)throw Error(`projective point not allowed`);let i=e=>n.eql(e,n.ZERO);return i(t)&&i(r)?h.ZERO:new h(t,r,n.ONE)}get x(){return this.toAffine().x}get y(){return this.toAffine().y}static normalizeZ(e){let t=kc(n,e.map(e=>e.pz));return e.map((e,n)=>e.toAffine(t[n])).map(h.fromAffine)}static fromHex(e){let t=h.fromAffine(a($s(`pointHex`,e)));return t.assertValidity(),t}static fromPrivateKey(e){return h.BASE.multiply(d(e))}static msm(e,t){return $c(h,r,e,t)}_setWindowSize(e){v.setWindowSize(this,e)}assertValidity(){m(this)}hasEvenY(){let{y:e}=this.toAffine();if(n.isOdd)return!n.isOdd(e);throw Error(`Field doesn't support isOdd`)}equals(e){f(e);let{px:t,py:r,pz:i}=this,{px:a,py:o,pz:s}=e,c=n.eql(n.mul(t,s),n.mul(a,i)),l=n.eql(n.mul(r,s),n.mul(o,i));return c&&l}negate(){return new h(this.px,n.neg(this.py),this.pz)}double(){let{a:e,b:r}=t,i=n.mul(r,yl),{px:a,py:o,pz:s}=this,c=n.ZERO,l=n.ZERO,u=n.ZERO,d=n.mul(a,a),f=n.mul(o,o),p=n.mul(s,s),m=n.mul(a,o);return m=n.add(m,m),u=n.mul(a,s),u=n.add(u,u),c=n.mul(e,u),l=n.mul(i,p),l=n.add(c,l),c=n.sub(f,l),l=n.add(f,l),l=n.mul(c,l),c=n.mul(m,c),u=n.mul(i,u),p=n.mul(e,p),m=n.sub(d,p),m=n.mul(e,m),m=n.add(m,u),u=n.add(d,d),d=n.add(u,d),d=n.add(d,p),d=n.mul(d,m),l=n.add(l,d),p=n.mul(o,s),p=n.add(p,p),d=n.mul(p,m),c=n.sub(c,d),u=n.mul(p,f),u=n.add(u,u),u=n.add(u,u),new h(c,l,u)}add(e){f(e);let{px:r,py:i,pz:a}=this,{px:o,py:s,pz:c}=e,l=n.ZERO,u=n.ZERO,d=n.ZERO,p=t.a,m=n.mul(t.b,yl),g=n.mul(r,o),_=n.mul(i,s),v=n.mul(a,c),y=n.add(r,i),b=n.add(o,s);y=n.mul(y,b),b=n.add(g,_),y=n.sub(y,b),b=n.add(r,a);let x=n.add(o,c);return b=n.mul(b,x),x=n.add(g,v),b=n.sub(b,x),x=n.add(i,a),l=n.add(s,c),x=n.mul(x,l),l=n.add(_,v),x=n.sub(x,l),d=n.mul(p,b),l=n.mul(m,v),d=n.add(l,d),l=n.sub(_,d),d=n.add(_,d),u=n.mul(l,d),_=n.add(g,g),_=n.add(_,g),v=n.mul(p,v),b=n.mul(m,b),_=n.add(_,v),v=n.sub(g,v),v=n.mul(p,v),b=n.add(b,v),g=n.mul(_,b),u=n.add(u,g),g=n.mul(x,b),l=n.mul(y,l),l=n.sub(l,g),g=n.mul(y,_),d=n.mul(x,d),d=n.add(d,g),new h(l,u,d)}subtract(e){return this.add(e.negate())}is0(){return this.equals(h.ZERO)}wNAF(e){return v.wNAFCached(this,e,h.normalizeZ)}multiplyUnsafe(e){let{endo:r,n:i}=t;ic(`scalar`,e,gl,i);let a=h.ZERO;if(e===gl)return a;if(this.is0()||e===_l)return this;if(!r||v.hasPrecomputes(this))return v.wNAFCachedUnsafe(this,e,h.normalizeZ);let{k1neg:o,k1:s,k2neg:c,k2:l}=r.splitScalar(e),u=a,d=a,f=this;for(;s>gl||l>gl;)s&_l&&(u=u.add(f)),l&_l&&(d=d.add(f)),f=f.double(),s>>=_l,l>>=_l;return o&&(u=u.negate()),c&&(d=d.negate()),d=new h(n.mul(d.px,r.beta),d.py,d.pz),u.add(d)}multiply(e){let{endo:r,n:i}=t;ic(`scalar`,e,_l,i);let a,o;if(r){let{k1neg:t,k1:i,k2neg:s,k2:c}=r.splitScalar(e),{p:l,f:u}=this.wNAF(i),{p:d,f}=this.wNAF(c);l=v.constTimeNegate(t,l),d=v.constTimeNegate(s,d),d=new h(n.mul(d.px,r.beta),d.py,d.pz),a=l.add(d),o=u.add(f)}else{let{p:t,f:n}=this.wNAF(e);a=t,o=n}return h.normalizeZ([a,o])[0]}multiplyAndAddUnsafe(e,t,n){let r=h.BASE,i=(e,t)=>t===gl||t===_l||!e.equals(r)?e.multiplyUnsafe(t):e.multiply(t),a=i(this,t).add(i(e,n));return a.is0()?void 0:a}toAffine(e){return p(this,e)}isTorsionFree(){let{h:e,isTorsionFree:n}=t;if(e===_l)return!0;if(n)return n(h,this);throw Error(`isTorsionFree() has not been declared for the elliptic curve`)}clearCofactor(){let{h:e,clearCofactor:n}=t;return e===_l?this:n?n(h,this):this.multiplyUnsafe(t.h)}toRawBytes(e=!0){return Us(`isCompressed`,e),this.assertValidity(),i(h,this,e)}toHex(e=!0){return Us(`isCompressed`,e),Ks(this.toRawBytes(e))}}h.BASE=new h(t.Gx,t.Gy,n.ONE),h.ZERO=new h(n.ZERO,n.ONE,n.ZERO);let{endo:g,nBitLength:_}=t,v=Qc(h,g?Math.ceil(_/2):_);return{CURVE:t,ProjectivePoint:h,normPrivateKeyToScalar:d,weierstrassEquation:o,isWithinCurveOrder:u}}function ul(e){let t=el(e);return sc(t,{hash:`hash`,hmac:`function`,randomBytes:`function`},{bits2int:`function`,bits2int_modN:`function`,lowS:`boolean`}),Object.freeze({lowS:!0,...t})}function dl(e){let t=ul(e),{Fp:n,n:r,nByteLength:i,nBitLength:a}=t,o=n.BYTES+1,s=2*n.BYTES+1;function c(e){return bc(e,r)}function l(e){return Sc(e,r)}let{ProjectivePoint:u,normPrivateKeyToScalar:d,weierstrassEquation:f,isWithinCurveOrder:p}=ll({...t,toBytes(e,t,r){let i=t.toAffine(),a=n.toBytes(i.x),o=ec;return Us(`isCompressed`,r),r?o(Uint8Array.from([t.hasEvenY()?2:3]),a):o(Uint8Array.from([4]),a,n.toBytes(i.y))},fromBytes(e){let t=e.length,r=e[0],i=e.subarray(1);if(t===o&&(r===2||r===3)){let e=Ys(i);if(!rc(e,_l,n.ORDER))throw Error(`Point is not on curve`);let t=f(e),a;try{a=n.sqrt(t)}catch(e){let t=e instanceof Error?`: `+e.message:``;throw Error(`Point is not on curve`+t)}let o=(a&_l)===_l;return(r&1)==1!==o&&(a=n.neg(a)),{x:e,y:a}}else if(t===s&&r===4)return{x:n.fromBytes(i.subarray(0,n.BYTES)),y:n.fromBytes(i.subarray(n.BYTES,2*n.BYTES))};else{let e=o,n=s;throw Error(`invalid Point, expected length of `+e+`, or uncompressed `+n+`, got `+t)}}});function m(e){return e>r>>_l}function h(e){return m(e)?c(-e):e}let g=(e,t,n)=>Ys(e.slice(t,n));class _{constructor(e,t,n){ic(`r`,e,_l,r),ic(`s`,t,_l,r),this.r=e,this.s=t,n!=null&&(this.recovery=n),Object.freeze(this)}static fromCompact(e){let t=i;return e=$s(`compactSignature`,e,t*2),new _(g(e,0,t),g(e,t,2*t))}static fromDER(e){let{r:t,s:n}=hl.toSig($s(`DER`,e));return new _(t,n)}assertValidity(){}addRecoveryBit(e){return new _(this.r,this.s,e)}recoverPublicKey(e){let{r,s:i,recovery:a}=this,o=ee($s(`msgHash`,e));if(a==null||![0,1,2,3].includes(a))throw Error(`recovery id invalid`);let s=a===2||a===3?r+t.n:r;if(s>=n.ORDER)throw Error(`recovery id 2 or 3 invalid`);let d=a&1?`03`:`02`,f=u.fromHex(d+cl(s,n.BYTES)),p=l(s),m=c(-o*p),h=c(i*p),g=u.BASE.multiplyAndAddUnsafe(f,m,h);if(!g)throw Error(`point at infinify`);return g.assertValidity(),g}hasHighS(){return m(this.s)}normalizeS(){return this.hasHighS()?new _(this.r,c(-this.s),this.recovery):this}toDERRawBytes(){return Js(this.toDERHex())}toDERHex(){return hl.hexFromSig(this)}toCompactRawBytes(){return Js(this.toCompactHex())}toCompactHex(){let e=i;return cl(this.r,e)+cl(this.s,e)}}let v={isValidPrivateKey(e){try{return d(e),!0}catch{return!1}},normPrivateKeyToScalar:d,randomPrivateKey:()=>{let e=Pc(t.n);return Fc(t.randomBytes(e),t.n)},precompute(e=8,t=u.BASE){return t._setWindowSize(e),t.multiply(BigInt(3)),t}};function y(e,t=!0){return u.fromPrivateKey(e).toRawBytes(t)}function b(e){if(typeof e==`bigint`)return!1;if(e instanceof u)return!0;let r=$s(`key`,e).length,a=n.BYTES,o=a+1,s=2*a+1;if(!(t.allowedPrivateKeyLengths||i===o))return r===o||r===s}function x(e,t,n=!0){if(b(e)===!0)throw Error(`first arg must be private key`);if(b(t)===!1)throw Error(`second arg must be public key`);return u.fromHex(t).multiply(d(e)).toRawBytes(n)}let S=t.bits2int||function(e){if(e.length>8192)throw Error(`input is too large`);let t=Ys(e),n=e.length*8-a;return n>0?t>>BigInt(n):t},ee=t.bits2int_modN||function(e){return c(S(e))},C=hc(a);function te(e){return ic(`num < 2^`+a,e,gl,C),Zs(e,i)}function ne(e,r,i=re){if([`recovered`,`canonical`].some(e=>e in i))throw Error(`sign() legacy options not supported`);let{hash:a,randomBytes:o}=t,{lowS:s,prehash:f,extraEntropy:g}=i;s??=!0,e=$s(`msgHash`,e),ol(i),f&&(e=$s(`prehashed msgHash`,a(e)));let v=ee(e),y=d(r),b=[te(y),te(v)];if(g!=null&&g!==!1){let e=g===!0?o(n.BYTES):g;b.push($s(`extraEntropy`,e))}let x=ec(...b),C=v;function ne(e){let t=S(e);if(!p(t))return;let n=l(t),r=u.BASE.multiply(t).toAffine(),i=c(r.x);if(i===gl)return;let a=c(n*c(C+i*y));if(a===gl)return;let o=(r.x===i?0:2)|Number(r.y&_l),d=a;return s&&m(a)&&(d=h(a),o^=1),new _(i,d,o)}return{seed:x,k2sig:ne}}let re={lowS:t.lowS,prehash:!1},ie={lowS:t.lowS,prehash:!1};function ae(e,n,r=re){let{seed:i,k2sig:a}=ne(e,n,r),o=t;return oc(o.hash.outputLen,o.nByteLength,o.hmac)(i,a)}u.BASE._setWindowSize(8);function oe(e,n,r,i=ie){let a=e;n=$s(`msgHash`,n),r=$s(`publicKey`,r);let{lowS:o,prehash:s,format:d}=i;if(ol(i),`strict`in i)throw Error(`options.strict was renamed to lowS`);if(d!==void 0&&d!==`compact`&&d!==`der`)throw Error(`format must be compact or der`);let f=typeof a==`string`||Vs(a),p=!f&&!d&&typeof a==`object`&&!!a&&typeof a.r==`bigint`&&typeof a.s==`bigint`;if(!f&&!p)throw Error(`invalid signature, expected Uint8Array, hex string or Signature instance`);let m,h;try{if(p&&(m=new _(a.r,a.s)),f){try{d!==`compact`&&(m=_.fromDER(a))}catch(e){if(!(e instanceof hl.Err))throw e}!m&&d!==`der`&&(m=_.fromCompact(a))}h=u.fromHex(r)}catch{return!1}if(!m||o&&m.hasHighS())return!1;s&&(n=t.hash(n));let{r:g,s:v}=m,y=ee(n),b=l(v),x=c(y*b),S=c(g*b),C=u.BASE.multiplyAndAddUnsafe(h,x,S)?.toAffine();return C?c(C.x)===g:!1}return{CURVE:t,getPublicKey:y,getSharedSecret:x,sign:ae,verify:oe,ProjectivePoint:u,Signature:_,utils:v}}function fl(e,t){let n=e.ORDER,r=gl;for(let e=n-_l;e%vl===gl;e/=vl)r+=_l;let i=r,a=vl<{let r=d,a=e.pow(n,l),o=e.sqr(a);o=e.mul(o,n);let s=e.mul(t,o);s=e.pow(s,c),s=e.mul(s,a),a=e.mul(s,n),o=e.mul(s,t);let p=e.mul(o,a);s=e.pow(p,u);let m=e.eql(s,e.ONE);a=e.mul(o,f),s=e.mul(p,r),o=e.cmov(a,o,m),p=e.cmov(s,p,m);for(let t=i;t>_l;t--){let n=t-vl;n=vl<{let a=e.sqr(i),o=e.mul(t,i);a=e.mul(a,o);let s=e.pow(a,n);s=e.mul(s,o);let c=e.mul(s,r),l=e.mul(e.sqr(s),i),u=e.eql(l,t);return{isValid:u,value:e.cmov(c,s,u)}}}return p}function pl(e,t){if(Dc(e),!e.isValid(t.A)||!e.isValid(t.B)||!e.isValid(t.Z))throw Error(`mapToCurveSimpleSWU: invalid opts`);let n=fl(e,t.Z);if(!e.isOdd)throw Error(`Fp.isOdd is not implemented!`);return r=>{let i,a,o,s,c,l,u,d;i=e.sqr(r),i=e.mul(i,t.Z),a=e.sqr(i),a=e.add(a,i),o=e.add(a,e.ONE),o=e.mul(o,t.B),s=e.cmov(t.Z,e.neg(a),!e.eql(a,e.ZERO)),s=e.mul(s,t.A),a=e.sqr(o),l=e.sqr(s),c=e.mul(l,t.A),a=e.add(a,c),a=e.mul(a,o),l=e.mul(l,s),c=e.mul(l,t.B),a=e.add(a,c),u=e.mul(i,o);let{isValid:f,value:p}=n(a,l);d=e.mul(i,r),d=e.mul(d,p),u=e.cmov(u,o,f),d=e.cmov(d,p,f);let m=e.isOdd(r)===e.isOdd(d);d=e.cmov(e.neg(d),d,m);let h=kc(e,[s],!0)[0];return u=e.mul(u,h),{x:u,y:d}}}var ml,hl,gl,_l,vl,yl,bl,xl=o((()=>{al(),Wc(),yc(),ml=class extends Error{constructor(e=``){super(e)}},hl={Err:ml,_tlv:{encode:(e,t)=>{let{Err:n}=hl;if(e<0||e>256)throw new n(`tlv.encode: wrong tag`);if(t.length&1)throw new n(`tlv.encode: unpadded data`);let r=t.length/2,i=Ws(r);if(i.length/2&128)throw new n(`tlv.encode: long form length too big`);let a=r>127?Ws(i.length/2|128):``;return Ws(e)+a+i+t},decode(e,t){let{Err:n}=hl,r=0;if(e<0||e>256)throw new n(`tlv.encode: wrong tag`);if(t.length<2||t[r++]!==e)throw new n(`tlv.decode: wrong tlv`);let i=t[r++],a=!!(i&128),o=0;if(!a)o=i;else{let e=i&127;if(!e)throw new n(`tlv.decode(long): indefinite length not supported`);if(e>4)throw new n(`tlv.decode(long): byte length is too big`);let a=t.subarray(r,r+e);if(a.length!==e)throw new n(`tlv.decode: length bytes not complete`);if(a[0]===0)throw new n(`tlv.decode(long): zero leftmost byte`);for(let e of a)o=o<<8|e;if(r+=e,o<128)throw new n(`tlv.decode(long): not minimal encoding`)}let s=t.subarray(r,r+o);if(s.length!==o)throw new n(`tlv.decode: wrong value length`);return{v:s,l:t.subarray(r+o)}}},_int:{encode(e){let{Err:t}=hl;if(ezs(e,t,Or(...n)),randomBytes:jr}}function Cl(e,t){let n=t=>dl({...e,...Sl(t)});return{...n(t),create:n}}var wl=o((()=>{Bs(),Fr(),xl()}));function Tl(e,t){if(Dl(e),Dl(t),e<0||e>=1<<8*t)throw Error(`invalid I2OSP input: `+e);let n=Array.from({length:t}).fill(0);for(let r=t-1;r>=0;r--)n[r]=e&255,e>>>=8;return new Uint8Array(n)}function El(e,t){let n=new Uint8Array(e.length);for(let r=0;r255&&(t=r(ec(nc(`H2C-OVERSIZE-DST-`),t)));let{outputLen:i,blockLen:a}=r,o=Math.ceil(n/i);if(n>65535||o>255)throw Error(`expand_message_xmd: invalid lenInBytes`);let s=ec(t,Tl(t.length,1)),c=Tl(0,a),l=Tl(n,2),u=Array(o),d=r(ec(c,e,l,Tl(0,1),s));u[0]=r(ec(d,Tl(1,1),s));for(let e=1;e<=o;e++)u[e]=r(ec(El(d,u[e-1]),Tl(e+1,1),s));return ec(...u).slice(0,n)}function kl(e,t,n,r,i){if(Hs(e),Hs(t),Dl(n),t.length>255){let e=Math.ceil(2*r/8);t=i.create({dkLen:e}).update(nc(`H2C-OVERSIZE-DST-`)).update(t).digest()}if(n>65535||t.length>255)throw Error(`expand_message_xof: invalid lenInBytes`);return i.create({dkLen:n}).update(e).update(Tl(n,2)).update(t).update(Tl(t.length,1)).digest()}function Al(e,t,n){sc(n,{DST:`stringOrUint8Array`,p:`bigint`,m:`isSafeInteger`,k:`isSafeInteger`,hash:`hash`});let{p:r,k:i,m:a,hash:o,expand:s,DST:c}=n;Hs(e),Dl(t);let l=typeof c==`string`?nc(c):c,u=r.toString(2).length,d=Math.ceil((u+i)/8),f=t*a*d,p;if(s===`xmd`)p=Ol(e,l,f,o);else if(s===`xof`)p=kl(e,l,f,i,o);else if(s===`_internal_pass`)p=e;else throw Error(`expand must be "xmd" or "xof"`);let m=Array(t);for(let e=0;eArray.from(e).reverse());return(t,r)=>{let[i,a,o,s]=n.map(n=>n.reduce((n,r)=>e.add(e.mul(n,t),r))),[c,l]=kc(e,[a,s],!0);return t=e.mul(i,c),r=e.mul(r,e.mul(o,l)),{x:t,y:r}}}function Ml(e,t,n){if(typeof t!=`function`)throw Error(`mapToCurve() must be defined`);function r(n){return e.fromAffine(t(n))}function i(t){let n=t.clearCofactor();return n.equals(e.ZERO)?e.ZERO:(n.assertValidity(),n)}return{defaults:n,hashToCurve(e,t){let a=Al(e,2,{...n,DST:n.DST,...t}),o=r(a[0]),s=r(a[1]);return i(o.add(s))},encodeToCurve(e,t){return i(r(Al(e,1,{...n,DST:n.encodeDST,...t})[0]))},mapToCurve(e){if(!Array.isArray(e))throw Error(`expected array of bigints`);for(let t of e)if(typeof t!=`bigint`)throw Error(`expected array of bigints`);return i(r(e))}}}var Nl,Pl=o((()=>{Wc(),yc(),Nl=Ys})),Fl=c({encodeToCurve:()=>lu,hashToCurve:()=>cu,schnorr:()=>iu,secp256k1:()=>Zl,secp256k1_hasher:()=>su});function Il(e){let t=Wl,n=BigInt(3),r=BigInt(6),i=BigInt(11),a=BigInt(22),o=BigInt(23),s=BigInt(44),c=BigInt(88),l=e*e*e%t,u=l*l*e%t,d=xc(xc(xc(u,n,t)*u%t,n,t)*u%t,Jl,t)*l%t,f=xc(d,i,t)*d%t,p=xc(f,a,t)*f%t,m=xc(p,s,t)*p%t,h=xc(xc(xc(xc(xc(xc(m,c,t)*m%t,s,t)*p%t,n,t)*u%t,o,t)*f%t,r,t)*l%t,Jl,t);if(!Xl.eql(Xl.sqr(h),e))throw Error(`Cannot find square root`);return h}function Ll(e,...t){let n=Ql[e];if(n===void 0){let t=Ps(Uint8Array.from(e,e=>e.charCodeAt(0)));n=ec(t,t),Ql[e]=n}return Ps(ec(n,...t))}function Rl(e){let t=Zl.utils.normPrivateKeyToScalar(e),n=M.fromPrivateKey(t);return{scalar:n.hasEvenY()?t:j(-t),bytes:$l(n)}}function zl(e){ic(`x`,e,ql,Wl);let t=Il(tu(tu(e*e)*e+BigInt(7)));t%Jl!==Kl&&(t=tu(-t));let n=new M(e,t,ql);return n.assertValidity(),n}function Bl(...e){return j(ru(Ll(`BIP0340/challenge`,...e)))}function Vl(e){return Rl(e).bytes}function Hl(e,t,n=jr(32)){let r=$s(`message`,e),{bytes:i,scalar:a}=Rl(t),o=j(ru(Ll(`BIP0340/nonce`,eu(a^ru(Ll(`BIP0340/aux`,$s(`auxRand`,n,32)))),i,r)));if(o===Kl)throw Error(`sign failed: k is zero`);let{bytes:s,scalar:c}=Rl(o),l=Bl(s,i,r),u=new Uint8Array(64);if(u.set(s,0),u.set(eu(j(c+l*a)),32),!Ul(u,r,i))throw Error(`sign: Invalid signature produced`);return u}function Ul(e,t,n){let r=$s(`signature`,e,64),i=$s(`message`,t),a=$s(`publicKey`,n,32);try{let e=zl(ru(a)),t=ru(r.subarray(0,32));if(!rc(t,ql,Wl))return!1;let n=ru(r.subarray(32,64));if(!rc(n,ql,Gl))return!1;let o=nu(e,n,j(-Bl(eu(t),$l(e),i)));return!(!o||!o.hasEvenY()||o.toAffine().x!==t)}catch{return!1}}var Wl,Gl,Kl,ql,Jl,Yl,Xl,Zl,Ql,$l,eu,tu,j,M,nu,ru,iu,au,ou,su,cu,lu,uu=o((()=>{Ls(),Fr(),wl(),Pl(),Wc(),yc(),xl(),Wl=BigInt(`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f`),Gl=BigInt(`0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141`),Kl=BigInt(0),ql=BigInt(1),Jl=BigInt(2),Yl=(e,t)=>(e+t/Jl)/t,Xl=Mc(Wl,void 0,void 0,{sqrt:Il}),Zl=Cl({a:Kl,b:BigInt(7),Fp:Xl,n:Gl,Gx:BigInt(`55066263022277343669578718895168534326250603453777594175500187360389116729240`),Gy:BigInt(`32670510020758816978083085130507043184471273380659243275938904335757337482424`),h:BigInt(1),lowS:!0,endo:{beta:BigInt(`0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee`),splitScalar:e=>{let t=Gl,n=BigInt(`0x3086d221a7d46bcde86c90e49284eb15`),r=-ql*BigInt(`0xe4437ed6010e88286f547fa90abfe4c3`),i=BigInt(`0x114ca50f7a8e2f3f657c1108d9d44cfd8`),a=n,o=BigInt(`0x100000000000000000000000000000000`),s=Yl(a*e,t),c=Yl(-r*e,t),l=bc(e-s*n-c*i,t),u=bc(-s*r-c*a,t),d=l>o,f=u>o;if(d&&(l=t-l),f&&(u=t-u),l>o||u>o)throw Error(`splitScalar: Endomorphism failed, k=`+e);return{k1neg:d,k1:l,k2neg:f,k2:u}}}},Ps),Ql={},$l=e=>e.toRawBytes(!0).slice(1),eu=e=>Zs(e,32),tu=e=>bc(e,Wl),j=e=>bc(e,Gl),M=Zl.ProjectivePoint,nu=(e,t,n)=>M.BASE.multiplyAndAddUnsafe(e,t,n),ru=Ys,iu={getPublicKey:Vl,sign:Hl,verify:Ul,utils:{randomPrivateKey:Zl.utils.randomPrivateKey,lift_x:zl,pointToBytes:$l,numberToBytesBE:Zs,bytesToNumberBE:Ys,taggedHash:Ll,mod:bc}},au=jl(Xl,[[`0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7`,`0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581`,`0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262`,`0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c`],[`0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b`,`0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14`,`0x0000000000000000000000000000000000000000000000000000000000000001`],[`0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c`,`0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3`,`0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931`,`0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84`],[`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b`,`0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573`,`0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f`,`0x0000000000000000000000000000000000000000000000000000000000000001`]].map(e=>e.map(e=>BigInt(e)))),ou=pl(Xl,{A:BigInt(`0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533`),B:BigInt(`1771`),Z:Xl.create(BigInt(`-11`))}),su=Ml(Zl.ProjectivePoint,e=>{let{x:t,y:n}=ou(Xl.create(e[0]));return au(t,n)},{DST:`secp256k1_XMD:SHA-256_SSWU_RO_`,encodeDST:`secp256k1_XMD:SHA-256_SSWU_NU_`,p:Xl.ORDER,m:1,k:128,expand:`xmd`,hash:Ps}),cu=su.hashToCurve,lu=su.encodeToCurve}));Ft(),Lt(),On(),k(),hs();async function du({hash:e,signature:t}){let n=Pt(e)?e:kn(e),{secp256k1:r}=await ms(async()=>{let{secp256k1:e}=await Promise.resolve().then(()=>(uu(),Fl));return{secp256k1:e}},void 0);return`0x${(()=>{if(typeof t==`object`&&`r`in t&&`s`in t){let{r:e,s:n,v:i,yParity:a}=t,o=fu(Number(a??i));return new r.Signature(Tn(e),Tn(n)).addRecoveryBit(o)}let e=Pt(t)?t:kn(t);if(It(e)!==65)throw Error(`invalid signature length`);let n=fu(Dn(`0x${e.slice(130)}`));return r.Signature.fromCompact(e.substring(2,130)).addRecoveryBit(n)})().recoverPublicKey(n.substring(2)).toHex(!1)}`}function fu(e){if(e===0||e===1)return e;if(e===27)return 0;if(e===28)return 1;throw Error(`Invalid yParityOrV value`)}async function pu({hash:e,signature:t}){return us(await du({hash:e,signature:t}))}D(),Sa(),Un(),k();function mu(e,t=`hex`){let n=hu(e),r=ba(new Uint8Array(n.length));return n.encode(r),t===`hex`?jn(r.bytes):r.bytes}function hu(e){return Array.isArray(e)?gu(e.map(e=>hu(e))):_u(e)}function gu(e){let t=e.reduce((e,t)=>e+t.length,0),n=vu(t);return{length:t<=55?1+t:1+n+t,encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function _u(e){let t=typeof e==`string`?Rn(e):e,n=vu(t.length);return{length:t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length,encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function vu(e){if(e<2**8)return 1;if(e<2**16)return 2;if(e<2**24)return 3;if(e<2**32)return 4;throw new E(`Length is too large.`)}Ai(),Un(),k(),ri();function yu(e){let{chainId:t,nonce:n,to:r}=e,i=e.contractAddress??e.address,a=ni(ki([`0x05`,mu([t?O(t):`0x`,i,n?O(n):`0x`])]));return r===`bytes`?Rn(a):a}async function bu(e){let{authorization:t,signature:n}=e;return pu({hash:yu(t),signature:n??t})}$a(),to(),D(),_o();var xu=class extends E{constructor(e,{account:t,docsPath:n,chain:r,data:i,gas:a,gasPrice:o,maxFeePerGas:s,maxPriorityFeePerGas:c,nonce:l,to:u,value:d}){let f=so({from:t?.address,to:u,value:d!==void 0&&`${Qa(d)} ${r?.nativeCurrency?.symbol||`ETH`}`,data:i,gas:a,gasPrice:o!==void 0&&`${eo(o)} gwei`,maxFeePerGas:s!==void 0&&`${eo(s)} gwei`,maxPriorityFeePerGas:c!==void 0&&`${eo(c)} gwei`,nonce:l});super(e.shortMessage,{cause:e,docsPath:n,metaMessages:[...e.metaMessages?[...e.metaMessages,` `]:[],`Estimate Gas Arguments:`,f].filter(Boolean),name:`EstimateGasExecutionError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=e}},Su,Cu,wu,Tu,Eu,Du,Ou,ku,Au,ju,Mu,Nu,Pu=o((()=>{to(),D(),Su=class extends E{constructor({cause:e,message:t}={}){let n=t?.replace(`execution reverted: `,``)?.replace(`execution reverted`,``);super(`Execution reverted ${n?`with reason: ${n}`:`for an unknown reason`}.`,{cause:e,name:`ExecutionRevertedError`})}},Object.defineProperty(Su,`code`,{enumerable:!0,configurable:!0,writable:!0,value:3}),Object.defineProperty(Su,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/execution reverted|gas required exceeds allowance/}),Cu=class extends E{constructor({cause:e,maxFeePerGas:t}={}){super(`The fee cap (\`maxFeePerGas\`${t?` = ${eo(t)} gwei`:``}) cannot be higher than the maximum allowed value (2^256-1).`,{cause:e,name:`FeeCapTooHighError`})}},Object.defineProperty(Cu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max fee per gas higher than 2\^256-1|fee cap higher than 2\^256-1/}),wu=class extends E{constructor({cause:e,maxFeePerGas:t}={}){super(`The fee cap (\`maxFeePerGas\`${t?` = ${eo(t)}`:``} gwei) cannot be lower than the block base fee.`,{cause:e,name:`FeeCapTooLowError`})}},Object.defineProperty(wu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max fee per gas less than block base fee|fee cap less than block base fee|transaction is outdated/}),Tu=class extends E{constructor({cause:e,nonce:t}={}){super(`Nonce provided for the transaction ${t?`(${t}) `:``}is higher than the next one expected.`,{cause:e,name:`NonceTooHighError`})}},Object.defineProperty(Tu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce too high/}),Eu=class extends E{constructor({cause:e,nonce:t}={}){super([`Nonce provided for the transaction ${t?`(${t}) `:``}is lower than the current nonce of the account.`,"Try increasing the nonce or find the latest nonce with `getTransactionCount`."].join(` -`),{cause:e,name:`NonceTooLowError`})}},Object.defineProperty(Eu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce too low|transaction already imported|already known/}),Du=class extends E{constructor({cause:e,nonce:t}={}){super(`Nonce provided for the transaction ${t?`(${t}) `:``}exceeds the maximum allowed nonce.`,{cause:e,name:`NonceMaxValueError`})}},Object.defineProperty(Du,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/nonce has max value/}),Ou=class extends E{constructor({cause:e}={}){super([`The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account.`].join(` -`),{cause:e,metaMessages:[`This error could arise when the account does not have enough funds to:`,` - pay for the total gas fee,`,` - pay for the value to send.`,` `,"The cost of the transaction is calculated as `gas * gas fee + value`, where:"," - `gas` is the amount of gas needed for transaction to execute,"," - `gas fee` is the gas fee,"," - `value` is the amount of ether to send to the recipient."],name:`InsufficientFundsError`})}},Object.defineProperty(Ou,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/insufficient funds|exceeds transaction sender account balance/}),ku=class extends E{constructor({cause:e,gas:t}={}){super(`The amount of gas ${t?`(${t}) `:``}provided for the transaction exceeds the limit allowed for the block.`,{cause:e,name:`IntrinsicGasTooHighError`})}},Object.defineProperty(ku,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/intrinsic gas too high|gas limit reached/}),Au=class extends E{constructor({cause:e,gas:t}={}){super(`The amount of gas ${t?`(${t}) `:``}provided for the transaction is too low.`,{cause:e,name:`IntrinsicGasTooLowError`})}},Object.defineProperty(Au,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/intrinsic gas too low/}),ju=class extends E{constructor({cause:e}){super(`The transaction type is not supported for this chain.`,{cause:e,name:`TransactionTypeNotSupportedError`})}},Object.defineProperty(ju,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/transaction type not valid/}),Mu=class extends E{constructor({cause:e,maxPriorityFeePerGas:t,maxFeePerGas:n}={}){super([`The provided tip (\`maxPriorityFeePerGas\`${t?` = ${eo(t)} gwei`:``}) cannot be higher than the fee cap (\`maxFeePerGas\`${n?` = ${eo(n)} gwei`:``}).`].join(` -`),{cause:e,name:`TipAboveFeeCapError`})}},Object.defineProperty(Mu,`nodeMessage`,{enumerable:!0,configurable:!0,writable:!0,value:/max priority fee per gas higher than max fee per gas|tip higher than fee cap/}),Nu=class extends E{constructor({cause:e}){super(`An error occurred while executing: ${e?.shortMessage}`,{cause:e,name:`UnknownNodeError`})}}}));function Fu(e,t){let n=(e.details||``).toLowerCase(),r=e instanceof E?e.walk(e=>e?.code===Su.code):e;return r instanceof E?new Su({cause:e,message:r.details}):Su.nodeMessage.test(n)?new Su({cause:e,message:e.details}):Cu.nodeMessage.test(n)?new Cu({cause:e,maxFeePerGas:t?.maxFeePerGas}):wu.nodeMessage.test(n)?new wu({cause:e,maxFeePerGas:t?.maxFeePerGas}):Tu.nodeMessage.test(n)?new Tu({cause:e,nonce:t?.nonce}):Eu.nodeMessage.test(n)?new Eu({cause:e,nonce:t?.nonce}):Du.nodeMessage.test(n)?new Du({cause:e,nonce:t?.nonce}):Ou.nodeMessage.test(n)?new Ou({cause:e}):ku.nodeMessage.test(n)?new ku({cause:e,gas:t?.gas}):Au.nodeMessage.test(n)?new Au({cause:e,gas:t?.gas}):ju.nodeMessage.test(n)?new ju({cause:e}):Mu.nodeMessage.test(n)?new Mu({cause:e,maxFeePerGas:t?.maxFeePerGas,maxPriorityFeePerGas:t?.maxPriorityFeePerGas}):new Nu({cause:e})}var Iu=o((()=>{D(),Pu()}));Pu(),Iu();function Lu(e,{docsPath:t,...n}){return new xu((()=>{let t=Fu(e,n);return t instanceof Nu?e:t})(),{docsPath:t,...n})}function Ru(e,{format:t}){if(!t)return{};let n={};function r(t){let i=Object.keys(t);for(let a of i)a in e&&(n[a]=e[a]),t[a]&&typeof t[a]==`object`&&!Array.isArray(t[a])&&r(t[a])}return r(t(e||{})),n}var zu=o((()=>{}));function Bu(e,t){return({exclude:n,format:r})=>({exclude:n,format:(e,i)=>{let a=t(e,i);if(n)for(let e of n)delete a[e];return{...a,...r(e,i)}},type:e})}var Vu=o((()=>{}));function Hu(e,t){let n={};return e.authorizationList!==void 0&&(n.authorizationList=Uu(e.authorizationList)),e.accessList!==void 0&&(n.accessList=e.accessList),e.blobVersionedHashes!==void 0&&(n.blobVersionedHashes=e.blobVersionedHashes),e.blobs!==void 0&&(typeof e.blobs[0]==`string`?n.blobs=e.blobs:n.blobs=e.blobs.map(e=>jn(e))),e.data!==void 0&&(n.data=e.data),e.account&&(n.from=e.account.address),e.from!==void 0&&(n.from=e.from),e.gas!==void 0&&(n.gas=O(e.gas)),e.gasPrice!==void 0&&(n.gasPrice=O(e.gasPrice)),e.maxFeePerBlobGas!==void 0&&(n.maxFeePerBlobGas=O(e.maxFeePerBlobGas)),e.maxFeePerGas!==void 0&&(n.maxFeePerGas=O(e.maxFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(n.maxPriorityFeePerGas=O(e.maxPriorityFeePerGas)),e.nonce!==void 0&&(n.nonce=O(e.nonce)),e.to!==void 0&&(n.to=e.to),e.type!==void 0&&(n.type=Wu[e.type]),e.value!==void 0&&(n.value=O(e.value)),n}function Uu(e){return e.map(e=>({address:e.address,r:e.r?O(BigInt(e.r)):e.r,s:e.s?O(BigInt(e.s)):e.s,chainId:O(e.chainId),nonce:O(e.nonce),...e.yParity===void 0?{}:{yParity:O(e.yParity)},...e.v!==void 0&&e.yParity===void 0?{v:O(e.v)}:{}}))}var Wu,Gu,Ku=o((()=>{k(),Vu(),Wu={legacy:`0x0`,eip2930:`0x1`,eip1559:`0x2`,eip4844:`0x3`,eip7702:`0x4`},Gu=Bu(`transactionRequest`,Hu)}));function qu(e){if(!(!e||e.length===0))return e.reduce((e,{slot:t,value:n})=>{if(t.length!==66)throw new dn({size:t.length,targetSize:66,type:`hex`});if(n.length!==66)throw new dn({size:n.length,targetSize:66,type:`hex`});return e[t]=n,e},{})}function Ju(e){let{balance:t,nonce:n,state:r,stateDiff:i,code:a}=e,o={};if(a!==void 0&&(o.code=a),t!==void 0&&(o.balance=O(t)),n!==void 0&&(o.nonce=O(n)),r!==void 0&&(o.state=qu(r)),i!==void 0){if(o.state)throw new ao;o.stateDiff=qu(i)}return o}function Yu(e){if(!e)return;let t={};for(let{address:n,...r}of e){if(!Ci(n,{strict:!1}))throw new hi({address:n});if(t[n])throw new io({address:n});t[n]=Ju(r)}return t}var Xu=o((()=>{gi(),fn(),oo(),Ei(),k()})),Zu,Qu,$u=o((()=>{2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n)),Zu=2n**16n-1n,Qu=2n**256n-1n}));function ed(e){let{account:t,maxFeePerGas:n,maxPriorityFeePerGas:r,to:i}=e,a=t?aa(t):void 0;if(a&&!Ci(a.address))throw new hi({address:a.address});if(i&&!Ci(i))throw new hi({address:i});if(n&&n>Qu)throw new Cu({maxFeePerGas:n});if(r&&n&&r>n)throw new Mu({maxFeePerGas:n,maxPriorityFeePerGas:r})}var td=o((()=>{oa(),$u(),gi(),Pu(),Ei()}));to(),D();var nd=class extends E{constructor(){super("`baseFeeMultiplier` must be greater than 1.",{name:`BaseFeeScalarError`})}},rd=class extends E{constructor(){super(`Chain does not support EIP-1559 fees.`,{name:`Eip1559FeesNotSupportedError`})}},id=class extends E{constructor({maxPriorityFeePerGas:e}){super(`\`maxFeePerGas\` cannot be less than the \`maxPriorityFeePerGas\` (${eo(e)} gwei).`,{name:`MaxFeePerGasTooLowError`})}};D();var ad=class extends E{constructor({blockHash:e,blockNumber:t}){let n=`Block`;e&&(n=`Block at hash "${e}"`),t&&(n=`Block at number "${t}"`),super(`${n} could not be found.`,{name:`BlockNotFoundError`})}};On(),Vu();var od={"0x0":`legacy`,"0x1":`eip2930`,"0x2":`eip1559`,"0x3":`eip4844`,"0x4":`eip7702`};function sd(e,t){let n={...e,blockHash:e.blockHash?e.blockHash:null,blockNumber:e.blockNumber?BigInt(e.blockNumber):null,chainId:e.chainId?Dn(e.chainId):void 0,gas:e.gas?BigInt(e.gas):void 0,gasPrice:e.gasPrice?BigInt(e.gasPrice):void 0,maxFeePerBlobGas:e.maxFeePerBlobGas?BigInt(e.maxFeePerBlobGas):void 0,maxFeePerGas:e.maxFeePerGas?BigInt(e.maxFeePerGas):void 0,maxPriorityFeePerGas:e.maxPriorityFeePerGas?BigInt(e.maxPriorityFeePerGas):void 0,nonce:e.nonce?Dn(e.nonce):void 0,to:e.to?e.to:null,transactionIndex:e.transactionIndex?Number(e.transactionIndex):null,type:e.type?od[e.type]:void 0,typeHex:e.type?e.type:void 0,value:e.value?BigInt(e.value):void 0,v:e.v?BigInt(e.v):void 0};return e.authorizationList&&(n.authorizationList=ld(e.authorizationList)),n.yParity=(()=>{if(e.yParity)return Number(e.yParity);if(typeof n.v==`bigint`){if(n.v===0n||n.v===27n)return 0;if(n.v===1n||n.v===28n)return 1;if(n.v>=35n)return n.v%2n==0n?1:0}})(),n.type===`legacy`&&(delete n.accessList,delete n.maxFeePerBlobGas,delete n.maxFeePerGas,delete n.maxPriorityFeePerGas,delete n.yParity),n.type===`eip2930`&&(delete n.maxFeePerBlobGas,delete n.maxFeePerGas,delete n.maxPriorityFeePerGas),n.type===`eip1559`&&delete n.maxFeePerBlobGas,n}var cd=Bu(`transaction`,sd);function ld(e){return e.map(e=>({address:e.address,chainId:Number(e.chainId),nonce:Number(e.nonce),r:e.r,s:e.s,yParity:Number(e.yParity)}))}Vu();function ud(e,t){let n=(e.transactions??[]).map(e=>typeof e==`string`?e:sd(e));return{...e,baseFeePerGas:e.baseFeePerGas?BigInt(e.baseFeePerGas):null,blobGasUsed:e.blobGasUsed?BigInt(e.blobGasUsed):void 0,difficulty:e.difficulty?BigInt(e.difficulty):void 0,excessBlobGas:e.excessBlobGas?BigInt(e.excessBlobGas):void 0,gasLimit:e.gasLimit?BigInt(e.gasLimit):void 0,gasUsed:e.gasUsed?BigInt(e.gasUsed):void 0,hash:e.hash?e.hash:null,logsBloom:e.logsBloom?e.logsBloom:null,nonce:e.nonce?e.nonce:null,number:e.number?BigInt(e.number):null,size:e.size?BigInt(e.size):void 0,timestamp:e.timestamp?BigInt(e.timestamp):void 0,transactions:n,totalDifficulty:e.totalDifficulty?BigInt(e.totalDifficulty):null}}var dd=Bu(`block`,ud);k();async function fd(e,{blockHash:t,blockNumber:n,blockTag:r=e.experimental_blockTag??`latest`,includeTransactions:i}={}){let a=i??!1,o=n===void 0?void 0:O(n),s=null;if(s=t?await e.request({method:`eth_getBlockByHash`,params:[t,a]},{dedupe:!0}):await e.request({method:`eth_getBlockByNumber`,params:[o||r,a]},{dedupe:!!o}),!s)throw new ad({blockHash:t,blockNumber:n});return(e.chain?.formatters?.block?.format||ud)(s,`getBlock`)}async function pd(e){let t=await e.request({method:`eth_gasPrice`});return BigInt(t)}On();async function md(e,t){let{block:n,chain:r=e.chain,request:i}=t||{};try{let t=r?.fees?.maxPriorityFeePerGas??r?.fees?.defaultPriorityFee;if(typeof t==`function`){let r=await t({block:n||await T(e,fd,`getBlock`)({}),client:e,request:i});if(r===null)throw Error();return r}return t===void 0?Tn(await e.request({method:`eth_maxPriorityFeePerGas`})):t}catch{let[t,r]=await Promise.all([n?Promise.resolve(n):T(e,fd,`getBlock`)({}),T(e,pd,`getGasPrice`)({})]);if(typeof t.baseFeePerGas!=`bigint`)throw new rd;let i=r-t.baseFeePerGas;return i<0n?0n:i}}async function hd(e,t){let{block:n,chain:r=e.chain,request:i,type:a=`eip1559`}=t||{},o=await(async()=>typeof r?.fees?.baseFeeMultiplier==`function`?r.fees.baseFeeMultiplier({block:n,client:e,request:i}):r?.fees?.baseFeeMultiplier??1.2)();if(o<1)throw new nd;let s=10**(o.toString().split(`.`)[1]?.length??0),c=e=>e*BigInt(Math.ceil(o*s))/BigInt(s),l=n||await T(e,fd,`getBlock`)({});if(typeof r?.fees?.estimateFeesPerGas==`function`){let t=await r.fees.estimateFeesPerGas({block:n,client:e,multiply:c,request:i,type:a});if(t!==null)return t}if(a===`eip1559`){if(typeof l.baseFeePerGas!=`bigint`)throw new rd;let t=typeof i?.maxPriorityFeePerGas==`bigint`?i.maxPriorityFeePerGas:await md(e,{block:l,chain:r,request:i}),n=c(l.baseFeePerGas);return{maxFeePerGas:i?.maxFeePerGas??n+t,maxPriorityFeePerGas:t}}return{gasPrice:i?.gasPrice??c(await T(e,pd,`getGasPrice`)({}))}}On(),k();async function gd(e,{address:t,blockTag:n=`latest`,blockNumber:r}){return Dn(await e.request({method:`eth_getTransactionCount`,params:[t,typeof r==`bigint`?O(r):n]},{dedupe:!!r}))}Ku(),Un(),k();function _d(e){let{kzg:t}=e,n=e.to??(typeof e.blobs[0]==`string`?`hex`:`bytes`),r=typeof e.blobs[0]==`string`?e.blobs.map(e=>Rn(e)):e.blobs,i=[];for(let e of r)i.push(Uint8Array.from(t.blobToKzgCommitment(e)));return n===`bytes`?i:i.map(e=>jn(e))}Un(),k();function vd(e){let{kzg:t}=e,n=e.to??(typeof e.blobs[0]==`string`?`hex`:`bytes`),r=typeof e.blobs[0]==`string`?e.blobs.map(e=>Rn(e)):e.blobs,i=typeof e.commitments[0]==`string`?e.commitments.map(e=>Rn(e)):e.commitments,a=[];for(let e=0;ejn(e))}Ls();var yd=Ps;Ft(),Un(),k();function bd(e,t){let n=t||`hex`,r=yd(Pt(e,{strict:!1})?Fn(e):e);return n===`bytes`?r:kn(r)}k();function xd(e){let{commitment:t,version:n=1}=e,r=e.to??(typeof t==`string`?`hex`:`bytes`),i=bd(t,`bytes`);return i.set([n],0),r===`bytes`?i:jn(i)}function Sd(e){let{commitments:t,version:n}=e,r=e.to??(typeof t[0]==`string`?`hex`:`bytes`),i=[];for(let e of t)i.push(xd({commitment:e,to:r,version:n}));return i}var Cd=6,wd=4096,Td=32*wd,Ed=Td*Cd-1-1*wd*Cd;D();var Dd=class extends E{constructor({maxSize:e,size:t}){super(`Blob size is too large.`,{metaMessages:[`Max: ${e} bytes`,`Given: ${t} bytes`],name:`BlobSizeTooLargeError`})}},Od=class extends E{constructor(){super(`Blob data must not be empty.`,{name:`EmptyBlobError`})}},kd=class extends E{constructor({hash:e,size:t}){super(`Versioned hash "${e}" size is invalid.`,{metaMessages:[`Expected: 32`,`Received: ${t}`],name:`InvalidVersionedHashSizeError`})}},Ad=class extends E{constructor({hash:e,version:t}){super(`Versioned hash "${e}" version is invalid.`,{metaMessages:[`Expected: 1`,`Received: ${t}`],name:`InvalidVersionedHashVersionError`})}};Sa(),Lt(),Un(),k();function jd(e){let t=e.to??(typeof e.data==`string`?`hex`:`bytes`),n=typeof e.data==`string`?Rn(e.data):e.data,r=It(n);if(!r)throw new Od;if(r>761855)throw new Dd({maxSize:Ed,size:r});let i=[],a=!0,o=0;for(;a;){let e=ba(new Uint8Array(Td)),t=0;for(;te.bytes):i.map(e=>jn(e.bytes))}function Md(e){let{data:t,kzg:n,to:r}=e,i=e.blobs??jd({data:t,to:r}),a=e.commitments??_d({blobs:i,kzg:n,to:r}),o=e.proofs??vd({blobs:i,commitments:a,kzg:n,to:r}),s=[];for(let e=0;e{let t=Fu(e,n);return t instanceof Nu?e:t})(),{docsPath:t,...n})}On();async function Fd(e){return Dn(await e.request({method:`eth_chainId`},{dedupe:!0}))}oa(),zu(),td();async function Id(e,t){let{account:n=e.account,accessList:r,authorizationList:i,chain:a=e.chain,blobVersionedHashes:o,blobs:s,data:c,gas:l,gasPrice:u,maxFeePerBlobGas:d,maxFeePerGas:f,maxPriorityFeePerGas:p,nonce:m,nonceManager:h,to:g,type:_,value:v,...y}=t,b=await(async()=>{if(!n||!h||m!==void 0)return m;let t=aa(n),r=a?a.id:await T(e,Fd,`getChainId`)({});return await h.consume({address:t.address,chainId:r,client:e})})();ed(t);let x=a?.formatters?.transactionRequest?.format,S=(x||Hu)({...Ru(y,{format:x}),account:n?aa(n):void 0,accessList:r,authorizationList:i,blobs:s,blobVersionedHashes:o,data:c,gas:l,gasPrice:u,maxFeePerBlobGas:d,maxFeePerGas:f,maxPriorityFeePerGas:p,nonce:b,to:g,type:_,value:v},`fillTransaction`);try{let n=await e.request({method:`eth_fillTransaction`,params:[S]}),r=(a?.formatters?.transaction?.format||sd)(n.tx);delete r.blockHash,delete r.blockNumber,delete r.r,delete r.s,delete r.transactionIndex,delete r.v,delete r.yParity,r.data=r.input,r.gas&&=t.gas??r.gas,r.gasPrice&&=t.gasPrice??r.gasPrice,r.maxFeePerBlobGas&&=t.maxFeePerBlobGas??r.maxFeePerBlobGas,r.maxFeePerGas&&=t.maxFeePerGas??r.maxFeePerGas,r.maxPriorityFeePerGas&&=t.maxPriorityFeePerGas??r.maxPriorityFeePerGas,r.nonce&&=t.nonce??r.nonce;let i=await(async()=>{if(typeof a?.fees?.baseFeeMultiplier==`function`){let n=await T(e,fd,`getBlock`)({});return a.fees.baseFeeMultiplier({block:n,client:e,request:t})}return a?.fees?.baseFeeMultiplier??1.2})();if(i<1)throw new nd;let o=10**(i.toString().split(`.`)[1]?.length??0),s=e=>e*BigInt(Math.ceil(i*o))/BigInt(o);return r.maxFeePerGas&&!t.maxFeePerGas&&(r.maxFeePerGas=s(r.maxFeePerGas)),r.gasPrice&&!t.gasPrice&&(r.gasPrice=s(r.gasPrice)),{raw:n.raw,transaction:{from:S.from,...r}}}catch(n){throw Pd(n,{...t,chain:e.chain})}}oa(),vi(),td();var Ld=[`blobVersionedHashes`,`chainId`,`fees`,`gas`,`nonce`,`type`],Rd=new Map,N=new _i(128);async function zd(e,t){let n=t;n.account??=e.account,n.parameters??=Ld;let{account:r,chain:i=e.chain,nonceManager:a,parameters:o}=n,s=(()=>{if(typeof i?.prepareTransactionRequest==`function`)return{fn:i.prepareTransactionRequest,runAt:[`beforeFillTransaction`]};if(Array.isArray(i?.prepareTransactionRequest))return{fn:i.prepareTransactionRequest[0],runAt:i.prepareTransactionRequest[1].runAt}})(),c;async function l(){return c||(n.chainId===void 0?i?i.id:(c=await T(e,Fd,`getChainId`)({}),c):n.chainId)}let u=r&&aa(r),d=n.nonce;if(o.includes(`nonce`)&&d===void 0&&u&&a){let t=await l();d=await a.consume({address:u.address,chainId:t,client:e})}s?.fn&&s.runAt?.includes(`beforeFillTransaction`)&&(n=await s.fn({...n,chain:i},{phase:`beforeFillTransaction`}),d??=n.nonce);let f=!((o.includes(`blobVersionedHashes`)||o.includes(`sidecars`))&&n.kzg&&n.blobs||N.get(e.uid)===!1||![`fees`,`gas`].some(e=>o.includes(e)))&&(o.includes(`chainId`)&&typeof n.chainId!=`number`||o.includes(`nonce`)&&typeof d!=`number`||o.includes(`fees`)&&typeof n.gasPrice!=`bigint`&&(typeof n.maxFeePerGas!=`bigint`||typeof n.maxPriorityFeePerGas!=`bigint`)||o.includes(`gas`)&&typeof n.gas!=`bigint`)?await T(e,Id,`fillTransaction`)({...n,nonce:d}).then(t=>{let{chainId:r,from:i,gas:a,gasPrice:o,nonce:s,maxFeePerBlobGas:c,maxFeePerGas:l,maxPriorityFeePerGas:u,type:d,...f}=t.transaction;return N.set(e.uid,!0),{...n,...i?{from:i}:{},...d&&!n.type?{type:d}:{},...r===void 0?{}:{chainId:r},...a===void 0?{}:{gas:a},...o===void 0?{}:{gasPrice:o},...s===void 0?{}:{nonce:s},...c!==void 0&&n.type!==`legacy`&&n.type!==`eip2930`?{maxFeePerBlobGas:c}:{},...l!==void 0&&n.type!==`legacy`&&n.type!==`eip2930`?{maxFeePerGas:l}:{},...u!==void 0&&n.type!==`legacy`&&n.type!==`eip2930`?{maxPriorityFeePerGas:u}:{},...`nonceKey`in f&&f.nonceKey!==void 0?{nonceKey:f.nonceKey}:{}}}).catch(t=>{let r=t;return r.name===`TransactionExecutionError`&&r.walk?.(e=>{let t=e;return t.name===`MethodNotFoundRpcError`||t.name===`MethodNotSupportedRpcError`||t.message?.includes(`eth_fillTransaction is not available`)})&&N.set(e.uid,!1),n}):n;d??=f.nonce,n={...f,...u?{from:u?.address}:{},...d?{nonce:d}:{}};let{blobs:p,gas:m,kzg:h,type:g}=n;s?.fn&&s.runAt?.includes(`beforeFillParameters`)&&(n=await s.fn({...n,chain:i},{phase:`beforeFillParameters`}));let _;async function v(){return _||(_=await T(e,fd,`getBlock`)({blockTag:`latest`}),_)}if(o.includes(`nonce`)&&d===void 0&&u&&!a&&(n.nonce=await T(e,gd,`getTransactionCount`)({address:u.address,blockTag:`pending`})),(o.includes(`blobVersionedHashes`)||o.includes(`sidecars`))&&p&&h){let e=_d({blobs:p,kzg:h});if(o.includes(`blobVersionedHashes`)){let t=Sd({commitments:e,to:`hex`});n.blobVersionedHashes=t}if(o.includes(`sidecars`)){let t=Md({blobs:p,commitments:e,proofs:vd({blobs:p,commitments:e,kzg:h}),to:`hex`});n.sidecars=t}}if(o.includes(`chainId`)&&(n.chainId=await l()),(o.includes(`fees`)||o.includes(`type`))&&g===void 0)try{n.type=Nd(n)}catch{let t=Rd.get(e.uid);t===void 0&&(t=typeof(await v())?.baseFeePerGas==`bigint`,Rd.set(e.uid,t)),n.type=t?`eip1559`:`legacy`}if(o.includes(`fees`))if(n.type!==`legacy`&&n.type!==`eip2930`){if(n.maxFeePerGas===void 0||n.maxPriorityFeePerGas===void 0){let{maxFeePerGas:t,maxPriorityFeePerGas:r}=await hd(e,{block:await v(),chain:i,request:n});if(n.maxPriorityFeePerGas===void 0&&n.maxFeePerGas&&n.maxFeePerGas{if(Array.isArray(r))return r;if(i?.type!==`local`)return[`blobVersionedHashes`]})();try{let n=await(async()=>{if(t.to)return t.to;if(t.authorizationList&&t.authorizationList.length>0)return await bu({authorization:t.authorizationList[0]}).catch(()=>{throw new E("`to` is required. Could not infer from `authorizationList`")})})(),{accessList:o,authorizationList:s,blobs:c,blobVersionedHashes:l,blockNumber:u,blockTag:d,data:f,gas:p,gasPrice:m,maxFeePerBlobGas:h,maxFeePerGas:g,maxPriorityFeePerGas:_,nonce:v,value:y,stateOverride:b,...x}=r?await zd(e,{...t,parameters:a,to:n}):t;if(p&&t.gas!==p)return p;let S=(typeof u==`bigint`?O(u):void 0)||d,ee=Yu(b);ed(t);let C=e.chain?.formatters?.transactionRequest?.format,te=(C||Hu)({...Ru(x,{format:C}),account:i,accessList:o,authorizationList:s,blobs:c,blobVersionedHashes:l,data:f,gasPrice:m,maxFeePerBlobGas:h,maxFeePerGas:g,maxPriorityFeePerGas:_,nonce:v,to:n,value:y},`estimateGas`);return BigInt(await e.request({method:`eth_estimateGas`,params:ee?[te,S??e.experimental_blockTag??`latest`,ee]:S?[te,S]:[te]}))}catch(n){throw Lu(n,{...t,account:i,chain:e.chain})}}function Vd(e,t){if(!Ci(e,{strict:!1}))throw new hi({address:e});if(!Ci(t,{strict:!1}))throw new hi({address:t});return e.toLowerCase()===t.toLowerCase()}var Hd=o((()=>{gi(),Ei()}));function Ud(e,{args:t,eventName:n}={}){return{...e,blockHash:e.blockHash?e.blockHash:null,blockNumber:e.blockNumber?BigInt(e.blockNumber):null,blockTimestamp:e.blockTimestamp?BigInt(e.blockTimestamp):e.blockTimestamp===null?null:void 0,logIndex:e.logIndex?Number(e.logIndex):null,transactionHash:e.transactionHash?e.transactionHash:null,transactionIndex:e.transactionIndex?Number(e.transactionIndex):null,...n?{args:t,eventName:n}:{}}}function Wd(e){let{abi:t,args:n,functionName:r,data:i}=e,a=t[0];if(r){let e=ta({abi:t,args:n,name:r});if(!e)throw new Qt(r,{docsPath:Gd});a=e}if(a.type!==`function`)throw new Qt(void 0,{docsPath:Gd});if(!a.outputs)throw new $t(a.name,{docsPath:Gd});let o=Oa(a.outputs,i);if(o&&o.length>1)return o;if(o&&o.length===1)return o[0]}var Gd,Kd=o((()=>{cn(),Ba(),ia(),Gd=`/docs/contract/decodeFunctionResult`})),qd,Jd=o((()=>{qd=`0.1.1`}));function Yd(){return qd}var Xd=o((()=>{Jd()}));function Zd(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause?Zd(e.cause,t):t?null:e}var P,Qd=o((()=>{Xd(),P=class e extends Error{static setStaticOptions(t){e.prototype.docsOrigin=t.docsOrigin,e.prototype.showVersion=t.showVersion,e.prototype.version=t.version}constructor(t,n={}){let r=(()=>{if(n.cause instanceof e){if(n.cause.details)return n.cause.details;if(n.cause.shortMessage)return n.cause.shortMessage}return n.cause&&`details`in n.cause&&typeof n.cause.details==`string`?n.cause.details:n.cause?.message?n.cause.message:n.details})(),i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=n.docsOrigin??e.prototype.docsOrigin,o=`${a}${i??``}`,s=!!(n.version??e.prototype.showVersion),c=n.version??e.prototype.version,l=[t||`An error occurred.`,...n.metaMessages?[``,...n.metaMessages]:[],...r||i||s?[``,r?`Details: ${r}`:void 0,i?`See: ${o}`:void 0,s?`Version: ${c}`:void 0]:[]].filter(e=>typeof e==`string`).join(` -`);super(l,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsOrigin`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`showVersion`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.cause=n.cause,this.details=r,this.docs=o,this.docsOrigin=a,this.docsPath=i,this.shortMessage=t,this.showVersion=s,this.version=c}walk(e){return Zd(this,e)}},Object.defineProperty(P,`defaultStaticOptions`,{enumerable:!0,configurable:!0,writable:!0,value:{docsOrigin:`https://oxlib.sh`,showVersion:!1,version:`ox@${Yd()}`}}),P.setStaticOptions(P.defaultStaticOptions)}));function $d(e,t){if(Ef(e)>t)throw new zf({givenSize:Ef(e),maxSize:t})}function ef(e,t){if(typeof t==`number`&&t>0&&t>Ef(e)-1)throw new Bf({offset:t,position:`start`,size:Ef(e)})}function tf(e,t,n){if(typeof t==`number`&&typeof n==`number`&&Ef(e)!==n-t)throw new Bf({offset:n,position:`end`,size:Ef(e)})}function nf(e){if(e>=of.zero&&e<=of.nine)return e-of.zero;if(e>=of.A&&e<=of.F)return e-(of.A-10);if(e>=of.a&&e<=of.f)return e-(of.a-10)}function rf(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;if(e.length>r)throw new Vf({size:e.length,targetSize:r,type:`Bytes`});let i=new Uint8Array(r);for(let t=0;t{Hf(),of={zero:48,nine:57,A:65,F:70,a:97,f:102}}));function cf(e,t){if(Qf(e)>t)throw new up({givenSize:Qf(e),maxSize:t})}function lf(e,t){if(typeof t==`number`&&t>0&&t>Qf(e)-1)throw new dp({offset:t,position:`start`,size:Qf(e)})}function uf(e,t,n){if(typeof t==`number`&&typeof n==`number`&&Qf(e)!==n-t)throw new dp({offset:n,position:`end`,size:Qf(e)})}function df(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;let i=e.replace(`0x`,``);if(i.length>r*2)throw new fp({size:Math.ceil(i.length/2),targetSize:r,type:`Hex`});return`0x${i[n===`right`?`padEnd`:`padStart`](r*2,`0`)}`}function ff(e,t={}){let{dir:n=`left`}=t,r=e.replace(`0x`,``),i=0;for(let e=0;e{pp()}));function mf(e,t,n){return JSON.stringify(e,(e,n)=>typeof t==`function`?t(e,n):typeof n==`bigint`?n.toString()+hf:n,n)}var hf,gf=o((()=>{hf=`#__bigint`}));function _f(e){if(!(e instanceof Uint8Array)&&(!e||typeof e!=`object`||!(`BYTES_PER_ELEMENT`in e)||e.BYTES_PER_ELEMENT!==1||e.constructor.name!==`Uint8Array`))throw new Rf(e)}function vf(...e){let t=0;for(let n of e)t+=n.length;let n=new Uint8Array(t);for(let t=0,r=0;t1||r[0]>1)throw new Lf(r);return!!r[0]}function Af(e,t={}){let{size:n}=t;return n!==void 0&&$d(e,n),tp(qf(e,t),t)}function jf(e,t={}){let{size:n}=t,r=e;return n!==void 0&&($d(r,n),r=Nf(r)),Ff.decode(r)}function Mf(e){return af(e,{dir:`left`})}function Nf(e){return af(e,{dir:`right`})}function Pf(e){try{return _f(e),!0}catch{return!1}}var Ff,If,Lf,Rf,zf,Bf,Vf,Hf=o((()=>{yc(),Qd(),pp(),sf(),pf(),gf(),Ff=new TextDecoder,If=new TextEncoder,Lf=class extends P{constructor(e){super(`Bytes value \`${e}\` is not a valid boolean.`,{metaMessages:["The bytes array must contain a single byte of either a `0` or `1` value."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.InvalidBytesBooleanError`})}},Rf=class extends P{constructor(e){super(`Value \`${typeof e==`object`?mf(e):e}\` of type \`${typeof e}\` is an invalid Bytes value.`,{metaMessages:["Bytes values must be of type `Bytes`."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.InvalidBytesTypeError`})}},zf=class extends P{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SizeOverflowError`})}},Bf=class extends P{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SliceOffsetOutOfBoundsError`})}},Vf=class extends P{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Bytes.SizeExceedsPaddingSizeError`})}}}));function Uf(e,t={}){let{strict:n=!1}=t;if(!e||typeof e!=`string`)throw new sp(e);if(n&&!/^0x[0-9a-fA-F]*$/.test(e)||!e.startsWith(`0x`))throw new cp(e)}function Wf(...e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}function Gf(e){return e instanceof Uint8Array?qf(e):Array.isArray(e)?qf(new Uint8Array(e)):e}function Kf(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(cf(n,t.size),Yf(n,t.size)):n}function qf(e,t={}){let n=``;for(let t=0;ta||i>1n?r:r-a-1n}function tp(e,t={}){let{signed:n,size:r}=t;return Number(!n&&!r?e:ep(e,t))}function np(e,t={}){let{size:n}=t,r=xf(e);return n&&($d(r,n),r=Nf(r)),new TextDecoder().decode(r)}function rp(e,t={}){let{strict:n=!1}=t;try{return Uf(e,{strict:n}),!0}catch{return!1}}var ip,ap,op,sp,cp,lp,up,dp,fp,pp=o((()=>{Hf(),Qd(),sf(),pf(),gf(),ip=new TextEncoder,ap=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`)),op=class extends P{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number \`${i}\` is not in safe${r?` ${r*8}-bit`:``}${n?` signed`:` unsigned`} integer range ${e?`(\`${t}\` to \`${e}\`)`:`(above \`${t}\`)`}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.IntegerOutOfRangeError`})}},sp=class extends P{constructor(e){super(`Value \`${typeof e==`object`?mf(e):e}\` of type \`${typeof e}\` is an invalid hex type.`,{metaMessages:['Hex types must be represented as `"0x${string}"`.']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexTypeError`})}},cp=class extends P{constructor(e){super(`Value \`${e}\` is an invalid hex value.`,{metaMessages:['Hex values must start with `"0x"` and contain only hexadecimal characters (0-9, a-f, A-F).']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexValueError`})}},lp=class extends P{constructor(e){super(`Hex value \`"${e}"\` is an odd length (${e.length-2} nibbles).`,{metaMessages:[`It must be an even length.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidLengthError`})}},up=class extends P{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeOverflowError`})}},dp=class extends P{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SliceOffsetOutOfBoundsError`})}},fp=class extends P{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeExceedsPaddingSizeError`})}}}));function mp(e){return{address:e.address,amount:F(e.amount),index:F(e.index),validatorIndex:F(e.validatorIndex)}}var hp=o((()=>{pp()}));function gp(e){return{...typeof e.baseFeePerGas==`bigint`&&{baseFeePerGas:F(e.baseFeePerGas)},...typeof e.blobBaseFee==`bigint`&&{blobBaseFee:F(e.blobBaseFee)},...typeof e.feeRecipient==`string`&&{feeRecipient:e.feeRecipient},...typeof e.gasLimit==`bigint`&&{gasLimit:F(e.gasLimit)},...typeof e.number==`bigint`&&{number:F(e.number)},...typeof e.prevRandao==`bigint`&&{prevRandao:F(e.prevRandao)},...typeof e.time==`bigint`&&{time:F(e.time)},...e.withdrawals&&{withdrawals:e.withdrawals.map(mp)}}}var _p=o((()=>{pp(),hp()})),vp,yp,bp,xp,Sp,Cp=o((()=>{vp=[{inputs:[{components:[{name:`target`,type:`address`},{name:`allowFailure`,type:`bool`},{name:`callData`,type:`bytes`}],name:`calls`,type:`tuple[]`}],name:`aggregate3`,outputs:[{components:[{name:`success`,type:`bool`},{name:`returnData`,type:`bytes`}],name:`returnData`,type:`tuple[]`}],stateMutability:`view`,type:`function`},{inputs:[{name:`addr`,type:`address`}],name:`getEthBalance`,outputs:[{name:`balance`,type:`uint256`}],stateMutability:`view`,type:`function`},{inputs:[],name:`getCurrentBlockTimestamp`,outputs:[{internalType:`uint256`,name:`timestamp`,type:`uint256`}],stateMutability:`view`,type:`function`}],yp=[{name:`query`,type:`function`,stateMutability:`view`,inputs:[{type:`tuple[]`,name:`queries`,components:[{type:`address`,name:`sender`},{type:`string[]`,name:`urls`},{type:`bytes`,name:`data`}]}],outputs:[{type:`bool[]`,name:`failures`},{type:`bytes[]`,name:`responses`}]},{name:`HttpError`,type:`error`,inputs:[{type:`uint16`,name:`status`},{type:`string`,name:`message`}]}],bp=[{inputs:[{name:`dns`,type:`bytes`}],name:`DNSDecodingFailed`,type:`error`},{inputs:[{name:`ens`,type:`string`}],name:`DNSEncodingFailed`,type:`error`},{inputs:[],name:`EmptyAddress`,type:`error`},{inputs:[{name:`status`,type:`uint16`},{name:`message`,type:`string`}],name:`HttpError`,type:`error`},{inputs:[],name:`InvalidBatchGatewayResponse`,type:`error`},{inputs:[{name:`errorData`,type:`bytes`}],name:`ResolverError`,type:`error`},{inputs:[{name:`name`,type:`bytes`},{name:`resolver`,type:`address`}],name:`ResolverNotContract`,type:`error`},{inputs:[{name:`name`,type:`bytes`}],name:`ResolverNotFound`,type:`error`},{inputs:[{name:`primary`,type:`string`},{name:`primaryAddress`,type:`bytes`}],name:`ReverseAddressMismatch`,type:`error`},{inputs:[{internalType:`bytes4`,name:`selector`,type:`bytes4`}],name:`UnsupportedResolverProfile`,type:`error`}],[...bp],[...bp],xp=[{name:`isValidSignature`,type:`function`,stateMutability:`view`,inputs:[{name:`hash`,type:`bytes32`},{name:`signature`,type:`bytes`}],outputs:[{name:``,type:`bytes4`}]}],Sp=[{inputs:[{name:`_signer`,type:`address`},{name:`_hash`,type:`bytes32`},{name:`_signature`,type:`bytes`}],stateMutability:`nonpayable`,type:`constructor`},{inputs:[{name:`_signer`,type:`address`},{name:`_hash`,type:`bytes32`},{name:`_signature`,type:`bytes`}],outputs:[{type:`bool`}],stateMutability:`nonpayable`,type:`function`,name:`isValidSig`}]})),wp=o((()=>{})),Tp,Ep,Dp,Op,kp=o((()=>{Tp=`0x608060405234801561001057600080fd5b5060405161018e38038061018e83398101604081905261002f91610124565b6000808351602085016000f59050803b61004857600080fd5b6000808351602085016000855af16040513d6000823e81610067573d81fd5b3d81f35b634e487b7160e01b600052604160045260246000fd5b600082601f83011261009257600080fd5b81516001600160401b038111156100ab576100ab61006b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156100d9576100d961006b565b6040528181528382016020018510156100f157600080fd5b60005b82811015610110576020818601810151838301820152016100f4565b506000918101602001919091529392505050565b6000806040838503121561013757600080fd5b82516001600160401b0381111561014d57600080fd5b61015985828601610081565b602085015190935090506001600160401b0381111561017757600080fd5b61018385828601610081565b915050925092905056fe`,Ep=`0x608060405234801561001057600080fd5b506040516102c03803806102c083398101604081905261002f916101e6565b836001600160a01b03163b6000036100e457600080836001600160a01b03168360405161005c9190610270565b6000604051808303816000865af19150503d8060008114610099576040519150601f19603f3d011682016040523d82523d6000602084013e61009e565b606091505b50915091508115806100b857506001600160a01b0386163b155b156100e1578060405163101bb98d60e01b81526004016100d8919061028c565b60405180910390fd5b50505b6000808451602086016000885af16040513d6000823e81610103573d81fd5b3d81f35b80516001600160a01b038116811461011e57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561015457818101518382015260200161013c565b50506000910152565b600082601f83011261016e57600080fd5b81516001600160401b0381111561018757610187610123565b604051601f8201601f19908116603f011681016001600160401b03811182821017156101b5576101b5610123565b6040528181528382016020018510156101cd57600080fd5b6101de826020830160208701610139565b949350505050565b600080600080608085870312156101fc57600080fd5b61020585610107565b60208601519094506001600160401b0381111561022157600080fd5b61022d8782880161015d565b93505061023c60408601610107565b60608601519092506001600160401b0381111561025857600080fd5b6102648782880161015d565b91505092959194509250565b60008251610282818460208701610139565b9190910192915050565b60208152600082518060208401526102ab816040850160208701610139565b601f01601f1916919091016040019291505056fe`,Dp=`0x608060405234801561001057600080fd5b5060405161069438038061069483398101604081905261002f9161051e565b600061003c848484610048565b9050806000526001601ff35b60007f64926492649264926492649264926492649264926492649264926492649264926100748361040c565b036101e7576000606080848060200190518101906100929190610577565b60405192955090935091506000906001600160a01b038516906100b69085906105dd565b6000604051808303816000865af19150503d80600081146100f3576040519150601f19603f3d011682016040523d82523d6000602084013e6100f8565b606091505b50509050876001600160a01b03163b60000361016057806101605760405162461bcd60e51b815260206004820152601e60248201527f5369676e617475726556616c696461746f723a206465706c6f796d656e74000060448201526064015b60405180910390fd5b604051630b135d3f60e11b808252906001600160a01b038a1690631626ba7e90610190908b9087906004016105f9565b602060405180830381865afa1580156101ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d19190610633565b6001600160e01b03191614945050505050610405565b6001600160a01b0384163b1561027a57604051630b135d3f60e11b808252906001600160a01b03861690631626ba7e9061022790879087906004016105f9565b602060405180830381865afa158015610244573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102689190610633565b6001600160e01b031916149050610405565b81516041146102df5760405162461bcd60e51b815260206004820152603a602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610157565b6102e7610425565b5060208201516040808401518451859392600091859190811061030c5761030c61065d565b016020015160f81c9050601b811480159061032b57508060ff16601c14155b1561038c5760405162461bcd60e51b815260206004820152603b602482015260008051602061067483398151915260448201527f3a20696e76616c6964207369676e617475726520762076616c756500000000006064820152608401610157565b60408051600081526020810180835289905260ff83169181019190915260608101849052608081018390526001600160a01b0389169060019060a0016020604051602081039080840390855afa1580156103ea573d6000803e3d6000fd5b505050602060405103516001600160a01b0316149450505050505b9392505050565b600060208251101561041d57600080fd5b508051015190565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b038116811461045857600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561048c578181015183820152602001610474565b50506000910152565b600082601f8301126104a657600080fd5b81516001600160401b038111156104bf576104bf61045b565b604051601f8201601f19908116603f011681016001600160401b03811182821017156104ed576104ed61045b565b60405281815283820160200185101561050557600080fd5b610516826020830160208701610471565b949350505050565b60008060006060848603121561053357600080fd5b835161053e81610443565b6020850151604086015191945092506001600160401b0381111561056157600080fd5b61056d86828701610495565b9150509250925092565b60008060006060848603121561058c57600080fd5b835161059781610443565b60208501519093506001600160401b038111156105b357600080fd5b6105bf86828701610495565b604086015190935090506001600160401b0381111561056157600080fd5b600082516105ef818460208701610471565b9190910192915050565b828152604060208201526000825180604084015261061e816060850160208701610471565b601f01601f1916919091016060019392505050565b60006020828403121561064557600080fd5b81516001600160e01b03198116811461040557600080fd5b634e487b7160e01b600052603260045260246000fdfe5369676e617475726556616c696461746f72237265636f7665725369676e6572`,Op=`0x608060405234801561001057600080fd5b506115b9806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e14610325578063bce38bd714610350578063c3077fa914610380578063ee82ac5e146103b2576100f3565b80634d2301cc1461026257806372425d9d1461029f57806382ad56cb146102ca57806386d516e8146102fa576100f3565b80633408e470116100c65780633408e470146101af578063399542e9146101da5780633e64a6961461020c57806342cbb15c14610237576100f3565b80630f28c97d146100f8578063174dea7114610123578063252dba421461015357806327e86d6e14610184575b600080fd5b34801561010457600080fd5b5061010d6103ef565b60405161011a9190610c0a565b60405180910390f35b61013d60048036038101906101389190610c94565b6103f7565b60405161014a9190610e94565b60405180910390f35b61016d60048036038101906101689190610f0c565b610615565b60405161017b92919061101b565b60405180910390f35b34801561019057600080fd5b506101996107ab565b6040516101a69190611064565b60405180910390f35b3480156101bb57600080fd5b506101c46107b7565b6040516101d19190610c0a565b60405180910390f35b6101f460048036038101906101ef91906110ab565b6107bf565b6040516102039392919061110b565b60405180910390f35b34801561021857600080fd5b506102216107e1565b60405161022e9190610c0a565b60405180910390f35b34801561024357600080fd5b5061024c6107e9565b6040516102599190610c0a565b60405180910390f35b34801561026e57600080fd5b50610289600480360381019061028491906111a7565b6107f1565b6040516102969190610c0a565b60405180910390f35b3480156102ab57600080fd5b506102b4610812565b6040516102c19190610c0a565b60405180910390f35b6102e460048036038101906102df919061122a565b61081a565b6040516102f19190610e94565b60405180910390f35b34801561030657600080fd5b5061030f6109e4565b60405161031c9190610c0a565b60405180910390f35b34801561033157600080fd5b5061033a6109ec565b6040516103479190611286565b60405180910390f35b61036a600480360381019061036591906110ab565b6109f4565b6040516103779190610e94565b60405180910390f35b61039a60048036038101906103959190610f0c565b610ba6565b6040516103a99392919061110b565b60405180910390f35b3480156103be57600080fd5b506103d960048036038101906103d491906112cd565b610bca565b6040516103e69190611064565b60405180910390f35b600042905090565b60606000808484905090508067ffffffffffffffff81111561041c5761041b6112fa565b5b60405190808252806020026020018201604052801561045557816020015b610442610bd5565b81526020019060019003908161043a5790505b5092503660005b828110156105c957600085828151811061047957610478611329565b5b6020026020010151905087878381811061049657610495611329565b5b90506020028101906104a89190611367565b925060008360400135905080860195508360000160208101906104cb91906111a7565b73ffffffffffffffffffffffffffffffffffffffff16818580606001906104f2919061138f565b604051610500929190611431565b60006040518083038185875af1925050503d806000811461053d576040519150601f19603f3d011682016040523d82523d6000602084013e610542565b606091505b5083600001846020018290528215151515815250505081516020850135176105bc577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b826001019250505061045c565b5082341461060c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610603906114a7565b60405180910390fd5b50505092915050565b6000606043915060008484905090508067ffffffffffffffff81111561063e5761063d6112fa565b5b60405190808252806020026020018201604052801561067157816020015b606081526020019060019003908161065c5790505b5091503660005b828110156107a157600087878381811061069557610694611329565b5b90506020028101906106a791906114c7565b92508260000160208101906106bc91906111a7565b73ffffffffffffffffffffffffffffffffffffffff168380602001906106e2919061138f565b6040516106f0929190611431565b6000604051808303816000865af19150503d806000811461072d576040519150601f19603f3d011682016040523d82523d6000602084013e610732565b606091505b5086848151811061074657610745611329565b5b60200260200101819052819250505080610795576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078c9061153b565b60405180910390fd5b81600101915050610678565b5050509250929050565b60006001430340905090565b600046905090565b6000806060439250434091506107d68686866109f4565b905093509350939050565b600048905090565b600043905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b600044905090565b606060008383905090508067ffffffffffffffff81111561083e5761083d6112fa565b5b60405190808252806020026020018201604052801561087757816020015b610864610bd5565b81526020019060019003908161085c5790505b5091503660005b828110156109db57600084828151811061089b5761089a611329565b5b602002602001015190508686838181106108b8576108b7611329565b5b90506020028101906108ca919061155b565b92508260000160208101906108df91906111a7565b73ffffffffffffffffffffffffffffffffffffffff16838060400190610905919061138f565b604051610913929190611431565b6000604051808303816000865af19150503d8060008114610950576040519150601f19603f3d011682016040523d82523d6000602084013e610955565b606091505b5082600001836020018290528215151515815250505080516020840135176109cf577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b8160010191505061087e565b50505092915050565b600045905090565b600041905090565b606060008383905090508067ffffffffffffffff811115610a1857610a176112fa565b5b604051908082528060200260200182016040528015610a5157816020015b610a3e610bd5565b815260200190600190039081610a365790505b5091503660005b82811015610b9c576000848281518110610a7557610a74611329565b5b60200260200101519050868683818110610a9257610a91611329565b5b9050602002810190610aa491906114c7565b9250826000016020810190610ab991906111a7565b73ffffffffffffffffffffffffffffffffffffffff16838060200190610adf919061138f565b604051610aed929190611431565b6000604051808303816000865af19150503d8060008114610b2a576040519150601f19603f3d011682016040523d82523d6000602084013e610b2f565b606091505b508260000183602001829052821515151581525050508715610b90578060000151610b8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b869061153b565b60405180910390fd5b5b81600101915050610a58565b5050509392505050565b6000806060610bb7600186866107bf565b8093508194508295505050509250925092565b600081409050919050565b6040518060400160405280600015158152602001606081525090565b6000819050919050565b610c0481610bf1565b82525050565b6000602082019050610c1f6000830184610bfb565b92915050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f840112610c5457610c53610c2f565b5b8235905067ffffffffffffffff811115610c7157610c70610c34565b5b602083019150836020820283011115610c8d57610c8c610c39565b5b9250929050565b60008060208385031215610cab57610caa610c25565b5b600083013567ffffffffffffffff811115610cc957610cc8610c2a565b5b610cd585828601610c3e565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60008115159050919050565b610d2281610d0d565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610d62578082015181840152602081019050610d47565b83811115610d71576000848401525b50505050565b6000601f19601f8301169050919050565b6000610d9382610d28565b610d9d8185610d33565b9350610dad818560208601610d44565b610db681610d77565b840191505092915050565b6000604083016000830151610dd96000860182610d19565b5060208301518482036020860152610df18282610d88565b9150508091505092915050565b6000610e0a8383610dc1565b905092915050565b6000602082019050919050565b6000610e2a82610ce1565b610e348185610cec565b935083602082028501610e4685610cfd565b8060005b85811015610e825784840389528151610e638582610dfe565b9450610e6e83610e12565b925060208a01995050600181019050610e4a565b50829750879550505050505092915050565b60006020820190508181036000830152610eae8184610e1f565b905092915050565b60008083601f840112610ecc57610ecb610c2f565b5b8235905067ffffffffffffffff811115610ee957610ee8610c34565b5b602083019150836020820283011115610f0557610f04610c39565b5b9250929050565b60008060208385031215610f2357610f22610c25565b5b600083013567ffffffffffffffff811115610f4157610f40610c2a565b5b610f4d85828601610eb6565b92509250509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6000610f918383610d88565b905092915050565b6000602082019050919050565b6000610fb182610f59565b610fbb8185610f64565b935083602082028501610fcd85610f75565b8060005b858110156110095784840389528151610fea8582610f85565b9450610ff583610f99565b925060208a01995050600181019050610fd1565b50829750879550505050505092915050565b60006040820190506110306000830185610bfb565b81810360208301526110428184610fa6565b90509392505050565b6000819050919050565b61105e8161104b565b82525050565b60006020820190506110796000830184611055565b92915050565b61108881610d0d565b811461109357600080fd5b50565b6000813590506110a58161107f565b92915050565b6000806000604084860312156110c4576110c3610c25565b5b60006110d286828701611096565b935050602084013567ffffffffffffffff8111156110f3576110f2610c2a565b5b6110ff86828701610eb6565b92509250509250925092565b60006060820190506111206000830186610bfb565b61112d6020830185611055565b818103604083015261113f8184610e1f565b9050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061117482611149565b9050919050565b61118481611169565b811461118f57600080fd5b50565b6000813590506111a18161117b565b92915050565b6000602082840312156111bd576111bc610c25565b5b60006111cb84828501611192565b91505092915050565b60008083601f8401126111ea576111e9610c2f565b5b8235905067ffffffffffffffff81111561120757611206610c34565b5b60208301915083602082028301111561122357611222610c39565b5b9250929050565b6000806020838503121561124157611240610c25565b5b600083013567ffffffffffffffff81111561125f5761125e610c2a565b5b61126b858286016111d4565b92509250509250929050565b61128081611169565b82525050565b600060208201905061129b6000830184611277565b92915050565b6112aa81610bf1565b81146112b557600080fd5b50565b6000813590506112c7816112a1565b92915050565b6000602082840312156112e3576112e2610c25565b5b60006112f1848285016112b8565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b60008235600160800383360303811261138357611382611358565b5b80830191505092915050565b600080833560016020038436030381126113ac576113ab611358565b5b80840192508235915067ffffffffffffffff8211156113ce576113cd61135d565b5b6020830192506001820236038313156113ea576113e9611362565b5b509250929050565b600081905092915050565b82818337600083830152505050565b600061141883856113f2565b93506114258385846113fd565b82840190509392505050565b600061143e82848661140c565b91508190509392505050565b600082825260208201905092915050565b7f4d756c746963616c6c333a2076616c7565206d69736d61746368000000000000600082015250565b6000611491601a8361144a565b915061149c8261145b565b602082019050919050565b600060208201905081810360008301526114c081611484565b9050919050565b6000823560016040038336030381126114e3576114e2611358565b5b80830191505092915050565b7f4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000600082015250565b600061152560178361144a565b9150611530826114ef565b602082019050919050565b6000602082019050818103600083015261155481611518565b9050919050565b60008235600160600383360303811261157757611576611358565b5b8083019150509291505056fea264697066735822122020c1bc9aacf8e4a6507193432a895a8e77094f45a1395583f07b24e860ef06cd64736f6c634300080c0033`})),Ap,jp,Mp,Np,Pp,Fp=o((()=>{D(),Ap=class extends E{constructor({blockNumber:e,chain:t,contract:n}){super(`Chain "${t.name}" does not support contract "${n.name}".`,{metaMessages:[`This could be due to any of the following:`,...e&&n.blockCreated&&n.blockCreated>e?[`- The contract "${n.name}" was not deployed until block ${n.blockCreated} (current block ${e}).`]:[`- The chain does not have the contract "${n.name}" configured.`]],name:`ChainDoesNotSupportContract`})}},jp=class extends E{constructor({chain:e,currentChainId:t}){super(`The current chain of the wallet (id: ${t}) does not match the target chain for the transaction (id: ${e.id} – ${e.name}).`,{metaMessages:[`Current Chain ID: ${t}`,`Expected Chain ID: ${e.id} – ${e.name}`],name:`ChainMismatchError`})}},Mp=class extends E{constructor(){super([`No chain was provided to the request.`,"Please provide a chain with the `chain` argument on the Action, or by supplying a `chain` to WalletClient."].join(` -`),{name:`ChainNotFoundError`})}},Np=class extends E{constructor(){super(`No chain was provided to the Client.`,{name:`ClientChainNotConfiguredError`})}},Pp=class extends E{constructor({chainId:e}){super(typeof e==`number`?`Chain ID "${e}" is invalid.`:`Chain ID is invalid.`,{name:`InvalidChainIdError`})}}}));function Ip(e){let{abi:t,args:n,bytecode:r}=e;if(!n||n.length===0)return r;let i=t.find(e=>`type`in e&&e.type===`constructor`);if(!i)throw new Ht({docsPath:Lp});if(!(`inputs`in i)||!i.inputs||i.inputs.length===0)throw new Ut({docsPath:Lp});return ki([r,Bi(i.inputs,n)])}var Lp,Rp=o((()=>{cn(),Ai(),Qi(),Lp=`/docs/contract/encodeDeployData`}));function zp({blockNumber:e,chain:t,contract:n}){let r=t?.contracts?.[n];if(!r)throw new Ap({chain:t,contract:{name:n}});if(e&&r.blockCreated&&r.blockCreated>e)throw new Ap({blockNumber:e,chain:t,contract:{name:n,blockCreated:r.blockCreated}});return r.address}var Bp=o((()=>{Fp()}));function Vp(e,{docsPath:t,...n}){return new xo((()=>{let t=Fu(e,n);return t instanceof Nu?e:t})(),{docsPath:t,...n})}var Hp=o((()=>{Eo(),Pu(),Iu()}));function Up(){let e=()=>void 0,t=()=>void 0;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var Wp=o((()=>{}));function Gp({fn:e,id:t,shouldSplitBatch:n,wait:r=0,sort:i}){let a=async()=>{let t=c();o();let n=t.map(({args:e})=>e);n.length!==0&&e(n).then(e=>{i&&Array.isArray(e)&&e.sort(i);for(let n=0;n{for(let n=0;nKp.delete(t),s=()=>c().map(({args:e})=>e),c=()=>Kp.get(t)||[],l=e=>Kp.set(t,[...c(),e]);return{flush:o,async schedule(e){let{promise:t,resolve:i,reject:o}=Up();return n?.([...s(),e])&&a(),c().length>0?(l({args:e,resolve:i,reject:o}),t):(l({args:e,resolve:i,reject:o}),setTimeout(a,r),t)}}}var Kp,qp=o((()=>{Wp(),Kp=new Map})),Jp,Yp,Xp,Zp=o((()=>{Wa(),D(),bo(),Jp=class extends E{constructor({callbackSelector:e,cause:t,data:n,extraData:r,sender:i,urls:a}){super(t.shortMessage||`An error occurred while fetching for an offchain result.`,{cause:t,metaMessages:[...t.metaMessages||[],t.metaMessages?.length?``:[],`Offchain Gateway Call:`,a&&[` Gateway URL(s):`,...a.map(e=>` ${yo(e)}`)],` Sender: ${i}`,` Data: ${n}`,` Callback selector: ${e}`,` Extra data: ${r}`].flat(),name:`OffchainLookupError`})}},Yp=class extends E{constructor({result:e,url:t}){super(`Offchain gateway response is malformed. Response data must be a hex value.`,{metaMessages:[`Gateway URL: ${yo(t)}`,`Response: ${Ua(e)}`],name:`OffchainLookupResponseMalformedError`})}},Xp=class extends E{constructor({sender:e,to:t}){super("Reverted sender address does not match target contract address (`to`).",{metaMessages:[`Contract address: ${t}`,`OffchainLookup sender address: ${e}`],name:`OffchainLookupSenderMismatchError`})}}}));function Qp(e){let{abi:t,data:n}=e,r=ji(n,0,4),i=t.find(e=>e.type===`function`&&r===$i(At(e)));if(!i)throw new en(r,{docsPath:`/docs/contract/decodeFunctionData`});return{functionName:i.name,args:`inputs`in i&&i.inputs&&i.inputs.length>0?Oa(i.inputs,ji(n,4)):void 0}}var $p=o((()=>{cn(),Ii(),ea(),Ba(),Nt()}));function em(e){let{abi:t,errorName:n,args:r}=e,i=t[0];if(n){let e=ta({abi:t,args:r,name:n});if(!e)throw new Xt(n,{docsPath:tm});i=e}if(i.type!==`error`)throw new Xt(void 0,{docsPath:tm});let a=$i(At(i)),o=`0x`;if(r&&r.length>0){if(!i.inputs)throw new Yt(i.name,{docsPath:tm});o=Bi(i.inputs,r)}return ki([a,o])}var tm,nm=o((()=>{cn(),Ai(),ea(),Qi(),Nt(),ia(),tm=`/docs/contract/encodeErrorResult`}));function rm(e){let{abi:t,functionName:n,result:r}=e,i=t[0];if(n){let e=ta({abi:t,name:n});if(!e)throw new Qt(n,{docsPath:im});i=e}if(i.type!==`function`)throw new Qt(void 0,{docsPath:im});if(!i.outputs)throw new $t(i.name,{docsPath:im});let a=(()=>{if(i.outputs.length===0)return[];if(i.outputs.length===1)return[r];if(Array.isArray(r))return r;throw new on(r)})();return Bi(i.outputs,a)}var im,am=o((()=>{cn(),Qi(),ia(),im=`/docs/contract/encodeFunctionResult`}));async function om(e){let{data:t,ccipRequest:n}=e,{args:[r]}=Qp({abi:yp,data:t}),i=[],a=[];return await Promise.all(r.map(async(e,t)=>{try{a[t]=e.urls.includes(`x-batch-gateway:true`)?await om({data:e.data,ccipRequest:n}):await n(e),i[t]=!1}catch(e){i[t]=!0,a[t]=sm(e)}})),rm({abi:yp,functionName:`query`,result:[i,a]})}function sm(e){return e.name===`HttpRequestError`&&e.status?em({abi:yp,errorName:`HttpError`,args:[e.status,e.shortMessage]}):em({abi:[pa],errorName:`Error`,args:[`shortMessage`in e?e.shortMessage:e.message]})}var cm=o((()=>{Cp(),ha(),$p(),nm(),am()})),lm=c({ccipRequest:()=>dm,offchainLookup:()=>um,offchainLookupAbiItem:()=>pm,offchainLookupSignature:()=>fm});async function um(e,{blockNumber:t,blockTag:n,data:r,to:i}){let{args:a}=Va({data:r,abi:[pm]}),[o,s,c,l,u]=a,{ccipRead:d}=e,f=d&&typeof d?.request==`function`?d.request:dm;try{if(!Vd(i,o))throw new Xp({sender:o,to:i});let{data:r}=await hm(e,{blockNumber:t,blockTag:n,data:Di([l,Bi([{type:`bytes`},{type:`bytes`}],[s.includes(`x-batch-gateway:true`)?await om({data:c,ccipRequest:f}):await f({data:c,sender:o,urls:s}),u])]),to:i});return r}catch(e){throw new Jp({callbackSelector:l,cause:e,data:r,extraData:u,sender:o,urls:s})}}async function dm({data:e,sender:t,urls:n}){let r=Error(`An unknown error occurred.`);for(let i=0;i{xm(),Zp(),Ao(),Ha(),Qi(),Hd(),Ai(),Ft(),cm(),Wa(),fm=`0x556f1830`,pm={name:`OffchainLookup`,type:`error`,inputs:[{name:`sender`,type:`address`},{name:`urls`,type:`string[]`},{name:`callData`,type:`bytes`},{name:`callbackFunction`,type:`bytes4`},{name:`extraData`,type:`bytes`}]}}));async function hm(e,t){let{account:n=e.account,authorizationList:r,batch:i=!!e.batch?.multicall,blockNumber:a,blockTag:o=e.experimental_blockTag??`latest`,accessList:s,blobs:c,blockOverrides:l,code:u,data:d,factory:f,factoryData:p,gas:m,gasPrice:h,maxFeePerBlobGas:g,maxFeePerGas:_,maxPriorityFeePerGas:v,nonce:y,to:b,value:x,stateOverride:S,...ee}=t,C=n?aa(n):void 0;if(u&&(f||p))throw new E("Cannot provide both `code` & `factory`/`factoryData` as parameters.");if(u&&b)throw new E("Cannot provide both `code` & `to` as parameters.");let te=u&&d,ne=f&&p&&b&&d,re=te||ne,ie=te?vm({code:u,data:d}):ne?ym({data:d,factory:f,factoryData:p,to:b}):d;try{ed(t);let n=(typeof a==`bigint`?O(a):void 0)||o,u=l?gp(l):void 0,d=Yu(S),f=e.chain?.formatters?.transactionRequest?.format,p=(f||Hu)({...Ru(ee,{format:f}),accessList:s,account:C,authorizationList:r,blobs:c,data:ie,gas:m,gasPrice:h,maxFeePerBlobGas:g,maxFeePerGas:_,maxPriorityFeePerGas:v,nonce:y,to:re?void 0:b,value:x},`call`);if(i&&gm({request:p})&&!d&&!u)try{return await _m(e,{...p,blockNumber:a,blockTag:o})}catch(e){if(!(e instanceof Np)&&!(e instanceof Ap))throw e}let te=(()=>{let e=[p,n];return d&&u?[...e,d,u]:d?[...e,d]:u?[...e,{},u]:e})(),ne=await e.request({method:`eth_call`,params:te});return ne===`0x`?{data:void 0}:{data:ne}}catch(n){let r=bm(n),{offchainLookup:i,offchainLookupSignature:a}=await ms(async()=>{let{offchainLookup:e,offchainLookupSignature:t}=await Promise.resolve().then(()=>(mm(),lm));return{offchainLookup:e,offchainLookupSignature:t}},void 0);if(e.ccipRead!==!1&&r?.slice(0,10)===a&&b)return{data:await i(e,{data:r,to:b})};throw re&&r?.slice(0,10)===`0x101bb98d`?new wo({factory:f}):Vp(n,{...t,account:C,chain:e.chain})}}function gm({request:e}){let{data:t,to:n,...r}=e;return!(!t||t.startsWith(`0x82ad56cb`)||!n||Object.values(r).filter(e=>e!==void 0).length>0)}async function _m(e,t){let{batchSize:n=1024,deployless:r=!1,wait:i=0}=typeof e.batch?.multicall==`object`?e.batch.multicall:{},{blockNumber:a,blockTag:o=e.experimental_blockTag??`latest`,data:s,to:c}=t,l=(()=>{if(r)return null;if(t.multicallAddress)return t.multicallAddress;if(e.chain)return zp({blockNumber:a,chain:e.chain,contract:`multicall3`});throw new Np})(),u=(typeof a==`bigint`?O(a):void 0)||o,{schedule:d}=Gp({id:`${e.uid}.${u}`,wait:i,shouldSplitBatch(e){return e.reduce((e,{data:t})=>e+(t.length-2),0)>n*2},fn:async t=>{let n=t.map(e=>({allowFailure:!0,callData:e.data,target:e.to})),r=ua({abi:vp,args:[n],functionName:`aggregate3`}),i=await e.request({method:`eth_call`,params:[{...l===null?{data:vm({code:Op,data:r})}:{to:l,data:r}},u]});return Wd({abi:vp,args:[n],functionName:`aggregate3`,data:i||`0x`})}}),[{returnData:f,success:p}]=await d({data:s,to:c});if(!p)throw new To({data:f});return f===`0x`?{data:void 0}:{data:f}}function vm(e){let{code:t,data:n}=e;return Ip({abi:Ct([`constructor(bytes, bytes)`]),bytecode:Tp,args:[t,n]})}function ym(e){let{data:t,factory:n,factoryData:r,to:i}=e;return Ip({abi:Ct([`constructor(address, bytes, address, bytes)`]),bytecode:Ep,args:[i,t,n,r]})}function bm(e){if(!(e instanceof E))return;let t=e.walk();return typeof t?.data==`object`?t.data?.data:t.data}var xm=o((()=>{kt(),_p(),oa(),Cp(),wp(),kp(),D(),Fp(),Eo(),Kd(),Rp(),Bp(),k(),Hp(),zu(),Ku(),qp(),Xu(),td(),hs()}));Kd(),da(),xm();async function Sm(e,t){let{abi:n,address:r,args:i,functionName:a,...o}=t,s=ua({abi:n,args:i,functionName:a});try{let{data:t}=await T(e,hm,`call`)({...o,data:s,to:r});return Wd({abi:n,args:i,functionName:a,data:t||`0x`})}catch(e){throw ls(e,{abi:n,address:r,args:i,docsPath:`/docs/contract/readContract`,functionName:a})}}var Cm=new Map,wm=new Map,Tm=0;function Em(e,t,n){let r=++Tm,i=()=>Cm.get(e)||[],a=()=>{let t=i();Cm.set(e,t.filter(e=>e.id!==r))},o=()=>{let t=i();if(!t.some(e=>e.id===r))return;let n=wm.get(e);if(t.length===1&&n){let e=n();e instanceof Promise&&e.catch(()=>{})}a()},s=i();if(Cm.set(e,[...s,{id:r,fns:t}]),s&&s.length>0)return o;let c={};for(let e in t)c[e]=((...t)=>{let n=i();if(n.length!==0)for(let r of n)r.fns[e]?.(...t)});let l=n(c);return typeof l==`function`&&wm.set(e,l),o}async function Dm(e){return new Promise(t=>setTimeout(t,e))}function Om(e,{emitOnBegin:t,initialWaitTime:n,interval:r}){let i=!0,a=()=>i=!1;return(async()=>{let o;t&&(o=await e({unpoll:a})),await Dm(await n?.(o)??r);let s=async()=>{i&&(await e({unpoll:a}),await Dm(r),s())};s()})(),a}var km=new Map,Am=new Map;function jm(e){let t=(e,t)=>({clear:()=>t.delete(e),get:()=>t.get(e),set:n=>t.set(e,n)}),n=t(e,km),r=t(e,Am);return{clear:()=>{n.clear(),r.clear()},promise:n,response:r}}async function Mm(e,{cacheKey:t,cacheTime:n=1/0}){let r=jm(t),i=r.response.get();if(i&&n>0&&Date.now()-i.created.getTime()`blockNumber.${e}`;async function Pm(e,{cacheTime:t=e.cacheTime}={}){let n=await Mm(()=>e.request({method:`eth_blockNumber`}),{cacheKey:Nm(e.uid),cacheTime:t});return BigInt(n)}D();var Fm=class extends E{constructor({docsPath:e}={}){super([`Could not find an Account to execute with this Action.`,"Please provide an Account with the `account` argument on the Action, or by supplying an `account` to the Client."].join(` -`),{docsPath:e,docsSlug:`account`,name:`AccountNotFoundError`})}},Im=class extends E{constructor({docsPath:e,metaMessages:t,type:n}){super(`Account type "${n}" is not supported.`,{docsPath:e,metaMessages:t,name:`AccountTypeNotSupportedError`})}};Fp();function Lm({chain:e,currentChainId:t}){if(!e)throw new Mp;if(t!==e.id)throw new jp({chain:e,currentChainId:t})}async function Rm(e,{serializedTransaction:t}){return e.request({method:`eth_sendRawTransaction`,params:[t]},{retryCount:0})}oa(),D(),Ai(),zu(),Ku(),vi(),td();var zm=new _i(128);async function Bm(e,t){let{account:n=e.account,assertChainId:r=!0,chain:i=e.chain,accessList:a,authorizationList:o,blobs:s,data:c,dataSuffix:l=typeof e.dataSuffix==`string`?e.dataSuffix:e.dataSuffix?.value,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,type:g,value:_,...v}=t;if(n===void 0)throw new Fm({docsPath:`/docs/actions/wallet/sendTransaction`});let y=n?aa(n):null;try{ed(t);let n=await(async()=>{if(t.to)return t.to;if(t.to!==null&&o&&o.length>0)return await bu({authorization:o[0]}).catch(()=>{throw new E("`to` is required. Could not infer from `authorizationList`.")})})();if(y?.type===`json-rpc`||y===null){let t;i!==null&&(t=await T(e,Fd,`getChainId`)({}),r&&Lm({currentChainId:t,chain:i}));let b=e.chain?.formatters?.transactionRequest?.format,x=(b||Hu)({...Ru(v,{format:b}),accessList:a,account:y,authorizationList:o,blobs:s,chainId:t,data:l?Di([c??`0x`,l]):c,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,to:n,type:g,value:_},`sendTransaction`),S=zm.get(e.uid),ee=S?`wallet_sendTransaction`:`eth_sendTransaction`;try{return await e.request({method:ee,params:[x]},{retryCount:0})}catch(t){if(S===!1)throw t;let n=t;if(n.name===`InvalidInputRpcError`||n.name===`InvalidParamsRpcError`||n.name===`MethodNotFoundRpcError`||n.name===`MethodNotSupportedRpcError`)return await e.request({method:`wallet_sendTransaction`,params:[x]},{retryCount:0}).then(t=>(zm.set(e.uid,!0),t)).catch(t=>{let r=t;throw r.name===`MethodNotFoundRpcError`||r.name===`MethodNotSupportedRpcError`?(zm.set(e.uid,!1),n):r});throw n}}if(y?.type===`local`){let t=await T(e,zd,`prepareTransactionRequest`)({account:y,accessList:a,authorizationList:o,blobs:s,chain:i,data:l?Di([c??`0x`,l]):c,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,nonceManager:y.nonceManager,parameters:[...Ld,`sidecars`],type:g,value:_,...v,to:n}),r=i?.serializers?.transaction,b=await y.signTransaction(t,{serializer:r});return await T(e,Rm,`sendRawTransaction`)({serializedTransaction:b})}throw y?.type===`smart`?new Im({metaMessages:["Consider using the `sendUserOperation` Action instead."],docsPath:`/docs/actions/bundler/sendUserOperation`,type:`smart`}):new Im({docsPath:`/docs/actions/wallet/sendTransaction`,type:y?.type})}catch(e){throw e instanceof Im?e:Pd(e,{...t,account:y,chain:t.chain||void 0})}}oa(),da();async function Vm(e,t){return Vm.internal(e,Bm,`sendTransaction`,t)}(function(e){async function t(e,t,n,r){let{abi:i,account:a=e.account,address:o,args:s,functionName:c,...l}=r;if(a===void 0)throw new Fm({docsPath:`/docs/contract/writeContract`});let u=a?aa(a):null,d=ua({abi:i,args:s,functionName:c});try{return await T(e,t,n)({data:d,to:o,account:u,...l})}catch(e){throw ls(e,{abi:i,address:o,args:s,docsPath:`/docs/contract/writeContract`,functionName:c,sender:u?.address})}}e.internal=t})(Vm||={}),D();var Hm=class extends E{constructor(e){super(`Call bundle failed with status: ${e.statusCode}`,{name:`BundleFailedError`}),Object.defineProperty(this,`result`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.result=e}};function Um(e,{delay:t=100,retryCount:n=2,shouldRetry:r=()=>!0}={}){return new Promise((i,a)=>{let o=async({count:s=0}={})=>{let c=async({error:e})=>{let n=typeof t==`function`?t({count:s,error:e}):t;n&&await Dm(n),o({count:s+1})};try{i(await e())}catch(e){if(sUd(e)):null,to:e.to?e.to:null,transactionIndex:e.transactionIndex?Dn(e.transactionIndex):null,status:e.status?Wm[e.status]:null,type:e.type?od[e.type]||e.type:null};return e.blobGasPrice&&(n.blobGasPrice=BigInt(e.blobGasPrice)),e.blobGasUsed&&(n.blobGasUsed=BigInt(e.blobGasUsed)),n}var Km=Bu(`transactionReceipt`,Gm);oa(),D(),ss(),da(),Ai(),On(),k();var qm=`0x5792579257925792579257925792579257925792579257925792579257925792`,Jm=O(0,{size:32});async function Ym(e,t){let{account:n=e.account,chain:r=e.chain,experimental_fallback:i,experimental_fallbackDelay:a=32,forceAtomic:o=!1,id:s,version:c=`2.0.0`}=t,l=n?aa(n):null,u=t.capabilities;e.dataSuffix&&!t.capabilities?.dataSuffix&&(u=typeof e.dataSuffix==`string`?{...t.capabilities,dataSuffix:{value:e.dataSuffix,optional:!0}}:{...t.capabilities,dataSuffix:{value:e.dataSuffix.value,...e.dataSuffix.required?{}:{optional:!0}}});let d=t.calls.map(e=>{let t=e,n=t.abi?ua({abi:t.abi,functionName:t.functionName,args:t.args}):t.data;return{data:t.dataSuffix&&n?Di([n,t.dataSuffix]):n,to:t.to,value:t.value?O(t.value):void 0}});try{let t=await e.request({method:`wallet_sendCalls`,params:[{atomicRequired:o,calls:d,capabilities:u,chainId:O(r.id),from:l?.address,id:s,version:c}]},{retryCount:0});return typeof t==`string`?{id:t}:t}catch(n){let s=n;if(i&&(s.name===`MethodNotFoundRpcError`||s.name===`MethodNotSupportedRpcError`||s.name===`UnknownRpcError`||s.details.toLowerCase().includes(`does not exist / is not available`)||s.details.toLowerCase().includes(`missing or invalid. request()`)||s.details.toLowerCase().includes(`did not match any variant of untagged enum`)||s.details.toLowerCase().includes(`account upgraded to unsupported contract`)||s.details.toLowerCase().includes(`eip-7702 not supported`)||s.details.toLowerCase().includes(`unsupported wc_ method`)||s.details.toLowerCase().includes(`feature toggled misconfigured`)||s.details.toLowerCase().includes(`jsonrpcengine: response has no error or result for request`))){if(u&&Object.values(u).some(e=>!e.optional)){let e="non-optional `capabilities` are not supported on fallback to `eth_sendTransaction`.";throw new Qo(new E(e,{details:e}))}if(o&&d.length>1){let e="`forceAtomic` is not supported on fallback to `eth_sendTransaction`.";throw new is(new E(e,{details:e}))}let t=[];for(let n of d){let i=Bm(e,{account:l,chain:r,data:n.data,to:n.to,value:n.value?Tn(n.value):void 0});t.push(i),a>0&&await new Promise(e=>setTimeout(e,a))}let n=await Promise.allSettled(t);if(n.every(e=>e.status===`rejected`))throw n[0].reason;return{id:Di([...n.map(e=>e.status===`fulfilled`?e.value:Jm),O(r.id,{size:32}),qm])}}throw Pd(n,{...t,account:l,chain:t.chain})}}Ii(),Cn(),On();async function Xm(e,t){async function n(t){if(t.endsWith(`5792579257925792579257925792579257925792579257925792579257925792`)){let n=Sn(Fi(t,-64,-32)),r=Fi(t,0,-64).slice(2).match(/.{1,64}/g),i=await Promise.all(r.map(t=>Jm.slice(2)===t?void 0:e.request({method:`eth_getTransactionReceipt`,params:[`0x${t}`]},{dedupe:!0}))),a=i.some(e=>e===null)?100:i.every(e=>e?.status===`0x1`)?200:i.every(e=>e?.status===`0x0`)?500:600;return{atomic:!1,chainId:Dn(n),receipts:i.filter(Boolean),status:a,version:`2.0.0`}}return e.request({method:`wallet_getCallsStatus`,params:[t]})}let{atomic:r=!1,chainId:i,receipts:a,version:o=`2.0.0`,...s}=await n(t.id),[c,l]=(()=>{let e=s.status;return e>=100&&e<200?[`pending`,e]:e>=200&&e<300?[`success`,e]:e>=300&&e<700?[`failure`,e]:e===`CONFIRMED`?[`success`,200]:e===`PENDING`?[`pending`,100]:[void 0,e]})();return{...s,atomic:r,chainId:i?Dn(i):void 0,receipts:a?.map(e=>({...e,blockNumber:Tn(e.blockNumber),gasUsed:Tn(e.gasUsed),status:Wm[e.status]}))??[],statusCode:l,status:c,version:o}}D(),Wp(),Wa();async function Zm(e,t){let{id:n,pollingInterval:r=e.pollingInterval,status:i=({statusCode:e})=>e===200||e>=300,retryCount:a=4,retryDelay:o=({count:e})=>~~(1<{let s=Om(async()=>{let r=e=>{clearTimeout(p),s(),e(),m()};try{let s=await Um(async()=>{let t=await T(e,Xm,`getCallsStatus`)({id:n});if(c&&t.status===`failure`)throw new Hm(t);return t},{retryCount:a,delay:o});if(!i(s))return;r(()=>t.resolve(s))}catch(e){r(()=>t.reject(e))}},{interval:r,emitOnBegin:!0});return s});return p=s?setTimeout(()=>{m(),clearTimeout(p),f(new Qm({id:n}))},s):void 0,await u}var Qm=class extends E{constructor({id:e}){super(`Timed out while waiting for call bundle with id "${e}" to be confirmed.`,{name:`WaitForCallsStatusTimeoutError`})}},$m=256,eh=$m,th;function nh(e=11){if(!th||eh+e>$m*2){th=``,eh=0;for(let e=0;e<$m;e++)th+=(256+Math.random()*256|0).toString(16).substring(1)}return th.substring(eh,eh+++e)}oa();function rh(e){let{batch:t,chain:n,ccipRead:r,dataSuffix:i,key:a=`base`,name:o=`Base Client`,type:s=`base`}=e,c=e.experimental_blockTag??(typeof n?.experimental_preconfirmationTime==`number`?`pending`:void 0),l=n?.blockTime??12e3,u=Math.min(Math.max(Math.floor(l/2),500),4e3),d=e.pollingInterval??u,f=e.cacheTime??d,p=e.account?aa(e.account):void 0,{config:m,request:h,value:g}=e.transport({account:p,chain:n,pollingInterval:d}),_={account:p,batch:t,cacheTime:f,ccipRead:r,chain:n,dataSuffix:i,key:a,name:o,pollingInterval:d,request:h,transport:{...m,...g},type:s,uid:nh(),...c?{experimental_blockTag:c}:{}};function v(e){return t=>{let n=t(e);for(let e in _)delete n[e];let r={...e,...n};return Object.assign(r,{extend:v(r)})}}return Object.assign(_,{extend:v(_)})}k();async function ih(e,{address:t,blockNumber:n,blockTag:r=`latest`}){let i=n===void 0?void 0:O(n),a=await e.request({method:`eth_getCode`,params:[t,i||r]},{dedupe:!!i});if(a!==`0x`)return a}D();var ah=class extends E{constructor({address:e}){super(`No EIP-712 domain found on contract "${e}".`,{metaMessages:[`Ensure that:`,`- The contract is deployed at the address "${e}".`,"- `eip712Domain()` function exists on the contract.","- `eip712Domain()` function matches signature to ERC-5267 specification."],name:`Eip712DomainNotFoundError`})}};async function oh(e,t){let{address:n,factory:r,factoryData:i}=t;try{let[t,a,o,s,c,l,u]=await T(e,Sm,`readContract`)({abi:sh,address:n,functionName:`eip712Domain`,factory:r,factoryData:i});return{domain:{name:a,version:o,chainId:Number(s),verifyingContract:c,salt:l},extensions:u,fields:t}}catch(e){let t=e;throw t.name===`ContractFunctionExecutionError`&&t.cause.name===`ContractFunctionZeroDataError`?new ah({address:n}):t}}var sh=[{inputs:[],name:`eip712Domain`,outputs:[{name:`fields`,type:`bytes1`},{name:`name`,type:`string`},{name:`version`,type:`string`},{name:`chainId`,type:`uint256`},{name:`verifyingContract`,type:`address`},{name:`salt`,type:`bytes32`},{name:`extensions`,type:`uint256[]`}],stateMutability:`view`,type:`function`}];$u(),gi(),D(),Fp(),Pu(),Ei(),Lt(),Ii(),On();function ch(e){let{authorizationList:t}=e;if(t)for(let e of t){let{chainId:t}=e,n=e.address;if(!Ci(n))throw new hi({address:n});if(t<0)throw new Pp({chainId:t})}uh(e)}function lh(e){let{blobVersionedHashes:t}=e;if(t){if(t.length===0)throw new Od;for(let e of t){let t=It(e),n=Dn(ji(e,0,1));if(t!==32)throw new kd({hash:e,size:t});if(n!==1)throw new Ad({hash:e,version:n})}}uh(e)}function uh(e){let{chainId:t,maxPriorityFeePerGas:n,maxFeePerGas:r,to:i}=e;if(t<=0)throw new Pp({chainId:t});if(i&&!Ci(i))throw new hi({address:i});if(r&&r>Qu)throw new Cu({maxFeePerGas:r});if(n&&r&&n>r)throw new Mu({maxFeePerGas:r,maxPriorityFeePerGas:n})}function dh(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a}=e;if(t<=0)throw new Pp({chainId:t});if(a&&!Ci(a))throw new hi({address:a});if(n||i)throw new E("`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid EIP-2930 Transaction attribute.");if(r&&r>Qu)throw new Cu({maxFeePerGas:r})}function fh(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a}=e;if(a&&!Ci(a))throw new hi({address:a});if(t!==void 0&&t<=0)throw new Pp({chainId:t});if(n||i)throw new E("`maxFeePerGas`/`maxPriorityFeePerGas` is not a valid Legacy Transaction attribute.");if(r&&r>Qu)throw new Cu({maxFeePerGas:r})}gi(),_o(),Ei();function ph(e){if(!e||e.length===0)return[];let t=[];for(let n=0;njn(e)),n=e.kzg,r=_d({blobs:t,kzg:n});f===void 0&&(f=Sd({commitments:r})),p===void 0&&(p=Md({blobs:t,commitments:r,proofs:vd({blobs:t,commitments:r,kzg:n})}))}let m=ph(u),h=[O(n),i?O(i):`0x`,l?O(l):`0x`,c?O(c):`0x`,r?O(r):`0x`,a??`0x`,o?O(o):`0x`,d??`0x`,m,s?O(s):`0x`,f??[],...bh(e,t)],g=[],_=[],v=[];if(p)for(let e=0;e{if(t.v>=35n)return(t.v-35n)/2n>0?t.v:27n+(t.v===35n?0n:1n);if(n>0)return BigInt(n*2)+BigInt(35n+t.v-27n);let e=27n+(t.v===27n?0n:1n);if(t.v!==e)throw new co({v:t.v});return e})(),r=Sn(t.r),i=Sn(t.s);l=[...l,O(e),r===`0x00`?`0x`:r,i===`0x00`?`0x`:i]}else n>0&&(l=[...l,O(n),`0x`,`0x`]);return mu(l)}function bh(e,t){let n=t??e,{v:r,yParity:i}=n;if(n.r===void 0||n.s===void 0||r===void 0&&i===void 0)return[];let a=Sn(n.r),o=Sn(n.s);return[typeof i==`number`?i?O(1):`0x`:r===0n?`0x`:r===1n?O(1):r===27n?`0x`:O(1),a===`0x00`?`0x`:a,o===`0x00`?`0x`:o]}k();function xh(e){if(!e||e.length===0)return[];let t=[];for(let n of e){let{chainId:e,nonce:r,...i}=n,a=n.address;t.push([e?kn(e):`0x`,a,r?kn(r):`0x`,...bh({},i)])}return t}Si(),Hd();async function Sh({address:e,authorization:t,signature:n}){return Vd(bi(e),await bu({authorization:t,signature:n}))}vi();var Ch=new _i(8192);function wh(e,{enabled:t=!0,id:n}){if(!t||!n)return e();if(Ch.get(n))return Ch.get(n);let r=e().finally(()=>Ch.delete(n));return Ch.set(n,r),r}D(),Ao(),ss(),k(),Wa();function Th(e,t={}){return async(n,r={})=>{let{dedupe:i=!1,methods:a,retryDelay:o=150,retryCount:s=3,uid:c}={...t,...r},{method:l}=n;if(a?.exclude?.includes(l)||a?.include&&!a.include.includes(l))throw new Uo(Error(`method not supported`),{method:l});return wh(()=>Um(async()=>{try{return await e(n)}catch(e){let t=e;switch(t.code){case Po.code:throw new Po(t);case Fo.code:throw new Fo(t);case Io.code:throw new Io(t,{method:n.method});case Lo.code:throw new Lo(t);case Ro.code:throw new Ro(t);case zo.code:throw new zo(t);case Bo.code:throw new Bo(t);case Vo.code:throw new Vo(t);case Ho.code:throw new Ho(t);case Uo.code:throw new Uo(t,{method:n.method});case Wo.code:throw new Wo(t);case Go.code:throw new Go(t);case Ko.code:throw new Ko(t);case qo.code:throw new qo(t);case Jo.code:throw new Jo(t);case Yo.code:throw new Yo(t);case Xo.code:throw new Xo(t);case Zo.code:throw new Zo(t);case Qo.code:throw new Qo(t);case $o.code:throw new $o(t);case es.code:throw new es(t);case ts.code:throw new ts(t);case ns.code:throw new ns(t);case rs.code:throw new rs(t);case is.code:throw new is(t);case 5e3:throw new Ko(t);case as.code:throw new as(t);default:throw e instanceof E?e:new os(t)}}},{delay:({count:e,error:t})=>{if(t&&t instanceof Do){let e=t?.headers?.get(`Retry-After`);if(e?.match(/\d/))return Number.parseInt(e,10)*1e3}return~~(1<Eh(e)}),{enabled:i,id:i?Mn(`${c}.${Ua(n)}`):void 0})}}function Eh(e){return`code`in e&&typeof e.code==`number`?e.code===-1||e.code===Wo.code||e.code===Ro.code:e instanceof Do&&e.status?e.status===403||e.status===408||e.status===413||e.status===429||e.status===500||e.status===502||e.status===503||e.status===504:!0}function L(e){let t={formatters:void 0,fees:void 0,serializers:void 0,...e};function n(e){return t=>{let r=typeof t==`function`?t(e):t,i={...e,...r};return Object.assign(i,{extend:n(i)})}}return Object.assign(t,{extend:n(t)})}function Dh(){return{}}function Oh(e,{errorInstance:t=Error(`timed out`),timeout:n,signal:r}){return new Promise((i,a)=>{(async()=>{let o;try{let s=new AbortController;n>0&&(o=setTimeout(()=>{r?s.abort():a(t)},n)),i(await e({signal:s?.signal||null}))}catch(e){e?.name===`AbortError`&&a(t),a(e)}finally{clearTimeout(o)}})()})}function kh(){return{current:0,take(){return this.current++},reset(){this.current=0}}}var Ah=kh();Ao(),Wa();function jh(e,t={}){let{url:n,headers:r}=Mh(e);return{async request(e){let{body:i,fetchFn:a=t.fetchFn??fetch,onRequest:o=t.onRequest,onResponse:s=t.onResponse,timeout:c=t.timeout??1e4}=e,l={...t.fetchOptions??{},...e.fetchOptions??{}},{headers:u,method:d,signal:f}=l;try{let e=await Oh(async({signal:e})=>{let t={...l,body:Ua(Array.isArray(i)?i.map(e=>({jsonrpc:`2.0`,id:e.id??Ah.take(),...e})):{jsonrpc:`2.0`,id:i.id??Ah.take(),...i}),headers:{...r,"Content-Type":`application/json`,...u},method:d||`POST`,signal:f||(c>0?e:null)},s=new Request(n,t),p=await o?.(s,t)??{...t,url:n};return await a(p.url??n,p)},{errorInstance:new ko({body:i,url:n}),timeout:c,signal:!0});s&&await s(e);let t;if(e.headers.get(`Content-Type`)?.startsWith(`application/json`))t=await e.json();else{t=await e.text();try{t=JSON.parse(t||`{}`)}catch(n){if(e.ok)throw n;t={error:t}}}if(!e.ok){if(typeof t.error?.code==`number`&&typeof t.error?.message==`string`)return t;throw new Do({body:i,details:Ua(t.error)||e.statusText,headers:e.headers,status:e.status,url:n})}return t}catch(e){throw e instanceof Do||e instanceof ko?e:new Do({body:i,cause:e,url:n})}}}}function Mh(e){try{let t=new URL(e),n=(()=>{if(t.username){let e=`${decodeURIComponent(t.username)}:${decodeURIComponent(t.password)}`;return t.username=``,t.password=``,{url:t.toString(),headers:{Authorization:`Basic ${btoa(e)}`}}}})();return{url:t.toString(),...n}}catch{return{url:e}}}var Nh=`Ethereum Signed Message: -`;Ai(),Lt(),k();function Ph(e){let t=typeof e==`string`?Mn(e):typeof e.raw==`string`?e.raw:jn(e.raw);return Di([Mn(`${Nh}${It(t)}`),t])}ri();function Fh(e,t){return ni(Ph(e),t)}Wa(),D();var Ih=class extends E{constructor({domain:e}){super(`Invalid domain "${Ua(e)}".`,{metaMessages:[`Must be a valid EIP-712 domain.`]})}},Lh=class extends E{constructor({primaryType:e,types:t}){super(`Invalid primary type \`${e}\` must be one of \`${JSON.stringify(Object.keys(t))}\`.`,{docsPath:`/api/glossary/Errors#typeddatainvalidprimarytypeerror`,metaMessages:["Check that the primary type is a key in `types`."]})}},Rh=class extends E{constructor({type:e}){super(`Struct type "${e}" is invalid.`,{metaMessages:[`Struct type must not be a Solidity type.`],name:`InvalidStructTypeError`})}};cn(),gi(),Ei(),Lt(),k(),zi(),Wa();function zh(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{let n={...t};for(let t of e){let{name:e,type:r}=t;r===`address`&&(n[e]=n[e].toLowerCase())}return n};return Ua({domain:!i.EIP712Domain||!t?{}:a(i.EIP712Domain,t),message:(()=>{if(r!==`EIP712Domain`)return a(i[r],n)})(),primaryType:r,types:i})}function Bh(e){let{domain:t,message:n,primaryType:r,types:i}=e,a=(e,t)=>{for(let n of e){let{name:e,type:r}=n,o=t[e],s=r.match(Ri);if(s&&(typeof o==`number`||typeof o==`bigint`)){let[e,t,n]=s;O(o,{signed:t===`int`,size:Number.parseInt(n,10)/8})}if(r===`address`&&typeof o==`string`&&!Ci(o))throw new hi({address:o});let c=r.match(Li);if(c){let[e,t]=c;if(t&&It(o)!==Number.parseInt(t,10))throw new nn({expectedSize:Number.parseInt(t,10),givenSize:It(o)})}let l=i[r];l&&(Hh(r),a(l,o))}};if(i.EIP712Domain&&t){if(typeof t!=`object`)throw new Ih({domain:t});a(i.EIP712Domain,t)}if(r!==`EIP712Domain`)if(i[r])a(i[r],n);else throw new Lh({primaryType:r,types:i})}function Vh({domain:e}){return[typeof e?.name==`string`&&{name:`name`,type:`string`},e?.version&&{name:`version`,type:`string`},(typeof e?.chainId==`number`||typeof e?.chainId==`bigint`)&&{name:`chainId`,type:`uint256`},e?.verifyingContract&&{name:`verifyingContract`,type:`address`},e?.salt&&{name:`salt`,type:`bytes32`}].filter(Boolean)}function Hh(e){if(e===`address`||e===`bool`||e===`string`||e.startsWith(`bytes`)||e.startsWith(`uint`)||e.startsWith(`int`))throw new Rh({type:e})}Qi(),Ai(),k(),ri();function Uh(e){let{domain:t={},message:n,primaryType:r}=e,i={EIP712Domain:Vh({domain:t}),...e.types};Bh({domain:t,message:n,primaryType:r,types:i});let a=[`0x1901`];return t&&a.push(Wh({domain:t,types:i})),r!==`EIP712Domain`&&a.push(Gh({data:n,primaryType:r,types:i})),ni(Di(a))}function Wh({domain:e,types:t}){return Gh({data:e,primaryType:`EIP712Domain`,types:t})}function Gh({data:e,primaryType:t,types:n}){return ni(Kh({data:e,primaryType:t,types:n}))}function Kh({data:e,primaryType:t,types:n}){let r=[{type:`bytes32`}],i=[qh({primaryType:t,types:n})];for(let a of n[t]){let[t,o]=Xh({types:n,name:a.name,type:a.type,value:e[a.name]});r.push(t),i.push(o)}return Bi(r,i)}function qh({primaryType:e,types:t}){return ni(kn(Jh({primaryType:e,types:t})))}function Jh({primaryType:e,types:t}){let n=``,r=Yh({primaryType:e,types:t});r.delete(e);let i=[e,...Array.from(r).sort()];for(let e of i)n+=`${e}(${t[e].map(({name:e,type:t})=>`${t} ${e}`).join(`,`)})`;return n}function Yh({primaryType:e,types:t},n=new Set){let r=e.match(/^\w*/u)?.[0];if(n.has(r)||t[r]===void 0)return n;n.add(r);for(let e of t[r])Yh({primaryType:e.type,types:t},n);return n}function Xh({types:e,name:t,type:n,value:r}){if(e[n]!==void 0)return[{type:`bytes32`},ni(Kh({data:r,primaryType:n,types:e}))];if(n===`bytes`)return[{type:`bytes32`},ni(r)];if(n===`string`)return[{type:`bytes32`},ni(kn(r))];if(n.lastIndexOf(`]`)===n.length-1){let i=n.slice(0,n.lastIndexOf(`[`)),a=r.map(n=>Xh({name:t,type:i,types:e,value:n}));return[{type:`bytes32`},ni(Bi(a.map(([e])=>e),a.map(([,e])=>e)))]}return[{type:n},r]}var Zh={checksum:new class extends Map{constructor(e){super(),Object.defineProperty(this,`maxSize`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.maxSize=e}get(e){let t=super.get(e);return super.has(e)&&t!==void 0&&(this.delete(e),super.set(e,t)),t}set(e,t){if(super.set(e,t),this.maxSize&&this.size>this.maxSize){let e=this.keys().next().value;e&&this.delete(e)}return this}}(8192)}.checksum;ti(),Hf(),pp();function Qh(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=$r(yf(e));return n===`Bytes`?r:qf(r)}function $h(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=yd(yf(e));return n===`Bytes`?r:qf(r)}function eg(e){return rp(e)&&Qf(e)===32}Hf(),Qd(),pp(),gf();function tg(e,t={}){let{compressed:n}=t,{prefix:r,x:i,y:a}=e;if(n===!1||typeof i==`bigint`&&typeof a==`bigint`){if(r!==4)throw new cg({prefix:r,cause:new ug});return}if(n===!0||typeof i==`bigint`&&a===void 0){if(r!==3&&r!==2)throw new cg({prefix:r,cause:new lg});return}throw new sg({publicKey:e})}function ng(e){let t=(()=>{if(rp(e))return ig(e);if(Pf(e))return rg(e);let{prefix:t,x:n,y:r}=e;return typeof n==`bigint`&&typeof r==`bigint`?{prefix:t??4,x:n,y:r}:{prefix:t,x:n}})();return tg(t),t}function rg(e){return ig(qf(e))}function ig(e){if(e.length!==132&&e.length!==130&&e.length!==68)throw new dg({publicKey:e});return e.length===130?{prefix:4,x:BigInt(I(e,0,32)),y:BigInt(I(e,32,64))}:e.length===132?{prefix:Number(I(e,0,1)),x:BigInt(I(e,1,33)),y:BigInt(I(e,33,65))}:{prefix:Number(I(e,0,1)),x:BigInt(I(e,1,33))}}function ag(e,t={}){return xf(og(e,t))}function og(e,t={}){tg(e);let{prefix:n,x:r,y:i}=e,{includePrefix:a=!0}=t;return Wf(a?F(n,{size:1}):`0x`,F(r,{size:32}),typeof i==`bigint`?F(i,{size:32}):`0x`)}var sg=class extends P{constructor({publicKey:e}){super(`Value \`${mf(e)}\` is not a valid public key.`,{metaMessages:[`Public key must contain:`,"- an `x` and `prefix` value (compressed)","- an `x`, `y`, and `prefix` value (uncompressed)"]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidError`})}},cg=class extends P{constructor({prefix:e,cause:t}){super(`Prefix "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidPrefixError`})}},lg=class extends P{constructor(){super(`Prefix must be 2 or 3 for compressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidCompressedPrefixError`})}},ug=class extends P{constructor(){super(`Prefix must be 4 for uncompressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidUncompressedPrefixError`})}},dg=class extends P{constructor({publicKey:e}){super(`Value \`${e}\` is an invalid public key size.`,{metaMessages:[`Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).`,`Received ${Qf(Gf(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidSerializedSizeError`})}};Hf(),Qd();var fg=/^0x[a-fA-F0-9]{40}$/;function pg(e,t={}){let{strict:n=!0}=t;if(!fg.test(e))throw new yg({address:e,cause:new bg});if(n){if(e.toLowerCase()===e)return;if(mg(e)!==e)throw new yg({address:e,cause:new xg})}}function mg(e){if(Zh.has(e))return Zh.get(e);pg(e,{strict:!1});let t=e.substring(2).toLowerCase(),n=Qh(Sf(t),{as:`Bytes`}),r=t.split(``);for(let e=0;e<40;e+=2)n[e>>1]>>4>=8&&r[e]&&(r[e]=r[e].toUpperCase()),(n[e>>1]&15)>=8&&r[e+1]&&(r[e+1]=r[e+1].toUpperCase());let i=`0x${r.join(``)}`;return Zh.set(e,i),i}function hg(e,t={}){let{checksum:n=!1}=t;return pg(e),n?mg(e):e}function gg(e,t={}){return hg(`0x${Qh(`0x${og(e).slice(4)}`).substring(26)}`,t)}function _g(e,t){return pg(e,{strict:!1}),pg(t,{strict:!1}),e.toLowerCase()===t.toLowerCase()}function vg(e,t={}){let{strict:n=!0}=t??{};try{return pg(e,{strict:n}),!0}catch{return!1}}var yg=class extends P{constructor({address:e,cause:t}){super(`Address "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidAddressError`})}},bg=class extends P{constructor(){super(`Address is not a 20 byte (40 hexadecimal character) value.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidInputError`})}},xg=class extends P{constructor(){super(`Address does not match its checksum counterpart.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidChecksumError`})}},Sg=/^(.*)\[([0-9]*)\]$/,Cg=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,wg=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n));var Tg=2n**256n-1n;Hf(),Qd(),pp();function Eg(e,t,n){let{checksumAddress:r,staticPosition:i}=n,a=Kg(t.type);if(a){let[n,o]=a;return Ag(e,{...t,type:o},{checksumAddress:r,length:n,staticPosition:i})}if(t.type===`tuple`)return Pg(e,t,{checksumAddress:r,staticPosition:i});if(t.type===`address`)return kg(e,{checksum:r});if(t.type===`bool`)return jg(e);if(t.type.startsWith(`bytes`))return Mg(e,t,{staticPosition:i});if(t.type.startsWith(`uint`)||t.type.startsWith(`int`))return Ng(e,t);if(t.type===`string`)return Fg(e,{staticPosition:i});throw new l_(t.type)}var Dg=32,Og=32;function kg(e,t={}){let{checksum:n=!1}=t;return[(e=>n?mg(e):e)(qf(Df(e.readBytes(32),-20))),32]}function Ag(e,t,n){let{checksumAddress:r,length:i,staticPosition:a}=n;if(!i){let n=a+Af(e.readBytes(Og)),i=n+Dg;e.setPosition(n);let o=Af(e.readBytes(Dg)),s=qg(t),c=0,l=[];for(let n=0;n48?Of(i,{signed:n}):Af(i,{signed:n}),32]}function Pg(e,t,n){let{checksumAddress:r,staticPosition:i}=n,a=t.components.length===0||t.components.some(({name:e})=>!e),o=a?[]:{},s=0;if(qg(t)){let n=i+Af(e.readBytes(Og));for(let i=0;i0?Wf(t,e):t}}if(o)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:Wf(...s.map(({encoded:e})=>e))}}function Vg(e,{type:t}){let[,n]=t.split(`bytes`),r=Qf(e);if(!n){let t=e;return r%32!=0&&(t=Xf(t,Math.ceil((e.length-2)/2/32)*32)),{dynamic:!0,encoded:Wf(Yf(F(r,{size:32})),t)}}if(r!==Number.parseInt(n,10))throw new o_({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:Xf(e)}}function Hg(e){if(typeof e!=`boolean`)throw new P(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:Yf(Kf(e))}}function Ug(e,{signed:t,size:n}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function Kg(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}function qg(e){let{type:t}=e;if(t===`string`||t===`bytes`||t.endsWith(`[]`))return!0;if(t===`tuple`)return e.components?.some(qg);let n=Kg(e.type);return!!(n&&qg({...e,type:n[1]}))}Qd();var Jg={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new Qg({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new Zg({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new Xg({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new Xg({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}};function Yg(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(Jg);return n.bytes=e,n.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var Xg=class extends P{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.NegativeOffsetError`})}},Zg=class extends P{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.PositionOutOfBoundsError`})}},Qg=class extends P{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.RecursiveReadLimitExceededError`})}};kt(),Hf(),Qd(),pp();function $g(e,t,n={}){let{as:r=`Array`,checksumAddress:i=!1}=n,a=typeof t==`string`?xf(t):t,o=Yg(a);if(Ef(a)===0&&e.length>0)throw new i_;if(Ef(a)&&Ef(a)<32)throw new r_({data:typeof t==`string`?t:qf(t),parameters:e,size:Ef(a)});let s=0,c=r===`Array`?[]:{};for(let t=0;t{if(typeof e==`string`){if(e.length>3&&e.length%2!=0)throw new lp(e);return xf(e)}return e})(),{recursiveReadLimit:1/0}),n)}function f_(e,t=`Hex`){if(e.bytes.length===0)return t===`Hex`?qf(e.bytes):e.bytes;let n=e.readByte();if(n<128&&e.decrementPosition(1),n<192){let r=p_(e,n,128),i=e.readBytes(r);return t===`Hex`?qf(i):i}return m_(e,p_(e,n,192),t)}function p_(e,t,n){if(n===128&&t<128)return 1;if(t<=n+55)return t-n;if(t===n+55+1)return e.readUint8();if(t===n+55+2)return e.readUint16();if(t===n+55+3)return e.readUint24();if(t===n+55+4)return e.readUint32();throw new P(`Invalid RLP prefix`)}function m_(e,t,n){let r=e.position,i=[];for(;e.position-r__(e))):y_(e)}function v_(e){let t=e.reduce((e,t)=>e+t.length,0),n=b_(t);return{length:t<=55?1+t:1+n+t,encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function y_(e){let t=typeof e==`string`?xf(e):e,n=b_(t.length);return{length:t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length,encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function b_(e){if(e<=255)return 1;if(e<=65535)return 2;if(e<=16777215)return 3;if(e<=4294967295)return 4;throw new P(`Length is too large.`)}Qd(),pp(),gf();function x_(e,t={}){let{recovered:n}=t;if(e.r===void 0||e.s===void 0||n&&e.yParity===void 0)throw new F_({signature:e});if(e.r<0n||e.r>Tg)throw new I_({value:e.r});if(e.s<0n||e.s>Tg)throw new L_({value:e.s});if(typeof e.yParity==`number`&&e.yParity!==0&&e.yParity!==1)throw new R_({value:e.yParity})}function S_(e){return C_(qf(e))}function C_(e){if(e.length!==130&&e.length!==132)throw new P_({signature:e});let t=BigInt(I(e,0,32)),n=BigInt(I(e,32,64)),r=(()=>{let t=Number(`0x${e.slice(130)}`);if(!Number.isNaN(t))try{return M_(t)}catch{throw new R_({value:t})}})();return r===void 0?{r:t,s:n}:{r:t,s:n,yParity:r}}function w_(e){if(e.r!==void 0&&e.s!==void 0)return T_(e)}function T_(e){let t=typeof e==`string`?C_(e):e instanceof Uint8Array?S_(e):typeof e.r==`string`?D_(e):e.v?E_(e):{r:e.r,s:e.s,...e.yParity===void 0?{}:{yParity:e.yParity}};return x_(t),t}function E_(e){return{r:e.r,s:e.s,yParity:M_(e.v)}}function D_(e){let t=(()=>{let t=e.v?Number(e.v):void 0,n=e.yParity?Number(e.yParity):void 0;if(typeof t==`number`&&typeof n!=`number`&&(n=M_(t)),typeof n!=`number`)throw new R_({value:e.yParity});return n})();return{r:BigInt(e.r),s:BigInt(e.s),yParity:t}}function O_(e){let[t,n,r]=e;return T_({r:n===`0x`?0n:BigInt(n),s:r===`0x`?0n:BigInt(r),yParity:t===`0x`?0:Number(t)})}function k_(e){x_(e);let t=e.r,n=e.s;return Wf(F(t,{size:32}),F(n,{size:32}),typeof e.yParity==`number`?F(N_(e.yParity),{size:1}):`0x`)}function A_(e){let{r:t,s:n,yParity:r}=e;return{r:F(t,{size:32}),s:F(n,{size:32}),yParity:r===0?`0x0`:`0x1`}}function j_(e){let{r:t,s:n,yParity:r}=e;return[r?`0x01`:`0x`,t===0n?`0x`:$f(F(t)),n===0n?`0x`:$f(F(n))]}function M_(e){if(e===0||e===27)return 0;if(e===1||e===28)return 1;if(e>=35)return e%2==0?1:0;throw new z_({value:e})}function N_(e){if(e===0)return 27;if(e===1)return 28;throw new R_({value:e})}var P_=class extends P{constructor({signature:e}){super(`Value \`${e}\` is an invalid signature size.`,{metaMessages:[`Expected: 64 bytes or 65 bytes.`,`Received ${Qf(Gf(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSerializedSizeError`})}},F_=class extends P{constructor({signature:e}){super(`Signature \`${mf(e)}\` is missing either an \`r\`, \`s\`, or \`yParity\` property.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.MissingPropertiesError`})}},I_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid r value. r must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidRError`})}},L_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid s value. s must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSError`})}},R_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid y-parity value. Y-parity must be 0 or 1.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidYParityError`})}},z_=class extends P{constructor({value:e}){super(`Value \`${e}\` is an invalid v value. v must be 27, 28 or >=35.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidVError`})}};pp();function B_(e,t={}){return typeof e.chainId==`string`?V_(e):{...e,...t.signature}}function V_(e){let{address:t,chainId:n,nonce:r}=e,i=w_(e);return{address:t,chainId:Number(n),nonce:BigInt(r),...i}}function H_(e){return e.map(V_)}function U_(e){let{address:t,chainId:n,nonce:r,...i}=e;return{address:t,chainId:F(n),nonce:F(r),...A_(i)}}function W_(e){return e.map(U_)}uu(),Hf(),pp();function G_(e){return gg(K_(e))}function K_(e){let{payload:t,signature:n}=e,{r,s:i,yParity:a}=n;return ng(new Zl.Signature(BigInt(r),BigInt(i)).addRecoveryBit(a).recoverPublicKey(Gf(t).substring(2)))}function q_(e){let{address:t,hash:n,payload:r,publicKey:i,signature:a}=e;return t?_g(t,G_({payload:r,signature:a})):Zl.verify(a,yf(r),ag(i),...n?[{prehash:!0,lowS:!0}]:[])}Qd(),pp();var J_=n_(`(uint256 chainId, address delegation, uint256 nonce, uint8 yParity, uint256 r, uint256 s), address to, bytes data`);function Y_(e){if(typeof e==`string`){if(I(e,-32)!==`0x8010801080108010801080108010801080108010801080108010801080108010`)throw new Q_(e)}else x_(e.authorization)}function X_(e){Y_(e);let t=tp(I(e,-64,-32)),n=I(e,-t-64,-64),r=I(e,0,-t-64),[i,a,o]=$g(J_,n);return{authorization:B_({address:i.delegation,chainId:Number(i.chainId),nonce:i.nonce,yParity:i.yParity,r:i.r,s:i.s}),signature:r,...o&&o!==`0x`?{data:o,to:a}:{}}}function Z_(e){try{return Y_(e),!0}catch{return!1}}var Q_=class extends P{constructor(e){super(`Value \`${e}\` is an invalid ERC-8010 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc8010.InvalidWrappedSignatureError`})}};_o(),k();async function $_(e,{blockHash:t,blockNumber:n,blockTag:r,hash:i,index:a,sender:o,nonce:s}){let c=r||`latest`,l=n===void 0?void 0:O(n),u=null;if(i?u=await e.request({method:`eth_getTransactionByHash`,params:[i]},{dedupe:!0}):t?u=await e.request({method:`eth_getTransactionByBlockHashAndIndex`,params:[t,O(a)]},{dedupe:!0}):(l||c)&&typeof a==`number`?u=await e.request({method:`eth_getTransactionByBlockNumberAndIndex`,params:[l||c,O(a)]},{dedupe:!!l}):o&&typeof s==`number`&&(u=await e.request({method:`eth_getTransactionBySenderAndNonce`,params:[o,O(s)]},{dedupe:!0})),!u)throw new po({blockHash:t,blockNumber:n,blockTag:c,hash:i,index:a});return(e.chain?.formatters?.transaction?.format||sd)(u,`getTransaction`)}_o();async function ev(e,{hash:t}){let n=await e.request({method:`eth_getTransactionReceipt`,params:[t]},{dedupe:!0});if(!n)throw new mo({hash:t});return(e.chain?.formatters?.transactionReceipt?.format||Gm)(n,`getTransactionReceipt`)}Qd();function tv(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;onv(Object.values(e)[n],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>nv(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function rv(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return rv(i.components,a.components,n[r]);let o=[i.type,a.type];if(o.includes(`address`)&&o.includes(`bytes20`)||(o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`))&&vg(n[r],{strict:!1}))return o}}kt(),Qd(),pp();function iv(e,t={}){let{prepare:n=!0}=t,r=Array.isArray(e)||typeof e==`string`?Tt(e):e;return{...r,...n?{hash:cv(r)}:{}}}function av(e,t,n){let{args:r=[],prepare:i=!0}=n??{},a=rp(t,{strict:!1}),o=e.filter(e=>a?e.type===`function`||e.type===`error`?ov(e)===I(t,0,4):e.type===`event`?cv(e)===t:!1:`name`in e&&e.name===t);if(o.length===0)throw new uv({name:t});if(o.length===1)return{...o[0],...i?{hash:cv(o[0])}:{}};let s;for(let e of o)if(`inputs`in e){if(!r||r.length===0){if(!e.inputs||e.inputs.length===0)return{...e,...i?{hash:cv(e)}:{}};continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===r.length&&r.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?nv(t,r):!1})){if(s&&`inputs`in s&&s.inputs){let t=rv(e.inputs,s.inputs,r);if(t)throw new lv({abiItem:e,type:t[0]},{abiItem:s,type:t[1]})}s=e}}let c=(()=>{if(s)return s;let[e,...t]=o;return{...e,overloads:t}})();if(!c)throw new uv({name:t});return{...c,...i?{hash:cv(c)}:{}}}function ov(...e){return I(cv((()=>{if(Array.isArray(e[0])){let[t,n]=e;return av(t,n)}return e[0]})()),0,4)}function sv(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return av(t,n)}return e[0]})();return tv(typeof t==`string`?t:ue(t))}function cv(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return av(t,n)}return e[0]})();return typeof t!=`string`&&`hash`in t&&t.hash?t.hash:Qh(Jf(sv(t)))}var lv=class extends P{constructor(e,t){super(`Found ambiguous types in overloaded ABI Items.`,{metaMessages:[`\`${e.type}\` in \`${tv(ue(e.abiItem))}\`, and`,`\`${t.type}\` in \`${tv(ue(t.abiItem))}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.AmbiguityError`})}},uv=class extends P{constructor({name:e,data:t,type:n=`item`}){let r=e?` with name "${e}"`:t?` with data "${t}"`:``;super(`ABI ${n}${r} not found.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.NotFoundError`})}},dv=`0x0000000000000000000000000000000000000000`;Qd(),pp();var fv=`0x6492649264926492649264926492649264926492649264926492649264926492`;function pv(e){if(I(e,-32)!==`0x6492649264926492649264926492649264926492649264926492649264926492`)throw new gv(e)}function mv(e){let{data:t,signature:n,to:r}=e;return Wf(e_(n_(`address, bytes, bytes`),[r,t,n]),fv)}function hv(e){try{return pv(e),!0}catch{return!1}}var gv=class extends P{constructor(e){super(`Value \`${e}\` is an invalid ERC-6492 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc6492.InvalidWrappedSignatureError`})}};uu(),On(),Un();function _v({r:e,s:t,to:n=`hex`,v:r,yParity:i}){let a=(()=>{if(i===0||i===1)return i;if(r&&(r===27n||r===28n||r>=35n))return r%2n==0n?1:0;throw Error("Invalid `v` or `yParity` value")})(),o=`0x${new Zl.Signature(Tn(e),Tn(t)).toCompactHex()}${a===0?`1b`:`1c`}`;return n===`hex`?o:Rn(o)}Cp(),kp(),Eo(),Rp(),da(),Si(),Hd(),Ai(),Ft(),On(),k(),xm();async function vv(e,t){let{address:n,chain:r=e.chain,hash:i,erc6492VerifierAddress:a=t.universalSignatureVerifierAddress??r?.contracts?.erc6492Verifier?.address,multicallAddress:o=t.multicallAddress??r?.contracts?.multicall3?.address,mode:s=`auto`}=t;if(r?.verifyHash)return await r.verifyHash(e,t);let c=(()=>{let e=t.signature;return Pt(e)?e:typeof e==`object`&&`r`in e&&`s`in e?_v(e):jn(e)})();try{if(s===`eoa`)try{if(Vd(bi(n),await pu({hash:i,signature:c})))return!0}catch{}return Z_(c)?await yv(e,{...t,multicallAddress:o,signature:c}):await bv(e,{...t,verifierAddress:a,signature:c})}catch(e){if(s!==`eoa`)try{if(Vd(bi(n),await pu({hash:i,signature:c})))return!0}catch{}if(e instanceof Sv)return!1;throw e}}async function yv(e,t){let{address:n,blockNumber:r,blockTag:i,hash:a,multicallAddress:o}=t,{authorization:s,data:c,signature:l,to:u}=X_(t.signature);if(await ih(e,{address:n,blockNumber:r,blockTag:i})===ki([`0xef0100`,s.address]))return await xv(e,{address:n,blockNumber:r,blockTag:i,hash:a,signature:l});let d={address:s.address,chainId:Number(s.chainId),nonce:Number(s.nonce),r:O(s.r,{size:32}),s:O(s.s,{size:32}),yParity:s.yParity};if(!await Sh({address:n,authorization:d}))throw new Sv;let f=await T(e,Sm,`readContract`)({...o?{address:o}:{code:Op},authorizationList:[d],abi:vp,blockNumber:r,blockTag:`pending`,functionName:`aggregate3`,args:[[...c?[{allowFailure:!0,target:u??n,callData:c}]:[],{allowFailure:!0,target:n,callData:ua({abi:xp,functionName:`isValidSignature`,args:[a,l]})}]]});if((f[f.length-1]?.returnData)?.startsWith(`0x1626ba7e`))return!0;throw new Sv}async function bv(e,t){let{address:n,factory:r,factoryData:i,hash:a,signature:o,verifierAddress:s,...c}=t,l=await(async()=>!r&&!i||hv(o)?o:mv({data:i,signature:o,to:r}))(),u=s?{to:s,data:ua({abi:Sp,functionName:`isValidSig`,args:[n,a,l]}),...c}:{data:Ip({abi:Sp,args:[n,a,l],bytecode:Dp}),...c},{data:d}=await T(e,hm,`call`)(u).catch(e=>{throw e instanceof xo?new Sv:e});if(En(d??`0x0`))return!0;throw new Sv}async function xv(e,t){let{address:n,blockNumber:r,blockTag:i,hash:a,signature:o}=t;if((await T(e,Sm,`readContract`)({address:n,abi:xp,args:[a,o],blockNumber:r,blockTag:i,functionName:`isValidSignature`}).catch(e=>{throw e instanceof So?new Sv:e})).startsWith(`0x1626ba7e`))return!0;throw new Sv}var Sv=class extends Error{};On(),Wa();function Cv(e,{emitOnBegin:t=!1,emitMissed:n=!1,onBlockNumber:r,onError:i,poll:a,pollingInterval:o=e.pollingInterval}){let s=a===void 0?!(e.transport.type===`webSocket`||e.transport.type===`ipc`||e.transport.type===`fallback`&&(e.transport.transports[0].config.type===`webSocket`||e.transport.transports[0].config.type===`ipc`)):a,c;return s?Em(Ua([`watchBlockNumber`,e.uid,t,n,o]),{onBlockNumber:r,onError:i},r=>Om(async()=>{try{let t=await T(e,Pm,`getBlockNumber`)({cacheTime:0});if(c!==void 0){if(t===c)return;if(t-c>1&&n)for(let e=c+1n;ec)&&(r.onBlockNumber(t,c),c=t)}catch(e){r.onError?.(e)}},{emitOnBegin:t,interval:o})):Em(Ua([`watchBlockNumber`,e.uid,t,n]),{onBlockNumber:r,onError:i},t=>{let n=!0,r=()=>n=!1;return(async()=>{try{let{unsubscribe:i}=await(()=>{if(e.transport.type===`fallback`){let t=e.transport.transports.find(e=>e.config.type===`webSocket`||e.config.type===`ipc`);return t?t.value:e.transport}return e.transport})().subscribe({params:[`newHeads`],onData(e){if(!n)return;let r=Tn(e.result?.number);t.onBlockNumber(r,c),c=r},onError(e){t.onError?.(e)}});r=i,n||r()}catch(e){i?.(e)}})(),()=>r()})}_o(),Wp(),Wa();async function wv(e,t){let{checkReplacement:n=!0,confirmations:r=1,hash:i,onReplaced:a,retryCount:o=6,retryDelay:s=({count:e})=>~~(1<{g?.(),h?.(),y(new go({hash:i}))},c):void 0;return h=Em(l,{onReplaced:a,resolve:v,reject:y},async t=>{if(p=await T(e,ev,`getTransactionReceipt`)({hash:i}).catch(()=>void 0),p&&r<=1){clearTimeout(b),t.resolve(p),h?.();return}g=T(e,Cv,`watchBlockNumber`)({emitMissed:!0,emitOnBegin:!0,poll:!0,pollingInterval:u,async onBlockNumber(a){let c=e=>{clearTimeout(b),g?.(),e(),h?.()},l=a;if(!m)try{if(p){if(r>1&&(!p.blockNumber||l-p.blockNumber+1nt.resolve(p));return}if(n&&!d&&(m=!0,await Um(async()=>{d=await T(e,$_,`getTransaction`)({hash:i}),d.blockNumber&&(l=d.blockNumber)},{delay:s,retryCount:o}),m=!1),p=await T(e,ev,`getTransactionReceipt`)({hash:i}),r>1&&(!p.blockNumber||l-p.blockNumber+1nt.resolve(p))}catch(n){if(n instanceof po||n instanceof mo){if(!d){m=!1;return}try{f=d,m=!0;let n=await Um(()=>T(e,fd,`getBlock`)({blockNumber:l,includeTransactions:!0}),{delay:s,retryCount:o,shouldRetry:({error:e})=>e instanceof ad});m=!1;let i=n.transactions.find(({from:e,nonce:t})=>e===f.from&&t===f.nonce);if(!i||(p=await T(e,ev,`getTransactionReceipt`)({hash:i.hash}),r>1&&(!p.blockNumber||l-p.blockNumber+1n{t.onReplaced?.({reason:a,replacedTransaction:f,transaction:i,transactionReceipt:p}),t.resolve(p)})}catch(e){c(()=>t.reject(e))}}else c(()=>t.reject(n))}}})}),_}_o();async function Tv(e,{serializedTransaction:t,throwOnReceiptRevert:n,timeout:r}){let i=await e.request({method:`eth_sendRawTransactionSync`,params:r?[t,r]:[t]},{retryCount:0}),a=(e.chain?.formatters?.transactionReceipt?.format||Gm)(i);if(a.status===`reverted`&&n)throw new ho({receipt:a});return a}k();async function Ev(e,{chain:t}){let{id:n,name:r,nativeCurrency:i,rpcUrls:a,blockExplorers:o}=t;await e.request({method:`wallet_addEthereumChain`,params:[{chainId:O(n),chainName:r,nativeCurrency:i,rpcUrls:a.default.http,blockExplorerUrls:o?Object.values(o).map(({url:e})=>e):void 0}]},{dedupe:!0,retryCount:0})}Rp();function Dv(e,t){let{abi:n,args:r,bytecode:i,...a}=t,o=Ip({abi:n,args:r,bytecode:i});return Bm(e,{...a,...a.authorizationList?{to:null}:{},data:o})}Si();async function Ov(e){return e.account?.type===`local`?[e.account.address]:(await e.request({method:`eth_accounts`},{dedupe:!0})).map(e=>yi(e))}oa(),k();async function kv(e,t={}){let{account:n=e.account,chainId:r}=t,i=n?aa(n):void 0,a=r?[i?.address,[O(r)]]:[i?.address],o=await e.request({method:`wallet_getCapabilities`,params:a}),s={};for(let[e,t]of Object.entries(o)){s[Number(e)]={};for(let[n,r]of Object.entries(t))n===`addSubAccount`&&(n=`unstable_addSubAccount`),s[Number(e)][n]=r}return typeof r==`number`?s[r]:s}async function Av(e){return await e.request({method:`wallet_getPermissions`},{dedupe:!0})}oa(),Hd();async function jv(e,t){let{account:n=e.account,chainId:r,nonce:i}=t;if(!n)throw new Fm({docsPath:`/docs/eip7702/prepareAuthorization`});let a=aa(n),o=(()=>{if(t.executor)return t.executor===`self`?t.executor:aa(t.executor)})(),s={address:t.contractAddress??t.address,chainId:r,nonce:i};return s.chainId===void 0&&(s.chainId=e.chain?.id??await T(e,Fd,`getChainId`)({})),s.nonce===void 0&&(s.nonce=await T(e,gd,`getTransactionCount`)({address:a.address,blockTag:`pending`}),(o===`self`||o?.address&&Vd(o.address,a.address))&&(s.nonce+=1)),s}Si();async function Mv(e){return(await e.request({method:`eth_requestAccounts`},{dedupe:!0,retryCount:0})).map(e=>bi(e))}async function Nv(e,t){return e.request({method:`wallet_requestPermissions`,params:[t]},{retryCount:0})}async function Pv(e,t){let{chain:n=e.chain}=t,r=t.timeout??Math.max((n?.blockTime??0)*3,5e3),i=await T(e,Ym,`sendCalls`)(t);return await T(e,Zm,`waitForCallsStatus`)({...t,id:i.id,timeout:r})}oa(),D(),_o(),Ai(),zu(),Ku(),vi(),td();var Fv=new _i(128);async function Iv(e,t){let{account:n=e.account,assertChainId:r=!0,chain:i=e.chain,accessList:a,authorizationList:o,blobs:s,data:c,dataSuffix:l=typeof e.dataSuffix==`string`?e.dataSuffix:e.dataSuffix?.value,gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,pollingInterval:g,throwOnReceiptRevert:_,type:v,value:y,...b}=t,x=t.timeout??Math.max((i?.blockTime??0)*3,5e3);if(n===void 0)throw new Fm({docsPath:`/docs/actions/wallet/sendTransactionSync`});let S=n?aa(n):null;try{ed(t);let n=await(async()=>{if(t.to)return t.to;if(t.to!==null&&o&&o.length>0)return await bu({authorization:o[0]}).catch(()=>{throw new E("`to` is required. Could not infer from `authorizationList`.")})})();if(S?.type===`json-rpc`||S===null){let t;i!==null&&(t=await T(e,Fd,`getChainId`)({}),r&&Lm({currentChainId:t,chain:i}));let ee=e.chain?.formatters?.transactionRequest?.format,C=(ee||Hu)({...Ru(b,{format:ee}),accessList:a,account:S,authorizationList:o,blobs:s,chainId:t,data:c&&Di([c,l??`0x`]),gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,to:n,type:v,value:y},`sendTransaction`),te=Fv.get(e.uid),ne=te?`wallet_sendTransaction`:`eth_sendTransaction`,re=await(async()=>{try{return await e.request({method:ne,params:[C]},{retryCount:0})}catch(t){if(te===!1)throw t;let n=t;if(n.name===`InvalidInputRpcError`||n.name===`InvalidParamsRpcError`||n.name===`MethodNotFoundRpcError`||n.name===`MethodNotSupportedRpcError`)return await e.request({method:`wallet_sendTransaction`,params:[C]},{retryCount:0}).then(t=>(Fv.set(e.uid,!0),t)).catch(t=>{let r=t;throw r.name===`MethodNotFoundRpcError`||r.name===`MethodNotSupportedRpcError`?(Fv.set(e.uid,!1),n):r});throw n}})(),ie=await T(e,wv,`waitForTransactionReceipt`)({checkReplacement:!1,hash:re,pollingInterval:g,timeout:x});if(_&&ie.status===`reverted`)throw new ho({receipt:ie});return ie}if(S?.type===`local`){let r=await T(e,zd,`prepareTransactionRequest`)({account:S,accessList:a,authorizationList:o,blobs:s,chain:i,data:c&&Di([c,l??`0x`]),gas:u,gasPrice:d,maxFeePerBlobGas:f,maxFeePerGas:p,maxPriorityFeePerGas:m,nonce:h,nonceManager:S.nonceManager,parameters:[...Ld,`sidecars`],type:v,value:y,...b,to:n}),g=i?.serializers?.transaction,x=await S.signTransaction(r,{serializer:g});return await T(e,Tv,`sendRawTransactionSync`)({serializedTransaction:x,throwOnReceiptRevert:_,timeout:t.timeout})}throw S?.type===`smart`?new Im({metaMessages:["Consider using the `sendUserOperation` Action instead."],docsPath:`/docs/actions/bundler/sendUserOperation`,type:`smart`}):new Im({docsPath:`/docs/actions/wallet/sendTransactionSync`,type:S?.type})}catch(e){throw e instanceof Im?e:Pd(e,{...t,account:S,chain:t.chain||void 0})}}async function Lv(e,t){let{id:n}=t;await e.request({method:`wallet_showCallsStatus`,params:[n]})}oa();async function Rv(e,t){let{account:n=e.account}=t;if(!n)throw new Fm({docsPath:`/docs/eip7702/signAuthorization`});let r=aa(n);if(!r.signAuthorization)throw new Im({docsPath:`/docs/eip7702/signAuthorization`,metaMessages:["The `signAuthorization` Action does not support JSON-RPC Accounts."],type:r.type});let i=await jv(e,t);return r.signAuthorization(i)}oa(),k();async function zv(e,{account:t=e.account,message:n}){if(!t)throw new Fm({docsPath:`/docs/actions/wallet/signMessage`});let r=aa(t);if(r.signMessage)return r.signMessage({message:n});let i=typeof n==`string`?Mn(n):n.raw instanceof Uint8Array?kn(n.raw):n.raw;return e.request({method:`personal_sign`,params:[i,r.address]},{retryCount:0})}oa(),k(),Ku(),td();async function Bv(e,t){let{account:n=e.account,chain:r=e.chain,...i}=t;if(!n)throw new Fm({docsPath:`/docs/actions/wallet/signTransaction`});let a=aa(n);ed({account:a,...t});let o=await T(e,Fd,`getChainId`)({});r!==null&&Lm({currentChainId:o,chain:r});let s=(r?.formatters||e.chain?.formatters)?.transactionRequest?.format||Hu;return a.signTransaction?a.signTransaction({...i,chainId:o},{serializer:e.chain?.serializers?.transaction}):await e.request({method:`eth_signTransaction`,params:[{...s({...i,account:a},`signTransaction`),chainId:O(o),from:a.address}]},{retryCount:0})}oa();async function Vv(e,t){let{account:n=e.account,domain:r,message:i,primaryType:a}=t;if(!n)throw new Fm({docsPath:`/docs/actions/wallet/signTypedData`});let o=aa(n),s={EIP712Domain:Vh({domain:r}),...t.types};if(Bh({domain:r,message:i,primaryType:a,types:s}),o.signTypedData)return o.signTypedData({domain:r,message:i,primaryType:a,types:s});let c=zh({domain:r,message:i,primaryType:a,types:s});return e.request({method:`eth_signTypedData_v4`,params:[o.address,c]},{retryCount:0})}k();async function Hv(e,{id:t}){await e.request({method:`wallet_switchEthereumChain`,params:[{chainId:O(t)}]},{retryCount:0})}async function Uv(e,t){return await e.request({method:`wallet_watchAsset`,params:t},{retryCount:0})}async function Wv(e,t){return Vm.internal(e,Iv,`sendTransactionSync`,t)}function Gv(e){return{addChain:t=>Ev(e,t),deployContract:t=>Dv(e,t),fillTransaction:t=>Id(e,t),getAddresses:()=>Ov(e),getCallsStatus:t=>Xm(e,t),getCapabilities:t=>kv(e,t),getChainId:()=>Fd(e),getPermissions:()=>Av(e),prepareAuthorization:t=>jv(e,t),prepareTransactionRequest:t=>zd(e,t),requestAddresses:()=>Mv(e),requestPermissions:t=>Nv(e,t),sendCalls:t=>Ym(e,t),sendCallsSync:t=>Pv(e,t),sendRawTransaction:t=>Rm(e,t),sendRawTransactionSync:t=>Tv(e,t),sendTransaction:t=>Bm(e,t),sendTransactionSync:t=>Iv(e,t),showCallsStatus:t=>Lv(e,t),signAuthorization:t=>Rv(e,t),signMessage:t=>zv(e,t),signTransaction:t=>Bv(e,t),signTypedData:t=>Vv(e,t),switchChain:t=>Hv(e,t),waitForCallsStatus:t=>Zm(e,t),watchAsset:t=>Uv(e,t),writeContract:t=>Vm(e,t),writeContractSync:t=>Wv(e,t)}}function Kv(e){let{key:t=`wallet`,name:n=`Wallet Client`,transport:r}=e;return rh({...e,key:t,name:n,transport:r,type:`walletClient`}).extend(Gv)}function qv({key:e,methods:t,name:n,request:r,retryCount:i=3,retryDelay:a=150,timeout:o,type:s},c){let l=nh();return{config:{key:e,methods:t,name:n,request:r,retryCount:i,retryDelay:a,timeout:o,type:s},request:Th(r,{methods:t,retryCount:i,retryDelay:a,uid:l}),value:c}}function Jv(e,t={}){let{key:n=`custom`,methods:r,name:i=`Custom Provider`,retryDelay:a}=t;return({retryCount:o})=>qv({key:n,methods:r,name:i,request:e.request.bind(e),retryCount:t.retryCount??o,retryDelay:a,type:`custom`})}Pu(),ss();function Yv(e,t={}){let{key:n=`fallback`,name:r=`Fallback`,rank:i=!1,shouldThrow:a=Xv,retryCount:o,retryDelay:s}=t;return(({chain:t,pollingInterval:c=4e3,timeout:l,...u})=>{let d=e,f=()=>{},p=qv({key:n,name:r,async request({method:e,params:n}){let r,i=async(o=0)=>{let s=d[o]({...u,chain:t,retryCount:0,timeout:l});try{let t=await s.request({method:e,params:n});return f({method:e,params:n,response:t,transport:s,status:`success`}),t}catch(c){if(f({error:c,method:e,params:n,transport:s,status:`error`}),a(c)||o===d.length-1||(r??=d.slice(o+1).some(n=>{let{include:r,exclude:i}=n({chain:t}).config.methods||{};return r?r.includes(e):i?!i.includes(e):!0}),!r))throw c;return i(o+1)}};return i()},retryCount:o,retryDelay:s,type:`fallback`},{onResponse:e=>f=e,transports:d.map(e=>e({chain:t,retryCount:0}))});if(i){let e=typeof i==`object`?i:{};Zv({chain:t,interval:e.interval??c,onTransports:e=>d=e,ping:e.ping,sampleCount:e.sampleCount,timeout:e.timeout,transports:d,weights:e.weights})}return p})}function Xv(e){return!!(`code`in e&&typeof e.code==`number`&&(e.code===Ho.code||e.code===Ko.code||e.code===as.code||Su.nodeMessage.test(e.message)||e.code===5e3))}function Zv({chain:e,interval:t=4e3,onTransports:n,ping:r,sampleCount:i=10,timeout:a=1e3,transports:o,weights:s={}}){let{stability:c=.7,latency:l=.3}=s,u=[],d=async()=>{let s=await Promise.all(o.map(async t=>{let n=t({chain:e,retryCount:0,timeout:a}),i=Date.now(),o,s;try{await(r?r({transport:n}):n.request({method:`net_listening`})),s=1}catch{s=0}finally{o=Date.now()}return{latency:o-i,success:s}}));u.push(s),u.length>i&&u.shift();let f=Math.max(...u.map(e=>Math.max(...e.map(({latency:e})=>e))));n(o.map((e,t)=>{let n=u.map(e=>e[t].latency),r=1-n.reduce((e,t)=>e+t,0)/n.length/f,i=u.map(e=>e[t].success),a=i.reduce((e,t)=>e+t,0)/i.length;return a===0?[0,t]:[l*r+c*a,t]}).sort((e,t)=>t[0]-e[0]).map(([,e])=>o[e])),await Dm(t),d()};d()}D();var Qv=class extends E{constructor(){super(`No URL was provided to the Transport. Please provide a valid RPC URL to the Transport.`,{docsPath:`/docs/clients/intro`,name:`UrlRequiredError`})}};Ao(),qp();function $v(e,t={}){let{batch:n,fetchFn:r,fetchOptions:i,key:a=`http`,methods:o,name:s=`HTTP JSON-RPC`,onFetchRequest:c,onFetchResponse:l,retryDelay:u,raw:d}=t;return({chain:f,retryCount:p,timeout:m})=>{let{batchSize:h=1e3,wait:g=0}=typeof n==`object`?n:{},_=t.retryCount??p,v=m??t.timeout??1e4,y=e||f?.rpcUrls.default.http[0];if(!y)throw new Qv;let b=jh(y,{fetchFn:r,fetchOptions:i,onRequest:c,onResponse:l,timeout:v});return qv({key:a,methods:o,name:s,async request({method:e,params:t}){let r={method:e,params:t},{schedule:i}=Gp({id:y,wait:g,shouldSplitBatch(e){return e.length>h},fn:e=>b.request({body:e}),sort:(e,t)=>e.id-t.id}),[{error:a,result:o}]=await(async e=>n?i(e):[await b.request({body:e})])(r);if(d)return{error:a,result:o};if(a)throw new Oo({body:r,error:a,url:y});return o},retryCount:_,retryDelay:u,timeout:v,type:`http`},{fetchOptions:i,url:y})}}var ey=L({id:16600,name:`0G Newton Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-newton.0g.ai`}},testnet:!0}),ty=L({id:16601,name:`0G Galileo Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-galileo.0g.ai`}},testnet:!0}),ny=L({id:16661,name:`0G Mainnet`,nativeCurrency:{name:`0G`,symbol:`0G`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan.0g.ai`}},testnet:!1}),ry=L({id:16602,name:`0G Galileo Testnet`,nativeCurrency:{name:`A0GI`,symbol:`A0GI`,decimals:18},rpcUrls:{default:{http:[`https://evmrpc-testnet.0g.ai`]}},blockExplorers:{default:{name:`0G BlockChain Explorer`,url:`https://chainscan-galileo.0g.ai`}},testnet:!0}),iy=L({id:995,name:`5ireChain`,nativeCurrency:{name:`5ire Token`,symbol:`5IRE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.5ire.network`]}},blockExplorers:{default:{name:`5ireChain Mainnet Explorer`,url:`https://5irescan.io/`}},testnet:!1}),ay=L({id:179,name:`ABEY Mainnet`,nativeCurrency:{name:`ABEY`,symbol:`ABEY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.abeychain.com`]}},blockExplorers:{default:{name:`Abey Scan`,url:`https://abeyscan.com`}},testnet:!1});$u();var oy=50000n,sy=Zu*32n;On(),Un(),k(),Ku();var cy={block:dd({format(e){let t=e.transactions?.map(e=>{if(typeof e==`string`)return e;let t=cy.transaction?.format(e);return t.typeHex===`0x71`?t.type=`eip712`:t.typeHex===`0xff`&&(t.type=`priority`),t});return{l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,l1BatchTimestamp:e.l1BatchTimestamp?Tn(e.l1BatchTimestamp):null,transactions:t}}}),transaction:cd({format(e){let t={};return e.type===`0x71`?t.type=`eip712`:e.type===`0xff`&&(t.type=`priority`),{...t,l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,l1BatchTxIndex:e.l1BatchTxIndex?Tn(e.l1BatchTxIndex):null}}}),transactionReceipt:Km({format(e){return{l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,l1BatchTxIndex:e.l1BatchTxIndex?Tn(e.l1BatchTxIndex):null,logs:e.logs.map(e=>({...Ud(e),l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,transactionLogIndex:Dn(e.transactionLogIndex),logType:e.logType})),l2ToL1Logs:e.l2ToL1Logs.map(e=>({blockNumber:Tn(e.blockHash),blockHash:e.blockHash,l1BatchNumber:e.l1BatchNumber?Tn(e.l1BatchNumber):null,transactionIndex:Tn(e.transactionIndex),shardId:Tn(e.shardId),isService:e.isService,sender:e.sender,key:e.key,value:e.value,transactionHash:e.transactionHash,logIndex:Tn(e.logIndex)}))}}}),transactionRequest:Gu({exclude:[`customSignature`,`factoryDeps`,`gasPerPubdata`,`paymaster`,`paymasterInput`],format(e){return e.gasPerPubdata||e.paymaster&&e.paymasterInput||e.factoryDeps||e.customSignature?{eip712Meta:{...e.gasPerPubdata?{gasPerPubdata:kn(e.gasPerPubdata)}:{gasPerPubdata:kn(oy)},...e.paymaster&&e.paymasterInput?{paymasterParams:{paymaster:e.paymaster,paymasterInput:Array.from(Rn(e.paymasterInput))}}:{},...e.factoryDeps?{factoryDeps:e.factoryDeps.map(e=>Array.from(Rn(e)))}:{},...e.customSignature?{customSignature:Array.from(Rn(e.customSignature))}:{}},type:`0x71`}:{}}})};D();var ly=class extends E{constructor(){super([`Transaction is not an EIP712 transaction.`,``,`Transaction must:`,' - include `type: "eip712"`'," - include one of the following: `customSignature`, `paymaster`, `paymasterInput`, `gasPerPubdata`, `factoryDeps`"].join(` -`),{name:`InvalidEip712TransactionError`})}};function uy(e){return!!(e.type===`eip712`||`customSignature`in e&&e.customSignature||`paymaster`in e&&e.paymaster||`paymasterInput`in e&&e.paymasterInput||`gasPerPubdata`in e&&typeof e.gasPerPubdata==`bigint`||`factoryDeps`in e&&e.factoryDeps)}gi(),D(),Fp(),Ei();function dy(e){let{chainId:t,to:n,from:r,paymaster:i,paymasterInput:a}=e;if(!uy(e))throw new ly;if(!t||t<=0)throw new Pp({chainId:t});if(n&&!Ci(n))throw new hi({address:n});if(r&&!Ci(r))throw new hi({address:r});if(i&&!Ci(i))throw new hi({address:i});if(i&&!a)throw new E("`paymasterInput` must be provided when `paymaster` is defined");if(!i&&a)throw new E("`paymaster` must be provided when `paymasterInput` is defined")}Ai(),k();function fy(e,t){return uy(e)?my(e):mh(e,t)}var py={transaction:fy};function my(e){let{chainId:t,gas:n,nonce:r,to:i,from:a,value:o,maxFeePerGas:s,maxPriorityFeePerGas:c,customSignature:l,factoryDeps:u,paymaster:d,paymasterInput:f,gasPerPubdata:p,data:m}=e;return dy(e),ki([`0x71`,mu([r?kn(r):`0x`,c?kn(c):`0x`,s?kn(s):`0x`,n?kn(n):`0x`,i??`0x`,o?kn(o):`0x`,m??`0x`,kn(t),kn(``),kn(``),kn(t),a??`0x`,kn(p||oy),u??[],l??`0x`,d&&f?[d,f]:[]])])}D();var hy=class extends E{constructor({givenLength:e,maxBytecodeSize:t}){super(`Bytecode cannot be longer than ${t} bytes. Given length: ${e}`,{name:`BytecodeLengthExceedsMaxSizeError`})}},gy=class extends E{constructor({givenLengthInWords:e}){super(`Bytecode length in 32-byte words must be odd. Given length in words: ${e}`,{name:`BytecodeLengthInWordsMustBeOddError`})}},_y=class extends E{constructor({givenLength:e}){super(`The bytecode length in bytes must be divisible by 32. Given length: ${e}`,{name:`BytecodeLengthMustBeDivisibleBy32Error`})}};gn(),Un();function vy(e){let t=Fn(e);if(t.length%32!=0)throw new _y({givenLength:t.length});if(t.length>sy)throw new hy({givenLength:t.length,maxBytecodeSize:sy});let n=Fn(bd(t)),r=t.length/32;if(r%2==0)throw new gy({givenLengthInWords:r});let i=pn(Fn(r),{size:2}),a=new Uint8Array([1,0]);return n.set(a,0),n.set(i,2),n}k();var yy=e=>{dy(e);let t=by(e);return{domain:{name:`zkSync`,version:`2`,chainId:e.chainId},types:{Transaction:[{name:`txType`,type:`uint256`},{name:`from`,type:`uint256`},{name:`to`,type:`uint256`},{name:`gasLimit`,type:`uint256`},{name:`gasPerPubdataByteLimit`,type:`uint256`},{name:`maxFeePerGas`,type:`uint256`},{name:`maxPriorityFeePerGas`,type:`uint256`},{name:`paymaster`,type:`uint256`},{name:`nonce`,type:`uint256`},{name:`value`,type:`uint256`},{name:`data`,type:`bytes`},{name:`factoryDeps`,type:`bytes32[]`},{name:`paymasterInput`,type:`bytes`}]},primaryType:`Transaction`,message:t}};function by(e){let{gas:t,nonce:n,to:r,from:i,value:a,maxFeePerGas:o,maxPriorityFeePerGas:s,factoryDeps:c,paymaster:l,paymasterInput:u,gasPerPubdata:d,data:f}=e;return{txType:113n,from:BigInt(i),to:r?BigInt(r):0n,gasLimit:t??0n,gasPerPubdataByteLimit:d??50000n,maxFeePerGas:o??0n,maxPriorityFeePerGas:s??0n,paymaster:l?BigInt(l):0n,nonce:n?BigInt(n):0n,value:a??0n,data:f??`0x`,factoryDeps:c?.map(e=>kn(vy(e)))??[],paymasterInput:u||`0x`}}var xy={blockTime:1e3,formatters:cy,serializers:py,custom:{getEip712Domain:yy}},Sy=L({...xy,blockTime:200,id:2741,name:`Abstract`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.mainnet.abs.xyz`],webSocket:[`wss://api.mainnet.abs.xyz/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://abscan.org`},native:{name:`Abstract Explorer`,url:`https://explorer.mainnet.abs.xyz`}},contracts:{multicall3:{address:`0xAa4De41dba0Ca5dCBb288b7cC6b708F3aaC759E7`,blockCreated:5288},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:5263}}}),Cy=L({...xy,blockTime:200,id:11124,name:`Abstract Testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.testnet.abs.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.abscan.org`},native:{name:`Abstract Explorer`,url:`https://explorer.testnet.abs.xyz`}},testnet:!0,contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:358349},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:431682}}}),wy=L({id:787,name:`Acala`,network:`acala`,nativeCurrency:{name:`Acala`,symbol:`ACA`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-acala.aca-api.network`],webSocket:[`wss://eth-rpc-acala.aca-api.network`]}},blockExplorers:{default:{name:`Acala Blockscout`,url:`https://blockscout.acala.network`,apiUrl:`https://blockscout.acala.network/api`}},testnet:!1}),Ty=L({id:47,name:`Acria IntelliChain`,nativeCurrency:{decimals:18,name:`ACRIA`,symbol:`ACRIA`},rpcUrls:{default:{http:[`https://aic.acria.ai`]}},blockExplorers:{default:{name:`Acria Explorer`,url:`https://explorer.acria.ai`}},testnet:!1}),Ey=L({id:1215,name:`ADF Chain`,nativeCurrency:{name:`ADDFILL`,symbol:`ADF`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.adftechnology.com`]}},blockExplorers:{default:{name:`ADF Mainnet Explorer`,url:`https://explorer.adftechnology.com`}},testnet:!1}),Dy=L({id:36900,name:`ADI_Chain`,nativeCurrency:{decimals:18,name:`ADI`,symbol:`ADI`},rpcUrls:{default:{http:[`https://rpc.adifoundation.ai`]}},blockExplorers:{default:{name:`ADI Explorer`,url:`https://explorer.adifoundation.ai`}},testnet:!1}),Oy=L({id:9990,name:`Agung Network`,nativeCurrency:{decimals:18,name:`Agung`,symbol:`AGNG`},rpcUrls:{default:{http:[`https://wss-async.agung.peaq.network`],webSocket:[`wss://wss-async.agung.peaq.network`]}},blockExplorers:{default:{name:`Subscan`,url:`https://agung-testnet.subscan.io`}},testnet:!0}),ky=L({id:168,name:`AIOZ Network`,nativeCurrency:{decimals:18,name:`AIOZ`,symbol:`AIOZ`},rpcUrls:{default:{http:[`https://eth-dataseed.aioz.network`]}},blockExplorers:{default:{name:`AIOZ Explorer`,url:`https://explorer.aioz.network`}},testnet:!1}),Ay=L({id:41455,name:`Aleph Zero`,nativeCurrency:{name:`Aleph Zero`,symbol:`AZERO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alephzero.raas.gelato.cloud`]}},blockExplorers:{default:{name:`Aleph Zero EVM Explorer`,url:`https://evm-explorer.alephzero.org`,apiUrl:`https://evm-explorer.alephzero.org/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4603377}}}),jy=L({id:2039,name:`Aleph Zero Testnet`,nativeCurrency:{name:`TZERO`,symbol:`TZERO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alephzero-testnet.gelato.digital`],webSocket:[`wss://ws.alephzero-testnet.gelato.digital`]}},blockExplorers:{default:{name:`Aleph Zero EVM Testnet explorer`,url:`https://evm-explorer-testnet.alephzero.org`,apiUrl:`https://evm-explorer-testnet.alephzero.org/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2861745}},testnet:!0}),My=L({id:10241024,name:`AlienX Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.alienxchain.io/http`]}},blockExplorers:{default:{name:`AlienX Explorer`,url:`https://explorer.alienxchain.io`}},testnet:!1}),Ny=L({id:10241025,name:`ALIENX Hal Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://hal-rpc.alienxchain.io/http`]}},blockExplorers:{default:{name:`AlienX Explorer`,url:`https://hal-explorer.alienxchain.io`}},testnet:!0}),Py=L({id:8150,name:`Alpen Testnet`,nativeCurrency:{name:`Signet BTC`,symbol:`sBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.alpenlabs.io`]}},blockExplorers:{default:{name:`Alpen Explorer`,url:`https://explorer.testnet.alpenlabs.io`,apiUrl:`https://explorer.testnet.alpenlabs.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:290408}},testnet:!0}),Fy={gasPriceOracle:{address:`0x420000000000000000000000000000000000000F`},l1Block:{address:`0x4200000000000000000000000000000000000015`},l2CrossDomainMessenger:{address:`0x4200000000000000000000000000000000000007`},l2Erc721Bridge:{address:`0x4200000000000000000000000000000000000014`},l2StandardBridge:{address:`0x4200000000000000000000000000000000000010`},l2ToL1MessagePasser:{address:`0x4200000000000000000000000000000000000016`}};On();var Iy={block:dd({format(e){return{transactions:e.transactions?.map(e=>{if(typeof e==`string`)return e;let t=sd(e);return t.typeHex===`0x7e`&&(t.isSystemTx=e.isSystemTx,t.mint=e.mint?Tn(e.mint):void 0,t.sourceHash=e.sourceHash,t.type=`deposit`),t}),stateRoot:e.stateRoot}}}),transaction:cd({format(e){let t={};return e.type===`0x7e`&&(t.isSystemTx=e.isSystemTx,t.mint=e.mint?Tn(e.mint):void 0,t.sourceHash=e.sourceHash,t.type=`deposit`),t}}),transactionReceipt:Km({format(e){return{l1GasPrice:e.l1GasPrice?Tn(e.l1GasPrice):null,l1GasUsed:e.l1GasUsed?Tn(e.l1GasUsed):null,l1Fee:e.l1Fee?Tn(e.l1Fee):null,l1FeeScalar:e.l1FeeScalar?Number(e.l1FeeScalar):null}}})};gi(),Ei(),Ai(),k();function Ly(e,t){return By(e)?zy(e):mh(e,t)}var Ry={transaction:Ly};function zy(e){Vy(e);let{sourceHash:t,data:n,from:r,gas:i,isSystemTx:a,mint:o,to:s,value:c}=e;return ki([`0x7e`,mu([t,r,s??`0x`,o?kn(o):`0x`,c?kn(c):`0x`,i?kn(i):`0x`,a?`0x1`:`0x`,n??`0x`])])}function By(e){return e.type===`deposit`||e.sourceHash!==void 0}function Vy(e){let{from:t,to:n}=e;if(t&&!Ci(t))throw new hi({address:t});if(n&&!Ci(n))throw new hi({address:n})}var R={blockTime:2e3,contracts:Fy,formatters:Iy,serializers:Ry},Hy=1,Uy=L({...R,id:888888888,name:`Ancient8`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.ancient8.gg`]}},blockExplorers:{default:{name:`Ancient8 explorer`,url:`https://scan.ancient8.gg`,apiUrl:`https://scan.ancient8.gg/api`}},contracts:{...R.contracts,l2OutputOracle:{[Hy]:{address:`0xB09DC08428C8b4EFB4ff9C0827386CDF34277996`}},portal:{[Hy]:{address:`0x639F2AECE398Aa76b07e59eF6abe2cFe32bacb68`,blockCreated:19070571}},l1StandardBridge:{[Hy]:{address:`0xd5e3eDf5b68135D559D572E26bF863FBC1950033`,blockCreated:19070571}}},sourceId:Hy}),Wy=11155111,Gy=L({...R,id:28122024,name:`Ancient8 Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpcv2-testnet.ancient8.gg`]}},blockExplorers:{default:{name:`Ancient8 Celestia Testnet explorer`,url:`https://scanv2-testnet.ancient8.gg`,apiUrl:`https://scanv2-testnet.ancient8.gg/api`}},contracts:{...R.contracts,l2OutputOracle:{[Wy]:{address:`0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB`}},portal:{[Wy]:{address:`0xfa1d9E26A6aCD7b22115D27572c1221B9803c960`,blockCreated:4972908}},l1StandardBridge:{[Wy]:{address:`0xF6Bc0146d3c74D48306e79Ae134A260E418C9335`,blockCreated:4972908}}},sourceId:Wy}),Ky=L({id:31337,name:`Anvil`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`],webSocket:[`ws://127.0.0.1:8545`]}}}),qy=L({id:33139,name:`ApeChain`,nativeCurrency:{name:`ApeCoin`,symbol:`APE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.apechain.com/http`],webSocket:[`wss://rpc.apechain.com/ws`]}},blockExplorers:{default:{name:`Apescan`,url:`https://apescan.io`,apiUrl:`https://api.apescan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:20889}},sourceId:42161}),Jy=L({id:3993,name:`APEX Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.apexlayer.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp-testnet.apexlayer.xyz`,apiUrl:`https://exp-testnet.apexlayer.xyz/api`}},contracts:{multicall3:{address:`0xf7642be33a6b18D16a995657adb5a68CD0438aE2`,blockCreated:283775}},testnet:!0}),Yy=L({id:62606,name:`Apollo`,nativeCurrency:{decimals:18,name:`Apollo`,symbol:`APOLLO`},rpcUrls:{default:{http:[`https://mainnet-rpc.apolloscan.io`]}},blockExplorers:{default:{name:`Apollo Explorer`,url:`https://apolloscan.io`}}}),Xy=L({id:42161,name:`Arbitrum One`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:250,rpcUrls:{default:{http:[`https://arb1.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://arbiscan.io`,apiUrl:`https://api.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7654707}}}),Zy=L({id:421613,name:`Arbitrum Goerli`,nativeCurrency:{name:`Arbitrum Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli-rollup.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://goerli.arbiscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:88114}},testnet:!0}),Qy=L({id:42170,name:`Arbitrum Nova`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://nova.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://nova.arbiscan.io`,apiUrl:`https://api-nova.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1746963}}}),$y=L({id:421614,name:`Arbitrum Sepolia`,blockTime:250,nativeCurrency:{name:`Arbitrum Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rollup.arbitrum.io/rpc`]}},blockExplorers:{default:{name:`Arbiscan`,url:`https://sepolia.arbiscan.io`,apiUrl:`https://api-sepolia.arbiscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:81930}},testnet:!0}),eb=L({id:5042002,name:`Arc Testnet`,nativeCurrency:{name:`USDC`,symbol:`USDC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.arc.network`,`https://rpc.quicknode.testnet.arc.network`,`https://rpc.blockdaemon.testnet.arc.network`],webSocket:[`wss://rpc.testnet.arc.network`,`wss://rpc.quicknode.testnet.arc.network`]}},blockExplorers:{default:{name:`ArcScan`,url:`https://testnet.arcscan.app`,apiUrl:`https://testnet.arcscan.app/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),tb=L({id:7897,name:`Arena-Z`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.arena-z.gg`]}},blockExplorers:{default:{name:`Arena-Z Explorer`,url:`https://explorer.arena-z.gg`,apiUrl:`https://explorer.arena-z.gg`}}}),nb=L({id:463,name:`Areon Network`,nativeCurrency:{decimals:18,name:`AREA`,symbol:`AREA`},rpcUrls:{default:{http:[`https://mainnet-rpc.areon.network`],webSocket:[`wss://mainnet-ws.areon.network`]}},blockExplorers:{default:{name:`Areonscan`,url:`https://areonscan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:353286}},testnet:!1}),rb=L({id:462,name:`Areon Network Testnet`,nativeCurrency:{decimals:18,name:`TAREA`,symbol:`TAREA`},rpcUrls:{default:{http:[`https://testnet-rpc.areon.network`],webSocket:[`wss://testnet-ws.areon.network`]}},blockExplorers:{default:{name:`Areonscan`,url:`https://areonscan.com`}},testnet:!0}),ib=L({id:463,name:`Areum`,nativeCurrency:{decimals:18,name:`AREA`,symbol:`AREA`},rpcUrls:{default:{http:[`https://mainnet-rpc.areum.network`],webSocket:[`wss://mainnet-ws.areum.network`]}},blockExplorers:{default:{name:`Areum Explorer`,url:`https://explorer.areum.network`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:353286}},testnet:!1}),ab=L({id:11822,name:`Artela Testnet`,nativeCurrency:{name:`ART`,symbol:`ART`,decimals:18},rpcUrls:{default:{http:[`https://betanet-rpc1.artela.network`]}},blockExplorers:{default:{name:`Artela`,url:`https://betanet-scan.artela.network`,apiUrl:`https://betanet-scan.artela.network/api`}},contracts:{multicall3:{address:`0xd07c8635f76e8745Ee7092fbb6e8fbc5FeF09DD7`,blockCreated:7001871}},testnet:!0}),ob=L({id:10242,name:`Arthera`,nativeCurrency:{name:`Arthera`,symbol:`AA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.arthera.net`]}},blockExplorers:{default:{name:`Arthera EVM Explorer`,url:`https://explorer.arthera.net`,apiUrl:`https://explorer.arthera.net/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4502791}}}),sb=L({id:10243,name:`Arthera Testnet`,nativeCurrency:{name:`Arthera`,symbol:`AA`,decimals:18},rpcUrls:{default:{http:[`https://rpc-test.arthera.net`]}},blockExplorers:{default:{name:`Arthera EVM Explorer`,url:`https://explorer-test.arthera.net`,apiUrl:`https://explorer-test.arthera.net/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:22051}}}),cb=L({id:42420,name:`AssetChain Mainnet`,nativeCurrency:{decimals:18,name:`Real World Asset`,symbol:`RWA`},rpcUrls:{default:{http:[`https://mainnet-rpc.assetchain.org`]}},blockExplorers:{default:{name:`Asset Chain Explorer`,url:`https://scan.assetchain.org`,apiUrl:`https://scan.assetchain.org/api`}},testnet:!1,contracts:{}}),lb=L({id:42421,name:`AssetChain Testnet`,nativeCurrency:{decimals:18,name:`Real World Asset`,symbol:`RWA`},rpcUrls:{default:{http:[`https://enugu-rpc.assetchain.org`]}},blockExplorers:{default:{name:`Asset Chain Testnet Explorer`,url:`https://scan-testnet.assetchain.org`,apiUrl:`https://scan-testnet.assetchain.org/api`}},testnet:!0,contracts:{multicall3:{address:`0x989F832D35988cb5e3eB001Fa2Fe789469EC31Ea`,blockCreated:17177}}}),ub=L({id:592,name:`Astar`,network:`astar-mainnet`,nativeCurrency:{name:`Astar`,symbol:`ASTR`,decimals:18},rpcUrls:{default:{http:[`https://astar.api.onfinality.io/public`]}},blockExplorers:{default:{name:`Astar Subscan`,url:`https://astar.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:761794}},testnet:!1}),db=L({id:3776,name:`Astar zkEVM`,network:`AstarZkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-zkevm.astar.network`]}},blockExplorers:{default:{name:`Astar zkEVM Explorer`,url:`https://astar-zkevm.explorer.startale.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:93528}},testnet:!1}),fb=L({id:6038361,name:`Astar zkEVM Testnet zKyoto`,network:`zKyoto`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.startale.com/zkyoto`]}},blockExplorers:{default:{name:`zKyoto Explorer`,url:`https://zkyoto.explorer.startale.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:196153}},testnet:!0}),pb=L({id:2340,name:`Atleta Olympia`,nativeCurrency:{decimals:18,name:`Atla`,symbol:`ATLA`},rpcUrls:{default:{http:[`https://testnet-rpc.atleta.network:9944`,`https://testnet-rpc.atleta.network`],ws:[`wss://testnet-rpc.atleta.network:9944`]}},blockExplorers:{default:{name:`Atleta Olympia Explorer`,url:`https://blockscout.atleta.network`,apiUrl:`https://blockscout.atleta.network/api`}},contracts:{multicall3:{address:`0x1472ec6392180fb84F345d2455bCC75B26577115`,blockCreated:1076473}},testnet:!0}),mb=L({id:1313161554,name:`Aurora`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.aurora.dev`]}},blockExplorers:{default:{name:`Aurorascan`,url:`https://aurorascan.dev`,apiUrl:`https://aurorascan.dev/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:62907816}}}),hb=L({id:1313161555,name:`Aurora Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet.aurora.dev`]}},blockExplorers:{default:{name:`Aurorascan`,url:`https://testnet.aurorascan.dev`,apiUrl:`https://testnet.aurorascan.dev/api`}},testnet:!0}),gb=L({id:205205,name:`Auroria Testnet`,network:`auroria`,nativeCurrency:{name:`Auroria Stratis`,symbol:`tSTRAX`,decimals:18},rpcUrls:{default:{http:[`https://auroria.rpc.stratisevm.com`]}},blockExplorers:{default:{name:`Auroria Testnet Explorer`,url:`https://auroria.explorer.stratisevm.com`}},testnet:!0}),_b=L({id:785,name:`Autheo Testnet`,nativeCurrency:{decimals:18,name:`Autheo`,symbol:`THEO`},rpcUrls:{default:{http:[`https://testnet-rpc1.autheo.com`,`https://testnet-rpc2.autheo.com`]}},blockExplorers:{default:{name:`Autheo Testnet Block Explorer`,url:`https://testnet-explorer.autheo.com/`}}}),vb=L({id:43114,name:`Avalanche`,blockTime:1700,nativeCurrency:{decimals:18,name:`Avalanche`,symbol:`AVAX`},rpcUrls:{default:{http:[`https://api.avax.network/ext/bc/C/rpc`]}},blockExplorers:{default:{name:`SnowTrace`,url:`https://snowtrace.io`,apiUrl:`https://api.snowtrace.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11907934}}}),yb=L({id:43113,name:`Avalanche Fuji`,nativeCurrency:{decimals:18,name:`Avalanche Fuji`,symbol:`AVAX`},rpcUrls:{default:{http:[`https://api.avax-test.network/ext/bc/C/rpc`]}},blockExplorers:{default:{name:`SnowTrace`,url:`https://testnet.snowtrace.io`,apiUrl:`https://api-testnet.snowtrace.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7096959}},testnet:!0}),bb=L({id:8333,name:`B3`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.b3.fun/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.b3.fun`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}},sourceId:8453}),xb=L({id:1993,name:`B3 Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.b3.fun/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia.explorer.b3.fun`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}},testnet:!0,sourceId:168587773}),Sb=L({id:5165,network:`bahamut`,name:`Bahamut`,nativeCurrency:{name:`Fasttoken`,symbol:`FTN`,decimals:18},rpcUrls:{default:{http:[`https://rpc1.bahamut.io`,`https://bahamut-rpc.publicnode.com`,`https://rpc2.bahamut.io`],webSocket:[`wss://ws1.sahara.bahamutchain.com`,`wss://bahamut-rpc.publicnode.com`,`wss://ws2.sahara.bahamutchain.com`]}},blockExplorers:{default:{name:`Ftnscan`,url:`https://www.ftnscan.com`,apiUrl:`https://www.ftnscan.com/api`}}}),Cb=1,wb=L({...R,id:8453,name:`Base`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://basescan.org`,apiUrl:`https://api.basescan.org/api`}},contracts:{...R.contracts,disputeGameFactory:{[Cb]:{address:`0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e`}},l2OutputOracle:{[Cb]:{address:`0x56315b90c40730925ec5485cf004d835058518A0`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:5022},portal:{[Cb]:{address:`0x49048044D57e1C92A77f79988d21Fa8fAF74E97e`,blockCreated:17482143}},l1StandardBridge:{[Cb]:{address:`0x3154Cf16ccdb4C6d922629664174b904d80F2C35`,blockCreated:17482143}}},sourceId:Cb}),Tb=L({...wb,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://mainnet-preconf.base.org`]}}}),Eb=L({id:123420001114,name:`Basecamp Testnet`,nativeCurrency:{decimals:18,name:`Camp`,symbol:`CAMP`},rpcUrls:{default:{http:[`https://rpc.basecamp.t.raas.gelato.cloud`]}},blockExplorers:{default:{name:`basecamp`,url:`https://basecamp.cloud.blockscout.com`}},testnet:!0}),Db=5,Ob=L({...R,id:84531,name:`Base Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://goerli.basescan.org`,apiUrl:`https://goerli.basescan.org/api`}},contracts:{...R.contracts,l2OutputOracle:{[Db]:{address:`0x2A35891ff30313CcFa6CE88dcf3858bb075A2298`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1376988},portal:{[Db]:{address:`0xe93c8cD0D409341205A592f8c4Ac1A5fe5585cfA`}},l1StandardBridge:{[Db]:{address:`0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a`}}},testnet:!0,sourceId:Db}),kb=11155111,Ab=L({...R,id:84532,network:`base-sepolia`,name:`Base Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.base.org`]}},blockExplorers:{default:{name:`Basescan`,url:`https://sepolia.basescan.org`,apiUrl:`https://api-sepolia.basescan.org/api`}},contracts:{...R.contracts,disputeGameFactory:{[kb]:{address:`0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1`}},l2OutputOracle:{[kb]:{address:`0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254`}},portal:{[kb]:{address:`0x49f53e41452c74589e85ca1677426ba426459e85`,blockCreated:4446677}},l1StandardBridge:{[kb]:{address:`0xfd0Bf71F60660E2f608ed56e1659C450eB113120`,blockCreated:4446677}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1059647}},testnet:!0,sourceId:kb}),jb=L({...Ab,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://sepolia-preconf.base.org`]}}}),Mb=L({id:4337,name:`Beam`,network:`beam`,nativeCurrency:{decimals:18,name:`Beam`,symbol:`BEAM`},rpcUrls:{default:{http:[`https://build.onbeam.com/rpc`],webSocket:[`wss://build.onbeam.com/ws`]}},blockExplorers:{default:{name:`Beam Explorer`,url:`https://subnets.avax.network/beam`}},contracts:{multicall3:{address:`0x4956f15efdc3dc16645e90cc356eafa65ffc65ec`,blockCreated:1}}}),Nb=L({id:13337,name:`Beam Testnet`,network:`beam`,nativeCurrency:{decimals:18,name:`Beam`,symbol:`BEAM`},rpcUrls:{default:{http:[`https://build.onbeam.com/rpc/testnet`],webSocket:[`wss://build.onbeam.com/ws/testnet`]}},blockExplorers:{default:{name:`Beam Explorer`,url:`https://subnets-test.avax.network/beam`}},contracts:{multicall3:{address:`0x9bf49b704ee2a095b95c1f2d4eb9010510c41c9e`,blockCreated:3}},testnet:!0}),Pb=L({id:641230,name:`Bear Network Chain Mainnet`,nativeCurrency:{decimals:18,name:`BearNetworkChain`,symbol:`BRNKC`},rpcUrls:{default:{http:[`https://brnkc-mainnet.bearnetwork.net`]}},blockExplorers:{default:{name:`BrnkScan`,url:`https://brnkscan.bearnetwork.net`,apiUrl:`https://brnkscan.bearnetwork.net/api`}}}),Fb=L({id:751230,name:`Bear Network Chain Testnet`,nativeCurrency:{decimals:18,name:`tBRNKC`,symbol:`tBRNKC`},rpcUrls:{default:{http:[`https://brnkc-test.bearnetwork.net`]}},blockExplorers:{default:{name:`BrnkTestScan`,url:`https://brnktest-scan.bearnetwork.net`,apiUrl:`https://brnktest-scan.bearnetwork.net/api`}},testnet:!0}),Ib=L({id:80094,name:`Berachain`,blockTime:2e3,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},ensRegistry:{address:`0x5b22280886a2f5e09a49bea7e320eab0e5320e28`,blockCreated:877007},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:9310021}},rpcUrls:{default:{http:[`https://rpc.berachain.com`]}},blockExplorers:{default:{name:`Berascan`,url:`https://berascan.com`}},ensTlds:[`.bera`],testnet:!1}),Lb=L({id:80069,blockTime:2e3,name:`Berachain Bepolia`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},rpcUrls:{default:{http:[`https://bepolia.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berascan`,url:`https://bepolia.beratrail.io`}},testnet:!0}),Rb=L({id:80085,name:`Berachain Artio`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},rpcUrls:{default:{http:[`https://artio.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berachain`,url:`https://artio.beratrail.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:866924}},testnet:!0}),zb=L({id:80084,name:`Berachain bArtio`,nativeCurrency:{decimals:18,name:`BERA Token`,symbol:`BERA`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:109269},ensRegistry:{address:`0xB0eef18971290b333450586D33dcA6cE122651D2`,blockCreated:7736794},ensUniversalResolver:{address:`0x41692Ef1EA0C79E6b73077E4A67572D2BDbD7057`,blockCreated:7736795}},ensTlds:[`.bera`],rpcUrls:{default:{http:[`https://bartio.rpc.berachain.com`]}},blockExplorers:{default:{name:`Berachain bArtio Beratrail`,url:`https://bartio.beratrail.io`}},testnet:!0}),Bb=L({id:11501,name:`BEVM Mainnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet-1.bevm.io`]}},blockExplorers:{default:{name:`Bevmscan`,url:`https://scan-mainnet.bevm.io`,apiUrl:`https://scan-mainnet-api.bevm.io/api`}}}),Vb=L({id:3068,name:`Bifrost Mainnet`,nativeCurrency:{name:`BFC`,symbol:`BFC`,decimals:18},rpcUrls:{default:{http:[`https://public-01.mainnet.bifrostnetwork.com/rpc`]}},blockExplorers:{default:{name:`Bifrost Blockscout`,url:`https://explorer.mainnet.bifrostnetwork.com`}},testnet:!1}),Hb=L({id:53456,name:`BirdLayer`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.birdlayer.xyz`,`https://rpc1.birdlayer.xyz`],webSocket:[`wss://rpc.birdlayer.xyz/ws`]}},blockExplorers:{default:{name:`BirdLayer Explorer`,url:`https://scan.birdlayer.xyz`}}}),Ub=L({id:32520,name:`Bitgert Mainnet`,nativeCurrency:{decimals:18,name:`Brise`,symbol:`Brise`},rpcUrls:{default:{http:[`https://rpc-bitgert.icecreamswap.com`]}},blockExplorers:{default:{name:`Bitgert Scan`,url:`https://brisescan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2118034}},testnet:!1}),Wb=L({id:96,name:`KUB Mainnet`,nativeCurrency:{name:`KUB Coin`,symbol:`KUB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitkubchain.io`]}},blockExplorers:{default:{name:`KUB Chain Mainnet Explorer`,url:`https://www.bkcscan.com`,apiUrl:`https://www.bkcscan.com/api`}}}),Gb=L({id:25925,name:`Bitkub Testnet`,network:`Bitkub Testnet`,nativeCurrency:{name:`Bitkub Test`,symbol:`tKUB`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.bitkubchain.io`]}},blockExplorers:{default:{name:`Bitkub Chain Testnet Explorer`,url:`https://testnet.bkcscan.com`,apiUrl:`https://testnet.bkcscan.com/api`}},testnet:!0}),Kb=L({id:200901,name:`Bitlayer Mainnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitlayer.org`],webSocket:[`wss://ws.bitlayer.org`]}},blockExplorers:{default:{name:`bitlayer mainnet scan`,url:`https://www.btrscan.com`}},contracts:{multicall3:{address:`0x5B256fE9e993902eCe49D138a5b1162cBb529474`,blockCreated:2421963}}}),qb=L({id:200810,name:`Bitlayer Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bitlayer.org`],webSocket:[`wss://testnet-ws.bitlayer.org`]}},blockExplorers:{default:{name:`bitlayer testnet scan`,url:`https://testnet.btrscan.com`}},contracts:{multicall3:{address:`0x5B256fE9e993902eCe49D138a5b1162cBb529474`,blockCreated:4135671}},testnet:!0}),Jb=L({id:7171,name:`Bitrock Mainnet`,nativeCurrency:{name:`BROCK`,symbol:`BROCK`,decimals:18},rpcUrls:{default:{http:[`https://brockrpc.io`]}},blockExplorers:{default:{name:`Bitrock Explorer`,url:`https://explorer.bit-rock.io`}},testnet:!1}),Yb=L({id:199,name:`BitTorrent`,network:`bittorrent-chain-mainnet`,nativeCurrency:{name:`BitTorrent`,symbol:`BTT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bittorrentchain.io`]}},blockExplorers:{default:{name:`Bttcscan`,url:`https://bttcscan.com`,apiUrl:`https://api.bttcscan.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:31078552}}}),Xb=L({id:1028,name:`BitTorrent Chain Testnet`,network:`bittorrent-chain-testnet`,nativeCurrency:{name:`BitTorrent`,symbol:`BTT`,decimals:18},rpcUrls:{default:{http:[`https://testrpc.bittorrentchain.io`]}},blockExplorers:{default:{name:`Bttcscan`,url:`https://testnet.bttcscan.com`,apiUrl:`https://testnet.bttcscan.com/api`}},testnet:!0}),Zb=1,Qb=L({...R,id:81457,name:`Blast`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.blast.io`]}},blockExplorers:{default:{name:`Blastscan`,url:`https://blastscan.io`,apiUrl:`https://api.blastscan.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:212929},l2OutputOracle:{[Zb]:{address:`0x826D1B0D4111Ad9146Eb8941D7Ca2B6a44215c76`,blockCreated:19300358}},portal:{[Zb]:{address:`0x0Ec68c5B10F21EFFb74f2A5C61DFe6b08C0Db6Cb`,blockCreated:19300357}},l1StandardBridge:{[Zb]:{address:`0x697402166Fbf2F22E970df8a6486Ef171dbfc524`,blockCreated:19300360}}},sourceId:Zb}),$b=L({id:168587773,name:`Blast Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.blast.io`]}},blockExplorers:{default:{name:`Blastscan`,url:`https://sepolia.blastscan.io`,apiUrl:`https://api-sepolia.blastscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:756690}},testnet:!0,sourceId:11155111}),ex=1,tx=L({...R,id:60808,name:`BOB`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.gobob.xyz`],webSocket:[`wss://rpc.gobob.xyz`]}},blockExplorers:{default:{name:`BOB Explorer`,url:`https://explorer.gobob.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:23131},l2OutputOracle:{[ex]:{address:`0xdDa53E23f8a32640b04D7256e651C1db98dB11C1`,blockCreated:4462615}},portal:{[ex]:{address:`0x8AdeE124447435fE03e3CD24dF3f4cAE32E65a3E`,blockCreated:4462615}}},sourceId:ex}),nx=L({id:288,name:`Boba Network`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.boba.network`]}},blockExplorers:{default:{name:`BOBAScan`,url:`https://bobascan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:446859}}}),rx=L({id:28882,name:`Boba Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.boba.network`]}},blockExplorers:{default:{name:`BOBAScan`,url:`https://testnet.bobascan.com`}},testnet:!0}),ix=11155111,ax=L({...R,id:808813,name:`BOB Sepolia`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://bob-sepolia.rpc.gobob.xyz`],webSocket:[`wss://bob-sepolia.rpc.gobob.xyz`]}},blockExplorers:{default:{name:`BOB Sepolia Explorer`,url:`https://bob-sepolia.explorer.gobob.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:35677},l2OutputOracle:{[ix]:{address:`0x14D0069452b4AE2b250B395b8adAb771E4267d2f`,blockCreated:4462615}},portal:{[ix]:{address:`0x867B1Aa872b9C8cB5E9F7755feDC45BB24Ad0ae4`,blockCreated:4462615}}},testnet:!0,sourceId:ix}),ox=L({id:11100,name:`Bool Beta Mainnet`,nativeCurrency:{decimals:18,name:`BOL`,symbol:`BOL`},rpcUrls:{default:{http:[`https://beta-rpc-node-http.bool.network`]}},blockExplorers:{default:{name:`BoolScan`,url:`https://beta-mainnet.boolscan.com/`}},testnet:!1}),sx=L({id:3637,name:`Botanix`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.botanixlabs.com`],webSocket:[`wss://rpc.botanixlabs.com/ws`]}},blockExplorers:{default:{name:`Botanixscan`,url:`https://botanixscan.io`}}}),cx=L({id:3636,name:`Botanix Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://node.botanixlabs.dev`]}},blockExplorers:{default:{name:`Botanix Testnet Explorer`,url:`https://testnet.botanixscan.io`}},testnet:!0}),lx=L({id:6001,name:`BounceBit Mainnet`,nativeCurrency:{name:`BounceBit`,symbol:`BB`,decimals:18},rpcUrls:{default:{http:[`https://fullnode-mainnet.bouncebitapi.com`]}},blockExplorers:{default:{name:`BB Scan`,url:`https://bbscan.io`}},testnet:!1}),ux=L({id:6e3,name:`BounceBit Testnet`,nativeCurrency:{name:`BounceBit`,symbol:`BB`,decimals:18},rpcUrls:{default:{http:[`https://fullnode-testnet.bouncebitapi.com`]}},blockExplorers:{default:{name:`BB Scan`,url:`https://testnet.bbscan.io`}},testnet:!0}),dx=L({id:1039,name:`Bronos`,nativeCurrency:{decimals:18,name:`BRO`,symbol:`BRO`},rpcUrls:{default:{http:[`https://evm.bronos.org`]}},blockExplorers:{default:{name:`BronoScan`,url:`https://broscan.bronos.org`}}}),fx=L({id:1038,name:`Bronos Testnet`,nativeCurrency:{decimals:18,name:`Bronos Coin`,symbol:`tBRO`},rpcUrls:{default:{http:[`https://evm-testnet.bronos.org`]}},blockExplorers:{default:{name:`BronoScan`,url:`https://tbroscan.bronos.org`}},testnet:!0}),px=L({id:56,name:`BNB Smart Chain`,blockTime:750,nativeCurrency:{decimals:18,name:`BNB`,symbol:`BNB`},rpcUrls:{default:{http:[`https://56.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`BscScan`,url:`https://bscscan.com`,apiUrl:`https://api.bscscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15921452}}}),mx=L({id:1017,name:`BNB Greenfield Chain`,nativeCurrency:{decimals:18,name:`BNB`,symbol:`BNB`},rpcUrls:{default:{http:[`https://greenfield-chain.bnbchain.org`]}},blockExplorers:{default:{name:`BNB Greenfield Mainnet Scan`,url:`https://greenfieldscan.com`}},testnet:!1}),hx=L({id:97,name:`BNB Smart Chain Testnet`,nativeCurrency:{decimals:18,name:`BNB`,symbol:`tBNB`},rpcUrls:{default:{http:[`https://data-seed-prebsc-1-s1.bnbchain.org:8545`]}},blockExplorers:{default:{name:`BscScan`,url:`https://testnet.bscscan.com`,apiUrl:`https://api-testnet.bscscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:17422483}},testnet:!0}),gx=L({id:223,name:`B2`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bsquared.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.bsquared.network`}}}),_x=L({id:1123,name:`B2 Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bsquared.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet-explorer.bsquared.network`}},testnet:!0}),vx=L({id:200901,name:`Bitlayer`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.bitlayer.org`,`https://rpc.bitlayer-rpc.com`],webSocket:[`wss://ws.bitlayer.org`,`wss://ws.bitlayer-rpc.com`]}},blockExplorers:{default:{name:`Bitlayer(BTR) Scan`,url:`https://www.btrscan.com`}}}),yx=L({id:200810,name:`Bitlayer Testnet`,nativeCurrency:{name:`Bitcoin`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.bitlayer.org`],webSocket:[`wss://testnet-ws.bitlayer.org`,`wss://testnet-ws.bitlayer-rpc.com`]}},blockExplorers:{default:{name:`Bitlayer(BTR) Scan`,url:`https://testnet.btrscan.com`}},testnet:!0}),bx=L({id:4999,name:`BlackFort Exchange Network`,nativeCurrency:{name:`BlackFort Token`,symbol:`BXN`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.blackfort.network/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.blackfort.network`,apiUrl:`https://explorer.blackfort.network/api`}}}),xx=L({id:4777,name:`BlackFort Exchange Network Testnet`,nativeCurrency:{name:`BlackFort Testnet Token`,symbol:`TBXN`,decimals:18},rpcUrls:{default:{http:[`https://testnet.blackfort.network/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.blackfort.network`,apiUrl:`https://testnet-explorer.blackfort.network/api`}},testnet:!0}),Sx=L({id:13370,name:`Cannon`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),Cx=L({id:7700,name:`Canto`,nativeCurrency:{decimals:18,name:`Canto`,symbol:`CANTO`},rpcUrls:{default:{http:[`https://canto.gravitychain.io`]}},blockExplorers:{default:{name:`Tuber.Build (Blockscout)`,url:`https://tuber.build`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2905789}}}),wx={estimateFeesPerGas:async e=>{if(!e.request?.feeCurrency)return null;let[t,n]=await Promise.all([Tx(e.client,e.request.feeCurrency),Ex(e.client,e.request.feeCurrency)]);return{maxFeePerGas:e.multiply(t-n)+n,maxPriorityFeePerGas:n}}};async function Tx(e,t){let n=await e.request({method:`eth_gasPrice`,params:[t]});return BigInt(n)}async function Ex(e,t){let n=await e.request({method:`eth_maxPriorityFeePerGas`,params:[t]});return BigInt(n)}Cn();function Dx(e){return e===0||e===0n||e==null||e===`0`||e===``||typeof e==`string`&&(Sn(e).toLowerCase()===`0x`||Sn(e).toLowerCase()===`0x00`)}function Ox(e){return!Dx(e)}function kx(e){return e.maxFeePerGas!==void 0&&e.maxPriorityFeePerGas!==void 0}function Ax(e){return e.type===`cip64`?!0:kx(e)&&Ox(e.feeCurrency)}On(),Ku();var jx={block:dd({format(e){return{transactions:e.transactions?.map(e=>typeof e==`string`?e:{...sd(e),...e.gatewayFee?{gatewayFee:Tn(e.gatewayFee),gatewayFeeRecipient:e.gatewayFeeRecipient}:{},feeCurrency:e.feeCurrency})}}}),transaction:cd({format(e){if(e.type===`0x7e`)return{isSystemTx:e.isSystemTx,mint:e.mint?Tn(e.mint):void 0,sourceHash:e.sourceHash,type:`deposit`};let t={feeCurrency:e.feeCurrency};return e.type===`0x7b`?t.type=`cip64`:(e.type===`0x7c`&&(t.type=`cip42`),t.gatewayFee=e.gatewayFee?Tn(e.gatewayFee):null,t.gatewayFeeRecipient=e.gatewayFeeRecipient),t}}),transactionRequest:Gu({format(e){let t={};return e.feeCurrency&&(t.feeCurrency=e.feeCurrency),Ax(e)&&(t.type=`0x7b`),t}})};$u(),gi(),D(),Fp(),Pu(),Ei(),Ai(),k();function Mx(e,t){return Ax(e)?Px(e,t):Ly(e,t)}var Nx={transaction:Mx};function Px(e,t){Ix(e);let{chainId:n,gas:r,nonce:i,to:a,value:o,maxFeePerGas:s,maxPriorityFeePerGas:c,accessList:l,feeCurrency:u,data:d}=e;return ki([`0x7b`,mu([kn(n),i?kn(i):`0x`,c?kn(c):`0x`,s?kn(s):`0x`,r?kn(r):`0x`,a??`0x`,o?kn(o):`0x`,d??`0x`,ph(l),u,...bh(e,t)])])}var Fx=Qu;function Ix(e){let{chainId:t,maxPriorityFeePerGas:n,gasPrice:r,maxFeePerGas:i,to:a,feeCurrency:o}=e;if(t<=0)throw new Pp({chainId:t});if(a&&!Ci(a))throw new hi({address:a});if(r)throw new E("`gasPrice` is not a valid CIP-64 Transaction attribute.");if(Ox(i)&&i>Fx)throw new Cu({maxFeePerGas:i});if(Ox(n)&&Ox(i)&&n>i)throw new Mu({maxFeePerGas:i,maxPriorityFeePerGas:n});if(Ox(o)&&!Ci(o))throw new E("`feeCurrency` MUST be a token address for CIP-64 transactions.");if(Dx(o))throw new E("`feeCurrency` must be provided for CIP-64 transactions.")}var Lx={blockTime:1e3,contracts:Fy,formatters:jx,serializers:Nx,fees:wx},Rx=L({...Lx,id:42220,name:`Celo`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`CELO`},rpcUrls:{default:{http:[`https://forno.celo.org`]}},blockExplorers:{default:{name:`Celo Explorer`,url:`https://celoscan.io`,apiUrl:`https://api.celoscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:13112599}},testnet:!1}),zx=17e3,Bx=L({...Lx,id:44787,name:`Alfajores`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`A-CELO`},rpcUrls:{default:{http:[`https://alfajores-forno.celo-testnet.org`]}},blockExplorers:{default:{name:`Celo Alfajores Explorer`,url:`https://celo-alfajores.blockscout.com`,apiUrl:`https://celo-alfajores.blockscout.com/api`}},contracts:{...Lx.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:14569001},portal:{[zx]:{address:`0x82527353927d8D069b3B452904c942dA149BA381`,blockCreated:2411324}},disputeGameFactory:{[zx]:{address:`0xE28AAdcd9883746c0e5068F58f9ea06027b214cb`,blockCreated:2411324}},l2OutputOracle:{[zx]:{address:`0x4a2635e9e4f6e45817b1D402ac4904c1d1752438`,blockCreated:2411324}},l1StandardBridge:{[zx]:{address:`0xD1B0E0581973c9eB7f886967A606b9441A897037`,blockCreated:2411324}}},testnet:!0}),Vx=11155111,Hx=L({...Lx,id:11142220,name:`Celo Sepolia Testnet`,nativeCurrency:{decimals:18,name:`CELO`,symbol:`S-CELO`},rpcUrls:{default:{http:[`https://forno.celo-sepolia.celo-testnet.org`]}},blockExplorers:{default:{name:`Celo Sepolia Explorer`,url:`https://celo-sepolia.blockscout.com/`,apiUrl:`https://celo-sepolia.blockscout.com/api`}},contracts:{...Lx.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1},portal:{[Vx]:{address:`0x44ae3d41a335a7d05eb533029917aad35662dcc2`,blockCreated:8825790}},disputeGameFactory:{[Vx]:{address:`0x57c45d82d1a995f1e135b8d7edc0a6bb5211cfaa`,blockCreated:8825790}},l1StandardBridge:{[Vx]:{address:`0xec18a3c30131a0db4246e785355fbc16e2eaf408`,blockCreated:8825790}}},testnet:!0}),Ux=L({id:5858,name:`Chang Chain Foundation Mainnet`,nativeCurrency:{decimals:18,name:`CTH`,symbol:`CTH`},rpcUrls:{default:{http:[`https://rpc.cthscan.com`]}},blockExplorers:{default:{name:`Chang Chain explorer`,url:`https://cthscan.com`}}}),Wx=L({id:88888,name:`Chiliz Chain`,network:`chiliz-chain`,nativeCurrency:{decimals:18,name:`CHZ`,symbol:`CHZ`},rpcUrls:{default:{http:[`https://rpc.chiliz.com`]}},blockExplorers:{default:{name:`Chiliz Explorer`,url:`https://scan.chiliz.com`,apiUrl:`https://scan.chiliz.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:8080847}}}),Gx=L({id:2882,name:`Chips Network`,network:`CHIPS`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://node.chips.ooo/wasp/api/v1/chains/iota1pp3d3mnap3ufmgqnjsnw344sqmf5svjh26y2khnmc89sv6788y3r207a8fn/evm`]}}}),Kx=L({id:4114,name:`Citrea Mainnet`,nativeCurrency:{name:`Citrea Bitcoin`,symbol:`cBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.citrea.xyz`]}},blockExplorers:{default:{name:`Citrea Explorer`,url:`https://explorer.mainnet.citrea.xyz`,apiUrl:`https://explorer.mainnet.citrea.xyz/api`}},testnet:!1}),qx=L({id:5115,name:`Citrea Testnet`,nativeCurrency:{name:`cBTC`,symbol:`cBTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.citrea.xyz`]}},blockExplorers:{default:{name:`Citrea Explorer`,url:`https://explorer.testnet.citrea.xyz`,apiUrl:`https://explorer.testnet.citrea.xyz/api`}},testnet:!0}),Jx=L({id:61,name:`Ethereum Classic`,nativeCurrency:{decimals:18,name:`ETC`,symbol:`ETC`},rpcUrls:{default:{http:[`https://etc.rivet.link`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.com/etc/mainnet`}}}),Yx=1,Xx=L({...R,id:81224,name:`Codex`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.codex.xyz`]}},blockExplorers:{default:{name:`Codex Explorer`,url:`https://explorer.codex.xyz`,apiUrl:`https://explorer.codex.xyz/api`}},contracts:{...R.contracts,disputeGameFactory:{[Yx]:{address:`0x6A3855dc26e2beA8Ac73f82Cda79f3808B6C6F6C`}},portal:{[Yx]:{address:`0x52759C07A759c81BAab28AE1BE5A19e6450959bD`}},l1StandardBridge:{[Yx]:{address:`0xa6b1A05a592719B0C8a70c69eac114C48410aDE4`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},sourceId:Yx}),Zx=11155111,Qx=L({...R,id:812242,name:`Codex Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.codex-stg.xyz`]}},blockExplorers:{default:{name:`Codex Testnet Explorer`,url:`https://explorer.codex-stg.xyz`,apiUrl:`https://explorer.codex-stg.xyz/api`}},contracts:{...R.contracts,disputeGameFactory:{[Zx]:{address:`0x390e24E8324E56f13A8d48eB938b6f9De24CD205`}},portal:{[Zx]:{address:`0x037F161D12c829A9ca4742eEd9371830CA54fcB2`}},l1StandardBridge:{[Zx]:{address:`0xCf4df2bDB14C8FDB25FdacCEC10Ce5C4bAEDB3De`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},sourceId:Zx}),$x=L({id:112,name:`Coinbit Mainnet`,nativeCurrency:{name:`GIDR`,symbol:`GIDR`,decimals:18},rpcUrls:{default:{http:[`https://coinbit-rpc-mainnet.chain.sbcrypto.app`]}},blockExplorers:{default:{name:`Coinbit Explorer`,url:`https://coinbit-explorer.chain.sbcrypto.app`}},testnet:!1}),eS=L({id:52,name:`CoinEx Mainnet`,nativeCurrency:{name:`cet`,symbol:`cet`,decimals:18},rpcUrls:{default:{http:[`https://rpc.coinex.net`]}},blockExplorers:{default:{name:`CoinEx Explorer`,url:`https://www.coinex.net`}},testnet:!1}),tS=L({id:1030,name:`Conflux eSpace`,nativeCurrency:{name:`Conflux`,symbol:`CFX`,decimals:18},rpcUrls:{default:{http:[`https://evm.confluxrpc.com`],webSocket:[`wss://evm.confluxrpc.com/ws`]}},blockExplorers:{default:{name:`ConfluxScan`,url:`https://evm.confluxscan.org`}},contracts:{multicall3:{address:`0xEFf0078910f638cd81996cc117bccD3eDf2B072F`,blockCreated:68602935}}}),nS=L({id:71,name:`Conflux eSpace Testnet`,network:`cfx-espace-testnet`,testnet:!0,nativeCurrency:{name:`Conflux`,symbol:`CFX`,decimals:18},rpcUrls:{default:{http:[`https://evmtestnet.confluxrpc.com`],webSocket:[`wss://evmtestnet.confluxrpc.com/ws`]}},blockExplorers:{default:{name:`ConfluxScan`,url:`https://evmtestnet.confluxscan.org`}},contracts:{multicall3:{address:`0xEFf0078910f638cd81996cc117bccD3eDf2B072F`,blockCreated:117499050}}}),rS=L({id:1116,name:`Core Dao`,nativeCurrency:{decimals:18,name:`Core`,symbol:`CORE`},rpcUrls:{default:{http:[`https://rpc.coredao.org`]}},blockExplorers:{default:{name:`CoreDao`,url:`https://scan.coredao.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:11907934}},testnet:!1}),iS=L({id:1115,name:`Core Testnet`,nativeCurrency:{decimals:18,name:`tCore`,symbol:`TCORE`},rpcUrls:{default:{http:[`https://rpc.test.btcs.network`]}},blockExplorers:{default:{name:`Core Testnet`,url:`https://scan.test.btcs.network`,apiUrl:`https://api.test.btcs.network/api`}},contracts:{multicall3:{address:`0xCcddF20A1932537123C2E48Bd8e00b108B8f7569`,blockCreated:29350509}},testnet:!0}),aS=L({id:1114,name:`Core Testnet2`,nativeCurrency:{decimals:18,name:`tCore2`,symbol:`TCORE2`},rpcUrls:{default:{http:[`https://rpc.test2.btcs.network`]}},blockExplorers:{default:{name:`Core Testnet2`,url:`https://scan.test2.btcs.network`,apiUrl:`https://api.test2.btcs.network/api`}},contracts:{multicall3:{address:`0x3CB285ff3Cd5C7C7e570b1E7DE3De17A0f985e56`,blockCreated:3838600}},testnet:!0}),oS=L({id:21e6,name:`Corn`,nativeCurrency:{decimals:18,name:`Bitcorn`,symbol:`BTCN`},rpcUrls:{default:{http:[`https://21000000.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Corn Explorer`,url:`https://cornscan.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/21000000/etherscan/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3228}},sourceId:1}),sS=L({id:21000001,name:`Corn Testnet`,nativeCurrency:{decimals:18,name:`Bitcorn`,symbol:`BTCN`},rpcUrls:{default:{http:[`https://21000001.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Corn Testnet Explorer`,url:`https://testnet.cornscan.io`,apiUrl:`https://api.routescan.io/v2/network/testnet/evm/21000001/etherscan/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4886}},testnet:!0,sourceId:11155111}),cS=L({id:86608,name:`CpChain`,nativeCurrency:{decimals:18,name:`CpChain`,symbol:`CP`},rpcUrls:{default:{http:[`https://rpc.cpchain.com`]}},blockExplorers:{default:{name:`CpChain Explorer`,url:`https://explorer.cpchain.com`}},testnet:!1}),lS=L({id:44,name:`Crab Network`,nativeCurrency:{decimals:18,name:`Crab Network Native Token`,symbol:`CRAB`},rpcUrls:{default:{http:[`https://crab-rpc.darwinia.network`],webSocket:[`wss://crab-rpc.darwinia.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://crab-scan.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3032593}}}),uS=L({id:66665,name:`Creator`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.creatorchain.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.creatorchain.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0}),dS=L({id:102032,name:`Creditcoin Devnet`,nativeCurrency:{name:`Devnet CTC`,symbol:`devCTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cc3-devnet.creditcoin.network`],webSocket:[`wss://rpc.cc3-devnet.creditcoin.network/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin-devnet.blockscout.com`,apiUrl:`https://creditcoin3-dev.subscan.io`}},testnet:!0}),fS=L({id:102030,name:`Creditcoin`,nativeCurrency:{name:`Creditcoin`,symbol:`CTC`,decimals:18},rpcUrls:{default:{http:[`https://mainnet3.creditcoin.network`],webSocket:[`wss://mainnet3.creditcoin.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin.blockscout.com`,apiUrl:`https://creditcoin.blockscout.com/api`}},testnet:!1}),pS=L({id:102031,name:`Creditcoin Testnet`,nativeCurrency:{name:`Creditcoin Testnet`,symbol:`tCTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cc3-testnet.creditcoin.network`],webSocket:[`wss://rpc.cc3-testnet.creditcoin.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://creditcoin-testnet.blockscout.com`,apiUrl:`https://creditcoin-testnet.blockscout.com/api`}},testnet:!0}),mS=L({id:25,name:`Cronos Mainnet`,nativeCurrency:{decimals:18,name:`Cronos`,symbol:`CRO`},rpcUrls:{default:{http:[`https://evm.cronos.org`]}},blockExplorers:{default:{name:`Cronos Explorer`,url:`https://explorer.cronos.org`,apiUrl:`https://explorer-api.cronos.org/mainnet/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1963112}}}),hS=L({id:338,name:`Cronos Testnet`,nativeCurrency:{decimals:18,name:`CRO`,symbol:`tCRO`},rpcUrls:{default:{http:[`https://evm-t3.cronos.org`]}},blockExplorers:{default:{name:`Cronos Explorer (Testnet)`,url:`https://explorer.cronos.org/testnet`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:10191251}},testnet:!0}),gS=L({id:388,name:`Cronos zkEVM Mainnet`,nativeCurrency:{decimals:18,name:`Cronos zkEVM CRO`,symbol:`zkCRO`},rpcUrls:{default:{http:[`https://mainnet.zkevm.cronos.org`]}},blockExplorers:{default:{name:`Cronos zkEVM (Mainnet) Chain Explorer`,url:`https://explorer.zkevm.cronos.org`}},contracts:{multicall3:{address:`0x06f4487d7c4a5983d2660db965cc6d2565e4cfaa`,blockCreated:72}}}),_S=L({id:282,name:`Cronos zkEVM Testnet`,nativeCurrency:{decimals:18,name:`Cronos zkEVM Test Coin`,symbol:`zkTCRO`},rpcUrls:{default:{http:[`https://testnet.zkevm.cronos.org`]}},blockExplorers:{default:{name:`Cronos zkEVM Testnet Explorer`,url:`https://explorer.zkevm.cronos.org/testnet`}},testnet:!0}),vS=L({id:3737,name:`Crossbell`,nativeCurrency:{decimals:18,name:`CSB`,symbol:`CSB`},rpcUrls:{default:{http:[`https://rpc.crossbell.io`]}},blockExplorers:{default:{name:`CrossScan`,url:`https://scan.crossbell.io`,apiUrl:`https://scan.crossbell.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:38246031}}}),yS=L({id:4158,name:`CrossFi Mainnet`,nativeCurrency:{decimals:18,name:`CrossFi`,symbol:`XFI`},rpcUrls:{default:{http:[`https://rpc.mainnet.ms`]}},blockExplorers:{default:{name:`CrossFi Blockchain Explorer`,url:`https://xfiscan.com`}},testnet:!1}),bS=L({id:33111,name:`Curtis`,nativeCurrency:{name:`ApeCoin`,symbol:`APE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.curtis.apechain.com`]}},blockExplorers:{default:{name:`Curtis Explorer`,url:`https://explorer.curtis.apechain.com`}},testnet:!0}),xS=L({id:7560,name:`Cyber`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://cyber.alt.technology`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://cyberscan.co`,apiUrl:`https://cyberscan.co/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),SS=L({id:111557560,name:`Cyber Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://cyber-testnet.alt.technology`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet.cyberscan.co`,apiUrl:`https://testnet.cyberscan.co/api`}},contracts:{multicall3:{address:`0xffc391F0018269d4758AEA1a144772E8FB99545E`,blockCreated:304545}},testnet:!0}),CS=L({id:824,name:`Daily Network Mainnet`,nativeCurrency:{decimals:18,name:`Daily`,symbol:`DLY`},rpcUrls:{default:{http:[`https://rpc.mainnet.dailycrypto.net`]}},blockExplorers:{default:{name:`Daily Mainnet Explorer`,url:`https://explorer.mainnet.dailycrypto.net`}},testnet:!1}),wS=L({id:825,name:`Daily Network Testnet`,nativeCurrency:{decimals:18,name:`Daily`,symbol:`DLY`},rpcUrls:{default:{http:[`https://rpc.testnet.dailycrypto.net`]}},blockExplorers:{default:{name:`Daily Testnet Explorer`,url:`https://explorer.testnet.dailycrypto.net`}},testnet:!0}),TS=L({id:46,name:`Darwinia Network`,nativeCurrency:{decimals:18,name:`RING`,symbol:`RING`},rpcUrls:{default:{http:[`https://rpc.darwinia.network`],webSocket:[`wss://rpc.darwinia.network`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:69420}}}),ES=L({id:55931,name:`Datahaven Testnet`,nativeCurrency:{decimals:18,name:`MOCK`,symbol:`MOCK`},rpcUrls:{default:{http:[`https://services.datahaven-testnet.network/testnet`],webSocket:[`wss://services.datahaven-testnet.network/testnet`]}},blockExplorers:{default:{name:`DhScan`,url:`https://testnet.dhscan.io/`,apiUrl:`https://testnet.dhscan.io/api-docs`}},contracts:{},testnet:!0}),DS=L({id:20240603,name:`DBK chain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.dbkchain.io`]}},blockExplorers:{default:{name:`DBK Chain Explorer`,url:`https://scan.dbkchain.io`}},testnet:!1}),OS=L({...R,id:0x9a697f88076c8,name:`Dchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://dchain-2716446429837000-1.jsonrpc.sagarpc.io`]}},blockExplorers:{default:{name:`Dchain Explorer`,url:`https://dchain-2716446429837000-1.sagaexplorer.io`,apiUrl:`https://api-dchain-2716446429837000-1.sagaexplorer.io/api`}},contracts:{...R.contracts}}),kS=L({...R,id:0x9a379ba03cf10,name:`Dchain Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://dchaintestnet-2713017997578000-1.jsonrpc.testnet.sagarpc.io`]}},blockExplorers:{default:{name:`Dchain Explorer`,url:`https://dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io`,apiUrl:`https://api-dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io/api`}},contracts:{...R.contracts}}),AS=L({id:1130,network:`defichain-evm`,name:`DeFiChain EVM Mainnet`,nativeCurrency:{name:`DeFiChain`,symbol:`DFI`,decimals:18},rpcUrls:{default:{http:[`https://eth.mainnet.ocean.jellyfishsdk.com`]}},blockExplorers:{default:{name:`DeFiScan`,url:`https://meta.defiscan.live`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:137852}}}),jS=L({id:1131,network:`defichain-evm-testnet`,name:`DeFiChain EVM Testnet`,nativeCurrency:{name:`DeFiChain`,symbol:`DFI`,decimals:18},rpcUrls:{default:{http:[`https://eth.testnet.ocean.jellyfishsdk.com`]}},blockExplorers:{default:{name:`DeFiScan`,url:`https://meta.defiscan.live/?network=TestNet`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:156462}},testnet:!0}),MS=L({id:666666666,name:`Degen`,nativeCurrency:{decimals:18,name:`Degen`,symbol:`DEGEN`},rpcUrls:{default:{http:[`https://rpc.degen.tips`],webSocket:[`wss://rpc.degen.tips`]}},blockExplorers:{default:{name:`Degen Chain Explorer`,url:`https://explorer.degen.tips`,apiUrl:`https://explorer.degen.tips/api/v2`}}}),NS=L({id:53935,name:`DFK Chain`,nativeCurrency:{decimals:18,name:`Jewel`,symbol:`JEWEL`},rpcUrls:{default:{http:[`https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc`]}},blockExplorers:{default:{name:`DFKSubnetScan`,url:`https://subnets.avax.network/defi-kingdoms`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14790551}}}),PS=L({id:15,name:`Diode Prenet`,nativeCurrency:{decimals:18,name:`DIODE`,symbol:`DIODE`},rpcUrls:{default:{http:[`https://prenet.diode.io:8443`],webSocket:[`wss://prenet.diode.io:8443/ws`]}},blockExplorers:{default:{name:`Diode Explorer`,url:`https://diode.io/prenet`}},testnet:!1}),FS=L({id:513100,name:`DisChain`,nativeCurrency:{decimals:18,name:`DIS`,symbol:`DIS`},rpcUrls:{default:{http:[`https://rpc.dischain.xyz`]}},blockExplorers:{default:{name:`DisChain Explorer`,url:`https://www.oklink.com/dis`}}}),IS=L({id:53457,name:`DODOchain Testnet`,nativeCurrency:{decimals:18,name:`DODO`,symbol:`DODO`},rpcUrls:{default:{http:[`https://dodochain-testnet.alt.technology`],webSocket:[`wss://dodochain-testnet.alt.technology/ws`]}},blockExplorers:{default:{name:`DODOchain Testnet (Sepolia) Explorer`,url:`https://testnet-scan.dodochain.com`}},testnet:!0}),LS=L({id:2e3,name:`Dogechain`,nativeCurrency:{decimals:18,name:`Wrapped Dogecoin`,symbol:`WDOGE`},rpcUrls:{default:{http:[`https://rpc.dogechain.dog`]}},blockExplorers:{default:{name:`DogeChainExplorer`,url:`https://explorer.dogechain.dog`,apiUrl:`https://explorer.dogechain.dog/api`}},contracts:{multicall3:{address:`0x68a8609a60a008EFA633dfdec592c03B030cC508`,blockCreated:25384031}}}),RS=L({id:97476,name:`Doma Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc-testnet.doma.xyz`]}},blockExplorers:{default:{name:`Doma Testnet Explorer`,url:`https://explorer-testnet.doma.xyz`}},testnet:!0}),zS=L({id:42026,name:`Donatuz`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.donatuz.com`]}},blockExplorers:{default:{name:`Donatuz Explorer`,url:`https://explorer.donatuz.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),BS=L({id:7979,name:`DOS Chain`,nativeCurrency:{decimals:18,name:`DOS Chain`,symbol:`DOS`},rpcUrls:{default:{http:[`https://main.doschain.com`]}},blockExplorers:{default:{name:`DOS Chain Explorer`,url:`https://doscan.io`,apiUrl:`https://api.doscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:161908}}}),VS=L({id:3939,name:`DOS Chain Testnet`,nativeCurrency:{decimals:18,name:`DOS Chain Testnet`,symbol:`DOS`},rpcUrls:{default:{http:[`https://test.doschain.com`]}},blockExplorers:{default:{name:`DOS Chain Testnet Explorer`,url:`https://test.doscan.io`,apiUrl:`https://api-test.doscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:69623}},testnet:!0}),HS=L({id:23451,name:`DreyerX Mainnet`,nativeCurrency:{name:`DreyerX`,symbol:`DRX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.dreyerx.com`]}},blockExplorers:{default:{name:`DreyerX Scan`,url:`https://scan.dreyerx.com`}}}),US=L({id:23452,name:`DreyerX Testnet`,nativeCurrency:{name:`DreyerX`,symbol:`DRX`,decimals:18},rpcUrls:{default:{http:[`http://testnet-rpc.dreyerx.com`]}},blockExplorers:{default:{name:`DreyerX Testnet Scan`,url:`https://testnet-scan.dreyerx.com`}},testnet:!0}),WS=L({id:555888,name:`DustBoy IoT`,nativeCurrency:{name:`Ether`,symbol:`DST`,decimals:18},rpcUrls:{default:{http:[`https://dustboy-rpc.jibl2.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://dustboy.jibl2.com`,apiUrl:`https://dustboy.jibl2.com/api`}},contracts:{multicall3:{address:`0xFFD34aa2C62B2D52E00A361e466C229788f4eD6a`,blockCreated:526569}},testnet:!1}),GS=L({id:1100,name:`Dymension`,nativeCurrency:{name:`DYM`,symbol:`DYM`,decimals:18},rpcUrls:{default:{http:[`https://dymension-evm-rpc.publicnode.com`],webSocket:[`wss://dymension-evm-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Dym FYI`,url:`https://dym.fyi`}},testnet:!1}),KS=L({id:5424,name:`edeXa`,nativeCurrency:{name:`edeXa`,symbol:`EDX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.edexa.network`]}},blockExplorers:{default:{name:`edeXa Explorer`,url:`https://explorer.edexa.network`,apiUrl:`https://explorer.edexa.network/api/v2`}}}),qS=L({id:1995,name:`edeXa Testnet`,nativeCurrency:{name:`edeXa`,symbol:`tEDX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.edexa.network`]}},blockExplorers:{default:{name:`edeXa Testnet Explorer`,url:`https://explorer.testnet.edexa.network`,apiUrl:`https://explorer.testnet.edexa.network/api/v2`}},testnet:!0}),JS=L({id:2026,name:`Edgeless Network`,nativeCurrency:{name:`Edgeless Wrapped ETH`,symbol:`EwETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.edgeless.network/http`],webSocket:[`wss://rpc.edgeless.network/ws`]}},blockExplorers:{default:{name:`Edgeless Explorer`,url:`https://explorer.edgeless.network`}}}),YS=L({id:202,name:`Edgeless Testnet`,nativeCurrency:{name:`Edgeless Wrapped ETH`,symbol:`EwETH`,decimals:18},rpcUrls:{default:{http:[`https://edgeless-testnet.rpc.caldera.xyz/http`],webSocket:[`wss://edgeless-testnet.rpc.caldera.xyz/ws`]}},blockExplorers:{default:{name:`Edgeless Testnet Explorer`,url:`https://testnet.explorer.edgeless.network`}}}),XS=L({id:2021,name:`Edgeware EdgeEVM Mainnet`,nativeCurrency:{decimals:18,name:`Edgeware`,symbol:`EDG`},rpcUrls:{default:{http:[`https://edgeware-evm.jelliedowl.net`]}},blockExplorers:{default:{name:`Edgscan by Bharathcoorg`,url:`https://edgscan.live`,apiUrl:`https://edgscan.live/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:18117872}}}),ZS=L({id:2022,name:`Beresheet BereEVM Testnet`,nativeCurrency:{decimals:18,name:`Testnet EDG`,symbol:`tEDG`},rpcUrls:{default:{http:[`https://beresheet-evm.jelliedowl.net`]}},blockExplorers:{default:{name:`Edgscan by Bharathcoorg`,url:`https://testnet.edgscan.live`,apiUrl:`https://testnet.edgscan.live/api`}}}),QS=L({id:41923,name:`EDU Chain`,nativeCurrency:{decimals:18,name:`EDU`,symbol:`EDU`},rpcUrls:{default:{http:[`https://rpc.edu-chain.raas.gelato.cloud`]}},blockExplorers:{default:{name:`EDU Chain Explorer`,url:`https://educhain.blockscout.com/`}},testnet:!1}),$S=L({id:656476,name:`EDU Chain Testnet`,nativeCurrency:{decimals:18,name:`EDU`,symbol:`EDU`},rpcUrls:{default:{http:[`https://rpc.open-campus-codex.gelato.digital/`],webSocket:[`wss://ws.open-campus-codex.gelato.digital`]}},blockExplorers:{default:{name:`EDU Chain Testnet Explorer`,url:`https://opencampus-codex.blockscout.com`,apiUrl:`https://opencampus-codex.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:15514133}},testnet:!0}),eC=L({id:20,name:`Elastos Smart Chain`,nativeCurrency:{name:`ELA`,symbol:`ELA`,decimals:18},rpcUrls:{default:{http:[`https://api2.elastos.io/eth`]}},blockExplorers:{default:{name:`Elastos Explorer`,url:`https://esc.elastos.io`}},testnet:!1}),tC=L({id:21,name:`Elastos Smart Chain Testnet`,nativeCurrency:{name:`tELA`,symbol:`tELA`,decimals:18},rpcUrls:{default:{http:[`https://api-testnet.elastos.io/eth`]}},blockExplorers:{default:{name:`Elastos Explorer`,url:`https://esc-testnet.elastos.io`}},testnet:!0}),nC=L({id:52014,name:`Electroneum Mainnet`,nativeCurrency:{name:`ETN`,symbol:`ETN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.electroneum.com`]}},blockExplorers:{default:{name:`Electroneum Block Explorer`,url:`https://blockexplorer.electroneum.com`}},testnet:!1}),rC=L({id:5201420,name:`Electroneum Testnet`,nativeCurrency:{name:`ETN`,symbol:`ETN`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.electroneum.com`]}},blockExplorers:{default:{name:`Electroneum Block Explorer`,url:`https://blockexplorer.thesecurityteam.rocks`}},testnet:!0}),iC=L({...R,id:1338,name:`Elysium Testnet`,nativeCurrency:{decimals:18,name:`LAVA`,symbol:`LAVA`},rpcUrls:{default:{http:[`https://elysium-test-rpc.vulcanforged.com`]}},blockExplorers:{default:{name:`Elysium testnet explorer`,url:`https://elysium-explorer.vulcanforged.com`}},testnet:!0}),aC=L({id:246,name:`Energy Mainnet`,nativeCurrency:{name:`EWT`,symbol:`EWT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.energyweb.org`]}},blockExplorers:{default:{name:`EnergyWeb Explorer`,url:`https://explorer.energyweb.org`}},testnet:!1}),oC=L({id:173,name:`ENI Mainnet`,nativeCurrency:{decimals:18,name:`ENI`,symbol:`ENI`},rpcUrls:{default:{http:[`https://rpc.eniac.network`]}},blockExplorers:{default:{name:`ENI Explorer`,url:`https://scan.eniac.network`}},testnet:!1}),sC=L({id:6912115,name:`ENI Testnet`,nativeCurrency:{decimals:18,name:`ENI Testnet Token`,symbol:`ENI`},rpcUrls:{default:{http:[`https://rpc-testnet.eniac.network`]}},blockExplorers:{default:{name:`ENI Testnet Explorer`,url:`https://scan-testnet.eniac.network`}},testnet:!0}),cC=L({id:119,name:`ENULS Mainnet`,nativeCurrency:{decimals:18,name:`NULS`,symbol:`NULS`},rpcUrls:{default:{http:[`https://evmapi2.nuls.io`]}},blockExplorers:{default:{name:`ENULS Explorer`,url:`https://evmscan.nuls.io`}},testnet:!1}),lC=L({id:7332,name:`Horizen EON`,nativeCurrency:{decimals:18,name:`ZEN`,symbol:`ZEN`},rpcUrls:{default:{http:[`https://eon-rpc.horizenlabs.io/ethv1`]}},blockExplorers:{default:{name:`EON Explorer`,url:`https://eon-explorer.horizenlabs.io`}},contracts:{}}),uC=L({id:17777,name:`EOS EVM`,nativeCurrency:{decimals:18,name:`EOS`,symbol:`EOS`},rpcUrls:{default:{http:[`https://api.evm.eosnetwork.com`]}},blockExplorers:{default:{name:`EOS EVM Explorer`,url:`https://explorer.evm.eosnetwork.com`,apiUrl:`https://explorer.evm.eosnetwork.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:7943933}}}),dC=L({id:15557,name:`EOS EVM Testnet`,nativeCurrency:{decimals:18,name:`EOS`,symbol:`EOS`},rpcUrls:{default:{http:[`https://api.testnet.evm.eosnetwork.com`]}},blockExplorers:{default:{name:`EOS EVM Testnet Explorer`,url:`https://explorer.testnet.evm.eosnetwork.com`,apiUrl:`https://explorer.testnet.evm.eosnetwork.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:9067940}},testnet:!0}),fC=L({id:140,name:`Eteria`,nativeCurrency:{name:`Eteria`,symbol:`ERA`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.eteria.io/v1`]}},blockExplorers:{default:{name:`Eteria Explorer`,url:`https://explorer.eteria.io`,apiUrl:`https://explorer.eteria.io/api`}}}),pC=L({id:42793,name:`Etherlink`,blockTime:4830,nativeCurrency:{decimals:18,name:`Tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.mainnet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink`,url:`https://explorer.etherlink.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:33899}}}),mC=L({id:127823,name:`Etherlink Shadownet Testnet`,nativeCurrency:{decimals:18,name:`tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.shadownet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink Shadownet Testnet Explorer`,url:`https://shadownet.explorer.etherlink.com`}},testnet:!0}),hC=L({id:128123,name:`Etherlink Testnet`,nativeCurrency:{decimals:18,name:`Tez`,symbol:`XTZ`},rpcUrls:{default:{http:[`https://node.ghostnet.etherlink.com`]}},blockExplorers:{default:{name:`Etherlink Testnet`,url:`https://testnet.explorer.etherlink.com`}},testnet:!0}),gC=L({id:183,name:`Ethernity`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.ethernitychain.io`]}},blockExplorers:{default:{name:`Ethernity Explorer`,url:`https://ernscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!1}),_C=L({id:20256789,name:`ETP Mainnet`,nativeCurrency:{decimals:18,name:`ETP Chain Native Token`,symbol:`ETP`},rpcUrls:{default:{http:[`https://rpc.etpscan.xyz`]}},blockExplorers:{default:{name:`ETP Scan`,url:`https://etpscan.xyz`}}}),vC=L({id:9001,name:`Evmos`,nativeCurrency:{decimals:18,name:`Evmos`,symbol:`EVMOS`},rpcUrls:{default:{http:[`https://eth.bd.evmos.org:8545`]}},blockExplorers:{default:{name:`Evmos Block Explorer`,url:`https://escan.live`}}}),yC=L({id:9e3,name:`Evmos Testnet`,nativeCurrency:{decimals:18,name:`Evmos`,symbol:`EVMOS`},rpcUrls:{default:{http:[`https://eth.bd.evmos.dev:8545`]}},blockExplorers:{default:{name:`Evmos Testnet Block Explorer`,url:`https://evm.evmos.dev/`}}}),bC=L({id:22052002,name:`Excelon Mainnet`,network:`XLON`,nativeCurrency:{decimals:18,name:`Excelon`,symbol:`xlon`},rpcUrls:{default:{http:[`https://edgewallet1.xlon.org`]}},blockExplorers:{default:{name:`Excelon explorer`,url:`https://explorer.excelon.io`}}}),xC=L({id:2,name:`Expanse Network`,nativeCurrency:{decimals:18,name:`EXP`,symbol:`EXP`},rpcUrls:{default:{http:[`https://node.expanse.tech`]}},blockExplorers:{default:{name:`Expanse Explorer`,url:`https://explorer.expanse.tech`}},testnet:!1}),SC=L({id:7200,name:`exSat Network`,nativeCurrency:{decimals:18,name:`BTC`,symbol:`BTC`},rpcUrls:{default:{http:[`https://evm.exsat.network`]}},blockExplorers:{default:{name:`exSat Explorer`,url:`https://scan.exsat.network`,apiUrl:`https://scan.exsat.network/api`}}}),CC=L({id:839999,name:`exSat Testnet`,nativeCurrency:{decimals:18,name:`BTC`,symbol:`BTC`},rpcUrls:{default:{http:[`https://evm-tst3.exsat.network`]}},blockExplorers:{default:{name:`exSat Explorer`,url:`https://scan-testnet.exsat.network`,apiUrl:`https://scan-testnet.exsat.network/api`}}}),wC=L({id:250,name:`Fantom`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://250.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`FTMScan`,url:`https://ftmscan.com`,apiUrl:`https://api.ftmscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:33001987}}}),TC=L({id:64240,name:`Fantom Sonic Open Testnet`,network:`fantom-sonic-testnet`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://rpcapi.sonic.fantom.network`]}},blockExplorers:{default:{name:`Fantom Sonic Open Testnet Explorer`,url:`https://public-sonic.fantom.network`}},testnet:!0}),EC=L({id:4002,name:`Fantom Testnet`,nativeCurrency:{decimals:18,name:`Fantom`,symbol:`FTM`},rpcUrls:{default:{http:[`https://rpc.testnet.fantom.network`]}},blockExplorers:{default:{name:`FTMScan`,url:`https://testnet.ftmscan.com`,apiUrl:`https://testnet.ftmscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:8328688}},testnet:!0}),DC=L({id:12306,name:`Fibo Chain`,nativeCurrency:{decimals:18,name:`fibo`,symbol:`FIBO`},rpcUrls:{default:{http:[`https://network.hzroc.art`]}},blockExplorers:{default:{name:`FiboScan`,url:`https://scan.fibochain.org`}}}),OC=L({id:314,name:`Filecoin Mainnet`,nativeCurrency:{decimals:18,name:`filecoin`,symbol:`FIL`},rpcUrls:{default:{http:[`https://api.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filfox`,url:`https://filfox.info/en`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3328594}}}),kC=L({id:314159,name:`Filecoin Calibration`,nativeCurrency:{decimals:18,name:`testnet filecoin`,symbol:`tFIL`},rpcUrls:{default:{http:[`https://api.calibration.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filscan`,url:`https://calibration.filscan.io`}},testnet:!0}),AC=L({id:3141,name:`Filecoin Hyperspace`,nativeCurrency:{decimals:18,name:`testnet filecoin`,symbol:`tFIL`},rpcUrls:{default:{http:[`https://api.hyperspace.node.glif.io/rpc/v1`]}},blockExplorers:{default:{name:`Filfox`,url:`https://hyperspace.filfox.info/en`}},testnet:!0}),jC=L({id:253368190,name:`Flame`,network:`flame`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.flame.astria.org`],webSocket:[`wss://ws.flame.astria.org`]}},blockExplorers:{default:{name:`Flame Explorer`,url:`https://explorer.flame.astria.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6829148}}}),MC=L({id:14,name:`Flare Mainnet`,nativeCurrency:{decimals:18,name:`Flare`,symbol:`FLR`},rpcUrls:{default:{http:[`https://flare-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Flare Explorer`,url:`https://flare-explorer.flare.network`,apiUrl:`https://flare-explorer.flare.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3002461}}}),NC=L({id:114,name:`Flare Testnet Coston2`,nativeCurrency:{decimals:18,name:`Coston2 Flare`,symbol:`C2FLR`},rpcUrls:{default:{http:[`https://coston2-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Coston2 Explorer`,url:`https://coston2-explorer.flare.network`,apiUrl:`https://coston2-explorer.flare.network/api`}},testnet:!0}),PC=L({id:747,name:`Flow EVM Mainnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://mainnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Mainnet Explorer`,url:`https://evm.flowscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6205}},blockTime:800}),FC=L({id:646,name:`Flow EVM Previewnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://previewnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Previewnet Explorer`,url:`https://previewnet.flowdiver.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6205}}}),IC=L({id:545,name:`Flow EVM Testnet`,nativeCurrency:{decimals:18,name:`Flow`,symbol:`FLOW`},rpcUrls:{default:{http:[`https://testnet.evm.nodes.onflow.org`]}},blockExplorers:{default:{name:`Flow Diver`,url:`https://evm-testnet.flowscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:137518}},testnet:!0,blockTime:800}),LC=L({id:9999999,name:`Fluence`,nativeCurrency:{name:`FLT`,symbol:`FLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.fluence.dev`],webSocket:[`wss://ws.mainnet.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.mainnet.fluence.dev`,apiUrl:`https://blockscout.mainnet.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:207583}}}),RC=L({id:123420000220,name:`Fluence Stage`,nativeCurrency:{name:`tFLT`,symbol:`tFLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stage.fluence.dev`],webSocket:[`wss://ws.stage.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.stage.fluence.dev`,apiUrl:`https://blockscout.stage.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:83227}},testnet:!0}),zC=L({id:52164803,name:`Fluence Testnet`,nativeCurrency:{name:`tFLT`,symbol:`tFLT`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.fluence.dev`],webSocket:[`wss://ws.testnet.fluence.dev`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.testnet.fluence.dev`,apiUrl:`https://blockscout.testnet.fluence.dev/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96424}},testnet:!0}),BC=L({id:20993,name:`Fluent Devnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.devnet.fluent.xyz`]}},blockExplorers:{default:{name:`Fluent Devnet Explorer`,url:`https://devnet.fluentscan.xyz`}},testnet:!0}),VC=L({id:20994,name:`Fluent Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.fluent.xyz`]}},blockExplorers:{default:{name:`Fluent Testnet Explorer`,url:`https://testnet.fluentscan.xyz`}},testnet:!0}),HC=1,UC=L({id:478,name:`Form Network`,nativeCurrency:{decimals:18,name:`Ethereum`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.form.network/http`],webSocket:[`wss://rpc.form.network/ws`]}},blockExplorers:{default:{name:`Form Explorer`,url:`https://explorer.form.network`}},contracts:{...R.contracts,addressManager:{[HC]:{address:`0x15c249E46A2F924C2dB3A1560CF86729bAD1f07B`}},l1CrossDomainMessenger:{[HC]:{address:`0xF333158DCCad1dF6C3F0a3aEe8BC31fA94d9eD5c`}},l2OutputOracle:{[HC]:{address:`0x4ccAAF69F41c5810cA875183648B577CaCf1F67E`}},portal:{[HC]:{address:`0x4E259Ee5F4136408908160dD32295A5031Fa426F`}},l1StandardBridge:{[HC]:{address:`0xdc20aA63D3DE59574E065957190D8f24e0F7B8Ba`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},sourceId:HC}),WC=L({id:984122,name:`Forma`,network:`forma`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.forma.art`],webSocket:[`wss://ws.forma.art`]}},blockExplorers:{default:{name:`Forma Explorer`,url:`https://explorer.forma.art`}},contracts:{multicall3:{address:`0xd53C6FFB123F7349A32980F87faeD8FfDc9ef079`,blockCreated:252705}}}),GC=11155111,KC=L({id:132902,name:`Form Testnet`,nativeCurrency:{decimals:18,name:`Ethereum`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia-rpc.form.network/http`],webSocket:[`wss://sepolia-rpc.form.network/ws`]}},blockExplorers:{default:{name:`Form Testnet Explorer`,url:`https://sepolia-explorer.form.network`}},contracts:{...R.contracts,addressManager:{[GC]:{address:`0xd5C38fa934f7fd7477D4800F4f38a1c5BFdF1373`}},l1CrossDomainMessenger:{[GC]:{address:`0x37A68565c4BE9700b3E3Ec60cC4416cAC3052FAa`}},l2OutputOracle:{[GC]:{address:`0x9eA2239E65a59EC9C7F1ED4C116dD58Da71Fc1e2`}},portal:{[GC]:{address:`0x60377e3cE15dF4CCA24c4beF076b60314240b032`}},l1StandardBridge:{[GC]:{address:`0xD4531f633942b2725896F47cD2aFd260b44Ab1F7`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},testnet:!0,sourceId:GC}),qC=L({id:80931,name:`Forta Chain`,nativeCurrency:{symbol:`FORT`,name:`FORT`,decimals:18},rpcUrls:{default:{http:[`https://rpc-forta-chain-8gj1qndmfc.t.conduit.xyz`]}},blockExplorers:{default:{name:`Forta Explorer`,url:`https://explorer.forta.org`}}}),JC=L({id:31337,name:`Foundry`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`],webSocket:[`ws://127.0.0.1:8545`]}}}),YC=1,XC=L({...R,id:252,name:`Fraxtal`,nativeCurrency:{name:`Frax`,symbol:`FRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.frax.com`]}},blockExplorers:{default:{name:`fraxscan`,url:`https://fraxscan.com`,apiUrl:`https://api.fraxscan.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[YC]:{address:`0x66CC916Ed5C6C2FA97014f7D1cD141528Ae171e4`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[YC]:{address:`0x36cb65c1967A0Fb0EEE11569C51C2f2aA1Ca6f6D`,blockCreated:19135323}},l1StandardBridge:{[YC]:{address:`0x34C0bD5877A5Ee7099D0f5688D65F4bB9158BDE2`,blockCreated:19135323}}},sourceId:YC}),ZC=17e3,QC=L({...R,id:2522,name:`Fraxtal Testnet`,nativeCurrency:{name:`Frax`,symbol:`FRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.frax.com`]}},blockExplorers:{default:{name:`fraxscan testnet`,url:`https://holesky.fraxscan.com`,apiUrl:`https://api-holesky.fraxscan.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[ZC]:{address:`0x715EA64DA13F4d0831ece4Ad3E8c1aa013167F32`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[ZC]:{address:`0xB9c64BfA498d5b9a8398Ed6f46eb76d90dE5505d`,blockCreated:318416}},l1StandardBridge:{[ZC]:{address:`0x0BaafC217162f64930909aD9f2B27125121d6332`,blockCreated:318416}}},sourceId:ZC}),$C=1,ew=L({...R,id:33979,name:`Funki`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.funkichain.com`]}},blockExplorers:{default:{name:`Funki Mainnet Explorer`,url:`https://funkiscan.io`}},contracts:{...R.contracts},sourceId:$C}),tw=11155111,nw=L({...R,id:3397901,network:`funkiSepolia`,name:`Funki Sepolia Sandbox`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://funki-testnet.alt.technology`]}},blockExplorers:{default:{name:`Funki Sepolia Sandbox Explorer`,url:`https://sepolia-sandbox.funkichain.com/`}},testnet:!0,contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1620204}},sourceId:tw}),rw=L({id:122,name:`Fuse`,nativeCurrency:{name:`Fuse`,symbol:`FUSE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.fuse.io`]}},blockExplorers:{default:{name:`Fuse Explorer`,url:`https://explorer.fuse.io`,apiUrl:`https://explorer.fuse.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:16146628}}}),iw=L({id:123,name:`Fuse Sparknet`,nativeCurrency:{name:`Spark`,symbol:`SPARK`,decimals:18},rpcUrls:{default:{http:[`https://rpc.fusespark.io`]}},blockExplorers:{default:{name:`Sparkent Explorer`,url:`https://explorer.fusespark.io`,apiUrl:`https://explorer.fusespark.io/api`}}}),aw=L({id:32659,name:`Fusion Mainnet`,nativeCurrency:{name:`Fusion`,symbol:`FSN`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.fusionnetwork.io`],webSocket:[`wss://mainnet.fusionnetwork.io`]}},blockExplorers:{default:{name:`FSNscan`,url:`https://fsnscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10441605}},testnet:!1}),ow=L({id:46688,name:`Fusion Testnet`,nativeCurrency:{name:`Fusion`,symbol:`FSN`,decimals:18},rpcUrls:{default:{http:[`https://testnet.fusionnetwork.io`],webSocket:[`wss://testnet.fusionnetwork.io`]}},blockExplorers:{default:{name:`FSNscan`,url:`https://testnet.fsnscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10428309}},testnet:!0}),sw=17e3,cw=L({...R,name:`Garnet Testnet`,testnet:!0,id:17069,sourceId:sw,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.garnetchain.com`],webSocket:[`wss://rpc.garnetchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.garnetchain.com`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[sw]:{address:`0x57ee40586fbE286AfC75E67cb69511A6D9aF5909`,blockCreated:1274684}},l2OutputOracle:{[sw]:{address:`0xCb8E7AC561b8EF04F2a15865e9fbc0766FEF569B`,blockCreated:1274684}},l1StandardBridge:{[sw]:{address:`0x09bcDd311FE398F80a78BE37E489f5D440DB95DE`,blockCreated:1274684}}}}),lw=L({id:86,name:`GateChain Mainnet`,nativeCurrency:{name:`GateChainToken`,symbol:`GT`,decimals:18},rpcUrls:{default:{http:[`https://evm.nodeinfo.cc`],webSocket:[`wss://evm-ws.gatenode.cc`]}},blockExplorers:{default:{name:`Gate Scan`,url:`https://www.gatescan.org`,apiUrl:`https://gatescan.org/api`}},testnet:!1}),uw=L({id:63157,name:`Geist Mainnet`,nativeCurrency:{decimals:18,name:`Aavegotchi GHST Token`,symbol:`GHST`},rpcUrls:{default:{http:[`https://geist-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://geist-mainnet.explorer.alchemy.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:660735}}}),dw=L({id:16507,name:`Genesys Mainnet`,nativeCurrency:{decimals:18,name:`GSYS`,symbol:`GSYS`},rpcUrls:{default:{http:[`https://rpc.genesys.network`]}},blockExplorers:{default:{name:`Genesys Explorer`,url:`https://gchainexplorer.genesys.network`}},testnet:!1}),fw=11155111,pw=L({...R,id:91342,network:`giwa-sepolia`,name:`GIWA Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://sepolia-rpc.giwa.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia-explorer.giwa.io`,apiUrl:`https://sepolia-explorer.giwa.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},disputeGameFactory:{[fw]:{address:`0x37347caB2afaa49B776372279143D71ad1f354F6`}},portal:{[fw]:{address:`0x956962C34687A954e611A83619ABaA37Ce6bC78A`}},l1StandardBridge:{[fw]:{address:`0x77b2ffc0F57598cAe1DB76cb398059cF5d10A7E7`}}},testnet:!0,sourceId:fw}),mw=L({...pw,experimental_preconfirmationTime:200,rpcUrls:{default:{http:[`https://sepolia-rpc-flashblocks.giwa.io`]}}}),hw=L({id:251,name:`Glide L1 Protocol XP`,nativeCurrency:{name:`GLXP`,symbol:`GLXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc-api.glideprotocol.xyz/l1-rpc`],webSocket:[`wss://rpc-api.glideprotocol.xyz/l1-rpc`]}},blockExplorers:{default:{name:`Glide Protocol Explore`,url:`https://blockchain-explorer.glideprotocol.xyz`}},testnet:!1}),gw=L({id:253,name:`Glide L2 Protocol XP`,nativeCurrency:{name:`GLXP`,symbol:`GLXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc-api.glideprotocol.xyz/l2-rpc`],webSocket:[`wss://rpc-api.glideprotocol.xyz/l2-rpc`]}},blockExplorers:{default:{name:`Glide Protocol Explore`,url:`https://blockchain-explorer.glideprotocol.xyz`}},testnet:!1}),_w=L({id:100,name:`Gnosis`,nativeCurrency:{decimals:18,name:`xDAI`,symbol:`XDAI`},blockTime:5e3,rpcUrls:{default:{http:[`https://rpc.gnosischain.com`],webSocket:[`wss://rpc.gnosischain.com/wss`]}},blockExplorers:{default:{name:`Gnosisscan`,url:`https://gnosisscan.io`,apiUrl:`https://api.gnosisscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:21022491}}}),vw=L({id:10200,name:`Gnosis Chiado`,nativeCurrency:{decimals:18,name:`Gnosis`,symbol:`xDAI`},blockTime:5e3,rpcUrls:{default:{http:[`https://rpc.chiadochain.net`],webSocket:[`wss://rpc.chiadochain.net/wss`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.chiadochain.net`,apiUrl:`https://blockscout.chiadochain.net/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4967313}},testnet:!0}),yw=L({id:2345,name:`GOAT`,nativeCurrency:{decimals:18,name:`Bitcoin`,symbol:`BTC`},rpcUrls:{default:{http:[`https://rpc.goat.network`]}},blockExplorers:{default:{name:`Goat Explorer`,url:`https://explorer.goat.network`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),bw=L({id:1663,name:`Horizen Gobi Testnet`,nativeCurrency:{decimals:18,name:`Test ZEN`,symbol:`tZEN`},rpcUrls:{default:{http:[`https://gobi-testnet.horizenlabs.io/ethv1`]}},blockExplorers:{default:{name:`Gobi Explorer`,url:`https://gobi-explorer.horizen.io`}},contracts:{},testnet:!0}),xw=L({id:60,name:`GoChain`,nativeCurrency:{decimals:18,name:`GO`,symbol:`GO`},rpcUrls:{default:{http:[`https://rpc.gochain.io`]}},blockExplorers:{default:{name:`GoChain Explorer`,url:`https://explorer.gochain.io`}},testnet:!1}),Sw=L({id:71402,name:`Godwoken Mainnet`,nativeCurrency:{decimals:18,name:`pCKB`,symbol:`pCKB`},rpcUrls:{default:{http:[`https://v1.mainnet.godwoken.io/rpc`]}},blockExplorers:{default:{name:`GW Scan`,url:`https://v1.gwscan.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:15034}},testnet:!1}),Cw=L({id:5,name:`Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://5.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.etherscan.io`,apiUrl:`https://api-goerli.etherscan.io/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},ensUniversalResolver:{address:`0xfc4AC75C46C914aF5892d6d3eFFcebD7917293F1`,blockCreated:10339206},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6507670}},testnet:!0}),ww=L({id:440017,name:`Graphite Network`,nativeCurrency:{name:`Graphite`,symbol:`@G`,decimals:18},rpcUrls:{default:{http:[`https://anon-entrypoint-1.atgraphite.com`],webSocket:[`wss://ws-anon-entrypoint-1.atgraphite.com`]}},blockExplorers:{default:{name:`Graphite Spectre`,url:`https://main.atgraphite.com`,apiUrl:`https://api.main.atgraphite.com/api`}},testnet:!1}),Tw=L({id:54170,name:`Graphite Network Testnet`,nativeCurrency:{name:`Graphite`,symbol:`@G`,decimals:18},rpcUrls:{default:{http:[`https://anon-entrypoint-test-1.atgraphite.com`],webSocket:[`wss://ws-anon-entrypoint-test-1.atgraphite.com`]}},blockExplorers:{default:{name:`Graphite Testnet Spectre`,url:`https://test.atgraphite.com`,apiUrl:`https://api.test.atgraphite.com/api`}},testnet:!0}),Ew=L({id:1625,name:`Gravity Alpha Mainnet`,nativeCurrency:{name:`G`,symbol:`G`,decimals:18},rpcUrls:{default:{http:[`https://rpc.gravity.xyz`]}},blockExplorers:{default:{name:`Gravity Explorer`,url:`https://explorer.gravity.xyz`,apiUrl:`https://explorer.gravity.xyz/api`}},contracts:{multicall3:{address:`0xf8ac4BEB2F75d2cFFb588c63251347fdD629B92c`,blockCreated:16851}}}),Dw=L({id:43419,name:`Gunz Mainnet`,nativeCurrency:{name:`GUN`,symbol:`GUN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.gunzchain.io/ext/bc/2M47TxWHGnhNtq6pM5zPXdATBtuqubxn5EPFgFmEawCQr9WFML/rpc`]}},blockExplorers:{default:{name:`Gunz Explorer`,url:`https://gunzscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:70502}}}),Ow=L({id:260,name:`Guru Network Mainnet`,nativeCurrency:{name:`GURU Token`,symbol:`GURU`,decimals:18},rpcUrls:{default:{http:[`https://rpc-main.gurunetwork.ai`,`https://rpc.gurunetwork.ai/archive/260`]}},blockExplorers:{default:{name:`Guruscan`,url:`https://scan.gurunetwork.ai`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:271691}},testnet:!1}),kw=L({id:261,name:`Guru Network Testnet`,nativeCurrency:{name:`tGURU Token`,symbol:`tGURU`,decimals:18},rpcUrls:{default:{http:[`https://rpc-test.gurunetwork.ai`,`https://rpc.gurunetwork.ai/archive/261`]}},blockExplorers:{default:{name:`Guruscan`,url:`https://sepolia.gurunetwork.ai`}},testnet:!0}),Aw=L({id:5112,name:`Ham`,nativeCurrency:{decimals:18,name:`Ham`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.ham.fun`],webSocket:[`wss://rpc.ham.fun`]}},blockExplorers:{default:{name:`Ham Chain Explorer`,url:`https://explorer.ham.fun`,apiUrl:`https://explorer.ham.fun/api/v2`}}}),jw=L({id:216,name:`Happychain Testnet`,nativeCurrency:{symbol:`HAPPY`,name:`HAPPY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.happy.tech/http`],webSocket:[`wss://rpc.testnet.happy.tech/ws`]}},blockExplorers:{default:{name:`Happy Chain Testnet Explorer`,url:`https://explorer.testnet.happy.tech`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1}},testnet:!0}),Mw=L({id:11235,name:`HAQQ Mainnet`,nativeCurrency:{decimals:18,name:`Islamic Coin`,symbol:`ISLM`},rpcUrls:{default:{http:[`https://rpc.eth.haqq.network`]}},blockExplorers:{default:{name:`HAQQ Explorer`,url:`https://explorer.haqq.network`,apiUrl:`https://explorer.haqq.network/api`}}}),Nw=L({id:54211,name:`HAQQ Testedge 2`,nativeCurrency:{decimals:18,name:`Islamic Coin`,symbol:`ISLMT`},rpcUrls:{default:{http:[`https://rpc.eth.testedge2.haqq.network`]}},blockExplorers:{default:{name:`HAQQ Explorer`,url:`https://explorer.testedge2.haqq.network`,apiUrl:`https://explorer.testedge2.haqq.network/api`}}}),Pw=L({id:31337,name:`Hardhat`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),Fw=L({id:16666e5,name:`Harmony One`,nativeCurrency:{name:`Harmony`,symbol:`ONE`,decimals:18},rpcUrls:{default:{http:[`https://1666600000.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Harmony Explorer`,url:`https://explorer.harmony.one`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:24185753}}}),Iw=L({id:177,name:`HashKey Chain`,nativeCurrency:{decimals:18,name:`HashKey EcoPoints`,symbol:`HSK`},rpcUrls:{default:{http:[`https://mainnet.hsk.xyz`]}},blockExplorers:{default:{name:`HashKey Chain Explorer`,url:`https://hashkey.blockscout.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),Lw=L({id:133,name:`HashKey Chain Testnet`,nativeCurrency:{decimals:18,name:`HashKey EcoPoints`,symbol:`HSK`},rpcUrls:{default:{http:[`https://testnet.hsk.xyz`]}},blockExplorers:{default:{name:`HashKey Chain Testnet explorer`,url:`https://testnet-explorer.hsk.xyz`}},testnet:!0}),Rw=L({id:1523903251,name:`Haust Network Testnet`,nativeCurrency:{decimals:18,name:`HAUST`,symbol:`HAUST`},rpcUrls:{default:{http:[`https://rpc-testnet.haust.app`]}},blockExplorers:{default:{name:`Haust Network Testnet Explorer`,url:`https://explorer-testnet.haust.app`}},testnet:!0}),zw=L({id:295,name:`Hedera Mainnet`,network:`hedera-mainnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/mainnet`}},testnet:!1}),Bw=L({id:297,name:`Hedera Previewnet`,network:`hedera-previewnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://previewnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/previewnet`}},testnet:!0}),Vw=L({id:296,name:`Hedera Testnet`,network:`hedera-testnet`,nativeCurrency:{symbol:`HBAR`,name:`HBAR`,decimals:18},rpcUrls:{default:{http:[`https://testnet.hashio.io/api`]}},blockExplorers:{default:{name:`Hashscan`,url:`https://hashscan.io/testnet`}},testnet:!0}),Hw=L({id:8668,name:`Hela Mainnet`,nativeCurrency:{name:`HLUSD`,symbol:`HLUSD`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.helachain.com`]}},blockExplorers:{default:{name:`Hela explorer`,url:`https://mainnet-blockexplorer.helachain.com`}},testnet:!1}),Uw=L({id:42e3,name:`Helios Testnet`,network:`helios-testnet`,nativeCurrency:{symbol:`HLS`,name:`Helios`,decimals:18},rpcUrls:{default:{http:[`https://testnet1.helioschainlabs.org`]}},blockExplorers:{default:{name:`Helios Testnet Explorer`,url:`https://explorer.helioschainlabs.org/`}},testnet:!0}),Ww=L({id:43111,name:`Hemi`,network:`Hemi`,blockTime:12e3,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hemi.network/rpc`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.hemi.xyz`}},testnet:!1}),Gw=L({id:743111,name:`Hemi Sepolia`,network:`Hemi Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.rpc.hemi.network/rpc`]}},blockExplorers:{default:{name:`Hemi Sepolia explorer`,url:`https://testnet.explorer.hemi.xyz`}},testnet:!0}),Kw=L({id:68414,name:`Henesys`,nativeCurrency:{name:`NEXPACE`,symbol:`NXPC`,decimals:18},rpcUrls:{default:{http:[`https://henesys-rpc.msu.io`]}},blockExplorers:{default:{name:`Avalanche Explorer`,url:`https://subnets.avax.network/henesys`}}}),qw=L({id:17e3,name:`Holesky`,nativeCurrency:{name:`Holesky Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://ethereum-holesky-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://holesky.etherscan.io`,apiUrl:`https://api-holesky.etherscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:77},ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:4295055}},testnet:!0}),Jw=L({id:560048,name:`Hoodi`,nativeCurrency:{name:`Hoodi Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hoodi.ethpandaops.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://hoodi.etherscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2589}},testnet:!0}),Yw=L({id:2651420,name:`Horizen Testnet`,network:`horizen-testnet`,nativeCurrency:{symbol:`Sepolia Ether`,name:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://horizen-testnet.rpc.caldera.xyz/http`]}},blockExplorers:{default:{name:`Horizen Testnet Caldera Explorer`,url:`https://horizen-testnet.explorer.caldera.xyz`}},testnet:!0}),Xw=L({id:269,name:`High Performance Blockchain`,nativeCurrency:{name:`HPB`,symbol:`HPB`,decimals:18},rpcUrls:{default:{http:[`https://hpbnode.com`]}},blockExplorers:{default:{name:`hpbScan`,url:`https://hscan.org`}},testnet:!1}),Zw=L({id:190415,name:`HPP Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.hpp.io`],webSocket:[`wss://mainnet.hpp.io`]}},blockExplorers:{default:{name:`HPP Mainnet Explorer`,url:`https://explorer.hpp.io`}},testnet:!1}),Qw=L({id:181228,name:`HPP Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.hpp.io`],webSocket:[`wss://testnet.hpp.io`]}},blockExplorers:{default:{name:`HPP Sepolia Explorer`,url:`https://sepolia-explorer.hpp.io`}},testnet:!0}),$w=L({id:12323,name:`Huddle01 dRTC Chain`,nativeCurrency:{name:`Ethereum`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://huddle01.calderachain.xyz/http`],webSocket:[`wss://huddle01.calderachain.xyz/ws`]}},blockExplorers:{default:{name:`Huddle01 Caldera Explorer`,url:`https://huddle01.calderaexplorer.xyz`,apiUrl:`https://huddle01.calderaexplorer.xyz/api`}},sourceId:42161}),eT=L({id:2524852,name:`Huddle01 dRTC Chain Testnet`,nativeCurrency:{name:`Ethereum`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://huddle-testnet.rpc.caldera.xyz/http`],webSocket:[`wss://huddle-testnet.rpc.caldera.xyz/ws`]}},blockExplorers:{default:{name:`Huddle01 Caldera Explorer`,url:`https://huddle-testnet.explorer.caldera.xyz`,apiUrl:`https://huddle-testnet.explorer.caldera.xyz/api`}},sourceId:421614}),tT=L({id:6985385,name:`Humanity`,nativeCurrency:{name:`H`,symbol:`H`,decimals:18},rpcUrls:{default:{http:[`https://humanity-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Humanity Mainnet Explorer`,url:`https://humanity-mainnet.explorer.alchemy.com`,apiUrl:`https://humanity-mainnet.explorer.alchemy.com/api`}},testnet:!1}),nT=L({id:7080969,name:`Humanity Testnet`,nativeCurrency:{name:`tHP`,symbol:`tHP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.humanity.org`]}},blockExplorers:{default:{name:`Humanity Testnet Explorer`,url:`https://humanity-testnet.explorer.alchemy.com`,apiUrl:`https://humanity-testnet.explorer.alchemy.com/api`}},testnet:!0}),rT=L({id:5234,name:`Humanode`,nativeCurrency:{name:`HMND`,symbol:`HMND`,decimals:18},rpcUrls:{default:{http:[`https://explorer-rpc-http.mainnet.stages.humanode.io`],webSocket:[`wss://explorer-rpc-ws.mainnet.stages.humanode.io`]}},blockExplorers:{default:{name:`Subscan`,url:`https://humanode.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4413097}}}),iT=L({id:14853,name:`Humanode Testnet 5`,nativeCurrency:{name:`HMND`,symbol:`HMND`,decimals:18},rpcUrls:{default:{http:[`https://explorer-rpc-http.testnet5.stages.humanode.io`],webSocket:[`wss://explorer-rpc-ws.testnet5.stages.humanode.io`]}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),aT=L({id:2911,name:`HYCHAIN`,nativeCurrency:{name:`HYTOPIA`,symbol:`TOPIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hychain.com/http`]}},blockExplorers:{default:{name:`HYCHAIN Explorer`,url:`https://explorer.hychain.com`}},testnet:!1}),oT=L({id:29112,name:`HYCHAIN Testnet`,nativeCurrency:{name:`HYTOPIA`,symbol:`TOPIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hychain.com/http`]}},blockExplorers:{default:{name:`HYCHAIN Explorer`,url:`https://testnet-rpc.hychain.com/http`}},testnet:!0}),sT=L({id:999,name:`HyperEVM`,nativeCurrency:{name:`HYPE`,symbol:`HYPE`,decimals:18},blockExplorers:{default:{name:`HyperEVMScan`,url:`https://hyperevmscan.io`}},rpcUrls:{default:{http:[`https://rpc.hyperliquid.xyz/evm`]}},testnet:!1}),cT=L({id:998,name:`Hyperliquid EVM Testnet`,nativeCurrency:{name:`HYPE`,symbol:`HYPE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hyperliquid-testnet.xyz/evm`]}},testnet:!0}),lT=L({id:73115,name:`ICB Network`,nativeCurrency:{decimals:18,name:`ICB Native Token`,symbol:`ICBX`},rpcUrls:{default:{http:[`https://rpc1-mainnet.icbnetwork.info`]}},blockExplorers:{default:{name:`ICB Explorer`,url:`https://icbscan.io`,apiUrl:`https://icbscan.io/api`}},testnet:!1}),uT=L({id:74,name:`IDChain Mainnet`,nativeCurrency:{decimals:18,name:`EIDI`,symbol:`EIDI`},rpcUrls:{default:{http:[`https://idchain.one/rpc`],webSocket:[`wss://idchain.one/ws`]}},blockExplorers:{default:{name:`IDChain Explorer`,url:`https://explorer.idchain.one`}},testnet:!1}),dT=L({id:13371,name:`Immutable zkEVM`,nativeCurrency:{decimals:18,name:`Immutable Coin`,symbol:`IMX`},rpcUrls:{default:{http:[`https://rpc.immutable.com`]}},blockExplorers:{default:{name:`Immutable Explorer`,url:`https://explorer.immutable.com`,apiUrl:`https://explorer.immutable.com/api`}},contracts:{multicall3:{address:`0x236bdA4589e44e6850f5aC6a74BfCa398a86c6c0`,blockCreated:4335972}}}),fT=L({id:13473,name:`Immutable zkEVM Testnet`,nativeCurrency:{decimals:18,name:`Immutable Coin`,symbol:`IMX`},rpcUrls:{default:{http:[`https://rpc.testnet.immutable.com`]}},blockExplorers:{default:{name:`Immutable Testnet Explorer`,url:`https://explorer.testnet.immutable.com/`}},contracts:{multicall3:{address:`0x2CC787Ed364600B0222361C4188308Fa8E68bA60`,blockCreated:5977391}},testnet:!0}),pT=L({id:2525,name:`inEVM Mainnet`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://mainnet.rpc.inevm.com/http`]}},blockExplorers:{default:{name:`inEVM Explorer`,url:`https://inevm.calderaexplorer.xyz`,apiUrl:`https://inevm.calderaexplorer.xyz/api/v2`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:118606}}}),mT=L({id:7233,name:`InitVerse Mainnet`,nativeCurrency:{decimals:18,name:`InitVerse`,symbol:`INI`},rpcUrls:{default:{http:[`https://rpc-mainnet.inichain.com`]}},blockExplorers:{default:{name:`InitVerseScan`,url:`https://www.iniscan.com`,apiUrl:`https://explorer-api.inichain.com/api`}},contracts:{multicall3:{address:`0x83466BE48A067115FFF91f7b892Ed1726d032e47`,blockCreated:2318}}}),hT=L({id:7234,name:`InitVerse Genesis Testnet`,nativeCurrency:{decimals:18,name:`InitVerse`,symbol:`INI`},rpcUrls:{default:{http:[`https://rpc-testnet.inichain.com`]}},blockExplorers:{default:{name:`InitVerseGenesisScan`,url:`https://genesis-testnet.iniscan.com`,apiUrl:`https://explorer-testnet-api.inichain.com/api`}},contracts:{multicall3:{address:`0x0cF32CBDd6c437331EA4f85ed2d881A5379B5a6F`,blockCreated:16361}},testnet:!0}),gT=L({id:1776,name:`Injective`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://sentry.evm-rpc.injective.network`],webSocket:[`wss://sentry.evm-ws.injective.network`]}},blockExplorers:{default:{name:`Injective Explorer`,url:`https://blockscout.injective.network`,apiUrl:`https://blockscout.injective.network/api`}},testnet:!1}),_T=L({id:1439,name:`Injective Testnet`,nativeCurrency:{decimals:18,name:`Injective`,symbol:`INJ`},rpcUrls:{default:{http:[`https://k8s.testnet.json-rpc.injective.network`],webSocket:[`wss://k8s.testnet.ws.injective.network`]}},blockExplorers:{default:{name:`Injective Explorer`,url:`https://testnet.blockscout.injective.network`,apiUrl:`https://testnet.blockscout.injective.network/api`}},testnet:!0}),vT=1,yT=L({...R,id:57073,name:`Ink`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-gel.inkonchain.com`,`https://rpc-qnd.inkonchain.com`],webSocket:[`wss://rpc-gel.inkonchain.com`,`wss://rpc-qnd.inkonchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.inkonchain.com`,apiUrl:`https://explorer.inkonchain.com/api/v2`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},...R.contracts,disputeGameFactory:{[vT]:{address:`0x10d7b35078d3baabb96dd45a9143b94be65b12cd`}},portal:{[vT]:{address:`0x5d66c1782664115999c47c9fa5cd031f495d3e4f`}},l1StandardBridge:{[vT]:{address:`0x88ff1e5b602916615391f55854588efcbb7663f0`}}},testnet:!1,sourceId:vT}),bT=11155111,xT=L({...R,id:763373,name:`Ink Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-gel-sepolia.inkonchain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer-sepolia.inkonchain.com/`,apiUrl:`https://explorer-sepolia.inkonchain.com/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},disputeGameFactory:{[bT]:{address:`0x860e626c700af381133d9f4af31412a2d1db3d5d`}},portal:{[bT]:{address:`0x5c1d29c6c9c8b0800692acc95d700bcb4966a1d7`}},l1StandardBridge:{[bT]:{address:`0x33f60714bbd74d62b66d79213c348614de51901c`}}},testnet:!0,sourceId:bT}),ST=L({id:8822,name:`IOTA EVM`,network:`iotaevm`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://json-rpc.evm.iotaledger.net`],webSocket:[`wss://ws.json-rpc.evm.iotaledger.net`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.evm.iota.org`,apiUrl:`https://explorer.evm.iota.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:25022}}}),CT=L({id:1075,name:`IOTA EVM Testnet`,network:`iotaevm-testnet`,nativeCurrency:{decimals:18,name:`IOTA`,symbol:`IOTA`},rpcUrls:{default:{http:[`https://json-rpc.evm.testnet.iotaledger.net`],webSocket:[`wss://ws.json-rpc.evm.testnet.iotaledger.net`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.evm.testnet.iotaledger.net`,apiUrl:`https://explorer.evm.testnet.iotaledger.net/api`}},testnet:!0}),wT=L({id:4689,name:`IoTeX`,nativeCurrency:{decimals:18,name:`IoTeX`,symbol:`IOTX`},rpcUrls:{default:{http:[`https://babel-api.mainnet.iotex.io`],webSocket:[`wss://babel-api.mainnet.iotex.io`]}},blockExplorers:{default:{name:`IoTeXScan`,url:`https://iotexscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:22163670}}}),TT=L({id:4690,name:`IoTeX Testnet`,nativeCurrency:{decimals:18,name:`IoTeX`,symbol:`IOTX`},rpcUrls:{default:{http:[`https://babel-api.testnet.iotex.io`],webSocket:[`wss://babel-api.testnet.iotex.io`]}},blockExplorers:{default:{name:`IoTeXScan`,url:`https://testnet.iotexscan.io`}},contracts:{multicall3:{address:`0xb5cecD6894c6f473Ec726A176f1512399A2e355d`,blockCreated:24347592}},testnet:!0}),ET=L({id:8017,name:`iSunCoin Mainnet`,nativeCurrency:{decimals:18,name:`ISC`,symbol:`ISC`},rpcUrls:{default:{http:[`https://mainnet.isuncoin.com`]}},blockExplorers:{default:{name:`iSunCoin Explorer`,url:`https://baifa.io/app/chains/8017`}}}),DT=L({id:680,name:`Jasmy Chain`,network:`jasmyChain`,nativeCurrency:{name:`JasmyCoin`,symbol:`JASMY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.jasmychain.io`],webSocket:[`wss://rpc.jasmychain.io`]}},testnet:!1}),OT=L({id:681,name:`Jasmy Chain Testnet`,network:`jasmyChainTestnet`,nativeCurrency:{name:`JasmyCoin`,symbol:`JASMY`,decimals:18},rpcUrls:{default:{http:[`https://rpc_testnet.jasmychain.io`],webSocket:[`wss://rpc_testnet.jasmychain.io`]}},testnet:!0}),kT=L({id:8899,name:`JB Chain`,network:`jbc`,nativeCurrency:{name:`JBC`,symbol:`JBC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-l1.jibchain.net`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp-l1.jibchain.net`,apiUrl:`https://exp-l1.jibchain.net/api`}},contracts:{multicall3:{address:`0xc0C8C486D1466C57Efe13C2bf000d4c56F47CBdC`,blockCreated:2299048}},testnet:!1}),AT=L({id:88991,name:`Jibchain Testnet`,nativeCurrency:{name:`tJBC`,symbol:`tJBC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.jibchain.net`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp.testnet.jibchain.net`,apiUrl:`https://exp.testnet.jibchain.net/api`}},contracts:{multicall3:{address:`0xa1a858ad9041B4741e620355a3F96B3c78e70ecE`,blockCreated:32848}},testnet:!0}),jT=L({id:81,name:`Japan Open Chain Mainnet`,nativeCurrency:{decimals:18,name:`Japan Open Chain Token`,symbol:`JOC`},rpcUrls:{default:{http:[`https://rpc-1.japanopenchain.org:8545`,`https://rpc-2.japanopenchain.org:8545`,`https://rpc-3.japanopenchain.org`]}},blockExplorers:{default:{name:`Block Explorer`,url:`https://explorer.japanopenchain.org`}},testnet:!1}),MT=L({id:10081,name:`Japan Open Chain Testnet`,nativeCurrency:{decimals:18,name:`Japan Open Chain Testnet Token`,symbol:`JOCT`},rpcUrls:{default:{http:[`https://rpc-1.testnet.japanopenchain.org:8545`,`https://rpc-2.testnet.japanopenchain.org:8545`,`https://rpc-3.testnet.japanopenchain.org`]}},blockExplorers:{default:{name:`Testnet Block Explorer`,url:`https://explorer.testnet.japanopenchain.org`}},testnet:!0}),NT=L({id:5734951,name:`Jovay Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.jovay.io`]}},blockExplorers:{default:{name:`Jovay Explorer`,url:`https://explorer.jovay.io/l2`}},testnet:!1}),PT=L({id:2019775,name:`Jovay Sepolia Testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://api.zan.top/public/jovay-testnet`]}},blockExplorers:{default:{name:`Jovay Testnet Explorer`,url:`https://sepolia-explorer.jovay.io/l2`}},testnet:!0}),FT=L({id:45003,name:`Juneo JUNE-Chain`,nativeCurrency:{decimals:18,name:`JUNE-Chain`,symbol:`JUNE`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/JUNE/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/2`,apiUrl:`https://juneoscan.io/chain/2/api`}}}),IT=L({id:45013,name:`Juneo BCH1-Chain`,nativeCurrency:{decimals:18,name:`Juneo BCH1-Chain`,symbol:`BCH1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/BCH1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/12`,apiUrl:`https://juneoscan.io/chain/12/api`}}}),LT=L({id:45004,name:`Juneo DAI1-Chain`,nativeCurrency:{decimals:18,name:`Juneo DAI1-Chain`,symbol:`DAI1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/DAI1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/5`,apiUrl:`https://juneoscan.io/chain/5/api`}}}),RT=L({id:45010,name:`Juneo DOGE1-Chain`,nativeCurrency:{decimals:18,name:`Juneo DOGE1-Chain`,symbol:`DOGE1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/DOGE1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/10`,apiUrl:`https://juneoscan.io/chain/10/api`}}}),zT=L({id:45011,name:`Juneo EUR1-Chain`,nativeCurrency:{decimals:18,name:`Juneo EUR1-Chain`,symbol:`EUR1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/EUR1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/6`,apiUrl:`https://juneoscan.io/chain/6/api`}}}),BT=L({id:45008,name:`Juneo GLD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo GLD1-Chain`,symbol:`GLD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/GLD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/8`,apiUrl:`https://juneoscan.io/chain/8/api`}}}),VT=L({id:45014,name:`Juneo LINK1-Chain`,nativeCurrency:{decimals:18,name:`Juneo LINK1-Chain`,symbol:`LINK1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/LINK1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/13`,apiUrl:`https://juneoscan.io/chain/13/api`}}}),HT=L({id:45009,name:`Juneo LTC1-Chain`,nativeCurrency:{decimals:18,name:`Juneo LTC1-Chain`,symbol:`LTC1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/LTC1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/11`,apiUrl:`https://juneoscan.io/chain/11/api`}}}),UT=L({id:45007,name:`Juneo mBTC1-Chain`,nativeCurrency:{decimals:18,name:`Juneo mBTC1-Chain`,symbol:`mBTC1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/mBTC1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/9`,apiUrl:`https://juneoscan.io/chain/9/api`}}}),WT=L({id:45012,name:`Juneo SGD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo SGD1-Chain`,symbol:`SGD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/SGD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/7`,apiUrl:`https://juneoscan.io/chain/7/api`}}}),GT=L({id:101003,name:`Socotra JUNE-Chain`,nativeCurrency:{decimals:18,name:`Socotra JUNE-Chain`,symbol:`JUNE`},rpcUrls:{default:{http:[`https://rpc.socotra-testnet.network/ext/bc/JUNE/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://socotra.juneoscan.io/chain/2`,apiUrl:`https://socotra.juneoscan.io/chain/2/api`}},testnet:!0}),KT=L({id:45006,name:`Juneo USD1-Chain`,nativeCurrency:{decimals:18,name:`Juneo USD1-Chain`,symbol:`USD1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/USD1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/4`,apiUrl:`https://juneoscan.io/chain/4/api`}}}),qT=L({id:45005,name:`Juneo USDT1-Chain`,nativeCurrency:{decimals:18,name:`Juneo USDT1-Chain`,symbol:`USDT1`},rpcUrls:{default:{http:[`https://rpc.juneo-mainnet.network/ext/bc/USDT1/rpc`]}},blockExplorers:{default:{name:`Juneo Scan`,url:`https://juneoscan.io/chain/3`,apiUrl:`https://juneoscan.io/chain/3/api`}}}),JT=L({id:8217,name:`Kaia`,nativeCurrency:{decimals:18,name:`Kaia`,symbol:`KAIA`},rpcUrls:{default:{http:[`https://public-en.node.kaia.io`]}},blockExplorers:{default:{name:`KaiaScan`,url:`https://kaiascan.io`,apiUrl:`https://api-cypress.klaytnscope.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96002415}}}),YT=L({id:1001,name:`Kairos Testnet`,network:`kairos`,nativeCurrency:{decimals:18,name:`Kairos KAIA`,symbol:`KAIA`},rpcUrls:{default:{http:[`https://public-en-kairos.node.kaia.io`]}},blockExplorers:{default:{name:`KaiaScan`,url:`https://kairos.kaiascan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:123390593}},testnet:!0}),XT=L({id:1802203764,name:`Kakarot Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.kakarot.org`]}},blockExplorers:{default:{name:`Kakarot Scan`,url:`https://sepolia.kakarotscan.org`}},testnet:!0}),ZT=L({id:920637907288165,name:`Kakarot Starknet Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.kakarot.org`]}},blockExplorers:{default:{name:`Kakarot Scan`,url:`https://sepolia.kakarotscan.org`}},testnet:!0}),QT=L({id:24,name:`KardiaChain Mainnet`,nativeCurrency:{name:`KAI`,symbol:`KAI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.kardiachain.io`]}},blockExplorers:{default:{name:`KardiaChain Explorer`,url:`https://explorer.kardiachain.io`}},testnet:!1}),$T=L({id:686,name:`Karura`,network:`karura`,nativeCurrency:{name:`Karura`,symbol:`KAR`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-karura.aca-api.network`],webSocket:[`wss://eth-rpc-karura.aca-api.network`]}},blockExplorers:{default:{name:`Karura Blockscout`,url:`https://blockscout.karura.network`,apiUrl:`https://blockscout.karura.network/api`}},testnet:!1}),eE=L({id:747474,name:`Katana`,network:`katana`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.katana.network`]}},blockExplorers:{default:{name:`katana explorer`,url:`https://katanascan.com`}},testnet:!1}),tE=L({id:2222,name:`Kava EVM`,network:`kava-mainnet`,nativeCurrency:{name:`Kava`,symbol:`KAVA`,decimals:18},rpcUrls:{default:{http:[`https://evm.kava.io`]}},blockExplorers:{default:{name:`Kava EVM Explorer`,url:`https://kavascan.com`,apiUrl:`https://kavascan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3661165}},testnet:!1}),nE=L({id:2221,name:`Kava EVM Testnet`,network:`kava-testnet`,nativeCurrency:{name:`Kava`,symbol:`KAVA`,decimals:18},rpcUrls:{default:{http:[`https://evm.testnet.kava.io`]}},blockExplorers:{default:{name:`Kava EVM Testnet Explorer`,url:`https://testnet.kavascan.com/`,apiUrl:`https://testnet.kavascan.com/api`}},contracts:{multicall3:{address:`0xDf1D724A7166261eEB015418fe8c7679BBEa7fd6`,blockCreated:7242179}},testnet:!0}),rE=L({id:321,name:`KCC Mainnet`,network:`KCC Mainnet`,nativeCurrency:{decimals:18,name:`KCS`,symbol:`KCS`},rpcUrls:{default:{http:[`https://kcc-rpc.com`]}},blockExplorers:{default:{name:`KCC Explorer`,url:`https://explorer.kcc.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11760430}},testnet:!1}),iE=L({id:1783,name:`KiiChain`,network:`kii-chain`,nativeCurrency:{name:`Kii`,symbol:`KII`,decimals:18},rpcUrls:{default:{http:[`https://json-rpc.kiivalidator.com`]}},blockExplorers:{default:{name:`KiiExplorer`,url:`https://explorer.kiichain.io`}}}),aE=L({id:1336,name:`Kii Testnet Oro`,network:`kii-testnet-oro`,nativeCurrency:{name:`Kii`,symbol:`KII`,decimals:18},rpcUrls:{default:{http:[`https://json-rpc.uno.sentry.testnet.v3.kiivalidator.com`]}},blockExplorers:{default:{name:`KiiExplorer`,url:`https://testnet.explorer.kiichain.io`}},testnet:!0}),oE=L({id:7887,name:`Kinto Mainnet`,network:`Kinto Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.kinto.xyz/http`]}},blockExplorers:{default:{name:`Kinto Explorer`,url:`https://explorer.kinto.xyz`}},testnet:!1}),sE=L({id:8217,name:`Klaytn`,nativeCurrency:{decimals:18,name:`Klaytn`,symbol:`KLAY`},rpcUrls:{default:{http:[`https://public-en-cypress.klaytn.net`]}},blockExplorers:{default:{name:`KlaytnScope`,url:`https://scope.klaytn.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:96002415}}}),cE=L({id:1001,name:`Klaytn Baobab Testnet`,network:`klaytn-baobab`,nativeCurrency:{decimals:18,name:`Baobab Klaytn`,symbol:`KLAY`},rpcUrls:{default:{http:[`https://public-en-baobab.klaytn.net`]}},blockExplorers:{default:{name:`KlaytnScope`,url:`https://baobab.klaytnscope.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:123390593}},testnet:!0}),lE=L({id:701,name:`Koi Network`,nativeCurrency:{decimals:18,name:`Koi Network Native Token`,symbol:`KRING`},rpcUrls:{default:{http:[`https://koi-rpc.darwinia.network`],webSocket:[`wss://koi-rpc.darwinia.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://koi-scan.darwinia.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:180001}},testnet:!0}),uE=L({id:255,name:`Kroma`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://api.kroma.network`]}},blockExplorers:{default:{name:`Kroma Explorer`,url:`https://blockscout.kroma.network`,apiUrl:`https://blockscout.kroma.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:16054868}},testnet:!1}),dE=L({id:2358,name:`Kroma Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://api.sepolia.kroma.network`]}},blockExplorers:{default:{name:`Kroma Sepolia Explorer`,url:`https://blockscout.sepolia.kroma.network`,apiUrl:`https://blockscout.sepolia.kroma.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:8900914}},testnet:!0}),fE=L({id:1983,name:`Krown`,nativeCurrency:{decimals:18,name:`Krown`,symbol:`KROWN`},rpcUrls:{default:{http:[`https://mainnet.krown.network`]}},blockExplorers:{default:{name:`Krown Explorer`,url:`https://explorer.krown.network`}},testnet:!1}),pE=L({id:12324,name:`L3X Protocol`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.l3x.com`],webSocket:[`wss://rpc-mainnet.l3x.com`]}},blockExplorers:{default:{name:`L3X Mainnet Explorer`,url:`https://explorer.l3x.com`,apiUrl:`https://explorer.l3x.com/api/v2`}},testnet:!1}),mE=L({id:12325,name:`L3X Protocol Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.l3x.com`],webSocket:[`wss://rpc-testnet.l3x.com`]}},blockExplorers:{default:{name:`L3X Testnet Explorer`,url:`https://explorer-testnet.l3x.com`,apiUrl:`https://explorer-testnet.l3x.com/api/v2`}},testnet:!0}),hE=L({id:360890,name:`LAVITA Mainnet`,nativeCurrency:{name:`vTFUEL`,symbol:`vTFUEL`,decimals:18},rpcUrls:{default:{http:[`https://tsub360890-eth-rpc.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`LAVITA Explorer`,url:`https://tsub360890-explorer.thetatoken.org`}},testnet:!1}),gE=L({id:232,name:`Lens`,nativeCurrency:{name:`GHO`,symbol:`GHO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.lens.xyz`]}},blockExplorers:{default:{name:`Lens Block Explorer`,url:`https://explorer.lens.xyz`,apiUrl:`https://explorer.lens.xyz/api`}}}),_E=L({id:37111,name:`Lens Testnet`,nativeCurrency:{name:`GRASS`,symbol:`GRASS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.lens.dev`],webSocket:[`wss://rpc.testnet.lens.dev/ws`]}},blockExplorers:{default:{name:`Lens Block Explorer`,url:`https://block-explorer.testnet.lens.dev`,apiUrl:`https://block-explorer-api.staging.lens.dev/api`}},testnet:!0}),vE=L({id:21363,name:`Lestnet`,nativeCurrency:{name:`Lestnet Ether`,symbol:`LETH`,decimals:18},rpcUrls:{default:{http:[`https://service.lestnet.org`]}},blockExplorers:{default:{name:`Lestnet Explorer`,url:`https://explore.lestnet.org`}},testnet:!0}),yE=L({id:1891,name:`LightLink Pegasus Testnet`,network:`lightlink-pegasus`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://replicator.pegasus.lightlink.io/rpc/v1`]}},blockExplorers:{default:{name:`LightLink Pegasus Explorer`,url:`https://pegasus.lightlink.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:127188532}},testnet:!0}),bE=L({id:1890,name:`LightLink Phoenix Mainnet`,network:`lightlink-phoenix`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://replicator.phoenix.lightlink.io/rpc/v1`]}},blockExplorers:{default:{name:`LightLink Phoenix Explorer`,url:`https://phoenix.lightlink.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:125499184}},testnet:!1});oa(),k(),Hp(),zu(),Ku(),td();async function xE(e,t){let{account:n=e.account}=t;if(!n)throw new Fm;let r=aa(n);try{let{accessList:n,blockNumber:i,blockTag:a,data:o,gas:s,gasPrice:c,maxFeePerGas:l,maxPriorityFeePerGas:u,nonce:d,to:f,value:p,...m}=t,h=(typeof i==`bigint`?O(i):void 0)||a;ed(t);let g=e.chain?.formatters?.transactionRequest?.format,_=(g||Hu)({...Ru(m,{format:g}),account:r,accessList:n,data:o,gas:s,gasPrice:c,maxFeePerGas:l,maxPriorityFeePerGas:u,nonce:d,to:f,value:p},`estimateGas`),{baseFeePerGas:v,gasLimit:y,priorityFeePerGas:b}=await e.request({method:`linea_estimateGas`,params:h?[_,h]:[_]});return{baseFeePerGas:BigInt(v),gasLimit:BigInt(y),priorityFeePerGas:BigInt(b)}}catch(n){throw Vp(n,{...t,account:r,chain:e.chain})}}var SE={fees:{estimateFeesPerGas:CE,async maxPriorityFeePerGas({block:e,client:t,request:n}){let r=await CE({block:e,client:t,multiply:e=>e,request:n,type:`eip1559`});return r?.maxPriorityFeePerGas?r.maxPriorityFeePerGas:null}}};async function CE({client:e,multiply:t,request:n,type:r}){try{let i=await xE(e,{...n,account:n?.account}),{priorityFeePerGas:a}=i,o=t(BigInt(i.baseFeePerGas))+a;return r===`legacy`?{gasPrice:o}:{maxFeePerGas:o,maxPriorityFeePerGas:a}}catch{return null}}var wE=L({...SE,id:59144,name:`Linea Mainnet`,blockTime:2e3,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.linea.build`],webSocket:[`wss://rpc.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://lineascan.build`,apiUrl:`https://api.lineascan.build/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:42},ensRegistry:{address:`0x50130b669B28C339991d8676FA73CF122a121267`,blockCreated:6682888},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:22222151}},ensTlds:[`.linea.eth`],testnet:!1}),TE=L({id:59140,name:`Linea Goerli Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.goerli.linea.build`],webSocket:[`wss://rpc.goerli.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.lineascan.build`,apiUrl:`https://api-goerli.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:498623}},testnet:!0}),EE=L({...SE,id:59141,name:`Linea Sepolia Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sepolia.linea.build`],webSocket:[`wss://rpc.sepolia.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.lineascan.build`,apiUrl:`https://api-sepolia.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:227427},ensRegistry:{address:`0x5B2636F0f2137B4aE722C01dd5122D7d3e9541f7`,blockCreated:2395094},ensUniversalResolver:{address:`0x4D41762915F83c76EcaF6776d9b08076aA32b492`,blockCreated:17168484}},ensTlds:[`.linea.eth`],testnet:!0}),DE=L({id:59140,name:`Linea Goerli Testnet`,nativeCurrency:{name:`Linea Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.goerli.linea.build`],webSocket:[`wss://rpc.goerli.linea.build`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli.lineascan.build`,apiUrl:`https://goerli.lineascan.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:498623}},testnet:!0}),OE=1,kE=L({...R,id:1135,name:`Lisk`,network:`lisk`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.api.lisk.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout.lisk.com`,apiUrl:`https://blockscout.lisk.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[OE]:{address:`0x0CF7D3706a27CCE2017aEB11E8a9c8b5388c282C`}},multicall3:{address:`0xA9d71E1dd7ca26F26e656E66d6AA81ed7f745bf0`},l2OutputOracle:{[OE]:{address:`0x113cB99283AF242Da0A0C54347667edF531Aa7d6`}},portal:{[OE]:{address:`0x26dB93F8b8b4f7016240af62F7730979d353f9A7`}},l1StandardBridge:{[OE]:{address:`0x2658723Bf70c7667De6B25F99fcce13A16D25d08`}}},sourceId:OE}),AE=11155111,jE=L({...R,id:4202,network:`lisk-sepolia`,name:`Lisk Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sepolia-api.lisk.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia-blockscout.lisk.com`,apiUrl:`https://sepolia-blockscout.lisk.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[AE]:{address:`0xA0E35F56C318DE1bD5D9ca6A94Fe7e37C5663348`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[AE]:{address:`0xe3d90F21490686Ec7eF37BE788E02dfC12787264`}},l1StandardBridge:{[AE]:{address:`0x1Fb30e446eA791cd1f011675E5F3f5311b70faF5`}}},testnet:!0,sourceId:AE}),ME=L({id:9496,name:`Load Alphanet`,nativeCurrency:{name:`Testnet LOAD`,symbol:`tLOAD`,decimals:18},rpcUrls:{default:{http:[`https://alphanet.load.network`]}},blockExplorers:{default:{name:`Load Alphanet Explorer`,url:`https://explorer.load.network`}},testnet:!0}),NE=L({id:1337,name:`Localhost`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`http://127.0.0.1:8545`]}}}),PE=L({id:15551,name:`LoopNetwork Mainnet`,nativeCurrency:{name:`LOOP`,symbol:`LOOP`,decimals:18},rpcUrls:{default:{http:[`https://api.mainnetloop.com`]}},blockExplorers:{default:{name:`LoopNetwork Blockchain Explorer`,url:`https://explorer.mainnetloop.com/`}},testnet:!1}),FE=L({id:42,network:`lukso`,name:`LUKSO`,nativeCurrency:{name:`LUKSO`,symbol:`LYX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.lukso.network`],webSocket:[`wss://ws-rpc.mainnet.lukso.network`]}},blockExplorers:{default:{name:`LUKSO Mainnet Explorer`,url:`https://explorer.execution.mainnet.lukso.network`,apiUrl:`https://api.explorer.execution.mainnet.lukso.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:468183}}}),IE=L({id:4201,name:`LUKSO Testnet`,nativeCurrency:{decimals:18,name:`LUKSO Testnet`,symbol:`LYXt`},rpcUrls:{default:{http:[`https://rpc.testnet.lukso.network`],webSocket:[`wss://ws-rpc.testnet.lukso.network`]}},blockExplorers:{default:{name:`LUKSO Testnet Explorer`,url:`https://explorer.execution.testnet.lukso.network`,apiUrl:`https://api.explorer.execution.testnet.lukso.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:605348}},testnet:!0}),LE=L({id:994873017,name:`Lumia Mainnet`,network:`LumiaMainnet`,nativeCurrency:{name:`Lumia`,symbol:`LUMIA`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.lumia.org`]}},blockExplorers:{default:{name:`Lumia Explorer`,url:`https://explorer.lumia.org/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3975939}},testnet:!1}),RE=L({id:1952959480,name:`Lumia Testnet`,network:`LumiaTestnet`,nativeCurrency:{name:`Lumia`,symbol:`LUMIA`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.lumia.org`]}},blockExplorers:{default:{name:`Lumia Testnet Explorer`,url:`https://testnet-explorer.lumia.org/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2235063}},testnet:!0}),zE=L({id:96370,name:`Lumoz`,nativeCurrency:{decimals:18,name:`Lumoz Token`,symbol:`MOZ`},rpcUrls:{default:{http:[`https://rpc.lumoz.org`]}},blockExplorers:{default:{name:`Lumoz Scan`,url:`https://scan.lumoz.info`}},testnet:!1}),BE=L({id:105363,name:`Lumoz Testnet`,nativeCurrency:{decimals:18,name:`Lumoz Testnet Token`,symbol:`MOZ`},rpcUrls:{default:{http:[`https://testnet-rpc.lumoz.org`]}},testnet:!0}),VE=L({id:1122,name:`LuxePorts`,network:`luxeports`,nativeCurrency:{name:`LuxePorts`,symbol:`LXP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.luxeports.com`,`https://erpc.luxeports.com`],webSocket:[`wss://rpc.luxeports.com/ws`,`wss://erpc.luxeports.com/ws`]}},blockExplorers:{default:{name:`LXPScan`,url:`https://lxpscan.com`}},testnet:!1}),HE=L({id:721,name:`Lycan`,nativeCurrency:{decimals:18,name:`Lycan`,symbol:`LYC`},rpcUrls:{default:{http:[`https://rpc.lycanchain.com`,`https://us-east.lycanchain.com`,`https://us-west.lycanchain.com`,`https://eu-north.lycanchain.com`,`https://eu-west.lycanchain.com`,`https://asia-southeast.lycanchain.com`],webSocket:[`wss://rpc.lycanchain.com`,`wss://us-east.lycanchain.com`,`wss://us-west.lycanchain.com`,`wss://eu-north.lycanchain.com`,`wss://eu-west.lycanchain.com`,`wss://asia-southeast.lycanchain.com`]}},blockExplorers:{default:{name:`Lycan Explorer`,url:`https://explorer.lycanchain.com`}}}),UE=L({id:957,name:`Lyra Chain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.lyra.finance`]}},blockExplorers:{default:{name:`Lyra Explorer`,url:`https://explorer.lyra.finance`,apiUrl:`https://explorer.lyra.finance/api/v2`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1935198}}}),WE=L({id:1,name:`Ethereum`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:12e3,rpcUrls:{default:{http:[`https://eth.merkle.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://etherscan.io`,apiUrl:`https://api.etherscan.io/api`}},contracts:{ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:23085558},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),GE=L({id:595,name:`Mandala TC9`,network:`mandala`,nativeCurrency:{name:`Mandala`,symbol:`mACA`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-tc9.aca-staging.network`],webSocket:[`wss://eth-rpc-tc9.aca-staging.network`]}},blockExplorers:{default:{name:`Mandala Blockscout`,url:`https://blockscout.mandala.aca-staging.network`,apiUrl:`https://blockscout.mandala.aca-staging.network/api`}},testnet:!0}),KE=L({id:169,name:`Manta Pacific Mainnet`,network:`manta`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://pacific-rpc.manta.network/http`]}},blockExplorers:{default:{name:`Manta Explorer`,url:`https://pacific-explorer.manta.network`,apiUrl:`https://pacific-explorer.manta.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:332890}}}),qE=L({id:3441006,name:`Manta Pacific Sepolia Testnet`,network:`manta-sepolia`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://pacific-rpc.sepolia-testnet.manta.network/http`]}},blockExplorers:{default:{name:`Manta Sepolia Testnet Explorer`,url:`https://pacific-explorer.sepolia-testnet.manta.network`,apiUrl:`https://pacific-explorer.sepolia-testnet.manta.network/api`}},contracts:{multicall3:{address:`0xca54918f7B525C8df894668846506767412b53E3`,blockCreated:479584}},testnet:!0}),JE=L({id:3441005,name:`Manta Pacific Testnet`,network:`manta-testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://manta-testnet.calderachain.xyz/http`]}},blockExplorers:{default:{name:`Manta Testnet Explorer`,url:`https://pacific-explorer.testnet.manta.network`,apiUrl:`https://pacific-explorer.testnet.manta.network/api`}},contracts:{multicall3:{address:`0x211B1643b95Fe76f11eD8880EE810ABD9A4cf56C`,blockCreated:419915}},testnet:!0}),YE=L({id:5e3,name:`Mantle`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Explorer`,url:`https://mantlescan.xyz/`,apiUrl:`https://api.mantlescan.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:304717}}}),XE=L({id:5003,name:`Mantle Sepolia Testnet`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.sepolia.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Testnet Explorer`,url:`https://explorer.sepolia.mantle.xyz/`,apiUrl:`https://explorer.sepolia.mantle.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4584012}},testnet:!0}),ZE=L({id:5001,name:`Mantle Testnet`,nativeCurrency:{decimals:18,name:`MNT`,symbol:`MNT`},rpcUrls:{default:{http:[`https://rpc.testnet.mantle.xyz`]}},blockExplorers:{default:{name:`Mantle Testnet Explorer`,url:`https://explorer.testnet.mantle.xyz`,apiUrl:`https://explorer.testnet.mantle.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:561333}},testnet:!0}),QE=L({id:5887,name:`MANTRA DuKong EVM Testnet`,nativeCurrency:{decimals:18,name:`MANTRA`,symbol:`MANTRA`},rpcUrls:{default:{http:[`https://evm.dukong.mantrachain.io`]}},blockExplorers:{default:{name:`MANTRAScan`,url:`https://mantrascan.io/dukong`}},testnet:!0}),$E=L({id:5888,name:`MANTRA EVM`,nativeCurrency:{decimals:18,name:`MANTRA`,symbol:`MANTRA`},rpcUrls:{default:{http:[`https://evm.mantrachain.io`],webSocket:[`https://evm.mantrachain.io/ws`]}},blockExplorers:{default:{name:`MANTRA Blockscout Explorer`,url:`https://blockscout.mantrascan.io`}}}),eD=L({id:22776,name:`MAP Protocol`,nativeCurrency:{decimals:18,name:`MAPO`,symbol:`MAPO`},rpcUrls:{default:{http:[`https://rpc.maplabs.io`]}},blockExplorers:{default:{name:`MAPO Scan`,url:`https://maposcan.io`}},testnet:!1}),tD=L({id:698,name:`Matchain`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.matchain.io`]}},blockExplorers:{default:{name:`Matchain Scan`,url:`https://matchscan.io`}}}),nD=L({id:699,name:`Matchain Testnet`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.matchain.io`]}},blockExplorers:{default:{name:`Matchain Scan`,url:`https://testnet.matchscan.io`}},testnet:!0}),rD=L({id:29548,name:`MCH Verse`,nativeCurrency:{name:`Oasys`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.oasys.mycryptoheroes.net`]}},blockExplorers:{default:{name:`MCH Verse Explorer`,url:`https://explorer.oasys.mycryptoheroes.net`,apiUrl:`https://explorer.oasys.mycryptoheroes.net/api`}},testnet:!1}),iD=L({id:4326,blockTime:1e3,name:`MegaETH`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.megaeth.com/rpc`],webSocket:[`wss://mainnet.megaeth.com/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://mega.etherscan.io`,apiUrl:`https://api.etherscan.io/v2/api`},blockscout:{name:`Etherscan`,url:`https://mega.etherscan.io`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),aD=L({id:6343,blockTime:1e3,name:`MegaETH Testnet`,nativeCurrency:{name:`MegaETH Testnet Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://carrot.megaeth.com/rpc`],webSocket:[`wss://carrot.megaeth.com/ws`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://testnet-mega.etherscan.io`,apiUrl:`https://api.etherscan.io/v2/api`},blockscout:{name:`Blockscout`,url:`https://megaeth-testnet-v2.blockscout.com`,apiUrl:`https://megaeth-testnet-v2.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),oD=L({id:7078815900,name:`Mekong Pectra Devnet`,nativeCurrency:{name:`eth`,symbol:`eth`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mekong.ethpandaops.io`]}},blockExplorers:{default:{name:`Block Explorer`,url:`https://explorer.mekong.ethpandaops.io`}},testnet:!0}),sD=L({id:333000333,name:`Meld`,nativeCurrency:{decimals:18,name:`Meld`,symbol:`MELD`},rpcUrls:{default:{http:[`https://rpc-1.meld.com`]}},blockExplorers:{default:{name:`MELDscan`,url:`https://meldscan.io`}},contracts:{multicall3:{address:`0x769ee5a8e82c15c1b6e358f62ac8eb6e3abe8dc5`,blockCreated:360069}}}),cD=L({id:4352,name:`MemeCore`,nativeCurrency:{decimals:18,name:`M`,symbol:`M`},rpcUrls:{default:{http:[`https://rpc.memecore.net`],webSocket:[`wss://ws.memecore.net`]}},blockExplorers:{default:{name:`MemeCore Explorer`,url:`https://memecorescan.io`,apiUrl:`https://api.memecorescan.io/api`},okx:{name:`MemeCore Explorer`,url:`https://web3.okx.com/explorer/memecore`},memecore:{name:`MemeCore Explorer`,url:`https://blockscout.memecore.com`,apiUrl:`https://blockscout.memecore.com/api`}}}),lD=L({id:43521,name:`Formicarium`,nativeCurrency:{decimals:18,name:`M`,symbol:`M`},rpcUrls:{default:{http:[`https://rpc.formicarium.memecore.net`],webSocket:[`wss://ws.formicarium.memecore.net`]}},blockExplorers:{default:{name:`MemeCore Testnet Explorer`,url:`https://formicarium.memecorescan.io`},okx:{name:`MemeCore Testnet Explorer`,url:`https://web3.okx.com/explorer/formicarium-testnet`},memecore:{name:`MemeCore Testnet Explorer`,url:`https://formicarium.blockscout.memecore.com`,apiUrl:`https://formicarium.blockscout.memecore.com/api`}},testnet:!0}),uD=L({id:4200,name:`Merlin`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.merlinchain.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://scan.merlinchain.io`,apiUrl:`https://scan.merlinchain.io/api`}}}),dD=L({id:4203,name:`Merlin Erigon Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://testnet-erigon-rpc.merlinchain.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet-erigon-scan.merlinchain.io`,apiUrl:`https://testnet-erigon-scan.merlinchain.io/api`}},testnet:!0}),fD=L({id:571,name:`MetaChain Mainnet`,nativeCurrency:{name:`Metatime Coin`,symbol:`MTC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.metatime.com`]}},blockExplorers:{default:{name:`MetaExplorer`,url:`https://explorer.metatime.com`}},contracts:{multicall3:{address:`0x0000000000000000000000000000000000003001`,blockCreated:0}}}),pD=L({id:1453,name:`MetaChain Istanbul`,nativeCurrency:{name:`Metatime Coin`,symbol:`MTC`,decimals:18},rpcUrls:{default:{http:[`https://istanbul-rpc.metachain.dev`]}},blockExplorers:{default:{name:`MetaExplorer`,url:`https://istanbul-explorer.metachain.dev`}},contracts:{multicall3:{address:`0x0000000000000000000000000000000000003001`,blockCreated:0}},testnet:!0}),mD=L({id:11,name:`Metadium Network`,nativeCurrency:{decimals:18,name:`META`,symbol:`META`},rpcUrls:{default:{http:[`https://api.metadium.com/prod`]}},blockExplorers:{default:{name:`Metadium Explorer`,url:`https://explorer.metadium.com`}},testnet:!1}),hD=1,gD=L({...R,id:1750,name:`Metal L2`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.metall2.com`],webSocket:[`wss://rpc.metall2.com`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.metall2.com`,apiUrl:`https://explorer.metall2.com/api`}},contracts:{...R.contracts,l2OutputOracle:{[hD]:{address:`0x3B1F7aDa0Fcc26B13515af752Dd07fB1CAc11426`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0},portal:{[hD]:{address:`0x3F37aBdE2C6b5B2ed6F8045787Df1ED1E3753956`}},l1StandardBridge:{[hD]:{address:`0x6d0f65D59b55B0FEC5d2d15365154DcADC140BF3`}}},sourceId:hD}),_D=L({id:82,name:`Meter`,nativeCurrency:{decimals:18,name:`MTR`,symbol:`MTR`},rpcUrls:{default:{http:[`https://rpc.meter.io`]}},blockExplorers:{default:{name:`MeterScan`,url:`https://scan.meter.io`}}}),vD=L({id:83,name:`Meter Testnet`,nativeCurrency:{decimals:18,name:`MTR`,symbol:`MTR`},rpcUrls:{default:{http:[`https://rpctest.meter.io`]}},blockExplorers:{default:{name:`MeterTestnetScan`,url:`https://scan-warringstakes.meter.io`}}}),yD=L({id:1088,name:`Metis`,nativeCurrency:{decimals:18,name:`Metis`,symbol:`METIS`},rpcUrls:{default:{http:[`https://metis.rpc.hypersync.xyz`,`https://metis-pokt.nodies.app`,`https://api.blockeden.xyz/metis/67nCBdZQSH9z3YqDDjdm`,`https://metis-andromeda.rpc.thirdweb.com`,`https://metis-andromeda.gateway.tenderly.co`,`https://metis.api.onfinality.io/public`,`https://andromeda.metis.io/?owner=1088`,`https://metis-mainnet.public.blastapi.io`],webSocket:[`wss://metis-rpc.publicnode.com`,`wss://metis.drpc.org`]}},blockExplorers:{default:{name:`Metis Explorer`,url:`https://explorer.metis.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/1088/etherscan/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2338552}}}),bD=L({id:599,name:`Metis Goerli`,nativeCurrency:{decimals:18,name:`Metis Goerli`,symbol:`METIS`},rpcUrls:{default:{http:[`https://goerli.gateway.metisdevops.link`]}},blockExplorers:{default:{name:`Metis Goerli Explorer`,url:`https://goerli.explorer.metisdevops.link`,apiUrl:`https://goerli.explorer.metisdevops.link/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1006207}}}),xD=L({id:59902,name:`Metis Sepolia`,nativeCurrency:{decimals:18,name:`Test Metis`,symbol:`tMETIS`},rpcUrls:{default:{http:[`https://sepolia.metisdevops.link`,`https://metis-sepolia-rpc.publicnode.com`,`https://metis-sepolia.gateway.tenderly.co`],webSocket:[`wss://metis-sepolia-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Metis Sepolia Explorer`,url:`https://sepolia-explorer.metisdevops.link`,apiUrl:`https://sepolia-explorer.metisdevops.link/api-docs`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:224185}}}),SD=L({id:7518,name:`MEVerse Chain Mainnet`,nativeCurrency:{decimals:18,name:`MEVerse`,symbol:`MEV`},rpcUrls:{default:{http:[`https://rpc.meversemainnet.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://www.meversescan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:86881340}}}),CD=L({id:4759,name:`MEVerse Chain Testnet`,nativeCurrency:{decimals:18,name:`MEVerse`,symbol:`MEV`},rpcUrls:{default:{http:[`https://rpc.meversetestnet.io`]}},blockExplorers:{default:{name:`Explorer`,url:`https://testnet.meversescan.io/`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:64371115}},testnet:!0}),wD=L({id:185,name:`Mint Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mintchain.io`]}},blockExplorers:{default:{name:`Mintchain explorer`,url:`https://explorer.mintchain.io`}},testnet:!1}),TD=L({id:1686,name:`Mint Sepolia Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.mintchain.io`]}},blockExplorers:{default:{name:`Mintchain Testnet explorer`,url:`https://testnet-explorer.mintchain.io`}},testnet:!0}),ED=L({id:124832,name:`Mitosis Testnet`,nativeCurrency:{name:`MITO`,symbol:`MITO`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.mitosis.org`]}},blockExplorers:{default:{name:`Mitosis testnet explorer`,url:`https://testnet.mitosiscan.xyz`}},testnet:!0}),DD=1,OD=L({...R,id:34443,name:`Mode Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.mode.network`]}},blockExplorers:{default:{name:`Modescan`,url:`https://modescan.io`}},contracts:{...R.contracts,disputeGameFactory:{[DD]:{address:`0x6f13EFadABD9269D6cEAd22b448d434A1f1B433E`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:2465882},l2OutputOracle:{[DD]:{address:`0x4317ba146D4933D889518a3e5E11Fe7a53199b04`}},portal:{[DD]:{address:`0x8B34b14c7c7123459Cf3076b8Cb929BE097d0C07`}},l1StandardBridge:{[DD]:{address:`0x735aDBbE72226BD52e818E7181953f42E3b0FF21`}}},sourceId:DD}),kD=11155111,AD=L({...R,id:919,name:`Mode Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.mode.network`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepolia.explorer.mode.network`,apiUrl:`https://sepolia.explorer.mode.network/api`}},contracts:{...R.contracts,l2OutputOracle:{[kD]:{address:`0x2634BD65ba27AB63811c74A63118ACb312701Bfa`,blockCreated:3778393}},portal:{[kD]:{address:`0x320e1580effF37E008F1C92700d1eBa47c1B23fD`,blockCreated:3778395}},l1StandardBridge:{[kD]:{address:`0xbC5C679879B2965296756CD959C3C739769995E2`,blockCreated:3778392}},multicall3:{address:`0xBAba8373113Fb7a68f195deF18732e01aF8eDfCF`,blockCreated:3019007}},testnet:!0,sourceId:kD}),jD=L({id:143,name:`Monad`,blockTime:400,nativeCurrency:{name:`Monad`,symbol:`MON`,decimals:18},rpcUrls:{default:{http:[`https://rpc.monad.xyz`,`https://rpc1.monad.xyz`],webSocket:[`wss://rpc.monad.xyz`,`wss://rpc1.monad.xyz`]}},blockExplorers:{default:{name:`MonadVision`,url:`https://monadvision.com`},monadscan:{name:`Monadscan`,url:`https://monadscan.com`,apiUrl:`https://api.monadscan.com/api`}},testnet:!1,contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:9248132}}}),MD=L({id:10143,name:`Monad Testnet`,blockTime:400,nativeCurrency:{name:`Testnet MON Token`,symbol:`MON`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.monad.xyz`]}},blockExplorers:{default:{name:`Monad Testnet explorer`,url:`https://testnet.monadexplorer.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:251449}},testnet:!0}),ND=L({id:1287,name:`Moonbase Alpha`,nativeCurrency:{decimals:18,name:`DEV`,symbol:`DEV`},rpcUrls:{default:{http:[`https://rpc.api.moonbase.moonbeam.network`],webSocket:[`wss://wss.api.moonbase.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonbase.moonscan.io`,apiUrl:`https://moonbase.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1850686}},testnet:!0}),PD=L({id:1284,name:`Moonbeam`,nativeCurrency:{decimals:18,name:`GLMR`,symbol:`GLMR`},rpcUrls:{default:{http:[`https://rpc.api.moonbeam.network`],webSocket:[`wss://wss.api.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonscan.io`,apiUrl:`https://api-moonbeam.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:609002}},testnet:!1}),FD=L({id:1281,name:`Moonbeam Development Node`,nativeCurrency:{decimals:18,name:`DEV`,symbol:`DEV`},rpcUrls:{default:{http:[`http://127.0.0.1:9944`],webSocket:[`wss://127.0.0.1:9944`]}}}),ID=L({id:1285,name:`Moonriver`,nativeCurrency:{decimals:18,name:`MOVR`,symbol:`MOVR`},rpcUrls:{default:{http:[`https://rpc.api.moonriver.moonbeam.network`],webSocket:[`wss://wss.api.moonriver.moonbeam.network`]}},blockExplorers:{default:{name:`Moonscan`,url:`https://moonriver.moonscan.io`,apiUrl:`https://api-moonriver.moonscan.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1597904}},testnet:!1}),LD=L({id:2818,name:`Morph`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.morphl2.io`],webSocket:[`wss://rpc.morphl2.io:8443`]}},blockExplorers:{default:{name:`Morph Explorer`,url:`https://explorer.morphl2.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3654913}},testnet:!1}),RD=L({id:2810,name:`Morph Holesky`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-quicknode-holesky.morphl2.io`],webSocket:[`wss://rpc-quicknode-holesky.morphl2.io`]}},blockExplorers:{default:{name:`Morph Holesky Explorer`,url:`https://explorer-holesky.morphl2.io`,apiUrl:`https://explorer-api-holesky.morphl2.io/api?`}},testnet:!0}),zD=L({id:2710,name:`Morph Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.morphl2.io`]}},blockExplorers:{default:{name:`Morph Testnet Explorer`,url:`https://explorer-testnet.morphl2.io`,apiUrl:`https://explorer-api-testnet.morphl2.io/api`}},testnet:!0}),BD=L({id:5551,name:`Nahmii 2 Mainnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://l2.nahmii.io`]}},blockExplorers:{default:{name:`Nahmii 2 Explorer`,url:`https://explorer.n2.nahmii.io`}},testnet:!1}),VD=L({id:22222,name:`Nautilus Mainnet`,nativeCurrency:{name:`ZBC`,symbol:`ZBC`,decimals:9},rpcUrls:{default:{http:[`https://api.nautilus.nautchain.xyz`]}},blockExplorers:{default:{name:`NautScan`,url:`https://nautscan.com`}}}),HD=L({id:397,name:`NEAR Protocol`,nativeCurrency:{decimals:18,name:`NEAR`,symbol:`NEAR`},rpcUrls:{default:{http:[`https://eth-rpc.mainnet.near.org`]}},blockExplorers:{default:{name:`NEAR Explorer`,url:`https://eth-explorer.near.org`}},testnet:!1}),UD=L({id:398,name:`NEAR Protocol Testnet`,nativeCurrency:{decimals:18,name:`NEAR`,symbol:`NEAR`},rpcUrls:{default:{http:[`https://eth-rpc.testnet.near.org`]}},blockExplorers:{default:{name:`NEAR Explorer`,url:`https://eth-explorer-testnet.near.org`}},testnet:!0}),WD=L({id:245022926,name:`Neon EVM DevNet`,nativeCurrency:{name:`NEON`,symbol:`NEON`,decimals:18},rpcUrls:{default:{http:[`https://devnet.neonevm.org`]}},blockExplorers:{default:{name:`Neonscan`,url:`https://devnet.neonscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:205206112}},testnet:!0}),GD=L({id:245022934,network:`neonMainnet`,name:`Neon EVM MainNet`,nativeCurrency:{name:`NEON`,symbol:`NEON`,decimals:18},rpcUrls:{default:{http:[`https://neon-proxy-mainnet.solana.p2p.org`]}},blockExplorers:{default:{name:`Neonscan`,url:`https://neonscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:206545524}},testnet:!1}),KD=L({id:47763,name:`Neo X Mainnet`,nativeCurrency:{name:`Gas`,symbol:`GAS`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-1.rpc.banelabs.org`,`https://mainnet-2.rpc.banelabs.org`]}},blockExplorers:{default:{name:`Neo X - Explorer`,url:`https://xexplorer.neo.org`}},testnet:!1}),qD=L({id:12227332,name:`Neo X Testnet T4`,nativeCurrency:{name:`Gas`,symbol:`GAS`,decimals:18},rpcUrls:{default:{http:[`https://testnet.rpc.banelabs.org/`]}},blockExplorers:{default:{name:`neox-scan`,url:`https://xt4scan.ngd.network`}},testnet:!0}),JD=L({id:1012,name:`Newton`,nativeCurrency:{name:`Newton`,symbol:`NEW`,decimals:18},rpcUrls:{default:{http:[`https://global.rpc.mainnet.newtonproject.org`]}},blockExplorers:{default:{name:`NewFi explorer`,url:`https://explorer.newtonproject.org/`}},testnet:!1}),YD=L({id:4242,name:`Nexi`,nativeCurrency:{name:`Nexi`,symbol:`NEXI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.chain.nexi.technology`]}},blockExplorers:{default:{name:`NexiScan`,url:`https://www.nexiscan.com`,apiUrl:`https://www.nexiscan.com/api`}},contracts:{multicall3:{address:`0x0277A46Cc69A57eE3A6C8c158bA874832F718B8E`,blockCreated:25770160}}}),XD=L({id:240,name:`Nexilix Smart Chain`,nativeCurrency:{decimals:18,name:`Nexilix`,symbol:`NEXILIX`},rpcUrls:{default:{http:[`https://rpcurl.pos.nexilix.com`]}},blockExplorers:{default:{name:`NexilixScan`,url:`https://scan.nexilix.com`}},contracts:{multicall3:{address:`0x58381c8e2BF9d0C2C4259cA14BdA9Afe02831244`,blockCreated:74448}}}),ZD=L({id:6900,name:`Nibiru`,nativeCurrency:{decimals:18,name:`NIBI`,symbol:`NIBI`},rpcUrls:{default:{http:[`https://evm-rpc.nibiru.fi`]}},blockExplorers:{default:{name:`NibiScan`,url:`https://nibiscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:19587573}}}),QD=L({id:200024,name:`Nitrograph Testnet`,testnet:!0,rpcUrls:{default:{http:[`https://rpc-testnet.nitrograph.foundation`]}},nativeCurrency:{name:`Nitro`,symbol:`NOS`,decimals:18},blockExplorers:{default:{url:`https://explorer-testnet.nitrograph.foundation`,name:`Nitrograph Explorer`}}}),$D=L({id:166,name:`Nomina`,nativeCurrency:{name:`Nomina`,symbol:`NOM`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.nomina.io`],webSocket:[`wss://mainnet.nomina.io`]}},blockExplorers:{default:{name:`Nomina Explorer`,url:`https://nomscan.io`}},testnet:!1}),eO=L({id:4090,network:`oasis-testnet`,name:`Oasis Testnet`,nativeCurrency:{name:`Fasttoken`,symbol:`FTN`,decimals:18},rpcUrls:{default:{http:[`https://rpc1.oasis.bahamutchain.com`]}},blockExplorers:{default:{name:`Ftnscan`,url:`https://oasis.ftnscan.com`,apiUrl:`https://oasis.ftnscan.com/api`}},testnet:!0}),tO=L({id:248,name:`Oasys`,nativeCurrency:{name:`Oasys`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.oasys.games`]}},blockExplorers:{default:{name:`OasysScan`,url:`https://scan.oasys.games`,apiUrl:`https://scan.oasys.games/api`}}}),nO=L({id:911867,name:`Odyssey Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://odyssey.ithaca.xyz`]}},blockExplorers:{default:{name:`Odyssey Explorer`,url:`https://odyssey-explorer.ithaca.xyz`,apiUrl:`https://odyssey-explorer.ithaca.xyz/api`}},testnet:!0}),rO=L({id:66,name:`OKC`,nativeCurrency:{decimals:18,name:`OKT`,symbol:`OKT`},rpcUrls:{default:{http:[`https://exchainrpc.okex.org`]}},blockExplorers:{default:{name:`oklink`,url:`https://www.oklink.com/okc`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:10364792}}}),iO=L({id:311,name:`Omax Mainnet`,nativeCurrency:{decimals:18,name:`OMAX`,symbol:`OMAX`},rpcUrls:{default:{http:[`https://mainapi.omaxray.com`]}},blockExplorers:{default:{name:`Omax Explorer`,url:`https://omaxscan.com`}},testnet:!1}),aO=L({id:166,name:`Omni`,nativeCurrency:{name:`Omni`,symbol:`OMNI`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.omni.network`],webSocket:[`wss://mainnet.omni.network`]}},blockExplorers:{default:{name:`OmniScan`,url:`https://omniscan.network`}},testnet:!1}),oO=L({id:164,name:`Omni Omega`,nativeCurrency:{name:`Omni`,symbol:`OMNI`,decimals:18},rpcUrls:{default:{http:[`https://omega.omni.network`],webSocket:[`wss://omega.omni.network`]}},blockExplorers:{default:{name:`Omega OmniScan`,url:`https://omega.omniscan.network/`}},testnet:!0}),sO=L({id:309075,name:`One World Chain Mainnet`,nativeCurrency:{decimals:18,name:`OWCT`,symbol:`OWCT`},rpcUrls:{default:{http:[`https://mainnet-rpc.oneworldchain.org`]}},blockExplorers:{default:{name:`One World Explorer`,url:`https://mainnet.oneworldchain.org`}},testnet:!1}),cO=L({id:9700,name:`OORT MainnetDev`,nativeCurrency:{decimals:18,name:`OORT`,symbol:`OORT`},rpcUrls:{default:{http:[`https://dev-rpc.oortech.com`]}},blockExplorers:{default:{name:`OORT MainnetDev Explorer`,url:`https://dev-scan.oortech.com`}}}),lO=56,uO=L({id:204,name:`opBNB`,nativeCurrency:{name:`BNB`,symbol:`BNB`,decimals:18},rpcUrls:{default:{http:[`https://opbnb-mainnet-rpc.bnbchain.org`]}},blockExplorers:{default:{name:`opBNB (BSCScan)`,url:`https://opbnb.bscscan.com`,apiUrl:`https://api-opbnb.bscscan.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:512881},l2OutputOracle:{[lO]:{address:`0x153CAB79f4767E2ff862C94aa49573294B13D169`}},portal:{[lO]:{address:`0x1876EA7702C0ad0C6A2ae6036DE7733edfBca519`}},l1StandardBridge:{[lO]:{address:`0xF05F0e4362859c3331Cb9395CBC201E3Fa6757Ea`}}},sourceId:lO}),dO=97,fO=L({id:5611,name:`opBNB Testnet`,nativeCurrency:{decimals:18,name:`tBNB`,symbol:`tBNB`},rpcUrls:{default:{http:[`https://opbnb-testnet-rpc.bnbchain.org`]}},blockExplorers:{default:{name:`opbnbscan`,url:`https://testnet.opbnbscan.com`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3705108},l2OutputOracle:{[dO]:{address:`0xFf2394Bb843012562f4349C6632a0EcB92fC8810`}},portal:{[dO]:{address:`0x4386C8ABf2009aC0c263462Da568DD9d46e52a31`}},l1StandardBridge:{[dO]:{address:`0x677311Fd2cCc511Bbc0f581E8d9a07B033D5E840`}}},testnet:!0,sourceId:dO}),pO=L({id:1612,name:`OpenLedger`,nativeCurrency:{name:`Open`,symbol:`OPEN`,decimals:18},rpcUrls:{default:{http:[`https://rpc.openledger.xyz`]}},blockExplorers:{default:{name:`OpenLedger Explorer`,url:`https://scan.openledger.xyz`}},testnet:!1}),mO=1,hO=L({...R,id:10,name:`OP Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.optimism.io`]}},blockExplorers:{default:{name:`Optimism Explorer`,url:`https://optimistic.etherscan.io`,apiUrl:`https://api-optimistic.etherscan.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[mO]:{address:`0xe5965Ab5962eDc7477C8520243A95517CD252fA9`}},l2OutputOracle:{[mO]:{address:`0xdfe97868233d1aa22e815a266982f2cf17685a27`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:4286263},portal:{[mO]:{address:`0xbEb5Fc579115071764c7423A4f12eDde41f106Ed`}},l1StandardBridge:{[mO]:{address:`0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1`}}},sourceId:mO}),gO=5,_O=L({...R,id:420,name:`Optimism Goerli`,nativeCurrency:{name:`Goerli Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://goerli.optimism.io`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://goerli-optimism.etherscan.io`,apiUrl:`https://goerli-optimism.etherscan.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[gO]:{address:`0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:49461},portal:{[gO]:{address:`0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383`}},l1StandardBridge:{[gO]:{address:`0x636Af16bf2f682dD3109e60102b8E1A089FedAa8`}}},testnet:!0,sourceId:gO}),vO=11155111,yO=L({...R,id:11155420,name:`OP Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.optimism.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://optimism-sepolia.blockscout.com`,apiUrl:`https://optimism-sepolia.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[vO]:{address:`0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1`}},l2OutputOracle:{[vO]:{address:`0x90E9c4f8a994a250F6aEfd61CAFb4F2e895D458F`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1620204},portal:{[vO]:{address:`0x16Fc5058F25648194471939df75CF27A2fdC48BC`}},l1StandardBridge:{[vO]:{address:`0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1`}}},testnet:!0,sourceId:vO}),bO=L({id:62050,name:`Optopia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.optopia.ai`]}},blockExplorers:{default:{name:`Optopia Explorer`,url:`https://scan.optopia.ai`}},testnet:!1}),xO=L({id:62049,name:`Optopia Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.optopia.ai`]}},blockExplorers:{default:{name:`Optopia Explorer`,url:`https://scan-testnet.optopia.ai`}},testnet:!0}),SO=L({id:291,name:`Orderly`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.orderly.network`]}},blockExplorers:{default:{name:`Orderly Explorer`,url:`https://explorer.orderly.network`}},testnet:!1}),CO=L({id:4460,name:`Orderly Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://l2-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz`]}},blockExplorers:{default:{name:`Orderly Explorer`,url:`https://explorerl2new-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz`}},testnet:!0}),wO=L({id:41144114,name:`Otim Devnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`http://devnet.otim.xyz`]}},contracts:{batchInvoker:{address:`0x5FbDB2315678afecb367f032d93F642f64180aa3`}}}),TO=L({id:11297108109,name:`Palm`,nativeCurrency:{decimals:18,name:`PALM`,symbol:`PALM`},rpcUrls:{default:{http:[`https://palm-mainnet.public.blastapi.io`],webSocket:[`wss://palm-mainnet.public.blastapi.io`]}},blockExplorers:{default:{name:`Chainlens`,url:`https://palm.chainlens.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15429248}}}),EO=L({id:11297108099,name:`Palm Testnet`,nativeCurrency:{decimals:18,name:`PALM`,symbol:`PALM`},rpcUrls:{default:{http:[`https://palm-mainnet.public.blastapi.io`],webSocket:[`wss://palm-mainnet.public.blastapi.io`]}},blockExplorers:{default:{name:`Chainlens`,url:`https://palm.chainlens.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:15429248}},testnet:!0}),DO=L({id:420420422,name:`Paseo PassetHub`,nativeCurrency:{name:`PAS`,symbol:`PAS`,decimals:18},rpcUrls:{default:{http:[`https://testnet-passet-hub-eth-rpc.polkadot.io`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://blockscout-passet-hub.parity-testnet.parity.io`}},testnet:!0}),OO=L({id:3338,name:`Peaq`,nativeCurrency:{decimals:18,name:`peaq`,symbol:`PEAQ`},rpcUrls:{default:{http:[`https://quicknode1.peaq.xyz`,`https://quicknode2.peaq.xyz`,`https://quicknode3.peaq.xyz`],webSocket:[`wss://quicknode1.peaq.xyz`,`wss://quicknode2.peaq.xyz`,`wss://quicknode3.peaq.xyz`]}},blockExplorers:{default:{name:`Subscan`,url:`https://peaq.subscan.io`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3566354}}}),kO=1,AO=L({id:424,network:`pgn`,name:`PGN`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.publicgoods.network`]}},blockExplorers:{default:{name:`PGN Explorer`,url:`https://explorer.publicgoods.network`,apiUrl:`https://explorer.publicgoods.network/api`}},contracts:{l2OutputOracle:{[kO]:{address:`0x9E6204F750cD866b299594e2aC9eA824E2e5f95c`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3380209},portal:{[kO]:{address:`0xb26Fd985c5959bBB382BAFdD0b879E149e48116c`}},l1StandardBridge:{[kO]:{address:`0xD0204B9527C1bA7bD765Fa5CCD9355d38338272b`}}},formatters:Iy,sourceId:kO}),jO=11155111,MO=L({id:58008,network:`pgn-testnet`,name:`PGN`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.publicgoods.network`]}},blockExplorers:{default:{name:`PGN Testnet Explorer`,url:`https://explorer.sepolia.publicgoods.network`,apiUrl:`https://explorer.sepolia.publicgoods.network/api`}},contracts:{l2OutputOracle:{[jO]:{address:`0xD5bAc3152ffC25318F848B3DD5dA6C85171BaEEe`}},portal:{[jO]:{address:`0xF04BdD5353Bb0EFF6CA60CfcC78594278eBfE179`}},l1StandardBridge:{[jO]:{address:`0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3754925}},formatters:Iy,sourceId:jO,testnet:!0}),NO=L({id:13381,name:`Phoenix Blockchain`,nativeCurrency:{name:`Phoenix`,symbol:`PHX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.phoenixplorer.com`]}},blockExplorers:{default:{name:`Phoenixplorer`,url:`https://phoenixplorer.com`,apiUrl:`https://phoenixplorer.com/api`}},contracts:{multicall3:{address:`0x498cF757a575cFF2c2Ed9f532f56Efa797f86442`,blockCreated:5620192}}}),PO=L({id:7070,name:`Planq Mainnet`,nativeCurrency:{decimals:18,name:`PLQ`,symbol:`PLQ`},rpcUrls:{default:{http:[`https://planq-rpc.nodies.app`,`https://evm-rpc.planq.network`,`https://jsonrpc.planq.nodestake.top`]}},blockExplorers:{default:{name:`Planq Explorer`,url:`https://evm.planq.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:8470015}},testnet:!1}),FO=L({id:9745,name:`Plasma`,blockTime:1e3,nativeCurrency:{name:`Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plasma.to`]}},blockExplorers:{default:{name:`PlasmaScan`,url:`https://plasmascan.to`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),IO=L({id:9747,name:`Plasma Devnet`,nativeCurrency:{name:`Devnet Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://devnet-rpc.plasma.to`]}},testnet:!0,contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}}}),LO=L({id:9746,name:`Plasma Testnet`,nativeCurrency:{name:`Testnet Plasma`,symbol:`XPL`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plasma.to`]}},blockExplorers:{default:{name:`RouteScan`,url:`https://testnet.plasmascan.to`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!0}),RO=L({...xy,id:1612127,name:`PlayFi Albireo Testnet`,network:`albireo`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://albireo-rpc.playfi.ai`],webSocket:[`wss://albireo-rpc-ws.playfi.ai/ws`]}},blockExplorers:{default:{name:`PlayFi Albireo Explorer`,url:`https://albireo-explorer.playfi.ai`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`}},testnet:!0}),zO=L({id:242,name:`Plinga`,nativeCurrency:{name:`Plinga`,symbol:`PLINGA`,decimals:18},rpcUrls:{default:{http:[`https://rpcurl.mainnet.plgchain.com`]}},blockExplorers:{default:{name:`Plgscan`,url:`https://www.plgscan.com`}},contracts:{multicall3:{address:`0x0989576160f2e7092908BB9479631b901060b6e4`,blockCreated:204489}}}),BO=L({id:98865,name:`Plume (Legacy)`,nativeCurrency:{name:`Plume Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plumenetwork.xyz`],webSocket:[`wss://rpc.plumenetwork.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.plumenetwork.xyz`,apiUrl:`https://explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:48577}},sourceId:1}),VO=L({id:98864,name:`Plume Devnet (Legacy)`,nativeCurrency:{name:`Plume Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://test-rpc.plumenetwork.xyz`],webSocket:[`wss://test-rpc.plumenetwork.xyz`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://test-explorer.plumenetwork.xyz`,apiUrl:`https://test-explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:481948}},testnet:!0,sourceId:11155111}),HO=L({id:98866,name:`Plume`,nativeCurrency:{name:`Plume`,symbol:`PLUME`,decimals:18},rpcUrls:{default:{http:[`https://rpc.plume.org`],webSocket:[`wss://rpc.plume.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.plume.org`,apiUrl:`https://explorer.plume.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:39679}},sourceId:1}),UO=L({id:98867,name:`Plume Testnet`,nativeCurrency:{name:`Plume`,symbol:`PLUME`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plume.org`],webSocket:[`wss://testnet-rpc.plume.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.plume.org`,apiUrl:`https://testnet-explorer.plume.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:199712}},testnet:!0,sourceId:11155111}),WO=L({id:161221135,name:`Plume Testnet (Legacy)`,nativeCurrency:{name:`Plume Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.plumenetwork.xyz/http`],webSocket:[`wss://testnet-rpc.plumenetwork.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer.plumenetwork.xyz`,apiUrl:`https://testnet-explorer.plumenetwork.xyz/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:6022332}},testnet:!0,sourceId:11155111}),GO=L({id:631571,name:`Polter Testnet`,nativeCurrency:{decimals:18,name:`Polter GHST`,symbol:`GHST`},rpcUrls:{default:{http:[`https://geist-polter.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://polter-testnet.explorer.alchemy.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:11245}},testnet:!0}),KO=L({id:137,name:`Polygon`,blockTime:2e3,nativeCurrency:{name:`POL`,symbol:`POL`,decimals:18},rpcUrls:{default:{http:[`https://polygon.drpc.org`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://polygonscan.com`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:25770160}}}),qO=L({id:80002,name:`Polygon Amoy`,nativeCurrency:{name:`POL`,symbol:`POL`,decimals:18},rpcUrls:{default:{http:[`https://rpc-amoy.polygon.technology`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://amoy.polygonscan.com`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:3127388}},testnet:!0}),JO=L({id:80001,name:`Polygon Mumbai`,nativeCurrency:{name:`MATIC`,symbol:`MATIC`,decimals:18},rpcUrls:{default:{http:[`https://80001.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://mumbai.polygonscan.com`,apiUrl:`https://api-testnet.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:25770160}},testnet:!0}),YO=L({id:1101,name:`Polygon zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://zkevm-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://zkevm.polygonscan.com`,apiUrl:`https://api-zkevm.polygonscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:57746}}}),XO=L({id:2442,name:`Polygon zkEVM Cardona`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.cardona.zkevm-rpc.com`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://cardona-zkevm.polygonscan.com`,apiUrl:`https://cardona-zkevm.polygonscan.com/api`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:114091}}}),ZO=L({id:1442,name:`Polygon zkEVM Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.public.zkevm-test.net`]}},blockExplorers:{default:{name:`PolygonScan`,url:`https://testnet-zkevm.polygonscan.com`,apiUrl:`https://testnet-zkevm.polygonscan.com/api`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:525686}}}),QO=L({id:8008,name:`Polynomial`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.polynomial.fi`]}},blockExplorers:{default:{name:`Polynomial Scan`,url:`https://polynomialscan.io`}},testnet:!1,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),$O=L({id:80008,name:`Polynomia Sepolia`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.sepolia.polynomial.fi`]}},blockExplorers:{default:{name:`Polynomial Scan`,url:`https://sepolia.polynomialscan.io`}},testnet:!0,contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),ek=L({id:60603,name:`POTOS Mainnet`,nativeCurrency:{decimals:18,name:`POTOS Token`,symbol:`POT`},rpcUrls:{default:{http:[`https://rpc.potos.hk`]}},blockExplorers:{default:{name:`POTOS Mainnet explorer`,url:`https://scan.potos.hk`}},testnet:!1}),tk=L({id:60600,name:`POTOS Testnet`,nativeCurrency:{decimals:18,name:`POTOS Token`,symbol:`POT`},rpcUrls:{default:{http:[`https://rpc-testnet.potos.hk`]}},blockExplorers:{default:{name:`POTOS Testnet explorer`,url:`https://scan-testnet.potos.hk`}},testnet:!0}),nk=L({id:23023,name:`PremiumBlock Testnet`,nativeCurrency:{name:`Premium Block`,symbol:`PBLK`,decimals:18},rpcUrls:{default:{http:[`https://rpc.premiumblock.org`]}},blockExplorers:{default:{name:`PremiumBlocks Explorer`,url:`https://scan.premiumblock.org`}},testnet:!0}),rk=L({id:369,name:`PulseChain`,nativeCurrency:{name:`Pulse`,symbol:`PLS`,decimals:18},testnet:!1,blockTime:1e4,rpcUrls:{default:{http:[`https://rpc.pulsechain.com`],webSocket:[`wss://ws.pulsechain.com`]}},blockExplorers:{default:{name:`PulseScan`,url:`https://ipfs.scan.pulsechain.com`,apiUrl:`https://api.scan.pulsechain.com/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),ik=L({id:943,name:`PulseChain V4`,testnet:!0,nativeCurrency:{name:`V4 Pulse`,symbol:`v4PLS`,decimals:18},blockTime:1e4,rpcUrls:{default:{http:[`https://rpc.v4.testnet.pulsechain.com`],webSocket:[`wss://ws.v4.testnet.pulsechain.com`]}},blockExplorers:{default:{name:`PulseScan`,url:`https://scan.v4.testnet.pulsechain.com`,apiUrl:`https://scan.v4.testnet.pulsechain.com/api`}},contracts:{ensRegistry:{address:`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14353601}}}),ak=L({id:490092,name:`Pumpfi Testnet`,nativeCurrency:{decimals:18,name:`PMPT`,symbol:`PMPT`},rpcUrls:{default:{http:[`https://rpc1testnet.pumpfi.me`]}},blockExplorers:{default:{name:`Pumpfi Testnet Scan`,url:`https://testnetscan.pumpfi.me`}},testnet:!0}),ok=11155111,sk=L({...R,name:`Pyrope Testnet`,testnet:!0,id:695569,sourceId:ok,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.pyropechain.com`],webSocket:[`wss://rpc.pyropechain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://pyrope.blockscout.com`}},contracts:{...R.contracts,l1StandardBridge:{[ok]:{address:`0xC24932c31D9621aE9e792576152B7ef010cFC2F8`}}}}),ck=L({id:766,name:`QL1`,nativeCurrency:{decimals:18,name:`QOM`,symbol:`QOM`},rpcUrls:{default:{http:[`https://rpc.qom.one`]}},blockExplorers:{default:{name:`Ql1 Explorer`,url:`https://scan.qom.one`}},contracts:{multicall3:{address:`0x7A52370716ea730585884F5BDB0f6E60C39b8C64`}},testnet:!1}),lk=L({id:35441,name:`Q Mainnet`,nativeCurrency:{decimals:18,name:`Q`,symbol:`Q`},rpcUrls:{default:{http:[`https://rpc.q.org`]}},blockExplorers:{default:{name:`Q Mainnet Explorer`,url:`https://explorer.q.org`,apiUrl:`https://explorer.q.org/api`}}}),uk=L({id:35443,name:`Q Testnet`,nativeCurrency:{decimals:18,name:`Q`,symbol:`Q`},rpcUrls:{default:{http:[`https://rpc.qtestnet.org`]}},blockExplorers:{default:{name:`Q Testnet Explorer`,url:`https://explorer.qtestnet.org`,apiUrl:`https://explorer.qtestnet.org/api`}},testnet:!0}),dk=L({id:9,name:`Quai Network Mainnet`,nativeCurrency:{decimals:18,name:`Quai`,symbol:`QUAI`},rpcUrls:{default:{http:[`https://rpc.quai.network/cyprus1`]}},blockExplorers:{default:{name:`Quaiscan`,url:`https://quaiscan.io`,apiUrl:`https://quaiscan.io/api`}},testnet:!1}),fk=L({id:15e3,name:`Quai Network Testnet`,nativeCurrency:{decimals:18,name:`Quai`,symbol:`QUAI`},rpcUrls:{default:{http:[`https://orchard.rpc.quai.network/cyprus1`]}},blockExplorers:{default:{name:`Orchard Quaiscan`,url:`https://orchard.quaiscan.io`,apiUrl:`https://orchard.quaiscan.io/api`}},testnet:!0}),pk=L({id:5318007,name:`Reactive Lasna Testnet`,nativeCurrency:{decimals:18,name:`Lasna React`,symbol:`lREACT`},rpcUrls:{default:{http:[`https://lasna-rpc.rnk.dev`]}},blockExplorers:{default:{name:`Reactscan`,url:`https://lasna.reactscan.net`}},testnet:!0}),mk=L({id:111188,name:`re.al`,nativeCurrency:{name:`reETH`,decimals:18,symbol:`reETH`},rpcUrls:{default:{http:[`https://rpc.realforreal.gelato.digital`]}},blockExplorers:{default:{name:`re.al Explorer`,url:`https://explorer.re.al`,apiUrl:`https://explorer.re.al/api/v2`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:695}}}),hk=L({id:151,name:`Redbelly Network Mainnet`,nativeCurrency:{name:`Redbelly Native Coin`,symbol:`RBNT`,decimals:18},rpcUrls:{default:{http:[`https://governors.mainnet.redbelly.network`]}},blockExplorers:{default:{name:`Routescan`,url:`https://redbelly.routescan.io`,apiUrl:`https://api.routescan.io/v2/network/mainnet/evm/151/etherscan/api`}},testnet:!1}),gk=L({id:153,name:`Redbelly Network Testnet`,nativeCurrency:{name:`Redbelly Native Coin`,symbol:`RBNT`,decimals:18},rpcUrls:{default:{http:[`https://governors.testnet.redbelly.network`]}},blockExplorers:{default:{name:`Routescan`,url:`https://redbelly.testnet.routescan.io`,apiUrl:`https://api.routescan.io/v2/network/testnet/evm/153_2/etherscan/api`}},testnet:!0}),_k=L({id:50342,name:`Reddio`,nativeCurrency:{name:`Reddio`,symbol:`RED`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.reddio.com/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://reddio.cloud.blockscout.com`,apiUrl:`https://reddio.cloud.blockscout.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:848849}},testnet:!1}),vk=L({id:50341,name:`Reddio Sepolia`,nativeCurrency:{name:`Reddio`,symbol:`RED`,decimals:18},rpcUrls:{default:{http:[`https://reddio-dev.reddio.com`]}},blockExplorers:{default:{name:`Reddioscan`,url:`https://reddio-devnet.l2scan.co`,apiUrl:`https://reddio-devnet.l2scan.co/api`}},testnet:!0}),yk=1,bk=L({...R,name:`Redstone`,id:690,sourceId:yk,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.redstonechain.com`],webSocket:[`wss://rpc.redstonechain.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.redstone.xyz`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[yk]:{address:`0xC7bCb0e8839a28A1cFadd1CF716de9016CdA51ae`,blockCreated:19578329}},l2OutputOracle:{[yk]:{address:`0xa426A052f657AEEefc298b3B5c35a470e4739d69`,blockCreated:19578337}},l1StandardBridge:{[yk]:{address:`0xc473ca7E02af24c129c2eEf51F2aDf0411c1Df69`,blockCreated:19578331}}}}),xk=L({id:47805,name:`REI Mainnet`,nativeCurrency:{decimals:18,name:`REI`,symbol:`REI`},rpcUrls:{default:{http:[`https://rpc.rei.network`],webSocket:[`wss://rpc.rei.network`]}},blockExplorers:{default:{name:`REI Scan`,url:`https://scan.rei.network`}},testnet:!1}),Sk=L({id:1729,name:`Reya Network`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.reya.network`],webSocket:[`wss://ws.reya.network`]}},blockExplorers:{default:{name:`Reya Network Explorer`,url:`https://explorer.reya.network`}},testnet:!1}),Ck=L({id:4153,name:`RISE`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.risechain.com`],webSocket:[`wss://rpc.risechain.com/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.risechain.com`,apiUrl:`https://explorer.risechain.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}}}),wk=L({id:11155931,name:`RISE Testnet`,nativeCurrency:{name:`RISE Testnet Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.riselabs.xyz`],webSocket:[`wss://testnet.riselabs.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.testnet.riselabs.xyz/`,apiUrl:`https://explorer.testnet.riselabs.xyz/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`}},testnet:!0}),Tk=L({id:753,name:`Rivalz`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rivalz.calderachain.xyz/http`]}},blockExplorers:{default:{name:`Rivalz Caldera Explorer`,url:`https://rivalz.calderaexplorer.xyz`}},testnet:!1}),Ek=L({id:570,name:`Rollux Mainnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.rollux.com`],webSocket:[`wss://rpc.rollux.com/wss`]}},blockExplorers:{default:{name:`RolluxExplorer`,url:`https://explorer.rollux.com`,apiUrl:`https://explorer.rollux.com/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:119222}}}),Dk=L({id:57e3,name:`Rollux Testnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc-tanenbaum.rollux.com/`],webSocket:[`wss://rpc-tanenbaum.rollux.com/wss`]}},blockExplorers:{default:{name:`RolluxTestnetExplorer`,url:`https://rollux.tanenbaum.io`,apiUrl:`https://rollux.tanenbaum.io/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1813675}}}),Ok=L({id:2020,name:`Ronin`,nativeCurrency:{name:`RON`,symbol:`RON`,decimals:18},rpcUrls:{default:{http:[`https://api.roninchain.com/rpc`]}},blockExplorers:{default:{name:`Ronin Explorer`,url:`https://app.roninchain.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:26023535}}}),kk=L({id:7668,name:`The Root Network`,nativeCurrency:{decimals:18,name:`XRP`,symbol:`XRP`},rpcUrls:{default:{http:[`https://root.rootnet.live/archive`],webSocket:[`wss://root.rootnet.live/archive/ws`]}},blockExplorers:{default:{name:`Rootscan`,url:`https://rootscan.io`}},contracts:{multicall3:{address:`0xc9C2E2429AeC354916c476B30d729deDdC94988d`,blockCreated:9218338}}}),Ak=L({id:7672,name:`The Root Network - Porcini`,nativeCurrency:{decimals:18,name:`XRP`,symbol:`XRP`},rpcUrls:{default:{http:[`https://porcini.rootnet.app/archive`],webSocket:[`wss://porcini.rootnet.app/archive/ws`]}},blockExplorers:{default:{name:`Rootscan`,url:`https://porcini.rootscan.io`}},contracts:{multicall3:{address:`0xc9C2E2429AeC354916c476B30d729deDdC94988d`,blockCreated:10555692}},testnet:!0}),jk=L({id:30,name:`Rootstock Mainnet`,network:`rootstock`,nativeCurrency:{decimals:18,name:`Rootstock Bitcoin`,symbol:`RBTC`},rpcUrls:{default:{http:[`https://public-node.rsk.co`]}},blockExplorers:{default:{name:`RSK Explorer`,url:`https://explorer.rsk.co`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:4249540}}}),Mk=L({id:31,name:`Rootstock Testnet`,network:`rootstock`,nativeCurrency:{decimals:18,name:`Rootstock Bitcoin`,symbol:`tRBTC`},rpcUrls:{default:{http:[`https://public-node.testnet.rsk.co`]}},blockExplorers:{default:{name:`RSK Explorer`,url:`https://explorer.testnet.rootstock.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2771150}},testnet:!0}),Nk=1,Pk=L({...R,id:12553,name:`RSS3 VSL Mainnet`,nativeCurrency:{name:`RSS3`,symbol:`RSS3`,decimals:18},rpcUrls:{default:{http:[`https://rpc.rss3.io`]}},blockExplorers:{default:{name:`RSS3 VSL Mainnet Scan`,url:`https://scan.rss3.io`,apiUrl:`https://scan.rss3.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[Nk]:{address:`0xE6f24d2C32B3109B18ed33cF08eFb490b1e09C10`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14193},portal:{[Nk]:{address:`0x6A12432491bbbE8d3babf75F759766774C778Db4`,blockCreated:19387057}},l1StandardBridge:{[Nk]:{address:`0x4cbab69108Aa72151EDa5A3c164eA86845f18438`}}},sourceId:Nk}),Fk=11155111,Ik=L({...R,id:2331,name:`RSS3 VSL Sepolia Testnet`,nativeCurrency:{name:`RSS3`,symbol:`RSS3`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.rss3.io`]}},blockExplorers:{default:{name:`RSS3 VSL Sepolia Testnet Scan`,url:`https://scan.testnet.rss3.io`,apiUrl:`https://scan.testnet.rss3.io/api`}},contracts:{...R.contracts,l2OutputOracle:{[Fk]:{address:`0xDb5c46C3Eaa6Ed6aE8b2379785DF7dd029C0dC81`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:55697},portal:{[Fk]:{address:`0xcBD77E8E1E7F06B25baDe67142cdE82652Da7b57`,blockCreated:5345035}},l1StandardBridge:{[Fk]:{address:`0xdDD29bb63B0839FB1cE0eE439Ff027738595D07B`}}},testnet:!0,sourceId:Fk}),Lk=L({id:7225878,name:`Saakuru Mainnet`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.saakuru.network`]}},blockExplorers:{default:{name:`Saakuru Explorer`,url:`https://explorer.saakuru.network`}},testnet:!1}),Rk=L({id:5464,name:`Saga`,network:`saga`,nativeCurrency:{decimals:18,name:`gas`,symbol:`GAS`},rpcUrls:{default:{http:[`https://sagaevm.jsonrpc.sagarpc.io`]}},blockExplorers:{default:{name:`Saga Explorer`,url:`https://sagaevm.sagaexplorer.io`}},contracts:{multicall3:{address:`0x864DDc9B50B9A0dF676d826c9B9EDe9F8913a160`,blockCreated:467530}}}),zk=L({id:202601,name:`Ronin Saigon Testnet`,nativeCurrency:{name:`RON`,symbol:`RON`,decimals:18},rpcUrls:{default:{http:[`https://saigon-testnet.roninchain.com/rpc`]}},blockExplorers:{default:{name:`Saigon Explorer`,url:`https://saigon-explorer.roninchain.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:18736871}},testnet:!0}),Bk=L({id:1996,name:`Sanko`,nativeCurrency:{name:`DMT`,symbol:`DMT`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.sanko.xyz`]}},blockExplorers:{default:{name:`Sanko Explorer`,url:`https://explorer.sanko.xyz`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:37}},testnet:!1}),Vk=L({id:23294,name:`Oasis Sapphire`,network:`sapphire`,nativeCurrency:{name:`Sapphire Rose`,symbol:`ROSE`,decimals:18},rpcUrls:{default:{http:[`https://sapphire.oasis.io`],webSocket:[`wss://sapphire.oasis.io/ws`]}},blockExplorers:{default:{name:`Oasis Explorer`,url:`https://explorer.oasis.io/mainnet/sapphire`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:734531}}}),Hk=L({id:23295,name:`Oasis Sapphire Testnet`,network:`sapphire-testnet`,nativeCurrency:{name:`Sapphire Test Rose`,symbol:`TEST`,decimals:18},rpcUrls:{default:{http:[`https://testnet.sapphire.oasis.dev`],webSocket:[`wss://testnet.sapphire.oasis.dev/ws`]}},blockExplorers:{default:{name:`Oasis Explorer`,url:`https://explorer.oasis.io/testnet/sapphire`}},testnet:!0}),Uk=L({id:3109,name:`SatoshiVM Alpha Mainnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://alpha-rpc-node-http.svmscan.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://svmscan.io`,apiUrl:`https://svmscan.io/api`}}}),Wk=L({id:3110,name:`SatoshiVM Testnet`,nativeCurrency:{name:`BTC`,symbol:`BTC`,decimals:18},rpcUrls:{default:{http:[`https://test-rpc-node-http.svmscan.io`]}},blockExplorers:{default:{name:`blockscout`,url:`https://testnet.svmscan.io`,apiUrl:`https://testnet.svmscan.io/api`}},testnet:!0}),Gk=L({id:534352,name:`Scroll`,blockTime:3e3,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.scroll.io`],webSocket:[`wss://wss-rpc.scroll.io/ws`]}},blockExplorers:{default:{name:`Scrollscan`,url:`https://scrollscan.com`,apiUrl:`https://api.scrollscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:14}},testnet:!1}),Kk=L({id:534351,name:`Scroll Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia-rpc.scroll.io`]}},blockExplorers:{default:{name:`Scrollscan`,url:`https://sepolia.scrollscan.com`,apiUrl:`https://api-sepolia.scrollscan.com/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:9473}},testnet:!0}),qk=L({id:1329,name:`Sei Network`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc.sei-apis.com/`],webSocket:[`wss://evm-ws.sei-apis.com/`]}},blockExplorers:{default:{name:`Seiscan`,url:`https://seiscan.io`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}}}),Jk=L({id:5124,name:`Seismic Devnet`,nativeCurrency:{name:`Seismic Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://node-2.seismicdev.net/rpc`]}},blockExplorers:{default:{name:`Seismic Devnet Explorer`,url:`https://explorer-2.seismicdev.net`}},testnet:!0}),Yk=L({id:1328,name:`Sei Testnet`,nativeCurrency:{name:`Sei`,symbol:`SEI`,decimals:18},rpcUrls:{default:{http:[`https://evm-rpc-testnet.sei-apis.com`],webSocket:[`wss://evm-ws-testnet.sei-apis.com`]}},blockExplorers:{default:{name:`Seiscan`,url:`https://testnet.seiscan.io`,apiUrl:`https://api.etherscan.io/v2/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:98697651}},testnet:!0}),Xk=L({id:11155111,name:`Sepolia`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://11155111.rpc.thirdweb.com`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://sepolia.etherscan.io`,apiUrl:`https://api-sepolia.etherscan.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:751532},ensUniversalResolver:{address:`0xeeeeeeee14d718c2b47d9923deab1335e144eeee`,blockCreated:8928790}},testnet:!0}),Zk=1,Qk=L({...R,id:360,name:`Shape`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.shape.network`]}},blockExplorers:{default:{name:`shapescan`,url:`https://shapescan.xyz`,apiUrl:`https://shapescan.xyz/api`}},contracts:{...R.contracts,l2OutputOracle:{[Zk]:{address:`0x6Ef8c69CfE4635d866e3E02732068022c06e724D`,blockCreated:20369940}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1},portal:{[Zk]:{address:`0xEB06fFa16011B5628BaB98E29776361c83741dd3`,blockCreated:20369933}},l1StandardBridge:{[Zk]:{address:`0x62Edd5f4930Ea92dCa3fB81689bDD9b9d076b57B`,blockCreated:20369935}}},sourceId:Zk}),$k=11155111,eA=L({...R,id:11011,name:`Shape Sepolia Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.shape.network`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer-sepolia.shape.network/`,apiUrl:`https://explorer-sepolia.shape.network/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1}},testnet:!0,sourceId:$k}),tA=L({id:8118,name:`Shardeum`,nativeCurrency:{name:`Shardeum`,symbol:`SHM`,decimals:18},rpcUrls:{default:{http:[`https://api.shardeum.org`]}},blockExplorers:{default:{name:`Shardeum Explorer`,url:`https://explorer.shardeum.org`}},testnet:!1}),nA=L({id:8082,name:`Shardeum Sphinx`,nativeCurrency:{name:`SHARDEUM`,symbol:`SHM`,decimals:18},rpcUrls:{default:{http:[`https://sphinx.shardeum.org`]}},blockExplorers:{default:{name:`Shardeum Explorer`,url:`https://explorer-sphinx.shardeum.org`}},testnet:!0}),rA=L({id:109,name:`Shibarium`,network:`shibarium`,nativeCurrency:{name:`Bone`,symbol:`BONE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.shibrpc.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://shibariumscan.io`}},contracts:{multicall3:{address:`0x864Bf681ADD6052395188A89101A1B37d3B4C961`,blockCreated:265900}}}),iA=L({id:157,name:`Puppynet Shibarium`,nativeCurrency:{decimals:18,name:`Bone`,symbol:`BONE`},rpcUrls:{default:{http:[`https://puppynet.shibrpc.com`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://puppyscan.shib.io`,apiUrl:`https://puppyscan.shib.io/api`}},contracts:{multicall3:{address:`0xA4029b74FBA366c926eDFA7Dd10B21C621170a4c`,blockCreated:3035769}},testnet:!0}),aA=L({id:336,name:`Shiden`,nativeCurrency:{decimals:18,name:`SDN`,symbol:`SDN`},rpcUrls:{default:{http:[`https://shiden.public.blastapi.io`],webSocket:[`wss://shiden-rpc.dwellir.com`]}},blockExplorers:{default:{name:`Shiden Scan`,url:`https://shiden.subscan.io`}},testnet:!1}),oA=L({id:148,name:`Shimmer`,network:`shimmer`,nativeCurrency:{decimals:18,name:`Shimmer`,symbol:`SMR`},rpcUrls:{default:{http:[`https://json-rpc.evm.shimmer.network`]}},blockExplorers:{default:{name:`Shimmer Network Explorer`,url:`https://explorer.evm.shimmer.network`,apiUrl:`https://explorer.evm.shimmer.network/api`}}}),sA=L({id:1073,name:`Shimmer Testnet`,network:`shimmer-testnet`,nativeCurrency:{decimals:18,name:`Shimmer`,symbol:`SMR`},rpcUrls:{default:{http:[`https://json-rpc.evm.testnet.shimmer.network`]}},blockExplorers:{default:{name:`Shimmer Network Explorer`,url:`https://explorer.evm.testnet.shimmer.network`,apiUrl:`https://explorer.evm.testnet.shimmer.network/api`}},testnet:!0}),cA=L({id:97453,name:`Sidra Chain`,nativeCurrency:{decimals:18,name:`Sidra Digital Asset`,symbol:`SDA`},rpcUrls:{default:{http:[`https://node.sidrachain.com`]}},blockExplorers:{default:{name:`Sidra Chain Explorer`,url:`https://ledger.sidrachain.com`}}}),lA=L({id:380929,name:`Silent Data Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.silentdata.com`]}},blockExplorers:{default:{name:`Silent Data Mainnet Explorer`,url:`https://explorer-mainnet.rollup.silentdata.com`}},testnet:!1}),uA=L({id:2355,name:`Silicon zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.silicon.network`,`https://silicon-mainnet.nodeinfra.com`]}},blockExplorers:{default:{name:`SiliconScope`,url:`https://scope.silicon.network`}}}),dA=L({id:1722641160,name:`Silicon Sepolia zkEVM`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-sepolia.silicon.network`,`https://silicon-testnet.nodeinfra.com`]}},blockExplorers:{default:{name:`SiliconSepoliaScope`,url:`https://scope-sepolia.silicon.network`}},testnet:!0}),fA=L({id:98,name:`Six Protocol`,nativeCurrency:{decimals:18,name:`SIX`,symbol:`SIX`},rpcUrls:{default:{http:[`https://sixnet-rpc-evm.sixprotocol.net`]}},blockExplorers:{default:{name:`Six Protocol Scan`,url:`https://sixscan.io/sixnet`}},testnet:!1}),pA=L({id:391845894,name:`SKALE | Block Brawlers`,nativeCurrency:{name:`BRAWL`,symbol:`BRAWL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/frayed-decent-antares`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/frayed-decent-antares`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://frayed-decent-antares.explorer.mainnet.skalenodes.com`}},contracts:{}}),mA=L({id:1564830818,name:`SKALE Calypso Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/honorable-steel-rasalhague`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/honorable-steel-rasalhague`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3107626}}}),hA=L({id:974399131,name:`SKALE Calypso Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/giant-half-dual-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/giant-half-dual-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://giant-half-dual-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:103220}},testnet:!0}),gA=L({id:1026062157,name:`SKALE | CryptoBlades`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/affectionate-immediate-pollux`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/affectionate-immediate-pollux`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://affectionate-immediate-pollux.explorer.mainnet.skalenodes.com`}},contracts:{}}),_A=L({id:1032942172,name:`SKALE | Crypto Colosseum`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/haunting-devoted-deneb`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/haunting-devoted-deneb`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://haunting-devoted-deneb.explorer.mainnet.skalenodes.com`}},contracts:{}}),vA=L({id:2046399126,name:`SKALE Europa Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/elated-tan-skat`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/elated-tan-skat`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://elated-tan-skat.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:3113495}}}),yA=L({id:1444673419,name:`SKALE Europa Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/juicy-low-small-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/juicy-low-small-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://juicy-low-small-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:110858}},testnet:!0}),bA=L({id:2139927552,name:`Exorde Network`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/light-vast-diphda`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/light-vast-diphda`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://light-vast-diphda.explorer.mainnet.skalenodes.com`}},contracts:{}}),xA=L({id:1273227453,name:`SKALE | Human Protocol`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/wan-red-ain`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/wan-red-ain`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://wan-red-ain.explorer.mainnet.skalenodes.com`}},contracts:{}}),SA=L({id:1482601649,name:`SKALE Nebula Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/green-giddy-denebola`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/green-giddy-denebola`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://green-giddy-denebola.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2372986}}}),CA=L({id:37084624,name:`SKALE Nebula Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/lanky-ill-funny-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/lanky-ill-funny-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://lanky-ill-funny-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:105141}},testnet:!0}),wA=L({id:278611351,name:`SKALE | Razor Network`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/turbulent-unique-scheat`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/turbulent-unique-scheat`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://turbulent-unique-scheat.explorer.mainnet.skalenodes.com`}},contracts:{}}),TA=L({id:1187947933,name:`SKALE Base`,nativeCurrency:{name:`Credits`,symbol:`CREDIT`,decimals:18},rpcUrls:{default:{http:[`https://skale-base.skalenodes.com/v1/base`],webSocket:[`wss://skale-base.skalenodes.com/v1/ws/base`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://skale-base-explorer.skalenodes.com/`}},testnet:!0}),EA=L({id:324705682,name:`SKALE Base Sepolia Testnet`,nativeCurrency:{name:`Credits`,symbol:`CREDIT`,decimals:18},rpcUrls:{default:{http:[`https://base-sepolia-testnet.skalenodes.com/v1/base-testnet`],webSocket:[`wss://base-sepolia-testnet.skalenodes.com/v1/ws/base-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://base-sepolia-testnet-explorer.skalenodes.com/`}},testnet:!0}),DA=L({id:1350216234,name:`SKALE Titan Hub`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.skalenodes.com/v1/parallel-stormy-spica`],webSocket:[`wss://mainnet.skalenodes.com/v1/ws/parallel-stormy-spica`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://parallel-stormy-spica.explorer.mainnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2076458}}}),OA=L({id:1020352220,name:`SKALE Titan Testnet`,nativeCurrency:{name:`sFUEL`,symbol:`sFUEL`,decimals:18},rpcUrls:{default:{http:[`https://testnet.skalenodes.com/v1/aware-fake-trim-testnet`],webSocket:[`wss://testnet.skalenodes.com/v1/ws/aware-fake-trim-testnet`]}},blockExplorers:{default:{name:`SKALE Explorer`,url:`https://aware-fake-trim-testnet.explorer.testnet.skalenodes.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:104072}},testnet:!0}),kA=L({id:984123,name:`Forma Sketchpad`,network:`sketchpad`,nativeCurrency:{symbol:`TIA`,name:`TIA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.sketchpad-1.forma.art`],webSocket:[`wss://ws.sketchpad-1.forma.art`]}},blockExplorers:{default:{name:`Sketchpad Explorer`,url:`https://explorer.sketchpad-1.forma.art`}},testnet:!0}),AA=1,jA=L({...R,id:2192,network:`snaxchain-mainnet`,name:`SnaxChain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.snaxchain.io`]}},blockExplorers:{default:{name:`Snax Explorer`,url:`https://explorer.snaxchain.io`,apiUrl:`https://explorer.snaxchain.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[AA]:{address:`0x472562Fcf26D6b2793f8E0b0fB660ba0E5e08A46`}},l2OutputOracle:{[AA]:{address:`0x2172e492Fc807F5d5645D0E3543f139ECF539294`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[AA]:{address:`0x79f446D024d74D0Bb6E699C131c703463c5D65E9`}},l1StandardBridge:{[AA]:{address:`0x6534Bdb6b5c060d3e6aa833433333135eFE8E0aA`}}},sourceId:AA}),MA=11155111,NA=L({...R,id:13001,network:`snaxchain-testnet`,name:`SnaxChain Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://testnet.snaxchain.io`]}},blockExplorers:{default:{name:`Snax Explorer`,url:`https://testnet-explorer.snaxchain.io`,apiUrl:`https://testnet-explorer.snaxchain.io/api`}},contracts:{...R.contracts,disputeGameFactory:{[MA]:{address:`0x206a75d89d45F146C54020F132FF93bEDD09f55E`}},l2OutputOracle:{[MA]:{address:`0x60e3A368a4cdCEf85ffB964e372726F56A46221e`}},multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`},portal:{[MA]:{address:`0xb5afdd0E8dDF081Ef90e8A3e0c7b5798e66E954E`}},l1StandardBridge:{[MA]:{address:`0xbd37E1a59D4C00C9A46F75018dffd84061bC5f74`}}},testnet:!0,sourceId:MA}),PA=L({id:5031,name:`Somnia`,nativeCurrency:{name:`Somnia`,symbol:`SOMI`,decimals:18},rpcUrls:{default:{http:[`https://api.infra.mainnet.somnia.network`]}},blockExplorers:{default:{name:`Somnia Explorer`,url:`https://explorer.somnia.network`,apiUrl:`https://explorer.somnia.network/api`}},testnet:!1}),FA=L({id:50312,name:`Somnia Testnet`,nativeCurrency:{name:`STT`,symbol:`STT`,decimals:18},rpcUrls:{default:{http:[`https://dream-rpc.somnia.network`]}},blockExplorers:{default:{name:`Somnia Testnet Explorer`,url:`https://shannon-explorer.somnia.network/`,apiUrl:`https://shannon-explorer.somnia.network/api`}},contracts:{multicall3:{address:`0x841b8199E6d3Db3C6f264f6C2bd8848b3cA64223`,blockCreated:71314235}},testnet:!0}),IA=1,LA=L({...R,id:1868,name:`Soneium Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.soneium.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://soneium.blockscout.com`,apiUrl:`https://soneium.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[IA]:{address:`0x512a3d2c7a43bd9261d2b8e8c9c70d4bd4d503c0`}},l2OutputOracle:{[IA]:{address:`0x0000000000000000000000000000000000000000`}},portal:{[IA]:{address:`0x88e529a6ccd302c948689cd5156c83d4614fae92`,blockCreated:7061266}},l1StandardBridge:{[IA]:{address:`0xeb9bf100225c214efc3e7c651ebbadcf85177607`,blockCreated:7061266}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}},sourceId:IA}),RA=11155111,zA=L({...R,id:1946,name:`Soneium Minato Testnet`,nativeCurrency:{name:`Sepolia Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.minato.soneium.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://soneium-minato.blockscout.com`,apiUrl:`https://soneium-minato.blockscout.com/api`}},contracts:{...R.contracts,disputeGameFactory:{[RA]:{address:`0xB3Ad2c38E6e0640d7ce6aA952AB3A60E81bf7a01`}},l2OutputOracle:{[RA]:{address:`0x710e5286C746eC38beeB7538d0146f60D27be343`}},portal:{[RA]:{address:`0x65ea1489741A5D72fFdD8e6485B216bBdcC15Af3`,blockCreated:6466136}},l1StandardBridge:{[RA]:{address:`0x5f5a404A5edabcDD80DB05E8e54A78c9EBF000C2`,blockCreated:6466136}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}},testnet:!0,sourceId:RA}),BA=L({id:19,name:`Songbird Canary-Network`,nativeCurrency:{decimals:18,name:`Songbird`,symbol:`SGB`},rpcUrls:{default:{http:[`https://songbird-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Songbird Explorer`,url:`https://songbird-explorer.flare.network`,apiUrl:`https://songbird-explorer.flare.network/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:13382504}}}),VA=L({id:16,name:`Songbird Testnet Coston`,nativeCurrency:{decimals:18,name:`Coston Flare`,symbol:`CFLR`},rpcUrls:{default:{http:[`https://coston-api.flare.network/ext/C/rpc`]}},blockExplorers:{default:{name:`Coston Explorer`,url:`https://coston-explorer.flare.network`,apiUrl:`https://coston-explorer.flare.network/api`}},testnet:!0}),HA=L({id:146,name:`Sonic`,blockTime:630,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Explorer`,url:`https://sonicscan.org`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:60}},testnet:!1}),UA=L({id:57054,name:`Sonic Blaze Testnet`,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.blaze.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Blaze Testnet Explorer`,url:`https://testnet.sonicscan.org`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1100}},testnet:!0}),WA=L({id:64165,name:`Sonic Testnet`,nativeCurrency:{decimals:18,name:`Sonic`,symbol:`S`},rpcUrls:{default:{http:[`https://rpc.testnet.soniclabs.com`]}},blockExplorers:{default:{name:`Sonic Testnet Explorer`,url:`https://testnet.soniclabs.com/`}},testnet:!0}),GA=L({...xy,blockTime:200,id:50104,name:`Sophon`,nativeCurrency:{decimals:18,name:`Sophon`,symbol:`SOPH`},rpcUrls:{default:{http:[`https://rpc.sophon.xyz`],webSocket:[`wss://rpc.sophon.xyz/ws`]}},blockExplorers:{default:{name:`Sophon Block Explorer`,url:`https://explorer.sophon.xyz`}},contracts:{multicall3:{address:`0x5f4867441d2416cA88B1b3fd38f21811680CD2C8`,blockCreated:116}},testnet:!1}),KA=L({...xy,blockTime:200,id:531050104,name:`Sophon Testnet`,nativeCurrency:{decimals:18,name:`Sophon`,symbol:`SOPH`},rpcUrls:{default:{http:[`https://rpc.testnet.sophon.xyz`],webSocket:[`wss://rpc.testnet.sophon.xyz/ws`]}},blockExplorers:{default:{name:`Sophon Block Explorer`,url:`https://explorer.testnet.sophon.xyz`}},contracts:{multicall3:{address:`0x83c04d112adedA2C6D9037bb6ecb42E7f0b108Af`,blockCreated:15642}},testnet:!0}),qA=L({id:100021,name:`Sova`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.sova.io`]}},blockExplorers:{default:{name:`Sova Block Explorer`,url:`hhttps://explorer.sova.io`}},testnet:!1}),JA=L({id:120893,name:`Sova Network Sepolia`,nativeCurrency:{decimals:18,name:`Sepolia Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.testnet.sova.io`]}},blockExplorers:{default:{name:`Sova Sepolia Explorer`,url:`https://explorer.testnet.sova.io`}},testnet:!0}),YA=L({id:88882,name:`Chiliz Spicy Testnet`,network:`chiliz-spicy-Testnet`,nativeCurrency:{decimals:18,name:`CHZ`,symbol:`CHZ`},rpcUrls:{default:{http:[`https://spicy-rpc.chiliz.com`,`https://chiliz-spicy-rpc.publicnode.com`],webSocket:[`wss://spicy-rpc-ws.chiliz.com`,`wss://chiliz-spicy-rpc.publicnode.com`]}},blockExplorers:{default:{name:`Chiliz Explorer`,url:`http://spicy-explorer.chiliz.com`,apiUrl:`http://spicy-explorer.chiliz.com/api`}},testnet:!0}),XA=L({id:988,name:`Stable Mainnet`,blockTime:700,nativeCurrency:{name:`USDT0`,symbol:`USDT0`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stable.xyz`],webSocket:[`wss://rpc.stable.xyz`]}},blockExplorers:{default:{name:`Stablescan`,url:`https://stablescan.xyz`,apiUrl:`https://api.etherscan.io/v2/api?chainid=988`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2423647}},testnet:!1}),ZA=L({id:2201,name:`Stable Testnet`,blockTime:700,nativeCurrency:{name:`USDT0`,symbol:`USDT0`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.stable.xyz`],webSocket:[`wss://rpc.testnet.stable.xyz`]}},blockExplorers:{default:{name:`Stablescan`,url:`https://testnet.stablescan.xyz`,apiUrl:`https://api.etherscan.io/v2/api?chainid=2201`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:22364430}},testnet:!0}),QA=L({...SE,id:1660990954,name:`Status Network Sepolia`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://public.sepolia.rpc.status.network`],webSocket:[`wss://public.sepolia.rpc.status.network/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://sepoliascan.status.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1578364}},testnet:!0}),$A=L({id:1234,name:`Step Network`,nativeCurrency:{name:`FITFI`,symbol:`FITFI`,decimals:18},rpcUrls:{default:{http:[`https://rpc.step.network`]}},blockExplorers:{default:{name:`Step Scan`,url:`https://stepscan.io`}},testnet:!1}),ej=L({id:1514,name:`Story`,nativeCurrency:{decimals:18,name:`IP Token`,symbol:`IP`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:340998},ensRegistry:{address:`0x5dc881dda4e4a8d312be3544ad13118d1a04cb17`,blockCreated:648924},ensUniversalResolver:{address:`0xddfb18888a9466688235887dec2a10c4f5effee9`,blockCreated:649114}},rpcUrls:{default:{http:[`https://mainnet.storyrpc.io`]}},blockExplorers:{default:{name:`Story explorer`,url:`https://storyscan.io`,apiUrl:`https://storyscan.io/api/v2`}},ensTlds:[`.ip`],testnet:!1}),tj=L({id:1315,name:`Story Aeneid`,network:`story-aeneid`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:1792},ensRegistry:{address:`0x5dC881dDA4e4a8d312be3544AD13118D1a04Cb17`,blockCreated:1322033},ensUniversalResolver:{address:`0x6D3B3F99177FB2A5de7F9E928a9BD807bF7b5BAD`,blockCreated:1322097}},rpcUrls:{default:{http:[`https://aeneid.storyrpc.io`]}},blockExplorers:{default:{name:`Story Aeneid Explorer`,url:`https://aeneid.storyscan.io`,apiUrl:`https://aeneid.storyscan.io/api/v2`}},ensTlds:[`.ip`],testnet:!0}),nj=L({id:1516,name:`Story Odyssey`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},rpcUrls:{default:{http:[`https://rpc.odyssey.storyrpc.io`]}},blockExplorers:{default:{name:`Story Odyssey Explorer`,url:`https://odyssey.storyscan.xyz`}},testnet:!0}),rj=L({id:1513,name:`Story Testnet`,nativeCurrency:{decimals:18,name:`IP`,symbol:`IP`},rpcUrls:{default:{http:[`https://testnet.storyrpc.io`]}},blockExplorers:{default:{name:`Story Testnet Explorer`,url:`https://testnet.storyscan.xyz`}},testnet:!0}),ij=L({id:105105,name:`Stratis Mainnet`,network:`stratis`,nativeCurrency:{name:`Stratis`,symbol:`STRAX`,decimals:18},rpcUrls:{default:{http:[`https://rpc.stratisevm.com`]}},blockExplorers:{default:{name:`Stratis Explorer`,url:`https://explorer.stratisevm.com`}}}),aj=L({id:964,name:`Subtensor EVM`,nativeCurrency:{decimals:18,name:`TAO`,symbol:`TAO`},rpcUrls:{default:{http:[`https://lite.chain.opentensor.ai`]}},blockExplorers:{default:{name:`Taostats EVM Explorer`,url:`https://evm.taostats.io`,apiUrl:`https://evm.taostats.io/api`}},testnet:!1}),oj=L({id:8866,name:`SuperLumio`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.lumio.io`]}},blockExplorers:{default:{name:`Lumio explorer`,url:`https://explorer.lumio.io`}},testnet:!1}),sj=L({id:55244,name:`Superposition`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.superposition.so`]}},blockExplorers:{default:{name:`Superposition Explorer`,url:`https://explorer.superposition.so`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:39}},testnet:!1}),cj=1,lj=L({...R,id:5330,name:`Superseed`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.superseed.xyz`]}},blockExplorers:{default:{name:`Superseed Explorer`,url:`https://explorer.superseed.xyz`,apiUrl:`https://explorer.superseed.xyz/api/v2`}},contracts:{...R.contracts,disputeGameFactory:{[cj]:{address:`0x8b097CF1f9BbD9cbFD0DD561858a1FCbC8857Be0`,blockCreated:20737481}},l2OutputOracle:{[cj]:{address:`0x693A0F8854F458D282DE3C5b69E8eE5EEE8aA949`,blockCreated:20737481}},portal:{[cj]:{address:`0x2c2150aa5c75A24fB93d4fD2F2a895D618054f07`,blockCreated:20737481}},l1StandardBridge:{[cj]:{address:`0x8b0576E39F1233679109F9b40cFcC2a7E0901Ede`,blockCreated:20737481}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`}},sourceId:cj}),uj=11155111,dj=L({...R,id:53302,name:`Superseed Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.superseed.xyz`]}},blockExplorers:{default:{name:`Superseed Sepolia Explorer`,url:`https://sepolia-explorer.superseed.xyz`,apiUrl:`https://sepolia-explorer.superseed.xyz/api/v2`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},portal:{[uj]:{address:`0x7A0db8C51432d2C3eb4e8f360a2EeB26FF2809fB`,blockCreated:5523438}},l1StandardBridge:{[uj]:{address:`0x2B227A603fAAdB3De0ED050b63ADD232B5f2c28C`,blockCreated:5523442}}},testnet:!0,sourceId:uj}),fj=L({id:763375,name:`Surge Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://l2-rpc.hoodi.surge.wtf`],webSocket:[`wss://l2-ws.hoodi.surge.wtf`]}},blockExplorers:{default:{name:`Surge Testnet Blockscout`,url:`https://explorer.hoodi.surge.wtf`}},testnet:!0}),pj=L({id:254,name:`Swan Chain Mainnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.swanchain.org`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://swanscan.io`}},testnet:!1}),mj=L({id:20241133,name:`Swan Proxima Testnet`,nativeCurrency:{name:`Swan Ether`,symbol:`sETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc-proxima.swanchain.io`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://proxima-explorer.swanchain.io`}},testnet:!0}),hj=L({id:2024,name:`Swan Saturn Testnet`,nativeCurrency:{name:`Swan Ether`,symbol:`sETH`,decimals:18},rpcUrls:{default:{http:[`https://saturn-rpc.swanchain.io`]}},blockExplorers:{default:{name:`Swan Explorer`,url:`https://saturn-explorer.swanchain.io`}},testnet:!0}),gj=L({...R,id:1923,name:`Swellchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://swell-mainnet.alt.technology`]}},blockExplorers:{default:{name:`Swell Explorer`,url:`https://explorer.swellnetwork.io`,apiUrl:`https://explorer.swellnetwork.io/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}}}),_j=L({...R,id:1924,name:`Swellchain Testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://swell-testnet.alt.technology`]}},blockExplorers:{default:{name:`Swellchain Testnet Explorer`,url:`https://swell-testnet-explorer.alt.technology`,apiUrl:`https://swell-testnet-explorer.alt.technology/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1}}}),vj=L({id:94,name:`SwissDLT Mainnet`,nativeCurrency:{decimals:18,name:`BCTS`,symbol:`BCTS`},rpcUrls:{default:{http:[`https://rpc.swissdlt.ch`]}},blockExplorers:{default:{name:`SwissDLT Explorer`,url:`https://explorer.swissdlt.ch`}},testnet:!1}),yj=L({id:57,name:`Syscoin Mainnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.syscoin.org`],webSocket:[`wss://rpc.syscoin.org/wss`]}},blockExplorers:{default:{name:`SyscoinExplorer`,url:`https://explorer.syscoin.org`,apiUrl:`https://explorer.syscoin.org/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:287139}}}),bj=L({id:5700,name:`Syscoin Tanenbaum Testnet`,nativeCurrency:{decimals:18,name:`Syscoin`,symbol:`SYS`},rpcUrls:{default:{http:[`https://rpc.tanenbaum.io`],webSocket:[`wss://rpc.tanenbaum.io/wss`]}},blockExplorers:{default:{name:`SyscoinTestnetExplorer`,url:`https://tanenbaum.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:271288}}}),xj=L({id:239,name:`TAC`,nativeCurrency:{name:`TAC`,symbol:`TAC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.ankr.com/tac`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://tac.blockscout.com`,apiUrl:`https://tac.blockscout.com/api`},native:{name:`TAC Explorer`,url:`https://explorer.tac.build`,apiUrl:`https://explorer.tac.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0}}}),Sj=L({id:2391,name:`TAC SPB Testnet`,nativeCurrency:{name:`TAC`,symbol:`TAC`,decimals:18},rpcUrls:{default:{http:[`https://spb.rpc.tac.build`]}},blockExplorers:{default:{name:`TAC`,url:`https://spb.explorer.tac.build`,apiUrl:`https://spb.explorer.tac.build/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:471429}},testnet:!0}),Cj=L({id:167e3,name:`Taiko Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.mainnet.taiko.xyz`],webSocket:[`wss://ws.mainnet.taiko.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://taikoscan.io`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:11269}}}),wj=L({id:167009,name:`Taiko Hekla L2`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hekla.taiko.xyz`]}},blockExplorers:{default:{name:`Taikoscan`,url:`https://hekla.taikoscan.network`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:59757}},testnet:!0}),Tj=L({id:167013,name:`Taiko Hoodi`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.hoodi.taiko.xyz`],webSocket:[`wss://ws.hoodi.taiko.xyz`]}},blockExplorers:{default:{name:`Etherscan`,url:`https://hoodi.taikoscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:581116}},testnet:!0}),Ej=L({id:167007,name:`Taiko Jolnir (Alpha-5 Testnet)`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.jolnir.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.jolnir.taiko.xyz`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:732706}},testnet:!0}),Dj=L({id:167008,name:`Taiko Katla (Alpha-6 Testnet)`,network:`tko-katla`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.katla.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.katla.taiko.xyz`}}}),Oj=L({id:167005,name:`Taiko (Alpha-3 Testnet)`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.test.taiko.xyz`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.test.taiko.xyz`}}}),kj=L({id:841,name:`Taraxa Mainnet`,nativeCurrency:{name:`Tara`,symbol:`TARA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.mainnet.taraxa.io`]}},blockExplorers:{default:{name:`Taraxa Explorer`,url:`https://explorer.mainnet.taraxa.io`}}}),Aj=L({id:842,name:`Taraxa Testnet`,nativeCurrency:{name:`Tara`,symbol:`TARA`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.taraxa.io`]}},blockExplorers:{default:{name:`Taraxa Explorer`,url:`https://explorer.testnet.taraxa.io`}},testnet:!0}),jj=L({id:10218,name:`Tea Sepolia`,nativeCurrency:{name:`Sepolia Tea`,symbol:`TEA`,decimals:18},rpcUrls:{default:{http:[`https://tea-sepolia.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Tea Sepolia Explorer`,url:`https://sepolia.tea.xyz`}},testnet:!0}),Mj=L({id:2017,name:`Telcoin Adiri Testnet`,nativeCurrency:{name:`Telcoin`,symbol:`TEL`,decimals:18},rpcUrls:{default:{http:[`https://rpc.telcoin.network`]}},blockExplorers:{default:{name:`telscan`,url:`https://telscan.io`}},testnet:!0}),Nj=L({id:40,name:`Telos`,nativeCurrency:{decimals:18,name:`Telos`,symbol:`TLOS`},rpcUrls:{default:{http:[`https://rpc.telos.net`]}},blockExplorers:{default:{name:`Teloscan`,url:`https://www.teloscan.io/`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:246530709}}}),Pj=L({id:41,name:`Telos`,nativeCurrency:{decimals:18,name:`Telos`,symbol:`TLOS`},rpcUrls:{default:{http:[`https://rpc.testnet.telos.net`]}},blockExplorers:{default:{name:`Teloscan (testnet)`,url:`https://testnet.teloscan.io/`}},testnet:!0});Ls(),wl(),Pl(),Wc(),xl();var Fj=Mc(BigInt(`0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff`)),Ij=Fj.create(BigInt(`-3`)),Lj=BigInt(`0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`),Rj=Cl({a:Ij,b:Lj,Fp:Fj,n:BigInt(`0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551`),Gx:BigInt(`0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296`),Gy:BigInt(`0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5`),h:BigInt(1),lowS:!1},Ps),zj=Rj,Bj=pl(Fj,{A:Ij,B:Lj,Z:Fj.create(BigInt(`-10`))}),Vj=Ml(zj.ProjectivePoint,e=>Bj(e[0]),{DST:`P256_XMD:SHA-256_SSWU_RO_`,encodeDST:`P256_XMD:SHA-256_SSWU_NU_`,p:Fj.ORDER,m:1,k:128,expand:`xmd`,hash:Ps}),Hj=Mc(BigInt(`0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff`)),Uj=Hj.create(BigInt(`-3`)),Wj=BigInt(`0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef`),Gj=Cl({a:Uj,b:Wj,Fp:Hj,n:BigInt(`0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973`),Gx:BigInt(`0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7`),Gy:BigInt(`0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f`),h:BigInt(1),lowS:!1},Is),Kj=pl(Hj,{A:Uj,B:Wj,Z:Hj.create(BigInt(`-12`))});Ml(Gj.ProjectivePoint,e=>Kj(e[0]),{DST:`P384_XMD:SHA-384_SSWU_RO_`,encodeDST:`P384_XMD:SHA-384_SSWU_NU_`,p:Hj.ORDER,m:1,k:192,expand:`xmd`,hash:Is});var qj=Mc(BigInt(`0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`)),Jj=qj.create(BigInt(`-3`)),Yj=BigInt(`0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00`),Xj=Cl({a:Jj,b:Yj,Fp:qj,n:BigInt(`0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409`),Gx:BigInt(`0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66`),Gy:BigInt(`0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650`),h:BigInt(1),lowS:!1,allowedPrivateKeyLengths:[130,131,132]},Fs),Zj=pl(qj,{A:Jj,B:Yj,Z:qj.create(BigInt(`-4`))});Ml(Xj.ProjectivePoint,e=>Zj(e[0]),{DST:`P521_XMD:SHA-512_SSWU_RO_`,encodeDST:`P521_XMD:SHA-512_SSWU_NU_`,p:qj.ORDER,m:1,k:256,expand:`xmd`,hash:Fs});var Qj=Rj,$j=Rj;Vj.hashToCurve,Vj.encodeToCurve,Hf();function eM(e){let{hash:t,payload:n,publicKey:r,signature:i}=e;return $j.verify(i,n instanceof Uint8Array?n:xf(n),og(r).substring(2),{lowS:!0,...t?{prehash:!0}:{}})}Hf();var tM=new TextEncoder;Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[t,e.charCodeAt(0)]);var nM={...Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[e.charCodeAt(0),t])),61:0,45:62,95:63};function rM(e){let t=e.replace(/=+$/,``),n=t.length,r=new Uint8Array(n+3);tM.encodeInto(t+`===`,r);for(let e=0,n=0;e>16,r[n+1]=t>>8&255,r[n+2]=t&255}let i=(n>>2)*3+(n%4&&n%4-1);return new Uint8Array(r.buffer,0,i)}Hf(),Qd(),pp();var iM=class extends P{constructor({majorType:e}){super(`Invalid CBOR major type: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidMajorTypeError`})}},aM=class extends P{constructor({additionalInfo:e}){super(`Invalid CBOR additional info: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidAdditionalInfoError`})}},oM=class extends P{constructor(){super(`64-bit integers are not supported in CBOR decoding.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.Unsupported64BitIntegerError`})}},sM=class extends P{constructor({tag:e}){super(`CBOR tagged data (tag ${e}) is not yet supported.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.UnsupportedTagError`})}},cM=class extends P{constructor({type:e}){super(`Invalid chunk type in indefinite-length ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidIndefiniteLengthChunkError`})}},lM=class extends P{constructor({value:e}){super(`Invalid CBOR simple value: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.InvalidSimpleValueError`})}},uM=class extends P{constructor(){super(`BigInt values are not supported in CBOR encoding.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.UnsupportedBigIntError`})}},dM=class extends P{constructor({token:e}){super(`Unexpected token: ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.UnexpectedTokenError`})}},fM=class extends P{constructor({number:e}){super(`Number exceeds maximum safe integer (${2**53-1}): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.NumberTooLargeError`})}},pM=class extends P{constructor({size:e}){super(`String length exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.StringTooLargeError`})}},mM=class extends P{constructor({size:e}){super(`Array length exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.ArrayTooLargeError`})}},hM=class extends P{constructor({size:e}){super(`Object size exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.ObjectTooLargeError`})}},gM=class extends P{constructor({size:e}){super(`Byte string length exceeds maximum (4294967295): ${e}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cbor.ByteStringTooLargeError`})}};function _M(e){if(e===void 0)return{length:1,encode:e=>e.pushUint8(247)};if(e===null)return{length:1,encode:e=>e.pushUint8(246)};if(typeof e==`boolean`)return{length:1,encode:t=>t.pushUint8(e?245:244)};if(typeof e==`number`)return _M.number(e);if(typeof e==`bigint`)throw new uM;if(typeof e==`string`)return _M.string(e);if(Array.isArray(e))return _M.array(e);if(e instanceof Uint8Array)return _M.byteString(e);if(e instanceof ArrayBuffer)return _M.byteString(new Uint8Array(e));if(ArrayBuffer.isView(e))return _M.byteString(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));if(e instanceof Map)return _M.map(e);if(typeof e==`object`)return _M.object(e);throw new dM({token:String(e)})}(function(e){function t(e){if(!Number.isSafeInteger(e)){let t=Math.fround(e);return Number.isNaN(e)||e===t?{length:5,encode(t){t.pushUint8(250),t.dataView.setFloat32(t.position,e,!1),t.position+=4}}:{length:9,encode(t){t.pushUint8(251),t.dataView.setFloat64(t.position,e,!1),t.position+=8}}}if(e>=0){if(e<=23)return{length:1,encode:t=>t.pushUint8(e)};if(e<=255)return{length:2,encode:t=>{t.pushUint8(24),t.pushUint8(e)}};if(e<=65535)return{length:3,encode:t=>{t.pushUint8(25),t.pushUint16(e)}};if(e<=4294967295)return{length:5,encode:t=>{t.pushUint8(26),t.pushUint32(e)}};throw new fM({number:e.toString(10)})}let t=-1-e;if(e>=-24)return{length:1,encode:e=>e.pushUint8(32+t)};if(t<=255)return{length:2,encode:e=>{e.pushUint8(56),e.pushUint8(t)}};if(t<=65535)return{length:3,encode:e=>{e.pushUint8(57),e.pushUint16(t)}};if(t<=4294967295)return{length:5,encode:e=>{e.pushUint8(58),e.pushUint32(t)}};throw new fM({number:e.toString(10)})}e.number=t;function n(e){let t=Sf(e),n=t.length;if(n<=23)return{length:1+n,encode(e){e.pushUint8(96+n),n>0&&e.pushBytes(t)}};if(n<=255)return{length:2+n,encode(e){e.pushUint8(120),e.pushUint8(n),e.pushBytes(t)}};if(n<=65535)return{length:3+n,encode(e){e.pushUint8(121),e.pushUint16(n),e.pushBytes(t)}};if(n<=4294967295)return{length:5+n,encode(e){e.pushUint8(122),e.pushUint32(n),e.pushBytes(t)}};throw new pM({size:n})}e.string=n;function r(t){let n=t.map(t=>e(t)),r=n.reduce((e,t)=>e+t.length,0),i=t.length;if(i<=23)return{length:1+r,encode(e){e.pushUint8(128+i);for(let t of n)t.encode(e)}};if(i<=255)return{length:2+r,encode(e){e.pushUint8(152),e.pushUint8(i);for(let t of n)t.encode(e)}};if(i<=65535)return{length:3+r,encode(e){e.pushUint8(153),e.pushUint16(i);for(let t of n)t.encode(e)}};if(i<=4294967295)return{length:5+r,encode(e){e.pushUint8(154),e.pushUint32(i);for(let t of n)t.encode(e)}};throw new mM({size:i})}e.array=r;function i(e){let t=e.byteLength;if(t<=23)return{length:1+t,encode(n){n.pushUint8(64+t),n.pushBytes(e)}};if(t<=255)return{length:2+t,encode(n){n.pushUint8(88),n.pushUint8(t),n.pushBytes(e)}};if(t<=65535)return{length:3+t,encode(n){n.pushUint8(89),n.pushUint16(t),n.pushBytes(e)}};if(t<=4294967295)return{length:5+t,encode(n){n.pushUint8(90),n.pushUint32(t),n.pushBytes(e)}};throw new gM({size:t})}e.byteString=i;function a(t){let n=Object.keys(t),r=n.map(n=>({key:e(n),value:e(t[n])})),i=r.reduce((e,t)=>e+t.key.length+t.value.length,0),a=n.length;if(a<=23)return{length:1+i,encode(e){e.pushUint8(160+a);for(let t of r)t.key.encode(e),t.value.encode(e)}};if(a<=255)return{length:2+i,encode(e){e.pushUint8(184),e.pushUint8(a);for(let t of r)t.key.encode(e),t.value.encode(e)}};if(a<=65535)return{length:3+i,encode(e){e.pushUint8(185),e.pushUint16(a);for(let t of r)t.key.encode(e),t.value.encode(e)}};if(a<=4294967295)return{length:5+i,encode(e){e.pushUint8(186),e.pushUint32(a);for(let t of r)t.key.encode(e),t.value.encode(e)}};throw new hM({size:a})}e.object=a;function o(t){let n=[];for(let[r,i]of t)n.push({key:e(r),value:e(i)});let r=n.reduce((e,t)=>e+t.key.length+t.value.length,0),i=t.size;if(i<=23)return{length:1+r,encode(e){e.pushUint8(160+i);for(let t of n)t.key.encode(e),t.value.encode(e)}};if(i<=255)return{length:2+r,encode(e){e.pushUint8(184),e.pushUint8(i);for(let t of n)t.key.encode(e),t.value.encode(e)}};if(i<=65535)return{length:3+r,encode(e){e.pushUint8(185),e.pushUint16(i);for(let t of n)t.key.encode(e),t.value.encode(e)}};if(i<=4294967295)return{length:5+r,encode(e){e.pushUint8(186),e.pushUint32(i);for(let t of n)t.key.encode(e),t.value.encode(e)}};throw new hM({size:i})}e.map=o})(_M||={});function vM(e){let t=e.readUint8(),n=t>>5,r=t&31;switch(n){case 0:return vM.readUnsignedInteger(e,r);case 1:return vM.readNegativeInteger(e,r);case 2:return vM.readByteString(e,r);case 3:return vM.readTextString(e,r);case 4:return vM.readArray(e,r);case 5:return vM.readMap(e,r);case 6:throw new sM({tag:r});case 7:return vM.readSimpleOrFloat(e,r);default:throw new iM({majorType:n})}}(function(e){function t(e,t){if(t<24)return t;if(t===24)return e.readUint8();if(t===25)return e.readUint16();if(t===26)return e.readUint32();throw t===27?new oM:new aM({additionalInfo:t})}function n(e,n){return t(e,n)}e.readUnsignedInteger=n;function r(e,n){return-1-t(e,n)}e.readNegativeInteger=r;function i(n,r){if(r===31){let t=[],r=0;for(;;){if(n.inspectUint8()===255){n.readUint8();break}let i=e(n);if(!(i instanceof Uint8Array))throw new cM({type:`byte string`});t.push(i),r+=i.length}let i=new Uint8Array(r),a=0;for(let e of t)i.set(e,a),a+=e.length;return i}let i=t(n,r);return n.readBytes(i)}e.readByteString=i;function a(n,r){if(r===31){let t=[];for(;;){if(n.inspectUint8()===255){n.readUint8();break}let r=e(n);if(typeof r!=`string`)throw new cM({type:`text string`});t.push(r)}return t.join(``)}let i=t(n,r);return jf(n.readBytes(i))}e.readTextString=a;function o(n,r){if(r===31){let t=[];for(;;){if(n.inspectUint8()===255){n.readUint8();break}t.push(e(n))}return t}let i=t(n,r),a=[];for(let t=0;t>15&1,n=e>>10&31,r=e&1023;if(n===0){if(r===0)return t?-0:0;let e=2**-14*(r/1024);return t?-e:e}if(n===31)return r===0?t?-1/0:1/0:NaN;let i=2**(n-15)*(1+r/1024);return t?-i:i}})(vM||={}),Hf(),Uint8Array.from([105,171,180,181,160,222,75,198,42,42,32,31,141,37,186,233]),pp(),Hf(),pp();function yM(e){let{challenge:t,metadata:n,origin:r,publicKey:i,rpId:a,signature:o}=e,{authenticatorData:s,clientDataJSON:c,userVerificationRequired:l}=n,u=xf(s);if(u.length<37||a!==void 0&&!Cf(u.slice(0,32),$h(Jf(a),{as:`Bytes`})))return!1;let d=u[32];if((d&1)!=1||l&&(d&4)!=4||(d&8)!=8&&(d&16)==16)return!1;let f=JSON.parse(c);return f.type!==`webauthn.get`||!f.challenge||qf(rM(f.challenge))!==t||r!==void 0&&!(Array.isArray(r)?r:[r]).includes(f.origin)?!1:eM({hash:!0,payload:vf(u,$h(Sf(c),{as:`Bytes`})),publicKey:i,signature:o})}function bM(e){return yM(e)}Qd(),pp(),gf();var xM=`0x01`,SM=`0x02`,CM=`0x03`,wM=`0x04`,TM=`0x7777777777777777777777777777777777777777777777777777777777777777`;function EM(e){let{signature:t,root:n}=e;return t.type===`keychain`?n?t.userAddress:EM({...e,signature:t.inner}):gg(DM(e))}function DM(e){let{payload:t,signature:n}=e;switch(n.type){case`secp256k1`:return K_({payload:t,signature:n.signature});case`p256`:case`webAuthn`:return n.publicKey;case`keychain`:return DM({payload:t,signature:n.inner})}}function OM(e){let t=e.endsWith(`7777777777777777777777777777777777777777777777777777777777777777`)?I(e,0,-Qf(TM)):e;if(Qf(t)===65){let e=C_(t);return x_(e),{signature:e,type:`secp256k1`}}let n=I(t,0,1),r=I(t,1),i=Qf(r);if(n===xM){if(i!==129)throw new IM({reason:`Invalid P256 signature envelope size: expected 129 bytes, got ${i} bytes`,serialized:t});return{publicKey:{prefix:4,x:ep(I(r,64,96)),y:ep(I(r,96,128))},prehash:tp(I(r,128,129))!==0,signature:{r:ep(I(r,0,32)),s:ep(I(r,32,64))},type:`p256`}}if(n===SM){if(i<128)throw new IM({reason:`Invalid WebAuthn signature envelope size: expected at least 128 bytes, got ${i} bytes`,serialized:t});let e=i-128,n=I(r,0,e),a,o;for(let t=37;t{let n=e.inner;return n.type===`p256`||n.type===`webAuthn`?{keyId:gg(n.publicKey)}:n.type===`secp256k1`&&t?.payload?{keyId:gg(K_({payload:t.payload,signature:n.signature}))}:{}})()}:{},type:n}}function AM(e){if(e.type===`secp256k1`)return{signature:D_(e),type:`secp256k1`};if(e.type===`p256`)return{prehash:e.preHash,publicKey:{prefix:4,x:ep(e.pubKeyX),y:ep(e.pubKeyY)},signature:{r:ep(e.r),s:ep(e.s)},type:`p256`};if(e.type===`webAuthn`){let t=e.webauthnData,n=Qf(t),r,i;for(let e=37;e{if(t.address)return t.address;if(t.publicKey)return gg(t.publicKey)})();if(!r)return!1;let i=kM(e);if(i.type===`secp256k1`)return r?q_({address:r,payload:n,signature:i.signature}):!1;if(i.type===`p256`)return _g(gg(i.publicKey),r)?eM({hash:i.prehash,publicKey:i.publicKey,payload:n,signature:i.signature}):!1;if(i.type===`webAuthn`)return _g(gg(i.publicKey),r)?bM({challenge:Gf(n),metadata:i.metadata,publicKey:i.publicKey,signature:i.signature}):!1;throw new LM(`Unable to verify signature envelope of type "${i.type}".`)}var FM=class extends P{constructor({envelope:e}){super(`Unable to coerce value (\`${mf(e)}\`) to a valid signature envelope.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureEnvelope.CoercionError`})}},IM=class extends P{constructor({reason:e,serialized:t}){super(`Unable to deserialize signature envelope: ${e}`,{metaMessages:[`Serialized: ${t}`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureEnvelope.InvalidSerializedError`})}},LM=class extends P{constructor(){super(...arguments),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureEnvelope.VerificationError`})}};Qd(),pp();function RM(e){return e.startsWith(`tempo`)?zM(e).address:e}function zM(e){if(!e.startsWith(`tempox`))throw new BM({address:e});let t=e.slice(6);return Uf(t,{strict:!0}),{address:mg(t)}}var BM=class extends P{constructor({address:e}){super(`Tempo address "${e}" has an invalid prefix. Expected "tempox".`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TempoAddress.InvalidPrefixError`})}};pp();function VM(e,t={}){if(typeof e.chainId==`string`)return HM(e);let n={...e,address:RM(e.address)};return t.signature?{...n,signature:t.signature}:n}function HM(e){let{address:t,chainId:n,nonce:r}=e,i=AM(e.signature);return{address:t,chainId:Number(n),nonce:BigInt(r),signature:i}}function UM(e){return e.map(e=>HM(e))}function WM(e){let[t,n,r,i]=e,a={address:n,chainId:t===`0x`?0:Number(t),nonce:r===`0x`?0n:BigInt(r)};return i&&(a.signature=OM(i)),VM(a)}function GM(e){let t=[];for(let n of e)t.push(WM(n));return t}function KM(e){let{address:t,chainId:n,nonce:r,signature:i}=e;return{address:t,chainId:F(n),nonce:F(r),signature:NM(i)}}function qM(e){return e.map(e=>KM(e))}function JM(e){let{address:t,chainId:n,nonce:r}=e,i=e.signature?MM(e.signature):void 0;return[n?F(n):`0x`,t,r?F(r):`0x`,...i?[i]:[]]}function YM(e){if(!e||e.length===0)return[];let t=[];for(let n of e)t.push(JM(n));return t}pp();function XM(e,t={}){if(`keyId`in e)return ZM(e);let n=e,r={...n,address:RM(n.address),...n.limits?{limits:n.limits.map(e=>({...e,token:RM(e.token)}))}:{}};return t.signature?{...r,signature:kM(t.signature)}:r}function ZM(e){let{chainId:t,keyId:n,expiry:r=0,limits:i,keyType:a}=e,o=AM(e.signature);return{address:n,chainId:t===`0x`?0n:ep(t),expiry:Number(r),limits:i?.map(e=>({token:e.token,limit:BigInt(e.limit)})),signature:o,type:a}}function QM(e){let[t,n]=e,[r,i,a,o,s]=t,c=(()=>{switch(i){case`0x`:case`0x00`:return`secp256k1`;case`0x01`:return`p256`;case`0x02`:return`webAuthn`;default:throw Error(`Invalid key type: ${i}`)}})(),l={address:a,expiry:o===void 0?void 0:iN(o),type:c,chainId:r===`0x`?0n:ep(r),...o===void 0?{}:{expiry:iN(o)},...s===void 0?{}:{limits:s.map(([e,t])=>({token:e,limit:rN(t)}))}};return n&&(l.signature=OM(n)),XM(l)}function $M(e){let{address:t,chainId:n,expiry:r,limits:i,type:a,signature:o}=e;return{chainId:n===0n?`0x`:F(n),expiry:typeof r==`number`?F(r):null,limits:i?.map(({token:e,limit:t})=>({token:e,limit:F(t)})),keyId:RM(t),signature:NM(o),keyType:a}}function eN(e){let{address:t,chainId:n,expiry:r,limits:i}=e,a=e.signature?MM(e.signature):void 0,o=(()=>{switch(e.type){case`secp256k1`:return`0x`;case`p256`:return`0x01`;case`webAuthn`:return`0x02`;default:throw Error(`Invalid key type: ${e.type}`)}})(),s=i?.map(e=>[e.token,tN(e.limit)]);return[[tN(n),o,t,typeof r==`number`||s?nN(r??0):void 0,s].filter(Boolean),...a?[a]:[]]}function tN(e){return e===0n?`0x`:F(e)}function nN(e){return e===0?`0x`:F(e)}function rN(e){return e===`0x`?0n:BigInt(e)}function iN(e){return e===`0x`?0:tp(e)}pp();var aN=`0x20c0`;function oN(e){if(typeof e==`string`){let t=RM(e);return pg(t),t}return Wf(aN,F(e,{size:18}))}var sN={legacy:`0x0`,eip2930:`0x1`,eip1559:`0x2`,eip4844:`0x3`,eip7702:`0x4`},cN={"0x0":`legacy`,"0x1":`eip2930`,"0x2":`eip1559`,"0x3":`eip4844`,"0x4":`eip7702`};function lN(e,t={}){if(!e)return null;let n=w_(e),r={...e,...n};return r.blockNumber=e.blockNumber?BigInt(e.blockNumber):null,r.data=e.input,r.gas=BigInt(e.gas??0n),r.nonce=BigInt(e.nonce??0n),r.transactionIndex=e.transactionIndex?Number(e.transactionIndex):null,r.value=BigInt(e.value??0n),e.authorizationList&&(r.authorizationList=H_(e.authorizationList)),e.chainId&&(r.chainId=Number(e.chainId)),e.gasPrice&&(r.gasPrice=BigInt(e.gasPrice)),e.maxFeePerBlobGas&&(r.maxFeePerBlobGas=BigInt(e.maxFeePerBlobGas)),e.maxFeePerGas&&(r.maxFeePerGas=BigInt(e.maxFeePerGas)),e.maxPriorityFeePerGas&&(r.maxPriorityFeePerGas=BigInt(e.maxPriorityFeePerGas)),e.type&&(r.type=cN[e.type]??e.type),n&&(r.v=N_(n.yParity)),r}var uN={...sN,tempo:`0x76`},dN={...cN,"0x76":`tempo`};function fN(e,t={}){if(!e)return null;let n=lN(e);return n.type=dN[e.type],e.aaAuthorizationList&&(n.authorizationList=UM(e.aaAuthorizationList),delete n.aaAuthorizationList),e.calls&&(n.calls=e.calls.map(e=>({to:e.to,value:e.value&&e.value!==`0x`?BigInt(e.value):void 0,data:e.input||e.data||`0x`}))),e.feeToken&&(n.feeToken=e.feeToken),e.nonceKey&&(n.nonceKey=BigInt(e.nonceKey)),e.signature&&(n.signature=AM(e.signature)),e.validAfter&&(n.validAfter=Number(e.validAfter)),e.validBefore&&(n.validBefore=Number(e.validBefore)),e.keyAuthorization&&(n.keyAuthorization=ZM(e.keyAuthorization)),e.feePayerSignature&&(n.feePayerSignature=D_(e.feePayerSignature),n.feePayerSignature.v=N_(n.feePayerSignature.yParity)),n}pp();function pN(e){let t={};return e.accessList!==void 0&&(t.accessList=e.accessList),e.authorizationList!==void 0&&(t.authorizationList=W_(e.authorizationList)),e.blobVersionedHashes!==void 0&&(t.blobVersionedHashes=e.blobVersionedHashes),e.blobs!==void 0&&(t.blobs=e.blobs),e.chainId!==void 0&&(t.chainId=F(e.chainId)),e.data===void 0?e.input!==void 0&&(t.data=e.input,t.input=e.input):(t.data=e.data,t.input=e.data),e.from!==void 0&&(t.from=e.from),e.gas!==void 0&&(t.gas=F(e.gas)),e.gasPrice!==void 0&&(t.gasPrice=F(e.gasPrice)),e.maxFeePerBlobGas!==void 0&&(t.maxFeePerBlobGas=F(e.maxFeePerBlobGas)),e.maxFeePerGas!==void 0&&(t.maxFeePerGas=F(e.maxFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(t.maxPriorityFeePerGas=F(e.maxPriorityFeePerGas)),e.maxPriorityFeePerGas!==void 0&&(t.maxPriorityFeePerGas=F(e.maxPriorityFeePerGas)),e.nonce!==void 0&&(t.nonce=F(e.nonce)),e.to!==void 0&&(t.to=e.to),e.type!==void 0&&(t.type=sN[e.type]||e.type),e.value!==void 0&&(t.value=F(e.value)),t}pp();function mN(e){let t=pN({...e,authorizationList:void 0});e.authorizationList&&(t.authorizationList=qM(e.authorizationList)),e.calls&&(t.calls=e.calls.map(e=>({to:e.to?RM(e.to):e.to,value:e.value?F(e.value):`0x`,data:e.data??`0x`}))),e.feeToken!==void 0&&(t.feeToken=oN(e.feeToken)),e.keyAuthorization&&(t.keyAuthorization=$M(e.keyAuthorization)),e.validBefore!==void 0&&(t.validBefore=F(e.validBefore)),e.validAfter!==void 0&&(t.validAfter=F(e.validAfter));let n=(()=>{if(e.nonceKey===`random`)return Zf(6);if(typeof e.nonceKey==`bigint`)return F(e.nonceKey)})();return n&&(t.nonceKey=n),(e.calls!==void 0||e.feeToken!==void 0||e.keyAuthorization!==void 0||e.nonceKey!==void 0||e.validBefore!==void 0||e.validAfter!==void 0||e.type===`tempo`)&&(t.type=uN.tempo,delete t.data,delete t.input,delete t.to,delete t.value),t}Qd(),pp();function hN(e){let t=[];for(let n=0;neg(e)?e:$f(e))})}return t}function gN(e){if(!e||e.length===0)return[];let t=[];for(let{address:n,storageKeys:r}of e){for(let e=0;et===void 0?e:void 0).filter(Boolean);super(`Invalid serialized transaction of type "${n}" was provided.`,{metaMessages:[`Serialized Transaction: "${t}"`,r.length>0?`Missing Attributes: ${r.join(`, `)}`:``].filter(Boolean)}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TransactionEnvelope.InvalidSerializedError`})}},wN=class extends P{constructor({maxPriorityFeePerGas:e,maxFeePerGas:t}={}){super([`The provided tip (\`maxPriorityFeePerGas\`${e?` = ${bN(e)} gwei`:``}) cannot be higher than the fee cap (\`maxFeePerGas\`${t?` = ${bN(t)} gwei`:``}).`].join(` -`)),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TransactionEnvelope.TipAboveFeeCapError`})}};Qd(),pp();var TN=`0x78`,EN=`0x76`,DN=`tempo`;function ON(e){let{calls:t,chainId:n,maxFeePerGas:r,maxPriorityFeePerGas:i,validBefore:a,validAfter:o}=e;if(!t||t.length===0)throw new FN;if(typeof a==`number`&&typeof o==`number`&&a<=o)throw new IN({validBefore:a,validAfter:o});if(t)for(let e of t)e.to&&pg(e.to,{strict:!1});if(n<=0)throw new SN({chainId:n});if(r&&BigInt(r)>2n**256n-1n)throw new xN({feeCap:r});if(i&&r&&i>r)throw new wN({maxFeePerGas:r,maxPriorityFeePerGas:i})}function kN(e){let t=u_(I(e,1)),[n,r,i,a,o,s,c,l,u,d,f,p,m,h,g]=t,_=Array.isArray(h)?h:void 0,v=_?g:h;if(!(t.length===13||t.length===14||t.length===15))throw new CN({attributes:{authorizationList:m,chainId:n,maxPriorityFeePerGas:r,maxFeePerGas:i,gas:a,calls:o,accessList:s,keyAuthorization:_,nonceKey:c,nonce:l,validBefore:u,validAfter:d,feeToken:f,feePayerSignatureOrSender:p,...t.length>12?{signature:v}:{}},serialized:e,type:DN});let y={chainId:Number(n),type:DN};rp(a)&&a!==`0x`&&(y.gas=BigInt(a)),rp(l)&&(y.nonce=l===`0x`?0n:BigInt(l)),rp(i)&&i!==`0x`&&(y.maxFeePerGas=BigInt(i)),rp(r)&&r!==`0x`&&(y.maxPriorityFeePerGas=BigInt(r)),rp(c)&&(y.nonceKey=c===`0x`?0n:BigInt(c)),rp(u)&&u!==`0x`&&(y.validBefore=Number(u)),rp(d)&&d!==`0x`&&(y.validAfter=Number(d)),rp(f)&&f!==`0x`&&(y.feeToken=f),o&&o!==`0x`&&(y.calls=o.map(e=>{let[t,n,r]=e,i={};return t&&t!==`0x`&&(i.to=t),n&&n!==`0x`&&(i.value=BigInt(n)),r&&r!==`0x`&&(i.data=r),i})),s?.length!==0&&s!==`0x`&&(y.accessList=hN(s)),m?.length!==0&&m!==`0x`&&(y.authorizationList=GM(m)),p!==`0x`&&p!==void 0&&(p===`0x00`||vg(p)?(y.feePayerSignature=null,vg(p)&&(y.from=p)):y.feePayerSignature=O_(p)),_&&(y.keyAuthorization=QM(_));let b=v?OM(v):void 0;if(b&&(y={...y,signature:b}),!y.from&&b)try{y.from=EM({payload:MN(AN(y)),signature:b,root:!0})}catch{}return ON(y),y}function AN(e,t={}){let{feePayerSignature:n,signature:r}=t,i=typeof e==`string`?kN(e):e;return i.from&&=RM(i.from),i.calls&&=i.calls.map(e=>({...e,...e.to?{to:RM(e.to)}:{}})),ON(i),{...i,...r?{signature:kM(r)}:{},...n?{feePayerSignature:T_(n)}:{},type:`tempo`}}function jN(e,t={}){let{accessList:n,authorizationList:r,calls:i,chainId:a,feeToken:o,gas:s,keyAuthorization:c,nonce:l,nonceKey:u,maxFeePerGas:d,maxPriorityFeePerGas:f,validBefore:p,validAfter:m}=e;ON(e);let h=gN(n),g=t.signature||e.signature,_=YM(r),v=i.map(e=>[e.to?RM(e.to):`0x`,e.value?F(e.value):`0x`,e.data??`0x`]),y=!1,b=(()=>{if(t.sender)return t.sender;if(t.format===`feePayer`&&g){let t=kM(g);if(t.type===`keychain`)return t.userAddress;if(t.type===`p256`||t.type===`webAuthn`)return gg(t.publicKey);if(t.type===`secp256k1`)return G_({payload:MN(AN(e)),signature:t.signature})}let n=t.feePayerSignature===void 0?e.feePayerSignature:t.feePayerSignature;return n===null?(y=!0,`0x00`):n?j_(n):`0x`})(),x=[F(a),f?F(f):`0x`,d?F(d):`0x`,s?F(s):`0x`,v,h,u?F(u):`0x`,l?F(l):`0x`,typeof p==`number`?F(p):`0x`,typeof m==`number`?F(m):`0x`,!y&&(typeof o==`bigint`||typeof o==`string`)?oN(o):`0x`,b,_,...c?[eN(c)]:[],...g?[MM(kM(g))]:[]];return Wf(t.format===`feePayer`?TN:EN,g_(x))}function MN(e,t={}){let n=NN(e,{presign:!0});return t.from?Qh(Wf(`0x04`,n,RM(t.from))):n}function NN(e,t={}){return Qh(jN({...e,...t.presign?{signature:void 0,...e.feePayerSignature===void 0?{}:{feePayerSignature:null}}:{}}))}function PN(e,t){let n=RM(t.sender);return Qh(jN({...e,signature:void 0},{sender:n,format:`feePayer`}))}var FN=class extends P{constructor(){super(`Calls list cannot be empty.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TxEnvelopeTempo.CallsEmptyError`})}},IN=class extends P{constructor({validBefore:e,validAfter:t}){super(`validBefore (${e}) must be greater than validAfter (${t}).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TxEnvelopeTempo.InvalidValidityWindowError`})}};pp();function LN(e){let t=e.account;return t?.keyType&&t.keyType!==`secp256k1`||e.calls!==void 0||e.feePayer!==void 0||e.feeToken!==void 0||e.keyAuthorization!==void 0||e.nonceKey!==void 0||e.signature!==void 0||e.validBefore!==void 0||e.validAfter!==void 0?`tempo`:e.type?e.type:Nd(e)}function RN(e){try{return LN(e)===`tempo`}catch{return!1}}async function zN(e,t){if(!RN(e)){if(t&&`type`in t&&t.type!==`secp256k1`)throw Error("Unsupported signature type. Expected `secp256k1` but got `"+t.type+"`.");if(t&&`type`in t){let{r:n,s:r,yParity:i}=t?.signature;return mh(e,{r:F(n,{size:32}),s:F(r,{size:32}),yParity:i})}return mh(e,t)}if(LN(e)===`tempo`)return BN(e,t);throw Error(`Unsupported transaction type`)}async function BN(e,t){let n=(()=>{if(e.signature)return e.signature;if(t&&`type`in t)return t;if(t)return kM({r:BigInt(t.r),s:BigInt(t.s),yParity:Number(t.yParity)})})(),{chainId:r,feePayer:i,feePayerSignature:a,nonce:o,...s}=e,c={...s,calls:s.calls?.length?s.calls:[{to:s.to||(!s.data||s.data===`0x`?`0x0000000000000000000000000000000000000000`:void 0),value:s.value,data:s.data}],chainId:Number(r),feePayerSignature:a?{r:BigInt(a.r),s:BigInt(a.s),yParity:Number(a.yParity)}:i?null:void 0,type:`tempo`,...o?{nonce:BigInt(o)}:{}};if(i===!0&&delete c.feeToken,n&&typeof e.feePayer==`object`){let t=AN(c,{signature:n}),r=PN(t,{sender:(()=>{if(e.from)return e.from;if(n.type===`secp256k1`)return G_({payload:MN(t),signature:n.signature});throw Error(`Unable to extract sender from transaction or signature.`)})()});return jN(t,{feePayerSignature:T_(await e.feePayer.sign({hash:r}))})}return i===!0?n?jN(c,{format:`feePayer`,sender:e.from,signature:n}):jN(c,{feePayerSignature:null}):jN({...c,...i?{feeToken:void 0}:{}},{feePayerSignature:void 0,signature:n})}pp(),oa(),Ku();function VN(e){if(!RN(e))return sd(e);let{feePayerSignature:t,gasPrice:n,nonce:r,...i}=fN(e);return{...i,accessList:i.accessList,feePayerSignature:t?{r:F(t.r,{size:32}),s:F(t.s,{size:32}),v:BigInt(t.v??27),yParity:t.yParity}:void 0,nonce:Number(r),typeHex:uN[i.type],type:i.type}}function HN(e){return Gm(e)}function UN(e,t){let n=e,r=n.account?aa(n.account):void 0;if(!RN(n))return Hu(e,t);t&&(n.calls=n.calls??[{to:e.to||(!e.data||e.data===`0x`?`0x0000000000000000000000000000000000000000`:void 0),value:e.value,data:e.data}]),n.feePayer===!0&&delete n.feeToken;let i=mN({...n,type:`tempo`});t===`estimateGas`&&(i.maxFeePerGas=void 0,i.maxPriorityFeePerGas=void 0),i.to=void 0,i.data=void 0,i.value=void 0;let[a,o]=(()=>{let e=r&&`keyType`in r?r.keyType:r?.source;return e?e===`webAuthn`?[`webAuthn`,`0x${`ff`.repeat(1400)}`]:[`p256`,`secp256k1`].includes(e)?[e,void 0]:[void 0,void 0]:[void 0,void 0]})(),s=r&&`accessKeyAddress`in r?r.accessKeyAddress:void 0;return{...i,...o?{keyData:o}:{},...s?{keyId:s}:{},...a?{keyType:a}:{},...n.feePayer?{feePayer:typeof n.feePayer==`object`?aa(n.feePayer):n.feePayer}:{}}}var WN=new Map;async function GN(e){WN.set(e,(WN.get(e)??0)+1),await Promise.resolve();let t=(WN.get(e)??0)>1;return queueMicrotask(()=>{let t=WN.get(e)??0;t<=1?WN.delete(e):WN.set(e,t-1)}),t}$u(),Ku();var KN=25,qN={blockTime:1e3,extendSchema:Dh(),formatters:{transaction:cd({exclude:[`aaAuthorizationList`],format:VN}),transactionReceipt:Km({format:HN}),transactionRequest:Gu({format:UN})},prepareTransactionRequest:[async(e,{phase:t})=>{let n=e;return t===`afterFillParameters`?(n.feePayer&&n.keyAuthorization?.signature.type===`webAuthn`&&(n.gas=(n.gas??0n)+20000n),n):(await(async()=>{if(n.nonceKey===`expiring`||n.feePayer&&n.nonceKey===void 0)return!0;let e=n.account?.address;return e&&n.nonceKey===void 0?await GN(e):!1})()?(n.nonceKey=Qu,n.nonce=0,n.validBefore===void 0&&(n.validBefore=Math.floor(Date.now()/1e3)+KN)):n.nonceKey!==void 0&&(n.nonce=typeof n.nonce==`number`?n.nonce:0),!n.feeToken&&n.chain?.feeToken&&(n.feeToken=n.chain.feeToken),n)},{runAt:[`beforeFillTransaction`,`afterFillParameters`]}],serializers:{transaction:((e,t)=>zN(e,t))},async verifyHash(e,t){let{address:n,hash:r,signature:i}=t;if(typeof i==`string`&&i.endsWith(`7777777777777777777777777777777777777777777777777777777777777777`)){let a=OM(i);if(a.type!==`keychain`){let i=await ih(e,{address:n,blockNumber:t.blockNumber,blockTag:t.blockTag});if(!i||i===`0xef01007702c00000000000000000000000000000000000`)return PM(a,{address:n,payload:r})}}return await T(e,vv,`verifyHash`)({...t,chain:null})}},JN=L({...qN,id:4217,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.tempo.xyz`}},name:`Tempo Mainnet`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.presto.tempo.xyz`],webSocket:[`wss://rpc.presto.tempo.xyz`]}}}),YN=L({...qN,id:42429,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.testnet.tempo.xyz`}},name:`Tempo Testnet (Andantino)`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.testnet.tempo.xyz`],webSocket:[`wss://rpc.testnet.tempo.xyz`]}}}),XN=L({...qN,id:31318,name:`Tempo Devnet`,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.devnet.tempo.xyz`}},nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.devnet.tempoxyz.dev`],webSocket:[`wss://rpc.devnet.tempoxyz.dev`]}}}),ZN=L({...qN,id:1337,name:`Tempo`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`http://localhost:8545`]}}}),QN=L({...qN,id:42431,blockExplorers:{default:{name:`Tempo Explorer`,url:`https://explore.moderato.tempo.xyz`}},name:`Tempo Testnet (Moderato)`,nativeCurrency:{name:`USD`,symbol:`USD`,decimals:6},rpcUrls:{default:{http:[`https://rpc.moderato.tempo.xyz`],webSocket:[`wss://rpc.moderato.tempo.xyz`]}}}),$N=L({id:1559,name:`Tenet`,network:`tenet-mainnet`,nativeCurrency:{name:`TENET`,symbol:`TENET`,decimals:18},rpcUrls:{default:{http:[`https://rpc.tenet.org`]}},blockExplorers:{default:{name:`TenetScan Mainnet`,url:`https://tenetscan.io`,apiUrl:`https://tenetscan.io/api`}},testnet:!1}),eP=L({id:752025,name:`Ternoa`,nativeCurrency:{name:`Capsule Coin`,symbol:`CAPS`,decimals:18},rpcUrls:{default:{http:[`https://rpc-mainnet.zkevm.ternoa.network`]}},blockExplorers:{default:{name:`Ternoa Explorer`,url:`https://explorer-mainnet.zkevm.ternoa.network`}},testnet:!1}),tP=L({id:7,name:`ThaiChain`,nativeCurrency:{name:`TCH`,symbol:`TCH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.thaichain.org`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://exp.thaichain.org`,apiUrl:`https://exp.thaichain.org/api`}},contracts:{multicall3:{address:`0x0DaD6130e832c21719C5CE3bae93454E16A84826`,blockCreated:4806386}},testnet:!1}),nP=L({id:8428,name:`THAT Mainnet`,nativeCurrency:{name:`THAT`,symbol:`THAT`,decimals:18},rpcUrls:{default:{http:[`https://api.thatchain.io/mainnet`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://that.blockscout.com`}},testnet:!1}),rP=L({id:361,name:`Theta Mainnet`,nativeCurrency:{name:`TFUEL`,symbol:`TFUEL`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-api.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`Theta Explorer`,url:`https://explorer.thetatoken.org`}},testnet:!1}),iP=L({id:365,name:`Theta Testnet`,nativeCurrency:{name:`TFUEL`,symbol:`TFUEL`,decimals:18},rpcUrls:{default:{http:[`https://eth-rpc-api-testnet.thetatoken.org/rpc`]}},blockExplorers:{default:{name:`Theta Explorer`,url:`https://testnet-explorer.thetatoken.org`}},testnet:!0}),aP=L({id:108,name:`ThunderCore Mainnet`,nativeCurrency:{name:`TT`,symbol:`TT`,decimals:18},rpcUrls:{default:{http:[`https://mainnet-rpc.thundercore.com`]}},blockExplorers:{default:{name:`ThunderCore Explorer`,url:`https://explorer-mainnet.thundercore.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:0}},testnet:!1}),oP=L({id:997,name:`5ireChain Thunder Testnet`,nativeCurrency:{name:`5ire Token`,symbol:`5IRE`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.5ire.network`]}},blockExplorers:{default:{name:`5ireChain Thunder Explorer`,url:`https://testnet.5irescan.io/`}},testnet:!0}),sP=L({id:62092,name:`TikTrix Testnet`,nativeCurrency:{name:`tTTX`,symbol:`tTTX`,decimals:18},rpcUrls:{default:{http:[`https://tiktrix-rpc.xyz`]}},blockExplorers:{default:{name:`TikTrix Testnet Explorer`,url:`https://tiktrix.xyz`}},testnet:!0}),cP=L({id:6969,name:`Tomb Mainnet`,nativeCurrency:{name:`TOMB`,symbol:`TOMB`,decimals:18},rpcUrls:{default:{http:[`https://rpc.tombchain.com`]}},blockExplorers:{default:{name:`Tomb Explorer`,url:`https://tombscout.com`}},testnet:!1}),lP=L({...xy,id:61166,name:`Treasure`,nativeCurrency:{decimals:18,name:`MAGIC`,symbol:`MAGIC`},rpcUrls:{default:{http:[`https://rpc.treasure.lol`],webSocket:[`wss://rpc.treasure.lol/ws`]}},blockExplorers:{default:{name:`Treasure Block Explorer`,url:`https://treasurescan.io`}},contracts:{multicall3:{address:`0x2e29fe39496a56856D8698bD43e1dF4D0CE6266a`,blockCreated:101}},testnet:!1}),uP=L({...xy,id:978658,name:`Treasure Topaz Testnet`,nativeCurrency:{decimals:18,name:`MAGIC`,symbol:`MAGIC`},rpcUrls:{default:{http:[`https://rpc.topaz.treasure.lol`],webSocket:[`wss://rpc.topaz.treasure.lol/ws`]}},blockExplorers:{default:{name:`Treasure Topaz Block Explorer`,url:`https://topaz.treasurescan.io`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:108112}},testnet:!0}),dP=L({id:728126428,name:`Tron`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://api.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://tronscan.org`,apiUrl:`https://apilist.tronscanapi.com/api`}}}),fP=L({id:3448148188,name:`Tron Nile`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://nile.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://nile.tronscan.org`}},testnet:!0}),pP=L({id:2494104990,name:`Tron Shasta`,nativeCurrency:{name:`TRON`,symbol:`TRX`,decimals:6},rpcUrls:{default:{http:[`https://api.shasta.trongrid.io/jsonrpc`]}},blockExplorers:{default:{name:`Tronscan`,url:`https://shasta.tronscan.org`}},testnet:!0}),mP=L({id:8,name:`Ubiq Mainnet`,nativeCurrency:{name:`UBQ`,symbol:`UBQ`,decimals:18},rpcUrls:{default:{http:[`https://pyrus2.ubiqscan.io`]}},blockExplorers:{default:{name:`Ubiq Scan`,url:`https://ubiqscan.io`}},testnet:!1}),hP=L({id:19991,name:`Ultra EVM`,nativeCurrency:{decimals:18,name:`Ultra Token`,symbol:`UOS`},rpcUrls:{default:{http:[`https://evm.ultra.eosusa.io`]}},blockExplorers:{default:{name:`Ultra EVM Explorer`,url:`https://evmexplorer.ultra.io`}}}),gP=L({id:18881,name:`Ultra EVM Testnet`,nativeCurrency:{decimals:18,name:`Ultra Token`,symbol:`UOS`},rpcUrls:{default:{http:[`https://evm.test.ultra.eosusa.io`]}},blockExplorers:{default:{name:`Ultra EVM Testnet Explorer`,url:`https://evmexplorer.testnet.ultra.io`}},testnet:!0}),_P=L({id:1231,name:`Ultron Mainnet`,nativeCurrency:{name:`ULX`,symbol:`ULX`,decimals:18},rpcUrls:{default:{http:[`https://ultron-rpc.net`]}},blockExplorers:{default:{name:`Ultron Scan`,url:`https://ulxscan.com`}},testnet:!1}),vP=L({id:1230,name:`Ultron Testnet`,nativeCurrency:{name:`ULX`,symbol:`ULX`,decimals:18},rpcUrls:{default:{http:[`https://ultron-dev.io`]}},blockExplorers:{default:{name:`Ultron Scan`,url:`https://explorer.ultron-dev.io`}},testnet:!0}),yP=1,bP=L({...R,id:130,name:`Unichain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://mainnet.unichain.org/`]}},blockExplorers:{default:{name:`Uniscan`,url:`https://uniscan.xyz`,apiUrl:`https://api.uniscan.xyz/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[yP]:{address:`0x2F12d621a16e2d3285929C9996f478508951dFe4`}},portal:{[yP]:{address:`0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2`}},l1StandardBridge:{[yP]:{address:`0x81014F44b0a345033bB2b3B21C7a1A308B35fEeA`}}},sourceId:yP}),xP=11155111,SP=L({...R,id:1301,name:`Unichain Sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},blockTime:1e3,rpcUrls:{default:{http:[`https://sepolia.unichain.org`]}},blockExplorers:{default:{name:`Uniscan`,url:`https://sepolia.uniscan.xyz`,apiUrl:`https://api-sepolia.uniscan.xyz/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},portal:{[xP]:{address:`0x0d83dab629f0e0F9d36c0Cbc89B69a489f0751bD`}},l1StandardBridge:{[xP]:{address:`0xea58fcA6849d79EAd1f26608855c2D6407d54Ce2`}},disputeGameFactory:{[xP]:{address:`0xeff73e5aa3B9AEC32c659Aa3E00444d20a84394b`}}},testnet:!0,sourceId:xP}),CP=L({id:8880,name:`Unique Mainnet`,nativeCurrency:{decimals:18,name:`UNQ`,symbol:`UNQ`},rpcUrls:{default:{http:[`https://rpc.unique.network`]}},blockExplorers:{default:{name:`Unique Subscan`,url:`https://unique.subscan.io/`}}}),wP=L({id:8882,name:`Opal Testnet`,nativeCurrency:{decimals:18,name:`OPL`,symbol:`OPL`},rpcUrls:{default:{http:[`https://rpc-opal.unique.network`]}},blockExplorers:{default:{name:`Opal Subscan`,url:`https://opal.subscan.io/`}},testnet:!0}),TP=L({id:8881,name:`Quartz Mainnet`,nativeCurrency:{decimals:18,name:`QTZ`,symbol:`QTZ`},rpcUrls:{default:{http:[`https://rpc-quartz.unique.network`]}},blockExplorers:{default:{name:`Quartz Subscan`,url:`https://quartz.subscan.io/`}}}),EP=L({id:18233,name:`Unreal`,nativeCurrency:{name:`reETH`,decimals:18,symbol:`reETH`},rpcUrls:{default:{http:[`https://rpc.unreal-orbit.gelato.digital`]}},blockExplorers:{default:{name:`Unreal Explorer`,url:`https://unreal.blockscout.com`,apiUrl:`https://unreal.blockscout.com/api/v2`}},testnet:!0,contracts:{multicall3:{address:`0x8b6B0e60D8CD84898Ea8b981065A12F876eA5677`,blockCreated:1745}}}),DP=L({id:1480,name:`Vana`,blockTime:6e3,nativeCurrency:{decimals:18,name:`Vana`,symbol:`VANA`},rpcUrls:{default:{http:[`https://rpc.vana.org/`]}},blockExplorers:{default:{name:`Vana Block Explorer`,url:`https://vanascan.io`,apiUrl:`https://vanascan.io/api`}},contracts:{multicall3:{address:`0xD8d2dFca27E8797fd779F8547166A2d3B29d360E`,blockCreated:716763}}}),OP=L({id:14800,name:`Vana Moksha Testnet`,blockTime:6e3,nativeCurrency:{decimals:18,name:`Vana`,symbol:`VANA`},rpcUrls:{default:{http:[`https://rpc.moksha.vana.org`]}},blockExplorers:{default:{name:`Vana Moksha Testnet`,url:`https://moksha.vanascan.io`,apiUrl:`https://moksha.vanascan.io/api`}},contracts:{multicall3:{address:`0xD8d2dFca27E8797fd779F8547166A2d3B29d360E`,blockCreated:732283}},testnet:!0}),kP=L({id:2040,name:`Vanar Mainnet`,nativeCurrency:{name:`VANRY`,symbol:`VANRY`,decimals:18},rpcUrls:{default:{http:[`https://rpc.vanarchain.com`]}},blockExplorers:{default:{name:`Vanar Mainnet Explorer`,url:`https://explorer.vanarchain.com/`}},testnet:!1}),AP=L({id:100009,name:`Vechain`,nativeCurrency:{name:`VeChain`,symbol:`VET`,decimals:18},rpcUrls:{default:{http:[`https://mainnet.vechain.org`]}},blockExplorers:{default:{name:`Vechain Explorer`,url:`https://explore.vechain.org`},vechainStats:{name:`Vechain Stats`,url:`https://vechainstats.com`}}}),jP=L({id:106,name:`Velas EVM Mainnet`,nativeCurrency:{name:`VLX`,symbol:`VLX`,decimals:18},rpcUrls:{default:{http:[`https://evmexplorer.velas.com/rpc`]}},blockExplorers:{default:{name:`Velas Explorer`,url:`https://evmexplorer.velas.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:55883577}},testnet:!1}),MP=L({id:88,name:`Viction`,nativeCurrency:{name:`Viction`,symbol:`VIC`,decimals:18},rpcUrls:{default:{http:[`https://rpc.viction.xyz`]}},blockExplorers:{default:{name:`VIC Scan`,url:`https://vicscan.xyz`}},testnet:!1}),NP=L({id:89,name:`Viction Testnet`,nativeCurrency:{name:`Viction`,symbol:`VIC`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet.viction.xyz`]}},blockExplorers:{default:{name:`VIC Scan`,url:`https://testnet.vicscan.xyz`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:12170179}},testnet:!0}),PP=L({id:888888,name:`Vision`,nativeCurrency:{name:`VISION`,symbol:`VS`,decimals:18},rpcUrls:{default:{http:[`https://infragrid.v.network/ethereum/compatible`]}},blockExplorers:{default:{name:`Vision Scan`,url:`https://visionscan.org`}},testnet:!1}),FP=L({id:666666,name:`Vision Testnet`,nativeCurrency:{name:`VISION`,symbol:`VS`,decimals:18},rpcUrls:{default:{http:[`https://vpioneer.infragrid.v.network/ethereum/compatible`]}},blockExplorers:{default:{name:`Vision Scan`,url:`https://visionscan.org/?chain=vpioneer`}},testnet:!0}),IP=L({id:888,name:`Wanchain`,nativeCurrency:{name:`WANCHAIN`,symbol:`WAN`,decimals:18},rpcUrls:{default:{http:[`https://gwan-ssl.wandevs.org:56891`,`https://gwan2-ssl.wandevs.org`]}},blockExplorers:{default:{name:`WanScan`,url:`https://wanscan.org`}},contracts:{multicall3:{address:`0xcDF6A1566e78EB4594c86Fe73Fcdc82429e97fbB`,blockCreated:25312390}}}),LP=L({id:999,name:`Wanchain Testnet`,nativeCurrency:{name:`WANCHAIN`,symbol:`WANt`,decimals:18},rpcUrls:{default:{http:[`https://gwan-ssl.wandevs.org:46891`]}},blockExplorers:{default:{name:`WanScanTest`,url:`https://wanscan.org`}},contracts:{multicall3:{address:`0x11c89bF4496c39FB80535Ffb4c92715839CC5324`,blockCreated:24743448}},testnet:!0}),RP=L({id:9496,name:`WeaveVM Alphanet`,nativeCurrency:{name:`Testnet WeaveVM`,symbol:`tWVM`,decimals:18},rpcUrls:{default:{http:[`https://testnet-rpc.wvm.dev`]}},blockExplorers:{default:{name:`WeaveVM Alphanet Explorer`,url:`https://explorer.wvm.dev`}},testnet:!0}),zP=L({id:1111,name:`WEMIX`,network:`wemix-mainnet`,nativeCurrency:{name:`WEMIX`,symbol:`WEMIX`,decimals:18},rpcUrls:{default:{http:[`https://api.wemix.com`]}},blockExplorers:{default:{name:`wemixExplorer`,url:`https://explorer.wemix.com`}}}),BP=L({id:1112,name:`WEMIX Testnet`,network:`wemix-testnet`,nativeCurrency:{name:`WEMIX`,symbol:`tWEMIX`,decimals:18},rpcUrls:{default:{http:[`https://api.test.wemix.com`]}},blockExplorers:{default:{name:`wemixExplorer`,url:`https://testnet.wemixscan.com`,apiUrl:`https://testnet.wemixscan.com/api`}},testnet:!0}),VP=L({id:420420421,name:`Westend Asset Hub`,nativeCurrency:{decimals:18,name:`Westies`,symbol:`WND`},rpcUrls:{default:{http:[`https://westend-asset-hub-eth-rpc.polkadot.io`]}},blockExplorers:{default:{name:`subscan`,url:`https://westend-asset-hub-eth-explorer.parity.io`}},testnet:!0}),HP=L({testnet:!1,name:`Whitechain`,blockExplorers:{default:{name:`Whitechain Explorer`,url:`https://explorer.whitechain.io`}},id:1875,rpcUrls:{default:{http:[`https://rpc.whitechain.io`]}},nativeCurrency:{decimals:18,name:`WhiteBIT Coin`,symbol:`WBT`},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:25212237}}}),UP=L({testnet:!0,name:`Whitechain Testnet`,blockExplorers:{default:{name:`Whitechain Explorer`,url:`https://testnet.whitechain.io`}},id:2625,rpcUrls:{default:{http:[`https://rpc-testnet.whitechain.io`]}},nativeCurrency:{decimals:18,name:`WhiteBIT Coin`,symbol:`WBT`}}),WP=L({id:42070,name:`WMC Testnet`,nativeCurrency:{name:`WMTx`,symbol:`WMTx`,decimals:18},rpcUrls:{default:{http:[`https://rpc-testnet-base.worldmobile.net`]}},blockExplorers:{default:{name:`WMC Explorer`,url:`https://explorer2-base-testnet.worldmobile.net`}},testnet:!0}),GP=1,KP=L({...R,id:480,name:`World Chain`,network:`worldchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://worldchain-mainnet.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Worldscan`,url:`https://worldscan.org`,apiUrl:`https://api.worldscan.org/api`},blockscout:{name:`Blockscout`,url:`https://worldchain-mainnet.explorer.alchemy.com`,apiUrl:`https://worldchain-mainnet.explorer.alchemy.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[GP]:{address:`0x069c4c579671f8c120b1327a73217D01Ea2EC5ea`}},l2OutputOracle:{[GP]:{address:`0x19A6d1E9034596196295CF148509796978343c5D`}},portal:{[GP]:{address:`0xd5ec14a83B7d95BE1E2Ac12523e2dEE12Cbeea6C`}},l1StandardBridge:{[GP]:{address:`0x470458C91978D2d929704489Ad730DC3E3001113`}}},testnet:!1,sourceId:GP}),qP=11155111,JP=L({...R,id:4801,name:`World Chain Sepolia`,network:`worldchain-sepolia`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://worldchain-sepolia.g.alchemy.com/public`]}},blockExplorers:{default:{name:`Worldscan Sepolia`,url:`https://sepolia.worldscan.org`,apiUrl:`https://api-sepolia.worldscan.org/api`},blockscout:{name:`Blockscout`,url:`https://worldchain-sepolia.explorer.alchemy.com`,apiUrl:`https://worldchain-sepolia.explorer.alchemy.com/api`}},contracts:{...R.contracts,multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:0},disputeGameFactory:{[qP]:{address:`0x8Ec1111f67Dad6b6A93B3F42DfBC92D81c98449A`}},l2OutputOracle:{[qP]:{address:`0xc8886f8BAb6Eaeb215aDB5f1c686BF699248300e`}},portal:{[qP]:{address:`0xFf6EBa109271fe6d4237EeeD4bAb1dD9A77dD1A4`}},l1StandardBridge:{[qP]:{address:`0xd7DF54b3989855eb66497301a4aAEc33Dbb3F8DE`}}},testnet:!0,sourceId:qP}),YP=L({id:103,name:`WorldLand Mainnet`,nativeCurrency:{decimals:18,name:`WLC`,symbol:`WLC`},rpcUrls:{default:{http:[`https://seoul.worldland.foundation`]}},blockExplorers:{default:{name:`WorldLand Scan`,url:`https://scan.worldland.foundation`}},testnet:!1}),XP=L({id:660279,name:`Xai Mainnet`,nativeCurrency:{name:`Xai`,symbol:`XAI`,decimals:18},rpcUrls:{default:{http:[`https://xai-chain.net/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://explorer.xai-chain.net`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:222549}},testnet:!1}),ZP=L({id:37714555429,name:`Xai Testnet`,nativeCurrency:{name:`sXai`,symbol:`sXAI`,decimals:18},rpcUrls:{default:{http:[`https://testnet-v2.xai-chain.net/rpc`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://testnet-explorer-v2.xai-chain.net`}},testnet:!0}),QP=L({id:50,name:`XDC Network`,nativeCurrency:{decimals:18,name:`XDC`,symbol:`XDC`},rpcUrls:{default:{http:[`https://rpc.xdcrpc.com`]}},blockExplorers:{default:{name:`XDCScan`,url:`https://xdcscan.com`}},contracts:{multicall3:{address:`0x0B1795ccA8E4eC4df02346a082df54D437F8D9aF`,blockCreated:75884020}}}),$P=L({id:51,name:`Apothem Network`,nativeCurrency:{decimals:18,name:`TXDC`,symbol:`TXDC`},rpcUrls:{default:{http:[`https://erpc.apothem.network`]}},blockExplorers:{default:{name:`XDCScan`,url:`https://testnet.xdcscan.com`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:59765389}}}),eF=L({id:1643,name:`XGR Mainnet`,nativeCurrency:{name:`XGR`,symbol:`XGR`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xgr.network`]}},blockExplorers:{default:{name:`XGR Explorer`,url:`https://explorer.xgr.network`}}}),tF=L({id:196,name:`X Layer Mainnet`,nativeCurrency:{decimals:18,name:`OKB`,symbol:`OKB`},rpcUrls:{default:{http:[`https://xlayerrpc.okx.com`]}},blockExplorers:{default:{name:`OKLink`,url:`https://www.oklink.com/xlayer`,apiUrl:`https://www.oklink.com/api/v5/explorer/xlayer/api`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:47416}}}),nF=L({id:1952,name:`X1 Testnet`,nativeCurrency:{decimals:18,name:`OKB`,symbol:`OKB`},rpcUrls:{default:{http:[`https://xlayertestrpc.okx.com`]}},blockExplorers:{default:{name:`OKLink`,url:`https://www.oklink.com/xlayer-test`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:624344}},testnet:!0}),rF=L({id:3721,name:`Xone Chain Mainnet`,nativeCurrency:{decimals:18,name:`XOC`,symbol:`XOC`},rpcUrls:{default:{http:[`https://rpc.xone.org`]}},blockExplorers:{default:{name:`Xone Mainnet Explorer`,url:`https://xonescan.com`,apiUrl:`http://api.xonescan.com/api`}},testnet:!1}),iF=L({id:33772211,name:`Xone Chain Testnet`,nativeCurrency:{decimals:18,name:`XOC`,symbol:`XOC`},rpcUrls:{default:{http:[`https://rpc-testnet.xone.org`,`https://rpc-testnet.xone.plus`,`https://rpc-testnet.knight.center`]}},blockExplorers:{default:{name:`Xone Testnet Explorer`,url:`https://testnet.xonescan.com`,apiUrl:`http://api.testnet.xonescan.com/api`}},testnet:!0}),aF=L({id:20250217,name:`Xphere Mainnet`,nativeCurrency:{decimals:18,name:`XP`,symbol:`XP`},rpcUrls:{default:{http:[`https://en-bkk.x-phere.com`]}},blockExplorers:{default:{name:`Xphere Tamsa Explorer`,url:`https://xp.tamsa.io`}},testnet:!1}),oF=L({id:1998991,name:`Xphere Testnet`,nativeCurrency:{decimals:18,name:`XPT`,symbol:`XPT`},rpcUrls:{default:{http:[`http://testnet.x-phere.com`]}},blockExplorers:{default:{name:`Xphere Tamsa Explorer`,url:`https://xpt.tamsa.io`}},testnet:!0}),sF=L({id:37,name:`CONX Chain`,nativeCurrency:{decimals:18,name:`XPLA`,symbol:`XPLA`},rpcUrls:{default:{http:[`https://dimension-evm-rpc.xpla.dev`]}},blockExplorers:{default:{name:`CONX Explorer`,url:`https://explorer.conx.xyz`}},testnet:!1}),cF=L({id:273,name:`XR One`,nativeCurrency:{decimals:18,name:`XR1`,symbol:`XR1`},rpcUrls:{default:{http:[`https://xr1.calderachain.xyz/http`],webSocket:[`wss://xr1.calderachain.xyz/ws`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://xr1.calderaexplorer.xyz`}},testnet:!1}),lF=L({id:144e4,name:`XRPL EVM`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xrplevm.org`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.xrplevm.org`,apiUrl:`https://explorer.xrplevm.org/api/v2`}},testnet:!1}),uF=L({id:1440002,name:`XRPL EVM Devnet`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.xrplevm.org/`]},public:{http:[`https://rpc.xrplevm.org/`]}},blockExplorers:{default:{name:`XRPLEVM Devnet Explorer`,url:`https://explorer.xrplevm.org/`}},contracts:{multicall3:{address:`0x82Cc144D7d0AD4B1c27cb41420e82b82Ad6e9B31`,blockCreated:15237286}},testnet:!0}),dF=L({id:1449e3,name:`XRPL EVM Testnet`,nativeCurrency:{name:`XRP`,symbol:`XRP`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.xrplevm.org`]}},blockExplorers:{default:{name:`blockscout`,url:`https://explorer.testnet.xrplevm.org`,apiUrl:`https://explorer.testnet.xrplevm.org/api/v2`}},contracts:{multicall3:{address:`0x82Cc144D7d0AD4B1c27cb41420e82b82Ad6e9B31`,blockCreated:492302}},testnet:!0}),fF=L({id:2730,name:`XR Sepolia`,nativeCurrency:{decimals:18,name:`tXR`,symbol:`tXR`},rpcUrls:{default:{http:[`https://xr-sepolia-testnet.rpc.caldera.xyz/http`]}},blockExplorers:{default:{name:`Blockscout`,url:`https://xr-sepolia-testnet.explorer.caldera.xyz`}},testnet:!0}),pF=L({id:50005,name:`Yooldo Verse`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.yooldo-verse.xyz`]}},blockExplorers:{default:{name:`Yooldo Verse Explorer`,url:`https://explorer.yooldo-verse.xyz`}}}),mF=L({id:50006,name:`Yooldo Verse Testnet`,nativeCurrency:{name:`OAS`,symbol:`OAS`,decimals:18},rpcUrls:{default:{http:[`https://rpc.testnet.yooldo-verse.xyz`]}},blockExplorers:{default:{name:`Yooldo Verse Testnet Explorer`,url:`https://explorer.testnet.yooldo-verse.xyz`}},testnet:!0}),hF=L({id:8408,name:`ZenChain Testnet`,nativeCurrency:{decimals:18,name:`ZTC`,symbol:`ZTC`},rpcUrls:{default:{http:[`https://zenchain-testnet.api.onfinality.io/public`],webSocket:[`wss://zenchain-testnet.api.onfinality.io/public-ws`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:230019}},blockExplorers:{default:{name:`Zentrace`,url:`https://zentrace.io`}},testnet:!0}),gF=L({id:383414847825,name:`Zeniq Mainnet`,nativeCurrency:{name:`ZENIQ`,symbol:`ZENIQ`,decimals:18},rpcUrls:{default:{http:[`https://api.zeniq.network`]}},blockExplorers:{default:{name:`Zeniq Explorer`,url:`https://zeniqscan.com`}},testnet:!1}),_F=L({id:543210,name:`Zero Network`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.zerion.io/v1/zero`]}},blockExplorers:{default:{name:`Zero Network Explorer`,url:`https://explorer.zero.network`}},testnet:!1}),vF=L({id:7e3,name:`ZetaChain`,nativeCurrency:{decimals:18,name:`Zeta`,symbol:`ZETA`},rpcUrls:{default:{http:[`https://zetachain-evm.blockpi.network/v1/rpc/public`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:1632781}},blockExplorers:{default:{name:`ZetaScan`,url:`https://zetascan.com`}},testnet:!1}),yF=L({id:7001,name:`ZetaChain Athens Testnet`,nativeCurrency:{decimals:18,name:`Zeta`,symbol:`aZETA`},rpcUrls:{default:{http:[`https://zetachain-athens-evm.blockpi.network/v1/rpc/public`]}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:2715217}},blockExplorers:{default:{name:`ZetaScan`,url:`https://testnet.zetascan.com`}},testnet:!0}),bF=L({id:1337803,name:`Zhejiang`,nativeCurrency:{name:`Zhejiang Ether`,symbol:`ZhejETH`,decimals:18},rpcUrls:{default:{http:[`https://rpc.zhejiang.ethpandaops.io`]}},blockExplorers:{default:{name:`Beaconchain`,url:`https://zhejiang.beaconcha.in`}},testnet:!0}),xF=L({id:32769,name:`Zilliqa`,network:`zilliqa`,nativeCurrency:{name:`Zilliqa`,symbol:`ZIL`,decimals:18},rpcUrls:{default:{http:[`https://api.zilliqa.com`]}},blockExplorers:{default:{name:`Ethernal`,url:`https://evmx.zilliqa.com`}},testnet:!1}),SF=L({id:33101,name:`Zilliqa Testnet`,network:`zilliqa-testnet`,nativeCurrency:{name:`Zilliqa`,symbol:`ZIL`,decimals:18},rpcUrls:{default:{http:[`https://dev-api.zilliqa.com`]}},blockExplorers:{default:{name:`Ethernal`,url:`https://evmx.testnet.zilliqa.com`}},testnet:!0}),CF=1,wF=L({...R,id:48900,name:`Zircuit Mainnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.zircuit.com`]}},blockExplorers:{default:{name:`Zircuit Explorer`,url:`https://explorer.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},l2OutputOracle:{[CF]:{address:`0x92Ef6Af472b39F1b363da45E35530c24619245A4`}},portal:{[CF]:{address:`0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1`}},l1StandardBridge:{[CF]:{address:`0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8`}}},testnet:!1}),TF=11155111,EF=L({...R,id:48898,name:`Zircuit Garfield Testnet`,nativeCurrency:{name:`ETH`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://garfield-testnet.zircuit.com/`]}},blockExplorers:{default:{name:`Zircuit Garfield Testnet Explorer`,url:`https://explorer.garfield-testnet.zircuit.com`}},contracts:{multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`},l2OutputOracle:{[TF]:{address:`0xd69D3AC5CA686cCF94b258291772bc520FEAf211`}},portal:{[TF]:{address:`0x4E21A71Ac3F7607Da5c06153A17B1DD20E702c21`}},l1StandardBridge:{[TF]:{address:`0x87a7E2bCA9E35BA49282E832a28A6023904460D8`}}},testnet:!0}),DF=L({id:42766,name:`ZKFair Mainnet`,network:`zkfair-mainnet`,nativeCurrency:{decimals:18,name:`USD Coin`,symbol:`USDC`},rpcUrls:{default:{http:[`https://rpc.zkfair.io`]}},blockExplorers:{default:{name:`zkFair Explorer`,url:`https://scan.zkfair.io`,apiUrl:`https://scan.zkfair.io/api`}},contracts:{multicall3:{address:`0xca11bde05977b3631167028862be2a173976ca11`,blockCreated:6090959}},testnet:!1}),OF=L({id:43851,name:`ZKFair Testnet`,network:`zkfair-testnet`,nativeCurrency:{decimals:18,name:`USD Coin`,symbol:`USDC`},rpcUrls:{default:{http:[`https://testnet-rpc.zkfair.io`]}},blockExplorers:{default:{name:`zkFair Explorer`,url:`https://testnet-scan.zkfair.io`}},testnet:!0}),kF=L({id:810180,name:`zkLink Nova`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zklink.io`]}},blockExplorers:{default:{name:`zkLink Nova Block Explorer`,url:`https://explorer.zklink.io`}}}),AF=L({id:810181,name:`zkLink Nova Sepolia Testnet`,nativeCurrency:{decimals:18,name:`ETH`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia.rpc.zklink.io`]}},blockExplorers:{default:{name:`zkLink Nova Block Explorer`,url:`https://sepolia.explorer.zklink.io`}}}),jF=L({...xy,blockTime:200,id:324,name:`ZKsync Era`,network:`zksync-era`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://mainnet.era.zksync.io`],webSocket:[`wss://mainnet.era.zksync.io/ws`]}},blockExplorers:{default:{name:`ZKsync Explorer`,url:`https://explorer.zksync.io/`,apiUrl:`https://block-explorer-api.mainnet.zksync.io/api`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`,blockCreated:3908235},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:45659388}}}),MF=L({...xy,id:260,name:`ZKsync InMemory Node`,network:`zksync-in-memory-node`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:8011`]}},testnet:!0}),NF=L({...xy,id:272,name:`ZKsync CLI Local Custom Hyperchain`,nativeCurrency:{name:`BAT`,symbol:`BAT`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15200`],webSocket:[`ws://localhost:15201`]}},blockExplorers:{default:{name:`ZKsync explorer`,url:`http://localhost:15005/`,apiUrl:`http://localhost:15005/api`}},testnet:!0}),PF=L({...xy,id:270,name:`ZKsync CLI Local Hyperchain`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15100`],webSocket:[`ws://localhost:15101`]}},blockExplorers:{default:{name:`ZKsync explorer`,url:`http://localhost:15005/`,apiUrl:`http://localhost:15005/api`}},testnet:!0}),FF=L({id:9,name:`ZKsync CLI Local Hyperchain L1`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:15045`]}},blockExplorers:{default:{name:`Blockscout`,url:`http://localhost:15001/`,apiUrl:`http://localhost:15001/api/v2`}},testnet:!0}),IF=L({...xy,id:270,name:`ZKsync CLI Local Node`,network:`zksync-cli-local-node`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`http://localhost:3050`]}},testnet:!0}),LF=L({...xy,blockTime:200,id:300,name:`ZKsync Sepolia Testnet`,network:`zksync-sepolia-testnet`,nativeCurrency:{name:`Ether`,symbol:`ETH`,decimals:18},rpcUrls:{default:{http:[`https://sepolia.era.zksync.dev`],webSocket:[`wss://sepolia.era.zksync.dev/ws`]}},blockExplorers:{default:{name:`ZKsync Explorer`,url:`https://sepolia.explorer.zksync.io/`,blockExplorerApi:`https://block-explorer-api.sepolia.zksync.dev/api`}},contracts:{multicall3:{address:`0xF9cda624FBC7e059355ce98a31693d299FACd963`},erc6492Verifier:{address:`0xfB688330379976DA81eB64Fe4BF50d7401763B9C`,blockCreated:3855712}},testnet:!0}),RF=L({id:375,name:`zkXPLA Mainnet`,network:`zkxpla`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zkxpla.io`]}},blockExplorers:{default:{name:`zkXPLA Mainnet Explorer`,url:`https://explorer.zkxpla.io`,apiUrl:`https://explorer.zkxpla.io/api`}},testnet:!1}),zF=L({id:475,name:`zkXPLA Testnet`,network:`zkxpla-testnet`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet-rpc.zkxpla.io`]}},blockExplorers:{default:{name:`zkXPLA Testnet Explorer`,url:`https://testnet-explorer.zkxpla.io`,apiUrl:`https://testnet-explorer.zkxpla.io/api`}},testnet:!0}),BF=1,VF=L({...R,id:7777777,name:`Zora`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[`https://rpc.zora.energy`],webSocket:[`wss://rpc.zora.energy`]}},blockExplorers:{default:{name:`Explorer`,url:`https://explorer.zora.energy`,apiUrl:`https://explorer.zora.energy/api`}},contracts:{...R.contracts,disputeGameFactory:{[BF]:{address:`0xB0F15106fa1e473Ddb39790f197275BC979Aa37e`}},l2OutputOracle:{[BF]:{address:`0x9E6204F750cD866b299594e2aC9eA824E2e5f95c`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:5882},portal:{[BF]:{address:`0x1a0ad011913A150f69f6A19DF447A0CfD9551054`}},l1StandardBridge:{[BF]:{address:`0x3e2Ea9B92B7E48A52296fD261dc26fd995284631`}}},sourceId:BF}),HF=11155111,UF=L({...R,id:999999999,name:`Zora Sepolia`,network:`zora-sepolia`,nativeCurrency:{decimals:18,name:`Zora Sepolia`,symbol:`ETH`},rpcUrls:{default:{http:[`https://sepolia.rpc.zora.energy`],webSocket:[`wss://sepolia.rpc.zora.energy`]}},blockExplorers:{default:{name:`Zora Sepolia Explorer`,url:`https://sepolia.explorer.zora.energy/`,apiUrl:`https://sepolia.explorer.zora.energy/api`}},contracts:{...R.contracts,l2OutputOracle:{[HF]:{address:`0x2615B481Bd3E5A1C0C7Ca3Da1bdc663E8615Ade9`}},multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:83160},portal:{[HF]:{address:`0xeffE2C6cA9Ab797D418f0D91eA60807713f3536f`}},l1StandardBridge:{[HF]:{address:`0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB`}}},sourceId:HF,testnet:!0}),WF=5,GF=L({...R,id:999,name:`Zora Goerli Testnet`,nativeCurrency:{decimals:18,name:`Zora Goerli`,symbol:`ETH`},rpcUrls:{default:{http:[`https://testnet.rpc.zora.energy`],webSocket:[`wss://testnet.rpc.zora.energy`]}},blockExplorers:{default:{name:`Explorer`,url:`https://testnet.explorer.zora.energy`,apiUrl:`https://testnet.explorer.zora.energy/api`}},contracts:{...R.contracts,multicall3:{address:`0xcA11bde05977b3631167028862bE2a173976CA11`,blockCreated:189123},portal:{[WF]:{address:`0xDb9F51790365e7dc196e7D072728df39Be958ACe`}}},sourceId:WF,testnet:!0}),KF=c({abey:()=>ay,abstract:()=>Sy,abstractTestnet:()=>Cy,acala:()=>wy,acria:()=>Ty,adf:()=>Ey,adi:()=>Dy,agungTestnet:()=>Oy,aioz:()=>ky,alephZero:()=>Ay,alephZeroTestnet:()=>jy,alienx:()=>My,alienxHalTestnet:()=>Ny,alpenTestnet:()=>Py,ancient8:()=>Uy,ancient8Sepolia:()=>Gy,anvil:()=>Ky,apeChain:()=>qy,apexTestnet:()=>Jy,apollo:()=>Yy,arbitrum:()=>Xy,arbitrumGoerli:()=>Zy,arbitrumNova:()=>Qy,arbitrumSepolia:()=>$y,arcTestnet:()=>eb,arenaz:()=>tb,areonNetwork:()=>nb,areonNetworkTestnet:()=>rb,areum:()=>ib,artelaTestnet:()=>ab,arthera:()=>ob,artheraTestnet:()=>sb,assetChain:()=>cb,assetChainTestnet:()=>lb,astar:()=>ub,astarZkEVM:()=>db,astarZkyoto:()=>fb,atletaOlympia:()=>pb,aurora:()=>mb,auroraTestnet:()=>hb,auroria:()=>gb,autheoTestnet:()=>_b,avalanche:()=>vb,avalancheFuji:()=>yb,b3:()=>bb,b3Sepolia:()=>xb,bahamut:()=>Sb,base:()=>wb,baseGoerli:()=>Ob,basePreconf:()=>Tb,baseSepolia:()=>Ab,baseSepoliaPreconf:()=>jb,basecampTestnet:()=>Eb,beam:()=>Mb,beamTestnet:()=>Nb,bearNetworkChainMainnet:()=>Pb,bearNetworkChainTestnet:()=>Fb,berachain:()=>Ib,berachainBepolia:()=>Lb,berachainTestnet:()=>Rb,berachainTestnetbArtio:()=>zb,bevmMainnet:()=>Bb,bifrost:()=>Vb,birdlayer:()=>Hb,bitTorrent:()=>Yb,bitTorrentTestnet:()=>Xb,bitgert:()=>Ub,bitkub:()=>Wb,bitkubTestnet:()=>Gb,bitlayer:()=>Kb,bitlayerTestnet:()=>qb,bitrock:()=>Jb,blast:()=>Qb,blastSepolia:()=>$b,bob:()=>tx,bobSepolia:()=>ax,boba:()=>nx,bobaSepolia:()=>rx,boolBetaMainnet:()=>ox,botanix:()=>sx,botanixTestnet:()=>cx,bounceBit:()=>lx,bounceBitTestnet:()=>ux,bronos:()=>dx,bronosTestnet:()=>fx,bsc:()=>px,bscGreenfield:()=>mx,bscTestnet:()=>hx,bsquared:()=>gx,bsquaredTestnet:()=>_x,btr:()=>vx,btrTestnet:()=>yx,bxn:()=>bx,bxnTestnet:()=>xx,cannon:()=>Sx,canto:()=>Cx,celo:()=>Rx,celoAlfajores:()=>Bx,celoSepolia:()=>Hx,chang:()=>Ux,chiliz:()=>Wx,chips:()=>Gx,citrea:()=>Kx,citreaTestnet:()=>qx,classic:()=>Jx,codex:()=>Xx,codexTestnet:()=>Qx,coinbit:()=>$x,coinex:()=>eS,confluxESpace:()=>tS,confluxESpaceTestnet:()=>nS,coreDao:()=>rS,coreTestnet1:()=>iS,coreTestnet2:()=>aS,corn:()=>oS,cornTestnet:()=>sS,cpchain:()=>cS,crab:()=>lS,creatorTestnet:()=>uS,creditCoin3Devnet:()=>dS,creditCoin3Mainnet:()=>fS,creditCoin3Testnet:()=>pS,cronos:()=>mS,cronosTestnet:()=>hS,cronoszkEVM:()=>gS,cronoszkEVMTestnet:()=>_S,crossbell:()=>vS,crossfi:()=>yS,curtis:()=>bS,cyber:()=>xS,cyberTestnet:()=>SS,dailyNetwork:()=>CS,dailyNetworkTestnet:()=>wS,darwinia:()=>TS,datahavenTestnet:()=>ES,dbkchain:()=>DS,dchain:()=>OS,dchainTestnet:()=>kS,defichainEvm:()=>AS,defichainEvmTestnet:()=>jS,degen:()=>MS,dfk:()=>NS,diode:()=>PS,disChain:()=>FS,dodochainTestnet:()=>IS,dogechain:()=>LS,domaTestnet:()=>RS,donatuz:()=>zS,dosChain:()=>BS,dosChainTestnet:()=>VS,dreyerxMainnet:()=>HS,dreyerxTestnet:()=>US,dustboyIoT:()=>WS,dymension:()=>GS,edexa:()=>KS,edexaTestnet:()=>qS,edgeless:()=>JS,edgelessTestnet:()=>YS,edgeware:()=>XS,edgewareTestnet:()=>ZS,eduChain:()=>QS,eduChainTestnet:()=>$S,elastos:()=>eC,elastosTestnet:()=>tC,electroneum:()=>nC,electroneumTestnet:()=>rC,elysiumTestnet:()=>iC,energy:()=>aC,eni:()=>oC,eniTestnet:()=>sC,enuls:()=>cC,eon:()=>lC,eos:()=>uC,eosTestnet:()=>dC,eteria:()=>fC,etherlink:()=>pC,etherlinkShadownetTestnet:()=>mC,etherlinkTestnet:()=>hC,ethernity:()=>gC,etp:()=>_C,evmos:()=>vC,evmosTestnet:()=>yC,excelonMainnet:()=>bC,expanse:()=>xC,exsat:()=>SC,exsatTestnet:()=>CC,fantom:()=>wC,fantomSonicTestnet:()=>TC,fantomTestnet:()=>EC,fibo:()=>DC,filecoin:()=>OC,filecoinCalibration:()=>kC,filecoinHyperspace:()=>AC,fireChain:()=>iy,flame:()=>jC,flare:()=>MC,flareTestnet:()=>NC,flowMainnet:()=>PC,flowPreviewnet:()=>FC,flowTestnet:()=>IC,fluence:()=>LC,fluenceStage:()=>RC,fluenceTestnet:()=>zC,fluentDevnet:()=>BC,fluentTestnet:()=>VC,form:()=>UC,formTestnet:()=>KC,forma:()=>WC,formicarium:()=>lD,forta:()=>qC,foundry:()=>JC,fraxtal:()=>XC,fraxtalTestnet:()=>QC,funkiMainnet:()=>ew,funkiSepolia:()=>nw,fuse:()=>rw,fuseSparknet:()=>iw,fusion:()=>aw,fusionTestnet:()=>ow,garnet:()=>cw,gatechain:()=>lw,geist:()=>uw,genesys:()=>dw,giwaSepolia:()=>pw,giwaSepoliaPreconf:()=>mw,glideL1Protocol:()=>hw,glideL2Protocol:()=>gw,gnosis:()=>_w,gnosisChiado:()=>vw,goChain:()=>xw,goat:()=>yw,gobi:()=>bw,godwoken:()=>Sw,goerli:()=>Cw,graphite:()=>ww,graphiteTestnet:()=>Tw,gravity:()=>Ew,gunz:()=>Dw,guruNetwork:()=>Ow,guruTestnet:()=>kw,ham:()=>Aw,happychainTestnet:()=>jw,haqqMainnet:()=>Mw,haqqTestedge2:()=>Nw,hardhat:()=>Pw,harmonyOne:()=>Fw,hashkey:()=>Iw,hashkeyTestnet:()=>Lw,haustTestnet:()=>Rw,hedera:()=>zw,hederaPreviewnet:()=>Bw,hederaTestnet:()=>Vw,hela:()=>Hw,heliosTestnet:()=>Uw,hemi:()=>Ww,hemiSepolia:()=>Gw,henesys:()=>Kw,holesky:()=>qw,hoodi:()=>Jw,horizenTestnet:()=>Yw,hpb:()=>Xw,hpp:()=>Zw,hppSepolia:()=>Qw,huddle01Mainnet:()=>$w,huddle01Testnet:()=>eT,humanity:()=>tT,humanityTestnet:()=>nT,humanode:()=>rT,humanodeTestnet5:()=>iT,hychain:()=>aT,hychainTestnet:()=>oT,hyperEvm:()=>sT,hyperliquid:()=>sT,hyperliquidEvmTestnet:()=>cT,iSunCoin:()=>ET,icbNetwork:()=>lT,idchain:()=>uT,immutableZkEvm:()=>dT,immutableZkEvmTestnet:()=>fT,inEVM:()=>pT,initVerse:()=>mT,initVerseGenesis:()=>hT,injective:()=>gT,injectiveTestnet:()=>_T,ink:()=>yT,inkSepolia:()=>xT,iota:()=>ST,iotaTestnet:()=>CT,iotex:()=>wT,iotexTestnet:()=>TT,jasmyChain:()=>DT,jasmyChainTestnet:()=>OT,jbc:()=>kT,jbcTestnet:()=>AT,jocMainnet:()=>jT,jocTestnet:()=>MT,jovay:()=>NT,jovaySepolia:()=>PT,juneo:()=>FT,juneoBCH1Chain:()=>IT,juneoDAI1Chain:()=>LT,juneoDOGE1Chain:()=>RT,juneoEUR1Chain:()=>zT,juneoGLD1Chain:()=>BT,juneoLINK1Chain:()=>VT,juneoLTC1Chain:()=>HT,juneoSGD1Chain:()=>WT,juneoSocotraTestnet:()=>GT,juneoUSD1Chain:()=>KT,juneoUSDT1Chain:()=>qT,juneomBTC1Chain:()=>UT,kaia:()=>JT,kairos:()=>YT,kakarotSepolia:()=>XT,kakarotStarknetSepolia:()=>ZT,kardiaChain:()=>QT,karura:()=>$T,katana:()=>eE,kava:()=>tE,kavaTestnet:()=>nE,kcc:()=>rE,kii:()=>iE,kiiTestnetOro:()=>aE,kinto:()=>oE,klaytn:()=>sE,klaytnBaobab:()=>cE,koi:()=>lE,kroma:()=>uE,kromaSepolia:()=>dE,krown:()=>fE,l3x:()=>pE,l3xTestnet:()=>mE,lavita:()=>hE,lens:()=>gE,lensTestnet:()=>_E,lestnet:()=>vE,lightlinkPegasus:()=>yE,lightlinkPhoenix:()=>bE,linea:()=>wE,lineaGoerli:()=>TE,lineaSepolia:()=>EE,lineaTestnet:()=>DE,lisk:()=>kE,liskSepolia:()=>jE,loadAlphanet:()=>ME,localhost:()=>NE,loop:()=>PE,lukso:()=>FE,luksoTestnet:()=>IE,lumiaMainnet:()=>LE,lumiaTestnet:()=>RE,lumoz:()=>zE,lumozTestnet:()=>BE,luxeports:()=>VE,lycan:()=>HE,lyra:()=>UE,mainnet:()=>WE,mandala:()=>GE,manta:()=>KE,mantaSepoliaTestnet:()=>qE,mantaTestnet:()=>JE,mantle:()=>YE,mantleSepoliaTestnet:()=>XE,mantleTestnet:()=>ZE,mantraDuKongEVMTestnet:()=>QE,mantraEVM:()=>$E,mapProtocol:()=>eD,matchain:()=>tD,matchainTestnet:()=>nD,mchVerse:()=>rD,megaeth:()=>iD,megaethTestnet:()=>aD,mekong:()=>oD,meld:()=>sD,memecore:()=>cD,merlin:()=>uD,merlinErigonTestnet:()=>dD,metachain:()=>fD,metachainIstanbul:()=>pD,metadium:()=>mD,metalL2:()=>gD,meter:()=>_D,meterTestnet:()=>vD,metis:()=>yD,metisGoerli:()=>bD,metisSepolia:()=>xD,mev:()=>SD,mevTestnet:()=>CD,mint:()=>wD,mintSepoliaTestnet:()=>TD,mitosisTestnet:()=>ED,mode:()=>OD,modeTestnet:()=>AD,monad:()=>jD,monadTestnet:()=>MD,moonbaseAlpha:()=>ND,moonbeam:()=>PD,moonbeamDev:()=>FD,moonriver:()=>ID,morph:()=>LD,morphHolesky:()=>RD,morphSepolia:()=>zD,nahmii:()=>BD,nautilus:()=>VD,near:()=>HD,nearTestnet:()=>UD,neonDevnet:()=>WD,neonMainnet:()=>GD,neoxMainnet:()=>KD,neoxT4:()=>qD,newton:()=>JD,nexi:()=>YD,nexilix:()=>XD,nibiru:()=>ZD,nitrographTestnet:()=>QD,nomina:()=>$D,oasisTestnet:()=>eO,oasys:()=>tO,odysseyTestnet:()=>nO,okc:()=>rO,omax:()=>iO,omni:()=>aO,omniOmega:()=>oO,oneWorld:()=>sO,oortMainnetDev:()=>cO,opBNB:()=>uO,opBNBTestnet:()=>fO,openledger:()=>pO,optimism:()=>hO,optimismGoerli:()=>_O,optimismSepolia:()=>yO,optopia:()=>bO,optopiaTestnet:()=>xO,orderly:()=>SO,orderlySepolia:()=>CO,otimDevnet:()=>wO,palm:()=>TO,palmTestnet:()=>EO,paseoPassetHub:()=>DO,peaq:()=>OO,pgn:()=>AO,pgnTestnet:()=>MO,phoenix:()=>NO,planq:()=>PO,plasma:()=>FO,plasmaDevnet:()=>IO,plasmaTestnet:()=>LO,playfiAlbireo:()=>RO,plinga:()=>zO,plume:()=>BO,plumeDevnet:()=>VO,plumeMainnet:()=>HO,plumeSepolia:()=>UO,plumeTestnet:()=>WO,polterTestnet:()=>GO,polygon:()=>KO,polygonAmoy:()=>qO,polygonMumbai:()=>JO,polygonZkEvm:()=>YO,polygonZkEvmCardona:()=>XO,polygonZkEvmTestnet:()=>ZO,polynomial:()=>QO,polynomialSepolia:()=>$O,potos:()=>ek,potosTestnet:()=>tk,premiumBlockTestnet:()=>nk,pulsechain:()=>rk,pulsechainV4:()=>ik,pumpfiTestnet:()=>ak,pyrope:()=>sk,qMainnet:()=>lk,qTestnet:()=>uk,ql1:()=>ck,quai:()=>dk,quaiTestnet:()=>fk,reactiveTestnet:()=>pk,real:()=>mk,redbellyMainnet:()=>hk,redbellyTestnet:()=>gk,reddio:()=>_k,reddioSepolia:()=>vk,redstone:()=>bk,rei:()=>xk,reyaNetwork:()=>Sk,rise:()=>Ck,riseTestnet:()=>wk,rivalz:()=>Tk,rollux:()=>Ek,rolluxTestnet:()=>Dk,ronin:()=>Ok,root:()=>kk,rootPorcini:()=>Ak,rootstock:()=>jk,rootstockTestnet:()=>Mk,rss3:()=>Pk,rss3Sepolia:()=>Ik,saakuru:()=>Lk,saga:()=>Rk,saigon:()=>zk,sanko:()=>Bk,sapphire:()=>Vk,sapphireTestnet:()=>Hk,satoshiVM:()=>Uk,satoshiVMTestnet:()=>Wk,scroll:()=>Gk,scrollSepolia:()=>Kk,sei:()=>qk,seiTestnet:()=>Yk,seismicDevnet:()=>Jk,sepolia:()=>Xk,shape:()=>Qk,shapeSepolia:()=>eA,shardeum:()=>tA,shardeumSphinx:()=>nA,shibarium:()=>rA,shibariumTestnet:()=>iA,shiden:()=>aA,shimmer:()=>oA,shimmerTestnet:()=>sA,sidraChain:()=>cA,silentData:()=>lA,silicon:()=>uA,siliconSepolia:()=>dA,sixProtocol:()=>fA,skaleBase:()=>TA,skaleBaseSepoliaTestnet:()=>EA,skaleBlockBrawlers:()=>pA,skaleCalypso:()=>mA,skaleCalypsoTestnet:()=>hA,skaleCryptoBlades:()=>gA,skaleCryptoColosseum:()=>_A,skaleEuropa:()=>vA,skaleEuropaTestnet:()=>yA,skaleExorde:()=>bA,skaleHumanProtocol:()=>xA,skaleNebula:()=>SA,skaleNebulaTestnet:()=>CA,skaleRazor:()=>wA,skaleTitan:()=>DA,skaleTitanTestnet:()=>OA,sketchpad:()=>kA,snax:()=>jA,snaxTestnet:()=>NA,somnia:()=>PA,somniaTestnet:()=>FA,soneium:()=>LA,soneiumMinato:()=>zA,songbird:()=>BA,songbirdTestnet:()=>VA,sonic:()=>HA,sonicBlazeTestnet:()=>UA,sonicTestnet:()=>WA,sophon:()=>GA,sophonTestnet:()=>KA,sova:()=>qA,sovaSepolia:()=>JA,spicy:()=>YA,stable:()=>XA,stableTestnet:()=>ZA,statusNetworkSepolia:()=>QA,statusSepolia:()=>QA,step:()=>$A,story:()=>ej,storyAeneid:()=>tj,storyOdyssey:()=>nj,storyTestnet:()=>rj,stratis:()=>ij,subtensorEvm:()=>aj,superlumio:()=>oj,superposition:()=>sj,superseed:()=>lj,superseedSepolia:()=>dj,surgeTestnet:()=>fj,swan:()=>pj,swanProximaTestnet:()=>mj,swanSaturnTestnet:()=>hj,swellchain:()=>gj,swellchainTestnet:()=>_j,swissdlt:()=>vj,syscoin:()=>yj,syscoinTestnet:()=>bj,tac:()=>xj,tacSPB:()=>Sj,taiko:()=>Cj,taikoHekla:()=>wj,taikoHoodi:()=>Tj,taikoJolnir:()=>Ej,taikoKatla:()=>Dj,taikoTestnetSepolia:()=>Oj,taraxa:()=>kj,taraxaTestnet:()=>Aj,teaSepolia:()=>jj,telcoinTestnet:()=>Mj,telos:()=>Nj,telosTestnet:()=>Pj,tempo:()=>JN,tempoAndantino:()=>YN,tempoDevnet:()=>XN,tempoLocalnet:()=>ZN,tempoModerato:()=>QN,tempoTestnet:()=>YN,tenet:()=>$N,ternoa:()=>eP,thaiChain:()=>tP,that:()=>nP,theta:()=>rP,thetaTestnet:()=>iP,thunderCore:()=>aP,thunderTestnet:()=>oP,tiktrixTestnet:()=>sP,tomb:()=>cP,treasure:()=>lP,treasureTopaz:()=>uP,tron:()=>dP,tronNile:()=>fP,tronShasta:()=>pP,ubiq:()=>mP,ultra:()=>hP,ultraTestnet:()=>gP,ultron:()=>_P,ultronTestnet:()=>vP,unichain:()=>bP,unichainSepolia:()=>SP,unique:()=>CP,uniqueOpal:()=>wP,uniqueQuartz:()=>TP,unreal:()=>EP,vana:()=>DP,vanaMoksha:()=>OP,vanar:()=>kP,vechain:()=>AP,velas:()=>jP,viction:()=>MP,victionTestnet:()=>NP,vision:()=>PP,visionTestnet:()=>FP,wanchain:()=>IP,wanchainTestnet:()=>LP,weaveVMAlphanet:()=>RP,wemix:()=>zP,wemixTestnet:()=>BP,westendAssetHub:()=>VP,whitechain:()=>HP,whitechainTestnet:()=>UP,wmcTestnet:()=>WP,worldLand:()=>YP,worldchain:()=>KP,worldchainSepolia:()=>JP,x1Testnet:()=>nF,xLayer:()=>tF,xLayerTestnet:()=>nF,xai:()=>XP,xaiTestnet:()=>ZP,xdc:()=>QP,xdcTestnet:()=>$P,xgr:()=>eF,xoneMainnet:()=>rF,xoneTestnet:()=>iF,xphereMainnet:()=>aF,xphereTestnet:()=>oF,xpla:()=>sF,xrOne:()=>cF,xrSepolia:()=>fF,xrplevm:()=>lF,xrplevmDevnet:()=>uF,xrplevmTestnet:()=>dF,yooldoVerse:()=>pF,yooldoVerseTestnet:()=>mF,zenchainTestnet:()=>hF,zeniq:()=>gF,zeroG:()=>ey,zeroGGalileoTestnet:()=>ty,zeroGMainnet:()=>ny,zeroGTestnet:()=>ry,zeroNetwork:()=>_F,zetachain:()=>vF,zetachainAthensTestnet:()=>yF,zhejiang:()=>bF,zilliqa:()=>xF,zilliqaTestnet:()=>SF,zircuit:()=>wF,zircuitGarfieldTestnet:()=>EF,zkFair:()=>DF,zkFairTestnet:()=>OF,zkLinkNova:()=>kF,zkLinkNovaSepoliaTestnet:()=>AF,zkSync:()=>jF,zkSyncInMemoryNode:()=>MF,zkSyncLocalNode:()=>IF,zkSyncSepoliaTestnet:()=>LF,zkXPLA:()=>RF,zkXPLATestnet:()=>zF,zksync:()=>jF,zksyncInMemoryNode:()=>MF,zksyncLocalCustomHyperchain:()=>NF,zksyncLocalHyperchain:()=>PF,zksyncLocalHyperchainL1:()=>FF,zksyncLocalNode:()=>IF,zksyncSepoliaTestnet:()=>LF,zora:()=>VF,zoraSepolia:()=>UF,zoraTestnet:()=>GF}),qF=[wb,...Object.values(c({arbitrum:()=>Xy,arbitrumSepolia:()=>$y,base:()=>wb,baseSepolia:()=>Ab,berachain:()=>Ib,berachainBepolia:()=>Lb,bsc:()=>px,celo:()=>Rx,gnosis:()=>_w,hoodi:()=>Jw,katana:()=>eE,mainnet:()=>WE,optimism:()=>hO,optimismSepolia:()=>yO,polygon:()=>KO,sepolia:()=>Xk})).filter(e=>e&&e.id!==wb.id)],JF=Ky;({...JF}),{...JF};var YF=`#__bigint`;function XF(e,t){return JSON.parse(e,(e,n)=>{let r=n;return typeof r==`string`&&r.endsWith(YF)?BigInt(r.slice(0,-9)):typeof t==`function`?t(e,r):r})}function ZF(e,t,n){return JSON.stringify(e,(e,n)=>typeof t==`function`?t(e,n):typeof n==`bigint`?n.toString()+YF:n,n)}var QF=u(s(((e,t)=>{var n=Object.prototype.hasOwnProperty,r=`~`;function i(){}Object.create&&(i.prototype=Object.create(null),new i().__proto__||(r=!1));function a(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function o(e,t,n,i,o){if(typeof n!=`function`)throw TypeError(`The listener must be a function`);var s=new a(n,i||e,o),c=r?r+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],s]:e._events[c].push(s):(e._events[c]=s,e._eventsCount++),e}function s(e,t){--e._eventsCount===0?e._events=new i:delete e._events[t]}function c(){this._events=new i,this._eventsCount=0}c.prototype.eventNames=function(){var e=[],t,i;if(this._eventsCount===0)return e;for(i in t=this._events)n.call(t,i)&&e.push(r?i.slice(1):i);return Object.getOwnPropertySymbols?e.concat(Object.getOwnPropertySymbols(t)):e},c.prototype.listeners=function(e){var t=r?r+e:e,n=this._events[t];if(!n)return[];if(n.fn)return[n.fn];for(var i=0,a=n.length,o=Array(a);i{if(n.cause instanceof e){if(n.cause.details)return n.cause.details;if(n.cause.shortMessage)return n.cause.shortMessage}return n.cause&&`details`in n.cause&&typeof n.cause.details==`string`?n.cause.details:n.cause?.message?n.cause.message:n.details})(),i=n.cause instanceof e&&n.cause.docsPath||n.docsPath,a=n.docsOrigin??e.prototype.docsOrigin,o=`${a}${i??``}`,s=!!(n.version??e.prototype.showVersion),c=n.version??e.prototype.version,l=[t||`An error occurred.`,...n.metaMessages?[``,...n.metaMessages]:[],...r||i||s?[``,r?`Details: ${r}`:void 0,i?`See: ${o}`:void 0,s?`Version: ${c}`:void 0]:[]].filter(e=>typeof e==`string`).join(` -`);super(l,n.cause?{cause:n.cause}:void 0),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docs`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsOrigin`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`docsPath`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`shortMessage`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`showVersion`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`version`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`BaseError`}),this.cause=n.cause,this.details=r,this.docs=o,this.docsOrigin=a,this.docsPath=i,this.shortMessage=t,this.showVersion=s,this.version=c}walk(e){return tI(this,e)}};Object.defineProperty(z,`defaultStaticOptions`,{enumerable:!0,configurable:!0,writable:!0,value:{docsOrigin:`https://oxlib.sh`,showVersion:!1,version:`ox@${eI()}`}}),z.setStaticOptions(z.defaultStaticOptions);function tI(e,t){return t?.(e)?e:e&&typeof e==`object`&&`cause`in e&&e.cause?tI(e.cause,t):t?null:e}function nI(e,t={}){let{raw:n=!1}=t,r=e;if(n)return e;if(r.error)throw rI(r.error);return r.result}function rI(e){let t=e;if(t instanceof Error&&!(`code`in t))return new hI({cause:t,data:t,message:t.message,stack:t.stack});let{code:n}=t;return n===hI.code?new hI(t):n===aI.code?new aI(t):n===mI.code?new mI(t):n===fI.code?new fI(t):n===uI.code?new uI(t):n===pI.code?new pI(t):n===lI.code?new lI(t):n===gI.code?new gI(t):n===oI.code?new oI(t):n===sI.code?new sI(t):n===cI.code?new cI(t):n===dI.code?new dI(t):new hI({cause:t instanceof Error?t:void 0,data:t,message:t.message,stack:t instanceof Error?t.stack:void 0})}var iI=class extends Error{constructor(e){let{cause:t,code:n,message:r,data:i,stack:a}=e;super(r,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.BaseError`}),Object.defineProperty(this,`cause`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`stack`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`data`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.cause=t,this.code=n,this.data=i,this.stack=a??``}},aI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Missing or invalid parameters.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InvalidInputError`})}};Object.defineProperty(aI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32e3});var oI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Requested resource not found.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.ResourceNotFoundError`})}};Object.defineProperty(oI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32001});var sI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Requested resource not available.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.ResourceUnavailableError`})}};Object.defineProperty(sI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32002});var cI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Transaction creation failed.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.TransactionRejectedError`})}};Object.defineProperty(cI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32003});var lI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Method is not implemented.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.MethodNotSupportedError`})}};Object.defineProperty(lI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32004});var uI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Rate limit exceeded.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.LimitExceededError`})}};Object.defineProperty(uI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32005});var dI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`JSON-RPC version not supported.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.VersionNotSupportedError`})}};Object.defineProperty(dI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32006});var fI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Input is not a valid JSON-RPC request.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InvalidRequestError`})}};Object.defineProperty(fI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32600});var pI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Method does not exist.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.MethodNotFoundError`})}};Object.defineProperty(pI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32601});var mI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Invalid method parameters.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InvalidParamsError`})}};Object.defineProperty(mI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32602});var hI=class e extends iI{constructor(t={}){super({cause:t.cause,code:e.code,data:t.data,message:t.message??`Internal JSON-RPC error.`,stack:t.stack}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.InternalError`})}};Object.defineProperty(hI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32603});var gI=class e extends iI{constructor(t={}){super({code:e.code,data:t.data,message:t.message??`Failed to parse JSON-RPC response.`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`RpcResponse.ParseError`})}};Object.defineProperty(gI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:-32700});var _I=class extends Error{constructor(e,t){super(t),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`ProviderRpcError`}),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`details`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.code=e,this.details=t}},vI=class extends _I{constructor({message:e=`The user rejected the request.`}={}){super(4001,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UserRejectedRequestError`})}};Object.defineProperty(vI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4001});var yI=class extends _I{constructor({message:e=`The requested method and/or account has not been authorized by the user.`}={}){super(4100,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnauthorizedError`})}};Object.defineProperty(yI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4100});var bI=class extends _I{constructor({message:e=`The provider does not support the requested method.`}={}){super(4200,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnsupportedMethodError`})}};Object.defineProperty(bI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4200});var xI=class extends _I{constructor({message:e=`The provider is disconnected from all chains.`}={}){super(4900,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.DisconnectedError`})}};Object.defineProperty(xI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4900});var SI=class extends _I{constructor({message:e=`The provider is not connected to the requested chain.`}={}){super(4901,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.ChainDisconnectedError`})}};Object.defineProperty(SI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4901});var CI=class extends _I{constructor({message:e=`An error occurred when attempting to switch chain.`}={}){super(4902,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.SwitchChainError`})}};Object.defineProperty(CI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:4902});var wI=class extends _I{constructor({message:e=`This Wallet does not support a capability that was not marked as optional.`}={}){super(5700,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnsupportedNonOptionalCapabilityError`})}};Object.defineProperty(wI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5700});var TI=class extends _I{constructor({message:e=`This Wallet does not support the requested chain ID.`}={}){super(5710,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnsupportedChainIdError`})}};Object.defineProperty(TI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5710});var EI=class extends _I{constructor({message:e=`There is already a bundle submitted with this ID.`}={}){super(5720,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.DuplicateIdError`})}};Object.defineProperty(EI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5720});var DI=class extends _I{constructor({message:e=`This bundle id is unknown / has not been submitted.`}={}){super(5730,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.UnknownBundleIdError`})}};Object.defineProperty(DI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5730});var OI=class extends _I{constructor({message:e=`The call bundle is too large for the Wallet to process.`}={}){super(5740,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.BundleTooLargeError`})}};Object.defineProperty(OI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5740});var kI=class extends _I{constructor({message:e=`The Wallet can support atomicity after an upgrade, but the user rejected the upgrade.`}={}){super(5750,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.AtomicReadyWalletRejectedUpgradeError`})}};Object.defineProperty(kI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5750});var AI=class extends _I{constructor({message:e=`The wallet does not support atomic execution but the request requires it.`}={}){super(5760,e),Object.defineProperty(this,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.AtomicityNotSupportedError`})}};Object.defineProperty(AI,`code`,{enumerable:!0,configurable:!0,writable:!0,value:5760});function jI(){let e=new QF.default;return{get eventNames(){return e.eventNames.bind(e)},get listenerCount(){return e.listenerCount.bind(e)},get listeners(){return e.listeners.bind(e)},addListener:e.addListener.bind(e),emit:e.emit.bind(e),off:e.off.bind(e),on:e.on.bind(e),once:e.once.bind(e),removeAllListeners:e.removeAllListeners.bind(e),removeListener:e.removeListener.bind(e)}}function MI(e,t={}){if(!e)throw new PI;return{...e,async request(t){try{let n=await e.request(t);return n&&typeof n==`object`&&`jsonrpc`in n?nI(n):n}catch(e){throw NI(e)}}}}function NI(e){let t=rI(e);if(t instanceof hI){if(!t.data)return t;let{code:e}=t.data;if(e===xI.code)return new xI(t);if(e===SI.code)return new SI(t);if(e===vI.code)return new vI(t);if(e===yI.code)return new yI(t);if(e===bI.code)return new bI(t);if(e===CI.code)return new CI(t);if(e===kI.code)return new kI(t);if(e===AI.code)return new AI(t);if(e===OI.code)return new OI(t);if(e===DI.code)return new DI(t);if(e===EI.code)return new EI(t);if(e===TI.code)return new TI(t);if(e===wI.code)return new wI(t)}return t}var PI=class extends z{constructor(){super("`provider` is undefined."),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Provider.IsUndefinedError`})}};function FI(e,t){if(SL(e)>t)throw new OL({givenSize:SL(e),maxSize:t})}function II(e,t){if(typeof t==`number`&&t>0&&t>SL(e)-1)throw new kL({offset:t,position:`start`,size:SL(e)})}function LI(e,t,n){if(typeof t==`number`&&typeof n==`number`&&SL(e)!==n-t)throw new kL({offset:n,position:`end`,size:SL(e)})}var RI={zero:48,nine:57,A:65,F:70,a:97,f:102};function zI(e){if(e>=RI.zero&&e<=RI.nine)return e-RI.zero;if(e>=RI.A&&e<=RI.F)return e-(RI.A-10);if(e>=RI.a&&e<=RI.f)return e-(RI.a-10)}function BI(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;if(e.length>r)throw new AL({size:e.length,targetSize:r,type:`Bytes`});let i=new Uint8Array(r);for(let t=0;tt)throw new fL({givenSize:iL(e),maxSize:t})}function HI(e,t){if(typeof t==`number`&&t>0&&t>iL(e)-1)throw new pL({offset:t,position:`start`,size:iL(e)})}function UI(e,t,n){if(typeof t==`number`&&typeof n==`number`&&iL(e)!==n-t)throw new pL({offset:n,position:`end`,size:iL(e)})}function WI(e,t={}){let{dir:n,size:r=32}=t;if(r===0)return e;let i=e.replace(`0x`,``);if(i.length>r*2)throw new mL({size:Math.ceil(i.length/2),targetSize:r,type:`Hex`});return`0x${i[n===`right`?`padEnd`:`padStart`](r*2,`0`)}`}function GI(e,t={}){let{dir:n=`left`}=t,r=e.replace(`0x`,``),i=0;for(let e=0;et.toString(16).padStart(2,`0`));function JI(e,t={}){let{strict:n=!1}=t;if(!e||typeof e!=`string`)throw new uL(e);if(n&&!/^0x[0-9a-fA-F]*$/.test(e)||!e.startsWith(`0x`))throw new dL(e)}function YI(...e){return`0x${e.reduce((e,t)=>e+t.replace(`0x`,``),``)}`}function XI(e){return e instanceof Uint8Array?QI(e):Array.isArray(e)?QI(new Uint8Array(e)):e}function ZI(e,t={}){let n=`0x${Number(e)}`;return typeof t.size==`number`?(VI(n,t.size),tL(n,t.size)):n}function QI(e,t={}){let n=``;for(let t=0;ta||i>1n?r:r-a-1n}function sL(e,t={}){let{signed:n,size:r}=t;return Number(!n&&!r?e:oL(e,t))}function cL(e,t={}){let{strict:n=!1}=t;try{return JI(e,{strict:n}),!0}catch{return!1}}var lL=class extends z{constructor({max:e,min:t,signed:n,size:r,value:i}){super(`Number \`${i}\` is not in safe${r?` ${r*8}-bit`:``}${n?` signed`:` unsigned`} integer range ${e?`(\`${t}\` to \`${e}\`)`:`(above \`${t}\`)`}`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.IntegerOutOfRangeError`})}},uL=class extends z{constructor(e){super(`Value \`${typeof e==`object`?ZF(e):e}\` of type \`${typeof e}\` is an invalid hex type.`,{metaMessages:['Hex types must be represented as `"0x${string}"`.']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexTypeError`})}},dL=class extends z{constructor(e){super(`Value \`${e}\` is an invalid hex value.`,{metaMessages:['Hex values must start with `"0x"` and contain only hexadecimal characters (0-9, a-f, A-F).']}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.InvalidHexValueError`})}},fL=class extends z{constructor({givenSize:e,maxSize:t}){super(`Size cannot exceed \`${t}\` bytes. Given size: \`${e}\` bytes.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeOverflowError`})}},pL=class extends z{constructor({offset:e,position:t,size:n}){super(`Slice ${t===`start`?`starting`:`ending`} at offset \`${e}\` is out-of-bounds (size: \`${n}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SliceOffsetOutOfBoundsError`})}},mL=class extends z{constructor({size:e,targetSize:t,type:n}){super(`${n.charAt(0).toUpperCase()}${n.slice(1).toLowerCase()} size (\`${e}\`) exceeds padding size (\`${t}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Hex.SizeExceedsPaddingSizeError`})}},hL=new TextEncoder;function gL(e){if(!(e instanceof Uint8Array)&&(!e||typeof e!=`object`||!(`BYTES_PER_ELEMENT`in e)||e.BYTES_PER_ELEMENT!==1||e.constructor.name!==`Uint8Array`))throw new DL(e)}function _L(e){return e instanceof Uint8Array?e:typeof e==`string`?yL(e):vL(e)}function vL(e){return e instanceof Uint8Array?e:new Uint8Array(e)}function yL(e,t={}){let{size:n}=t,r=e;n&&(VI(e,n),r=nL(e,n));let i=r.slice(2);i.length%2&&(i=`0${i}`);let a=i.length/2,o=new Uint8Array(a);for(let e=0,t=0;ethis.maxSize){let e=this.keys().next().value;e&&this.delete(e)}return this}}(8192)}.checksum;ti();function ML(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=$r(_L(e));return n===`Bytes`?r:QI(r)}function NL(e,t={}){let{as:n=typeof e==`string`?`Hex`:`Bytes`}=t,r=yd(_L(e));return n===`Bytes`?r:QI(r)}function PL(e,t={}){let{compressed:n}=t,{prefix:r,x:i,y:a}=e;if(n===!1||typeof i==`bigint`&&typeof a==`bigint`){if(r!==4)throw new BL({prefix:r,cause:new HL});return}if(n===!0||typeof i==`bigint`&&a===void 0){if(r!==3&&r!==2)throw new BL({prefix:r,cause:new VL});return}throw new zL({publicKey:e})}function FL(e){let t=(()=>{if(cL(e))return LL(e);if(EL(e))return IL(e);let{prefix:t,x:n,y:r}=e;return typeof n==`bigint`&&typeof r==`bigint`?{prefix:t??4,x:n,y:r}:{prefix:t,x:n}})();return PL(t),t}function IL(e){return LL(QI(e))}function LL(e){if(e.length!==132&&e.length!==130&&e.length!==68)throw new UL({publicKey:e});return e.length===130?{prefix:4,x:BigInt(rL(e,0,32)),y:BigInt(rL(e,32,64))}:e.length===132?{prefix:Number(rL(e,0,1)),x:BigInt(rL(e,1,33)),y:BigInt(rL(e,33,65))}:{prefix:Number(rL(e,0,1)),x:BigInt(rL(e,1,33))}}function RL(e,t={}){PL(e);let{prefix:n,x:r,y:i}=e,{includePrefix:a=!0}=t;return YI(a?$I(n,{size:1}):`0x`,$I(r,{size:32}),typeof i==`bigint`?$I(i,{size:32}):`0x`)}var zL=class extends z{constructor({publicKey:e}){super(`Value \`${ZF(e)}\` is not a valid public key.`,{metaMessages:[`Public key must contain:`,"- an `x` and `prefix` value (compressed)","- an `x`, `y`, and `prefix` value (uncompressed)"]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidError`})}},BL=class extends z{constructor({prefix:e,cause:t}){super(`Prefix "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidPrefixError`})}},VL=class extends z{constructor(){super(`Prefix must be 2 or 3 for compressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidCompressedPrefixError`})}},HL=class extends z{constructor(){super(`Prefix must be 4 for uncompressed public keys.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidUncompressedPrefixError`})}},UL=class extends z{constructor({publicKey:e}){super(`Value \`${e}\` is an invalid public key size.`,{metaMessages:[`Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).`,`Received ${iL(XI(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`PublicKey.InvalidSerializedSizeError`})}},WL=/^0x[a-fA-F0-9]{40}$/;function GL(e,t={}){let{strict:n=!0}=t;if(!WL.test(e))throw new ZL({address:e,cause:new QL});if(n){if(e.toLowerCase()===e)return;if(KL(e)!==e)throw new ZL({address:e,cause:new $L})}}function KL(e){if(jL.has(e))return jL.get(e);GL(e,{strict:!1});let t=e.substring(2).toLowerCase(),n=ML(bL(t),{as:`Bytes`}),r=t.split(``);for(let e=0;e<40;e+=2)n[e>>1]>>4>=8&&r[e]&&(r[e]=r[e].toUpperCase()),(n[e>>1]&15)>=8&&r[e+1]&&(r[e+1]=r[e+1].toUpperCase());let i=`0x${r.join(``)}`;return jL.set(e,i),i}function qL(e,t={}){let{checksum:n=!1}=t;return GL(e),n?KL(e):e}function JL(e,t={}){return qL(`0x${ML(`0x${RL(e).slice(4)}`).substring(26)}`,t)}function YL(e,t){return GL(e,{strict:!1}),GL(t,{strict:!1}),e.toLowerCase()===t.toLowerCase()}function XL(e,t={}){let{strict:n=!0}=t??{};try{return GL(e,{strict:n}),!0}catch{return!1}}var ZL=class extends z{constructor({address:e,cause:t}){super(`Address "${e}" is invalid.`,{cause:t}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidAddressError`})}},QL=class extends z{constructor(){super(`Address is not a 20 byte (40 hexadecimal character) value.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidInputError`})}},$L=class extends z{constructor(){super(`Address does not match its checksum counterpart.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Address.InvalidChecksumError`})}},eR=!1;uu();function tR(e){let{privateKey:t}=e;return FL(Zl.ProjectivePoint.fromPrivateKey(XI(t).slice(2)))}function nR(e={}){let{as:t=`Hex`}=e,n=Zl.utils.randomPrivateKey();return t===`Hex`?QI(n):n}function rR(e){return JL(iR(e))}function iR(e){let{payload:t,signature:n}=e,{r,s:i,yParity:a}=n;return FL(new Zl.Signature(BigInt(r),BigInt(i)).addRecoveryBit(a).recoverPublicKey(XI(t).substring(2)))}function aR(e){let{extraEntropy:t=eR,hash:n,payload:r,privateKey:i}=e,{r:a,s:o,recovery:s}=Zl.sign(_L(r),_L(i),{extraEntropy:typeof t==`boolean`?t:XI(t).slice(2),lowS:!0,...n?{prehash:!0}:{}});return{r:a,s:o,yParity:s}}var oR=/^(.*)\[([0-9]*)\]$/,sR=/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/,cR=/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;2n**(8n-1n)-1n,2n**(16n-1n)-1n,2n**(24n-1n)-1n,2n**(32n-1n)-1n,2n**(40n-1n)-1n,2n**(48n-1n)-1n,2n**(56n-1n)-1n,2n**(64n-1n)-1n,2n**(72n-1n)-1n,2n**(80n-1n)-1n,2n**(88n-1n)-1n,2n**(96n-1n)-1n,2n**(104n-1n)-1n,2n**(112n-1n)-1n,2n**(120n-1n)-1n,2n**(128n-1n)-1n,2n**(136n-1n)-1n,2n**(144n-1n)-1n,2n**(152n-1n)-1n,2n**(160n-1n)-1n,2n**(168n-1n)-1n,2n**(176n-1n)-1n,2n**(184n-1n)-1n,2n**(192n-1n)-1n,2n**(200n-1n)-1n,2n**(208n-1n)-1n,2n**(216n-1n)-1n,2n**(224n-1n)-1n,2n**(232n-1n)-1n,2n**(240n-1n)-1n,2n**(248n-1n)-1n,2n**(256n-1n)-1n,-(2n**(8n-1n)),-(2n**(16n-1n)),-(2n**(24n-1n)),-(2n**(32n-1n)),-(2n**(40n-1n)),-(2n**(48n-1n)),-(2n**(56n-1n)),-(2n**(64n-1n)),-(2n**(72n-1n)),-(2n**(80n-1n)),-(2n**(88n-1n)),-(2n**(96n-1n)),-(2n**(104n-1n)),-(2n**(112n-1n)),-(2n**(120n-1n)),-(2n**(128n-1n)),-(2n**(136n-1n)),-(2n**(144n-1n)),-(2n**(152n-1n)),-(2n**(160n-1n)),-(2n**(168n-1n)),-(2n**(176n-1n)),-(2n**(184n-1n)),-(2n**(192n-1n)),-(2n**(200n-1n)),-(2n**(208n-1n)),-(2n**(216n-1n)),-(2n**(224n-1n)),-(2n**(232n-1n)),-(2n**(240n-1n)),-(2n**(248n-1n)),-(2n**(256n-1n));var lR=2n**256n-1n;function uR(e,t={}){let{recovered:n}=t;if(e.r===void 0||e.s===void 0||n&&e.yParity===void 0)throw new SR({signature:e});if(e.r<0n||e.r>lR)throw new CR({value:e.r});if(e.s<0n||e.s>lR)throw new wR({value:e.s});if(typeof e.yParity==`number`&&e.yParity!==0&&e.yParity!==1)throw new TR({value:e.yParity})}function dR(e){return fR(QI(e))}function fR(e){if(e.length!==130&&e.length!==132)throw new xR({signature:e});let t=BigInt(rL(e,0,32)),n=BigInt(rL(e,32,64)),r=(()=>{let t=Number(`0x${e.slice(130)}`);if(!Number.isNaN(t))try{return yR(t)}catch{throw new TR({value:t})}})();return r===void 0?{r:t,s:n}:{r:t,s:n,yParity:r}}function pR(e){if(e.r!==void 0&&e.s!==void 0)return mR(e)}function mR(e){let t=typeof e==`string`?fR(e):e instanceof Uint8Array?dR(e):typeof e.r==`string`?gR(e):e.v?hR(e):{r:e.r,s:e.s,...e.yParity===void 0?{}:{yParity:e.yParity}};return uR(t),t}function hR(e){return{r:e.r,s:e.s,yParity:yR(e.v)}}function gR(e){let t=(()=>{let t=e.v?Number(e.v):void 0,n=e.yParity?Number(e.yParity):void 0;if(typeof t==`number`&&typeof n!=`number`&&(n=yR(t)),typeof n!=`number`)throw new TR({value:e.yParity});return n})();return{r:BigInt(e.r),s:BigInt(e.s),yParity:t}}function _R(e){uR(e);let t=e.r,n=e.s;return YI($I(t,{size:32}),$I(n,{size:32}),typeof e.yParity==`number`?$I(bR(e.yParity),{size:1}):`0x`)}function vR(e){let{r:t,s:n,yParity:r}=e;return[r?`0x01`:`0x`,t===0n?`0x`:aL($I(t)),n===0n?`0x`:aL($I(n))]}function yR(e){if(e===0||e===27)return 0;if(e===1||e===28)return 1;if(e>=35)return e%2==0?1:0;throw new ER({value:e})}function bR(e){if(e===0)return 27;if(e===1)return 28;throw new TR({value:e})}var xR=class extends z{constructor({signature:e}){super(`Value \`${e}\` is an invalid signature size.`,{metaMessages:[`Expected: 64 bytes or 65 bytes.`,`Received ${iL(XI(e))} bytes.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSerializedSizeError`})}},SR=class extends z{constructor({signature:e}){super(`Signature \`${ZF(e)}\` is missing either an \`r\`, \`s\`, or \`yParity\` property.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.MissingPropertiesError`})}},CR=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid r value. r must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidRError`})}},wR=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid s value. s must be a positive integer less than 2^256.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidSError`})}},TR=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid y-parity value. Y-parity must be 0 or 1.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidYParityError`})}},ER=class extends z{constructor({value:e}){super(`Value \`${e}\` is an invalid v value. v must be 27, 28 or >=35.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Signature.InvalidVError`})}};function DR({checksumAddress:e,parameters:t,values:n}){let r=[];for(let i=0;i0?YI(t,e):t}}if(o)return{dynamic:!0,encoded:e}}return{dynamic:!1,encoded:YI(...s.map(({encoded:e})=>e))}}function MR(e,{type:t}){let[,n]=t.split(`bytes`),r=iL(e);if(!n){let t=e;return r%32!=0&&(t=nL(t,Math.ceil((e.length-2)/2/32)*32)),{dynamic:!0,encoded:YI(tL($I(r,{size:32})),t)}}if(r!==Number.parseInt(n,10))throw new qR({expectedSize:Number.parseInt(n,10),value:e});return{dynamic:!1,encoded:nL(e)}}function NR(e){if(typeof e!=`boolean`)throw new z(`Invalid boolean value: "${e}" (type: ${typeof e}). Expected: \`true\` or \`false\`.`);return{dynamic:!1,encoded:tL(ZI(e))}}function PR(e,{signed:t,size:n}){if(typeof n==`number`){let r=2n**(BigInt(n)-(t?1n:0n))-1n,i=t?-r-1n:0n;if(e>r||ee))}}function LR(e){let t=e.match(/^(.*)\[(\d+)?\]$/);return t?[t[2]?Number(t[2]):null,t[1]]:void 0}var RR={bytes:new Uint8Array,dataView:new DataView(new ArrayBuffer(0)),position:0,positionReadCount:new Map,recursiveReadCount:0,recursiveReadLimit:1/0,assertReadLimit(){if(this.recursiveReadCount>=this.recursiveReadLimit)throw new HR({count:this.recursiveReadCount+1,limit:this.recursiveReadLimit})},assertPosition(e){if(e<0||e>this.bytes.length-1)throw new VR({length:this.bytes.length,position:e})},decrementPosition(e){if(e<0)throw new BR({offset:e});let t=this.position-e;this.assertPosition(t),this.position=t},getReadCount(e){return this.positionReadCount.get(e||this.position)||0},incrementPosition(e){if(e<0)throw new BR({offset:e});let t=this.position+e;this.assertPosition(t),this.position=t},inspectByte(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectBytes(e,t){let n=t??this.position;return this.assertPosition(n+e-1),this.bytes.subarray(n,n+e)},inspectUint8(e){let t=e??this.position;return this.assertPosition(t),this.bytes[t]},inspectUint16(e){let t=e??this.position;return this.assertPosition(t+1),this.dataView.getUint16(t)},inspectUint24(e){let t=e??this.position;return this.assertPosition(t+2),(this.dataView.getUint16(t)<<8)+this.dataView.getUint8(t+2)},inspectUint32(e){let t=e??this.position;return this.assertPosition(t+3),this.dataView.getUint32(t)},pushByte(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushBytes(e){this.assertPosition(this.position+e.length-1),this.bytes.set(e,this.position),this.position+=e.length},pushUint8(e){this.assertPosition(this.position),this.bytes[this.position]=e,this.position++},pushUint16(e){this.assertPosition(this.position+1),this.dataView.setUint16(this.position,e),this.position+=2},pushUint24(e){this.assertPosition(this.position+2),this.dataView.setUint16(this.position,e>>8),this.dataView.setUint8(this.position+2,e&255),this.position+=3},pushUint32(e){this.assertPosition(this.position+3),this.dataView.setUint32(this.position,e),this.position+=4},readByte(){this.assertReadLimit(),this._touch();let e=this.inspectByte();return this.position++,e},readBytes(e,t){this.assertReadLimit(),this._touch();let n=this.inspectBytes(e);return this.position+=t??e,n},readUint8(){this.assertReadLimit(),this._touch();let e=this.inspectUint8();return this.position+=1,e},readUint16(){this.assertReadLimit(),this._touch();let e=this.inspectUint16();return this.position+=2,e},readUint24(){this.assertReadLimit(),this._touch();let e=this.inspectUint24();return this.position+=3,e},readUint32(){this.assertReadLimit(),this._touch();let e=this.inspectUint32();return this.position+=4,e},get remaining(){return this.bytes.length-this.position},setPosition(e){let t=this.position;return this.assertPosition(e),this.position=e,()=>this.position=t},_touch(){if(this.recursiveReadLimit===1/0)return;let e=this.getReadCount();this.positionReadCount.set(this.position,e+1),e>0&&this.recursiveReadCount++}};function zR(e,{recursiveReadLimit:t=8192}={}){let n=Object.create(RR);return n.bytes=e,n.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength),n.positionReadCount=new Map,n.recursiveReadLimit=t,n}var BR=class extends z{constructor({offset:e}){super(`Offset \`${e}\` cannot be negative.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.NegativeOffsetError`})}},VR=class extends z{constructor({length:e,position:t}){super(`Position \`${t}\` is out of bounds (\`0 < position < ${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.PositionOutOfBoundsError`})}},HR=class extends z{constructor({count:e,limit:t}){super(`Recursive read limit of \`${t}\` exceeded (recursive read count: \`${e}\`).`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Cursor.RecursiveReadLimitExceededError`})}};kt();function UR(e,t,n){let{checksumAddress:r=!1}=n??{};if(e.length!==t.length)throw new JR({expectedLength:e.length,givenLength:t.length});let i=kR(DR({checksumAddress:r,parameters:e,values:t}));return i.length===0?`0x`:i}function WR(e,t){if(e.length!==t.length)throw new JR({expectedLength:e.length,givenLength:t.length});let n=[];for(let r=0;r{for(let n of e){let{name:e,type:r}=n,o=t[e],s=r.match(cR);if(s&&(typeof o==`number`||typeof o==`bigint`)){let[,e,t]=s;$I(o,{signed:e===`int`,size:Number.parseInt(t??``,10)/8})}if(r===`address`&&typeof o==`string`&&!XL(o))throw new ZL({address:o,cause:new QL});let c=r.match(sR);if(c){let[,e]=c;if(e&&iL(o)!==Number.parseInt(e,10))throw new iz({expectedSize:Number.parseInt(e,10),givenSize:iL(o)})}let l=i[r];l&&(fz(r),a(l,o))}};if(i.EIP712Domain&&t){if(typeof t!=`object`)throw new az({domain:t});a(i.EIP712Domain,t)}if(r!==`EIP712Domain`)if(i[r])a(i[r],n);else throw new oz({primaryType:r,types:i})}function QR(e){let{domain:t={},message:n,primaryType:r}=e,i={EIP712Domain:ez(t),...e.types};ZR({domain:t,message:n,primaryType:r,types:i});let a=[`0x19`,`0x01`];return t&&a.push(nz({domain:t,types:i})),r!==`EIP712Domain`&&a.push(rz({data:n,primaryType:r,types:i})),YI(...a)}function $R(e){let{primaryType:t,types:n}=e,r=``,i=dz({primaryType:t,types:n});i.delete(t);let a=[t,...Array.from(i).sort()];for(let e of a)r+=`${e}(${(n[e]??[]).map(({name:e,type:t})=>`${t} ${e}`).join(`,`)})`;return r}function ez(e){return[typeof e?.name==`string`&&{name:`name`,type:`string`},e?.version&&{name:`version`,type:`string`},(typeof e?.chainId==`number`||typeof e?.chainId==`bigint`)&&{name:`chainId`,type:`uint256`},e?.verifyingContract&&{name:`verifyingContract`,type:`address`},e?.salt&&{name:`salt`,type:`bytes32`}].filter(Boolean)}function tz(e){return ML(QR(e))}function nz(e){let{domain:t,types:n}=e;return rz({data:t,primaryType:`EIP712Domain`,types:{...n,EIP712Domain:n?.EIP712Domain||ez(t)}})}function rz(e){let{data:t,primaryType:n,types:r}=e;return ML(cz({data:t,primaryType:n,types:r}))}var iz=class extends z{constructor({expectedSize:e,givenSize:t}){super(`Expected bytes${e}, got bytes${t}.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.BytesSizeMismatchError`})}},az=class extends z{constructor({domain:e}){super(`Invalid domain "${ZF(e)}".`,{metaMessages:[`Must be a valid EIP-712 domain.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidDomainError`})}},oz=class extends z{constructor({primaryType:e,types:t}){super(`Invalid primary type \`${e}\` must be one of \`${JSON.stringify(Object.keys(t))}\`.`,{metaMessages:["Check that the primary type is a key in `types`."]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidPrimaryTypeError`})}},sz=class extends z{constructor({type:e}){super(`Struct type "${e}" is invalid.`,{metaMessages:[`Struct type must not be a Solidity type.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`TypedData.InvalidStructTypeError`})}};function cz(e){let{data:t,primaryType:n,types:r}=e,i=[{type:`bytes32`}],a=[lz({primaryType:n,types:r})];for(let e of r[n]??[]){let[n,o]=uz({types:r,name:e.name,type:e.type,value:t[e.name]});i.push(n),a.push(o)}return UR(i,a)}function lz(e){let{primaryType:t,types:n}=e;return ML(eL($R({primaryType:t,types:n})))}function uz(e){let{types:t,name:n,type:r,value:i}=e;if(t[r]!==void 0)return[{type:`bytes32`},ML(cz({data:i,primaryType:r,types:t}))];if(r===`bytes`)return i=`0x${(i.length%2?`0`:``)+i.slice(2)}`,[{type:`bytes32`},ML(i,{as:`Hex`})];if(r===`string`)return[{type:`bytes32`},ML(bL(i),{as:`Hex`})];if(r.lastIndexOf(`]`)===r.length-1){let e=r.slice(0,r.lastIndexOf(`[`)),a=i.map(r=>uz({name:n,type:e,types:t,value:r}));return[{type:`bytes32`},ML(UR(a.map(([e])=>e),a.map(([,e])=>e)))]}return[{type:r},i]}function dz(e,t=new Set){let{primaryType:n,types:r}=e,i=n.match(/^\w*/u)?.[0];if(t.has(i)||r[i]===void 0)return t;t.add(i);for(let e of r[i])dz({primaryType:e.type,types:r},t);return t}function fz(e){if(e===`address`||e===`bool`||e===`string`||e.startsWith(`bytes`)||e.startsWith(`uint`)||e.startsWith(`int`))throw new sz({type:e})}gi(),Ei();function pz(e){if(typeof e==`string`){if(!Ci(e,{strict:!1}))throw new hi({address:e});return{address:e,type:`json-rpc`}}if(!Ci(e.address,{strict:!1}))throw new hi({address:e.address});return{address:e.address,nonceManager:e.nonceManager,sign:e.sign,signAuthorization:e.signAuthorization,signMessage:e.signMessage,signTransaction:e.signTransaction,signTypedData:e.signTypedData,source:`custom`,type:`local`}}function mz(e){let t=!0,n=``,r=0,i=``,a=!1;for(let o=0;ohz(Object.values(e)[n],t)):/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(r)?n===`number`||n===`bigint`:/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(r)?n===`string`||e instanceof Uint8Array:/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(r)?Array.isArray(e)&&e.every(e=>hz(e,{...t,type:r.replace(/(\[[0-9]{0,}\])$/,``)})):!1}}function gz(e,t,n){for(let r in e){let i=e[r],a=t[r];if(i.type===`tuple`&&a.type===`tuple`&&`components`in i&&`components`in a)return gz(i.components,a.components,n[r]);let o=[i.type,a.type];if(o.includes(`address`)&&o.includes(`bytes20`)||(o.includes(`address`)&&o.includes(`string`)||o.includes(`address`)&&o.includes(`bytes`))&&XL(n[r],{strict:!1}))return o}}kt();function _z(e,t={}){let{prepare:n=!0}=t,r=Array.isArray(e)||typeof e==`string`?Tt(e):e;return{...r,...n?{hash:xz(r)}:{}}}function vz(e,t,n){let{args:r=[],prepare:i=!0}=n??{},a=cL(t,{strict:!1}),o=e.filter(e=>a?e.type===`function`||e.type===`error`?yz(e)===rL(t,0,4):e.type===`event`?xz(e)===t:!1:`name`in e&&e.name===t);if(o.length===0)throw new Cz({name:t});if(o.length===1)return{...o[0],...i?{hash:xz(o[0])}:{}};let s;for(let e of o)if(`inputs`in e){if(!r||r.length===0){if(!e.inputs||e.inputs.length===0)return{...e,...i?{hash:xz(e)}:{}};continue}if(e.inputs&&e.inputs.length!==0&&e.inputs.length===r.length&&r.every((t,n)=>{let r=`inputs`in e&&e.inputs[n];return r?hz(t,r):!1})){if(s&&`inputs`in s&&s.inputs){let t=gz(e.inputs,s.inputs,r);if(t)throw new Sz({abiItem:e,type:t[0]},{abiItem:s,type:t[1]})}s=e}}let c=(()=>{if(s)return s;let[e,...t]=o;return{...e,overloads:t}})();if(!c)throw new Cz({name:t});return{...c,...i?{hash:xz(c)}:{}}}function yz(...e){return rL(xz((()=>{if(Array.isArray(e[0])){let[t,n]=e;return vz(t,n)}return e[0]})()),0,4)}function bz(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return vz(t,n)}return e[0]})();return mz(typeof t==`string`?t:ue(t))}function xz(...e){let t=(()=>{if(Array.isArray(e[0])){let[t,n]=e;return vz(t,n)}return e[0]})();return typeof t!=`string`&&`hash`in t&&t.hash?t.hash:ML(eL(bz(t)))}var Sz=class extends z{constructor(e,t){super(`Found ambiguous types in overloaded ABI Items.`,{metaMessages:[`\`${e.type}\` in \`${mz(ue(e.abiItem))}\`, and`,`\`${t.type}\` in \`${mz(ue(t.abiItem))}\``,``,`These types encode differently and cannot be distinguished at runtime.`,`Remove one of the ambiguous items in the ABI.`]}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.AmbiguityError`})}},Cz=class extends z{constructor({name:e,data:t,type:n=`item`}){let r=e?` with name "${e}"`:t?` with data "${t}"`:``;super(`ABI ${n}${r} not found.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`AbiItem.NotFoundError`})}};function wz(...e){let[t,n=[]]=(()=>{if(Array.isArray(e[0])){let[t,n,r]=e;return[Tz(t,n,{args:r}),r]}let[t,n]=e;return[t,n]})(),{overloads:r}=t,i=r?Tz([t,...r],t.name,{args:n}):t,a=Ez(i),o=n.length>0?UR(i.inputs,n):void 0;return o?YI(a,o):a}function Tz(e,t,n){let r=vz(e,t,n);if(r.type!==`function`)throw new Cz({name:t,type:`function`});return r}function Ez(e){return yz(e)}function Dz(e){let{privateKey:t}=e;return FL($j.ProjectivePoint.fromPrivateKey(typeof t==`string`?t.slice(2):QI(t).slice(2)))}function Oz(e={}){let{as:t=`Hex`}=e,n=$j.utils.randomPrivateKey();return t===`Hex`?QI(n):n}function kz(e){let{extraEntropy:t=eR,hash:n,payload:r,privateKey:i}=e,{r:a,s:o,recovery:s}=$j.sign(r instanceof Uint8Array?r:yL(r),i instanceof Uint8Array?i:yL(i),{extraEntropy:typeof t==`boolean`?t:XI(t).slice(2),lowS:!0,...n?{prehash:!0}:{}});return{r:a,s:o,yParity:s}}function Az(e,t=0){if(!/^(-?)([0-9]*)\.?([0-9]*)$/.test(e))throw new jz({value:e});let[n=``,r=`0`]=e.split(`.`),i=n.startsWith(`-`);if(i&&(n=n.slice(1)),r=r.replace(/(0+)$/,``),t===0)Math.round(Number(`.${r}`))===1&&(n=`${BigInt(n)+1n}`),r=``;else if(r.length>t){let[e,i,a]=[r.slice(0,t-1),r.slice(t-1,t),r.slice(t)],o=Math.round(Number(`${i}.${a}`));r=o>9?`${BigInt(e)+BigInt(1)}0`.padStart(e.length+1,`0`):`${e}${o}`,r.length>t&&(r=r.slice(1),n=`${BigInt(n)+1n}`),r=r.slice(0,t)}else r=r.padEnd(t,`0`);return BigInt(`${i?`-`:``}${n}${r}`)}var jz=class extends z{constructor({value:e}){super(`Value \`${e}\` is not a valid decimal number.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Value.InvalidDecimalNumberError`})}},Mz=new TextEncoder,Nz=new TextDecoder,Pz=Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[t,e.charCodeAt(0)])),Fz={...Object.fromEntries(Array.from(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`).map((e,t)=>[e.charCodeAt(0),t])),61:0,45:62,95:63};function Iz(e,t={}){let{pad:n=!0,url:r=!1}=t,i=new Uint8Array(Math.ceil(e.length/3)*4);for(let t=0,n=0;n>18],i[t+1]=Pz[r>>12&63],i[t+2]=Pz[r>>6&63],i[t+3]=Pz[r&63]}let a=e.length%3,o=Math.floor(e.length/3)*4+(a&&a+1),s=Nz.decode(new Uint8Array(i.buffer,0,o));return n&&a===1&&(s+=`==`),n&&a===2&&(s+=`=`),r&&(s=s.replaceAll(`+`,`-`).replaceAll(`/`,`_`)),s}function Lz(e,t={}){return Iz(yL(e),t)}function Rz(e){let t=e.replace(/=+$/,``),n=t.length,r=new Uint8Array(n+3);Mz.encodeInto(t+`===`,r);for(let e=0,n=0;e>16,r[n+1]=t>>8&255,r[n+2]=t&255}let i=(n>>2)*3+(n%4&&n%4-1);return new Uint8Array(r.buffer,0,i)}function zz(e){let t=e[4]===0?5:4,n=t+32,r=e[n+2]===0?n+3:n+2,i=BigInt(QI(e.slice(t,n))),a=BigInt(QI(e.slice(r)));return{r:i,s:a>Qj.CURVE.n/2n?Qj.CURVE.n-a:a}}async function Bz(e){try{let t=e.getPublicKey();if(!t)throw new Yz;let n=new Uint8Array(t),r=await crypto.subtle.importKey(`spki`,new Uint8Array(n),{name:`ECDSA`,namedCurve:`P-256`,hash:`SHA-256`},!0,[`verify`]);return FL(new Uint8Array(await crypto.subtle.exportKey(`raw`,r)))}catch(t){if(t.message!==`Permission denied to access object`)throw t;let n=new Uint8Array(e.attestationObject),r=e=>{let t=new Uint8Array([e,88,32]);for(let e=0;en[e+r]===t))return e+t.length;throw new Yz},i=r(33),a=r(34);return FL(new Uint8Array([4,...n.slice(i,i+32),...n.slice(a,a+32)]))}}var Vz=Uint8Array.from([105,171,180,181,160,222,75,198,42,42,32,31,141,37,186,233]);async function Hz(e){let{createFn:t=window.navigator.credentials.create.bind(window.navigator.credentials),...n}=e,r=Gz(n);try{let e=await t(r);if(!e)throw new Yz;let n=e.response,i=await Bz(n);return{id:e.id,publicKey:i,raw:e}}catch(e){throw new Yz({cause:e})}}function Uz(e={}){let{flag:t=5,rpId:n=window.location.hostname,signCount:r=0}=e;return YI(NL(eL(n)),$I(t,{size:1}),$I(r,{size:4}))}function Wz(e){let{challenge:t,crossOrigin:n=!1,extraClientData:r,origin:i=window.location.origin}=e;return JSON.stringify({type:`webauthn.get`,challenge:Lz(t,{url:!0,pad:!1}),origin:i,crossOrigin:n,...r})}function Gz(e){let{attestation:t=`none`,authenticatorSelection:n={residentKey:`preferred`,requireResidentKey:!1,userVerification:`required`},challenge:r=Vz,excludeCredentialIds:i,extensions:a,name:o,rp:s={id:window.location.hostname,name:window.document.title},user:c}=e,l=c?.name??o;return{publicKey:{attestation:t,authenticatorSelection:n,challenge:typeof r==`string`?yL(r):r,...i?{excludeCredentials:i?.map(e=>({id:Rz(e),type:`public-key`}))}:{},pubKeyCredParams:[{type:`public-key`,alg:-7}],...a&&{extensions:a},rp:s,user:{id:c?.id??ML(bL(l),{as:`Bytes`}),name:l,displayName:c?.displayName??l}}}}function Kz(e){let{credentialId:t,challenge:n,extensions:r,rpId:i=window.location.hostname,userVerification:a=`required`}=e;return{publicKey:{...t?{allowCredentials:Array.isArray(t)?t.map(e=>({id:Rz(e),type:`public-key`})):[{id:Rz(t),type:`public-key`}]}:{},challenge:yL(n),...r&&{extensions:r},rpId:i,userVerification:a}}}function qz(e){let{challenge:t,crossOrigin:n,extraClientData:r,flag:i,origin:a,rpId:o,signCount:s,userVerification:c=`required`}=e,l=Uz({flag:i,rpId:o,signCount:s}),u=Wz({challenge:t,crossOrigin:n,extraClientData:r,origin:a}),d=NL(eL(u));return{metadata:{authenticatorData:l,clientDataJSON:u,challengeIndex:u.indexOf(`"challenge"`),typeIndex:u.indexOf(`"type"`),userVerificationRequired:c===`required`},payload:YI(l,d)}}async function Jz(e){let{getFn:t=window.navigator.credentials.get.bind(window.navigator.credentials),...n}=e,r=Kz(n);try{let e=await t(r);if(!e)throw new Xz;let n=e.response,i=String.fromCharCode(...new Uint8Array(n.clientDataJSON)),a=i.indexOf(`"challenge"`),o=i.indexOf(`"type"`),s=zz(new Uint8Array(n.signature));return{metadata:{authenticatorData:QI(new Uint8Array(n.authenticatorData)),clientDataJSON:i,challengeIndex:a,typeIndex:o,userVerificationRequired:r.publicKey.userVerification===`required`},signature:s,raw:e}}catch(e){throw new Xz({cause:e})}}var Yz=class extends z{constructor({cause:e}={}){super(`Failed to create credential.`,{cause:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`WebAuthnP256.CredentialCreationFailedError`})}},Xz=class extends z{constructor({cause:e}={}){super(`Failed to request credential.`,{cause:e}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`WebAuthnP256.CredentialRequestFailedError`})}};async function Zz(e={}){let{extractable:t=!1}=e,n=await globalThis.crypto.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},t,[`sign`,`verify`]),r=await globalThis.crypto.subtle.exportKey(`raw`,n.publicKey),i=FL(new Uint8Array(r));return{privateKey:n.privateKey,publicKey:i}}async function Qz(e){let{payload:t,privateKey:n}=e,r=await globalThis.crypto.subtle.sign({name:`ECDSA`,hash:`SHA-256`},n,_L(t)),i=vL(new Uint8Array(r)),a=wL(CL(i,0,32)),o=wL(CL(i,32,64));return o>Qj.CURVE.n/2n&&(o=Qj.CURVE.n-o),{r:a,s:o}}var $z=`0x32323232`,eB={p256:`p256`,secp256k1:`secp256k1`,webauthnp256:`webauthn-p256`},tB={admin:`admin`,normal:`session`},nB={0:`minute`,1:`hour`,2:`day`,3:`week`,4:`month`,5:`year`},rB={address:`secp256k1`,p256:`p256`,secp256k1:`secp256k1`,"webauthn-p256":`webauthnp256`},iB={admin:`admin`,session:`normal`},aB={address:2,p256:0,secp256k1:2,"webauthn-p256":1},oB={day:2,hour:1,minute:0,month:4,week:3,year:5};function sB(e={}){let t=Oz();return fB({...e,privateKey:t})}async function cB(e){let{createFn:t,label:n,rpId:r,userId:i}=e,a=await Hz({authenticatorSelection:{requireResidentKey:!0,residentKey:`required`,userVerification:`required`},createFn:t,extensions:{credProps:!0},rp:r?{id:r,name:r}:void 0,user:{displayName:n,id:new Uint8Array(i??bL(n)),name:n}});return mB({...e,credential:{id:a.id,publicKey:a.publicKey},id:i?TL(i):RL(a.publicKey,{includePrefix:!1})})}function lB(e={}){let t=Oz();return hB({...e,privateKey:t})}async function uB(e={}){let t=await Zz();return gB({...e,keyPair:t})}function dB(e,t={}){let{chainId:n=e.chainId}=t,{expiry:r=0,id:i,prehash:a=!1,role:o=`admin`,type:s}=e,c=(()=>{let t=e.publicKey;return t===`0x`?t:s===`secp256k1`||s===`address`?iL(t)===20||oL(rL(t,0,12))===0n?rL(t,-20):JL(LL(t)):t})();return{...e,chainId:n,expiry:r,hash:_B({publicKey:c,type:s}),id:(i??c).toLowerCase(),prehash:a,publicKey:c.toLowerCase(),role:o,type:s}}function fB(e){let{chainId:t,expiry:n,feeToken:r,permissions:i,privateKey:a,role:o}=e;return dB({chainId:t,expiry:n,feeToken:r,permissions:i,privateKey(){return a},publicKey:RL(Dz({privateKey:a}),{includePrefix:!1}),role:o,type:`p256`})}function pB(e,t){let{chainId:n}=t,{publicKey:r}=e,i=iL(r)===20||oL(rL(r,0,12))===0n,a={};for(let t of e.permissions)t.type===`call`&&(a.calls??=[],a.calls.push({signature:t.selector,to:t.to===`0x3232323232323232323232323232323232323232`?void 0:t.to})),t.type===`spend`&&(a.spend??=[],a.spend.push({limit:t.limit,period:t.period,token:t.token}));return dB({chainId:n,expiry:e.expiry,permissions:a,publicKey:e.publicKey,role:tB[e.role],type:i?`address`:eB[e.type]})}function mB(e){let{credential:t,id:n,rpId:r}=e,i=RL(t.publicKey,{includePrefix:!1});return dB({chainId:e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,id:n,permissions:e.permissions,privateKey:{credential:t,rpId:r},publicKey:i,role:e.role,type:`webauthn-p256`})}function hB(e){let{privateKey:t}=e,n=RL(Dz({privateKey:t}),{includePrefix:!1});return dB({chainId:e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,permissions:e.permissions,privateKey:{privateKey(){return t}},publicKey:n,role:e.role,type:`webauthn-p256`})}function gB(e){let{chainId:t,expiry:n,feeToken:r,keyPair:i,permissions:a,role:o}=e,{privateKey:s}=i;return dB({chainId:t,expiry:n,feeToken:r,permissions:a,prehash:!0,privateKey:s,publicKey:RL(i.publicKey,{includePrefix:!1}),role:o,type:`p256`})}function _B(e){let{type:t}=e,n=vB(e.publicKey);return ML(UR([{type:`uint8`},{type:`bytes32`}],[aB[t],ML(n)]))}function vB(e){return iL(e)<32?tL(e,32):e}async function yB(e,t){let{address:n,storage:r,webAuthn:i,wrap:a=!0}=t,{privateKey:o,publicKey:s,type:c}=e;if(!o)throw Error(`Key does not have a private key to sign with. - -Key: -`+ZF(e,null,2));let l=n?tz({domain:{verifyingContract:n},message:{digest:t.payload},primaryType:`ERC1271Sign`,types:{ERC1271Sign:[{name:`digest`,type:`bytes32`}]}}):t.payload,[u,d]=await(async()=>{if(c===`p256`){let{privateKey:t}=e;if(typeof t==`function`)return[_R(kz({payload:l,privateKey:t()})),!1];if(t instanceof CryptoKey)return[_R(await Qz({payload:l,privateKey:t})),!0]}if(c===`secp256k1`)return[_R(aR({payload:l,privateKey:o()})),!1];if(c===`webauthn-p256`){if(o.privateKey){let{payload:e,metadata:t}=qz({challenge:l,origin:`https://ithaca.xyz`,rpId:`ithaca.xyz`}),{r:n,s:r}=kz({hash:!0,payload:e,privateKey:o.privateKey()});return[CB({metadata:t,signature:{r:n,s:r}}),!1]}let{credential:t,rpId:n}=o,a=`porto.webauthnVerified.${e.hash}`,s=Date.now(),c=!0;if(r){let e=await r.getItem(a);c=!e||s-e>6e5}let{signature:{r:u,s:d},raw:f,metadata:p}=await Jz({challenge:l,credentialId:t.id,getFn:i?.getFn,rpId:n,userVerification:c?`required`:`preferred`}),m=f.response;if(!m?.userHandle)throw Error(`No user handle in response`,{cause:{response:m}});let h=TL(new Uint8Array(m.userHandle));if(e.id&&XL(e.id)&&!YL(e.id,h))throw Error(`supplied webauthn key "${e.id}" does not match signature webauthn key "${h}"`,{cause:{id:h,key:e}});return c&&r&&await r.setItem(a,s),[CB({metadata:p,signature:{r:u,s:d}}),!1]}throw Error(`Key type "${c}" is not supported.\n\nKey:\n`+ZF(e,null,2))})();return a?wB(u,{keyType:c,prehash:d,publicKey:s}):u}function bB(e,t={}){let{expiry:n=0,prehash:r=!1,publicKey:i,role:a=`admin`,type:o}=e,{feeTokens:s,orchestrator:c}=t,l=Object.entries(xB(e,{feeTokens:s})).map(([e,t])=>{if(e===`calls`)return t.map(({signature:e,to:t})=>({selector:e?cL(e)?e:Ez(e):$z,to:t??`0x3232323232323232323232323232323232323232`,type:`call`}));if(e!==`feeToken`){if(e===`spend`)return t.map(({limit:e,period:t,token:n})=>({limit:e,period:t,token:n,type:`spend`}));throw Error(`Invalid permission type "${e}".`)}}).flat().filter(Boolean);return e.role===`session`&&c&&l.push({selector:$z,to:c,type:`call`}),{expiry:n,permissions:l??[],prehash:r,publicKey:vB(i),role:iB[a],type:rB[o]}}function xB(e,t){let{permissions:n}=e,r=n?.calls?[...n.calls]:[],i=n?.spend?[...n.spend]:[],a=t.feeTokens?.filter(e=>e.feeToken);if(a&&a.length>0){let t=SB(e,{feeTokens:a});if(t){let e=-1,n=oB.year;for(let r=0;re.feeToken.symbol===t.symbol?!0:!e.feeToken.symbol||e.feeToken.symbol===`native`?t.address===dv:!1);if(!r)return;let i=Az(e.feeToken.limit,r.decimals);return{...r,value:i}}function CB(e){let{metadata:t,signature:n}=e;return UR(GR([`struct WebAuthnAuth { bytes authenticatorData; string clientDataJSON; uint256 challengeIndex; uint256 typeIndex; bytes32 r; bytes32 s; }`,`WebAuthnAuth auth`]),[{authenticatorData:t.authenticatorData,challengeIndex:BigInt(t.challengeIndex),clientDataJSON:t.clientDataJSON,r:$I(n.r,{size:32}),s:$I(n.s,{size:32}),typeIndex:BigInt(t.typeIndex)}])}function wB(e,t){let{keyType:n,prehash:r=!1,publicKey:i}=t,a=_B({publicKey:i,type:n});return WR([`bytes`,`bytes32`,`bool`],[e,a,r])}function TB(e){let t=typeof e==`string`?{address:e}:e,n=t.sign?`privateKey`:`porto`,{address:r,sign:i,signMessage:a,signTransaction:o,signTypedData:s,type:c}=pz({address:t.address,sign({hash:e}){if(n===`porto`)throw Error("`sign` not supported on porto accounts.");if(!t.sign)throw Error("`sign` not supported.");return t.sign({hash:e})},signMessage({message:e}){return this.sign({hash:Fh(e)})},signTransaction(){throw Error("`signTransaction` not supported on porto accounts.")},signTypedData(e){return this.sign({hash:Uh(e)})}});return{address:r,keys:t.keys??void 0,sign:i,signMessage:a,signTransaction:o,signTypedData:s,source:n,type:c}}function EB(e,t={}){let{keys:n}=t;return TB({address:JL(tR({privateKey:e})),keys:n,async sign({hash:t}){return _R(aR({payload:t,privateKey:e}))},source:`privateKey`})}function DB(e,t={}){let{key:n,role:r}=t;if(n!==null){if(typeof n==`object`)return n;if(e.keys&&e.keys.length>0)return typeof n==`number`?e.keys[n]:e.keys.find(e=>e.privateKey&&(!r||e.role===r))}}async function OB(e,t){let{storage:n,replaySafe:r=!0,wrap:i=!0,webAuthn:a}=t,o=DB(e,t),s=r?tz({domain:{verifyingContract:e.address},message:{digest:t.payload},primaryType:`ERC1271Sign`,types:{ERC1271Sign:[{name:`digest`,type:`bytes32`}]}}):t.payload,c=o?({hash:e})=>yB(o,{address:null,payload:e,storage:n,webAuthn:a,wrap:i}):e.source===`privateKey`?e.sign:void 0;if(!c)throw Error(`cannot find key to sign with.`);return await c({hash:s})}var kB=()=>`IntersectionObserver`in window&&`IntersectionObserverEntry`in window&&`intersectionRatio`in IntersectionObserverEntry.prototype&&`isVisible`in IntersectionObserverEntry.prototype;function AB(e={}){let{prefix:t=`[Porto]`}=e,n=new Set;return{error:MB(console.error,{prefix:t}),errorOnce:MB(console.error,{memo:n,prefix:t}),log:MB(console.log,{prefix:t}),logOnce:MB(console.log,{memo:n,prefix:t}),warn:MB(console.warn,{prefix:t}),warnOnce:MB(console.warn,{memo:n,prefix:t})}}var jB=AB();function MB(e,t={}){let{memo:n,prefix:r}=t;return(...t)=>{let i=t.join(` `);n?.has(i)||(n?.add(i),e(`${r} ${i}`))}}function NB(){let e=navigator.userAgent.toLowerCase();return e.includes(`safari`)&&!e.includes(`chrome`)}function PB(){let e=navigator.userAgent.toLowerCase();return(e.includes(`firefox`)||e.includes(`fxios`))&&!e.includes(`seamonkey`)}function FB(){return window.navigator?.userAgentData?.mobile?!0:navigator.maxTouchPoints>1||/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(navigator.userAgent)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(navigator.userAgent.slice(0,4))}function IB(){let e=()=>void 0,t=()=>void 0;return{promise:new Promise((n,r)=>{e=n,t=r}),reject:t,resolve:e}}function LB(e){if(Array.isArray(e))return e.map(LB);if(typeof e==`function`)return;if(typeof e!=`object`||!e)return e;if(Object.getPrototypeOf(e)!==Object.prototype)try{return structuredClone(e)}catch{return}let t={};for(let[n,r]of Object.entries(e))t[n]=LB(r);return t}function RB(e,t){let n=[],r=new Set;for(let i of e){let e=t(i);r.has(e)||(r.add(e),n.push(i))}return n}function zB(){return typeof globalThis<`u`&&`crypto`in globalThis?globalThis.crypto.randomUUID():crypto.randomUUID()}function BB(e,{enabled:t=!0,id:n}){if(!t||!n)return e();if(BB.cache.get(n))return BB.cache.get(n);let r=e().finally(()=>BB.cache.delete(n));return BB.cache.set(n,r),r}(function(e){e.cache=new Map})(BB||={});function VB(e){return e}function HB(e,t={}){let{targetOrigin:n}=t,r=new Map;return VB({destroy(){for(let t of r.values())e.removeEventListener(`message`,t)},on(t,i,a){function o(e){e.data.topic===t&&(a&&e.data.id!==a||n&&e.origin!==n||i(e.data.payload,e))}return e.addEventListener(`message`,o),r.set(t,o),()=>e.removeEventListener(`message`,o)},async send(t,r,i){let a=zB();return e.postMessage(LB({id:a,payload:r,topic:t}),i??n??`*`),{id:a,payload:r,topic:t}},async sendAsync(e,t,n){let{id:r}=await this.send(e,t,n);return new Promise(t=>this.on(e,t,r))}})}function UB(e){let{from:t,to:n,waitForReady:r=!1}=e,i=!1,a=IB();t.on(`ready`,a.resolve);let o=VB({destroy(){t.destroy(),n.destroy(),i&&a.reject()},on(e,n,r){return t.on(e,n,r)},async send(e,t){return i=!0,r&&await a.promise.finally(()=>i=!1),n.send(e,t)},async sendAsync(e,t){return i=!0,r&&await a.promise.finally(()=>i=!1),n.sendAsync(e,t)}});return{...o,ready(e){o.send(`ready`,e)},waitForReady(){return a.promise}}}var WB={local:`http://localhost:5175/dialog/`,prod:`https://id.porto.sh/dialog`,stg:`https://stg.id.porto.sh/dialog`};function GB(e){return e}function KB(e={}){let{skipProtocolCheck:t,skipUnsupported:n}=e,r=e=>!n&&NB()&&e?.some(e=>[`wallet_connect`,`eth_requestAccounts`].includes(e.method));return typeof window>`u`?JB():GB({name:`iframe`,setup(e){let{host:n,internal:i,theme:a,themeController:o}=e,{store:s}=i,c=qB().setup(e),l=!1,u=new URL(n),d=document.createElement(`dialog`);d.dataset.porto=``,d.setAttribute(`role`,`dialog`),d.setAttribute(`aria-closed`,`true`),d.setAttribute(`aria-label`,`Porto Wallet`),d.setAttribute(`hidden`,`until-found`),Object.assign(d.style,{background:`transparent`,border:`0`,outline:`0`,padding:`0`,position:`fixed`}),document.body.appendChild(d);let f=document.createElement(`iframe`);f.setAttribute(`data-testid`,`porto`);let p=[`payment`,`publickey-credentials-get ${u.origin}`,`publickey-credentials-create ${u.origin}`];PB()||p.push(`clipboard-write`),f.setAttribute(`allow`,p.join(`; `)),f.setAttribute(`tabindex`,`0`),f.setAttribute(`sandbox`,`allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox`),f.setAttribute(`src`,tV(n)),f.setAttribute(`title`,`Porto`),Object.assign(f.style,{backgroundColor:`transparent`,border:`0`,colorScheme:`light dark`,height:`100%`,left:`0`,position:`fixed`,top:`0`,width:`100%`});let m=document.createElement(`style`);m.innerHTML=` - dialog[data-porto]::backdrop { - background: transparent!important; - } - `,d.appendChild(m),d.appendChild(f);let h=UB({from:HB(window,{targetOrigin:u.origin}),to:HB(f.contentWindow,{targetOrigin:u.origin}),waitForReady:!0});o?._setup(h,!0);let g=window.matchMedia(`(max-width: 460px)`),_=()=>{h.send(`__internal`,{type:`resize`,width:g.matches?460:461})};g.addEventListener(`change`,_),h.on(`ready`,t=>{let n=e.internal.store.getState().chainIds.filter(e=>t.chainIds.includes(e));n.length===0&&(n=t.chainIds),s.setState(e=>({...e,chainIds:n})),h.send(`__internal`,{chainIds:n,mode:`iframe`,referrer:ZB(),theme:a,type:`init`}),_()}),h.on(`rpc-response`,e=>{r([e._request])&&(f.src=f.src),$B(s,e)}),h.on(`account`,e=>{eV(s,e)}),h.on(`__internal`,e=>{e.type===`switch`&&e.mode===`popup`&&(c.open(),c.syncRequests(s.getState().requestQueue))});let v=null,y=null,b=()=>QB(s),x=e=>{e.key===`Escape`&&QB(s)},S=new MutationObserver(e=>{for(let t of e){if(t.type!==`attributes`)continue;let e=t.attributeName;e&&e===`inert`&&d.removeAttribute(e)}});S.observe(d,{attributeOldValue:!0,attributes:!0});let ee=!1,C=()=>{ee&&(ee=!1,d.removeEventListener(`click`,b),document.removeEventListener(`keydown`,x),d.style.pointerEvents=`none`,y?.focus(),y=null,Object.assign(document.body.style,v??``),document.body.style.overflow=v?.overflow??``)},te=()=>{ee||(ee=!0,d.addEventListener(`click`,b),document.addEventListener(`keydown`,x),f.focus(),d.style.pointerEvents=`auto`,v=Object.assign({},document.body.style),document.body.style.overflow=`hidden`)},ne=!1,re=()=>{ne||(ne=!0,document.activeElement instanceof HTMLElement&&(y=document.activeElement),d.removeAttribute(`hidden`),d.removeAttribute(`aria-closed`),d.showModal())},ie=()=>{if(ne){ne=!1,d.setAttribute(`hidden`,`true`),d.setAttribute(`aria-closed`,`true`),d.close();for(let e of d.parentNode?Array.from(d.parentNode.children):[])e!==d&&e.hasAttribute(`inert`)&&e.removeAttribute(`inert`)}};return{close(){c.close(),l=!1,h.send(`__internal`,{mode:`iframe`,referrer:ZB(),type:`init`}),ie(),C()},destroy(){c.close(),l=!1,C(),ie(),c.destroy(),h.destroy(),d.remove(),S.disconnect(),g.removeEventListener(`change`,_)},open(){l||(l=!0,re(),te(),h.send(`__internal`,{mode:`iframe`,referrer:ZB(),type:`init`}))},async secure(){let{trustedHosts:e}=await h.waitForReady(),n=(()=>{if(t)return!0;let e=window.location.protocol.startsWith(`https`);return e||jB.warnOnce(`Detected insecure protocol (HTTP).`,`\n\nThe Porto iframe is not supported on HTTP origins (${window.location.origin})`,`due to lack of WebAuthn support.`,`See https://porto.sh/sdk#secure-origins-https for more information.`),e})(),r=kB(),i=!!e?.includes(window.location.hostname),a=!!(r||i);return a||jB.warnOnce([`Warning: Browser does not support IntersectionObserver v2 or host "${u.hostname}" is not trusted by Porto.`,`This may result in the dialog falling back to a popup.`,``,`Add "${u.hostname}" to the trusted hosts list to enable iframe dialog: https://github.com/ithacaxyz/porto/edit/main/src/trusted-hosts.ts`].join(` -`)),{frame:a,host:i,protocol:n}},async syncRequests(e){let{methodPolicies:t}=await h.waitForReady(),n=await this.secure(),i=e?.every(e=>t?.find(t=>t.method===e.request.method)?.modes?.headless===!0),a=r(e.map(e=>e.request));if(!i&&(a||!n.protocol||!n.frame))c.syncRequests(e);else{let n=e.some(e=>XB(e.request,{methodPolicies:t,targetOrigin:u.origin}));!l&&n&&this.open(),h.send(`rpc-requests`,e)}}}},supportsHeadless:!0})}function qB(e={}){if(typeof window>`u`)return JB();let{type:t=`auto`,size:n=YB}=e;return GB({name:`popup`,setup(e){let{host:r,internal:i,themeController:a}=e,{store:o}=i,s=new URL(r),c=null,l=t===`page`||t===`auto`&&FB()?`page`:`popup`;function u(){c&&QB(o)}let d=(()=>{let e=setInterval(()=>{c?.closed&&QB(o)},100);return()=>clearInterval(e)})(),f;return a?._setup(null,!0),{close(){c&&=(c.close(),null)},destroy(){this.close(),window.removeEventListener(`focus`,u),f?.destroy(),d()},open(){if(l===`popup`){let e=(window.innerWidth-n.width)/2+window.screenX,t=window.screenY+100;c=window.open(tV(r),`_blank`,`width=${n.width},height=${n.height},left=${e},top=${t}`)}else c=window.open(tV(r),`_blank`);if(!c)throw Error(`Failed to open popup`);f=UB({from:HB(window,{targetOrigin:s.origin}),to:HB(c,{targetOrigin:s.origin}),waitForReady:!0}),a?._setup(f,!1),f.send(`__internal`,{mode:l===`page`?`page`:`popup`,referrer:ZB(),theme:a?.getTheme()??e.theme,type:`init`}),f.on(`rpc-response`,e=>$B(o,e)),f.on(`account`,e=>{eV(o,e)}),window.removeEventListener(`focus`,u),window.addEventListener(`focus`,u)},async secure(){return{frame:!0,host:!0,protocol:!0}},async syncRequests(e){e.some(e=>XB(e.request))&&((!c||c.closed)&&this.open(),c?.focus()),f?.send(`rpc-requests`,e)}}},supportsHeadless:!1})}function JB(){return GB({name:`noop`,setup(){return{close(){},destroy(){},open(){},async secure(){return{frame:!0,host:!0,protocol:!0}},async syncRequests(){}}},supportsHeadless:!0})}var YB={height:282,width:360};function XB(e,t={}){let{methodPolicies:n,targetOrigin:r}=t,i=n?.find(t=>t.method===e.method);return i&&i.modes?.headless?!!(typeof i.modes.headless==`object`&&i.modes.headless.sameOrigin&&r!==window.location.origin):!0}function ZB(){return{icon:(()=>{let e=document.querySelector(`link[rel="icon"][media="(prefers-color-scheme: dark)"]`)?.href,t=document.querySelector(`link[rel="icon"][media="(prefers-color-scheme: light)"]`)?.href??document.querySelector(`link[rel="icon"]`)?.href;return e&&t&&e!==t?{dark:e,light:t}:window.matchMedia(`(prefers-color-scheme: dark)`).matches?e:t})(),title:document.title}}function QB(e){e.setState(e=>({...e,requestQueue:e.requestQueue.map(e=>({account:e.account,error:new vI,request:e.request,status:`error`}))}))}function $B(e,t){e.setState(e=>({...e,requestQueue:e.requestQueue.map(e=>e.request.id===t.id?t.error?{account:e.account,error:t.error,request:e.request,status:`error`}:{account:e.account,request:e.request,result:t.result,status:`success`}:e)}))}function eV(e,t){let{account:n}=t;e.setState(e=>({...e,accounts:[TB({address:n.address,keys:n.capabilities?.admins?.map(e=>dB(e))??[]})]}))}function tV(e){let t=new URL(e),n=new URLSearchParams(window.location.search);for(let[e,r]of n.entries())e.startsWith(`porto.`)&&t.searchParams.set(e.slice(6),r);return t.toString()}function nV(e){let t=new CustomEvent(`eip6963:announceProvider`,{detail:Object.freeze(e)});window.dispatchEvent(t);let n=()=>window.dispatchEvent(t);return window.addEventListener(`eip6963:requestProvider`,n),()=>window.removeEventListener(`eip6963:requestProvider`,n)}Object.freeze({status:`aborted`});function B(e,t,n){function r(n,r){if(n._zod||Object.defineProperty(n,`_zod`,{value:{def:r,constr:o,traits:new Set},enumerable:!1}),n._zod.traits.has(e))return;n._zod.traits.add(e),t(n,r);let i=o.prototype,a=Object.keys(i);for(let e=0;en?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,`name`,{value:e}),o}var rV=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}},iV={};function aV(e){return e&&Object.assign(iV,e),iV}function oV(e,t){return typeof t==`bigint`?t.toString():t}function sV(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function cV(e){return e==null}function lV(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}var uV=Symbol(`evaluating`);function dV(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==uV)return r===void 0&&(r=uV,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}function fV(e,t,n){Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0})}function pV(...e){let t={};for(let n of e)Object.assign(t,Object.getOwnPropertyDescriptors(n));return Object.defineProperties({},t)}var mV=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function hV(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function gV(e){if(hV(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(hV(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}var _V=new Set([`string`,`number`,`bigint`,`boolean`,`symbol`,`undefined`]);function vV(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function yV(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function bV(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error("Cannot specify both `message` and `error` params");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function xV(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function SV(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.pick() cannot be used on object schemas containing refinements`);return yV(e,pV(e._zod.def,{get shape(){let e={};for(let r in t){if(!(r in n.shape))throw Error(`Unrecognized key: "${r}"`);t[r]&&(e[r]=n.shape[r])}return fV(this,`shape`,e),e},checks:[]}))}function CV(e,t){let n=e._zod.def,r=n.checks;if(r&&r.length>0)throw Error(`.omit() cannot be used on object schemas containing refinements`);return yV(e,pV(e._zod.def,{get shape(){let r={...e._zod.def.shape};for(let e in t){if(!(e in n.shape))throw Error(`Unrecognized key: "${e}"`);t[e]&&delete r[e]}return fV(this,`shape`,r),r},checks:[]}))}function wV(e,t,n){let r=t._zod.def.checks;if(r&&r.length>0)throw Error(`.partial() cannot be used on object schemas containing refinements`);return yV(t,pV(t._zod.def,{get shape(){let r=t._zod.def.shape,i={...r};if(n)for(let t in n){if(!(t in r))throw Error(`Unrecognized key: "${t}"`);n[t]&&(i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t])}else for(let t in r)i[t]=e?new e({type:`optional`,innerType:r[t]}):r[t];return fV(this,`shape`,i),i},checks:[]}))}function TV(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function DV(e){return typeof e==`string`?e:e?.message}function OV(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=DV(e.inst?._zod.def?.error?.(e))??DV(t?.error?.(e))??DV(n.customError?.(e))??DV(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}function kV(e){return Array.isArray(e)?`array`:typeof e==`string`?`string`:`unknown`}var AV=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,`_zod`,{value:e._zod,enumerable:!1}),Object.defineProperty(e,`issues`,{value:t,enumerable:!1}),e.message=JSON.stringify(t,oV,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},jV=B(`$ZodError`,AV),MV=B(`$ZodError`,AV,{Parent:Error}),NV=e=>(t,n,r,i)=>{let a=r?Object.assign(r,{async:!1}):{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new rV;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>OV(e,a,aV())));throw mV(t,i?.callee),t}return o.value},PV=NV(MV),FV=(e=>async(t,n,r,i)=>{let a=r?Object.assign(r,{async:!0}):{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>OV(e,a,aV())));throw mV(t,i?.callee),t}return o.value})(MV),IV=(e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new rV;return a.issues.length?{success:!1,error:new(e??jV)(a.issues.map(e=>OV(e,i,aV())))}:{success:!0,data:a.value}})(MV),LV=(e=>async(t,n,r)=>{let i=r?Object.assign(r,{async:!0}):{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>OV(e,i,aV())))}:{success:!0,data:a.value}})(MV),RV=(e=>(t,n,r)=>{let i=r?Object.assign(r,{direction:`backward`}):{direction:`backward`};return NV(e)(t,n,i)})(MV),zV=(e=>(t,n,r)=>NV(e)(t,n,r))(MV),BV=e=>{let t=e?`[\\s\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\s\\S]*`;return RegExp(`^${t}$`)},VV=/^-?\d+n?$/,HV=/^-?\d+(?:\.\d+)?$/,UV=/^(?:true|false)$/i,WV=/^null$/i,GV=/^undefined$/i,KV=B(`$ZodCheck`,(e,t)=>{var n;e._zod??={},e._zod.def=t,(n=e._zod).onattach??(n.onattach=[])}),qV={number:`number`,bigint:`bigint`,object:`date`},JV=B(`$ZodCheckGreaterThan`,(e,t)=>{KV.init(e,t);let n=qV[typeof t.value];e._zod.onattach.push(e=>{let n=e._zod.bag,r=(t.inclusive?n.minimum:n.exclusiveMinimum)??-1/0;t.value>r&&(t.inclusive?n.minimum=t.value:n.exclusiveMinimum=t.value)}),e._zod.check=r=>{(t.inclusive?r.value>=t.value:r.value>t.value)||r.issues.push({origin:n,code:`too_small`,minimum:typeof t.value==`object`?t.value.getTime():t.value,input:r.value,inclusive:t.inclusive,inst:e,continue:!t.abort})}}),YV=B(`$ZodCheckMinLength`,(e,t)=>{var n;KV.init(e,t),(n=e._zod.def).when??(n.when=e=>{let t=e.value;return!cV(t)&&t.length!==void 0}),e._zod.onattach.push(e=>{let n=e._zod.bag.minimum??-1/0;t.minimum>n&&(e._zod.bag.minimum=t.minimum)}),e._zod.check=n=>{let r=n.value;if(r.length>=t.minimum)return;let i=kV(r);n.issues.push({origin:i,code:`too_small`,minimum:t.minimum,inclusive:!0,input:r,inst:e,continue:!t.abort})}}),XV=B(`$ZodCheckStringFormat`,(e,t)=>{var n,r;KV.init(e,t),e._zod.onattach.push(e=>{let n=e._zod.bag;n.format=t.format,t.pattern&&(n.patterns??=new Set,n.patterns.add(t.pattern))}),t.pattern?(n=e._zod).check??(n.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:t.format,input:n.value,...t.pattern?{pattern:t.pattern.toString()}:{},inst:e,continue:!t.abort})}):(r=e._zod).check??(r.check=()=>{})}),ZV=B(`$ZodCheckRegex`,(e,t)=>{XV.init(e,t),e._zod.check=n=>{t.pattern.lastIndex=0,!t.pattern.test(n.value)&&n.issues.push({origin:`string`,code:`invalid_format`,format:`regex`,input:n.value,pattern:t.pattern.toString(),inst:e,continue:!t.abort})}}),QV={major:4,minor:3,patch:6},$V=B(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=QV;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=TV(e),i;for(let a of t){if(a._zod.def.when){if(!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new rV;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=TV(e,t))});else{if(e.issues.length===t)continue;r||=TV(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(TV(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new rV;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new rV;return o.then(e=>t(e,r,a))}return t(o,r,a)}}dV(e,`~standard`,()=>({validate:t=>{try{let n=IV(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return LV(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),eH=B(`$ZodString`,(e,t)=>{$V.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??BV(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),tH=B(`$ZodNumber`,(e,t)=>{$V.init(e,t),e._zod.pattern=e._zod.bag.pattern??HV,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=Number(n.value)}catch{}let i=n.value;if(typeof i==`number`&&!Number.isNaN(i)&&Number.isFinite(i))return n;let a=typeof i==`number`?Number.isNaN(i)?`NaN`:Number.isFinite(i)?void 0:`Infinity`:void 0;return n.issues.push({expected:`number`,code:`invalid_type`,input:i,inst:e,...a?{received:a}:{}}),n}}),nH=B(`$ZodBoolean`,(e,t)=>{$V.init(e,t),e._zod.pattern=UV,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=!!n.value}catch{}let i=n.value;return typeof i==`boolean`||n.issues.push({expected:`boolean`,code:`invalid_type`,input:i,inst:e}),n}}),rH=B(`$ZodBigInt`,(e,t)=>{$V.init(e,t),e._zod.pattern=VV,e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=BigInt(n.value)}catch{}return typeof n.value==`bigint`||n.issues.push({expected:`bigint`,code:`invalid_type`,input:n.value,inst:e}),n}}),iH=B(`$ZodUndefined`,(e,t)=>{$V.init(e,t),e._zod.pattern=GV,e._zod.values=new Set([void 0]),e._zod.optin=`optional`,e._zod.optout=`optional`,e._zod.parse=(t,n)=>{let r=t.value;return r===void 0||t.issues.push({expected:`undefined`,code:`invalid_type`,input:r,inst:e}),t}}),aH=B(`$ZodNull`,(e,t)=>{$V.init(e,t),e._zod.pattern=WV,e._zod.values=new Set([null]),e._zod.parse=(t,n)=>{let r=t.value;return r===null||t.issues.push({expected:`null`,code:`invalid_type`,input:r,inst:e}),t}}),oH=B(`$ZodAny`,(e,t)=>{$V.init(e,t),e._zod.parse=e=>e}),sH=B(`$ZodUnknown`,(e,t)=>{$V.init(e,t),e._zod.parse=e=>e}),cH=B(`$ZodDate`,(e,t)=>{$V.init(e,t),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=new Date(n.value)}catch{}let i=n.value,a=i instanceof Date;return a&&!Number.isNaN(i.getTime())||n.issues.push({expected:`date`,code:`invalid_type`,input:i,...a?{received:`Invalid Date`}:{},inst:e}),n}});function lH(e,t,n){e.issues.length&&t.issues.push(...EV(n,e.issues)),t.value[n]=e.value}var uH=B(`$ZodArray`,(e,t)=>{$V.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;elH(t,n,e))):lH(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function dH(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...EV(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function fH(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key "${n}": expected a Zod schema`);let n=xV(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function pH(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optout===`optional`;for(let i in t){if(s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>dH(e,n,i,t,u))):dH(a,n,i,t,u)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}var mH=B(`$ZodObject`,(e,t)=>{if($V.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,`shape`,{get:()=>{let n={...e};return Object.defineProperty(t,`shape`,{value:n}),n}})}let n=sV(()=>fH(t));dV(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=hV,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e],r=n._zod.optout===`optional`,i=n._zod.run({value:s[e],issues:[]},o);i instanceof Promise?c.push(i.then(n=>dH(n,t,e,s,r))):dH(i,t,e,s,r)}return i?pH(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}});function hH(e,t,n,r){for(let n of e)if(n.issues.length===0)return t.value=n.value,t;let i=e.filter(e=>!TV(e));return i.length===1?(t.value=i[0].value,i[0]):(t.issues.push({code:`invalid_union`,input:t.value,inst:n,errors:e.map(e=>e.issues.map(e=>OV(e,r,aV())))}),t)}var gH=B(`$ZodUnion`,(e,t)=>{$V.init(e,t),dV(e._zod,`optin`,()=>t.options.some(e=>e._zod.optin===`optional`)?`optional`:void 0),dV(e._zod,`optout`,()=>t.options.some(e=>e._zod.optout===`optional`)?`optional`:void 0),dV(e._zod,`values`,()=>{if(t.options.every(e=>e._zod.values))return new Set(t.options.flatMap(e=>Array.from(e._zod.values)))}),dV(e._zod,`pattern`,()=>{if(t.options.every(e=>e._zod.pattern)){let e=t.options.map(e=>e._zod.pattern);return RegExp(`^(${e.map(e=>lV(e.source)).join(`|`)})$`)}});let n=t.options.length===1,r=t.options[0]._zod.run;e._zod.parse=(i,a)=>{if(n)return r(i,a);let o=!1,s=[];for(let e of t.options){let t=e._zod.run({value:i.value,issues:[]},a);if(t instanceof Promise)s.push(t),o=!0;else{if(t.issues.length===0)return t;s.push(t)}}return o?Promise.all(s).then(t=>hH(t,i,e,a)):hH(s,i,e,a)}}),_H=B(`$ZodDiscriminatedUnion`,(e,t)=>{t.inclusive=!1,gH.init(e,t);let n=e._zod.parse;dV(e._zod,`propValues`,()=>{let e={};for(let n of t.options){let r=n._zod.propValues;if(!r||Object.keys(r).length===0)throw Error(`Invalid discriminated union option at index "${t.options.indexOf(n)}"`);for(let[t,n]of Object.entries(r)){e[t]||(e[t]=new Set);for(let r of n)e[t].add(r)}}return e});let r=sV(()=>{let e=t.options,n=new Map;for(let r of e){let e=r._zod.propValues?.[t.discriminator];if(!e||e.size===0)throw Error(`Invalid discriminated union option at index "${t.options.indexOf(r)}"`);for(let t of e){if(n.has(t))throw Error(`Duplicate discriminator value "${String(t)}"`);n.set(t,r)}}return n});e._zod.parse=(i,a)=>{let o=i.value;if(!hV(o))return i.issues.push({code:`invalid_type`,expected:`object`,input:o,inst:e}),i;let s=r.value.get(o?.[t.discriminator]);return s?s._zod.run(i,a):t.unionFallback?n(i,a):(i.issues.push({code:`invalid_union`,errors:[],note:`No matching discriminator`,discriminator:t.discriminator,input:o,path:[t.discriminator],inst:e}),i)}}),vH=B(`$ZodTuple`,(e,t)=>{$V.init(e,t);let n=t.items;e._zod.parse=(r,i)=>{let a=r.value;if(!Array.isArray(a))return r.issues.push({input:a,inst:e,expected:`tuple`,code:`invalid_type`}),r;r.value=[];let o=[],s=[...n].reverse().findIndex(e=>e._zod.optin!==`optional`),c=s===-1?0:n.length-s;if(!t.rest){let t=a.length>n.length,i=a.length=a.length&&l>=c)continue;let t=e._zod.run({value:a[l],issues:[]},i);t instanceof Promise?o.push(t.then(e=>yH(e,r,l))):yH(t,r,l)}if(t.rest){let e=a.slice(n.length);for(let n of e){l++;let e=t.rest._zod.run({value:n,issues:[]},i);e instanceof Promise?o.push(e.then(e=>yH(e,r,l))):yH(e,r,l)}}return o.length?Promise.all(o).then(()=>r):r}});function yH(e,t,n){e.issues.length&&t.issues.push(...EV(n,e.issues)),t.value[n]=e.value}var bH=B(`$ZodRecord`,(e,t)=>{$V.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!gV(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[],o=t.keyType._zod.values;if(o){n.value={};let s=new Set;for(let e of o)if(typeof e==`string`||typeof e==`number`||typeof e==`symbol`){s.add(typeof e==`number`?e.toString():e);let o=t.valueType._zod.run({value:i[e],issues:[]},r);o instanceof Promise?a.push(o.then(t=>{t.issues.length&&n.issues.push(...EV(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...EV(e,o.issues)),n.value[e]=o.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`)continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&HV.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>OV(e,r,aV())),input:o,path:[o],inst:e});continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...EV(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...EV(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}}),xH=B(`$ZodLiteral`,(e,t)=>{if($V.init(e,t),t.values.length===0)throw Error(`Cannot create literal schema with no valid values`);let n=new Set(t.values);e._zod.values=n,e._zod.pattern=RegExp(`^(${t.values.map(e=>typeof e==`string`?vV(e):e?vV(e.toString()):String(e)).join(`|`)})$`),e._zod.parse=(r,i)=>{let a=r.value;return n.has(a)||r.issues.push({code:`invalid_value`,values:t.values,input:a,inst:e}),r}});function SH(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}var CH=B(`$ZodOptional`,(e,t)=>{$V.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,dV(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),dV(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${lV(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(t=>SH(t,e.value)):SH(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}}),wH=B(`$ZodNullable`,(e,t)=>{$V.init(e,t),dV(e._zod,`optin`,()=>t.innerType._zod.optin),dV(e._zod,`optout`,()=>t.innerType._zod.optout),dV(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${lV(e.source)}|null)$`):void 0}),dV(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,null]):void 0),e._zod.parse=(e,n)=>e.value===null?e:t.innerType._zod.run(e,n)}),TH=B(`$ZodPipe`,(e,t)=>{$V.init(e,t),dV(e._zod,`values`,()=>t.in._zod.values),dV(e._zod,`optin`,()=>t.in._zod.optin),dV(e._zod,`optout`,()=>t.out._zod.optout),dV(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if(n.direction===`backward`){let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>EH(e,t.in,n)):EH(r,t.in,n)}let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>EH(e,t.out,n)):EH(r,t.out,n)}});function EH(e,t,n){return e.issues.length?(e.aborted=!0,e):t._zod.run({value:e.value,issues:e.issues},n)}var DH=B(`$ZodCodec`,(e,t)=>{$V.init(e,t),dV(e._zod,`values`,()=>t.in._zod.values),dV(e._zod,`optin`,()=>t.in._zod.optin),dV(e._zod,`optout`,()=>t.out._zod.optout),dV(e._zod,`propValues`,()=>t.in._zod.propValues),e._zod.parse=(e,n)=>{if((n.direction||`forward`)===`forward`){let r=t.in._zod.run(e,n);return r instanceof Promise?r.then(e=>OH(e,t,n)):OH(r,t,n)}else{let r=t.out._zod.run(e,n);return r instanceof Promise?r.then(e=>OH(e,t,n)):OH(r,t,n)}}});function OH(e,t,n){if(e.issues.length)return e.aborted=!0,e;if((n.direction||`forward`)===`forward`){let r=t.transform(e.value,e);return r instanceof Promise?r.then(r=>kH(e,r,t.out,n)):kH(e,r,t.out,n)}else{let r=t.reverseTransform(e.value,e);return r instanceof Promise?r.then(r=>kH(e,r,t.in,n)):kH(e,r,t.in,n)}}function kH(e,t,n,r){return e.issues.length?(e.aborted=!0,e):n._zod.run({value:t,issues:e.issues},r)}var AH=B(`$ZodReadonly`,(e,t)=>{$V.init(e,t),dV(e._zod,`propValues`,()=>t.innerType._zod.propValues),dV(e._zod,`values`,()=>t.innerType._zod.values),dV(e._zod,`optin`,()=>t.innerType?._zod?.optin),dV(e._zod,`optout`,()=>t.innerType?._zod?.optout),e._zod.parse=(e,n)=>{if(n.direction===`backward`)return t.innerType._zod.run(e,n);let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(jH):jH(r)}});function jH(e){return e.value=Object.freeze(e.value),e}var MH=B(`$ZodTemplateLiteral`,(e,t)=>{$V.init(e,t);let n=[];for(let e of t.parts)if(typeof e==`object`&&e){if(!e._zod.pattern)throw Error(`Invalid template literal part, no pattern found: ${[...e._zod.traits].shift()}`);let t=e._zod.pattern instanceof RegExp?e._zod.pattern.source:e._zod.pattern;if(!t)throw Error(`Invalid template literal part: ${e._zod.traits}`);let r=t.startsWith(`^`)?1:0,i=t.endsWith(`$`)?t.length-1:t.length;n.push(t.slice(r,i))}else if(e===null||_V.has(typeof e))n.push(vV(`${e}`));else throw Error(`Invalid template literal part: ${e}`);e._zod.pattern=RegExp(`^${n.join(``)}$`),e._zod.parse=(n,r)=>typeof n.value==`string`?(e._zod.pattern.lastIndex=0,e._zod.pattern.test(n.value)||n.issues.push({input:n.value,inst:e,code:`invalid_format`,format:t.format??`template_literal`,pattern:e._zod.pattern.source}),n):(n.issues.push({input:n.value,inst:e,expected:`string`,code:`invalid_type`}),n)}),NH,PH=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function FH(){return new PH}(NH=globalThis).__zod_globalRegistry??(NH.__zod_globalRegistry=FH()),globalThis.__zod_globalRegistry;function IH(e,t){return new e({type:`string`,...bV(t)})}function LH(e,t){return new e({type:`number`,checks:[],...bV(t)})}function RH(e,t){return new e({type:`boolean`,...bV(t)})}function zH(e,t){return new e({type:`bigint`,...bV(t)})}function BH(e,t){return new e({type:`undefined`,...bV(t)})}function VH(e,t){return new e({type:`null`,...bV(t)})}function HH(e){return new e({type:`any`})}function UH(e){return new e({type:`unknown`})}function WH(e,t){return new e({type:`date`,...bV(t)})}function GH(e,t){return new JV({check:`greater_than`,...bV(t),value:e,inclusive:!0})}function KH(e,t){return new YV({check:`min_length`,...bV(t),minimum:e})}function qH(e,t){return new ZV({check:`string_format`,format:`regex`,...bV(t),pattern:e})}var JH=B(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);$V.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>PV(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>IV(e,t,n),e.parseAsync=async(t,n)=>FV(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>LV(e,t,n),e.check=(...n)=>e.clone({...t,checks:[...t.checks??[],...n.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]},{parent:!0}),e.with=e.check,e.clone=(t,n)=>yV(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),YH=B(`ZodMiniString`,(e,t)=>{eH.init(e,t),JH.init(e,t)});function V(e){return IH(YH,e)}var XH=B(`ZodMiniNumber`,(e,t)=>{tH.init(e,t),JH.init(e,t)});function H(e){return LH(XH,e)}var ZH=B(`ZodMiniBoolean`,(e,t)=>{nH.init(e,t),JH.init(e,t)});function QH(e){return RH(ZH,e)}var $H=B(`ZodMiniBigInt`,(e,t)=>{rH.init(e,t),JH.init(e,t)});function eU(e){return zH($H,e)}var tU=B(`ZodMiniUndefined`,(e,t)=>{iH.init(e,t),JH.init(e,t)});function nU(e){return BH(tU,e)}var rU=B(`ZodMiniNull`,(e,t)=>{aH.init(e,t),JH.init(e,t)});function iU(e){return VH(rU,e)}var aU=B(`ZodMiniAny`,(e,t)=>{oH.init(e,t),JH.init(e,t)});function oU(){return HH(aU)}var sU=B(`ZodMiniUnknown`,(e,t)=>{sH.init(e,t),JH.init(e,t)});function cU(){return UH(sU)}var lU=B(`ZodMiniDate`,(e,t)=>{cH.init(e,t),JH.init(e,t)});function uU(e){return WH(lU,e)}var dU=B(`ZodMiniArray`,(e,t)=>{uH.init(e,t),JH.init(e,t)});function U(e,t){return new dU({type:`array`,element:e,...bV(t)})}var fU=B(`ZodMiniObject`,(e,t)=>{mH.init(e,t),JH.init(e,t),dV(e,`shape`,()=>t.shape)});function W(e,t){return new fU({type:`object`,shape:e??{},...bV(t)})}function pU(e,t){return SV(e,t)}function mU(e,t){return CV(e,t)}function hU(e,t){return wV(CU,e,t)}var gU=B(`ZodMiniUnion`,(e,t)=>{gH.init(e,t),JH.init(e,t)});function G(e,t){return new gU({type:`union`,options:e,...bV(t)})}var _U=B(`ZodMiniDiscriminatedUnion`,(e,t)=>{_H.init(e,t),JH.init(e,t)});function vU(e,t,n){return new _U({type:`union`,options:t,discriminator:e,...bV(n)})}var yU=B(`ZodMiniTuple`,(e,t)=>{vH.init(e,t),JH.init(e,t)});function K(e,t,n){let r=t instanceof $V;return new yU({type:`tuple`,items:e,rest:r?t:null,...bV(r?n:t)})}var bU=B(`ZodMiniRecord`,(e,t)=>{bH.init(e,t),JH.init(e,t)});function xU(e,t,n){return new bU({type:`record`,keyType:e,valueType:t,...bV(n)})}var SU=B(`ZodMiniLiteral`,(e,t)=>{xH.init(e,t),JH.init(e,t)});function q(e,t){return new SU({type:`literal`,values:Array.isArray(e)?e:[e],...bV(t)})}var CU=B(`ZodMiniOptional`,(e,t)=>{CH.init(e,t),JH.init(e,t)});function J(e){return new CU({type:`optional`,innerType:e})}var wU=B(`ZodMiniNullable`,(e,t)=>{wH.init(e,t),JH.init(e,t)});function TU(e){return new wU({type:`nullable`,innerType:e})}function EU(e){return J(TU(e))}var DU=B(`ZodMiniPipe`,(e,t)=>{TH.init(e,t),JH.init(e,t)}),OU=B(`ZodMiniCodec`,(e,t)=>{DU.init(e,t),DH.init(e,t)});function kU(e,t,n){return new OU({type:`pipe`,in:e,out:t,transform:n.decode,reverseTransform:n.encode})}var AU=B(`ZodMiniReadonly`,(e,t)=>{AH.init(e,t),JH.init(e,t)});function Y(e){return new AU({type:`readonly`,innerType:e})}var jU=B(`ZodMiniTemplateLiteral`,(e,t)=>{MH.init(e,t),JH.init(e,t)});function MU(e,t){return new jU({type:`template_literal`,parts:e,...bV(t)})}function NU(e,t={}){return _z(e,t)}function PU(e,t,n){if(t===`Error`)return FU;if(t===`Panic`)return IU;if(cL(t,{strict:!1})){let e=rL(t,0,4);if(e===`0x08c379a0`)return FU;if(e===`0x4e487b71`)return IU}let r=vz(e,t,n);if(r.type!==`error`)throw new Cz({name:t,type:`error`});return r}var FU=NU({inputs:[{name:`message`,type:`string`}],name:`Error`,type:`error`}),IU=NU({inputs:[{name:`reason`,type:`uint8`}],name:`Panic`,type:`error`});D();var LU=class extends E{constructor(){super(`Function is not recognized.`,{metaMessages:[`This could be due to any of the following:`,` - The contract does not have the function,`,` - The address is not a contract.`],name:`FunctionSelectorNotRecognizedError`})}};function RU(e,t={}){return iv(e,t)}function zU(e){return ov(e)}Ha();function BU(e,t){let n=e.walk(e=>`data`in e);if(!n?.data)return e;if(n.data===zU(RU(`error FnSelectorNotRecognized()`)))return new LU;let r=null;for(let e of t.calls){let t=e;if(t.abi)try{if(!Va({abi:t.abi,data:n.data}))continue;r=t}catch{}}return r?ls(n,{abi:r.abi,address:r.to,args:r.args,functionName:r.functionName}):e}var X=()=>MU([`0x`,V()],{message:`Needs string in format ^0x[A-Fa-f0-9]{40}$.`}),Z=()=>MU([`0x`,V()],{message:`Needs string in format ^0x[A-Fa-f0-9]+$.`}),Q=()=>kU(Z(),H(),{decode:e=>sL(e),encode:e=>$I(e)}),VU=()=>kU(Z(),eU({message:`Required bigint`}),{decode:e=>oL(e),encode:e=>$I(e)});function HU(e){return G(e)}var UU=class extends z{constructor(){super(...arguments),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Schema.ValidationError`})}};function WU(e){let t=e,n=`Validation failed with ${t.issues.length} error${t.issues.length===1?``:`s`}:`;n+=` -`;for(let e of t.issues)e&&(n+=` -`,n+=GU(e));return new UU(n)}function GU(e,t=0){let n=KU(e.path),r=`- ${n?`${n}: `:``}`,i=` `.repeat(t+1),a=r;switch(e.code){case`invalid_type`:{let t=e.expected,n=e.input?qU(e):`undefined`;a+=`Expected ${t}. ${e.message===`Invalid input`?``:e.message}`,n!==`undefined`&&(a+=`but received ${n}`);break}case`too_big`:{let t=e.maximum,n=e.inclusive??!0;e.exact??!1?a+=`${e.origin} must be exactly ${t}`:a+=`${e.origin} must be ${n?`at most`:`less than`} ${t}`;break}case`too_small`:{let t=e.minimum,n=e.inclusive??!0;e.exact??!1?a+=`${e.origin} must be exactly ${t}`:a+=`${e.origin} must be ${n?`at least`:`greater than`} ${t}`;break}case`invalid_format`:switch(e.format){case`regex`:a+=`Must match pattern: ${e.pattern}`;break;case`starts_with`:a+=`Must start with "${e.prefix}"`;break;case`ends_with`:a+=`Must end with "${e.suffix}"`;break;case`includes`:a+=`Must include "${e.includes}"`;break;case`template_literal`:a+=`Must match pattern: ${e.pattern}`;break;default:a+=`Invalid ${e.format} format`}break;case`not_multiple_of`:a+=`Number must be a multiple of ${e.divisor}`;break;case`unrecognized_keys`:{let t=e.keys.map(e=>`"${e}"`).join(`, `);a+=`Unrecognized key${e.keys.length>1?`s`:``}: ${t}`;break}case`invalid_union`:{let n=e.errors&&e.errors.length>0;a+=`Invalid union value.`,n&&e.errors.forEach(e=>{e.length>0&&e.forEach(e=>{a+=` -`,a+=i,a+=GU(e,t+1)})});break}case`invalid_key`:a+=`Invalid ${e.origin} key`,e.issues&&e.issues.length>0&&e.issues.forEach(e=>{a+=` -`,a+=i,a+=GU(e,t+1)});break;case`invalid_element`:a+=`Invalid ${e.origin} element at key "${e.key}"`,e.issues&&e.issues.length>0&&e.issues.forEach(e=>{a+=` -`,a+=i,a+=GU(e,t+1)});break;case`invalid_value`:{let t=e.values.map(e=>JSON.stringify(e)).join(`, `);e.values.length>1?a+=`Expected one of: ${t}`:a+=`Expected ${t}`;break}case`custom`:a+=e.message||`Custom validation failed`;break;default:a+=e.message||`Validation failed`}return a}function KU(e){return e.length===0?``:"at `"+e.map((e,t)=>typeof e==`number`?`[${e}]`:typeof e==`symbol`?`[${e.toString()}]`:/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e)&&t>0?`.${e}`:t===0&&/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e)?e:`["${e}"]`).join(``)+"`"}function qU(e){let t=e.input;if(t===void 0)return`undefined`;if(t===null)return`null`;let n=typeof t;return n===`object`?Array.isArray(t)?`array`:t instanceof Date?`date`:t instanceof Map?`map`:t instanceof Set?`set`:`object`:n}var JU=G([W({selector:Z(),to:X(),type:q(`call`)}),W({limit:VU(),period:G([q(`minute`),q(`hour`),q(`day`),q(`week`),q(`month`),q(`year`)]),token:J(G([X(),iU()])),type:q(`spend`)})]),YU=W({expiry:Q(),prehash:J(QH()),publicKey:Z(),role:G([q(`admin`),q(`normal`)]),type:G([q(`p256`),q(`secp256k1`),q(`webauthnp256`)])}),XU=W({...YU.shape,permissions:Y(U(JU))}),ZU;(function(e){e.AssetDiffAsset=G([W({address:J(G([X(),iU()])),decimals:J(G([H(),iU()])),direction:G([q(`incoming`),q(`outgoing`)]),fiat:J(W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})})),name:J(G([V(),iU()])),symbol:V(),type:q(`erc20`),value:VU()}),W({address:J(G([X(),iU()])),direction:G([q(`incoming`),q(`outgoing`)]),fiat:J(W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})})),name:J(G([V(),iU()])),symbol:V(),type:q(`erc721`),uri:V(),value:VU()}),W({address:iU(),decimals:J(G([H(),iU()])),direction:G([q(`incoming`),q(`outgoing`)]),fiat:J(W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})})),symbol:V(),type:iU(),value:VU()})]),e.Response=xU(Z(),Y(U(Y(K([X(),Y(U(e.AssetDiffAsset))])))))})(ZU||={});var QU;(function(e){e.Request=Y(U(XU)),e.Response=Y(U(W({...XU.shape,hash:Z()})))})(QU||={});var $U;(function(e){e.Response=xU(Z(),W({currency:V(),value:V()}))})($U||={});var eW;(function(e){e.Request=W({feePayer:J(X()),feeToken:J(X()),nonce:J(VU())})})(eW||={});var tW;(function(e){e.Request=Y(U(W({address:X(),value:VU()})))})(tW||={});var nW;(function(e){e.Request=Y(U(W({hash:Z()}))),e.Response=Y(U(W({hash:Z()})))})(nW||={});var rW=W({eoa:X(),executionData:Z(),nonce:Z(),signature:Z()}),iW=W({...rW.shape,chainId:Q()}),aW=G([W({combinedGas:VU(),encodedFundTransfers:Y(U(Z())),encodedPreCalls:Y(U(Z())),eoa:X(),executionData:Z(),expiry:VU(),funder:X(),funderSignature:Z(),isMultichain:QH(),nonce:VU(),payer:X(),paymentAmount:VU(),paymentMaxAmount:VU(),paymentRecipient:X(),paymentSignature:Z(),paymentToken:X(),settler:X(),settlerContext:Z(),signature:Z(),supportedAccountImplementation:X()}),W({combinedGas:VU(),encodedFundTransfers:Y(U(Z())),encodedPreCalls:Y(U(Z())),eoa:X(),executionData:Z(),expiry:VU(),funder:X(),funderSignature:Z(),isMultichain:QH(),nonce:VU(),payer:X(),paymentRecipient:X(),paymentSignature:Z(),paymentToken:X(),prePaymentAmount:VU(),prePaymentMaxAmount:VU(),settler:X(),settlerContext:Z(),signature:Z(),supportedAccountImplementation:X(),totalPaymentAmount:VU(),totalPaymentMaxAmount:VU()})]);VU();var oW=W({address:G([X(),iU()]),decimals:J(H()),deficit:VU(),fiat:J(W({currency:V(),value:V()})),name:J(V()),required:VU(),symbol:J(V())}),sW=W({additionalAuthorization:EU(W({address:X(),chainId:Q(),nonce:Q(),r:Z(),s:Z(),yParity:Q()})),assetDeficits:J(U(oW)),authorizationAddress:J(G([X(),iU()])),chainId:Q(),ethPrice:VU(),extraPayment:VU(),feeTokenDeficit:VU(),intent:aW,nativeFeeEstimate:W({maxFeePerGas:VU(),maxPriorityFeePerGas:VU()}),orchestrator:X(),paymentTokenDecimals:H(),txGas:VU()}),cW=W({...W({multiChainRoot:J(G([Z(),iU()])),quotes:Y(U(sW)).check(KH(1)),ttl:H()}).shape,hash:Z(),r:Z(),s:Z(),v:J(Z()),yParity:J(Z())}),lW=W({address:X(),decimals:H(),feeToken:J(QH()),interop:J(QH()),nativeRate:J(VU()),symbol:V(),uid:V()}),uW=V().check(qH(/^[A-Z0-9]+$/)),dW=W({address:X(),chainId:Q(),nonce:Q()}),fW=W({...dW.shape,r:Z(),s:Z(),yParity:Q()}),pW=W({data:J(Z()),to:X(),value:J(VU())}),mW;(function(e){e.Parameters=W({address:X(),secret:V()}),e.Request=W({method:q(`account_getOnrampContactInfo`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(V()),phone:J(V()),phoneVerifiedAt:J(H())})})(mW||={});var hW;(function(e){e.Parameters=W({address:X()}),e.Request=W({method:q(`account_onrampStatus`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(H()),phone:J(H())})})(hW||={});var gW;(function(e){e.Parameters=W({phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_resendVerifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(gW||={});var _W;(function(e){e.Parameters=W({email:V().check(qH(/^.*@.*$/)),walletAddress:X()}),e.Request=W({method:q(`account_setEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(_W||={});var vW;(function(e){e.Parameters=W({phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_setPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(vW||={});var yW;(function(e){e.Parameters=W({chainId:Q(),email:V(),signature:Z(),token:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(yW||={});var bW;(function(e){e.Parameters=W({code:V(),phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(bW||={});var xW;(function(e){e.Request=W({method:q(`health`),params:nU()}),e.Response=W({quoteSigner:X(),status:V(),version:V()})})(xW||={});var SW;(function(e){e.Parameters=W({address:X(),chainId:Q(),tokenAddress:X(),value:VU()}),e.Request=W({method:q(`wallet_addFaucetFunds`),params:Y(K([e.Parameters]))}),e.Response=W({message:J(V()),transactionHash:Z()})})(SW||={});var CW;(function(e){e.Parameters=W({chainId:Q(),id:Z()}),e.Request=W({method:q(`wallet_getAccounts`),params:Y(K([e.Parameters]))}),e.Response=Y(U(W({address:X(),keys:QU.Response})))})(CW||={});var wW;(function(e){e.Parameters=W({address:X()}),e.Request=W({method:q(`wallet_getAuthorization`),params:Y(K([e.Parameters]))}),e.Response=W({authorization:fW,data:Z(),to:X()})})(wW||={});var TW;(function(e){e.Request=W({method:q(`wallet_getCapabilities`),params:J(K([Y(U(H()))]))});let t=W({address:X(),version:J(G([V(),iU()]))});e.Response=xU(Z(),W({contracts:W({accountImplementation:t,accountProxy:t,legacyAccountImplementations:Y(U(t)),legacyOrchestrators:Y(U(G([W({orchestrator:t,simulator:t}),t]))),orchestrator:t,simulator:t}),fees:W({quoteConfig:W({constantRate:J(G([H(),iU()])),gas:J(W({intentBuffer:J(H()),txBuffer:J(H())})),rateTtl:H(),ttl:H()}),recipient:X(),tokens:Y(U(lW))})}))})(TW||={});var EW;(function(e){let t=G([q(`native`),q(`erc20`),q(`erc721`),V()]);e.Parameters=W({account:X(),assetFilter:J(xU(Z(),Y(U(W({address:G([X(),q(`native`)]),type:t}))))),assetTypeFilter:J(Y(U(t))),chainFilter:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getAssets`),params:Y(K([e.Parameters]))}),e.Price=W({currency:V(),value:kU(V(),H(),{decode:e=>Number(e),encode:e=>String(e)})}),e.Response=xU(V(),Y(U(HU([W({address:X(),balance:VU(),metadata:TU(W({decimals:H(),fiat:EU(e.Price),name:V(),symbol:V()})),type:q(`erc20`)}),W({address:TU(q(`native`)),balance:VU(),metadata:TU(W({decimals:H(),fiat:EU(e.Price),name:J(V()),symbol:J(V())})),type:q(`native`)})]))))})(EW||={});var DW;(function(e){e.Request=W({method:q(`wallet_getCallsStatus`),params:Y(K([Z()]))}),e.Response=W({id:V(),receipts:J(Y(U(W({blockHash:Z(),blockNumber:Q(),chainId:Q(),gasUsed:Q(),logs:Y(U(W({address:X(),data:Z(),topics:Y(U(Z()))}))),status:Z(),transactionHash:Z()})))),status:H()})})(DW||={});var OW;(function(e){e.Parameters=W({address:X(),index:J(H()),limit:H(),sort:G([q(`asc`),q(`desc`)])}),e.Request=W({method:q(`wallet_getCallsHistory`),params:Y(K([e.Parameters]))}),e.Transaction=W({chainId:Q(),transactionHash:Z()}),e.Capabilities=W({assetDiffs:J(ZU.Response),feeTotals:J($U.Response),quotes:J(Y(U(sW)))}),e.Entry=W({capabilities:e.Capabilities,id:Z(),index:H(),keyHash:Z(),status:H(),timestamp:H(),transactions:Y(U(e.Transaction))}),e.Response=Y(U(e.Entry))})(OW||={});var kW;(function(e){e.Parameters=W({address:X(),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getKeys`),params:Y(K([e.Parameters]))}),e.Response=xU(Z(),QU.Response)})(kW||={});var AW;(function(e){e.Capabilities=W({authorizeKeys:J(QU.Request),meta:eW.Request,preCall:J(QH()),preCalls:J(Y(U(rW))),requiredFunds:J(tW.Request),revokeKeys:J(nW.Request)}),e.ResponseCapabilities=W({assetDiffs:J(ZU.Response),authorizeKeys:EU(QU.Response),feePayerDigest:J(Z()),feeSignature:J(Z()),feeTotals:J($U.Response),revokeKeys:EU(nW.Response)}),e.Parameters=W({calls:Y(U(pW)),capabilities:e.Capabilities,chainId:Q(),from:J(X()),key:J(W({prehash:QH(),publicKey:Z(),type:YU.shape.type}))}),e.Request=W({method:q(`wallet_prepareCalls`),params:Y(K([e.Parameters]))}),e.Response=W({capabilities:e.ResponseCapabilities,context:W({preCall:J(hU(iW)),quote:J(hU(cW))}),digest:Z(),key:EU(W({prehash:QH(),publicKey:Z(),type:YU.shape.type})),signature:Z(),typedData:W({domain:G([W({chainId:G([Q(),H()]),name:V(),verifyingContract:X(),version:V()}),W({})]),message:xU(V(),cU()),primaryType:V(),types:xU(V(),cU())})})})(AW||={});var jW;(function(e){e.Capabilities=W({authorizeKeys:QU.Request}),e.Parameters=W({address:X(),capabilities:e.Capabilities,chainId:J(H()),delegation:X()}),e.Request=W({method:q(`wallet_prepareUpgradeAccount`),params:Y(K([e.Parameters]))}),e.Response=W({capabilities:e.Capabilities,chainId:Q(),context:W({address:X(),authorization:dW,chainId:Q(),preCall:rW}),digests:W({auth:Z(),exec:Z()}),typedData:W({domain:G([W({chainId:G([Q(),H()]),name:V(),verifyingContract:X(),version:V()}),W({})]),message:xU(V(),cU()),primaryType:V(),types:xU(V(),cU())})})})(jW||={});var MW;(function(e){e.Request=W({method:q(`wallet_feeTokens`),params:J(nU())}),e.Response=xU(Z(),Y(U(W({address:X(),decimals:H(),nativeRate:J(VU()),symbol:V()}))))})(MW||={});var NW;(function(e){e.Parameters=W({capabilities:J(W({feeSignature:J(Z())})),context:W({preCall:J(hU(iW)),quote:J(hU(cW))}),key:J(W({prehash:QH(),publicKey:Z(),type:YU.shape.type})),signature:Z()}),e.Request=W({method:q(`wallet_sendPreparedCalls`),params:Y(K([e.Parameters]))}),e.Response=W({id:Z()})})(NW||={});var PW;(function(e){e.Parameters=W({context:W({address:X(),authorization:dW,chainId:Q(),preCall:rW}),signatures:W({auth:Z(),exec:Z()})}),e.Request=W({method:q(`wallet_upgradeAccount`),params:Y(K([e.Parameters]))}),e.Response=nU()})(PW||={});var FW;(function(e){e.Parameters=W({address:Z(),chainId:Q(),digest:Z(),signature:Z()}),e.Request=W({method:q(`wallet_verifySignature`),params:Y(K([e.Parameters]))}),e.Response=W({proof:EU(W({account:X(),initPreCall:EU(rW),keyHash:Z()})),valid:QH()})})(FW||={});async function IW(e,t){try{let n=`wallet_getAuthorization`,r=await Mm(()=>e.request({method:n,params:[RV(wW.Parameters,t)]}),{cacheKey:`${e.uid}.${n}.${t.address}`});return zV(wW.Response,r)}catch(e){throw $W(e),e}}async function LW(e,t={}){let n=(()=>{if(t.chainId)return[t.chainId];if(t.chainIds!==`all`)return t.chainIds?t.chainIds:[e.chain.id]})();try{let r=`wallet_getCapabilities`,i=await Mm(()=>e.request({method:r,params:n?[n]:void 0},{retryCount:0}),{cacheKey:`${e.uid}.${r}.${n?.join(`,`)}`}),a=t.raw?i:zV(TW.Response,i);return t.chainIds?a:Object.values(a)[0]}catch(e){throw $W(e),e}}async function RW(e,t){let{account:n,assetFilter:r,assetTypeFilter:i,chainFilter:a}=t;try{let t=await e.request({method:`wallet_getAssets`,params:[RV(EW.Parameters,{account:n,assetFilter:r,assetTypeFilter:i,chainFilter:a})]}),o=zV(EW.Response,t),s=Object.entries(o).reduce((e,[t,n])=>(e[sL(t)]=n,e),{}),c={};for(let e of Object.values(s))for(let t of e){let e=JSON.stringify(t.metadata);c[e]={...t,balance:t.balance+(c[e]?.balance??0n)}}return{...s,0:Object.values(c)}}catch(e){throw $W(e),e}}async function zW(e,t){let{id:n}=t;try{let t=await e.request({method:`wallet_getCallsStatus`,params:[n]});return zV(DW.Response,t)}catch(e){throw $W(e),e}}async function BW(e,t){try{let n=await e.request({method:`wallet_getCallsHistory`,params:[RV(OW.Parameters,t)]});return zV(OW.Response,n)}catch(e){throw $W(e),e}}async function VW(e,t){let{address:n,chainIds:r}=t;try{let t=await e.request({method:`wallet_getKeys`,params:[RV(kW.Parameters,{address:n,chainIds:r})]});return zV(kW.Response,t)}catch(e){throw $W(e),e}}async function HW(e){let t=`health`,n=await Mm(()=>e.request({method:t}),{cacheKey:`${e.uid}.${t}`});return zV(xW.Response,n)}async function UW(e,t){let{address:n,capabilities:r,chain:i=e.chain,key:a}=t,o=t.calls.map(e=>({data:e.abi?wz(Tz(e.abi,e.functionName),e.args):e.data??`0x`,to:e.to,value:e.value??0n}));try{let t=await e.request({method:`wallet_prepareCalls`,params:[RV(AW.Parameters,{calls:o,capabilities:{...r,meta:{...r?.meta}},chainId:i?.id,from:n,key:a?{prehash:a.prehash,publicKey:a.publicKey,type:a.type}:void 0})]},{retryCount:0});return Object.assign(zV(AW.Response,t),{_raw:t})}catch(e){throw $W(e),ZW(e,{calls:t.calls}),e}}async function WW(e,t){let{address:n,chain:r=e.chain,delegation:i,...a}=t;try{let t=await e.request({method:`wallet_prepareUpgradeAccount`,params:[RV(jW.Parameters,LB({address:n,capabilities:a,chainId:r?.id,delegation:i}))]},{retryCount:0});return zV(jW.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function GW(e,t){let{capabilities:n,context:r,key:i,signature:a}=t;try{let t=await e.request({method:`wallet_sendPreparedCalls`,params:[RV(NW.Parameters,{capabilities:n,context:{preCall:r.preCall,quote:r.quote},key:i?{prehash:i.prehash,publicKey:i.publicKey,type:i.type}:void 0,signature:a})]},{retryCount:0});return zV(NW.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function KW(e,t){let{email:n,walletAddress:r}=t;try{let t=await e.request({method:`account_setEmail`,params:[RV(_W.Parameters,{email:n,walletAddress:r})]},{retryCount:0});return zV(_W.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function qW(e,t){let{context:n,signatures:r}=t;try{await e.request({method:`wallet_upgradeAccount`,params:[RV(PW.Parameters,{context:n,signatures:r})]},{retryCount:0})}catch(e){throw $W(e),ZW(e),e}}async function JW(e,t){let{chainId:n,email:r,signature:i,token:a,walletAddress:o}=t;try{let t=await e.request({method:`account_verifyEmail`,params:[RV(yW.Parameters,{chainId:n,email:r,signature:i,token:a,walletAddress:o})]},{retryCount:0});return zV(yW.Response,t)}catch(e){throw $W(e),ZW(e),e}}async function YW(e,t){let{signature:n}=t,{signature:r,capabilities:{feeSignature:i,...a},...o}=t.response,s=QW({capabilities:a,...o}),c=rR({payload:ML(eL(JSON.stringify(s))),signature:fR(n)}),{quoteSigner:l}=await HW(e);return c===l}async function XW(e,t){let{address:n,chain:r=e.chain,digest:i,signature:a}=t;try{async function t(){return{proof:null,valid:await vv(e,{address:n,hash:i,signature:a})}}let o=await(async()=>{let o=await e.request({method:`wallet_verifySignature`,params:[RV(FW.Parameters,{address:n,chainId:r?.id,digest:i,signature:a})]},{retryCount:0}).catch(t);return o.valid?o:t()})();return zV(FW.Response,o)}catch(e){throw $W(e),e}}function ZW(e,{calls:t}={}){if(!(e instanceof E))return;let n=e=>{try{if(e.name===`ContractFunctionExecutionError`){let t=e.cause.name===`ContractFunctionRevertedError`?e.cause.data:void 0;if(t)return PU([t.abiItem],t.errorName)}let t=e.walk(e=>!(e instanceof Error)&&e.code===3);if(!t)return;let{data:n,message:r}=t;return n===`0xd0d5039b`?NU(`error Unauthorized()`):{inputs:[],name:(r??n).split(`(`)[0],type:`error`}}catch{return}},r=BU(e,{calls:t??[]}),i=n(r);if(!(r===e&&!i))throw new eG(Object.assign(r,{abiError:i}))}function QW(e){if(typeof e==`object`&&e){if(Array.isArray(e))return e.map(QW);let t={};for(let n of Object.keys(e).sort())t[n]=QW(e[n]);return t}return e}function $W(e){if(e.name===`$ZodError`)throw WU(e)}var eG=class extends z{constructor(e){super(`An error occurred while executing calls.`,{cause:e,metaMessages:[e.abiError&&`Reason: `+e.abiError.name].filter(Boolean)}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Rpc.ExecutionError`}),Object.defineProperty(this,`abiError`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.abiError=e.abiError}},tG={anvil:{http:`http://localhost:9119`},prod:{http:`https://rpc.porto.sh`},stg:{http:`https://stg-rpc.porto.sh`}};function nG(e){return t=>{let n=e.public(t),r=e.relay(t);return qv({key:nG.type,name:`Relay Proxy`,async request({method:e,params:t},i){return rG(e)?r.request({method:e,params:t},i):n.request({method:e,params:t},i)},type:nG.type})}}(function(e){e.type=`relayProxy`})(nG||={});function rG(e){return!!(e.startsWith(`wallet_`)||e.startsWith(`account_`)||e===`health`)}var iG=new Map;function aG(e,t={}){let{config:n,id:r,store:i}=e._internal,{chains:a,relay:o}=n,s=i.getState(),c=t.chainId??s.chainIds[0],l=a.find(e=>e.id===c);if(!l)throw Error([`Could not find a compatible Porto chain on the given chain configuration.`,``,`Provided chains: [${a.map(e=>`${e.name} (id: ${e.id})`).join(`, `)}]`,`Needed chain (id): ${c}`,`Please add this chain (id) to your chain configuration.`].join(` -`));let u=nG({public:n.transports[l.id]??Yv(l.rpcUrls.default.http.map(e=>$v(e))),relay:o}),d=[r,ZF(l)].filter(Boolean).join(`:`);if(iG.has(d))return iG.get(d);let f=rh({...t,chain:l,pollingInterval:1e3,transport:u});return iG.set(d,f),f}var oG=W({chainId:J(Q()),expiry:Q(),hash:Z(),id:Z(),prehash:J(QH()),publicKey:Z(),role:G([q(`admin`),q(`session`)]),type:G([q(`address`),q(`p256`),q(`secp256k1`),q(`webauthn-p256`)])}),sG=Y(U(HU([W({signature:V(),to:X()}),W({signature:V()}),W({to:X()})])).check(KH(1))),cG=W({limit:G([MU([H(),`.`,H()]),MU([H()])]).check(qH(/^\d+(\.\d+)?$/)),symbol:J(G([q(`native`),uW]))}),lG=W({addresses:Y(U(X()))}),uG=Y(U(W({limit:VU(),period:G([q(`minute`),q(`hour`),q(`day`),q(`week`),q(`month`),q(`year`)]),token:J(X())}))),dG=W({calls:J(sG),signatureVerification:J(lG),spend:J(uG)}),fG=W({...oG.shape,feeToken:J(TU(cG)),permissions:J(dG)}),pG=W({address:X(),chainId:J(Q()),expiry:H(),id:Z(),key:pU(oG,{publicKey:!0,type:!0}),permissions:W({calls:sG,signatureVerification:J(lG),spend:J(uG)})}),mG=W({address:J(X()),chainId:J(Q()),expiry:H().check(GH(1)),feeToken:TU(cG),key:J(pU(oG,{publicKey:!0,type:!0})),permissions:W({calls:sG,signatureVerification:J(lG),spend:J(uG)})}),hG=pG;function gG(e,t){let{chainId:n,expiry:r,permissions:i,id:a,publicKey:o,type:s}=e,{address:c}=t;return{address:c,chainId:n,expiry:r,id:a,key:{publicKey:o,type:s},permissions:i??{}}}function _G(e){let{chainId:t,expiry:n,key:r}=e;return dB({chainId:t,expiry:n,permissions:e.permissions??{},publicKey:r.publicKey,role:`session`,type:r.type})}var vG;(function(e){e.GetCapabilitiesResponse=W({status:G([q(`supported`),q(`unsupported`)])})})(vG||={});var yG;(function(e){e.Request=G([QH(),W({chainId:J(Q()),label:J(V())})])})(yG||={});var bG;(function(e){e.Request=HU([W({chainId:J(H()),domain:J(V()),expirationTime:J(uU()),issuedAt:J(uU()),nonce:V(),notBefore:J(uU()),requestId:J(V()),resources:J(Y(U(V()))),scheme:J(V()),statement:J(V()),uri:J(V()),version:J(q(`1`))}),W({authUrl:G([V(),W({logout:V(),nonce:V(),verify:V()})]),chainId:J(Q()),domain:J(V()),expirationTime:J(uU()),issuedAt:J(uU()),notBefore:J(uU()),requestId:J(V()),resources:J(Y(U(V()))),scheme:J(V()),statement:J(V()),uri:J(V()),version:J(q(`1`))})]),e.Response=W({message:V(),signature:Z(),token:J(V())})})(bG||={});var xG;(function(e){e.GetCapabilitiesResponse=W({supported:QH(),tokens:Y(U(lW))}),e.Request=G([uW,X()])})(xG||={});var SG;(function(e){e.Request=mG})(SG||={});var CG;(function(e){e.GetCapabilitiesResponse=W({supported:QH()})})(CG||={});var wG;(function(e){e.GetCapabilitiesResponse=W({supported:QH()}),e.Request=W({id:J(G([Z(),iU()]))}),e.Response=Y(U(pG))})(wG||={});var TG;(function(e){e.Request=Y(U(W({context:cU(),signature:Z()}))),e.Response=e.Request})(TG||={});var EG;(function(e){e.Request=V()})(EG||={});var DG;(function(e){e.GetCapabilitiesResponse=W({supported:QH(),tokens:Y(U(lW))}),e.Request=Y(U(HU([W({address:X(),value:VU()}),W({symbol:uW,value:G([MU([H(),`.`,H()]),MU([H()])]).check(qH(/^\d+(\.\d+)?$/))})])))})(DG||={});var OG=W({...pU(oG,{id:!0,publicKey:!0,type:!0}).shape,credentialId:J(V()),privateKey:J(oU())}),kG;(function(e){e.Parameters=W({address:X(),secret:V()}),e.Request=W({method:q(`account_getOnrampContactInfo`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(V()),phone:J(V()),phoneVerifiedAt:J(H())})})(kG||={});var AG;(function(e){e.Parameters=W({address:X()}),e.Request=W({method:q(`account_onrampStatus`),params:Y(K([e.Parameters]))}),e.Response=W({email:J(H()),phone:J(H())})})(AG||={});var jG;(function(e){e.Parameters=W({email:V(),walletAddress:X()}),e.Request=W({method:q(`account_resendVerifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(jG||={});var MG;(function(e){e.Parameters=W({email:V(),walletAddress:X()}),e.Request=W({method:q(`account_setEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(MG||={});var NG;(function(e){e.Parameters=W({email:V(),walletAddress:X()}),e.Request=W({method:q(`account_setPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(NG||={});var PG;(function(e){e.Parameters=W({chainId:Q(),email:V(),token:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyEmail`),params:Y(K([e.Parameters]))}),e.Response=iU()})(PG||={});var FG;(function(e){e.Parameters=W({code:V(),phone:V(),walletAddress:X()}),e.Request=W({method:q(`account_verifyPhone`),params:Y(K([e.Parameters]))}),e.Response=iU()})(FG||={});var IG;(function(e){e.Parameters=W({address:J(X()),chainId:J(Q()),token:J(X()),value:J(V())}),e.Request=W({method:q(`wallet_addFunds`),params:Y(K([e.Parameters]))}),e.Response=W({id:Z()})})(IG||={});var LG;(function(e){e.Request=W({method:q(`eth_accounts`),params:J(cU())}),e.Response=Y(U(X()))})(LG||={});var RG;(function(e){e.Request=W({method:q(`eth_chainId`),params:J(cU())}),e.Response=Z()})(RG||={});var zG;(function(e){e.Request=W({method:q(`eth_requestAccounts`),params:J(cU())}),e.Response=Y(U(X()))})(zG||={});var BG;(function(e){e.Request=W({method:q(`eth_sendTransaction`),params:Y(K([W({capabilities:J(W({feeToken:J(xG.Request),merchantUrl:J(EG.Request),preCalls:J(TG.Request)})),chainId:J(Q()),data:J(Z()),from:J(X()),to:X(),value:J(VU())})]))}),e.Response=Z()})(BG||={});var VG;(function(e){e.Request=W({method:q(`eth_signTypedData_v4`),params:Y(K([X(),V()]))}),e.Response=Z()})(VG||={});var HG;(function(e){e.Parameters=W({address:J(X()),chainId:J(Q())}),e.Request=W({method:q(`wallet_getAdmins`),params:J(Y(K([e.Parameters])))}),e.Key=OG,e.Response=W({address:X(),chainId:Q(),keys:Y(U(e.Key))})})(HG||={});var UG;(function(e){e.Capabilities=W({feeToken:J(xG.Request)}),e.Parameters=W({address:J(X()),capabilities:J(e.Capabilities),chainId:J(Q()),key:pU(oG,{publicKey:!0,type:!0})}),e.Request=W({method:q(`wallet_grantAdmin`),params:Y(K([e.Parameters]))}),e.Response=W({address:X(),chainId:Q(),key:HG.Key})})(UG||={});var WG;(function(e){e.Parameters=mG,e.Request=W({method:q(`wallet_grantPermissions`),params:Y(K([e.Parameters]))}),e.ResponseCapabilities=W({preCalls:J(TG.Response)}),e.Response=W({...pG.shape,capabilities:J(oU())})})(WG||={});var GG;(function(e){e.Parameters=W({address:J(X())}),e.Request=W({method:q(`wallet_getAccountVersion`),params:J(Y(K([e.Parameters])))}),e.Response=W({current:V(),latest:V()})})(GG||={});var KG;(function(e){e.Parameters=W({address:J(X()),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getPermissions`),params:J(Y(K([e.Parameters])))}),e.Response=wG.Response})(KG||={});var qG;(function(e){e.Capabilities=W({feeToken:J(xG.Request)}),e.Parameters=W({address:J(X()),capabilities:J(e.Capabilities),chainId:J(Q()),id:Z()}),e.Request=W({method:q(`wallet_revokeAdmin`),params:Y(K([e.Parameters]))}),e.Response=void 0})(qG||={});var JG;(function(e){e.Capabilities=W({feeToken:J(xG.Request)}),e.Parameters=W({address:J(X()),capabilities:J(e.Capabilities),id:Z()}),e.Request=W({method:q(`wallet_revokePermissions`),params:Y(K([e.Parameters]))}),e.Response=void 0})(JG||={});var YG;(function(e){e.Request=W({method:q(`wallet_switchEthereumChain`),params:Y(K([W({chainId:Z()})]))})})(YG||={});var XG;(function(e){e.Parameters=W({context:cU(),signatures:W({auth:Z(),exec:Z()})}),e.Request=W({method:q(`wallet_upgradeAccount`),params:Y(K([e.Parameters]))}),e.ResponseCapabilities=W({admins:J(Y(U(HG.Key))),permissions:J(wG.Response)}),e.Response=W({address:X(),capabilities:J(e.ResponseCapabilities)})})(XG||={});var ZG;(function(e){e.Request=W({method:q(`personal_sign`),params:Y(K([Z(),X()]))}),e.Response=Z()})(ZG||={});var QG;(function(e){e.Request=W({method:q(`porto_ping`),params:J(nU())}),e.Response=q(`pong`)})(QG||={});var $G;(function(e){e.Capabilities=W({createAccount:J(yG.Request),email:J(QH()),grantAdmins:J(Y(U(pU(oG,{publicKey:!0,type:!0})))),grantPermissions:J(SG.Request),preCalls:J(TG.Request),selectAccount:J(G([QH(),W({address:X(),key:J(W({credentialId:J(V()),publicKey:Z()}))})])),signInWithEthereum:J(bG.Request)}),e.Parameters=W({capabilities:J(e.Capabilities),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_connect`),params:J(Y(K([e.Parameters])))}),e.ResponseCapabilities=W({admins:J(Y(U(W({...pU(oG,{id:!0,publicKey:!0,type:!0}).shape,credentialId:J(V())})))),permissions:J(wG.Response),preCalls:J(TG.Response),signInWithEthereum:J(bG.Response)}),e.Response=W({accounts:Y(U(W({address:X(),capabilities:J(e.ResponseCapabilities)}))),chainIds:Y(U(Q()))})})($G||={});var eK;(function(e){e.Request=W({method:q(`wallet_disconnect`),params:J(cU())}),e.Response=void 0})(eK||={});var tK;(function(e){e.Parameters=EW.Parameters,e.Request=EW.Request,e.Response=EW.Response})(tK||={});var nK;(function(e){e.Request=W({method:q(`wallet_getCallsStatus`),params:K([Z()])}),e.Response=W({atomic:QH(),chainId:Q(),id:V(),receipts:J(Y(U(W({blockHash:Z(),blockNumber:Z(),gasUsed:Z(),logs:Y(U(W({address:X(),data:Z(),topics:Y(U(Z()))}))),status:Z(),transactionHash:Z()})))),status:H(),version:V()})})(nK||={});var rK;(function(e){e.Parameters=OW.Parameters,e.Request=OW.Request,e.Transaction=OW.Transaction,e.Capabilities=OW.Capabilities,e.Entry=OW.Entry,e.Response=OW.Response})(rK||={});var iK;(function(e){e.Request=W({method:q(`wallet_getCapabilities`),params:J(G([Y(K([G([Z(),nU()])])),Y(K([G([Z(),nU()]),Y(U(Q()))]))]))}),e.Response=xU(Z(),W({atomic:vG.GetCapabilitiesResponse,feeToken:xG.GetCapabilitiesResponse,merchant:CG.GetCapabilitiesResponse,permissions:wG.GetCapabilitiesResponse,requiredFunds:DG.GetCapabilitiesResponse}))})(iK||={});var aK;(function(e){e.Parameters=W({address:X(),chainIds:J(Y(U(Q())))}),e.Request=W({method:q(`wallet_getKeys`),params:Y(K([e.Parameters]))}),e.Response=Y(U(fG))})(aK||={});var oK;(function(e){e.Capabilities=W({feeToken:J(xG.Request),merchantUrl:J(EG.Request),permissions:J(wG.Request),preCalls:J(TG.Request),requiredFunds:J(DG.Request)}),e.Parameters=W({calls:Y(U(W({data:J(Z()),to:X(),value:J(VU())}))),capabilities:J(e.Capabilities),chainId:J(Q()),from:J(X()),key:J(pU(oG,{prehash:!0,publicKey:!0,type:!0})),version:J(V())}),e.Request=W({method:q(`wallet_prepareCalls`),params:Y(K([e.Parameters]))}),e.Response=W({capabilities:J(W({...AW.ResponseCapabilities.shape,quote:J(cW)})),chainId:Z(),context:W({account:W({address:X()}),calls:e.Parameters.shape.calls,nonce:VU(),quote:J(hU(cW))}),digest:Z(),key:pU(oG,{prehash:!0,publicKey:!0,type:!0}),typedData:W({domain:G([W({chainId:Q(),name:V(),verifyingContract:X(),version:V()}),W({})]),message:xU(V(),cU()),primaryType:V(),types:xU(V(),cU())})})})(oK||={});var sK;(function(e){e.Capabilities=W({...$G.Capabilities.shape,label:J(V())}),e.Parameters=W({address:X(),capabilities:J(e.Capabilities),chainId:J(Q())}),e.Request=W({method:q(`wallet_prepareUpgradeAccount`),params:Y(K([e.Parameters]))}),e.Response=W({context:cU(),digests:W({auth:Z(),exec:Z()})})})(sK||={});var cK;(function(e){e.Capabilities=oK.Capabilities,e.Request=W({method:q(`wallet_sendCalls`),params:Y(K([mU(oK.Parameters,{key:!0})]))}),e.Response=W({id:Z()})})(cK||={});var lK;(function(e){e.Parameters=W({capabilities:oK.Response.shape.capabilities,chainId:Z(),context:oK.Response.shape.context,key:oK.Response.shape.key,signature:Z()}),e.Request=W({method:q(`wallet_sendPreparedCalls`),params:Y(K([e.Parameters]))}),e.Response=Y(U(W({capabilities:J(xU(V(),cU())),id:Z()})))})(lK||={});var uK;(function(e){e.Parameters=W({address:X(),chainId:J(Q()),digest:Z(),signature:Z()}),e.Request=W({method:q(`wallet_verifySignature`),params:Y(K([e.Parameters]))}),e.Response=W({address:X(),chainId:Q(),proof:J(cU()),valid:QH()})})(uK||={});var dK=vU(`method`,[PG.Request,IG.Request,LG.Request,RG.Request,zG.Request,BG.Request,VG.Request,GG.Request,HG.Request,KG.Request,UG.Request,WG.Request,sK.Request,qG.Request,JG.Request,XG.Request,ZG.Request,QG.Request,$G.Request,eK.Request,tK.Request,nK.Request,rK.Request,iK.Request,aK.Request,oK.Request,cK.Request,lK.Request,YG.Request,uK.Request]);function fK(e,t){let n=IV(e,t);if(n.error){let e=n.error.issues.at(0);throw e?.code===`invalid_union`&&e.note===`No matching discriminator`?new lI:new mI(WU(n.error))}return{...t,_decoded:n.data}}async function pK(e){e.persist.hasHydrated()||await new Promise(t=>{e.persist.onFinishHydration(()=>t(!0)),setTimeout(()=>t(!0),100)})}function mK(e){if(e)return e.startsWith(`/`)?`${window.location.origin}${e}`:e}function hK(e){let{config:t,getMode:n,id:r,store:i}=e,{announceProvider:a}=t;function o(e={}){let a=s(),o=e.request??fK(dK,{method:`wallet_getCapabilities`,params:e.chainIds?[void 0,e.chainIds]:void 0});return Mm(()=>n().actions.getCapabilities({chainIds:e.chainIds,internal:{client:a,config:t,request:o,store:i}}),{cacheKey:`getCapabilities.${r}.${e.chainIds?.join(`,`)}`})}function s(t){let n=typeof t==`string`?sL(t):t;return aG({_internal:e},{chainId:n})}let c=new Map,l=[],u=jI(),d=MI({...u,async request(e){return await pK(i),BB(async()=>{let r;try{r=fK(dK,e)}catch(t){let n=t;if(!(n instanceof lI))throw n;if(e.method.startsWith(`wallet_`))throw new bI;return s().request(e)}let a=i.getState();switch(r.method){case`account_verifyEmail`:{if(a.accounts.length===0)throw new xI;let[e]=r._decoded.params,{chainId:o,email:c,token:l,walletAddress:u}=e,d=s(o);if(o&&o!==d.chain.id)throw new SI;let f=u?a.accounts.find(e=>YL(e.address,u)):a.accounts[0];if(!f)throw new yI;return await n().actions.verifyEmail({account:f,chainId:o,email:c,internal:{client:d,config:t,request:r,store:i},token:l,walletAddress:u})}case`wallet_addFunds`:{let{address:e,value:o,token:c}=r.params[0]??{},l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0],d=s(),f=await n().actions.addFunds({address:l?.address,internal:{client:d,config:t,request:r,store:i},token:c,value:o});return u.emit(`message`,{data:null,type:`assetsChanged`}),f}case`eth_accounts`:if(a.accounts.length===0)throw new xI;return a.accounts.map(yK);case`eth_chainId`:return $I(a.chainIds[0]);case`eth_requestAccounts`:{if(a.accounts.length>0&&c.get(`eth_requestAccounts`))return a.accounts.map(yK);let e=s(),{accounts:o}=await n().actions.loadAccounts({internal:{client:e,config:t,request:r,store:i}});return i.setState(e=>({...e,accounts:o})),u.emit(`connect`,{chainId:$I(e.chain.id)}),c.set(`eth_requestAccounts`,!0),setTimeout(()=>c.delete(`eth_requestAccounts`),1e3),o.map(yK)}case`eth_sendTransaction`:{let[{capabilities:e,chainId:o,data:c=`0x`,from:l,to:u,value:d}]=r._decoded.params,f=s(o);if(o&&o!==f.chain.id)throw new SI;let p=l?a.accounts.find(e=>YL(e.address,l)):a.accounts[0];if(l&&!p)throw new yI;let{id:m}=await n().actions.sendCalls({account:p,asTxHash:!0,calls:[{data:c,to:u,value:d}],chainId:f.chain.id,internal:{client:f,config:t,request:r,store:i},merchantUrl:mK(t.merchantUrl??e?.merchantUrl)});return m}case`eth_signTypedData_v4`:{if(a.accounts.length===0)throw new xI;let[e,o]=r._decoded.params,c=a.accounts.find(t=>YL(t.address,e));if(!c)throw new yI;let l=s();return await n().actions.signTypedData({account:c,data:o,internal:{client:l,config:t,request:r,store:i}})}case`wallet_grantAdmin`:{if(a.accounts.length===0)throw new xI;let[{address:e,capabilities:o,chainId:c,key:l}]=r._decoded.params??[{}],d=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!d)throw new yI;let f=s(c);if(_K([...d.keys??[]])?.some(e=>e.publicKey?.toLowerCase()===l.publicKey.toLowerCase()))throw new mI({message:`Key already granted as admin.`});let{key:p}=await n().actions.grantAdmin({account:d,feeToken:o?.feeToken,internal:{client:f,config:t,request:r,store:i},key:l});i.setState(e=>{let t=e.accounts.findIndex(e=>d?YL(e.address,d.address):!0);return t===-1?e:{...e,accounts:e.accounts.map((e,n)=>n===t?{...e,keys:[...e.keys??[],p]}:e)}});let m=_K([...d.keys??[],p]);return u.emit(`message`,{data:null,type:`adminsChanged`}),RV(UG.Response,{address:d.address,chainId:f.chain.id,key:m.at(-1)})}case`wallet_grantPermissions`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainId:o,...c}]=r._decoded.params??[{}],l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!l)throw new yI;let d=s(o),{key:f}=await n().actions.grantPermissions({account:l,internal:{client:d,config:t,request:r,store:i},permissions:c});return i.setState(e=>{let t=e.accounts.findIndex(e=>l?YL(e.address,l.address):!0);return t===-1?e:{...e,accounts:e.accounts.map((e,n)=>n===t?{...e,keys:[...e.keys??[],f]}:e)}}),u.emit(`message`,{data:null,type:`permissionsChanged`}),RV(WG.Response,{...gG(f,{address:l.address})})}case`wallet_getAdmins`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainId:o}]=r._decoded.params??[{}],c=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!c)throw new yI;let l=s(o),u=_K(await n().actions.getKeys({account:c,internal:{client:l,config:t,request:r,store:i}}));return RV(HG.Response,{address:c.address,chainId:l.chain.id,keys:u})}case`wallet_prepareUpgradeAccount`:{let[{address:e,capabilities:a,chainId:o}]=r._decoded.params??[{}],{email:c,label:u,grantPermissions:d}=a??{},f=s(o),{context:p,digests:m}=await n().actions.prepareUpgradeAccount({address:e,email:c,internal:{client:f,config:t,request:r,store:i},label:u,permissions:d});return l.push(p.account),{context:p,digests:m}}case`wallet_getAccountVersion`:{if(a.accounts.length===0)throw new xI;let[{address:e}]=r._decoded.params??[{}],o=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!o)throw new yI;let c=s(),{current:l,latest:u}=await n().actions.getAccountVersion({address:o.address,internal:{client:c,config:t,request:r,store:i}});return{current:l,latest:u}}case`wallet_getKeys`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainIds:o}]=r._decoded.params??[{}],c=a.accounts.find(t=>YL(t.address,e));if(!c)throw new yI;let l=s(),u=await n().actions.getKeys({account:c,chainIds:o,internal:{client:l,config:t,request:r,store:i}});return RV(aK.Response,u)}case`wallet_getPermissions`:{if(a.accounts.length===0)throw new xI;let[{address:e,chainIds:o}]=r._decoded.params??[{}],c=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!c)throw new yI;let l=s();return vK(await n().actions.getKeys({account:c,chainIds:o,internal:{client:l,config:t,request:r,store:i}}),{address:c.address})}case`wallet_revokeAdmin`:{if(a.accounts.length===0)throw new xI;let[{address:e,capabilities:o,id:c}]=r._decoded.params,l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!l)throw new yI;let d=s();await n().actions.revokeAdmin({account:l,feeToken:o?.feeToken,id:c,internal:{client:d,config:t,request:r,store:i}});let f=l.keys?.filter(e=>e.id.toLowerCase()!==c.toLowerCase());i.setState(e=>({...e,accounts:e.accounts.map(e=>YL(e.address,l.address)?{...e,keys:f}:e)})),u.emit(`message`,{data:null,type:`adminsChanged`});return}case`wallet_revokePermissions`:{if(a.accounts.length===0)throw new xI;let[{address:e,capabilities:o,id:c}]=r._decoded.params,l=e?a.accounts.find(t=>YL(t.address,e)):a.accounts[0];if(!l)throw new yI;let d=s();await n().actions.revokePermissions({account:l,feeToken:o?.feeToken,id:c,internal:{client:d,config:t,request:r,store:i}});let f=l.keys?.filter(e=>e.id.toLowerCase()!==c.toLowerCase());i.setState(e=>({...e,accounts:e.accounts.map(e=>YL(e.address,l.address)?{...e,keys:f}:e)})),u.emit(`message`,{data:null,type:`permissionsChanged`});return}case`wallet_upgradeAccount`:{let[{context:e,signatures:a}]=r._decoded.params??[{}],o=s(),c=l.find(t=>YL(t.address,e.account.address));if(!c)throw new yI;let{account:d}=await n().actions.upgradeAccount({account:c,context:e,internal:{client:o,config:t,request:r,store:i},signatures:a}),f=_K(d.keys??[]),p=vK(d.keys??[],{address:d.address});return i.setState(e=>({...e,accounts:[d]})),u.emit(`connect`,{chainId:$I(o.chain.id)}),{address:d.address,capabilities:{admins:f,...p.length>0?{permissions:p}:{}}}}case`porto_ping`:return`pong`;case`personal_sign`:{if(a.accounts.length===0)throw new xI;let[e,o]=r._decoded.params,c=a.accounts.find(e=>YL(e.address,o));if(!c)throw new yI;let l=s();return await n().actions.signPersonalMessage({account:c,data:e,internal:{client:l,config:t,request:r,store:i}})}case`wallet_connect`:{let[{capabilities:e,chainIds:o}]=r._decoded.params??[{}],c=s(o?.[0]),l=c.chain.id,{createAccount:d,email:f,grantAdmins:p,grantPermissions:m,selectAccount:h,signInWithEthereum:g}=e??{},_={client:c,config:t,request:r,store:i},{accounts:v}=await(async()=>{if(f||d){let{label:e=void 0}=typeof d==`object`?d:{},{account:t}=await n().actions.createAccount({admins:p,email:f,internal:_,label:e,permissions:m,signInWithEthereum:g});return{accounts:[t]}}let e=a.accounts[0],{address:t,key:r}=(()=>{if(h)return typeof h==`object`?h:{address:void 0,key:void 0};for(let t of e?.keys??[])if(t.type===`webauthn-p256`&&t.role===`admin`)return{address:e?.address,key:{credentialId:t.credentialId??t.privateKey?.credential?.id,publicKey:t.publicKey}};return{address:void 0,key:void 0}})(),i={internal:_,permissions:m,signInWithEthereum:g};try{return await n().actions.loadAccounts({address:t,key:r,...i})}catch(e){if(e instanceof vI)throw e;if(t&&r)return await n().actions.loadAccounts(i);throw e}})();i.setState(e=>({...e,accounts:v}));let y=[l,...i.getState().chainIds.filter(e=>e!==l)];return u.emit(`connect`,{chainId:$I(y[0])}),{accounts:v.map(e=>({address:yK(e),capabilities:{admins:e.keys?_K(e.keys):[],permissions:e.keys?vK(e.keys,{address:e.address}):[],...e.signInWithEthereum&&{signInWithEthereum:e.signInWithEthereum}}})),chainIds:y.map(e=>$I(e))}}case`wallet_disconnect`:{let e=s();await n().actions.disconnect?.({internal:{client:e,config:t,request:r,store:i}}),i.setState(e=>({...e,accounts:[]})),u.emit(`disconnect`,new xI);return}case`wallet_getAssets`:{let[e]=r._decoded.params??[],{account:a,chainFilter:o,assetFilter:c,assetTypeFilter:l}=e,u=s(),d=await n().actions.getAssets({account:a,assetFilter:c,assetTypeFilter:l,chainFilter:o,internal:{client:u,config:t,request:r,store:i}}),f=Object.entries(d).reduce((e,[t,n])=>(e[$I(Number(t))]=n,e),{});return RV(tK.Response,f)}case`wallet_getCallsStatus`:{let[e]=r._decoded.params??[],a=s();return await n().actions.getCallsStatus({id:e,internal:{client:a,config:t,request:r,store:i}})}case`wallet_getCallsHistory`:{let[e]=r._decoded.params??[],a=s(),o=await n().actions.getCallsHistory({...e,internal:{client:a,config:t,request:r,store:i}});return RV(rK.Response,o)}case`wallet_getCapabilities`:{let[e,t]=r.params??[];return await o({chainIds:t,request:r})}case`wallet_prepareCalls`:{let[e]=r._decoded.params,{calls:o,capabilities:c,chainId:l,key:u,from:d}=e,f=s(l),p=d??a.accounts[0];if(!p)throw new yI;if(l&&l!==f.chain.id)throw new SI;let{digest:m,...h}=await n().actions.prepareCalls({account:TB(p),calls:o,feeToken:c?.feeToken,internal:{client:f,config:t,request:r,store:i},key:u,merchantUrl:mK(t.merchantUrl??c?.merchantUrl),requiredFunds:c?.requiredFunds});return RV(oK.Response,{capabilities:h.capabilities,chainId:$I(h.chainId??f.chain.id),context:{...h.context,account:{address:h.account.address},calls:h.context.calls??[],nonce:h.context.nonce??0n},digest:m,key:h.key,typedData:h.typedData})}case`wallet_sendPreparedCalls`:{let[e]=r._decoded.params,{chainId:a,context:o,key:c,signature:l}=e,{account:u}=e.context,d=s(a);if(a&&sL(a)!==d.chain.id)throw new SI;return[{id:await n().actions.sendPreparedCalls({account:TB(u),context:o,internal:{client:d,config:t,request:r,store:i},key:c,signature:l})}]}case`wallet_sendCalls`:{let[e]=r._decoded.params,{calls:o,capabilities:c,chainId:l,from:u}=e,d=s(l);if(l&&l!==d.chain.id)throw new SI;let f=u?a.accounts.find(e=>YL(e.address,u)):a.accounts[0];if(u&&!f)throw new yI;let{id:p}=await n().actions.sendCalls({account:f,calls:o,chainId:d.chain.id,feeToken:c?.feeToken,internal:{client:d,config:t,request:r,store:i},merchantUrl:mK(t.merchantUrl??c?.merchantUrl),permissionsId:c?.permissions?.id,requiredFunds:c?.requiredFunds});return{id:p}}case`wallet_switchEthereumChain`:{let[e]=r._decoded.params,{chainId:a}=e,o=sL(a);if(!t.chains.find(e=>e.id===o))throw new TI;let c=s(a);await n().actions.switchChain?.({chainId:c.chain.id,internal:{client:c,config:t,request:r,store:i}}),i.setState(e=>({...e,chainIds:[o,...e.chainIds.filter(e=>e!==o)]}));return}case`wallet_verifySignature`:{let[e]=r._decoded.params,{address:t,chainId:n,digest:i,signature:a}=e,o=s(n);return{...await XW(o,{address:t,digest:i,signature:a}),address:t,chainId:$I(o.chain.id)}}}},{enabled:[`eth_accounts`,`eth_chainId`,`eth_requestAccounts`,`wallet_getAssets`,`wallet_getCapabilities`,`wallet_getKeys`,`wallet_getPermissions`,`wallet_getAccountVersion`,`wallet_connect`].includes(e.method),id:ZF(e)})}});function f(){let e=()=>{},t=()=>{};pK(i).then(()=>{o().catch(()=>{}),e(),e=i.subscribe(e=>e.accounts,e=>{u.emit(`accountsChanged`,e.map(yK))},{equalityFn:(e,t)=>e.every((e,n)=>e.address===t[n]?.address)}),t(),t=i.subscribe(e=>e.chainIds[0],(e,t)=>{e!==t&&u.emit(`chainChanged`,$I(e))})});let n=gK(d,a);return()=>{e(),t(),n()}}let p=f();return Object.assign(d,{_internal:{destroy:p}})}function gK(e,t){if(!t||typeof window>`u`||!window.dispatchEvent)return()=>{};let{icon:n=`data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIyIiBoZWlnaHQ9IjQyMiIgdmlld0JveD0iMCAwIDQyMiA0MjIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI0MjIiIGhlaWdodD0iNDIyIiBmaWxsPSJibGFjayIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMV8xNSkiPgo8cGF0aCBkPSJNODEgMjg2LjM2NkM4MSAyODAuODkzIDg1LjQ1MDUgMjc2LjQ1NSA5MC45NDA0IDI3Ni40NTVIMzI5LjUxMUMzMzUuMDAxIDI3Ni40NTUgMzM5LjQ1MiAyODAuODkzIDMzOS40NTIgMjg2LjM2NlYzMDYuMTg4QzMzOS40NTIgMzExLjY2MiAzMzUuMDAxIDMxNi4wOTkgMzI5LjUxMSAzMTYuMDk5SDkwLjk0MDRDODUuNDUwNSAzMTYuMDk5IDgxIDMxMS42NjIgODEgMzA2LjE4OFYyODYuMzY2WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC41Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTAuOTQwNCAyMzQuODI4Qzg1LjQ1MDUgMjM0LjgyOCA4MSAyMzkuMjY2IDgxIDI0NC43MzlWMjcxLjUzMUM4My44NDMyIDI2OS42MzMgODcuMjYyMiAyNjguNTI2IDkwLjk0MDQgMjY4LjUyNkgzMjkuNTExQzMzMy4xODggMjY4LjUyNiAzMzYuNjA4IDI2OS42MzMgMzM5LjQ1MiAyNzEuNTMxVjI0NC43MzlDMzM5LjQ1MiAyMzkuMjY2IDMzNS4wMDEgMjM0LjgyOCAzMjkuNTExIDIzNC44MjhIOTAuOTQwNFpNMzM5LjQ1MiAyODYuMzY2QzMzOS40NTIgMjgwLjg5MyAzMzUuMDAxIDI3Ni40NTUgMzI5LjUxMSAyNzYuNDU1SDkwLjk0MDRDODUuNDUwNSAyNzYuNDU1IDgxIDI4MC44OTMgODEgMjg2LjM2NlYzMDYuMTlDODEgMzExLjY2NCA4NS40NTA1IDMxNi4xMDEgOTAuOTQwNCAzMTYuMTAxSDMyOS41MTFDMzM1LjAwMSAzMTYuMTAxIDMzOS40NTIgMzExLjY2NCAzMzkuNDUyIDMwNi4xOVYyODYuMzY2WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC41Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTAuOTQwNCAxOTMuMjAxQzg1LjQ1MDUgMTkzLjIwMSA4MSAxOTcuNjM4IDgxIDIwMy4xMTJWMjI5LjkwM0M4My44NDMyIDIyOC4wMDYgODcuMjYyMiAyMjYuODk5IDkwLjk0MDQgMjI2Ljg5OUgzMjkuNTExQzMzMy4xODggMjI2Ljg5OSAzMzYuNjA4IDIyOC4wMDYgMzM5LjQ1MiAyMjkuOTAzVjIwMy4xMTJDMzM5LjQ1MiAxOTcuNjM4IDMzNS4wMDEgMTkzLjIwMSAzMjkuNTExIDE5My4yMDFIOTAuOTQwNFpNMzM5LjQ1MiAyNDQuNzM5QzMzOS40NTIgMjM5LjI2NSAzMzUuMDAxIDIzNC44MjggMzI5LjUxMSAyMzQuODI4SDkwLjk0MDRDODUuNDUwNSAyMzQuODI4IDgxIDIzOS4yNjUgODEgMjQ0LjczOVYyNzEuNTNDODEuMjE3NSAyNzEuMzg1IDgxLjQzODMgMjcxLjI0NSA4MS42NjI0IDI3MS4xMDlDODMuODMyNSAyNjkuNzk0IDg2LjMwNTQgMjY4LjkyNyA4OC45NTIzIDI2OC42MzVDODkuNjA1MSAyNjguNTYzIDkwLjI2ODQgMjY4LjUyNiA5MC45NDA0IDI2OC41MjZIMzI5LjUxMUMzMzAuMTgzIDI2OC41MjYgMzMwLjg0NiAyNjguNTYzIDMzMS40OTggMjY4LjYzNUMzMzQuNDE5IDI2OC45NTcgMzM3LjEyOCAyNjkuOTggMzM5LjQ1MiAyNzEuNTNWMjQ0LjczOVpNMzM5LjQ1MiAyODYuMzY2QzMzOS40NTIgMjgxLjAyMSAzMzUuMjA2IDI3Ni42NjMgMzI5Ljg5MyAyNzYuNDYyQzMyOS43NjcgMjc2LjQ1NyAzMjkuNjQgMjc2LjQ1NSAzMjkuNTExIDI3Ni40NTVIOTAuOTQwNEM4NS40NTA1IDI3Ni40NTUgODEgMjgwLjg5MyA4MSAyODYuMzY2VjMwNi4xODhDODEgMzExLjY2MiA4NS40NTA1IDMxNi4xMDEgOTAuOTQwNCAzMTYuMTAxSDMyOS41MTFDMzM1LjAwMSAzMTYuMTAxIDMzOS40NTIgMzExLjY2MiAzMzkuNDUyIDMwNi4xODhWMjg2LjM2NloiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNSIvPgo8cGF0aCBvcGFjaXR5PSIwLjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOTguMDE0NiAxMDRDODguNjE3NyAxMDQgODEgMTExLjU5NSA4MSAxMjAuOTY1VjE4OC4yNzZDODMuODQzMiAxODYuMzc5IDg3LjI2MjIgMTg1LjI3MiA5MC45NDA0IDE4NS4yNzJIMzI5LjUxMUMzMzMuMTg4IDE4NS4yNzIgMzM2LjYwOCAxODYuMzc5IDMzOS40NTIgMTg4LjI3NlYxMjAuOTY1QzMzOS40NTIgMTExLjU5NSAzMzEuODMzIDEwNCAzMjIuNDM3IDEwNEg5OC4wMTQ2Wk0zMzkuNDUyIDIwMy4xMTJDMzM5LjQ1MiAxOTcuNjM4IDMzNS4wMDEgMTkzLjIwMSAzMjkuNTExIDE5My4yMDFIOTAuOTQwNEM4NS40NTA1IDE5My4yMDEgODEgMTk3LjYzOCA4MSAyMDMuMTEyVjIyOS45MDNDODEuMjE3NSAyMjkuNzU4IDgxLjQzODMgMjI5LjYxOCA4MS42NjI0IDIyOS40ODJDODMuODMyNSAyMjguMTY3IDg2LjMwNTQgMjI3LjMgODguOTUyMyAyMjcuMDA4Qzg5LjYwNTEgMjI2LjkzNiA5MC4yNjg0IDIyNi44OTkgOTAuOTQwNCAyMjYuODk5SDMyOS41MTFDMzMwLjE4MyAyMjYuODk5IDMzMC44NDYgMjI2LjkzNiAzMzEuNDk4IDIyNy4wMDhDMzM0LjQxOSAyMjcuMzMgMzM3LjEyOCAyMjguMzUyIDMzOS40NTIgMjI5LjkwM1YyMDMuMTEyWk0zMzkuNDUyIDI0NC43MzlDMzM5LjQ1MiAyMzkuMzkzIDMzNS4yMDYgMjM1LjAzNiAzMjkuODkzIDIzNC44MzVDMzI5Ljc2NyAyMzQuODMgMzI5LjY0IDIzNC44MjggMzI5LjUxMSAyMzQuODI4SDkwLjk0MDRDODUuNDUwNSAyMzQuODI4IDgxIDIzOS4yNjUgODEgMjQ0LjczOVYyNzEuNTNMODEuMDcwNyAyNzEuNDgzQzgxLjI2NTMgMjcxLjM1NSA4MS40NjI1IDI3MS4yMyA4MS42NjI0IDI3MS4xMDlDODEuOTA4MyAyNzAuOTYgODIuMTU4MSAyNzAuODE3IDgyLjQxMTcgMjcwLjY3OUM4NC4zOTUzIDI2OS42MDUgODYuNjA1NCAyNjguODk0IDg4Ljk1MjMgMjY4LjYzNUM4OS4wMDUyIDI2OC42MjkgODkuMDU4IDI2OC42MjQgODkuMTExIDI2OC42MThDODkuNzEyNSAyNjguNTU3IDkwLjMyMjggMjY4LjUyNiA5MC45NDA0IDI2OC41MjZIMzI5LjUxMUMzMjkuNzM4IDI2OC41MjYgMzI5Ljk2NSAyNjguNTMgMzMwLjE5MiAyNjguNTM5QzMzMC42MzEgMjY4LjU1NSAzMzEuMDY3IDI2OC41ODcgMzMxLjQ5OCAyNjguNjM1QzMzNC40MTkgMjY4Ljk1NyAzMzcuMTI4IDI2OS45OCAzMzkuNDUyIDI3MS41M1YyNDQuNzM5Wk0zMzkuNDUyIDI4Ni4zNjZDMzM5LjQ1MiAyODEuMDIxIDMzNS4yMDYgMjc2LjY2MyAzMjkuODkzIDI3Ni40NjJMMzI5Ljg2NSAyNzYuNDYxQzMyOS43NDggMjc2LjQ1NyAzMjkuNjI5IDI3Ni40NTUgMzI5LjUxMSAyNzYuNDU1SDkwLjk0MDRDODUuNDUwNSAyNzYuNDU1IDgxIDI4MC44OTMgODEgMjg2LjM2NlYzMDYuMTg4QzgxIDMxMS42NjIgODUuNDUwNSAzMTYuMTAxIDkwLjk0MDQgMzE2LjEwMUgzMjkuNTExQzMzNS4wMDEgMzE2LjEwMSAzMzkuNDUyIDMxMS42NjIgMzM5LjQ1MiAzMDYuMTg4VjI4Ni4zNjZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjY5Ljg2OCAxMzEuNzUyQzI2OS44NjggMTI2LjI3OCAyNzQuMzE4IDEyMS44NCAyNzkuODA4IDEyMS44NEgzMTEuNjE4QzMxNy4xMDggMTIxLjg0IDMyMS41NTggMTI2LjI3OCAzMjEuNTU4IDEzMS43NTJWMTYxLjQ4NUMzMjEuNTU4IDE2Ni45NTkgMzE3LjEwOCAxNzEuMzk2IDMxMS42MTggMTcxLjM5NkgyNzkuODA4QzI3NC4zMTggMTcxLjM5NiAyNjkuODY4IDE2Ni45NTkgMjY5Ljg2OCAxNjEuNDg1VjEzMS43NTJaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzFfMTUiPgo8cmVjdCB3aWR0aD0iMjU5IiBoZWlnaHQ9IjIxMyIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgxIDEwNCkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K`,name:r=`Porto`,rdns:i=`xyz.ithaca.porto`}=typeof t==`object`?t:{};return nV({info:{icon:n,name:r,rdns:i,uuid:zB()},provider:e})}function _K(e){return e.map(e=>{if(e.role===`admin`)try{return RV(HG.Key,{id:e.id??e.publicKey,publicKey:e.publicKey,type:e.type,...e.type===`webauthn-p256`?{credentialId:e.privateKey?.credential?.id,privateKey:{credential:{id:e.privateKey?.credential?.id},rpId:e.privateKey?.rpId}}:{}})}catch{return}}).filter(Boolean)}function vK(e,{address:t}){return e.map(e=>{if(e.chainId&&e.role===`session`&&!(e.expiry>0&&e.expiry()=>{})}}async function xK(e){let{account:t,calls:n,permissionsId:r}=e;if(r!==void 0){if(r===null)return;let e=t.keys?.find(e=>e.publicKey===r&&e.privateKey);if(!e)throw Error(`permission (id: ${r}) does not exist.`);return e}let i=t.keys?.find(e=>!e.privateKey||e.role!==`session`||e.expirye.permissions?.calls?.some(e=>{if(e.to&&e.to!==t.to)return!1;if(e.signature){if(!t.data)return!1;let n=rL(t.data,0,4);if(cL(e.signature))return e.signature===n;if(yz(e.signature)!==n)return!1}return!0}))),a=t.keys?.find(e=>e.role===`admin`&&e.privateKey);return i??a}function SK(e={}){let t=e.id??0;return{prepare(e){return CK({id:t++,...e})},get id(){return t}}}function CK(e){return{...e,jsonrpc:`2.0`}}function wK(){return null}var TK=mG;function EK(e){let{expiry:t,feeToken:n,permissions:r,publicKey:i,type:a}=e;return{expiry:t,feeToken:n??null,key:{publicKey:i,type:a},permissions:r??{}}}async function DK(e,t={}){if(!e)return;let n={chainId:t.chainId??e.chainId,expiry:e.expiry??0,feeToken:e.feeToken,permissions:xB(e,{feeTokens:t.feeTokens}),role:`session`};if(e?.key)return dB({...n,publicKey:e.key.publicKey,type:e.key.type??`secp256k1`});if(typeof globalThis.crypto?.subtle?.generateKey==`function`)try{return await uB(n)}catch(e){if(!OK(e))throw e}return sB(n)}function OK(e){if(!(e instanceof Error))return!1;let t=e.message?.toLowerCase()??``;return e.name===`TypeError`||e.name===`ReferenceError`||t.includes(`subtle`)||t.includes(`generatekey`)}var kK=/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:[0-9]{1,5})?$/,AK=/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:[0-9]{1,5})?$/,jK=/^localhost(:[0-9]{1,5})?$/,MK=/^[a-zA-Z0-9]{8,}$/,NK=/^([a-zA-Z][a-zA-Z0-9+-.]*)$/,PK=/^(?:(?[a-zA-Z][a-zA-Z0-9+-.]*):\/\/)?(?[a-zA-Z0-9+-.]*(?::[0-9]{1,5})?) (?:wants you to sign in with your Ethereum account:\n)(?
0x[a-fA-F0-9]{40})\n\n(?:(?.*)\n\n)?/,FK=/(?:URI: (?.+))\n(?:Version: (?.+))\n(?:Chain ID: (?\d+))\n(?:Nonce: (?[a-zA-Z0-9]+))\n(?:Issued At: (?.+))(?:\nExpiration Time: (?.+))?(?:\nNot Before: (?.+))?(?:\nRequest ID: (?.+))?/;function IK(e){let{chainId:t,domain:n,expirationTime:r,issuedAt:i=new Date,nonce:a,notBefore:o,requestId:s,resources:c,scheme:l,uri:u,version:d}=e;{if(t!==Math.floor(t))throw new BK({field:`chainId`,metaMessages:[`- Chain ID must be a EIP-155 chain ID.`,`- See https://eips.ethereum.org/EIPS/eip-155`,``,`Provided value: ${t}`]});if(!(kK.test(n)||AK.test(n)||jK.test(n)))throw new BK({field:`domain`,metaMessages:[`- Domain must be an RFC 3986 authority.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${n}`]});if(!MK.test(a))throw new BK({field:`nonce`,metaMessages:[`- Nonce must be at least 8 characters.`,`- Nonce must be alphanumeric.`,``,`Provided value: ${a}`]});if(!LK(u))throw new BK({field:`uri`,metaMessages:[`- URI must be a RFC 3986 URI referring to the resource that is the subject of the signing.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${u}`]});if(d!==`1`)throw new BK({field:`version`,metaMessages:[`- Version must be '1'.`,``,`Provided value: ${d}`]});if(l&&!NK.test(l))throw new BK({field:`scheme`,metaMessages:[`- Scheme must be an RFC 3986 URI scheme.`,`- See https://www.rfc-editor.org/rfc/rfc3986#section-3.1`,``,`Provided value: ${l}`]});let r=e.statement;if(r?.includes(` -`))throw new BK({field:`statement`,metaMessages:[`- Statement must not include '\\n'.`,``,`Provided value: ${r}`]})}let f=qL(e.address,{checksum:!0}),p=`${l?`${l}://${n}`:n} wants you to sign in with your Ethereum account:\n${f}\n\n${e.statement?`${e.statement}\n`:``}`,m=`URI: ${u}\nVersion: ${d}\nChain ID: ${t}\nNonce: ${a}\nIssued At: ${i.toISOString()}`;if(r&&(m+=`\nExpiration Time: ${r.toISOString()}`),o&&(m+=`\nNot Before: ${o.toISOString()}`),s&&(m+=`\nRequest ID: ${s}`),c){let e=` -Resources:`;for(let t of c){if(!LK(t))throw new BK({field:`resources`,metaMessages:[`- Every resource must be a RFC 3986 URI.`,`- See https://www.rfc-editor.org/rfc/rfc3986`,``,`Provided value: ${t}`]});e+=`\n- ${t}`}m+=e}return`${p}\n${m}`}function LK(e){if(/[^a-z0-9:/?#[\]@!$&'()*+,;=.\-_~%]/i.test(e)||/%[^0-9a-f]/i.test(e)||/%[0-9a-f](:?[^0-9a-f]|$)/i.test(e))return!1;let t=RK(e),n=t[1],r=t[2],i=t[3],a=t[4],o=t[5];if(!(n?.length&&i&&i.length>=0))return!1;if(r?.length){if(!(i.length===0||/^\//.test(i)))return!1}else if(/^\/\//.test(i))return!1;if(!/^[a-z][a-z0-9+\-.]*$/.test(n.toLowerCase()))return!1;let s=``;return s+=`${n}:`,r?.length&&(s+=`//${r}`),s+=i,a?.length&&(s+=`?${a}`),o?.length&&(s+=`#${o}`),s}function RK(e){return e.match(/(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/)}function zK(e){let{scheme:t,statement:n,...r}=e.match(PK)?.groups??{},{chainId:i,expirationTime:a,issuedAt:o,notBefore:s,requestId:c,...l}=e.match(FK)?.groups??{},u=e.split(`Resources:`)[1]?.split(` -- `).slice(1);return{...r,...l,...i?{chainId:Number(i)}:{},...a?{expirationTime:new Date(a)}:{},...o?{issuedAt:new Date(o)}:{},...s?{notBefore:new Date(s)}:{},...c?{requestId:c}:{},...u?{resources:u}:{},...t?{scheme:t}:{},...n?{statement:n}:{}}}var BK=class extends z{constructor(e){let{field:t,metaMessages:n}=e;super(`Invalid Sign-In with Ethereum message field "${t}".`,{metaMessages:n}),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`Siwe.InvalidMessageFieldError`})}};async function VK(e){let{address:t,authUrl:n,message:r,signature:i,publicKey:a}=e,{chainId:o}=zK(r);return await fetch(n.verify,{body:JSON.stringify({address:t,chainId:o,message:r,signature:i,walletAddress:t,...a&&{publicKey:a}}),credentials:`include`,headers:{"Content-Type":`application/json`},method:`POST`}).then(e=>e.json())}async function HK(e,t,n){let{chainId:r=e.chain?.id,domain:i,uri:a,resources:o,version:s=`1`}=t,{address:c}=n,l=t.authUrl?UK(t.authUrl):void 0;if(!r)throw Error("`chainId` is required.");if(!i)throw Error("`domain` is required.");if(!t.nonce&&!l?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");if(!a)throw Error("`uri` is required.");let u=await(async()=>{if(t.nonce)return t.nonce;if(!l?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");let e=await(await fetch(l.nonce,{body:JSON.stringify({address:c,chainId:r,walletAddress:c}),headers:{"Content-Type":`application/json`},method:`POST`})).json().catch(()=>void 0);if(!e?.nonce)throw Error("`nonce` or `authUrl.nonce` is required.");return e.nonce})();return IK({...t,address:n.address,chainId:r,domain:i,nonce:u,resources:o,uri:a,version:s})}function UK(e,t=``){if(!e)return;let n=(()=>{if(typeof e==`string`){let t=e.replace(/\/$/,``);return{logout:t+`/logout`,nonce:t+`/nonce`,verify:t+`/verify`}}return e})();return{logout:WK(n.logout,t),nonce:WK(n.nonce,t),verify:WK(n.verify,t)}}function WK(e,t){return!t||!e.startsWith(`/`)?e:t+e}function GK(e){let t=XI(e);return YI(`0x19`,eL(`Ethereum Signed Message: -`+iL(t)),t)}function KK(e){return ML(GK(e))}function qK(e,t){let{as:n}=t,r=YK(e),i=zR(new Uint8Array(r.length));return r.encode(i),n===`Hex`?QI(i.bytes):i.bytes}function JK(e,t={}){let{as:n=`Hex`}=t;return qK(e,{as:n})}function YK(e){return Array.isArray(e)?XK(e.map(e=>YK(e))):ZK(e)}function XK(e){let t=e.reduce((e,t)=>e+t.length,0),n=QK(t);return{length:t<=55?1+t:1+n+t,encode(r){t<=55?r.pushByte(192+t):(r.pushByte(247+n),n===1?r.pushUint8(t):n===2?r.pushUint16(t):n===3?r.pushUint24(t):r.pushUint32(t));for(let{encode:t}of e)t(r)}}}function ZK(e){let t=typeof e==`string`?yL(e):e,n=QK(t.length);return{length:t.length===1&&t[0]<128?1:t.length<=55?1+t.length:1+n+t.length,encode(e){t.length===1&&t[0]<128?e.pushBytes(t):t.length<=55?(e.pushByte(128+t.length),e.pushBytes(t)):(e.pushByte(183+n),n===1?e.pushUint8(t.length):n===2?e.pushUint16(t.length):n===3?e.pushUint24(t.length):e.pushUint32(t.length),e.pushBytes(t))}}}function QK(e){if(e<=255)return 1;if(e<=65535)return 2;if(e<=16777215)return 3;if(e<=4294967295)return 4;throw new z(`Length is too large.`)}function $K(e){return eq(e,{presign:!0})}function eq(e,t={}){let{presign:n}=t;return ML(YI(`0x05`,JK(tq(n?{address:e.address,chainId:e.chainId,nonce:e.nonce}:e))))}function tq(e){let{address:t,chainId:n,nonce:r}=e,i=pR(e);return[n?$I(n):`0x`,t,r?$I(r):`0x`,...i?vR(i):[]]}async function nq(e,t){let{account:n=e.account}=t,r=n?TB(n):void 0;if(!r)throw Error(`account is required.`);let{domain:{name:i,version:a}}=await oh(e,{address:r.address});if(!e.chain)throw Error(`client.chain is required`);return{chainId:e.chain.id,name:i,verifyingContract:r.address,version:a}}async function rq(e,t){let{account:n=e.account,chainIds:r}=t,i=n?TB(n):void 0;if(!i)throw Error(`account is required.`);let a=await VW(e,{address:i.address,chainIds:r});return Object.entries(a).flatMap(([e,t])=>t.map(t=>pB(t,{chainId:Number(e)})))}async function iq(e,t){let{account:n=e.account,calls:r,chain:i=e.chain,feePayer:a,merchantUrl:o,nonce:s,preCalls:c,requiredFunds:l,revokeKeys:u}=t,d=n?TB(n):void 0,f=t.key??(d?DB(d,{role:`admin`}):void 0),p=t.authorizeKeys?.some(e=>e.role===`session`),{contracts:m,fees:{tokens:h}}=await LW(e,{chainId:i?.id}),g=p?m.orchestrator.address:void 0,_=(t.authorizeKeys??[]).map(e=>bB(e,{feeTokens:h,orchestrator:g})),v=t.feeToken?t.feeToken:f?.permissions?.spend?.[0]?.token,y=typeof c==`boolean`?c:!1,b=typeof c==`object`?c.map(({context:e,signature:t})=>({...e.preCall,signature:t})):void 0,x={address:d?.address,calls:r??[],capabilities:{authorizeKeys:_,meta:{feePayer:a,feeToken:v,nonce:s},preCall:y,preCalls:b,requiredFunds:l,revokeKeys:u?.map(e=>({hash:e.hash}))},chain:i,key:f?bB(f,{feeTokens:h}):void 0},S=await(async()=>o?await UW(rh({chain:e.chain,transport:$v(o)}),x).catch(t=>(console.error(t),UW(e,x))):await UW(e,x))(),{capabilities:ee,context:C,digest:te,signature:ne,typedData:re}=S;if(o&&!await YW(e,{response:S._raw,signature:ne}))throw Error(`cannot verify integrity of \`wallet_prepareCalls\` response from ${o}`);return{capabilities:{...ee,quote:C.quote},context:C,digest:te,key:f,typedData:re}}async function aq(e,t){let{address:n,authorizeKeys:r,chain:i=e.chain}=t;if(!i)throw Error(`chain is required.`);let{contracts:a,fees:{tokens:o}}=await LW(e,{chainId:i.id}),s=t.delegation??a.accountProxy.address,c=r.some(e=>e.role===`session`)?a.orchestrator.address:void 0,{capabilities:l,chainId:u,context:d,digests:f,typedData:p}=await WW(e,{address:n,authorizeKeys:r.map(e=>{let t=e.role===`session`?e.permissions:{};return bB({...e,permissions:t},{feeTokens:o,orchestrator:c})}),chain:i,delegation:s}),m=TB({address:n,keys:r});return{capabilities:l,chainId:u,context:{...d,account:m},digests:f,typedData:p}}async function oq(e,t){let{account:n=e.account,chain:r=e.chain,webAuthn:i}=t;if(!r)throw Error("`chain` is required.");let a=n?TB(n):void 0;if(!a)throw Error("`account` is required.");let o=t.key??DB(a,t);if(!o&&!a.sign)throw Error("`key` or `account` with `sign` is required");let s=await Promise.all((t.preCalls??[]).map(async n=>{if(n.signature)return n;let{authorizeKeys:o,key:s,calls:c,revokeKeys:l}=n,{context:u,digest:d}=await iq(e,{account:a,authorizeKeys:o,calls:c,chain:r,feeToken:t.feeToken,key:s,preCalls:!0,revokeKeys:l});return{context:u,signature:await yB(s,{address:null,payload:d,webAuthn:i})}})),{capabilities:c,context:l,digest:u}=await iq(e,{...t,account:a,chain:r,key:o,preCalls:s}),d=await(async()=>o?await yB(o,{address:null,payload:u,webAuthn:i,wrap:!1}):await a.sign({hash:u}))();return await sq(e,{capabilities:c.feeSignature?{feeSignature:c.feeSignature}:void 0,context:l,key:o,signature:d})}async function sq(e,t){let{capabilities:n,context:r,key:i,signature:a}=t;return await GW(e,{capabilities:n,context:r,key:i?bB(i):void 0,signature:a})}async function cq(e,t){let{email:n,walletAddress:r}=t;return await KW(e,{email:n,walletAddress:r})}async function lq(e,t){if(t.account){let{account:n}=t,r=[...n.keys??[],...t.authorizeKeys??[]].filter((e,t,n)=>n.findIndex(t=>t.id===e.id)===t),{digests:i,...a}=await aq(e,{...t,address:n.address,authorizeKeys:r}),o={auth:await n.sign({hash:i.auth}),exec:await n.sign({hash:i.exec})};return await lq(e,{...a,signatures:o})}let{context:n,signatures:r}=t,i=TB(n.account);return await qW(e,{context:n,signatures:r}),i}async function uq(e,t){let{chainId:n,email:r,signature:i,token:a,walletAddress:o}=t;return await JW(e,{chainId:n,email:r,signature:i,token:a,walletAddress:o})}var dq=`0x8010801080108010801080108010801080108010801080108010801080108010`,fq=GR(`(uint256 chainId, address delegation, uint256 nonce, uint8 yParity, uint256 r, uint256 s), address to, bytes data`);function pq(e){if(typeof e==`string`){if(rL(e,-32)!==`0x8010801080108010801080108010801080108010801080108010801080108010`)throw new hq(e)}else uR(e.authorization)}function mq(e){let{data:t,signature:n}=e;pq(e);let r=rR({payload:$K(e.authorization),signature:mR(e.authorization)}),i=UR(fq,[{...e.authorization,delegation:e.authorization.address,chainId:BigInt(e.authorization.chainId)},e.to??r,t??`0x`]);return YI(n,i,$I(iL(i),{size:32}),dq)}var hq=class extends z{constructor(e){super(`Value \`${e}\` is an invalid ERC-8010 wrapped signature.`),Object.defineProperty(this,`name`,{enumerable:!0,configurable:!0,writable:!0,value:`SignatureErc8010.InvalidWrappedSignatureError`})}};async function gq(e,t){let{address:n}=t,{authorization:r,data:i,to:a}=await IW(e,{address:n});return mq({authorization:{...r,nonce:BigInt(r.nonce),r:BigInt(r.r),s:BigInt(r.s)},data:i,signature:t.signature,to:a})}function _q(e,t){let{tokens:n}=t,r=n.filter(e=>e.interop);return e.map(e=>{if(e.address)return e;let t=r.find(t=>t.symbol===e.symbol);if(!t)throw Error(`interop token not found: ${e.symbol}`);return{address:t.address,value:Az(e.value,t.decimals)}})}async function vq(e,t){let{chain:n=e.chain}=t??{};return await LW(e,{chainId:n?.id}).then(e=>e.fees.tokens)}async function yq(e,t){let{addressOrSymbol:n}=t;return(await vq(e,t)).find(yq.predicate(n))}(function(e){function t(e){return t=>e?XL(e)?YL(t.address,e):e===`native`?t.address===dv:e===t.symbol:!1}e.predicate=t})(yq||={});async function bq(e,t){let{chain:n=e.chain,store:r}=t??{},i=r?.getState()??{},a=t?.addressOrSymbol??i.feeToken;return(await vq(e,{chain:n}).then(e=>e.filter(e=>e.feeToken)))?.find(e=>a?a===`native`&&e.address===`0x0000000000000000000000000000000000000000`||XL(a)&&YL(e.address,a)?!0:a===e.symbol:!1)}function xq(e={}){let t=e,{mock:n,multichain:r=!0,webAuthn:i}=t,a,o,s=(()=>{if(t.keystoreHost!==`self`&&!(typeof window<`u`&&window.location?.hostname===`localhost`))return t.keystoreHost})();return bK({actions:{async addFunds(){throw new bI},async createAccount(e){let{admins:t,email:r,eoa:o,label:c,permissions:l,internal:u,signInWithEthereum:d}=e,{client:f}=u,p=o??EB(nR()),m=await vq(f),h=n?lB():await cB({createFn:i?.createFn,label:c||`${p.address.slice(0,8)}\u2026${p.address.slice(-6)}`,rpId:s,userId:_L(p.address)}),g=await DK(l,{chainId:f.chain.id,feeTokens:m}),_=t?.map(e=>dB(e)),v=await lq(f,{account:p,authorizeKeys:[h,..._??[],...g?[g]:[]]});a=p.address,r&&c&&await cq(f,{email:c,walletAddress:v.address});let y=await(async()=>{if(!d)return;let e=await HK(f,d,{address:v.address}),t=await OB(p,{payload:KK(eL(e))});return{message:e,signature:await gq(f,{address:v.address,signature:t})}})();return{account:{...v,signInWithEthereum:y}}},async getAccountVersion(e){let{address:t,internal:n}=e,{client:r}=n,{contracts:i}=await LW(r),{accountImplementation:a}=i,o=await nq(r,{account:TB(a)}).then(e=>e.version),s=await nq(r,{account:t}).then(e=>e.version).catch(()=>o);if(!s||!o)throw Error(`version not found.`);return{current:s,latest:o}},async getAssets(e){let{account:t,chainFilter:n,assetFilter:r,assetTypeFilter:i,internal:a}=e,{client:o}=a;return await RW(o,{account:t,assetFilter:r,assetTypeFilter:i,chainFilter:n})},async getCallsHistory(e){let{internal:t,...n}=e,{client:r}=t;return await BW(r,n)},async getCallsStatus(e){let{id:t,internal:n}=e,{client:r}=n,i=await zW(r,{id:t});return{atomic:!0,chainId:$I(r.chain.id),id:t,receipts:i.receipts?.map(e=>({blockHash:e.blockHash,blockNumber:$I(e.blockNumber),gasUsed:$I(e.gasUsed),logs:e.logs,status:e.status,transactionHash:e.transactionHash})),status:i.status,version:`1.0`}},async getCapabilities(e){let{chainIds:t,internal:n}=e,{client:i}=n,a={atomic:{status:`supported`},atomicBatch:{supported:!0},feeToken:{supported:!0,tokens:[]},merchant:{supported:!0},permissions:{supported:!0},requiredFunds:{supported:!!r,tokens:[]}},o=await LW(i,{chainIds:t?t.map(e=>sL(e)):`all`,raw:!0});return Object.entries(o).reduce((e,[t,n])=>({...e,[t]:{...a,...n,feeToken:{supported:!0,tokens:n.fees.tokens},requiredFunds:{supported:!!r,tokens:r?n.fees.tokens.filter(e=>e.interop):[]}}}),{})},async getKeys(e){let{account:t,chainIds:n,internal:r}=e,{client:i}=r;return RB([...await rq(i,{account:t,chainIds:n}),...t.keys??[]],e=>e.publicKey)},async grantAdmin(e){let{account:t,internal:n}=e,{client:r}=n,a=dB(e.key,{chainId:r.chain.id}),o=await bq(r,{addressOrSymbol:e.feeToken,store:n.store}),{id:s}=await oq(r,{account:t,authorizeKeys:[a],feeToken:o?.address,webAuthn:i});return await Zm(r,{id:s,pollingInterval:500}),{key:a}},async grantPermissions(e){let{account:t,internal:n,permissions:r}=e,{client:i}=n,a=await vq(i),o=await DK(r,{chainId:i.chain.id,feeTokens:a});if(!o)throw Error(`key to authorize not found.`);let s=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!s)throw Error(`admin key not found.`);let{context:c,digest:l}=await iq(i,{account:t,authorizeKeys:[o],key:s,preCalls:!0});return await sq(i,{context:c,key:s,signature:await yB(s,{address:null,payload:l})}),{key:o}},async loadAccounts(e){let{internal:t,permissions:r,signInWithEthereum:o}=e,{client:c}=t,l=await vq(c),u=await DK(r,{chainId:c.chain.id,feeTokens:l}),{digest:d,digestType:f,message:p}=await(async()=>{if(o&&e.address){let t=await HK(c,o,{address:e.address});return{context:void 0,digest:KK(eL(t)),digestType:`siwe`,message:t}}return{context:void 0,digest:`0x`,message:void 0}})(),{address:m,credentialId:h,webAuthnSignature:g}=await(async()=>{if(n){if(!a)throw Error(`address_internal not found.`);return{address:a,credentialId:void 0}}if(e.address&&e.key)return{address:e.address,credentialId:e.key.credentialId};let t=await Jz({challenge:d,getFn:i?.getFn,rpId:s}),r=t.raw.response;return{address:TL(new Uint8Array(r.userHandle)),credentialId:t.raw.id,webAuthnSignature:t}})(),_=TB({address:m,keys:[...await rq(c,{account:m,chainIds:[c.chain.id]}),...u?[u]:[]].map((e,t)=>t===0&&e.type===`webauthn-p256`?mB({...e,credential:{id:h,publicKey:LL(e.publicKey)},id:m,rpId:s}):e)}),v=DB(_,{role:`admin`}),y=await(async()=>{if(d!==`0x`)return g?wB(CB(g),{keyType:`webauthn-p256`,publicKey:v.publicKey}):await yB(v,{address:_.address,payload:d})})();if(u){let{context:e,digest:t}=await iq(c,{account:_,authorizeKeys:[u],preCalls:!0});await sq(c,{context:e,key:v,signature:await yB(v,{address:null,payload:t})})}let b=await(async()=>{if(o){if(f===`siwe`&&p&&y)return{message:p,signature:await gq(c,{address:_.address,signature:y})};{let e=await HK(c,o,{address:_.address}),t=await OB(_,{payload:KK(eL(e)),role:`admin`});return{message:e,signature:await gq(c,{address:_.address,signature:t})}}}})();return{accounts:[{..._,signInWithEthereum:b}]}},async prepareCalls(e){let{account:t,calls:n,internal:i,merchantUrl:a}=e,{client:o}=i,s=e.key??await xK({account:t,calls:n});if(!s)throw Error(`cannot find authorized key to sign with.`);let[c,l]=await Promise.all([vq(o),bq(o,{addressOrSymbol:e.feeToken,store:i.store})]),u=_q(e.requiredFunds??[],{tokens:c}),{capabilities:d,context:f,digest:p,typedData:m}=await iq(o,{account:t,calls:n,feeToken:l?.address,key:s,merchantUrl:a,requiredFunds:r?u:void 0}),h=f.quote?.quotes??[],g=h[h.length-1];return{account:t,capabilities:{...d,quote:f.quote},chainId:o.chain.id,context:{...f,account:t,calls:n,nonce:g?.intent.nonce},digest:p,key:s,typedData:m}},async prepareUpgradeAccount(e){let{address:t,email:r,label:a,internal:c,permissions:l}=e,{client:u}=c,[d,f]=await Promise.all([vq(u),bq(u,{store:c.store})]),p=n?lB():await cB({createFn:i?.createFn,label:a||`${t.slice(0,8)}\u2026${t.slice(-6)}`,rpId:s,userId:_L(t)}),m=await DK(l,{chainId:u.chain.id,feeTokens:d}),{context:h,digests:g}=await aq(u,{address:t,authorizeKeys:[p,...m?[m]:[]],feeToken:f?.address});return r&&(o=a),{context:h,digests:g}},async revokeAdmin(e){let{account:t,id:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.id===n);if(o){if(o.type===`webauthn-p256`&&t.keys?.filter(e=>e.type===`webauthn-p256`).length===1)throw Error(`revoke the only WebAuthn key left.`);try{let{id:n}=await oq(a,{account:t,feeToken:(await bq(a,{addressOrSymbol:e.feeToken,store:r.store}))?.address,revokeKeys:[o],webAuthn:i});await Zm(a,{id:n})}catch(e){let t=e;if(t.name===`Rpc.ExecutionError`&&t.abiError?.name===`KeyDoesNotExist`)return;throw e}}},async revokePermissions(e){let{account:t,id:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.id===n);if(o){if(o.role===`admin`)throw Error(`cannot revoke admins.`);try{let{id:n}=await oq(a,{account:t,feeToken:(await bq(a,{addressOrSymbol:e.feeToken,store:r.store}))?.address,revokeKeys:[o],webAuthn:i});await Zm(a,{id:n})}catch(e){let t=e;if(t.name===`Rpc.ExecutionError`&&t.abiError?.name===`KeyDoesNotExist`)return;throw e}}},async sendCalls(e){let{account:t,asTxHash:n,calls:a,chainId:o,internal:s,merchantUrl:c}=e,{client:l}=s;if(!t)throw Error(`account required for relay mode`);let u=await xK({account:t,calls:a,permissionsId:e.permissionsId}),[d,f]=await Promise.all([vq(l),bq(l,{addressOrSymbol:e.feeToken,store:s.store})]),p=_q(e.requiredFunds??[],{tokens:d}),m=await oq(l,{account:t,calls:a,feeToken:f?.address,key:u,merchantUrl:c,requiredFunds:r?p:void 0,webAuthn:i,...o?{chain:{id:o}}:{}});if(n){let{id:e,receipts:t,status:n}=await Zm(l,{id:m.id,pollingInterval:500});if(!t?.[0])throw n===`success`?new DI({message:`Call bundle with id: `+e+` not found.`}):new cI({message:`Transaction failed under call bundle id: `+e+`.`});return{id:t[0].transactionHash}}return m},async sendPreparedCalls(e){let{context:t,key:n,internal:r,signature:i}=e,{client:a}=r,{id:o}=await sq(a,{context:t,key:n,signature:i});return o},async signPersonalMessage(e){let{account:t,data:n,internal:r}=e,{client:a}=r,o=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!o)throw Error(`cannot find admin key to sign with.`);let s=await OB(t,{key:o,payload:KK(n),webAuthn:i});return gq(a,{address:t.address,signature:s})},async signTypedData(e){let{account:t,internal:n}=e,{client:r}=n,a=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!a)throw Error(`cannot find admin key to sign with.`);let o=XF(e.data),s=o.domain?.name===`Orchestrator`,c=await OB(t,{key:a,payload:tz(o),replaySafe:!s,webAuthn:i});return s?c:gq(r,{address:t.address,signature:c})},async upgradeAccount(e){let{account:t,context:n,internal:r,signatures:i}=e,{client:a}=r;return await lq(a,{context:n,signatures:i}),o&&await cq(a,{email:o,walletAddress:t.address}),{account:t}},async verifyEmail(e){let{account:t,chainId:n,email:r,token:a,internal:o,walletAddress:s}=e,{client:c}=o,l=t.keys?.find(e=>e.role===`admin`&&e.privateKey);if(!l)throw Error(`cannot find admin key to sign with.`);return await uq(c,{chainId:n,email:r,signature:await OB(t,{key:l,payload:ML(eL(`${r}${a}`)),webAuthn:i}),token:a,walletAddress:s})}},config:e,name:`rpc`})}function Sq(e={}){let{fallback:t=xq(),host:n=WB.prod,renderer:r=KB(),theme:i,themeController:a}=e,o=new Set,s=SK();function c(e,t){return new Promise((n,r)=>{let i=a=>{let s=a.find(t=>t.request.id===e);if(!s&&a.length===0){o.delete(i),r(new vI);return}s&&(s.status!==`success`&&s.status!==`error`||(o.delete(i),s.status===`success`?n(s.result):r(NI(s.error)),t.setState(t=>({...t,requestQueue:t.requestQueue.filter(t=>t.request.id!==e)}))))};o.add(i)})}function l(e){return MI({async request(t){let n=s.prepare(t);return e.setState(e=>{let t=e.accounts[0],r=t?.keys?.find(e=>e.role===`admin`&&e.type===`webauthn-p256`);return{...e,requestQueue:[...e.requestQueue,{account:t?{address:t.address,key:r?{credentialId:r?.credentialId,publicKey:r.publicKey}:void 0}:void 0,request:n,status:`pending`}]}}),c(n.id,e)}},{schema:wK()})}return bK({actions:{async addFunds(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`wallet_addFunds`)throw Error(`Cannot add funds for method: `+n.method);return await l(r).request(n)},async createAccount(e){let{internal:t}=e,{client:n,config:r,request:i,store:a}=t,{storage:o}=r,s=l(a);return{account:await(async()=>{if(i.method===`wallet_connect`){let[{capabilities:e,chainIds:t}]=i._decoded.params??[{}],a=wq(e?.signInWithEthereum?.authUrl??r.authUrl,{storage:o}),c=i.params?.[0]?.capabilities?.signInWithEthereum,l=await DK(e?.grantPermissions,{chainId:n.chain.id}),u=l?RV(TK,EK(l)):void 0,{accounts:d}=await s.request({...i,params:[{capabilities:{...i.params?.[0]?.capabilities,grantPermissions:u,signInWithEthereum:a||c?{...c,authUrl:a}:void 0},chainIds:t?.map(e=>$I(e))}]}),[f]=d;if(!f)throw Error(`no account found.`);let p=f.capabilities?.admins?.map(e=>dB(e,{chainId:n.chain.id})).filter(Boolean),m=f.capabilities?.permissions?.map(e=>{try{let t=_G(zV(hG,e));return t.id===l?.id?{...t,...l,permissions:t.permissions}:t}catch{return}}).filter(Boolean),h=await(async()=>{if(!f.capabilities?.signInWithEthereum)return;let{message:e,signature:t}=f.capabilities.signInWithEthereum;if(!a)return{message:e,signature:t};let{token:n}=await VK({address:f.address,authUrl:a,message:e,publicKey:f.capabilities?.admins?.[0]?.publicKey,signature:t});return{message:e,signature:t,token:n}})();return{...TB({address:f.address,keys:[...p??[],...m??[]]}),signInWithEthereum:h}}throw Error(`Account creation not supported on method: ${i.method}`)})()}},async disconnect(e){let{internal:t}=e,{config:n}=t,{storage:r}=n,i=await r.getItem(`porto.authUrl`)||void 0,a=wq(n.authUrl??i,{storage:r});a&&await fetch(a.logout,{credentials:`include`,method:`POST`}).catch(()=>{})},async getAccountVersion(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getAccountVersion`)throw Error(`Cannot get version for method: `+a.method);return r.supportsHeadless?await l(i).request(a):t.actions.getAccountVersion(e)},async getAssets(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getAssets`)throw Error(`Cannot get assets for method: `+a.method);if(!r.supportsHeadless)return t.actions.getAssets(e);let o=await l(i).request(a);return zV(tK.Response,o)},async getCallsHistory(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCallsHistory`)throw Error(`Cannot get history for method: `+a.method);if(!r.supportsHeadless)return t.actions.getCallsHistory(e);let o=await l(i).request(a);return zV(rK.Response,o)},async getCallsStatus(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCallsStatus`)throw Error(`Cannot get status for method: `+a.method);return r.supportsHeadless?await l(i).request(a):t.actions.getCallsStatus(e)},async getCapabilities(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_getCapabilities`)throw Error(`Cannot get capabilities for method: `+a.method);return r.supportsHeadless?await l(i).request(a):t.actions.getCapabilities(e)},async getKeys(e){let{account:n,chainIds:i,internal:a}=e,{store:o}=a;return RB([...await(async()=>{if(!r.supportsHeadless)return t.actions.getKeys(e);let a=await l(o).request({method:`wallet_getKeys`,params:[RV(aK.Parameters,{address:n.address,chainIds:i})]});return zV(aK.Response,a)})(),...n.keys??[]],e=>e.publicKey)},async grantAdmin(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`wallet_grantAdmin`)throw Error(`Cannot authorize admin for method: `+n.method);let[i]=n._decoded.params,a=dB(i.key);if(!a)throw Error(`no key found.`);let o=await Cq(t,e);return await l(r).request({method:`wallet_grantAdmin`,params:[{...n.params?.[0],capabilities:{...n.params?.[0]?.capabilities,feeToken:o}}]}),{key:a}},async grantPermissions(e){let{internal:t}=e,{client:n,request:r,store:i}=t;if(r.method!==`wallet_grantPermissions`)throw Error(`Cannot grant permissions for method: `+r.method);let[{address:a,...o}]=r._decoded.params,s=await DK(o,{chainId:n.chain.id});if(!s)throw Error(`no key found.`);let c=RV(TK,EK(s));return await l(i).request({method:`wallet_grantPermissions`,params:[c]}),{key:s}},async loadAccounts(e){let{internal:t}=e,{client:n,config:r,store:i}=t,{storage:a}=r,o=l(i),s=t.request;if(s.method!==`wallet_connect`&&s.method!==`eth_requestAccounts`)throw Error(`Cannot load accounts for method: `+s.method);return{accounts:await(async()=>{let[e]=s._decoded.params??[],{capabilities:t}=e??{},i=wq(t?.signInWithEthereum?.authUrl??r.authUrl,{storage:a}),c=s.params?.[0]?.capabilities?.signInWithEthereum,l=await DK(t?.grantPermissions,{chainId:n.chain.id}),u=l?RV(TK,EK(l)):void 0,{accounts:d}=await o.request({method:`wallet_connect`,params:[{...s.params?.[0],capabilities:{...s.params?.[0]?.capabilities,grantPermissions:u,signInWithEthereum:i||c?{...c,authUrl:i}:void 0}}]});return Promise.all(d.map(async e=>{let t=e.capabilities?.admins?.map(e=>dB(e)).filter(Boolean),n=e.capabilities?.permissions?.map(e=>{try{let t=_G(zV(hG,e));return t.id===l?.id?{...t,...l,permissions:t.permissions}:t}catch{return}}).filter(Boolean),r=await(async()=>{if(!e.capabilities?.signInWithEthereum)return;let{message:t,signature:n}=e.capabilities.signInWithEthereum;if(!i)return{message:t,signature:n};let{token:r}=await VK({address:e.address,authUrl:i,message:t,publicKey:e.capabilities?.admins?.[0]?.publicKey,signature:n});return{message:t,signature:n,token:r}})();return{...TB({address:e.address,keys:[...t??[],...n??[]]}),signInWithEthereum:r}}))})()}},async prepareCalls(e){let{account:n,internal:i}=e,{store:a,request:o}=i;if(o.method!==`wallet_prepareCalls`)throw Error(`Cannot prepare calls for method: `+o.method);if(!r.supportsHeadless)return t.actions.prepareCalls(e);let s=await Cq(i,e),c=l(a),u=zV(oK.Response,await c.request({...o,params:[{...o.params?.[0],capabilities:{...o.params?.[0]?.capabilities,feeToken:s}}]}));return{account:n,chainId:Number(u.chainId),context:u.context,digest:u.digest,key:u.key,typedData:u.typedData}},async prepareUpgradeAccount(e){let{internal:n}=e,{client:i,store:a,request:o}=n;if(o.method!==`wallet_prepareUpgradeAccount`)throw Error(`Cannot prepare upgrade for method: `+o.method);if(!r.supportsHeadless)return t.actions.prepareUpgradeAccount(e);let[{capabilities:s}]=o._decoded.params??[{}],c=await DK(s?.grantPermissions,{chainId:i.chain.id}),u=c?RV(TK,EK(c)):void 0,{context:d,digests:f}=await l(a).request({...o,params:[{...o.params?.[0],capabilities:{...o.params?.[0]?.capabilities,grantPermissions:u}}]}),p=d.account.keys?.map(e=>e.id===c?.id?{...e,...c}:e);return{context:{...d,account:{...d.account,keys:p}},digests:f}},async revokeAdmin(e){let{account:t,id:n,internal:r}=e,{store:i,request:a}=r;if(a.method!==`wallet_revokeAdmin`)throw Error(`Cannot revoke admin for method: `+a.method);let o=t.keys?.find(e=>e.id===n);if(!o)return;if(o.type===`webauthn-p256`&&t.keys?.filter(e=>e.type===`webauthn-p256`).length===1)throw Error(`revoke the only WebAuthn key left.`);let s=await Cq(r,e);return await l(i).request({...a,params:[{...a.params?.[0],capabilities:{...a.params?.[0]?.capabilities,feeToken:s}}]})},async revokePermissions(e){let{account:t,id:n,internal:r}=e,{store:i,request:a}=r;if(a.method!==`wallet_revokePermissions`)throw Error(`Cannot revoke permissions for method: `+a.method);let o=t.keys?.find(e=>e.id===n);if(o){if(o.role===`admin`)throw Error(`cannot revoke permissions.`);return await l(i).request(a)}},async sendCalls(e){let{account:n,asTxHash:i,calls:a,chainId:o,internal:s,merchantUrl:c,requiredFunds:u}=e,{client:d,store:f,request:p}=s,m=l(f),h=await Cq(s,e),g=n?await xK({account:n,calls:a,permissionsId:e.permissionsId}):void 0;if(g?.role===`session`&&n){if(!r.supportsHeadless)return t.actions.sendCalls(e);try{let e=await m.request(RV(oK.Request,{method:`wallet_prepareCalls`,params:[{calls:a,capabilities:{...p._decoded.method===`wallet_sendCalls`?p._decoded.params?.[0]?.capabilities:void 0,feeToken:h,merchantUrl:c,requiredFunds:u},chainId:o,from:n.address,key:g}]})),t=e.capabilities?.quote?.quotes??[];if(t.some((e,n)=>n===t.length-1&&t.length>1?!1:oL(e.feeTokenDeficit)>0n))throw Error(`insufficient funds`);let r=await yB(g,{address:null,payload:e.digest,wrap:!1}),s=(await m.request({method:`wallet_sendPreparedCalls`,params:[{...e,signature:r}]}))[0];if(!s)throw Error(`id not found`);if(i){let{id:e,receipts:t,status:n}=await Zm(d,{id:s.id,pollingInterval:500});if(!t?.[0])throw n===`success`?new DI({message:`Call bundle with id: `+e+` not found.`}):new cI({message:`Transaction failed under call bundle id: `+e+`.`});return{id:t[0].transactionHash}}return s}catch{}}if(p.method===`eth_sendTransaction`)return{id:await m.request({...p,params:[{...p.params?.[0],capabilities:{feeToken:h,merchantUrl:c},...o?{chainId:$I(o)}:{}}]})};if(p.method===`wallet_sendCalls`)return await m.request({method:`wallet_sendCalls`,params:[{...p.params?.[0],capabilities:{...p.params?.[0]?.capabilities,feeToken:h,merchantUrl:c},...o?{chainId:$I(o)}:{}}]});throw Error(`Cannot execute for method: `+p.method)},async sendPreparedCalls(e){let{internal:n}=e,{store:i,request:a}=n;if(a.method!==`wallet_sendPreparedCalls`)throw Error(`Cannot send prepared calls for method: `+a.method);if(!r.supportsHeadless)return t.actions.sendPreparedCalls(e);let o=(await l(i).request(a))[0]?.id;if(!o)throw Error(`id not found`);return o},async signPersonalMessage(e){let{internal:t}=e,{store:n,request:r}=t;if(r.method!==`personal_sign`)throw Error(`Cannot sign personal message for method: `+r.method);return await l(n).request(r)},async signTypedData(e){let{internal:t}=e,{store:n,request:r}=t;if(r.method!==`eth_signTypedData_v4`)throw Error(`Cannot sign typed data for method: `+r.method);return await l(n).request(r)},async switchChain(e){let{internal:t}=e,{store:n,request:i}=t;if(i.method!==`wallet_switchEthereumChain`)throw Error(`Cannot switch chain for method: `+i.method);if(r.supportsHeadless)return await l(n).request(i)},async upgradeAccount(e){let{account:t,internal:n}=e,{store:r,request:i}=n;if(i.method!==`wallet_upgradeAccount`)throw Error(`Cannot upgrade account for method: `+i.method);return await l(r).request(i),{account:t}},async verifyEmail(e){let{internal:t}=e,{request:n,store:r}=t;if(n.method!==`account_verifyEmail`)throw Error(`Cannot verify email for method: `+n.method);return await l(r).request(n)}},config:e,name:`dialog`,setup(e){let{internal:t}=e,{store:s}=t,c=r.setup({host:n,internal:t,theme:i,themeController:a}),l=s.subscribe(e=>e.requestQueue,e=>{for(let t of o)t(e);let t=e.map(e=>e.status===`pending`?e:void 0).filter(Boolean);c.syncRequests(t).catch(()=>{}),t.length===0&&c.close()});return()=>{l(),c.destroy()}}})}async function Cq(e,t){let{config:{feeToken:n}}=e,{feeToken:r}=t??{};return r??n}function wq(e,{storage:t}){if(!e)return;let n=UK(e,typeof window<`u`?window.location.origin:void 0);return n&&t.setItem(`porto.authUrl`,n),n}var Tq=e=>!!e.dispatchFromDevtools&&typeof e.dispatch==`function`,Eq=new Map,Dq=e=>{let t=Eq.get(e);return t?Object.fromEntries(Object.entries(t.stores).map(([e,t])=>[e,t.getState()])):{}},Oq=(e,t,n)=>{if(e===void 0)return{type:`untracked`,connection:t.connect(n)};let r=Eq.get(n.name);if(r)return{type:`tracked`,store:e,...r};let i={connection:t.connect(n),stores:{}};return Eq.set(n.name,i),{type:`tracked`,store:e,...i}},kq=(e,t)=>{if(t===void 0)return;let n=Eq.get(e);n&&(delete n.stores[t],Object.keys(n.stores).length===0&&Eq.delete(e))},Aq=e=>{if(!e)return;let t=e.split(` -`),n=t.findIndex(e=>e.includes(`api.setState`));if(n<0)return;let r=t[n+1]?.trim()||``;return/.+ (.+) .+/.exec(r)?.[1]},jq=(e,t={})=>(n,r,i)=>{let{enabled:a,anonymousActionType:o,store:s,...c}=t,l;try{l=(a??!1)&&window.__REDUX_DEVTOOLS_EXTENSION__}catch{}if(!l)return e(n,r,i);let{connection:u,...d}=Oq(s,l,c),f=!0;i.setState=((e,t,a)=>{let l=n(e,t);if(!f)return l;let d=a===void 0?{type:o||Aq(Error().stack)||`anonymous`}:typeof a==`string`?{type:a}:a;return s===void 0?(u?.send(d,r()),l):(u?.send({...d,type:`${s}/${d.type}`},{...Dq(c.name),[s]:i.getState()}),l)}),i.devtools={cleanup:()=>{u&&typeof u.unsubscribe==`function`&&u.unsubscribe(),kq(c.name,s)}};let p=(...e)=>{let t=f;f=!1,n(...e),f=t},m=e(i.setState,r,i);if(d.type===`untracked`?u?.init(m):(d.stores[d.store]=i,u?.init(Object.fromEntries(Object.entries(d.stores).map(([e,t])=>[e,e===d.store?m:t.getState()])))),Tq(i)){let e=i.dispatch;i.dispatch=(...t)=>{e(...t)}}return u.subscribe(e=>{switch(e.type){case`ACTION`:if(typeof e.payload!=`string`){console.error(`[zustand devtools middleware] Unsupported action format`);return}return Mq(e.payload,e=>{if(e.type===`__setState`){if(s===void 0){p(e.state);return}Object.keys(e.state).length!==1&&console.error(` - [zustand devtools middleware] Unsupported __setState action format. - When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), - and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } - `);let t=e.state[s];if(t==null)return;JSON.stringify(i.getState())!==JSON.stringify(t)&&p(t);return}Tq(i)&&i.dispatch(e)});case`DISPATCH`:switch(e.payload.type){case`RESET`:return p(m),s===void 0?u?.init(i.getState()):u?.init(Dq(c.name));case`COMMIT`:if(s===void 0){u?.init(i.getState());return}return u?.init(Dq(c.name));case`ROLLBACK`:return Mq(e.state,e=>{if(s===void 0){p(e),u?.init(i.getState());return}p(e[s]),u?.init(Dq(c.name))});case`JUMP_TO_STATE`:case`JUMP_TO_ACTION`:return Mq(e.state,e=>{if(s===void 0){p(e);return}JSON.stringify(i.getState())!==JSON.stringify(e[s])&&p(e[s])});case`IMPORT_STATE`:{let{nextLiftedState:t}=e.payload,n=t.computedStates.slice(-1)[0]?.state;if(!n)return;p(s===void 0?n:n[s]),u?.send(null,t);return}case`PAUSE_RECORDING`:return f=!f}return}}),m},Mq=(e,t)=>{let n;try{n=JSON.parse(e)}catch(e){console.error(`[zustand devtools middleware] Could not parse the received json`,e)}n!==void 0&&t(n)},Nq=e=>(t,n,r)=>{let i=r.subscribe;return r.subscribe=((e,t,n)=>{let a=e;if(t){let i=n?.equalityFn||Object.is,o=e(r.getState());a=n=>{let r=e(n);if(!i(o,r)){let e=o;t(o=r,e)}},n?.fireImmediately&&t(o,o)}return i(a)}),e(t,n,r)};function Pq(e,t){let n;try{n=e()}catch{return}return{getItem:e=>{let r=e=>e===null?null:JSON.parse(e,t?.reviver),i=n.getItem(e)??null;return i instanceof Promise?i.then(r):r(i)},setItem:(e,r)=>n.setItem(e,JSON.stringify(r,t?.replacer)),removeItem:e=>n.removeItem(e)}}var Fq=e=>t=>{try{let n=e(t);return n instanceof Promise?n:{then(e){return Fq(e)(n)},catch(e){return this}}}catch(e){return{then(e){return this},catch(t){return Fq(t)(e)}}}},Iq=(e,t)=>(n,r,i)=>{let a={storage:Pq(()=>window.localStorage),partialize:e=>e,version:0,merge:(e,t)=>({...t,...e}),...t},o=!1,s=0,c=new Set,l=new Set,u=a.storage;if(!u)return e((...e)=>{console.warn(`[zustand persist middleware] Unable to update item '${a.name}', the given storage is currently unavailable.`),n(...e)},r,i);let d=()=>{let e=a.partialize({...r()});return u.setItem(a.name,{state:e,version:a.version})},f=i.setState;i.setState=(e,t)=>(f(e,t),d());let p=e((...e)=>(n(...e),d()),r,i);i.getInitialState=()=>p;let m,h=()=>{if(!u)return;let e=++s;o=!1,c.forEach(e=>e(r()??p));let t=a.onRehydrateStorage?.call(a,r()??p)||void 0;return Fq(u.getItem.bind(u))(a.name).then(e=>{if(e)if(typeof e.version==`number`&&e.version!==a.version){if(a.migrate){let t=a.migrate(e.state,e.version);return t instanceof Promise?t.then(e=>[!0,e]):[!0,t]}console.error(`State loaded from storage couldn't be migrated since no migrate function was provided`)}else return[!1,e.state];return[!1,void 0]}).then(t=>{if(e!==s)return;let[i,o]=t;if(m=a.merge(o,r()??p),n(m,!0),i)return d()}).then(()=>{e===s&&(t?.(r(),void 0),m=r(),o=!0,l.forEach(e=>e(m)))}).catch(n=>{e===s&&t?.(void 0,n)})};return i.persist={setOptions:e=>{a={...a,...e},e.storage&&(u=e.storage)},clearStorage:()=>{u?.removeItem(a.name)},getOptions:()=>a,rehydrate:()=>h(),hasHydrated:()=>o,onHydrate:e=>(c.add(e),()=>{c.delete(e)}),onFinishHydration:e=>(l.add(e),()=>{l.delete(e)})},a.skipHydration||h(),m||p},Lq=e=>{let t,n=new Set,r=(e,r)=>{let i=typeof e==`function`?e(t):e;if(!Object.is(i,t)){let e=t;t=r??(typeof i!=`object`||!i)?i:Object.assign({},t,i),n.forEach(n=>n(t,e))}},i=()=>t,a={setState:r,getState:i,getInitialState:()=>o,subscribe:e=>(n.add(e),()=>n.delete(e))},o=t=e(r,i,a);return a},Rq=(e=>e?Lq(e):Lq);function zq(e){return new Promise((t,n)=>{e.oncomplete=e.onsuccess=()=>t(e.result),e.onabort=e.onerror=()=>n(e.error)})}function Bq(e,t){let n,r=()=>{if(n)return n;let r=indexedDB.open(e);return r.onupgradeneeded=()=>r.result.createObjectStore(t),n=zq(r),n.then(e=>{e.onclose=()=>n=void 0},()=>{}),n};return(e,n)=>r().then(r=>n(r.transaction(t,e).objectStore(t)))}var Vq;function Hq(){return Vq||=Bq(`keyval-store`,`keyval`),Vq}function Uq(e,t=Hq()){return t(`readonly`,t=>zq(t.get(e)))}function Wq(e,t,n=Hq()){return n(`readwrite`,n=>(n.put(t,e),zq(n.transaction)))}function Gq(e,t=Hq()){return t(`readwrite`,t=>(t.delete(e),zq(t.transaction)))}function Kq(e){return e}function qq(){let e=typeof indexedDB<`u`?Bq(`porto`,`store`):void 0;return Kq({async getItem(t){let n=await Uq(t,e);return n===null?null:n},async removeItem(t){await Gq(t,e)},async setItem(t,n){await Wq(t,LB(n),e)},sizeLimit:1024*1024*50})}function Jq(){let e=new Map;return Kq({getItem(t){return e.get(t)??null},removeItem(t){e.delete(t)},setItem(t,n){e.set(t,n)},sizeLimit:1/0})}var Yq=typeof window<`u`&&typeof document<`u`,Xq={announceProvider:!0,chains:qF,mode:Yq?Sq({host:WB.prod}):xq(),relay:$v(tG.prod.http),storage:Yq&&typeof indexedDB<`u`?qq():Jq(),storageKey:`porto.store`};function Zq(e={}){let t=e.chains??Xq.chains,n=Object.fromEntries(t.map(t=>[t.id,e.transports?.[t.id]??$v()])),r={announceProvider:e.announceProvider??Xq.announceProvider,authUrl:e.authUrl,chains:t,feeToken:e.feeToken,merchantUrl:e.merchantUrl,mode:e.mode??Xq.mode,relay:e.relay??Xq.relay,storage:e.storage??Xq.storage,storageKey:e.storageKey??Xq.storageKey,transports:n},i=Rq(jq(Nq(Iq(e=>({accounts:[],chainIds:r.chains.map(e=>e.id),feeToken:r.feeToken,requestQueue:[]}),{merge(e,t){let n=e,i=r.chains.find(e=>e.id===n.chainIds[0])?.id??r.chains[0].id,a=[i,...r.chains.map(e=>e.id).filter(e=>e!==i)];return{...t,...n,chainIds:a}},name:r.storageKey,partialize:e=>({accounts:e.accounts.map(e=>LB(e)),chainIds:e.chainIds}),storage:r.storage,version:5})))),a=r.mode,o={config:r,getMode(){return a},id:zB(),setMode(e){return c?.(),a=e,c=e.setup({internal:o}),c},store:i},s=hK(o),c=a===null?()=>{}:a.setup({internal:o});return{_internal:o,config:r,destroy(){c(),s._internal.destroy()},provider:s}}var Qq=`http://127.0.0.1:9545`,$q=Object.freeze(Object.values(KF)),eJ=e=>$q.find(t=>t.id===e),tJ=e=>{if(typeof e==`number`)return Number.isFinite(e)?e:void 0;if(typeof e!=`string`)return;let t=e.trim();if(/^0x[0-9a-fA-F]+$/.test(t)){let e=Number.parseInt(t,16);return Number.isNaN(e)?void 0:e}if(/^\d+$/.test(t)){let e=Number.parseInt(t,10);return Number.isNaN(e)?void 0:e}},nJ=(e,t,n)=>{let r=tJ(e);if(t(r),r!=null){let e=eJ(r);e||={id:r,name:`Chain ${r}`,network:`chain-${r}`,nativeCurrency:{decimals:18,name:`Ether`,symbol:`ETH`},rpcUrls:{default:{http:[]},public:{http:[]}}},n(e)}else n(void 0)},rJ=async(e,t=`GET`,n)=>{let r={"Content-Type":`application/json`},i=typeof window<`u`?window.__SESSION_TOKEN__:void 0;i&&(r[`X-Session-Token`]=i);let a=await fetch(`${Qq}${e}`,{method:t,headers:r,body:n===void 0?void 0:JSON.stringify(n)});if(!a.ok)throw Error(`API request failed: ${a.status} ${a.statusText}`);try{return await a.json()}catch{throw Error(`Invalid JSON response`)}},iJ=e=>JSON.stringify(e,(e,t)=>typeof t==`bigint`?t.toString():t,2),aJ=e=>{if(e==null)return iJ(e);if(typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`){let t=e;try{let n=JSON.parse(t.message);return iJ({...e,message:n})}catch{return iJ(e)}}return iJ(e)},oJ=e=>!!e&&e.status===`ok`,sJ=s((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),$=s(((e,t)=>{t.exports=sJ()}))();function cJ(){(0,y.useEffect)(()=>{window.__PORTO__||(window.__PORTO__=Zq())},[]);let[e,t]=(0,y.useState)([]),[n,r]=(0,y.useState)(!1),[i,a]=(0,y.useState)(null),[o,s]=(0,y.useState)(null),[c,l]=(0,y.useState)(null),u=e.find(e=>e.info.uuid===c)??null,[d,f]=(0,y.useState)(),[p,m]=(0,y.useState)(),[h,g]=(0,y.useState)(),[_,v]=(0,y.useState)(null),[b,x]=(0,y.useState)(null),[S,ee]=(0,y.useState)(null),C=(0,y.useRef)(null),te=(0,y.useRef)(null),ne=(0,y.useRef)(null),re=async()=>{if(!(!u||n)){f((await u.provider.request({method:`eth_requestAccounts`}))?.[0]??void 0);try{nJ(await u.provider.request({method:`eth_chainId`}),m,g)}catch{m(void 0),g(void 0)}}},ie=async()=>{if(!(!d||p==null)){try{await rJ(`/api/connection`,`POST`,[d,p])}catch{return}r(!0)}},ae=async()=>{if(!u||!o)return;let{id:e,signType:t,request:n}=o,r=n.address,i=n.message;try{let n;switch(t){case`PersonalSign`:n=await u.provider.request({method:`personal_sign`,params:[i,r]});break;case`SignTypedDataV4`:n=await u.provider.request({method:`eth_signTypedData_v4`,params:[r,i]});break;default:throw Error(`Unsupported signType: ${t}`)}await rJ(`/api/signing/response`,`POST`,{id:e,signature:n,error:null}),ee(n),s(null)}catch(t){let n=typeof t==`object`&&t&&`message`in t&&typeof t.message==`string`?t.message:String(t);try{await rJ(`/api/signing/response`,`POST`,{id:e,signature:null,error:n})}catch{}ee(null),s(null)}},oe=async()=>{if(!u||!i?.request)return;let e=Kv({transport:Jv(u.provider),chain:h});try{let{from:t,input:n,to:r,...a}=i.request,o=await e.sendTransaction({...a,account:t||(await e.getAddresses())[0],...n?{data:n}:{},...r?{to:r}:{},chain:h});x(o),await rJ(`/api/transaction/response`,`POST`,{id:i.id,hash:o,error:null}),v(await wv(e,{hash:o}))}catch(e){let t=typeof e==`object`&&e&&`message`in e&&typeof e.message==`string`?e.message:String(e);console.error(`send failed:`,t);try{await rJ(`/api/transaction/response`,`POST`,{id:i.id,hash:null,error:t})}catch{}}},se=(0,y.useCallback)(()=>{C.current&&=(window.clearInterval(C.current),null),te.current&&=(window.clearInterval(te.current),null),a(null),s(null),x(null),v(null),f(void 0),m(void 0),g(void 0),r(!1),rJ(`/api/connection`,`POST`,null)},[]);return(0,y.useEffect)(()=>{ne.current&&c&&ne.current!==c&&se(),ne.current=c},[c,se]),(0,y.useEffect)(()=>{e.length===1&&!u&&l(e[0].info.uuid)},[e,u]),(0,y.useEffect)(()=>{let e=e=>{let{info:n,provider:r}=e.detail;t(e=>e.some(e=>e.info.uuid===n.uuid)?e:[...e,{info:n,provider:r}])};return window.addEventListener(`eip6963:announceProvider`,e),window.dispatchEvent(new Event(`eip6963:requestProvider`)),()=>window.removeEventListener(`eip6963:announceProvider`,e)},[]),(0,y.useEffect)(()=>{if(!u)return;let e=e=>{n||f(e[0]??void 0)},t=e=>{n||nJ(e,m,g)};return u.provider.on?.(`accountsChanged`,e),u.provider.on?.(`chainChanged`,t),()=>{u.provider.removeListener?.(`accountsChanged`,e),u.provider.removeListener?.(`chainChanged`,t)}},[u,n]),(0,y.useEffect)(()=>{if(!n||i||o)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await rJ(`/api/transaction/request`);oJ(n)&&(window.clearInterval(t),e&&a(n.data))}catch{}},1e3);return C.current=t,()=>{e=!1,window.clearInterval(t),C.current===t&&(C.current=null)}},[n,i,o]),(0,y.useEffect)(()=>{if(!n||o||i)return;let e=!0,t=window.setInterval(async()=>{if(e)try{let n=await rJ(`/api/signing/request`);oJ(n)&&(window.clearInterval(t),e&&s(n.data))}catch{}},1e3);return te.current=t,()=>{e=!1,window.clearInterval(t),te.current===t&&(te.current=null)}},[n,o,i]),(0,$.jsx)(`div`,{className:`wrapper`,children:(0,$.jsxs)(`div`,{className:`container`,children:[(0,$.jsx)(`div`,{className:`notice`,children:`Browser wallet is still in early development. Use with caution!`}),(0,$.jsx)(`img`,{className:`banner`,src:`banner.png`,alt:`Foundry Browser Wallet`}),e.length>1&&(0,$.jsx)(`div`,{className:`wallet-selector`,children:(0,$.jsx)(`label`,{children:(0,$.jsxs)(`select`,{value:c??``,onChange:e=>l(e.target.value||null),disabled:n,children:[(0,$.jsx)(`option`,{value:``,disabled:!0,children:`Select wallet…`}),e.map(({info:e})=>(0,$.jsxs)(`option`,{value:e.uuid,children:[e.name,` (`,e.rdns,`)`]},e.uuid))]})})}),e.length===0&&(0,$.jsx)(`p`,{children:`No wallets found.`}),u&&!d&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-connect`,onClick:re,disabled:n,children:`Connect Wallet`}),u&&d&&!n&&(0,$.jsx)(`button`,{type:`button`,className:`wallet-confirm`,onClick:ie,disabled:!d||p==null,children:`Confirm Connection`}),u&&d&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Connected`}),(0,$.jsx)(`pre`,{className:`box`,children:`\ -account: ${d} -chain: ${h?`${h.name} (${p})`:p??`unknown`} -rpc: ${h?.rpcUrls?.default?.http?.[0]??h?.rpcUrls?.public?.http?.[0]??`unknown`}`})]}),u&&d&&n&&!i&&!o&&!b&&!S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction To Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:`No pending transaction or signing request`})})]}),u&&d&&n&&!b&&i&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction to Sign & Send`}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:oe,children:`Sign & Send`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:iJ(i.request)})})]}),u&&d&&n&&!i&&o&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Message / Data to Sign`}),(0,$.jsx)(`button`,{type:`button`,className:`wallet-send`,onClick:ae,children:`Sign`}),(0,$.jsx)(`div`,{className:`box`,children:(0,$.jsx)(`pre`,{children:aJ(o.request)})})]}),u&&d&&b&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Transaction Hash`}),(0,$.jsx)(`pre`,{className:`box`,children:b}),(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Receipt`}),(0,$.jsx)(`pre`,{className:`box`,children:_?iJ(_):`Waiting for receipt...`})]})]}),u&&d&&n&&S&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`section-title`,children:`Signature Result`}),(0,$.jsx)(`pre`,{className:`box`,children:S})]})]})})}var lJ=document.getElementById(`root`);if(lJ)(0,v.createRoot)(lJ).render((0,$.jsx)(y.StrictMode,{children:(0,$.jsx)(cJ,{})}));else throw Error(`Root element with id "root" not found`); \ No newline at end of file diff --git a/crates/wallets/src/wallet_browser/app/assets/styles.css b/crates/wallets/src/wallet_browser/app/assets/styles.css deleted file mode 100644 index 3e00c2bb172d8..0000000000000 --- a/crates/wallets/src/wallet_browser/app/assets/styles.css +++ /dev/null @@ -1,2 +0,0 @@ -*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;color:inherit;letter-spacing:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;color:inherit;letter-spacing:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html,body,#root{color:#f8f8f8;background-color:#13151b;width:100%;height:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}button{color:#f8f8f8;cursor:pointer;background-color:#3a3f51;border:1px solid #e1e4e8;border-radius:4px;padding:8px 12px}button:hover{background-color:#50566e}select{color:#f8f8f8;cursor:pointer;background-color:#1e2026;border:1px solid #e1e4e8;border-radius:8px;margin-bottom:16px;padding:8px}option{color:#f8f8f8;background-color:#1e2026}pre{white-space:pre-wrap;word-break:break-all;overflow-wrap:anywhere}.wrapper{flex-direction:column;justify-content:center;align-items:center;min-height:100vh;padding:24px;display:flex}.container{background-color:#3b3b3b;border-radius:8px;flex-direction:column;align-items:flex-start;max-width:600px;padding:16px;display:flex}.notice{color:#333;text-align:center;background-color:#fc0;border-radius:8px;width:100%;margin-bottom:16px;padding:8px;font-size:13px;font-weight:700}.banner{border-radius:8px;width:100%;height:auto}.wallet-selector,.wallet-connect,.wallet-send,.wallet-confirm{align-self:center;margin-top:16px}.wallet-send{margin-bottom:16px}.title,.section-title{color:#f8f8f8}.title{margin-bottom:24px;font-size:36px}.section-title{margin-bottom:16px;font-size:24px}.box{border:1px solid #e1e4e8;border-radius:8px;margin-bottom:16px;padding:8px 12px;font-size:13px} -/*$vite$:1*/ \ No newline at end of file diff --git a/crates/wallets/src/wallet_browser/app/mod.rs b/crates/wallets/src/wallet_browser/app/mod.rs deleted file mode 100644 index 6b1adcdbe47c9..0000000000000 --- a/crates/wallets/src/wallet_browser/app/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub(crate) mod contents { - pub const INDEX_HTML: &str = include_str!("assets/index.html"); - pub const STYLES_CSS: &str = include_str!("assets/styles.css"); - pub const MAIN_JS: &str = include_str!("assets/main.js"); - pub const BANNER_PNG: &[u8] = include_bytes!("assets/banner.png"); - pub const LOGO_PNG: &[u8] = include_bytes!("assets/logo.png"); -} diff --git a/crates/wallets/src/wallet_browser/error.rs b/crates/wallets/src/wallet_browser/error.rs deleted file mode 100644 index ace0f1c1cf3f5..0000000000000 --- a/crates/wallets/src/wallet_browser/error.rs +++ /dev/null @@ -1,28 +0,0 @@ -use alloy_signer::Error as SignerError; - -#[derive(Debug, thiserror::Error)] -pub enum BrowserWalletError { - #[error("{operation} request timed out")] - Timeout { operation: &'static str }, - - #[error("{operation} rejected: {reason}")] - Rejected { operation: &'static str, reason: String }, - - #[error("Wallet not connected")] - NotConnected, - - #[error("Server error: {0}")] - ServerError(String), -} - -impl From for SignerError { - fn from(err: BrowserWalletError) -> Self { - Self::other(err) - } -} - -impl From for BrowserWalletError { - fn from(err: SignerError) -> Self { - Self::ServerError(err.to_string()) - } -} diff --git a/crates/wallets/src/wallet_browser/handlers.rs b/crates/wallets/src/wallet_browser/handlers.rs deleted file mode 100644 index 4b210dca60d7f..0000000000000 --- a/crates/wallets/src/wallet_browser/handlers.rs +++ /dev/null @@ -1,195 +0,0 @@ -use std::sync::Arc; - -use alloy_network::Network; -use axum::{ - Json, - extract::State, - http::{ - HeaderMap, HeaderValue, - header::{CACHE_CONTROL, CONTENT_TYPE, EXPIRES, PRAGMA}, - }, - response::Html, -}; - -use crate::wallet_browser::{ - app::contents, - state::BrowserWalletState, - types::{ - BrowserApiResponse, BrowserSignRequest, BrowserSignResponse, BrowserTransactionRequest, - BrowserTransactionResponse, Connection, - }, -}; - -/// Serve index.html -pub(crate) async fn serve_index() -> impl axum::response::IntoResponse { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("text/html; charset=utf-8")); - headers.insert( - CACHE_CONTROL, - HeaderValue::from_static("no-store, no-cache, must-revalidate, max-age=0"), - ); - headers.insert(PRAGMA, HeaderValue::from_static("no-cache")); - headers.insert(EXPIRES, HeaderValue::from_static("0")); - (headers, Html(contents::INDEX_HTML)) -} - -/// Serve styles.css -pub(crate) async fn serve_css() -> impl axum::response::IntoResponse { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("text/css; charset=utf-8")); - headers.insert( - CACHE_CONTROL, - HeaderValue::from_static("no-store, no-cache, must-revalidate, max-age=0"), - ); - headers.insert(PRAGMA, HeaderValue::from_static("no-cache")); - headers.insert(EXPIRES, HeaderValue::from_static("0")); - (headers, contents::STYLES_CSS) -} - -/// Serve main.js with injected session token. -pub(crate) async fn serve_js( - State(state): State>>, -) -> impl axum::response::IntoResponse { - let token = state.session_token(); - let js = format!("window.__SESSION_TOKEN__ = \"{}\";\n{}", token, contents::MAIN_JS); - - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/javascript; charset=utf-8")); - headers.insert( - CACHE_CONTROL, - HeaderValue::from_static("no-store, no-cache, must-revalidate, max-age=0"), - ); - headers.insert(PRAGMA, HeaderValue::from_static("no-cache")); - headers.insert(EXPIRES, HeaderValue::from_static("0")); - (headers, js) -} - -/// Serve banner.png -pub(crate) async fn serve_banner_png() -> impl axum::response::IntoResponse { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("image/png")); - headers.insert(CACHE_CONTROL, HeaderValue::from_static("public, max-age=31536000, immutable")); - (headers, contents::BANNER_PNG) -} - -/// Serve logo.png -pub(crate) async fn serve_logo_png() -> impl axum::response::IntoResponse { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("image/png")); - headers.insert(CACHE_CONTROL, HeaderValue::from_static("public, max-age=31536000, immutable")); - (headers, contents::LOGO_PNG) -} - -/// Get the next pending transaction request. -/// Route: GET /api/transaction/request -pub(crate) async fn get_next_transaction_request( - State(state): State>>, -) -> Json>> { - match state.read_next_transaction_request().await { - Some(tx) => Json(BrowserApiResponse::with_data(tx)), - None => Json(BrowserApiResponse::error("No pending transaction request")), - } -} - -/// Post a transaction response (signed or error). -/// Route: POST /api/transaction/response -pub(crate) async fn post_transaction_response( - State(state): State>>, - Json(body): Json, -) -> Json { - // Ensure that the transaction request exists. - if !state.has_transaction_request(&body.id).await { - return Json(BrowserApiResponse::error("Unknown transaction id")); - } - - // Ensure that exactly one of hash or error is provided. - match (&body.hash, &body.error) { - (None, None) => { - return Json(BrowserApiResponse::error("Either hash or error must be provided")); - } - (Some(_), Some(_)) => { - return Json(BrowserApiResponse::error("Only one of hash or error can be provided")); - } - _ => {} - } - - // Validate transaction hash if provided. - if let Some(hash) = &body.hash { - // Check for all-zero hash - if hash.is_zero() { - return Json(BrowserApiResponse::error("Invalid (zero) transaction hash")); - } - - // Sanity check: ensure the hash is exactly 32 bytes - if hash.as_slice().len() != 32 { - return Json(BrowserApiResponse::error( - "Malformed transaction hash (expected 32 bytes)", - )); - } - } - - state.add_transaction_response(body).await; - - Json(BrowserApiResponse::ok()) -} - -/// Get the next pending signing request. -/// Route: GET /api/signing/request -pub(crate) async fn get_next_signing_request( - State(state): State>>, -) -> Json> { - match state.read_next_signing_request().await { - Some(req) => Json(BrowserApiResponse::with_data(req)), - None => Json(BrowserApiResponse::error("No pending signing request")), - } -} - -/// Post a signing response (signature or error). -/// Route: POST /api/signing/response -pub(crate) async fn post_signing_response( - State(state): State>>, - Json(body): Json, -) -> Json { - // Ensure that the signing request exists. - if !state.has_signing_request(&body.id).await { - return Json(BrowserApiResponse::error("Unknown signing request id")); - } - - // Ensure that exactly one of signature or error is provided. - match (&body.signature, &body.error) { - (None, None) => { - return Json(BrowserApiResponse::error("Either signature or error must be provided")); - } - (Some(_), Some(_)) => { - return Json(BrowserApiResponse::error( - "Only one of signature or error can be provided", - )); - } - _ => {} - } - - state.add_signing_response(body).await; - - Json(BrowserApiResponse::ok()) -} - -/// Get current connection information. -/// Route: GET /api/connection -pub(crate) async fn get_connection_info( - State(state): State>>, -) -> Json>> { - let connection = state.get_connection().await; - - Json(BrowserApiResponse::with_data(connection)) -} - -/// Post connection update (connect or disconnect). -/// Route: POST /api/connection -pub(crate) async fn post_connection_update( - State(state): State>>, - Json(body): Json>, -) -> Json { - state.set_connection(body).await; - - Json(BrowserApiResponse::ok()) -} diff --git a/crates/wallets/src/wallet_browser/mod.rs b/crates/wallets/src/wallet_browser/mod.rs deleted file mode 100644 index dc7df0a01b107..0000000000000 --- a/crates/wallets/src/wallet_browser/mod.rs +++ /dev/null @@ -1,976 +0,0 @@ -pub mod error; -pub mod opts; -pub mod server; -pub mod signer; -pub mod state; - -mod app; -mod handlers; -mod queue; -mod router; -mod types; - -#[cfg(test)] -mod tests { - use std::time::Duration; - - use alloy_network::{Ethereum, Network, TransactionBuilder}; - use alloy_primitives::{Address, Bytes, TxHash, TxKind, U256, address}; - use axum::http::{HeaderMap, HeaderValue}; - use tokio::task::JoinHandle; - use uuid::Uuid; - - use crate::wallet_browser::{ - error::BrowserWalletError, - server::BrowserWalletServer, - types::{ - BrowserApiResponse, BrowserSignRequest, BrowserSignResponse, BrowserTransactionRequest, - BrowserTransactionResponse, Connection, SignRequest, SignType, - }, - }; - - const ALICE: Address = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); - const BOB: Address = address!("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"); - - const DEFAULT_TIMEOUT: Duration = Duration::from_secs(1); - const DEFAULT_DEVELOPMENT: bool = false; - - #[tokio::test] - async fn test_setup_server() { - let mut server = create_server::(); - let client = client_with_token(&server); - - // Check initial state - assert!(!server.is_connected().await); - assert!(!server.open_browser()); - assert!(server.timeout() == DEFAULT_TIMEOUT); - - // Start server - server.start().await.unwrap(); - - // Check that the transaction request queue is empty - check_transaction_request_queue_empty(&client, &server).await; - - // Stop server - server.stop().await.unwrap(); - } - - #[tokio::test] - async fn test_connect_disconnect_wallet() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - - // Check that the transaction request queue is empty - check_transaction_request_queue_empty(&client, &server).await; - - // Connect Alice's wallet - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Check connection state - let Connection { address, chain_id } = - server.get_connection().await.expect("expected an active wallet connection"); - assert_eq!(address, ALICE); - assert_eq!(chain_id, 1); - - // Disconnect wallet - disconnect_wallet(&client, &server).await; - - // Check disconnected state - assert!(!server.is_connected().await); - - // Connect Bob's wallet - connect_wallet(&client, &server, Connection::new(BOB, 42)).await; - - // Check connection state - let Connection { address, chain_id } = - server.get_connection().await.expect("expected an active wallet connection"); - assert_eq!(address, BOB); - assert_eq!(chain_id, 42); - - // Stop server - server.stop().await.unwrap(); - } - - #[tokio::test] - async fn test_switch_wallet() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - - // Connect Alice, assert connected - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - let Connection { address, chain_id } = - server.get_connection().await.expect("expected an active wallet connection"); - assert_eq!(address, ALICE); - assert_eq!(chain_id, 1); - - // Connect Bob, assert switched - connect_wallet(&client, &server, Connection::new(BOB, 42)).await; - let Connection { address, chain_id } = - server.get_connection().await.expect("expected an active wallet connection"); - assert_eq!(address, BOB); - assert_eq!(chain_id, 42); - - server.stop().await.unwrap(); - } - - #[tokio::test] - async fn test_transaction_response_both_hash_and_error_rejected() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Enqueue a tx - let (tx_request_id, tx_request) = create_browser_transaction_request(); - let _handle = wait_for_transaction_signing(&server, tx_request).await; - check_transaction_request_content(&client, &server, tx_request_id).await; - - // Wallet posts both hash and error -> should be rejected - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { - id: tx_request_id, - hash: Some(TxHash::random()), - error: Some("should not have both".into()), - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - assert_eq!(message, "Only one of hash or error can be provided"); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_transaction_response_neither_hash_nor_error_rejected() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (tx_request_id, tx_request) = create_browser_transaction_request(); - let _handle = wait_for_transaction_signing(&server, tx_request).await; - check_transaction_request_content(&client, &server, tx_request_id).await; - - // Neither hash nor error -> rejected - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { id: tx_request_id, hash: None, error: None }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - assert_eq!(message, "Either hash or error must be provided"); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_transaction_response_zero_hash_rejected() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (tx_request_id, tx_request) = create_browser_transaction_request(); - let _handle = wait_for_transaction_signing(&server, tx_request).await; - check_transaction_request_content(&client, &server, tx_request_id).await; - - // Zero hash -> rejected - let zero = TxHash::new([0u8; 32]); - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { id: tx_request_id, hash: Some(zero), error: None }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - // Message text per your handler; adjust if you use a different string. - assert!( - message.contains("Invalid") || message.contains("Malformed"), - "unexpected message: {message}" - ); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_send_transaction_client_accept() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (tx_request_id, tx_request) = create_browser_transaction_request(); - let handle = wait_for_transaction_signing(&server, tx_request).await; - check_transaction_request_content(&client, &server, tx_request_id).await; - - // Simulate the wallet accepting and signing the tx - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { - id: tx_request_id, - hash: Some(TxHash::random()), - error: None, - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp.status(), reqwest::StatusCode::OK); - - // The join handle should now return the tx hash - let res = handle.await.expect("task panicked"); - match res { - Ok(hash) => { - assert!(hash != TxHash::new([0; 32])); - } - other => panic!("expected success, got {other:?}"), - } - } - - #[tokio::test] - async fn test_send_transaction_client_not_requested() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Create a random transaction response without a matching request - let tx_request_id = Uuid::new_v4(); - - // Simulate the wallet sending a response for an unknown request - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { - id: tx_request_id, - hash: Some(TxHash::random()), - error: None, - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - assert_eq!(resp.status(), reqwest::StatusCode::OK); - - // Assert that no transaction without a matching request is accepted - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - assert_eq!(message, "Unknown transaction id"); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_send_transaction_invalid_response_format() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Simulate the wallet sending a response with an invalid UUID - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .body( - r#"{ - "id": "invalid-uuid", - "hash": "invalid-hash", - "error": null - }"#, - ) - .header("Content-Type", "application/json") - .send() - .await - .unwrap(); - - // The server should respond with a 422 Unprocessable Entity status - assert_eq!(resp.status(), reqwest::StatusCode::UNPROCESSABLE_ENTITY); - } - - #[tokio::test] - async fn test_send_transaction_client_reject() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Create a browser transaction request - let (tx_request_id, tx_request) = create_browser_transaction_request(); - - // Spawn the transaction signing flow in the background - let handle = wait_for_transaction_signing(&server, tx_request).await; - - // Check transaction request - check_transaction_request_content(&client, &server, tx_request_id).await; - - // Simulate the wallet rejecting the tx - let resp = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { - id: tx_request_id, - hash: None, - error: Some("User rejected the transaction".into()), - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp.status(), reqwest::StatusCode::OK); - - // The join handle should now return a rejection error - let res = handle.await.expect("task panicked"); - match res { - Err(BrowserWalletError::Rejected { operation, reason }) => { - assert_eq!(operation, "Transaction"); - assert_eq!(reason, "User rejected the transaction"); - } - other => panic!("expected rejection, got {other:?}"), - } - } - - #[tokio::test] - async fn test_send_multiple_transaction_requests() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Create multiple browser transaction requests - let (tx_request_id1, tx_request1) = create_browser_transaction_request(); - let (tx_request_id2, tx_request2) = create_different_browser_transaction_request(); - - // Spawn signing flows for both transactions concurrently - let handle1 = wait_for_transaction_signing(&server, tx_request1.clone()).await; - let handle2 = wait_for_transaction_signing(&server, tx_request2.clone()).await; - - // Check first transaction request - { - let resp = client - .get(format!("http://localhost:{}/api/transaction/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Ok(pending_tx) = resp - .json::>>() - .await - .unwrap() - else { - panic!("expected BrowserApiResponse::Ok with a pending transaction"); - }; - - assert_eq!( - pending_tx.id, tx_request_id1, - "expected the first transaction to be at the front of the queue" - ); - assert_eq!(pending_tx.request.from, tx_request1.request.from); - assert_eq!(pending_tx.request.to, tx_request1.request.to); - assert_eq!(pending_tx.request.value, tx_request1.request.value); - } - - // Simulate the wallet accepting and signing the first transaction - let resp1 = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { - id: tx_request_id1, - hash: Some(TxHash::random()), - error: None, - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp1.status(), reqwest::StatusCode::OK); - - let res1 = handle1.await.expect("first signing flow panicked"); - match res1 { - Ok(hash) => assert!(!hash.is_zero(), "first tx hash should not be zero"), - other => panic!("expected success, got {other:?}"), - } - - // Check second transaction request - { - let resp = client - .get(format!("http://localhost:{}/api/transaction/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Ok(pending_tx) = resp - .json::>>() - .await - .unwrap() - else { - panic!("expected BrowserApiResponse::Ok with a pending transaction"); - }; - - assert_eq!( - pending_tx.id, tx_request_id2, - "expected the second transaction to be pending after the first one completed" - ); - assert_eq!(pending_tx.request.from, tx_request2.request.from); - assert_eq!(pending_tx.request.to, tx_request2.request.to); - assert_eq!(pending_tx.request.value, tx_request2.request.value); - } - - // Simulate the wallet rejecting the second transaction - let resp2 = client - .post(format!("http://localhost:{}/api/transaction/response", server.port())) - .json(&BrowserTransactionResponse { - id: tx_request_id2, - hash: None, - error: Some("User rejected the transaction".into()), - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp2.status(), reqwest::StatusCode::OK); - - let res2 = handle2.await.expect("second signing flow panicked"); - match res2 { - Err(BrowserWalletError::Rejected { operation, reason }) => { - assert_eq!(operation, "Transaction"); - assert_eq!(reason, "User rejected the transaction"); - } - other => panic!("expected BrowserWalletError::Rejected, got {other:?}"), - } - - check_transaction_request_queue_empty(&client, &server).await; - - server.stop().await.unwrap(); - } - - #[tokio::test] - async fn test_send_sign_response_both_signature_and_error_rejected() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (sign_request_id, sign_request) = create_browser_sign_request(); - let _handle = wait_for_message_signing(&server, sign_request).await; - check_sign_request_content(&client, &server, sign_request_id).await; - - // Both signature and error -> should be rejected - let resp = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { - id: sign_request_id, - signature: Some(Bytes::from("Hello World")), - error: Some("Should not have both".into()), - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - assert_eq!(message, "Only one of signature or error can be provided"); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_send_sign_response_neither_hash_nor_error_rejected() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (sign_request_id, sign_request) = create_browser_sign_request(); - let _handle = wait_for_message_signing(&server, sign_request).await; - check_sign_request_content(&client, &server, sign_request_id).await; - - // Neither signature nor error -> rejected - let resp = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { id: sign_request_id, signature: None, error: None }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - assert_eq!(message, "Either signature or error must be provided"); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_send_sign_client_accept() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (sign_request_id, sign_request) = create_browser_sign_request(); - let handle = wait_for_message_signing(&server, sign_request).await; - check_sign_request_content(&client, &server, sign_request_id).await; - - // Simulate the wallet accepting and signing the message - let resp = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { - id: sign_request_id, - signature: Some(Bytes::from("FakeSignature")), - error: None, - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp.status(), reqwest::StatusCode::OK); - - // The join handle should now return the signature - let res = handle.await.expect("task panicked"); - match res { - Ok(signature) => { - assert_eq!(signature, Bytes::from("FakeSignature")); - } - other => panic!("expected success, got {other:?}"), - } - } - - #[tokio::test] - async fn test_send_sign_client_not_requested() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Create a random signing response without a matching request - let sign_request_id = Uuid::new_v4(); - - // Simulate the wallet sending a response for an unknown request - let resp = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { - id: sign_request_id, - signature: Some(Bytes::from("FakeSignature")), - error: None, - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - - assert_eq!(resp.status(), reqwest::StatusCode::OK); - - // Assert that no signing response without a matching request is accepted - let api: BrowserApiResponse<()> = resp.json().await.unwrap(); - match api { - BrowserApiResponse::Error { message } => { - assert_eq!(message, "Unknown signing request id"); - } - _ => panic!("expected error response"), - } - } - - #[tokio::test] - async fn test_send_sign_invalid_response_format() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Simulate the wallet sending a response with an invalid UUID - let resp = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .body( - r#"{ - "id": "invalid-uuid", - "signature": "invalid-signature", - "error": null - }"#, - ) - .header("Content-Type", "application/json") - .send() - .await - .unwrap(); - - // The server should respond with a 422 Unprocessable Entity status - assert_eq!(resp.status(), reqwest::StatusCode::UNPROCESSABLE_ENTITY); - } - - #[tokio::test] - async fn test_send_sign_client_reject() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - let (sign_request_id, sign_request) = create_browser_sign_request(); - let handle = wait_for_message_signing(&server, sign_request).await; - check_sign_request_content(&client, &server, sign_request_id).await; - - // Simulate the wallet rejecting the signing request - let resp = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { - id: sign_request_id, - signature: None, - error: Some("User rejected the signing request".into()), - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp.status(), reqwest::StatusCode::OK); - - // The join handle should now return a rejection error - let res = handle.await.expect("task panicked"); - match res { - Err(BrowserWalletError::Rejected { operation, reason }) => { - assert_eq!(operation, "Signing"); - assert_eq!(reason, "User rejected the signing request"); - } - other => panic!("expected rejection, got {other:?}"), - } - } - - #[tokio::test] - async fn test_send_multiple_sign_requests() { - let mut server = create_server::(); - let client = client_with_token(&server); - server.start().await.unwrap(); - connect_wallet(&client, &server, Connection::new(ALICE, 1)).await; - - // Create multiple browser sign requests - let (sign_request_id1, sign_request1) = create_browser_sign_request(); - let (sign_request_id2, sign_request2) = create_different_browser_sign_request(); - - // Spawn signing flows for both sign requests concurrently - let handle1 = wait_for_message_signing(&server, sign_request1.clone()).await; - let handle2 = wait_for_message_signing(&server, sign_request2.clone()).await; - - // Check first sign request - { - let resp = client - .get(format!("http://localhost:{}/api/signing/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Ok(pending_sign) = - resp.json::>().await.unwrap() - else { - panic!("expected BrowserApiResponse::Ok with a pending sign request"); - }; - - assert_eq!(pending_sign.id, sign_request_id1); - assert_eq!(pending_sign.sign_type, sign_request1.sign_type); - assert_eq!(pending_sign.request.address, sign_request1.request.address); - assert_eq!(pending_sign.request.message, sign_request1.request.message); - } - - // Simulate the wallet accepting and signing the first sign request - let resp1 = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { - id: sign_request_id1, - signature: Some(Bytes::from("Signature1")), - error: None, - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp1.status(), reqwest::StatusCode::OK); - - let res1 = handle1.await.expect("first signing flow panicked"); - match res1 { - Ok(signature) => assert_eq!(signature, Bytes::from("Signature1")), - other => panic!("expected success, got {other:?}"), - } - - // Check second sign request - { - let resp = client - .get(format!("http://localhost:{}/api/signing/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Ok(pending_sign) = - resp.json::>().await.unwrap() - else { - panic!("expected BrowserApiResponse::Ok with a pending sign request"); - }; - - assert_eq!(pending_sign.id, sign_request_id2,); - assert_eq!(pending_sign.sign_type, sign_request2.sign_type); - assert_eq!(pending_sign.request.address, sign_request2.request.address); - assert_eq!(pending_sign.request.message, sign_request2.request.message); - } - - // Simulate the wallet rejecting the second sign request - let resp2 = client - .post(format!("http://localhost:{}/api/signing/response", server.port())) - .json(&BrowserSignResponse { - id: sign_request_id2, - signature: None, - error: Some("User rejected the signing request".into()), - }) - .send() - .await - .unwrap() - .error_for_status() - .unwrap(); - assert_eq!(resp2.status(), reqwest::StatusCode::OK); - - let res2 = handle2.await.expect("second signing flow panicked"); - match res2 { - Err(BrowserWalletError::Rejected { operation, reason }) => { - assert_eq!(operation, "Signing"); - assert_eq!(reason, "User rejected the signing request"); - } - other => panic!("expected BrowserWalletError::Rejected, got {other:?}"), - } - - check_sign_request_queue_empty(&client, &server).await; - - server.stop().await.unwrap(); - } - - /// Helper to create a default browser wallet server. - fn create_server() -> BrowserWalletServer { - BrowserWalletServer::new(0, false, DEFAULT_TIMEOUT, DEFAULT_DEVELOPMENT) - } - - /// Helper to create a reqwest client with the session token header. - fn client_with_token(server: &BrowserWalletServer) -> reqwest::Client { - let mut headers = HeaderMap::new(); - headers.insert("X-Session-Token", HeaderValue::from_str(server.session_token()).unwrap()); - reqwest::Client::builder().default_headers(headers).build().unwrap() - } - - /// Helper to connect a wallet to the server. - async fn connect_wallet( - client: &reqwest::Client, - server: &BrowserWalletServer, - connection: Connection, - ) { - let resp = client - .post(format!("http://localhost:{}/api/connection", server.port())) - .json(&connection) - .send(); - assert!(resp.await.is_ok()); - } - - /// Helper to disconnect a wallet from the server. - async fn disconnect_wallet( - client: &reqwest::Client, - server: &BrowserWalletServer, - ) { - let resp = client - .post(format!("http://localhost:{}/api/connection", server.port())) - .json(&Option::::None) - .send(); - assert!(resp.await.is_ok()); - } - - /// Spawn the transaction signing flow in the background and return the join handle. - async fn wait_for_transaction_signing( - server: &BrowserWalletServer, - tx_request: BrowserTransactionRequest, - ) -> JoinHandle> { - // Spawn the signing flow in the background - let browser_server = server.clone(); - let join_handle = - tokio::spawn(async move { browser_server.request_transaction(tx_request).await }); - tokio::task::yield_now().await; - tokio::time::sleep(Duration::from_millis(100)).await; - - join_handle - } - - /// Spawn the message signing flow in the background and return the join handle. - async fn wait_for_message_signing( - server: &BrowserWalletServer, - sign_request: BrowserSignRequest, - ) -> JoinHandle> { - // Spawn the signing flow in the background - let browser_server = server.clone(); - let join_handle = - tokio::spawn(async move { browser_server.request_signing(sign_request).await }); - tokio::task::yield_now().await; - tokio::time::sleep(Duration::from_millis(100)).await; - - join_handle - } - - /// Create a simple browser transaction request. - fn create_browser_transaction_request() -> (Uuid, BrowserTransactionRequest) { - let id = Uuid::new_v4(); - let request = N::TransactionRequest::default() - .with_from(ALICE) - .with_to(BOB) - .with_value(U256::from(1000)); - let tx = BrowserTransactionRequest { id, request }; - (id, tx) - } - - /// Create a different browser transaction request (from the first one). - fn create_different_browser_transaction_request() - -> (Uuid, BrowserTransactionRequest) { - let id = Uuid::new_v4(); - let request = N::TransactionRequest::default() - .with_from(BOB) - .with_to(ALICE) - .with_value(U256::from(2000)); - let tx = BrowserTransactionRequest { id, request }; - (id, tx) - } - - /// Create a simple browser sign request. - fn create_browser_sign_request() -> (Uuid, BrowserSignRequest) { - let id = Uuid::new_v4(); - let req = BrowserSignRequest { - id, - sign_type: SignType::PersonalSign, - request: SignRequest { message: "Hello, world!".into(), address: ALICE }, - }; - (id, req) - } - - /// Create a different browser sign request (from the first one). - fn create_different_browser_sign_request() -> (Uuid, BrowserSignRequest) { - let id = Uuid::new_v4(); - let req = BrowserSignRequest { - id, - sign_type: SignType::SignTypedDataV4, - request: SignRequest { message: "Different message".into(), address: BOB }, - }; - (id, req) - } - - /// Check that the transaction request queue is empty, if not panic. - async fn check_transaction_request_queue_empty( - client: &reqwest::Client, - server: &BrowserWalletServer, - ) { - let resp = client - .get(format!("http://localhost:{}/api/transaction/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Error { message } = - resp.json::>>().await.unwrap() - else { - panic!("expected BrowserApiResponse::Error (no pending transaction), but got Ok"); - }; - - assert_eq!(message, "No pending transaction request"); - } - - /// Check that the transaction request matches the expected request ID and fields. - async fn check_transaction_request_content( - client: &reqwest::Client, - server: &BrowserWalletServer, - tx_request_id: Uuid, - ) { - let resp = client - .get(format!("http://localhost:{}/api/transaction/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Ok(pending_tx) = - resp.json::>>().await.unwrap() - else { - panic!("expected BrowserApiResponse::Ok with a pending transaction"); - }; - - assert_eq!(pending_tx.id, tx_request_id); - assert_eq!(pending_tx.request.from(), Some(ALICE)); - assert_eq!(pending_tx.request.kind(), Some(TxKind::Call(BOB))); - assert_eq!(pending_tx.request.value(), Some(U256::from(1000))); - } - - /// Check that the sign request queue is empty, if not panic. - async fn check_sign_request_queue_empty( - client: &reqwest::Client, - server: &BrowserWalletServer, - ) { - let resp = client - .get(format!("http://localhost:{}/api/signing/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Error { message } = - resp.json::>().await.unwrap() - else { - panic!("expected BrowserApiResponse::Error (no pending signing request), but got Ok"); - }; - - assert_eq!(message, "No pending signing request"); - } - - /// Check that the sign request matches the expected request ID and fields. - async fn check_sign_request_content( - client: &reqwest::Client, - server: &BrowserWalletServer, - sign_request_id: Uuid, - ) { - let resp = client - .get(format!("http://localhost:{}/api/signing/request", server.port())) - .send() - .await - .unwrap(); - - let BrowserApiResponse::Ok(pending_req) = - resp.json::>().await.unwrap() - else { - panic!("expected BrowserApiResponse::Ok with a pending signing request"); - }; - - assert_eq!(pending_req.id, sign_request_id); - assert_eq!(pending_req.sign_type, SignType::PersonalSign); - assert_eq!(pending_req.request.address, ALICE); - assert_eq!(pending_req.request.message, "Hello, world!"); - } -} diff --git a/crates/wallets/src/wallet_browser/opts.rs b/crates/wallets/src/wallet_browser/opts.rs deleted file mode 100644 index b8a30727115a5..0000000000000 --- a/crates/wallets/src/wallet_browser/opts.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::time::Duration; - -use alloy_network::Network; -use clap::Parser; -use eyre::Result; -use serde::Serialize; - -use crate::wallet_browser::signer::BrowserSigner; - -/// Browser wallet options -#[derive(Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "Wallet options - browser wallet")] -pub struct BrowserWalletOpts { - /// Use a browser wallet. - #[arg(long)] - pub browser: bool, - - /// Port for the browser wallet server. - #[arg(long, value_name = "PORT", default_value = "9545", requires = "browser")] - pub browser_port: u16, - - /// Whether to open the browser for wallet connection. - #[arg(long, default_value_t = false, requires = "browser")] - pub browser_disable_open: bool, - - /// Enable development mode for the browser wallet. - /// This relaxes certain security features for local development. - /// - /// **WARNING**: This should only be used in a development environment. - #[arg(long, hide = true)] - pub browser_development: bool, -} - -impl BrowserWalletOpts { - pub async fn run(&self) -> Result>> { - Ok(if self.browser { - Some( - BrowserSigner::new( - self.browser_port, - !self.browser_disable_open, - Duration::from_secs(300), - self.browser_development, - ) - .await?, - ) - } else { - None - }) - } -} diff --git a/crates/wallets/src/wallet_browser/queue.rs b/crates/wallets/src/wallet_browser/queue.rs deleted file mode 100644 index 5df44a09e9fde..0000000000000 --- a/crates/wallets/src/wallet_browser/queue.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::collections::{HashMap, VecDeque}; - -use alloy_network::Network; -use uuid::Uuid; - -use crate::wallet_browser::types::{BrowserSignRequest, BrowserTransactionRequest}; - -#[derive(Debug)] -pub(crate) struct RequestQueue { - /// Pending requests from CLI to browser - requests: VecDeque, - /// Responses from browser indexed by request ID - responses: HashMap, -} - -impl Default for RequestQueue { - fn default() -> Self { - Self::new() - } -} - -impl RequestQueue { - /// Create a new request queue. - pub fn new() -> Self { - Self { requests: VecDeque::new(), responses: HashMap::new() } - } - - /// Add a new request to the queue. - pub fn add_request(&mut self, request: Req) { - self.requests.push_back(request); - } - - /// Check if the queue contains any pending requests matching the given ID. - pub fn has_request(&self, id: &Uuid) -> bool - where - Req: HasId, - { - self.requests.iter().any(|r| r.id() == id) - } - - /// Read the next request from the queue without removing it. - pub fn read_request(&self) -> Option<&Req> { - self.requests.front() - } - - /// Remove a request by its ID. - pub fn remove_request(&mut self, id: &Uuid) -> Option - where - Req: HasId, - { - if let Some(pos) = self.requests.iter().position(|r| r.id() == id) { - self.requests.remove(pos) - } else { - None - } - } - - /// Add a response to the queue. - pub fn add_response(&mut self, id: Uuid, response: Res) { - self.responses.insert(id, response); - } - - /// Get a response by its ID, removing it from the queue. - pub fn get_response(&mut self, id: &Uuid) -> Option { - self.responses.remove(id) - } -} - -pub(crate) trait HasId { - fn id(&self) -> &Uuid; -} - -impl HasId for BrowserTransactionRequest { - fn id(&self) -> &Uuid { - &self.id - } -} - -impl HasId for BrowserSignRequest { - fn id(&self) -> &Uuid { - &self.id - } -} diff --git a/crates/wallets/src/wallet_browser/router.rs b/crates/wallets/src/wallet_browser/router.rs deleted file mode 100644 index cb29e9122a5d8..0000000000000 --- a/crates/wallets/src/wallet_browser/router.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::sync::Arc; - -use alloy_network::Network; -use axum::{ - Router, - extract::{Request, State}, - http::{HeaderValue, Method, StatusCode, header}, - middleware::{self, Next}, - response::Response, - routing::{get, post}, -}; -use tower::ServiceBuilder; -use tower_http::{cors::CorsLayer, set_header::SetResponseHeaderLayer}; - -use crate::wallet_browser::{handlers, state::BrowserWalletState}; - -pub async fn build_router(state: Arc>, port: u16) -> Router { - let api = Router::new() - .route("/transaction/request", get(handlers::get_next_transaction_request)) - .route("/transaction/response", post(handlers::post_transaction_response)) - .route("/signing/request", get(handlers::get_next_signing_request)) - .route("/signing/response", post(handlers::post_signing_response)) - .route("/connection", get(handlers::get_connection_info)) - .route("/connection", post(handlers::post_connection_update)) - .route_layer(middleware::from_fn_with_state(state.clone(), require_session_token)) - .with_state(state.clone()); - - let mut origins = vec![format!("http://127.0.0.1:{port}").parse().unwrap()]; - - // Allow default port of 5173 in development mode. - if state.is_development() { - origins.push("https://localhost:5173".to_string().parse().unwrap()); - } - - let security_headers = ServiceBuilder::new() - .layer(SetResponseHeaderLayer::if_not_present( - header::CONTENT_SECURITY_POLICY, - HeaderValue::from_static(concat!( - "default-src 'none'; ", - "object-src 'none'; ", - "base-uri 'none'; ", - "frame-ancestors 'none'; ", - "img-src 'self'; ", - "font-src 'none'; ", - "connect-src 'self' https: http: wss: ws:;", - "style-src 'self'; ", - "script-src 'self'; ", - "form-action 'none'; ", - "worker-src 'none'; ", - "frame-src https://id.porto.sh;" - )), - )) - .layer(SetResponseHeaderLayer::if_not_present( - header::REFERRER_POLICY, - HeaderValue::from_static("no-referrer"), - )) - .layer(SetResponseHeaderLayer::if_not_present( - header::X_CONTENT_TYPE_OPTIONS, - HeaderValue::from_static("nosniff"), - )) - .layer( - CorsLayer::new() - .allow_origin(origins) - .allow_methods([Method::GET, Method::POST, Method::OPTIONS]) - .allow_headers([header::CONTENT_TYPE]) - .allow_credentials(false), - ); - - Router::new() - .route("/", get(handlers::serve_index)) - .route("/styles.css", get(handlers::serve_css)) - .route("/main.js", get(handlers::serve_js)) - .route("/banner.png", get(handlers::serve_banner_png)) - .route("/logo.png", get(handlers::serve_logo_png)) - .nest("/api", api) - .layer(security_headers) - .with_state(state) -} - -async fn require_session_token( - State(state): State>>, - req: Request, - next: Next, -) -> Result { - if req.method() == Method::OPTIONS { - return Ok(next.run(req).await); - } - - // In development mode, skip session token check. - if state.is_development() { - return Ok(next.run(req).await); - } - - let expected = state.session_token(); - let provided = req - .headers() - .get("X-Session-Token") - .and_then(|v| v.to_str().ok()) - .ok_or(StatusCode::FORBIDDEN)?; - - if provided != expected { - return Err(StatusCode::FORBIDDEN); - } - - Ok(next.run(req).await) -} diff --git a/crates/wallets/src/wallet_browser/server.rs b/crates/wallets/src/wallet_browser/server.rs deleted file mode 100644 index 0361416640c3a..0000000000000 --- a/crates/wallets/src/wallet_browser/server.rs +++ /dev/null @@ -1,218 +0,0 @@ -use std::{ - net::SocketAddr, - sync::Arc, - time::{Duration, Instant}, -}; - -use alloy_dyn_abi::TypedData; -use alloy_network::Network; -use alloy_primitives::{Address, Bytes, TxHash}; -use tokio::{ - net::TcpListener, - sync::{Mutex, oneshot}, -}; -use uuid::Uuid; - -use crate::wallet_browser::{ - error::BrowserWalletError, - router::build_router, - state::BrowserWalletState, - types::{ - BrowserSignRequest, BrowserSignTypedDataRequest, BrowserTransactionRequest, Connection, - SignRequest, SignType, - }, -}; - -/// Browser wallet server. -#[derive(Debug, Clone)] -pub struct BrowserWalletServer { - port: u16, - state: Arc>, - shutdown_tx: Option>>>>, - open_browser: bool, - timeout: Duration, -} - -impl BrowserWalletServer { - /// Create a new browser wallet server. - pub fn new(port: u16, open_browser: bool, timeout: Duration, development: bool) -> Self { - Self { - port, - state: Arc::new(BrowserWalletState::new(Uuid::new_v4().to_string(), development)), - shutdown_tx: None, - open_browser, - timeout, - } - } - - /// Start the server and open browser. - pub async fn start(&mut self) -> Result<(), BrowserWalletError> { - let router = build_router(self.state.clone(), self.port).await; - - let addr = SocketAddr::from(([127, 0, 0, 1], self.port)); - let listener = TcpListener::bind(addr) - .await - .map_err(|e| BrowserWalletError::ServerError(e.to_string()))?; - self.port = listener.local_addr().unwrap().port(); - - let (shutdown_tx, shutdown_rx) = oneshot::channel(); - self.shutdown_tx = Some(Arc::new(Mutex::new(Some(shutdown_tx)))); - - tokio::spawn(async move { - let server = axum::serve(listener, router); - let _ = server - .with_graceful_shutdown(async { - let _ = shutdown_rx.await; - }) - .await; - }); - - if self.open_browser { - webbrowser::open(&format!("http://127.0.0.1:{}", self.port)).map_err(|e| { - BrowserWalletError::ServerError(format!("Failed to open browser: {e}")) - })?; - } - - Ok(()) - } - - /// Stop the server. - pub async fn stop(&mut self) -> Result<(), BrowserWalletError> { - if let Some(shutdown_arc) = self.shutdown_tx.take() - && let Some(tx) = shutdown_arc.lock().await.take() - { - let _ = tx.send(()); - } - Ok(()) - } - - /// Get the server port. - pub const fn port(&self) -> u16 { - self.port - } - - /// Check if the browser should be opened. - pub const fn open_browser(&self) -> bool { - self.open_browser - } - - /// Get the timeout duration. - pub const fn timeout(&self) -> Duration { - self.timeout - } - - /// Get the session token. - pub fn session_token(&self) -> &str { - self.state.session_token() - } - - /// Check if a wallet is connected. - pub async fn is_connected(&self) -> bool { - self.state.is_connected().await - } - - /// Get current wallet connection. - pub async fn get_connection(&self) -> Option { - self.state.get_connection().await - } - - /// Request a transaction to be signed and sent via the browser wallet. - pub async fn request_transaction( - &self, - request: BrowserTransactionRequest, - ) -> Result { - if !self.is_connected().await { - return Err(BrowserWalletError::NotConnected); - } - - let tx_id = request.id; - - self.state.add_transaction_request(request).await; - - let start = Instant::now(); - - loop { - if let Some(response) = self.state.get_transaction_response(&tx_id).await { - if let Some(hash) = response.hash { - return Ok(hash); - } else if let Some(error) = response.error { - return Err(BrowserWalletError::Rejected { - operation: "Transaction", - reason: error, - }); - } - return Err(BrowserWalletError::ServerError( - "Transaction response missing both hash and error".to_string(), - )); - } - - if start.elapsed() > self.timeout { - self.state.remove_transaction_request(&tx_id).await; - return Err(BrowserWalletError::Timeout { operation: "Transaction" }); - } - - tokio::time::sleep(Duration::from_millis(100)).await; - } - } - - /// Request a message to be signed via the browser wallet. - pub async fn request_signing( - &self, - request: BrowserSignRequest, - ) -> Result { - if !self.is_connected().await { - return Err(BrowserWalletError::NotConnected); - } - - let tx_id = request.id; - - self.state.add_signing_request(request).await; - - let start = Instant::now(); - - loop { - if let Some(response) = self.state.get_signing_response(&tx_id).await { - if let Some(signature) = response.signature { - return Ok(signature); - } else if let Some(error) = response.error { - return Err(BrowserWalletError::Rejected { - operation: "Signing", - reason: error, - }); - } - return Err(BrowserWalletError::ServerError( - "Signing response missing both signature and error".to_string(), - )); - } - - if start.elapsed() > self.timeout { - self.state.remove_signing_request(&tx_id).await; - return Err(BrowserWalletError::Timeout { operation: "Signing" }); - } - - tokio::time::sleep(Duration::from_millis(100)).await; - } - } - - /// Request EIP-712 typed data signing via the browser wallet. - pub async fn request_typed_data_signing( - &self, - address: Address, - typed_data: TypedData, - ) -> Result { - let request = BrowserSignTypedDataRequest { id: Uuid::new_v4(), address, typed_data }; - - let sign_request = BrowserSignRequest { - id: request.id, - sign_type: SignType::SignTypedDataV4, - request: SignRequest { - message: serde_json::to_string(&request.typed_data).map_err(|e| { - BrowserWalletError::ServerError(format!("Failed to serialize typed data: {e}")) - })?, - address: request.address, - }, - }; - - self.request_signing(sign_request).await - } -} diff --git a/crates/wallets/src/wallet_browser/signer.rs b/crates/wallets/src/wallet_browser/signer.rs deleted file mode 100644 index 55f3bad5a4394..0000000000000 --- a/crates/wallets/src/wallet_browser/signer.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; - -use alloy_network::{Network, TransactionBuilder}; -use alloy_primitives::{Address, B256, ChainId}; -use alloy_signer::Result; -use tokio::sync::Mutex; -use uuid::Uuid; - -use crate::wallet_browser::{ - server::BrowserWalletServer, - types::{BrowserTransactionRequest, Connection}, -}; - -#[derive(Clone, Debug)] -pub struct BrowserSigner { - server: Arc>>, - address: Address, - chain_id: ChainId, -} - -impl BrowserSigner { - pub async fn new( - port: u16, - open_browser: bool, - timeout: Duration, - development: bool, - ) -> Result { - let mut server = BrowserWalletServer::new(port, open_browser, timeout, development); - - server.start().await.map_err(alloy_signer::Error::other)?; - - let _ = sh_warn!("Browser wallet is still in early development. Use with caution!"); - let _ = sh_println!("Opening browser for wallet connection..."); - let _ = sh_println!("Waiting for wallet connection..."); - - let start = Instant::now(); - - loop { - if let Some(Connection { address, chain_id }) = server.get_connection().await { - let _ = sh_println!("Wallet connected: {}", address); - let _ = sh_println!("Chain ID: {}", chain_id); - - return Ok(Self { server: Arc::new(Mutex::new(server)), address, chain_id }); - } - - if start.elapsed() > timeout { - return Err(alloy_signer::Error::other("Wallet connection timeout")); - } - - tokio::time::sleep(Duration::from_secs(1)).await; - } - } - - /// Send a transaction through the browser wallet. - pub async fn send_transaction_via_browser( - &self, - tx_request: N::TransactionRequest, - ) -> Result { - if let Some(from) = tx_request.from() - && from != self.address - { - return Err(alloy_signer::Error::other( - "Transaction `from` address does not match connected wallet address", - )); - } - - if let Some(chain_id) = tx_request.chain_id() - && chain_id != self.chain_id - { - return Err(alloy_signer::Error::other( - "Transaction `chainId` does not match connected wallet chain ID", - )); - } - - let request = BrowserTransactionRequest { id: Uuid::new_v4(), request: tx_request }; - - let server = self.server.lock().await; - let tx_hash = - server.request_transaction(request).await.map_err(alloy_signer::Error::other)?; - - tokio::time::sleep(Duration::from_millis(500)).await; - - Ok(tx_hash) - } - - pub const fn address(&self) -> Address { - self.address - } -} - -impl Drop for BrowserSigner { - fn drop(&mut self) { - let server = self.server.clone(); - - tokio::spawn(async move { - let mut server = server.lock().await; - let _ = server.stop().await; - }); - } -} diff --git a/crates/wallets/src/wallet_browser/state.rs b/crates/wallets/src/wallet_browser/state.rs deleted file mode 100644 index c7c9859ebdcf4..0000000000000 --- a/crates/wallets/src/wallet_browser/state.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::sync::Arc; - -use alloy_network::Network; -use tokio::sync::{Mutex, RwLock}; -use uuid::Uuid; - -use crate::wallet_browser::{ - queue::RequestQueue, - types::{ - BrowserSignRequest, BrowserSignResponse, BrowserTransactionRequest, - BrowserTransactionResponse, Connection, - }, -}; - -#[derive(Debug, Clone)] -pub(crate) struct BrowserWalletState { - /// Current information about the wallet connection. - connection: Arc>>, - /// Request/response queue for transactions. - transactions: - Arc, BrowserTransactionResponse>>>, - /// Request/response queue for signings. - signings: Arc>>, - /// Unique session token for the wallet browser instance. - /// The CSP on the served page prevents this token from being loaded by other origins. - session_token: String, - /// If true, the server is running in development mode. - /// This relaxes certain security restrictions for local development. - /// - /// **WARNING**: This should only be used in a development environment. - development: bool, -} - -impl BrowserWalletState { - /// Create a new browser wallet state. - pub fn new(session_token: String, development: bool) -> Self { - Self { - connection: Arc::new(RwLock::new(None)), - transactions: Arc::new(Mutex::new(RequestQueue::new())), - signings: Arc::new(Mutex::new(RequestQueue::new())), - session_token, - development, - } - } - - /// Get the session token. - pub fn session_token(&self) -> &str { - &self.session_token - } - - /// Check if in development mode. - /// This relaxes certain security restrictions for local development. - /// - /// **WARNING**: This should only be used in a development environment. - pub const fn is_development(&self) -> bool { - self.development - } - - /// Check if wallet is connected. - pub async fn is_connected(&self) -> bool { - self.connection.read().await.is_some() - } - - /// Get current connection information. - pub async fn get_connection(&self) -> Option { - *self.connection.read().await - } - - /// Set connection information. - pub async fn set_connection(&self, connection: Option) { - *self.connection.write().await = connection; - } - - /// Add a transaction request. - pub async fn add_transaction_request(&self, request: BrowserTransactionRequest) { - self.transactions.lock().await.add_request(request); - } - - /// Check if a transaction request exists. - pub async fn has_transaction_request(&self, id: &Uuid) -> bool { - self.transactions.lock().await.has_request(id) - } - - /// Read the next transaction request. - pub async fn read_next_transaction_request(&self) -> Option> { - self.transactions.lock().await.read_request().cloned() - } - - // Remove a transaction request. - pub async fn remove_transaction_request(&self, id: &Uuid) { - self.transactions.lock().await.remove_request(id); - } - - /// Add transaction response. - pub async fn add_transaction_response(&self, response: BrowserTransactionResponse) { - let id = response.id; - let mut transactions = self.transactions.lock().await; - transactions.add_response(id, response); - transactions.remove_request(&id); - } - - /// Get transaction response, removing it from the queue. - pub async fn get_transaction_response(&self, id: &Uuid) -> Option { - self.transactions.lock().await.get_response(id) - } - - /// Add a signing request. - pub async fn add_signing_request(&self, request: BrowserSignRequest) { - self.signings.lock().await.add_request(request); - } - - /// Check if a signing request exists. - pub async fn has_signing_request(&self, id: &Uuid) -> bool { - self.signings.lock().await.has_request(id) - } - - /// Read the next signing request. - pub async fn read_next_signing_request(&self) -> Option { - self.signings.lock().await.read_request().cloned() - } - - /// Remove a signing request. - pub async fn remove_signing_request(&self, id: &Uuid) { - self.signings.lock().await.remove_request(id); - } - - /// Add signing response. - pub async fn add_signing_response(&self, response: BrowserSignResponse) { - let id = response.id; - let mut signings = self.signings.lock().await; - signings.add_response(id, response); - signings.remove_request(&id); - } - - /// Get signing response, removing it from the queue. - pub async fn get_signing_response(&self, id: &Uuid) -> Option { - self.signings.lock().await.get_response(id) - } -} diff --git a/crates/wallets/src/wallet_browser/types.rs b/crates/wallets/src/wallet_browser/types.rs deleted file mode 100644 index 3e37498bb1e19..0000000000000 --- a/crates/wallets/src/wallet_browser/types.rs +++ /dev/null @@ -1,127 +0,0 @@ -use alloy_dyn_abi::TypedData; -use alloy_network::Network; -use alloy_primitives::{Address, Bytes, ChainId, TxHash}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -/// Response format for API endpoints. -/// - `Ok(T)` serializes as: {"status":"ok","data": ...} -/// - `Ok(())` serializes as: {"status":"ok"} (no data key) -/// - `Error { message }` as: {"status":"error","message":"..."} -#[derive(Serialize, Deserialize, Debug)] -#[serde(tag = "status", content = "data", rename_all = "lowercase")] -pub(crate) enum BrowserApiResponse { - Ok(T), - Error { message: String }, -} - -impl BrowserApiResponse<()> { - /// Create a successful response with no data. - pub const fn ok() -> Self { - Self::Ok(()) - } -} - -impl BrowserApiResponse { - /// Create a successful response with the given data. - pub const fn with_data(data: T) -> Self { - Self::Ok(data) - } - - /// Create an error response with the given message. - pub fn error(msg: impl Into) -> Self { - Self::Error { message: msg.into() } - } -} - -/// Represents a transaction request sent to the browser wallet. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct BrowserTransactionRequest { - /// The unique identifier for the transaction. - pub id: Uuid, - /// The transaction request details. - pub request: N::TransactionRequest, -} - -/// Represents a transaction response sent from the browser wallet. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub(crate) struct BrowserTransactionResponse { - /// The unique identifier for the transaction, must match the request ID sent earlier. - pub id: Uuid, - /// The transaction hash if the transaction was successful. - pub hash: Option, - /// The error message if the transaction failed. - pub error: Option, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub enum SignType { - /// Standard personal sign: `eth_sign` / `personal_sign` - PersonalSign, - /// EIP-712 typed data sign: `eth_signTypedData_v4` - SignTypedDataV4, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct SignRequest { - /// The message to be signed. - pub message: String, - /// The address that should sign the message. - pub address: Address, -} - -/// Represents a signing request sent to the browser wallet. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "camelCase")] -pub struct BrowserSignRequest { - /// The unique identifier for the signing request. - pub id: Uuid, - /// The type of signing operation. - pub sign_type: SignType, - /// The sign request details. - pub request: SignRequest, -} - -/// Represents a typed data signing request sent to the browser wallet. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields, rename_all = "camelCase")] -pub struct BrowserSignTypedDataRequest { - /// The unique identifier for the signing request. - pub id: Uuid, - /// The address that should sign the typed data. - pub address: Address, - /// The typed data to be signed. - pub typed_data: TypedData, -} - -/// Represents a signing response sent from the browser wallet. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub(crate) struct BrowserSignResponse { - /// The unique identifier for the signing request, must match the request ID sent earlier. - pub id: Uuid, - /// The signature if the signing was successful. - pub signature: Option, - /// The error message if the signing failed. - pub error: Option, -} - -/// Represents an active connection to a browser wallet. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct Connection { - /// The address of the connected wallet. - pub address: Address, - /// The chain ID of the connected wallet. - pub chain_id: ChainId, -} - -impl Connection { - /// Create a new connection instance. - pub const fn new(address: Address, chain_id: ChainId) -> Self { - Self { address, chain_id } - } -} diff --git a/crates/wallets/src/wallet_multi/mod.rs b/crates/wallets/src/wallet_multi/mod.rs deleted file mode 100644 index a6d85581e19f0..0000000000000 --- a/crates/wallets/src/wallet_multi/mod.rs +++ /dev/null @@ -1,621 +0,0 @@ -use crate::{ - BrowserWalletOpts, - signer::{PendingSigner, WalletSigner}, - utils, - wallet_browser::signer::BrowserSigner, -}; -use alloy_network::Network; -use alloy_primitives::map::AddressHashMap; -use alloy_signer::Signer; -use clap::Parser; -use derive_builder::Builder; -use eyre::Result; -use foundry_config::Config; -use serde::Serialize; -use std::path::PathBuf; - -/// Container for multiple wallets. -#[derive(Debug, Default)] -pub struct MultiWallet { - /// Vector of wallets that require an action to be unlocked. - /// Those are lazily unlocked on the first access of the signers. - pending_signers: Vec, - /// Contains unlocked signers. - signers: AddressHashMap, -} - -impl MultiWallet { - pub fn new(pending_signers: Vec, signers: Vec) -> Self { - 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(), signer); - } - Ok(()) - } - - pub fn signers(&mut self) -> Result<&AddressHashMap> { - self.maybe_unlock_pending()?; - Ok(&self.signers) - } - - pub fn into_signers(mut self) -> Result> { - self.maybe_unlock_pending()?; - Ok(self.signers) - } - - pub fn add_signer(&mut self, signer: WalletSigner) { - self.signers.insert(signer.address(), signer); - } -} - -/// A macro that initializes multiple wallets -/// -/// Should be used with a [`MultiWallet`] instance -macro_rules! create_hw_wallets { - ($self:ident, $create_signer:expr, $signers:ident) => { - let mut $signers = vec![]; - - if let Some(hd_paths) = &$self.hd_paths { - for path in hd_paths { - let hw = $create_signer(Some(path), 0).await?; - $signers.push(hw); - } - } - - if let Some(mnemonic_indexes) = &$self.mnemonic_indexes { - for index in mnemonic_indexes { - let hw = $create_signer(None, *index).await?; - $signers.push(hw); - } - } - - if $signers.is_empty() { - let hw = $create_signer(None, 0).await?; - $signers.push(hw); - } - }; -} - -/// The wallet options can either be: -/// 1. Ledger -/// 2. Trezor -/// 3. Mnemonics (via file path) -/// 4. Keystores (via file path) -/// 5. Private Keys (cleartext in CLI) -/// 6. Private Keys (interactively via secure prompt) -/// 7. AWS KMS -/// 8. Turnkey -#[derive(Builder, Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "Wallet options", about = None, long_about = None)] -pub struct MultiWalletOpts { - /// Open an interactive prompt to enter your private key. - /// - /// Takes a value for the number of keys to enter. - #[arg(long, help_heading = "Wallet options - raw", default_value = "0", value_name = "NUM")] - pub interactives: u32, - - /// Open an interactive prompt to enter your private key. - #[arg(long, short, help_heading = "Wallet options - raw", conflicts_with = "interactives")] - pub interactive: bool, - - /// Use the provided 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. - #[arg( - long, - help_heading = "Wallet options - raw", - conflicts_with = "private_keys", - value_name = "RAW_PRIVATE_KEY" - )] - #[builder(default = "None")] - pub private_key: Option, - - /// Use the mnemonic phrases of mnemonic files at the specified paths. - #[arg(long, alias = "mnemonic-paths", help_heading = "Wallet options - raw")] - #[builder(default = "None")] - pub mnemonics: Option>, - - /// Use a BIP39 passphrases for the mnemonic. - #[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. - #[arg( - long = "mnemonic-derivation-paths", - alias = "hd-paths", - help_heading = "Wallet options - raw", - value_name = "PATH" - )] - #[builder(default = "None")] - pub hd_paths: Option>, - - /// Use the private key from the given mnemonic index. - /// - /// Can be used with --mnemonics, --ledger, --aws and --trezor. - #[arg( - long, - conflicts_with = "hd_paths", - help_heading = "Wallet options - raw", - default_value = "0", - value_name = "INDEXES" - )] - pub mnemonic_indexes: Option>, - - /// Use the keystore by its filename in the given folder. - #[arg( - long = "keystore", - visible_alias = "keystores", - help_heading = "Wallet options - keystore", - value_name = "PATHS", - env = "ETH_KEYSTORE" - )] - #[builder(default = "None")] - pub keystore_paths: Option>, - - /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename. - #[arg( - long = "account", - visible_alias = "accounts", - help_heading = "Wallet options - keystore", - value_name = "ACCOUNT_NAMES", - env = "ETH_KEYSTORE_ACCOUNT", - conflicts_with = "keystore_paths" - )] - #[builder(default = "None")] - pub keystore_account_names: Option>, - - /// The keystore password. - /// - /// Used with --keystore. - #[arg( - long = "password", - help_heading = "Wallet options - keystore", - requires = "keystore_paths", - value_name = "PASSWORDS" - )] - #[builder(default = "None")] - pub keystore_passwords: Option>, - - /// The keystore password file path. - /// - /// Used with --keystore. - #[arg( - long = "password-file", - help_heading = "Wallet options - keystore", - requires = "keystore_paths", - value_name = "PATHS", - env = "ETH_PASSWORD" - )] - #[builder(default = "None")] - pub keystore_password_files: Option>, - - /// Use a Ledger hardware wallet. - #[arg(long, short, help_heading = "Wallet options - hardware wallet")] - pub ledger: bool, - - /// Use a Trezor hardware wallet. - #[arg(long, short, help_heading = "Wallet options - hardware wallet")] - pub trezor: bool, - - /// Use AWS Key Management Service. - /// - /// Ensure either one of AWS_KMS_KEY_IDS (comma-separated) or AWS_KMS_KEY_ID environment - /// variables are set. - #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "aws-kms"))] - pub aws: bool, - - /// Use Google Cloud Key Management Service. - /// - /// Ensure the following environment variables are set: GCP_PROJECT_ID, GCP_LOCATION, - /// GCP_KEY_RING, GCP_KEY_NAME, GCP_KEY_VERSION. - /// - /// See: - #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "gcp-kms"))] - pub gcp: bool, - - /// Use Turnkey. - /// - /// Ensure the following environment variables are set: TURNKEY_API_PRIVATE_KEY, - /// TURNKEY_ORGANIZATION_ID, TURNKEY_ADDRESS. - /// - /// See: - #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "turnkey"))] - pub turnkey: bool, - - /// Browser wallet options - #[command(flatten)] - pub browser: BrowserWalletOpts, -} - -impl MultiWalletOpts { - /// Returns [MultiWallet] container configured with provided options. - pub async fn get_multi_wallet(&self) -> Result { - let mut pending = Vec::new(); - let mut signers: Vec = Vec::new(); - - if let Some(ledgers) = self.ledgers().await? { - signers.extend(ledgers); - } - if let Some(trezors) = self.trezors().await? { - signers.extend(trezors); - } - if let Some(aws_signers) = self.aws_signers().await? { - signers.extend(aws_signers); - } - if let Some(gcp_signer) = self.gcp_signers().await? { - signers.extend(gcp_signer); - } - if let Some(turnkey_signers) = self.turnkey_signers()? { - signers.extend(turnkey_signers); - } - if let Some((pending_keystores, unlocked)) = self.keystores()? { - pending.extend(pending_keystores); - signers.extend(unlocked); - } - if let Some(pks) = self.private_keys()? { - signers.extend(pks); - } - if let Some(mnemonics) = self.mnemonics()? { - signers.extend(mnemonics); - } - if self.interactive { - pending.push(PendingSigner::Interactive); - } - if self.interactives > 0 { - pending.extend(std::iter::repeat_n( - PendingSigner::Interactive, - self.interactives as usize, - )); - } - - Ok(MultiWallet::new(pending, signers)) - } - - pub fn private_keys(&self) -> Result>> { - let mut pks = vec![]; - if let Some(private_key) = &self.private_key { - pks.push(private_key); - } - if let Some(private_keys) = &self.private_keys { - for pk in private_keys { - pks.push(pk); - } - } - if pks.is_empty() { - Ok(None) - } else { - let wallets = pks - .into_iter() - .map(|pk| utils::create_private_key_signer(pk)) - .collect::>>()?; - Ok(Some(wallets)) - } - } - - fn keystore_paths(&self) -> Result>> { - if let Some(keystore_paths) = &self.keystore_paths { - return Ok(Some(keystore_paths.iter().map(PathBuf::from).collect())); - } - if let Some(keystore_account_names) = &self.keystore_account_names { - let default_keystore_dir = Config::foundry_keystores_dir() - .ok_or_else(|| eyre::eyre!("Could not find the default keystore directory."))?; - return Ok(Some( - keystore_account_names - .iter() - .map(|keystore_name| default_keystore_dir.join(keystore_name)) - .collect(), - )); - } - Ok(None) - } - - /// Returns all wallets read from the provided keystores arguments - /// - /// Returns `Ok(None)` if no keystore provided. - pub fn keystores(&self) -> Result, Vec)>> { - if let Some(keystore_paths) = self.keystore_paths()? { - let mut pending = Vec::new(); - let mut signers = Vec::new(); - - let mut passwords_iter = - self.keystore_passwords.iter().flat_map(|passwords| passwords.iter()); - - let mut password_files_iter = self - .keystore_password_files - .iter() - .flat_map(|password_files| password_files.iter()); - - for path in &keystore_paths { - let (maybe_signer, maybe_pending) = utils::create_keystore_signer( - path, - passwords_iter.next().map(|password| password.as_str()), - password_files_iter.next().map(|password_file| password_file.as_str()), - )?; - if let Some(pending_signer) = maybe_pending { - pending.push(pending_signer); - } else if let Some(signer) = maybe_signer { - signers.push(signer); - } - } - return Ok(Some((pending, signers))); - } - Ok(None) - } - - pub fn mnemonics(&self) -> Result>> { - if let Some(ref mnemonics) = self.mnemonics { - let mut wallets = vec![]; - - let mut hd_paths_iter = - self.hd_paths.iter().flat_map(|paths| paths.iter().map(String::as_str)); - - let mut passphrases_iter = self - .mnemonic_passphrases - .iter() - .flat_map(|passphrases| passphrases.iter().map(String::as_str)); - - let mut indexes_iter = - self.mnemonic_indexes.iter().flat_map(|indexes| indexes.iter().copied()); - - for mnemonic in mnemonics { - let wallet = utils::create_mnemonic_signer( - mnemonic, - passphrases_iter.next(), - hd_paths_iter.next(), - indexes_iter.next().unwrap_or(0), - )?; - wallets.push(wallet); - } - return Ok(Some(wallets)); - } - Ok(None) - } - - pub async fn ledgers(&self) -> Result>> { - if self.ledger { - let mut args = self.clone(); - - if let Some(paths) = &args.hd_paths { - if paths.len() > 1 { - eyre::bail!("Ledger only supports one signer."); - } - args.mnemonic_indexes = None; - } - - create_hw_wallets!(args, utils::create_ledger_signer, wallets); - return Ok(Some(wallets)); - } - Ok(None) - } - - pub async fn trezors(&self) -> Result>> { - if self.trezor { - let mut args = self.clone(); - - if args.hd_paths.is_some() { - args.mnemonic_indexes = None; - } - - create_hw_wallets!(args, utils::create_trezor_signer, wallets); - return Ok(Some(wallets)); - } - Ok(None) - } - - 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") - .or(std::env::var("AWS_KMS_KEY_ID"))? - .split(',') - .map(|k| k.to_string()) - .collect::>(); - - for key in aws_keys { - let aws_signer = WalletSigner::from_aws(key).await?; - wallets.push(aws_signer) - } - - return Ok(Some(wallets)); - } - - Ok(None) - } - - /// Returns a list of GCP signers if the GCP flag is set. - /// - /// The GCP signers are created from the following environment variables: - /// - GCP_PROJECT_ID: The GCP project ID. e.g. `my-project-123456`. - /// - GCP_LOCATION: The GCP location. e.g. `us-central1`. - /// - GCP_KEY_RING: The GCP key ring name. e.g. `my-key-ring`. - /// - GCP_KEY_NAME: The GCP key name. e.g. `my-key`. - /// - GCP_KEY_VERSION: The GCP key version. e.g. `1`. - /// - /// For more information on GCP KMS, see the [official documentation](https://cloud.google.com/kms/docs). - pub async fn gcp_signers(&self) -> Result>> { - #[cfg(feature = "gcp-kms")] - if self.gcp { - let mut wallets = vec![]; - - let project_id = std::env::var("GCP_PROJECT_ID")?; - let location = std::env::var("GCP_LOCATION")?; - let key_ring = std::env::var("GCP_KEY_RING")?; - let key_name = std::env::var("GCP_KEY_NAME")?; - let key_version = std::env::var("GCP_KEY_VERSION")?; - - let gcp_signer = WalletSigner::from_gcp( - project_id, - location, - key_ring, - key_name, - key_version.parse()?, - ) - .await?; - wallets.push(gcp_signer); - - return Ok(Some(wallets)); - } - - Ok(None) - } - - pub fn turnkey_signers(&self) -> Result>> { - #[cfg(feature = "turnkey")] - if self.turnkey { - let api_private_key = std::env::var("TURNKEY_API_PRIVATE_KEY")?; - let organization_id = std::env::var("TURNKEY_ORGANIZATION_ID")?; - let address = std::env::var("TURNKEY_ADDRESS")?.parse()?; - - let signer = WalletSigner::from_turnkey(api_private_key, organization_id, address)?; - return Ok(Some(vec![signer])); - } - - Ok(None) - } - - /// Returns the Turnkey address if `--turnkey` flag is set and `TURNKEY_ADDRESS` is available. - pub fn turnkey_address(&self) -> Option { - #[cfg(feature = "turnkey")] - if self.turnkey { - return std::env::var("TURNKEY_ADDRESS").ok().and_then(|addr| addr.parse().ok()); - } - - None - } - - /// Launches and returns the Browser signer if `--browser` flag is set - pub async fn browser_signer(&self) -> Result>> { - self.browser.run().await - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::address; - use std::path::Path; - - #[test] - fn parse_keystore_args() { - let args: MultiWalletOpts = - MultiWalletOpts::parse_from(["foundry-cli", "--keystores", "my/keystore/path"]); - assert_eq!(args.keystore_paths, Some(vec!["my/keystore/path".to_string()])); - - unsafe { - std::env::set_var("ETH_KEYSTORE", "MY_KEYSTORE"); - } - let args: MultiWalletOpts = MultiWalletOpts::parse_from(["foundry-cli"]); - assert_eq!(args.keystore_paths, Some(vec!["MY_KEYSTORE".to_string()])); - - unsafe { - std::env::remove_var("ETH_KEYSTORE"); - } - } - - #[test] - fn parse_keystore_password_file() { - let keystore = - Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../cast/tests/fixtures/keystore")); - let keystore_file = keystore - .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); - - let keystore_password_file = keystore.join("password-ec554").into_os_string(); - - let args: MultiWalletOpts = MultiWalletOpts::parse_from([ - "foundry-cli", - "--keystores", - keystore_file.to_str().unwrap(), - "--password-file", - keystore_password_file.to_str().unwrap(), - ]); - assert_eq!( - args.keystore_password_files, - Some(vec![keystore_password_file.to_str().unwrap().to_string()]) - ); - - let (_, unlocked) = args.keystores().unwrap().unwrap(); - assert_eq!(unlocked.len(), 1); - assert_eq!(unlocked[0].address(), address!("0xec554aeafe75601aaab43bd4621a22284db566c2")); - } - - // https://github.com/foundry-rs/foundry/issues/12916 - #[test] - #[cfg(feature = "turnkey")] - fn turnkey_address_returns_address_when_flag_set() { - let args: MultiWalletOpts = MultiWalletOpts::parse_from(["foundry-cli", "--turnkey"]); - assert!(args.turnkey); - - unsafe { - std::env::set_var("TURNKEY_ADDRESS", "0x1234567890123456789012345678901234567890"); - } - - let addr = args.turnkey_address(); - assert_eq!(addr, Some(address!("0x1234567890123456789012345678901234567890"))); - - unsafe { - std::env::remove_var("TURNKEY_ADDRESS"); - } - } - - #[test] - fn turnkey_address_returns_none_when_flag_not_set() { - let args: MultiWalletOpts = MultiWalletOpts::parse_from(["foundry-cli"]); - assert!(!args.turnkey); - - unsafe { - std::env::set_var("TURNKEY_ADDRESS", "0x1234567890123456789012345678901234567890"); - } - - let addr = args.turnkey_address(); - assert_eq!(addr, None); - - unsafe { - std::env::remove_var("TURNKEY_ADDRESS"); - } - } - - // https://github.com/foundry-rs/foundry/issues/5179 - #[test] - fn should_not_require_the_mnemonics_flag_with_mnemonic_indexes() { - let wallet_options = vec![ - ("ledger", "--mnemonic-indexes", 1), - ("trezor", "--mnemonic-indexes", 2), - ("aws", "--mnemonic-indexes", 10), - ("turnkey", "--mnemonic-indexes", 11), - ]; - - for test_case in wallet_options { - let args: MultiWalletOpts = MultiWalletOpts::parse_from([ - "foundry-cli", - &format!("--{}", test_case.0), - test_case.1, - &test_case.2.to_string(), - ]); - - match test_case.0 { - "ledger" => assert!(args.ledger), - "trezor" => assert!(args.trezor), - "aws" => assert!(args.aws), - "turnkey" => assert!(args.turnkey), - _ => panic!("Should have matched one of the previous wallet options"), - } - - assert_eq!( - args.mnemonic_indexes.expect("--mnemonic-indexes should have been set")[0], - test_case.2 - ) - } - } -} diff --git a/crates/wallets/src/wallet_raw/mod.rs b/crates/wallets/src/wallet_raw/mod.rs deleted file mode 100644 index c13076dafaf51..0000000000000 --- a/crates/wallets/src/wallet_raw/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::{PendingSigner, WalletSigner, utils}; -use clap::Parser; -use eyre::Result; -use serde::Serialize; - -/// A wrapper for the raw data options for `Wallet`, extracted to also be used standalone. -/// The raw wallet options can either be: -/// 1. Private Key (cleartext in CLI) -/// 2. Private Key (interactively via secure prompt) -/// 3. Mnemonic (via file path) -#[derive(Clone, Debug, Default, Serialize, Parser)] -#[command(next_help_heading = "Wallet options - raw", about = None, long_about = None)] -pub struct RawWalletOpts { - /// Open an interactive prompt to enter your private key. - #[arg(long, short)] - pub interactive: bool, - - /// Use the provided private key. - #[arg(long, value_name = "RAW_PRIVATE_KEY")] - pub private_key: Option, - - /// Use the mnemonic phrase of mnemonic file at the specified path. - #[arg(long, alias = "mnemonic-path")] - pub mnemonic: Option, - - /// Use a BIP39 passphrase for the mnemonic. - #[arg(long, value_name = "PASSPHRASE")] - pub mnemonic_passphrase: Option, - - /// The wallet derivation path. - /// - /// Works with both --mnemonic-path and hardware wallets. - #[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. - #[arg(long, conflicts_with = "hd_path", default_value_t = 0, value_name = "INDEX")] - pub mnemonic_index: u32, -} - -impl RawWalletOpts { - /// Returns signer configured by provided parameters. - pub fn signer(&self) -> Result> { - if self.interactive { - return Ok(Some(PendingSigner::Interactive.unlock()?)); - } - if let Some(private_key) = &self.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( - mnemonic, - self.mnemonic_passphrase.as_deref(), - self.hd_path.as_deref(), - self.mnemonic_index, - )?)); - } - Ok(None) - } -} diff --git a/deny.toml b/deny.toml index 15f839ae72189..b9dae10bfabf3 100644 --- a/deny.toml +++ b/deny.toml @@ -102,6 +102,7 @@ allow-git = [ "https://github.com/alloy-rs/evm", "https://github.com/foundry-rs/compilers", "https://github.com/foundry-rs/foundry-fork-db", + "https://github.com/foundry-rs/foundry-core", "https://github.com/foundry-rs/optimism", "https://github.com/paradigmxyz/revm-inspectors", "https://github.com/paradigmxyz/solar", diff --git a/doc/doc-filelist.js b/doc/doc-filelist.js new file mode 100644 index 0000000000000..c2a398ff94c23 --- /dev/null +++ b/doc/doc-filelist.js @@ -0,0 +1 @@ +var tree={}; \ No newline at end of file diff --git a/doc/doc-script.js b/doc/doc-script.js new file mode 100644 index 0000000000000..7fa122605e7cb --- /dev/null +++ b/doc/doc-script.js @@ -0,0 +1,228 @@ +// # res/script.js +// +// This is the script file that gets copied into the output. It mainly manages the display +// of the folder tree. The idea of this script file is to be minimal and standalone. So +// that means no jQuery. + +// Use localStorage to store data about the tree's state: whether or not +// the tree is visible and which directories are expanded. Unless the state +var sidebarVisible = (window.localStorage && window.localStorage.docker_showSidebar) ? + window.localStorage.docker_showSidebar == 'yes' : + defaultSidebar; + +/** + * ## makeTree + * + * Consructs the folder tree view + * + * @param {object} treeData Folder structure as in [queueFile](../src/docker.js.html#docker.prototype.queuefile) + * @param {string} root Path from current file to root (ie `'../../'` etc.) + * @param {string} filename The current file name + */ +function makeTree(treeData, root, filename) { + var treeNode = document.getElementById('tree'); + var treeHandle = document.getElementById('sidebar-toggle'); + treeHandle.addEventListener('click', toggleTree, false); + + // Build the html and add it to the container. + treeNode.innerHTML = nodeHtml('', treeData, '', root); + + // Root folder (whole tree) should always be open + treeNode.childNodes[0].className += ' open'; + + // Attach click event handler + treeNode.addEventListener('click', nodeClicked, false); + + if (sidebarVisible) document.body.className += ' sidebar'; + + // Restore scroll position from localStorage if set. And attach scroll handler + if (window.localStorage && window.localStorage.docker_treeScroll) treeNode.scrollTop = window.localStorage.docker_treeScroll; + treeNode.onscroll = treeScrolled; + + // Only set a class to allow CSS transitions after the tree state has been painted + setTimeout(function() { document.body.className += ' slidey'; }, 100); +} + +/** + * ## treeScrolled + * + * Called when the tree is scrolled. Stores the scroll position in localStorage + * so it can be restored on the next pageview. + */ +function treeScrolled() { + var tree = document.getElementById('tree'); + if (window.localStorage) window.localStorage.docker_treeScroll = tree.scrollTop; +} + +/** + * ## nodeClicked + * + * Called when a directory is clicked. Toggles open state of the directory + * + * @param {Event} e The click event + */ +function nodeClicked(e) { + // Find the target + var t = e.target; + + // If the click target is actually a file (rather than a directory), ignore it + if (t.tagName.toLowerCase() !== 'div' || t.className === 'children') return; + + // Recurse upwards until we find the actual directory node + while (t && t.className.substring(0, 3) != 'dir') t = t.parentNode; + + // If we're at the root node, then do nothing (we don't allow collapsing of the whole tree) + if (!t || t.parentNode.id == 'tree') return; + + // Find the path and toggle the state, saving the state in the localStorage variable + var path = t.getAttribute('rel'); + if (t.className.indexOf('open') !== -1) { + t.className = t.className.replace(/\s*open/g, ''); + if (window.localStorage) window.localStorage.removeItem('docker_openPath:' + path); + } else { + t.className += ' open'; + if (window.localStorage) window.localStorage['docker_openPath:' + path] = 'yes'; + } +} + + +/** + * ## nodeHtml + * + * Constructs the markup for a directory in the tree + * + * @param {string} nodename The node name. + * @param {object} node Node object of same format as whole tree. + * @param {string} path The path form the base to this node + * @param {string} root Relative path from current page to root + */ +function nodeHtml(nodename, node, path, root) { + // Firstly, figure out whether or not the directory is expanded from localStorage + var isOpen = window.localStorage && window.localStorage['docker_openPath:' + path] == 'yes'; + var out = '
'; + out += '
' + nodename + '
'; + out += '
'; + + // Loop through all child directories first + if (node.dirs) { + var dirs = []; + for (var i in node.dirs) { + if (node.dirs.hasOwnProperty(i)) dirs.push({ name: i, html: nodeHtml(i, node.dirs[i], path + i + '/', root) }); + } + // Have to store them in an array first and then sort them alphabetically here + dirs.sort(function(a, b) { return (a.name > b.name) ? 1 : (a.name == b.name) ? 0 : -1; }); + + for (var k = 0; k < dirs.length; k += 1) out += dirs[k].html; + } + + // Now loop through all the child files alphabetically + if (node.files) { + node.files.sort(); + for (var j = 0; j < node.files.length; j += 1) { + out += '' + node.files[j] + ''; + } + } + + // Close things off + out += '
'; + + return out; +} + +/** + * ## toggleTree + * + * Toggles the visibility of the folder tree + */ +function toggleTree() { + // Do the actual toggling by modifying the class on the body element. That way we can get some nice CSS transitions going. + if (sidebarVisible) { + document.body.className = document.body.className.replace(/\s*sidebar/g, ''); + sidebarVisible = false; + } else { + document.body.className += ' sidebar'; + sidebarVisible = true; + } + if (window.localStorage) { + if (sidebarVisible) { + window.localStorage.docker_showSidebar = 'yes'; + } else { + window.localStorage.docker_showSidebar = 'no'; + } + } +} + +/** + * ## wireUpTabs + * + * Wires up events on the sidebar tabe + */ +function wireUpTabs() { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Each tab has a class corresponding of the id of its tab pane + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + children[i].addEventListener('click', function(c) { + return function() { switchTab(c); }; + }(children[i].className)); + } +} + +/** + * ## switchTab + * + * Switches tabs in the sidebar + * + * @param {string} tab The ID of the tab to switch to + */ +function switchTab(tab) { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Easiest way to go through tabs without any kind of selector is just to look at the tab bar + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + + // Figure out what tab pane this tab button corresponts to + var t = children[i].className.replace(/\s.*$/, ''); + if (t === tab) { + // Show the tab pane, select the tab button + document.getElementById(t).style.display = 'block'; + if (children[i].className.indexOf('selected') === -1) children[i].className += ' selected'; + } else { + // Hide the tab pane, deselect the tab button + document.getElementById(t).style.display = 'none'; + children[i].className = children[i].className.replace(/\sselected/, ''); + } + } + + // Store the last open tab in localStorage + if (window.localStorage) window.localStorage.docker_sidebarTab = tab; +} + +/** + * ## window.onload + * + * When the document is ready, make the sidebar and all that jazz + */ +(function(init) { + if (window.addEventListener) { + window.addEventListener('DOMContentLoaded', init); + } else { // IE8 and below + window.onload = init; + } +}(function() { + makeTree(tree, relativeDir, thisFile); + wireUpTabs(); + + // Switch to the last viewed sidebar tab if stored, otherwise default to folder tree + if (window.localStorage && window.localStorage.docker_sidebarTab) { + switchTab(window.localStorage.docker_sidebarTab); + } else { + switchTab('tree'); + } +})); diff --git a/doc/doc-style.css b/doc/doc-style.css new file mode 100644 index 0000000000000..2019a1b7659c6 --- /dev/null +++ b/doc/doc-style.css @@ -0,0 +1,403 @@ +/* + +Original highlight.js style (c) Ivan Sagalaev + +*/ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +/* Base color: saturation 0; */ +.hljs, +.hljs-subst { + color: #444; +} +.hljs-comment { + color: #888888; +} +.hljs-keyword, +.hljs-attribute, +.hljs-selector-tag, +.hljs-meta-keyword, +.hljs-doctag, +.hljs-name { + font-weight: bold; +} +/* User color: hue: 0 */ +.hljs-type, +.hljs-string, +.hljs-number, +.hljs-selector-id, +.hljs-selector-class, +.hljs-quote, +.hljs-template-tag, +.hljs-deletion { + color: #880000; +} +.hljs-title, +.hljs-section { + color: #880000; + font-weight: bold; +} +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #BC6060; +} +/* Language color: hue: 90; */ +.hljs-literal { + color: #78A960; +} +.hljs-built_in, +.hljs-bullet, +.hljs-code, +.hljs-addition { + color: #397300; +} +/* Meta color: hue: 200 */ +.hljs-meta { + color: #1f7199; +} +.hljs-meta-string { + color: #4d99bf; +} +/* Misc effects */ +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: bold; +} +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + margin: 0; + padding: 0; + background: #ffffff; + color: #4d4d4d; +} +p, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0 0 15px 0; +} +h1 { + margin-top: 40px; +} +a { + color: #880000; +} +a:visited { + color: #880000; +} +#tree, +#headings { + position: absolute; + top: 30px; + left: 0; + bottom: 0; + width: 290px; + padding: 10px 0; + overflow: auto; +} +#sidebar_wrapper { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 0; + overflow: hidden; + background: #e7e7e7; +} +#sidebar_switch { + position: absolute; + top: 0; + left: 0; + width: 290px; + height: 29px; + border-bottom: 1px solid; + background: #e2e2e2; + border-bottom-color: #d6d6d6; +} +#sidebar_switch span { + display: block; + float: left; + width: 50%; + text-align: center; + line-height: 29px; + cursor: pointer; + color: #4b4b4b; +} +#sidebar_switch span:hover { + background: #e7e7e7; +} +#sidebar_switch .selected { + font-weight: bold; + background: #ededed; + color: #444; +} +.slidey #sidebar_wrapper { + -webkit-transition: width 250ms linear; + -moz-transition: width 250ms linear; + -ms-transition: width 250ms linear; + -o-transition: width 250ms linear; + transition: width 250ms linear; +} +.sidebar #sidebar_wrapper { + width: 290px; +} +#tree .nodename { + text-indent: 12px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAg0lEQVQYlWNIS0tbAcSK////Z8CHGTIzM7+mp6d/ASouwqswKyvrO1DRfyg+CcRaxCgE4Z9A3AjEbIQUgjHQOQvwKgS6+ffChQt3AiUDcCqsra29d/v27R6ghCVWN2ZnZ/9YuXLlRqBAPBALYvVMR0fHmQcPHrQBOUZ4gwfqFj5CAQ4Al6wLIYDwo9QAAAAASUVORK5CYII="); + background-repeat: no-repeat; + background-position: left center; + cursor: pointer; +} +#tree .open > .nodename { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAlElEQVQYlWNIS0tbCsT/8eCN////Z2B49OhRfHZ29jdsioDiP27evJkNVggkONeuXbscm8Jly5atA8rzwRSCsG5DQ8MtZEU1NTUPgOLGUHm4QgaQFVlZWT9BijIzM39fuHChDCaHohBkBdCq9SCF8+bN2wHkC+FSCMLGkyZNOvb9+3dbNHEMhSDsDsRMxCjEiolWCADeUBHgU/IGQQAAAABJRU5ErkJggg=="); + background-position: left 7px; +} +#tree .dir, +#tree .file { + position: relative; + min-height: 20px; + line-height: 20px; + padding-left: 12px; +} +#tree .dir > .children, +#tree .file > .children { + display: none; +} +#tree .dir.open > .children, +#tree .file.open > .children { + display: block; +} +#tree .file { + padding-left: 24px; + display: block; + text-decoration: none; + color: #444; +} +#tree > .dir { + padding-left: 0; +} +#headings .heading a { + text-decoration: none; + padding-left: 10px; + display: block; + color: #444; +} +#headings .h1 { + padding-left: 0; + margin-top: 10px; + font-size: 1.3em; +} +#headings .h2 { + padding-left: 10px; + margin-top: 8px; + font-size: 1.1em; +} +#headings .h3 { + padding-left: 20px; + margin-top: 5px; + font-size: 1em; +} +#headings .h4 { + padding-left: 30px; + margin-top: 3px; + font-size: 0.9em; +} +#headings .h5 { + padding-left: 40px; + margin-top: 1px; + font-size: 0.8em; +} +#headings .h6 { + padding-left: 50px; + font-size: 0.75em; +} +#sidebar-toggle { + position: fixed; + top: 0; + left: 0; + width: 5px; + bottom: 0; + z-index: 2; + cursor: pointer; + background: #dfdfdf; +} +#sidebar-toggle:hover { + width: 10px; + background: #d6d6d6; +} +.slidey #sidebar-toggle, +.slidey #container { + -webkit-transition: all 250ms linear; + -moz-transition: all 250ms linear; + -ms-transition: all 250ms linear; + -o-transition: all 250ms linear; + transition: all 250ms linear; +} +.sidebar #sidebar-toggle { + left: 290px; +} +#container { + position: fixed; + left: 5px; + right: 0; + top: 0; + bottom: 0; + overflow: auto; +} +.sidebar #container { + left: 295px; +} +.no-sidebar #sidebar_wrapper, +.no-sidebar #sidebar-toggle { + display: none; +} +.no-sidebar #container { + left: 0; +} +#page { + padding-top: 40px; +} +table td { + border: 0; + outline: 0; +} +.docs.markdown { + padding: 10px 50px; +} +td.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; +} +.docs pre { + margin: 15px 0 15px; + padding: 5px; + padding-left: 10px; + border: 1px solid #d6d6d6; + background: #F0F0F0; + font-size: 12px; + overflow: auto; +} +.docs pre.code_stats { + font-size: 60%; +} +.docs p tt, +.docs li tt, +.docs p code, +.docs li code { + border: 1px solid #d6d6d6; + font-size: 12px; + padding: 0 0.2em; + background: #e7e7e7; +} +.dox { + border-top: 1px solid #dddddd; + padding-top: 10px; + padding-bottom: 10px; +} +.dox .details { + padding: 10px; + background: #F0F0F0; + border: 1px solid #d6d6d6; + margin-bottom: 10px; +} +.dox .dox_tag_title { + font-weight: bold; +} +.dox .dox_tag_detail { + margin-left: 10px; +} +.dox .dox_tag_detail span { + margin-right: 5px; +} +.dox .dox_type { + font-style: italic; +} +.dox .dox_tag_name { + font-weight: bold; +} +.pilwrap { + position: relative; + padding-top: 1px; +} +.pilwrap .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + color: #555555; +} +.pilwrap .pilcrow:before { + content: '\b6'; +} +.pilwrap:hover .pilcrow { + opacity: 1; +} +td.code { + padding: 8px 15px 8px 25px; + width: 100%; + vertical-align: top; + border-left: 1px solid #d6d6d6; + background: #F0F0F0; +} +.background { + border-left: 1px solid #d6d6d6; + position: absolute; + z-index: -1; + top: 0; + right: 0; + bottom: 0; + left: 525px; + background: #F0F0F0; +} +pre, +tt, +code { + font-size: 12px; + line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; + padding: 0; + white-space: pre-wrap; + background: #F0F0F0; +} +.line-num { + display: inline-block; + width: 50px; + text-align: right; + opacity: 0.3; + margin-left: -20px; + text-decoration: none; + color: #888888; +} +.line-num:before { + content: attr(data-line); +} diff --git a/flake.lock b/flake.lock index 3b3ca69bf658b..4fa81efa24f11 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1775891769, - "narHash": "sha256-EOfVlTKw2n8w1uhfh46GS4hEGnQ7oWrIWQfIY6utIkI=", + "lastModified": 1776497206, + "narHash": "sha256-Em+RSdFnwyyKPGUBFtQYtVjm+1UvIc9gOR91Y22zlzg=", "owner": "nix-community", "repo": "fenix", - "rev": "6fbc54dde15aee725bdc7aae5e478849685d5f56", + "rev": "df2295365fb081fe0745449762a771290782c22d", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1775888245, - "narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=", + "lastModified": 1776329215, + "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "13043924aaa7375ce482ebe2494338e058282925", + "rev": "b86751bc4085f48661017fa226dee99fab6c651b", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1775843361, - "narHash": "sha256-j53ZgyDvmYf3Sjh1IPvvTjqa614qUfVQSzj59+MpzkY=", + "lastModified": 1776441750, + "narHash": "sha256-1rVfG+mj8R4ze+lSYCa4iAv7FzrB03Cprtxmd1MfZak=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "9eb97ea96d8400e8957ddd56702e962614296583", + "rev": "251df518d73abb5c5d573c4d5d266a3edae9ca5a", "type": "github" }, "original": { diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix diff --git a/npm/src/const.mjs b/npm/src/const.mjs index 6b3dcf3f9fbed..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** From 54113b1a460e5dc4ce50113690b375bb0057b7a8 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:58:26 +0000 Subject: [PATCH 305/374] fix(cheatcodes): read broadcasts with the active network #437 (#440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry (#14347) * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry When an access key is refreshed but not yet provisioned on-chain, the first charge payment strips key_authorization (assuming provisioned), the server rejects with verification-failed, and the retry must obtain a fresh 402 challenge — the server consumes challenge IDs on first use, so reusing the original challenge would fail again. Amp-Thread-ID: https://ampcode.com/threads/T-019d8bde-8cf7-778c-9c9c-727632e62bd4 Co-authored-by: Amp * refactor(mpp): extract select_challenge helper, improve retry robustness - Extract shared select_challenge() for consistent 402 diagnostics on both initial and verification-failed retry paths - Don't restore key_provisioned(true) on non-402 fresh probe responses to avoid bad state on transient server errors - Add rollback_pending() to key-not-provisioned retry to prevent dirty local state from producing wrong credential shapes Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019d9619-6c24-75af-b1e8-7d02d77e0561 --------- Co-authored-by: Amp * refactor(evm): remove useless Eth/Tempo EVM wrapper structs (#14350) * chore(mpp): add built-in rpc url mapping (#14353) add built-in rpc url mapping * fix(evm): skip isolation for CREATE2 factory redirect calls (#14360) * fix(evm): preserve CREATE2 redirect state across isolated transactions (#14363) * feat(init): add `SignatureVerifier` to tempo example (#14351) * feat(init): add `SignatureVerifier` to tempo example * chore: also call `recover()` * style: typo --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(config): validate `optimizer_runs` does not exceed u32::MAX (#14354) * fix(config): validate `optimizer_runs` does not exceed u32::MAX * fix clippy * ci: remove softprops/action-gh-release dependabot ignore (#14364) The draft-finalize race that caused `Too many retries` in matrix jobs (v2.5.0) was fixed in v2.5.2. We're already on v2.6.1, so the ignore rule is no longer needed. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): mine TIP20 virtual address master salt (#14365) * chore(deps): bump actions/github-script from 8.0.0 to 9.0.0 (#14367) * chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (#14368) * chore(deps): bump taiki-e/install-action from 2.75.0 to 2.75.5 (#14369) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.75.0 to 2.75.5. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/cf39a74df4a72510be4e5b63348d61067f11e64a...a2352fc6ce487f030a3aa709482d57823eadfb37) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.75.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump anstream from 0.6.21 to 1.0.0 (#14329) Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.21 to 1.0.0. - [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.21...anstream-v1.0.0) --- updated-dependencies: - dependency-name: anstream dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * Update flake.lock (#14375) Co-authored-by: github-actions[bot] * fix(ci): handle stale branches in bump-tempo workflow (#14377) * fix(config): surface cleanup failures as warnings instead of hard errors (#14379) * fix(config): surface cleanup failures as warnings instead of hard errors Cache cleanup is best-effort by design. Instead of silently swallowing errors or making them fatal, return warnings from Config and emit them via sh_warn! at the CLI layer. All cleanup steps now run even if some fail. Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * fix: address CI - collapse nested ifs, remove disallowed eprintln Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * chore(tempo): bump rev + use extension traits (#14378) * feat(tempo): bump tempo rev and use TempoProviderExt::is_hardfork_active Bumps tempo crates from 6f4f5cc to bb08bb9 (latest main) and replaces the local is_hardfork_active helper with the new trait method on TempoProviderExt, which queries the tempo_forkSchedule RPC directly. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: propagate is_hardfork_active error instead of unwrap_or Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * refactor: use TempoAddressExt in tempo_labels and fee token parsing Replace is_tip20_prefix() with addr.is_tip20() in the TempoLabels inspector and use Address::TIP20_PREFIX instead of hardcoded hex bytes in token_id_to_address. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: enable reth-codec feature on tempo-primitives and fix rustfmt The rev bump split tempo crates into two copies (6f4f5cc for mpp-rs, bb08bb9 for foundry). The bb08bb9 copy lost the reth-codec feature that was previously unified from other workspace members. Explicitly enable it to restore Compact derives on TempoTxEnvelope/TempoHeader. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * chore: remove cargo-update workflow (#14382) Dependabot is now configured for weekly cargo updates, making this workflow redundant. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): return error for eth_getLogs with unknown blockHash instead of empty (#14371) Co-authored-by: Claude Opus 4.7 (1M context) * refactor(cast): cleanup `cast send` (#14385) * refactor(cast): cleanup `cast send` * docs: clarify `TempoNetwork` requirement * style: clippy * fix: review feedback * fix(anvil): refetch full fork blocks with missing tx cache (#14384) * docs: correct Tempo TIP-1022 documentation URL (#14387) * feat(verify): clearer error when `ETHERSCAN_API_KEY` selects etherscan (#14383) * feat: make asm-keccak a default feature in all binaries (#14389) * chore(wallets): move `wallets` crate to `foundry-core` (#14348) * fix(cheatcodes): read broadcasts with the active network (#14388) --------- Signed-off-by: dependabot[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Louis Peter Sitoe Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: o-az From 6c5e62d9212aba839d8e1662defefd8377a3adbf Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:48:14 +0700 Subject: [PATCH 306/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..ca543dbc1fe83 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} From ae901be8c6e93bc3ab4095999b0e49168d8f85f2 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:48:32 +0700 Subject: [PATCH 307/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index ca543dbc1fe83..0865147167243 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -19,7 +19,7 @@ use std::{ops::ControlFlow, path::PathBuf}; /// /// # Required Methods /// -/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +/// - `lint`: Scans the provided source files emitting a diagnostic for lints found. pub trait Linter: Send + Sync + Clone { type Language: Language; type Lint: Lint; From f29d0aa1016c29ce5e9221a246dc2d41c1ced0c7 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:00:45 +0000 Subject: [PATCH 308/374] Wagmi (e604566) (#412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update docker.yml * Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory (#422) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * doc-script.js (#438) * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <… * fix(cheatcodes): read broadcasts with the active network #437 (#440) * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry (#14347) * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry When an access key is refreshed but not yet provisioned on-chain, the first charge payment strips key_authorization (assuming provisioned), the server rejects with verification-failed, and the retry must obtain a fresh 402 challenge — the server consumes challenge IDs on first use, so reusing the original challenge would fail again. Amp-Thread-ID: https://ampcode.com/threads/T-019d8bde-8cf7-778c-9c9c-727632e62bd4 Co-authored-by: Amp * refactor(mpp): extract select_challenge helper, improve retry robustness - Extract shared select_challenge() for consistent 402 diagnostics on both initial and verification-failed retry paths - Don't restore key_provisioned(true) on non-402 fresh probe responses to avoid bad state on transient server errors - Add rollback_pending() to key-not-provisioned retry to prevent dirty local state from producing wrong credential shapes Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019d9619-6c24-75af-b1e8-7d02d77e0561 --------- Co-authored-by: Amp * refactor(evm): remove useless Eth/Tempo EVM wrapper structs (#14350) * chore(mpp): add built-in rpc url mapping (#14353) add built-in rpc url mapping * fix(evm): skip isolation for CREATE2 factory redirect calls (#14360) * fix(evm): preserve CREATE2 redirect state across isolated transactions (#14363) * feat(init): add `SignatureVerifier` to tempo example (#14351) * feat(init): add `SignatureVerifier` to tempo example * chore: also call `recover()` * style: typo --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(config): validate `optimizer_runs` does not exceed u32::MAX (#14354) * fix(config): validate `optimizer_runs` does not exceed u32::MAX * fix clippy * ci: remove softprops/action-gh-release dependabot ignore (#14364) The draft-finalize race that caused `Too many retries` in matrix jobs (v2.5.0) was fixed in v2.5.2. We're already on v2.6.1, so the ignore rule is no longer needed. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): mine TIP20 virtual address master salt (#14365) * chore(deps): bump actions/github-script from 8.0.0 to 9.0.0 (#14367) * chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (#14368) * chore(deps): bump taiki-e/install-action from 2.75.0 to 2.75.5 (#14369) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.75.0 to 2.75.5. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/cf39a74df4a72510be4e5b63348d61067f11e64a...a2352fc6ce487f030a3aa709482d57823eadfb37) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.75.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump anstream from 0.6.21 to 1.0.0 (#14329) Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.21 to 1.0.0. - [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.21...anstream-v1.0.0) --- updated-dependencies: - dependency-name: anstream dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * Update flake.lock (#14375) Co-authored-by: github-actions[bot] * fix(ci): handle stale branches in bump-tempo workflow (#14377) * fix(config): surface cleanup failures as warnings instead of hard errors (#14379) * fix(config): surface cleanup failures as warnings instead of hard errors Cache cleanup is best-effort by design. Instead of silently swallowing errors or making them fatal, return warnings from Config and emit them via sh_warn! at the CLI layer. All cleanup steps now run even if some fail. Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * fix: address CI - collapse nested ifs, remove disallowed eprintln Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * chore(tempo): bump rev + use extension traits (#14378) * feat(tempo): bump tempo rev and use TempoProviderExt::is_hardfork_active Bumps tempo crates from 6f4f5cc to bb08bb9 (latest main) and replaces the local is_hardfork_active helper with the new trait method on TempoProviderExt, which queries the tempo_forkSchedule RPC directly. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: propagate is_hardfork_active error instead of unwrap_or Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * refactor: use TempoAddressExt in tempo_labels and fee token parsing Replace is_tip20_prefix() with addr.is_tip20() in the TempoLabels inspector and use Address::TIP20_PREFIX instead of hardcoded hex bytes in token_id_to_address. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: enable reth-codec feature on tempo-primitives and fix rustfmt The rev bump split tempo crates into two copies (6f4f5cc for mpp-rs, bb08bb9 for foundry). The bb08bb9 copy lost the reth-codec feature that was previously unified from other workspace members. Explicitly enable it to restore Compact derives on TempoTxEnvelope/TempoHeader. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * chore: remove cargo-update workflow (#14382) Dependabot is now configured for weekly cargo updates, making this workflow redundant. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): return error for eth_getLogs with unknown blockHash instead of empty (#14371) Co-authored-by: Claude Opus 4.7 (1M context) * refactor(cast): cleanup `cast send` (#14385) * refactor(cast): cleanup `cast send` * docs: clarify `TempoNetwork` requirement * style: clippy * fix: review feedback * fix(anvil): refetch full fork blocks with missing tx cache (#14384) * docs: correct Tempo TIP-1022 documentation URL (#14387) * feat(verify): clearer error when `ETHERSCAN_API_KEY` selects etherscan (#14383) * feat: make asm-keccak a default feature in all binaries (#14389) * chore(wallets): move `wallets` crate to `foundry-core` (#14348) * fix(cheatcodes): read broadcasts with the active network (#14388) --------- Signed-off-by: dependabot[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Louis Peter Sitoe Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: o-az --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: cui Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Louis Peter Sitoe Co-authored-by: o-az --- .circleci/ci_v1.yml | 31 ------- .circleci/config.yml | 31 ------- crates/doc/src/writer/as_doc.rs | 39 +++++--- crates/lint/src/sol/gas/keccak.rs | 98 +++----------------- crates/script-sequence/src/sequence.rs | 85 ++++++----------- crates/test-utils/src/script.rs | 15 +-- crates/verify/src/etherscan/standard_json.rs | 11 ++- 7 files changed, 86 insertions(+), 224 deletions(-) delete mode 100644 .circleci/ci_v1.yml delete mode 100644 .circleci/config.yml diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 82c6de5b42b73..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index d5d401c51893c..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 1502322ac87f4..e3358b2f437dd 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -1,12 +1,11 @@ use crate::{ - CONTRACT_INHERITANCE_ID, CommentTag, Comments, CommentsRef, DEPLOYMENTS_ID, Document, - GIT_SOURCE_ID, INHERITDOC_ID, Markdown, PreprocessorOutput, - document::{DocumentContent, read_context}, - helpers::function_signature, + document::{read_context, DocumentContent}, parser::ParseSource, - solang_ext::SafeUnwrap, writer::BufWriter, + CommentTag, Comments, CommentsRef, Document, Markdown, PreprocessorOutput, + CONTRACT_INHERITANCE_ID, DEPLOYMENTS_ID, GIT_SOURCE_ID, INHERITDOC_ID, }; +use forge_fmt::solang_ext::SafeUnwrap; use itertools::Itertools; use solang_parser::pt::{Base, FunctionDefinition, VariableAttribute}; use std::path::Path; @@ -63,7 +62,7 @@ impl AsDoc for CommentsRef<'_> { // Write dev tags let devs = self.include_tag(CommentTag::Dev); for d in devs.iter() { - writer.write_dev_content(&d.value)?; + writer.write_italic(&d.value)?; writer.writeln()?; } @@ -88,7 +87,7 @@ impl AsDoc for CommentsRef<'_> { impl AsDoc for Base { fn as_doc(&self) -> AsDocResult { - Ok(self.name.identifiers.iter().map(|ident| ident.name.clone()).join(".")) + Ok(self.name.identifiers.iter().map(|ident| ident.name.to_owned()).join(".")) } } @@ -107,7 +106,16 @@ impl AsDoc for Document { for item in items { let func = item.as_function().unwrap(); - let heading = function_signature(func).replace(',', ", "); + let mut heading = item.source.ident(); + if !func.params.is_empty() { + heading.push_str(&format!( + "({})", + func.params + .iter() + .map(|p| p.1.as_ref().map(|p| p.ty.to_string()).unwrap_or_default()) + .join(", ") + )); + } writer.write_heading(&heading)?; writer.write_section(&item.comments, &item.code)?; } @@ -253,7 +261,15 @@ impl AsDoc for Document { writer.write_subtitle("Enums")?; enums.into_iter().try_for_each(|(item, comments, code)| { writer.write_heading(&item.name.safe_unwrap().name)?; - writer.write_section(comments, code)?; + + let filtered_comments: Comments = (*comments) + .iter() + .filter(|c| c.tag != CommentTag::Custom("variant".to_string())) + .cloned() + .collect::>() + .into(); + + writer.write_section(&filtered_comments, code)?; writer.try_write_variant_table(item, comments) })?; } @@ -319,10 +335,9 @@ impl Document { comments: &Comments, code: &str, ) -> Result<(), std::fmt::Error> { - let func_sign = function_signature(func); - let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.clone()); + let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned()); let comments = - comments.merge_inheritdoc(&func_sign, read_context!(self, INHERITDOC_ID, Inheritdoc)); + comments.merge_inheritdoc(&func_name, read_context!(self, INHERITDOC_ID, Inheritdoc)); // Write function name writer.write_heading(&func_name)?; diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index b6a18f292a91b..7316f4c4239b7 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -1,103 +1,27 @@ use super::AsmKeccak256; use crate::{ - linter::{LateLintPass, LintContext}, + declare_forge_lint, + linter::EarlyLintPass, sol::{Severity, SolLint}, }; -use solar::{ - ast::{self as ast, Span}, - interface::kw, - sema::hir::{self}, -}; +use solar_ast::{Expr, ExprKind}; +use solar_interface::kw; declare_forge_lint!( ASM_KECCAK256, Severity::Gas, "asm-keccak256", - "use of inefficient hashing mechanism; consider using inline assembly" + "hash using inline assembly to save gas" ); -impl<'hir> LateLintPass<'hir> for AsmKeccak256 { - fn check_stmt( - &mut self, - ctx: &LintContext, - hir: &'hir hir::Hir<'hir>, - stmt: &'hir hir::Stmt<'hir>, - ) { - let check_expr_and_emit_lint = - |expr: &'hir hir::Expr<'hir>, assign: Option, is_return: bool| { - if let Some(hash_arg) = extract_keccak256_arg(expr) { - self.emit_lint( - ctx, - hir, - stmt.span, - expr, - hash_arg, - AsmContext { _assign: assign, _is_return: is_return }, - ); - } - }; - - match stmt.kind { - hir::StmtKind::DeclSingle(var_id) => { - let var = hir.variable(var_id); - if let Some(init) = var.initializer { - // Constants should be optimized by the compiler, so no gas savings apply. - if !matches!(var.mutability, Some(hir::VarMut::Constant)) { - check_expr_and_emit_lint(init, var.name, false); - } +impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 { + fn check_expr(&mut self, ctx: &crate::linter::LintContext<'_>, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(expr, _) = &expr.kind { + if let ExprKind::Ident(ident) = &expr.kind { + if ident.name == kw::Keccak256 { + ctx.emit(&ASM_KECCAK256, expr.span); } } - // Expressions that don't (directly) assign to a variable - hir::StmtKind::Expr(expr) - | hir::StmtKind::Emit(expr) - | hir::StmtKind::Revert(expr) - | hir::StmtKind::DeclMulti(_, expr) - | hir::StmtKind::If(expr, ..) => check_expr_and_emit_lint(expr, None, false), - hir::StmtKind::Return(Some(expr)) => check_expr_and_emit_lint(expr, None, true), - _ => (), } } } - -impl AsmKeccak256 { - /// Emits lints (when possible with fix suggestions) for inefficient `keccak256` calls. - fn emit_lint( - &self, - ctx: &LintContext, - _hir: &hir::Hir<'_>, - _stmt_span: Span, - call: &hir::Expr<'_>, - _hash: &hir::Expr<'_>, - _asm_ctx: AsmContext, - ) { - ctx.emit(&ASM_KECCAK256, call.span); - } -} - -/// If the expression is a call to `keccak256` with one argument, returns that argument. -fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Expr<'hir>> { - let hir::ExprKind::Call( - callee, - hir::CallArgs { kind: hir::CallArgsKind::Unnamed(args), .. }, - .., - ) = &expr.kind - else { - return None; - }; - - let is_keccak = if let hir::ExprKind::Ident([hir::Res::Builtin(builtin)]) = callee.kind { - matches!(builtin.name(), kw::Keccak256) - } else { - return None; - }; - - (is_keccak && args.len() == 1).then(|| &args[0]) -} - -// -- HELPER FUNCTIONS AND STRUCTS ---------------------------------------------------------------- - -#[derive(Debug, Clone, Copy)] -struct AsmContext { - _assign: Option, - _is_return: bool, -} diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index fb0c01eb4fd1e..37c59fc47f17c 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -1,6 +1,7 @@ use crate::transaction::TransactionWithMetadata; -use alloy_network::{Network, ReceiptResponse}; +use alloy_network::ReceiptResponse; use alloy_primitives::{TxHash, hex, map::HashMap}; +use alloy_rpc_types_eth::TransactionReceipt; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{SELECTOR_LEN, TransactionMaybeSigned, fs, shell}; use foundry_compilers::ArtifactId; @@ -11,7 +12,7 @@ use std::{ path::PathBuf, time::{Duration, SystemTime, UNIX_EPOCH}, }; - +# pub const DRY_RUN_DIR: &str = "dry-run"; #[derive(Clone, Serialize, Deserialize)] @@ -19,29 +20,13 @@ pub struct NestedValue { pub internal_type: String, pub value: String, } - -/// Sensitive values from the transactions in a script sequence -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveTransactionMetadata { - pub rpc: String, -} - -/// Sensitive info from the script sequence which is saved into the cache folder -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveScriptSequence { - pub transactions: VecDeque, -} - +# /// Helper that saves the transactions sequence and its state on which transactions have been /// broadcasted -#[derive(Clone, Serialize, Deserialize)] -#[serde(bound( - serialize = "N::TransactionRequest: Serialize, N::TxEnvelope: Serialize", - deserialize = "N::TransactionRequest: for<'de2> Deserialize<'de2>, N::TxEnvelope: for<'de2> Deserialize<'de2>" -))] -pub struct ScriptSequence { - pub transactions: VecDeque>, - pub receipts: Vec, +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct ScriptSequence { + pub transactions: VecDeque, + pub receipts: Vec, pub libraries: Vec, pub pending: Vec, #[serde(skip)] @@ -54,24 +39,20 @@ pub struct ScriptSequence { pub commit: Option, } -impl Default for ScriptSequence { - fn default() -> Self { - Self { - transactions: Default::default(), - receipts: Default::default(), - libraries: Default::default(), - pending: Default::default(), - paths: Default::default(), - returns: Default::default(), - timestamp: Default::default(), - chain: Default::default(), - commit: Default::default(), - } - } +/// Sensitive values from the transactions in a script sequence +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveTransactionMetadata { + pub rpc: String, +} + +/// Sensitive info from the script sequence which is saved into the cache folder +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveScriptSequence { + pub transactions: VecDeque, } -impl From<&ScriptSequence> for SensitiveScriptSequence { - fn from(sequence: &ScriptSequence) -> Self { +impl From<&ScriptSequence> for SensitiveScriptSequence { + fn from(sequence: &ScriptSequence) -> Self { Self { transactions: sequence .transactions @@ -82,7 +63,7 @@ impl From<&ScriptSequence> for SensitiveScriptSequence { } } -impl ScriptSequence { +impl ScriptSequence { /// Loads The sequence for the corresponding json file pub fn load( config: &Config, @@ -90,10 +71,7 @@ impl ScriptSequence { target: &ArtifactId, chain_id: u64, dry_run: bool, - ) -> Result - where - N::TxEnvelope: for<'d> Deserialize<'d>, - { + ) -> Result { let (path, sensitive_path) = Self::get_paths(config, sig, target, chain_id, dry_run)?; let mut script_sequence: Self = fs::read_json_file(&path) @@ -114,10 +92,7 @@ impl ScriptSequence { /// Saves the transactions as file if it's a standalone deployment. /// `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<()> - where - N::TxEnvelope: Serialize, - { + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { self.sort_receipts(); if self.transactions.is_empty() { @@ -166,10 +141,10 @@ impl ScriptSequence { Ok(()) } - pub fn add_receipt(&mut self, receipt: N::ReceiptResponse) { + pub fn add_receipt(&mut self, receipt: TransactionReceipt) { self.receipts.push(receipt); } - + # /// Sorts all receipts with ascending transaction index pub fn sort_receipts(&mut self) { self.receipts.sort_by_key(|r| (r.block_number(), r.transaction_index())); @@ -185,7 +160,7 @@ impl ScriptSequence { pub fn remove_pending(&mut self, tx_hash: TxHash) { self.pending.retain(|element| element != &tx_hash); } - + # /// Gets paths in the formats /// `./broadcast/[contract_filename]/[chain_id]/[sig]-latest.json` and /// `./cache/[contract_filename]/[chain_id]/[sig]-latest.json`. @@ -196,8 +171,8 @@ impl ScriptSequence { chain_id: u64, dry_run: bool, ) -> Result<(PathBuf, PathBuf)> { - let mut broadcast = config.broadcast.clone(); - let mut cache = config.cache_path.clone(); + 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.")?; @@ -229,7 +204,7 @@ impl ScriptSequence { } /// Returns the list of the transactions without the metadata. - pub fn transactions(&self) -> impl Iterator> { + pub fn transactions(&self) -> impl Iterator { self.transactions.iter().map(|tx| tx.tx()) } @@ -240,7 +215,7 @@ impl ScriptSequence { .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); } } - +# /// 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/test-utils/src/script.rs b/crates/test-utils/src/script.rs index c1a6cb53bdbff..07413fef5495c 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -247,11 +247,12 @@ impl ScriptTester { trace!(target: "tests", "STDOUT\n{stdout}\n\nSTDERR\n{stderr}"); - assert!( - !(!stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str())), - "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", - expected.as_str() - ); + if !stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str()) { + panic!( + "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", + expected.as_str() + ); + } self } @@ -299,7 +300,7 @@ pub enum ScriptOutcome { } impl ScriptOutcome { - pub const fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> &'static str { match self { Self::OkNoEndpoint => "If you wish to simulate on-chain transactions pass a RPC URL.", Self::OkSimulation => "SIMULATION COMPLETE. To broadcast these", @@ -323,7 +324,7 @@ impl ScriptOutcome { } } - pub const fn is_err(&self) -> bool { + pub fn is_err(&self) -> bool { match self { Self::OkNoEndpoint | Self::OkSimulation diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index 848d5808fd77b..bb0549b9ad19e 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -2,6 +2,8 @@ use super::{EtherscanSourceProvider, VerifyArgs}; use crate::{provider::VerificationContext, verify::ContractLanguage}; use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; +use foundry_compilers::artifacts::{Source, vyper::VyperInput}; +use std::path::Path; #[derive(Debug)] pub struct EtherscanStandardJsonSource; @@ -24,7 +26,14 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { serde_json::to_string(&input).wrap_err("Failed to parse standard json input")? } ContractLanguage::Vyper => { - let input = context.get_vyper_standard_json_input()?; + let path = Path::new(&context.target_path); + let sources = Source::read_all_from(path, &["vy", "vyi"])?; + let input = VyperInput::new( + sources, + context.compiler_settings.vyper.clone(), + &context.compiler_version, + ); + serde_json::to_string(&input).wrap_err("Failed to parse vyper json input")? } }; From 4efa9689307401de63d634a3032ee435c43d282c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:20:00 +0700 Subject: [PATCH 309/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cheatcodes/src/fs.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 5e762ec62a035..3a63c5631c297 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1129,6 +1129,14 @@ mod tests { assert_eq!(latest.contractAddress, address!("20c0000000000000000000000000000000000000")); assert!(latest.success); + let temp_root = env::temp_dir().canonicalize().unwrap(); + let root_parent = root.parent().and_then(|p| p.canonicalize().ok()).unwrap(); + let root_name_ok = root + .file_name() + .and_then(|n| n.to_str()) + .map(|n| n.starts_with("foundry-cheatcodes-")) + .unwrap_or(false); + assert!(root_parent == temp_root && root_name_ok, "refusing to delete unexpected path"); stdfs::remove_dir_all(root).unwrap(); } } From 48b8782dd113ffdacc7d7bfc4fd0606269b8db4a Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:20:27 +0700 Subject: [PATCH 310/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 36d8e609cb36f..b695c94b8ab6c 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -118,17 +118,28 @@ impl ScriptTester { fn copy_testdata(root: &Path) -> Result<()> { let testdata = Self::testdata_path(); let from_dir = testdata.join("utils"); + let canonical_from_dir = from_dir.canonicalize()?; let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); + + // Canonicalize and confine entry path before any filesystem operation on it. + let canonical_file = match file.canonicalize() { + Ok(path) => path, + Err(_) => continue, + }; + if !canonical_file.starts_with(&canonical_from_dir) { + continue; + } + // Only operate on regular files to avoid following symlinks or directories - let metadata = fs::symlink_metadata(&file)?; + let metadata = fs::symlink_metadata(&canonical_file)?; let ftype = metadata.file_type(); if !ftype.is_file() { continue; } - let name = match file.file_name() { + let name = match canonical_file.file_name() { Some(name) => name, None => continue, }; @@ -138,15 +149,8 @@ impl ScriptTester { // Skip invalid (potentially dangerous) file names continue; } - // Verify canonicalized file is in from_dir to avoid symlink traversal - if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&from_dir) { - continue; - } - } else { - continue; - } - fs::copy(&file, to_dir.join(name))?; + + fs::copy(&canonical_file, to_dir.join(name))?; } Ok(()) } From 9f6402dea78dde8f396812aa2b5c008b1ca769d1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:21:10 +0700 Subject: [PATCH 311/374] Update crates/cast/src/cmd/miner.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cast/src/cmd/miner.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cast/src/cmd/miner.rs b/crates/cast/src/cmd/miner.rs index 346efb9a503a0..3b901235258ae 100644 --- a/crates/cast/src/cmd/miner.rs +++ b/crates/cast/src/cmd/miner.rs @@ -23,8 +23,8 @@ where handles.push(std::thread::spawn(move || { #[repr(C)] - struct B256Aligned(B256, [usize; 0]); - +#[repr(C, align(8))] +struct B256Aligned(B256); let mut salt = B256Aligned(salt, []); // SAFETY: `B256` is aligned to `usize`. let salt_word = unsafe { From a35104a60dc4da43d5735e0102b9ae3ef49a4a72 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:21:46 +0700 Subject: [PATCH 312/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/config/src/lib.rs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index c0f8249f37dfd..b0594a23355b3 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1314,19 +1314,42 @@ impl Config { } // Remove last test run failures file. - if let Err(err) = fs::remove_file(&self.test_failures_file) - && err.kind() != io::ErrorKind::NotFound - { + let test_failures_path = if self.test_failures_file.is_absolute() { + self.test_failures_file.clone() + } else { + project.root().join(&self.test_failures_file) + }; + if test_failures_path.starts_with(project.root()) { + if let Err(err) = fs::remove_file(&test_failures_path) + && err.kind() != io::ErrorKind::NotFound + { + warnings.push(format!( + "failed to remove test failures file {}: {err}", + test_failures_path.display() + )); + } + } else { warnings.push(format!( - "failed to remove test failures file {}: {err}", - self.test_failures_file.display() + "skipping removal of test failures file outside project root: {}", + test_failures_path.display() )); } // Remove fuzz and invariant cache directories. let mut remove_test_dir = |test_dir: &Option| { if let Some(test_dir) = test_dir { - let path = project.root().join(test_dir); + let path = if test_dir.is_absolute() { + test_dir.clone() + } else { + project.root().join(test_dir) + }; + if !path.starts_with(project.root()) { + warnings.push(format!( + "skipping removal of test cache directory outside project root: {}", + path.display() + )); + return; + } if let Err(err) = fs::remove_dir_all(&path) && err.kind() != io::ErrorKind::NotFound { From fc3a2aade0fc5ba551daa137b7984e0510a7ab89 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:37:22 +0000 Subject: [PATCH 313/374] Remove duplicate logic in TxSigner::address() implementations (#447) Co-authored-by: Aganis From 4916e016fbaae0bd4a0f4347379bc7da10661039 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:40:27 +0000 Subject: [PATCH 314/374] fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#448) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: googleworkspace-bot From cc8a7ec2661048dc231f273d63ffb0f279ea3e69 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:31:07 +0700 Subject: [PATCH 315/374] Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } From 16480aa5b98fb5435b0f2ce900705cea7028e481 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:31:32 +0700 Subject: [PATCH 316/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From 3820bfc1f104645770ba92c8c1e649f29b650a5f Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:32:21 +0700 Subject: [PATCH 317/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..fc395c963375a 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -19,7 +19,7 @@ use std::{ops::ControlFlow, path::PathBuf}; /// /// # Required Methods /// -/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +/// - `lint`: Scans the provided source files emitting a diagnostic for lints found. pub trait Linter: Send + Sync + Clone { type Language: Language; type Lint: Lint; From 0ba669014b63104795ea18b0ea09a5a41dd7df84 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:32:48 +0700 Subject: [PATCH 318/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index fc395c963375a..0865147167243 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} From 218c62a1afa02aaa69eb3a2fa3601525c9944a44 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 06:38:36 +0700 Subject: [PATCH 319/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From f8b0f1684a91f9eeace13c20fb4f8ac81c452025 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 06:38:57 +0700 Subject: [PATCH 320/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..fc395c963375a 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -19,7 +19,7 @@ use std::{ops::ControlFlow, path::PathBuf}; /// /// # Required Methods /// -/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +/// - `lint`: Scans the provided source files emitting a diagnostic for lints found. pub trait Linter: Send + Sync + Clone { type Language: Language; type Lint: Lint; From aa64de4071b5f39da194da17a80db2fbb8837bb4 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 06:39:15 +0700 Subject: [PATCH 321/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index fc395c963375a..0865147167243 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} From bcf6d774b149610d761c3b048cf9ae68d0695a29 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 00:27:14 +0000 Subject: [PATCH 322/374] feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp --- crates/anvil/src/cmd.rs | 125 +++++++++++++- crates/anvil/src/config.rs | 68 ++++++-- crates/anvil/src/eth/api.rs | 12 +- crates/anvil/src/eth/backend/db.rs | 4 +- crates/anvil/src/eth/backend/fork.rs | 62 ++++--- crates/anvil/src/eth/backend/mem/fork_db.rs | 4 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 12 +- crates/anvil/tests/it/fork.rs | 58 +++++++ crates/common/src/provider/mod.rs | 128 ++++++++++++++- crates/config/src/endpoints.rs | 155 ++++++++++++++++-- crates/config/src/lib.rs | 4 + crates/evm/core/src/fork/database.rs | 2 +- 13 files changed, 565 insertions(+), 71 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 68bd83a1e6e89..fb75529b82908 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -227,6 +227,18 @@ impl NodeArgs { let compute_units_per_second = if self.evm.no_rate_limit { Some(u64::MAX) } else { self.evm.compute_units_per_second }; + // Validate that secondary fork URLs don't have conflicting block number suffixes + if self.evm.fork_url.len() > 1 { + for fork in &self.evm.fork_url[1..] { + if fork.block.is_some() { + eyre::bail!( + "Block number suffixes (@block) on secondary --fork-url values are not supported. \ + Use --fork-block-number to set the fork block for all endpoints." + ); + } + } + } + let hardfork = match &self.hardfork { Some(hf) => { if self.evm.networks.is_optimism() { @@ -260,7 +272,7 @@ impl NodeArgs { _ => self .evm .fork_url - .as_ref() + .first() .and_then(|f| f.block) .map(|num| ForkChoice::Block(num as i128)), }) @@ -270,7 +282,7 @@ impl NodeArgs { .fork_request_retries(self.evm.fork_request_retries) .fork_retry_backoff(self.evm.fork_retry_backoff.map(Duration::from_millis)) .fork_compute_units_per_second(compute_units_per_second) - .with_eth_rpc_url(self.evm.fork_url.map(|fork| fork.url)) + .with_fork_urls(self.evm.fork_url.into_iter().map(|f| f.url).collect()) .with_base_fee(self.evm.block_base_fee_per_gas) .disable_min_priority_fee(self.evm.disable_min_priority_fee) .with_no_storage_caching(self.evm.no_storage_caching) @@ -426,6 +438,10 @@ 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. + /// + /// Multiple `--fork-url` flags can be provided to distribute requests across endpoints + /// using round-robin load balancing. On failure, the retry layer rotates to the next + /// endpoint. #[arg( long, short, @@ -433,7 +449,7 @@ pub struct AnvilEvmArgs { value_name = "URL", help_heading = "Fork config" )] - pub fork_url: Option, + pub fork_url: Vec, /// Headers to use for the rpc client, e.g. "User-Agent: test-agent" /// @@ -630,13 +646,45 @@ pub struct AnvilEvmArgs { /// Resolves an alias passed as fork-url to the matching url defined in the rpc_endpoints section /// of the project configuration file. /// Does nothing if the fork-url is not a configured alias. +/// +/// When an alias maps to an `RpcEndpoint` with multiple `endpoints`, all URLs are expanded +/// into additional `--fork-url` entries for multi-endpoint load balancing. impl AnvilEvmArgs { pub fn resolve_rpc_alias(&mut self) { - if let Some(fork_url) = &self.fork_url - && let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) - && 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 }); + if let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) { + let mut resolved_urls = Vec::new(); + for fork_url in &self.fork_url { + let mut endpoints = config.rpc_endpoints.clone().resolved(); + if let Some(endpoint) = endpoints.remove(&fork_url.url) { + // Alias matched — expand all URLs from the endpoint config + match endpoint.all_urls() { + Ok(urls) => { + for (i, url) in urls.into_iter().enumerate() { + resolved_urls.push(ForkUrl { + url, + // Only the first URL inherits the block suffix + block: if i == 0 { fork_url.block } else { None }, + }); + } + } + Err(e) => { + warn!(target: "node", alias=%fork_url.url, %e, "could not resolve all endpoints, using primary endpoint only"); + if let Ok(url) = endpoint.url() { + resolved_urls.push(ForkUrl { url, block: fork_url.block }); + } else { + resolved_urls.push(fork_url.clone()); + } + } + } + } else if let Some(Ok(url)) = config.get_rpc_url_with_alias(&fork_url.url) { + // Try mesc or other resolution + resolved_urls.push(ForkUrl { url: url.to_string(), block: fork_url.block }); + } else { + // Not an alias — keep as-is + resolved_urls.push(fork_url.clone()); + } + } + self.fork_url = resolved_urls; } } } @@ -965,4 +1013,65 @@ mod tests { ["::1", "1.1.1.1", "2.2.2.2"].map(|ip| ip.parse::().unwrap()).to_vec() ); } + + #[test] + fn can_parse_multiple_fork_urls() { + let args: NodeArgs = NodeArgs::parse_from([ + "anvil", + "--fork-url", + "http://localhost:8545", + "--fork-url", + "http://localhost:8546", + "--fork-url", + "http://localhost:8547", + ]); + assert_eq!(args.evm.fork_url.len(), 3); + assert_eq!(args.evm.fork_url[0].url, "http://localhost:8545"); + assert_eq!(args.evm.fork_url[1].url, "http://localhost:8546"); + assert_eq!(args.evm.fork_url[2].url, "http://localhost:8547"); + + // Block suffix on first URL should work + let args: NodeArgs = NodeArgs::parse_from([ + "anvil", + "--fork-url", + "http://localhost:8545@1000000", + "--fork-url", + "http://localhost:8546", + ]); + assert_eq!(args.evm.fork_url[0].block, Some(1000000)); + assert_eq!(args.evm.fork_url[1].block, None); + } + + #[test] + fn rejects_block_suffix_on_secondary_fork_urls() { + let args: NodeArgs = NodeArgs::parse_from([ + "anvil", + "--fork-url", + "http://localhost:8545@1000000", + "--fork-url", + "http://localhost:8546@2000000", + ]); + let result = args.into_node_config(); + assert!(result.is_err()); + assert!( + result.unwrap_err().to_string().contains("Block number suffixes"), + "should reject block suffix on secondary fork URL" + ); + } + + #[test] + fn fork_dependent_args_require_fork_url() { + // All these args have `requires = "fork_url"` — they should fail without --fork-url + let cases = [ + vec!["anvil", "--fork-header", "X-Api-Key: test"], + vec!["anvil", "--timeout", "5000"], + vec!["anvil", "--retries", "3"], + vec!["anvil", "--fork-block-number", "100"], + vec!["anvil", "--fork-retry-backoff", "500"], + ]; + for args in &cases { + let result = NodeArgs::try_parse_from(args); + assert!(result.is_err(), "expected error when using {:?} without --fork-url", args[1]); + } + } } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index c9b63f08cb2f5..23cd6e61bc076 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -134,11 +134,13 @@ pub struct NodeConfig { pub port: u16, /// maximum number of transactions in a block pub max_transactions: usize, - /// url of the rpc server that should be used for any rpc calls - pub eth_rpc_url: Option, + /// Fork URLs for RPC calls. The first entry is the primary endpoint. + /// When multiple URLs are provided, requests are distributed using + /// round-robin load balancing with retry-based failover. + pub fork_urls: Vec, /// pins the block number or transaction hash for the state fork pub fork_choice: Option, - /// headers to use with `eth_rpc_url` + /// headers to use with fork RPC endpoints pub fork_headers: Vec, /// specifies chain id for cache to skip fetching from remote in offline-start mode pub fork_chain_id: Option, @@ -268,12 +270,19 @@ Block number: {} Block hash: {:?} Chain ID: {} "#, - fork.eth_rpc_url(), + fork.eth_rpc_url().as_deref().unwrap_or("none"), fork.block_number(), fork.block_hash(), fork.chain_id() ); + if self.fork_urls.len() > 1 { + let _ = writeln!(s, "Endpoints: {}", self.fork_urls.len()); + for (i, url) in self.fork_urls.iter().enumerate() { + let _ = writeln!(s, " ({i}) {url}"); + } + } + if let Some(tx_hash) = fork.transaction_hash() { let _ = writeln!(s, "Transaction hash: {tx_hash}"); } @@ -393,7 +402,7 @@ Genesis Number json!({ "available_accounts": available_accounts, "private_keys": private_keys, - "endpoint": fork.eth_rpc_url(), + "endpoint": fork.eth_rpc_url().unwrap_or_default(), "block_number": fork.block_number(), "block_hash": fork.block_hash(), "chain_id": fork.chain_id(), @@ -466,7 +475,7 @@ impl Default for NodeConfig { mixed_mining: false, port: NODE_PORT, max_transactions: 1_000, - eth_rpc_url: None, + fork_urls: vec![], fork_choice: None, account_generator: None, base_fee: None, @@ -855,10 +864,19 @@ impl NodeConfig { self } - /// Sets the `eth_rpc_url` to use when forking + /// Sets the `eth_rpc_url` to use when forking (single endpoint convenience). #[must_use] pub fn with_eth_rpc_url>(mut self, eth_rpc_url: Option) -> Self { - self.eth_rpc_url = eth_rpc_url.map(Into::into); + if let Some(url) = eth_rpc_url { + self.fork_urls = vec![url.into()]; + } + self + } + + /// Sets the fork URLs for load-balanced multi-endpoint forking. + #[must_use] + pub fn with_fork_urls(mut self, fork_urls: Vec) -> Self { + self.fork_urls = fork_urls; self } @@ -891,7 +909,7 @@ impl NodeConfig { self } - /// Sets the `fork_headers` to use with `eth_rpc_url` + /// Sets the `fork_headers` to use with fork RPC endpoints #[must_use] pub fn with_fork_headers(mut self, headers: Vec) -> Self { self.fork_headers = headers; @@ -1017,7 +1035,7 @@ impl NodeConfig { /// /// See also [ Config::foundry_block_cache_file()] pub fn block_cache_path(&self, block: u64) -> Option { - if self.no_storage_caching || self.eth_rpc_url.is_none() { + if self.no_storage_caching || self.fork_urls.is_empty() { return None; } let chain_id = self.get_chain_id(); @@ -1145,7 +1163,7 @@ impl NodeConfig { ); let (db, fork): (Arc>>, Option) = - if let Some(eth_rpc_url) = self.eth_rpc_url.clone() { + if let Some(eth_rpc_url) = self.fork_urls.first().cloned() { self.setup_fork_db(eth_rpc_url, &mut evm_env, &fees).await? } else { (Arc::new(TokioRwLock::new(Box::::default())), None) @@ -1208,7 +1226,7 @@ impl NodeConfig { // Writes the default create2 deployer to the backend, // if the option is not disabled and we are not forking. - if !self.disable_default_create2_deployer && self.eth_rpc_url.is_none() { + if !self.disable_default_create2_deployer && self.fork_urls.is_empty() { backend .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER) .await @@ -1248,6 +1266,10 @@ impl NodeConfig { fees: &FeeManager, ) -> Result<(ForkedDatabase, ClientForkConfig)> { debug!(target: "node", ?eth_rpc_url, "setting up fork db"); + + // Always bootstrap with the primary URL only to avoid race conditions + // where discovery calls (get_chain_id, find_latest_fork_block, get_block) + // hit different endpoints that may be at different chain tips. let provider = Arc::new( ProviderBuilder::new(ð_rpc_url) .timeout(self.fork_request_timeout) @@ -1409,6 +1431,25 @@ latest block number: {latest_block}" BlockchainDb::new(meta, self.block_cache_path(fork_block_number)) }; + // After bootstrap, rebuild the provider with round-robin if multiple URLs are + // configured. This ensures bootstrap used only the primary endpoint for consistency, + // while ongoing requests are distributed across all endpoints. + let provider = if self.fork_urls.len() > 1 { + debug!(target: "node", urls=?self.fork_urls, "using multi-endpoint round-robin provider"); + Arc::new( + ProviderBuilder::new(ð_rpc_url) + .timeout(self.fork_request_timeout) + .initial_backoff(self.fork_retry_backoff.as_millis() as u64) + .compute_units_per_second(self.compute_units_per_second) + .max_retry(self.fork_request_retries) + .headers(self.fork_headers.clone()) + .build_fallback(self.fork_urls.clone()) + .wrap_err("failed to establish round-robin provider to fork urls")?, + ) + } else { + provider + }; + // This will spawn the background thread that will use the provider to fetch // blockchain data from the other client let backend = SharedBackend::spawn_backend( @@ -1419,7 +1460,7 @@ latest block number: {latest_block}" .await; let config = ClientForkConfig { - eth_rpc_url, + fork_urls: self.fork_urls.clone(), block_number: fork_block_number, block_hash, transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()), @@ -1432,6 +1473,7 @@ latest block number: {latest_block}" retries: self.fork_request_retries, backoff: self.fork_retry_backoff, compute_units_per_second: self.compute_units_per_second, + headers: self.fork_headers.clone(), total_difficulty: block.header.total_difficulty.unwrap_or_default(), blob_gas_used: block.header.blob_gas_used().map(|g| g as u128), blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price, diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index f48a2d96cc12c..0a0fd97d06556 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -416,7 +416,7 @@ impl EthApi { let config = fork.config.read(); NodeForkConfig { - fork_url: Some(config.eth_rpc_url.clone()), + fork_url: config.eth_rpc_url().map(|s| s.to_string()), fork_block_number: Some(config.block_number), fork_retry_backoff: Some(config.backoff.as_millis()), } @@ -527,7 +527,7 @@ impl EthApi { /// Sets the backend rpc url /// /// Handler for ETH RPC call: `anvil_setRpcUrl` - pub fn anvil_set_rpc_url(&self, url: String) -> Result<()> { + pub async fn anvil_set_rpc_url(&self, url: String) -> Result<()> { node_info!("anvil_setRpcUrl"); if let Some(fork) = self.backend.get_fork() { let mut config = fork.config.write(); @@ -543,9 +543,11 @@ impl EthApi { )?, // .interval(interval), ); config.provider = new_provider; - trace!(target: "backend", "Updated fork rpc from \"{}\" to \"{}\"", config.eth_rpc_url, url); - config.eth_rpc_url = url; + trace!(target: "backend", "Updated fork rpc from \"{}\" to \"{}\"", config.eth_rpc_url().unwrap_or("none"), url); + config.fork_urls = vec![url.clone()]; } + // Keep node_config in sync so anvil_reset(None) uses the updated URL + self.backend.node_config.write().await.fork_urls = vec![url]; Ok(()) } @@ -1791,7 +1793,7 @@ impl EthApi { EthRequest::EvmMineDetailed(mine) => { self.evm_mine_detailed(mine.and_then(|p| p.params)).await.to_rpc_result() } - EthRequest::SetRpcUrl(url) => self.anvil_set_rpc_url(url).to_rpc_result(), + EthRequest::SetRpcUrl(url) => self.anvil_set_rpc_url(url).await.to_rpc_result(), EthRequest::EthSendUnsignedTransaction(tx) => { self.eth_send_unsigned_transaction(*tx).await.to_rpc_result() } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index de9ad434252f3..d1c842d12d108 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -84,7 +84,7 @@ where /// Helper trait to reset the DB if it's forked pub trait MaybeForkedDatabase { - fn maybe_reset(&mut self, _url: Option, block_number: BlockId) -> Result<(), String>; + fn maybe_reset(&mut self, _urls: Vec, block_number: BlockId) -> Result<(), String>; fn maybe_flush_cache(&self) -> Result<(), String>; @@ -375,7 +375,7 @@ impl + Debug> MaybeFullDatabase for CacheD } impl> MaybeForkedDatabase for CacheDB { - fn maybe_reset(&mut self, _url: Option, _block_number: BlockId) -> Result<(), String> { + fn maybe_reset(&mut self, _urls: Vec, _block_number: BlockId) -> Result<(), String> { Err("not supported".to_string()) } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index b1bff4e66cd21..bc87fe2fc2052 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -97,8 +97,8 @@ impl ClientFork { self.config.read().block_hash } - pub fn eth_rpc_url(&self) -> String { - self.config.read().eth_rpc_url.clone() + pub fn eth_rpc_url(&self) -> Option { + self.config.read().eth_rpc_url().map(|s| s.to_string()) } pub fn chain_id(&self) -> u64 { @@ -269,7 +269,7 @@ impl ClientFork { /// Reset the fork to a fresh forked state, and optionally update the fork config pub async fn reset( &self, - url: Option, + urls: Vec, block_number: impl Into, ) -> Result<(), BlockchainError> { let block_number = block_number.into(); @@ -277,12 +277,12 @@ impl ClientFork { self.database .write() .await - .maybe_reset(url.clone(), block_number) + .maybe_reset(urls.clone(), block_number) .map_err(BlockchainError::Internal)?; } - if let Some(url) = url { - self.config.write().update_url(url)?; + if !urls.is_empty() { + self.config.write().update_urls(urls)?; let override_chain_id = self.config.read().override_chain_id; let chain_id = if let Some(chain_id) = override_chain_id { chain_id @@ -629,7 +629,10 @@ impl ClientFork { /// Contains all fork metadata #[derive(Clone, Debug)] pub struct ClientForkConfig { - pub eth_rpc_url: String, + /// All fork URLs. The first entry is the primary endpoint. + /// When multiple URLs are present, requests are distributed using + /// round-robin load balancing with retry-based failover. + pub fork_urls: Vec, /// The block number of the forked block pub block_number: u64, /// The hash of the forked block @@ -655,6 +658,8 @@ pub struct ClientForkConfig { pub backoff: Duration, /// available CUPS pub compute_units_per_second: u64, + /// Headers to include with RPC requests + pub headers: Vec, /// total difficulty of the chain until this block pub total_difficulty: U256, /// Transactions to force include in the forked chain @@ -662,27 +667,40 @@ pub struct ClientForkConfig { } impl ClientForkConfig { - /// Updates the provider URL + /// Returns the primary RPC URL (first entry in `fork_urls`). + pub fn eth_rpc_url(&self) -> Option<&str> { + self.fork_urls.first().map(|s| s.as_str()) + } + + /// Updates the provider URLs /// /// # Errors /// /// This will fail if no new provider could be established (erroneous URL) - fn update_url(&mut self, url: String) -> Result<(), BlockchainError> { - // let interval = self.provider.get_interval(); - self.provider = Arc::new( - ProviderBuilder::::new(url.as_str()) - .timeout(self.timeout) - // .timeout_retry(self.retries) - .max_retry(self.retries) - .initial_backoff(self.backoff.as_millis() as u64) - .compute_units_per_second(self.compute_units_per_second) - .build() - .map_err(|e| BlockchainError::InvalidUrl(format!("{url}: {e}")))?, /* .interval(interval), */ - ); - trace!(target: "fork", "Updated rpc url {}", url); - self.eth_rpc_url = url; + fn update_urls(&mut self, urls: Vec) -> Result<(), BlockchainError> { + let primary = urls.first().ok_or_else(|| { + BlockchainError::InvalidUrl("at least one fork URL required".to_string()) + })?; + + let builder = ProviderBuilder::::new(primary.as_str()) + .timeout(self.timeout) + .max_retry(self.retries) + .initial_backoff(self.backoff.as_millis() as u64) + .compute_units_per_second(self.compute_units_per_second) + .headers(self.headers.clone()); + + self.provider = Arc::new(if urls.len() > 1 { + builder + .build_fallback(urls.clone()) + .map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))? + } else { + builder.build().map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))? + }); + trace!(target: "fork", "Updated fork urls: {:?}", urls); + self.fork_urls = urls; Ok(()) } + /// Updates the block forked off `(block number, block hash, timestamp)` pub fn update_block( &mut self, diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 308cdd2e4334c..497e8ac8e4428 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -156,8 +156,8 @@ impl MaybeFullDatabase for ForkDbStateSnapshot { } impl MaybeForkedDatabase for ForkedDatabase { - fn maybe_reset(&mut self, url: Option, block_number: BlockId) -> Result<(), String> { - self.reset(url, block_number) + fn maybe_reset(&mut self, urls: Vec, block_number: BlockId) -> Result<(), String> { + self.reset(urls, block_number) } fn maybe_flush_cache(&self) -> Result<(), String> { 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 766805ee7ae9d..1d06ad03cae2f 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -128,7 +128,7 @@ impl MaybeFullDatabase for MemDb { } impl MaybeForkedDatabase for MemDb { - fn maybe_reset(&mut self, _url: Option, _block_number: BlockId) -> Result<(), String> { + fn maybe_reset(&mut self, _urls: Vec, _block_number: BlockId) -> Result<(), String> { Err("not supported".to_string()) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index ed928ef25213d..b538086fe6f10 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -246,7 +246,7 @@ pub struct Backend { prune_state_history_config: PruneStateHistoryConfig, /// max number of blocks with transactions in memory transaction_block_keeper: Option, - node_config: Arc>, + pub(crate) node_config: Arc>, /// Slots in an epoch slots_in_an_epoch: u64, /// Precompiles to inject to the EVM. @@ -2079,6 +2079,7 @@ impl Backend { // we want to force the correct base fee for the next block during // `setup_fork_db_config` node_config.base_fee.take(); + node_config.fork_urls = vec![eth_rpc_url.clone()]; node_config.setup_fork_db_config(eth_rpc_url, &mut evm_env, &self.fees).await? }; @@ -2101,7 +2102,9 @@ impl Backend { let block_number = forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest); // reset the fork entirely and reapply the genesis config - fork.reset(forking.json_rpc_url.clone(), block_number).await?; + let reset_urls = + forking.json_rpc_url.as_ref().map(|url| vec![url.clone()]).unwrap_or_default(); + fork.reset(reset_urls, block_number).await?; let fork_block_number = fork.block_number(); let fork_block = fork .block_by_number(fork_block_number) @@ -2115,7 +2118,8 @@ impl Backend { // If rpc url is unspecified, then update the fork with the new block number and // existing rpc url, this updates the cache path { - let maybe_fork_url = { self.node_config.read().await.eth_rpc_url.clone() }; + let maybe_fork_url = + { self.node_config.read().await.fork_urls.first().cloned() }; if let Some(fork_url) = maybe_fork_url { self.reset_block_number(fork_url, fork_block_number).await?; } @@ -2229,6 +2233,8 @@ impl Backend { ) -> Result<(), BlockchainError> { let mut node_config = self.node_config.write().await; node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128)); + // Update fork_urls so setup_fork_db_config uses the correct URL set + node_config.fork_urls = vec![fork_url.clone()]; let mut evm_env = self.evm_env.read().clone(); let (forked_db, client_fork_config) = diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index e802d8a19be7b..37d3f6f4a1dc4 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -2011,3 +2011,61 @@ async fn test_config_with_osaka_hardfork_with_precompile_factory() { &expected_system_contracts, ); } + +// Regression tests: verify that `anvil_setRpcUrl` and `anvil_reset` keep +// `ClientForkConfig.fork_urls` in sync so that subsequent resets don't +// silently revert to stale URLs. + +#[tokio::test(flavor = "multi_thread")] +async fn test_anvil_set_rpc_url_syncs_fork_config() { + // Spawn an origin node and fork off it + let (_origin_api, origin_handle) = spawn(NodeConfig::test()).await; + let origin_url = origin_handle.http_endpoint(); + + let (api, _handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_url.clone()))).await; + + // Verify initial fork URL + let fork = api.backend.get_fork().unwrap(); + assert_eq!(fork.config.read().fork_urls, vec![origin_url.clone()]); + + // Spawn a second origin to use as the new URL + let (_origin2_api, origin2_handle) = spawn(NodeConfig::test()).await; + let new_url = origin2_handle.http_endpoint(); + + // Set RPC URL via the API + api.anvil_set_rpc_url(new_url.clone()).await.unwrap(); + + // Verify ClientForkConfig is updated + let fork = api.backend.get_fork().unwrap(); + assert_eq!( + fork.config.read().fork_urls, + vec![new_url.clone()], + "ClientForkConfig.fork_urls should be updated after anvil_setRpcUrl" + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_anvil_reset_with_url_updates_fork_urls() { + // Spawn an origin node and fork off it + let (_origin_api, origin_handle) = spawn(NodeConfig::test()).await; + let origin_url = origin_handle.http_endpoint(); + + let (api, _handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_url.clone()))).await; + + // Spawn a second origin + let (_origin2_api, origin2_handle) = spawn(NodeConfig::test()).await; + let new_url = origin2_handle.http_endpoint(); + + // Reset fork with a new URL + api.anvil_reset(Some(Forking { json_rpc_url: Some(new_url.clone()), block_number: None })) + .await + .unwrap(); + + // Verify the fork config uses the new URL, not the old one + let fork = api.backend.get_fork().unwrap(); + assert_eq!( + fork.config.read().fork_urls, + vec![new_url.clone()], + "ClientForkConfig.fork_urls should reflect the new URL after anvil_reset" + ); +} diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 3b7432cd67c55..1620342989a29 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -9,6 +9,7 @@ use crate::{ provider::{curl_transport::CurlTransport, runtime_transport::RuntimeTransportBuilder}, }; use alloy_chains::NamedChain; +use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_network::{Network, NetworkWallet}; use alloy_provider::{ Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, @@ -16,7 +17,9 @@ use alloy_provider::{ network::{AnyNetwork, EthereumWallet}, }; use alloy_rpc_client::ClientBuilder; -use alloy_transport::{layers::RetryBackoffLayer, utils::guess_local_url}; +use alloy_transport::{ + TransportError, TransportFut, layers::RetryBackoffLayer, utils::guess_local_url, +}; use eyre::{Result, WrapErr}; use foundry_config::Config; use reqwest::Url; @@ -25,8 +28,14 @@ use std::{ net::SocketAddr, path::{Path, PathBuf}, str::FromStr, + sync::{ + Arc, + atomic::{AtomicUsize, Ordering}, + }, + task::{Context, Poll}, time::Duration, }; +use tower::Service; use url::ParseError; /// The assumed block time for unknown chains. @@ -75,6 +84,56 @@ pub fn try_get_http_provider(builder: impl AsRef) -> Result ProviderBuilder::new(builder.as_ref()).build() } +/// A round-robin transport that distributes requests across multiple transports. +/// +/// Each request is sent to exactly one transport, rotating through the list. +/// Failover on error is handled by the retry layer above this service. +#[derive(Clone)] +pub struct RoundRobinService { + transports: Arc>, + next: Arc, +} + +impl RoundRobinService { + /// Creates a new round-robin service from a non-empty list of transports. + /// + /// # Panics + /// + /// Panics if `transports` is empty. + pub fn new(transports: Vec) -> Self { + assert!(!transports.is_empty(), "RoundRobinService requires at least one transport"); + Self { transports: Arc::new(transports), next: Arc::new(AtomicUsize::new(0)) } + } +} + +impl Service for RoundRobinService +where + S: Service< + RequestPacket, + Response = ResponsePacket, + Error = TransportError, + Future = TransportFut<'static>, + > + Clone + + Send + + Sync + + 'static, +{ + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: RequestPacket) -> Self::Future { + let transports = self.transports.clone(); + let idx = self.next.fetch_add(1, Ordering::Relaxed) % transports.len(); + let mut transport = transports[idx].clone(); + transport.call(req) + } +} + /// Helper type to construct a `RetryProvider` /// /// This builder is generic over the network type `N`, defaulting to `AnyNetwork`. @@ -368,6 +427,73 @@ impl ProviderBuilder { } impl ProviderBuilder { + /// Constructs a `RetryProvider` backed by multiple URLs using round-robin load balancing. + /// + /// Each request is sent to exactly one transport, rotating through the list via + /// [`RoundRobinService`]. There is no health scoring or endpoint deprioritization. + /// On failure, the `RetryBackoffLayer` retries the request, which naturally hits + /// the next transport in the rotation. + pub fn build_fallback(self, urls: Vec) -> Result> { + let Self { + chain, + max_retry, + initial_backoff, + timeout, + compute_units_per_second, + jwt, + headers, + accept_invalid_certs, + no_proxy, + curl_mode, + .. + } = self; + + eyre::ensure!(!urls.is_empty(), "at least one fork URL is required"); + eyre::ensure!(!curl_mode, "curl mode is not supported with multiple fork URLs"); + + // Build a RuntimeTransport for each URL, using the same URL normalization + // as ProviderBuilder::new() (handles localhost:port, raw socket addrs, IPC paths) + let mut parsed_urls = Vec::with_capacity(urls.len()); + let transports: Vec<_> = urls + .iter() + .map(|url_str| { + let builder = Self::new(url_str); + let url = builder.url?; + parsed_urls.push(url.clone()); + Ok(RuntimeTransportBuilder::new(url) + .with_timeout(timeout) + .with_headers(headers.clone()) + .with_jwt(jwt.clone()) + .accept_invalid_certs(accept_invalid_certs) + .no_proxy(no_proxy) + .build()) + }) + .collect::>>()?; + + let round_robin = RoundRobinService::new(transports); + + let retry_layer = + RetryBackoffLayer::new(max_retry, initial_backoff, compute_units_per_second); + // Use normalized/parsed URLs for local detection, consistent with build() + let is_local = parsed_urls.iter().all(|url| guess_local_url(url.as_str())); + let client = ClientBuilder::default().layer(retry_layer).transport(round_robin, is_local); + + if !is_local { + client.set_poll_interval( + chain + .average_blocktime_hint() + .map(|hint| hint.min(DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME)) + .unwrap_or(DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME) + .mul_f32(POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR), + ); + } + + let provider = + AlloyProviderBuilder::<_, _, N>::default().connect_provider(RootProvider::new(client)); + + Ok(provider) + } + /// Constructs the `RetryProvider` with a wallet. pub fn build_with_wallet + Clone>( self, diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index 64ee7e05fc75f..60a15206bc51a 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -277,6 +277,11 @@ pub struct RpcEndpoint { /// endpoint url or env pub endpoint: RpcEndpointUrl, + /// Additional fallback endpoints for load-balanced multi-endpoint forking. + /// When set, requests are distributed across all endpoints (primary + extra) + /// with automatic failover. + pub extra_endpoints: Vec, + /// Token to be used as authentication pub auth: Option, @@ -293,6 +298,7 @@ impl RpcEndpoint { pub fn resolve(self) -> ResolvedRpcEndpoint { ResolvedRpcEndpoint { endpoint: self.endpoint.resolve(), + extra_endpoints: self.extra_endpoints.into_iter().map(|e| e.resolve()).collect(), auth: self.auth.map(|auth| auth.resolve()), config: self.config, } @@ -301,7 +307,7 @@ impl RpcEndpoint { impl fmt::Display for RpcEndpoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { endpoint, auth, config } = self; + let Self { endpoint, auth, config, .. } = self; write!(f, "{endpoint}")?; write!(f, "{config}")?; if let Some(auth) = auth { @@ -316,16 +322,24 @@ impl Serialize for RpcEndpoint { where S: Serializer, { - if self.config.retries.is_none() - && self.config.retry_backoff.is_none() - && self.config.compute_units_per_second.is_none() - && self.auth.is_none() - { - // serialize as endpoint if there's no additional config + let has_config = self.config.retries.is_some() + || self.config.retry_backoff.is_some() + || self.config.compute_units_per_second.is_some() + || self.auth.is_some(); + + if !has_config && self.extra_endpoints.is_empty() { + // serialize as plain endpoint string if there's no additional config self.endpoint.serialize(serializer) } else { - let mut map = serializer.serialize_map(Some(5))?; - map.serialize_entry("endpoint", &self.endpoint)?; + let mut map = serializer.serialize_map(None)?; + if self.extra_endpoints.is_empty() { + map.serialize_entry("endpoint", &self.endpoint)?; + } else { + // Serialize all endpoints as an array under "endpoints" + let all: Vec<&RpcEndpointUrl> = + std::iter::once(&self.endpoint).chain(&self.extra_endpoints).collect(); + map.serialize_entry("endpoints", &all)?; + } map.serialize_entry("retries", &self.config.retries)?; map.serialize_entry("retry_backoff", &self.config.retry_backoff)?; map.serialize_entry("compute_units_per_second", &self.config.compute_units_per_second)?; @@ -348,10 +362,13 @@ impl<'de> Deserialize<'de> for RpcEndpoint { }); } + // Support both single "endpoint" and array "endpoints" for backwards compatibility #[derive(Deserialize)] struct RpcEndpointConfigInner { #[serde(alias = "url")] - endpoint: RpcEndpointUrl, + endpoint: Option, + /// Array of endpoint URLs for multi-endpoint load balancing + endpoints: Option>, retries: Option, retry_backoff: Option, compute_units_per_second: Option, @@ -360,14 +377,43 @@ impl<'de> Deserialize<'de> for RpcEndpoint { let RpcEndpointConfigInner { endpoint, + endpoints, retries, retry_backoff, compute_units_per_second, auth, } = serde_json::from_value(value).map_err(serde::de::Error::custom)?; + let (primary, extra) = match (endpoint, endpoints) { + // Single endpoint: endpoint = "..." + (Some(ep), None) => (ep, vec![]), + // Array of endpoints: endpoints = ["...", "..."] + (None, Some(mut eps)) => { + if eps.is_empty() { + return Err(serde::de::Error::custom( + "endpoints array must contain at least one URL", + )); + } + let primary = eps.remove(0); + (primary, eps) + } + // Both provided — error + (Some(_), Some(_)) => { + return Err(serde::de::Error::custom( + "cannot specify both `endpoint` and `endpoints`", + )); + } + // Neither provided — error + (None, None) => { + return Err(serde::de::Error::custom( + "must specify either `endpoint` or `endpoints`", + )); + } + }; + Ok(Self { - endpoint, + endpoint: primary, + extra_endpoints: extra, auth, config: RpcEndpointConfig { retries, retry_backoff, compute_units_per_second }, }) @@ -384,6 +430,7 @@ impl Default for RpcEndpoint { fn default() -> Self { Self { endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig::default(), auth: None, } @@ -394,21 +441,38 @@ impl Default for RpcEndpoint { #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResolvedRpcEndpoint { pub endpoint: Result, + /// Additional resolved endpoints for multi-endpoint load balancing. + pub extra_endpoints: Vec>, pub auth: Option>, pub config: RpcEndpointConfig, } impl ResolvedRpcEndpoint { - /// Returns the url this type holds, see [`RpcEndpoint::resolve`] + /// Returns the primary url this type holds, see [`RpcEndpoint::resolve`] pub fn url(&self) -> Result { self.endpoint.clone() } + /// Returns all resolved URLs (primary + extra) for multi-endpoint configurations. + /// Returns an empty vec if no extra endpoints are configured. + pub fn all_urls(&self) -> Result, UnresolvedEnvVarError> { + let primary = self.endpoint.clone()?; + if self.extra_endpoints.is_empty() { + return Ok(vec![primary]); + } + let mut urls = vec![primary]; + for ep in &self.extra_endpoints { + urls.push(ep.clone()?); + } + Ok(urls) + } + // Returns true if all environment variables are resolved successfully pub fn is_unresolved(&self) -> bool { let endpoint_err = self.endpoint.is_err(); + let extra_err = self.extra_endpoints.iter().any(|e| e.is_err()); let auth_err = self.auth.as_ref().map(|auth| auth.is_err()).unwrap_or(false); - endpoint_err || auth_err + endpoint_err || extra_err || auth_err } // Attempts to resolve unresolved environment variables into a new instance @@ -419,6 +483,11 @@ impl ResolvedRpcEndpoint { if let Err(err) = self.endpoint { self.endpoint = err.try_resolve() } + for ep in &mut self.extra_endpoints { + if let Err(err) = std::mem::replace(ep, Ok(String::new())) { + *ep = err.try_resolve(); + } + } if let Some(Err(err)) = self.auth { self.auth = Some(err.try_resolve()) } @@ -483,6 +552,7 @@ mod tests { config, RpcEndpoint { endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(5), retry_backoff: Some(250), @@ -498,6 +568,7 @@ mod tests { config, RpcEndpoint { endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: None, retry_backoff: None, @@ -507,4 +578,62 @@ mod tests { } ); } + + #[test] + fn serde_rpc_config_multi_endpoints() { + // Array of endpoints via "endpoints" key + let s = r#"{ + "endpoints": ["https://rpc1.example.com", "https://rpc2.example.com", "https://rpc3.example.com"], + "retries": 5, + "retry_backoff": 1000 + }"#; + let config: RpcEndpoint = serde_json::from_str(s).unwrap(); + assert_eq!( + config, + RpcEndpoint { + endpoint: RpcEndpointUrl::Url("https://rpc1.example.com".to_string()), + extra_endpoints: vec![ + RpcEndpointUrl::Url("https://rpc2.example.com".to_string()), + RpcEndpointUrl::Url("https://rpc3.example.com".to_string()), + ], + config: RpcEndpointConfig { + retries: Some(5), + retry_backoff: Some(1000), + compute_units_per_second: None, + }, + auth: None, + } + ); + + // Resolved URLs + let resolved = config.resolve(); + let all_urls = resolved.all_urls().unwrap(); + assert_eq!( + all_urls, + vec![ + "https://rpc1.example.com".to_string(), + "https://rpc2.example.com".to_string(), + "https://rpc3.example.com".to_string(), + ] + ); + } + + #[test] + fn serde_rpc_config_rejects_both_endpoint_and_endpoints() { + let s = r#"{ + "endpoint": "https://rpc1.example.com", + "endpoints": ["https://rpc2.example.com"] + }"#; + let result: Result = serde_json::from_str(s); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("cannot specify both")); + } + + #[test] + fn serde_rpc_config_rejects_empty_endpoints() { + let s = r#"{ "endpoints": [] }"#; + let result: Result = serde_json::from_str(s); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("at least one URL")); + } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index c0f8249f37dfd..f148432c42bed 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -3681,6 +3681,7 @@ mod tests { "mainnet", RpcEndpointType::Config(RpcEndpoint { endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), @@ -3706,6 +3707,7 @@ mod tests { "mainnet", RpcEndpointType::Config(RpcEndpoint { endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), @@ -3753,6 +3755,7 @@ mod tests { "mainnet", RpcEndpointType::Config(RpcEndpoint { endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), @@ -3779,6 +3782,7 @@ mod tests { endpoint: RpcEndpointUrl::Url( "https://eth-mainnet.alchemyapi.io/v2/123455".to_string() ), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 6f539d356e5a9..aefa0e2ee9741 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -71,7 +71,7 @@ impl ForkedDatabase { /// Reset the fork to a fresh forked state, and optionally update the fork config pub fn reset( &mut self, - _url: Option, + _urls: Vec, block_number: impl Into, ) -> Result<(), String> { self.backend.set_pinned_block(block_number).map_err(|err| err.to_string())?; From 0203943fd04808b5ee7ac58e7288c64cf0c34755 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 07:38:52 +0700 Subject: [PATCH 323/374] Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } From d41b319ba8f3dc5a66d39c488db79d2fd3f8a4a3 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 07:39:21 +0700 Subject: [PATCH 324/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From bec42f81b6a03cb6cddc2547f5e27d4e67eab687 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 07:39:39 +0700 Subject: [PATCH 325/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..fc395c963375a 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -19,7 +19,7 @@ use std::{ops::ControlFlow, path::PathBuf}; /// /// # Required Methods /// -/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +/// - `lint`: Scans the provided source files emitting a diagnostic for lints found. pub trait Linter: Send + Sync + Clone { type Language: Language; type Lint: Lint; From 88c60a66dbce3327e4cd4fd793bee056c1096267 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 07:39:58 +0700 Subject: [PATCH 326/374] Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/lint/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index fc395c963375a..0865147167243 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} From c679ca726e9c519a8ee466a43885fb8e25ffb0b5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:34:13 +0000 Subject: [PATCH 327/374] Add docs, sidebar assets and Foundry deps (#459) * Add docs, sidebar assets and Foundry deps Add generated documentation assets (doc-script.js, doc-style.css, doc-filelist.js) under both doc/ and counter/doc/ to provide a collapsible sidebar/tree UI. Add counter/.gas-snapshot test gas output and update counter/.gitignore to ignore Soldeer dependencies. Update counter/foundry.toml to include a dependencies section and forge-std, and add remappings.txt plus soldeer.lock to pin the forge-std dependency. * Update counter/doc/doc-script.js Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- counter/.gas-snapshot | 2 + counter/.gitignore | 4 + counter/doc/doc-filelist.js | 1 + counter/doc/doc-script.js | 228 ++++++++++++++++++++ counter/doc/doc-style.css | 403 ++++++++++++++++++++++++++++++++++++ counter/foundry.toml | 5 +- counter/remappings.txt | 1 + counter/soldeer.lock | 6 + 8 files changed, 649 insertions(+), 1 deletion(-) create mode 100644 counter/.gas-snapshot create mode 100644 counter/doc/doc-filelist.js create mode 100644 counter/doc/doc-script.js create mode 100644 counter/doc/doc-style.css create mode 100644 counter/remappings.txt create mode 100644 counter/soldeer.lock diff --git a/counter/.gas-snapshot b/counter/.gas-snapshot new file mode 100644 index 0000000000000..ef525c09384e6 --- /dev/null +++ b/counter/.gas-snapshot @@ -0,0 +1,2 @@ +CounterTest:testFuzz_SetNumber(uint256) (runs: 256, μ: 30177, ~: 32354) +CounterTest:test_Increment() (gas: 31851) \ No newline at end of file diff --git a/counter/.gitignore b/counter/.gitignore index 85198aaa55b84..052b88bb6516b 100644 --- a/counter/.gitignore +++ b/counter/.gitignore @@ -12,3 +12,7 @@ docs/ # Dotenv file .env + + +# Soldeer +/dependencies diff --git a/counter/doc/doc-filelist.js b/counter/doc/doc-filelist.js new file mode 100644 index 0000000000000..c2a398ff94c23 --- /dev/null +++ b/counter/doc/doc-filelist.js @@ -0,0 +1 @@ +var tree={}; \ No newline at end of file diff --git a/counter/doc/doc-script.js b/counter/doc/doc-script.js new file mode 100644 index 0000000000000..62eeda3efc308 --- /dev/null +++ b/counter/doc/doc-script.js @@ -0,0 +1,228 @@ +// # res/script.js +// +// This is the script file that gets copied into the output. It mainly manages the display +// of the folder tree. The idea of this script file is to be minimal and standalone. So +// that means no jQuery. + +// Use localStorage to store data about the tree's state: whether or not +// the tree is visible and which directories are expanded. Unless the state +let sidebarVisible = (window.localStorage && window.localStorage.docker_showSidebar) ? + window.localStorage.docker_showSidebar == 'yes' : + defaultSidebar; + +/** + * ## makeTree + * + * Consructs the folder tree view + * + * @param {object} treeData Folder structure as in [queueFile](../src/docker.js.html#docker.prototype.queuefile) + * @param {string} root Path from current file to root (ie `'../../'` etc.) + * @param {string} filename The current file name + */ +function makeTree(treeData, root, filename) { + var treeNode = document.getElementById('tree'); + var treeHandle = document.getElementById('sidebar-toggle'); + treeHandle.addEventListener('click', toggleTree, false); + + // Build the html and add it to the container. + treeNode.innerHTML = nodeHtml('', treeData, '', root); + + // Root folder (whole tree) should always be open + treeNode.childNodes[0].className += ' open'; + + // Attach click event handler + treeNode.addEventListener('click', nodeClicked, false); + + if (sidebarVisible) document.body.className += ' sidebar'; + + // Restore scroll position from localStorage if set. And attach scroll handler + if (window.localStorage && window.localStorage.docker_treeScroll) treeNode.scrollTop = window.localStorage.docker_treeScroll; + treeNode.onscroll = treeScrolled; + + // Only set a class to allow CSS transitions after the tree state has been painted + setTimeout(function() { document.body.className += ' slidey'; }, 100); +} + +/** + * ## treeScrolled + * + * Called when the tree is scrolled. Stores the scroll position in localStorage + * so it can be restored on the next pageview. + */ +function treeScrolled() { + var tree = document.getElementById('tree'); + if (window.localStorage) window.localStorage.docker_treeScroll = tree.scrollTop; +} + +/** + * ## nodeClicked + * + * Called when a directory is clicked. Toggles open state of the directory + * + * @param {Event} e The click event + */ +function nodeClicked(e) { + // Find the target + var t = e.target; + + // If the click target is actually a file (rather than a directory), ignore it + if (t.tagName.toLowerCase() !== 'div' || t.className === 'children') return; + + // Recurse upwards until we find the actual directory node + while (t && t.className.substring(0, 3) != 'dir') t = t.parentNode; + + // If we're at the root node, then do nothing (we don't allow collapsing of the whole tree) + if (!t || t.parentNode.id == 'tree') return; + + // Find the path and toggle the state, saving the state in the localStorage variable + var path = t.getAttribute('rel'); + if (t.className.indexOf('open') !== -1) { + t.className = t.className.replace(/\s*open/g, ''); + if (window.localStorage) window.localStorage.removeItem('docker_openPath:' + path); + } else { + t.className += ' open'; + if (window.localStorage) window.localStorage['docker_openPath:' + path] = 'yes'; + } +} + + +/** + * ## nodeHtml + * + * Constructs the markup for a directory in the tree + * + * @param {string} nodename The node name. + * @param {object} node Node object of same format as whole tree. + * @param {string} path The path form the base to this node + * @param {string} root Relative path from current page to root + */ +function nodeHtml(nodename, node, path, root) { + // Firstly, figure out whether or not the directory is expanded from localStorage + var isOpen = window.localStorage && window.localStorage['docker_openPath:' + path] == 'yes'; + var out = '
'; + out += '
' + nodename + '
'; + out += '
'; + + // Loop through all child directories first + if (node.dirs) { + var dirs = []; + for (var i in node.dirs) { + if (node.dirs.hasOwnProperty(i)) dirs.push({ name: i, html: nodeHtml(i, node.dirs[i], path + i + '/', root) }); + } + // Have to store them in an array first and then sort them alphabetically here + dirs.sort(function(a, b) { return (a.name > b.name) ? 1 : (a.name == b.name) ? 0 : -1; }); + + for (var k = 0; k < dirs.length; k += 1) out += dirs[k].html; + } + + // Now loop through all the child files alphabetically + if (node.files) { + node.files.sort(); + for (var j = 0; j < node.files.length; j += 1) { + out += '' + node.files[j] + ''; + } + } + + // Close things off + out += '
'; + + return out; +} + +/** + * ## toggleTree + * + * Toggles the visibility of the folder tree + */ +function toggleTree() { + // Do the actual toggling by modifying the class on the body element. That way we can get some nice CSS transitions going. + if (sidebarVisible) { + document.body.className = document.body.className.replace(/\s*sidebar/g, ''); + sidebarVisible = false; + } else { + document.body.className += ' sidebar'; + sidebarVisible = true; + } + if (window.localStorage) { + if (sidebarVisible) { + window.localStorage.docker_showSidebar = 'yes'; + } else { + window.localStorage.docker_showSidebar = 'no'; + } + } +} + +/** + * ## wireUpTabs + * + * Wires up events on the sidebar tabe + */ +function wireUpTabs() { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Each tab has a class corresponding of the id of its tab pane + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + children[i].addEventListener('click', function(c) { + return function() { switchTab(c); }; + }(children[i].className)); + } +} + +/** + * ## switchTab + * + * Switches tabs in the sidebar + * + * @param {string} tab The ID of the tab to switch to + */ +function switchTab(tab) { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Easiest way to go through tabs without any kind of selector is just to look at the tab bar + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + + // Figure out what tab pane this tab button corresponts to + var t = children[i].className.replace(/\s.*$/, ''); + if (t === tab) { + // Show the tab pane, select the tab button + document.getElementById(t).style.display = 'block'; + if (children[i].className.indexOf('selected') === -1) children[i].className += ' selected'; + } else { + // Hide the tab pane, deselect the tab button + document.getElementById(t).style.display = 'none'; + children[i].className = children[i].className.replace(/\sselected/, ''); + } + } + + // Store the last open tab in localStorage + if (window.localStorage) window.localStorage.docker_sidebarTab = tab; +} + +/** + * ## window.onload + * + * When the document is ready, make the sidebar and all that jazz + */ +(function(init) { + if (window.addEventListener) { + window.addEventListener('DOMContentLoaded', init); + } else { // IE8 and below + window.onload = init; + } +}(function() { + makeTree(tree, relativeDir, thisFile); + wireUpTabs(); + + // Switch to the last viewed sidebar tab if stored, otherwise default to folder tree + if (window.localStorage && window.localStorage.docker_sidebarTab) { + switchTab(window.localStorage.docker_sidebarTab); + } else { + switchTab('tree'); + } +})); diff --git a/counter/doc/doc-style.css b/counter/doc/doc-style.css new file mode 100644 index 0000000000000..2019a1b7659c6 --- /dev/null +++ b/counter/doc/doc-style.css @@ -0,0 +1,403 @@ +/* + +Original highlight.js style (c) Ivan Sagalaev + +*/ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +/* Base color: saturation 0; */ +.hljs, +.hljs-subst { + color: #444; +} +.hljs-comment { + color: #888888; +} +.hljs-keyword, +.hljs-attribute, +.hljs-selector-tag, +.hljs-meta-keyword, +.hljs-doctag, +.hljs-name { + font-weight: bold; +} +/* User color: hue: 0 */ +.hljs-type, +.hljs-string, +.hljs-number, +.hljs-selector-id, +.hljs-selector-class, +.hljs-quote, +.hljs-template-tag, +.hljs-deletion { + color: #880000; +} +.hljs-title, +.hljs-section { + color: #880000; + font-weight: bold; +} +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #BC6060; +} +/* Language color: hue: 90; */ +.hljs-literal { + color: #78A960; +} +.hljs-built_in, +.hljs-bullet, +.hljs-code, +.hljs-addition { + color: #397300; +} +/* Meta color: hue: 200 */ +.hljs-meta { + color: #1f7199; +} +.hljs-meta-string { + color: #4d99bf; +} +/* Misc effects */ +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: bold; +} +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + margin: 0; + padding: 0; + background: #ffffff; + color: #4d4d4d; +} +p, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0 0 15px 0; +} +h1 { + margin-top: 40px; +} +a { + color: #880000; +} +a:visited { + color: #880000; +} +#tree, +#headings { + position: absolute; + top: 30px; + left: 0; + bottom: 0; + width: 290px; + padding: 10px 0; + overflow: auto; +} +#sidebar_wrapper { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 0; + overflow: hidden; + background: #e7e7e7; +} +#sidebar_switch { + position: absolute; + top: 0; + left: 0; + width: 290px; + height: 29px; + border-bottom: 1px solid; + background: #e2e2e2; + border-bottom-color: #d6d6d6; +} +#sidebar_switch span { + display: block; + float: left; + width: 50%; + text-align: center; + line-height: 29px; + cursor: pointer; + color: #4b4b4b; +} +#sidebar_switch span:hover { + background: #e7e7e7; +} +#sidebar_switch .selected { + font-weight: bold; + background: #ededed; + color: #444; +} +.slidey #sidebar_wrapper { + -webkit-transition: width 250ms linear; + -moz-transition: width 250ms linear; + -ms-transition: width 250ms linear; + -o-transition: width 250ms linear; + transition: width 250ms linear; +} +.sidebar #sidebar_wrapper { + width: 290px; +} +#tree .nodename { + text-indent: 12px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAg0lEQVQYlWNIS0tbAcSK////Z8CHGTIzM7+mp6d/ASouwqswKyvrO1DRfyg+CcRaxCgE4Z9A3AjEbIQUgjHQOQvwKgS6+ffChQt3AiUDcCqsra29d/v27R6ghCVWN2ZnZ/9YuXLlRqBAPBALYvVMR0fHmQcPHrQBOUZ4gwfqFj5CAQ4Al6wLIYDwo9QAAAAASUVORK5CYII="); + background-repeat: no-repeat; + background-position: left center; + cursor: pointer; +} +#tree .open > .nodename { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAlElEQVQYlWNIS0tbCsT/8eCN////Z2B49OhRfHZ29jdsioDiP27evJkNVggkONeuXbscm8Jly5atA8rzwRSCsG5DQ8MtZEU1NTUPgOLGUHm4QgaQFVlZWT9BijIzM39fuHChDCaHohBkBdCq9SCF8+bN2wHkC+FSCMLGkyZNOvb9+3dbNHEMhSDsDsRMxCjEiolWCADeUBHgU/IGQQAAAABJRU5ErkJggg=="); + background-position: left 7px; +} +#tree .dir, +#tree .file { + position: relative; + min-height: 20px; + line-height: 20px; + padding-left: 12px; +} +#tree .dir > .children, +#tree .file > .children { + display: none; +} +#tree .dir.open > .children, +#tree .file.open > .children { + display: block; +} +#tree .file { + padding-left: 24px; + display: block; + text-decoration: none; + color: #444; +} +#tree > .dir { + padding-left: 0; +} +#headings .heading a { + text-decoration: none; + padding-left: 10px; + display: block; + color: #444; +} +#headings .h1 { + padding-left: 0; + margin-top: 10px; + font-size: 1.3em; +} +#headings .h2 { + padding-left: 10px; + margin-top: 8px; + font-size: 1.1em; +} +#headings .h3 { + padding-left: 20px; + margin-top: 5px; + font-size: 1em; +} +#headings .h4 { + padding-left: 30px; + margin-top: 3px; + font-size: 0.9em; +} +#headings .h5 { + padding-left: 40px; + margin-top: 1px; + font-size: 0.8em; +} +#headings .h6 { + padding-left: 50px; + font-size: 0.75em; +} +#sidebar-toggle { + position: fixed; + top: 0; + left: 0; + width: 5px; + bottom: 0; + z-index: 2; + cursor: pointer; + background: #dfdfdf; +} +#sidebar-toggle:hover { + width: 10px; + background: #d6d6d6; +} +.slidey #sidebar-toggle, +.slidey #container { + -webkit-transition: all 250ms linear; + -moz-transition: all 250ms linear; + -ms-transition: all 250ms linear; + -o-transition: all 250ms linear; + transition: all 250ms linear; +} +.sidebar #sidebar-toggle { + left: 290px; +} +#container { + position: fixed; + left: 5px; + right: 0; + top: 0; + bottom: 0; + overflow: auto; +} +.sidebar #container { + left: 295px; +} +.no-sidebar #sidebar_wrapper, +.no-sidebar #sidebar-toggle { + display: none; +} +.no-sidebar #container { + left: 0; +} +#page { + padding-top: 40px; +} +table td { + border: 0; + outline: 0; +} +.docs.markdown { + padding: 10px 50px; +} +td.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; +} +.docs pre { + margin: 15px 0 15px; + padding: 5px; + padding-left: 10px; + border: 1px solid #d6d6d6; + background: #F0F0F0; + font-size: 12px; + overflow: auto; +} +.docs pre.code_stats { + font-size: 60%; +} +.docs p tt, +.docs li tt, +.docs p code, +.docs li code { + border: 1px solid #d6d6d6; + font-size: 12px; + padding: 0 0.2em; + background: #e7e7e7; +} +.dox { + border-top: 1px solid #dddddd; + padding-top: 10px; + padding-bottom: 10px; +} +.dox .details { + padding: 10px; + background: #F0F0F0; + border: 1px solid #d6d6d6; + margin-bottom: 10px; +} +.dox .dox_tag_title { + font-weight: bold; +} +.dox .dox_tag_detail { + margin-left: 10px; +} +.dox .dox_tag_detail span { + margin-right: 5px; +} +.dox .dox_type { + font-style: italic; +} +.dox .dox_tag_name { + font-weight: bold; +} +.pilwrap { + position: relative; + padding-top: 1px; +} +.pilwrap .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + color: #555555; +} +.pilwrap .pilcrow:before { + content: '\b6'; +} +.pilwrap:hover .pilcrow { + opacity: 1; +} +td.code { + padding: 8px 15px 8px 25px; + width: 100%; + vertical-align: top; + border-left: 1px solid #d6d6d6; + background: #F0F0F0; +} +.background { + border-left: 1px solid #d6d6d6; + position: absolute; + z-index: -1; + top: 0; + right: 0; + bottom: 0; + left: 525px; + background: #F0F0F0; +} +pre, +tt, +code { + font-size: 12px; + line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; + padding: 0; + white-space: pre-wrap; + background: #F0F0F0; +} +.line-num { + display: inline-block; + width: 50px; + text-align: right; + opacity: 0.3; + margin-left: -20px; + text-decoration: none; + color: #888888; +} +.line-num:before { + content: attr(data-line); +} diff --git a/counter/foundry.toml b/counter/foundry.toml index 25b918f9c9a96..c27b8ed21ba0b 100644 --- a/counter/foundry.toml +++ b/counter/foundry.toml @@ -1,6 +1,9 @@ [profile.default] src = "src" out = "out" -libs = ["lib"] +libs = ["lib", "dependencies"] + +[dependencies] +forge-std = "1.15.0" # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/counter/remappings.txt b/counter/remappings.txt new file mode 100644 index 0000000000000..6c93bbb0c4b12 --- /dev/null +++ b/counter/remappings.txt @@ -0,0 +1 @@ +forge-std-1.15.0/=dependencies/forge-std-1.15.0/ diff --git a/counter/soldeer.lock b/counter/soldeer.lock new file mode 100644 index 0000000000000..af6c8601cd7ba --- /dev/null +++ b/counter/soldeer.lock @@ -0,0 +1,6 @@ +[[dependencies]] +name = "forge-std" +version = "1.15.0" +url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_15_0_27-02-2026_08:26:17_forge-std-1.15.zip" +checksum = "40d9b3b3d786eec4cd05fb9d818616015cbe7b8866643a9f0854495c938588c4" +integrity = "92accf4f7850eb9f5832f0ea77d633d36ebe993efc6d6c9f32369d31befc8a75" From 3c69d9eb222f13bbc96249f4a0aeff87d9a4b494 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 02:59:01 +0000 Subject: [PATCH 328/374] feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp From 3d516b69d912c32ea5615d21fd9375c53e7264d2 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 08:37:57 +0000 Subject: [PATCH 329/374] foundry-rs#14280 (#462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c332240fea036..9ce8f1bc25bd9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: crate-ci/typos@02ea592e44b3a53c302f697cddca7641cd051c3d # v1.45.0 + - uses: crate-ci/typos@cf5f1c29a8ac336af8568821ec41919923b05a83 # v1.45.1 shellcheck: runs-on: depot-ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ad324e7ef9c1..e8c2b4b7d2700 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -279,7 +279,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -294,7 +294,7 @@ jobs: # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release if: ${{ env.IS_NIGHTLY == 'true' }} - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: "Nightly" tag_name: "nightly" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9ccb850dbc5c..93c57d29ec1dd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -97,7 +97,7 @@ jobs: run: pip --version && pip install vyper==0.4.3 - name: Foundry test cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.foundry/cache From 1f430c22a2f8785ca33b231215584af3fa5fc2fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 10:39:30 +0000 Subject: [PATCH 330/374] chore(deps): bump strum from 0.27.2 to 0.28.0 Bumps [strum](https://github.com/Peternator7/strum) from 0.27.2 to 0.28.0. - [Release notes](https://github.com/Peternator7/strum/releases) - [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md) - [Commits](https://github.com/Peternator7/strum/compare/v0.27.2...v0.28.0) --- updated-dependencies: - dependency-name: strum dependency-version: 0.28.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 79 ++++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 2 +- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfc0874cf1fd3..65a696a8be26d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,7 +111,7 @@ dependencies = [ "alloy-rlp", "num_enum", "serde", - "strum", + "strum 0.27.2", ] [[package]] @@ -717,7 +717,7 @@ dependencies = [ "jsonwebtoken", "rand 0.8.5", "serde", - "strum", + "strum 0.27.2", ] [[package]] @@ -1194,7 +1194,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1218,7 +1218,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3021,7 +3021,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3174,7 +3174,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3958,7 +3958,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4263,7 +4263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4630,7 +4630,7 @@ dependencies = [ "solar-compiler", "soldeer-commands", "soldeer-core", - "strum", + "strum 0.28.0", "svm-rs", "tempfile", "tempo-alloy", @@ -4964,7 +4964,7 @@ dependencies = [ "serde_json", "solar-compiler", "strsim", - "strum", + "strum 0.28.0", "tempfile", "tempo-primitives", "tikv-jemallocator", @@ -6588,7 +6588,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7514,7 +7514,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7735,7 +7735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b685c8311c9171d1bd2895222965d25616b2de2cb5819dd3504ed9250df9fecd" dependencies = [ "ahash", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "parking_lot", "stable_deref_trait", ] @@ -8813,7 +8813,7 @@ dependencies = [ "itertools 0.14.0", "kasuari", "lru", - "strum", + "strum 0.27.2", "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", @@ -8845,7 +8845,7 @@ dependencies = [ "itertools 0.14.0", "line-clipping", "ratatui-core", - "strum", + "strum 0.27.2", "time", "unicode-segmentation", "unicode-width 0.2.2", @@ -9398,7 +9398,7 @@ dependencies = [ "modular-bitfield", "reth-codecs 0.3.0", "serde", - "strum", + "strum 0.27.2", "thiserror 2.0.18", "tracing", ] @@ -9489,7 +9489,7 @@ dependencies = [ "fixed-map", "reth-stages-types", "serde", - "strum", + "strum 0.27.2", "tracing", ] @@ -10048,7 +10048,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10107,7 +10107,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10810,7 +10810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10826,7 +10826,7 @@ dependencies = [ "solar-data-structures", "solar-interface", "solar-macros", - "strum", + "strum 0.27.2", ] [[package]] @@ -10850,7 +10850,7 @@ version = "0.1.8" source = "git+https://github.com/paradigmxyz/solar?rev=530f129#530f129b1b2d7138df973dd71d2fc1e592b593d7" dependencies = [ "colorchoice", - "strum", + "strum 0.27.2", ] [[package]] @@ -10947,7 +10947,7 @@ dependencies = [ "solar-interface", "solar-macros", "solar-parse", - "strum", + "strum 0.27.2", "thread_local", "tracing", ] @@ -11104,7 +11104,16 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", ] [[package]] @@ -11119,6 +11128,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" @@ -11337,7 +11358,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11542,7 +11563,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11552,7 +11573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -12769,7 +12790,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -12919,7 +12940,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 33ba7f64fab10..7cc434c28e278 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -483,7 +483,7 @@ serde_json = { version = "1.0", features = ["arbitrary_precision"] } similar-asserts = "1.7" soldeer-commands = "=0.10.0" soldeer-core = { version = "=0.10.1", features = ["serde"] } -strum = "0.27" +strum = "0.28" tempfile = "3.23" tokio = "1" toml = "0.9" From 04d520f5db9889fd779246e9f9cb8846ccfa0ed6 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:30:38 +0000 Subject: [PATCH 331/374] chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (foundry-rs#14398 (#463) * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: zerosnacks Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- .github/scripts/tempo-mpp.sh | 18 +- .github/workflows/benchmarks.yml | 2 +- .github/workflows/ci.yml | 6 +- .github/workflows/crate-checks.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/npm.yml | 11 +- .github/workflows/release.yml | 6 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 4 +- Cargo.lock | 99 ++++-- Cargo.toml | 6 +- Makefile | 41 ++- crates/cast/Cargo.toml | 2 +- crates/cast/src/call_spec.rs | 10 +- crates/cast/src/cmd/erc20.rs | 103 +++++- crates/cli/Cargo.toml | 2 +- crates/cli/src/utils/mod.rs | 14 +- crates/common/Cargo.toml | 1 + crates/common/fmt/Cargo.toml | 1 + crates/common/fmt/src/console.rs | 107 ++++++ crates/common/fmt/src/lib.rs | 2 +- crates/common/src/provider/mpp/persist.rs | 410 +++++++++------------- crates/common/src/provider/mpp/session.rs | 38 +- crates/evm/abi/src/Console.json | 2 +- crates/evm/abi/src/console.py | 8 +- crates/evm/evm/src/inspectors/logs.rs | 4 +- crates/forge/Cargo.toml | 2 +- crates/forge/src/cmd/create.rs | 127 +++++-- crates/macros/src/console_fmt.rs | 46 ++- crates/script/Cargo.toml | 2 +- npm/bun.lock | 15 +- npm/scripts/publish.mjs | 10 - npm/tsconfig.json | 1 - 34 files changed, 714 insertions(+), 394 deletions(-) diff --git a/.github/scripts/tempo-mpp.sh b/.github/scripts/tempo-mpp.sh index 1a98b1ed9c494..0d34c4a3b5fa5 100755 --- a/.github/scripts/tempo-mpp.sh +++ b/.github/scripts/tempo-mpp.sh @@ -110,7 +110,7 @@ BLOCK2=$("$CAST" block-number --rpc-url "$RPC_MPP") AFTER2=$("$CAST" erc20 balance "$TOKEN" "$WALLET" --rpc-url "$RPC" | awk '{print $1}') SPENT2=$((BEFORE2 - AFTER2)) echo "Block: $BLOCK2" -echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/foundry/channels.json)" +echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/channels.db)" # 6. forge script via MPP echo "" @@ -129,9 +129,9 @@ contract MppCheck is Script { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" script "$TMPDIR/script/Mpp.s.sol" --rpc-url "$RPC_MPP" --root "$TMPDIR" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 7. forge test with vm.createSelectFork via MPP @@ -149,15 +149,15 @@ contract MppForkTest is Test { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" test --match-test test_fork_via_mpp --root "$TMPDIR" -vvv -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 8. anvil fork via MPP echo "" echo "=== 8. anvil --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$ANVIL" --fork-url "$RPC_MPP" --port 8555 --silent & ANVIL_PID=$! for _ in $(seq 1 30); do @@ -167,15 +167,15 @@ done echo "chain-id: $("$CAST" chain-id --rpc-url http://localhost:8555)" kill $ANVIL_PID 2>/dev/null wait $ANVIL_PID 2>/dev/null -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 9. chisel fork via MPP echo "" echo "=== 9. chisel --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo 'block.number' | "$CHISEL" --fork-url "$RPC_MPP" 2>&1 | grep -E "Decimal|Type" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" echo "" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 96af1f4ab9a1e..8465874854551 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -61,7 +61,7 @@ jobs: printf '%s\n' "$GITHUB_WORKSPACE/.foundry/bin" >> "$GITHUB_PATH" - name: Build benchmark binary - run: cargo build --release --bin foundry-bench + run: cargo build --locked --release --bin foundry-bench - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c332240fea036..53d009de4c442 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo test --workspace --doc + - run: cargo test --workspace --doc --locked typos: runs-on: depot-ubuntu-latest @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: crate-ci/typos@02ea592e44b3a53c302f697cddca7641cd051c3d # v1.45.0 + - uses: crate-ci/typos@cf5f1c29a8ac336af8568821ec41919923b05a83 # v1.45.1 shellcheck: runs-on: depot-ubuntu-latest @@ -92,7 +92,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo clippy --workspace --all-targets --all-features + - run: cargo clippy --workspace --all-targets --all-features --locked env: RUSTFLAGS: -Dwarnings diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index 781d63759045e..b2eb6261efd63 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -34,7 +34,7 @@ jobs: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo hack check + - run: cargo hack check --locked issue: name: Open an issue diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a8a3dcbb8c7d3..288d9d127592f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: Build documentation - run: cargo doc --workspace --all-features --no-deps --document-private-items + run: cargo doc --workspace --all-features --no-deps --document-private-items --locked env: RUSTDOCFLAGS: --cfg docsrs -D warnings --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options - name: Setup Pages diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 007ef5f4286aa..4ef7c4ac04dfc 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -31,6 +31,7 @@ jobs: contents: read actions: read id-token: write + environment: release name: ${{ matrix.tool }}-${{ matrix.os }}-${{ matrix.arch }} runs-on: ubuntu-latest strategy: @@ -151,9 +152,6 @@ jobs: id: release-version working-directory: ./npm env: - PROVENANCE: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} run: | @@ -224,8 +222,6 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ steps.release-version.outputs.RELEASE_VERSION }} RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | set -euo pipefail @@ -282,12 +278,11 @@ jobs: contents: read actions: read id-token: write + environment: release needs: publish-arch name: Publish Meta Package runs-on: ubuntu-latest env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} RELEASE_VERSION: ${{ needs.publish-arch.outputs.RELEASE_VERSION }} steps: - name: Checkout @@ -323,6 +318,4 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ env.RELEASE_VERSION }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} - NPM_TOKEN: ${{ env.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ env.NODE_AUTH_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ad324e7ef9c1..52b33787de3f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -179,7 +179,7 @@ jobs: shell: bash run: | set -eo pipefail - flags=(--target $TARGET --profile $RUST_PROFILE --bins + flags=(--locked --target $TARGET --profile $RUST_PROFILE --bins --no-default-features --features "$RUST_FEATURES") # `jemalloc` is not fully supported on MSVC or aarch64 Linux. @@ -279,7 +279,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -294,7 +294,7 @@ jobs: # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release if: ${{ env.IS_NIGHTLY == 'true' }} - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: "Nightly" tag_name: "nightly" diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index af4415b1e8ba1..c0e4fcb5f3263 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -50,7 +50,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --no-fail-fast + run: cargo nextest run --locked --profile flaky --no-fail-fast # If any of the jobs fail, this will create a normal-priority issue to signal so. issue: diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index afd91d3d72c05..37c3edc9b916a 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -54,7 +54,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --features=isolate-by-default --no-fail-fast + run: cargo nextest run --locked --profile flaky --features=isolate-by-default --no-fail-fast # If nextest fails, create a high-priority issue for isolation failures. issue-isolate: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9ccb850dbc5c..7c79cd266f152 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -97,7 +97,7 @@ jobs: run: pip --version && pip install vyper==0.4.3 - name: Foundry test cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.foundry/cache @@ -121,4 +121,4 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run ${{ matrix.flags }} + run: cargo nextest run --locked ${{ matrix.flags }} diff --git a/Cargo.lock b/Cargo.lock index d15f7c092e642..5a102a3276c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,7 +1194,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1218,7 +1218,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2558,7 +2558,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -3021,7 +3021,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3174,7 +3174,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3958,7 +3958,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4263,7 +4263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4376,6 +4376,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast-float2" version = "0.2.3" @@ -5009,6 +5021,7 @@ dependencies = [ "foundry-common-fmt", "foundry-compilers", "foundry-config", + "foundry-wallets", "itertools 0.14.0", "jiff", "mpp", @@ -5048,6 +5061,7 @@ dependencies = [ "alloy-rpc-types", "alloy-serde 2.0.0", "chrono", + "comfy-table", "eyre", "foundry-macros", "op-alloy-consensus", @@ -5544,7 +5558,7 @@ dependencies = [ [[package]] name = "foundry-wallets" version = "0.1.0" -source = "git+https://github.com/foundry-rs/foundry-core?rev=7f401c1397af90a0a94ef7424a48bbf3dc0248cc#7f401c1397af90a0a94ef7424a48bbf3dc0248cc" +source = "git+https://github.com/foundry-rs/foundry-core?rev=d6c92dfcb41be26fbc1de21221a5bfc6bdd93245#d6c92dfcb41be26fbc1de21221a5bfc6bdd93245" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -5568,6 +5582,7 @@ dependencies = [ "eth-keystore", "eyre", "rpassword", + "rusqlite", "serde", "serde_json", "tempo-primitives", @@ -5970,6 +5985,15 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -6565,7 +6589,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -6920,6 +6944,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -7480,7 +7515,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7701,7 +7736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b685c8311c9171d1bd2895222965d25616b2de2cb5819dd3504ed9250df9fecd" dependencies = [ "ahash", - "hashbrown 0.17.0", + "hashbrown 0.16.1", "parking_lot", "stable_deref_trait", ] @@ -8464,7 +8499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8633,7 +8668,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -9930,6 +9965,20 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rusqlite" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b" +dependencies = [ + "bitflags 2.11.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -10000,7 +10049,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -10059,7 +10108,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -10070,9 +10119,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -10762,7 +10811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -10830,7 +10879,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.12.1", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -10865,7 +10914,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.12.1", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -11289,7 +11338,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -11494,7 +11543,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -11504,7 +11553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -12721,7 +12770,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -12871,7 +12920,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 70d6eba6a6aec..20fba04e5f329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -357,7 +357,7 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features ] } ## foundry-core -foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "7f401c1397af90a0a94ef7424a48bbf3dc0248cc", default-features = false } +foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "d6c92dfcb41be26fbc1de21221a5bfc6bdd93245", default-features = false } ## alloy alloy-consensus = { version = "2.0.0", default-features = false } @@ -384,7 +384,6 @@ alloy-signer-local = { version = "2.0.0", default-features = false } alloy-signer-trezor = { version = "2.0.0", default-features = false } alloy-signer-turnkey = { version = "2.0.0", default-features = false } alloy-transport = { version = "2.0.0", default-features = false } -alloy-transport-http = { version = "2.0.0", default-features = false } alloy-transport-ipc = { version = "2.0.0", default-features = false } alloy-transport-ws = { version = "2.0.0", default-features = false } alloy-hardforks = { version = "0.4.7", default-features = false } @@ -622,3 +621,6 @@ solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/sola solar-interface = { package = "solar-interface", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-ast = { package = "solar-ast", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-sema = { package = "solar-sema", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } + +[workspace.metadata.cargo-shear] +ignored = ["idna_adapter", "cast", "chisel", "forge", "alloy-contract"] diff --git a/Makefile b/Makefile index fe768f19abf43..1bf20f4eeebd1 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ help: ## Display this help. .PHONY: build build: ## Build the project. - cargo build --features "$(FEATURES)" --profile "$(PROFILE)" + cargo build --locked --features "$(FEATURES)" --profile "$(PROFILE)" .PHONY: build-docker build-docker: ## Build the docker image. @@ -47,17 +47,17 @@ build-docker: ## Build the docker image. ## is unstable (https://github.com/taiki-e/cargo-llvm-cov/issues/2). .PHONY: test-coverage test-coverage: - cargo +nightly llvm-cov --no-report nextest -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ - cargo +nightly llvm-cov --no-report --doc && \ + cargo +nightly llvm-cov --no-report nextest --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ + cargo +nightly llvm-cov --no-report --doc --locked && \ cargo +nightly llvm-cov report --doctests --open .PHONY: test-unit test-unit: ## Run unit tests. - cargo nextest run -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' + cargo nextest run --workspace --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' .PHONY: test-doc test-doc: ## Run doc tests. - cargo test --doc --workspace + cargo test --doc --workspace --locked .PHONY: test test: ## Run all tests. @@ -77,6 +77,7 @@ lint-clippy: ## Run clippy on the codebase. --workspace \ --all-targets \ --all-features \ + --locked \ -- -D warnings .PHONY: lint-clippy-fix @@ -85,6 +86,7 @@ lint-clippy-fix: ## Run clippy on the codebase and fix warnings. --workspace \ --all-targets \ --all-features \ + --locked \ --fix \ --allow-dirty \ --allow-staged \ @@ -104,21 +106,46 @@ lint: ## Run all linters. $(MAKE) lint-clippy && \ $(MAKE) lint-typos +##@ Documentation + +.PHONY: doc +doc: ## Build the documentation. + RUSTDOCFLAGS="--cfg docsrs -D warnings -Zunstable-options --show-type-layout --generate-link-to-definition" \ + cargo +nightly doc \ + --workspace \ + --all-features \ + --document-private-items \ + --no-deps \ + --locked + ##@ Other +.PHONY: lock +lock: ## Update the Cargo.lock file with the current dependencies. + cargo fetch + .PHONY: clean clean: ## Clean the project. cargo clean .PHONY: deny deny: ## Perform a `cargo` deny check. - cargo deny --all-features check all + cargo deny --locked --all-features check all + +.PHONY: check +check: ## Run a feature check on all crates and binaries. + cargo hack check --locked --feature-powerset --depth 1 + +.PHONY: shear +shear: ## Run `cargo shear` to check for unused dependencies. + cargo shear --locked .PHONY: pr pr: ## Run all checks and tests. $(MAKE) deny && \ $(MAKE) lint && \ - $(MAKE) test + $(MAKE) test && \ + $(MAKE) doc # dprint formatting commands .PHONY: dprint-fmt diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index dbd89ce88827d..56164c8f6367a 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -28,7 +28,7 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-debugger.workspace = true foundry-evm.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } forge-fmt.workspace = true alloy-chains.workspace = true diff --git a/crates/cast/src/call_spec.rs b/crates/cast/src/call_spec.rs index b39e024f38af5..0047b56d25f03 100644 --- a/crates/cast/src/call_spec.rs +++ b/crates/cast/src/call_spec.rs @@ -145,8 +145,8 @@ impl FromStr for CallSpec { /// Parse a value string that can be in ether notation (e.g., "0.1ether") or raw wei. fn parse_ether_or_wei(s: &str) -> Result { // Use alloy's DynSolType coercion which handles "1ether", "1gwei", "1000" etc. - if s.starts_with("0x") { - U256::from_str_radix(s, 16).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) + if s.starts_with("0x") || s.starts_with("0X") { + U256::from_str(s).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), s) .wrap_err_with(|| format!("Invalid value '{s}'"))? @@ -180,6 +180,12 @@ mod tests { assert!(spec.sig.is_none()); } + #[test] + fn test_parse_hex_value() { + assert_eq!(parse_ether_or_wei("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_or_wei("0X10").unwrap(), U256::from(16)); + } + #[test] fn test_parse_with_sig() { let spec = CallSpec::parse( diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index e629b8eca0d5a..1d27b7a23dcd5 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -3,13 +3,13 @@ use std::{str::FromStr, time::Duration}; use crate::{ cmd::send::{cast_send, cast_send_with_access_key}, format_uint_exp, - tx::{SendTxOpts, TxParams}, + tx::{CastTxSender, SendTxOpts, TxParams}, }; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::BlockId; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network}; -use alloy_primitives::U256; +use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder}; +use alloy_primitives::{Address, U256}; use alloy_provider::{Provider, fillers::RecommendedFillers}; use alloy_signer::Signature; use alloy_sol_types::sol; @@ -28,6 +28,7 @@ use foundry_common::{ pub use foundry_config::{Chain, utils::*}; use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; +use tempo_contracts::precompiles::PATH_USD_ADDRESS; sol! { #[sol(rpc)] @@ -281,6 +282,34 @@ impl Erc20Subcommand { } } + const fn uses_browser_send(&self) -> bool { + match self { + Self::Transfer { send_tx, .. } + | Self::Approve { send_tx, .. } + | Self::Mint { send_tx, .. } + | Self::Burn { send_tx, .. } => send_tx.browser.browser, + _ => false, + } + } + + async fn should_use_tempo_network( + &self, + tempo_access_key: &Option, + ) -> eyre::Result { + if self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) + || tempo_access_key.is_some() + { + return Ok(true); + } + + if self.uses_browser_send() { + let config = self.rpc_opts().load_config()?; + return Ok(get_chain(config.chain, &get_provider(&config)?).await?.is_tempo()); + } + + Ok(false) + } + pub async fn run(self) -> eyre::Result<()> { // Resolve the signer once for state-changing variants. let (signer, tempo_access_key) = match &self { @@ -299,8 +328,7 @@ impl Erc20Subcommand { _ => (None, None), }; - let is_tempo = self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) - || tempo_access_key.is_some(); + let is_tempo = self.should_use_tempo_network(&tempo_access_key).await?; if is_tempo { self.run_generic::(signer, tempo_access_key).await @@ -356,6 +384,28 @@ impl Erc20Subcommand { timeout, ) .await? + } else if let Some(browser) = $send_tx.browser.run::().await? { + let $provider = ProviderBuilder::::from_config(&config)?.build()?; + if let Some(interval) = $send_tx.poll_interval { + $provider.client().set_poll_interval(Duration::from_secs(interval)); + } + let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); + let mut tx = { $build_tx }.into_transaction_request(); + let chain = get_chain(config.chain, &$provider).await?; + $tx_opts.apply::(&mut tx, chain.is_legacy()); + if chain.is_tempo() && tx.fee_token().is_none() { + tx.set_fee_token(PATH_USD_ADDRESS); + } + fill_tx(&$provider, &mut tx, browser.address(), chain).await?; + let tx_hash = browser.send_transaction_via_browser(tx).await?; + CastTxSender::new(&$provider) + .print_tx_result( + tx_hash, + $send_tx.cast_async, + $send_tx.confirmations, + timeout, + ) + .await? } else { let signer = pre_resolved_signer.unwrap_or($send_tx.eth.wallet.signer().await?); let $provider = build_provider_with_signer::(&$send_tx, signer)?; @@ -503,3 +553,46 @@ impl Erc20Subcommand { Ok(()) } } + +/// Fills from, chain_id, nonce, fees, and gas limit on a transaction request for the browser +/// wallet path. Mirrors the filling logic in the shared tx builder but operates on a +/// pre-built transaction request from the sol! macro rather than through the builder pipeline. +/// Only fills fields that haven't already been set by the user. +async fn fill_tx>( + provider: &P, + tx: &mut N::TransactionRequest, + from: Address, + chain: Chain, +) -> eyre::Result<()> +where + N::TransactionRequest: FoundryTransactionBuilder, +{ + tx.set_from(from); + tx.set_chain_id(chain.id()); + + if tx.nonce().is_none() { + tx.set_nonce(provider.get_transaction_count(from).await?); + } + + let legacy = chain.is_legacy(); + + if legacy { + if tx.gas_price().is_none() { + tx.set_gas_price(provider.get_gas_price().await?); + } + } else if tx.max_fee_per_gas().is_none() || tx.max_priority_fee_per_gas().is_none() { + let estimate = provider.estimate_eip1559_fees().await?; + if tx.max_fee_per_gas().is_none() { + tx.set_max_fee_per_gas(estimate.max_fee_per_gas); + } + if tx.max_priority_fee_per_gas().is_none() { + tx.set_max_priority_fee_per_gas(estimate.max_priority_fee_per_gas); + } + } + + if tx.gas_limit().is_none() { + tx.set_gas_limit(provider.estimate_gas(tx.clone()).await?); + } + + Ok(()) +} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 165154da88f63..bcbe837e824e3 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,7 +19,7 @@ foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-evm-networks.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } tempo-primitives.workspace = true diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f1c6c435217b7..be566288ef87a 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -129,8 +129,8 @@ where /// If the string represents an untagged amount (e.g. "100") then /// it is interpreted as wei. pub fn parse_ether_value(value: &str) -> Result { - Ok(if value.starts_with("0x") { - U256::from_str_radix(value, 16)? + Ok(if value.starts_with("0x") || value.starts_with("0X") { + U256::from_str(value)? } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), value)? .as_uint() @@ -844,6 +844,16 @@ mod tests { assert!(!p.is_sol_test()); } + #[test] + fn parse_ether_value_accepts_hex_prefixed_wei() { + assert_eq!(parse_ether_value("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0X10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0x12").unwrap(), U256::from(0x12)); + assert_eq!(parse_ether_value("0xff").unwrap(), U256::from(0xff)); + assert_eq!(parse_ether_value("100").unwrap(), U256::from(100)); + assert_eq!(parse_ether_value("1ether").unwrap(), U256::from(1000000000000000000u128)); + } + // loads .env from cwd and project dir, See [`find_project_root()`] #[test] fn can_load_dotenv() { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 706527093cc8e..6697aff36e4f8 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -83,6 +83,7 @@ flate2.workspace = true tempo-alloy.workspace = true tempo-primitives.workspace = true mpp.workspace = true +foundry-wallets.workspace = true [build-dependencies] chrono.workspace = true diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 78759848fc4bd..2c8e16bccdcc6 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -30,6 +30,7 @@ serde_json.workspace = true chrono.workspace = true revm.workspace = true yansi.workspace = true +comfy-table.workspace = true # Tempo tempo-alloy.workspace = true diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index d751ddba245ba..8b66335445046 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -1,5 +1,6 @@ use super::UIfmt; use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256}; +use comfy_table::{Table, TableComponent, presets::UTF8_FULL}; use std::fmt::{self, Write}; /// A piece is a portion of the format string which represents the next part to emit. @@ -407,6 +408,36 @@ fn format_spec<'a>( } } +pub fn console_table_format( + keys: Option<&[&dyn ConsoleFmt]>, + values: &[&dyn ConsoleFmt], +) -> String { + let keys_strings: Vec = match keys { + Some(keys) => keys.iter().map(|k| k.fmt(FormatSpec::String)).collect(), + None => (0..values.len()).map(|i| i.to_string()).collect(), + }; + let values_strings: Vec = values.iter().map(|v| v.fmt(FormatSpec::String)).collect(); + + let mut table = Table::new(); + table.load_preset(UTF8_FULL); + table.set_style(TableComponent::VerticalLines, '│'); + table.set_style(TableComponent::HeaderLines, '─'); + table.set_style(TableComponent::MiddleHeaderIntersections, '┼'); + table.set_style(TableComponent::LeftHeaderIntersection, '├'); + table.set_style(TableComponent::RightHeaderIntersection, '┤'); + table.set_header(vec!["(index)", "Values"]); + table.remove_style(TableComponent::HorizontalLines); + table.remove_style(TableComponent::MiddleIntersections); + table.remove_style(TableComponent::LeftBorderIntersections); + table.remove_style(TableComponent::RightBorderIntersections); + for i in 0..keys_strings.len().max(values_strings.len()) { + let key = keys_strings.get(i).map(String::as_str).unwrap_or(""); + let value = values_strings.get(i).map(String::as_str).unwrap_or(""); + table.add_row(vec![key, value]); + } + table.to_string() +} + #[cfg(test)] mod tests { use super::*; @@ -610,4 +641,80 @@ mod tests { let call = Logs::Log1(log1); assert_eq!(call.fmt(Default::default()), "foo 42 bar"); } + + #[test] + fn test_console_table_format() { + // auto-indexed, uint256 values + let values: &[&dyn ConsoleFmt] = &[&U256::from(100), &U256::from(200), &U256::from(300)]; + assert_eq!( + console_table_format(None, values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ 0 │ 100 │\n\ + │ 1 │ 200 │\n\ + │ 2 │ 300 │\n\ + └─────────┴────────┘" + ); + + // string keys, uint256 values + // key col expands to fit "charlie123" and value col expands to fit "20000000000000000" + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie123")]; + let values: &[&dyn ConsoleFmt] = &[ + &U256::from(1), + &U256::from_str("20000000000000000").unwrap(), + &U256::from_str("30000000000").unwrap(), + ]; + assert_eq!( + console_table_format(Some(keys), values), + "┌────────────┬───────────────────┐\n\ + │ (index) │ Values │\n\ + ├────────────┼───────────────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 20000000000000000 │\n\ + │ charlie123 │ 30000000000 │\n\ + └────────────┴───────────────────┘" + ); + + // empty table + assert_eq!( + console_table_format(None, &[]), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + └─────────┴────────┘" + ); + + // more keys than values + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie")]; + let values: &[&dyn ConsoleFmt] = &[&U256::from(1), &U256::from(2)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ charlie │ │\n\ + └─────────┴────────┘" + ); + + // more values than keys + let keys: &[&dyn ConsoleFmt] = &[&String::from("alice"), &String::from("bob")]; + let values: &[&dyn ConsoleFmt] = + &[&U256::from(1), &U256::from(2), &U256::from(3), &U256::from(4)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ │ 3 │\n\ + │ │ 4 │\n\ + └─────────┴────────┘" + ); + } } diff --git a/crates/common/fmt/src/lib.rs b/crates/common/fmt/src/lib.rs index 89297a8e2c6b5..45ed47263ce32 100644 --- a/crates/common/fmt/src/lib.rs +++ b/crates/common/fmt/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] mod console; -pub use console::{ConsoleFmt, FormatSpec, console_format}; +pub use console::{ConsoleFmt, FormatSpec, console_format, console_table_format}; mod dynamic; pub use dynamic::{ diff --git a/crates/common/src/provider/mpp/persist.rs b/crates/common/src/provider/mpp/persist.rs index 6d6781ab4c77b..803c7270e041e 100644 --- a/crates/common/src/provider/mpp/persist.rs +++ b/crates/common/src/provider/mpp/persist.rs @@ -1,243 +1,241 @@ //! Persistent channel storage for MPP sessions. //! -//! Stores open payment channel state in a JSON file at -//! `$TEMPO_HOME/foundry/channels.json` (default: `~/.tempo/foundry/channels.json`). +//! Stores open payment channel state in a SQLite database at +//! `$TEMPO_HOME/channels.db` (default: `~/.tempo/channels.db`). //! This allows channel reuse across process invocations, avoiding the cost of //! opening a new on-chain channel for every `cast` / `forge` command. use alloy_primitives::{Address, B256}; +use foundry_wallets::{Channel, ChannelDb}; use mpp::client::channel_ops::ChannelEntry; -use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, - path::PathBuf, + sync::OnceLock, time::{SystemTime, UNIX_EPOCH}, }; use tracing::{debug, warn}; use crate::tempo::tempo_home; -/// Relative path from Tempo home to the Foundry channels file. -const CHANNELS_PATH: &str = "foundry/channels.json"; +/// Process-wide database handle. +fn global_db() -> Option<&'static ChannelDb> { + static DB: OnceLock> = OnceLock::new(); + DB.get_or_init(|| { + let path = tempo_home()?.join("channels.db"); + if let Some(parent) = path.parent() { + let _ = std::fs::create_dir_all(parent); + } + if let Some(old) = + tempo_home().map(|h| h.join("foundry/channels.json")).filter(|p| p.exists()) + { + warn!( + ?old, + "found old channels.json — this file is no longer used; channels will be re-opened" + ); + } -/// Current schema version. -const SCHEMA_VERSION: u64 = 2; + match ChannelDb::open(&path) { + Ok(db) => { + debug!(?path, "opened channel database"); + Some(db) + } + Err(e) => { + warn!(?path, %e, "failed to open channel database"); + None + } + } + }) + .as_ref() +} -/// On-disk representation of the channel store. -#[derive(Debug, Serialize, Deserialize)] -struct ChannelStore { - version: u64, - #[serde(default)] - channels: HashMap, +/// Reconstruct the composite HashMap key from a persisted `Channel`. +/// +/// Mirrors `SessionProvider::channel_key()` in session.rs. +fn channel_key_from_persisted(ch: &Channel) -> String { + let origin_hash = &alloy_primitives::keccak256(ch.origin.as_bytes()).to_string()[..18]; + format!( + "{}:{}:{}:{}:{}:{}:{}", + origin_hash, + ch.chain_id, + ch.payer, + ch.authorized_signer, + ch.payee, + ch.token, + ch.escrow_contract + ) + .to_lowercase() } -impl Default for ChannelStore { - fn default() -> Self { - Self { version: SCHEMA_VERSION, channels: HashMap::new() } +/// Whether a channel can still be used (active and not fully spent). +fn is_usable(ch: &Channel) -> bool { + if ch.state != "active" { + return false; } + let cumulative: u128 = ch.cumulative_amount.parse().unwrap_or(u128::MAX); + let deposit: u128 = ch.deposit.parse().unwrap_or(0); + cumulative < deposit } -/// A persisted channel entry. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PersistedChannel { - pub channel_id: String, - pub salt: String, - pub escrow_contract: String, - pub chain_id: u64, - pub cumulative_amount: String, - pub deposit: String, - pub status: String, - pub origin: String, - pub created_at: u64, - pub last_used_at: u64, +/// Convert a persisted `Channel` to a `ChannelEntry`. +pub fn to_channel_entry(ch: &Channel) -> Option { + let channel_id: B256 = ch.channel_id.parse().ok()?; + let salt: B256 = ch.salt.parse().ok()?; + let escrow_contract: Address = ch.escrow_contract.parse().ok()?; + let cumulative_amount: u128 = ch.cumulative_amount.parse().ok()?; + + Some(ChannelEntry { + channel_id, + salt, + cumulative_amount, + escrow_contract, + chain_id: ch.chain_id as u64, + opened: ch.state == "active", + }) } -impl PersistedChannel { - /// Convert to an mpp `ChannelEntry` for use in the session provider. - pub fn to_channel_entry(&self) -> Option { - let channel_id: B256 = self.channel_id.parse().ok()?; - let salt: B256 = self.salt.parse().ok()?; - let escrow_contract: Address = self.escrow_contract.parse().ok()?; - let cumulative_amount: u128 = self.cumulative_amount.parse().ok()?; - - Some(ChannelEntry { - channel_id, - salt, - cumulative_amount, - escrow_contract, - chain_id: self.chain_id, - opened: self.status == "active", - }) - } - - /// Create from a `ChannelEntry` with metadata. - pub fn from_channel_entry(entry: &ChannelEntry, deposit: u128, origin: &str) -> Self { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - - Self { - channel_id: entry.channel_id.to_string(), - salt: entry.salt.to_string(), - escrow_contract: entry.escrow_contract.to_string(), - chain_id: entry.chain_id, - cumulative_amount: entry.cumulative_amount.to_string(), - deposit: deposit.to_string(), - status: if entry.opened { "active" } else { "closed" }.to_string(), - origin: origin.to_string(), - created_at: now, - last_used_at: now, - } - } - - /// Whether this channel can still be used (active and not fully spent). - fn is_usable(&self) -> bool { - if self.status != "active" { - return false; - } - let cumulative: u128 = self.cumulative_amount.parse().unwrap_or(u128::MAX); - let deposit: u128 = self.deposit.parse().unwrap_or(0); - cumulative < deposit +/// Create a `Channel` from a `ChannelEntry` with metadata. +pub fn from_channel_entry( + entry: &ChannelEntry, + deposit: u128, + origin: &str, + payer: &Address, + payee: &Address, + token: &Address, + authorized_signer: &Address, +) -> Channel { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; + + Channel { + channel_id: entry.channel_id.to_string(), + version: 1, + origin: origin.to_string(), + request_url: String::new(), + chain_id: entry.chain_id as i64, + escrow_contract: entry.escrow_contract.to_string(), + token: token.to_string(), + payee: payee.to_string(), + payer: payer.to_string(), + authorized_signer: authorized_signer.to_string(), + salt: entry.salt.to_string(), + deposit: deposit.to_string(), + cumulative_amount: entry.cumulative_amount.to_string(), + challenge_echo: String::new(), + state: if entry.opened { "active" } else { "closed" }.to_string(), + close_requested_at: 0, + grace_ready_at: 0, + created_at: now, + last_used_at: now, } } -/// Returns the path to the channels file. -fn channels_path() -> Option { - tempo_home().map(|home| home.join(CHANNELS_PATH)) -} - -/// Load channels from disk, evicting spent/inactive entries. -pub fn load_channels() -> HashMap { - let Some(path) = channels_path().filter(|p| p.exists()) else { +/// Load channels from database, evicting spent/inactive entries. +pub fn load_channels() -> HashMap { + let Some(db) = global_db() else { return HashMap::new(); }; - let Ok(contents) = std::fs::read_to_string(&path).inspect_err(|e| { - warn!(?path, %e, "failed to read channels file"); - }) else { - return HashMap::new(); - }; - - let Ok(store) = serde_json::from_str::(&contents).inspect_err(|e| { - warn!(?path, %e, "failed to parse channels file, starting fresh"); - }) else { - return HashMap::new(); + let channels = match db.load() { + Ok(channels) => channels, + Err(e) => { + warn!(%e, "failed to load channels from database"); + return HashMap::new(); + } }; - if store.version != SCHEMA_VERSION { - warn!( - version = store.version, - expected = SCHEMA_VERSION, - "channels file version mismatch, starting fresh" - ); - return HashMap::new(); - } - - // Evict spent/inactive entries - let usable: HashMap = - store.channels.into_iter().filter(|(_, ch)| ch.is_usable()).collect(); + let usable: HashMap = channels + .into_iter() + .filter(is_usable) + .map(|ch| { + let key = channel_key_from_persisted(&ch); + (key, ch) + }) + .collect(); debug!(count = usable.len(), "loaded persisted MPP channels"); usable } -/// Save channels to disk. -pub fn save_channels(channels: &HashMap) { - let Some(path) = channels_path() else { +/// Save channels to database. +pub fn save_channels(channels: &HashMap) { + let Some(db) = global_db() else { return; }; - if let Some(parent) = path.parent() - && let Err(e) = std::fs::create_dir_all(parent) - { - warn!(?path, %e, "failed to create channels directory"); - return; + for ch in channels.values() { + if let Err(e) = db.upsert(ch) { + warn!(%e, channel_id = %ch.channel_id, "failed to save channel"); + } } + debug!(count = channels.len(), "saved MPP channels"); +} - let store = ChannelStore { version: SCHEMA_VERSION, channels: channels.clone() }; - - match serde_json::to_string_pretty(&store) { - Ok(json) => { - if let Err(e) = std::fs::write(&path, json) { - warn!(?path, %e, "failed to write channels file"); - } else { - debug!(?path, count = channels.len(), "saved MPP channels"); - } - } - Err(e) => warn!(%e, "failed to serialize channels"), +/// Delete a channel from the database by its channel ID. +pub fn delete_channel_from_db(channel_id: &str) { + let Some(db) = global_db() else { + return; + }; + if let Err(e) = db.delete(channel_id) { + warn!(%e, channel_id, "failed to delete channel from database"); } } /// Look up a usable persisted channel by key. -pub fn find_channel( - channels: &HashMap, - key: &str, -) -> Option { - channels.get(key).filter(|ch| ch.is_usable()).and_then(|ch| ch.to_channel_entry()) +pub fn find_channel(channels: &HashMap, key: &str) -> Option { + channels.get(key).filter(|ch| is_usable(ch)).and_then(to_channel_entry) } -/// Insert or update a channel entry in memory only (no disk write). -/// -/// Use [`upsert_channel`] when you want to persist immediately, or call -/// [`save_channels`] separately after this. +/// Insert or update a channel entry in memory only (no DB write). pub fn upsert_channel_in_memory( - channels: &mut HashMap, + channels: &mut HashMap, key: &str, entry: &ChannelEntry, - deposit: u128, - origin: &str, ) { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; if let Some(existing) = channels.get_mut(key) { existing.cumulative_amount = entry.cumulative_amount.to_string(); existing.last_used_at = now; - existing.status = if entry.opened { "active" } else { "closed" }.to_string(); + existing.state = if entry.opened { "active" } else { "closed" }.to_string(); } else { - channels - .insert(key.to_string(), PersistedChannel::from_channel_entry(entry, deposit, origin)); + warn!(key, "upsert_channel_in_memory called for unknown channel"); } } -/// Insert or update a channel entry and save to disk. -/// -/// When updating an existing entry, `deposit` is ignored (preserved from the -/// original open). When inserting a new entry, `deposit` is recorded. -pub fn upsert_channel( - channels: &mut HashMap, - key: &str, - entry: &ChannelEntry, - deposit: u128, - origin: &str, -) { - upsert_channel_in_memory(channels, key, entry, deposit, origin); - save_channels(channels); -} - #[cfg(test)] mod tests { use super::*; - fn test_channel(status: &str, cumulative: &str, deposit: &str) -> PersistedChannel { - PersistedChannel { + fn test_channel(state: &str, cumulative: &str, deposit: &str) -> Channel { + Channel { channel_id: format!("0x{}", "ab".repeat(32)), - salt: format!("0x{}", "cd".repeat(32)), - escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + version: 1, + origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + request_url: String::new(), chain_id: 42431, - cumulative_amount: cumulative.to_string(), + escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + token: "0x20c0000000000000000000000000000000000000".to_string(), + payee: "0x3333333333333333333333333333333333333333".to_string(), + payer: "0x1111111111111111111111111111111111111111".to_string(), + authorized_signer: "0x1111111111111111111111111111111111111111".to_string(), + salt: format!("0x{}", "cd".repeat(32)), deposit: deposit.to_string(), - status: status.to_string(), - origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + cumulative_amount: cumulative.to_string(), + challenge_echo: String::new(), + state: state.to_string(), + close_requested_at: 0, + grace_ready_at: 0, created_at: 1000, last_used_at: 1000, } } #[test] - fn is_usable() { - assert!(test_channel("active", "5000", "100000").is_usable()); - assert!(!test_channel("active", "100000", "100000").is_usable()); - assert!(!test_channel("active", "200000", "100000").is_usable()); - assert!(!test_channel("closed", "0", "100000").is_usable()); - assert!(!test_channel("closing", "0", "100000").is_usable()); + fn usable() { + assert!(is_usable(&test_channel("active", "5000", "100000"))); + assert!(!is_usable(&test_channel("active", "100000", "100000"))); + assert!(!is_usable(&test_channel("active", "200000", "100000"))); + assert!(!is_usable(&test_channel("closed", "0", "100000"))); + assert!(!is_usable(&test_channel("closing", "0", "100000"))); } #[test] @@ -251,8 +249,12 @@ mod tests { opened: true, }; - let persisted = PersistedChannel::from_channel_entry(&entry, 100_000, "https://rpc.test"); - let restored = persisted.to_channel_entry().expect("should parse back"); + let payer = Address::random(); + let payee = Address::random(); + let token = Address::random(); + let persisted = + from_channel_entry(&entry, 100_000, "https://rpc.test", &payer, &payee, &token, &payer); + let restored = to_channel_entry(&persisted).expect("should parse back"); assert_eq!(restored.channel_id, entry.channel_id); assert_eq!(restored.salt, entry.salt); @@ -262,46 +264,6 @@ mod tests { assert!(restored.opened); } - #[test] - fn load_evicts_and_handles_edge_cases() { - let dir = tempfile::tempdir().unwrap(); - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - - let store = ChannelStore { - version: SCHEMA_VERSION, - channels: HashMap::from([ - ("active".into(), test_channel("active", "1000", "100000")), - ("spent".into(), test_channel("active", "100000", "100000")), - ("closed".into(), test_channel("closed", "0", "100000")), - ]), - }; - let json = serde_json::to_string(&store).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), &json).unwrap(); - - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - let loaded = load_channels(); - unsafe { std::env::remove_var("TEMPO_HOME") }; - - assert_eq!(loaded.len(), 1); - assert!(loaded.contains_key("active")); - } - - #[test] - fn load_missing_and_wrong_version() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - assert!(load_channels().is_empty()); - - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), r#"{"version": 999, "channels": {}}"#) - .unwrap(); - assert!(load_channels().is_empty()); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } - #[test] fn find_channel_filters_unusable() { let mut channels = HashMap::new(); @@ -312,34 +274,4 @@ mod tests { assert!(find_channel(&channels, "spent").is_none()); assert!(find_channel(&channels, "missing").is_none()); } - - #[test] - fn upsert_inserts_and_updates() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - - let mut channels = HashMap::new(); - let entry = ChannelEntry { - channel_id: B256::random(), - salt: B256::random(), - cumulative_amount: 1000, - escrow_contract: Address::random(), - chain_id: 42431, - opened: true, - }; - - upsert_channel(&mut channels, "key1", &entry, 100_000, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "1000"); - assert_eq!(channels["key1"].deposit, "100000"); - let created_at = channels["key1"].created_at; - - let mut updated = entry.clone(); - updated.cumulative_amount = 5000; - upsert_channel(&mut channels, "key1", &updated, 0, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "5000"); - assert_eq!(channels["key1"].deposit, "100000"); - assert_eq!(channels["key1"].created_at, created_at); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } } diff --git a/crates/common/src/provider/mpp/session.rs b/crates/common/src/provider/mpp/session.rs index 5996c933218d2..334166b844613 100644 --- a/crates/common/src/provider/mpp/session.rs +++ b/crates/common/src/provider/mpp/session.rs @@ -6,8 +6,9 @@ //! `eth_getTransactionCount`. This avoids the chicken-and-egg problem when //! the RPC endpoint is itself 402-gated. -use super::persist::{self, PersistedChannel}; +use super::persist; use alloy_primitives::{Address, B256, Bytes, TxKind, U256}; +use foundry_wallets::Channel; use mpp::{ client::{ PaymentProvider, @@ -42,7 +43,7 @@ static GLOBAL_CHANNELS: OnceLock>> = O /// /// Using a single map ensures saves from different origins don't clobber /// each other's state. -static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); +static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); /// Tracks uncommitted channel state from the most recent payment. /// @@ -86,7 +87,7 @@ pub struct SessionProvider { default_deposit: Option, channels: Arc>>, key_provisioned: Arc>, - persisted: Arc>>, + persisted: Arc>>, /// Tracks uncommitted open/top-up state for deferred persistence. pending: Arc>>, /// Chain ID from the key entry in `keys.toml` that was used to initialize @@ -128,10 +129,10 @@ impl SessionProvider { map.entry(origin.clone()) .or_insert_with(|| { // Hydrate only channels belonging to this origin. - let mut channels = HashMap::new(); + let mut channels: HashMap = HashMap::new(); for (key, ch) in persisted.lock().unwrap().iter() { if ch.origin == origin - && let Some(entry) = ch.to_channel_entry() + && let Some(entry) = persist::to_channel_entry(ch) { channels.insert(key.clone(), entry); } @@ -209,16 +210,16 @@ impl SessionProvider { // Lock order: channels → persisted (consistent with pay_session) let mut channels = self.channels.lock().unwrap(); let mut persisted = self.persisted.lock().unwrap(); - let keys_to_remove: Vec = persisted + let keys_to_remove: Vec<(String, String)> = persisted .iter() .filter(|(_, ch)| ch.origin == *origin) - .map(|(k, _)| k.clone()) + .map(|(k, ch): (&String, &Channel)| (k.clone(), ch.channel_id.clone())) .collect(); - for key in &keys_to_remove { + for (key, channel_id) in &keys_to_remove { channels.remove(key); persisted.remove(key); + persist::delete_channel_from_db(channel_id); } - persist::save_channels(&persisted); } /// Mark whether the access key has been provisioned on-chain. @@ -685,13 +686,7 @@ impl SessionProvider { // confirms acceptance. let updated_entry = ChannelEntry { cumulative_amount: new_cumulative, ..entry }; let mut persisted = self.persisted.lock().unwrap(); - persist::upsert_channel_in_memory( - &mut persisted, - &key, - &updated_entry, - 0, - &self.origin, - ); + persist::upsert_channel_in_memory(&mut persisted, &key, &updated_entry); drop(persisted); // Track the voucher so we can roll back cumulative_amount @@ -727,9 +722,18 @@ impl SessionProvider { // Update in-memory state but defer disk persistence until server confirms. self.channels.lock().unwrap().insert(key.clone(), entry.clone()); + let authorized_signer = self.authorized_signer.unwrap_or(payer); self.persisted.lock().unwrap().insert( key.clone(), - persist::PersistedChannel::from_channel_entry(&entry, deposit, &self.origin), + persist::from_channel_entry( + &entry, + deposit, + &self.origin, + &payer, + &payee, + ¤cy, + &authorized_signer, + ), ); *self.pending.lock().unwrap() = Some(PendingAction::Open { key }); Ok(build_credential(challenge, payload, chain_id, payer)) diff --git a/crates/evm/abi/src/Console.json b/crates/evm/abi/src/Console.json index 54e6d46dff349..f275f471087ee 100644 --- a/crates/evm/abi/src/Console.json +++ b/crates/evm/abi/src/Console.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file diff --git a/crates/evm/abi/src/console.py b/crates/evm/abi/src/console.py index 2e28fece21e42..d0b32ff410941 100755 --- a/crates/evm/abi/src/console.py +++ b/crates/evm/abi/src/console.py @@ -16,12 +16,15 @@ def main(): # Parse signatures from `console.sol`'s string literals console_sol = open(console_file).read() sig_strings = re.findall( - r'"(log.*?)"', + r'"((?:log|table).*?)"', console_sol, ) raw_sigs = [s.strip().strip('"') for s in sig_strings] sigs = [ - s.replace("string", "string memory").replace("bytes)", "bytes memory)") + re.sub(r"(\w+\[\])", r"\1 memory", s) + .replace("string,", "string memory,") + .replace("string)", "string memory)") + .replace("bytes)", "bytes memory)") for s in raw_sigs ] sigs = list(set(sigs)) @@ -38,6 +41,7 @@ def main(): ) combined = json.loads(r.stdout.strip()) abi = combined["contracts"][":HardhatConsole"]["abi"] + open(abi_file, "w").write(json.dumps(abi, separators=(",", ":"), indent=None)) diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index 26acbe56a97cb..c591b9426bf9e 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -50,7 +50,9 @@ impl LogCollector { fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> { let decoded = console::hh::ConsoleCalls::abi_decode(data)?; - self.push_msg(&decoded.fmt(Default::default())); + for line in decoded.fmt(Default::default()).lines() { + self.push_msg(line); + } Ok(()) } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9a7a237855b97..ecdc13ded67cf 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -51,7 +51,7 @@ forge-script.workspace = true forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } alloy-chains.workspace = true alloy-consensus.workspace = true diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 122020571ad24..a48d2a9350c70 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -9,7 +9,7 @@ use alloy_provider::{PendingTransactionError, Provider, ProviderBuilder as Alloy use alloy_signer::{Signature, Signer}; use alloy_transport::TransportError; use clap::{Parser, ValueHint}; -use eyre::{Context, Result}; +use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, @@ -33,10 +33,12 @@ use foundry_config::{ }, merge_impl_figment_convert, }; -use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; +use foundry_wallets::{ + BrowserWalletOpts, TempoAccessKeyConfig, WalletSigner, wallet_browser::signer::BrowserSigner, +}; use serde_json::json; use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc, time::Duration}; -use tempo_alloy::TempoNetwork; +use tempo_alloy::{TempoNetwork, contracts::precompiles::DEFAULT_FEE_TOKEN}; merge_impl_figment_convert!(CreateArgs, build, eth); @@ -101,14 +103,29 @@ pub struct CreateArgs { #[command(flatten)] retry: RetryArgs, + + /// Browser wallet options + #[command(flatten)] + browser: BrowserWalletOpts, } impl CreateArgs { /// Executes the command to create a contract - pub async fn run(self) -> Result<()> { + pub async fn run(mut self) -> Result<()> { let (signer, tempo_access_key) = self.eth.wallet.maybe_signer().await?; - if tempo_access_key.is_some() || self.tx.tempo.is_tempo() { + // Resolve chain early so we can dispatch to the correct network type. + if self.chain_id().is_none() { + let config = self.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let chain_id = provider.get_chain_id().await?; + self.eth.etherscan.chain = Some(chain_id.into()); + } + + if tempo_access_key.is_some() + || self.tx.tempo.is_tempo() + || self.chain_id().is_some_and(|c| c.is_tempo()) + { self.run_generic::(signer, tempo_access_key).await } else { self.run_generic::(signer, None).await @@ -185,17 +202,29 @@ impl CreateArgs { self.tx.tempo.key_id = Some(ak.key_address); } - // respect chain, if set explicitly via cmd args - let chain_id = if let Some(chain_id) = self.chain_id() { - chain_id - } else { - provider.get_chain_id().await? - }; - // Whether to broadcast the transaction or not let dry_run = !self.broadcast; - if self.unlocked { + // Launch browser signer if `--browser` flag is set + let browser = self.browser.run::().await?; + + if let Some(browser) = browser { + // Deploy with browser wallet + let deployer_address = browser.address(); + self.deploy( + abi, + bin, + params, + provider, + deployer_address, + config.transaction_timeout, + id, + dry_run, + None, + Some(browser), + ) + .await + } else if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); self.deploy( @@ -203,12 +232,12 @@ impl CreateArgs { bin, params, provider, - chain_id, sender, config.transaction_timeout, id, dry_run, None, + None, ) .await } else if let Some(ak) = access_key { @@ -223,12 +252,12 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer_address, config.transaction_timeout, id, dry_run, Some((signer, ak)), + None, ) .await } else { @@ -246,20 +275,20 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer, config.transaction_timeout, id, dry_run, None, + None, ) .await } } - /// Returns the provided chain id, if any. - fn chain_id(&self) -> Option { - self.eth.etherscan.chain.map(|chain| chain.id()) + /// Returns the resolved chain, if any. + const fn chain_id(&self) -> Option { + self.eth.etherscan.chain } /// Ensures the verify command can be executed. @@ -271,7 +300,6 @@ impl CreateArgs { async fn verify_preflight_check( &self, constructor_args: Option, - chain: u64, id: &ArtifactId, ) -> Result<()> { // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, @@ -287,7 +315,7 @@ impl CreateArgs { num_of_optimizations: None, etherscan: EtherscanOpts { key: self.eth.etherscan.key.clone(), - chain: Some(chain.into()), + chain: self.chain_id(), }, rpc: Default::default(), flatten: false, @@ -311,7 +339,7 @@ impl CreateArgs { // ETHERSCAN_API_KEY value set. let config = verify.load_config()?; verify.etherscan.key = - config.get_etherscan_config_with_chain(Some(chain.into()))?.map(|c| c.key); + config.get_etherscan_config_with_chain(self.chain_id())?.map(|c| c.key); let context = verify.resolve_context().await?; @@ -327,17 +355,19 @@ impl CreateArgs { bin: BytecodeObject, args: Vec, provider: P, - chain: u64, deployer_address: Address, timeout: u64, id: ArtifactId, dry_run: bool, tempo_keychain: Option<(WalletSigner, TempoAccessKeyConfig)>, + browser_signer: Option>, ) -> Result<()> where N::TransactionRequest: FoundryTransactionBuilder + serde::Serialize, N::ReceiptResponse: serde::Serialize, { + let chain = self.chain_id().context("chain ID not resolved")?; + let bin = bin.into_bytes().unwrap_or_default(); if bin.is_empty() { eyre::bail!("no bytecode found in bin object for {}", self.contract.name) @@ -349,22 +379,31 @@ impl CreateArgs { let is_args_empty = args.is_empty(); let mut deployer = - factory.deploy_tokens(args.clone(), self.tx.tempo.fee_token).context("failed to deploy contract").map_err(|e| { + 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") } else { e } })?; - let is_legacy = self.tx.legacy || Chain::from(chain).is_legacy(); + let is_legacy = self.tx.legacy || chain.is_legacy(); deployer.tx.set_from(deployer_address); - deployer.tx.set_chain_id(chain); + deployer.tx.set_chain_id(chain.id()); // `to` field must be set explicitly, cannot be None. if deployer.tx.to().is_none() { deployer.tx.set_create(); } + // If Tempo chain fee token must be set + if chain.is_tempo() { + if let Some(fee_token) = self.tx.tempo.fee_token { + deployer.tx.set_fee_token(fee_token); + } else { + deployer.tx.set_fee_token(DEFAULT_FEE_TOKEN); + } + } + // Apply user-provided gas, fee, nonce, and Tempo options. self.tx.apply::(&mut deployer.tx, is_legacy); @@ -422,7 +461,7 @@ impl CreateArgs { constructor_args = Some(hex::encode(encoded_args)); } - self.verify_preflight_check(constructor_args.clone(), chain, &id).await?; + self.verify_preflight_check(constructor_args.clone(), &id).await?; } if dry_run { @@ -452,7 +491,31 @@ impl CreateArgs { } // Deploy the actual contract - let (deployed_contract, receipt) = if let Some((signer, ak)) = tempo_keychain { + let (deployed_contract, receipt) = if let Some(browser) = browser_signer { + // Browser wallet signs and sends the transaction + let tx_hash = browser.send_transaction_via_browser(deployer.tx).await?; + + // Wait for the transaction to be confirmed, then fetch the receipt. + provider + .watch_pending_transaction(alloy_provider::PendingTransactionConfig::new(tx_hash)) + .await? + .await?; + + let receipt = provider + .get_transaction_receipt(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("could not get transaction receipt for {tx_hash}"))?; + + if !receipt.status() { + eyre::bail!("deployment transaction failed (receipt status 0): {tx_hash}"); + } + + let address = receipt + .contract_address() + .ok_or_else(|| eyre::eyre!("contract was not deployed"))?; + + (address, receipt) + } else if let Some((signer, ak)) = tempo_keychain { // Tempo keychain mode: sign with access key provisioning and send raw let raw_tx = deployer .tx @@ -518,7 +581,7 @@ impl CreateArgs { no_auto_detect: false, use_solc: None, num_of_optimizations, - etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) }, + etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain) }, rpc: Default::default(), flatten: false, force: false, @@ -681,7 +744,6 @@ impl + Clone> DeploymentTxFactory { pub fn deploy_tokens( self, params: Vec, - fee_token: Option
, ) -> Result, ContractDeploymentError> where N::TransactionRequest: FoundryTransactionBuilder, @@ -703,9 +765,6 @@ impl + Clone> DeploymentTxFactory { // create the tx object. Since we're deploying a contract, `to` is `None` let mut tx = N::TransactionRequest::default(); tx.set_input(data); - if let Some(fee_token) = fee_token { - tx.set_fee_token(fee_token); - } Ok(Deployer { client: self.client.clone(), tx, confs: 1, timeout: self.timeout }) } } @@ -763,7 +822,7 @@ mod tests { "--chain-id", "9999", ]); - assert_eq!(args.chain_id(), Some(9999)); + assert_eq!(args.chain_id().map(|c| c.id()), Some(9999)); } #[test] diff --git a/crates/macros/src/console_fmt.rs b/crates/macros/src/console_fmt.rs index 0c95841d1e653..245ba6e5b78e2 100644 --- a/crates/macros/src/console_fmt.rs +++ b/crates/macros/src/console_fmt.rs @@ -7,7 +7,7 @@ use syn::{ pub fn console_fmt(input: &DeriveInput) -> TokenStream { let name = &input.ident; let tokens = match &input.data { - Data::Struct(s) => derive_struct(s), + Data::Struct(s) => derive_struct(s, name), Data::Enum(e) => derive_enum(e), Data::Union(_) => return quote!(compile_error!("Unions are unsupported");), }; @@ -18,8 +18,8 @@ pub fn console_fmt(input: &DeriveInput) -> TokenStream { } } -fn derive_struct(s: &DataStruct) -> TokenStream { - let imp = impl_struct(s).unwrap_or_else(|| quote!(String::new())); +fn derive_struct(s: &DataStruct, name: &Ident) -> TokenStream { + let imp = impl_struct(s, name).unwrap_or_else(|| quote!(String::new())); quote! { fn fmt(&self, _spec: FormatSpec) -> String { #imp @@ -27,7 +27,7 @@ fn derive_struct(s: &DataStruct) -> TokenStream { } } -fn impl_struct(s: &DataStruct) -> Option { +fn impl_struct(s: &DataStruct, name: &Ident) -> Option { if s.fields.is_empty() { return None; } @@ -36,13 +36,49 @@ fn impl_struct(s: &DataStruct) -> Option { return None; } + let members = s.fields.members().collect::>(); let fields = s.fields.iter().collect::>(); + + // Detect table call structs: name must start with "table" (from the ABI function name) and + // all fields must be Vec types (Solidity arrays). Both conditions together prevent + // accidental table rendering for unrelated structs that happen to have Vec fields. + let is_table = name.to_string().starts_with("table") + && !fields.is_empty() + && fields.iter().all(|f| match &f.ty { + Type::Path(path) => path.path.segments.last().is_some_and(|seg| seg.ident == "Vec"), + _ => false, + }); + if is_table { + let member_ref = |m: &Member| match m { + Member::Named(ident) => quote!(&self.#ident), + Member::Unnamed(idx) => quote!(&self.#idx), + }; + let imp = if members.len() == 1 { + let vals = member_ref(&members[0]); + quote! { + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(None, &values) + } + } else { + let keys = member_ref(&members[0]); + let vals = member_ref(&members[1]); + quote! { + let keys: ::std::vec::Vec<&dyn ConsoleFmt> = + (#keys).iter().map(|v| v as &dyn ConsoleFmt).collect(); + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(Some(&keys), &values) + } + }; + return Some(imp); + } + let first_ty = match &fields.first().unwrap().ty { Type::Path(path) => path.path.segments.last().unwrap().ident.to_string(), _ => String::new(), }; - let members = s.fields.members().collect::>(); let args: Punctuated = members .into_iter() .map(|member| match member { diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 65b75b8b39bb1..332420d0d1c42 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -23,7 +23,7 @@ foundry-evm-networks.workspace = true alloy-evm.workspace = true foundry-debugger.workspace = true foundry-cheatcodes.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } foundry-linking.workspace = true forge-script-sequence.workspace = true diff --git a/npm/bun.lock b/npm/bun.lock index e8ef087be9d50..d9e8fed5ce479 100644 --- a/npm/bun.lock +++ b/npm/bun.lock @@ -1,12 +1,13 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.5.2", "bun": "^1.3.1", - "typescript": "^5.9.3", + "typescript": "^6.0.2", }, }, }, @@ -35,7 +36,7 @@ "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], - "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], @@ -45,8 +46,12 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "bun-types/@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], } } diff --git a/npm/scripts/publish.mjs b/npm/scripts/publish.mjs index 0f582c8ebc555..deee29e46c92f 100644 --- a/npm/scripts/publish.mjs +++ b/npm/scripts/publish.mjs @@ -7,17 +7,12 @@ import { colors } from '#const.mjs' const REGISTRY_URL = Bun.env.NPM_REGISTRY_URL || 'https://registry.npmjs.org' -const NPM_TOKEN = Bun.env.NPM_TOKEN -if (!NPM_TOKEN) throw new Error('NPM_TOKEN is required') - main().catch(error => { console.error(error) process.exit(1) }) async function main() { - const npmToken = Bun.env.NPM_TOKEN - if (!npmToken) throw new Error('NPM_TOKEN is required') const inputPath = Bun.argv[2] if (!inputPath) throw new Error('Package path is required') @@ -114,11 +109,6 @@ async function setPackageVersion(packagePath, version) { console.info(colors.green, 'Setting package version:', version) const result = await Bun.$`npm version ${version} --allow-same-version --no-git-tag-version` .cwd(packagePath) - .env({ - ...Bun.env, - ...process.env, - NPM_TOKEN - }) .quiet() .nothrow() diff --git a/npm/tsconfig.json b/npm/tsconfig.json index ca8e3c92f2daa..a7a0093d7ee56 100644 --- a/npm/tsconfig.json +++ b/npm/tsconfig.json @@ -2,7 +2,6 @@ "schema": "https://json.schemastore.org/tsconfig.json", "compilerOptions": { "strict": true, - "baseUrl": ".", "noEmit": true, "allowJs": true, "checkJs": true, From d0f3cff79cce76d6e152521924ba26042cbd8eed Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:34:42 +0000 Subject: [PATCH 332/374] fix(npm): remove deprecated baseUrl compiler option (foundry-rs#14413) (#467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- .github/scripts/tempo-mpp.sh | 18 +- .github/workflows/benchmarks.yml | 2 +- .github/workflows/ci.yml | 4 +- .github/workflows/crate-checks.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/npm.yml | 11 +- .github/workflows/release.yml | 2 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 2 +- Cargo.lock | 99 ++++-- Cargo.toml | 6 +- Makefile | 41 ++- crates/cast/Cargo.toml | 2 +- crates/cast/src/call_spec.rs | 10 +- crates/cast/src/cmd/erc20.rs | 103 +++++- crates/cli/Cargo.toml | 2 +- crates/cli/src/utils/mod.rs | 14 +- crates/common/Cargo.toml | 1 + crates/common/fmt/Cargo.toml | 1 + crates/common/fmt/src/console.rs | 107 ++++++ crates/common/fmt/src/lib.rs | 2 +- crates/common/src/provider/mpp/persist.rs | 410 +++++++++------------- crates/common/src/provider/mpp/session.rs | 38 +- crates/evm/abi/src/Console.json | 2 +- crates/evm/abi/src/console.py | 8 +- crates/evm/evm/src/inspectors/logs.rs | 4 +- crates/forge/Cargo.toml | 2 +- crates/forge/src/cmd/create.rs | 127 +++++-- crates/macros/src/console_fmt.rs | 46 ++- crates/script/Cargo.toml | 2 +- npm/bun.lock | 15 +- npm/scripts/publish.mjs | 10 - npm/tsconfig.json | 1 - 34 files changed, 710 insertions(+), 390 deletions(-) diff --git a/.github/scripts/tempo-mpp.sh b/.github/scripts/tempo-mpp.sh index 1a98b1ed9c494..0d34c4a3b5fa5 100755 --- a/.github/scripts/tempo-mpp.sh +++ b/.github/scripts/tempo-mpp.sh @@ -110,7 +110,7 @@ BLOCK2=$("$CAST" block-number --rpc-url "$RPC_MPP") AFTER2=$("$CAST" erc20 balance "$TOKEN" "$WALLET" --rpc-url "$RPC" | awk '{print $1}') SPENT2=$((BEFORE2 - AFTER2)) echo "Block: $BLOCK2" -echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/foundry/channels.json)" +echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/channels.db)" # 6. forge script via MPP echo "" @@ -129,9 +129,9 @@ contract MppCheck is Script { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" script "$TMPDIR/script/Mpp.s.sol" --rpc-url "$RPC_MPP" --root "$TMPDIR" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 7. forge test with vm.createSelectFork via MPP @@ -149,15 +149,15 @@ contract MppForkTest is Test { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" test --match-test test_fork_via_mpp --root "$TMPDIR" -vvv -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 8. anvil fork via MPP echo "" echo "=== 8. anvil --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$ANVIL" --fork-url "$RPC_MPP" --port 8555 --silent & ANVIL_PID=$! for _ in $(seq 1 30); do @@ -167,15 +167,15 @@ done echo "chain-id: $("$CAST" chain-id --rpc-url http://localhost:8555)" kill $ANVIL_PID 2>/dev/null wait $ANVIL_PID 2>/dev/null -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 9. chisel fork via MPP echo "" echo "=== 9. chisel --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo 'block.number' | "$CHISEL" --fork-url "$RPC_MPP" 2>&1 | grep -E "Decimal|Type" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" echo "" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 96af1f4ab9a1e..8465874854551 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -61,7 +61,7 @@ jobs: printf '%s\n' "$GITHUB_WORKSPACE/.foundry/bin" >> "$GITHUB_PATH" - name: Build benchmark binary - run: cargo build --release --bin foundry-bench + run: cargo build --locked --release --bin foundry-bench - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ce8f1bc25bd9..53d009de4c442 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo test --workspace --doc + - run: cargo test --workspace --doc --locked typos: runs-on: depot-ubuntu-latest @@ -92,7 +92,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo clippy --workspace --all-targets --all-features + - run: cargo clippy --workspace --all-targets --all-features --locked env: RUSTFLAGS: -Dwarnings diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index 781d63759045e..b2eb6261efd63 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -34,7 +34,7 @@ jobs: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo hack check + - run: cargo hack check --locked issue: name: Open an issue diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a8a3dcbb8c7d3..288d9d127592f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: Build documentation - run: cargo doc --workspace --all-features --no-deps --document-private-items + run: cargo doc --workspace --all-features --no-deps --document-private-items --locked env: RUSTDOCFLAGS: --cfg docsrs -D warnings --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options - name: Setup Pages diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 9b9b798435c90..fdc5e5716c577 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -31,6 +31,7 @@ jobs: contents: read actions: read id-token: write + environment: release name: ${{ matrix.tool }}-${{ matrix.os }}-${{ matrix.arch }} runs-on: ubuntu-latest strategy: @@ -151,9 +152,6 @@ jobs: id: release-version working-directory: ./npm env: - PROVENANCE: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} run: | @@ -224,8 +222,6 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ steps.release-version.outputs.RELEASE_VERSION }} RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | set -euo pipefail @@ -245,12 +241,11 @@ jobs: contents: read actions: read id-token: write + environment: release needs: publish-arch name: Publish Meta Package runs-on: ubuntu-latest env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} RELEASE_VERSION: ${{ needs.publish-arch.outputs.RELEASE_VERSION }} steps: - name: Checkout @@ -286,6 +281,4 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ env.RELEASE_VERSION }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} - NPM_TOKEN: ${{ env.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ env.NODE_AUTH_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8c2b4b7d2700..52b33787de3f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -179,7 +179,7 @@ jobs: shell: bash run: | set -eo pipefail - flags=(--target $TARGET --profile $RUST_PROFILE --bins + flags=(--locked --target $TARGET --profile $RUST_PROFILE --bins --no-default-features --features "$RUST_FEATURES") # `jemalloc` is not fully supported on MSVC or aarch64 Linux. diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index af4415b1e8ba1..c0e4fcb5f3263 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -50,7 +50,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --no-fail-fast + run: cargo nextest run --locked --profile flaky --no-fail-fast # If any of the jobs fail, this will create a normal-priority issue to signal so. issue: diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index afd91d3d72c05..37c3edc9b916a 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -54,7 +54,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --features=isolate-by-default --no-fail-fast + run: cargo nextest run --locked --profile flaky --features=isolate-by-default --no-fail-fast # If nextest fails, create a high-priority issue for isolation failures. issue-isolate: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 93c57d29ec1dd..7c79cd266f152 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -121,4 +121,4 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run ${{ matrix.flags }} + run: cargo nextest run --locked ${{ matrix.flags }} diff --git a/Cargo.lock b/Cargo.lock index d15f7c092e642..5a102a3276c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,7 +1194,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1218,7 +1218,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2558,7 +2558,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -3021,7 +3021,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3174,7 +3174,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3958,7 +3958,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4263,7 +4263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4376,6 +4376,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast-float2" version = "0.2.3" @@ -5009,6 +5021,7 @@ dependencies = [ "foundry-common-fmt", "foundry-compilers", "foundry-config", + "foundry-wallets", "itertools 0.14.0", "jiff", "mpp", @@ -5048,6 +5061,7 @@ dependencies = [ "alloy-rpc-types", "alloy-serde 2.0.0", "chrono", + "comfy-table", "eyre", "foundry-macros", "op-alloy-consensus", @@ -5544,7 +5558,7 @@ dependencies = [ [[package]] name = "foundry-wallets" version = "0.1.0" -source = "git+https://github.com/foundry-rs/foundry-core?rev=7f401c1397af90a0a94ef7424a48bbf3dc0248cc#7f401c1397af90a0a94ef7424a48bbf3dc0248cc" +source = "git+https://github.com/foundry-rs/foundry-core?rev=d6c92dfcb41be26fbc1de21221a5bfc6bdd93245#d6c92dfcb41be26fbc1de21221a5bfc6bdd93245" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -5568,6 +5582,7 @@ dependencies = [ "eth-keystore", "eyre", "rpassword", + "rusqlite", "serde", "serde_json", "tempo-primitives", @@ -5970,6 +5985,15 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -6565,7 +6589,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -6920,6 +6944,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -7480,7 +7515,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7701,7 +7736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b685c8311c9171d1bd2895222965d25616b2de2cb5819dd3504ed9250df9fecd" dependencies = [ "ahash", - "hashbrown 0.17.0", + "hashbrown 0.16.1", "parking_lot", "stable_deref_trait", ] @@ -8464,7 +8499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8633,7 +8668,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -9930,6 +9965,20 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rusqlite" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b" +dependencies = [ + "bitflags 2.11.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -10000,7 +10049,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -10059,7 +10108,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -10070,9 +10119,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -10762,7 +10811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -10830,7 +10879,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.12.1", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -10865,7 +10914,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.12.1", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -11289,7 +11338,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -11494,7 +11543,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -11504,7 +11553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -12721,7 +12770,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -12871,7 +12920,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f902786070d81..9ca49c1bfc48a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -355,7 +355,7 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features ] } ## foundry-core -foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "7f401c1397af90a0a94ef7424a48bbf3dc0248cc", default-features = false } +foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "d6c92dfcb41be26fbc1de21221a5bfc6bdd93245", default-features = false } ## alloy alloy-consensus = { version = "2.0.0", default-features = false } @@ -382,7 +382,6 @@ alloy-signer-local = { version = "2.0.0", default-features = false } alloy-signer-trezor = { version = "2.0.0", default-features = false } alloy-signer-turnkey = { version = "2.0.0", default-features = false } alloy-transport = { version = "2.0.0", default-features = false } -alloy-transport-http = { version = "2.0.0", default-features = false } alloy-transport-ipc = { version = "2.0.0", default-features = false } alloy-transport-ws = { version = "2.0.0", default-features = false } alloy-hardforks = { version = "0.4.7", default-features = false } @@ -620,3 +619,6 @@ solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/sola solar-interface = { package = "solar-interface", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-ast = { package = "solar-ast", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-sema = { package = "solar-sema", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } + +[workspace.metadata.cargo-shear] +ignored = ["idna_adapter", "cast", "chisel", "forge", "alloy-contract"] diff --git a/Makefile b/Makefile index fe768f19abf43..1bf20f4eeebd1 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ help: ## Display this help. .PHONY: build build: ## Build the project. - cargo build --features "$(FEATURES)" --profile "$(PROFILE)" + cargo build --locked --features "$(FEATURES)" --profile "$(PROFILE)" .PHONY: build-docker build-docker: ## Build the docker image. @@ -47,17 +47,17 @@ build-docker: ## Build the docker image. ## is unstable (https://github.com/taiki-e/cargo-llvm-cov/issues/2). .PHONY: test-coverage test-coverage: - cargo +nightly llvm-cov --no-report nextest -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ - cargo +nightly llvm-cov --no-report --doc && \ + cargo +nightly llvm-cov --no-report nextest --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ + cargo +nightly llvm-cov --no-report --doc --locked && \ cargo +nightly llvm-cov report --doctests --open .PHONY: test-unit test-unit: ## Run unit tests. - cargo nextest run -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' + cargo nextest run --workspace --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' .PHONY: test-doc test-doc: ## Run doc tests. - cargo test --doc --workspace + cargo test --doc --workspace --locked .PHONY: test test: ## Run all tests. @@ -77,6 +77,7 @@ lint-clippy: ## Run clippy on the codebase. --workspace \ --all-targets \ --all-features \ + --locked \ -- -D warnings .PHONY: lint-clippy-fix @@ -85,6 +86,7 @@ lint-clippy-fix: ## Run clippy on the codebase and fix warnings. --workspace \ --all-targets \ --all-features \ + --locked \ --fix \ --allow-dirty \ --allow-staged \ @@ -104,21 +106,46 @@ lint: ## Run all linters. $(MAKE) lint-clippy && \ $(MAKE) lint-typos +##@ Documentation + +.PHONY: doc +doc: ## Build the documentation. + RUSTDOCFLAGS="--cfg docsrs -D warnings -Zunstable-options --show-type-layout --generate-link-to-definition" \ + cargo +nightly doc \ + --workspace \ + --all-features \ + --document-private-items \ + --no-deps \ + --locked + ##@ Other +.PHONY: lock +lock: ## Update the Cargo.lock file with the current dependencies. + cargo fetch + .PHONY: clean clean: ## Clean the project. cargo clean .PHONY: deny deny: ## Perform a `cargo` deny check. - cargo deny --all-features check all + cargo deny --locked --all-features check all + +.PHONY: check +check: ## Run a feature check on all crates and binaries. + cargo hack check --locked --feature-powerset --depth 1 + +.PHONY: shear +shear: ## Run `cargo shear` to check for unused dependencies. + cargo shear --locked .PHONY: pr pr: ## Run all checks and tests. $(MAKE) deny && \ $(MAKE) lint && \ - $(MAKE) test + $(MAKE) test && \ + $(MAKE) doc # dprint formatting commands .PHONY: dprint-fmt diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index dbd89ce88827d..56164c8f6367a 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -28,7 +28,7 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-debugger.workspace = true foundry-evm.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } forge-fmt.workspace = true alloy-chains.workspace = true diff --git a/crates/cast/src/call_spec.rs b/crates/cast/src/call_spec.rs index b39e024f38af5..0047b56d25f03 100644 --- a/crates/cast/src/call_spec.rs +++ b/crates/cast/src/call_spec.rs @@ -145,8 +145,8 @@ impl FromStr for CallSpec { /// Parse a value string that can be in ether notation (e.g., "0.1ether") or raw wei. fn parse_ether_or_wei(s: &str) -> Result { // Use alloy's DynSolType coercion which handles "1ether", "1gwei", "1000" etc. - if s.starts_with("0x") { - U256::from_str_radix(s, 16).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) + if s.starts_with("0x") || s.starts_with("0X") { + U256::from_str(s).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), s) .wrap_err_with(|| format!("Invalid value '{s}'"))? @@ -180,6 +180,12 @@ mod tests { assert!(spec.sig.is_none()); } + #[test] + fn test_parse_hex_value() { + assert_eq!(parse_ether_or_wei("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_or_wei("0X10").unwrap(), U256::from(16)); + } + #[test] fn test_parse_with_sig() { let spec = CallSpec::parse( diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index e629b8eca0d5a..1d27b7a23dcd5 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -3,13 +3,13 @@ use std::{str::FromStr, time::Duration}; use crate::{ cmd::send::{cast_send, cast_send_with_access_key}, format_uint_exp, - tx::{SendTxOpts, TxParams}, + tx::{CastTxSender, SendTxOpts, TxParams}, }; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::BlockId; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network}; -use alloy_primitives::U256; +use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder}; +use alloy_primitives::{Address, U256}; use alloy_provider::{Provider, fillers::RecommendedFillers}; use alloy_signer::Signature; use alloy_sol_types::sol; @@ -28,6 +28,7 @@ use foundry_common::{ pub use foundry_config::{Chain, utils::*}; use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; +use tempo_contracts::precompiles::PATH_USD_ADDRESS; sol! { #[sol(rpc)] @@ -281,6 +282,34 @@ impl Erc20Subcommand { } } + const fn uses_browser_send(&self) -> bool { + match self { + Self::Transfer { send_tx, .. } + | Self::Approve { send_tx, .. } + | Self::Mint { send_tx, .. } + | Self::Burn { send_tx, .. } => send_tx.browser.browser, + _ => false, + } + } + + async fn should_use_tempo_network( + &self, + tempo_access_key: &Option, + ) -> eyre::Result { + if self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) + || tempo_access_key.is_some() + { + return Ok(true); + } + + if self.uses_browser_send() { + let config = self.rpc_opts().load_config()?; + return Ok(get_chain(config.chain, &get_provider(&config)?).await?.is_tempo()); + } + + Ok(false) + } + pub async fn run(self) -> eyre::Result<()> { // Resolve the signer once for state-changing variants. let (signer, tempo_access_key) = match &self { @@ -299,8 +328,7 @@ impl Erc20Subcommand { _ => (None, None), }; - let is_tempo = self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) - || tempo_access_key.is_some(); + let is_tempo = self.should_use_tempo_network(&tempo_access_key).await?; if is_tempo { self.run_generic::(signer, tempo_access_key).await @@ -356,6 +384,28 @@ impl Erc20Subcommand { timeout, ) .await? + } else if let Some(browser) = $send_tx.browser.run::().await? { + let $provider = ProviderBuilder::::from_config(&config)?.build()?; + if let Some(interval) = $send_tx.poll_interval { + $provider.client().set_poll_interval(Duration::from_secs(interval)); + } + let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); + let mut tx = { $build_tx }.into_transaction_request(); + let chain = get_chain(config.chain, &$provider).await?; + $tx_opts.apply::(&mut tx, chain.is_legacy()); + if chain.is_tempo() && tx.fee_token().is_none() { + tx.set_fee_token(PATH_USD_ADDRESS); + } + fill_tx(&$provider, &mut tx, browser.address(), chain).await?; + let tx_hash = browser.send_transaction_via_browser(tx).await?; + CastTxSender::new(&$provider) + .print_tx_result( + tx_hash, + $send_tx.cast_async, + $send_tx.confirmations, + timeout, + ) + .await? } else { let signer = pre_resolved_signer.unwrap_or($send_tx.eth.wallet.signer().await?); let $provider = build_provider_with_signer::(&$send_tx, signer)?; @@ -503,3 +553,46 @@ impl Erc20Subcommand { Ok(()) } } + +/// Fills from, chain_id, nonce, fees, and gas limit on a transaction request for the browser +/// wallet path. Mirrors the filling logic in the shared tx builder but operates on a +/// pre-built transaction request from the sol! macro rather than through the builder pipeline. +/// Only fills fields that haven't already been set by the user. +async fn fill_tx>( + provider: &P, + tx: &mut N::TransactionRequest, + from: Address, + chain: Chain, +) -> eyre::Result<()> +where + N::TransactionRequest: FoundryTransactionBuilder, +{ + tx.set_from(from); + tx.set_chain_id(chain.id()); + + if tx.nonce().is_none() { + tx.set_nonce(provider.get_transaction_count(from).await?); + } + + let legacy = chain.is_legacy(); + + if legacy { + if tx.gas_price().is_none() { + tx.set_gas_price(provider.get_gas_price().await?); + } + } else if tx.max_fee_per_gas().is_none() || tx.max_priority_fee_per_gas().is_none() { + let estimate = provider.estimate_eip1559_fees().await?; + if tx.max_fee_per_gas().is_none() { + tx.set_max_fee_per_gas(estimate.max_fee_per_gas); + } + if tx.max_priority_fee_per_gas().is_none() { + tx.set_max_priority_fee_per_gas(estimate.max_priority_fee_per_gas); + } + } + + if tx.gas_limit().is_none() { + tx.set_gas_limit(provider.estimate_gas(tx.clone()).await?); + } + + Ok(()) +} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 165154da88f63..bcbe837e824e3 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,7 +19,7 @@ foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-evm-networks.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } tempo-primitives.workspace = true diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f1c6c435217b7..be566288ef87a 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -129,8 +129,8 @@ where /// If the string represents an untagged amount (e.g. "100") then /// it is interpreted as wei. pub fn parse_ether_value(value: &str) -> Result { - Ok(if value.starts_with("0x") { - U256::from_str_radix(value, 16)? + Ok(if value.starts_with("0x") || value.starts_with("0X") { + U256::from_str(value)? } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), value)? .as_uint() @@ -844,6 +844,16 @@ mod tests { assert!(!p.is_sol_test()); } + #[test] + fn parse_ether_value_accepts_hex_prefixed_wei() { + assert_eq!(parse_ether_value("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0X10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0x12").unwrap(), U256::from(0x12)); + assert_eq!(parse_ether_value("0xff").unwrap(), U256::from(0xff)); + assert_eq!(parse_ether_value("100").unwrap(), U256::from(100)); + assert_eq!(parse_ether_value("1ether").unwrap(), U256::from(1000000000000000000u128)); + } + // loads .env from cwd and project dir, See [`find_project_root()`] #[test] fn can_load_dotenv() { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index ac7a8c12eae71..04c2a06a3fa90 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -82,6 +82,7 @@ flate2.workspace = true tempo-alloy.workspace = true tempo-primitives.workspace = true mpp.workspace = true +foundry-wallets.workspace = true [build-dependencies] chrono.workspace = true diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 78759848fc4bd..2c8e16bccdcc6 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -30,6 +30,7 @@ serde_json.workspace = true chrono.workspace = true revm.workspace = true yansi.workspace = true +comfy-table.workspace = true # Tempo tempo-alloy.workspace = true diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index d751ddba245ba..8b66335445046 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -1,5 +1,6 @@ use super::UIfmt; use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256}; +use comfy_table::{Table, TableComponent, presets::UTF8_FULL}; use std::fmt::{self, Write}; /// A piece is a portion of the format string which represents the next part to emit. @@ -407,6 +408,36 @@ fn format_spec<'a>( } } +pub fn console_table_format( + keys: Option<&[&dyn ConsoleFmt]>, + values: &[&dyn ConsoleFmt], +) -> String { + let keys_strings: Vec = match keys { + Some(keys) => keys.iter().map(|k| k.fmt(FormatSpec::String)).collect(), + None => (0..values.len()).map(|i| i.to_string()).collect(), + }; + let values_strings: Vec = values.iter().map(|v| v.fmt(FormatSpec::String)).collect(); + + let mut table = Table::new(); + table.load_preset(UTF8_FULL); + table.set_style(TableComponent::VerticalLines, '│'); + table.set_style(TableComponent::HeaderLines, '─'); + table.set_style(TableComponent::MiddleHeaderIntersections, '┼'); + table.set_style(TableComponent::LeftHeaderIntersection, '├'); + table.set_style(TableComponent::RightHeaderIntersection, '┤'); + table.set_header(vec!["(index)", "Values"]); + table.remove_style(TableComponent::HorizontalLines); + table.remove_style(TableComponent::MiddleIntersections); + table.remove_style(TableComponent::LeftBorderIntersections); + table.remove_style(TableComponent::RightBorderIntersections); + for i in 0..keys_strings.len().max(values_strings.len()) { + let key = keys_strings.get(i).map(String::as_str).unwrap_or(""); + let value = values_strings.get(i).map(String::as_str).unwrap_or(""); + table.add_row(vec![key, value]); + } + table.to_string() +} + #[cfg(test)] mod tests { use super::*; @@ -610,4 +641,80 @@ mod tests { let call = Logs::Log1(log1); assert_eq!(call.fmt(Default::default()), "foo 42 bar"); } + + #[test] + fn test_console_table_format() { + // auto-indexed, uint256 values + let values: &[&dyn ConsoleFmt] = &[&U256::from(100), &U256::from(200), &U256::from(300)]; + assert_eq!( + console_table_format(None, values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ 0 │ 100 │\n\ + │ 1 │ 200 │\n\ + │ 2 │ 300 │\n\ + └─────────┴────────┘" + ); + + // string keys, uint256 values + // key col expands to fit "charlie123" and value col expands to fit "20000000000000000" + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie123")]; + let values: &[&dyn ConsoleFmt] = &[ + &U256::from(1), + &U256::from_str("20000000000000000").unwrap(), + &U256::from_str("30000000000").unwrap(), + ]; + assert_eq!( + console_table_format(Some(keys), values), + "┌────────────┬───────────────────┐\n\ + │ (index) │ Values │\n\ + ├────────────┼───────────────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 20000000000000000 │\n\ + │ charlie123 │ 30000000000 │\n\ + └────────────┴───────────────────┘" + ); + + // empty table + assert_eq!( + console_table_format(None, &[]), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + └─────────┴────────┘" + ); + + // more keys than values + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie")]; + let values: &[&dyn ConsoleFmt] = &[&U256::from(1), &U256::from(2)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ charlie │ │\n\ + └─────────┴────────┘" + ); + + // more values than keys + let keys: &[&dyn ConsoleFmt] = &[&String::from("alice"), &String::from("bob")]; + let values: &[&dyn ConsoleFmt] = + &[&U256::from(1), &U256::from(2), &U256::from(3), &U256::from(4)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ │ 3 │\n\ + │ │ 4 │\n\ + └─────────┴────────┘" + ); + } } diff --git a/crates/common/fmt/src/lib.rs b/crates/common/fmt/src/lib.rs index 89297a8e2c6b5..45ed47263ce32 100644 --- a/crates/common/fmt/src/lib.rs +++ b/crates/common/fmt/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] mod console; -pub use console::{ConsoleFmt, FormatSpec, console_format}; +pub use console::{ConsoleFmt, FormatSpec, console_format, console_table_format}; mod dynamic; pub use dynamic::{ diff --git a/crates/common/src/provider/mpp/persist.rs b/crates/common/src/provider/mpp/persist.rs index 6d6781ab4c77b..803c7270e041e 100644 --- a/crates/common/src/provider/mpp/persist.rs +++ b/crates/common/src/provider/mpp/persist.rs @@ -1,243 +1,241 @@ //! Persistent channel storage for MPP sessions. //! -//! Stores open payment channel state in a JSON file at -//! `$TEMPO_HOME/foundry/channels.json` (default: `~/.tempo/foundry/channels.json`). +//! Stores open payment channel state in a SQLite database at +//! `$TEMPO_HOME/channels.db` (default: `~/.tempo/channels.db`). //! This allows channel reuse across process invocations, avoiding the cost of //! opening a new on-chain channel for every `cast` / `forge` command. use alloy_primitives::{Address, B256}; +use foundry_wallets::{Channel, ChannelDb}; use mpp::client::channel_ops::ChannelEntry; -use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, - path::PathBuf, + sync::OnceLock, time::{SystemTime, UNIX_EPOCH}, }; use tracing::{debug, warn}; use crate::tempo::tempo_home; -/// Relative path from Tempo home to the Foundry channels file. -const CHANNELS_PATH: &str = "foundry/channels.json"; +/// Process-wide database handle. +fn global_db() -> Option<&'static ChannelDb> { + static DB: OnceLock> = OnceLock::new(); + DB.get_or_init(|| { + let path = tempo_home()?.join("channels.db"); + if let Some(parent) = path.parent() { + let _ = std::fs::create_dir_all(parent); + } + if let Some(old) = + tempo_home().map(|h| h.join("foundry/channels.json")).filter(|p| p.exists()) + { + warn!( + ?old, + "found old channels.json — this file is no longer used; channels will be re-opened" + ); + } -/// Current schema version. -const SCHEMA_VERSION: u64 = 2; + match ChannelDb::open(&path) { + Ok(db) => { + debug!(?path, "opened channel database"); + Some(db) + } + Err(e) => { + warn!(?path, %e, "failed to open channel database"); + None + } + } + }) + .as_ref() +} -/// On-disk representation of the channel store. -#[derive(Debug, Serialize, Deserialize)] -struct ChannelStore { - version: u64, - #[serde(default)] - channels: HashMap, +/// Reconstruct the composite HashMap key from a persisted `Channel`. +/// +/// Mirrors `SessionProvider::channel_key()` in session.rs. +fn channel_key_from_persisted(ch: &Channel) -> String { + let origin_hash = &alloy_primitives::keccak256(ch.origin.as_bytes()).to_string()[..18]; + format!( + "{}:{}:{}:{}:{}:{}:{}", + origin_hash, + ch.chain_id, + ch.payer, + ch.authorized_signer, + ch.payee, + ch.token, + ch.escrow_contract + ) + .to_lowercase() } -impl Default for ChannelStore { - fn default() -> Self { - Self { version: SCHEMA_VERSION, channels: HashMap::new() } +/// Whether a channel can still be used (active and not fully spent). +fn is_usable(ch: &Channel) -> bool { + if ch.state != "active" { + return false; } + let cumulative: u128 = ch.cumulative_amount.parse().unwrap_or(u128::MAX); + let deposit: u128 = ch.deposit.parse().unwrap_or(0); + cumulative < deposit } -/// A persisted channel entry. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PersistedChannel { - pub channel_id: String, - pub salt: String, - pub escrow_contract: String, - pub chain_id: u64, - pub cumulative_amount: String, - pub deposit: String, - pub status: String, - pub origin: String, - pub created_at: u64, - pub last_used_at: u64, +/// Convert a persisted `Channel` to a `ChannelEntry`. +pub fn to_channel_entry(ch: &Channel) -> Option { + let channel_id: B256 = ch.channel_id.parse().ok()?; + let salt: B256 = ch.salt.parse().ok()?; + let escrow_contract: Address = ch.escrow_contract.parse().ok()?; + let cumulative_amount: u128 = ch.cumulative_amount.parse().ok()?; + + Some(ChannelEntry { + channel_id, + salt, + cumulative_amount, + escrow_contract, + chain_id: ch.chain_id as u64, + opened: ch.state == "active", + }) } -impl PersistedChannel { - /// Convert to an mpp `ChannelEntry` for use in the session provider. - pub fn to_channel_entry(&self) -> Option { - let channel_id: B256 = self.channel_id.parse().ok()?; - let salt: B256 = self.salt.parse().ok()?; - let escrow_contract: Address = self.escrow_contract.parse().ok()?; - let cumulative_amount: u128 = self.cumulative_amount.parse().ok()?; - - Some(ChannelEntry { - channel_id, - salt, - cumulative_amount, - escrow_contract, - chain_id: self.chain_id, - opened: self.status == "active", - }) - } - - /// Create from a `ChannelEntry` with metadata. - pub fn from_channel_entry(entry: &ChannelEntry, deposit: u128, origin: &str) -> Self { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - - Self { - channel_id: entry.channel_id.to_string(), - salt: entry.salt.to_string(), - escrow_contract: entry.escrow_contract.to_string(), - chain_id: entry.chain_id, - cumulative_amount: entry.cumulative_amount.to_string(), - deposit: deposit.to_string(), - status: if entry.opened { "active" } else { "closed" }.to_string(), - origin: origin.to_string(), - created_at: now, - last_used_at: now, - } - } - - /// Whether this channel can still be used (active and not fully spent). - fn is_usable(&self) -> bool { - if self.status != "active" { - return false; - } - let cumulative: u128 = self.cumulative_amount.parse().unwrap_or(u128::MAX); - let deposit: u128 = self.deposit.parse().unwrap_or(0); - cumulative < deposit +/// Create a `Channel` from a `ChannelEntry` with metadata. +pub fn from_channel_entry( + entry: &ChannelEntry, + deposit: u128, + origin: &str, + payer: &Address, + payee: &Address, + token: &Address, + authorized_signer: &Address, +) -> Channel { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; + + Channel { + channel_id: entry.channel_id.to_string(), + version: 1, + origin: origin.to_string(), + request_url: String::new(), + chain_id: entry.chain_id as i64, + escrow_contract: entry.escrow_contract.to_string(), + token: token.to_string(), + payee: payee.to_string(), + payer: payer.to_string(), + authorized_signer: authorized_signer.to_string(), + salt: entry.salt.to_string(), + deposit: deposit.to_string(), + cumulative_amount: entry.cumulative_amount.to_string(), + challenge_echo: String::new(), + state: if entry.opened { "active" } else { "closed" }.to_string(), + close_requested_at: 0, + grace_ready_at: 0, + created_at: now, + last_used_at: now, } } -/// Returns the path to the channels file. -fn channels_path() -> Option { - tempo_home().map(|home| home.join(CHANNELS_PATH)) -} - -/// Load channels from disk, evicting spent/inactive entries. -pub fn load_channels() -> HashMap { - let Some(path) = channels_path().filter(|p| p.exists()) else { +/// Load channels from database, evicting spent/inactive entries. +pub fn load_channels() -> HashMap { + let Some(db) = global_db() else { return HashMap::new(); }; - let Ok(contents) = std::fs::read_to_string(&path).inspect_err(|e| { - warn!(?path, %e, "failed to read channels file"); - }) else { - return HashMap::new(); - }; - - let Ok(store) = serde_json::from_str::(&contents).inspect_err(|e| { - warn!(?path, %e, "failed to parse channels file, starting fresh"); - }) else { - return HashMap::new(); + let channels = match db.load() { + Ok(channels) => channels, + Err(e) => { + warn!(%e, "failed to load channels from database"); + return HashMap::new(); + } }; - if store.version != SCHEMA_VERSION { - warn!( - version = store.version, - expected = SCHEMA_VERSION, - "channels file version mismatch, starting fresh" - ); - return HashMap::new(); - } - - // Evict spent/inactive entries - let usable: HashMap = - store.channels.into_iter().filter(|(_, ch)| ch.is_usable()).collect(); + let usable: HashMap = channels + .into_iter() + .filter(is_usable) + .map(|ch| { + let key = channel_key_from_persisted(&ch); + (key, ch) + }) + .collect(); debug!(count = usable.len(), "loaded persisted MPP channels"); usable } -/// Save channels to disk. -pub fn save_channels(channels: &HashMap) { - let Some(path) = channels_path() else { +/// Save channels to database. +pub fn save_channels(channels: &HashMap) { + let Some(db) = global_db() else { return; }; - if let Some(parent) = path.parent() - && let Err(e) = std::fs::create_dir_all(parent) - { - warn!(?path, %e, "failed to create channels directory"); - return; + for ch in channels.values() { + if let Err(e) = db.upsert(ch) { + warn!(%e, channel_id = %ch.channel_id, "failed to save channel"); + } } + debug!(count = channels.len(), "saved MPP channels"); +} - let store = ChannelStore { version: SCHEMA_VERSION, channels: channels.clone() }; - - match serde_json::to_string_pretty(&store) { - Ok(json) => { - if let Err(e) = std::fs::write(&path, json) { - warn!(?path, %e, "failed to write channels file"); - } else { - debug!(?path, count = channels.len(), "saved MPP channels"); - } - } - Err(e) => warn!(%e, "failed to serialize channels"), +/// Delete a channel from the database by its channel ID. +pub fn delete_channel_from_db(channel_id: &str) { + let Some(db) = global_db() else { + return; + }; + if let Err(e) = db.delete(channel_id) { + warn!(%e, channel_id, "failed to delete channel from database"); } } /// Look up a usable persisted channel by key. -pub fn find_channel( - channels: &HashMap, - key: &str, -) -> Option { - channels.get(key).filter(|ch| ch.is_usable()).and_then(|ch| ch.to_channel_entry()) +pub fn find_channel(channels: &HashMap, key: &str) -> Option { + channels.get(key).filter(|ch| is_usable(ch)).and_then(to_channel_entry) } -/// Insert or update a channel entry in memory only (no disk write). -/// -/// Use [`upsert_channel`] when you want to persist immediately, or call -/// [`save_channels`] separately after this. +/// Insert or update a channel entry in memory only (no DB write). pub fn upsert_channel_in_memory( - channels: &mut HashMap, + channels: &mut HashMap, key: &str, entry: &ChannelEntry, - deposit: u128, - origin: &str, ) { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; if let Some(existing) = channels.get_mut(key) { existing.cumulative_amount = entry.cumulative_amount.to_string(); existing.last_used_at = now; - existing.status = if entry.opened { "active" } else { "closed" }.to_string(); + existing.state = if entry.opened { "active" } else { "closed" }.to_string(); } else { - channels - .insert(key.to_string(), PersistedChannel::from_channel_entry(entry, deposit, origin)); + warn!(key, "upsert_channel_in_memory called for unknown channel"); } } -/// Insert or update a channel entry and save to disk. -/// -/// When updating an existing entry, `deposit` is ignored (preserved from the -/// original open). When inserting a new entry, `deposit` is recorded. -pub fn upsert_channel( - channels: &mut HashMap, - key: &str, - entry: &ChannelEntry, - deposit: u128, - origin: &str, -) { - upsert_channel_in_memory(channels, key, entry, deposit, origin); - save_channels(channels); -} - #[cfg(test)] mod tests { use super::*; - fn test_channel(status: &str, cumulative: &str, deposit: &str) -> PersistedChannel { - PersistedChannel { + fn test_channel(state: &str, cumulative: &str, deposit: &str) -> Channel { + Channel { channel_id: format!("0x{}", "ab".repeat(32)), - salt: format!("0x{}", "cd".repeat(32)), - escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + version: 1, + origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + request_url: String::new(), chain_id: 42431, - cumulative_amount: cumulative.to_string(), + escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + token: "0x20c0000000000000000000000000000000000000".to_string(), + payee: "0x3333333333333333333333333333333333333333".to_string(), + payer: "0x1111111111111111111111111111111111111111".to_string(), + authorized_signer: "0x1111111111111111111111111111111111111111".to_string(), + salt: format!("0x{}", "cd".repeat(32)), deposit: deposit.to_string(), - status: status.to_string(), - origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + cumulative_amount: cumulative.to_string(), + challenge_echo: String::new(), + state: state.to_string(), + close_requested_at: 0, + grace_ready_at: 0, created_at: 1000, last_used_at: 1000, } } #[test] - fn is_usable() { - assert!(test_channel("active", "5000", "100000").is_usable()); - assert!(!test_channel("active", "100000", "100000").is_usable()); - assert!(!test_channel("active", "200000", "100000").is_usable()); - assert!(!test_channel("closed", "0", "100000").is_usable()); - assert!(!test_channel("closing", "0", "100000").is_usable()); + fn usable() { + assert!(is_usable(&test_channel("active", "5000", "100000"))); + assert!(!is_usable(&test_channel("active", "100000", "100000"))); + assert!(!is_usable(&test_channel("active", "200000", "100000"))); + assert!(!is_usable(&test_channel("closed", "0", "100000"))); + assert!(!is_usable(&test_channel("closing", "0", "100000"))); } #[test] @@ -251,8 +249,12 @@ mod tests { opened: true, }; - let persisted = PersistedChannel::from_channel_entry(&entry, 100_000, "https://rpc.test"); - let restored = persisted.to_channel_entry().expect("should parse back"); + let payer = Address::random(); + let payee = Address::random(); + let token = Address::random(); + let persisted = + from_channel_entry(&entry, 100_000, "https://rpc.test", &payer, &payee, &token, &payer); + let restored = to_channel_entry(&persisted).expect("should parse back"); assert_eq!(restored.channel_id, entry.channel_id); assert_eq!(restored.salt, entry.salt); @@ -262,46 +264,6 @@ mod tests { assert!(restored.opened); } - #[test] - fn load_evicts_and_handles_edge_cases() { - let dir = tempfile::tempdir().unwrap(); - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - - let store = ChannelStore { - version: SCHEMA_VERSION, - channels: HashMap::from([ - ("active".into(), test_channel("active", "1000", "100000")), - ("spent".into(), test_channel("active", "100000", "100000")), - ("closed".into(), test_channel("closed", "0", "100000")), - ]), - }; - let json = serde_json::to_string(&store).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), &json).unwrap(); - - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - let loaded = load_channels(); - unsafe { std::env::remove_var("TEMPO_HOME") }; - - assert_eq!(loaded.len(), 1); - assert!(loaded.contains_key("active")); - } - - #[test] - fn load_missing_and_wrong_version() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - assert!(load_channels().is_empty()); - - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), r#"{"version": 999, "channels": {}}"#) - .unwrap(); - assert!(load_channels().is_empty()); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } - #[test] fn find_channel_filters_unusable() { let mut channels = HashMap::new(); @@ -312,34 +274,4 @@ mod tests { assert!(find_channel(&channels, "spent").is_none()); assert!(find_channel(&channels, "missing").is_none()); } - - #[test] - fn upsert_inserts_and_updates() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - - let mut channels = HashMap::new(); - let entry = ChannelEntry { - channel_id: B256::random(), - salt: B256::random(), - cumulative_amount: 1000, - escrow_contract: Address::random(), - chain_id: 42431, - opened: true, - }; - - upsert_channel(&mut channels, "key1", &entry, 100_000, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "1000"); - assert_eq!(channels["key1"].deposit, "100000"); - let created_at = channels["key1"].created_at; - - let mut updated = entry.clone(); - updated.cumulative_amount = 5000; - upsert_channel(&mut channels, "key1", &updated, 0, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "5000"); - assert_eq!(channels["key1"].deposit, "100000"); - assert_eq!(channels["key1"].created_at, created_at); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } } diff --git a/crates/common/src/provider/mpp/session.rs b/crates/common/src/provider/mpp/session.rs index 5996c933218d2..334166b844613 100644 --- a/crates/common/src/provider/mpp/session.rs +++ b/crates/common/src/provider/mpp/session.rs @@ -6,8 +6,9 @@ //! `eth_getTransactionCount`. This avoids the chicken-and-egg problem when //! the RPC endpoint is itself 402-gated. -use super::persist::{self, PersistedChannel}; +use super::persist; use alloy_primitives::{Address, B256, Bytes, TxKind, U256}; +use foundry_wallets::Channel; use mpp::{ client::{ PaymentProvider, @@ -42,7 +43,7 @@ static GLOBAL_CHANNELS: OnceLock>> = O /// /// Using a single map ensures saves from different origins don't clobber /// each other's state. -static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); +static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); /// Tracks uncommitted channel state from the most recent payment. /// @@ -86,7 +87,7 @@ pub struct SessionProvider { default_deposit: Option, channels: Arc>>, key_provisioned: Arc>, - persisted: Arc>>, + persisted: Arc>>, /// Tracks uncommitted open/top-up state for deferred persistence. pending: Arc>>, /// Chain ID from the key entry in `keys.toml` that was used to initialize @@ -128,10 +129,10 @@ impl SessionProvider { map.entry(origin.clone()) .or_insert_with(|| { // Hydrate only channels belonging to this origin. - let mut channels = HashMap::new(); + let mut channels: HashMap = HashMap::new(); for (key, ch) in persisted.lock().unwrap().iter() { if ch.origin == origin - && let Some(entry) = ch.to_channel_entry() + && let Some(entry) = persist::to_channel_entry(ch) { channels.insert(key.clone(), entry); } @@ -209,16 +210,16 @@ impl SessionProvider { // Lock order: channels → persisted (consistent with pay_session) let mut channels = self.channels.lock().unwrap(); let mut persisted = self.persisted.lock().unwrap(); - let keys_to_remove: Vec = persisted + let keys_to_remove: Vec<(String, String)> = persisted .iter() .filter(|(_, ch)| ch.origin == *origin) - .map(|(k, _)| k.clone()) + .map(|(k, ch): (&String, &Channel)| (k.clone(), ch.channel_id.clone())) .collect(); - for key in &keys_to_remove { + for (key, channel_id) in &keys_to_remove { channels.remove(key); persisted.remove(key); + persist::delete_channel_from_db(channel_id); } - persist::save_channels(&persisted); } /// Mark whether the access key has been provisioned on-chain. @@ -685,13 +686,7 @@ impl SessionProvider { // confirms acceptance. let updated_entry = ChannelEntry { cumulative_amount: new_cumulative, ..entry }; let mut persisted = self.persisted.lock().unwrap(); - persist::upsert_channel_in_memory( - &mut persisted, - &key, - &updated_entry, - 0, - &self.origin, - ); + persist::upsert_channel_in_memory(&mut persisted, &key, &updated_entry); drop(persisted); // Track the voucher so we can roll back cumulative_amount @@ -727,9 +722,18 @@ impl SessionProvider { // Update in-memory state but defer disk persistence until server confirms. self.channels.lock().unwrap().insert(key.clone(), entry.clone()); + let authorized_signer = self.authorized_signer.unwrap_or(payer); self.persisted.lock().unwrap().insert( key.clone(), - persist::PersistedChannel::from_channel_entry(&entry, deposit, &self.origin), + persist::from_channel_entry( + &entry, + deposit, + &self.origin, + &payer, + &payee, + ¤cy, + &authorized_signer, + ), ); *self.pending.lock().unwrap() = Some(PendingAction::Open { key }); Ok(build_credential(challenge, payload, chain_id, payer)) diff --git a/crates/evm/abi/src/Console.json b/crates/evm/abi/src/Console.json index 54e6d46dff349..f275f471087ee 100644 --- a/crates/evm/abi/src/Console.json +++ b/crates/evm/abi/src/Console.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file diff --git a/crates/evm/abi/src/console.py b/crates/evm/abi/src/console.py index 2e28fece21e42..d0b32ff410941 100755 --- a/crates/evm/abi/src/console.py +++ b/crates/evm/abi/src/console.py @@ -16,12 +16,15 @@ def main(): # Parse signatures from `console.sol`'s string literals console_sol = open(console_file).read() sig_strings = re.findall( - r'"(log.*?)"', + r'"((?:log|table).*?)"', console_sol, ) raw_sigs = [s.strip().strip('"') for s in sig_strings] sigs = [ - s.replace("string", "string memory").replace("bytes)", "bytes memory)") + re.sub(r"(\w+\[\])", r"\1 memory", s) + .replace("string,", "string memory,") + .replace("string)", "string memory)") + .replace("bytes)", "bytes memory)") for s in raw_sigs ] sigs = list(set(sigs)) @@ -38,6 +41,7 @@ def main(): ) combined = json.loads(r.stdout.strip()) abi = combined["contracts"][":HardhatConsole"]["abi"] + open(abi_file, "w").write(json.dumps(abi, separators=(",", ":"), indent=None)) diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index 26acbe56a97cb..c591b9426bf9e 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -50,7 +50,9 @@ impl LogCollector { fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> { let decoded = console::hh::ConsoleCalls::abi_decode(data)?; - self.push_msg(&decoded.fmt(Default::default())); + for line in decoded.fmt(Default::default()).lines() { + self.push_msg(line); + } Ok(()) } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9a7a237855b97..ecdc13ded67cf 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -51,7 +51,7 @@ forge-script.workspace = true forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } alloy-chains.workspace = true alloy-consensus.workspace = true diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 122020571ad24..a48d2a9350c70 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -9,7 +9,7 @@ use alloy_provider::{PendingTransactionError, Provider, ProviderBuilder as Alloy use alloy_signer::{Signature, Signer}; use alloy_transport::TransportError; use clap::{Parser, ValueHint}; -use eyre::{Context, Result}; +use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, @@ -33,10 +33,12 @@ use foundry_config::{ }, merge_impl_figment_convert, }; -use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; +use foundry_wallets::{ + BrowserWalletOpts, TempoAccessKeyConfig, WalletSigner, wallet_browser::signer::BrowserSigner, +}; use serde_json::json; use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc, time::Duration}; -use tempo_alloy::TempoNetwork; +use tempo_alloy::{TempoNetwork, contracts::precompiles::DEFAULT_FEE_TOKEN}; merge_impl_figment_convert!(CreateArgs, build, eth); @@ -101,14 +103,29 @@ pub struct CreateArgs { #[command(flatten)] retry: RetryArgs, + + /// Browser wallet options + #[command(flatten)] + browser: BrowserWalletOpts, } impl CreateArgs { /// Executes the command to create a contract - pub async fn run(self) -> Result<()> { + pub async fn run(mut self) -> Result<()> { let (signer, tempo_access_key) = self.eth.wallet.maybe_signer().await?; - if tempo_access_key.is_some() || self.tx.tempo.is_tempo() { + // Resolve chain early so we can dispatch to the correct network type. + if self.chain_id().is_none() { + let config = self.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let chain_id = provider.get_chain_id().await?; + self.eth.etherscan.chain = Some(chain_id.into()); + } + + if tempo_access_key.is_some() + || self.tx.tempo.is_tempo() + || self.chain_id().is_some_and(|c| c.is_tempo()) + { self.run_generic::(signer, tempo_access_key).await } else { self.run_generic::(signer, None).await @@ -185,17 +202,29 @@ impl CreateArgs { self.tx.tempo.key_id = Some(ak.key_address); } - // respect chain, if set explicitly via cmd args - let chain_id = if let Some(chain_id) = self.chain_id() { - chain_id - } else { - provider.get_chain_id().await? - }; - // Whether to broadcast the transaction or not let dry_run = !self.broadcast; - if self.unlocked { + // Launch browser signer if `--browser` flag is set + let browser = self.browser.run::().await?; + + if let Some(browser) = browser { + // Deploy with browser wallet + let deployer_address = browser.address(); + self.deploy( + abi, + bin, + params, + provider, + deployer_address, + config.transaction_timeout, + id, + dry_run, + None, + Some(browser), + ) + .await + } else if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); self.deploy( @@ -203,12 +232,12 @@ impl CreateArgs { bin, params, provider, - chain_id, sender, config.transaction_timeout, id, dry_run, None, + None, ) .await } else if let Some(ak) = access_key { @@ -223,12 +252,12 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer_address, config.transaction_timeout, id, dry_run, Some((signer, ak)), + None, ) .await } else { @@ -246,20 +275,20 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer, config.transaction_timeout, id, dry_run, None, + None, ) .await } } - /// Returns the provided chain id, if any. - fn chain_id(&self) -> Option { - self.eth.etherscan.chain.map(|chain| chain.id()) + /// Returns the resolved chain, if any. + const fn chain_id(&self) -> Option { + self.eth.etherscan.chain } /// Ensures the verify command can be executed. @@ -271,7 +300,6 @@ impl CreateArgs { async fn verify_preflight_check( &self, constructor_args: Option, - chain: u64, id: &ArtifactId, ) -> Result<()> { // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, @@ -287,7 +315,7 @@ impl CreateArgs { num_of_optimizations: None, etherscan: EtherscanOpts { key: self.eth.etherscan.key.clone(), - chain: Some(chain.into()), + chain: self.chain_id(), }, rpc: Default::default(), flatten: false, @@ -311,7 +339,7 @@ impl CreateArgs { // ETHERSCAN_API_KEY value set. let config = verify.load_config()?; verify.etherscan.key = - config.get_etherscan_config_with_chain(Some(chain.into()))?.map(|c| c.key); + config.get_etherscan_config_with_chain(self.chain_id())?.map(|c| c.key); let context = verify.resolve_context().await?; @@ -327,17 +355,19 @@ impl CreateArgs { bin: BytecodeObject, args: Vec, provider: P, - chain: u64, deployer_address: Address, timeout: u64, id: ArtifactId, dry_run: bool, tempo_keychain: Option<(WalletSigner, TempoAccessKeyConfig)>, + browser_signer: Option>, ) -> Result<()> where N::TransactionRequest: FoundryTransactionBuilder + serde::Serialize, N::ReceiptResponse: serde::Serialize, { + let chain = self.chain_id().context("chain ID not resolved")?; + let bin = bin.into_bytes().unwrap_or_default(); if bin.is_empty() { eyre::bail!("no bytecode found in bin object for {}", self.contract.name) @@ -349,22 +379,31 @@ impl CreateArgs { let is_args_empty = args.is_empty(); let mut deployer = - factory.deploy_tokens(args.clone(), self.tx.tempo.fee_token).context("failed to deploy contract").map_err(|e| { + 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") } else { e } })?; - let is_legacy = self.tx.legacy || Chain::from(chain).is_legacy(); + let is_legacy = self.tx.legacy || chain.is_legacy(); deployer.tx.set_from(deployer_address); - deployer.tx.set_chain_id(chain); + deployer.tx.set_chain_id(chain.id()); // `to` field must be set explicitly, cannot be None. if deployer.tx.to().is_none() { deployer.tx.set_create(); } + // If Tempo chain fee token must be set + if chain.is_tempo() { + if let Some(fee_token) = self.tx.tempo.fee_token { + deployer.tx.set_fee_token(fee_token); + } else { + deployer.tx.set_fee_token(DEFAULT_FEE_TOKEN); + } + } + // Apply user-provided gas, fee, nonce, and Tempo options. self.tx.apply::(&mut deployer.tx, is_legacy); @@ -422,7 +461,7 @@ impl CreateArgs { constructor_args = Some(hex::encode(encoded_args)); } - self.verify_preflight_check(constructor_args.clone(), chain, &id).await?; + self.verify_preflight_check(constructor_args.clone(), &id).await?; } if dry_run { @@ -452,7 +491,31 @@ impl CreateArgs { } // Deploy the actual contract - let (deployed_contract, receipt) = if let Some((signer, ak)) = tempo_keychain { + let (deployed_contract, receipt) = if let Some(browser) = browser_signer { + // Browser wallet signs and sends the transaction + let tx_hash = browser.send_transaction_via_browser(deployer.tx).await?; + + // Wait for the transaction to be confirmed, then fetch the receipt. + provider + .watch_pending_transaction(alloy_provider::PendingTransactionConfig::new(tx_hash)) + .await? + .await?; + + let receipt = provider + .get_transaction_receipt(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("could not get transaction receipt for {tx_hash}"))?; + + if !receipt.status() { + eyre::bail!("deployment transaction failed (receipt status 0): {tx_hash}"); + } + + let address = receipt + .contract_address() + .ok_or_else(|| eyre::eyre!("contract was not deployed"))?; + + (address, receipt) + } else if let Some((signer, ak)) = tempo_keychain { // Tempo keychain mode: sign with access key provisioning and send raw let raw_tx = deployer .tx @@ -518,7 +581,7 @@ impl CreateArgs { no_auto_detect: false, use_solc: None, num_of_optimizations, - etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) }, + etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain) }, rpc: Default::default(), flatten: false, force: false, @@ -681,7 +744,6 @@ impl + Clone> DeploymentTxFactory { pub fn deploy_tokens( self, params: Vec, - fee_token: Option
, ) -> Result, ContractDeploymentError> where N::TransactionRequest: FoundryTransactionBuilder, @@ -703,9 +765,6 @@ impl + Clone> DeploymentTxFactory { // create the tx object. Since we're deploying a contract, `to` is `None` let mut tx = N::TransactionRequest::default(); tx.set_input(data); - if let Some(fee_token) = fee_token { - tx.set_fee_token(fee_token); - } Ok(Deployer { client: self.client.clone(), tx, confs: 1, timeout: self.timeout }) } } @@ -763,7 +822,7 @@ mod tests { "--chain-id", "9999", ]); - assert_eq!(args.chain_id(), Some(9999)); + assert_eq!(args.chain_id().map(|c| c.id()), Some(9999)); } #[test] diff --git a/crates/macros/src/console_fmt.rs b/crates/macros/src/console_fmt.rs index 0c95841d1e653..245ba6e5b78e2 100644 --- a/crates/macros/src/console_fmt.rs +++ b/crates/macros/src/console_fmt.rs @@ -7,7 +7,7 @@ use syn::{ pub fn console_fmt(input: &DeriveInput) -> TokenStream { let name = &input.ident; let tokens = match &input.data { - Data::Struct(s) => derive_struct(s), + Data::Struct(s) => derive_struct(s, name), Data::Enum(e) => derive_enum(e), Data::Union(_) => return quote!(compile_error!("Unions are unsupported");), }; @@ -18,8 +18,8 @@ pub fn console_fmt(input: &DeriveInput) -> TokenStream { } } -fn derive_struct(s: &DataStruct) -> TokenStream { - let imp = impl_struct(s).unwrap_or_else(|| quote!(String::new())); +fn derive_struct(s: &DataStruct, name: &Ident) -> TokenStream { + let imp = impl_struct(s, name).unwrap_or_else(|| quote!(String::new())); quote! { fn fmt(&self, _spec: FormatSpec) -> String { #imp @@ -27,7 +27,7 @@ fn derive_struct(s: &DataStruct) -> TokenStream { } } -fn impl_struct(s: &DataStruct) -> Option { +fn impl_struct(s: &DataStruct, name: &Ident) -> Option { if s.fields.is_empty() { return None; } @@ -36,13 +36,49 @@ fn impl_struct(s: &DataStruct) -> Option { return None; } + let members = s.fields.members().collect::>(); let fields = s.fields.iter().collect::>(); + + // Detect table call structs: name must start with "table" (from the ABI function name) and + // all fields must be Vec types (Solidity arrays). Both conditions together prevent + // accidental table rendering for unrelated structs that happen to have Vec fields. + let is_table = name.to_string().starts_with("table") + && !fields.is_empty() + && fields.iter().all(|f| match &f.ty { + Type::Path(path) => path.path.segments.last().is_some_and(|seg| seg.ident == "Vec"), + _ => false, + }); + if is_table { + let member_ref = |m: &Member| match m { + Member::Named(ident) => quote!(&self.#ident), + Member::Unnamed(idx) => quote!(&self.#idx), + }; + let imp = if members.len() == 1 { + let vals = member_ref(&members[0]); + quote! { + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(None, &values) + } + } else { + let keys = member_ref(&members[0]); + let vals = member_ref(&members[1]); + quote! { + let keys: ::std::vec::Vec<&dyn ConsoleFmt> = + (#keys).iter().map(|v| v as &dyn ConsoleFmt).collect(); + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(Some(&keys), &values) + } + }; + return Some(imp); + } + let first_ty = match &fields.first().unwrap().ty { Type::Path(path) => path.path.segments.last().unwrap().ident.to_string(), _ => String::new(), }; - let members = s.fields.members().collect::>(); let args: Punctuated = members .into_iter() .map(|member| match member { diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 4146e07d2248b..acdcbbdbf95ea 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -23,7 +23,7 @@ foundry-evm-networks.workspace = true alloy-evm.workspace = true foundry-debugger.workspace = true foundry-cheatcodes.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } foundry-linking.workspace = true forge-script-sequence.workspace = true diff --git a/npm/bun.lock b/npm/bun.lock index e8ef087be9d50..d9e8fed5ce479 100644 --- a/npm/bun.lock +++ b/npm/bun.lock @@ -1,12 +1,13 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.5.2", "bun": "^1.3.1", - "typescript": "^5.9.3", + "typescript": "^6.0.2", }, }, }, @@ -35,7 +36,7 @@ "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], - "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], @@ -45,8 +46,12 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "bun-types/@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], } } diff --git a/npm/scripts/publish.mjs b/npm/scripts/publish.mjs index 0f582c8ebc555..deee29e46c92f 100644 --- a/npm/scripts/publish.mjs +++ b/npm/scripts/publish.mjs @@ -7,17 +7,12 @@ import { colors } from '#const.mjs' const REGISTRY_URL = Bun.env.NPM_REGISTRY_URL || 'https://registry.npmjs.org' -const NPM_TOKEN = Bun.env.NPM_TOKEN -if (!NPM_TOKEN) throw new Error('NPM_TOKEN is required') - main().catch(error => { console.error(error) process.exit(1) }) async function main() { - const npmToken = Bun.env.NPM_TOKEN - if (!npmToken) throw new Error('NPM_TOKEN is required') const inputPath = Bun.argv[2] if (!inputPath) throw new Error('Package path is required') @@ -114,11 +109,6 @@ async function setPackageVersion(packagePath, version) { console.info(colors.green, 'Setting package version:', version) const result = await Bun.$`npm version ${version} --allow-same-version --no-git-tag-version` .cwd(packagePath) - .env({ - ...Bun.env, - ...process.env, - NPM_TOKEN - }) .quiet() .nothrow() diff --git a/npm/tsconfig.json b/npm/tsconfig.json index ca8e3c92f2daa..a7a0093d7ee56 100644 --- a/npm/tsconfig.json +++ b/npm/tsconfig.json @@ -2,7 +2,6 @@ "schema": "https://json.schemastore.org/tsconfig.json", "compilerOptions": { "strict": true, - "baseUrl": ".", "noEmit": true, "allowJs": true, "checkJs": true, From 3299d9f18eb681d3017006092eb147e09f365ac2 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:37:39 +0700 Subject: [PATCH 333/374] Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/anvil/src/cmd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index fb75529b82908..edababc83aa7f 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -654,8 +654,8 @@ impl AnvilEvmArgs { if let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) { let mut resolved_urls = Vec::new(); for fork_url in &self.fork_url { - let mut endpoints = config.rpc_endpoints.clone().resolved(); - if let Some(endpoint) = endpoints.remove(&fork_url.url) { + if let Some(endpoint) = config.rpc_endpoints.get(&fork_url.url) { + let endpoint = endpoint.clone().resolve(); // Alias matched — expand all URLs from the endpoint config match endpoint.all_urls() { Ok(urls) => { From abda75815d62bd6bc320704d63c581ee5f828048 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:54:51 +0700 Subject: [PATCH 334/374] chore(deps): bump docker/metadata-action from 5 to 6 (#415) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/v5...v6) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/Docker.yml | 2 +- .github/workflows/docker.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index 5978701aade87..53b7d1ed0ac7c 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -35,7 +35,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5978701aade87..53b7d1ed0ac7c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -35,7 +35,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | From eea1bf7634e812a29568fd9c607254ad1bf34537 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:57:41 +0700 Subject: [PATCH 335/374] chore(deps): bump actions/configure-pages from 5 to 6 (#428) Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 5 to 6. - [Release notes](https://github.com/actions/configure-pages/releases) - [Commits](https://github.com/actions/configure-pages/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/configure-pages dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/static.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 0ba82305f82b2..6026a64f84d28 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -32,7 +32,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v6 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: From 5d0847a77f040c0f8e1710be493b67c201021b5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:59:55 +0700 Subject: [PATCH 336/374] chore(deps): bump actions/upload-pages-artifact from 3 to 5 (#430) Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 5. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v5) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/static.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 6026a64f84d28..fcfbbbbec7948 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -34,7 +34,7 @@ jobs: - name: Setup Pages uses: actions/configure-pages@v6 - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: # Upload entire repository path: '.' From ac77b5cf9aea007bafa37ea71695961d5ee4ea4f Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:04:46 +0000 Subject: [PATCH 337/374] Potential fix for code scanning alert no. 108: Artifact poisoning (#431) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/npm.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 4ef7c4ac04dfc..938e85c9330be 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -113,6 +113,22 @@ jobs: with: persist-credentials: false + - name: Validate Trusted Artifact Source + run: | + set -euo pipefail + + if [[ "${{ github.event_name }}" == "workflow_run" ]]; then + [[ "${{ github.event.workflow_run.conclusion }}" == "success" ]] || { echo "ERROR: Upstream workflow_run did not succeed."; exit 1; } + [[ "${{ github.event.workflow_run.head_repository.full_name }}" == "${{ github.repository }}" ]] || { echo "ERROR: Upstream run repository mismatch."; exit 1; } + [[ "${{ github.event.workflow_run.event }}" != "pull_request" ]] || { echo "ERROR: Refusing artifacts from pull_request-triggered upstream runs."; exit 1; } + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + [[ -n "${{ inputs.run_id }}" ]] || { echo "ERROR: inputs.run_id is required for workflow_dispatch."; exit 1; } + [[ "${{ inputs.run_id }}" =~ ^[0-9]+$ ]] || { echo "ERROR: inputs.run_id must be numeric."; exit 1; } + else + echo "ERROR: Unsupported event: ${{ github.event_name }}" + exit 1 + fi + - name: Set Isolated Artifact Directory id: paths run: | From 412f874110561d46c32743479f954bf346f8686b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:21:08 +0000 Subject: [PATCH 338/374] 7 bug release workflow failed (#470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol * Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#345) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 110: Uncontrolled data used in path expression (#347) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 102: Artifact poisoning (#351) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md (#350) * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 109: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> … * Potential fix for code scanning alert no. 102: Artifact poisoning (#354) * Potential fix for code scanning alert no. 102: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#357) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * refactor(common): make ProviderBuilder generic over Network #13250 (#361) * refactor(common): make `ProviderBuilder` generic over `Network` (#13250) * refactor(common): make `ProviderBuilder` generic over `Network` - Updated `ProviderBuilder` helper to be generic, which will facilate `FoundryNetwork` rollout - Adjusted the instantiation of `ProviderBuilder` in various locations to use `AnyNetwork`. - Enhanced the `build` and `build_with_wallet` methods to accommodate the new generic structure. * fix: relax trait bound on `N::TransactionRequest` * fix: comment * fix: comment * fix(anvil): return error instead of empty vec for out-of-range log queries (#13251) * fix(config): respect user-configured etherscan URL over chain defaults (#13239) fix(config): Respect user-configured etherscan URL over chain defaults (#13238) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` (#13218) * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` * fix: after `TransactionBuilder4844` impl * feat(common): introduce generic `ProviderBuilder::from_config` method (#13268) - keep the existing helpers that erase generic to avoid breaking change * fix(cast): remove redundant chain() call in explorer_client (#13272) Co-authored-by: tefyosL-sol * fix(verify): respect user-configured etherscan URL over chain defaults (#13275) Co-authored-by: tefyosL-sol * Update flake.lock (#13279) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/93523fa' (2026-01-24) → 'github:nix-community/fenix/b2344f3' (2026-01-31) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) → 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) → 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) Co-authored-by: github-actions[bot] * fix(invariant): remove unused cloned calldata (#12893) * fix(invariant): prune calldata to bound memory usage in long runs * style: fix formatting in invariant executor * chore: remove vyper files from testdata to fix CI * chore: trigger ci update * fix(invariant): remove unused FuzzCase.calldata field to prevent OOM The calldata field in FuzzCase was stored but never read after construction. Removing it entirely eliminates memory accumulation during long invariant runs. Changes: - Remove FuzzCase.calldata field (unused after construction) - Remove prune_calldata() methods (no longer needed) - Restore vyper test files that were incorrectly deleted Fixes #12397 Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(debugger): display actual gas usage alongside refund counter (#13271) * feat(debugger): display actual gas usage alongside refund counter * fix(forge-test): fix flamegraph gas inaccuracy issues * reverse `--decode-internal` not default with `--flamechart` * make gas unsigned as `inferno` doesn't support anyway * replace revm-inspectors patch * fix clippy * update tests * update tests * fmt * fix(config): handle decimal string in U256 deserialization (#13284) * fix: adjust numerical cells in gas report to be right aligned (#12883) * Adjust Cells to be Right Aligned in Gas Report * fix test and fmt --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): weekly `cargo update` (#13280) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v1.5.2 -> v1.5.4 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-json-abi v1.5.2 -> v1.5.4 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-primitives v1.5.2 -> v1.5.4 Updating alloy-sol-macro v1.5.2 -> v1.5.4 Updating alloy-sol-macro-expander v1.5.2 -> v1.5.4 Updating alloy-sol-macro-input v1.5.2 -> v1.5.4 Updating alloy-sol-type-parser v1.5.2 -> v1.5.4 Updating alloy-sol-types v1.5.2 -> v1.5.4 Updating annotate-snippets v0.12.10 -> v0.12.11 Updating aws-smithy-async v1.2.7 -> v1.2.10 Updating aws-smithy-http-client v1.1.5 -> v1.1.8 Updating aws-smithy-observability v0.2.0 -> v0.2.3 Updating aws-smithy-query v0.60.9 -> v0.60.12 Updating aws-smithy-runtime-api v1.10.0 -> v1.11.2 Updating aws-smithy-types v1.3.6 -> v1.4.2 Updating bytemuck v1.24.0 -> v1.25.0 Updating cc v1.2.54 -> v1.2.55 Updating clap v4.5.54 -> v4.5.56 Updating clap_builder v4.5.54 -> v4.5.56 Updating clap_derive v4.5.49 -> v4.5.55 Updating cliclack v0.3.7 -> v0.3.8 Updating find-msvc-tools v0.1.8 -> v0.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating iana-time-zone v0.1.64 -> v0.1.65 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating keccak-asm v0.1.4 -> v0.1.5 Unchanged matchit v0.8.4 (available: v0.8.6) Updating notify-types v2.0.0 -> v2.1.0 Updating portable-atomic v1.13.0 -> v1.13.1 Updating portable-atomic-util v0.2.4 -> v0.2.5 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating revm-inspectors v0.34.0 -> v0.34.2 Updating sha3-asm v0.1.4 -> v0.1.5 Updating siphasher v1.0.1 -> v1.0.2 Updating slab v0.4.11 -> v0.4.12 Updating syn-solidity v1.5.2 -> v1.5.4 Removing tiny-keccak v2.0.2 Updating zerocopy v0.8.33 -> v0.8.37 Updating zerocopy-derive v0.8.33 -> v0.8.37 Updating zmij v1.0.16 -> v1.0.18 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: update RPC URLs from ithaca.xyz to reth.rs (#13261) * chore: update RPC URLs from ithaca.xyz to reth.rs Co-authored-by: Tim Beiko Amp-Thread-ID: https://ampcode.com/threads/T-019c0a51-3f0a-76eb-ba4a-bfb6a697d9ba Co-authored-by: Amp * fix * fmt * Update rpc.rs * Update rpc.rs * test: update test --------- Co-authored-by: Tim Beiko Co-authored-by: Amp Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg * ci(bench): use depot runner for benchmarks (#13288) * feat(cli): cli markdown docs (#13291) * feat: add foundry-cli-markdown crate Add a new crate for generating Markdown documentation from clap CLIs. This is a fork of clap-markdown with the following enhancements: - Support for grouped options by help heading (PR #48) - Show environment variable names for arguments (PR #50) - Add version information to generated Markdown (PR #52) * feat(cli): add hidden --markdown-help flag Add a hidden --markdown-help flag to forge, cast, anvil, and chisel that prints CLI reference documentation as Markdown and exits. This uses the new foundry-cli-markdown crate to generate the output. * chore(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#13298) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/5e57cd118135c172c3672efd75eb46360885c0ef...c94ce9fb468520275223c153574b00df6fe4bcc9) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.13 to 2.67.18 (#13297) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.13 to 2.67.18. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/710817a1645ef40daad5bcde7431ceccf6cc3528...650c5ca14212efbbf3e580844b04bdccf68dac31) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 (#13299) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/727cc5b0b19bc265bd5ef28fc66bccb284473b5d...5adeaaaf36f64df54f62adb34aa5fbfdb0109d34) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.0.1 to 6.1.0 (#13300) chore(deps): bump mikepenz/release-changelog-builder-action Bumps [mikepenz/release-changelog-builder-action](https://github.com/mikepenz/release-changelog-builder-action) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/mikepenz/release-changelog-builder-action/releases) - [Commits](https://github.com/mikepenz/release-changelog-builder-action/compare/439f79b5b5428107c7688c1d2b0e8bacc9b8792c...6faf020194b7c8853f9e55c4fd92e40b02122a04) --- updated-dependencies: - dependency-name: mikepenz/release-changelog-builder-action dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: fix typos CI (#13303) - Add consts to typos ignore list (valid Rust std::env::consts) - Fix LintCotext -> LintContext typos in linter docs * chore(deps): bump crate-ci/typos from 1.42.2 to 1.43.0 (#13296) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.2 to 1.43.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/a1d64977b4aa1709d6328d518aa753f4899352d8...93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * sec: bump to `bytes` `^1.11.1` for `RUSTSEC-2026-0007` (#13306) bump to 1.11.1 for patch: https://rustsec.org/advisories/RUSTSEC-2026-0007 * chore(anvil,cast): remove unnecessary `populate_blob_hashes()` (#13308) Both `set_blob_sidecar`/`set_blob_sidecar_7594` implement a call to `populate_blob_hashes()` * feat(forge): generate random fuzz seed if none provided (#13309) * feat(forge): generate random fuzz seed if none provided Generate a random seed for fuzz/invariant tests when no seed is explicitly configured. This ensures reproducibility by always having a seed available that can be passed via --fuzz-seed to reproduce test runs. * feat(forge): print fuzz seed on fuzz failure (#13310) * feat(forge): print fuzz seed on test failure When a fuzz or invariant test fails, print the seed used so users can reproduce the failure with --fuzz-seed. * test: update snapshots for fuzz seed output Add [SEED] redaction pattern to match 'Fuzz seed: 0x...' output. Update all test snapshots that have fuzz/invariant failures to include the new seed line. * fix: use [SEED] placeholder in issue_3055 test snapshot Amp-Thread-ID: https://ampcode.com/threads/T-019c26cb-9d21-74f9-9e49-7ea59885e827 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(anvil): cache block timestamp in mined receipts (#13311) * fix: use shared `display_chain` helper in CLI error handler (#13314) * Update handler.rs * Update handler.rs * Add --enable-tx-gas-limit CLI flag for EIP-7825 support (#13307) add --enable-tx-gas-limit * fix(anvil): use consistent chain_id fallback in fork setup (#13276) Co-authored-by: tefyosL-sol * fix(test-utils): skip build artifacts when copying to temp workspace (#13266) * feat(lint): add common uppercase abbreviations to mixedCase exceptions (#13305) Co-authored-by: onbjerg * fix(fmt): correct indentation for closing brace in empty contracts with comments (#13319) Co-authored-by: onbjerg * feat(anvil): add `trace_replayBlockTransactions` endpoint for block txs tracing (#13098) Co-authored-by: onbjerg * fix(eip712): write diagnostics to stderr instead of stdout (#13293) Co-authored-by: onbjerg --------- Signed-off-by: dependabot[bot] Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Matt D Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Tim Beiko Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#373) * Potential fix for code scanning alert no. 108: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Forge/master (#376) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar Co-authored-by: googleworkspace-bot * chore(deps): bump the cargo group across 1 directory with 9 updates (#377) Bumps the cargo group with 3 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing), [keccak](https://github.com/RustCrypto/sponges) and [slab](https://github.com/tokio-rs/slab). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `time` from 0.3.41 to 0.3.47 - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.41...v0.3.47) Updates `alloy-dyn-abi` from 1.3.0 to 1.5.7 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.3.0...v1.5.7) Updates `bytes` from 1.10.1 to 1.11.1 - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.1...v1.11.1) Updates `keccak` from 0.1.5 to 0.1.6 - [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6) Updates `lru` from 0.12.5 to 0.16.3 - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.12.5...0.16.3) Updates `protobuf` from 3.3.0 to 3.7.2 Updates `ruint` from 1.15.0 to 1.17.2 - [Release notes](https://github.com/recmo/uint/releases) - [Changelog](https://github.com/recmo/uint/blob/main/CHANGELOG.md) - [Commits](https://github.com/recmo/uint/compare/v1.15.0...v1.17.2) Updates `slab` from 0.4.10 to 0.4.12 - [Release notes](https://github.com/tokio-rs/slab/releases) - [Changelog](https://github.com/tokio-rs/slab/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/slab/compare/v0.4.10...v0.4.12) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: time dependency-version: 0.3.47 dependency-type: direct:production dependency-group: cargo - dependency-name: alloy-dyn-abi dependency-version: 1.5.7 dependency-type: direct:production dependency-group: cargo - dependency-name: bytes dependency-version: 1.11.1 dependency-type: direct:production dependency-group: cargo - dependency-name: keccak dependency-version: 0.1.6 dependency-type: indirect dependency-group: cargo - dependency-name: lru dependency-version: 0.16.3 dependency-type: indirect dependency-group: cargo - dependency-name: protobuf dependency-version: 3.7.2 dependency-type: indirect dependency-group: cargo - dependency-name: ruint dependency-version: 1.17.2 dependency-type: indirect dependency-group: cargo - dependency-name: slab dependency-version: 0.4.12 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create static.yml (#381) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Hardhat project (#389) * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot * Change branch trigger from 'main' to 'master' (#403) https://github.com/Dargon789/hardhat-project/issues/2073 https://github.com/Dargon789/hardhat-project/pull/2078/commits/a98e69d5803ec208947f592c91749f248e2dd07c Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/args.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update docker.yml * Revise Foundry benchmark results and system info (#407) Updated benchmark results for Foundry versions and system information. https://github.com/foundry-rs/foundry/commit/1c4d334e95bc1cad3a390cc735d0f3ec59eea07f Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revise Foundry benchmark results and system info (#408) Updated benchmark results for Foundry versions v1.3.6 and v1.4.0-rc1, including new test data and system information. https://github.com/foundry-rs/foundry/commit/1c4d334e95bc1cad3a390cc735d0f3ec59eea07f Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "benches\LATEST.md (#350)" (#409) This reverts commit 71a26eea6cce80052b3a82da9aa7c9aa0d987b09. * Update Docker.yml * chore(deps): bump DeterminateSystems/update-flake-lock (#419) Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from e80a657d7603606be0c69b117cfdc240f1e6af88 to ff43f160ef7014ae1a1fd85699fb6a44f436135b. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/e80a657d7603606be0c69b117cfdc240f1e6af88...ff43f160ef7014ae1a1fd85699fb6a44f436135b) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: ff43f160ef7014ae1a1fd85699fb6a44f436135b dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/config.yml (#420) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Docker.yml * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (foundry-rs#14398 (#464) * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp * chore: bump alloy 2.0.1 (#14417) --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: googleworkspace-bot Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> --- .cargo/config.toml | 6 +- .codesandbox/tasks.json | 7 + .deps/remix-tests/remix_accounts.sol | 39 ++ .deps/remix-tests/remix_tests.sol | 225 ++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 39 ++ .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/scripts/tempo-mpp.sh | 18 +- .github/workflows/Docker.yml | 62 +++ .github/workflows/apisec-scan.yml | 29 ++ .github/workflows/benchmarks.yml | 2 +- .github/workflows/ci.yml | 6 +- .github/workflows/codeql.yml | 92 ++++ .github/workflows/crate-checks.yml | 2 +- .github/workflows/deploy.yml | 27 ++ .github/workflows/docker.yml | 62 +++ .github/workflows/docs.yml | 2 +- .github/workflows/google.yml | 117 +++++ .github/workflows/nix.yml | 2 +- .github/workflows/npm.yml | 48 +- .github/workflows/release.yml | 6 +- .github/workflows/snyk-container.yml | 55 +++ .github/workflows/static.yml | 43 ++ .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 4 +- .gitmodules | 6 + Cargo.lock | 363 +++++++++------- Cargo.toml | 60 +-- Makefile | 41 +- benches/src/lib.rs | 16 +- counter/.github/workflows/test.yml | 43 ++ counter/.gitignore | 14 + counter/README.md | 66 +++ counter/foundry.toml | 6 + counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 + counter/src/Counter.sol | 14 + counter/test/Counter.t.sol | 24 + crates/cast/Cargo.toml | 2 +- crates/cast/src/args.rs | 2 +- crates/cast/src/call_spec.rs | 10 +- crates/cast/src/cmd/erc20.rs | 103 ++++- crates/cast/tests/cli/main.rs | 28 ++ .../cheatcodes/assets/cheatcodes.schema.json | 2 +- crates/cheatcodes/spec/src/lib.rs | 4 +- crates/cheatcodes/src/fs.rs | 11 +- crates/cli/Cargo.toml | 2 +- crates/cli/src/utils/mod.rs | 14 +- crates/cli/src/utils/suggestions.rs | 3 +- crates/common/Cargo.toml | 1 + crates/common/fmt/Cargo.toml | 1 + crates/common/fmt/src/console.rs | 107 +++++ crates/common/fmt/src/lib.rs | 2 +- crates/common/src/contracts.rs | 2 +- crates/common/src/provider/mpp/persist.rs | 410 ++++++++---------- crates/common/src/provider/mpp/session.rs | 38 +- crates/config/Cargo.toml | 4 + crates/config/assets/config.schema.json | 6 + crates/config/spec/Cargo.toml | 28 ++ crates/config/spec/src/lib.rs | 64 +++ crates/config/src/lib.rs | 33 +- crates/doc/src/parser/comment.rs | 6 + crates/doc/src/writer/buf_writer.rs | 20 +- crates/evm/abi/src/Console.json | 2 +- crates/evm/abi/src/console.py | 8 +- crates/evm/evm/src/inspectors/logs.rs | 4 +- crates/forge/Cargo.toml | 3 +- crates/forge/src/cmd/config.rs | 2 +- crates/forge/src/cmd/create.rs | 127 ++++-- crates/forge/tests/cli/test_optimizer.rs | 1 - crates/lint/src/linter.rs | 129 ++++++ crates/macros/src/console_fmt.rs | 46 +- crates/script/Cargo.toml | 2 +- crates/test-utils/src/script.rs | 29 +- crates/test-utils/src/util.rs | 104 ++++- npm/bun.lock | 15 +- npm/scripts/publish.mjs | 10 - npm/scripts/stage-from-artifact.mjs | 28 +- npm/src/const.mjs | 30 +- npm/tsconfig.json | 1 - 82 files changed, 2442 insertions(+), 603 deletions(-) create mode 100644 .codesandbox/tasks.json create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/Docker.yml create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/google.yml create mode 100644 .github/workflows/snyk-container.yml create mode 100644 .github/workflows/static.yml create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol create mode 100644 crates/config/assets/config.schema.json create mode 100644 crates/config/spec/Cargo.toml create mode 100644 crates/config/spec/src/lib.rs create mode 100644 crates/lint/src/linter.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 1ca035a75d78c..ca844fb33e15b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,12 @@ [alias] -cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-cheats = "test -p foundry-cheatcodes-spec --features schema tests::" +spec-config = "test -p foundry-config-spec --features schema tests::" test-debugger = "test -p forge --test cli manual_debug_setup -- --include-ignored --nocapture" bless-lints = "test -p forge --test ui -- --bless" +# Backwards compatibility alias for `spec-cheats` +cheats = "spec-cheats" + # Increase the stack size to 10MB for Windows targets, which is in line with Linux # (whereas default for Windows is 1MB). [target.x86_64-pc-windows-msvc] diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..53a505774ac88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/scripts/tempo-mpp.sh b/.github/scripts/tempo-mpp.sh index 1a98b1ed9c494..0d34c4a3b5fa5 100755 --- a/.github/scripts/tempo-mpp.sh +++ b/.github/scripts/tempo-mpp.sh @@ -110,7 +110,7 @@ BLOCK2=$("$CAST" block-number --rpc-url "$RPC_MPP") AFTER2=$("$CAST" erc20 balance "$TOKEN" "$WALLET" --rpc-url "$RPC" | awk '{print $1}') SPENT2=$((BEFORE2 - AFTER2)) echo "Block: $BLOCK2" -echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/foundry/channels.json)" +echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/channels.db)" # 6. forge script via MPP echo "" @@ -129,9 +129,9 @@ contract MppCheck is Script { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" script "$TMPDIR/script/Mpp.s.sol" --rpc-url "$RPC_MPP" --root "$TMPDIR" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 7. forge test with vm.createSelectFork via MPP @@ -149,15 +149,15 @@ contract MppForkTest is Test { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" test --match-test test_fork_via_mpp --root "$TMPDIR" -vvv -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 8. anvil fork via MPP echo "" echo "=== 8. anvil --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$ANVIL" --fork-url "$RPC_MPP" --port 8555 --silent & ANVIL_PID=$! for _ in $(seq 1 30); do @@ -167,15 +167,15 @@ done echo "chain-id: $("$CAST" chain-id --rpc-url http://localhost:8555)" kill $ANVIL_PID 2>/dev/null wait $ANVIL_PID 2>/dev/null -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 9. chisel fork via MPP echo "" echo "=== 9. chisel --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo 'block.number' | "$CHISEL" --fork-url "$RPC_MPP" 2>&1 | grep -E "Decimal|Type" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" echo "" diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml new file mode 100644 index 0000000000000..7b85ca2ae00c8 --- /dev/null +++ b/.github/workflows/Docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "master" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 96af1f4ab9a1e..8465874854551 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -61,7 +61,7 @@ jobs: printf '%s\n' "$GITHUB_WORKSPACE/.foundry/bin" >> "$GITHUB_PATH" - name: Build benchmark binary - run: cargo build --release --bin foundry-bench + run: cargo build --locked --release --bin foundry-bench - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c332240fea036..53d009de4c442 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo test --workspace --doc + - run: cargo test --workspace --doc --locked typos: runs-on: depot-ubuntu-latest @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: crate-ci/typos@02ea592e44b3a53c302f697cddca7641cd051c3d # v1.45.0 + - uses: crate-ci/typos@cf5f1c29a8ac336af8568821ec41919923b05a83 # v1.45.1 shellcheck: runs-on: depot-ubuntu-latest @@ -92,7 +92,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo clippy --workspace --all-targets --all-features + - run: cargo clippy --workspace --all-targets --all-features --locked env: RUSTFLAGS: -Dwarnings diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index 781d63759045e..b2eb6261efd63 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -34,7 +34,7 @@ jobs: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo hack check + - run: cargo hack check --locked issue: name: Open an issue diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..7b85ca2ae00c8 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "master" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a8a3dcbb8c7d3..288d9d127592f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: Build documentation - run: cargo doc --workspace --all-features --no-deps --document-private-items + run: cargo doc --workspace --all-features --no-deps --document-private-items --locked env: RUSTDOCFLAGS: --cfg docsrs -D warnings --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options - name: Setup Pages diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 0a18c99a41f82..f6e068eeec62a 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: DeterminateSystems/update-flake-lock@e80a657d7603606be0c69b117cfdc240f1e6af88 # main + - uses: DeterminateSystems/update-flake-lock@ff43f160ef7014ae1a1fd85699fb6a44f436135b # main with: pr-title: "Update flake.lock" pr-labels: | diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 9b9b798435c90..4ef7c4ac04dfc 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -31,6 +31,7 @@ jobs: contents: read actions: read id-token: write + environment: release name: ${{ matrix.tool }}-${{ matrix.os }}-${{ matrix.arch }} runs-on: ubuntu-latest strategy: @@ -151,9 +152,6 @@ jobs: id: release-version working-directory: ./npm env: - PROVENANCE: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} run: | @@ -224,8 +222,6 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ steps.release-version.outputs.RELEASE_VERSION }} RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | set -euo pipefail @@ -233,9 +229,46 @@ jobs: PLATFORM='${{ matrix.os }}' ARCH='${{ matrix.arch }}' + # Basic validation of matrix-derived values to avoid path manipulation + case "$TOOL" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid TOOL value: $TOOL" >&2; exit 1 ;; + esac + case "$PLATFORM" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid PLATFORM value: $PLATFORM" >&2; exit 1 ;; + esac + case "$ARCH" in + (*[!a-zA-Z0-9_-]*|'') echo "ERROR: Invalid ARCH value: $ARCH" >&2; exit 1 ;; + esac + PACKAGE_DIR="./@foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" + echo "Preparing to publish package from: $PACKAGE_DIR" + + if [[ ! -d "$PACKAGE_DIR" ]]; then + echo "ERROR: Package directory does not exist: $PACKAGE_DIR" >&2 + exit 1 + fi + + # Resolve to an absolute path and ensure it stays within ./@foundry-rs + ABS_PACKAGE_DIR="$(realpath "$PACKAGE_DIR")" + ABS_EXPECTED_ROOT="$(realpath "./@foundry-rs")" + case "$ABS_PACKAGE_DIR" in + "$ABS_EXPECTED_ROOT"/*) ;; + *) + echo "ERROR: Resolved package directory is outside expected root:" >&2 + echo " ABS_PACKAGE_DIR=$ABS_PACKAGE_DIR" >&2 + echo " ABS_EXPECTED_ROOT=$ABS_EXPECTED_ROOT" >&2 + exit 1 + ;; + esac + ls -la "$PACKAGE_DIR" + # Minimal sanity check: require a package.json before publishing + if [[ ! -f "$PACKAGE_DIR/package.json" ]]; then + echo "ERROR: package.json not found in $PACKAGE_DIR; refusing to publish." >&2 + exit 1 + fi + bun ./scripts/publish.mjs "$PACKAGE_DIR" echo "Published @foundry-rs/${TOOL}-${PLATFORM}-${ARCH}" @@ -245,12 +278,11 @@ jobs: contents: read actions: read id-token: write + environment: release needs: publish-arch name: Publish Meta Package runs-on: ubuntu-latest env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} RELEASE_VERSION: ${{ needs.publish-arch.outputs.RELEASE_VERSION }} steps: - name: Checkout @@ -286,6 +318,4 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ env.RELEASE_VERSION }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} - NPM_TOKEN: ${{ env.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ env.NODE_AUTH_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ad324e7ef9c1..52b33787de3f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -179,7 +179,7 @@ jobs: shell: bash run: | set -eo pipefail - flags=(--target $TARGET --profile $RUST_PROFILE --bins + flags=(--locked --target $TARGET --profile $RUST_PROFILE --bins --no-default-features --features "$RUST_FEATURES") # `jemalloc` is not fully supported on MSVC or aarch64 Linux. @@ -279,7 +279,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -294,7 +294,7 @@ jobs: # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release if: ${{ env.IS_NIGHTLY == 'true' }} - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: "Nightly" tag_name: "nightly" diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000000000..0ba82305f82b2 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index af4415b1e8ba1..c0e4fcb5f3263 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -50,7 +50,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --no-fail-fast + run: cargo nextest run --locked --profile flaky --no-fail-fast # If any of the jobs fail, this will create a normal-priority issue to signal so. issue: diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index afd91d3d72c05..37c3edc9b916a 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -54,7 +54,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --features=isolate-by-default --no-fail-fast + run: cargo nextest run --locked --profile flaky --features=isolate-by-default --no-fail-fast # If nextest fails, create a high-priority issue for isolation failures. issue-isolate: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9ccb850dbc5c..7c79cd266f152 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -97,7 +97,7 @@ jobs: run: pip --version && pip install vyper==0.4.3 - name: Foundry test cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.foundry/cache @@ -121,4 +121,4 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run ${{ matrix.flags }} + run: cargo nextest run --locked ${{ matrix.flags }} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/Cargo.lock b/Cargo.lock index d15f7c092e642..b3741acb52d7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,14 +84,14 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-core", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-network", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -116,14 +116,14 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dbe4e5e9107bf6854e7550b666ca654ff2027eabf8153913e2e31ac4b089779" +checksum = "ae8c24c95e90c1608c2d91cff1b451d796474168d3310ccc8b7cd12502ca8169" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-trie", "alloy-tx-macros", "auto_impl", @@ -143,23 +143,23 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88fc7bbfb98cf5605a35aadf0ba43a7d9f1608d6f220d05e4fbd5144d3b0b625" +checksum = "7d211ad0ef468a70a7a829e49683ff59ad25f02b4ab3764344c4c2663329a52c" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-contract" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c16fa30b623e40a5b216da00f3b61870f5cbe863b59816ac1ecc2489515a40" +checksum = "c59d55233ac14aa7fa6bcdcad45ba305e90c556065e0947cd9f243c4469e7c2d" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -238,12 +238,12 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9252074d8b7028bae6844827f51ed8f8a036367604dce9d30d16cedfcc53577" +checksum = "250ba1168b8a049185a68c4dfa7f2a6a4046bd26fcc8c68632caeb216a5e12dc" dependencies = [ "alloy-primitives", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", "serde_json", ] @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb4919fa34b268842f434bfafa9c09136ab7b1a87ce0dd40a61befa35b5408c" +checksum = "ae69eaa5096b47ffe97e6a5d6bde7e7fa2dec106af22a9315621d11039c3de3c" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -310,7 +310,7 @@ dependencies = [ "alloy-eip7928", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "auto_impl", "borsh", "c-kzg", @@ -325,9 +325,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cbf60d85481b12fcc06e29fbe14584fdc5743a34ba7719037f210decd020761" +checksum = "34a8c1330ad33c95b5958573bca9a1ad0b419a51d76bb4c521556fbba8539b8d" dependencies = [ "alloy-contract", "alloy-primitives", @@ -344,7 +344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f107d0e588e5d25fcf2db216390445d5804b875a22a419407ad0389b925bb4d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-hardforks", "alloy-primitives", "alloy-rpc-types-engine", @@ -359,13 +359,13 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e111e22c1a2133e9ebfd9051ea0eaf63559594d2f50d43cbc6762fbb95fc3c2" +checksum = "39789db0b3f3bbef0e6549c87bc6842b73886ebabee1405b6941685b1cc34083" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-trie", "borsh", "serde", @@ -400,9 +400,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b6af6f374c1eeef8ab8dc26232cd440db167322a4207a3debd3d1ee565ca47" +checksum = "662b525af73e86b2167dae923261c8edf440ba7e1426b30a8b993177bc214c02" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -415,19 +415,19 @@ dependencies = [ [[package]] name = "alloy-network" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a3f5a7f3678b71d33fcc45b714fab8928dbc647d5aff2145e72032d5c849bb" +checksum = "c657c2d9751d3c7d94990554b231e5372c3c2e4bad842806280b6151a0d6a05d" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-any", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-sol-types", "async-trait", @@ -441,14 +441,14 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb50dc1fb0e0b2c8748d5bee1aa7acdd18f9e036311bc93a71d97be624030317" +checksum = "59e7c4bb0ebbd6d7406d2808968f43c0d5186c69c5e58cedcbee7380f4cd1fcf" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] @@ -458,7 +458,7 @@ version = "0.30.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -513,13 +513,13 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ba5468f78c8893be2d68a7f2fda61753336e5653f006af19781001b5f99e6c" +checksum = "c4fea0fc2628cdbc851aaa333124f9d8ab9f567ab8d4c20202819db13aa1a534" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-json-rpc", "alloy-network", "alloy-network-primitives", @@ -559,9 +559,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffcefb5d3391a320eadb95d398e4135f8cc35c7bf29a6bdb357eadcfc5ee5638" +checksum = "edc7b42e514613c717887dc77bb58d35e845557ebd63a18c3f92a77094e4891f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -603,9 +603,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222fd4efff0fb9a25184684742c44fe9fa9a16c4ab5bf97583e71c86598ef8f0" +checksum = "d5ee7b51752c68fb95f21705e402700750e692b1d21ccc294ac48fadc8655d53" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -629,9 +629,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974df1e56405c27cb8242381f45d8b212ba9df5006046ccf704764a2a4634366" +checksum = "8fa76988f54105ad4398828e8aaf1a39b3f07f91fb79091529056689514ee8c2" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -640,44 +640,44 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-rpc-types-anvil" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06bc10b0dca4f5bfc3cd30ed46eab5d651b5bb2cd300d683bdcdf5d2bfe6e82c" +checksum = "3d276bea4e92e4991269d31b9abd3e722eed2565b82036478a4416adb8dd4992" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-rpc-types-any" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949c0f16a94ae33cdb1139b8dbf9e34d7f26ebfe97962e2a4d620b5f65f48fe4" +checksum = "1f1a9a3bda9be7f6515316eb792710532411878bbfc88934973f4b371376b00d" dependencies = [ "alloy-consensus-any", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", "serde_json", ] [[package]] name = "alloy-rpc-types-beacon" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8f7fa8ca056bb797a368aeed329e6ace6b62ee4271432ac36ab8ae87a5e60d" +checksum = "caf5d68ddca890854fb78291cbde06115473ded00b2337d0f815e92c0c1f8003" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rpc-types-engine", "derive_more", @@ -689,9 +689,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301249e3c9e43661cfd7ebbb4746a00af6ce1ef58b5c968451882cd60438417d" +checksum = "ea21739e232c221779741eba7e7b9bc19ad8ff777b72736647ae519f5c9f6f33" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -702,15 +702,15 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e59bc947935732cae5b072753e5e034c0b70a8b031c2839f45e2659ba07df9ae" +checksum = "5f05338cfb4ee5508ff76f01c88142cab8a4579db74b7d9432936c26e4f11374" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -722,17 +722,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc280a41931bd419af86e9e859dd9726b73313aaa2e479b33c0e344f4b892ddb" +checksum = "dda4ece0050154ab278241aeffade58916b04f38254832e8cb6e4671c6e72ed2" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-sol-types", "itertools 0.14.0", "serde", @@ -743,13 +743,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede0458c51bef23620aa6bd01a0b4f608be7bcb61d98e91b8530208ae545f3c2" +checksum = "f5905ac3663b0859d67b82d912acce20887d20682a0cadde79c8a763b133a515" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", "serde_json", "thiserror 2.0.18", @@ -757,13 +757,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f4df183248b57f3e0b99054b1b6786769d3fdff6d01a702234068140c8ba76" +checksum = "f7fbf71892d4df9cae8d35dc96f15d522384bb93806205465e2c8c012b7f0a34" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] @@ -780,9 +780,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4848831ff994c88b1c32b7df9c4c1c3eedea4b535bde5eb3c421ef0bdc5ac052" +checksum = "beaa5c581a67e2743d95b4849eb9cfeb90866429cdaa6d8f6b75eb988b2d0cd9" dependencies = [ "alloy-primitives", "serde", @@ -791,9 +791,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b8ad9890b212e224291024b1aecfeef72127d27a2f6eebc5e347c40275c4bf" +checksum = "c5da9ae50f9b48d7b4e2e5cde87175257be7e5e56909a7794720597c1d9806f6" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -808,9 +808,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a57d1e72b1f9b11e5e71ebdab0569cb02277a462bbea6793fcaebfcd794ae9" +checksum = "a19d1985804e9a46d3b1b4f0654a68210b44007ffdaddd0e0a35d2b8db6cc1f0" dependencies = [ "alloy-consensus", "alloy-network", @@ -827,9 +827,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b27f20b5298b76a5a3b7cdbe6bdb184ab1ebd6e120e00dad748867673f5c90" +checksum = "34ce972f2ade53477e8e9336773a417f731fc7c02f41b9cd3b8a2a273e06363e" dependencies = [ "alloy-consensus", "alloy-network", @@ -845,9 +845,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c7acc40ffbfd37d4113eb619863099f3235d78d044006a1eecb94d8b0b2f1a" +checksum = "943c0105e0294b34cd06417129fadc591aed464d06f0614a7e998e585d27fbb1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -865,9 +865,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67d2372aada343130d41e249b59a3cef29b1678dcd3fd80f1c2c4d6b5318f2" +checksum = "49b794002d57fd2f71b4c87298a41ca24dfc0f2cf6630d95106a477e451747ba" dependencies = [ "alloy-consensus", "alloy-network", @@ -885,9 +885,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a09a865ae9e1f05478429ef0d935b16467f35c6e0b02cb10f23f66a3b33fc3" +checksum = "efe910cd3f56f7e4b26b8b7330b11c11c81286eaa8aa9fa6157e767a95e0f310" dependencies = [ "alloy-consensus", "alloy-network", @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "alloy-signer-turnkey" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bb8218544ab635281f1be180a1cfd9b5d549db686faa7e85b3b2c10969819e" +checksum = "2296519a2342608afbd9baddd9d8df9f487604c3f1c2703b27bdd259f6018ea3" dependencies = [ "alloy-consensus", "alloy-network", @@ -991,9 +991,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b7b755e64ae6b5de0d762ed2c780e072167ea5e542076a559e00314352a0bf" +checksum = "19dec9bfb59647254afdecbb5ddcddd7ba02edcd48ffa40510bddfbed0be1634" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -1014,9 +1014,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a29980e69119444ed26b75e7ee5bed2043870f904a64318297e55800db686564" +checksum = "2035f3c4d6bee20624da2dcf765d469b292398e48d766ffade61b0fcf8b4d45d" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -1030,9 +1030,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b27802653330740c88c28394cdaf1d55190b15b48ef9b99a946f37c9cdb19fa" +checksum = "cfad7aa9206fcb831ae401b6a1c893a402b8eed74f9c8ffbb7a7323afb0d9a4c" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -1050,9 +1050,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b71dc951db66795cfb52eef835f64cf15163bc93b656e061b457ce5ebff370" +checksum = "a5aa8ff49386df3e008b73c7fb0a5479410e8493fdb86a8b916877a16e8aead9" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -1085,9 +1085,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8228b9236479ff16b03041b64b86c2bd4e53da1caa45d59b5868cd1571131e" +checksum = "3520337f3d3d063a7fe20f47aaa62d695e3dc0372b34f601560dee24e76988b9" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -1229,7 +1229,7 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-genesis", "alloy-network", @@ -1241,7 +1241,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-beacon", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -1302,12 +1302,12 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eip5792", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "bytes", "foundry-common", "foundry-evm", @@ -2558,7 +2558,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -2730,7 +2730,7 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-ens", "alloy-evm", "alloy-hardforks", @@ -2742,7 +2742,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-beacon", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -3021,7 +3021,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3174,7 +3174,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3958,7 +3958,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4263,7 +4263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4376,6 +4376,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast-float2" version = "0.2.3" @@ -4693,7 +4705,7 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-json-abi", "alloy-network", @@ -4916,7 +4928,7 @@ version = "1.6.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-ens", "alloy-json-abi", "alloy-network", @@ -4978,7 +4990,7 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -5009,6 +5021,7 @@ dependencies = [ "foundry-common-fmt", "foundry-compilers", "foundry-config", + "foundry-wallets", "itertools 0.14.0", "jiff", "mpp", @@ -5046,8 +5059,9 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "chrono", + "comfy-table", "eyre", "foundry-macros", "op-alloy-consensus", @@ -5285,7 +5299,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-sol-types", "anvil", "auto_impl", @@ -5381,7 +5395,7 @@ name = "foundry-evm-networks" version = "1.6.0" dependencies = [ "alloy-chains", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -5485,7 +5499,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "derive_more", "op-alloy-consensus", @@ -5544,7 +5558,7 @@ dependencies = [ [[package]] name = "foundry-wallets" version = "0.1.0" -source = "git+https://github.com/foundry-rs/foundry-core?rev=7f401c1397af90a0a94ef7424a48bbf3dc0248cc#7f401c1397af90a0a94ef7424a48bbf3dc0248cc" +source = "git+https://github.com/foundry-rs/foundry-core?rev=d6c92dfcb41be26fbc1de21221a5bfc6bdd93245#d6c92dfcb41be26fbc1de21221a5bfc6bdd93245" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -5568,6 +5582,7 @@ dependencies = [ "eth-keystore", "eyre", "rpassword", + "rusqlite", "serde", "serde_json", "tempo-primitives", @@ -5970,6 +5985,15 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -6565,7 +6589,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6920,6 +6944,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -7480,7 +7515,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7724,12 +7759,12 @@ version = "0.24.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "bytes", "derive_more", "reth-codecs 0.2.0", @@ -7778,12 +7813,12 @@ version = "0.24.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "derive_more", "op-alloy-consensus", "reth-rpc-traits 0.2.0", @@ -7798,11 +7833,11 @@ version = "0.24.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -8464,7 +8499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8633,7 +8668,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -9033,7 +9068,7 @@ source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed4 dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-genesis", "alloy-primitives", @@ -9053,7 +9088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a29541038ab108b2e9d527c66b565e717e252e4eef6675377fc21f9ba587f792" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -9072,7 +9107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a79b3247ae4fbb1d4d35ce83a11fc596428a4c6ea836c98a75a55340192578a4" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -9125,7 +9160,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9161,7 +9196,7 @@ name = "reth-db-models" version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "bytes", "modular-bitfield", @@ -9176,7 +9211,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9205,7 +9240,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rpc-types-eth", "reth-codecs 0.3.0", @@ -9219,7 +9254,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-primitives", "auto_impl", @@ -9241,7 +9276,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-primitives", "alloy-rpc-types-engine", @@ -9274,7 +9309,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-primitives", "alloy-rlp", @@ -9307,7 +9342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96ffdb2ce0cdcd814d39428dc1e9b660c85d85e0b75eb465a1ed0943a48c7bd" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9332,7 +9367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc759fd87c3f65440e5d3bfa3107fe8a13a61a6807cd485c62c49d63c7bf6717" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9465,7 +9500,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -9487,7 +9522,7 @@ name = "reth-storage-errors" version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", "derive_more", @@ -9509,7 +9544,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-trie", "arrayvec", "bytes", @@ -9930,6 +9965,20 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rusqlite" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b" +dependencies = [ + "bitflags 2.11.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -10000,7 +10049,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10059,7 +10108,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10070,9 +10119,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -10830,7 +10879,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.12.1", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -10865,7 +10914,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.12.1", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -11289,7 +11338,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11299,12 +11348,12 @@ source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe0 dependencies = [ "alloy-consensus", "alloy-contract", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer-local", "alloy-sol-types", "alloy-transport", @@ -11324,7 +11373,7 @@ name = "tempo-chainspec" version = "1.5.3" source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-genesis", "alloy-hardforks", @@ -11429,12 +11478,12 @@ version = "1.6.0" source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "aws-lc-rs", "base64 0.22.1", "derive_more", @@ -11494,7 +11543,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11504,7 +11553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -12721,7 +12770,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -12871,7 +12920,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f902786070d81..7388b62a8acfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/cli/", "crates/common/", "crates/config/", + "crates/config/spec/", "crates/debugger/", "crates/doc/", "crates/evm/core/", @@ -326,6 +327,7 @@ foundry-cli-markdown = { path = "crates/cli-markdown" } foundry-common = { path = "crates/common" } foundry-common-fmt = { path = "crates/common/fmt" } foundry-config = { path = "crates/config" } +foundry-config-spec = { path = "crates/config/spec" } foundry-debugger = { path = "crates/debugger" } foundry-evm = { path = "crates/evm/evm" } foundry-evm-abi = { path = "crates/evm/abi" } @@ -355,36 +357,35 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features ] } ## foundry-core -foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "7f401c1397af90a0a94ef7424a48bbf3dc0248cc", default-features = false } +foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "d6c92dfcb41be26fbc1de21221a5bfc6bdd93245", default-features = false } ## alloy -alloy-consensus = { version = "2.0.0", default-features = false } -alloy-contract = { version = "2.0.0", default-features = false } -alloy-eips = { version = "2.0.0", default-features = false } -alloy-eip5792 = { version = "2.0.0", default-features = false } -alloy-ens = { version = "2.0.0", default-features = false } -alloy-genesis = { version = "2.0.0", default-features = false } -alloy-json-rpc = { version = "2.0.0", default-features = false } -alloy-network = { version = "2.0.0", default-features = false } -alloy-provider = { version = "2.0.0", default-features = false } -alloy-pubsub = { version = "2.0.0", default-features = false } -alloy-rpc-client = { version = "2.0.0", default-features = false } -alloy-rpc-types = { version = "2.0.0", default-features = true } -alloy-rpc-types-beacon = { version = "2.0.0", default-features = true } -alloy-rpc-types-engine = { version = "2.0.0", default-features = false } -alloy-rpc-types-eth = { version = "2.0.0", default-features = false } -alloy-serde = { version = "2.0.0", default-features = false } -alloy-signer = { version = "2.0.0", default-features = false } -alloy-signer-aws = { version = "2.0.0", default-features = false } -alloy-signer-gcp = { version = "2.0.0", default-features = false } -alloy-signer-ledger = { version = "2.0.0", default-features = false } -alloy-signer-local = { version = "2.0.0", default-features = false } -alloy-signer-trezor = { version = "2.0.0", default-features = false } -alloy-signer-turnkey = { version = "2.0.0", default-features = false } -alloy-transport = { version = "2.0.0", default-features = false } -alloy-transport-http = { version = "2.0.0", default-features = false } -alloy-transport-ipc = { version = "2.0.0", default-features = false } -alloy-transport-ws = { version = "2.0.0", default-features = false } +alloy-consensus = { version = "2.0.1", default-features = false } +alloy-contract = { version = "2.0.1", default-features = false } +alloy-eips = { version = "2.0.1", default-features = false } +alloy-eip5792 = { version = "2.0.1", default-features = false } +alloy-ens = { version = "2.0.1", default-features = false } +alloy-genesis = { version = "2.0.1", default-features = false } +alloy-json-rpc = { version = "2.0.1", default-features = false } +alloy-network = { version = "2.0.1", default-features = false } +alloy-provider = { version = "2.0.1", default-features = false } +alloy-pubsub = { version = "2.0.1", default-features = false } +alloy-rpc-client = { version = "2.0.1", default-features = false } +alloy-rpc-types = { version = "2.0.1", default-features = true } +alloy-rpc-types-beacon = { version = "2.0.1", default-features = true } +alloy-rpc-types-engine = { version = "2.0.1", default-features = false } +alloy-rpc-types-eth = { version = "2.0.1", default-features = false } +alloy-serde = { version = "2.0.1", default-features = false } +alloy-signer = { version = "2.0.1", default-features = false } +alloy-signer-aws = { version = "2.0.1", default-features = false } +alloy-signer-gcp = { version = "2.0.1", default-features = false } +alloy-signer-ledger = { version = "2.0.1", default-features = false } +alloy-signer-local = { version = "2.0.1", default-features = false } +alloy-signer-trezor = { version = "2.0.1", default-features = false } +alloy-signer-turnkey = { version = "2.0.1", default-features = false } +alloy-transport = { version = "2.0.1", default-features = false } +alloy-transport-ipc = { version = "2.0.1", default-features = false } +alloy-transport-ws = { version = "2.0.1", default-features = false } alloy-hardforks = { version = "0.4.7", default-features = false } alloy-op-hardforks = { version = "0.4.7", default-features = false } @@ -620,3 +621,6 @@ solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/sola solar-interface = { package = "solar-interface", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-ast = { package = "solar-ast", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-sema = { package = "solar-sema", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } + +[workspace.metadata.cargo-shear] +ignored = ["idna_adapter", "cast", "chisel", "forge", "alloy-contract"] diff --git a/Makefile b/Makefile index fe768f19abf43..1bf20f4eeebd1 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ help: ## Display this help. .PHONY: build build: ## Build the project. - cargo build --features "$(FEATURES)" --profile "$(PROFILE)" + cargo build --locked --features "$(FEATURES)" --profile "$(PROFILE)" .PHONY: build-docker build-docker: ## Build the docker image. @@ -47,17 +47,17 @@ build-docker: ## Build the docker image. ## is unstable (https://github.com/taiki-e/cargo-llvm-cov/issues/2). .PHONY: test-coverage test-coverage: - cargo +nightly llvm-cov --no-report nextest -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ - cargo +nightly llvm-cov --no-report --doc && \ + cargo +nightly llvm-cov --no-report nextest --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ + cargo +nightly llvm-cov --no-report --doc --locked && \ cargo +nightly llvm-cov report --doctests --open .PHONY: test-unit test-unit: ## Run unit tests. - cargo nextest run -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' + cargo nextest run --workspace --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' .PHONY: test-doc test-doc: ## Run doc tests. - cargo test --doc --workspace + cargo test --doc --workspace --locked .PHONY: test test: ## Run all tests. @@ -77,6 +77,7 @@ lint-clippy: ## Run clippy on the codebase. --workspace \ --all-targets \ --all-features \ + --locked \ -- -D warnings .PHONY: lint-clippy-fix @@ -85,6 +86,7 @@ lint-clippy-fix: ## Run clippy on the codebase and fix warnings. --workspace \ --all-targets \ --all-features \ + --locked \ --fix \ --allow-dirty \ --allow-staged \ @@ -104,21 +106,46 @@ lint: ## Run all linters. $(MAKE) lint-clippy && \ $(MAKE) lint-typos +##@ Documentation + +.PHONY: doc +doc: ## Build the documentation. + RUSTDOCFLAGS="--cfg docsrs -D warnings -Zunstable-options --show-type-layout --generate-link-to-definition" \ + cargo +nightly doc \ + --workspace \ + --all-features \ + --document-private-items \ + --no-deps \ + --locked + ##@ Other +.PHONY: lock +lock: ## Update the Cargo.lock file with the current dependencies. + cargo fetch + .PHONY: clean clean: ## Clean the project. cargo clean .PHONY: deny deny: ## Perform a `cargo` deny check. - cargo deny --all-features check all + cargo deny --locked --all-features check all + +.PHONY: check +check: ## Run a feature check on all crates and binaries. + cargo hack check --locked --feature-powerset --depth 1 + +.PHONY: shear +shear: ## Run `cargo shear` to check for unused dependencies. + cargo shear --locked .PHONY: pr pr: ## Run all checks and tests. $(MAKE) deny && \ $(MAKE) lint && \ - $(MAKE) test + $(MAKE) test && \ + $(MAKE) doc # dprint formatting commands .PHONY: dprint-fmt diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ca429168c2095..ab3bec614e3cd 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -130,10 +130,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index dbd89ce88827d..56164c8f6367a 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -28,7 +28,7 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-debugger.workspace = true foundry-evm.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } forge-fmt.workspace = true alloy-chains.workspace = true diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index d36e74b53b6d1..94a435c08559b 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -801,7 +801,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { } _ => SimpleCast::decode_raw_transaction::(&tx)?, }; - sh_println!("{}", serde_json::to_string_pretty(&decoded_tx)?)?; + sh_println!("{decoded_tx}")?; } CastSubcommand::RecoverAuthority { auth } => { let auth: SignedAuthorization = serde_json::from_str(&auth)?; diff --git a/crates/cast/src/call_spec.rs b/crates/cast/src/call_spec.rs index b39e024f38af5..0047b56d25f03 100644 --- a/crates/cast/src/call_spec.rs +++ b/crates/cast/src/call_spec.rs @@ -145,8 +145,8 @@ impl FromStr for CallSpec { /// Parse a value string that can be in ether notation (e.g., "0.1ether") or raw wei. fn parse_ether_or_wei(s: &str) -> Result { // Use alloy's DynSolType coercion which handles "1ether", "1gwei", "1000" etc. - if s.starts_with("0x") { - U256::from_str_radix(s, 16).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) + if s.starts_with("0x") || s.starts_with("0X") { + U256::from_str(s).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), s) .wrap_err_with(|| format!("Invalid value '{s}'"))? @@ -180,6 +180,12 @@ mod tests { assert!(spec.sig.is_none()); } + #[test] + fn test_parse_hex_value() { + assert_eq!(parse_ether_or_wei("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_or_wei("0X10").unwrap(), U256::from(16)); + } + #[test] fn test_parse_with_sig() { let spec = CallSpec::parse( diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index e629b8eca0d5a..1d27b7a23dcd5 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -3,13 +3,13 @@ use std::{str::FromStr, time::Duration}; use crate::{ cmd::send::{cast_send, cast_send_with_access_key}, format_uint_exp, - tx::{SendTxOpts, TxParams}, + tx::{CastTxSender, SendTxOpts, TxParams}, }; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::BlockId; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network}; -use alloy_primitives::U256; +use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder}; +use alloy_primitives::{Address, U256}; use alloy_provider::{Provider, fillers::RecommendedFillers}; use alloy_signer::Signature; use alloy_sol_types::sol; @@ -28,6 +28,7 @@ use foundry_common::{ pub use foundry_config::{Chain, utils::*}; use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; +use tempo_contracts::precompiles::PATH_USD_ADDRESS; sol! { #[sol(rpc)] @@ -281,6 +282,34 @@ impl Erc20Subcommand { } } + const fn uses_browser_send(&self) -> bool { + match self { + Self::Transfer { send_tx, .. } + | Self::Approve { send_tx, .. } + | Self::Mint { send_tx, .. } + | Self::Burn { send_tx, .. } => send_tx.browser.browser, + _ => false, + } + } + + async fn should_use_tempo_network( + &self, + tempo_access_key: &Option, + ) -> eyre::Result { + if self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) + || tempo_access_key.is_some() + { + return Ok(true); + } + + if self.uses_browser_send() { + let config = self.rpc_opts().load_config()?; + return Ok(get_chain(config.chain, &get_provider(&config)?).await?.is_tempo()); + } + + Ok(false) + } + pub async fn run(self) -> eyre::Result<()> { // Resolve the signer once for state-changing variants. let (signer, tempo_access_key) = match &self { @@ -299,8 +328,7 @@ impl Erc20Subcommand { _ => (None, None), }; - let is_tempo = self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) - || tempo_access_key.is_some(); + let is_tempo = self.should_use_tempo_network(&tempo_access_key).await?; if is_tempo { self.run_generic::(signer, tempo_access_key).await @@ -356,6 +384,28 @@ impl Erc20Subcommand { timeout, ) .await? + } else if let Some(browser) = $send_tx.browser.run::().await? { + let $provider = ProviderBuilder::::from_config(&config)?.build()?; + if let Some(interval) = $send_tx.poll_interval { + $provider.client().set_poll_interval(Duration::from_secs(interval)); + } + let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); + let mut tx = { $build_tx }.into_transaction_request(); + let chain = get_chain(config.chain, &$provider).await?; + $tx_opts.apply::(&mut tx, chain.is_legacy()); + if chain.is_tempo() && tx.fee_token().is_none() { + tx.set_fee_token(PATH_USD_ADDRESS); + } + fill_tx(&$provider, &mut tx, browser.address(), chain).await?; + let tx_hash = browser.send_transaction_via_browser(tx).await?; + CastTxSender::new(&$provider) + .print_tx_result( + tx_hash, + $send_tx.cast_async, + $send_tx.confirmations, + timeout, + ) + .await? } else { let signer = pre_resolved_signer.unwrap_or($send_tx.eth.wallet.signer().await?); let $provider = build_provider_with_signer::(&$send_tx, signer)?; @@ -503,3 +553,46 @@ impl Erc20Subcommand { Ok(()) } } + +/// Fills from, chain_id, nonce, fees, and gas limit on a transaction request for the browser +/// wallet path. Mirrors the filling logic in the shared tx builder but operates on a +/// pre-built transaction request from the sol! macro rather than through the builder pipeline. +/// Only fills fields that haven't already been set by the user. +async fn fill_tx>( + provider: &P, + tx: &mut N::TransactionRequest, + from: Address, + chain: Chain, +) -> eyre::Result<()> +where + N::TransactionRequest: FoundryTransactionBuilder, +{ + tx.set_from(from); + tx.set_chain_id(chain.id()); + + if tx.nonce().is_none() { + tx.set_nonce(provider.get_transaction_count(from).await?); + } + + let legacy = chain.is_legacy(); + + if legacy { + if tx.gas_price().is_none() { + tx.set_gas_price(provider.get_gas_price().await?); + } + } else if tx.max_fee_per_gas().is_none() || tx.max_priority_fee_per_gas().is_none() { + let estimate = provider.estimate_eip1559_fees().await?; + if tx.max_fee_per_gas().is_none() { + tx.set_max_fee_per_gas(estimate.max_fee_per_gas); + } + if tx.max_priority_fee_per_gas().is_none() { + tx.set_max_priority_fee_per_gas(estimate.max_priority_fee_per_gas); + } + } + + if tx.gas_limit().is_none() { + tx.set_gas_limit(provider.estimate_gas(tx.clone()).await?); + } + + Ok(()) +} diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 0df824f3588da..7d3b913b8780c 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -3106,6 +3106,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index c98cfb69357bd..af1a09c38d109 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Cheatcodes", - "description": "Foundry cheatcodes. Learn more: ", + "description": "Foundry cheatcodes. Learn more: ", "type": "object", "properties": { "cheatcodes": { diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 29e968aeb5bc6..202b590769857 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -19,7 +19,7 @@ mod vm; pub use vm::Vm; // The `cheatcodes.json` schema. -/// Foundry cheatcodes. Learn more: +/// Foundry cheatcodes. Learn more: #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] @@ -178,7 +178,7 @@ interface Vm {{ eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); if std::env::var("CI").is_ok() { - eprintln!(" NOTE: run `cargo cheats` locally and commit the updated files\n"); + eprintln!(" NOTE: run `cargo spec-cheats` locally and commit the updated files\n"); } if let Some(parent) = file.parent() { let _ = fs::create_dir_all(parent); diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 5e762ec62a035..20ba969abbff1 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1129,6 +1129,15 @@ mod tests { assert_eq!(latest.contractAddress, address!("20c0000000000000000000000000000000000000")); assert!(latest.success); - stdfs::remove_dir_all(root).unwrap(); + if root.exists() { + let root_canon = stdfs::canonicalize(&root).unwrap(); + let temp_canon = stdfs::canonicalize(env::temp_dir()).unwrap(); + assert!( + root_canon.starts_with(&temp_canon), + "refusing to remove non-temp test directory: {}", + root_canon.display() + ); + stdfs::remove_dir_all(&root).unwrap(); + } } } diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 165154da88f63..bcbe837e824e3 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,7 +19,7 @@ foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-evm-networks.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } tempo-primitives.workspace = true diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f1c6c435217b7..be566288ef87a 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -129,8 +129,8 @@ where /// If the string represents an untagged amount (e.g. "100") then /// it is interpreted as wei. pub fn parse_ether_value(value: &str) -> Result { - Ok(if value.starts_with("0x") { - U256::from_str_radix(value, 16)? + Ok(if value.starts_with("0x") || value.starts_with("0X") { + U256::from_str(value)? } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), value)? .as_uint() @@ -844,6 +844,16 @@ mod tests { assert!(!p.is_sol_test()); } + #[test] + fn parse_ether_value_accepts_hex_prefixed_wei() { + assert_eq!(parse_ether_value("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0X10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0x12").unwrap(), U256::from(0x12)); + assert_eq!(parse_ether_value("0xff").unwrap(), U256::from(0xff)); + assert_eq!(parse_ether_value("100").unwrap(), U256::from(100)); + assert_eq!(parse_ether_value("1ether").unwrap(), U256::from(1000000000000000000u128)); + } + // loads .env from cwd and project dir, See [`find_project_root()`] #[test] fn can_load_dotenv() { diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..8f6d7f3cde092 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -16,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); + candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index ac7a8c12eae71..04c2a06a3fa90 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -82,6 +82,7 @@ flate2.workspace = true tempo-alloy.workspace = true tempo-primitives.workspace = true mpp.workspace = true +foundry-wallets.workspace = true [build-dependencies] chrono.workspace = true diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 78759848fc4bd..2c8e16bccdcc6 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -30,6 +30,7 @@ serde_json.workspace = true chrono.workspace = true revm.workspace = true yansi.workspace = true +comfy-table.workspace = true # Tempo tempo-alloy.workspace = true diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index d751ddba245ba..8b66335445046 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -1,5 +1,6 @@ use super::UIfmt; use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256}; +use comfy_table::{Table, TableComponent, presets::UTF8_FULL}; use std::fmt::{self, Write}; /// A piece is a portion of the format string which represents the next part to emit. @@ -407,6 +408,36 @@ fn format_spec<'a>( } } +pub fn console_table_format( + keys: Option<&[&dyn ConsoleFmt]>, + values: &[&dyn ConsoleFmt], +) -> String { + let keys_strings: Vec = match keys { + Some(keys) => keys.iter().map(|k| k.fmt(FormatSpec::String)).collect(), + None => (0..values.len()).map(|i| i.to_string()).collect(), + }; + let values_strings: Vec = values.iter().map(|v| v.fmt(FormatSpec::String)).collect(); + + let mut table = Table::new(); + table.load_preset(UTF8_FULL); + table.set_style(TableComponent::VerticalLines, '│'); + table.set_style(TableComponent::HeaderLines, '─'); + table.set_style(TableComponent::MiddleHeaderIntersections, '┼'); + table.set_style(TableComponent::LeftHeaderIntersection, '├'); + table.set_style(TableComponent::RightHeaderIntersection, '┤'); + table.set_header(vec!["(index)", "Values"]); + table.remove_style(TableComponent::HorizontalLines); + table.remove_style(TableComponent::MiddleIntersections); + table.remove_style(TableComponent::LeftBorderIntersections); + table.remove_style(TableComponent::RightBorderIntersections); + for i in 0..keys_strings.len().max(values_strings.len()) { + let key = keys_strings.get(i).map(String::as_str).unwrap_or(""); + let value = values_strings.get(i).map(String::as_str).unwrap_or(""); + table.add_row(vec![key, value]); + } + table.to_string() +} + #[cfg(test)] mod tests { use super::*; @@ -610,4 +641,80 @@ mod tests { let call = Logs::Log1(log1); assert_eq!(call.fmt(Default::default()), "foo 42 bar"); } + + #[test] + fn test_console_table_format() { + // auto-indexed, uint256 values + let values: &[&dyn ConsoleFmt] = &[&U256::from(100), &U256::from(200), &U256::from(300)]; + assert_eq!( + console_table_format(None, values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ 0 │ 100 │\n\ + │ 1 │ 200 │\n\ + │ 2 │ 300 │\n\ + └─────────┴────────┘" + ); + + // string keys, uint256 values + // key col expands to fit "charlie123" and value col expands to fit "20000000000000000" + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie123")]; + let values: &[&dyn ConsoleFmt] = &[ + &U256::from(1), + &U256::from_str("20000000000000000").unwrap(), + &U256::from_str("30000000000").unwrap(), + ]; + assert_eq!( + console_table_format(Some(keys), values), + "┌────────────┬───────────────────┐\n\ + │ (index) │ Values │\n\ + ├────────────┼───────────────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 20000000000000000 │\n\ + │ charlie123 │ 30000000000 │\n\ + └────────────┴───────────────────┘" + ); + + // empty table + assert_eq!( + console_table_format(None, &[]), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + └─────────┴────────┘" + ); + + // more keys than values + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie")]; + let values: &[&dyn ConsoleFmt] = &[&U256::from(1), &U256::from(2)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ charlie │ │\n\ + └─────────┴────────┘" + ); + + // more values than keys + let keys: &[&dyn ConsoleFmt] = &[&String::from("alice"), &String::from("bob")]; + let values: &[&dyn ConsoleFmt] = + &[&U256::from(1), &U256::from(2), &U256::from(3), &U256::from(4)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ │ 3 │\n\ + │ │ 4 │\n\ + └─────────┴────────┘" + ); + } } diff --git a/crates/common/fmt/src/lib.rs b/crates/common/fmt/src/lib.rs index 89297a8e2c6b5..45ed47263ce32 100644 --- a/crates/common/fmt/src/lib.rs +++ b/crates/common/fmt/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] mod console; -pub use console::{ConsoleFmt, FormatSpec, console_format}; +pub use console::{ConsoleFmt, FormatSpec, console_format, console_table_format}; mod dynamic; pub use dynamic::{ diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 895b16b3b4532..e9f203ebf7539 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) + .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) .map(|(_, data)| data) } diff --git a/crates/common/src/provider/mpp/persist.rs b/crates/common/src/provider/mpp/persist.rs index 6d6781ab4c77b..803c7270e041e 100644 --- a/crates/common/src/provider/mpp/persist.rs +++ b/crates/common/src/provider/mpp/persist.rs @@ -1,243 +1,241 @@ //! Persistent channel storage for MPP sessions. //! -//! Stores open payment channel state in a JSON file at -//! `$TEMPO_HOME/foundry/channels.json` (default: `~/.tempo/foundry/channels.json`). +//! Stores open payment channel state in a SQLite database at +//! `$TEMPO_HOME/channels.db` (default: `~/.tempo/channels.db`). //! This allows channel reuse across process invocations, avoiding the cost of //! opening a new on-chain channel for every `cast` / `forge` command. use alloy_primitives::{Address, B256}; +use foundry_wallets::{Channel, ChannelDb}; use mpp::client::channel_ops::ChannelEntry; -use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, - path::PathBuf, + sync::OnceLock, time::{SystemTime, UNIX_EPOCH}, }; use tracing::{debug, warn}; use crate::tempo::tempo_home; -/// Relative path from Tempo home to the Foundry channels file. -const CHANNELS_PATH: &str = "foundry/channels.json"; +/// Process-wide database handle. +fn global_db() -> Option<&'static ChannelDb> { + static DB: OnceLock> = OnceLock::new(); + DB.get_or_init(|| { + let path = tempo_home()?.join("channels.db"); + if let Some(parent) = path.parent() { + let _ = std::fs::create_dir_all(parent); + } + if let Some(old) = + tempo_home().map(|h| h.join("foundry/channels.json")).filter(|p| p.exists()) + { + warn!( + ?old, + "found old channels.json — this file is no longer used; channels will be re-opened" + ); + } -/// Current schema version. -const SCHEMA_VERSION: u64 = 2; + match ChannelDb::open(&path) { + Ok(db) => { + debug!(?path, "opened channel database"); + Some(db) + } + Err(e) => { + warn!(?path, %e, "failed to open channel database"); + None + } + } + }) + .as_ref() +} -/// On-disk representation of the channel store. -#[derive(Debug, Serialize, Deserialize)] -struct ChannelStore { - version: u64, - #[serde(default)] - channels: HashMap, +/// Reconstruct the composite HashMap key from a persisted `Channel`. +/// +/// Mirrors `SessionProvider::channel_key()` in session.rs. +fn channel_key_from_persisted(ch: &Channel) -> String { + let origin_hash = &alloy_primitives::keccak256(ch.origin.as_bytes()).to_string()[..18]; + format!( + "{}:{}:{}:{}:{}:{}:{}", + origin_hash, + ch.chain_id, + ch.payer, + ch.authorized_signer, + ch.payee, + ch.token, + ch.escrow_contract + ) + .to_lowercase() } -impl Default for ChannelStore { - fn default() -> Self { - Self { version: SCHEMA_VERSION, channels: HashMap::new() } +/// Whether a channel can still be used (active and not fully spent). +fn is_usable(ch: &Channel) -> bool { + if ch.state != "active" { + return false; } + let cumulative: u128 = ch.cumulative_amount.parse().unwrap_or(u128::MAX); + let deposit: u128 = ch.deposit.parse().unwrap_or(0); + cumulative < deposit } -/// A persisted channel entry. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PersistedChannel { - pub channel_id: String, - pub salt: String, - pub escrow_contract: String, - pub chain_id: u64, - pub cumulative_amount: String, - pub deposit: String, - pub status: String, - pub origin: String, - pub created_at: u64, - pub last_used_at: u64, +/// Convert a persisted `Channel` to a `ChannelEntry`. +pub fn to_channel_entry(ch: &Channel) -> Option { + let channel_id: B256 = ch.channel_id.parse().ok()?; + let salt: B256 = ch.salt.parse().ok()?; + let escrow_contract: Address = ch.escrow_contract.parse().ok()?; + let cumulative_amount: u128 = ch.cumulative_amount.parse().ok()?; + + Some(ChannelEntry { + channel_id, + salt, + cumulative_amount, + escrow_contract, + chain_id: ch.chain_id as u64, + opened: ch.state == "active", + }) } -impl PersistedChannel { - /// Convert to an mpp `ChannelEntry` for use in the session provider. - pub fn to_channel_entry(&self) -> Option { - let channel_id: B256 = self.channel_id.parse().ok()?; - let salt: B256 = self.salt.parse().ok()?; - let escrow_contract: Address = self.escrow_contract.parse().ok()?; - let cumulative_amount: u128 = self.cumulative_amount.parse().ok()?; - - Some(ChannelEntry { - channel_id, - salt, - cumulative_amount, - escrow_contract, - chain_id: self.chain_id, - opened: self.status == "active", - }) - } - - /// Create from a `ChannelEntry` with metadata. - pub fn from_channel_entry(entry: &ChannelEntry, deposit: u128, origin: &str) -> Self { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - - Self { - channel_id: entry.channel_id.to_string(), - salt: entry.salt.to_string(), - escrow_contract: entry.escrow_contract.to_string(), - chain_id: entry.chain_id, - cumulative_amount: entry.cumulative_amount.to_string(), - deposit: deposit.to_string(), - status: if entry.opened { "active" } else { "closed" }.to_string(), - origin: origin.to_string(), - created_at: now, - last_used_at: now, - } - } - - /// Whether this channel can still be used (active and not fully spent). - fn is_usable(&self) -> bool { - if self.status != "active" { - return false; - } - let cumulative: u128 = self.cumulative_amount.parse().unwrap_or(u128::MAX); - let deposit: u128 = self.deposit.parse().unwrap_or(0); - cumulative < deposit +/// Create a `Channel` from a `ChannelEntry` with metadata. +pub fn from_channel_entry( + entry: &ChannelEntry, + deposit: u128, + origin: &str, + payer: &Address, + payee: &Address, + token: &Address, + authorized_signer: &Address, +) -> Channel { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; + + Channel { + channel_id: entry.channel_id.to_string(), + version: 1, + origin: origin.to_string(), + request_url: String::new(), + chain_id: entry.chain_id as i64, + escrow_contract: entry.escrow_contract.to_string(), + token: token.to_string(), + payee: payee.to_string(), + payer: payer.to_string(), + authorized_signer: authorized_signer.to_string(), + salt: entry.salt.to_string(), + deposit: deposit.to_string(), + cumulative_amount: entry.cumulative_amount.to_string(), + challenge_echo: String::new(), + state: if entry.opened { "active" } else { "closed" }.to_string(), + close_requested_at: 0, + grace_ready_at: 0, + created_at: now, + last_used_at: now, } } -/// Returns the path to the channels file. -fn channels_path() -> Option { - tempo_home().map(|home| home.join(CHANNELS_PATH)) -} - -/// Load channels from disk, evicting spent/inactive entries. -pub fn load_channels() -> HashMap { - let Some(path) = channels_path().filter(|p| p.exists()) else { +/// Load channels from database, evicting spent/inactive entries. +pub fn load_channels() -> HashMap { + let Some(db) = global_db() else { return HashMap::new(); }; - let Ok(contents) = std::fs::read_to_string(&path).inspect_err(|e| { - warn!(?path, %e, "failed to read channels file"); - }) else { - return HashMap::new(); - }; - - let Ok(store) = serde_json::from_str::(&contents).inspect_err(|e| { - warn!(?path, %e, "failed to parse channels file, starting fresh"); - }) else { - return HashMap::new(); + let channels = match db.load() { + Ok(channels) => channels, + Err(e) => { + warn!(%e, "failed to load channels from database"); + return HashMap::new(); + } }; - if store.version != SCHEMA_VERSION { - warn!( - version = store.version, - expected = SCHEMA_VERSION, - "channels file version mismatch, starting fresh" - ); - return HashMap::new(); - } - - // Evict spent/inactive entries - let usable: HashMap = - store.channels.into_iter().filter(|(_, ch)| ch.is_usable()).collect(); + let usable: HashMap = channels + .into_iter() + .filter(is_usable) + .map(|ch| { + let key = channel_key_from_persisted(&ch); + (key, ch) + }) + .collect(); debug!(count = usable.len(), "loaded persisted MPP channels"); usable } -/// Save channels to disk. -pub fn save_channels(channels: &HashMap) { - let Some(path) = channels_path() else { +/// Save channels to database. +pub fn save_channels(channels: &HashMap) { + let Some(db) = global_db() else { return; }; - if let Some(parent) = path.parent() - && let Err(e) = std::fs::create_dir_all(parent) - { - warn!(?path, %e, "failed to create channels directory"); - return; + for ch in channels.values() { + if let Err(e) = db.upsert(ch) { + warn!(%e, channel_id = %ch.channel_id, "failed to save channel"); + } } + debug!(count = channels.len(), "saved MPP channels"); +} - let store = ChannelStore { version: SCHEMA_VERSION, channels: channels.clone() }; - - match serde_json::to_string_pretty(&store) { - Ok(json) => { - if let Err(e) = std::fs::write(&path, json) { - warn!(?path, %e, "failed to write channels file"); - } else { - debug!(?path, count = channels.len(), "saved MPP channels"); - } - } - Err(e) => warn!(%e, "failed to serialize channels"), +/// Delete a channel from the database by its channel ID. +pub fn delete_channel_from_db(channel_id: &str) { + let Some(db) = global_db() else { + return; + }; + if let Err(e) = db.delete(channel_id) { + warn!(%e, channel_id, "failed to delete channel from database"); } } /// Look up a usable persisted channel by key. -pub fn find_channel( - channels: &HashMap, - key: &str, -) -> Option { - channels.get(key).filter(|ch| ch.is_usable()).and_then(|ch| ch.to_channel_entry()) +pub fn find_channel(channels: &HashMap, key: &str) -> Option { + channels.get(key).filter(|ch| is_usable(ch)).and_then(to_channel_entry) } -/// Insert or update a channel entry in memory only (no disk write). -/// -/// Use [`upsert_channel`] when you want to persist immediately, or call -/// [`save_channels`] separately after this. +/// Insert or update a channel entry in memory only (no DB write). pub fn upsert_channel_in_memory( - channels: &mut HashMap, + channels: &mut HashMap, key: &str, entry: &ChannelEntry, - deposit: u128, - origin: &str, ) { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; if let Some(existing) = channels.get_mut(key) { existing.cumulative_amount = entry.cumulative_amount.to_string(); existing.last_used_at = now; - existing.status = if entry.opened { "active" } else { "closed" }.to_string(); + existing.state = if entry.opened { "active" } else { "closed" }.to_string(); } else { - channels - .insert(key.to_string(), PersistedChannel::from_channel_entry(entry, deposit, origin)); + warn!(key, "upsert_channel_in_memory called for unknown channel"); } } -/// Insert or update a channel entry and save to disk. -/// -/// When updating an existing entry, `deposit` is ignored (preserved from the -/// original open). When inserting a new entry, `deposit` is recorded. -pub fn upsert_channel( - channels: &mut HashMap, - key: &str, - entry: &ChannelEntry, - deposit: u128, - origin: &str, -) { - upsert_channel_in_memory(channels, key, entry, deposit, origin); - save_channels(channels); -} - #[cfg(test)] mod tests { use super::*; - fn test_channel(status: &str, cumulative: &str, deposit: &str) -> PersistedChannel { - PersistedChannel { + fn test_channel(state: &str, cumulative: &str, deposit: &str) -> Channel { + Channel { channel_id: format!("0x{}", "ab".repeat(32)), - salt: format!("0x{}", "cd".repeat(32)), - escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + version: 1, + origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + request_url: String::new(), chain_id: 42431, - cumulative_amount: cumulative.to_string(), + escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + token: "0x20c0000000000000000000000000000000000000".to_string(), + payee: "0x3333333333333333333333333333333333333333".to_string(), + payer: "0x1111111111111111111111111111111111111111".to_string(), + authorized_signer: "0x1111111111111111111111111111111111111111".to_string(), + salt: format!("0x{}", "cd".repeat(32)), deposit: deposit.to_string(), - status: status.to_string(), - origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + cumulative_amount: cumulative.to_string(), + challenge_echo: String::new(), + state: state.to_string(), + close_requested_at: 0, + grace_ready_at: 0, created_at: 1000, last_used_at: 1000, } } #[test] - fn is_usable() { - assert!(test_channel("active", "5000", "100000").is_usable()); - assert!(!test_channel("active", "100000", "100000").is_usable()); - assert!(!test_channel("active", "200000", "100000").is_usable()); - assert!(!test_channel("closed", "0", "100000").is_usable()); - assert!(!test_channel("closing", "0", "100000").is_usable()); + fn usable() { + assert!(is_usable(&test_channel("active", "5000", "100000"))); + assert!(!is_usable(&test_channel("active", "100000", "100000"))); + assert!(!is_usable(&test_channel("active", "200000", "100000"))); + assert!(!is_usable(&test_channel("closed", "0", "100000"))); + assert!(!is_usable(&test_channel("closing", "0", "100000"))); } #[test] @@ -251,8 +249,12 @@ mod tests { opened: true, }; - let persisted = PersistedChannel::from_channel_entry(&entry, 100_000, "https://rpc.test"); - let restored = persisted.to_channel_entry().expect("should parse back"); + let payer = Address::random(); + let payee = Address::random(); + let token = Address::random(); + let persisted = + from_channel_entry(&entry, 100_000, "https://rpc.test", &payer, &payee, &token, &payer); + let restored = to_channel_entry(&persisted).expect("should parse back"); assert_eq!(restored.channel_id, entry.channel_id); assert_eq!(restored.salt, entry.salt); @@ -262,46 +264,6 @@ mod tests { assert!(restored.opened); } - #[test] - fn load_evicts_and_handles_edge_cases() { - let dir = tempfile::tempdir().unwrap(); - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - - let store = ChannelStore { - version: SCHEMA_VERSION, - channels: HashMap::from([ - ("active".into(), test_channel("active", "1000", "100000")), - ("spent".into(), test_channel("active", "100000", "100000")), - ("closed".into(), test_channel("closed", "0", "100000")), - ]), - }; - let json = serde_json::to_string(&store).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), &json).unwrap(); - - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - let loaded = load_channels(); - unsafe { std::env::remove_var("TEMPO_HOME") }; - - assert_eq!(loaded.len(), 1); - assert!(loaded.contains_key("active")); - } - - #[test] - fn load_missing_and_wrong_version() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - assert!(load_channels().is_empty()); - - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), r#"{"version": 999, "channels": {}}"#) - .unwrap(); - assert!(load_channels().is_empty()); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } - #[test] fn find_channel_filters_unusable() { let mut channels = HashMap::new(); @@ -312,34 +274,4 @@ mod tests { assert!(find_channel(&channels, "spent").is_none()); assert!(find_channel(&channels, "missing").is_none()); } - - #[test] - fn upsert_inserts_and_updates() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - - let mut channels = HashMap::new(); - let entry = ChannelEntry { - channel_id: B256::random(), - salt: B256::random(), - cumulative_amount: 1000, - escrow_contract: Address::random(), - chain_id: 42431, - opened: true, - }; - - upsert_channel(&mut channels, "key1", &entry, 100_000, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "1000"); - assert_eq!(channels["key1"].deposit, "100000"); - let created_at = channels["key1"].created_at; - - let mut updated = entry.clone(); - updated.cumulative_amount = 5000; - upsert_channel(&mut channels, "key1", &updated, 0, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "5000"); - assert_eq!(channels["key1"].deposit, "100000"); - assert_eq!(channels["key1"].created_at, created_at); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } } diff --git a/crates/common/src/provider/mpp/session.rs b/crates/common/src/provider/mpp/session.rs index 5996c933218d2..334166b844613 100644 --- a/crates/common/src/provider/mpp/session.rs +++ b/crates/common/src/provider/mpp/session.rs @@ -6,8 +6,9 @@ //! `eth_getTransactionCount`. This avoids the chicken-and-egg problem when //! the RPC endpoint is itself 402-gated. -use super::persist::{self, PersistedChannel}; +use super::persist; use alloy_primitives::{Address, B256, Bytes, TxKind, U256}; +use foundry_wallets::Channel; use mpp::{ client::{ PaymentProvider, @@ -42,7 +43,7 @@ static GLOBAL_CHANNELS: OnceLock>> = O /// /// Using a single map ensures saves from different origins don't clobber /// each other's state. -static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); +static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); /// Tracks uncommitted channel state from the most recent payment. /// @@ -86,7 +87,7 @@ pub struct SessionProvider { default_deposit: Option, channels: Arc>>, key_provisioned: Arc>, - persisted: Arc>>, + persisted: Arc>>, /// Tracks uncommitted open/top-up state for deferred persistence. pending: Arc>>, /// Chain ID from the key entry in `keys.toml` that was used to initialize @@ -128,10 +129,10 @@ impl SessionProvider { map.entry(origin.clone()) .or_insert_with(|| { // Hydrate only channels belonging to this origin. - let mut channels = HashMap::new(); + let mut channels: HashMap = HashMap::new(); for (key, ch) in persisted.lock().unwrap().iter() { if ch.origin == origin - && let Some(entry) = ch.to_channel_entry() + && let Some(entry) = persist::to_channel_entry(ch) { channels.insert(key.clone(), entry); } @@ -209,16 +210,16 @@ impl SessionProvider { // Lock order: channels → persisted (consistent with pay_session) let mut channels = self.channels.lock().unwrap(); let mut persisted = self.persisted.lock().unwrap(); - let keys_to_remove: Vec = persisted + let keys_to_remove: Vec<(String, String)> = persisted .iter() .filter(|(_, ch)| ch.origin == *origin) - .map(|(k, _)| k.clone()) + .map(|(k, ch): (&String, &Channel)| (k.clone(), ch.channel_id.clone())) .collect(); - for key in &keys_to_remove { + for (key, channel_id) in &keys_to_remove { channels.remove(key); persisted.remove(key); + persist::delete_channel_from_db(channel_id); } - persist::save_channels(&persisted); } /// Mark whether the access key has been provisioned on-chain. @@ -685,13 +686,7 @@ impl SessionProvider { // confirms acceptance. let updated_entry = ChannelEntry { cumulative_amount: new_cumulative, ..entry }; let mut persisted = self.persisted.lock().unwrap(); - persist::upsert_channel_in_memory( - &mut persisted, - &key, - &updated_entry, - 0, - &self.origin, - ); + persist::upsert_channel_in_memory(&mut persisted, &key, &updated_entry); drop(persisted); // Track the voucher so we can roll back cumulative_amount @@ -727,9 +722,18 @@ impl SessionProvider { // Update in-memory state but defer disk persistence until server confirms. self.channels.lock().unwrap().insert(key.clone(), entry.clone()); + let authorized_signer = self.authorized_signer.unwrap_or(payer); self.persisted.lock().unwrap().insert( key.clone(), - persist::PersistedChannel::from_channel_entry(&entry, deposit, &self.origin), + persist::from_channel_entry( + &entry, + deposit, + &self.origin, + &payer, + &payee, + ¤cy, + &authorized_signer, + ), ); *self.pending.lock().unwrap() = Some(PendingAction::Open { key }); Ok(build_credential(challenge, payload, chain_id, payer)) diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 39c281e81be25..4c6978e0d9750 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -49,6 +49,9 @@ walkdir.workspace = true yansi.workspace = true clap = { version = "4", features = ["derive"] } +# schema +schemars = { version = "1.0", optional = true } + [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2" @@ -60,3 +63,4 @@ tempfile.workspace = true [features] isolate-by-default = [] +schema = ["dep:schemars"] diff --git a/crates/config/assets/config.schema.json b/crates/config/assets/config.schema.json new file mode 100644 index 0000000000000..00a381eaf66ff --- /dev/null +++ b/crates/config/assets/config.schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Config", + "description": "Foundry configuration. Learn more: ", + "type": "object" +} \ No newline at end of file diff --git a/crates/config/spec/Cargo.toml b/crates/config/spec/Cargo.toml new file mode 100644 index 0000000000000..89d40cf5606ce --- /dev/null +++ b/crates/config/spec/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "foundry-config-spec" +description = "Foundry configuration specification" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[dependencies] +foundry-config.workspace = true +serde.workspace = true + +# schema +schemars = { version = "1.0", optional = true } + +[dev-dependencies] +serde_json.workspace = true + +[features] +schema = ["dep:schemars", "foundry-config/schema"] diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs new file mode 100644 index 0000000000000..5a362e963d956 --- /dev/null +++ b/crates/config/spec/src/lib.rs @@ -0,0 +1,64 @@ +//! Config specification for Foundry. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use foundry_config::Config; +use serde::{Deserialize, Serialize}; + +// The `config.json` schema. +/// Foundry configuration. Learn more: +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct ConfigSchema { + #[serde(flatten)] + pub config: Config, +} + +#[cfg(test)] +#[expect(clippy::disallowed_macros)] +mod tests { + use super::*; + use std::{fs, path::Path}; + + #[cfg(feature = "schema")] + const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/config.schema.json"); + + /// Generates the configuration JSON schema. + #[cfg(feature = "schema")] + fn json_schema() -> String { + serde_json::to_string_pretty(&schemars::schema_for!(ConfigSchema)).unwrap() + } + + #[test] + #[cfg(feature = "schema")] + fn schema_up_to_date() { + ensure_file_contents(Path::new(SCHEMA_PATH), &json_schema()); + } + + /// Checks that the `file` has the specified `contents`. If that is not the + /// case, updates the file and then fails the test. + fn ensure_file_contents(file: &Path, contents: &str) { + if let Ok(old_contents) = fs::read_to_string(file) + && normalize_newlines(&old_contents) == normalize_newlines(contents) + { + // File is already up to date. + return; + } + + eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); + if std::env::var("CI").is_ok() { + eprintln!(" NOTE: run `cargo spec-config` locally and commit the updated files\n"); + } + if let Some(parent) = file.parent() { + let _ = fs::create_dir_all(parent); + } + fs::write(file, contents).unwrap(); + panic!("some file was not up to date and has been updated, simply re-run the tests"); + } + + fn normalize_newlines(s: &str) -> String { + s.replace("\r\n", "\n") + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f148432c42bed..b1b889031c690 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1314,13 +1314,32 @@ impl Config { } // Remove last test run failures file. - if let Err(err) = fs::remove_file(&self.test_failures_file) - && err.kind() != io::ErrorKind::NotFound - { - warnings.push(format!( - "failed to remove test failures file {}: {err}", - self.test_failures_file.display() - )); + let test_failures_path = if self.test_failures_file.is_absolute() { + self.test_failures_file.clone() + } else { + project.root().join(&self.test_failures_file) + }; + let root_canon = dunce::canonicalize(project.root()).unwrap_or_else(|_| project.root().to_path_buf()); + let validated_test_failures_path = test_failures_path + .parent() + .and_then(|parent| dunce::canonicalize(parent).ok().map(|p| (p, test_failures_path.file_name()))) + .and_then(|(parent, file_name)| file_name.map(|name| parent.join(name))); + + match validated_test_failures_path { + Some(path) if path.starts_with(&root_canon) => { + if let Err(err) = fs::remove_file(&path) && err.kind() != io::ErrorKind::NotFound { + warnings.push(format!( + "failed to remove test failures file {}: {err}", + path.display() + )); + } + } + _ => { + warnings.push(format!( + "skipped removing test failures file outside project root: {}", + test_failures_path.display() + )); + } } // Remove fuzz and invariant cache directories. diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 3e915b1b7f479..3d4c57fe5dc3e 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -151,6 +151,12 @@ impl From> for Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/doc/src/writer/buf_writer.rs b/crates/doc/src/writer/buf_writer.rs index 8081de7dac660..085664f85fa81 100644 --- a/crates/doc/src/writer/buf_writer.rs +++ b/crates/doc/src/writer/buf_writer.rs @@ -1,4 +1,4 @@ -use crate::{AsDoc, CommentTag, Comments, Deployment, Markdown, writer::traits::ParamLike}; +use crate::{writer::traits::ParamLike, AsDoc, CommentTag, Comments, Deployment, Markdown}; use itertools::Itertools; use solang_parser::pt::{ EnumDefinition, ErrorParameter, EventParameter, Parameter, VariableDeclaration, @@ -89,17 +89,6 @@ impl BufWriter { writeln!(self.buf, "{}", Markdown::Italic(text)) } - /// Writes dev content to the buffer, handling markdown lists properly. - /// If the content contains markdown lists, it formats them correctly. - /// Otherwise, it writes the content in italics. - pub fn write_dev_content(&mut self, text: &str) -> fmt::Result { - for line in text.lines() { - writeln!(self.buf, "{line}")?; - } - - Ok(()) - } - /// Writes bold text to the buffer formatted as [Markdown::Bold]. pub fn write_bold(&mut self, text: &str) -> fmt::Result { writeln!(self.buf, "{}", Markdown::Bold(text)) @@ -202,7 +191,8 @@ impl BufWriter { params: &EnumDefinition, comments: &Comments, ) -> fmt::Result { - let comments = comments.include_tags(&[CommentTag::Param]); + let comments = + comments.include_tags(&[CommentTag::Param, CommentTag::Custom("variant".to_string())]); // There is nothing to write. if comments.is_empty() { @@ -215,7 +205,7 @@ impl BufWriter { self.write_piped(&VARIANTS_TABLE_HEADERS.join("|"))?; self.write_piped(&VARIANTS_TABLE_SEPARATOR)?; - for value in ¶ms.values { + for value in params.values.iter() { let param_name = value.as_ref().map(|v| v.name.clone()); let comment = param_name.as_ref().and_then(|name| { @@ -223,7 +213,7 @@ impl BufWriter { }); let row = [ - Markdown::Code(¶m_name.unwrap_or("".to_string())).as_doc()?, + Markdown::Code(param_name.as_deref().unwrap_or("")).as_doc()?, comment.unwrap_or_default().replace('\n', " "), ]; self.write_piped(&row.join("|"))?; diff --git a/crates/evm/abi/src/Console.json b/crates/evm/abi/src/Console.json index 54e6d46dff349..f275f471087ee 100644 --- a/crates/evm/abi/src/Console.json +++ b/crates/evm/abi/src/Console.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file diff --git a/crates/evm/abi/src/console.py b/crates/evm/abi/src/console.py index 2e28fece21e42..d0b32ff410941 100755 --- a/crates/evm/abi/src/console.py +++ b/crates/evm/abi/src/console.py @@ -16,12 +16,15 @@ def main(): # Parse signatures from `console.sol`'s string literals console_sol = open(console_file).read() sig_strings = re.findall( - r'"(log.*?)"', + r'"((?:log|table).*?)"', console_sol, ) raw_sigs = [s.strip().strip('"') for s in sig_strings] sigs = [ - s.replace("string", "string memory").replace("bytes)", "bytes memory)") + re.sub(r"(\w+\[\])", r"\1 memory", s) + .replace("string,", "string memory,") + .replace("string)", "string memory)") + .replace("bytes)", "bytes memory)") for s in raw_sigs ] sigs = list(set(sigs)) @@ -38,6 +41,7 @@ def main(): ) combined = json.loads(r.stdout.strip()) abi = combined["contracts"][":HardhatConsole"]["abi"] + open(abi_file, "w").write(json.dumps(abi, separators=(",", ":"), indent=None)) diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index 26acbe56a97cb..c591b9426bf9e 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -50,7 +50,9 @@ impl LogCollector { fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> { let decoded = console::hh::ConsoleCalls::abi_decode(data)?; - self.push_msg(&decoded.fmt(Default::default())); + for line in decoded.fmt(Default::default()).lines() { + self.push_msg(line); + } Ok(()) } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 540c5209aa981..ecdc13ded67cf 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -51,7 +51,7 @@ forge-script.workspace = true forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } alloy-chains.workspace = true alloy-consensus.workspace = true @@ -62,6 +62,7 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true tempo-alloy.workspace = true diff --git a/crates/forge/src/cmd/config.rs b/crates/forge/src/cmd/config.rs index 49716146c456b..fac66727c9d99 100644 --- a/crates/forge/src/cmd/config.rs +++ b/crates/forge/src/cmd/config.rs @@ -53,8 +53,8 @@ impl ConfigArgs { } else { config.to_string_pretty()? }; - sh_println!("{s}")?; + Ok(()) } } diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 122020571ad24..a48d2a9350c70 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -9,7 +9,7 @@ use alloy_provider::{PendingTransactionError, Provider, ProviderBuilder as Alloy use alloy_signer::{Signature, Signer}; use alloy_transport::TransportError; use clap::{Parser, ValueHint}; -use eyre::{Context, Result}; +use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, @@ -33,10 +33,12 @@ use foundry_config::{ }, merge_impl_figment_convert, }; -use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; +use foundry_wallets::{ + BrowserWalletOpts, TempoAccessKeyConfig, WalletSigner, wallet_browser::signer::BrowserSigner, +}; use serde_json::json; use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc, time::Duration}; -use tempo_alloy::TempoNetwork; +use tempo_alloy::{TempoNetwork, contracts::precompiles::DEFAULT_FEE_TOKEN}; merge_impl_figment_convert!(CreateArgs, build, eth); @@ -101,14 +103,29 @@ pub struct CreateArgs { #[command(flatten)] retry: RetryArgs, + + /// Browser wallet options + #[command(flatten)] + browser: BrowserWalletOpts, } impl CreateArgs { /// Executes the command to create a contract - pub async fn run(self) -> Result<()> { + pub async fn run(mut self) -> Result<()> { let (signer, tempo_access_key) = self.eth.wallet.maybe_signer().await?; - if tempo_access_key.is_some() || self.tx.tempo.is_tempo() { + // Resolve chain early so we can dispatch to the correct network type. + if self.chain_id().is_none() { + let config = self.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let chain_id = provider.get_chain_id().await?; + self.eth.etherscan.chain = Some(chain_id.into()); + } + + if tempo_access_key.is_some() + || self.tx.tempo.is_tempo() + || self.chain_id().is_some_and(|c| c.is_tempo()) + { self.run_generic::(signer, tempo_access_key).await } else { self.run_generic::(signer, None).await @@ -185,17 +202,29 @@ impl CreateArgs { self.tx.tempo.key_id = Some(ak.key_address); } - // respect chain, if set explicitly via cmd args - let chain_id = if let Some(chain_id) = self.chain_id() { - chain_id - } else { - provider.get_chain_id().await? - }; - // Whether to broadcast the transaction or not let dry_run = !self.broadcast; - if self.unlocked { + // Launch browser signer if `--browser` flag is set + let browser = self.browser.run::().await?; + + if let Some(browser) = browser { + // Deploy with browser wallet + let deployer_address = browser.address(); + self.deploy( + abi, + bin, + params, + provider, + deployer_address, + config.transaction_timeout, + id, + dry_run, + None, + Some(browser), + ) + .await + } else if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); self.deploy( @@ -203,12 +232,12 @@ impl CreateArgs { bin, params, provider, - chain_id, sender, config.transaction_timeout, id, dry_run, None, + None, ) .await } else if let Some(ak) = access_key { @@ -223,12 +252,12 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer_address, config.transaction_timeout, id, dry_run, Some((signer, ak)), + None, ) .await } else { @@ -246,20 +275,20 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer, config.transaction_timeout, id, dry_run, None, + None, ) .await } } - /// Returns the provided chain id, if any. - fn chain_id(&self) -> Option { - self.eth.etherscan.chain.map(|chain| chain.id()) + /// Returns the resolved chain, if any. + const fn chain_id(&self) -> Option { + self.eth.etherscan.chain } /// Ensures the verify command can be executed. @@ -271,7 +300,6 @@ impl CreateArgs { async fn verify_preflight_check( &self, constructor_args: Option, - chain: u64, id: &ArtifactId, ) -> Result<()> { // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, @@ -287,7 +315,7 @@ impl CreateArgs { num_of_optimizations: None, etherscan: EtherscanOpts { key: self.eth.etherscan.key.clone(), - chain: Some(chain.into()), + chain: self.chain_id(), }, rpc: Default::default(), flatten: false, @@ -311,7 +339,7 @@ impl CreateArgs { // ETHERSCAN_API_KEY value set. let config = verify.load_config()?; verify.etherscan.key = - config.get_etherscan_config_with_chain(Some(chain.into()))?.map(|c| c.key); + config.get_etherscan_config_with_chain(self.chain_id())?.map(|c| c.key); let context = verify.resolve_context().await?; @@ -327,17 +355,19 @@ impl CreateArgs { bin: BytecodeObject, args: Vec, provider: P, - chain: u64, deployer_address: Address, timeout: u64, id: ArtifactId, dry_run: bool, tempo_keychain: Option<(WalletSigner, TempoAccessKeyConfig)>, + browser_signer: Option>, ) -> Result<()> where N::TransactionRequest: FoundryTransactionBuilder + serde::Serialize, N::ReceiptResponse: serde::Serialize, { + let chain = self.chain_id().context("chain ID not resolved")?; + let bin = bin.into_bytes().unwrap_or_default(); if bin.is_empty() { eyre::bail!("no bytecode found in bin object for {}", self.contract.name) @@ -349,22 +379,31 @@ impl CreateArgs { let is_args_empty = args.is_empty(); let mut deployer = - factory.deploy_tokens(args.clone(), self.tx.tempo.fee_token).context("failed to deploy contract").map_err(|e| { + 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") } else { e } })?; - let is_legacy = self.tx.legacy || Chain::from(chain).is_legacy(); + let is_legacy = self.tx.legacy || chain.is_legacy(); deployer.tx.set_from(deployer_address); - deployer.tx.set_chain_id(chain); + deployer.tx.set_chain_id(chain.id()); // `to` field must be set explicitly, cannot be None. if deployer.tx.to().is_none() { deployer.tx.set_create(); } + // If Tempo chain fee token must be set + if chain.is_tempo() { + if let Some(fee_token) = self.tx.tempo.fee_token { + deployer.tx.set_fee_token(fee_token); + } else { + deployer.tx.set_fee_token(DEFAULT_FEE_TOKEN); + } + } + // Apply user-provided gas, fee, nonce, and Tempo options. self.tx.apply::(&mut deployer.tx, is_legacy); @@ -422,7 +461,7 @@ impl CreateArgs { constructor_args = Some(hex::encode(encoded_args)); } - self.verify_preflight_check(constructor_args.clone(), chain, &id).await?; + self.verify_preflight_check(constructor_args.clone(), &id).await?; } if dry_run { @@ -452,7 +491,31 @@ impl CreateArgs { } // Deploy the actual contract - let (deployed_contract, receipt) = if let Some((signer, ak)) = tempo_keychain { + let (deployed_contract, receipt) = if let Some(browser) = browser_signer { + // Browser wallet signs and sends the transaction + let tx_hash = browser.send_transaction_via_browser(deployer.tx).await?; + + // Wait for the transaction to be confirmed, then fetch the receipt. + provider + .watch_pending_transaction(alloy_provider::PendingTransactionConfig::new(tx_hash)) + .await? + .await?; + + let receipt = provider + .get_transaction_receipt(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("could not get transaction receipt for {tx_hash}"))?; + + if !receipt.status() { + eyre::bail!("deployment transaction failed (receipt status 0): {tx_hash}"); + } + + let address = receipt + .contract_address() + .ok_or_else(|| eyre::eyre!("contract was not deployed"))?; + + (address, receipt) + } else if let Some((signer, ak)) = tempo_keychain { // Tempo keychain mode: sign with access key provisioning and send raw let raw_tx = deployer .tx @@ -518,7 +581,7 @@ impl CreateArgs { no_auto_detect: false, use_solc: None, num_of_optimizations, - etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) }, + etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain) }, rpc: Default::default(), flatten: false, force: false, @@ -681,7 +744,6 @@ impl + Clone> DeploymentTxFactory { pub fn deploy_tokens( self, params: Vec, - fee_token: Option
, ) -> Result, ContractDeploymentError> where N::TransactionRequest: FoundryTransactionBuilder, @@ -703,9 +765,6 @@ impl + Clone> DeploymentTxFactory { // create the tx object. Since we're deploying a contract, `to` is `None` let mut tx = N::TransactionRequest::default(); tx.set_input(data); - if let Some(fee_token) = fee_token { - tx.set_fee_token(fee_token); - } Ok(Deployer { client: self.client.clone(), tx, confs: 1, timeout: self.timeout }) } } @@ -763,7 +822,7 @@ mod tests { "--chain-id", "9999", ]); - assert_eq!(args.chain_id(), Some(9999)); + assert_eq!(args.chain_id().map(|c| c.id()), Some(9999)); } #[test] diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/macros/src/console_fmt.rs b/crates/macros/src/console_fmt.rs index 0c95841d1e653..245ba6e5b78e2 100644 --- a/crates/macros/src/console_fmt.rs +++ b/crates/macros/src/console_fmt.rs @@ -7,7 +7,7 @@ use syn::{ pub fn console_fmt(input: &DeriveInput) -> TokenStream { let name = &input.ident; let tokens = match &input.data { - Data::Struct(s) => derive_struct(s), + Data::Struct(s) => derive_struct(s, name), Data::Enum(e) => derive_enum(e), Data::Union(_) => return quote!(compile_error!("Unions are unsupported");), }; @@ -18,8 +18,8 @@ pub fn console_fmt(input: &DeriveInput) -> TokenStream { } } -fn derive_struct(s: &DataStruct) -> TokenStream { - let imp = impl_struct(s).unwrap_or_else(|| quote!(String::new())); +fn derive_struct(s: &DataStruct, name: &Ident) -> TokenStream { + let imp = impl_struct(s, name).unwrap_or_else(|| quote!(String::new())); quote! { fn fmt(&self, _spec: FormatSpec) -> String { #imp @@ -27,7 +27,7 @@ fn derive_struct(s: &DataStruct) -> TokenStream { } } -fn impl_struct(s: &DataStruct) -> Option { +fn impl_struct(s: &DataStruct, name: &Ident) -> Option { if s.fields.is_empty() { return None; } @@ -36,13 +36,49 @@ fn impl_struct(s: &DataStruct) -> Option { return None; } + let members = s.fields.members().collect::>(); let fields = s.fields.iter().collect::>(); + + // Detect table call structs: name must start with "table" (from the ABI function name) and + // all fields must be Vec types (Solidity arrays). Both conditions together prevent + // accidental table rendering for unrelated structs that happen to have Vec fields. + let is_table = name.to_string().starts_with("table") + && !fields.is_empty() + && fields.iter().all(|f| match &f.ty { + Type::Path(path) => path.path.segments.last().is_some_and(|seg| seg.ident == "Vec"), + _ => false, + }); + if is_table { + let member_ref = |m: &Member| match m { + Member::Named(ident) => quote!(&self.#ident), + Member::Unnamed(idx) => quote!(&self.#idx), + }; + let imp = if members.len() == 1 { + let vals = member_ref(&members[0]); + quote! { + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(None, &values) + } + } else { + let keys = member_ref(&members[0]); + let vals = member_ref(&members[1]); + quote! { + let keys: ::std::vec::Vec<&dyn ConsoleFmt> = + (#keys).iter().map(|v| v as &dyn ConsoleFmt).collect(); + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(Some(&keys), &values) + } + }; + return Some(imp); + } + let first_ty = match &fields.first().unwrap().ty { Type::Path(path) => path.path.segments.last().unwrap().ident.to_string(), _ => String::new(), }; - let members = s.fields.members().collect::>(); let args: Punctuated = members .into_iter() .map(|member| match member { diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 4146e07d2248b..acdcbbdbf95ea 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -23,7 +23,7 @@ foundry-evm-networks.workspace = true alloy-evm.workspace = true foundry-debugger.workspace = true foundry-cheatcodes.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } foundry-linking.workspace = true forge-script-sequence.workspace = true diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 06f83886d0fd2..c1a6cb53bdbff 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,9 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); - fs::copy(file, to_dir.join(name))?; + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } else { + continue; + } + fs::copy(&file, to_dir.join(name))?; } Ok(()) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index d22e12736d832..a6df9cda5e976 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -4,7 +4,7 @@ use std::{ env, fs::{self, File}, io::{Read, Seek, Write}, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf}, process::Command, sync::LazyLock, }; @@ -231,15 +231,59 @@ pub fn read_string(path: impl AsRef) -> String { /// copied to temporary test workspaces. pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { fs::create_dir_all(dst)?; - copy_dir_filtered_inner(src, dst, true) + // Canonicalize the source once and treat it as the base directory for all traversal. + let base = src.canonicalize()?; + // Canonicalize the destination once and treat it as the base directory for all writes. + let dst_base = dst.canonicalize()?; + copy_dir_filtered_inner(src, dst, &base, &dst_base, true) } -fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Result<()> { +fn copy_dir_filtered_inner( + src: &Path, + dst: &Path, + base_src: &Path, + base_dst: &Path, + is_root: bool, +) -> std::io::Result<()> { for entry in fs::read_dir(src)? { let entry = entry?; let ty = entry.file_type()?; let src_path = entry.path(); - let dst_path = dst.join(entry.file_name()); + // Ensure that any path we operate on stays within the original source base directory. + let canonical_src_path = src_path.canonicalize()?; + if !canonical_src_path.starts_with(base_src) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + let relative_src_path = canonical_src_path.strip_prefix(base_src).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "failed to derive relative path within source base directory", + ) + })?; + for component in relative_src_path.components() { + match component { + Component::Normal(name) => { + let name = name.to_string_lossy(); + if name.contains("..") || name.contains('/') || name.contains('\\') { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "invalid path component in source entry", + )); + } + } + Component::CurDir => {} + Component::ParentDir | Component::RootDir | Component::Prefix(_) => { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to access path outside of allowed source base directory", + )); + } + } + } + let dst_path = base_dst.join(relative_src_path); if ty.is_dir() { // Skip build artifact directories at the root level @@ -249,10 +293,58 @@ fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Re { continue; } + // Ensure that the destination directory stays within the allowed destination base. + let canonical_dst_dir = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // Directory does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_dir.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to create directory outside of allowed destination base directory", + )); + } fs::create_dir_all(&dst_path)?; - copy_dir_filtered_inner(&src_path, &dst_path, false)?; + copy_dir_filtered_inner(&src_path, &dst_path, base_src, base_dst, false)?; } else { - fs::copy(&src_path, &dst_path)?; + // Ensure that the destination file path stays within the allowed destination base. + let canonical_dst_path = dst_path.canonicalize().or_else(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + // File does not exist yet; ensure its parent is within base_dst. + if let Some(parent) = dst_path.parent() { + let parent_canonical = parent.canonicalize()?; + if !parent_canonical.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + } + Ok(dst_path.clone()) + } else { + Err(err) + } + })?; + if !canonical_dst_path.starts_with(base_dst) { + return Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "attempted to write file outside of allowed destination base directory", + )); + } + fs::copy(&canonical_src_path, &canonical_dst_path)?; } } Ok(()) diff --git a/npm/bun.lock b/npm/bun.lock index e8ef087be9d50..d9e8fed5ce479 100644 --- a/npm/bun.lock +++ b/npm/bun.lock @@ -1,12 +1,13 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.5.2", "bun": "^1.3.1", - "typescript": "^5.9.3", + "typescript": "^6.0.2", }, }, }, @@ -35,7 +36,7 @@ "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], - "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], @@ -45,8 +46,12 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "bun-types/@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], } } diff --git a/npm/scripts/publish.mjs b/npm/scripts/publish.mjs index 0f582c8ebc555..deee29e46c92f 100644 --- a/npm/scripts/publish.mjs +++ b/npm/scripts/publish.mjs @@ -7,17 +7,12 @@ import { colors } from '#const.mjs' const REGISTRY_URL = Bun.env.NPM_REGISTRY_URL || 'https://registry.npmjs.org' -const NPM_TOKEN = Bun.env.NPM_TOKEN -if (!NPM_TOKEN) throw new Error('NPM_TOKEN is required') - main().catch(error => { console.error(error) process.exit(1) }) async function main() { - const npmToken = Bun.env.NPM_TOKEN - if (!npmToken) throw new Error('NPM_TOKEN is required') const inputPath = Bun.argv[2] if (!inputPath) throw new Error('Package path is required') @@ -114,11 +109,6 @@ async function setPackageVersion(packagePath, version) { console.info(colors.green, 'Setting package version:', version) const result = await Bun.$`npm version ${version} --allow-same-version --no-git-tag-version` .cwd(packagePath) - .env({ - ...Bun.env, - ...process.env, - NPM_TOKEN - }) .quiet() .nothrow() diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix diff --git a/npm/src/const.mjs b/npm/src/const.mjs index 6b3dcf3f9fbed..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** diff --git a/npm/tsconfig.json b/npm/tsconfig.json index ca8e3c92f2daa..a7a0093d7ee56 100644 --- a/npm/tsconfig.json +++ b/npm/tsconfig.json @@ -2,7 +2,6 @@ "schema": "https://json.schemastore.org/tsconfig.json", "compilerOptions": { "strict": true, - "baseUrl": ".", "noEmit": true, "allowJs": true, "checkJs": true, From a40377f84edb43264e330a82ef9303d90b48de99 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 02:28:23 +0700 Subject: [PATCH 339/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From b300dc7fb82e5c662c3fcd9ea5ec2d96a8a015df Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:24:38 +0000 Subject: [PATCH 340/374] Potential fix for code scanning alert no. 19 (#471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 156: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 155: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 170: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create apisec-scan.yml (#404) * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Cr… * Delete .circleci directory (#423) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert 350 dargon789/gamefi (#424) * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production depende… * Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/config/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#412) * Update docker.yml * Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory (#422) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * doc-script.js (#438) * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI p… * Remove duplicate logic in TxSigner::address() implementations (#447) Co-authored-by: Aganis * fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#448) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: googleworkspace-bot * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Add docs, sidebar assets and Foundry deps (#459) * Add docs, sidebar assets and Foundry deps Add generated documentation assets (doc-script.js, doc-style.css, doc-filelist.js) under both doc/ and counter/doc/ to provide a collapsible sidebar/tree UI. Add counter/.gas-snapshot test gas output and update counter/.gitignore to ignore Soldeer dependencies. Update counter/foundry.toml to include a dependencies section and forge-std, and add remappings.txt plus soldeer.lock to pin the forge-std dependency. * Update counter/doc/doc-script.js Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (foundry-rs#14398 (#463) * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: zerosnacks Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: bump alloy 2.0.1 (#14417) * chore(deps): bump docker/metadata-action from 5 to 6 (#415) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/v5...v6) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/configure-pages from 5 to 6 (#428) Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 5 to 6. - [Release notes](https://github.com/actions/configure-pages/releases) - [Commits](https://github.com/actions/configure-pages/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/configure-pages dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/upload-pages-artifact from 3 to 5 (#430) Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 5. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v5) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#431) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fix(wallets): add gas limit margin to handle WebAuthn/P256 signing (#14416) fix(wallets): add gas limit margin to handle WebAuthn/P256 signing --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar Co-authored-by: googleworkspace-bot Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: cui Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Louis Peter Sitoe Co-authored-by: o-az Co-authored-by: steven Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> --- .circleci/ci_v1.yml | 31 -- .circleci/config.yml | 31 -- .github/scripts/tempo-mpp.sh | 18 +- .github/workflows/Docker.yml | 3 +- .github/workflows/benchmarks.yml | 2 +- .github/workflows/ci.yml | 6 +- .github/workflows/crate-checks.yml | 2 +- .github/workflows/docker.yml | 3 +- .github/workflows/docs.yml | 2 +- .github/workflows/nix.yml | 2 +- .github/workflows/npm.yml | 27 +- .github/workflows/release.yml | 6 +- .github/workflows/static.yml | 4 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 4 +- Cargo.lock | 363 +++++++++------- Cargo.toml | 58 +-- Makefile | 41 +- benches/src/lib.rs | 1 + counter/.gas-snapshot | 2 + counter/.gitignore | 4 + counter/doc/doc-filelist.js | 1 + counter/doc/doc-script.js | 228 ++++++++++ counter/doc/doc-style.css | 403 +++++++++++++++++ counter/foundry.toml | 5 +- counter/remappings.txt | 1 + counter/soldeer.lock | 6 + crates/anvil/src/cmd.rs | 125 +++++- crates/anvil/src/config.rs | 299 ++++++++----- crates/anvil/src/eth/api.rs | 12 +- crates/anvil/src/eth/backend/db.rs | 4 +- crates/anvil/src/eth/backend/fork.rs | 62 ++- crates/anvil/src/eth/backend/mem/fork_db.rs | 4 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 12 +- crates/anvil/tests/it/fork.rs | 58 +++ crates/cast/Cargo.toml | 2 +- crates/cast/src/call_spec.rs | 10 +- crates/cast/src/cmd/erc20.rs | 113 ++++- crates/cast/src/cmd/send.rs | 14 +- crates/cast/src/tx.rs | 7 + crates/cli/Cargo.toml | 2 +- crates/cli/src/utils/mod.rs | 14 +- crates/cli/src/utils/suggestions.rs | 2 +- crates/common/Cargo.toml | 1 + crates/common/fmt/Cargo.toml | 1 + crates/common/fmt/src/console.rs | 107 +++++ crates/common/fmt/src/lib.rs | 2 +- crates/common/src/contracts.rs | 2 +- crates/common/src/provider/mod.rs | 128 +++++- crates/common/src/provider/mpp/persist.rs | 410 ++++++++---------- crates/common/src/provider/mpp/session.rs | 38 +- crates/common/src/tempo/mod.rs | 11 + crates/config/spec/src/lib.rs | 3 +- crates/config/src/endpoints.rs | 155 ++++++- crates/config/src/lib.rs | 4 + crates/doc/src/parser/comment.rs | 6 + crates/evm/abi/src/Console.json | 2 +- crates/evm/abi/src/console.py | 8 +- crates/evm/core/src/fork/database.rs | 2 +- crates/evm/evm/src/inspectors/logs.rs | 4 +- crates/forge/Cargo.toml | 2 +- crates/forge/src/cmd/create.rs | 139 ++++-- crates/lint/src/linter.rs | 4 +- crates/lint/src/sol/gas/keccak.rs | 2 +- crates/macros/src/console_fmt.rs | 46 +- crates/script/Cargo.toml | 2 +- crates/script/src/simulate.rs | 86 ++-- crates/test-utils/src/script.rs | 16 +- npm/bun.lock | 15 +- npm/scripts/publish.mjs | 10 - npm/tsconfig.json | 1 - 73 files changed, 2364 insertions(+), 843 deletions(-) delete mode 100644 .circleci/ci_v1.yml delete mode 100644 .circleci/config.yml create mode 100644 counter/.gas-snapshot create mode 100644 counter/doc/doc-filelist.js create mode 100644 counter/doc/doc-script.js create mode 100644 counter/doc/doc-style.css create mode 100644 counter/remappings.txt create mode 100644 counter/soldeer.lock diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml deleted file mode 100644 index 82c6de5b42b73..0000000000000 --- a/.circleci/ci_v1.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2.1 - -jobs: - build-and-test: - docker: - - image: cimg/rust:1.89.0 - steps: - - checkout - - restore_cache: - keys: - - v1-cargo-{{ checksum "Cargo.lock" }} - - v1-cargo- - - run: - name: "Check formatting" - command: cargo fmt -- --check - - run: - name: "Run tests" - command: cargo test - - save_cache: - key: v1-cargo-{{ checksum "Cargo.lock" }} - paths: - - "~/.cargo/bin" - - "~/.cargo/registry/index" - - "~/.cargo/registry/cache" - - "~/.cargo/git/db" - - "target" - -workflows: - ci: - jobs: - - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index d5d401c51893c..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/jobs-steps/#jobs-overview & https://circleci.com/docs/configuration-reference/#jobs -jobs: - say-hello: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/executor-intro/ & https://circleci.com/docs/configuration-reference/#executor-job - docker: - # Specify the version you desire here - # See: https://circleci.com/developer/images/image/cimg/base - - image: cimg/base:current - - # Add steps to the job - # See: https://circleci.com/docs/jobs-steps/#steps-overview & https://circleci.com/docs/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -# Orchestrate jobs using workflows -# See: https://circleci.com/docs/workflows/ & https://circleci.com/docs/configuration-reference/#workflows -workflows: - say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - jobs: - - say-hello diff --git a/.github/scripts/tempo-mpp.sh b/.github/scripts/tempo-mpp.sh index 1a98b1ed9c494..0d34c4a3b5fa5 100755 --- a/.github/scripts/tempo-mpp.sh +++ b/.github/scripts/tempo-mpp.sh @@ -110,7 +110,7 @@ BLOCK2=$("$CAST" block-number --rpc-url "$RPC_MPP") AFTER2=$("$CAST" erc20 balance "$TOKEN" "$WALLET" --rpc-url "$RPC" | awk '{print $1}') SPENT2=$((BEFORE2 - AFTER2)) echo "Block: $BLOCK2" -echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/foundry/channels.json)" +echo "Spent: $SPENT2 units (should be 0 — channel reused from ~/.tempo/channels.db)" # 6. forge script via MPP echo "" @@ -129,9 +129,9 @@ contract MppCheck is Script { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" script "$TMPDIR/script/Mpp.s.sol" --rpc-url "$RPC_MPP" --root "$TMPDIR" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 7. forge test with vm.createSelectFork via MPP @@ -149,15 +149,15 @@ contract MppForkTest is Test { } } SOL -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$FORGE" test --match-test test_fork_via_mpp --root "$TMPDIR" -vvv -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 8. anvil fork via MPP echo "" echo "=== 8. anvil --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") "$ANVIL" --fork-url "$RPC_MPP" --port 8555 --silent & ANVIL_PID=$! for _ in $(seq 1 30); do @@ -167,15 +167,15 @@ done echo "chain-id: $("$CAST" chain-id --rpc-url http://localhost:8555)" kill $ANVIL_PID 2>/dev/null wait $ANVIL_PID 2>/dev/null -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" # 9. chisel fork via MPP echo "" echo "=== 9. chisel --fork-url (via MPP) ===" -VCNT_BEFORE=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_BEFORE=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo 'block.number' | "$CHISEL" --fork-url "$RPC_MPP" 2>&1 | grep -E "Decimal|Type" -VCNT_AFTER=$(grep cumulative_amount ~/.tempo/foundry/channels.json | awk -F'"' '{print $4}') +VCNT_AFTER=$(sqlite3 ~/.tempo/channels.db "SELECT cumulative_amount FROM channels LIMIT 1") echo "Vouchers paid: +$((VCNT_AFTER - VCNT_BEFORE)) ($((( VCNT_AFTER - VCNT_BEFORE ) / 1000)) RPC calls via MPP)" echo "" diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index 5a2330e7d5d62..53b7d1ed0ac7c 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -4,7 +4,6 @@ on: push: tags: ["*"] branches: - - "main" pull_request: branches: ["**"] @@ -36,7 +35,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 96af1f4ab9a1e..8465874854551 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -61,7 +61,7 @@ jobs: printf '%s\n' "$GITHUB_WORKSPACE/.foundry/bin" >> "$GITHUB_PATH" - name: Build benchmark binary - run: cargo build --release --bin foundry-bench + run: cargo build --locked --release --bin foundry-bench - name: Setup Node.js uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c332240fea036..53d009de4c442 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo test --workspace --doc + - run: cargo test --workspace --doc --locked typos: runs-on: depot-ubuntu-latest @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: crate-ci/typos@02ea592e44b3a53c302f697cddca7641cd051c3d # v1.45.0 + - uses: crate-ci/typos@cf5f1c29a8ac336af8568821ec41919923b05a83 # v1.45.1 shellcheck: runs-on: depot-ubuntu-latest @@ -92,7 +92,7 @@ jobs: - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo clippy --workspace --all-targets --all-features + - run: cargo clippy --workspace --all-targets --all-features --locked env: RUSTFLAGS: -Dwarnings diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index 781d63759045e..b2eb6261efd63 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -34,7 +34,7 @@ jobs: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - - run: cargo hack check + - run: cargo hack check --locked issue: name: Open an issue diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5a2330e7d5d62..53b7d1ed0ac7c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,7 +4,6 @@ on: push: tags: ["*"] branches: - - "main" pull_request: branches: ["**"] @@ -36,7 +35,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a8a3dcbb8c7d3..288d9d127592f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: Build documentation - run: cargo doc --workspace --all-features --no-deps --document-private-items + run: cargo doc --workspace --all-features --no-deps --document-private-items --locked env: RUSTDOCFLAGS: --cfg docsrs -D warnings --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options - name: Setup Pages diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 0a18c99a41f82..f6e068eeec62a 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: DeterminateSystems/update-flake-lock@e80a657d7603606be0c69b117cfdc240f1e6af88 # main + - uses: DeterminateSystems/update-flake-lock@ff43f160ef7014ae1a1fd85699fb6a44f436135b # main with: pr-title: "Update flake.lock" pr-labels: | diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 007ef5f4286aa..938e85c9330be 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -31,6 +31,7 @@ jobs: contents: read actions: read id-token: write + environment: release name: ${{ matrix.tool }}-${{ matrix.os }}-${{ matrix.arch }} runs-on: ubuntu-latest strategy: @@ -112,6 +113,22 @@ jobs: with: persist-credentials: false + - name: Validate Trusted Artifact Source + run: | + set -euo pipefail + + if [[ "${{ github.event_name }}" == "workflow_run" ]]; then + [[ "${{ github.event.workflow_run.conclusion }}" == "success" ]] || { echo "ERROR: Upstream workflow_run did not succeed."; exit 1; } + [[ "${{ github.event.workflow_run.head_repository.full_name }}" == "${{ github.repository }}" ]] || { echo "ERROR: Upstream run repository mismatch."; exit 1; } + [[ "${{ github.event.workflow_run.event }}" != "pull_request" ]] || { echo "ERROR: Refusing artifacts from pull_request-triggered upstream runs."; exit 1; } + elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + [[ -n "${{ inputs.run_id }}" ]] || { echo "ERROR: inputs.run_id is required for workflow_dispatch."; exit 1; } + [[ "${{ inputs.run_id }}" =~ ^[0-9]+$ ]] || { echo "ERROR: inputs.run_id must be numeric."; exit 1; } + else + echo "ERROR: Unsupported event: ${{ github.event_name }}" + exit 1 + fi + - name: Set Isolated Artifact Directory id: paths run: | @@ -151,9 +168,6 @@ jobs: id: release-version working-directory: ./npm env: - PROVENANCE: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} run: | @@ -224,8 +238,6 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ steps.release-version.outputs.RELEASE_VERSION }} RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | set -euo pipefail @@ -282,12 +294,11 @@ jobs: contents: read actions: read id-token: write + environment: release needs: publish-arch name: Publish Meta Package runs-on: ubuntu-latest env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} RELEASE_VERSION: ${{ needs.publish-arch.outputs.RELEASE_VERSION }} steps: - name: Checkout @@ -323,6 +334,4 @@ jobs: PROVENANCE: true VERSION_NAME: ${{ env.RELEASE_VERSION }} RELEASE_VERSION: ${{ env.RELEASE_VERSION }} - NPM_TOKEN: ${{ env.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ env.NODE_AUTH_TOKEN }} NPM_REGISTRY_URL: ${{ env.NPM_REGISTRY_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ad324e7ef9c1..52b33787de3f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -179,7 +179,7 @@ jobs: shell: bash run: | set -eo pipefail - flags=(--target $TARGET --profile $RUST_PROFILE --bins + flags=(--locked --target $TARGET --profile $RUST_PROFILE --bins --no-default-features --features "$RUST_FEATURES") # `jemalloc` is not fully supported on MSVC or aarch64 Linux. @@ -279,7 +279,7 @@ jobs: # Creates the release for this specific version - name: Create release - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: ${{ needs.prepare.outputs.release_name }} tag_name: ${{ needs.prepare.outputs.tag_name }} @@ -294,7 +294,7 @@ jobs: # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release if: ${{ env.IS_NIGHTLY == 'true' }} - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: name: "Nightly" tag_name: "nightly" diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 0ba82305f82b2..fcfbbbbec7948 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -32,9 +32,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v6 - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: # Upload entire repository path: '.' diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index af4415b1e8ba1..c0e4fcb5f3263 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -50,7 +50,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --no-fail-fast + run: cargo nextest run --locked --profile flaky --no-fail-fast # If any of the jobs fail, this will create a normal-priority issue to signal so. issue: diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index afd91d3d72c05..37c3edc9b916a 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -54,7 +54,7 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run --profile flaky --features=isolate-by-default --no-fail-fast + run: cargo nextest run --locked --profile flaky --features=isolate-by-default --no-fail-fast # If nextest fails, create a high-priority issue for isolation failures. issue-isolate: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9ccb850dbc5c..7c79cd266f152 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -97,7 +97,7 @@ jobs: run: pip --version && pip install vyper==0.4.3 - name: Foundry test cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.foundry/cache @@ -121,4 +121,4 @@ jobs: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_API_KEY }} ARBITRUM_RPC: ${{ secrets.ARBITRUM_RPC }} ETH_SEPOLIA_RPC: ${{ secrets.ETH_SEPOLIA_RPC }} - run: cargo nextest run ${{ matrix.flags }} + run: cargo nextest run --locked ${{ matrix.flags }} diff --git a/Cargo.lock b/Cargo.lock index d15f7c092e642..b3741acb52d7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,14 +84,14 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-core", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-network", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -116,14 +116,14 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dbe4e5e9107bf6854e7550b666ca654ff2027eabf8153913e2e31ac4b089779" +checksum = "ae8c24c95e90c1608c2d91cff1b451d796474168d3310ccc8b7cd12502ca8169" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-trie", "alloy-tx-macros", "auto_impl", @@ -143,23 +143,23 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88fc7bbfb98cf5605a35aadf0ba43a7d9f1608d6f220d05e4fbd5144d3b0b625" +checksum = "7d211ad0ef468a70a7a829e49683ff59ad25f02b4ab3764344c4c2663329a52c" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-contract" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c16fa30b623e40a5b216da00f3b61870f5cbe863b59816ac1ecc2489515a40" +checksum = "c59d55233ac14aa7fa6bcdcad45ba305e90c556065e0947cd9f243c4469e7c2d" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -238,12 +238,12 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9252074d8b7028bae6844827f51ed8f8a036367604dce9d30d16cedfcc53577" +checksum = "250ba1168b8a049185a68c4dfa7f2a6a4046bd26fcc8c68632caeb216a5e12dc" dependencies = [ "alloy-primitives", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", "serde_json", ] @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb4919fa34b268842f434bfafa9c09136ab7b1a87ce0dd40a61befa35b5408c" +checksum = "ae69eaa5096b47ffe97e6a5d6bde7e7fa2dec106af22a9315621d11039c3de3c" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -310,7 +310,7 @@ dependencies = [ "alloy-eip7928", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "auto_impl", "borsh", "c-kzg", @@ -325,9 +325,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cbf60d85481b12fcc06e29fbe14584fdc5743a34ba7719037f210decd020761" +checksum = "34a8c1330ad33c95b5958573bca9a1ad0b419a51d76bb4c521556fbba8539b8d" dependencies = [ "alloy-contract", "alloy-primitives", @@ -344,7 +344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f107d0e588e5d25fcf2db216390445d5804b875a22a419407ad0389b925bb4d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-hardforks", "alloy-primitives", "alloy-rpc-types-engine", @@ -359,13 +359,13 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e111e22c1a2133e9ebfd9051ea0eaf63559594d2f50d43cbc6762fbb95fc3c2" +checksum = "39789db0b3f3bbef0e6549c87bc6842b73886ebabee1405b6941685b1cc34083" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-trie", "borsh", "serde", @@ -400,9 +400,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b6af6f374c1eeef8ab8dc26232cd440db167322a4207a3debd3d1ee565ca47" +checksum = "662b525af73e86b2167dae923261c8edf440ba7e1426b30a8b993177bc214c02" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -415,19 +415,19 @@ dependencies = [ [[package]] name = "alloy-network" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a3f5a7f3678b71d33fcc45b714fab8928dbc647d5aff2145e72032d5c849bb" +checksum = "c657c2d9751d3c7d94990554b231e5372c3c2e4bad842806280b6151a0d6a05d" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-any", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-sol-types", "async-trait", @@ -441,14 +441,14 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb50dc1fb0e0b2c8748d5bee1aa7acdd18f9e036311bc93a71d97be624030317" +checksum = "59e7c4bb0ebbd6d7406d2808968f43c0d5186c69c5e58cedcbee7380f4cd1fcf" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] @@ -458,7 +458,7 @@ version = "0.30.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -513,13 +513,13 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ba5468f78c8893be2d68a7f2fda61753336e5653f006af19781001b5f99e6c" +checksum = "c4fea0fc2628cdbc851aaa333124f9d8ab9f567ab8d4c20202819db13aa1a534" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-json-rpc", "alloy-network", "alloy-network-primitives", @@ -559,9 +559,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffcefb5d3391a320eadb95d398e4135f8cc35c7bf29a6bdb357eadcfc5ee5638" +checksum = "edc7b42e514613c717887dc77bb58d35e845557ebd63a18c3f92a77094e4891f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -603,9 +603,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222fd4efff0fb9a25184684742c44fe9fa9a16c4ab5bf97583e71c86598ef8f0" +checksum = "d5ee7b51752c68fb95f21705e402700750e692b1d21ccc294ac48fadc8655d53" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -629,9 +629,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974df1e56405c27cb8242381f45d8b212ba9df5006046ccf704764a2a4634366" +checksum = "8fa76988f54105ad4398828e8aaf1a39b3f07f91fb79091529056689514ee8c2" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -640,44 +640,44 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-rpc-types-anvil" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06bc10b0dca4f5bfc3cd30ed46eab5d651b5bb2cd300d683bdcdf5d2bfe6e82c" +checksum = "3d276bea4e92e4991269d31b9abd3e722eed2565b82036478a4416adb8dd4992" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-rpc-types-any" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949c0f16a94ae33cdb1139b8dbf9e34d7f26ebfe97962e2a4d620b5f65f48fe4" +checksum = "1f1a9a3bda9be7f6515316eb792710532411878bbfc88934973f4b371376b00d" dependencies = [ "alloy-consensus-any", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", "serde_json", ] [[package]] name = "alloy-rpc-types-beacon" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8f7fa8ca056bb797a368aeed329e6ace6b62ee4271432ac36ab8ae87a5e60d" +checksum = "caf5d68ddca890854fb78291cbde06115473ded00b2337d0f815e92c0c1f8003" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rpc-types-engine", "derive_more", @@ -689,9 +689,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301249e3c9e43661cfd7ebbb4746a00af6ce1ef58b5c968451882cd60438417d" +checksum = "ea21739e232c221779741eba7e7b9bc19ad8ff777b72736647ae519f5c9f6f33" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -702,15 +702,15 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e59bc947935732cae5b072753e5e034c0b70a8b031c2839f45e2659ba07df9ae" +checksum = "5f05338cfb4ee5508ff76f01c88142cab8a4579db74b7d9432936c26e4f11374" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -722,17 +722,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc280a41931bd419af86e9e859dd9726b73313aaa2e479b33c0e344f4b892ddb" +checksum = "dda4ece0050154ab278241aeffade58916b04f38254832e8cb6e4671c6e72ed2" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-sol-types", "itertools 0.14.0", "serde", @@ -743,13 +743,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede0458c51bef23620aa6bd01a0b4f608be7bcb61d98e91b8530208ae545f3c2" +checksum = "f5905ac3663b0859d67b82d912acce20887d20682a0cadde79c8a763b133a515" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", "serde_json", "thiserror 2.0.18", @@ -757,13 +757,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f4df183248b57f3e0b99054b1b6786769d3fdff6d01a702234068140c8ba76" +checksum = "f7fbf71892d4df9cae8d35dc96f15d522384bb93806205465e2c8c012b7f0a34" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "serde", ] @@ -780,9 +780,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4848831ff994c88b1c32b7df9c4c1c3eedea4b535bde5eb3c421ef0bdc5ac052" +checksum = "beaa5c581a67e2743d95b4849eb9cfeb90866429cdaa6d8f6b75eb988b2d0cd9" dependencies = [ "alloy-primitives", "serde", @@ -791,9 +791,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b8ad9890b212e224291024b1aecfeef72127d27a2f6eebc5e347c40275c4bf" +checksum = "c5da9ae50f9b48d7b4e2e5cde87175257be7e5e56909a7794720597c1d9806f6" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -808,9 +808,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a57d1e72b1f9b11e5e71ebdab0569cb02277a462bbea6793fcaebfcd794ae9" +checksum = "a19d1985804e9a46d3b1b4f0654a68210b44007ffdaddd0e0a35d2b8db6cc1f0" dependencies = [ "alloy-consensus", "alloy-network", @@ -827,9 +827,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b27f20b5298b76a5a3b7cdbe6bdb184ab1ebd6e120e00dad748867673f5c90" +checksum = "34ce972f2ade53477e8e9336773a417f731fc7c02f41b9cd3b8a2a273e06363e" dependencies = [ "alloy-consensus", "alloy-network", @@ -845,9 +845,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c7acc40ffbfd37d4113eb619863099f3235d78d044006a1eecb94d8b0b2f1a" +checksum = "943c0105e0294b34cd06417129fadc591aed464d06f0614a7e998e585d27fbb1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -865,9 +865,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67d2372aada343130d41e249b59a3cef29b1678dcd3fd80f1c2c4d6b5318f2" +checksum = "49b794002d57fd2f71b4c87298a41ca24dfc0f2cf6630d95106a477e451747ba" dependencies = [ "alloy-consensus", "alloy-network", @@ -885,9 +885,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a09a865ae9e1f05478429ef0d935b16467f35c6e0b02cb10f23f66a3b33fc3" +checksum = "efe910cd3f56f7e4b26b8b7330b11c11c81286eaa8aa9fa6157e767a95e0f310" dependencies = [ "alloy-consensus", "alloy-network", @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "alloy-signer-turnkey" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bb8218544ab635281f1be180a1cfd9b5d549db686faa7e85b3b2c10969819e" +checksum = "2296519a2342608afbd9baddd9d8df9f487604c3f1c2703b27bdd259f6018ea3" dependencies = [ "alloy-consensus", "alloy-network", @@ -991,9 +991,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b7b755e64ae6b5de0d762ed2c780e072167ea5e542076a559e00314352a0bf" +checksum = "19dec9bfb59647254afdecbb5ddcddd7ba02edcd48ffa40510bddfbed0be1634" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -1014,9 +1014,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a29980e69119444ed26b75e7ee5bed2043870f904a64318297e55800db686564" +checksum = "2035f3c4d6bee20624da2dcf765d469b292398e48d766ffade61b0fcf8b4d45d" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -1030,9 +1030,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b27802653330740c88c28394cdaf1d55190b15b48ef9b99a946f37c9cdb19fa" +checksum = "cfad7aa9206fcb831ae401b6a1c893a402b8eed74f9c8ffbb7a7323afb0d9a4c" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -1050,9 +1050,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b71dc951db66795cfb52eef835f64cf15163bc93b656e061b457ce5ebff370" +checksum = "a5aa8ff49386df3e008b73c7fb0a5479410e8493fdb86a8b916877a16e8aead9" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -1085,9 +1085,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8228b9236479ff16b03041b64b86c2bd4e53da1caa45d59b5868cd1571131e" +checksum = "3520337f3d3d063a7fe20f47aaa62d695e3dc0372b34f601560dee24e76988b9" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -1229,7 +1229,7 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-genesis", "alloy-network", @@ -1241,7 +1241,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-beacon", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -1302,12 +1302,12 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eip5792", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "bytes", "foundry-common", "foundry-evm", @@ -2558,7 +2558,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -2730,7 +2730,7 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-ens", "alloy-evm", "alloy-hardforks", @@ -2742,7 +2742,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-beacon", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -3021,7 +3021,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3174,7 +3174,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3958,7 +3958,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4263,7 +4263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4376,6 +4376,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast-float2" version = "0.2.3" @@ -4693,7 +4705,7 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-json-abi", "alloy-network", @@ -4916,7 +4928,7 @@ version = "1.6.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-ens", "alloy-json-abi", "alloy-network", @@ -4978,7 +4990,7 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -5009,6 +5021,7 @@ dependencies = [ "foundry-common-fmt", "foundry-compilers", "foundry-config", + "foundry-wallets", "itertools 0.14.0", "jiff", "mpp", @@ -5046,8 +5059,9 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "chrono", + "comfy-table", "eyre", "foundry-macros", "op-alloy-consensus", @@ -5285,7 +5299,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-sol-types", "anvil", "auto_impl", @@ -5381,7 +5395,7 @@ name = "foundry-evm-networks" version = "1.6.0" dependencies = [ "alloy-chains", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -5485,7 +5499,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer", "derive_more", "op-alloy-consensus", @@ -5544,7 +5558,7 @@ dependencies = [ [[package]] name = "foundry-wallets" version = "0.1.0" -source = "git+https://github.com/foundry-rs/foundry-core?rev=7f401c1397af90a0a94ef7424a48bbf3dc0248cc#7f401c1397af90a0a94ef7424a48bbf3dc0248cc" +source = "git+https://github.com/foundry-rs/foundry-core?rev=d6c92dfcb41be26fbc1de21221a5bfc6bdd93245#d6c92dfcb41be26fbc1de21221a5bfc6bdd93245" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -5568,6 +5582,7 @@ dependencies = [ "eth-keystore", "eyre", "rpassword", + "rusqlite", "serde", "serde_json", "tempo-primitives", @@ -5970,6 +5985,15 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -6565,7 +6589,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6920,6 +6944,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -7480,7 +7515,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7724,12 +7759,12 @@ version = "0.24.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "bytes", "derive_more", "reth-codecs 0.2.0", @@ -7778,12 +7813,12 @@ version = "0.24.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "derive_more", "op-alloy-consensus", "reth-rpc-traits 0.2.0", @@ -7798,11 +7833,11 @@ version = "0.24.0" source = "git+https://github.com/foundry-rs/optimism?branch=develop#0dc3c00bd1b3b2976f53503ba92fe84c602deca6" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -8464,7 +8499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8633,7 +8668,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -9033,7 +9068,7 @@ source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed4 dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-genesis", "alloy-primitives", @@ -9053,7 +9088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a29541038ab108b2e9d527c66b565e717e252e4eef6675377fc21f9ba587f792" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -9072,7 +9107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a79b3247ae4fbb1d4d35ce83a11fc596428a4c6ea836c98a75a55340192578a4" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -9125,7 +9160,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9161,7 +9196,7 @@ name = "reth-db-models" version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "bytes", "modular-bitfield", @@ -9176,7 +9211,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9205,7 +9240,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rpc-types-eth", "reth-codecs 0.3.0", @@ -9219,7 +9254,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-primitives", "auto_impl", @@ -9241,7 +9276,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-primitives", "alloy-rpc-types-engine", @@ -9274,7 +9309,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-primitives", "alloy-rlp", @@ -9307,7 +9342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96ffdb2ce0cdcd814d39428dc1e9b660c85d85e0b75eb465a1ed0943a48c7bd" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9332,7 +9367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc759fd87c3f65440e5d3bfa3107fe8a13a61a6807cd485c62c49d63c7bf6717" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9465,7 +9500,7 @@ version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -9487,7 +9522,7 @@ name = "reth-storage-errors" version = "2.0.0" source = "git+https://github.com/paradigmxyz/reth?rev=0b33057#0b33057414c8815ed499e0aa837ae52d4d366150" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", "derive_more", @@ -9509,7 +9544,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-trie", "arrayvec", "bytes", @@ -9930,6 +9965,20 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rusqlite" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b" +dependencies = [ + "bitflags 2.11.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -10000,7 +10049,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10059,7 +10108,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10070,9 +10119,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -10830,7 +10879,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.12.1", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -10865,7 +10914,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.12.1", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -11289,7 +11338,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11299,12 +11348,12 @@ source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe0 dependencies = [ "alloy-consensus", "alloy-contract", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "alloy-signer-local", "alloy-sol-types", "alloy-transport", @@ -11324,7 +11373,7 @@ name = "tempo-chainspec" version = "1.5.3" source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-evm", "alloy-genesis", "alloy-hardforks", @@ -11429,12 +11478,12 @@ version = "1.6.0" source = "git+https://github.com/tempoxyz/tempo?rev=bb08bb9#bb08bb905b6ee13fafe046aa9531aea2cdf60651" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.0", + "alloy-eips 2.0.1", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.0", + "alloy-serde 2.0.1", "aws-lc-rs", "base64 0.22.1", "derive_more", @@ -11494,7 +11543,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11504,7 +11553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -12721,7 +12770,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -12871,7 +12920,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 70d6eba6a6aec..7388b62a8acfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -357,36 +357,35 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features ] } ## foundry-core -foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "7f401c1397af90a0a94ef7424a48bbf3dc0248cc", default-features = false } +foundry-wallets = { git = "https://github.com/foundry-rs/foundry-core", rev = "d6c92dfcb41be26fbc1de21221a5bfc6bdd93245", default-features = false } ## alloy -alloy-consensus = { version = "2.0.0", default-features = false } -alloy-contract = { version = "2.0.0", default-features = false } -alloy-eips = { version = "2.0.0", default-features = false } -alloy-eip5792 = { version = "2.0.0", default-features = false } -alloy-ens = { version = "2.0.0", default-features = false } -alloy-genesis = { version = "2.0.0", default-features = false } -alloy-json-rpc = { version = "2.0.0", default-features = false } -alloy-network = { version = "2.0.0", default-features = false } -alloy-provider = { version = "2.0.0", default-features = false } -alloy-pubsub = { version = "2.0.0", default-features = false } -alloy-rpc-client = { version = "2.0.0", default-features = false } -alloy-rpc-types = { version = "2.0.0", default-features = true } -alloy-rpc-types-beacon = { version = "2.0.0", default-features = true } -alloy-rpc-types-engine = { version = "2.0.0", default-features = false } -alloy-rpc-types-eth = { version = "2.0.0", default-features = false } -alloy-serde = { version = "2.0.0", default-features = false } -alloy-signer = { version = "2.0.0", default-features = false } -alloy-signer-aws = { version = "2.0.0", default-features = false } -alloy-signer-gcp = { version = "2.0.0", default-features = false } -alloy-signer-ledger = { version = "2.0.0", default-features = false } -alloy-signer-local = { version = "2.0.0", default-features = false } -alloy-signer-trezor = { version = "2.0.0", default-features = false } -alloy-signer-turnkey = { version = "2.0.0", default-features = false } -alloy-transport = { version = "2.0.0", default-features = false } -alloy-transport-http = { version = "2.0.0", default-features = false } -alloy-transport-ipc = { version = "2.0.0", default-features = false } -alloy-transport-ws = { version = "2.0.0", default-features = false } +alloy-consensus = { version = "2.0.1", default-features = false } +alloy-contract = { version = "2.0.1", default-features = false } +alloy-eips = { version = "2.0.1", default-features = false } +alloy-eip5792 = { version = "2.0.1", default-features = false } +alloy-ens = { version = "2.0.1", default-features = false } +alloy-genesis = { version = "2.0.1", default-features = false } +alloy-json-rpc = { version = "2.0.1", default-features = false } +alloy-network = { version = "2.0.1", default-features = false } +alloy-provider = { version = "2.0.1", default-features = false } +alloy-pubsub = { version = "2.0.1", default-features = false } +alloy-rpc-client = { version = "2.0.1", default-features = false } +alloy-rpc-types = { version = "2.0.1", default-features = true } +alloy-rpc-types-beacon = { version = "2.0.1", default-features = true } +alloy-rpc-types-engine = { version = "2.0.1", default-features = false } +alloy-rpc-types-eth = { version = "2.0.1", default-features = false } +alloy-serde = { version = "2.0.1", default-features = false } +alloy-signer = { version = "2.0.1", default-features = false } +alloy-signer-aws = { version = "2.0.1", default-features = false } +alloy-signer-gcp = { version = "2.0.1", default-features = false } +alloy-signer-ledger = { version = "2.0.1", default-features = false } +alloy-signer-local = { version = "2.0.1", default-features = false } +alloy-signer-trezor = { version = "2.0.1", default-features = false } +alloy-signer-turnkey = { version = "2.0.1", default-features = false } +alloy-transport = { version = "2.0.1", default-features = false } +alloy-transport-ipc = { version = "2.0.1", default-features = false } +alloy-transport-ws = { version = "2.0.1", default-features = false } alloy-hardforks = { version = "0.4.7", default-features = false } alloy-op-hardforks = { version = "0.4.7", default-features = false } @@ -622,3 +621,6 @@ solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/sola solar-interface = { package = "solar-interface", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-ast = { package = "solar-ast", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } solar-sema = { package = "solar-sema", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } + +[workspace.metadata.cargo-shear] +ignored = ["idna_adapter", "cast", "chisel", "forge", "alloy-contract"] diff --git a/Makefile b/Makefile index fe768f19abf43..1bf20f4eeebd1 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ help: ## Display this help. .PHONY: build build: ## Build the project. - cargo build --features "$(FEATURES)" --profile "$(PROFILE)" + cargo build --locked --features "$(FEATURES)" --profile "$(PROFILE)" .PHONY: build-docker build-docker: ## Build the docker image. @@ -47,17 +47,17 @@ build-docker: ## Build the docker image. ## is unstable (https://github.com/taiki-e/cargo-llvm-cov/issues/2). .PHONY: test-coverage test-coverage: - cargo +nightly llvm-cov --no-report nextest -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ - cargo +nightly llvm-cov --no-report --doc && \ + cargo +nightly llvm-cov --no-report nextest --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' && \ + cargo +nightly llvm-cov --no-report --doc --locked && \ cargo +nightly llvm-cov report --doctests --open .PHONY: test-unit test-unit: ## Run unit tests. - cargo nextest run -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' + cargo nextest run --workspace --locked -E 'kind(test) & !test(/\b(issue|ext_integration|flaky_)/)' .PHONY: test-doc test-doc: ## Run doc tests. - cargo test --doc --workspace + cargo test --doc --workspace --locked .PHONY: test test: ## Run all tests. @@ -77,6 +77,7 @@ lint-clippy: ## Run clippy on the codebase. --workspace \ --all-targets \ --all-features \ + --locked \ -- -D warnings .PHONY: lint-clippy-fix @@ -85,6 +86,7 @@ lint-clippy-fix: ## Run clippy on the codebase and fix warnings. --workspace \ --all-targets \ --all-features \ + --locked \ --fix \ --allow-dirty \ --allow-staged \ @@ -104,21 +106,46 @@ lint: ## Run all linters. $(MAKE) lint-clippy && \ $(MAKE) lint-typos +##@ Documentation + +.PHONY: doc +doc: ## Build the documentation. + RUSTDOCFLAGS="--cfg docsrs -D warnings -Zunstable-options --show-type-layout --generate-link-to-definition" \ + cargo +nightly doc \ + --workspace \ + --all-features \ + --document-private-items \ + --no-deps \ + --locked + ##@ Other +.PHONY: lock +lock: ## Update the Cargo.lock file with the current dependencies. + cargo fetch + .PHONY: clean clean: ## Clean the project. cargo clean .PHONY: deny deny: ## Perform a `cargo` deny check. - cargo deny --all-features check all + cargo deny --locked --all-features check all + +.PHONY: check +check: ## Run a feature check on all crates and binaries. + cargo hack check --locked --feature-powerset --depth 1 + +.PHONY: shear +shear: ## Run `cargo shear` to check for unused dependencies. + cargo shear --locked .PHONY: pr pr: ## Run all checks and tests. $(MAKE) deny && \ $(MAKE) lint && \ - $(MAKE) test + $(MAKE) test && \ + $(MAKE) doc # dprint formatting commands .PHONY: dprint-fmt diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ab3bec614e3cd..516d73531007a 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -127,6 +127,7 @@ impl BenchmarkProject { let root = root_path.to_str().unwrap(); // Remove all files in the directory + let root_path = root_path.canonicalize()?; for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); diff --git a/counter/.gas-snapshot b/counter/.gas-snapshot new file mode 100644 index 0000000000000..ef525c09384e6 --- /dev/null +++ b/counter/.gas-snapshot @@ -0,0 +1,2 @@ +CounterTest:testFuzz_SetNumber(uint256) (runs: 256, μ: 30177, ~: 32354) +CounterTest:test_Increment() (gas: 31851) \ No newline at end of file diff --git a/counter/.gitignore b/counter/.gitignore index 85198aaa55b84..052b88bb6516b 100644 --- a/counter/.gitignore +++ b/counter/.gitignore @@ -12,3 +12,7 @@ docs/ # Dotenv file .env + + +# Soldeer +/dependencies diff --git a/counter/doc/doc-filelist.js b/counter/doc/doc-filelist.js new file mode 100644 index 0000000000000..c2a398ff94c23 --- /dev/null +++ b/counter/doc/doc-filelist.js @@ -0,0 +1 @@ +var tree={}; \ No newline at end of file diff --git a/counter/doc/doc-script.js b/counter/doc/doc-script.js new file mode 100644 index 0000000000000..62eeda3efc308 --- /dev/null +++ b/counter/doc/doc-script.js @@ -0,0 +1,228 @@ +// # res/script.js +// +// This is the script file that gets copied into the output. It mainly manages the display +// of the folder tree. The idea of this script file is to be minimal and standalone. So +// that means no jQuery. + +// Use localStorage to store data about the tree's state: whether or not +// the tree is visible and which directories are expanded. Unless the state +let sidebarVisible = (window.localStorage && window.localStorage.docker_showSidebar) ? + window.localStorage.docker_showSidebar == 'yes' : + defaultSidebar; + +/** + * ## makeTree + * + * Consructs the folder tree view + * + * @param {object} treeData Folder structure as in [queueFile](../src/docker.js.html#docker.prototype.queuefile) + * @param {string} root Path from current file to root (ie `'../../'` etc.) + * @param {string} filename The current file name + */ +function makeTree(treeData, root, filename) { + var treeNode = document.getElementById('tree'); + var treeHandle = document.getElementById('sidebar-toggle'); + treeHandle.addEventListener('click', toggleTree, false); + + // Build the html and add it to the container. + treeNode.innerHTML = nodeHtml('', treeData, '', root); + + // Root folder (whole tree) should always be open + treeNode.childNodes[0].className += ' open'; + + // Attach click event handler + treeNode.addEventListener('click', nodeClicked, false); + + if (sidebarVisible) document.body.className += ' sidebar'; + + // Restore scroll position from localStorage if set. And attach scroll handler + if (window.localStorage && window.localStorage.docker_treeScroll) treeNode.scrollTop = window.localStorage.docker_treeScroll; + treeNode.onscroll = treeScrolled; + + // Only set a class to allow CSS transitions after the tree state has been painted + setTimeout(function() { document.body.className += ' slidey'; }, 100); +} + +/** + * ## treeScrolled + * + * Called when the tree is scrolled. Stores the scroll position in localStorage + * so it can be restored on the next pageview. + */ +function treeScrolled() { + var tree = document.getElementById('tree'); + if (window.localStorage) window.localStorage.docker_treeScroll = tree.scrollTop; +} + +/** + * ## nodeClicked + * + * Called when a directory is clicked. Toggles open state of the directory + * + * @param {Event} e The click event + */ +function nodeClicked(e) { + // Find the target + var t = e.target; + + // If the click target is actually a file (rather than a directory), ignore it + if (t.tagName.toLowerCase() !== 'div' || t.className === 'children') return; + + // Recurse upwards until we find the actual directory node + while (t && t.className.substring(0, 3) != 'dir') t = t.parentNode; + + // If we're at the root node, then do nothing (we don't allow collapsing of the whole tree) + if (!t || t.parentNode.id == 'tree') return; + + // Find the path and toggle the state, saving the state in the localStorage variable + var path = t.getAttribute('rel'); + if (t.className.indexOf('open') !== -1) { + t.className = t.className.replace(/\s*open/g, ''); + if (window.localStorage) window.localStorage.removeItem('docker_openPath:' + path); + } else { + t.className += ' open'; + if (window.localStorage) window.localStorage['docker_openPath:' + path] = 'yes'; + } +} + + +/** + * ## nodeHtml + * + * Constructs the markup for a directory in the tree + * + * @param {string} nodename The node name. + * @param {object} node Node object of same format as whole tree. + * @param {string} path The path form the base to this node + * @param {string} root Relative path from current page to root + */ +function nodeHtml(nodename, node, path, root) { + // Firstly, figure out whether or not the directory is expanded from localStorage + var isOpen = window.localStorage && window.localStorage['docker_openPath:' + path] == 'yes'; + var out = '
'; + out += '
' + nodename + '
'; + out += '
'; + + // Loop through all child directories first + if (node.dirs) { + var dirs = []; + for (var i in node.dirs) { + if (node.dirs.hasOwnProperty(i)) dirs.push({ name: i, html: nodeHtml(i, node.dirs[i], path + i + '/', root) }); + } + // Have to store them in an array first and then sort them alphabetically here + dirs.sort(function(a, b) { return (a.name > b.name) ? 1 : (a.name == b.name) ? 0 : -1; }); + + for (var k = 0; k < dirs.length; k += 1) out += dirs[k].html; + } + + // Now loop through all the child files alphabetically + if (node.files) { + node.files.sort(); + for (var j = 0; j < node.files.length; j += 1) { + out += '' + node.files[j] + ''; + } + } + + // Close things off + out += '
'; + + return out; +} + +/** + * ## toggleTree + * + * Toggles the visibility of the folder tree + */ +function toggleTree() { + // Do the actual toggling by modifying the class on the body element. That way we can get some nice CSS transitions going. + if (sidebarVisible) { + document.body.className = document.body.className.replace(/\s*sidebar/g, ''); + sidebarVisible = false; + } else { + document.body.className += ' sidebar'; + sidebarVisible = true; + } + if (window.localStorage) { + if (sidebarVisible) { + window.localStorage.docker_showSidebar = 'yes'; + } else { + window.localStorage.docker_showSidebar = 'no'; + } + } +} + +/** + * ## wireUpTabs + * + * Wires up events on the sidebar tabe + */ +function wireUpTabs() { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Each tab has a class corresponding of the id of its tab pane + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + children[i].addEventListener('click', function(c) { + return function() { switchTab(c); }; + }(children[i].className)); + } +} + +/** + * ## switchTab + * + * Switches tabs in the sidebar + * + * @param {string} tab The ID of the tab to switch to + */ +function switchTab(tab) { + var tabEl = document.getElementById('sidebar_switch'); + var children = tabEl.childNodes; + + // Easiest way to go through tabs without any kind of selector is just to look at the tab bar + for (var i = 0, l = children.length; i < l; i += 1) { + // Ignore text nodes + if (children[i].nodeType !== 1) continue; + + // Figure out what tab pane this tab button corresponts to + var t = children[i].className.replace(/\s.*$/, ''); + if (t === tab) { + // Show the tab pane, select the tab button + document.getElementById(t).style.display = 'block'; + if (children[i].className.indexOf('selected') === -1) children[i].className += ' selected'; + } else { + // Hide the tab pane, deselect the tab button + document.getElementById(t).style.display = 'none'; + children[i].className = children[i].className.replace(/\sselected/, ''); + } + } + + // Store the last open tab in localStorage + if (window.localStorage) window.localStorage.docker_sidebarTab = tab; +} + +/** + * ## window.onload + * + * When the document is ready, make the sidebar and all that jazz + */ +(function(init) { + if (window.addEventListener) { + window.addEventListener('DOMContentLoaded', init); + } else { // IE8 and below + window.onload = init; + } +}(function() { + makeTree(tree, relativeDir, thisFile); + wireUpTabs(); + + // Switch to the last viewed sidebar tab if stored, otherwise default to folder tree + if (window.localStorage && window.localStorage.docker_sidebarTab) { + switchTab(window.localStorage.docker_sidebarTab); + } else { + switchTab('tree'); + } +})); diff --git a/counter/doc/doc-style.css b/counter/doc/doc-style.css new file mode 100644 index 0000000000000..2019a1b7659c6 --- /dev/null +++ b/counter/doc/doc-style.css @@ -0,0 +1,403 @@ +/* + +Original highlight.js style (c) Ivan Sagalaev + +*/ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #F0F0F0; +} +/* Base color: saturation 0; */ +.hljs, +.hljs-subst { + color: #444; +} +.hljs-comment { + color: #888888; +} +.hljs-keyword, +.hljs-attribute, +.hljs-selector-tag, +.hljs-meta-keyword, +.hljs-doctag, +.hljs-name { + font-weight: bold; +} +/* User color: hue: 0 */ +.hljs-type, +.hljs-string, +.hljs-number, +.hljs-selector-id, +.hljs-selector-class, +.hljs-quote, +.hljs-template-tag, +.hljs-deletion { + color: #880000; +} +.hljs-title, +.hljs-section { + color: #880000; + font-weight: bold; +} +.hljs-regexp, +.hljs-symbol, +.hljs-variable, +.hljs-template-variable, +.hljs-link, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #BC6060; +} +/* Language color: hue: 90; */ +.hljs-literal { + color: #78A960; +} +.hljs-built_in, +.hljs-bullet, +.hljs-code, +.hljs-addition { + color: #397300; +} +/* Meta color: hue: 200 */ +.hljs-meta { + color: #1f7199; +} +.hljs-meta-string { + color: #4d99bf; +} +/* Misc effects */ +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: bold; +} +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + margin: 0; + padding: 0; + background: #ffffff; + color: #4d4d4d; +} +p, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0 0 15px 0; +} +h1 { + margin-top: 40px; +} +a { + color: #880000; +} +a:visited { + color: #880000; +} +#tree, +#headings { + position: absolute; + top: 30px; + left: 0; + bottom: 0; + width: 290px; + padding: 10px 0; + overflow: auto; +} +#sidebar_wrapper { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 0; + overflow: hidden; + background: #e7e7e7; +} +#sidebar_switch { + position: absolute; + top: 0; + left: 0; + width: 290px; + height: 29px; + border-bottom: 1px solid; + background: #e2e2e2; + border-bottom-color: #d6d6d6; +} +#sidebar_switch span { + display: block; + float: left; + width: 50%; + text-align: center; + line-height: 29px; + cursor: pointer; + color: #4b4b4b; +} +#sidebar_switch span:hover { + background: #e7e7e7; +} +#sidebar_switch .selected { + font-weight: bold; + background: #ededed; + color: #444; +} +.slidey #sidebar_wrapper { + -webkit-transition: width 250ms linear; + -moz-transition: width 250ms linear; + -ms-transition: width 250ms linear; + -o-transition: width 250ms linear; + transition: width 250ms linear; +} +.sidebar #sidebar_wrapper { + width: 290px; +} +#tree .nodename { + text-indent: 12px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAg0lEQVQYlWNIS0tbAcSK////Z8CHGTIzM7+mp6d/ASouwqswKyvrO1DRfyg+CcRaxCgE4Z9A3AjEbIQUgjHQOQvwKgS6+ffChQt3AiUDcCqsra29d/v27R6ghCVWN2ZnZ/9YuXLlRqBAPBALYvVMR0fHmQcPHrQBOUZ4gwfqFj5CAQ4Al6wLIYDwo9QAAAAASUVORK5CYII="); + background-repeat: no-repeat; + background-position: left center; + cursor: pointer; +} +#tree .open > .nodename { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAlElEQVQYlWNIS0tbCsT/8eCN////Z2B49OhRfHZ29jdsioDiP27evJkNVggkONeuXbscm8Jly5atA8rzwRSCsG5DQ8MtZEU1NTUPgOLGUHm4QgaQFVlZWT9BijIzM39fuHChDCaHohBkBdCq9SCF8+bN2wHkC+FSCMLGkyZNOvb9+3dbNHEMhSDsDsRMxCjEiolWCADeUBHgU/IGQQAAAABJRU5ErkJggg=="); + background-position: left 7px; +} +#tree .dir, +#tree .file { + position: relative; + min-height: 20px; + line-height: 20px; + padding-left: 12px; +} +#tree .dir > .children, +#tree .file > .children { + display: none; +} +#tree .dir.open > .children, +#tree .file.open > .children { + display: block; +} +#tree .file { + padding-left: 24px; + display: block; + text-decoration: none; + color: #444; +} +#tree > .dir { + padding-left: 0; +} +#headings .heading a { + text-decoration: none; + padding-left: 10px; + display: block; + color: #444; +} +#headings .h1 { + padding-left: 0; + margin-top: 10px; + font-size: 1.3em; +} +#headings .h2 { + padding-left: 10px; + margin-top: 8px; + font-size: 1.1em; +} +#headings .h3 { + padding-left: 20px; + margin-top: 5px; + font-size: 1em; +} +#headings .h4 { + padding-left: 30px; + margin-top: 3px; + font-size: 0.9em; +} +#headings .h5 { + padding-left: 40px; + margin-top: 1px; + font-size: 0.8em; +} +#headings .h6 { + padding-left: 50px; + font-size: 0.75em; +} +#sidebar-toggle { + position: fixed; + top: 0; + left: 0; + width: 5px; + bottom: 0; + z-index: 2; + cursor: pointer; + background: #dfdfdf; +} +#sidebar-toggle:hover { + width: 10px; + background: #d6d6d6; +} +.slidey #sidebar-toggle, +.slidey #container { + -webkit-transition: all 250ms linear; + -moz-transition: all 250ms linear; + -ms-transition: all 250ms linear; + -o-transition: all 250ms linear; + transition: all 250ms linear; +} +.sidebar #sidebar-toggle { + left: 290px; +} +#container { + position: fixed; + left: 5px; + right: 0; + top: 0; + bottom: 0; + overflow: auto; +} +.sidebar #container { + left: 295px; +} +.no-sidebar #sidebar_wrapper, +.no-sidebar #sidebar-toggle { + display: none; +} +.no-sidebar #container { + left: 0; +} +#page { + padding-top: 40px; +} +table td { + border: 0; + outline: 0; +} +.docs.markdown { + padding: 10px 50px; +} +td.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; +} +.docs pre { + margin: 15px 0 15px; + padding: 5px; + padding-left: 10px; + border: 1px solid #d6d6d6; + background: #F0F0F0; + font-size: 12px; + overflow: auto; +} +.docs pre.code_stats { + font-size: 60%; +} +.docs p tt, +.docs li tt, +.docs p code, +.docs li code { + border: 1px solid #d6d6d6; + font-size: 12px; + padding: 0 0.2em; + background: #e7e7e7; +} +.dox { + border-top: 1px solid #dddddd; + padding-top: 10px; + padding-bottom: 10px; +} +.dox .details { + padding: 10px; + background: #F0F0F0; + border: 1px solid #d6d6d6; + margin-bottom: 10px; +} +.dox .dox_tag_title { + font-weight: bold; +} +.dox .dox_tag_detail { + margin-left: 10px; +} +.dox .dox_tag_detail span { + margin-right: 5px; +} +.dox .dox_type { + font-style: italic; +} +.dox .dox_tag_name { + font-weight: bold; +} +.pilwrap { + position: relative; + padding-top: 1px; +} +.pilwrap .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + color: #555555; +} +.pilwrap .pilcrow:before { + content: '\b6'; +} +.pilwrap:hover .pilcrow { + opacity: 1; +} +td.code { + padding: 8px 15px 8px 25px; + width: 100%; + vertical-align: top; + border-left: 1px solid #d6d6d6; + background: #F0F0F0; +} +.background { + border-left: 1px solid #d6d6d6; + position: absolute; + z-index: -1; + top: 0; + right: 0; + bottom: 0; + left: 525px; + background: #F0F0F0; +} +pre, +tt, +code { + font-size: 12px; + line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; + padding: 0; + white-space: pre-wrap; + background: #F0F0F0; +} +.line-num { + display: inline-block; + width: 50px; + text-align: right; + opacity: 0.3; + margin-left: -20px; + text-decoration: none; + color: #888888; +} +.line-num:before { + content: attr(data-line); +} diff --git a/counter/foundry.toml b/counter/foundry.toml index 25b918f9c9a96..c27b8ed21ba0b 100644 --- a/counter/foundry.toml +++ b/counter/foundry.toml @@ -1,6 +1,9 @@ [profile.default] src = "src" out = "out" -libs = ["lib"] +libs = ["lib", "dependencies"] + +[dependencies] +forge-std = "1.15.0" # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/counter/remappings.txt b/counter/remappings.txt new file mode 100644 index 0000000000000..6c93bbb0c4b12 --- /dev/null +++ b/counter/remappings.txt @@ -0,0 +1 @@ +forge-std-1.15.0/=dependencies/forge-std-1.15.0/ diff --git a/counter/soldeer.lock b/counter/soldeer.lock new file mode 100644 index 0000000000000..af6c8601cd7ba --- /dev/null +++ b/counter/soldeer.lock @@ -0,0 +1,6 @@ +[[dependencies]] +name = "forge-std" +version = "1.15.0" +url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_15_0_27-02-2026_08:26:17_forge-std-1.15.zip" +checksum = "40d9b3b3d786eec4cd05fb9d818616015cbe7b8866643a9f0854495c938588c4" +integrity = "92accf4f7850eb9f5832f0ea77d633d36ebe993efc6d6c9f32369d31befc8a75" diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 9dac717e1f8a2..07c345b4ce81a 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -224,6 +224,18 @@ impl NodeArgs { let compute_units_per_second = if self.evm.no_rate_limit { Some(u64::MAX) } else { self.evm.compute_units_per_second }; + // Validate that secondary fork URLs don't have conflicting block number suffixes + if self.evm.fork_url.len() > 1 { + for fork in &self.evm.fork_url[1..] { + if fork.block.is_some() { + eyre::bail!( + "Block number suffixes (@block) on secondary --fork-url values are not supported. \ + Use --fork-block-number to set the fork block for all endpoints." + ); + } + } + } + let hardfork = match &self.hardfork { Some(hf) => { if self.evm.networks.is_optimism() { @@ -255,7 +267,7 @@ impl NodeArgs { _ => self .evm .fork_url - .as_ref() + .first() .and_then(|f| f.block) .map(|num| ForkChoice::Block(num as i128)), }) @@ -265,7 +277,7 @@ impl NodeArgs { .fork_request_retries(self.evm.fork_request_retries) .fork_retry_backoff(self.evm.fork_retry_backoff.map(Duration::from_millis)) .fork_compute_units_per_second(compute_units_per_second) - .with_eth_rpc_url(self.evm.fork_url.map(|fork| fork.url)) + .with_fork_urls(self.evm.fork_url.into_iter().map(|f| f.url).collect()) .with_base_fee(self.evm.block_base_fee_per_gas) .disable_min_priority_fee(self.evm.disable_min_priority_fee) .with_no_storage_caching(self.evm.no_storage_caching) @@ -421,6 +433,10 @@ 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. + /// + /// Multiple `--fork-url` flags can be provided to distribute requests across endpoints + /// using round-robin load balancing. On failure, the retry layer rotates to the next + /// endpoint. #[arg( long, short, @@ -428,7 +444,7 @@ pub struct AnvilEvmArgs { value_name = "URL", help_heading = "Fork config" )] - pub fork_url: Option, + pub fork_url: Vec, /// Headers to use for the rpc client, e.g. "User-Agent: test-agent" /// @@ -625,13 +641,45 @@ pub struct AnvilEvmArgs { /// Resolves an alias passed as fork-url to the matching url defined in the rpc_endpoints section /// of the project configuration file. /// Does nothing if the fork-url is not a configured alias. +/// +/// When an alias maps to an `RpcEndpoint` with multiple `endpoints`, all URLs are expanded +/// into additional `--fork-url` entries for multi-endpoint load balancing. impl AnvilEvmArgs { pub fn resolve_rpc_alias(&mut self) { - if let Some(fork_url) = &self.fork_url - && let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) - && 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 }); + if let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) { + let mut resolved_urls = Vec::new(); + for fork_url in &self.fork_url { + let mut endpoints = config.rpc_endpoints.clone().resolved(); + if let Some(endpoint) = endpoints.remove(&fork_url.url) { + // Alias matched — expand all URLs from the endpoint config + match endpoint.all_urls() { + Ok(urls) => { + for (i, url) in urls.into_iter().enumerate() { + resolved_urls.push(ForkUrl { + url, + // Only the first URL inherits the block suffix + block: if i == 0 { fork_url.block } else { None }, + }); + } + } + Err(e) => { + warn!(target: "node", alias=%fork_url.url, %e, "could not resolve all endpoints, using primary endpoint only"); + if let Ok(url) = endpoint.url() { + resolved_urls.push(ForkUrl { url, block: fork_url.block }); + } else { + resolved_urls.push(fork_url.clone()); + } + } + } + } else if let Some(Ok(url)) = config.get_rpc_url_with_alias(&fork_url.url) { + // Try mesc or other resolution + resolved_urls.push(ForkUrl { url: url.to_string(), block: fork_url.block }); + } else { + // Not an alias — keep as-is + resolved_urls.push(fork_url.clone()); + } + } + self.fork_url = resolved_urls; } } } @@ -960,4 +1008,65 @@ mod tests { ["::1", "1.1.1.1", "2.2.2.2"].map(|ip| ip.parse::().unwrap()).to_vec() ); } + + #[test] + fn can_parse_multiple_fork_urls() { + let args: NodeArgs = NodeArgs::parse_from([ + "anvil", + "--fork-url", + "http://localhost:8545", + "--fork-url", + "http://localhost:8546", + "--fork-url", + "http://localhost:8547", + ]); + assert_eq!(args.evm.fork_url.len(), 3); + assert_eq!(args.evm.fork_url[0].url, "http://localhost:8545"); + assert_eq!(args.evm.fork_url[1].url, "http://localhost:8546"); + assert_eq!(args.evm.fork_url[2].url, "http://localhost:8547"); + + // Block suffix on first URL should work + let args: NodeArgs = NodeArgs::parse_from([ + "anvil", + "--fork-url", + "http://localhost:8545@1000000", + "--fork-url", + "http://localhost:8546", + ]); + assert_eq!(args.evm.fork_url[0].block, Some(1000000)); + assert_eq!(args.evm.fork_url[1].block, None); + } + + #[test] + fn rejects_block_suffix_on_secondary_fork_urls() { + let args: NodeArgs = NodeArgs::parse_from([ + "anvil", + "--fork-url", + "http://localhost:8545@1000000", + "--fork-url", + "http://localhost:8546@2000000", + ]); + let result = args.into_node_config(); + assert!(result.is_err()); + assert!( + result.unwrap_err().to_string().contains("Block number suffixes"), + "should reject block suffix on secondary fork URL" + ); + } + + #[test] + fn fork_dependent_args_require_fork_url() { + // All these args have `requires = "fork_url"` — they should fail without --fork-url + let cases = [ + vec!["anvil", "--fork-header", "X-Api-Key: test"], + vec!["anvil", "--timeout", "5000"], + vec!["anvil", "--retries", "3"], + vec!["anvil", "--fork-block-number", "100"], + vec!["anvil", "--fork-retry-backoff", "500"], + ]; + for args in &cases { + let result = NodeArgs::try_parse_from(args); + assert!(result.is_err(), "expected error when using {:?} without --fork-url", args[1]); + } + } } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 56a5e4ddcfa81..23cd6e61bc076 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -3,7 +3,6 @@ use crate::{ eth::{ backend::{ db::{Db, SerializableState}, - env::Env, fork::{ClientFork, ClientForkConfig}, genesis::GenesisConfig, mem::fork_db::ForkedDatabase, @@ -38,19 +37,18 @@ use foundry_config::Config; use foundry_evm::{ backend::{BlockchainDb, BlockchainDbMeta, SharedBackend}, constants::DEFAULT_CREATE2_DEPLOYER, - hardfork::{ - FoundryHardfork, OpHardfork, ethereum_hardfork_from_block_tag, - spec_id_from_ethereum_hardfork, + hardfork::{FoundryHardfork, OpHardfork}, + utils::{ + apply_chain_and_block_specific_env_changes, block_env_from_header, + get_blob_base_fee_update_fraction, }, - utils::{apply_chain_and_block_specific_env_changes, get_blob_base_fee_update_fraction}, }; -use foundry_primitives::FoundryNetwork; +use foundry_primitives::FoundryTxEnvelope; use itertools::Itertools; -use op_revm::OpTransaction; use parking_lot::RwLock; use rand_08::thread_rng; use revm::{ - context::{BlockEnv, CfgEnv, TxEnv}, + context::{BlockEnv, CfgEnv}, context_interface::block::BlobExcessGasAndPrice, primitives::hardfork::SpecId, }; @@ -62,6 +60,7 @@ use std::{ sync::Arc, time::Duration, }; +use tempo_chainspec::hardfork::TempoHardfork; use tokio::sync::RwLock as TokioRwLock; use yansi::Paint; @@ -135,11 +134,13 @@ pub struct NodeConfig { pub port: u16, /// maximum number of transactions in a block pub max_transactions: usize, - /// url of the rpc server that should be used for any rpc calls - pub eth_rpc_url: Option, + /// Fork URLs for RPC calls. The first entry is the primary endpoint. + /// When multiple URLs are provided, requests are distributed using + /// round-robin load balancing with retry-based failover. + pub fork_urls: Vec, /// pins the block number or transaction hash for the state fork pub fork_choice: Option, - /// headers to use with `eth_rpc_url` + /// headers to use with fork RPC endpoints pub fork_headers: Vec, /// specifies chain id for cache to skip fetching from remote in offline-start mode pub fork_chain_id: Option, @@ -269,12 +270,19 @@ Block number: {} Block hash: {:?} Chain ID: {} "#, - fork.eth_rpc_url(), + fork.eth_rpc_url().as_deref().unwrap_or("none"), fork.block_number(), fork.block_hash(), fork.chain_id() ); + if self.fork_urls.len() > 1 { + let _ = writeln!(s, "Endpoints: {}", self.fork_urls.len()); + for (i, url) in self.fork_urls.iter().enumerate() { + let _ = writeln!(s, " ({i}) {url}"); + } + } + if let Some(tx_hash) = fork.transaction_hash() { let _ = writeln!(s, "Transaction hash: {tx_hash}"); } @@ -394,7 +402,7 @@ Genesis Number json!({ "available_accounts": available_accounts, "private_keys": private_keys, - "endpoint": fork.eth_rpc_url(), + "endpoint": fork.eth_rpc_url().unwrap_or_default(), "block_number": fork.block_number(), "block_hash": fork.block_hash(), "chain_id": fork.chain_id(), @@ -425,6 +433,12 @@ impl NodeConfig { Self { enable_tracing: true, port: 0, silent: true, ..Default::default() } } + /// Returns a test config with Tempo network enabled. + #[doc(hidden)] + pub fn test_tempo() -> Self { + Self { networks: NetworkConfigs::with_tempo(), ..Self::test() } + } + /// Returns a new config which does not initialize any accounts on node startup. pub fn empty_state() -> Self { Self { @@ -461,7 +475,7 @@ impl Default for NodeConfig { mixed_mining: false, port: NODE_PORT, max_transactions: 1_000, - eth_rpc_url: None, + fork_urls: vec![], fork_choice: None, account_generator: None, base_fee: None, @@ -506,20 +520,35 @@ impl Default for NodeConfig { impl NodeConfig { /// Returns the memory limit of the node #[must_use] - pub fn with_memory_limit(mut self, mems_value: Option) -> Self { + pub const fn with_memory_limit(mut self, mems_value: Option) -> Self { self.memory_limit = mems_value; self } - /// Returns the base fee to use + + /// Returns the base fee to use. + /// + /// In Tempo mode, uses the hardfork-specific base fee (10 gwei pre-T1, 20 gwei T1+). pub fn get_base_fee(&self) -> u64 { + let default = if self.networks.is_tempo() { + TempoHardfork::from(self.get_hardfork()).base_fee() + } else { + INITIAL_BASE_FEE + }; self.base_fee .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64))) - .unwrap_or(INITIAL_BASE_FEE) + .unwrap_or(default) } - /// Returns the base fee to use + /// Returns the gas price to use. + /// + /// In Tempo mode, defaults to the hardfork-specific base fee. pub fn get_gas_price(&self) -> u128 { - self.gas_price.unwrap_or(INITIAL_GAS_PRICE) + let default = if self.networks.is_tempo() { + TempoHardfork::from(self.get_hardfork()).base_fee() as u128 + } else { + INITIAL_GAS_PRICE + }; + self.gas_price.unwrap_or(default) } pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice { @@ -551,18 +580,21 @@ impl NodeConfig { if self.networks.is_optimism() { return OpHardfork::default().into(); } + if self.networks.is_tempo() { + return TempoHardfork::default().into(); + } EthereumHardfork::default().into() } /// Sets a custom code size limit #[must_use] - pub fn with_code_size_limit(mut self, code_size_limit: Option) -> Self { + pub const fn with_code_size_limit(mut self, code_size_limit: Option) -> Self { self.code_size_limit = code_size_limit; self } /// Disables code size limit #[must_use] - pub fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { + pub const fn disable_code_size_limit(mut self, disable_code_size_limit: bool) -> Self { if disable_code_size_limit { self.code_size_limit = Some(usize::MAX); } @@ -613,7 +645,7 @@ impl NodeConfig { /// Sets the gas limit #[must_use] - pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { + pub const fn with_gas_limit(mut self, gas_limit: Option) -> Self { self.gas_limit = gas_limit; self } @@ -622,7 +654,7 @@ impl NodeConfig { /// /// If set to `true` block gas limit will not be enforced #[must_use] - pub fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self { + pub const fn disable_block_gas_limit(mut self, disable_block_gas_limit: bool) -> Self { self.disable_block_gas_limit = disable_block_gas_limit; self } @@ -631,14 +663,14 @@ impl NodeConfig { /// /// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825) #[must_use] - pub fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self { + pub const fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self { self.enable_tx_gas_limit = enable_tx_gas_limit; self } /// Sets the gas price #[must_use] - pub fn with_gas_price(mut self, gas_price: Option) -> Self { + pub const fn with_gas_price(mut self, gas_price: Option) -> Self { self.gas_price = gas_price; self } @@ -662,7 +694,7 @@ impl NodeConfig { /// Sets the max number of transactions in a block #[must_use] - pub fn with_max_transactions(mut self, max_transactions: Option) -> Self { + pub const fn with_max_transactions(mut self, max_transactions: Option) -> Self { if let Some(max_transactions) = max_transactions { self.max_transactions = max_transactions; } @@ -681,14 +713,14 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub fn with_base_fee(mut self, base_fee: Option) -> Self { + pub const fn with_base_fee(mut self, base_fee: Option) -> Self { self.base_fee = base_fee; self } /// Disable the enforcement of a minimum suggested priority fee #[must_use] - pub fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { + pub const fn disable_min_priority_fee(mut self, disable_min_priority_fee: bool) -> Self { self.disable_min_priority_fee = disable_min_priority_fee; self } @@ -734,7 +766,7 @@ impl NodeConfig { /// Sets the hardfork #[must_use] - pub fn with_hardfork(mut self, hardfork: Option) -> Self { + pub const fn with_hardfork(mut self, hardfork: Option) -> Self { self.hardfork = hardfork; self } @@ -788,21 +820,21 @@ impl NodeConfig { /// If set to `true` auto mining will be disabled #[must_use] - pub fn with_no_mining(mut self, no_mining: bool) -> Self { + pub const fn with_no_mining(mut self, no_mining: bool) -> Self { self.no_mining = no_mining; self } /// Sets the slots in an epoch #[must_use] - pub fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self { + pub const 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 { + pub const fn with_port(mut self, port: u16) -> Self { self.port = port; self } @@ -827,15 +859,24 @@ impl NodeConfig { } #[must_use] - pub fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { + pub const fn with_no_storage_caching(mut self, no_storage_caching: bool) -> Self { self.no_storage_caching = no_storage_caching; self } - /// Sets the `eth_rpc_url` to use when forking + /// Sets the `eth_rpc_url` to use when forking (single endpoint convenience). #[must_use] pub fn with_eth_rpc_url>(mut self, eth_rpc_url: Option) -> Self { - self.eth_rpc_url = eth_rpc_url.map(Into::into); + if let Some(url) = eth_rpc_url { + self.fork_urls = vec![url.into()]; + } + self + } + + /// Sets the fork URLs for load-balanced multi-endpoint forking. + #[must_use] + pub fn with_fork_urls(mut self, fork_urls: Vec) -> Self { + self.fork_urls = fork_urls; self } @@ -863,12 +904,12 @@ impl NodeConfig { /// Sets the `fork_chain_id` to use to fork off local cache from #[must_use] - pub fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { + pub const fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { self.fork_chain_id = fork_chain_id; self } - /// Sets the `fork_headers` to use with `eth_rpc_url` + /// Sets the `fork_headers` to use with fork RPC endpoints #[must_use] pub fn with_fork_headers(mut self, headers: Vec) -> Self { self.fork_headers = headers; @@ -877,7 +918,7 @@ impl NodeConfig { /// Sets the `fork_request_timeout` to use for requests #[must_use] - pub fn fork_request_timeout(mut self, fork_request_timeout: Option) -> Self { + pub const fn fork_request_timeout(mut self, fork_request_timeout: Option) -> Self { if let Some(fork_request_timeout) = fork_request_timeout { self.fork_request_timeout = fork_request_timeout; } @@ -886,7 +927,7 @@ impl NodeConfig { /// Sets the `fork_request_retries` to use for spurious networks #[must_use] - pub fn fork_request_retries(mut self, fork_request_retries: Option) -> Self { + pub const fn fork_request_retries(mut self, fork_request_retries: Option) -> Self { if let Some(fork_request_retries) = fork_request_retries { self.fork_request_retries = fork_request_retries; } @@ -895,7 +936,7 @@ impl NodeConfig { /// Sets the initial `fork_retry_backoff` for rate limits #[must_use] - pub fn fork_retry_backoff(mut self, fork_retry_backoff: Option) -> Self { + pub const fn fork_retry_backoff(mut self, fork_retry_backoff: Option) -> Self { if let Some(fork_retry_backoff) = fork_retry_backoff { self.fork_retry_backoff = fork_retry_backoff; } @@ -906,7 +947,10 @@ impl NodeConfig { /// /// See also, #[must_use] - pub fn fork_compute_units_per_second(mut self, compute_units_per_second: Option) -> Self { + pub const fn fork_compute_units_per_second( + mut self, + compute_units_per_second: Option, + ) -> Self { if let Some(compute_units_per_second) = compute_units_per_second { self.compute_units_per_second = compute_units_per_second; } @@ -915,35 +959,35 @@ impl NodeConfig { /// Sets whether to enable tracing #[must_use] - pub fn with_tracing(mut self, enable_tracing: bool) -> Self { + pub const fn with_tracing(mut self, enable_tracing: bool) -> Self { self.enable_tracing = enable_tracing; self } /// Sets whether to enable steps tracing #[must_use] - pub fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self { + pub const fn with_steps_tracing(mut self, enable_steps_tracing: bool) -> Self { self.enable_steps_tracing = enable_steps_tracing; self } /// Sets whether to print `console.log` invocations to stdout. #[must_use] - pub fn with_print_logs(mut self, print_logs: bool) -> Self { + pub const fn with_print_logs(mut self, print_logs: bool) -> Self { self.print_logs = print_logs; self } /// Sets whether to print traces to stdout. #[must_use] - pub fn with_print_traces(mut self, print_traces: bool) -> Self { + pub const fn with_print_traces(mut self, print_traces: bool) -> Self { self.print_traces = print_traces; self } /// Sets whether to enable autoImpersonate #[must_use] - pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self { + pub const fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self { self.enable_auto_impersonate = enable_auto_impersonate; self } @@ -962,7 +1006,7 @@ impl NodeConfig { } #[must_use] - pub fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self { + pub const fn with_transaction_order(mut self, transaction_order: TransactionOrder) -> Self { self.transaction_order = transaction_order; self } @@ -991,7 +1035,7 @@ impl NodeConfig { /// /// See also [ Config::foundry_block_cache_file()] pub fn block_cache_path(&self, block: u64) -> Option { - if self.no_storage_caching || self.eth_rpc_url.is_none() { + if self.no_storage_caching || self.fork_urls.is_empty() { return None; } let chain_id = self.get_chain_id(); @@ -1001,14 +1045,14 @@ impl NodeConfig { /// Sets whether to disable the default create2 deployer #[must_use] - pub fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { + pub const fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { self.disable_default_create2_deployer = yes; self } /// Sets whether to disable pool balance checks #[must_use] - pub fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self { + pub const fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self { self.disable_pool_balance_checks = yes; self } @@ -1022,19 +1066,33 @@ impl NodeConfig { /// Enable features for provided networks. #[must_use] - pub fn with_networks(mut self, networks: NetworkConfigs) -> Self { + pub const fn with_networks(mut self, networks: NetworkConfigs) -> Self { self.networks = networks; self } + /// Enable Tempo network features. + #[must_use] + pub fn with_tempo(mut self) -> Self { + self.networks = NetworkConfigs::with_tempo(); + self + } + + /// Enable Optimism network features. + #[must_use] + pub fn with_optimism(mut self) -> Self { + self.networks = NetworkConfigs::with_optimism(); + self + } + /// Makes the node silent to not emit anything on stdout #[must_use] - pub fn silent(self) -> Self { + pub const fn silent(self) -> Self { self.set_silent(true) } #[must_use] - pub fn set_silent(mut self, silent: bool) -> Self { + pub const fn set_silent(mut self, silent: bool) -> Self { self.silent = silent; self } @@ -1053,7 +1111,13 @@ impl NodeConfig { /// [Backend](mem::Backend) /// /// *Note*: only memory based backend for now - pub(crate) async fn setup(&mut self) -> Result> { + pub(crate) async fn setup(&mut self) -> Result> + where + N: alloy_network::Network< + TxEnvelope = foundry_primitives::FoundryTxEnvelope, + ReceiptEnvelope = foundry_primitives::FoundryReceiptEnvelope, + >, + { // configure the revm environment let mut cfg = CfgEnv::default(); @@ -1076,20 +1140,13 @@ impl NodeConfig { } let spec_id = cfg.spec; - let mut env = Env::new( - EvmEnv::new( - cfg, - BlockEnv { - gas_limit: self.gas_limit(), - basefee: self.get_base_fee(), - ..Default::default() - }, - ), - OpTransaction { - base: TxEnv { chain_id: Some(self.get_chain_id()), ..Default::default() }, + let mut evm_env = EvmEnv::new( + cfg, + BlockEnv { + gas_limit: self.gas_limit(), + basefee: self.get_base_fee(), ..Default::default() }, - self.networks, ); let base_fee_params: BaseFeeParams = @@ -1106,8 +1163,8 @@ impl NodeConfig { ); let (db, fork): (Arc>>, Option) = - if let Some(eth_rpc_url) = self.eth_rpc_url.clone() { - self.setup_fork_db(eth_rpc_url, &mut env, &fees).await? + if let Some(eth_rpc_url) = self.fork_urls.first().cloned() { + self.setup_fork_db(eth_rpc_url, &mut evm_env, &fees).await? } else { (Arc::new(TokioRwLock::new(Box::::default())), None) }; @@ -1117,16 +1174,16 @@ impl NodeConfig { // --chain-id flag gets precedence over the genesis.json chain id // if self.chain_id.is_none() { - env.evm_env.cfg_env.chain_id = genesis.config.chain_id; + evm_env.cfg_env.chain_id = genesis.config.chain_id; } - env.evm_env.block_env.timestamp = U256::from(genesis.timestamp); + evm_env.block_env.timestamp = U256::from(genesis.timestamp); if let Some(base_fee) = genesis.base_fee_per_gas { - env.evm_env.block_env.basefee = base_fee.try_into()?; + evm_env.block_env.basefee = base_fee.try_into()?; } if let Some(number) = genesis.number { - env.evm_env.block_env.number = U256::from(number); + evm_env.block_env.number = U256::from(number); } - env.evm_env.block_env.beneficiary = genesis.coinbase; + evm_env.block_env.beneficiary = genesis.coinbase; } let genesis = GenesisConfig { @@ -1149,7 +1206,8 @@ impl NodeConfig { // only memory based backend for now let backend = mem::Backend::with_genesis( db, - Arc::new(RwLock::new(env)), + Arc::new(RwLock::new(evm_env)), + self.networks, genesis, fees, Arc::new(RwLock::new(fork)), @@ -1168,17 +1226,13 @@ impl NodeConfig { // Writes the default create2 deployer to the backend, // if the option is not disabled and we are not forking. - if !self.disable_default_create2_deployer && self.eth_rpc_url.is_none() { + if !self.disable_default_create2_deployer && self.fork_urls.is_empty() { backend .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER) .await .wrap_err("failed to create default create2 deployer")?; } - if let Some(state) = self.init_state.clone() { - backend.load_state(state).await.wrap_err("failed to load init state")?; - } - Ok(backend) } @@ -1191,10 +1245,10 @@ impl NodeConfig { pub async fn setup_fork_db( &mut self, eth_rpc_url: String, - env: &mut Env, + evm_env: &mut EvmEnv, fees: &FeeManager, ) -> Result<(Arc>>, Option)> { - let (db, config) = self.setup_fork_db_config(eth_rpc_url, env, fees).await?; + let (db, config) = self.setup_fork_db_config(eth_rpc_url, evm_env, fees).await?; let db: Arc>> = Arc::new(TokioRwLock::new(Box::new(db))); let fork = ClientFork::new(config, Arc::clone(&db)); Ok((db, Some(fork))) @@ -1208,10 +1262,14 @@ impl NodeConfig { pub async fn setup_fork_db_config( &mut self, eth_rpc_url: String, - env: &mut Env, + evm_env: &mut EvmEnv, fees: &FeeManager, ) -> Result<(ForkedDatabase, ClientForkConfig)> { debug!(target: "node", ?eth_rpc_url, "setting up fork db"); + + // Always bootstrap with the primary URL only to avoid race conditions + // where discovery calls (get_chain_id, find_latest_fork_block, get_block) + // hit different endpoints that may be at different chain tips. let provider = Arc::new( ProviderBuilder::new(ð_rpc_url) .timeout(self.fork_request_timeout) @@ -1233,16 +1291,8 @@ impl NodeConfig { let chain_id = if let Some(chain_id) = self.fork_chain_id { Some(chain_id) } else if self.hardfork.is_none() { - // Auto-adjust hardfork if not specified, but only if we're forking mainnet. let chain_id = provider.get_chain_id().await.wrap_err("failed to fetch network chain ID")?; - if alloy_chains::NamedChain::Mainnet == chain_id { - let hardfork: EthereumHardfork = - ethereum_hardfork_from_block_tag(fork_block_number); - - env.evm_env.cfg_env.spec = spec_id_from_ethereum_hardfork(hardfork); - self.hardfork = Some(FoundryHardfork::Ethereum(hardfork)); - } Some(U256::from(chain_id)) } else { None @@ -1284,17 +1334,12 @@ latest block number: {latest_block}" let gas_limit = self.fork_gas_limit(&block); self.gas_limit = Some(gas_limit); - env.evm_env.block_env = BlockEnv { - number: U256::from(fork_block_number), - timestamp: U256::from(block.header.timestamp()), - difficulty: block.header.difficulty(), - // ensures prevrandao is set - prevrandao: Some(block.header.mix_hash().unwrap_or_default()), + evm_env.block_env = BlockEnv { gas_limit, // Keep previous `coinbase` and `basefee` value - beneficiary: env.evm_env.block_env.beneficiary, - basefee: env.evm_env.block_env.basefee, - ..Default::default() + beneficiary: evm_env.block_env.beneficiary, + basefee: evm_env.block_env.basefee, + ..block_env_from_header(&block.header) }; // Determine chain_id early so we can use it consistently @@ -1309,17 +1354,25 @@ latest block number: {latest_block}" // need to update the dev signers and env with the chain id self.set_chain_id(Some(chain_id)); - env.evm_env.cfg_env.chain_id = chain_id; - env.tx.base.chain_id = chain_id.into(); + evm_env.cfg_env.chain_id = chain_id; chain_id }; + // Auto-detect hardfork from chain activation data if not explicitly set. + if self.hardfork.is_none() + && let Some(hardfork) = + FoundryHardfork::from_chain_and_timestamp(chain_id, block.header.timestamp()) + { + evm_env.cfg_env.spec = SpecId::from(hardfork); + self.hardfork = Some(hardfork); + } + // if not set explicitly we use the base fee of the latest block if self.base_fee.is_none() && let Some(base_fee) = block.header.base_fee_per_gas() { self.base_fee = Some(base_fee); - env.evm_env.block_env.basefee = base_fee; + evm_env.block_env.basefee = 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( @@ -1338,7 +1391,7 @@ latest block number: {latest_block}" // Derive blob params using the fork block timestamp regardless of explicit base fee. let blob_params = get_blob_params(chain_id, block.header.timestamp()); - env.evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( + evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( blob_excess_gas, blob_params.update_fraction as u64, )); @@ -1365,19 +1418,38 @@ latest block number: {latest_block}" 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::( - &mut env.evm_env, + apply_chain_and_block_specific_env_changes::( + evm_env, &block, self.networks, ); - let meta = BlockchainDbMeta::new(env.evm_env.block_env.clone(), eth_rpc_url.clone()); + let meta = BlockchainDbMeta::new(evm_env.block_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 { BlockchainDb::new(meta, self.block_cache_path(fork_block_number)) }; + // After bootstrap, rebuild the provider with round-robin if multiple URLs are + // configured. This ensures bootstrap used only the primary endpoint for consistency, + // while ongoing requests are distributed across all endpoints. + let provider = if self.fork_urls.len() > 1 { + debug!(target: "node", urls=?self.fork_urls, "using multi-endpoint round-robin provider"); + Arc::new( + ProviderBuilder::new(ð_rpc_url) + .timeout(self.fork_request_timeout) + .initial_backoff(self.fork_retry_backoff.as_millis() as u64) + .compute_units_per_second(self.compute_units_per_second) + .max_retry(self.fork_request_retries) + .headers(self.fork_headers.clone()) + .build_fallback(self.fork_urls.clone()) + .wrap_err("failed to establish round-robin provider to fork urls")?, + ) + } else { + provider + }; + // This will spawn the background thread that will use the provider to fetch // blockchain data from the other client let backend = SharedBackend::spawn_backend( @@ -1388,7 +1460,7 @@ latest block number: {latest_block}" .await; let config = ClientForkConfig { - eth_rpc_url, + fork_urls: self.fork_urls.clone(), block_number: fork_block_number, block_hash, transaction_hash: self.fork_choice.and_then(|fc| fc.transaction_hash()), @@ -1401,9 +1473,10 @@ latest block number: {latest_block}" retries: self.fork_request_retries, backoff: self.fork_retry_backoff, compute_units_per_second: self.compute_units_per_second, + headers: self.fork_headers.clone(), total_difficulty: block.header.total_difficulty.unwrap_or_default(), blob_gas_used: block.header.blob_gas_used().map(|g| g as u128), - blob_excess_gas_and_price: env.evm_env.block_env.blob_excess_gas_and_price, + blob_excess_gas_and_price: evm_env.block_env.blob_excess_gas_and_price, force_transactions, }; @@ -1451,7 +1524,7 @@ latest block number: {latest_block}" async fn derive_block_and_transactions( fork_choice: &ForkChoice, provider: &Arc, -) -> eyre::Result<(BlockNumber, Option>)> { +) -> eyre::Result<(BlockNumber, Option>>)> { match fork_choice { ForkChoice::Block(block_number) => { let block_number = *block_number; @@ -1513,7 +1586,7 @@ pub enum ForkChoice { impl ForkChoice { /// Returns the block number to fork from - pub fn block_number(&self) -> Option { + pub const fn block_number(&self) -> Option { match self { Self::Block(block_number) => Some(*block_number), Self::Transaction(_) => None, @@ -1521,7 +1594,7 @@ impl ForkChoice { } /// Returns the transaction hash to fork from - pub fn transaction_hash(&self) -> Option { + pub const fn transaction_hash(&self) -> Option { match self { Self::Block(_) => None, Self::Transaction(transaction_hash) => Some(*transaction_hash), @@ -1551,12 +1624,12 @@ pub struct PruneStateHistoryConfig { impl PruneStateHistoryConfig { /// Returns `true` if writing state history is supported - pub fn is_state_history_supported(&self) -> bool { + pub const fn is_state_history_supported(&self) -> bool { !self.enabled || self.max_memory_history.is_some() } /// Returns true if this setting was enabled. - pub fn is_config_enabled(&self) -> bool { + pub const fn is_config_enabled(&self) -> bool { self.enabled } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index f48a2d96cc12c..0a0fd97d06556 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -416,7 +416,7 @@ impl EthApi { let config = fork.config.read(); NodeForkConfig { - fork_url: Some(config.eth_rpc_url.clone()), + fork_url: config.eth_rpc_url().map(|s| s.to_string()), fork_block_number: Some(config.block_number), fork_retry_backoff: Some(config.backoff.as_millis()), } @@ -527,7 +527,7 @@ impl EthApi { /// Sets the backend rpc url /// /// Handler for ETH RPC call: `anvil_setRpcUrl` - pub fn anvil_set_rpc_url(&self, url: String) -> Result<()> { + pub async fn anvil_set_rpc_url(&self, url: String) -> Result<()> { node_info!("anvil_setRpcUrl"); if let Some(fork) = self.backend.get_fork() { let mut config = fork.config.write(); @@ -543,9 +543,11 @@ impl EthApi { )?, // .interval(interval), ); config.provider = new_provider; - trace!(target: "backend", "Updated fork rpc from \"{}\" to \"{}\"", config.eth_rpc_url, url); - config.eth_rpc_url = url; + trace!(target: "backend", "Updated fork rpc from \"{}\" to \"{}\"", config.eth_rpc_url().unwrap_or("none"), url); + config.fork_urls = vec![url.clone()]; } + // Keep node_config in sync so anvil_reset(None) uses the updated URL + self.backend.node_config.write().await.fork_urls = vec![url]; Ok(()) } @@ -1791,7 +1793,7 @@ impl EthApi { EthRequest::EvmMineDetailed(mine) => { self.evm_mine_detailed(mine.and_then(|p| p.params)).await.to_rpc_result() } - EthRequest::SetRpcUrl(url) => self.anvil_set_rpc_url(url).to_rpc_result(), + EthRequest::SetRpcUrl(url) => self.anvil_set_rpc_url(url).await.to_rpc_result(), EthRequest::EthSendUnsignedTransaction(tx) => { self.eth_send_unsigned_transaction(*tx).await.to_rpc_result() } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 14691862a225e..71764468fab99 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -83,7 +83,7 @@ where /// Helper trait to reset the DB if it's forked pub trait MaybeForkedDatabase { - fn maybe_reset(&mut self, _url: Option, block_number: BlockId) -> Result<(), String>; + fn maybe_reset(&mut self, _urls: Vec, block_number: BlockId) -> Result<(), String>; fn maybe_flush_cache(&self) -> Result<(), String>; @@ -301,7 +301,7 @@ impl + Debug> MaybeFullDatabase for CacheD } impl> MaybeForkedDatabase for CacheDB { - fn maybe_reset(&mut self, _url: Option, _block_number: BlockId) -> Result<(), String> { + fn maybe_reset(&mut self, _urls: Vec, _block_number: BlockId) -> Result<(), String> { Err("not supported".to_string()) } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index b1bff4e66cd21..bc87fe2fc2052 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -97,8 +97,8 @@ impl ClientFork { self.config.read().block_hash } - pub fn eth_rpc_url(&self) -> String { - self.config.read().eth_rpc_url.clone() + pub fn eth_rpc_url(&self) -> Option { + self.config.read().eth_rpc_url().map(|s| s.to_string()) } pub fn chain_id(&self) -> u64 { @@ -269,7 +269,7 @@ impl ClientFork { /// Reset the fork to a fresh forked state, and optionally update the fork config pub async fn reset( &self, - url: Option, + urls: Vec, block_number: impl Into, ) -> Result<(), BlockchainError> { let block_number = block_number.into(); @@ -277,12 +277,12 @@ impl ClientFork { self.database .write() .await - .maybe_reset(url.clone(), block_number) + .maybe_reset(urls.clone(), block_number) .map_err(BlockchainError::Internal)?; } - if let Some(url) = url { - self.config.write().update_url(url)?; + if !urls.is_empty() { + self.config.write().update_urls(urls)?; let override_chain_id = self.config.read().override_chain_id; let chain_id = if let Some(chain_id) = override_chain_id { chain_id @@ -629,7 +629,10 @@ impl ClientFork { /// Contains all fork metadata #[derive(Clone, Debug)] pub struct ClientForkConfig { - pub eth_rpc_url: String, + /// All fork URLs. The first entry is the primary endpoint. + /// When multiple URLs are present, requests are distributed using + /// round-robin load balancing with retry-based failover. + pub fork_urls: Vec, /// The block number of the forked block pub block_number: u64, /// The hash of the forked block @@ -655,6 +658,8 @@ pub struct ClientForkConfig { pub backoff: Duration, /// available CUPS pub compute_units_per_second: u64, + /// Headers to include with RPC requests + pub headers: Vec, /// total difficulty of the chain until this block pub total_difficulty: U256, /// Transactions to force include in the forked chain @@ -662,27 +667,40 @@ pub struct ClientForkConfig { } impl ClientForkConfig { - /// Updates the provider URL + /// Returns the primary RPC URL (first entry in `fork_urls`). + pub fn eth_rpc_url(&self) -> Option<&str> { + self.fork_urls.first().map(|s| s.as_str()) + } + + /// Updates the provider URLs /// /// # Errors /// /// This will fail if no new provider could be established (erroneous URL) - fn update_url(&mut self, url: String) -> Result<(), BlockchainError> { - // let interval = self.provider.get_interval(); - self.provider = Arc::new( - ProviderBuilder::::new(url.as_str()) - .timeout(self.timeout) - // .timeout_retry(self.retries) - .max_retry(self.retries) - .initial_backoff(self.backoff.as_millis() as u64) - .compute_units_per_second(self.compute_units_per_second) - .build() - .map_err(|e| BlockchainError::InvalidUrl(format!("{url}: {e}")))?, /* .interval(interval), */ - ); - trace!(target: "fork", "Updated rpc url {}", url); - self.eth_rpc_url = url; + fn update_urls(&mut self, urls: Vec) -> Result<(), BlockchainError> { + let primary = urls.first().ok_or_else(|| { + BlockchainError::InvalidUrl("at least one fork URL required".to_string()) + })?; + + let builder = ProviderBuilder::::new(primary.as_str()) + .timeout(self.timeout) + .max_retry(self.retries) + .initial_backoff(self.backoff.as_millis() as u64) + .compute_units_per_second(self.compute_units_per_second) + .headers(self.headers.clone()); + + self.provider = Arc::new(if urls.len() > 1 { + builder + .build_fallback(urls.clone()) + .map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))? + } else { + builder.build().map_err(|e| BlockchainError::InvalidUrl(format!("{primary}: {e}")))? + }); + trace!(target: "fork", "Updated fork urls: {:?}", urls); + self.fork_urls = urls; Ok(()) } + /// Updates the block forked off `(block number, block hash, timestamp)` pub fn update_block( &mut self, diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 308cdd2e4334c..497e8ac8e4428 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -156,8 +156,8 @@ impl MaybeFullDatabase for ForkDbStateSnapshot { } impl MaybeForkedDatabase for ForkedDatabase { - fn maybe_reset(&mut self, url: Option, block_number: BlockId) -> Result<(), String> { - self.reset(url, block_number) + fn maybe_reset(&mut self, urls: Vec, block_number: BlockId) -> Result<(), String> { + self.reset(urls, block_number) } fn maybe_flush_cache(&self) -> Result<(), String> { 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 766805ee7ae9d..1d06ad03cae2f 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -128,7 +128,7 @@ impl MaybeFullDatabase for MemDb { } impl MaybeForkedDatabase for MemDb { - fn maybe_reset(&mut self, _url: Option, _block_number: BlockId) -> Result<(), String> { + fn maybe_reset(&mut self, _urls: Vec, _block_number: BlockId) -> Result<(), String> { Err("not supported".to_string()) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index ed928ef25213d..b538086fe6f10 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -246,7 +246,7 @@ pub struct Backend { prune_state_history_config: PruneStateHistoryConfig, /// max number of blocks with transactions in memory transaction_block_keeper: Option, - node_config: Arc>, + pub(crate) node_config: Arc>, /// Slots in an epoch slots_in_an_epoch: u64, /// Precompiles to inject to the EVM. @@ -2079,6 +2079,7 @@ impl Backend { // we want to force the correct base fee for the next block during // `setup_fork_db_config` node_config.base_fee.take(); + node_config.fork_urls = vec![eth_rpc_url.clone()]; node_config.setup_fork_db_config(eth_rpc_url, &mut evm_env, &self.fees).await? }; @@ -2101,7 +2102,9 @@ impl Backend { let block_number = forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest); // reset the fork entirely and reapply the genesis config - fork.reset(forking.json_rpc_url.clone(), block_number).await?; + let reset_urls = + forking.json_rpc_url.as_ref().map(|url| vec![url.clone()]).unwrap_or_default(); + fork.reset(reset_urls, block_number).await?; let fork_block_number = fork.block_number(); let fork_block = fork .block_by_number(fork_block_number) @@ -2115,7 +2118,8 @@ impl Backend { // If rpc url is unspecified, then update the fork with the new block number and // existing rpc url, this updates the cache path { - let maybe_fork_url = { self.node_config.read().await.eth_rpc_url.clone() }; + let maybe_fork_url = + { self.node_config.read().await.fork_urls.first().cloned() }; if let Some(fork_url) = maybe_fork_url { self.reset_block_number(fork_url, fork_block_number).await?; } @@ -2229,6 +2233,8 @@ impl Backend { ) -> Result<(), BlockchainError> { let mut node_config = self.node_config.write().await; node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128)); + // Update fork_urls so setup_fork_db_config uses the correct URL set + node_config.fork_urls = vec![fork_url.clone()]; let mut evm_env = self.evm_env.read().clone(); let (forked_db, client_fork_config) = diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index e802d8a19be7b..37d3f6f4a1dc4 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -2011,3 +2011,61 @@ async fn test_config_with_osaka_hardfork_with_precompile_factory() { &expected_system_contracts, ); } + +// Regression tests: verify that `anvil_setRpcUrl` and `anvil_reset` keep +// `ClientForkConfig.fork_urls` in sync so that subsequent resets don't +// silently revert to stale URLs. + +#[tokio::test(flavor = "multi_thread")] +async fn test_anvil_set_rpc_url_syncs_fork_config() { + // Spawn an origin node and fork off it + let (_origin_api, origin_handle) = spawn(NodeConfig::test()).await; + let origin_url = origin_handle.http_endpoint(); + + let (api, _handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_url.clone()))).await; + + // Verify initial fork URL + let fork = api.backend.get_fork().unwrap(); + assert_eq!(fork.config.read().fork_urls, vec![origin_url.clone()]); + + // Spawn a second origin to use as the new URL + let (_origin2_api, origin2_handle) = spawn(NodeConfig::test()).await; + let new_url = origin2_handle.http_endpoint(); + + // Set RPC URL via the API + api.anvil_set_rpc_url(new_url.clone()).await.unwrap(); + + // Verify ClientForkConfig is updated + let fork = api.backend.get_fork().unwrap(); + assert_eq!( + fork.config.read().fork_urls, + vec![new_url.clone()], + "ClientForkConfig.fork_urls should be updated after anvil_setRpcUrl" + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_anvil_reset_with_url_updates_fork_urls() { + // Spawn an origin node and fork off it + let (_origin_api, origin_handle) = spawn(NodeConfig::test()).await; + let origin_url = origin_handle.http_endpoint(); + + let (api, _handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_url.clone()))).await; + + // Spawn a second origin + let (_origin2_api, origin2_handle) = spawn(NodeConfig::test()).await; + let new_url = origin2_handle.http_endpoint(); + + // Reset fork with a new URL + api.anvil_reset(Some(Forking { json_rpc_url: Some(new_url.clone()), block_number: None })) + .await + .unwrap(); + + // Verify the fork config uses the new URL, not the old one + let fork = api.backend.get_fork().unwrap(); + assert_eq!( + fork.config.read().fork_urls, + vec![new_url.clone()], + "ClientForkConfig.fork_urls should reflect the new URL after anvil_reset" + ); +} diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index dbd89ce88827d..56164c8f6367a 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -28,7 +28,7 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-debugger.workspace = true foundry-evm.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } forge-fmt.workspace = true alloy-chains.workspace = true diff --git a/crates/cast/src/call_spec.rs b/crates/cast/src/call_spec.rs index b39e024f38af5..0047b56d25f03 100644 --- a/crates/cast/src/call_spec.rs +++ b/crates/cast/src/call_spec.rs @@ -145,8 +145,8 @@ impl FromStr for CallSpec { /// Parse a value string that can be in ether notation (e.g., "0.1ether") or raw wei. fn parse_ether_or_wei(s: &str) -> Result { // Use alloy's DynSolType coercion which handles "1ether", "1gwei", "1000" etc. - if s.starts_with("0x") { - U256::from_str_radix(s, 16).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) + if s.starts_with("0x") || s.starts_with("0X") { + U256::from_str(s).map_err(|e| eyre!("Invalid hex value '{}': {}", s, e)) } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), s) .wrap_err_with(|| format!("Invalid value '{s}'"))? @@ -180,6 +180,12 @@ mod tests { assert!(spec.sig.is_none()); } + #[test] + fn test_parse_hex_value() { + assert_eq!(parse_ether_or_wei("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_or_wei("0X10").unwrap(), U256::from(16)); + } + #[test] fn test_parse_with_sig() { let spec = CallSpec::parse( diff --git a/crates/cast/src/cmd/erc20.rs b/crates/cast/src/cmd/erc20.rs index e629b8eca0d5a..27e33ec03e7e7 100644 --- a/crates/cast/src/cmd/erc20.rs +++ b/crates/cast/src/cmd/erc20.rs @@ -3,13 +3,13 @@ use std::{str::FromStr, time::Duration}; use crate::{ cmd::send::{cast_send, cast_send_with_access_key}, format_uint_exp, - tx::{SendTxOpts, TxParams}, + tx::{CastTxSender, SendTxOpts, TxParams}, }; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::BlockId; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network}; -use alloy_primitives::U256; +use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder}; +use alloy_primitives::{Address, U256}; use alloy_provider::{Provider, fillers::RecommendedFillers}; use alloy_signer::Signature; use alloy_sol_types::sol; @@ -23,11 +23,13 @@ use foundry_common::{ fmt::{UIfmt, UIfmtReceiptExt}, provider::{ProviderBuilder, RetryProviderWithSigner}, shell, + tempo::TEMPO_BROWSER_GAS_BUFFER, }; #[doc(hidden)] pub use foundry_config::{Chain, utils::*}; use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; +use tempo_contracts::precompiles::PATH_USD_ADDRESS; sol! { #[sol(rpc)] @@ -281,6 +283,34 @@ impl Erc20Subcommand { } } + const fn uses_browser_send(&self) -> bool { + match self { + Self::Transfer { send_tx, .. } + | Self::Approve { send_tx, .. } + | Self::Mint { send_tx, .. } + | Self::Burn { send_tx, .. } => send_tx.browser.browser, + _ => false, + } + } + + async fn should_use_tempo_network( + &self, + tempo_access_key: &Option, + ) -> eyre::Result { + if self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) + || tempo_access_key.is_some() + { + return Ok(true); + } + + if self.uses_browser_send() { + let config = self.rpc_opts().load_config()?; + return Ok(get_chain(config.chain, &get_provider(&config)?).await?.is_tempo()); + } + + Ok(false) + } + pub async fn run(self) -> eyre::Result<()> { // Resolve the signer once for state-changing variants. let (signer, tempo_access_key) = match &self { @@ -299,8 +329,7 @@ impl Erc20Subcommand { _ => (None, None), }; - let is_tempo = self.erc20_opts().is_some_and(|erc20| erc20.tempo.is_tempo()) - || tempo_access_key.is_some(); + let is_tempo = self.should_use_tempo_network(&tempo_access_key).await?; if is_tempo { self.run_generic::(signer, tempo_access_key).await @@ -356,6 +385,28 @@ impl Erc20Subcommand { timeout, ) .await? + } else if let Some(browser) = $send_tx.browser.run::().await? { + let $provider = ProviderBuilder::::from_config(&config)?.build()?; + if let Some(interval) = $send_tx.poll_interval { + $provider.client().set_poll_interval(Duration::from_secs(interval)); + } + let $erc20 = IERC20::new($token.resolve(&$provider).await?, &$provider); + let mut tx = { $build_tx }.into_transaction_request(); + let chain = get_chain(config.chain, &$provider).await?; + $tx_opts.apply::(&mut tx, chain.is_legacy()); + if chain.is_tempo() && tx.fee_token().is_none() { + tx.set_fee_token(PATH_USD_ADDRESS); + } + fill_tx(&$provider, &mut tx, browser.address(), chain).await?; + let tx_hash = browser.send_transaction_via_browser(tx).await?; + CastTxSender::new(&$provider) + .print_tx_result( + tx_hash, + $send_tx.cast_async, + $send_tx.confirmations, + timeout, + ) + .await? } else { let signer = pre_resolved_signer.unwrap_or($send_tx.eth.wallet.signer().await?); let $provider = build_provider_with_signer::(&$send_tx, signer)?; @@ -503,3 +554,55 @@ impl Erc20Subcommand { Ok(()) } } + +/// Fills from, chain_id, nonce, fees, and gas limit on a transaction request for the browser +/// wallet path. Mirrors the filling logic in the shared tx builder but operates on a +/// pre-built transaction request from the sol! macro rather than through the builder pipeline. +/// Only fills fields that haven't already been set by the user. +async fn fill_tx>( + provider: &P, + tx: &mut N::TransactionRequest, + from: Address, + chain: Chain, +) -> eyre::Result<()> +where + N::TransactionRequest: FoundryTransactionBuilder, +{ + tx.set_from(from); + tx.set_chain_id(chain.id()); + + if tx.nonce().is_none() { + tx.set_nonce(provider.get_transaction_count(from).await?); + } + + let legacy = chain.is_legacy(); + + if legacy { + if tx.gas_price().is_none() { + tx.set_gas_price(provider.get_gas_price().await?); + } + } else if tx.max_fee_per_gas().is_none() || tx.max_priority_fee_per_gas().is_none() { + let estimate = provider.estimate_eip1559_fees().await?; + if tx.max_fee_per_gas().is_none() { + tx.set_max_fee_per_gas(estimate.max_fee_per_gas); + } + if tx.max_priority_fee_per_gas().is_none() { + tx.set_max_priority_fee_per_gas(estimate.max_priority_fee_per_gas); + } + } + + if tx.gas_limit().is_none() { + let mut estimated = provider.estimate_gas(tx.clone()).await?; + + // Browser wallets may sign with P256/WebAuthn instead of secp256k1, which + // costs more gas for signature verification on Tempo chains. Add a + // conservative buffer since we can't determine the signature type beforehand. + if chain.is_tempo() { + estimated += TEMPO_BROWSER_GAS_BUFFER; + } + + tx.set_gas_limit(estimated); + } + + Ok(()) +} diff --git a/crates/cast/src/cmd/send.rs b/crates/cast/src/cmd/send.rs index 08c535bef8a18..2d6e248cc7a73 100644 --- a/crates/cast/src/cmd/send.rs +++ b/crates/cast/src/cmd/send.rs @@ -13,6 +13,7 @@ use foundry_common::{ FoundryTransactionBuilder, fmt::{UIfmt, UIfmtReceiptExt}, provider::ProviderBuilder, + tempo::TEMPO_BROWSER_GAS_BUFFER, }; use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; use tempo_alloy::TempoNetwork; @@ -258,7 +259,18 @@ impl SendTxArgs { // Case 2: // Browser wallet signs and sends the transaction in one step. } else if let Some(browser) = browser { - let (tx_request, _) = builder.build(browser.address()).await?; + let chain = builder.chain(); + let (mut tx_request, _) = builder.build(browser.address()).await?; + + // Browser wallets may sign with P256/WebAuthn instead of secp256k1, which + // costs more gas for signature verification on Tempo chains. Add a + // conservative buffer since we can't determine the signature type beforehand. + if chain.is_tempo() + && let Some(gas) = tx_request.gas_limit() + { + tx_request.set_gas_limit(gas + TEMPO_BROWSER_GAS_BUFFER); + } + let tx_hash = browser.send_transaction_via_browser(tx_request).await?; let cast = CastTxSender::new(&provider); diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 131c96e6f6c17..184289a399450 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -398,6 +398,13 @@ pub struct CastTxBuilder { state: S, } +impl CastTxBuilder { + /// Returns the resolved chain for this builder. + pub const fn chain(&self) -> Chain { + self.chain + } +} + impl> CastTxBuilder where N::TransactionRequest: FoundryTransactionBuilder, diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 165154da88f63..bcbe837e824e3 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,7 +19,7 @@ foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-evm-networks.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } tempo-primitives.workspace = true diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index f1c6c435217b7..be566288ef87a 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -129,8 +129,8 @@ where /// If the string represents an untagged amount (e.g. "100") then /// it is interpreted as wei. pub fn parse_ether_value(value: &str) -> Result { - Ok(if value.starts_with("0x") { - U256::from_str_radix(value, 16)? + Ok(if value.starts_with("0x") || value.starts_with("0X") { + U256::from_str(value)? } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), value)? .as_uint() @@ -844,6 +844,16 @@ mod tests { assert!(!p.is_sol_test()); } + #[test] + fn parse_ether_value_accepts_hex_prefixed_wei() { + assert_eq!(parse_ether_value("0x10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0X10").unwrap(), U256::from(16)); + assert_eq!(parse_ether_value("0x12").unwrap(), U256::from(0x12)); + assert_eq!(parse_ether_value("0xff").unwrap(), U256::from(0xff)); + assert_eq!(parse_ether_value("100").unwrap(), U256::from(100)); + assert_eq!(parse_ether_value("1ether").unwrap(), U256::from(1000000000000000000u128)); + } + // loads .env from cwd and project dir, See [`find_project_root()`] #[test] fn can_load_dotenv() { diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 706527093cc8e..6697aff36e4f8 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -83,6 +83,7 @@ flate2.workspace = true tempo-alloy.workspace = true tempo-primitives.workspace = true mpp.workspace = true +foundry-wallets.workspace = true [build-dependencies] chrono.workspace = true diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 78759848fc4bd..2c8e16bccdcc6 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -30,6 +30,7 @@ serde_json.workspace = true chrono.workspace = true revm.workspace = true yansi.workspace = true +comfy-table.workspace = true # Tempo tempo-alloy.workspace = true diff --git a/crates/common/fmt/src/console.rs b/crates/common/fmt/src/console.rs index d751ddba245ba..8b66335445046 100644 --- a/crates/common/fmt/src/console.rs +++ b/crates/common/fmt/src/console.rs @@ -1,5 +1,6 @@ use super::UIfmt; use alloy_primitives::{Address, Bytes, FixedBytes, I256, U256}; +use comfy_table::{Table, TableComponent, presets::UTF8_FULL}; use std::fmt::{self, Write}; /// A piece is a portion of the format string which represents the next part to emit. @@ -407,6 +408,36 @@ fn format_spec<'a>( } } +pub fn console_table_format( + keys: Option<&[&dyn ConsoleFmt]>, + values: &[&dyn ConsoleFmt], +) -> String { + let keys_strings: Vec = match keys { + Some(keys) => keys.iter().map(|k| k.fmt(FormatSpec::String)).collect(), + None => (0..values.len()).map(|i| i.to_string()).collect(), + }; + let values_strings: Vec = values.iter().map(|v| v.fmt(FormatSpec::String)).collect(); + + let mut table = Table::new(); + table.load_preset(UTF8_FULL); + table.set_style(TableComponent::VerticalLines, '│'); + table.set_style(TableComponent::HeaderLines, '─'); + table.set_style(TableComponent::MiddleHeaderIntersections, '┼'); + table.set_style(TableComponent::LeftHeaderIntersection, '├'); + table.set_style(TableComponent::RightHeaderIntersection, '┤'); + table.set_header(vec!["(index)", "Values"]); + table.remove_style(TableComponent::HorizontalLines); + table.remove_style(TableComponent::MiddleIntersections); + table.remove_style(TableComponent::LeftBorderIntersections); + table.remove_style(TableComponent::RightBorderIntersections); + for i in 0..keys_strings.len().max(values_strings.len()) { + let key = keys_strings.get(i).map(String::as_str).unwrap_or(""); + let value = values_strings.get(i).map(String::as_str).unwrap_or(""); + table.add_row(vec![key, value]); + } + table.to_string() +} + #[cfg(test)] mod tests { use super::*; @@ -610,4 +641,80 @@ mod tests { let call = Logs::Log1(log1); assert_eq!(call.fmt(Default::default()), "foo 42 bar"); } + + #[test] + fn test_console_table_format() { + // auto-indexed, uint256 values + let values: &[&dyn ConsoleFmt] = &[&U256::from(100), &U256::from(200), &U256::from(300)]; + assert_eq!( + console_table_format(None, values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ 0 │ 100 │\n\ + │ 1 │ 200 │\n\ + │ 2 │ 300 │\n\ + └─────────┴────────┘" + ); + + // string keys, uint256 values + // key col expands to fit "charlie123" and value col expands to fit "20000000000000000" + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie123")]; + let values: &[&dyn ConsoleFmt] = &[ + &U256::from(1), + &U256::from_str("20000000000000000").unwrap(), + &U256::from_str("30000000000").unwrap(), + ]; + assert_eq!( + console_table_format(Some(keys), values), + "┌────────────┬───────────────────┐\n\ + │ (index) │ Values │\n\ + ├────────────┼───────────────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 20000000000000000 │\n\ + │ charlie123 │ 30000000000 │\n\ + └────────────┴───────────────────┘" + ); + + // empty table + assert_eq!( + console_table_format(None, &[]), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + └─────────┴────────┘" + ); + + // more keys than values + let keys: &[&dyn ConsoleFmt] = + &[&String::from("alice"), &String::from("bob"), &String::from("charlie")]; + let values: &[&dyn ConsoleFmt] = &[&U256::from(1), &U256::from(2)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ charlie │ │\n\ + └─────────┴────────┘" + ); + + // more values than keys + let keys: &[&dyn ConsoleFmt] = &[&String::from("alice"), &String::from("bob")]; + let values: &[&dyn ConsoleFmt] = + &[&U256::from(1), &U256::from(2), &U256::from(3), &U256::from(4)]; + assert_eq!( + console_table_format(Some(keys), values), + "┌─────────┬────────┐\n\ + │ (index) │ Values │\n\ + ├─────────┼────────┤\n\ + │ alice │ 1 │\n\ + │ bob │ 2 │\n\ + │ │ 3 │\n\ + │ │ 4 │\n\ + └─────────┴────────┘" + ); + } } diff --git a/crates/common/fmt/src/lib.rs b/crates/common/fmt/src/lib.rs index 89297a8e2c6b5..45ed47263ce32 100644 --- a/crates/common/fmt/src/lib.rs +++ b/crates/common/fmt/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] mod console; -pub use console::{ConsoleFmt, FormatSpec, console_format}; +pub use console::{ConsoleFmt, FormatSpec, console_format, console_table_format}; mod dynamic; pub use dynamic::{ diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 3b7432cd67c55..1620342989a29 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -9,6 +9,7 @@ use crate::{ provider::{curl_transport::CurlTransport, runtime_transport::RuntimeTransportBuilder}, }; use alloy_chains::NamedChain; +use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_network::{Network, NetworkWallet}; use alloy_provider::{ Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, @@ -16,7 +17,9 @@ use alloy_provider::{ network::{AnyNetwork, EthereumWallet}, }; use alloy_rpc_client::ClientBuilder; -use alloy_transport::{layers::RetryBackoffLayer, utils::guess_local_url}; +use alloy_transport::{ + TransportError, TransportFut, layers::RetryBackoffLayer, utils::guess_local_url, +}; use eyre::{Result, WrapErr}; use foundry_config::Config; use reqwest::Url; @@ -25,8 +28,14 @@ use std::{ net::SocketAddr, path::{Path, PathBuf}, str::FromStr, + sync::{ + Arc, + atomic::{AtomicUsize, Ordering}, + }, + task::{Context, Poll}, time::Duration, }; +use tower::Service; use url::ParseError; /// The assumed block time for unknown chains. @@ -75,6 +84,56 @@ pub fn try_get_http_provider(builder: impl AsRef) -> Result ProviderBuilder::new(builder.as_ref()).build() } +/// A round-robin transport that distributes requests across multiple transports. +/// +/// Each request is sent to exactly one transport, rotating through the list. +/// Failover on error is handled by the retry layer above this service. +#[derive(Clone)] +pub struct RoundRobinService { + transports: Arc>, + next: Arc, +} + +impl RoundRobinService { + /// Creates a new round-robin service from a non-empty list of transports. + /// + /// # Panics + /// + /// Panics if `transports` is empty. + pub fn new(transports: Vec) -> Self { + assert!(!transports.is_empty(), "RoundRobinService requires at least one transport"); + Self { transports: Arc::new(transports), next: Arc::new(AtomicUsize::new(0)) } + } +} + +impl Service for RoundRobinService +where + S: Service< + RequestPacket, + Response = ResponsePacket, + Error = TransportError, + Future = TransportFut<'static>, + > + Clone + + Send + + Sync + + 'static, +{ + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: RequestPacket) -> Self::Future { + let transports = self.transports.clone(); + let idx = self.next.fetch_add(1, Ordering::Relaxed) % transports.len(); + let mut transport = transports[idx].clone(); + transport.call(req) + } +} + /// Helper type to construct a `RetryProvider` /// /// This builder is generic over the network type `N`, defaulting to `AnyNetwork`. @@ -368,6 +427,73 @@ impl ProviderBuilder { } impl ProviderBuilder { + /// Constructs a `RetryProvider` backed by multiple URLs using round-robin load balancing. + /// + /// Each request is sent to exactly one transport, rotating through the list via + /// [`RoundRobinService`]. There is no health scoring or endpoint deprioritization. + /// On failure, the `RetryBackoffLayer` retries the request, which naturally hits + /// the next transport in the rotation. + pub fn build_fallback(self, urls: Vec) -> Result> { + let Self { + chain, + max_retry, + initial_backoff, + timeout, + compute_units_per_second, + jwt, + headers, + accept_invalid_certs, + no_proxy, + curl_mode, + .. + } = self; + + eyre::ensure!(!urls.is_empty(), "at least one fork URL is required"); + eyre::ensure!(!curl_mode, "curl mode is not supported with multiple fork URLs"); + + // Build a RuntimeTransport for each URL, using the same URL normalization + // as ProviderBuilder::new() (handles localhost:port, raw socket addrs, IPC paths) + let mut parsed_urls = Vec::with_capacity(urls.len()); + let transports: Vec<_> = urls + .iter() + .map(|url_str| { + let builder = Self::new(url_str); + let url = builder.url?; + parsed_urls.push(url.clone()); + Ok(RuntimeTransportBuilder::new(url) + .with_timeout(timeout) + .with_headers(headers.clone()) + .with_jwt(jwt.clone()) + .accept_invalid_certs(accept_invalid_certs) + .no_proxy(no_proxy) + .build()) + }) + .collect::>>()?; + + let round_robin = RoundRobinService::new(transports); + + let retry_layer = + RetryBackoffLayer::new(max_retry, initial_backoff, compute_units_per_second); + // Use normalized/parsed URLs for local detection, consistent with build() + let is_local = parsed_urls.iter().all(|url| guess_local_url(url.as_str())); + let client = ClientBuilder::default().layer(retry_layer).transport(round_robin, is_local); + + if !is_local { + client.set_poll_interval( + chain + .average_blocktime_hint() + .map(|hint| hint.min(DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME)) + .unwrap_or(DEFAULT_UNKNOWN_CHAIN_BLOCK_TIME) + .mul_f32(POLL_INTERVAL_BLOCK_TIME_SCALE_FACTOR), + ); + } + + let provider = + AlloyProviderBuilder::<_, _, N>::default().connect_provider(RootProvider::new(client)); + + Ok(provider) + } + /// Constructs the `RetryProvider` with a wallet. pub fn build_with_wallet + Clone>( self, diff --git a/crates/common/src/provider/mpp/persist.rs b/crates/common/src/provider/mpp/persist.rs index 6d6781ab4c77b..803c7270e041e 100644 --- a/crates/common/src/provider/mpp/persist.rs +++ b/crates/common/src/provider/mpp/persist.rs @@ -1,243 +1,241 @@ //! Persistent channel storage for MPP sessions. //! -//! Stores open payment channel state in a JSON file at -//! `$TEMPO_HOME/foundry/channels.json` (default: `~/.tempo/foundry/channels.json`). +//! Stores open payment channel state in a SQLite database at +//! `$TEMPO_HOME/channels.db` (default: `~/.tempo/channels.db`). //! This allows channel reuse across process invocations, avoiding the cost of //! opening a new on-chain channel for every `cast` / `forge` command. use alloy_primitives::{Address, B256}; +use foundry_wallets::{Channel, ChannelDb}; use mpp::client::channel_ops::ChannelEntry; -use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, - path::PathBuf, + sync::OnceLock, time::{SystemTime, UNIX_EPOCH}, }; use tracing::{debug, warn}; use crate::tempo::tempo_home; -/// Relative path from Tempo home to the Foundry channels file. -const CHANNELS_PATH: &str = "foundry/channels.json"; +/// Process-wide database handle. +fn global_db() -> Option<&'static ChannelDb> { + static DB: OnceLock> = OnceLock::new(); + DB.get_or_init(|| { + let path = tempo_home()?.join("channels.db"); + if let Some(parent) = path.parent() { + let _ = std::fs::create_dir_all(parent); + } + if let Some(old) = + tempo_home().map(|h| h.join("foundry/channels.json")).filter(|p| p.exists()) + { + warn!( + ?old, + "found old channels.json — this file is no longer used; channels will be re-opened" + ); + } -/// Current schema version. -const SCHEMA_VERSION: u64 = 2; + match ChannelDb::open(&path) { + Ok(db) => { + debug!(?path, "opened channel database"); + Some(db) + } + Err(e) => { + warn!(?path, %e, "failed to open channel database"); + None + } + } + }) + .as_ref() +} -/// On-disk representation of the channel store. -#[derive(Debug, Serialize, Deserialize)] -struct ChannelStore { - version: u64, - #[serde(default)] - channels: HashMap, +/// Reconstruct the composite HashMap key from a persisted `Channel`. +/// +/// Mirrors `SessionProvider::channel_key()` in session.rs. +fn channel_key_from_persisted(ch: &Channel) -> String { + let origin_hash = &alloy_primitives::keccak256(ch.origin.as_bytes()).to_string()[..18]; + format!( + "{}:{}:{}:{}:{}:{}:{}", + origin_hash, + ch.chain_id, + ch.payer, + ch.authorized_signer, + ch.payee, + ch.token, + ch.escrow_contract + ) + .to_lowercase() } -impl Default for ChannelStore { - fn default() -> Self { - Self { version: SCHEMA_VERSION, channels: HashMap::new() } +/// Whether a channel can still be used (active and not fully spent). +fn is_usable(ch: &Channel) -> bool { + if ch.state != "active" { + return false; } + let cumulative: u128 = ch.cumulative_amount.parse().unwrap_or(u128::MAX); + let deposit: u128 = ch.deposit.parse().unwrap_or(0); + cumulative < deposit } -/// A persisted channel entry. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PersistedChannel { - pub channel_id: String, - pub salt: String, - pub escrow_contract: String, - pub chain_id: u64, - pub cumulative_amount: String, - pub deposit: String, - pub status: String, - pub origin: String, - pub created_at: u64, - pub last_used_at: u64, +/// Convert a persisted `Channel` to a `ChannelEntry`. +pub fn to_channel_entry(ch: &Channel) -> Option { + let channel_id: B256 = ch.channel_id.parse().ok()?; + let salt: B256 = ch.salt.parse().ok()?; + let escrow_contract: Address = ch.escrow_contract.parse().ok()?; + let cumulative_amount: u128 = ch.cumulative_amount.parse().ok()?; + + Some(ChannelEntry { + channel_id, + salt, + cumulative_amount, + escrow_contract, + chain_id: ch.chain_id as u64, + opened: ch.state == "active", + }) } -impl PersistedChannel { - /// Convert to an mpp `ChannelEntry` for use in the session provider. - pub fn to_channel_entry(&self) -> Option { - let channel_id: B256 = self.channel_id.parse().ok()?; - let salt: B256 = self.salt.parse().ok()?; - let escrow_contract: Address = self.escrow_contract.parse().ok()?; - let cumulative_amount: u128 = self.cumulative_amount.parse().ok()?; - - Some(ChannelEntry { - channel_id, - salt, - cumulative_amount, - escrow_contract, - chain_id: self.chain_id, - opened: self.status == "active", - }) - } - - /// Create from a `ChannelEntry` with metadata. - pub fn from_channel_entry(entry: &ChannelEntry, deposit: u128, origin: &str) -> Self { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - - Self { - channel_id: entry.channel_id.to_string(), - salt: entry.salt.to_string(), - escrow_contract: entry.escrow_contract.to_string(), - chain_id: entry.chain_id, - cumulative_amount: entry.cumulative_amount.to_string(), - deposit: deposit.to_string(), - status: if entry.opened { "active" } else { "closed" }.to_string(), - origin: origin.to_string(), - created_at: now, - last_used_at: now, - } - } - - /// Whether this channel can still be used (active and not fully spent). - fn is_usable(&self) -> bool { - if self.status != "active" { - return false; - } - let cumulative: u128 = self.cumulative_amount.parse().unwrap_or(u128::MAX); - let deposit: u128 = self.deposit.parse().unwrap_or(0); - cumulative < deposit +/// Create a `Channel` from a `ChannelEntry` with metadata. +pub fn from_channel_entry( + entry: &ChannelEntry, + deposit: u128, + origin: &str, + payer: &Address, + payee: &Address, + token: &Address, + authorized_signer: &Address, +) -> Channel { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; + + Channel { + channel_id: entry.channel_id.to_string(), + version: 1, + origin: origin.to_string(), + request_url: String::new(), + chain_id: entry.chain_id as i64, + escrow_contract: entry.escrow_contract.to_string(), + token: token.to_string(), + payee: payee.to_string(), + payer: payer.to_string(), + authorized_signer: authorized_signer.to_string(), + salt: entry.salt.to_string(), + deposit: deposit.to_string(), + cumulative_amount: entry.cumulative_amount.to_string(), + challenge_echo: String::new(), + state: if entry.opened { "active" } else { "closed" }.to_string(), + close_requested_at: 0, + grace_ready_at: 0, + created_at: now, + last_used_at: now, } } -/// Returns the path to the channels file. -fn channels_path() -> Option { - tempo_home().map(|home| home.join(CHANNELS_PATH)) -} - -/// Load channels from disk, evicting spent/inactive entries. -pub fn load_channels() -> HashMap { - let Some(path) = channels_path().filter(|p| p.exists()) else { +/// Load channels from database, evicting spent/inactive entries. +pub fn load_channels() -> HashMap { + let Some(db) = global_db() else { return HashMap::new(); }; - let Ok(contents) = std::fs::read_to_string(&path).inspect_err(|e| { - warn!(?path, %e, "failed to read channels file"); - }) else { - return HashMap::new(); - }; - - let Ok(store) = serde_json::from_str::(&contents).inspect_err(|e| { - warn!(?path, %e, "failed to parse channels file, starting fresh"); - }) else { - return HashMap::new(); + let channels = match db.load() { + Ok(channels) => channels, + Err(e) => { + warn!(%e, "failed to load channels from database"); + return HashMap::new(); + } }; - if store.version != SCHEMA_VERSION { - warn!( - version = store.version, - expected = SCHEMA_VERSION, - "channels file version mismatch, starting fresh" - ); - return HashMap::new(); - } - - // Evict spent/inactive entries - let usable: HashMap = - store.channels.into_iter().filter(|(_, ch)| ch.is_usable()).collect(); + let usable: HashMap = channels + .into_iter() + .filter(is_usable) + .map(|ch| { + let key = channel_key_from_persisted(&ch); + (key, ch) + }) + .collect(); debug!(count = usable.len(), "loaded persisted MPP channels"); usable } -/// Save channels to disk. -pub fn save_channels(channels: &HashMap) { - let Some(path) = channels_path() else { +/// Save channels to database. +pub fn save_channels(channels: &HashMap) { + let Some(db) = global_db() else { return; }; - if let Some(parent) = path.parent() - && let Err(e) = std::fs::create_dir_all(parent) - { - warn!(?path, %e, "failed to create channels directory"); - return; + for ch in channels.values() { + if let Err(e) = db.upsert(ch) { + warn!(%e, channel_id = %ch.channel_id, "failed to save channel"); + } } + debug!(count = channels.len(), "saved MPP channels"); +} - let store = ChannelStore { version: SCHEMA_VERSION, channels: channels.clone() }; - - match serde_json::to_string_pretty(&store) { - Ok(json) => { - if let Err(e) = std::fs::write(&path, json) { - warn!(?path, %e, "failed to write channels file"); - } else { - debug!(?path, count = channels.len(), "saved MPP channels"); - } - } - Err(e) => warn!(%e, "failed to serialize channels"), +/// Delete a channel from the database by its channel ID. +pub fn delete_channel_from_db(channel_id: &str) { + let Some(db) = global_db() else { + return; + }; + if let Err(e) = db.delete(channel_id) { + warn!(%e, channel_id, "failed to delete channel from database"); } } /// Look up a usable persisted channel by key. -pub fn find_channel( - channels: &HashMap, - key: &str, -) -> Option { - channels.get(key).filter(|ch| ch.is_usable()).and_then(|ch| ch.to_channel_entry()) +pub fn find_channel(channels: &HashMap, key: &str) -> Option { + channels.get(key).filter(|ch| is_usable(ch)).and_then(to_channel_entry) } -/// Insert or update a channel entry in memory only (no disk write). -/// -/// Use [`upsert_channel`] when you want to persist immediately, or call -/// [`save_channels`] separately after this. +/// Insert or update a channel entry in memory only (no DB write). pub fn upsert_channel_in_memory( - channels: &mut HashMap, + channels: &mut HashMap, key: &str, entry: &ChannelEntry, - deposit: u128, - origin: &str, ) { - let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64; if let Some(existing) = channels.get_mut(key) { existing.cumulative_amount = entry.cumulative_amount.to_string(); existing.last_used_at = now; - existing.status = if entry.opened { "active" } else { "closed" }.to_string(); + existing.state = if entry.opened { "active" } else { "closed" }.to_string(); } else { - channels - .insert(key.to_string(), PersistedChannel::from_channel_entry(entry, deposit, origin)); + warn!(key, "upsert_channel_in_memory called for unknown channel"); } } -/// Insert or update a channel entry and save to disk. -/// -/// When updating an existing entry, `deposit` is ignored (preserved from the -/// original open). When inserting a new entry, `deposit` is recorded. -pub fn upsert_channel( - channels: &mut HashMap, - key: &str, - entry: &ChannelEntry, - deposit: u128, - origin: &str, -) { - upsert_channel_in_memory(channels, key, entry, deposit, origin); - save_channels(channels); -} - #[cfg(test)] mod tests { use super::*; - fn test_channel(status: &str, cumulative: &str, deposit: &str) -> PersistedChannel { - PersistedChannel { + fn test_channel(state: &str, cumulative: &str, deposit: &str) -> Channel { + Channel { channel_id: format!("0x{}", "ab".repeat(32)), - salt: format!("0x{}", "cd".repeat(32)), - escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + version: 1, + origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + request_url: String::new(), chain_id: 42431, - cumulative_amount: cumulative.to_string(), + escrow_contract: "0xe1c4d3dce17bc111181ddf716f75bae49e61a336".to_string(), + token: "0x20c0000000000000000000000000000000000000".to_string(), + payee: "0x3333333333333333333333333333333333333333".to_string(), + payer: "0x1111111111111111111111111111111111111111".to_string(), + authorized_signer: "0x1111111111111111111111111111111111111111".to_string(), + salt: format!("0x{}", "cd".repeat(32)), deposit: deposit.to_string(), - status: status.to_string(), - origin: "https://rpc.mpp.moderato.tempo.xyz".to_string(), + cumulative_amount: cumulative.to_string(), + challenge_echo: String::new(), + state: state.to_string(), + close_requested_at: 0, + grace_ready_at: 0, created_at: 1000, last_used_at: 1000, } } #[test] - fn is_usable() { - assert!(test_channel("active", "5000", "100000").is_usable()); - assert!(!test_channel("active", "100000", "100000").is_usable()); - assert!(!test_channel("active", "200000", "100000").is_usable()); - assert!(!test_channel("closed", "0", "100000").is_usable()); - assert!(!test_channel("closing", "0", "100000").is_usable()); + fn usable() { + assert!(is_usable(&test_channel("active", "5000", "100000"))); + assert!(!is_usable(&test_channel("active", "100000", "100000"))); + assert!(!is_usable(&test_channel("active", "200000", "100000"))); + assert!(!is_usable(&test_channel("closed", "0", "100000"))); + assert!(!is_usable(&test_channel("closing", "0", "100000"))); } #[test] @@ -251,8 +249,12 @@ mod tests { opened: true, }; - let persisted = PersistedChannel::from_channel_entry(&entry, 100_000, "https://rpc.test"); - let restored = persisted.to_channel_entry().expect("should parse back"); + let payer = Address::random(); + let payee = Address::random(); + let token = Address::random(); + let persisted = + from_channel_entry(&entry, 100_000, "https://rpc.test", &payer, &payee, &token, &payer); + let restored = to_channel_entry(&persisted).expect("should parse back"); assert_eq!(restored.channel_id, entry.channel_id); assert_eq!(restored.salt, entry.salt); @@ -262,46 +264,6 @@ mod tests { assert!(restored.opened); } - #[test] - fn load_evicts_and_handles_edge_cases() { - let dir = tempfile::tempdir().unwrap(); - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - - let store = ChannelStore { - version: SCHEMA_VERSION, - channels: HashMap::from([ - ("active".into(), test_channel("active", "1000", "100000")), - ("spent".into(), test_channel("active", "100000", "100000")), - ("closed".into(), test_channel("closed", "0", "100000")), - ]), - }; - let json = serde_json::to_string(&store).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), &json).unwrap(); - - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - let loaded = load_channels(); - unsafe { std::env::remove_var("TEMPO_HOME") }; - - assert_eq!(loaded.len(), 1); - assert!(loaded.contains_key("active")); - } - - #[test] - fn load_missing_and_wrong_version() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - assert!(load_channels().is_empty()); - - let foundry_dir = dir.path().join("foundry"); - std::fs::create_dir_all(&foundry_dir).unwrap(); - std::fs::write(foundry_dir.join("channels.json"), r#"{"version": 999, "channels": {}}"#) - .unwrap(); - assert!(load_channels().is_empty()); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } - #[test] fn find_channel_filters_unusable() { let mut channels = HashMap::new(); @@ -312,34 +274,4 @@ mod tests { assert!(find_channel(&channels, "spent").is_none()); assert!(find_channel(&channels, "missing").is_none()); } - - #[test] - fn upsert_inserts_and_updates() { - let dir = tempfile::tempdir().unwrap(); - unsafe { std::env::set_var("TEMPO_HOME", dir.path()) }; - - let mut channels = HashMap::new(); - let entry = ChannelEntry { - channel_id: B256::random(), - salt: B256::random(), - cumulative_amount: 1000, - escrow_contract: Address::random(), - chain_id: 42431, - opened: true, - }; - - upsert_channel(&mut channels, "key1", &entry, 100_000, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "1000"); - assert_eq!(channels["key1"].deposit, "100000"); - let created_at = channels["key1"].created_at; - - let mut updated = entry.clone(); - updated.cumulative_amount = 5000; - upsert_channel(&mut channels, "key1", &updated, 0, "https://rpc.test"); - assert_eq!(channels["key1"].cumulative_amount, "5000"); - assert_eq!(channels["key1"].deposit, "100000"); - assert_eq!(channels["key1"].created_at, created_at); - - unsafe { std::env::remove_var("TEMPO_HOME") }; - } } diff --git a/crates/common/src/provider/mpp/session.rs b/crates/common/src/provider/mpp/session.rs index 5996c933218d2..334166b844613 100644 --- a/crates/common/src/provider/mpp/session.rs +++ b/crates/common/src/provider/mpp/session.rs @@ -6,8 +6,9 @@ //! `eth_getTransactionCount`. This avoids the chicken-and-egg problem when //! the RPC endpoint is itself 402-gated. -use super::persist::{self, PersistedChannel}; +use super::persist; use alloy_primitives::{Address, B256, Bytes, TxKind, U256}; +use foundry_wallets::Channel; use mpp::{ client::{ PaymentProvider, @@ -42,7 +43,7 @@ static GLOBAL_CHANNELS: OnceLock>> = O /// /// Using a single map ensures saves from different origins don't clobber /// each other's state. -static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); +static GLOBAL_PERSISTED: OnceLock>>> = OnceLock::new(); /// Tracks uncommitted channel state from the most recent payment. /// @@ -86,7 +87,7 @@ pub struct SessionProvider { default_deposit: Option, channels: Arc>>, key_provisioned: Arc>, - persisted: Arc>>, + persisted: Arc>>, /// Tracks uncommitted open/top-up state for deferred persistence. pending: Arc>>, /// Chain ID from the key entry in `keys.toml` that was used to initialize @@ -128,10 +129,10 @@ impl SessionProvider { map.entry(origin.clone()) .or_insert_with(|| { // Hydrate only channels belonging to this origin. - let mut channels = HashMap::new(); + let mut channels: HashMap = HashMap::new(); for (key, ch) in persisted.lock().unwrap().iter() { if ch.origin == origin - && let Some(entry) = ch.to_channel_entry() + && let Some(entry) = persist::to_channel_entry(ch) { channels.insert(key.clone(), entry); } @@ -209,16 +210,16 @@ impl SessionProvider { // Lock order: channels → persisted (consistent with pay_session) let mut channels = self.channels.lock().unwrap(); let mut persisted = self.persisted.lock().unwrap(); - let keys_to_remove: Vec = persisted + let keys_to_remove: Vec<(String, String)> = persisted .iter() .filter(|(_, ch)| ch.origin == *origin) - .map(|(k, _)| k.clone()) + .map(|(k, ch): (&String, &Channel)| (k.clone(), ch.channel_id.clone())) .collect(); - for key in &keys_to_remove { + for (key, channel_id) in &keys_to_remove { channels.remove(key); persisted.remove(key); + persist::delete_channel_from_db(channel_id); } - persist::save_channels(&persisted); } /// Mark whether the access key has been provisioned on-chain. @@ -685,13 +686,7 @@ impl SessionProvider { // confirms acceptance. let updated_entry = ChannelEntry { cumulative_amount: new_cumulative, ..entry }; let mut persisted = self.persisted.lock().unwrap(); - persist::upsert_channel_in_memory( - &mut persisted, - &key, - &updated_entry, - 0, - &self.origin, - ); + persist::upsert_channel_in_memory(&mut persisted, &key, &updated_entry); drop(persisted); // Track the voucher so we can roll back cumulative_amount @@ -727,9 +722,18 @@ impl SessionProvider { // Update in-memory state but defer disk persistence until server confirms. self.channels.lock().unwrap().insert(key.clone(), entry.clone()); + let authorized_signer = self.authorized_signer.unwrap_or(payer); self.persisted.lock().unwrap().insert( key.clone(), - persist::PersistedChannel::from_channel_entry(&entry, deposit, &self.origin), + persist::from_channel_entry( + &entry, + deposit, + &self.origin, + &payer, + &payee, + ¤cy, + &authorized_signer, + ), ); *self.pending.lock().unwrap() = Some(PendingAction::Open { key }); Ok(build_credential(challenge, payload, chain_id, payer)) diff --git a/crates/common/src/tempo/mod.rs b/crates/common/src/tempo/mod.rs index d6c6340cc4d12..ef7be1b8e189e 100644 --- a/crates/common/src/tempo/mod.rs +++ b/crates/common/src/tempo/mod.rs @@ -2,3 +2,14 @@ mod keystore; pub use keystore::*; + +/// Conservative gas buffer for browser wallet transactions on Tempo chains. +/// +/// Browser wallets may sign with P256 or WebAuthn instead of secp256k1, which costs more gas +/// for signature verification. Since we can't determine the signature type before signing, +/// we add the worst-case (WebAuthn) overhead: +/// - P256: +5,000 gas (P256 precompile cost minus ecrecover savings) +/// - WebAuthn: ~6,500 gas (P256 cost + calldata for webauthn_data) +/// +/// See +pub const TEMPO_BROWSER_GAS_BUFFER: u64 = 7_000; diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs index 5a362e963d956..77a3bb7ab11c2 100644 --- a/crates/config/spec/src/lib.rs +++ b/crates/config/spec/src/lib.rs @@ -40,8 +40,7 @@ mod tests { /// Checks that the `file` has the specified `contents`. If that is not the /// case, updates the file and then fails the test. fn ensure_file_contents(file: &Path, contents: &str) { - if let Ok(old_contents) = fs::read_to_string(file) - && normalize_newlines(&old_contents) == normalize_newlines(contents) + if fs::read_to_string(file).map(|old| normalize_newlines(&old) == normalize_newlines(contents)).unwrap_or(false) { // File is already up to date. return; diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index 64ee7e05fc75f..60a15206bc51a 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -277,6 +277,11 @@ pub struct RpcEndpoint { /// endpoint url or env pub endpoint: RpcEndpointUrl, + /// Additional fallback endpoints for load-balanced multi-endpoint forking. + /// When set, requests are distributed across all endpoints (primary + extra) + /// with automatic failover. + pub extra_endpoints: Vec, + /// Token to be used as authentication pub auth: Option, @@ -293,6 +298,7 @@ impl RpcEndpoint { pub fn resolve(self) -> ResolvedRpcEndpoint { ResolvedRpcEndpoint { endpoint: self.endpoint.resolve(), + extra_endpoints: self.extra_endpoints.into_iter().map(|e| e.resolve()).collect(), auth: self.auth.map(|auth| auth.resolve()), config: self.config, } @@ -301,7 +307,7 @@ impl RpcEndpoint { impl fmt::Display for RpcEndpoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { endpoint, auth, config } = self; + let Self { endpoint, auth, config, .. } = self; write!(f, "{endpoint}")?; write!(f, "{config}")?; if let Some(auth) = auth { @@ -316,16 +322,24 @@ impl Serialize for RpcEndpoint { where S: Serializer, { - if self.config.retries.is_none() - && self.config.retry_backoff.is_none() - && self.config.compute_units_per_second.is_none() - && self.auth.is_none() - { - // serialize as endpoint if there's no additional config + let has_config = self.config.retries.is_some() + || self.config.retry_backoff.is_some() + || self.config.compute_units_per_second.is_some() + || self.auth.is_some(); + + if !has_config && self.extra_endpoints.is_empty() { + // serialize as plain endpoint string if there's no additional config self.endpoint.serialize(serializer) } else { - let mut map = serializer.serialize_map(Some(5))?; - map.serialize_entry("endpoint", &self.endpoint)?; + let mut map = serializer.serialize_map(None)?; + if self.extra_endpoints.is_empty() { + map.serialize_entry("endpoint", &self.endpoint)?; + } else { + // Serialize all endpoints as an array under "endpoints" + let all: Vec<&RpcEndpointUrl> = + std::iter::once(&self.endpoint).chain(&self.extra_endpoints).collect(); + map.serialize_entry("endpoints", &all)?; + } map.serialize_entry("retries", &self.config.retries)?; map.serialize_entry("retry_backoff", &self.config.retry_backoff)?; map.serialize_entry("compute_units_per_second", &self.config.compute_units_per_second)?; @@ -348,10 +362,13 @@ impl<'de> Deserialize<'de> for RpcEndpoint { }); } + // Support both single "endpoint" and array "endpoints" for backwards compatibility #[derive(Deserialize)] struct RpcEndpointConfigInner { #[serde(alias = "url")] - endpoint: RpcEndpointUrl, + endpoint: Option, + /// Array of endpoint URLs for multi-endpoint load balancing + endpoints: Option>, retries: Option, retry_backoff: Option, compute_units_per_second: Option, @@ -360,14 +377,43 @@ impl<'de> Deserialize<'de> for RpcEndpoint { let RpcEndpointConfigInner { endpoint, + endpoints, retries, retry_backoff, compute_units_per_second, auth, } = serde_json::from_value(value).map_err(serde::de::Error::custom)?; + let (primary, extra) = match (endpoint, endpoints) { + // Single endpoint: endpoint = "..." + (Some(ep), None) => (ep, vec![]), + // Array of endpoints: endpoints = ["...", "..."] + (None, Some(mut eps)) => { + if eps.is_empty() { + return Err(serde::de::Error::custom( + "endpoints array must contain at least one URL", + )); + } + let primary = eps.remove(0); + (primary, eps) + } + // Both provided — error + (Some(_), Some(_)) => { + return Err(serde::de::Error::custom( + "cannot specify both `endpoint` and `endpoints`", + )); + } + // Neither provided — error + (None, None) => { + return Err(serde::de::Error::custom( + "must specify either `endpoint` or `endpoints`", + )); + } + }; + Ok(Self { - endpoint, + endpoint: primary, + extra_endpoints: extra, auth, config: RpcEndpointConfig { retries, retry_backoff, compute_units_per_second }, }) @@ -384,6 +430,7 @@ impl Default for RpcEndpoint { fn default() -> Self { Self { endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig::default(), auth: None, } @@ -394,21 +441,38 @@ impl Default for RpcEndpoint { #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResolvedRpcEndpoint { pub endpoint: Result, + /// Additional resolved endpoints for multi-endpoint load balancing. + pub extra_endpoints: Vec>, pub auth: Option>, pub config: RpcEndpointConfig, } impl ResolvedRpcEndpoint { - /// Returns the url this type holds, see [`RpcEndpoint::resolve`] + /// Returns the primary url this type holds, see [`RpcEndpoint::resolve`] pub fn url(&self) -> Result { self.endpoint.clone() } + /// Returns all resolved URLs (primary + extra) for multi-endpoint configurations. + /// Returns an empty vec if no extra endpoints are configured. + pub fn all_urls(&self) -> Result, UnresolvedEnvVarError> { + let primary = self.endpoint.clone()?; + if self.extra_endpoints.is_empty() { + return Ok(vec![primary]); + } + let mut urls = vec![primary]; + for ep in &self.extra_endpoints { + urls.push(ep.clone()?); + } + Ok(urls) + } + // Returns true if all environment variables are resolved successfully pub fn is_unresolved(&self) -> bool { let endpoint_err = self.endpoint.is_err(); + let extra_err = self.extra_endpoints.iter().any(|e| e.is_err()); let auth_err = self.auth.as_ref().map(|auth| auth.is_err()).unwrap_or(false); - endpoint_err || auth_err + endpoint_err || extra_err || auth_err } // Attempts to resolve unresolved environment variables into a new instance @@ -419,6 +483,11 @@ impl ResolvedRpcEndpoint { if let Err(err) = self.endpoint { self.endpoint = err.try_resolve() } + for ep in &mut self.extra_endpoints { + if let Err(err) = std::mem::replace(ep, Ok(String::new())) { + *ep = err.try_resolve(); + } + } if let Some(Err(err)) = self.auth { self.auth = Some(err.try_resolve()) } @@ -483,6 +552,7 @@ mod tests { config, RpcEndpoint { endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(5), retry_backoff: Some(250), @@ -498,6 +568,7 @@ mod tests { config, RpcEndpoint { endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: None, retry_backoff: None, @@ -507,4 +578,62 @@ mod tests { } ); } + + #[test] + fn serde_rpc_config_multi_endpoints() { + // Array of endpoints via "endpoints" key + let s = r#"{ + "endpoints": ["https://rpc1.example.com", "https://rpc2.example.com", "https://rpc3.example.com"], + "retries": 5, + "retry_backoff": 1000 + }"#; + let config: RpcEndpoint = serde_json::from_str(s).unwrap(); + assert_eq!( + config, + RpcEndpoint { + endpoint: RpcEndpointUrl::Url("https://rpc1.example.com".to_string()), + extra_endpoints: vec![ + RpcEndpointUrl::Url("https://rpc2.example.com".to_string()), + RpcEndpointUrl::Url("https://rpc3.example.com".to_string()), + ], + config: RpcEndpointConfig { + retries: Some(5), + retry_backoff: Some(1000), + compute_units_per_second: None, + }, + auth: None, + } + ); + + // Resolved URLs + let resolved = config.resolve(); + let all_urls = resolved.all_urls().unwrap(); + assert_eq!( + all_urls, + vec![ + "https://rpc1.example.com".to_string(), + "https://rpc2.example.com".to_string(), + "https://rpc3.example.com".to_string(), + ] + ); + } + + #[test] + fn serde_rpc_config_rejects_both_endpoint_and_endpoints() { + let s = r#"{ + "endpoint": "https://rpc1.example.com", + "endpoints": ["https://rpc2.example.com"] + }"#; + let result: Result = serde_json::from_str(s); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("cannot specify both")); + } + + #[test] + fn serde_rpc_config_rejects_empty_endpoints() { + let s = r#"{ "endpoints": [] }"#; + let result: Result = serde_json::from_str(s); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("at least one URL")); + } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index c0f8249f37dfd..f148432c42bed 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -3681,6 +3681,7 @@ mod tests { "mainnet", RpcEndpointType::Config(RpcEndpoint { endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), @@ -3706,6 +3707,7 @@ mod tests { "mainnet", RpcEndpointType::Config(RpcEndpoint { endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), @@ -3753,6 +3755,7 @@ mod tests { "mainnet", RpcEndpointType::Config(RpcEndpoint { endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), @@ -3779,6 +3782,7 @@ mod tests { endpoint: RpcEndpointUrl::Url( "https://eth-mainnet.alchemyapi.io/v2/123455".to_string() ), + extra_endpoints: vec![], config: RpcEndpointConfig { retries: Some(3), retry_backoff: Some(1000), diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 3d4c57fe5dc3e..ee0175a5e21b1 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -157,6 +157,12 @@ impl From> for Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/evm/abi/src/Console.json b/crates/evm/abi/src/Console.json index 54e6d46dff349..f275f471087ee 100644 --- a/crates/evm/abi/src/Console.json +++ b/crates/evm/abi/src/Console.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes10","name":"","type":"bytes10"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes11","name":"","type":"bytes11"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes25","name":"","type":"bytes25"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes17","name":"","type":"bytes17"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes27","name":"","type":"bytes27"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"int256","name":"","type":"int256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes29","name":"","type":"bytes29"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes7","name":"","type":"bytes7"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes8","name":"","type":"bytes8"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes19","name":"","type":"bytes19"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes9","name":"","type":"bytes9"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes14","name":"","type":"bytes14"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes13","name":"","type":"bytes13"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes5","name":"","type":"bytes5"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes23","name":"","type":"bytes23"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes6","name":"","type":"bytes6"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes31","name":"","type":"bytes31"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes18","name":"","type":"bytes18"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes28","name":"","type":"bytes28"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes22","name":"","type":"bytes22"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes15","name":"","type":"bytes15"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes2","name":"","type":"bytes2"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes21","name":"","type":"bytes21"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes30","name":"","type":"bytes30"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes24","name":"","type":"bytes24"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes26","name":"","type":"bytes26"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"name":"log","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256[]","name":"","type":"int256[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"address[]","name":"","type":"address[]"}],"name":"table","outputs":[],"stateMutability":"pure","type":"function"}] \ No newline at end of file diff --git a/crates/evm/abi/src/console.py b/crates/evm/abi/src/console.py index 2e28fece21e42..d0b32ff410941 100755 --- a/crates/evm/abi/src/console.py +++ b/crates/evm/abi/src/console.py @@ -16,12 +16,15 @@ def main(): # Parse signatures from `console.sol`'s string literals console_sol = open(console_file).read() sig_strings = re.findall( - r'"(log.*?)"', + r'"((?:log|table).*?)"', console_sol, ) raw_sigs = [s.strip().strip('"') for s in sig_strings] sigs = [ - s.replace("string", "string memory").replace("bytes)", "bytes memory)") + re.sub(r"(\w+\[\])", r"\1 memory", s) + .replace("string,", "string memory,") + .replace("string)", "string memory)") + .replace("bytes)", "bytes memory)") for s in raw_sigs ] sigs = list(set(sigs)) @@ -38,6 +41,7 @@ def main(): ) combined = json.loads(r.stdout.strip()) abi = combined["contracts"][":HardhatConsole"]["abi"] + open(abi_file, "w").write(json.dumps(abi, separators=(",", ":"), indent=None)) diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 6f539d356e5a9..aefa0e2ee9741 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -71,7 +71,7 @@ impl ForkedDatabase { /// Reset the fork to a fresh forked state, and optionally update the fork config pub fn reset( &mut self, - _url: Option, + _urls: Vec, block_number: impl Into, ) -> Result<(), String> { self.backend.set_pinned_block(block_number).map_err(|err| err.to_string())?; diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index 26acbe56a97cb..c591b9426bf9e 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -50,7 +50,9 @@ impl LogCollector { fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> { let decoded = console::hh::ConsoleCalls::abi_decode(data)?; - self.push_msg(&decoded.fmt(Default::default())); + for line in decoded.fmt(Default::default()).lines() { + self.push_msg(line); + } Ok(()) } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9a7a237855b97..ecdc13ded67cf 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -51,7 +51,7 @@ forge-script.workspace = true forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } alloy-chains.workspace = true alloy-consensus.workspace = true diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 122020571ad24..765bb64f95fdd 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -9,7 +9,7 @@ use alloy_provider::{PendingTransactionError, Provider, ProviderBuilder as Alloy use alloy_signer::{Signature, Signer}; use alloy_transport::TransportError; use clap::{Parser, ValueHint}; -use eyre::{Context, Result}; +use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, @@ -21,6 +21,7 @@ use foundry_common::{ fmt::parse_tokens, provider::ProviderBuilder, shell, + tempo::TEMPO_BROWSER_GAS_BUFFER, }; use foundry_compilers::{ ArtifactId, artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize, @@ -33,10 +34,12 @@ use foundry_config::{ }, merge_impl_figment_convert, }; -use foundry_wallets::{TempoAccessKeyConfig, WalletSigner}; +use foundry_wallets::{ + BrowserWalletOpts, TempoAccessKeyConfig, WalletSigner, wallet_browser::signer::BrowserSigner, +}; use serde_json::json; use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc, time::Duration}; -use tempo_alloy::TempoNetwork; +use tempo_alloy::{TempoNetwork, contracts::precompiles::DEFAULT_FEE_TOKEN}; merge_impl_figment_convert!(CreateArgs, build, eth); @@ -101,14 +104,29 @@ pub struct CreateArgs { #[command(flatten)] retry: RetryArgs, + + /// Browser wallet options + #[command(flatten)] + browser: BrowserWalletOpts, } impl CreateArgs { /// Executes the command to create a contract - pub async fn run(self) -> Result<()> { + pub async fn run(mut self) -> Result<()> { let (signer, tempo_access_key) = self.eth.wallet.maybe_signer().await?; - if tempo_access_key.is_some() || self.tx.tempo.is_tempo() { + // Resolve chain early so we can dispatch to the correct network type. + if self.chain_id().is_none() { + let config = self.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let chain_id = provider.get_chain_id().await?; + self.eth.etherscan.chain = Some(chain_id.into()); + } + + if tempo_access_key.is_some() + || self.tx.tempo.is_tempo() + || self.chain_id().is_some_and(|c| c.is_tempo()) + { self.run_generic::(signer, tempo_access_key).await } else { self.run_generic::(signer, None).await @@ -185,17 +203,29 @@ impl CreateArgs { self.tx.tempo.key_id = Some(ak.key_address); } - // respect chain, if set explicitly via cmd args - let chain_id = if let Some(chain_id) = self.chain_id() { - chain_id - } else { - provider.get_chain_id().await? - }; - // Whether to broadcast the transaction or not let dry_run = !self.broadcast; - if self.unlocked { + // Launch browser signer if `--browser` flag is set + let browser = self.browser.run::().await?; + + if let Some(browser) = browser { + // Deploy with browser wallet + let deployer_address = browser.address(); + self.deploy( + abi, + bin, + params, + provider, + deployer_address, + config.transaction_timeout, + id, + dry_run, + None, + Some(browser), + ) + .await + } else if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); self.deploy( @@ -203,12 +233,12 @@ impl CreateArgs { bin, params, provider, - chain_id, sender, config.transaction_timeout, id, dry_run, None, + None, ) .await } else if let Some(ak) = access_key { @@ -223,12 +253,12 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer_address, config.transaction_timeout, id, dry_run, Some((signer, ak)), + None, ) .await } else { @@ -246,20 +276,20 @@ impl CreateArgs { bin, params, provider, - chain_id, deployer, config.transaction_timeout, id, dry_run, None, + None, ) .await } } - /// Returns the provided chain id, if any. - fn chain_id(&self) -> Option { - self.eth.etherscan.chain.map(|chain| chain.id()) + /// Returns the resolved chain, if any. + const fn chain_id(&self) -> Option { + self.eth.etherscan.chain } /// Ensures the verify command can be executed. @@ -271,7 +301,6 @@ impl CreateArgs { async fn verify_preflight_check( &self, constructor_args: Option, - chain: u64, id: &ArtifactId, ) -> Result<()> { // NOTE: this does not represent the same `VerifyArgs` that would be sent after deployment, @@ -287,7 +316,7 @@ impl CreateArgs { num_of_optimizations: None, etherscan: EtherscanOpts { key: self.eth.etherscan.key.clone(), - chain: Some(chain.into()), + chain: self.chain_id(), }, rpc: Default::default(), flatten: false, @@ -311,7 +340,7 @@ impl CreateArgs { // ETHERSCAN_API_KEY value set. let config = verify.load_config()?; verify.etherscan.key = - config.get_etherscan_config_with_chain(Some(chain.into()))?.map(|c| c.key); + config.get_etherscan_config_with_chain(self.chain_id())?.map(|c| c.key); let context = verify.resolve_context().await?; @@ -327,17 +356,19 @@ impl CreateArgs { bin: BytecodeObject, args: Vec, provider: P, - chain: u64, deployer_address: Address, timeout: u64, id: ArtifactId, dry_run: bool, tempo_keychain: Option<(WalletSigner, TempoAccessKeyConfig)>, + browser_signer: Option>, ) -> Result<()> where N::TransactionRequest: FoundryTransactionBuilder + serde::Serialize, N::ReceiptResponse: serde::Serialize, { + let chain = self.chain_id().context("chain ID not resolved")?; + let bin = bin.into_bytes().unwrap_or_default(); if bin.is_empty() { eyre::bail!("no bytecode found in bin object for {}", self.contract.name) @@ -349,22 +380,31 @@ impl CreateArgs { let is_args_empty = args.is_empty(); let mut deployer = - factory.deploy_tokens(args.clone(), self.tx.tempo.fee_token).context("failed to deploy contract").map_err(|e| { + 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") } else { e } })?; - let is_legacy = self.tx.legacy || Chain::from(chain).is_legacy(); + let is_legacy = self.tx.legacy || chain.is_legacy(); deployer.tx.set_from(deployer_address); - deployer.tx.set_chain_id(chain); + deployer.tx.set_chain_id(chain.id()); // `to` field must be set explicitly, cannot be None. if deployer.tx.to().is_none() { deployer.tx.set_create(); } + // If Tempo chain fee token must be set + if chain.is_tempo() { + if let Some(fee_token) = self.tx.tempo.fee_token { + deployer.tx.set_fee_token(fee_token); + } else { + deployer.tx.set_fee_token(DEFAULT_FEE_TOKEN); + } + } + // Apply user-provided gas, fee, nonce, and Tempo options. self.tx.apply::(&mut deployer.tx, is_legacy); @@ -394,7 +434,16 @@ impl CreateArgs { } if self.tx.gas_limit.is_none() { - deployer.tx.set_gas_limit(provider.estimate_gas(deployer.tx.clone()).await?); + let mut estimated = provider.estimate_gas(deployer.tx.clone()).await?; + + // Browser wallets may sign with P256/WebAuthn instead of secp256k1, which + // costs more gas for signature verification on Tempo chains. Add a + // conservative buffer since we can't determine the signature type beforehand. + if browser_signer.is_some() && chain.is_tempo() { + estimated += TEMPO_BROWSER_GAS_BUFFER; + } + + deployer.tx.set_gas_limit(estimated); } if is_legacy { @@ -422,7 +471,7 @@ impl CreateArgs { constructor_args = Some(hex::encode(encoded_args)); } - self.verify_preflight_check(constructor_args.clone(), chain, &id).await?; + self.verify_preflight_check(constructor_args.clone(), &id).await?; } if dry_run { @@ -452,7 +501,31 @@ impl CreateArgs { } // Deploy the actual contract - let (deployed_contract, receipt) = if let Some((signer, ak)) = tempo_keychain { + let (deployed_contract, receipt) = if let Some(browser) = browser_signer { + // Browser wallet signs and sends the transaction + let tx_hash = browser.send_transaction_via_browser(deployer.tx).await?; + + // Wait for the transaction to be confirmed, then fetch the receipt. + provider + .watch_pending_transaction(alloy_provider::PendingTransactionConfig::new(tx_hash)) + .await? + .await?; + + let receipt = provider + .get_transaction_receipt(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("could not get transaction receipt for {tx_hash}"))?; + + if !receipt.status() { + eyre::bail!("deployment transaction failed (receipt status 0): {tx_hash}"); + } + + let address = receipt + .contract_address() + .ok_or_else(|| eyre::eyre!("contract was not deployed"))?; + + (address, receipt) + } else if let Some((signer, ak)) = tempo_keychain { // Tempo keychain mode: sign with access key provisioning and send raw let raw_tx = deployer .tx @@ -518,7 +591,7 @@ impl CreateArgs { no_auto_detect: false, use_solc: None, num_of_optimizations, - etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) }, + etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain) }, rpc: Default::default(), flatten: false, force: false, @@ -681,7 +754,6 @@ impl + Clone> DeploymentTxFactory { pub fn deploy_tokens( self, params: Vec, - fee_token: Option
, ) -> Result, ContractDeploymentError> where N::TransactionRequest: FoundryTransactionBuilder, @@ -703,9 +775,6 @@ impl + Clone> DeploymentTxFactory { // create the tx object. Since we're deploying a contract, `to` is `None` let mut tx = N::TransactionRequest::default(); tx.set_input(data); - if let Some(fee_token) = fee_token { - tx.set_fee_token(fee_token); - } Ok(Deployer { client: self.client.clone(), tx, confs: 1, timeout: self.timeout }) } } @@ -763,7 +832,7 @@ mod tests { "--chain-id", "9999", ]); - assert_eq!(args.chain_id(), Some(9999)); + assert_eq!(args.chain_id().map(|c| c.id()), Some(9999)); } #[test] diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..0865147167243 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -19,7 +19,7 @@ use std::{ops::ControlFlow, path::PathBuf}; /// /// # Required Methods /// -/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +/// - `lint`: Scans the provided source files emitting a diagnostic for lints found. pub trait Linter: Send + Sync + Clone { type Language: Language; type Lint: Lint; @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} diff --git a/crates/lint/src/sol/gas/keccak.rs b/crates/lint/src/sol/gas/keccak.rs index b6a18f292a91b..cb942510bbb49 100644 --- a/crates/lint/src/sol/gas/keccak.rs +++ b/crates/lint/src/sol/gas/keccak.rs @@ -91,7 +91,7 @@ fn extract_keccak256_arg<'hir>(expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir: return None; }; - (is_keccak && args.len() == 1).then(|| &args[0]) + if is_keccak && args.len() == 1 { Some(&args[0]) } else { None } } // -- HELPER FUNCTIONS AND STRUCTS ---------------------------------------------------------------- diff --git a/crates/macros/src/console_fmt.rs b/crates/macros/src/console_fmt.rs index 0c95841d1e653..245ba6e5b78e2 100644 --- a/crates/macros/src/console_fmt.rs +++ b/crates/macros/src/console_fmt.rs @@ -7,7 +7,7 @@ use syn::{ pub fn console_fmt(input: &DeriveInput) -> TokenStream { let name = &input.ident; let tokens = match &input.data { - Data::Struct(s) => derive_struct(s), + Data::Struct(s) => derive_struct(s, name), Data::Enum(e) => derive_enum(e), Data::Union(_) => return quote!(compile_error!("Unions are unsupported");), }; @@ -18,8 +18,8 @@ pub fn console_fmt(input: &DeriveInput) -> TokenStream { } } -fn derive_struct(s: &DataStruct) -> TokenStream { - let imp = impl_struct(s).unwrap_or_else(|| quote!(String::new())); +fn derive_struct(s: &DataStruct, name: &Ident) -> TokenStream { + let imp = impl_struct(s, name).unwrap_or_else(|| quote!(String::new())); quote! { fn fmt(&self, _spec: FormatSpec) -> String { #imp @@ -27,7 +27,7 @@ fn derive_struct(s: &DataStruct) -> TokenStream { } } -fn impl_struct(s: &DataStruct) -> Option { +fn impl_struct(s: &DataStruct, name: &Ident) -> Option { if s.fields.is_empty() { return None; } @@ -36,13 +36,49 @@ fn impl_struct(s: &DataStruct) -> Option { return None; } + let members = s.fields.members().collect::>(); let fields = s.fields.iter().collect::>(); + + // Detect table call structs: name must start with "table" (from the ABI function name) and + // all fields must be Vec types (Solidity arrays). Both conditions together prevent + // accidental table rendering for unrelated structs that happen to have Vec fields. + let is_table = name.to_string().starts_with("table") + && !fields.is_empty() + && fields.iter().all(|f| match &f.ty { + Type::Path(path) => path.path.segments.last().is_some_and(|seg| seg.ident == "Vec"), + _ => false, + }); + if is_table { + let member_ref = |m: &Member| match m { + Member::Named(ident) => quote!(&self.#ident), + Member::Unnamed(idx) => quote!(&self.#idx), + }; + let imp = if members.len() == 1 { + let vals = member_ref(&members[0]); + quote! { + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(None, &values) + } + } else { + let keys = member_ref(&members[0]); + let vals = member_ref(&members[1]); + quote! { + let keys: ::std::vec::Vec<&dyn ConsoleFmt> = + (#keys).iter().map(|v| v as &dyn ConsoleFmt).collect(); + let values: ::std::vec::Vec<&dyn ConsoleFmt> = + (#vals).iter().map(|v| v as &dyn ConsoleFmt).collect(); + console_table_format(Some(&keys), &values) + } + }; + return Some(imp); + } + let first_ty = match &fields.first().unwrap().ty { Type::Path(path) => path.path.segments.last().unwrap().ident.to_string(), _ => String::new(), }; - let members = s.fields.members().collect::>(); let args: Punctuated = members .into_iter() .map(|member| match member { diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 65b75b8b39bb1..332420d0d1c42 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -23,7 +23,7 @@ foundry-evm-networks.workspace = true alloy-evm.workspace = true foundry-debugger.workspace = true foundry-cheatcodes.workspace = true -foundry-wallets.workspace = true +foundry-wallets = { workspace = true, features = ["browser", "tempo"] } foundry-linking.workspace = true forge-script-sequence.workspace = true diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 05834ed8bb036..5c5155aa1678b 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,15 +9,21 @@ use crate::{ execute::{ExecutionArtifacts, ExecutionData}, sequence::get_commit_hash, }; +use alloy_chains::NamedChain; +use alloy_evm::revm::context::Block; use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, TxKind, U256, map::HashMap, utils::format_units}; +use alloy_primitives::{Address, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{ContractData, shell}; -use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; +use foundry_evm::{ + core::{FoundryBlock, evm::FoundryEvmNetwork}, + traces::{decode_trace_arena, render_trace_arena}, +}; +use foundry_wallets::wallet_browser::signer::BrowserSigner; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ @@ -31,23 +37,24 @@ use std::{ /// /// Can be either converted directly to [BundledState] or driven to it through /// [FilledTransactionsState]. -pub struct PreSimulationState { +pub struct PreSimulationState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, - pub execution_result: ScriptResult, + pub execution_result: ScriptResult, pub execution_artifacts: ExecutionArtifacts, } -impl PreSimulationState { +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 { + pub async fn fill_metadata(self) -> Result> { let address_to_abi = self.build_address_to_abi_map(); let mut transactions = self @@ -64,7 +71,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if let Some(TxKind::Call(_)) = to { + if to.is_some() { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, @@ -88,6 +95,7 @@ impl PreSimulationState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, execution_artifacts: self.execution_artifacts, transactions, @@ -100,8 +108,8 @@ impl PreSimulationState { /// Collects gas usage and metadata for each transaction. pub async fn simulate_and_fill( &self, - transactions: VecDeque, - ) -> Result> { + transactions: VecDeque>, + ) -> Result>> { trace!(target: "script", "executing onchain simulation"); let runners = Arc::new( @@ -121,7 +129,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; + let to = tx.to(); let result = runner .simulate( tx.from() @@ -139,7 +147,8 @@ impl PreSimulationState { // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { - runner.executor.env_mut().evm_env.block_env.number += U256::from(1); + let block_number = runner.executor.evm_env().block_env.number() + U256::from(1); + runner.executor.evm_env_mut().block_env.set_number(block_number); } let is_noop_tx = if let Some(to) = to { @@ -226,12 +235,12 @@ 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::is_json() { let n = rpcs.len(); - let s = if n != 1 { "s" } else { "" }; + let s = if n == 1 { "" } else { "s" }; sh_println!("\n## Setting up {n} EVM{s}.")?; } @@ -248,22 +257,23 @@ impl PreSimulationState { /// 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 struct FilledTransactionsState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, - pub transactions: VecDeque, + pub transactions: VecDeque>, } -impl FilledTransactionsState { +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(mut self) -> Result { + pub async fn bundle(mut 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() { @@ -274,7 +284,7 @@ impl FilledTransactionsState { // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); - let mut manager = ProvidersManager::default(); + 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 @@ -282,7 +292,7 @@ impl FilledTransactionsState { let mut txes_iter = mem::take(&mut self.transactions).into_iter().peekable(); while let Some(mut tx) = txes_iter.next() { - let tx_rpc = tx.rpc.to_owned(); + let tx_rpc = tx.rpc.clone(); let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; if let Some(tx) = tx.tx_mut().as_unsigned_mut() { @@ -297,7 +307,7 @@ impl FilledTransactionsState { // only estimate gas for unsigned transactions if let Some(tx) = tx.as_unsigned_mut() { trace!("estimating with different gas calculation"); - let gas = tx.gas.expect("gas is set by simulation."); + let gas = tx.gas_limit().expect("gas is set by simulation."); // We are trying to show the user an estimation of the total gas usage. // @@ -351,6 +361,12 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); + // Get the native token symbol for the chain using NamedChain + let token_symbol = NamedChain::try_from(provider_info.chain) + .unwrap_or_default() + .native_currency_symbol() + .unwrap_or("ETH"); + // 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 { @@ -368,15 +384,7 @@ impl FilledTransactionsState { .unwrap_or_else(|_| "[Could not calculate]".to_string()); let estimated_amount = estimated_amount_raw.trim_end_matches('0'); - if !shell::is_json() { - sh_println!("\n==========================")?; - sh_println!("\nChain {}", provider_info.chain)?; - - sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; - sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; - sh_println!("\n==========================")?; - } else { + if shell::is_json() { sh_println!( "{}", serde_json::json!({ @@ -384,8 +392,17 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, + "token_symbol": token_symbol, }) )?; + } else { + sh_println!("\n==========================")?; + sh_println!("\nChain {}", provider_info.chain)?; + + sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; + sh_println!("\nEstimated total gas used for script: {total_gas}")?; + sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; + sh_println!("\n==========================")?; } } } @@ -406,6 +423,7 @@ impl FilledTransactionsState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, sequence, }) @@ -416,14 +434,14 @@ impl FilledTransactionsState { &self, multi: bool, chain: u64, - transactions: VecDeque, - ) -> Result { + 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( + Some(ScriptSequence::::get_paths( &self.script_config.config, &self.args.sig, &self.build_data.build_data.target, diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index c1a6cb53bdbff..8ee2228ebfe1d 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -120,6 +120,7 @@ impl ScriptTester { let from_dir = testdata.join("utils"); let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; + let from_dir = from_dir.canonicalize()?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); // Only operate on regular files to avoid following symlinks or directories @@ -247,11 +248,12 @@ impl ScriptTester { trace!(target: "tests", "STDOUT\n{stdout}\n\nSTDERR\n{stderr}"); - assert!( - !(!stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str())), - "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", - expected.as_str() - ); + if !stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str()) { + panic!( + "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", + expected.as_str() + ); + } self } @@ -299,7 +301,7 @@ pub enum ScriptOutcome { } impl ScriptOutcome { - pub const fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> &'static str { match self { Self::OkNoEndpoint => "If you wish to simulate on-chain transactions pass a RPC URL.", Self::OkSimulation => "SIMULATION COMPLETE. To broadcast these", @@ -323,7 +325,7 @@ impl ScriptOutcome { } } - pub const fn is_err(&self) -> bool { + pub fn is_err(&self) -> bool { match self { Self::OkNoEndpoint | Self::OkSimulation diff --git a/npm/bun.lock b/npm/bun.lock index e8ef087be9d50..d9e8fed5ce479 100644 --- a/npm/bun.lock +++ b/npm/bun.lock @@ -1,12 +1,13 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^24.9.1", + "@types/node": "^25.5.2", "bun": "^1.3.1", - "typescript": "^5.9.3", + "typescript": "^6.0.2", }, }, }, @@ -35,7 +36,7 @@ "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], - "@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], @@ -45,8 +46,12 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "bun-types/@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="], + + "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], } } diff --git a/npm/scripts/publish.mjs b/npm/scripts/publish.mjs index 0f582c8ebc555..deee29e46c92f 100644 --- a/npm/scripts/publish.mjs +++ b/npm/scripts/publish.mjs @@ -7,17 +7,12 @@ import { colors } from '#const.mjs' const REGISTRY_URL = Bun.env.NPM_REGISTRY_URL || 'https://registry.npmjs.org' -const NPM_TOKEN = Bun.env.NPM_TOKEN -if (!NPM_TOKEN) throw new Error('NPM_TOKEN is required') - main().catch(error => { console.error(error) process.exit(1) }) async function main() { - const npmToken = Bun.env.NPM_TOKEN - if (!npmToken) throw new Error('NPM_TOKEN is required') const inputPath = Bun.argv[2] if (!inputPath) throw new Error('Package path is required') @@ -114,11 +109,6 @@ async function setPackageVersion(packagePath, version) { console.info(colors.green, 'Setting package version:', version) const result = await Bun.$`npm version ${version} --allow-same-version --no-git-tag-version` .cwd(packagePath) - .env({ - ...Bun.env, - ...process.env, - NPM_TOKEN - }) .quiet() .nothrow() diff --git a/npm/tsconfig.json b/npm/tsconfig.json index ca8e3c92f2daa..a7a0093d7ee56 100644 --- a/npm/tsconfig.json +++ b/npm/tsconfig.json @@ -2,7 +2,6 @@ "schema": "https://json.schemastore.org/tsconfig.json", "compilerOptions": { "strict": true, - "baseUrl": ".", "noEmit": true, "allowJs": true, "checkJs": true, From 4adcd2918acf2f668c0bd5cb074105e8e6eeab01 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 05:45:49 +0700 Subject: [PATCH 341/374] Update crates/doc/src/parser/comment.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/doc/src/parser/comment.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index ee0175a5e21b1..3d4c57fe5dc3e 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -157,12 +157,6 @@ impl From> for Comments { } } -impl From> for Comments { - fn from(value: Vec) -> Self { - Self(value) - } -} - /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); From cd92feeb94bc6bb08f6e6816ec9e5f2e68835806 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 05:46:19 +0700 Subject: [PATCH 342/374] Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/evm/traces/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 5e8c452c8695a..a9c149ef10bcc 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -372,7 +372,7 @@ impl TraceMode { 3..=4 => std::cmp::max(self, Self::Call), // Enable step recording for backtraces when verbosity is 5 or higher. // We need to ensure we're recording JUMP AND JUMPDEST steps. - _ => std::cmp::min(self, Self::Steps), + _ => std::cmp::max(self, Self::Steps), } } From eca1d88662817bd1ae34e78ab4f053c527457920 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:10:08 +0700 Subject: [PATCH 343/374] Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/anvil/src/cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 07c345b4ce81a..2215e914c2e36 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -648,8 +648,8 @@ impl AnvilEvmArgs { pub fn resolve_rpc_alias(&mut self) { if let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) { let mut resolved_urls = Vec::new(); + let mut endpoints = config.rpc_endpoints.clone().resolved(); for fork_url in &self.fork_url { - let mut endpoints = config.rpc_endpoints.clone().resolved(); if let Some(endpoint) = endpoints.remove(&fork_url.url) { // Alias matched — expand all URLs from the endpoint config match endpoint.all_urls() { From 99b827a469f77a9c4d1382af097861c021f48b4c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:11:29 +0700 Subject: [PATCH 344/374] Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/script/src/simulate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 5c5155aa1678b..26c1bd7ed9c04 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -71,7 +71,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if to.is_some() { + if let Some(alloy_primitives::TxKind::Call(_)) = to { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, From cafb30dbdd8eaed95efda81567bd6ca301c15265 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:12:10 +0700 Subject: [PATCH 345/374] Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/script/src/simulate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 26c1bd7ed9c04..4ee9c3fb53cb6 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -129,7 +129,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = tx.to(); + let to = if let Some(alloy_primitives::TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( tx.from() From c3b65e25a99c29d132e307b92c77a5b160da7a14 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:12:42 +0700 Subject: [PATCH 346/374] Update crates/cast/src/args.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cast/src/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index 94a435c08559b..d36e74b53b6d1 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -801,7 +801,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { } _ => SimpleCast::decode_raw_transaction::(&tx)?, }; - sh_println!("{decoded_tx}")?; + sh_println!("{}", serde_json::to_string_pretty(&decoded_tx)?)?; } CastSubcommand::RecoverAuthority { auth } => { let auth: SignedAuthorization = serde_json::from_str(&auth)?; From 926c4d1b81a10eed9bf26b42b7cdde6a47d916e1 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:38:07 +0700 Subject: [PATCH 347/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/tempo/keystore.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/common/src/tempo/keystore.rs b/crates/common/src/tempo/keystore.rs index 18edf39be59bd..eb1e5649092b1 100644 --- a/crates/common/src/tempo/keystore.rs +++ b/crates/common/src/tempo/keystore.rs @@ -6,7 +6,7 @@ use alloy_primitives::{Address, hex}; use alloy_rlp::Decodable; use serde::Deserialize; -use std::path::PathBuf; +use std::path::{Component, Path, PathBuf}; /// Environment variable for an ephemeral Tempo private key. pub const TEMPO_PRIVATE_KEY_ENV: &str = "TEMPO_PRIVATE_KEY"; @@ -99,9 +99,23 @@ pub struct KeysFile { /// Resolve the Tempo home directory. /// /// Uses `TEMPO_HOME` env var if set, otherwise `~/.tempo`. +fn is_safe_tempo_home_override(path: &Path) -> bool { + !path + .components() + .any(|component| matches!(component, Component::ParentDir)) +} + pub fn tempo_home() -> Option { if let Ok(home) = std::env::var(TEMPO_HOME_ENV) { - return Some(PathBuf::from(home)); + let candidate = PathBuf::from(home); + if is_safe_tempo_home_override(&candidate) { + return Some(candidate); + } + tracing::warn!( + env = TEMPO_HOME_ENV, + ?candidate, + "ignoring unsafe TEMPO_HOME override containing parent directory components" + ); } dirs::home_dir().map(|h| h.join(DEFAULT_TEMPO_HOME)) } From cc0ef96672cd98a241491ed8905f60fda1c67cae Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 08:04:31 +0700 Subject: [PATCH 348/374] Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } From 88ba719044302ba69c2698f7b3b4776af007ad56 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 08:05:04 +0700 Subject: [PATCH 349/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From b51d8a0e39bdcfc449345559e5ab649eebb68b43 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:09:58 +0000 Subject: [PATCH 350/374] Foundry/master test ux (#478) * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> From 0a7e77a9886823c39fa08ae45a16eac4827feaa9 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 08:13:33 +0700 Subject: [PATCH 351/374] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/tempo/keystore.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/common/src/tempo/keystore.rs b/crates/common/src/tempo/keystore.rs index 18edf39be59bd..0c1ce2d1a3c48 100644 --- a/crates/common/src/tempo/keystore.rs +++ b/crates/common/src/tempo/keystore.rs @@ -101,11 +101,29 @@ pub struct KeysFile { /// Uses `TEMPO_HOME` env var if set, otherwise `~/.tempo`. pub fn tempo_home() -> Option { if let Ok(home) = std::env::var(TEMPO_HOME_ENV) { - return Some(PathBuf::from(home)); + if is_safe_home_override(&home) { + return Some(PathBuf::from(home)); + } + tracing::warn!( + env = TEMPO_HOME_ENV, + "ignoring unsafe TEMPO_HOME override; falling back to default" + ); } dirs::home_dir().map(|h| h.join(DEFAULT_TEMPO_HOME)) } +/// Validates a Tempo home override from environment. +/// +/// Accepts only a single relative path component (no separators, no `..`, +/// and not absolute) to prevent path traversal / arbitrary path targeting. +fn is_safe_home_override(home: &str) -> bool { + if home.trim().is_empty() || home.contains("..") || home.contains('/') || home.contains('\\') { + return false; + } + let p = std::path::Path::new(home); + p.components().count() == 1 && !p.is_absolute() +} + /// Returns the path to the Tempo wallet keys file. pub fn tempo_keys_path() -> Option { tempo_home().map(|home| home.join(WALLET_KEYS_PATH)) From a95aaa53601f51ddd6e55586c89e9eb54878b5bb Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 23 Apr 2026 01:36:03 +0000 Subject: [PATCH 352/374] Dargon789/gamefi (#480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update docker.yml * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/args.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update docker.yml * Revise Foundry benchmark results and system info (#407) Updated benchmark results for Foundry versions and system information. https://github.com/foundry-rs/foundry/commit/1c4d334e95bc1cad3a390cc735d0f3ec59eea07f Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#413) * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration… * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory (#422) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * doc-script.js (#438) * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <… * fix(cheatcodes): read broadcasts with the active network #437 (#440) * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry (#14347) * fix(mpp): fetch fresh challenge on verification-failed key provisioning retry When an access key is refreshed but not yet provisioned on-chain, the first charge payment strips key_authorization (assuming provisioned), the server rejects with verification-failed, and the retry must obtain a fresh 402 challenge — the server consumes challenge IDs on first use, so reusing the original challenge would fail again. Amp-Thread-ID: https://ampcode.com/threads/T-019d8bde-8cf7-778c-9c9c-727632e62bd4 Co-authored-by: Amp * refactor(mpp): extract select_challenge helper, improve retry robustness - Extract shared select_challenge() for consistent 402 diagnostics on both initial and verification-failed retry paths - Don't restore key_provisioned(true) on non-402 fresh probe responses to avoid bad state on transient server errors - Add rollback_pending() to key-not-provisioned retry to prevent dirty local state from producing wrong credential shapes Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019d9619-6c24-75af-b1e8-7d02d77e0561 --------- Co-authored-by: Amp * refactor(evm): remove useless Eth/Tempo EVM wrapper structs (#14350) * chore(mpp): add built-in rpc url mapping (#14353) add built-in rpc url mapping * fix(evm): skip isolation for CREATE2 factory redirect calls (#14360) * fix(evm): preserve CREATE2 redirect state across isolated transactions (#14363) * feat(init): add `SignatureVerifier` to tempo example (#14351) * feat(init): add `SignatureVerifier` to tempo example * chore: also call `recover()` * style: typo --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(config): validate `optimizer_runs` does not exceed u32::MAX (#14354) * fix(config): validate `optimizer_runs` does not exceed u32::MAX * fix clippy * ci: remove softprops/action-gh-release dependabot ignore (#14364) The draft-finalize race that caused `Too many retries` in matrix jobs (v2.5.0) was fixed in v2.5.2. We're already on v2.6.1, so the ignore rule is no longer needed. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): mine TIP20 virtual address master salt (#14365) * chore(deps): bump actions/github-script from 8.0.0 to 9.0.0 (#14367) * chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (#14368) * chore(deps): bump taiki-e/install-action from 2.75.0 to 2.75.5 (#14369) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.75.0 to 2.75.5. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/cf39a74df4a72510be4e5b63348d61067f11e64a...a2352fc6ce487f030a3aa709482d57823eadfb37) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.75.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump anstream from 0.6.21 to 1.0.0 (#14329) Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.6.21 to 1.0.0. - [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.21...anstream-v1.0.0) --- updated-dependencies: - dependency-name: anstream dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * Update flake.lock (#14375) Co-authored-by: github-actions[bot] * fix(ci): handle stale branches in bump-tempo workflow (#14377) * fix(config): surface cleanup failures as warnings instead of hard errors (#14379) * fix(config): surface cleanup failures as warnings instead of hard errors Cache cleanup is best-effort by design. Instead of silently swallowing errors or making them fatal, return warnings from Config and emit them via sh_warn! at the CLI layer. All cleanup steps now run even if some fail. Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * fix: address CI - collapse nested ifs, remove disallowed eprintln Amp-Thread-ID: https://ampcode.com/threads/T-019da9b2-d739-753a-a978-71ec793678a5 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> * chore(tempo): bump rev + use extension traits (#14378) * feat(tempo): bump tempo rev and use TempoProviderExt::is_hardfork_active Bumps tempo crates from 6f4f5cc to bb08bb9 (latest main) and replaces the local is_hardfork_active helper with the new trait method on TempoProviderExt, which queries the tempo_forkSchedule RPC directly. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: propagate is_hardfork_active error instead of unwrap_or Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * refactor: use TempoAddressExt in tempo_labels and fee token parsing Replace is_tip20_prefix() with addr.is_tip20() in the TempoLabels inspector and use Address::TIP20_PREFIX instead of hardcoded hex bytes in token_id_to_address. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 * fix: enable reth-codec feature on tempo-primitives and fix rustfmt The rev bump split tempo crates into two copies (6f4f5cc for mpp-rs, bb08bb9 for foundry). The bb08bb9 copy lost the reth-codec feature that was previously unified from other workspace members. Explicitly enable it to restore Compact derives on TempoTxEnvelope/TempoHeader. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019da9d4-456b-75e5-a0e5-ae9287a7f5c7 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * chore: remove cargo-update workflow (#14382) Dependabot is now configured for weekly cargo updates, making this workflow redundant. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): return error for eth_getLogs with unknown blockHash instead of empty (#14371) Co-authored-by: Claude Opus 4.7 (1M context) * refactor(cast): cleanup `cast send` (#14385) * refactor(cast): cleanup `cast send` * docs: clarify `TempoNetwork` requirement * style: clippy * fix: review feedback * fix(anvil): refetch full fork blocks with missing tx cache (#14384) * docs: correct Tempo TIP-1022 documentation URL (#14387) * feat(verify): clearer error when `ETHERSCAN_API_KEY` selects etherscan (#14383) * feat: make asm-keccak a default feature in all binaries (#14389) * chore(wallets): move `wallets` crate to `foundry-core` (#14348) * fix(cheatcodes): read broadcasts with the active network (#14388) --------- Signed-off-by: dependabot[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Louis Peter Sitoe Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: o-az * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/miner.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Docker.yml * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#457) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#460) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * foundry-rs#14280 (#462) * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(npm): remove deprecated baseUrl compiler option (foundry-rs#14413) (#467) * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * 7 bug release workflow failed (#470) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fi… * Potential fix for code scanning alert no. 19 (#471) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 156: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 155: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 170: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create apisec-scan.yml (#404) * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bo… * Update crates/doc/src/parser/comment.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/args.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove CircleCI; update workflows, code, deps Delete CircleCI deployment config (.circleci/ci_deploy.yml). Update GitHub workflow files (.github/workflows/Docker.yml and docker.yml) to trigger on pushes to master and pin docker/metadata-action to v5 (was v6). Adjust Rust output in crates/cast/src/args.rs to print decoded_tx via its Display implementation instead of serializing with serde_json::to_string_pretty. Bump npm dev deps in npm/package.json: @types/node to ^25.5.2 and typescript to ^6.0.2. Co-Authored-By: Copilot <198982749+Copilot@users.noreply.github.com> Co-Authored-By: Dargon789 <64915515+dargon789@users.noreply.github.com> Signed-off-by: googleworkspace-bot --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: googleworkspace-bot Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: cui Co-authored-by: Karl Yu <43113774+0xKarl98@users.noreply.github.com> Co-authored-by: Arsh Co-authored-by: cui <1579517+cuiweixie@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Louis Peter Sitoe Co-authored-by: o-az Co-authored-by: steven Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> --- .github/scripts/bump-tempo.sh | 0 .github/scripts/tempo-check.sh | 0 .github/scripts/tempo-deploy.sh | 0 .github/scripts/tempo-mpp.sh | 0 .github/scripts/tempo-wallet.sh | 0 .github/workflows/Docker.yml | 3 ++- .github/workflows/docker.yml | 3 ++- crates/anvil/src/cmd.rs | 2 +- crates/cast/src/args.rs | 2 +- crates/cast/src/cmd/miner.rs | 4 ++-- crates/cheatcodes/src/fs.rs | 11 +++++++++- crates/common/src/tempo/keystore.rs | 28 +++++++++++------------- crates/config/src/lib.rs | 33 +++++++++++++++++++++++------ crates/doc/src/parser/comment.rs | 12 ----------- crates/evm/traces/src/lib.rs | 2 +- crates/forge/Cargo.toml | 1 - crates/script/src/simulate.rs | 4 ++-- crates/test-utils/src/script.rs | 26 +++++++++++++---------- npm/package.json | 4 ++-- 19 files changed, 76 insertions(+), 59 deletions(-) mode change 100755 => 100644 .github/scripts/bump-tempo.sh mode change 100755 => 100644 .github/scripts/tempo-check.sh mode change 100755 => 100644 .github/scripts/tempo-deploy.sh mode change 100755 => 100644 .github/scripts/tempo-mpp.sh mode change 100755 => 100644 .github/scripts/tempo-wallet.sh diff --git a/.github/scripts/bump-tempo.sh b/.github/scripts/bump-tempo.sh old mode 100755 new mode 100644 diff --git a/.github/scripts/tempo-check.sh b/.github/scripts/tempo-check.sh old mode 100755 new mode 100644 diff --git a/.github/scripts/tempo-deploy.sh b/.github/scripts/tempo-deploy.sh old mode 100755 new mode 100644 diff --git a/.github/scripts/tempo-mpp.sh b/.github/scripts/tempo-mpp.sh old mode 100755 new mode 100644 diff --git a/.github/scripts/tempo-wallet.sh b/.github/scripts/tempo-wallet.sh old mode 100755 new mode 100644 diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index 53b7d1ed0ac7c..7b85ca2ae00c8 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -4,6 +4,7 @@ on: push: tags: ["*"] branches: + - "master" pull_request: branches: ["**"] @@ -35,7 +36,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 53b7d1ed0ac7c..7b85ca2ae00c8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,6 +4,7 @@ on: push: tags: ["*"] branches: + - "master" pull_request: branches: ["**"] @@ -35,7 +36,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 07c345b4ce81a..2215e914c2e36 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -648,8 +648,8 @@ impl AnvilEvmArgs { pub fn resolve_rpc_alias(&mut self) { if let Ok(config) = Config::load_with_providers(FigmentProviders::Anvil) { let mut resolved_urls = Vec::new(); + let mut endpoints = config.rpc_endpoints.clone().resolved(); for fork_url in &self.fork_url { - let mut endpoints = config.rpc_endpoints.clone().resolved(); if let Some(endpoint) = endpoints.remove(&fork_url.url) { // Alias matched — expand all URLs from the endpoint config match endpoint.all_urls() { diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index d36e74b53b6d1..94a435c08559b 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -801,7 +801,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { } _ => SimpleCast::decode_raw_transaction::(&tx)?, }; - sh_println!("{}", serde_json::to_string_pretty(&decoded_tx)?)?; + sh_println!("{decoded_tx}")?; } CastSubcommand::RecoverAuthority { auth } => { let auth: SignedAuthorization = serde_json::from_str(&auth)?; diff --git a/crates/cast/src/cmd/miner.rs b/crates/cast/src/cmd/miner.rs index 346efb9a503a0..3b901235258ae 100644 --- a/crates/cast/src/cmd/miner.rs +++ b/crates/cast/src/cmd/miner.rs @@ -23,8 +23,8 @@ where handles.push(std::thread::spawn(move || { #[repr(C)] - struct B256Aligned(B256, [usize; 0]); - +#[repr(C, align(8))] +struct B256Aligned(B256); let mut salt = B256Aligned(salt, []); // SAFETY: `B256` is aligned to `usize`. let salt_word = unsafe { diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 5e762ec62a035..20ba969abbff1 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1129,6 +1129,15 @@ mod tests { assert_eq!(latest.contractAddress, address!("20c0000000000000000000000000000000000000")); assert!(latest.success); - stdfs::remove_dir_all(root).unwrap(); + if root.exists() { + let root_canon = stdfs::canonicalize(&root).unwrap(); + let temp_canon = stdfs::canonicalize(env::temp_dir()).unwrap(); + assert!( + root_canon.starts_with(&temp_canon), + "refusing to remove non-temp test directory: {}", + root_canon.display() + ); + stdfs::remove_dir_all(&root).unwrap(); + } } } diff --git a/crates/common/src/tempo/keystore.rs b/crates/common/src/tempo/keystore.rs index 0c1ce2d1a3c48..eb1e5649092b1 100644 --- a/crates/common/src/tempo/keystore.rs +++ b/crates/common/src/tempo/keystore.rs @@ -6,7 +6,7 @@ use alloy_primitives::{Address, hex}; use alloy_rlp::Decodable; use serde::Deserialize; -use std::path::PathBuf; +use std::path::{Component, Path, PathBuf}; /// Environment variable for an ephemeral Tempo private key. pub const TEMPO_PRIVATE_KEY_ENV: &str = "TEMPO_PRIVATE_KEY"; @@ -99,31 +99,27 @@ pub struct KeysFile { /// Resolve the Tempo home directory. /// /// Uses `TEMPO_HOME` env var if set, otherwise `~/.tempo`. +fn is_safe_tempo_home_override(path: &Path) -> bool { + !path + .components() + .any(|component| matches!(component, Component::ParentDir)) +} + pub fn tempo_home() -> Option { if let Ok(home) = std::env::var(TEMPO_HOME_ENV) { - if is_safe_home_override(&home) { - return Some(PathBuf::from(home)); + let candidate = PathBuf::from(home); + if is_safe_tempo_home_override(&candidate) { + return Some(candidate); } tracing::warn!( env = TEMPO_HOME_ENV, - "ignoring unsafe TEMPO_HOME override; falling back to default" + ?candidate, + "ignoring unsafe TEMPO_HOME override containing parent directory components" ); } dirs::home_dir().map(|h| h.join(DEFAULT_TEMPO_HOME)) } -/// Validates a Tempo home override from environment. -/// -/// Accepts only a single relative path component (no separators, no `..`, -/// and not absolute) to prevent path traversal / arbitrary path targeting. -fn is_safe_home_override(home: &str) -> bool { - if home.trim().is_empty() || home.contains("..") || home.contains('/') || home.contains('\\') { - return false; - } - let p = std::path::Path::new(home); - p.components().count() == 1 && !p.is_absolute() -} - /// Returns the path to the Tempo wallet keys file. pub fn tempo_keys_path() -> Option { tempo_home().map(|home| home.join(WALLET_KEYS_PATH)) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f148432c42bed..b1b889031c690 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1314,13 +1314,32 @@ impl Config { } // Remove last test run failures file. - if let Err(err) = fs::remove_file(&self.test_failures_file) - && err.kind() != io::ErrorKind::NotFound - { - warnings.push(format!( - "failed to remove test failures file {}: {err}", - self.test_failures_file.display() - )); + let test_failures_path = if self.test_failures_file.is_absolute() { + self.test_failures_file.clone() + } else { + project.root().join(&self.test_failures_file) + }; + let root_canon = dunce::canonicalize(project.root()).unwrap_or_else(|_| project.root().to_path_buf()); + let validated_test_failures_path = test_failures_path + .parent() + .and_then(|parent| dunce::canonicalize(parent).ok().map(|p| (p, test_failures_path.file_name()))) + .and_then(|(parent, file_name)| file_name.map(|name| parent.join(name))); + + match validated_test_failures_path { + Some(path) if path.starts_with(&root_canon) => { + if let Err(err) = fs::remove_file(&path) && err.kind() != io::ErrorKind::NotFound { + warnings.push(format!( + "failed to remove test failures file {}: {err}", + path.display() + )); + } + } + _ => { + warnings.push(format!( + "skipped removing test failures file outside project root: {}", + test_failures_path.display() + )); + } } // Remove fuzz and invariant cache directories. diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index ee0175a5e21b1..3e915b1b7f479 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -151,18 +151,6 @@ impl From> for Comments { } } -impl From> for Comments { - fn from(value: Vec) -> Self { - Self(value) - } -} - -impl From> for Comments { - fn from(value: Vec) -> Self { - Self(value) - } -} - /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 5e8c452c8695a..a9c149ef10bcc 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -372,7 +372,7 @@ impl TraceMode { 3..=4 => std::cmp::max(self, Self::Call), // Enable step recording for backtraces when verbosity is 5 or higher. // We need to ensure we're recording JUMP AND JUMPDEST steps. - _ => std::cmp::min(self, Self::Steps), + _ => std::cmp::max(self, Self::Steps), } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index ecdc13ded67cf..064834d248d5f 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,7 +62,6 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true -alloy-hardforks.workspace = true tempo-alloy.workspace = true diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 5c5155aa1678b..4ee9c3fb53cb6 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -71,7 +71,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if to.is_some() { + if let Some(alloy_primitives::TxKind::Call(_)) = to { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, @@ -129,7 +129,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = tx.to(); + let to = if let Some(alloy_primitives::TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( tx.from() diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 8ee2228ebfe1d..4d2e031f05639 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -118,18 +118,29 @@ impl ScriptTester { fn copy_testdata(root: &Path) -> Result<()> { let testdata = Self::testdata_path(); let from_dir = testdata.join("utils"); + let canonical_from_dir = from_dir.canonicalize()?; let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; let from_dir = from_dir.canonicalize()?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); + + // Canonicalize and confine entry path before any filesystem operation on it. + let canonical_file = match file.canonicalize() { + Ok(path) => path, + Err(_) => continue, + }; + if !canonical_file.starts_with(&canonical_from_dir) { + continue; + } + // Only operate on regular files to avoid following symlinks or directories - let metadata = fs::symlink_metadata(&file)?; + let metadata = fs::symlink_metadata(&canonical_file)?; let ftype = metadata.file_type(); if !ftype.is_file() { continue; } - let name = match file.file_name() { + let name = match canonical_file.file_name() { Some(name) => name, None => continue, }; @@ -139,15 +150,8 @@ impl ScriptTester { // Skip invalid (potentially dangerous) file names continue; } - // Verify canonicalized file is in from_dir to avoid symlink traversal - if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&from_dir) { - continue; - } - } else { - continue; - } - fs::copy(&file, to_dir.join(name))?; + + fs::copy(&canonical_file, to_dir.join(name))?; } Ok(()) } diff --git a/npm/package.json b/npm/package.json index d596a7930b609..bac357038a1ea 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,9 +8,9 @@ }, "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^25.0.2", + "@types/node": "^25.5.2", "bun": "^1.3.1", - "typescript": "^5.9.3" + "typescript": "^6.0.2" }, "license": "MIT OR Apache-2.0", "$schema": "https://json.schemastore.org/package.json" From aa0525c797b05dad1c610e16a6cdbbadba54fd17 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:22:58 +0700 Subject: [PATCH 353/374] Update benches/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- benches/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ab3bec614e3cd..c1d84e8d188f2 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -136,7 +136,7 @@ impl BenchmarkProject { Err(_) => continue, // Skip if unable to canonicalize }; // Ensure canonicalized path stays strictly within root_path (TempProject root) - if !canon.starts_with(&root_path) { + if !canon.starts_with(root_path.canonicalize().unwrap_or_else(|_| root_path.clone())) { sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); continue; } From dd87c0c845219a39e15929cf84b582d26a36203b Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:23:14 +0700 Subject: [PATCH 354/374] Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/cli/src/utils/suggestions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } From 29c11e58bc85427fed1a6fecafb47732bfdbbfff Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:23:30 +0700 Subject: [PATCH 355/374] Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/common/src/contracts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } From a5ad063abbbeda29314a8be0d3828b58292dc7ef Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:23:47 +0700 Subject: [PATCH 356/374] Update crates/test-utils/src/script.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/test-utils/src/script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 4e318b7fc5569..f0f8ef94ed978 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -131,7 +131,7 @@ impl ScriptTester { } // Optionally verify canonicalized file is in from_dir to avoid symlink traversal if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&from_dir) { + if !canonical_file.starts_with(from_dir.canonicalize().unwrap_or_else(|_| from_dir.clone())) { continue; } } From a2b7742c76055fbde74716f437e7a37382e53f23 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Fri, 24 Apr 2026 06:38:14 +0000 Subject: [PATCH 357/374] Main (#486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update docker.yml * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#413) * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration… * Delete .circleci directory (#422) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/miner.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#457) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#460) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * foundry-rs#14280 (#462) * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(npm): remove deprecated baseUrl compiler option (foundry-rs#14413) (#467) * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * 7 bug release workflow failed (#470) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fi… * Potential fix for code scanning alert no. 19 (#471) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 156: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 155: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 170: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create apisec-scan.yml (#404) * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bo… * Update crates/doc/src/parser/comment.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/args.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: googleworkspace-bot Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: cui Co-authored-by: Karl Yu <43113774+0xKarl98@users.noreply.github.com> Co-authored-by: Arsh Co-authored-by: cui <1579517+cuiweixie@users.noreply.github.com> Co-authored-by: steven Co-authored-by: zerosnacks Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Louis Peter Sitoe Co-authored-by: o-az Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ++++++++++++ .github/workflows/Docker.yml | 3 +- .github/workflows/docker.yml | 3 +- .github/workflows/static.yml | 4 +- counter/.gitignore | 4 ++ counter/foundry.toml | 5 +- crates/cli/src/utils/suggestions.rs | 2 +- crates/common/src/contracts.rs | 2 +- crates/config/spec/src/lib.rs | 3 +- crates/doc/src/parser/comment.rs | 6 -- crates/evm/traces/src/lib.rs | 2 +- crates/forge/Cargo.toml | 1 - crates/lint/src/linter.rs | 4 +- crates/script/src/simulate.rs | 86 +++++++++++++++++------------ crates/test-utils/src/script.rs | 42 ++++++++------ 15 files changed, 128 insertions(+), 73 deletions(-) create mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index 5a2330e7d5d62..53b7d1ed0ac7c 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -4,7 +4,6 @@ on: push: tags: ["*"] branches: - - "main" pull_request: branches: ["**"] @@ -36,7 +35,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5a2330e7d5d62..53b7d1ed0ac7c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,7 +4,6 @@ on: push: tags: ["*"] branches: - - "main" pull_request: branches: ["**"] @@ -36,7 +35,7 @@ jobs: # Extract metadata (tags, labels) for Docker - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 0ba82305f82b2..fcfbbbbec7948 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -32,9 +32,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@v6 - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: # Upload entire repository path: '.' diff --git a/counter/.gitignore b/counter/.gitignore index 85198aaa55b84..052b88bb6516b 100644 --- a/counter/.gitignore +++ b/counter/.gitignore @@ -12,3 +12,7 @@ docs/ # Dotenv file .env + + +# Soldeer +/dependencies diff --git a/counter/foundry.toml b/counter/foundry.toml index 25b918f9c9a96..c27b8ed21ba0b 100644 --- a/counter/foundry.toml +++ b/counter/foundry.toml @@ -1,6 +1,9 @@ [profile.default] src = "src" out = "out" -libs = ["lib"] +libs = ["lib", "dependencies"] + +[dependencies] +forge-std = "1.15.0" # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index 8f6d7f3cde092..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -17,7 +17,7 @@ where .map(|pv| (strsim::jaro_winkler(v, pv.as_ref()), pv.as_ref().to_owned())) .filter(|(similarity, _)| *similarity > 0.8) .collect(); - candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); + candidates.sort_by(|a, b| a.0.total_cmp(&b.0)); candidates.into_iter().map(|(_, pv)| pv).collect() } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e9f203ebf7539..895b16b3b4532 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -239,7 +239,7 @@ impl ContractsByArtifact { None } }) - .min_by(|(score1, _), (score2, _)| score1.partial_cmp(score2).unwrap_or(std::cmp::Ordering::Equal)) + .min_by(|(score1, _), (score2, _)| score1.total_cmp(score2)) .map(|(_, data)| data) } diff --git a/crates/config/spec/src/lib.rs b/crates/config/spec/src/lib.rs index 5a362e963d956..77a3bb7ab11c2 100644 --- a/crates/config/spec/src/lib.rs +++ b/crates/config/spec/src/lib.rs @@ -40,8 +40,7 @@ mod tests { /// Checks that the `file` has the specified `contents`. If that is not the /// case, updates the file and then fails the test. fn ensure_file_contents(file: &Path, contents: &str) { - if let Ok(old_contents) = fs::read_to_string(file) - && normalize_newlines(&old_contents) == normalize_newlines(contents) + if fs::read_to_string(file).map(|old| normalize_newlines(&old) == normalize_newlines(contents)).unwrap_or(false) { // File is already up to date. return; diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 3d4c57fe5dc3e..3e915b1b7f479 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -151,12 +151,6 @@ impl From> for Comments { } } -impl From> for Comments { - fn from(value: Vec) -> Self { - Self(value) - } -} - /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 5e8c452c8695a..a9c149ef10bcc 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -372,7 +372,7 @@ impl TraceMode { 3..=4 => std::cmp::max(self, Self::Call), // Enable step recording for backtraces when verbosity is 5 or higher. // We need to ensure we're recording JUMP AND JUMPDEST steps. - _ => std::cmp::min(self, Self::Steps), + _ => std::cmp::max(self, Self::Steps), } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index ecdc13ded67cf..064834d248d5f 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,7 +62,6 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true -alloy-hardforks.workspace = true tempo-alloy.workspace = true diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs index 2c11e0222a286..0865147167243 100644 --- a/crates/lint/src/linter.rs +++ b/crates/lint/src/linter.rs @@ -19,7 +19,7 @@ use std::{ops::ControlFlow, path::PathBuf}; /// /// # Required Methods /// -/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +/// - `lint`: Scans the provided source files emitting a diagnostic for lints found. pub trait Linter: Send + Sync + Clone { type Language: Language; type Lint: Lint; @@ -60,7 +60,7 @@ impl<'s> LintContext<'s> { } /// Trait for lints that operate directly on the AST. -/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintContext`. pub trait EarlyLintPass<'ast>: Send + Sync { fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 05834ed8bb036..4ee9c3fb53cb6 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -9,15 +9,21 @@ use crate::{ execute::{ExecutionArtifacts, ExecutionData}, sequence::get_commit_hash, }; +use alloy_chains::NamedChain; +use alloy_evm::revm::context::Block; use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, TxKind, U256, map::HashMap, utils::format_units}; +use alloy_primitives::{Address, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; use foundry_common::{ContractData, shell}; -use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; +use foundry_evm::{ + core::{FoundryBlock, evm::FoundryEvmNetwork}, + traces::{decode_trace_arena, render_trace_arena}, +}; +use foundry_wallets::wallet_browser::signer::BrowserSigner; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ @@ -31,23 +37,24 @@ use std::{ /// /// Can be either converted directly to [BundledState] or driven to it through /// [FilledTransactionsState]. -pub struct PreSimulationState { +pub struct PreSimulationState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_data: ExecutionData, - pub execution_result: ScriptResult, + pub execution_result: ScriptResult, pub execution_artifacts: ExecutionArtifacts, } -impl PreSimulationState { +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 { + pub async fn fill_metadata(self) -> Result> { let address_to_abi = self.build_address_to_abi_map(); let mut transactions = self @@ -64,7 +71,7 @@ impl PreSimulationState { let mut builder = ScriptTransactionBuilder::new(tx.transaction, rpc); - if let Some(TxKind::Call(_)) = to { + if let Some(alloy_primitives::TxKind::Call(_)) = to { builder.set_call( &address_to_abi, &self.execution_artifacts.decoder, @@ -88,6 +95,7 @@ impl PreSimulationState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, execution_artifacts: self.execution_artifacts, transactions, @@ -100,8 +108,8 @@ impl PreSimulationState { /// Collects gas usage and metadata for each transaction. pub async fn simulate_and_fill( &self, - transactions: VecDeque, - ) -> Result> { + transactions: VecDeque>, + ) -> Result>> { trace!(target: "script", "executing onchain simulation"); let runners = Arc::new( @@ -121,7 +129,7 @@ impl PreSimulationState { let mut runner = runners.get(&transaction.rpc).expect("invalid rpc url").write(); let tx = transaction.tx_mut(); - let to = if let Some(TxKind::Call(to)) = tx.to() { Some(to) } else { None }; + let to = if let Some(alloy_primitives::TxKind::Call(to)) = tx.to() { Some(to) } else { None }; let result = runner .simulate( tx.from() @@ -139,7 +147,8 @@ impl PreSimulationState { // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { - runner.executor.env_mut().evm_env.block_env.number += U256::from(1); + let block_number = runner.executor.evm_env().block_env.number() + U256::from(1); + runner.executor.evm_env_mut().block_env.set_number(block_number); } let is_noop_tx = if let Some(to) = to { @@ -226,12 +235,12 @@ 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::is_json() { let n = rpcs.len(); - let s = if n != 1 { "s" } else { "" }; + let s = if n == 1 { "" } else { "s" }; sh_println!("\n## Setting up {n} EVM{s}.")?; } @@ -248,22 +257,23 @@ impl PreSimulationState { /// 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 struct FilledTransactionsState { pub args: ScriptArgs, - pub script_config: ScriptConfig, + pub script_config: ScriptConfig, pub script_wallets: Wallets, + pub browser_wallet: Option>, pub build_data: LinkedBuildData, pub execution_artifacts: ExecutionArtifacts, - pub transactions: VecDeque, + pub transactions: VecDeque>, } -impl FilledTransactionsState { +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(mut self) -> Result { + pub async fn bundle(mut 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() { @@ -274,7 +284,7 @@ impl FilledTransactionsState { // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); - let mut manager = ProvidersManager::default(); + 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 @@ -282,7 +292,7 @@ impl FilledTransactionsState { let mut txes_iter = mem::take(&mut self.transactions).into_iter().peekable(); while let Some(mut tx) = txes_iter.next() { - let tx_rpc = tx.rpc.to_owned(); + let tx_rpc = tx.rpc.clone(); let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; if let Some(tx) = tx.tx_mut().as_unsigned_mut() { @@ -297,7 +307,7 @@ impl FilledTransactionsState { // only estimate gas for unsigned transactions if let Some(tx) = tx.as_unsigned_mut() { trace!("estimating with different gas calculation"); - let gas = tx.gas.expect("gas is set by simulation."); + let gas = tx.gas_limit().expect("gas is set by simulation."); // We are trying to show the user an estimation of the total gas usage. // @@ -351,6 +361,12 @@ impl FilledTransactionsState { for (rpc, total_gas) in total_gas_per_rpc { let provider_info = manager.get(&rpc).expect("provider is set."); + // Get the native token symbol for the chain using NamedChain + let token_symbol = NamedChain::try_from(provider_info.chain) + .unwrap_or_default() + .native_currency_symbol() + .unwrap_or("ETH"); + // 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 { @@ -368,15 +384,7 @@ impl FilledTransactionsState { .unwrap_or_else(|_| "[Could not calculate]".to_string()); let estimated_amount = estimated_amount_raw.trim_end_matches('0'); - if !shell::is_json() { - sh_println!("\n==========================")?; - sh_println!("\nChain {}", provider_info.chain)?; - - sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; - sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; - sh_println!("\n==========================")?; - } else { + if shell::is_json() { sh_println!( "{}", serde_json::json!({ @@ -384,8 +392,17 @@ impl FilledTransactionsState { "estimated_gas_price": estimated_gas_price, "estimated_total_gas_used": total_gas, "estimated_amount_required": estimated_amount, + "token_symbol": token_symbol, }) )?; + } else { + sh_println!("\n==========================")?; + sh_println!("\nChain {}", provider_info.chain)?; + + sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; + sh_println!("\nEstimated total gas used for script: {total_gas}")?; + sh_println!("\nEstimated amount required: {estimated_amount} {token_symbol}")?; + sh_println!("\n==========================")?; } } } @@ -406,6 +423,7 @@ impl FilledTransactionsState { args: self.args, script_config: self.script_config, script_wallets: self.script_wallets, + browser_wallet: self.browser_wallet, build_data: self.build_data, sequence, }) @@ -416,14 +434,14 @@ impl FilledTransactionsState { &self, multi: bool, chain: u64, - transactions: VecDeque, - ) -> Result { + 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( + Some(ScriptSequence::::get_paths( &self.script_config.config, &self.args.sig, &self.build_data.build_data.target, diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index c1a6cb53bdbff..4d2e031f05639 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -118,17 +118,29 @@ impl ScriptTester { fn copy_testdata(root: &Path) -> Result<()> { let testdata = Self::testdata_path(); let from_dir = testdata.join("utils"); + let canonical_from_dir = from_dir.canonicalize()?; let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; + let from_dir = from_dir.canonicalize()?; for entry in fs::read_dir(&from_dir)? { let file = entry?.path(); + + // Canonicalize and confine entry path before any filesystem operation on it. + let canonical_file = match file.canonicalize() { + Ok(path) => path, + Err(_) => continue, + }; + if !canonical_file.starts_with(&canonical_from_dir) { + continue; + } + // Only operate on regular files to avoid following symlinks or directories - let metadata = fs::symlink_metadata(&file)?; + let metadata = fs::symlink_metadata(&canonical_file)?; let ftype = metadata.file_type(); if !ftype.is_file() { continue; } - let name = match file.file_name() { + let name = match canonical_file.file_name() { Some(name) => name, None => continue, }; @@ -138,15 +150,8 @@ impl ScriptTester { // Skip invalid (potentially dangerous) file names continue; } - // Verify canonicalized file is in from_dir to avoid symlink traversal - if let Ok(canonical_file) = file.canonicalize() { - if !canonical_file.starts_with(&from_dir) { - continue; - } - } else { - continue; - } - fs::copy(&file, to_dir.join(name))?; + + fs::copy(&canonical_file, to_dir.join(name))?; } Ok(()) } @@ -247,11 +252,12 @@ impl ScriptTester { trace!(target: "tests", "STDOUT\n{stdout}\n\nSTDERR\n{stderr}"); - assert!( - !(!stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str())), - "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", - expected.as_str() - ); + if !stdout.contains(expected.as_str()) && !stderr.contains(expected.as_str()) { + panic!( + "--STDOUT--\n{stdout}\n\n--STDERR--\n{stderr}\n\n--EXPECTED--\n{:?} not found in stdout or stderr", + expected.as_str() + ); + } self } @@ -299,7 +305,7 @@ pub enum ScriptOutcome { } impl ScriptOutcome { - pub const fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> &'static str { match self { Self::OkNoEndpoint => "If you wish to simulate on-chain transactions pass a RPC URL.", Self::OkSimulation => "SIMULATION COMPLETE. To broadcast these", @@ -323,7 +329,7 @@ impl ScriptOutcome { } } - pub const fn is_err(&self) -> bool { + pub fn is_err(&self) -> bool { match self { Self::OkNoEndpoint | Self::OkSimulation From 044ef43b5d568210b85a953e00a2ce71125e0072 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 07:15:21 +0000 Subject: [PATCH 358/374] Main (#493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: compare sign github passkey (#132) * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#181) CI Configuration Removal: The .circleci/cargo.yml file, which defined specific CircleCI jobs for building and testing Rust code, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#212) This pull request updates the CircleCI configuration by correcting a repository URL in a comment and fixing a job name by removing a trailing hyphen. These are good fixes. However, the job name web3-defi-game-project itself appears to be a leftover from another project and is not descriptive. I've added a comment suggesting a rename to improve maintainability. For the same reason, you might also consider renaming the workflow my-custom-workflow to something more descriptive of what it does. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-say-hello.yml (#320) bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#321) * Delete .circleci/ci-say-hello.yml bug Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#322) clean vercel block account api app next react Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#323) https://github.com/Dargon789/hardhat-project/commit/92a3e1c76ad0a29dcb545e1589d6ed3b48dd5c81 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update docker.yml * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * Potential fix for code scanning alert no. 132: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 172: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/evm/src/executors/invariant/mod.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#413) * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration… * Delete .circleci directory (#422) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/miner.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/lint/src/linter.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#457) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) (#460) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * foundry-rs#14280 (#462) * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(npm): remove deprecated baseUrl compiler option (foundry-rs#14413) (#467) * feat(anvil): support multiple fork URLs with round-robin load balancing (#14280) * feat(anvil): support multiple fork URLs with fallback Allow `--fork-url` to be specified multiple times to distribute RPC requests across endpoints using Alloy's FallbackService. Endpoints are scored by latency and success rate; unhealthy endpoints (429s, timeouts) are automatically deprioritized. Uses active_transport_count=1 for sequential best-endpoint routing. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * fix: fmt * feat(config): support multi-endpoint `endpoints` array in foundry.toml Add `endpoints` key to `[rpc_endpoints]` config as a backwards-compatible alternative to `endpoint`. When an alias with multiple endpoints is used as `--fork-url`, all URLs are expanded for multi-endpoint forking. Example: [rpc_endpoints] mainnet = { endpoints = ["https://rpc1.example.com", "https://rpc2.example.com"], retries = 5 } Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * test: add guardrail tests and handle curl_mode in build_fallback - Add test confirming `requires = "fork_url"` guards still fire with Vec - Bail on curl_mode in build_fallback (incompatible with multi-endpoint) - Clean up redundant extra_endpoints default in From impl Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d85e6-b08f-748c-8f05-17ebee8e17f8 * refactor(anvil): consolidate eth_rpc_url into fork_urls Remove duplicate `eth_rpc_url` field from `NodeConfig` and `ClientForkConfig`. The primary URL is now always `fork_urls[0]`, eliminating the invariant that `eth_rpc_url == fork_urls[0]`. - `ClientForkConfig::eth_rpc_url()` is now an accessor returning `&str` - `NodeConfig::with_eth_rpc_url()` kept as convenience, wraps into `fork_urls` - Checks for forking enabled use `fork_urls.is_empty()` instead of `eth_rpc_url.is_none()` Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): simplify update_url to replace all fork_urls When resetting the fork via anvil_reset or anvil_setRpcUrl, the intent is to switch the fork target entirely — not swap one endpoint in a load-balanced pool. Replace the whole fork_urls vec with the single new URL instead of mutating fork_urls[0] in-place. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): rename update_url to update_urls taking Vec Accepts a Vec so the reset/update path can reconstruct a fallback provider when given multiple URLs, instead of always collapsing to a single endpoint. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor(anvil): update ClientFork::reset to accept Vec Change reset(url: Option) to reset(urls: Vec) so the reset path can preserve multi-endpoint fallback. The underlying MaybeForkedDatabase::maybe_reset still takes Option since it ignores the url anyway (marked TODO upstream). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * refactor: use Vec throughout reset path Commit to Vec in MaybeForkedDatabase::maybe_reset, ForkedDatabase::reset, and ClientFork::reset. The Option → Vec conversion now happens only at the RPC boundary (Forking.json_rpc_url in reset_fork). Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * fix: fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d8b67-c7cc-764f-bb5e-74b6e8fa6858 * round-robin for load-balance * comments * fix(anvil): sync fork_urls after anvil_setRpcUrl and anvil_reset Fix two bugs where node_config.fork_urls could get out of sync: 1. reset_block_number() now updates node_config.fork_urls before calling setup_fork_db_config(), preventing stale multi-URL lists from persisting after anvil_reset with a new URL. 2. anvil_setRpcUrl now also updates node_config.fork_urls, so subsequent anvil_reset(None) uses the correct URL instead of reverting to the original startup URL. Added regression tests for both scenarios. Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp * chore: fmt Amp-Thread-ID: https://ampcode.com/threads/T-019db0c0-6038-7428-8483-365402ff31a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * 7 bug release workflow failed (#470) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fi… * Potential fix for code scanning alert no. 19 (#471) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * deprecate forge config --basic * fix * start adding schema generation for config * naive implementation, restore basic config - relevant for use in forge init * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 156: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 155: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 170: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create apisec-scan.yml (#404) * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bo… * Update crates/doc/src/parser/comment.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/anvil/src/cmd.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/args.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: googleworkspace-bot Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: cui Co-authored-by: Karl Yu <43113774+0xKarl98@users.noreply.github.com> Co-authored-by: Arsh Co-authored-by: cui <1579517+cuiweixie@users.noreply.github.com> Co-authored-by: steven Co-authored-by: zerosnacks Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Georgios Konstantopoulos Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan Co-authored-by: Louis Peter Sitoe <202908293+solanaXpeter@users.noreply.github.com> Co-authored-by: Santiago Palladino Co-authored-by: Louis Peter Sitoe Co-authored-by: o-az Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> From c4c131e4a5122543c9c8ee891432c54b4bbac4a5 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 07:26:50 +0000 Subject: [PATCH 359/374] Add CircleCI configuration for testing workflow (#494) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/config.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..2ef62819f07dc --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,22 @@ +version: 2.1 + +jobs: + test: + docker: + - image: ghcr.io/foundry-rs/foundry:latest + steps: + - checkout + - run: + name: Install submodules + command: git submodule update --init --recursive + - run: + name: Build + command: forge build + - run: + name: Test + command: forge test -vvv + +workflows: + main: + jobs: + - test From fb01017d511bbe31f8b7a746a752d406c8efa48d Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 14:31:34 +0700 Subject: [PATCH 360/374] Update crates/script/src/simulate.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/script/src/simulate.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 4ee9c3fb53cb6..43b529a8c3e98 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -363,10 +363,9 @@ impl FilledTransactionsState { // Get the native token symbol for the chain using NamedChain let token_symbol = NamedChain::try_from(provider_info.chain) - .unwrap_or_default() + let token_symbol = alloy_chains::Chain::from_id(provider_info.chain) .native_currency_symbol() .unwrap_or("ETH"); - // 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 { From a4b89428bae66a26c17706dc527db7a36513c944 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 07:37:53 +0000 Subject: [PATCH 361/374] Update crates/script/src/simulate.rs (#488) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- Cargo.lock | 60 ++++++++++++++++++++++++----- crates/cast/src/args.rs | 6 +-- crates/evm/evm/src/executors/mod.rs | 33 ++++++++++++++-- crates/script/src/execute.rs | 13 ++++++- crates/script/src/runner.rs | 8 +++- crates/script/src/simulate.rs | 3 +- 6 files changed, 100 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60349fa6ca760..71d56653d7a20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,9 +77,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85805c194576017df6c11057504e1d60b36f3913f8e365945486931f6ee81e40" +checksum = "a547705d5c1b42575a0542bae2ba45bc62a6154be86611afaef1c0ab5c38598e" dependencies = [ "alloy-consensus", "alloy-contract", @@ -2579,7 +2579,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.23.0", + "darling 0.20.11", "ident_case", "prettyplease", "proc-macro2", @@ -2785,6 +2785,7 @@ dependencies = [ "foundry-config", "foundry-debugger", "foundry-evm", + "foundry-evm-networks", "foundry-test-utils", "foundry-wallets", "futures", @@ -4985,7 +4986,6 @@ dependencies = [ "dotenvy", "dunce", "eyre", - "foundry-block-explorers", "foundry-cli-markdown", "foundry-common", "foundry-compilers", @@ -5063,6 +5063,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-wallets", + "futures", "itertools 0.14.0", "jiff", "mpp", @@ -5073,6 +5074,7 @@ dependencies = [ "regex", "reqwest 0.13.2", "revm", + "rustls", "semver 1.0.28", "serde", "serde_json", @@ -5082,6 +5084,7 @@ dependencies = [ "tempo-primitives", "thiserror 2.0.18", "tokio", + "tokio-tungstenite 0.28.0", "toml", "tower", "tracing", @@ -5442,6 +5445,7 @@ dependencies = [ "foundry-evm-hardforks", "revm", "serde", + "serde_json", ] [[package]] @@ -5598,7 +5602,8 @@ dependencies = [ [[package]] name = "foundry-wallets" version = "0.1.0" -source = "git+https://github.com/foundry-rs/foundry-core?rev=d6c92dfcb41be26fbc1de21221a5bfc6bdd93245#d6c92dfcb41be26fbc1de21221a5bfc6bdd93245" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f2cc1ff7ea002addf41bd9c5785cd34f6a7b9658998c50035cc860ffebc3a9" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -7428,13 +7433,17 @@ dependencies = [ [[package]] name = "mpp" -version = "0.9.3" -source = "git+https://github.com/tempoxyz/mpp-rs?rev=310c9a1f3fe485fa9c7a877fd4da6b7a6ab34815#310c9a1f3fe485fa9c7a877fd4da6b7a6ab34815" +version = "0.10.0" +source = "git+https://github.com/tempoxyz/mpp-rs?rev=554d20112eb014bd223d54de7f152ca59b2aa4fd#554d20112eb014bd223d54de7f152ca59b2aa4fd" dependencies = [ "alloy", + "async-stream", "base64 0.22.1", + "futures-core", + "futures-util", "hex", "hmac", + "http 1.4.0", "rand 0.9.4", "reqwest 0.12.28", "serde", @@ -7445,6 +7454,8 @@ dependencies = [ "tempo-primitives", "thiserror 2.0.18", "time", + "tokio", + "tokio-tungstenite 0.26.2", "uuid 1.23.1", ] @@ -8562,7 +8573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.117", @@ -10833,7 +10844,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.14.0", + "itertools 0.12.1", "itoa", "normalize-path", "once_map", @@ -10868,7 +10879,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.14.0", + "itertools 0.12.1", "memchr", "num-bigint", "num-rational", @@ -11734,6 +11745,18 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.26.2", +] + [[package]] name = "tokio-tungstenite" version = "0.28.0" @@ -12105,6 +12128,23 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.28.0" diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index d36e74b53b6d1..6dc2ed6acf430 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -15,10 +15,7 @@ use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; use clap::{CommandFactory, Parser}; use clap_complete::generate; use eyre::{Result, WrapErr}; -use foundry_cli::{ - opts::NetworkVariant, - utils::{self, LoadConfig}, -}; +use foundry_cli::utils::{self, LoadConfig}; use foundry_common::{ abi::{get_error, get_event}, fmt::{format_tokens, format_uint_exp, serialize_value_as_json}, @@ -31,6 +28,7 @@ use foundry_common::{ }, shell, stdin, }; +use foundry_evm_networks::NetworkVariant; use op_alloy_network::Optimism; use std::time::Instant; use tempo_alloy::TempoNetwork; diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 2d712dcf5a48d..3ffa92b459905 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -207,9 +207,9 @@ impl Executor { self.evm_env.cfg_env.spec } - /// Sets the EVM spec. - pub const fn set_spec_id(&mut self, spec_id: SpecFor) { - self.evm_env.cfg_env.spec = spec_id; + /// Sets the EVM spec and updates spec-dependent gas parameters. + pub fn set_spec_id(&mut self, spec_id: SpecFor) { + self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id); } /// Returns the gas limit for calls and deployments. @@ -1316,6 +1316,7 @@ impl EarlyExit { mod tests { use super::*; use foundry_evm_core::constants::MAGIC_SKIP; + use revm::{context::Cfg, primitives::hardfork::SpecId}; #[test] fn cheatcode_skip_payload_is_classified_as_skip() { @@ -1352,4 +1353,30 @@ mod tests { let err = raw.into_evm_error(None); assert!(matches!(err, EvmError::Execution(_))); } + + #[test] + fn set_spec_id_updates_spec_dependent_cfg_state() { + let backend = Backend::::spawn(None).unwrap(); + let mut executor = ExecutorBuilder::default().build( + EvmEnvFor::::default(), + TxEnvFor::::default(), + backend, + ); + + executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD); + assert_eq!( + executor.evm_env().cfg_env.gas_params(), + &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD), + ); + assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled()); + + executor.set_spec_id(SpecId::AMSTERDAM); + + assert_eq!(executor.spec_id(), SpecId::AMSTERDAM); + assert_eq!( + executor.evm_env().cfg_env.gas_params(), + &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM), + ); + assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled()); + } } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 162d061462b66..627bcfffd3802 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -168,6 +168,7 @@ impl PreExecutionState { setup_result.traces.extend(script_result.traces); setup_result.labeled_addresses.extend(script_result.labeled_addresses); setup_result.returned = script_result.returned; + setup_result.exit_reason = script_result.exit_reason; setup_result.breakpoints = script_result.breakpoints; match (&mut setup_result.transactions, script_result.transactions) { @@ -422,7 +423,11 @@ impl PreSimulationState { if !self.execution_result.success { return Err(eyre::eyre!( "script failed: {}", - &self.execution_artifacts.decoder.revert_decoder.decode(&result.returned[..], None) + &self + .execution_artifacts + .decoder + .revert_decoder + .decode(&result.returned[..], result.exit_reason) )); } @@ -505,7 +510,11 @@ impl PreSimulationState { if !result.success { return Err(eyre::eyre!( "script failed: {}", - &self.execution_artifacts.decoder.revert_decoder.decode(&result.returned[..], None) + &self + .execution_artifacts + .decoder + .revert_decoder + .decode(&result.returned[..], result.exit_reason) )); } diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index de80b1bf98c47..e2404d60ce2b9 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -275,7 +275,7 @@ impl ScriptRunner { value.unwrap_or(U256::ZERO), None, ); - let (address, RawCallResult { gas_used, logs, traces, .. }) = match res { + let (address, RawCallResult { gas_used, logs, traces, exit_reason, .. }) = match res { Ok(DeployResult { address, raw }) => (address, raw), Err(EvmError::Execution(err)) => { let ExecutionErr { raw, reason } = *err; @@ -294,6 +294,7 @@ impl ScriptRunner { traces: traces .map(|traces| vec![(TraceKind::Execution, traces)]) .unwrap_or_default(), + exit_reason, address: Some(address), ..Default::default() }) @@ -348,7 +349,9 @@ impl ScriptRunner { } } - let RawCallResult { result, reverted, logs, traces, labels, transactions, .. } = res; + let RawCallResult { + result, reverted, logs, traces, labels, transactions, exit_reason, .. + } = res; let breakpoints = res.cheatcodes.map(|cheats| cheats.breakpoints).unwrap_or_default(); Ok(ScriptResult { @@ -365,6 +368,7 @@ impl ScriptRunner { .unwrap_or_default(), labeled_addresses: labels, transactions, + exit_reason, address: None, breakpoints, }) diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 4ee9c3fb53cb6..43b529a8c3e98 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -363,10 +363,9 @@ impl FilledTransactionsState { // Get the native token symbol for the chain using NamedChain let token_symbol = NamedChain::try_from(provider_info.chain) - .unwrap_or_default() + let token_symbol = alloy_chains::Chain::from_id(provider_info.chain) .native_currency_symbol() .unwrap_or("ETH"); - // 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 { From 2d27168115d731f32f9de1097f28be85acd7d8dc Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 07:57:20 +0000 Subject: [PATCH 362/374] Refine construction of Vyper compiler input by cloning only the required compiler settings field instead of the entire verification context. (#455) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#14396) * chore(deps): bump actions/cache from 5.0.4 to 5.0.5 (#14397) * chore(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 (#14398) * fix(cast): add browser wallet support for erc20 commands (#14395) * fix(cast): add browser wallet support for erc20 commands Add a browser wallet branch to the erc20_send! macro so that cast erc20 transfer/approve/mint/burn --browser works the same way cast send --browser does. The new path builds the transaction via the sol! IERC20 macro, applies user-provided tx params, then fills missing nonce, fees, and gas limit from the provider before handing the request to the browser wallet. Closes #13103 Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * fix(cast): default Tempo browser erc20 fee token * fix(cast): scope Tempo erc20 browser auto-detection * style(cast): format erc20 Tempo network helper --------- Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> * feat(forge): browser wallet support for `create` subcommand (#14394) * fix(deps): update rustls-webpki to fix RUSTSEC-2026-0104 (#14408) chore: update rustls-webpki to fix RUSTSEC-2026-0104 Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * fix: accept 0x-prefixed value inputs (#14406) * fix: accept 0x-prefixed value inputs * use U256::from_str for 0x-prefixed hex parsing, add 0X support and expand tests Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp --------- Co-authored-by: zerosnacks Co-authored-by: Amp * ci(npm): use OIDC trusted publishing, remove NPM_TOKEN (#14249) * chore: harden Makefile & CI (#14386) chore: harden Makefile, remove unused deps found w/ cargo shear Amp-Thread-ID: https://ampcode.com/threads/T-019daf55-1b0e-7756-8fff-4506e2e239c8 Co-authored-by: Amp * feat: use ChannelDb for channel persistence (#14355) * feat: use mpp-rs ChannelDb for channel persistence * use foundry-core * rm blank line * warn when old channels.json is found after SQLite migration * rename Channel --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat: support console.table (#14338) * feat(abi): add console.table variants to Console ABI * feat(fmt): implement console_table_format * feat(macros): extend ConsoleFmt derive for console.table * feat(inspectors): push msg per line in hardhat_log * chore: run cargo fmt * chore: add todo comment * refactor: use comfy-table for console_table_format Amp-Thread-ID: https://ampcode.com/threads/T-019db45a-2ee1-771d-8127-2052dc6df2a3 Co-authored-by: Amp * refactor(macros): detect table structs by field types instead of name Replace fragile name-based detection (`starts_with("table")`) with a structural check: table call structs have all fields of type `Vec` (Solidity arrays), while regular `log` calls never use array parameters. * refactor(macros): gate table detection on both name prefix and Vec fields Combine the name-based check (`starts_with("table")`) with the structural Vec field check so both must hold. This prevents accidental table rendering for unrelated structs that happen to have all-Vec fields. --------- Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore: bump `foundry-wallets` dep (#14409) * chore: bump `foundry-wallets` dep + set `browser` & `tempo` features * chore: set features * fix(npm): add release environment for OIDC trusted publishing (#14411) Add `environment: release` to both publish-arch and publish-meta jobs to match the npm trusted publisher configuration on npmjs.com. Without the environment claim in the OIDC token, the registry rejects publishes with a misleading E404. Also refreshes bun.lock to fix frozen-lockfile failures. Amp-Thread-ID: https://ampcode.com/threads/T-019db4f4-ee94-735b-b75d-4f1bcbbb3041 Co-authored-by: Amp * fix(npm): remove deprecated `baseUrl` compiler option (#14413) Amp-Thread-ID: https://ampcode.com/threads/T-019db557-8d02-7723-bfd6-68c4107b9433 Co-authored-by: Amp * chore: bump alloy 2.0.1 (#14417) * fix(wallets): add gas limit margin to handle WebAuthn/P256 signing (#14416) fix(wallets): add gas limit margin to handle WebAuthn/P256 signing * fix(evm): sync gas params when updating executor spec (#14420) * fix(evm): sync gas params when updating executor spec * fix: make clippy happy --------- Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat(lint): add incorrect ERC721 interface lint (#14412) * fix(common): enable `foundry-wallets` browser/tempo feats (#14421) * chore(tests): bump forge-std version (#14422) chore: bump forge-std version used for tests Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil-server): distinguish empty and notification batches (#14405) Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat(anvil): add debug_traceBlockByHash and debug_traceBlockByNumber (#14391) * feat(anvil): add `debug_traceBlockByHash` and `debug_traceBlockByNumber` RPC endpoints Implement the two missing geth debug_ block tracing endpoints that trace all transactions in a block at once, returning per-tx results. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: fmt --------- Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * chore(foundryup): remove tempo fork support (#14324) * chore: remove tempo fork support Tempo is now fully upstream in the main Foundry repo, so the separate network handling is no longer needed. * Apply suggestion from @zerosnacks Update Foundryup version * warn when foundryup --network is ignored * Update foundryup/foundryup Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update foundryup/foundryup Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(lint): add missing visit methods to LateLintVisitor (#14276) * fix(lint): add missing visit methods to LateLintVisitor * fix(lint): dispatch deprecated nested late lint hooks * fix(lint): use by-value nested late lint hooks * test(lint): cover late visitor hooks --------- Co-authored-by: figtracer * fix: remove `network: tempo` from Tempo template (#14424) remove network: tempo from template * feat(anvil): RPC methods for tempo's `TipFeeManager` in `anvil_*` namespace (#14414) * feat(anvil): RPC methods for tempo's `TipFeeManager` in`anvil_*` namespace * feat(anvil): add tests + mint TIP20 tokens to admin before adding FeeAMM liquidity --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): separate queued txs in txpool RPC responses (#14425) * refactor(cli): unify `NetworkVariant` with `NetworkConfigs` (#14426) * refactor(cli): unify `NetworkVariant` with `NetworkConfigs` * chore: clean-ups + tests * fix: after fig's review * fix: skip serialization if `network` is `None` * chore: pin to foundry-wallets release (#14429) pin to foundry-wallets release * fix(config): respect custom Etherscan URL in cast/forge commands (#14319) * fix(config): respect custom Etherscan URL in cast/forge commands Amp-Thread-ID: https://ampcode.com/threads/T-019db9f9-01dd-729a-9f7a-ed150aabf130 Co-authored-by: Amp Co-authored-by: Gustavo Figueiredo * fix lint * fix clippy * chore: update Cargo.lock Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019db9f9-01dd-729a-9f7a-ed150aabf130 * fix: update Cargo.lock from upstream base Amp-Thread-ID: https://ampcode.com/threads/T-019db9f9-01dd-729a-9f7a-ed150aabf130 Co-authored-by: Amp --------- Co-authored-by: Amp Co-authored-by: Gustavo Figueiredo * feat(common): add MPP WebSocket transport (#14404) * feat(common): add MPP WebSocket transport * docs * use alloy's wsbakcend::from_socket * use main repo * remove patches * bump rustls-webpki * fix: only use `MppWsConnect` when MPP key is available * fix: install default rustls crypto provider * fix: clean imports * fix: MPP known host check * fix: MPP only if known endpoint && key available --------- Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * fix(script): preserve exit reason in failed revert decode (#13985) * fix(script): preserve exit reason in failed revert decode * test(script): cover exit reason output in script failures * fix(script): initialize exit_reason in ScriptResult default --------- Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * fix(anvil): fix flaky test_increase_time_by_zero test (#14430) Co-authored-by: Amp * feat(lint): add incorrect ERC20 interface lint (#14428) * feat(lint): add incorrect ERC20 interface lint * test(lint): cover direct-name IERC20 case * fix(lint): revert IncorrectERC20Interface test to use empty IERC20 base Expanding IERC20 with full function signatures caused solc compilation failures (Error 4822: Overriding function return types differ) because IERC20Incorrect inherits from IERC20 and overrides with incompatible return types. Revert to the empty-base pattern used by IncorrectERC721Interface. Amp-Thread-ID: https://ampcode.com/threads/T-019dbb72-89c7-740d-b6cf-b87a1509d3e3 Co-authored-by: Amp * test: clarify incorrect ERC20 interface fixtures * test: align ERC20 fixture with ERC721 --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp * fix(cheatcodes): reject nested debug trace recording (#14423) * fix(cheatcodes): reject nested debug trace recording * test(cheatcodes): restore RecordDebugTrace formatting * chore: bump revm v38 (#14436) * deps: bump tempo to TIP-1016 (rev 2e6e9d1) Patches revm to bluealloy/revm@a1a1824 and op-revm to foundry-rs/op-revm@66388e6 for the InitialAndFloorGas handler change. Patches reth-core to paradigmxyz/reth-core@6b12498 for the Bytecode::new_analyzed unsafe change. Adds gas_limit() and state_gas_used() to AnvilStorageProvider to satisfy the updated PrecompileStorageProvider trait. Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d97c8-3033-770a-bd6c-714c2a8393d5 * fix: resolve CI failures from patched revm - Replace deprecated OpCode::new_unchecked with new_or_unknown - Add gas_refunded field to PrecompileOutput struct literals - Allow foundry-rs/op-revm and paradigmxyz/reth-core git sources in deny.toml Co-Authored-By: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-Authored-By: grandizzy <38490174+grandizzy@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d97c8-3033-770a-bd6c-714c2a8393d5 * fix: align revm/op-revm/tempo revs with tip1016 branch Updates patch revisions to match tempo/tip1016 HEAD: - revm: a1a1824 → ea8d1f5 - op-revm: 66388e6 → 780b812 - tempo: 2e6e9d1 → 4092dfe Fixes CreateInputs::new reservoir parameter for the new revm. Co-Authored-By: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-Authored-By: grandizzy <38490174+grandizzy@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d97c8-3033-770a-bd6c-714c2a8393d5 * fix: pin tempo to tip1016 HEAD (807b57c) Tempo's [patch.crates-io] now includes all revm sub-crates, preventing the invariant workflow's patch propagation from clobbering foundry's patches. Co-Authored-By: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-Authored-By: grandizzy <38490174+grandizzy@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d97c8-3033-770a-bd6c-714c2a8393d5 * bump to later commit in https://github.com/tempoxyz/tempo/pull/2684/commits * chore: bump revm 37→38, tempo crates to tip1016 head - revm 37.0.0 → 38.0.0 (crates.io release, no more git patches) - revm-inspectors 0.38.1 → 0.39.0 - alloy-evm 0.32.0 → 0.33.1 - tempo crates rev f873f0e → c095527 (tip1016 head) - foundry-fork-db, op-revm, foundry-rs/optimism bumped via branches - fix OpCode::new_or_unknown → new_unchecked (API change in revm 38) - add missing Evm::cfg_env impl (new trait method in alloy-evm 0.33.1) Co-Authored-By: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * bump alloy-evm dep to 0.33.2 * chore: bump revm v38 using upstream OP * fix: deny.toml * fix(deps): merge conflict rustls * style: drop cmnt Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: restore cargo-shear --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Federico Gimenez Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: group dependabot updates by ecosystem (#14438) Group minor and patch version updates per ecosystem into single PRs to reduce PR noise. Major version bumps still get individual PRs for careful review. Before: up to 15 PRs/week (5 per ecosystem) After: up to 6 PRs/week (1 grouped + 1 major per ecosystem, worst case) Amp-Thread-ID: https://ampcode.com/threads/T-019dbe78-a376-7248-a549-6ddbe2c960d4 Co-authored-by: George Niculae Co-authored-by: Amp * refactor(evm): remove useless OP EVM wrapper (#14440) Co-authored-by: Copilot * chore(deps): bump the actions-weekly group with 4 updates (#14441) Bumps the actions-weekly group with 4 updates: [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request), [github/codeql-action](https://github.com/github/codeql-action), [taiki-e/install-action](https://github.com/taiki-e/install-action) and [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action). Updates `peter-evans/create-pull-request` from 8.1.0 to 8.1.1 - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/c0f553fe549906ede9cf27b5156039d195d2ece0...5f6978faf089d4d20b00c7766989d076bb2fc7f1) Updates `github/codeql-action` from 4.35.1 to 4.35.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/c10b8064de6f491fea524254123dbe5e09572f13...95e58e9a2cdfd71adc6e0353d5c52f41a045d225) Updates `taiki-e/install-action` from 2.75.16 to 2.75.17 - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/a2352fc6ce487f030a3aa709482d57823eadfb37...58e862542551f667fa44c8a2a4a1d64ad477c96a) Updates `taiki-e/cache-cargo-install-action` from 3.0.5 to 3.0.6 - [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases) - [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/a8b9ecf8e0c0ea09d7481cfc583a5203ecd585b5...f9eed3e4680f27610dc6d8c67be1b88593f7dade) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-weekly - dependency-name: github/codeql-action dependency-version: 4.35.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-weekly - dependency-name: taiki-e/install-action dependency-version: 2.75.17 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-weekly - dependency-name: taiki-e/cache-cargo-install-action dependency-version: 3.0.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions-weekly ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(tests): update gas report snapshot after gas params sync fix (#14439) The deployment cost for CounterWithFallback changed from 132471 to 132459 after #14420 started properly syncing gas parameters when updating the executor spec via set_spec_id. Amp-Thread-ID: https://ampcode.com/threads/T-019dbea7-a331-7269-80a8-b935a7bdaa49 Co-authored-by: Amp * feat: log broken invariant as soon as it is found (#14433) * feat: log broken invariant as soon as it is found * fix * nest failure counts inside metrics * chore: bump foundry-compilers to 0.20.0, foundry-block-explorers to 0.23.0, foundry-fork-db to 0.26.0 (#14443) * chore: bump foundry-compilers to 0.20.0 Amp-Thread-ID: https://ampcode.com/threads/T-019dbf04-adac-755f-b685-5fa64d792acb Co-authored-by: Amp * chore: bump foundry-compilers to 0.20.0, foundry-block-explorers to 0.23.0 Amp-Thread-ID: https://ampcode.com/threads/T-019dbf01-f2de-711e-8edf-581a979f9f0d Co-authored-by: Amp * chore: bump foundry-fork-db to 0.26.0 Amp-Thread-ID: https://ampcode.com/threads/T-019dbf01-f2de-711e-8edf-581a979f9f0d Co-authored-by: Amp * chore: enable zstd feature for foundry-fork-db Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019dbf01-f2de-711e-8edf-581a979f9f0d --------- Co-authored-by: Amp * chore: remove unmaintained devcontainer (#14449) deprecate unmaintained devcontainer * chore: update security contact email (#14450) update link * feat(lint): add block-timestamp lint (#14431) * feat: add ignored_error_codes_from config option (#13841) * chore(deny): remove deprecated repos from whitelist (#14455) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: figtracer Co-authored-by: John Chase <68833933+joohhnnn@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: zerosnacks Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Nuno David <74260683+ndavd@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Louis Peter Sitoe Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: MD Islam Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: mk0walsk Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: Arsh Co-authored-by: Perico Perica Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Federico Gimenez Co-authored-by: George Niculae Co-authored-by: Copilot Co-authored-by: googleworkspace-bot From 5dbd7b227f598a9f5b3678fc6b9bab46089e03ab Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:04:37 +0700 Subject: [PATCH 363/374] Update crates/anvil/server/src/handler.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- crates/anvil/server/src/handler.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/anvil/server/src/handler.rs b/crates/anvil/server/src/handler.rs index 250c486986240..95659d9eefbf6 100644 --- a/crates/anvil/server/src/handler.rs +++ b/crates/anvil/server/src/handler.rs @@ -49,7 +49,9 @@ pub async fn handle_request( Request::Single(call) => handle_call(call, handler).await.map(Response::Single), Request::Batch(calls) => { if calls.is_empty() { - return Some(Response::error(RpcError::invalid_request())); + return Some(Response::Batch(vec![anvil_rpc::response::RpcResponse::from( + RpcError::invalid_request(), + )])); } future::join_all(calls.into_iter().map(move |call| handle_call(call, handler.clone()))) .map(responses_as_batch) From c2f5430a5119d813cfb11ff80d1dcea501cccf83 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 25 Apr 2026 09:11:43 +0000 Subject: [PATCH 364/374] Create static.yml (#472) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: googleworkspace-bot From 1ffa06d70d0930bfff962b653682b93caabc7a09 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:22:36 +0700 Subject: [PATCH 365/374] Delete .circleci/ci_deploy.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail From 47e2bcc418d7209e5679fea3925c68537956349e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:23:40 +0700 Subject: [PATCH 366/374] Delete .circleci/ci_deploy.yml (#496) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- .circleci/ci_deploy.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .circleci/ci_deploy.yml diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml deleted file mode 100644 index 0c8ae5507187d..0000000000000 --- a/.circleci/ci_deploy.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - say-hello: - docker: - - image: cimg/base:current - - steps: - - checkout - - run: - name: "Say hello" - command: "echo Hello, World!" - -workflows: - say-hello-workflow: - jobs: - - say-hello - -- run: - name: Plan a deploy - command: | - circleci run release plan \ - --environment-name="" \ - --component-name="" \ - --target-version="" -# Your job here doing the actual deployment -- run: - name: Update a deploy to SUCCESS - command: circleci run release update --status=SUCCESS - when: on_success -- run: - name: Update planned deploy to FAILED - command: circleci run release update --status=FAILED - when: on_fail From 5e16eb3fc44328c4a0b5b8ff3e0dec0e37d81d7c Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 28 Apr 2026 06:22:07 +0700 Subject: [PATCH 367/374] fix(forge): adjust gas assertion CounterWithFallback (foundry-rs#14465 ) (#498) * chore(deps): bump rui314/setup-mold from 725a8794d15fc7563f59595bd9556495c0564878 to 9c9c13bf4c3f1adef0cc596abc155580bcb04444 (#14442) chore(deps): bump rui314/setup-mold Bumps [rui314/setup-mold](https://github.com/rui314/setup-mold) from 725a8794d15fc7563f59595bd9556495c0564878 to 9c9c13bf4c3f1adef0cc596abc155580bcb04444. - [Commits](https://github.com/rui314/setup-mold/compare/725a8794d15fc7563f59595bd9556495c0564878...9c9c13bf4c3f1adef0cc596abc155580bcb04444) --- updated-dependencies: - dependency-name: rui314/setup-mold dependency-version: 9c9c13bf4c3f1adef0cc596abc155580bcb04444 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update flake.lock (#14458) Co-authored-by: github-actions[bot] * fix(forge): adjust gas assertion `CounterWithFallback` (#14465) * chore: update latest benchmarks (#14467) * ci: split MPP e2e into its own workflow (#14468) * ci: split MPP e2e into its own workflow Move the MPP e2e step from ci-tempo.yml into a standalone ci-mpp.yml workflow so transient HTTP 402 failures from the MPP RPC do not block the Tempo CI workflow. Amp-Thread-ID: https://ampcode.com/threads/T-019dceb8-61e5-734f-b047-17665b4ea7d3 Co-authored-by: Amp * ci: rename sanity-check job to tempo-check Amp-Thread-ID: https://ampcode.com/threads/T-019dceb8-61e5-734f-b047-17665b4ea7d3 Co-authored-by: Amp * ci: rename mpp-e2e job to mpp-check Amp-Thread-ID: https://ampcode.com/threads/T-019dceb8-61e5-734f-b047-17665b4ea7d3 Co-authored-by: Amp --------- Co-authored-by: Amp * Improve GH actions (#14473) * fix(benches): add repos + extra args support to prevent blocking errors (#14470) * fix(benches): add repos + extra args support to prevent blocking errors * fix(ci): set `inputs.repos` default to empty * fix: remove `--verbose` flags * fix: exclude `uniswap/v4-core` `TickMathTestTest` --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> --- .github/workflows/benchmarks.yml | 50 ++++++++++++--- .github/workflows/ci-mpp.yml | 55 ++++++++++++++++ .github/workflows/ci-tempo.yml | 17 +---- .github/workflows/ci.yml | 6 +- .github/workflows/crate-checks.yml | 2 +- .github/workflows/docker-publish.yml | 1 + .github/workflows/docs.yml | 5 +- .github/workflows/release.yml | 7 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 2 +- benches/LATEST.md | 79 ++++++++++++----------- benches/src/lib.rs | 96 +++++++++++++++++----------- benches/src/main.rs | 12 +++- crates/forge/tests/cli/cmd.rs | 2 +- flake.lock | 18 +++--- 16 files changed, 226 insertions(+), 130 deletions(-) create mode 100644 .github/workflows/ci-mpp.yml diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 8465874854551..9b7f834c420c7 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -15,15 +15,44 @@ on: type: string default: "stable,nightly" repos: - description: "Comma-separated list of repos to benchmark (e.g., ithacaxyz/account:main,Vectorized/solady)" + description: "Comma-separated repos to benchmark. Each entry: org/repo[:rev][ ] (e.g. vectorized/solady:v0.1.26 --nmc BrokenTest). Leave empty to use the per-benchmark default repo lists." required: false type: string - default: "ithacaxyz/account:v0.3.2,Vectorized/solady:v0.1.22" + default: "" env: - ITHACAXYZ_ACCOUNT: "ithacaxyz/account:v0.3.2" - VECTORIZED_SOLADY: "Vectorized/solady:v0.1.22" - DEFAULT_REPOS: "ithacaxyz/account:v0.3.2,Vectorized/solady:v0.1.22" + # Repos to benchmark per step. Each comma-separated entry has the form + # org/repo[:rev][ ] + # where anything after the first whitespace is appended to every benchmark + # command for that repo (use this to skip a broken test contract via e.g. + # `--nmc BrokenTest`, so a single failing test does not fail the whole CI). + TEST_REPOS: >- + ithacaxyz/account:v0.5.7, + vectorized/solady:v0.1.26 --nmc 'LifebuoyTest|LibBitTest|Base58Test', + aave/aave-v4:v0.5.11, + uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc 'TickMathTestTest', + sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting + + ISOLATE_TEST_REPOS: >- + ithacaxyz/account:v0.5.7 --nmc SimulateExecuteTest, + vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test', + aave/aave-v4:v0.5.11, + uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc 'TickMathTestTest', + sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting + + BUILD_REPOS: >- + ithacaxyz/account:v0.5.7, + vectorized/solady:v0.1.26, + aave/aave-v4:v0.5.11, + uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75, + sparkdotfi/spark-psm:v1.0.0 + + COVERAGE_REPOS: >- + ithacaxyz/account:v0.5.7, + aave/aave-v4:v0.5.11, + uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75, + sparkdotfi/spark-psm:v1.0.0 + RUSTC_WRAPPER: "sccache" jobs: @@ -48,7 +77,7 @@ jobs: with: toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 @@ -78,7 +107,7 @@ jobs: env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} - REPOS: ${{ github.event.inputs.repos || env.DEFAULT_REPOS }} + REPOS: ${{ github.event.inputs.repos || env.TEST_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ @@ -90,7 +119,7 @@ jobs: env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} - REPOS: ${{ github.event.inputs.repos || env.VECTORIZED_SOLADY }} + REPOS: ${{ github.event.inputs.repos || env.ISOLATE_TEST_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ @@ -102,7 +131,7 @@ jobs: env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} - REPOS: ${{ github.event.inputs.repos || env.DEFAULT_REPOS }} + REPOS: ${{ github.event.inputs.repos || env.BUILD_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ @@ -114,10 +143,11 @@ jobs: env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + REPOS: ${{ github.event.inputs.repos || env.COVERAGE_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ - --repos ${{ env.ITHACAXYZ_ACCOUNT }} \ + --repos "$REPOS" \ --benchmarks forge_coverage \ --output-file forge_coverage_bench.md diff --git a/.github/workflows/ci-mpp.yml b/.github/workflows/ci-mpp.yml new file mode 100644 index 0000000000000..00ec35f8f8aba --- /dev/null +++ b/.github/workflows/ci-mpp.yml @@ -0,0 +1,55 @@ +name: CI MPP + +permissions: {} + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + RUSTC_WRAPPER: "sccache" + +jobs: + mpp-check: + runs-on: depot-ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + steps: + # Checkout the repository + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2 + + # Build and install binaries + - name: Build and install Foundry binaries + run: | + cargo build --profile dev --locked -p forge -p cast -p anvil -p chisel + echo "${{ github.workspace }}/target/debug" >> "$GITHUB_PATH" + + - name: Run MPP e2e test + env: + TEMPO_KEYS_TOML_B64: ${{ secrets.TEMPO_KEYS_TOML_B64 }} + MPP_API_KEY: ${{ secrets.MPP_API_KEY }} + MPP_DEPOSIT: "1000000" + run: | + if [ -z "${TEMPO_KEYS_TOML_B64:-}" ]; then + echo "::warning::TEMPO_KEYS_TOML_B64 secret not set, skipping MPP e2e" + exit 0 + fi + mkdir -p ~/.tempo/wallet + echo "$TEMPO_KEYS_TOML_B64" | tr -d '[:space:]' | base64 -d > ~/.tempo/wallet/keys.toml + ./.github/scripts/tempo-mpp.sh "$(which cast | xargs dirname)" diff --git a/.github/workflows/ci-tempo.yml b/.github/workflows/ci-tempo.yml index 942f595a34977..b4bc98391e72d 100644 --- a/.github/workflows/ci-tempo.yml +++ b/.github/workflows/ci-tempo.yml @@ -36,7 +36,7 @@ env: RUSTC_WRAPPER: "sccache" jobs: - sanity-check: + tempo-check: runs-on: depot-ubuntu-latest timeout-minutes: 60 permissions: @@ -59,21 +59,6 @@ jobs: cargo build --profile dev --locked -p forge -p cast -p anvil -p chisel echo "${{ github.workspace }}/target/debug" >> "$GITHUB_PATH" - - name: Run MPP e2e test - if: github.event_name == 'push' || github.event_name == 'pull_request' - env: - TEMPO_KEYS_TOML_B64: ${{ secrets.TEMPO_KEYS_TOML_B64 }} - MPP_API_KEY: ${{ secrets.MPP_API_KEY }} - MPP_DEPOSIT: "1000000" - run: | - if [ -z "${TEMPO_KEYS_TOML_B64:-}" ]; then - echo "::warning::TEMPO_KEYS_TOML_B64 secret not set, skipping MPP e2e" - exit 0 - fi - mkdir -p ~/.tempo/wallet - echo "$TEMPO_KEYS_TOML_B64" | tr -d '[:space:]' | base64 -d > ~/.tempo/wallet/keys.toml - ./.github/scripts/tempo-mpp.sh "$(which cast | xargs dirname)" - # TODO(upstream): re-enable when flaky devnet faucet is fixed # - name: Run Tempo check on devnet # if: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d5564f94a7e2..9eb90a76cdbfd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - run: cargo test --workspace --doc --locked @@ -89,7 +89,7 @@ jobs: with: toolchain: nightly components: clippy - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - run: cargo clippy --workspace --all-targets --all-features --locked @@ -123,7 +123,7 @@ jobs: - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - name: forge fmt diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index ce2988f3e0260..eb865bddc10e3 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -28,7 +28,7 @@ jobs: - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 with: tool: cargo-hack diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 3372e816328b7..5b7cf1631fea8 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -108,3 +108,4 @@ jobs: labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64 push: true + no-cache: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 288d9d127592f..45d00708394f5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,7 +15,6 @@ concurrency: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full - RUSTC_WRAPPER: "sccache" jobs: docs: @@ -30,9 +29,7 @@ jobs: - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: toolchain: nightly - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - name: Build documentation run: cargo doc --workspace --all-features --no-deps --document-private-items --locked env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 351d8a334362d..9cb1cf51dda24 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -148,12 +148,7 @@ jobs: with: toolchain: stable targets: ${{ matrix.target }} - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - - - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - if: ${{ contains(matrix.runner, 'depot') }} - - run: printf 'RUSTC_WRAPPER=sccache\n' >> "$GITHUB_ENV" - if: ${{ contains(matrix.runner, 'depot') }} + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - name: Apple M1 setup if: matrix.target == 'aarch64-apple-darwin' diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index 7e3fa21d64b9a..d6244f826887e 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -32,7 +32,7 @@ jobs: - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 with: tool: nextest diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index 4446cdbba8ce6..141e3a049a73b 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -36,7 +36,7 @@ jobs: - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master with: toolchain: stable - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 with: tool: nextest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 80a32068a21e3..eaa46c8e79f7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,7 +72,7 @@ jobs: with: toolchain: stable target: ${{ matrix.target }} - - uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 with: tool: nextest diff --git a/benches/LATEST.md b/benches/LATEST.md index 7ea1049a2ac41..238a691229389 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,6 +1,6 @@ # Foundry Benchmark Results -**Date**: 2025-10-02 12:14:23 +**Date**: 2026-04-24 23:10:24 ## Repositories Tested @@ -8,66 +8,67 @@ 2. [Vectorized/solady](https://github.com/Vectorized/solady) 3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) 4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +5. [aave/aave-v4](https://github.com/aave/aave-v4) ## Foundry Versions -- **v1.3.6**: forge Version: 1.3.6-v1.3.6 (d241588 2025-09-16) -- **v1.4.0-rc1**: forge Version: 1.4.0-v1.4.0-rc1 (bd0e4a7 2025-10-01) +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **nightly**: forge Version: 1.6.0-nightly (a249f5c 2026-04-24) ## Forge Test -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 3.17 s | 2.94 s | -| solady | 2.28 s | 2.10 s | -| Uniswap-v4-core | 7.27 s | 6.13 s | -| sparkdotfi-spark-psm | 43.04 s | 44.08 s | +| Repository | v1.5.1 | nightly | +| -------------------- | -------- | -------- | +| vectorized-solady | 1.46 s | 1.38 s | +| aave-aave-v4 | 4m 14.2s | 3m 29.1s | ## Forge Fuzz Test -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------ | ---------- | -| ithacaxyz-account | 3.18 s | 3.02 s | -| solady | 2.39 s | 2.24 s | -| Uniswap-v4-core | 6.84 s | 6.20 s | -| sparkdotfi-spark-psm | 3.07 s | 2.72 s | +| Repository | v1.5.1 | nightly | +| -------------------- | --------- | -------- | +| ithacaxyz-account | 2.81 s | 1.59 s | +| vectorized-solady | 1.40 s | 1.34 s | +| Uniswap-v4-core | 3.01 s | 2.87 s | +| sparkdotfi-spark-psm | 2.04 s | 1.87 s | +| aave-aave-v4 | 3m 46.0s | 3m 17.3s | ## Forge Test (Isolated) -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| solady | 2.26 s | 2.41 s | -| Uniswap-v4-core | 7.22 s | 7.71 s | -| sparkdotfi-spark-psm | 45.53 s | 50.49 s | +| Repository | v1.5.1 | nightly | +| -------------------- | -------- | -------- | +| Uniswap-v4-core | 3.50 s | 3.48 s. | +| aave-aave-v4 | 4m 14.0s | 3m 53.4s | ## Forge Build (No Cache) -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 9.16 s | 9.08 s | -| solady | 14.62 s | 14.69 s | -| Uniswap-v4-core | 2m 3.8s | 2m 5.3s | -| sparkdotfi-spark-psm | 13.17 s | 13.14 s | +| Repository | v1.5.1 | nightly | +| -------------------- | -------- | -------- | +| ithacaxyz-account | 26.06 s | 26.61 s | +| vectorized-solady | 14.20 s | 14.26 s | +| Uniswap-v4-core | 2m 1.3s | 2m 5.0s | +| sparkdotfi-spark-psm | 15.16 s | 15.30 s | +| aave-aave-v4 | 3m 37.0s | 3m 35.1s | ## Forge Build (With Cache) -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | ------- | ---------- | -| ithacaxyz-account | 0.156 s | 0.113 s | -| solady | 0.089 s | 0.094 s | -| Uniswap-v4-core | 0.133 s | 0.127 s | -| sparkdotfi-spark-psm | 0.173 s | 0.131 s | +| Repository | v1.5.1 | nightly | +| -------------------- | ------- | ------- | +| ithacaxyz-account | 0.167 s | 0.201 s | +| vectorized-solady | 0.099 s | 0.098 s | +| Uniswap-v4-core | 0.139 s | 0.140 s | +| sparkdotfi-spark-psm | 0.168 s | 0.173 s | +| aave-aave-v4 | 0.370 s | 0.357 s | ## Forge Coverage -| Repository | v1.3.6 | v1.4.0-rc1 | -| -------------------- | -------- | ---------- | -| ithacaxyz-account | 14.91 s | 13.34 s | -| Uniswap-v4-core | 1m 34.8s | 1m 30.3s | -| sparkdotfi-spark-psm | 3m 49.3s | 3m 40.2s | +| Repository | v1.5.1 | nightly | +| -------------------- | --------- | ---------- | +| Uniswap-v4-core | 1m 13.9s | 1m 10.3s | +| sparkdotfi-spark-psm | 2m 54.7s | 2m 50.0s | +| aave-aave-v4 | 11m 20.8s | 10m 58.7s | ## System Information - **OS**: macos -- **CPU**: 8 -- **Rustc**: rustc 1.90.0-nightly (3014e79f9 2025-07-15) +- **CPU**: 12 +- **Rustc**: rustc 1.95.0 (59807616e 2026-04-14) \ No newline at end of file diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ab3bec614e3cd..7ed8807cbf0f5 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -24,46 +24,53 @@ pub struct RepoConfig { pub org: String, pub repo: String, pub rev: String, + /// Optional extra arguments appended to every benchmark command for this + /// repo (e.g. `--nmc BrokenTest` to skip a broken test contract). + pub extra_args: Option, } impl FromStr for RepoConfig { type Err = eyre::Error; + /// Parse a repo spec of the form `org/repo[:rev][ ]`. + /// + /// Anything after the first whitespace is treated as extra arguments + /// appended to every benchmark command for this repo. fn from_str(spec: &str) -> Result { - // Split by ':' first to separate repo path from optional rev - let parts: Vec<&str> = spec.splitn(2, ':').collect(); - let repo_path = parts[0]; - let custom_rev = parts.get(1).copied(); - - // Now split the repo path by '/' - let path_parts: Vec<&str> = repo_path.split('/').collect(); - if path_parts.len() != 2 { - eyre::bail!("Invalid repo format '{}'. Expected 'org/repo' or 'org/repo:rev'", spec); - } - - let org = path_parts[0]; - let repo = path_parts[1]; + let spec = spec.trim(); + // Anything after the first whitespace is per-repo extra args. + let (head, extra_args) = match spec.split_once(char::is_whitespace) { + Some((head, rest)) => (head, Some(rest.trim().to_string())), + None => (spec, None), + }; - // Try to find this repo in BENCHMARK_REPOS to get the full config - let existing_config = BENCHMARK_REPOS.iter().find(|r| r.org == org && r.repo == repo); + let (repo_path, custom_rev) = match head.split_once(':') { + Some((path, rev)) => (path, Some(rev)), + None => (head, None), + }; - let config = if let Some(existing) = existing_config { - // Use existing config but allow custom rev to override - let mut config = existing.clone(); - if let Some(rev) = custom_rev { - config.rev = rev.to_string(); - } - config - } else { - // Create new config with custom rev or default - // Name should follow the format: org-repo (with hyphen) - Self { + let (org, repo) = repo_path.split_once('/').ok_or_else(|| { + eyre::eyre!("Invalid repo format '{spec}'. Expected 'org/repo' or 'org/repo:rev'") + })?; + + // Inherit defaults from BENCHMARK_REPOS when available, otherwise build + // a fresh config. Custom rev / extra args always override. + let mut config = BENCHMARK_REPOS + .iter() + .find(|r| r.org == org && r.repo == repo) + .cloned() + .unwrap_or_else(|| Self { name: format!("{org}-{repo}"), org: org.to_string(), repo: repo.to_string(), - rev: custom_rev.unwrap_or("main").to_string(), - } - }; + rev: "main".to_string(), + extra_args: None, + }); + + if let Some(rev) = custom_rev { + config.rev = rev.to_string(); + } + config.extra_args = extra_args; let _ = sh_println!("Parsed repo spec '{spec}' -> {config:?}"); Ok(config) @@ -78,12 +85,14 @@ pub fn default_benchmark_repos() -> Vec { org: "ithacaxyz".to_string(), repo: "account".to_string(), rev: "main".to_string(), + extra_args: None, }, RepoConfig { name: "solady".to_string(), org: "Vectorized".to_string(), repo: "solady".to_string(), rev: "main".to_string(), + extra_args: None, }, ] } @@ -113,6 +122,8 @@ pub struct BenchmarkProject { pub name: String, pub temp_project: TempProject, pub root_path: PathBuf, + /// Optional extra arguments appended to every benchmark command. + pub extra_args: Option, } impl BenchmarkProject { @@ -169,7 +180,20 @@ impl BenchmarkProject { Self::install_npm_dependencies(&root_path)?; sh_println!(" ✅ Project {} setup complete at {}", config.name, root); - Ok(Self { name: config.name.clone(), root_path, temp_project }) + Ok(Self { + name: config.name.clone(), + root_path, + temp_project, + extra_args: config.extra_args.clone(), + }) + } + + /// Append `self.extra_args` to a benchmark shell command, if any. + fn cmd(&self, base: &str) -> String { + match self.extra_args.as_deref().map(str::trim).filter(|s| !s.is_empty()) { + Some(extra) => format!("{base} {extra}"), + None => base.to_string(), + } } /// Install npm dependencies if package.json exists @@ -296,7 +320,7 @@ impl BenchmarkProject { self.hyperfine( "forge_test", version, - "forge test", + &self.cmd("forge test"), runs, Some("forge build"), None, @@ -315,7 +339,7 @@ impl BenchmarkProject { self.hyperfine( "forge_build_with_cache", version, - "FOUNDRY_LINT_LINT_ON_BUILD=false forge build", + &self.cmd("FOUNDRY_LINT_LINT_ON_BUILD=false forge build"), runs, None, Some("forge build"), @@ -335,7 +359,7 @@ impl BenchmarkProject { self.hyperfine( "forge_build_no_cache", version, - "FOUNDRY_LINT_LINT_ON_BUILD=false forge build", + &self.cmd("FOUNDRY_LINT_LINT_ON_BUILD=false forge build"), runs, Some("forge clean"), None, @@ -355,7 +379,7 @@ impl BenchmarkProject { self.hyperfine( "forge_fuzz_test", version, - r#"forge test --match-test "test[^(]*\([^)]+\)""#, + &self.cmd(r#"forge test --match-test "test[^(]*\([^)]+\)""#), runs, Some("forge build"), None, @@ -376,7 +400,7 @@ impl BenchmarkProject { self.hyperfine( "forge_coverage", version, - "forge coverage --ir-minimum", + &self.cmd("forge coverage --ir-minimum"), runs, None, None, @@ -396,7 +420,7 @@ impl BenchmarkProject { self.hyperfine( "forge_isolate_test", version, - "forge test --isolate", + &self.cmd("forge test --isolate"), runs, Some("forge build"), None, diff --git a/benches/src/main.rs b/benches/src/main.rs index f3361bf30b6e6..60e815cecb0ec 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -48,8 +48,16 @@ struct Cli { #[clap(long, value_delimiter = ',')] benchmarks: Option>, - /// Run only on specific repositories (comma-separated in org/repo[:rev] format: - /// ithacaxyz/account,Vectorized/solady:main,foundry-rs/foundry:v1.0.0) + /// Comma-separated list of repositories to benchmark. + /// + /// Each entry has the form `org/repo[:rev][ ]`. Anything + /// after the first whitespace is appended to every benchmark command for + /// that repo (handy to skip a broken test contract via e.g. + /// `--nmc BrokenTest`). + /// + /// Examples: + /// `ithacaxyz/account:v0.5.7` + /// `vectorized/solady:v0.1.26 --nmc 'LifebuoyTest|LibBitTest'` #[clap(long, value_delimiter = ',')] repos: Option>, } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 13bf5cfdbacce..d3f1503faf569 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -3016,7 +3016,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "test/FallbackWithCalldataTest.sol:CounterWithFallback", "deployment": { - "gas": 132471, + "gas": 132459, "size": 396 }, "functions": { diff --git a/flake.lock b/flake.lock index 4fa81efa24f11..27f426f491da6 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1776497206, - "narHash": "sha256-Em+RSdFnwyyKPGUBFtQYtVjm+1UvIc9gOR91Y22zlzg=", + "lastModified": 1777102577, + "narHash": "sha256-ycoy9svZOQgyInu/lwO7IEQtlP5liqYhEcF9m9hPRbM=", "owner": "nix-community", "repo": "fenix", - "rev": "df2295365fb081fe0745449762a771290782c22d", + "rev": "f37403486c59376cd285f9685a8ef8ff25c09a3c", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1776329215, - "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", + "lastModified": 1776949667, + "narHash": "sha256-GMSVw35Q+294GlrTUKlx087E31z7KurReQ1YHSKp5iw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b86751bc4085f48661017fa226dee99fab6c651b", + "rev": "01fbdeef22b76df85ea168fbfe1bfd9e63681b30", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1776441750, - "narHash": "sha256-1rVfG+mj8R4ze+lSYCa4iAv7FzrB03Cprtxmd1MfZak=", + "lastModified": 1776800521, + "narHash": "sha256-f8YJfwAOsLFpIoqZuX3yF69UvMLrkx7iVzMH1pJU7cM=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "251df518d73abb5c5d573c4d5d266a3edae9ca5a", + "rev": "8954b66d43225e62c92e8bbcc8500191b5cceb1e", "type": "github" }, "original": { From 28430c4ab494cb40fb4f31bb686869d6b6c06a2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 12:44:42 +0700 Subject: [PATCH 368/374] chore(deps): bump the cargo-weekly group with 5 updates (#507) Bumps the cargo-weekly group with 5 updates: | Package | From | To | | --- | --- | --- | | [mimalloc](https://github.com/purpleprotocol/mimalloc_rust) | `0.1.48` | `0.1.50` | | [rustls](https://github.com/rustls/rustls) | `0.23.38` | `0.23.39` | | [jiff](https://github.com/BurntSushi/jiff) | `0.2.23` | `0.2.24` | | [idna_adapter](https://github.com/hsivonen/idna_adapter) | `1.1.0` | `1.2.1` | | [interprocess](https://github.com/kotauskas/interprocess) | `2.4.0` | `2.4.2` | Updates `mimalloc` from 0.1.48 to 0.1.50 - [Release notes](https://github.com/purpleprotocol/mimalloc_rust/releases) - [Commits](https://github.com/purpleprotocol/mimalloc_rust/compare/v0.1.48...v0.1.50) Updates `rustls` from 0.23.38 to 0.23.39 - [Release notes](https://github.com/rustls/rustls/releases) - [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md) - [Commits](https://github.com/rustls/rustls/compare/v/0.23.38...v/0.23.39) Updates `jiff` from 0.2.23 to 0.2.24 - [Release notes](https://github.com/BurntSushi/jiff/releases) - [Changelog](https://github.com/BurntSushi/jiff/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/jiff/compare/jiff-static-0.2.23...jiff-static-0.2.24) Updates `idna_adapter` from 1.1.0 to 1.2.1 - [Commits](https://github.com/hsivonen/idna_adapter/compare/v1.1.0...v1.2.1) Updates `interprocess` from 2.4.0 to 2.4.2 - [Release notes](https://github.com/kotauskas/interprocess/releases) - [Commits](https://github.com/kotauskas/interprocess/compare/2.4.0...2.4.2) --- updated-dependencies: - dependency-name: mimalloc dependency-version: 0.1.50 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: rustls dependency-version: 0.23.39 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: jiff dependency-version: 0.2.24 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: idna_adapter dependency-version: 1.2.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-weekly - dependency-name: interprocess dependency-version: 2.4.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d113b82b696ed..253ed6afdf6fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -537,7 +537,7 @@ snapbox = { version = "0.6", features = ["json", "regex", "term-svg"] } # Use unicode-rs which has a smaller binary size than the default ICU4X as the IDNA backend, used # by the `url` crate. # See the `idna_adapter` README.md for more details: https://docs.rs/crate/idna_adapter/latest -idna_adapter = "=1.1.0" +idna_adapter = "=1.2.1" [patch.crates-io] # https://github.com/rust-cli/rexpect/pull/106 From 6c44631ab8e2dfa35ccf2b7a26f1eb70a038eeaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 15:47:16 +0700 Subject: [PATCH 369/374] chore(deps): bump strum from 0.27.2 to 0.28.0 (#509) Bumps [strum](https://github.com/Peternator7/strum) from 0.27.2 to 0.28.0. - [Release notes](https://github.com/Peternator7/strum/releases) - [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md) - [Commits](https://github.com/Peternator7/strum/compare/v0.27.2...v0.28.0) --- updated-dependencies: - dependency-name: strum dependency-version: 0.28.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> From b8e3a0c2d38faa684c0a8b9cce4dd5504eb28149 Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Sat, 2 May 2026 15:56:21 +0700 Subject: [PATCH 370/374] gas-snapshot --- counter/.gas-snapshot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counter/.gas-snapshot b/counter/.gas-snapshot index ef525c09384e6..797ceebb2f595 100644 --- a/counter/.gas-snapshot +++ b/counter/.gas-snapshot @@ -1,2 +1,2 @@ -CounterTest:testFuzz_SetNumber(uint256) (runs: 256, μ: 30177, ~: 32354) +CounterTest:testFuzz_SetNumber(uint256) (runs: 256, μ: 30410, ~: 32354) CounterTest:test_Increment() (gas: 31851) \ No newline at end of file From 432d718616712d3586c067e773eeef120e222dd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 16:25:58 +0700 Subject: [PATCH 371/374] chore(deps): bump similar-asserts from 1.7.0 to 2.0.0 (#508) Bumps [similar-asserts](https://github.com/mitsuhiko/similar-asserts) from 1.7.0 to 2.0.0. - [Changelog](https://github.com/mitsuhiko/similar-asserts/blob/main/CHANGELOG.md) - [Commits](https://github.com/mitsuhiko/similar-asserts/compare/1.7.0...2.0.0) --- updated-dependencies: - dependency-name: similar-asserts dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 +++++++++++++------- Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65a696a8be26d..7b52c5f580fad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4625,7 +4625,7 @@ dependencies = [ "semver 1.0.28", "serde", "serde_json", - "similar", + "similar 2.7.0", "similar-asserts", "solar-compiler", "soldeer-commands", @@ -4678,7 +4678,7 @@ dependencies = [ "foundry-config", "foundry-test-utils", "itertools 0.14.0", - "similar", + "similar 2.7.0", "snapbox", "solar-compiler", "toml", @@ -10708,6 +10708,12 @@ name = "similar" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "similar" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d93e861ede2e497b47833469b8ec9d5c07fa4c78ce7a00f6eb7dd8168b4b3f" dependencies = [ "bstr", "unicode-segmentation", @@ -10715,12 +10721,12 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.7.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" +checksum = "997e6ca38e97437973fc9f7f50a50d1274cacd874341a4960fea90067291038c" dependencies = [ - "console 0.15.11", - "similar", + "console 0.16.3", + "similar 3.1.0", ] [[package]] @@ -10790,7 +10796,7 @@ dependencies = [ "regex", "serde", "serde_json", - "similar", + "similar 2.7.0", "snapbox-macros", ] diff --git a/Cargo.toml b/Cargo.toml index c93dc44c47884..469d5bea793e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -481,7 +481,7 @@ rustls = "0.23" semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } -similar-asserts = "1.7" +similar-asserts = "2.0" soldeer-commands = "=0.10.0" soldeer-core = { version = "=0.10.1", features = ["serde"] } strum = "0.28" From 53c39b4bd4832c5539b28900a9c63df4006e7cc7 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 7 May 2026 01:06:45 +0700 Subject: [PATCH 372/374] ci: sign release archives, docker images, and publish SBOMs (#519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * anvil: unify Tempo nonce markers across send RPCs (#14536) Co-authored-by: Amp Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Matthias Seitz * fix(forge): `flaky_gas_report_fallback_with_calldata` deployment cost (#14545) * chore(lint): add missing lints to README (#14551) * chore(bench): update `benchmark.sh` (#14548) Co-authored-by: Matthias Seitz * chore(clippy): fix for_kv_map and useless_borrows_in_formatting (#14554) * chore(clippy): fix for_kv_map and useless_borrows_in_formatting Amp-Thread-ID: https://ampcode.com/threads/T-019df0f9-62e7-74b8-bd5e-da2acce678fb Co-authored-by: Amp * chore(clippy): drop redundant borrows in cheatcodes assert formatters Amp-Thread-ID: https://ampcode.com/threads/T-019df0f9-62e7-74b8-bd5e-da2acce678fb Co-authored-by: Amp --------- Co-authored-by: Amp * fix(ci): use `PATH_USD` fallback fee token in Mail templates (#14546) * chore(deps): bump the actions-weekly group with 3 updates (#14497) * refactor(chisel): migrate to solar (#14532) * feat(lint): add too-many-digits lint (#14549) * feat: feature-gate optimism deps in common-fmt, common, cast (#14539) * feat(forge): support per-test network selection via inline config (#14530) * feat(cli): `--tempo.expires` retry-safe mode (TIP-1009 expiring nonces) (#14521) * fix(forge): `per_test_network_routing` match undeterministic order (#14557) output * chore(ci): run tempo mainnet and testnet checks before devnet (#14556) * Update flake.lock (#14553) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/f374034' (2026-04-25) → 'github:nix-community/fenix/74c1591' (2026-05-02) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/8954b66' (2026-04-21) → 'github:rust-lang/rust-analyzer/64cdaeb' (2026-05-01) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/01fbdee' (2026-04-23) → 'github:NixOS/nixpkgs/c6d6588' (2026-05-01) Co-authored-by: github-actions[bot] * chore(bench): update benchmark results (#14552) * fix(forge): ignore ETH_RPC_URL for test forking (#14555) Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat(cast): add Tempo keychain policy commands (#14531) * feat(cast): add tempo keychain policy commands * fix(cast): address keychain policy review * fix(cli): fix jsonwebtoken panic (#14562) `cast` panicked with this message coming from jsonwebtoken: ``` Call CryptoProvider::install_default() before this point to select a provider manually, or make sure exactly one of the 'rust_crypto' and 'aws_lc_rs' features is enabled. See the documentation of the CryptoProvider type for more information. ``` This seemingly was introduced with the bump of jsonwebtoken to 10. Now it requires you to pick one backend used by default controlled by the compile time cargo features or call `CryptoProvider::install_default()` at the beginning. I realized that probably it would be better to just select the feature and I picked `aws_lc_rs` as it seems to be increasingly a default and we already are using the C toolchain. Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(cli): tidy ETH_RPC_URL handling and add forge regression test (#14559) Follow-up to #14555: - Drop the redundant flashbots branch in RpcOpts::dict; self.url(None) already returns FLASHBOTS_URL when --flashbots is set, so the subsequent overwrite was dead code. - Inline the resolve_rpc_url helper back into RpcCommonOpts::url; it was only called from one place and added unneeded surface area. - Restore the doc comment on RpcCommonOpts and document why ETH_RPC_URL is intentionally not a clap env on the shared field (so EvmArgs cannot inherit it). - Add an integration test that runs forge config with ETH_RPC_URL set in the environment and asserts that eth_rpc_url stays None, directly exercising the regression scenario from #14538. Amp-Thread-ID: https://ampcode.com/threads/T-019df243-267f-7779-93e1-5d6686082444 Co-authored-by: zerosnacks Co-authored-by: Amp * feat(cast): open Tempo wallet fund flow for MPP failures (#14505) * feat(cast): open Tempo wallet fund flow for MPP failures * ci(tempo): skip network checks without rpc secrets * Revert "ci(tempo): skip network checks without rpc secrets" This reverts commit f8dd70163f850b854888fd1c962174e1663284f4. * fix(common): address mpp funding review --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * ci: sign release archives, docker images, and publish SBOMs (#14563) - release.yml: emit per-archive sha256 + SPDX SBOM (Syft), cosign keyless sign-blob of the archive, and use actions/attest@v4 for both build provenance and SBOM attestations. Upload all artifacts to the draft release. - docker-publish.yml: enable BuildKit SBOM, capture the build digest, cosign keyless sign each pushed tag, and publish a Sigstore-signed SLSA provenance attestation via actions/attest with push-to-registry. - SECURITY.md: document how external users verify archives and the docker image (gh attestation, cosign, plain sha256, buildx imagetools). - README.md: link to the new verification section. * perf(common): short-circuit `find_by_name_or_identifier` instead of `collect` (#14514) * feat(foundryup): retry GitHub API fetches on transient errors (#14566) GitHub api.github.com occasionally returns transient 403s on certain VMs (per-IP rate limiting / WAF hiccups), causing foundryup to fail to resolve the latest stable / nightly release tag, e.g.: foundryup: fetching latest nightly releases from foundry-rs/foundry... Error: curl: (56) The requested URL returned error: 403 foundryup: failed to fetch releases from GitHub API Add curl/wget retry logic to the `fetch` helper (used exclusively for GitHub API releases endpoints): - curl: --retry 5 --retry-delay 2 --retry-max-time 60, plus --retry-all-errors when supported (curl 7.71+, feature-detected so older curl does not hard-fail). --retry-all-errors is required to retry HTTP 403, which is not in curl's default retryable set. - wget fallback: --tries=5 --waitretry=2 --retry-on-http-error=403,408,429,5xx. `fetch` now buffers to a temp file before emitting to stdout, since curl's --retry-all-errors is unsafe with piped consumers (mid-stream retries can duplicate bytes). Existing callers pipe into awk/grep. Tunable via FOUNDRYUP_MAX_RETRIES (default 5). `download` (binary tarballs, attestations, manpages) is intentionally left unchanged — those rarely fail and changing them affects the attestation existence check semantics. Bumps installer version 1.8.1 -> 1.8.2. Amp-Thread-ID: https://ampcode.com/threads/T-019df2f5-9b97-717a-b959-cf7cbc7ca3bb Co-authored-by: Amp * feat(lint): project-wide passes + pragma-inconsistent (#14543) * feat(lint): project-wide passes + pragma-inconsistent * rm hashset, msg * test(lint): exhaustive pragma-inconsistent coverage + clearer testdata names (#14561) * test(lint): exhaustive coverage for pragma-inconsistent Follow-up to #14543 expanding test coverage for the cross-file `pragma-inconsistent` lint across the syntax variants users encounter in real Solidity projects. Multi-file scenarios (added as `forgetest!` cases in `crates/forge/tests/cli/lint.rs`, since they cannot be expressed in a single `.sol` testdata file): - Negative (must NOT warn): - all files use the same exact pragma (`0.8.20`) - all files use the same caret pragma (`^0.8.20`) - single file in the project - Positive (must warn): - duplicates among a conflict -- two identical files plus one different pragma still emits three warnings - Mixed: - file without an explicit pragma uses the test-utils default (`add_raw_source` is used to bypass the auto-injected pragma) Source bodies are pulled out into module-level `const` raw strings so rustfmt does not collapse the inline `\n`-escaped strings into wide horizontal blobs. Single-file scenarios (added as `.sol` files under `crates/lint/testdata/` in the existing `//~NOTE:` annotation style): - `PragmaInconsistentCaretVsTilde.sol`: `^0.8.20` vs `~0.8.20` - `PragmaInconsistentRangeVsExact.sol`: `>=0.8.0 <0.9.0` vs `0.8.20` -- range satisfies exact but lint is intentionally string-based, matching SLITHER-W1078 - `PragmaInconsistentOrVsExact.sol`: `0.8.20 || 0.8.21` vs `0.8.20` - `PragmaInconsistentThreeDistinct.sol`: `>=0.8.0`, `^0.8.0`, `~0.8.0` -- verifies the `others` list contains every other variant * test(lint): rename pragma-inconsistent testdata to describe the case under test The two testdata files added in #14543 were named `PragmaInconsistent.sol` and `PragmaInconsistent2.sol`, which made them look like duplicates. They actually exercise distinct edge cases of the same string-based detection: - `PragmaInconsistentCaretAboveExact.sol` (was `PragmaInconsistent.sol`): caret range whose lower bound is strictly below the exact version (`^0.8.0` + `0.8.18`). - `PragmaInconsistentCaretMatchesExact.sol` (was `PragmaInconsistent2.sol`): caret range whose lower bound equals the exact version (`^0.8.20` + `0.8.20`) -- the looks-the-same-but-still-distinct case that guards SLITHER-W1078 parity (no semver intersection). Amp-Thread-ID: https://ampcode.com/threads/T-019df243-267f-7779-93e1-5d6686082444 Co-authored-by: Amp --------- Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp * refactor(script): reuse shared Tempo CLI opts (#14558) * deps: bump tempo to 6bf9903 (T6 hardfork) + fix alloy-evm 0.34 compat (#14567) * deps: bump tempo to 6bf9903 (T6 hardfork) Bumps tempo crates to 6bf9903d, adding the T6 hardfork variant to TempoHardfork. Without this, cast's tempo_forkSchedule lookup parses the chain's reported active fork ("T6") into TempoHardfork::FromStr, fails because T6 was unknown to the enum, and silently returns is_hardfork_active(T3) = false. That made 'cast keychain auth' fall back to the legacy authorizeKey selector and revert with LegacyAuthorizeKeySelectorChanged on any T6 chain. Also bumps alloy-evm to 0.34 and the optimism git pin to develop (e3b59e7) so alloy-op-evm picks up an EvmFactory impl built against alloy-evm 0.34. Removes the now-unused paradigmxyz/reth-core [patch] entries. No source changes; lockfile churn is transitive only. * fix: adapt AnvilBlockExecutor to alloy-evm 0.34.0 breaking changes - Add Send + 'static bounds to TxResult impl for AnvilTxResult - Change commit_transaction return type from Result to GasOutput - Remove .expect() on commit_transaction call site Amp-Thread-ID: https://ampcode.com/threads/T-019df322-c0f1-73e7-858c-5ca2d242ddb4 * style: rustfmt commit_transaction signature Amp-Thread-ID: https://ampcode.com/threads/T-019df322-c0f1-73e7-858c-5ca2d242ddb4 --------- Co-authored-by: Centaur AI * docs: add forge lint rule docs (#14571) * feat(forge): add fuzz run selection (#14522) * feat(forge): add fuzz run selection * fix(fuzz): make metadata builder const * test(fuzz): cover generated seed replay * fix(forge): persist fuzz worker for run replay * fix(evm): satisfy clippy in fuzz replay * fix(fuzz): reuse fuzz run metadata * forge(lint/docs): validate deployed forge lint docs (#14573) test: validate deployed forge lint docs * feat: gate foundry-primitives behind optimism feature (#14572) * fix(ci): increase permissions for the enhanced attestation writing (#14584) * increase permissions for artifact writing * apply write permissions to release-docker * feat(hardforks, networks): gate optimism behind cargo feature (#14581) * fix(forge): encode Tempo creates as AA calls (#14585) * feat(anvil): gate optimism behind cargo feature (#14577) Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): introduce `vaddr` cmd for TIP-1022 (#14508) * feat(cast): introduce `vaddr` cmd for tip-1022 * fix: doc * chore: touch-ups * add tests * chore: move tests to tempo ci * feat: add vaddr watch test * feat: count 0 hadling, add `no_register` flag * fix: remove sweep subcommand * fix: make clippy happy * feat(bench): nightly regression tracking workflow (#14586) * fix(cli): fix release version strings for immutable tags, bump to 1.7.1 (#14496) * Fix release version metadata for immutable tags Amp-Thread-ID: https://ampcode.com/threads/T-019dd617-b29f-7409-8523-9858a1504f17 Co-authored-by: Amp * Derive nightly release suffix from commit SHA Amp-Thread-ID: https://ampcode.com/threads/T-019dd617-b29f-7409-8523-9858a1504f17 Co-authored-by: Amp * Apply suggestion from @zerosnacks * Apply suggestion from @zerosnacks * Apply suggestion from @zerosnacks * bump to v1.7.1 * avoid appending whole sha hash, not necessary, handle version cmp correctly. after v1.7.1 release we need to bump to v1.7.2 for nightlies following it to compare correctly * Make foundryVersionCmp tolerate new version format and add tests - Strip both pre-release ('-nightly', '-dev') and build metadata ('+..') from SEMVER_VERSION before comparison so the cheatcode keeps working for tagged releases (which have no '-' separator). - Extract strip_semver_metadata helper and add Rust unit tests covering all SEMVER_VERSION shapes, version_cmp ordering, and parse_version rejection of pre-release/build/garbage input. - Extend the Solidity test suite for vm.getFoundryVersion()/foundryVersionCmp/foundryVersionAtLeast: validate MAJOR.MINOR.PATCH parseability, build profile value, cmp/atLeast invariant, and error paths for invalid user-supplied versions. Amp-Thread-ID: https://ampcode.com/threads/T-019dd971-fcb7-7149-9680-f0134130844c Co-authored-by: Amp * fix(test): drop view from solidity tests using assert helpers and fix fmt - assertTrue/assertEq aren't view, so testGetFoundryVersionBuildProfile and testFoundryVersionCmpAndAtLeastAreConsistent can't be view either. - Collapse the buildType assertion onto one line to satisfy forge fmt. Amp-Thread-ID: https://ampcode.com/threads/T-019dd971-fcb7-7149-9680-f0134130844c Co-authored-by: Amp * test(version): assert build profile is non-empty instead of debug|release The dist profile (used for distributed release binaries) is also valid; just require non-empty so any future profile works. Amp-Thread-ID: https://ampcode.com/threads/T-019dd971-fcb7-7149-9680-f0134130844c Co-authored-by: Amp * Normalize nightly- to nightly in release_version Ensures tarball and Docker nightly artifacts produce the same version string. The commit identifier is already included in the SemVer build metadata (after `+`), so collapsing `nightly-` to `nightly` avoids duplicating the SHA in the pre-release tag. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019df79e-d4c9-707c-85eb-2efbf59160b3 --------- Co-authored-by: Centaur AI Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks * fix(evm): query `state_snapshot.storage` in `ForkDbStateSnapshot::storage_ref` (#14007) * fix(evm): query `state_snapshot.storage` in `ForkDbStateSnapshot::storage_ref` * test(evm): cover `ForkDbStateSnapshot::storage_ref` snapshot lookup * fix(cast): consistent `--json` output for `keychain` subcommands (#14590) - `keychain rl`: wrap remaining limit in `{"remaining":"..."}` object instead of emitting a bare JSON string - `keychain policy add-call`: emit `{"status":"already_present","target":"..."}` when the rule already exists, instead of plain text - `send_keychain_tx`: wrap sponsor hash in `{"sponsor_hash":"0x..."}` object when --tempo.print-sponsor-hash is used with --json Add CLI tests covering the rl and sponsor-hash JSON output shapes. * feat(tempo): add sponsored transaction plumbing (#14560) * feat(tempo): add sponsored transaction plumbing * addressing mablr comments * fix tempo sponsor signer future layout * preserve json output for tempo sponsor preview * fix(cast): `--json` output support for `vaddr` (#14591) * feat(tempo): add named nonce lanes (#14527) * fix(cheatcodes): transfer value for payable mock calls (#14547) * test: updated tests * fix: execute value transfer * test: improve * imp: review item * test: vm.prank test * imp: moved mocked-call handling after prank application --------- Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat(lint): add inline-assembly lint (#14575) * feat(lint): add inline-assembly lint * lint(inline-assembly): also recognize `/// @solidity memory-safe-assembly` NatSpec Amp-Thread-ID: https://ampcode.com/threads/T-019df4b6-1b76-734c-9a9b-29db9fb7d461 Co-authored-by: Amp --------- Co-authored-by: Amp * refactor(script): remove `ScriptConfig::{fee_token,expires_at}` in favour of `TempoOpts` (#14594) * feat(evm-core): gate optimism behind cargo feature (#14593) * fix(cli): resolve Tempo expires once (#14595) fix(cli): resolve tempo expires once * feat(cli): gate optimism behind cargo feature (#14596) * fix(anvil): classify EVM halts as transaction rejections (#14592) Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat: drop optimism deps under no-default-features (#14600) * fix(forge): `--fuzz-seed` parameter is not effective in `forge coverage` (#14610) fix --fuzz-seed not effective in forge coverage * fix(foundryup): mirror tag resolution for install & use (#14611) * fix(foundryup): mirror tag resolution for install & use * fix(foundryup): mirror semver version normalization in `use` `install` auto-prepends `v` to bare semver versions (e.g. `1.7.0` -> `v1.7.0`) so the on-disk directory is always `v`-prefixed. `use` was doing a literal lookup, so `foundryup -u 1.7.0` failed even though `foundryup -i 1.7.0` had succeeded. Broaden the channel `case` in `use()` to also match bare semver inputs (`MAJOR.MINOR.PATCH[-prerelease]`) so they go through the same `resolve_version_and_tag` normalizer. The pattern is intentionally tighter than `install`'s `[[:digit:]]*` so locally-built versions whose names happen to start with a digit are still looked up literally. Amp-Thread-ID: https://ampcode.com/threads/T-019dfc78-8557-712b-9944-bbff9a4a3b76 Co-authored-by: Amp * chore(foundryup): clarify tag-resolution log and error messages Distinguish the GitHub API tag-resolution phase from the actual binary download by consistently referring to "release tag(s)" in the `resolve_version_and_tag` helper's `say` and `err` messages. Amp-Thread-ID: https://ampcode.com/threads/T-019dfc78-8557-712b-9944-bbff9a4a3b76 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp * fix(ci): keep no-default builds free of op deps (#14612) * feat: cast unauthorized flow → wallet.tempo access-key authorization (#14517) * feat: cast unauthorized flow → wallet.tempo access-key authorization Amp-Thread-ID: https://ampcode.com/threads/T-019df174-9538-713b-b8c9-5001b1ad4719 Co-authored-by: Amp * fmt * feat(cast): replace TEMPO_NO_BROWSER env with flag * revert token addresses --------- Co-authored-by: Amp Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * docs(expect-emit): clarify next-call semantics and warn about caught-revert leak (#14620) docs(cheatcodes): clarify expectEmit next-call semantics and caught-revert leak expectEmit is a 'next call' assertion. If the call immediately after expectEmit reverts and the revert is swallowed by the caller (low-level call or try/catch), the unmatched expectation can leak forward and be satisfied by a later unrelated emission, silently turning a broken test green. Document the constraint on the natspec for both no-arg and topic-checking overloads, and regenerate cheatcodes.json. Refs: https://github.com/foundry-rs/foundry/issues/14618 Amp-Thread-ID: https://ampcode.com/threads/T-019dfd96-7a03-7249-8c10-af20ee2729f5 Co-authored-by: Amp * fix(cheatcodes): enforce `expectRevert` reverter address for CREATE frames (#14615) * fix(cheatcodes): enforce `expectRevert` reverter address for CREATE frames The reverter address argument to `vm.expectRevert` was silently ignored when the innermost reverting frame was a CREATE (top-level or nested), because create_end never populated `expected_revert.reverted_by`. Mirror call_end's logic in create_end: when the outcome reverts and a reverter address is expected, record outcome.address (revm guarantees this is Some(would-be address) whenever the constructor executed). Adds positive regression tests for top-level and nested-CREATE reverts, and a negative regression test asserting wrong-reverter now fails. Co-authored-by: Amp * improve coverage * add Derek's suggested test cases * fix: forge fmt for ExpectRevert.t.sol Amp-Thread-ID: https://ampcode.com/threads/T-019dfdc5-5414-70b6-9f49-cb5797a37a29 Co-authored-by: Amp --------- Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(script): keep plain Tempo broadcasts non-AA (#14616) * fix(script): don't force Tempo AA fee_token from --network tempo alone Plain --network tempo (or any selection that just sets the network to Tempo) does not by itself imply a Tempo AA / type 0x76 transaction. Defaulting tempo.common.fee_token to PATH_USD_ADDRESS solely from evm_opts.networks.is_tempo() caused every unsigned broadcast tx to flow through TempoOpts::apply, which set fee_token on the request and promoted it to the Tempo AA tx envelope. Signers that only know how to sign ordinary Ethereum transactions (e.g. the Ledger Ethereum app) then rejected the transaction with 'received an unexpected empty response'. Gate the default on an actual Tempo AA opt-in: - --batch (Tempo batch txs are themselves AA and need a fee token), or - any explicit --tempo.* flag (sponsor, expiring nonce, nonce key/lane, ...) which already forces an AA tx and benefits from a default fee token. Explicit --tempo.fee-token continues to win over the default in all cases, and non-Tempo networks never default the fee token. Add unit tests for each scenario. Amp-Thread-ID: https://ampcode.com/threads/T-019dfd37-2354-712f-95b1-2584fd47ad5e Co-authored-by: Amp * fix(script): don't force eth_estimateGas on plain Tempo broadcasts Plain --network tempo produces an ordinary EIP-1559/legacy transaction (see tempo-alloy::TempoTransactionRequest::output_tx_type), so the local simulation gas estimate is sufficient. Forcing RPC re-estimation in this case can surface node-side errors such as 'gas required exceeds allowance (0)' (Geth-style balance/gasPrice cap from eth_estimateGas) on flows that previously worked, including Ledger-signed broadcasts that just got unblocked from the type 0x76 regression. Match tempo-foundry's behaviour: only force eth_estimateGas on Tempo when the user has actually opted into Tempo AA semantics (--batch or any explicit --tempo.* flag). Extract the gating into needs_tempo_aa_rpc_estimate(...) and add focused unit tests mirroring the fee-token gating tests. Amp-Thread-ID: https://ampcode.com/threads/T-019dfd37-2354-712f-95b1-2584fd47ad5e Co-authored-by: Amp * fix(script): don't re-estimate plain Tempo chain broadcasts --------- Co-authored-by: Amp * fix(cheatcodes): preserve reverts with `expectEmit` (#14619) * test: added regression test * fix: re-order revert handling * refactor: simplify * lint: fmt * polish: tighten comment, extend test with revert reason and custom error Amp-Thread-ID: https://ampcode.com/threads/T-019dfd96-7a03-7249-8c10-af20ee2729f5 Co-authored-by: Amp --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Amp * feat(lint): add tx-origin detector (#14589) * feat(lint): add tx-origin detector * test(lint): address tx-origin review feedback * fix: ui bless * fix(lint): cover tx-origin index and ternary predicates * test(lint): bless tx-origin snapshot --------- Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * refactor(tempo): prepare batch access key txs w/ helper (#14597) fix(tempo): prepare batch access key txs before estimation * fix(anvil): respect non-zero genesis block in Otterscan APIs (#14490) fix(anvil): respect non-zero genesis block in Otterscan APIs The three Otterscan address-history endpoints (`ots_searchTransactionsBefore`/`After`, `ots_getTransactionBySenderAndNonce`) hardcoded `unwrap_or(1)` / `unwrap_or_default()` as the lower bound of their block scan, which breaks when `genesis_block_number` is non-zero (e.g. `genesis.json` `number: 73`). Expose `Backend::genesis_number()` and fall back to `genesis_number() + 1` in non-fork mode, mirroring the existing post-fork `f.block_number() + 1` convention. --------- Co-authored-by: Isagi Yates Co-authored-by: Amp Co-authored-by: steven Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: figtracer Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Sergei Shulepov Co-authored-by: zerosnacks Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cui Co-authored-by: Centaur AI Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: srdtrk <59252793+srdtrk@users.noreply.github.com> Co-authored-by: Mikhail Mikheev <16622558+mmv08@users.noreply.github.com> Co-authored-by: lazymio Co-authored-by: Emma Jamieson-Hoare Co-authored-by: VIkions <99107287+vikions@users.noreply.github.com> Co-authored-by: Aïssata --- .github/scripts/commit-and-read-benchmarks.sh | 114 -- .github/scripts/commit-benchmark-results.sh | 75 + .github/scripts/compare-nightly.sh | 56 + .github/scripts/read-benchmark-results.sh | 37 + .github/scripts/tempo-check.sh | 86 +- .github/workflows/benchmarks-nightly.yml | 217 +++ .github/workflows/benchmarks.yml | 73 +- .github/workflows/ci-tempo.yml | 44 +- .github/workflows/ci.yml | 17 + .github/workflows/crate-checks.yml | 2 +- .github/workflows/docker-publish.yml | 30 + .github/workflows/nix.yml | 4 +- .github/workflows/npm.yml | 4 +- .github/workflows/release.yml | 78 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 4 +- Cargo.lock | 1011 +++++------ Cargo.toml | 98 +- README.md | 2 + SECURITY.md | 109 ++ benches/LATEST.md | 134 +- benches/src/main.rs | 40 +- benches/src/results.rs | 19 + benchmark.sh | 60 +- crates/anvil/Cargo.toml | 34 +- crates/anvil/core/Cargo.toml | 10 +- crates/anvil/src/cmd.rs | 27 +- crates/anvil/src/config.rs | 6 +- crates/anvil/src/eth/api.rs | 44 +- crates/anvil/src/eth/backend/executor.rs | 23 +- crates/anvil/src/eth/backend/mem/mod.rs | 267 ++- crates/anvil/src/eth/backend/mem/optimism.rs | 61 + .../anvil/src/eth/{error.rs => error/mod.rs} | 69 +- crates/anvil/src/eth/error/optimism.rs | 62 + crates/anvil/src/eth/otterscan/api.rs | 19 +- crates/anvil/src/eth/pool/transactions.rs | 4 +- crates/anvil/src/eth/sign.rs | 8 +- crates/anvil/src/{evm.rs => evm/mod.rs} | 84 +- crates/anvil/src/evm/optimism.rs | 87 + crates/anvil/src/lib.rs | 3 + crates/anvil/tests/it/main.rs | 1 + crates/anvil/tests/it/revert.rs | 50 + crates/cast/Cargo.toml | 17 +- crates/cast/src/args.rs | 7 + crates/cast/src/cmd/batch_mktx.rs | 27 +- crates/cast/src/cmd/batch_send.rs | 32 +- crates/cast/src/cmd/call.rs | 25 +- crates/cast/src/cmd/keychain.rs | 1022 ++++++++++- crates/cast/src/cmd/mktx.rs | 44 +- crates/cast/src/cmd/mod.rs | 3 + crates/cast/src/cmd/run.rs | 17 +- crates/cast/src/cmd/send.rs | 72 +- crates/cast/src/cmd/tempo.rs | 45 + crates/cast/src/cmd/tip20/mine.rs | 23 +- crates/cast/src/cmd/tip20/mod.rs | 2 +- crates/cast/src/cmd/vaddr/create.rs | 181 ++ crates/cast/src/cmd/vaddr/mod.rs | 131 ++ crates/cast/src/cmd/vaddr/resolve.rs | 52 + crates/cast/src/cmd/vaddr/watch.rs | 108 ++ crates/cast/src/cmd/wallet/mod.rs | 13 +- crates/cast/src/lib.rs | 4 +- crates/cast/src/opts.rs | 25 +- crates/cast/src/tempo.rs | 3 + crates/cast/src/tx.rs | 30 +- crates/cast/tests/cli/keychain.rs | 76 + crates/cast/tests/cli/main.rs | 119 ++ crates/cheatcodes/Cargo.toml | 10 + crates/cheatcodes/assets/cheatcodes.json | 4 +- crates/cheatcodes/spec/src/vm.rs | 6 + crates/cheatcodes/src/inspector.rs | 145 +- crates/cheatcodes/src/test/assert.rs | 4 +- crates/cheatcodes/src/version.rs | 67 +- crates/chisel/Cargo.toml | 8 +- crates/chisel/src/executor.rs | 1617 ++++++++--------- crates/chisel/src/source.rs | 561 ++---- crates/chisel/tests/it/repl/mod.rs | 20 + crates/cli/Cargo.toml | 7 + crates/cli/src/opts/evm.rs | 11 + crates/cli/src/opts/rpc.rs | 56 +- crates/cli/src/opts/rpc_common.rs | 7 +- crates/cli/src/opts/tempo.rs | 320 +++- crates/cli/src/utils/tempo.rs | 193 +- crates/common/Cargo.toml | 20 +- crates/common/build.rs | 20 +- crates/common/fmt/Cargo.toml | 8 +- crates/common/fmt/src/ui.rs | 8 + crates/common/src/contracts.rs | 14 +- crates/common/src/provider/mpp/keys.rs | 73 +- crates/common/src/provider/mpp/session.rs | 10 + crates/common/src/provider/mpp/transport.rs | 922 +++++++++- crates/common/src/provider/mpp/ws.rs | 4 + .../common/src/provider/runtime_transport.rs | 6 +- crates/common/src/tempo/auth.rs | 494 +++++ crates/common/src/tempo/keystore.rs | 147 +- crates/common/src/tempo/mod.rs | 186 ++ crates/common/src/transactions/builder.rs | 57 +- crates/common/src/transactions/receipt.rs | 2 + crates/config/src/fuzz.rs | 6 + crates/config/src/inline/mod.rs | 39 + crates/debugger/Cargo.toml | 8 + crates/doc/Cargo.toml | 4 + crates/doc/src/writer/as_doc.rs | 4 +- crates/evm/core/Cargo.toml | 24 +- crates/evm/core/src/decode.rs | 4 +- crates/evm/core/src/env.rs | 605 +++--- crates/evm/core/src/evm/mod.rs | 21 +- crates/evm/core/src/evm/op.rs | 22 +- crates/evm/core/src/fork/database.rs | 53 +- crates/evm/core/src/lib.rs | 3 + crates/evm/core/src/opts.rs | 7 +- crates/evm/coverage/Cargo.toml | 4 + crates/evm/evm/Cargo.toml | 13 + crates/evm/evm/src/executors/fuzz/mod.rs | 125 +- crates/evm/evm/src/executors/invariant/mod.rs | 2 +- crates/evm/fuzz/Cargo.toml | 9 + crates/evm/fuzz/src/lib.rs | 35 +- crates/evm/hardforks/Cargo.toml | 8 +- crates/evm/hardforks/src/lib.rs | 74 +- crates/evm/networks/Cargo.toml | 8 +- crates/evm/networks/src/lib.rs | 238 ++- crates/evm/networks/src/optimism.rs | 25 + crates/evm/traces/Cargo.toml | 4 + crates/fmt/Cargo.toml | 4 + crates/fmt/src/state/mod.rs | 2 +- crates/forge/Cargo.toml | 14 +- crates/forge/assets/tempo/MailTemplate.s.sol | 2 +- crates/forge/assets/tempo/MailTemplate.t.sol | 2 +- crates/forge/src/cmd/coverage.rs | 7 +- crates/forge/src/cmd/create.rs | 43 +- crates/forge/src/cmd/snapshot.rs | 7 +- crates/forge/src/cmd/test/mod.rs | 226 ++- crates/forge/src/cmd/test/summary.rs | 4 +- crates/forge/src/gas_report.rs | 2 +- crates/forge/src/multi_runner.rs | 32 + crates/forge/src/runner.rs | 24 +- crates/forge/tests/cli/cmd.rs | 4 +- crates/forge/tests/cli/config.rs | 28 + crates/forge/tests/cli/failure_assertions.rs | 7 +- crates/forge/tests/cli/inline_config.rs | 104 ++ crates/forge/tests/cli/lint.rs | 289 ++- crates/forge/tests/cli/lint/geiger.rs | 10 +- crates/forge/tests/cli/script.rs | 2 +- crates/forge/tests/cli/test_cmd/fuzz.rs | 145 ++ crates/forge/tests/cli/test_cmd/repros.rs | 60 + .../tests/fixtures/ExpectRevertFailures.t.sol | 57 + crates/lint/Cargo.toml | 4 + crates/lint/README.md | 8 + crates/lint/docs/README.md | 52 + crates/lint/docs/_template.md | 28 + crates/lint/docs/asm-keccak256.md | 42 + crates/lint/docs/block-timestamp.md | 44 + crates/lint/docs/boolean-cst.md | 37 + crates/lint/docs/boolean-equal.md | 34 + crates/lint/docs/could-be-immutable.md | 42 + crates/lint/docs/custom-errors.md | 45 + crates/lint/docs/divide-before-multiply.md | 32 + crates/lint/docs/erc20-unchecked-transfer.md | 43 + crates/lint/docs/incorrect-erc20-interface.md | 42 + .../lint/docs/incorrect-erc721-interface.md | 48 + crates/lint/docs/incorrect-shift.md | 37 + crates/lint/docs/inline-assembly.md | 69 + crates/lint/docs/interface-file-naming.md | 31 + crates/lint/docs/interface-naming.md | 31 + crates/lint/docs/missing-zero-check.md | 39 + crates/lint/docs/mixed-case-function.md | 32 + crates/lint/docs/mixed-case-variable.md | 36 + crates/lint/docs/multi-contract-file.md | 37 + crates/lint/docs/named-struct-fields.md | 31 + crates/lint/docs/pascal-case-struct.md | 31 + crates/lint/docs/pragma-inconsistent.md | 41 + crates/lint/docs/rtlo.md | 32 + .../lint/docs/screaming-snake-case-const.md | 30 + .../docs/screaming-snake-case-immutable.md | 31 + crates/lint/docs/too-many-digits.md | 32 + crates/lint/docs/tx-origin.md | 34 + crates/lint/docs/unaliased-plain-import.md | 34 + crates/lint/docs/unchecked-call.md | 34 + crates/lint/docs/unsafe-cheatcode.md | 35 + crates/lint/docs/unsafe-typecast.md | 40 + crates/lint/docs/unused-import.md | 40 + crates/lint/docs/unused-state-variables.md | 39 + crates/lint/docs/unwrapped-modifier-logic.md | 51 + crates/lint/src/linter/mod.rs | 2 + crates/lint/src/linter/project.rs | 92 + crates/lint/src/sol/info/inline_assembly.rs | 71 + crates/lint/src/sol/info/mod.rs | 12 + crates/lint/src/sol/info/pragma_directive.rs | 71 + crates/lint/src/sol/info/too_many_digits.rs | 50 + crates/lint/src/sol/macros.rs | 42 +- crates/lint/src/sol/med/mod.rs | 4 + crates/lint/src/sol/med/tx_origin.rs | 101 + crates/lint/src/sol/mod.rs | 133 +- crates/lint/testdata/BlockTimestamp.stderr | 24 +- crates/lint/testdata/BooleanCst.stderr | 10 +- crates/lint/testdata/BooleanEqual.stderr | 14 +- crates/lint/testdata/CouldBeImmutable.stderr | 14 +- crates/lint/testdata/CustomErrors.stderr | 10 +- .../lint/testdata/DivideBeforeMultiply.stderr | 12 +- crates/lint/testdata/Imports.stderr | 26 +- .../testdata/IncorrectERC20Interface.stderr | 30 +- .../testdata/IncorrectERC721Interface.stderr | 38 +- crates/lint/testdata/IncorrectShift.stderr | 10 +- crates/lint/testdata/InlineAssembly.sol | 110 ++ crates/lint/testdata/InlineAssembly.stderr | 96 + crates/lint/testdata/Keccak256.sol | 1 + crates/lint/testdata/Keccak256.stderr | 36 +- crates/lint/testdata/MissingZeroCheck.stderr | 46 +- crates/lint/testdata/MixedCase.stderr | 38 +- crates/lint/testdata/MultiContractFile.stderr | 10 +- .../MultiContractFile_InterfaceLibrary.stderr | 6 +- crates/lint/testdata/NamedStructFields.stderr | 2 +- .../PragmaInconsistentCaretAboveExact.sol | 7 + .../PragmaInconsistentCaretAboveExact.stderr | 16 + .../PragmaInconsistentCaretMatchesExact.sol | 7 + ...PragmaInconsistentCaretMatchesExact.stderr | 16 + .../PragmaInconsistentCaretVsTilde.sol | 7 + .../PragmaInconsistentCaretVsTilde.stderr | 16 + .../testdata/PragmaInconsistentOrVsExact.sol | 7 + .../PragmaInconsistentOrVsExact.stderr | 16 + .../PragmaInconsistentRangeVsExact.sol | 7 + .../PragmaInconsistentRangeVsExact.stderr | 16 + .../PragmaInconsistentThreeDistinct.sol | 8 + .../PragmaInconsistentThreeDistinct.stderr | 24 + crates/lint/testdata/Rtlo.stderr | 48 +- crates/lint/testdata/RtloCommentsOnly.stderr | 8 +- .../lint/testdata/ScreamingSnakeCase.stderr | 16 +- crates/lint/testdata/StructPascalCase.stderr | 12 +- crates/lint/testdata/TooManyDigits.sol | 73 + crates/lint/testdata/TooManyDigits.stderr | 72 + crates/lint/testdata/TxOrigin.sol | 65 + crates/lint/testdata/TxOrigin.stderr | 72 + crates/lint/testdata/UncheckedCall.stderr | 16 +- .../testdata/UncheckedTransferERC20.stderr | 22 +- crates/lint/testdata/UnsafeCheatcodes.stderr | 26 +- crates/lint/testdata/UnsafeTypecast.stderr | 330 ++-- .../lint/testdata/UnusedStateVariables.stderr | 10 +- .../testdata/UnwrappedModifierLogic.stderr | 22 +- crates/primitives/Cargo.toml | 17 +- crates/primitives/src/network/mod.rs | 10 +- crates/primitives/src/network/optimism.rs | 47 + crates/primitives/src/network/receipt.rs | 40 +- crates/primitives/src/transaction/envelope.rs | 281 +-- crates/primitives/src/transaction/mod.rs | 6 +- crates/primitives/src/transaction/optimism.rs | 300 +++ crates/primitives/src/transaction/receipt.rs | 114 +- crates/primitives/src/transaction/request.rs | 110 +- crates/script-sequence/Cargo.toml | 4 + crates/script/Cargo.toml | 12 + crates/script/src/broadcast.rs | 143 +- crates/script/src/lib.rs | 131 +- crates/script/src/runner.rs | 10 +- crates/script/src/verify.rs | 2 +- crates/sol-macro-gen/Cargo.toml | 4 + crates/test-utils/Cargo.toml | 4 + crates/verify/Cargo.toml | 9 + docs/dev/lintrules.md | 2 + flake.lock | 18 +- foundryup/README.md | 4 +- foundryup/foundryup | 135 +- testdata/default/cheats/ExpectRevert.t.sol | 85 + .../default/cheats/GetFoundryVersion.t.sol | 51 + testdata/default/cheats/MockCall.t.sol | 41 +- testdata/default/cheats/MockCalls.t.sol | 4 + 264 files changed, 13431 insertions(+), 4233 deletions(-) delete mode 100755 .github/scripts/commit-and-read-benchmarks.sh create mode 100755 .github/scripts/commit-benchmark-results.sh create mode 100755 .github/scripts/compare-nightly.sh create mode 100755 .github/scripts/read-benchmark-results.sh create mode 100644 .github/workflows/benchmarks-nightly.yml create mode 100644 crates/anvil/src/eth/backend/mem/optimism.rs rename crates/anvil/src/eth/{error.rs => error/mod.rs} (91%) create mode 100644 crates/anvil/src/eth/error/optimism.rs rename crates/anvil/src/{evm.rs => evm/mod.rs} (64%) create mode 100644 crates/anvil/src/evm/optimism.rs create mode 100644 crates/cast/src/cmd/tempo.rs create mode 100644 crates/cast/src/cmd/vaddr/create.rs create mode 100644 crates/cast/src/cmd/vaddr/mod.rs create mode 100644 crates/cast/src/cmd/vaddr/resolve.rs create mode 100644 crates/cast/src/cmd/vaddr/watch.rs create mode 100644 crates/cast/src/tempo.rs create mode 100644 crates/cast/tests/cli/keychain.rs create mode 100644 crates/common/src/tempo/auth.rs create mode 100644 crates/evm/networks/src/optimism.rs create mode 100644 crates/lint/docs/README.md create mode 100644 crates/lint/docs/_template.md create mode 100644 crates/lint/docs/asm-keccak256.md create mode 100644 crates/lint/docs/block-timestamp.md create mode 100644 crates/lint/docs/boolean-cst.md create mode 100644 crates/lint/docs/boolean-equal.md create mode 100644 crates/lint/docs/could-be-immutable.md create mode 100644 crates/lint/docs/custom-errors.md create mode 100644 crates/lint/docs/divide-before-multiply.md create mode 100644 crates/lint/docs/erc20-unchecked-transfer.md create mode 100644 crates/lint/docs/incorrect-erc20-interface.md create mode 100644 crates/lint/docs/incorrect-erc721-interface.md create mode 100644 crates/lint/docs/incorrect-shift.md create mode 100644 crates/lint/docs/inline-assembly.md create mode 100644 crates/lint/docs/interface-file-naming.md create mode 100644 crates/lint/docs/interface-naming.md create mode 100644 crates/lint/docs/missing-zero-check.md create mode 100644 crates/lint/docs/mixed-case-function.md create mode 100644 crates/lint/docs/mixed-case-variable.md create mode 100644 crates/lint/docs/multi-contract-file.md create mode 100644 crates/lint/docs/named-struct-fields.md create mode 100644 crates/lint/docs/pascal-case-struct.md create mode 100644 crates/lint/docs/pragma-inconsistent.md create mode 100644 crates/lint/docs/rtlo.md create mode 100644 crates/lint/docs/screaming-snake-case-const.md create mode 100644 crates/lint/docs/screaming-snake-case-immutable.md create mode 100644 crates/lint/docs/too-many-digits.md create mode 100644 crates/lint/docs/tx-origin.md create mode 100644 crates/lint/docs/unaliased-plain-import.md create mode 100644 crates/lint/docs/unchecked-call.md create mode 100644 crates/lint/docs/unsafe-cheatcode.md create mode 100644 crates/lint/docs/unsafe-typecast.md create mode 100644 crates/lint/docs/unused-import.md create mode 100644 crates/lint/docs/unused-state-variables.md create mode 100644 crates/lint/docs/unwrapped-modifier-logic.md create mode 100644 crates/lint/src/linter/project.rs create mode 100644 crates/lint/src/sol/info/inline_assembly.rs create mode 100644 crates/lint/src/sol/info/pragma_directive.rs create mode 100644 crates/lint/src/sol/info/too_many_digits.rs create mode 100644 crates/lint/src/sol/med/tx_origin.rs create mode 100644 crates/lint/testdata/InlineAssembly.sol create mode 100644 crates/lint/testdata/InlineAssembly.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol create mode 100644 crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentOrVsExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentOrVsExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentRangeVsExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentThreeDistinct.sol create mode 100644 crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr create mode 100644 crates/lint/testdata/TooManyDigits.sol create mode 100644 crates/lint/testdata/TooManyDigits.stderr create mode 100644 crates/lint/testdata/TxOrigin.sol create mode 100644 crates/lint/testdata/TxOrigin.stderr create mode 100644 crates/primitives/src/network/optimism.rs create mode 100644 crates/primitives/src/transaction/optimism.rs diff --git a/.github/scripts/commit-and-read-benchmarks.sh b/.github/scripts/commit-and-read-benchmarks.sh deleted file mode 100755 index 358b53a73155a..0000000000000 --- a/.github/scripts/commit-and-read-benchmarks.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Script to commit benchmark results and read them for GitHub Actions output -# Usage: ./commit-and-read-benchmarks.sh - -OUTPUT_DIR="${1:-benches}" -GITHUB_EVENT_NAME="${2:-pull_request}" -GITHUB_REPOSITORY="${3:-}" - -# Global variable for branch name -BRANCH_NAME="" - -# Function to commit benchmark results -commit_results() { - echo "Configuring git..." - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - # For PR runs, fetch and checkout the PR branch to ensure we're up to date - if [ "$GITHUB_EVENT_NAME" = "pull_request" ] && [ -n "${GITHUB_HEAD_REF:-}" ]; then - echo "Fetching latest changes for PR branch: $GITHUB_HEAD_REF" - git fetch origin "$GITHUB_HEAD_REF" - git checkout -B "$GITHUB_HEAD_REF" "origin/$GITHUB_HEAD_REF" - fi - - echo "Adding benchmark file..." - git add "$OUTPUT_DIR/LATEST.md" - - if git diff --staged --quiet; then - echo "No changes to commit" - else - echo "Committing benchmark results..." - git commit -m "chore(\`benches\`): update benchmark results - -🤖 Generated with [Foundry Benchmarks](https://github.com/${GITHUB_REPOSITORY}/actions) - -Co-Authored-By: github-actions " - - echo "Pushing to repository..." - if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then - # For manual runs, we're on a new branch - git push origin "$BRANCH_NAME" - elif [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then - # For PR runs, push to the PR branch - if [ -n "${GITHUB_HEAD_REF:-}" ]; then - echo "Pushing to PR branch: $GITHUB_HEAD_REF" - git push origin "$GITHUB_HEAD_REF" - else - echo "Error: GITHUB_HEAD_REF not set for pull_request event" - exit 1 - fi - else - # This workflow should only run on workflow_dispatch or pull_request - echo "Error: Unexpected event type: $GITHUB_EVENT_NAME" - echo "This workflow only supports 'workflow_dispatch' and 'pull_request' events" - exit 1 - fi - echo "Successfully pushed benchmark results" - fi -} - -# Function to read benchmark results and output for GitHub Actions -read_results() { - if [ -f "$OUTPUT_DIR/LATEST.md" ]; then - echo "Reading benchmark results..." - - # Output full results - { - echo 'results<> "$GITHUB_OUTPUT" - - # Format results for PR comment - echo "Formatting results for PR comment..." - FORMATTED_COMMENT=$("$(dirname "$0")/format-pr-comment.sh" "$OUTPUT_DIR/LATEST.md") - - { - echo 'pr_comment<> "$GITHUB_OUTPUT" - - echo "Successfully read and formatted benchmark results" - else - echo 'results=No benchmark results found.' >> "$GITHUB_OUTPUT" - echo 'pr_comment=No benchmark results found.' >> "$GITHUB_OUTPUT" - echo "Warning: No benchmark results found at $OUTPUT_DIR/LATEST.md" - fi -} - -# Main execution -echo "Starting benchmark results processing..." - -# Create new branch for manual runs -if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then - echo "Manual workflow run detected, creating new branch..." - BRANCH_NAME="benchmarks/results-$(date +%Y%m%d-%H%M%S)" - git checkout -b "$BRANCH_NAME" - echo "Created branch: $BRANCH_NAME" - - # Output branch name for later use - echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT" -fi - -# Always commit benchmark results -echo "Committing benchmark results..." -commit_results - -# Always read results for output -read_results - -echo "Benchmark results processing complete" \ No newline at end of file diff --git a/.github/scripts/commit-benchmark-results.sh b/.github/scripts/commit-benchmark-results.sh new file mode 100755 index 0000000000000..f7dba8980fd64 --- /dev/null +++ b/.github/scripts/commit-benchmark-results.sh @@ -0,0 +1,75 @@ +#!/bin/bash +set -euo pipefail + +# Script to commit and push benchmark results. +# +# This script is intended to run from the lightweight `publish-results` job, +# which checks out the repo with credentials and only operates on the +# trusted artifact produced by the benchmark job. Keeping the write-scoped +# token away from the bench job (which runs untrusted third-party builds) +# limits the blast radius of a compromised dependency. +# +# Usage: ./commit-benchmark-results.sh + +OUTPUT_DIR="${1:-benches}" +GITHUB_EVENT_NAME="${2:-workflow_dispatch}" +GITHUB_REPOSITORY="${3:-}" + +if [ ! -f "$OUTPUT_DIR/LATEST.md" ]; then + echo "Error: $OUTPUT_DIR/LATEST.md not found, nothing to commit" + exit 1 +fi + +echo "Configuring git..." +git config --local user.email "action@github.com" +git config --local user.name "GitHub Action" + +# Decide which branch to commit to based on the event. +BRANCH_NAME="" +case "$GITHUB_EVENT_NAME" in + workflow_dispatch) + echo "Manual workflow run detected, creating new branch..." + BRANCH_NAME="benchmarks/results-$(date +%Y%m%d-%H%M%S)" + git checkout -b "$BRANCH_NAME" + echo "Created branch: $BRANCH_NAME" + ;; + pull_request) + if [ -z "${GITHUB_HEAD_REF:-}" ]; then + echo "Error: GITHUB_HEAD_REF not set for pull_request event" + exit 1 + fi + echo "Fetching latest changes for PR branch: $GITHUB_HEAD_REF" + git fetch origin "$GITHUB_HEAD_REF" + git checkout -B "$GITHUB_HEAD_REF" "origin/$GITHUB_HEAD_REF" + BRANCH_NAME="$GITHUB_HEAD_REF" + ;; + *) + echo "Error: Unexpected event type: $GITHUB_EVENT_NAME" + echo "This workflow only supports 'workflow_dispatch' and 'pull_request' events" + exit 1 + ;; +esac + +# Always emit the branch name so downstream steps (e.g. PR creation) can use it. +echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT" + +echo "Adding benchmark file..." +git add "$OUTPUT_DIR/LATEST.md" + +if git diff --staged --quiet; then + echo "No changes to commit" + echo "committed=false" >> "$GITHUB_OUTPUT" + exit 0 +fi + +echo "Committing benchmark results..." +git commit -m "chore(\`benches\`): update benchmark results + +🤖 Generated with [Foundry Benchmarks](https://github.com/${GITHUB_REPOSITORY}/actions) + +Co-Authored-By: github-actions " + +echo "Pushing to repository..." +git push origin "$BRANCH_NAME" +echo "Successfully pushed benchmark results to $BRANCH_NAME" +echo "committed=true" >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/compare-nightly.sh b/.github/scripts/compare-nightly.sh new file mode 100755 index 0000000000000..674cc0fe01754 --- /dev/null +++ b/.github/scripts/compare-nightly.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Compare two nightly benchmark JSON summaries and report regressions. +# +# Usage: compare-nightly.sh [warn_pct] [fail_pct] +# Exits 0 if no regressions, 1 if any metric exceeds fail_pct. +# Exits 0 gracefully when prev.json is missing (first run / gap > 7 days). +set -euo pipefail + +PREV_JSON="${1:-}" +TODAY_JSON="${2:-}" +WARN="${3:-1}" +FAIL="${4:-3}" + +PREV_JSON="$PREV_JSON" TODAY_JSON="$TODAY_JSON" WARN="$WARN" FAIL="$FAIL" \ +python3 - <<'EOF' +import json, os, sys + +warn = float(os.environ["WARN"]) +fail = float(os.environ["FAIL"]) + +prev_path = os.environ.get("PREV_JSON", "") +prev = json.load(open(prev_path)) if prev_path and os.path.isfile(prev_path) else {} +with open(os.environ["TODAY_JSON"]) as f: + today = json.load(f) + +print("## Nightly Benchmark Regression Report\n") +print("| Benchmark | Previous | Today | Δ | Status |") +print("|-----------|----------|-------|---|--------|") + +has_regression = False +all_keys = sorted(prev.keys() | today.keys()) +for key in all_keys: + t = today.get(key) + p = prev.get(key) + if t is None: + print(f"| `{key}` | {p:.5f}s | N/A | — | ⚠️ Missing |") + has_regression = True + continue + if p is None: + print(f"| `{key}` | N/A | {t:.5f}s | — | 🆕 New |") + continue + delta = (t - p) / p * 100 + if delta >= fail: + status = "🔴 Regression" + has_regression = True + elif delta >= warn: + status = "🟡 Warning" + elif delta <= -warn: + status = "🟢 Improvement" + else: + status = "➡️ OK" + sign = "+" if delta > 0 else "" + print(f"| `{key}` | {p}s | {t}s | {sign}{delta:.1f}% | {status} |") + +sys.exit(1 if has_regression else 0) +EOF diff --git a/.github/scripts/read-benchmark-results.sh b/.github/scripts/read-benchmark-results.sh new file mode 100755 index 0000000000000..548611a7d204a --- /dev/null +++ b/.github/scripts/read-benchmark-results.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -euo pipefail + +# Script to read benchmark results and emit them as GitHub Actions outputs. +# This script performs no git operations — it only reads the combined +# benchmark file and writes outputs for the workflow to consume. +# +# Usage: ./read-benchmark-results.sh + +OUTPUT_DIR="${1:-benches}" + +echo "Reading benchmark results from $OUTPUT_DIR..." + +if [ -f "$OUTPUT_DIR/LATEST.md" ]; then + # Output full results + { + echo 'results<> "$GITHUB_OUTPUT" + + # Format results for PR comment + echo "Formatting results for PR comment..." + FORMATTED_COMMENT=$("$(dirname "$0")/format-pr-comment.sh" "$OUTPUT_DIR/LATEST.md") + + { + echo 'pr_comment<> "$GITHUB_OUTPUT" + + echo "Successfully read and formatted benchmark results" +else + echo 'results=No benchmark results found.' >> "$GITHUB_OUTPUT" + echo 'pr_comment=No benchmark results found.' >> "$GITHUB_OUTPUT" + echo "Warning: No benchmark results found at $OUTPUT_DIR/LATEST.md" +fi diff --git a/.github/scripts/tempo-check.sh b/.github/scripts/tempo-check.sh index b730c466bde55..3caea992cfe7e 100755 --- a/.github/scripts/tempo-check.sh +++ b/.github/scripts/tempo-check.sh @@ -445,7 +445,7 @@ echo -e "\n=== CAST SEND WITH SPONSOR (--tempo.sponsor-signature) ===" # Test sponsored transactions using pre-signed signature. # Step 1: Get the fee_payer_signature_hash using --tempo.print-sponsor-hash # Step 2: Sign it with the sponsor's private key -# Step 3: Send with --tempo.sponsor-signature +# Step 3: Send with --tempo.sponsor and --tempo.sponsor-signature # Step 1: Get the hash that the sponsor needs to sign FEE_PAYER_HASH=$(cast mktx ${FEE_TOKEN_ARG[@]+"${FEE_TOKEN_ARG[@]}"} --rpc-url "$ETH_RPC_URL" \ @@ -460,7 +460,7 @@ printf "Sponsor signature: %s\n" "$SPONSOR_SIG" # Step 3: Send the sponsored transaction with the signature RECEIPT=$(cast send ${FEE_TOKEN_ARG[@]+"${FEE_TOKEN_ARG[@]}"} --rpc-url "$ETH_RPC_URL" \ 0x86A2EE8FAf9A840F7a2c64CA3d51209F9A02081D 'increment()' --private-key "$PK" \ - --tempo.sponsor-signature "$SPONSOR_SIG" --json) + --tempo.sponsor "$SPONSOR_ADDR" --tempo.sponsor-signature "$SPONSOR_SIG" --json) # Verify the fee_payer in the receipt matches the sponsor address RECEIPT_FEE_PAYER=$(echo "$RECEIPT" | jq -r '.feePayer // .fee_payer // empty') @@ -897,3 +897,85 @@ check_has_code "Nonce" "0x4e4F4E4345000000000000000000000000000000" check_has_code "AccountKeychain" "0xaAAAaaAA00000000000000000000000000000000" echo -e "\n=== CHISEL FORK TESTS COMPLETE ===" + +# --- cast virtual-address (TIP-1022) tests --- + +echo -e "\n=== CAST VIRTUAL-ADDRESS: SETUP MASTER WALLET ===" +vaddr_master_json="$(cast wallet new --json)" +VADDR_MASTER_ADDR="$(jq -r '.[0].address' <<<"$vaddr_master_json")" +VADDR_MASTER_PK="$(jq -r '.[0].private_key' <<<"$vaddr_master_json")" +printf "Master address: %s\n" "$VADDR_MASTER_ADDR" +fund_and_wait "$VADDR_MASTER_ADDR" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: CREATE (mine + register) ===" +# Use the `vaddr` alias to also exercise it. +VADDR_CREATE_OUT=$(cast vaddr create \ +--owner "$VADDR_MASTER_ADDR" \ +--private-key "$VADDR_MASTER_PK" \ +--rpc-url "$ETH_RPC_URL") +echo "$VADDR_CREATE_OUT" +VADDR=$(echo "$VADDR_CREATE_OUT" | sed -n 's/^ tag=0x000000000000 \(0x[a-fA-F0-9]\{40\}\).*/\1/p' | head -1) +if [[ -z "$VADDR" ]]; then +echo "ERROR: failed to parse virtual address from create output" +exit 1 +fi +echo "Virtual address: $VADDR" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: RESOLVE ===" +VADDR_RESOLVE_OUT=$(cast virtual-address resolve "$VADDR" --rpc-url "$ETH_RPC_URL") +echo "$VADDR_RESOLVE_OUT" +RESOLVED_MASTER=$(echo "$VADDR_RESOLVE_OUT" | sed -n 's/^Master address: \(0x[a-fA-F0-9]\{40\}\).*/\1/p') +RESOLVED_LOWER=$(echo "$RESOLVED_MASTER" | tr '[:upper:]' '[:lower:]') +EXPECTED_LOWER=$(echo "$VADDR_MASTER_ADDR" | tr '[:upper:]' '[:lower:]') +if [[ "$RESOLVED_LOWER" != "$EXPECTED_LOWER" ]]; then +echo "ERROR: resolve returned master $RESOLVED_MASTER, expected $VADDR_MASTER_ADDR" +exit 1 +fi +echo "OK: resolve returned the registered master" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: AUTO-FORWARD TO MASTER ===" +# Create a separate sender, fund it, and transfer the fee token to the +# virtual address. The protocol must auto-forward to the master wallet. +vaddr_sender_json="$(cast wallet new --json)" +VADDR_SENDER_ADDR="$(jq -r '.[0].address' <<<"$vaddr_sender_json")" +VADDR_SENDER_PK="$(jq -r '.[0].private_key' <<<"$vaddr_sender_json")" +fund_and_wait "$VADDR_SENDER_ADDR" + +BAL_BEFORE=$(cast call --rpc-url "$ETH_RPC_URL" "$FEE_TOKEN" 'balanceOf(address)(uint256)' "$VADDR_MASTER_ADDR" | awk '{print $1}') +echo "Master balance before: $BAL_BEFORE" + +# Capture the current block before the transfer so `cast vaddr watch` can +# replay the Transfer log via --from-block. +BLOCK_BEFORE_TRANSFER=$(cast block-number --rpc-url "$ETH_RPC_URL") + +AMOUNT=1000000 +cast send "$FEE_TOKEN" 'transfer(address,uint256)' "$VADDR" "$AMOUNT" \ +--rpc-url "$ETH_RPC_URL" --private-key "$VADDR_SENDER_PK" + +BAL_AFTER=$(cast call --rpc-url "$ETH_RPC_URL" "$FEE_TOKEN" 'balanceOf(address)(uint256)' "$VADDR_MASTER_ADDR" | awk '{print $1}') +echo "Master balance after: $BAL_AFTER" + +EXPECTED=$((BAL_BEFORE + AMOUNT)) +if [[ "$BAL_AFTER" != "$EXPECTED" ]]; then +echo "ERROR: master balance grew by $((BAL_AFTER - BAL_BEFORE)), expected $AMOUNT" +exit 1 +fi +echo "OK: transfer to virtual address auto-forwarded to master" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: WATCH ===" +# Tail incoming TIP-20 transfers to the virtual address. `cast vaddr watch` +# polls indefinitely, so we cap it with `timeout`; the historical fetch via +# --from-block emits the prior Transfer log immediately at startup. +WATCH_OUT=$(timeout 5 cast vaddr watch "$VADDR" \ + --token "$FEE_TOKEN" \ + --from-block "$BLOCK_BEFORE_TRANSFER" \ + --rpc-url "$ETH_RPC_URL" 2>&1 || true) +echo "$WATCH_OUT" + +EXPECTED_PATTERN="token=$FEE_TOKEN from=$VADDR_SENDER_ADDR amount=$AMOUNT" +echo "Expected pattern: $EXPECTED_PATTERN" +if ! echo "$WATCH_OUT" | grep -iqF "$EXPECTED_PATTERN"; then + echo "ERROR: cast vaddr watch output did not contain expected '$EXPECTED_PATTERN'" + exit 1 +fi +echo "OK: cast vaddr watch reported correct token/from/amount" diff --git a/.github/workflows/benchmarks-nightly.yml b/.github/workflows/benchmarks-nightly.yml new file mode 100644 index 0000000000000..8569f52ce3b93 --- /dev/null +++ b/.github/workflows/benchmarks-nightly.yml @@ -0,0 +1,217 @@ +name: Nightly Benchmarks (AAVE v4) + +permissions: {} + +on: + schedule: + - cron: "0 2 * * *" # 2am UTC nightly + workflow_dispatch: # allow manual triggering for testing + +env: + AAVE_V4_REPO: "aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35" + RUSTC_WRAPPER: "sccache" + +jobs: + run-benchmarks: + name: Run Nightly Benchmarks + runs-on: depot-ubuntu-24.04-32 + permissions: + contents: read + actions: read # needed to download artifacts from previous runs + outputs: + has_regression: ${{ steps.compare.outputs.has_regression }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential pkg-config + + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 + + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + + - name: Setup Foundry + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + ./.github/scripts/setup-foundryup.sh + printf '%s\n' "$GITHUB_WORKSPACE/.foundry/bin" >> "$GITHUB_PATH" + + - name: Build benchmark binary + run: cargo build --locked --release --bin foundry-bench + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "24" + + - name: Install hyperfine + run: | + curl -L https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine-v1.19.0-x86_64-unknown-linux-gnu.tar.gz | tar xz + sudo mv hyperfine-v1.19.0-x86_64-unknown-linux-gnu/hyperfine /usr/local/bin/ + rm -rf hyperfine-v1.19.0-x86_64-unknown-linux-gnu + + - name: Download previous benchmark results + env: + GH_TOKEN: ${{ github.token }} + run: | + mkdir -p prev-results + PREV_RUN_ID=$(gh run list \ + --workflow=benchmarks-nightly.yml \ + --status=success \ + --limit=1 \ + --json databaseId \ + -q '.[0].databaseId // empty' 2>/dev/null || true) + if [[ -n "$PREV_RUN_ID" ]]; then + echo "Downloading results from previous run $PREV_RUN_ID" + gh run download "$PREV_RUN_ID" \ + --name nightly-bench-results \ + --dir prev-results/ || true + else + echo "No previous successful run found, skipping download." + fi + + - name: Run forge test benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches --force-install \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_test \ + --json-output "nightly-${DATE}-forge_test.json" \ + --verbose + + - name: Run forge fuzz test benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_fuzz_test \ + --json-output "nightly-${DATE}-forge_fuzz_test.json" \ + --verbose + + - name: Run forge isolate test benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_isolate_test \ + --json-output "nightly-${DATE}-forge_isolate_test.json" \ + --verbose + + - name: Run forge build benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_build_no_cache,forge_build_with_cache \ + --json-output "nightly-${DATE}-forge_build.json" \ + --verbose + + - name: Run forge coverage benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_coverage \ + --json-output "nightly-${DATE}-forge_coverage.json" \ + --verbose + + - name: Merge benchmark JSON results + run: | + DATE=$(date -u +%Y-%m-%d) + shopt -s nullglob + parts=( benches/nightly-${DATE}-*.json ) + if [[ ${#parts[@]} -eq 0 ]]; then + echo "No benchmark results produced — all steps failed." + exit 1 + fi + jq -s 'add' "${parts[@]}" > "benches/nightly-${DATE}.json" + echo "Merged ${#parts[@]} result file(s) into nightly-${DATE}.json" + + - name: Compare with previous results + id: compare + run: | + DATE=$(date -u +%Y-%m-%d) + PREV_JSON=$(ls prev-results/nightly-*.json 2>/dev/null | head -1 || true) + TODAY_JSON="benches/nightly-${DATE}.json" + if ./.github/scripts/compare-nightly.sh "$PREV_JSON" "$TODAY_JSON" > regression.md 2>&1; then + echo "has_regression=false" >> "$GITHUB_OUTPUT" + else + echo "has_regression=true" >> "$GITHUB_OUTPUT" + fi + cat regression.md + + - name: Upload benchmark results + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: nightly-bench-results + retention-days: 7 + path: | + benches/nightly-*.json + regression.md + + report-regression: + name: Report Regression + needs: run-benchmarks + if: needs.run-benchmarks.outputs.has_regression == 'true' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Download benchmark results + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: nightly-bench-results + path: results/ + + - name: Open regression issue + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + DATE=$(date -u +%Y-%m-%d) + BODY="$(cat results/regression.md) + + --- + + **Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + **Date**: ${DATE} + **Repo benchmarked**: \`aave/aave-v4\` (pinned commit) + **Threshold**: 🔴 >=3% regression, 🟡 >=1% warning" + + gh issue create \ + --title "[Nightly Regression] ${DATE}" \ + --body "$BODY" \ + --label "regression" \ + --repo "$GH_REPO" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 5d4767ad3b554..a136703abc294 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -10,17 +10,17 @@ on: required: false type: string versions: - description: "Comma-separated list of Foundry versions to benchmark (e.g., stable,nightly,v1.0.0)" + description: "Comma-separated list of Foundry versions to benchmark (optional, defaults to 'v1.5.1,v1.7.0')" required: false type: string - default: "stable,nightly" repos: - description: "Comma-separated repos to benchmark. Each entry: org/repo[:rev][ ] (e.g. vectorized/solady:v0.1.26 --nmc BrokenTest). Leave empty to use the per-benchmark default repo lists." + description: "Comma-separated repos to benchmark. Each entry: org/repo[:rev] (e.g. aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35). Leave empty to use the per-benchmark default repo lists." required: false type: string - default: "" env: + DEFAULT_VERSIONS: "v1.5.1,v1.7.0" + # Repos to benchmark per step. Each comma-separated entry has the form # org/repo[:rev][ ] # where anything after the first whitespace is appended to every benchmark @@ -29,27 +29,23 @@ env: TEST_REPOS: >- ithacaxyz/account:v0.5.7, vectorized/solady:v0.1.26 --nmc 'LifebuoyTest|LibBitTest|Base58Test', - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc 'TickMathTestTest', sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting ISOLATE_TEST_REPOS: >- ithacaxyz/account:v0.5.7 --nmc SimulateExecuteTest, - vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test', - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, + vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test|LibStringTest', uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc 'TickMathTestTest', sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting BUILD_REPOS: >- ithacaxyz/account:v0.5.7, vectorized/solady:v0.1.26, - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75, sparkdotfi/spark-psm:v1.0.0 COVERAGE_REPOS: >- ithacaxyz/account:v0.5.7, - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75, sparkdotfi/spark-psm:v1.0.0 @@ -60,7 +56,7 @@ jobs: name: Run All Benchmarks runs-on: depot-ubuntu-24.04-32 permissions: - contents: write + contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -93,7 +89,7 @@ jobs: run: cargo build --locked --release --bin foundry-bench - name: Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" @@ -106,59 +102,61 @@ jobs: - name: Run forge test benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.TEST_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_test,forge_fuzz_test \ - --output-file forge_test_bench.md + --output-file forge_test_bench.md \ + --verbose - name: Run forge isolate test benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.ISOLATE_TEST_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_isolate_test \ - --output-file forge_isolate_test_bench.md + --output-file forge_isolate_test_bench.md \ + --verbose - name: Run forge build benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.BUILD_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_build_no_cache,forge_build_with_cache \ - --output-file forge_build_bench.md + --output-file forge_build_bench.md \ + --verbose - name: Run forge coverage benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.COVERAGE_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_coverage \ - --output-file forge_coverage_bench.md + --output-file forge_coverage_bench.md \ + --verbose - name: Combine benchmark results run: ./.github/scripts/combine-benchmarks.sh benches - - name: Commit and read benchmark results + - name: Read benchmark results id: benchmark_results - env: - GITHUB_HEAD_REF: ${{ github.head_ref }} - run: ./.github/scripts/commit-and-read-benchmarks.sh benches "${{ github.event_name }}" "${{ github.repository }}" + run: ./.github/scripts/read-benchmark-results.sh benches - name: Upload benchmark results as artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -172,21 +170,21 @@ jobs: benches/LATEST.md outputs: - branch_name: ${{ steps.benchmark_results.outputs.branch_name }} pr_comment: ${{ steps.benchmark_results.outputs.pr_comment }} publish-results: name: Publish Results needs: run-benchmarks runs-on: ubuntu-latest + # All git writes happen here, on a clean ubuntu-latest runner that has + # never executed third-party benchmark code. permissions: contents: write pull-requests: write steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + # persist-credentials defaults to true so we can push. - name: Download benchmark results uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -194,19 +192,22 @@ jobs: name: benchmark-results path: benches/ - - name: Push branch for manual runs - if: github.event_name == 'workflow_dispatch' - run: | - git push origin "${{ needs.run-benchmarks.outputs.branch_name }}" - echo "Pushed branch: ${{ needs.run-benchmarks.outputs.branch_name }}" + - name: Commit benchmark results + id: commit_results + env: + GITHUB_HEAD_REF: ${{ github.head_ref }} + run: ./.github/scripts/commit-benchmark-results.sh benches "${{ github.event_name }}" "${{ github.repository }}" - name: Create PR for manual runs - if: github.event_name == 'workflow_dispatch' + if: github.event_name == 'workflow_dispatch' && steps.commit_results.outputs.committed == 'true' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + BRANCH_NAME: ${{ steps.commit_results.outputs.branch_name }} + PR_COMMENT: ${{ needs.run-benchmarks.outputs.pr_comment }} with: script: | - const branchName = '${{ needs.run-benchmarks.outputs.branch_name }}'; - const prComment = `${{ needs.run-benchmarks.outputs.pr_comment }}`; + const branchName = process.env.BRANCH_NAME; + const prComment = process.env.PR_COMMENT; // Create the pull request const { data: pr } = await github.rest.pulls.create({ @@ -231,10 +232,12 @@ jobs: - name: Comment on PR if: github.event.inputs.pr_number != '' || github.event_name == 'pull_request' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + PR_COMMENT: ${{ needs.run-benchmarks.outputs.pr_comment }} with: script: | const prNumber = ${{ github.event.inputs.pr_number || github.event.pull_request.number }}; - const prComment = `${{ needs.run-benchmarks.outputs.pr_comment }}`; + const prComment = process.env.PR_COMMENT; const comment = `${prComment} diff --git a/.github/workflows/ci-tempo.yml b/.github/workflows/ci-tempo.yml index ad4c424b7e8b2..1ef4c760f324e 100644 --- a/.github/workflows/ci-tempo.yml +++ b/.github/workflows/ci-tempo.yml @@ -69,14 +69,16 @@ jobs: run: | cargo test --locked -p foundry-common --lib tempo::tests::test_fork_schedule_parses_configured_rpcs -- --exact --nocapture - - name: Run Tempo check on devnet + - name: Run Tempo check on mainnet if: | - github.event_name == 'push' || - github.event_name == 'pull_request' || - github.event.inputs.network == 'devnet' || + github.event_name == 'schedule' || + github.event.inputs.network == 'mainnet' || github.event.inputs.network == 'all' env: - ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} + ETH_RPC_URL: ${{ secrets.TEMPO_MAINNET_RPC_URL }} + TEMPO_FEE_TOKEN: "0x20c0000000000000000000000000000000000000" + VERIFIER_URL: ${{ secrets.VERIFIER_URL }} + PRIVATE_KEY: ${{ secrets.THROW_AWAY_MAINNET_PKEY }} SCRIPTS: ${{ github.event.inputs.scripts || 'both' }} run: | if [ "$SCRIPTS" = "check" ] || [ "$SCRIPTS" = "both" ]; then @@ -86,16 +88,6 @@ jobs: ./.github/scripts/tempo-deploy.sh fi - - name: Run Tempo wallet tests on devnet - if: | - github.event_name == 'push' || - github.event_name == 'pull_request' || - github.event.inputs.network == 'devnet' || - github.event.inputs.network == 'all' - env: - ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} - run: ./.github/scripts/tempo-wallet.sh - - name: Run Tempo check on testnet if: | github.event_name == 'schedule' || @@ -113,16 +105,14 @@ jobs: ./.github/scripts/tempo-deploy.sh fi - - name: Run Tempo check on mainnet + - name: Run Tempo check on devnet if: | - github.event_name == 'schedule' || - github.event.inputs.network == 'mainnet' || + github.event_name == 'push' || + github.event_name == 'pull_request' || + github.event.inputs.network == 'devnet' || github.event.inputs.network == 'all' env: - ETH_RPC_URL: ${{ secrets.TEMPO_MAINNET_RPC_URL }} - TEMPO_FEE_TOKEN: "0x20c0000000000000000000000000000000000000" - VERIFIER_URL: ${{ secrets.VERIFIER_URL }} - PRIVATE_KEY: ${{ secrets.THROW_AWAY_MAINNET_PKEY }} + ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} SCRIPTS: ${{ github.event.inputs.scripts || 'both' }} run: | if [ "$SCRIPTS" = "check" ] || [ "$SCRIPTS" = "both" ]; then @@ -132,6 +122,16 @@ jobs: ./.github/scripts/tempo-deploy.sh fi + - name: Run Tempo wallet tests on devnet + if: | + github.event_name == 'push' || + github.event_name == 'pull_request' || + github.event.inputs.network == 'devnet' || + github.event.inputs.network == 'all' + env: + ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} + run: ./.github/scripts/tempo-wallet.sh + # If the nightly run fails, this will create an issue to signal so. issue: name: Open an issue diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb90a76cdbfd..a434028fdd18c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,23 @@ jobs: - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - run: cargo test --workspace --doc --locked + no-default-features: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + - run: cargo build --workspace --no-default-features --locked + typos: runs-on: depot-ubuntu-latest timeout-minutes: 30 diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index eb865bddc10e3..f0d460da6fbb1 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -29,7 +29,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b97a99d5310a4..6120735657ee6 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -32,6 +32,8 @@ jobs: name: build and push runs-on: depot-ubuntu-latest permissions: + attestations: write + artifact-metadata: write contents: read id-token: write packages: write @@ -92,6 +94,7 @@ jobs: uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1 - name: Build and push Foundry image + id: build uses: depot/build-push-action@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0 with: build-args: | @@ -106,3 +109,30 @@ jobs: platforms: linux/amd64,linux/arm64 push: true no-cache: true + sbom: true + provenance: mode=max + + - name: Install cosign + uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + + - name: Sign image with cosign (keyless) + env: + DOCKER_TAGS: ${{ steps.docker_tagging.outputs.docker_tags }} + DIGEST: ${{ steps.build.outputs.digest }} + shell: bash + run: | + set -euo pipefail + IFS=',' read -r -a tags <<< "$DOCKER_TAGS" + for tag in "${tags[@]}"; do + # Strip any tag suffix and pin to immutable digest, then sign. + ref="${tag%%:*}@${DIGEST}" + printf 'Signing %s\n' "$ref" + cosign sign --yes "$ref" + done + + - name: Image build provenance attestation + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.build.outputs.digest }} + push-to-registry: true diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 0a18c99a41f82..8528b71f299a9 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,7 +19,7 @@ jobs: contents: write pull-requests: write steps: - - uses: DeterminateSystems/determinate-nix-action@32cb6a5ae30bb0dfc996fa7baf8bf1ed28442fa4 # v3.17.3 + - uses: DeterminateSystems/determinate-nix-action@2be1df9ed6cfd12d52bfbba7af79472420fa5299 # v3.18.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -38,7 +38,7 @@ jobs: permissions: contents: read steps: - - uses: DeterminateSystems/determinate-nix-action@32cb6a5ae30bb0dfc996fa7baf8bf1ed28442fa4 # v3.17.3 + - uses: DeterminateSystems/determinate-nix-action@2be1df9ed6cfd12d52bfbba7af79472420fa5299 # v3.18.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index fdc5e5716c577..323059e99e6b6 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -143,7 +143,7 @@ jobs: bun-version: latest - name: Setup Node (for npm publish auth) - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" @@ -259,7 +259,7 @@ jobs: bun-version: latest - name: Setup Node (for npm publish auth) - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 682c9214284f6..38fa791fb655f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: - name: Build changelog id: build_changelog - uses: mikepenz/release-changelog-builder-action@bcae7115752d4ed746ff92feb666574428a79415 # v6.2 + uses: mikepenz/release-changelog-builder-action@bcae7115752d4ed746ff92feb666574428a79415 # v6.2.1 with: configuration: "./.github/changelog.json" fromTag: ${{ steps.release_info.outputs.from_tag || '' }} @@ -117,6 +117,8 @@ jobs: needs: prepare uses: ./.github/workflows/docker-publish.yml permissions: + attestations: write + artifact-metadata: write contents: read id-token: write packages: write @@ -129,9 +131,10 @@ jobs: # way, GitHub's immutable-releases setting seals the release at publish. release: permissions: - id-token: write - contents: write attestations: write + artifact-metadata: write + contents: write + id-token: write name: release ${{ matrix.target }} (${{ matrix.runner }}) runs-on: ${{ matrix.runner }} timeout-minutes: 240 @@ -264,6 +267,38 @@ jobs: printf "file_name=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> "$GITHUB_OUTPUT" fi printf "foundry_attestation=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.attestation.txt" >> "$GITHUB_OUTPUT" + printf "foundry_sbom=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.spdx.json" >> "$GITHUB_OUTPUT" + printf "foundry_checksum=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.sha256" >> "$GITHUB_OUTPUT" + printf "foundry_signature=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.sigstore.json" >> "$GITHUB_OUTPUT" + + - name: Generate archive checksum + env: + FILE_NAME: ${{ steps.artifacts.outputs.file_name }} + FOUNDRY_CHECKSUM: ${{ steps.artifacts.outputs.foundry_checksum }} + shell: bash + run: | + set -euo pipefail + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$FILE_NAME" > "$FOUNDRY_CHECKSUM" + else + shasum -a 256 "$FILE_NAME" > "$FOUNDRY_CHECKSUM" + fi + cat "$FOUNDRY_CHECKSUM" + + - name: Install Syft + uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + + - name: Generate SBOM (SPDX) + env: + FOUNDRY_SBOM: ${{ steps.artifacts.outputs.foundry_sbom }} + VERSION_NAME: ${{ (env.IS_NIGHTLY == 'true' && 'nightly') || needs.prepare.outputs.tag_name }} + shell: bash + run: | + set -euo pipefail + syft scan dir:. \ + --source-name foundry \ + --source-version "$VERSION_NAME" \ + -o spdx-json="$FOUNDRY_SBOM" - name: Upload build artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -292,15 +327,37 @@ jobs: tar -czvf "foundry_man_${VERSION_NAME}.tar.gz" forge.1.gz cast.1.gz anvil.1.gz chisel.1.gz printf 'foundry_man=%s\n' "foundry_man_${VERSION_NAME}.tar.gz" >> "$GITHUB_OUTPUT" - - name: Binaries attestation + - name: Binaries and archive provenance attestation id: attestation - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 with: subject-path: | ${{ env.anvil_bin_path }} ${{ env.cast_bin_path }} ${{ env.chisel_bin_path }} ${{ env.forge_bin_path }} + ${{ steps.artifacts.outputs.file_name }} + + - name: Archive SBOM attestation + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + with: + subject-path: ${{ steps.artifacts.outputs.file_name }} + sbom-path: ${{ steps.artifacts.outputs.foundry_sbom }} + + - name: Install cosign + uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + + - name: Sign archive with cosign (keyless) + env: + FILE_NAME: ${{ steps.artifacts.outputs.file_name }} + FOUNDRY_SIGNATURE: ${{ steps.artifacts.outputs.foundry_signature }} + shell: bash + run: | + set -euo pipefail + cosign sign-blob \ + --yes \ + --bundle "$FOUNDRY_SIGNATURE" \ + "$FILE_NAME" - name: Record attestation URL env: @@ -321,11 +378,20 @@ jobs: TAG_NAME: ${{ needs.prepare.outputs.tag_name }} FILE_NAME: ${{ steps.artifacts.outputs.file_name }} FOUNDRY_ATTESTATION: ${{ steps.artifacts.outputs.foundry_attestation }} + FOUNDRY_SBOM: ${{ steps.artifacts.outputs.foundry_sbom }} + FOUNDRY_CHECKSUM: ${{ steps.artifacts.outputs.foundry_checksum }} + FOUNDRY_SIGNATURE: ${{ steps.artifacts.outputs.foundry_signature }} FOUNDRY_MAN: ${{ steps.man.outputs.foundry_man }} shell: bash run: | set -euo pipefail - files=("$FILE_NAME" "$FOUNDRY_ATTESTATION") + files=( + "$FILE_NAME" + "$FOUNDRY_ATTESTATION" + "$FOUNDRY_SBOM" + "$FOUNDRY_CHECKSUM" + "$FOUNDRY_SIGNATURE" + ) if [[ -n "${FOUNDRY_MAN:-}" ]]; then files+=("$FOUNDRY_MAN") fi diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index d6244f826887e..9caa254f05c10 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -33,7 +33,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: nextest - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index 141e3a049a73b..6763f1f80bde3 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -37,7 +37,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: nextest - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eaa46c8e79f7c..daa4822b6e395 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,14 +73,14 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: nextest # External tests dependencies - name: Setup Node.js if: contains(matrix.name, 'external') - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 - name: Install Bun diff --git a/Cargo.lock b/Cargo.lock index 781b2c7f02b50..9db38f1967f4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,21 +77,21 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a547705d5c1b42575a0542bae2ba45bc62a6154be86611afaef1c0ab5c38598e" +checksum = "d8010fc7e9e8643ef4e758cdccf3eef26734594aedf88a9d5ed35e51837d42ef" dependencies = [ "alloy-consensus", "alloy-contract", "alloy-core", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-genesis", "alloy-network", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -116,14 +116,14 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8c24c95e90c1608c2d91cff1b451d796474168d3310ccc8b7cd12502ca8169" +checksum = "e3d64da86c616b5092ea64eea648f311bbd58630a0b384c42d699175d6f9122b" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-trie", "alloy-tx-macros", "auto_impl", @@ -143,23 +143,23 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d211ad0ef468a70a7a829e49683ff59ad25f02b4ab3764344c4c2663329a52c" +checksum = "8fd98696ca3617d3a9ba1a6f2011880cbfd5618228dab6400c9f8bca457859a8" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-contract" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59d55233ac14aa7fa6bcdcad45ba305e90c556065e0947cd9f243c4469e7c2d" +checksum = "de3df0aadc569a8b277808a7d0ad0e421180654ea36a3c59e9ed2bb968c9a1cd" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -238,12 +238,12 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250ba1168b8a049185a68c4dfa7f2a6a4046bd26fcc8c68632caeb216a5e12dc" +checksum = "1ceb16e7fe5a95825305f218ccd356665f848831f94ce2bbf55339bf5d21e88a" dependencies = [ "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", "serde_json", ] @@ -265,13 +265,14 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +checksum = "ec6ae911a2fc304a7cb80a79fb7bed6d1474aed4e7c203df1f8ff538f64fc78d" dependencies = [ "alloy-primitives", "alloy-rlp", "borsh", + "once_cell", "serde", ] @@ -300,9 +301,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae69eaa5096b47ffe97e6a5d6bde7e7fa2dec106af22a9315621d11039c3de3c" +checksum = "64c0456f5f7a4497e9342d20f528e30f5288ddfa0d6a012bd5044afee46cd8a0" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -310,7 +311,7 @@ dependencies = [ "alloy-eip7928", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "auto_impl", "borsh", "c-kzg", @@ -325,9 +326,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a8c1330ad33c95b5958573bca9a1ad0b419a51d76bb4c521556fbba8539b8d" +checksum = "d5638cbbffb318d440fdb009de019090d8d117dae40de9d10cdb29891ea59eb9" dependencies = [ "alloy-contract", "alloy-primitives", @@ -339,12 +340,12 @@ dependencies = [ [[package]] name = "alloy-evm" -version = "0.33.2" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc4b83cb672156663e6094d098beb509965b7fe684bb3d6e44bb9ca2e9ae714" +checksum = "c1ceeea6dcbbcd4e546b27700763a6f6c3b3fee30054209884f521078b6fda4f" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-hardforks", "alloy-primitives", "alloy-rpc-types-engine", @@ -359,13 +360,13 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39789db0b3f3bbef0e6549c87bc6842b73886ebabee1405b6941685b1cc34083" +checksum = "a71ff8b55d2b8aa05259f474cae7dea0e4991724dc18936b81cb23ec492a0c2a" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-trie", "borsh", "serde", @@ -400,9 +401,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662b525af73e86b2167dae923261c8edf440ba7e1426b30a8b993177bc214c02" +checksum = "19e352478b756bad5d7203148e4b461861282ea2ded3da406ba24868b52cd098" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -415,19 +416,19 @@ dependencies = [ [[package]] name = "alloy-network" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c657c2d9751d3c7d94990554b231e5372c3c2e4bad842806280b6151a0d6a05d" +checksum = "ed08ae169869e08370ed121612e0d3dadac33d1a256e9f2465926b23f0bd7d95" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-any", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-sol-types", "async-trait", @@ -441,24 +442,24 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e7c4bb0ebbd6d7406d2808968f43c0d5186c69c5e58cedcbee7380f4cd1fcf" +checksum = "02e6c7ad28afe348a9a9c5624b67ee5b3607b8de98d5816b3056ecdfa6fa2697" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-op-evm" version = "0.31.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -472,7 +473,7 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" version = "0.4.7" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -513,13 +514,13 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4fea0fc2628cdbc851aaa333124f9d8ab9f567ab8d4c20202819db13aa1a534" +checksum = "93a7c17472b55482d4734154c2f5ed13f72e03f6752cebb927f6a2d8b52e646c" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-json-rpc", "alloy-network", "alloy-network-primitives", @@ -547,7 +548,7 @@ dependencies = [ "lru", "parking_lot", "pin-project", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde", "serde_json", "thiserror 2.0.18", @@ -559,9 +560,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7b42e514613c717887dc77bb58d35e845557ebd63a18c3f92a77094e4891f" +checksum = "a8d86958b02bca85103d64fa60d7b364a8b017c6e40f2b02c3f50ca22964a738" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -603,9 +604,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ee7b51752c68fb95f21705e402700750e692b1d21ccc294ac48fadc8655d53" +checksum = "5beb5c2fe6b960c8e8b038e69fd502a90a2e930afa4770efb748b163b0767729" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -616,7 +617,7 @@ dependencies = [ "alloy-transport-ws", "futures", "pin-project", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde", "serde_json", "tokio", @@ -629,9 +630,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fa76988f54105ad4398828e8aaf1a39b3f07f91fb79091529056689514ee8c2" +checksum = "4ee1257a278f6d293e05c5162c5940a1561b1aa85ded0028b464c81de37ebfa5" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -640,44 +641,44 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-rpc-types-anvil" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d276bea4e92e4991269d31b9abd3e722eed2565b82036478a4416adb8dd4992" +checksum = "df32156f085e74eac942b6103744be49b817c302341aaa8cb0c1c88dc29228d9" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-rpc-types-any" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1a9a3bda9be7f6515316eb792710532411878bbfc88934973f4b371376b00d" +checksum = "6a234bfbdf7a76c3d13808f729af5321852de3dedcaa6fc6d5f54787aaf54c6a" dependencies = [ "alloy-consensus-any", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", "serde_json", ] [[package]] name = "alloy-rpc-types-beacon" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5d68ddca890854fb78291cbde06115473ded00b2337d0f815e92c0c1f8003" +checksum = "296450f5e76bece0116c939b9437b0421a5da9c5d40031bf4cf9b38d3d94e475" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rpc-types-engine", "derive_more", @@ -689,9 +690,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea21739e232c221779741eba7e7b9bc19ad8ff777b72736647ae519f5c9f6f33" +checksum = "0ab075ac1c25bcf697f133b7cd92e2fb26afe213e872ef79fdf77f0d7bcb3793" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -702,15 +703,15 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f05338cfb4ee5508ff76f01c88142cab8a4579db74b7d9432936c26e4f11374" +checksum = "73b12366c96f4013e1aeebc96c6b56e5f33f07853c42ea2f485045c0c157a4a1" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -722,17 +723,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda4ece0050154ab278241aeffade58916b04f38254832e8cb6e4671c6e72ed2" +checksum = "56a282daf869eeb7383d3d5c2deb35b0b3fb45ecb329513af4090fc61245ee18" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-sol-types", "itertools 0.14.0", "serde", @@ -743,13 +744,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5905ac3663b0859d67b82d912acce20887d20682a0cadde79c8a763b133a515" +checksum = "6184b5d14152b68b0bb8beb621339d94f0b761a37958bb365fbf7c00922125c2" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", "serde_json", "thiserror 2.0.18", @@ -757,13 +758,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fbf71892d4df9cae8d35dc96f15d522384bb93806205465e2c8c012b7f0a34" +checksum = "f00b631c361e7c7baaf4f1f5a9877730f3507fed2acb9d4b34841b8184b2ec28" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] @@ -780,9 +781,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beaa5c581a67e2743d95b4849eb9cfeb90866429cdaa6d8f6b75eb988b2d0cd9" +checksum = "a0eada2558e921b39dfcead33c487364df9b31374f5733c1c9d2c891c4529933" dependencies = [ "alloy-primitives", "serde", @@ -791,9 +792,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5da9ae50f9b48d7b4e2e5cde87175257be7e5e56909a7794720597c1d9806f6" +checksum = "41eb29f7a8adcd8941fbb8e134022a133e6f8dfd345f2e3b7109599f8a7dca08" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -808,9 +809,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a57d1e72b1f9b11e5e71ebdab0569cb02277a462bbea6793fcaebfcd794ae9" +checksum = "1258987fbc82716b5153ec7bb95a8a295e7640871b8f03d8ec7c4000dc80c215" dependencies = [ "alloy-consensus", "alloy-network", @@ -827,9 +828,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b27f20b5298b76a5a3b7cdbe6bdb184ab1ebd6e120e00dad748867673f5c90" +checksum = "7ffc2a49bca5b73c6964711b57452f6c36a6bcb7f845ab7e9ad05b5a828d0161" dependencies = [ "alloy-consensus", "alloy-network", @@ -845,9 +846,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c7acc40ffbfd37d4113eb619863099f3235d78d044006a1eecb94d8b0b2f1a" +checksum = "94e11ddaddfb98c1ddce737dc440225565b0ae0987ac9ad5e59a85db5904878c" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -865,9 +866,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b794002d57fd2f71b4c87298a41ca24dfc0f2cf6630d95106a477e451747ba" +checksum = "bef839e7ce9b59aa60fa9a175e97986c6145c888d643b0f1fb0a3e7b8e56a2e2" dependencies = [ "alloy-consensus", "alloy-network", @@ -885,9 +886,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a09a865ae9e1f05478429ef0d935b16467f35c6e0b02cb10f23f66a3b33fc3" +checksum = "44eb341d0013784da6a39e5bbdc11b95d6744993b12a1c3fd55df795a850dd42" dependencies = [ "alloy-consensus", "alloy-network", @@ -902,9 +903,9 @@ dependencies = [ [[package]] name = "alloy-signer-turnkey" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bb8218544ab635281f1be180a1cfd9b5d549db686faa7e85b3b2c10969819e" +checksum = "82ff16b4166fb90bbe79bd1e49244824fb3cadc6b8cd11e9c8a002c1f8c07492" dependencies = [ "alloy-consensus", "alloy-network", @@ -991,9 +992,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19dec9bfb59647254afdecbb5ddcddd7ba02edcd48ffa40510bddfbed0be1634" +checksum = "3ac7a80c0bac3e44559d53d002e34c461dc2f23262b42cafec019bc70551abbe" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -1014,14 +1015,14 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2035f3c4d6bee20624da2dcf765d469b292398e48d766ffade61b0fcf8b4d45d" +checksum = "eed3ed3300a998f88639ed619fdbbd88bd82865e00c6a8ecb796c99eb12358f6" dependencies = [ "alloy-json-rpc", "alloy-transport", "itertools 0.14.0", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde_json", "tower", "tracing", @@ -1030,9 +1031,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfad7aa9206fcb831ae401b6a1c893a402b8eed74f9c8ffbb7a7323afb0d9a4c" +checksum = "1075d9d30fd4d71e50000fd4afb19ed2664ceab20c2a29f3889a6e988329e02d" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -1050,9 +1051,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5aa8ff49386df3e008b73c7fb0a5479410e8493fdb86a8b916877a16e8aead9" +checksum = "0e3bff84b2b2a46eb34cc522dc3f889a2867c70be90a377421429b662b3ec4ce" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -1085,9 +1086,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3520337f3d3d063a7fe20f47aaa62d695e3dc0372b34f601560dee24e76988b9" +checksum = "99fce0350197dcd4ba4e9a7dd43915d908c0eb0e7352755791709a705e1c76b6" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -1223,13 +1224,13 @@ dependencies = [ [[package]] name = "anvil" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-genesis", "alloy-network", @@ -1241,7 +1242,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-beacon", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -1276,7 +1277,7 @@ dependencies = [ "parking_lot", "rand 0.8.6", "rand 0.9.4", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "revm-inspectors", "serde", @@ -1297,17 +1298,17 @@ dependencies = [ [[package]] name = "anvil-core" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eip5792", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "bytes", "foundry-common", "foundry-evm", @@ -1321,7 +1322,7 @@ dependencies = [ [[package]] name = "anvil-rpc" -version = "1.6.0" +version = "1.7.1" dependencies = [ "serde", "serde_json", @@ -1329,7 +1330,7 @@ dependencies = [ [[package]] name = "anvil-server" -version = "1.6.0" +version = "1.7.1" dependencies = [ "anvil-rpc", "async-trait", @@ -1669,20 +1670,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ascii-canvas" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" -dependencies = [ - "term", -] - [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -1955,9 +1947,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.101.0" +version = "1.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab41ad64e4051ecabeea802d6a17845a91e83287e1dd249e6963ea1ba78c428a" +checksum = "0fc35b7a14cabdad13795fbbbd26d5ddec0882c01492ceedf2af575aad5f37dd" dependencies = [ "aws-credential-types", "aws-runtime", @@ -2172,9 +2164,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.14" +version = "1.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8323699dd9b3c8d5b3c13051ae9cdef58fd179957c882f8374dd8725962d9" +checksum = "2f4bbcaa9304ea40902d3d5f42a0428d1bd895a2b0f6999436fb279ffddc58ac" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -2361,9 +2353,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.4" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", @@ -2579,7 +2571,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -2745,13 +2737,13 @@ dependencies = [ [[package]] name = "cast" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-ens", "alloy-evm", "alloy-hardforks", @@ -2763,7 +2755,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-beacon", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -2822,9 +2814,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.60" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -2832,12 +2824,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" @@ -2876,7 +2862,7 @@ dependencies = [ [[package]] name = "chisel" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -2890,10 +2876,9 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm", - "foundry-solang-parser", "foundry-test-utils", "itertools 0.14.0", - "reqwest 0.13.2", + "reqwest 0.13.3", "rexpect", "rustyline", "semver 1.0.28", @@ -2997,9 +2982,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.2" +version = "4.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb" +checksum = "660c0520455b1013b9bcb0393d5f643d7e4454fb69c915b8d6d2aa0e9a45acc3" dependencies = [ "clap", ] @@ -3042,7 +3027,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3195,7 +3180,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3364,9 +3349,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "compression-core", "flate2", @@ -3375,9 +3360,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "concurrent-queue" @@ -3425,9 +3410,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +checksum = "20d9a563d167a9cce0f94153382b33cb6eded6dfabff03c69ad65a28ea1514e0" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -3538,9 +3523,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc-fast" @@ -3821,9 +3806,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "der" @@ -3973,9 +3958,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", "crypto-common 0.2.1", @@ -3999,7 +3984,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4187,15 +4172,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ena" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" -dependencies = [ - "log", -] - [[package]] name = "encode_unicode" version = "1.0.0" @@ -4304,7 +4280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4502,15 +4478,15 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "figment2" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4380ce44915a6227efbb61e3885bc1c8e99fb9820f5db612abfac2c5cfc46871" +checksum = "87d63dee16df12076c7770919713c0b92f4e1c85eac828dc2ade0b6c998f016b" dependencies = [ "atomic", "parking_lot", "serde", "tempfile", - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.11+spec-1.1.0", "uncased", "version_check", ] @@ -4607,7 +4583,7 @@ checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc" [[package]] name = "forge" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4661,7 +4637,7 @@ dependencies = [ "rand 0.9.4", "rayon", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "semver 1.0.28", "serde", @@ -4689,7 +4665,7 @@ dependencies = [ [[package]] name = "forge-doc" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "derive_more", @@ -4711,7 +4687,7 @@ dependencies = [ [[package]] name = "forge-fmt" -version = "1.6.0" +version = "1.7.1" dependencies = [ "foundry-common", "foundry-config", @@ -4725,7 +4701,7 @@ dependencies = [ [[package]] name = "forge-lint" -version = "1.6.0" +version = "1.7.1" dependencies = [ "eyre", "foundry-common", @@ -4739,12 +4715,12 @@ dependencies = [ [[package]] name = "forge-script" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-json-abi", "alloy-network", @@ -4787,7 +4763,7 @@ dependencies = [ [[package]] name = "forge-script-sequence" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-network", "alloy-primitives", @@ -4803,7 +4779,7 @@ dependencies = [ [[package]] name = "forge-sol-macro-gen" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -4818,7 +4794,7 @@ dependencies = [ [[package]] name = "forge-verify" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -4841,7 +4817,7 @@ dependencies = [ "futures", "itertools 0.14.0", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "semver 1.0.28", "serde", @@ -4864,7 +4840,7 @@ dependencies = [ [[package]] name = "foundry-bench" -version = "1.6.0" +version = "1.7.1" dependencies = [ "chrono", "clap", @@ -4889,7 +4865,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.13.2", + "reqwest 0.13.3", "semver 1.0.28", "serde", "serde_json", @@ -4899,7 +4875,7 @@ dependencies = [ [[package]] name = "foundry-cheatcodes" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4952,7 +4928,7 @@ dependencies = [ [[package]] name = "foundry-cheatcodes-spec" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-sol-types", "foundry-macros", @@ -4963,11 +4939,11 @@ dependencies = [ [[package]] name = "foundry-cli" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-ens", "alloy-json-abi", "alloy-network", @@ -5007,6 +4983,7 @@ dependencies = [ "tempo-primitives", "tikv-jemallocator", "tokio", + "toml", "tracing", "tracing-subscriber 0.3.23", "tracing-tracy", @@ -5015,7 +4992,7 @@ dependencies = [ [[package]] name = "foundry-cli-markdown" -version = "1.6.0" +version = "1.7.1" dependencies = [ "clap", "pretty_assertions", @@ -5023,12 +5000,12 @@ dependencies = [ [[package]] name = "foundry-common" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -5040,6 +5017,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-engine", "alloy-signer", + "alloy-signer-local", "alloy-sol-types", "alloy-transport", "alloy-transport-ipc", @@ -5047,6 +5025,7 @@ dependencies = [ "anstream 0.6.21", "anstyle", "axum", + "base64 0.22.1", "chrono", "ciborium", "clap", @@ -5064,18 +5043,20 @@ dependencies = [ "futures", "itertools 0.14.0", "jiff", + "k256", "mpp", "num-format", "op-alloy-network", "op-alloy-rpc-types", "path-slash", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "rustls", "semver 1.0.28", "serde", "serde_json", + "sha2 0.10.9", "solar-compiler", "tempfile", "tempo-alloy", @@ -5094,14 +5075,14 @@ dependencies = [ [[package]] name = "foundry-common-fmt" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "chrono", "comfy-table", "eyre", @@ -5217,7 +5198,7 @@ dependencies = [ [[package]] name = "foundry-config" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-primitives", @@ -5257,7 +5238,7 @@ dependencies = [ [[package]] name = "foundry-debugger" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "crossterm", @@ -5275,7 +5256,7 @@ dependencies = [ [[package]] name = "foundry-evm" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -5312,7 +5293,7 @@ dependencies = [ [[package]] name = "foundry-evm-abi" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -5324,7 +5305,7 @@ dependencies = [ [[package]] name = "foundry-evm-core" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -5339,7 +5320,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-sol-types", "anvil", "auto_impl", @@ -5376,7 +5357,7 @@ dependencies = [ [[package]] name = "foundry-evm-coverage" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "eyre", @@ -5392,7 +5373,7 @@ dependencies = [ [[package]] name = "foundry-evm-fuzz" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -5417,7 +5398,7 @@ dependencies = [ [[package]] name = "foundry-evm-hardforks" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -5432,10 +5413,10 @@ dependencies = [ [[package]] name = "foundry-evm-networks" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -5448,11 +5429,11 @@ dependencies = [ [[package]] name = "foundry-evm-sancov" -version = "1.6.0" +version = "1.7.1" [[package]] name = "foundry-evm-traces" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -5470,7 +5451,7 @@ dependencies = [ "itertools 0.14.0", "memchr", "rayon", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "revm-inspectors", "serde", @@ -5509,7 +5490,7 @@ dependencies = [ [[package]] name = "foundry-linking" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "foundry-compilers", @@ -5520,7 +5501,7 @@ dependencies = [ [[package]] name = "foundry-macros" -version = "1.6.0" +version = "1.7.1" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -5530,7 +5511,7 @@ dependencies = [ [[package]] name = "foundry-primitives" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-evm", @@ -5541,7 +5522,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "derive_more", "op-alloy-consensus", @@ -5555,23 +5536,9 @@ dependencies = [ "tempo-revm", ] -[[package]] -name = "foundry-solang-parser" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9645e75b89f977423690f3b4bfd8d84825e5fdabd7803cbce6d4a2c4d54972b4" -dependencies = [ - "itertools 0.14.0", - "lalrpop", - "lalrpop-util", - "phf 0.11.3", - "thiserror 2.0.18", - "unicode-xid", -] - [[package]] name = "foundry-test-utils" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-primitives", @@ -5586,7 +5553,7 @@ dependencies = [ "parking_lot", "rand 0.9.4", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde_json", "snapbox", "svm-rs", @@ -5814,7 +5781,7 @@ dependencies = [ "once_cell", "prost 0.14.3", "prost-types 0.14.3", - "reqwest 0.13.2", + "reqwest 0.13.3", "secret-vault-value", "serde", "serde_json", @@ -6188,9 +6155,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5" dependencies = [ "typenum", ] @@ -6582,9 +6549,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" +checksum = "069323743400cb7ab06a8fe5c1ed911d36b6919ec531661d034c89083629595b" dependencies = [ "doctest-file", "futures-core", @@ -6592,7 +6559,7 @@ dependencies = [ "recvmsg", "tokio", "widestring", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6641,7 +6608,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6694,9 +6661,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "log", @@ -6707,31 +6674,15 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - [[package]] name = "jni" version = "0.22.4" @@ -6741,7 +6692,7 @@ dependencies = [ "cfg-if", "combine", "jni-macros", - "jni-sys 0.4.1", + "jni-sys", "log", "simd_cesu8", "thiserror 2.0.18", @@ -6762,15 +6713,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - [[package]] name = "jni-sys" version = "0.4.1" @@ -6802,9 +6744,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ "cfg-if", "futures-util", @@ -6841,6 +6783,7 @@ version = "10.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "getrandom 0.2.17", "js-sys", @@ -6923,45 +6866,14 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +checksum = "a7b65860415f949f23fa882e669f2dbd4a0f0eeb1acdd56790b30494afd7da2f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.1", "libc", ] -[[package]] -name = "lalrpop" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools 0.14.0", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax", - "sha3", - "string_cache 0.8.9", - "term", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" -dependencies = [ - "regex-automata", - "rustversion", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -6982,9 +6894,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -6994,12 +6906,11 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libmimalloc-sys" -version = "0.1.44" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" +checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6" dependencies = [ "cc", - "libc", ] [[package]] @@ -7299,12 +7210,12 @@ dependencies = [ [[package]] name = "metrics" -version = "0.24.3" +version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +checksum = "ff56c2e7dce6bd462e3b8919986a617027481b1dcc703175b58cf9dd98a2f071" dependencies = [ - "ahash", "portable-atomic", + "rapidhash", ] [[package]] @@ -7331,9 +7242,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.48" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" +checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640" dependencies = [ "libmimalloc-sys", ] @@ -7588,7 +7499,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7809,7 +7720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b685c8311c9171d1bd2895222965d25616b2de2cb5819dd3504ed9250df9fecd" dependencies = [ "ahash", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "parking_lot", "stable_deref_trait", ] @@ -7817,7 +7728,7 @@ dependencies = [ [[package]] name = "op-alloy" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "op-alloy-consensus", "op-alloy-network", @@ -7829,15 +7740,15 @@ dependencies = [ [[package]] name = "op-alloy-consensus" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "bytes", "derive_more", "reth-codecs", @@ -7856,7 +7767,7 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc" [[package]] name = "op-alloy-network" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", "alloy-network", @@ -7869,7 +7780,7 @@ dependencies = [ [[package]] name = "op-alloy-provider" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-network", "alloy-primitives", @@ -7883,15 +7794,15 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "derive_more", "op-alloy-consensus", "reth-rpc-traits", @@ -7903,15 +7814,14 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", - "alloy-serde 2.0.1", - "derive_more", + "alloy-serde 2.0.4", "ethereum_ssz", "ethereum_ssz_derive", "op-alloy-consensus", @@ -7924,7 +7834,7 @@ dependencies = [ [[package]] name = "op-revm" version = "19.0.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "auto_impl", "revm", @@ -8142,16 +8052,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset", - "indexmap 2.14.0", -] - [[package]] name = "pharos" version = "0.5.3" @@ -8168,7 +8068,6 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", "phf_shared 0.11.3", ] @@ -8178,7 +8077,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros 0.13.1", + "phf_macros", "phf_shared 0.13.1", "serde", ] @@ -8223,19 +8122,6 @@ dependencies = [ "phf_shared 0.13.1", ] -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "phf_macros" version = "0.13.1" @@ -8571,7 +8457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8740,7 +8626,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -9091,9 +8977,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64 0.22.1", "bytes", @@ -9135,12 +9021,12 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-genesis", "alloy-primitives", @@ -9155,11 +9041,12 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce542a96bf888f31854803e80b3340bc233927743aa580838014e8a88fe0d66" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -9173,8 +9060,9 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634c90f1cc0f9887680ca785b0b21aa961070b9465917bf65afaec56a6d005bb" dependencies = [ "proc-macro2", "quote", @@ -9183,8 +9071,8 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9196,11 +9084,11 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9209,8 +9097,8 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9233,10 +9121,10 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "bytes", "modular-bitfield", @@ -9247,11 +9135,11 @@ dependencies = [ [[package]] name = "reth-ethereum-consensus" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9263,8 +9151,8 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-eip2124", "alloy-hardforks", @@ -9276,11 +9164,11 @@ dependencies = [ [[package]] name = "reth-ethereum-primitives" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rpc-types-eth", "reth-codecs", @@ -9290,11 +9178,11 @@ dependencies = [ [[package]] name = "reth-evm" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-primitives", "auto_impl", @@ -9312,11 +9200,11 @@ dependencies = [ [[package]] name = "reth-evm-ethereum" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-primitives", "alloy-rpc-types-engine", @@ -9332,8 +9220,8 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-evm", "alloy-primitives", @@ -9345,11 +9233,11 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-primitives", "alloy-rlp", @@ -9364,8 +9252,8 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9377,11 +9265,12 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee12e304adbacbb32248c9806ebafbe1e2811fbfefe53c5e5b710a8438b7ec0" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9405,8 +9294,8 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "derive_more", @@ -9420,8 +9309,8 @@ dependencies = [ [[package]] name = "reth-revm" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9433,8 +9322,8 @@ dependencies = [ [[package]] name = "reth-rpc-convert" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-evm", @@ -9453,9 +9342,9 @@ dependencies = [ [[package]] name = "reth-rpc-traits" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b766da61ec7c46596386b4bc88d9b57d1939d3da2bc9e927567a8a23650e5ce9" +checksum = "860fe223501a76ff14aa3bf164f739f31008c2a2905ac85708bfd88f042e6151" dependencies = [ "alloy-consensus", "alloy-network", @@ -9468,8 +9357,8 @@ dependencies = [ [[package]] name = "reth-stages-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "bytes", @@ -9481,8 +9370,8 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "derive_more", @@ -9495,11 +9384,11 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -9518,10 +9407,10 @@ dependencies = [ [[package]] name = "reth-storage-errors" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", "derive_more", @@ -9536,14 +9425,14 @@ dependencies = [ [[package]] name = "reth-trie-common" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-trie", "arrayvec", "bytes", @@ -9559,8 +9448,9 @@ dependencies = [ [[package]] name = "reth-zstd-compressors" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c12fafa33d2f420a9d39249a3e0357b1928d09429f30758b85280409092873b2" dependencies = [ "zstd", ] @@ -9843,9 +9733,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba9ce64a8f45d7fc86358410bb1a82e8c987504c0d4900e9141d69a9f26c885" +checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9" dependencies = [ "bytemuck", "byteorder", @@ -9853,20 +9743,20 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.4.0" +version = "7.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +checksum = "5ac5b223d9738ef56e0b98305410be40fa0941bf6036c56f1506751e43552d64" dependencies = [ "libc", "rtoolbox", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rtoolbox" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327b72899159dfae8060c51a1f6aebe955245bcd9cc4997eed0f623caea022e4" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" dependencies = [ "libc", "windows-sys 0.59.0", @@ -9874,9 +9764,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" dependencies = [ "alloy-rlp", "arbitrary", @@ -10001,14 +9891,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.38" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", "log", @@ -10034,9 +9924,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -10044,13 +9934,13 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni 0.21.1", + "jni", "log", "once_cell", "rustls", @@ -10060,7 +9950,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10478,9 +10368,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19" dependencies = [ "base64 0.22.1", "chrono", @@ -10497,9 +10387,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "cf2ebbe86054f9b45bc3881e865683ccfaccce97b9b4cb53f3039d67f355a334" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -10560,14 +10450,14 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] name = "sha3" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", "keccak", @@ -10701,9 +10591,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -10842,7 +10732,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.12.1", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -10877,7 +10767,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.12.1", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -11011,18 +10901,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared 0.11.3", - "precomputed-hash", -] - [[package]] name = "string_cache" version = "0.9.0" @@ -11181,7 +11059,7 @@ checksum = "4572dd9845e37ca0293acb5fe591a7f61b51f1b7b62d3dc6fb8e99e2664f3755" dependencies = [ "const-hex", "dirs", - "reqwest 0.13.2", + "reqwest 0.13.3", "semver 1.0.28", "serde", "serde_json", @@ -11301,22 +11179,22 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tempo-alloy" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-contract", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer-local", "alloy-sol-types", "alloy-transport", @@ -11334,9 +11212,9 @@ dependencies = [ [[package]] name = "tempo-chainspec" version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-genesis", "alloy-hardforks", @@ -11353,7 +11231,7 @@ dependencies = [ [[package]] name = "tempo-consensus" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11371,7 +11249,7 @@ dependencies = [ [[package]] name = "tempo-contracts" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-contract", "alloy-primitives", @@ -11382,7 +11260,7 @@ dependencies = [ [[package]] name = "tempo-evm" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11409,7 +11287,7 @@ dependencies = [ [[package]] name = "tempo-precompiles" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy", "alloy-evm", @@ -11429,7 +11307,7 @@ dependencies = [ [[package]] name = "tempo-precompiles-macros" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy", "proc-macro2", @@ -11440,15 +11318,15 @@ dependencies = [ [[package]] name = "tempo-primitives" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "aws-lc-rs", "base64 0.22.1", "derive_more", @@ -11471,7 +11349,7 @@ dependencies = [ [[package]] name = "tempo-revm" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11502,15 +11380,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "term" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "terminal_size" version = "0.4.4" @@ -11518,7 +11387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11552,9 +11421,9 @@ dependencies = [ [[package]] name = "thin-vec" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259cdf8ed4e4aca6f1e9d011e10bd53f524a2d0637d7b28450f6c64ac298c4c6" +checksum = "b0f7e269b48f0a7dd0146680fa24b50cc67fc0373f086a5b2f99bd084639b482" [[package]] name = "thiserror" @@ -11695,9 +11564,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ "bytes", "libc", @@ -11864,9 +11733,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap 2.14.0", + "serde_core", + "serde_spanned", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -11875,7 +11746,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -12220,9 +12091,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -12506,9 +12377,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "10.0.0-beta.6" +version = "10.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "174a690eb3293a5666442b0738d080df9ea6b9e03782bbe78875c89ff914a77c" +checksum = "2d7cb4a83971db3f6ae36f0aa41eaf5985d2e2b469581fa755c132f9c2a1ec89" dependencies = [ "anyhow", "bon", @@ -12519,9 +12390,9 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "10.0.0-beta.6" +version = "10.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f628f4acc90a5c1a8136495eaf5f9ef94e03c174d6fb2e6de691bc58fc721ee" +checksum = "bba14c9676943b2899cea2ed7ea194b89b3d13564a3c93a61882a978b123a41c" dependencies = [ "anyhow", "bon", @@ -12533,9 +12404,9 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "10.0.0-beta.6" +version = "10.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390d0442b660baedd7a6f60d2af01bd8967e0d7fe69cd15e13c82c811d82b709" +checksum = "fb684e6d170ef15a9b3c20561779a50ba8c806f8acdaff47c0a2c5c4c6cadd43" dependencies = [ "anyhow", "bon", @@ -12600,11 +12471,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -12613,14 +12484,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -12631,9 +12502,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ "js-sys", "wasm-bindgen", @@ -12641,9 +12512,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12651,9 +12522,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -12664,9 +12535,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -12764,7 +12635,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -12804,9 +12675,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -12824,13 +12695,13 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", - "string_cache 0.9.0", + "string_cache", "string_cache_codegen", ] @@ -12841,7 +12712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" dependencies = [ "core-foundation 0.10.1", - "jni 0.22.4", + "jni", "log", "ndk-context", "objc2", @@ -12914,7 +12785,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -13035,15 +12906,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -13080,21 +12942,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -13137,12 +12984,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -13155,12 +12996,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -13173,12 +13008,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -13203,12 +13032,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -13221,12 +13044,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -13239,12 +13056,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -13257,12 +13068,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -13286,9 +13091,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] @@ -13302,6 +13107,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index c412dad16366c..4db027dd400cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ members = [ resolver = "2" [workspace.package] -version = "1.6.0" +version = "1.7.1" edition = "2024" rust-version = "1.89" authors = ["Foundry Contributors"] @@ -174,9 +174,6 @@ foundry-compilers.opt-level = 3 serde_json.opt-level = 3 serde.opt-level = 3 -foundry-solang-parser.opt-level = 3 -lalrpop-util.opt-level = 3 - solar-compiler.opt-level = 3 solar-ast.opt-level = 3 solar-data-structures.opt-level = 3 @@ -307,40 +304,40 @@ crossterm.opt-level = "s" alloy-json-abi.opt-level = "s" [workspace.dependencies] -anvil = { path = "crates/anvil" } -cast = { path = "crates/cast" } -chisel = { path = "crates/chisel" } -forge = { path = "crates/forge" } - -forge-doc = { path = "crates/doc" } -forge-fmt = { path = "crates/fmt" } -forge-lint = { path = "crates/lint" } -forge-verify = { path = "crates/verify" } -forge-script = { path = "crates/script" } -forge-sol-macro-gen = { path = "crates/sol-macro-gen" } -forge-script-sequence = { path = "crates/script-sequence" } -foundry-cheatcodes = { path = "crates/cheatcodes" } +anvil = { path = "crates/anvil", default-features = false } +cast = { path = "crates/cast", default-features = false } +chisel = { path = "crates/chisel", default-features = false } +forge = { path = "crates/forge", default-features = false } + +forge-doc = { path = "crates/doc", default-features = false } +forge-fmt = { path = "crates/fmt", default-features = false } +forge-lint = { path = "crates/lint", default-features = false } +forge-verify = { path = "crates/verify", default-features = false } +forge-script = { path = "crates/script", default-features = false } +forge-sol-macro-gen = { path = "crates/sol-macro-gen", default-features = false } +forge-script-sequence = { path = "crates/script-sequence", default-features = false } +foundry-cheatcodes = { path = "crates/cheatcodes", default-features = false } foundry-cheatcodes-spec = { path = "crates/cheatcodes/spec" } -foundry-cli = { path = "crates/cli" } +foundry-cli = { path = "crates/cli", default-features = false } foundry-cli-markdown = { path = "crates/cli-markdown" } -foundry-common = { path = "crates/common" } -foundry-common-fmt = { path = "crates/common/fmt" } +foundry-common = { path = "crates/common", default-features = false } +foundry-common-fmt = { path = "crates/common/fmt", default-features = false } foundry-config = { path = "crates/config" } -foundry-debugger = { path = "crates/debugger" } -foundry-evm = { path = "crates/evm/evm" } +foundry-debugger = { path = "crates/debugger", default-features = false } +foundry-evm = { path = "crates/evm/evm", default-features = false } foundry-evm-abi = { path = "crates/evm/abi" } -foundry-evm-core = { path = "crates/evm/core" } -foundry-evm-coverage = { path = "crates/evm/coverage" } -foundry-evm-hardforks = { path = "crates/evm/hardforks" } -foundry-evm-networks = { path = "crates/evm/networks" } -foundry-evm-fuzz = { path = "crates/evm/fuzz" } +foundry-evm-core = { path = "crates/evm/core", default-features = false } +foundry-evm-coverage = { path = "crates/evm/coverage", default-features = false } +foundry-evm-hardforks = { path = "crates/evm/hardforks", default-features = false } +foundry-evm-networks = { path = "crates/evm/networks", default-features = false } +foundry-evm-fuzz = { path = "crates/evm/fuzz", default-features = false } foundry-evm-sancov = { path = "crates/evm/sancov" } -foundry-evm-traces = { path = "crates/evm/traces" } +foundry-evm-traces = { path = "crates/evm/traces", default-features = false } foundry-macros = { path = "crates/macros" } -foundry-test-utils = { path = "crates/test-utils" } +foundry-test-utils = { path = "crates/test-utils", default-features = false } foundry-wallets = { version = "0.1.0", default-features = false } foundry-linking = { path = "crates/linking" } -foundry-primitives = { path = "crates/primitives" } +foundry-primitives = { path = "crates/primitives", default-features = false } # solc & compilation utilities foundry-block-explorers = { version = "0.23.0", default-features = false } @@ -349,7 +346,6 @@ foundry-compilers = { version = "0.20.0", default-features = false, features = [ "svm-solc", ] } foundry-fork-db = { version = "0.26.0", features = ["zstd"] } -solang-parser = { version = "=0.3.9", package = "foundry-solang-parser" } solar = { package = "solar-compiler", version = "=0.1.8", default-features = false } svm = { package = "svm-rs", version = "0.5", default-features = false, features = [ "rustls", @@ -410,7 +406,7 @@ op-alloy-rpc-types = "0.24.0" op-alloy-flz = "0.13.1" ## alloy-evm -alloy-evm = "0.33.2" +alloy-evm = "0.34.0" alloy-op-evm = "0.31.0" # revm @@ -515,17 +511,17 @@ mpp = { git = "https://github.com/tempoxyz/mpp-rs", rev = "554d20112eb014bd223d5 "reqwest-rustls-tls", "ws", ] } -tempo-chainspec = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a", default-features = false } -tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a", default-features = false, features = [ +tempo-chainspec = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa", default-features = false } +tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa", default-features = false, features = [ "serde", ] } -tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a", default-features = false } -tempo-evm = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a", default-features = false } -tempo-revm = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a", default-features = false, features = [ +tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa", default-features = false } +tempo-evm = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa", default-features = false } +tempo-revm = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa", default-features = false, features = [ "serde", ] } -tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a" } -tempo-precompiles = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a" } +tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa" } +tempo-precompiles = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa" } ## Pinned dependencies. Enabled for the workspace in crates/test-utils. @@ -589,27 +585,21 @@ rexpect = { git = "https://github.com/rust-cli/rexpect", rev = "2ed0b1898d7edaf6 ## alloy-evm # alloy-evm = { git = "https://github.com/paradigmxyz/evm.git", rev = "04d8e4a" } -## reth-core -reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth-core", rev = "6b12498" } -reth-codecs = { git = "https://github.com/paradigmxyz/reth-core", rev = "6b12498" } -reth-codecs-derive = { git = "https://github.com/paradigmxyz/reth-core", rev = "6b12498" } -reth-zstd-compressors = { git = "https://github.com/paradigmxyz/reth-core", rev = "6b12498" } - ## op-revm / op-alloy / alloy-op-evm -op-revm = { git = "https://github.com/ethereum-optimism/optimism", rev = "42f5117c2e7de0614cd3b96f274d0a3078f9701c" } -op-alloy-consensus = { git = "https://github.com/ethereum-optimism/optimism", rev = "42f5117c2e7de0614cd3b96f274d0a3078f9701c" } -op-alloy-network = { git = "https://github.com/ethereum-optimism/optimism", rev = "42f5117c2e7de0614cd3b96f274d0a3078f9701c" } -op-alloy-rpc-types = { git = "https://github.com/ethereum-optimism/optimism", rev = "42f5117c2e7de0614cd3b96f274d0a3078f9701c" } -alloy-op-evm = { git = "https://github.com/ethereum-optimism/optimism", rev = "42f5117c2e7de0614cd3b96f274d0a3078f9701c" } -alloy-op-hardforks = { git = "https://github.com/ethereum-optimism/optimism", rev = "42f5117c2e7de0614cd3b96f274d0a3078f9701c" } +op-revm = { git = "https://github.com/ethereum-optimism/optimism", rev = "e3b59e76588f99db17205f5601e45a5b00f0bfbb" } +op-alloy-consensus = { git = "https://github.com/ethereum-optimism/optimism", rev = "e3b59e76588f99db17205f5601e45a5b00f0bfbb" } +op-alloy-network = { git = "https://github.com/ethereum-optimism/optimism", rev = "e3b59e76588f99db17205f5601e45a5b00f0bfbb" } +op-alloy-rpc-types = { git = "https://github.com/ethereum-optimism/optimism", rev = "e3b59e76588f99db17205f5601e45a5b00f0bfbb" } +alloy-op-evm = { git = "https://github.com/ethereum-optimism/optimism", rev = "e3b59e76588f99db17205f5601e45a5b00f0bfbb" } +alloy-op-hardforks = { git = "https://github.com/ethereum-optimism/optimism", rev = "e3b59e76588f99db17205f5601e45a5b00f0bfbb" } ## foundry-fork-db # foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-core", rev = "2f90eb86d4549fa15a8cc2d99bfc1039bc083977" } ## tempo — unify crates.io versions (pulled by mpp) with git rev -tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a" } -tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a" } -tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "8bd4d01d37e3cc324030baacbce2da0862d7735a" } +tempo-primitives = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa" } +tempo-alloy = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa" } +tempo-contracts = { git = "https://github.com/tempoxyz/tempo", rev = "6bf9903d6a75cc264029dcf54183adea38d3cfaa" } # solar solar = { package = "solar-compiler", git = "https://github.com/paradigmxyz/solar", rev = "530f129" } diff --git a/README.md b/README.md index c9f0a45c57b0a..90cd1d8a7865e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ foundryup See the [installation guide](https://getfoundry.sh/getting-started/installation) for more details. +To verify a downloaded release archive or container image, see [Verifying Releases](./SECURITY.md#verifying-releases). + ## Getting Started Initialize a new project, build and test: diff --git a/SECURITY.md b/SECURITY.md index d84327cc18e91..6296066db5e73 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,3 +3,112 @@ ## Reporting a Vulnerability Contact [security@tempo.xyz](mailto:security@tempo.xyz). + +## Verifying Releases + +Every official Foundry release ships with multiple, independent integrity +artifacts. All signing is keyless via [Sigstore](https://www.sigstore.dev/) — +no Foundry-managed key material is involved, and every signature is recorded +in the public [Rekor](https://docs.sigstore.dev/logging/overview/) transparency +log. The signing identity is the GitHub Actions OIDC token of this repository's +`release.yml` / `docker-publish.yml` workflows. + +### Per-release artifacts + +For each `foundry___.{tar.gz,zip}` archive on the +[releases page](https://github.com/foundry-rs/foundry/releases), the same +release also publishes: + +| Suffix | Purpose | +| --- | --- | +| `.sha256` | SHA-256 checksum of the archive (`sha256sum` format) | +| `.sigstore.json` | Cosign keyless signature bundle (cert + signature + Rekor proof) over the archive | +| `.spdx.json` | SPDX 2.3 SBOM of the source workspace used for the build | +| `.attestation.txt` | URL of the GitHub artifact-attestation summary | + +In addition, GitHub stores SLSA build-provenance and SBOM attestations against +the archive's digest; these are queryable via `gh attestation` without +downloading anything else. + +### Verifying an archive + +Pick whichever toolchain you have available — they verify the same signatures. + +#### Option 1: GitHub CLI (simplest) + +```bash +gh attestation verify foundry_v1.4.0_linux_amd64.tar.gz \ + --repo foundry-rs/foundry +``` + +This computes the file's digest, fetches the matching attestation from GitHub, +and verifies the Sigstore signature plus the SLSA provenance predicate. Add +`--signer-workflow foundry-rs/foundry/.github/workflows/release.yml` to also +require the workflow identity. + +To verify the SBOM attestation specifically: + +```bash +gh attestation verify foundry_v1.4.0_linux_amd64.tar.gz \ + --repo foundry-rs/foundry \ + --predicate-type 'https://spdx.dev/Document/v2.3' +``` + +#### Option 2: Cosign (offline-friendly) + +Download the archive and its `.sigstore.json` bundle from the release page, +then: + +```bash +cosign verify-blob \ + --bundle foundry_v1.4.0_linux_amd64.sigstore.json \ + --certificate-identity-regexp '^https://github.com/foundry-rs/foundry/\.github/workflows/release\.yml@.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + foundry_v1.4.0_linux_amd64.tar.gz +``` + +For nightly builds the certificate identity points at `refs/heads/master` +instead of a tag; the regex above matches both. + +#### Option 3: Plain checksum (integrity only) + +```bash +sha256sum -c foundry_v1.4.0_linux_amd64.sha256 # GNU coreutils +shasum -a 256 -c foundry_v1.4.0_linux_amd64.sha256 # macOS +``` + +This proves the bytes match what was uploaded, but says nothing about who +uploaded them. Combine with one of the verifications above for end-to-end +trust. + +### Verifying the Docker image + +Container signatures and attestations are pushed as OCI referrers to GHCR, so +no separate files need to be downloaded. + +```bash +# Cosign keyless signature on the image +cosign verify ghcr.io/foundry-rs/foundry:v1.4.0 \ + --certificate-identity-regexp '^https://github.com/foundry-rs/foundry/\.github/workflows/(release|docker-publish)\.yml@.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' + +# SLSA build-provenance attestation +gh attestation verify oci://ghcr.io/foundry-rs/foundry:v1.4.0 \ + --repo foundry-rs/foundry + +# Inspect the buildx-attached SBOM and provenance +docker buildx imagetools inspect ghcr.io/foundry-rs/foundry:v1.4.0 \ + --format '{{ json .SBOM }}' +docker buildx imagetools inspect ghcr.io/foundry-rs/foundry:v1.4.0 \ + --format '{{ json .Provenance }}' +``` + +To pin to an immutable digest (recommended for reproducible deployments): + +```bash +docker pull ghcr.io/foundry-rs/foundry:v1.4.0 +DIGEST=$(docker buildx imagetools inspect ghcr.io/foundry-rs/foundry:v1.4.0 --format '{{ .Manifest.Digest }}') +cosign verify "ghcr.io/foundry-rs/foundry@${DIGEST}" \ + --certificate-identity-regexp '^https://github.com/foundry-rs/foundry/\.github/workflows/(release|docker-publish)\.yml@.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' +``` diff --git a/benches/LATEST.md b/benches/LATEST.md index 238a691229389..cb75f8d68780b 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,74 +1,108 @@ -# Foundry Benchmark Results +# 📊 Foundry Benchmark Results -**Date**: 2026-04-24 23:10:24 +**Generated at**: 2026-05-02 21:53:46 UTC -## Repositories Tested +## Forge Test + +### Repositories Tested 1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -2. [Vectorized/solady](https://github.com/Vectorized/solady) -3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) +2. [vectorized/solady](https://github.com/vectorized/solady) +3. [uniswap/v4-core](https://github.com/uniswap/v4-core) 4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) -5. [aave/aave-v4](https://github.com/aave/aave-v4) - -## Foundry Versions +### Foundry Versions - **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) -- **nightly**: forge Version: 1.6.0-nightly (a249f5c 2026-04-24) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) -## Forge Test - -| Repository | v1.5.1 | nightly | -| -------------------- | -------- | -------- | -| vectorized-solady | 1.46 s | 1.38 s | -| aave-aave-v4 | 4m 14.2s | 3m 29.1s | +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 2.78 s | 0.965 s | +| vectorized-solady | 0.995 s | 0.645 s | +| uniswap-v4-core | 5.97 s | 1.51 s | +| sparkdotfi-spark-psm | 19.98 s | 10.20 s | ## Forge Fuzz Test -| Repository | v1.5.1 | nightly | -| -------------------- | --------- | -------- | -| ithacaxyz-account | 2.81 s | 1.59 s | -| vectorized-solady | 1.40 s | 1.34 s | -| Uniswap-v4-core | 3.01 s | 2.87 s | -| sparkdotfi-spark-psm | 2.04 s | 1.87 s | -| aave-aave-v4 | 3m 46.0s | 3m 17.3s | +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 2.54 s | 0.923 s | +| vectorized-solady | 0.929 s | 0.617 s | +| uniswap-v4-core | 6.44 s | 1.40 s | +| sparkdotfi-spark-psm | 2.25 s | 2.03 s | ## Forge Test (Isolated) -| Repository | v1.5.1 | nightly | -| -------------------- | -------- | -------- | -| Uniswap-v4-core | 3.50 s | 3.48 s. | -| aave-aave-v4 | 4m 14.0s | 3m 53.4s | +### Repositories Tested + +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [vectorized/solady](https://github.com/vectorized/solady) +3. [uniswap/v4-core](https://github.com/uniswap/v4-core) +4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +### Foundry Versions + +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) + +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 3.05 s | 1.02 s | +| vectorized-solady | 0.871 s | 0.741 s | +| uniswap-v4-core | 6.81 s | 1.68 s | +| sparkdotfi-spark-psm | 21.96 s | 11.26 s | + +## Forge Build -## Forge Build (No Cache) +### Repositories Tested -| Repository | v1.5.1 | nightly | -| -------------------- | -------- | -------- | -| ithacaxyz-account | 26.06 s | 26.61 s | -| vectorized-solady | 14.20 s | 14.26 s | -| Uniswap-v4-core | 2m 1.3s | 2m 5.0s | -| sparkdotfi-spark-psm | 15.16 s | 15.30 s | -| aave-aave-v4 | 3m 37.0s | 3m 35.1s | +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [vectorized/solady](https://github.com/vectorized/solady) +3. [uniswap/v4-core](https://github.com/uniswap/v4-core) +4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +### Foundry Versions + +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) + +### No Cache + +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 34.58 s | 33.29 s | +| vectorized-solady | 14.40 s | 14.41 s | +| uniswap-v4-core | 2m 17.6s | 2m 17.7s | +| sparkdotfi-spark-psm | 12.62 s | 12.61 s | -## Forge Build (With Cache) +### With Cache -| Repository | v1.5.1 | nightly | -| -------------------- | ------- | ------- | -| ithacaxyz-account | 0.167 s | 0.201 s | -| vectorized-solady | 0.099 s | 0.098 s | -| Uniswap-v4-core | 0.139 s | 0.140 s | -| sparkdotfi-spark-psm | 0.168 s | 0.173 s | -| aave-aave-v4 | 0.370 s | 0.357 s | +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 0.083 s | 0.089 s | +| vectorized-solady | 0.062 s | 0.064 s | +| uniswap-v4-core | 0.071 s | 0.074 s | +| sparkdotfi-spark-psm | 0.066 s | 0.068 s | ## Forge Coverage -| Repository | v1.5.1 | nightly | -| -------------------- | --------- | ---------- | -| Uniswap-v4-core | 1m 13.9s | 1m 10.3s | -| sparkdotfi-spark-psm | 2m 54.7s | 2m 50.0s | -| aave-aave-v4 | 11m 20.8s | 10m 58.7s | +### Repositories Tested + +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [uniswap/v4-core](https://github.com/uniswap/v4-core) +3. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +### Foundry Versions + +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) + +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 29.35 s | 18.69 s | +| uniswap-v4-core | 1m 26.8s | 1m 4.1s | +| sparkdotfi-spark-psm | 2m 1.6s | 1m 28.4s | ## System Information -- **OS**: macos -- **CPU**: 12 -- **Rustc**: rustc 1.95.0 (59807616e 2026-04-14) \ No newline at end of file + +- **OS**: linux +- **CPU**: 32 +- **Rustc**: rustc 1.95.0 (59807616e 2026-04-14) diff --git a/benches/src/main.rs b/benches/src/main.rs index 60e815cecb0ec..8d7134b1c25bc 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -39,9 +39,15 @@ struct Cli { #[clap(long, default_value = ".")] output_dir: PathBuf, - /// Name of the output file (default: LATEST.md) - #[clap(long, default_value = "LATEST.md")] - output_file: String, + /// Name of the output file. Defaults to LATEST.md unless --json-output is set + /// without this flag, in which case no Markdown is written. + #[clap(long)] + output_file: Option, + + /// Filename for a flat JSON summary (benchmark/repo -> mean_seconds). + /// Resolved relative to --output-dir. Used by the nightly regression comparison script. + #[clap(long)] + json_output: Option, /// Run only specific benchmarks (comma-separated: /// forge_test,forge_build_no_cache,forge_build_with_cache,forge_fuzz_test,forge_coverage) @@ -216,12 +222,28 @@ fn main() -> Result<()> { } } - // Generate markdown report - sh_println!("📝 Generating report..."); - let markdown = results.generate_markdown(&versions, &repos); - let output_path = cli.output_dir.join(cli.output_file); - fs::write(&output_path, markdown).wrap_err("Failed to write output file")?; - sh_println!("✅ Report written to: {}", output_path.display()); + // Write Markdown report unless --json-output is set without an explicit --output-file. + let md_filename = match cli.output_file { + Some(f) => Some(f), + None if cli.json_output.is_none() => Some("LATEST.md".to_string()), + None => None, + }; + if let Some(filename) = md_filename { + sh_println!("📝 Generating report..."); + let markdown = results.generate_markdown(&versions, &repos); + let output_path = cli.output_dir.join(filename); + fs::write(&output_path, markdown).wrap_err("Failed to write output file")?; + sh_println!("✅ Report written to: {}", output_path.display()); + } + + if let Some(json_filename) = cli.json_output { + let summary = results.generate_json_summary(&versions); + let json = + serde_json::to_string_pretty(&summary).wrap_err("Failed to serialize JSON summary")?; + let json_path = cli.output_dir.join(json_filename); + fs::write(&json_path, json).wrap_err("Failed to write JSON summary")?; + sh_println!("✅ JSON summary written to: {}", json_path.display()); + } Ok(()) } diff --git a/benches/src/results.rs b/benches/src/results.rs index 447e8ed2766b4..e7d57250fc9a1 100644 --- a/benches/src/results.rs +++ b/benches/src/results.rs @@ -66,6 +66,25 @@ impl BenchmarkResults { self.version_details.insert(version.to_string(), details); } + /// Generate a flat JSON summary mapping `"benchmark/repo" -> mean_seconds`. + /// + /// Used by the nightly regression comparison script. + pub fn generate_json_summary(&self, versions: &[String]) -> HashMap { + let mut summary = HashMap::new(); + for (benchmark_name, version_data) in &self.data { + for version in versions { + if let Some(repo_data) = version_data.get(version) { + for (repo_name, result) in repo_data { + let key = format!("{benchmark_name}/{repo_name}"); + let rounded = (result.mean * 10_000.0).round() / 10_000.0; + summary.insert(key, rounded); + } + } + } + } + summary + } + pub fn generate_markdown(&self, versions: &[String], repos: &[RepoConfig]) -> String { let mut output = String::new(); diff --git a/benchmark.sh b/benchmark.sh index 2faffa93dfa1d..ac6159099069b 100755 --- a/benchmark.sh +++ b/benchmark.sh @@ -1,36 +1,52 @@ #!/bin/bash -versions="v1.3.6,v1.4.0-rc1" +versions="v1.5.1,v1.7.0" # Repositories -export ITHACA_ACCOUNT="ithacaxyz/account:v0.3.2" -export SOLADY_REPO="Vectorized/solady:v0.1.22" -export UNISWAP_V4_CORE="Uniswap/v4-core:59d3ecf" -export SPARK_PSM="sparkdotfi/spark-psm:v1.0.0" +ITHACA_ACCOUNT="ithacaxyz/account:v0.5.7" +SOLADY_REPO="vectorized/solady:v0.1.26 --nmc 'LifebuoyTest|LibBitTest|Base58Test'" +AAVE_V4="aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35" +UNISWAP_V4_CORE="uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc TickMathTestTest" +SPARK_PSM="sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting" -# Benches -echo "===========FORGE TEST AND BUILD BENCHMARKS===========" +SOLADY_ISOLATE="vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test|LibStringTest'" +ITHACA_ISOLATE="ithacaxyz/account:v0.5.7 --nmc SimulateExecuteTest" -foundry-bench --versions $versions \ - --repos $ITHACA_ACCOUNT,$SOLADY_REPO,$UNISWAP_V4_CORE,$SPARK_PSM \ - --benchmarks forge_test,forge_fuzz_test,forge_build_no_cache,forge_build_with_cache \ - --output-dir ./benches/results \ - --output-file TEST_BUILD.md +SOLADY_BUILD="vectorized/solady:v0.1.26" +UNISWAP_BUILD="uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75" +SPARK_PSM_BUILD="sparkdotfi/spark-psm:v1.0.0" -echo "===========FORGE COVERAGE BENCHMARKS===========" +# Benches +echo "===========FORGE TEST BENCHMARKS===========" -foundry-bench --versions $versions \ - --repos $ITHACA_ACCOUNT,$UNISWAP_V4_CORE,$SPARK_PSM \ - --benchmarks forge_coverage \ - --output-dir ./benches/results \ - --output-file COVERAGE.md +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ACCOUNT,$SOLADY_REPO,$AAVE_V4,$UNISWAP_V4_CORE,$SPARK_PSM" \ + --benchmarks forge_test,forge_fuzz_test \ + --output-dir ./benches \ + --output-file forge_test_bench.md echo "===========FORGE ISOLATE TEST BENCHMARKS===========" -foundry-bench --versions $versions \ - --repos $SOLADY_REPO,$UNISWAP_V4_CORE,$SPARK_PSM \ +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ISOLATE,$SOLADY_ISOLATE,$AAVE_V4,$UNISWAP_V4_CORE,$SPARK_PSM" \ --benchmarks forge_isolate_test \ - --output-dir ./benches/results \ - --output-file ISOLATE_TEST.md + --output-dir ./benches \ + --output-file forge_isolate_test_bench.md + +echo "===========FORGE BUILD BENCHMARKS===========" + +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ACCOUNT,$SOLADY_BUILD,$AAVE_V4,$UNISWAP_BUILD,$SPARK_PSM_BUILD" \ + --benchmarks forge_build_no_cache,forge_build_with_cache \ + --output-dir ./benches \ + --output-file forge_build_bench.md + +echo "===========FORGE COVERAGE BENCHMARKS===========" + +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ACCOUNT,$AAVE_V4,$UNISWAP_BUILD,$SPARK_PSM_BUILD" \ + --benchmarks forge_coverage \ + --output-dir ./benches \ + --output-file forge_coverage_bench.md echo "===========BENCHMARKS COMPLETED===========" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index b664266450d07..d6404b21e2e8e 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -20,15 +20,15 @@ required-features = ["cli"] [dependencies] # foundry internal -anvil-core = { path = "core" } +anvil-core = { path = "core", default-features = false } anvil-rpc = { path = "rpc" } anvil-server = { path = "server" } -foundry-cli.workspace = true +foundry-cli = { workspace = true, optional = true } foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-evm-networks.workspace = true -foundry-primitives.workspace = true +foundry-primitives = { workspace = true, default-features = false } tempo-chainspec.workspace = true tempo-primitives.workspace = true tempo-precompiles.workspace = true @@ -37,7 +37,7 @@ tempo-revm.workspace = true # alloy alloy-evm = { workspace = true, features = ["call-util"] } -alloy-op-evm.workspace = true +alloy-op-evm = { workspace = true, optional = true } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-contract = { workspace = true, features = ["pubsub"] } @@ -63,7 +63,8 @@ alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true alloy-trie.workspace = true -op-alloy-consensus = { workspace = true, features = ["serde"] } +op-alloy-consensus = { workspace = true, features = ["serde"], optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } # revm revm = { workspace = true, features = [ @@ -73,7 +74,7 @@ revm = { workspace = true, features = [ "c-kzg", ] } revm-inspectors.workspace = true -op-revm.workspace = true +op-revm = { workspace = true, optional = true } # axum related axum.workspace = true @@ -120,17 +121,28 @@ reqwest.workspace = true foundry-test-utils.workspace = true tokio = { workspace = true, features = ["full"] } -op-alloy-rpc-types.workspace = true tempo-alloy.workspace = true [features] -default = ["cli", "jemalloc", "asm-keccak"] +default = ["cli", "jemalloc", "asm-keccak", "optimism"] +optimism = [ + "dep:op-alloy-consensus", + "dep:op-alloy-rpc-types", + "dep:alloy-op-evm", + "dep:op-revm", + "anvil-core/optimism", + "foundry-primitives/optimism", + "foundry-common/optimism", + "foundry-evm/optimism", + "foundry-cli?/optimism", +] asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] -jemalloc = ["foundry-cli/jemalloc"] -mimalloc = ["foundry-cli/mimalloc"] -tracy-allocator = ["foundry-cli/tracy-allocator"] +jemalloc = ["foundry-cli?/jemalloc"] +mimalloc = ["foundry-cli?/mimalloc"] +tracy-allocator = ["foundry-cli?/tracy-allocator"] cli = ["tokio/full", "cmd"] cmd = [ + "dep:foundry-cli", "clap", "clap_complete", "dep:fdlimit", diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index cf4b952ecfaa3..8456413a78b1f 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -15,7 +15,7 @@ workspace = true [dependencies] foundry-common.workspace = true foundry-evm.workspace = true -foundry-primitives.workspace = true +foundry-primitives = { workspace = true, default-features = false } revm = { workspace = true, default-features = false, features = [ "std", "serde", @@ -39,3 +39,11 @@ bytes.workspace = true # misc rand.workspace = true thiserror.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-primitives/optimism", + "foundry-common/optimism", + "foundry-evm/optimism", +] diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index fb75529b82908..4fd2aabb82a8b 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -12,7 +12,9 @@ use clap::Parser; use core::fmt; use foundry_common::shell; use foundry_config::{Chain, Config, FigmentProviders}; -use foundry_evm::hardfork::{EthereumHardfork, OpHardfork}; +#[cfg(feature = "optimism")] +use foundry_evm::hardfork::OpHardfork; +use foundry_evm::hardfork::{EthereumHardfork, FoundryHardfork}; use foundry_evm_networks::NetworkConfigs; use foundry_primitives::FoundryReceiptEnvelope; use futures::FutureExt; @@ -240,15 +242,7 @@ impl NodeArgs { } let hardfork = match &self.hardfork { - Some(hf) => { - if self.evm.networks.is_optimism() { - Some(OpHardfork::from_str(hf)?.into()) - } else if self.evm.networks.is_tempo() { - Some(TempoHardfork::from_str(hf)?.into()) - } else { - Some(EthereumHardfork::from_str(hf)?.into()) - } - } + Some(hf) => Some(parse_hardfork(hf, &self.evm.networks)?), None => None, }; @@ -849,6 +843,19 @@ impl FromStr for ForkUrl { } } +/// Parses a hardfork string against the active network configuration. +fn parse_hardfork(hf: &str, networks: &NetworkConfigs) -> eyre::Result { + #[cfg(feature = "optimism")] + if networks.is_optimism() { + return Ok(OpHardfork::from_str(hf)?.into()); + } + if networks.is_tempo() { + Ok(TempoHardfork::from_str(hf)?.into()) + } else { + Ok(EthereumHardfork::from_str(hf)?.into()) + } +} + /// Clap's value parser for genesis. Loads a genesis.json file. fn read_genesis_file(path: &str) -> Result { foundry_common::fs::read_json_file(path.as_ref()).map_err(|err| err.to_string()) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 23cd6e61bc076..7c91590c071fa 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -37,7 +37,7 @@ use foundry_config::Config; use foundry_evm::{ backend::{BlockchainDb, BlockchainDbMeta, SharedBackend}, constants::DEFAULT_CREATE2_DEPLOYER, - hardfork::{FoundryHardfork, OpHardfork}, + hardfork::FoundryHardfork, utils::{ apply_chain_and_block_specific_env_changes, block_env_from_header, get_blob_base_fee_update_fraction, @@ -577,8 +577,9 @@ impl NodeConfig { if let Some(hardfork) = self.hardfork { return hardfork; } + #[cfg(feature = "optimism")] if self.networks.is_optimism() { - return OpHardfork::default().into(); + return foundry_evm::hardforks::OpHardfork::default().into(); } if self.networks.is_tempo() { return TempoHardfork::default().into(); @@ -1079,6 +1080,7 @@ impl NodeConfig { } /// Enable Optimism network features. + #[cfg(feature = "optimism")] #[must_use] pub fn with_optimism(mut self) -> Self { self.networks = NetworkConfigs::with_optimism(); diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 78cce938318c1..4900c18eebd82 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1882,6 +1882,7 @@ impl EthApi { fn sign_request(&self, from: &Address, typed_tx: FoundryTypedTx) -> Result { match typed_tx { + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(_) => return Ok(build_impersonated(typed_tx)), _ => { for signer in self.signers.iter() { @@ -2210,9 +2211,13 @@ impl EthApi { // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce, from)]; - debug_assert!(requires != provides); + let (requires, provides) = if let Some((requires, provides)) = + tempo_parallel_nonce_markers(&pending_transaction) + { + (requires, provides) + } else { + (required_marker(nonce, on_chain_nonce, from), vec![to_marker(nonce, from)]) + }; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -2288,11 +2293,10 @@ impl EthApi { let priority = self.transaction_priority(&pending_transaction.transaction); // Tempo txs use a 2D nonce system — no sequential ordering by account nonce. - let (requires, provides) = if let FoundryTxEnvelope::Tempo(aa_tx) = - pending_transaction.transaction.as_ref() - && !aa_tx.tx().nonce_key.is_zero() + let (requires, provides) = if let Some((requires, provides)) = + tempo_parallel_nonce_markers(&pending_transaction) { - (vec![], vec![pending_transaction.hash().to_vec()]) + (requires, provides) } else { let on_chain_nonce = self.backend.current_nonce(from).await?; let nonce = pending_transaction.transaction.nonce(); @@ -3192,8 +3196,13 @@ impl EthApi { // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce, from)]; + let (requires, provides) = if let Some((requires, provides)) = + tempo_parallel_nonce_markers(&pending_transaction) + { + (requires, provides) + } else { + (required_marker(nonce, on_chain_nonce, from), vec![to_marker(nonce, from)]) + }; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -3549,6 +3558,7 @@ impl EthApi { requires: Vec, provides: Vec, ) -> Result { + debug_assert!(requires != provides); let from = *pending_transaction.sender(); let priority = self.transaction_priority(&pending_transaction.transaction); let pool_transaction = @@ -3565,7 +3575,9 @@ impl EthApi { FoundryTxEnvelope::Eip1559(_) => self.backend.ensure_eip1559_active(), FoundryTxEnvelope::Eip4844(_) => self.backend.ensure_eip4844_active(), FoundryTxEnvelope::Eip7702(_) => self.backend.ensure_eip7702_active(), + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(_) => self.backend.ensure_op_deposits_active(), + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(_) => Err(BlockchainError::InvalidTransactionRequest( "not implemented for post-exec tx".to_string(), )), @@ -3634,6 +3646,20 @@ fn required_marker(provided_nonce: u64, on_chain_nonce: u64, from: Address) -> V if on_chain_nonce <= prev_nonce { vec![to_marker(prev_nonce, from)] } else { Vec::new() } } +fn tempo_parallel_nonce_markers( + pending_transaction: &PendingTransaction, +) -> Option<(Vec, Vec)> { + // Tempo txs with non-zero nonce_key use a 2D nonce system and should not + // be sequenced by account nonce markers. + if let FoundryTxEnvelope::Tempo(aa_tx) = pending_transaction.transaction.as_ref() + && !aa_tx.tx().nonce_key.is_zero() + { + Some((vec![], vec![pending_transaction.hash().to_vec()])) + } else { + None + } +} + fn convert_transact_out(out: &Option) -> Bytes { match out { None => Default::default(), diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 614f409c3eb65..c41937055fdd4 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -67,9 +67,11 @@ impl ReceiptBuilder for FoundryReceiptBuilder { FoundryTxType::Eip1559 => FoundryReceiptEnvelope::Eip1559(receipt), FoundryTxType::Eip4844 => FoundryReceiptEnvelope::Eip4844(receipt), FoundryTxType::Eip7702 => FoundryReceiptEnvelope::Eip7702(receipt), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => { unreachable!("deposit receipts are built in commit_transaction") } + #[cfg(feature = "optimism")] FoundryTxType::PostExec => FoundryReceiptEnvelope::PostExec(receipt), FoundryTxType::Tempo => FoundryReceiptEnvelope::Tempo(receipt), } @@ -85,7 +87,7 @@ pub struct AnvilTxResult { pub sender: Address, } -impl TxResult for AnvilTxResult { +impl TxResult for AnvilTxResult { type HaltReason = H; fn result(&self) -> &ResultAndState { @@ -217,12 +219,10 @@ where }) } - fn commit_transaction( - &mut self, - output: Self::Result, - ) -> Result { + fn commit_transaction(&mut self, output: Self::Result) -> GasOutput { let AnvilTxResult { inner: EthTxResult { result: ResultAndState { result, state }, blob_gas_used, tx_type }, + #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] sender, } = output; @@ -237,6 +237,7 @@ where self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas_used); } + #[cfg(feature = "optimism")] let receipt = if tx_type == FoundryTxType::Deposit { let deposit_nonce = state.get(&sender).map(|acc| acc.info.nonce); let receipt = alloy_consensus::Receipt { @@ -262,11 +263,19 @@ where cumulative_gas_used: self.gas_used, }) }; + #[cfg(not(feature = "optimism"))] + let receipt = self.receipt_builder.build_receipt(ReceiptBuilderCtx { + tx_type, + evm: &self.evm, + result, + state: &state, + cumulative_gas_used: self.gas_used, + }); self.receipts.push(receipt); self.evm.db_mut().commit(state); - Ok(GasOutput::new(gas_used)) + GasOutput::new(gas_used) } fn finish( @@ -429,7 +438,7 @@ where let exec_result = result.result().result.clone(); let gas_used = result.result().result.tx_gas_used(); - executor.commit_transaction(result).expect("commit failed"); + executor.commit_transaction(result); let traces = executor.evm_mut().inspector_mut().finish_transaction(inspector_config); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index f404031445611..1c8dfffab9d95 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -57,6 +57,7 @@ use alloy_network::{ AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, Network, NetworkTransactionBuilder, ReceiptResponse, UnknownTxEnvelope, UnknownTypedTransaction, }; +#[cfg(feature = "optimism")] use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx}; use alloy_primitives::{ Address, B256, Bloom, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom, @@ -108,20 +109,60 @@ use foundry_evm::{ }, }; use foundry_evm_networks::NetworkConfigs; +#[cfg(feature = "optimism")] +use foundry_primitives::get_deposit_tx_parts; use foundry_primitives::{ FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope, - FoundryTxReceipt, get_deposit_tx_parts, + FoundryTxReceipt, }; use futures::channel::mpsc::{UnboundedSender, unbounded}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait}; -use op_revm::{OpHaltReason, OpSpecId, OpTransaction}; +#[cfg(feature = "optimism")] +use op_revm::{OpSpecId, OpTransaction, transaction::deposit::DepositTransactionParts}; + +/// Side-channel container for OP-specific deposit info produced by +/// [`Backend::build_call_env`] and consumed by the OP transact path. +/// +/// When the `optimism` feature is enabled, this is an alias for +/// `op_revm::DepositTransactionParts`. When disabled, it is a zero-sized +/// stand-in so the eth/tempo dispatch chain still type-checks. +#[cfg(feature = "optimism")] +type OpCallDepositInfo = DepositTransactionParts; +#[cfg(not(feature = "optimism"))] +#[derive(Default, Clone, Debug)] +struct OpCallDepositInfo; + +/// Marker trait that abstracts over the per-network inspector trait bounds +/// required by the in-memory backend. The OP bound is only included when the +/// `optimism` feature is enabled. +#[cfg(feature = "optimism")] +pub trait BackendInspector: + Inspector> + Inspector> + Inspector> +{ +} +#[cfg(feature = "optimism")] +impl BackendInspector for T where + T: Inspector> + Inspector> + Inspector> +{ +} +#[cfg(not(feature = "optimism"))] +pub trait BackendInspector: + Inspector> + Inspector> +{ +} +#[cfg(not(feature = "optimism"))] +impl BackendInspector for T where + T: Inspector> + Inspector> +{ +} use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use revm::{ DatabaseCommit, Inspector, context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv}, context_interface::{ block::BlobExcessGasAndPrice, - result::{EVMError, ExecutionResult, HaltReason, Output, ResultAndState}, + result::{ExecutionResult, HaltReason, Output, ResultAndState}, }, database::{CacheDB, DbAccount, WrapDatabaseRef}, interpreter::InstructionResult, @@ -157,6 +198,8 @@ pub mod cache; pub mod fork_db; pub mod in_memory_db; pub mod inspector; +#[cfg(feature = "optimism")] +pub mod optimism; pub mod state; pub mod storage; @@ -419,6 +462,11 @@ impl Backend { self.genesis.timestamp } + /// Returns the configured genesis block number. + pub const fn genesis_number(&self) -> u64 { + self.genesis.number + } + /// Returns balance of the given account. pub async fn current_balance(&self, address: Address) -> DatabaseResult { Ok(self.get_account(address).await?.balance) @@ -490,12 +538,21 @@ impl Backend { } /// Returns true if op-stack deposits are active - pub fn is_optimism(&self) -> bool { + #[cfg(feature = "optimism")] + pub const fn is_optimism(&self) -> bool { self.networks.is_optimism() } + /// Returns true if op-stack deposits are active. + /// + /// Always `false` when built without the `optimism` feature. + #[cfg(not(feature = "optimism"))] + pub const fn is_optimism(&self) -> bool { + false + } + /// Returns true if Tempo network mode is active - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { self.networks.is_tempo() } @@ -589,7 +646,8 @@ impl Backend { } /// Returns an error if op-stack deposits are not active - pub fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { + #[cfg(feature = "optimism")] + pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { if self.is_optimism() { return Ok(()); } @@ -597,7 +655,7 @@ impl Backend { } /// Returns an error if Tempo transactions are not active - pub fn ensure_tempo_active(&self) -> Result<(), BlockchainError> { + pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> { if self.is_tempo() { return Ok(()); } @@ -1139,58 +1197,53 @@ impl Backend { db: &'db DB, evm_env: &EvmEnv, inspector: &mut I, - tx_env: OpTransaction, + tx_env: TxEnv, + op_deposit: OpCallDepositInfo, ) -> Result, BlockchainError> where DB: DatabaseRef + ?Sized, - I: Inspector>> - + Inspector>> - + Inspector>>, + I: BackendInspector>, WrapDatabaseRef<&'db DB>: Database, { + #[cfg(feature = "optimism")] if self.is_optimism() { - let op_env = EvmEnv::new( - evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(OpSpecId::ISTHMUS), - evm_env.block_env.clone(), - ); - let mut evm = OpEvmFactory::default().create_evm_with_inspector( - WrapDatabaseRef(db), - op_env, - inspector, - ); - self.inject_precompiles(evm.precompiles_mut()); - let result = evm.transact(OpTx(tx_env)).map_err(|e| match e { - EVMError::Database(db) => EVMError::Database(db), - EVMError::Header(h) => EVMError::Header(h), - EVMError::Custom(s) => EVMError::Custom(s), - EVMError::CustomAny(err) => EVMError::CustomAny(err), - EVMError::Transaction(t) => EVMError::Transaction(t), - })?; - Ok(ResultAndState { - result: result.result.map_haltreason(|h| match h { - OpHaltReason::Base(eth) => eth, - _ => HaltReason::PrecompileError, - }), - state: result.state, - }) - } else if self.is_tempo() { - self.transact_tempo_with_inspector_ref( - db, - evm_env, - inspector, - TempoTxEnv::from(tx_env.base), - ) + let op_tx = OpTransaction { base: tx_env, deposit: op_deposit, ..Default::default() }; + return self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx); + } + // `op_deposit` only matters on the OP path; eth/tempo ignore it. + let _ = op_deposit; + if self.is_tempo() { + self.transact_tempo_with_inspector_ref(db, evm_env, inspector, TempoTxEnv::from(tx_env)) } else { - let mut evm = EthEvmFactory::default().create_evm_with_inspector( - WrapDatabaseRef(db), - evm_env.clone(), - inspector, - ); - self.inject_precompiles(evm.precompiles_mut()); - Ok(evm.transact(tx_env.base)?) + self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env) } } + /// Eth path of [`Backend::transact_with_inspector_ref`]. + /// + /// Creates an Ethereum EVM, injects precompiles, and transacts with a + /// plain [`TxEnv`]. + fn transact_eth_with_inspector_ref<'db, I, DB>( + &self, + db: &'db DB, + evm_env: &EvmEnv, + inspector: &mut I, + tx_env: TxEnv, + ) -> Result, BlockchainError> + where + DB: DatabaseRef + ?Sized, + I: Inspector>>, + WrapDatabaseRef<&'db DB>: Database, + { + let mut evm = EthEvmFactory::default().create_evm_with_inspector( + WrapDatabaseRef(db), + evm_env.clone(), + inspector, + ); + self.inject_precompiles(evm.precompiles_mut()); + Ok(evm.transact(tx_env)?) + } + /// Builds the appropriate tx env from a [`FoundryTxEnvelope`], executes via the correct /// EVM backend (Op/Tempo/Eth), and returns both the result and the base [`TxEnv`]. fn transact_envelope_with_inspector_ref<'db, I, DB>( @@ -1203,9 +1256,7 @@ impl Backend { ) -> Result<(ResultAndState, TxEnv), BlockchainError> where DB: DatabaseRef + ?Sized, - I: Inspector>> - + Inspector>> - + Inspector>>, + I: BackendInspector>, WrapDatabaseRef<&'db DB>: Database, { if tx.is_tempo() { @@ -1213,14 +1264,21 @@ impl Backend { FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into()); let base = tx_env.inner.clone(); let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?; - Ok((result, base)) - } else { - let tx_env: OpTransaction = + return Ok((result, base)); + } + #[cfg(feature = "optimism")] + if self.is_optimism() { + let op_tx: OpTransaction = FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into()); - let base = tx_env.base.clone(); - let result = self.transact_with_inspector_ref(db, evm_env, inspector, tx_env)?; - Ok((result, base)) + let base = op_tx.base.clone(); + let result = self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx)?; + return Ok((result, base)); } + let tx_env: TxEnv = + FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into()); + let base = tx_env.clone(); + let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?; + Ok((result, base)) } /// Creates a Tempo EVM, injects precompiles, and transacts with a native [`TempoTxEnv`]. @@ -1298,6 +1356,7 @@ impl Backend { }}; } + #[cfg(feature = "optimism")] if self.is_optimism() { let op_env = EvmEnv::new( evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(OpSpecId::ISTHMUS), @@ -1305,8 +1364,10 @@ impl Backend { ); let mut evm = OpEvmFactory::::default().create_evm_with_inspector(db, op_env, inspector); - run!(evm) - } else if self.is_tempo() { + return run!(evm); + } + + if self.is_tempo() { let hardfork = TempoHardfork::from(self.hardfork); let tempo_env = EvmEnv::new( evm_env @@ -1338,7 +1399,7 @@ impl Backend { request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> (EvmEnv, OpTransaction) { + ) -> (EvmEnv, TxEnv, OpCallDepositInfo) { let tx_type = request.minimal_tx_type() as u8; let WithOtherFields:: { @@ -1391,7 +1452,7 @@ impl Backend { let caller = from.unwrap_or_default(); let to = to.as_ref().and_then(TxKind::to); let blob_hashes = blob_versioned_hashes.unwrap_or_default(); - let mut base = TxEnv { + let mut tx_env = TxEnv { caller, gas_limit, gas_price, @@ -1413,11 +1474,10 @@ impl Backend { blob_hashes, ..Default::default() }; - base.set_signed_authorization(authorization_list.unwrap_or_default()); - let mut tx_env = OpTransaction { base, ..Default::default() }; + tx_env.set_signed_authorization(authorization_list.unwrap_or_default()); if let Some(nonce) = nonce { - tx_env.base.nonce = nonce; + tx_env.nonce = nonce; } if evm_env.block_env.basefee == 0 { @@ -1427,13 +1487,22 @@ impl Backend { } // Deposit transaction? (only valid when op-stack deposits are active) - if self.ensure_op_deposits_active().is_ok() + #[cfg(feature = "optimism")] + let op_deposit = if self.ensure_op_deposits_active().is_ok() && let Ok(deposit) = get_deposit_tx_parts(&other) { - tx_env.deposit = deposit; - } + deposit + } else { + OpCallDepositInfo::default() + }; + #[cfg(not(feature = "optimism"))] + let op_deposit = { + // `other` carries OP-only deposit fields; consumed only when feature is enabled. + let _ = &other; + OpCallDepositInfo::default() + }; - (evm_env, tx_env) + (evm_env, tx_env, op_deposit) } pub fn call_with_state( @@ -1467,13 +1536,13 @@ impl Backend { (fee_token, nonce_key, valid_before, valid_after) }); - let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env); let ResultAndState { result, state } = if let Some((fee_token, nonce_key, valid_before, valid_after)) = tempo_overrides { use tempo_primitives::transaction::Call; - let base = tx_env.base; + let base = tx_env; let mut tempo_tx = TempoTxEnv::from(base.clone()); tempo_tx.fee_token = fee_token; @@ -1495,7 +1564,13 @@ impl Backend { } self.transact_tempo_with_inspector_ref(state, &evm_env, &mut inspector, tempo_tx)? } else { - self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)? + self.transact_with_inspector_ref( + state, + &evm_env, + &mut inspector, + tx_env, + op_deposit, + )? }; let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); @@ -1518,9 +1593,9 @@ impl Backend { let mut inspector = AccessListInspector::new(request.access_list.clone().unwrap_or_default()); - let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env); let ResultAndState { result, state: _ } = - self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)?; + self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env, op_deposit)?; let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); let access_list = inspector.access_list(); Ok((exit_reason, out, gas_used, access_list)) @@ -2912,7 +2987,7 @@ where TracingInspectorConfig::from_geth_call_config(&call_config), ); - let (evm_env, tx_env) = + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block); let ResultAndState { result, state: _ } = self .transact_with_inspector_ref( @@ -2920,6 +2995,7 @@ where &evm_env, &mut inspector, tx_env, + op_deposit, )?; inspector.print_logs(); @@ -2945,13 +3021,14 @@ where ), ); - let (evm_env, tx_env) = + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block); let result = self.transact_with_inspector_ref( &cache_db, &evm_env, &mut inspector, tx_env, + op_deposit, )?; Ok(inspector @@ -2973,22 +3050,22 @@ where } #[cfg(feature = "js-tracer")] GethDebugTracerType::JsTracer(code) => { - use alloy_evm::IntoTxEnv; let config = tracer_config.into_json(); let mut inspector = revm_inspectors::tracing::js::JsInspector::new(code, config) .map_err(|err| BlockchainError::Message(err.to_string()))?; - let (evm_env, tx_env) = + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block.clone()); let result = self.transact_with_inspector_ref( &cache_db, &evm_env, &mut inspector, tx_env.clone(), + op_deposit, )?; let res = inspector - .json_result(result, &OpTx(tx_env).into_tx_env(), &block, &cache_db) + .json_result(result, &tx_env, &block, &cache_db) .map_err(|err| BlockchainError::Message(err.to_string()))?; Ok(GethTrace::JS(res)) @@ -3001,9 +3078,14 @@ where .build_inspector() .with_tracing_config(TracingInspectorConfig::from_geth_config(&config)); - let (evm_env, tx_env) = self.build_call_env(request, fee_details, block); - let ResultAndState { result, state: _ } = - self.transact_with_inspector_ref(&cache_db, &evm_env, &mut inspector, tx_env)?; + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block); + let ResultAndState { result, state: _ } = self.transact_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + tx_env, + op_deposit, + )?; let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); @@ -3187,10 +3269,7 @@ where f: F, ) -> Result where - for<'a> I: Inspector>>>> - + Inspector>>>> - + Inspector>>>> - + 'a, + for<'a> I: BackendInspector>>> + 'a, for<'a> F: FnOnce(ResultAndState, CacheDB>, I, TxEnv, EvmEnv) -> T, { @@ -3965,7 +4044,7 @@ impl Backend { )? .or_zero_fees(); - let (mut evm_env, tx_env) = self.build_call_env( + let (mut evm_env, tx_env, op_deposit) = self.build_call_env( WithOtherFields::new(request.clone()), fee_details, block_env.clone(), @@ -3986,8 +4065,13 @@ impl Backend { inspector = inspector.with_transfers(); } trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env"); - let ResultAndState { result, state } = - self.transact_with_inspector_ref(&cache_db, &evm_env, &mut inspector, tx_env)?; + let ResultAndState { result, state } = self.transact_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + tx_env, + op_deposit, + )?; trace!(target: "backend", ?result, ?request, "simulate call"); inspector.print_logs(); @@ -4359,7 +4443,10 @@ where } // Nonce validation — skip for deposits (L1→L2) and Tempo txs (2D nonce system) + #[cfg(feature = "optimism")] let is_deposit_tx = pending.transaction.as_ref().is_deposit(); + #[cfg(not(feature = "optimism"))] + let is_deposit_tx = false; let is_tempo_tx = pending.transaction.as_ref().is_tempo(); let nonce = tx.nonce(); if nonce < account.nonce && !is_deposit_tx && !is_tempo_tx { @@ -4475,6 +4562,7 @@ where ); let value = tx.value(); match tx.as_ref() { + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(deposit_tx) => { // Deposit transactions // https://specs.optimism.io/protocol/deposits.html#execution @@ -4538,6 +4626,7 @@ pub fn transaction_build( info: Option, base_fee: Option, ) -> AnyRpcTransaction { + #[cfg(feature = "optimism")] if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() { let dep_tx = deposit_tx; diff --git a/crates/anvil/src/eth/backend/mem/optimism.rs b/crates/anvil/src/eth/backend/mem/optimism.rs new file mode 100644 index 0000000000000..e9a94cc254fb7 --- /dev/null +++ b/crates/anvil/src/eth/backend/mem/optimism.rs @@ -0,0 +1,61 @@ +//! Optimism-specific transact helpers for the in-memory backend. + +use super::Backend; +use crate::eth::error::BlockchainError; +use alloy_evm::{Database, Evm, EvmEnv, EvmFactory}; +use alloy_network::Network; +use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx}; +use foundry_evm::backend::DatabaseError; +use op_revm::{OpHaltReason, OpSpecId, OpTransaction}; +use revm::{ + DatabaseRef, Inspector, + context::{ + TxEnv, + result::{EVMError, HaltReason, ResultAndState}, + }, + database_interface::WrapDatabaseRef, +}; + +impl Backend { + /// Optimism path of [`Backend::transact_with_inspector_ref`]. + /// + /// Creates an OP EVM, injects precompiles, transacts, and maps the + /// OP-specific halt reason back to the shared [`HaltReason`]. + pub(super) fn transact_op_with_inspector_ref<'db, I, DB>( + &self, + db: &'db DB, + evm_env: &EvmEnv, + inspector: &mut I, + tx_env: OpTransaction, + ) -> Result, BlockchainError> + where + DB: DatabaseRef + ?Sized, + I: Inspector>>, + WrapDatabaseRef<&'db DB>: Database, + { + let op_env = EvmEnv::new( + evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(OpSpecId::ISTHMUS), + evm_env.block_env.clone(), + ); + let mut evm = OpEvmFactory::default().create_evm_with_inspector( + WrapDatabaseRef(db), + op_env, + inspector, + ); + self.inject_precompiles(evm.precompiles_mut()); + let result = evm.transact(OpTx(tx_env)).map_err(|e| match e { + EVMError::Database(db) => EVMError::Database(db), + EVMError::Header(h) => EVMError::Header(h), + EVMError::Custom(s) => EVMError::Custom(s), + EVMError::CustomAny(err) => EVMError::CustomAny(err), + EVMError::Transaction(t) => EVMError::Transaction(t), + })?; + Ok(ResultAndState { + result: result.result.map_haltreason(|h| match h { + OpHaltReason::Base(eth) => eth, + _ => HaltReason::PrecompileError, + }), + state: result.state, + }) + } +} diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error/mod.rs similarity index 91% rename from crates/anvil/src/eth/error.rs rename to crates/anvil/src/eth/error/mod.rs index 3b2ada43d7731..482df681b184a 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error/mod.rs @@ -12,7 +12,6 @@ use anvil_rpc::{ response::ResponseResult, }; use foundry_evm::{backend::DatabaseError, decode::RevertDecoder}; -use op_revm::OpTransactionError; use revm::{ context_interface::result::{EVMError, InvalidHeader, InvalidTransaction}, interpreter::InstructionResult, @@ -21,6 +20,9 @@ use serde::Serialize; use tempo_revm::TempoInvalidTransaction; use tokio::time::Duration; +#[cfg(feature = "optimism")] +mod optimism; + pub(crate) type Result = std::result::Result; #[derive(Debug, thiserror::Error)] @@ -163,51 +165,6 @@ where } } -impl From> for BlockchainError -where - T: Into, -{ - fn from(err: EVMError) -> Self { - match err { - EVMError::Transaction(err) => match err { - OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(), - OpTransactionError::DepositSystemTxPostRegolith => { - Self::DepositTransactionUnsupported - } - OpTransactionError::HaltedDepositPostRegolith => { - Self::DepositTransactionUnsupported - } - OpTransactionError::MissingEnvelopedTx => Self::InvalidTransaction(err.into()), - }, - EVMError::Header(err) => match err { - InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet, - InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet, - }, - EVMError::Database(err) => err.into(), - EVMError::Custom(err) => Self::Message(err), - EVMError::CustomAny(err) => Self::Message(err.to_string()), - } - } -} - -impl From> for BlockchainError -where - T: Into, -{ - fn from(err: EVMError) -> Self { - match err { - EVMError::Transaction(err) => { - let op_err: OpTransactionError = err.0; - EVMError::::Transaction(op_err).into() - } - EVMError::Header(err) => EVMError::::Header(err).into(), - EVMError::Database(err) => err.into(), - EVMError::Custom(err) => Self::Message(err), - EVMError::CustomAny(err) => Self::Message(err.to_string()), - } - } -} - impl From> for BlockchainError where T: Into, @@ -451,16 +408,6 @@ impl From for InvalidTransactionError { } } -impl From for InvalidTransactionError { - fn from(value: OpTransactionError) -> Self { - match value { - OpTransactionError::Base(err) => err.into(), - OpTransactionError::DepositSystemTxPostRegolith - | OpTransactionError::HaltedDepositPostRegolith => Self::DepositTxErrorPostRegolith, - OpTransactionError::MissingEnvelopedTx => Self::MissingEnvelopedTx, - } - } -} /// Helper trait to easily convert results to rpc results pub(crate) trait ToRpcResponseResult { fn to_rpc_result(self) -> ResponseResult; @@ -577,9 +524,13 @@ impl ToRpcResponseResult for Result { err => RpcError::internal_error_with(format!("Fork Error: {err:?}")), } } - err @ BlockchainError::EvmError(_) => { - RpcError::internal_error_with(err.to_string()) - } + err @ BlockchainError::EvmError(_) => RpcError { + // VM halts are execution failures, not JSON-RPC server faults. REVERT has a + // dedicated code/data path above; other halts, such as invalid opcode, do not. + code: ErrorCode::TransactionRejected, + message: err.to_string().into(), + data: None, + }, err @ BlockchainError::EvmOverrideError(_) => { RpcError::invalid_params(err.to_string()) } diff --git a/crates/anvil/src/eth/error/optimism.rs b/crates/anvil/src/eth/error/optimism.rs new file mode 100644 index 0000000000000..1207fde30a72a --- /dev/null +++ b/crates/anvil/src/eth/error/optimism.rs @@ -0,0 +1,62 @@ +//! Optimism-specific error conversions for [`BlockchainError`] and +//! [`InvalidTransactionError`]. + +use super::{BlockchainError, InvalidTransactionError}; +use op_revm::OpTransactionError; +use revm::context_interface::result::{EVMError, InvalidHeader}; + +impl From> for BlockchainError +where + T: Into, +{ + fn from(err: EVMError) -> Self { + match err { + EVMError::Transaction(err) => match err { + OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(), + OpTransactionError::DepositSystemTxPostRegolith => { + Self::DepositTransactionUnsupported + } + OpTransactionError::HaltedDepositPostRegolith => { + Self::DepositTransactionUnsupported + } + OpTransactionError::MissingEnvelopedTx => Self::InvalidTransaction(err.into()), + }, + EVMError::Header(err) => match err { + InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet, + InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet, + }, + EVMError::Database(err) => err.into(), + EVMError::Custom(err) => Self::Message(err), + EVMError::CustomAny(err) => Self::Message(err.to_string()), + } + } +} + +impl From> for BlockchainError +where + T: Into, +{ + fn from(err: EVMError) -> Self { + match err { + EVMError::Transaction(err) => { + let op_err: OpTransactionError = err.0; + EVMError::::Transaction(op_err).into() + } + EVMError::Header(err) => EVMError::::Header(err).into(), + EVMError::Database(err) => err.into(), + EVMError::Custom(err) => Self::Message(err), + EVMError::CustomAny(err) => Self::Message(err.to_string()), + } + } +} + +impl From for InvalidTransactionError { + fn from(value: OpTransactionError) -> Self { + match value { + OpTransactionError::Base(err) => err.into(), + OpTransactionError::DepositSystemTxPostRegolith + | OpTransactionError::HaltedDepositPostRegolith => Self::DepositTxErrorPostRegolith, + OpTransactionError::MissingEnvelopedTx => Self::MissingEnvelopedTx, + } + } +} diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 4da86933020ae..2ccf65ba721f5 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -155,9 +155,12 @@ impl EthApi { let best = self.backend.best_number(); // we go from given block (defaulting to best) down to first block - // considering only post-fork + // considering only post-fork (or post-genesis in non-fork mode) 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 to = self + .get_fork() + .map(|f| f.block_number() + 1) + .unwrap_or_else(|| self.backend.genesis_number() + 1); let first_page = from >= best; let mut last_page = false; @@ -198,8 +201,11 @@ impl EthApi { node_info!("ots_searchTransactionsAfter"); let best = self.backend.best_number(); - // we go from the first post-fork block, up to the tip - let first_block = self.get_fork().map(|f| f.block_number() + 1).unwrap_or(1); + // we go from the first post-fork (or post-genesis) block, up to the tip + let first_block = self + .get_fork() + .map(|f| f.block_number() + 1) + .unwrap_or_else(|| self.backend.genesis_number() + 1); let from = if block_number == 0 { first_block } else { block_number + 1 }; let to = best; @@ -248,7 +254,10 @@ impl EthApi { ) -> Result> { node_info!("ots_getTransactionBySenderAndNonce"); - let from = self.get_fork().map(|f| f.block_number() + 1).unwrap_or_default(); + let from = self + .get_fork() + .map(|f| f.block_number() + 1) + .unwrap_or_else(|| self.backend.genesis_number() + 1); let to = self.backend.best_number(); for n in (from..=to).rev() { diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index df65822e1eab3..5280987483dd7 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -123,10 +123,10 @@ impl PoolTransaction { impl fmt::Debug for PoolTransaction { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "Transaction {{ ")?; - write!(fmt, "hash: {:?}, ", &self.pending_transaction.hash())?; + write!(fmt, "hash: {:?}, ", self.pending_transaction.hash())?; write!(fmt, "requires: [{}], ", hex_fmt_many(self.requires.iter()))?; write!(fmt, "provides: [{}], ", hex_fmt_many(self.provides.iter()))?; - write!(fmt, "raw tx: {:?}", &self.pending_transaction)?; + write!(fmt, "raw tx: {:?}", self.pending_transaction)?; write!(fmt, "}}")?; Ok(()) } diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 3fdf6192c4537..d1736c3093056 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,5 +1,7 @@ use crate::eth::error::BlockchainError; -use alloy_consensus::{Sealed, SignableTransaction}; +#[cfg(feature = "optimism")] +use alloy_consensus::Sealed; +use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; use alloy_network::{Network, TxSignerSync}; use alloy_primitives::{Address, B256, Signature, map::AddressHashMap}; @@ -130,9 +132,11 @@ impl Signer for DevSigner { let sig = signer.sign_transaction_sync(&mut t)?; FoundryTxEnvelope::Eip4844(t.into_signed(sig)) } + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(_) => { unreachable!("op deposit txs should not be signed") } + #[cfg(feature = "optimism")] FoundryTypedTx::PostExec(_) => { unreachable!("op post-exec txs should not be signed") } @@ -156,7 +160,9 @@ pub fn build_impersonated(typed_tx: FoundryTypedTx) -> FoundryTxEnvelope { FoundryTypedTx::Eip1559(tx) => FoundryTxEnvelope::Eip1559(tx.into_signed(signature)), FoundryTypedTx::Eip7702(tx) => FoundryTxEnvelope::Eip7702(tx.into_signed(signature)), FoundryTypedTx::Eip4844(tx) => FoundryTxEnvelope::Eip4844(tx.into_signed(signature)), + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(tx) => FoundryTxEnvelope::Deposit(Sealed::new(tx)), + #[cfg(feature = "optimism")] FoundryTypedTx::PostExec(_) => { unreachable!("op post-exec txs should not be impersonated") } diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm/mod.rs similarity index 64% rename from crates/anvil/src/evm.rs rename to crates/anvil/src/evm/mod.rs index d1b40ba56ebbd..85e43d371b097 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm/mod.rs @@ -2,6 +2,9 @@ use alloy_evm::precompiles::DynPrecompile; use alloy_primitives::Address; use std::fmt::Debug; +#[cfg(feature = "optimism")] +mod optimism; + /// Object-safe trait that enables injecting extra precompiles when using /// `anvil` as a library. pub trait PrecompileFactory: Send + Sync + Unpin + Debug { @@ -15,14 +18,12 @@ mod tests { use crate::PrecompileFactory; use alloy_evm::{ - EthEvm, Evm, EvmEnv, EvmFactory, + EthEvm, Evm, eth::EthEvmContext, precompiles::{DynPrecompile, PrecompilesMap}, }; - use alloy_op_evm::{OpEvm, OpEvmFactory, OpTx}; - use alloy_primitives::{Address, Bytes, TxKind, U256, address}; + use alloy_primitives::{Address, Bytes, TxKind, address}; use itertools::Itertools; - use op_revm::{OpSpecId, OpTransaction}; use revm::{ Journal, context::{BlockEnv, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv}, @@ -35,20 +36,19 @@ mod tests { }; // A precompile activated in the `Prague` spec (BLS12-381 G2 map). - const ETH_PRAGUE_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000011"); + pub(super) const ETH_PRAGUE_PRECOMPILE: Address = + address!("0x0000000000000000000000000000000000000011"); // A precompile activated in the `Osaka` spec (EIP-7951). const ETH_OSAKA_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); - // A precompile activated in the `Isthmus` spec. - const OP_ISTHMUS_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); - // A custom precompile address and payload for testing. - const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071"); - const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; + pub(super) const PRECOMPILE_ADDR: Address = + address!("0x0000000000000000000000000000000000000071"); + pub(super) const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; #[derive(Debug)] - struct CustomPrecompileFactory; + pub(super) struct CustomPrecompileFactory; impl PrecompileFactory for CustomPrecompileFactory { fn precompiles(&self) -> Vec<(Address, DynPrecompile)> { @@ -109,34 +109,6 @@ mod tests { (tx_env, eth_evm) } - /// Creates a new OP EVM instance. - fn create_op_evm( - _spec: SpecId, - op_spec: OpSpecId, - ) -> (OpTx, OpEvm, NoOpInspector, PrecompilesMap, OpTx>) { - let tx = OpTx(OpTransaction:: { - base: TxEnv { - kind: TxKind::Call(PRECOMPILE_ADDR), - data: PAYLOAD.into(), - ..Default::default() - }, - ..Default::default() - }); - - let mut evm = OpEvmFactory::::default().create_evm_with_inspector( - EmptyDB::default(), - EvmEnv::new(CfgEnv::new_with_spec(op_spec), BlockEnv::default()), - NoOpInspector, - ); - - if op_spec == OpSpecId::ISTHMUS { - evm.ctx_mut().chain.operator_fee_constant = Some(U256::ZERO); - evm.ctx_mut().chain.operator_fee_scalar = Some(U256::ZERO); - } - - (tx, evm) - } - #[test] fn build_eth_evm_with_extra_precompiles_osaka_spec() { let (tx_env, mut evm) = create_eth_evm(SpecId::OSAKA); @@ -187,38 +159,4 @@ mod tests { assert!(result.result.is_success()); assert_eq!(result.result.output(), Some(&PAYLOAD.into())); } - - #[test] - fn build_op_evm_with_extra_precompiles_isthmus_spec() { - let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::ISTHMUS); - - assert!(evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); - assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); - assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); - - assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - let result = evm.transact(tx).unwrap(); - assert!(result.result.is_success()); - assert_eq!(result.result.output(), Some(&PAYLOAD.into())); - } - - #[test] - fn build_op_evm_with_extra_precompiles_bedrock_spec() { - let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::BEDROCK); - - assert!(!evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); - assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); - assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); - - assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - let result = evm.transact(tx).unwrap(); - assert!(result.result.is_success()); - assert_eq!(result.result.output(), Some(&PAYLOAD.into())); - } } diff --git a/crates/anvil/src/evm/optimism.rs b/crates/anvil/src/evm/optimism.rs new file mode 100644 index 0000000000000..526375fec31ea --- /dev/null +++ b/crates/anvil/src/evm/optimism.rs @@ -0,0 +1,87 @@ +//! Optimism-specific EVM helpers. + +#[cfg(test)] +mod tests { + use std::convert::Infallible; + + use super::super::tests::{ + CustomPrecompileFactory, ETH_PRAGUE_PRECOMPILE, PAYLOAD, PRECOMPILE_ADDR, + }; + use crate::PrecompileFactory; + use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap}; + use alloy_op_evm::{OpEvm, OpEvmFactory, OpTx}; + use alloy_primitives::{Address, TxKind, U256, address}; + use itertools::Itertools; + use op_revm::{OpSpecId, OpTransaction}; + use revm::{ + context::{BlockEnv, CfgEnv, TxEnv}, + database::{EmptyDB, EmptyDBTyped}, + inspector::NoOpInspector, + primitives::hardfork::SpecId, + }; + + // A precompile activated in the `Isthmus` spec. + const OP_ISTHMUS_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); + + /// Creates a new OP EVM instance. + fn create_op_evm( + _spec: SpecId, + op_spec: OpSpecId, + ) -> (OpTx, OpEvm, NoOpInspector, PrecompilesMap, OpTx>) { + let tx = OpTx(OpTransaction:: { + base: TxEnv { + kind: TxKind::Call(PRECOMPILE_ADDR), + data: PAYLOAD.into(), + ..Default::default() + }, + ..Default::default() + }); + + let mut evm = OpEvmFactory::::default().create_evm_with_inspector( + EmptyDB::default(), + EvmEnv::new(CfgEnv::new_with_spec(op_spec), BlockEnv::default()), + NoOpInspector, + ); + + if op_spec == OpSpecId::ISTHMUS { + evm.ctx_mut().chain.operator_fee_constant = Some(U256::ZERO); + evm.ctx_mut().chain.operator_fee_scalar = Some(U256::ZERO); + } + + (tx, evm) + } + + #[test] + fn build_op_evm_with_extra_precompiles_isthmus_spec() { + let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::ISTHMUS); + + assert!(evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); + assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); + assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); + + assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + let result = evm.transact(tx).unwrap(); + assert!(result.result.is_success()); + assert_eq!(result.result.output(), Some(&PAYLOAD.into())); + } + + #[test] + fn build_op_evm_with_extra_precompiles_bedrock_spec() { + let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::BEDROCK); + + assert!(!evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); + assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); + assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); + + assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + let result = evm.transact(tx).unwrap(); + assert!(result.result.is_success()); + assert_eq!(result.result.output(), Some(&PAYLOAD.into())); + } +} diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 26e587e8b5123..a661e9c765b26 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -3,6 +3,9 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "optimism")] +use op_alloy_rpc_types as _; + use crate::{ error::{NodeError, NodeResult}, eth::{ diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index c4879e36d5240..216476c69eeb8 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -11,6 +11,7 @@ mod gas; mod genesis; mod ipc; mod logs; +#[cfg(feature = "optimism")] mod optimism; mod otterscan; mod proof; diff --git a/crates/anvil/tests/it/revert.rs b/crates/anvil/tests/it/revert.rs index ab85fc89abf80..a15454fa5593e 100644 --- a/crates/anvil/tests/it/revert.rs +++ b/crates/anvil/tests/it/revert.rs @@ -28,6 +28,38 @@ async fn test_deploy_reverting() { assert!(!receipt.inner.inner.status()); } +#[tokio::test(flavor = "multi_thread")] +async fn test_invalid_opcode_rpc_error_code() { + let (_api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); + + // Deploy a contract whose runtime bytecode is the invalid opcode 0xfe. + let code = bytes!("60fe60005360016000f3"); + let tx = TransactionRequest::default().from(sender).with_deploy_code(code); + let receipt = provider + .send_transaction(WithOtherFields::new(tx)) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract = receipt.contract_address.unwrap(); + + for (method, params) in [ + ("eth_call", serde_json::json!([{ "from": sender, "to": contract }, "latest"])), + ("eth_estimateGas", serde_json::json!([{ "from": sender, "to": contract }])), + ] { + let error = rpc_error(&handle.http_endpoint(), method, params).await; + assert_eq!(error["code"], serde_json::json!(-32003), "{error}"); + assert!(error.get("data").is_none(), "{error}"); + + let message = error["message"].as_str().unwrap(); + assert!(message.contains("EVM error InvalidFEOpcode"), "{error}"); + assert!(!message.contains("execution reverted"), "{error}"); + } +} + #[tokio::test(flavor = "multi_thread")] async fn test_revert_messages() { sol!( @@ -124,3 +156,21 @@ async fn test_solc_revert_custom_errors() { let s = err.to_string(); assert!(s.contains("execution reverted"), "{s:?}"); } + +async fn rpc_error(endpoint: &str, method: &str, params: serde_json::Value) -> serde_json::Value { + let response = reqwest::Client::new() + .post(endpoint) + .json(&serde_json::json!({ + "jsonrpc": "2.0", + "id": 1, + "method": method, + "params": params, + })) + .send() + .await + .unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + let body = response.json::().await.unwrap(); + body.get("error").cloned().unwrap_or_else(|| panic!("expected JSON-RPC error, got {body}")) +} diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index dc5be77a244da..03be26a631a11 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -61,9 +61,9 @@ tempo-contracts.workspace = true tempo-primitives.workspace = true alloy-evm.workspace = true -op-alloy-consensus = { workspace = true, features = ["k256"] } -op-alloy-flz.workspace = true -op-alloy-network.workspace = true +op-alloy-consensus = { workspace = true, features = ["k256"], optional = true } +op-alloy-flz = { workspace = true, optional = true } +op-alloy-network = { workspace = true, optional = true } chrono.workspace = true eyre.workspace = true @@ -100,7 +100,7 @@ anvil.workspace = true foundry-test-utils.workspace = true [features] -default = ["jemalloc", "asm-keccak"] +default = ["jemalloc", "asm-keccak", "optimism"] asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] @@ -109,3 +109,12 @@ aws-kms = ["foundry-wallets/aws-kms"] gcp-kms = ["foundry-wallets/gcp-kms"] turnkey = ["foundry-wallets/turnkey"] isolate-by-default = ["foundry-config/isolate-by-default"] +optimism = [ + "dep:op-alloy-flz", + "dep:op-alloy-consensus", + "dep:op-alloy-network", + "foundry-common/optimism", + "foundry-evm-networks/optimism", + "foundry-evm/optimism", + "foundry-cli/optimism", +] diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index 6dc2ed6acf430..23fda25c40faa 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -29,6 +29,7 @@ use foundry_common::{ shell, stdin, }; use foundry_evm_networks::NetworkVariant; +#[cfg(feature = "optimism")] use op_alloy_network::Optimism; use std::time::Instant; use tempo_alloy::TempoNetwork; @@ -351,6 +352,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { // Can use either --raw or specify raw as a field let output = if raw || fields.contains(&"raw".into()) { match network { + #[cfg(feature = "optimism")] Some(NetworkVariant::Optimism) => { let provider = ProviderBuilder::::from_config(&config)?.build()?; @@ -569,6 +571,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { // Can use either --raw or specify raw as a field let is_raw = raw || field.as_ref().is_some_and(|f| f == "raw"); let output = match network { + #[cfg(feature = "optimism")] Some(NetworkVariant::Optimism) => { let provider = ProviderBuilder::::from_config(&config)?.build()?; @@ -791,6 +794,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { CastSubcommand::DecodeTransaction { tx, network } => { let tx = stdin::unwrap_line(tx)?; let decoded_tx = match network { + #[cfg(feature = "optimism")] Some(NetworkVariant::Optimism) => { SimpleCast::decode_raw_transaction::(&tx)? } @@ -809,6 +813,9 @@ pub async fn run_command(args: CastArgs) -> Result<()> { CastSubcommand::Erc20Token { command } => command.run().await?, CastSubcommand::Tip20Token { command } => command.run().await?, CastSubcommand::Keychain { command } => command.run().await?, + CastSubcommand::Tempo { command } => command.run().await?, + CastSubcommand::VirtualAddress { command } => command.run().await?, + #[cfg(feature = "optimism")] CastSubcommand::DAEstimate(cmd) => { cmd.run().await?; } diff --git a/crates/cast/src/cmd/batch_mktx.rs b/crates/cast/src/cmd/batch_mktx.rs index ae5c9668f522f..e25798086d512 100644 --- a/crates/cast/src/cmd/batch_mktx.rs +++ b/crates/cast/src/cmd/batch_mktx.rs @@ -9,7 +9,7 @@ use crate::{ }; use alloy_consensus::SignableTransaction; use alloy_eips::eip2718::Encodable2718; -use alloy_network::{EthereumWallet, NetworkTransactionBuilder}; +use alloy_network::{EthereumWallet, NetworkTransactionBuilder, TransactionBuilder}; use alloy_primitives::Address; use alloy_provider::Provider; use alloy_signer::Signer; @@ -17,7 +17,7 @@ use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, LoadConfig}, + utils::{self, LoadConfig, maybe_print_resolved_lane, resolve_lane}, }; use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; use tempo_alloy::TempoNetwork; @@ -53,7 +53,7 @@ pub struct BatchMakeTxArgs { impl BatchMakeTxArgs { pub async fn run(self) -> Result<()> { - let Self { calls, tx, eth, raw_unsigned, ethsign } = self; + let Self { calls, mut tx, eth, raw_unsigned, ethsign } = self; let has_nonce = tx.nonce.is_some(); if calls.is_empty() { @@ -63,6 +63,10 @@ impl BatchMakeTxArgs { let config = eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx.tempo.nonce_key` from the lane. + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + // Resolve signer to detect keychain mode let (signer, tempo_access_key) = eth.wallet.maybe_signer().await?; @@ -92,14 +96,14 @@ impl BatchMakeTxArgs { sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?; - // Build transaction request with calls - let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; - - // Set key_id for access key transactions + // Preserve key_id for modes that do not call build_with_access_key, such as raw unsigned. if let Some(ref access_key) = tempo_access_key { - builder.tx.set_key_id(access_key.key_address); + tx.tempo.key_id = Some(access_key.key_address); } + // Build transaction request with calls + let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; + // Set calls on the transaction builder.tx.calls = tempo_calls; @@ -117,6 +121,7 @@ impl BatchMakeTxArgs { let from = eth.wallet.from.unwrap_or(Address::ZERO); let (tx, _) = tx_builder.build(from).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let raw_tx = alloy_primitives::hex::encode_prefixed(tx.build_unsigned()?.encoded_for_signing()); sh_println!("{raw_tx}")?; @@ -125,6 +130,7 @@ impl BatchMakeTxArgs { if ethsign { let (tx, _) = tx_builder.build(config.sender).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let signed_tx = provider.sign_transaction(tx).await?; sh_println!("{signed_tx}")?; return Ok(()); @@ -137,7 +143,9 @@ impl BatchMakeTxArgs { }; let signed_tx = if let Some(ref access_key) = tempo_access_key { - let (tx, _) = tx_builder.build(access_key.wallet_address).await?; + let (tx, _) = + tx_builder.build_with_access_key(access_key.wallet_address, access_key).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let raw_tx = tx .sign_with_access_key( &provider, @@ -151,6 +159,7 @@ impl BatchMakeTxArgs { } else { tx::validate_from_address(eth.wallet.from, Signer::address(&signer))?; let (tx, _) = tx_builder.build(&signer).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let envelope = tx.build(&EthereumWallet::new(signer)).await?; alloy_primitives::hex::encode(envelope.encoded_2718()) }; diff --git a/crates/cast/src/cmd/batch_send.rs b/crates/cast/src/cmd/batch_send.rs index ec7254b08e2f5..33128ae897898 100644 --- a/crates/cast/src/cmd/batch_send.rs +++ b/crates/cast/src/cmd/batch_send.rs @@ -9,14 +9,14 @@ use crate::{ cmd::send::{cast_send, cast_send_with_access_key}, tx::{self, CastTxBuilder, SendTxOpts}, }; -use alloy_network::EthereumWallet; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::Signer; use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::TransactionOpts, - utils::{self, LoadConfig}, + utils::{self, LoadConfig, maybe_print_resolved_lane, resolve_lane}, }; use foundry_common::provider::ProviderBuilder; use std::time::Duration; @@ -50,7 +50,7 @@ pub struct BatchSendArgs { impl BatchSendArgs { pub async fn run(self) -> Result<()> { - let Self { calls, send_tx, tx, unlocked } = self; + let Self { calls, send_tx, mut tx, unlocked } = self; if calls.is_empty() { return Err(eyre!("No calls specified. Use --call to specify at least one call.")); @@ -59,6 +59,10 @@ impl BatchSendArgs { let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx.tempo.nonce_key` from the lane. + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + if let Some(interval) = send_tx.poll_interval { provider.client().set_poll_interval(Duration::from_secs(interval)) } @@ -93,14 +97,14 @@ impl BatchSendArgs { sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?; - // Build transaction request with calls - let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; - - // Set key_id for access key transactions + // Preserve key_id for modes that do not call build_with_access_key, such as unlocked. if let Some(ref access_key) = tempo_access_key { - builder.tx.set_key_id(access_key.key_address); + tx.tempo.key_id = Some(access_key.key_address); } + // Build transaction request with calls + let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; + // Access the inner tx and set calls builder.tx.calls = tempo_calls; @@ -116,6 +120,7 @@ impl BatchSendArgs { if unlocked { let (tx, _) = builder.build(config.sender).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; cast_send( provider, tx, @@ -132,7 +137,12 @@ impl BatchSendArgs { }; if let Some(ref access_key) = tempo_access_key { - let (tx_request, _) = builder.build(access_key.wallet_address).await?; + let (tx_request, _) = + builder.build_with_access_key(access_key.wallet_address, access_key).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; cast_send_with_access_key( &provider, tx_request, @@ -146,6 +156,10 @@ impl BatchSendArgs { } else { tx::validate_from_address(send_tx.eth.wallet.from, Signer::address(&signer))?; let (tx_request, _) = builder.build(&signer).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; let wallet = EthereumWallet::from(signer); let provider = AlloyProviderBuilder::<_, _, TempoNetwork>::default() .wallet(wallet) diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index 3637f166a53df..63ea17f707e03 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -33,10 +33,12 @@ use foundry_config::{ value::{Dict, Map}, }, }; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ core::{ FoundryBlock, FoundryTransaction, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork}, + evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork}, }, executors::TracingExecutor, opts::EvmOpts, @@ -222,18 +224,19 @@ impl CallArgs { return self.run_curl().await; } if self.tx.tempo.is_tempo() { - self.run_with_network::().await - } else { - let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); - let mut evm_opts = figment.extract::()?; - evm_opts.infer_network_from_fork().await; + return self.run_with_network::().await; + } - if evm_opts.networks.is_optimism() { - self.run_with_network::().await - } else { - self.run_with_network::().await - } + let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); + let mut evm_opts = figment.extract::()?; + evm_opts.infer_network_from_fork().await; + + #[cfg(feature = "optimism")] + if evm_opts.networks.is_optimism() { + return self.run_with_network::().await; } + + self.run_with_network::().await } pub async fn run_with_network(self) -> Result<()> diff --git a/crates/cast/src/cmd/keychain.rs b/crates/cast/src/cmd/keychain.rs index 8b7d80786dfad..897a01c39202a 100644 --- a/crates/cast/src/cmd/keychain.rs +++ b/crates/cast/src/cmd/keychain.rs @@ -1,9 +1,12 @@ use alloy_ens::NameOrAddress; -use alloy_network::EthereumWallet; +use std::time::Duration; + +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, U256, hex, keccak256}; -use alloy_provider::ProviderBuilder as AlloyProviderBuilder; +use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::Signer; use alloy_sol_types::SolCall; +use alloy_transport::TransportError; use chrono::DateTime; use clap::Parser; use eyre::Result; @@ -12,11 +15,16 @@ use foundry_cli::{ utils::LoadConfig, }; use foundry_common::{ + FoundryTransactionBuilder, provider::ProviderBuilder, - shell, - tempo::{self, KeyType, KeysFile, WalletType, read_tempo_keys_file, tempo_keys_path}, + sh_warn, shell, + tempo::{ + self, KeyType, KeysFile, TEMPO_BROWSER_GAS_BUFFER, WalletType, read_tempo_keys_file, + tempo_keys_path, + }, }; use foundry_evm::hardfork::TempoHardfork; +use serde::Deserialize; use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; use tempo_contracts::precompiles::{ ACCOUNT_KEYCHAIN_ADDRESS, IAccountKeychain, @@ -24,13 +32,16 @@ use tempo_contracts::precompiles::{ CallScope, KeyInfo, KeyRestrictions, LegacyTokenLimit, SelectorRule, SignatureType, TokenLimit, }, + ITIP20, PATH_USD_ADDRESS, account_keychain::{authorizeKeyCall, legacyAuthorizeKeyCall}, }; use yansi::Paint; +use foundry_cli::utils::{maybe_print_resolved_lane, resolve_lane}; + use crate::{ - cmd::send::{cast_send, cast_send_with_access_key}, - tx::{CastTxBuilder, SendTxOpts}, + cmd::send::cast_send, + tx::{CastTxBuilder, CastTxSender, SendTxOpts}, }; /// Tempo keychain management commands. @@ -62,6 +73,19 @@ pub enum KeychainSubcommand { rpc: RpcOpts, }, + /// Inspect an access key policy using the local key registry and on-chain state. + Inspect { + /// The key address to inspect. + key_address: Address, + + /// Root account address. Required when the key is not present in the local keys.toml. + #[arg(long, visible_alias = "wallet-address", value_name = "ADDRESS")] + root_account: Option
, + + #[command(flatten)] + rpc: RpcOpts, + }, + /// Authorize a new key on-chain via the AccountKeychain precompile. #[command(visible_alias = "auth")] Authorize { @@ -183,8 +207,92 @@ pub enum KeychainSubcommand { #[command(flatten)] send_tx: SendTxOpts, }, + + /// Read or edit TIP-1011 access-key permissions. + Policy { + #[command(subcommand)] + command: KeychainPolicySubcommand, + }, +} + +/// Higher-level access-key policy editing commands. +#[derive(Debug, Parser)] +pub enum KeychainPolicySubcommand { + /// Add or widen an allowed call rule for a target contract. + AddCall { + /// The key address to update. + key_address: Address, + + /// Root account address. Required when the key is not present in the local keys.toml. + #[arg(long, visible_alias = "wallet-address", value_name = "ADDRESS")] + root_account: Option
, + + /// Target contract address. + #[arg(long)] + target: Address, + + /// Function selector, full signature, or known TIP-20 shorthand. + #[arg(long, value_parser = parse_selector_arg)] + selector: SelectorArg, + + /// Optional recipient/spender restrictions for selector calls. + #[arg(long, value_delimiter = ',')] + recipients: Vec
, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Update a token spending limit amount for a key. + SetLimit { + /// The key address to update. + key_address: Address, + + /// Token address, numeric TIP-20 token id, or PathUSD. + #[arg(long, value_parser = parse_policy_token)] + token: Address, + + /// New raw token-denominated limit. + #[arg(long)] + amount: U256, + + /// Limit period such as 7d, 24h, or 3600s. + /// + /// The current AccountKeychain update entrypoint cannot change periods, so non-zero + /// values are rejected. + #[arg(long, value_parser = parse_period)] + period: Option, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Remove all allowed-call rules for a target contract. + RemoveTarget { + /// The key address to update. + key_address: Address, + + /// Target contract address to remove. + #[arg(long)] + target: Address, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, } +#[derive(Debug, Clone, Copy)] +pub struct SelectorArg([u8; 4]); + fn parse_signature_type(s: &str) -> Result { match s.to_lowercase().as_str() { "secp256k1" => Ok(SignatureType::Secp256k1), @@ -203,6 +311,15 @@ const fn signature_type_name(t: &SignatureType) -> &'static str { } } +const fn signature_type_label(t: &SignatureType) -> &'static str { + match t { + SignatureType::Secp256k1 => "Secp256k1", + SignatureType::P256 => "P256", + SignatureType::WebAuthn => "WebAuthn", + _ => "unknown", + } +} + const fn key_type_name(t: &KeyType) -> &'static str { match t { KeyType::Secp256k1 => "secp256k1", @@ -211,6 +328,14 @@ const fn key_type_name(t: &KeyType) -> &'static str { } } +const fn key_type_label(t: &KeyType) -> &'static str { + match t { + KeyType::Secp256k1 => "Secp256k1", + KeyType::P256 => "P256", + KeyType::WebAuthn => "WebAuthn", + } +} + const fn wallet_type_name(t: &WalletType) -> &'static str { match t { WalletType::Local => "local", @@ -332,6 +457,48 @@ fn parse_selector_bytes(s: &str) -> Result<[u8; 4], String> { } } +fn parse_selector_arg(s: &str) -> Result { + parse_selector_bytes(s).map(SelectorArg) +} + +fn parse_policy_token(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "pathusd" | "path_usd" | "path-usd" | "usd" => Ok(PATH_USD_ADDRESS), + _ => foundry_cli::utils::parse_fee_token_address(s).map_err(|e| e.to_string()), + } +} + +fn parse_period(s: &str) -> Result { + let s = s.trim(); + if s.is_empty() { + return Err("period cannot be empty".to_string()); + } + + let split = s.find(|c: char| !c.is_ascii_digit()).unwrap_or(s.len()); + if split == 0 { + return Err(format!( + "invalid period '{s}': expected a number followed by s, m, h, d, or w" + )); + } + + let value: u64 = + s[..split].parse().map_err(|e| format!("invalid period value '{}': {e}", &s[..split]))?; + let multiplier = match &s[split..].to_ascii_lowercase()[..] { + "" | "s" => 1, + "m" => 60, + "h" => 60 * 60, + "d" => 24 * 60 * 60, + "w" => 7 * 24 * 60 * 60, + unit => { + return Err(format!( + "invalid period unit '{unit}' in '{s}' (expected s, m, h, d, or w)" + )); + } + }; + + value.checked_mul(multiplier).ok_or_else(|| format!("period '{s}' is too large")) +} + /// Represents a single scope entry in JSON format for `--scopes`. #[derive(serde::Deserialize)] struct JsonCallScope { @@ -402,6 +569,9 @@ impl KeychainSubcommand { Self::Check { wallet_address, key_address, rpc } => { run_check(wallet_address, key_address, rpc).await } + Self::Inspect { key_address, root_account, rpc } => { + run_inspect(key_address, root_account, rpc).await + } Self::Authorize { key_address, key_type, @@ -443,6 +613,40 @@ impl KeychainSubcommand { Self::RemoveScope { key_address, target, tx, send_tx } => { run_remove_scope(key_address, target, tx, send_tx).await } + Self::Policy { command } => command.run().await, + } + } +} + +impl KeychainPolicySubcommand { + pub async fn run(self) -> Result<()> { + match self { + Self::AddCall { + key_address, + root_account, + target, + selector, + recipients, + tx, + send_tx, + } => { + run_policy_add_call( + key_address, + root_account, + target, + selector.0, + recipients, + tx, + send_tx, + ) + .await + } + Self::SetLimit { key_address, token, amount, period, tx, send_tx } => { + run_policy_set_limit(key_address, token, amount, period, tx, send_tx).await + } + Self::RemoveTarget { key_address, target, tx, send_tx } => { + run_remove_scope(key_address, target, tx, send_tx).await + } } } } @@ -500,6 +704,143 @@ fn run_show(wallet_address: Address) -> Result<()> { Ok(()) } +#[derive(Debug, Clone)] +struct LocalLimitMetadata { + token: Address, + amount: String, +} + +#[derive(Debug, Clone)] +struct KeyMetadata { + root_account: Address, + key_type: Option, + limits: Vec, +} + +#[derive(Debug, Clone)] +struct InspectedLimit { + token: Address, + configured_amount: Option, + remaining: U256, + period_end: Option, +} + +#[derive(Debug, Clone)] +enum AllowedCallsView { + Unsupported, + Unrestricted, + Scoped(Vec), +} + +/// `cast keychain inspect ` — inspect on-chain key policy. +async fn run_inspect( + key_address: Address, + root_account: Option
, + rpc: RpcOpts, +) -> Result<()> { + let metadata = resolve_key_metadata(key_address, root_account)?; + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + let info: KeyInfo = provider.get_keychain_key(metadata.root_account, key_address).await?; + let provisioned = info.keyId != Address::ZERO; + let is_t3 = is_tempo_hardfork_active(&provider, TempoHardfork::T3).await?; + + let mut limits = Vec::new(); + if info.enforceLimits { + for local_limit in &metadata.limits { + let (remaining, period_end) = if is_t3 { + let limit = provider + .get_keychain_remaining_limit_with_period( + metadata.root_account, + key_address, + local_limit.token, + ) + .await?; + (limit.remaining, Some(limit.periodEnd)) + } else { + let remaining = provider + .account_keychain() + .getRemainingLimit(metadata.root_account, key_address, local_limit.token) + .call() + .await?; + (remaining, None) + }; + + limits.push(InspectedLimit { + token: local_limit.token, + configured_amount: Some(local_limit.amount.clone()), + remaining, + period_end, + }); + } + } + + let allowed_calls = if is_t3 { + let allowed = provider + .account_keychain() + .getAllowedCalls(metadata.root_account, key_address) + .call() + .await?; + if allowed.isScoped { + AllowedCallsView::Scoped(allowed.scopes) + } else { + AllowedCallsView::Unrestricted + } + } else { + AllowedCallsView::Unsupported + }; + + if shell::is_json() { + let key_type = if provisioned { + signature_type_name(&info.signatureType).to_string() + } else { + metadata + .key_type + .map(|key_type| key_type_name(&key_type).to_string()) + .unwrap_or_else(|| "unknown".to_string()) + }; + let json = serde_json::json!({ + "root_account": metadata.root_account.to_string(), + "key_id": key_address.to_string(), + "provisioned": provisioned, + "type": key_type, + "expiry": provisioned.then_some(info.expiry), + "expiry_human": provisioned.then(|| format_expiry_for_inspect(info.expiry)), + "enforce_limits": info.enforceLimits, + "is_revoked": info.isRevoked, + "limits": limits.iter().map(inspected_limit_to_json).collect::>(), + "allowed_calls": allowed_calls_to_json(&allowed_calls), + }); + sh_println!("{}", serde_json::to_string_pretty(&json)?)?; + return Ok(()); + } + + let key_type = if provisioned { + signature_type_label(&info.signatureType) + } else { + metadata.key_type.map(|key_type| key_type_label(&key_type)).unwrap_or("unknown") + }; + + sh_println!("Root account: {}", metadata.root_account)?; + sh_println!("Key id: {key_address}")?; + sh_println!("Type: {key_type}")?; + + if info.isRevoked { + sh_println!("Status: revoked")?; + } else if !provisioned { + sh_println!("Status: not provisioned")?; + } else { + sh_println!("Status: active")?; + sh_println!("Expiry: {}", format_expiry_for_inspect(info.expiry))?; + } + + print_inspected_limits(info.enforceLimits, &limits)?; + print_allowed_calls(&allowed_calls)?; + + Ok(()) +} + /// `cast keychain check` / `cast keychain info` — query on-chain key status. async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) -> Result<()> { let config = rpc.load_config()?; @@ -584,7 +925,7 @@ async fn run_authorize( let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; - let calldata = if provider.is_hardfork_active(TempoHardfork::T3).await? { + let calldata = if is_tempo_hardfork_active(&provider, TempoHardfork::T3).await? { // T3+ authorizeKey(address,SignatureType,KeyRestrictions) let restrictions = KeyRestrictions { expiry, @@ -634,7 +975,7 @@ async fn run_remaining_limit( let config = rpc.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; - let remaining: U256 = if provider.is_hardfork_active(TempoHardfork::T3).await? { + let remaining: U256 = if is_tempo_hardfork_active(&provider, TempoHardfork::T3).await? { provider.get_keychain_remaining_limit(wallet_address, key_address, token).await? } else { // Pre-T3: use the legacy getRemainingLimit(address,address,address) @@ -646,7 +987,7 @@ async fn run_remaining_limit( }; if shell::is_json() { - sh_println!("{}", serde_json::to_string(&remaining.to_string())?)?; + sh_println!("{}", serde_json::json!({ "remaining": remaining.to_string() }))?; } else { sh_println!("{remaining}")?; } @@ -695,6 +1036,88 @@ async fn run_remove_scope( send_keychain_tx(calldata, tx_opts, &send_tx).await } +/// `cast keychain policy add-call` — merge a selector rule into a target scope. +async fn run_policy_add_call( + key_address: Address, + root_account: Option
, + target: Address, + selector: [u8; 4], + recipients: Vec
, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + let metadata = resolve_key_metadata(key_address, root_account)?; + let config = send_tx.eth.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + if !is_tempo_hardfork_active(&provider, TempoHardfork::T3).await? { + eyre::bail!("allowed-call policy editing requires the Tempo T3 hardfork"); + } + + let allowed = provider + .account_keychain() + .getAllowedCalls(metadata.root_account, key_address) + .call() + .await?; + + let new_rule = SelectorRule { selector: selector.into(), recipients }; + let existing_target = allowed + .isScoped + .then(|| allowed.scopes.into_iter().find(|scope| scope.target == target)) + .flatten(); + + let (target_scope, changed) = match existing_target { + Some(mut scope) => { + if scope.selectorRules.is_empty() { + sh_warn!( + "Allowed calls for {} already allow any selector; leaving wildcard scope unchanged", + address_label_with_address(target) + )?; + } + let changed = add_selector_rule_to_scope(&mut scope, new_rule); + (scope, changed) + } + None => (CallScope { target, selectorRules: vec![new_rule] }, true), + }; + + if !changed { + if shell::is_json() { + sh_println!( + "{}", + serde_json::json!({ "status": "already_present", "target": target.to_string() }) + )?; + } else { + sh_println!("Allowed call already present for {}", address_label_with_address(target))?; + } + return Ok(()); + } + + let calldata = + IAccountKeychain::setAllowedCallsCall { keyId: key_address, scopes: vec![target_scope] } + .abi_encode(); + send_keychain_tx(calldata, tx_opts, &send_tx).await +} + +/// `cast keychain policy set-limit` — update a spending limit amount. +async fn run_policy_set_limit( + key_address: Address, + token: Address, + amount: U256, + period: Option, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + if period.is_some_and(|period| period != 0) { + eyre::bail!( + "--period is not supported by the current AccountKeychain updateSpendingLimit \ + precompile; periods can only be set when authorizing a key" + ); + } + + // updateSpendingLimit authorizes against msg.sender; the root account is not part of calldata. + run_update_limit(key_address, token, amount, tx_opts, send_tx).await +} + /// Shared helper to send a keychain precompile transaction. async fn send_keychain_tx( calldata: Vec, @@ -702,16 +1125,22 @@ async fn send_keychain_tx( send_tx: &SendTxOpts, ) -> Result<()> { let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; + let print_sponsor_hash = tx_opts.tempo.print_sponsor_hash; + let tempo_sponsor = + if print_sponsor_hash { None } else { tx_opts.tempo.sponsor_config().await? }; let config = send_tx.eth.load_config()?; let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); let provider = ProviderBuilder::::from_config(&config)?.build()?; - // Inject key_id for correct gas estimation with keychain signature overhead. - if let Some(ref ak) = tempo_access_key { - tx_opts.tempo.key_id = Some(ak.key_address); + if let Some(interval) = send_tx.poll_interval { + provider.client().set_poll_interval(Duration::from_secs(interval)); } + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx_opts.tempo.nonce_key` from the lane. + let resolved_lane = resolve_lane(&mut tx_opts.tempo, &config.root)?; + let builder = CastTxBuilder::new(&provider, tx_opts, &config) .await? .with_to(Some(NameOrAddress::Address(ACCOUNT_KEYCHAIN_ADDRESS))) @@ -719,27 +1148,70 @@ async fn send_keychain_tx( .with_code_sig_and_args(None, Some(hex::encode_prefixed(&calldata)), vec![]) .await?; - if let Some(ref ak) = tempo_access_key { - let signer = - signer.as_ref().ok_or_else(|| eyre::eyre!("signer required for access key"))?; - let (tx, _) = builder.build(ak.wallet_address).await?; - cast_send_with_access_key( - &provider, - tx, - signer, - ak, - send_tx.cast_async, - send_tx.confirmations, - timeout, - ) - .await?; + // Keychain management calls are authorized by the root account. Access keys can use their + // permissions, but cannot mutate their own key policy. + let browser = send_tx.browser.run::().await?; + + if print_sponsor_hash { + let from = if let Some(ref browser) = browser { + browser.address() + } else { + signer + .as_ref() + .ok_or_else(|| { + eyre::eyre!( + "--tempo.print-sponsor-hash requires a root account signer, such as \ + --browser, --private-key, or --keystore" + ) + })? + .address() + }; + + let (tx, _) = builder.build(from).await?; + let hash = tx + .compute_sponsor_hash(from) + .ok_or_else(|| eyre::eyre!("This network does not support sponsored transactions"))?; + if shell::is_json() { + sh_println!("{}", serde_json::json!({ "sponsor_hash": format!("{hash:?}") }))?; + } else { + sh_println!("{hash:?}")?; + } + return Ok(()); + } + + if let Some(browser) = browser { + let chain = builder.chain(); + let (mut tx, _) = builder.build(browser.address()).await?; + if chain.is_tempo() + && let Some(gas) = tx.gas_limit() + { + tx.set_gas_limit(gas + TEMPO_BROWSER_GAS_BUFFER); + } + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, browser.address()).await?; + } + + let tx_hash = browser.send_transaction_via_browser(tx).await?; + CastTxSender::new(&provider) + .print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout) + .await?; + } else if tempo_access_key.is_some() { + eyre::bail!( + "keychain policy changes must be signed by the root account; the selected `--from` \ + resolved to a Tempo access key. Use `--browser` for passkey roots, or pass a root \ + account signer with `--private-key`, `--keystore`, Ledger, Trezor, AWS, GCP, or Turnkey." + ); } else { let signer = match signer { Some(s) => s, None => send_tx.eth.wallet.signer().await?, }; let from = signer.address(); - let (tx, _) = builder.build(from).await?; + let (mut tx, _) = builder.build(from).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, from).await?; + } let wallet = EthereumWallet::from(signer); let provider = AlloyProviderBuilder::<_, _, TempoNetwork>::default() @@ -753,6 +1225,361 @@ async fn send_keychain_tx( Ok(()) } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct AnvilNodeInfo { + hard_fork: Option, + network: Option, +} + +async fn is_tempo_hardfork_active

(provider: &P, hardfork: TempoHardfork) -> Result +where + P: Provider, +{ + match provider.is_hardfork_active(hardfork).await { + Ok(active) => Ok(active), + Err(err) if is_rpc_method_not_found(&err) => { + match anvil_tempo_hardfork_active(provider, hardfork).await { + Ok(Some(active)) => Ok(active), + _ => Err(err.into()), + } + } + Err(err) => Err(err.into()), + } +} + +async fn anvil_tempo_hardfork_active

( + provider: &P, + hardfork: TempoHardfork, +) -> Result, TransportError> +where + P: Provider, +{ + let info = provider.raw_request::<_, AnvilNodeInfo>("anvil_nodeInfo".into(), ()).await?; + Ok(active_from_anvil_node_info(&info, hardfork)) +} + +fn active_from_anvil_node_info(info: &AnvilNodeInfo, hardfork: TempoHardfork) -> Option { + (info.network.as_deref() == Some("tempo")).then(|| { + info.hard_fork + .as_deref() + .and_then(|active_hardfork| active_hardfork.parse::().ok()) + .is_some_and(|active_hardfork| active_hardfork >= hardfork) + }) +} + +fn is_rpc_method_not_found(err: &TransportError) -> bool { + err.as_error_resp().is_some_and(|payload| payload.code == -32601) +} + +fn resolve_key_metadata( + key_address: Address, + root_account: Option

, +) -> Result { + let keys_file = read_tempo_keys_file(); + + if let Some(root_account) = root_account { + if let Some(keys_file) = keys_file.as_ref() + && let Some(entry) = keys_file.keys.iter().find(|entry| { + entry.wallet_address == root_account + && key_entry_effective_key(entry) == key_address + }) + { + return Ok(key_metadata_from_entry(entry)); + } + + return Ok(KeyMetadata { root_account, key_type: None, limits: Vec::new() }); + } + + let Some(keys_file) = keys_file.as_ref() else { + eyre::bail!( + "key {key_address} was not found because the local keys file could not be read at {}; pass --root-account", + tempo_keys_path_display() + ); + }; + + let matches: Vec<_> = keys_file + .keys + .iter() + .filter(|entry| key_entry_effective_key(entry) == key_address) + .collect(); + + if matches.is_empty() { + eyre::bail!( + "key {key_address} was not found in {}; pass --root-account", + tempo_keys_path_display() + ); + } + + let root_account = matches[0].wallet_address; + if matches.iter().any(|entry| entry.wallet_address != root_account) { + eyre::bail!( + "key {key_address} matches multiple root accounts in {}; pass --root-account", + tempo_keys_path_display() + ); + } + + let entry = + matches.iter().copied().find(|entry| !entry.limits.is_empty()).unwrap_or(matches[0]); + Ok(key_metadata_from_entry(entry)) +} + +fn key_entry_effective_key(entry: &tempo::KeyEntry) -> Address { + entry.key_address.unwrap_or(entry.wallet_address) +} + +fn key_metadata_from_entry(entry: &tempo::KeyEntry) -> KeyMetadata { + KeyMetadata { + root_account: entry.wallet_address, + key_type: Some(entry.key_type), + limits: entry + .limits + .iter() + .map(|limit| LocalLimitMetadata { token: limit.currency, amount: limit.limit.clone() }) + .collect(), + } +} + +fn tempo_keys_path_display() -> String { + tempo_keys_path() + .map(|path| path.display().to_string()) + .unwrap_or_else(|| "(unknown)".to_string()) +} + +fn add_selector_rule_to_scope(scope: &mut CallScope, rule: SelectorRule) -> bool { + if scope.selectorRules.is_empty() { + return false; + } + + let Some(existing_rule) = + scope.selectorRules.iter_mut().find(|existing| existing.selector == rule.selector) + else { + scope.selectorRules.push(rule); + return true; + }; + + if existing_rule.recipients.is_empty() { + return false; + } + + if rule.recipients.is_empty() { + existing_rule.recipients = Vec::new(); + return true; + } + + let mut changed = false; + for recipient in rule.recipients { + if !existing_rule.recipients.contains(&recipient) { + existing_rule.recipients.push(recipient); + changed = true; + } + } + changed +} + +fn inspected_limit_to_json(limit: &InspectedLimit) -> serde_json::Value { + serde_json::json!({ + "token": limit.token.to_string(), + "token_label": address_label(limit.token), + "configured_amount": limit.configured_amount.as_deref(), + "remaining": limit.remaining.to_string(), + "period_end": limit.period_end, + "period_end_human": limit.period_end.and_then(|period_end| { + (period_end != 0).then(|| format_period_end(period_end)) + }), + }) +} + +fn allowed_calls_to_json(allowed_calls: &AllowedCallsView) -> serde_json::Value { + match allowed_calls { + AllowedCallsView::Unsupported => serde_json::json!({ + "mode": "unsupported", + "scopes": [], + }), + AllowedCallsView::Unrestricted => serde_json::json!({ + "mode": "any", + "scopes": [], + }), + AllowedCallsView::Scoped(scopes) => serde_json::json!({ + "mode": if scopes.is_empty() { "none" } else { "scoped" }, + "scopes": scopes.iter().map(call_scope_to_json).collect::>(), + }), + } +} + +fn call_scope_to_json(scope: &CallScope) -> serde_json::Value { + serde_json::json!({ + "target": scope.target.to_string(), + "target_label": address_label(scope.target), + "selectors": scope.selectorRules.iter().map(selector_rule_to_json).collect::>(), + }) +} + +fn selector_rule_to_json(rule: &SelectorRule) -> serde_json::Value { + serde_json::json!({ + "selector": selector_hex(&rule.selector.0), + "signature": selector_signature(&rule.selector.0), + "recipients": rule.recipients.iter().map(ToString::to_string).collect::>(), + }) +} + +fn print_inspected_limits(enforce_limits: bool, limits: &[InspectedLimit]) -> Result<()> { + if !enforce_limits { + sh_println!("Limits: none")?; + return Ok(()); + } + + sh_println!("Limits:")?; + if limits.is_empty() { + sh_println!(" enforced, but no local limit metadata was found")?; + return Ok(()); + } + + for limit in limits { + let configured = limit.configured_amount.as_deref().unwrap_or("unknown"); + let period = limit + .period_end + .and_then(|period_end| { + (period_end != 0).then(|| format!(" ({})", format_period_end(period_end))) + }) + .unwrap_or_default(); + sh_println!( + " {}: {} / {} remaining{}", + address_label(limit.token), + limit.remaining, + configured, + period + )?; + } + + Ok(()) +} + +fn print_allowed_calls(allowed_calls: &AllowedCallsView) -> Result<()> { + match allowed_calls { + AllowedCallsView::Unsupported => sh_println!("Allowed calls: unsupported before T3")?, + AllowedCallsView::Unrestricted => sh_println!("Allowed calls: any")?, + AllowedCallsView::Scoped(scopes) if scopes.is_empty() => { + sh_println!("Allowed calls: none")?; + } + AllowedCallsView::Scoped(scopes) => { + sh_println!("Allowed calls:")?; + for scope in scopes { + sh_println!(" {}:", address_label_with_address(scope.target))?; + if scope.selectorRules.is_empty() { + sh_println!(" any selector")?; + continue; + } + + for rule in &scope.selectorRules { + sh_println!( + " {} -> {}", + format_selector(&rule.selector.0), + format_recipients(&rule.recipients) + )?; + } + } + } + } + + Ok(()) +} + +fn address_label(address: Address) -> String { + if address == PATH_USD_ADDRESS { "PathUSD".to_string() } else { address.to_string() } +} + +fn address_label_with_address(address: Address) -> String { + if address == PATH_USD_ADDRESS { format!("PathUSD ({address})") } else { address.to_string() } +} + +fn format_selector(selector: &[u8; 4]) -> String { + selector_signature(selector).map(str::to_string).unwrap_or_else(|| selector_hex(selector)) +} + +fn selector_signature(selector: &[u8; 4]) -> Option<&'static str> { + if selector == &ITIP20::transferCall::SELECTOR { + Some("transfer(address,uint256)") + } else if selector == &ITIP20::approveCall::SELECTOR { + Some("approve(address,uint256)") + } else if selector == &ITIP20::transferFromCall::SELECTOR { + Some("transferFrom(address,address,uint256)") + } else if selector == &ITIP20::transferWithMemoCall::SELECTOR { + Some("transferWithMemo(address,uint256,bytes32)") + } else if selector == &ITIP20::transferFromWithMemoCall::SELECTOR { + Some("transferFromWithMemo(address,address,uint256,bytes32)") + } else if selector == &ITIP20::mintCall::SELECTOR { + Some("mint(address,uint256)") + } else if selector == &ITIP20::burnCall::SELECTOR { + Some("burn(uint256)") + } else { + None + } +} + +fn selector_hex(selector: &[u8; 4]) -> String { + hex::encode_prefixed(selector) +} + +fn format_recipients(recipients: &[Address]) -> String { + if recipients.is_empty() { + return "any recipient".to_string(); + } + + let recipients = recipients.iter().map(ToString::to_string).collect::>().join(", "); + format!("recipients [{recipients}]") +} + +fn format_expiry_for_inspect(expiry: u64) -> String { + if expiry == u64::MAX { + return "never".to_string(); + } + + format!("{} ({})", format_timestamp_iso(expiry), format_relative_timestamp(expiry)) +} + +fn format_period_end(period_end: u64) -> String { + format!("period resets {}", format_relative_timestamp(period_end)) +} + +fn format_timestamp_iso(timestamp: u64) -> String { + DateTime::from_timestamp(timestamp as i64, 0) + .map(|dt| dt.format("%Y-%m-%dT%H:%M:%SZ").to_string()) + .unwrap_or_else(|| timestamp.to_string()) +} + +fn format_relative_timestamp(timestamp: u64) -> String { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + if timestamp == now { + "now".to_string() + } else if timestamp > now { + format!("in {}", format_duration_words(timestamp - now)) + } else { + format!("{} ago", format_duration_words(now - timestamp)) + } +} + +fn format_duration_words(seconds: u64) -> String { + const MINUTE: u64 = 60; + const HOUR: u64 = 60 * MINUTE; + const DAY: u64 = 24 * HOUR; + + if seconds >= DAY { + let days = seconds / DAY; + if days == 1 { "1 day".to_string() } else { format!("{days} days") } + } else if seconds >= HOUR { + format!("{}h", seconds / HOUR) + } else if seconds >= MINUTE { + format!("{}m", seconds / MINUTE) + } else { + format!("{seconds}s") + } +} + fn format_expiry(expiry: u64) -> String { if expiry == u64::MAX { return "never".to_string(); @@ -842,6 +1669,7 @@ fn key_entry_to_json(entry: &tempo::KeyEntry) -> serde_json::Value { #[cfg(test)] mod tests { use super::*; + use alloy_json_rpc::ErrorPayload; use std::str::FromStr; #[test] @@ -967,4 +1795,144 @@ mod tests { let json = r#"[{"target":"0x20c0000000000000000000000000000000000001","selectors":[{"selector":"transfer","recipients":[],"bogus":true}]}]"#; assert!(parse_scopes_json(json).is_err()); } + + #[test] + fn test_parse_policy_token_path_usd() { + assert_eq!(parse_policy_token("PathUSD").unwrap(), PATH_USD_ADDRESS); + assert_eq!(parse_policy_token("path-usd").unwrap(), PATH_USD_ADDRESS); + } + + #[test] + fn test_parse_period_units() { + assert_eq!(parse_period("0").unwrap(), 0); + assert_eq!(parse_period("30s").unwrap(), 30); + assert_eq!(parse_period("5m").unwrap(), 300); + assert_eq!(parse_period("2h").unwrap(), 7200); + assert_eq!(parse_period("7d").unwrap(), 604800); + assert_eq!(parse_period("2w").unwrap(), 1209600); + assert!(parse_period("1mo").is_err()); + } + + #[test] + fn test_add_selector_rule_merges_recipients() { + let first = Address::from_str("0x1111111111111111111111111111111111111111").unwrap(); + let second = Address::from_str("0x2222222222222222222222222222222222222222").unwrap(); + let mut scope = CallScope { + target: PATH_USD_ADDRESS, + selectorRules: vec![SelectorRule { + selector: parse_selector_bytes("transfer").unwrap().into(), + recipients: vec![first], + }], + }; + + let changed = add_selector_rule_to_scope( + &mut scope, + SelectorRule { + selector: parse_selector_bytes("transfer").unwrap().into(), + recipients: vec![second], + }, + ); + + assert!(changed); + assert_eq!(scope.selectorRules.len(), 1); + assert_eq!(scope.selectorRules[0].recipients, vec![first, second]); + } + + #[test] + fn test_add_selector_rule_empty_recipients_widens_to_any() { + let first = Address::from_str("0x1111111111111111111111111111111111111111").unwrap(); + let mut scope = CallScope { + target: PATH_USD_ADDRESS, + selectorRules: vec![SelectorRule { + selector: parse_selector_bytes("approve").unwrap().into(), + recipients: vec![first], + }], + }; + + let changed = add_selector_rule_to_scope( + &mut scope, + SelectorRule { + selector: parse_selector_bytes("approve").unwrap().into(), + recipients: vec![], + }, + ); + + assert!(changed); + assert!(scope.selectorRules[0].recipients.is_empty()); + } + + #[test] + fn test_add_selector_rule_target_wildcard_is_unchanged() { + let mut scope = CallScope { target: PATH_USD_ADDRESS, selectorRules: vec![] }; + + let changed = add_selector_rule_to_scope( + &mut scope, + SelectorRule { + selector: parse_selector_bytes("transfer").unwrap().into(), + recipients: vec![], + }, + ); + + assert!(!changed); + assert!(scope.selectorRules.is_empty()); + } + + #[test] + fn test_policy_set_limit_parses() { + let key = "0x1111111111111111111111111111111111111111"; + + let command = KeychainSubcommand::try_parse_from([ + "keychain", + "policy", + "set-limit", + key, + "--token", + "PathUSD", + "--amount", + "123", + ]) + .unwrap(); + + match command { + KeychainSubcommand::Policy { + command: + KeychainPolicySubcommand::SetLimit { key_address, token, amount, period, .. }, + } => { + assert_eq!(key_address, Address::from_str(key).unwrap()); + assert_eq!(token, PATH_USD_ADDRESS); + assert_eq!(amount, U256::from(123)); + assert_eq!(period, None); + } + other => panic!("unexpected command: {other:?}"), + } + } + + #[test] + fn test_active_from_anvil_node_info_requires_tempo_network() { + let tempo_t3 = + AnvilNodeInfo { network: Some("tempo".to_string()), hard_fork: Some("T3".to_string()) }; + assert_eq!(active_from_anvil_node_info(&tempo_t3, TempoHardfork::T2), Some(true)); + assert_eq!(active_from_anvil_node_info(&tempo_t3, TempoHardfork::T3), Some(true)); + assert_eq!(active_from_anvil_node_info(&tempo_t3, TempoHardfork::T4), Some(false)); + + let ethereum_t3 = AnvilNodeInfo { + network: Some("ethereum".to_string()), + hard_fork: Some("T3".to_string()), + }; + assert_eq!(active_from_anvil_node_info(ðereum_t3, TempoHardfork::T3), None); + } + + #[test] + fn test_rpc_method_not_found_detection() { + let method_missing: TransportError = + TransportError::ErrorResp(ErrorPayload::method_not_found()); + assert!(is_rpc_method_not_found(&method_missing)); + + let internal_error: TransportError = + TransportError::ErrorResp(ErrorPayload::internal_error()); + assert!(!is_rpc_method_not_found(&internal_error)); + + let transport_error = alloy_transport::TransportErrorKind::backend_gone(); + assert!(!is_rpc_method_not_found(&transport_error)); + } } diff --git a/crates/cast/src/cmd/mktx.rs b/crates/cast/src/cmd/mktx.rs index 8aaf6e97a1827..67178cd093d7b 100644 --- a/crates/cast/src/cmd/mktx.rs +++ b/crates/cast/src/cmd/mktx.rs @@ -2,7 +2,9 @@ use crate::tx::{self, CastTxBuilder}; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::Encodable2718; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network, NetworkTransactionBuilder}; +use alloy_network::{ + Ethereum, EthereumWallet, Network, NetworkTransactionBuilder, TransactionBuilder, +}; use alloy_primitives::{Address, hex}; use alloy_provider::Provider; use alloy_signer::{Signature, Signer}; @@ -10,7 +12,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::LoadConfig, + utils::{LoadConfig, maybe_print_resolved_lane, resolve_lane}, }; use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; use std::{path::PathBuf, str::FromStr}; @@ -94,9 +96,13 @@ impl MakeTxArgs { N::UnsignedTx: SignableTransaction, N::TransactionRequest: FoundryTransactionBuilder, { - let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self; + let Self { to, mut sig, mut args, command, mut tx, path, eth, raw_unsigned, ethsign } = + self; let print_sponsor_hash = tx.tempo.print_sponsor_hash; + let expires_at = tx.tempo.resolve_expires(); + let tempo_sponsor = + if print_sponsor_hash { None } else { tx.tempo.sponsor_config().await? }; let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; @@ -117,6 +123,11 @@ impl MakeTxArgs { let provider = ProviderBuilder::::from_config(&config)?.build()?; + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx.tempo.nonce_key` from the lane. + // Must happen before `tx.clone()` so the cloned tx carries the resolved nonce_key. + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config) .await? .with_to(to) @@ -139,6 +150,10 @@ impl MakeTxArgs { return Ok(()); } + if let Some(ts) = expires_at { + sh_println!("Transaction expires at unix timestamp {ts}")?; + } + if raw_unsigned { // Build unsigned raw tx // Check if nonce is provided when --from is not specified @@ -148,11 +163,20 @@ impl MakeTxArgs { "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce" ); } + if tempo_sponsor.is_some() && eth.wallet.from.is_none() { + eyre::bail!( + "--tempo.sponsor requires --from for --raw-unsigned because the sponsor digest commits to the sender" + ); + } // Use zero address as placeholder for unsigned transactions let from = eth.wallet.from.unwrap_or(Address::ZERO); - let (tx, _) = tx_builder.build(from).await?; + let (mut tx, _) = tx_builder.build(from).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, from).await?; + } let raw_tx = hex::encode_prefixed(tx.build_unsigned()?.encoded_for_signing()); sh_println!("{raw_tx}")?; @@ -162,7 +186,11 @@ impl MakeTxArgs { if ethsign { // Use "eth_signTransaction" to sign the transaction only works if the node/RPC has // unlocked accounts. - let (tx, _) = tx_builder.build(config.sender).await?; + let (mut tx, _) = tx_builder.build(config.sender).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, config.sender).await?; + } let signed_tx = provider.sign_transaction(tx).await?; sh_println!("{signed_tx}")?; @@ -176,7 +204,11 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; - let (tx, _) = tx_builder.build(&signer).await?; + let (mut tx, _) = tx_builder.build(&signer).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, from).await?; + } let tx = tx.build(&EthereumWallet::new(signer)).await?; diff --git a/crates/cast/src/cmd/mod.rs b/crates/cast/src/cmd/mod.rs index 6a9d11f5dc61d..0b1b26615694a 100644 --- a/crates/cast/src/cmd/mod.rs +++ b/crates/cast/src/cmd/mod.rs @@ -15,6 +15,7 @@ pub mod call; pub mod constructor_args; pub mod create2; pub mod creation_code; +#[cfg(feature = "optimism")] pub mod da_estimate; pub mod erc20; pub mod estimate; @@ -28,7 +29,9 @@ pub mod rpc; pub mod run; pub mod send; pub mod storage; +pub mod tempo; pub mod tip20; pub mod trace; pub mod txpool; +pub mod vaddr; pub mod wallet; diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 84699d6cd6956..7e52a9e265f25 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -29,10 +29,12 @@ use foundry_config::{ value::{Dict, Map}, }, }; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ core::{ FoundryBlock as _, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, + evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork, TxEnvFor}, }, executors::{EvmError, Executor, TracingExecutor}, hardforks::FoundryHardfork, @@ -123,12 +125,15 @@ impl RunArgs { evm_opts.infer_network_from_fork().await; if evm_opts.networks.is_tempo() { - self.run_with_evm::().await - } else if evm_opts.networks.is_optimism() { - self.run_with_evm::().await - } else { - self.run_with_evm::().await + return self.run_with_evm::().await; + } + + #[cfg(feature = "optimism")] + if evm_opts.networks.is_optimism() { + return self.run_with_evm::().await; } + + self.run_with_evm::().await } async fn run_with_evm(self) -> Result<()> { diff --git a/crates/cast/src/cmd/send.rs b/crates/cast/src/cmd/send.rs index 2d6e248cc7a73..421aae1f2153e 100644 --- a/crates/cast/src/cmd/send.rs +++ b/crates/cast/src/cmd/send.rs @@ -8,7 +8,10 @@ use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::{Signature, Signer}; use clap::Parser; use eyre::{Result, eyre}; -use foundry_cli::{opts::TransactionOpts, utils::LoadConfig}; +use foundry_cli::{ + opts::TransactionOpts, + utils::{LoadConfig, maybe_print_resolved_lane, resolve_lane}, +}; use foundry_common::{ FoundryTransactionBuilder, fmt::{UIfmt, UIfmtReceiptExt}, @@ -119,7 +122,9 @@ impl SendTxArgs { self; let print_sponsor_hash = tx.tempo.print_sponsor_hash; - let sponsor_signature = tx.tempo.sponsor_signature; + let expires_at = tx.tempo.resolve_expires(); + let tempo_sponsor = + if print_sponsor_hash { None } else { tx.tempo.sponsor_config().await? }; let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; @@ -183,6 +188,8 @@ impl SendTxArgs { let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + if let Some(interval) = send_tx.poll_interval { provider.client().set_poll_interval(Duration::from_secs(interval)) } @@ -202,13 +209,19 @@ impl SendTxArgs { // If --tempo.print-sponsor-hash was passed, build the tx, print the hash, and exit. if print_sponsor_hash { - // Use the pre-resolved signer to derive the actual sender address, since the - // sponsor hash commits to the sender. - let signer = pre_resolved_signer.as_ref().ok_or_else(|| { - eyre!("--tempo.print-sponsor-hash requires a signer (e.g. --private-key)") - })?; - let from = signer.address(); - let (tx, _) = builder.build(from).await?; + let (tx, from) = if let Some(ref ak) = access_key { + let (tx, _) = builder.build_with_access_key(ak.wallet_address, ak).await?; + (tx, ak.wallet_address) + } else { + // Use the pre-resolved signer to derive the actual sender address, since the + // sponsor hash commits to the sender. + let signer = pre_resolved_signer.as_ref().ok_or_else(|| { + eyre!("--tempo.print-sponsor-hash requires a signer (e.g. --private-key)") + })?; + let from = signer.address(); + let (tx, _) = builder.build(from).await?; + (tx, from) + }; let hash = tx .compute_sponsor_hash(from) .ok_or_else(|| eyre!("This network does not support sponsored transactions"))?; @@ -216,6 +229,10 @@ impl SendTxArgs { return Ok(()); } + if let Some(ts) = expires_at { + sh_println!("Transaction expires at unix timestamp {ts}")?; + } + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); // Launch browser signer if `--browser` flag is set @@ -245,11 +262,18 @@ impl SendTxArgs { } } - let (tx, _) = builder.build(config.sender).await?; + let (mut tx_request, _) = builder.build(config.sender).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, config.sender).await?; + } cast_send( provider, - tx, + tx_request, send_tx.cast_async, send_tx.sync, send_tx.confirmations, @@ -261,6 +285,10 @@ impl SendTxArgs { } else if let Some(browser) = browser { let chain = builder.chain(); let (mut tx_request, _) = builder.build(browser.address()).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; // Browser wallets may sign with P256/WebAuthn instead of secp256k1, which // costs more gas for signature verification on Tempo chains. Add a @@ -270,6 +298,9 @@ impl SendTxArgs { { tx_request.set_gas_limit(gas + TEMPO_BROWSER_GAS_BUFFER); } + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, browser.address()).await?; + } let tx_hash = browser.send_transaction_via_browser(tx_request).await?; @@ -283,7 +314,14 @@ impl SendTxArgs { Some(s) => s, None => send_tx.eth.wallet.signer().await?, }; - let (tx_request, _) = builder.build(ak.wallet_address).await?; + let (mut tx_request, _) = builder.build_with_access_key(ak.wallet_address, &ak).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, ak.wallet_address).await?; + } cast_send_with_access_key( &provider, tx_request, @@ -308,11 +346,13 @@ impl SendTxArgs { tx::validate_from_address(send_tx.eth.wallet.from, from)?; let (mut tx_request, _) = builder.build(&signer).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; - // Apply sponsor signature after gas estimation so the estimate is - // consistent with what `--tempo.print-sponsor-hash` computes. - if let Some(sig) = sponsor_signature { - tx_request.set_fee_payer_signature(sig); + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, from).await?; } let wallet = EthereumWallet::from(signer); diff --git a/crates/cast/src/cmd/tempo.rs b/crates/cast/src/cmd/tempo.rs new file mode 100644 index 0000000000000..6744e6b73c039 --- /dev/null +++ b/crates/cast/src/cmd/tempo.rs @@ -0,0 +1,45 @@ +use clap::Parser; +use eyre::Result; +use foundry_common::tempo::{EnsureAccessKeyConfig, ensure_access_key}; + +/// Tempo wallet integration commands. +#[derive(Debug, Parser)] +pub enum TempoSubcommand { + /// Authorize a new access key against your Tempo wallet via wallet.tempo. + /// + /// Persists the key to `$TEMPO_HOME/wallet/keys.toml` (default + /// `~/.tempo/wallet/keys.toml`). Also runs automatically on a 402 from a + /// Tempo RPC when no local key is configured. + /// + /// Env: `TEMPO_HOME`, `TEMPO_CLI_AUTH_URL` (override auth service). + Login { + /// Chain ID to authorize the key for. Defaults to Tempo mainnet (4217). + #[arg(long, default_value_t = 4217)] + chain_id: u64, + + /// Print the authorization URL to stderr instead of opening a browser. + #[arg(long)] + no_browser: bool, + }, +} + +impl TempoSubcommand { + pub async fn run(self) -> Result<()> { + match self { + Self::Login { chain_id, no_browser } => { + let mut cfg = EnsureAccessKeyConfig::from_env(chain_id); + if no_browser { + cfg.no_browser = true; + } + let outcome = ensure_access_key(cfg).await?; + let _ = foundry_common::sh_println!( + "Authorized key {} for wallet {} on chain {}", + outcome.key_address, + outcome.wallet_address, + outcome.chain_id, + ); + Ok(()) + } + } + } +} diff --git a/crates/cast/src/cmd/tip20/mine.rs b/crates/cast/src/cmd/tip20/mine.rs index a5f9062482a01..0367450a19b06 100644 --- a/crates/cast/src/cmd/tip20/mine.rs +++ b/crates/cast/src/cmd/tip20/mine.rs @@ -20,11 +20,11 @@ use tempo_primitives::{MasterId, TempoAddressExt, UserTag}; const POW_BYTES: usize = 4; -pub(super) struct Output { - pub(super) salt: B256, - pub(super) registration_hash: B256, - pub(super) master_id: MasterId, - pub(super) zero_tag_virtual_address: Address, +pub(crate) struct Output { + pub(crate) salt: B256, + pub(crate) registration_hash: B256, + pub(crate) master_id: MasterId, + pub(crate) zero_tag_virtual_address: Address, } pub(super) fn run( @@ -127,7 +127,12 @@ pub(super) async fn register( Ok(()) } -fn mine(master: Address, salt: B256, n_threads: usize, pow_bytes: usize) -> Result { +pub(crate) fn mine( + master: Address, + salt: B256, + n_threads: usize, + pow_bytes: usize, +) -> Result { let mut packed = [0u8; 52]; packed[..20].copy_from_slice(master.as_slice()); @@ -144,7 +149,7 @@ fn mine(master: Address, salt: B256, n_threads: usize, pow_bytes: usize) -> Resu .ok_or_else(|| eyre::eyre!("virtual master mining failed: all threads panicked")) } -fn derive(master: Address, salt: B256) -> Output { +pub(crate) fn derive(master: Address, salt: B256) -> Output { let registration_hash = registration_hash(master, salt); let master_id = MasterId::from_slice(®istration_hash[4..8]); let zero_tag_virtual_address = Address::new_virtual(master_id, UserTag::ZERO); @@ -152,14 +157,14 @@ fn derive(master: Address, salt: B256) -> Output { Output { salt, registration_hash, master_id, zero_tag_virtual_address } } -fn registration_hash(master: Address, salt: B256) -> B256 { +pub(crate) fn registration_hash(master: Address, salt: B256) -> B256 { let mut packed = [0u8; 52]; packed[..20].copy_from_slice(master.as_slice()); packed[20..].copy_from_slice(salt.as_slice()); keccak256(packed) } -fn has_pow(registration_hash: &B256, pow_bytes: usize) -> bool { +pub(crate) fn has_pow(registration_hash: &B256, pow_bytes: usize) -> bool { registration_hash[..pow_bytes].iter().all(|byte| *byte == 0) } diff --git a/crates/cast/src/cmd/tip20/mod.rs b/crates/cast/src/cmd/tip20/mod.rs index e3c39b2c9bb18..edb4c3b7a57b3 100644 --- a/crates/cast/src/cmd/tip20/mod.rs +++ b/crates/cast/src/cmd/tip20/mod.rs @@ -6,7 +6,7 @@ use std::str::FromStr; mod create; pub(crate) use create::iso4217_warning_message; -mod mine; +pub(crate) mod mine; /// TIP-20 token operations (Tempo). #[derive(Debug, Parser, Clone)] diff --git a/crates/cast/src/cmd/vaddr/create.rs b/crates/cast/src/cmd/vaddr/create.rs new file mode 100644 index 0000000000000..0563b09601323 --- /dev/null +++ b/crates/cast/src/cmd/vaddr/create.rs @@ -0,0 +1,181 @@ +use crate::{ + cmd::{ + erc20::build_provider_with_signer, + send::{cast_send, cast_send_with_access_key}, + tip20::mine, + }, + tx::{SendTxOpts, TxParams}, +}; +use alloy_primitives::{Address, B256}; +use alloy_signer::Signer; +use eyre::Result; +use foundry_cli::utils::{LoadConfig, get_chain}; +use foundry_common::{provider::ProviderBuilder, shell}; +use rand::{RngCore, SeedableRng, rngs::StdRng}; +use serde_json::json; +use std::time::Instant; +use tempo_alloy::{ + TempoNetwork, + contracts::precompiles::{ADDRESS_REGISTRY_ADDRESS, IAddressRegistry}, +}; +use tempo_primitives::{TempoAddressExt, UserTag}; + +const POW_BYTES: usize = 4; + +#[allow(clippy::too_many_arguments)] +pub(super) async fn run( + owner: Address, + salt: Option, + tag: u64, + count: u32, + threads: Option, + seed: Option, + no_random: bool, + no_register: bool, + send_tx: SendTxOpts, + tx_opts: TxParams, +) -> Result<()> { + if count == 0 { + // no virtual addresses to compute + return Ok(()); + } + + if !owner.is_valid_master() { + eyre::bail!( + "invalid owner address {owner}; see https://docs.tempo.xyz/protocol/tips/tip-1022" + ); + } + + let output = if let Some(salt) = salt { + let output = mine::derive(owner, salt); + if !mine::has_pow(&output.registration_hash, POW_BYTES) { + eyre::bail!( + "provided salt does not satisfy TIP-1022 proof of work: {}", + output.registration_hash + ); + } + output + } else { + let mut n_threads = threads.unwrap_or(0); + if n_threads == 0 { + n_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); + } + + let mut start_salt = B256::ZERO; + if !no_random { + let mut rng = match seed { + Some(seed) => StdRng::from_seed(seed.0), + None => StdRng::from_os_rng(), + }; + rng.fill_bytes(&mut start_salt[..]); + } + + if !shell::is_json() { + sh_println!("Mining TIP-1022 salt for {owner} with {n_threads} threads...")?; + } + let timer = Instant::now(); + let output = mine::mine(owner, start_salt, n_threads, POW_BYTES)?; + if !shell::is_json() { + sh_println!("Found salt in {:?}", timer.elapsed())?; + } + output + }; + + const MAX_USER_TAG: u64 = 0x0000_FFFF_FFFF_FFFF; + let mut virtual_addresses = Vec::with_capacity(count as usize); + for i in 0..count { + let tag_value = tag + .checked_add(i as u64) + .filter(|&t| t <= MAX_USER_TAG) + .ok_or_else(|| eyre::eyre!("tag overflow: tag + count exceeds the 6-byte user tag range (max {MAX_USER_TAG:#x})"))?; + let raw = tag_value.to_be_bytes(); + let user_tag = UserTag::new(raw[2..].try_into().expect("slice is 6 bytes")); + let vaddr = Address::new_virtual(output.master_id, user_tag); + virtual_addresses.push((user_tag, vaddr)); + } + + if shell::is_json() { + sh_println!( + "{}", + serde_json::to_string_pretty(&json!({ + "salt": format!("{}", output.salt), + "registration_hash": format!("{}", output.registration_hash), + "master_id": format!("{}", output.master_id), + "virtual_addresses": virtual_addresses.iter().map(|(tag, addr)| json!({ + "tag": format!("{tag}"), + "address": format!("{addr}"), + })).collect::>(), + }))? + )?; + } else { + sh_println!( + "Salt: {} +Registration hash: {} +Master ID: {}", + output.salt, + output.registration_hash, + output.master_id, + )?; + sh_println!("\nVirtual addresses:")?; + for (tag, vaddr) in &virtual_addresses { + sh_println!(" tag={tag} {vaddr}")?; + } + } + + if no_register { + return Ok(()); + } + + register(owner, output.salt, send_tx, tx_opts).await +} + +async fn register( + owner: Address, + salt: B256, + send_tx: SendTxOpts, + tx_opts: TxParams, +) -> Result<()> { + let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; + let signer = signer.ok_or_else(|| { + eyre::eyre!("cast vaddr create requires a signer (for example --private-key or --from)") + })?; + + let sender = + tempo_access_key.as_ref().map(|ak| ak.wallet_address).unwrap_or_else(|| signer.address()); + + if sender != owner { + eyre::bail!( + "signer mismatch: salt is for {owner}, but the configured signer would register as {sender}" + ); + } + + let config = send_tx.eth.load_config()?; + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + let mut tx = IAddressRegistry::new(ADDRESS_REGISTRY_ADDRESS, &provider) + .registerVirtualMaster(salt) + .into_transaction_request(); + tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); + + sh_println!("Submitting registerVirtualMaster({salt})...")?; + + if let Some(ref access_key) = tempo_access_key { + cast_send_with_access_key( + &provider, + tx, + &signer, + access_key, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await?; + } else { + let provider = build_provider_with_signer::(&send_tx, signer)?; + cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) + .await?; + } + + Ok(()) +} diff --git a/crates/cast/src/cmd/vaddr/mod.rs b/crates/cast/src/cmd/vaddr/mod.rs new file mode 100644 index 0000000000000..446d1e7ece5e2 --- /dev/null +++ b/crates/cast/src/cmd/vaddr/mod.rs @@ -0,0 +1,131 @@ +use crate::tx::{SendTxOpts, TxParams}; +use alloy_primitives::{Address, B256}; +use clap::Parser; +use foundry_cli::opts::RpcOpts; + +mod create; +mod resolve; +mod watch; + +/// TIP-1022 virtual address registry operations (Tempo). +/// +/// Virtual addresses are deterministic 20-byte aliases (masterId || VIRTUAL_MAGIC || userTag) +/// that auto-forward TIP-20 deposits to a registered master wallet at the protocol level, +/// with no on-chain sweep transaction required. +/// +/// See: +#[derive(Debug, Parser, Clone)] +pub enum VaddrSubcommand { + /// Mine a TIP-1022 proof-of-work salt, register as a virtual address master, and print + /// derived virtual addresses for the given owner. + #[command(visible_alias = "c")] + Create { + /// The master (owner) address that will control all virtual addresses under this + /// registration. Must not be the zero address, a virtual address, or a TIP-20 token. + #[arg(long, value_name = "ADDRESS")] + owner: Address, + + /// Use this salt directly instead of mining one. Must satisfy the 32-bit PoW requirement. + #[arg(long, conflicts_with_all = ["seed", "no_random"], value_name = "HEX")] + salt: Option, + + /// Starting user tag for the derived virtual address output (hex-encoded 6 bytes). + #[arg(long, default_value = "0", value_name = "U64")] + tag: u64, + + /// Number of virtual addresses to derive and print. + #[arg(long, default_value = "1", value_name = "N")] + count: u32, + + /// Number of threads to use for mining. Defaults to number of logical cores. + #[arg(long, short = 'j', visible_alias = "jobs")] + threads: Option, + + /// Seed for the random number generator used to initialize the salt search. + #[arg(long, value_name = "HEX")] + seed: Option, + + /// Start salt search from zero instead of a random value. + #[arg(long, conflicts_with = "seed")] + no_random: bool, + + /// Mine and print the salt and derived virtual addresses without submitting the + /// registerVirtualMaster transaction. + #[arg(long)] + no_register: bool, + + #[command(flatten)] + send_tx: Box, + + #[command(flatten)] + tx: Box, + }, + + /// Resolve a virtual address to its registered master and decode its components. + #[command(visible_alias = "r")] + Resolve { + /// The virtual address to resolve. + #[arg(value_name = "ADDRESS")] + addr: Address, + + #[command(flatten)] + rpc: RpcOpts, + }, + + /// Watch (tail) incoming TIP-20 transfers to a virtual address. + #[command(visible_alias = "w")] + Watch { + /// The virtual address to monitor. + #[arg(value_name = "ADDRESS")] + addr: Address, + + /// Filter on a specific TIP-20 token address. Watches all tokens if omitted. + #[arg(long, value_name = "ADDRESS")] + token: Option
, + + /// Block number to start from. Defaults to the current latest block. + #[arg(long, value_name = "BLOCK")] + from_block: Option, + + #[command(flatten)] + rpc: RpcOpts, + }, +} + +impl VaddrSubcommand { + pub async fn run(self) -> eyre::Result<()> { + match self { + Self::Create { + owner, + salt, + tag, + count, + threads, + seed, + no_random, + no_register, + send_tx, + tx, + } => { + create::run( + owner, + salt, + tag, + count, + threads, + seed, + no_random, + no_register, + *send_tx, + *tx, + ) + .await? + } + Self::Resolve { addr, rpc } => resolve::run(addr, rpc).await?, + Self::Watch { addr, token, from_block, rpc } => { + watch::run(addr, token, from_block, rpc).await? + } + } + Ok(()) + } +} diff --git a/crates/cast/src/cmd/vaddr/resolve.rs b/crates/cast/src/cmd/vaddr/resolve.rs new file mode 100644 index 0000000000000..96936f4fe4713 --- /dev/null +++ b/crates/cast/src/cmd/vaddr/resolve.rs @@ -0,0 +1,52 @@ +use alloy_primitives::{Address, hex}; +use eyre::Result; +use foundry_cli::{opts::RpcOpts, utils::LoadConfig}; +use foundry_common::{provider::ProviderBuilder, shell}; +use serde_json::json; +use tempo_alloy::{ + TempoNetwork, + contracts::precompiles::{ADDRESS_REGISTRY_ADDRESS, IAddressRegistry}, +}; + +pub(super) async fn run(addr: Address, rpc: RpcOpts) -> Result<()> { + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let registry = IAddressRegistry::new(ADDRESS_REGISTRY_ADDRESS, &provider); + + let decode_builder = registry.decodeVirtualAddress(addr); + let resolve_builder = registry.resolveVirtualAddress(addr); + let (decoded, master) = tokio::try_join!(decode_builder.call(), resolve_builder.call())?; + + if !decoded.isVirtual { + sh_println!("{addr} is not a virtual address")?; + return Ok(()); + } + + let master_id = decoded.masterId; + let user_tag = decoded.userTag; + let master: Address = master; + + if shell::is_json() { + let master_address = if master.is_zero() { None } else { Some(format!("{master}")) }; + sh_println!( + "{}", + serde_json::to_string_pretty(&json!({ + "address": format!("{addr}"), + "master_id": format!("0x{}", hex::encode(master_id)), + "user_tag": format!("0x{}", hex::encode(user_tag)), + "master_address": master_address, + }))? + )?; + } else { + sh_println!("Virtual address: {addr}")?; + sh_println!("Master ID: 0x{}", hex::encode(master_id))?; + sh_println!("User tag: 0x{}", hex::encode(user_tag))?; + if master.is_zero() { + sh_println!("Master address: (unregistered)")?; + } else { + sh_println!("Master address: {master}")?; + } + } + + Ok(()) +} diff --git a/crates/cast/src/cmd/vaddr/watch.rs b/crates/cast/src/cmd/vaddr/watch.rs new file mode 100644 index 0000000000000..dc159d5e37c8e --- /dev/null +++ b/crates/cast/src/cmd/vaddr/watch.rs @@ -0,0 +1,108 @@ +use alloy_primitives::{Address, B256, keccak256}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockNumberOrTag, Filter}; +use eyre::Result; +use foundry_cli::{opts::RpcOpts, utils::LoadConfig}; +use foundry_common::{provider::ProviderBuilder, shell}; +use serde_json::json; +use std::sync::LazyLock; +use tempo_alloy::TempoNetwork; +use tempo_primitives::TempoAddressExt; + +static TRANSFER_TOPIC: LazyLock = + LazyLock::new(|| keccak256(b"Transfer(address,address,uint256)")); + +pub(super) async fn run( + addr: Address, + token: Option
, + from_block: Option, + rpc: RpcOpts, +) -> Result<()> { + if !addr.is_virtual() { + eyre::bail!("{addr} is not a virtual address"); + } + + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + // Transfer(address indexed from, address indexed to, uint256 value) + // topic[0] = event sig, topic[1] = from, topic[2] = to + let to_topic: B256 = { + let mut buf = [0u8; 32]; + buf[12..].copy_from_slice(addr.as_slice()); + buf.into() + }; + + let start = from_block.map(BlockNumberOrTag::Number).unwrap_or(BlockNumberOrTag::Latest); + + let mut filter = + Filter::new().event_signature(*TRANSFER_TOPIC).topic2(to_topic).from_block(start); + + if let Some(tok) = token { + filter = filter.address(tok); + } + + if !shell::is_json() { + sh_println!("Watching transfers to {addr}... (Ctrl-C to stop)")?; + } + + // Fetch logs from the requested start block (historical when from_block is set) + let logs = provider.get_logs(&filter).await?; + for log in &logs { + print_transfer_log(log)?; + } + + // Poll for new logs + let mut last_block = provider.get_block_number().await?; + loop { + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let current = provider.get_block_number().await?; + if current > last_block { + let poll_filter = filter.clone().from_block(last_block + 1).to_block(current); + let new_logs = provider.get_logs(&poll_filter).await?; + for log in &new_logs { + print_transfer_log(log)?; + } + last_block = current; + } + } +} + +fn print_transfer_log(log: &alloy_rpc_types::Log) -> Result<()> { + let block = log.block_number.unwrap_or(0); + let tx = log.transaction_hash.unwrap_or_default(); + let token = log.address(); + + // Decode topics: topic[1]=from, topic[2]=to + let from = log.topics().get(1).map(|t| { + let mut addr = [0u8; 20]; + addr.copy_from_slice(&t[12..]); + Address::from(addr) + }); + + // Decode amount from data + let amount = if log.data().data.len() >= 32 { + alloy_primitives::U256::from_be_slice(&log.data().data[..32]) + } else { + alloy_primitives::U256::ZERO + }; + + if shell::is_json() { + sh_println!( + "{}", + serde_json::to_string(&json!({ + "block": block, + "tx": format!("{tx}"), + "token": format!("{token}"), + "from": from.map(|a| format!("{a}")).unwrap_or_default(), + "amount": amount.to_string(), + }))? + )?; + } else { + sh_println!( + "block={block} tx={tx} token={token} from={} amount={amount}", + from.map(|a| a.to_string()).unwrap_or_default(), + )?; + } + Ok(()) +} diff --git a/crates/cast/src/cmd/wallet/mod.rs b/crates/cast/src/cmd/wallet/mod.rs index 8e0dd8dd3ed8c..b2378d8bfdc58 100644 --- a/crates/cast/src/cmd/wallet/mod.rs +++ b/crates/cast/src/cmd/wallet/mod.rs @@ -779,8 +779,7 @@ flag to set your key via: )?; let address = wallet.address(); let success_message = format!( - "`{}` keystore was saved successfully. Address: {:?}", - &account_name, address, + "`{account_name}` keystore was saved successfully. Address: {address:?}", ); sh_println!("{}", success_message.green())?; } @@ -815,7 +814,7 @@ flag to set your key via: format!("Failed to remove keystore file at {}", keystore_path.display()) })?; - let success_message = format!("`{}` keystore was removed successfully.", &name); + let success_message = format!("`{name}` keystore was removed successfully."); sh_println!("{}", success_message.green())?; } Self::PrivateKey { @@ -886,8 +885,7 @@ flag to set your key via: let private_key = B256::from_slice(&wallet.credential().to_bytes()); - let success_message = - format!("{}'s private key is: {}", &account_name, private_key); + let success_message = format!("{account_name}'s private key is: {private_key}"); sh_println!("{}", success_message.green())?; } @@ -945,10 +943,9 @@ flag to set your key via: Some(&account_name), )?; + let address = wallet.address(); let success_message = format!( - "Password for keystore `{}` was changed successfully. Address: {:?}", - &account_name, - wallet.address(), + "Password for keystore `{account_name}` was changed successfully. Address: {address:?}", ); sh_println!("{}", success_message.green())?; } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index ce5572acebc13..2b1b03486bf04 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -40,6 +40,7 @@ use foundry_common::{ use foundry_config::Chain; use foundry_evm::core::bytecode::InstIter; use futures::{FutureExt, StreamExt, future::Either}; +#[cfg(feature = "optimism")] use op_alloy_consensus as _; use rayon::prelude::*; @@ -60,6 +61,7 @@ pub use foundry_evm::*; pub mod args; pub mod cmd; pub mod opts; +pub mod tempo; pub mod base; pub mod call_spec; @@ -246,7 +248,7 @@ impl + Clone + Unpin, N: Network> Cast { 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_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 { diff --git a/crates/cast/src/opts.rs b/crates/cast/src/opts.rs index effc081e8072a..763eb132ddb5c 100644 --- a/crates/cast/src/opts.rs +++ b/crates/cast/src/opts.rs @@ -1,11 +1,13 @@ +#[cfg(feature = "optimism")] +use crate::cmd::da_estimate::DAEstimateArgs; use crate::cmd::{ access_list::AccessListArgs, artifact::ArtifactArgs, b2e_payload::B2EPayloadArgs, batch_mktx::BatchMakeTxArgs, batch_send::BatchSendArgs, bind::BindArgs, call::CallArgs, constructor_args::ConstructorArgsArgs, create2::Create2Args, creation_code::CreationCodeArgs, - da_estimate::DAEstimateArgs, erc20::Erc20Subcommand, estimate::EstimateArgs, - find_block::FindBlockArgs, interface::InterfaceArgs, keychain::KeychainSubcommand, - logs::LogsArgs, mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs, send::SendTxArgs, - storage::StorageArgs, tip20::Tip20Subcommand, trace::TraceArgs, txpool::TxPoolSubcommands, + erc20::Erc20Subcommand, estimate::EstimateArgs, find_block::FindBlockArgs, + interface::InterfaceArgs, keychain::KeychainSubcommand, logs::LogsArgs, mktx::MakeTxArgs, + rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, tempo::TempoSubcommand, + tip20::Tip20Subcommand, trace::TraceArgs, txpool::TxPoolSubcommands, vaddr::VaddrSubcommand, wallet::WalletSubcommands, }; use alloy_ens::NameOrAddress; @@ -1163,6 +1165,7 @@ pub enum CastSubcommand { command: TxPoolSubcommands, }, /// Estimates the data availability size of a given opstack block. + #[cfg(feature = "optimism")] #[command(name = "da-estimate")] DAEstimate(DAEstimateArgs), @@ -1186,6 +1189,20 @@ pub enum CastSubcommand { #[command(subcommand)] command: KeychainSubcommand, }, + + /// Tempo wallet integration (login, etc.). + Tempo { + #[command(subcommand)] + command: TempoSubcommand, + }, + + /// TIP-1022 virtual address registry operations (Tempo). + #[command(visible_alias = "vaddr")] + VirtualAddress { + #[command(subcommand)] + command: VaddrSubcommand, + }, + #[command(name = "trace")] Trace(TraceArgs), } diff --git a/crates/cast/src/tempo.rs b/crates/cast/src/tempo.rs new file mode 100644 index 0000000000000..737c33f5b70de --- /dev/null +++ b/crates/cast/src/tempo.rs @@ -0,0 +1,3 @@ +//! Tempo transaction helpers used by Cast-facing commands. + +pub use foundry_common::tempo::{TempoSponsor, TempoSponsorPreview, resolve_tempo_sponsor_signer}; diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 96f5fc5137575..b58136f4ae9de 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -20,7 +20,7 @@ use foundry_common::{ get_pretty_receipt_w_reason_attr, shell, }; use foundry_config::{Chain, Config}; -use foundry_wallets::{BrowserWalletOpts, WalletOpts, WalletSigner}; +use foundry_wallets::{BrowserWalletOpts, TempoAccessKeyConfig, WalletOpts, WalletSigner}; use itertools::Itertools; use serde_json::value::RawValue; use std::{fmt::Write, marker::PhantomData, str::FromStr, time::Duration}; @@ -535,13 +535,29 @@ where sender: impl Into>, ) -> Result<(N::TransactionRequest, Option)> { let fill = self.fill; - self._build(sender, fill).await + self._build(sender, fill, None).await + } + + /// Builds a transaction that will be signed by a Tempo access key. + /// + /// The access-key id is set before gas estimation. If the access key needs on-chain + /// provisioning, its authorization is embedded before access-list/gas estimation and before + /// any sponsor digest can be computed. + pub async fn build_with_access_key( + mut self, + sender: impl Into>, + access_key: &TempoAccessKeyConfig, + ) -> Result<(N::TransactionRequest, Option)> { + self.tx.set_key_id(access_key.key_address); + let fill = self.fill; + self._build(sender, fill, Some(access_key)).await } async fn _build( mut self, sender: impl Into>, fill: bool, + access_key: Option<&TempoAccessKeyConfig>, ) -> Result<(N::TransactionRequest, Option)> { // prepare let sender = sender.into(); @@ -555,6 +571,16 @@ where // resolve let tx_nonce = self.resolve_nonce(sender.address(), fill).await?; self.resolve_auth(&sender, tx_nonce).await?; + if let Some(access_key) = access_key { + self.tx + .prepare_access_key_authorization( + &self.provider, + access_key.wallet_address, + access_key.key_address, + access_key.key_authorization.as_ref(), + ) + .await?; + } self.resolve_access_list().await?; // fill diff --git a/crates/cast/tests/cli/keychain.rs b/crates/cast/tests/cli/keychain.rs new file mode 100644 index 0000000000000..88e9e16983cc5 --- /dev/null +++ b/crates/cast/tests/cli/keychain.rs @@ -0,0 +1,76 @@ +//! CLI tests for `cast keychain` subcommands. + +use anvil::NodeConfig; +use foundry_test_utils::util::OutputExt; + +/// Anvil test accounts (standard mnemonic). +mod accounts { + pub const PK1: &str = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + pub const ADDR1: &str = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + pub const ADDR2: &str = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"; + pub const TOKEN: &str = "0x20C000000000000000000000b9537d11c60E8b50"; // PathUSD +} + +// `cast keychain rl --json` must emit `{"remaining":""}`, not a bare string. +casttest!(keychain_rl_json_is_object, async |_prj, cmd| { + let (_, handle) = anvil::spawn(NodeConfig::test_tempo()).await; + let rpc = handle.http_endpoint(); + + let output = cmd + .args([ + "keychain", + "rl", + accounts::ADDR1, + accounts::ADDR2, + accounts::TOKEN, + "--rpc-url", + &rpc, + "--json", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let parsed: serde_json::Value = serde_json::from_str(output.trim()) + .expect("cast keychain rl --json should emit valid JSON"); + assert!(parsed.is_object(), "expected JSON object, got: {output}"); + assert!( + parsed.get("remaining").is_some(), + "expected 'remaining' key in JSON output, got: {output}" + ); + // Must not be a bare string (old bug: `"0"`) + assert!(!parsed.is_string(), "JSON output must not be a bare string, got: {output}"); +}); + +// `cast keychain authorize --tempo.print-sponsor-hash --json` must emit +// `{"sponsor_hash":"0x..."}`, not a raw hex string. +casttest!(keychain_authorize_sponsor_hash_json_is_object, async |_prj, cmd| { + let (_, handle) = anvil::spawn(NodeConfig::test_tempo()).await; + let rpc = handle.http_endpoint(); + + let output = cmd + .args([ + "keychain", + "authorize", + accounts::ADDR2, // key to authorize + "--private-key", + accounts::PK1, + "--rpc-url", + &rpc, + "--tempo.print-sponsor-hash", + "--json", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let parsed: serde_json::Value = serde_json::from_str(output.trim()) + .expect("cast keychain authorize --tempo.print-sponsor-hash --json should emit valid JSON"); + assert!(parsed.is_object(), "expected JSON object, got: {output}"); + let hash = parsed + .get("sponsor_hash") + .and_then(|v| v.as_str()) + .unwrap_or_else(|| panic!("expected 'sponsor_hash' key in JSON output, got: {output}")); + assert!(hash.starts_with("0x"), "sponsor_hash should be 0x-prefixed, got: {hash}"); + assert_eq!(hash.len(), 66, "sponsor_hash should be 32-byte hex (66 chars), got: {hash}"); +}); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index da33a34d849db..2f744efe4d4f0 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,6 +1,7 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; +use alloy_eips::Decodable2718; use alloy_hardforks::EthereumHardfork; use alloy_network::{TransactionBuilder, TransactionResponse}; use alloy_primitives::{B256, Bytes, U256, address, b256, hex}; @@ -20,11 +21,13 @@ use foundry_test_utils::{ }; use serde_json::json; use std::{fs, path::Path, str::FromStr}; +use tempo_primitives::TempoTxEnvelope; #[macro_use] extern crate foundry_test_utils; mod erc20; +mod keychain; mod selectors; casttest!(print_short_version, |_prj, cmd| { @@ -2055,6 +2058,55 @@ casttest!(mktx_ethsign, async |_prj, cmd| { ]]); }); +// tests that `cast mktx --tempo.lane ` resolves the lane against a `tempo.lanes.toml` file at +// the project root, sets the corresponding `nonce_key` on the produced Tempo AA transaction. +casttest!(mktx_tempo_lane_resolves_nonce_key, |prj, cmd| { + // Write a shared lanes file at the project root. + let lanes_path = prj.root().join("tempo.lanes.toml"); + fs::write(&lanes_path, "deploy = 1\nops = 2\npayments = 42\n").unwrap(); + + let output = cmd + .current_dir(prj.root()) + .args([ + "mktx", + "--tempo.lane", + "payments", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--chain", + "1", + "--nonce", + "0", + "--gas-limit", + "21000", + "--gas-price", + "10000000000", + "--priority-gas-price", + "1000000000", + "0x0000000000000000000000000000000000000001", + ]) + .assert_success() + .get_output() + .clone(); + + // The resolved-lane breadcrumb is printed to stderr so it doesn't pollute stdout + // (which carries the raw signed transaction). + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("lane: payments (nonce_key=42, nonce=0)"), + "expected lane breadcrumb on stderr, got: {stderr}", + ); + + // Decode the produced signed Tempo AA transaction and verify it carries the + // resolved 2D nonce key. + let stdout = String::from_utf8_lossy(&output.stdout); + let raw_hex = stdout.trim().trim_start_matches("0x"); + let raw = hex::decode(raw_hex).expect("decode hex output"); + let envelope = TempoTxEnvelope::decode_2718(&mut raw.as_slice()).expect("decode tempo tx"); + assert!(envelope.is_aa(), "expected Tempo AA transaction, got: {envelope:?}"); + assert_eq!(envelope.nonce_key(), Some(U256::from(42_u64))); +}); + // tests that the raw encoded transaction is returned casttest!(tx_raw, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); @@ -4024,6 +4076,7 @@ Warning: Contract code is empty }); // +#[cfg(feature = "optimism")] casttest!(tx_raw_opstack_deposit, |_prj, cmd| { cmd.args([ "tx", @@ -5020,6 +5073,7 @@ casttest!(cast_decode_tx_network_flag_short_and_long_equivalent, |_prj, cmd| { // Test that `--network optimism` and `-n optimism` produce identical output for decode-tx. // Uses a known OP-stack deposit transaction (same tx as tx_raw_opstack_deposit test). +#[cfg(feature = "optimism")] casttest!(cast_decode_tx_network_optimism_short_and_long_equivalent, |_prj, cmd| { let tx = "0x7ef90207a0cbde10ec697aff886f95d2514bab434e455620627b9bb8ba33baaaa4d537d62794d45955f4de64f1840e5686e64278da901e263031944200000000000000000000000000000000000007872386f26fc10000872386f26fc1000083096c4980b901a4d764ad0b0001000000000000000000000000000000000000000000000000000000065132000000000000000000000000fd0bf71f60660e2f608ed56e1659c450eb1131200000000000000000000000004200000000000000000000000000000000000010000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a41635f5fd000000000000000000000000ca11bde05977b3631167028862be2a173976ca110000000000000000000000005703b26fe5a7be820db1bf34c901a79da1a46ba4000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; @@ -5076,3 +5130,68 @@ casttest!(run_evm_version_updates_gas_params, |_prj, cmd| { "expected Spurious Dragon gas (177241), got: {sd_output}" ); }); + +// Tests for `cast vaddr` JSON output +casttest!(vaddr_create_json_output, |_prj, cmd| { + // Use a pre-computed salt that satisfies the 4-byte PoW requirement for this owner. + // Salt: 0x0000000000000000000000000000000000000000000000003ee0a78d00000000 + // Owner: 0x1234567890123456789012345678901234567890 + let out = cmd + .args([ + "--json", + "vaddr", + "create", + "--owner", + "0x1234567890123456789012345678901234567890", + "--salt", + "0x0000000000000000000000000000000000000000000000003ee0a78d00000000", + "--no-register", + "--count", + "2", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let v: serde_json::Value = serde_json::from_str(out.trim()).expect("valid JSON"); + assert_eq!(v["salt"], "0x0000000000000000000000000000000000000000000000003ee0a78d00000000"); + assert_eq!( + v["registration_hash"], + "0x000000002f51c0c4f66f3910f799c6b98e2123ef43a401a062eb8ee07498c396" + ); + assert_eq!(v["master_id"], "0x2f51c0c4"); + let addrs = v["virtual_addresses"].as_array().expect("array"); + assert_eq!(addrs.len(), 2); + assert_eq!(addrs[0]["tag"], "0x000000000000"); + assert_eq!( + addrs[0]["address"].as_str().unwrap().to_lowercase(), + "0x2f51c0c4fdfdfdfdfdfdfdfdfdfd000000000000" + ); + assert_eq!(addrs[1]["tag"], "0x000000000001"); + assert_eq!( + addrs[1]["address"].as_str().unwrap().to_lowercase(), + "0x2f51c0c4fdfdfdfdfdfdfdfdfdfd000000000001" + ); +}); + +casttest!(vaddr_create_plain_output, |_prj, cmd| { + cmd.args([ + "vaddr", + "create", + "--owner", + "0x1234567890123456789012345678901234567890", + "--salt", + "0x0000000000000000000000000000000000000000000000003ee0a78d00000000", + "--no-register", + ]) + .assert_success() + .stdout_eq(str![[r#" +Salt: 0x0000000000000000000000000000000000000000000000003ee0a78d00000000 +Registration hash: 0x000000002f51c0c4f66f3910f799c6b98e2123ef43a401a062eb8ee07498c396 +Master ID: 0x2f51c0c4 + +Virtual addresses: + tag=0x000000000000 [..] + +"#]]); +}); diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 659fec7f1a333..0eab12331be04 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -68,3 +68,13 @@ tracing.workspace = true walkdir.workspace = true proptest.workspace = true serde.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm-core/optimism", + "foundry-evm-fuzz/optimism", + "foundry-evm-traces/optimism", + "forge-script-sequence/optimism", +] diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 94974301df8ac..01de77b9c95fd 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -5447,7 +5447,7 @@ { "func": { "id": "expectEmit_0", - "description": "Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.).\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data (as specified by the booleans).", + "description": "Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.).\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data (as specified by the booleans).\nMust be placed immediately before the call you want to assert on. If the next call reverts and the\nrevert is caught by the caller (low-level call or try/catch), the expectation remains active and may\nbe satisfied by a log emitted from a later call.", "declaration": "function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) external;", "visibility": "external", "mutability": "", @@ -5487,7 +5487,7 @@ { "func": { "id": "expectEmit_2", - "description": "Prepare an expected log with all topic and data checks enabled.\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data.", + "description": "Prepare an expected log with all topic and data checks enabled.\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data.\nMust be placed immediately before the call you want to assert on. If the next call reverts and the\nrevert is caught by the caller (low-level call or try/catch), the expectation remains active and may\nbe satisfied by a log emitted from a later call.", "declaration": "function expectEmit() external;", "visibility": "external", "mutability": "", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 7c2e0741704b3..12cfd19017770 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1082,6 +1082,9 @@ interface Vm { /// Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.). /// Call this function, then emit an event, then call a function. Internally after the call, we check if /// logs were emitted in the expected order with the expected topics and data (as specified by the booleans). + /// Must be placed immediately before the call you want to assert on. If the next call reverts and the + /// revert is caught by the caller (low-level call or try/catch), the expectation remains active and may + /// be satisfied by a log emitted from a later call. #[cheatcode(group = Testing, safety = Unsafe)] function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) external; @@ -1093,6 +1096,9 @@ interface Vm { /// Prepare an expected log with all topic and data checks enabled. /// Call this function, then emit an event, then call a function. Internally after the call, we check if /// logs were emitted in the expected order with the expected topics and data. + /// Must be placed immediately before the call you want to assert on. If the next call reverts and the + /// revert is caught by the caller (low-level call or try/catch), the expectation remains active and may + /// be satisfied by a log emitted from a later call. #[cheatcode(group = Testing, safety = Unsafe)] function expectEmit() external; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index b22f76714dd0f..27545c1b6cd33 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -856,42 +856,6 @@ impl Cheatcodes { } } - // Handle mocked calls - if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) { - let ctx = MockCallDataContext { - calldata: call.input.bytes(ecx), - value: call.transfer_value(), - }; - - if let Some(return_data_queue) = match mocks.get_mut(&ctx) { - Some(queue) => Some(queue), - None => mocks - .iter_mut() - .find(|(mock, _)| { - call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..]) - && mock.value.is_none_or(|value| Some(value) == call.transfer_value()) - }) - .map(|(_, v)| v), - } && let Some(return_data) = if return_data_queue.len() == 1 { - // If the mocked calls stack has a single element in it, don't empty it - return_data_queue.front().map(|x| x.to_owned()) - } else { - // Else, we pop the front element - return_data_queue.pop_front() - } { - return Some(CallOutcome { - result: InterpreterResult { - result: return_data.ret_type, - output: return_data.data, - gas, - }, - memory_offset: call.return_memory_offset.clone(), - was_precompile_called: true, - precompile_call_logs: vec![], - }); - } - } - // Apply our prank if let Some(prank) = &self.get_prank(curr_depth) { // Apply delegate call, `call.caller`` will not equal `prank.prank_caller` @@ -932,6 +896,72 @@ impl Cheatcodes { } } + // Handle mocked calls + if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) { + let ctx = MockCallDataContext { + calldata: call.input.bytes(ecx), + value: call.transfer_value(), + }; + + if let Some(return_data_queue) = match mocks.get_mut(&ctx) { + Some(queue) => Some(queue), + None => mocks + .iter_mut() + .find(|(mock, _)| { + call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..]) + && mock.value.is_none_or(|value| Some(value) == call.transfer_value()) + }) + .map(|(_, v)| v), + } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned()) + { + if let Some(value) = call.transfer_value() { + let checkpoint = ecx.journal_mut().checkpoint(); + match ecx.journal_mut().transfer_loaded( + call.transfer_from(), + call.transfer_to(), + value, + ) { + None => { + if return_data.ret_type.is_ok() { + ecx.journal_mut().checkpoint_commit(); + } else { + ecx.journal_mut().checkpoint_revert(checkpoint); + } + } + Some(err) => { + ecx.journal_mut().checkpoint_revert(checkpoint); + return Some(CallOutcome { + result: InterpreterResult { + result: err.into(), + output: Bytes::new(), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + was_precompile_called: false, + precompile_call_logs: vec![], + }); + } + } + } + + // If the mocked calls stack has a single element in it, don't empty it + if return_data_queue.len() > 1 { + return_data_queue.pop_front(); + } + + return Some(CallOutcome { + result: InterpreterResult { + result: return_data.ret_type, + output: return_data.data, + gas, + }, + memory_offset: call.return_memory_offset.clone(), + was_precompile_called: true, + precompile_call_logs: vec![], + }); + } + } + // Apply EIP-2930 access list self.apply_accesslist(ecx); @@ -1497,6 +1527,21 @@ impl Inspector> for Cheatcode } } + // this will ensure we don't have false positives when trying to diagnose reverts in fork + // mode + let diag = self.fork_revert_diagnostic.take(); + + // If the call already reverted, preserve that primary failure and skip post-call + // expect* validation so it cannot overwrite the original revert. + if outcome.result.is_revert() { + // if there's a revert and a previous call was diagnosed as fork related revert then we + // can return a better error here + if let Some(err) = diag { + outcome.result.output = Error::encode(err.to_error_msg(&self.labels)); + } + return; + } + // At the end of the call, // we need to check if we've found all the emits. // We know we've found all the expected emits in the right order @@ -1574,19 +1619,6 @@ impl Inspector> for Cheatcode self.expected_emits.clear() } - // this will ensure we don't have false positives when trying to diagnose reverts in fork - // mode - let diag = self.fork_revert_diagnostic.take(); - - // if there's a revert and a previous call was diagnosed as fork related revert then we can - // return a better error here - if outcome.result.is_revert() - && let Some(err) = diag - { - outcome.result.output = Error::encode(err.to_error_msg(&self.labels)); - return; - } - // try to diagnose reverts in multi-fork mode where a call is made to an address that does // not exist if let TxKind::Call(test_contract) = ecx.tx().kind() { @@ -1867,10 +1899,23 @@ impl Inspector> for Cheatcode } // Handle expected reverts - if let Some(expected_revert) = &self.expected_revert + if let Some(expected_revert) = &mut self.expected_revert && curr_depth <= expected_revert.depth && matches!(expected_revert.kind, ExpectedRevertKind::Default) { + // Mirror the logic in `call_end`: when an expected reverter address is set + // and we don't yet have one (or we're matching multiple reverts), record the + // would-be deployed address as the reverter. revm guarantees `outcome.address` + // is `Some(_)` whenever the constructor actually ran (including the revert + // case); it is only `None` for pre-frame rejection (depth/balance/nonce), + // for which a reverter address is meaningless. + if outcome.result.is_revert() + && expected_revert.reverter.is_some() + && (expected_revert.reverted_by.is_none() || expected_revert.count > 1) + && let Some(addr) = outcome.address + { + expected_revert.reverted_by = Some(addr); + } let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap(); return match revert_handlers::handle_expect_revert( false, diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index 608f20f0c4e32..12d625768a0c9 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -164,7 +164,7 @@ impl EqRelAssertionError { format_units_uint(&f.left, decimals), format_units_uint(&f.right, decimals), format_delta_percent(&f.max_delta), - &f.real_delta, + f.real_delta, ), Self::Overflow => self.to_string(), } @@ -179,7 +179,7 @@ impl EqRelAssertionError { format_units_int(&f.left, decimals), format_units_int(&f.right, decimals), format_delta_percent(&f.max_delta), - &f.real_delta, + f.real_delta, ), Self::Overflow => self.to_string(), } diff --git a/crates/cheatcodes/src/version.rs b/crates/cheatcodes/src/version.rs index fb722c2814baa..2b8f81518a621 100644 --- a/crates/cheatcodes/src/version.rs +++ b/crates/cheatcodes/src/version.rs @@ -20,7 +20,14 @@ impl Cheatcode for foundryVersionAtLeastCall { } fn foundry_version_cmp(version: &str) -> Result { - version_cmp(SEMVER_VERSION.split('-').next().unwrap(), version) + version_cmp(strip_semver_metadata(SEMVER_VERSION), version) +} + +/// Strips pre-release (e.g. `-nightly`, `-dev`) and build metadata +/// (e.g. `+..`) from a version string +/// so we compare on `MAJOR.MINOR.PATCH` only. +fn strip_semver_metadata(version: &str) -> &str { + version.split(['-', '+']).next().unwrap() } fn version_cmp(version_a: &str, version_b: &str) -> Result { @@ -42,3 +49,61 @@ fn parse_version(version: &str) -> Result { } Ok(version) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn strips_build_metadata_only() { + // Tagged release: `1.7.1+..` + assert_eq!(strip_semver_metadata("1.7.1+abc1234567.1737036656.release"), "1.7.1"); + } + + #[test] + fn strips_pre_release_and_build_metadata() { + // Nightly: `1.7.1-nightly+..` + assert_eq!(strip_semver_metadata("1.7.1-nightly+abc1234567.1737036656.release"), "1.7.1"); + // Dev: `1.7.1-dev+..` + assert_eq!(strip_semver_metadata("1.7.1-dev+abc1234567.1737036656.debug"), "1.7.1"); + } + + #[test] + fn strips_plain_version() { + assert_eq!(strip_semver_metadata("1.7.1"), "1.7.1"); + } + + #[test] + fn version_cmp_orders_correctly() { + assert_eq!(version_cmp("1.7.1", "1.7.1").unwrap(), Ordering::Equal); + assert_eq!(version_cmp("1.7.1", "1.7.0").unwrap(), Ordering::Greater); + assert_eq!(version_cmp("1.7.1", "1.7.2").unwrap(), Ordering::Less); + assert_eq!(version_cmp("1.7.1", "0.0.1").unwrap(), Ordering::Greater); + assert_eq!(version_cmp("1.7.1", "99.0.0").unwrap(), Ordering::Less); + } + + #[test] + fn parse_version_rejects_pre_release_and_build_metadata() { + // User-supplied versions must be plain `MAJOR.MINOR.PATCH`. + assert!(parse_version("1.7.1-nightly").is_err()); + assert!(parse_version("1.7.1+abc").is_err()); + assert!(parse_version("not-a-version").is_err()); + assert!(parse_version("1.7.1").is_ok()); + } + + #[test] + fn cmp_works_against_full_semver_version_strings() { + // Simulate comparing each shape of `SEMVER_VERSION` against a user-supplied version. + for current in [ + "1.7.1+abc1234567.1737036656.release", + "1.7.1-nightly+abc1234567.1737036656.release", + "1.7.1-dev+abc1234567.1737036656.debug", + "1.7.1", + ] { + let stripped = strip_semver_metadata(current); + assert_eq!(version_cmp(stripped, "1.7.1").unwrap(), Ordering::Equal); + assert_eq!(version_cmp(stripped, "1.7.0").unwrap(), Ordering::Greater); + assert_eq!(version_cmp(stripped, "1.7.2").unwrap(), Ordering::Less); + } + } +} diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index a9396b4208886..bb673c9219e10 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -49,7 +49,6 @@ itertools.workspace = true semver.workspace = true serde_json.workspace = true serde.workspace = true -solang-parser.workspace = true time = { version = "0.3", features = ["formatting"] } yansi.workspace = true tracing.workspace = true @@ -64,8 +63,13 @@ foundry-test-utils.workspace = true rexpect = "0.6" [features] -default = ["jemalloc", "asm-keccak"] +default = ["jemalloc", "asm-keccak", "optimism"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] tracy-allocator = ["foundry-cli/tracy-allocator"] +optimism = [ + "foundry-common/optimism", + "foundry-evm/optimism", + "foundry-cli/optimism", +] diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index da2c7f4caff02..2ec057b8167c0 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -2,10 +2,7 @@ //! //! This module contains the execution logic for the [SessionSource]. -use crate::{ - prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper}, - source::IntermediateOutput, -}; +use crate::prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper}; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_json_abi::EventParam; use alloy_primitives::{Address, B256, U256, hex}; @@ -15,7 +12,17 @@ use foundry_evm::{ backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder, inspectors::CheatsConfig, traces::TraceMode, }; -use solang_parser::pt; +use solar::{ + ast::{BinOpKind, ElementaryType, FunctionKind, LitKind, StateMutability, StrKind, UnOpKind}, + interface::Symbol, + sema::{ + hir::{ + ContractId, Event, Expr, ExprKind, Function, ItemId, Res, StmtKind, Type as HirType, + TypeKind, Visibility, + }, + ty::{Gcx, Ty, TyKind}, + }, +}; use std::ops::ControlFlow; use yansi::Paint; @@ -86,8 +93,10 @@ impl SessionSource { if let Some(err) = err { let output = source_without_inspector.build()?; - let formatted_event = - output.enter(|output| output.get_event(input).map(format_event_definition)); + let formatted_event = output.enter(|output| { + let gcx = output.gcx(); + output.get_event(input).map(|eid| format_event_definition(gcx, gcx.hir.event(eid))) + }); if let Some(formatted_event) = formatted_event { return Ok((ControlFlow::Break(()), Some(formatted_event?))); } @@ -122,30 +131,37 @@ impl SessionSource { // which was wrapped in `abi.encode`. let generated_output = source.build()?; - // If the expression is a variable declaration within the REPL contract, use its type; - // otherwise, attempt to infer the type. - let contract_expr = generated_output - .intermediate - .repl_contract_expressions - .get(input) - .or_else(|| source.infer_inner_expr_type()); + // Inside the compiler closure, infer the DynSolType of the inspected expression and + // determine whether the REPL should continue. + let res_ty = generated_output.enter(|out| -> Option<(bool, DynSolType)> { + let gcx = out.gcx(); - // If the current action is a function call, we get its return type - // otherwise it returns None - let function_call_return_type = - Type::get_function_return_type(contract_expr, &generated_output.intermediate); + // Try direct lookup of `input` as a named variable in the REPL contract. + if let Some(direct_ty) = lookup_named_variable_type(gcx, input) { + return Some((false, direct_ty)); + } - let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type - { - (function_call_return_type.0, function_call_return_type.1) - } else { - match contract_expr.and_then(|e| { - Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty)) - }) { - Some(res) => res, - // this type was denied for inspection, continue - None => return Ok((ControlFlow::Continue(()), None)), + // Otherwise, find the appended `bytes memory inspectoor = abi.encode();` + // and pull out the first call argument. + let block = out.run_func_body(); + let last = block.last()?; + let StmtKind::DeclSingle(vid) = last.kind else { return None }; + let var = gcx.hir.variable(vid); + let init = var.initializer?; + let ExprKind::Call(_callee, args, _) = &init.kind else { return None }; + let inner_expr = args.exprs().next()?; + + // If the call is `func()` returning a single value, prefer the function return type. + if let Some(ty) = get_function_return_type(gcx, inner_expr) { + return Some((should_continue(inner_expr), ty)); } + + let ty = expr_to_dyn(gcx, inner_expr, true)?; + Some((should_continue(inner_expr), ty)) + }); + + let Some((cont, ty)) = res_ty else { + return Ok((ControlFlow::Continue(()), None)); }; // the file compiled correctly, thus the last stack item must be the memory offset of @@ -162,42 +178,10 @@ impl SessionSource { eyre::bail!("Failed to inspect last expression: could not retrieve data from memory") }; let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?; - let c = if should_continue(contract_expr) { - ControlFlow::Continue(()) - } else { - ControlFlow::Break(()) - }; + let c = if cont { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }; Ok((c, Some(format_token(token)))) } - /// Gracefully attempts to extract the type of the expression within the `abi.encode(...)` - /// call inserted by the inspect function. - /// - /// ### Takes - /// - /// A reference to a [SessionSource] - /// - /// ### Returns - /// - /// Optionally, a [Type] - fn infer_inner_expr_type(&self) -> Option<&pt::Expression> { - let out = self.build().ok()?; - let run = out.run_func_body().ok()?.last(); - match run { - Some(pt::Statement::VariableDefinition( - _, - _, - Some(pt::Expression::FunctionCall(_, _, args)), - )) => { - // We can safely unwrap the first expression because this function - // will only be called on a session source that has just had an - // `inspectoor` variable appended to it. - Some(args.first().unwrap()) - } - _ => None, - } - } - async fn build_runner(&mut self, final_pc: usize) -> Result { let (evm_env, tx_env, fork_block) = self.config.evm_opts.env().await?; @@ -241,6 +225,51 @@ impl SessionSource { } } +/// Looks up `name` as a named variable in the REPL contract (state variables or run() locals) +/// and returns its type as a [`DynSolType`]. +/// +/// Only top-level statements of `run()` are scanned. Variables declared inside nested blocks +/// (`if`, `for`, `while`, `unchecked`, etc.) are not visible here; the caller falls back to +/// the `inspectoor`-based path for those cases. +fn lookup_named_variable_type(gcx: Gcx<'_>, name: &str) -> Option { + let hir = &gcx.hir; + let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?; + + // State variables. + for vid in repl.variables() { + let var = hir.variable(vid); + if var.name.map(|n| n.as_str() == name).unwrap_or(false) { + return solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())); + } + } + + // Locals declared in run(). + let run_fid = repl + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?; + let body = hir.function(run_fid).body?; + for stmt in body.stmts { + match stmt.kind { + StmtKind::DeclSingle(vid) => { + let var = hir.variable(vid); + if var.name.map(|n| n.as_str() == name).unwrap_or(false) { + return solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())); + } + } + StmtKind::DeclMulti(vids, _) => { + for vid in vids.iter().flatten() { + let var = hir.variable(*vid); + if var.name.map(|n| n.as_str() == name).unwrap_or(false) { + return solar_ty_to_dyn(gcx, gcx.type_of_item((*vid).into())); + } + } + } + _ => {} + } + } + None +} + /// Formats a value into an inspection message // TODO: Verbosity option fn format_token(token: DynSolValue) -> String { @@ -343,49 +372,37 @@ fn format_token(token: DynSolValue) -> String { } } -/// Formats a [pt::EventDefinition] into an inspection message -/// -/// ### Takes -/// -/// An borrowed [pt::EventDefinition] -/// -/// ### Returns -/// -/// A formatted [pt::EventDefinition] for use in inspection output. +/// Formats an [`Event`] into an inspection message. // TODO: Verbosity option -fn format_event_definition(event_definition: &pt::EventDefinition) -> Result { - let event_name = event_definition.name.as_ref().expect("Event has a name").to_string(); - let inputs = event_definition - .fields +fn format_event_definition(gcx: Gcx<'_>, event: &Event<'_>) -> Result { + let event_name = event.name.as_str().to_string(); + let inputs = event + .parameters .iter() - .map(|param| { - let name = param - .name - .as_ref() - .map(ToString::to_string) - .unwrap_or_else(|| "".to_string()); - let kind = Type::from_expression(¶m.ty) - .and_then(Type::into_builtin) + .map(|&pid| { + let var = gcx.hir.variable(pid); + let name = + var.name.map(|n| n.as_str().to_string()).unwrap_or_else(|| "".into()); + let kind = solar_ty_to_dyn(gcx, gcx.type_of_item(pid.into())) .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?; Ok(EventParam { name, ty: kind.to_string(), components: vec![], - indexed: param.indexed, + indexed: var.indexed, internal_type: None, }) }) .collect::>>()?; - let event = - alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous }; + let event = alloy_json_abi::Event { name: event_name, inputs, anonymous: event.anonymous }; Ok(format!( "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}", "event".red(), SolidityHelper::new().highlight(&format!( "{}({})", - &event.name, - &event + event.name, + event .inputs .iter() .map(|param| format!( @@ -395,7 +412,7 @@ fn format_event_definition(event_definition: &pt::EventDefinition) -> Result>() @@ -411,844 +428,724 @@ fn format_event_definition(event_definition: &pt::EventDefinition) -> Result), - - /// (type, length) - FixedArray(Box, usize), +/// Converts an [`Expr`] directly to a [`DynSolType`] for ABI inspection. +/// +/// `lookup` controls whether user-defined type names are resolved via the HIR. +fn expr_to_dyn(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option { + match &expr.kind { + // Elementary type expression: `uint256`, `address`, etc. + ExprKind::Type(ty) => hir_ty_to_dyn(gcx, ty), + + // `type(T)`: only meaningful as the lhs of a member access. + ExprKind::TypeCall(_) => None, + + // Literals. + ExprKind::Lit(lit) => match &lit.kind { + LitKind::Address(_) => Some(DynSolType::Address), + LitKind::Bool(_) => Some(DynSolType::Bool), + LitKind::Str(kind, _, _) => match kind { + StrKind::Hex => Some(DynSolType::Bytes), + StrKind::Str | StrKind::Unicode => Some(DynSolType::String), + }, + LitKind::Number(_) | LitKind::Rational(_) => Some(DynSolType::Uint(256)), + LitKind::Err(_) => None, + }, + + // Resolved identifier: `foo`. + ExprKind::Ident(reses) => { + let res = reses.first()?; + match *res { + Res::Item(ItemId::Variable(vid)) => { + solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())) + } + Res::Item(ItemId::Struct(sid)) => { + // Struct reference used as a constructor produces a tuple of field types. + Some(DynSolType::Tuple( + gcx.struct_field_types(sid) + .iter() + .filter_map(|&t| solar_ty_to_dyn(gcx, t)) + .collect(), + )) + } + // Other items and builtins: handled by enclosing Call/Member expressions. + _ => None, + } + } - /// (type, index) - ArrayIndex(Box, Option), + // Index/access: `arr[i]`, `MyType[]`, `MyType[N]`. + ExprKind::Index(base, idx) => { + let base_ty = expr_to_dyn(gcx, base, lookup)?; + let num = + idx.and_then(|e| parse_number_literal(e)).and_then(|n| usize::try_from(n).ok()); + match &base.kind { + // Type-level indexing builds an array type expression. + ExprKind::Type(_) | ExprKind::TypeCall(_) => { + if let Some(n) = num { + Some(DynSolType::FixedArray(Box::new(base_ty), n)) + } else { + Some(DynSolType::Array(Box::new(base_ty))) + } + } + // Runtime indexing returns the element type. + _ => match base_ty { + DynSolType::Array(inner) | DynSolType::FixedArray(inner, _) => Some(*inner), + DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_) => { + Some(DynSolType::FixedBytes(1)) + } + other => Some(other), + }, + } + } - /// (types) - Tuple(Vec>), + // Slice: same type as the base. + ExprKind::Slice(base, _, _) => expr_to_dyn(gcx, base, lookup), - /// (name, params, returns) - Function(Box, Vec>, Vec>), + // Array literal `[a, b, c]`. + ExprKind::Array(values) => values + .first() + .and_then(|e| expr_to_dyn(gcx, e, lookup)) + .map(|ty| DynSolType::FixedArray(Box::new(ty), values.len())), - /// (lhs, rhs) - Access(Box, String), + // Tuple expression `(a, b, c)`. + ExprKind::Tuple(items) => Some(DynSolType::Tuple( + items.iter().filter_map(|opt| opt.and_then(|e| expr_to_dyn(gcx, e, lookup))).collect(), + )), - /// (types) - Custom(Vec), -} + // Member access `lhs.member`. + ExprKind::Member(_, _) => resolve_member(gcx, expr, lookup), -impl Type { - /// Convert a [pt::Expression] to a [Type] - /// - /// ### Takes - /// - /// A reference to a [pt::Expression] to convert. - /// - /// ### Returns - /// - /// Optionally, an owned [Type] - fn from_expression(expr: &pt::Expression) -> Option { - match expr { - pt::Expression::Type(_, ty) => Self::from_type(ty), - - pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])), - - // array - pt::Expression::ArraySubscript(_, expr, num) => { - // if num is Some then this is either an index operation (arr[]) - // or a FixedArray statement (new uint256[]) - Self::from_expression(expr).and_then(|ty| { - let boxed = Box::new(ty); - let num = num.as_deref().and_then(parse_number_literal).and_then(|n| { - usize::try_from(n).ok() - }); - match expr.as_ref() { - // statement - pt::Expression::Type(_, _) => { - if let Some(num) = num { - Some(Self::FixedArray(boxed, num)) - } else { - Some(Self::Array(boxed)) - } - } - // index - pt::Expression::Variable(_) => { - Some(Self::ArrayIndex(boxed, num)) - } - _ => None - } - }) - } - pt::Expression::ArrayLiteral(_, values) => { - values.first().and_then(Self::from_expression).map(|ty| { - Self::FixedArray(Box::new(ty), values.len()) - }) - } + // Function/constructor call. + ExprKind::Call(_, _, _) => resolve_call(gcx, expr, lookup), - // tuple - pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))), + // `new T`: produces a value of type T. + ExprKind::New(ty) => hir_ty_to_dyn(gcx, ty), - // . - pt::Expression::MemberAccess(_, lhs, rhs) => { - Self::from_expression(lhs).map(|lhs| { - Self::Access(Box::new(lhs), rhs.name.clone()) - }) - } + // `payable(addr)`. + ExprKind::Payable(_) => Some(DynSolType::Address), - // - pt::Expression::Parenthesis(_, inner) | // () - pt::Expression::New(_, inner) | // new - pt::Expression::UnaryPlus(_, inner) | // + - // ops - pt::Expression::BitwiseNot(_, inner) | // ~ - pt::Expression::ArraySlice(_, inner, _, _) | // [*start*:*end*] - // assign ops - pt::Expression::PreDecrement(_, inner) | // -- - pt::Expression::PostDecrement(_, inner) | // -- - pt::Expression::PreIncrement(_, inner) | // ++ - pt::Expression::PostIncrement(_, inner) | // ++ - pt::Expression::Assign(_, inner, _) | // = ... - pt::Expression::AssignAdd(_, inner, _) | // += ... - pt::Expression::AssignSubtract(_, inner, _) | // -= ... - pt::Expression::AssignMultiply(_, inner, _) | // *= ... - pt::Expression::AssignDivide(_, inner, _) | // /= ... - pt::Expression::AssignModulo(_, inner, _) | // %= ... - pt::Expression::AssignAnd(_, inner, _) | // &= ... - pt::Expression::AssignOr(_, inner, _) | // |= ... - pt::Expression::AssignXor(_, inner, _) | // ^= ... - pt::Expression::AssignShiftLeft(_, inner, _) | // <<= ... - pt::Expression::AssignShiftRight(_, inner, _) // >>= ... - => Self::from_expression(inner), - - // *condition* ? : - pt::Expression::ConditionalOperator(_, _, if_true, if_false) => { - Self::from_expression(if_true).or_else(|| Self::from_expression(if_false)) - } + // Ternary: prefer truthy branch's type, fall back to else branch. + ExprKind::Ternary(_, t, e) => { + expr_to_dyn(gcx, t, lookup).or_else(|| expr_to_dyn(gcx, e, lookup)) + } - // address - pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)), - pt::Expression::HexNumberLiteral(_, s, _) => { - match s.parse::
() { - Ok(addr) if *s == addr.to_checksum(None) => { - Some(Self::Builtin(DynSolType::Address)) + // Delete has no return type. + ExprKind::Delete(_) => None, + + // Unary operations. + ExprKind::Unary(op, inner) => match op.kind { + UnOpKind::Neg => expr_to_dyn(gcx, inner, lookup).map(|ty| match ty { + DynSolType::Uint(n) => DynSolType::Int(n), + DynSolType::Int(n) => DynSolType::Uint(n), + x => x, + }), + UnOpKind::Not => Some(DynSolType::Bool), + UnOpKind::BitNot + | UnOpKind::PreInc + | UnOpKind::PreDec + | UnOpKind::PostInc + | UnOpKind::PostDec => expr_to_dyn(gcx, inner, lookup), + }, + + // Binary operations. + ExprKind::Binary(lhs, op, rhs) => match op.kind { + BinOpKind::Lt + | BinOpKind::Le + | BinOpKind::Gt + | BinOpKind::Ge + | BinOpKind::Eq + | BinOpKind::Ne + | BinOpKind::And + | BinOpKind::Or => Some(DynSolType::Bool), + BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul | BinOpKind::Div => { + match (expr_to_dyn(gcx, lhs, false), expr_to_dyn(gcx, rhs, false)) { + (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_))) + | (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => { + Some(DynSolType::Int(256)) } - _ => Some(Self::Builtin(DynSolType::Uint(256))), + _ => Some(DynSolType::Uint(256)), } } + BinOpKind::Rem + | BinOpKind::Pow + | BinOpKind::BitAnd + | BinOpKind::BitOr + | BinOpKind::BitXor + | BinOpKind::Shl + | BinOpKind::Shr + | BinOpKind::Sar => Some(DynSolType::Uint(256)), + }, + + // Assignments: type of the lhs. + ExprKind::Assign(lhs, _, _) => expr_to_dyn(gcx, lhs, lookup), + + ExprKind::Err(_) => None, + } +} - // uint and int - // invert - pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int), - - // int if either operand is int - // TODO: will need an update for Solidity v0.8.18 user defined operators: - // https://github.com/ethereum/solidity/issues/13718#issuecomment-1341058649 - pt::Expression::Add(_, lhs, rhs) | - pt::Expression::Subtract(_, lhs, rhs) | - pt::Expression::Multiply(_, lhs, rhs) | - pt::Expression::Divide(_, lhs, rhs) => { - match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) { - (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_))) | -(Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => { - Some(Self::Builtin(DynSolType::Int(256))) - } - _ => { - Some(Self::Builtin(DynSolType::Uint(256))) - } +/// Converts a [`HirType`] to a [`DynSolType`]. +fn hir_ty_to_dyn(gcx: Gcx<'_>, ty: &HirType<'_>) -> Option { + match &ty.kind { + TypeKind::Elementary(et) => elementary_to_dyn(*et), + TypeKind::Array(arr) => { + let elem = hir_ty_to_dyn(gcx, &arr.element)?; + if let Some(size) = arr.size { + let n = parse_number_literal(size).and_then(|n| usize::try_from(n).ok()); + if let Some(n) = n { + Some(DynSolType::FixedArray(Box::new(elem), n)) + } else { + Some(DynSolType::Array(Box::new(elem))) } + } else { + Some(DynSolType::Array(Box::new(elem))) } - - // always assume uint - pt::Expression::Modulo(_, _, _) | - pt::Expression::Power(_, _, _) | - pt::Expression::BitwiseOr(_, _, _) | - pt::Expression::BitwiseAnd(_, _, _) | - pt::Expression::BitwiseXor(_, _, _) | - pt::Expression::ShiftRight(_, _, _) | - pt::Expression::ShiftLeft(_, _, _) | - pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))), - - // TODO: Rational numbers - pt::Expression::RationalNumberLiteral(_, _, _, _, _) => { - Some(Self::Builtin(DynSolType::Uint(256))) + } + TypeKind::Function(f) => match f.returns.len() { + 0 => None, + 1 => { + let var = gcx.hir.variable(f.returns[0]); + hir_ty_to_dyn(gcx, &var.ty) } + _ => Some(DynSolType::Tuple( + f.returns + .iter() + .filter_map(|&pid| hir_ty_to_dyn(gcx, &gcx.hir.variable(pid).ty)) + .collect(), + )), + }, + TypeKind::Mapping(m) => hir_ty_to_dyn(gcx, &m.value), + TypeKind::Custom(item) => solar_ty_to_dyn(gcx, gcx.type_of_item(*item)), + TypeKind::Err(_) => None, + } +} - // bool - pt::Expression::BoolLiteral(_, _) | - pt::Expression::And(_, _, _) | - pt::Expression::Or(_, _, _) | - pt::Expression::Equal(_, _, _) | - pt::Expression::NotEqual(_, _, _) | - pt::Expression::Less(_, _, _) | - pt::Expression::LessEqual(_, _, _) | - pt::Expression::More(_, _, _) | - pt::Expression::MoreEqual(_, _, _) | - pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)), - - // string - pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)), - - // bytes - pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)), - - // function - pt::Expression::FunctionCall(_, name, args) => { - Self::from_expression(name).map(|name| { - let args = args.iter().map(Self::from_expression).collect(); - Self::Function(Box::new(name), args, vec![]) - }) - } - pt::Expression::NamedFunctionCall(_, name, args) => { - Self::from_expression(name).map(|name| { - let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect(); - Self::Function(Box::new(name), args, vec![]) - }) - } +/// Resolves a member-access expression (`lhs.member`) to its [`DynSolType`]. +/// +/// `expr` must be `ExprKind::Member`. +fn resolve_member(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option { + let ExprKind::Member(lhs, ident) = &expr.kind else { return None }; + let member = ident.name; + + // `type(T).member` — type introspection. + if let ExprKind::TypeCall(ty) = &lhs.kind { + return match member.as_str() { + "name" => Some(DynSolType::String), + "creationCode" | "runtimeCode" => Some(DynSolType::Bytes), + "interfaceId" => Some(DynSolType::FixedBytes(4)), + // Only valid for integer types; custom types (enums) fall back to Uint(256). + "min" | "max" => match &ty.kind { + TypeKind::Elementary(et) => elementary_to_dyn(*et), + _ => Some(DynSolType::Uint(256)), + }, + _ => None, + }; + } - // explicitly None - pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None, - } + // Built-in namespace identifier: `block.timestamp`, `msg.sender`, `abi.encode`, etc. + if let ExprKind::Ident(reses) = &lhs.kind + && let Some(Res::Builtin(b)) = reses.first() + && let Some(ty) = builtin_member(b.name().as_str(), member.as_str()) + { + return Some(ty); } - /// Convert a [pt::Type] to a [Type] - /// - /// ### Takes - /// - /// A reference to a [pt::Type] to convert. - /// - /// ### Returns - /// - /// Optionally, an owned [Type] - fn from_type(ty: &pt::Type) -> Option { - let ty = match ty { - pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => { - Self::Builtin(DynSolType::Address) - } - pt::Type::Bool => Self::Builtin(DynSolType::Bool), - pt::Type::String => Self::Builtin(DynSolType::String), - pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)), - pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)), - pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)), - pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes), - pt::Type::Mapping { value, .. } => Self::from_expression(value)?, - pt::Type::Function { params, returns, .. } => { - let params = map_parameters(params); - let returns = returns - .as_ref() - .map(|(returns, _)| map_parameters(returns)) - .unwrap_or_default(); - Self::Function( - Box::new(Self::Custom(vec!["__fn_type__".to_string()])), - params, - returns, - ) - } - // TODO: Rational numbers - pt::Type::Rational => return None, + // Elementary type used as a namespace: `address.balance`, `bytes.concat`, etc. + if let ExprKind::Type(ty) = &lhs.kind + && let TypeKind::Elementary(et) = &ty.kind + { + return match et { + ElementaryType::Address(_) => match member.as_str() { + "balance" => Some(DynSolType::Uint(256)), + "code" => Some(DynSolType::Bytes), + "codehash" => Some(DynSolType::FixedBytes(32)), + "send" => Some(DynSolType::Bool), + _ => None, + }, + ElementaryType::Bytes => match member.as_str() { + "concat" => Some(DynSolType::Bytes), + _ => None, + }, + ElementaryType::String => match member.as_str() { + "concat" => Some(DynSolType::String), + _ => None, + }, + _ => None, }; - Some(ty) } - /// Handle special expressions like [global variables](https://docs.soliditylang.org/en/latest/cheatsheet.html#global-variables) - /// - /// See: - fn map_special(self) -> Self { - if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) { - return self; - } + // Members on a resolved DynSolType (`.length`, `.pop`, `.selector`, `.address`). + if let Some(lhs_ty) = expr_to_dyn(gcx, lhs, lookup) + && let Some(ty) = dyn_member(&lhs_ty, member.as_str()) + { + return Some(ty); + } - let mut types = Vec::with_capacity(5); - let mut args = None; - self.recurse(&mut types, &mut args); + // HIR lookup for user-defined type members. + if lookup && let Some(mut chain) = expr_name_chain(gcx, lhs) { + chain.insert(0, member); + return infer_custom_type(gcx, &mut chain, None).ok().flatten(); + } - let len = types.len(); - if len == 0 { - return self; - } + None +} + +/// Returns the type of `builtin_ns.member` for built-in global namespaces. +fn builtin_member(builtin: &str, member: &str) -> Option { + match builtin { + "block" => match member { + "coinbase" => Some(DynSolType::Address), + "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" | "chainid" + | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)), + _ => None, + }, + "msg" => match member { + "sender" => Some(DynSolType::Address), + "gas" | "value" => Some(DynSolType::Uint(256)), + "data" => Some(DynSolType::Bytes), + "sig" => Some(DynSolType::FixedBytes(4)), + _ => None, + }, + "tx" => match member { + "origin" => Some(DynSolType::Address), + "gasprice" => Some(DynSolType::Uint(256)), + _ => None, + }, + "address" => match member { + "balance" => Some(DynSolType::Uint(256)), + "code" => Some(DynSolType::Bytes), + "codehash" => Some(DynSolType::FixedBytes(32)), + "send" => Some(DynSolType::Bool), + _ => None, + }, + _ => None, + } +} + +/// Returns the type of `ty.member` for a known [`DynSolType`]. +fn dyn_member(ty: &DynSolType, member: &str) -> Option { + match member { + "length" => match ty { + DynSolType::Array(_) + | DynSolType::FixedArray(_, _) + | DynSolType::Bytes + | DynSolType::String + | DynSolType::FixedBytes(_) => Some(DynSolType::Uint(256)), + _ => None, + }, + "pop" => match ty { + DynSolType::Array(inner) => Some(*inner.clone()), + _ => None, + }, + // Address members. + "balance" => match ty { + DynSolType::Address => Some(DynSolType::Uint(256)), + _ => None, + }, + "code" => match ty { + DynSolType::Address => Some(DynSolType::Bytes), + _ => None, + }, + "codehash" => match ty { + DynSolType::Address => Some(DynSolType::FixedBytes(32)), + _ => None, + }, + "send" => match ty { + DynSolType::Address => Some(DynSolType::Bool), + _ => None, + }, + // External function members. + "selector" => Some(DynSolType::FixedBytes(4)), + "address" => Some(DynSolType::Address), + _ => None, + } +} - // Type members, like array, bytes etc - #[expect(clippy::single_match)] - #[allow(clippy::collapsible_match)] - match &self { - Self::Access(inner, access) => { - if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) { - // Array / bytes members - let ty = Self::Builtin(ty); - match access.as_str() { - "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => { - return Self::Builtin(DynSolType::Uint(256)); +/// Resolves a call expression to its return [`DynSolType`]. +/// +/// `expr` must be `ExprKind::Call`. +fn resolve_call(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option { + let ExprKind::Call(callee, args, _named) = &expr.kind else { return None }; + + // Type cast: `uint256(x)`, `address(y)`, etc. + if let ExprKind::Type(ty) = &callee.kind { + return hir_ty_to_dyn(gcx, ty); + } + + // Member call: `ns.method(...)`. + if let ExprKind::Member(lhs, method) = &callee.kind + && let ExprKind::Ident(reses) = &lhs.kind + && let Some(Res::Builtin(b)) = reses.first() + { + match b.name().as_str() { + "abi" => { + return match method.as_str() { + "decode" => { + let last = args.exprs().last()?; + match expr_to_dyn(gcx, last, false)? { + DynSolType::Tuple(tys) => Some(DynSolType::Tuple(tys)), + ty => Some(DynSolType::Tuple(vec![ty])), } - "pop" if ty.is_dynamic_array() => return ty, - _ => {} } - } + s if s.starts_with("encode") => Some(DynSolType::Bytes), + _ => None, + }; } + "string" if method.as_str() == "concat" => return Some(DynSolType::String), + "bytes" if method.as_str() == "concat" => return Some(DynSolType::Bytes), _ => {} } + } - let this = { - let name = types.last().unwrap().as_str(); - match len { - 0 => unreachable!(), - 1 => match name { + // Simple identifier call: built-in global functions and HIR function calls. + if let ExprKind::Ident(reses) = &callee.kind { + match reses.first() { + Some(Res::Builtin(b)) => { + return match b.name().as_str() { "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)), "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)), "ripemd160" => Some(DynSolType::FixedBytes(20)), "ecrecover" => Some(DynSolType::Address), _ => None, - }, - 2 => { - let access = types.first().unwrap().as_str(); - match name { - "block" => match access { - "coinbase" => Some(DynSolType::Address), - "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" - | "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)), - _ => None, - }, - "msg" => match access { - "sender" => Some(DynSolType::Address), - "gas" => Some(DynSolType::Uint(256)), - "value" => Some(DynSolType::Uint(256)), - "data" => Some(DynSolType::Bytes), - "sig" => Some(DynSolType::FixedBytes(4)), - _ => None, - }, - "tx" => match access { - "origin" => Some(DynSolType::Address), - "gasprice" => Some(DynSolType::Uint(256)), - _ => None, - }, - "abi" => match access { - "decode" => { - // args = Some([Bytes(_), Tuple(args)]) - // unwrapping is safe because this is first compiled by solc so - // it is guaranteed to be a valid call - let mut args = args.unwrap(); - let last = args.pop().unwrap(); - match last { - Some(ty) => { - return match ty { - Self::Tuple(_) => ty, - ty => Self::Tuple(vec![Some(ty)]), - }; - } - None => None, - } - } - s if s.starts_with("encode") => Some(DynSolType::Bytes), - _ => None, - }, - "address" => match access { - "balance" => Some(DynSolType::Uint(256)), - "code" => Some(DynSolType::Bytes), - "codehash" => Some(DynSolType::FixedBytes(32)), - "send" => Some(DynSolType::Bool), - _ => None, - }, - "type" => match access { - "name" => Some(DynSolType::String), - "creationCode" | "runtimeCode" => Some(DynSolType::Bytes), - "interfaceId" => Some(DynSolType::FixedBytes(4)), - "min" | "max" => Some( - // Either a builtin or an enum - (|| args?.pop()??.into_builtin())() - .unwrap_or(DynSolType::Uint(256)), - ), - _ => None, - }, - "string" => match access { - "concat" => Some(DynSolType::String), - _ => None, - }, - "bytes" => match access { - "concat" => Some(DynSolType::Bytes), - _ => None, - }, - _ => None, - } - } - _ => None, - } - }; - - this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() { - "this" | "super" => Self::Custom(types), - _ => match self { - Self::Custom(_) | Self::Access(_, _) => Self::Custom(types), - Self::Function(_, _, _) => self, - _ => unreachable!(), - }, - }) - } - - /// 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>>) { - match self { - Self::Builtin(ty) => types.push(ty.to_string()), - Self::Custom(tys) => types.extend(tys.clone()), - Self::Access(expr, name) => { - types.push(name.clone()); - expr.recurse(types, args); + }; } - Self::Function(fn_name, fn_args, _fn_ret) => { - if args.is_none() && !fn_args.is_empty() { - *args = Some(fn_args.clone()); + Some(Res::Item(ItemId::Function(fid))) if lookup => { + let func = gcx.hir.function(*fid); + if !matches!(func.state_mutability, StateMutability::View | StateMutability::Pure) { + return None; } - fn_name.recurse(types, args); + let ret_id = *func.returns.first()?; + return solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into())); } _ => {} } } - /// Infers a custom type's true type by recursing up the parse tree - /// - /// ### Takes - /// - A reference to the [IntermediateOutput] - /// - An array of custom types generated by the `MemberAccess` arm of [Self::from_expression] - /// - An optional contract name. This should always be `None` when this function is first - /// called. - /// - /// ### Returns - /// - /// If successful, an `Ok(Some(DynSolType))` variant. - /// If gracefully failed, an `Ok(None)` variant. - /// If failed, an `Err(e)` variant. - fn infer_custom_type( - intermediate: &IntermediateOutput, - custom_type: &mut Vec, - contract_name: Option, - ) -> Result> { - if let Some("this" | "super") = custom_type.last().map(String::as_str) { - custom_type.pop(); + // Fall back to the callee's resolved type. + expr_to_dyn(gcx, callee, lookup) +} + +/// Extracts a name chain from a member-access expression tree for HIR lookup. +/// +/// The chain is ordered outermost-first so `a.b.c` produces `["c", "b", "a"]` with the root +/// identifier at the back. This matches the convention expected by [`infer_custom_type`]. +fn expr_name_chain(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option> { + match &expr.kind { + ExprKind::Ident(reses) => { + let res = reses.first()?; + let name = match *res { + Res::Item(ItemId::Variable(vid)) => gcx.hir.variable(vid).name?.name, + Res::Item(ItemId::Function(fid)) => gcx.hir.function(fid).name?.name, + Res::Item(ItemId::Contract(cid)) => gcx.hir.contract(cid).name.name, + Res::Builtin(b) => b.name(), + _ => return None, + }; + Some(vec![name]) } - if custom_type.is_empty() { - return Ok(None); + ExprKind::Member(lhs, ident) => { + let mut chain = expr_name_chain(gcx, lhs)?; + chain.insert(0, ident.name); + Some(chain) } + _ => None, + } +} - // If a contract exists with the given name, check its definitions for a match. - // Otherwise look in the `run` - if let Some(contract_name) = contract_name { - let intermediate_contract = intermediate - .intermediate_contracts - .get(&contract_name) - .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?; - - let cur_type = custom_type.last().unwrap(); - if let Some(func) = intermediate_contract.function_definitions.get(cur_type) { - // Check if the custom type is a function pointer member access - if let res @ Some(_) = func_members(func, custom_type) { - return Ok(res); - } - - // Because tuple types cannot be passed to `abi.encode`, we will only be - // receiving functions that have 0 or 1 return parameters here. - if func.returns.is_empty() { - eyre::bail!( - "This call expression does not return any values to inspect. Insert as statement." - ) - } +/// Infers a custom type's true type by recursing through the HIR. +/// +/// `custom_type` is a name chain ordered outermost-first (root at back). This is mutated during +/// resolution. `contract_id` narrows the search to a specific contract scope. +fn infer_custom_type( + gcx: Gcx<'_>, + custom_type: &mut Vec, + contract_id: Option, +) -> Result> { + if let Some(last) = custom_type.last() + && (last.as_str() == "this" || last.as_str() == "super") + { + custom_type.pop(); + } + if custom_type.is_empty() { + return Ok(None); + } - // Empty return types check is done above - let (_, param) = func.returns.first().unwrap(); - // Return type should always be present - let return_ty = ¶m.as_ref().unwrap().ty; - - // If the return type is a variable (not a type expression), re-enter the recursion - // on the same contract for a variable / struct search. It could be a contract, - // struct, array, etc. - if let pt::Expression::Variable(ident) = return_ty { - custom_type.push(ident.name.clone()); - return Self::infer_custom_type(intermediate, custom_type, Some(contract_name)); - } + if let Some(cid) = contract_id { + let hir = &gcx.hir; + let contract = hir.contract(cid); - // Check if our final function call alters the state. If it does, we bail so that it - // will be inserted normally without inspecting. If the state mutability was not - // expressly set, the function is inferred to alter state. - if let Some(pt::FunctionAttribute::Mutability(_mut)) = func - .attributes - .iter() - .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_))) - { - if let pt::Mutability::Payable(_) = _mut { - eyre::bail!("This function mutates state. Insert as a statement.") - } - } else { - eyre::bail!("This function mutates state. Insert as a statement.") - } + let cur_name = *custom_type.last().unwrap(); + let cur = cur_name.as_str(); - Ok(Self::ethabi(return_ty, Some(intermediate))) - } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) { - Self::infer_var_expr(&var.ty, Some(intermediate), custom_type) - } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) { - let inner_types = strukt - .fields - .iter() - .map(|var| { - Self::ethabi(&var.ty, Some(intermediate)) - .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields")) - }) - .collect::>>()?; - Ok(Some(DynSolType::Tuple(inner_types))) - } else { - eyre::bail!( - "Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}" - ) - } - } else { - // Check if the custom type is a variable or function within the REPL contract before - // anything. If it is, we can stop here. - if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into())) - { + // Function? + if let Some(fid) = contract + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str() == cur).unwrap_or(false)) + { + let func = hir.function(fid); + if let res @ Some(_) = func_members(func, custom_type) { return Ok(res); } - // Check if the first element of the custom type is a known contract. If it is, begin - // our recursion on that contract's definitions. - let name = custom_type.last().unwrap(); - let contract = intermediate.intermediate_contracts.get(name); - if contract.is_some() { - let contract_name = custom_type.pop(); - return Self::infer_custom_type(intermediate, custom_type, contract_name); + if func.returns.is_empty() { + eyre::bail!( + "This call expression does not return any values to inspect. Insert as statement." + ) } - // See [`Type::infer_var_expr`] - let name = custom_type.last().unwrap(); - if let Some(expr) = intermediate.repl_contract_expressions.get(name) { - return Self::infer_var_expr(expr, Some(intermediate), custom_type); + let sm = func.state_mutability; + if !matches!(sm, StateMutability::View | StateMutability::Pure) { + eyre::bail!("This function mutates state. Insert as a statement.") } - // The first element of our custom type was neither a variable or a function within the - // REPL contract, move on to globally available types gracefully. - Ok(None) + let ret_id = func.returns[0]; + let ret_var = hir.variable(ret_id); + return Ok(solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into())) + .or_else(|| hir_ty_to_dyn(gcx, &ret_var.ty))); } - } - /// Infers the type from a variable's type - fn infer_var_expr( - expr: &pt::Expression, - intermediate: Option<&IntermediateOutput>, - custom_type: &mut Vec, - ) -> Result> { - // Resolve local (in `run` function) or global (in the `REPL` or other contract) variable - let res = match &expr { - // Custom variable handling - pt::Expression::Variable(ident) => { - let name = &ident.name; - - if let Some(intermediate) = intermediate { - // expression in `run` - if let Some(expr) = intermediate.repl_contract_expressions.get(name) { - Self::infer_var_expr(expr, Some(intermediate), custom_type) - } else if intermediate.intermediate_contracts.contains_key(name) { - if custom_type.len() > 1 { - // There is still some recursing left to do: jump into the contract. - custom_type.pop(); - Self::infer_custom_type(intermediate, custom_type, Some(name.clone())) - } else { - // We have no types left to recurse: return the address of the contract. - Ok(Some(DynSolType::Address)) - } - } else { - Err(eyre::eyre!("Could not infer variable type")) - } - } else { - Ok(None) - } - } - other_expr => Ok(Self::ethabi(other_expr, intermediate)), - }; - // re-run everything with the resolved variable in case we're accessing a builtin member - // for example array or bytes length etc - match res { - Ok(Some(ty)) => { - let box_ty = Box::new(Self::Builtin(ty.clone())); - let access = Self::Access(box_ty, custom_type.drain(..).next().unwrap_or_default()); - if let Some(mapped) = access.map_special().try_as_ethabi(intermediate) { - Ok(Some(mapped)) - } else { - Ok(Some(ty)) + // Variable? + if let Some(vid) = contract + .variables() + .find(|&v| hir.variable(v).name.as_ref().map(|n| n.as_str() == cur).unwrap_or(false)) + { + if let Some(ty) = solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())) { + custom_type.pop(); + if custom_type.is_empty() { + return Ok(Some(ty)); } + let next_member = custom_type.drain(..).next().unwrap_or(Symbol::DUMMY); + return Ok(dyn_member(&ty, next_member.as_str()).or(Some(ty))); } - res => res, - } - } - - /// Attempt to convert this type into a [DynSolType] - /// - /// ### Takes - /// An immutable reference to an [IntermediateOutput] - /// - /// ### Returns - /// Optionally, a [DynSolType] - fn try_as_ethabi(self, intermediate: Option<&IntermediateOutput>) -> Option { - match self { - Self::Builtin(ty) => Some(ty), - Self::Tuple(types) => Some(DynSolType::Tuple(types_to_parameters(types, intermediate))), - Self::Array(inner) => match *inner { - ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate), - _ => inner - .try_as_ethabi(intermediate) - .map(|inner| DynSolType::Array(Box::new(inner))), - }, - Self::FixedArray(inner, size) => match *inner { - ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate), - _ => inner - .try_as_ethabi(intermediate) - .map(|inner| DynSolType::FixedArray(Box::new(inner), size)), - }, - ty @ Self::ArrayIndex(_, _) => ty.into_array_index(intermediate), - Self::Function(ty, _, _) => ty.try_as_ethabi(intermediate), - // should have been mapped to `Custom` in previous steps - Self::Access(_, _) => None, - Self::Custom(mut types) => { - // Cover any local non-state-modifying function call expressions - intermediate.and_then(|intermediate| { - Self::infer_custom_type(intermediate, &mut types, None).ok().flatten() - }) - } + let var = hir.variable(vid); + return infer_var_ty(gcx, &var.ty, custom_type); } - } - - /// Equivalent to `Type::from_expression` + `Type::map_special` + `Type::try_as_ethabi` - fn ethabi( - expr: &pt::Expression, - intermediate: Option<&IntermediateOutput>, - ) -> Option { - Self::from_expression(expr) - .map(Self::map_special) - .and_then(|ty| ty.try_as_ethabi(intermediate)) - } - /// Get the return type of a function call expression. - fn get_function_return_type<'a>( - contract_expr: Option<&'a pt::Expression>, - intermediate: &IntermediateOutput, - ) -> Option<(&'a pt::Expression, DynSolType)> { - let function_call = match contract_expr? { - pt::Expression::FunctionCall(_, function_call, _) => function_call, - _ => return None, - }; - let (contract_name, function_name) = match function_call.as_ref() { - pt::Expression::MemberAccess(_, contract_name, function_name) => { - (contract_name, function_name) + // Struct? + if let Some(sid) = contract.items.iter().find_map(|i| { + if let ItemId::Struct(sid) = i + && hir.strukt(*sid).name.as_str() == cur + { + Some(*sid) + } else { + None } - _ => return None, - }; - let contract_name = match contract_name.as_ref() { - pt::Expression::Variable(contract_name) => contract_name.to_owned(), - _ => return None, - }; - - let pt::Expression::Variable(contract_name) = - intermediate.repl_contract_expressions.get(&contract_name.name)? - else { - return None; - }; - - let contract = intermediate - .intermediate_contracts - .get(&contract_name.name)? - .function_definitions - .get(&function_name.name)?; - let return_parameter = contract.as_ref().returns.first()?.to_owned().1?; - Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p)) - } - - /// Inverts Int to Uint and vice-versa. - fn invert_int(self) -> Self { - match self { - Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)), - Self::Builtin(DynSolType::Int(n)) => Self::Builtin(DynSolType::Uint(n)), - x => x, + }) { + let inner = gcx + .struct_field_types(sid) + .iter() + .map(|&t| { + solar_ty_to_dyn(gcx, t) + .ok_or_else(|| eyre::eyre!("Struct `{cur}` has invalid fields")) + }) + .collect::>>()?; + return Ok(Some(DynSolType::Tuple(inner))); } - } - /// Returns the `DynSolType` contained by `Type::Builtin` - #[inline] - fn into_builtin(self) -> Option { - match self { - Self::Builtin(ty) => Some(ty), - _ => None, - } + eyre::bail!( + "Could not find any definition in contract \"{}\" for type: {custom_type:?}", + contract.name.as_str() + ) } - /// Returns the resulting `DynSolType` of indexing self - fn into_array_index(self, intermediate: Option<&IntermediateOutput>) -> Option { - match self { - Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => { - match inner.try_as_ethabi(intermediate) { - Some(DynSolType::Array(inner) | DynSolType::FixedArray(inner, _)) => { - Some(*inner) - } - Some(DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_)) => { - Some(DynSolType::FixedBytes(1)) - } - ty => ty, - } - } - _ => None, - } + let repl_id = gcx + .hir + .contracts_enumerated() + .find_map(|(cid, c)| (c.name.as_str() == "REPL").then_some(cid)); + if let Some(repl_id) = repl_id + && let Ok(res) = infer_custom_type(gcx, custom_type, Some(repl_id)) + { + return Ok(res); } - /// Returns whether this type is dynamic - #[inline] - const fn is_dynamic(&self) -> bool { - match self { - // 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, - } + let last_name = *custom_type.last().unwrap(); + let last = last_name.as_str(); + let contract_match = gcx + .hir + .contracts_enumerated() + .find_map(|(cid, c)| (c.name.as_str() == last).then_some(cid)); + if let Some(cid) = contract_match { + custom_type.pop(); + return infer_custom_type(gcx, custom_type, Some(cid)); } - /// Returns whether this type is an array - #[inline] - const fn is_array(&self) -> bool { - matches!( - self, - Self::Array(_) - | Self::FixedArray(_, _) - | Self::Builtin(DynSolType::Array(_) | DynSolType::FixedArray(_, _)) - ) - } + Ok(None) +} - /// Returns whether this type is a dynamic array (can call push, pop) - #[inline] - const fn is_dynamic_array(&self) -> bool { - matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_))) +/// Infers the type from a variable's HIR type, optionally accessing a named member. +fn infer_var_ty( + gcx: Gcx<'_>, + ty: &HirType<'_>, + custom_type: &mut Vec, +) -> Result> { + let Some(ty) = hir_ty_to_dyn(gcx, ty) else { return Ok(None) }; + let next_member = custom_type.drain(..).next(); + if let Some(m) = next_member { + Ok(dyn_member(&ty, m.as_str()).or(Some(ty))) + } else { + Ok(Some(ty)) } +} - const fn is_fixed_bytes(&self) -> bool { - matches!(self, Self::Builtin(DynSolType::FixedBytes(_))) - } +/// Get the return type of a contract method call `receiver.method()`. +fn get_function_return_type(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option { + let ExprKind::Call(callee, _, _) = &expr.kind else { return None }; + let ExprKind::Member(obj, fn_ident) = &callee.kind else { return None }; + let ExprKind::Ident(reses) = &obj.kind else { return None }; + let res = reses.first()?; + let var_id = match res { + Res::Item(ItemId::Variable(vid)) => *vid, + _ => return None, + }; + let var_ty = gcx.type_of_item(var_id.into()).peel_refs(); + let cid = match var_ty.kind { + TyKind::Contract(cid) => cid, + _ => return None, + }; + + let hir = &gcx.hir; + let contract = hir.contract(cid); + let fid = contract + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some(fn_ident.as_str()))?; + let func = hir.function(fid); + let ret_id = *func.returns.first()?; + solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into())) } -/// Returns Some if the custom type is a function member access +/// Returns Some if the custom type is a function member access. /// /// Ref: #[inline] -fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option { - if !matches!(func.ty, pt::FunctionTy::Function) { +fn func_members(func: &Function<'_>, custom_type: &[Symbol]) -> Option { + if !matches!(func.kind, FunctionKind::Function) { return None; } - - let vis = func.attributes.iter().find_map(|attr| match attr { - pt::FunctionAttribute::Visibility(vis) => Some(vis), - _ => None, - }); - match vis { - Some(pt::Visibility::External(_) | pt::Visibility::Public(_)) => { - match custom_type.first().unwrap().as_str() { - "address" => Some(DynSolType::Address), - "selector" => Some(DynSolType::FixedBytes(4)), - _ => None, - } - } + if !matches!(func.visibility, Visibility::External | Visibility::Public) { + return None; + } + match custom_type.first().unwrap().as_str() { + "address" => Some(DynSolType::Address), + "selector" => Some(DynSolType::FixedBytes(4)), _ => None, } } -/// Whether execution should continue after inspecting this expression +/// Whether execution should continue after inspecting this expression. #[inline] -fn should_continue(expr: &pt::Expression) -> bool { - match expr { - // assignments - pt::Expression::PreDecrement(_, _) | // -- - pt::Expression::PostDecrement(_, _) | // -- - pt::Expression::PreIncrement(_, _) | // ++ - pt::Expression::PostIncrement(_, _) | // ++ - pt::Expression::Assign(_, _, _) | // = ... - pt::Expression::AssignAdd(_, _, _) | // += ... - pt::Expression::AssignSubtract(_, _, _) | // -= ... - pt::Expression::AssignMultiply(_, _, _) | // *= ... - pt::Expression::AssignDivide(_, _, _) | // /= ... - pt::Expression::AssignModulo(_, _, _) | // %= ... - pt::Expression::AssignAnd(_, _, _) | // &= ... - pt::Expression::AssignOr(_, _, _) | // |= ... - pt::Expression::AssignXor(_, _, _) | // ^= ... - pt::Expression::AssignShiftLeft(_, _, _) | // <<= ... - pt::Expression::AssignShiftRight(_, _, _) // >>= ... - => { - true - } - +fn should_continue(expr: &Expr<'_>) -> bool { + match &expr.kind { + // assignments and compound assignments + ExprKind::Assign(_, _, _) => true, + // ++/-- pre/post operations + ExprKind::Unary(op, _) => matches!( + op.kind, + UnOpKind::PreInc | UnOpKind::PreDec | UnOpKind::PostInc | UnOpKind::PostDec + ), // Array.pop() - pt::Expression::FunctionCall(_, lhs, _) => { - match lhs.as_ref() { - pt::Expression::MemberAccess(_, _inner, access) => access.name == "pop", - _ => false - } - } - - _ => false + ExprKind::Call(callee, _, _) => match &callee.kind { + ExprKind::Member(_, ident) => ident.as_str() == "pop", + _ => false, + }, + _ => false, } } -fn map_parameters(params: &[(pt::Loc, Option)]) -> Vec> { - params - .iter() - .map(|(_, param)| param.as_ref().and_then(|param| Type::from_expression(¶m.ty))) - .collect() +/// Parses an [`Expr`] number/hex literal into a `U256`. Returns `None` if the expression +/// is not a numeric literal. +/// +/// SubDenominations are already applied to numeric literals in solar's HIR. +const fn parse_number_literal(expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::Lit(lit) => match &lit.kind { + LitKind::Number(n) => Some(*n), + _ => None, + }, + _ => None, + } } -fn types_to_parameters( - types: Vec>, - intermediate: Option<&IntermediateOutput>, -) -> Vec { - types.into_iter().filter_map(|ty| ty.and_then(|ty| ty.try_as_ethabi(intermediate))).collect() +/// Maps a solar [`ElementaryType`] to a [`DynSolType`]. +const fn elementary_to_dyn(et: ElementaryType) -> Option { + Some(match et { + ElementaryType::Address(_) => DynSolType::Address, + ElementaryType::Bool => DynSolType::Bool, + ElementaryType::String => DynSolType::String, + ElementaryType::Bytes => DynSolType::Bytes, + ElementaryType::Int(size) => DynSolType::Int(size.bits() as usize), + ElementaryType::UInt(size) => DynSolType::Uint(size.bits() as usize), + ElementaryType::FixedBytes(size) => DynSolType::FixedBytes(size.bytes() as usize), + // Fixed-point numbers are not yet representable as DynSolType. + ElementaryType::Fixed(_, _) | ElementaryType::UFixed(_, _) => return None, + }) } -fn parse_number_literal(expr: &pt::Expression) -> Option { - match expr { - pt::Expression::NumberLiteral(_, num, exp, unit) => { - let num = num.parse::().unwrap_or(U256::ZERO); - let exp = exp.parse().unwrap_or(0u32); - if exp > 77 { - None +/// Maps a solar [`Ty`] to a [`DynSolType`]. +fn solar_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> Option { + match ty.kind { + TyKind::Elementary(et) => elementary_to_dyn(et), + TyKind::Ref(inner, _) => solar_ty_to_dyn(gcx, inner), + TyKind::Array(elem, n) => { + let inner = solar_ty_to_dyn(gcx, elem)?; + let size: usize = n.try_into().ok()?; + Some(DynSolType::FixedArray(Box::new(inner), size)) + } + TyKind::DynArray(elem) | TyKind::Slice(elem) => { + let inner = solar_ty_to_dyn(gcx, elem)?; + Some(DynSolType::Array(Box::new(inner))) + } + TyKind::Tuple(tys) => { + Some(DynSolType::Tuple(tys.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect())) + } + TyKind::Mapping(_, _) => None, + TyKind::Struct(sid) => Some(DynSolType::Tuple( + gcx.struct_field_types(sid).iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(), + )), + TyKind::Enum(_) => Some(DynSolType::Uint(8)), + TyKind::Udvt(inner, _) => solar_ty_to_dyn(gcx, inner), + TyKind::Contract(_) => Some(DynSolType::Address), + // For a function-pointer type we return the ABI type of what the call *produces*, not a + // representation of the pointer itself. This is intentional: chisel inspects values, so + // the interesting type is the returned value. A zero-return function pointer has no + // inspectable value, so we return `None`. + TyKind::FnPtr(f) => match f.returns.len() { + 0 => None, + 1 => solar_ty_to_dyn(gcx, f.returns[0]), + _ => Some(DynSolType::Tuple( + f.returns.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(), + )), + }, + TyKind::Type(inner) => solar_ty_to_dyn(gcx, inner), + TyKind::Meta(inner) => solar_ty_to_dyn(gcx, inner), + TyKind::IntLiteral(neg, size) => { + let bits = (size.bits() as usize).max(8); + // Round up to the nearest multiple of 8 bits, capped at 256. + let bits = bits.div_ceil(8) * 8; + let bits = bits.min(256); + if neg { + Some(DynSolType::Int(bits.max(8))) } else { - let exp = U256::from(10usize.pow(exp)); - let unit_mul = unit_multiplier(unit).ok()?; - Some(num * exp * unit_mul) + Some(DynSolType::Uint(bits.max(8))) } } - pt::Expression::HexNumberLiteral(_, num, unit) => { - let unit_mul = unit_multiplier(unit).ok()?; - num.parse::().map(|num| num * unit_mul).ok() + TyKind::StringLiteral(valid_utf8, _) => { + if valid_utf8 { + Some(DynSolType::String) + } else { + Some(DynSolType::Bytes) + } } - // TODO: Rational numbers - pt::Expression::RationalNumberLiteral(..) => None, + TyKind::Module(_) + | TyKind::BuiltinModule(_) + | TyKind::Error(_, _) + | TyKind::Event(_, _) + | TyKind::Err(_) => None, _ => None, } } -#[inline] -fn unit_multiplier(unit: &Option) -> Result { - if let Some(unit) = unit { - let mul = match unit.name.as_str() { - "seconds" => 1, - "minutes" => 60, - "hours" => 60 * 60, - "days" => 60 * 60 * 24, - "weeks" => 60 * 60 * 24 * 7, - "wei" => 1, - "gwei" => 10_usize.pow(9), - "ether" => 10_usize.pow(18), - other => eyre::bail!("unknown unit: {other}"), - }; - Ok(U256::from(mul)) - } else { - Ok(U256::from(1)) - } -} - #[cfg(test)] mod tests { use super::*; use foundry_compilers::{error::SolcError, solc::Solc}; + use solar::sema::Compiler; use std::sync::Mutex; #[test] @@ -1558,46 +1455,66 @@ mod tests { DynSolType::FixedArray(Box::new(ty), len) } - fn parse(s: &mut SessionSource, input: &str, clear: bool) -> IntermediateOutput { + /// Lowers the given snippet appended to the REPL contract via solar's HIR pipeline (without + /// invoking solc) and returns the resulting `DynSolType` of the last expression statement in + /// the run() body. + /// + /// Tests bypass `SessionSource::build` (which routes through foundry-compilers + solc) so that + /// inputs which are syntactically valid but semantically rejected by solc (e.g. + /// `abi.decode(bytes, (uint8[13]))` or `a[0:3]` on a memory array) can still exercise the + /// HIR-based type-inference engine. + fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option { if clear { s.clear(); } + // Always declare a sample enum so `Enum1` is available for `type(Enum1)` tests. *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0; let input = format!("{};", input.trim_end().trim_end_matches(';')); - let (mut _s, _) = s.clone_with_new_line(input).unwrap(); - *s = _s.clone(); - let s = &mut _s; - - if let Err(e) = s.parse() { - let source = s.to_repl_source(); - panic!("{e}\n\ncould not parse input:\n{source}") - } - s.generate_intermediate_output().expect("could not generate intermediate output") - } - - fn expr(stmts: &[pt::Statement]) -> pt::Expression { - match stmts.last().expect("no statements") { - pt::Statement::Expression(_, e) => e.clone(), - s => panic!("Not an expression: {s:?}"), - } - } - - fn get_type( - s: &mut SessionSource, - input: &str, - clear: bool, - ) -> (Option, IntermediateOutput) { - let intermediate = parse(s, input, clear); - let run_func_body = intermediate.run_func_body().expect("no run func body"); - let expr = expr(run_func_body); - (Type::from_expression(&expr).map(Type::map_special), intermediate) - } + let (new_source, _) = s.clone_with_new_line(input).unwrap(); + *s = new_source.clone(); + + let src = new_source.to_repl_source(); + let sess = + solar::interface::Session::builder().with_buffer_emitter(Default::default()).build(); + let mut compiler = Compiler::new(sess); + + compiler.enter_mut(|c| -> Option { + // Stage 1: parse + lower (mutable access required). + let lowered = { + let mut pcx = c.parse(); + let file = c + .sess() + .source_map() + .new_source_file( + std::path::PathBuf::from(new_source.file_name.clone()), + src.clone(), + ) + .ok()?; + pcx.add_file(file); + pcx.parse(); + matches!(c.lower_asts(), Ok(ControlFlow::Continue(()))) + }; + if !lowered { + return None; + } - fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option { - let (ty, intermediate) = get_type(s, input, clear); - ty.and_then(|ty| ty.try_as_ethabi(Some(&intermediate))) + // Stage 2: walk HIR (immutable access). + let gcx = c.gcx(); + let hir = &gcx.hir; + let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?; + let run_fid = repl + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?; + let body = hir.function(run_fid).body?; + let last = body.last()?; + let expr = match last.kind { + StmtKind::Expr(e) => e, + _ => return None, + }; + expr_to_dyn(gcx, expr, true) + }) } fn generic_type_test<'a, T, I>(s: &mut SessionSource, input: I) diff --git a/crates/chisel/src/source.rs b/crates/chisel/src/source.rs index 8133de364c1da..90c0bad874622 100644 --- a/crates/chisel/src/source.rs +++ b/crates/chisel/src/source.rs @@ -5,7 +5,6 @@ //! execution helpers. use eyre::Result; -use foundry_common::fs; use foundry_compilers::{ Artifact, ProjectCompileOutput, artifacts::{ConfigurableContractArtifact, Source, Sources}, @@ -16,9 +15,16 @@ use foundry_config::{Config, SolcReq}; use foundry_evm::{backend::Backend, core::bytecode::InstIter, opts::EvmOpts}; use semver::Version; use serde::{Deserialize, Serialize}; -use solang_parser::pt::{self, CodeLocation}; -use solar::interface::diagnostics::EmittedDiagnostics; -use std::{cell::OnceCell, collections::HashMap, fmt, path::PathBuf}; +use solar::{ + ast::{ItemKind, StmtKind as AstStmtKind, yul}, + interface::{Span, diagnostics::EmittedDiagnostics}, + sema::{ + CompilerRef, + hir::{Block, Contract, EventId, ItemId, Stmt, StmtKind as HirStmtKind}, + ty::Gcx, + }, +}; +use std::{cell::OnceCell, fmt}; use walkdir::WalkDir; /// The minimum Solidity version of the `Vm` interface. @@ -30,41 +36,8 @@ static VM_SOURCE: &str = include_str!("../../../testdata/utils/Vm.sol"); /// [`SessionSource`] build output. pub struct GeneratedOutput { output: ProjectCompileOutput, - pub(crate) intermediate: IntermediateOutput, -} - -pub struct GeneratedOutputRef<'a> { - output: &'a ProjectCompileOutput, - // compiler: &'b solar::sema::CompilerRef<'c>, - pub(crate) intermediate: &'a IntermediateOutput, -} - -/// Intermediate output for the compiled [SessionSource] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct IntermediateOutput { - /// All expressions within the REPL contract's run function and top level scope. - pub repl_contract_expressions: HashMap, - /// Intermediate contracts - pub intermediate_contracts: IntermediateContracts, -} - -/// A refined intermediate parse tree for a contract that enables easy lookups -/// of definitions. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct IntermediateContract { - /// All function definitions within the contract - pub function_definitions: HashMap>, - /// All event definitions within the contract - pub event_definitions: HashMap>, - /// All struct definitions within the contract - pub struct_definitions: HashMap>, - /// All variable definitions within the top level scope of the contract - pub variable_definitions: HashMap>, } -/// A defined type for a map of contract names to [IntermediateContract]s -type IntermediateContracts = HashMap; - impl fmt::Debug for GeneratedOutput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("GeneratedOutput").finish_non_exhaustive() @@ -72,158 +45,25 @@ impl fmt::Debug for GeneratedOutput { } impl GeneratedOutput { - pub fn enter(&self, f: impl FnOnce(GeneratedOutputRef<'_>) -> T + Send) -> T { - // TODO(dani): once intermediate is removed - // self.output - // .parser() - // .solc() - // .compiler() - // .enter(|compiler| f(GeneratedOutputRef { output: &self.output, compiler })) - f(GeneratedOutputRef { output: &self.output, intermediate: &self.intermediate }) - } -} - -impl GeneratedOutputRef<'_> { - pub fn repl_contract(&self) -> Option<&ConfigurableContractArtifact> { - self.output.find_first("REPL") - } -} - -impl std::ops::Deref for GeneratedOutput { - type Target = IntermediateOutput; - fn deref(&self) -> &Self::Target { - &self.intermediate - } -} -impl std::ops::Deref for GeneratedOutputRef<'_> { - type Target = IntermediateOutput; - fn deref(&self) -> &Self::Target { - self.intermediate + /// Enters the solar compiler context, providing access to the HIR and `Gcx`. + pub fn enter( + &self, + f: impl for<'a, 'b, 'gcx> FnOnce(GeneratedOutputRef<'a, 'b, 'gcx>) -> R + Send, + ) -> R { + self.output + .parser() + .solc() + .compiler() + .enter(|c| f(GeneratedOutputRef { output: &self.output, compiler: c })) } } -impl IntermediateOutput { - pub fn get_event(&self, input: &str) -> Option<&pt::EventDefinition> { - self.intermediate_contracts - .get("REPL") - .and_then(|contract| contract.event_definitions.get(input).map(std::ops::Deref::deref)) - } - - pub fn final_pc(&self, contract: &ConfigurableContractArtifact) -> Result> { - let deployed_bytecode = contract - .get_deployed_bytecode() - .ok_or_else(|| eyre::eyre!("No deployed bytecode found for `REPL` contract"))?; - let deployed_bytecode_bytes = deployed_bytecode - .bytes() - .ok_or_else(|| eyre::eyre!("No deployed bytecode found for `REPL` contract"))?; - - let run_func_statements = self.run_func_body()?; - - // Record loc of first yul block return statement (if any). - // This is used to decide which is the final statement within the `run()` method. - // see . - let last_yul_return = run_func_statements.iter().find_map(|statement| { - if let pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } = statement - && let Some(statement) = block.statements.last() - && let pt::YulStatement::FunctionCall(yul_call) = statement - && yul_call.id.name == "return" - { - return Some(statement.loc()); - } - None - }); - - // Find the last statement within the "run()" method and get the program - // counter via the source map. - let Some(final_statement) = run_func_statements.last() else { return Ok(None) }; - - // If the final statement is some type of block (assembly, unchecked, or regular), - // we need to find the final statement within that block. Otherwise, default to - // the source loc of the final statement of the `run()` function's block. - // - // There is some code duplication within the arms due to the difference between - // the [pt::Statement] type and the [pt::YulStatement] types. - let mut source_loc = match final_statement { - pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => { - // Select last non variable declaration statement, see . - let last_statement = block.statements.iter().rev().find(|statement| { - !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _)) - }); - if let Some(statement) = last_statement { - statement.loc() - } else { - // In the case where the block is empty, attempt to grab the statement - // before the asm block. Because we use saturating sub to get the second - // to last index, this can always be safely unwrapped. - run_func_statements - .get(run_func_statements.len().saturating_sub(2)) - .unwrap() - .loc() - } - } - pt::Statement::Block { loc: _, unchecked: _, statements } => { - if let Some(statement) = statements.last() { - statement.loc() - } else { - // In the case where the block is empty, attempt to grab the statement - // before the block. Because we use saturating sub to get the second to - // last index, this can always be safely unwrapped. - run_func_statements - .get(run_func_statements.len().saturating_sub(2)) - .unwrap() - .loc() - } - } - _ => final_statement.loc(), - }; - - // Consider yul return statement as final statement (if it's loc is lower) . - if let Some(yul_return) = last_yul_return - && yul_return.end() < source_loc.start() - { - source_loc = yul_return; - } - - // 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() as u32; - let length = (source_loc.end() - source_loc.start()) as u32; - trace!(%offset, %length, "find pc"); - contract - .get_source_map_deployed() - .unwrap() - .unwrap() - .into_iter() - .zip(InstIter::new(deployed_bytecode_bytes).with_pc().map(|(pc, _)| pc)) - .filter(|(s, _)| s.offset() == offset && s.length() == length) - .map(|(_, pc)| pc) - .max() - }; - trace!(?final_pc); - Ok(final_pc) - } - - pub fn run_func_body(&self) -> Result<&Vec> { - match self - .intermediate_contracts - .get("REPL") - .ok_or_else(|| eyre::eyre!("Could not find REPL intermediate contract!"))? - .function_definitions - .get("run") - .ok_or_else(|| eyre::eyre!("Could not find run function definition in REPL contract!"))? - .body - .as_ref() - .ok_or_else(|| eyre::eyre!("Could not find run function body!"))? - { - pt::Statement::Block { statements, .. } => Ok(statements), - _ => eyre::bail!("Could not find statements within run function body!"), - } - } +/// A scoped reference to a [`GeneratedOutput`] together with an entered solar compiler. +pub struct GeneratedOutputRef<'a, 'b, 'gcx> { + output: &'a ProjectCompileOutput, + pub(crate) compiler: &'b CompilerRef<'gcx>, } -// TODO(dani): further migration blocked on upstream work -#[cfg(false)] impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { pub fn gcx(&self) -> Gcx<'gcx> { self.compiler.gcx() @@ -233,8 +73,35 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { self.output.find_first("REPL") } - pub fn get_event(&self, input: &str) -> Option { - self.gcx().hir.events_enumerated().find(|(_, e)| e.name.as_str() == input).map(|(id, _)| id) + /// Looks up the REPL contract in the HIR. + pub fn repl_contract_hir(&self) -> Option<&'gcx Contract<'gcx>> { + self.gcx().hir.contracts().find(|c| c.name.as_str() == "REPL") + } + + /// Returns the body block of the REPL `run()` function. + pub fn run_func_body(&self) -> Block<'gcx> { + let hir = &self.gcx().hir; + let c = self.repl_contract_hir().expect("REPL contract not found in HIR"); + let f = c + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run")) + .expect("`run()` function not found in REPL contract"); + hir.function(f).body.expect("`run()` function does not have a body") + } + + /// Returns the [`EventId`] of an event named `input` in the REPL contract, if any. + pub fn get_event(&self, input: &str) -> Option { + let hir = &self.gcx().hir; + let c = self.repl_contract_hir()?; + c.items.iter().find_map(|id| { + if let ItemId::Event(eid) = id + && hir.event(*eid).name.as_str() == input + { + Some(*eid) + } else { + None + } + }) } pub fn final_pc(&self, contract: &ConfigurableContractArtifact) -> Result> { @@ -251,52 +118,25 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { // Record loc of first yul block return statement (if any). // This is used to decide which is the final statement within the `run()` method. // see . - let last_yul_return_span: Option = run_body.iter().find_map(|stmt| { - // TODO(dani): Yul is not yet lowered to HIR. - let _ = stmt; - /* - if let hir::StmtKind::Assembly { block, .. } = stmt { - if let Some(stmt) = block.last() { - if let pt::YulStatement::FunctionCall(yul_call) = stmt { - if yul_call.id.name == "return" { - return Some(stmt.loc()) - } - } - } - } - */ - None - }); + // + // Yul is not yet lowered to HIR (assembly statements appear as `StmtKind::Err`), + // so we walk the AST of the REPL source to find a top-level `return(...)` call + // inside any `assembly { ... }` block in `run()`. + let last_yul_return_span: Option = self.first_yul_return_span(); // Find the last statement within the "run()" method and get the program // counter via the source map. let Some(last_stmt) = run_body.last() else { return Ok(None) }; - // If the final statement is some type of block (assembly, unchecked, or regular), + // If the final statement is some type of block (unchecked or regular), // we need to find the final statement within that block. Otherwise, default to // the source loc of the final statement of the `run()` function's block. // - // There is some code duplication within the arms due to the difference between - // the [pt::Statement] type and the [pt::YulStatement] types. + // Inline assembly blocks (lowered to `StmtKind::Err` in HIR in the pinned solar + // version) are handled separately via `trailing_assembly_last_stmt_span`, which + // walks the AST to recover the last meaningful Yul statement. let source_stmt = match &last_stmt.kind { - // TODO(dani): Yul is not yet lowered to HIR. - /* - pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => { - // Select last non variable declaration statement, see . - let last_statement = block.statements.iter().rev().find(|statement| { - !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _)) - }); - if let Some(stmt) = last_statement { - stmt - } else { - // In the case where the block is empty, attempt to grab the statement - // before the block. Because we use saturating sub to get the second to - // last index, this can always be safely unwrapped. - &run_body[run_body.len().saturating_sub(2)] - } - } - */ - hir::StmtKind::UncheckedBlock(stmts) | hir::StmtKind::Block(stmts) => { + HirStmtKind::UncheckedBlock(stmts) | HirStmtKind::Block(stmts) => { if let Some(stmt) = stmts.last() { stmt } else { @@ -308,9 +148,25 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { } _ => last_stmt, }; - let mut source_span = self.stmt_span_without_semicolon(source_stmt); + // If the trailing statement is an assembly block, prefer the last meaningful + // (non-`let`) Yul statement's span as the source location for `final_pc`. + // See . + // + // Two guards are required: + // 1. `StmtKind::Err`, assembly lowers to an error node in the current pinned solar + // version; this ensures we don't apply the AST fallback to properly-lowered stmts. + // 2. `trailing_assembly_last_stmt_span` returning `Some`, verifies via the AST that the + // failing HIR node actually corresponds to an assembly block (not some other lowering + // failure), and supplies the concrete span to use. + let mut source_span = if matches!(last_stmt.kind, HirStmtKind::Err(_)) + && let Some(span) = self.trailing_assembly_last_stmt_span() + { + span + } else { + self.stmt_span_without_semicolon(source_stmt) + }; - // Consider yul return statement as final statement (if it's loc is lower) . + // Consider yul return statement as final statement (if it's loc is lower). if let Some(yul_return_span) = last_yul_return_span && yul_return_span.hi() < source_span.lo() { @@ -319,26 +175,32 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { // Map the source location of the final statement of the `run()` function to its // corresponding runtime program counter - let (_sf, range) = self.compiler.sess().source_map().span_to_source(source_span).unwrap(); - dbg!(source_span, &range, &_sf.src[range.clone()]); + let result = self + .compiler + .sess() + .source_map() + .span_to_source(source_span) + .map_err(|e| eyre::eyre!("failed to resolve span: {e:?}"))?; + let range = result.data; let offset = range.start as u32; let length = range.len() as u32; - let final_pc = deployed_bytecode - .source_map() + trace!(%offset, %length, "find pc"); + let final_pc = contract + .get_source_map_deployed() .ok_or_else(|| eyre::eyre!("No source map found for `REPL` contract"))?? .into_iter() - .zip(InstructionIter::new(deployed_bytecode_bytes)) + .zip(InstIter::new(deployed_bytecode_bytes).with_pc().map(|(pc, _)| pc)) .filter(|(s, _)| s.offset() == offset && s.length() == length) - .map(|(_, i)| i.pc) - .max() - .unwrap_or_default(); - Ok(Some(final_pc)) + .map(|(_, pc)| pc) + .max(); + trace!(?final_pc); + Ok(final_pc) } /// Statements' ranges in the solc source map do not include the semicolon. - fn stmt_span_without_semicolon(&self, stmt: &hir::Stmt<'_>) -> Span { + fn stmt_span_without_semicolon(&self, stmt: &Stmt<'_>) -> Span { match stmt.kind { - hir::StmtKind::DeclSingle(id) => { + HirStmtKind::DeclSingle(id) => { let decl = self.gcx().hir.variable(id); if let Some(expr) = decl.initializer { stmt.span.with_hi(expr.span.hi()) @@ -346,23 +208,65 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { stmt.span } } - hir::StmtKind::DeclMulti(_, expr) => stmt.span.with_hi(expr.span.hi()), - hir::StmtKind::Expr(expr) => expr.span, + HirStmtKind::DeclMulti(_, expr) => stmt.span.with_hi(expr.span.hi()), + HirStmtKind::Expr(expr) => expr.span, _ => stmt.span, } } - fn run_func_body(&self) -> hir::Block<'_> { - let c = self.repl_contract_hir().expect("REPL contract not found in HIR"); - let f = c - .functions() - .find(|&f| self.gcx().hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run")) - .expect("`run()` function not found in REPL contract"); - self.gcx().hir.function(f).body.expect("`run()` function does not have a body") + /// Returns the AST `run()` body of the REPL contract, if any. + /// + /// Yul/assembly is not yet lowered to HIR in the pinned solar version, so we + /// keep around the AST to be able to inspect inline assembly blocks. + fn repl_run_ast_body(&self) -> Option<&'gcx solar::ast::Block<'gcx>> { + let contract = self.repl_contract_hir()?; + let source = self.gcx().sources.get(contract.source)?; + let ast = source.ast.as_ref()?; + + let contract_ast = ast.items.iter().find_map(|i| match &i.kind { + ItemKind::Contract(c) if c.name.as_str() == "REPL" => Some(c), + _ => None, + })?; + contract_ast.body.iter().find_map(|i| match &i.kind { + ItemKind::Function(f) if f.header.name.is_some_and(|n| n.as_str() == "run") => { + f.body.as_ref() + } + _ => None, + }) } - fn repl_contract_hir(&self) -> Option<&hir::Contract<'_>> { - self.gcx().hir.contracts().find(|c| c.name.as_str() == "REPL") + /// Returns the span of the first top-level `return(...)` call inside any + /// `assembly { ... }` block in the REPL `run()` function, if any. + fn first_yul_return_span(&self) -> Option { + let run_body = self.repl_run_ast_body()?; + for stmt in run_body.stmts.iter() { + let AstStmtKind::Assembly(asm) = &stmt.kind else { continue }; + for ystmt in asm.block.stmts.iter() { + if let yul::StmtKind::Expr(e) = &ystmt.kind + && let yul::ExprKind::Call(call) = &e.kind + && call.name.as_str() == "return" + { + return Some(ystmt.span); + } + } + } + None + } + + /// If the last statement of the REPL `run()` function is an `assembly { ... }` block, + /// returns the span of its last non-`let` (i.e. non-VarDecl) Yul statement. + /// + /// This mirrors the legacy behavior used to pick a meaningful end-of-function PC when + /// the trailing statement is inline assembly. + fn trailing_assembly_last_stmt_span(&self) -> Option { + let run_body = self.repl_run_ast_body()?; + let AstStmtKind::Assembly(asm) = &run_body.stmts.last()?.kind else { return None }; + asm.block + .stmts + .iter() + .rev() + .find(|s| !matches!(s.kind, yul::StmtKind::VarDecl(_, _))) + .map(|s| s.span) } } @@ -584,8 +488,7 @@ impl SessionSource { return Ok(output); } let output = self.compile()?; - let intermediate = self.generate_intermediate_output()?; - let output = GeneratedOutput { output, intermediate }; + let output = GeneratedOutput { output }; Ok(self.output.get_or_init(|| output)) } @@ -602,12 +505,11 @@ impl SessionSource { eyre::bail!("{output}"); } - // TODO(dani): re-enable - if cfg!(false) { - output.parser_mut().solc_mut().compiler_mut().enter_mut(|c| { - let _ = c.lower_asts(); - }); - } + // Drive HIR lowering and analysis so that subsequent `enter` queries can use them. + output.parser_mut().solc_mut().compiler_mut().enter_mut(|c| { + let _ = c.lower_asts(); + let _ = c.analysis(); + }); Ok(output) } @@ -632,53 +534,6 @@ impl SessionSource { sources } - /// Generate intermediate contracts for all contract definitions in the compilation source. - /// - /// ### Returns - /// - /// Optionally, a map of contract names to a vec of [IntermediateContract]s. - pub fn generate_intermediate_contracts(&self) -> Result> { - let mut res_map = HashMap::default(); - let parsed_map = self.get_sources(); - for source in parsed_map.values() { - Self::get_intermediate_contract(&source.content, &mut res_map); - } - Ok(res_map) - } - - /// Generate intermediate output for the REPL contract - pub fn generate_intermediate_output(&self) -> Result { - // Parse generate intermediate contracts - let intermediate_contracts = self.generate_intermediate_contracts()?; - - // Construct variable definitions - let variable_definitions = intermediate_contracts - .get("REPL") - .ok_or_else(|| eyre::eyre!("Could not find intermediate REPL contract!"))? - .variable_definitions - .clone() - .into_iter() - .map(|(k, v)| (k, v.ty)) - .collect::>(); - // Construct intermediate output - let mut intermediate_output = IntermediateOutput { - repl_contract_expressions: variable_definitions, - intermediate_contracts, - }; - - // Add all statements within the run function to the repl_contract_expressions map - for (key, val) in intermediate_output - .run_func_body()? - .clone() - .iter() - .flat_map(Self::get_statement_definitions) - { - intermediate_output.repl_contract_expressions.insert(key, val); - } - - Ok(intermediate_output) - } - /// Construct the REPL source. pub fn to_repl_source(&self) -> String { let Self { @@ -741,108 +596,6 @@ contract {contract_name} {{ }); sess.dcx.emitted_errors().unwrap() } - - /// Gets the [IntermediateContract] for a Solidity source string and inserts it into the - /// passed `res_map`. In addition, recurses on any imported files as well. - /// - /// ### Takes - /// - `content` - A Solidity source string - /// - `res_map` - A mutable reference to a map of contract names to [IntermediateContract]s - pub fn get_intermediate_contract( - content: &str, - res_map: &mut HashMap, - ) { - if let Ok((pt::SourceUnit(source_unit_parts), _)) = solang_parser::parse(content, 0) { - let func_defs = source_unit_parts - .into_iter() - .filter_map(|sup| match sup { - pt::SourceUnitPart::ImportDirective(i) => match i { - pt::Import::Plain(s, _) - | pt::Import::Rename(s, _, _) - | pt::Import::GlobalSymbol(s, _, _) => { - let s = match s { - pt::ImportPath::Filename(s) => s.string, - pt::ImportPath::Path(p) => p.to_string(), - }; - let path = PathBuf::from(s); - - match fs::read_to_string(path) { - Ok(source) => { - Self::get_intermediate_contract(&source, res_map); - None - } - Err(_) => None, - } - } - }, - pt::SourceUnitPart::ContractDefinition(cd) => { - let mut intermediate = IntermediateContract::default(); - - cd.parts.into_iter().for_each(|part| match part { - pt::ContractPart::FunctionDefinition(def) => { - // Only match normal function definitions here. - if matches!(def.ty, pt::FunctionTy::Function) { - intermediate - .function_definitions - .insert(def.name.clone().unwrap().name, def); - } - } - pt::ContractPart::EventDefinition(def) => { - let event_name = def.name.as_ref().unwrap().name.clone(); - intermediate.event_definitions.insert(event_name, def); - } - pt::ContractPart::StructDefinition(def) => { - let struct_name = def.name.as_ref().unwrap().name.clone(); - intermediate.struct_definitions.insert(struct_name, def); - } - pt::ContractPart::VariableDefinition(def) => { - let var_name = def.name.as_ref().unwrap().name.clone(); - intermediate.variable_definitions.insert(var_name, def); - } - _ => {} - }); - Some((cd.name.as_ref().unwrap().name.clone(), intermediate)) - } - _ => None, - }) - .collect::>(); - res_map.extend(func_defs); - } - } - - /// Helper to deconstruct a statement - /// - /// ### Takes - /// - /// A reference to a [pt::Statement] - /// - /// ### Returns - /// - /// A vector containing tuples of the inner expressions' names, types, and storage locations. - pub fn get_statement_definitions(statement: &pt::Statement) -> Vec<(String, pt::Expression)> { - match statement { - pt::Statement::VariableDefinition(_, def, _) => { - vec![(def.name.as_ref().unwrap().name.clone(), def.ty.clone())] - } - pt::Statement::Expression(_, pt::Expression::Assign(_, left, _)) => { - if let pt::Expression::List(_, list) = left.as_ref() { - list.iter() - .filter_map(|(_, param)| { - param.as_ref().and_then(|param| { - param - .name - .as_ref() - .map(|name| (name.name.clone(), param.ty.clone())) - }) - }) - .collect() - } else { - Vec::default() - } - } - _ => Vec::default(), - } - } } /// A Parse Tree Fragment diff --git a/crates/chisel/tests/it/repl/mod.rs b/crates/chisel/tests/it/repl/mod.rs index 704d30405eed9..338b7d2043809 100644 --- a/crates/chisel/tests/it/repl/mod.rs +++ b/crates/chisel/tests/it/repl/mod.rs @@ -153,6 +153,26 @@ assembly { repl.expect("[0x00:0x20]"); }); +// Assembly as the final statement with a return — exercises the path where both +// `first_yul_return_span` and `trailing_assembly_last_stmt_span` resolve to the same `return(...)` +// span (no subsequent Solidity statement after the assembly block). +repl_test!(assembly_return_final, |repl| { + repl.sendln("uint x = 0xbeef;"); + repl.sendln("assembly { mstore(0x0, sload(0)) return(0x0, 0x20) }"); + repl.sendln("!md"); + repl.expect("[0x00:0x20]"); +}); + +// Assembly block without a `return(...)` call as an intermediate statement, exercises +// `first_yul_return_span` returning `None` while a subsequent Solidity statement is still evaluated +// correctly. +repl_test!(assembly_no_return_intermediate, |repl| { + repl.sendln("uint x = 1;"); + repl.sendln("assembly { x := add(x, 1) }"); + repl.sendln("x"); + repl.expect("Decimal: 2"); +}); + // Issue #5051, #8978: Test EVM version normalization. repl_test!(flaky_evm_version_normalization, "--use 0.7.6 --evm-version london", |repl| { repl.sendln("uint x;\nx"); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 37340f5f4cc3c..606b8291819e4 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -52,6 +52,7 @@ rayon.workspace = true regex = { workspace = true, default-features = false } serde_json.workspace = true serde.workspace = true +toml.workspace = true strsim = "0.11" strum = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["macros"] } @@ -70,7 +71,13 @@ tempfile.workspace = true tikv-jemallocator = { workspace = true, optional = true } [features] +default = ["optimism"] tracy = ["dep:tracing-tracy"] tracy-allocator = ["tracy"] jemalloc = ["dep:tikv-jemallocator"] mimalloc = ["dep:mimalloc"] +optimism = [ + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-evm/optimism", +] diff --git a/crates/cli/src/opts/evm.rs b/crates/cli/src/opts/evm.rs index 87f14e2039606..4fc437c7232f8 100644 --- a/crates/cli/src/opts/evm.rs +++ b/crates/cli/src/opts/evm.rs @@ -307,6 +307,17 @@ mod tests { assert_eq!(val, &Value::from(1000u64)); } + #[test] + fn rpc_url_arg_does_not_read_eth_rpc_url_env() { + use clap::CommandFactory; + + let command = EvmArgs::command(); + let rpc_url = + command.get_arguments().find(|arg| arg.get_id() == "rpc_url").expect("rpc_url arg"); + + assert!(rpc_url.get_env().is_none()); + } + #[test] fn can_parse_chain_id() { let args = EvmArgs { diff --git a/crates/cli/src/opts/rpc.rs b/crates/cli/src/opts/rpc.rs index 8c37860446683..f846da5002354 100644 --- a/crates/cli/src/opts/rpc.rs +++ b/crates/cli/src/opts/rpc.rs @@ -66,8 +66,20 @@ impl figment::Provider for RpcOpts { impl RpcOpts { /// Returns the RPC endpoint. pub fn url<'a>(&'a self, config: Option<&'a Config>) -> Result>> { + self.url_with_env(config, std::env::var("ETH_RPC_URL").ok()) + } + + fn url_with_env<'a>( + &'a self, + config: Option<&'a Config>, + env_url: Option, + ) -> Result>> { if self.flashbots { Ok(Some(Cow::Borrowed(FLASHBOTS_URL))) + } else if let Some(url) = self.common.rpc_url.as_deref() { + Ok(Some(Cow::Borrowed(url))) + } else if let Some(url) = env_url { + Ok(Some(Cow::Owned(url))) } else { self.common.url(config) } @@ -85,8 +97,10 @@ impl RpcOpts { pub fn dict(&self) -> Dict { let mut dict = self.common.dict(); - if self.flashbots { - dict.insert("eth_rpc_url".into(), FLASHBOTS_URL.into()); + // `self.url(None)` already accounts for `flashbots` and the `ETH_RPC_URL` env var, + // so a single insert here covers both. + if let Ok(Some(url)) = self.url(None) { + dict.insert("eth_rpc_url".into(), url.into_owned().into()); } if let Ok(Some(jwt)) = self.jwt(None) { dict.insert("eth_rpc_jwt".into(), jwt.into_owned().into()); @@ -199,6 +213,7 @@ impl figment::Provider for EthereumOpts { #[cfg(test)] mod tests { use super::*; + use clap::CommandFactory; #[test] fn parse_etherscan_opts() { @@ -223,4 +238,41 @@ mod tests { let id: u64 = chain_id.deserialize().expect("chain_id should deserialize as u64"); assert_eq!(id, 9745); } + + #[test] + fn rpc_url_arg_does_not_read_eth_rpc_url_env() { + let command = RpcOpts::command(); + let rpc_url = + command.get_arguments().find(|arg| arg.get_id() == "rpc_url").expect("rpc_url arg"); + + assert!(rpc_url.get_env().is_none()); + } + + #[test] + fn rpc_url_resolves_eth_rpc_url_env() { + let args = RpcOpts::default(); + let url = args + .url_with_env(None, Some("http://127.0.0.1:8545".to_string())) + .expect("url") + .expect("url"); + + assert_eq!(url.as_ref(), "http://127.0.0.1:8545"); + } + + #[test] + fn explicit_rpc_url_takes_precedence_over_eth_rpc_url_env() { + let args = RpcOpts { + common: RpcCommonOpts { + rpc_url: Some("http://127.0.0.1:8546".to_string()), + ..Default::default() + }, + ..Default::default() + }; + let url = args + .url_with_env(None, Some("http://127.0.0.1:8545".to_string())) + .expect("url") + .expect("url"); + + assert_eq!(url.as_ref(), "http://127.0.0.1:8546"); + } } diff --git a/crates/cli/src/opts/rpc_common.rs b/crates/cli/src/opts/rpc_common.rs index 05b98582fa88f..6a5fe5ed4e9e4 100644 --- a/crates/cli/src/opts/rpc_common.rs +++ b/crates/cli/src/opts/rpc_common.rs @@ -17,10 +17,15 @@ use std::borrow::Cow; /// This struct holds fields that both [`super::RpcOpts`] (cast) and /// [`super::EvmArgs`] (forge/script) need, eliminating duplication and /// making the two structs composable. +/// +/// Note: `ETH_RPC_URL` is intentionally **not** bound here as a clap env +/// fallback; otherwise it would be inherited by `EvmArgs` and silently +/// fork all `forge test` runs. Cast resolves `ETH_RPC_URL` explicitly +/// at the call site (see [`super::RpcOpts::url`]). #[derive(Clone, Debug, Default, Serialize, Parser)] pub struct RpcCommonOpts { /// The RPC endpoint. - #[arg(short, long, visible_alias = "fork-url", env = "ETH_RPC_URL")] + #[arg(short, long, visible_alias = "fork-url", value_name = "URL")] #[serde(rename = "eth_rpc_url", skip_serializing_if = "Option::is_none")] pub rpc_url: Option, diff --git a/crates/cli/src/opts/tempo.rs b/crates/cli/src/opts/tempo.rs index 8c2a12e661e18..88119f163b325 100644 --- a/crates/cli/src/opts/tempo.rs +++ b/crates/cli/src/opts/tempo.rs @@ -1,16 +1,26 @@ use alloy_network::{Network, TransactionBuilder}; use alloy_primitives::{Address, ruint::aliases::U256}; -use alloy_signer::Signature; +use alloy_signer::{Signature, Signer}; use clap::Parser; -use foundry_common::FoundryTransactionBuilder; -use std::{num::NonZeroU64, str::FromStr}; +use eyre::Result; +use foundry_common::{ + FoundryTransactionBuilder, + tempo::{TempoSponsor, resolve_tempo_sponsor_signer}, +}; +use std::{ + num::NonZeroU64, + path::PathBuf, + str::FromStr, + sync::Arc, + time::{SystemTime, UNIX_EPOCH}, +}; use crate::utils::parse_fee_token_address; -/// CLI options for Tempo transactions. +/// CLI options common to Tempo transactions across commands. #[derive(Clone, Debug, Default, Parser)] #[command(next_help_heading = "Tempo")] -pub struct TempoOpts { +pub struct TempoCommonOpts { /// Fee token address for Tempo transactions. /// /// When set, builds a Tempo (type 0x76) transaction that pays gas fees @@ -21,6 +31,40 @@ pub struct TempoOpts { #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] pub fee_token: Option
, + /// Opt into TIP-1009 expiring-nonce mode with a validity window. + /// + /// Convenience flag that combines `--tempo.expiring-nonce` with a relative + /// `--tempo.valid-before`. Sets nonce_key = U256::MAX, nonce = 0, and valid_before = now + + /// seconds. + /// + /// Maximum value is 30 seconds. The transaction must be mined before the deadline or it + /// becomes permanently invalid, giving safe retry semantics: retries produce a fresh tx hash + /// and the old tx can never land late. + #[arg(long = "tempo.expires", value_name = "SECONDS", value_parser = parse_expires_seconds)] + pub expires: Option, +} + +impl TempoCommonOpts { + /// Returns `true` if any Tempo-specific option is set. + pub const fn is_tempo(&self) -> bool { + self.fee_token.is_some() || self.expires.is_some() + } + + /// Returns the absolute `valid_before` unix timestamp derived from `--tempo.expires`, if set. + pub fn expires_at(&self) -> Option { + let secs = self.expires?; + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); + Some(now.as_secs() + secs) + } +} + +/// CLI options for Tempo transactions. +#[derive(Clone, Debug, Default, Parser)] +#[command(next_help_heading = "Tempo")] +pub struct TempoOpts { + #[command(flatten)] + pub common: TempoCommonOpts, + /// Nonce key for Tempo parallelizable nonces. /// /// When set, builds a Tempo (type 0x76) transaction with the specified nonce key, @@ -28,21 +72,69 @@ pub struct TempoOpts { /// to be executed in parallel. If not set, the protocol nonce key (0) will be used. /// /// For more information see . - #[arg(long = "tempo.nonce-key", value_name = "NONCE_KEY")] + #[arg(long = "tempo.nonce-key", value_name = "NONCE_KEY", conflicts_with = "lane")] pub nonce_key: Option, + /// Named nonce lane for Tempo parallelizable nonces. + /// + /// Resolves a friendly lane name (e.g. `deploy`, `payments`) to a `nonce_key` via a + /// shared lanes file (default: `tempo.lanes.toml` at the project root). The lanes file + /// is a TOML map of `name = ` entries, e.g.: + /// + /// ```toml + /// deploy = 1 + /// ops = 2 + /// payments = 3 + /// ``` + /// + /// Mutually exclusive with `--tempo.nonce-key`. + #[arg(long = "tempo.lane", value_name = "NAME")] + pub lane: Option, + + /// Path to the Tempo lanes file used by `--tempo.lane`. + /// + /// Defaults to `tempo.lanes.toml` at the project root. + #[arg(long = "tempo.lanes-file", value_name = "PATH")] + pub lanes_file: Option, + + /// Sponsor (fee payer) address for Tempo sponsored transactions. + #[arg(long = "tempo.sponsor", value_name = "ADDRESS")] + pub sponsor: Option
, + + /// Sign Tempo sponsor digests in-band with the given signer URI. + /// + /// Supported forms include `env://VAR`, `keystore://PATH`, `account://NAME`, + /// `ledger://`, `trezor://`, `aws://`, `gcp://`, `turnkey://`, and + /// `private-key://KEY`. + #[arg( + long = "tempo.sponsor-signer", + value_name = "SIGNER", + requires = "sponsor", + conflicts_with = "sponsor_sig" + )] + pub sponsor_signer: Option, + /// Sponsor (fee payer) signature for Tempo sponsored transactions. /// /// The sponsor signs the `fee_payer_signature_hash` to commit to paying gas fees /// on behalf of the sender. Provide as a hex-encoded signature. - #[arg(long = "tempo.sponsor-signature", value_parser = parse_signature)] - pub sponsor_signature: Option, + #[arg( + long = "tempo.sponsor-sig", + alias = "tempo.sponsor-signature", + value_parser = parse_signature, + requires = "sponsor", + conflicts_with = "sponsor_signer" + )] + pub sponsor_sig: Option, /// Print the sponsor signature hash and exit. /// /// Computes the `fee_payer_signature_hash` for the transaction so that a sponsor /// knows what hash to sign. The transaction is not sent. - #[arg(long = "tempo.print-sponsor-hash")] + #[arg( + long = "tempo.print-sponsor-hash", + conflicts_with_all = &["sponsor", "sponsor_signer", "sponsor_sig"] + )] pub print_sponsor_hash: bool, /// Access key ID for Tempo Keychain signature transactions. @@ -56,14 +148,14 @@ pub struct TempoOpts { /// /// Sets nonce to 0 and nonce_key to U256::MAX, enabling time-bounded transaction /// validity via `--tempo.valid-before` and `--tempo.valid-after`. - #[arg(long = "tempo.expiring-nonce", requires = "valid_before")] + #[arg(long = "tempo.expiring-nonce", requires = "valid_before", conflicts_with = "expires")] pub expiring_nonce: bool, /// Upper bound timestamp for Tempo expiring nonce transactions. /// /// The transaction is only valid before this unix timestamp. /// Requires `--tempo.expiring-nonce`. - #[arg(long = "tempo.valid-before")] + #[arg(long = "tempo.valid-before", conflicts_with = "expires")] pub valid_before: Option, /// Lower bound timestamp for Tempo expiring nonce transactions. @@ -77,9 +169,12 @@ pub struct TempoOpts { impl TempoOpts { /// Returns `true` if any Tempo-specific option is set. pub const fn is_tempo(&self) -> bool { - self.fee_token.is_some() + self.common.is_tempo() || self.nonce_key.is_some() - || self.sponsor_signature.is_some() + || self.lane.is_some() + || self.sponsor.is_some() + || self.sponsor_signer.is_some() + || self.sponsor_sig.is_some() || self.print_sponsor_hash || self.key_id.is_some() || self.expiring_nonce @@ -87,6 +182,58 @@ impl TempoOpts { || self.valid_after.is_some() } + /// Returns the absolute `valid_before` unix timestamp derived from `--tempo.expires`, if set. + pub fn expires_at(&self) -> Option { + self.common.expires_at() + } + + /// Resolves `--tempo.expires` into concrete expiring-nonce fields. + /// + /// This computes the relative deadline once so later calls to [`Self::apply`] reuse the same + /// `valid_before` timestamp instead of deriving a fresh one. + pub fn resolve_expires(&mut self) -> Option { + let ts = self.expires_at()?; + self.expiring_nonce = true; + self.valid_before = Some(ts); + self.common.expires = None; + Some(ts) + } + + /// Returns `true` if a sponsor signature should be attached before submission. + pub const fn has_sponsor_submission(&self) -> bool { + self.sponsor.is_some() || self.sponsor_signer.is_some() || self.sponsor_sig.is_some() + } + + /// Resolves sponsor CLI options into a reusable sponsor config for transaction submission. + pub async fn sponsor_config(&self) -> Result> { + let Some(sponsor) = self.sponsor else { + return Ok(None); + }; + + let signer = if let Some(spec) = &self.sponsor_signer { + Some(Arc::new(Box::pin(resolve_tempo_sponsor_signer(spec)).await?)) + } else { + None + }; + + if let Some(signer) = &signer { + let signer_address = signer.address(); + if signer_address != sponsor { + eyre::bail!( + "Tempo sponsor signer address {signer_address} does not match --tempo.sponsor {sponsor}" + ); + } + } + + if signer.is_none() && self.sponsor_sig.is_none() { + eyre::bail!( + "--tempo.sponsor requires either --tempo.sponsor-signer or --tempo.sponsor-sig" + ); + } + + Ok(Some(TempoSponsor::new(sponsor, signer, self.sponsor_sig))) + } + /// Applies Tempo-specific options to a transaction request. /// /// All setters are no-ops for non-Tempo networks, so this is safe to call unconditionally. @@ -94,8 +241,9 @@ impl TempoOpts { where N::TransactionRequest: FoundryTransactionBuilder, { - // Handle expiring nonce mode: sets nonce=0 and nonce_key=U256::MAX - if self.expiring_nonce { + // Handle expiring nonce mode: sets nonce=0 and nonce_key=U256::MAX. + // --tempo.expires is a convenience alias that also sets valid_before = now + duration. + if self.expiring_nonce || self.common.expires.is_some() { tx.set_nonce(0); tx.set_nonce_key(U256::MAX); } else { @@ -107,11 +255,14 @@ impl TempoOpts { } } - if let Some(fee_token) = self.fee_token { + if let Some(fee_token) = self.common.fee_token { tx.set_fee_token(fee_token); } - if let Some(valid_before) = self.valid_before + // --tempo.expires sets valid_before relative to now; --tempo.valid-before takes a raw + // unix timestamp. The two flags are mutually exclusive (enforced by clap). + let effective_valid_before = self.expires_at().or(self.valid_before); + if let Some(valid_before) = effective_valid_before && let Some(v) = NonZeroU64::new(valid_before) { tx.set_valid_before(v); @@ -131,8 +282,7 @@ impl TempoOpts { // gas estimation so that `--tempo.print-sponsor-hash` and // `--tempo.sponsor-signature` produce identical gas estimates. Callers // should call `set_fee_payer_signature` on the built tx request. - if (self.sponsor_signature.is_some() || self.print_sponsor_hash) && tx.nonce_key().is_none() - { + if (self.has_sponsor_submission() || self.print_sponsor_hash) && tx.nonce_key().is_none() { tx.set_nonce_key(U256::ZERO); } } @@ -142,11 +292,83 @@ fn parse_signature(s: &str) -> Result { Signature::from_str(s).map_err(|e| format!("invalid signature: {e}")) } +/// Parses a seconds value for `--tempo.expires`, capped at the protocol maximum of 30 seconds. +fn parse_expires_seconds(s: &str) -> Result { + let secs: u64 = s + .parse() + .map_err(|_| format!("invalid value '{s}': expected an integer number of seconds"))?; + if secs > 30 { + return Err(format!("expires must be at most 30 seconds (got {secs})")); + } + Ok(secs) +} + #[cfg(test)] mod tests { use super::*; use alloy_primitives::address; + #[test] + fn parses_lane_arg() { + let opts = TempoOpts::try_parse_from(["", "--tempo.lane", "deploy"]).unwrap(); + assert_eq!(opts.lane.as_deref(), Some("deploy")); + assert!(opts.nonce_key.is_none()); + } + + #[test] + fn lane_conflicts_with_nonce_key() { + let err = + TempoOpts::try_parse_from(["", "--tempo.lane", "deploy", "--tempo.nonce-key", "1"]) + .unwrap_err(); + assert!( + err.to_string().contains("cannot be used with"), + "expected clap conflict error, got: {err}", + ); + } + + #[test] + fn parse_expires_flag() { + let opts = TempoOpts::try_parse_from(["", "--tempo.expires", "30"]).unwrap(); + assert_eq!(opts.common.expires, Some(30)); + + let opts = TempoOpts::try_parse_from(["", "--tempo.expires", "10"]).unwrap(); + assert_eq!(opts.common.expires, Some(10)); + + // exceeds 30s maximum + assert!(TempoOpts::try_parse_from(["", "--tempo.expires", "31"]).is_err()); + + // conflicts with --tempo.expiring-nonce + assert!( + TempoOpts::try_parse_from([ + "", + "--tempo.expires", + "30", + "--tempo.expiring-nonce", + "--tempo.valid-before", + "999" + ]) + .is_err() + ); + } + + #[test] + fn resolve_expires_materializes_valid_before() { + let before = + SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards").as_secs(); + let mut opts = TempoOpts::try_parse_from(["", "--tempo.expires", "10"]).unwrap(); + + let resolved = opts.resolve_expires().unwrap(); + let after = + SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards").as_secs(); + + assert!(resolved >= before + 10); + assert!(resolved <= after + 10); + assert!(opts.expiring_nonce); + assert_eq!(opts.valid_before, Some(resolved)); + assert_eq!(opts.common.expires, None); + assert_eq!(opts.expires_at(), None); + } + #[test] fn parse_fee_token_id() { let opts = TempoOpts::try_parse_from([ @@ -155,13 +377,69 @@ mod tests { "0x20C0000000000000000000000000000000000002", ]) .unwrap(); - assert_eq!(opts.fee_token, Some(address!("0x20C0000000000000000000000000000000000002")),); + assert_eq!( + opts.common.fee_token, + Some(address!("0x20C0000000000000000000000000000000000002")), + ); // AlphaUSD token ID is 1u64 let opts_with_id = TempoOpts::try_parse_from(["", "--tempo.fee-token", "1"]).unwrap(); assert_eq!( - opts_with_id.fee_token, + opts_with_id.common.fee_token, Some(address!("0x20C0000000000000000000000000000000000001")), ); } + + #[test] + fn parse_sponsor_signer() { + let opts = TempoOpts::try_parse_from([ + "", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + "--tempo.sponsor-signer", + "env://TEMPO_SPONSOR_PK", + ]) + .unwrap(); + + assert_eq!(opts.sponsor, Some(address!("0x1111111111111111111111111111111111111111"))); + assert_eq!(opts.sponsor_signer.as_deref(), Some("env://TEMPO_SPONSOR_PK")); + assert!(opts.sponsor_sig.is_none()); + assert!(opts.is_tempo()); + assert!(opts.has_sponsor_submission()); + } + + #[test] + fn sponsor_signer_requires_sponsor() { + assert!( + TempoOpts::try_parse_from(["", "--tempo.sponsor-signer", "env://SPONSOR"]).is_err() + ); + } + + #[test] + fn parse_sponsor_signature_alias() { + let opts = TempoOpts::try_parse_from([ + "", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + "--tempo.sponsor-signature", + "0x0eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca182b", + ]) + .unwrap(); + + assert_eq!(opts.sponsor, Some(address!("0x1111111111111111111111111111111111111111"))); + assert!(opts.sponsor_sig.is_some()); + } + + #[test] + fn print_sponsor_hash_conflicts_with_sponsor_submission() { + assert!( + TempoOpts::try_parse_from([ + "", + "--tempo.print-sponsor-hash", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + ]) + .is_err() + ); + } } diff --git a/crates/cli/src/utils/tempo.rs b/crates/cli/src/utils/tempo.rs index 647f52d316a6c..4b5715b9ebe08 100644 --- a/crates/cli/src/utils/tempo.rs +++ b/crates/cli/src/utils/tempo.rs @@ -1,8 +1,44 @@ -use std::str::FromStr; +//! Tempo utilities: fee token parsing and named nonce lanes (2D nonces). +//! +//! A "lane" is a friendly alias for a Tempo `nonce_key` (a [`U256`]). Lanes are defined in a +//! shared TOML file (default `tempo.lanes.toml` at the project root) so a team can reserve +//! independent sequential nonce streams for parallel scripts without coordinating on raw +//! `U256` selectors. +//! +//! Example `tempo.lanes.toml`: +//! +//! ```toml +//! deploy = 1 +//! ops = 2 +//! payments = 3 +//! ``` +//! +//! ```bash +//! cast erc20 transfer ... --tempo.lane payments +//! ``` -use alloy_primitives::Address; +use crate::opts::TempoOpts; +use alloy_primitives::{Address, U256}; +use eyre::{Result, eyre}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, + str::FromStr, +}; use tempo_primitives::TempoAddressExt; +/// Default name of the lanes file at the project root. +pub const DEFAULT_LANES_FILE: &str = "tempo.lanes.toml"; + +/// Result of resolving a `--tempo.lane ` argument against a lanes file. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolvedLane { + /// The lane name as provided on the CLI. + pub name: String, + /// The `nonce_key` the lane resolved to. + pub nonce_key: U256, +} + /// Parses a fee token address. pub fn parse_fee_token_address(address_or_id: &str) -> eyre::Result
{ Address::from_str(address_or_id).or_else(|_| Ok(token_id_to_address(address_or_id.parse()?))) @@ -14,3 +50,156 @@ fn token_id_to_address(token_id: u64) -> Address { address_bytes[12..20].copy_from_slice(&token_id.to_be_bytes()); Address::from(address_bytes) } + +/// Loads a TOML lanes file from `path`. +/// +/// Each top-level key is a lane name, and the value is the `nonce_key` (an integer or a +/// decimal/hex string parsed as [`U256`]). +pub fn load_lanes(path: &Path) -> Result> { + let contents = std::fs::read_to_string(path) + .map_err(|e| eyre!("failed to read tempo lanes file {}: {}", path.display(), e))?; + parse_lanes(&contents) + .map_err(|e| eyre!("failed to parse tempo lanes file {}: {}", path.display(), e)) +} + +fn parse_lanes(contents: &str) -> Result> { + let raw: BTreeMap = toml::from_str(contents)?; + let mut out = BTreeMap::new(); + for (name, value) in raw { + let nonce_key = match value { + toml::Value::Integer(n) => { + if n < 0 { + return Err(eyre!("invalid nonce_key for lane '{name}': must be non-negative")); + } + U256::from(n as u64) + } + toml::Value::String(s) => U256::from_str(s.trim()) + .map_err(|e| eyre!("invalid nonce_key for lane '{name}': {e}"))?, + other => { + return Err(eyre!( + "invalid nonce_key for lane '{name}': expected integer or string, got {}", + other.type_str(), + )); + } + }; + out.insert(name, nonce_key); + } + Ok(out) +} + +/// Resolves `opts.lane` against a lanes file and writes the resulting `nonce_key` to +/// `opts.nonce_key`. Returns the resolved lane (or `None` if no `--tempo.lane` was set). +/// +/// `root` is the project root used to locate the default lanes file +/// (`/tempo.lanes.toml`) when `--tempo.lanes-file` was not provided. +pub fn resolve_lane(opts: &mut TempoOpts, root: &Path) -> Result> { + let Some(lane_name) = opts.lane.clone() else { return Ok(None) }; + + let path: PathBuf = opts.lanes_file.clone().unwrap_or_else(|| root.join(DEFAULT_LANES_FILE)); + + if !path.exists() { + return Err(eyre!( + "tempo lanes file not found at {}\n\ + create it with `name = ` entries, e.g.:\n \ + deploy = 1\n \ + ops = 2\n \ + payments = 3", + path.display(), + )); + } + + let lanes = load_lanes(&path)?; + + let nonce_key = lanes.get(&lane_name).copied().ok_or_else(|| { + let mut known: Vec<&str> = lanes.keys().map(String::as_str).collect(); + known.sort_unstable(); + eyre!( + "lane '{lane_name}' not found in {} (known lanes: {})", + path.display(), + if known.is_empty() { "".to_string() } else { known.join(", ") }, + ) + })?; + + opts.nonce_key = Some(nonce_key); + Ok(Some(ResolvedLane { name: lane_name, nonce_key })) +} + +/// Prints `lane: (nonce_key=, nonce=)` to stderr (so it doesn't pollute +/// stdout for commands like `cast mktx` whose stdout is meant to be piped), giving +/// visibility into which 2D nonce lane was used. +pub fn maybe_print_resolved_lane(resolved: Option<&ResolvedLane>, nonce: u64) -> Result<()> { + if let Some(lane) = resolved { + sh_eprintln!("lane: {} (nonce_key={}, nonce={})", lane.name, lane.nonce_key, nonce)?; + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_int_and_string_lane_values() { + let toml = r#" +deploy = 1 +ops = 2 +payments = "3" +big = "115792089237316195423570985008687907853269984665640564039457584007913129639935" +"#; + let lanes = parse_lanes(toml).unwrap(); + assert_eq!(lanes.get("deploy"), Some(&U256::from(1u64))); + assert_eq!(lanes.get("ops"), Some(&U256::from(2u64))); + assert_eq!(lanes.get("payments"), Some(&U256::from(3u64))); + assert_eq!(lanes.get("big"), Some(&U256::MAX)); + } + + #[test] + fn parse_lanes_rejects_invalid_string() { + let toml = "broken = \"not-a-number\""; + let err = parse_lanes(toml).unwrap_err(); + assert!(err.to_string().contains("invalid nonce_key for lane 'broken'")); + } + + #[test] + fn resolve_lane_sets_nonce_key_and_returns_resolved() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join(DEFAULT_LANES_FILE); + std::fs::write(&path, "deploy = 7\npayments = 42\n").unwrap(); + + let mut opts = TempoOpts { lane: Some("payments".to_string()), ..Default::default() }; + let resolved = resolve_lane(&mut opts, dir.path()).unwrap().unwrap(); + assert_eq!(resolved.name, "payments"); + assert_eq!(resolved.nonce_key, U256::from(42u64)); + assert_eq!(opts.nonce_key, Some(U256::from(42u64))); + } + + #[test] + fn resolve_lane_returns_none_when_no_lane() { + let dir = tempfile::tempdir().unwrap(); + let mut opts = TempoOpts::default(); + let resolved = resolve_lane(&mut opts, dir.path()).unwrap(); + assert!(resolved.is_none()); + assert!(opts.nonce_key.is_none()); + } + + #[test] + fn resolve_lane_errors_when_file_missing() { + let dir = tempfile::tempdir().unwrap(); + let mut opts = TempoOpts { lane: Some("deploy".to_string()), ..Default::default() }; + let err = resolve_lane(&mut opts, dir.path()).unwrap_err(); + assert!(err.to_string().contains("tempo lanes file not found")); + } + + #[test] + fn resolve_lane_errors_when_lane_unknown() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join(DEFAULT_LANES_FILE); + std::fs::write(&path, "deploy = 1\nops = 2\n").unwrap(); + + let mut opts = TempoOpts { lane: Some("payments".to_string()), ..Default::default() }; + let err = resolve_lane(&mut opts, dir.path()).unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("lane 'payments' not found")); + assert!(msg.contains("deploy, ops")); + } +} diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 7fd94c07242e8..a66a0fef2fe0a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -34,7 +34,7 @@ alloy-signer.workspace = true alloy-pubsub.workspace = true alloy-rpc-client.workspace = true alloy-rpc-types = { workspace = true, features = ["eth", "engine"] } -alloy-rpc-types-engine = { workspace = true, features = ["jwt"] } +alloy-rpc-types-engine = { workspace = true, features = ["jwt-aws-lc-rs"] } alloy-sol-types.workspace = true alloy-transport-ipc.workspace = true alloy-transport-ws.workspace = true @@ -43,8 +43,8 @@ alloy-transport.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } alloy-network.workspace = true -op-alloy-network.workspace = true -op-alloy-rpc-types.workspace = true +op-alloy-network = { workspace = true, optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } revm.workspace = true @@ -86,6 +86,10 @@ mpp.workspace = true foundry-wallets = { workspace = true, features = ["browser", "tempo"] } tokio-tungstenite.workspace = true futures.workspace = true +alloy-signer-local.workspace = true +base64.workspace = true +sha2 = "0.10" +tempfile.workspace = true [build-dependencies] chrono.workspace = true @@ -95,4 +99,12 @@ vergen = { workspace = true, features = ["build", "emit_and_set"] } foundry-evm-hardforks.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } axum = { workspace = true } -tempfile.workspace = true +k256 = { workspace = true } + +[features] +default = ["optimism"] +optimism = [ + "dep:op-alloy-network", + "dep:op-alloy-rpc-types", + "foundry-common-fmt/optimism", +] diff --git a/crates/common/build.rs b/crates/common/build.rs index d89e23be850f4..9afa01b5757ef 100644 --- a/crates/common/build.rs +++ b/crates/common/build.rs @@ -15,16 +15,13 @@ fn main() -> Result<(), Box> { let sha_short = &sha[..10]; let tag_name = try_env_var("TAG_NAME").unwrap_or_else(|| String::from("dev")); - let is_nightly = tag_name.contains("nightly"); - let version_suffix = if is_nightly { "nightly" } else { &tag_name }; + let version = release_version(&env_var("CARGO_PKG_VERSION"), &tag_name); + let is_nightly = tag_name.starts_with("nightly"); if is_nightly { println!("cargo:rustc-env=FOUNDRY_IS_NIGHTLY_VERSION=true"); } - let pkg_version = env_var("CARGO_PKG_VERSION"); - let version = format!("{pkg_version}-{version_suffix}"); - // `PROFILE` captures only release or debug. Get the actual name from the out directory. let out_dir = PathBuf::from(env_var("OUT_DIR")); let profile = out_dir.components().rev().nth(3).unwrap().as_os_str().to_str().unwrap(); @@ -87,6 +84,19 @@ fn env_var(name: &str) -> String { try_env_var(name).unwrap() } +fn release_version(pkg_version: &str, tag_name: &str) -> String { + if let Some(version) = tag_name.strip_prefix('v') { + return version.to_owned(); + } + + // Normalize `nightly-` to `nightly` so tarball and Docker nightly + // artifacts produce the same version string. The commit identifier is + // already included in the SemVer build metadata (after `+`). + let normalized = if tag_name.starts_with("nightly-") { "nightly" } else { tag_name }; + + format!("{pkg_version}-{normalized}") +} + fn try_env_var(name: &str) -> Option { println!("cargo:rerun-if-env-changed={name}"); std::env::var(name).ok() diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 2c8e16bccdcc6..179c71048da5b 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -20,10 +20,10 @@ eyre.workspace = true # ui alloy-consensus.workspace = true -op-alloy-consensus.workspace = true +op-alloy-consensus = { workspace = true, optional = true } alloy-network.workspace = true alloy-rpc-types = { workspace = true, features = ["eth"] } -op-alloy-rpc-types.workspace = true +op-alloy-rpc-types = { workspace = true, optional = true } alloy-serde.workspace = true serde.workspace = true serde_json.workspace = true @@ -38,3 +38,7 @@ tempo-alloy.workspace = true [dev-dependencies] foundry-macros.workspace = true similar-asserts.workspace = true + +[features] +default = ["optimism"] +optimism = ["dep:op-alloy-consensus", "dep:op-alloy-rpc-types"] diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index e883810dcda34..2087a85236154 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -18,6 +18,7 @@ use alloy_rpc_types::{ AccessListItem, Block, BlockTransactions, Header, Log, Transaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{OpTxEnvelope, TxDeposit, TxPostExec}; use revm::context_interface::transaction::SignedAuthorization; use serde::Deserialize; @@ -448,6 +449,7 @@ input {}", } } +#[cfg(feature = "optimism")] impl UIfmt for TxDeposit { fn pretty(&self) -> String { format!( @@ -472,6 +474,7 @@ input {}", } } +#[cfg(feature = "optimism")] impl UIfmt for TxPostExec { fn pretty(&self) -> String { format!( @@ -606,6 +609,7 @@ type {:#x} } } +#[cfg(feature = "optimism")] impl UIfmt for OpTxEnvelope { fn pretty(&self) -> String { match self { @@ -651,6 +655,7 @@ effectiveGasPrice {} } } +#[cfg(feature = "optimism")] impl UIfmt for op_alloy_rpc_types::Transaction { fn pretty(&self) -> String { format!( @@ -786,6 +791,7 @@ impl UIfmtSignatureExt for AnyTxEnvelope { } } +#[cfg(feature = "optimism")] impl UIfmtSignatureExt for OpTxEnvelope { fn signature_pretty(&self) -> Option<(String, String, String)> { self.signature().map(|sig| { @@ -1135,6 +1141,7 @@ mod tests { assert_eq!(b.pretty(), b32.pretty()); } + #[cfg(feature = "optimism")] #[test] fn can_pretty_print_optimism_tx() { let s = r#" @@ -1186,6 +1193,7 @@ yParity 1 ); } + #[cfg(feature = "optimism")] #[test] fn can_pretty_print_optimism_tx_through_any() { let s = r#" diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 895b16b3b4532..95c7d4083f34e 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -383,16 +383,14 @@ impl ContractsByArtifact { &self, id: &str, ) -> Result>> { - let contracts = self - .iter() - .filter(|(artifact, _)| artifact.name == id || artifact.identifier() == id) - .collect::>(); - - if contracts.len() > 1 { + let mut iter = + self.iter().filter(|(artifact, _)| artifact.name == id || artifact.identifier() == id); + let first = iter.next(); + if first.is_some() && iter.next().is_some() { eyre::bail!("{id} has more than one implementation."); } - Ok(contracts.first().copied()) + Ok(first) } /// Finds abi by name or source path @@ -411,7 +409,7 @@ impl ContractsByArtifact { let mut funcs = BTreeMap::new(); let mut events = BTreeMap::new(); let mut errors_abi = JsonAbi::new(); - for (_name, contract) in self.iter() { + for contract in self.values() { for func in contract.abi.functions() { funcs.insert(func.selector(), func.clone()); } diff --git a/crates/common/src/provider/mpp/keys.rs b/crates/common/src/provider/mpp/keys.rs index 65640c48ab841..fa0fc80ed3d03 100644 --- a/crates/common/src/provider/mpp/keys.rs +++ b/crates/common/src/provider/mpp/keys.rs @@ -7,6 +7,7 @@ use crate::tempo::{TEMPO_PRIVATE_KEY_ENV, WalletType, read_tempo_keys_file}; use alloy_primitives::Address; +use std::env; use tracing::debug; /// Options for MPP key discovery filtering. @@ -55,7 +56,7 @@ pub fn discover_mpp_key() -> Option { /// target chain and the required currency. pub fn discover_mpp_config(opts: DiscoverOptions) -> Option { // 1. Check TEMPO_PRIVATE_KEY env var (no keychain metadata available) - if let Ok(key) = std::env::var(TEMPO_PRIVATE_KEY_ENV) { + if let Ok(key) = env::var(TEMPO_PRIVATE_KEY_ENV) { let key = key.trim().to_string(); if !key.is_empty() { debug!("using MPP key from {TEMPO_PRIVATE_KEY_ENV} env var"); @@ -73,11 +74,17 @@ pub fn discover_mpp_config(opts: DiscoverOptions) -> Option { // 2. Read $TEMPO_HOME/wallet/keys.toml (default: ~/.tempo/wallet/keys.toml) let keys_file = read_tempo_keys_file()?; + // `expiry == 0` means "no expiry" on the wire. + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0); + // Pick primary key using the same deterministic order as // `Keystore::primary_key()` in tempo-common: // passkey > first entry with inline key > first entry // Only entries with a usable inline key can provide a signing key. - // Filter by chain_id and currency when provided. + // Filter by chain_id, currency, and freshness when provided. let candidates: Vec<_> = keys_file .keys .iter() @@ -86,6 +93,7 @@ pub fn discover_mpp_config(opts: DiscoverOptions) -> Option { opts.currency .is_none_or(|cur| k.limits.is_empty() || k.limits.iter().any(|l| l.currency == cur)) }) + .filter(|k| k.expiry.is_none_or(|e| e == 0 || e > now)) .collect(); let primary = candidates @@ -135,6 +143,7 @@ mod tests { #[test] fn discover_from_tempo_home_keys_toml() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; let toml_content = format!( r#" @@ -160,6 +169,7 @@ chain_id = 4217 #[test] fn discover_env_var_takes_priority_over_keys_toml() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let file_key = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; let env_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; let toml_content = format!( @@ -187,6 +197,7 @@ key = "{file_key}" #[test] fn discover_returns_none_when_no_keys() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let (dir, _) = setup_keys_toml(""); unsafe { @@ -202,6 +213,7 @@ key = "{file_key}" #[test] fn discover_skips_entries_without_inline_key() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; let toml_content = format!( r#" @@ -344,6 +356,7 @@ key = "0xthe_key" #[test] fn discover_filters_by_chain_id() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let mainnet_key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let testnet_key = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; let toml_content = format!( @@ -416,6 +429,62 @@ chain_id = 4217 unsafe { std::env::remove_var("TEMPO_HOME") }; } + #[test] + fn discover_filters_expired_entries() { + // Expired entries must not be selected, so the next 402 re-triggers + // the device-code flow instead of returning a stale key. + let _g = crate::tempo::test_env_mutex().blocking_lock(); + let expired_key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let fresh_key = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + let toml_content = format!( + r#" +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000001" +key = "{expired_key}" +chain_id = 4217 +expiry = 1 + +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000002" +key = "{fresh_key}" +chain_id = 4217 +expiry = 0 +"# + ); + let (dir, _) = setup_keys_toml(&toml_content); + unsafe { + std::env::set_var("TEMPO_HOME", dir.path()); + std::env::remove_var("TEMPO_PRIVATE_KEY"); + } + + // Even though the expired entry comes first, discovery skips it. + let config = + discover_mpp_config(DiscoverOptions { chain_id: Some(4217), ..Default::default() }); + assert_eq!(config.as_ref().unwrap().key, fresh_key); + + // With only the expired entry present, discovery returns None so the + // 402 path can run `ensure_access_key` again. + let only_expired = format!( + r#" +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000001" +key = "{expired_key}" +chain_id = 4217 +expiry = 1 +"# + ); + let (dir2, _) = setup_keys_toml(&only_expired); + unsafe { std::env::set_var("TEMPO_HOME", dir2.path()) }; + let config = + discover_mpp_config(DiscoverOptions { chain_id: Some(4217), ..Default::default() }); + assert!(config.is_none(), "expired-only keys.toml must not yield a usable key"); + + unsafe { std::env::remove_var("TEMPO_HOME") }; + } + #[test] fn parse_keys_toml_unknown_fields_ignored() { let toml_str = r#" diff --git a/crates/common/src/provider/mpp/session.rs b/crates/common/src/provider/mpp/session.rs index 334166b844613..c3e87f8cf42b5 100644 --- a/crates/common/src/provider/mpp/session.rs +++ b/crates/common/src/provider/mpp/session.rs @@ -175,6 +175,16 @@ impl SessionProvider { self } + /// Address that funds payments for this provider. + pub fn funding_wallet_address(&self) -> Address { + self.signing_mode.from_address(self.signer.address()) + } + + /// Chain ID from the selected wallet key, when known. + pub const fn key_chain_id(&self) -> Option { + self.key_chain_id + } + /// Set the chain ID and currencies from the key entry used to initialize /// this provider. Used to reject challenges for incompatible chains/currencies. /// When `chain_id` is `None` (e.g. env var key), chain filtering is skipped. diff --git a/crates/common/src/provider/mpp/transport.rs b/crates/common/src/provider/mpp/transport.rs index 67354dc2bd60d..9e3b16dedd59e 100644 --- a/crates/common/src/provider/mpp/transport.rs +++ b/crates/common/src/provider/mpp/transport.rs @@ -4,6 +4,7 @@ //! handling via the MPP protocol. When the RPC endpoint returns a 402 response, //! this transport automatically pays the challenge and retries the request. +use alloy_chains::Chain; use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_transport::{TransportError, TransportErrorKind, TransportFut, TransportResult}; use mpp::{ @@ -16,12 +17,17 @@ use mpp::{ use reqwest::{StatusCode, header::HeaderMap}; use std::{ collections::HashMap, - fmt, - sync::{Mutex, OnceLock}, + env, fmt, io, + io::IsTerminal, + process::{Command, Stdio}, + sync::{ + Arc, LazyLock, Mutex, + atomic::{AtomicBool, Ordering}, + }, task, time::Duration, }; -use tokio::sync::OwnedMutexGuard; +use tokio::sync::{Mutex as AsyncMutex, OwnedMutexGuard}; use tower::Service; use tracing::{Instrument, debug, debug_span, trace}; use url::Url; @@ -39,7 +45,27 @@ const MPP_RETRY_TIMEOUT: Duration = Duration::from_secs(120); /// Resolve the deposit amount from `MPP_DEPOSIT` env var or the default. fn default_deposit() -> u128 { - std::env::var("MPP_DEPOSIT").ok().and_then(|s| s.parse().ok()).unwrap_or(DEFAULT_DEPOSIT) + env::var("MPP_DEPOSIT").ok().and_then(|s| s.parse().ok()).unwrap_or(DEFAULT_DEPOSIT) +} + +#[derive(Clone, Debug, Default)] +pub(crate) struct FundingContext { + wallet_address: Option, + token: Option, + chain_id: Option, +} + +impl FundingContext { + fn token_line(&self) -> String { + self.token + .as_ref() + .map(|token| format!("Requested payment token: {token}\n\n")) + .unwrap_or_default() + } + + fn network(&self) -> Option { + self.chain_id.filter(|chain| chain.is_tempo()).map(|chain| chain.to_string()) + } } fn format_http_diagnostics(headers: &HeaderMap) -> String { @@ -60,12 +86,173 @@ fn format_http_diagnostics(headers: &HeaderMap) -> String { } } +fn tempo_wallet_fund_help(ctx: &FundingContext) -> String { + let mut command = "tempo wallet fund".to_string(); + if let Some(address) = ctx.wallet_address { + command.push_str(&format!(" --address {address}")); + } + if let Some(network) = ctx.network() { + command.push_str(&format!(" --network {network}")); + } + + let mut no_browser = command.clone(); + no_browser.push_str(" --no-browser"); + + format!( + "\n\nTempo wallet payment could not be funded for this paid RPC request.\n\n{}\ + Fund the wallet, then rerun the command:\n {command}\n\n\ + If this CLI is running on a remote or headless host, use:\n {no_browser}", + ctx.token_line() + ) +} + +/// Decide whether the interactive `tempo wallet fund` flow may be launched. +/// +/// Policy (library-safe): +/// - never run inside CI +/// - never run unless both stdin and stderr are real terminals +/// - `FOUNDRY_MPP_NO_AUTO_FUND` is honored as an opt-out; it must not bypass CI/TTY guards in +/// shared transport code that may be embedded inside long-running RPC daemons. +fn interactive_tempo_fund_allowed( + no_auto_fund: Option<&str>, + in_ci: bool, + stdin_is_terminal: bool, + stderr_is_terminal: bool, +) -> bool { + if no_auto_fund.is_some_and(|v| { + !(v == "0" || v.eq_ignore_ascii_case("false") || v.eq_ignore_ascii_case("off")) + }) { + return false; + } + + if in_ci { + return false; + } + + stdin_is_terminal && stderr_is_terminal +} + +fn can_run_interactive_tempo_fund() -> bool { + if cfg!(test) { + return false; + } + + interactive_tempo_fund_allowed( + std::env::var("FOUNDRY_MPP_NO_AUTO_FUND").ok().as_deref(), + std::env::var_os("CI").is_some(), + std::io::stdin().is_terminal(), + std::io::stderr().is_terminal(), + ) +} + +fn tempo_bin() -> String { + std::env::var("TEMPO_BIN").unwrap_or_else(|_| "tempo".to_string()) +} + +async fn run_interactive_tempo_fund(ctx: &FundingContext) -> TransportResult { + if !can_run_interactive_tempo_fund() { + return Ok(false); + } + + let tempo = tempo_bin(); + let mut args = vec!["wallet".to_string(), "fund".to_string()]; + if let Some(address) = ctx.wallet_address { + args.push("--address".to_string()); + args.push(address.to_string()); + } + if let Some(network) = ctx.network() { + args.push("--network".to_string()); + args.push(network); + } + + tracing::warn!( + token = ?ctx.token, + chain_id = ?ctx.chain_id, + "MPP payment could not be funded; opening `tempo wallet fund`" + ); + + let status = tokio::task::spawn_blocking(move || { + Command::new(tempo) + .args(args) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + }) + .await + .map_err(|e| { + TransportErrorKind::custom(std::io::Error::other(format!( + "failed to join tempo wallet fund process: {e}" + ))) + })? + .map_err(|e| { + TransportErrorKind::custom(std::io::Error::other(format!( + "failed to run `tempo wallet fund`: {e}{}", + tempo_wallet_fund_help(ctx) + ))) + })?; + + if status.success() { + Ok(true) + } else { + Err(TransportErrorKind::custom(std::io::Error::other(format!( + "`tempo wallet fund` exited with status {status}{}", + tempo_wallet_fund_help(ctx) + )))) + } +} + +/// Single-attempt guard around [`run_interactive_tempo_fund`]. +/// +/// Ensures that for one logical request we launch `tempo wallet fund` at most +/// once, regardless of how many recovery paths (`do_request`, `pay_and_retry`, +/// `handle_response_or_retry_after_fund`, ...) attempt it. +async fn maybe_auto_fund(used: &AtomicBool, ctx: &FundingContext) -> TransportResult { + if !can_run_interactive_tempo_fund() { + return Ok(false); + } + if used.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() { + return Ok(false); + } + run_interactive_tempo_fund(ctx).await +} + +/// Returns true iff a 402 response carries a structured insufficient-balance +/// problem (RFC 9457 `PaymentErrorDetails`). +/// +/// We deliberately do **not** match on free-text body content or on generic +/// `verification-failed` problem types, as those have many non-funding causes +/// (bad signature, replay, expired challenge, clock skew, key provisioning, +/// malformed auth, ...). +fn should_suggest_tempo_fund(status: StatusCode, body: &[u8]) -> bool { + if status != StatusCode::PAYMENT_REQUIRED { + return false; + } + let Ok(problem) = serde_json::from_slice::(body) else { + return false; + }; + problem.problem_type.ends_with("/insufficient-balance") +} + +fn format_mpp_payment_failure( + error: impl fmt::Display, + ctx: &FundingContext, + suggest_fund: bool, +) -> String { + let message = error.to_string(); + if suggest_fund { + format!("MPP payment failed: {message}{}", tempo_wallet_fund_help(ctx)) + } else { + format!("MPP payment failed: {message}") + } +} + /// Process-wide payment serialization locks, keyed by origin URL. /// /// Created eagerly so the lock exists before the first provider init, /// preventing concurrent first-402 races. -static GLOBAL_PAY_LOCKS: OnceLock>>>> = - OnceLock::new(); +static GLOBAL_PAY_LOCKS: LazyLock>>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); /// Production transport: lazily discovers MPP keys from the Tempo wallet on /// first 402 response. @@ -75,24 +262,21 @@ pub type LazyMppHttpTransport = MppHttpTransport; /// Tempo wallet configuration on first use. #[derive(Clone, Debug)] pub struct LazySessionProvider { - inner: std::sync::Arc>>, + inner: Arc>>, /// Eagerly-created, process-wide payment serialization lock for this origin. - pay_lock: std::sync::Arc>, + pay_lock: Arc>, origin: String, } impl LazySessionProvider { pub(super) fn new(origin: String) -> Self { - let pay_lock = { - let global = GLOBAL_PAY_LOCKS.get_or_init(|| Mutex::new(HashMap::new())); - global - .lock() - .unwrap() - .entry(origin.clone()) - .or_insert_with(|| std::sync::Arc::new(tokio::sync::Mutex::new(()))) - .clone() - }; - Self { inner: std::sync::Arc::new(Mutex::new(None)), pay_lock, origin } + let pay_lock = GLOBAL_PAY_LOCKS + .lock() + .unwrap() + .entry(origin.clone()) + .or_insert_with(|| Arc::new(AsyncMutex::new(()))) + .clone(); + Self { inner: Arc::new(Mutex::new(None)), pay_lock, origin } } fn set_key_provisioned(&self, provisioned: bool) { @@ -125,6 +309,14 @@ impl LazySessionProvider { } } + /// Drop the cached `SessionProvider` so the next `get_or_init` re-runs + /// discovery. Called after the device-code flow writes a fresh + /// `keys.toml` entry, so a long-lived transport doesn't keep paying with + /// the superseded key. + fn invalidate(&self) { + *self.inner.lock().unwrap() = None; + } + pub(super) fn get_or_init(&self, opts: DiscoverOptions) -> TransportResult { let mut guard = self.inner.lock().unwrap(); if let Some(ref provider) = *guard { @@ -132,18 +324,20 @@ impl LazySessionProvider { } let config = discover_mpp_config(opts).ok_or_else(|| { - TransportErrorKind::custom(std::io::Error::other( + TransportErrorKind::custom(io::Error::other( "RPC endpoint returned HTTP 402 Payment Required. \ This endpoint requires payment via the Machine Payments Protocol (MPP).\n\n\ - To configure MPP, install the Tempo wallet CLI and create a key:\n\ - \n curl -sSL https://tempo.xyz/install.sh | bash\ - \n tempo wallet login\ + Authorize an access key against your Tempo wallet:\n\ + \n cast tempo login\ + \n\nIn headless environments, pass `--no-browser` to print the authorization \ + URL instead of launching a browser:\n\ + \n cast tempo login --no-browser\ \n\nSee https://docs.tempo.xyz for more information.", )) })?; let signer: mpp::PrivateKeySigner = config.key.parse().map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!("invalid MPP key: {e}"))) + TransportErrorKind::custom(io::Error::other(format!("invalid MPP key: {e}"))) })?; let signing_mode = if let Some(wallet) = config.wallet_address { @@ -152,7 +346,7 @@ impl LazySessionProvider { .as_ref() .map(|hex_str| { crate::tempo::decode_key_authorization(hex_str).map(Box::new).map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( + TransportErrorKind::custom(io::Error::other(format!( "invalid MPP key_authorization: {e}" ))) }) @@ -223,6 +417,17 @@ where P::Provider: Send + Sync + 'static, { async fn do_request(self, req: RequestPacket) -> TransportResult { + // Per-request guard: launch `tempo wallet fund` at most once for one + // logical request, regardless of how many recovery paths attempt it. + let auto_fund_used = AtomicBool::new(false); + self.do_request_inner(req, &auto_fund_used).await + } + + async fn do_request_inner( + self, + req: RequestPacket, + auto_fund_used: &AtomicBool, + ) -> TransportResult { let body = serde_json::to_vec(&req).map_err(TransportErrorKind::custom)?; let headers = req.headers(); @@ -246,15 +451,53 @@ where // held until the retry response is fully handled. let _pay_guard = self.provider.lock_pay().await; - let (resolved, challenge) = Self::select_challenge(&resp, &self.provider)?; + // No local key for any offered challenge → run device-code flow, + // invalidate the cached provider, and fetch a fresh 402 (the original + // may have expired during the browser/passkey flow). + let (resolved, challenge) = + if let Some(chain_id) = tempo_chain_needing_auth(&self.url, &resp) { + debug!(chain_id, "launching wallet.tempo authorization"); + let cfg = crate::tempo::EnsureAccessKeyConfig::from_env(chain_id); + crate::tempo::ensure_access_key(cfg).await.map_err(|e| { + TransportErrorKind::custom(io::Error::other(format!( + "tempo access key authorization failed: {e}" + ))) + })?; + self.provider.invalidate_cached_provider(); + self.fetch_fresh_challenge(&headers, &body).await? + } else { + Self::select_challenge(&resp, &self.provider)? + }; + let funding_ctx = self.provider.funding_context(&challenge); debug!(id = %challenge.id, method = %challenge.method, intent = %challenge.intent, "received MPP 402 challenge, paying"); - let credential = resolved.pay(&challenge).await.map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!("MPP payment failed: {e}"))) - })?; + let credential = match resolved.pay(&challenge).await { + Ok(credential) => credential, + Err(e) => { + // Only the explicit `InsufficientBalance` variant is treated as + // a fundable error. Any other failure must surface unchanged so + // we don't mask payment/protocol issues behind a fund prompt. + let is_insufficient = matches!(e, mpp::MppError::InsufficientBalance(_)); + self.provider.rollback_pending(); + if is_insufficient && maybe_auto_fund(auto_fund_used, &funding_ctx).await? { + resolved.pay(&challenge).await.map_err(|e2| { + let suggest = matches!(e2, mpp::MppError::InsufficientBalance(_)); + self.provider.rollback_pending(); + TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e2, &funding_ctx, suggest), + )) + })? + } else { + return Err(TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e, &funding_ctx, is_insufficient), + ))); + } + } + }; let auth_header = format_authorization(&credential).map_err(|e| { + self.provider.rollback_pending(); TransportErrorKind::custom(std::io::Error::other(format!( "failed to format MPP credential: {e}" ))) @@ -286,9 +529,20 @@ where self.provider.commit_topup_and_track_voucher(); let resolved = self.provider.resolve()?; - let voucher_resp = self.pay_and_retry(&challenge, &resolved, &headers, &body).await?; - - let result = Self::handle_response(voucher_resp).await; + let voucher_resp = + self.pay_and_retry(&challenge, &resolved, &headers, &body, auto_fund_used).await?; + + // Route the voucher response through the funding-aware handler so + // a final 402 here also gets the fund retry / contextual help. + let result = self + .handle_response_or_retry_after_fund( + voucher_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; if result.is_ok() { self.provider.set_key_provisioned(true); self.provider.flush_pending(); @@ -304,7 +558,7 @@ where self.provider.rollback_pending(); self.provider.clear_channels(); - return Err(TransportErrorKind::custom(std::io::Error::other( + return Err(TransportErrorKind::custom(io::Error::other( "MPP channel not found on server (410 Gone). \ The server may have restarted or the channel was closed externally.\n\ Local channel state has been cleared. Re-run to open a new channel.", @@ -333,10 +587,19 @@ where debug!("MPP voucher stale, retrying with fresh voucher"); let resolved = self.provider.resolve()?; if resolved.supports(challenge.method.as_str(), challenge.intent.as_str()) { - let final_resp = - self.pay_and_retry(&challenge, &resolved, &headers, &body).await?; - - let result = Self::handle_response(final_resp).await; + let final_resp = self + .pay_and_retry(&challenge, &resolved, &headers, &body, auto_fund_used) + .await?; + + let result = self + .handle_response_or_retry_after_fund( + final_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; if result.is_ok() { self.provider.flush_pending(); } else { @@ -372,10 +635,19 @@ where let (resolved, fresh_challenge) = self.fetch_fresh_challenge(&headers, &body).await?; - let final_resp = - self.pay_and_retry(&fresh_challenge, &resolved, &headers, &body).await?; - - let result = Self::handle_response(final_resp).await; + let final_resp = self + .pay_and_retry(&fresh_challenge, &resolved, &headers, &body, auto_fund_used) + .await?; + + let result = self + .handle_response_or_retry_after_fund( + final_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; if result.is_ok() { self.provider.set_key_provisioned(true); self.provider.flush_pending(); @@ -386,9 +658,40 @@ where } self.provider.rollback_pending(); + if should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &retry_body) + && maybe_auto_fund(auto_fund_used, &funding_ctx).await? + { + let (resolved, fresh_challenge) = + self.fetch_fresh_challenge(&headers, &body).await?; + let final_resp = self + .pay_and_retry(&fresh_challenge, &resolved, &headers, &body, auto_fund_used) + .await?; + + let result = self + .handle_response_or_retry_after_fund( + final_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; + if result.is_ok() { + self.provider.set_key_provisioned(true); + self.provider.flush_pending(); + } else { + self.provider.rollback_pending(); + } + return result; + } + + let mut error_text = format!("{retry_text}{diagnostics}"); + if should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &retry_body) { + error_text.push_str(&tempo_wallet_fund_help(&funding_ctx)); + } return Err(TransportErrorKind::http_error( StatusCode::PAYMENT_REQUIRED.as_u16(), - format!("{retry_text}{diagnostics}"), + error_text, )); } @@ -409,15 +712,32 @@ where provider: &P::Provider, headers: &reqwest::header::HeaderMap, body: &[u8], + auto_fund_used: &AtomicBool, ) -> TransportResult { - let credential = provider.pay(challenge).await.map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(std::io::Error::other(format!("MPP payment failed: {e}"))) - })?; + let funding_ctx = self.provider.funding_context(challenge); + let credential = match provider.pay(challenge).await { + Ok(credential) => credential, + Err(e) => { + self.provider.rollback_pending(); + let is_insufficient = matches!(e, mpp::MppError::InsufficientBalance(_)); + if is_insufficient && maybe_auto_fund(auto_fund_used, &funding_ctx).await? { + provider.pay(challenge).await.map_err(|e2| { + let suggest = matches!(e2, mpp::MppError::InsufficientBalance(_)); + TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e2, &funding_ctx, suggest), + )) + })? + } else { + return Err(TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e, &funding_ctx, is_insufficient), + ))); + } + } + }; let auth_header = format_authorization(&credential).map_err(|e| { self.provider.rollback_pending(); - TransportErrorKind::custom(std::io::Error::other(format!( + TransportErrorKind::custom(io::Error::other(format!( "failed to format MPP credential: {e}" ))) })?; @@ -437,6 +757,41 @@ where }) } + async fn handle_response_or_retry_after_fund( + &self, + resp: reqwest::Response, + headers: &reqwest::header::HeaderMap, + body: &[u8], + funding_ctx: &FundingContext, + auto_fund_used: &AtomicBool, + ) -> TransportResult { + if resp.status() != StatusCode::PAYMENT_REQUIRED { + return Self::handle_response_with_funding(resp, Some(funding_ctx)).await; + } + + let diagnostics = format_http_diagnostics(resp.headers()); + let status = resp.status(); + let resp_body = resp.bytes().await.map_err(TransportErrorKind::custom)?; + + if should_suggest_tempo_fund(status, &resp_body) + && maybe_auto_fund(auto_fund_used, funding_ctx).await? + { + self.provider.rollback_pending(); + + let (resolved, fresh_challenge) = self.fetch_fresh_challenge(headers, body).await?; + let final_resp = self + .pay_and_retry(&fresh_challenge, &resolved, headers, body, auto_fund_used) + .await?; + return Self::handle_response_with_funding(final_resp, Some(funding_ctx)).await; + } + + let mut error_text = format!("{}{diagnostics}", String::from_utf8_lossy(&resp_body)); + if should_suggest_tempo_fund(status, &resp_body) { + error_text.push_str(&tempo_wallet_fund_help(funding_ctx)); + } + Err(TransportErrorKind::http_error(status.as_u16(), error_text)) + } + /// Fetch a fresh 402 challenge from the server (unauthenticated request). /// /// Returns `Ok(Some((provider, challenge)))` if the server returns a 402 @@ -462,7 +817,7 @@ where // Non-402 → return whatever the server sent (could be success or error). let result = Self::handle_response(fresh_resp).await; return Err(result.err().unwrap_or_else(|| { - TransportErrorKind::custom(std::io::Error::other( + TransportErrorKind::custom(io::Error::other( "unexpected success on unauthenticated fresh probe", )) })); @@ -477,25 +832,14 @@ where resp: &reqwest::Response, provider: &P, ) -> TransportResult<(P::Provider, mpp::protocol::core::PaymentChallenge)> { - let www_auth_values: Vec<&str> = resp - .headers() - .get_all(WWW_AUTHENTICATE_HEADER) - .iter() - .filter_map(|v| v.to_str().ok()) - .collect(); - - if www_auth_values.is_empty() { - return Err(TransportErrorKind::custom(std::io::Error::other(format!( + let challenges = parse_challenges(resp); + if challenges.is_empty() && resp.headers().get(WWW_AUTHENTICATE_HEADER).is_none() { + return Err(TransportErrorKind::custom(io::Error::other(format!( "402 response missing WWW-Authenticate header{}", format_http_diagnostics(resp.headers()) )))); } - let challenges: Vec<_> = parse_www_authenticate_all(www_auth_values) - .into_iter() - .filter_map(|r| r.ok()) - .collect(); - let mut last_resolve_err: Option = None; let resolved_pair = challenges.iter().find_map(|c| { let (chain_id, currency) = extract_challenge_chain_and_currency(c); @@ -515,7 +859,7 @@ where } let offered: Vec<_> = challenges.iter().map(|c| format!("{}.{}", c.method, c.intent)).collect(); - TransportErrorKind::custom(std::io::Error::other(format!( + TransportErrorKind::custom(io::Error::other(format!( "no supported MPP challenge; server offered [{}]", offered.join(", "), ))) @@ -523,6 +867,17 @@ where } async fn handle_response(resp: reqwest::Response) -> TransportResult { + Self::handle_response_with_funding(resp, None).await + } + + /// Like [`Self::handle_response`] but, when an unsuccessful 402 looks like a + /// fundable error, appends actionable `tempo wallet fund` help that uses + /// the per-request `FundingContext` (so the suggested command includes + /// `--address` and `--network` when known). + async fn handle_response_with_funding( + resp: reqwest::Response, + funding_ctx: Option<&FundingContext>, + ) -> TransportResult { let status = resp.status(); debug!(%status, "received response from MPP transport"); let diagnostics = format_http_diagnostics(resp.headers()); @@ -536,10 +891,19 @@ where } if !status.is_success() { - return Err(TransportErrorKind::http_error( - status.as_u16(), - format!("{}{diagnostics}", String::from_utf8_lossy(&body)), - )); + let mut body_text = format!("{}{diagnostics}", String::from_utf8_lossy(&body)); + if should_suggest_tempo_fund(status, &body) { + let default_ctx; + let ctx = match funding_ctx { + Some(c) => c, + None => { + default_ctx = FundingContext::default(); + &default_ctx + } + }; + body_text.push_str(&tempo_wallet_fund_help(ctx)); + } + return Err(TransportErrorKind::http_error(status.as_u16(), body_text)); } serde_json::from_slice(&body) @@ -547,6 +911,57 @@ where } } +/// Returns `Some(chain_id)` when a 402 response should trigger the +/// `wallet.tempo.xyz` device-code authorization flow. +/// +/// Conditions: known Tempo endpoint, interactive (TTY, not `CI`), and no +/// offered Tempo challenge resolves against a local key on `(chain, currency)`. +/// The picked chain matches the first unresolved challenge — same iteration +/// order [`MppHttpTransport::select_challenge`] uses. +fn tempo_chain_needing_auth(url: &Url, resp: &reqwest::Response) -> Option { + if !io::stderr().is_terminal() || env::var_os("CI").is_some() { + return None; + } + pick_chain_needing_auth(url, &parse_challenges(resp)) +} + +/// Extract all parseable MPP challenges from a 402 response's `WWW-Authenticate` headers. +fn parse_challenges(resp: &reqwest::Response) -> Vec { + let values: Vec<&str> = resp + .headers() + .get_all(WWW_AUTHENTICATE_HEADER) + .iter() + .filter_map(|v| v.to_str().ok()) + .collect(); + parse_www_authenticate_all(values).into_iter().filter_map(|r| r.ok()).collect() +} + +/// Inner logic of [`tempo_chain_needing_auth`], factored out for testing. +fn pick_chain_needing_auth( + url: &Url, + challenges: &[mpp::protocol::core::PaymentChallenge], +) -> Option { + if !crate::tempo::is_known_tempo_endpoint(url) { + return None; + } + + let tempo_challenges: Vec<_> = + challenges.iter().filter(|c| c.method.as_str() == "tempo").collect(); + + // If any challenge already resolves with a local key, no auth needed. + let any_resolvable = tempo_challenges.iter().any(|c| { + let (chain_id, currency) = extract_challenge_chain_and_currency(c); + let currency = currency.and_then(|s| s.parse().ok()); + super::keys::discover_mpp_config(super::keys::DiscoverOptions { chain_id, currency }) + .is_some() + }); + if any_resolvable { + return None; + } + + tempo_challenges.iter().find_map(|c| extract_challenge_chain_and_currency(c).0) +} + /// Extract `(chainId, currency)` from a parsed MPP challenge. pub(super) fn extract_challenge_chain_and_currency( c: &mpp::protocol::core::PaymentChallenge, @@ -576,10 +991,28 @@ pub(crate) trait ResolveProvider { fn flush_pending(&self) {} fn rollback_pending(&self) {} fn commit_topup_and_track_voucher(&self) {} + /// Drop any cached payment provider so the next `resolve_for` re-runs + /// discovery. Called after the device-code flow writes a fresh + /// `keys.toml` entry. + fn invalidate_cached_provider(&self) {} + fn funding_wallet_address(&self) -> Option { + None + } + fn funding_chain_id(&self) -> Option { + None + } + fn funding_context(&self, challenge: &mpp::protocol::core::PaymentChallenge) -> FundingContext { + let (challenge_chain_id, token) = extract_challenge_chain_and_currency(challenge); + FundingContext { + wallet_address: self.funding_wallet_address(), + token, + chain_id: challenge_chain_id.or_else(|| self.funding_chain_id()).map(Chain::from_id), + } + } /// Acquire the payment serialization lock. The returned guard must be held /// across the entire 402 → pay → retry → response cycle to prevent /// concurrent channel opens and colliding expiring-nonce transactions. - fn lock_pay(&self) -> impl std::future::Future>> + Send { + fn lock_pay(&self) -> impl Future>> + Send { async { None } } } @@ -599,7 +1032,7 @@ impl ResolveProvider for LazySessionProvider { // regardless of opts. Re-check that the provider's key is compatible // with this challenge's chain/currency. if !provider.matches_challenge(opts.chain_id, opts.currency) { - return Err(TransportErrorKind::custom(std::io::Error::other( + return Err(TransportErrorKind::custom(io::Error::other( "cached provider does not match challenge chain/currency", ))); } @@ -623,7 +1056,16 @@ impl ResolveProvider for LazySessionProvider { fn commit_topup_and_track_voucher(&self) { Self::commit_topup_and_track_voucher(self) } - fn lock_pay(&self) -> impl std::future::Future>> + Send { + fn invalidate_cached_provider(&self) { + Self::invalidate(self) + } + fn funding_wallet_address(&self) -> Option { + self.inner.lock().unwrap().as_ref().map(|p| p.funding_wallet_address()) + } + fn funding_chain_id(&self) -> Option { + self.inner.lock().unwrap().as_ref().and_then(|p| p.key_chain_id()) + } + fn lock_pay(&self) -> impl Future>> + Send { let lock = self.pay_lock.clone(); async move { Some(lock.lock_owned().await) } } @@ -685,7 +1127,7 @@ mod tests { fn pay( &self, challenge: &PaymentChallenge, - ) -> impl std::future::Future> + Send { + ) -> impl Future> + Send { let echo = challenge.to_echo(); async move { Ok(PaymentCredential::with_source( @@ -697,6 +1139,21 @@ mod tests { } } + #[derive(Clone, Debug)] + struct InsufficientBalanceProvider; + + impl PaymentProvider for InsufficientBalanceProvider { + fn supports(&self, method: &str, intent: &str) -> bool { + method == "tempo" && (intent == "session" || intent == "charge") + } + + async fn pay(&self, _challenge: &PaymentChallenge) -> Result { + Err(MppError::InsufficientBalance(Some( + "wallet has 0 pathUSD but needs 100000".to_string(), + ))) + } + } + fn test_challenge() -> (PaymentChallenge, String) { let request = Base64UrlJson::from_value(&serde_json::json!({ "amount": "1000", @@ -853,8 +1310,238 @@ mod tests { handle.abort(); } + #[tokio::test] + async fn test_mpp_transport_payment_failure_suggests_tempo_wallet_fund() { + let (_, www_auth) = test_challenge(); + + let app = axum::Router::new().route( + "/", + post(move || { + let www_auth = www_auth.clone(); + async move { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("www-authenticate", www_auth)], + "Payment Required", + ) + } + }), + ); + + let (base_url, handle) = spawn_server(app).await; + let mut transport = MppHttpTransport::new( + reqwest::Client::new(), + Url::parse(&base_url).unwrap(), + InsufficientBalanceProvider, + ); + + let err = tower::Service::call(&mut transport, test_request()).await.unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("Tempo wallet payment could not be funded"), "got: {msg}"); + assert!(msg.contains("tempo wallet fund"), "got: {msg}"); + assert!(msg.contains("--no-browser"), "got: {msg}"); + assert!(msg.contains("Requested payment token: 0x20c0"), "got: {msg}"); + + handle.abort(); + } + + #[tokio::test] + async fn test_mpp_transport_retry_402_insufficient_balance_suggests_fund() { + let (_, www_auth) = test_challenge(); + + let app = axum::Router::new().route( + "/", + post(move |req: axum::http::Request| { + let www_auth = www_auth.clone(); + async move { + if req.headers().get("authorization").is_some() { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("content-type", "application/problem+json")], + serde_json::to_string( + &mpp::error::PaymentErrorDetails::session("insufficient-balance") + .with_title("InsufficientBalanceError") + .with_detail( + "Insufficient pathUSD balance: have 0, need 100000", + ), + ) + .unwrap(), + ) + .into_response() + } else { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("www-authenticate", www_auth)], + "Payment Required".to_string(), + ) + .into_response() + } + } + }), + ); + + let (base_url, handle) = spawn_server(app).await; + let mut transport = MppHttpTransport::new( + reqwest::Client::new(), + Url::parse(&base_url).unwrap(), + MockPaymentProvider, + ); + + let err = tower::Service::call(&mut transport, test_request()).await.unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("InsufficientBalanceError"), "got: {msg}"); + assert!(msg.contains("Tempo wallet payment could not be funded"), "got: {msg}"); + assert!(msg.contains("tempo wallet fund"), "got: {msg}"); + assert!(msg.contains("--no-browser"), "got: {msg}"); + assert!(msg.contains("Requested payment token: 0x20c0"), "got: {msg}"); + + handle.abort(); + } + + /// Generic `verification-failed` has many non-funding causes (bad signature, + /// replay, expired challenge, clock skew, ...). The transport must surface + /// the original error verbatim and must NOT add a "fund your wallet" hint. + #[tokio::test] + async fn test_mpp_transport_final_402_verification_failed_does_not_suggest_fund() { + let (_, www_auth) = test_challenge(); + + let app = axum::Router::new().route( + "/", + post(move |req: axum::http::Request| { + let www_auth = www_auth.clone(); + async move { + if req.headers().get("authorization").is_some() { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("content-type", "application/problem+json")], + serde_json::to_string( + &mpp::error::PaymentErrorDetails::core("verification-failed") + .with_title("Verification Failed") + .with_detail("Payment verification failed."), + ) + .unwrap(), + ) + .into_response() + } else { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("www-authenticate", www_auth)], + "Payment Required".to_string(), + ) + .into_response() + } + } + }), + ); + + let (base_url, handle) = spawn_server(app).await; + let mut transport = MppHttpTransport::new( + reqwest::Client::new(), + Url::parse(&base_url).unwrap(), + MockPaymentProvider, + ); + + let err = tower::Service::call(&mut transport, test_request()).await.unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("Verification Failed"), "got: {msg}"); + assert!( + !msg.contains("Tempo wallet payment could not be funded"), + "verification-failed must not be classified as fundable; got: {msg}" + ); + + handle.abort(); + } + + // --- Classifier unit tests -------------------------------------------- + + #[test] + fn classifier_only_triggers_on_explicit_insufficient_balance_problem() { + // explicit insufficient-balance → true + let body = serde_json::to_vec( + &mpp::error::PaymentErrorDetails::session("insufficient-balance") + .with_title("InsufficientBalanceError") + .with_detail("Insufficient pathUSD balance"), + ) + .unwrap(); + assert!(should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &body)); + } + + #[test] + fn classifier_does_not_trigger_on_verification_failed() { + let body = serde_json::to_vec( + &mpp::error::PaymentErrorDetails::core("verification-failed") + .with_title("Verification Failed") + .with_detail("Payment verification failed."), + ) + .unwrap(); + assert!(!should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &body)); + } + + #[test] + fn classifier_does_not_trigger_on_unrelated_text_with_balance_words() { + // Free-text 402 body that just happens to mention the word "balance" + // must NOT trigger the fund suggestion (no structured problem details). + let body = + b"402 Payment Required: server could not balance ledger entries; insufficient inputs."; + assert!(!should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, body)); + } + + #[test] + fn classifier_does_not_trigger_outside_402() { + let body = serde_json::to_vec( + &mpp::error::PaymentErrorDetails::session("insufficient-balance") + .with_detail("Insufficient balance"), + ) + .unwrap(); + assert!(!should_suggest_tempo_fund(StatusCode::INTERNAL_SERVER_ERROR, &body)); + assert!(!should_suggest_tempo_fund(StatusCode::OK, &body)); + } + + #[test] + fn fund_help_includes_address_and_network_for_known_chain() { + let ctx = FundingContext { + wallet_address: Some("0x000000000000000000000000000000000000dEaD".parse().unwrap()), + token: Some("0x20c0".to_string()), + chain_id: Some(Chain::from_id(42431)), + }; + let help = tempo_wallet_fund_help(&ctx); + assert!(help.contains("--address 0x"), "missing --address: {help}"); + assert!(help.contains("--network tempo-moderato"), "missing --network: {help}"); + assert!(help.contains("--no-browser"), "missing --no-browser: {help}"); + assert!(help.contains("Requested payment token: 0x20c0"), "missing token: {help}"); + + let mainnet = FundingContext { chain_id: Some(Chain::from_id(4217)), ..ctx }; + let help2 = tempo_wallet_fund_help(&mainnet); + assert!(help2.contains("--network tempo"), "missing tempo network: {help2}"); + } + + #[test] + fn auto_fund_policy_blocks_in_ci_and_non_tty() { + assert!(!interactive_tempo_fund_allowed(Some("1"), true, true, true), "must not run in CI"); + assert!( + interactive_tempo_fund_allowed(Some("0"), false, true, true), + "FOUNDRY_MPP_NO_AUTO_FUND=0 must not disable" + ); + assert!( + interactive_tempo_fund_allowed(Some("false"), false, true, true), + "FOUNDRY_MPP_NO_AUTO_FUND=false must not disable" + ); + assert!( + !interactive_tempo_fund_allowed(None, false, false, true), + "stdin must be a terminal" + ); + assert!( + !interactive_tempo_fund_allowed(None, false, true, false), + "stderr must be a terminal" + ); + assert!(!interactive_tempo_fund_allowed(Some("1"), false, true, true)); + assert!(!interactive_tempo_fund_allowed(Some("true"), false, true, true)); + assert!(interactive_tempo_fund_allowed(None, false, true, true)); + } + #[tokio::test] async fn test_plain_http_402_shows_mpp_setup_instructions() { + let _g = crate::tempo::test_env_mutex().lock().await; let (_, www_auth) = test_challenge(); let app = axum::Router::new().route( @@ -920,6 +1607,32 @@ mod tests { ); } + /// `invalidate_cached_provider` clears the cache so the next + /// `get_or_init` re-runs discovery — the path `do_request` takes after + /// `ensure_access_key` writes a fresh `keys.toml` entry. + #[tokio::test] + async fn lazy_session_provider_invalidate_clears_cache() { + let _g = crate::tempo::test_env_mutex().lock().await; + // TEMPO_PRIVATE_KEY lets discovery succeed without a keys.toml. + let key_hex = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + unsafe { + std::env::set_var(crate::tempo::TEMPO_PRIVATE_KEY_ENV, key_hex); + std::env::remove_var(crate::tempo::TEMPO_HOME_ENV); + } + + let lazy = LazySessionProvider::new("https://rpc.example.com".into()); + let _ = lazy.get_or_init(Default::default()).expect("discovery succeeds"); + assert!(lazy.inner.lock().unwrap().is_some(), "expected provider to be cached"); + + ResolveProvider::invalidate_cached_provider(&lazy); + assert!(lazy.inner.lock().unwrap().is_none(), "expected cache to be cleared"); + + let _ = lazy.get_or_init(Default::default()).expect("re-discovery succeeds"); + assert!(lazy.inner.lock().unwrap().is_some(), "expected re-init to repopulate cache"); + + unsafe { std::env::remove_var(crate::tempo::TEMPO_PRIVATE_KEY_ENV) }; + } + #[test] fn challenge_chain_and_currency_extraction() { let extract = |headers: Vec<&str>| -> Vec<(Option, Option)> { @@ -955,4 +1668,73 @@ mod tests { ); assert_eq!(extract(vec![&no_details]), vec![(None, Some("0x20c0".into()))]); } + + /// Auth must trigger when a key matches the chain but not the currency. + #[test] + fn pick_chain_needing_auth_currency_aware() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); + let dir = tempfile::tempdir().unwrap(); + let wallet = dir.path().join("wallet"); + std::fs::create_dir_all(&wallet).unwrap(); + std::fs::write( + wallet.join("keys.toml"), + r#" +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000001" +key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +chain_id = 4217 + +[[keys.limits]] +currency = "0x20c0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +limit = "1000" +"#, + ) + .unwrap(); + unsafe { + std::env::set_var(crate::tempo::TEMPO_HOME_ENV, dir.path()); + std::env::remove_var(crate::tempo::TEMPO_PRIVATE_KEY_ENV); + } + + let url = Url::parse("https://rpc.mpp.tempo.xyz").unwrap(); + let mk = |currency: &str| -> PaymentChallenge { + PaymentChallenge { + id: "x".into(), + realm: "api".into(), + method: MethodName::new("tempo"), + intent: IntentName::new("charge"), + request: Base64UrlJson::from_value(&serde_json::json!({ + "amount": "1", + "currency": currency, + "recipient": "0xabc", + "methodDetails": { "chainId": 4217 } + })) + .unwrap(), + expires: None, + description: None, + digest: None, + opaque: None, + } + }; + + // Currency mismatch → auth needed. + let mismatched = mk("0x20c0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + assert_eq!(pick_chain_needing_auth(&url, &[mismatched]), Some(4217)); + + // Currency match → no auth. + let matched = mk("0x20c0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + assert_eq!(pick_chain_needing_auth(&url, &[matched]), None); + + // Non-Tempo host → never triggers, even without a key. + let stripe_url = Url::parse("https://api.stripe.com").unwrap(); + assert_eq!( + pick_chain_needing_auth( + &stripe_url, + &[mk("0x20c0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")] + ), + None, + ); + + unsafe { std::env::remove_var(crate::tempo::TEMPO_HOME_ENV) }; + } } diff --git a/crates/common/src/provider/mpp/ws.rs b/crates/common/src/provider/mpp/ws.rs index f631d0b08a2fc..69aef7d4f4cbc 100644 --- a/crates/common/src/provider/mpp/ws.rs +++ b/crates/common/src/provider/mpp/ws.rs @@ -378,6 +378,8 @@ mod tests { /// MPP server sends challenge → client pays → server sends receipt. #[tokio::test] async fn test_ws_mpp_challenge_credential_receipt() { + // Serialize with other tests that mutate TEMPO_PRIVATE_KEY / TEMPO_HOME. + let _g = crate::tempo::test_env_mutex().lock().await; let challenge = test_challenge(); let challenge_json = serde_json::to_value(&challenge).unwrap(); @@ -452,6 +454,8 @@ mod tests { /// MPP server sends challenge, client pays, server closes → rollback. #[tokio::test] async fn test_ws_mpp_rollback_on_post_pay_close() { + // Serialize with other tests that mutate TEMPO_PRIVATE_KEY / TEMPO_HOME. + let _g = crate::tempo::test_env_mutex().lock().await; let challenge = test_challenge(); let challenge_json = serde_json::to_value(&challenge).unwrap(); diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 7db1ebd1b3f91..f59a2efa75b8e 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -36,7 +36,11 @@ fn is_known_mpp_endpoint(url: &Url) -> bool { /// Only meant to be used internally by [RuntimeTransport]. #[derive(Clone, Debug)] pub enum InnerTransport { - /// HTTP transport with lazy MPP 402 handling + /// HTTP transport with lazy MPP 402 handling. + /// + /// For known Tempo endpoints, the MPP layer additionally runs the + /// `wallet.tempo.xyz` device-code flow on a 402 when no local access key + /// is configured (see [`crate::tempo::ensure_access_key`]). Http(LazyMppHttpTransport), /// WebSocket transport Ws(PubSubFrontend), diff --git a/crates/common/src/tempo/auth.rs b/crates/common/src/tempo/auth.rs new file mode 100644 index 0000000000000..d79306cfb74f2 --- /dev/null +++ b/crates/common/src/tempo/auth.rs @@ -0,0 +1,494 @@ +//! Tempo wallet device-code authorization flow. +//! +//! Implements the CLI side of the tempoxyz/accounts `cli-auth` device-code +//! protocol: generates a local secp256k1 access key, creates a PKCE-protected +//! device code, opens `wallet.tempo.xyz/cli-auth?code=` in the browser, +//! polls until the user authorizes the key on their passkey wallet, and writes +//! the resulting `keyAuthorization` to `~/.tempo/wallet/keys.toml`. + +use crate::tempo::{ + KeyEntry, KeyType, StoredTokenLimit, WalletType, decode_key_authorization, upsert_key_entry, +}; +use alloy_primitives::{Address, B256, hex}; +use alloy_signer_local::PrivateKeySigner; +use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; +use eyre::Result; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +#[cfg(any(unix, windows))] +use std::process::Command; +use std::{ + env, + sync::LazyLock, + time::{Duration, Instant}, +}; +use tempo_primitives::transaction::{SignatureType, SignedKeyAuthorization}; +use tokio::sync::Mutex; + +/// Default device-code service URL (production wallet.tempo.xyz). +const DEFAULT_CLI_AUTH_URL: &str = "https://wallet.tempo.xyz/cli-auth"; + +/// Returns `true` if `url`'s host is `tempo.xyz` or a subdomain of it. +pub(crate) fn is_known_tempo_endpoint(url: &url::Url) -> bool { + url.host_str().is_some_and(|host| host == "tempo.xyz" || host.ends_with(".tempo.xyz")) +} + +/// Env var to override the device-code service URL (for tests / staging). +const TEMPO_CLI_AUTH_URL_ENV: &str = "TEMPO_CLI_AUTH_URL"; + +const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(2); +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(300); + +/// Per-process serialization of concurrent `ensure_access_key` calls. +/// +/// Prevents two `cast` invocations in the same process from racing two browser +/// popups for the same chain. +static AUTH_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); + +/// Configuration for [`ensure_access_key`]. +#[derive(Clone, Debug)] +pub struct EnsureAccessKeyConfig { + /// Chain ID the access key is being authorized for. + pub chain_id: u64, + /// Device-code service base URL. Defaults to [`DEFAULT_CLI_AUTH_URL`]. + pub(crate) service_url: String, + /// Poll interval. + pub(crate) poll_interval: Duration, + /// Total timeout for the authorization flow. + pub(crate) timeout: Duration, + /// If `true`, print the authorization URL to stderr instead of opening a + /// browser. + pub no_browser: bool, +} + +impl EnsureAccessKeyConfig { + /// Build a config from the environment for the given chain. + /// + /// `no_browser` defaults to `true` under `CI`; callers (e.g. `cast tempo + /// login --no-browser`) may override it. + pub fn from_env(chain_id: u64) -> Self { + Self { + chain_id, + service_url: env::var(TEMPO_CLI_AUTH_URL_ENV) + .unwrap_or_else(|_| DEFAULT_CLI_AUTH_URL.to_string()), + poll_interval: DEFAULT_POLL_INTERVAL, + timeout: DEFAULT_TIMEOUT, + no_browser: env::var_os("CI").is_some(), + } + } +} + +/// Open `url` via the OS default browser handler. On platforms without a known +/// opener, this is a no-op (the URL is still printed by [`ensure_access_key`]). +fn open_browser(_url: &str) { + #[cfg(target_os = "macos")] + let _ = Command::new("open").arg(_url).spawn(); + #[cfg(target_os = "windows")] + let _ = Command::new("cmd").args(["/c", "start", "", _url]).spawn(); + #[cfg(all(unix, not(target_os = "macos")))] + let _ = Command::new("xdg-open").arg(_url).spawn(); +} + +/// Result of [`ensure_access_key`]. +#[derive(Debug, Clone)] +pub struct AccessKeyOutcome { + pub wallet_address: Address, + pub key_address: Address, + pub chain_id: u64, +} + +/// Run the device-code flow, persist the resulting key to `keys.toml`, and +/// return the new entry's identifying fields. +pub async fn ensure_access_key(cfg: EnsureAccessKeyConfig) -> Result { + let _guard = AUTH_LOCK.lock().await; + + let signer = PrivateKeySigner::random(); + let key_address = signer.address(); + // The server requires uncompressed SEC1 (65-byte `0x04 || X || Y`); the + // default `to_sec1_bytes()` would emit the compressed 33-byte form. + let pub_key_hex = format!( + "0x{}", + hex::encode(signer.credential().verifying_key().to_encoded_point(false).as_bytes()), + ); + + let code_verifier = random_code_verifier(); + let client = reqwest::Client::builder().timeout(Duration::from_secs(30)).build()?; + let service = cfg.service_url.trim_end_matches('/'); + + let create_req = CreateCodeRequest { + chain_id: cfg.chain_id, + code_challenge: sha256_b64url(&code_verifier), + key_type: "secp256k1", + pub_key: pub_key_hex, + }; + let code = create_code_with_retry(&client, service, &create_req, cfg.timeout).await?; + + let browser_url = format!("{service}?code={code}"); + if cfg.no_browser { + let _ = crate::sh_eprintln!("Open this URL to authorize: {browser_url}"); + } else { + let _ = crate::sh_eprintln!( + "Opening wallet.tempo to authorize an access key…\n {browser_url}" + ); + open_browser(&browser_url); + } + + let poll = PollRequest { code_verifier }; + let started = Instant::now(); + loop { + // Retry transient network/5xx/429 failures within `cfg.timeout`. + let send_res = client.post(format!("{service}/poll/{code}")).json(&poll).send().await; + + let resp = match send_res { + Ok(r) => r, + Err(e) if is_transient_error(&e) && started.elapsed() < cfg.timeout => { + tracing::debug!(error = %e, "transient error polling device code, retrying"); + tokio::time::sleep(cfg.poll_interval).await; + continue; + } + Err(e) => return Err(e.into()), + }; + + let status = resp.status(); + if !status.is_success() { + if is_transient_status(status) && started.elapsed() < cfg.timeout { + tracing::debug!(%status, "transient HTTP status polling device code, retrying"); + tokio::time::sleep(cfg.poll_interval).await; + continue; + } + let body = resp.text().await.unwrap_or_default(); + eyre::bail!("device-code poll failed ({status}): {body}"); + } + + let body: PollResponse = resp.json().await?; + match body { + PollResponse::Pending => { + if started.elapsed() > cfg.timeout { + eyre::bail!("timed out waiting for wallet authorization (code {code})"); + } + tokio::time::sleep(cfg.poll_interval).await; + } + PollResponse::Expired => { + eyre::bail!("device code {code} expired before authorization"); + } + PollResponse::Authorized { account_address, key_authorization } => { + let hex_str = key_authorization.ok_or_else(|| { + eyre::eyre!("wallet authorized response missing key_authorization") + })?; + let signed: SignedKeyAuthorization = decode_key_authorization(&hex_str)?; + // Reject mismatches before persisting — an unusable keys.toml + // entry would silently break the next 402 retry. + if signed.authorization.key_id != key_address { + eyre::bail!( + "wallet authorized key {} but the locally generated key is {}", + signed.authorization.key_id, + key_address, + ); + } + if signed.authorization.chain_id != cfg.chain_id { + eyre::bail!( + "wallet authorized chain {} but {} was requested", + signed.authorization.chain_id, + cfg.chain_id, + ); + } + if signed.authorization.key_type != SignatureType::Secp256k1 { + eyre::bail!( + "wallet returned keyType {:?} but secp256k1 was requested", + signed.authorization.key_type, + ); + } + let chain_id = signed.authorization.chain_id; + let key_authorization = + if hex_str.starts_with("0x") { hex_str } else { format!("0x{hex_str}") }; + let entry = KeyEntry { + wallet_type: WalletType::Passkey, + wallet_address: account_address, + chain_id, + key_type: match signed.authorization.key_type { + SignatureType::P256 => KeyType::P256, + SignatureType::WebAuthn => KeyType::WebAuthn, + _ => KeyType::Secp256k1, + }, + key_address: Some(key_address), + key: Some(format!("0x{}", hex::encode(signer.to_bytes()))), + key_authorization: Some(key_authorization), + expiry: signed.authorization.expiry.map(|n| n.get()), + limits: signed + .authorization + .limits + .unwrap_or_default() + .into_iter() + .map(|l| StoredTokenLimit { currency: l.token, limit: l.limit.to_string() }) + .collect(), + }; + upsert_key_entry(entry)?; + return Ok(AccessKeyOutcome { + wallet_address: account_address, + key_address, + chain_id, + }); + } + } + } +} + +fn is_transient_error(err: &reqwest::Error) -> bool { + err.is_timeout() || err.is_connect() || err.is_request() +} + +fn is_transient_status(status: reqwest::StatusCode) -> bool { + status.is_server_error() || status == reqwest::StatusCode::TOO_MANY_REQUESTS +} + +/// POST `/code` with exponential backoff on transient errors, bounded by `timeout`. +async fn create_code_with_retry( + client: &reqwest::Client, + service: &str, + req: &CreateCodeRequest, + timeout: Duration, +) -> Result { + let started = Instant::now(); + let mut backoff = Duration::from_millis(500); + loop { + let send_res = client.post(format!("{service}/code")).json(req).send().await; + + match send_res { + Ok(resp) => { + let status = resp.status(); + if status.is_success() { + let CreateCodeResponse { code } = resp.json().await?; + return Ok(code); + } + if is_transient_status(status) && started.elapsed() < timeout { + tracing::debug!(%status, "transient HTTP status creating device code, retrying"); + tokio::time::sleep(backoff).await; + backoff = (backoff * 2).min(Duration::from_secs(5)); + continue; + } + let body = resp.text().await.unwrap_or_default(); + eyre::bail!("device-code create failed ({status}): {body}"); + } + Err(e) if is_transient_error(&e) && started.elapsed() < timeout => { + tracing::debug!(error = %e, "transient error creating device code, retrying"); + tokio::time::sleep(backoff).await; + backoff = (backoff * 2).min(Duration::from_secs(5)); + } + Err(e) => return Err(e.into()), + } + } +} + +fn random_code_verifier() -> String { + let bytes = B256::random(); + URL_SAFE_NO_PAD.encode(bytes.as_slice()) +} + +fn sha256_b64url(input: &str) -> String { + let digest = Sha256::digest(input.as_bytes()); + URL_SAFE_NO_PAD.encode(digest) +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct CreateCodeRequest { + /// `0x`-hex per the SDK schema (server accepts hex string or bigint, not a plain JSON number). + #[serde(serialize_with = "serialize_u64_hex")] + chain_id: u64, + code_challenge: String, + key_type: &'static str, + pub_key: String, +} + +fn serialize_u64_hex(v: &u64, s: S) -> std::result::Result { + s.serialize_str(&format!("0x{v:x}")) +} + +#[derive(Deserialize)] +struct CreateCodeResponse { + code: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct PollRequest { + code_verifier: String, +} + +/// Matches `tempoxyz/wallet` poll response shape. +#[derive(Deserialize)] +#[serde(tag = "status", rename_all = "lowercase")] +enum PollResponse { + Pending, + Expired, + Authorized { + account_address: Address, + #[serde(default)] + key_authorization: Option, + }, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tempo::{TEMPO_HOME_ENV, read_tempo_keys_file, test_env_mutex}; + use axum::{Json, Router, extract::State, routing::post}; + use std::sync::{Arc, Mutex}; + + #[test] + fn pkce_challenge_matches_sdk_format() { + // Vector from RFC 7636 §4.2. + let verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + let challenge = sha256_b64url(verifier); + assert_eq!(challenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"); + } + + /// Recover the EOA from a SEC1-encoded public key (compressed or + /// uncompressed). + fn address_from_sec1_hex(s: &str) -> Address { + let stripped = s.strip_prefix("0x").unwrap_or(s); + let bytes = hex::decode(stripped).expect("valid hex"); + let vk = k256::ecdsa::VerifyingKey::from_sec1_bytes(&bytes).expect("valid SEC1 pubkey"); + Address::from_public_key(&vk) + } + + #[derive(Clone)] + struct MockState { + wallet: Arc>>, + /// Derived from the `pubKey` posted to `/code` so `/poll` can echo + /// back a matching `keyId`, like a real wallet would. + key_id: Arc>>, + /// Chain ID the mock `/poll` returns in `keyAuthorization`. + poll_chain_id: u64, + } + + async fn create_code_handler( + State(state): State, + Json(body): Json, + ) -> Json { + // Sanity: required fields present and chainId is a 0x-hex string, + // matching the SDK wire format the live server enforces. + let pub_key = body + .get("pubKey") + .and_then(|v| v.as_str()) + .unwrap_or_else(|| panic!("pubKey missing: {body}")); + assert!(body.get("codeChallenge").is_some(), "codeChallenge missing: {body}"); + let chain_id = body.get("chainId").unwrap_or_else(|| panic!("chainId missing: {body}")); + let chain_str = chain_id + .as_str() + .unwrap_or_else(|| panic!("chainId must be string, got {chain_id}: {body}")); + assert!(chain_str.starts_with("0x"), "chainId must be 0x-hex, got {chain_str}"); + let wallet: Address = "0x0000000000000000000000000000000000000042".parse().unwrap(); + *state.wallet.lock().unwrap() = Some(wallet); + *state.key_id.lock().unwrap() = Some(address_from_sec1_hex(pub_key)); + Json(serde_json::json!({ "code": "ABCDEFGH" })) + } + + /// Build the RLP-hex `SignedKeyAuthorization` blob the live server returns + /// in the `key_authorization` field. + fn signed_key_auth_hex(chain_id: u64, key_id: Address, expiry: u64) -> String { + use alloy_rlp::Encodable; + use tempo_primitives::transaction::{KeyAuthorization, PrimitiveSignature}; + let auth = KeyAuthorization::unrestricted(chain_id, SignatureType::Secp256k1, key_id) + .with_expiry(expiry); + let sig: PrimitiveSignature = serde_json::from_value(serde_json::json!({ + "type": "secp256k1", "r": "0x0", "s": "0x0", "yParity": 0 + })) + .unwrap(); + let signed = auth.into_signed(sig); + let mut buf = Vec::new(); + signed.encode(&mut buf); + format!("0x{}", hex::encode(buf)) + } + + async fn poll_handler(State(state): State) -> Json { + let wallet = state.wallet.lock().unwrap().expect("create_code must be called first"); + let key_id = state.key_id.lock().unwrap().expect("create_code must be called first"); + Json(serde_json::json!({ + "status": "authorized", + "account_address": wallet, + "key_authorization": signed_key_auth_hex(state.poll_chain_id, key_id, 9_999_999_999), + })) + } + + /// Spawn a mock wallet.tempo server whose `/poll` echoes `poll_chain_id`. + async fn spawn_mock_wallet(poll_chain_id: u64) -> (String, tokio::task::JoinHandle<()>) { + let app = Router::new() + .route("/code", post(create_code_handler)) + .route("/poll/{code}", post(poll_handler)) + .with_state(MockState { + wallet: Arc::default(), + key_id: Arc::default(), + poll_chain_id, + }); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let handle = tokio::spawn(async move { + axum::serve(listener, app).await.unwrap(); + }); + (format!("http://{addr}"), handle) + } + + fn test_cfg(service_url: String) -> EnsureAccessKeyConfig { + EnsureAccessKeyConfig { + chain_id: 4217, + service_url, + poll_interval: Duration::from_millis(10), + timeout: Duration::from_secs(2), + no_browser: true, + } + } + + #[tokio::test(flavor = "multi_thread")] + async fn ensure_access_key_happy_path_writes_keys_toml() { + // SAFETY: serialized with other tests that mutate TEMPO_HOME. + let _g = test_env_mutex().lock().await; + let tmp = tempfile::tempdir().unwrap(); + unsafe { std::env::set_var(TEMPO_HOME_ENV, tmp.path()) }; + + let (service_url, server) = spawn_mock_wallet(4217).await; + let outcome = ensure_access_key(test_cfg(service_url)).await.unwrap(); + + let expected_wallet: Address = + "0x0000000000000000000000000000000000000042".parse().unwrap(); + assert_eq!(outcome.chain_id, 4217); + assert_eq!(outcome.wallet_address, expected_wallet); + + let file = read_tempo_keys_file().expect("keys.toml written"); + assert_eq!(file.keys.len(), 1); + let entry = &file.keys[0]; + assert_eq!(entry.wallet_address, outcome.wallet_address); + assert_eq!(entry.key_address, Some(outcome.key_address)); + assert_eq!(entry.chain_id, 4217); + assert_eq!(entry.expiry, Some(9_999_999_999)); + let decoded: tempo_primitives::transaction::SignedKeyAuthorization = + crate::tempo::decode_key_authorization(entry.key_authorization.as_deref().unwrap()) + .expect("RLP roundtrip"); + assert_eq!(decoded.authorization.chain_id, 4217); + + server.abort(); + unsafe { std::env::remove_var(TEMPO_HOME_ENV) }; + } + + #[tokio::test(flavor = "multi_thread")] + async fn ensure_access_key_rejects_wrong_chain_id() { + // Wallet returns chain 99999 but client requested 4217 → must reject + // and persist nothing, else discovery would later fail to find a key + // for the requested chain. + let _g = test_env_mutex().lock().await; + let tmp = tempfile::tempdir().unwrap(); + unsafe { std::env::set_var(TEMPO_HOME_ENV, tmp.path()) }; + + let (service_url, server) = spawn_mock_wallet(99999).await; + let err = ensure_access_key(test_cfg(service_url)).await.unwrap_err(); + assert!( + err.to_string().contains("wallet authorized chain 99999 but 4217 was requested"), + "expected chain mismatch error, got: {err}" + ); + assert!(read_tempo_keys_file().is_none_or(|f| f.keys.is_empty())); + + server.abort(); + unsafe { std::env::remove_var(TEMPO_HOME_ENV) }; + } +} diff --git a/crates/common/src/tempo/keystore.rs b/crates/common/src/tempo/keystore.rs index 18edf39be59bd..b4f9527d1b106 100644 --- a/crates/common/src/tempo/keystore.rs +++ b/crates/common/src/tempo/keystore.rs @@ -5,8 +5,8 @@ use alloy_primitives::{Address, hex}; use alloy_rlp::Decodable; -use serde::Deserialize; -use std::path::PathBuf; +use serde::{Deserialize, Serialize}; +use std::{env, fs, io::Write, path::PathBuf}; /// Environment variable for an ephemeral Tempo private key. pub const TEMPO_PRIVATE_KEY_ENV: &str = "TEMPO_PRIVATE_KEY"; @@ -21,7 +21,7 @@ pub const DEFAULT_TEMPO_HOME: &str = ".tempo"; pub const WALLET_KEYS_PATH: &str = "wallet/keys.toml"; /// Wallet type matching `tempo-common`'s `WalletType` enum. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum WalletType { #[default] @@ -30,7 +30,7 @@ pub enum WalletType { } /// Cryptographic key type. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum KeyType { #[default] @@ -40,7 +40,7 @@ pub enum KeyType { } /// Per-token spending limit stored in `keys.toml`. -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct StoredTokenLimit { pub currency: Address, pub limit: String, @@ -50,7 +50,7 @@ pub struct StoredTokenLimit { /// /// Mirrors the fields from `tempo-common::keys::model::KeyEntry`. /// Unknown fields are ignored by serde. -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct KeyEntry { /// Wallet type: "local" or "passkey". #[serde(default)] @@ -65,20 +65,20 @@ pub struct KeyEntry { #[serde(default)] pub key_type: KeyType, /// Key address (the EOA derived from the private key). - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub key_address: Option
, /// Key private key, stored inline in keys.toml. - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub key: Option, /// RLP-encoded signed key authorization (hex string). /// Used in keychain mode to atomically provision the access key on-chain. - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub key_authorization: Option, /// Expiry timestamp. - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub expiry: Option, /// Per-token spending limits. - #[serde(default)] + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub limits: Vec, } @@ -90,17 +90,27 @@ impl KeyEntry { } /// The top-level structure of `keys.toml`. -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct KeysFile { #[serde(default)] pub keys: Vec, } +/// Process-wide mutex used by tests that mutate `TEMPO_HOME`. +/// +/// Returns a [`tokio::sync::Mutex`] so async tests can hold it across `.await` +/// points without tripping `clippy::await_holding_lock`. +#[cfg(test)] +pub(crate) fn test_env_mutex() -> &'static tokio::sync::Mutex<()> { + static M: std::sync::OnceLock> = std::sync::OnceLock::new(); + M.get_or_init(|| tokio::sync::Mutex::new(())) +} + /// Resolve the Tempo home directory. /// /// Uses `TEMPO_HOME` env var if set, otherwise `~/.tempo`. pub fn tempo_home() -> Option { - if let Ok(home) = std::env::var(TEMPO_HOME_ENV) { + if let Ok(home) = env::var(TEMPO_HOME_ENV) { return Some(PathBuf::from(home)); } dirs::home_dir().map(|h| h.join(DEFAULT_TEMPO_HOME)) @@ -122,7 +132,7 @@ pub fn read_tempo_keys_file() -> Option { return None; } - let contents = match std::fs::read_to_string(&keys_path) { + let contents = match fs::read_to_string(&keys_path) { Ok(c) => c, Err(e) => { tracing::warn!(?keys_path, %e, "failed to read tempo keys file"); @@ -148,3 +158,112 @@ pub fn decode_key_authorization(hex_str: &str) -> eyre::Result let auth = T::decode(&mut bytes.as_slice())?; Ok(auth) } + +/// Atomically upsert a [`KeyEntry`] into `keys.toml`. +/// +/// Replaces any existing entry for the same `(wallet_address, chain_id)`. +/// Each Tempo wallet has at most one active access key per chain, so a fresh +/// login always supersedes the previous entry regardless of the new key +/// address. Creates the file (and parent directories) if missing. Writes via +/// temp file + rename so a crash mid-write cannot corrupt the file. +pub(crate) fn upsert_key_entry(entry: KeyEntry) -> eyre::Result<()> { + let path = tempo_keys_path().ok_or_else(|| eyre::eyre!("could not resolve tempo home"))?; + let dir = path.parent().ok_or_else(|| eyre::eyre!("invalid keys path: {}", path.display()))?; + fs::create_dir_all(dir)?; + + let mut file = read_tempo_keys_file().unwrap_or_default(); + file.keys + .retain(|k| !(k.wallet_address == entry.wallet_address && k.chain_id == entry.chain_id)); + file.keys.push(entry); + + let body = toml::to_string_pretty(&file)?; + let contents = format!( + "# Tempo wallet keys — managed by Foundry / Tempo CLI.\n# Do not edit manually.\n\n{body}" + ); + + let mut tmp = tempfile::NamedTempFile::new_in(dir)?; + tmp.write_all(contents.as_bytes())?; + tmp.flush()?; + tmp.persist(&path).map_err(|e| eyre::eyre!("failed to persist keys.toml: {e}"))?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + fn with_tempo_home(f: F) { + let tmp = tempfile::tempdir().unwrap(); + // SAFETY: process-global env access is serialized via the shared mutex. + let _g = test_env_mutex().blocking_lock(); + unsafe { std::env::set_var(TEMPO_HOME_ENV, tmp.path()) }; + f(); + unsafe { std::env::remove_var(TEMPO_HOME_ENV) }; + } + + #[test] + fn upsert_replaces_matching_entry_atomically() { + with_tempo_home(|| { + let wallet = Address::from_str("0x0000000000000000000000000000000000000001").unwrap(); + let key = Address::from_str("0x0000000000000000000000000000000000000abc").unwrap(); + + let mk = |expiry: u64| KeyEntry { + wallet_type: WalletType::Passkey, + wallet_address: wallet, + chain_id: 4217, + key_type: KeyType::Secp256k1, + key_address: Some(key), + key: Some("0xdead".to_string()), + key_authorization: Some("0xbeef".to_string()), + expiry: Some(expiry), + limits: vec![], + }; + + upsert_key_entry(mk(100)).unwrap(); + upsert_key_entry(mk(200)).unwrap(); + + let file = read_tempo_keys_file().unwrap(); + assert_eq!(file.keys.len(), 1); + assert_eq!(file.keys[0].expiry, Some(200)); + + // Different chain_id => separate entry. + let mut other = mk(300); + other.chain_id = 42431; + upsert_key_entry(other).unwrap(); + let file = read_tempo_keys_file().unwrap(); + assert_eq!(file.keys.len(), 2); + }); + } + + #[test] + fn upsert_replaces_when_key_address_changes() { + // Re-login produces a fresh random key address; the new entry must + // supersede the old one for the same (wallet, chain), not coexist. + with_tempo_home(|| { + let wallet = Address::from_str("0x0000000000000000000000000000000000000001").unwrap(); + let old_key = Address::from_str("0x000000000000000000000000000000000000aaaa").unwrap(); + let new_key = Address::from_str("0x000000000000000000000000000000000000bbbb").unwrap(); + + let mk = |key_addr: Address| KeyEntry { + wallet_type: WalletType::Passkey, + wallet_address: wallet, + chain_id: 4217, + key_type: KeyType::Secp256k1, + key_address: Some(key_addr), + key: Some("0xdead".to_string()), + key_authorization: Some("0xbeef".to_string()), + expiry: Some(100), + limits: vec![], + }; + + upsert_key_entry(mk(old_key)).unwrap(); + upsert_key_entry(mk(new_key)).unwrap(); + + let file = read_tempo_keys_file().unwrap(); + assert_eq!(file.keys.len(), 1, "old entry must be replaced, not duplicated"); + assert_eq!(file.keys[0].key_address, Some(new_key)); + }); + } +} diff --git a/crates/common/src/tempo/mod.rs b/crates/common/src/tempo/mod.rs index ec51dc607b5ab..ef8d0212bd453 100644 --- a/crates/common/src/tempo/mod.rs +++ b/crates/common/src/tempo/mod.rs @@ -1,8 +1,24 @@ //! Tempo network utilities. +pub mod auth; + +use crate::FoundryTransactionBuilder; +use alloy_network::Network; +use alloy_primitives::{Address, B256, Signature}; +use alloy_signer::Signer; +use eyre::{Context, Result}; +use foundry_wallets::{RawWalletOpts, WalletOpts, WalletSigner}; +use std::sync::Arc; + mod keystore; + +pub(crate) use auth::is_known_tempo_endpoint; +pub use auth::{AccessKeyOutcome, EnsureAccessKeyConfig, ensure_access_key}; pub use keystore::*; +#[cfg(test)] +pub(crate) use keystore::test_env_mutex; + #[cfg(test)] mod tests; @@ -16,3 +32,173 @@ mod tests; /// /// See pub const TEMPO_BROWSER_GAS_BUFFER: u64 = 7_000; + +/// Gas sponsor configuration for Tempo fee-payer signatures. +#[derive(Clone, Debug)] +pub struct TempoSponsor { + sponsor: Address, + signer: Option>, + signature: Option, +} + +impl TempoSponsor { + pub const fn new( + sponsor: Address, + signer: Option>, + signature: Option, + ) -> Self { + Self { sponsor, signer, signature } + } + + pub const fn sponsor(&self) -> Address { + self.sponsor + } + + pub async fn attach_and_print( + &self, + tx: &mut N::TransactionRequest, + sender: Address, + ) -> Result + where + N::TransactionRequest: FoundryTransactionBuilder, + { + if self.sponsor == sender { + eyre::bail!( + "invalid Tempo sponsorship: sponsor {} must not equal transaction sender", + self.sponsor + ); + } + + let digest = tx.compute_sponsor_hash(sender).ok_or_else(|| { + eyre::eyre!( + "failed to compute Tempo sponsor digest; make sure this is a complete Tempo AA transaction" + ) + })?; + + let preview = TempoSponsorPreview { + sponsor: self.sponsor, + fee_token: tx.fee_token(), + valid_before: tx.valid_before().map(|v| v.get()), + valid_after: tx.valid_after().map(|v| v.get()), + digest, + }; + preview.print()?; + + let signature = if let Some(signature) = self.signature { + signature + } else if let Some(signer) = &self.signer { + signer.sign_hash(&digest).await.context("failed to sign Tempo sponsor digest")? + } else { + eyre::bail!("missing Tempo sponsor signature or signer") + }; + + let recovered = signature + .recover_address_from_prehash(&digest) + .context("failed to recover Tempo sponsor signature")?; + if recovered != self.sponsor { + eyre::bail!("Tempo sponsor signature recovered {recovered}, expected {}", self.sponsor); + } + if recovered == sender { + eyre::bail!( + "invalid Tempo sponsorship: recovered fee payer {recovered} must not equal transaction sender" + ); + } + + tx.set_fee_payer_signature(signature); + Ok(preview) + } +} + +/// User-visible sponsor digest metadata for a single outgoing Tempo transaction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TempoSponsorPreview { + pub sponsor: Address, + pub fee_token: Option
, + pub valid_before: Option, + pub valid_after: Option, + pub digest: B256, +} + +impl TempoSponsorPreview { + pub fn print(&self) -> Result<()> { + crate::sh_eprintln!("Tempo sponsor: {}", self.sponsor)?; + crate::sh_eprintln!( + "Tempo fee token: {}", + self.fee_token.map_or_else(|| "network default".to_string(), |addr| addr.to_string()) + )?; + crate::sh_eprintln!( + "Tempo validity: after {}, before {}", + self.valid_after.map_or_else(|| "none".to_string(), |v| v.to_string()), + self.valid_before.map_or_else(|| "none".to_string(), |v| v.to_string()) + )?; + crate::sh_eprintln!("Tempo sponsor digest: {:?}", self.digest)?; + Ok(()) + } +} + +/// Resolves a `--tempo.sponsor-signer` URI into a Foundry wallet signer. +pub async fn resolve_tempo_sponsor_signer(spec: &str) -> Result { + let spec = spec.trim(); + let (scheme, value) = spec + .split_once("://") + .map(|(scheme, value)| (scheme.to_ascii_lowercase(), value)) + .unwrap_or_else(|| (spec.to_ascii_lowercase(), "")); + + match scheme.as_str() { + "env" => { + if value.is_empty() { + eyre::bail!("env:// sponsor signer requires an environment variable name"); + } + let private_key = std::env::var(value) + .wrap_err_with(|| format!("{value} environment variable is required"))?; + foundry_wallets::utils::create_private_key_signer(&private_key) + } + "private-key" => { + if value.is_empty() { + eyre::bail!("private-key:// sponsor signer requires a private key"); + } + foundry_wallets::utils::create_private_key_signer(value) + } + "keystore" => { + if value.is_empty() { + eyre::bail!("keystore:// sponsor signer requires a keystore path"); + } + WalletOpts { keystore_path: Some(value.to_string()), ..Default::default() } + .signer() + .await + } + "account" => { + if value.is_empty() { + eyre::bail!("account:// sponsor signer requires an account name"); + } + WalletOpts { keystore_account_name: Some(value.to_string()), ..Default::default() } + .signer() + .await + } + "ledger" => { + let raw = RawWalletOpts { + hd_path: (!value.is_empty()).then(|| value.to_string()), + ..Default::default() + }; + WalletOpts { ledger: true, raw, ..Default::default() }.signer().await + } + "trezor" => { + let raw = RawWalletOpts { + hd_path: (!value.is_empty()).then(|| value.to_string()), + ..Default::default() + }; + WalletOpts { trezor: true, raw, ..Default::default() }.signer().await + } + "aws" => WalletOpts { aws: true, ..Default::default() }.signer().await, + "gcp" => WalletOpts { gcp: true, ..Default::default() }.signer().await, + "turnkey" => WalletOpts { turnkey: true, ..Default::default() }.signer().await, + "browser" => { + eyre::bail!( + "browser:// sponsor signing is not supported by the current browser wallet API; use --tempo.sponsor-sig or another sponsor signer" + ) + } + _ => eyre::bail!( + "unsupported Tempo sponsor signer `{spec}`; expected env://VAR, keystore://PATH, account://NAME, ledger://, trezor://, aws://, gcp://, turnkey://, or private-key://KEY" + ), + } +} diff --git a/crates/common/src/transactions/builder.rs b/crates/common/src/transactions/builder.rs index de03cf3adc73e..aa4c971680d00 100644 --- a/crates/common/src/transactions/builder.rs +++ b/crates/common/src/transactions/builder.rs @@ -9,7 +9,9 @@ use alloy_primitives::{Address, B256, Signature, TxKind, U256}; use alloy_provider::Provider; use alloy_signer::Signer; use eyre::Result; +#[cfg(feature = "optimism")] use op_alloy_network::Optimism; +#[cfg(feature = "optimism")] use op_alloy_rpc_types::OpTransactionRequest; use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; use tempo_primitives::{ @@ -244,6 +246,24 @@ pub trait FoundryTransactionBuilder: NetworkTransactionBuilder { /// on-chain as part of this transaction. fn set_key_authorization(&mut self, _key_authorization: SignedKeyAuthorization) {} + /// Embeds key authorization before gas estimation/signing if the access key is not yet + /// provisioned on-chain. + /// + /// This mirrors the mutation performed by [`Self::sign_with_access_key`], but makes the final + /// transaction body available before fee-payer sponsor digests are computed. + fn prepare_access_key_authorization<'a>( + &'a mut self, + _provider: &'a impl Provider, + _wallet_address: Address, + _key_address: Address, + _key_authorization: Option<&'a SignedKeyAuthorization>, + ) -> impl Future> + Send + 'a + where + Self: Send, + { + async { Ok(()) } + } + /// Converts a CREATE transaction into an AA-compatible call entry. /// /// Tempo AA transactions use a `calls` list instead of `to`+`input`. Must be @@ -355,6 +375,7 @@ impl FoundryTransactionBuilder for ::Transact } } +#[cfg(feature = "optimism")] impl FoundryTransactionBuilder for OpTransactionRequest { fn reset_gas_limit(&mut self) { self.as_mut().gas = None; @@ -439,6 +460,35 @@ impl FoundryTransactionBuilder for ::Tran self.key_authorization = Some(key_authorization); } + fn prepare_access_key_authorization<'a>( + &'a mut self, + provider: &'a impl Provider, + wallet_address: Address, + key_address: Address, + key_authorization: Option<&'a SignedKeyAuthorization>, + ) -> impl Future> + Send + 'a + where + Self: Send, + { + let auth = key_authorization.cloned(); + + async move { + if let Some(auth) = auth { + let is_provisioned = provider + .get_keychain_key(wallet_address, key_address) + .await + .map(|info| info.keyId != Address::ZERO) + .unwrap_or(false); + + if !is_provisioned { + self.set_key_authorization(auth); + } + } + + Ok(()) + } + } + fn convert_create_to_call(&mut self) { if self.calls.is_empty() && self.inner.to.is_some_and(|to| to.is_create()) { let input = self.inner.input.input().cloned().unwrap_or_default(); @@ -473,7 +523,12 @@ impl FoundryTransactionBuilder for ::Tran let is_provisioned = provisioning_fut.await.map(|info| info.keyId != Address::ZERO).unwrap_or(false); - if !is_provisioned { + if !is_provisioned && self.key_authorization.is_none() { + if self.fee_payer_signature.is_some() { + eyre::bail!( + "cannot add Tempo key authorization after fee payer signature was attached" + ); + } self.set_key_authorization(auth); } } diff --git a/crates/common/src/transactions/receipt.rs b/crates/common/src/transactions/receipt.rs index 9ca6cb02b10ee..c2e34419248c4 100644 --- a/crates/common/src/transactions/receipt.rs +++ b/crates/common/src/transactions/receipt.rs @@ -7,6 +7,7 @@ use alloy_provider::{ use alloy_rpc_types::{BlockId, TransactionReceipt}; use eyre::Result; use foundry_common_fmt::{UIfmt, UIfmtReceiptExt, get_pretty_receipt_attr}; +#[cfg(feature = "optimism")] use op_alloy_rpc_types::OpTransactionReceipt; use serde::{Deserialize, Serialize}; use tempo_alloy::rpc::TempoTransactionReceipt; @@ -23,6 +24,7 @@ impl FoundryReceiptResponse for TransactionReceipt { } } +#[cfg(feature = "optimism")] impl FoundryReceiptResponse for OpTransactionReceipt { fn set_contract_address(&mut self, contract_address: Address) { self.inner.contract_address = Some(contract_address); diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index bab59137b0130..8f63718e086cd 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -10,6 +10,10 @@ use std::path::PathBuf; pub struct FuzzConfig { /// The number of test cases that must execute for each property test pub runs: u32, + /// Optional 1-based fuzz run to execute. + pub run: Option, + /// Optional fuzz worker ID to pair with `run`. + pub worker: Option, /// Fails the fuzzed test if a revert occurs. pub fail_on_revert: bool, /// The maximum number of test case rejections allowed, @@ -37,6 +41,8 @@ impl Default for FuzzConfig { fn default() -> Self { Self { runs: 256, + run: None, + worker: None, fail_on_revert: true, max_test_rejects: 65536, seed: None, diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 270df14a6c291..000cefc26737a 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use crate::Config; use alloy_primitives::map::HashMap; use figment::{ @@ -5,6 +7,7 @@ use figment::{ value::{Dict, Map, Value}, }; use foundry_compilers::ProjectCompileOutput; +use foundry_evm_networks::NetworkVariant; use itertools::Itertools; mod natspec; @@ -123,6 +126,42 @@ impl InlineConfig { self.get_function(contract, function).is_some_and(|map| !map.is_empty()) } + /// Returns the configured [`NetworkVariant`] for a given test, checking function-level first + /// then contract-level. Returns `None` if no network annotation is present. + pub fn network_for( + &self, + profile: &Profile, + contract: &str, + function: &str, + ) -> Option { + let data = self.provide(contract, function).data().ok()?; + let dict = data.get(profile).or_else(|| data.get(&Profile::Default))?; + if let Some(Value::Dict(_, networks)) = dict.get("networks") + && let Some(Value::String(_, s)) = networks.get("network") + { + return s.parse().ok(); + } + None + } + + /// Returns all distinct [`NetworkVariant`]s referenced in any inline config annotation. + /// + /// This is used to determine whether a multi-network test pass is needed. + pub fn referenced_override_networks(&self, profile: &Profile) -> Vec { + let mut seen = BTreeSet::new(); + for (contract, function) in self.fn_level.keys() { + if let Some(v) = self.network_for(profile, contract, function) { + seen.insert(v); + } + } + for contract in self.contract_level.keys() { + if let Some(v) = self.network_for(profile, contract, "") { + seen.insert(v); + } + } + seen.into_iter().collect() + } + fn get_contract(&self, contract: &str) -> Option<&DataMap> { self.contract_level.get(contract) } diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 3c8cad85bae10..cc3dabd32d4bf 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -29,3 +29,11 @@ ratatui = { version = "0.30", default-features = false, features = [ revm.workspace = true tracing.workspace = true serde.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm-core/optimism", + "foundry-evm-traces/optimism", +] diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 809e15b077c37..814beab402729 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -32,3 +32,7 @@ thiserror.workspace = true toml.workspace = true tracing.workspace = true regex.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index b8fd3760cd850..888f6269623a5 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -72,8 +72,8 @@ impl AsDoc for CommentsRef<'_> { writer.writeln_raw(format!( "{}{}: {}", if customs.len() == 1 { "" } else { "- " }, - &c.tag, - &c.value + c.tag, + c.value ))?; writer.writeln()?; } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 03d569c17f500..801e813026a39 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -36,7 +36,7 @@ alloy-primitives = { workspace = true, features = [ alloy-provider.workspace = true alloy-network.workspace = true alloy-consensus.workspace = true -alloy-op-evm.workspace = true +alloy-op-evm = { workspace = true, optional = true } alloy-rpc-types = { workspace = true, features = ["anvil"] } alloy-sol-types.workspace = true alloy-rlp.workspace = true @@ -54,9 +54,10 @@ revm = { workspace = true, features = [ "blst", ] } revm-inspectors.workspace = true -op-alloy-consensus = { workspace = true, features = ["k256"] } -op-alloy-network.workspace = true -op-revm.workspace = true +op-alloy-consensus = { workspace = true, features = ["k256"], optional = true } +op-alloy-network = { workspace = true, optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } +op-revm = { workspace = true, optional = true } tempo-revm.workspace = true tempo-alloy.workspace = true tempo-contracts.workspace = true @@ -77,7 +78,18 @@ url.workspace = true [dev-dependencies] alloy-serde.workspace = true -op-alloy-consensus.workspace = true -op-alloy-rpc-types.workspace = true anvil.workspace = true foundry-test-utils.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "dep:op-alloy-consensus", + "dep:op-alloy-network", + "dep:op-alloy-rpc-types", + "dep:alloy-op-evm", + "dep:op-revm", + "foundry-common/optimism", + "foundry-evm-hardforks/optimism", + "foundry-evm-networks/optimism", +] diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 0cfd56a44219c..b836023a968b7 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -223,8 +223,8 @@ fn trimmed_hex(s: &[u8]) -> String { } else { format!( "{}…{} ({} bytes)", - &hex::encode(&s[..n / 2]), - &hex::encode(&s[s.len() - n / 2..]), + hex::encode(&s[..n / 2]), + hex::encode(&s[s.len() - n / 2..]), s.len(), ) } diff --git a/crates/evm/core/src/env.rs b/crates/evm/core/src/env.rs index 132b986f55e7f..bfc45b6b5d773 100644 --- a/crates/evm/core/src/env.rs +++ b/crates/evm/core/src/env.rs @@ -4,13 +4,9 @@ use alloy_consensus::Typed2718; pub use alloy_evm::EvmEnv; use alloy_evm::FromRecoveredTx; use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse}; -use alloy_op_evm::OpTx; use alloy_primitives::{Address, B256, Bytes, U256}; -use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit}; -use op_revm::{ - OpTransaction, - transaction::{OpTxTr, deposit::DEPOSIT_TRANSACTION_TYPE}, -}; +#[cfg(feature = "optimism")] +use op_revm::transaction::deposit::DEPOSIT_TRANSACTION_TYPE; use revm::{ Context, Database, Journal, context::{Block, BlockEnv, Cfg, CfgEnv, Transaction, TxEnv}, @@ -236,9 +232,16 @@ pub trait FoundryTransaction: Transaction { /// Sets whether the transaction is a system transaction fn set_system_transaction(&mut self, _is_system_transaction: bool) {} - /// Returns `true` if transaction is of type [`DEPOSIT_TRANSACTION_TYPE`]. + /// Returns `true` if transaction is an Optimism deposit transaction. fn is_deposit(&self) -> bool { - self.tx_type() == DEPOSIT_TRANSACTION_TYPE + #[cfg(feature = "optimism")] + { + self.tx_type() == DEPOSIT_TRANSACTION_TYPE + } + #[cfg(not(feature = "optimism"))] + { + false + } } // Tempo methods @@ -320,188 +323,6 @@ impl FoundryTransaction for TxEnv { } } -impl FoundryTransaction for OpTransaction { - fn set_tx_type(&mut self, tx_type: u8) { - self.base.set_tx_type(tx_type); - } - - fn set_caller(&mut self, caller: Address) { - self.base.set_caller(caller); - } - - fn set_gas_limit(&mut self, gas_limit: u64) { - self.base.set_gas_limit(gas_limit); - } - - fn set_gas_price(&mut self, gas_price: u128) { - self.base.set_gas_price(gas_price); - } - - fn set_kind(&mut self, kind: TxKind) { - self.base.set_kind(kind); - } - - fn set_value(&mut self, value: U256) { - self.base.set_value(value); - } - - fn set_data(&mut self, data: Bytes) { - self.base.set_data(data); - } - - fn set_nonce(&mut self, nonce: u64) { - self.base.set_nonce(nonce); - } - - fn set_chain_id(&mut self, chain_id: Option) { - self.base.set_chain_id(chain_id); - } - - fn set_access_list(&mut self, access_list: AccessList) { - self.base.set_access_list(access_list); - } - - fn authorization_list_mut( - &mut self, - ) -> &mut Vec> { - self.base.authorization_list_mut() - } - - fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { - self.base.set_gas_priority_fee(gas_priority_fee); - } - - fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} - - fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} - - fn enveloped_tx(&self) -> Option<&Bytes> { - OpTxTr::enveloped_tx(self) - } - - fn set_enveloped_tx(&mut self, bytes: Bytes) { - self.enveloped_tx = Some(bytes); - } - - fn source_hash(&self) -> Option { - OpTxTr::source_hash(self) - } - - fn set_source_hash(&mut self, source_hash: B256) { - if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { - self.deposit.source_hash = source_hash; - } - } - - fn mint(&self) -> Option { - OpTxTr::mint(self) - } - - fn set_mint(&mut self, mint: u128) { - if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { - self.deposit.mint = Some(mint); - } - } - - fn is_system_transaction(&self) -> bool { - OpTxTr::is_system_transaction(self) - } - - fn set_system_transaction(&mut self, is_system_transaction: bool) { - if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { - self.deposit.is_system_transaction = is_system_transaction; - } - } -} - -impl FoundryTransaction for OpTx { - fn set_tx_type(&mut self, tx_type: u8) { - self.0.set_tx_type(tx_type); - } - - fn set_caller(&mut self, caller: Address) { - self.0.set_caller(caller); - } - - fn set_gas_limit(&mut self, gas_limit: u64) { - self.0.set_gas_limit(gas_limit); - } - - fn set_gas_price(&mut self, gas_price: u128) { - self.0.set_gas_price(gas_price); - } - - fn set_kind(&mut self, kind: TxKind) { - self.0.set_kind(kind); - } - - fn set_value(&mut self, value: U256) { - self.0.set_value(value); - } - - fn set_data(&mut self, data: Bytes) { - self.0.set_data(data); - } - - fn set_nonce(&mut self, nonce: u64) { - self.0.set_nonce(nonce); - } - - fn set_chain_id(&mut self, chain_id: Option) { - self.0.set_chain_id(chain_id); - } - - fn set_access_list(&mut self, access_list: AccessList) { - self.0.set_access_list(access_list); - } - - fn authorization_list_mut( - &mut self, - ) -> &mut Vec> { - self.0.authorization_list_mut() - } - - fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { - self.0.set_gas_priority_fee(gas_priority_fee); - } - - fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} - - fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} - - fn enveloped_tx(&self) -> Option<&Bytes> { - FoundryTransaction::enveloped_tx(&self.0) - } - - fn set_enveloped_tx(&mut self, bytes: Bytes) { - self.0.set_enveloped_tx(bytes); - } - - fn source_hash(&self) -> Option { - FoundryTransaction::source_hash(&self.0) - } - - fn set_source_hash(&mut self, source_hash: B256) { - self.0.set_source_hash(source_hash); - } - - fn mint(&self) -> Option { - FoundryTransaction::mint(&self.0) - } - - fn set_mint(&mut self, mint: u128) { - self.0.set_mint(mint); - } - - fn is_system_transaction(&self) -> bool { - FoundryTransaction::is_system_transaction(&self.0) - } - - fn set_system_transaction(&mut self, is_system_transaction: bool) { - self.0.set_system_transaction(is_system_transaction); - } -} - impl FoundryTransaction for TempoTxEnv { fn set_tx_type(&mut self, tx_type: u8) { self.inner.set_tx_type(tx_type); @@ -687,32 +508,6 @@ impl FromAnyRpcTransaction for TxEnv { } } -impl FromAnyRpcTransaction for OpTx { - fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { - if let Some(envelope) = tx.as_envelope() { - return Ok(Self(OpTransaction:: { - base: TxEnv::from_recovered_tx(envelope, tx.from()), - enveloped_tx: None, - deposit: Default::default(), - })); - } - - // Handle OP deposit transactions from `Unknown` envelope variant. - if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner - && unknown.ty() == DEPOSIT_TX_TYPE_ID - { - let mut fields = unknown.inner.fields.clone(); - fields.insert("from".to_string(), serde_json::to_value(tx.from())?); - let deposit_tx: TxDeposit = fields - .deserialize_into() - .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?; - return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from)); - } - - eyre::bail!("cannot convert unknown transaction type to OpTransaction") - } -} - impl FromAnyRpcTransaction for TempoTxEnv { fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { use alloy_consensus::Transaction as _; @@ -747,6 +542,222 @@ impl FromAnyRpcTransaction for TempoTxEnv { } } +#[cfg(feature = "optimism")] +mod optimism { + use super::*; + use alloy_op_evm::OpTx; + use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit}; + use op_revm::{OpTransaction, transaction::OpTxTr}; + + impl FoundryTransaction for OpTransaction { + fn set_tx_type(&mut self, tx_type: u8) { + self.base.set_tx_type(tx_type); + } + + fn set_caller(&mut self, caller: Address) { + self.base.set_caller(caller); + } + + fn set_gas_limit(&mut self, gas_limit: u64) { + self.base.set_gas_limit(gas_limit); + } + + fn set_gas_price(&mut self, gas_price: u128) { + self.base.set_gas_price(gas_price); + } + + fn set_kind(&mut self, kind: TxKind) { + self.base.set_kind(kind); + } + + fn set_value(&mut self, value: U256) { + self.base.set_value(value); + } + + fn set_data(&mut self, data: Bytes) { + self.base.set_data(data); + } + + fn set_nonce(&mut self, nonce: u64) { + self.base.set_nonce(nonce); + } + + fn set_chain_id(&mut self, chain_id: Option) { + self.base.set_chain_id(chain_id); + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.base.set_access_list(access_list); + } + + fn authorization_list_mut( + &mut self, + ) -> &mut Vec> { + self.base.authorization_list_mut() + } + + fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { + self.base.set_gas_priority_fee(gas_priority_fee); + } + + fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} + + fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} + + fn enveloped_tx(&self) -> Option<&Bytes> { + OpTxTr::enveloped_tx(self) + } + + fn set_enveloped_tx(&mut self, bytes: Bytes) { + self.enveloped_tx = Some(bytes); + } + + fn source_hash(&self) -> Option { + OpTxTr::source_hash(self) + } + + fn set_source_hash(&mut self, source_hash: B256) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.source_hash = source_hash; + } + } + + fn mint(&self) -> Option { + OpTxTr::mint(self) + } + + fn set_mint(&mut self, mint: u128) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.mint = Some(mint); + } + } + + fn is_system_transaction(&self) -> bool { + OpTxTr::is_system_transaction(self) + } + + fn set_system_transaction(&mut self, is_system_transaction: bool) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.is_system_transaction = is_system_transaction; + } + } + } + + impl FoundryTransaction for OpTx { + fn set_tx_type(&mut self, tx_type: u8) { + self.0.set_tx_type(tx_type); + } + + fn set_caller(&mut self, caller: Address) { + self.0.set_caller(caller); + } + + fn set_gas_limit(&mut self, gas_limit: u64) { + self.0.set_gas_limit(gas_limit); + } + + fn set_gas_price(&mut self, gas_price: u128) { + self.0.set_gas_price(gas_price); + } + + fn set_kind(&mut self, kind: TxKind) { + self.0.set_kind(kind); + } + + fn set_value(&mut self, value: U256) { + self.0.set_value(value); + } + + fn set_data(&mut self, data: Bytes) { + self.0.set_data(data); + } + + fn set_nonce(&mut self, nonce: u64) { + self.0.set_nonce(nonce); + } + + fn set_chain_id(&mut self, chain_id: Option) { + self.0.set_chain_id(chain_id); + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.0.set_access_list(access_list); + } + + fn authorization_list_mut( + &mut self, + ) -> &mut Vec> { + self.0.authorization_list_mut() + } + + fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { + self.0.set_gas_priority_fee(gas_priority_fee); + } + + fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} + + fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} + + fn enveloped_tx(&self) -> Option<&Bytes> { + FoundryTransaction::enveloped_tx(&self.0) + } + + fn set_enveloped_tx(&mut self, bytes: Bytes) { + self.0.set_enveloped_tx(bytes); + } + + fn source_hash(&self) -> Option { + FoundryTransaction::source_hash(&self.0) + } + + fn set_source_hash(&mut self, source_hash: B256) { + self.0.set_source_hash(source_hash); + } + + fn mint(&self) -> Option { + FoundryTransaction::mint(&self.0) + } + + fn set_mint(&mut self, mint: u128) { + self.0.set_mint(mint); + } + + fn is_system_transaction(&self) -> bool { + FoundryTransaction::is_system_transaction(&self.0) + } + + fn set_system_transaction(&mut self, is_system_transaction: bool) { + self.0.set_system_transaction(is_system_transaction); + } + } + + impl FromAnyRpcTransaction for OpTx { + fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { + if let Some(envelope) = tx.as_envelope() { + return Ok(Self(OpTransaction:: { + base: TxEnv::from_recovered_tx(envelope, tx.from()), + enveloped_tx: None, + deposit: Default::default(), + })); + } + + // Handle OP deposit transactions from `Unknown` envelope variant. + if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner + && unknown.ty() == DEPOSIT_TX_TYPE_ID + { + let mut fields = unknown.inner.fields.clone(); + fields.insert("from".to_string(), serde_json::to_value(tx.from())?); + let deposit_tx: TxDeposit = fields + .deserialize_into() + .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?; + return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from)); + } + + eyre::bail!("cannot convert unknown transaction type to OpTransaction") + } + } +} + #[cfg(test)] mod tests { use std::num::NonZeroU64; @@ -755,14 +766,10 @@ mod tests { use alloy_consensus::{Sealed, Signed, TxEip1559, transaction::Recovered}; use alloy_evm::{EthEvmFactory, EvmFactory}; use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction}; - use alloy_op_evm::OpEvmFactory; use alloy_primitives::Signature; use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo}; use alloy_serde::WithOtherFields; use foundry_evm_hardforks::TempoHardfork; - use op_alloy_consensus::{OpTxEnvelope, transaction::OpTransactionInfo}; - use op_alloy_rpc_types::Transaction as OpRpcTransaction; - use op_revm::OpSpecId; use revm::database::EmptyDB; use tempo_alloy::primitives::{ AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope, @@ -793,30 +800,6 @@ mod tests { evm.ctx_mut().set_evm(evm_env); } - #[test] - fn op_evm_foundry_context_ext_implementation() { - let mut evm = - OpEvmFactory::::default().create_evm(EmptyDB::default(), EvmEnv::default()); - - // Test EVM Context Block mutation - evm.ctx_mut().block_mut().set_number(U256::from(123)); - assert_eq!(evm.ctx().block().number(), U256::from(123)); - - // Test EVM Context Tx mutation - evm.ctx_mut().tx_mut().set_nonce(99); - assert_eq!(evm.ctx().tx().nonce(), 99); - - // Test EVM Context Cfg mutation - evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN; - assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN); - - // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env - let tx_env = evm.ctx().tx_clone(); - evm.ctx_mut().set_tx(tx_env); - let evm_env = evm.ctx().evm_clone(); - evm.ctx_mut().set_evm(evm_env); - } - #[test] fn tempo_evm_foundry_context_ext_implementation() { let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default()); @@ -874,23 +857,6 @@ mod tests { assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB))); } - #[test] - fn from_any_rpc_transaction_for_op() { - let from = Address::random(); - let signed_tx = make_signed_eip1559(); - - // Build the eth TxEnv to compare against op base - let rpc_tx = RpcTransaction::from_transaction( - Recovered::new_unchecked(signed_tx.into(), from), - TransactionInfo::default(), - ); - let any_tx = >::from(rpc_tx); - let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap(); - - let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); - assert_eq!(op_tx_env.base, expected_base); - } - #[test] fn from_any_rpc_transaction_unknown_envelope_errors() { let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope { @@ -915,39 +881,6 @@ mod tests { assert!(result.to_string().contains("unknown transaction type")); } - #[test] - fn from_any_rpc_transaction_for_op_deposit() { - let from = Address::random(); - let source_hash = B256::random(); - let deposit = TxDeposit { - source_hash, - from, - to: TxKind::Call(Address::with_last_byte(0xCC)), - mint: 1111, - value: U256::from(200), - gas_limit: 21000, - is_system_transaction: true, - input: Default::default(), - }; - - // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as AnyRpcTransaction. - let op_rpc_tx = OpRpcTransaction::from_transaction( - Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from), - OpTransactionInfo::default(), - ); - let json = serde_json::to_value(&op_rpc_tx).unwrap(); - let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap(); - - let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); - assert_eq!(op_tx_env.base.caller, from); - assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC))); - assert_eq!(op_tx_env.base.value, U256::from(200)); - assert_eq!(op_tx_env.base.gas_limit, 21000); - assert_eq!(op_tx_env.deposit.source_hash, source_hash); - assert_eq!(op_tx_env.deposit.mint, Some(1111)); - assert!(op_tx_env.deposit.is_system_transaction); - } - #[test] fn from_any_rpc_transaction_for_tempo_eth_envelope() { let from = Address::random(); @@ -1004,4 +937,88 @@ mod tests { assert_eq!(tx_env.inner.chain_id, Some(42431)); assert_eq!(tx_env.fee_token, fee_token); } + + #[cfg(feature = "optimism")] + mod optimism { + use super::*; + use alloy_op_evm::{OpEvmFactory, OpTx}; + use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo}; + use op_alloy_rpc_types::Transaction as OpRpcTransaction; + use op_revm::OpSpecId; + + #[test] + fn op_evm_foundry_context_ext_implementation() { + let mut evm = + OpEvmFactory::::default().create_evm(EmptyDB::default(), EvmEnv::default()); + + // Test EVM Context Block mutation + evm.ctx_mut().block_mut().set_number(U256::from(123)); + assert_eq!(evm.ctx().block().number(), U256::from(123)); + + // Test EVM Context Tx mutation + evm.ctx_mut().tx_mut().set_nonce(99); + assert_eq!(evm.ctx().tx().nonce(), 99); + + // Test EVM Context Cfg mutation + evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN; + assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN); + + // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env + let tx_env = evm.ctx().tx_clone(); + evm.ctx_mut().set_tx(tx_env); + let evm_env = evm.ctx().evm_clone(); + evm.ctx_mut().set_evm(evm_env); + } + + #[test] + fn from_any_rpc_transaction_for_op() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + + // Build the eth TxEnv to compare against op base + let rpc_tx = RpcTransaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + TransactionInfo::default(), + ); + let any_tx = >::from(rpc_tx); + let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap(); + + let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(op_tx_env.base, expected_base); + } + + #[test] + fn from_any_rpc_transaction_for_op_deposit() { + let from = Address::random(); + let source_hash = B256::random(); + let deposit = TxDeposit { + source_hash, + from, + to: TxKind::Call(Address::with_last_byte(0xCC)), + mint: 1111, + value: U256::from(200), + gas_limit: 21000, + is_system_transaction: true, + input: Default::default(), + }; + + // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as + // AnyRpcTransaction. + let op_rpc_tx = OpRpcTransaction::from_transaction( + Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from), + OpTransactionInfo::default(), + ); + let json = serde_json::to_value(&op_rpc_tx).unwrap(); + let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap(); + + let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(op_tx_env.base.caller, from); + assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC))); + assert_eq!(op_tx_env.base.value, U256::from(200)); + assert_eq!(op_tx_env.base.gas_limit, 21000); + assert_eq!(op_tx_env.deposit.source_hash, source_hash); + assert_eq!(op_tx_env.deposit.mint, Some(1111)); + assert!(op_tx_env.deposit.is_system_transaction); + } + } } diff --git a/crates/evm/core/src/evm/mod.rs b/crates/evm/core/src/evm/mod.rs index 708226be003a2..fc9e9e7d2810f 100644 --- a/crates/evm/core/src/evm/mod.rs +++ b/crates/evm/core/src/evm/mod.rs @@ -10,14 +10,11 @@ use alloy_evm::{ EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, precompiles::PrecompilesMap, }; use alloy_network::{Ethereum, Network}; -use alloy_op_evm::OpEvmFactory; use alloy_primitives::{Address, Signature, U256}; use alloy_rlp::Decodable; use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt}; use foundry_config::FromEvmVersion; use foundry_fork_db::{DatabaseError, ForkBlockEnv}; -use op_alloy_network::Optimism; -use op_revm::OpHaltReason; use revm::{ Database, context::{ @@ -36,10 +33,12 @@ use tempo_evm::evm::TempoEvmFactory; use tempo_revm::TempoHaltReason; pub mod eth; +#[cfg(feature = "optimism")] pub mod op; pub mod tempo; pub use eth::*; +#[cfg(feature = "optimism")] pub use op::*; pub use tempo::*; @@ -75,13 +74,6 @@ impl FoundryEvmNetwork for TempoEvmNetwork { type EvmFactory = TempoEvmFactory; } -#[derive(Clone, Copy, Debug, Default)] -pub struct OpEvmNetwork; -impl FoundryEvmNetwork for OpEvmNetwork { - type Network = Optimism; - type EvmFactory = OpEvmFactory; -} - /// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`]. pub type EvmFactoryFor = ::EvmFactory; pub type FoundryContextFor<'db, FEN> = @@ -249,15 +241,6 @@ impl IntoInstructionResult for HaltReason { } } -impl IntoInstructionResult for OpHaltReason { - fn into_instruction_result(self) -> InstructionResult { - match self { - Self::Base(eth) => eth.into(), - Self::FailedDeposit => InstructionResult::Stop, - } - } -} - impl IntoInstructionResult for TempoHaltReason { fn into_instruction_result(self) -> InstructionResult { match self { diff --git a/crates/evm/core/src/evm/op.rs b/crates/evm/core/src/evm/op.rs index cb8bf272d9a05..efb74ad3abf50 100644 --- a/crates/evm/core/src/evm/op.rs +++ b/crates/evm/core/src/evm/op.rs @@ -1,6 +1,7 @@ use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap}; use alloy_op_evm::{OpEvm, OpEvmContext, OpEvmFactory, OpTx}; use foundry_fork_db::DatabaseError; +use op_alloy_network::Optimism; use op_revm::{OpEvm as RevmEvm, OpHaltReason, OpSpecId, OpTransactionError, handler::OpHandler}; use revm::{ context::{ @@ -10,16 +11,33 @@ use revm::{ handler::{EthFrame, EvmTr, FrameResult, Handler, instructions::EthInstructions}, inspector::InspectorHandler, interpreter::{ - FrameInput, SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit, + FrameInput, InstructionResult, SharedMemory, interpreter::EthInterpreter, + interpreter_action::FrameInit, }, }; use crate::{ FoundryContextExt, FoundryInspectorExt, backend::{DatabaseExt, JournaledState}, - evm::{FoundryEvmFactory, NestedEvm}, + evm::{FoundryEvmFactory, FoundryEvmNetwork, IntoInstructionResult, NestedEvm}, }; +#[derive(Clone, Copy, Debug, Default)] +pub struct OpEvmNetwork; +impl FoundryEvmNetwork for OpEvmNetwork { + type Network = Optimism; + type EvmFactory = OpEvmFactory; +} + +impl IntoInstructionResult for OpHaltReason { + fn into_instruction_result(self) -> InstructionResult { + match self { + Self::Base(eth) => eth.into(), + Self::FailedDeposit => InstructionResult::Stop, + } + } +} + type OpEvmHandler<'db, I> = OpHandler, EVMError, EthFrame>; diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index aefa0e2ee9741..2284823047ca6 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -212,13 +212,18 @@ pub struct ForkDbStateSnapshot { } impl ForkDbStateSnapshot { - fn get_storage(&self, address: Address, index: U256) -> Option { - self.local - .cache - .accounts - .get(&address) - .and_then(|account| account.storage.get(&index)) - .copied() + /// Lookup storage in `state_snapshot`, then fall back to the backend (remote RPC). + fn storage_from_snapshot_or_backend( + &self, + address: Address, + index: U256, + ) -> Result { + // Check state_snapshot.storage first (data fetched by SharedBackend / disk cache). + if let Some(val) = self.state_snapshot.storage.get(&address).and_then(|s| s.get(&index)) { + return Ok(*val); + } + // Fall back to the underlying backend (SharedBackend → remote RPC). + DatabaseRef::storage_ref(&self.local, address, index) } } @@ -250,15 +255,9 @@ impl DatabaseRef for ForkDbStateSnapshot { match self.local.cache.accounts.get(&address) { Some(account) => match account.storage.get(&index) { Some(entry) => Ok(*entry), - None => match self.get_storage(address, index) { - None => DatabaseRef::storage_ref(&self.local, address, index), - Some(storage) => Ok(storage), - }, - }, - None => match self.get_storage(address, index) { - None => DatabaseRef::storage_ref(&self.local, address, index), - Some(storage) => Ok(storage), + None => self.storage_from_snapshot_or_backend(address, index), }, + None => self.storage_from_snapshot_or_backend(address, index), } } @@ -303,4 +302,28 @@ mod tests { assert!(loaded.is_some()); assert_eq!(loaded.unwrap(), info); } + + /// Verifies that `ForkDbStateSnapshot::storage_ref` reads from `state_snapshot.storage` + /// when the slot is missing from `local.cache.accounts`. Without this lookup the call + /// would fall through to the backend and return the unrelated remote value. + #[tokio::test(flavor = "multi_thread")] + async fn fork_db_state_snapshot_reads_storage_from_snapshot() { + let rpc = foundry_test_utils::rpc::next_http_rpc_endpoint(); + let provider = get_http_provider(rpc.clone()); + let meta = BlockchainDbMeta::new(BlockEnv::default(), rpc); + let db = BlockchainDb::new(meta, None); + let backend = SharedBackend::spawn_backend(Arc::new(provider), db, None).await; + + let address = Address::random(); + let slot = U256::from(42u64); + let expected = U256::from(0xdeadbeefu64); + + let mut state_snapshot = StateSnapshot::default(); + state_snapshot.storage.entry(address).or_default().insert(slot, expected); + + let snapshot = ForkDbStateSnapshot { local: CacheDB::new(backend), state_snapshot }; + + let got = DatabaseRef::storage_ref(&snapshot, address, slot).unwrap(); + assert_eq!(got, expected); + } } diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 1b2201a9b8b84..c2edbb9dfd33b 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -5,6 +5,9 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "optimism")] +use op_alloy_rpc_types as _; + use crate::constants::DEFAULT_CREATE2_DEPLOYER; use alloy_primitives::{Address, map::HashMap}; use auto_impl::auto_impl; diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 82efd4a1aaaaa..ab68eb08821e8 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -137,8 +137,12 @@ impl EvmOpts { /// [`NetworkConfigs::with_chain_id`] to auto-enable the correct network /// (e.g. Tempo, OP Stack) based on the chain ID. pub async fn infer_network_from_fork(&mut self) { + #[cfg(feature = "optimism")] + let already_op = self.networks.is_optimism(); + #[cfg(not(feature = "optimism"))] + let already_op = false; if !self.networks.is_tempo() - && !self.networks.is_optimism() + && !already_op && let Some(ref fork_url) = self.fork_url && let Ok(provider) = self.fork_provider_with_url::(fork_url) && let Ok(chain_id) = provider.get_chain_id().await @@ -474,6 +478,7 @@ mod tests { // Plain anvil (chain id 31337) without tempo flag -> Ethereum (no network flags set). assert!(!evm_opts.networks.is_tempo()); + #[cfg(feature = "optimism")] assert!(!evm_opts.networks.is_optimism()); assert!(!evm_opts.networks.is_celo()); assert_eq!(evm_opts.networks, NetworkConfigs::default()); diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index d2d7b077ee9f0..758604564726e 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -25,3 +25,7 @@ semver.workspace = true tracing.workspace = true rayon.workspace = true solar.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism", "foundry-evm-core/optimism"] diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 70bce50a89882..5dbf07c7a356c 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -61,3 +61,16 @@ serde.workspace = true uuid.workspace = true rayon.workspace = true tokio.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-evm-core/optimism", + "foundry-evm-hardforks/optimism", + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-cheatcodes/optimism", + "foundry-evm-coverage/optimism", + "foundry-evm-fuzz/optimism", + "foundry-evm-traces/optimism", +] diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 33152b73dda3c..1932a834ab397 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -17,7 +17,7 @@ use foundry_evm_core::{ use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ BaseCounterExample, BasicTxDetails, CallDetails, CounterExample, FuzzCase, FuzzError, - FuzzFixtures, FuzzTestResult, + FuzzFixtures, FuzzRunMetadata, FuzzTestResult, strategies::{EvmFuzzState, fuzz_calldata, fuzz_calldata_from_state}, }; use foundry_evm_traces::SparsedTraceArena; @@ -71,6 +71,8 @@ struct WorkerState { runs: u32, /// Failure reason if this worker failed failure: Option, + /// Fuzz run metadata that produced the failure. + failure_run: Option, /// Last run timestamp in milliseconds /// /// Used to identify which worker ran last and collect its traces and call breakpoints @@ -93,6 +95,7 @@ impl WorkerState { deprecated_cheatcodes: HashMap::default(), runs: 0, failure: None, + failure_run: None, last_run_timestamp: 0, failed_corpus_replays: 0, } @@ -196,8 +199,14 @@ impl FuzzedExecutor { config: FuzzConfig, persisted_failure: Option, ) -> Self { - let max_workers = - if config.runs == 0 { 0 } else { Ord::max(1, config.runs / MIN_RUNS_PER_WORKER) }; + let run_limit = if config.run.is_some() { 1 } else { config.runs }; + let max_workers = if run_limit == 0 { + 0 + } else if config.run.is_some() { + 1 + } else { + Ord::max(1, run_limit / MIN_RUNS_PER_WORKER) + }; let num_workers = Ord::min(rayon::current_num_threads(), max_workers as usize); Self { executor_f: executor, runner, sender, config, persisted_failure, num_workers } } @@ -221,8 +230,9 @@ impl FuzzedExecutor { ) -> Result { let shared_state = SharedFuzzState::new(state, self.config.timeout, early_exit.clone()); - debug!(n = self.num_workers, "spawning workers"); - let workers = (0..self.num_workers) + let worker_ids = self.worker_ids(); + debug!(n = worker_ids.len(), "spawning workers"); + let workers = worker_ids .into_par_iter() .map(|worker_id| { let _guard = tokio_handle.enter(); @@ -364,8 +374,14 @@ impl FuzzedExecutor { } else { vec![] }; + let fuzz = failed_worker.failure_run.unwrap_or_default(); result.counterexample = Some(CounterExample::Single( - BaseCounterExample::from_fuzz_call(calldata, args, call.traces), + BaseCounterExample::from_fuzz_call(calldata, args, call.traces) + .with_fuzz_metadata(FuzzRunMetadata::new( + fuzz.seed.or(self.config.seed), + fuzz.run, + fuzz.worker, + )), )); } Some(TestCaseError::Reject(reason)) => { @@ -453,16 +469,7 @@ impl FuzzedExecutor { runner_config.cases = worker_runs; let mut runner = if let Some(seed) = self.config.seed { - // For deterministic parallel fuzzing, derive a unique seed for each worker - let worker_seed = if worker_id == 0 { - // Master worker uses the provided seed as is. - seed - } else { - // Derive a worker-specific seed using keccak256(seed || worker_id) - let seed_data = - [&seed.to_be_bytes::<32>()[..], &worker_id.to_be_bytes()[..]].concat(); - U256::from_be_bytes(keccak256(seed_data).0) - }; + let worker_seed = Self::fuzz_worker_seed(seed, worker_id); trace!(target: "forge::test", ?worker_seed, "deterministic seed for worker {worker_id}"); let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &worker_seed.to_be_bytes::<32>()); TestRunner::new_with_rng(runner_config, rng) @@ -470,11 +477,25 @@ impl FuzzedExecutor { TestRunner::new(runner_config) }; - let mut persisted_failure = self.persisted_failure.as_ref().filter(|_| worker_id == 0); + if let Some(target_run) = self.config.run { + for _ in 1..target_run { + if let Err(err) = corpus.new_input(&mut runner, &shared_state.state, func) { + worker.failure = Some(TestCaseError::fail(format!( + "failed to generate fuzzed input in worker {}: {err}", + worker.id + ))); + shared_state.try_claim_failure(worker_id); + return Ok(worker); + } + } + } + + let mut persisted_failure = + self.persisted_failure.as_ref().filter(|_| worker_id == 0 && self.config.run.is_none()); // Offset to stagger corpus syncs across workers; so that workers don't sync at the same // time. - let sync_offset = worker_id as u32 * 100; + let sync_offset = (worker_id as u32).saturating_mul(100); let sync_threshold = SYNC_INTERVAL + sync_offset; let mut runs_since_sync = sync_threshold; // Always sync at the start. let mut last_metrics_report = Instant::now(); @@ -483,11 +504,27 @@ impl FuzzedExecutor { // 2. Worker hasn't reached its specific run limit 'stop: while shared_state.should_continue() && worker.runs < worker_runs { // If counterexample recorded, replay it first, without incrementing runs. - let input = if worker_id == 0 + let (input, fuzz_run) = if worker_id == 0 && let Some(failure) = persisted_failure.take() && failure.calldata.get(..4).is_some_and(|selector| func.selector() == selector) { - failure.calldata.clone() + let seed = failure.fuzz.seed.or(self.config.seed); + if let Some(cheats) = executor.inspector_mut().cheatcodes.as_mut() + && let Some(seed) = seed + { + let run = failure.fuzz.run.unwrap_or(1); + let worker = failure.fuzz.worker.unwrap_or(worker_id as u32) as usize; + cheats.set_seed(Self::fuzz_run_seed(seed, worker, run)); + } + + ( + failure.calldata.clone(), + Some(FuzzRunMetadata::new( + seed, + failure.fuzz.run, + Some(failure.fuzz.worker.unwrap_or(worker_id as u32)), + )), + ) } else { runs_since_sync += 1; if runs_since_sync >= sync_threshold { @@ -503,13 +540,14 @@ impl FuzzedExecutor { runs_since_sync = 0; } + let fuzz_run = self.config.run.unwrap_or(worker.runs + 1); if let Some(cheats) = executor.inspector_mut().cheatcodes.as_mut() && let Some(seed) = self.config.seed { - cheats.set_seed(seed.wrapping_add(U256::from(worker.runs))); + cheats.set_seed(Self::fuzz_run_seed(seed, worker_id, fuzz_run)); } - match corpus.new_input(&mut runner, &shared_state.state, func) { + let input = match corpus.new_input(&mut runner, &shared_state.state, func) { Ok(input) => input, Err(err) => { worker.failure = Some(TestCaseError::fail(format!( @@ -519,13 +557,24 @@ impl FuzzedExecutor { shared_state.try_claim_failure(worker_id); break 'stop; } - } + }; + + ( + input, + Some(FuzzRunMetadata::new( + self.config.seed, + Some(fuzz_run), + Some(worker_id as u32), + )), + ) }; let mut inc_runs = || { let total_runs = shared_state.increment_runs(); debug_assert!( - shared_state.timer.is_enabled() || total_runs <= self.config.runs, + shared_state.timer.is_enabled() + || total_runs + <= if self.config.run.is_some() { 1 } else { self.config.runs }, "worker runs were not distributed correctly" ); worker.runs += 1; @@ -595,6 +644,7 @@ impl FuzzedExecutor { .. }) => { inc_runs(); + worker.failure_run = fuzz_run; // Only classify magic skip payloads when the revert originates from the // cheatcode address. @@ -656,7 +706,7 @@ impl FuzzedExecutor { /// Determines the number of runs per worker. const fn runs_per_worker(&self, worker_id: usize) -> u32 { let worker_id = worker_id as u32; - let total_runs = self.config.runs; + let total_runs = if self.config.run.is_some() { 1 } else { self.config.runs }; let n = self.num_workers as u32; let runs = total_runs / n; let remainder = total_runs % n; @@ -664,4 +714,29 @@ impl FuzzedExecutor { // assuming `worker_id` is in `0..n`. if worker_id < remainder { runs + 1 } else { runs } } + + /// Returns the worker IDs to execute. + fn worker_ids(&self) -> Vec { + if self.config.run.is_some() { + vec![self.config.worker.unwrap_or(0) as usize] + } else { + (0..self.num_workers).collect() + } + } + + /// Derives the deterministic RNG seed for a fuzz worker. + fn fuzz_worker_seed(seed: U256, worker_id: usize) -> U256 { + if worker_id == 0 { + seed + } else { + let worker_id = worker_id as u32; + let seed_data = [&seed.to_be_bytes::<32>()[..], &worker_id.to_be_bytes()[..]].concat(); + U256::from_be_bytes(keccak256(seed_data).0) + } + } + + /// Derives the deterministic RNG seed for cheatcode randomness in a worker-local run. + fn fuzz_run_seed(seed: U256, worker_id: usize, run: u32) -> U256 { + Self::fuzz_worker_seed(seed, worker_id).wrapping_add(U256::from(run.saturating_sub(1))) + } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 27d8e6a0ed588..e02cdbc393ee6 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -736,7 +736,7 @@ impl<'a, FEN: FoundryEvmNetwork> InvariantExecutor<'a, FEN> { if !msg.is_empty() { msg.push_str(", "); } - msg.push_str(&format!("{}", &corpus_manager.metrics)); + msg.push_str(&format!("{}", corpus_manager.metrics)); } progress.set_message(msg); } diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 62e4e80a73674..5629b17d936da 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -50,3 +50,12 @@ rand.workspace = true serde.workspace = true thiserror.workspace = true tracing.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm-core/optimism", + "foundry-evm-coverage/optimism", + "foundry-evm-traces/optimism", +] diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 44d71fb6deee3..9c3e7d179c7f6 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -33,6 +33,27 @@ pub use strategies::LiteralMaps; mod inspector; pub use inspector::Fuzzer; +/// Metadata needed to reproduce a fuzz run. +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] +pub struct FuzzRunMetadata { + /// Seed used for the worker's input stream. + #[serde(default, rename = "fuzz_seed", skip_serializing_if = "Option::is_none")] + pub seed: Option, + /// 1-based run inside the worker's input stream. + #[serde(default, rename = "fuzz_run", skip_serializing_if = "Option::is_none")] + pub run: Option, + /// Worker that generated the input stream. + #[serde(default, rename = "fuzz_worker", skip_serializing_if = "Option::is_none")] + pub worker: Option, +} + +impl FuzzRunMetadata { + /// Creates metadata for reproducing a fuzz run. + pub const fn new(seed: Option, run: Option, worker: Option) -> Self { + Self { seed, run, worker } + } +} + /// Details of a transaction generated by fuzz strategy for fuzzing a target. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BasicTxDetails { @@ -102,6 +123,9 @@ pub struct BaseCounterExample { /// Whether to display sequence as solidity. #[serde(skip)] pub show_solidity: bool, + /// Fuzz metadata needed to reproduce this counterexample. + #[serde(flatten)] + pub fuzz: FuzzRunMetadata, } impl BaseCounterExample { @@ -137,6 +161,7 @@ impl BaseCounterExample { ), traces, show_solidity, + fuzz: FuzzRunMetadata::default(), }; } } @@ -154,6 +179,7 @@ impl BaseCounterExample { raw_args: None, traces, show_solidity: false, + fuzz: FuzzRunMetadata::default(), } } @@ -176,8 +202,15 @@ impl BaseCounterExample { raw_args: Some(foundry_common::fmt::format_tokens_raw(&args).format(", ").to_string()), traces, show_solidity: false, + fuzz: FuzzRunMetadata::default(), } } + + /// Sets fuzz metadata for reproducing this counterexample. + pub const fn with_fuzz_metadata(mut self, fuzz: FuzzRunMetadata) -> Self { + self.fuzz = fuzz; + self + } } impl fmt::Display for BaseCounterExample { @@ -229,7 +262,7 @@ 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)? } if let Some(args) = &self.args { diff --git a/crates/evm/hardforks/Cargo.toml b/crates/evm/hardforks/Cargo.toml index 9bf318028487a..68f6fb23bab07 100644 --- a/crates/evm/hardforks/Cargo.toml +++ b/crates/evm/hardforks/Cargo.toml @@ -16,10 +16,14 @@ workspace = true [dependencies] alloy-chains.workspace = true alloy-hardforks = { workspace = true, features = ["serde"] } -alloy-op-hardforks = { workspace = true, features = ["serde"] } +alloy-op-hardforks = { workspace = true, features = ["serde"], optional = true } alloy-rpc-types.workspace = true -op-revm.workspace = true +op-revm = { workspace = true, optional = true } revm.workspace = true serde = { workspace = true, features = ["derive"] } tempo-chainspec.workspace = true foundry-compilers.workspace = true + +[features] +default = ["optimism"] +optimism = ["dep:alloy-op-hardforks", "dep:op-revm"] diff --git a/crates/evm/hardforks/src/lib.rs b/crates/evm/hardforks/src/lib.rs index 8a29ebb7af4ec..a8e0d51738263 100644 --- a/crates/evm/hardforks/src/lib.rs +++ b/crates/evm/hardforks/src/lib.rs @@ -8,11 +8,13 @@ use std::str::FromStr; use alloy_chains::Chain; use alloy_rpc_types::BlockNumberOrTag; use foundry_compilers::artifacts::EvmVersion; +#[cfg(feature = "optimism")] use op_revm::OpSpecId; use revm::primitives::hardfork::SpecId; use serde::{Deserialize, Serialize}; pub use alloy_hardforks::EthereumHardfork; +#[cfg(feature = "optimism")] pub use alloy_op_hardforks::OpHardfork; pub use tempo_chainspec::hardfork::TempoHardfork; @@ -20,6 +22,7 @@ pub use tempo_chainspec::hardfork::TempoHardfork; #[serde(into = "String")] pub enum FoundryHardfork { Ethereum(EthereumHardfork), + #[cfg(feature = "optimism")] Optimism(OpHardfork), Tempo(TempoHardfork), } @@ -28,6 +31,7 @@ impl From for String { fn from(fork: FoundryHardfork) -> Self { match fork { FoundryHardfork::Ethereum(h) => format!("{h}"), + #[cfg(feature = "optimism")] FoundryHardfork::Optimism(h) => format!("optimism:{h}"), FoundryHardfork::Tempo(h) => format!("tempo:{h}"), } @@ -64,6 +68,7 @@ impl FromStr for FoundryHardfork { .map(Self::Ethereum) .map_err(|_| format!("unknown ethereum hardfork '{fork_raw}'")), + #[cfg(feature = "optimism")] "op" | "optimism" => OpHardfork::from_str(&fork) .map(Self::Optimism) .map_err(|_| format!("unknown optimism hardfork '{fork_raw}'")), @@ -83,6 +88,7 @@ impl FoundryHardfork { Self::Ethereum(h) } + #[cfg(feature = "optimism")] pub const fn optimism(h: OpHardfork) -> Self { Self::Optimism(h) } @@ -95,6 +101,7 @@ impl FoundryHardfork { pub fn name(&self) -> String { match self { Self::Ethereum(h) => format!("{h}"), + #[cfg(feature = "optimism")] Self::Optimism(h) => format!("{h}"), Self::Tempo(h) => format!("{h}"), } @@ -106,6 +113,7 @@ impl FoundryHardfork { pub const fn namespace(&self) -> Option<&'static str> { match self { Self::Ethereum(_) => None, + #[cfg(feature = "optimism")] Self::Optimism(_) => Some("optimism"), Self::Tempo(_) => Some("tempo"), } @@ -119,6 +127,7 @@ impl FoundryHardfork { if let Some(fork) = EthereumHardfork::from_chain_and_timestamp(chain, timestamp) { return Some(Self::Ethereum(fork)); } + #[cfg(feature = "optimism")] if let Some(fork) = OpHardfork::from_chain_and_timestamp(chain, timestamp) { return Some(Self::Optimism(fork)); } @@ -143,12 +152,14 @@ impl From for EthereumHardfork { } } +#[cfg(feature = "optimism")] impl From for FoundryHardfork { fn from(value: OpHardfork) -> Self { Self::Optimism(value) } } +#[cfg(feature = "optimism")] impl From for OpHardfork { fn from(fork: FoundryHardfork) -> Self { match fork { @@ -177,12 +188,14 @@ impl From for SpecId { fn from(fork: FoundryHardfork) -> Self { match fork { FoundryHardfork::Ethereum(hardfork) => spec_id_from_ethereum_hardfork(hardfork), + #[cfg(feature = "optimism")] FoundryHardfork::Optimism(hardfork) => spec_id_from_optimism_hardfork(hardfork).into(), FoundryHardfork::Tempo(hardfork) => hardfork.into(), } } } +#[cfg(feature = "optimism")] impl From for OpSpecId { fn from(fork: FoundryHardfork) -> Self { match fork { @@ -223,6 +236,7 @@ pub fn spec_id_from_ethereum_hardfork(hardfork: EthereumHardfork) -> SpecId { } /// Map an `OptimismHardfork` enum into its corresponding `OpSpecId`. +#[cfg(feature = "optimism")] pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId { match hardfork { OpHardfork::Bedrock => OpSpecId::BEDROCK, @@ -265,6 +279,7 @@ impl FromEvmVersion for SpecId { } } +#[cfg(feature = "optimism")] impl FromEvmVersion for OpSpecId { fn from_evm_version(version: EvmVersion) -> Self { match version { @@ -324,16 +339,6 @@ mod tests { assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Osaka), SpecId::OSAKA); } - #[test] - fn test_optimism_spec_id_mapping() { - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK); - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH); - - // Test latest hardforks - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE); - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP); - } - #[test] fn test_tempo_spec_id_mapping() { assert_eq!(SpecId::from(TempoHardfork::Genesis), SpecId::OSAKA); @@ -371,25 +376,40 @@ mod tests { } #[test] - fn test_from_chain_and_timestamp_op_mainnet() { - let op_chain_id = 10; - assert!(matches!( - FoundryHardfork::from_chain_and_timestamp(op_chain_id, u64::MAX), - Some(FoundryHardfork::Optimism(_)) - )); + fn test_from_chain_and_timestamp_unknown_chain() { + assert_eq!(FoundryHardfork::from_chain_and_timestamp(999999, 0), None); } - #[test] - fn test_from_chain_and_timestamp_base() { - let base_chain_id = 8453; - assert!(matches!( - FoundryHardfork::from_chain_and_timestamp(base_chain_id, u64::MAX), - Some(FoundryHardfork::Optimism(_)) - )); - } + #[cfg(feature = "optimism")] + mod optimism { + use super::*; - #[test] - fn test_from_chain_and_timestamp_unknown_chain() { - assert_eq!(FoundryHardfork::from_chain_and_timestamp(999999, 0), None); + #[test] + fn test_optimism_spec_id_mapping() { + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK); + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH); + + // Test latest hardforks + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE); + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP); + } + + #[test] + fn test_from_chain_and_timestamp_op_mainnet() { + let op_chain_id = 10; + assert!(matches!( + FoundryHardfork::from_chain_and_timestamp(op_chain_id, u64::MAX), + Some(FoundryHardfork::Optimism(_)) + )); + } + + #[test] + fn test_from_chain_and_timestamp_base() { + let base_chain_id = 8453; + assert!(matches!( + FoundryHardfork::from_chain_and_timestamp(base_chain_id, u64::MAX), + Some(FoundryHardfork::Optimism(_)) + )); + } } } diff --git a/crates/evm/networks/Cargo.toml b/crates/evm/networks/Cargo.toml index a63ed34ba61cf..00c9abf0f90f7 100644 --- a/crates/evm/networks/Cargo.toml +++ b/crates/evm/networks/Cargo.toml @@ -19,7 +19,7 @@ foundry-evm-hardforks.workspace = true alloy-chains.workspace = true alloy-eips.workspace = true alloy-evm.workspace = true -alloy-op-hardforks.workspace = true +alloy-op-hardforks = { workspace = true, optional = true } alloy-primitives = { workspace = true, features = [ "serde", "getrandom", @@ -43,4 +43,8 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } serde.workspace = true [dev-dependencies] -serde_json.workspace = true \ No newline at end of file +serde_json.workspace = true + +[features] +default = ["optimism"] +optimism = ["dep:alloy-op-hardforks", "foundry-evm-hardforks/optimism"] diff --git a/crates/evm/networks/src/lib.rs b/crates/evm/networks/src/lib.rs index 303b9ca8b7a13..384cee5a7bed3 100644 --- a/crates/evm/networks/src/lib.rs +++ b/crates/evm/networks/src/lib.rs @@ -11,7 +11,6 @@ use alloy_chains::{ }; use alloy_eips::eip1559::BaseFeeParams; use alloy_evm::precompiles::PrecompilesMap; -use alloy_op_hardforks::{OpChainHardforks, OpHardforks}; use alloy_primitives::{Address, ChainId, map::AddressHashMap}; use clap::Parser; use foundry_evm_hardforks::FoundryHardfork; @@ -20,20 +19,52 @@ use std::collections::BTreeMap; pub mod celo; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, clap::ValueEnum)] +#[cfg(feature = "optimism")] +mod optimism; + +#[derive( + Clone, + Copy, + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + clap::ValueEnum, +)] #[serde(rename_all = "lowercase")] #[clap(rename_all = "lowercase")] pub enum NetworkVariant { #[default] Ethereum, + #[cfg(feature = "optimism")] Optimism, Tempo, } +impl std::str::FromStr for NetworkVariant { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "ethereum" => Ok(Self::Ethereum), + #[cfg(feature = "optimism")] + "optimism" => Ok(Self::Optimism), + "tempo" => Ok(Self::Tempo), + _ => Err(format!("unknown network variant: {s}")), + } + } +} + impl NetworkVariant { pub const fn name(&self) -> &'static str { match self { Self::Ethereum => "ethereum", + #[cfg(feature = "optimism")] Self::Optimism => "optimism", Self::Tempo => "tempo", } @@ -50,32 +81,37 @@ impl From for NetworkVariant { fn from(chain_id: ChainId) -> Self { let chain = Chain::from_id(chain_id); if chain.is_tempo() { - Self::Tempo - } else if chain.is_optimism() { - Self::Optimism - } else { - Self::Ethereum + return Self::Tempo; + } + #[cfg(feature = "optimism")] + if chain.is_optimism() { + return Self::Optimism; } + Self::Ethereum } } #[derive(Clone, Debug, Default, Parser, Deserialize, Copy, PartialEq, Eq)] pub struct NetworkConfigs { /// Enable a specific network family. - #[arg(help_heading = "Networks", long, short, num_args = 1, value_name = "NETWORK", value_enum, conflicts_with_all = ["celo", "optimism", "tempo"])] + #[arg(help_heading = "Networks", long, short, num_args = 1, value_name = "NETWORK", value_enum, conflicts_with_all = ["celo", "tempo"])] + #[cfg_attr(feature = "optimism", arg(conflicts_with = "optimism"))] #[serde(default)] - network: Option, + pub(crate) network: Option, /// Enable Celo network features. - #[arg(help_heading = "Networks", long, conflicts_with_all = ["network", "optimism", "tempo"])] + #[arg(help_heading = "Networks", long, conflicts_with_all = ["network", "tempo"])] + #[cfg_attr(feature = "optimism", arg(conflicts_with = "optimism"))] celo: bool, /// Enable Optimism network features (deprecated: use --network optimism). + #[cfg(feature = "optimism")] #[arg(long, hide = true, conflicts_with_all = ["network", "celo", "tempo"])] // Deserialize-only legacy alias: accepted in foundry.toml but never serialized — the // canonical form is `network = "optimism"`. #[serde(default)] - optimism: bool, + pub(crate) optimism: bool, /// Enable Tempo network features (deprecated: use --network tempo). - #[arg(long, hide = true, conflicts_with_all = ["network", "celo", "optimism"])] + #[arg(long, hide = true, conflicts_with_all = ["network", "celo"])] + #[cfg_attr(feature = "optimism", arg(conflicts_with = "optimism"))] // Deserialize-only legacy alias: accepted in foundry.toml but never serialized — the // canonical form is `network = "tempo"`. #[serde(default)] @@ -102,10 +138,6 @@ impl Serialize for NetworkConfigs { } impl NetworkConfigs { - pub fn with_optimism() -> Self { - Self { network: Some(NetworkVariant::Optimism), optimism: true, ..Default::default() } - } - pub fn with_celo() -> Self { Self { celo: true, ..Default::default() } } @@ -114,11 +146,7 @@ impl NetworkConfigs { Self { network: Some(NetworkVariant::Tempo), tempo: true, ..Default::default() } } - pub fn is_optimism(&self) -> bool { - matches!(self.resolved_network(), Some(NetworkVariant::Optimism)) - } - - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { matches!(self.resolved_network(), Some(NetworkVariant::Tempo)) } @@ -127,14 +155,18 @@ impl NetworkConfigs { } /// Returns the resolved network variant, folding legacy flags. - fn resolved_network(&self) -> Option { - self.network.or(if self.optimism { - Some(NetworkVariant::Optimism) - } else if self.tempo { - Some(NetworkVariant::Tempo) - } else { - None - }) + const fn resolved_network(&self) -> Option { + if let Some(n) = self.network { + return Some(n); + } + #[cfg(feature = "optimism")] + if self.optimism { + return Some(NetworkVariant::Optimism); + } + if self.tempo { + return Some(NetworkVariant::Tempo); + } + None } /// Returns the name of the currently active non-Ethereum network, or `None` for plain Ethereum. @@ -150,16 +182,12 @@ impl NetworkConfigs { /// For Optimism networks, returns Canyon parameters if the Canyon hardfork is active /// at the given timestamp, otherwise returns pre-Canyon parameters. pub fn base_fee_params(&self, timestamp: u64) -> BaseFeeParams { + #[cfg(feature = "optimism")] if self.is_optimism() { - let op_hardforks = OpChainHardforks::op_mainnet(); - if op_hardforks.is_canyon_active_at_timestamp(timestamp) { - BaseFeeParams::optimism_canyon() - } else { - BaseFeeParams::optimism() - } - } else { - BaseFeeParams::ethereum() + return self.op_base_fee_params(timestamp); } + let _ = timestamp; + BaseFeeParams::ethereum() } pub fn bypass_prevrandao(&self, chain_id: u64) -> bool { @@ -174,21 +202,23 @@ impl NetworkConfigs { pub fn with_chain_id(self, chain_id: u64) -> Self { let chain = Chain::from_id(chain_id); - if self.resolved_network().is_none() { - if chain.is_tempo() { - Self::with_tempo() - } else if chain.is_optimism() { - Self::with_optimism() + if self.resolved_network().is_some() { + return if !self.celo + && matches!(chain.named(), Some(NamedChain::Celo | NamedChain::CeloSepolia)) + { + Self::with_celo() } else { self - } - } else if !self.celo - && matches!(chain.named(), Some(NamedChain::Celo | NamedChain::CeloSepolia)) - { - Self::with_celo() - } else { - self + }; + } + if chain.is_tempo() { + return Self::with_tempo(); + } + #[cfg(feature = "optimism")] + if chain.is_optimism() { + return Self::with_optimism(); } + self } /// Validates `hardfork` against the current `NetworkConfigs` and, if consistent, returns an @@ -208,6 +238,7 @@ impl NetworkConfigs { let network = match hardfork { FoundryHardfork::Ethereum(_) => self, FoundryHardfork::Tempo(_) => Self::with_tempo(), + #[cfg(feature = "optimism")] FoundryHardfork::Optimism(_) => Self::with_optimism(), }; @@ -243,6 +274,21 @@ impl NetworkConfigs { } } +impl From for NetworkConfigs { + fn from(network: NetworkVariant) -> Self { + match network { + NetworkVariant::Ethereum => Self::default(), + NetworkVariant::Tempo => { + Self { network: Some(network), tempo: true, ..Default::default() } + } + #[cfg(feature = "optimism")] + NetworkVariant::Optimism => { + Self { network: Some(network), optimism: true, ..Default::default() } + } + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -254,17 +300,6 @@ mod tests { let via_new = NetworkConfigs { network: Some(NetworkVariant::Tempo), ..Default::default() }; let via_old = NetworkConfigs { tempo: true, ..Default::default() }; assert_eq!(via_new.is_tempo(), via_old.is_tempo()); - assert_eq!(via_new.is_optimism(), via_old.is_optimism()); - assert_eq!(via_new.active_network_name(), via_old.active_network_name()); - } - - #[test] - fn new_optimism_flag_equivalent_to_legacy() { - let via_new = - NetworkConfigs { network: Some(NetworkVariant::Optimism), ..Default::default() }; - let via_old = NetworkConfigs { optimism: true, ..Default::default() }; - assert_eq!(via_new.is_optimism(), via_old.is_optimism()); - assert_eq!(via_new.is_tempo(), via_old.is_tempo()); assert_eq!(via_new.active_network_name(), via_old.active_network_name()); } @@ -276,31 +311,11 @@ mod tests { assert_eq!(cfg.active_network_name(), Some("tempo")); } - #[test] - fn active_network_name_optimism() { - let cfg = NetworkConfigs::with_optimism(); - assert_eq!(cfg.active_network_name(), Some("optimism")); - } - #[test] fn active_network_name_default_is_none() { assert_eq!(NetworkConfigs::default().active_network_name(), None); } - // --- new flag takes precedence over legacy flag --- - - #[test] - fn new_flag_wins_over_legacy_when_both_set() { - // --network optimism --tempo: network field wins - let cfg = NetworkConfigs { - network: Some(NetworkVariant::Optimism), - tempo: true, - ..Default::default() - }; - assert!(cfg.is_optimism()); - assert!(!cfg.is_tempo()); - } - // --- Serde round-trip --- #[test] @@ -309,16 +324,6 @@ mod tests { let json = serde_json::to_string(&original).unwrap(); let restored: NetworkConfigs = serde_json::from_str(&json).unwrap(); assert!(restored.is_tempo()); - assert!(!restored.is_optimism()); - } - - #[test] - fn serde_roundtrip_optimism() { - let original = NetworkConfigs::with_optimism(); - let json = serde_json::to_string(&original).unwrap(); - let restored: NetworkConfigs = serde_json::from_str(&json).unwrap(); - assert!(restored.is_optimism()); - assert!(!restored.is_tempo()); } #[test] @@ -345,8 +350,55 @@ mod tests { let json_tempo = r#"{"network": "tempo", "celo": false, "bypass_prevrandao": false}"#; let cfg_tempo: NetworkConfigs = serde_json::from_str(json_tempo).unwrap(); assert!(cfg_tempo.is_tempo()); - let json_optimism = r#"{"network": "optimism", "celo": false, "bypass_prevrandao": false}"#; - let cfg_optimism: NetworkConfigs = serde_json::from_str(json_optimism).unwrap(); - assert!(cfg_optimism.is_optimism()); + } + + #[cfg(feature = "optimism")] + mod optimism { + use super::*; + + #[test] + fn new_optimism_flag_equivalent_to_legacy() { + let via_new = + NetworkConfigs { network: Some(NetworkVariant::Optimism), ..Default::default() }; + let via_old = NetworkConfigs { optimism: true, ..Default::default() }; + assert_eq!(via_new.is_optimism(), via_old.is_optimism()); + assert_eq!(via_new.is_tempo(), via_old.is_tempo()); + assert_eq!(via_new.active_network_name(), via_old.active_network_name()); + } + + #[test] + fn active_network_name_optimism() { + let cfg = NetworkConfigs::with_optimism(); + assert_eq!(cfg.active_network_name(), Some("optimism")); + } + + #[test] + fn new_flag_wins_over_legacy_when_both_set() { + // --network optimism --tempo: network field wins + let cfg = NetworkConfigs { + network: Some(NetworkVariant::Optimism), + tempo: true, + ..Default::default() + }; + assert!(cfg.is_optimism()); + assert!(!cfg.is_tempo()); + } + + #[test] + fn serde_roundtrip_optimism() { + let original = NetworkConfigs::with_optimism(); + let json = serde_json::to_string(&original).unwrap(); + let restored: NetworkConfigs = serde_json::from_str(&json).unwrap(); + assert!(restored.is_optimism()); + assert!(!restored.is_tempo()); + } + + #[test] + fn serde_optimism_field_deserialized() { + let json_optimism = + r#"{"network": "optimism", "celo": false, "bypass_prevrandao": false}"#; + let cfg_optimism: NetworkConfigs = serde_json::from_str(json_optimism).unwrap(); + assert!(cfg_optimism.is_optimism()); + } } } diff --git a/crates/evm/networks/src/optimism.rs b/crates/evm/networks/src/optimism.rs new file mode 100644 index 0000000000000..5fffa38a333c7 --- /dev/null +++ b/crates/evm/networks/src/optimism.rs @@ -0,0 +1,25 @@ +//! Optimism-specific extensions for [`NetworkConfigs`] and related helpers. + +use crate::{NetworkConfigs, NetworkVariant}; +use alloy_eips::eip1559::BaseFeeParams; +use alloy_op_hardforks::{OpChainHardforks, OpHardforks}; + +impl NetworkConfigs { + pub fn with_optimism() -> Self { + Self { network: Some(NetworkVariant::Optimism), optimism: true, ..Default::default() } + } + + pub const fn is_optimism(&self) -> bool { + matches!(self.resolved_network(), Some(NetworkVariant::Optimism)) + } + + /// Optimism-specific base fee parameters, picking Canyon vs pre-Canyon based on `timestamp`. + pub(crate) fn op_base_fee_params(&self, timestamp: u64) -> BaseFeeParams { + let op_hardforks = OpChainHardforks::op_mainnet(); + if op_hardforks.is_canyon_active_at_timestamp(timestamp) { + BaseFeeParams::optimism_canyon() + } else { + BaseFeeParams::optimism() + } + } +} diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 90d2db724cebc..73d64d3ab5d07 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -50,3 +50,7 @@ tempfile.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true yansi.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism", "foundry-evm-core/optimism"] diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index bad5c577bc69e..b6f11772620f9 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -26,3 +26,7 @@ foundry-test-utils.workspace = true toml.workspace = true snapbox.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/fmt/src/state/mod.rs b/crates/fmt/src/state/mod.rs index 89a9bf152c8c2..4b986017b71dd 100644 --- a/crates/fmt/src/state/mod.rs +++ b/crates/fmt/src/state/mod.rs @@ -711,7 +711,7 @@ impl<'sess> State<'sess, '_> { // Merge the lines and let the wrapper handle breaking if needed let merged_line = format!( "{current_line} {next_content}", - next_content = &next_line[prefix.len()..].trim_start() + next_content = next_line[prefix.len()..].trim_start() ); result.push(merged_line); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 064834d248d5f..667da6b442ca1 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -117,7 +117,7 @@ tempfile.workspace = true alloy-signer-local.workspace = true [features] -default = ["jemalloc", "asm-keccak"] +default = ["jemalloc", "asm-keccak", "optimism"] asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] @@ -126,3 +126,15 @@ aws-kms = ["foundry-wallets/aws-kms"] gcp-kms = ["foundry-wallets/gcp-kms"] turnkey = ["foundry-wallets/turnkey"] isolate-by-default = ["foundry-config/isolate-by-default"] +optimism = [ + "foundry-evm/optimism", + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-cli/optimism", + "forge-script/optimism", + "forge-verify/optimism", + "forge-doc/optimism", + "forge-fmt/optimism", + "forge-lint/optimism", + "forge-sol-macro-gen/optimism", +] diff --git a/crates/forge/assets/tempo/MailTemplate.s.sol b/crates/forge/assets/tempo/MailTemplate.s.sol index 27512efe4d5ec..45006f7cd0e06 100644 --- a/crates/forge/assets/tempo/MailTemplate.s.sol +++ b/crates/forge/assets/tempo/MailTemplate.s.sol @@ -14,7 +14,7 @@ contract MailScript is Script { function run(string memory salt) public { vm.startBroadcast(); - address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.ALPHA_USD_ADDRESS); + address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.PATH_USD_ADDRESS); StdPrecompiles.TIP_FEE_MANAGER.setUserToken(feeToken); ITIP20 token = ITIP20( diff --git a/crates/forge/assets/tempo/MailTemplate.t.sol b/crates/forge/assets/tempo/MailTemplate.t.sol index b1749db5df0bf..19760303860a1 100644 --- a/crates/forge/assets/tempo/MailTemplate.t.sol +++ b/crates/forge/assets/tempo/MailTemplate.t.sol @@ -17,7 +17,7 @@ contract MailTest is Test { address public constant BOB = address(0x70997970C51812dc3A010C7d01b50e0d17dc79C8); function setUp() public virtual { - address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.ALPHA_USD_ADDRESS); + address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.PATH_USD_ADDRESS); StdPrecompiles.TIP_FEE_MANAGER.setUserToken(feeToken); token = ITIP20( diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index ea034bce87185..b8ce2a9b945b1 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -87,8 +87,11 @@ impl CoverageArgs { config = self.load_config()?; } - // Set fuzz seed so coverage reports are deterministic - config.fuzz.seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + // Default to a static fuzz seed so coverage reports are deterministic, + // but allow the user to override it via `--fuzz-seed` or `[fuzz] seed` in config. + if config.fuzz.seed.is_none() { + config.fuzz.seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + } let (paths, mut output) = { let (project, output) = self.build(&config)?; diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 765bb64f95fdd..4f638b6edcdf8 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -13,7 +13,10 @@ use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, - utils::{LoadConfig, find_contract_artifacts, read_constructor_args_file}, + utils::{ + LoadConfig, ResolvedLane, find_contract_artifacts, maybe_print_resolved_lane, + read_constructor_args_file, resolve_lane, + }, }; use foundry_common::{ FoundryTransactionBuilder, @@ -203,6 +206,11 @@ impl CreateArgs { self.tx.tempo.key_id = Some(ak.key_address); } + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `self.tx.tempo.nonce_key` from the lane. + // Must happen before `self.deploy(...)` so `TempoOpts::apply` picks up the nonce_key. + let resolved_lane = resolve_lane(&mut self.tx.tempo, &config.root)?; + // Whether to broadcast the transaction or not let dry_run = !self.broadcast; @@ -223,6 +231,7 @@ impl CreateArgs { dry_run, None, Some(browser), + resolved_lane, ) .await } else if self.unlocked { @@ -239,6 +248,7 @@ impl CreateArgs { dry_run, None, None, + resolved_lane, ) .await } else if let Some(ak) = access_key { @@ -259,6 +269,7 @@ impl CreateArgs { dry_run, Some((signer, ak)), None, + resolved_lane, ) .await } else { @@ -282,6 +293,7 @@ impl CreateArgs { dry_run, None, None, + resolved_lane, ) .await } @@ -362,6 +374,7 @@ impl CreateArgs { dry_run: bool, tempo_keychain: Option<(WalletSigner, TempoAccessKeyConfig)>, browser_signer: Option>, + resolved_lane: Option, ) -> Result<()> where N::TransactionRequest: FoundryTransactionBuilder + serde::Serialize, @@ -398,7 +411,7 @@ impl CreateArgs { // If Tempo chain fee token must be set if chain.is_tempo() { - if let Some(fee_token) = self.tx.tempo.fee_token { + if let Some(fee_token) = self.tx.tempo.common.fee_token { deployer.tx.set_fee_token(fee_token); } else { deployer.tx.set_fee_token(DEFAULT_FEE_TOKEN); @@ -408,15 +421,18 @@ impl CreateArgs { // Apply user-provided gas, fee, nonce, and Tempo options. self.tx.apply::(&mut deployer.tx, is_legacy); - // For keychain mode, set key_id and nonce_key before gas estimation. // Convert the CREATE into an AA-compatible call entry since Tempo AA // transactions use a `calls` list instead of `to`+`input`. + if chain.is_tempo() { + deployer.tx.convert_create_to_call(); + } + + // For keychain mode, set key_id and nonce_key before gas estimation. if let Some((_, ref ak)) = tempo_keychain { deployer.tx.set_key_id(ak.key_address); if deployer.tx.nonce_key().is_none() { deployer.tx.set_nonce_key(U256::ZERO); } - deployer.tx.convert_create_to_call(); } // Fetch defaults from provider for values not specified by user. @@ -424,6 +440,20 @@ impl CreateArgs { deployer.tx.set_nonce(provider.get_transaction_count(deployer_address).await?); } + maybe_print_resolved_lane(resolved_lane.as_ref(), deployer.tx.nonce().unwrap_or_default())?; + + if let Some((_, ref ak)) = tempo_keychain { + deployer + .tx + .prepare_access_key_authorization( + provider.as_ref(), + ak.wallet_address, + ak.key_address, + ak.key_authorization.as_ref(), + ) + .await?; + } + // set access list if specified if let Some(access_list) = match self.tx.access_list { None => None, @@ -500,6 +530,11 @@ impl CreateArgs { return Ok(()); } + let tempo_sponsor = self.tx.tempo.sponsor_config().await?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut deployer.tx, deployer_address).await?; + } + // Deploy the actual contract let (deployed_contract, receipt) = if let Some(browser) = browser_signer { // Browser wallet signs and sends the transaction diff --git a/crates/forge/src/cmd/snapshot.rs b/crates/forge/src/cmd/snapshot.rs index c8dc2ba72aae1..7c6fb51ce3266 100644 --- a/crates/forge/src/cmd/snapshot.rs +++ b/crates/forge/src/cmd/snapshot.rs @@ -99,8 +99,11 @@ impl GasSnapshotArgs { } pub async fn run(mut self) -> Result<()> { - // Set fuzz seed so gas snapshots are deterministic - self.test.fuzz_seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + // Default to a static fuzz seed so gas snapshots are deterministic, + // but allow the user to override it via `--fuzz-seed`. + if self.test.fuzz_seed.is_none() { + self.test.fuzz_seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + } let outcome = self.test.compile_and_run().await?; outcome.ensure_ok(false)?; diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index 94376dc5238cd..dd8f3afd56197 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -3,7 +3,7 @@ use crate::{ MultiContractRunner, MultiContractRunnerBuilder, decode::decode_console_logs, gas_report::GasReport, - multi_runner::matches_artifact, + multi_runner::{MultiNetworkConfig, matches_artifact}, result::{SuiteResult, TestOutcome, TestStatus}, traces::{ CallTraceDecoderBuilder, InternalTraceMode, TraceKind, @@ -31,7 +31,7 @@ use foundry_compilers::{ utils::source_files_iter, }; use foundry_config::{ - Config, figment, + Config, InlineConfig, figment, figment::{ Metadata, Profile, Provider, value::{Dict, Map}, @@ -39,10 +39,11 @@ use foundry_config::{ filter::GlobMatcher, }; use foundry_debugger::Debugger; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ core::evm::{ - BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, SpecFor, TempoEvmNetwork, - TxEnvFor, + BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, SpecFor, TempoEvmNetwork, TxEnvFor, }, opts::EvmOpts, traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth}, @@ -169,6 +170,14 @@ pub struct TestArgs { #[arg(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] pub fuzz_runs: Option, + /// Run only the fuzz case at the given 1-based run index. + #[arg(long, env = "FOUNDRY_FUZZ_RUN", value_name = "RUN")] + pub fuzz_run: Option, + + /// Run the fuzz case from the given worker. Requires `--fuzz-run`. + #[arg(long, env = "FOUNDRY_FUZZ_WORKER", value_name = "WORKER", requires = "fuzz_run")] + pub fuzz_worker: Option, + /// Timeout for each fuzz run in seconds. #[arg(long, env = "FOUNDRY_FUZZ_TIMEOUT", value_name = "TIMEOUT")] pub fuzz_timeout: Option, @@ -301,6 +310,10 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, coverage: bool, ) -> Result { + if config.fuzz.run == Some(0) { + bail!("`fuzz.run` must be greater than 0"); + } + // Explicitly enable isolation for gas reports for more correct gas accounting. if self.gas_report { evm_opts.isolate = true; @@ -342,40 +355,80 @@ impl TestArgs { // Auto-detect network from fork chain ID when not explicitly configured. evm_opts.infer_network_from_fork().await; - // Dispatch based on network type. - let (libraries, mut outcome) = if evm_opts.networks.is_tempo() { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? - } else if evm_opts.networks.is_optimism() { - self.build_and_run_tests::( + // Parse inline config early to detect per-test network annotations. + let inline_config = InlineConfig::new_parsed(output, &config)?; + let override_networks = inline_config.referenced_override_networks(&config.profile); + + let (libraries, mut outcome) = if override_networks.is_empty() { + // Single-pass: no per-test network overrides, use global network setting. + self.dispatch_network( + &evm_opts, config, - evm_opts, + evm_opts.clone(), output, filter, coverage, should_debug, decode_internal, + MultiNetworkConfig::default(), ) .await? } else { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? + // Multi-pass: run each distinct network separately and merge results. + let all_override_networks = override_networks.clone(); + let multi_pass_timer = Instant::now(); + + // Default pass: global network, runs tests without an explicit network annotation. + let (libraries, mut outcome) = self + .dispatch_network( + &evm_opts, + config.clone(), + evm_opts.clone(), + output, + filter, + coverage, + should_debug, + decode_internal, + MultiNetworkConfig { + all_override_networks: all_override_networks.clone(), + pass_network: None, + }, + ) + .await?; + + // Override passes: one per annotated network. + for &network in &override_networks { + let mut pass_evm_opts = evm_opts.clone(); + pass_evm_opts.networks = network.into(); + let (_, pass_outcome) = self + .dispatch_network( + &pass_evm_opts, + config.clone(), + pass_evm_opts.clone(), + output, + filter, + coverage, + should_debug, + decode_internal, + MultiNetworkConfig { + all_override_networks: all_override_networks.clone(), + pass_network: Some(network), + }, + ) + .await?; + merge_outcomes(&mut outcome, pass_outcome); + } + + // Print the merged summary (per-pass summaries are suppressed in `run_tests_inner`). + if !self.summary && !shell::is_json() { + sh_println!("{}", outcome.summary(multi_pass_timer.elapsed()))?; + } + if self.summary && !outcome.results.is_empty() { + let summary_report = TestSummaryReport::new(self.detailed, outcome.clone()); + sh_println!("{}", &summary_report)?; + } + + (libraries, outcome) }; if should_draw { @@ -461,6 +514,7 @@ impl TestArgs { coverage: bool, should_debug: bool, decode_internal: InternalTraceMode, + multi_network: MultiNetworkConfig, ) -> eyre::Result<(Libraries, TestOutcome)> { let verbosity = evm_opts.verbosity; let (evm_env, tx_env, fork_block) = @@ -476,6 +530,7 @@ impl TestArgs { .enable_isolation(evm_opts.isolate) .fail_fast(self.fail_fast) .set_coverage(coverage) + .with_multi_network(multi_network) .build::(output, evm_env, tx_env, evm_opts)?; let libraries = runner.libraries.clone(); @@ -483,6 +538,62 @@ impl TestArgs { Ok((libraries, outcome)) } + /// Dispatches `build_and_run_tests` to the correct network type based on `evm_opts.networks`. + #[allow(clippy::too_many_arguments)] + async fn dispatch_network( + &self, + dispatch_opts: &EvmOpts, + config: Config, + evm_opts: EvmOpts, + output: &ProjectCompileOutput, + filter: &ProjectPathsAwareFilter, + coverage: bool, + should_debug: bool, + decode_internal: InternalTraceMode, + multi_network: MultiNetworkConfig, + ) -> eyre::Result<(Libraries, TestOutcome)> { + if dispatch_opts.networks.is_tempo() { + self.build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + multi_network, + ) + .await + } else { + #[cfg(feature = "optimism")] + if dispatch_opts.networks.is_optimism() { + return self + .build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + multi_network, + ) + .await; + } + self.build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + multi_network, + ) + .await + } + } + /// Run all tests that matches the filter predicate from a test runner async fn run_tests_inner( &self, @@ -586,6 +697,11 @@ impl TestArgs { let libraries = runner.libraries.clone(); + // Capture multi-pass state before moving `runner` into the spawn task. + // In multi-pass mode the per-pass summary is suppressed; the merged summary is + // printed once by the caller after all passes complete. + let is_multi_pass = !runner.tcfg.multi_network.all_override_networks.is_empty(); + // Run tests in a streaming fashion. let (tx, rx) = channel::<(String, SuiteResult)>(); let timer = Instant::now(); @@ -643,6 +759,13 @@ impl TestArgs { let tests = &mut suite_result.test_results; let has_tests = !tests.is_empty(); + // In multi-pass (per-test network override) mode, skip suites that contributed no + // tests to this pass so we don't emit a stray blank line in the suite header or + // pollute the outcome with empty entries. + if is_multi_pass && !has_tests && suite_result.warnings.is_empty() { + continue; + } + // Clear the addresses and labels from previous test. decoder.clear_addresses(); @@ -903,17 +1026,17 @@ impl TestArgs { if let Some(gas_report) = gas_report { let finalized = gas_report.finalize(); - sh_println!("{}", &finalized)?; + sh_println!("{finalized}")?; outcome.gas_report = Some(finalized); } - if !self.summary && !shell::is_json() { + if !is_multi_pass && !self.summary && !shell::is_json() { sh_println!("{}", outcome.summary(duration))?; } - if self.summary && !outcome.results.is_empty() { + if !is_multi_pass && self.summary && !outcome.results.is_empty() { let summary_report = TestSummaryReport::new(self.detailed, outcome.clone()); - sh_println!("{}", &summary_report)?; + sh_println!("{summary_report}")?; } // Reattach the task. @@ -980,6 +1103,12 @@ 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_run) = self.fuzz_run { + fuzz_dict.insert("run".to_string(), fuzz_run.into()); + } + if let Some(fuzz_worker) = self.fuzz_worker { + fuzz_dict.insert("worker".to_string(), fuzz_worker.into()); + } if let Some(fuzz_timeout) = self.fuzz_timeout { fuzz_dict.insert("timeout".to_string(), fuzz_timeout.into()); } @@ -1023,6 +1152,29 @@ fn list( Ok(TestOutcome::empty(Some(runner.known_contracts), false)) } +/// Merges `other` into `base` by extending suite results. +/// +/// For suites that appear in both, test results are combined (function-level pass routing ensures +/// each function appears in exactly one pass, so there are no key conflicts in practice). +fn merge_outcomes(base: &mut TestOutcome, other: TestOutcome) { + for (suite_id, other_suite) in other.results { + match base.results.entry(suite_id) { + std::collections::btree_map::Entry::Vacant(e) => { + e.insert(other_suite); + } + std::collections::btree_map::Entry::Occupied(mut e) => { + let base_suite = e.get_mut(); + base_suite.test_results.extend(other_suite.test_results); + base_suite.warnings.extend(other_suite.warnings); + base_suite.duration = base_suite.duration.max(other_suite.duration); + } + } + } + if let Some(decoder) = other.last_run_decoder { + base.last_run_decoder = Some(decoder); + } +} + /// Load persisted filter (with last test run failures) from file. fn last_run_failures(config: &Config) -> Option { match fs::read_to_string(&config.test_failures_file) { @@ -1131,6 +1283,14 @@ mod tests { assert!(args.fuzz_seed.is_some()); } + #[test] + fn fuzz_run() { + let args: TestArgs = + TestArgs::parse_from(["foundry-cli", "--fuzz-run", "10", "--fuzz-worker", "2"]); + assert_eq!(args.fuzz_run, Some(10)); + assert_eq!(args.fuzz_worker, Some(2)); + } + #[test] fn extract_chain() { let test = |arg: &str, expected: Chain| { diff --git a/crates/forge/src/cmd/test/summary.rs b/crates/forge/src/cmd/test/summary.rs index f8a72272af53c..a0123e896d0bf 100644 --- a/crates/forge/src/cmd/test/summary.rs +++ b/crates/forge/src/cmd/test/summary.rs @@ -25,9 +25,9 @@ impl TestSummaryReport { impl Display for TestSummaryReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { if shell::is_json() { - writeln!(f, "{}", &self.format_json_output(&self.is_detailed, &self.outcome))?; + writeln!(f, "{}", self.format_json_output(&self.is_detailed, &self.outcome))?; } else { - writeln!(f, "\n{}", &self.format_table_output(&self.is_detailed, &self.outcome))?; + writeln!(f, "\n{}", self.format_table_output(&self.is_detailed, &self.outcome))?; } Ok(()) } diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 6c93dc03b28b5..58b11d98874ed 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -146,7 +146,7 @@ impl GasReport { impl Display for GasReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { if shell::is_json() { - writeln!(f, "{}", &self.format_json_output())?; + writeln!(f, "{}", self.format_json_output())?; } else { for (name, contract) in &self.contracts { if contract.functions.is_empty() { diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 88bbc6156c812..675f0c3e6c99c 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -27,6 +27,7 @@ use foundry_evm::{ opts::EvmOpts, traces::{InternalTraceMode, TraceMode}, }; +use foundry_evm_networks::NetworkVariant; use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; @@ -280,6 +281,25 @@ impl MultiContractRunner { } } +/// Tracks network assignment across a multi-network test run. +/// +/// When inline config specifies different networks for different tests, the runner performs one +/// pass per distinct network. This struct encodes which pass we're in so each `ContractRunner` +/// can skip tests that belong to a different pass. +/// +/// Default (empty `all_override_networks`, `None` pass) = single-pass mode, every test runs. +#[derive(Clone, Debug, Default)] +pub struct MultiNetworkConfig { + /// All networks explicitly referenced in inline config annotations across the whole suite. + /// Empty means single-pass mode (no per-test network overrides present). + pub all_override_networks: Vec, + /// The network this pass is responsible for. + /// `None` = default pass: runs tests *without* an explicit network annotation (or annotated + /// with a network not in `all_override_networks`). + /// `Some(v)` = override pass: runs only tests annotated with exactly `v`. + pub pass_network: Option, +} + /// Configuration for the test runner. /// /// This is modified after instantiation through inline config. @@ -311,6 +331,9 @@ pub struct TestRunnerConfig { pub isolation: bool, /// Whether to exit early on test failure or if test run interrupted. pub early_exit: EarlyExit, + + /// Multi-network pass configuration. Default = single-pass mode. + pub multi_network: MultiNetworkConfig, } impl TestRunnerConfig { @@ -423,6 +446,8 @@ pub struct MultiContractRunnerBuilder { pub isolation: bool, /// Whether to exit early on test failure. pub fail_fast: bool, + /// Multi-network pass configuration. + pub multi_network: MultiNetworkConfig, } impl MultiContractRunnerBuilder { @@ -437,6 +462,7 @@ impl MultiContractRunnerBuilder { isolation: Default::default(), decode_internal: Default::default(), fail_fast: false, + multi_network: Default::default(), } } @@ -470,6 +496,11 @@ impl MultiContractRunnerBuilder { self } + pub fn with_multi_network(mut self, multi_network: MultiNetworkConfig) -> Self { + self.multi_network = multi_network; + self + } + pub const fn fail_fast(mut self, fail_fast: bool) -> Self { self.fail_fast = fail_fast; self @@ -594,6 +625,7 @@ impl MultiContractRunnerBuilder { inline_config: Arc::new(InlineConfig::new_parsed(output, &self.config)?), isolation: self.isolation, early_exit: EarlyExit::new(self.fail_fast), + multi_network: self.multi_network, config: self.config, }, diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index d924c416759a2..7feaf35254636 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -109,6 +109,25 @@ impl<'a, FEN: FoundryEvmNetwork> ContractRunner<'a, FEN> { } } + /// Returns `true` if `func` should run in the current multi-network pass. + /// + /// In single-pass mode (no inline network overrides) every function passes. + /// In multi-pass mode: + /// - Default pass (`pass_network = None`): includes functions *without* an override annotation. + /// - Override pass (`pass_network = Some(v)`): includes only functions annotated with `v`. + fn function_matches_network_pass(&self, func: &Function) -> bool { + let multi = &self.mcr.tcfg.multi_network; + if multi.all_override_networks.is_empty() { + return true; + } + let profile = &self.tcfg.config.profile; + let func_network = self.mcr.inline_config.network_for(profile, self.name, &func.name); + match &multi.pass_network { + None => func_network.is_none_or(|n| !multi.all_override_networks.contains(&n)), + Some(target) => func_network.as_ref() == Some(target), + } + } + /// 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, call_setup: bool) -> TestSetup { @@ -380,6 +399,7 @@ impl<'a, FEN: FoundryEvmNetwork> ContractRunner<'a, FEN> { .abi .functions() .filter(|func| filter.matches_test_function(func)) + .filter(|func| self.function_matches_network_pass(func)) .collect::>(); debug!( "Found {} test functions out of {} in {:?}", @@ -826,7 +846,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { ); if let Some(ref progress) = progress { - progress.set_prefix(format!("{}\n{warn}\n", &func.name)); + progress.set_prefix(format!("{}\n{warn}\n", func.name)); } else { let _ = sh_warn!("{warn}"); } @@ -1052,7 +1072,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { self.cr.name, &func.name, fuzz_config.timeout, - fuzz_config.runs, + if fuzz_config.run.is_some() { 1 } else { fuzz_config.runs }, ); let state = self.build_fuzz_state(false); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 6e0acebc67225..5d8378c50c9ac 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -3000,7 +3000,7 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] +=====================================================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| 132459 | 396 | | | | | +| 132471 | 396 | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| @@ -3023,7 +3023,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "test/FallbackWithCalldataTest.sol:CounterWithFallback", "deployment": { - "gas": 132459, + "gas": 132471, "size": 396 }, "functions": { diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 0eeb3757982e9..3eebca475a781 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -577,6 +577,32 @@ forgetest_init!(can_get_evm_opts, |prj, _cmd| { } }); +// Regression test for : +// the bare `ETH_RPC_URL` env var must NOT cause `forge` commands to set +// `eth_rpc_url` (which would silently fork all `forge test` runs). +// Only `--rpc-url`, `foundry.toml`, the `FOUNDRY_ETH_RPC_URL` env var, or +// cheatcodes should configure forking. +forgetest_init!(eth_rpc_url_env_does_not_set_fork_url, |prj, _cmd| { + prj.initialize_default_contracts(); + let url = "http://127.0.0.1:8545"; + + let mut cmd = prj.forge_bin(); + cmd.arg("config") + .arg("--root") + .arg(prj.root()) + .arg("--json") + .env("ETH_RPC_URL", url) + // Make sure the figment-style env var is not set in the test environment. + .env_remove("FOUNDRY_ETH_RPC_URL"); + let output = cmd.output().unwrap(); + let stdout = String::from_utf8_lossy(&output.stdout); + let config: Config = serde_json::from_str(stdout.as_ref()).unwrap(); + assert_eq!( + config.eth_rpc_url, None, + "bare ETH_RPC_URL must not propagate to forge config (regression #14538)" + ); +}); + // checks that we can set various config values forgetest_init!(can_set_config_values, |prj, _cmd| { prj.initialize_default_contracts(); @@ -1269,6 +1295,8 @@ forgetest_init!(test_default_config, |prj, cmd| { "show_progress": false, "fuzz": { "runs": 256, + "run": null, + "worker": null, "fail_on_revert": true, "max_test_rejects": 65536, "seed": null, diff --git a/crates/forge/tests/cli/failure_assertions.rs b/crates/forge/tests/cli/failure_assertions.rs index 48a17c723b261..77d5a5e84cfbb 100644 --- a/crates/forge/tests/cli/failure_assertions.rs +++ b/crates/forge/tests/cli/failure_assertions.rs @@ -70,8 +70,13 @@ Suite result: FAILED. 0 passed; 7 failed; 0 skipped; [ELAPSED] .stdout_eq( r#"No files changed, compilation skipped ... +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectPartialRevertWrongReverterTopLevelCreate() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != [..]] testShouldFailExpectRevertNestedCreateInnerAddress() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectRevertWithBytesWrongReverterTopLevelCreate() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectRevertWrongReverterNestedCreate() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectRevertWrongReverterTopLevelCreate() ([GAS]) [FAIL: next call did not revert as expected] testShouldFailExpectRevertsNotOnImmediateNextCall() ([GAS]) -Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +Suite result: FAILED. 0 passed; 6 failed; 0 skipped; [ELAPSED] ... "#, ); diff --git a/crates/forge/tests/cli/inline_config.rs b/crates/forge/tests/cli/inline_config.rs index 04fb2369d83b0..ba01767d58b26 100644 --- a/crates/forge/tests/cli/inline_config.rs +++ b/crates/forge/tests/cli/inline_config.rs @@ -425,3 +425,107 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); }); + +// Checks that tests annotated with `forge-config: default.networks.network` run on the correct +// EVM network, and that unannotated tests run on the globally configured network. +// +// Each test makes a real call to the Tempo `TipFeeManager` precompile at +// `0xfeec000000000000000000000000000000000000` (a Tempo-only contract that exists on the +// Moderato testnet and is auto-injected by the in-memory Tempo EVM): +// +// * The default-network test asserts the precompile has no code (it does not exist on Ethereum). +// * The Tempo-network test asserts the precompile has code and `userTokens(address)` returns the +// unset zero-address sentinel, proving the Tempo network was actually selected for that test and +// the Tempo genesis state was loaded. +forgetest!(per_test_network_routing, |prj, cmd| { + prj.add_test( + "inline.sol", + r#" + address constant TIP_FEE_MANAGER = 0xfeEC000000000000000000000000000000000000; + + contract DefaultNetwork { + // No annotation -> runs on the globally selected network (Ethereum by default). + // The Tempo FeeManager precompile must NOT exist here. + function test_fee_manager_absent_on_ethereum() public view { + require( + TIP_FEE_MANAGER.code.length == 0, + "TipFeeManager should not exist on Ethereum" + ); + } + } + + contract TempoNetwork { + /// forge-config: default.networks.network = "tempo" + function test_fee_manager_callable_on_tempo() public view { + // Sentinel bytecode (0xef) is injected at every Tempo precompile address. + require( + TIP_FEE_MANAGER.code.length > 0, + "TipFeeManager must be deployed on Tempo" + ); + + // Call a Tempo-only method: `userTokens(address)` returns the user's preferred + // fee token, or the zero address when none is set. + (bool ok, bytes memory ret) = TIP_FEE_MANAGER.staticcall( + abi.encodeWithSignature("userTokens(address)", address(0)) + ); + require(ok, "userTokens call to TipFeeManager failed"); + require(ret.length == 32, "unexpected return data length"); + address token = abi.decode(ret, (address)); + require(token == address(0), "expected unset user fee token"); + } + } + + // Mixed contract: one function annotated with Tempo, one unannotated (runs on Ethereum). + contract MixedNetwork { + // No annotation -> runs on Ethereum; precompile must be absent. + function test_fee_manager_absent_on_ethereum() public view { + require( + TIP_FEE_MANAGER.code.length == 0, + "TipFeeManager should not exist on Ethereum" + ); + } + + /// forge-config: default.networks.network = "tempo" + function test_fee_manager_callable_on_tempo() public view { + require( + TIP_FEE_MANAGER.code.length > 0, + "TipFeeManager must be deployed on Tempo" + ); + + (bool ok, bytes memory ret) = TIP_FEE_MANAGER.staticcall( + abi.encodeWithSignature("userTokens(address)", address(0)) + ); + require(ok, "userTokens call to TipFeeManager failed"); + require(ret.length == 32, "unexpected return data length"); + address token = abi.decode(ret, (address)); + require(token == address(0), "expected unset user fee token"); + } + } + "#, + ); + + cmd.arg("test").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_absent_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_absent_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_callable_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_callable_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 3 test suites [ELAPSED]: 4 tests passed, 0 failed, 0 skipped (4 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/cli/lint.rs b/crates/forge/tests/cli/lint.rs index fd69907be2f09..8420e24eb3df7 100644 --- a/crates/forge/tests/cli/lint.rs +++ b/crates/forge/tests/cli/lint.rs @@ -1,4 +1,7 @@ -use forge_lint::{linter::Lint, sol::med::REGISTERED_LINTS}; +use forge_lint::{ + linter::Lint, + sol::{self, SolLint}, +}; use foundry_config::{ DenyLevel, LintSeverity, LinterConfig, SolidityErrorCode, lint::LintSpecificConfig, }; @@ -203,7 +206,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply "#]]); @@ -230,7 +233,7 @@ note[mixed-case-function]: function names should use mixedCase 9 │ function functionMIXEDCaseInfo() public {} │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCaseInfo` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function "#]]); @@ -610,7 +613,7 @@ note[mixed-case-function]: function names should use mixedCase 9 │ function functionMIXEDCaseInfo() public {} │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCaseInfo` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function "#]]); @@ -637,7 +640,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply "#]]); @@ -665,7 +668,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 13 │ uint256 result = 8 >> localValue; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift "# @@ -694,7 +697,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply "#]]).stdout_eq(str![[r#" @@ -855,7 +858,7 @@ note[unused-import]: unused imports should be removed 8 │ import { _PascalCaseInfo } from "./ContractWithLints.sol"; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import "#]]); @@ -887,7 +890,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 6 │ uint256 public CounterB_Fail_Lint; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `counterBFailLint` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable "#]]); @@ -992,7 +995,7 @@ forgetest!(lint_json_output_no_ansi_escape_codes, |prj, cmd| { ], "children": [ { - "message": "https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic", + "message": "https://getfoundry.sh/forge/linting/unwrapped-modifier-logic", "code": null, "level": "help", "spans": [], @@ -1048,7 +1051,7 @@ forgetest!(lint_json_output_no_ansi_escape_codes, |prj, cmd| { "rendered": null } ], - "rendered": "note[unwrapped-modifier-logic]: wrap modifier logic to reduce code size\n\nhelp: wrap modifier logic to reduce code size\n 9 + _onlyOwner();\n10 + _;\n11 + }\n12 + \n13 + function _onlyOwner() internal {\n14 + require(isOwner[msg.sender], \"Not owner\");\n15 + require(msg.sender != address(0), \"Zero address\");\n16 + }\n ╭▸ src/UnwrappedModifierTest.sol:8:13\n │\n 8 │ ┏ modifier onlyOwner() {\n 9 │ ┃ require(isOwner[msg.sender], \"Not owner\");\n10 │ ┃ require(msg.sender != address(0), \"Zero address\");\n11 │ ┃ _;\n12 │ ┃ }\n │ ┗━━━━━━━━━━━━━┛\n │\n ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic\n ╭╴\n 8 ± modifier onlyOwner() {\n ╰╴\n" + "rendered": "note[unwrapped-modifier-logic]: wrap modifier logic to reduce code size\n\nhelp: wrap modifier logic to reduce code size\n 9 + _onlyOwner();\n10 + _;\n11 + }\n12 + \n13 + function _onlyOwner() internal {\n14 + require(isOwner[msg.sender], \"Not owner\");\n15 + require(msg.sender != address(0), \"Zero address\");\n16 + }\n ╭▸ src/UnwrappedModifierTest.sol:8:13\n │\n 8 │ ┏ modifier onlyOwner() {\n 9 │ ┃ require(isOwner[msg.sender], \"Not owner\");\n10 │ ┃ require(msg.sender != address(0), \"Zero address\");\n11 │ ┃ _;\n12 │ ┃ }\n │ ┗━━━━━━━━━━━━━┛\n │\n ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic\n ╭╴\n 8 ± modifier onlyOwner() {\n ╰╴\n" } "#]], ); @@ -1129,46 +1132,46 @@ Warning: Key `deny_warnings` is being deprecated in favor of `deny = warnings`. #[tokio::test] async fn ensure_lint_rule_docs() { - const FOUNDRY_BOOK_LINT_PAGE_URL: &str = "https://book.getfoundry.sh/forge/linting"; - - // Fetch the content of the lint reference - let content = match reqwest::get(FOUNDRY_BOOK_LINT_PAGE_URL).await { - Ok(resp) => { - assert!( - resp.status().is_success(), - "Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}). Status: {status}", - status = resp.status() - ); - match resp.text().await { - Ok(text) => text, - Err(e) => { - panic!("Failed to read response text: {e}"); - } + let client = reqwest::Client::new(); + let mut failures = Vec::new(); + + for lint in registered_lints() { + let url = lint.help(); + let response = match client.get(url).send().await { + Ok(response) => response, + Err(err) => { + failures.push(format!("{} ({url}) could not be fetched: {err}", lint.id())); + continue; } + }; + + if !response.status().is_success() { + failures.push(format!("{} ({url}) returned HTTP {}", lint.id(), response.status())); + continue; } - Err(e) => { - panic!("Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}): {e}",); - } - }; - // Ensure no missing lints - let mut missing_lints = Vec::new(); - for lint in REGISTERED_LINTS { + let content = match response.text().await { + Ok(content) => content.to_lowercase(), + Err(err) => { + failures + .push(format!("{} ({url}) response body could not be read: {err}", lint.id())); + continue; + } + }; + let selector = lint.id().to_lowercase(); let selector_with_space = selector.replace('-', " "); - if !content.to_lowercase().contains(&selector) - && !content.to_lowercase().contains(&selector_with_space) - { - missing_lints.push(lint.id()); + if !content.contains(&selector) && !content.contains(&selector_with_space) { + failures.push(format!("{} ({url}) did not mention the lint id", lint.id())); } } - if !missing_lints.is_empty() { + if !failures.is_empty() { let mut msg = String::from( - "Foundry Book lint validation failed. The following lints must be added to the docs:\n", + "Foundry Book lint validation failed. The following lint pages are missing or invalid:\n", ); - for lint in missing_lints { - msg.push_str(&format!(" - {lint}\n")); + for failure in failures { + msg.push_str(&format!(" - {failure}\n")); } msg.push_str("Please open a PR: https://github.com/foundry-rs/book"); panic!("{msg}"); @@ -1177,11 +1180,21 @@ async fn ensure_lint_rule_docs() { #[test] fn ensure_no_privileged_lint_id() { - for lint in REGISTERED_LINTS { + for lint in registered_lints() { assert_ne!(lint.id(), "all", "lint-id 'all' is reserved. Please use a different id"); } } +fn registered_lints() -> impl Iterator { + sol::high::REGISTERED_LINTS + .iter() + .chain(sol::med::REGISTERED_LINTS) + .chain(sol::low::REGISTERED_LINTS) + .chain(sol::info::REGISTERED_LINTS) + .chain(sol::gas::REGISTERED_LINTS) + .chain(sol::codesize::REGISTERED_LINTS) +} + // forgetest!(dependency_warnings_do_not_affect_lint_exit_code, |prj, cmd| { // Library with code that triggers a solc warning (unused local variable) @@ -1265,3 +1278,195 @@ contract OldContract { "# ]]); }); + +const PRAGMA_INCONSISTENT_ALPHA: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract Alpha {} +"#; + +const PRAGMA_INCONSISTENT_BETA: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract Beta {} +"#; + +forgetest!(pragma_inconsistent_cross_file, |prj, cmd| { + prj.add_source("Alpha", PRAGMA_INCONSISTENT_ALPHA); + prj.add_source("Beta", PRAGMA_INCONSISTENT_BETA); + + cmd.arg("lint").args(["--only-lint", "pragma-inconsistent"]).assert_success().stderr_eq(str![ + [r#" +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + + +"#] + ]); +}); + +const PRAGMA_EXACT_A: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract A {} +"#; + +const PRAGMA_EXACT_B: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract B {} +"#; + +const PRAGMA_EXACT_C: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract C {} +"#; + +const PRAGMA_CARET_A: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract A {} +"#; + +const PRAGMA_CARET_B: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract B {} +"#; + +const PRAGMA_CARET_C: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract C {} +"#; + +const NO_PRAGMA_C: &str = r#" +// SPDX-License-Identifier: MIT + +contract C {} +"#; + +// Multiple files all using the exact same pragma must NOT warn. +forgetest!(pragma_inconsistent_consistent_exact_no_warning, |prj, cmd| { + prj.add_source("A", PRAGMA_EXACT_A); + prj.add_source("B", PRAGMA_EXACT_B); + prj.add_source("C", PRAGMA_EXACT_C); + + cmd.arg("lint") + .args(["--only-lint", "pragma-inconsistent"]) + .assert_success() + .stderr_eq(str![[r#""#]]); +}); + +// Multiple files all using the exact same caret pragma must NOT warn. +forgetest!(pragma_inconsistent_consistent_caret_no_warning, |prj, cmd| { + prj.add_source("A", PRAGMA_CARET_A); + prj.add_source("B", PRAGMA_CARET_B); + + cmd.arg("lint") + .args(["--only-lint", "pragma-inconsistent"]) + .assert_success() + .stderr_eq(str![[r#""#]]); +}); + +// A single file in the project cannot conflict with itself. +forgetest!(pragma_inconsistent_single_file_no_warning, |prj, cmd| { + prj.add_source("A", PRAGMA_CARET_A); + + cmd.arg("lint") + .args(["--only-lint", "pragma-inconsistent"]) + .assert_success() + .stderr_eq(str![[r#""#]]); +}); + +// Even files that share a requirement still emit when ANY other variant exists. +// Two files with `0.8.20` plus one file with `^0.8.20` => 3 emits total. +forgetest!(pragma_inconsistent_duplicates_among_conflict, |prj, cmd| { + prj.add_source("A", PRAGMA_EXACT_A); + prj.add_source("B", PRAGMA_EXACT_B); + prj.add_source("C", PRAGMA_CARET_C); + + cmd.arg("lint").args(["--only-lint", "pragma-inconsistent"]).assert_success().stderr_eq(str![ + [r#" +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + + +"#] + ]); +}); + +// Files without a `pragma solidity` directive must not affect the conflict computation. +// Note: `add_raw_source` is used here to bypass the helper that would otherwise inject a default +// `pragma solidity =;` for files that omit one. +forgetest!(pragma_inconsistent_files_without_pragma, |prj, cmd| { + prj.add_raw_source("A", PRAGMA_EXACT_A); + prj.add_raw_source("B", PRAGMA_CARET_B); + // C has no pragma at all; should be ignored by the cross-file check. + prj.add_raw_source("C", NO_PRAGMA_C); + + cmd.arg("lint").args(["--only-lint", "pragma-inconsistent"]).assert_success().stderr_eq(str![ + [r#" +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + + +"#] + ]); +}); diff --git a/crates/forge/tests/cli/lint/geiger.rs b/crates/forge/tests/cli/lint/geiger.rs index faecfb212fb90..202866e83e35f 100644 --- a/crates/forge/tests/cli/lint/geiger.rs +++ b/crates/forge/tests/cli/lint/geiger.rs @@ -21,7 +21,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 9 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode Error: aborting due to 1 linter note(s) ... @@ -52,7 +52,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 9 │ bytes memory stuff = vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode Error: aborting due to 1 linter note(s) ... @@ -84,7 +84,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 9 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations [FILE]:10:20 @@ -92,7 +92,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 10 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations [FILE]:11:20 @@ -100,7 +100,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 11 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode Error: aborting due to 3 linter note(s) ... diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 242a0ebb4267f..031d80f0cf071 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -3241,7 +3241,7 @@ contract CounterScript is Script { error: the following required arguments were not provided: --broadcast -Usage: [..] script --broadcast --verify --rpc-url [ARGS]... +Usage: [..] script --broadcast --verify --rpc-url [ARGS]... For more information, try '--help'. diff --git a/crates/forge/tests/cli/test_cmd/fuzz.rs b/crates/forge/tests/cli/test_cmd/fuzz.rs index fefefb30d9b15..454b014a6e1bc 100644 --- a/crates/forge/tests/cli/test_cmd/fuzz.rs +++ b/crates/forge/tests/cli/test_cmd/fuzz.rs @@ -1,4 +1,5 @@ use alloy_primitives::U256; +use foundry_evm::fuzz::BaseCounterExample; use foundry_test_utils::{TestCommand, forgetest_init, str}; use regex::Regex; @@ -845,6 +846,8 @@ forgetest_init!(test_fuzz_random_uint_varies_across_runs, |prj, cmd| { prj.add_test( "RandomFuzzTest.t.sol", r#" +pragma solidity >=0.8.0; + import {Test} from "forge-std/Test.sol"; contract RandomFuzzTest is Test { @@ -868,3 +871,145 @@ Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) ... "#]]); }); + +forgetest_init!(test_fuzz_run_replays_random_uint_failure, |prj, cmd| { + prj.add_test( + "RandomFuzzTest.t.sol", + r#" +pragma solidity >=0.8.0; + +import {Test} from "forge-std/Test.sol"; + +contract RandomFuzzTest is Test { + function testFuzz_randomUint_shouldFail(uint256) public { + uint256 rand = vm.randomUint(0, 4); + assertTrue(rand != 0, "hit value 0"); + } +} + "#, + ); + + let expected_output = str![[r#" +... +Ran 1 test for test/RandomFuzzTest.t.sol:RandomFuzzTest +[FAIL: hit value 0; counterexample: [..]] testFuzz_randomUint_shouldFail(uint256) (runs: [..], [AVG_GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"#]]; + + cmd.args(["test", "--fuzz-seed", "1", "--mt", "testFuzz_randomUint_shouldFail", "-j1"]) + .assert_failure() + .stdout_eq(expected_output.clone()); + + let failure_file = + prj.root().join("cache/fuzz/failures/RandomFuzzTest/testFuzz_randomUint_shouldFail"); + let persisted_failure: BaseCounterExample = + serde_json::from_slice(&std::fs::read(&failure_file).unwrap()).unwrap(); + assert_eq!(persisted_failure.fuzz.seed, Some(U256::from(1))); + assert_eq!(persisted_failure.fuzz.worker, Some(0)); + let fuzz_run = persisted_failure.fuzz.run.unwrap().to_string(); + let fuzz_worker = persisted_failure.fuzz.worker.unwrap().to_string(); + + cmd.forge_fuse() + .args([ + "test", + "--fuzz-seed", + "1", + "--fuzz-run", + &fuzz_run, + "--fuzz-worker", + &fuzz_worker, + "--mt", + "testFuzz_randomUint_shouldFail", + "-j1", + ]) + .assert_failure() + .stdout_eq(expected_output.clone()); + + cmd.forge_fuse().args(["test", "--rerun", "-j1"]).assert_failure().stdout_eq(expected_output); +}); + +forgetest_init!(test_fuzz_rerun_replays_random_uint_failure_without_seed, |prj, cmd| { + prj.add_test( + "RandomFuzzTest.t.sol", + r#" +pragma solidity >=0.8.0; + +import {Test} from "forge-std/Test.sol"; + +contract RandomFuzzTest is Test { + error Random(uint256 value); + + function testFuzz_randomUint_shouldFail(uint256) public { + revert Random(vm.randomUint()); + } +} + "#, + ); + + let expected_output = str![[r#" +... +Ran 1 test for test/RandomFuzzTest.t.sol:RandomFuzzTest +[FAIL: Random([..]); counterexample: [..]] testFuzz_randomUint_shouldFail(uint256) (runs: [..], [AVG_GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +Tip: Run `forge test --rerun` to retry only the 1 failed test + +[SEED] (use `--fuzz-seed` to reproduce) + +"#]]; + + let assert = cmd + .args(["test", "--mt", "testFuzz_randomUint_shouldFail", "-j1"]) + .assert_failure() + .stdout_eq(expected_output.clone()); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let reason = random_failure_reason(&stdout); + + let failure_file = + prj.root().join("cache/fuzz/failures/RandomFuzzTest/testFuzz_randomUint_shouldFail"); + let persisted_failure: BaseCounterExample = + serde_json::from_slice(&std::fs::read(&failure_file).unwrap()).unwrap(); + let fuzz_seed = format!("{:#x}", persisted_failure.fuzz.seed.unwrap()); + let fuzz_run = persisted_failure.fuzz.run.unwrap().to_string(); + let fuzz_worker = persisted_failure.fuzz.worker.unwrap().to_string(); + + let assert = cmd + .forge_fuse() + .args([ + "test", + "--fuzz-seed", + &fuzz_seed, + "--fuzz-run", + &fuzz_run, + "--fuzz-worker", + &fuzz_worker, + "--mt", + "testFuzz_randomUint_shouldFail", + "-j1", + ]) + .assert_failure() + .stdout_eq(expected_output.clone()); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + assert_eq!(random_failure_reason(&stdout), reason, "{stdout}"); + + let assert = cmd + .forge_fuse() + .args(["test", "--rerun", "-j1"]) + .assert_failure() + .stdout_eq(expected_output); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + assert_eq!(random_failure_reason(&stdout), reason, "{stdout}"); + + let assert = cmd.forge_fuse().args(["test", "--rerun", "-j1"]).assert_failure(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + assert_eq!(random_failure_reason(&stdout), reason, "{stdout}"); +}); + +fn random_failure_reason(stdout: &str) -> String { + Regex::new(r"\[FAIL: (Random\([^)]+\))") + .unwrap() + .captures(stdout) + .unwrap_or_else(|| panic!("{stdout}"))[1] + .to_string() +} diff --git a/crates/forge/tests/cli/test_cmd/repros.rs b/crates/forge/tests/cli/test_cmd/repros.rs index 32bfe6a98a9fd..3803385b496ab 100644 --- a/crates/forge/tests/cli/test_cmd/repros.rs +++ b/crates/forge/tests/cli/test_cmd/repros.rs @@ -783,6 +783,66 @@ ParserError: Source "Missing.sol" not found: File not found. Searched the follow "#]]); }); +// https://github.com/foundry-rs/foundry/issues/10463 +forgetest_init!(issue_10463, |prj, cmd| { + prj.add_test( + "Issue10463.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Issue10463Test is Test { + event Foo(); + + error CustomError(uint256 code); + + function revertingBefore(bool shouldRevert) external { + if (shouldRevert) revert(); + emit Foo(); + } + + function revertingWithReason() external pure { + revert("revert reason"); + } + + function revertingWithCustomError() external pure { + revert CustomError(42); + } + + function testExpectEmitPreservesRevertWhenCallRevertsBeforeLog() public { + vm.expectEmit(); + emit Foo(); + + this.revertingBefore(true); + } + + function testExpectEmitPreservesRevertReason() public { + vm.expectEmit(); + emit Foo(); + + this.revertingWithReason(); + } + + function testExpectEmitPreservesCustomError() public { + vm.expectEmit(); + emit Foo(); + + this.revertingWithCustomError(); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 3 tests for test/Issue10463.t.sol:Issue10463Test +[FAIL: CustomError(42)] testExpectEmitPreservesCustomError() ([GAS]) +[FAIL: revert reason] testExpectEmitPreservesRevertReason() ([GAS]) +[FAIL: EvmError: Revert] testExpectEmitPreservesRevertWhenCallRevertsBeforeLog() ([GAS]) +Suite result: FAILED. 0 passed; 3 failed; 0 skipped; [ELAPSED] +... +"#]]); +}); + // https://github.com/foundry-rs/foundry/issues/12803 // Test gas underflow prevention on Cancun (no EIP-7702 gas floor) forgetest_init!(issue_12803_cancun, |prj, cmd| { diff --git a/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol b/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol index 7e482c6673155..838183a1b0b5e 100644 --- a/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol +++ b/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol @@ -233,6 +233,63 @@ contract ExpectRevertWithReverterFailureTest is DSTest { aContract.doNotRevert(); aContract.callAndRevert(); } + + // + // Regression: must fail because 0xdead is not the actual reverter when a + // top-level CREATE constructor reverts directly. + function testShouldFailExpectRevertWrongReverterTopLevelCreate() public { + vm.expectRevert(address(0xdead)); + new DContract(); + } + + // + // Regression: must fail because the reverter address argument is enforced + // even when an exact-bytes pattern is also supplied for a top-level CREATE. + function testShouldFailExpectRevertWithBytesWrongReverterTopLevelCreate() public { + vm.expectRevert(abi.encodePacked("Reverted by DContract"), address(0xdead)); + new DContract(); + } + + // + // Regression: must fail because the reverter address argument is enforced + // for `expectPartialRevert(bytes4, address)` against a top-level CREATE. + function testShouldFailExpectPartialRevertWrongReverterTopLevelCreate() public { + vm.expectPartialRevert(bytes4(keccak256("Error(string)")), address(0xdead)); + new DContract(); + } + + // + // Regression: must fail when the innermost reverting frame is a nested + // CREATE and the reverter address argument does not match the would-be + // deployed address of the failed deployment. + function testShouldFailExpectRevertWrongReverterNestedCreate() public { + vm.expectRevert(address(0xdead)); + new NestedDContractCreator(); + } + + // + // Regression: documents the intended semantics for nested CREATEs — the + // matched reverter is the *outer* would-be-deployed address (the contract + // whose deployment failed), NOT the innermost reverting CREATE's address. + // Supplying the inner address must fail. + function testShouldFailExpectRevertNestedCreateInnerAddress() public { + // Outer = NestedDContractCreator at this contract's next nonce. + // Inner = DContract created from inside the outer constructor (deployer + // is the outer, nonce 1). + address outer = + vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + address inner = vm.computeCreateAddress(outer, 1); + vm.expectRevert(inner); + new NestedDContractCreator(); + } +} + +// Used by `testShouldFailExpectRevertWrongReverterNestedCreate`: a contract whose +// constructor directly creates another contract that reverts. +contract NestedDContractCreator { + constructor() { + new DContract(); + } } contract ExpectRevertCountFailureTest is DSTest { diff --git a/crates/lint/Cargo.toml b/crates/lint/Cargo.toml index 87864721432d9..589a0a5069e37 100644 --- a/crates/lint/Cargo.toml +++ b/crates/lint/Cargo.toml @@ -24,3 +24,7 @@ eyre.workspace = true heck.workspace = true rayon.workspace = true thiserror.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/lint/README.md b/crates/lint/README.md index e7c6471555da3..9d5dad0c0272e 100644 --- a/crates/lint/README.md +++ b/crates/lint/README.md @@ -17,11 +17,14 @@ It helps enforce best practices and improve code quality within Foundry projects - `divide-before-multiply`: Warns against performing division before multiplication in the same expression, which can cause precision loss. - `incorrect-erc20-interface`: Flags ERC20 interfaces and implementations with non-compliant function signatures. - `incorrect-erc721-interface`: Flags ERC721 interfaces and implementations with non-compliant function signatures. + - `tx-origin`: Flags use of `tx.origin` in authorization-like predicates. - `unsafe-typecast`: Typecasts that can truncate values should be checked. - **Low Severity:** - `block-timestamp`: Warns when `block.timestamp` is used in a comparison, as it may be manipulated by validators. + - `missing-zero-check`: Address parameter is used in a state write or value transfer without a zero-address check. - **Informational / Style Guide:** - `boolean-equal`: Boolean comparisons to constants should be simplified. + - `too-many-digits`: Numeric literals with 5+ consecutive zeros are error-prone. - `pascal-case-struct`: Flags for struct names not adhering to `PascalCase`. - `mixed-case-function`: Flags for function names not adhering to `mixedCase`. - `mixed-case-variable`: Flags for mutable variable names not adhering to `mixedCase`. @@ -31,10 +34,15 @@ It helps enforce best practices and improve code quality within Foundry projects - `unaliased-plain-import`: Use named imports `{A, B}` or alias `import ".." as X`. - `named-struct-fields`: Prefer initializing structs with named fields. - `unsafe-cheatcode`: Usage of unsafe cheatcodes that can perform dangerous operations. + - `multi-contract-file`: Prefer having only one contract, interface, or library per file. + - `interface-file-naming`: Interface file names should be prefixed with `I`. + - `interface-naming`: Interface names should be prefixed with `I`. + - `pragma-inconsistent`: Flags projects whose source files declare different Solidity pragma version requirements. - **Gas Optimizations:** - `asm-keccak256`: Recommends using inline assembly for `keccak256` for potential gas savings. - `could-be-immutable`: Recommends declaring constructor-only state variables as `immutable`. - `custom-errors`: Recommends using custom errors instead of strings and plain reverts for potential gas savings. + - `unused-state-variables`: State variables that are never used should be removed. - **Code Size:** - `unwrapped-modifier-logic`: Recommends wrapping modifier logic to reduce contract code size. diff --git a/crates/lint/docs/README.md b/crates/lint/docs/README.md new file mode 100644 index 0000000000000..5eb4110a92e57 --- /dev/null +++ b/crates/lint/docs/README.md @@ -0,0 +1,52 @@ +# Forge lint documentation + +This directory contains one markdown file per registered `forge-lint` rule. Each file is referenced +by the lint's `help` URL (`https://getfoundry.sh/forge/linting/`) and is consumed by the +[Foundry book](https://github.com/foundry-rs/book) to render the lint reference page. + +## Adding a new lint + +When you add a new lint with `declare_forge_lint!`, you **must** also add a documentation file at +`crates/lint/docs/.md`. The presence of the file is enforced by the +`registered_lints_have_docs` unit test in [`crates/lint/src/sol/mod.rs`](../src/sol/mod.rs). + +Use [`_template.md`](./_template.md) as a starting point. + +## File structure + +Each lint doc file should follow this structure: + +```markdown +# + +**Severity**: `` +**ID**: `` + +A one-paragraph description of what this lint detects and why it matters. + +## What it does + +Explain precisely what the lint flags. + +## Why is this bad? + +Explain the impact (security, correctness, gas, readability). + +## Example + +### Bad + +```solidity +// triggering example +``` + +### Good + +```solidity +// non-triggering, recommended example +``` + +## Configuration + +Document any inline-config or `foundry.toml` options that affect this lint, if any. +``` diff --git a/crates/lint/docs/_template.md b/crates/lint/docs/_template.md new file mode 100644 index 0000000000000..41c735a0ba579 --- /dev/null +++ b/crates/lint/docs/_template.md @@ -0,0 +1,28 @@ +# + +**Severity**: `` +**ID**: `` + +One-paragraph summary of what this lint detects and why it matters. + +## What it does + +Explain precisely what the lint flags. + +## Why is this bad? + +Explain the impact (security, correctness, gas, readability). + +## Example + +### Bad + +```solidity +// triggering example +``` + +### Good + +```solidity +// non-triggering, recommended example +``` diff --git a/crates/lint/docs/asm-keccak256.md b/crates/lint/docs/asm-keccak256.md new file mode 100644 index 0000000000000..4678cfe9f8d12 --- /dev/null +++ b/crates/lint/docs/asm-keccak256.md @@ -0,0 +1,42 @@ +# Inefficient keccak256 call + +**Severity**: `Gas` +**ID**: `asm-keccak256` + +Flags calls to the high-level `keccak256(...)` builtin that can be cheaply rewritten with inline +assembly. + +## What it does + +Reports `keccak256(arg)` calls and (when possible) emits a fix suggestion that uses inline +assembly to compute the hash directly, avoiding the overhead of the high-level call. + +## Why is this bad? + +The high-level `keccak256` call performs additional memory management and ABI encoding compared +to a direct `keccak256(ptr, len)` opcode invocation. In hot paths the difference is visible in +gas reports. + +## Example + +### Bad + +```solidity +bytes32 h = keccak256(abi.encodePacked(a, b)); +``` + +### Good + +```solidity +bytes32 h; +assembly ("memory-safe") { + let m := mload(0x40) + mstore(m, a) + mstore(add(m, 0x20), b) + h := keccak256(m, 0x40) +} +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/block-timestamp.md b/crates/lint/docs/block-timestamp.md new file mode 100644 index 0000000000000..a51b55ff5d8cc --- /dev/null +++ b/crates/lint/docs/block-timestamp.md @@ -0,0 +1,44 @@ +# Use of block.timestamp in comparisons + +**Severity**: `Low` +**ID**: `block-timestamp` + +Flags use of `block.timestamp` as an operand of a comparison, where its value can be slightly +manipulated by the block proposer. + +## What it does + +Reports any comparison expression (`<`, `<=`, `>`, `>=`, `==`, `!=`) that directly or +transitively reads `block.timestamp`. + +## Why is this bad? + +Block proposers can adjust `block.timestamp` within a small window (a few seconds). This is +usually harmless, but for short-window logic — auctions ending, randomness, time-locked +withdrawals — a few seconds of manipulation can be enough for an attacker to capture value. + +Using `block.timestamp` for general scheduling (hours/days) is fine; what's risky is fine-grained +timing and treating timestamps as a source of randomness. + +## Example + +### Bad + +```solidity +function settle() external { + require(block.timestamp >= auctionEnd, "auction ongoing"); + // ... +} +``` + +### Good + +```solidity +// Prefer block numbers for tight windows, or accept a clearly large grace period. +require(block.number >= endBlock, "auction ongoing"); +``` + +## Notes + +This lint is intentionally conservative: not every flagged comparison is exploitable. Review +each occurrence in context. diff --git a/crates/lint/docs/boolean-cst.md b/crates/lint/docs/boolean-cst.md new file mode 100644 index 0000000000000..f5c65dfec2789 --- /dev/null +++ b/crates/lint/docs/boolean-cst.md @@ -0,0 +1,37 @@ +# Misuse of a boolean constant + +**Severity**: `Med` +**ID**: `boolean-cst` + +Flags expressions where a boolean constant (`true`/`false`) is used as a control-flow condition +or operand of a boolean operator, which usually indicates dead code or a leftover debug toggle. + +## What it does + +Reports `if (true)`, `if (false)`, `while (true)` outside of intentional infinite loops, and +boolean operators (`&&`, `||`) where one side is a literal `true`/`false`. + +## Why is this bad? + +A literal boolean as a condition makes the surrounding branch dead, hides logic errors, or +preserves a forgotten debug shortcut that bypasses real checks. + +## Example + +### Bad + +```solidity +if (true) { // always taken + doSomething(); +} +require(condition && true, "unreachable"); // 'true' is redundant +``` + +### Good + +```solidity +if (condition) { + doSomething(); +} +require(condition, "..."); +``` diff --git a/crates/lint/docs/boolean-equal.md b/crates/lint/docs/boolean-equal.md new file mode 100644 index 0000000000000..9397003b039b4 --- /dev/null +++ b/crates/lint/docs/boolean-equal.md @@ -0,0 +1,34 @@ +# Boolean comparison to a constant + +**Severity**: `Info` +**ID**: `boolean-equal` + +Flags expressions of the form `x == true`, `x == false`, `x != true`, `x != false`, which can be +simplified. + +## What it does + +Reports any equality comparison between a boolean expression and a literal `true` or `false`. + +## Why is this bad? + +Comparing a boolean to a boolean literal is redundant and harms readability. Use the boolean +expression directly (or its negation). + +## Example + +### Bad + +```solidity +if (paused == true) revert(); +if (paused == false) doSomething(); +require(ok != false, "fail"); +``` + +### Good + +```solidity +if (paused) revert(); +if (!paused) doSomething(); +require(ok, "fail"); +``` diff --git a/crates/lint/docs/could-be-immutable.md b/crates/lint/docs/could-be-immutable.md new file mode 100644 index 0000000000000..bda1de6379955 --- /dev/null +++ b/crates/lint/docs/could-be-immutable.md @@ -0,0 +1,42 @@ +# State variable could be immutable + +**Severity**: `Gas` +**ID**: `could-be-immutable` + +Flags state variables that are assigned only in the constructor and never written to afterward — +making them eligible to be declared `immutable`. + +## What it does + +Reports each non-`constant`, non-`immutable` state variable whose only writes occur in the +constructor (or in initialization at declaration time). + +## Why is this bad? + +`immutable` state variables are stored in the deployed bytecode rather than in storage, eliminating +an `SLOAD` per access and saving substantial gas across the contract's lifetime. Declaring such +variables `immutable` also expresses intent and prevents future writes. + +## Example + +### Bad + +```solidity +contract C { + address owner; + constructor() { owner = msg.sender; } +} +``` + +### Good + +```solidity +contract C { + address immutable OWNER; + constructor() { OWNER = msg.sender; } +} +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/custom-errors.md b/crates/lint/docs/custom-errors.md new file mode 100644 index 0000000000000..9e01e01d593e9 --- /dev/null +++ b/crates/lint/docs/custom-errors.md @@ -0,0 +1,45 @@ +# Prefer custom errors over revert strings + +**Severity**: `Gas` +**ID**: `custom-errors` + +Flags `require(cond, "message")`, `revert("message")`, and `revert()` calls; suggests replacing +them with a `revert CustomError(...)`. + +## What it does + +Reports `require` calls whose second argument is a string literal, and `revert(...)` calls that +are either bare or have a string-literal argument. + +## Why is this bad? + +Custom errors: +- cost less gas than encoding/decoding a string, +- can carry typed parameters for richer diagnostics, +- shrink contract bytecode (string constants live in code). + +Solidity 0.8.4+ supports custom errors natively. + +## Example + +### Bad + +```solidity +require(amount > 0, "amount must be > 0"); +revert("not authorized"); +revert(); +``` + +### Good + +```solidity +error AmountZero(); +error NotAuthorized(); + +if (amount == 0) revert AmountZero(); +if (!authorized) revert NotAuthorized(); +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/divide-before-multiply.md b/crates/lint/docs/divide-before-multiply.md new file mode 100644 index 0000000000000..f082bef19a1bd --- /dev/null +++ b/crates/lint/docs/divide-before-multiply.md @@ -0,0 +1,32 @@ +# Divide before multiply + +**Severity**: `Med` +**ID**: `divide-before-multiply` + +Flags arithmetic expressions where division is performed before multiplication, which can cause +unintended precision loss in integer arithmetic. + +## What it does + +Warns on expressions of the form `(a / b) * c` (or equivalent shapes), where the integer division +truncates before the result is multiplied. + +## Why is this bad? + +Solidity's integer division truncates toward zero. Performing `(a / b) * c` discards the remainder +of `a / b` before scaling, while `(a * c) / b` preserves precision. This pattern frequently +manifests as fee/share/yield miscalculations. + +## Example + +### Bad + +```solidity +uint256 share = (amount / total) * weight; // truncates first, then scales +``` + +### Good + +```solidity +uint256 share = (amount * weight) / total; // preserves precision +``` diff --git a/crates/lint/docs/erc20-unchecked-transfer.md b/crates/lint/docs/erc20-unchecked-transfer.md new file mode 100644 index 0000000000000..d7d053e020cca --- /dev/null +++ b/crates/lint/docs/erc20-unchecked-transfer.md @@ -0,0 +1,43 @@ +# Unchecked ERC20 transfer return value + +**Severity**: `High` +**ID**: `erc20-unchecked-transfer` + +Flags calls to ERC20 `transfer` and `transferFrom` where the boolean return value is ignored. + +## What it does + +Warns when a function with the same signature as +`transfer(address,uint256)` or `transferFrom(address,address,uint256)` and a `bool` return type is +invoked but the result is not checked. + +## Why is this bad? + +The ERC20 spec allows tokens to signal failure by returning `false` instead of reverting. Ignoring +the return value lets a "failed" transfer go unnoticed, allowing accounting to drift and creating +common DeFi exploits. Use a wrapper such as OpenZeppelin's `SafeERC20` or check the boolean +explicitly. + +## Example + +### Bad + +```solidity +token.transfer(to, amount); +token.transferFrom(from, to, amount); +``` + +### Good + +```solidity +require(token.transfer(to, amount), "transfer failed"); +require(token.transferFrom(from, to, amount), "transferFrom failed"); + +// or use SafeERC20 +SafeERC20.safeTransfer(token, to, amount); +``` + +## Notes + +This lint can produce false positives when the callee does not strictly conform to the ERC20 +interface (e.g. tokens that revert on failure rather than returning `false`). diff --git a/crates/lint/docs/incorrect-erc20-interface.md b/crates/lint/docs/incorrect-erc20-interface.md new file mode 100644 index 0000000000000..65fb8313c205f --- /dev/null +++ b/crates/lint/docs/incorrect-erc20-interface.md @@ -0,0 +1,42 @@ +# Incorrect ERC20 interface + +**Severity**: `Med` +**ID**: `incorrect-erc20-interface` + +Flags interfaces or contracts whose function signatures match an ERC20 method by name and +parameters but use the wrong return type. + +## What it does + +For each function whose name and parameter types match a canonical ERC20 method +(`totalSupply`, `balanceOf`, `transfer`, `transferFrom`, `approve`, `allowance`), the lint checks +that the return type matches the spec. A mismatch is reported. + +## Why is this bad? + +Tokens that diverge from the ERC20 spec break composability with the wider ecosystem (DEXes, +lending protocols, multisigs) and are a common source of integration bugs and exploits. + +## Example + +### Bad + +```solidity +interface IBadERC20 { + function balanceOf(address) external view returns (bool); // should be uint256 + function transfer(address, uint256) external; // should return bool +} +``` + +### Good + +```solidity +interface IERC20 { + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); +} +``` diff --git a/crates/lint/docs/incorrect-erc721-interface.md b/crates/lint/docs/incorrect-erc721-interface.md new file mode 100644 index 0000000000000..4803afdde7cc1 --- /dev/null +++ b/crates/lint/docs/incorrect-erc721-interface.md @@ -0,0 +1,48 @@ +# Incorrect ERC721 interface + +**Severity**: `Med` +**ID**: `incorrect-erc721-interface` + +Flags interfaces or contracts whose function signatures match an ERC721 (or ERC165) method by +name and parameters but use the wrong return type. + +## What it does + +For each function whose name and parameter types match a canonical ERC721/ERC165 method +(`balanceOf`, `ownerOf`, `safeTransferFrom`, `transferFrom`, `approve`, `setApprovalForAll`, +`getApproved`, `isApprovedForAll`, `supportsInterface`), the lint checks that the return type +matches the spec. A mismatch is reported. + +## Why is this bad? + +Non-conforming NFT contracts break marketplaces, indexers, and any protocol that relies on the +ERC721 spec. A wrong return type often compiles and deploys silently but causes integration +failures at runtime. + +## Example + +### Bad + +```solidity +interface IBadERC721 { + function balanceOf(address) external view returns (bool); // should be uint256 + function ownerOf(uint256) external view returns (bool); // should be address + function supportsInterface(bytes4) external view returns (uint256); // should be bool +} +``` + +### Good + +```solidity +interface IERC721 { + function balanceOf(address owner) external view returns (uint256); + function ownerOf(uint256 tokenId) external view returns (address); + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function transferFrom(address from, address to, uint256 tokenId) external; + function approve(address to, uint256 tokenId) external; + function setApprovalForAll(address operator, bool approved) external; + function getApproved(uint256 tokenId) external view returns (address); + function isApprovedForAll(address owner, address operator) external view returns (bool); + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} +``` diff --git a/crates/lint/docs/incorrect-shift.md b/crates/lint/docs/incorrect-shift.md new file mode 100644 index 0000000000000..a9a70c7f93128 --- /dev/null +++ b/crates/lint/docs/incorrect-shift.md @@ -0,0 +1,37 @@ +# Incorrect shift order + +**Severity**: `High` +**ID**: `incorrect-shift` + +Flags shift operations where a literal appears on the left and a non-literal on the right, which +is almost always the wrong operand order. + +## What it does + +Warns when the left-hand operand of `<<` or `>>` is a numeric literal and the right-hand operand +is a non-literal expression (e.g. a variable, function call, or composite expression). + +## Why is this bad? + +Shift expressions like `2 << x` are usually a typo for `x << 2`. In the former, the *value being +shifted* is a tiny constant and the *shift amount* is dynamic — almost never the intended +behavior, and a known source of bugs in production contracts. + +## Example + +### Bad + +```solidity +result = 2 << stateValue; // shift amount comes from state +result = 8 >> localValue; // shift amount comes from a local +result = 16 << (stateValue + 1); // shift amount is a dynamic expression +``` + +### Good + +```solidity +result = stateValue << 2; +result = localValue >> 3; +result = stateValue << localShiftAmount; +result = 1 << 8; // both literals — fine +``` diff --git a/crates/lint/docs/inline-assembly.md b/crates/lint/docs/inline-assembly.md new file mode 100644 index 0000000000000..bba61148b84c5 --- /dev/null +++ b/crates/lint/docs/inline-assembly.md @@ -0,0 +1,69 @@ +# Inline assembly + +**Severity**: `Info` +**ID**: `inline-assembly` + +Flags every `assembly { ... }` block. Inline assembly bypasses many of Solidity's safety +features (type checks, overflow checks, memory layout invariants) and is a common source of +high-impact bugs, so each occurrence should be reviewed deliberately. + +## What it does + +Reports every inline assembly statement, including blocks declared with the `"evmasm"` dialect +and/or the `("memory-safe")` flag. Blocks declared as memory-safe — either via the modern +`("memory-safe")` flag or the legacy `/// @solidity memory-safe-assembly` NatSpec marker — are +still reported, but with a softer message acknowledging the developer attestation: review +focuses on business logic and side effects rather than memory layout. + +## Why is this bad? + +Assembly skips Solidity's compile-time checks and many of its runtime guarantees. Mistakes +inside an `assembly` block can corrupt memory, break the free memory pointer, leak storage, +escalate privileges via `delegatecall`, or destroy the contract via `selfdestruct`. Even when +required for gas or features unavailable in high-level Solidity, assembly should be small, +documented, and reviewed. + +## When inline assembly is reasonable + +Some idioms are widely used and generally safe: + +- Reading transaction/chain context: `chainid()`, `gas()`, `returndatasize()`. +- Probing code: `codesize()`, `extcodesize(addr)`, `extcodehash(addr)`. +- Reading the free memory pointer: `mload(0x40)`. +- Cheap hashing of a known memory layout, when paired with `("memory-safe")`. + +If you must use assembly: + +1. Keep the block minimal and well-commented. +2. Add the `("memory-safe")` flag when the block does not violate Solidity's memory model, so + the optimizer (and reviewers) can rely on it. The legacy + `/// @solidity memory-safe-assembly` NatSpec marker on the line directly above the block is + also recognized for compatibility with older codebases. +3. Suppress the lint locally to mark the block as audited: + ```solidity + // forge-lint: disable-next-line(inline-assembly) + assembly ("memory-safe") { /* reviewed: ... */ } + ``` + +## Example + +### Bad + +```solidity +function rawCall(address target, bytes calldata data) external returns (bytes memory) { + assembly { + let ok := call(gas(), target, 0, add(data.offset, 0), data.length, 0, 0) + // ... + } +} +``` + +### Good + +```solidity +function rawCall(address target, bytes calldata data) external returns (bytes memory result) { + bool ok; + (ok, result) = target.call(data); + require(ok, "call failed"); +} +``` diff --git a/crates/lint/docs/interface-file-naming.md b/crates/lint/docs/interface-file-naming.md new file mode 100644 index 0000000000000..ff72a0c175e8e --- /dev/null +++ b/crates/lint/docs/interface-file-naming.md @@ -0,0 +1,31 @@ +# Interface file naming + +**Severity**: `Info` +**ID**: `interface-file-naming` + +Flags Solidity files whose only top-level declaration is an interface but whose filename is not +prefixed with `I`. + +## What it does + +Reports interface-only files whose path basename does not start with `I` (e.g. `IERC20.sol`). + +## Why is this bad? + +Prefixing interface filenames with `I` is the prevailing convention in the Solidity ecosystem. +Following it makes import paths predictable and lets reviewers tell at a glance whether they are +looking at an interface or an implementation. + +## Example + +### Bad + +```text +contracts/Token.sol // file contains only `interface Token { ... }` +``` + +### Good + +```text +contracts/IToken.sol // file contains only `interface IToken { ... }` +``` diff --git a/crates/lint/docs/interface-naming.md b/crates/lint/docs/interface-naming.md new file mode 100644 index 0000000000000..5c6b12b946091 --- /dev/null +++ b/crates/lint/docs/interface-naming.md @@ -0,0 +1,31 @@ +# Interface name should be prefixed with 'I' + +**Severity**: `Info` +**ID**: `interface-naming` + +Flags `interface` declarations whose names are not prefixed with `I`. + +## What it does + +Reports `interface Foo` where `Foo` does not start with `I` (e.g. `IFoo`). + +## Why is this bad? + +Prefixing interfaces with `I` is the prevailing convention in Solidity codebases (`IERC20`, +`IERC721`, `IUniswapV2Pair`, ...). Following it makes the role of each type unambiguous at use +sites and aligns with the matching +[`interface-file-naming`](https://getfoundry.sh/forge/linting/interface-file-naming) lint. + +## Example + +### Bad + +```solidity +interface ERC20 { /* ... */ } +``` + +### Good + +```solidity +interface IERC20 { /* ... */ } +``` diff --git a/crates/lint/docs/missing-zero-check.md b/crates/lint/docs/missing-zero-check.md new file mode 100644 index 0000000000000..7eab1f3a00117 --- /dev/null +++ b/crates/lint/docs/missing-zero-check.md @@ -0,0 +1,39 @@ +# Missing zero-address check + +**Severity**: `Low` +**ID**: `missing-zero-check` + +Flags entry-point functions and constructors where an `address` parameter flows into a state write +or value transfer without a zero-address guard. + +## What it does + +Performs a taint analysis from each `address` parameter of an externally callable, state-mutating +function (or constructor) and reports a parameter that reaches a sink (state write, `transfer`, +`call{value: ...}`, etc.) without first being compared against `address(0)` in an `if`/`require`/ +`assert` predicate. + +## Why is this bad? + +Forgetting a zero-address check is a common source of value loss: tokens become permanently +unrecoverable, ownership is renounced unintentionally, or upgrades are bricked. Adding an explicit +guard is cheap and removes an entire class of operational mistakes. + +## Example + +### Bad + +```solidity +function setOwner(address newOwner) external onlyOwner { + owner = newOwner; // no zero-address check +} +``` + +### Good + +```solidity +function setOwner(address newOwner) external onlyOwner { + require(newOwner != address(0), "zero address"); + owner = newOwner; +} +``` diff --git a/crates/lint/docs/mixed-case-function.md b/crates/lint/docs/mixed-case-function.md new file mode 100644 index 0000000000000..9997dcb5691c7 --- /dev/null +++ b/crates/lint/docs/mixed-case-function.md @@ -0,0 +1,32 @@ +# Function names should use mixedCase + +**Severity**: `Info` +**ID**: `mixed-case-function` + +Flags function names that do not follow `mixedCase`. + +## What it does + +Reports functions whose names contain underscores, start with an uppercase letter, or otherwise +deviate from `mixedCase`. Test functions starting with `test`, `invariant_`, or `statefulFuzz` +and user-defined patterns (e.g. `ERC20`) are exempted. + +## Why is this bad? + +The Solidity style guide recommends `mixedCase` for function names. Consistent style makes call +sites uniform, helps editor tooling, and reduces friction in code review. + +## Example + +### Bad + +```solidity +function get_balance() external view returns (uint256); +function GetBalance() external view returns (uint256); +``` + +### Good + +```solidity +function getBalance() external view returns (uint256); +``` diff --git a/crates/lint/docs/mixed-case-variable.md b/crates/lint/docs/mixed-case-variable.md new file mode 100644 index 0000000000000..3341e1a0c48ad --- /dev/null +++ b/crates/lint/docs/mixed-case-variable.md @@ -0,0 +1,36 @@ +# Mutable variable names should use mixedCase + +**Severity**: `Info` +**ID**: `mixed-case-variable` + +Flags mutable variable names (locals, parameters, mutable state) that do not follow `mixedCase`. + +## What it does + +Reports mutable variable identifiers that contain underscores, start with an uppercase letter, +or otherwise deviate from `mixedCase`. + +`constant` and `immutable` state variables are not flagged by this lint — see +[`screaming-snake-case-const`](https://getfoundry.sh/forge/linting/screaming-snake-case-const) and +[`screaming-snake-case-immutable`](https://getfoundry.sh/forge/linting/screaming-snake-case-immutable). + +## Why is this bad? + +The Solidity style guide recommends `mixedCase` for mutable variables. Consistent style makes +code easier to scan and review. + +## Example + +### Bad + +```solidity +uint256 public total_supply; +address Owner; +``` + +### Good + +```solidity +uint256 public totalSupply; +address owner; +``` diff --git a/crates/lint/docs/multi-contract-file.md b/crates/lint/docs/multi-contract-file.md new file mode 100644 index 0000000000000..beabc827e4ea6 --- /dev/null +++ b/crates/lint/docs/multi-contract-file.md @@ -0,0 +1,37 @@ +# Multiple contracts in one file + +**Severity**: `Info` +**ID**: `multi-contract-file` + +Flags source files that declare more than one top-level contract, interface, or library. + +## What it does + +Reports each top-level `contract`, `interface`, or `library` definition (after the first) in a +file that contains more than one such declaration. + +## Why is this bad? + +Keeping one contract per file improves discoverability (`grep`, IDE jump-to-file), simplifies +import paths, and avoids unintentional bytecode bloat from artifacts that bundle unrelated +contracts. + +## Example + +### Bad + +```solidity +// File: Token.sol +contract TokenA { /* ... */ } +contract TokenB { /* ... */ } +``` + +### Good + +```solidity +// File: TokenA.sol +contract TokenA { /* ... */ } + +// File: TokenB.sol +contract TokenB { /* ... */ } +``` diff --git a/crates/lint/docs/named-struct-fields.md b/crates/lint/docs/named-struct-fields.md new file mode 100644 index 0000000000000..45713e2555ddc --- /dev/null +++ b/crates/lint/docs/named-struct-fields.md @@ -0,0 +1,31 @@ +# Prefer named struct fields + +**Severity**: `Info` +**ID**: `named-struct-fields` + +Flags struct construction expressions that pass fields positionally instead of by name. + +## What it does + +Reports `Struct(a, b, c)` style struct construction; suggests `Struct({ field1: a, field2: b, +field3: c })` instead. + +## Why is this bad? + +Positional struct construction is fragile: adding or reordering fields silently changes the +meaning of every existing call site. Named-field construction is self-documenting and resilient +to struct changes. + +## Example + +### Bad + +```solidity +User memory u = User(addr, 100, true); +``` + +### Good + +```solidity +User memory u = User({ wallet: addr, balance: 100, active: true }); +``` diff --git a/crates/lint/docs/pascal-case-struct.md b/crates/lint/docs/pascal-case-struct.md new file mode 100644 index 0000000000000..02a243bd56bf4 --- /dev/null +++ b/crates/lint/docs/pascal-case-struct.md @@ -0,0 +1,31 @@ +# Struct names should use PascalCase + +**Severity**: `Info` +**ID**: `pascal-case-struct` + +Flags struct definitions whose names do not follow `PascalCase`. + +## What it does + +Reports any `struct` whose identifier does not match the `PascalCase` convention. + +## Why is this bad? + +The Solidity style guide recommends `PascalCase` for type-like names (contracts, structs, +enums, libraries). Consistent casing makes code easier to scan and integrates with editor +features and external tooling. + +## Example + +### Bad + +```solidity +struct user_info { uint256 balance; } +struct USERINFO { uint256 balance; } +``` + +### Good + +```solidity +struct UserInfo { uint256 balance; } +``` diff --git a/crates/lint/docs/pragma-inconsistent.md b/crates/lint/docs/pragma-inconsistent.md new file mode 100644 index 0000000000000..095f45783773d --- /dev/null +++ b/crates/lint/docs/pragma-inconsistent.md @@ -0,0 +1,41 @@ +# Inconsistent pragma directives + +**Severity**: `Info` +**ID**: `pragma-inconsistent` + +Flags projects whose source files declare incompatible or differently-shaped Solidity version +pragmas. + +## What it does + +Inspects every `pragma solidity ...;` directive across all input source files and reports when +their version requirements are inconsistent (different exact versions, mixed caret/tilde/range +shapes, etc.). + +## Why is this bad? + +A project compiled under multiple Solidity versions can subtly change behavior between files +(e.g. checked arithmetic, default visibility, ABI encoding). Aligning pragmas across the project +removes a hidden source of integration bugs and makes upgrades coordinated. + +## Example + +### Bad + +```solidity +// A.sol +pragma solidity 0.8.18; + +// B.sol +pragma solidity ^0.8.20; + +// C.sol +pragma solidity >=0.7.0 <0.9.0; +``` + +### Good + +```solidity +// All files +pragma solidity 0.8.20; +``` diff --git a/crates/lint/docs/rtlo.md b/crates/lint/docs/rtlo.md new file mode 100644 index 0000000000000..58ce648752c6f --- /dev/null +++ b/crates/lint/docs/rtlo.md @@ -0,0 +1,32 @@ +# Right-to-left override character + +**Severity**: `High` +**ID**: `rtlo` + +Flags the presence of Unicode bidirectional override characters in source code, which can be used +to hide malicious behavior ("Trojan Source", [CVE-2021-42574](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574)). + +## What it does + +Detects the right-to-left override codepoint (`U+202E`) and other bidirectional control characters +embedded in identifiers, strings, and comments. + +## Why is this bad? + +These characters render source code in a different visual order than how the compiler reads it, +allowing an attacker to make malicious code look benign on review. Solidity contracts are public +and frequently audited visually; this attack vector must not be ignored. + +## Example + +### Bad + +```solidity +// transfer(victim‮, attacker)/* // U+202E hidden between args +``` + +### Good + +```solidity +// Avoid bidirectional override characters in code and comments. +``` diff --git a/crates/lint/docs/screaming-snake-case-const.md b/crates/lint/docs/screaming-snake-case-const.md new file mode 100644 index 0000000000000..72a16c5875fae --- /dev/null +++ b/crates/lint/docs/screaming-snake-case-const.md @@ -0,0 +1,30 @@ +# Constants should use SCREAMING_SNAKE_CASE + +**Severity**: `Info` +**ID**: `screaming-snake-case-const` + +Flags `constant` state variables whose names do not follow `SCREAMING_SNAKE_CASE`. + +## What it does + +Reports state variables declared `constant` whose identifier deviates from `SCREAMING_SNAKE_CASE`. + +## Why is this bad? + +The Solidity style guide recommends `SCREAMING_SNAKE_CASE` for constants so they stand out from +mutable state and immutables at call sites. + +## Example + +### Bad + +```solidity +uint256 constant maxSupply = 1_000_000; +uint256 constant Max_Supply = 1_000_000; +``` + +### Good + +```solidity +uint256 constant MAX_SUPPLY = 1_000_000; +``` diff --git a/crates/lint/docs/screaming-snake-case-immutable.md b/crates/lint/docs/screaming-snake-case-immutable.md new file mode 100644 index 0000000000000..cee5590e16d27 --- /dev/null +++ b/crates/lint/docs/screaming-snake-case-immutable.md @@ -0,0 +1,31 @@ +# Immutables should use SCREAMING_SNAKE_CASE + +**Severity**: `Info` +**ID**: `screaming-snake-case-immutable` + +Flags `immutable` state variables whose names do not follow `SCREAMING_SNAKE_CASE`. + +## What it does + +Reports state variables declared `immutable` whose identifier deviates from +`SCREAMING_SNAKE_CASE`. + +## Why is this bad? + +The Solidity style guide recommends `SCREAMING_SNAKE_CASE` for `immutable` variables so they +visually align with `constant` ones and stand out from mutable state at call sites. + +## Example + +### Bad + +```solidity +address immutable owner; +address immutable Owner; +``` + +### Good + +```solidity +address immutable OWNER; +``` diff --git a/crates/lint/docs/too-many-digits.md b/crates/lint/docs/too-many-digits.md new file mode 100644 index 0000000000000..5decb67bec9c3 --- /dev/null +++ b/crates/lint/docs/too-many-digits.md @@ -0,0 +1,32 @@ +# Numeric literal with too many digits + +**Severity**: `Info` +**ID**: `too-many-digits` + +Flags numeric literals containing five or more consecutive zeros, which are easy to misread. + +## What it does + +Reports decimal numeric literals that contain a run of 5 or more `0` characters. + +## Why is this bad? + +Long sequences of zeros are difficult to count visually, and an off-by-one zero is a common bug +(e.g. funding `1_000_000` instead of `10_000_000`). Use scientific notation, sub-denominations, or +underscore separators to make the magnitude obvious. + +## Example + +### Bad + +```solidity +uint256 amount = 1000000000000000000; +``` + +### Good + +```solidity +uint256 amount = 1e18; +uint256 amount2 = 1 ether; +uint256 amount3 = 1_000_000_000_000_000_000; +``` diff --git a/crates/lint/docs/tx-origin.md b/crates/lint/docs/tx-origin.md new file mode 100644 index 0000000000000..26877cf9c0116 --- /dev/null +++ b/crates/lint/docs/tx-origin.md @@ -0,0 +1,34 @@ +# Use of tx.origin for authorization + +**Severity**: `Med` +**ID**: `tx-origin` + +Flags use of `tx.origin` inside authorization-like predicates such as `require`, `assert`, `if`, +`while`, and `for` conditions. + +## What it does + +Reports `tx.origin` reads when they are used as part of a guard condition. Plain reads outside of +guard predicates are not reported. + +## Why is this bad? + +`tx.origin` is the original externally owned account that started the whole transaction, not the +immediate caller. If authorization checks rely on `tx.origin`, a malicious contract can call the +protected contract while the legitimate owner is the transaction origin. + +Use `msg.sender` for authorization checks instead. + +## Example + +### Bad + +```solidity +require(tx.origin == owner, "not owner"); +``` + +### Good + +```solidity +require(msg.sender == owner, "not owner"); +``` diff --git a/crates/lint/docs/unaliased-plain-import.md b/crates/lint/docs/unaliased-plain-import.md new file mode 100644 index 0000000000000..be8c5120028d6 --- /dev/null +++ b/crates/lint/docs/unaliased-plain-import.md @@ -0,0 +1,34 @@ +# Unaliased plain import + +**Severity**: `Info` +**ID**: `unaliased-plain-import` + +Flags `import "path";` statements that pull in every top-level symbol from another file without +an alias. + +## What it does + +Reports plain imports of the form `import "path";`. Suggests using either named imports +(`import { A, B } from "path"`) or an aliased import (`import "path" as X`). + +## Why is this bad? + +Plain imports pollute the importing file's namespace and make the source of each symbol +non-obvious. Named or aliased imports make the dependency surface explicit and reduce the chance +of accidental name collisions. + +## Example + +### Bad + +```solidity +import "./Lib.sol"; +``` + +### Good + +```solidity +import { Foo, Bar } from "./Lib.sol"; +// or +import "./Lib.sol" as Lib; +``` diff --git a/crates/lint/docs/unchecked-call.md b/crates/lint/docs/unchecked-call.md new file mode 100644 index 0000000000000..9a0a4143a0e0e --- /dev/null +++ b/crates/lint/docs/unchecked-call.md @@ -0,0 +1,34 @@ +# Unchecked low-level call + +**Severity**: `High` +**ID**: `unchecked-call` + +Flags low-level calls (`call`, `delegatecall`, `staticcall`, `callcode`) whose `success` return +value is ignored. + +## What it does + +Warns when the boolean returned by a low-level call is discarded — either because the return value +is not assigned or because only the `bytes memory` payload is used. + +## Why is this bad? + +Low-level calls do **not** revert when the callee fails; they silently return `false`. Ignoring +the success flag means a failed call is indistinguishable from a successful one, leading to bugs +where state is updated on the assumption that an external interaction succeeded. + +## Example + +### Bad + +```solidity +target.call(data); // success ignored +(, bytes memory ret) = target.call(data); // only payload kept +``` + +### Good + +```solidity +(bool ok, ) = target.call(data); +require(ok, "call failed"); +``` diff --git a/crates/lint/docs/unsafe-cheatcode.md b/crates/lint/docs/unsafe-cheatcode.md new file mode 100644 index 0000000000000..0aef657b0b7be --- /dev/null +++ b/crates/lint/docs/unsafe-cheatcode.md @@ -0,0 +1,35 @@ +# Usage of unsafe cheatcodes + +**Severity**: `Info` +**ID**: `unsafe-cheatcode` + +Flags use of Foundry cheatcodes that perform dangerous side effects (filesystem access, network +activity, environment variable reads, etc.) so they cannot slip into production code unnoticed. + +## What it does + +Reports calls to cheatcodes whose effects extend beyond the EVM sandbox or that bypass typical +test invariants. The flagged set follows the cheatcode's +[`Safety::Unsafe`](https://book.getfoundry.sh/cheatcodes) classification. + +## Why is this bad? + +Unsafe cheatcodes can read/write files, hit the network, or fork external state. They are +appropriate in tests with explicit intent but should not be added without review, and must +never end up in shipped contract code. + +## Example + +### Bad + +```solidity +vm.writeFile("./out.txt", data); // unsafe — writes to host filesystem +vm.envString("PRIVATE_KEY"); // unsafe — reads host environment +``` + +### Good + +```solidity +// Use safe cheatcodes (vm.expectRevert, vm.prank, vm.warp, ...) and explicit +// inputs/fixtures instead of pulling state from the host environment. +``` diff --git a/crates/lint/docs/unsafe-typecast.md b/crates/lint/docs/unsafe-typecast.md new file mode 100644 index 0000000000000..89d493eec3c3f --- /dev/null +++ b/crates/lint/docs/unsafe-typecast.md @@ -0,0 +1,40 @@ +# Unsafe typecast + +**Severity**: `Med` +**ID**: `unsafe-typecast` + +Flags explicit numeric typecasts that can silently truncate or alter the value. + +## What it does + +Reports casts where the source value's type is wider than the target type +(e.g. `uint256 → uint128`, `int256 → uint128`), unless the cast is preceded by a check that +guarantees the value fits in the target. + +## Why is this bad? + +Solidity does **not** revert on narrowing casts; it silently keeps the lowest bits, which can +cause severe accounting bugs (e.g. amount overflows, wrong fees, broken invariants). Use a checked +cast helper such as OpenZeppelin's `SafeCast` whenever the source value is not provably bounded. + +## Example + +### Bad + +```solidity +function setAmount(uint256 amount) external { + smallAmount = uint128(amount); // silent truncation if amount >= 2**128 +} +``` + +### Good + +```solidity +function setAmount(uint256 amount) external { + require(amount <= type(uint128).max, "overflow"); + smallAmount = uint128(amount); +} + +// or +smallAmount = SafeCast.toUint128(amount); +``` diff --git a/crates/lint/docs/unused-import.md b/crates/lint/docs/unused-import.md new file mode 100644 index 0000000000000..08f2545a36587 --- /dev/null +++ b/crates/lint/docs/unused-import.md @@ -0,0 +1,40 @@ +# Unused import + +**Severity**: `Info` +**ID**: `unused-import` + +Flags imported symbols (or whole import statements) whose imported names are not referenced +anywhere in the source unit. + +## What it does + +Reports `import "..."`, `import "..." as X`, and `import { A, B } from "..."` statements where one +or more imported names are never used. Symbols brought in via `import * as X` are tracked through +`X.member` accesses. + +## Why is this bad? + +Unused imports add noise, slow down compilation, can cause name collisions, and frequently +indicate dead code or stale refactors. + +## Example + +### Bad + +```solidity +import { A, B } from "./Lib.sol"; // B is never used + +contract C { + A internal a; +} +``` + +### Good + +```solidity +import { A } from "./Lib.sol"; + +contract C { + A internal a; +} +``` diff --git a/crates/lint/docs/unused-state-variables.md b/crates/lint/docs/unused-state-variables.md new file mode 100644 index 0000000000000..758c6e58b911b --- /dev/null +++ b/crates/lint/docs/unused-state-variables.md @@ -0,0 +1,39 @@ +# Unused state variable + +**Severity**: `Gas` +**ID**: `unused-state-variables` + +Flags state variables that are declared but never read or written anywhere in the contract or its +descendants. + +## What it does + +Reports each state variable that has no read or write site across the project. + +## Why is this bad? + +Unused state variables waste storage slots, inflate deployment cost, and are a strong signal of +dead or stale code that should be removed. + +## Example + +### Bad + +```solidity +contract C { + uint256 unused; // never read or written + uint256 public total; // used elsewhere +} +``` + +### Good + +```solidity +contract C { + uint256 public total; +} +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/unwrapped-modifier-logic.md b/crates/lint/docs/unwrapped-modifier-logic.md new file mode 100644 index 0000000000000..985c79962af07 --- /dev/null +++ b/crates/lint/docs/unwrapped-modifier-logic.md @@ -0,0 +1,51 @@ +# Unwrapped modifier logic + +**Severity**: `CodeSize` +**ID**: `unwrapped-modifier-logic` + +Flags modifiers whose body contains non-trivial logic that should be moved into a helper function +to reduce contract code size. + +## What it does + +Reports modifiers whose body contains statements other than a single placeholder, simple builtin +calls (`require`/`assert`), or a single library function call. Modifiers that use inline assembly +are exempted. + +## Why is this bad? + +Solidity inlines a modifier's body at every call site, so any non-trivial logic is duplicated +across all functions that use the modifier. Wrapping the logic in an internal function and calling +it from the modifier keeps the bytecode small while preserving behavior. + +## Example + +### Bad + +```solidity +modifier onlyAuth() { + if (!auth[msg.sender]) revert NotAuth(); + bytes32 nonce = keccak256(abi.encodePacked(msg.sender, block.number)); + seenNonce[nonce] = true; + _; +} +``` + +### Good + +```solidity +modifier onlyAuth() { + _checkAuth(); + _; +} + +function _checkAuth() internal { + if (!auth[msg.sender]) revert NotAuth(); + bytes32 nonce = keccak256(abi.encodePacked(msg.sender, block.number)); + seenNonce[nonce] = true; +} +``` + +## Notes + +This is a `CodeSize`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/src/linter/mod.rs b/crates/lint/src/linter/mod.rs index 0a5b4a40a5118..3e38a02726605 100644 --- a/crates/lint/src/linter/mod.rs +++ b/crates/lint/src/linter/mod.rs @@ -1,8 +1,10 @@ mod early; mod late; +mod project; pub use early::{EarlyLintPass, EarlyLintVisitor}; pub use late::{LateLintPass, LateLintVisitor}; +pub use project::{ProjectLintEmitter, ProjectLintPass, ProjectSource}; use foundry_common::comments::inline_config::InlineConfig; use foundry_compilers::Language; diff --git a/crates/lint/src/linter/project.rs b/crates/lint/src/linter/project.rs new file mode 100644 index 0000000000000..38fc1ad1ba59f --- /dev/null +++ b/crates/lint/src/linter/project.rs @@ -0,0 +1,92 @@ +use super::{Lint, LintContext, LinterConfig}; +use foundry_common::comments::inline_config::InlineConfig; +use foundry_config::lint::LintSpecificConfig; +use solar::{ + ast, + interface::{Session, Span, diagnostics::DiagMsg, source_map::SourceFile}, +}; +use std::{path::PathBuf, sync::Arc}; + +/// A single source unit visible to a project-wide lint pass, pre-loaded with its inline config so +/// emits respect `// forge-lint: disable-*` markers without rebuilding it per emit. +pub struct ProjectSource<'ast> { + pub path: PathBuf, + pub file: Arc, + pub ast: &'ast ast::SourceUnit<'ast>, + pub inline_config: InlineConfig>, +} + +/// Trait for lints that need to inspect every input source at once (e.g. cross-file checks). +/// +/// `check_project` runs once after all per-file [`super::EarlyLintPass`] / +/// [`super::LateLintPass`] passes have completed. +pub trait ProjectLintPass<'ast>: Send + Sync { + fn check_project(&mut self, ctx: &ProjectLintEmitter<'_, '_>, sources: &[ProjectSource<'ast>]); +} + +/// Helper passed to [`ProjectLintPass::check_project`] for emitting diagnostics against a specific +/// source. +pub struct ProjectLintEmitter<'s, 'c> { + sess: &'s Session, + with_description: bool, + with_json_emitter: bool, + lint_specific: &'c LintSpecificConfig, + active_lints: Vec<&'static str>, +} + +impl<'s, 'c> ProjectLintEmitter<'s, 'c> { + pub const fn new( + sess: &'s Session, + with_description: bool, + with_json_emitter: bool, + lint_specific: &'c LintSpecificConfig, + active_lints: Vec<&'static str>, + ) -> Self { + Self { sess, with_description, with_json_emitter, lint_specific, active_lints } + } + + /// Returns `true` if the given lint id is enabled for this run. Project passes that perform + /// expensive analysis should guard their work behind this check. + pub fn is_lint_enabled(&self, id: &'static str) -> bool { + self.active_lints.contains(&id) + } + + /// Emits a diagnostic with the lint's default description as the message. + pub fn emit<'a, 'ast, L: Lint>( + &'a self, + source: &'a ProjectSource<'ast>, + lint: &'static L, + span: Span, + ) where + 'c: 'a, + { + self.build_ctx(source).emit(lint, span); + } + + /// Emits a diagnostic with a caller-provided message. + pub fn emit_with_msg<'a, 'ast, L: Lint>( + &'a self, + source: &'a ProjectSource<'ast>, + lint: &'static L, + span: Span, + msg: impl Into, + ) where + 'c: 'a, + { + self.build_ctx(source).emit_with_msg(lint, span, msg); + } + + fn build_ctx<'a, 'ast>(&'a self, source: &'a ProjectSource<'ast>) -> LintContext<'s, 'a> + where + 'c: 'a, + { + LintContext::new( + self.sess, + self.with_description, + self.with_json_emitter, + LinterConfig { inline: &source.inline_config, lint_specific: self.lint_specific }, + self.active_lints.clone(), + Some(source.file.clone()), + ) + } +} diff --git a/crates/lint/src/sol/info/inline_assembly.rs b/crates/lint/src/sol/info/inline_assembly.rs new file mode 100644 index 0000000000000..1111129dada34 --- /dev/null +++ b/crates/lint/src/sol/info/inline_assembly.rs @@ -0,0 +1,71 @@ +use super::InlineAssembly; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{Stmt, StmtKind}, + interface::{BytePos, Span}, +}; + +declare_forge_lint!( + INLINE_ASSEMBLY, + Severity::Info, + "inline-assembly", + "usage of inline assembly; assembly bypasses Solidity safety features and should be reviewed" +); + +const ASSEMBLY_KW_LEN: u32 = 8; +const NATSPEC_MEMORY_SAFE_MARKER: &str = "@solidity memory-safe-assembly"; + +impl<'ast> EarlyLintPass<'ast> for InlineAssembly { + fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) { + let StmtKind::Assembly(asm) = &stmt.kind else { return }; + + let kw_span = assembly_keyword_span(stmt.span); + + let memory_safe = asm.flags.iter().any(|f| f.value.as_str() == "memory-safe") + || has_memory_safe_natspec(ctx, stmt.span.lo()); + + let msg = if memory_safe { + "inline assembly (declared memory-safe); review business logic and side effects" + } else { + "inline assembly used; review for memory safety and side effects" + }; + + ctx.emit_with_msg(&INLINE_ASSEMBLY, kw_span, msg); + } +} + +/// Narrows a span to the leading `assembly` keyword to keep diagnostics readable. +fn assembly_keyword_span(span: Span) -> Span { + span.with_hi(span.lo() + BytePos(ASSEMBLY_KW_LEN)) +} + +/// Returns `true` when the lines immediately preceding `stmt_lo` form a `///` NatSpec block +/// containing `@solidity memory-safe-assembly`. +fn has_memory_safe_natspec(ctx: &LintContext, stmt_lo: BytePos) -> bool { + let Some(source_file) = ctx.source_file() else { return false }; + let src = source_file.src.as_str(); + let start_pos = source_file.start_pos.to_u32(); + let lo_abs = stmt_lo.to_u32(); + if lo_abs < start_pos { + return false; + } + let offset = (lo_abs - start_pos) as usize; + if offset > src.len() { + return false; + } + + for line in src[..offset].lines().rev() { + let trimmed = line.trim_start(); + if trimmed.is_empty() { + continue; + } + let Some(rest) = trimmed.strip_prefix("///") else { return false }; + if rest.trim_start().starts_with(NATSPEC_MEMORY_SAFE_MARKER) { + return true; + } + } + false +} diff --git a/crates/lint/src/sol/info/mod.rs b/crates/lint/src/sol/info/mod.rs index c7800a417bafc..913c5d2ea9da3 100644 --- a/crates/lint/src/sol/info/mod.rs +++ b/crates/lint/src/sol/info/mod.rs @@ -30,6 +30,15 @@ use multi_contract_file::MULTI_CONTRACT_FILE; mod interface_naming; use interface_naming::{INTERFACE_FILE_NAMING, INTERFACE_NAMING}; +mod too_many_digits; +use too_many_digits::TOO_MANY_DIGITS; + +mod pragma_directive; +use pragma_directive::PRAGMA_INCONSISTENT; + +mod inline_assembly; +use inline_assembly::INLINE_ASSEMBLY; + register_lints!( (BooleanCst, early, (BOOLEAN_CST)), (BooleanEqual, early, (BOOLEAN_EQUAL)), @@ -42,4 +51,7 @@ register_lints!( (UnsafeCheatcodes, early, (UNSAFE_CHEATCODE_USAGE)), (MultiContractFile, early, (MULTI_CONTRACT_FILE)), (InterfaceFileNaming, early, (INTERFACE_FILE_NAMING, INTERFACE_NAMING)), + (TooManyDigits, early, (TOO_MANY_DIGITS)), + (PragmaDirective, project, (PRAGMA_INCONSISTENT)), + (InlineAssembly, early, (INLINE_ASSEMBLY)), ); diff --git a/crates/lint/src/sol/info/pragma_directive.rs b/crates/lint/src/sol/info/pragma_directive.rs new file mode 100644 index 0000000000000..b66b6bcff6ade --- /dev/null +++ b/crates/lint/src/sol/info/pragma_directive.rs @@ -0,0 +1,71 @@ +use crate::{ + linter::{Lint, ProjectLintEmitter, ProjectLintPass, ProjectSource}, + sol::{Severity, SolLint, info::PragmaDirective}, +}; +use solar::{ast, interface::Span}; + +declare_forge_lint!( + PRAGMA_INCONSISTENT, + Severity::Info, + "pragma-inconsistent", + "inconsistent Solidity pragma version requirements across the project" +); + +impl<'ast> ProjectLintPass<'ast> for PragmaDirective { + fn check_project(&mut self, ctx: &ProjectLintEmitter<'_, '_>, sources: &[ProjectSource<'ast>]) { + if !ctx.is_lint_enabled(PRAGMA_INCONSISTENT.id()) { + return; + } + + // Collect every `pragma solidity` directive across input sources, with its rendered + // version-requirement string for grouping. Stores source index to avoid lifetime + // invariance issues with `&ProjectSource<'ast>`. + let mut entries: Vec<(usize, Span, String)> = Vec::new(); + for (idx, source) in sources.iter().enumerate() { + for (span, req) in solidity_pragmas(source.ast) { + entries.push((idx, span, req.to_string())); + } + } + + // Stable order for snapshots and JSON output. + entries.sort_by(|a, b| { + sources[a.0].path.cmp(&sources[b.0].path).then(a.1.lo().cmp(&b.1.lo())) + }); + + // Build the distinct list once and bail if all sources agree. + let mut distinct: Vec<&str> = entries.iter().map(|(_, _, s)| s.as_str()).collect(); + distinct.sort_unstable(); + distinct.dedup(); + if distinct.len() < 2 { + return; + } + + for (idx, span, req_str) in &entries { + let others = distinct + .iter() + .filter(|v| **v != req_str.as_str()) + .copied() + .collect::>() + .join(", "); + let msg = format!( + "'pragma solidity {req_str};' conflicts with other version requirements in the project: {others}" + ); + ctx.emit_with_msg(&sources[*idx], &PRAGMA_INCONSISTENT, *span, msg); + } + } +} + +/// Yields every top-level `pragma solidity ...;` directive in `unit`. +fn solidity_pragmas<'ast>( + unit: &'ast ast::SourceUnit<'ast>, +) -> impl Iterator)> + 'ast { + unit.items.iter().filter_map(|item| match &item.kind { + ast::ItemKind::Pragma(p) => match &p.tokens { + ast::PragmaTokens::Version(ident, req) if ident.as_str() == "solidity" => { + Some((item.span, req)) + } + _ => None, + }, + _ => None, + }) +} diff --git a/crates/lint/src/sol/info/too_many_digits.rs b/crates/lint/src/sol/info/too_many_digits.rs new file mode 100644 index 0000000000000..3ba9e8abba2de --- /dev/null +++ b/crates/lint/src/sol/info/too_many_digits.rs @@ -0,0 +1,50 @@ +use super::TooManyDigits; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::ast::{Expr, ExprKind, LitKind}; + +declare_forge_lint!( + TOO_MANY_DIGITS, + Severity::Info, + "too-many-digits", + "numeric literal with many digits is error-prone; \ + use scientific notation, sub-denominations, or underscore separators" +); + +impl<'ast> EarlyLintPass<'ast> for TooManyDigits { + fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) { + let ExprKind::Lit(lit, sub_denom) = &expr.kind else { return }; + + // Only plain integer literals. `LitKind::Address` (40-hex-digit address) is a + // distinct variant and is therefore skipped automatically. + if !matches!(lit.kind, LitKind::Number(_)) { + return; + } + + // Skip literals with a sub-denomination, e.g. `1000000 gwei`, `5 minutes`. + if sub_denom.is_some() { + return; + } + + let s = lit.symbol.as_str(); + + // Skip hex literals — long zero runs in hex are usually intentional (masks, + // selectors, bit patterns) and there is no scientific-notation alternative. + if s.starts_with("0x") || s.starts_with("0X") { + return; + } + + // Skip if the user already used scientific notation (`1e18`). + if s.contains('e') || s.contains('E') { + return; + } + + // 5+ consecutive zeros in the literal as written. Underscores are + // preserved, so `1_000_000` passes while `1_000000` is flagged. + if s.contains("00000") { + ctx.emit(&TOO_MANY_DIGITS, lit.span); + } + } +} diff --git a/crates/lint/src/sol/macros.rs b/crates/lint/src/sol/macros.rs index 00d764770374a..8540ab8b95b8f 100644 --- a/crates/lint/src/sol/macros.rs +++ b/crates/lint/src/sol/macros.rs @@ -9,9 +9,11 @@ /// - `$desc`: A short description of the lint. /// /// # Note -/// Each lint must have a `help` section in the foundry book. This help field is auto-generated by -/// the macro. Because of that, to ensure that new lint rules have their corresponding docs in the -/// book, the existence of the lint rule's help section is validated with a unit test. +/// Each lint must have a corresponding markdown documentation file at +/// `crates/lint/docs/.md`. The `help` URL is auto-generated by the macro and points to +/// the per-lint page on the Foundry docs site (`getfoundry.sh/forge/linting/`). To +/// ensure that new lint rules have their corresponding docs, the existence of every registered +/// lint's markdown file is validated by a unit test (see `crates/lint/src/sol/mod.rs`). #[macro_export] macro_rules! declare_forge_lint { ($id:ident, $severity:expr, $str_id:expr, $desc:expr) => { @@ -20,7 +22,7 @@ macro_rules! declare_forge_lint { id: $str_id, severity: $severity, description: $desc, - help: concat!("https://book.getfoundry.sh/reference/forge/forge-lint#", $str_id), + help: concat!("https://getfoundry.sh/forge/linting/", $str_id), }; }; } @@ -53,6 +55,7 @@ macro_rules! register_lints { register_lints!(@early_impl $pass_id, $pass_type); register_lints!(@late_impl $pass_id, $pass_type); + register_lints!(@project_impl $pass_id, $pass_type); } )* }; @@ -89,10 +92,22 @@ macro_rules! register_lints { .flatten() .collect() } + + pub fn create_project_lint_passes<'ast>() -> Vec<(Box>, &'static [SolLint])> { + [ + $( + register_lints!(@project_create $pass_id, $pass_type), + )* + ] + .into_iter() + .flatten() + .collect() + } }; // --- HELPERS ------------------------------------------------------------ (@early_impl $_pass_id:ident, late) => {}; + (@early_impl $_pass_id:ident, project) => {}; (@early_impl $pass_id:ident, $other:ident) => { pub fn as_early_lint_pass<'a>() -> Box> { Box::new(Self::default()) @@ -100,22 +115,41 @@ macro_rules! register_lints { }; (@late_impl $_pass_id:ident, early) => {}; + (@late_impl $_pass_id:ident, project) => {}; (@late_impl $pass_id:ident, $other:ident) => { pub fn as_late_lint_pass<'hir>() -> Box> { Box::new(Self::default()) } }; + (@project_impl $_pass_id:ident, early) => {}; + (@project_impl $_pass_id:ident, late) => {}; + (@project_impl $_pass_id:ident, both) => {}; + (@project_impl $pass_id:ident, $other:ident) => { + pub fn as_project_lint_pass<'ast>() -> Box> { + Box::new(Self::default()) + } + }; + (@early_create $_pass_id:ident, late) => { None }; + (@early_create $_pass_id:ident, project) => { None }; (@early_create $pass_id:ident, $_other:ident) => { Some(($pass_id::as_early_lint_pass(), $pass_id::LINTS)) }; (@late_create $_pass_id:ident, early) => { None }; + (@late_create $_pass_id:ident, project) => { None }; (@late_create $pass_id:ident, $_other:ident) => { Some(($pass_id::as_late_lint_pass(), $pass_id::LINTS)) }; + (@project_create $_pass_id:ident, early) => { None }; + (@project_create $_pass_id:ident, late) => { None }; + (@project_create $_pass_id:ident, both) => { None }; + (@project_create $pass_id:ident, $_other:ident) => { + Some(($pass_id::as_project_lint_pass(), $pass_id::LINTS)) + }; + // --- ENTRY POINT --------------------------------------------------------- ( $($tokens:tt)* ) => { register_lints! { @declare_structs $($tokens)* } diff --git a/crates/lint/src/sol/med/mod.rs b/crates/lint/src/sol/med/mod.rs index ba7a09b0e9bac..2673ba23d3252 100644 --- a/crates/lint/src/sol/med/mod.rs +++ b/crates/lint/src/sol/med/mod.rs @@ -9,6 +9,9 @@ use incorrect_erc20_interface::INCORRECT_ERC20_INTERFACE; mod incorrect_erc721_interface; use incorrect_erc721_interface::INCORRECT_ERC721_INTERFACE; +mod tx_origin; +use tx_origin::TX_ORIGIN; + mod unsafe_typecast; use unsafe_typecast::UNSAFE_TYPECAST; @@ -16,5 +19,6 @@ register_lints!( (DivideBeforeMultiply, early, (DIVIDE_BEFORE_MULTIPLY)), (IncorrectERC20Interface, late, (INCORRECT_ERC20_INTERFACE)), (IncorrectERC721Interface, late, (INCORRECT_ERC721_INTERFACE)), + (TxOrigin, early, (TX_ORIGIN)), (UnsafeTypecast, late, (UNSAFE_TYPECAST)) ); diff --git a/crates/lint/src/sol/med/tx_origin.rs b/crates/lint/src/sol/med/tx_origin.rs new file mode 100644 index 0000000000000..00ff5f939ebfb --- /dev/null +++ b/crates/lint/src/sol/med/tx_origin.rs @@ -0,0 +1,101 @@ +use super::TxOrigin; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{Expr, ExprKind, IndexKind, Stmt, StmtKind}, + interface::SpannedOption, +}; + +declare_forge_lint!( + TX_ORIGIN, + Severity::Med, + "tx-origin", + "`tx.origin` should not be used for authorization" +); + +impl<'ast> EarlyLintPass<'ast> for TxOrigin { + fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) { + match &stmt.kind { + StmtKind::If(cond, ..) | StmtKind::DoWhile(_, cond) => { + emit_if_contains_tx_origin(ctx, cond); + } + StmtKind::While(cond, _) => { + emit_if_contains_tx_origin(ctx, cond); + } + StmtKind::For { cond: Some(cond), .. } => { + emit_if_contains_tx_origin(ctx, cond); + } + _ => {} + } + } + + fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(callee, args) = &expr.kind + && is_require_or_assert_call(callee) + && let Some(cond) = args.exprs().next() + { + emit_if_contains_tx_origin(ctx, cond); + } + } +} + +fn emit_if_contains_tx_origin(ctx: &LintContext, expr: &Expr<'_>) { + if contains_tx_origin(expr) { + ctx.emit(&TX_ORIGIN, expr.span); + } +} + +fn contains_tx_origin(expr: &Expr<'_>) -> bool { + if is_tx_origin(expr) { + return true; + } + match &expr.kind { + ExprKind::Unary(_, inner) => contains_tx_origin(inner), + ExprKind::Binary(lhs, _, rhs) => contains_tx_origin(lhs) || contains_tx_origin(rhs), + ExprKind::Index(base, index) => { + contains_tx_origin(base) + || match index { + IndexKind::Index(Some(index)) => contains_tx_origin(index), + IndexKind::Range(start, end) => { + start.as_ref().is_some_and(|start| contains_tx_origin(start)) + || end.as_ref().is_some_and(|end| contains_tx_origin(end)) + } + _ => false, + } + } + ExprKind::Tuple(elems) => elems.iter().any(|elem| { + if let SpannedOption::Some(inner) = elem.as_ref() { + contains_tx_origin(inner) + } else { + false + } + }), + ExprKind::Call(callee, args) => { + contains_tx_origin(callee) || args.exprs().any(contains_tx_origin) + } + ExprKind::Ternary(cond, then_expr, else_expr) => { + contains_tx_origin(cond) + || contains_tx_origin(then_expr) + || contains_tx_origin(else_expr) + } + _ => false, + } +} + +fn is_tx_origin(expr: &Expr<'_>) -> bool { + matches!( + &expr.kind, + ExprKind::Member(base, member) + if member.as_str() == "origin" + && matches!(&base.kind, ExprKind::Ident(ident) if ident.as_str() == "tx") + ) +} + +fn is_require_or_assert_call(callee: &Expr<'_>) -> bool { + matches!( + &callee.kind, + ExprKind::Ident(ident) if matches!(ident.as_str(), "require" | "assert") + ) +} diff --git a/crates/lint/src/sol/mod.rs b/crates/lint/src/sol/mod.rs index 1f7c515585fb4..7ae073f2ea20b 100644 --- a/crates/lint/src/sol/mod.rs +++ b/crates/lint/src/sol/mod.rs @@ -1,6 +1,6 @@ use crate::linter::{ EarlyLintPass, EarlyLintVisitor, LateLintPass, LateLintVisitor, Lint, LintContext, Linter, - LinterConfig, + LinterConfig, ProjectLintEmitter, ProjectLintPass, ProjectSource, }; use foundry_common::{ comments::{ @@ -179,6 +179,62 @@ impl<'a> SolidityLinter<'a> { Ok(()) } + /// Runs all enabled project-wide lint passes against the given input sources. + fn process_project<'gcx>(&self, gcx: Gcx<'gcx>, input: &[PathBuf]) { + // Gather enabled project passes from every severity bucket. + let mut passes_and_lints: Vec<(Box>, &'static [SolLint])> = + Vec::new(); + passes_and_lints.extend(high::create_project_lint_passes()); + passes_and_lints.extend(med::create_project_lint_passes()); + passes_and_lints.extend(low::create_project_lint_passes()); + passes_and_lints.extend(info::create_project_lint_passes()); + passes_and_lints.extend(gas::create_project_lint_passes()); + passes_and_lints.extend(codesize::create_project_lint_passes()); + + let (mut passes, lint_ids): (Vec>>, Vec<_>) = passes_and_lints + .into_iter() + .fold((Vec::new(), Vec::new()), |(mut passes, mut ids), (pass, lints)| { + let included: Vec<_> = lints + .iter() + .filter_map(|lint| self.include_lint(*lint).then_some(lint.id)) + .collect(); + if !included.is_empty() { + passes.push(pass); + ids.extend(included); + } + (passes, ids) + }); + + if passes.is_empty() { + return; + } + + // Pre-load every input source with its inline config, in input order. + let sources: Vec> = input + .iter() + .filter_map(|path| { + let path = self.path_config.root.join(path); + let (_, source) = gcx.get_ast_source(&path)?; + let ast = source.ast.as_ref()?; + let comments = + Comments::new(&source.file, gcx.sess.source_map(), false, false, None); + let inline_config = parse_inline_config(gcx.sess, &comments, ast); + Some(ProjectSource { path, file: source.file.clone(), ast, inline_config }) + }) + .collect(); + + let emitter = ProjectLintEmitter::new( + gcx.sess, + self.with_description, + self.with_json_emitter, + self.lint_specific, + lint_ids, + ); + for pass in &mut passes { + pass.check_project(&emitter, &sources); + } + } + fn process_source_hir<'gcx>( &self, gcx: Gcx<'gcx>, @@ -314,6 +370,9 @@ impl<'a> Linter for SolidityLinter<'a> { ); }); + // Project-wide lints, run once after all per-file passes. + self.process_project(gcx, input); + convert_solar_errors(compiler.dcx()) })?; @@ -453,3 +512,75 @@ impl<'a> TryFrom<&'a str> for SolLint { Err(SolLintError::InvalidId(value.to_string())) } } + +#[cfg(test)] +mod tests { + use super::*; + + /// Every registered lint must have a markdown documentation file at + /// `crates/lint/docs/.md`. This test enforces that contract so that the `help` URL + /// generated by `declare_forge_lint!` always resolves to real documentation. + /// + /// When this test fails, add a new file at `crates/lint/docs/.md` describing the + /// lint. See [`crates/lint/docs/_template.md`](../../docs/_template.md) for the expected + /// structure. + #[test] + fn registered_lints_have_docs() { + let docs_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("docs"); + assert!(docs_dir.is_dir(), "missing docs directory at {}", docs_dir.display()); + + let all_lints: Vec<&'static SolLint> = high::REGISTERED_LINTS + .iter() + .chain(med::REGISTERED_LINTS) + .chain(low::REGISTERED_LINTS) + .chain(info::REGISTERED_LINTS) + .chain(gas::REGISTERED_LINTS) + .chain(codesize::REGISTERED_LINTS) + .collect(); + + let mut missing: Vec<&'static str> = Vec::new(); + let mut empty: Vec<&'static str> = Vec::new(); + for lint in &all_lints { + let path = docs_dir.join(format!("{}.md", lint.id())); + match std::fs::read_to_string(&path) { + Ok(content) => { + // Basic sanity: file should be non-trivial and reference the lint id. + if content.trim().is_empty() || !content.contains(lint.id()) { + empty.push(lint.id()); + } + } + Err(_) => missing.push(lint.id()), + } + } + + assert!( + missing.is_empty(), + "the following registered lints are missing a docs file at \ + `crates/lint/docs/.md`: {missing:?}\n\ + See `crates/lint/docs/_template.md` for the expected structure." + ); + assert!( + empty.is_empty(), + "the following lint docs files are empty or do not reference the lint id: {empty:?}" + ); + } + + /// The auto-generated `help` URL must point at the canonical Foundry docs site so that the + /// link printed in diagnostics resolves correctly. + #[test] + fn registered_lints_have_canonical_help_url() { + let all_lints: Vec<&'static SolLint> = high::REGISTERED_LINTS + .iter() + .chain(med::REGISTERED_LINTS) + .chain(low::REGISTERED_LINTS) + .chain(info::REGISTERED_LINTS) + .chain(gas::REGISTERED_LINTS) + .chain(codesize::REGISTERED_LINTS) + .collect(); + + for lint in all_lints { + let expected = format!("https://getfoundry.sh/forge/linting/{}", lint.id()); + assert_eq!(lint.help(), expected, "lint `{}` has a non-canonical help URL", lint.id()); + } + } +} diff --git a/crates/lint/testdata/BlockTimestamp.stderr b/crates/lint/testdata/BlockTimestamp.stderr index 016f8fa2bdb2d..62ab588ae7340 100644 --- a/crates/lint/testdata/BlockTimestamp.stderr +++ b/crates/lint/testdata/BlockTimestamp.stderr @@ -4,7 +4,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp > deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -12,7 +12,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp == 0; │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -20,7 +20,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp != 0; │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -28,7 +28,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp <= deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -36,7 +36,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp >= deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -44,7 +44,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp < deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -52,7 +52,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return deadline > block.timestamp; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -60,7 +60,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp + 1 > deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -68,7 +68,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return (block.timestamp / 3600) == 0; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -76,7 +76,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ require(block.timestamp > deadline); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -84,7 +84,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ if (block.timestamp > deadline) { │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -92,5 +92,5 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return foo(block.timestamp) > 0; │ ━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp diff --git a/crates/lint/testdata/BooleanCst.stderr b/crates/lint/testdata/BooleanCst.stderr index 53b89fcb11735..75fdb0b57cea7 100644 --- a/crates/lint/testdata/BooleanCst.stderr +++ b/crates/lint/testdata/BooleanCst.stderr @@ -4,7 +4,7 @@ warning[boolean-cst]: misuse of a boolean constant LL │ if (false) {} │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-cst + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst warning[boolean-cst]: misuse of a boolean constant ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC @@ -12,7 +12,7 @@ warning[boolean-cst]: misuse of a boolean constant LL │ if (flag || true) {} │ ━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-cst + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst warning[boolean-cst]: misuse of a boolean constant ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC @@ -20,7 +20,7 @@ warning[boolean-cst]: misuse of a boolean constant LL │ if (flag ? true : false) {} │ ━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-cst + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst warning[boolean-cst]: misuse of a boolean constant ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC @@ -28,7 +28,7 @@ warning[boolean-cst]: misuse of a boolean constant LL │ if (flag ? true : false) {} │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-cst + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst warning[boolean-cst]: misuse of a boolean constant ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC @@ -36,5 +36,5 @@ warning[boolean-cst]: misuse of a boolean constant LL │ return assigned && false; │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-cst + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst diff --git a/crates/lint/testdata/BooleanEqual.stderr b/crates/lint/testdata/BooleanEqual.stderr index 11749698f5714..590a85b806fcf 100644 --- a/crates/lint/testdata/BooleanEqual.stderr +++ b/crates/lint/testdata/BooleanEqual.stderr @@ -4,7 +4,7 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ if (enabled == true) {} │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `enabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal note[boolean-equal]: boolean comparisons to constants should be simplified ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC @@ -12,7 +12,7 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ if (paused == false) {} │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `!paused` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal note[boolean-equal]: boolean comparisons to constants should be simplified ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC @@ -20,7 +20,7 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ if (true != ready) {} │ ━━━━━━━━━━━━━ help: consider simplifying to: `!ready` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal note[boolean-equal]: boolean comparisons to constants should be simplified ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC @@ -28,7 +28,7 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ while (done != false) { │ ━━━━━━━━━━━━━ help: consider simplifying to: `done` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal note[boolean-equal]: boolean comparisons to constants should be simplified ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC @@ -36,7 +36,7 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ for (; enabled == true && paused != false;) { │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `enabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal note[boolean-equal]: boolean comparisons to constants should be simplified ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC @@ -44,7 +44,7 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ for (; enabled == true && paused != false;) { │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `paused` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal note[boolean-equal]: boolean comparisons to constants should be simplified ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC @@ -52,5 +52,5 @@ note[boolean-equal]: boolean comparisons to constants should be simplified LL │ return enabled == true; │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `enabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#boolean-equal + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal diff --git a/crates/lint/testdata/CouldBeImmutable.stderr b/crates/lint/testdata/CouldBeImmutable.stderr index 2858b2311cd95..170682baf89d3 100644 --- a/crates/lint/testdata/CouldBeImmutable.stderr +++ b/crates/lint/testdata/CouldBeImmutable.stderr @@ -4,7 +4,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ address public owner; │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[could-be-immutable]: state variable could be declared immutable ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC @@ -12,7 +12,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ address public deployer = msg.sender; │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[could-be-immutable]: state variable could be declared immutable ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC @@ -20,7 +20,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ uint256 private configured; │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[could-be-immutable]: state variable could be declared immutable ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC @@ -28,7 +28,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ bytes32 internal salt = keccak256(abi.encodePacked(block.timestamp)); │ ━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[could-be-immutable]: state variable could be declared immutable ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC @@ -36,7 +36,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ CouldBeImmutable private peer; │ ━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[could-be-immutable]: state variable could be declared immutable ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC @@ -44,7 +44,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ uint256 internal inheritedBase; │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[could-be-immutable]: state variable could be declared immutable ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC @@ -52,5 +52,5 @@ note[could-be-immutable]: state variable could be declared immutable LL │ uint256 internal baseConfigured; │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable diff --git a/crates/lint/testdata/CustomErrors.stderr b/crates/lint/testdata/CustomErrors.stderr index 66b3c11bc183c..286a649aee269 100644 --- a/crates/lint/testdata/CustomErrors.stderr +++ b/crates/lint/testdata/CustomErrors.stderr @@ -4,7 +4,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ require(a > 0, "Value must be greater than zero"); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -12,7 +12,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ … require(a >= 0 && a <= 100 || b == 50, "Complex condition should be linted"); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -20,7 +20,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ revert("Something went wrong"); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -28,7 +28,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ revert(""); │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -36,5 +36,5 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ revert(); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors diff --git a/crates/lint/testdata/DivideBeforeMultiply.stderr b/crates/lint/testdata/DivideBeforeMultiply.stderr index c0e5ef78e2e1c..95022f65db874 100644 --- a/crates/lint/testdata/DivideBeforeMultiply.stderr +++ b/crates/lint/testdata/DivideBeforeMultiply.stderr @@ -4,7 +4,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -12,7 +12,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ ((1 / 2) * 3) * 4; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -20,7 +20,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ ((1 * 2) / 3) * 4; │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -28,7 +28,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ (1 / 2 / 3) * 4; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -36,7 +36,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ (1 / (2 + 3)) * 4; │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -44,5 +44,5 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ 1 / ((2 / 3) * 3); │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply diff --git a/crates/lint/testdata/Imports.stderr b/crates/lint/testdata/Imports.stderr index 8fa9800b27ded..1031f4f6f8ca0 100644 --- a/crates/lint/testdata/Imports.stderr +++ b/crates/lint/testdata/Imports.stderr @@ -4,7 +4,7 @@ note[unaliased-plain-import]: use named imports '{A, B}' or alias 'import ".." a LL │ import "./auxiliary/ImportsSomeFile.sol"; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unaliased-plain-import + ╰ help: https://getfoundry.sh/forge/linting/unaliased-plain-import note[unaliased-plain-import]: use named imports '{A, B}' or alias 'import ".." as X' ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -12,7 +12,7 @@ note[unaliased-plain-import]: use named imports '{A, B}' or alias 'import ".." a LL │ import "./auxiliary/ImportsAnotherFile.sol"; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unaliased-plain-import + ╰ help: https://getfoundry.sh/forge/linting/unaliased-plain-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -20,7 +20,7 @@ note[unused-import]: unused imports should be removed LL │ symbol2 as notUsed, │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -28,7 +28,7 @@ note[unused-import]: unused imports should be removed LL │ docSymbol, │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -36,7 +36,7 @@ note[unused-import]: unused imports should be removed LL │ docSymbol2, │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -44,7 +44,7 @@ note[unused-import]: unused imports should be removed LL │ docSymbolWrongTag, │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -52,7 +52,7 @@ note[unused-import]: unused imports should be removed LL │ symbolNotUsed, │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -60,7 +60,7 @@ note[unused-import]: unused imports should be removed LL │ IContractNotUsed │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -68,7 +68,7 @@ note[unused-import]: unused imports should be removed LL │ symbolNotUsed3 │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -76,7 +76,7 @@ note[unused-import]: unused imports should be removed LL │ CONSTANT_1 │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -84,7 +84,7 @@ note[unused-import]: unused imports should be removed LL │ YetAnotherType │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -92,7 +92,7 @@ note[unused-import]: unused imports should be removed LL │ import "./auxiliary/ImportsAnotherFile2.sol" as AnotherFile2; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -100,5 +100,5 @@ note[unused-import]: unused imports should be removed LL │ import * as OtherUtils from "./auxiliary/ImportsUtils2.sol"; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import diff --git a/crates/lint/testdata/IncorrectERC20Interface.stderr b/crates/lint/testdata/IncorrectERC20Interface.stderr index 3bb60ecce8320..33e2f1ca27d22 100644 --- a/crates/lint/testdata/IncorrectERC20Interface.stderr +++ b/crates/lint/testdata/IncorrectERC20Interface.stderr @@ -4,7 +4,7 @@ note[interface-naming]: interface names should be prefixed with 'I' LL │ interface ERC20 { │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#interface-naming + ╰ help: https://getfoundry.sh/forge/linting/interface-naming note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20 {} │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface ERC20 { │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20Incorrect is IERC20 { │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -36,7 +36,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20Correct is IERC20 { │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -44,7 +44,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20NamedCorrect { │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -52,7 +52,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface INotERC20 { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -60,7 +60,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function transfer(address to, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -68,7 +68,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function approve(address spender, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -76,7 +76,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function transfer(address to, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -84,7 +84,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function transferFrom(address from, address to, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -92,7 +92,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function approve(address spender, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -100,7 +100,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function allowance(address owner, address spender) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -108,7 +108,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function balanceOf(address account) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -116,5 +116,5 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function totalSupply() external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface diff --git a/crates/lint/testdata/IncorrectERC721Interface.stderr b/crates/lint/testdata/IncorrectERC721Interface.stderr index a88db93e39b10..2e68084c1cec1 100644 --- a/crates/lint/testdata/IncorrectERC721Interface.stderr +++ b/crates/lint/testdata/IncorrectERC721Interface.stderr @@ -4,7 +4,7 @@ note[interface-naming]: interface names should be prefixed with 'I' LL │ interface ERC721 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#interface-naming + ╰ help: https://getfoundry.sh/forge/linting/interface-naming note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721 {} │ ━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface ERC721 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721Incorrect is IERC721 { │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -36,7 +36,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721Correct is IERC721 { │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -44,7 +44,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721NamedCorrect { │ ━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -52,7 +52,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface INotERC721 { │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -60,7 +60,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function balanceOf(address owner) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -68,7 +68,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function ownerOf(uint256 tokenId) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -76,7 +76,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function balanceOf(address owner) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -84,7 +84,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function ownerOf(uint256 tokenId) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -92,7 +92,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -100,7 +100,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function safeTransferFrom(address from, address to, uint256 tokenId) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -108,7 +108,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function transferFrom(address from, address to, uint256 tokenId) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -116,7 +116,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function approve(address to, uint256 tokenId) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -124,7 +124,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function setApprovalForAll(address operator, bool approved) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -132,7 +132,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function getApproved(uint256 tokenId) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -140,7 +140,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function isApprovedForAll(address owner, address operator) external view returns (address); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -148,5 +148,5 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function supportsInterface(bytes4 interfaceId) external view returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface diff --git a/crates/lint/testdata/IncorrectShift.stderr b/crates/lint/testdata/IncorrectShift.stderr index bce84c98df432..dfff32db897bb 100644 --- a/crates/lint/testdata/IncorrectShift.stderr +++ b/crates/lint/testdata/IncorrectShift.stderr @@ -4,7 +4,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 2 << stateValue; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -12,7 +12,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 8 >> localValue; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -20,7 +20,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 16 << (stateValue + 1); │ ━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -28,7 +28,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 32 >> getAmount(); │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -36,5 +36,5 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ … result = 1 << (localValue > 10 ? localShiftAmount : stateShiftAmount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift diff --git a/crates/lint/testdata/InlineAssembly.sol b/crates/lint/testdata/InlineAssembly.sol new file mode 100644 index 0000000000000..05917ea22784c --- /dev/null +++ b/crates/lint/testdata/InlineAssembly.sol @@ -0,0 +1,110 @@ +//@compile-flags: --severity info + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract InlineAssembly { + function bare() public view returns (uint256 id) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + id := chainid() + } + } + + function withMemorySafe() public view returns (uint256 size) { + assembly ("memory-safe") { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + size := extcodesize(address()) + } + } + + function withDialectAndMemorySafe() public view returns (uint256 ptr) { + assembly "evmasm" ("memory-safe") { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + ptr := mload(0x40) + } + } + + function withNatspecMemorySafe() public view returns (uint256 v) { + /// @solidity memory-safe-assembly + assembly { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + v := chainid() + } + } + + function withNatspecMemorySafeAndOtherDocs() public view returns (uint256 v) { + /// @notice does a thing + /// @solidity memory-safe-assembly + assembly { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + v := gas() + } + } + + function plainCommentDoesNotCount() public view returns (uint256 v) { + // solidity memory-safe-assembly + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := chainid() + } + } + + function nestedInControlFlow(bool flag) public view returns (uint256 v) { + if (flag) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := gas() + } + } + + for (uint256 i = 0; i < 1; ++i) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := add(v, 1) + } + } + } + + function nestedInUnchecked(uint256 x) public pure returns (uint256 v) { + unchecked { + v = x + 1; + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := add(v, 1) + } + } + } + + function nestedInTryCatch() public returns (uint256 v) { + try this.bare() returns (uint256) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := 1 + } + } catch { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := 2 + } + } + } + + function suppressed() public view returns (uint256 id) { + // forge-lint: disable-next-line(inline-assembly) + assembly { + id := chainid() + } + } + + modifier guarded() { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + if iszero(caller()) { revert(0, 0) } + } + _; + } + + function suppressedRegion() public view returns (uint256 a, uint256 b) { + // forge-lint: disable-start(inline-assembly) + assembly { + a := chainid() + } + assembly ("memory-safe") { + b := gas() + } + // forge-lint: disable-end(inline-assembly) + } + + function noAssembly() public pure returns (uint256) { + return 42; + } +} diff --git a/crates/lint/testdata/InlineAssembly.stderr b/crates/lint/testdata/InlineAssembly.stderr new file mode 100644 index 0000000000000..12f8bcbacd14e --- /dev/null +++ b/crates/lint/testdata/InlineAssembly.stderr @@ -0,0 +1,96 @@ +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly ("memory-safe") { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly "evmasm" ("memory-safe") { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + diff --git a/crates/lint/testdata/Keccak256.sol b/crates/lint/testdata/Keccak256.sol index 41f856336b5b3..2457aed96d601 100644 --- a/crates/lint/testdata/Keccak256.sol +++ b/crates/lint/testdata/Keccak256.sol @@ -52,6 +52,7 @@ contract AsmKeccak256 { function assemblyHash(uint256 a, uint256 b) public pure returns (bytes32) { //optimized + // forge-lint: disable-next-line(inline-assembly) assembly { mstore(0x00, a) mstore(0x20, b) diff --git a/crates/lint/testdata/Keccak256.stderr b/crates/lint/testdata/Keccak256.stderr index 4203d950d9cba..a81e429e389a1 100644 --- a/crates/lint/testdata/Keccak256.stderr +++ b/crates/lint/testdata/Keccak256.stderr @@ -4,7 +4,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 MixedCase_Variable = 1; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `mixedCaseVariable` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -12,7 +12,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Another_MixedCase = 2; │ ━━━━━━━━━━━━━━━━━ help: consider using: `anotherMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -20,7 +20,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 YetAnother_MixedCase = 3; │ ━━━━━━━━━━━━━━━━━━━━ help: consider using: `yetAnotherMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -28,7 +28,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Enabled_MixedCase_Variable; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `enabledMixedCaseVariable` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -36,7 +36,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Enabled_MixedCase_Variable = 1; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `enabledMixedCaseVariable` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -44,7 +44,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract AsmKeccak256 { │ ━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -52,7 +52,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract OtherAsmKeccak256 { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -60,7 +60,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract YetAnotherAsmKeccak256 { │ ━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -68,7 +68,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 hash = keccak256(abi.encodePacked(a, b, bytes32(bytes20(c)))); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -76,7 +76,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 afterDisabledBlock = keccak256(abi.encode(a, b, c)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -84,7 +84,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 loadsFromCalldata = keccak256(z); │ ━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -92,7 +92,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 loadsFromMemory = keccak256(y); │ ━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -100,7 +100,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 lintWithoutFix = keccak256(abi.encodePacked(a, b, c)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -108,7 +108,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ return keccak256(abi.encode(a, b, c)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[unused-state-variables]: state variable is never used ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -116,7 +116,7 @@ note[unused-state-variables]: state variable is never used LL │ uint256 Enabled_MixedCase_Variable; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-state-variables + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -124,7 +124,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 doesNotUseScratchSpace = keccak256(abi.encode(x, y, x, y, x, y)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -132,7 +132,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 doesUseScratchSpace = keccak256(abi.encode(x)); │ ━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -140,5 +140,5 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ return keccak256(abi.encode(doesUseScratchSpace, doesNotUseScratchSpace)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 diff --git a/crates/lint/testdata/MissingZeroCheck.stderr b/crates/lint/testdata/MissingZeroCheck.stderr index b55a902547fcf..81a9179e79c94 100644 --- a/crates/lint/testdata/MissingZeroCheck.stderr +++ b/crates/lint/testdata/MissingZeroCheck.stderr @@ -4,7 +4,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwner(address newOwner) external { │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -12,7 +12,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ constructor(address initialOwner) { │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -20,7 +20,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function pay(address payable to) external { │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -28,7 +28,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function lowLevel(address payable to, bytes calldata data) external { │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -36,7 +36,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function withUselessModifier(address a) external doesNothing(a) { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -44,7 +44,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwnerViaAlias(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -52,7 +52,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwnerViaReassign(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -60,7 +60,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwnerViaCast(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -68,7 +68,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function payViaAlias(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -76,7 +76,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function mixedParams(address a, address b) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -84,7 +84,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function bothSinks(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -92,7 +92,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function ternaryAlias(address a, bool flag) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -100,7 +100,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function payableWrap(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -108,7 +108,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function modifierWithExpr(address a) external nonZero(addrIdentity(a)) { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -116,7 +116,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function delegateCallSink(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -124,7 +124,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function sendSinkStmt(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -132,7 +132,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function sendSinkDecl(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -140,7 +140,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function multiHopTaint(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -148,7 +148,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardAfterSink(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -156,7 +156,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardOnOneBranch(address a, bool flag) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -164,7 +164,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardInForLoop(address a, uint256 n) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -172,7 +172,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardInWhileLoop(address a, bool flag) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -180,5 +180,5 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardInTryClause(address a, address payable target) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check diff --git a/crates/lint/testdata/MixedCase.stderr b/crates/lint/testdata/MixedCase.stderr index d290af5cdb5a8..2db30559ba5a6 100644 --- a/crates/lint/testdata/MixedCase.stderr +++ b/crates/lint/testdata/MixedCase.stderr @@ -4,7 +4,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Variablemixedcase; │ ━━━━━━━━━━━━━━━━━ help: consider using: `variablemixedcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -12,7 +12,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 VARIABLE_MIXED_CASE; │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `variableMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -20,7 +20,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 VariableMixedCase; │ ━━━━━━━━━━━━━━━━━ help: consider using: `variableMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -28,7 +28,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 testVAL; │ ━━━━━━━ help: consider using: `testVal` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -36,7 +36,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 TestVal; │ ━━━━━━━ help: consider using: `testVal` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -44,7 +44,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 TESTVAL; │ ━━━━━━━ help: consider using: `testval` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -52,7 +52,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function Functionmixedcase() public {} │ ━━━━━━━━━━━━━━━━━ help: consider using: `functionmixedcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -60,7 +60,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function FUNCTION_MIXED_CASE() public {} │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -68,7 +68,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function FunctionMixedCase() public {} │ ━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -76,7 +76,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function function_mixed_case() public {} │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -84,7 +84,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function invariantBalance_MixedCase_Enabled() public {} │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `invariantBalanceMixedCaseEnabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -92,7 +92,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function invariantbalance_mixedcase_enabled() public {} │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `invariantbalanceMixedcaseEnabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -100,7 +100,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function ERC20_DoSomething() public {} // invalid because of the underscore │ ━━━━━━━━━━━━━━━━━ help: consider using: `erc20DoSomething` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -108,7 +108,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function HAS_PARAMS(address addr) external view returns (uint256) {} │ ━━━━━━━━━━ help: consider using: `hasParams` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -116,7 +116,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function HAS_NO_RETURN() external view {} │ ━━━━━━━━━━━━━ help: consider using: `hasNoReturn` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -124,7 +124,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function HAS_MORE_THAN_ONE_RETURN() external view returns (uint256, uint256) {} │ ━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `hasMoreThanOneReturn` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -132,7 +132,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function NOT_ELEMENTARY_RETURN() external view returns (uint256[] memory) {} │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `notElementaryReturn` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -140,7 +140,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -148,5 +148,5 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract MixedCaseTest { │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file diff --git a/crates/lint/testdata/MultiContractFile.stderr b/crates/lint/testdata/MultiContractFile.stderr index c6e4e32a2df55..e25f3d72ad01a 100644 --- a/crates/lint/testdata/MultiContractFile.stderr +++ b/crates/lint/testdata/MultiContractFile.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract A {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract B {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract C {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface I {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -36,5 +36,5 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ library L {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file diff --git a/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr b/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr index 41fc439ea7d1b..1912f16863712 100644 --- a/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr +++ b/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface I1 {} │ ━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile_InterfaceLibrary.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ library L1 {} │ ━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile_InterfaceLibrary.sol:LL:CC @@ -20,5 +20,5 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract C1 {} │ ━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file diff --git a/crates/lint/testdata/NamedStructFields.stderr b/crates/lint/testdata/NamedStructFields.stderr index 6ee2160791cd2..cfb35637176bd 100644 --- a/crates/lint/testdata/NamedStructFields.stderr +++ b/crates/lint/testdata/NamedStructFields.stderr @@ -4,5 +4,5 @@ note[named-struct-fields]: prefer initializing structs with named fields LL │ Person memory person = Person("Alice", 25, address(0)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using named fields: `Person({ name: "Alice", age: 25, wallet: address(0) })` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#named-struct-fields + ╰ help: https://getfoundry.sh/forge/linting/named-struct-fields diff --git a/crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol new file mode 100644 index 0000000000000..bfc993baab794 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; //~NOTE: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: 0.8.18 +pragma solidity 0.8.18; //~NOTE: 'pragma solidity 0.8.18;' conflicts with other version requirements in the project: ^0.8.0 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr new file mode 100644 index 0000000000000..c2c967dee792f --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: 0.8.18 + ╭▸ ROOT/testdata/PragmaInconsistentCaretAboveExact.sol:LL:CC + │ +LL │ pragma solidity ^0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.18;' conflicts with other version requirements in the project: ^0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentCaretAboveExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.18; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol new file mode 100644 index 0000000000000..75bc17988accc --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; //~NOTE: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 +pragma solidity 0.8.20; //~NOTE: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr new file mode 100644 index 0000000000000..f60361718ba9b --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretMatchesExact.sol:LL:CC + │ +LL │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretMatchesExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol new file mode 100644 index 0000000000000..37b06040c33a6 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; //~NOTE: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: ~0.8.20 +pragma solidity ~0.8.20; //~NOTE: 'pragma solidity ~0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr new file mode 100644 index 0000000000000..6c46f2478208d --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: ~0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretVsTilde.sol:LL:CC + │ +LL │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ~0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretVsTilde.sol:LL:CC + │ +LL │ pragma solidity ~0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentOrVsExact.sol b/crates/lint/testdata/PragmaInconsistentOrVsExact.sol new file mode 100644 index 0000000000000..f85a477cc8744 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentOrVsExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20 || 0.8.21; //~NOTE: 'pragma solidity 0.8.20 || 0.8.21;' conflicts with other version requirements in the project: 0.8.20 +pragma solidity 0.8.20; //~NOTE: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: 0.8.20 || 0.8.21 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentOrVsExact.stderr b/crates/lint/testdata/PragmaInconsistentOrVsExact.stderr new file mode 100644 index 0000000000000..acf6bd7c2d6e0 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentOrVsExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity 0.8.20 || 0.8.21;' conflicts with other version requirements in the project: 0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentOrVsExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20 || 0.8.21; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: 0.8.20 || 0.8.21 + ╭▸ ROOT/testdata/PragmaInconsistentOrVsExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentRangeVsExact.sol b/crates/lint/testdata/PragmaInconsistentRangeVsExact.sol new file mode 100644 index 0000000000000..d8fcb7a0eb4b1 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentRangeVsExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; //~NOTE: 'pragma solidity >=0.8.0 <0.9.0;' conflicts with other version requirements in the project: 0.8.20 +pragma solidity 0.8.20; //~NOTE: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: >=0.8.0 <0.9.0 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr b/crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr new file mode 100644 index 0000000000000..5ac221b924c9a --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity >=0.8.0 <0.9.0;' conflicts with other version requirements in the project: 0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentRangeVsExact.sol:LL:CC + │ +LL │ pragma solidity >=0.8.0 <0.9.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: >=0.8.0 <0.9.0 + ╭▸ ROOT/testdata/PragmaInconsistentRangeVsExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentThreeDistinct.sol b/crates/lint/testdata/PragmaInconsistentThreeDistinct.sol new file mode 100644 index 0000000000000..fe208e15efb63 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentThreeDistinct.sol @@ -0,0 +1,8 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; //~NOTE: 'pragma solidity >=0.8.0;' conflicts with other version requirements in the project: ^0.8.0, ~0.8.0 +pragma solidity ^0.8.0; //~NOTE: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ~0.8.0 +pragma solidity ~0.8.0; //~NOTE: 'pragma solidity ~0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ^0.8.0 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr b/crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr new file mode 100644 index 0000000000000..e1e5ad7333fb2 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr @@ -0,0 +1,24 @@ +note[pragma-inconsistent]: 'pragma solidity >=0.8.0;' conflicts with other version requirements in the project: ^0.8.0, ~0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentThreeDistinct.sol:LL:CC + │ +LL │ pragma solidity >=0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ~0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentThreeDistinct.sol:LL:CC + │ +LL │ pragma solidity ^0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ~0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ^0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentThreeDistinct.sol:LL:CC + │ +LL │ pragma solidity ~0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/Rtlo.stderr b/crates/lint/testdata/Rtlo.stderr index 2c2b53df646e1..93f5bb191532f 100644 --- a/crates/lint/testdata/Rtlo.stderr +++ b/crates/lint/testdata/Rtlo.stderr @@ -4,7 +4,7 @@ warning[rtlo]: U+202A (Left-to-Right Embedding) detected LL │ string public lre = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -12,7 +12,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ string public lre = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202B (Right-to-Left Embedding) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -20,7 +20,7 @@ warning[rtlo]: U+202B (Right-to-Left Embedding) detected LL │ string public rle = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -28,7 +28,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ string public rle = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202A (Left-to-Right Embedding) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -36,7 +36,7 @@ warning[rtlo]: U+202A (Left-to-Right Embedding) detected LL │ string public pdf = unicode"��"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -44,7 +44,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ string public pdf = unicode"��"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202D (Left-to-Right Override) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -52,7 +52,7 @@ warning[rtlo]: U+202D (Left-to-Right Override) detected LL │ string public lro = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -60,7 +60,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ string public lro = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202E (Right-to-Left Override) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -68,7 +68,7 @@ warning[rtlo]: U+202E (Right-to-Left Override) detected LL │ string public rlo = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -76,7 +76,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ string public rlo = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2066 (Left-to-Right Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -84,7 +84,7 @@ warning[rtlo]: U+2066 (Left-to-Right Isolate) detected LL │ string public lri = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2069 (Pop Directional Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -92,7 +92,7 @@ warning[rtlo]: U+2069 (Pop Directional Isolate) detected LL │ string public lri = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2067 (Right-to-Left Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -100,7 +100,7 @@ warning[rtlo]: U+2067 (Right-to-Left Isolate) detected LL │ string public rli = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2069 (Pop Directional Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -108,7 +108,7 @@ warning[rtlo]: U+2069 (Pop Directional Isolate) detected LL │ string public rli = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2068 (First Strong Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -116,7 +116,7 @@ warning[rtlo]: U+2068 (First Strong Isolate) detected LL │ string public fsi = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2069 (Pop Directional Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -124,7 +124,7 @@ warning[rtlo]: U+2069 (Pop Directional Isolate) detected LL │ string public fsi = unicode"�_�"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2066 (Left-to-Right Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -132,7 +132,7 @@ warning[rtlo]: U+2066 (Left-to-Right Isolate) detected LL │ string public pdi = unicode"��"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+2069 (Pop Directional Isolate) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -140,7 +140,7 @@ warning[rtlo]: U+2069 (Pop Directional Isolate) detected LL │ string public pdi = unicode"��"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202E (Right-to-Left Override) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -148,7 +148,7 @@ warning[rtlo]: U+202E (Right-to-Left Override) detected LL │ /* hidden� /* text � */ uint256 inBlockComment; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -156,7 +156,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ /* hidden� /* text � */ uint256 inBlockComment; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202E (Right-to-Left Override) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -164,7 +164,7 @@ warning[rtlo]: U+202E (Right-to-Left Override) detected LL │ // sneaky� payload � trailing │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -172,7 +172,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ // sneaky� payload � trailing │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+200E (Left-to-Right Mark) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -180,7 +180,7 @@ warning[rtlo]: U+200E (Left-to-Right Mark) detected LL │ string public marks = unicode"left‎right‏end"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+200F (Right-to-Left Mark) detected ╭▸ ROOT/testdata/Rtlo.sol:LL:CC @@ -188,5 +188,5 @@ warning[rtlo]: U+200F (Right-to-Left Mark) detected LL │ string public marks = unicode"left‎right‏end"; │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo diff --git a/crates/lint/testdata/RtloCommentsOnly.stderr b/crates/lint/testdata/RtloCommentsOnly.stderr index 88a354432867e..5a7ec9ee6e69d 100644 --- a/crates/lint/testdata/RtloCommentsOnly.stderr +++ b/crates/lint/testdata/RtloCommentsOnly.stderr @@ -4,7 +4,7 @@ warning[rtlo]: U+202E (Right-to-Left Override) detected LL │ // hidden� payload � trailing │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC @@ -12,7 +12,7 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ // hidden� payload � trailing │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202E (Right-to-Left Override) detected ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC @@ -20,7 +20,7 @@ warning[rtlo]: U+202E (Right-to-Left Override) detected LL │ /* block� comment � end */ │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo warning[rtlo]: U+202C (Pop Directional Formatting) detected ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC @@ -28,5 +28,5 @@ warning[rtlo]: U+202C (Pop Directional Formatting) detected LL │ /* block� comment � end */ │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#rtlo + ╰ help: https://getfoundry.sh/forge/linting/rtlo diff --git a/crates/lint/testdata/ScreamingSnakeCase.stderr b/crates/lint/testdata/ScreamingSnakeCase.stderr index a740506ed74d8..36305bb268d9a 100644 --- a/crates/lint/testdata/ScreamingSnakeCase.stderr +++ b/crates/lint/testdata/ScreamingSnakeCase.stderr @@ -4,7 +4,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant screamingSnakeCase = 0; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -12,7 +12,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant screaming_snake_case = 0; │ ━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -20,7 +20,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant ScreamingSnakeCase = 0; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -28,7 +28,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant SCREAMING_snake_case = 0; │ ━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -36,7 +36,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable screamingSnakeCase0 = 0; │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -44,7 +44,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable screaming_snake_case0 = 0; │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -52,7 +52,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable ScreamingSnakeCase0 = 0; │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -60,5 +60,5 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable SCREAMING_snake_case_0 = 0; │ ━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE_0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable diff --git a/crates/lint/testdata/StructPascalCase.stderr b/crates/lint/testdata/StructPascalCase.stderr index 1c7bfa13ba84b..255c1c4d5d74b 100644 --- a/crates/lint/testdata/StructPascalCase.stderr +++ b/crates/lint/testdata/StructPascalCase.stderr @@ -4,7 +4,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct _PascalCase { │ ━━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -12,7 +12,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct pascalCase { │ ━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -20,7 +20,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct pascalcase { │ ━━━━━━━━━━ help: consider using: `Pascalcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -28,7 +28,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct pascal_case { │ ━━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -36,7 +36,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct PASCAL_CASE { │ ━━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -44,5 +44,5 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct PASCALCASE { │ ━━━━━━━━━━ help: consider using: `Pascalcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct diff --git a/crates/lint/testdata/TooManyDigits.sol b/crates/lint/testdata/TooManyDigits.sol new file mode 100644 index 0000000000000..a56ad67fe379e --- /dev/null +++ b/crates/lint/testdata/TooManyDigits.sol @@ -0,0 +1,73 @@ +//@compile-flags: --severity info + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract TooManyDigits { + // SHOULD FAIL: plain decimal integer literals with 5+ consecutive zeros. + + uint256 stateA = 1000000000000000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + uint256 stateB = 100000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + function asReturn() public pure returns (uint256) { + return 10000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + function asComparison(uint256 x) public pure returns (bool) { + return x == 1000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + function asArg(address to) public { + _send(to, 50000000000); //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + function asArraySize() public pure { + uint256[100000] memory _arr; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + // Zero-run in the middle (not just trailing). + uint256 middleZeros = 123000007; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + // Underscores that don't actually break up the zero run. + uint256 badGrouping = 1_000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + // Underscore right after a single digit, leaving a 5-zero group. + uint256 badGrouping2 = 1_00000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + // SHOULD PASS: + + // Boundary: 4 consecutive zeros (one short of the threshold). + uint256 fourZeros = 10000; + + // Uppercase scientific notation. + uint256 sciUpper = 1E18; + + // Scientific notation. + uint256 sci = 1e18; + + // Underscore-separated digit groups. + uint256 grouped = 1_000_000_000_000_000_000; + + // Sub-denominations. + uint256 oneEther = 1 ether; + uint256 oneGwei = 1 gwei; + uint256 fiveMin = 5 minutes; + + // Address literal (distinct AST kind, not flagged). + address adr = 0x1234567890123456789012345678901234567890; + + // Hex literal — intentional zero patterns (mask / padded value). + bytes32 mask = 0x0000000000000000000000000000000000000000000000000000000000000001; + uint256 hexNum = 0x100000; + + // Small literals (< 5 consecutive zeros). + uint256 small1 = 100; + uint256 small2 = 9999; + uint256 small3 = 1234; + uint256 spread = 101010; + + // Boolean literal. + bool flag = true; + + function _send(address, uint256) internal pure {} +} diff --git a/crates/lint/testdata/TooManyDigits.stderr b/crates/lint/testdata/TooManyDigits.stderr new file mode 100644 index 0000000000000..7e21a530776c2 --- /dev/null +++ b/crates/lint/testdata/TooManyDigits.stderr @@ -0,0 +1,72 @@ +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 stateA = 1000000000000000000; + │ ━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 stateB = 100000; + │ ━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … return 10000000; + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … return x == 1000000; + │ ━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … _send(to, 50000000000); + │ ━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … uint256[100000] memory _arr; + │ ━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 middleZeros = 123000007; + │ ━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 badGrouping = 1_000000; + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 badGrouping2 = 1_00000; + │ ━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + diff --git a/crates/lint/testdata/TxOrigin.sol b/crates/lint/testdata/TxOrigin.sol new file mode 100644 index 0000000000000..9728a7e528e5b --- /dev/null +++ b/crates/lint/testdata/TxOrigin.sol @@ -0,0 +1,65 @@ +//@compile-flags: --only-lint tx-origin +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract TxOrigin { + address public owner; + mapping(address => bool) public allowed; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(tx.origin == owner, "not owner"); //~WARN: `tx.origin` should not be used for authorization + _; + } + + function guardedByIf() external view { + if (tx.origin != owner) { //~WARN: `tx.origin` should not be used for authorization + revert("not owner"); + } + } + + function guardedByPredicate() external view { + assert(isOwner(tx.origin)); //~WARN: `tx.origin` should not be used for authorization + } + + function guardedByWhile() external view { + while (tx.origin == owner) { //~WARN: `tx.origin` should not be used for authorization + break; + } + } + + function guardedByFor() external view { + for (; tx.origin == owner;) { //~WARN: `tx.origin` should not be used for authorization + break; + } + } + + function guardedByDoWhile() external view { + do { + } while (tx.origin == owner); //~WARN: `tx.origin` should not be used for authorization + } + + function guardedByMapping() external view { + require(allowed[tx.origin], "not allowed"); //~WARN: `tx.origin` should not be used for authorization + require(allowed[tx.origin] == true, "not allowed"); //~WARN: `tx.origin` should not be used for authorization + } + + function guardedByTernary() external view { + require(tx.origin == owner ? true : false, "not owner"); //~WARN: `tx.origin` should not be used for authorization + } + + function readForLogging() external view returns (address) { + return tx.origin; + } + + function explicitSenderCheck() external view { + require(msg.sender == owner, "not owner"); + } + + function isOwner(address account) internal view returns (bool) { + return account == owner; + } +} diff --git a/crates/lint/testdata/TxOrigin.stderr b/crates/lint/testdata/TxOrigin.stderr new file mode 100644 index 0000000000000..7c2e70225b76d --- /dev/null +++ b/crates/lint/testdata/TxOrigin.stderr @@ -0,0 +1,72 @@ +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(tx.origin == owner, "not owner"); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ if (tx.origin != owner) { + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ assert(isOwner(tx.origin)); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ while (tx.origin == owner) { + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ for (; tx.origin == owner;) { + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ } while (tx.origin == owner); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(allowed[tx.origin], "not allowed"); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(allowed[tx.origin] == true, "not allowed"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(tx.origin == owner ? true : false, "not owner"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + diff --git a/crates/lint/testdata/UncheckedCall.stderr b/crates/lint/testdata/UncheckedCall.stderr index afb8ade4ea89b..8a8a9fa9b5e17 100644 --- a/crates/lint/testdata/UncheckedCall.stderr +++ b/crates/lint/testdata/UncheckedCall.stderr @@ -4,7 +4,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.call(data); │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -12,7 +12,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.call{value: value}(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -20,7 +20,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.delegatecall(data); │ ━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -28,7 +28,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.staticcall(data); │ ━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -36,7 +36,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target1.call(""); │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -44,7 +44,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target2.delegatecall(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -52,7 +52,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ (, bytes memory data) = target.call(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -60,5 +60,5 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ (, existingData) = target.call(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call diff --git a/crates/lint/testdata/UncheckedTransferERC20.stderr b/crates/lint/testdata/UncheckedTransferERC20.stderr index 733d22ce610d1..2c2caa69e7215 100644 --- a/crates/lint/testdata/UncheckedTransferERC20.stderr +++ b/crates/lint/testdata/UncheckedTransferERC20.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20Wrapper { │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract UncheckedTransfer { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ library Currency { │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -36,7 +36,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract UncheckedTransferUsingCurrencyLib { │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -44,7 +44,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ IERC20(address(token)).transfer(to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -52,7 +52,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ token.transfer(to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -60,7 +60,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ … IERC20(address(token)).transferFrom(from, to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -68,7 +68,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ token.transferFrom(from, to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -76,7 +76,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ … IERC20(address(token)).transfer(recipients[i], amounts[i]); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -84,5 +84,5 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ token.transfer(recipients[i], amounts[i]); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer diff --git a/crates/lint/testdata/UnsafeCheatcodes.stderr b/crates/lint/testdata/UnsafeCheatcodes.stderr index e66a4d72c70de..5b8b429942e80 100644 --- a/crates/lint/testdata/UnsafeCheatcodes.stderr +++ b/crates/lint/testdata/UnsafeCheatcodes.stderr @@ -4,7 +4,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -12,7 +12,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.readFile("test.txt"); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -20,7 +20,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.readLine("test.txt"); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -28,7 +28,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.writeFile("test.txt", "data"); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -36,7 +36,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.writeLine("test.txt", "data"); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -44,7 +44,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.removeFile("test.txt"); │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -52,7 +52,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.closeFile("test.txt"); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -60,7 +60,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.setEnv("KEY", "value"); │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -68,7 +68,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.deriveKey("mnemonic", 0); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -76,7 +76,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ bytes memory result = vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -84,7 +84,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.ffi(new string[](1)); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -92,7 +92,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.setEnv("KEY", "value"); │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -100,5 +100,5 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.readFile("test.txt"); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode diff --git a/crates/lint/testdata/UnsafeTypecast.stderr b/crates/lint/testdata/UnsafeTypecast.stderr index b3e0334d63d43..d909b90973e00 100644 --- a/crates/lint/testdata/UnsafeTypecast.stderr +++ b/crates/lint/testdata/UnsafeTypecast.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract UnsafeTypecast { │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract Repros { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -26,7 +26,7 @@ LL │ uint248 b = uint248(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -40,7 +40,7 @@ LL │ uint240 c = uint240(b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -54,7 +54,7 @@ LL │ uint232 d = uint232(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -68,7 +68,7 @@ LL │ uint224 e = uint224(d); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -82,7 +82,7 @@ LL │ uint216 f = uint216(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -96,7 +96,7 @@ LL │ uint208 g = uint208(f); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -110,7 +110,7 @@ LL │ uint200 h = uint200(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -124,7 +124,7 @@ LL │ uint192 i = uint192(h); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -138,7 +138,7 @@ LL │ uint184 j = uint184(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -152,7 +152,7 @@ LL │ uint176 k = uint176(j); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -166,7 +166,7 @@ LL │ uint168 l = uint168(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -180,7 +180,7 @@ LL │ uint160 m = uint160(l); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -194,7 +194,7 @@ LL │ uint152 n = uint152(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -208,7 +208,7 @@ LL │ uint144 o = uint144(n); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -222,7 +222,7 @@ LL │ uint136 p = uint136(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -236,7 +236,7 @@ LL │ uint128 q = uint128(p); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -250,7 +250,7 @@ LL │ uint120 r = uint120(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -264,7 +264,7 @@ LL │ uint112 s = uint112(r); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -278,7 +278,7 @@ LL │ uint104 t = uint104(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -292,7 +292,7 @@ LL │ uint96 u = uint96(t); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -306,7 +306,7 @@ LL │ uint88 v = uint88(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -320,7 +320,7 @@ LL │ uint80 w = uint80(v); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -334,7 +334,7 @@ LL │ uint72 x = uint72(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -348,7 +348,7 @@ LL │ uint64 y = uint64(x); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -362,7 +362,7 @@ LL │ uint56 z = uint56(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -376,7 +376,7 @@ LL │ uint48 A = uint48(z); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -390,7 +390,7 @@ LL │ uint40 B = uint40(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -404,7 +404,7 @@ LL │ uint32 C = uint32(B); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -418,7 +418,7 @@ LL │ uint24 D = uint24(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -432,7 +432,7 @@ LL │ uint16 E = uint16(D); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -446,7 +446,7 @@ LL │ uint8 F = uint8(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -460,7 +460,7 @@ LL │ int248 b = int248(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -474,7 +474,7 @@ LL │ int240 c = int240(b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -488,7 +488,7 @@ LL │ int232 d = int232(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -502,7 +502,7 @@ LL │ int224 e = int224(d); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -516,7 +516,7 @@ LL │ int216 f = int216(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -530,7 +530,7 @@ LL │ int208 g = int208(f); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -544,7 +544,7 @@ LL │ int200 h = int200(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -558,7 +558,7 @@ LL │ int192 i = int192(h); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -572,7 +572,7 @@ LL │ int184 j = int184(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -586,7 +586,7 @@ LL │ int176 k = int176(j); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -600,7 +600,7 @@ LL │ int168 l = int168(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -614,7 +614,7 @@ LL │ int160 m = int160(l); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -628,7 +628,7 @@ LL │ int152 n = int152(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -642,7 +642,7 @@ LL │ int144 o = int144(n); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -656,7 +656,7 @@ LL │ int136 p = int136(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -670,7 +670,7 @@ LL │ int128 q = int128(p); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -684,7 +684,7 @@ LL │ int120 r = int120(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -698,7 +698,7 @@ LL │ int112 s = int112(r); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -712,7 +712,7 @@ LL │ int104 t = int104(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -726,7 +726,7 @@ LL │ int96 u = int96(t); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -740,7 +740,7 @@ LL │ int88 v = int88(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -754,7 +754,7 @@ LL │ int80 w = int80(v); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -768,7 +768,7 @@ LL │ int72 x = int72(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -782,7 +782,7 @@ LL │ int64 y = int64(x); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -796,7 +796,7 @@ LL │ int56 z = int56(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -810,7 +810,7 @@ LL │ int48 A = int48(z); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -824,7 +824,7 @@ LL │ int40 B = int40(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -838,7 +838,7 @@ LL │ int32 C = int32(B); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -852,7 +852,7 @@ LL │ int24 D = int24(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -866,7 +866,7 @@ LL │ int16 E = int16(D); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -880,7 +880,7 @@ LL │ int8 F = int8(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -894,7 +894,7 @@ LL │ bytes31 b = bytes31(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -908,7 +908,7 @@ LL │ bytes30 c = bytes30(b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -922,7 +922,7 @@ LL │ bytes29 d = bytes29(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -936,7 +936,7 @@ LL │ bytes28 e = bytes28(d); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -950,7 +950,7 @@ LL │ bytes27 f = bytes27(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -964,7 +964,7 @@ LL │ bytes26 g = bytes26(f); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -978,7 +978,7 @@ LL │ bytes25 h = bytes25(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -992,7 +992,7 @@ LL │ bytes24 i = bytes24(h); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1006,7 +1006,7 @@ LL │ bytes23 j = bytes23(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1020,7 +1020,7 @@ LL │ bytes22 k = bytes22(j); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1034,7 +1034,7 @@ LL │ bytes21 l = bytes21(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1048,7 +1048,7 @@ LL │ bytes20 m = bytes20(l); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1062,7 +1062,7 @@ LL │ bytes19 n = bytes19(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1076,7 +1076,7 @@ LL │ bytes18 o = bytes18(n); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1090,7 +1090,7 @@ LL │ bytes17 p = bytes17(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1104,7 +1104,7 @@ LL │ bytes16 q = bytes16(p); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1118,7 +1118,7 @@ LL │ bytes15 r = bytes15(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1132,7 +1132,7 @@ LL │ bytes14 s = bytes14(r); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1146,7 +1146,7 @@ LL │ bytes13 t = bytes13(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1160,7 +1160,7 @@ LL │ bytes12 u = bytes12(t); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1174,7 +1174,7 @@ LL │ bytes11 v = bytes11(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1188,7 +1188,7 @@ LL │ bytes10 w = bytes10(v); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1202,7 +1202,7 @@ LL │ bytes9 x = bytes9(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1216,7 +1216,7 @@ LL │ bytes8 y = bytes8(x); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1230,7 +1230,7 @@ LL │ bytes7 z = bytes7(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1244,7 +1244,7 @@ LL │ bytes6 A = bytes6(z); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1258,7 +1258,7 @@ LL │ bytes5 B = bytes5(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1272,7 +1272,7 @@ LL │ bytes4 C = bytes4(B); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1286,7 +1286,7 @@ LL │ bytes3 D = bytes3(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1300,7 +1300,7 @@ LL │ bytes2 E = bytes2(D); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1314,7 +1314,7 @@ LL │ bytes1 F = bytes1(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1328,7 +1328,7 @@ LL │ int256 b = int256(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1342,7 +1342,7 @@ LL │ int248 d = int248(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1356,7 +1356,7 @@ LL │ int240 f = int240(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1370,7 +1370,7 @@ LL │ int232 h = int232(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1384,7 +1384,7 @@ LL │ int224 j = int224(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1398,7 +1398,7 @@ LL │ int216 l = int216(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1412,7 +1412,7 @@ LL │ int208 n = int208(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1426,7 +1426,7 @@ LL │ int200 p = int200(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1440,7 +1440,7 @@ LL │ int192 r = int192(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1454,7 +1454,7 @@ LL │ int184 t = int184(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1468,7 +1468,7 @@ LL │ int176 v = int176(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1482,7 +1482,7 @@ LL │ int168 x = int168(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1496,7 +1496,7 @@ LL │ int160 z = int160(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1510,7 +1510,7 @@ LL │ int152 B = int152(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1524,7 +1524,7 @@ LL │ int144 D = int144(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1538,7 +1538,7 @@ LL │ int136 F = int136(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1552,7 +1552,7 @@ LL │ int128 H = int128(G); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1566,7 +1566,7 @@ LL │ int120 J = int120(I); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1580,7 +1580,7 @@ LL │ int112 L = int112(K); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1594,7 +1594,7 @@ LL │ int104 N = int104(M); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1608,7 +1608,7 @@ LL │ int96 P = int96(O); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1622,7 +1622,7 @@ LL │ int88 R = int88(Q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1636,7 +1636,7 @@ LL │ int80 T = int80(S); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1650,7 +1650,7 @@ LL │ int72 V = int72(U); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1664,7 +1664,7 @@ LL │ int64 X = int64(W); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1678,7 +1678,7 @@ LL │ int56 Z = int56(Y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1692,7 +1692,7 @@ LL │ int48 BB = int48(AA); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1706,7 +1706,7 @@ LL │ int40 DD = int40(CC); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1720,7 +1720,7 @@ LL │ int32 FF = int32(EE); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1734,7 +1734,7 @@ LL │ int24 HH = int24(GG); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1748,7 +1748,7 @@ LL │ int16 JJ = int16(II); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1762,7 +1762,7 @@ LL │ int8 LL = int8(KK); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1776,7 +1776,7 @@ LL │ uint256 b = uint256(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1790,7 +1790,7 @@ LL │ uint248 d = uint248(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1804,7 +1804,7 @@ LL │ uint240 f = uint240(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1818,7 +1818,7 @@ LL │ uint232 h = uint232(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1832,7 +1832,7 @@ LL │ uint224 j = uint224(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1846,7 +1846,7 @@ LL │ uint216 l = uint216(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1860,7 +1860,7 @@ LL │ uint208 n = uint208(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1874,7 +1874,7 @@ LL │ uint200 p = uint200(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1888,7 +1888,7 @@ LL │ uint192 r = uint192(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1902,7 +1902,7 @@ LL │ uint184 t = uint184(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1916,7 +1916,7 @@ LL │ uint176 v = uint176(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1930,7 +1930,7 @@ LL │ uint168 x = uint168(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1944,7 +1944,7 @@ LL │ uint160 z = uint160(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1958,7 +1958,7 @@ LL │ uint152 B = uint152(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1972,7 +1972,7 @@ LL │ uint144 D = uint144(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1986,7 +1986,7 @@ LL │ uint136 F = uint136(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2000,7 +2000,7 @@ LL │ uint128 H = uint128(G); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2014,7 +2014,7 @@ LL │ uint120 J = uint120(I); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2028,7 +2028,7 @@ LL │ uint112 L = uint112(K); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2042,7 +2042,7 @@ LL │ uint104 N = uint104(M); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2056,7 +2056,7 @@ LL │ uint96 P = uint96(O); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2070,7 +2070,7 @@ LL │ uint88 R = uint88(Q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2084,7 +2084,7 @@ LL │ uint80 T = uint80(S); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2098,7 +2098,7 @@ LL │ uint72 V = uint72(U); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2112,7 +2112,7 @@ LL │ uint64 X = uint64(W); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2126,7 +2126,7 @@ LL │ uint56 Z = uint56(Y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2140,7 +2140,7 @@ LL │ uint48 BB = uint48(AA); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2154,7 +2154,7 @@ LL │ uint40 DD = uint40(CC); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2168,7 +2168,7 @@ LL │ uint32 FF = uint32(EE); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2182,7 +2182,7 @@ LL │ uint24 HH = uint24(GG); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2196,7 +2196,7 @@ LL │ uint16 JJ = uint16(II); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2210,7 +2210,7 @@ LL │ uint8 LL = uint8(KK); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2224,7 +2224,7 @@ LL │ bytes32 dataSlice = bytes32(data); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2238,7 +2238,7 @@ LL │ bytes32 strSlice = bytes32(bytes(str)); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2252,7 +2252,7 @@ LL │ uint128 aPlusB = uint128(int128(uint128(a)) + b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2266,7 +2266,7 @@ LL │ uint64 unsafe = uint64(aPlusB); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2280,7 +2280,7 @@ LL │ return uint64(uint128(int128(uint128(a)) + b)); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2294,5 +2294,5 @@ LL │ return uint64(uint128(int128(uint128(a)) + b)); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast diff --git a/crates/lint/testdata/UnusedStateVariables.stderr b/crates/lint/testdata/UnusedStateVariables.stderr index ecc9e87efc105..92a0082bb8293 100644 --- a/crates/lint/testdata/UnusedStateVariables.stderr +++ b/crates/lint/testdata/UnusedStateVariables.stderr @@ -4,7 +4,7 @@ note[could-be-immutable]: state variable could be declared immutable LL │ address usedInBoth; │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#could-be-immutable + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable note[unused-state-variables]: state variable is never used ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC @@ -12,7 +12,7 @@ note[unused-state-variables]: state variable is never used LL │ uint256 unused; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-state-variables + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables note[unused-state-variables]: state variable is never used ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC @@ -20,7 +20,7 @@ note[unused-state-variables]: state variable is never used LL │ uint256 unused; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-state-variables + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables note[unused-state-variables]: state variable is never used ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC @@ -28,7 +28,7 @@ note[unused-state-variables]: state variable is never used LL │ uint256 firstUnused; │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-state-variables + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables note[unused-state-variables]: state variable is never used ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC @@ -36,5 +36,5 @@ note[unused-state-variables]: state variable is never used LL │ uint256 secondUnused; │ ━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-state-variables + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables diff --git a/crates/lint/testdata/UnwrappedModifierLogic.stderr b/crates/lint/testdata/UnwrappedModifierLogic.stderr index 5e1bf754e60e4..dc5514c2d5e98 100644 --- a/crates/lint/testdata/UnwrappedModifierLogic.stderr +++ b/crates/lint/testdata/UnwrappedModifierLogic.stderr @@ -9,7 +9,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier multipleBeforePlaceholder() { @@ -35,7 +35,7 @@ LL │ ┃ checkInternal(msg.sender); LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier multipleAfterPlaceholder() { @@ -62,7 +62,7 @@ LL │ ┃ checkPublic(sender); LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier multipleBeforeAfterPlaceholder(address sender) { @@ -91,7 +91,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyOwner() { @@ -113,7 +113,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyRole(bytes32 role) { @@ -135,7 +135,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyRoleOrOpenRole(bytes32 role) { @@ -157,7 +157,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyRoleOrAdmin(bytes32 role, address admin) { @@ -180,7 +180,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier assign(address sender) { @@ -204,7 +204,7 @@ LL │ ┃ sender; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier uncheckedBlock(address sender) { @@ -228,7 +228,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier emitEvent(address sender) { @@ -250,7 +250,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyOwnerContract(address sender) { diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 9970616900e6b..15363a6a40bb0 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -23,10 +23,10 @@ alloy-rpc-types-eth.workspace = true alloy-serde.workspace = true alloy-signer.workspace = true alloy-evm.workspace = true -op-alloy-consensus = { workspace = true, features = ["serde", "alloy-compat"] } -op-alloy-rpc-types.workspace = true -alloy-op-evm.workspace = true -op-revm.workspace = true +op-alloy-consensus = { workspace = true, features = ["serde", "alloy-compat"], optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } +alloy-op-evm = { workspace = true, optional = true } +op-revm = { workspace = true, optional = true } revm.workspace = true serde_json.workspace = true serde = { version = "1.0", features = ["derive"] } @@ -34,3 +34,12 @@ derive_more.workspace = true tempo-primitives.workspace = true tempo-alloy.workspace = true tempo-revm.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "dep:op-alloy-consensus", + "dep:op-alloy-rpc-types", + "dep:alloy-op-evm", + "dep:op-revm", +] diff --git a/crates/primitives/src/network/mod.rs b/crates/primitives/src/network/mod.rs index 7000b580b6a73..0b840185b0383 100644 --- a/crates/primitives/src/network/mod.rs +++ b/crates/primitives/src/network/mod.rs @@ -1,12 +1,20 @@ use alloy_network::Network; +#[cfg(feature = "optimism")] +mod optimism; mod receipt; use alloy_provider::fillers::{ BlobGasFiller, ChainIdFiller, GasFiller, JoinFill, NonceFiller, RecommendedFillers, }; +#[cfg(feature = "optimism")] +pub use optimism::FoundryTransactionResponse; pub use receipt::*; +/// Default JSON-RPC transaction response when the `optimism` feature is disabled. +#[cfg(not(feature = "optimism"))] +pub type FoundryTransactionResponse = alloy_rpc_types_eth::Transaction; + /// Foundry network type. /// /// This network type supports Foundry-specific transaction types, including @@ -36,7 +44,7 @@ impl Network for FoundryNetwork { type TransactionRequest = crate::FoundryTransactionRequest; - type TransactionResponse = op_alloy_rpc_types::Transaction; + type TransactionResponse = FoundryTransactionResponse; type ReceiptResponse = crate::FoundryTxReceipt; diff --git a/crates/primitives/src/network/optimism.rs b/crates/primitives/src/network/optimism.rs new file mode 100644 index 0000000000000..aff30a755663f --- /dev/null +++ b/crates/primitives/src/network/optimism.rs @@ -0,0 +1,47 @@ +//! OP-stack-specific helpers and type aliases used by [`super::FoundryNetwork`] and +//! [`super::FoundryTxReceipt`]. + +use alloy_consensus::{Receipt, ReceiptWithBloom, TxReceipt}; +use alloy_primitives::U64; +use alloy_rpc_types::Log; +use alloy_serde::OtherFields; +use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom}; + +use crate::FoundryReceiptEnvelope; + +/// JSON-RPC transaction response type used by [`super::FoundryNetwork`]. +pub type FoundryTransactionResponse = op_alloy_rpc_types::Transaction; + +/// Build a [`FoundryReceiptEnvelope::Deposit`] from a `ReceiptWithBloom` plus the OP +/// deposit-specific fields decoded from the [`OtherFields`] of an `AnyTransactionReceipt`. +pub(super) fn build_deposit_receipt_envelope( + receipt_with_bloom: ReceiptWithBloom>, + other: &OtherFields, +) -> FoundryReceiptEnvelope { + // These fields may not be present in all receipts, so missing/invalid values are None. + let deposit_nonce = other + .get_deserialized::("depositNonce") + .transpose() + .ok() + .flatten() + .map(|v| v.to::()); + let deposit_receipt_version = other + .get_deserialized::("depositReceiptVersion") + .transpose() + .ok() + .flatten() + .map(|v| v.to::()); + + FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom { + receipt: OpDepositReceipt { + inner: Receipt { + status: alloy_consensus::Eip658Value::Eip658(receipt_with_bloom.status()), + cumulative_gas_used: receipt_with_bloom.cumulative_gas_used(), + logs: receipt_with_bloom.receipt.logs, + }, + deposit_nonce, + deposit_receipt_version, + }, + logs_bloom: receipt_with_bloom.logs_bloom, + }) +} diff --git a/crates/primitives/src/network/receipt.rs b/crates/primitives/src/network/receipt.rs index b727b4c39b345..6b01f9eaa9ee9 100644 --- a/crates/primitives/src/network/receipt.rs +++ b/crates/primitives/src/network/receipt.rs @@ -1,13 +1,13 @@ -use alloy_consensus::{Receipt, TxReceipt}; use alloy_network::{AnyReceiptEnvelope, AnyTransactionReceipt, ReceiptResponse}; -use alloy_primitives::{Address, B256, BlockHash, TxHash, U64}; +use alloy_primitives::{Address, B256, BlockHash, TxHash}; use alloy_rpc_types::{ConversionError, Log, TransactionReceipt}; use alloy_serde::WithOtherFields; use derive_more::AsRef; -use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom}; use serde::{Deserialize, Serialize}; use tempo_primitives::TEMPO_TX_TYPE_ID; +#[cfg(feature = "optimism")] +use super::optimism::build_deposit_receipt_envelope; use crate::FoundryReceiptEnvelope; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, AsRef)] @@ -144,38 +144,8 @@ impl TryFrom for FoundryTxReceipt { 0x03 => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom), 0x04 => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom), TEMPO_TX_TYPE_ID => FoundryReceiptEnvelope::Tempo(receipt_with_bloom), - 0x7E => { - // Construct the deposit receipt, extracting optional deposit fields - // These fields may not be present in all receipts, so missing/invalid - // values are None - let deposit_nonce = other - .get_deserialized::("depositNonce") - .transpose() - .ok() - .flatten() - .map(|v| v.to::()); - let deposit_receipt_version = other - .get_deserialized::("depositReceiptVersion") - .transpose() - .ok() - .flatten() - .map(|v| v.to::()); - - FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom { - receipt: OpDepositReceipt { - inner: Receipt { - status: alloy_consensus::Eip658Value::Eip658( - receipt_with_bloom.status(), - ), - cumulative_gas_used: receipt_with_bloom.cumulative_gas_used(), - logs: receipt_with_bloom.receipt.logs, - }, - deposit_nonce, - deposit_receipt_version, - }, - logs_bloom: receipt_with_bloom.logs_bloom, - }) - } + #[cfg(feature = "optimism")] + 0x7E => build_deposit_receipt_envelope(receipt_with_bloom, &other), _ => { let tx_type = r#type; return Err(ConversionError::Custom(format!( diff --git a/crates/primitives/src/transaction/envelope.rs b/crates/primitives/src/transaction/envelope.rs index ab4aafcc294c0..0a009a1931f06 100644 --- a/crates/primitives/src/transaction/envelope.rs +++ b/crates/primitives/src/transaction/envelope.rs @@ -1,6 +1,7 @@ +#[cfg(feature = "optimism")] +use alloy_consensus::{Sealed, Transaction as _}; use alloy_consensus::{ - Sealed, Signed, Transaction as _, TransactionEnvelope, TxEip1559, TxEip2930, TxEnvelope, - TxLegacy, TxType, Typed2718, + Signed, TransactionEnvelope, TxEip1559, TxEip2930, TxEnvelope, TxLegacy, TxType, Typed2718, crypto::RecoveryError, transaction::{ SignerRecoverable, TxEip7702, TxHashRef, @@ -9,14 +10,10 @@ use alloy_consensus::{ }; use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse}; -use alloy_op_evm::OpTx; use alloy_primitives::{Address, B256, Bytes, TxHash}; use alloy_rpc_types::ConversionError; -use op_alloy_consensus::{ - DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait, POST_EXEC_TX_TYPE_ID, TxDeposit, - TxPostExec, -}; -use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; +#[cfg(feature = "optimism")] +use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, POST_EXEC_TX_TYPE_ID, TxDeposit, TxPostExec}; use revm::context::TxEnv; use tempo_primitives::{AASigned, TempoTransaction}; use tempo_revm::TempoTxEnv; @@ -57,9 +54,11 @@ pub enum FoundryTxEnvelope { /// OP stack deposit transaction. /// /// See . + #[cfg(feature = "optimism")] #[envelope(ty = 126)] Deposit(Sealed), /// OP stack post-execution synthetic transaction. + #[cfg(feature = "optimism")] #[envelope(ty = 0x7D)] PostExec(Sealed), /// Tempo transaction type. @@ -80,7 +79,9 @@ impl FoundryTxEnvelope { Self::Eip1559(tx) => Ok(TxEnvelope::Eip1559(tx)), Self::Eip4844(tx) => Ok(TxEnvelope::Eip4844(tx)), Self::Eip7702(tx) => Ok(TxEnvelope::Eip7702(tx)), + #[cfg(feature = "optimism")] Self::Deposit(_) => Err(self), + #[cfg(feature = "optimism")] Self::PostExec(_) => Err(self), Self::Tempo(_) => Err(self), } @@ -109,7 +110,9 @@ impl FoundryTxEnvelope { Self::Eip1559(t) => *t.hash(), Self::Eip4844(t) => *t.hash(), Self::Eip7702(t) => *t.hash(), + #[cfg(feature = "optimism")] Self::Deposit(t) => t.tx_hash(), + #[cfg(feature = "optimism")] Self::PostExec(t) => t.tx_hash(), Self::Tempo(t) => *t.hash(), } @@ -128,7 +131,9 @@ impl FoundryTxEnvelope { Self::Eip1559(tx) => tx.recover_signer()?, Self::Eip4844(tx) => tx.recover_signer()?, Self::Eip7702(tx) => tx.recover_signer()?, + #[cfg(feature = "optimism")] Self::Deposit(tx) => tx.from, + #[cfg(feature = "optimism")] Self::PostExec(tx) => tx.inner().signer_address(), Self::Tempo(tx) => tx.signature().recover_signer(&tx.signature_hash())?, }) @@ -143,7 +148,9 @@ impl TxHashRef for FoundryTxEnvelope { Self::Eip1559(t) => t.hash(), Self::Eip4844(t) => t.hash(), Self::Eip7702(t) => t.hash(), + #[cfg(feature = "optimism")] Self::Deposit(t) => t.hash_ref(), + #[cfg(feature = "optimism")] Self::PostExec(t) => t.hash_ref(), Self::Tempo(t) => t.hash(), } @@ -160,23 +167,6 @@ impl SignerRecoverable for FoundryTxEnvelope { } } -impl OpTransactionTrait for FoundryTxEnvelope { - fn is_deposit(&self) -> bool { - matches!(self, Self::Deposit(_)) - } - - fn as_deposit(&self) -> Option<&Sealed> { - match self { - Self::Deposit(tx) => Some(tx), - _ => None, - } - } - - fn as_post_exec(&self) -> Option<&Sealed> { - if let Self::PostExec(tx) = self { Some(tx) } else { None } - } -} - impl TryFrom for TxEnvelope { type Error = FoundryTxEnvelope; @@ -197,19 +187,6 @@ impl From for FoundryTxEnvelope { } } -impl From for FoundryTxEnvelope { - fn from(tx: op_alloy_consensus::OpTxEnvelope) -> Self { - match tx { - op_alloy_consensus::OpTxEnvelope::Legacy(tx) => Self::Legacy(tx), - op_alloy_consensus::OpTxEnvelope::Eip2930(tx) => Self::Eip2930(tx), - op_alloy_consensus::OpTxEnvelope::Eip1559(tx) => Self::Eip1559(tx), - op_alloy_consensus::OpTxEnvelope::Eip7702(tx) => Self::Eip7702(tx), - op_alloy_consensus::OpTxEnvelope::Deposit(tx) => Self::Deposit(tx), - op_alloy_consensus::OpTxEnvelope::PostExec(tx) => Self::PostExec(tx), - } - } -} - impl From for FoundryTxEnvelope { fn from(tx: tempo_primitives::TempoTxEnvelope) -> Self { match tx { @@ -236,33 +213,50 @@ impl TryFrom for FoundryTxEnvelope { TxEnvelope::Eip4844(tx) => Ok(Self::Eip4844(tx)), TxEnvelope::Eip7702(tx) => Ok(Self::Eip7702(tx)), }, - AnyTxEnvelope::Unknown(mut tx) => { - // Try to convert to deposit transaction - if tx.ty() == DEPOSIT_TX_TYPE_ID { - tx.inner.fields.insert("from".to_string(), serde_json::to_value(from).unwrap()); - let deposit_tx = - tx.inner.fields.deserialize_into::().map_err(|e| { - ConversionError::Custom(format!( - "Failed to deserialize deposit tx: {e}" - )) - })?; - - return Ok(Self::Deposit(Sealed::new(deposit_tx))); + AnyTxEnvelope::Unknown(tx) => { + #[cfg(feature = "optimism")] + { + let mut tx = tx; + let _ = from; + // Try to convert to deposit transaction + if tx.ty() == DEPOSIT_TX_TYPE_ID { + tx.inner + .fields + .insert("from".to_string(), serde_json::to_value(from).unwrap()); + let deposit_tx = + tx.inner.fields.deserialize_into::().map_err(|e| { + ConversionError::Custom(format!( + "Failed to deserialize deposit tx: {e}" + )) + })?; + + return Ok(Self::Deposit(Sealed::new(deposit_tx))); + } + + if tx.ty() == POST_EXEC_TX_TYPE_ID { + let post_exec_tx = + tx.inner.fields.deserialize_into::().map_err(|e| { + ConversionError::Custom(format!( + "Failed to deserialize post-exec tx: {e}" + )) + })?; + + return Ok(Self::PostExec(Sealed::new(post_exec_tx))); + } + + let tx_type = tx.ty(); + Err(ConversionError::Custom(format!( + "Unknown transaction type: 0x{tx_type:02X}" + ))) } - - if tx.ty() == POST_EXEC_TX_TYPE_ID { - let post_exec_tx = - tx.inner.fields.deserialize_into::().map_err(|e| { - ConversionError::Custom(format!( - "Failed to deserialize post-exec tx: {e}" - )) - })?; - - return Ok(Self::PostExec(Sealed::new(post_exec_tx))); + #[cfg(not(feature = "optimism"))] + { + let _ = from; + let tx_type = tx.ty(); + Err(ConversionError::Custom(format!( + "Unknown transaction type: 0x{tx_type:02X}" + ))) } - - let tx_type = tx.ty(); - Err(ConversionError::Custom(format!("Unknown transaction type: 0x{tx_type:02X}"))) } } } @@ -276,6 +270,7 @@ impl FromRecoveredTx for TxEnv { FoundryTxEnvelope::Eip1559(signed_tx) => Self::from_recovered_tx(signed_tx, caller), FoundryTxEnvelope::Eip4844(signed_tx) => Self::from_recovered_tx(signed_tx, caller), FoundryTxEnvelope::Eip7702(signed_tx) => Self::from_recovered_tx(signed_tx, caller), + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(sealed_tx) => { let tx = sealed_tx.inner(); Self { @@ -288,6 +283,7 @@ impl FromRecoveredTx for TxEnv { ..Default::default() } } + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(sealed_tx) => { let tx = sealed_tx.inner(); Self { @@ -303,63 +299,6 @@ impl FromRecoveredTx for TxEnv { } } -impl FromRecoveredTx for OpTransaction { - fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { - match tx { - FoundryTxEnvelope::Legacy(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip2930(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip1559(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip4844(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip7702(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Deposit(sealed_tx) => { - let deposit_tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: deposit_tx.ty(), - caller, - gas_limit: deposit_tx.gas_limit, - kind: deposit_tx.to, - value: deposit_tx.value, - data: deposit_tx.input.clone(), - ..Default::default() - }; - let deposit = DepositTransactionParts { - source_hash: deposit_tx.source_hash, - mint: Some(deposit_tx.mint), - is_system_transaction: deposit_tx.is_system_transaction, - }; - Self { base, enveloped_tx: None, deposit } - } - FoundryTxEnvelope::PostExec(sealed_tx) => { - let tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: tx.ty(), - caller, - kind: tx.kind(), - data: tx.input.clone(), - ..Default::default() - }; - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), - } - } -} - impl FromTxWithEncoded for TxEnv { fn from_encoded_tx(tx: &FoundryTxEnvelope, sender: Address, _encoded: Bytes) -> Self { Self::from_recovered_tx(tx, sender) @@ -384,7 +323,9 @@ impl FromRecoveredTx for TempoTxEnv { FoundryTxEnvelope::Eip7702(signed_tx) => { Self::from(TxEnv::from_recovered_tx(signed_tx, caller)) } + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(_) => unreachable!("Deposit tx in Tempo context"), + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(_) => unreachable!("Post-exec tx in Tempo context"), FoundryTxEnvelope::Tempo(aa_signed) => Self::from_recovered_tx(aa_signed, caller), } @@ -397,75 +338,6 @@ impl FromTxWithEncoded for TempoTxEnv { } } -impl FromRecoveredTx for OpTx { - fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { - Self(OpTransaction::::from_recovered_tx(tx, caller)) - } -} - -impl FromTxWithEncoded for OpTx { - fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { - Self(OpTransaction::::from_encoded_tx(tx, caller, encoded)) - } -} - -impl FromTxWithEncoded for OpTransaction { - fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { - match tx { - FoundryTxEnvelope::Legacy(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip2930(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip1559(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip4844(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip7702(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Deposit(sealed_tx) => { - let deposit_tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: deposit_tx.ty(), - caller, - gas_limit: deposit_tx.gas_limit, - kind: deposit_tx.to, - value: deposit_tx.value, - data: deposit_tx.input.clone(), - ..Default::default() - }; - let deposit = DepositTransactionParts { - source_hash: deposit_tx.source_hash, - mint: Some(deposit_tx.mint), - is_system_transaction: deposit_tx.is_system_transaction, - }; - Self { base, enveloped_tx: Some(encoded), deposit } - } - FoundryTxEnvelope::PostExec(sealed_tx) => { - let tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: tx.ty(), - caller, - kind: tx.kind(), - data: tx.input.clone(), - ..Default::default() - }; - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), - } - } -} - impl std::fmt::Display for FoundryTxType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -474,7 +346,9 @@ impl std::fmt::Display for FoundryTxType { Self::Eip1559 => write!(f, "eip1559"), Self::Eip4844 => write!(f, "eip4844"), Self::Eip7702 => write!(f, "eip7702"), + #[cfg(feature = "optimism")] Self::Deposit => write!(f, "deposit"), + #[cfg(feature = "optimism")] Self::PostExec => write!(f, "post-exec"), Self::Tempo => write!(f, "tempo"), } @@ -501,7 +375,9 @@ impl From for FoundryTypedTx { FoundryTxEnvelope::Eip1559(signed_tx) => Self::Eip1559(signed_tx.strip_signature()), FoundryTxEnvelope::Eip4844(signed_tx) => Self::Eip4844(signed_tx.strip_signature()), FoundryTxEnvelope::Eip7702(signed_tx) => Self::Eip7702(signed_tx.strip_signature()), + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(sealed_tx) => Self::Deposit(sealed_tx.into_inner()), + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(sealed_tx) => Self::PostExec(sealed_tx.into_inner()), FoundryTxEnvelope::Tempo(signed_tx) => Self::Tempo(signed_tx.strip_signature()), } @@ -609,28 +485,6 @@ mod tests { assert_eq!(from, address!("0xA83C816D4f9b2783761a22BA6FADB0eB0606D7B2")); } - #[test] - fn test_decode_encode_deposit_tx() { - // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 - let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" - .parse::() - .unwrap(); - - // https://sepolia-optimism.etherscan.io/getRawTx?tx=0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 - let raw_tx = alloy_primitives::hex::decode( - "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", - ) - .unwrap(); - let dep_tx = FoundryTxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); - - let mut encoded = Vec::new(); - dep_tx.encode_2718(&mut encoded); - - assert_eq!(raw_tx, encoded); - - assert_eq!(tx_hash, dep_tx.hash()); - } - #[test] fn can_recover_sender_not_normalized() { let bytes = hex::decode("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); @@ -707,11 +561,6 @@ mod tests { assert_eq!(tx_env.caller, sender); assert_eq!(tx_env.gas_limit, 0x5208); assert_eq!(tx_env.gas_price, 1); - - // Test OpTransaction conversion via FromRecoveredTx trait - let op_tx = OpTransaction::::from_recovered_tx(&typed_tx, sender); - assert_eq!(op_tx.base.caller, sender); - assert_eq!(op_tx.base.gas_limit, 0x5208); } // Test vector from Tempo testnet: diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 7dccf3e30752f..18f39c437bfbc 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,7 +1,11 @@ mod envelope; +#[cfg(feature = "optimism")] +mod optimism; mod receipt; mod request; pub use envelope::{FoundryTxEnvelope, FoundryTxType, FoundryTypedTx}; +#[cfg(feature = "optimism")] +pub use optimism::get_deposit_tx_parts; pub use receipt::FoundryReceiptEnvelope; -pub use request::{FoundryTransactionRequest, get_deposit_tx_parts}; +pub use request::FoundryTransactionRequest; diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs new file mode 100644 index 0000000000000..5952b5d6a9020 --- /dev/null +++ b/crates/primitives/src/transaction/optimism.rs @@ -0,0 +1,300 @@ +//! OP-stack-specific impls for [`FoundryTxEnvelope`] and [`FoundryTransactionRequest`]. + +use alloy_consensus::{Sealed, Transaction as _, Typed2718}; +use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; +use alloy_op_evm::OpTx; +use alloy_primitives::{Address, B256, Bytes, U256}; +use alloy_serde::OtherFields; +use op_alloy_consensus::{ + OpDepositReceipt, OpDepositReceiptWithBloom, OpTransaction as OpTransactionTrait, OpTxEnvelope, + TxDeposit, TxPostExec, +}; +use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; +use revm::context::TxEnv; + +use super::{FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope}; + +impl OpTransactionTrait for FoundryTxEnvelope { + fn is_deposit(&self) -> bool { + matches!(self, Self::Deposit(_)) + } + + fn as_deposit(&self) -> Option<&Sealed> { + match self { + Self::Deposit(tx) => Some(tx), + _ => None, + } + } + + fn as_post_exec(&self) -> Option<&Sealed> { + if let Self::PostExec(tx) = self { Some(tx) } else { None } + } +} + +impl From for FoundryTxEnvelope { + fn from(tx: OpTxEnvelope) -> Self { + match tx { + OpTxEnvelope::Legacy(tx) => Self::Legacy(tx), + OpTxEnvelope::Eip2930(tx) => Self::Eip2930(tx), + OpTxEnvelope::Eip1559(tx) => Self::Eip1559(tx), + OpTxEnvelope::Eip7702(tx) => Self::Eip7702(tx), + OpTxEnvelope::Deposit(tx) => Self::Deposit(tx), + OpTxEnvelope::PostExec(tx) => Self::PostExec(tx), + } + } +} + +impl FromRecoveredTx for OpTransaction { + fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { + match tx { + FoundryTxEnvelope::Legacy(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip2930(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip1559(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip4844(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip7702(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Deposit(sealed_tx) => { + let deposit_tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: deposit_tx.ty(), + caller, + gas_limit: deposit_tx.gas_limit, + kind: deposit_tx.to, + value: deposit_tx.value, + data: deposit_tx.input.clone(), + ..Default::default() + }; + let deposit = DepositTransactionParts { + source_hash: deposit_tx.source_hash, + mint: Some(deposit_tx.mint), + is_system_transaction: deposit_tx.is_system_transaction, + }; + Self { base, enveloped_tx: None, deposit } + } + FoundryTxEnvelope::PostExec(sealed_tx) => { + let tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: tx.ty(), + caller, + kind: tx.kind(), + data: tx.input.clone(), + ..Default::default() + }; + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), + } + } +} + +impl FromRecoveredTx for OpTx { + fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { + Self(OpTransaction::::from_recovered_tx(tx, caller)) + } +} + +impl FromTxWithEncoded for OpTx { + fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { + Self(OpTransaction::::from_encoded_tx(tx, caller, encoded)) + } +} + +impl FromTxWithEncoded for OpTransaction { + fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { + match tx { + FoundryTxEnvelope::Legacy(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip2930(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip1559(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip4844(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip7702(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Deposit(sealed_tx) => { + let deposit_tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: deposit_tx.ty(), + caller, + gas_limit: deposit_tx.gas_limit, + kind: deposit_tx.to, + value: deposit_tx.value, + data: deposit_tx.input.clone(), + ..Default::default() + }; + let deposit = DepositTransactionParts { + source_hash: deposit_tx.source_hash, + mint: Some(deposit_tx.mint), + is_system_transaction: deposit_tx.is_system_transaction, + }; + Self { base, enveloped_tx: Some(encoded), deposit } + } + FoundryTxEnvelope::PostExec(sealed_tx) => { + let tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: tx.ty(), + caller, + kind: tx.kind(), + data: tx.input.clone(), + ..Default::default() + }; + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), + } + } +} + +impl From> for FoundryTransactionRequest { + fn from(tx: op_alloy_rpc_types::Transaction) -> Self { + tx.inner.into_inner().into() + } +} + +/// Converts `OtherFields` to `DepositTransactionParts`, produces error with missing fields. +pub fn get_deposit_tx_parts( + other: &OtherFields, +) -> Result> { + let mut missing = Vec::new(); + let source_hash = + other.get_deserialized::("sourceHash").transpose().ok().flatten().unwrap_or_else( + || { + missing.push("sourceHash"); + Default::default() + }, + ); + let mint = other + .get_deserialized::("mint") + .transpose() + .unwrap_or_else(|_| { + missing.push("mint"); + Default::default() + }) + .map(|value| value.to::()); + let is_system_transaction = + other.get_deserialized::("isSystemTx").transpose().ok().flatten().unwrap_or_else( + || { + missing.push("isSystemTx"); + Default::default() + }, + ); + if missing.is_empty() { + Ok(DepositTransactionParts { source_hash, mint, is_system_transaction }) + } else { + Err(missing) + } +} + +/// OP-stack-specific accessors on [`FoundryReceiptEnvelope`]. +impl FoundryReceiptEnvelope { + /// Return the receipt's deposit_nonce if it is a deposit receipt. + pub fn deposit_nonce(&self) -> Option { + self.as_deposit_receipt().and_then(|r| r.deposit_nonce) + } + + /// Return the receipt's deposit version if it is a deposit receipt. + pub fn deposit_receipt_version(&self) -> Option { + self.as_deposit_receipt().and_then(|r| r.deposit_receipt_version) + } + + /// Returns the deposit receipt if it is a deposit receipt. + pub const fn as_deposit_receipt_with_bloom(&self) -> Option<&OpDepositReceiptWithBloom> { + match self { + Self::Deposit(t) => Some(t), + _ => None, + } + } + + /// Returns the deposit receipt if it is a deposit receipt. + pub const fn as_deposit_receipt(&self) -> Option<&OpDepositReceipt> { + match self { + Self::Deposit(t) => Some(&t.receipt), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use alloy_network::eip2718::Encodable2718; + use alloy_primitives::TxHash; + use alloy_rlp::Decodable; + + use super::*; + + #[test] + fn test_from_recovered_tx_legacy_op() { + use alloy_consensus::transaction::SignerRecoverable; + + let tx = r#" + { + "type": "0x0", + "chainId": "0x1", + "nonce": "0x0", + "gas": "0x5208", + "gasPrice": "0x1", + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "value": "0x1", + "input": "0x", + "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0", + "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd", + "v": "0x1b", + "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515" + }"#; + + let typed_tx: FoundryTxEnvelope = serde_json::from_str(tx).unwrap(); + let sender = typed_tx.recover_signer().unwrap(); + + // Test OpTransaction conversion via FromRecoveredTx trait + let op_tx = OpTransaction::::from_recovered_tx(&typed_tx, sender); + assert_eq!(op_tx.base.caller, sender); + assert_eq!(op_tx.base.gas_limit, 0x5208); + } + + #[test] + fn test_decode_encode_deposit_tx() { + // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" + .parse::() + .unwrap(); + + // https://sepolia-optimism.etherscan.io/getRawTx?tx=0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let raw_tx = alloy_primitives::hex::decode( + "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", + ) + .unwrap(); + let dep_tx = FoundryTxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); + + let mut encoded = Vec::new(); + dep_tx.encode_2718(&mut encoded); + + assert_eq!(raw_tx, encoded); + + assert_eq!(tx_hash, dep_tx.hash()); + } +} diff --git a/crates/primitives/src/transaction/receipt.rs b/crates/primitives/src/transaction/receipt.rs index fe209fc72c907..78bbcd1efa2fb 100644 --- a/crates/primitives/src/transaction/receipt.rs +++ b/crates/primitives/src/transaction/receipt.rs @@ -8,6 +8,7 @@ use alloy_network::eip2718::{ use alloy_primitives::{Bloom, Log, TxHash, logs_bloom}; use alloy_rlp::{BufMut, Decodable, Encodable, Header, bytes}; use alloy_rpc_types::{BlockNumHash, trace::otterscan::OtsReceipt}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{ DEPOSIT_TX_TYPE_ID, OpDepositReceipt, OpDepositReceiptWithBloom, POST_EXEC_TX_TYPE_ID, }; @@ -29,8 +30,10 @@ pub enum FoundryReceiptEnvelope { Eip4844(ReceiptWithBloom>), #[serde(rename = "0x4", alias = "0x04")] Eip7702(ReceiptWithBloom>), + #[cfg(feature = "optimism")] #[serde(rename = "0x7D", alias = "0x7d")] PostExec(ReceiptWithBloom>), + #[cfg(feature = "optimism")] #[serde(rename = "0x7E", alias = "0x7e")] Deposit(OpDepositReceiptWithBloom), #[serde(rename = "0x76")] @@ -44,7 +47,8 @@ impl FoundryReceiptEnvelope { cumulative_gas_used: u64, logs: impl IntoIterator, tx_type: FoundryTxType, - deposit_nonce: Option, + #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] deposit_nonce: Option, + #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] deposit_receipt_version: Option, ) -> Self { let logs = logs.into_iter().collect::>(); @@ -67,9 +71,11 @@ impl FoundryReceiptEnvelope { FoundryTxType::Eip7702 => { Self::Eip7702(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) } + #[cfg(feature = "optimism")] FoundryTxType::PostExec => { Self::PostExec(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) } + #[cfg(feature = "optimism")] FoundryTxType::Deposit => { let inner = OpDepositReceiptWithBloom { receipt: OpDepositReceipt { @@ -112,13 +118,18 @@ impl FoundryReceiptEnvelope { removed: false, }) .collect::>(); + #[cfg(feature = "optimism")] + let (deposit_nonce, deposit_receipt_version) = + (self.deposit_nonce(), self.deposit_receipt_version()); + #[cfg(not(feature = "optimism"))] + let (deposit_nonce, deposit_receipt_version) = (None, None); FoundryReceiptEnvelope::::from_parts( self.status(), self.cumulative_gas_used(), logs, self.tx_type(), - self.deposit_nonce(), - self.deposit_receipt_version(), + deposit_nonce, + deposit_receipt_version, ) } } @@ -132,7 +143,9 @@ impl FoundryReceiptEnvelope { Self::Eip1559(_) => FoundryTxType::Eip1559, Self::Eip4844(_) => FoundryTxType::Eip4844, Self::Eip7702(_) => FoundryTxType::Eip7702, + #[cfg(feature = "optimism")] Self::PostExec(_) => FoundryTxType::PostExec, + #[cfg(feature = "optimism")] Self::Deposit(_) => FoundryTxType::Deposit, Self::Tempo(_) => FoundryTxType::Tempo, } @@ -158,8 +171,12 @@ impl FoundryReceiptEnvelope { Self::Eip1559(r) => FoundryReceiptEnvelope::Eip1559(r.map_logs(f)), Self::Eip4844(r) => FoundryReceiptEnvelope::Eip4844(r.map_logs(f)), Self::Eip7702(r) => FoundryReceiptEnvelope::Eip7702(r.map_logs(f)), + #[cfg(feature = "optimism")] Self::PostExec(r) => FoundryReceiptEnvelope::PostExec(r.map_logs(f)), - Self::Deposit(r) => FoundryReceiptEnvelope::Deposit(r.map_receipt(|r| r.map_logs(f))), + #[cfg(feature = "optimism")] + Self::Deposit(r) => FoundryReceiptEnvelope::Deposit( + r.map_receipt(|r: OpDepositReceipt| r.map_logs(f)), + ), Self::Tempo(r) => FoundryReceiptEnvelope::Tempo(r.map_logs(f)), } } @@ -182,38 +199,14 @@ impl FoundryReceiptEnvelope { Self::Eip1559(t) => &t.logs_bloom, Self::Eip4844(t) => &t.logs_bloom, Self::Eip7702(t) => &t.logs_bloom, + #[cfg(feature = "optimism")] Self::PostExec(t) => &t.logs_bloom, + #[cfg(feature = "optimism")] Self::Deposit(t) => &t.logs_bloom, Self::Tempo(t) => &t.logs_bloom, } } - /// Return the receipt's deposit_nonce if it is a deposit receipt. - pub fn deposit_nonce(&self) -> Option { - self.as_deposit_receipt().and_then(|r| r.deposit_nonce) - } - - /// Return the receipt's deposit version if it is a deposit receipt. - pub fn deposit_receipt_version(&self) -> Option { - self.as_deposit_receipt().and_then(|r| r.deposit_receipt_version) - } - - /// Returns the deposit receipt if it is a deposit receipt. - pub const fn as_deposit_receipt_with_bloom(&self) -> Option<&OpDepositReceiptWithBloom> { - match self { - Self::Deposit(t) => Some(t), - _ => None, - } - } - - /// Returns the deposit receipt if it is a deposit receipt. - pub const fn as_deposit_receipt(&self) -> Option<&OpDepositReceipt> { - match self { - Self::Deposit(t) => Some(&t.receipt), - _ => None, - } - } - /// Consumes the type and returns the underlying [`Receipt`]. pub fn into_receipt(self) -> Receipt { match self { @@ -222,8 +215,10 @@ impl FoundryReceiptEnvelope { | Self::Eip1559(t) | Self::Eip4844(t) | Self::Eip7702(t) - | Self::PostExec(t) | Self::Tempo(t) => t.receipt, + #[cfg(feature = "optimism")] + Self::PostExec(t) => t.receipt, + #[cfg(feature = "optimism")] Self::Deposit(t) => t.receipt.into_inner(), } } @@ -236,8 +231,10 @@ impl FoundryReceiptEnvelope { | Self::Eip1559(t) | Self::Eip4844(t) | Self::Eip7702(t) - | Self::PostExec(t) | Self::Tempo(t) => &t.receipt, + #[cfg(feature = "optimism")] + Self::PostExec(t) => &t.receipt, + #[cfg(feature = "optimism")] Self::Deposit(t) => &t.receipt.inner, } } @@ -287,7 +284,9 @@ impl Encodable for FoundryReceiptEnvelope { Self::Eip1559(r) => r.length() + 1, Self::Eip4844(r) => r.length() + 1, Self::Eip7702(r) => r.length() + 1, + #[cfg(feature = "optimism")] Self::PostExec(r) => r.length() + 1, + #[cfg(feature = "optimism")] Self::Deposit(r) => r.length() + 1, Self::Tempo(r) => r.length() + 1, _ => unreachable!("receipt already matched"), @@ -314,11 +313,13 @@ impl Encodable for FoundryReceiptEnvelope { EIP7702_TX_TYPE_ID.encode(out); r.encode(out); } + #[cfg(feature = "optimism")] Self::PostExec(r) => { Header { list: true, payload_length: payload_len }.encode(out); POST_EXEC_TX_TYPE_ID.encode(out); r.encode(out); } + #[cfg(feature = "optimism")] Self::Deposit(r) => { Header { list: true, payload_length: payload_len }.encode(out); DEPOSIT_TX_TYPE_ID.encode(out); @@ -371,18 +372,23 @@ impl Decodable for FoundryReceiptEnvelope { buf.advance(1); ::decode(buf) .map(FoundryReceiptEnvelope::Eip7702) - } else if receipt_type == POST_EXEC_TX_TYPE_ID { - buf.advance(1); - ::decode(buf) - .map(FoundryReceiptEnvelope::PostExec) - } else if receipt_type == DEPOSIT_TX_TYPE_ID { - buf.advance(1); - ::decode(buf) - .map(FoundryReceiptEnvelope::Deposit) } else if receipt_type == TEMPO_TX_TYPE_ID { buf.advance(1); ::decode(buf).map(FoundryReceiptEnvelope::Tempo) } else { + #[cfg(feature = "optimism")] + { + if receipt_type == POST_EXEC_TX_TYPE_ID { + buf.advance(1); + return ::decode(buf) + .map(FoundryReceiptEnvelope::PostExec); + } + if receipt_type == DEPOSIT_TX_TYPE_ID { + buf.advance(1); + return ::decode(buf) + .map(FoundryReceiptEnvelope::Deposit); + } + } Err(alloy_rlp::Error::Custom("invalid receipt type")) } } @@ -404,7 +410,9 @@ impl Typed2718 for FoundryReceiptEnvelope { Self::Eip1559(_) => EIP1559_TX_TYPE_ID, Self::Eip4844(_) => EIP4844_TX_TYPE_ID, Self::Eip7702(_) => EIP7702_TX_TYPE_ID, + #[cfg(feature = "optimism")] Self::PostExec(_) => POST_EXEC_TX_TYPE_ID, + #[cfg(feature = "optimism")] Self::Deposit(_) => DEPOSIT_TX_TYPE_ID, Self::Tempo(_) => TEMPO_TX_TYPE_ID, } @@ -419,7 +427,9 @@ impl Encodable2718 for FoundryReceiptEnvelope { Self::Eip1559(r) => 1 + r.length(), Self::Eip4844(r) => 1 + r.length(), Self::Eip7702(r) => 1 + r.length(), + #[cfg(feature = "optimism")] Self::PostExec(r) => 1 + r.length(), + #[cfg(feature = "optimism")] Self::Deposit(r) => 1 + r.length(), Self::Tempo(r) => 1 + r.length(), } @@ -435,8 +445,10 @@ impl Encodable2718 for FoundryReceiptEnvelope { | Self::Eip1559(r) | Self::Eip4844(r) | Self::Eip7702(r) - | Self::PostExec(r) | Self::Tempo(r) => r.encode(out), + #[cfg(feature = "optimism")] + Self::PostExec(r) => r.encode(out), + #[cfg(feature = "optimism")] Self::Deposit(r) => r.encode(out), } } @@ -444,15 +456,18 @@ impl Encodable2718 for FoundryReceiptEnvelope { impl Decodable2718 for FoundryReceiptEnvelope { fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result { - if ty == DEPOSIT_TX_TYPE_ID { - return Ok(Self::Deposit(OpDepositReceiptWithBloom::decode(buf)?)); + #[cfg(feature = "optimism")] + { + if ty == DEPOSIT_TX_TYPE_ID { + return Ok(Self::Deposit(OpDepositReceiptWithBloom::decode(buf)?)); + } + if ty == POST_EXEC_TX_TYPE_ID { + return Ok(Self::PostExec(ReceiptWithBloom::decode(buf)?)); + } } if ty == TEMPO_TX_TYPE_ID { return Ok(Self::Tempo(ReceiptWithBloom::decode(buf)?)); } - if ty == POST_EXEC_TX_TYPE_ID { - return Ok(Self::PostExec(ReceiptWithBloom::decode(buf)?)); - } match ReceiptEnvelope::typed_decode(ty, buf)? { ReceiptEnvelope::Eip2930(tx) => Ok(Self::Eip2930(tx)), ReceiptEnvelope::Eip1559(tx) => Ok(Self::Eip1559(tx)), @@ -646,8 +661,11 @@ mod tests { assert!(receipt.status()); assert_eq!(receipt.cumulative_gas_used(), 100000); assert!(receipt.logs().is_empty()); - assert!(receipt.deposit_nonce().is_none()); - assert!(receipt.deposit_receipt_version().is_none()); + #[cfg(feature = "optimism")] + { + assert!(receipt.deposit_nonce().is_none()); + assert!(receipt.deposit_receipt_version().is_none()); + } } #[test] diff --git a/crates/primitives/src/transaction/request.rs b/crates/primitives/src/transaction/request.rs index 2c4dbae8fdcd4..8ae31efbd5cb1 100644 --- a/crates/primitives/src/transaction/request.rs +++ b/crates/primitives/src/transaction/request.rs @@ -3,15 +3,19 @@ use alloy_network::{ BuildResult, NetworkTransactionBuilder, NetworkWallet, TransactionBuilder, TransactionBuilder4844, TransactionBuilderError, }; -use alloy_primitives::{Address, B256, ChainId, TxKind, U256}; +use alloy_primitives::{Address, ChainId, TxKind, U256}; use alloy_rpc_types::{AccessList, TransactionInputKind, TransactionRequest}; use alloy_serde::{OtherFields, WithOtherFields}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, POST_EXEC_TX_TYPE_ID, TxDeposit}; +#[cfg(feature = "optimism")] use op_revm::transaction::deposit::DepositTransactionParts; use serde::{Deserialize, Serialize}; use tempo_alloy::rpc::TempoTransactionRequest; use tempo_primitives::{TEMPO_TX_TYPE_ID, TempoTxType}; +#[cfg(feature = "optimism")] +use super::optimism::get_deposit_tx_parts; use super::{FoundryTxEnvelope, FoundryTxType, FoundryTypedTx}; use crate::FoundryNetwork; @@ -28,6 +32,7 @@ use crate::FoundryNetwork; #[derive(Clone, Debug, PartialEq, Eq)] pub enum FoundryTransactionRequest { Ethereum(TransactionRequest), + #[cfg(feature = "optimism")] Op(WithOtherFields), Tempo(Box), } @@ -44,6 +49,7 @@ impl FoundryTransactionRequest { pub fn into_inner(self) -> TransactionRequest { match self { Self::Ethereum(tx) => tx, + #[cfg(feature = "optimism")] Self::Op(tx) => tx.inner, Self::Tempo(tx) => tx.inner, } @@ -55,6 +61,7 @@ impl FoundryTransactionRequest { /// # Returns /// - Ok(deposit_tx_parts) if all necessary keys are present to build a deposit transaction. /// - Err(missing) if some keys are missing to build a deposit transaction. + #[cfg(feature = "optimism")] pub fn get_deposit_tx_parts(&self) -> Result> { match self { Self::Op(tx) => get_deposit_tx_parts(&tx.other), @@ -69,9 +76,11 @@ impl FoundryTransactionRequest { pub fn preferred_type(&self) -> FoundryTxType { match self { Self::Ethereum(tx) => tx.preferred_type().into(), + #[cfg(feature = "optimism")] Self::Op(tx) if tx.inner.transaction_type == Some(POST_EXEC_TX_TYPE_ID) => { FoundryTxType::PostExec } + #[cfg(feature = "optimism")] Self::Op(_) => FoundryTxType::Deposit, Self::Tempo(_) => FoundryTxType::Tempo, } @@ -95,6 +104,7 @@ impl FoundryTransactionRequest { /// Check if all necessary keys are present to build a Deposit transaction, returning a list of /// keys that are missing. + #[cfg(feature = "optimism")] pub fn complete_deposit(&self) -> Result<(), Vec<&'static str>> { self.get_deposit_tx_parts().map(|_| ()) } @@ -123,7 +133,9 @@ impl FoundryTransactionRequest { FoundryTxType::Eip1559 => self.as_ref().complete_1559(), FoundryTxType::Eip4844 => self.complete_4844(), FoundryTxType::Eip7702 => self.as_ref().complete_7702(), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => self.complete_deposit(), + #[cfg(feature = "optimism")] FoundryTxType::PostExec => Err(vec!["not implemented for post-exec tx"]), FoundryTxType::Tempo => self.complete_tempo(), } { @@ -138,9 +150,10 @@ impl FoundryTransactionRequest { /// Converts the request into a `FoundryTypedTx`, handling all Ethereum and OP-stack transaction /// types. pub fn build_typed_tx(self) -> Result { + #[cfg(feature = "optimism")] if let Ok(deposit_tx_parts) = self.get_deposit_tx_parts() { // Build deposit transaction - Ok(FoundryTypedTx::Deposit(TxDeposit { + return Ok(FoundryTypedTx::Deposit(TxDeposit { from: self.from().unwrap_or_default(), source_hash: deposit_tx_parts.source_hash, to: self.kind().unwrap_or_default(), @@ -149,8 +162,9 @@ impl FoundryTransactionRequest { gas_limit: self.gas_limit().unwrap_or_default(), is_system_transaction: deposit_tx_parts.is_system_transaction, input: self.input().cloned().unwrap_or_default(), - })) - } else if self.complete_tempo().is_ok() + })); + } + if self.complete_tempo().is_ok() && let Self::Tempo(tx_req) = self { // Build Tempo transaction @@ -192,6 +206,7 @@ impl Serialize for FoundryTransactionRequest { { match self { Self::Ethereum(tx) => tx.serialize(serializer), + #[cfg(feature = "optimism")] Self::Op(tx) => tx.serialize(serializer), Self::Tempo(tx) => tx.serialize(serializer), } @@ -211,6 +226,7 @@ impl AsRef for FoundryTransactionRequest { fn as_ref(&self) -> &TransactionRequest { match self { Self::Ethereum(tx) => tx, + #[cfg(feature = "optimism")] Self::Op(tx) => tx, Self::Tempo(tx) => tx.as_ref(), } @@ -221,6 +237,7 @@ impl AsMut for FoundryTransactionRequest { fn as_mut(&mut self) -> &mut TransactionRequest { match self { Self::Ethereum(tx) => tx, + #[cfg(feature = "optimism")] Self::Op(tx) => tx, Self::Tempo(tx) => tx.as_mut(), } @@ -244,15 +261,16 @@ impl From> for FoundryTransactionRequest { { tempo_tx_req.set_nonce_key(nonce_key); } - Self::Tempo(Box::new(tempo_tx_req)) - } else if tx.transaction_type == Some(DEPOSIT_TX_TYPE_ID) + return Self::Tempo(Box::new(tempo_tx_req)); + } + #[cfg(feature = "optimism")] + if tx.transaction_type == Some(DEPOSIT_TX_TYPE_ID) || tx.transaction_type == Some(POST_EXEC_TX_TYPE_ID) || get_deposit_tx_parts(&tx.other).is_ok() { - Self::Op(tx) - } else { - Self::Ethereum(tx.into_inner()) + return Self::Op(tx); } + Self::Ethereum(tx.into_inner()) } } @@ -264,6 +282,7 @@ impl From for FoundryTransactionRequest { FoundryTypedTx::Eip1559(tx) => Self::Ethereum(Into::::into(tx)), FoundryTypedTx::Eip4844(tx) => Self::Ethereum(Into::::into(tx)), FoundryTypedTx::Eip7702(tx) => Self::Ethereum(Into::::into(tx)), + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(tx) => { let other = OtherFields::from_iter([ ("sourceHash", tx.source_hash.to_string().into()), @@ -272,6 +291,7 @@ impl From for FoundryTransactionRequest { ]); WithOtherFields { inner: Into::::into(tx), other }.into() } + #[cfg(feature = "optimism")] FoundryTypedTx::PostExec(tx) => WithOtherFields { inner: Into::::into(tx), other: OtherFields::default(), @@ -307,8 +327,9 @@ impl From for FoundryTransactionRequest { } } -impl From> for FoundryTransactionRequest { - fn from(tx: op_alloy_rpc_types::Transaction) -> Self { +#[cfg(not(feature = "optimism"))] +impl From> for FoundryTransactionRequest { + fn from(tx: alloy_rpc_types_eth::Transaction) -> Self { tx.inner.into_inner().into() } } @@ -437,7 +458,9 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { FoundryTxType::Eip1559 => self.as_ref().complete_1559(), FoundryTxType::Eip4844 => self.as_ref().complete_4844(), FoundryTxType::Eip7702 => self.as_ref().complete_7702(), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => self.complete_deposit(), + #[cfg(feature = "optimism")] FoundryTxType::PostExec => Err(vec!["not implemented for post-exec tx"]), FoundryTxType::Tempo => self.complete_tempo(), } @@ -448,9 +471,14 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { } fn can_build(&self) -> bool { - self.as_ref().can_build() - || self.complete_deposit().is_ok() - || self.complete_tempo().is_ok() + if self.as_ref().can_build() || self.complete_tempo().is_ok() { + return true; + } + #[cfg(feature = "optimism")] + if self.complete_deposit().is_ok() { + return true; + } + false } fn output_tx_type(&self) -> FoundryTxType { @@ -465,7 +493,9 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { FoundryTxType::Eip1559 => self.as_ref().complete_1559().ok(), FoundryTxType::Eip4844 => self.as_ref().complete_4844().ok(), FoundryTxType::Eip7702 => self.as_ref().complete_7702().ok(), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => self.complete_deposit().ok(), + #[cfg(feature = "optimism")] FoundryTxType::PostExec => self.complete_type(pref).ok(), FoundryTxType::Tempo => self.complete_tempo().ok(), }?; @@ -479,11 +509,21 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { let inner = self.as_mut(); inner.transaction_type = Some(preferred_type as u8); inner.gas.is_none().then(|| inner.set_gas_limit(Default::default())); - if !matches!(preferred_type, FoundryTxType::Deposit | FoundryTxType::Tempo) { + let is_deposit = { + #[cfg(feature = "optimism")] + { + preferred_type == FoundryTxType::Deposit + } + #[cfg(not(feature = "optimism"))] + { + false + } + }; + if !is_deposit && preferred_type != FoundryTxType::Tempo { inner.trim_conflicting_keys(); inner.populate_blob_hashes(); } - if preferred_type != FoundryTxType::Deposit { + if !is_deposit { inner.nonce.is_none().then(|| inner.set_nonce(Default::default())); } if matches!(preferred_type, FoundryTxType::Legacy | FoundryTxType::Eip2930) { @@ -548,42 +588,10 @@ impl TransactionBuilder4844 for FoundryTransactionRequest { } } -/// Converts `OtherFields` to `DepositTransactionParts`, produces error with missing fields -pub fn get_deposit_tx_parts( - other: &OtherFields, -) -> Result> { - let mut missing = Vec::new(); - let source_hash = - other.get_deserialized::("sourceHash").transpose().ok().flatten().unwrap_or_else( - || { - missing.push("sourceHash"); - Default::default() - }, - ); - let mint = other - .get_deserialized::("mint") - .transpose() - .unwrap_or_else(|_| { - missing.push("mint"); - Default::default() - }) - .map(|value| value.to::()); - let is_system_transaction = - other.get_deserialized::("isSystemTx").transpose().ok().flatten().unwrap_or_else( - || { - missing.push("isSystemTx"); - Default::default() - }, - ); - if missing.is_empty() { - Ok(DepositTransactionParts { source_hash, mint, is_system_transaction }) - } else { - Err(missing) - } -} - #[cfg(test)] mod tests { + use alloy_primitives::B256; + use super::*; fn default_tx_req() -> TransactionRequest { @@ -618,6 +626,7 @@ mod tests { } #[test] + #[cfg(feature = "optimism")] fn test_routing_op_by_deposit_fields() { let tx = default_tx_req(); let mut other = OtherFields::default(); @@ -669,6 +678,7 @@ mod tests { } #[test] + #[cfg(feature = "optimism")] fn test_serialization_op() { let tx = default_tx_req(); let mut other = OtherFields::default(); diff --git a/crates/script-sequence/Cargo.toml b/crates/script-sequence/Cargo.toml index 7f112ce1bbda8..ce94945fb27cf 100644 --- a/crates/script-sequence/Cargo.toml +++ b/crates/script-sequence/Cargo.toml @@ -27,3 +27,7 @@ revm-inspectors.workspace = true alloy-network.workspace = true alloy-primitives.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index acdcbbdbf95ea..6de71571ac7eb 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -62,3 +62,15 @@ tempo-primitives.workspace = true [dev-dependencies] tempfile.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-evm/optimism", + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-cheatcodes/optimism", + "foundry-cli/optimism", + "forge-script-sequence/optimism", + "forge-verify/optimism", +] diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 5ac7e4c669ade..f1bda2ccdcf55 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,8 +1,8 @@ -use std::{cmp::Ordering, sync::Arc, time::Duration}; +use std::{cmp::Ordering, num::NonZeroU64, sync::Arc, time::Duration}; use crate::{ - ScriptArgs, ScriptConfig, build::LinkedBuildData, progress::ScriptProgress, - sequence::ScriptSequenceKind, verify::BroadcastedState, + ScriptArgs, ScriptConfig, build::LinkedBuildData, needs_script_rpc_estimate, + progress::ScriptProgress, sequence::ScriptSequenceKind, verify::BroadcastedState, }; use alloy_chains::{Chain, NamedChain}; use alloy_consensus::{SignableTransaction, Signed}; @@ -21,11 +21,12 @@ use alloy_signer::Signature; use eyre::{Context, Result, bail}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::Wallets; -use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; +use foundry_cli::utils::has_batch_support; use foundry_common::{ FoundryTransactionBuilder, TransactionMaybeSigned, provider::{ProviderBuilder, try_get_http_provider}, shell, + tempo::TempoSponsor, }; use foundry_config::Config; use foundry_evm::core::evm::{FoundryEvmNetwork, TempoEvmNetwork}; @@ -100,7 +101,17 @@ where is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, + tempo_sponsor: Option<&TempoSponsor>, ) -> Result<()> { + let access_key_authorization = match self { + Self::AccessKey(_, _, access_key) => Some(( + access_key.wallet_address, + access_key.key_address, + access_key.key_authorization.clone(), + )), + _ => None, + }; + if let Self::Raw(tx, _) | Self::Unlocked(tx) | Self::Browser(tx, _) @@ -137,11 +148,28 @@ where } } + if let Some((wallet_address, key_address, key_authorization)) = + access_key_authorization.as_ref() + { + tx.prepare_access_key_authorization( + provider, + *wallet_address, + *key_address, + key_authorization.as_ref(), + ) + .await?; + } + // 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(tx, provider, estimate_multiplier).await?; } + + if let Some(sponsor) = tempo_sponsor { + let from = tx.from().expect("no sender"); + sponsor.attach_and_print::(tx, from).await?; + } } Ok(()) @@ -211,6 +239,7 @@ where is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, + tempo_sponsor: Option<&TempoSponsor>, ) -> Result { self.prepare( &provider, @@ -218,6 +247,7 @@ where is_fixed_gas_limit, estimate_via_rpc, estimate_multiplier, + tempo_sponsor, ) .await?; @@ -387,6 +417,27 @@ impl BundledState { SendTransactionsKind::Raw { eth_wallets, browser: self.browser_wallet, access_keys } }; + let tempo_sponsor = self.script_config.tempo.sponsor_config().await?.map(Arc::new); + if tempo_sponsor.is_some() && self.script_config.tempo.sponsor_sig.is_some() { + let remaining = self + .sequence + .sequences() + .iter() + .map(|sequence| { + sequence + .transactions() + .skip(sequence.receipts.len()) + .filter(|tx| tx.is_unsigned()) + .count() + }) + .sum::(); + if remaining > 1 { + eyre::bail!( + "--tempo.sponsor-sig can only sponsor one remaining script transaction; use --tempo.sponsor-signer for multi-transaction scripts" + ); + } + } + let progress = ScriptProgress::default(); for i in 0..self.sequence.sequences().len() { @@ -464,6 +515,11 @@ impl BundledState { let kind = match tx_with_metadata.tx().clone() { TransactionMaybeSigned::Signed { tx, .. } => { + if tempo_sponsor.is_some() { + eyre::bail!( + "cannot attach Tempo sponsor signature to an already signed script transaction" + ); + } SendTransactionKind::Signed(tx) } TransactionMaybeSigned::Unsigned(mut tx) => { @@ -487,6 +543,8 @@ impl BundledState { tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); } + self.script_config.tempo.apply::(&mut tx, None); + send_kind.for_sender(&from, tx)? } }; @@ -495,9 +553,13 @@ impl BundledState { }) .collect::>>()?; - let estimate_via_rpc = has_different_gas_calc(sequence.chain) - || self.script_config.evm_opts.networks.is_tempo() - || self.args.skip_simulation; + let estimate_via_rpc = needs_script_rpc_estimate( + sequence.chain, + self.script_config.evm_opts.networks.is_tempo(), + self.script_config.batch, + &self.script_config.tempo, + 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 @@ -525,6 +587,7 @@ impl BundledState { let pending_transactions = batch.iter().map(|(kind, is_fixed_gas_limit)| { let provider = provider.clone(); + let tempo_sponsor = tempo_sponsor.clone(); async move { let res = kind .clone() @@ -534,22 +597,36 @@ impl BundledState { *is_fixed_gas_limit, estimate_via_rpc, self.args.gas_estimate_multiplier, + tempo_sponsor.as_deref(), ) .await; - (res, kind, 0, None) + (res, kind, *is_fixed_gas_limit, 0, None) } .boxed() }); let mut buffer = pending_transactions.collect::>(); - 'send: while let Some((res, kind, attempt, original_res)) = - buffer.next().await + 'send: while let Some(( + res, + kind, + is_fixed_gas_limit, + attempt, + original_res, + )) = buffer.next().await { - if res.is_err() && attempt <= 3 { + if res.is_err() + && self.script_config.tempo.sponsor_sig.is_some() + && attempt == 0 + { + debug!( + "not retrying transaction because --tempo.sponsor-sig is a static signature" + ); + } else if res.is_err() && attempt <= 3 { // Try to resubmit the transaction let provider = provider.clone(); let progress = seq_progress.inner.clone(); + let tempo_sponsor = tempo_sponsor.clone(); buffer.push(Box::pin(async move { debug!(err=?res, ?attempt, "retrying transaction "); let attempt = attempt + 1; @@ -557,8 +634,24 @@ impl BundledState { "retrying transaction {res:?} (attempt {attempt})" )); tokio::time::sleep(Duration::from_millis(1000 * attempt)).await; - let r = kind.clone().send(provider).await; - (r, kind, attempt, original_res.or(Some(res))) + let r = kind + .clone() + .prepare_and_send( + provider, + sequential_broadcast, + is_fixed_gas_limit, + estimate_via_rpc, + self.args.gas_estimate_multiplier, + tempo_sponsor.as_deref(), + ) + .await; + ( + r, + kind, + is_fixed_gas_limit, + attempt, + original_res.or(Some(res)), + ) })); continue 'send; @@ -675,6 +768,7 @@ impl BundledState { let sequence = self.sequence.sequences_mut().get_mut(0).unwrap(); let provider = Arc::new(ProviderBuilder::::new(sequence.rpc_url()).build()?); + let tempo_sponsor = self.script_config.tempo.sponsor_config().await?; // Collect sender addresses - batch mode requires single sender let senders: AddressHashSet = sequence @@ -794,16 +888,35 @@ impl BundledState { max_priority_fee_per_gas: Some(max_priority_fee_per_gas), ..Default::default() }, - fee_token: self.script_config.fee_token, + fee_token: self.script_config.tempo.common.fee_token, calls: calls.clone(), + nonce_key: self.script_config.tempo.expiring_nonce.then_some(U256::MAX), + valid_before: self.script_config.tempo.valid_before.and_then(NonZeroU64::new), ..Default::default() }; + self.script_config.tempo.apply::(&mut batch_tx, None); + + if let BatchSigner::TempoKeychain(_, ak) = &batch_signer { + batch_tx.key_id = Some(ak.key_address); + batch_tx + .prepare_access_key_authorization( + provider.as_ref(), + ak.wallet_address, + ak.key_address, + ak.key_authorization.as_ref(), + ) + .await?; + } // Estimate gas for the batch transaction estimate_gas(&mut batch_tx, provider.as_ref(), self.args.gas_estimate_multiplier).await?; sh_println!("Estimated gas: {}", batch_tx.inner.gas.unwrap_or(0))?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut batch_tx, sender).await?; + } + // Sign and send let tx_hash = match batch_signer { BatchSigner::Wallet(wallet) => { @@ -816,8 +929,6 @@ impl BundledState { *pending.tx_hash() } BatchSigner::TempoKeychain(signer, access_key) => { - batch_tx.key_id = Some(access_key.key_address); - let raw_tx = batch_tx .sign_with_access_key( provider.as_ref(), diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index f5e4d46de0344..ea906c9b872e1 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -28,8 +28,8 @@ use eyre::{ContextCompat, Result}; use forge_script_sequence::{AdditionalContract, NestedValue}; use forge_verify::{RetryArgs, VerifierArgs}; use foundry_cli::{ - opts::{BuildOpts, EvmArgs, GlobalArgs}, - utils::{LoadConfig, parse_fee_token_address}, + opts::{BuildOpts, EvmArgs, GlobalArgs, TempoOpts}, + utils::{LoadConfig, has_different_gas_calc}, }; use foundry_common::{ CONTRACT_MAX_SIZE, ContractsByArtifact, SELECTOR_LEN, @@ -44,11 +44,13 @@ use foundry_config::{ value::{Dict, Map}, }, }; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ backend::Backend, core::{ Breakpoints, FoundryTransaction, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, + evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork, TxEnvFor}, tempo::PATH_USD_ADDRESS, }, executors::ExecutorBuilder, @@ -140,9 +142,9 @@ pub struct ScriptArgs { #[arg(long, requires = "batch", default_value = "100")] pub batch_size: usize, - /// Tempo fee token address for paying transaction fees. - #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] - pub fee_token: Option
, + /// Tempo transaction options. + #[command(flatten)] + pub tempo: TempoOpts, /// Skips on-chain simulation. #[arg(long)] @@ -246,13 +248,43 @@ pub struct ScriptArgs { pub retry: RetryArgs, } +const fn should_default_tempo_fee_token( + is_tempo_network: bool, + batch: bool, + tempo: &TempoOpts, +) -> bool { + // Plain `--network tempo` should stay an ordinary transaction; only Tempo AA opts get defaults. + is_tempo_network && tempo.common.fee_token.is_none() && (batch || tempo.is_tempo()) +} + +const fn needs_tempo_aa_rpc_estimate( + is_tempo_network: bool, + batch: bool, + tempo: &TempoOpts, +) -> bool { + is_tempo_network && (batch || tempo.is_tempo()) +} + +pub(crate) fn needs_script_rpc_estimate( + chain_id: u64, + is_tempo_network: bool, + batch: bool, + tempo: &TempoOpts, + skip_simulation: bool, +) -> bool { + // Tempo AA needs RPC estimation; plain Tempo scripts can use the local simulation result. + (has_different_gas_calc(chain_id) && !is_tempo_network) + || needs_tempo_aa_rpc_estimate(is_tempo_network, batch, tempo) + || skip_simulation +} + impl ScriptArgs { /// Loads config, resolves evm_opts (including network inference from fork), and returns them. async fn resolved_evm_opts(&self) -> Result<(Config, EvmOpts)> { let (config, mut evm_opts) = self.load_config_and_evm_opts()?; - if self.fee_token.is_some() { - // If fee token is set directly select tempo + if self.tempo.is_tempo() { + // If fee token or expiry is set directly select tempo evm_opts.networks = NetworkConfigs::with_tempo(); } else { // Auto-detect network from fork chain ID when not explicitly configured. @@ -285,13 +317,14 @@ impl ScriptArgs { } } - let fee_token = if evm_opts.networks.is_tempo() && self.fee_token.is_none() { - Some(PATH_USD_ADDRESS) - } else { - self.fee_token - }; + let mut tempo = self.tempo.clone(); + tempo.resolve_expires(); + + if should_default_tempo_fee_token(evm_opts.networks.is_tempo(), self.batch, &tempo) { + tempo.common.fee_token = Some(PATH_USD_ADDRESS); + } - let script_config = ScriptConfig::new(config, evm_opts, self.batch, fee_token).await?; + let script_config = ScriptConfig::new(config, evm_opts, self.batch, tempo).await?; Ok(PreprocessedState { args: self, script_config, script_wallets, browser_wallet }) } @@ -320,12 +353,15 @@ impl ScriptArgs { if broadcasted.args.verify { broadcasted.verify().await?; } - Ok(()) - } else if evm_opts.networks.is_optimism() { - self.run_generic_script::(config, evm_opts).await - } else { - self.run_generic_script::(config, evm_opts).await + return Ok(()); } + + #[cfg(feature = "optimism")] + if evm_opts.networks.is_optimism() { + return self.run_generic_script::(config, evm_opts).await; + } + + self.run_generic_script::(config, evm_opts).await } /// Prepares the bundled state (compile, simulate, bundle) and returns it @@ -708,8 +744,8 @@ pub struct ScriptConfig { pub backends: HashMap>, /// Whether to batch all broadcast transactions into a single Tempo batch transaction. pub batch: bool, - /// Tempo fee token address for paying transaction fees. - pub fee_token: Option
, + /// Tempo transaction options applied to broadcast transactions. + pub tempo: TempoOpts, } impl ScriptConfig { @@ -717,7 +753,7 @@ impl ScriptConfig { config: Config, evm_opts: EvmOpts, batch: bool, - fee_token: Option
, + tempo: TempoOpts, ) -> Result { let sender_nonce = if let Some(fork_url) = evm_opts.fork_url.as_ref() { next_nonce(evm_opts.sender, fork_url, evm_opts.fork_block_number).await? @@ -726,7 +762,7 @@ impl ScriptConfig { 1 }; - Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default(), batch, fee_token }) + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default(), batch, tempo }) } pub async fn update_sender(&mut self, sender: Address) -> Result<()> { @@ -802,7 +838,7 @@ impl ScriptConfig { self.evm_opts.clone(), Some(known_contracts), Some(target), - self.fee_token, + self.tempo.common.fee_token, ) .into(), ) @@ -813,7 +849,7 @@ impl ScriptConfig { // Propagate fee token to the transaction environment so that internal EVM calls // (e.g. script deployment, setUp) use the correct fee token for Tempo networks. - tx_env.set_fee_token(self.fee_token); + tx_env.set_fee_token(self.tempo.common.fee_token); Ok(ScriptRunner::new(builder.build(evm_env, tx_env, db), self.evm_opts.clone())) } @@ -823,6 +859,7 @@ impl ScriptConfig { mod tests { use super::*; use alloy_network::Ethereum; + use alloy_primitives::address; use foundry_config::{NamedChain, UnresolvedEnvVarError}; use std::fs; use tempfile::tempdir; @@ -834,6 +871,50 @@ mod tests { assert_eq!(args.sig, sig); } + #[test] + fn can_parse_shared_tempo_opts() { + let args = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--tempo.fee-token", + "1", + "--tempo.expires", + "10", + ]); + + assert_eq!( + args.tempo.common.fee_token, + Some(address!("0x20C0000000000000000000000000000000000001")) + ); + assert_eq!(args.tempo.common.expires, Some(10)); + } + + #[test] + fn can_parse_sponsor_tempo_opts() { + let args = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + "--tempo.sponsor-signer", + "env://TEMPO_SPONSOR_PK", + ]); + + assert_eq!( + args.tempo.sponsor, + Some(address!("0x1111111111111111111111111111111111111111")) + ); + assert_eq!(args.tempo.sponsor_signer.as_deref(), Some("env://TEMPO_SPONSOR_PK")); + } + + #[test] + fn can_parse_full_tempo_opts() { + let args = + ScriptArgs::parse_from(["foundry-cli", "Contract.sol", "--tempo.nonce-key", "1"]); + + assert_eq!(args.tempo.nonce_key, Some(U256::from(1))); + } + #[test] fn can_parse_unlocked() { let args = ScriptArgs::parse_from([ diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index e2404d60ce2b9..b085f8eaf4545 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -6,7 +6,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; -use foundry_common::{FoundryTransactionBuilder, TransactionMaybeSigned}; +use foundry_common::TransactionMaybeSigned; use foundry_config::Config; use foundry_evm::{ constants::CALLER, @@ -84,9 +84,7 @@ impl ScriptRunner { .with_input(code.clone()) .with_nonce(sender_nonce + library_transactions.len() as u64); - if let Some(fee_token) = script_config.fee_token { - tx_req.set_fee_token(fee_token); - } + script_config.tempo.apply::(&mut tx_req, None); library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), @@ -122,9 +120,7 @@ impl ScriptRunner { .with_nonce(sender_nonce + library_transactions.len() as u64) .with_to(create2_deployer); - if let Some(fee_token) = script_config.fee_token { - tx_req.set_fee_token(fee_token); - } + script_config.tempo.apply::(&mut tx_req, None); library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index ef10a1ce94082..fe1e9345fa23c 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -129,7 +129,7 @@ impl VerifyBundle { path: Some(artifact.source.to_string_lossy().to_string()), name: artifact .name - .strip_suffix(&format!(".{}", &artifact.profile)) + .strip_suffix(&format!(".{}", artifact.profile)) .unwrap_or_else(|| &artifact.name) .to_string(), }; diff --git a/crates/sol-macro-gen/Cargo.toml b/crates/sol-macro-gen/Cargo.toml index 69ea952d4d040..d3ad56a96cdd2 100644 --- a/crates/sol-macro-gen/Cargo.toml +++ b/crates/sol-macro-gen/Cargo.toml @@ -27,3 +27,7 @@ prettyplease.workspace = true eyre.workspace = true heck.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index d29f6358e93d2..8dc652bcb32bd 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -44,3 +44,7 @@ idna_adapter.workspace = true [dev-dependencies] tokio.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 65a202911509f..e3372a2494f34 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -48,3 +48,12 @@ url.workspace = true tokio = { workspace = true, features = ["macros"] } foundry-test-utils.workspace = true tempfile.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm/optimism", + "foundry-evm-networks/optimism", + "foundry-cli/optimism", +] diff --git a/docs/dev/lintrules.md b/docs/dev/lintrules.md index 6f5dbbd850784..969d7effe142f 100644 --- a/docs/dev/lintrules.md +++ b/docs/dev/lintrules.md @@ -60,6 +60,8 @@ Next, choose whether you want an [early or late lint pass](#choosing-between-ear - Implement the appropriate trait logic (`EarlyLintPass` or `LateLintPass`) for your lint. Do it in a new file within the relevant severity module (e.g., `src/sol/med/my_new_lint.rs`). +- Add a markdown documentation file for the lint at `crates/lint/docs/.md`. The file is referenced by the lint's `help` URL (`https://getfoundry.sh/forge/linting/`) and is consumed by the [Foundry book](https://github.com/foundry-rs/book) to render the lint reference page. Use [`crates/lint/docs/_template.md`](../../crates/lint/docs/_template.md) as a starting point. The presence of this file is enforced by the `registered_lints_have_docs` unit test in `crates/lint/src/sol/mod.rs`. + ### Choosing Between Early and Late Passes - **Use `EarlyLintPass`** for: diff --git a/flake.lock b/flake.lock index 27f426f491da6..343a79c0cda3d 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1777102577, - "narHash": "sha256-ycoy9svZOQgyInu/lwO7IEQtlP5liqYhEcF9m9hPRbM=", + "lastModified": 1777708550, + "narHash": "sha256-Qif3UXT0l5OQq8H9pRWt4/ia4gF48MWK2oHKL8uVx8U=", "owner": "nix-community", "repo": "fenix", - "rev": "f37403486c59376cd285f9685a8ef8ff25c09a3c", + "rev": "74c1591efaff494756b8d35ebe357c6c2bbdca96", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1776949667, - "narHash": "sha256-GMSVw35Q+294GlrTUKlx087E31z7KurReQ1YHSKp5iw=", + "lastModified": 1777641297, + "narHash": "sha256-WNGcmeOZ8Tr9dq6ztCspYbzWFswr2mPebM9LpsfGxPk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "01fbdeef22b76df85ea168fbfe1bfd9e63681b30", + "rev": "c6d65881c5624c9cae5ea6cedef24699b0c0a4c0", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1776800521, - "narHash": "sha256-f8YJfwAOsLFpIoqZuX3yF69UvMLrkx7iVzMH1pJU7cM=", + "lastModified": 1777639980, + "narHash": "sha256-6d7Hdurvbjc5uwJuc0YiK7rZBGj6Gs3uzfBFcTs+xCc=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "8954b66d43225e62c92e8bbcc8500191b5cceb1e", + "rev": "64cdaeb06f69b6b769a492edd88b022ae88e8ca2", "type": "github" }, "original": { diff --git a/foundryup/README.md b/foundryup/README.md index 29e91378929cc..2cf61f8725227 100644 --- a/foundryup/README.md +++ b/foundryup/README.md @@ -30,10 +30,10 @@ To install the latest **nightly** version: foundryup --install nightly ``` -To install a specific version (e.g. `v1.6.0`): +To install a specific version (e.g. `v1.7.0`): ```sh -foundryup --install v1.6.0 +foundryup --install v1.7.0 ``` To **list** all **versions** installed: diff --git a/foundryup/foundryup b/foundryup/foundryup index 7576501b4619e..5fb086a75f8c1 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -3,7 +3,7 @@ set -eo pipefail # NOTE: if you make modifications to this script, please increment the version number. # WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date. -FOUNDRYUP_INSTALLER_VERSION="1.8.1" +FOUNDRYUP_INSTALLER_VERSION="1.8.3" BASE_DIR=${XDG_CONFIG_HOME:-$HOME} FOUNDRY_DIR=${FOUNDRY_DIR:-"$BASE_DIR/.foundry"} @@ -15,6 +15,13 @@ FOUNDRY_BIN_PATH="$FOUNDRY_BIN_DIR/foundryup" FOUNDRYUP_JOBS="" FOUNDRYUP_IGNORE_VERIFICATION=false +# Retry/backoff settings used for `fetch` (GitHub API calls). +# Recovers from transient HTTP 403/429/5xx responses returned by +# api.github.com under heavy load or per-IP rate limiting. +FOUNDRYUP_MAX_RETRIES=5 +FOUNDRYUP_RETRY_DELAY=2 +FOUNDRYUP_RETRY_MAX_TIME=60 + BINS=(forge cast anvil chisel) HASH_NAMES=() HASH_VALUES=() @@ -111,51 +118,7 @@ main() { # Install by downloading binaries if [[ "$FOUNDRYUP_REPO" == "foundry-rs/foundry" && -z "$FOUNDRYUP_BRANCH" && -z "$FOUNDRYUP_COMMIT" ]]; then FOUNDRYUP_VERSION=${FOUNDRYUP_VERSION:-latest} - - # Normalize versions (handle channels, versions without v prefix) - if [[ "$FOUNDRYUP_VERSION" == "latest" || "$FOUNDRYUP_VERSION" == "stable" ]]; then - # Resolve to the latest release (non-prerelease) via the GitHub API - say "fetching latest release from ${FOUNDRYUP_REPO}..." - FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases/latest" | awk ' - /"tag_name"[[:space:]]*:/ && !found { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); print; found=1 } - ') || err "failed to fetch releases from GitHub API" - if [ -z "$FOUNDRYUP_TAG" ]; then - err "could not find a latest release for ${FOUNDRYUP_REPO}" - fi - say "resolved release tag: ${FOUNDRYUP_TAG}" - FOUNDRYUP_VERSION="$FOUNDRYUP_TAG" - elif [[ "$FOUNDRYUP_VERSION" == "nightly" ]]; then - # Resolve to the latest nightly (prerelease) release via the GitHub API. - # The GitHub API does not guarantee that releases are returned in - # chronological order, so we collect all matching nightlies along with - # their `published_at` timestamps and sort them ourselves. - say "fetching latest nightly releases from ${FOUNDRYUP_REPO}..." - FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases" | awk ' - /"tag_name"[[:space:]]*:/ { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); tag=$0 } - /"published_at"[[:space:]]*:[[:space:]]*"/ { - pub=$0 - gsub(/.*"published_at"[[:space:]]*:[[:space:]]*"/, "", pub) - gsub(/".*/, "", pub) - if (tag ~ /^nightly-/) print pub "\t" tag - tag="" - } - ' | sort -r | awk -F '\t' 'NR==1 { print $2 }') || err "failed to fetch releases from GitHub API" - if [ -z "$FOUNDRYUP_TAG" ]; then - err "could not find a nightly release for ${FOUNDRYUP_REPO}" - fi - say "resolved nightly release tag: ${FOUNDRYUP_TAG}" - FOUNDRYUP_VERSION="nightly" - elif [[ "$FOUNDRYUP_VERSION" =~ ^nightly- ]]; then - # Specific nightly tag (e.g. nightly-abc123...) - FOUNDRYUP_TAG="$FOUNDRYUP_VERSION" - FOUNDRYUP_VERSION="nightly" - elif [[ "$FOUNDRYUP_VERSION" == [[:digit:]]* ]]; then - # Add v prefix - FOUNDRYUP_VERSION="v${FOUNDRYUP_VERSION}" - FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" - else - FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" - fi + resolve_version_and_tag say "installing foundry (version ${FOUNDRYUP_VERSION}, tag ${FOUNDRYUP_TAG})" @@ -179,7 +142,7 @@ main() { tmp_dir="$(mktemp -d 2>/dev/null)" || err "failed to create temp dir" tmp="$tmp_dir/attestation.txt" ensure download "$ATTESTATION_URL" "$tmp" - + # Read the first line of the attestation file to get the artifact link. # The first line should contain the link to the attestation artifact. attestation_artifact_link="$(head -n1 "$tmp" | tr -d '\r')" @@ -292,7 +255,7 @@ main() { else say 'skipping manpage download: missing "tar"' fi - + if [ "$FOUNDRYUP_IGNORE_VERIFICATION" = true ]; then say "skipped SHA verification for downloaded binaries due to --force flag" else @@ -505,6 +468,18 @@ list() { use() { [ -z "$FOUNDRYUP_VERSION" ] && err "no version provided" + + # If the requested version is a channel (`latest`, `stable`, `nightly`) or a bare semver + # version (e.g. `1.7.0`, `1.6.0-rc1`), resolve it to the immutable tag directory created by + # `--install` (channels hit the GitHub API; semver versions get a `v` prefix). + # Falls back to the literal value for locally-built versions (branches, PRs, commits, custom names). + case "$FOUNDRYUP_VERSION" in + latest|stable|nightly|[0-9]*.[0-9]*.[0-9]*) + resolve_version_and_tag + FOUNDRYUP_VERSION="$FOUNDRYUP_TAG" + ;; + esac + FOUNDRY_VERSION_DIR="$FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_VERSION" if [ -d "$FOUNDRY_VERSION_DIR" ]; then @@ -683,12 +658,70 @@ ensure() { if ! "$@"; then err "command failed: $*"; fi } -# Silently fetches $1 to stdout +# Normalizes `FOUNDRYUP_VERSION` and resolves it to a concrete release tag, +# populating `FOUNDRYUP_TAG`. Handles the `latest`/`stable`/`nightly` channels +# (looked up via the GitHub API). +resolve_version_and_tag() { + FOUNDRYUP_REPO=${FOUNDRYUP_REPO:-foundry-rs/foundry} + if [[ "$FOUNDRYUP_VERSION" == "latest" || "$FOUNDRYUP_VERSION" == "stable" ]]; then + # Resolve to the latest release (non-prerelease) via the GitHub API. + say "fetching latest release tag from ${FOUNDRYUP_REPO}..." + FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases/latest" | awk ' + /"tag_name"[[:space:]]*:/ && !found { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); print; found=1 } + ') || err "failed to fetch release tags from GitHub API" + if [ -z "$FOUNDRYUP_TAG" ]; then + err "could not find a latest release tag for ${FOUNDRYUP_REPO}" + fi + say "resolved release tag: ${FOUNDRYUP_TAG}" + FOUNDRYUP_VERSION="$FOUNDRYUP_TAG" + elif [[ "$FOUNDRYUP_VERSION" == "nightly" ]]; then + # Resolve to the latest nightly (prerelease) release via the GitHub API. + # The GitHub API does not guarantee that releases are returned in + # chronological order, so we collect all matching nightlies along with + # their `published_at` timestamps and sort them ourselves. + say "fetching latest nightly release tags from ${FOUNDRYUP_REPO}..." + FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases" | awk ' + /"tag_name"[[:space:]]*:/ { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); tag=$0 } + /"published_at"[[:space:]]*:[[:space:]]*"/ { + pub=$0 + gsub(/.*"published_at"[[:space:]]*:[[:space:]]*"/, "", pub) + gsub(/".*/, "", pub) + if (tag ~ /^nightly-/) print pub "\t" tag + tag="" + } + ' | sort -r | awk -F '\t' 'NR==1 { print $2 }') || err "failed to fetch release tags from GitHub API" + if [ -z "$FOUNDRYUP_TAG" ]; then + err "could not find a nightly release tag for ${FOUNDRYUP_REPO}" + fi + say "resolved nightly release tag: ${FOUNDRYUP_TAG}" + FOUNDRYUP_VERSION="nightly" + elif [[ "$FOUNDRYUP_VERSION" =~ ^nightly- ]]; then + # Specific nightly tag (e.g. nightly-abc123...) + FOUNDRYUP_TAG="$FOUNDRYUP_VERSION" + FOUNDRYUP_VERSION="nightly" + elif [[ "$FOUNDRYUP_VERSION" == [[:digit:]]* ]]; then + # Add v prefix + FOUNDRYUP_VERSION="v${FOUNDRYUP_VERSION}" + FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" + else + FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" + fi +} + +# Silently fetches $1 to stdout. fetch() { if check_cmd curl; then - curl -fsSL "$1" + curl -fsSL \ + --retry "$FOUNDRYUP_MAX_RETRIES" \ + --retry-delay "$FOUNDRYUP_RETRY_DELAY" \ + --retry-max-time "$FOUNDRYUP_RETRY_MAX_TIME" \ + --retry-all-errors \ + "$1" else - wget -qO- "$1" + wget --tries="$FOUNDRYUP_MAX_RETRIES" \ + --waitretry="$FOUNDRYUP_RETRY_DELAY" \ + --retry-on-http-error=403,408,429,500,502,503,504 \ + -qO- "$1" fi } diff --git a/testdata/default/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol index 839d97962aa94..ae0c8ed844f5d 100644 --- a/testdata/default/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -305,6 +305,91 @@ contract ExpectRevertWithReverterTest is Test { vm.expectRevert(address(cContract)); aContract.createDContractThroughCContract(); } + + // + // Regression: when the next operation is a top-level CREATE whose constructor + // reverts directly, the reverter address argument must be enforced (it used to + // be silently ignored). The matched reverter is the would-be-deployed address. + function testExpectRevertsWithReverterTopLevelCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(expected); + new DContract(); + + expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(abi.encodePacked("Reverted by DContract"), expected); + new DContract(); + } + + // + // Regression: when the next operation is a top-level CREATE whose constructor + // synchronously creates another contract that reverts (i.e. innermost frame is + // a CREATE), the matched reverter is the outer would-be-deployed address (the + // contract whose deployment failed). + function testExpectRevertsWithReverterNestedCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(expected); + new NestedDContractCreator(); + } + + // + // Regression: `expectPartialRevert(bytes4, address)` overload must enforce + // the reverter address argument when matching a top-level CREATE revert. + function testExpectPartialRevertWithReverterTopLevelCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + // `Reverted by DContract` triggers Solidity's `Error(string)` selector. + vm.expectPartialRevert(bytes4(keccak256("Error(string)")), expected); + new DContract(); + } + + // + // Regression: `expectRevert(bytes4, address)` (exact 4-byte selector + reverter) + // overload must enforce the reverter address argument for a top-level CREATE. + function testExpectRevertWithBytes4SelectorAndReverterTopLevelCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(DCustomErrorContract.CustomError.selector, expected); + new DCustomErrorContract(); + } + + // + // Regression: `expectRevert(address, uint64)` count-bearing overload must + // exercise the `count > 1` branch in `create_end`. Use CREATE2 with the same + // salt so both deploys would resolve to the same would-be address (each + // constructor reverts so no contract is ever actually placed there). + function testExpectRevertsWithReverterCountTopLevelCreate2() public { + bytes32 salt = bytes32(uint256(0x42)); + address expected = vm.computeCreate2Address(salt, keccak256(type(DContract).creationCode), address(this)); + vm.expectRevert(expected, 2); + new DContract{salt: salt}(); + new DContract{salt: salt}(); + } + + // + // Regression: CREATE2 deploys must also enforce the reverter address argument. + function testExpectRevertsWithReverterTopLevelCreate2() public { + bytes32 salt = bytes32(uint256(0xC0FFEE)); + address expected = vm.computeCreate2Address(salt, keccak256(type(DContract).creationCode), address(this)); + vm.expectRevert(expected); + new DContract{salt: salt}(); + } +} + +// Used by `testExpectRevertsWithReverterNestedCreate`: a contract whose constructor +// directly creates another contract that reverts. +contract NestedDContractCreator { + constructor() { + new DContract(); + } +} + +// Used by `testExpectRevertWithBytes4SelectorAndReverterTopLevelCreate`: constructor +// reverts with a parameter-less custom error so the full revert data is exactly the +// 4-byte selector. +contract DCustomErrorContract { + error CustomError(); + + constructor() { + revert CustomError(); + } } contract ExpectRevertCount is Test { diff --git a/testdata/default/cheats/GetFoundryVersion.t.sol b/testdata/default/cheats/GetFoundryVersion.t.sol index 6139b8b6b6a5e..f01b7cdd7d213 100644 --- a/testdata/default/cheats/GetFoundryVersion.t.sol +++ b/testdata/default/cheats/GetFoundryVersion.t.sol @@ -84,4 +84,55 @@ contract GetFoundryVersionTest is Test { // Should return true for past versions assertTrue(vm.foundryVersionAtLeast("0.2.0")); } + + /// Returns the `MAJOR.MINOR.PATCH` prefix of `vm.getFoundryVersion()`, + /// stripping any pre-release suffix (`-nightly`, `-dev`, …) and the + /// `+..` build metadata. + function _semverPrefix() internal view returns (string memory) { + string[] memory plusSplit = vm.split(vm.getFoundryVersion(), "+"); + require(plusSplit.length == 2, "Invalid version format: Missing '+' separator"); + string[] memory dashSplit = vm.split(plusSplit[0], "-"); + return dashSplit[0]; + } + + function testGetFoundryVersionMajorMinorPatchIsParseable() public view { + // The MAJOR.MINOR.PATCH prefix must always be three numeric components, + // regardless of build kind (tagged release / nightly / dev). + string[] memory parts = vm.split(_semverPrefix(), "."); + require(parts.length == 3, "Invalid semver prefix: expected MAJOR.MINOR.PATCH"); + // Each component must parse as a uint (this reverts on garbage). + vm.parseUint(parts[0]); + vm.parseUint(parts[1]); + vm.parseUint(parts[2]); + } + + function testGetFoundryVersionBuildProfile() public view { + // The build profile must be present and non-empty (e.g. "debug", "release", "dist", …). + string[] memory plusSplit = vm.split(vm.getFoundryVersion(), "+"); + string[] memory metadataComponents = vm.split(plusSplit[1], "."); + require(bytes(metadataComponents[2]).length > 0, "Build profile is empty"); + } + + function testFoundryVersionCmpAndAtLeastAreConsistent() public { + // `foundryVersionAtLeast(v)` must equal `foundryVersionCmp(v) >= 0` for any input. + string[3] memory probes = ["0.0.1", _semverPrefix(), "99.0.0"]; + for (uint256 i = 0; i < probes.length; i++) { + assertEq(vm.foundryVersionAtLeast(probes[i]), vm.foundryVersionCmp(probes[i]) >= 0); + } + } + + function testFoundryVersionCmpRejectsPreRelease() public { + vm._expectCheatcodeRevert(); + vm.foundryVersionCmp("1.0.0-nightly"); + } + + function testFoundryVersionCmpRejectsBuildMetadata() public { + vm._expectCheatcodeRevert(); + vm.foundryVersionCmp("1.0.0+abc1234567.1700000000.release"); + } + + function testFoundryVersionCmpRejectsInvalidVersion() public { + vm._expectCheatcodeRevert(); + vm.foundryVersionCmp("not-a-version"); + } } diff --git a/testdata/default/cheats/MockCall.t.sol b/testdata/default/cheats/MockCall.t.sol index e2ac74d6f70fa..d8019ab4f6ee8 100644 --- a/testdata/default/cheats/MockCall.t.sol +++ b/testdata/default/cheats/MockCall.t.sol @@ -158,6 +158,35 @@ contract MockCallTest is Test { assertEq(mock.pay{value: 50}(1), 100); } + function testMockCallWithValueTransfersBalance() public { + Mock mock = new Mock(); + uint256 value = 10; + vm.deal(address(this), value); + + vm.mockCall(address(mock), value, abi.encodeWithSelector(mock.pay.selector), abi.encode(10)); + + assertEq(address(mock).balance, 0); + assertEq(mock.pay{value: value}(1), 10); + assertEq(address(mock).balance, value); + assertEq(address(this).balance, 0); + } + + function testMockCallWithValueTransfersPrankedSenderBalance() public { + Mock mock = new Mock(); + address sender = address(0xBEEF); + uint256 value = 10; + vm.deal(address(this), 0); + vm.deal(sender, value); + + vm.mockCall(address(mock), value, abi.encodeWithSelector(mock.pay.selector), abi.encode(10)); + + vm.prank(sender); + assertEq(mock.pay{value: value}(1), 10); + assertEq(address(mock).balance, value); + assertEq(address(this).balance, 0); + assertEq(sender.balance, 0); + } + function testMockCallWithValueCalldataPrecedence() public { Mock mock = new Mock(); @@ -279,17 +308,25 @@ contract MockCallRevertTest is Test { function testMockCallRevertWithValue() public { Mock mock = new Mock(); + uint256 value = 10; + vm.deal(address(this), value); - vm.mockCallRevert(address(mock), 10, abi.encodeWithSelector(mock.pay.selector), ERROR_MESSAGE); + vm.mockCallRevert(address(mock), value, abi.encodeWithSelector(mock.pay.selector), ERROR_MESSAGE); assertEq(mock.pay(1), 1); assertEq(mock.pay(2), 2); - try mock.pay{value: 10}(1) { + uint256 initSenderBalance = address(this).balance; + uint256 initTargetBalance = address(mock).balance; + + try mock.pay{value: value}(1) { revert(); } catch (bytes memory err) { require(keccak256(err) == keccak256(ERROR_MESSAGE)); } + + assertEq(address(this).balance, initSenderBalance); + assertEq(address(mock).balance, initTargetBalance); } function testMockCallResetsMockCallRevert() public { diff --git a/testdata/default/cheats/MockCalls.t.sol b/testdata/default/cheats/MockCalls.t.sol index e0f5eef151db6..777543f28e361 100644 --- a/testdata/default/cheats/MockCalls.t.sol +++ b/testdata/default/cheats/MockCalls.t.sol @@ -28,13 +28,17 @@ contract MockCallsTest is Test { mocks[0] = abi.encode(2 ether); mocks[1] = abi.encode(1 ether); mocks[2] = abi.encode(6.423 ether); + vm.deal(address(this), 3 ether); vm.mockCalls(mockErc20, 1 ether, data, mocks); (, bytes memory ret1) = mockErc20.call{value: 1 ether}(data); assertEq(abi.decode(ret1, (uint256)), 2 ether); + assertEq(mockErc20.balance, 1 ether); (, bytes memory ret2) = mockErc20.call{value: 1 ether}(data); assertEq(abi.decode(ret2, (uint256)), 1 ether); + assertEq(mockErc20.balance, 2 ether); (, bytes memory ret3) = mockErc20.call{value: 1 ether}(data); assertEq(abi.decode(ret3, (uint256)), 6.423 ether); + assertEq(mockErc20.balance, 3 ether); } function testMockCalls() public { From fc617f4b1e82ecceb1f4850d373e8d8373b5f38e Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Thu, 7 May 2026 08:36:51 +0700 Subject: [PATCH 373/374] Tempo signer lookup and access key signing (#523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update crates/evm/evm/src/executors/corpus.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry/master test ux (#295) * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#211) This pull request updates the Rust version in the CircleCI workflow to 1.89.0. This is a good maintenance task to keep the CI environment up-to-date. I have one suggestion regarding the Docker image tag to potentially simplify future maintenance by automatically adopting patch releases. Overall, the change is correct and beneficial. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#250) CI: Include the 'main' branch in the push event triggers for the test workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * fix(fmt): handle trailing coments between base contracts (#296) (#299) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) * Create config.yml * Rename ci_cargo.yml to cargo.yml * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis * Update CircleCI configuration for dev stage (#300) fix Automatic reruns provide a safety net for your CI/CD pipelines by automatically retrying failed steps and/or workflows. Automatic reruns help teams maintain productivity by reducing the need for manual intervention when steps and workflows fail due to temporary issues. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * EIP-4788 implementation * formatting * add beacon block root tests * Update crates/evm/evm/src/executors/trace.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cast/src/cmd/run.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * feat: upgrade @types/node from 24.10.4 to 25.0.2 Snyk has created this PR to upgrade @types/node from 24.10.4 to 25.0.2. See this package in npm: @types/node See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * fix: `svm fails to download solc 0.8.33 on linux/arm64`, bump `svm-rs` (#13007) (#309) bump svm-rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Ethereumjs/master (#310) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Forge/master (#311) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Update dev_stage.yml (#313) (#315) * Update dev_stage.yml (#313) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/dev_stage.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Foundry/main (#316) * chore(deps): bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing) and [ammonia](https://github.com/rust-ammonia/ammonia). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) Updates `ammonia` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/rust-ammonia/ammonia/releases) - [Changelog](https://github.com/rust-ammonia/ammonia/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-ammonia/ammonia/compare/v4.1.0...v4.1.2) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo - dependency-name: ammonia dependency-version: 4.1.2 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update crates/verify/src/provider.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/as_doc.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update as_doc.rs (#235) Tidy up formatting in as_doc.rs by removing extraneous blank lines in the Document::as_doc implementation Enhancements: Remove unnecessary blank line before initializing bases Remove unnecessary blank line before writing state variables Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore: ignore RUSTSEC (#13011) * update deny for CI * Update more * chore(chisel): rm dead code (#13014) * chore(cli): rm dead code (#13015) * chore(cheatcodes): rm dead code (#13016) * chore(common): rm dead code (#13018) * chore(bench): rm dead code (#13017) * fix(forge): respect lint ignore config in solar compilation (#12978) Co-authored-by: tefyosL-sol * fix: deduplicate submodule status check logic (#13010) Update mod.rs * Foundry/ethereum ux fix tempo #296 (#319) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Remove duplicate logic in TxSigner::address() implementations * fix(fmt): handle trailing coments between base contracts (#296) @0xrusowsky @Dargon789 fix(fmt): handle trailing coments between base contracts Revert 142 master (#296) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * Revert "fix(fmt): handle trailing coments between base contracts (#12127)" This reverts commit b8b5fbb83fa2436063cebc34ddf900abc972b11d. * Update cargo.yml (#172) CI/CD Configuration Update: The CircleCI configuration file, .circleci/cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring builds and tests run with the latest stable Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Remove duplicate logic in TxSigner::address() implementations --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Aganis --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: Gengar * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 104: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 105: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: add Tempo transaction receipt type support in TryFrom conversion (#334) * fix: add Tempo transaction receipt type support in TryFrom conversion (#13047) Amp-Thread-ID: https://ampcode.com/threads/T-019bbf45-d7c8-75ed-8c05-bc1638d487ee Co-authored-by: Matthias Seitz Co-authored-by: Amp * feat(cheatcodes): add getRecordedLogsJson cheatcode (#13093) Adds a new cheatcode `getRecordedLogsJson` that returns recorded logs as a JSON string, similar to the existing `getStateDiffJson` pattern. This allows users to easily post-process recorded logs externally without needing to manually transform the Log[] array to JSON. JSON format: ```json [{"topics": ["0x..."], "data": "0x...", "emitter": "0x..."}] ``` Closes #12854 * feat: add Sourcify support to forge clone (#12900) * Integrate Sourcify API for contract cloning Added support for Sourcify API in `forge clone` command. * Add reqwest dependency with json feature * Remove unused import in clone.rs Removed unused import of BTreeMap. * Refactor EtherscanClient to ExplorerClient * Change sourcify module from private to public * Implement test for sourcify clone functionality Add test for cloning with sourcify source * Update clone.rs * Add url dependency to Cargo.toml * cargo fmt * Enhance Sourcify client with cached creation data Updated the Sourcify client to cache creation data and reuse it across API calls, improving efficiency. Modified the contract source code retrieval to include additional creation data fields. * Improve error handling for contract data retrieval Refactor contract source code and creation data retrieval to use fallback values when API requests fail or fields are unavailable. * Enhance contract_source_code with improved caching Updated contract_source_code to include additional fields in the API request and improved caching of creation data. Removed fallback logic for fetching creation data from the API. * Refactor creation_data handling in clone.rs Removed redundant creation_data initialization and caching. * Refactor response deserialization to use untagged enum * fix: use serde_json::Value for abi in Sourcify parsing The #[serde(untagged)] enum SourcifyContractResponse failed to deserialize because Box doesn't work with untagged enums. RawValue requires borrowing from the original JSON, but untagged enums buffer data during variant matching. Changes: - Change abi field from Box to serde_json::Value - Truncate response in error messages to avoid huge output * feat: add --sourcify-url option for custom Sourcify API endpoint * feat: imply --source sourcify when --sourcify-url is specified * feat: support full path in --sourcify-url When --sourcify-url contains v2/contract/chain, only append address and fields instead of building the full path again. --------- Co-authored-by: grandizzy * perf: add dist profile for smaller release binaries (#13097) * perf: add dist profile for smaller release binaries Add a new 'dist' Cargo profile optimized for distribution: - Fat LTO and codegen-units=1 for better optimization - Strip symbols for smaller binaries - opt-level="s" overrides for non-perf-critical dependencies Benchmarks on Solady test suite show dist is 8% faster than release while being 45% smaller (43MB vs 78MB). Update release workflows to use the dist profile instead of maxperf. * Apply suggestion from @DaniPopes --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore(deps): update figment to figment2 v0.11 (#13099) * chore(deps): update figment to figment2 v0.11 * rename * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY (#13094) * feat: add precompile decoding for Prague BLS12-381 and Osaka P256VERIFY * wip * wip * fix(traces): use raw byte decoding for P256VERIFY precompile P256VERIFY (RIP-7212) uses concatenated raw bytes, not ABI encoding: - Input: hash (32) + r (32) + s (32) + qx (32) + qy (32) = 160 bytes - Output: 32 bytes where 0x...01 means success * fix(traces): use raw byte decoding for all precompiles Precompiles use concatenated raw bytes, not ABI encoding: - ecrecover: hash (32) + v (32) + r (32) + s (32), returns address in last 20 bytes - sha256/ripemd160: raw input, raw 32-byte output (ripemd in last 20 bytes) - ecadd: x1/y1/x2/y2 (32 each), returns x/y (32 each) - ecmul: x1/y1/s (32 each), returns x/y (32 each) - ecpairing: returns 32-byte bool (1 = success) - bls12PairingCheck: returns 32-byte bool (1 = success) * fix(traces): restore ABI-based precompile decoding * fix * fix(anvil): use suggested priority fee by default (#13092) * fix(anvil): use suggested priority fee by default * test: fix anvil trace expectations --------- Co-authored-by: tefyosL-sol * chore: aggregate PRs (#13100) * chore: aggregate PRs This PR aggregates changes from the following PRs: - Closes #13032 by @\splinter012 - Closes #13059 by @\phrwlk * fmt * chore(evm): misleading error message in traces serialization (#13081) Co-authored-by: tefyosL-sol --------- Co-authored-by: Desant pivo Co-authored-by: Matthias Seitz Co-authored-by: Amp Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Avory Co-authored-by: grandizzy Co-authored-by: onbjerg Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol * Potential fix for code scanning alert no. 103: Artifact poisoning (#336) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Create Docker.yml (#338) Build: Introduce a Docker GitHub Actions workflow that logs into Docker Hub, builds images with buildx, tags them based on branch, semver, and SHA, and pushes them on non-PR events while only loading them for pull requests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 108: Artifact poisoning (#345) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 110: Uncontrolled data used in path expression (#347) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 102: Artifact poisoning (#351) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * benches\LATEST.md (#350) * benches\LATEST.md * Update benches/LATEST.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 109: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Wagmi (e604566) (#344) * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci.yml (#57) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#59) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create web3_defi_gamefi.yml (#61) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#66) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#71) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 21: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml (#107) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#114) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#113) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action (#111) Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/dbda91f6efef3ee627f56175120aa9543687d830...762d7fdba79d046449732c729c1d3aaad021baa2) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.38.0 to 1.38.1 (#112) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.38.0 to 1.38.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/83157de2df0fa7c7ae20f73f9dbed44c41f2bb64...80c8a4945eec0f6d464eaf9e65ed98ef085283d1) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.38.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#110) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.28 (#109) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.28. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/522492a8c115f1b6d4d318581f09638e9442547b...e7ef886cf8f69c25ecef6bbc2858a42e273496ec) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#115) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update crates/doc/src/writer/buf_writer.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Foundry/master (#122) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename config.yml to ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to ci_v1.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/ci_v1.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename config.yml to ci_deploy.yml (#123) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create snyk-container.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci.yml to ci-say-hello.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.ym (#128) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory (#129) Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 1.4.1 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create cargo.yml (#74) (#130) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix typo in CircleCI config file name Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix formatting in cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation for on_fail condition in CI config Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Fix indentation in CircleCI configuration Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.21 to 2.62.31 (#139) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.21 to 2.62.31. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.21...0005e0116e92d8489d8d96fbff83f061c79ba95a) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action from 3 to 4 (#138) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump snyk/actions Bumps [snyk/actions](https://github.com/snyk/actions) from 14818c4695ecc4045f33c9cee9e795a788711ca4 to 9adf32b1121593767fc3c057af55b55db032dc04. - [Release notes](https://github.com/snyk/actions/releases) - [Commits](https://github.com/snyk/actions/compare/14818c4695ecc4045f33c9cee9e795a788711ca4...9adf32b1121593767fc3c057af55b55db032dc04) --- updated-dependencies: - dependency-name: snyk/actions dependency-version: 9adf32b1121593767fc3c057af55b55db032dc04 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * Update CircleCI config with comments and formatting Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update and rename ci-say-hello.yml to ci-web3-defi-gamefi.yml (#154) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci-web3-defi-gamefi.yml (#155) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_deploy.yml (#158) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/cargo.yml (#159) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.31 to 2.62.33 (#162) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.31 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/0005e0116e92d8489d8d96fbff83f061c79ba95a...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 4 to 5 (#163) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Merge branch 'foundry-rs:master' (#164) * Create ci_cargo.yml (#72) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Rename ci_cargo.yml to cargo.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * fix(fmt): handle trailing coments between base contracts (#12127) * fix(fmt): account for ternary operators when estimating size * fix(fmt): handle comments between inherited base contracts * test: layout + base inheritance * feat(forge): add bypass prevrandao (#12125) * feat(forge): add bypass prevrandao * Update crates/evm/networks/src/lib.rs Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * changes after review: remove duped code --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * fix(fmt): filter libs when recursing (#12119) * fix(fmt): account for ternary operators when estimating size * fix(fmt): filter libs when recursing * style: clippy * test: wipe contracts before formatting * test: explicitly test ignore * fix(fmt): break try stmts in a fn header-like fashion (#12131) * chore(deps): bump softprops/action-gh-release from 2.3.4 to 2.4.1 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.4 to 2.4.1. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/62c96d0c4e8a889135c1f3a25910db8dbe0e85f7...6da8fa9354ddfdc4aeace5fc48d7f679b5214090) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#161) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/e7ef886cf8f69c25ecef6bbc2858a42e273496ec...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): always disable nonce check (foundry-rs#12144) (#165) * test: refactor testdata/ tests to be run in `forge test` (#12049) * test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b10047f608d57c74fb3500a5fbe7e2650e. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a3023ca7ce8e7effccf0ea0a0f28f6e510. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea105c6f38b82664b50ca854f95e456df795. * Revert "debug: fs lock error context" This reverts commit e5caeddd1e4cb457d7b24d7d7fdfdb370e2feabf. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy * fix(anvil): always disable nonce check (#12144) * deps: bump deps (#12149) * deps: bump deps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * minimum Cargo.lock --------- Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#168) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#171) CI/CD Configuration Update: The CircleCI configuration file, cargo.yml, has been updated to use a newer version of the Rust Docker image. Rust Toolchain Version Bump: The cimg/rust Docker image version has been incremented from 1.88.0 to 1.89.0, ensuring the CI pipeline utilizes a more recent Rust toolchain. Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.62.28 to 2.62.33 (#175) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.62.28 to 2.62.33. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.62.28...e43a5023a747770bfcb71ae048541a681714b951) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.62.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .circleci/cargo.yml (#179) I Configuration Removal: The .circleci/cargo.yml file, which defined CircleCI jobs for building and testing Rust projects, has been completely removed from the repository. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#182) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#183) Configuration File Cleanup: Removed an unnecessary blank line in the .circleci/config.yml file, improving its formatting and readability. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#187) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci directory Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci_v1.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Rust Docker image version to 1.89.0 Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Create ci-web3-gamefi.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create codeql.yml (#208) * Update ci.yml (#209) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues. * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Foundry rs maste 1f4b36a (#214) * Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (#10344)" This reverts commit 70ded2b35f95ee9b4ee94f5e44961914d30a87f7. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update and rename docker-image.yml to docker.yml (#218) Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update ci.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml (#224) CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#225) CI: Insert comment lines to delineate and structure sections in .circleci/config.yml for enhanced clarity Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update sequence.rs (#226) Enhancements: Add standalone # lines in sequence.rs to serve as hidden placeholders for rustdoc examples Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#227) * Update dependencies.yml Refactor the weekly dependencies workflow to inline cargo update steps, auto-generate commit messages and PR bodies with update logs, and use the create-pull-request action to open update PRs on a dedicated branch. Enhancements: Define environment variables for GitHub token, branch name, PR title, and PR body including cargo update logs Inline checkout, Rust toolchain setup, and cargo update command with log cleanup instead of relying on an external workflow Craft commit messages and PR bodies dynamically by capturing and formatting cargo update output Use peter-evans/create-pull-request to push Cargo.lock updates to a 'cargo-update' branch CI: Move permissions and GitHub token configuration into the job context Explicitly set the runner to ubuntu-latest and remove the top-level empty permissions block Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/dependencies.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update npm.yml (#228) CI: Add comment to the Publish Binary step indicating it runs automatically after a successful release workflow or can be triggered manually with a run_id Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update snyk-container.yml (#229) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.yml (#230) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update const.ts (#231) Code Formatting: Removed an extraneous blank line in npm/src/const.ts to improve code cleanliness and consistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Revert "Create web3_defi_gamefi.yml (#61)" (#233) This reverts commit 8575916b7675f246b54daf70cfddccb3f5b97fb0. * Create deploy.yml (#240) * Create deploy.yml CI: Add GitHub Actions workflow to build the Rust project, run tests, and build a Docker image on pushes to main/master Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 106: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#247) Improve readability of the GitHub Actions dependencies workflow by adjusting whitespace and adding blank lines CI: Add blank line before the workflow name declaration Insert blank line after the scheduled cron job entry Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dependencies.yml (#248) CI: Remove extraneous blank line in .github/workflows/dependencies.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#249) CI: Remove dev branch from test workflow triggers Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#253) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.lock (#254) Chores: Regenerate Cargo.lock to update dependencies Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#255) * Create config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#256) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: upgrade tsdown from 0.15.12 to 0.16.1 Snyk has created this PR to upgrade tsdown from 0.15.12 to 0.16.1. See this package in npm: tsdown See this project in Snyk: https://app.snyk.io/org/dargon789/project/8da85645-409e-46fa-bd46-9b58e7905fb8?utm_source=github-cloud-app&utm_medium=referral&page=upgrade-pr * Create google.yml (#266) CI: Introduce a Google Cloud deployment workflow that builds a Docker image, pushes it to Artifact Registry, and deploys it to a GKE cluster on pushes to the main branches. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.lock (#269) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update flake.nix (#270) Adjust Nix flake development shell configuration for better cross-platform support and simplify dependencies. Enhancements: Remove the dprint dependency from the Nix development shell. Add conditional AppKit framework linkage on Darwin systems in the Nix shell configuration. Drop custom hardeningDisable settings from the Nix development shell definition. https://github.com/apps/gemini-code-assist Code Review This pull request updates the Nix flake configuration to improve cross-platform support and simplify dependencies. The changes include removing dprint and hardeningDisable settings, and conditionally adding the AppKit framework for Darwin systems. While most changes are beneficial, removing dprint from the development shell dependencies while its configuration file remains could cause issues for contributors. I've added a comment regarding this potential inconsistency. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update Cargo.toml (#271) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update nextest.toml (#272) Adjust test runner configuration for nextest to better handle long-running and specific tests. Enhancements: Introduce a dedicated test group that limits chisel-serial tests to a single thread. Increase the default slow-test timeout period to reduce premature terminations for longer-running tests. Expand the slow-timeout override filter to include both ext_integration and can_test_forge_std tests. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update dprint.json (#273) (https://github.com/apps/gemini-code-assist) Code Review This pull request updates the dprint.json configuration file. The changes correctly enable formatting for dprint.json itself by modifying the excludes list, update the JSON and Markdown dprint plugins to their latest versions, and add a final newline to the file for POSIX compliance. These are all good maintenance improvements. The changes have been reviewed and appear to be correct and beneficial. No issues were found. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/apisec-scan.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update counter/README.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Dependabot/cargo/cargo 38744a1864 (#282) * chore(deps): bump alloy-dyn-abi in the cargo group across 1 directory Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] * Update and rename ci.yml to cargo.yml (#268) Update CircleCI configuration to use a different Rust toolchain image and rename the workflow file. Build: Rename the CircleCI configuration file from ci.yml to cargo.yml. Change the CircleCI Docker image to use Rust 1.78.0 instead of 1.88.0. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. * Update config.yml (#283) Summary by Sourcery Update CircleCI pipeline to use a custom Docker executor and job tailored to the project instead of the example hello-world workflow. Enhancements: Introduce a reusable custom executor that pulls from the stable cimg/base Docker image with Docker Hub authentication. CI: Replace the sample say-hello job and workflow with a project-specific job and workflow wired to the new custom executor in .circleci/config.yml. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: use network-specific BaseFeeParams for Optimism in Anvil * Dargon789 patch 1 (#285) * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#167) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/ci_v1.yml (#173) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#174) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .circleci/config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 83: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 93: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 76: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 94: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 80: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update cargo.yml (#210) https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Fix cloning of compiler settings for Vyper input Replace context.clone().compiler_settings.vyper with context.compiler_settings.vyper.clone() to avoid unnecessary cloning of the entire VerificationContext. This reduces memory allocations when creating VyperInput instances. Applied to both etherscan and sourcify verification providers. --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Gengar * merge gh-master (#287) * Create config.yml (#236) Create .circleci/config.yml defining a version 2.1 pipeline with a docker-based "say-hello" job, checkout and echo steps, and a workflow to orchestrate it Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * Update crates/config/src/compilation.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Foundry/ethereum ux (#284) * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 61: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 74: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#105) * Create cargo.yml (#106) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Delete .github/workflows/docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "Create cargo.yml (#106)" This reverts commit 251a2b4fce0c50e3426ffb2022d9abef5b948fa9. * Create cargo.yml (#213) https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Gamefi defi (#288) * chore: ignore RUSTSEC-2025-0137 (#12941) Co-authored-by: Claude * chore(deps): weekly `cargo update` (#12940) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar.git` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 71 packages to latest compatible versions Updating alloy-chains v0.2.23 -> v0.2.24 Updating alloy-consensus v1.1.3 -> v1.2.1 Updating alloy-consensus-any v1.1.3 -> v1.2.1 Updating alloy-contract v1.1.3 -> v1.2.1 Updating alloy-dyn-abi v1.5.1 -> v1.5.2 Updating alloy-eip5792 v1.1.3 -> v1.2.1 Updating alloy-eips v1.1.3 -> v1.2.1 Updating alloy-ens v1.1.3 -> v1.2.1 Updating alloy-genesis v1.1.3 -> v1.2.1 Updating alloy-json-abi v1.5.1 -> v1.5.2 Updating alloy-json-rpc v1.1.3 -> v1.2.1 Updating alloy-network v1.1.3 -> v1.2.1 Updating alloy-network-primitives v1.1.3 -> v1.2.1 Updating alloy-primitives v1.5.1 -> v1.5.2 Updating alloy-provider v1.1.3 -> v1.2.1 Updating alloy-pubsub v1.1.3 -> v1.2.1 Updating alloy-rpc-client v1.1.3 -> v1.2.1 Updating alloy-rpc-types v1.1.3 -> v1.2.1 Updating alloy-rpc-types-anvil v1.1.3 -> v1.2.1 Updating alloy-rpc-types-any v1.1.3 -> v1.2.1 Updating alloy-rpc-types-beacon v1.1.3 -> v1.2.1 Updating alloy-rpc-types-debug v1.1.3 -> v1.2.1 Updating alloy-rpc-types-engine v1.1.3 -> v1.2.1 Updating alloy-rpc-types-eth v1.1.3 -> v1.2.1 Updating alloy-rpc-types-trace v1.1.3 -> v1.2.1 Updating alloy-rpc-types-txpool v1.1.3 -> v1.2.1 Updating alloy-serde v1.1.3 -> v1.2.1 Updating alloy-signer v1.1.3 -> v1.2.1 Updating alloy-signer-aws v1.1.3 -> v1.2.1 Updating alloy-signer-gcp v1.1.3 -> v1.2.1 Updating alloy-signer-ledger v1.1.3 -> v1.2.1 Updating alloy-signer-local v1.1.3 -> v1.2.1 Updating alloy-signer-trezor v1.1.3 -> v1.2.1 Updating alloy-signer-turnkey v1.1.3 -> v1.2.1 Updating alloy-sol-macro v1.5.1 -> v1.5.2 Updating alloy-sol-macro-expander v1.5.1 -> v1.5.2 Updating alloy-sol-macro-input v1.5.1 -> v1.5.2 Updating alloy-sol-type-parser v1.5.1 -> v1.5.2 Updating alloy-sol-types v1.5.1 -> v1.5.2 Updating alloy-transport v1.1.3 -> v1.2.1 Updating alloy-transport-http v1.1.3 -> v1.2.1 Updating alloy-transport-ipc v1.1.3 -> v1.2.1 Updating alloy-transport-ws v1.1.3 -> v1.2.1 Updating alloy-trie v0.9.1 -> v0.9.2 Updating alloy-tx-macros v1.1.3 -> v1.2.1 Unchanged annotate-snippets v0.12.5 (available: v0.12.10) Unchanged anstyle-svg v0.1.11 (available: v0.1.12) Downgrading aws-smithy-runtime v1.9.6 -> v1.9.5 Updating axum-core v0.5.5 -> v0.5.6 Updating cc v1.2.50 -> v1.2.51 Updating derive_more v2.1.0 -> v2.1.1 Updating derive_more-impl v2.1.0 -> v2.1.1 Updating dtoa v1.0.10 -> v1.0.11 Updating find-msvc-tools v0.1.5 -> v0.1.6 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating itoa v1.0.15 -> v1.0.17 Updating jiff v0.2.16 -> v0.2.17 Updating jiff-static v0.2.16 -> v0.2.17 Updating libredox v0.1.11 -> v0.1.12 Updating libz-rs-sys v0.5.4 -> v0.5.5 Unchanged matchit v0.8.4 (available: v0.8.6) Unchanged mdbook v0.4.52 (available: v0.5.2) Updating portable-atomic v1.12.0 -> v1.13.0 Updating proc-macro2 v1.0.103 -> v1.0.104 Unchanged protobuf v3.3.0 (available: v3.7.2) Unchanged protobuf-support v3.3.0 (available: v3.7.2) Unchanged rand v0.8.5 (available: v0.9.2) Unchanged ratatui v0.29.0 (available: v0.30.0) Updating reqwest v0.12.26 -> v0.12.28 Updating ruint v1.17.0 -> v1.17.1 Updating rustix v1.1.2 -> v1.1.3 Updating ryu v1.0.21 -> v1.0.22 Updating schemars v1.1.0 -> v1.2.0 Updating schemars_derive v1.1.0 -> v1.2.0 Updating serde_json v1.0.145 -> v1.0.148 Updating signal-hook-registry v1.4.7 -> v1.4.8 Updating syn-solidity v1.5.1 -> v1.5.2 Updating tempfile v3.23.0 -> v3.24.0 Unchanged trezor-client v0.1.4 (available: v0.1.5) Unchanged unicode-width v0.2.0 (available: v0.2.2) Unchanged vergen v8.3.2 (available: v9.0.6) Updating zlib-rs v0.5.4 -> v0.5.5 Adding zmij v1.0.0 note: to see how you depend on a package, run `cargo tree --invert @` * touchups * touchups --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * Update flake.lock (#12939) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/16642c5' (2025-12-20) → 'github:nix-community/fenix/3479aaf' (2025-12-27) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ea1d299' (2025-12-18) → 'github:rust-lang/rust-analyzer/8c5a68e' (2025-12-26) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/7d853e5' (2025-12-19) → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27) Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(chisel): uninitalized variables (#12937) * chore(deps): bump Swatinem/rust-cache from 2.8.1 to 2.8.2 (#12919) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.8.1 to 2.8.2. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/f13886b937689c021905a6b90929199931d60db1...779680da715d629ac1d338a641029a2f4372abb5) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-version: 2.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 7.0.11 to 8.0.0 (#12918) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.11 to 8.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/22a9089034f40e5a961c8808d113e2c98fb63676...98357b18bf14b5342f975ff684046ec3b2a07725) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore: sepolia rpc url (#12945) chore: sepolia rpc url private * chore(deps): bump crate-ci/typos from 1.40.0 to 1.40.1 (#12949) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.40.0 to 1.40.1. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/2d0ce569feab1f8752f1dde43cc2f2aa53236e06...1a319b54cc9e3b333fed6a5c88ba1a90324da514) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.0 to 3.15.1 (#12950) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.0 to 3.15.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/95732e95d70db3ba1e0adc26a63c5e0375aba78c...1d699fc25db3f9e079cd2f168ca007a4183389be) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.65.1 to 2.65.7 (#12951) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.65.1 to 2.65.7. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/b9c5db3aef04caffaf95a1d03931de10fb2a140f...4c6723ec9c638cccae824b8957c5085b695c8085) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.65.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(config): err on unknown profile (#12946) * test: remove duplicate Issue2851 test (#12953) * chore(cheats): make sign(Wallet) pure (#12912) * chore(cheats): make sign(Wallet) pure * ignore --------- Co-authored-by: Matthias Seitz Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(evm): use timestamp-based blob base fee calculation (#12959) * fix(evm): use timestamp-based blob base fee calculation * chore: use patch * Now BPO1 is default * bump to hardforks to 0.4.7 --------- Co-authored-by: Matthias Seitz * fix(config): reject bare versions in compilation restrictions (#12955) fmt Co-authored-by: tefyosL-sol * Revert "fix(config): err on unknown profile (#12946)" (#12964) This reverts commit 6ff4b52e2e572e93d0cd81591b1bd0e6ad9ed507. * fix(anvil): use B256 instead of TxHash for block hash parameters (#12961) Update mod.rs * Update crates/config/src/compilation.rs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Claude Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: Desant pivo Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Create ci-web3-gamefi.yml (#217) (#289) Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Merge pull request #47 (#290) * Add .circleci/config.yml * Updated config.yml * Updated config.yml * Updated config.yml * Update test.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update test.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * chore(deps): bump revm to 24.0.0 (#10601) * feat: implement add_balance endpoint (#10636) * fix(bindings): ensure forge bind generates snake_case file names (#10622) * fix(bindings): ensure forge bind generates snake_case file names * refactor: use heck crate for snake_case conversion --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: standardize lint help + validate docs existance (#10639) * feat(cast mktx): add support for "--ethsign" option (#10641) - Sign transactions using "eth_signTransaction" on local node with unlocked accounts. - Same TX building logic as in "cast send --unlocked". - Added a test case to validate the new functionality. * chore(wallets): improve error message for signer instantiation failure (#10646) chore(wallets): improve error message on signer instantiation failure * chore: replaced anvil hardforks with alloy hardforks (#10612) * chore: replaced anvil hardforks with alloy hardforks * fixes * fixes * fixes * removed redundant op and alloy hardforks enum * fixes * fixes * bumped alloy hardforks and kept default to prague and isthmus * bumped alloy-hardforks and fixes --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(`anvil`): latest evm version should be prague (#10653) * fix(`anvil`): latest evm version should be prague * fix test * nit * chore(deps): bump tracing-subscriber (#51) Bumps the cargo group with 1 update in the / directory: [tracing-subscriber](https://github.com/tokio-rs/tracing). Updates `tracing-subscriber` from 0.3.19 to 0.3.20 - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update test.yml (#52) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml (#53) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> … * Potential fix for code scanning alert no. 102: Artifact poisoning (#354) * Potential fix for code scanning alert no. 102: Artifact poisoning Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/npm.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix(config): Respect user-configured etherscan URL over chain defaults (#13238) (#357) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * Potential fix for code scanning alert no. 154: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 156: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Hardhat project (#378) * docs(config): complete foundry.toml configuration reference (#13198) * complete config * docs: mark optional fields in config README * use foundry book for full config * docs(config): point to Foundry Book for configuration reference * feat(invariant): add optimization mode for invariant testing (#13196) * feat(invariant): add optimization mode for invariant testing Adds optimization mode for invariant testing, similar to Echidna. This mode maximizes an `int256` return value from a function prefixed with `optimize_`. **Usage:** - Define an invariant function returning `int256` with `optimize_` prefix - Foundry will fuzz to find the sequence that maximizes this value - The best value and sequence are tracked and reported - Sequence shrinking is applied to find the minimal reproducing sequence **Example:** ```solidity function optimize_maxRoundingError() external view returns (int256) { return int256(pool.totalShares()) - int256(pool.expectedShares()); } ``` **Bug fix during implementation:** Fixed a bug in shrink logic where `vm.warp` and `vm.roll` values were not correctly accumulated when replaying shrunk sequences. This caused the "best" sequence to be non-reproducible because time/block values depended on removed calls. The fix: - Keeps reverted calls in sequence during optimization to preserve warp/roll - Accumulates warps/rolls from removed calls into kept calls during shrinking - Updates inspector's cheatcodes.block alongside executor.env Closes #12190 Amp-Thread-ID: https://ampcode.com/threads/T-019bea21-149c-728c-9556-850778b70ea3 Co-authored-by: Amp * refactor: isolate optimization shrinking logic from check mode - Keep shrink_sequence and check_sequence unchanged for regular invariant checks - Add shrink_sequence_value and check_sequence_value for optimization mode - Optimization mode handles warp/roll accumulation from removed calls - replay.rs dispatches to correct shrinking function based on target_value * refactor: cleanup shrink optimization code, extract helpers --------- Co-authored-by: Amp * fix: only classify setUp as test setup if it has no parameters (#13204) Contracts with `setUp(bytes memory)` (common in Gnosis Safe/Zodiac modules) were incorrectly classified as dev/test contracts and excluded from `forge build --sizes` output. Now `setUp` is only classified as a test `Setup` function when it has no parameters, matching Forge's actual test setup behavior. Fixes #11126 * chore: fix clippy lints (#13207) * feat(cast): add --flatten flag to cast interface (#13201) * feat(cast): add --all-in-one flag to cast interface Adds a new `--all-in-one` flag to `cast interface` that inlines inherited/library struct types directly into the generated interface. This addresses the issue where `cast interface` generates a separate `library` block for struct types that originate from inherited interfaces, making the generated interface less usable for some workflows. With `--all-in-one`, all types are consolidated into a single interface: ```solidity // Before (default): library IBase { struct TestStruct { address asset; } } interface Contract { function test(IBase.TestStruct memory) external; } // After (with --all-in-one): interface Contract { struct TestStruct { address asset; } function test(TestStruct memory) external; } ``` Uses alloy-json-abi's `ToSolConfig::one_contract(true)` option introduced in alloy-core 0.8.24. Closes #9960 * chore: cargo fmt * refactor: rename --all-in-one to --flatten per review * chore: fix rustfmt * Update flake.lock (#13212) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/edd5602' (2026-01-17) → 'github:nix-community/fenix/93523fa' (2026-01-24) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/adbff8b' (2026-01-15) → 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/be5afa0' (2026-01-16) → 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) Co-authored-by: github-actions[bot] * chore(deps): weekly `cargo update` (#13213) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 62 packages to latest compatible versions Updating alloy-chains v0.2.29 -> v0.2.30 Updating alloy-consensus v1.4.3 -> v1.5.2 Updating alloy-consensus-any v1.4.3 -> v1.5.2 Updating alloy-contract v1.4.3 -> v1.5.2 Updating alloy-eip5792 v1.4.3 -> v1.5.2 Updating alloy-eip7928 v0.3.0 -> v0.3.2 Updating alloy-eips v1.4.3 -> v1.5.2 Updating alloy-ens v1.4.3 -> v1.5.2 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-genesis v1.4.3 -> v1.5.2 Updating alloy-json-rpc v1.4.3 -> v1.5.2 Updating alloy-network v1.4.3 -> v1.5.2 Updating alloy-network-primitives v1.4.3 -> v1.5.2 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-provider v1.4.3 -> v1.5.2 Updating alloy-pubsub v1.4.3 -> v1.5.2 Updating alloy-rpc-client v1.4.3 -> v1.5.2 Updating alloy-rpc-types v1.4.3 -> v1.5.2 Updating alloy-rpc-types-anvil v1.4.3 -> v1.5.2 Updating alloy-rpc-types-any v1.4.3 -> v1.5.2 Updating alloy-rpc-types-beacon v1.4.3 -> v1.5.2 Updating alloy-rpc-types-debug v1.4.3 -> v1.5.2 Updating alloy-rpc-types-engine v1.4.3 -> v1.5.2 Updating alloy-rpc-types-eth v1.4.3 -> v1.5.2 Updating alloy-rpc-types-trace v1.4.3 -> v1.5.2 Updating alloy-rpc-types-txpool v1.4.3 -> v1.5.2 Updating alloy-serde v1.4.3 -> v1.5.2 Updating alloy-signer v1.4.3 -> v1.5.2 Updating alloy-signer-aws v1.4.3 -> v1.5.2 Updating alloy-signer-gcp v1.4.3 -> v1.5.2 Updating alloy-signer-ledger v1.4.3 -> v1.5.2 Updating alloy-signer-local v1.4.3 -> v1.5.2 Updating alloy-signer-trezor v1.4.3 -> v1.5.2 Updating alloy-signer-turnkey v1.4.3 -> v1.5.2 Updating alloy-transport v1.4.3 -> v1.5.2 Updating alloy-transport-http v1.4.3 -> v1.5.2 Updating alloy-transport-ipc v1.4.3 -> v1.5.2 Updating alloy-transport-ws v1.4.3 -> v1.5.2 Updating alloy-tx-macros v1.4.3 -> v1.5.2 Updating aws-lc-rs v1.15.3 -> v1.15.4 Updating aws-lc-sys v0.36.0 -> v0.37.0 Updating cc v1.2.53 -> v1.2.54 Updating clearscreen v4.0.2 -> v4.0.3 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating libm v0.2.15 -> v0.2.16 Unchanged matchit v0.8.4 (available: v0.8.6) Removing nix v0.29.0 Updating num-conv v0.1.0 -> v0.2.0 Updating opener v0.8.3 -> v0.8.4 Updating openssl-probe v0.2.0 -> v0.2.1 Updating proc-macro2 v1.0.105 -> v1.0.106 Updating process-wrap v8.2.1 -> v9.0.1 Updating quote v1.0.43 -> v1.0.44 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating socket2 v0.6.1 -> v0.6.2 Updating time v0.3.45 -> v0.3.46 Updating time-core v0.1.7 -> v0.1.8 Updating time-macros v0.2.25 -> v0.2.26 Updating uuid v1.19.0 -> v1.20.0 Updating watchexec-signals v5.0.0 -> v5.0.1 Updating watchexec-supervisor v5.0.1 -> v5.0.2 Updating web_atoms v0.2.1 -> v0.2.3 Updating windows v0.61.3 -> v0.62.2 Updating windows-collections v0.2.0 -> v0.3.2 Removing windows-core v0.61.2 Updating windows-future v0.2.1 -> v0.3.2 Removing windows-link v0.1.3 Updating windows-numerics v0.2.0 -> v0.3.1 Removing windows-result v0.3.4 Removing windows-strings v0.4.2 Updating windows-threading v0.1.0 -> v0.2.1 Updating zmij v1.0.15 -> v1.0.16 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * chore(anvil): clean up remaining dead code after Odyssey sunset (#13211) * fix(coverage): correct BRDA hit values for LCOV consistency (#13151) * fix(coverage): correct BRDA hit values for LCOV consistency Per the LCOV tracefile format specification, BRDA hit values should be: - "-" when the expression was never evaluated (line not executed) - "0" when the branch exists and was evaluated but never taken - "N" when the branch was taken N times Previously, we were outputting "-" for all branches with 0 hits, which caused genhtml to fail with "inconsistent" errors when a line was hit (DA shows hits > 0) but branches on that line showed "-". This fix tracks line hits in a first pass, then uses that information to determine whether to output "-" (line never hit) or "0" (line hit but branch not taken) for branches with 0 hits. Fixes foundry-rs/foundry#11548 * fix: clippy explicit_iter_loop warning * test(coverage): add brda_lcov_consistency test for BRDA hit values Verifies that BRDA outputs follow LCOV spec: - "0" when line was executed but branch not taken - "-" when line was never executed This catches the inconsistency that caused genhtml to fail. --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks * feat(forge): add browser wallet support for `forge script` (#12952) * feat(script): add support for browser wallet * fix: browser wallet opts defaults * chore: bump foundry-browser-wallet v0.1.0 * ci: use shared cache mounts for parallel builds (#13231) perf(docker): use shared cache mounts for parallel builds Change cache mount sharing mode from `locked` to `shared` for cargo registry, git, and sccache directories. With `sharing=locked`, parallel builds must wait for exclusive access to each cache, causing builds to queue up even when compilation itself is fast. Both cargo and sccache handle concurrent access correctly, so `shared` is safe and allows parallel builds to proceed without blocking. Amp-Thread-ID: https://ampcode.com/threads/T-019bfc1d-3cee-70ca-9caa-01e33acdff46 Co-authored-by: Amp * fix(anvil): preserve withdrawals in SerializableBlock for state dump/load (#13227) * fix(anvil): preserve withdrawals in SerializableBlock for state dump/load * add: test * fix ci * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.1 to 3.15.2 (#13236) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.15.1 to 3.15.2. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/1d699fc25db3f9e079cd2f168ca007a4183389be...89ab342bd48ff7318caf8d39d6a330c7b1df8f2f) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.15.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.66.2 to 2.67.13 (#13235) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.66.2 to 2.67.13. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/v2.66.2...710817a1645ef40daad5bcde7431ceccf6cc3528) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.13 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump peter-evans/create-pull-request from 8.0.0 to 8.1.0 (#13234) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 8.0.0 to 8.1.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/98357b18bf14b5342f975ff684046ec3b2a07725...c0f553fe549906ede9cf27b5156039d195d2ece0) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 8.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.42.1 to 1.42.2 (#13233) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.1 to 1.42.2. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/65120634e79d8374d1aa2f27e54baa0c364fff5a...a1d64977b4aa1709d6328d518aa753f4899352d8) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.42.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * fix(docker): expose VERGEN_GIT_SHA to build environment (#13237) * docs(coverage): link to stack-too-deep guide in warnings (#13240) Update coverage warnings to point to the new Foundry Book guide instead of the GitHub issue, providing users with actionable techniques to resolve the error. Amp-Thread-ID: https://ampcode.com/threads/T-019bff7a-8502-72b7-af76-794f258e8c67 Co-authored-by: Amp * Polkadot/Kusama/PolkadotTestnet for RPC gas estimation (#12537) * adds polkadot testnet * adds polkadot testnet * bump alloy chains * fmt * Remove kluster * update rpc url * update rpc url * fmt * adds polkadot kusama * bump alloy chains * perf(anvil): avoid redundant block queries in ots_getBlockTransactions (#13243) * fix(anvil): use consistent chain_id fallback for blob params (#13241) * Revert "Delete .circleci/ci_deploy.yml (#322)" (#358) This reverts commit 87dd517cf50fef686c8ef39431d06b16d4744d88. * feat(primitives): introduce `NetworkWallet` impl for `EthereumWallet` (#13248) - Seamless support for eth/op/tempo txs - This aims to replace `WalletSigner`'s impl once `FoundryNetwork` will be used everywhere * refactor(common): make `ProviderBuilder` generic over `Network` (#13250) * refactor(common): make `ProviderBuilder` generic over `Network` - Updated `ProviderBuilder` helper to be generic, which will facilate `FoundryNetwork` rollout - Adjusted the instantiation of `ProviderBuilder` in various locations to use `AnyNetwork`. - Enhanced the `build` and `build_with_wallet` methods to accommodate the new generic structure. * fix: relax trait bound on `N::TransactionRequest` * fix: comment * fix: comment * fix(anvil): return error instead of empty vec for out-of-range log queries (#13251) * fix(config): respect user-configured etherscan URL over chain defaults (#13239) fix(config): Respect user-configured etherscan URL over chain defaults (#13238) * fix(config): respect user-configured etherscan URL over chain defaults * test(config): add tests for custom etherscan URL handling Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` (#13218) * fix(primitives): track both 4844/7594 sidecars presence in `FoundryTransactionRequest::build_typed_tx` * fix: after `TransactionBuilder4844` impl * feat(common): introduce generic `ProviderBuilder::from_config` method (#13268) - keep the existing helpers that erase generic to avoid breaking change * fix(cast): remove redundant chain() call in explorer_client (#13272) Co-authored-by: tefyosL-sol * fix(verify): respect user-configured etherscan URL over chain defaults (#13275) Co-authored-by: tefyosL-sol * Update flake.lock (#13279) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/93523fa' (2026-01-24) → 'github:nix-community/fenix/b2344f3' (2026-01-31) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/39018ac' (2026-01-23) → 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/ab9fbbc' (2026-01-24) → 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) Co-authored-by: github-actions[bot] * fix(invariant): remove unused cloned calldata (#12893) * fix(invariant): prune calldata to bound memory usage in long runs * style: fix formatting in invariant executor * chore: remove vyper files from testdata to fix CI * chore: trigger ci update * fix(invariant): remove unused FuzzCase.calldata field to prevent OOM The calldata field in FuzzCase was stored but never read after construction. Removing it entirely eliminates memory accumulation during long invariant runs. Changes: - Remove FuzzCase.calldata field (unused after construction) - Remove prune_calldata() methods (no longer needed) - Restore vyper test files that were incorrectly deleted Fixes #12397 Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c17c9-d969-7370-bf0d-495e473e8e30 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(debugger): display actual gas usage alongside refund counter (#13271) * feat(debugger): display actual gas usage alongside refund counter * fix(forge-test): fix flamegraph gas inaccuracy issues * reverse `--decode-internal` not default with `--flamechart` * make gas unsigned as `inferno` doesn't support anyway * replace revm-inspectors patch * fix clippy * update tests * update tests * fmt * fix(config): handle decimal string in U256 deserialization (#13284) * fix: adjust numerical cells in gas report to be right aligned (#12883) * Adjust Cells to be Right Aligned in Gas Report * fix test and fmt --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore(deps): weekly `cargo update` (#13280) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 35 packages to latest compatible versions Updating alloy-dyn-abi v1.5.2 -> v1.5.4 Unchanged alloy-evm v0.26.3 (available: v0.27.0) Updating alloy-json-abi v1.5.2 -> v1.5.4 Unchanged alloy-op-evm v0.26.3 (available: v0.27.0) Updating alloy-primitives v1.5.2 -> v1.5.4 Updating alloy-sol-macro v1.5.2 -> v1.5.4 Updating alloy-sol-macro-expander v1.5.2 -> v1.5.4 Updating alloy-sol-macro-input v1.5.2 -> v1.5.4 Updating alloy-sol-type-parser v1.5.2 -> v1.5.4 Updating alloy-sol-types v1.5.2 -> v1.5.4 Updating annotate-snippets v0.12.10 -> v0.12.11 Updating aws-smithy-async v1.2.7 -> v1.2.10 Updating aws-smithy-http-client v1.1.5 -> v1.1.8 Updating aws-smithy-observability v0.2.0 -> v0.2.3 Updating aws-smithy-query v0.60.9 -> v0.60.12 Updating aws-smithy-runtime-api v1.10.0 -> v1.11.2 Updating aws-smithy-types v1.3.6 -> v1.4.2 Updating bytemuck v1.24.0 -> v1.25.0 Updating cc v1.2.54 -> v1.2.55 Updating clap v4.5.54 -> v4.5.56 Updating clap_builder v4.5.54 -> v4.5.56 Updating clap_derive v4.5.49 -> v4.5.55 Updating cliclack v0.3.7 -> v0.3.8 Updating find-msvc-tools v0.1.8 -> v0.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating iana-time-zone v0.1.64 -> v0.1.65 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating keccak-asm v0.1.4 -> v0.1.5 Unchanged matchit v0.8.4 (available: v0.8.6) Updating notify-types v2.0.0 -> v2.1.0 Updating portable-atomic v1.13.0 -> v1.13.1 Updating portable-atomic-util v0.2.4 -> v0.2.5 Unchanged rand v0.8.5 (available: v0.9.2) Unchanged reqwest v0.12.28 (available: v0.13.1) Updating revm-inspectors v0.34.0 -> v0.34.2 Updating sha3-asm v0.1.4 -> v0.1.5 Updating siphasher v1.0.1 -> v1.0.2 Updating slab v0.4.11 -> v0.4.12 Updating syn-solidity v1.5.2 -> v1.5.4 Removing tiny-keccak v2.0.2 Updating zerocopy v0.8.33 -> v0.8.37 Updating zerocopy-derive v0.8.33 -> v0.8.37 Updating zmij v1.0.16 -> v1.0.18 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: update RPC URLs from ithaca.xyz to reth.rs (#13261) * chore: update RPC URLs from ithaca.xyz to reth.rs Co-authored-by: Tim Beiko Amp-Thread-ID: https://ampcode.com/threads/T-019c0a51-3f0a-76eb-ba4a-bfb6a697d9ba Co-authored-by: Amp * fix * fmt * Update rpc.rs * Update rpc.rs * test: update test --------- Co-authored-by: Tim Beiko Co-authored-by: Amp Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: Oliver Nordbjerg Co-authored-by: onbjerg * ci(bench): use depot runner for benchmarks (#13288) * feat(cli): cli markdown docs (#13291) * feat: add foundry-cli-markdown crate Add a new crate for generating Markdown documentation from clap CLIs. This is a fork of clap-markdown with the following enhancements: - Support for grouped options by help heading (PR #48) - Show environment variable names for arguments (PR #50) - Add version information to generated Markdown (PR #52) * feat(cli): add hidden --markdown-help flag Add a hidden --markdown-help flag to forge, cast, anvil, and chisel that prints CLI reference documentation as Markdown and exits. This uses the new foundry-cli-markdown crate to generate the output. * chore(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#13298) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.6.0 to 3.7.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/5e57cd118135c172c3672efd75eb46360885c0ef...c94ce9fb468520275223c153574b00df6fe4bcc9) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 3.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.13 to 2.67.18 (#13297) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.13 to 2.67.18. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/710817a1645ef40daad5bcde7431ceccf6cc3528...650c5ca14212efbbf3e580844b04bdccf68dac31) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 (#13299) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 727cc5b0b19bc265bd5ef28fc66bccb284473b5d to 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/727cc5b0b19bc265bd5ef28fc66bccb284473b5d...5adeaaaf36f64df54f62adb34aa5fbfdb0109d34) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump mikepenz/release-changelog-builder-action from 6.0.1 to 6.1.0 (#13300) chore(deps): bump mikepenz/release-changelog-builder-action Bumps [mikepenz/release-changelog-builder-action](https://github.com/mikepenz/release-changelog-builder-action) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/mikepenz/release-changelog-builder-action/releases) - [Commits](https://github.com/mikepenz/release-changelog-builder-action/compare/439f79b5b5428107c7688c1d2b0e8bacc9b8792c...6faf020194b7c8853f9e55c4fd92e40b02122a04) --- updated-dependencies: - dependency-name: mikepenz/release-changelog-builder-action dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: fix typos CI (#13303) - Add consts to typos ignore list (valid Rust std::env::consts) - Fix LintCotext -> LintContext typos in linter docs * chore(deps): bump crate-ci/typos from 1.42.2 to 1.43.0 (#13296) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.42.2 to 1.43.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/a1d64977b4aa1709d6328d518aa753f4899352d8...93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * sec: bump to `bytes` `^1.11.1` for `RUSTSEC-2026-0007` (#13306) bump to 1.11.1 for patch: https://rustsec.org/advisories/RUSTSEC-2026-0007 * chore(anvil,cast): remove unnecessary `populate_blob_hashes()` (#13308) Both `set_blob_sidecar`/`set_blob_sidecar_7594` implement a call to `populate_blob_hashes()` * feat(forge): generate random fuzz seed if none provided (#13309) * feat(forge): generate random fuzz seed if none provided Generate a random seed for fuzz/invariant tests when no seed is explicitly configured. This ensures reproducibility by always having a seed available that can be passed via --fuzz-seed to reproduce test runs. * feat(forge): print fuzz seed on fuzz failure (#13310) * feat(forge): print fuzz seed on test failure When a fuzz or invariant test fails, print the seed used so users can reproduce the failure with --fuzz-seed. * test: update snapshots for fuzz seed output Add [SEED] redaction pattern to match 'Fuzz seed: 0x...' output. Update all test snapshots that have fuzz/invariant failures to include the new seed line. * fix: use [SEED] placeholder in issue_3055 test snapshot Amp-Thread-ID: https://ampcode.com/threads/T-019c26cb-9d21-74f9-9e49-7ea59885e827 Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp --------- Co-authored-by: Georgios Konstantopoulos Co-authored-by: Amp * feat(anvil): cache block timestamp in mined receipts (#13311) * fix: use shared `display_chain` helper in CLI error handler (#13314) * Update handler.rs * Update handler.rs * Add --enable-tx-gas-limit CLI flag for EIP-7825 support (#13307) add --enable-tx-gas-limit * fix(anvil): use consistent chain_id fallback in fork setup (#13276) Co-authored-by: tefyosL-sol * fix(test-utils): skip build artifacts when copying to temp workspace (#13266) * feat(lint): add common uppercase abbreviations to mixedCase exceptions (#13305) Co-authored-by: onbjerg * fix(fmt): correct indentation for closing brace in empty contracts with comments (#13319) Co-authored-by: onbjerg * feat(anvil): add `trace_replayBlockTransactions` endpoint for block txs tracing (#13098) Co-authored-by: onbjerg * fix(eip712): write diagnostics to stderr instead of stdout (#13293) Co-authored-by: onbjerg * foundryup: tempo now distributes all binaries (#13337) Amp-Thread-ID: https://ampcode.com/threads/T-019c2ea2-963a-744a-8b1d-57709bc295be Co-authored-by: Amp * fix(config): handle vyper section with skip_serializing_if fields (#13318) * fix(config): handle vyper section with skip_serializing_if fields The vyper config section uses skip_serializing_if = Option::is_none on all fields, causing the default serialization to produce an empty dict. This led to all vyper keys being flagged as unknown. Add explicit VYPER_KEYS constant and special-case the vyper section in collect_standalone_section_warnings to use these known keys instead of deriving them from the (empty) default serialization. Fixes #13316 * test(config): add regression tests for vyper config warnings Tests for #13316: - no_false_warnings_for_vyper_config_keys: valid vyper keys in standalone section - no_false_warnings_for_nested_vyper_config_keys: valid vyper keys in profile - warns_on_unknown_vyper_keys: unknown vyper keys should still warn Amp-Thread-ID: https://ampcode.com/threads/T-019c28b9-9c8c-76bf-96a5-ff5a504c0507 Co-authored-by: Amp * fix build issues * fix(config): handle nested vyper section with skip_serializing_if fields The VyperConfig struct uses skip_serializing_if on all Option fields, causing the default serialization to produce an empty dict. This caused false warnings for valid vyper keys like optimize, path, and experimental_codegen when used in profile nested sections like [profile.default.vyper]. Uses the existing VYPER_KEYS constant for nested vyper sections, matching how standalone [vyper] sections are already handled. Fixes #13316 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c297e-a282-7188-8f79-5080d3e451a9 --------- Co-authored-by: Amp Co-authored-by: zerosnacks Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix: broken config test, currently blocking CI (#13340) fix broken config * skip checksum hash in create2 mining when case-insensitive (#13331) * kip checksum hash in create2 mining * fix the clippy * fix: avoid setting FOUNDRY_PROFILE: ci in template workflows, profile does not exist (#13339) avoid encoding FOUNDRY_PROFILE: ci, profile does not exist * perf(evm): wrap Executor.backend in Arc for copy-on-write cloning (#13327) * perf(evm): wrap Executor.backend in Arc for copy-on-write cloning During parallel fuzzing, each worker clones the Executor. Previously this deep-cloned the entire Backend (CacheDB, JournaledState, state snapshots), which could be 10-50MB per clone with 16 workers = 160-800MB wasted memory. This change wraps Backend in Arc and uses Arc::make_mut() for copy-on-write semantics. When workers only read state, they share the same backend. When a worker mutates, it gets its own copy. Expected impact: - ~80% memory reduction for parallel fuzz runs - Faster executor clone (pointer copy instead of deep clone) - No behavioral change: mutations still get isolated copies Amp-Thread-ID: https://ampcode.com/threads/T-019c2af1-f00b-723a-a3c3-25cbd6f3e92b Co-authored-by: Amp * test: update config test expectations for new mixed_case_exceptions Fix test expectations after 1bd687f0d added new values (ID, URL, API, JSON, XML, HTML, HTTP, HTTPS) to lint.mixed_case_exceptions defaults. Amp-Thread-ID: https://ampcode.com/threads/T-019c2af1-f00b-723a-a3c3-25cbd6f3e92b Co-authored-by: Amp * Update config.rs * Update config.rs * fix: restore "URI" in config test JSON expectations Amp-Thread-ID: https://ampcode.com/threads/T-019c2f68-f9df-76bc-ba4c-94fbe1789c9c Co-authored-by: Amp --------- Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks * chore(ci): update time crate (#13348) * test(cast): ignore flaky_run_celo_with_precompiles (Celo RPC no longer supports debug_traceTransaction) (#13347) * fix(test-utils): create destination directory in copy_dir_filtered (#13350) * chore(wallets): Remove `NetworkWallet` impl for `WalletSigner` (#13343) - superseeded by `EthereumWallet`'s one, which is integrated in `TransactionBuilder` flow * fix(anvil): return error when querying future block number in with_database_at (#13267) * return error when querying future block number * fix test --------- Co-authored-by: onbjerg Co-authored-by: Matthias Seitz * chore: remove stale `tiny-keccak` references (#13358) * chore: remove stale tiny-keccak profile override * chore: remove stale tiny-keccak deny exception * chore(script): typo (#13353) * perf(cheatcodes): loop invariant code motion by hand (#13357) * chore(anvil): remove unnecessary clone operations (#13330) * perf(linking): replace double hash mpa lookup contains_key + [] with single get (#13361) * fix(verify): correct Sourcify API URL construction for custom chains (#13360) Update verify.rs * chore(common): remove dead `with_spinner_reporter` function (#13366) * resolve absolute and relative paths on Windows (#13364) * fix: unittest failed (#13371) * perf(anvil): reuse storage root from prove_storage instead of recompu… (#13363) perf(anvil): reuse storage root from prove_storage instead of recomputing * chore(deps): weekly `cargo update` (#13384) Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git repository `https://github.com/paradigmxyz/reth` Locking 94 packages to latest compatible versions Updating alloy-consensus v1.5.2 -> v1.6.1 Updating alloy-consensus-any v1.5.2 -> v1.6.1 Updating alloy-contract v1.5.2 -> v1.6.1 Updating alloy-eip5792 v1.5.2 -> v1.6.1 Updating alloy-eips v1.5.2 -> v1.6.1 Updating alloy-ens v1.5.2 -> v1.6.1 Updating alloy-evm v0.26.3 -> v0.26.4 (available: v0.27.2) Updating alloy-genesis v1.5.2 -> v1.6.1 Updating alloy-json-rpc v1.5.2 -> v1.6.1 Updating alloy-network v1.5.2 -> v1.6.1 Updating alloy-network-primitives v1.5.2 -> v1.6.1 Updating alloy-op-evm v0.26.3 -> v0.26.4 (available: v0.27.2) Updating alloy-provider v1.5.2 -> v1.6.1 Updating alloy-pubsub v1.5.2 -> v1.6.1 Updating alloy-rlp v0.3.12 -> v0.3.13 Updating alloy-rlp-derive v0.3.12 -> v0.3.13 Updating alloy-rpc-client v1.5.2 -> v1.6.1 Updating alloy-rpc-types v1.5.2 -> v1.6.1 Updating alloy-rpc-types-anvil v1.5.2 -> v1.6.1 Updating alloy-rpc-types-any v1.5.2 -> v1.6.1 Updating alloy-rpc-types-beacon v1.5.2 -> v1.6.1 Updating alloy-rpc-types-debug v1.5.2 -> v1.6.1 Updating alloy-rpc-types-engine v1.5.2 -> v1.6.1 Updating alloy-rpc-types-eth v1.5.2 -> v1.6.1 Updating alloy-rpc-types-trace v1.5.2 -> v1.6.1 Updating alloy-rpc-types-txpool v1.5.2 -> v1.6.1 Updating alloy-serde v1.5.2 -> v1.6.1 Updating alloy-signer v1.5.2 -> v1.6.1 Updating alloy-signer-aws v1.5.2 -> v1.6.1 Updating alloy-signer-gcp v1.5.2 -> v1.6.1 Updating alloy-signer-ledger v1.5.2 -> v1.6.1 Updating alloy-signer-local v1.5.2 -> v1.6.1 Updating alloy-signer-trezor v1.5.2 -> v1.6.1 Updating alloy-signer-turnkey v1.5.2 -> v1.6.1 Updating alloy-transport v1.5.2 -> v1.6.1 Updating alloy-transport-http v1.5.2 -> v1.6.1 Updating alloy-transport-ipc v1.5.2 -> v1.6.1 Updating alloy-transport-ws v1.5.2 -> v1.6.1 Updating alloy-trie v0.9.3 -> v0.9.4 Updating alloy-tx-macros v1.5.2 -> v1.6.1 Updating anyhow v1.0.100 -> v1.0.101 Updating async-compression v0.4.37 -> v0.4.39 Updating aws-config v1.8.12 -> v1.8.13 Updating aws-runtime v1.5.18 -> v1.6.0 Updating aws-sdk-kms v1.98.0 -> v1.99.0 Updating aws-sdk-sso v1.92.0 -> v1.93.0 Updating aws-sdk-ssooidc v1.94.0 -> v1.95.0 Updating aws-sdk-sts v1.96.0 -> v1.97.0 Updating aws-sigv4 v1.3.7 -> v1.3.8 Updating aws-smithy-async v1.2.10 -> v1.2.11 Updating aws-smithy-http v0.62.6 -> v0.63.3 Updating aws-smithy-http-client v1.1.8 -> v1.1.9 Updating aws-smithy-json v0.61.9 -> v0.62.3 Updating aws-smithy-observability v0.2.3 -> v0.2.4 Updating aws-smithy-query v0.60.12 -> v0.60.13 Updating aws-smithy-runtime v1.9.8 -> v1.10.0 Updating aws-smithy-runtime-api v1.11.2 -> v1.11.3 Updating aws-smithy-types v1.4.2 -> v1.4.3 Updating clap v4.5.56 -> v4.5.57 Updating clap_builder v4.5.56 -> v4.5.57 Updating flate2 v1.1.8 -> v1.1.9 Unchanged generic-array v0.14.7 (available: v0.14.9) Updating hyper-util v0.1.19 -> v0.1.20 Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating interprocess v2.2.3 -> v2.3.1 Updating jiff v0.2.18 -> v0.2.19 Updating jiff-static v0.2.18 -> v0.2.19 Unchanged matchit v0.8.4 (available: v0.8.6) Updating memchr v2.7.6 -> v2.8.0 Updating nybbles v0.4.7 -> v0.4.8 Updating pest v2.8.5 -> v2.8.6 Updating pest_derive v2.8.5 -> v2.8.6 Updating pest_generator v2.8.5 -> v2.8.6 Updating pest_meta v2.8.5 -> v2.8.6 Updating proptest v1.9.0 -> v1.10.0 Unchanged rand v0.8.5 (available: v0.9.2) Updating rapidhash v4.2.1 -> v4.2.2 Updating regex v1.12.2 -> v1.12.3 Updating regex-automata v0.4.13 -> v0.4.14 Updating regex-lite v0.1.8 -> v0.1.9 Updating regex-syntax v0.8.8 -> v0.8.9 Unchanged reqwest v0.12.28 (available: v0.13.2) Updating schemars v1.2.0 -> v1.2.1 Updating schemars_derive v1.2.0 -> v1.2.1 Updating sval v2.16.0 -> v2.17.0 Updating sval_buffer v2.16.0 -> v2.17.0 Updating sval_dynamic v2.16.0 -> v2.17.0 Updating sval_fmt v2.16.0 -> v2.17.0 Updating sval_json v2.16.0 -> v2.17.0 Updating sval_nested v2.16.0 -> v2.17.0 Updating sval_ref v2.16.0 -> v2.17.0 Updating sval_serde v2.16.0 -> v2.17.0 Updating system-configuration v0.6.1 -> v0.7.0 Updating webbrowser v1.0.6 -> v1.1.0 Updating webpki-roots v1.0.5 -> v1.0.6 Updating zerocopy v0.8.37 -> v0.8.39 Updating zerocopy-derive v0.8.37 -> v0.8.39 Updating zlib-rs v0.5.5 -> v0.6.0 Updating zmij v1.0.18 -> v1.0.19 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * Update flake.lock (#13383) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/b2344f3' (2026-01-31) → 'github:nix-community/fenix/e1b28f6' (2026-02-07) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/eb05888' (2026-01-30) → 'github:rust-lang/rust-analyzer/d2a00da' (2026-02-05) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/6308c3b' (2026-01-30) → 'github:NixOS/nixpkgs/ae67888' (2026-02-06) Co-authored-by: github-actions[bot] * perf(verify): reuse transaction from earlier RPC call instead of fetching twice (#13391) * perf(verify): reuse transaction from earlier RPC call instead of fetching twice * fix ci * fix(cast): --json support for erc20 cmds (#12727) * refactor(anvil): using is_ok since it's more robust (#13377) * fix: may div by zero (#13369) * refactor(primitives): turn `FoundryTransactionRequest` into an enum (#13278) - Combines Eth's, Op's, and Tempo's transaction requests to inherit Op/Tempo tx building * perf: avoid checksum (#13374) * docs: slim readme (#13393) * fix: correct trace message in dynamic linking preprocessor (#13394) * perf(invariant): avoid cloning state changeset in fuzz runs (#13398) Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> * chore(deps): bump depot/build-push-action from 1.16.2 to 1.17.0 (#13405) Bumps [depot/build-push-action](https://github.com/depot/build-push-action) from 1.16.2 to 1.17.0. - [Release notes](https://github.com/depot/build-push-action/releases) - [Commits](https://github.com/depot/build-push-action/compare/9785b135c3c76c33db102e45be96a25ab55cd507...5f3b3c2e5a00f0093de47f657aeaefcedff27d18) --- updated-dependencies: - dependency-name: depot/build-push-action dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.67.18 to 2.67.27 (#13406) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.18 to 2.67.27. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/650c5ca14212efbbf3e580844b04bdccf68dac31...1e67dedb5e3c590e1c9d9272ace46ef689da250d) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.67.27 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.0 to 1.43.4 (#13407) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.43.0 to 1.43.4. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/93cbdb2d23269548cf0db0f74d0bc6a09a3f0d5c...78bc6fb2c0d734235d57a2d6b9de923cc325ebdd) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ci: use dedicated template for isolate flaky test failures (#13409) * chore(deps): bump depot/setup-action from 1.6.0 to 1.7.1 (#13408) * fix(primitives): `FoundryTransactionRequest` conversion w/ tempo variant (#13401) - Fix `TempoTransactionRequest` variant, as inner req was always set to default. - Added unit tests to assess `FoundryTransactionRequest` proper variant routing * return error instead of empty array when filter not found (#13415) * chore(config): remove unused enum accessor methods (#13414) * fix(cast): clean up temp dir in `cast storage` when etherscan cache is unavailable (#13418) * perf(primitives): avoid cloning receipts (#13396) * fix: constructor params and args check (#13375) * fix: correct path format in get_paths doc comment (#13388) * ci: replace merge_group with push on master (#13419) * ci(release): pin action-gh-release to v2.4.2 (#13420) v2.5.0 introduced a draft→finalize flow that races in matrix jobs, causing 'Too many retries' failures in the Create release step. See: https://github.com/softprops/action-gh-release/issues/704 Amp-Thread-ID: https://ampcode.com/threads/T-019c4c6d-c55a-752a-8b27-25413f485bed Co-authored-by: Amp * fix(anvil): handle disk cache write failures in state eviction (#13332) * feat(forge,chisel): realtime `console.log` (#13321) * fix(cast): remove duplicate receipt handling in Tempo transactions (#13378) * perf(traces): deduplicate addresses before external fetching (#13320) * fix: prevent panic on etherscan client creation failure in test command (#13395) * perf(config): skip redundant remapping detection in _with_root (#13389) * fix(common): remove trailing space in `state_root` match pattern (#13426) * chore(config): `curl` mode as config key (#13260) Co-authored-by: onbjerg * fix(config): normalize deny_warnings from env vars (#13434) * fix: correct dead condition in command error formatting (#13427) * add missing JSON output support for `erc20 decimals` (#13438) * fix(anvil): variable shadowing bug in ReadyTransactions::remove_with_markers (#13436) Update transactions.rs * Update flake.lock (#13448) * chore(deps): weekly `cargo update` (#13449) * feat(evm): `ForkDatabase`/`MultiFork` generic over `Network` (#13459) * feat(evm): `ForkDatabase`/`MultiFork` generic over `Network` bump `foundry-fork-db` * fix: typo * fix(cheatcodes): fix vm.expectRevert for direct precompile calls (#13460) Precompile calls don't create an interpreter frame, so `initialize_interp` never fires and `max_depth` never gets bumped beyond the cheatcode call depth. This causes the depth check in `handle_expect_revert` to fail with "call didn't revert at a lower depth than cheatcode call depth". Track `max_depth` in the `call` hook as well, accounting for the callee depth (`curr_depth + 1`). Amp-Thread-ID: https://ampcode.com/threads/T-019c63a2-2c36-7334-ab55-2931a174b59c Co-authored-by: Amp * fix(lint): remove unreachable macro arm in declare_forge_lint (#13452) * chore(flake): use nightly rustfmt (#13441) * chore(flake): use nightly rustfmt * chore(flake): update flake * feat: add `executeTransaction` cheatcode (#13437) feat: add executeTransaction cheatcode Port the executeTransaction cheatcode from tempoxyz/tempo-foundry. Executes RLP-encoded signed transactions in an isolated EVM context with full semantics (like --isolate mode). OP deposit and Tempo AA transactions return errors for now (marked with TODOs). * fix(forge): don't reset snapshot diff result on missing file (#13442) * fix(traces): check HTTP status before JSON parsing in Sourcify fetcher (#13446) * Update external.rs * chore: fmt * test: rm useless tests --------- Co-authored-by: Oliver Nordbjerg * feat(cheatcodes): add Ed25519 crypto cheatcodes (#13450) * feat(cheatcodes): add Ed25519 crypto cheatcodes Add four new cheatcodes for Ed25519 cryptography: - createEd25519Key(bytes32 salt) - deterministic key generation - publicKeyEd25519(bytes32 privateKey) - derive public key - signEd25519(namespace, message, privateKey) - sign with domain separation - verifyEd25519(signature, namespace, message, publicKey) - verify signatures Uses ed25519-consensus crate. Includes comprehensive unit tests for determinism, namespace separation, edge cases, and invalid inputs. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c5f04-a6ed-7015-9b4d-4464a35bc26c * chore: solidity test * test: fix assertions --------- Co-authored-by: Amp Co-authored-by: Oliver Nordbjerg * feat(lint): add missing visit methods to EarlyLintVisitor (#13454) Update early.rs * notify subscribers for txs promoted after block mining (#13464) * notify subscribers for txs promoted after block mining * refactor: extract notify_ready helper to deduplicate notification logic Amp-Thread-ID: https://ampcode.com/threads/T-019c6840-d225-723a-bf92-46e4e29c7ad1 Co-authored-by: Amp --------- Co-authored-by: Matthias Seitz Co-authored-by: Amp * chore(deps): bump taiki-e/install-action from 2.67.27 to 2.68.0 (#13465) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.67.27 to 2.68.0. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/1e67dedb5e3c590e1c9d9272ace46ef689da250d...f8d25fb8a2df08dcd3cead89780d572767b8655f) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.68.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/cache-cargo-install-action from 3.0.1 to 3.0.2 (#13466) Bumps [taiki-e/cache-cargo-install-action](https://github.com/taiki-e/cache-cargo-install-action) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/taiki-e/cache-cargo-install-action/releases) - [Changelog](https://github.com/taiki-e/cache-cargo-install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/cache-cargo-install-action/compare/34ce5120836e5f9f1508d8713d7fdea0e8facd6f...2bfc3cedaf2ee5e7fa5d0ae034ccd5fb50cf8e1f) --- updated-dependencies: - dependency-name: taiki-e/cache-cargo-install-action dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.4 to 1.43.5 (#13467) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.43.4 to 1.43.5. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/78bc6fb2c0d734235d57a2d6b9de923cc325ebdd...57b11c6b7e54c402ccd9cda953f1072ec4f78e33) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-version: 1.43.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/update-flake-lock from 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 to a135ea602656a8348c5c34887131dd9f7a28bd8c (#13468) chore(deps): bump DeterminateSystems/update-flake-lock Bumps [DeterminateSystems/update-flake-lock](https://github.com/determinatesystems/update-flake-lock) from 5adeaaaf36f64df54f62adb34aa5fbfdb0109d34 to a135ea602656a8348c5c34887131dd9f7a28bd8c. - [Release notes](https://github.com/determinatesystems/update-flake-lock/releases) - [Commits](https://github.com/determinatesystems/update-flake-lock/compare/5adeaaaf36f64df54f62adb34aa5fbfdb0109d34...a135ea602656a8348c5c34887131dd9f7a28bd8c) --- updated-dependencies: - dependency-name: DeterminateSystems/update-flake-lock dependency-version: a135ea602656a8348c5c34887131dd9f7a28bd8c dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump softprops/action-gh-release from 2.4.2 to 2.5.0 (#13469) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.4.2 to 2.5.0. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/5be0e66d93ac7ed76da52eca8bb058f665c3a5fe...a06a81a03ee405af7f2048a818ed3f03bbf83c7b) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * test: mark clone CLI tests as flaky (#13472) These tests hit real Etherscan/Sourcify APIs and fail intermittently due to rate limiting and network issues. They will now run in the nightly flaky test workflow with retries instead of blocking every PR. Amp-Thread-ID: https://ampcode.com/threads/T-019c6840-d225-723a-bf92-46e4e29c7ad1 Co-authored-by: Amp * fix: bind TempDir guard in clone test to prevent premature cleanup (#13471) Amp-Thread-ID: https://ampcode.com/threads/T-019c6840-d225-723a-bf92-46e4e29c7ad1 Co-authored-by: Amp * prevent balance overflow in anvil_addBalance (#13457) Co-authored-by: Matthias Seitz * chore(tests): bump forge-std version (#13482) * move `sccache --show-stats` into build RUN to show actual stats (#13483) * fix(anvil): correct blob_gas_used_ratio calculation in fee history (#13491) Update fees.rs * fix(cheatcodes): make vm.executeTransaction work in isolation mode (#13475) * fix(test): exclude ExecuteTransactionTest from isolation mode vm.executeTransaction already performs its own isolated execution (fresh EVM, cloned state, state merging). When isolation mode is enabled, the inspector's transact_inner intercepts CALLs at depth==1 inside the cheatcode's inner EVM, causing double-isolation that results in 'transaction reverted: 0x'. Amp-Thread-ID: https://ampcode.com/threads/T-019c6ad3-d3f0-70d3-8d78-38ccd8444e9e Co-authored-by: Amp * fix(cheatcodes): make vm.executeTransaction work in isolation mode Two bugs prevented vm.executeTransaction from working with --isolate: 1. Double isolation: executeTransaction creates its own inner EVM at depth=1, but the isolation inspector also intercepts CALLs at depth=1, causing a nested transact_inner. Fix: add set_in_inner_context() to CheatcodesExecutor trait and set it before/after the inner EVM run, matching how transact_inner already handles this. 2. Corrupted cfg env: executeTransaction modified env.cfg (disabled nonce checks, set initcode size limit) but never restored it. Subsequent isolated calls then failed nonce validation (NonceTooHigh). Fix: restore env.cfg from the cached copy alongside env.tx and basefee. Amp-Thread-ID: https://ampcode.com/threads/T-019c6ad3-d3f0-70d3-8d78-38ccd8444e9e Co-authored-by: Amp --------- Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test-utils: remove unused `IS_TTY` helper (#13492) Update util.rs * chore: update LATEST_SOLC to 0.8.34 (#13489) Amp-Thread-ID: https://ampcode.com/threads/T-019c7441-de53-7338-86cb-6d84f755016a Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(anvil): add --max-transactions CLI flag (#13495) * remove unimplemented anvil_enableTraces endpoint (#13499) * feat(fmt): pretty printing for generic block/transaction responses (#13497) feat(fmt): pretty printing for generic block/transaction reponses * Refactor `locked_read_to_string` to reuse `locked_read (#13494) Update fs.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(script-sequence): extract duplicated filter logic (#13500) * chore(broadcast): cleanup avg gas price calculation (#13509) * chore(deps): weekly `cargo update` (#13513) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * feat(common): generic `TransactionReceiptWithRevertReason` + pprinting (#13503) * refactor: Use `fs::write_pretty_json_file` in `MultiChainSequence::save` (#13510) * Update flake.lock (#13511) flake.lock: Update Flake lock file updates: • Updated input 'fenix': 'github:nix-community/fenix/d0555da' (2026-02-14) → 'github:nix-community/fenix/6d86ae5' (2026-02-21) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/bbc84d3' (2026-02-13) → 'github:rust-lang/rust-analyzer/46a214b' (2026-02-20) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/2343bbb' (2026-02-11) → 'github:NixOS/nixpkgs/d1c15b7' (2026-02-16) Co-authored-by: github-actions[bot] * fix(sol-macro-gen): correct identifier check in write_mod_name (#13508) * chore(deps): bump DeterminateSystems/update-flake-lock from a135ea602656a8348c5c34887131dd9f7a28bd8c to 5909792a83875ddb5dd4b18734534a98a74a709c (#13524) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump taiki-e/install-action from 2.68.0 to 2.68.8 (#13523) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.15.2 to 3.16.1 (#13522) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): prevent panic in `utc_from_secs` for out-of-range timestamps (#13520) * ci(release): revert action-gh-release to v2.4.2 (#13527) * ci: ignore softprops/action-gh-release in dependabot (#13528) * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.16.1 to 3.16.3 (#13529) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.16.1 to 3.16.3. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/681d8e8bfdb5d7af56f113ba2425b1fb00ec9edc...73327eb48f028efaaf5013656ba216ca3cdeca7b) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.16.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(primitives): add `FoundryTransactionBuilder` trait (#13512) Co-authored-by: figtracer <1gusredo@gmail.com> * fix(wallets): use turnkey_unsupported() instead of hardcoded error (#13535) * chore(deps): bump taiki-e/install-action from 2.68.8 to 2.68.9 (#13534) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.68.8 to 2.68.9. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/cfdb446e391c69574ebc316dfb7d7849ec12b940...7f491e26f71f4ec2e6902c7c95c73043f209ab79) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.68.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(anvil): preserve original error in update_url (#13531) Co-authored-by: Oliver Nordbjerg * fix: swap incorrect doc comments for archive RPC URL functions in rpc. (#13480) * Allow verifier-url for unknown Etherscan chains (#13079) Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: onbjerg * fix: remove unused `no_storage_caching()` method from `NodeConfig` (#13487) * fix: remove code duplication in `get_runtime_codes` (#13502) * forge: avoid repeated selector decoding in find command (#13516) * fix(cli): Git::is_repo_root always returns false (#13505) * fix(verify): remove unused functions from `VerificationContext` (#13481) Co-authored-by: onbjerg * add missing doc section keys to config validation (#13447) * fix(anvil): use EIP-2718 encoding for OP enveloped_tx (#13537) * fix: correct flag name in error message from --compiler-version to --… (#13539) * chore(ci): unblock ci, fix clippy lint (#13543) * add lint-fix * fix: resolve nightly clippy warnings - collapsible_match: collapse plain if into match guards, allow for if-let - iter_kv_map: use .values()/.keys() instead of .iter().flat_map(|(_, v)| v) - useless_conversion: remove unnecessary .into_iter() Amp-Thread-ID: https://ampcode.com/threads/T-019c9930-51be-760a-b2c7-9a029f851fee Co-authored-by: Amp * fix: add missing match arm for Occupied entry in remappings Amp-Thread-ID: https://ampcode.com/threads/T-019c99a5-39f3-72be-ad16-e7d041662ea9 Co-authored-by: Amp * fix: revert incorrect .values() call on Vec in runner Amp-Thread-ID: https://ampcode.com/threads/T-019c99a5-39f3-72be-ad16-e7d041662ea9 Co-authored-by: Amp * fix: resolve irrefutable let pattern warning in MultiForkHandler Amp-Thread-ID: https://ampcode.com/threads/T-019c99a5-39f3-72be-ad16-e7d041662ea9 Co-authored-by: Amp --------- Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Amp * refactor(wallets): browser wallet generic `Network` (#13550) * fix(anvil): use individual tx gas instead of cumulative in fee history (#13552) * fix(anvil): clear tx pool on anvil_reset (#13544) * add EIP-3860 initcode size check in txpool validation (#13473) * fix: clarify Anvil storage caching builder naming (#13546) Co-authored-by: Amp Co-authored-by: Oliver Nordbjerg * refactor(evm): remove dead BackendError::Other variant (#13553) * fix(script): actually skip Vyper contract verification (#13484) Co-authored-by: onbjerg * feat(doc): Adding new 'Constants' section for constants and immutables (#13116) Co-authored-by: onbjerg Co-authored-by: Oliver Nordbjerg * fix(evm): avoid wrong CowBackend initialization in load_allocs and clone_account (#13554) Co-authored-by: Amp Co-authored-by: Matthias Seitz * chore: use `fs::write_pretty_json_file` in `ScriptSequence::save` (#13562) * fix(forge): apply --access-list in forge create (#13557) Co-authored-by: onbjerg * Update flake.lock (#13564) Co-authored-by: github-actions[bot] Co-authored-by: onbjerg * chore(deps): weekly `cargo update` (#13565) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * fix(cast): correct max_int boundary check for uint255 (#13568) * fix(fmt): correct total_difficulty attribute name (#13578) * chore(evm): use `AnyRpcTransaction` in `configure_tx_env`/`commit_transaction` (#13572) * feat(evm): add `AsEnvMut::set_env` (#13573) * fix(fmt): don't inline while/for/if blocks with multiple statements (#13566) * refactor(cheatcodes,evm): use ContextTr read accessors for env fields (#13582) Co-authored-by: Amp * chore(evm): simplify `CowBackend` init logic by using `Option` (#13584) * feat(anvil): add EIP-2935 blockhash histo… * Potential fix for code scanning alert no. 155: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 170: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * foundry-rs#13763 (#398) * fix: update EtherlinkTestnet -> EtherlinkShadownet for alloy-chains v0.2.31 (#13763) Co-authored-by: Matthias Seitz <19890894+mattsse@users.noreply.github.com> * chore(evm): make `FoundryCfg` generic over `Spec` (#13757) * chore(deps): weekly `cargo update` (#13760) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/alloy-rs/evm.git` Updating git repository `https://github.com/foundry-rs/optimism` Updating git submodule `https://github.com/flashbots/op-rbuilder` Updating git submodule `https://github.com/foundry-rs/forge-std` Updating git submodule `https://github.com/runtimeverification/kontrol-cheatcodes` Updating git submodule `https://github.com/ethereum-optimism/lib-keccak` Updating git submodule `https://github.com/dapphub/ds-test` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable` Updating git submodule `https://github.com/OpenZeppelin/openzeppelin-contracts` Updating git submodule `https://github.com/a16z/erc4626-tests.git` Updating git submodule `https://github.com/safe-global/safe-contracts` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/transmissions11/solmate` Updating git submodule `https://github.com/ethereum-optimism/superchain-registry` Updating git submodule `https://github.com/flashbots/rollup-boost` Updating git repository `https://github.com/foundry-rs/foundry-fork-db` Updating git repository `https://github.com/paradigmxyz/revm-inspectors.git` Updating git repository `https://github.com/rust-cli/rexpect` Updating git repository `https://github.com/paradigmxyz/solar` Skipping git submodule `https://github.com/argotorg/solidity.git` due to update strategy in .gitmodules Updating git repository `https://github.com/tempoxyz/tempo` Updating git submodule `https://github.com/foundry-rs/forge-std` Updating git submodule `https://github.com/vectorized/solady` Updating git submodule `https://github.com/tempoxyz/tempo-std` Updating git repository `https://github.com/stevencartavia/reth` Locking 31 packages to latest compatible versions Updating alloy-chains v0.2.30 -> v0.2.31 Updating alloy-trie v0.9.4 -> v0.9.5 Adding anstream v1.0.0 Unchanged anstream v0.6.21 (available: v1.0.0) Updating anstyle v1.0.13 -> v1.0.14 Updating anstyle-lossy v1.1.4 -> v1.1.5 Adding anstyle-parse v1.0.0 Updating bon v3.9.0 -> v3.9.1 Updating bon-macros v3.9.0 -> v3.9.1 Updating c-kzg v2.1.6 -> v2.1.7 Updating cc v1.2.56 -> v1.2.57 Updating clap v4.5.60 -> v4.6.0 Updating clap_builder v4.5.60 -> v4.6.0 Updating clap_complete v4.5.66 -> v4.6.0 Updating clap_complete_nushell v4.5.10 -> v4.6.0 Updating clap_derive v4.5.55 -> v4.6.0 Updating clap_lex v1.0.0 -> v1.1.0 Updating colorchoice v1.0.4 -> v1.0.5 Updating console v0.16.2 -> v0.16.3 Removing darling v0.21.3 Removing darling_core v0.21.3 Removing darling_macro v0.21.3 Updating derive-where v1.6.0 -> v1.6.1 Updating evmole v0.8.2 -> v0.8.4 Unchanged generic-array v0.14.7 (available: v0.14.9) Unchanged icu_collections v2.0.0 (available: v2.1.1) Unchanged icu_normalizer v2.0.1 (available: v2.1.1) Unchanged icu_normalizer_data v2.0.0 (available: v2.1.1) Unchanged icu_properties v2.0.2 (available: v2.1.2) Unchanged icu_properties_data v2.0.1 (available: v2.1.2) Unchanged idna_adapter v1.1.0 (available: v1.2.1) Updating kasuari v0.4.11 -> v0.4.12 Unchanged matchit v0.8.4 (available: v0.8.6) Updating once_cell v1.21.3 -> v1.21.4 Unchanged op-revm v15.0.0 (available: v17.0.0) Updating portable-atomic-util v0.2.5 -> v0.2.6 Unchanged quick-junit v0.5.2 (available: v0.6.0) Unchanged rand v0.8.5 (available: v0.10.0) Unchanged rand v0.9.2 (available: v0.10.0) Unchanged revm v34.0.0 (available: v36.0.0) Updating schannel v0.1.28 -> v0.1.29 Updating serde_with v3.17.0 -> v3.18.0 Updating serde_with_macros v3.17.0 -> v3.18.0 Unchanged snapbox v0.6.24 (available: v1.1.0) Unchanged soldeer-commands v0.10.0 (available: v0.10.1) Unchanged soldeer-core v0.10.0 (available: v0.10.1) Unchanged strum v0.27.2 (available: v0.28.0) Updating tempfile v3.26.0 -> v3.27.0 Updating tinyvec v1.10.0 -> v1.11.0 Unchanged toml v0.9.12+spec-1.1.0 (available: v1.0.6+spec-1.1.0) Unchanged toml_edit v0.24.1+spec-1.1.0 (available: v0.25.4+spec-1.1.0) Updating tracing-subscriber v0.3.22 -> v0.3.23 Updating zerocopy v0.8.41 -> v0.8.42 Updating zerocopy-derive v0.8.41 -> v0.8.42 note: to see how you depend on a package, run `cargo tree --invert @` Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz * feat(cheatcodes): bubble-up `Network` generic to `Wallets` (#13768) * feat(script): generic `TxStatus` receipt type (#13770) feat(script): generic `TxStatus` * chore(script): idiomatic `BroadcastReader::into_tx_receipts` (#13771) * refactor(evm): simplify `Backend::initialize` and `CowBackend::backend_mut` (#13755) `initialize` now takes `(SpecId, Address, TxKind)` instead of `&Env`, since those are the only fields it reads. This removes the need for `backend_mut` to clone `EvmEnv` — it only needs `&TxEnv` now, because the `SpecId` already comes from `self.spec_id`. Moves towards aligning with alloy-evm's BlockExecutor ownership model where `EvmEnv` is block-scoped and `TxEnv` is tx-scoped. * feat(cheatcodes): make `Cheatcodes` context-generic (#13767) * feat(cheatcodes): make `Cheatcodes` context-generic * fix: merge conflict --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(cast): add `--network` flag to `cast tx` for network-specific raw encoding (#13745) * feat(cast): add --network flag to `cast tx` for multi-network raw encoding Replace the `FoundryNetwork`-based `transaction_raw` workaround with proper network selection via a new `--network` / `-n` CLI flag. This moves raw tx encoding back into `Cast::transaction` dispatching to the correct provider (Ethereum, Optimism, or Tempo) based on the flag. - Add `NetworkVariant` enum (Ethereum/Optimism/Tempo) in foundry-cli opts - use it w/ `--network` through CastSubcommand::Tx and provider construction - Add test for Tempo raw tx encoding (`tx_raw_tempo`) * fix: network num args * feat(cast): `block --raw` network selection (#13754) * rebase PR 13745 * feat(cast): `block` for multi-network raw encoding Co-authored-by: figtracer <1gusredo@gmail.com> * fix: doctest --------- Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): make mined_receipts generic (#13761) * chore(evm): split `Executor::env` into `evm_env` and `tx_env` fields (#13773) Split the getters/setters accordingly, and update all call sites. * refactor(cheatcodes): `CheatcodesExecutor` generic (#13774) refactor(cheatcodes): make `CheatcodesExecutor` use generic env types from `ContextTr` Replace concrete `EvmEnv`/`TxEnv` in `with_fresh_nested_evm` with `EvmEnv<::Spec, CTX::Block>` and `CTX::Tx` to support non-Eth networks. Add `FoundryContextTr` trait as fully generic counterpart to the concrete `FoundryContextExt`. Remove unused `clone_to_cfg_env`/`apply_cfg_env` from `FoundryCfg`. * fix(anvil): flaky `test_trace_filter()` (#13764) fix * chore(cast): granular bounds on `Cast` (#13776) * refactor(evm): `FoundryContextExt` generic types (#13778) * fix(cheatcodes): create file in writeJson/writeToml 3-arg overload (#13777) Closes foundry-rs/foundry#13775 Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(anvil): make EthApi generic over `N: Network` (#13751) * refactor(anvil): make EthApi generic over N: Network * rm todo comments --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): move `CheatsCtxExt` trait to `foundry-evm-core` (#13781) Previously `CheatsCtxExt` was defined in `foundry-cheatcodes`. Move it to `foundry-evm-core`, and rename it to `EthCheatCtx` to make clear it pins the context to Ethereum-specific env types (`BlockEnv`/`TxEnv`/`CfgEnv`) as a temporary alias during the transition to fully generic EVM and cheatcodes. * refactor(evm): make `NestedEvm` trait generic with associated types (#13782) Add `Tx`, `Block`, and `Spec` associated types to `NestedEvm` so each network can specify its own environment types. The trait remains object-safe when used as `dyn NestedEvm`. Update `NestedEvmClosure` to pin the associated types to Eth types, and set the concrete types in the `FoundryEvm` implementation. * refactor(anvil): propagate `EthApi` to all holders (#13783) refactor(anvil): propagate EthApi to all holders * refactor(evm): rename `NestedEvmClosure` and move to `foundry-evm-core` (#13785) refactor(evm): rename `NestedEvmClosure` to `EthNestedEvmClosure` and move to `foundry-evm-core` * refactor(evm): remove `Env` abstraction from `Executor` impl (#13790) * refactor(anvil): remove redundant param (#13792) * refactor(cheatcodes): tighten verbose bounds to `EthCheatCtx` (#13791) * refactor(evm): remove `eth_*_mut()` from `FoundryContextExt` (#13789) * feat(script): generic `TransactionWithMetadata` + generic pprinting `TransactionMaybeSigned` (#13795) * refactor(evm): `DatabaseExt` generic over env types (#13797) refactor(evm): make DatabaseExt generic over environment types Add generic parameters with defaults to DatabaseExt: `DatabaseExt` This makes the trait generic over EVM environment types while remaining fully backwards-compatible — all existing code uses the defaults. Non-Ethereum networks (e.g. Tempo) can now implement DatabaseExt with their own environment types. 9 method signatures updated: snapshot_state, revert_state, create_select_fork, create_select_fork_at_transaction, select_fork, roll_fork, roll_fork_to_transaction, transact, transact_from_tx. * test(cast): mark flaky revert_reason_from and wildcard RPC-dependent tail (#13796) * fix(anvil): reject invalid versioned_hashes in beacon blobs endpoint (#13787) * fix(cheatcodes): prevent panic in expectRevert with empty bytes (#13769) * fix(cheatcodes): prevent panic in expectRevert with empty bytes When vm.expectRevert(bytes('')) catches a revert with non-empty data, decode_error in alloy-dyn-abi panics on the empty expected_reason (slice index out of range). Guard the decode_error call with a length check. Closes #13766 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test: add regression test for expectRevert empty bytes panic Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test: fix snapshot for expectRevert empty bytes regression test Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): add `DB` associated type to `FoundryJournalExt` (#13799) * refactor(evm): make DatabaseExt generic over environment types Add generic parameters with defaults to DatabaseExt: `DatabaseExt` This makes the trait generic over EVM environment types while remaining fully backwards-compatible — all existing code uses the defaults. Non-Ethereum networks (e.g. Tempo) can now implement DatabaseExt with their own environment types. 9 method signatures updated: snapshot_state, revert_state, create_select_fork, create_select_fork_at_transaction, select_fork, roll_fork, roll_fork_to_transaction, transact, transact_from_tx. * refactor(evm): replace dyn DatabaseExt in FoundryJournalExt with associated type Replace `&mut dyn DatabaseExt` return type in `FoundryJournalExt` with an associated type `type DB: DatabaseExt`. This removes 3 uses of `dyn DatabaseExt` while remaining backwards-compatible — `&mut DB` auto-coerces to `&mut dyn DatabaseExt` at call sites that still need it. `FoundryJournalExt` is never used as a trait object itself, so adding the associated type has no object-safety impact. --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(anvil): add `AnvilBlockExecutor` and `FoundryReceiptBuilder` (#13788) feat(anvil): add AnvilBlockExecutor and FoundryReceiptBuilder * fix(anvil): swap param order in get_next_block_blob_excess_gas to match callers (#13740) * feat(script): `Network`-generic `ScriptSequence` (#13803) * fix(config): add symmetric serialization for FuzzDictionaryConfig usize fields (#13723) * chore(evm): remove `Env::new_with_spec_id()` method (#13806) * fix(install): clean up nested submodules when using --no-git (#13779) * fix(install): clean up nested submodules when using --no-git Fixes #13688 Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019cf6ab-6839-70a9-98a9-289974db717b * test: mark regression test as flaky_ instead of ignored Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * style: fix fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019cf6ab-6839-70a9-98a9-289974db717b --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * refactor(evm): use associated types in `with_cloned_context` (#13802) refactor(evm): use associated types in `with_cloned_context` closure signature * refactor(evm): propagate env types through `FoundryJournalExt` (#13808) * refactor(evm): simplify `FoundryCfg` to marker trait (#13810) * feat(anvil): add `AnvilBlockExecutorFactory` (#13811) * feat(script): `Network`-generic `ScriptSequenceKind` (#13809) * feature(evm): owned `Tx`/`Evm` getters and `Evm` setter for `FoundryContextExt` (#13812) * chore(evm): remove `Env::{clone_evm_and_tx,apply_evm_and_tx}` methods (#13813) * chore(evm): rename `InspectorExt` to `EthInspectorExt` (#13815) * refactor(evm): add `Fork::backend()` accessor (#13817) * refactor(evm): remove `Env` from `commit_transaction` and `replay_until` (#13816) * feat(script): generic `BundledState` impl (#13825) * chore: bump alloy chains (#13827) * chore(evm): remove `Env` abstraction (#13826) * chore(evm): remove `Env` abstraction completely * fix: nit Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(macros): use correct index for tuple struct fields in ConsoleFmt (#13829) * chore(evm): fix stale `Env` references in doc comments (#13828) The combined `Env` wrapper struct was removed in #13826. Update remaining doc comments that still reference it to use the current `EvmEnv`/`TxEnv` names instead. * refactor(anvil): wire `AnvilBlockExecutorFactory` into `Backend::mine_block` (#13814) refactor(anvil): wire AnvilBlockExecutorFactory into Backend::mine_block * feat(script): generic `ScriptTransactionBuilder` (#13830) Remove hardcoded `Ethereum` default from `TransactionWithMetadata`, making `ScriptTransactionBuilder`, `simulate_and_fill`, and broadcast reader calls `Network`-generic. Cheatcode broadcast helpers now explicitly use Ethereum. * fix(`foundryup`): bump foundryup version (#13832) bump foundryup version * fix(foundryup): tempo-foundry now ships all binaries (#13834) nit * chore(deps): bump taiki-e/install-action from 2.68.17 to 2.68.35 (#13821) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump oven-sh/setup-bun from 2.1.2 to 2.2.0 (#13819) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump Swatinem/rust-cache from 2.8.2 to 2.9.1 (#13818) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump crate-ci/typos from 1.43.5 to 1.44.0 (#13820) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(evm): backend env helpers generic (#13836) Update `update_env_block` and `update_current_env_with_fork_env` to use generic `FoundryBlock`/`FoundryTransaction` bounds and `BlockHeader` trait methods via setters, replacing direct field access on `AnyRpcBlock`. * refactor(anvil): rm `TransactionExecutor`, mv revm callers to `AnvilBlockExecutorFactory` (#13835) refactor(anvil): rm , mv remai callers AnvilBlockExecutorFactory * refactor(script): extract `BrowserSigner` from `MultiWallet` (#13839) * refactor(anvil): mv `Backend` methods to generic impl, thread N through NodeConfig/spawn (#13840) * refactor(anvil): extract `block_env_from_header` utility (#13838) * chore(evm): clean-up `FoundryEvm` impl (#13844) * feat(cheatcodes): add `currentFilePath` cheatcode (#13735) * feat(cheatcodes): add `currentFilePath` cheatcode Add `vm.currentFilePath()` that returns the source file path of the currently running test or script contract, relative to the project root. This enables contracts to locate sibling files (calldata JSONs, markdown descriptions, configs) without hardcoding paths. A common pattern in proposal/script repos is overriding a `dirPath()` function with a hardcoded string — this cheatcode eliminates that boilerplate. Implementation leverages `CheatsConfig::running_artifact` which already tracks the entry-point `ArtifactId`. The `source` field is stripped of the project root prefix to return a consistent relative path. * fix: rustfmt Amp-Thread-ID: https://ampcode.com/threads/T-019cfb9f-8fce-717d-b9de-fedd8ee7d555 Co-authored-by: Amp * fix: remove view from test functions, fix forge-fmt Amp-Thread-ID: https://ampcode.com/threads/T-019cfba7-4986-77c6-9630-574261e9d580 Co-authored-by: Amp --------- Co-authored-by: Alex Netto Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(evm): simplify nested Evm handling (#13846) refactor(evm): simplify nested EVM handling - Replace `to_env()` (returning cfg+block+tx tuple) with `to_evm_env()` returning only `EvmEnv` (cfg+block), since tx is not needed by callers - Remove unused `with_cloned_context` generic type parameter `R` - Drop `set_tx` call in `with_cloned_context` since nested tx modifications should not propagate back to the outer context - Remove `Evm` useless `db_mut`, `precompiles`, `precompiles_mut`, `inspector`, `inspector_mut` provideds methods reimplementations * refactor(cheatcodes): extract fork env helper to reduce duplication (#13848) Extract `fork_env_op` helper that handles the common pattern shared by all fork-switching cheatcodes: clone EVM/tx env → run db operation → write env back. Deduplicates 7 call sites (rollFork × 4, selectFork, createSelectFork × 2). * refactor(cheatcodes): `BroadcastableTransaction` network-agnostic (#13849) refactor(cheatcodes): make BroadcastableTransaction network-agnostic Remove the `Network` type parameter from `BroadcastableTransaction` by storing raw EVM data (from, to, value, input, nonce, gas) as primitive fields instead of wrapping `TransactionMaybeSigned`. This prevents the `Network` generic from leaking through `Cheatcodes`, `RawCallResult`, and `ScriptResult`. The conversion to network-specific types (`TransactionMaybeSigned`) now happens in the script layer (`simulate.rs`) at the point where transactions are handed off to `ScriptTransactionBuilder`. * feat(evm): generic `NestedEvmClosure` (#13850) refactor(evm): generalize NestedEvmClosure with generic type params Replace `EthNestedEvmClosure` (pinned to Eth types) with generic `NestedEvmClosure` to support non-Eth networks. * feat(evm): `FoundryContextExt` generic impl (#13857) * feat(evm): wire Inspector and DatabaseExt Context generics (#13856) * refactor(anvil): make `PendingTransaction` generic over tx type (#13854) * chore(cheatcodes): remove `Cheatcodes` context generic (#13861) * refactor(evm): delegate to alloy's `EthEvmFactory` in `new_evm_with_inspector` (#13860) * chore: add `id` attributes to issue templates (#13864) * chore: add `id` attributes to issue templates Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0b22-cf39-75b7-b3d7-9280780eecd5 * chore: add `id` attributes to required issue template fields only Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0b22-cf39-75b7-b3d7-9280780eecd5 --------- Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> * refactor(evm): merge `FoundryJournalExt` into `FoundryContextExt` (#13863) * refactor(evm): merge `FoundryJournalExt` into `FoundryContextExt` * fix: failed merge * fix(traces): fix verbosity trace mode and unify verbosity handling (#13859) * fix(traces): use max instead of min for verbosity trace mode Add regression tests for TraceMode::with_verbosity to ensure verbosity 5 raises the trace mode to at least Steps rather than lowering it. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-Authored-By: James Niken <155266991+dizer-ti@users.noreply.github.com> * fix(traces): unify verbosity handling in TraceMode with_verbosity now raises to RecordStateDiff at verbosity 5, matching the documented behavior of -vvvvv (storage changes + backtraces). This removes the separate with_state_changes(verbosity() > 4) call in trace_mode() which used the global shell verbosity instead of the local evm_opts.verbosity — these two values can diverge when --gas-report or --flamegraph bumps evm_opts.verbosity to 3. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(forge): only show backtraces at verbosity 5 Backtraces require opcode-level step recording which is expensive. Gate backtrace display at verbosity >= 5 (-vvvvv) instead of >= 3, matching the documented behavior and the step recording threshold. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): comprehensive unit tests for TraceMode verbosity levels Tests every verbosity level (0-5) × every TraceMode variant: - verbosity 0-2: noop across all modes - verbosity 3-4: raises to Call, never downgrades - verbosity 5: raises to RecordStateDiff, never downgrades - into_config correctness at each level (record_steps, record_state_diff) - monotonicity invariant: with_verbosity never lowers any mode Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): add integration test for backtrace verbosity levels Runs the same failing test at verbosity 1, 3, 4, and 5 with snapshot assertions to verify backtraces only appear at -vvvvv. Removes the monotonicity unit test in favor of concrete integration coverage. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(traces): keep backtraces at verbosity >= 3, add source locations at 5 Backtraces are useful even without source locations — they show contract/function names at -vvv/-vvvv. Source file locations are only added at -vvvvv when step recording is enabled. Updates integration test to assert all three behaviors: - -vvv: backtrace with function names only - -vvvv: same, plus setup traces - -vvvvv: backtrace with source file:line:col locations Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: fix rustfmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): handle compiler warnings in snapshot Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): add [staticcall] to snapshot for pure function Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * perf(traces): RecordStateDiff should not enable debug-level recording RecordStateDiff now behaves as Steps + state diff, not Debug + state diff. This avoids recording full stack snapshots (memcpy per opcode), memory snapshots, and unfiltered opcode recording at -vvvvv. Before: 50k opcodes → 50k step records with full stack copies (~16MB) After: 50k opcodes → ~500 step records (JUMP/JUMPDEST only), no copies Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * test(traces): assert Debug mode config is unchanged Locks in that Debug mode still enables full memory/stack snapshots, returndata, immediate bytes, and unfiltered opcode recording — ensuring the RecordStateDiff optimization doesn't affect the debugger or cheatcode recording paths. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(traces): state diff needs unfiltered opcodes for SLOAD/SSTORE record_state_diff captures storage changes in the step() callback, which only fires for recorded opcodes. With a JUMP/JUMPDEST filter, SSTORE steps are skipped and state diffs are lost. RecordStateDiff now disables the opcode filter (like before) but still skips memory/stack snapshots — saving the expensive per-opcode memcpy. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(test): update JSON fixture for RecordStateDiff config Stack and memory snapshots are no longer recorded at -vvvvv since RecordStateDiff now uses Steps-level config. These fields are null in the JSON output instead of populated. Full debugger-level data is still available via --debug. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> * refactor(anvil): make Block generic over tx type (#13865) * refactor(evm): `FoundryContextExt` bound, use generic `Spec` in `EthCheatCtx` (#13866) * refactor(evm): use `TxEnv` directly in `DatabaseExt` instead of `TransactionRequest` (#13867) * chore(deps): weekly `cargo update` (#13878) Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> * Update flake.lock (#13877) Co-authored-by: github-actions[bot] * fix(deps): bump to Foundry browser wallet version 0.2.0 (#13890) bump to https://github.com/foundry-rs/foundry-browser-wallet/releases/tag/v0.2.0 * revert: "BroadcastableTransaction network-agnostic" (#13849) (#13891) * perf(anvil): remove redundant clone in create_access_list (#13887) * perf(evm): make `NestedEvm::to_evm_env` consuming to avoid useless clone (#13893) Change `to_evm_env(&self)` to `to_evm_env(self)` so the implementation can use `Evm::finish()` to destructure the EVM without an extra clone of `cfg_env` and `block_env`. Update `with_fresh_nested_evm` to return `EvmEnv` after consuming the EVM post-closure, so callers no longer need to capture the env inside a `NestedEvmClosure` (which only has `&mut dyn NestedEvm` and cannot call a consuming method). Fix the `sub_inner` / `sub_evm_env` ordering in `with_nested_evm` impls so the journal is cloned before `to_evm_env` consumes the EVM. * chore(common): consistency fix on `TransactionMaybeSigned::to()` (#13895) * feat(cheatcodes): Network/Evm generic `Cheatcodes` (#13894) * feat(cheatcodes): Network/Evm generic `Cheatcodes` * fix: tx create detection * fix(ci): adapt to `TransactionMaybeSigned::to()` return type change (#13896) * chore(cheatcodes): remove unused `cheats` param from `CheatcodesExecutor::console_log` (#13897) * refactor(evm): remove `tx_env` param from EVM constructor helper (#13903) Remove the `tx_env` parameter from `new_eth_evm_with_inspector`, `with_cloned_context`, and `with_fresh_nested_evm`. Instead of setting the transaction environment at EVM construction time (where it may become stale or be set redundantly), callers now pass the tx env directly to `evm.transact()` at execution time. This avoids some clones. * chore: remove dead code `paths_config` and `log_status` from inspector stack (#13905) chore: remove dead code in InspectorStackInner Remove two unused methods: - `InspectorStack::paths_config()`: zero callers across the codebase - `InspectorStackInner::log_status()`: zero callers across the codebase Also removes the now-unused `foundry_compilers::ProjectPathsConfig` import. * chore: remove no-op `spec_id` reassignment in `Executor::clone_with_backend` (#13906) chore: remove no-op spec_id reassignment in clone_with_backend After cloning `self.evm_env`, `evm_env.cfg_env.spec` already equals `self.spec_id()` (which simply returns `self.evm_env.cfg_env.spec`). The reassignment is a no-op. * feat(evm): `Backend` generic network (#13579) * feat(evm): `Backend` generic over `Network` * fix(evm): use `AnyNetwork` for fork env init to support non-standard chains When forking non-standard EVM chains (e.g. Moonbeam, Arbitrum), their block headers may lack standard Ethereum fields like `mixHash`. Using the generic `N`-typed provider (defaulting to `Ethereum`) caused deserialization failures for these chains. * feat(evm): add `TryAnyIntoTxEnv` trait for `AnyTxEnvelope` to `TxEnv` conversion Introduces TryAnyIntoTxEnv trait in foundry-evm-core to replace the TxEnv: FromRecoveredTx bound on Backend. This allows Backend to default to AnyNetwork: - TxEnvelope (Ethereum): delegates to FromRecoveredTx directly - AnyTxEnvelope: extracts inner TxEnvelope via as_envelope(), then delegates to FromRecoveredTx; returns error for unknown tx types Co-authored-by: Amp * chore: fix `cargo deny` failure --------- Co-authored-by: Amp * refactor(evm): `NestedEvm` based on revm's `Evm` instead of alloy-evm (#13908) * Add feature: `forge inspect linearization` to see a Solidity contract's linearized inheritance (#13704) * feat: Tempo wallet access key support for cast (#13909) * refactor(script-sequence): rename misleading `opcode` field to `call_kind` (#13907) * fix(fmt): swap valid_before/valid_after in TempoTransaction pretty print (#13910) * fix(fmt): prefer header total_difficulty for totalDifficulty (#13919) * feat(evm): `FoundryContextExt` impls for OP (#13925) * refactor(evm): simplify `NestedEvm` trait by removing `Block` and `Spec` (#13933) * refactor(evm): make `FoundryInspectorExt` generic over `CTX` and rename traits (#13922) * refactor(evm): make `FoundryInspectorExt` generic over `CTX` and rename traits Restructure the inspector extension traits for clarity and genericity: - Rename FoundryInspectorExt -> InspectorExt: the context-free base trait providing Foundry-specific inspector methods (console logging, network config) without any Inspector supertrait. - Rename EthInspectorExt -> FoundryInspectorExt: the combined trait that unifies Inspector + InspectorExt. Generic over any CTX that implements FoundryContextExt, rather than being hardcoded to specific BLOCK/TX/SPEC type parameters with for<'a> HRTB baked in. - Introduce EthEvmCtx<'db> type alias for the concrete Eth context (Context), keeping usage sites concise. This makes the trait hierarchy composable for multi-network support while keeping the Eth-specific default path ergonomic via the type alias during refactor. Co-authored-by: Amp * fix: use `EthEvmContext` instead of EthEvmCtx as default --------- Co-authored-by: Amp * fix(evm): restore `code_size_limit` config in `CfgEnv` (#13912) `cfg_env()` unconditionally set `limit_contract_code_size = Some(usize::MAX)`, ignoring the user's `code_size_limit` setting from foundry.toml. Use the configured value when present, falling back to `usize::MAX` (disabled). * perf(evm): remove unnecessary clones in do_call_end/do_create_end (#13913) perf(evm): remove unnecessary clones in `do_call_end`/`do_create_end` Both methods returned `CallOutcome`/`CreateOutcome` but all callers discarded the return value — the outcome is already mutated in-place via `&mut`. This removes the final `outcome.clone()` and changes the early-return in the `#[ret]` macro from `then_some(outcome.clone())` to `then(|| ())`, eliminating up to 2 clones per call/create end. * fix(wallets): browser wallet CLI help heading formatting (#13876) fix: browser wallet CLI help heading formatting - Set proper help_heading on BrowserWalletOpts to use 'Wallet options - browser wallet' consistently - Add next_help_heading to Erc20TxOpts to prevent transaction options from leaking into the browser wallet section Co-authored-by: Amp Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(forge-script): Tempo access key support for forge script (#13917) * refactor(anvil): encapsulate per-tx inspector lifecycle in `finish_transaction` (#13932) * refactor(anvil): encapsulate per-tx inspector lifecycle in `finish_transaction` Extract the repeated ~20-line drain-and-reset block from 3 locations in the block mining loop into `AnvilInspector::finish_transaction()`. The method prints traces/logs, drains the tracer into `Vec`, and reinstalls fresh tracer + log collector for the next transaction. Introduces `InspectorTxConfig` to carry the print/tracing settings, replacing direct field access into the inspector internals. * style: fix rustfmt * refactor(evm): generalize `TryAnyToTxEnv` trait over TxEnv + simplification (#13924) * refactor(evm): generalize `TryAnyToTxEnv` trait over TxEnv + simplification The old trait converted `TxEnvelope` (the consensus-layer type) into `TxEnv`, requiring callers to separately pass the sender address. This was a leaky abstraction: the sender is already carried by the RPC transaction wrapper (TransactionResponse::from()), so callers had to extract it themselves before calling `try_into_tx_env`. The new trait `TryAnyToTxEnv` is generic over the output TxEnv type and takes the full RPC transaction as receiver, which means: - Implementations for `alloy_rpc_types::Transaction` and `AnyRpcTransaction` can call ToTxEnv / FromRecoveredTx internally without leaking address handling to callers. - A new impl for `op_alloy_rpc_types::Transaction` produces `OpTransaction`, enabling OP-stack fork replay without a separate code path. - `Backend` now constrains `N::TransactionResponse: TryAnyToTxEnv` instead of `N::TxEnvelope: TryAnyIntoTxEnv`, which is both more accurate and unlocks multi-network support. * fix: docs --------- Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(evm): simplify `replay_until` to use single `ForkDB` clone (#13931) * refactor: simplify replay_until to use single ForkDB clone Replace the per-transaction Backend + EVM creation in replay_until with a single cloned ForkDB and one persistent EthEvm instance. Before: for each tx in the block, commit_transaction would clone Fork + JournaledState, spawn a new Backend (including MultiFork), create a new FoundryEvm, transact, then apply_state_changeset. In a block with N preceding transactions, this meant N full clones. After: clone the fork's CacheDB once (SharedBackend is Arc-backed, so only the local cache layer is duplicated), create one EthEvm via EthEvmFactory, call transact_commit for each tx, then replace the fork's DB and refresh journaled states once at the end. Also: - Remove unused tx_env parameter from replay_until - Extract Fork::refresh_journaled_states helper to deduplicate the paired update_state calls in both replay_until and apply_state_changeset - Remove unused NoOpInspector import Amp-Thread-ID: https://ampcode.com/threads/T-019d2512-8074-72ac-92d8-e8f887911219 Co-authored-by: Amp * style: fix rustfmt * refactor: simplify tx collection loop and remove stale comments --------- Co-authored-by: Amp Co-authored-by: zerosnacks * fix(evm): use try_any_to_tx_env in replay_until (#13938) The merge of #13931 introduced a call to the old `try_into_tx_env` method which was renamed to `try_any_to_tx_env` in #13924. This fixes the docs CI build. Amp-Thread-ID: https://ampcode.com/threads/T-019d2952-9ec2-76f9-8f90-b7b3735d4ce3 Co-authored-by: Amp * chore(deps): bump taiki-e/install-action from 2.68.35 to 2.69.8 (#13915) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.68.35 to 2.69.8. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/taiki-e/install-action/compare/94a7388bec5d4c8dd93e3ebf09e0ff448f3f6f4d...7bc99eee1f1b8902a125006cf790a1f4c8461e63) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.69.8 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump DeterminateSystems/determinate-nix-action from 3.17.0 to 3.17.1 (#13914) chore(deps): bump DeterminateSystems/determinate-nix-action Bumps [DeterminateSystems/determinate-nix-action](https://github.com/determinatesystems/determinate-nix-action) from 3.17.0 to 3.17.1. - [Release notes](https://github.com/determinatesystems/determinate-nix-action/releases) - [Commits](https://github.com/determinatesystems/determinate-nix-action/compare/131015bad844610e5e6300f8a143bf625d3e74f4...a18f73c54ca8525de051e73c31512a67f44df919) --- updated-dependencies: - dependency-name: DeterminateSystems/determinate-nix-action dependency-version: 3.17.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor(evm): simplify `FoundryContextExt` spec handling (#13935) - Introduced `FoundryContextExt::Spec` type for convenience - Removed complex bounds on `ContextTr::Cfg` using instead the new `Spec` type - Simplified accordingly the rest of the code Co-authored-by: zerosnacks Co-authored-by: Amp * refactor(anvil): remove some intermediate `Env` wrappers, pass `EvmEnv` directly (#13941) refactor(anvil): remove some intermediate Env wrapper, pass EvmEnv directly Replace usage of the `Env` struct wrapper with direct `EvmEnv` references in `new_eth_evm_with_inspector`, `validate_pool_transaction_for`, and `validate_for`. The optimism flag is now passed explicitly as `is_optimism: bool` instead of being extracted from `env.networks`. This eliminates unnecessary `Env::new(...)` construction in several hot paths (block mining, pending block simulation, tx replay) and simplifies the function signatures. * refactor(anvil): remove Env from storage, call env, and executor APIs (#13942) Continue the cleanup of the Env wrapper by passing EvmEnv directly in remaining call sites: - BlockchainStorage::new / Blockchain::new: drop the redundant spec_id parameter, derive it from evm_env.spec_id() instead - build_call_env: return (EvmEnv, OpTransaction) instead of Env, consumers destructure and use directly - new_eth_evm_with_inspector_ref: accept &EvmEnv instead of &Env, derive is_optimism from self.is_optimism() - build_tx_env_for_pending: take is_optimism: bool instead of NetworkConfigs + &EvmEnv - inspect_tx / replay_block_transactions_with_inspector: extract only evm_env from next_env(), avoid cloning the whole Env - api.rs: use spec_id() / chain_id() helpers instead of digging into env.evm_env.cfg_env fields directly - Various minor cleanups: inline single-use env write guards, use self.chain_id() / self.spec_id() helpers consistently --------- Signed-off-by: dependabot[bot] Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Matthias Seitz <19890894+mattsse@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matthias Seitz Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: Nikki Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: zerosnacks Co-authored-by: Amp Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Edgar Richards Co-authored-by: Red Swan Co-authored-by: onbjerg Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Cleartext logging of sensitive information' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/evm/traces/src/lib.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/anvil/src/eth/backend/executor.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/cli/src/utils/suggestions.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update crates/common/src/contracts.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .github/ISSUE_TEMPLATE/bug_report.md Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 0xrusowsky <90208954+0xrusowsky@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: grandizzy Co-authored-by: Rafael Quintero <32346241+rplusq@users.noreply.github.com> Co-authored-by: rplusq Co-authored-by: Claude Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> Co-authored-by: Gengar Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: cakevm Co-authored-by: Matthias Seitz Co-authored-by: Theodore Solis Co-authored-by: tefyosL-sol Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Desant pivo Co-authored-by: pistomat Co-authored-by: zark <77061323+zarkk01@users.noreply.github.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: Ishika Choudhury <117741714+Rimeeeeee@users.noreply.github.com> Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Aganis Co-authored-by: tskoyo Co-authored-by: Matt D Co-authored-by: onbjerg Co-authored-by: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Co-authored-by: Amp Co-authored-by: Avory Co-authored-by: Yuya Maruyama <69783679+YuyaMaruyama21D4E@users.noreply.github.com> Co-authored-by: Georgios Konstantopoulos Co-authored-by: zerosnacks Co-authored-by: albertov19 <64150856+albertov19@users.noreply.github.com> Co-authored-by: Yero~ Co-authored-by: Philippe Dumonet Co-authored-by: Vicze Osikata Co-authored-by: Mahmoud Lababidi Co-authored-by: Tim Beiko Co-authored-by: Oliver Nordbjerg Co-authored-by: Alvarez <140459501+prestoalvarez@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: Ninja Co-authored-by: cui Co-authored-by: Mark Fizer Co-authored-by: marukai67 Co-authored-by: sashass1315 Co-authored-by: 0xferrous <0xferrous@proton.me> Co-authored-by: radik878 Co-authored-by: MozirDmitriy Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: Tran Quang Loc Co-authored-by: James Niken <155266991+dizer-ti@users.noreply.github.com> Co-authored-by: 0xMars42 Co-authored-by: strmfos <155266597+strmfos@users.noreply.github.com> Co-authored-by: bigbear <155267841+aso20455@users.noreply.github.com> Co-authored-by: iPLAY888 <133153661+letmehateu@users.noreply.github.com> Co-authored-by: Valentin B. <703631+beeb@users.noreply.github.com> Co-authored-by: howy <132113803+howydev@users.noreply.github.com> Co-authored-by: Adrian Co-authored-by: kilavvy <140459108+kilavvy@users.noreply.github.com> Co-authored-by: fuder.eth Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: figtracer <1gusredo@gmail.com> Co-authored-by: Tomass <155266802+zeroprooff@users.noreply.github.com> Co-authored-by: Nikki Co-authored-by: Mayank Sharma <82099885+mayanksharma-eth@users.noreply.github.com> Co-authored-by: Mayank Sharma <82099885+codersharma2001@users.noreply.github.com> Co-authored-by: Snezhkko Co-authored-by: anim001k <140460766+anim001k@users.noreply.github.com> Co-authored-by: Forostovec Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: Suuuuuuperrrrr fred Co-authored-by: andrewshab <152420261+andrewshab3@users.noreply.github.com> Co-authored-by: emmmm <155267286+eeemmmmmm@users.noreply.github.com> Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com> Co-authored-by: Amlandeep Bhadra Co-authored-by: Edgar Richards Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Co-authored-by: googleworkspace-bot Co-authored-by: Alexandro T. Netto <56097505+alextnetto@users.noreply.github.com> Co-authored-by: Alex Netto Co-authored-by: Red Swan --- .circleci/cargo.yml | 32 + .circleci/ci-web3-gamefi.yml | 26 + .circleci/ci.yml | 31 + .circleci/ci_cargo.yml | 37 + .circleci/ci_deploy.yml | 34 + .circleci/ci_v1.yml | 31 + .circleci/config.yml | 32 + .circleci/dev_stage.yml | 70 ++ .circleci/web3_defi_gamefi.yml | 26 + .codesandbox/tasks.json | 7 + .deps/remix-tests/remix_accounts.sol | 39 + .deps/remix-tests/remix_tests.sol | 225 +++++ .github/ISSUE_TEMPLATE/bug_report.md | 41 + .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/workflows/Docker.yml | 62 ++ .github/workflows/apisec-scan.yml | 29 + .github/workflows/codeql.yml | 92 +++ .github/workflows/deploy.yml | 27 + .github/workflows/docker.yml | 62 ++ .github/workflows/google.yml | 117 +++ .github/workflows/npm.yml | 30 + .github/workflows/snyk-container.yml | 55 ++ .gitmodules | 6 + benches/src/lib.rs | 16 +- counter/.github/workflows/test.yml | 43 + counter/.gitignore | 14 + counter/README.md | 66 ++ counter/foundry.toml | 6 + counter/lib/forge-std | 1 + counter/lib/openzeppelin-contracts | 1 + counter/script/Counter.s.sol | 19 + counter/src/Counter.sol | 14 + counter/test/Counter.t.sol | 24 + crates/cast/tests/cli/main.rs | 28 + crates/cli/src/utils/suggestions.rs | 1 + crates/common/Cargo.toml | 1 + crates/doc/src/parser/comment.rs | 6 + crates/evm/evm/Cargo.toml | 1 + crates/forge/Cargo.toml | 1 + crates/forge/tests/cli/test_optimizer.rs | 1 - crates/lint/src/linter.rs | 129 +++ crates/script/Cargo.toml | 1 + crates/test-utils/src/script.rs | 29 +- crates/test-utils/src/util.rs | 96 ++- crates/wallets/src/tempo.rs | 196 +++++ deny.toml | 7 +- npm/scripts/stage-from-artifact.mjs | 28 +- npm/src/const.mjs | 30 +- sleep.json | 955 ++++++++++++++++++++++ 50 files changed, 2829 insertions(+), 26 deletions(-) create mode 100644 .circleci/cargo.yml create mode 100644 .circleci/ci-web3-gamefi.yml create mode 100644 .circleci/ci.yml create mode 100644 .circleci/ci_cargo.yml create mode 100644 .circleci/ci_deploy.yml create mode 100644 .circleci/ci_v1.yml create mode 100644 .circleci/config.yml create mode 100644 .circleci/dev_stage.yml create mode 100644 .circleci/web3_defi_gamefi.yml create mode 100644 .codesandbox/tasks.json create mode 100644 .deps/remix-tests/remix_accounts.sol create mode 100644 .deps/remix-tests/remix_tests.sol create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/Docker.yml create mode 100644 .github/workflows/apisec-scan.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/google.yml create mode 100644 .github/workflows/snyk-container.yml create mode 100644 .gitmodules create mode 100644 counter/.github/workflows/test.yml create mode 100644 counter/.gitignore create mode 100644 counter/README.md create mode 100644 counter/foundry.toml create mode 160000 counter/lib/forge-std create mode 160000 counter/lib/openzeppelin-contracts create mode 100644 counter/script/Counter.s.sol create mode 100644 counter/src/Counter.sol create mode 100644 counter/test/Counter.t.sol create mode 100644 crates/lint/src/linter.rs create mode 100644 crates/wallets/src/tempo.rs create mode 100644 sleep.json diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..32b65e6a23cc5 --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,32 @@ +version: 2.1 +# +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci_deploy.yml b/.circleci/ci_deploy.yml new file mode 100644 index 0000000000000..0c8ae5507187d --- /dev/null +++ b/.circleci/ci_deploy.yml @@ -0,0 +1,34 @@ +version: 2.1 + +jobs: + say-hello: + docker: + - image: cimg/base:current + + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +workflows: + say-hello-workflow: + jobs: + - say-hello + +- run: + name: Plan a deploy + command: | + circleci run release plan \ + --environment-name="" \ + --component-name="" \ + --target-version="" +# Your job here doing the actual deployment +- run: + name: Update a deploy to SUCCESS + command: circleci run release update --status=SUCCESS + when: on_success +- run: + name: Update planned deploy to FAILED + command: circleci run release update --status=FAILED + when: on_fail diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..82c6de5b42b73 --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..4168efef0971f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..5ba351727d22b --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,70 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..edd3e4a15ddbc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,41 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, Safari] + - Version [e.g. 22] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/Docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..5a2330e7d5d62 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,62 @@ +name: Docker + +on: + push: + tags: ["*"] + branches: + - "main" + pull_request: + branches: ["**"] + +env: + # Hostname of your registry + REGISTRY: docker.io + # Image repository, without hostname and tag + IMAGE_NAME: ${{ github.repository }} + SHA: ${{ github.event.pull_request.head.sha || github.event.after }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + # Authenticate to the container registry + - name: Authenticate to registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + # Extract metadata (tags, labels) for Docker + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ env.SHA }} + tags: | + type=edge,branch=$repo.default_branch + type=semver,pattern=v{{version}} + type=sha,prefix=,suffix=,format=short + + # Build and push Docker image with Buildx + # (don't push on PR, load instead) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + sbom: ${{ github.event_name != 'pull_request' }} + provenance: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' }} + load: ${{ github.event_name == 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 323059e99e6b6..e3a5b385a28a3 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -137,6 +137,36 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id || inputs.run_id }} + - name: Validate Downloaded Artifacts + env: + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} + run: | + set -euo pipefail + + echo "Validating artifacts in: $ARTIFACT_DIR" + + if [[ ! -d "$ARTIFACT_DIR" ]]; then + echo "ERROR: Artifact directory does not exist: $ARTIFACT_DIR" >&2 + exit 1 + fi + + if ! find "$ARTIFACT_DIR" -mindepth 1 -print -quit | grep -q .; then + echo "ERROR: Artifact directory is empty: $ARTIFACT_DIR" >&2 + exit 1 + fi + + # Reject files with suspicious paths (absolute paths or parent directory traversals) + # Use null-delimited paths to safely handle filenames with newlines or whitespace + while IFS= read -r -d '' path; do + rel="${path#"$ARTIFACT_DIR"/}" + if [[ "$rel" == /* ]] || [[ "$rel" == *".."* ]]; then + echo "ERROR: Suspicious artifact path detected: $rel" >&2 + exit 1 + fi + done < <(find "$ARTIFACT_DIR" -type f -print0) + + echo "Artifact validation completed successfully." + - name: Setup Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/benches/src/lib.rs b/benches/src/lib.rs index 50c7afae2ddec..7ed8807cbf0f5 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -141,10 +141,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("⚠️ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 2f744efe4d4f0..9071614e3a7a9 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -3158,6 +3158,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index a66a0fef2fe0a..6921faabcb102 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,6 +17,7 @@ foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-common-fmt.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true +foundry-primitives.workspace = true alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 42b91ccc366aa..e70f47e174a81 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -205,6 +205,12 @@ impl Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 5dbf07c7a356c..dd5138f532074 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -28,6 +28,7 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true +alloy-network.workspace = true alloy-primitives = { workspace = true, features = [ "serde", "getrandom", diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 667da6b442ca1..4e83eb168abca 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,6 +62,7 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true tempo-alloy.workspace = true diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 6de71571ac7eb..d243814c4b148 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -56,6 +56,7 @@ alloy-primitives.workspace = true alloy-eips.workspace = true alloy-consensus.workspace = true thiserror.workspace = true +tempo-alloy.workspace = true tempo-alloy.workspace = true tempo-primitives.workspace = true diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 06f83886d0fd2..c1a6cb53bdbff 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -121,9 +121,32 @@ impl ScriptTester { let to_dir = root.join("utils"); fs::create_dir_all(&to_dir)?; for entry in fs::read_dir(&from_dir)? { - let file = &entry?.path(); - let name = file.file_name().unwrap(); - fs::copy(file, to_dir.join(name))?; + let file = entry?.path(); + // Only operate on regular files to avoid following symlinks or directories + let metadata = fs::symlink_metadata(&file)?; + let ftype = metadata.file_type(); + if !ftype.is_file() { + continue; + } + let name = match file.file_name() { + Some(name) => name, + None => continue, + }; + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } else { + continue; + } + fs::copy(&file, to_dir.join(name))?; } Ok(()) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 48489e43e34d9..fa065425b5281 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -3,12 +3,28 @@ use foundry_config::Config; use std::{ env, fs::{self, File}, - io::{Read, Seek, Write}, + io::{self, IsTerminal, Read, Seek, Write}, path::{Path, PathBuf}, process::Command, sync::LazyLock, }; +/// Base directory under which all test utility filesystem operations are constrained. +/// Using a fixed directory under the system temp dir avoids trusting the current +/// working directory (which may be user-controlled) as a security boundary. +static TEST_UTIL_BASE: LazyLock = LazyLock::new(|| { + // Resolve the system temp directory to an absolute, canonical path where possible. + // If canonicalization fails for any reason, fall back to the raw temp_dir value. + let tmp = env::temp_dir(); + let mut base = tmp + .canonicalize() + .unwrap_or(tmp); + base.push("foundry_test_utils"); + // Ignore errors here; they will surface when the path is actually used. + let _ = fs::create_dir_all(&base); + base +}); + /// Directories to skip when copying project directories. /// These are build artifacts and runtime-generated files that should not be copied to temp /// workspaces. @@ -19,6 +35,9 @@ pub use crate::{ext::*, prj::*}; /// The commit of forge-std to use. pub const FORGE_STD_REVISION: &str = include_str!("../../../testdata/forge-std-rev"); +/// Stores whether `stdout` is a tty / terminal. +pub static IS_TTY: LazyLock = LazyLock::new(|| std::io::stdout().is_terminal()); + /// Global default template path. Contains the global template project from which all other /// temp projects are initialized. See [`initialize()`] for more info. static TEMPLATE_PATH: LazyLock = @@ -146,7 +165,9 @@ pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput { out = project.compile().unwrap(); test_debug!("compiled {}", lock_file_path.display()); - assert!(!out.has_compiler_errors(), "Compiled with errors:\n{out}"); + if out.has_compiler_errors() { + panic!("Compiled with errors:\n{out}"); + } if let Some(write) = &mut write { write.write_all(crate::fd_lock::LOCK_TOKEN).unwrap(); @@ -174,7 +195,7 @@ pub fn get_vyper() -> Vyper { let path = VYPER.as_path(); let mut file = File::create(path).unwrap(); if let Err(e) = file.try_lock() { - if matches!(e, fs::TryLockError::WouldBlock) { + if let fs::TryLockError::WouldBlock = e { file.lock().unwrap(); assert!(path.exists()); return Vyper::new(path).unwrap(); @@ -230,22 +251,77 @@ pub fn read_string(path: impl AsRef) -> String { /// like `out/`, `cache/`, and `broadcast/` which are build artifacts that should not be /// copied to temporary test workspaces. pub fn copy_dir_filtered(src: &Path, dst: &Path) -> std::io::Result<()> { - fs::create_dir_all(dst)?; - copy_dir_filtered_inner(src, dst, true) + let src = resolve_and_validate_under_base(src)?; + let dst = resolve_and_validate_under_base(dst)?; + + fs::create_dir_all(&dst)?; + copy_dir_filtered_inner(&src, &dst, true) +} + +/// Resolve a path against a safe base directory and ensure it does not escape that base. +/// +/// This guards against using uncontrolled paths that could traverse outside the intended +/// workspace (for example, via `..` components or absolute paths). +fn resolve_and_validate_under_base(path: &Path) -> io::Result { + // Use a fixed base directory for test utilities instead of the current working + // directory, which may be influenced by the environment. + let base = TEST_UTIL_BASE.clone(); + + // If `path` is absolute, interpret it relative to the base by stripping the + // root and joining the remaining components. This avoids treating arbitrary + // absolute paths as trustworthy. + let joined = if path.is_absolute() { + let relative_components = path.components().filter_map(|c| { + use std::path::Component; + match c { + Component::Normal(p) => Some(PathBuf::from(p)), + // Skip root and current-dir components; preserve parent-dir so that + // canonicalization below can detect and resolve them safely. + Component::RootDir | Component::CurDir => None, + Component::ParentDir => Some(PathBuf::from("..")), + Component::Prefix(_) => None, + } + }); + let mut rel = PathBuf::new(); + for c in relative_components { + rel.push(c); + } + base.join(rel) + } else { + base.join(path) + }; + + let canonical = joined.canonicalize()?; + if !canonical.starts_with(&base) { + return Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "path escapes allowed base directory", + )); + } + + Ok(canonical) } fn copy_dir_filtered_inner(src: &Path, dst: &Path, is_root: bool) -> std::io::Result<()> { - for entry in fs::read_dir(src)? { + // Ensure that each recursion step operates on paths that are constrained to the + // configured base directory. This guarantees that any `src_path` passed to + // filesystem operations cannot escape the allowed workspace even if the initial + // input was influenced by the user. + let src = resolve_and_validate_under_base(src)?; + let dst = resolve_and_validate_under_base(dst)?; + + for entry in fs::read_dir(&src)? { let entry = entry?; let ty = entry.file_type()?; - let src_path = entry.path(); - let dst_path = dst.join(entry.file_name()); + let name = entry.file_name(); + let src_path = src.join(&name); + let dst_path = dst.join(&name); if ty.is_dir() { // Skip build artifact directories at the root level if is_root - && let Some(name) = entry.file_name().to_str() - && SKIP_DIRS.contains(&name) + && let Some(name_str) = name.to_str() + && SKIP_DIRS.contains(&name_str) { continue; } diff --git a/crates/wallets/src/tempo.rs b/crates/wallets/src/tempo.rs new file mode 100644 index 0000000000000..a86b568fdea2b --- /dev/null +++ b/crates/wallets/src/tempo.rs @@ -0,0 +1,196 @@ +use alloy_eips::Encodable2718; +use alloy_primitives::{Address, hex}; +use alloy_rlp::Decodable; +use alloy_signer::Signer; +use eyre::Result; +use std::path::PathBuf; +use tempo_alloy::rpc::TempoTransactionRequest; +use tempo_primitives::transaction::{ + KeychainSignature, PrimitiveSignature, SignedKeyAuthorization, TempoSignature, +}; + +use crate::{WalletSigner, utils}; + +/// Wallet type: how this wallet was created. +#[derive(Clone, Copy, Default, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +enum WalletType { + #[default] + Local, + Passkey, +} + +/// Cryptographic key type. +#[derive(Clone, Copy, Default, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +enum KeyType { + #[default] + Secp256k1, + P256, + WebAuthn, +} + +/// A single entry from Tempo's `keys.toml`. +#[derive(serde::Deserialize)] +#[allow(dead_code)] +struct KeyEntry { + #[serde(default)] + wallet_type: WalletType, + #[serde(default)] + wallet_address: Address, + #[serde(default)] + chain_id: u64, + #[serde(default)] + key_type: KeyType, + #[serde(default)] + key_address: Option
, + #[serde(default)] + key: Option, + #[serde(default)] + key_authorization: Option, + #[serde(default)] + expiry: Option, + #[serde(default)] + limits: Vec, +} + +/// Per-token spending limit stored in `keys.toml`. +#[derive(serde::Deserialize)] +struct StoredTokenLimit { + #[allow(dead_code)] + currency: Address, + #[allow(dead_code)] + limit: String, +} + +/// The top-level structure of `~/.tempo/wallet/keys.toml`. +#[derive(serde::Deserialize)] +struct KeysFile { + #[serde(default)] + keys: Vec, +} + +/// Configuration for a Tempo access key (keychain mode). +/// +/// When a Tempo wallet entry uses keychain mode (`wallet_address != key_address`), the signer +/// is an access key that signs on behalf of the root wallet. This struct carries the metadata +/// needed to construct the correct transaction. +#[derive(Debug, Clone)] +pub struct TempoAccessKeyConfig { + /// The root wallet address (the `from` address for transactions). + pub wallet_address: Address, + /// The access key's address (derived from the private key that actually signs). + pub key_address: Address, + /// Decoded key authorization for on-chain provisioning. + /// + /// When present, callers should check whether the key is already provisioned on-chain + /// (via the AccountKeychain precompile) before including this in a transaction. + pub key_authorization: Option, +} + +/// Result of looking up an address in Tempo's key store. +pub enum TempoLookup { + /// A direct (EOA) signer was found — `wallet_address == key_address`. + Direct(WalletSigner), + /// A keychain (access key) signer was found — `wallet_address != key_address`. + Keychain(WalletSigner, Box), + /// No matching entry was found. + NotFound, +} + +/// Returns the path to Tempo's keys file. +/// +/// Respects `TEMPO_HOME` env var, defaulting to `~/.tempo`. +fn keys_path() -> Option { + let base = std::env::var_os("TEMPO_HOME") + .map(PathBuf::from) + .or_else(|| dirs::home_dir().map(|h| h.join(".tempo")))?; + Some(base.join("wallet").join("keys.toml")) +} + +/// Decodes a hex-encoded, RLP-encoded [`SignedKeyAuthorization`]. +fn decode_key_authorization(hex_str: &str) -> Result { + let bytes = hex::decode(hex_str)?; + let auth = SignedKeyAuthorization::decode(&mut bytes.as_slice())?; + Ok(auth) +} + +/// Looks up a signer for the given address in Tempo's `keys.toml`. +/// +/// Returns [`TempoLookup::Direct`] if a direct-mode (EOA) key is found, +/// [`TempoLookup::Keychain`] if a keychain-mode access key is found, +/// or [`TempoLookup::NotFound`] if no entry matches. +pub fn lookup_signer(from: Address) -> Result { + let path = match keys_path() { + Some(p) if p.is_file() => p, + _ => return Ok(TempoLookup::NotFound), + }; + + let contents = std::fs::read_to_string(&path)?; + let file: KeysFile = toml::from_str(&contents)?; + + for entry in &file.keys { + if entry.wallet_address != from { + continue; + } + + let Some(key) = &entry.key else { + continue; + }; + + // Direct mode: wallet_address == key_address (or key_address is absent). + let is_direct = + entry.key_address.is_none() || entry.key_address == Some(entry.wallet_address); + + let signer = utils::create_private_key_signer(key)?; + + if is_direct { + return Ok(TempoLookup::Direct(signer)); + } + + // Keychain mode: the key is an access key signing on behalf of wallet_address. + let key_authorization = + entry.key_authorization.as_deref().map(decode_key_authorization).transpose()?; + + let config = TempoAccessKeyConfig { + wallet_address: entry.wallet_address, + // SAFETY: `is_direct` was false, so `key_address` is `Some` and != wallet_address + key_address: entry.key_address.unwrap(), + key_authorization, + }; + return Ok(TempoLookup::Keychain(signer, Box::new(config))); + } + + Ok(TempoLookup::NotFound) +} + +/// Signs a Tempo transaction request using an access key (keychain V2 mode). +/// +/// Bypasses the standard `EthereumWallet` signing path and instead: +/// 1. Builds the `TempoTransaction` from the request +/// 2. Computes the V2 keychain signing hash +/// 3. Signs with the access key +/// 4. Wraps in a `KeychainSignature` and encodes to EIP-2718 wire format +pub async fn sign_with_access_key( + tx_request: impl Into, + signer: &impl Signer, + wallet_address: Address, +) -> Result> { + let tx_request: TempoTransactionRequest = tx_request.into(); + let tempo_tx = tx_request + .build_aa() + .map_err(|e| eyre::eyre!("failed to build Tempo AA transaction: {e}"))?; + + let sig_hash = tempo_tx.signature_hash(); + let signing_hash = KeychainSignature::signing_hash(sig_hash, wallet_address); + let raw_sig = signer.sign_hash(&signing_hash).await?; + + let keychain_sig = + KeychainSignature::new(wallet_address, PrimitiveSignature::Secp256k1(raw_sig)); + let aa_signed = tempo_tx.into_signed(TempoSignature::Keychain(keychain_sig)); + + let mut buf = Vec::new(); + aa_signed.encode_2718(&mut buf); + + Ok(buf) +} diff --git a/deny.toml b/deny.toml index 1a0e1e8e53005..0f891df4bfbfe 100644 --- a/deny.toml +++ b/deny.toml @@ -100,7 +100,10 @@ unknown-git = "deny" allow-git = [ "https://github.com/alloy-rs/alloy", "https://github.com/alloy-rs/evm", + "https://github.com/foundry-rs/compilers", + "https://github.com/foundry-rs/foundry-fork-db", "https://github.com/foundry-rs/foundry-core", + "https://github.com/foundry-rs/optimism", "https://github.com/paradigmxyz/revm-inspectors", "https://github.com/paradigmxyz/solar", "https://github.com/bluealloy/revm", @@ -111,7 +114,5 @@ allow-git = [ "https://github.com/tempoxyz/mpp-rs", # Transitive dependency of Tempo "https://github.com/paradigmxyz/reth", - "https://github.com/paradigmxyz/reth-core", - # Temporary: upstream OP crates until release is published. - "https://github.com/ethereum-optimism/optimism", + "https://github.com/stevencartavia/reth", ] diff --git a/npm/scripts/stage-from-artifact.mjs b/npm/scripts/stage-from-artifact.mjs index c1ca22c8bb2ed..1d39fdc82e84f 100755 --- a/npm/scripts/stage-from-artifact.mjs +++ b/npm/scripts/stage-from-artifact.mjs @@ -64,10 +64,10 @@ function resolveArgs() { strict: true }) - const tool = requireValue(values.tool || process.env.TARGET_TOOL, 'tool') - const platform = requireValue(values.platform || process.env.PLATFORM_NAME, 'platform') - const arch = requireValue(values.arch || process.env.ARCH, 'arch') - const releaseVersion = requireValue( + const tool = requireSafeIdentifier(values.tool || process.env.TARGET_TOOL, 'tool') + const platform = requireSafeIdentifier(values.platform || process.env.PLATFORM_NAME, 'platform') + const arch = requireSafeIdentifier(values.arch || process.env.ARCH, 'arch') + const releaseVersion = requireSafeIdentifier( values.release || values['release-version'] || process.env.RELEASE_VERSION, 'release version' ) @@ -95,6 +95,26 @@ function requireValue(value, name) { throw new Error(`Missing required ${name}`) } +/** + * Ensure a required value is present and consists only of safe identifier + * characters suitable for use in file and directory names. + * + * Allowed characters: letters, digits, dot, underscore, and hyphen. + * + * @param {string | undefined} value + * @param {string} name + * @returns {string} + */ +function requireSafeIdentifier(value, name) { + const trimmed = requireValue(value, name) + if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) { + throw new Error( + `Invalid ${name}: "${trimmed}". Only letters, digits, ".", "_", and "-" are allowed.` + ) + } + return trimmed +} + /** * Determine which archive variant exists for the given artifact prefix. * @param {string} prefix diff --git a/npm/src/const.mjs b/npm/src/const.mjs index 6b3dcf3f9fbed..e606759888acb 100644 --- a/npm/src/const.mjs +++ b/npm/src/const.mjs @@ -1,4 +1,5 @@ import * as NodePath from 'node:path' +import { URL } from 'node:url' /** * @typedef {'amd64' | 'arm64'} Arch @@ -33,11 +34,36 @@ export function resolveTargetTool(raw = process.env.TARGET_TOOL || process.argv[ export function getRegistryUrl() { // Prefer npm's configured registry (works with Verdaccio and custom registries) // Fallback to REGISTRY_URL for tests/dev, then npmjs - return ( + const raw = process.env.npm_config_registry || process.env.REGISTRY_URL || 'https://registry.npmjs.org' - ) + + let parsed + try { + parsed = new URL(raw) + } catch { + throw new Error(`Invalid registry URL: "${raw}"`) + } + + // Enforce secure scheme + if (parsed.protocol !== 'https:') { + throw new Error(`Insecure registry URL scheme "${parsed.protocol}". Only "https:" is allowed.`) + } + + // Basic SSRF mitigation: disallow obvious loopback hosts + const hostname = parsed.hostname.toLowerCase() + if ( + hostname === 'localhost' + || hostname === '127.0.0.1' + || hostname === '::1' + ) { + throw new Error(`Registry URL host "${parsed.hostname}" is not allowed.`) + } + + // Normalize to a consistent base URL without trailing slash + const base = parsed.origin + parsed.pathname + return base.replace(/\/+$/, '') } /** diff --git a/sleep.json b/sleep.json new file mode 100644 index 0000000000000..5b430e1e663f6 --- /dev/null +++ b/sleep.json @@ -0,0 +1,955 @@ +{ + "results": [ + { + "command": "sleep 0.020", + "mean": 0.023726515413333333, + "stddev": 0.004602014051751124, + "median": 0.02267755758, + "user": 0.0013185473333333334, + "system": 0.0020899164444444446, + "min": 0.02109890308, + "max": 0.05602819808, + "times": [ + 0.02856005608, + 0.02346135008, + 0.02202502208, + 0.02139558708, + 0.02265920408, + 0.02121691608, + 0.02272505608, + 0.02114247908, + 0.02157142808, + 0.021514666079999998, + 0.02161920108, + 0.02335035008, + 0.02224331408, + 0.02228639708, + 0.02152537208, + 0.021732302079999998, + 0.02273370308, + 0.02115513608, + 0.02268494308, + 0.02244547308, + 0.023943647079999998, + 0.02324528508, + 0.02152617908, + 0.023991903079999998, + 0.02250884108, + 0.02342551708, + 0.02113216608, + 0.02168223108, + 0.02222267508, + 0.02273532108, + 0.02273995308, + 0.05602819808, + 0.02501500608, + 0.03121396008, + 0.02424400108, + 0.02459129108, + 0.02633760708, + 0.02377406808, + 0.02365474708, + 0.02406064008, + 0.02300910408, + 0.02437339208, + 0.02317403908, + 0.02257532008, + 0.02267017208, + 0.02356714508, + 0.02367204808, + 0.02258227108, + 0.02330384008, + 0.02225645108, + 0.02478414908, + 0.02484724308, + 0.02270765708, + 0.02339114708, + 0.02450795908, + 0.02348840008, + 0.044674490080000004, + 0.028041754080000002, + 0.022940745079999998, + 0.02259975308, + 0.022112378079999998, + 0.02271348408, + 0.02320266708, + 0.02284982108, + 0.02244050908, + 0.02238655808, + 0.022084648079999998, + 0.02241669808, + 0.02523103408, + 0.02256237908, + 0.03532525108, + 0.02232798408, + 0.02173793008, + 0.021903001079999998, + 0.02288046308, + 0.02368652508, + 0.02211418708, + 0.02265551308, + 0.02187778308, + 0.02191395108, + 0.02182523808, + 0.02185612208, + 0.02109890308, + 0.02294132008, + 0.02191512608, + 0.02264461208, + 0.02227651108, + 0.02307147508, + 0.02227169708, + 0.02177434208 + ], + "memory_usage_byte": [ + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.021", + "mean": 0.022889189941111117, + "stddev": 0.0007161191938371117, + "median": 0.02280623708, + "user": 0.0009166992592592593, + "system": 0.0016941181481481477, + "min": 0.02132554808, + "max": 0.02453766808, + "times": [ + 0.02311599608, + 0.02274468508, + 0.02193879008, + 0.02158843608, + 0.02329398008, + 0.02379494508, + 0.02260801308, + 0.02439507908, + 0.02448522508, + 0.02403379508, + 0.02298143008, + 0.02263027308, + 0.02229235308, + 0.02335063508, + 0.02377098008, + 0.02269184108, + 0.023631199079999998, + 0.02338021508, + 0.02198521708, + 0.02251586208, + 0.022295963079999998, + 0.02226397608, + 0.02453766808, + 0.02184453408, + 0.02289659908, + 0.02382663208, + 0.02347397108, + 0.02225926308, + 0.02207640608, + 0.02243237108, + 0.02278192608, + 0.02270514808, + 0.02245069008, + 0.023018867079999998, + 0.02399866208, + 0.02236840708, + 0.02366382208, + 0.02294188908, + 0.02155127708, + 0.02294999808, + 0.02132554808, + 0.02242025908, + 0.02202766108, + 0.02182175108, + 0.02272186608, + 0.02211805308, + 0.02319764908, + 0.022308045079999998, + 0.02345400908, + 0.022437877079999998, + 0.02273417808, + 0.02217370908, + 0.02254318408, + 0.023269922079999998, + 0.02384951108, + 0.02419476108, + 0.02439866908, + 0.02354840508, + 0.02304219108, + 0.02354960608, + 0.02382648708, + 0.02345751208, + 0.02367913708, + 0.02253067208, + 0.02215132608, + 0.022603942079999998, + 0.02284062808, + 0.02252907808, + 0.02220393508, + 0.023291509079999998, + 0.02399456908, + 0.02407123208, + 0.02279175108, + 0.02300624708, + 0.02309500408, + 0.023036532079999998, + 0.02303833108, + 0.02316846908, + 0.02228349608, + 0.02247140608, + 0.022482600079999998, + 0.02370720808, + 0.02220123708, + 0.02230588608, + 0.02333678708, + 0.02153336008, + 0.02203071908, + 0.02279195108, + 0.02353659108, + 0.02267460708, + 0.022536274079999998, + 0.022769262079999998, + 0.02314857808, + 0.02194885908, + 0.02355038408, + 0.02320035308, + 0.02307451408, + 0.02379926408, + 0.02330480208, + 0.02257055708, + 0.02330320308, + 0.02303003208, + 0.02327859908, + 0.02171311608, + 0.02282052308, + 0.02170123708, + 0.02254831308, + 0.02235855408 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.022", + "mean": 0.02415569324504855, + "stddev": 0.0009830972994273135, + "median": 0.02409406108, + "user": 0.001165289514563107, + "system": 0.001767603883495146, + "min": 0.02243173808, + "max": 0.02755932908, + "times": [ + 0.02456728108, + 0.02650439708, + 0.02480475408, + 0.02452974808, + 0.02300978308, + 0.02521451608, + 0.02543841408, + 0.02538411108, + 0.02475773908, + 0.02403843308, + 0.02426362708, + 0.02326921708, + 0.02447185308, + 0.02361749008, + 0.02410661008, + 0.02371481508, + 0.02327300908, + 0.02430165908, + 0.02328269108, + 0.02315262608, + 0.02380195808, + 0.02283639508, + 0.02491355808, + 0.02401717008, + 0.02556049408, + 0.02350359508, + 0.02400529208, + 0.02533555808, + 0.02467923308, + 0.02478442308, + 0.02422068708, + 0.02352175108, + 0.02481882108, + 0.02456148108, + 0.02314905108, + 0.024188183079999998, + 0.02483985908, + 0.02289141308, + 0.02364977308, + 0.02354907008, + 0.02379135508, + 0.026812933079999997, + 0.023360627079999998, + 0.02331436308, + 0.02504176308, + 0.02358805508, + 0.02409406108, + 0.02350689508, + 0.02303628508, + 0.02430972408, + 0.02516170908, + 0.02352843108, + 0.02274564308, + 0.02345165808, + 0.02429327308, + 0.02252948108, + 0.02445868508, + 0.02755932908, + 0.02522621808, + 0.02491753008, + 0.022858510079999998, + 0.02401968108, + 0.02409596908, + 0.02390450108, + 0.02373108808, + 0.027211489079999998, + 0.02537487108, + 0.02319182608, + 0.02390569508, + 0.02490164708, + 0.02384732708, + 0.02243173808, + 0.02367003008, + 0.02494288308, + 0.02436298308, + 0.02390639308, + 0.02423030808, + 0.02430082908, + 0.02320845908, + 0.02421546708, + 0.02530823508, + 0.02368935308, + 0.02306283708, + 0.023536658079999998, + 0.02359881208, + 0.02438320308, + 0.02477724008, + 0.02362231908, + 0.02419465008, + 0.02596891608, + 0.02307578608, + 0.02459456508, + 0.02384055408, + 0.02421387408, + 0.02510733208, + 0.02473580508, + 0.02243970708, + 0.02253156008, + 0.02550018108, + 0.02440877608, + 0.02281331608, + 0.02354148408, + 0.02352098308 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] +} From 1175872ab3ae417ede7dce3d6d4c1cc160a492be Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Thu, 7 May 2026 08:42:20 +0700 Subject: [PATCH 374/374] ci: sign release archives, docker images, and publish SBOMs --- .circleci/cargo.yml | 32 + .circleci/ci-web3-gamefi.yml | 26 + .circleci/ci.yml | 31 + .circleci/ci_cargo.yml | 37 + .circleci/ci_v1.yml | 31 + .circleci/dev_stage.yml | 70 + .circleci/web3_defi_gamefi.yml | 26 + .github/CODEOWNERS | 2 +- .github/TEMPO_NIGHTLY_FAILURE_TEMPLATE.md | 10 + .github/scripts/commit-and-read-benchmarks.sh | 114 -- .github/scripts/commit-benchmark-results.sh | 75 + .github/scripts/compare-nightly.sh | 56 + .github/scripts/read-benchmark-results.sh | 37 + .github/scripts/tempo-check.sh | 86 +- .github/workflows/benchmarks-nightly.yml | 217 +++ .github/workflows/benchmarks.yml | 73 +- .github/workflows/ci-tempo.yml | 91 +- .github/workflows/ci.yml | 18 + .github/workflows/crate-checks.yml | 2 +- .github/workflows/docker-publish.yml | 30 + .github/workflows/nix.yml | 4 +- .github/workflows/npm.yml | 34 +- .github/workflows/release.yml | 78 +- .github/workflows/test-flaky.yml | 2 +- .github/workflows/test-isolate.yml | 2 +- .github/workflows/test.yml | 4 +- Cargo.lock | 1024 +++++------ README.md | 2 + SECURITY.md | 109 ++ benches/LATEST.md | 134 +- benches/src/main.rs | 40 +- benches/src/results.rs | 19 + benchmark.sh | 60 +- crates/anvil/Cargo.toml | 34 +- crates/anvil/core/Cargo.toml | 10 +- crates/anvil/src/cmd.rs | 27 +- crates/anvil/src/config.rs | 6 +- crates/anvil/src/eth/api.rs | 44 +- crates/anvil/src/eth/backend/executor.rs | 23 +- crates/anvil/src/eth/backend/mem/mod.rs | 267 ++- crates/anvil/src/eth/backend/mem/optimism.rs | 61 + .../anvil/src/eth/{error.rs => error/mod.rs} | 69 +- crates/anvil/src/eth/error/optimism.rs | 62 + crates/anvil/src/eth/otterscan/api.rs | 19 +- crates/anvil/src/eth/pool/transactions.rs | 4 +- crates/anvil/src/eth/sign.rs | 8 +- crates/anvil/src/{evm.rs => evm/mod.rs} | 84 +- crates/anvil/src/evm/optimism.rs | 87 + crates/anvil/src/lib.rs | 3 + crates/anvil/tests/it/main.rs | 2 + crates/anvil/tests/it/revert.rs | 50 + crates/cast/Cargo.toml | 17 +- crates/cast/src/args.rs | 7 + crates/cast/src/cmd/batch_mktx.rs | 27 +- crates/cast/src/cmd/batch_send.rs | 32 +- crates/cast/src/cmd/call.rs | 25 +- crates/cast/src/cmd/keychain.rs | 1022 ++++++++++- crates/cast/src/cmd/mktx.rs | 44 +- crates/cast/src/cmd/mod.rs | 3 + crates/cast/src/cmd/run.rs | 17 +- crates/cast/src/cmd/send.rs | 72 +- crates/cast/src/cmd/tempo.rs | 45 + crates/cast/src/cmd/tip20/mine.rs | 23 +- crates/cast/src/cmd/tip20/mod.rs | 2 +- crates/cast/src/cmd/vaddr/create.rs | 181 ++ crates/cast/src/cmd/vaddr/mod.rs | 131 ++ crates/cast/src/cmd/vaddr/resolve.rs | 52 + crates/cast/src/cmd/vaddr/watch.rs | 108 ++ crates/cast/src/cmd/wallet/mod.rs | 13 +- crates/cast/src/lib.rs | 4 +- crates/cast/src/opts.rs | 25 +- crates/cast/src/tx.rs | 30 +- crates/cast/tests/cli/keychain.rs | 76 + crates/cast/tests/cli/main.rs | 119 ++ crates/cheatcodes/Cargo.toml | 10 + crates/cheatcodes/assets/cheatcodes.json | 120 +- crates/cheatcodes/spec/src/vm.rs | 122 +- crates/cheatcodes/src/inspector.rs | 145 +- crates/cheatcodes/src/test/assert.rs | 14 +- crates/cheatcodes/src/version.rs | 67 +- crates/chisel/Cargo.toml | 9 +- crates/chisel/src/executor.rs | 1617 ++++++++--------- crates/chisel/src/source.rs | 562 ++---- crates/chisel/tests/it/repl/mod.rs | 20 + crates/cli/Cargo.toml | 7 + crates/cli/src/opts/evm.rs | 11 + crates/cli/src/opts/rpc.rs | 56 +- crates/cli/src/opts/rpc_common.rs | 7 +- crates/cli/src/opts/tempo.rs | 320 +++- crates/cli/src/utils/tempo.rs | 193 +- crates/common/Cargo.toml | 20 +- crates/common/build.rs | 20 +- crates/common/fmt/Cargo.toml | 8 +- crates/common/fmt/src/ui.rs | 8 + crates/common/src/contracts.rs | 14 +- crates/common/src/provider/mpp/keys.rs | 73 +- crates/common/src/provider/mpp/session.rs | 10 + crates/common/src/provider/mpp/transport.rs | 922 +++++++++- crates/common/src/provider/mpp/ws.rs | 4 + .../common/src/provider/runtime_transport.rs | 6 +- crates/common/src/tempo/auth.rs | 494 +++++ crates/common/src/tempo/mod.rs | 186 ++ crates/common/src/transactions/builder.rs | 57 +- crates/common/src/transactions/receipt.rs | 2 + crates/config/src/fuzz.rs | 6 + crates/config/src/inline/mod.rs | 39 + crates/config/src/lib.rs | 83 +- crates/config/src/providers/warnings.rs | 5 +- crates/debugger/Cargo.toml | 8 + crates/doc/Cargo.toml | 6 +- crates/doc/src/builder.rs | 40 +- crates/doc/src/helpers.rs | 91 - crates/doc/src/lib.rs | 7 +- crates/doc/src/parser/comment.rs | 74 +- crates/doc/src/parser/item.rs | 136 +- crates/doc/src/parser/mod.rs | 407 +++-- crates/doc/src/parser/source.rs | 172 ++ .../src/preprocessor/contract_inheritance.rs | 12 +- .../doc/src/preprocessor/infer_hyperlinks.rs | 17 +- crates/doc/src/preprocessor/inheritdoc.rs | 3 +- crates/doc/src/solang_ext/ast_eq.rs | 708 -------- crates/doc/src/solang_ext/loc.rs | 168 -- crates/doc/src/solang_ext/mod.rs | 31 - crates/doc/src/solang_ext/safe_unwrap.rs | 52 - crates/doc/src/solang_ext/visit.rs | 621 ------- crates/doc/src/writer/as_doc.rs | 69 +- crates/doc/src/writer/traits.rs | 51 +- crates/evm/core/Cargo.toml | 24 +- crates/evm/core/src/decode.rs | 4 +- crates/evm/core/src/env.rs | 608 ++++--- crates/evm/core/src/evm/mod.rs | 21 +- crates/evm/core/src/evm/op.rs | 22 +- crates/evm/core/src/fork/database.rs | 53 +- crates/evm/core/src/lib.rs | 3 + crates/evm/core/src/opts.rs | 7 +- crates/evm/coverage/Cargo.toml | 4 + crates/evm/evm/Cargo.toml | 13 + crates/evm/evm/src/executors/fuzz/mod.rs | 125 +- crates/evm/evm/src/executors/invariant/mod.rs | 2 +- crates/evm/fuzz/Cargo.toml | 9 + crates/evm/fuzz/src/lib.rs | 35 +- crates/evm/hardforks/Cargo.toml | 8 +- crates/evm/hardforks/src/lib.rs | 74 +- crates/evm/networks/Cargo.toml | 8 +- crates/evm/networks/src/lib.rs | 273 ++- crates/evm/networks/src/optimism.rs | 25 + crates/evm/traces/Cargo.toml | 4 + crates/fmt/Cargo.toml | 4 + crates/fmt/src/state/mod.rs | 2 +- .../bracket-spacing.fmt.sol | 6 + .../contract-new-lines.fmt.sol | 9 + .../fmt/testdata/ContractDefinition/fmt.sol | 9 + .../testdata/ContractDefinition/original.sol | 3 + crates/forge/Cargo.toml | 15 +- crates/forge/assets/tempo/MailTemplate.s.sol | 2 +- crates/forge/assets/tempo/MailTemplate.t.sol | 2 +- crates/forge/src/cmd/coverage.rs | 7 +- crates/forge/src/cmd/create.rs | 49 +- crates/forge/src/cmd/snapshot.rs | 7 +- crates/forge/src/cmd/test/mod.rs | 238 ++- crates/forge/src/cmd/test/summary.rs | 4 +- crates/forge/src/gas_report.rs | 2 +- crates/forge/src/multi_runner.rs | 32 + crates/forge/src/runner.rs | 24 +- crates/forge/tests/cli/cmd.rs | 127 +- crates/forge/tests/cli/config.rs | 31 +- crates/forge/tests/cli/ext_integration.rs | 14 +- crates/forge/tests/cli/failure_assertions.rs | 7 +- crates/forge/tests/cli/inline_config.rs | 104 ++ crates/forge/tests/cli/lint.rs | 289 ++- crates/forge/tests/cli/lint/geiger.rs | 10 +- crates/forge/tests/cli/script.rs | 2 +- crates/forge/tests/cli/test_cmd/fuzz.rs | 145 ++ .../tests/cli/test_cmd/invariant/common.rs | 2 +- .../forge/tests/cli/test_cmd/invariant/mod.rs | 12 +- crates/forge/tests/cli/test_cmd/repros.rs | 60 + .../tests/fixtures/ExpectRevertFailures.t.sol | 57 + crates/lint/Cargo.toml | 4 + crates/lint/README.md | 12 + crates/lint/docs/README.md | 52 + crates/lint/docs/_template.md | 28 + crates/lint/docs/asm-keccak256.md | 42 + crates/lint/docs/block-timestamp.md | 44 + crates/lint/docs/boolean-cst.md | 37 + crates/lint/docs/boolean-equal.md | 34 + crates/lint/docs/could-be-immutable.md | 42 + crates/lint/docs/custom-errors.md | 45 + crates/lint/docs/divide-before-multiply.md | 32 + crates/lint/docs/erc20-unchecked-transfer.md | 43 + crates/lint/docs/incorrect-erc20-interface.md | 42 + .../lint/docs/incorrect-erc721-interface.md | 48 + crates/lint/docs/incorrect-shift.md | 37 + crates/lint/docs/inline-assembly.md | 69 + crates/lint/docs/interface-file-naming.md | 31 + crates/lint/docs/interface-naming.md | 31 + crates/lint/docs/missing-zero-check.md | 39 + crates/lint/docs/mixed-case-function.md | 32 + crates/lint/docs/mixed-case-variable.md | 36 + crates/lint/docs/multi-contract-file.md | 37 + crates/lint/docs/named-struct-fields.md | 31 + crates/lint/docs/pascal-case-struct.md | 31 + crates/lint/docs/pragma-inconsistent.md | 41 + crates/lint/docs/rtlo.md | 32 + .../lint/docs/screaming-snake-case-const.md | 30 + .../docs/screaming-snake-case-immutable.md | 31 + crates/lint/docs/too-many-digits.md | 32 + crates/lint/docs/tx-origin.md | 34 + crates/lint/docs/unaliased-plain-import.md | 34 + crates/lint/docs/unchecked-call.md | 34 + crates/lint/docs/unsafe-cheatcode.md | 35 + crates/lint/docs/unsafe-typecast.md | 40 + crates/lint/docs/unused-import.md | 40 + crates/lint/docs/unused-state-variables.md | 39 + crates/lint/docs/unwrapped-modifier-logic.md | 51 + crates/lint/src/linter/late.rs | 1 + crates/lint/src/linter/mod.rs | 34 +- crates/lint/src/linter/project.rs | 92 + crates/lint/src/sol/gas/immutable.rs | 406 +++++ crates/lint/src/sol/gas/mod.rs | 11 +- .../src/sol/gas/unused_state_variables.rs | 90 + crates/lint/src/sol/high/mod.rs | 5 +- crates/lint/src/sol/high/rtlo.rs | 58 + crates/lint/src/sol/info/boolean_cst.rs | 116 ++ crates/lint/src/sol/info/boolean_equal.rs | 108 ++ crates/lint/src/sol/info/inline_assembly.rs | 71 + crates/lint/src/sol/info/mod.rs | 20 + crates/lint/src/sol/info/pragma_directive.rs | 71 + crates/lint/src/sol/info/too_many_digits.rs | 50 + crates/lint/src/sol/macros.rs | 42 +- crates/lint/src/sol/med/mod.rs | 4 + crates/lint/src/sol/med/tx_origin.rs | 101 + crates/lint/src/sol/mod.rs | 156 +- crates/lint/testdata/BlockTimestamp.stderr | 24 +- crates/lint/testdata/BooleanCst.sol | 25 + crates/lint/testdata/BooleanCst.stderr | 40 + crates/lint/testdata/BooleanEqual.sol | 24 + crates/lint/testdata/BooleanEqual.stderr | 56 + crates/lint/testdata/CouldBeImmutable.sol | 85 + crates/lint/testdata/CouldBeImmutable.stderr | 56 + crates/lint/testdata/CustomErrors.stderr | 10 +- .../lint/testdata/DivideBeforeMultiply.stderr | 12 +- crates/lint/testdata/Imports.stderr | 26 +- .../testdata/IncorrectERC20Interface.stderr | 30 +- .../testdata/IncorrectERC721Interface.stderr | 38 +- crates/lint/testdata/IncorrectShift.stderr | 10 +- crates/lint/testdata/InlineAssembly.sol | 110 ++ crates/lint/testdata/InlineAssembly.stderr | 96 + crates/lint/testdata/Keccak256.sol | 1 + crates/lint/testdata/Keccak256.stderr | 42 +- crates/lint/testdata/MissingZeroCheck.stderr | 46 +- crates/lint/testdata/MixedCase.stderr | 38 +- crates/lint/testdata/MultiContractFile.stderr | 10 +- .../MultiContractFile_InterfaceLibrary.stderr | 6 +- crates/lint/testdata/NamedStructFields.stderr | 2 +- .../PragmaInconsistentCaretAboveExact.sol | 7 + .../PragmaInconsistentCaretAboveExact.stderr | 16 + .../PragmaInconsistentCaretMatchesExact.sol | 7 + ...PragmaInconsistentCaretMatchesExact.stderr | 16 + .../PragmaInconsistentCaretVsTilde.sol | 7 + .../PragmaInconsistentCaretVsTilde.stderr | 16 + .../testdata/PragmaInconsistentOrVsExact.sol | 7 + .../PragmaInconsistentOrVsExact.stderr | 16 + .../PragmaInconsistentRangeVsExact.sol | 7 + .../PragmaInconsistentRangeVsExact.stderr | 16 + .../PragmaInconsistentThreeDistinct.sol | 8 + .../PragmaInconsistentThreeDistinct.stderr | 24 + crates/lint/testdata/Rtlo.sol | 81 + crates/lint/testdata/Rtlo.stderr | 192 ++ crates/lint/testdata/RtloCommentsOnly.sol | 15 + crates/lint/testdata/RtloCommentsOnly.stderr | 32 + .../lint/testdata/ScreamingSnakeCase.stderr | 16 +- crates/lint/testdata/StructPascalCase.stderr | 12 +- crates/lint/testdata/TooManyDigits.sol | 73 + crates/lint/testdata/TooManyDigits.stderr | 72 + crates/lint/testdata/TxOrigin.sol | 65 + crates/lint/testdata/TxOrigin.stderr | 72 + crates/lint/testdata/UncheckedCall.stderr | 16 +- .../testdata/UncheckedTransferERC20.stderr | 22 +- crates/lint/testdata/UnsafeCheatcodes.stderr | 26 +- crates/lint/testdata/UnsafeTypecast.stderr | 330 ++-- crates/lint/testdata/UnusedStateVariables.sol | 52 + .../lint/testdata/UnusedStateVariables.stderr | 40 + .../testdata/UnwrappedModifierLogic.stderr | 22 +- crates/primitives/Cargo.toml | 17 +- crates/primitives/src/network/mod.rs | 10 +- crates/primitives/src/network/optimism.rs | 47 + crates/primitives/src/network/receipt.rs | 40 +- crates/primitives/src/transaction/envelope.rs | 281 +-- crates/primitives/src/transaction/mod.rs | 6 +- crates/primitives/src/transaction/optimism.rs | 300 +++ crates/primitives/src/transaction/receipt.rs | 114 +- crates/primitives/src/transaction/request.rs | 110 +- crates/script-sequence/Cargo.toml | 4 + crates/script/Cargo.toml | 12 + crates/script/src/broadcast.rs | 143 +- crates/script/src/lib.rs | 131 +- crates/script/src/runner.rs | 10 +- crates/script/src/verify.rs | 2 +- crates/sol-macro-gen/Cargo.toml | 4 + crates/test-utils/Cargo.toml | 4 + crates/test-utils/src/util.rs | 2 +- crates/verify/Cargo.toml | 9 + crates/wallets/src/tempo.rs | 196 ++ deny.toml | 7 +- docs/dev/lintrules.md | 2 + flake.lock | 18 +- foundryup/README.md | 4 +- foundryup/foundryup | 127 +- sleep.json | 955 ++++++++++ testdata/default/cheats/ExpectRevert.t.sol | 85 + testdata/default/cheats/Fork2.t.sol | 1 + .../default/cheats/GetFoundryVersion.t.sol | 51 + testdata/default/cheats/MockCall.t.sol | 41 +- testdata/default/cheats/MockCalls.t.sol | 4 + testdata/forge-std-rev | 2 +- testdata/utils/Vm.sol | 116 +- 316 files changed, 17238 insertions(+), 6495 deletions(-) create mode 100644 .circleci/cargo.yml create mode 100644 .circleci/ci-web3-gamefi.yml create mode 100644 .circleci/ci.yml create mode 100644 .circleci/ci_cargo.yml create mode 100644 .circleci/ci_v1.yml create mode 100644 .circleci/dev_stage.yml create mode 100644 .circleci/web3_defi_gamefi.yml create mode 100644 .github/TEMPO_NIGHTLY_FAILURE_TEMPLATE.md delete mode 100755 .github/scripts/commit-and-read-benchmarks.sh create mode 100644 .github/scripts/commit-benchmark-results.sh create mode 100644 .github/scripts/compare-nightly.sh create mode 100644 .github/scripts/read-benchmark-results.sh create mode 100644 .github/workflows/benchmarks-nightly.yml create mode 100644 crates/anvil/src/eth/backend/mem/optimism.rs rename crates/anvil/src/eth/{error.rs => error/mod.rs} (91%) create mode 100644 crates/anvil/src/eth/error/optimism.rs rename crates/anvil/src/{evm.rs => evm/mod.rs} (64%) create mode 100644 crates/anvil/src/evm/optimism.rs create mode 100644 crates/cast/src/cmd/tempo.rs create mode 100644 crates/cast/src/cmd/vaddr/create.rs create mode 100644 crates/cast/src/cmd/vaddr/mod.rs create mode 100644 crates/cast/src/cmd/vaddr/resolve.rs create mode 100644 crates/cast/src/cmd/vaddr/watch.rs create mode 100644 crates/cast/tests/cli/keychain.rs create mode 100644 crates/common/src/tempo/auth.rs create mode 100644 crates/doc/src/parser/source.rs delete mode 100644 crates/doc/src/solang_ext/ast_eq.rs delete mode 100644 crates/doc/src/solang_ext/loc.rs delete mode 100644 crates/doc/src/solang_ext/mod.rs delete mode 100644 crates/doc/src/solang_ext/safe_unwrap.rs delete mode 100644 crates/doc/src/solang_ext/visit.rs create mode 100644 crates/evm/networks/src/optimism.rs create mode 100644 crates/lint/docs/README.md create mode 100644 crates/lint/docs/_template.md create mode 100644 crates/lint/docs/asm-keccak256.md create mode 100644 crates/lint/docs/block-timestamp.md create mode 100644 crates/lint/docs/boolean-cst.md create mode 100644 crates/lint/docs/boolean-equal.md create mode 100644 crates/lint/docs/could-be-immutable.md create mode 100644 crates/lint/docs/custom-errors.md create mode 100644 crates/lint/docs/divide-before-multiply.md create mode 100644 crates/lint/docs/erc20-unchecked-transfer.md create mode 100644 crates/lint/docs/incorrect-erc20-interface.md create mode 100644 crates/lint/docs/incorrect-erc721-interface.md create mode 100644 crates/lint/docs/incorrect-shift.md create mode 100644 crates/lint/docs/inline-assembly.md create mode 100644 crates/lint/docs/interface-file-naming.md create mode 100644 crates/lint/docs/interface-naming.md create mode 100644 crates/lint/docs/missing-zero-check.md create mode 100644 crates/lint/docs/mixed-case-function.md create mode 100644 crates/lint/docs/mixed-case-variable.md create mode 100644 crates/lint/docs/multi-contract-file.md create mode 100644 crates/lint/docs/named-struct-fields.md create mode 100644 crates/lint/docs/pascal-case-struct.md create mode 100644 crates/lint/docs/pragma-inconsistent.md create mode 100644 crates/lint/docs/rtlo.md create mode 100644 crates/lint/docs/screaming-snake-case-const.md create mode 100644 crates/lint/docs/screaming-snake-case-immutable.md create mode 100644 crates/lint/docs/too-many-digits.md create mode 100644 crates/lint/docs/tx-origin.md create mode 100644 crates/lint/docs/unaliased-plain-import.md create mode 100644 crates/lint/docs/unchecked-call.md create mode 100644 crates/lint/docs/unsafe-cheatcode.md create mode 100644 crates/lint/docs/unsafe-typecast.md create mode 100644 crates/lint/docs/unused-import.md create mode 100644 crates/lint/docs/unused-state-variables.md create mode 100644 crates/lint/docs/unwrapped-modifier-logic.md create mode 100644 crates/lint/src/linter/project.rs create mode 100644 crates/lint/src/sol/gas/immutable.rs create mode 100644 crates/lint/src/sol/gas/unused_state_variables.rs create mode 100644 crates/lint/src/sol/high/rtlo.rs create mode 100644 crates/lint/src/sol/info/boolean_cst.rs create mode 100644 crates/lint/src/sol/info/boolean_equal.rs create mode 100644 crates/lint/src/sol/info/inline_assembly.rs create mode 100644 crates/lint/src/sol/info/pragma_directive.rs create mode 100644 crates/lint/src/sol/info/too_many_digits.rs create mode 100644 crates/lint/src/sol/med/tx_origin.rs create mode 100644 crates/lint/testdata/BooleanCst.sol create mode 100644 crates/lint/testdata/BooleanCst.stderr create mode 100644 crates/lint/testdata/BooleanEqual.sol create mode 100644 crates/lint/testdata/BooleanEqual.stderr create mode 100644 crates/lint/testdata/CouldBeImmutable.sol create mode 100644 crates/lint/testdata/CouldBeImmutable.stderr create mode 100644 crates/lint/testdata/InlineAssembly.sol create mode 100644 crates/lint/testdata/InlineAssembly.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol create mode 100644 crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentOrVsExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentOrVsExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentRangeVsExact.sol create mode 100644 crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr create mode 100644 crates/lint/testdata/PragmaInconsistentThreeDistinct.sol create mode 100644 crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr create mode 100644 crates/lint/testdata/Rtlo.sol create mode 100644 crates/lint/testdata/Rtlo.stderr create mode 100644 crates/lint/testdata/RtloCommentsOnly.sol create mode 100644 crates/lint/testdata/RtloCommentsOnly.stderr create mode 100644 crates/lint/testdata/TooManyDigits.sol create mode 100644 crates/lint/testdata/TooManyDigits.stderr create mode 100644 crates/lint/testdata/TxOrigin.sol create mode 100644 crates/lint/testdata/TxOrigin.stderr create mode 100644 crates/lint/testdata/UnusedStateVariables.sol create mode 100644 crates/lint/testdata/UnusedStateVariables.stderr create mode 100644 crates/primitives/src/network/optimism.rs create mode 100644 crates/primitives/src/transaction/optimism.rs create mode 100644 crates/wallets/src/tempo.rs create mode 100644 sleep.json diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..32b65e6a23cc5 --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,32 @@ +version: 2.1 +# +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..82c6de5b42b73 --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..5ba351727d22b --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,70 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 83ff83e43c870..a6726847fd70b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @danipopes @mattsse @grandizzy @zerosnacks @onbjerg @0xrusowsky @mablr @figtracer @stevencartavia +* @danipopes @mattsse @grandizzy @zerosnacks @0xrusowsky @mablr @figtracer @stevencartavia diff --git a/.github/TEMPO_NIGHTLY_FAILURE_TEMPLATE.md b/.github/TEMPO_NIGHTLY_FAILURE_TEMPLATE.md new file mode 100644 index 0000000000000..8f72899c63611 --- /dev/null +++ b/.github/TEMPO_NIGHTLY_FAILURE_TEMPLATE.md @@ -0,0 +1,10 @@ +--- +title: "bug: tempo nightly workflow failed" +labels: P-high, T-bug +--- + +The nightly Tempo workflow (mainnet and testnet checks) has failed. This indicates a regression in Foundry's Tempo support or an issue with the Tempo mainnet/testnet endpoints. + +Check the [tempo nightly workflow page]({{ env.WORKFLOW_URL }}) for details. + +This issue was raised by the workflow at `.github/workflows/ci-tempo.yml`. diff --git a/.github/scripts/commit-and-read-benchmarks.sh b/.github/scripts/commit-and-read-benchmarks.sh deleted file mode 100755 index 358b53a73155a..0000000000000 --- a/.github/scripts/commit-and-read-benchmarks.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Script to commit benchmark results and read them for GitHub Actions output -# Usage: ./commit-and-read-benchmarks.sh - -OUTPUT_DIR="${1:-benches}" -GITHUB_EVENT_NAME="${2:-pull_request}" -GITHUB_REPOSITORY="${3:-}" - -# Global variable for branch name -BRANCH_NAME="" - -# Function to commit benchmark results -commit_results() { - echo "Configuring git..." - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - # For PR runs, fetch and checkout the PR branch to ensure we're up to date - if [ "$GITHUB_EVENT_NAME" = "pull_request" ] && [ -n "${GITHUB_HEAD_REF:-}" ]; then - echo "Fetching latest changes for PR branch: $GITHUB_HEAD_REF" - git fetch origin "$GITHUB_HEAD_REF" - git checkout -B "$GITHUB_HEAD_REF" "origin/$GITHUB_HEAD_REF" - fi - - echo "Adding benchmark file..." - git add "$OUTPUT_DIR/LATEST.md" - - if git diff --staged --quiet; then - echo "No changes to commit" - else - echo "Committing benchmark results..." - git commit -m "chore(\`benches\`): update benchmark results - -🤖 Generated with [Foundry Benchmarks](https://github.com/${GITHUB_REPOSITORY}/actions) - -Co-Authored-By: github-actions " - - echo "Pushing to repository..." - if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then - # For manual runs, we're on a new branch - git push origin "$BRANCH_NAME" - elif [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then - # For PR runs, push to the PR branch - if [ -n "${GITHUB_HEAD_REF:-}" ]; then - echo "Pushing to PR branch: $GITHUB_HEAD_REF" - git push origin "$GITHUB_HEAD_REF" - else - echo "Error: GITHUB_HEAD_REF not set for pull_request event" - exit 1 - fi - else - # This workflow should only run on workflow_dispatch or pull_request - echo "Error: Unexpected event type: $GITHUB_EVENT_NAME" - echo "This workflow only supports 'workflow_dispatch' and 'pull_request' events" - exit 1 - fi - echo "Successfully pushed benchmark results" - fi -} - -# Function to read benchmark results and output for GitHub Actions -read_results() { - if [ -f "$OUTPUT_DIR/LATEST.md" ]; then - echo "Reading benchmark results..." - - # Output full results - { - echo 'results<> "$GITHUB_OUTPUT" - - # Format results for PR comment - echo "Formatting results for PR comment..." - FORMATTED_COMMENT=$("$(dirname "$0")/format-pr-comment.sh" "$OUTPUT_DIR/LATEST.md") - - { - echo 'pr_comment<> "$GITHUB_OUTPUT" - - echo "Successfully read and formatted benchmark results" - else - echo 'results=No benchmark results found.' >> "$GITHUB_OUTPUT" - echo 'pr_comment=No benchmark results found.' >> "$GITHUB_OUTPUT" - echo "Warning: No benchmark results found at $OUTPUT_DIR/LATEST.md" - fi -} - -# Main execution -echo "Starting benchmark results processing..." - -# Create new branch for manual runs -if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then - echo "Manual workflow run detected, creating new branch..." - BRANCH_NAME="benchmarks/results-$(date +%Y%m%d-%H%M%S)" - git checkout -b "$BRANCH_NAME" - echo "Created branch: $BRANCH_NAME" - - # Output branch name for later use - echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT" -fi - -# Always commit benchmark results -echo "Committing benchmark results..." -commit_results - -# Always read results for output -read_results - -echo "Benchmark results processing complete" \ No newline at end of file diff --git a/.github/scripts/commit-benchmark-results.sh b/.github/scripts/commit-benchmark-results.sh new file mode 100644 index 0000000000000..f7dba8980fd64 --- /dev/null +++ b/.github/scripts/commit-benchmark-results.sh @@ -0,0 +1,75 @@ +#!/bin/bash +set -euo pipefail + +# Script to commit and push benchmark results. +# +# This script is intended to run from the lightweight `publish-results` job, +# which checks out the repo with credentials and only operates on the +# trusted artifact produced by the benchmark job. Keeping the write-scoped +# token away from the bench job (which runs untrusted third-party builds) +# limits the blast radius of a compromised dependency. +# +# Usage: ./commit-benchmark-results.sh + +OUTPUT_DIR="${1:-benches}" +GITHUB_EVENT_NAME="${2:-workflow_dispatch}" +GITHUB_REPOSITORY="${3:-}" + +if [ ! -f "$OUTPUT_DIR/LATEST.md" ]; then + echo "Error: $OUTPUT_DIR/LATEST.md not found, nothing to commit" + exit 1 +fi + +echo "Configuring git..." +git config --local user.email "action@github.com" +git config --local user.name "GitHub Action" + +# Decide which branch to commit to based on the event. +BRANCH_NAME="" +case "$GITHUB_EVENT_NAME" in + workflow_dispatch) + echo "Manual workflow run detected, creating new branch..." + BRANCH_NAME="benchmarks/results-$(date +%Y%m%d-%H%M%S)" + git checkout -b "$BRANCH_NAME" + echo "Created branch: $BRANCH_NAME" + ;; + pull_request) + if [ -z "${GITHUB_HEAD_REF:-}" ]; then + echo "Error: GITHUB_HEAD_REF not set for pull_request event" + exit 1 + fi + echo "Fetching latest changes for PR branch: $GITHUB_HEAD_REF" + git fetch origin "$GITHUB_HEAD_REF" + git checkout -B "$GITHUB_HEAD_REF" "origin/$GITHUB_HEAD_REF" + BRANCH_NAME="$GITHUB_HEAD_REF" + ;; + *) + echo "Error: Unexpected event type: $GITHUB_EVENT_NAME" + echo "This workflow only supports 'workflow_dispatch' and 'pull_request' events" + exit 1 + ;; +esac + +# Always emit the branch name so downstream steps (e.g. PR creation) can use it. +echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT" + +echo "Adding benchmark file..." +git add "$OUTPUT_DIR/LATEST.md" + +if git diff --staged --quiet; then + echo "No changes to commit" + echo "committed=false" >> "$GITHUB_OUTPUT" + exit 0 +fi + +echo "Committing benchmark results..." +git commit -m "chore(\`benches\`): update benchmark results + +🤖 Generated with [Foundry Benchmarks](https://github.com/${GITHUB_REPOSITORY}/actions) + +Co-Authored-By: github-actions " + +echo "Pushing to repository..." +git push origin "$BRANCH_NAME" +echo "Successfully pushed benchmark results to $BRANCH_NAME" +echo "committed=true" >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/compare-nightly.sh b/.github/scripts/compare-nightly.sh new file mode 100644 index 0000000000000..674cc0fe01754 --- /dev/null +++ b/.github/scripts/compare-nightly.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Compare two nightly benchmark JSON summaries and report regressions. +# +# Usage: compare-nightly.sh [warn_pct] [fail_pct] +# Exits 0 if no regressions, 1 if any metric exceeds fail_pct. +# Exits 0 gracefully when prev.json is missing (first run / gap > 7 days). +set -euo pipefail + +PREV_JSON="${1:-}" +TODAY_JSON="${2:-}" +WARN="${3:-1}" +FAIL="${4:-3}" + +PREV_JSON="$PREV_JSON" TODAY_JSON="$TODAY_JSON" WARN="$WARN" FAIL="$FAIL" \ +python3 - <<'EOF' +import json, os, sys + +warn = float(os.environ["WARN"]) +fail = float(os.environ["FAIL"]) + +prev_path = os.environ.get("PREV_JSON", "") +prev = json.load(open(prev_path)) if prev_path and os.path.isfile(prev_path) else {} +with open(os.environ["TODAY_JSON"]) as f: + today = json.load(f) + +print("## Nightly Benchmark Regression Report\n") +print("| Benchmark | Previous | Today | Δ | Status |") +print("|-----------|----------|-------|---|--------|") + +has_regression = False +all_keys = sorted(prev.keys() | today.keys()) +for key in all_keys: + t = today.get(key) + p = prev.get(key) + if t is None: + print(f"| `{key}` | {p:.5f}s | N/A | — | ⚠️ Missing |") + has_regression = True + continue + if p is None: + print(f"| `{key}` | N/A | {t:.5f}s | — | 🆕 New |") + continue + delta = (t - p) / p * 100 + if delta >= fail: + status = "🔴 Regression" + has_regression = True + elif delta >= warn: + status = "🟡 Warning" + elif delta <= -warn: + status = "🟢 Improvement" + else: + status = "➡️ OK" + sign = "+" if delta > 0 else "" + print(f"| `{key}` | {p}s | {t}s | {sign}{delta:.1f}% | {status} |") + +sys.exit(1 if has_regression else 0) +EOF diff --git a/.github/scripts/read-benchmark-results.sh b/.github/scripts/read-benchmark-results.sh new file mode 100644 index 0000000000000..548611a7d204a --- /dev/null +++ b/.github/scripts/read-benchmark-results.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -euo pipefail + +# Script to read benchmark results and emit them as GitHub Actions outputs. +# This script performs no git operations — it only reads the combined +# benchmark file and writes outputs for the workflow to consume. +# +# Usage: ./read-benchmark-results.sh + +OUTPUT_DIR="${1:-benches}" + +echo "Reading benchmark results from $OUTPUT_DIR..." + +if [ -f "$OUTPUT_DIR/LATEST.md" ]; then + # Output full results + { + echo 'results<> "$GITHUB_OUTPUT" + + # Format results for PR comment + echo "Formatting results for PR comment..." + FORMATTED_COMMENT=$("$(dirname "$0")/format-pr-comment.sh" "$OUTPUT_DIR/LATEST.md") + + { + echo 'pr_comment<> "$GITHUB_OUTPUT" + + echo "Successfully read and formatted benchmark results" +else + echo 'results=No benchmark results found.' >> "$GITHUB_OUTPUT" + echo 'pr_comment=No benchmark results found.' >> "$GITHUB_OUTPUT" + echo "Warning: No benchmark results found at $OUTPUT_DIR/LATEST.md" +fi diff --git a/.github/scripts/tempo-check.sh b/.github/scripts/tempo-check.sh index b730c466bde55..3caea992cfe7e 100644 --- a/.github/scripts/tempo-check.sh +++ b/.github/scripts/tempo-check.sh @@ -445,7 +445,7 @@ echo -e "\n=== CAST SEND WITH SPONSOR (--tempo.sponsor-signature) ===" # Test sponsored transactions using pre-signed signature. # Step 1: Get the fee_payer_signature_hash using --tempo.print-sponsor-hash # Step 2: Sign it with the sponsor's private key -# Step 3: Send with --tempo.sponsor-signature +# Step 3: Send with --tempo.sponsor and --tempo.sponsor-signature # Step 1: Get the hash that the sponsor needs to sign FEE_PAYER_HASH=$(cast mktx ${FEE_TOKEN_ARG[@]+"${FEE_TOKEN_ARG[@]}"} --rpc-url "$ETH_RPC_URL" \ @@ -460,7 +460,7 @@ printf "Sponsor signature: %s\n" "$SPONSOR_SIG" # Step 3: Send the sponsored transaction with the signature RECEIPT=$(cast send ${FEE_TOKEN_ARG[@]+"${FEE_TOKEN_ARG[@]}"} --rpc-url "$ETH_RPC_URL" \ 0x86A2EE8FAf9A840F7a2c64CA3d51209F9A02081D 'increment()' --private-key "$PK" \ - --tempo.sponsor-signature "$SPONSOR_SIG" --json) + --tempo.sponsor "$SPONSOR_ADDR" --tempo.sponsor-signature "$SPONSOR_SIG" --json) # Verify the fee_payer in the receipt matches the sponsor address RECEIPT_FEE_PAYER=$(echo "$RECEIPT" | jq -r '.feePayer // .fee_payer // empty') @@ -897,3 +897,85 @@ check_has_code "Nonce" "0x4e4F4E4345000000000000000000000000000000" check_has_code "AccountKeychain" "0xaAAAaaAA00000000000000000000000000000000" echo -e "\n=== CHISEL FORK TESTS COMPLETE ===" + +# --- cast virtual-address (TIP-1022) tests --- + +echo -e "\n=== CAST VIRTUAL-ADDRESS: SETUP MASTER WALLET ===" +vaddr_master_json="$(cast wallet new --json)" +VADDR_MASTER_ADDR="$(jq -r '.[0].address' <<<"$vaddr_master_json")" +VADDR_MASTER_PK="$(jq -r '.[0].private_key' <<<"$vaddr_master_json")" +printf "Master address: %s\n" "$VADDR_MASTER_ADDR" +fund_and_wait "$VADDR_MASTER_ADDR" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: CREATE (mine + register) ===" +# Use the `vaddr` alias to also exercise it. +VADDR_CREATE_OUT=$(cast vaddr create \ +--owner "$VADDR_MASTER_ADDR" \ +--private-key "$VADDR_MASTER_PK" \ +--rpc-url "$ETH_RPC_URL") +echo "$VADDR_CREATE_OUT" +VADDR=$(echo "$VADDR_CREATE_OUT" | sed -n 's/^ tag=0x000000000000 \(0x[a-fA-F0-9]\{40\}\).*/\1/p' | head -1) +if [[ -z "$VADDR" ]]; then +echo "ERROR: failed to parse virtual address from create output" +exit 1 +fi +echo "Virtual address: $VADDR" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: RESOLVE ===" +VADDR_RESOLVE_OUT=$(cast virtual-address resolve "$VADDR" --rpc-url "$ETH_RPC_URL") +echo "$VADDR_RESOLVE_OUT" +RESOLVED_MASTER=$(echo "$VADDR_RESOLVE_OUT" | sed -n 's/^Master address: \(0x[a-fA-F0-9]\{40\}\).*/\1/p') +RESOLVED_LOWER=$(echo "$RESOLVED_MASTER" | tr '[:upper:]' '[:lower:]') +EXPECTED_LOWER=$(echo "$VADDR_MASTER_ADDR" | tr '[:upper:]' '[:lower:]') +if [[ "$RESOLVED_LOWER" != "$EXPECTED_LOWER" ]]; then +echo "ERROR: resolve returned master $RESOLVED_MASTER, expected $VADDR_MASTER_ADDR" +exit 1 +fi +echo "OK: resolve returned the registered master" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: AUTO-FORWARD TO MASTER ===" +# Create a separate sender, fund it, and transfer the fee token to the +# virtual address. The protocol must auto-forward to the master wallet. +vaddr_sender_json="$(cast wallet new --json)" +VADDR_SENDER_ADDR="$(jq -r '.[0].address' <<<"$vaddr_sender_json")" +VADDR_SENDER_PK="$(jq -r '.[0].private_key' <<<"$vaddr_sender_json")" +fund_and_wait "$VADDR_SENDER_ADDR" + +BAL_BEFORE=$(cast call --rpc-url "$ETH_RPC_URL" "$FEE_TOKEN" 'balanceOf(address)(uint256)' "$VADDR_MASTER_ADDR" | awk '{print $1}') +echo "Master balance before: $BAL_BEFORE" + +# Capture the current block before the transfer so `cast vaddr watch` can +# replay the Transfer log via --from-block. +BLOCK_BEFORE_TRANSFER=$(cast block-number --rpc-url "$ETH_RPC_URL") + +AMOUNT=1000000 +cast send "$FEE_TOKEN" 'transfer(address,uint256)' "$VADDR" "$AMOUNT" \ +--rpc-url "$ETH_RPC_URL" --private-key "$VADDR_SENDER_PK" + +BAL_AFTER=$(cast call --rpc-url "$ETH_RPC_URL" "$FEE_TOKEN" 'balanceOf(address)(uint256)' "$VADDR_MASTER_ADDR" | awk '{print $1}') +echo "Master balance after: $BAL_AFTER" + +EXPECTED=$((BAL_BEFORE + AMOUNT)) +if [[ "$BAL_AFTER" != "$EXPECTED" ]]; then +echo "ERROR: master balance grew by $((BAL_AFTER - BAL_BEFORE)), expected $AMOUNT" +exit 1 +fi +echo "OK: transfer to virtual address auto-forwarded to master" + +echo -e "\n=== CAST VIRTUAL-ADDRESS: WATCH ===" +# Tail incoming TIP-20 transfers to the virtual address. `cast vaddr watch` +# polls indefinitely, so we cap it with `timeout`; the historical fetch via +# --from-block emits the prior Transfer log immediately at startup. +WATCH_OUT=$(timeout 5 cast vaddr watch "$VADDR" \ + --token "$FEE_TOKEN" \ + --from-block "$BLOCK_BEFORE_TRANSFER" \ + --rpc-url "$ETH_RPC_URL" 2>&1 || true) +echo "$WATCH_OUT" + +EXPECTED_PATTERN="token=$FEE_TOKEN from=$VADDR_SENDER_ADDR amount=$AMOUNT" +echo "Expected pattern: $EXPECTED_PATTERN" +if ! echo "$WATCH_OUT" | grep -iqF "$EXPECTED_PATTERN"; then + echo "ERROR: cast vaddr watch output did not contain expected '$EXPECTED_PATTERN'" + exit 1 +fi +echo "OK: cast vaddr watch reported correct token/from/amount" diff --git a/.github/workflows/benchmarks-nightly.yml b/.github/workflows/benchmarks-nightly.yml new file mode 100644 index 0000000000000..8569f52ce3b93 --- /dev/null +++ b/.github/workflows/benchmarks-nightly.yml @@ -0,0 +1,217 @@ +name: Nightly Benchmarks (AAVE v4) + +permissions: {} + +on: + schedule: + - cron: "0 2 * * *" # 2am UTC nightly + workflow_dispatch: # allow manual triggering for testing + +env: + AAVE_V4_REPO: "aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35" + RUSTC_WRAPPER: "sccache" + +jobs: + run-benchmarks: + name: Run Nightly Benchmarks + runs-on: depot-ubuntu-24.04-32 + permissions: + contents: read + actions: read # needed to download artifacts from previous runs + outputs: + has_regression: ${{ steps.compare.outputs.has_regression }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential pkg-config + + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 + + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + + - name: Setup Foundry + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + ./.github/scripts/setup-foundryup.sh + printf '%s\n' "$GITHUB_WORKSPACE/.foundry/bin" >> "$GITHUB_PATH" + + - name: Build benchmark binary + run: cargo build --locked --release --bin foundry-bench + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "24" + + - name: Install hyperfine + run: | + curl -L https://github.com/sharkdp/hyperfine/releases/download/v1.19.0/hyperfine-v1.19.0-x86_64-unknown-linux-gnu.tar.gz | tar xz + sudo mv hyperfine-v1.19.0-x86_64-unknown-linux-gnu/hyperfine /usr/local/bin/ + rm -rf hyperfine-v1.19.0-x86_64-unknown-linux-gnu + + - name: Download previous benchmark results + env: + GH_TOKEN: ${{ github.token }} + run: | + mkdir -p prev-results + PREV_RUN_ID=$(gh run list \ + --workflow=benchmarks-nightly.yml \ + --status=success \ + --limit=1 \ + --json databaseId \ + -q '.[0].databaseId // empty' 2>/dev/null || true) + if [[ -n "$PREV_RUN_ID" ]]; then + echo "Downloading results from previous run $PREV_RUN_ID" + gh run download "$PREV_RUN_ID" \ + --name nightly-bench-results \ + --dir prev-results/ || true + else + echo "No previous successful run found, skipping download." + fi + + - name: Run forge test benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches --force-install \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_test \ + --json-output "nightly-${DATE}-forge_test.json" \ + --verbose + + - name: Run forge fuzz test benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_fuzz_test \ + --json-output "nightly-${DATE}-forge_fuzz_test.json" \ + --verbose + + - name: Run forge isolate test benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_isolate_test \ + --json-output "nightly-${DATE}-forge_isolate_test.json" \ + --verbose + + - name: Run forge build benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_build_no_cache,forge_build_with_cache \ + --json-output "nightly-${DATE}-forge_build.json" \ + --verbose + + - name: Run forge coverage benchmarks + continue-on-error: true + env: + FOUNDRY_DIR: ${{ github.workspace }}/.foundry + run: | + DATE=$(date -u +%Y-%m-%d) + ./target/release/foundry-bench --output-dir ./benches \ + --versions nightly \ + --repos "$AAVE_V4_REPO" \ + --benchmarks forge_coverage \ + --json-output "nightly-${DATE}-forge_coverage.json" \ + --verbose + + - name: Merge benchmark JSON results + run: | + DATE=$(date -u +%Y-%m-%d) + shopt -s nullglob + parts=( benches/nightly-${DATE}-*.json ) + if [[ ${#parts[@]} -eq 0 ]]; then + echo "No benchmark results produced — all steps failed." + exit 1 + fi + jq -s 'add' "${parts[@]}" > "benches/nightly-${DATE}.json" + echo "Merged ${#parts[@]} result file(s) into nightly-${DATE}.json" + + - name: Compare with previous results + id: compare + run: | + DATE=$(date -u +%Y-%m-%d) + PREV_JSON=$(ls prev-results/nightly-*.json 2>/dev/null | head -1 || true) + TODAY_JSON="benches/nightly-${DATE}.json" + if ./.github/scripts/compare-nightly.sh "$PREV_JSON" "$TODAY_JSON" > regression.md 2>&1; then + echo "has_regression=false" >> "$GITHUB_OUTPUT" + else + echo "has_regression=true" >> "$GITHUB_OUTPUT" + fi + cat regression.md + + - name: Upload benchmark results + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: nightly-bench-results + retention-days: 7 + path: | + benches/nightly-*.json + regression.md + + report-regression: + name: Report Regression + needs: run-benchmarks + if: needs.run-benchmarks.outputs.has_regression == 'true' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Download benchmark results + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: nightly-bench-results + path: results/ + + - name: Open regression issue + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + DATE=$(date -u +%Y-%m-%d) + BODY="$(cat results/regression.md) + + --- + + **Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + **Date**: ${DATE} + **Repo benchmarked**: \`aave/aave-v4\` (pinned commit) + **Threshold**: 🔴 >=3% regression, 🟡 >=1% warning" + + gh issue create \ + --title "[Nightly Regression] ${DATE}" \ + --body "$BODY" \ + --label "regression" \ + --repo "$GH_REPO" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 5d4767ad3b554..a136703abc294 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -10,17 +10,17 @@ on: required: false type: string versions: - description: "Comma-separated list of Foundry versions to benchmark (e.g., stable,nightly,v1.0.0)" + description: "Comma-separated list of Foundry versions to benchmark (optional, defaults to 'v1.5.1,v1.7.0')" required: false type: string - default: "stable,nightly" repos: - description: "Comma-separated repos to benchmark. Each entry: org/repo[:rev][ ] (e.g. vectorized/solady:v0.1.26 --nmc BrokenTest). Leave empty to use the per-benchmark default repo lists." + description: "Comma-separated repos to benchmark. Each entry: org/repo[:rev] (e.g. aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35). Leave empty to use the per-benchmark default repo lists." required: false type: string - default: "" env: + DEFAULT_VERSIONS: "v1.5.1,v1.7.0" + # Repos to benchmark per step. Each comma-separated entry has the form # org/repo[:rev][ ] # where anything after the first whitespace is appended to every benchmark @@ -29,27 +29,23 @@ env: TEST_REPOS: >- ithacaxyz/account:v0.5.7, vectorized/solady:v0.1.26 --nmc 'LifebuoyTest|LibBitTest|Base58Test', - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc 'TickMathTestTest', sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting ISOLATE_TEST_REPOS: >- ithacaxyz/account:v0.5.7 --nmc SimulateExecuteTest, - vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test', - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, + vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test|LibStringTest', uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc 'TickMathTestTest', sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting BUILD_REPOS: >- ithacaxyz/account:v0.5.7, vectorized/solady:v0.1.26, - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75, sparkdotfi/spark-psm:v1.0.0 COVERAGE_REPOS: >- ithacaxyz/account:v0.5.7, - aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35, uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75, sparkdotfi/spark-psm:v1.0.0 @@ -60,7 +56,7 @@ jobs: name: Run All Benchmarks runs-on: depot-ubuntu-24.04-32 permissions: - contents: write + contents: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -93,7 +89,7 @@ jobs: run: cargo build --locked --release --bin foundry-bench - name: Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" @@ -106,59 +102,61 @@ jobs: - name: Run forge test benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.TEST_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_test,forge_fuzz_test \ - --output-file forge_test_bench.md + --output-file forge_test_bench.md \ + --verbose - name: Run forge isolate test benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.ISOLATE_TEST_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_isolate_test \ - --output-file forge_isolate_test_bench.md + --output-file forge_isolate_test_bench.md \ + --verbose - name: Run forge build benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.BUILD_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_build_no_cache,forge_build_with_cache \ - --output-file forge_build_bench.md + --output-file forge_build_bench.md \ + --verbose - name: Run forge coverage benchmarks env: FOUNDRY_DIR: ${{ github.workspace }}/.foundry - VERSIONS: ${{ github.event.inputs.versions || 'stable,nightly' }} + VERSIONS: ${{ github.event.inputs.versions || env.DEFAULT_VERSIONS }} REPOS: ${{ github.event.inputs.repos || env.COVERAGE_REPOS }} run: | ./target/release/foundry-bench --output-dir ./benches --force-install \ --versions "$VERSIONS" \ --repos "$REPOS" \ --benchmarks forge_coverage \ - --output-file forge_coverage_bench.md + --output-file forge_coverage_bench.md \ + --verbose - name: Combine benchmark results run: ./.github/scripts/combine-benchmarks.sh benches - - name: Commit and read benchmark results + - name: Read benchmark results id: benchmark_results - env: - GITHUB_HEAD_REF: ${{ github.head_ref }} - run: ./.github/scripts/commit-and-read-benchmarks.sh benches "${{ github.event_name }}" "${{ github.repository }}" + run: ./.github/scripts/read-benchmark-results.sh benches - name: Upload benchmark results as artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -172,21 +170,21 @@ jobs: benches/LATEST.md outputs: - branch_name: ${{ steps.benchmark_results.outputs.branch_name }} pr_comment: ${{ steps.benchmark_results.outputs.pr_comment }} publish-results: name: Publish Results needs: run-benchmarks runs-on: ubuntu-latest + # All git writes happen here, on a clean ubuntu-latest runner that has + # never executed third-party benchmark code. permissions: contents: write pull-requests: write steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + # persist-credentials defaults to true so we can push. - name: Download benchmark results uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -194,19 +192,22 @@ jobs: name: benchmark-results path: benches/ - - name: Push branch for manual runs - if: github.event_name == 'workflow_dispatch' - run: | - git push origin "${{ needs.run-benchmarks.outputs.branch_name }}" - echo "Pushed branch: ${{ needs.run-benchmarks.outputs.branch_name }}" + - name: Commit benchmark results + id: commit_results + env: + GITHUB_HEAD_REF: ${{ github.head_ref }} + run: ./.github/scripts/commit-benchmark-results.sh benches "${{ github.event_name }}" "${{ github.repository }}" - name: Create PR for manual runs - if: github.event_name == 'workflow_dispatch' + if: github.event_name == 'workflow_dispatch' && steps.commit_results.outputs.committed == 'true' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + BRANCH_NAME: ${{ steps.commit_results.outputs.branch_name }} + PR_COMMENT: ${{ needs.run-benchmarks.outputs.pr_comment }} with: script: | - const branchName = '${{ needs.run-benchmarks.outputs.branch_name }}'; - const prComment = `${{ needs.run-benchmarks.outputs.pr_comment }}`; + const branchName = process.env.BRANCH_NAME; + const prComment = process.env.PR_COMMENT; // Create the pull request const { data: pr } = await github.rest.pulls.create({ @@ -231,10 +232,12 @@ jobs: - name: Comment on PR if: github.event.inputs.pr_number != '' || github.event_name == 'pull_request' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + PR_COMMENT: ${{ needs.run-benchmarks.outputs.pr_comment }} with: script: | const prNumber = ${{ github.event.inputs.pr_number || github.event.pull_request.number }}; - const prComment = `${{ needs.run-benchmarks.outputs.pr_comment }}`; + const prComment = process.env.PR_COMMENT; const comment = `${prComment} diff --git a/.github/workflows/ci-tempo.yml b/.github/workflows/ci-tempo.yml index b2be5a5312a10..1ef4c760f324e 100644 --- a/.github/workflows/ci-tempo.yml +++ b/.github/workflows/ci-tempo.yml @@ -6,6 +6,8 @@ on: push: branches: [master] pull_request: + schedule: + - cron: "0 2 * * *" # Run daily at 2 AM UTC (offset from other nightlies) workflow_dispatch: inputs: network: @@ -67,36 +69,28 @@ jobs: run: | cargo test --locked -p foundry-common --lib tempo::tests::test_fork_schedule_parses_configured_rpcs -- --exact --nocapture - # TODO: re-enable once devnet is up and stable - # - name: Run Tempo check on devnet - # if: | - # github.event_name == 'pull_request' || - # github.event.inputs.network == 'devnet' || - # github.event.inputs.network == 'all' - # env: - # ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} - # SCRIPTS: ${{ github.event.inputs.scripts || 'both' }} - # run: | - # if [ "$SCRIPTS" = "check" ] || [ "$SCRIPTS" = "both" ]; then - # ./.github/scripts/tempo-check.sh - # fi - # if [ "$SCRIPTS" = "deploy" ] || [ "$SCRIPTS" = "both" ]; then - # ./.github/scripts/tempo-deploy.sh - # fi - - # TODO: re-enable once devnet is up and stable - # - name: Run Tempo wallet tests on devnet - # if: | - # github.event_name == 'pull_request' || - # github.event.inputs.network == 'devnet' || - # github.event.inputs.network == 'all' - # env: - # ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} - # run: ./.github/scripts/tempo-wallet.sh + - name: Run Tempo check on mainnet + if: | + github.event_name == 'schedule' || + github.event.inputs.network == 'mainnet' || + github.event.inputs.network == 'all' + env: + ETH_RPC_URL: ${{ secrets.TEMPO_MAINNET_RPC_URL }} + TEMPO_FEE_TOKEN: "0x20c0000000000000000000000000000000000000" + VERIFIER_URL: ${{ secrets.VERIFIER_URL }} + PRIVATE_KEY: ${{ secrets.THROW_AWAY_MAINNET_PKEY }} + SCRIPTS: ${{ github.event.inputs.scripts || 'both' }} + run: | + if [ "$SCRIPTS" = "check" ] || [ "$SCRIPTS" = "both" ]; then + ./.github/scripts/tempo-check.sh + fi + if [ "$SCRIPTS" = "deploy" ] || [ "$SCRIPTS" = "both" ]; then + ./.github/scripts/tempo-deploy.sh + fi - name: Run Tempo check on testnet if: | - github.event_name == 'pull_request' || + github.event_name == 'schedule' || github.event.inputs.network == 'testnet' || github.event.inputs.network == 'all' env: @@ -111,15 +105,14 @@ jobs: ./.github/scripts/tempo-deploy.sh fi - - name: Run Tempo check on mainnet + - name: Run Tempo check on devnet if: | - github.event.inputs.network == 'mainnet' || + github.event_name == 'push' || + github.event_name == 'pull_request' || + github.event.inputs.network == 'devnet' || github.event.inputs.network == 'all' env: - ETH_RPC_URL: ${{ secrets.TEMPO_MAINNET_RPC_URL }} - TEMPO_FEE_TOKEN: "0x20c0000000000000000000000000000000000000" - VERIFIER_URL: ${{ secrets.VERIFIER_URL }} - PRIVATE_KEY: ${{ secrets.THROW_AWAY_MAINNET_PKEY }} + ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} SCRIPTS: ${{ github.event.inputs.scripts || 'both' }} run: | if [ "$SCRIPTS" = "check" ] || [ "$SCRIPTS" = "both" ]; then @@ -128,3 +121,35 @@ jobs: if [ "$SCRIPTS" = "deploy" ] || [ "$SCRIPTS" = "both" ]; then ./.github/scripts/tempo-deploy.sh fi + + - name: Run Tempo wallet tests on devnet + if: | + github.event_name == 'push' || + github.event_name == 'pull_request' || + github.event.inputs.network == 'devnet' || + github.event.inputs.network == 'all' + env: + ETH_RPC_URL: ${{ secrets.TEMPO_DEVNET_RPC_URL }} + run: ./.github/scripts/tempo-wallet.sh + + # If the nightly run fails, this will create an issue to signal so. + issue: + name: Open an issue + runs-on: ubuntu-latest + needs: [tempo-check] + if: failure() && github.event_name == 'schedule' + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_URL: | + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + update_existing: true + filename: .github/TEMPO_NIGHTLY_FAILURE_TEMPLATE.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb90a76cdbfd..9b63b3e01b4b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,23 @@ jobs: - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 - run: cargo test --workspace --doc --locked + no-default-features: + runs-on: depot-ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # master + with: + toolchain: stable + - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 + - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + - run: cargo build --workspace --no-default-features --locked + typos: runs-on: depot-ubuntu-latest timeout-minutes: 30 @@ -171,6 +188,7 @@ jobs: - test - docs - doctest + - no-default-features - typos - clippy - rustfmt diff --git a/.github/workflows/crate-checks.yml b/.github/workflows/crate-checks.yml index eb865bddc10e3..f0d460da6fbb1 100644 --- a/.github/workflows/crate-checks.yml +++ b/.github/workflows/crate-checks.yml @@ -29,7 +29,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: cargo-hack - uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b97a99d5310a4..6120735657ee6 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -32,6 +32,8 @@ jobs: name: build and push runs-on: depot-ubuntu-latest permissions: + attestations: write + artifact-metadata: write contents: read id-token: write packages: write @@ -92,6 +94,7 @@ jobs: uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1 - name: Build and push Foundry image + id: build uses: depot/build-push-action@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0 with: build-args: | @@ -106,3 +109,30 @@ jobs: platforms: linux/amd64,linux/arm64 push: true no-cache: true + sbom: true + provenance: mode=max + + - name: Install cosign + uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + + - name: Sign image with cosign (keyless) + env: + DOCKER_TAGS: ${{ steps.docker_tagging.outputs.docker_tags }} + DIGEST: ${{ steps.build.outputs.digest }} + shell: bash + run: | + set -euo pipefail + IFS=',' read -r -a tags <<< "$DOCKER_TAGS" + for tag in "${tags[@]}"; do + # Strip any tag suffix and pin to immutable digest, then sign. + ref="${tag%%:*}@${DIGEST}" + printf 'Signing %s\n' "$ref" + cosign sign --yes "$ref" + done + + - name: Image build provenance attestation + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.build.outputs.digest }} + push-to-registry: true diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index f6e068eeec62a..258c86e9e72c2 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,7 +19,7 @@ jobs: contents: write pull-requests: write steps: - - uses: DeterminateSystems/determinate-nix-action@32cb6a5ae30bb0dfc996fa7baf8bf1ed28442fa4 # v3.17.3 + - uses: DeterminateSystems/determinate-nix-action@2be1df9ed6cfd12d52bfbba7af79472420fa5299 # v3.18.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -38,7 +38,7 @@ jobs: permissions: contents: read steps: - - uses: DeterminateSystems/determinate-nix-action@32cb6a5ae30bb0dfc996fa7baf8bf1ed28442fa4 # v3.17.3 + - uses: DeterminateSystems/determinate-nix-action@2be1df9ed6cfd12d52bfbba7af79472420fa5299 # v3.18.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index 2f990e2f9da92..7e849f31c34fa 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -153,13 +153,43 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id || inputs.run_id }} + - name: Validate Downloaded Artifacts + env: + ARTIFACT_DIR: ${{ steps.paths.outputs.artifact_dir }} + run: | + set -euo pipefail + + echo "Validating artifacts in: $ARTIFACT_DIR" + + if [[ ! -d "$ARTIFACT_DIR" ]]; then + echo "ERROR: Artifact directory does not exist: $ARTIFACT_DIR" >&2 + exit 1 + fi + + if ! find "$ARTIFACT_DIR" -mindepth 1 -print -quit | grep -q .; then + echo "ERROR: Artifact directory is empty: $ARTIFACT_DIR" >&2 + exit 1 + fi + + # Reject files with suspicious paths (absolute paths or parent directory traversals) + # Use null-delimited paths to safely handle filenames with newlines or whitespace + while IFS= read -r -d '' path; do + rel="${path#"$ARTIFACT_DIR"/}" + if [[ "$rel" == /* ]] || [[ "$rel" == *".."* ]]; then + echo "ERROR: Suspicious artifact path detected: $rel" >&2 + exit 1 + fi + done < <(find "$ARTIFACT_DIR" -type f -print0) + + echo "Artifact validation completed successfully." + - name: Setup Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version: latest - name: Setup Node (for npm publish auth) - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" @@ -275,7 +305,7 @@ jobs: bun-version: latest - name: Setup Node (for npm publish auth) - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 682c9214284f6..38fa791fb655f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: - name: Build changelog id: build_changelog - uses: mikepenz/release-changelog-builder-action@bcae7115752d4ed746ff92feb666574428a79415 # v6.2 + uses: mikepenz/release-changelog-builder-action@bcae7115752d4ed746ff92feb666574428a79415 # v6.2.1 with: configuration: "./.github/changelog.json" fromTag: ${{ steps.release_info.outputs.from_tag || '' }} @@ -117,6 +117,8 @@ jobs: needs: prepare uses: ./.github/workflows/docker-publish.yml permissions: + attestations: write + artifact-metadata: write contents: read id-token: write packages: write @@ -129,9 +131,10 @@ jobs: # way, GitHub's immutable-releases setting seals the release at publish. release: permissions: - id-token: write - contents: write attestations: write + artifact-metadata: write + contents: write + id-token: write name: release ${{ matrix.target }} (${{ matrix.runner }}) runs-on: ${{ matrix.runner }} timeout-minutes: 240 @@ -264,6 +267,38 @@ jobs: printf "file_name=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> "$GITHUB_OUTPUT" fi printf "foundry_attestation=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.attestation.txt" >> "$GITHUB_OUTPUT" + printf "foundry_sbom=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.spdx.json" >> "$GITHUB_OUTPUT" + printf "foundry_checksum=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.sha256" >> "$GITHUB_OUTPUT" + printf "foundry_signature=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.sigstore.json" >> "$GITHUB_OUTPUT" + + - name: Generate archive checksum + env: + FILE_NAME: ${{ steps.artifacts.outputs.file_name }} + FOUNDRY_CHECKSUM: ${{ steps.artifacts.outputs.foundry_checksum }} + shell: bash + run: | + set -euo pipefail + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$FILE_NAME" > "$FOUNDRY_CHECKSUM" + else + shasum -a 256 "$FILE_NAME" > "$FOUNDRY_CHECKSUM" + fi + cat "$FOUNDRY_CHECKSUM" + + - name: Install Syft + uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + + - name: Generate SBOM (SPDX) + env: + FOUNDRY_SBOM: ${{ steps.artifacts.outputs.foundry_sbom }} + VERSION_NAME: ${{ (env.IS_NIGHTLY == 'true' && 'nightly') || needs.prepare.outputs.tag_name }} + shell: bash + run: | + set -euo pipefail + syft scan dir:. \ + --source-name foundry \ + --source-version "$VERSION_NAME" \ + -o spdx-json="$FOUNDRY_SBOM" - name: Upload build artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -292,15 +327,37 @@ jobs: tar -czvf "foundry_man_${VERSION_NAME}.tar.gz" forge.1.gz cast.1.gz anvil.1.gz chisel.1.gz printf 'foundry_man=%s\n' "foundry_man_${VERSION_NAME}.tar.gz" >> "$GITHUB_OUTPUT" - - name: Binaries attestation + - name: Binaries and archive provenance attestation id: attestation - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 with: subject-path: | ${{ env.anvil_bin_path }} ${{ env.cast_bin_path }} ${{ env.chisel_bin_path }} ${{ env.forge_bin_path }} + ${{ steps.artifacts.outputs.file_name }} + + - name: Archive SBOM attestation + uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 + with: + subject-path: ${{ steps.artifacts.outputs.file_name }} + sbom-path: ${{ steps.artifacts.outputs.foundry_sbom }} + + - name: Install cosign + uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + + - name: Sign archive with cosign (keyless) + env: + FILE_NAME: ${{ steps.artifacts.outputs.file_name }} + FOUNDRY_SIGNATURE: ${{ steps.artifacts.outputs.foundry_signature }} + shell: bash + run: | + set -euo pipefail + cosign sign-blob \ + --yes \ + --bundle "$FOUNDRY_SIGNATURE" \ + "$FILE_NAME" - name: Record attestation URL env: @@ -321,11 +378,20 @@ jobs: TAG_NAME: ${{ needs.prepare.outputs.tag_name }} FILE_NAME: ${{ steps.artifacts.outputs.file_name }} FOUNDRY_ATTESTATION: ${{ steps.artifacts.outputs.foundry_attestation }} + FOUNDRY_SBOM: ${{ steps.artifacts.outputs.foundry_sbom }} + FOUNDRY_CHECKSUM: ${{ steps.artifacts.outputs.foundry_checksum }} + FOUNDRY_SIGNATURE: ${{ steps.artifacts.outputs.foundry_signature }} FOUNDRY_MAN: ${{ steps.man.outputs.foundry_man }} shell: bash run: | set -euo pipefail - files=("$FILE_NAME" "$FOUNDRY_ATTESTATION") + files=( + "$FILE_NAME" + "$FOUNDRY_ATTESTATION" + "$FOUNDRY_SBOM" + "$FOUNDRY_CHECKSUM" + "$FOUNDRY_SIGNATURE" + ) if [[ -n "${FOUNDRY_MAN:-}" ]]; then files+=("$FOUNDRY_MAN") fi diff --git a/.github/workflows/test-flaky.yml b/.github/workflows/test-flaky.yml index d6244f826887e..9caa254f05c10 100644 --- a/.github/workflows/test-flaky.yml +++ b/.github/workflows/test-flaky.yml @@ -33,7 +33,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: nextest - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/test-isolate.yml b/.github/workflows/test-isolate.yml index 141e3a049a73b..6763f1f80bde3 100644 --- a/.github/workflows/test-isolate.yml +++ b/.github/workflows/test-isolate.yml @@ -37,7 +37,7 @@ jobs: with: toolchain: stable - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: nextest - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eaa46c8e79f7c..daa4822b6e395 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,14 +73,14 @@ jobs: toolchain: stable target: ${{ matrix.target }} - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 - - uses: taiki-e/install-action@58e862542551f667fa44c8a2a4a1d64ad477c96a # v2.75.17 + - uses: taiki-e/install-action@5f57d6cb7cd20b14a8a27f522884c4bc8a187458 # v2.75.19 with: tool: nextest # External tests dependencies - name: Setup Node.js if: contains(matrix.name, 'external') - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 - name: Install Bun diff --git a/Cargo.lock b/Cargo.lock index 6996d21b881d9..9db38f1967f4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,21 +77,21 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a547705d5c1b42575a0542bae2ba45bc62a6154be86611afaef1c0ab5c38598e" +checksum = "d8010fc7e9e8643ef4e758cdccf3eef26734594aedf88a9d5ed35e51837d42ef" dependencies = [ "alloy-consensus", "alloy-contract", "alloy-core", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-genesis", "alloy-network", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -116,14 +116,14 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8c24c95e90c1608c2d91cff1b451d796474168d3310ccc8b7cd12502ca8169" +checksum = "e3d64da86c616b5092ea64eea648f311bbd58630a0b384c42d699175d6f9122b" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-trie", "alloy-tx-macros", "auto_impl", @@ -143,23 +143,23 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d211ad0ef468a70a7a829e49683ff59ad25f02b4ab3764344c4c2663329a52c" +checksum = "8fd98696ca3617d3a9ba1a6f2011880cbfd5618228dab6400c9f8bca457859a8" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-contract" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59d55233ac14aa7fa6bcdcad45ba305e90c556065e0947cd9f243c4469e7c2d" +checksum = "de3df0aadc569a8b277808a7d0ad0e421180654ea36a3c59e9ed2bb968c9a1cd" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -238,12 +238,12 @@ dependencies = [ [[package]] name = "alloy-eip5792" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250ba1168b8a049185a68c4dfa7f2a6a4046bd26fcc8c68632caeb216a5e12dc" +checksum = "1ceb16e7fe5a95825305f218ccd356665f848831f94ce2bbf55339bf5d21e88a" dependencies = [ "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", "serde_json", ] @@ -265,13 +265,14 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +checksum = "ec6ae911a2fc304a7cb80a79fb7bed6d1474aed4e7c203df1f8ff538f64fc78d" dependencies = [ "alloy-primitives", "alloy-rlp", "borsh", + "once_cell", "serde", ] @@ -300,9 +301,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae69eaa5096b47ffe97e6a5d6bde7e7fa2dec106af22a9315621d11039c3de3c" +checksum = "64c0456f5f7a4497e9342d20f528e30f5288ddfa0d6a012bd5044afee46cd8a0" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -310,7 +311,7 @@ dependencies = [ "alloy-eip7928", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "auto_impl", "borsh", "c-kzg", @@ -325,9 +326,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a8c1330ad33c95b5958573bca9a1ad0b419a51d76bb4c521556fbba8539b8d" +checksum = "d5638cbbffb318d440fdb009de019090d8d117dae40de9d10cdb29891ea59eb9" dependencies = [ "alloy-contract", "alloy-primitives", @@ -339,12 +340,12 @@ dependencies = [ [[package]] name = "alloy-evm" -version = "0.33.2" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc4b83cb672156663e6094d098beb509965b7fe684bb3d6e44bb9ca2e9ae714" +checksum = "c1ceeea6dcbbcd4e546b27700763a6f6c3b3fee30054209884f521078b6fda4f" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-hardforks", "alloy-primitives", "alloy-rpc-types-engine", @@ -359,13 +360,13 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39789db0b3f3bbef0e6549c87bc6842b73886ebabee1405b6941685b1cc34083" +checksum = "a71ff8b55d2b8aa05259f474cae7dea0e4991724dc18936b81cb23ec492a0c2a" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-trie", "borsh", "serde", @@ -400,9 +401,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662b525af73e86b2167dae923261c8edf440ba7e1426b30a8b993177bc214c02" +checksum = "19e352478b756bad5d7203148e4b461861282ea2ded3da406ba24868b52cd098" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -415,19 +416,19 @@ dependencies = [ [[package]] name = "alloy-network" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c657c2d9751d3c7d94990554b231e5372c3c2e4bad842806280b6151a0d6a05d" +checksum = "ed08ae169869e08370ed121612e0d3dadac33d1a256e9f2465926b23f0bd7d95" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-any", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-sol-types", "async-trait", @@ -441,24 +442,24 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e7c4bb0ebbd6d7406d2808968f43c0d5186c69c5e58cedcbee7380f4cd1fcf" +checksum = "02e6c7ad28afe348a9a9c5624b67ee5b3607b8de98d5816b3056ecdfa6fa2697" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-op-evm" version = "0.31.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -472,7 +473,7 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" version = "0.4.7" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -513,13 +514,13 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4fea0fc2628cdbc851aaa333124f9d8ab9f567ab8d4c20202819db13aa1a534" +checksum = "93a7c17472b55482d4734154c2f5ed13f72e03f6752cebb927f6a2d8b52e646c" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-json-rpc", "alloy-network", "alloy-network-primitives", @@ -547,7 +548,7 @@ dependencies = [ "lru", "parking_lot", "pin-project", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde", "serde_json", "thiserror 2.0.18", @@ -559,9 +560,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7b42e514613c717887dc77bb58d35e845557ebd63a18c3f92a77094e4891f" +checksum = "a8d86958b02bca85103d64fa60d7b364a8b017c6e40f2b02c3f50ca22964a738" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -603,9 +604,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ee7b51752c68fb95f21705e402700750e692b1d21ccc294ac48fadc8655d53" +checksum = "5beb5c2fe6b960c8e8b038e69fd502a90a2e930afa4770efb748b163b0767729" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -616,7 +617,7 @@ dependencies = [ "alloy-transport-ws", "futures", "pin-project", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde", "serde_json", "tokio", @@ -629,9 +630,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fa76988f54105ad4398828e8aaf1a39b3f07f91fb79091529056689514ee8c2" +checksum = "4ee1257a278f6d293e05c5162c5940a1561b1aa85ded0028b464c81de37ebfa5" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -640,44 +641,44 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-rpc-types-anvil" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d276bea4e92e4991269d31b9abd3e722eed2565b82036478a4416adb8dd4992" +checksum = "df32156f085e74eac942b6103744be49b817c302341aaa8cb0c1c88dc29228d9" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] [[package]] name = "alloy-rpc-types-any" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1a9a3bda9be7f6515316eb792710532411878bbfc88934973f4b371376b00d" +checksum = "6a234bfbdf7a76c3d13808f729af5321852de3dedcaa6fc6d5f54787aaf54c6a" dependencies = [ "alloy-consensus-any", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", "serde_json", ] [[package]] name = "alloy-rpc-types-beacon" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5d68ddca890854fb78291cbde06115473ded00b2337d0f815e92c0c1f8003" +checksum = "296450f5e76bece0116c939b9437b0421a5da9c5d40031bf4cf9b38d3d94e475" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rpc-types-engine", "derive_more", @@ -689,9 +690,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea21739e232c221779741eba7e7b9bc19ad8ff777b72736647ae519f5c9f6f33" +checksum = "0ab075ac1c25bcf697f133b7cd92e2fb26afe213e872ef79fdf77f0d7bcb3793" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -702,15 +703,15 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f05338cfb4ee5508ff76f01c88142cab8a4579db74b7d9432936c26e4f11374" +checksum = "73b12366c96f4013e1aeebc96c6b56e5f33f07853c42ea2f485045c0c157a4a1" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -722,17 +723,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda4ece0050154ab278241aeffade58916b04f38254832e8cb6e4671c6e72ed2" +checksum = "56a282daf869eeb7383d3d5c2deb35b0b3fb45ecb329513af4090fc61245ee18" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-sol-types", "itertools 0.14.0", "serde", @@ -743,13 +744,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5905ac3663b0859d67b82d912acce20887d20682a0cadde79c8a763b133a515" +checksum = "6184b5d14152b68b0bb8beb621339d94f0b761a37958bb365fbf7c00922125c2" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", "serde_json", "thiserror 2.0.18", @@ -757,13 +758,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fbf71892d4df9cae8d35dc96f15d522384bb93806205465e2c8c012b7f0a34" +checksum = "f00b631c361e7c7baaf4f1f5a9877730f3507fed2acb9d4b34841b8184b2ec28" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "serde", ] @@ -780,9 +781,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beaa5c581a67e2743d95b4849eb9cfeb90866429cdaa6d8f6b75eb988b2d0cd9" +checksum = "a0eada2558e921b39dfcead33c487364df9b31374f5733c1c9d2c891c4529933" dependencies = [ "alloy-primitives", "serde", @@ -791,9 +792,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5da9ae50f9b48d7b4e2e5cde87175257be7e5e56909a7794720597c1d9806f6" +checksum = "41eb29f7a8adcd8941fbb8e134022a133e6f8dfd345f2e3b7109599f8a7dca08" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -808,9 +809,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a57d1e72b1f9b11e5e71ebdab0569cb02277a462bbea6793fcaebfcd794ae9" +checksum = "1258987fbc82716b5153ec7bb95a8a295e7640871b8f03d8ec7c4000dc80c215" dependencies = [ "alloy-consensus", "alloy-network", @@ -827,9 +828,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b27f20b5298b76a5a3b7cdbe6bdb184ab1ebd6e120e00dad748867673f5c90" +checksum = "7ffc2a49bca5b73c6964711b57452f6c36a6bcb7f845ab7e9ad05b5a828d0161" dependencies = [ "alloy-consensus", "alloy-network", @@ -845,9 +846,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c7acc40ffbfd37d4113eb619863099f3235d78d044006a1eecb94d8b0b2f1a" +checksum = "94e11ddaddfb98c1ddce737dc440225565b0ae0987ac9ad5e59a85db5904878c" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -865,9 +866,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b794002d57fd2f71b4c87298a41ca24dfc0f2cf6630d95106a477e451747ba" +checksum = "bef839e7ce9b59aa60fa9a175e97986c6145c888d643b0f1fb0a3e7b8e56a2e2" dependencies = [ "alloy-consensus", "alloy-network", @@ -885,9 +886,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a09a865ae9e1f05478429ef0d935b16467f35c6e0b02cb10f23f66a3b33fc3" +checksum = "44eb341d0013784da6a39e5bbdc11b95d6744993b12a1c3fd55df795a850dd42" dependencies = [ "alloy-consensus", "alloy-network", @@ -902,9 +903,9 @@ dependencies = [ [[package]] name = "alloy-signer-turnkey" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bb8218544ab635281f1be180a1cfd9b5d549db686faa7e85b3b2c10969819e" +checksum = "82ff16b4166fb90bbe79bd1e49244824fb3cadc6b8cd11e9c8a002c1f8c07492" dependencies = [ "alloy-consensus", "alloy-network", @@ -991,9 +992,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19dec9bfb59647254afdecbb5ddcddd7ba02edcd48ffa40510bddfbed0be1634" +checksum = "3ac7a80c0bac3e44559d53d002e34c461dc2f23262b42cafec019bc70551abbe" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -1014,14 +1015,14 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2035f3c4d6bee20624da2dcf765d469b292398e48d766ffade61b0fcf8b4d45d" +checksum = "eed3ed3300a998f88639ed619fdbbd88bd82865e00c6a8ecb796c99eb12358f6" dependencies = [ "alloy-json-rpc", "alloy-transport", "itertools 0.14.0", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde_json", "tower", "tracing", @@ -1030,9 +1031,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfad7aa9206fcb831ae401b6a1c893a402b8eed74f9c8ffbb7a7323afb0d9a4c" +checksum = "1075d9d30fd4d71e50000fd4afb19ed2664ceab20c2a29f3889a6e988329e02d" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -1050,9 +1051,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5aa8ff49386df3e008b73c7fb0a5479410e8493fdb86a8b916877a16e8aead9" +checksum = "0e3bff84b2b2a46eb34cc522dc3f889a2867c70be90a377421429b662b3ec4ce" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -1085,9 +1086,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3520337f3d3d063a7fe20f47aaa62d695e3dc0372b34f601560dee24e76988b9" +checksum = "99fce0350197dcd4ba4e9a7dd43915d908c0eb0e7352755791709a705e1c76b6" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -1194,7 +1195,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1218,18 +1219,18 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anvil" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-genesis", "alloy-network", @@ -1241,7 +1242,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-beacon", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -1276,7 +1277,7 @@ dependencies = [ "parking_lot", "rand 0.8.6", "rand 0.9.4", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "revm-inspectors", "serde", @@ -1297,17 +1298,17 @@ dependencies = [ [[package]] name = "anvil-core" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eip5792", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "bytes", "foundry-common", "foundry-evm", @@ -1321,7 +1322,7 @@ dependencies = [ [[package]] name = "anvil-rpc" -version = "1.6.0" +version = "1.7.1" dependencies = [ "serde", "serde_json", @@ -1329,7 +1330,7 @@ dependencies = [ [[package]] name = "anvil-server" -version = "1.6.0" +version = "1.7.1" dependencies = [ "anvil-rpc", "async-trait", @@ -1669,20 +1670,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ascii-canvas" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" -dependencies = [ - "term", -] - [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -1955,9 +1947,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.101.0" +version = "1.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab41ad64e4051ecabeea802d6a17845a91e83287e1dd249e6963ea1ba78c428a" +checksum = "0fc35b7a14cabdad13795fbbbd26d5ddec0882c01492ceedf2af575aad5f37dd" dependencies = [ "aws-credential-types", "aws-runtime", @@ -2172,9 +2164,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.14" +version = "1.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c8323699dd9b3c8d5b3c13051ae9cdef58fd179957c882f8374dd8725962d9" +checksum = "2f4bbcaa9304ea40902d3d5f42a0428d1bd895a2b0f6999436fb279ffddc58ac" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -2361,9 +2353,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.4" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", @@ -2579,7 +2571,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -2745,13 +2737,13 @@ dependencies = [ [[package]] name = "cast" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-ens", "alloy-evm", "alloy-hardforks", @@ -2763,7 +2755,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-beacon", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "alloy-signer-local", "alloy-sol-types", @@ -2822,9 +2814,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.60" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -2832,12 +2824,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" @@ -2876,7 +2862,7 @@ dependencies = [ [[package]] name = "chisel" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -2884,17 +2870,15 @@ dependencies = [ "clap", "dirs", "eyre", - "forge-doc", "forge-fmt", "foundry-cli", "foundry-common", "foundry-compilers", "foundry-config", "foundry-evm", - "foundry-solang-parser", "foundry-test-utils", "itertools 0.14.0", - "reqwest 0.13.2", + "reqwest 0.13.3", "rexpect", "rustyline", "semver 1.0.28", @@ -2998,9 +2982,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.2" +version = "4.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff7a1dccbdd8b078c2bdebff47e404615151534d5043da397ec50286816f9cb" +checksum = "660c0520455b1013b9bcb0393d5f643d7e4454fb69c915b8d6d2aa0e9a45acc3" dependencies = [ "clap", ] @@ -3043,7 +3027,7 @@ dependencies = [ "terminfo", "thiserror 2.0.18", "which", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3196,7 +3180,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3365,9 +3349,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "compression-core", "flate2", @@ -3376,9 +3360,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "concurrent-queue" @@ -3426,9 +3410,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +checksum = "20d9a563d167a9cce0f94153382b33cb6eded6dfabff03c69ad65a28ea1514e0" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -3539,9 +3523,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc-fast" @@ -3822,9 +3806,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "der" @@ -3974,9 +3958,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", "crypto-common 0.2.1", @@ -4000,7 +3984,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4188,15 +4172,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ena" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" -dependencies = [ - "log", -] - [[package]] name = "encode_unicode" version = "1.0.0" @@ -4305,7 +4280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4503,15 +4478,15 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "figment2" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4380ce44915a6227efbb61e3885bc1c8e99fb9820f5db612abfac2c5cfc46871" +checksum = "87d63dee16df12076c7770919713c0b92f4e1c85eac828dc2ade0b6c998f016b" dependencies = [ "atomic", "parking_lot", "serde", "tempfile", - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.11+spec-1.1.0", "uncased", "version_check", ] @@ -4608,7 +4583,7 @@ checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc" [[package]] name = "forge" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4662,7 +4637,7 @@ dependencies = [ "rand 0.9.4", "rayon", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "semver 1.0.28", "serde", @@ -4690,16 +4665,14 @@ dependencies = [ [[package]] name = "forge-doc" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "derive_more", "eyre", - "forge-fmt", "foundry-common", "foundry-compilers", "foundry-config", - "foundry-solang-parser", "itertools 0.14.0", "mdbook-driver", "rayon", @@ -4714,7 +4687,7 @@ dependencies = [ [[package]] name = "forge-fmt" -version = "1.6.0" +version = "1.7.1" dependencies = [ "foundry-common", "foundry-config", @@ -4728,7 +4701,7 @@ dependencies = [ [[package]] name = "forge-lint" -version = "1.6.0" +version = "1.7.1" dependencies = [ "eyre", "foundry-common", @@ -4742,12 +4715,12 @@ dependencies = [ [[package]] name = "forge-script" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-json-abi", "alloy-network", @@ -4790,7 +4763,7 @@ dependencies = [ [[package]] name = "forge-script-sequence" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-network", "alloy-primitives", @@ -4806,7 +4779,7 @@ dependencies = [ [[package]] name = "forge-sol-macro-gen" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -4821,7 +4794,7 @@ dependencies = [ [[package]] name = "forge-verify" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -4844,7 +4817,7 @@ dependencies = [ "futures", "itertools 0.14.0", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "semver 1.0.28", "serde", @@ -4867,7 +4840,7 @@ dependencies = [ [[package]] name = "foundry-bench" -version = "1.6.0" +version = "1.7.1" dependencies = [ "chrono", "clap", @@ -4892,7 +4865,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.13.2", + "reqwest 0.13.3", "semver 1.0.28", "serde", "serde_json", @@ -4902,7 +4875,7 @@ dependencies = [ [[package]] name = "foundry-cheatcodes" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -4955,7 +4928,7 @@ dependencies = [ [[package]] name = "foundry-cheatcodes-spec" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-sol-types", "foundry-macros", @@ -4966,11 +4939,11 @@ dependencies = [ [[package]] name = "foundry-cli" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-ens", "alloy-json-abi", "alloy-network", @@ -5010,6 +4983,7 @@ dependencies = [ "tempo-primitives", "tikv-jemallocator", "tokio", + "toml", "tracing", "tracing-subscriber 0.3.23", "tracing-tracy", @@ -5018,7 +4992,7 @@ dependencies = [ [[package]] name = "foundry-cli-markdown" -version = "1.6.0" +version = "1.7.1" dependencies = [ "clap", "pretty_assertions", @@ -5026,12 +5000,12 @@ dependencies = [ [[package]] name = "foundry-common" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -5043,6 +5017,7 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-engine", "alloy-signer", + "alloy-signer-local", "alloy-sol-types", "alloy-transport", "alloy-transport-ipc", @@ -5050,6 +5025,7 @@ dependencies = [ "anstream 0.6.21", "anstyle", "axum", + "base64 0.22.1", "chrono", "ciborium", "clap", @@ -5067,18 +5043,20 @@ dependencies = [ "futures", "itertools 0.14.0", "jiff", + "k256", "mpp", "num-format", "op-alloy-network", "op-alloy-rpc-types", "path-slash", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "rustls", "semver 1.0.28", "serde", "serde_json", + "sha2 0.10.9", "solar-compiler", "tempfile", "tempo-alloy", @@ -5097,14 +5075,14 @@ dependencies = [ [[package]] name = "foundry-common-fmt" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "chrono", "comfy-table", "eyre", @@ -5220,7 +5198,7 @@ dependencies = [ [[package]] name = "foundry-config" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-primitives", @@ -5260,7 +5238,7 @@ dependencies = [ [[package]] name = "foundry-debugger" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "crossterm", @@ -5278,7 +5256,7 @@ dependencies = [ [[package]] name = "foundry-evm" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -5315,7 +5293,7 @@ dependencies = [ [[package]] name = "foundry-evm-abi" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -5327,7 +5305,7 @@ dependencies = [ [[package]] name = "foundry-evm-core" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-consensus", @@ -5342,7 +5320,7 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-sol-types", "anvil", "auto_impl", @@ -5379,7 +5357,7 @@ dependencies = [ [[package]] name = "foundry-evm-coverage" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "eyre", @@ -5395,7 +5373,7 @@ dependencies = [ [[package]] name = "foundry-evm-fuzz" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -5420,7 +5398,7 @@ dependencies = [ [[package]] name = "foundry-evm-hardforks" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -5435,10 +5413,10 @@ dependencies = [ [[package]] name = "foundry-evm-networks" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-op-hardforks", "alloy-primitives", @@ -5451,11 +5429,11 @@ dependencies = [ [[package]] name = "foundry-evm-sancov" -version = "1.6.0" +version = "1.7.1" [[package]] name = "foundry-evm-traces" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -5473,7 +5451,7 @@ dependencies = [ "itertools 0.14.0", "memchr", "rayon", - "reqwest 0.13.2", + "reqwest 0.13.3", "revm", "revm-inspectors", "serde", @@ -5512,7 +5490,7 @@ dependencies = [ [[package]] name = "foundry-linking" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-primitives", "foundry-compilers", @@ -5523,7 +5501,7 @@ dependencies = [ [[package]] name = "foundry-macros" -version = "1.6.0" +version = "1.7.1" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -5533,7 +5511,7 @@ dependencies = [ [[package]] name = "foundry-primitives" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-consensus", "alloy-evm", @@ -5544,7 +5522,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer", "derive_more", "op-alloy-consensus", @@ -5558,23 +5536,9 @@ dependencies = [ "tempo-revm", ] -[[package]] -name = "foundry-solang-parser" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9645e75b89f977423690f3b4bfd8d84825e5fdabd7803cbce6d4a2c4d54972b4" -dependencies = [ - "itertools 0.14.0", - "lalrpop", - "lalrpop-util", - "phf 0.11.3", - "thiserror 2.0.18", - "unicode-xid", -] - [[package]] name = "foundry-test-utils" -version = "1.6.0" +version = "1.7.1" dependencies = [ "alloy-chains", "alloy-primitives", @@ -5589,7 +5553,7 @@ dependencies = [ "parking_lot", "rand 0.9.4", "regex", - "reqwest 0.13.2", + "reqwest 0.13.3", "serde_json", "snapbox", "svm-rs", @@ -5817,7 +5781,7 @@ dependencies = [ "once_cell", "prost 0.14.3", "prost-types 0.14.3", - "reqwest 0.13.2", + "reqwest 0.13.3", "secret-vault-value", "serde", "serde_json", @@ -6191,9 +6155,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5" dependencies = [ "typenum", ] @@ -6585,9 +6549,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" +checksum = "069323743400cb7ab06a8fe5c1ed911d36b6919ec531661d034c89083629595b" dependencies = [ "doctest-file", "futures-core", @@ -6595,7 +6559,7 @@ dependencies = [ "recvmsg", "tokio", "widestring", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6644,7 +6608,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6697,9 +6661,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "log", @@ -6710,31 +6674,15 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - [[package]] name = "jni" version = "0.22.4" @@ -6744,7 +6692,7 @@ dependencies = [ "cfg-if", "combine", "jni-macros", - "jni-sys 0.4.1", + "jni-sys", "log", "simd_cesu8", "thiserror 2.0.18", @@ -6765,15 +6713,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - [[package]] name = "jni-sys" version = "0.4.1" @@ -6805,9 +6744,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ "cfg-if", "futures-util", @@ -6844,6 +6783,7 @@ version = "10.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" dependencies = [ + "aws-lc-rs", "base64 0.22.1", "getrandom 0.2.17", "js-sys", @@ -6926,45 +6866,14 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +checksum = "a7b65860415f949f23fa882e669f2dbd4a0f0eeb1acdd56790b30494afd7da2f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.11.1", "libc", ] -[[package]] -name = "lalrpop" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools 0.14.0", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax", - "sha3", - "string_cache 0.8.9", - "term", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" -dependencies = [ - "regex-automata", - "rustversion", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -6985,9 +6894,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -6997,12 +6906,11 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libmimalloc-sys" -version = "0.1.44" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" +checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6" dependencies = [ "cc", - "libc", ] [[package]] @@ -7302,12 +7210,12 @@ dependencies = [ [[package]] name = "metrics" -version = "0.24.3" +version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +checksum = "ff56c2e7dce6bd462e3b8919986a617027481b1dcc703175b58cf9dd98a2f071" dependencies = [ - "ahash", "portable-atomic", + "rapidhash", ] [[package]] @@ -7334,9 +7242,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.48" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" +checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640" dependencies = [ "libmimalloc-sys", ] @@ -7591,7 +7499,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7820,7 +7728,7 @@ dependencies = [ [[package]] name = "op-alloy" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "op-alloy-consensus", "op-alloy-network", @@ -7832,15 +7740,15 @@ dependencies = [ [[package]] name = "op-alloy-consensus" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "bytes", "derive_more", "reth-codecs", @@ -7859,7 +7767,7 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc" [[package]] name = "op-alloy-network" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", "alloy-network", @@ -7872,7 +7780,7 @@ dependencies = [ [[package]] name = "op-alloy-provider" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-network", "alloy-primitives", @@ -7886,15 +7794,15 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "derive_more", "op-alloy-consensus", "reth-rpc-traits", @@ -7906,15 +7814,14 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" version = "0.24.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", - "alloy-serde 2.0.1", - "derive_more", + "alloy-serde 2.0.4", "ethereum_ssz", "ethereum_ssz_derive", "op-alloy-consensus", @@ -7927,7 +7834,7 @@ dependencies = [ [[package]] name = "op-revm" version = "19.0.0" -source = "git+https://github.com/ethereum-optimism/optimism?rev=42f5117c2e7de0614cd3b96f274d0a3078f9701c#42f5117c2e7de0614cd3b96f274d0a3078f9701c" +source = "git+https://github.com/ethereum-optimism/optimism?rev=e3b59e76588f99db17205f5601e45a5b00f0bfbb#e3b59e76588f99db17205f5601e45a5b00f0bfbb" dependencies = [ "auto_impl", "revm", @@ -8145,16 +8052,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset", - "indexmap 2.14.0", -] - [[package]] name = "pharos" version = "0.5.3" @@ -8171,7 +8068,6 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", "phf_shared 0.11.3", ] @@ -8181,7 +8077,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros 0.13.1", + "phf_macros", "phf_shared 0.13.1", "serde", ] @@ -8226,19 +8122,6 @@ dependencies = [ "phf_shared 0.13.1", ] -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "phf_macros" version = "0.13.1" @@ -8574,7 +8457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -9094,9 +8977,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64 0.22.1", "bytes", @@ -9138,12 +9021,12 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-genesis", "alloy-primitives", @@ -9158,11 +9041,12 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce542a96bf888f31854803e80b3340bc233927743aa580838014e8a88fe0d66" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -9176,8 +9060,9 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634c90f1cc0f9887680ca785b0b21aa961070b9465917bf65afaec56a6d005bb" dependencies = [ "proc-macro2", "quote", @@ -9186,8 +9071,8 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9199,11 +9084,11 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9212,8 +9097,8 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9236,10 +9121,10 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "bytes", "modular-bitfield", @@ -9250,11 +9135,11 @@ dependencies = [ [[package]] name = "reth-ethereum-consensus" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -9266,8 +9151,8 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-eip2124", "alloy-hardforks", @@ -9279,11 +9164,11 @@ dependencies = [ [[package]] name = "reth-ethereum-primitives" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rpc-types-eth", "reth-codecs", @@ -9293,11 +9178,11 @@ dependencies = [ [[package]] name = "reth-evm" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-primitives", "auto_impl", @@ -9315,11 +9200,11 @@ dependencies = [ [[package]] name = "reth-evm-ethereum" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-primitives", "alloy-rpc-types-engine", @@ -9335,8 +9220,8 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-evm", "alloy-primitives", @@ -9348,11 +9233,11 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-primitives", "alloy-rlp", @@ -9367,8 +9252,8 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9380,11 +9265,12 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee12e304adbacbb32248c9806ebafbe1e2811fbfefe53c5e5b710a8438b7ec0" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9408,8 +9294,8 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "derive_more", @@ -9423,8 +9309,8 @@ dependencies = [ [[package]] name = "reth-revm" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9436,8 +9322,8 @@ dependencies = [ [[package]] name = "reth-rpc-convert" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-evm", @@ -9456,9 +9342,9 @@ dependencies = [ [[package]] name = "reth-rpc-traits" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b766da61ec7c46596386b4bc88d9b57d1939d3da2bc9e927567a8a23650e5ce9" +checksum = "860fe223501a76ff14aa3bf164f739f31008c2a2905ac85708bfd88f042e6151" dependencies = [ "alloy-consensus", "alloy-network", @@ -9471,8 +9357,8 @@ dependencies = [ [[package]] name = "reth-stages-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "bytes", @@ -9484,8 +9370,8 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-primitives", "derive_more", @@ -9498,11 +9384,11 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -9521,10 +9407,10 @@ dependencies = [ [[package]] name = "reth-storage-errors" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-primitives", "alloy-rlp", "derive_more", @@ -9539,14 +9425,14 @@ dependencies = [ [[package]] name = "reth-trie-common" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?rev=7839f3d#7839f3d876b32842b059ca8171242b807ba1fc80" +version = "2.2.0" +source = "git+https://github.com/paradigmxyz/reth?rev=38c627c#38c627ce8f1a3bb82bed8a6beb3016f62c50016d" dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-trie", "arrayvec", "bytes", @@ -9562,8 +9448,9 @@ dependencies = [ [[package]] name = "reth-zstd-compressors" -version = "0.3.0" -source = "git+https://github.com/paradigmxyz/reth-core?rev=6b12498#6b12498871bc1b1d42c6dcf28968c271660de8c0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c12fafa33d2f420a9d39249a3e0357b1928d09429f30758b85280409092873b2" dependencies = [ "zstd", ] @@ -9846,9 +9733,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba9ce64a8f45d7fc86358410bb1a82e8c987504c0d4900e9141d69a9f26c885" +checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9" dependencies = [ "bytemuck", "byteorder", @@ -9856,20 +9743,20 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.4.0" +version = "7.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +checksum = "5ac5b223d9738ef56e0b98305410be40fa0941bf6036c56f1506751e43552d64" dependencies = [ "libc", "rtoolbox", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rtoolbox" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327b72899159dfae8060c51a1f6aebe955245bcd9cc4997eed0f623caea022e4" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" dependencies = [ "libc", "windows-sys 0.59.0", @@ -9877,9 +9764,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" dependencies = [ "alloy-rlp", "arbitrary", @@ -10004,14 +9891,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.38" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", "log", @@ -10037,9 +9924,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -10047,13 +9934,13 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni 0.21.1", + "jni", "log", "once_cell", "rustls", @@ -10063,7 +9950,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10481,9 +10368,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19" dependencies = [ "base64 0.22.1", "chrono", @@ -10500,9 +10387,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "cf2ebbe86054f9b45bc3881e865683ccfaccce97b9b4cb53f3039d67f355a334" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -10563,14 +10450,14 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] name = "sha3" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", "keccak", @@ -10704,9 +10591,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -10777,7 +10664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10845,7 +10732,7 @@ dependencies = [ "derive_more", "dunce", "inturn", - "itertools 0.12.1", + "itertools 0.14.0", "itoa", "normalize-path", "once_map", @@ -10880,7 +10767,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.11.1", "bumpalo", - "itertools 0.12.1", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", @@ -11014,18 +10901,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared 0.11.3", - "precomputed-hash", -] - [[package]] name = "string_cache" version = "0.9.0" @@ -11178,13 +11053,13 @@ dependencies = [ [[package]] name = "svm-rs" -version = "0.5.24" +version = "0.5.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "230df06b463c7251e4d1b39b1b3e6f25a9b3a42630179053a1e5f919e6e15534" +checksum = "4572dd9845e37ca0293acb5fe591a7f61b51f1b7b62d3dc6fb8e99e2664f3755" dependencies = [ "const-hex", "dirs", - "reqwest 0.13.2", + "reqwest 0.13.3", "semver 1.0.28", "serde", "serde_json", @@ -11197,9 +11072,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.24" +version = "0.5.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b271921143e5b12947a526de464db02b00363919d582a7ea712374840f928328" +checksum = "74224f62f19c1309caa071de7c1c9c1ad1d7551d2f881af4046f3d71880c820a" dependencies = [ "const-hex", "semver 1.0.28", @@ -11304,22 +11179,22 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "tempo-alloy" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-contract", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "alloy-signer-local", "alloy-sol-types", "alloy-transport", @@ -11337,9 +11212,9 @@ dependencies = [ [[package]] name = "tempo-chainspec" version = "1.5.3" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-evm", "alloy-genesis", "alloy-hardforks", @@ -11356,7 +11231,7 @@ dependencies = [ [[package]] name = "tempo-consensus" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11374,7 +11249,7 @@ dependencies = [ [[package]] name = "tempo-contracts" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-contract", "alloy-primitives", @@ -11385,7 +11260,7 @@ dependencies = [ [[package]] name = "tempo-evm" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11412,7 +11287,7 @@ dependencies = [ [[package]] name = "tempo-precompiles" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy", "alloy-evm", @@ -11432,7 +11307,7 @@ dependencies = [ [[package]] name = "tempo-precompiles-macros" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy", "proc-macro2", @@ -11443,15 +11318,15 @@ dependencies = [ [[package]] name = "tempo-primitives" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.4", "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.0.4", "aws-lc-rs", "base64 0.22.1", "derive_more", @@ -11474,7 +11349,7 @@ dependencies = [ [[package]] name = "tempo-revm" version = "1.6.0" -source = "git+https://github.com/tempoxyz/tempo?rev=8bd4d01d37e3cc324030baacbce2da0862d7735a#8bd4d01d37e3cc324030baacbce2da0862d7735a" +source = "git+https://github.com/tempoxyz/tempo?rev=6bf9903d6a75cc264029dcf54183adea38d3cfaa#6bf9903d6a75cc264029dcf54183adea38d3cfaa" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11505,15 +11380,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "term" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" -dependencies = [ - "windows-sys 0.60.2", -] - [[package]] name = "terminal_size" version = "0.4.4" @@ -11521,7 +11387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -11555,9 +11421,9 @@ dependencies = [ [[package]] name = "thin-vec" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259cdf8ed4e4aca6f1e9d011e10bd53f524a2d0637d7b28450f6c64ac298c4c6" +checksum = "b0f7e269b48f0a7dd0146680fa24b50cc67fc0373f086a5b2f99bd084639b482" [[package]] name = "thiserror" @@ -11698,9 +11564,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ "bytes", "libc", @@ -11867,9 +11733,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap 2.14.0", + "serde_core", + "serde_spanned", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -11878,7 +11746,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -12223,9 +12091,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -12509,9 +12377,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "10.0.0-beta.6" +version = "10.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "174a690eb3293a5666442b0738d080df9ea6b9e03782bbe78875c89ff914a77c" +checksum = "2d7cb4a83971db3f6ae36f0aa41eaf5985d2e2b469581fa755c132f9c2a1ec89" dependencies = [ "anyhow", "bon", @@ -12522,9 +12390,9 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "10.0.0-beta.6" +version = "10.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f628f4acc90a5c1a8136495eaf5f9ef94e03c174d6fb2e6de691bc58fc721ee" +checksum = "bba14c9676943b2899cea2ed7ea194b89b3d13564a3c93a61882a978b123a41c" dependencies = [ "anyhow", "bon", @@ -12536,9 +12404,9 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "10.0.0-beta.6" +version = "10.0.0-beta.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390d0442b660baedd7a6f60d2af01bd8967e0d7fe69cd15e13c82c811d82b709" +checksum = "fb684e6d170ef15a9b3c20561779a50ba8c806f8acdaff47c0a2c5c4c6cadd43" dependencies = [ "anyhow", "bon", @@ -12603,11 +12471,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -12616,14 +12484,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -12634,9 +12502,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ "js-sys", "wasm-bindgen", @@ -12644,9 +12512,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12654,9 +12522,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -12667,9 +12535,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -12767,7 +12635,7 @@ dependencies = [ "watchexec-events", "watchexec-signals", "watchexec-supervisor", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -12807,9 +12675,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -12827,13 +12695,13 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", - "string_cache 0.9.0", + "string_cache", "string_cache_codegen", ] @@ -12844,7 +12712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" dependencies = [ "core-foundation 0.10.1", - "jni 0.22.4", + "jni", "log", "ndk-context", "objc2", @@ -12917,7 +12785,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -13038,15 +12906,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -13083,21 +12942,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -13140,12 +12984,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -13158,12 +12996,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -13176,12 +13008,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -13206,12 +13032,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -13224,12 +13044,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -13242,12 +13056,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -13260,12 +13068,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -13289,9 +13091,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] @@ -13305,6 +13107,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" diff --git a/README.md b/README.md index c9f0a45c57b0a..90cd1d8a7865e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ foundryup See the [installation guide](https://getfoundry.sh/getting-started/installation) for more details. +To verify a downloaded release archive or container image, see [Verifying Releases](./SECURITY.md#verifying-releases). + ## Getting Started Initialize a new project, build and test: diff --git a/SECURITY.md b/SECURITY.md index d84327cc18e91..6296066db5e73 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,3 +3,112 @@ ## Reporting a Vulnerability Contact [security@tempo.xyz](mailto:security@tempo.xyz). + +## Verifying Releases + +Every official Foundry release ships with multiple, independent integrity +artifacts. All signing is keyless via [Sigstore](https://www.sigstore.dev/) — +no Foundry-managed key material is involved, and every signature is recorded +in the public [Rekor](https://docs.sigstore.dev/logging/overview/) transparency +log. The signing identity is the GitHub Actions OIDC token of this repository's +`release.yml` / `docker-publish.yml` workflows. + +### Per-release artifacts + +For each `foundry___.{tar.gz,zip}` archive on the +[releases page](https://github.com/foundry-rs/foundry/releases), the same +release also publishes: + +| Suffix | Purpose | +| --- | --- | +| `.sha256` | SHA-256 checksum of the archive (`sha256sum` format) | +| `.sigstore.json` | Cosign keyless signature bundle (cert + signature + Rekor proof) over the archive | +| `.spdx.json` | SPDX 2.3 SBOM of the source workspace used for the build | +| `.attestation.txt` | URL of the GitHub artifact-attestation summary | + +In addition, GitHub stores SLSA build-provenance and SBOM attestations against +the archive's digest; these are queryable via `gh attestation` without +downloading anything else. + +### Verifying an archive + +Pick whichever toolchain you have available — they verify the same signatures. + +#### Option 1: GitHub CLI (simplest) + +```bash +gh attestation verify foundry_v1.4.0_linux_amd64.tar.gz \ + --repo foundry-rs/foundry +``` + +This computes the file's digest, fetches the matching attestation from GitHub, +and verifies the Sigstore signature plus the SLSA provenance predicate. Add +`--signer-workflow foundry-rs/foundry/.github/workflows/release.yml` to also +require the workflow identity. + +To verify the SBOM attestation specifically: + +```bash +gh attestation verify foundry_v1.4.0_linux_amd64.tar.gz \ + --repo foundry-rs/foundry \ + --predicate-type 'https://spdx.dev/Document/v2.3' +``` + +#### Option 2: Cosign (offline-friendly) + +Download the archive and its `.sigstore.json` bundle from the release page, +then: + +```bash +cosign verify-blob \ + --bundle foundry_v1.4.0_linux_amd64.sigstore.json \ + --certificate-identity-regexp '^https://github.com/foundry-rs/foundry/\.github/workflows/release\.yml@.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + foundry_v1.4.0_linux_amd64.tar.gz +``` + +For nightly builds the certificate identity points at `refs/heads/master` +instead of a tag; the regex above matches both. + +#### Option 3: Plain checksum (integrity only) + +```bash +sha256sum -c foundry_v1.4.0_linux_amd64.sha256 # GNU coreutils +shasum -a 256 -c foundry_v1.4.0_linux_amd64.sha256 # macOS +``` + +This proves the bytes match what was uploaded, but says nothing about who +uploaded them. Combine with one of the verifications above for end-to-end +trust. + +### Verifying the Docker image + +Container signatures and attestations are pushed as OCI referrers to GHCR, so +no separate files need to be downloaded. + +```bash +# Cosign keyless signature on the image +cosign verify ghcr.io/foundry-rs/foundry:v1.4.0 \ + --certificate-identity-regexp '^https://github.com/foundry-rs/foundry/\.github/workflows/(release|docker-publish)\.yml@.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' + +# SLSA build-provenance attestation +gh attestation verify oci://ghcr.io/foundry-rs/foundry:v1.4.0 \ + --repo foundry-rs/foundry + +# Inspect the buildx-attached SBOM and provenance +docker buildx imagetools inspect ghcr.io/foundry-rs/foundry:v1.4.0 \ + --format '{{ json .SBOM }}' +docker buildx imagetools inspect ghcr.io/foundry-rs/foundry:v1.4.0 \ + --format '{{ json .Provenance }}' +``` + +To pin to an immutable digest (recommended for reproducible deployments): + +```bash +docker pull ghcr.io/foundry-rs/foundry:v1.4.0 +DIGEST=$(docker buildx imagetools inspect ghcr.io/foundry-rs/foundry:v1.4.0 --format '{{ .Manifest.Digest }}') +cosign verify "ghcr.io/foundry-rs/foundry@${DIGEST}" \ + --certificate-identity-regexp '^https://github.com/foundry-rs/foundry/\.github/workflows/(release|docker-publish)\.yml@.*' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' +``` diff --git a/benches/LATEST.md b/benches/LATEST.md index 238a691229389..cb75f8d68780b 100644 --- a/benches/LATEST.md +++ b/benches/LATEST.md @@ -1,74 +1,108 @@ -# Foundry Benchmark Results +# 📊 Foundry Benchmark Results -**Date**: 2026-04-24 23:10:24 +**Generated at**: 2026-05-02 21:53:46 UTC -## Repositories Tested +## Forge Test + +### Repositories Tested 1. [ithacaxyz/account](https://github.com/ithacaxyz/account) -2. [Vectorized/solady](https://github.com/Vectorized/solady) -3. [Uniswap/v4-core](https://github.com/Uniswap/v4-core) +2. [vectorized/solady](https://github.com/vectorized/solady) +3. [uniswap/v4-core](https://github.com/uniswap/v4-core) 4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) -5. [aave/aave-v4](https://github.com/aave/aave-v4) - -## Foundry Versions +### Foundry Versions - **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) -- **nightly**: forge Version: 1.6.0-nightly (a249f5c 2026-04-24) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) -## Forge Test - -| Repository | v1.5.1 | nightly | -| -------------------- | -------- | -------- | -| vectorized-solady | 1.46 s | 1.38 s | -| aave-aave-v4 | 4m 14.2s | 3m 29.1s | +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 2.78 s | 0.965 s | +| vectorized-solady | 0.995 s | 0.645 s | +| uniswap-v4-core | 5.97 s | 1.51 s | +| sparkdotfi-spark-psm | 19.98 s | 10.20 s | ## Forge Fuzz Test -| Repository | v1.5.1 | nightly | -| -------------------- | --------- | -------- | -| ithacaxyz-account | 2.81 s | 1.59 s | -| vectorized-solady | 1.40 s | 1.34 s | -| Uniswap-v4-core | 3.01 s | 2.87 s | -| sparkdotfi-spark-psm | 2.04 s | 1.87 s | -| aave-aave-v4 | 3m 46.0s | 3m 17.3s | +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 2.54 s | 0.923 s | +| vectorized-solady | 0.929 s | 0.617 s | +| uniswap-v4-core | 6.44 s | 1.40 s | +| sparkdotfi-spark-psm | 2.25 s | 2.03 s | ## Forge Test (Isolated) -| Repository | v1.5.1 | nightly | -| -------------------- | -------- | -------- | -| Uniswap-v4-core | 3.50 s | 3.48 s. | -| aave-aave-v4 | 4m 14.0s | 3m 53.4s | +### Repositories Tested + +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [vectorized/solady](https://github.com/vectorized/solady) +3. [uniswap/v4-core](https://github.com/uniswap/v4-core) +4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +### Foundry Versions + +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) + +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 3.05 s | 1.02 s | +| vectorized-solady | 0.871 s | 0.741 s | +| uniswap-v4-core | 6.81 s | 1.68 s | +| sparkdotfi-spark-psm | 21.96 s | 11.26 s | + +## Forge Build -## Forge Build (No Cache) +### Repositories Tested -| Repository | v1.5.1 | nightly | -| -------------------- | -------- | -------- | -| ithacaxyz-account | 26.06 s | 26.61 s | -| vectorized-solady | 14.20 s | 14.26 s | -| Uniswap-v4-core | 2m 1.3s | 2m 5.0s | -| sparkdotfi-spark-psm | 15.16 s | 15.30 s | -| aave-aave-v4 | 3m 37.0s | 3m 35.1s | +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [vectorized/solady](https://github.com/vectorized/solady) +3. [uniswap/v4-core](https://github.com/uniswap/v4-core) +4. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +### Foundry Versions + +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) + +### No Cache + +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 34.58 s | 33.29 s | +| vectorized-solady | 14.40 s | 14.41 s | +| uniswap-v4-core | 2m 17.6s | 2m 17.7s | +| sparkdotfi-spark-psm | 12.62 s | 12.61 s | -## Forge Build (With Cache) +### With Cache -| Repository | v1.5.1 | nightly | -| -------------------- | ------- | ------- | -| ithacaxyz-account | 0.167 s | 0.201 s | -| vectorized-solady | 0.099 s | 0.098 s | -| Uniswap-v4-core | 0.139 s | 0.140 s | -| sparkdotfi-spark-psm | 0.168 s | 0.173 s | -| aave-aave-v4 | 0.370 s | 0.357 s | +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 0.083 s | 0.089 s | +| vectorized-solady | 0.062 s | 0.064 s | +| uniswap-v4-core | 0.071 s | 0.074 s | +| sparkdotfi-spark-psm | 0.066 s | 0.068 s | ## Forge Coverage -| Repository | v1.5.1 | nightly | -| -------------------- | --------- | ---------- | -| Uniswap-v4-core | 1m 13.9s | 1m 10.3s | -| sparkdotfi-spark-psm | 2m 54.7s | 2m 50.0s | -| aave-aave-v4 | 11m 20.8s | 10m 58.7s | +### Repositories Tested + +1. [ithacaxyz/account](https://github.com/ithacaxyz/account) +2. [uniswap/v4-core](https://github.com/uniswap/v4-core) +3. [sparkdotfi/spark-psm](https://github.com/sparkdotfi/spark-psm) +### Foundry Versions + +- **v1.5.1**: forge Version: 1.5.1-v1.5.1 (b0a9dd9 2025-12-19) +- **v1.7.0**: forge Version: 1.6.0-v1.7.0 (f83bad9 2026-04-28) + +| Repository | v1.5.1 | v1.7.0 | +|------------|----------|----------| +| ithacaxyz-account | 29.35 s | 18.69 s | +| uniswap-v4-core | 1m 26.8s | 1m 4.1s | +| sparkdotfi-spark-psm | 2m 1.6s | 1m 28.4s | ## System Information -- **OS**: macos -- **CPU**: 12 -- **Rustc**: rustc 1.95.0 (59807616e 2026-04-14) \ No newline at end of file + +- **OS**: linux +- **CPU**: 32 +- **Rustc**: rustc 1.95.0 (59807616e 2026-04-14) diff --git a/benches/src/main.rs b/benches/src/main.rs index 60e815cecb0ec..8d7134b1c25bc 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -39,9 +39,15 @@ struct Cli { #[clap(long, default_value = ".")] output_dir: PathBuf, - /// Name of the output file (default: LATEST.md) - #[clap(long, default_value = "LATEST.md")] - output_file: String, + /// Name of the output file. Defaults to LATEST.md unless --json-output is set + /// without this flag, in which case no Markdown is written. + #[clap(long)] + output_file: Option, + + /// Filename for a flat JSON summary (benchmark/repo -> mean_seconds). + /// Resolved relative to --output-dir. Used by the nightly regression comparison script. + #[clap(long)] + json_output: Option, /// Run only specific benchmarks (comma-separated: /// forge_test,forge_build_no_cache,forge_build_with_cache,forge_fuzz_test,forge_coverage) @@ -216,12 +222,28 @@ fn main() -> Result<()> { } } - // Generate markdown report - sh_println!("📝 Generating report..."); - let markdown = results.generate_markdown(&versions, &repos); - let output_path = cli.output_dir.join(cli.output_file); - fs::write(&output_path, markdown).wrap_err("Failed to write output file")?; - sh_println!("✅ Report written to: {}", output_path.display()); + // Write Markdown report unless --json-output is set without an explicit --output-file. + let md_filename = match cli.output_file { + Some(f) => Some(f), + None if cli.json_output.is_none() => Some("LATEST.md".to_string()), + None => None, + }; + if let Some(filename) = md_filename { + sh_println!("📝 Generating report..."); + let markdown = results.generate_markdown(&versions, &repos); + let output_path = cli.output_dir.join(filename); + fs::write(&output_path, markdown).wrap_err("Failed to write output file")?; + sh_println!("✅ Report written to: {}", output_path.display()); + } + + if let Some(json_filename) = cli.json_output { + let summary = results.generate_json_summary(&versions); + let json = + serde_json::to_string_pretty(&summary).wrap_err("Failed to serialize JSON summary")?; + let json_path = cli.output_dir.join(json_filename); + fs::write(&json_path, json).wrap_err("Failed to write JSON summary")?; + sh_println!("✅ JSON summary written to: {}", json_path.display()); + } Ok(()) } diff --git a/benches/src/results.rs b/benches/src/results.rs index 447e8ed2766b4..e7d57250fc9a1 100644 --- a/benches/src/results.rs +++ b/benches/src/results.rs @@ -66,6 +66,25 @@ impl BenchmarkResults { self.version_details.insert(version.to_string(), details); } + /// Generate a flat JSON summary mapping `"benchmark/repo" -> mean_seconds`. + /// + /// Used by the nightly regression comparison script. + pub fn generate_json_summary(&self, versions: &[String]) -> HashMap { + let mut summary = HashMap::new(); + for (benchmark_name, version_data) in &self.data { + for version in versions { + if let Some(repo_data) = version_data.get(version) { + for (repo_name, result) in repo_data { + let key = format!("{benchmark_name}/{repo_name}"); + let rounded = (result.mean * 10_000.0).round() / 10_000.0; + summary.insert(key, rounded); + } + } + } + } + summary + } + pub fn generate_markdown(&self, versions: &[String], repos: &[RepoConfig]) -> String { let mut output = String::new(); diff --git a/benchmark.sh b/benchmark.sh index 2faffa93dfa1d..ac6159099069b 100755 --- a/benchmark.sh +++ b/benchmark.sh @@ -1,36 +1,52 @@ #!/bin/bash -versions="v1.3.6,v1.4.0-rc1" +versions="v1.5.1,v1.7.0" # Repositories -export ITHACA_ACCOUNT="ithacaxyz/account:v0.3.2" -export SOLADY_REPO="Vectorized/solady:v0.1.22" -export UNISWAP_V4_CORE="Uniswap/v4-core:59d3ecf" -export SPARK_PSM="sparkdotfi/spark-psm:v1.0.0" +ITHACA_ACCOUNT="ithacaxyz/account:v0.5.7" +SOLADY_REPO="vectorized/solady:v0.1.26 --nmc 'LifebuoyTest|LibBitTest|Base58Test'" +AAVE_V4="aave/aave-v4:af1f0f2ba323ac6fbaaee3abf6be060c78e22d35" +UNISWAP_V4_CORE="uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75 --nmc TickMathTestTest" +SPARK_PSM="sparkdotfi/spark-psm:v1.0.0 --nmc PSMInvariants_TimeBasedRateSetting_WithTransfers_WithPocketSetting" -# Benches -echo "===========FORGE TEST AND BUILD BENCHMARKS===========" +SOLADY_ISOLATE="vectorized/solady:v0.1.26 --nmc 'SafeTransferLibTest|LifebuoyTest|LibBitTest|Base58Test|LibStringTest'" +ITHACA_ISOLATE="ithacaxyz/account:v0.5.7 --nmc SimulateExecuteTest" -foundry-bench --versions $versions \ - --repos $ITHACA_ACCOUNT,$SOLADY_REPO,$UNISWAP_V4_CORE,$SPARK_PSM \ - --benchmarks forge_test,forge_fuzz_test,forge_build_no_cache,forge_build_with_cache \ - --output-dir ./benches/results \ - --output-file TEST_BUILD.md +SOLADY_BUILD="vectorized/solady:v0.1.26" +UNISWAP_BUILD="uniswap/v4-core:46c6834698c48bc4a463a86d8420f4eb1d7f3b75" +SPARK_PSM_BUILD="sparkdotfi/spark-psm:v1.0.0" -echo "===========FORGE COVERAGE BENCHMARKS===========" +# Benches +echo "===========FORGE TEST BENCHMARKS===========" -foundry-bench --versions $versions \ - --repos $ITHACA_ACCOUNT,$UNISWAP_V4_CORE,$SPARK_PSM \ - --benchmarks forge_coverage \ - --output-dir ./benches/results \ - --output-file COVERAGE.md +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ACCOUNT,$SOLADY_REPO,$AAVE_V4,$UNISWAP_V4_CORE,$SPARK_PSM" \ + --benchmarks forge_test,forge_fuzz_test \ + --output-dir ./benches \ + --output-file forge_test_bench.md echo "===========FORGE ISOLATE TEST BENCHMARKS===========" -foundry-bench --versions $versions \ - --repos $SOLADY_REPO,$UNISWAP_V4_CORE,$SPARK_PSM \ +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ISOLATE,$SOLADY_ISOLATE,$AAVE_V4,$UNISWAP_V4_CORE,$SPARK_PSM" \ --benchmarks forge_isolate_test \ - --output-dir ./benches/results \ - --output-file ISOLATE_TEST.md + --output-dir ./benches \ + --output-file forge_isolate_test_bench.md + +echo "===========FORGE BUILD BENCHMARKS===========" + +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ACCOUNT,$SOLADY_BUILD,$AAVE_V4,$UNISWAP_BUILD,$SPARK_PSM_BUILD" \ + --benchmarks forge_build_no_cache,forge_build_with_cache \ + --output-dir ./benches \ + --output-file forge_build_bench.md + +echo "===========FORGE COVERAGE BENCHMARKS===========" + +foundry-bench --versions "$versions" \ + --repos "$ITHACA_ACCOUNT,$AAVE_V4,$UNISWAP_BUILD,$SPARK_PSM_BUILD" \ + --benchmarks forge_coverage \ + --output-dir ./benches \ + --output-file forge_coverage_bench.md echo "===========BENCHMARKS COMPLETED===========" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index b664266450d07..d6404b21e2e8e 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -20,15 +20,15 @@ required-features = ["cli"] [dependencies] # foundry internal -anvil-core = { path = "core" } +anvil-core = { path = "core", default-features = false } anvil-rpc = { path = "rpc" } anvil-server = { path = "server" } -foundry-cli.workspace = true +foundry-cli = { workspace = true, optional = true } foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true foundry-evm-networks.workspace = true -foundry-primitives.workspace = true +foundry-primitives = { workspace = true, default-features = false } tempo-chainspec.workspace = true tempo-primitives.workspace = true tempo-precompiles.workspace = true @@ -37,7 +37,7 @@ tempo-revm.workspace = true # alloy alloy-evm = { workspace = true, features = ["call-util"] } -alloy-op-evm.workspace = true +alloy-op-evm = { workspace = true, optional = true } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-contract = { workspace = true, features = ["pubsub"] } @@ -63,7 +63,8 @@ alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true alloy-trie.workspace = true -op-alloy-consensus = { workspace = true, features = ["serde"] } +op-alloy-consensus = { workspace = true, features = ["serde"], optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } # revm revm = { workspace = true, features = [ @@ -73,7 +74,7 @@ revm = { workspace = true, features = [ "c-kzg", ] } revm-inspectors.workspace = true -op-revm.workspace = true +op-revm = { workspace = true, optional = true } # axum related axum.workspace = true @@ -120,17 +121,28 @@ reqwest.workspace = true foundry-test-utils.workspace = true tokio = { workspace = true, features = ["full"] } -op-alloy-rpc-types.workspace = true tempo-alloy.workspace = true [features] -default = ["cli", "jemalloc", "asm-keccak"] +default = ["cli", "jemalloc", "asm-keccak", "optimism"] +optimism = [ + "dep:op-alloy-consensus", + "dep:op-alloy-rpc-types", + "dep:alloy-op-evm", + "dep:op-revm", + "anvil-core/optimism", + "foundry-primitives/optimism", + "foundry-common/optimism", + "foundry-evm/optimism", + "foundry-cli?/optimism", +] asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] -jemalloc = ["foundry-cli/jemalloc"] -mimalloc = ["foundry-cli/mimalloc"] -tracy-allocator = ["foundry-cli/tracy-allocator"] +jemalloc = ["foundry-cli?/jemalloc"] +mimalloc = ["foundry-cli?/mimalloc"] +tracy-allocator = ["foundry-cli?/tracy-allocator"] cli = ["tokio/full", "cmd"] cmd = [ + "dep:foundry-cli", "clap", "clap_complete", "dep:fdlimit", diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index cf4b952ecfaa3..8456413a78b1f 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -15,7 +15,7 @@ workspace = true [dependencies] foundry-common.workspace = true foundry-evm.workspace = true -foundry-primitives.workspace = true +foundry-primitives = { workspace = true, default-features = false } revm = { workspace = true, default-features = false, features = [ "std", "serde", @@ -39,3 +39,11 @@ bytes.workspace = true # misc rand.workspace = true thiserror.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-primitives/optimism", + "foundry-common/optimism", + "foundry-evm/optimism", +] diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index fb75529b82908..4fd2aabb82a8b 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -12,7 +12,9 @@ use clap::Parser; use core::fmt; use foundry_common::shell; use foundry_config::{Chain, Config, FigmentProviders}; -use foundry_evm::hardfork::{EthereumHardfork, OpHardfork}; +#[cfg(feature = "optimism")] +use foundry_evm::hardfork::OpHardfork; +use foundry_evm::hardfork::{EthereumHardfork, FoundryHardfork}; use foundry_evm_networks::NetworkConfigs; use foundry_primitives::FoundryReceiptEnvelope; use futures::FutureExt; @@ -240,15 +242,7 @@ impl NodeArgs { } let hardfork = match &self.hardfork { - Some(hf) => { - if self.evm.networks.is_optimism() { - Some(OpHardfork::from_str(hf)?.into()) - } else if self.evm.networks.is_tempo() { - Some(TempoHardfork::from_str(hf)?.into()) - } else { - Some(EthereumHardfork::from_str(hf)?.into()) - } - } + Some(hf) => Some(parse_hardfork(hf, &self.evm.networks)?), None => None, }; @@ -849,6 +843,19 @@ impl FromStr for ForkUrl { } } +/// Parses a hardfork string against the active network configuration. +fn parse_hardfork(hf: &str, networks: &NetworkConfigs) -> eyre::Result { + #[cfg(feature = "optimism")] + if networks.is_optimism() { + return Ok(OpHardfork::from_str(hf)?.into()); + } + if networks.is_tempo() { + Ok(TempoHardfork::from_str(hf)?.into()) + } else { + Ok(EthereumHardfork::from_str(hf)?.into()) + } +} + /// Clap's value parser for genesis. Loads a genesis.json file. fn read_genesis_file(path: &str) -> Result { foundry_common::fs::read_json_file(path.as_ref()).map_err(|err| err.to_string()) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 23cd6e61bc076..7c91590c071fa 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -37,7 +37,7 @@ use foundry_config::Config; use foundry_evm::{ backend::{BlockchainDb, BlockchainDbMeta, SharedBackend}, constants::DEFAULT_CREATE2_DEPLOYER, - hardfork::{FoundryHardfork, OpHardfork}, + hardfork::FoundryHardfork, utils::{ apply_chain_and_block_specific_env_changes, block_env_from_header, get_blob_base_fee_update_fraction, @@ -577,8 +577,9 @@ impl NodeConfig { if let Some(hardfork) = self.hardfork { return hardfork; } + #[cfg(feature = "optimism")] if self.networks.is_optimism() { - return OpHardfork::default().into(); + return foundry_evm::hardforks::OpHardfork::default().into(); } if self.networks.is_tempo() { return TempoHardfork::default().into(); @@ -1079,6 +1080,7 @@ impl NodeConfig { } /// Enable Optimism network features. + #[cfg(feature = "optimism")] #[must_use] pub fn with_optimism(mut self) -> Self { self.networks = NetworkConfigs::with_optimism(); diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 78cce938318c1..4900c18eebd82 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1882,6 +1882,7 @@ impl EthApi { fn sign_request(&self, from: &Address, typed_tx: FoundryTypedTx) -> Result { match typed_tx { + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(_) => return Ok(build_impersonated(typed_tx)), _ => { for signer in self.signers.iter() { @@ -2210,9 +2211,13 @@ impl EthApi { // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce, from)]; - debug_assert!(requires != provides); + let (requires, provides) = if let Some((requires, provides)) = + tempo_parallel_nonce_markers(&pending_transaction) + { + (requires, provides) + } else { + (required_marker(nonce, on_chain_nonce, from), vec![to_marker(nonce, from)]) + }; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -2288,11 +2293,10 @@ impl EthApi { let priority = self.transaction_priority(&pending_transaction.transaction); // Tempo txs use a 2D nonce system — no sequential ordering by account nonce. - let (requires, provides) = if let FoundryTxEnvelope::Tempo(aa_tx) = - pending_transaction.transaction.as_ref() - && !aa_tx.tx().nonce_key.is_zero() + let (requires, provides) = if let Some((requires, provides)) = + tempo_parallel_nonce_markers(&pending_transaction) { - (vec![], vec![pending_transaction.hash().to_vec()]) + (requires, provides) } else { let on_chain_nonce = self.backend.current_nonce(from).await?; let nonce = pending_transaction.transaction.nonce(); @@ -3192,8 +3196,13 @@ impl EthApi { // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce, from)]; + let (requires, provides) = if let Some((requires, provides)) = + tempo_parallel_nonce_markers(&pending_transaction) + { + (requires, provides) + } else { + (required_marker(nonce, on_chain_nonce, from), vec![to_marker(nonce, from)]) + }; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -3549,6 +3558,7 @@ impl EthApi { requires: Vec, provides: Vec, ) -> Result { + debug_assert!(requires != provides); let from = *pending_transaction.sender(); let priority = self.transaction_priority(&pending_transaction.transaction); let pool_transaction = @@ -3565,7 +3575,9 @@ impl EthApi { FoundryTxEnvelope::Eip1559(_) => self.backend.ensure_eip1559_active(), FoundryTxEnvelope::Eip4844(_) => self.backend.ensure_eip4844_active(), FoundryTxEnvelope::Eip7702(_) => self.backend.ensure_eip7702_active(), + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(_) => self.backend.ensure_op_deposits_active(), + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(_) => Err(BlockchainError::InvalidTransactionRequest( "not implemented for post-exec tx".to_string(), )), @@ -3634,6 +3646,20 @@ fn required_marker(provided_nonce: u64, on_chain_nonce: u64, from: Address) -> V if on_chain_nonce <= prev_nonce { vec![to_marker(prev_nonce, from)] } else { Vec::new() } } +fn tempo_parallel_nonce_markers( + pending_transaction: &PendingTransaction, +) -> Option<(Vec, Vec)> { + // Tempo txs with non-zero nonce_key use a 2D nonce system and should not + // be sequenced by account nonce markers. + if let FoundryTxEnvelope::Tempo(aa_tx) = pending_transaction.transaction.as_ref() + && !aa_tx.tx().nonce_key.is_zero() + { + Some((vec![], vec![pending_transaction.hash().to_vec()])) + } else { + None + } +} + fn convert_transact_out(out: &Option) -> Bytes { match out { None => Default::default(), diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 614f409c3eb65..c41937055fdd4 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -67,9 +67,11 @@ impl ReceiptBuilder for FoundryReceiptBuilder { FoundryTxType::Eip1559 => FoundryReceiptEnvelope::Eip1559(receipt), FoundryTxType::Eip4844 => FoundryReceiptEnvelope::Eip4844(receipt), FoundryTxType::Eip7702 => FoundryReceiptEnvelope::Eip7702(receipt), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => { unreachable!("deposit receipts are built in commit_transaction") } + #[cfg(feature = "optimism")] FoundryTxType::PostExec => FoundryReceiptEnvelope::PostExec(receipt), FoundryTxType::Tempo => FoundryReceiptEnvelope::Tempo(receipt), } @@ -85,7 +87,7 @@ pub struct AnvilTxResult { pub sender: Address, } -impl TxResult for AnvilTxResult { +impl TxResult for AnvilTxResult { type HaltReason = H; fn result(&self) -> &ResultAndState { @@ -217,12 +219,10 @@ where }) } - fn commit_transaction( - &mut self, - output: Self::Result, - ) -> Result { + fn commit_transaction(&mut self, output: Self::Result) -> GasOutput { let AnvilTxResult { inner: EthTxResult { result: ResultAndState { result, state }, blob_gas_used, tx_type }, + #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] sender, } = output; @@ -237,6 +237,7 @@ where self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas_used); } + #[cfg(feature = "optimism")] let receipt = if tx_type == FoundryTxType::Deposit { let deposit_nonce = state.get(&sender).map(|acc| acc.info.nonce); let receipt = alloy_consensus::Receipt { @@ -262,11 +263,19 @@ where cumulative_gas_used: self.gas_used, }) }; + #[cfg(not(feature = "optimism"))] + let receipt = self.receipt_builder.build_receipt(ReceiptBuilderCtx { + tx_type, + evm: &self.evm, + result, + state: &state, + cumulative_gas_used: self.gas_used, + }); self.receipts.push(receipt); self.evm.db_mut().commit(state); - Ok(GasOutput::new(gas_used)) + GasOutput::new(gas_used) } fn finish( @@ -429,7 +438,7 @@ where let exec_result = result.result().result.clone(); let gas_used = result.result().result.tx_gas_used(); - executor.commit_transaction(result).expect("commit failed"); + executor.commit_transaction(result); let traces = executor.evm_mut().inspector_mut().finish_transaction(inspector_config); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index f404031445611..19f39f3d368de 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -57,6 +57,7 @@ use alloy_network::{ AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, Network, NetworkTransactionBuilder, ReceiptResponse, UnknownTxEnvelope, UnknownTypedTransaction, }; +#[cfg(feature = "optimism")] use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx}; use alloy_primitives::{ Address, B256, Bloom, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom, @@ -108,20 +109,60 @@ use foundry_evm::{ }, }; use foundry_evm_networks::NetworkConfigs; +#[cfg(feature = "optimism")] +use foundry_primitives::get_deposit_tx_parts; use foundry_primitives::{ FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope, - FoundryTxReceipt, get_deposit_tx_parts, + FoundryTxReceipt, }; use futures::channel::mpsc::{UnboundedSender, unbounded}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait}; -use op_revm::{OpHaltReason, OpSpecId, OpTransaction}; +#[cfg(feature = "optimism")] +use op_revm::{OpSpecId, OpTransaction, transaction::deposit::DepositTransactionParts}; + +/// Side-channel container for OP-specific deposit info produced by +/// [`Backend::build_call_env`] and consumed by the OP transact path. +/// +/// When the `optimism` feature is enabled, this is an alias for +/// `op_revm::DepositTransactionParts`. When disabled, it is a zero-sized +/// stand-in so the eth/tempo dispatch chain still type-checks. +#[cfg(feature = "optimism")] +type OpCallDepositInfo = DepositTransactionParts; +#[cfg(not(feature = "optimism"))] +#[derive(Default, Clone, Debug)] +struct OpCallDepositInfo; + +/// Marker trait that abstracts over the per-network inspector trait bounds +/// required by the in-memory backend. The OP bound is only included when the +/// `optimism` feature is enabled. +#[cfg(feature = "optimism")] +pub trait BackendInspector: + Inspector> + Inspector> + Inspector> +{ +} +#[cfg(feature = "optimism")] +impl BackendInspector for T where + T: Inspector> + Inspector> + Inspector> +{ +} +#[cfg(not(feature = "optimism"))] +pub trait BackendInspector: + Inspector> + Inspector> +{ +} +#[cfg(not(feature = "optimism"))] +impl BackendInspector for T where + T: Inspector> + Inspector> +{ +} use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use revm::{ DatabaseCommit, Inspector, context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv}, context_interface::{ block::BlobExcessGasAndPrice, - result::{EVMError, ExecutionResult, HaltReason, Output, ResultAndState}, + result::{ExecutionResult, HaltReason, Output, ResultAndState}, }, database::{CacheDB, DbAccount, WrapDatabaseRef}, interpreter::InstructionResult, @@ -157,6 +198,8 @@ pub mod cache; pub mod fork_db; pub mod in_memory_db; pub mod inspector; +#[cfg(feature = "optimism")] +pub mod optimism; pub mod state; pub mod storage; @@ -419,6 +462,11 @@ impl Backend { self.genesis.timestamp } + /// Returns the configured genesis block number. + pub const fn genesis_number(&self) -> u64 { + self.genesis.number + } + /// Returns balance of the given account. pub async fn current_balance(&self, address: Address) -> DatabaseResult { Ok(self.get_account(address).await?.balance) @@ -490,12 +538,21 @@ impl Backend { } /// Returns true if op-stack deposits are active - pub fn is_optimism(&self) -> bool { + #[cfg(feature = "optimism")] + pub const fn is_optimism(&self) -> bool { self.networks.is_optimism() } + /// Returns true if op-stack deposits are active. + /// + /// Always `false` when built without the `optimism` feature. + #[cfg(not(feature = "optimism"))] + pub const fn is_optimism(&self) -> bool { + false + } + /// Returns true if Tempo network mode is active - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { self.networks.is_tempo() } @@ -589,7 +646,8 @@ impl Backend { } /// Returns an error if op-stack deposits are not active - pub fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { + #[cfg(feature = "optimism")] + pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { if self.is_optimism() { return Ok(()); } @@ -597,7 +655,7 @@ impl Backend { } /// Returns an error if Tempo transactions are not active - pub fn ensure_tempo_active(&self) -> Result<(), BlockchainError> { + pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> { if self.is_tempo() { return Ok(()); } @@ -1139,58 +1197,53 @@ impl Backend { db: &'db DB, evm_env: &EvmEnv, inspector: &mut I, - tx_env: OpTransaction, + tx_env: TxEnv, + op_deposit: OpCallDepositInfo, ) -> Result, BlockchainError> where DB: DatabaseRef + ?Sized, - I: Inspector>> - + Inspector>> - + Inspector>>, + I: BackendInspector>, WrapDatabaseRef<&'db DB>: Database, { + #[cfg(feature = "optimism")] if self.is_optimism() { - let op_env = EvmEnv::new( - evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(OpSpecId::ISTHMUS), - evm_env.block_env.clone(), - ); - let mut evm = OpEvmFactory::default().create_evm_with_inspector( - WrapDatabaseRef(db), - op_env, - inspector, - ); - self.inject_precompiles(evm.precompiles_mut()); - let result = evm.transact(OpTx(tx_env)).map_err(|e| match e { - EVMError::Database(db) => EVMError::Database(db), - EVMError::Header(h) => EVMError::Header(h), - EVMError::Custom(s) => EVMError::Custom(s), - EVMError::CustomAny(err) => EVMError::CustomAny(err), - EVMError::Transaction(t) => EVMError::Transaction(t), - })?; - Ok(ResultAndState { - result: result.result.map_haltreason(|h| match h { - OpHaltReason::Base(eth) => eth, - _ => HaltReason::PrecompileError, - }), - state: result.state, - }) - } else if self.is_tempo() { - self.transact_tempo_with_inspector_ref( - db, - evm_env, - inspector, - TempoTxEnv::from(tx_env.base), - ) + let op_tx = OpTransaction { base: tx_env, deposit: op_deposit, ..Default::default() }; + return self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx); + } + // `op_deposit` only matters on the OP path; eth/tempo ignore it. + let _ = op_deposit; + if self.is_tempo() { + self.transact_tempo_with_inspector_ref(db, evm_env, inspector, TempoTxEnv::from(tx_env)) } else { - let mut evm = EthEvmFactory::default().create_evm_with_inspector( - WrapDatabaseRef(db), - evm_env.clone(), - inspector, - ); - self.inject_precompiles(evm.precompiles_mut()); - Ok(evm.transact(tx_env.base)?) + self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env) } } + /// Eth path of [`Backend::transact_with_inspector_ref`]. + /// + /// Creates an Ethereum EVM, injects precompiles, and transacts with a + /// plain [`TxEnv`]. + fn transact_eth_with_inspector_ref<'db, I, DB>( + &self, + db: &'db DB, + evm_env: &EvmEnv, + inspector: &mut I, + tx_env: TxEnv, + ) -> Result, BlockchainError> + where + DB: DatabaseRef + ?Sized, + I: Inspector>>, + WrapDatabaseRef<&'db DB>: Database, + { + let mut evm = EthEvmFactory::default().create_evm_with_inspector( + WrapDatabaseRef(db), + evm_env.clone(), + inspector, + ); + self.inject_precompiles(evm.precompiles_mut()); + Ok(evm.transact(tx_env)?) + } + /// Builds the appropriate tx env from a [`FoundryTxEnvelope`], executes via the correct /// EVM backend (Op/Tempo/Eth), and returns both the result and the base [`TxEnv`]. fn transact_envelope_with_inspector_ref<'db, I, DB>( @@ -1203,9 +1256,7 @@ impl Backend { ) -> Result<(ResultAndState, TxEnv), BlockchainError> where DB: DatabaseRef + ?Sized, - I: Inspector>> - + Inspector>> - + Inspector>>, + I: BackendInspector>, WrapDatabaseRef<&'db DB>: Database, { if tx.is_tempo() { @@ -1213,14 +1264,21 @@ impl Backend { FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into()); let base = tx_env.inner.clone(); let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?; - Ok((result, base)) - } else { - let tx_env: OpTransaction = + return Ok((result, base)); + } + #[cfg(feature = "optimism")] + if self.is_optimism() { + let op_tx: OpTransaction = FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into()); - let base = tx_env.base.clone(); - let result = self.transact_with_inspector_ref(db, evm_env, inspector, tx_env)?; - Ok((result, base)) + let base = op_tx.base.clone(); + let result = self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx)?; + return Ok((result, base)); } + let tx_env: TxEnv = + FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into()); + let base = tx_env.clone(); + let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?; + Ok((result, base)) } /// Creates a Tempo EVM, injects precompiles, and transacts with a native [`TempoTxEnv`]. @@ -1298,6 +1356,7 @@ impl Backend { }}; } + #[cfg(feature = "optimism")] if self.is_optimism() { let op_env = EvmEnv::new( evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(OpSpecId::ISTHMUS), @@ -1305,8 +1364,10 @@ impl Backend { ); let mut evm = OpEvmFactory::::default().create_evm_with_inspector(db, op_env, inspector); - run!(evm) - } else if self.is_tempo() { + return run!(evm); + } + + if self.is_tempo() { let hardfork = TempoHardfork::from(self.hardfork); let tempo_env = EvmEnv::new( evm_env @@ -1338,7 +1399,7 @@ impl Backend { request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> (EvmEnv, OpTransaction) { + ) -> (EvmEnv, TxEnv, OpCallDepositInfo) { let tx_type = request.minimal_tx_type() as u8; let WithOtherFields:: { @@ -1391,7 +1452,7 @@ impl Backend { let caller = from.unwrap_or_default(); let to = to.as_ref().and_then(TxKind::to); let blob_hashes = blob_versioned_hashes.unwrap_or_default(); - let mut base = TxEnv { + let mut tx_env = TxEnv { caller, gas_limit, gas_price, @@ -1413,11 +1474,10 @@ impl Backend { blob_hashes, ..Default::default() }; - base.set_signed_authorization(authorization_list.unwrap_or_default()); - let mut tx_env = OpTransaction { base, ..Default::default() }; + tx_env.set_signed_authorization(authorization_list.unwrap_or_default()); if let Some(nonce) = nonce { - tx_env.base.nonce = nonce; + tx_env.nonce = nonce; } if evm_env.block_env.basefee == 0 { @@ -1427,13 +1487,22 @@ impl Backend { } // Deposit transaction? (only valid when op-stack deposits are active) - if self.ensure_op_deposits_active().is_ok() + #[cfg(feature = "optimism")] + let op_deposit = if self.ensure_op_deposits_active().is_ok() && let Ok(deposit) = get_deposit_tx_parts(&other) { - tx_env.deposit = deposit; - } + deposit + } else { + OpCallDepositInfo::default() + }; + #[cfg(not(feature = "optimism"))] + let op_deposit = { + // `other` carries OP-only deposit fields; consumed only when feature is enabled. + let _ = &other; + OpCallDepositInfo + }; - (evm_env, tx_env) + (evm_env, tx_env, op_deposit) } pub fn call_with_state( @@ -1467,13 +1536,13 @@ impl Backend { (fee_token, nonce_key, valid_before, valid_after) }); - let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env); let ResultAndState { result, state } = if let Some((fee_token, nonce_key, valid_before, valid_after)) = tempo_overrides { use tempo_primitives::transaction::Call; - let base = tx_env.base; + let base = tx_env; let mut tempo_tx = TempoTxEnv::from(base.clone()); tempo_tx.fee_token = fee_token; @@ -1495,7 +1564,13 @@ impl Backend { } self.transact_tempo_with_inspector_ref(state, &evm_env, &mut inspector, tempo_tx)? } else { - self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)? + self.transact_with_inspector_ref( + state, + &evm_env, + &mut inspector, + tx_env, + op_deposit, + )? }; let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); @@ -1518,9 +1593,9 @@ impl Backend { let mut inspector = AccessListInspector::new(request.access_list.clone().unwrap_or_default()); - let (evm_env, tx_env) = self.build_call_env(request, fee_details, block_env); + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env); let ResultAndState { result, state: _ } = - self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)?; + self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env, op_deposit)?; let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); let access_list = inspector.access_list(); Ok((exit_reason, out, gas_used, access_list)) @@ -2912,7 +2987,7 @@ where TracingInspectorConfig::from_geth_call_config(&call_config), ); - let (evm_env, tx_env) = + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block); let ResultAndState { result, state: _ } = self .transact_with_inspector_ref( @@ -2920,6 +2995,7 @@ where &evm_env, &mut inspector, tx_env, + op_deposit, )?; inspector.print_logs(); @@ -2945,13 +3021,14 @@ where ), ); - let (evm_env, tx_env) = + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block); let result = self.transact_with_inspector_ref( &cache_db, &evm_env, &mut inspector, tx_env, + op_deposit, )?; Ok(inspector @@ -2973,22 +3050,22 @@ where } #[cfg(feature = "js-tracer")] GethDebugTracerType::JsTracer(code) => { - use alloy_evm::IntoTxEnv; let config = tracer_config.into_json(); let mut inspector = revm_inspectors::tracing::js::JsInspector::new(code, config) .map_err(|err| BlockchainError::Message(err.to_string()))?; - let (evm_env, tx_env) = + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block.clone()); let result = self.transact_with_inspector_ref( &cache_db, &evm_env, &mut inspector, tx_env.clone(), + op_deposit, )?; let res = inspector - .json_result(result, &OpTx(tx_env).into_tx_env(), &block, &cache_db) + .json_result(result, &tx_env, &block, &cache_db) .map_err(|err| BlockchainError::Message(err.to_string()))?; Ok(GethTrace::JS(res)) @@ -3001,9 +3078,14 @@ where .build_inspector() .with_tracing_config(TracingInspectorConfig::from_geth_config(&config)); - let (evm_env, tx_env) = self.build_call_env(request, fee_details, block); - let ResultAndState { result, state: _ } = - self.transact_with_inspector_ref(&cache_db, &evm_env, &mut inspector, tx_env)?; + let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block); + let ResultAndState { result, state: _ } = self.transact_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + tx_env, + op_deposit, + )?; let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result); @@ -3187,10 +3269,7 @@ where f: F, ) -> Result where - for<'a> I: Inspector>>>> - + Inspector>>>> - + Inspector>>>> - + 'a, + for<'a> I: BackendInspector>>> + 'a, for<'a> F: FnOnce(ResultAndState, CacheDB>, I, TxEnv, EvmEnv) -> T, { @@ -3965,7 +4044,7 @@ impl Backend { )? .or_zero_fees(); - let (mut evm_env, tx_env) = self.build_call_env( + let (mut evm_env, tx_env, op_deposit) = self.build_call_env( WithOtherFields::new(request.clone()), fee_details, block_env.clone(), @@ -3986,8 +4065,13 @@ impl Backend { inspector = inspector.with_transfers(); } trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env"); - let ResultAndState { result, state } = - self.transact_with_inspector_ref(&cache_db, &evm_env, &mut inspector, tx_env)?; + let ResultAndState { result, state } = self.transact_with_inspector_ref( + &cache_db, + &evm_env, + &mut inspector, + tx_env, + op_deposit, + )?; trace!(target: "backend", ?result, ?request, "simulate call"); inspector.print_logs(); @@ -4359,7 +4443,10 @@ where } // Nonce validation — skip for deposits (L1→L2) and Tempo txs (2D nonce system) + #[cfg(feature = "optimism")] let is_deposit_tx = pending.transaction.as_ref().is_deposit(); + #[cfg(not(feature = "optimism"))] + let is_deposit_tx = false; let is_tempo_tx = pending.transaction.as_ref().is_tempo(); let nonce = tx.nonce(); if nonce < account.nonce && !is_deposit_tx && !is_tempo_tx { @@ -4475,6 +4562,7 @@ where ); let value = tx.value(); match tx.as_ref() { + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(deposit_tx) => { // Deposit transactions // https://specs.optimism.io/protocol/deposits.html#execution @@ -4538,6 +4626,7 @@ pub fn transaction_build( info: Option, base_fee: Option, ) -> AnyRpcTransaction { + #[cfg(feature = "optimism")] if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() { let dep_tx = deposit_tx; diff --git a/crates/anvil/src/eth/backend/mem/optimism.rs b/crates/anvil/src/eth/backend/mem/optimism.rs new file mode 100644 index 0000000000000..e9a94cc254fb7 --- /dev/null +++ b/crates/anvil/src/eth/backend/mem/optimism.rs @@ -0,0 +1,61 @@ +//! Optimism-specific transact helpers for the in-memory backend. + +use super::Backend; +use crate::eth::error::BlockchainError; +use alloy_evm::{Database, Evm, EvmEnv, EvmFactory}; +use alloy_network::Network; +use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx}; +use foundry_evm::backend::DatabaseError; +use op_revm::{OpHaltReason, OpSpecId, OpTransaction}; +use revm::{ + DatabaseRef, Inspector, + context::{ + TxEnv, + result::{EVMError, HaltReason, ResultAndState}, + }, + database_interface::WrapDatabaseRef, +}; + +impl Backend { + /// Optimism path of [`Backend::transact_with_inspector_ref`]. + /// + /// Creates an OP EVM, injects precompiles, transacts, and maps the + /// OP-specific halt reason back to the shared [`HaltReason`]. + pub(super) fn transact_op_with_inspector_ref<'db, I, DB>( + &self, + db: &'db DB, + evm_env: &EvmEnv, + inspector: &mut I, + tx_env: OpTransaction, + ) -> Result, BlockchainError> + where + DB: DatabaseRef + ?Sized, + I: Inspector>>, + WrapDatabaseRef<&'db DB>: Database, + { + let op_env = EvmEnv::new( + evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(OpSpecId::ISTHMUS), + evm_env.block_env.clone(), + ); + let mut evm = OpEvmFactory::default().create_evm_with_inspector( + WrapDatabaseRef(db), + op_env, + inspector, + ); + self.inject_precompiles(evm.precompiles_mut()); + let result = evm.transact(OpTx(tx_env)).map_err(|e| match e { + EVMError::Database(db) => EVMError::Database(db), + EVMError::Header(h) => EVMError::Header(h), + EVMError::Custom(s) => EVMError::Custom(s), + EVMError::CustomAny(err) => EVMError::CustomAny(err), + EVMError::Transaction(t) => EVMError::Transaction(t), + })?; + Ok(ResultAndState { + result: result.result.map_haltreason(|h| match h { + OpHaltReason::Base(eth) => eth, + _ => HaltReason::PrecompileError, + }), + state: result.state, + }) + } +} diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error/mod.rs similarity index 91% rename from crates/anvil/src/eth/error.rs rename to crates/anvil/src/eth/error/mod.rs index 3b2ada43d7731..482df681b184a 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error/mod.rs @@ -12,7 +12,6 @@ use anvil_rpc::{ response::ResponseResult, }; use foundry_evm::{backend::DatabaseError, decode::RevertDecoder}; -use op_revm::OpTransactionError; use revm::{ context_interface::result::{EVMError, InvalidHeader, InvalidTransaction}, interpreter::InstructionResult, @@ -21,6 +20,9 @@ use serde::Serialize; use tempo_revm::TempoInvalidTransaction; use tokio::time::Duration; +#[cfg(feature = "optimism")] +mod optimism; + pub(crate) type Result = std::result::Result; #[derive(Debug, thiserror::Error)] @@ -163,51 +165,6 @@ where } } -impl From> for BlockchainError -where - T: Into, -{ - fn from(err: EVMError) -> Self { - match err { - EVMError::Transaction(err) => match err { - OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(), - OpTransactionError::DepositSystemTxPostRegolith => { - Self::DepositTransactionUnsupported - } - OpTransactionError::HaltedDepositPostRegolith => { - Self::DepositTransactionUnsupported - } - OpTransactionError::MissingEnvelopedTx => Self::InvalidTransaction(err.into()), - }, - EVMError::Header(err) => match err { - InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet, - InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet, - }, - EVMError::Database(err) => err.into(), - EVMError::Custom(err) => Self::Message(err), - EVMError::CustomAny(err) => Self::Message(err.to_string()), - } - } -} - -impl From> for BlockchainError -where - T: Into, -{ - fn from(err: EVMError) -> Self { - match err { - EVMError::Transaction(err) => { - let op_err: OpTransactionError = err.0; - EVMError::::Transaction(op_err).into() - } - EVMError::Header(err) => EVMError::::Header(err).into(), - EVMError::Database(err) => err.into(), - EVMError::Custom(err) => Self::Message(err), - EVMError::CustomAny(err) => Self::Message(err.to_string()), - } - } -} - impl From> for BlockchainError where T: Into, @@ -451,16 +408,6 @@ impl From for InvalidTransactionError { } } -impl From for InvalidTransactionError { - fn from(value: OpTransactionError) -> Self { - match value { - OpTransactionError::Base(err) => err.into(), - OpTransactionError::DepositSystemTxPostRegolith - | OpTransactionError::HaltedDepositPostRegolith => Self::DepositTxErrorPostRegolith, - OpTransactionError::MissingEnvelopedTx => Self::MissingEnvelopedTx, - } - } -} /// Helper trait to easily convert results to rpc results pub(crate) trait ToRpcResponseResult { fn to_rpc_result(self) -> ResponseResult; @@ -577,9 +524,13 @@ impl ToRpcResponseResult for Result { err => RpcError::internal_error_with(format!("Fork Error: {err:?}")), } } - err @ BlockchainError::EvmError(_) => { - RpcError::internal_error_with(err.to_string()) - } + err @ BlockchainError::EvmError(_) => RpcError { + // VM halts are execution failures, not JSON-RPC server faults. REVERT has a + // dedicated code/data path above; other halts, such as invalid opcode, do not. + code: ErrorCode::TransactionRejected, + message: err.to_string().into(), + data: None, + }, err @ BlockchainError::EvmOverrideError(_) => { RpcError::invalid_params(err.to_string()) } diff --git a/crates/anvil/src/eth/error/optimism.rs b/crates/anvil/src/eth/error/optimism.rs new file mode 100644 index 0000000000000..1207fde30a72a --- /dev/null +++ b/crates/anvil/src/eth/error/optimism.rs @@ -0,0 +1,62 @@ +//! Optimism-specific error conversions for [`BlockchainError`] and +//! [`InvalidTransactionError`]. + +use super::{BlockchainError, InvalidTransactionError}; +use op_revm::OpTransactionError; +use revm::context_interface::result::{EVMError, InvalidHeader}; + +impl From> for BlockchainError +where + T: Into, +{ + fn from(err: EVMError) -> Self { + match err { + EVMError::Transaction(err) => match err { + OpTransactionError::Base(err) => InvalidTransactionError::from(err).into(), + OpTransactionError::DepositSystemTxPostRegolith => { + Self::DepositTransactionUnsupported + } + OpTransactionError::HaltedDepositPostRegolith => { + Self::DepositTransactionUnsupported + } + OpTransactionError::MissingEnvelopedTx => Self::InvalidTransaction(err.into()), + }, + EVMError::Header(err) => match err { + InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet, + InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet, + }, + EVMError::Database(err) => err.into(), + EVMError::Custom(err) => Self::Message(err), + EVMError::CustomAny(err) => Self::Message(err.to_string()), + } + } +} + +impl From> for BlockchainError +where + T: Into, +{ + fn from(err: EVMError) -> Self { + match err { + EVMError::Transaction(err) => { + let op_err: OpTransactionError = err.0; + EVMError::::Transaction(op_err).into() + } + EVMError::Header(err) => EVMError::::Header(err).into(), + EVMError::Database(err) => err.into(), + EVMError::Custom(err) => Self::Message(err), + EVMError::CustomAny(err) => Self::Message(err.to_string()), + } + } +} + +impl From for InvalidTransactionError { + fn from(value: OpTransactionError) -> Self { + match value { + OpTransactionError::Base(err) => err.into(), + OpTransactionError::DepositSystemTxPostRegolith + | OpTransactionError::HaltedDepositPostRegolith => Self::DepositTxErrorPostRegolith, + OpTransactionError::MissingEnvelopedTx => Self::MissingEnvelopedTx, + } + } +} diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 4da86933020ae..2ccf65ba721f5 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -155,9 +155,12 @@ impl EthApi { let best = self.backend.best_number(); // we go from given block (defaulting to best) down to first block - // considering only post-fork + // considering only post-fork (or post-genesis in non-fork mode) 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 to = self + .get_fork() + .map(|f| f.block_number() + 1) + .unwrap_or_else(|| self.backend.genesis_number() + 1); let first_page = from >= best; let mut last_page = false; @@ -198,8 +201,11 @@ impl EthApi { node_info!("ots_searchTransactionsAfter"); let best = self.backend.best_number(); - // we go from the first post-fork block, up to the tip - let first_block = self.get_fork().map(|f| f.block_number() + 1).unwrap_or(1); + // we go from the first post-fork (or post-genesis) block, up to the tip + let first_block = self + .get_fork() + .map(|f| f.block_number() + 1) + .unwrap_or_else(|| self.backend.genesis_number() + 1); let from = if block_number == 0 { first_block } else { block_number + 1 }; let to = best; @@ -248,7 +254,10 @@ impl EthApi { ) -> Result> { node_info!("ots_getTransactionBySenderAndNonce"); - let from = self.get_fork().map(|f| f.block_number() + 1).unwrap_or_default(); + let from = self + .get_fork() + .map(|f| f.block_number() + 1) + .unwrap_or_else(|| self.backend.genesis_number() + 1); let to = self.backend.best_number(); for n in (from..=to).rev() { diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index df65822e1eab3..5280987483dd7 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -123,10 +123,10 @@ impl PoolTransaction { impl fmt::Debug for PoolTransaction { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "Transaction {{ ")?; - write!(fmt, "hash: {:?}, ", &self.pending_transaction.hash())?; + write!(fmt, "hash: {:?}, ", self.pending_transaction.hash())?; write!(fmt, "requires: [{}], ", hex_fmt_many(self.requires.iter()))?; write!(fmt, "provides: [{}], ", hex_fmt_many(self.provides.iter()))?; - write!(fmt, "raw tx: {:?}", &self.pending_transaction)?; + write!(fmt, "raw tx: {:?}", self.pending_transaction)?; write!(fmt, "}}")?; Ok(()) } diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 3fdf6192c4537..d1736c3093056 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,5 +1,7 @@ use crate::eth::error::BlockchainError; -use alloy_consensus::{Sealed, SignableTransaction}; +#[cfg(feature = "optimism")] +use alloy_consensus::Sealed; +use alloy_consensus::SignableTransaction; use alloy_dyn_abi::TypedData; use alloy_network::{Network, TxSignerSync}; use alloy_primitives::{Address, B256, Signature, map::AddressHashMap}; @@ -130,9 +132,11 @@ impl Signer for DevSigner { let sig = signer.sign_transaction_sync(&mut t)?; FoundryTxEnvelope::Eip4844(t.into_signed(sig)) } + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(_) => { unreachable!("op deposit txs should not be signed") } + #[cfg(feature = "optimism")] FoundryTypedTx::PostExec(_) => { unreachable!("op post-exec txs should not be signed") } @@ -156,7 +160,9 @@ pub fn build_impersonated(typed_tx: FoundryTypedTx) -> FoundryTxEnvelope { FoundryTypedTx::Eip1559(tx) => FoundryTxEnvelope::Eip1559(tx.into_signed(signature)), FoundryTypedTx::Eip7702(tx) => FoundryTxEnvelope::Eip7702(tx.into_signed(signature)), FoundryTypedTx::Eip4844(tx) => FoundryTxEnvelope::Eip4844(tx.into_signed(signature)), + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(tx) => FoundryTxEnvelope::Deposit(Sealed::new(tx)), + #[cfg(feature = "optimism")] FoundryTypedTx::PostExec(_) => { unreachable!("op post-exec txs should not be impersonated") } diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm/mod.rs similarity index 64% rename from crates/anvil/src/evm.rs rename to crates/anvil/src/evm/mod.rs index d1b40ba56ebbd..85e43d371b097 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm/mod.rs @@ -2,6 +2,9 @@ use alloy_evm::precompiles::DynPrecompile; use alloy_primitives::Address; use std::fmt::Debug; +#[cfg(feature = "optimism")] +mod optimism; + /// Object-safe trait that enables injecting extra precompiles when using /// `anvil` as a library. pub trait PrecompileFactory: Send + Sync + Unpin + Debug { @@ -15,14 +18,12 @@ mod tests { use crate::PrecompileFactory; use alloy_evm::{ - EthEvm, Evm, EvmEnv, EvmFactory, + EthEvm, Evm, eth::EthEvmContext, precompiles::{DynPrecompile, PrecompilesMap}, }; - use alloy_op_evm::{OpEvm, OpEvmFactory, OpTx}; - use alloy_primitives::{Address, Bytes, TxKind, U256, address}; + use alloy_primitives::{Address, Bytes, TxKind, address}; use itertools::Itertools; - use op_revm::{OpSpecId, OpTransaction}; use revm::{ Journal, context::{BlockEnv, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv}, @@ -35,20 +36,19 @@ mod tests { }; // A precompile activated in the `Prague` spec (BLS12-381 G2 map). - const ETH_PRAGUE_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000011"); + pub(super) const ETH_PRAGUE_PRECOMPILE: Address = + address!("0x0000000000000000000000000000000000000011"); // A precompile activated in the `Osaka` spec (EIP-7951). const ETH_OSAKA_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); - // A precompile activated in the `Isthmus` spec. - const OP_ISTHMUS_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); - // A custom precompile address and payload for testing. - const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071"); - const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; + pub(super) const PRECOMPILE_ADDR: Address = + address!("0x0000000000000000000000000000000000000071"); + pub(super) const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; #[derive(Debug)] - struct CustomPrecompileFactory; + pub(super) struct CustomPrecompileFactory; impl PrecompileFactory for CustomPrecompileFactory { fn precompiles(&self) -> Vec<(Address, DynPrecompile)> { @@ -109,34 +109,6 @@ mod tests { (tx_env, eth_evm) } - /// Creates a new OP EVM instance. - fn create_op_evm( - _spec: SpecId, - op_spec: OpSpecId, - ) -> (OpTx, OpEvm, NoOpInspector, PrecompilesMap, OpTx>) { - let tx = OpTx(OpTransaction:: { - base: TxEnv { - kind: TxKind::Call(PRECOMPILE_ADDR), - data: PAYLOAD.into(), - ..Default::default() - }, - ..Default::default() - }); - - let mut evm = OpEvmFactory::::default().create_evm_with_inspector( - EmptyDB::default(), - EvmEnv::new(CfgEnv::new_with_spec(op_spec), BlockEnv::default()), - NoOpInspector, - ); - - if op_spec == OpSpecId::ISTHMUS { - evm.ctx_mut().chain.operator_fee_constant = Some(U256::ZERO); - evm.ctx_mut().chain.operator_fee_scalar = Some(U256::ZERO); - } - - (tx, evm) - } - #[test] fn build_eth_evm_with_extra_precompiles_osaka_spec() { let (tx_env, mut evm) = create_eth_evm(SpecId::OSAKA); @@ -187,38 +159,4 @@ mod tests { assert!(result.result.is_success()); assert_eq!(result.result.output(), Some(&PAYLOAD.into())); } - - #[test] - fn build_op_evm_with_extra_precompiles_isthmus_spec() { - let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::ISTHMUS); - - assert!(evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); - assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); - assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); - - assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - let result = evm.transact(tx).unwrap(); - assert!(result.result.is_success()); - assert_eq!(result.result.output(), Some(&PAYLOAD.into())); - } - - #[test] - fn build_op_evm_with_extra_precompiles_bedrock_spec() { - let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::BEDROCK); - - assert!(!evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); - assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); - assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); - - assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); - - let result = evm.transact(tx).unwrap(); - assert!(result.result.is_success()); - assert_eq!(result.result.output(), Some(&PAYLOAD.into())); - } } diff --git a/crates/anvil/src/evm/optimism.rs b/crates/anvil/src/evm/optimism.rs new file mode 100644 index 0000000000000..526375fec31ea --- /dev/null +++ b/crates/anvil/src/evm/optimism.rs @@ -0,0 +1,87 @@ +//! Optimism-specific EVM helpers. + +#[cfg(test)] +mod tests { + use std::convert::Infallible; + + use super::super::tests::{ + CustomPrecompileFactory, ETH_PRAGUE_PRECOMPILE, PAYLOAD, PRECOMPILE_ADDR, + }; + use crate::PrecompileFactory; + use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap}; + use alloy_op_evm::{OpEvm, OpEvmFactory, OpTx}; + use alloy_primitives::{Address, TxKind, U256, address}; + use itertools::Itertools; + use op_revm::{OpSpecId, OpTransaction}; + use revm::{ + context::{BlockEnv, CfgEnv, TxEnv}, + database::{EmptyDB, EmptyDBTyped}, + inspector::NoOpInspector, + primitives::hardfork::SpecId, + }; + + // A precompile activated in the `Isthmus` spec. + const OP_ISTHMUS_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100"); + + /// Creates a new OP EVM instance. + fn create_op_evm( + _spec: SpecId, + op_spec: OpSpecId, + ) -> (OpTx, OpEvm, NoOpInspector, PrecompilesMap, OpTx>) { + let tx = OpTx(OpTransaction:: { + base: TxEnv { + kind: TxKind::Call(PRECOMPILE_ADDR), + data: PAYLOAD.into(), + ..Default::default() + }, + ..Default::default() + }); + + let mut evm = OpEvmFactory::::default().create_evm_with_inspector( + EmptyDB::default(), + EvmEnv::new(CfgEnv::new_with_spec(op_spec), BlockEnv::default()), + NoOpInspector, + ); + + if op_spec == OpSpecId::ISTHMUS { + evm.ctx_mut().chain.operator_fee_constant = Some(U256::ZERO); + evm.ctx_mut().chain.operator_fee_scalar = Some(U256::ZERO); + } + + (tx, evm) + } + + #[test] + fn build_op_evm_with_extra_precompiles_isthmus_spec() { + let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::ISTHMUS); + + assert!(evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); + assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); + assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); + + assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + let result = evm.transact(tx).unwrap(); + assert!(result.result.is_success()); + assert_eq!(result.result.output(), Some(&PAYLOAD.into())); + } + + #[test] + fn build_op_evm_with_extra_precompiles_bedrock_spec() { + let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::BEDROCK); + + assert!(!evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE)); + assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE)); + assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles()); + + assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR)); + + let result = evm.transact(tx).unwrap(); + assert!(result.result.is_success()); + assert_eq!(result.result.output(), Some(&PAYLOAD.into())); + } +} diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 26e587e8b5123..a661e9c765b26 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -3,6 +3,9 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "optimism")] +use op_alloy_rpc_types as _; + use crate::{ error::{NodeError, NodeResult}, eth::{ diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index c4879e36d5240..84cc9b861bbbf 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -11,6 +11,7 @@ mod gas; mod genesis; mod ipc; mod logs; +#[cfg(feature = "optimism")] mod optimism; mod otterscan; mod proof; @@ -18,6 +19,7 @@ mod pubsub; mod revert; mod sign; mod simulate; +#[cfg(feature = "cmd")] mod state; mod tempo; mod traces; diff --git a/crates/anvil/tests/it/revert.rs b/crates/anvil/tests/it/revert.rs index ab85fc89abf80..a15454fa5593e 100644 --- a/crates/anvil/tests/it/revert.rs +++ b/crates/anvil/tests/it/revert.rs @@ -28,6 +28,38 @@ async fn test_deploy_reverting() { assert!(!receipt.inner.inner.status()); } +#[tokio::test(flavor = "multi_thread")] +async fn test_invalid_opcode_rpc_error_code() { + let (_api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); + + // Deploy a contract whose runtime bytecode is the invalid opcode 0xfe. + let code = bytes!("60fe60005360016000f3"); + let tx = TransactionRequest::default().from(sender).with_deploy_code(code); + let receipt = provider + .send_transaction(WithOtherFields::new(tx)) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract = receipt.contract_address.unwrap(); + + for (method, params) in [ + ("eth_call", serde_json::json!([{ "from": sender, "to": contract }, "latest"])), + ("eth_estimateGas", serde_json::json!([{ "from": sender, "to": contract }])), + ] { + let error = rpc_error(&handle.http_endpoint(), method, params).await; + assert_eq!(error["code"], serde_json::json!(-32003), "{error}"); + assert!(error.get("data").is_none(), "{error}"); + + let message = error["message"].as_str().unwrap(); + assert!(message.contains("EVM error InvalidFEOpcode"), "{error}"); + assert!(!message.contains("execution reverted"), "{error}"); + } +} + #[tokio::test(flavor = "multi_thread")] async fn test_revert_messages() { sol!( @@ -124,3 +156,21 @@ async fn test_solc_revert_custom_errors() { let s = err.to_string(); assert!(s.contains("execution reverted"), "{s:?}"); } + +async fn rpc_error(endpoint: &str, method: &str, params: serde_json::Value) -> serde_json::Value { + let response = reqwest::Client::new() + .post(endpoint) + .json(&serde_json::json!({ + "jsonrpc": "2.0", + "id": 1, + "method": method, + "params": params, + })) + .send() + .await + .unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + + let body = response.json::().await.unwrap(); + body.get("error").cloned().unwrap_or_else(|| panic!("expected JSON-RPC error, got {body}")) +} diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index dc5be77a244da..03be26a631a11 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -61,9 +61,9 @@ tempo-contracts.workspace = true tempo-primitives.workspace = true alloy-evm.workspace = true -op-alloy-consensus = { workspace = true, features = ["k256"] } -op-alloy-flz.workspace = true -op-alloy-network.workspace = true +op-alloy-consensus = { workspace = true, features = ["k256"], optional = true } +op-alloy-flz = { workspace = true, optional = true } +op-alloy-network = { workspace = true, optional = true } chrono.workspace = true eyre.workspace = true @@ -100,7 +100,7 @@ anvil.workspace = true foundry-test-utils.workspace = true [features] -default = ["jemalloc", "asm-keccak"] +default = ["jemalloc", "asm-keccak", "optimism"] asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] @@ -109,3 +109,12 @@ aws-kms = ["foundry-wallets/aws-kms"] gcp-kms = ["foundry-wallets/gcp-kms"] turnkey = ["foundry-wallets/turnkey"] isolate-by-default = ["foundry-config/isolate-by-default"] +optimism = [ + "dep:op-alloy-flz", + "dep:op-alloy-consensus", + "dep:op-alloy-network", + "foundry-common/optimism", + "foundry-evm-networks/optimism", + "foundry-evm/optimism", + "foundry-cli/optimism", +] diff --git a/crates/cast/src/args.rs b/crates/cast/src/args.rs index 6dc2ed6acf430..23fda25c40faa 100644 --- a/crates/cast/src/args.rs +++ b/crates/cast/src/args.rs @@ -29,6 +29,7 @@ use foundry_common::{ shell, stdin, }; use foundry_evm_networks::NetworkVariant; +#[cfg(feature = "optimism")] use op_alloy_network::Optimism; use std::time::Instant; use tempo_alloy::TempoNetwork; @@ -351,6 +352,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { // Can use either --raw or specify raw as a field let output = if raw || fields.contains(&"raw".into()) { match network { + #[cfg(feature = "optimism")] Some(NetworkVariant::Optimism) => { let provider = ProviderBuilder::::from_config(&config)?.build()?; @@ -569,6 +571,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { // Can use either --raw or specify raw as a field let is_raw = raw || field.as_ref().is_some_and(|f| f == "raw"); let output = match network { + #[cfg(feature = "optimism")] Some(NetworkVariant::Optimism) => { let provider = ProviderBuilder::::from_config(&config)?.build()?; @@ -791,6 +794,7 @@ pub async fn run_command(args: CastArgs) -> Result<()> { CastSubcommand::DecodeTransaction { tx, network } => { let tx = stdin::unwrap_line(tx)?; let decoded_tx = match network { + #[cfg(feature = "optimism")] Some(NetworkVariant::Optimism) => { SimpleCast::decode_raw_transaction::(&tx)? } @@ -809,6 +813,9 @@ pub async fn run_command(args: CastArgs) -> Result<()> { CastSubcommand::Erc20Token { command } => command.run().await?, CastSubcommand::Tip20Token { command } => command.run().await?, CastSubcommand::Keychain { command } => command.run().await?, + CastSubcommand::Tempo { command } => command.run().await?, + CastSubcommand::VirtualAddress { command } => command.run().await?, + #[cfg(feature = "optimism")] CastSubcommand::DAEstimate(cmd) => { cmd.run().await?; } diff --git a/crates/cast/src/cmd/batch_mktx.rs b/crates/cast/src/cmd/batch_mktx.rs index ae5c9668f522f..e25798086d512 100644 --- a/crates/cast/src/cmd/batch_mktx.rs +++ b/crates/cast/src/cmd/batch_mktx.rs @@ -9,7 +9,7 @@ use crate::{ }; use alloy_consensus::SignableTransaction; use alloy_eips::eip2718::Encodable2718; -use alloy_network::{EthereumWallet, NetworkTransactionBuilder}; +use alloy_network::{EthereumWallet, NetworkTransactionBuilder, TransactionBuilder}; use alloy_primitives::Address; use alloy_provider::Provider; use alloy_signer::Signer; @@ -17,7 +17,7 @@ use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, LoadConfig}, + utils::{self, LoadConfig, maybe_print_resolved_lane, resolve_lane}, }; use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; use tempo_alloy::TempoNetwork; @@ -53,7 +53,7 @@ pub struct BatchMakeTxArgs { impl BatchMakeTxArgs { pub async fn run(self) -> Result<()> { - let Self { calls, tx, eth, raw_unsigned, ethsign } = self; + let Self { calls, mut tx, eth, raw_unsigned, ethsign } = self; let has_nonce = tx.nonce.is_some(); if calls.is_empty() { @@ -63,6 +63,10 @@ impl BatchMakeTxArgs { let config = eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx.tempo.nonce_key` from the lane. + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + // Resolve signer to detect keychain mode let (signer, tempo_access_key) = eth.wallet.maybe_signer().await?; @@ -92,14 +96,14 @@ impl BatchMakeTxArgs { sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?; - // Build transaction request with calls - let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; - - // Set key_id for access key transactions + // Preserve key_id for modes that do not call build_with_access_key, such as raw unsigned. if let Some(ref access_key) = tempo_access_key { - builder.tx.set_key_id(access_key.key_address); + tx.tempo.key_id = Some(access_key.key_address); } + // Build transaction request with calls + let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; + // Set calls on the transaction builder.tx.calls = tempo_calls; @@ -117,6 +121,7 @@ impl BatchMakeTxArgs { let from = eth.wallet.from.unwrap_or(Address::ZERO); let (tx, _) = tx_builder.build(from).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let raw_tx = alloy_primitives::hex::encode_prefixed(tx.build_unsigned()?.encoded_for_signing()); sh_println!("{raw_tx}")?; @@ -125,6 +130,7 @@ impl BatchMakeTxArgs { if ethsign { let (tx, _) = tx_builder.build(config.sender).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let signed_tx = provider.sign_transaction(tx).await?; sh_println!("{signed_tx}")?; return Ok(()); @@ -137,7 +143,9 @@ impl BatchMakeTxArgs { }; let signed_tx = if let Some(ref access_key) = tempo_access_key { - let (tx, _) = tx_builder.build(access_key.wallet_address).await?; + let (tx, _) = + tx_builder.build_with_access_key(access_key.wallet_address, access_key).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let raw_tx = tx .sign_with_access_key( &provider, @@ -151,6 +159,7 @@ impl BatchMakeTxArgs { } else { tx::validate_from_address(eth.wallet.from, Signer::address(&signer))?; let (tx, _) = tx_builder.build(&signer).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; let envelope = tx.build(&EthereumWallet::new(signer)).await?; alloy_primitives::hex::encode(envelope.encoded_2718()) }; diff --git a/crates/cast/src/cmd/batch_send.rs b/crates/cast/src/cmd/batch_send.rs index ec7254b08e2f5..33128ae897898 100644 --- a/crates/cast/src/cmd/batch_send.rs +++ b/crates/cast/src/cmd/batch_send.rs @@ -9,14 +9,14 @@ use crate::{ cmd::send::{cast_send, cast_send_with_access_key}, tx::{self, CastTxBuilder, SendTxOpts}, }; -use alloy_network::EthereumWallet; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::Signer; use clap::Parser; use eyre::{Result, eyre}; use foundry_cli::{ opts::TransactionOpts, - utils::{self, LoadConfig}, + utils::{self, LoadConfig, maybe_print_resolved_lane, resolve_lane}, }; use foundry_common::provider::ProviderBuilder; use std::time::Duration; @@ -50,7 +50,7 @@ pub struct BatchSendArgs { impl BatchSendArgs { pub async fn run(self) -> Result<()> { - let Self { calls, send_tx, tx, unlocked } = self; + let Self { calls, send_tx, mut tx, unlocked } = self; if calls.is_empty() { return Err(eyre!("No calls specified. Use --call to specify at least one call.")); @@ -59,6 +59,10 @@ impl BatchSendArgs { let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx.tempo.nonce_key` from the lane. + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + if let Some(interval) = send_tx.poll_interval { provider.client().set_poll_interval(Duration::from_secs(interval)) } @@ -93,14 +97,14 @@ impl BatchSendArgs { sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?; - // Build transaction request with calls - let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; - - // Set key_id for access key transactions + // Preserve key_id for modes that do not call build_with_access_key, such as unlocked. if let Some(ref access_key) = tempo_access_key { - builder.tx.set_key_id(access_key.key_address); + tx.tempo.key_id = Some(access_key.key_address); } + // Build transaction request with calls + let mut builder = CastTxBuilder::::new(&provider, tx, &config).await?; + // Access the inner tx and set calls builder.tx.calls = tempo_calls; @@ -116,6 +120,7 @@ impl BatchSendArgs { if unlocked { let (tx, _) = builder.build(config.sender).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; cast_send( provider, tx, @@ -132,7 +137,12 @@ impl BatchSendArgs { }; if let Some(ref access_key) = tempo_access_key { - let (tx_request, _) = builder.build(access_key.wallet_address).await?; + let (tx_request, _) = + builder.build_with_access_key(access_key.wallet_address, access_key).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; cast_send_with_access_key( &provider, tx_request, @@ -146,6 +156,10 @@ impl BatchSendArgs { } else { tx::validate_from_address(send_tx.eth.wallet.from, Signer::address(&signer))?; let (tx_request, _) = builder.build(&signer).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; let wallet = EthereumWallet::from(signer); let provider = AlloyProviderBuilder::<_, _, TempoNetwork>::default() .wallet(wallet) diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index 3637f166a53df..63ea17f707e03 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -33,10 +33,12 @@ use foundry_config::{ value::{Dict, Map}, }, }; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ core::{ FoundryBlock, FoundryTransaction, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork}, + evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork}, }, executors::TracingExecutor, opts::EvmOpts, @@ -222,18 +224,19 @@ impl CallArgs { return self.run_curl().await; } if self.tx.tempo.is_tempo() { - self.run_with_network::().await - } else { - let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); - let mut evm_opts = figment.extract::()?; - evm_opts.infer_network_from_fork().await; + return self.run_with_network::().await; + } - if evm_opts.networks.is_optimism() { - self.run_with_network::().await - } else { - self.run_with_network::().await - } + let figment = self.rpc.clone().into_figment(self.with_local_artifacts).merge(&self); + let mut evm_opts = figment.extract::()?; + evm_opts.infer_network_from_fork().await; + + #[cfg(feature = "optimism")] + if evm_opts.networks.is_optimism() { + return self.run_with_network::().await; } + + self.run_with_network::().await } pub async fn run_with_network(self) -> Result<()> diff --git a/crates/cast/src/cmd/keychain.rs b/crates/cast/src/cmd/keychain.rs index 8b7d80786dfad..897a01c39202a 100644 --- a/crates/cast/src/cmd/keychain.rs +++ b/crates/cast/src/cmd/keychain.rs @@ -1,9 +1,12 @@ use alloy_ens::NameOrAddress; -use alloy_network::EthereumWallet; +use std::time::Duration; + +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, U256, hex, keccak256}; -use alloy_provider::ProviderBuilder as AlloyProviderBuilder; +use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::Signer; use alloy_sol_types::SolCall; +use alloy_transport::TransportError; use chrono::DateTime; use clap::Parser; use eyre::Result; @@ -12,11 +15,16 @@ use foundry_cli::{ utils::LoadConfig, }; use foundry_common::{ + FoundryTransactionBuilder, provider::ProviderBuilder, - shell, - tempo::{self, KeyType, KeysFile, WalletType, read_tempo_keys_file, tempo_keys_path}, + sh_warn, shell, + tempo::{ + self, KeyType, KeysFile, TEMPO_BROWSER_GAS_BUFFER, WalletType, read_tempo_keys_file, + tempo_keys_path, + }, }; use foundry_evm::hardfork::TempoHardfork; +use serde::Deserialize; use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; use tempo_contracts::precompiles::{ ACCOUNT_KEYCHAIN_ADDRESS, IAccountKeychain, @@ -24,13 +32,16 @@ use tempo_contracts::precompiles::{ CallScope, KeyInfo, KeyRestrictions, LegacyTokenLimit, SelectorRule, SignatureType, TokenLimit, }, + ITIP20, PATH_USD_ADDRESS, account_keychain::{authorizeKeyCall, legacyAuthorizeKeyCall}, }; use yansi::Paint; +use foundry_cli::utils::{maybe_print_resolved_lane, resolve_lane}; + use crate::{ - cmd::send::{cast_send, cast_send_with_access_key}, - tx::{CastTxBuilder, SendTxOpts}, + cmd::send::cast_send, + tx::{CastTxBuilder, CastTxSender, SendTxOpts}, }; /// Tempo keychain management commands. @@ -62,6 +73,19 @@ pub enum KeychainSubcommand { rpc: RpcOpts, }, + /// Inspect an access key policy using the local key registry and on-chain state. + Inspect { + /// The key address to inspect. + key_address: Address, + + /// Root account address. Required when the key is not present in the local keys.toml. + #[arg(long, visible_alias = "wallet-address", value_name = "ADDRESS")] + root_account: Option
, + + #[command(flatten)] + rpc: RpcOpts, + }, + /// Authorize a new key on-chain via the AccountKeychain precompile. #[command(visible_alias = "auth")] Authorize { @@ -183,8 +207,92 @@ pub enum KeychainSubcommand { #[command(flatten)] send_tx: SendTxOpts, }, + + /// Read or edit TIP-1011 access-key permissions. + Policy { + #[command(subcommand)] + command: KeychainPolicySubcommand, + }, +} + +/// Higher-level access-key policy editing commands. +#[derive(Debug, Parser)] +pub enum KeychainPolicySubcommand { + /// Add or widen an allowed call rule for a target contract. + AddCall { + /// The key address to update. + key_address: Address, + + /// Root account address. Required when the key is not present in the local keys.toml. + #[arg(long, visible_alias = "wallet-address", value_name = "ADDRESS")] + root_account: Option
, + + /// Target contract address. + #[arg(long)] + target: Address, + + /// Function selector, full signature, or known TIP-20 shorthand. + #[arg(long, value_parser = parse_selector_arg)] + selector: SelectorArg, + + /// Optional recipient/spender restrictions for selector calls. + #[arg(long, value_delimiter = ',')] + recipients: Vec
, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Update a token spending limit amount for a key. + SetLimit { + /// The key address to update. + key_address: Address, + + /// Token address, numeric TIP-20 token id, or PathUSD. + #[arg(long, value_parser = parse_policy_token)] + token: Address, + + /// New raw token-denominated limit. + #[arg(long)] + amount: U256, + + /// Limit period such as 7d, 24h, or 3600s. + /// + /// The current AccountKeychain update entrypoint cannot change periods, so non-zero + /// values are rejected. + #[arg(long, value_parser = parse_period)] + period: Option, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, + + /// Remove all allowed-call rules for a target contract. + RemoveTarget { + /// The key address to update. + key_address: Address, + + /// Target contract address to remove. + #[arg(long)] + target: Address, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + send_tx: SendTxOpts, + }, } +#[derive(Debug, Clone, Copy)] +pub struct SelectorArg([u8; 4]); + fn parse_signature_type(s: &str) -> Result { match s.to_lowercase().as_str() { "secp256k1" => Ok(SignatureType::Secp256k1), @@ -203,6 +311,15 @@ const fn signature_type_name(t: &SignatureType) -> &'static str { } } +const fn signature_type_label(t: &SignatureType) -> &'static str { + match t { + SignatureType::Secp256k1 => "Secp256k1", + SignatureType::P256 => "P256", + SignatureType::WebAuthn => "WebAuthn", + _ => "unknown", + } +} + const fn key_type_name(t: &KeyType) -> &'static str { match t { KeyType::Secp256k1 => "secp256k1", @@ -211,6 +328,14 @@ const fn key_type_name(t: &KeyType) -> &'static str { } } +const fn key_type_label(t: &KeyType) -> &'static str { + match t { + KeyType::Secp256k1 => "Secp256k1", + KeyType::P256 => "P256", + KeyType::WebAuthn => "WebAuthn", + } +} + const fn wallet_type_name(t: &WalletType) -> &'static str { match t { WalletType::Local => "local", @@ -332,6 +457,48 @@ fn parse_selector_bytes(s: &str) -> Result<[u8; 4], String> { } } +fn parse_selector_arg(s: &str) -> Result { + parse_selector_bytes(s).map(SelectorArg) +} + +fn parse_policy_token(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "pathusd" | "path_usd" | "path-usd" | "usd" => Ok(PATH_USD_ADDRESS), + _ => foundry_cli::utils::parse_fee_token_address(s).map_err(|e| e.to_string()), + } +} + +fn parse_period(s: &str) -> Result { + let s = s.trim(); + if s.is_empty() { + return Err("period cannot be empty".to_string()); + } + + let split = s.find(|c: char| !c.is_ascii_digit()).unwrap_or(s.len()); + if split == 0 { + return Err(format!( + "invalid period '{s}': expected a number followed by s, m, h, d, or w" + )); + } + + let value: u64 = + s[..split].parse().map_err(|e| format!("invalid period value '{}': {e}", &s[..split]))?; + let multiplier = match &s[split..].to_ascii_lowercase()[..] { + "" | "s" => 1, + "m" => 60, + "h" => 60 * 60, + "d" => 24 * 60 * 60, + "w" => 7 * 24 * 60 * 60, + unit => { + return Err(format!( + "invalid period unit '{unit}' in '{s}' (expected s, m, h, d, or w)" + )); + } + }; + + value.checked_mul(multiplier).ok_or_else(|| format!("period '{s}' is too large")) +} + /// Represents a single scope entry in JSON format for `--scopes`. #[derive(serde::Deserialize)] struct JsonCallScope { @@ -402,6 +569,9 @@ impl KeychainSubcommand { Self::Check { wallet_address, key_address, rpc } => { run_check(wallet_address, key_address, rpc).await } + Self::Inspect { key_address, root_account, rpc } => { + run_inspect(key_address, root_account, rpc).await + } Self::Authorize { key_address, key_type, @@ -443,6 +613,40 @@ impl KeychainSubcommand { Self::RemoveScope { key_address, target, tx, send_tx } => { run_remove_scope(key_address, target, tx, send_tx).await } + Self::Policy { command } => command.run().await, + } + } +} + +impl KeychainPolicySubcommand { + pub async fn run(self) -> Result<()> { + match self { + Self::AddCall { + key_address, + root_account, + target, + selector, + recipients, + tx, + send_tx, + } => { + run_policy_add_call( + key_address, + root_account, + target, + selector.0, + recipients, + tx, + send_tx, + ) + .await + } + Self::SetLimit { key_address, token, amount, period, tx, send_tx } => { + run_policy_set_limit(key_address, token, amount, period, tx, send_tx).await + } + Self::RemoveTarget { key_address, target, tx, send_tx } => { + run_remove_scope(key_address, target, tx, send_tx).await + } } } } @@ -500,6 +704,143 @@ fn run_show(wallet_address: Address) -> Result<()> { Ok(()) } +#[derive(Debug, Clone)] +struct LocalLimitMetadata { + token: Address, + amount: String, +} + +#[derive(Debug, Clone)] +struct KeyMetadata { + root_account: Address, + key_type: Option, + limits: Vec, +} + +#[derive(Debug, Clone)] +struct InspectedLimit { + token: Address, + configured_amount: Option, + remaining: U256, + period_end: Option, +} + +#[derive(Debug, Clone)] +enum AllowedCallsView { + Unsupported, + Unrestricted, + Scoped(Vec), +} + +/// `cast keychain inspect ` — inspect on-chain key policy. +async fn run_inspect( + key_address: Address, + root_account: Option
, + rpc: RpcOpts, +) -> Result<()> { + let metadata = resolve_key_metadata(key_address, root_account)?; + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + let info: KeyInfo = provider.get_keychain_key(metadata.root_account, key_address).await?; + let provisioned = info.keyId != Address::ZERO; + let is_t3 = is_tempo_hardfork_active(&provider, TempoHardfork::T3).await?; + + let mut limits = Vec::new(); + if info.enforceLimits { + for local_limit in &metadata.limits { + let (remaining, period_end) = if is_t3 { + let limit = provider + .get_keychain_remaining_limit_with_period( + metadata.root_account, + key_address, + local_limit.token, + ) + .await?; + (limit.remaining, Some(limit.periodEnd)) + } else { + let remaining = provider + .account_keychain() + .getRemainingLimit(metadata.root_account, key_address, local_limit.token) + .call() + .await?; + (remaining, None) + }; + + limits.push(InspectedLimit { + token: local_limit.token, + configured_amount: Some(local_limit.amount.clone()), + remaining, + period_end, + }); + } + } + + let allowed_calls = if is_t3 { + let allowed = provider + .account_keychain() + .getAllowedCalls(metadata.root_account, key_address) + .call() + .await?; + if allowed.isScoped { + AllowedCallsView::Scoped(allowed.scopes) + } else { + AllowedCallsView::Unrestricted + } + } else { + AllowedCallsView::Unsupported + }; + + if shell::is_json() { + let key_type = if provisioned { + signature_type_name(&info.signatureType).to_string() + } else { + metadata + .key_type + .map(|key_type| key_type_name(&key_type).to_string()) + .unwrap_or_else(|| "unknown".to_string()) + }; + let json = serde_json::json!({ + "root_account": metadata.root_account.to_string(), + "key_id": key_address.to_string(), + "provisioned": provisioned, + "type": key_type, + "expiry": provisioned.then_some(info.expiry), + "expiry_human": provisioned.then(|| format_expiry_for_inspect(info.expiry)), + "enforce_limits": info.enforceLimits, + "is_revoked": info.isRevoked, + "limits": limits.iter().map(inspected_limit_to_json).collect::>(), + "allowed_calls": allowed_calls_to_json(&allowed_calls), + }); + sh_println!("{}", serde_json::to_string_pretty(&json)?)?; + return Ok(()); + } + + let key_type = if provisioned { + signature_type_label(&info.signatureType) + } else { + metadata.key_type.map(|key_type| key_type_label(&key_type)).unwrap_or("unknown") + }; + + sh_println!("Root account: {}", metadata.root_account)?; + sh_println!("Key id: {key_address}")?; + sh_println!("Type: {key_type}")?; + + if info.isRevoked { + sh_println!("Status: revoked")?; + } else if !provisioned { + sh_println!("Status: not provisioned")?; + } else { + sh_println!("Status: active")?; + sh_println!("Expiry: {}", format_expiry_for_inspect(info.expiry))?; + } + + print_inspected_limits(info.enforceLimits, &limits)?; + print_allowed_calls(&allowed_calls)?; + + Ok(()) +} + /// `cast keychain check` / `cast keychain info` — query on-chain key status. async fn run_check(wallet_address: Address, key_address: Address, rpc: RpcOpts) -> Result<()> { let config = rpc.load_config()?; @@ -584,7 +925,7 @@ async fn run_authorize( let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; - let calldata = if provider.is_hardfork_active(TempoHardfork::T3).await? { + let calldata = if is_tempo_hardfork_active(&provider, TempoHardfork::T3).await? { // T3+ authorizeKey(address,SignatureType,KeyRestrictions) let restrictions = KeyRestrictions { expiry, @@ -634,7 +975,7 @@ async fn run_remaining_limit( let config = rpc.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; - let remaining: U256 = if provider.is_hardfork_active(TempoHardfork::T3).await? { + let remaining: U256 = if is_tempo_hardfork_active(&provider, TempoHardfork::T3).await? { provider.get_keychain_remaining_limit(wallet_address, key_address, token).await? } else { // Pre-T3: use the legacy getRemainingLimit(address,address,address) @@ -646,7 +987,7 @@ async fn run_remaining_limit( }; if shell::is_json() { - sh_println!("{}", serde_json::to_string(&remaining.to_string())?)?; + sh_println!("{}", serde_json::json!({ "remaining": remaining.to_string() }))?; } else { sh_println!("{remaining}")?; } @@ -695,6 +1036,88 @@ async fn run_remove_scope( send_keychain_tx(calldata, tx_opts, &send_tx).await } +/// `cast keychain policy add-call` — merge a selector rule into a target scope. +async fn run_policy_add_call( + key_address: Address, + root_account: Option
, + target: Address, + selector: [u8; 4], + recipients: Vec
, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + let metadata = resolve_key_metadata(key_address, root_account)?; + let config = send_tx.eth.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + if !is_tempo_hardfork_active(&provider, TempoHardfork::T3).await? { + eyre::bail!("allowed-call policy editing requires the Tempo T3 hardfork"); + } + + let allowed = provider + .account_keychain() + .getAllowedCalls(metadata.root_account, key_address) + .call() + .await?; + + let new_rule = SelectorRule { selector: selector.into(), recipients }; + let existing_target = allowed + .isScoped + .then(|| allowed.scopes.into_iter().find(|scope| scope.target == target)) + .flatten(); + + let (target_scope, changed) = match existing_target { + Some(mut scope) => { + if scope.selectorRules.is_empty() { + sh_warn!( + "Allowed calls for {} already allow any selector; leaving wildcard scope unchanged", + address_label_with_address(target) + )?; + } + let changed = add_selector_rule_to_scope(&mut scope, new_rule); + (scope, changed) + } + None => (CallScope { target, selectorRules: vec![new_rule] }, true), + }; + + if !changed { + if shell::is_json() { + sh_println!( + "{}", + serde_json::json!({ "status": "already_present", "target": target.to_string() }) + )?; + } else { + sh_println!("Allowed call already present for {}", address_label_with_address(target))?; + } + return Ok(()); + } + + let calldata = + IAccountKeychain::setAllowedCallsCall { keyId: key_address, scopes: vec![target_scope] } + .abi_encode(); + send_keychain_tx(calldata, tx_opts, &send_tx).await +} + +/// `cast keychain policy set-limit` — update a spending limit amount. +async fn run_policy_set_limit( + key_address: Address, + token: Address, + amount: U256, + period: Option, + tx_opts: TransactionOpts, + send_tx: SendTxOpts, +) -> Result<()> { + if period.is_some_and(|period| period != 0) { + eyre::bail!( + "--period is not supported by the current AccountKeychain updateSpendingLimit \ + precompile; periods can only be set when authorizing a key" + ); + } + + // updateSpendingLimit authorizes against msg.sender; the root account is not part of calldata. + run_update_limit(key_address, token, amount, tx_opts, send_tx).await +} + /// Shared helper to send a keychain precompile transaction. async fn send_keychain_tx( calldata: Vec, @@ -702,16 +1125,22 @@ async fn send_keychain_tx( send_tx: &SendTxOpts, ) -> Result<()> { let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; + let print_sponsor_hash = tx_opts.tempo.print_sponsor_hash; + let tempo_sponsor = + if print_sponsor_hash { None } else { tx_opts.tempo.sponsor_config().await? }; let config = send_tx.eth.load_config()?; let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); let provider = ProviderBuilder::::from_config(&config)?.build()?; - // Inject key_id for correct gas estimation with keychain signature overhead. - if let Some(ref ak) = tempo_access_key { - tx_opts.tempo.key_id = Some(ak.key_address); + if let Some(interval) = send_tx.poll_interval { + provider.client().set_poll_interval(Duration::from_secs(interval)); } + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx_opts.tempo.nonce_key` from the lane. + let resolved_lane = resolve_lane(&mut tx_opts.tempo, &config.root)?; + let builder = CastTxBuilder::new(&provider, tx_opts, &config) .await? .with_to(Some(NameOrAddress::Address(ACCOUNT_KEYCHAIN_ADDRESS))) @@ -719,27 +1148,70 @@ async fn send_keychain_tx( .with_code_sig_and_args(None, Some(hex::encode_prefixed(&calldata)), vec![]) .await?; - if let Some(ref ak) = tempo_access_key { - let signer = - signer.as_ref().ok_or_else(|| eyre::eyre!("signer required for access key"))?; - let (tx, _) = builder.build(ak.wallet_address).await?; - cast_send_with_access_key( - &provider, - tx, - signer, - ak, - send_tx.cast_async, - send_tx.confirmations, - timeout, - ) - .await?; + // Keychain management calls are authorized by the root account. Access keys can use their + // permissions, but cannot mutate their own key policy. + let browser = send_tx.browser.run::().await?; + + if print_sponsor_hash { + let from = if let Some(ref browser) = browser { + browser.address() + } else { + signer + .as_ref() + .ok_or_else(|| { + eyre::eyre!( + "--tempo.print-sponsor-hash requires a root account signer, such as \ + --browser, --private-key, or --keystore" + ) + })? + .address() + }; + + let (tx, _) = builder.build(from).await?; + let hash = tx + .compute_sponsor_hash(from) + .ok_or_else(|| eyre::eyre!("This network does not support sponsored transactions"))?; + if shell::is_json() { + sh_println!("{}", serde_json::json!({ "sponsor_hash": format!("{hash:?}") }))?; + } else { + sh_println!("{hash:?}")?; + } + return Ok(()); + } + + if let Some(browser) = browser { + let chain = builder.chain(); + let (mut tx, _) = builder.build(browser.address()).await?; + if chain.is_tempo() + && let Some(gas) = tx.gas_limit() + { + tx.set_gas_limit(gas + TEMPO_BROWSER_GAS_BUFFER); + } + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, browser.address()).await?; + } + + let tx_hash = browser.send_transaction_via_browser(tx).await?; + CastTxSender::new(&provider) + .print_tx_result(tx_hash, send_tx.cast_async, send_tx.confirmations, timeout) + .await?; + } else if tempo_access_key.is_some() { + eyre::bail!( + "keychain policy changes must be signed by the root account; the selected `--from` \ + resolved to a Tempo access key. Use `--browser` for passkey roots, or pass a root \ + account signer with `--private-key`, `--keystore`, Ledger, Trezor, AWS, GCP, or Turnkey." + ); } else { let signer = match signer { Some(s) => s, None => send_tx.eth.wallet.signer().await?, }; let from = signer.address(); - let (tx, _) = builder.build(from).await?; + let (mut tx, _) = builder.build(from).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, from).await?; + } let wallet = EthereumWallet::from(signer); let provider = AlloyProviderBuilder::<_, _, TempoNetwork>::default() @@ -753,6 +1225,361 @@ async fn send_keychain_tx( Ok(()) } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct AnvilNodeInfo { + hard_fork: Option, + network: Option, +} + +async fn is_tempo_hardfork_active

(provider: &P, hardfork: TempoHardfork) -> Result +where + P: Provider, +{ + match provider.is_hardfork_active(hardfork).await { + Ok(active) => Ok(active), + Err(err) if is_rpc_method_not_found(&err) => { + match anvil_tempo_hardfork_active(provider, hardfork).await { + Ok(Some(active)) => Ok(active), + _ => Err(err.into()), + } + } + Err(err) => Err(err.into()), + } +} + +async fn anvil_tempo_hardfork_active

( + provider: &P, + hardfork: TempoHardfork, +) -> Result, TransportError> +where + P: Provider, +{ + let info = provider.raw_request::<_, AnvilNodeInfo>("anvil_nodeInfo".into(), ()).await?; + Ok(active_from_anvil_node_info(&info, hardfork)) +} + +fn active_from_anvil_node_info(info: &AnvilNodeInfo, hardfork: TempoHardfork) -> Option { + (info.network.as_deref() == Some("tempo")).then(|| { + info.hard_fork + .as_deref() + .and_then(|active_hardfork| active_hardfork.parse::().ok()) + .is_some_and(|active_hardfork| active_hardfork >= hardfork) + }) +} + +fn is_rpc_method_not_found(err: &TransportError) -> bool { + err.as_error_resp().is_some_and(|payload| payload.code == -32601) +} + +fn resolve_key_metadata( + key_address: Address, + root_account: Option

, +) -> Result { + let keys_file = read_tempo_keys_file(); + + if let Some(root_account) = root_account { + if let Some(keys_file) = keys_file.as_ref() + && let Some(entry) = keys_file.keys.iter().find(|entry| { + entry.wallet_address == root_account + && key_entry_effective_key(entry) == key_address + }) + { + return Ok(key_metadata_from_entry(entry)); + } + + return Ok(KeyMetadata { root_account, key_type: None, limits: Vec::new() }); + } + + let Some(keys_file) = keys_file.as_ref() else { + eyre::bail!( + "key {key_address} was not found because the local keys file could not be read at {}; pass --root-account", + tempo_keys_path_display() + ); + }; + + let matches: Vec<_> = keys_file + .keys + .iter() + .filter(|entry| key_entry_effective_key(entry) == key_address) + .collect(); + + if matches.is_empty() { + eyre::bail!( + "key {key_address} was not found in {}; pass --root-account", + tempo_keys_path_display() + ); + } + + let root_account = matches[0].wallet_address; + if matches.iter().any(|entry| entry.wallet_address != root_account) { + eyre::bail!( + "key {key_address} matches multiple root accounts in {}; pass --root-account", + tempo_keys_path_display() + ); + } + + let entry = + matches.iter().copied().find(|entry| !entry.limits.is_empty()).unwrap_or(matches[0]); + Ok(key_metadata_from_entry(entry)) +} + +fn key_entry_effective_key(entry: &tempo::KeyEntry) -> Address { + entry.key_address.unwrap_or(entry.wallet_address) +} + +fn key_metadata_from_entry(entry: &tempo::KeyEntry) -> KeyMetadata { + KeyMetadata { + root_account: entry.wallet_address, + key_type: Some(entry.key_type), + limits: entry + .limits + .iter() + .map(|limit| LocalLimitMetadata { token: limit.currency, amount: limit.limit.clone() }) + .collect(), + } +} + +fn tempo_keys_path_display() -> String { + tempo_keys_path() + .map(|path| path.display().to_string()) + .unwrap_or_else(|| "(unknown)".to_string()) +} + +fn add_selector_rule_to_scope(scope: &mut CallScope, rule: SelectorRule) -> bool { + if scope.selectorRules.is_empty() { + return false; + } + + let Some(existing_rule) = + scope.selectorRules.iter_mut().find(|existing| existing.selector == rule.selector) + else { + scope.selectorRules.push(rule); + return true; + }; + + if existing_rule.recipients.is_empty() { + return false; + } + + if rule.recipients.is_empty() { + existing_rule.recipients = Vec::new(); + return true; + } + + let mut changed = false; + for recipient in rule.recipients { + if !existing_rule.recipients.contains(&recipient) { + existing_rule.recipients.push(recipient); + changed = true; + } + } + changed +} + +fn inspected_limit_to_json(limit: &InspectedLimit) -> serde_json::Value { + serde_json::json!({ + "token": limit.token.to_string(), + "token_label": address_label(limit.token), + "configured_amount": limit.configured_amount.as_deref(), + "remaining": limit.remaining.to_string(), + "period_end": limit.period_end, + "period_end_human": limit.period_end.and_then(|period_end| { + (period_end != 0).then(|| format_period_end(period_end)) + }), + }) +} + +fn allowed_calls_to_json(allowed_calls: &AllowedCallsView) -> serde_json::Value { + match allowed_calls { + AllowedCallsView::Unsupported => serde_json::json!({ + "mode": "unsupported", + "scopes": [], + }), + AllowedCallsView::Unrestricted => serde_json::json!({ + "mode": "any", + "scopes": [], + }), + AllowedCallsView::Scoped(scopes) => serde_json::json!({ + "mode": if scopes.is_empty() { "none" } else { "scoped" }, + "scopes": scopes.iter().map(call_scope_to_json).collect::>(), + }), + } +} + +fn call_scope_to_json(scope: &CallScope) -> serde_json::Value { + serde_json::json!({ + "target": scope.target.to_string(), + "target_label": address_label(scope.target), + "selectors": scope.selectorRules.iter().map(selector_rule_to_json).collect::>(), + }) +} + +fn selector_rule_to_json(rule: &SelectorRule) -> serde_json::Value { + serde_json::json!({ + "selector": selector_hex(&rule.selector.0), + "signature": selector_signature(&rule.selector.0), + "recipients": rule.recipients.iter().map(ToString::to_string).collect::>(), + }) +} + +fn print_inspected_limits(enforce_limits: bool, limits: &[InspectedLimit]) -> Result<()> { + if !enforce_limits { + sh_println!("Limits: none")?; + return Ok(()); + } + + sh_println!("Limits:")?; + if limits.is_empty() { + sh_println!(" enforced, but no local limit metadata was found")?; + return Ok(()); + } + + for limit in limits { + let configured = limit.configured_amount.as_deref().unwrap_or("unknown"); + let period = limit + .period_end + .and_then(|period_end| { + (period_end != 0).then(|| format!(" ({})", format_period_end(period_end))) + }) + .unwrap_or_default(); + sh_println!( + " {}: {} / {} remaining{}", + address_label(limit.token), + limit.remaining, + configured, + period + )?; + } + + Ok(()) +} + +fn print_allowed_calls(allowed_calls: &AllowedCallsView) -> Result<()> { + match allowed_calls { + AllowedCallsView::Unsupported => sh_println!("Allowed calls: unsupported before T3")?, + AllowedCallsView::Unrestricted => sh_println!("Allowed calls: any")?, + AllowedCallsView::Scoped(scopes) if scopes.is_empty() => { + sh_println!("Allowed calls: none")?; + } + AllowedCallsView::Scoped(scopes) => { + sh_println!("Allowed calls:")?; + for scope in scopes { + sh_println!(" {}:", address_label_with_address(scope.target))?; + if scope.selectorRules.is_empty() { + sh_println!(" any selector")?; + continue; + } + + for rule in &scope.selectorRules { + sh_println!( + " {} -> {}", + format_selector(&rule.selector.0), + format_recipients(&rule.recipients) + )?; + } + } + } + } + + Ok(()) +} + +fn address_label(address: Address) -> String { + if address == PATH_USD_ADDRESS { "PathUSD".to_string() } else { address.to_string() } +} + +fn address_label_with_address(address: Address) -> String { + if address == PATH_USD_ADDRESS { format!("PathUSD ({address})") } else { address.to_string() } +} + +fn format_selector(selector: &[u8; 4]) -> String { + selector_signature(selector).map(str::to_string).unwrap_or_else(|| selector_hex(selector)) +} + +fn selector_signature(selector: &[u8; 4]) -> Option<&'static str> { + if selector == &ITIP20::transferCall::SELECTOR { + Some("transfer(address,uint256)") + } else if selector == &ITIP20::approveCall::SELECTOR { + Some("approve(address,uint256)") + } else if selector == &ITIP20::transferFromCall::SELECTOR { + Some("transferFrom(address,address,uint256)") + } else if selector == &ITIP20::transferWithMemoCall::SELECTOR { + Some("transferWithMemo(address,uint256,bytes32)") + } else if selector == &ITIP20::transferFromWithMemoCall::SELECTOR { + Some("transferFromWithMemo(address,address,uint256,bytes32)") + } else if selector == &ITIP20::mintCall::SELECTOR { + Some("mint(address,uint256)") + } else if selector == &ITIP20::burnCall::SELECTOR { + Some("burn(uint256)") + } else { + None + } +} + +fn selector_hex(selector: &[u8; 4]) -> String { + hex::encode_prefixed(selector) +} + +fn format_recipients(recipients: &[Address]) -> String { + if recipients.is_empty() { + return "any recipient".to_string(); + } + + let recipients = recipients.iter().map(ToString::to_string).collect::>().join(", "); + format!("recipients [{recipients}]") +} + +fn format_expiry_for_inspect(expiry: u64) -> String { + if expiry == u64::MAX { + return "never".to_string(); + } + + format!("{} ({})", format_timestamp_iso(expiry), format_relative_timestamp(expiry)) +} + +fn format_period_end(period_end: u64) -> String { + format!("period resets {}", format_relative_timestamp(period_end)) +} + +fn format_timestamp_iso(timestamp: u64) -> String { + DateTime::from_timestamp(timestamp as i64, 0) + .map(|dt| dt.format("%Y-%m-%dT%H:%M:%SZ").to_string()) + .unwrap_or_else(|| timestamp.to_string()) +} + +fn format_relative_timestamp(timestamp: u64) -> String { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + if timestamp == now { + "now".to_string() + } else if timestamp > now { + format!("in {}", format_duration_words(timestamp - now)) + } else { + format!("{} ago", format_duration_words(now - timestamp)) + } +} + +fn format_duration_words(seconds: u64) -> String { + const MINUTE: u64 = 60; + const HOUR: u64 = 60 * MINUTE; + const DAY: u64 = 24 * HOUR; + + if seconds >= DAY { + let days = seconds / DAY; + if days == 1 { "1 day".to_string() } else { format!("{days} days") } + } else if seconds >= HOUR { + format!("{}h", seconds / HOUR) + } else if seconds >= MINUTE { + format!("{}m", seconds / MINUTE) + } else { + format!("{seconds}s") + } +} + fn format_expiry(expiry: u64) -> String { if expiry == u64::MAX { return "never".to_string(); @@ -842,6 +1669,7 @@ fn key_entry_to_json(entry: &tempo::KeyEntry) -> serde_json::Value { #[cfg(test)] mod tests { use super::*; + use alloy_json_rpc::ErrorPayload; use std::str::FromStr; #[test] @@ -967,4 +1795,144 @@ mod tests { let json = r#"[{"target":"0x20c0000000000000000000000000000000000001","selectors":[{"selector":"transfer","recipients":[],"bogus":true}]}]"#; assert!(parse_scopes_json(json).is_err()); } + + #[test] + fn test_parse_policy_token_path_usd() { + assert_eq!(parse_policy_token("PathUSD").unwrap(), PATH_USD_ADDRESS); + assert_eq!(parse_policy_token("path-usd").unwrap(), PATH_USD_ADDRESS); + } + + #[test] + fn test_parse_period_units() { + assert_eq!(parse_period("0").unwrap(), 0); + assert_eq!(parse_period("30s").unwrap(), 30); + assert_eq!(parse_period("5m").unwrap(), 300); + assert_eq!(parse_period("2h").unwrap(), 7200); + assert_eq!(parse_period("7d").unwrap(), 604800); + assert_eq!(parse_period("2w").unwrap(), 1209600); + assert!(parse_period("1mo").is_err()); + } + + #[test] + fn test_add_selector_rule_merges_recipients() { + let first = Address::from_str("0x1111111111111111111111111111111111111111").unwrap(); + let second = Address::from_str("0x2222222222222222222222222222222222222222").unwrap(); + let mut scope = CallScope { + target: PATH_USD_ADDRESS, + selectorRules: vec![SelectorRule { + selector: parse_selector_bytes("transfer").unwrap().into(), + recipients: vec![first], + }], + }; + + let changed = add_selector_rule_to_scope( + &mut scope, + SelectorRule { + selector: parse_selector_bytes("transfer").unwrap().into(), + recipients: vec![second], + }, + ); + + assert!(changed); + assert_eq!(scope.selectorRules.len(), 1); + assert_eq!(scope.selectorRules[0].recipients, vec![first, second]); + } + + #[test] + fn test_add_selector_rule_empty_recipients_widens_to_any() { + let first = Address::from_str("0x1111111111111111111111111111111111111111").unwrap(); + let mut scope = CallScope { + target: PATH_USD_ADDRESS, + selectorRules: vec![SelectorRule { + selector: parse_selector_bytes("approve").unwrap().into(), + recipients: vec![first], + }], + }; + + let changed = add_selector_rule_to_scope( + &mut scope, + SelectorRule { + selector: parse_selector_bytes("approve").unwrap().into(), + recipients: vec![], + }, + ); + + assert!(changed); + assert!(scope.selectorRules[0].recipients.is_empty()); + } + + #[test] + fn test_add_selector_rule_target_wildcard_is_unchanged() { + let mut scope = CallScope { target: PATH_USD_ADDRESS, selectorRules: vec![] }; + + let changed = add_selector_rule_to_scope( + &mut scope, + SelectorRule { + selector: parse_selector_bytes("transfer").unwrap().into(), + recipients: vec![], + }, + ); + + assert!(!changed); + assert!(scope.selectorRules.is_empty()); + } + + #[test] + fn test_policy_set_limit_parses() { + let key = "0x1111111111111111111111111111111111111111"; + + let command = KeychainSubcommand::try_parse_from([ + "keychain", + "policy", + "set-limit", + key, + "--token", + "PathUSD", + "--amount", + "123", + ]) + .unwrap(); + + match command { + KeychainSubcommand::Policy { + command: + KeychainPolicySubcommand::SetLimit { key_address, token, amount, period, .. }, + } => { + assert_eq!(key_address, Address::from_str(key).unwrap()); + assert_eq!(token, PATH_USD_ADDRESS); + assert_eq!(amount, U256::from(123)); + assert_eq!(period, None); + } + other => panic!("unexpected command: {other:?}"), + } + } + + #[test] + fn test_active_from_anvil_node_info_requires_tempo_network() { + let tempo_t3 = + AnvilNodeInfo { network: Some("tempo".to_string()), hard_fork: Some("T3".to_string()) }; + assert_eq!(active_from_anvil_node_info(&tempo_t3, TempoHardfork::T2), Some(true)); + assert_eq!(active_from_anvil_node_info(&tempo_t3, TempoHardfork::T3), Some(true)); + assert_eq!(active_from_anvil_node_info(&tempo_t3, TempoHardfork::T4), Some(false)); + + let ethereum_t3 = AnvilNodeInfo { + network: Some("ethereum".to_string()), + hard_fork: Some("T3".to_string()), + }; + assert_eq!(active_from_anvil_node_info(ðereum_t3, TempoHardfork::T3), None); + } + + #[test] + fn test_rpc_method_not_found_detection() { + let method_missing: TransportError = + TransportError::ErrorResp(ErrorPayload::method_not_found()); + assert!(is_rpc_method_not_found(&method_missing)); + + let internal_error: TransportError = + TransportError::ErrorResp(ErrorPayload::internal_error()); + assert!(!is_rpc_method_not_found(&internal_error)); + + let transport_error = alloy_transport::TransportErrorKind::backend_gone(); + assert!(!is_rpc_method_not_found(&transport_error)); + } } diff --git a/crates/cast/src/cmd/mktx.rs b/crates/cast/src/cmd/mktx.rs index 8aaf6e97a1827..67178cd093d7b 100644 --- a/crates/cast/src/cmd/mktx.rs +++ b/crates/cast/src/cmd/mktx.rs @@ -2,7 +2,9 @@ use crate::tx::{self, CastTxBuilder}; use alloy_consensus::{SignableTransaction, Signed}; use alloy_eips::Encodable2718; use alloy_ens::NameOrAddress; -use alloy_network::{Ethereum, EthereumWallet, Network, NetworkTransactionBuilder}; +use alloy_network::{ + Ethereum, EthereumWallet, Network, NetworkTransactionBuilder, TransactionBuilder, +}; use alloy_primitives::{Address, hex}; use alloy_provider::Provider; use alloy_signer::{Signature, Signer}; @@ -10,7 +12,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::LoadConfig, + utils::{LoadConfig, maybe_print_resolved_lane, resolve_lane}, }; use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder}; use std::{path::PathBuf, str::FromStr}; @@ -94,9 +96,13 @@ impl MakeTxArgs { N::UnsignedTx: SignableTransaction, N::TransactionRequest: FoundryTransactionBuilder, { - let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self; + let Self { to, mut sig, mut args, command, mut tx, path, eth, raw_unsigned, ethsign } = + self; let print_sponsor_hash = tx.tempo.print_sponsor_hash; + let expires_at = tx.tempo.resolve_expires(); + let tempo_sponsor = + if print_sponsor_hash { None } else { tx.tempo.sponsor_config().await? }; let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; @@ -117,6 +123,11 @@ impl MakeTxArgs { let provider = ProviderBuilder::::from_config(&config)?.build()?; + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `tx.tempo.nonce_key` from the lane. + // Must happen before `tx.clone()` so the cloned tx carries the resolved nonce_key. + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config) .await? .with_to(to) @@ -139,6 +150,10 @@ impl MakeTxArgs { return Ok(()); } + if let Some(ts) = expires_at { + sh_println!("Transaction expires at unix timestamp {ts}")?; + } + if raw_unsigned { // Build unsigned raw tx // Check if nonce is provided when --from is not specified @@ -148,11 +163,20 @@ impl MakeTxArgs { "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce" ); } + if tempo_sponsor.is_some() && eth.wallet.from.is_none() { + eyre::bail!( + "--tempo.sponsor requires --from for --raw-unsigned because the sponsor digest commits to the sender" + ); + } // Use zero address as placeholder for unsigned transactions let from = eth.wallet.from.unwrap_or(Address::ZERO); - let (tx, _) = tx_builder.build(from).await?; + let (mut tx, _) = tx_builder.build(from).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, from).await?; + } let raw_tx = hex::encode_prefixed(tx.build_unsigned()?.encoded_for_signing()); sh_println!("{raw_tx}")?; @@ -162,7 +186,11 @@ impl MakeTxArgs { if ethsign { // Use "eth_signTransaction" to sign the transaction only works if the node/RPC has // unlocked accounts. - let (tx, _) = tx_builder.build(config.sender).await?; + let (mut tx, _) = tx_builder.build(config.sender).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, config.sender).await?; + } let signed_tx = provider.sign_transaction(tx).await?; sh_println!("{signed_tx}")?; @@ -176,7 +204,11 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; - let (tx, _) = tx_builder.build(&signer).await?; + let (mut tx, _) = tx_builder.build(&signer).await?; + maybe_print_resolved_lane(resolved_lane.as_ref(), tx.nonce().unwrap_or_default())?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx, from).await?; + } let tx = tx.build(&EthereumWallet::new(signer)).await?; diff --git a/crates/cast/src/cmd/mod.rs b/crates/cast/src/cmd/mod.rs index 6a9d11f5dc61d..0b1b26615694a 100644 --- a/crates/cast/src/cmd/mod.rs +++ b/crates/cast/src/cmd/mod.rs @@ -15,6 +15,7 @@ pub mod call; pub mod constructor_args; pub mod create2; pub mod creation_code; +#[cfg(feature = "optimism")] pub mod da_estimate; pub mod erc20; pub mod estimate; @@ -28,7 +29,9 @@ pub mod rpc; pub mod run; pub mod send; pub mod storage; +pub mod tempo; pub mod tip20; pub mod trace; pub mod txpool; +pub mod vaddr; pub mod wallet; diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 84699d6cd6956..7e52a9e265f25 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -29,10 +29,12 @@ use foundry_config::{ value::{Dict, Map}, }, }; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ core::{ FoundryBlock as _, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, + evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork, TxEnvFor}, }, executors::{EvmError, Executor, TracingExecutor}, hardforks::FoundryHardfork, @@ -123,12 +125,15 @@ impl RunArgs { evm_opts.infer_network_from_fork().await; if evm_opts.networks.is_tempo() { - self.run_with_evm::().await - } else if evm_opts.networks.is_optimism() { - self.run_with_evm::().await - } else { - self.run_with_evm::().await + return self.run_with_evm::().await; + } + + #[cfg(feature = "optimism")] + if evm_opts.networks.is_optimism() { + return self.run_with_evm::().await; } + + self.run_with_evm::().await } async fn run_with_evm(self) -> Result<()> { diff --git a/crates/cast/src/cmd/send.rs b/crates/cast/src/cmd/send.rs index 2d6e248cc7a73..421aae1f2153e 100644 --- a/crates/cast/src/cmd/send.rs +++ b/crates/cast/src/cmd/send.rs @@ -8,7 +8,10 @@ use alloy_provider::{Provider, ProviderBuilder as AlloyProviderBuilder}; use alloy_signer::{Signature, Signer}; use clap::Parser; use eyre::{Result, eyre}; -use foundry_cli::{opts::TransactionOpts, utils::LoadConfig}; +use foundry_cli::{ + opts::TransactionOpts, + utils::{LoadConfig, maybe_print_resolved_lane, resolve_lane}, +}; use foundry_common::{ FoundryTransactionBuilder, fmt::{UIfmt, UIfmtReceiptExt}, @@ -119,7 +122,9 @@ impl SendTxArgs { self; let print_sponsor_hash = tx.tempo.print_sponsor_hash; - let sponsor_signature = tx.tempo.sponsor_signature; + let expires_at = tx.tempo.resolve_expires(); + let tempo_sponsor = + if print_sponsor_hash { None } else { tx.tempo.sponsor_config().await? }; let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; @@ -183,6 +188,8 @@ impl SendTxArgs { let config = send_tx.eth.load_config()?; let provider = ProviderBuilder::::from_config(&config)?.build()?; + let resolved_lane = resolve_lane(&mut tx.tempo, &config.root)?; + if let Some(interval) = send_tx.poll_interval { provider.client().set_poll_interval(Duration::from_secs(interval)) } @@ -202,13 +209,19 @@ impl SendTxArgs { // If --tempo.print-sponsor-hash was passed, build the tx, print the hash, and exit. if print_sponsor_hash { - // Use the pre-resolved signer to derive the actual sender address, since the - // sponsor hash commits to the sender. - let signer = pre_resolved_signer.as_ref().ok_or_else(|| { - eyre!("--tempo.print-sponsor-hash requires a signer (e.g. --private-key)") - })?; - let from = signer.address(); - let (tx, _) = builder.build(from).await?; + let (tx, from) = if let Some(ref ak) = access_key { + let (tx, _) = builder.build_with_access_key(ak.wallet_address, ak).await?; + (tx, ak.wallet_address) + } else { + // Use the pre-resolved signer to derive the actual sender address, since the + // sponsor hash commits to the sender. + let signer = pre_resolved_signer.as_ref().ok_or_else(|| { + eyre!("--tempo.print-sponsor-hash requires a signer (e.g. --private-key)") + })?; + let from = signer.address(); + let (tx, _) = builder.build(from).await?; + (tx, from) + }; let hash = tx .compute_sponsor_hash(from) .ok_or_else(|| eyre!("This network does not support sponsored transactions"))?; @@ -216,6 +229,10 @@ impl SendTxArgs { return Ok(()); } + if let Some(ts) = expires_at { + sh_println!("Transaction expires at unix timestamp {ts}")?; + } + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); // Launch browser signer if `--browser` flag is set @@ -245,11 +262,18 @@ impl SendTxArgs { } } - let (tx, _) = builder.build(config.sender).await?; + let (mut tx_request, _) = builder.build(config.sender).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, config.sender).await?; + } cast_send( provider, - tx, + tx_request, send_tx.cast_async, send_tx.sync, send_tx.confirmations, @@ -261,6 +285,10 @@ impl SendTxArgs { } else if let Some(browser) = browser { let chain = builder.chain(); let (mut tx_request, _) = builder.build(browser.address()).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; // Browser wallets may sign with P256/WebAuthn instead of secp256k1, which // costs more gas for signature verification on Tempo chains. Add a @@ -270,6 +298,9 @@ impl SendTxArgs { { tx_request.set_gas_limit(gas + TEMPO_BROWSER_GAS_BUFFER); } + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, browser.address()).await?; + } let tx_hash = browser.send_transaction_via_browser(tx_request).await?; @@ -283,7 +314,14 @@ impl SendTxArgs { Some(s) => s, None => send_tx.eth.wallet.signer().await?, }; - let (tx_request, _) = builder.build(ak.wallet_address).await?; + let (mut tx_request, _) = builder.build_with_access_key(ak.wallet_address, &ak).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, ak.wallet_address).await?; + } cast_send_with_access_key( &provider, tx_request, @@ -308,11 +346,13 @@ impl SendTxArgs { tx::validate_from_address(send_tx.eth.wallet.from, from)?; let (mut tx_request, _) = builder.build(&signer).await?; + maybe_print_resolved_lane( + resolved_lane.as_ref(), + tx_request.nonce().unwrap_or_default(), + )?; - // Apply sponsor signature after gas estimation so the estimate is - // consistent with what `--tempo.print-sponsor-hash` computes. - if let Some(sig) = sponsor_signature { - tx_request.set_fee_payer_signature(sig); + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut tx_request, from).await?; } let wallet = EthereumWallet::from(signer); diff --git a/crates/cast/src/cmd/tempo.rs b/crates/cast/src/cmd/tempo.rs new file mode 100644 index 0000000000000..6744e6b73c039 --- /dev/null +++ b/crates/cast/src/cmd/tempo.rs @@ -0,0 +1,45 @@ +use clap::Parser; +use eyre::Result; +use foundry_common::tempo::{EnsureAccessKeyConfig, ensure_access_key}; + +/// Tempo wallet integration commands. +#[derive(Debug, Parser)] +pub enum TempoSubcommand { + /// Authorize a new access key against your Tempo wallet via wallet.tempo. + /// + /// Persists the key to `$TEMPO_HOME/wallet/keys.toml` (default + /// `~/.tempo/wallet/keys.toml`). Also runs automatically on a 402 from a + /// Tempo RPC when no local key is configured. + /// + /// Env: `TEMPO_HOME`, `TEMPO_CLI_AUTH_URL` (override auth service). + Login { + /// Chain ID to authorize the key for. Defaults to Tempo mainnet (4217). + #[arg(long, default_value_t = 4217)] + chain_id: u64, + + /// Print the authorization URL to stderr instead of opening a browser. + #[arg(long)] + no_browser: bool, + }, +} + +impl TempoSubcommand { + pub async fn run(self) -> Result<()> { + match self { + Self::Login { chain_id, no_browser } => { + let mut cfg = EnsureAccessKeyConfig::from_env(chain_id); + if no_browser { + cfg.no_browser = true; + } + let outcome = ensure_access_key(cfg).await?; + let _ = foundry_common::sh_println!( + "Authorized key {} for wallet {} on chain {}", + outcome.key_address, + outcome.wallet_address, + outcome.chain_id, + ); + Ok(()) + } + } + } +} diff --git a/crates/cast/src/cmd/tip20/mine.rs b/crates/cast/src/cmd/tip20/mine.rs index a5f9062482a01..0367450a19b06 100644 --- a/crates/cast/src/cmd/tip20/mine.rs +++ b/crates/cast/src/cmd/tip20/mine.rs @@ -20,11 +20,11 @@ use tempo_primitives::{MasterId, TempoAddressExt, UserTag}; const POW_BYTES: usize = 4; -pub(super) struct Output { - pub(super) salt: B256, - pub(super) registration_hash: B256, - pub(super) master_id: MasterId, - pub(super) zero_tag_virtual_address: Address, +pub(crate) struct Output { + pub(crate) salt: B256, + pub(crate) registration_hash: B256, + pub(crate) master_id: MasterId, + pub(crate) zero_tag_virtual_address: Address, } pub(super) fn run( @@ -127,7 +127,12 @@ pub(super) async fn register( Ok(()) } -fn mine(master: Address, salt: B256, n_threads: usize, pow_bytes: usize) -> Result { +pub(crate) fn mine( + master: Address, + salt: B256, + n_threads: usize, + pow_bytes: usize, +) -> Result { let mut packed = [0u8; 52]; packed[..20].copy_from_slice(master.as_slice()); @@ -144,7 +149,7 @@ fn mine(master: Address, salt: B256, n_threads: usize, pow_bytes: usize) -> Resu .ok_or_else(|| eyre::eyre!("virtual master mining failed: all threads panicked")) } -fn derive(master: Address, salt: B256) -> Output { +pub(crate) fn derive(master: Address, salt: B256) -> Output { let registration_hash = registration_hash(master, salt); let master_id = MasterId::from_slice(®istration_hash[4..8]); let zero_tag_virtual_address = Address::new_virtual(master_id, UserTag::ZERO); @@ -152,14 +157,14 @@ fn derive(master: Address, salt: B256) -> Output { Output { salt, registration_hash, master_id, zero_tag_virtual_address } } -fn registration_hash(master: Address, salt: B256) -> B256 { +pub(crate) fn registration_hash(master: Address, salt: B256) -> B256 { let mut packed = [0u8; 52]; packed[..20].copy_from_slice(master.as_slice()); packed[20..].copy_from_slice(salt.as_slice()); keccak256(packed) } -fn has_pow(registration_hash: &B256, pow_bytes: usize) -> bool { +pub(crate) fn has_pow(registration_hash: &B256, pow_bytes: usize) -> bool { registration_hash[..pow_bytes].iter().all(|byte| *byte == 0) } diff --git a/crates/cast/src/cmd/tip20/mod.rs b/crates/cast/src/cmd/tip20/mod.rs index e3c39b2c9bb18..edb4c3b7a57b3 100644 --- a/crates/cast/src/cmd/tip20/mod.rs +++ b/crates/cast/src/cmd/tip20/mod.rs @@ -6,7 +6,7 @@ use std::str::FromStr; mod create; pub(crate) use create::iso4217_warning_message; -mod mine; +pub(crate) mod mine; /// TIP-20 token operations (Tempo). #[derive(Debug, Parser, Clone)] diff --git a/crates/cast/src/cmd/vaddr/create.rs b/crates/cast/src/cmd/vaddr/create.rs new file mode 100644 index 0000000000000..0563b09601323 --- /dev/null +++ b/crates/cast/src/cmd/vaddr/create.rs @@ -0,0 +1,181 @@ +use crate::{ + cmd::{ + erc20::build_provider_with_signer, + send::{cast_send, cast_send_with_access_key}, + tip20::mine, + }, + tx::{SendTxOpts, TxParams}, +}; +use alloy_primitives::{Address, B256}; +use alloy_signer::Signer; +use eyre::Result; +use foundry_cli::utils::{LoadConfig, get_chain}; +use foundry_common::{provider::ProviderBuilder, shell}; +use rand::{RngCore, SeedableRng, rngs::StdRng}; +use serde_json::json; +use std::time::Instant; +use tempo_alloy::{ + TempoNetwork, + contracts::precompiles::{ADDRESS_REGISTRY_ADDRESS, IAddressRegistry}, +}; +use tempo_primitives::{TempoAddressExt, UserTag}; + +const POW_BYTES: usize = 4; + +#[allow(clippy::too_many_arguments)] +pub(super) async fn run( + owner: Address, + salt: Option, + tag: u64, + count: u32, + threads: Option, + seed: Option, + no_random: bool, + no_register: bool, + send_tx: SendTxOpts, + tx_opts: TxParams, +) -> Result<()> { + if count == 0 { + // no virtual addresses to compute + return Ok(()); + } + + if !owner.is_valid_master() { + eyre::bail!( + "invalid owner address {owner}; see https://docs.tempo.xyz/protocol/tips/tip-1022" + ); + } + + let output = if let Some(salt) = salt { + let output = mine::derive(owner, salt); + if !mine::has_pow(&output.registration_hash, POW_BYTES) { + eyre::bail!( + "provided salt does not satisfy TIP-1022 proof of work: {}", + output.registration_hash + ); + } + output + } else { + let mut n_threads = threads.unwrap_or(0); + if n_threads == 0 { + n_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); + } + + let mut start_salt = B256::ZERO; + if !no_random { + let mut rng = match seed { + Some(seed) => StdRng::from_seed(seed.0), + None => StdRng::from_os_rng(), + }; + rng.fill_bytes(&mut start_salt[..]); + } + + if !shell::is_json() { + sh_println!("Mining TIP-1022 salt for {owner} with {n_threads} threads...")?; + } + let timer = Instant::now(); + let output = mine::mine(owner, start_salt, n_threads, POW_BYTES)?; + if !shell::is_json() { + sh_println!("Found salt in {:?}", timer.elapsed())?; + } + output + }; + + const MAX_USER_TAG: u64 = 0x0000_FFFF_FFFF_FFFF; + let mut virtual_addresses = Vec::with_capacity(count as usize); + for i in 0..count { + let tag_value = tag + .checked_add(i as u64) + .filter(|&t| t <= MAX_USER_TAG) + .ok_or_else(|| eyre::eyre!("tag overflow: tag + count exceeds the 6-byte user tag range (max {MAX_USER_TAG:#x})"))?; + let raw = tag_value.to_be_bytes(); + let user_tag = UserTag::new(raw[2..].try_into().expect("slice is 6 bytes")); + let vaddr = Address::new_virtual(output.master_id, user_tag); + virtual_addresses.push((user_tag, vaddr)); + } + + if shell::is_json() { + sh_println!( + "{}", + serde_json::to_string_pretty(&json!({ + "salt": format!("{}", output.salt), + "registration_hash": format!("{}", output.registration_hash), + "master_id": format!("{}", output.master_id), + "virtual_addresses": virtual_addresses.iter().map(|(tag, addr)| json!({ + "tag": format!("{tag}"), + "address": format!("{addr}"), + })).collect::>(), + }))? + )?; + } else { + sh_println!( + "Salt: {} +Registration hash: {} +Master ID: {}", + output.salt, + output.registration_hash, + output.master_id, + )?; + sh_println!("\nVirtual addresses:")?; + for (tag, vaddr) in &virtual_addresses { + sh_println!(" tag={tag} {vaddr}")?; + } + } + + if no_register { + return Ok(()); + } + + register(owner, output.salt, send_tx, tx_opts).await +} + +async fn register( + owner: Address, + salt: B256, + send_tx: SendTxOpts, + tx_opts: TxParams, +) -> Result<()> { + let (signer, tempo_access_key) = send_tx.eth.wallet.maybe_signer().await?; + let signer = signer.ok_or_else(|| { + eyre::eyre!("cast vaddr create requires a signer (for example --private-key or --from)") + })?; + + let sender = + tempo_access_key.as_ref().map(|ak| ak.wallet_address).unwrap_or_else(|| signer.address()); + + if sender != owner { + eyre::bail!( + "signer mismatch: salt is for {owner}, but the configured signer would register as {sender}" + ); + } + + let config = send_tx.eth.load_config()?; + let timeout = send_tx.timeout.unwrap_or(config.transaction_timeout); + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + let mut tx = IAddressRegistry::new(ADDRESS_REGISTRY_ADDRESS, &provider) + .registerVirtualMaster(salt) + .into_transaction_request(); + tx_opts.apply::(&mut tx, get_chain(config.chain, &provider).await?.is_legacy()); + + sh_println!("Submitting registerVirtualMaster({salt})...")?; + + if let Some(ref access_key) = tempo_access_key { + cast_send_with_access_key( + &provider, + tx, + &signer, + access_key, + send_tx.cast_async, + send_tx.confirmations, + timeout, + ) + .await?; + } else { + let provider = build_provider_with_signer::(&send_tx, signer)?; + cast_send(provider, tx, send_tx.cast_async, send_tx.sync, send_tx.confirmations, timeout) + .await?; + } + + Ok(()) +} diff --git a/crates/cast/src/cmd/vaddr/mod.rs b/crates/cast/src/cmd/vaddr/mod.rs new file mode 100644 index 0000000000000..446d1e7ece5e2 --- /dev/null +++ b/crates/cast/src/cmd/vaddr/mod.rs @@ -0,0 +1,131 @@ +use crate::tx::{SendTxOpts, TxParams}; +use alloy_primitives::{Address, B256}; +use clap::Parser; +use foundry_cli::opts::RpcOpts; + +mod create; +mod resolve; +mod watch; + +/// TIP-1022 virtual address registry operations (Tempo). +/// +/// Virtual addresses are deterministic 20-byte aliases (masterId || VIRTUAL_MAGIC || userTag) +/// that auto-forward TIP-20 deposits to a registered master wallet at the protocol level, +/// with no on-chain sweep transaction required. +/// +/// See: +#[derive(Debug, Parser, Clone)] +pub enum VaddrSubcommand { + /// Mine a TIP-1022 proof-of-work salt, register as a virtual address master, and print + /// derived virtual addresses for the given owner. + #[command(visible_alias = "c")] + Create { + /// The master (owner) address that will control all virtual addresses under this + /// registration. Must not be the zero address, a virtual address, or a TIP-20 token. + #[arg(long, value_name = "ADDRESS")] + owner: Address, + + /// Use this salt directly instead of mining one. Must satisfy the 32-bit PoW requirement. + #[arg(long, conflicts_with_all = ["seed", "no_random"], value_name = "HEX")] + salt: Option, + + /// Starting user tag for the derived virtual address output (hex-encoded 6 bytes). + #[arg(long, default_value = "0", value_name = "U64")] + tag: u64, + + /// Number of virtual addresses to derive and print. + #[arg(long, default_value = "1", value_name = "N")] + count: u32, + + /// Number of threads to use for mining. Defaults to number of logical cores. + #[arg(long, short = 'j', visible_alias = "jobs")] + threads: Option, + + /// Seed for the random number generator used to initialize the salt search. + #[arg(long, value_name = "HEX")] + seed: Option, + + /// Start salt search from zero instead of a random value. + #[arg(long, conflicts_with = "seed")] + no_random: bool, + + /// Mine and print the salt and derived virtual addresses without submitting the + /// registerVirtualMaster transaction. + #[arg(long)] + no_register: bool, + + #[command(flatten)] + send_tx: Box, + + #[command(flatten)] + tx: Box, + }, + + /// Resolve a virtual address to its registered master and decode its components. + #[command(visible_alias = "r")] + Resolve { + /// The virtual address to resolve. + #[arg(value_name = "ADDRESS")] + addr: Address, + + #[command(flatten)] + rpc: RpcOpts, + }, + + /// Watch (tail) incoming TIP-20 transfers to a virtual address. + #[command(visible_alias = "w")] + Watch { + /// The virtual address to monitor. + #[arg(value_name = "ADDRESS")] + addr: Address, + + /// Filter on a specific TIP-20 token address. Watches all tokens if omitted. + #[arg(long, value_name = "ADDRESS")] + token: Option
, + + /// Block number to start from. Defaults to the current latest block. + #[arg(long, value_name = "BLOCK")] + from_block: Option, + + #[command(flatten)] + rpc: RpcOpts, + }, +} + +impl VaddrSubcommand { + pub async fn run(self) -> eyre::Result<()> { + match self { + Self::Create { + owner, + salt, + tag, + count, + threads, + seed, + no_random, + no_register, + send_tx, + tx, + } => { + create::run( + owner, + salt, + tag, + count, + threads, + seed, + no_random, + no_register, + *send_tx, + *tx, + ) + .await? + } + Self::Resolve { addr, rpc } => resolve::run(addr, rpc).await?, + Self::Watch { addr, token, from_block, rpc } => { + watch::run(addr, token, from_block, rpc).await? + } + } + Ok(()) + } +} diff --git a/crates/cast/src/cmd/vaddr/resolve.rs b/crates/cast/src/cmd/vaddr/resolve.rs new file mode 100644 index 0000000000000..96936f4fe4713 --- /dev/null +++ b/crates/cast/src/cmd/vaddr/resolve.rs @@ -0,0 +1,52 @@ +use alloy_primitives::{Address, hex}; +use eyre::Result; +use foundry_cli::{opts::RpcOpts, utils::LoadConfig}; +use foundry_common::{provider::ProviderBuilder, shell}; +use serde_json::json; +use tempo_alloy::{ + TempoNetwork, + contracts::precompiles::{ADDRESS_REGISTRY_ADDRESS, IAddressRegistry}, +}; + +pub(super) async fn run(addr: Address, rpc: RpcOpts) -> Result<()> { + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + let registry = IAddressRegistry::new(ADDRESS_REGISTRY_ADDRESS, &provider); + + let decode_builder = registry.decodeVirtualAddress(addr); + let resolve_builder = registry.resolveVirtualAddress(addr); + let (decoded, master) = tokio::try_join!(decode_builder.call(), resolve_builder.call())?; + + if !decoded.isVirtual { + sh_println!("{addr} is not a virtual address")?; + return Ok(()); + } + + let master_id = decoded.masterId; + let user_tag = decoded.userTag; + let master: Address = master; + + if shell::is_json() { + let master_address = if master.is_zero() { None } else { Some(format!("{master}")) }; + sh_println!( + "{}", + serde_json::to_string_pretty(&json!({ + "address": format!("{addr}"), + "master_id": format!("0x{}", hex::encode(master_id)), + "user_tag": format!("0x{}", hex::encode(user_tag)), + "master_address": master_address, + }))? + )?; + } else { + sh_println!("Virtual address: {addr}")?; + sh_println!("Master ID: 0x{}", hex::encode(master_id))?; + sh_println!("User tag: 0x{}", hex::encode(user_tag))?; + if master.is_zero() { + sh_println!("Master address: (unregistered)")?; + } else { + sh_println!("Master address: {master}")?; + } + } + + Ok(()) +} diff --git a/crates/cast/src/cmd/vaddr/watch.rs b/crates/cast/src/cmd/vaddr/watch.rs new file mode 100644 index 0000000000000..dc159d5e37c8e --- /dev/null +++ b/crates/cast/src/cmd/vaddr/watch.rs @@ -0,0 +1,108 @@ +use alloy_primitives::{Address, B256, keccak256}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockNumberOrTag, Filter}; +use eyre::Result; +use foundry_cli::{opts::RpcOpts, utils::LoadConfig}; +use foundry_common::{provider::ProviderBuilder, shell}; +use serde_json::json; +use std::sync::LazyLock; +use tempo_alloy::TempoNetwork; +use tempo_primitives::TempoAddressExt; + +static TRANSFER_TOPIC: LazyLock = + LazyLock::new(|| keccak256(b"Transfer(address,address,uint256)")); + +pub(super) async fn run( + addr: Address, + token: Option
, + from_block: Option, + rpc: RpcOpts, +) -> Result<()> { + if !addr.is_virtual() { + eyre::bail!("{addr} is not a virtual address"); + } + + let config = rpc.load_config()?; + let provider = ProviderBuilder::::from_config(&config)?.build()?; + + // Transfer(address indexed from, address indexed to, uint256 value) + // topic[0] = event sig, topic[1] = from, topic[2] = to + let to_topic: B256 = { + let mut buf = [0u8; 32]; + buf[12..].copy_from_slice(addr.as_slice()); + buf.into() + }; + + let start = from_block.map(BlockNumberOrTag::Number).unwrap_or(BlockNumberOrTag::Latest); + + let mut filter = + Filter::new().event_signature(*TRANSFER_TOPIC).topic2(to_topic).from_block(start); + + if let Some(tok) = token { + filter = filter.address(tok); + } + + if !shell::is_json() { + sh_println!("Watching transfers to {addr}... (Ctrl-C to stop)")?; + } + + // Fetch logs from the requested start block (historical when from_block is set) + let logs = provider.get_logs(&filter).await?; + for log in &logs { + print_transfer_log(log)?; + } + + // Poll for new logs + let mut last_block = provider.get_block_number().await?; + loop { + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let current = provider.get_block_number().await?; + if current > last_block { + let poll_filter = filter.clone().from_block(last_block + 1).to_block(current); + let new_logs = provider.get_logs(&poll_filter).await?; + for log in &new_logs { + print_transfer_log(log)?; + } + last_block = current; + } + } +} + +fn print_transfer_log(log: &alloy_rpc_types::Log) -> Result<()> { + let block = log.block_number.unwrap_or(0); + let tx = log.transaction_hash.unwrap_or_default(); + let token = log.address(); + + // Decode topics: topic[1]=from, topic[2]=to + let from = log.topics().get(1).map(|t| { + let mut addr = [0u8; 20]; + addr.copy_from_slice(&t[12..]); + Address::from(addr) + }); + + // Decode amount from data + let amount = if log.data().data.len() >= 32 { + alloy_primitives::U256::from_be_slice(&log.data().data[..32]) + } else { + alloy_primitives::U256::ZERO + }; + + if shell::is_json() { + sh_println!( + "{}", + serde_json::to_string(&json!({ + "block": block, + "tx": format!("{tx}"), + "token": format!("{token}"), + "from": from.map(|a| format!("{a}")).unwrap_or_default(), + "amount": amount.to_string(), + }))? + )?; + } else { + sh_println!( + "block={block} tx={tx} token={token} from={} amount={amount}", + from.map(|a| a.to_string()).unwrap_or_default(), + )?; + } + Ok(()) +} diff --git a/crates/cast/src/cmd/wallet/mod.rs b/crates/cast/src/cmd/wallet/mod.rs index 8e0dd8dd3ed8c..b2378d8bfdc58 100644 --- a/crates/cast/src/cmd/wallet/mod.rs +++ b/crates/cast/src/cmd/wallet/mod.rs @@ -779,8 +779,7 @@ flag to set your key via: )?; let address = wallet.address(); let success_message = format!( - "`{}` keystore was saved successfully. Address: {:?}", - &account_name, address, + "`{account_name}` keystore was saved successfully. Address: {address:?}", ); sh_println!("{}", success_message.green())?; } @@ -815,7 +814,7 @@ flag to set your key via: format!("Failed to remove keystore file at {}", keystore_path.display()) })?; - let success_message = format!("`{}` keystore was removed successfully.", &name); + let success_message = format!("`{name}` keystore was removed successfully."); sh_println!("{}", success_message.green())?; } Self::PrivateKey { @@ -886,8 +885,7 @@ flag to set your key via: let private_key = B256::from_slice(&wallet.credential().to_bytes()); - let success_message = - format!("{}'s private key is: {}", &account_name, private_key); + let success_message = format!("{account_name}'s private key is: {private_key}"); sh_println!("{}", success_message.green())?; } @@ -945,10 +943,9 @@ flag to set your key via: Some(&account_name), )?; + let address = wallet.address(); let success_message = format!( - "Password for keystore `{}` was changed successfully. Address: {:?}", - &account_name, - wallet.address(), + "Password for keystore `{account_name}` was changed successfully. Address: {address:?}", ); sh_println!("{}", success_message.green())?; } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index ce5572acebc13..2b1b03486bf04 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -40,6 +40,7 @@ use foundry_common::{ use foundry_config::Chain; use foundry_evm::core::bytecode::InstIter; use futures::{FutureExt, StreamExt, future::Either}; +#[cfg(feature = "optimism")] use op_alloy_consensus as _; use rayon::prelude::*; @@ -60,6 +61,7 @@ pub use foundry_evm::*; pub mod args; pub mod cmd; pub mod opts; +pub mod tempo; pub mod base; pub mod call_spec; @@ -246,7 +248,7 @@ impl + Clone + Unpin, N: Network> Cast { 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_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 { diff --git a/crates/cast/src/opts.rs b/crates/cast/src/opts.rs index effc081e8072a..763eb132ddb5c 100644 --- a/crates/cast/src/opts.rs +++ b/crates/cast/src/opts.rs @@ -1,11 +1,13 @@ +#[cfg(feature = "optimism")] +use crate::cmd::da_estimate::DAEstimateArgs; use crate::cmd::{ access_list::AccessListArgs, artifact::ArtifactArgs, b2e_payload::B2EPayloadArgs, batch_mktx::BatchMakeTxArgs, batch_send::BatchSendArgs, bind::BindArgs, call::CallArgs, constructor_args::ConstructorArgsArgs, create2::Create2Args, creation_code::CreationCodeArgs, - da_estimate::DAEstimateArgs, erc20::Erc20Subcommand, estimate::EstimateArgs, - find_block::FindBlockArgs, interface::InterfaceArgs, keychain::KeychainSubcommand, - logs::LogsArgs, mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs, send::SendTxArgs, - storage::StorageArgs, tip20::Tip20Subcommand, trace::TraceArgs, txpool::TxPoolSubcommands, + erc20::Erc20Subcommand, estimate::EstimateArgs, find_block::FindBlockArgs, + interface::InterfaceArgs, keychain::KeychainSubcommand, logs::LogsArgs, mktx::MakeTxArgs, + rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, tempo::TempoSubcommand, + tip20::Tip20Subcommand, trace::TraceArgs, txpool::TxPoolSubcommands, vaddr::VaddrSubcommand, wallet::WalletSubcommands, }; use alloy_ens::NameOrAddress; @@ -1163,6 +1165,7 @@ pub enum CastSubcommand { command: TxPoolSubcommands, }, /// Estimates the data availability size of a given opstack block. + #[cfg(feature = "optimism")] #[command(name = "da-estimate")] DAEstimate(DAEstimateArgs), @@ -1186,6 +1189,20 @@ pub enum CastSubcommand { #[command(subcommand)] command: KeychainSubcommand, }, + + /// Tempo wallet integration (login, etc.). + Tempo { + #[command(subcommand)] + command: TempoSubcommand, + }, + + /// TIP-1022 virtual address registry operations (Tempo). + #[command(visible_alias = "vaddr")] + VirtualAddress { + #[command(subcommand)] + command: VaddrSubcommand, + }, + #[command(name = "trace")] Trace(TraceArgs), } diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs index 96f5fc5137575..b58136f4ae9de 100644 --- a/crates/cast/src/tx.rs +++ b/crates/cast/src/tx.rs @@ -20,7 +20,7 @@ use foundry_common::{ get_pretty_receipt_w_reason_attr, shell, }; use foundry_config::{Chain, Config}; -use foundry_wallets::{BrowserWalletOpts, WalletOpts, WalletSigner}; +use foundry_wallets::{BrowserWalletOpts, TempoAccessKeyConfig, WalletOpts, WalletSigner}; use itertools::Itertools; use serde_json::value::RawValue; use std::{fmt::Write, marker::PhantomData, str::FromStr, time::Duration}; @@ -535,13 +535,29 @@ where sender: impl Into>, ) -> Result<(N::TransactionRequest, Option)> { let fill = self.fill; - self._build(sender, fill).await + self._build(sender, fill, None).await + } + + /// Builds a transaction that will be signed by a Tempo access key. + /// + /// The access-key id is set before gas estimation. If the access key needs on-chain + /// provisioning, its authorization is embedded before access-list/gas estimation and before + /// any sponsor digest can be computed. + pub async fn build_with_access_key( + mut self, + sender: impl Into>, + access_key: &TempoAccessKeyConfig, + ) -> Result<(N::TransactionRequest, Option)> { + self.tx.set_key_id(access_key.key_address); + let fill = self.fill; + self._build(sender, fill, Some(access_key)).await } async fn _build( mut self, sender: impl Into>, fill: bool, + access_key: Option<&TempoAccessKeyConfig>, ) -> Result<(N::TransactionRequest, Option)> { // prepare let sender = sender.into(); @@ -555,6 +571,16 @@ where // resolve let tx_nonce = self.resolve_nonce(sender.address(), fill).await?; self.resolve_auth(&sender, tx_nonce).await?; + if let Some(access_key) = access_key { + self.tx + .prepare_access_key_authorization( + &self.provider, + access_key.wallet_address, + access_key.key_address, + access_key.key_authorization.as_ref(), + ) + .await?; + } self.resolve_access_list().await?; // fill diff --git a/crates/cast/tests/cli/keychain.rs b/crates/cast/tests/cli/keychain.rs new file mode 100644 index 0000000000000..88e9e16983cc5 --- /dev/null +++ b/crates/cast/tests/cli/keychain.rs @@ -0,0 +1,76 @@ +//! CLI tests for `cast keychain` subcommands. + +use anvil::NodeConfig; +use foundry_test_utils::util::OutputExt; + +/// Anvil test accounts (standard mnemonic). +mod accounts { + pub const PK1: &str = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + pub const ADDR1: &str = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + pub const ADDR2: &str = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"; + pub const TOKEN: &str = "0x20C000000000000000000000b9537d11c60E8b50"; // PathUSD +} + +// `cast keychain rl --json` must emit `{"remaining":""}`, not a bare string. +casttest!(keychain_rl_json_is_object, async |_prj, cmd| { + let (_, handle) = anvil::spawn(NodeConfig::test_tempo()).await; + let rpc = handle.http_endpoint(); + + let output = cmd + .args([ + "keychain", + "rl", + accounts::ADDR1, + accounts::ADDR2, + accounts::TOKEN, + "--rpc-url", + &rpc, + "--json", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let parsed: serde_json::Value = serde_json::from_str(output.trim()) + .expect("cast keychain rl --json should emit valid JSON"); + assert!(parsed.is_object(), "expected JSON object, got: {output}"); + assert!( + parsed.get("remaining").is_some(), + "expected 'remaining' key in JSON output, got: {output}" + ); + // Must not be a bare string (old bug: `"0"`) + assert!(!parsed.is_string(), "JSON output must not be a bare string, got: {output}"); +}); + +// `cast keychain authorize --tempo.print-sponsor-hash --json` must emit +// `{"sponsor_hash":"0x..."}`, not a raw hex string. +casttest!(keychain_authorize_sponsor_hash_json_is_object, async |_prj, cmd| { + let (_, handle) = anvil::spawn(NodeConfig::test_tempo()).await; + let rpc = handle.http_endpoint(); + + let output = cmd + .args([ + "keychain", + "authorize", + accounts::ADDR2, // key to authorize + "--private-key", + accounts::PK1, + "--rpc-url", + &rpc, + "--tempo.print-sponsor-hash", + "--json", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let parsed: serde_json::Value = serde_json::from_str(output.trim()) + .expect("cast keychain authorize --tempo.print-sponsor-hash --json should emit valid JSON"); + assert!(parsed.is_object(), "expected JSON object, got: {output}"); + let hash = parsed + .get("sponsor_hash") + .and_then(|v| v.as_str()) + .unwrap_or_else(|| panic!("expected 'sponsor_hash' key in JSON output, got: {output}")); + assert!(hash.starts_with("0x"), "sponsor_hash should be 0x-prefixed, got: {hash}"); + assert_eq!(hash.len(), 66, "sponsor_hash should be 32-byte hex (66 chars), got: {hash}"); +}); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index c26baaca638da..9071614e3a7a9 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,6 +1,7 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; +use alloy_eips::Decodable2718; use alloy_hardforks::EthereumHardfork; use alloy_network::{TransactionBuilder, TransactionResponse}; use alloy_primitives::{B256, Bytes, U256, address, b256, hex}; @@ -20,11 +21,13 @@ use foundry_test_utils::{ }; use serde_json::json; use std::{fs, path::Path, str::FromStr}; +use tempo_primitives::TempoTxEnvelope; #[macro_use] extern crate foundry_test_utils; mod erc20; +mod keychain; mod selectors; casttest!(print_short_version, |_prj, cmd| { @@ -2055,6 +2058,55 @@ casttest!(mktx_ethsign, async |_prj, cmd| { ]]); }); +// tests that `cast mktx --tempo.lane ` resolves the lane against a `tempo.lanes.toml` file at +// the project root, sets the corresponding `nonce_key` on the produced Tempo AA transaction. +casttest!(mktx_tempo_lane_resolves_nonce_key, |prj, cmd| { + // Write a shared lanes file at the project root. + let lanes_path = prj.root().join("tempo.lanes.toml"); + fs::write(&lanes_path, "deploy = 1\nops = 2\npayments = 42\n").unwrap(); + + let output = cmd + .current_dir(prj.root()) + .args([ + "mktx", + "--tempo.lane", + "payments", + "--private-key", + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "--chain", + "1", + "--nonce", + "0", + "--gas-limit", + "21000", + "--gas-price", + "10000000000", + "--priority-gas-price", + "1000000000", + "0x0000000000000000000000000000000000000001", + ]) + .assert_success() + .get_output() + .clone(); + + // The resolved-lane breadcrumb is printed to stderr so it doesn't pollute stdout + // (which carries the raw signed transaction). + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("lane: payments (nonce_key=42, nonce=0)"), + "expected lane breadcrumb on stderr, got: {stderr}", + ); + + // Decode the produced signed Tempo AA transaction and verify it carries the + // resolved 2D nonce key. + let stdout = String::from_utf8_lossy(&output.stdout); + let raw_hex = stdout.trim().trim_start_matches("0x"); + let raw = hex::decode(raw_hex).expect("decode hex output"); + let envelope = TempoTxEnvelope::decode_2718(&mut raw.as_slice()).expect("decode tempo tx"); + assert!(envelope.is_aa(), "expected Tempo AA transaction, got: {envelope:?}"); + assert_eq!(envelope.nonce_key(), Some(U256::from(42_u64))); +}); + // tests that the raw encoded transaction is returned casttest!(tx_raw, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); @@ -4052,6 +4104,7 @@ Warning: Contract code is empty }); // +#[cfg(feature = "optimism")] casttest!(tx_raw_opstack_deposit, |_prj, cmd| { cmd.args([ "tx", @@ -5048,6 +5101,7 @@ casttest!(cast_decode_tx_network_flag_short_and_long_equivalent, |_prj, cmd| { // Test that `--network optimism` and `-n optimism` produce identical output for decode-tx. // Uses a known OP-stack deposit transaction (same tx as tx_raw_opstack_deposit test). +#[cfg(feature = "optimism")] casttest!(cast_decode_tx_network_optimism_short_and_long_equivalent, |_prj, cmd| { let tx = "0x7ef90207a0cbde10ec697aff886f95d2514bab434e455620627b9bb8ba33baaaa4d537d62794d45955f4de64f1840e5686e64278da901e263031944200000000000000000000000000000000000007872386f26fc10000872386f26fc1000083096c4980b901a4d764ad0b0001000000000000000000000000000000000000000000000000000000065132000000000000000000000000fd0bf71f60660e2f608ed56e1659c450eb1131200000000000000000000000004200000000000000000000000000000000000010000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a41635f5fd000000000000000000000000ca11bde05977b3631167028862be2a173976ca110000000000000000000000005703b26fe5a7be820db1bf34c901a79da1a46ba4000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; @@ -5104,3 +5158,68 @@ casttest!(run_evm_version_updates_gas_params, |_prj, cmd| { "expected Spurious Dragon gas (177241), got: {sd_output}" ); }); + +// Tests for `cast vaddr` JSON output +casttest!(vaddr_create_json_output, |_prj, cmd| { + // Use a pre-computed salt that satisfies the 4-byte PoW requirement for this owner. + // Salt: 0x0000000000000000000000000000000000000000000000003ee0a78d00000000 + // Owner: 0x1234567890123456789012345678901234567890 + let out = cmd + .args([ + "--json", + "vaddr", + "create", + "--owner", + "0x1234567890123456789012345678901234567890", + "--salt", + "0x0000000000000000000000000000000000000000000000003ee0a78d00000000", + "--no-register", + "--count", + "2", + ]) + .assert_success() + .get_output() + .stdout_lossy(); + + let v: serde_json::Value = serde_json::from_str(out.trim()).expect("valid JSON"); + assert_eq!(v["salt"], "0x0000000000000000000000000000000000000000000000003ee0a78d00000000"); + assert_eq!( + v["registration_hash"], + "0x000000002f51c0c4f66f3910f799c6b98e2123ef43a401a062eb8ee07498c396" + ); + assert_eq!(v["master_id"], "0x2f51c0c4"); + let addrs = v["virtual_addresses"].as_array().expect("array"); + assert_eq!(addrs.len(), 2); + assert_eq!(addrs[0]["tag"], "0x000000000000"); + assert_eq!( + addrs[0]["address"].as_str().unwrap().to_lowercase(), + "0x2f51c0c4fdfdfdfdfdfdfdfdfdfd000000000000" + ); + assert_eq!(addrs[1]["tag"], "0x000000000001"); + assert_eq!( + addrs[1]["address"].as_str().unwrap().to_lowercase(), + "0x2f51c0c4fdfdfdfdfdfdfdfdfdfd000000000001" + ); +}); + +casttest!(vaddr_create_plain_output, |_prj, cmd| { + cmd.args([ + "vaddr", + "create", + "--owner", + "0x1234567890123456789012345678901234567890", + "--salt", + "0x0000000000000000000000000000000000000000000000003ee0a78d00000000", + "--no-register", + ]) + .assert_success() + .stdout_eq(str![[r#" +Salt: 0x0000000000000000000000000000000000000000000000003ee0a78d00000000 +Registration hash: 0x000000002f51c0c4f66f3910f799c6b98e2123ef43a401a062eb8ee07498c396 +Master ID: 0x2f51c0c4 + +Virtual addresses: + tag=0x000000000000 [..] + +"#]]); +}); diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 659fec7f1a333..0eab12331be04 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -68,3 +68,13 @@ tracing.workspace = true walkdir.workspace = true proptest.workspace = true serde.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm-core/optimism", + "foundry-evm-fuzz/optimism", + "foundry-evm-traces/optimism", + "forge-script-sequence/optimism", +] diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 7d6cb9e481502..01de77b9c95fd 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -864,7 +864,7 @@ "func": { "id": "assertApproxEqAbsDecimal_1", "description": "Compares two `uint256` values. Expects difference to be less than or equal to `maxDelta`.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqAbsDecimal(uint256,uint256,uint256,uint256,string)", @@ -904,7 +904,7 @@ "func": { "id": "assertApproxEqAbsDecimal_3", "description": "Compares two `int256` values. Expects difference to be less than or equal to `maxDelta`.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqAbsDecimal(int256,int256,uint256,uint256,string)", @@ -944,7 +944,7 @@ "func": { "id": "assertApproxEqAbs_1", "description": "Compares two `uint256` values. Expects difference to be less than or equal to `maxDelta`.\nIncludes error message into revert string on failure.", - "declaration": "function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string calldata error) external pure;", + "declaration": "function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqAbs(uint256,uint256,uint256,string)", @@ -984,7 +984,7 @@ "func": { "id": "assertApproxEqAbs_3", "description": "Compares two `int256` values. Expects difference to be less than or equal to `maxDelta`.\nIncludes error message into revert string on failure.", - "declaration": "function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string calldata error) external pure;", + "declaration": "function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqAbs(int256,int256,uint256,string)", @@ -1024,7 +1024,7 @@ "func": { "id": "assertApproxEqRelDecimal_1", "description": "Compares two `uint256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`.\n`maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100%\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertApproxEqRelDecimal(uint256 left, uint256 right, uint256 maxPercentDelta, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertApproxEqRelDecimal(uint256 left, uint256 right, uint256 maxPercentDelta, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqRelDecimal(uint256,uint256,uint256,uint256,string)", @@ -1064,7 +1064,7 @@ "func": { "id": "assertApproxEqRelDecimal_3", "description": "Compares two `int256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`.\n`maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100%\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertApproxEqRelDecimal(int256 left, int256 right, uint256 maxPercentDelta, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertApproxEqRelDecimal(int256 left, int256 right, uint256 maxPercentDelta, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqRelDecimal(int256,int256,uint256,uint256,string)", @@ -1104,7 +1104,7 @@ "func": { "id": "assertApproxEqRel_1", "description": "Compares two `uint256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`.\n`maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100%\nIncludes error message into revert string on failure.", - "declaration": "function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string calldata error) external pure;", + "declaration": "function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqRel(uint256,uint256,uint256,string)", @@ -1144,7 +1144,7 @@ "func": { "id": "assertApproxEqRel_3", "description": "Compares two `int256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`.\n`maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100%\nIncludes error message into revert string on failure.", - "declaration": "function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta, string calldata error) external pure;", + "declaration": "function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertApproxEqRel(int256,int256,uint256,string)", @@ -1184,7 +1184,7 @@ "func": { "id": "assertEqDecimal_1", "description": "Asserts that two `uint256` values are equal, formatting them with decimals in failure message.\nIncludes error message into revert string on failure.", - "declaration": "function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEqDecimal(uint256,uint256,uint256,string)", @@ -1224,7 +1224,7 @@ "func": { "id": "assertEqDecimal_3", "description": "Asserts that two `int256` values are equal, formatting them with decimals in failure message.\nIncludes error message into revert string on failure.", - "declaration": "function assertEqDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertEqDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEqDecimal(int256,int256,uint256,string)", @@ -1264,7 +1264,7 @@ "func": { "id": "assertEq_1", "description": "Asserts that two `bool` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(bool left, bool right, string calldata error) external pure;", + "declaration": "function assertEq(bool left, bool right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(bool,bool,string)", @@ -1304,7 +1304,7 @@ "func": { "id": "assertEq_11", "description": "Asserts that two `string` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(string calldata left, string calldata right, string calldata error) external pure;", + "declaration": "function assertEq(string calldata left, string calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(string,string,string)", @@ -1344,7 +1344,7 @@ "func": { "id": "assertEq_13", "description": "Asserts that two `bytes` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(bytes calldata left, bytes calldata right, string calldata error) external pure;", + "declaration": "function assertEq(bytes calldata left, bytes calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(bytes,bytes,string)", @@ -1384,7 +1384,7 @@ "func": { "id": "assertEq_15", "description": "Asserts that two arrays of `bool` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(bool[] calldata left, bool[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(bool[] calldata left, bool[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(bool[],bool[],string)", @@ -1424,7 +1424,7 @@ "func": { "id": "assertEq_17", "description": "Asserts that two arrays of `uint256` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(uint256[] calldata left, uint256[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(uint256[] calldata left, uint256[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(uint256[],uint256[],string)", @@ -1464,7 +1464,7 @@ "func": { "id": "assertEq_19", "description": "Asserts that two arrays of `int256` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(int256[] calldata left, int256[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(int256[] calldata left, int256[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(int256[],int256[],string)", @@ -1524,7 +1524,7 @@ "func": { "id": "assertEq_21", "description": "Asserts that two arrays of `address` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(address[] calldata left, address[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(address[] calldata left, address[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(address[],address[],string)", @@ -1564,7 +1564,7 @@ "func": { "id": "assertEq_23", "description": "Asserts that two arrays of `bytes32` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(bytes32[] calldata left, bytes32[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(bytes32[] calldata left, bytes32[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(bytes32[],bytes32[],string)", @@ -1604,7 +1604,7 @@ "func": { "id": "assertEq_25", "description": "Asserts that two arrays of `string` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(string[] calldata left, string[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(string[] calldata left, string[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(string[],string[],string)", @@ -1644,7 +1644,7 @@ "func": { "id": "assertEq_27", "description": "Asserts that two arrays of `bytes` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(bytes[] calldata left, bytes[] calldata right, string calldata error) external pure;", + "declaration": "function assertEq(bytes[] calldata left, bytes[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(bytes[],bytes[],string)", @@ -1664,7 +1664,7 @@ "func": { "id": "assertEq_3", "description": "Asserts that two `uint256` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(uint256 left, uint256 right, string calldata error) external pure;", + "declaration": "function assertEq(uint256 left, uint256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(uint256,uint256,string)", @@ -1704,7 +1704,7 @@ "func": { "id": "assertEq_5", "description": "Asserts that two `int256` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(int256 left, int256 right, string calldata error) external pure;", + "declaration": "function assertEq(int256 left, int256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(int256,int256,string)", @@ -1744,7 +1744,7 @@ "func": { "id": "assertEq_7", "description": "Asserts that two `address` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(address left, address right, string calldata error) external pure;", + "declaration": "function assertEq(address left, address right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(address,address,string)", @@ -1784,7 +1784,7 @@ "func": { "id": "assertEq_9", "description": "Asserts that two `bytes32` values are equal and includes error message into revert string on failure.", - "declaration": "function assertEq(bytes32 left, bytes32 right, string calldata error) external pure;", + "declaration": "function assertEq(bytes32 left, bytes32 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertEq(bytes32,bytes32,string)", @@ -1824,7 +1824,7 @@ "func": { "id": "assertFalse_1", "description": "Asserts that the given condition is false and includes error message into revert string on failure.", - "declaration": "function assertFalse(bool condition, string calldata error) external pure;", + "declaration": "function assertFalse(bool condition, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertFalse(bool,string)", @@ -1864,7 +1864,7 @@ "func": { "id": "assertGeDecimal_1", "description": "Compares two `uint256` values. Expects first value to be greater than or equal to second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGeDecimal(uint256,uint256,uint256,string)", @@ -1904,7 +1904,7 @@ "func": { "id": "assertGeDecimal_3", "description": "Compares two `int256` values. Expects first value to be greater than or equal to second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertGeDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertGeDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGeDecimal(int256,int256,uint256,string)", @@ -1944,7 +1944,7 @@ "func": { "id": "assertGe_1", "description": "Compares two `uint256` values. Expects first value to be greater than or equal to second.\nIncludes error message into revert string on failure.", - "declaration": "function assertGe(uint256 left, uint256 right, string calldata error) external pure;", + "declaration": "function assertGe(uint256 left, uint256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGe(uint256,uint256,string)", @@ -1984,7 +1984,7 @@ "func": { "id": "assertGe_3", "description": "Compares two `int256` values. Expects first value to be greater than or equal to second.\nIncludes error message into revert string on failure.", - "declaration": "function assertGe(int256 left, int256 right, string calldata error) external pure;", + "declaration": "function assertGe(int256 left, int256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGe(int256,int256,string)", @@ -2024,7 +2024,7 @@ "func": { "id": "assertGtDecimal_1", "description": "Compares two `uint256` values. Expects first value to be greater than second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGtDecimal(uint256,uint256,uint256,string)", @@ -2064,7 +2064,7 @@ "func": { "id": "assertGtDecimal_3", "description": "Compares two `int256` values. Expects first value to be greater than second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertGtDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertGtDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGtDecimal(int256,int256,uint256,string)", @@ -2104,7 +2104,7 @@ "func": { "id": "assertGt_1", "description": "Compares two `uint256` values. Expects first value to be greater than second.\nIncludes error message into revert string on failure.", - "declaration": "function assertGt(uint256 left, uint256 right, string calldata error) external pure;", + "declaration": "function assertGt(uint256 left, uint256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGt(uint256,uint256,string)", @@ -2144,7 +2144,7 @@ "func": { "id": "assertGt_3", "description": "Compares two `int256` values. Expects first value to be greater than second.\nIncludes error message into revert string on failure.", - "declaration": "function assertGt(int256 left, int256 right, string calldata error) external pure;", + "declaration": "function assertGt(int256 left, int256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertGt(int256,int256,string)", @@ -2184,7 +2184,7 @@ "func": { "id": "assertLeDecimal_1", "description": "Compares two `uint256` values. Expects first value to be less than or equal to second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLeDecimal(uint256,uint256,uint256,string)", @@ -2224,7 +2224,7 @@ "func": { "id": "assertLeDecimal_3", "description": "Compares two `int256` values. Expects first value to be less than or equal to second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertLeDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertLeDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLeDecimal(int256,int256,uint256,string)", @@ -2264,7 +2264,7 @@ "func": { "id": "assertLe_1", "description": "Compares two `uint256` values. Expects first value to be less than or equal to second.\nIncludes error message into revert string on failure.", - "declaration": "function assertLe(uint256 left, uint256 right, string calldata error) external pure;", + "declaration": "function assertLe(uint256 left, uint256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLe(uint256,uint256,string)", @@ -2304,7 +2304,7 @@ "func": { "id": "assertLe_3", "description": "Compares two `int256` values. Expects first value to be less than or equal to second.\nIncludes error message into revert string on failure.", - "declaration": "function assertLe(int256 left, int256 right, string calldata error) external pure;", + "declaration": "function assertLe(int256 left, int256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLe(int256,int256,string)", @@ -2344,7 +2344,7 @@ "func": { "id": "assertLtDecimal_1", "description": "Compares two `uint256` values. Expects first value to be less than second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLtDecimal(uint256,uint256,uint256,string)", @@ -2384,7 +2384,7 @@ "func": { "id": "assertLtDecimal_3", "description": "Compares two `int256` values. Expects first value to be less than second.\nFormats values with decimals in failure message. Includes error message into revert string on failure.", - "declaration": "function assertLtDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertLtDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLtDecimal(int256,int256,uint256,string)", @@ -2424,7 +2424,7 @@ "func": { "id": "assertLt_1", "description": "Compares two `uint256` values. Expects first value to be less than second.\nIncludes error message into revert string on failure.", - "declaration": "function assertLt(uint256 left, uint256 right, string calldata error) external pure;", + "declaration": "function assertLt(uint256 left, uint256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLt(uint256,uint256,string)", @@ -2464,7 +2464,7 @@ "func": { "id": "assertLt_3", "description": "Compares two `int256` values. Expects first value to be less than second.\nIncludes error message into revert string on failure.", - "declaration": "function assertLt(int256 left, int256 right, string calldata error) external pure;", + "declaration": "function assertLt(int256 left, int256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertLt(int256,int256,string)", @@ -2504,7 +2504,7 @@ "func": { "id": "assertNotEqDecimal_1", "description": "Asserts that two `uint256` values are not equal, formatting them with decimals in failure message.\nIncludes error message into revert string on failure.", - "declaration": "function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEqDecimal(uint256,uint256,uint256,string)", @@ -2544,7 +2544,7 @@ "func": { "id": "assertNotEqDecimal_3", "description": "Asserts that two `int256` values are not equal, formatting them with decimals in failure message.\nIncludes error message into revert string on failure.", - "declaration": "function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure;", + "declaration": "function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEqDecimal(int256,int256,uint256,string)", @@ -2584,7 +2584,7 @@ "func": { "id": "assertNotEq_1", "description": "Asserts that two `bool` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(bool left, bool right, string calldata error) external pure;", + "declaration": "function assertNotEq(bool left, bool right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(bool,bool,string)", @@ -2624,7 +2624,7 @@ "func": { "id": "assertNotEq_11", "description": "Asserts that two `string` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(string calldata left, string calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(string calldata left, string calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(string,string,string)", @@ -2664,7 +2664,7 @@ "func": { "id": "assertNotEq_13", "description": "Asserts that two `bytes` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(bytes calldata left, bytes calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(bytes calldata left, bytes calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(bytes,bytes,string)", @@ -2704,7 +2704,7 @@ "func": { "id": "assertNotEq_15", "description": "Asserts that two arrays of `bool` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(bool[] calldata left, bool[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(bool[] calldata left, bool[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(bool[],bool[],string)", @@ -2744,7 +2744,7 @@ "func": { "id": "assertNotEq_17", "description": "Asserts that two arrays of `uint256` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(uint256[] calldata left, uint256[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(uint256[] calldata left, uint256[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(uint256[],uint256[],string)", @@ -2784,7 +2784,7 @@ "func": { "id": "assertNotEq_19", "description": "Asserts that two arrays of `int256` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(int256[] calldata left, int256[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(int256[] calldata left, int256[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(int256[],int256[],string)", @@ -2844,7 +2844,7 @@ "func": { "id": "assertNotEq_21", "description": "Asserts that two arrays of `address` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(address[] calldata left, address[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(address[] calldata left, address[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(address[],address[],string)", @@ -2884,7 +2884,7 @@ "func": { "id": "assertNotEq_23", "description": "Asserts that two arrays of `bytes32` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(bytes32[] calldata left, bytes32[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(bytes32[] calldata left, bytes32[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(bytes32[],bytes32[],string)", @@ -2924,7 +2924,7 @@ "func": { "id": "assertNotEq_25", "description": "Asserts that two arrays of `string` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(string[] calldata left, string[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(string[] calldata left, string[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(string[],string[],string)", @@ -2964,7 +2964,7 @@ "func": { "id": "assertNotEq_27", "description": "Asserts that two arrays of `bytes` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(bytes[] calldata left, bytes[] calldata right, string calldata error) external pure;", + "declaration": "function assertNotEq(bytes[] calldata left, bytes[] calldata right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(bytes[],bytes[],string)", @@ -2984,7 +2984,7 @@ "func": { "id": "assertNotEq_3", "description": "Asserts that two `uint256` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(uint256 left, uint256 right, string calldata error) external pure;", + "declaration": "function assertNotEq(uint256 left, uint256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(uint256,uint256,string)", @@ -3024,7 +3024,7 @@ "func": { "id": "assertNotEq_5", "description": "Asserts that two `int256` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(int256 left, int256 right, string calldata error) external pure;", + "declaration": "function assertNotEq(int256 left, int256 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(int256,int256,string)", @@ -3064,7 +3064,7 @@ "func": { "id": "assertNotEq_7", "description": "Asserts that two `address` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(address left, address right, string calldata error) external pure;", + "declaration": "function assertNotEq(address left, address right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(address,address,string)", @@ -3104,7 +3104,7 @@ "func": { "id": "assertNotEq_9", "description": "Asserts that two `bytes32` values are not equal and includes error message into revert string on failure.", - "declaration": "function assertNotEq(bytes32 left, bytes32 right, string calldata error) external pure;", + "declaration": "function assertNotEq(bytes32 left, bytes32 right, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertNotEq(bytes32,bytes32,string)", @@ -3144,7 +3144,7 @@ "func": { "id": "assertTrue_1", "description": "Asserts that the given condition is true and includes error message into revert string on failure.", - "declaration": "function assertTrue(bool condition, string calldata error) external pure;", + "declaration": "function assertTrue(bool condition, string calldata err) external pure;", "visibility": "external", "mutability": "pure", "signature": "assertTrue(bool,string)", @@ -5447,7 +5447,7 @@ { "func": { "id": "expectEmit_0", - "description": "Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.).\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data (as specified by the booleans).", + "description": "Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.).\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data (as specified by the booleans).\nMust be placed immediately before the call you want to assert on. If the next call reverts and the\nrevert is caught by the caller (low-level call or try/catch), the expectation remains active and may\nbe satisfied by a log emitted from a later call.", "declaration": "function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) external;", "visibility": "external", "mutability": "", @@ -5487,7 +5487,7 @@ { "func": { "id": "expectEmit_2", - "description": "Prepare an expected log with all topic and data checks enabled.\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data.", + "description": "Prepare an expected log with all topic and data checks enabled.\nCall this function, then emit an event, then call a function. Internally after the call, we check if\nlogs were emitted in the expected order with the expected topics and data.\nMust be placed immediately before the call you want to assert on. If the next call reverts and the\nrevert is caught by the caller (low-level call or try/catch), the expectation remains active and may\nbe satisfied by a log emitted from a later call.", "declaration": "function expectEmit() external;", "visibility": "external", "mutability": "", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 65f181f6b0e35..12cfd19017770 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1082,6 +1082,9 @@ interface Vm { /// Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.). /// Call this function, then emit an event, then call a function. Internally after the call, we check if /// logs were emitted in the expected order with the expected topics and data (as specified by the booleans). + /// Must be placed immediately before the call you want to assert on. If the next call reverts and the + /// revert is caught by the caller (low-level call or try/catch), the expectation remains active and may + /// be satisfied by a log emitted from a later call. #[cheatcode(group = Testing, safety = Unsafe)] function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) external; @@ -1093,6 +1096,9 @@ interface Vm { /// Prepare an expected log with all topic and data checks enabled. /// Call this function, then emit an event, then call a function. Internally after the call, we check if /// logs were emitted in the expected order with the expected topics and data. + /// Must be placed immediately before the call you want to assert on. If the next call reverts and the + /// revert is caught by the caller (low-level call or try/catch), the expectation remains active and may + /// be satisfied by a log emitted from a later call. #[cheatcode(group = Testing, safety = Unsafe)] function expectEmit() external; @@ -1243,7 +1249,7 @@ interface Vm { /// Asserts that the given condition is true and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertTrue(bool condition, string calldata error) external pure; + function assertTrue(bool condition, string calldata err) external pure; /// Asserts that the given condition is false. #[cheatcode(group = Testing, safety = Safe)] @@ -1251,7 +1257,7 @@ interface Vm { /// Asserts that the given condition is false and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertFalse(bool condition, string calldata error) external pure; + function assertFalse(bool condition, string calldata err) external pure; /// Asserts that two `bool` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1259,7 +1265,7 @@ interface Vm { /// Asserts that two `bool` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(bool left, bool right, string calldata error) external pure; + function assertEq(bool left, bool right, string calldata err) external pure; /// Asserts that two `uint256` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1267,7 +1273,7 @@ interface Vm { /// Asserts that two `uint256` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(uint256 left, uint256 right, string calldata error) external pure; + function assertEq(uint256 left, uint256 right, string calldata err) external pure; /// Asserts that two `int256` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1275,7 +1281,7 @@ interface Vm { /// Asserts that two `int256` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(int256 left, int256 right, string calldata error) external pure; + function assertEq(int256 left, int256 right, string calldata err) external pure; /// Asserts that two `address` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1283,7 +1289,7 @@ interface Vm { /// Asserts that two `address` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(address left, address right, string calldata error) external pure; + function assertEq(address left, address right, string calldata err) external pure; /// Asserts that two `bytes32` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1291,7 +1297,7 @@ interface Vm { /// Asserts that two `bytes32` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(bytes32 left, bytes32 right, string calldata error) external pure; + function assertEq(bytes32 left, bytes32 right, string calldata err) external pure; /// Asserts that two `string` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1299,7 +1305,7 @@ interface Vm { /// Asserts that two `string` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(string calldata left, string calldata right, string calldata error) external pure; + function assertEq(string calldata left, string calldata right, string calldata err) external pure; /// Asserts that two `bytes` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1307,7 +1313,7 @@ interface Vm { /// Asserts that two `bytes` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(bytes calldata left, bytes calldata right, string calldata error) external pure; + function assertEq(bytes calldata left, bytes calldata right, string calldata err) external pure; /// Asserts that two arrays of `bool` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1315,7 +1321,7 @@ interface Vm { /// Asserts that two arrays of `bool` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(bool[] calldata left, bool[] calldata right, string calldata error) external pure; + function assertEq(bool[] calldata left, bool[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `uint256 values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1323,7 +1329,7 @@ interface Vm { /// Asserts that two arrays of `uint256` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(uint256[] calldata left, uint256[] calldata right, string calldata error) external pure; + function assertEq(uint256[] calldata left, uint256[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `int256` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1331,7 +1337,7 @@ interface Vm { /// Asserts that two arrays of `int256` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(int256[] calldata left, int256[] calldata right, string calldata error) external pure; + function assertEq(int256[] calldata left, int256[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `address` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1339,7 +1345,7 @@ interface Vm { /// Asserts that two arrays of `address` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(address[] calldata left, address[] calldata right, string calldata error) external pure; + function assertEq(address[] calldata left, address[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `bytes32` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1347,7 +1353,7 @@ interface Vm { /// Asserts that two arrays of `bytes32` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(bytes32[] calldata left, bytes32[] calldata right, string calldata error) external pure; + function assertEq(bytes32[] calldata left, bytes32[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `string` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1355,7 +1361,7 @@ interface Vm { /// Asserts that two arrays of `string` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(string[] calldata left, string[] calldata right, string calldata error) external pure; + function assertEq(string[] calldata left, string[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `bytes` values are equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1363,7 +1369,7 @@ interface Vm { /// Asserts that two arrays of `bytes` values are equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEq(bytes[] calldata left, bytes[] calldata right, string calldata error) external pure; + function assertEq(bytes[] calldata left, bytes[] calldata right, string calldata err) external pure; /// Asserts that two `uint256` values are equal, formatting them with decimals in failure message. #[cheatcode(group = Testing, safety = Safe)] @@ -1372,7 +1378,7 @@ interface Vm { /// Asserts that two `uint256` values are equal, formatting them with decimals in failure message. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; /// Asserts that two `int256` values are equal, formatting them with decimals in failure message. #[cheatcode(group = Testing, safety = Safe)] @@ -1381,7 +1387,7 @@ interface Vm { /// Asserts that two `int256` values are equal, formatting them with decimals in failure message. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertEqDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertEqDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; /// Asserts that two `bool` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1389,7 +1395,7 @@ interface Vm { /// Asserts that two `bool` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(bool left, bool right, string calldata error) external pure; + function assertNotEq(bool left, bool right, string calldata err) external pure; /// Asserts that two `uint256` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1397,7 +1403,7 @@ interface Vm { /// Asserts that two `uint256` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(uint256 left, uint256 right, string calldata error) external pure; + function assertNotEq(uint256 left, uint256 right, string calldata err) external pure; /// Asserts that two `int256` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1405,7 +1411,7 @@ interface Vm { /// Asserts that two `int256` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(int256 left, int256 right, string calldata error) external pure; + function assertNotEq(int256 left, int256 right, string calldata err) external pure; /// Asserts that two `address` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1413,7 +1419,7 @@ interface Vm { /// Asserts that two `address` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(address left, address right, string calldata error) external pure; + function assertNotEq(address left, address right, string calldata err) external pure; /// Asserts that two `bytes32` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1421,7 +1427,7 @@ interface Vm { /// Asserts that two `bytes32` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(bytes32 left, bytes32 right, string calldata error) external pure; + function assertNotEq(bytes32 left, bytes32 right, string calldata err) external pure; /// Asserts that two `string` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1429,7 +1435,7 @@ interface Vm { /// Asserts that two `string` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(string calldata left, string calldata right, string calldata error) external pure; + function assertNotEq(string calldata left, string calldata right, string calldata err) external pure; /// Asserts that two `bytes` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1437,7 +1443,7 @@ interface Vm { /// Asserts that two `bytes` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(bytes calldata left, bytes calldata right, string calldata error) external pure; + function assertNotEq(bytes calldata left, bytes calldata right, string calldata err) external pure; /// Asserts that two arrays of `bool` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1445,7 +1451,7 @@ interface Vm { /// Asserts that two arrays of `bool` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(bool[] calldata left, bool[] calldata right, string calldata error) external pure; + function assertNotEq(bool[] calldata left, bool[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `uint256` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1453,7 +1459,7 @@ interface Vm { /// Asserts that two arrays of `uint256` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(uint256[] calldata left, uint256[] calldata right, string calldata error) external pure; + function assertNotEq(uint256[] calldata left, uint256[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `int256` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1461,7 +1467,7 @@ interface Vm { /// Asserts that two arrays of `int256` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(int256[] calldata left, int256[] calldata right, string calldata error) external pure; + function assertNotEq(int256[] calldata left, int256[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `address` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1469,7 +1475,7 @@ interface Vm { /// Asserts that two arrays of `address` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(address[] calldata left, address[] calldata right, string calldata error) external pure; + function assertNotEq(address[] calldata left, address[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `bytes32` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1477,7 +1483,7 @@ interface Vm { /// Asserts that two arrays of `bytes32` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(bytes32[] calldata left, bytes32[] calldata right, string calldata error) external pure; + function assertNotEq(bytes32[] calldata left, bytes32[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `string` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1485,7 +1491,7 @@ interface Vm { /// Asserts that two arrays of `string` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(string[] calldata left, string[] calldata right, string calldata error) external pure; + function assertNotEq(string[] calldata left, string[] calldata right, string calldata err) external pure; /// Asserts that two arrays of `bytes` values are not equal. #[cheatcode(group = Testing, safety = Safe)] @@ -1493,7 +1499,7 @@ interface Vm { /// Asserts that two arrays of `bytes` values are not equal and includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEq(bytes[] calldata left, bytes[] calldata right, string calldata error) external pure; + function assertNotEq(bytes[] calldata left, bytes[] calldata right, string calldata err) external pure; /// Asserts that two `uint256` values are not equal, formatting them with decimals in failure message. #[cheatcode(group = Testing, safety = Safe)] @@ -1502,7 +1508,7 @@ interface Vm { /// Asserts that two `uint256` values are not equal, formatting them with decimals in failure message. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; /// Asserts that two `int256` values are not equal, formatting them with decimals in failure message. #[cheatcode(group = Testing, safety = Safe)] @@ -1511,7 +1517,7 @@ interface Vm { /// Asserts that two `int256` values are not equal, formatting them with decimals in failure message. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be greater than second. #[cheatcode(group = Testing, safety = Safe)] @@ -1520,7 +1526,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be greater than second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGt(uint256 left, uint256 right, string calldata error) external pure; + function assertGt(uint256 left, uint256 right, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be greater than second. #[cheatcode(group = Testing, safety = Safe)] @@ -1529,7 +1535,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be greater than second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGt(int256 left, int256 right, string calldata error) external pure; + function assertGt(int256 left, int256 right, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be greater than second. /// Formats values with decimals in failure message. @@ -1539,7 +1545,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be greater than second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be greater than second. /// Formats values with decimals in failure message. @@ -1549,7 +1555,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be greater than second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGtDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertGtDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be greater than or equal to second. #[cheatcode(group = Testing, safety = Safe)] @@ -1558,7 +1564,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be greater than or equal to second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGe(uint256 left, uint256 right, string calldata error) external pure; + function assertGe(uint256 left, uint256 right, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be greater than or equal to second. #[cheatcode(group = Testing, safety = Safe)] @@ -1567,7 +1573,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be greater than or equal to second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGe(int256 left, int256 right, string calldata error) external pure; + function assertGe(int256 left, int256 right, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be greater than or equal to second. /// Formats values with decimals in failure message. @@ -1577,7 +1583,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be greater than or equal to second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be greater than or equal to second. /// Formats values with decimals in failure message. @@ -1587,7 +1593,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be greater than or equal to second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertGeDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertGeDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be less than second. #[cheatcode(group = Testing, safety = Safe)] @@ -1596,7 +1602,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be less than second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLt(uint256 left, uint256 right, string calldata error) external pure; + function assertLt(uint256 left, uint256 right, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be less than second. #[cheatcode(group = Testing, safety = Safe)] @@ -1605,7 +1611,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be less than second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLt(int256 left, int256 right, string calldata error) external pure; + function assertLt(int256 left, int256 right, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be less than second. /// Formats values with decimals in failure message. @@ -1615,7 +1621,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be less than second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be less than second. /// Formats values with decimals in failure message. @@ -1625,7 +1631,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be less than second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLtDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertLtDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be less than or equal to second. #[cheatcode(group = Testing, safety = Safe)] @@ -1634,7 +1640,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be less than or equal to second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLe(uint256 left, uint256 right, string calldata error) external pure; + function assertLe(uint256 left, uint256 right, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be less than or equal to second. #[cheatcode(group = Testing, safety = Safe)] @@ -1643,7 +1649,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be less than or equal to second. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLe(int256 left, int256 right, string calldata error) external pure; + function assertLe(int256 left, int256 right, string calldata err) external pure; /// Compares two `uint256` values. Expects first value to be less than or equal to second. /// Formats values with decimals in failure message. @@ -1653,7 +1659,7 @@ interface Vm { /// Compares two `uint256` values. Expects first value to be less than or equal to second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; /// Compares two `int256` values. Expects first value to be less than or equal to second. /// Formats values with decimals in failure message. @@ -1663,7 +1669,7 @@ interface Vm { /// Compares two `int256` values. Expects first value to be less than or equal to second. /// Formats values with decimals in failure message. Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertLeDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertLeDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; /// Compares two `uint256` values. Expects difference to be less than or equal to `maxDelta`. #[cheatcode(group = Testing, safety = Safe)] @@ -1672,7 +1678,7 @@ interface Vm { /// Compares two `uint256` values. Expects difference to be less than or equal to `maxDelta`. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string calldata error) external pure; + function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string calldata err) external pure; /// Compares two `int256` values. Expects difference to be less than or equal to `maxDelta`. #[cheatcode(group = Testing, safety = Safe)] @@ -1681,7 +1687,7 @@ interface Vm { /// Compares two `int256` values. Expects difference to be less than or equal to `maxDelta`. /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string calldata error) external pure; + function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string calldata err) external pure; /// Compares two `uint256` values. Expects difference to be less than or equal to `maxDelta`. /// Formats values with decimals in failure message. @@ -1696,7 +1702,7 @@ interface Vm { uint256 right, uint256 maxDelta, uint256 decimals, - string calldata error + string calldata err ) external pure; /// Compares two `int256` values. Expects difference to be less than or equal to `maxDelta`. @@ -1712,7 +1718,7 @@ interface Vm { int256 right, uint256 maxDelta, uint256 decimals, - string calldata error + string calldata err ) external pure; /// Compares two `uint256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`. @@ -1724,7 +1730,7 @@ interface Vm { /// `maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100% /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string calldata error) external pure; + function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string calldata err) external pure; /// Compares two `int256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`. /// `maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100% @@ -1735,7 +1741,7 @@ interface Vm { /// `maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100% /// Includes error message into revert string on failure. #[cheatcode(group = Testing, safety = Safe)] - function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta, string calldata error) external pure; + function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta, string calldata err) external pure; /// Compares two `uint256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`. /// `maxPercentDelta` is an 18 decimal fixed point number, where 1e18 == 100% @@ -1757,7 +1763,7 @@ interface Vm { uint256 right, uint256 maxPercentDelta, uint256 decimals, - string calldata error + string calldata err ) external pure; /// Compares two `int256` values. Expects relative difference in percents to be less than or equal to `maxPercentDelta`. @@ -1780,7 +1786,7 @@ interface Vm { int256 right, uint256 maxPercentDelta, uint256 decimals, - string calldata error + string calldata err ) external pure; /// Returns true if the current Foundry version is greater than or equal to the given version. diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index b22f76714dd0f..27545c1b6cd33 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -856,42 +856,6 @@ impl Cheatcodes { } } - // Handle mocked calls - if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) { - let ctx = MockCallDataContext { - calldata: call.input.bytes(ecx), - value: call.transfer_value(), - }; - - if let Some(return_data_queue) = match mocks.get_mut(&ctx) { - Some(queue) => Some(queue), - None => mocks - .iter_mut() - .find(|(mock, _)| { - call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..]) - && mock.value.is_none_or(|value| Some(value) == call.transfer_value()) - }) - .map(|(_, v)| v), - } && let Some(return_data) = if return_data_queue.len() == 1 { - // If the mocked calls stack has a single element in it, don't empty it - return_data_queue.front().map(|x| x.to_owned()) - } else { - // Else, we pop the front element - return_data_queue.pop_front() - } { - return Some(CallOutcome { - result: InterpreterResult { - result: return_data.ret_type, - output: return_data.data, - gas, - }, - memory_offset: call.return_memory_offset.clone(), - was_precompile_called: true, - precompile_call_logs: vec![], - }); - } - } - // Apply our prank if let Some(prank) = &self.get_prank(curr_depth) { // Apply delegate call, `call.caller`` will not equal `prank.prank_caller` @@ -932,6 +896,72 @@ impl Cheatcodes { } } + // Handle mocked calls + if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) { + let ctx = MockCallDataContext { + calldata: call.input.bytes(ecx), + value: call.transfer_value(), + }; + + if let Some(return_data_queue) = match mocks.get_mut(&ctx) { + Some(queue) => Some(queue), + None => mocks + .iter_mut() + .find(|(mock, _)| { + call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..]) + && mock.value.is_none_or(|value| Some(value) == call.transfer_value()) + }) + .map(|(_, v)| v), + } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned()) + { + if let Some(value) = call.transfer_value() { + let checkpoint = ecx.journal_mut().checkpoint(); + match ecx.journal_mut().transfer_loaded( + call.transfer_from(), + call.transfer_to(), + value, + ) { + None => { + if return_data.ret_type.is_ok() { + ecx.journal_mut().checkpoint_commit(); + } else { + ecx.journal_mut().checkpoint_revert(checkpoint); + } + } + Some(err) => { + ecx.journal_mut().checkpoint_revert(checkpoint); + return Some(CallOutcome { + result: InterpreterResult { + result: err.into(), + output: Bytes::new(), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + was_precompile_called: false, + precompile_call_logs: vec![], + }); + } + } + } + + // If the mocked calls stack has a single element in it, don't empty it + if return_data_queue.len() > 1 { + return_data_queue.pop_front(); + } + + return Some(CallOutcome { + result: InterpreterResult { + result: return_data.ret_type, + output: return_data.data, + gas, + }, + memory_offset: call.return_memory_offset.clone(), + was_precompile_called: true, + precompile_call_logs: vec![], + }); + } + } + // Apply EIP-2930 access list self.apply_accesslist(ecx); @@ -1497,6 +1527,21 @@ impl Inspector> for Cheatcode } } + // this will ensure we don't have false positives when trying to diagnose reverts in fork + // mode + let diag = self.fork_revert_diagnostic.take(); + + // If the call already reverted, preserve that primary failure and skip post-call + // expect* validation so it cannot overwrite the original revert. + if outcome.result.is_revert() { + // if there's a revert and a previous call was diagnosed as fork related revert then we + // can return a better error here + if let Some(err) = diag { + outcome.result.output = Error::encode(err.to_error_msg(&self.labels)); + } + return; + } + // At the end of the call, // we need to check if we've found all the emits. // We know we've found all the expected emits in the right order @@ -1574,19 +1619,6 @@ impl Inspector> for Cheatcode self.expected_emits.clear() } - // this will ensure we don't have false positives when trying to diagnose reverts in fork - // mode - let diag = self.fork_revert_diagnostic.take(); - - // if there's a revert and a previous call was diagnosed as fork related revert then we can - // return a better error here - if outcome.result.is_revert() - && let Some(err) = diag - { - outcome.result.output = Error::encode(err.to_error_msg(&self.labels)); - return; - } - // try to diagnose reverts in multi-fork mode where a call is made to an address that does // not exist if let TxKind::Call(test_contract) = ecx.tx().kind() { @@ -1867,10 +1899,23 @@ impl Inspector> for Cheatcode } // Handle expected reverts - if let Some(expected_revert) = &self.expected_revert + if let Some(expected_revert) = &mut self.expected_revert && curr_depth <= expected_revert.depth && matches!(expected_revert.kind, ExpectedRevertKind::Default) { + // Mirror the logic in `call_end`: when an expected reverter address is set + // and we don't yet have one (or we're matching multiple reverts), record the + // would-be deployed address as the reverter. revm guarantees `outcome.address` + // is `Some(_)` whenever the constructor actually ran (including the revert + // case); it is only `None` for pre-frame rejection (depth/balance/nonce), + // for which a reverter address is meaningless. + if outcome.result.is_revert() + && expected_revert.reverter.is_some() + && (expected_revert.reverted_by.is_none() || expected_revert.count > 1) + && let Some(addr) = outcome.address + { + expected_revert.reverted_by = Some(addr); + } let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap(); return match revert_handlers::handle_expect_revert( false, diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index 632ba8e04245b..12d625768a0c9 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -164,7 +164,7 @@ impl EqRelAssertionError { format_units_uint(&f.left, decimals), format_units_uint(&f.right, decimals), format_delta_percent(&f.max_delta), - &f.real_delta, + f.real_delta, ), Self::Overflow => self.to_string(), } @@ -179,7 +179,7 @@ impl EqRelAssertionError { format_units_int(&f.left, decimals), format_units_int(&f.right, decimals), format_delta_percent(&f.max_delta), - &f.real_delta, + f.real_delta, ), Self::Overflow => self.to_string(), } @@ -222,9 +222,9 @@ fn handle_assertion_result_mono( /// Implements [crate::Cheatcode] for pairs of cheatcodes. /// /// Accepts a list of pairs of cheatcodes, where the first cheatcode is the one that doesn't contain -/// a custom error message, and the second one contains it at `error` field. +/// a custom error message, and the second one contains it at `err` field. /// -/// Passed `args` are the common arguments for both cheatcode structs (excluding `error` field). +/// Passed `args` are the common arguments for both cheatcode structs (excluding `err` field). /// /// Macro also accepts an optional closure that formats the error returned by the assertion. macro_rules! impl_assertions { @@ -267,10 +267,12 @@ macro_rules! impl_assertions { ccx: &mut CheatsCtxt<'_, '_, FEN>, executor: &mut dyn CheatcodesExecutor, ) -> Result { - let Self { $($arg,)* error } = self; + let Self { $($arg,)* err } = self; match $body { Ok(()) => Ok(Default::default()), - Err(err) => handle_assertion_result(ccx, executor, err, $error_formatter, Some(error)) + Err(assertion_err) => { + handle_assertion_result(ccx, executor, assertion_err, $error_formatter, Some(err)) + } } } } diff --git a/crates/cheatcodes/src/version.rs b/crates/cheatcodes/src/version.rs index fb722c2814baa..2b8f81518a621 100644 --- a/crates/cheatcodes/src/version.rs +++ b/crates/cheatcodes/src/version.rs @@ -20,7 +20,14 @@ impl Cheatcode for foundryVersionAtLeastCall { } fn foundry_version_cmp(version: &str) -> Result { - version_cmp(SEMVER_VERSION.split('-').next().unwrap(), version) + version_cmp(strip_semver_metadata(SEMVER_VERSION), version) +} + +/// Strips pre-release (e.g. `-nightly`, `-dev`) and build metadata +/// (e.g. `+..`) from a version string +/// so we compare on `MAJOR.MINOR.PATCH` only. +fn strip_semver_metadata(version: &str) -> &str { + version.split(['-', '+']).next().unwrap() } fn version_cmp(version_a: &str, version_b: &str) -> Result { @@ -42,3 +49,61 @@ fn parse_version(version: &str) -> Result { } Ok(version) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn strips_build_metadata_only() { + // Tagged release: `1.7.1+..` + assert_eq!(strip_semver_metadata("1.7.1+abc1234567.1737036656.release"), "1.7.1"); + } + + #[test] + fn strips_pre_release_and_build_metadata() { + // Nightly: `1.7.1-nightly+..` + assert_eq!(strip_semver_metadata("1.7.1-nightly+abc1234567.1737036656.release"), "1.7.1"); + // Dev: `1.7.1-dev+..` + assert_eq!(strip_semver_metadata("1.7.1-dev+abc1234567.1737036656.debug"), "1.7.1"); + } + + #[test] + fn strips_plain_version() { + assert_eq!(strip_semver_metadata("1.7.1"), "1.7.1"); + } + + #[test] + fn version_cmp_orders_correctly() { + assert_eq!(version_cmp("1.7.1", "1.7.1").unwrap(), Ordering::Equal); + assert_eq!(version_cmp("1.7.1", "1.7.0").unwrap(), Ordering::Greater); + assert_eq!(version_cmp("1.7.1", "1.7.2").unwrap(), Ordering::Less); + assert_eq!(version_cmp("1.7.1", "0.0.1").unwrap(), Ordering::Greater); + assert_eq!(version_cmp("1.7.1", "99.0.0").unwrap(), Ordering::Less); + } + + #[test] + fn parse_version_rejects_pre_release_and_build_metadata() { + // User-supplied versions must be plain `MAJOR.MINOR.PATCH`. + assert!(parse_version("1.7.1-nightly").is_err()); + assert!(parse_version("1.7.1+abc").is_err()); + assert!(parse_version("not-a-version").is_err()); + assert!(parse_version("1.7.1").is_ok()); + } + + #[test] + fn cmp_works_against_full_semver_version_strings() { + // Simulate comparing each shape of `SEMVER_VERSION` against a user-supplied version. + for current in [ + "1.7.1+abc1234567.1737036656.release", + "1.7.1-nightly+abc1234567.1737036656.release", + "1.7.1-dev+abc1234567.1737036656.debug", + "1.7.1", + ] { + let stripped = strip_semver_metadata(current); + assert_eq!(version_cmp(stripped, "1.7.1").unwrap(), Ordering::Equal); + assert_eq!(version_cmp(stripped, "1.7.0").unwrap(), Ordering::Greater); + assert_eq!(version_cmp(stripped, "1.7.2").unwrap(), Ordering::Less); + } + } +} diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index e9a1936013272..bb673c9219e10 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -19,7 +19,6 @@ path = "bin/main.rs" [dependencies] # forge -forge-doc.workspace = true forge-fmt.workspace = true foundry-cli.workspace = true foundry-common.workspace = true @@ -50,7 +49,6 @@ itertools.workspace = true semver.workspace = true serde_json.workspace = true serde.workspace = true -solang-parser.workspace = true time = { version = "0.3", features = ["formatting"] } yansi.workspace = true tracing.workspace = true @@ -65,8 +63,13 @@ foundry-test-utils.workspace = true rexpect = "0.6" [features] -default = ["jemalloc", "asm-keccak"] +default = ["jemalloc", "asm-keccak", "optimism"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] tracy-allocator = ["foundry-cli/tracy-allocator"] +optimism = [ + "foundry-common/optimism", + "foundry-evm/optimism", + "foundry-cli/optimism", +] diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index da2c7f4caff02..2ec057b8167c0 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -2,10 +2,7 @@ //! //! This module contains the execution logic for the [SessionSource]. -use crate::{ - prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper}, - source::IntermediateOutput, -}; +use crate::prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper}; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_json_abi::EventParam; use alloy_primitives::{Address, B256, U256, hex}; @@ -15,7 +12,17 @@ use foundry_evm::{ backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder, inspectors::CheatsConfig, traces::TraceMode, }; -use solang_parser::pt; +use solar::{ + ast::{BinOpKind, ElementaryType, FunctionKind, LitKind, StateMutability, StrKind, UnOpKind}, + interface::Symbol, + sema::{ + hir::{ + ContractId, Event, Expr, ExprKind, Function, ItemId, Res, StmtKind, Type as HirType, + TypeKind, Visibility, + }, + ty::{Gcx, Ty, TyKind}, + }, +}; use std::ops::ControlFlow; use yansi::Paint; @@ -86,8 +93,10 @@ impl SessionSource { if let Some(err) = err { let output = source_without_inspector.build()?; - let formatted_event = - output.enter(|output| output.get_event(input).map(format_event_definition)); + let formatted_event = output.enter(|output| { + let gcx = output.gcx(); + output.get_event(input).map(|eid| format_event_definition(gcx, gcx.hir.event(eid))) + }); if let Some(formatted_event) = formatted_event { return Ok((ControlFlow::Break(()), Some(formatted_event?))); } @@ -122,30 +131,37 @@ impl SessionSource { // which was wrapped in `abi.encode`. let generated_output = source.build()?; - // If the expression is a variable declaration within the REPL contract, use its type; - // otherwise, attempt to infer the type. - let contract_expr = generated_output - .intermediate - .repl_contract_expressions - .get(input) - .or_else(|| source.infer_inner_expr_type()); + // Inside the compiler closure, infer the DynSolType of the inspected expression and + // determine whether the REPL should continue. + let res_ty = generated_output.enter(|out| -> Option<(bool, DynSolType)> { + let gcx = out.gcx(); - // If the current action is a function call, we get its return type - // otherwise it returns None - let function_call_return_type = - Type::get_function_return_type(contract_expr, &generated_output.intermediate); + // Try direct lookup of `input` as a named variable in the REPL contract. + if let Some(direct_ty) = lookup_named_variable_type(gcx, input) { + return Some((false, direct_ty)); + } - let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type - { - (function_call_return_type.0, function_call_return_type.1) - } else { - match contract_expr.and_then(|e| { - Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty)) - }) { - Some(res) => res, - // this type was denied for inspection, continue - None => return Ok((ControlFlow::Continue(()), None)), + // Otherwise, find the appended `bytes memory inspectoor = abi.encode();` + // and pull out the first call argument. + let block = out.run_func_body(); + let last = block.last()?; + let StmtKind::DeclSingle(vid) = last.kind else { return None }; + let var = gcx.hir.variable(vid); + let init = var.initializer?; + let ExprKind::Call(_callee, args, _) = &init.kind else { return None }; + let inner_expr = args.exprs().next()?; + + // If the call is `func()` returning a single value, prefer the function return type. + if let Some(ty) = get_function_return_type(gcx, inner_expr) { + return Some((should_continue(inner_expr), ty)); } + + let ty = expr_to_dyn(gcx, inner_expr, true)?; + Some((should_continue(inner_expr), ty)) + }); + + let Some((cont, ty)) = res_ty else { + return Ok((ControlFlow::Continue(()), None)); }; // the file compiled correctly, thus the last stack item must be the memory offset of @@ -162,42 +178,10 @@ impl SessionSource { eyre::bail!("Failed to inspect last expression: could not retrieve data from memory") }; let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?; - let c = if should_continue(contract_expr) { - ControlFlow::Continue(()) - } else { - ControlFlow::Break(()) - }; + let c = if cont { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }; Ok((c, Some(format_token(token)))) } - /// Gracefully attempts to extract the type of the expression within the `abi.encode(...)` - /// call inserted by the inspect function. - /// - /// ### Takes - /// - /// A reference to a [SessionSource] - /// - /// ### Returns - /// - /// Optionally, a [Type] - fn infer_inner_expr_type(&self) -> Option<&pt::Expression> { - let out = self.build().ok()?; - let run = out.run_func_body().ok()?.last(); - match run { - Some(pt::Statement::VariableDefinition( - _, - _, - Some(pt::Expression::FunctionCall(_, _, args)), - )) => { - // We can safely unwrap the first expression because this function - // will only be called on a session source that has just had an - // `inspectoor` variable appended to it. - Some(args.first().unwrap()) - } - _ => None, - } - } - async fn build_runner(&mut self, final_pc: usize) -> Result { let (evm_env, tx_env, fork_block) = self.config.evm_opts.env().await?; @@ -241,6 +225,51 @@ impl SessionSource { } } +/// Looks up `name` as a named variable in the REPL contract (state variables or run() locals) +/// and returns its type as a [`DynSolType`]. +/// +/// Only top-level statements of `run()` are scanned. Variables declared inside nested blocks +/// (`if`, `for`, `while`, `unchecked`, etc.) are not visible here; the caller falls back to +/// the `inspectoor`-based path for those cases. +fn lookup_named_variable_type(gcx: Gcx<'_>, name: &str) -> Option { + let hir = &gcx.hir; + let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?; + + // State variables. + for vid in repl.variables() { + let var = hir.variable(vid); + if var.name.map(|n| n.as_str() == name).unwrap_or(false) { + return solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())); + } + } + + // Locals declared in run(). + let run_fid = repl + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?; + let body = hir.function(run_fid).body?; + for stmt in body.stmts { + match stmt.kind { + StmtKind::DeclSingle(vid) => { + let var = hir.variable(vid); + if var.name.map(|n| n.as_str() == name).unwrap_or(false) { + return solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())); + } + } + StmtKind::DeclMulti(vids, _) => { + for vid in vids.iter().flatten() { + let var = hir.variable(*vid); + if var.name.map(|n| n.as_str() == name).unwrap_or(false) { + return solar_ty_to_dyn(gcx, gcx.type_of_item((*vid).into())); + } + } + } + _ => {} + } + } + None +} + /// Formats a value into an inspection message // TODO: Verbosity option fn format_token(token: DynSolValue) -> String { @@ -343,49 +372,37 @@ fn format_token(token: DynSolValue) -> String { } } -/// Formats a [pt::EventDefinition] into an inspection message -/// -/// ### Takes -/// -/// An borrowed [pt::EventDefinition] -/// -/// ### Returns -/// -/// A formatted [pt::EventDefinition] for use in inspection output. +/// Formats an [`Event`] into an inspection message. // TODO: Verbosity option -fn format_event_definition(event_definition: &pt::EventDefinition) -> Result { - let event_name = event_definition.name.as_ref().expect("Event has a name").to_string(); - let inputs = event_definition - .fields +fn format_event_definition(gcx: Gcx<'_>, event: &Event<'_>) -> Result { + let event_name = event.name.as_str().to_string(); + let inputs = event + .parameters .iter() - .map(|param| { - let name = param - .name - .as_ref() - .map(ToString::to_string) - .unwrap_or_else(|| "".to_string()); - let kind = Type::from_expression(¶m.ty) - .and_then(Type::into_builtin) + .map(|&pid| { + let var = gcx.hir.variable(pid); + let name = + var.name.map(|n| n.as_str().to_string()).unwrap_or_else(|| "".into()); + let kind = solar_ty_to_dyn(gcx, gcx.type_of_item(pid.into())) .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?; Ok(EventParam { name, ty: kind.to_string(), components: vec![], - indexed: param.indexed, + indexed: var.indexed, internal_type: None, }) }) .collect::>>()?; - let event = - alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous }; + let event = alloy_json_abi::Event { name: event_name, inputs, anonymous: event.anonymous }; Ok(format!( "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}", "event".red(), SolidityHelper::new().highlight(&format!( "{}({})", - &event.name, - &event + event.name, + event .inputs .iter() .map(|param| format!( @@ -395,7 +412,7 @@ fn format_event_definition(event_definition: &pt::EventDefinition) -> Result>() @@ -411,844 +428,724 @@ fn format_event_definition(event_definition: &pt::EventDefinition) -> Result), - - /// (type, length) - FixedArray(Box, usize), +/// Converts an [`Expr`] directly to a [`DynSolType`] for ABI inspection. +/// +/// `lookup` controls whether user-defined type names are resolved via the HIR. +fn expr_to_dyn(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option { + match &expr.kind { + // Elementary type expression: `uint256`, `address`, etc. + ExprKind::Type(ty) => hir_ty_to_dyn(gcx, ty), + + // `type(T)`: only meaningful as the lhs of a member access. + ExprKind::TypeCall(_) => None, + + // Literals. + ExprKind::Lit(lit) => match &lit.kind { + LitKind::Address(_) => Some(DynSolType::Address), + LitKind::Bool(_) => Some(DynSolType::Bool), + LitKind::Str(kind, _, _) => match kind { + StrKind::Hex => Some(DynSolType::Bytes), + StrKind::Str | StrKind::Unicode => Some(DynSolType::String), + }, + LitKind::Number(_) | LitKind::Rational(_) => Some(DynSolType::Uint(256)), + LitKind::Err(_) => None, + }, + + // Resolved identifier: `foo`. + ExprKind::Ident(reses) => { + let res = reses.first()?; + match *res { + Res::Item(ItemId::Variable(vid)) => { + solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())) + } + Res::Item(ItemId::Struct(sid)) => { + // Struct reference used as a constructor produces a tuple of field types. + Some(DynSolType::Tuple( + gcx.struct_field_types(sid) + .iter() + .filter_map(|&t| solar_ty_to_dyn(gcx, t)) + .collect(), + )) + } + // Other items and builtins: handled by enclosing Call/Member expressions. + _ => None, + } + } - /// (type, index) - ArrayIndex(Box, Option), + // Index/access: `arr[i]`, `MyType[]`, `MyType[N]`. + ExprKind::Index(base, idx) => { + let base_ty = expr_to_dyn(gcx, base, lookup)?; + let num = + idx.and_then(|e| parse_number_literal(e)).and_then(|n| usize::try_from(n).ok()); + match &base.kind { + // Type-level indexing builds an array type expression. + ExprKind::Type(_) | ExprKind::TypeCall(_) => { + if let Some(n) = num { + Some(DynSolType::FixedArray(Box::new(base_ty), n)) + } else { + Some(DynSolType::Array(Box::new(base_ty))) + } + } + // Runtime indexing returns the element type. + _ => match base_ty { + DynSolType::Array(inner) | DynSolType::FixedArray(inner, _) => Some(*inner), + DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_) => { + Some(DynSolType::FixedBytes(1)) + } + other => Some(other), + }, + } + } - /// (types) - Tuple(Vec>), + // Slice: same type as the base. + ExprKind::Slice(base, _, _) => expr_to_dyn(gcx, base, lookup), - /// (name, params, returns) - Function(Box, Vec>, Vec>), + // Array literal `[a, b, c]`. + ExprKind::Array(values) => values + .first() + .and_then(|e| expr_to_dyn(gcx, e, lookup)) + .map(|ty| DynSolType::FixedArray(Box::new(ty), values.len())), - /// (lhs, rhs) - Access(Box, String), + // Tuple expression `(a, b, c)`. + ExprKind::Tuple(items) => Some(DynSolType::Tuple( + items.iter().filter_map(|opt| opt.and_then(|e| expr_to_dyn(gcx, e, lookup))).collect(), + )), - /// (types) - Custom(Vec), -} + // Member access `lhs.member`. + ExprKind::Member(_, _) => resolve_member(gcx, expr, lookup), -impl Type { - /// Convert a [pt::Expression] to a [Type] - /// - /// ### Takes - /// - /// A reference to a [pt::Expression] to convert. - /// - /// ### Returns - /// - /// Optionally, an owned [Type] - fn from_expression(expr: &pt::Expression) -> Option { - match expr { - pt::Expression::Type(_, ty) => Self::from_type(ty), - - pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])), - - // array - pt::Expression::ArraySubscript(_, expr, num) => { - // if num is Some then this is either an index operation (arr[]) - // or a FixedArray statement (new uint256[]) - Self::from_expression(expr).and_then(|ty| { - let boxed = Box::new(ty); - let num = num.as_deref().and_then(parse_number_literal).and_then(|n| { - usize::try_from(n).ok() - }); - match expr.as_ref() { - // statement - pt::Expression::Type(_, _) => { - if let Some(num) = num { - Some(Self::FixedArray(boxed, num)) - } else { - Some(Self::Array(boxed)) - } - } - // index - pt::Expression::Variable(_) => { - Some(Self::ArrayIndex(boxed, num)) - } - _ => None - } - }) - } - pt::Expression::ArrayLiteral(_, values) => { - values.first().and_then(Self::from_expression).map(|ty| { - Self::FixedArray(Box::new(ty), values.len()) - }) - } + // Function/constructor call. + ExprKind::Call(_, _, _) => resolve_call(gcx, expr, lookup), - // tuple - pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))), + // `new T`: produces a value of type T. + ExprKind::New(ty) => hir_ty_to_dyn(gcx, ty), - // . - pt::Expression::MemberAccess(_, lhs, rhs) => { - Self::from_expression(lhs).map(|lhs| { - Self::Access(Box::new(lhs), rhs.name.clone()) - }) - } + // `payable(addr)`. + ExprKind::Payable(_) => Some(DynSolType::Address), - // - pt::Expression::Parenthesis(_, inner) | // () - pt::Expression::New(_, inner) | // new - pt::Expression::UnaryPlus(_, inner) | // + - // ops - pt::Expression::BitwiseNot(_, inner) | // ~ - pt::Expression::ArraySlice(_, inner, _, _) | // [*start*:*end*] - // assign ops - pt::Expression::PreDecrement(_, inner) | // -- - pt::Expression::PostDecrement(_, inner) | // -- - pt::Expression::PreIncrement(_, inner) | // ++ - pt::Expression::PostIncrement(_, inner) | // ++ - pt::Expression::Assign(_, inner, _) | // = ... - pt::Expression::AssignAdd(_, inner, _) | // += ... - pt::Expression::AssignSubtract(_, inner, _) | // -= ... - pt::Expression::AssignMultiply(_, inner, _) | // *= ... - pt::Expression::AssignDivide(_, inner, _) | // /= ... - pt::Expression::AssignModulo(_, inner, _) | // %= ... - pt::Expression::AssignAnd(_, inner, _) | // &= ... - pt::Expression::AssignOr(_, inner, _) | // |= ... - pt::Expression::AssignXor(_, inner, _) | // ^= ... - pt::Expression::AssignShiftLeft(_, inner, _) | // <<= ... - pt::Expression::AssignShiftRight(_, inner, _) // >>= ... - => Self::from_expression(inner), - - // *condition* ? : - pt::Expression::ConditionalOperator(_, _, if_true, if_false) => { - Self::from_expression(if_true).or_else(|| Self::from_expression(if_false)) - } + // Ternary: prefer truthy branch's type, fall back to else branch. + ExprKind::Ternary(_, t, e) => { + expr_to_dyn(gcx, t, lookup).or_else(|| expr_to_dyn(gcx, e, lookup)) + } - // address - pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)), - pt::Expression::HexNumberLiteral(_, s, _) => { - match s.parse::
() { - Ok(addr) if *s == addr.to_checksum(None) => { - Some(Self::Builtin(DynSolType::Address)) + // Delete has no return type. + ExprKind::Delete(_) => None, + + // Unary operations. + ExprKind::Unary(op, inner) => match op.kind { + UnOpKind::Neg => expr_to_dyn(gcx, inner, lookup).map(|ty| match ty { + DynSolType::Uint(n) => DynSolType::Int(n), + DynSolType::Int(n) => DynSolType::Uint(n), + x => x, + }), + UnOpKind::Not => Some(DynSolType::Bool), + UnOpKind::BitNot + | UnOpKind::PreInc + | UnOpKind::PreDec + | UnOpKind::PostInc + | UnOpKind::PostDec => expr_to_dyn(gcx, inner, lookup), + }, + + // Binary operations. + ExprKind::Binary(lhs, op, rhs) => match op.kind { + BinOpKind::Lt + | BinOpKind::Le + | BinOpKind::Gt + | BinOpKind::Ge + | BinOpKind::Eq + | BinOpKind::Ne + | BinOpKind::And + | BinOpKind::Or => Some(DynSolType::Bool), + BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul | BinOpKind::Div => { + match (expr_to_dyn(gcx, lhs, false), expr_to_dyn(gcx, rhs, false)) { + (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_))) + | (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => { + Some(DynSolType::Int(256)) } - _ => Some(Self::Builtin(DynSolType::Uint(256))), + _ => Some(DynSolType::Uint(256)), } } + BinOpKind::Rem + | BinOpKind::Pow + | BinOpKind::BitAnd + | BinOpKind::BitOr + | BinOpKind::BitXor + | BinOpKind::Shl + | BinOpKind::Shr + | BinOpKind::Sar => Some(DynSolType::Uint(256)), + }, + + // Assignments: type of the lhs. + ExprKind::Assign(lhs, _, _) => expr_to_dyn(gcx, lhs, lookup), + + ExprKind::Err(_) => None, + } +} - // uint and int - // invert - pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int), - - // int if either operand is int - // TODO: will need an update for Solidity v0.8.18 user defined operators: - // https://github.com/ethereum/solidity/issues/13718#issuecomment-1341058649 - pt::Expression::Add(_, lhs, rhs) | - pt::Expression::Subtract(_, lhs, rhs) | - pt::Expression::Multiply(_, lhs, rhs) | - pt::Expression::Divide(_, lhs, rhs) => { - match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) { - (Some(DynSolType::Int(_) | DynSolType::Uint(_)), Some(DynSolType::Int(_))) | -(Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) => { - Some(Self::Builtin(DynSolType::Int(256))) - } - _ => { - Some(Self::Builtin(DynSolType::Uint(256))) - } +/// Converts a [`HirType`] to a [`DynSolType`]. +fn hir_ty_to_dyn(gcx: Gcx<'_>, ty: &HirType<'_>) -> Option { + match &ty.kind { + TypeKind::Elementary(et) => elementary_to_dyn(*et), + TypeKind::Array(arr) => { + let elem = hir_ty_to_dyn(gcx, &arr.element)?; + if let Some(size) = arr.size { + let n = parse_number_literal(size).and_then(|n| usize::try_from(n).ok()); + if let Some(n) = n { + Some(DynSolType::FixedArray(Box::new(elem), n)) + } else { + Some(DynSolType::Array(Box::new(elem))) } + } else { + Some(DynSolType::Array(Box::new(elem))) } - - // always assume uint - pt::Expression::Modulo(_, _, _) | - pt::Expression::Power(_, _, _) | - pt::Expression::BitwiseOr(_, _, _) | - pt::Expression::BitwiseAnd(_, _, _) | - pt::Expression::BitwiseXor(_, _, _) | - pt::Expression::ShiftRight(_, _, _) | - pt::Expression::ShiftLeft(_, _, _) | - pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))), - - // TODO: Rational numbers - pt::Expression::RationalNumberLiteral(_, _, _, _, _) => { - Some(Self::Builtin(DynSolType::Uint(256))) + } + TypeKind::Function(f) => match f.returns.len() { + 0 => None, + 1 => { + let var = gcx.hir.variable(f.returns[0]); + hir_ty_to_dyn(gcx, &var.ty) } + _ => Some(DynSolType::Tuple( + f.returns + .iter() + .filter_map(|&pid| hir_ty_to_dyn(gcx, &gcx.hir.variable(pid).ty)) + .collect(), + )), + }, + TypeKind::Mapping(m) => hir_ty_to_dyn(gcx, &m.value), + TypeKind::Custom(item) => solar_ty_to_dyn(gcx, gcx.type_of_item(*item)), + TypeKind::Err(_) => None, + } +} - // bool - pt::Expression::BoolLiteral(_, _) | - pt::Expression::And(_, _, _) | - pt::Expression::Or(_, _, _) | - pt::Expression::Equal(_, _, _) | - pt::Expression::NotEqual(_, _, _) | - pt::Expression::Less(_, _, _) | - pt::Expression::LessEqual(_, _, _) | - pt::Expression::More(_, _, _) | - pt::Expression::MoreEqual(_, _, _) | - pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)), - - // string - pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)), - - // bytes - pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)), - - // function - pt::Expression::FunctionCall(_, name, args) => { - Self::from_expression(name).map(|name| { - let args = args.iter().map(Self::from_expression).collect(); - Self::Function(Box::new(name), args, vec![]) - }) - } - pt::Expression::NamedFunctionCall(_, name, args) => { - Self::from_expression(name).map(|name| { - let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect(); - Self::Function(Box::new(name), args, vec![]) - }) - } +/// Resolves a member-access expression (`lhs.member`) to its [`DynSolType`]. +/// +/// `expr` must be `ExprKind::Member`. +fn resolve_member(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option { + let ExprKind::Member(lhs, ident) = &expr.kind else { return None }; + let member = ident.name; + + // `type(T).member` — type introspection. + if let ExprKind::TypeCall(ty) = &lhs.kind { + return match member.as_str() { + "name" => Some(DynSolType::String), + "creationCode" | "runtimeCode" => Some(DynSolType::Bytes), + "interfaceId" => Some(DynSolType::FixedBytes(4)), + // Only valid for integer types; custom types (enums) fall back to Uint(256). + "min" | "max" => match &ty.kind { + TypeKind::Elementary(et) => elementary_to_dyn(*et), + _ => Some(DynSolType::Uint(256)), + }, + _ => None, + }; + } - // explicitly None - pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None, - } + // Built-in namespace identifier: `block.timestamp`, `msg.sender`, `abi.encode`, etc. + if let ExprKind::Ident(reses) = &lhs.kind + && let Some(Res::Builtin(b)) = reses.first() + && let Some(ty) = builtin_member(b.name().as_str(), member.as_str()) + { + return Some(ty); } - /// Convert a [pt::Type] to a [Type] - /// - /// ### Takes - /// - /// A reference to a [pt::Type] to convert. - /// - /// ### Returns - /// - /// Optionally, an owned [Type] - fn from_type(ty: &pt::Type) -> Option { - let ty = match ty { - pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => { - Self::Builtin(DynSolType::Address) - } - pt::Type::Bool => Self::Builtin(DynSolType::Bool), - pt::Type::String => Self::Builtin(DynSolType::String), - pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)), - pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)), - pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)), - pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes), - pt::Type::Mapping { value, .. } => Self::from_expression(value)?, - pt::Type::Function { params, returns, .. } => { - let params = map_parameters(params); - let returns = returns - .as_ref() - .map(|(returns, _)| map_parameters(returns)) - .unwrap_or_default(); - Self::Function( - Box::new(Self::Custom(vec!["__fn_type__".to_string()])), - params, - returns, - ) - } - // TODO: Rational numbers - pt::Type::Rational => return None, + // Elementary type used as a namespace: `address.balance`, `bytes.concat`, etc. + if let ExprKind::Type(ty) = &lhs.kind + && let TypeKind::Elementary(et) = &ty.kind + { + return match et { + ElementaryType::Address(_) => match member.as_str() { + "balance" => Some(DynSolType::Uint(256)), + "code" => Some(DynSolType::Bytes), + "codehash" => Some(DynSolType::FixedBytes(32)), + "send" => Some(DynSolType::Bool), + _ => None, + }, + ElementaryType::Bytes => match member.as_str() { + "concat" => Some(DynSolType::Bytes), + _ => None, + }, + ElementaryType::String => match member.as_str() { + "concat" => Some(DynSolType::String), + _ => None, + }, + _ => None, }; - Some(ty) } - /// Handle special expressions like [global variables](https://docs.soliditylang.org/en/latest/cheatsheet.html#global-variables) - /// - /// See: - fn map_special(self) -> Self { - if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) { - return self; - } + // Members on a resolved DynSolType (`.length`, `.pop`, `.selector`, `.address`). + if let Some(lhs_ty) = expr_to_dyn(gcx, lhs, lookup) + && let Some(ty) = dyn_member(&lhs_ty, member.as_str()) + { + return Some(ty); + } - let mut types = Vec::with_capacity(5); - let mut args = None; - self.recurse(&mut types, &mut args); + // HIR lookup for user-defined type members. + if lookup && let Some(mut chain) = expr_name_chain(gcx, lhs) { + chain.insert(0, member); + return infer_custom_type(gcx, &mut chain, None).ok().flatten(); + } - let len = types.len(); - if len == 0 { - return self; - } + None +} + +/// Returns the type of `builtin_ns.member` for built-in global namespaces. +fn builtin_member(builtin: &str, member: &str) -> Option { + match builtin { + "block" => match member { + "coinbase" => Some(DynSolType::Address), + "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" | "chainid" + | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)), + _ => None, + }, + "msg" => match member { + "sender" => Some(DynSolType::Address), + "gas" | "value" => Some(DynSolType::Uint(256)), + "data" => Some(DynSolType::Bytes), + "sig" => Some(DynSolType::FixedBytes(4)), + _ => None, + }, + "tx" => match member { + "origin" => Some(DynSolType::Address), + "gasprice" => Some(DynSolType::Uint(256)), + _ => None, + }, + "address" => match member { + "balance" => Some(DynSolType::Uint(256)), + "code" => Some(DynSolType::Bytes), + "codehash" => Some(DynSolType::FixedBytes(32)), + "send" => Some(DynSolType::Bool), + _ => None, + }, + _ => None, + } +} + +/// Returns the type of `ty.member` for a known [`DynSolType`]. +fn dyn_member(ty: &DynSolType, member: &str) -> Option { + match member { + "length" => match ty { + DynSolType::Array(_) + | DynSolType::FixedArray(_, _) + | DynSolType::Bytes + | DynSolType::String + | DynSolType::FixedBytes(_) => Some(DynSolType::Uint(256)), + _ => None, + }, + "pop" => match ty { + DynSolType::Array(inner) => Some(*inner.clone()), + _ => None, + }, + // Address members. + "balance" => match ty { + DynSolType::Address => Some(DynSolType::Uint(256)), + _ => None, + }, + "code" => match ty { + DynSolType::Address => Some(DynSolType::Bytes), + _ => None, + }, + "codehash" => match ty { + DynSolType::Address => Some(DynSolType::FixedBytes(32)), + _ => None, + }, + "send" => match ty { + DynSolType::Address => Some(DynSolType::Bool), + _ => None, + }, + // External function members. + "selector" => Some(DynSolType::FixedBytes(4)), + "address" => Some(DynSolType::Address), + _ => None, + } +} - // Type members, like array, bytes etc - #[expect(clippy::single_match)] - #[allow(clippy::collapsible_match)] - match &self { - Self::Access(inner, access) => { - if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) { - // Array / bytes members - let ty = Self::Builtin(ty); - match access.as_str() { - "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => { - return Self::Builtin(DynSolType::Uint(256)); +/// Resolves a call expression to its return [`DynSolType`]. +/// +/// `expr` must be `ExprKind::Call`. +fn resolve_call(gcx: Gcx<'_>, expr: &Expr<'_>, lookup: bool) -> Option { + let ExprKind::Call(callee, args, _named) = &expr.kind else { return None }; + + // Type cast: `uint256(x)`, `address(y)`, etc. + if let ExprKind::Type(ty) = &callee.kind { + return hir_ty_to_dyn(gcx, ty); + } + + // Member call: `ns.method(...)`. + if let ExprKind::Member(lhs, method) = &callee.kind + && let ExprKind::Ident(reses) = &lhs.kind + && let Some(Res::Builtin(b)) = reses.first() + { + match b.name().as_str() { + "abi" => { + return match method.as_str() { + "decode" => { + let last = args.exprs().last()?; + match expr_to_dyn(gcx, last, false)? { + DynSolType::Tuple(tys) => Some(DynSolType::Tuple(tys)), + ty => Some(DynSolType::Tuple(vec![ty])), } - "pop" if ty.is_dynamic_array() => return ty, - _ => {} } - } + s if s.starts_with("encode") => Some(DynSolType::Bytes), + _ => None, + }; } + "string" if method.as_str() == "concat" => return Some(DynSolType::String), + "bytes" if method.as_str() == "concat" => return Some(DynSolType::Bytes), _ => {} } + } - let this = { - let name = types.last().unwrap().as_str(); - match len { - 0 => unreachable!(), - 1 => match name { + // Simple identifier call: built-in global functions and HIR function calls. + if let ExprKind::Ident(reses) = &callee.kind { + match reses.first() { + Some(Res::Builtin(b)) => { + return match b.name().as_str() { "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)), "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)), "ripemd160" => Some(DynSolType::FixedBytes(20)), "ecrecover" => Some(DynSolType::Address), _ => None, - }, - 2 => { - let access = types.first().unwrap().as_str(); - match name { - "block" => match access { - "coinbase" => Some(DynSolType::Address), - "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" - | "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)), - _ => None, - }, - "msg" => match access { - "sender" => Some(DynSolType::Address), - "gas" => Some(DynSolType::Uint(256)), - "value" => Some(DynSolType::Uint(256)), - "data" => Some(DynSolType::Bytes), - "sig" => Some(DynSolType::FixedBytes(4)), - _ => None, - }, - "tx" => match access { - "origin" => Some(DynSolType::Address), - "gasprice" => Some(DynSolType::Uint(256)), - _ => None, - }, - "abi" => match access { - "decode" => { - // args = Some([Bytes(_), Tuple(args)]) - // unwrapping is safe because this is first compiled by solc so - // it is guaranteed to be a valid call - let mut args = args.unwrap(); - let last = args.pop().unwrap(); - match last { - Some(ty) => { - return match ty { - Self::Tuple(_) => ty, - ty => Self::Tuple(vec![Some(ty)]), - }; - } - None => None, - } - } - s if s.starts_with("encode") => Some(DynSolType::Bytes), - _ => None, - }, - "address" => match access { - "balance" => Some(DynSolType::Uint(256)), - "code" => Some(DynSolType::Bytes), - "codehash" => Some(DynSolType::FixedBytes(32)), - "send" => Some(DynSolType::Bool), - _ => None, - }, - "type" => match access { - "name" => Some(DynSolType::String), - "creationCode" | "runtimeCode" => Some(DynSolType::Bytes), - "interfaceId" => Some(DynSolType::FixedBytes(4)), - "min" | "max" => Some( - // Either a builtin or an enum - (|| args?.pop()??.into_builtin())() - .unwrap_or(DynSolType::Uint(256)), - ), - _ => None, - }, - "string" => match access { - "concat" => Some(DynSolType::String), - _ => None, - }, - "bytes" => match access { - "concat" => Some(DynSolType::Bytes), - _ => None, - }, - _ => None, - } - } - _ => None, - } - }; - - this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() { - "this" | "super" => Self::Custom(types), - _ => match self { - Self::Custom(_) | Self::Access(_, _) => Self::Custom(types), - Self::Function(_, _, _) => self, - _ => unreachable!(), - }, - }) - } - - /// 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>>) { - match self { - Self::Builtin(ty) => types.push(ty.to_string()), - Self::Custom(tys) => types.extend(tys.clone()), - Self::Access(expr, name) => { - types.push(name.clone()); - expr.recurse(types, args); + }; } - Self::Function(fn_name, fn_args, _fn_ret) => { - if args.is_none() && !fn_args.is_empty() { - *args = Some(fn_args.clone()); + Some(Res::Item(ItemId::Function(fid))) if lookup => { + let func = gcx.hir.function(*fid); + if !matches!(func.state_mutability, StateMutability::View | StateMutability::Pure) { + return None; } - fn_name.recurse(types, args); + let ret_id = *func.returns.first()?; + return solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into())); } _ => {} } } - /// Infers a custom type's true type by recursing up the parse tree - /// - /// ### Takes - /// - A reference to the [IntermediateOutput] - /// - An array of custom types generated by the `MemberAccess` arm of [Self::from_expression] - /// - An optional contract name. This should always be `None` when this function is first - /// called. - /// - /// ### Returns - /// - /// If successful, an `Ok(Some(DynSolType))` variant. - /// If gracefully failed, an `Ok(None)` variant. - /// If failed, an `Err(e)` variant. - fn infer_custom_type( - intermediate: &IntermediateOutput, - custom_type: &mut Vec, - contract_name: Option, - ) -> Result> { - if let Some("this" | "super") = custom_type.last().map(String::as_str) { - custom_type.pop(); + // Fall back to the callee's resolved type. + expr_to_dyn(gcx, callee, lookup) +} + +/// Extracts a name chain from a member-access expression tree for HIR lookup. +/// +/// The chain is ordered outermost-first so `a.b.c` produces `["c", "b", "a"]` with the root +/// identifier at the back. This matches the convention expected by [`infer_custom_type`]. +fn expr_name_chain(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option> { + match &expr.kind { + ExprKind::Ident(reses) => { + let res = reses.first()?; + let name = match *res { + Res::Item(ItemId::Variable(vid)) => gcx.hir.variable(vid).name?.name, + Res::Item(ItemId::Function(fid)) => gcx.hir.function(fid).name?.name, + Res::Item(ItemId::Contract(cid)) => gcx.hir.contract(cid).name.name, + Res::Builtin(b) => b.name(), + _ => return None, + }; + Some(vec![name]) } - if custom_type.is_empty() { - return Ok(None); + ExprKind::Member(lhs, ident) => { + let mut chain = expr_name_chain(gcx, lhs)?; + chain.insert(0, ident.name); + Some(chain) } + _ => None, + } +} - // If a contract exists with the given name, check its definitions for a match. - // Otherwise look in the `run` - if let Some(contract_name) = contract_name { - let intermediate_contract = intermediate - .intermediate_contracts - .get(&contract_name) - .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?; - - let cur_type = custom_type.last().unwrap(); - if let Some(func) = intermediate_contract.function_definitions.get(cur_type) { - // Check if the custom type is a function pointer member access - if let res @ Some(_) = func_members(func, custom_type) { - return Ok(res); - } - - // Because tuple types cannot be passed to `abi.encode`, we will only be - // receiving functions that have 0 or 1 return parameters here. - if func.returns.is_empty() { - eyre::bail!( - "This call expression does not return any values to inspect. Insert as statement." - ) - } +/// Infers a custom type's true type by recursing through the HIR. +/// +/// `custom_type` is a name chain ordered outermost-first (root at back). This is mutated during +/// resolution. `contract_id` narrows the search to a specific contract scope. +fn infer_custom_type( + gcx: Gcx<'_>, + custom_type: &mut Vec, + contract_id: Option, +) -> Result> { + if let Some(last) = custom_type.last() + && (last.as_str() == "this" || last.as_str() == "super") + { + custom_type.pop(); + } + if custom_type.is_empty() { + return Ok(None); + } - // Empty return types check is done above - let (_, param) = func.returns.first().unwrap(); - // Return type should always be present - let return_ty = ¶m.as_ref().unwrap().ty; - - // If the return type is a variable (not a type expression), re-enter the recursion - // on the same contract for a variable / struct search. It could be a contract, - // struct, array, etc. - if let pt::Expression::Variable(ident) = return_ty { - custom_type.push(ident.name.clone()); - return Self::infer_custom_type(intermediate, custom_type, Some(contract_name)); - } + if let Some(cid) = contract_id { + let hir = &gcx.hir; + let contract = hir.contract(cid); - // Check if our final function call alters the state. If it does, we bail so that it - // will be inserted normally without inspecting. If the state mutability was not - // expressly set, the function is inferred to alter state. - if let Some(pt::FunctionAttribute::Mutability(_mut)) = func - .attributes - .iter() - .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_))) - { - if let pt::Mutability::Payable(_) = _mut { - eyre::bail!("This function mutates state. Insert as a statement.") - } - } else { - eyre::bail!("This function mutates state. Insert as a statement.") - } + let cur_name = *custom_type.last().unwrap(); + let cur = cur_name.as_str(); - Ok(Self::ethabi(return_ty, Some(intermediate))) - } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) { - Self::infer_var_expr(&var.ty, Some(intermediate), custom_type) - } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) { - let inner_types = strukt - .fields - .iter() - .map(|var| { - Self::ethabi(&var.ty, Some(intermediate)) - .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields")) - }) - .collect::>>()?; - Ok(Some(DynSolType::Tuple(inner_types))) - } else { - eyre::bail!( - "Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}" - ) - } - } else { - // Check if the custom type is a variable or function within the REPL contract before - // anything. If it is, we can stop here. - if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into())) - { + // Function? + if let Some(fid) = contract + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str() == cur).unwrap_or(false)) + { + let func = hir.function(fid); + if let res @ Some(_) = func_members(func, custom_type) { return Ok(res); } - // Check if the first element of the custom type is a known contract. If it is, begin - // our recursion on that contract's definitions. - let name = custom_type.last().unwrap(); - let contract = intermediate.intermediate_contracts.get(name); - if contract.is_some() { - let contract_name = custom_type.pop(); - return Self::infer_custom_type(intermediate, custom_type, contract_name); + if func.returns.is_empty() { + eyre::bail!( + "This call expression does not return any values to inspect. Insert as statement." + ) } - // See [`Type::infer_var_expr`] - let name = custom_type.last().unwrap(); - if let Some(expr) = intermediate.repl_contract_expressions.get(name) { - return Self::infer_var_expr(expr, Some(intermediate), custom_type); + let sm = func.state_mutability; + if !matches!(sm, StateMutability::View | StateMutability::Pure) { + eyre::bail!("This function mutates state. Insert as a statement.") } - // The first element of our custom type was neither a variable or a function within the - // REPL contract, move on to globally available types gracefully. - Ok(None) + let ret_id = func.returns[0]; + let ret_var = hir.variable(ret_id); + return Ok(solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into())) + .or_else(|| hir_ty_to_dyn(gcx, &ret_var.ty))); } - } - /// Infers the type from a variable's type - fn infer_var_expr( - expr: &pt::Expression, - intermediate: Option<&IntermediateOutput>, - custom_type: &mut Vec, - ) -> Result> { - // Resolve local (in `run` function) or global (in the `REPL` or other contract) variable - let res = match &expr { - // Custom variable handling - pt::Expression::Variable(ident) => { - let name = &ident.name; - - if let Some(intermediate) = intermediate { - // expression in `run` - if let Some(expr) = intermediate.repl_contract_expressions.get(name) { - Self::infer_var_expr(expr, Some(intermediate), custom_type) - } else if intermediate.intermediate_contracts.contains_key(name) { - if custom_type.len() > 1 { - // There is still some recursing left to do: jump into the contract. - custom_type.pop(); - Self::infer_custom_type(intermediate, custom_type, Some(name.clone())) - } else { - // We have no types left to recurse: return the address of the contract. - Ok(Some(DynSolType::Address)) - } - } else { - Err(eyre::eyre!("Could not infer variable type")) - } - } else { - Ok(None) - } - } - other_expr => Ok(Self::ethabi(other_expr, intermediate)), - }; - // re-run everything with the resolved variable in case we're accessing a builtin member - // for example array or bytes length etc - match res { - Ok(Some(ty)) => { - let box_ty = Box::new(Self::Builtin(ty.clone())); - let access = Self::Access(box_ty, custom_type.drain(..).next().unwrap_or_default()); - if let Some(mapped) = access.map_special().try_as_ethabi(intermediate) { - Ok(Some(mapped)) - } else { - Ok(Some(ty)) + // Variable? + if let Some(vid) = contract + .variables() + .find(|&v| hir.variable(v).name.as_ref().map(|n| n.as_str() == cur).unwrap_or(false)) + { + if let Some(ty) = solar_ty_to_dyn(gcx, gcx.type_of_item(vid.into())) { + custom_type.pop(); + if custom_type.is_empty() { + return Ok(Some(ty)); } + let next_member = custom_type.drain(..).next().unwrap_or(Symbol::DUMMY); + return Ok(dyn_member(&ty, next_member.as_str()).or(Some(ty))); } - res => res, - } - } - - /// Attempt to convert this type into a [DynSolType] - /// - /// ### Takes - /// An immutable reference to an [IntermediateOutput] - /// - /// ### Returns - /// Optionally, a [DynSolType] - fn try_as_ethabi(self, intermediate: Option<&IntermediateOutput>) -> Option { - match self { - Self::Builtin(ty) => Some(ty), - Self::Tuple(types) => Some(DynSolType::Tuple(types_to_parameters(types, intermediate))), - Self::Array(inner) => match *inner { - ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate), - _ => inner - .try_as_ethabi(intermediate) - .map(|inner| DynSolType::Array(Box::new(inner))), - }, - Self::FixedArray(inner, size) => match *inner { - ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate), - _ => inner - .try_as_ethabi(intermediate) - .map(|inner| DynSolType::FixedArray(Box::new(inner), size)), - }, - ty @ Self::ArrayIndex(_, _) => ty.into_array_index(intermediate), - Self::Function(ty, _, _) => ty.try_as_ethabi(intermediate), - // should have been mapped to `Custom` in previous steps - Self::Access(_, _) => None, - Self::Custom(mut types) => { - // Cover any local non-state-modifying function call expressions - intermediate.and_then(|intermediate| { - Self::infer_custom_type(intermediate, &mut types, None).ok().flatten() - }) - } + let var = hir.variable(vid); + return infer_var_ty(gcx, &var.ty, custom_type); } - } - - /// Equivalent to `Type::from_expression` + `Type::map_special` + `Type::try_as_ethabi` - fn ethabi( - expr: &pt::Expression, - intermediate: Option<&IntermediateOutput>, - ) -> Option { - Self::from_expression(expr) - .map(Self::map_special) - .and_then(|ty| ty.try_as_ethabi(intermediate)) - } - /// Get the return type of a function call expression. - fn get_function_return_type<'a>( - contract_expr: Option<&'a pt::Expression>, - intermediate: &IntermediateOutput, - ) -> Option<(&'a pt::Expression, DynSolType)> { - let function_call = match contract_expr? { - pt::Expression::FunctionCall(_, function_call, _) => function_call, - _ => return None, - }; - let (contract_name, function_name) = match function_call.as_ref() { - pt::Expression::MemberAccess(_, contract_name, function_name) => { - (contract_name, function_name) + // Struct? + if let Some(sid) = contract.items.iter().find_map(|i| { + if let ItemId::Struct(sid) = i + && hir.strukt(*sid).name.as_str() == cur + { + Some(*sid) + } else { + None } - _ => return None, - }; - let contract_name = match contract_name.as_ref() { - pt::Expression::Variable(contract_name) => contract_name.to_owned(), - _ => return None, - }; - - let pt::Expression::Variable(contract_name) = - intermediate.repl_contract_expressions.get(&contract_name.name)? - else { - return None; - }; - - let contract = intermediate - .intermediate_contracts - .get(&contract_name.name)? - .function_definitions - .get(&function_name.name)?; - let return_parameter = contract.as_ref().returns.first()?.to_owned().1?; - Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p)) - } - - /// Inverts Int to Uint and vice-versa. - fn invert_int(self) -> Self { - match self { - Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)), - Self::Builtin(DynSolType::Int(n)) => Self::Builtin(DynSolType::Uint(n)), - x => x, + }) { + let inner = gcx + .struct_field_types(sid) + .iter() + .map(|&t| { + solar_ty_to_dyn(gcx, t) + .ok_or_else(|| eyre::eyre!("Struct `{cur}` has invalid fields")) + }) + .collect::>>()?; + return Ok(Some(DynSolType::Tuple(inner))); } - } - /// Returns the `DynSolType` contained by `Type::Builtin` - #[inline] - fn into_builtin(self) -> Option { - match self { - Self::Builtin(ty) => Some(ty), - _ => None, - } + eyre::bail!( + "Could not find any definition in contract \"{}\" for type: {custom_type:?}", + contract.name.as_str() + ) } - /// Returns the resulting `DynSolType` of indexing self - fn into_array_index(self, intermediate: Option<&IntermediateOutput>) -> Option { - match self { - Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => { - match inner.try_as_ethabi(intermediate) { - Some(DynSolType::Array(inner) | DynSolType::FixedArray(inner, _)) => { - Some(*inner) - } - Some(DynSolType::Bytes | DynSolType::String | DynSolType::FixedBytes(_)) => { - Some(DynSolType::FixedBytes(1)) - } - ty => ty, - } - } - _ => None, - } + let repl_id = gcx + .hir + .contracts_enumerated() + .find_map(|(cid, c)| (c.name.as_str() == "REPL").then_some(cid)); + if let Some(repl_id) = repl_id + && let Ok(res) = infer_custom_type(gcx, custom_type, Some(repl_id)) + { + return Ok(res); } - /// Returns whether this type is dynamic - #[inline] - const fn is_dynamic(&self) -> bool { - match self { - // 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, - } + let last_name = *custom_type.last().unwrap(); + let last = last_name.as_str(); + let contract_match = gcx + .hir + .contracts_enumerated() + .find_map(|(cid, c)| (c.name.as_str() == last).then_some(cid)); + if let Some(cid) = contract_match { + custom_type.pop(); + return infer_custom_type(gcx, custom_type, Some(cid)); } - /// Returns whether this type is an array - #[inline] - const fn is_array(&self) -> bool { - matches!( - self, - Self::Array(_) - | Self::FixedArray(_, _) - | Self::Builtin(DynSolType::Array(_) | DynSolType::FixedArray(_, _)) - ) - } + Ok(None) +} - /// Returns whether this type is a dynamic array (can call push, pop) - #[inline] - const fn is_dynamic_array(&self) -> bool { - matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_))) +/// Infers the type from a variable's HIR type, optionally accessing a named member. +fn infer_var_ty( + gcx: Gcx<'_>, + ty: &HirType<'_>, + custom_type: &mut Vec, +) -> Result> { + let Some(ty) = hir_ty_to_dyn(gcx, ty) else { return Ok(None) }; + let next_member = custom_type.drain(..).next(); + if let Some(m) = next_member { + Ok(dyn_member(&ty, m.as_str()).or(Some(ty))) + } else { + Ok(Some(ty)) } +} - const fn is_fixed_bytes(&self) -> bool { - matches!(self, Self::Builtin(DynSolType::FixedBytes(_))) - } +/// Get the return type of a contract method call `receiver.method()`. +fn get_function_return_type(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option { + let ExprKind::Call(callee, _, _) = &expr.kind else { return None }; + let ExprKind::Member(obj, fn_ident) = &callee.kind else { return None }; + let ExprKind::Ident(reses) = &obj.kind else { return None }; + let res = reses.first()?; + let var_id = match res { + Res::Item(ItemId::Variable(vid)) => *vid, + _ => return None, + }; + let var_ty = gcx.type_of_item(var_id.into()).peel_refs(); + let cid = match var_ty.kind { + TyKind::Contract(cid) => cid, + _ => return None, + }; + + let hir = &gcx.hir; + let contract = hir.contract(cid); + let fid = contract + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some(fn_ident.as_str()))?; + let func = hir.function(fid); + let ret_id = *func.returns.first()?; + solar_ty_to_dyn(gcx, gcx.type_of_item(ret_id.into())) } -/// Returns Some if the custom type is a function member access +/// Returns Some if the custom type is a function member access. /// /// Ref: #[inline] -fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option { - if !matches!(func.ty, pt::FunctionTy::Function) { +fn func_members(func: &Function<'_>, custom_type: &[Symbol]) -> Option { + if !matches!(func.kind, FunctionKind::Function) { return None; } - - let vis = func.attributes.iter().find_map(|attr| match attr { - pt::FunctionAttribute::Visibility(vis) => Some(vis), - _ => None, - }); - match vis { - Some(pt::Visibility::External(_) | pt::Visibility::Public(_)) => { - match custom_type.first().unwrap().as_str() { - "address" => Some(DynSolType::Address), - "selector" => Some(DynSolType::FixedBytes(4)), - _ => None, - } - } + if !matches!(func.visibility, Visibility::External | Visibility::Public) { + return None; + } + match custom_type.first().unwrap().as_str() { + "address" => Some(DynSolType::Address), + "selector" => Some(DynSolType::FixedBytes(4)), _ => None, } } -/// Whether execution should continue after inspecting this expression +/// Whether execution should continue after inspecting this expression. #[inline] -fn should_continue(expr: &pt::Expression) -> bool { - match expr { - // assignments - pt::Expression::PreDecrement(_, _) | // -- - pt::Expression::PostDecrement(_, _) | // -- - pt::Expression::PreIncrement(_, _) | // ++ - pt::Expression::PostIncrement(_, _) | // ++ - pt::Expression::Assign(_, _, _) | // = ... - pt::Expression::AssignAdd(_, _, _) | // += ... - pt::Expression::AssignSubtract(_, _, _) | // -= ... - pt::Expression::AssignMultiply(_, _, _) | // *= ... - pt::Expression::AssignDivide(_, _, _) | // /= ... - pt::Expression::AssignModulo(_, _, _) | // %= ... - pt::Expression::AssignAnd(_, _, _) | // &= ... - pt::Expression::AssignOr(_, _, _) | // |= ... - pt::Expression::AssignXor(_, _, _) | // ^= ... - pt::Expression::AssignShiftLeft(_, _, _) | // <<= ... - pt::Expression::AssignShiftRight(_, _, _) // >>= ... - => { - true - } - +fn should_continue(expr: &Expr<'_>) -> bool { + match &expr.kind { + // assignments and compound assignments + ExprKind::Assign(_, _, _) => true, + // ++/-- pre/post operations + ExprKind::Unary(op, _) => matches!( + op.kind, + UnOpKind::PreInc | UnOpKind::PreDec | UnOpKind::PostInc | UnOpKind::PostDec + ), // Array.pop() - pt::Expression::FunctionCall(_, lhs, _) => { - match lhs.as_ref() { - pt::Expression::MemberAccess(_, _inner, access) => access.name == "pop", - _ => false - } - } - - _ => false + ExprKind::Call(callee, _, _) => match &callee.kind { + ExprKind::Member(_, ident) => ident.as_str() == "pop", + _ => false, + }, + _ => false, } } -fn map_parameters(params: &[(pt::Loc, Option)]) -> Vec> { - params - .iter() - .map(|(_, param)| param.as_ref().and_then(|param| Type::from_expression(¶m.ty))) - .collect() +/// Parses an [`Expr`] number/hex literal into a `U256`. Returns `None` if the expression +/// is not a numeric literal. +/// +/// SubDenominations are already applied to numeric literals in solar's HIR. +const fn parse_number_literal(expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::Lit(lit) => match &lit.kind { + LitKind::Number(n) => Some(*n), + _ => None, + }, + _ => None, + } } -fn types_to_parameters( - types: Vec>, - intermediate: Option<&IntermediateOutput>, -) -> Vec { - types.into_iter().filter_map(|ty| ty.and_then(|ty| ty.try_as_ethabi(intermediate))).collect() +/// Maps a solar [`ElementaryType`] to a [`DynSolType`]. +const fn elementary_to_dyn(et: ElementaryType) -> Option { + Some(match et { + ElementaryType::Address(_) => DynSolType::Address, + ElementaryType::Bool => DynSolType::Bool, + ElementaryType::String => DynSolType::String, + ElementaryType::Bytes => DynSolType::Bytes, + ElementaryType::Int(size) => DynSolType::Int(size.bits() as usize), + ElementaryType::UInt(size) => DynSolType::Uint(size.bits() as usize), + ElementaryType::FixedBytes(size) => DynSolType::FixedBytes(size.bytes() as usize), + // Fixed-point numbers are not yet representable as DynSolType. + ElementaryType::Fixed(_, _) | ElementaryType::UFixed(_, _) => return None, + }) } -fn parse_number_literal(expr: &pt::Expression) -> Option { - match expr { - pt::Expression::NumberLiteral(_, num, exp, unit) => { - let num = num.parse::().unwrap_or(U256::ZERO); - let exp = exp.parse().unwrap_or(0u32); - if exp > 77 { - None +/// Maps a solar [`Ty`] to a [`DynSolType`]. +fn solar_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> Option { + match ty.kind { + TyKind::Elementary(et) => elementary_to_dyn(et), + TyKind::Ref(inner, _) => solar_ty_to_dyn(gcx, inner), + TyKind::Array(elem, n) => { + let inner = solar_ty_to_dyn(gcx, elem)?; + let size: usize = n.try_into().ok()?; + Some(DynSolType::FixedArray(Box::new(inner), size)) + } + TyKind::DynArray(elem) | TyKind::Slice(elem) => { + let inner = solar_ty_to_dyn(gcx, elem)?; + Some(DynSolType::Array(Box::new(inner))) + } + TyKind::Tuple(tys) => { + Some(DynSolType::Tuple(tys.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect())) + } + TyKind::Mapping(_, _) => None, + TyKind::Struct(sid) => Some(DynSolType::Tuple( + gcx.struct_field_types(sid).iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(), + )), + TyKind::Enum(_) => Some(DynSolType::Uint(8)), + TyKind::Udvt(inner, _) => solar_ty_to_dyn(gcx, inner), + TyKind::Contract(_) => Some(DynSolType::Address), + // For a function-pointer type we return the ABI type of what the call *produces*, not a + // representation of the pointer itself. This is intentional: chisel inspects values, so + // the interesting type is the returned value. A zero-return function pointer has no + // inspectable value, so we return `None`. + TyKind::FnPtr(f) => match f.returns.len() { + 0 => None, + 1 => solar_ty_to_dyn(gcx, f.returns[0]), + _ => Some(DynSolType::Tuple( + f.returns.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(), + )), + }, + TyKind::Type(inner) => solar_ty_to_dyn(gcx, inner), + TyKind::Meta(inner) => solar_ty_to_dyn(gcx, inner), + TyKind::IntLiteral(neg, size) => { + let bits = (size.bits() as usize).max(8); + // Round up to the nearest multiple of 8 bits, capped at 256. + let bits = bits.div_ceil(8) * 8; + let bits = bits.min(256); + if neg { + Some(DynSolType::Int(bits.max(8))) } else { - let exp = U256::from(10usize.pow(exp)); - let unit_mul = unit_multiplier(unit).ok()?; - Some(num * exp * unit_mul) + Some(DynSolType::Uint(bits.max(8))) } } - pt::Expression::HexNumberLiteral(_, num, unit) => { - let unit_mul = unit_multiplier(unit).ok()?; - num.parse::().map(|num| num * unit_mul).ok() + TyKind::StringLiteral(valid_utf8, _) => { + if valid_utf8 { + Some(DynSolType::String) + } else { + Some(DynSolType::Bytes) + } } - // TODO: Rational numbers - pt::Expression::RationalNumberLiteral(..) => None, + TyKind::Module(_) + | TyKind::BuiltinModule(_) + | TyKind::Error(_, _) + | TyKind::Event(_, _) + | TyKind::Err(_) => None, _ => None, } } -#[inline] -fn unit_multiplier(unit: &Option) -> Result { - if let Some(unit) = unit { - let mul = match unit.name.as_str() { - "seconds" => 1, - "minutes" => 60, - "hours" => 60 * 60, - "days" => 60 * 60 * 24, - "weeks" => 60 * 60 * 24 * 7, - "wei" => 1, - "gwei" => 10_usize.pow(9), - "ether" => 10_usize.pow(18), - other => eyre::bail!("unknown unit: {other}"), - }; - Ok(U256::from(mul)) - } else { - Ok(U256::from(1)) - } -} - #[cfg(test)] mod tests { use super::*; use foundry_compilers::{error::SolcError, solc::Solc}; + use solar::sema::Compiler; use std::sync::Mutex; #[test] @@ -1558,46 +1455,66 @@ mod tests { DynSolType::FixedArray(Box::new(ty), len) } - fn parse(s: &mut SessionSource, input: &str, clear: bool) -> IntermediateOutput { + /// Lowers the given snippet appended to the REPL contract via solar's HIR pipeline (without + /// invoking solc) and returns the resulting `DynSolType` of the last expression statement in + /// the run() body. + /// + /// Tests bypass `SessionSource::build` (which routes through foundry-compilers + solc) so that + /// inputs which are syntactically valid but semantically rejected by solc (e.g. + /// `abi.decode(bytes, (uint8[13]))` or `a[0:3]` on a memory array) can still exercise the + /// HIR-based type-inference engine. + fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option { if clear { s.clear(); } + // Always declare a sample enum so `Enum1` is available for `type(Enum1)` tests. *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0; let input = format!("{};", input.trim_end().trim_end_matches(';')); - let (mut _s, _) = s.clone_with_new_line(input).unwrap(); - *s = _s.clone(); - let s = &mut _s; - - if let Err(e) = s.parse() { - let source = s.to_repl_source(); - panic!("{e}\n\ncould not parse input:\n{source}") - } - s.generate_intermediate_output().expect("could not generate intermediate output") - } - - fn expr(stmts: &[pt::Statement]) -> pt::Expression { - match stmts.last().expect("no statements") { - pt::Statement::Expression(_, e) => e.clone(), - s => panic!("Not an expression: {s:?}"), - } - } - - fn get_type( - s: &mut SessionSource, - input: &str, - clear: bool, - ) -> (Option, IntermediateOutput) { - let intermediate = parse(s, input, clear); - let run_func_body = intermediate.run_func_body().expect("no run func body"); - let expr = expr(run_func_body); - (Type::from_expression(&expr).map(Type::map_special), intermediate) - } + let (new_source, _) = s.clone_with_new_line(input).unwrap(); + *s = new_source.clone(); + + let src = new_source.to_repl_source(); + let sess = + solar::interface::Session::builder().with_buffer_emitter(Default::default()).build(); + let mut compiler = Compiler::new(sess); + + compiler.enter_mut(|c| -> Option { + // Stage 1: parse + lower (mutable access required). + let lowered = { + let mut pcx = c.parse(); + let file = c + .sess() + .source_map() + .new_source_file( + std::path::PathBuf::from(new_source.file_name.clone()), + src.clone(), + ) + .ok()?; + pcx.add_file(file); + pcx.parse(); + matches!(c.lower_asts(), Ok(ControlFlow::Continue(()))) + }; + if !lowered { + return None; + } - fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option { - let (ty, intermediate) = get_type(s, input, clear); - ty.and_then(|ty| ty.try_as_ethabi(Some(&intermediate))) + // Stage 2: walk HIR (immutable access). + let gcx = c.gcx(); + let hir = &gcx.hir; + let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?; + let run_fid = repl + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?; + let body = hir.function(run_fid).body?; + let last = body.last()?; + let expr = match last.kind { + StmtKind::Expr(e) => e, + _ => return None, + }; + expr_to_dyn(gcx, expr, true) + }) } fn generic_type_test<'a, T, I>(s: &mut SessionSource, input: I) diff --git a/crates/chisel/src/source.rs b/crates/chisel/src/source.rs index 7eedb31923439..90c0bad874622 100644 --- a/crates/chisel/src/source.rs +++ b/crates/chisel/src/source.rs @@ -5,8 +5,6 @@ //! execution helpers. use eyre::Result; -use forge_doc::solang_ext::{CodeLocationExt, SafeUnwrap}; -use foundry_common::fs; use foundry_compilers::{ Artifact, ProjectCompileOutput, artifacts::{ConfigurableContractArtifact, Source, Sources}, @@ -17,9 +15,16 @@ use foundry_config::{Config, SolcReq}; use foundry_evm::{backend::Backend, core::bytecode::InstIter, opts::EvmOpts}; use semver::Version; use serde::{Deserialize, Serialize}; -use solang_parser::pt; -use solar::interface::diagnostics::EmittedDiagnostics; -use std::{cell::OnceCell, collections::HashMap, fmt, path::PathBuf}; +use solar::{ + ast::{ItemKind, StmtKind as AstStmtKind, yul}, + interface::{Span, diagnostics::EmittedDiagnostics}, + sema::{ + CompilerRef, + hir::{Block, Contract, EventId, ItemId, Stmt, StmtKind as HirStmtKind}, + ty::Gcx, + }, +}; +use std::{cell::OnceCell, fmt}; use walkdir::WalkDir; /// The minimum Solidity version of the `Vm` interface. @@ -31,41 +36,8 @@ static VM_SOURCE: &str = include_str!("../../../testdata/utils/Vm.sol"); /// [`SessionSource`] build output. pub struct GeneratedOutput { output: ProjectCompileOutput, - pub(crate) intermediate: IntermediateOutput, -} - -pub struct GeneratedOutputRef<'a> { - output: &'a ProjectCompileOutput, - // compiler: &'b solar::sema::CompilerRef<'c>, - pub(crate) intermediate: &'a IntermediateOutput, -} - -/// Intermediate output for the compiled [SessionSource] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct IntermediateOutput { - /// All expressions within the REPL contract's run function and top level scope. - pub repl_contract_expressions: HashMap, - /// Intermediate contracts - pub intermediate_contracts: IntermediateContracts, -} - -/// A refined intermediate parse tree for a contract that enables easy lookups -/// of definitions. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct IntermediateContract { - /// All function definitions within the contract - pub function_definitions: HashMap>, - /// All event definitions within the contract - pub event_definitions: HashMap>, - /// All struct definitions within the contract - pub struct_definitions: HashMap>, - /// All variable definitions within the top level scope of the contract - pub variable_definitions: HashMap>, } -/// A defined type for a map of contract names to [IntermediateContract]s -type IntermediateContracts = HashMap; - impl fmt::Debug for GeneratedOutput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("GeneratedOutput").finish_non_exhaustive() @@ -73,158 +45,25 @@ impl fmt::Debug for GeneratedOutput { } impl GeneratedOutput { - pub fn enter(&self, f: impl FnOnce(GeneratedOutputRef<'_>) -> T + Send) -> T { - // TODO(dani): once intermediate is removed - // self.output - // .parser() - // .solc() - // .compiler() - // .enter(|compiler| f(GeneratedOutputRef { output: &self.output, compiler })) - f(GeneratedOutputRef { output: &self.output, intermediate: &self.intermediate }) - } -} - -impl GeneratedOutputRef<'_> { - pub fn repl_contract(&self) -> Option<&ConfigurableContractArtifact> { - self.output.find_first("REPL") - } -} - -impl std::ops::Deref for GeneratedOutput { - type Target = IntermediateOutput; - fn deref(&self) -> &Self::Target { - &self.intermediate - } -} -impl std::ops::Deref for GeneratedOutputRef<'_> { - type Target = IntermediateOutput; - fn deref(&self) -> &Self::Target { - self.intermediate + /// Enters the solar compiler context, providing access to the HIR and `Gcx`. + pub fn enter( + &self, + f: impl for<'a, 'b, 'gcx> FnOnce(GeneratedOutputRef<'a, 'b, 'gcx>) -> R + Send, + ) -> R { + self.output + .parser() + .solc() + .compiler() + .enter(|c| f(GeneratedOutputRef { output: &self.output, compiler: c })) } } -impl IntermediateOutput { - pub fn get_event(&self, input: &str) -> Option<&pt::EventDefinition> { - self.intermediate_contracts - .get("REPL") - .and_then(|contract| contract.event_definitions.get(input).map(std::ops::Deref::deref)) - } - - pub fn final_pc(&self, contract: &ConfigurableContractArtifact) -> Result> { - let deployed_bytecode = contract - .get_deployed_bytecode() - .ok_or_else(|| eyre::eyre!("No deployed bytecode found for `REPL` contract"))?; - let deployed_bytecode_bytes = deployed_bytecode - .bytes() - .ok_or_else(|| eyre::eyre!("No deployed bytecode found for `REPL` contract"))?; - - let run_func_statements = self.run_func_body()?; - - // Record loc of first yul block return statement (if any). - // This is used to decide which is the final statement within the `run()` method. - // see . - let last_yul_return = run_func_statements.iter().find_map(|statement| { - if let pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } = statement - && let Some(statement) = block.statements.last() - && let pt::YulStatement::FunctionCall(yul_call) = statement - && yul_call.id.name == "return" - { - return Some(statement.loc()); - } - None - }); - - // Find the last statement within the "run()" method and get the program - // counter via the source map. - let Some(final_statement) = run_func_statements.last() else { return Ok(None) }; - - // If the final statement is some type of block (assembly, unchecked, or regular), - // we need to find the final statement within that block. Otherwise, default to - // the source loc of the final statement of the `run()` function's block. - // - // There is some code duplication within the arms due to the difference between - // the [pt::Statement] type and the [pt::YulStatement] types. - let mut source_loc = match final_statement { - pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => { - // Select last non variable declaration statement, see . - let last_statement = block.statements.iter().rev().find(|statement| { - !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _)) - }); - if let Some(statement) = last_statement { - statement.loc() - } else { - // In the case where the block is empty, attempt to grab the statement - // before the asm block. Because we use saturating sub to get the second - // to last index, this can always be safely unwrapped. - run_func_statements - .get(run_func_statements.len().saturating_sub(2)) - .unwrap() - .loc() - } - } - pt::Statement::Block { loc: _, unchecked: _, statements } => { - if let Some(statement) = statements.last() { - statement.loc() - } else { - // In the case where the block is empty, attempt to grab the statement - // before the block. Because we use saturating sub to get the second to - // last index, this can always be safely unwrapped. - run_func_statements - .get(run_func_statements.len().saturating_sub(2)) - .unwrap() - .loc() - } - } - _ => final_statement.loc(), - }; - - // Consider yul return statement as final statement (if it's loc is lower) . - if let Some(yul_return) = last_yul_return - && yul_return.end() < source_loc.start() - { - source_loc = yul_return; - } - - // 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() as u32; - let length = (source_loc.end() - source_loc.start()) as u32; - trace!(%offset, %length, "find pc"); - contract - .get_source_map_deployed() - .unwrap() - .unwrap() - .into_iter() - .zip(InstIter::new(deployed_bytecode_bytes).with_pc().map(|(pc, _)| pc)) - .filter(|(s, _)| s.offset() == offset && s.length() == length) - .map(|(_, pc)| pc) - .max() - }; - trace!(?final_pc); - Ok(final_pc) - } - - pub fn run_func_body(&self) -> Result<&Vec> { - match self - .intermediate_contracts - .get("REPL") - .ok_or_else(|| eyre::eyre!("Could not find REPL intermediate contract!"))? - .function_definitions - .get("run") - .ok_or_else(|| eyre::eyre!("Could not find run function definition in REPL contract!"))? - .body - .as_ref() - .ok_or_else(|| eyre::eyre!("Could not find run function body!"))? - { - pt::Statement::Block { statements, .. } => Ok(statements), - _ => eyre::bail!("Could not find statements within run function body!"), - } - } +/// A scoped reference to a [`GeneratedOutput`] together with an entered solar compiler. +pub struct GeneratedOutputRef<'a, 'b, 'gcx> { + output: &'a ProjectCompileOutput, + pub(crate) compiler: &'b CompilerRef<'gcx>, } -// TODO(dani): further migration blocked on upstream work -#[cfg(false)] impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { pub fn gcx(&self) -> Gcx<'gcx> { self.compiler.gcx() @@ -234,8 +73,35 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { self.output.find_first("REPL") } - pub fn get_event(&self, input: &str) -> Option { - self.gcx().hir.events_enumerated().find(|(_, e)| e.name.as_str() == input).map(|(id, _)| id) + /// Looks up the REPL contract in the HIR. + pub fn repl_contract_hir(&self) -> Option<&'gcx Contract<'gcx>> { + self.gcx().hir.contracts().find(|c| c.name.as_str() == "REPL") + } + + /// Returns the body block of the REPL `run()` function. + pub fn run_func_body(&self) -> Block<'gcx> { + let hir = &self.gcx().hir; + let c = self.repl_contract_hir().expect("REPL contract not found in HIR"); + let f = c + .functions() + .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run")) + .expect("`run()` function not found in REPL contract"); + hir.function(f).body.expect("`run()` function does not have a body") + } + + /// Returns the [`EventId`] of an event named `input` in the REPL contract, if any. + pub fn get_event(&self, input: &str) -> Option { + let hir = &self.gcx().hir; + let c = self.repl_contract_hir()?; + c.items.iter().find_map(|id| { + if let ItemId::Event(eid) = id + && hir.event(*eid).name.as_str() == input + { + Some(*eid) + } else { + None + } + }) } pub fn final_pc(&self, contract: &ConfigurableContractArtifact) -> Result> { @@ -252,52 +118,25 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { // Record loc of first yul block return statement (if any). // This is used to decide which is the final statement within the `run()` method. // see . - let last_yul_return_span: Option = run_body.iter().find_map(|stmt| { - // TODO(dani): Yul is not yet lowered to HIR. - let _ = stmt; - /* - if let hir::StmtKind::Assembly { block, .. } = stmt { - if let Some(stmt) = block.last() { - if let pt::YulStatement::FunctionCall(yul_call) = stmt { - if yul_call.id.name == "return" { - return Some(stmt.loc()) - } - } - } - } - */ - None - }); + // + // Yul is not yet lowered to HIR (assembly statements appear as `StmtKind::Err`), + // so we walk the AST of the REPL source to find a top-level `return(...)` call + // inside any `assembly { ... }` block in `run()`. + let last_yul_return_span: Option = self.first_yul_return_span(); // Find the last statement within the "run()" method and get the program // counter via the source map. let Some(last_stmt) = run_body.last() else { return Ok(None) }; - // If the final statement is some type of block (assembly, unchecked, or regular), + // If the final statement is some type of block (unchecked or regular), // we need to find the final statement within that block. Otherwise, default to // the source loc of the final statement of the `run()` function's block. // - // There is some code duplication within the arms due to the difference between - // the [pt::Statement] type and the [pt::YulStatement] types. + // Inline assembly blocks (lowered to `StmtKind::Err` in HIR in the pinned solar + // version) are handled separately via `trailing_assembly_last_stmt_span`, which + // walks the AST to recover the last meaningful Yul statement. let source_stmt = match &last_stmt.kind { - // TODO(dani): Yul is not yet lowered to HIR. - /* - pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => { - // Select last non variable declaration statement, see . - let last_statement = block.statements.iter().rev().find(|statement| { - !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _)) - }); - if let Some(stmt) = last_statement { - stmt - } else { - // In the case where the block is empty, attempt to grab the statement - // before the block. Because we use saturating sub to get the second to - // last index, this can always be safely unwrapped. - &run_body[run_body.len().saturating_sub(2)] - } - } - */ - hir::StmtKind::UncheckedBlock(stmts) | hir::StmtKind::Block(stmts) => { + HirStmtKind::UncheckedBlock(stmts) | HirStmtKind::Block(stmts) => { if let Some(stmt) = stmts.last() { stmt } else { @@ -309,9 +148,25 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { } _ => last_stmt, }; - let mut source_span = self.stmt_span_without_semicolon(source_stmt); + // If the trailing statement is an assembly block, prefer the last meaningful + // (non-`let`) Yul statement's span as the source location for `final_pc`. + // See . + // + // Two guards are required: + // 1. `StmtKind::Err`, assembly lowers to an error node in the current pinned solar + // version; this ensures we don't apply the AST fallback to properly-lowered stmts. + // 2. `trailing_assembly_last_stmt_span` returning `Some`, verifies via the AST that the + // failing HIR node actually corresponds to an assembly block (not some other lowering + // failure), and supplies the concrete span to use. + let mut source_span = if matches!(last_stmt.kind, HirStmtKind::Err(_)) + && let Some(span) = self.trailing_assembly_last_stmt_span() + { + span + } else { + self.stmt_span_without_semicolon(source_stmt) + }; - // Consider yul return statement as final statement (if it's loc is lower) . + // Consider yul return statement as final statement (if it's loc is lower). if let Some(yul_return_span) = last_yul_return_span && yul_return_span.hi() < source_span.lo() { @@ -320,26 +175,32 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { // Map the source location of the final statement of the `run()` function to its // corresponding runtime program counter - let (_sf, range) = self.compiler.sess().source_map().span_to_source(source_span).unwrap(); - dbg!(source_span, &range, &_sf.src[range.clone()]); + let result = self + .compiler + .sess() + .source_map() + .span_to_source(source_span) + .map_err(|e| eyre::eyre!("failed to resolve span: {e:?}"))?; + let range = result.data; let offset = range.start as u32; let length = range.len() as u32; - let final_pc = deployed_bytecode - .source_map() + trace!(%offset, %length, "find pc"); + let final_pc = contract + .get_source_map_deployed() .ok_or_else(|| eyre::eyre!("No source map found for `REPL` contract"))?? .into_iter() - .zip(InstructionIter::new(deployed_bytecode_bytes)) + .zip(InstIter::new(deployed_bytecode_bytes).with_pc().map(|(pc, _)| pc)) .filter(|(s, _)| s.offset() == offset && s.length() == length) - .map(|(_, i)| i.pc) - .max() - .unwrap_or_default(); - Ok(Some(final_pc)) + .map(|(_, pc)| pc) + .max(); + trace!(?final_pc); + Ok(final_pc) } /// Statements' ranges in the solc source map do not include the semicolon. - fn stmt_span_without_semicolon(&self, stmt: &hir::Stmt<'_>) -> Span { + fn stmt_span_without_semicolon(&self, stmt: &Stmt<'_>) -> Span { match stmt.kind { - hir::StmtKind::DeclSingle(id) => { + HirStmtKind::DeclSingle(id) => { let decl = self.gcx().hir.variable(id); if let Some(expr) = decl.initializer { stmt.span.with_hi(expr.span.hi()) @@ -347,23 +208,65 @@ impl<'gcx> GeneratedOutputRef<'_, '_, 'gcx> { stmt.span } } - hir::StmtKind::DeclMulti(_, expr) => stmt.span.with_hi(expr.span.hi()), - hir::StmtKind::Expr(expr) => expr.span, + HirStmtKind::DeclMulti(_, expr) => stmt.span.with_hi(expr.span.hi()), + HirStmtKind::Expr(expr) => expr.span, _ => stmt.span, } } - fn run_func_body(&self) -> hir::Block<'_> { - let c = self.repl_contract_hir().expect("REPL contract not found in HIR"); - let f = c - .functions() - .find(|&f| self.gcx().hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run")) - .expect("`run()` function not found in REPL contract"); - self.gcx().hir.function(f).body.expect("`run()` function does not have a body") + /// Returns the AST `run()` body of the REPL contract, if any. + /// + /// Yul/assembly is not yet lowered to HIR in the pinned solar version, so we + /// keep around the AST to be able to inspect inline assembly blocks. + fn repl_run_ast_body(&self) -> Option<&'gcx solar::ast::Block<'gcx>> { + let contract = self.repl_contract_hir()?; + let source = self.gcx().sources.get(contract.source)?; + let ast = source.ast.as_ref()?; + + let contract_ast = ast.items.iter().find_map(|i| match &i.kind { + ItemKind::Contract(c) if c.name.as_str() == "REPL" => Some(c), + _ => None, + })?; + contract_ast.body.iter().find_map(|i| match &i.kind { + ItemKind::Function(f) if f.header.name.is_some_and(|n| n.as_str() == "run") => { + f.body.as_ref() + } + _ => None, + }) } - fn repl_contract_hir(&self) -> Option<&hir::Contract<'_>> { - self.gcx().hir.contracts().find(|c| c.name.as_str() == "REPL") + /// Returns the span of the first top-level `return(...)` call inside any + /// `assembly { ... }` block in the REPL `run()` function, if any. + fn first_yul_return_span(&self) -> Option { + let run_body = self.repl_run_ast_body()?; + for stmt in run_body.stmts.iter() { + let AstStmtKind::Assembly(asm) = &stmt.kind else { continue }; + for ystmt in asm.block.stmts.iter() { + if let yul::StmtKind::Expr(e) = &ystmt.kind + && let yul::ExprKind::Call(call) = &e.kind + && call.name.as_str() == "return" + { + return Some(ystmt.span); + } + } + } + None + } + + /// If the last statement of the REPL `run()` function is an `assembly { ... }` block, + /// returns the span of its last non-`let` (i.e. non-VarDecl) Yul statement. + /// + /// This mirrors the legacy behavior used to pick a meaningful end-of-function PC when + /// the trailing statement is inline assembly. + fn trailing_assembly_last_stmt_span(&self) -> Option { + let run_body = self.repl_run_ast_body()?; + let AstStmtKind::Assembly(asm) = &run_body.stmts.last()?.kind else { return None }; + asm.block + .stmts + .iter() + .rev() + .find(|s| !matches!(s.kind, yul::StmtKind::VarDecl(_, _))) + .map(|s| s.span) } } @@ -585,8 +488,7 @@ impl SessionSource { return Ok(output); } let output = self.compile()?; - let intermediate = self.generate_intermediate_output()?; - let output = GeneratedOutput { output, intermediate }; + let output = GeneratedOutput { output }; Ok(self.output.get_or_init(|| output)) } @@ -603,12 +505,11 @@ impl SessionSource { eyre::bail!("{output}"); } - // TODO(dani): re-enable - if cfg!(false) { - output.parser_mut().solc_mut().compiler_mut().enter_mut(|c| { - let _ = c.lower_asts(); - }); - } + // Drive HIR lowering and analysis so that subsequent `enter` queries can use them. + output.parser_mut().solc_mut().compiler_mut().enter_mut(|c| { + let _ = c.lower_asts(); + let _ = c.analysis(); + }); Ok(output) } @@ -633,53 +534,6 @@ impl SessionSource { sources } - /// Generate intermediate contracts for all contract definitions in the compilation source. - /// - /// ### Returns - /// - /// Optionally, a map of contract names to a vec of [IntermediateContract]s. - pub fn generate_intermediate_contracts(&self) -> Result> { - let mut res_map = HashMap::default(); - let parsed_map = self.get_sources(); - for source in parsed_map.values() { - Self::get_intermediate_contract(&source.content, &mut res_map); - } - Ok(res_map) - } - - /// Generate intermediate output for the REPL contract - pub fn generate_intermediate_output(&self) -> Result { - // Parse generate intermediate contracts - let intermediate_contracts = self.generate_intermediate_contracts()?; - - // Construct variable definitions - let variable_definitions = intermediate_contracts - .get("REPL") - .ok_or_else(|| eyre::eyre!("Could not find intermediate REPL contract!"))? - .variable_definitions - .clone() - .into_iter() - .map(|(k, v)| (k, v.ty)) - .collect::>(); - // Construct intermediate output - let mut intermediate_output = IntermediateOutput { - repl_contract_expressions: variable_definitions, - intermediate_contracts, - }; - - // Add all statements within the run function to the repl_contract_expressions map - for (key, val) in intermediate_output - .run_func_body()? - .clone() - .iter() - .flat_map(Self::get_statement_definitions) - { - intermediate_output.repl_contract_expressions.insert(key, val); - } - - Ok(intermediate_output) - } - /// Construct the REPL source. pub fn to_repl_source(&self) -> String { let Self { @@ -742,108 +596,6 @@ contract {contract_name} {{ }); sess.dcx.emitted_errors().unwrap() } - - /// Gets the [IntermediateContract] for a Solidity source string and inserts it into the - /// passed `res_map`. In addition, recurses on any imported files as well. - /// - /// ### Takes - /// - `content` - A Solidity source string - /// - `res_map` - A mutable reference to a map of contract names to [IntermediateContract]s - pub fn get_intermediate_contract( - content: &str, - res_map: &mut HashMap, - ) { - if let Ok((pt::SourceUnit(source_unit_parts), _)) = solang_parser::parse(content, 0) { - let func_defs = source_unit_parts - .into_iter() - .filter_map(|sup| match sup { - pt::SourceUnitPart::ImportDirective(i) => match i { - pt::Import::Plain(s, _) - | pt::Import::Rename(s, _, _) - | pt::Import::GlobalSymbol(s, _, _) => { - let s = match s { - pt::ImportPath::Filename(s) => s.string, - pt::ImportPath::Path(p) => p.to_string(), - }; - let path = PathBuf::from(s); - - match fs::read_to_string(path) { - Ok(source) => { - Self::get_intermediate_contract(&source, res_map); - None - } - Err(_) => None, - } - } - }, - pt::SourceUnitPart::ContractDefinition(cd) => { - let mut intermediate = IntermediateContract::default(); - - cd.parts.into_iter().for_each(|part| match part { - pt::ContractPart::FunctionDefinition(def) => { - // Only match normal function definitions here. - if matches!(def.ty, pt::FunctionTy::Function) { - intermediate - .function_definitions - .insert(def.name.clone().unwrap().name, def); - } - } - pt::ContractPart::EventDefinition(def) => { - let event_name = def.name.safe_unwrap().name.clone(); - intermediate.event_definitions.insert(event_name, def); - } - pt::ContractPart::StructDefinition(def) => { - let struct_name = def.name.safe_unwrap().name.clone(); - intermediate.struct_definitions.insert(struct_name, def); - } - pt::ContractPart::VariableDefinition(def) => { - let var_name = def.name.safe_unwrap().name.clone(); - intermediate.variable_definitions.insert(var_name, def); - } - _ => {} - }); - Some((cd.name.safe_unwrap().name.clone(), intermediate)) - } - _ => None, - }) - .collect::>(); - res_map.extend(func_defs); - } - } - - /// Helper to deconstruct a statement - /// - /// ### Takes - /// - /// A reference to a [pt::Statement] - /// - /// ### Returns - /// - /// A vector containing tuples of the inner expressions' names, types, and storage locations. - pub fn get_statement_definitions(statement: &pt::Statement) -> Vec<(String, pt::Expression)> { - match statement { - pt::Statement::VariableDefinition(_, def, _) => { - vec![(def.name.safe_unwrap().name.clone(), def.ty.clone())] - } - pt::Statement::Expression(_, pt::Expression::Assign(_, left, _)) => { - if let pt::Expression::List(_, list) = left.as_ref() { - list.iter() - .filter_map(|(_, param)| { - param.as_ref().and_then(|param| { - param - .name - .as_ref() - .map(|name| (name.name.clone(), param.ty.clone())) - }) - }) - .collect() - } else { - Vec::default() - } - } - _ => Vec::default(), - } - } } /// A Parse Tree Fragment diff --git a/crates/chisel/tests/it/repl/mod.rs b/crates/chisel/tests/it/repl/mod.rs index 704d30405eed9..338b7d2043809 100644 --- a/crates/chisel/tests/it/repl/mod.rs +++ b/crates/chisel/tests/it/repl/mod.rs @@ -153,6 +153,26 @@ assembly { repl.expect("[0x00:0x20]"); }); +// Assembly as the final statement with a return — exercises the path where both +// `first_yul_return_span` and `trailing_assembly_last_stmt_span` resolve to the same `return(...)` +// span (no subsequent Solidity statement after the assembly block). +repl_test!(assembly_return_final, |repl| { + repl.sendln("uint x = 0xbeef;"); + repl.sendln("assembly { mstore(0x0, sload(0)) return(0x0, 0x20) }"); + repl.sendln("!md"); + repl.expect("[0x00:0x20]"); +}); + +// Assembly block without a `return(...)` call as an intermediate statement, exercises +// `first_yul_return_span` returning `None` while a subsequent Solidity statement is still evaluated +// correctly. +repl_test!(assembly_no_return_intermediate, |repl| { + repl.sendln("uint x = 1;"); + repl.sendln("assembly { x := add(x, 1) }"); + repl.sendln("x"); + repl.expect("Decimal: 2"); +}); + // Issue #5051, #8978: Test EVM version normalization. repl_test!(flaky_evm_version_normalization, "--use 0.7.6 --evm-version london", |repl| { repl.sendln("uint x;\nx"); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 37340f5f4cc3c..606b8291819e4 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -52,6 +52,7 @@ rayon.workspace = true regex = { workspace = true, default-features = false } serde_json.workspace = true serde.workspace = true +toml.workspace = true strsim = "0.11" strum = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["macros"] } @@ -70,7 +71,13 @@ tempfile.workspace = true tikv-jemallocator = { workspace = true, optional = true } [features] +default = ["optimism"] tracy = ["dep:tracing-tracy"] tracy-allocator = ["tracy"] jemalloc = ["dep:tikv-jemallocator"] mimalloc = ["dep:mimalloc"] +optimism = [ + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-evm/optimism", +] diff --git a/crates/cli/src/opts/evm.rs b/crates/cli/src/opts/evm.rs index 87f14e2039606..4fc437c7232f8 100644 --- a/crates/cli/src/opts/evm.rs +++ b/crates/cli/src/opts/evm.rs @@ -307,6 +307,17 @@ mod tests { assert_eq!(val, &Value::from(1000u64)); } + #[test] + fn rpc_url_arg_does_not_read_eth_rpc_url_env() { + use clap::CommandFactory; + + let command = EvmArgs::command(); + let rpc_url = + command.get_arguments().find(|arg| arg.get_id() == "rpc_url").expect("rpc_url arg"); + + assert!(rpc_url.get_env().is_none()); + } + #[test] fn can_parse_chain_id() { let args = EvmArgs { diff --git a/crates/cli/src/opts/rpc.rs b/crates/cli/src/opts/rpc.rs index 8c37860446683..f846da5002354 100644 --- a/crates/cli/src/opts/rpc.rs +++ b/crates/cli/src/opts/rpc.rs @@ -66,8 +66,20 @@ impl figment::Provider for RpcOpts { impl RpcOpts { /// Returns the RPC endpoint. pub fn url<'a>(&'a self, config: Option<&'a Config>) -> Result>> { + self.url_with_env(config, std::env::var("ETH_RPC_URL").ok()) + } + + fn url_with_env<'a>( + &'a self, + config: Option<&'a Config>, + env_url: Option, + ) -> Result>> { if self.flashbots { Ok(Some(Cow::Borrowed(FLASHBOTS_URL))) + } else if let Some(url) = self.common.rpc_url.as_deref() { + Ok(Some(Cow::Borrowed(url))) + } else if let Some(url) = env_url { + Ok(Some(Cow::Owned(url))) } else { self.common.url(config) } @@ -85,8 +97,10 @@ impl RpcOpts { pub fn dict(&self) -> Dict { let mut dict = self.common.dict(); - if self.flashbots { - dict.insert("eth_rpc_url".into(), FLASHBOTS_URL.into()); + // `self.url(None)` already accounts for `flashbots` and the `ETH_RPC_URL` env var, + // so a single insert here covers both. + if let Ok(Some(url)) = self.url(None) { + dict.insert("eth_rpc_url".into(), url.into_owned().into()); } if let Ok(Some(jwt)) = self.jwt(None) { dict.insert("eth_rpc_jwt".into(), jwt.into_owned().into()); @@ -199,6 +213,7 @@ impl figment::Provider for EthereumOpts { #[cfg(test)] mod tests { use super::*; + use clap::CommandFactory; #[test] fn parse_etherscan_opts() { @@ -223,4 +238,41 @@ mod tests { let id: u64 = chain_id.deserialize().expect("chain_id should deserialize as u64"); assert_eq!(id, 9745); } + + #[test] + fn rpc_url_arg_does_not_read_eth_rpc_url_env() { + let command = RpcOpts::command(); + let rpc_url = + command.get_arguments().find(|arg| arg.get_id() == "rpc_url").expect("rpc_url arg"); + + assert!(rpc_url.get_env().is_none()); + } + + #[test] + fn rpc_url_resolves_eth_rpc_url_env() { + let args = RpcOpts::default(); + let url = args + .url_with_env(None, Some("http://127.0.0.1:8545".to_string())) + .expect("url") + .expect("url"); + + assert_eq!(url.as_ref(), "http://127.0.0.1:8545"); + } + + #[test] + fn explicit_rpc_url_takes_precedence_over_eth_rpc_url_env() { + let args = RpcOpts { + common: RpcCommonOpts { + rpc_url: Some("http://127.0.0.1:8546".to_string()), + ..Default::default() + }, + ..Default::default() + }; + let url = args + .url_with_env(None, Some("http://127.0.0.1:8545".to_string())) + .expect("url") + .expect("url"); + + assert_eq!(url.as_ref(), "http://127.0.0.1:8546"); + } } diff --git a/crates/cli/src/opts/rpc_common.rs b/crates/cli/src/opts/rpc_common.rs index 05b98582fa88f..6a5fe5ed4e9e4 100644 --- a/crates/cli/src/opts/rpc_common.rs +++ b/crates/cli/src/opts/rpc_common.rs @@ -17,10 +17,15 @@ use std::borrow::Cow; /// This struct holds fields that both [`super::RpcOpts`] (cast) and /// [`super::EvmArgs`] (forge/script) need, eliminating duplication and /// making the two structs composable. +/// +/// Note: `ETH_RPC_URL` is intentionally **not** bound here as a clap env +/// fallback; otherwise it would be inherited by `EvmArgs` and silently +/// fork all `forge test` runs. Cast resolves `ETH_RPC_URL` explicitly +/// at the call site (see [`super::RpcOpts::url`]). #[derive(Clone, Debug, Default, Serialize, Parser)] pub struct RpcCommonOpts { /// The RPC endpoint. - #[arg(short, long, visible_alias = "fork-url", env = "ETH_RPC_URL")] + #[arg(short, long, visible_alias = "fork-url", value_name = "URL")] #[serde(rename = "eth_rpc_url", skip_serializing_if = "Option::is_none")] pub rpc_url: Option, diff --git a/crates/cli/src/opts/tempo.rs b/crates/cli/src/opts/tempo.rs index 8c2a12e661e18..88119f163b325 100644 --- a/crates/cli/src/opts/tempo.rs +++ b/crates/cli/src/opts/tempo.rs @@ -1,16 +1,26 @@ use alloy_network::{Network, TransactionBuilder}; use alloy_primitives::{Address, ruint::aliases::U256}; -use alloy_signer::Signature; +use alloy_signer::{Signature, Signer}; use clap::Parser; -use foundry_common::FoundryTransactionBuilder; -use std::{num::NonZeroU64, str::FromStr}; +use eyre::Result; +use foundry_common::{ + FoundryTransactionBuilder, + tempo::{TempoSponsor, resolve_tempo_sponsor_signer}, +}; +use std::{ + num::NonZeroU64, + path::PathBuf, + str::FromStr, + sync::Arc, + time::{SystemTime, UNIX_EPOCH}, +}; use crate::utils::parse_fee_token_address; -/// CLI options for Tempo transactions. +/// CLI options common to Tempo transactions across commands. #[derive(Clone, Debug, Default, Parser)] #[command(next_help_heading = "Tempo")] -pub struct TempoOpts { +pub struct TempoCommonOpts { /// Fee token address for Tempo transactions. /// /// When set, builds a Tempo (type 0x76) transaction that pays gas fees @@ -21,6 +31,40 @@ pub struct TempoOpts { #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] pub fee_token: Option
, + /// Opt into TIP-1009 expiring-nonce mode with a validity window. + /// + /// Convenience flag that combines `--tempo.expiring-nonce` with a relative + /// `--tempo.valid-before`. Sets nonce_key = U256::MAX, nonce = 0, and valid_before = now + + /// seconds. + /// + /// Maximum value is 30 seconds. The transaction must be mined before the deadline or it + /// becomes permanently invalid, giving safe retry semantics: retries produce a fresh tx hash + /// and the old tx can never land late. + #[arg(long = "tempo.expires", value_name = "SECONDS", value_parser = parse_expires_seconds)] + pub expires: Option, +} + +impl TempoCommonOpts { + /// Returns `true` if any Tempo-specific option is set. + pub const fn is_tempo(&self) -> bool { + self.fee_token.is_some() || self.expires.is_some() + } + + /// Returns the absolute `valid_before` unix timestamp derived from `--tempo.expires`, if set. + pub fn expires_at(&self) -> Option { + let secs = self.expires?; + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); + Some(now.as_secs() + secs) + } +} + +/// CLI options for Tempo transactions. +#[derive(Clone, Debug, Default, Parser)] +#[command(next_help_heading = "Tempo")] +pub struct TempoOpts { + #[command(flatten)] + pub common: TempoCommonOpts, + /// Nonce key for Tempo parallelizable nonces. /// /// When set, builds a Tempo (type 0x76) transaction with the specified nonce key, @@ -28,21 +72,69 @@ pub struct TempoOpts { /// to be executed in parallel. If not set, the protocol nonce key (0) will be used. /// /// For more information see . - #[arg(long = "tempo.nonce-key", value_name = "NONCE_KEY")] + #[arg(long = "tempo.nonce-key", value_name = "NONCE_KEY", conflicts_with = "lane")] pub nonce_key: Option, + /// Named nonce lane for Tempo parallelizable nonces. + /// + /// Resolves a friendly lane name (e.g. `deploy`, `payments`) to a `nonce_key` via a + /// shared lanes file (default: `tempo.lanes.toml` at the project root). The lanes file + /// is a TOML map of `name = ` entries, e.g.: + /// + /// ```toml + /// deploy = 1 + /// ops = 2 + /// payments = 3 + /// ``` + /// + /// Mutually exclusive with `--tempo.nonce-key`. + #[arg(long = "tempo.lane", value_name = "NAME")] + pub lane: Option, + + /// Path to the Tempo lanes file used by `--tempo.lane`. + /// + /// Defaults to `tempo.lanes.toml` at the project root. + #[arg(long = "tempo.lanes-file", value_name = "PATH")] + pub lanes_file: Option, + + /// Sponsor (fee payer) address for Tempo sponsored transactions. + #[arg(long = "tempo.sponsor", value_name = "ADDRESS")] + pub sponsor: Option
, + + /// Sign Tempo sponsor digests in-band with the given signer URI. + /// + /// Supported forms include `env://VAR`, `keystore://PATH`, `account://NAME`, + /// `ledger://`, `trezor://`, `aws://`, `gcp://`, `turnkey://`, and + /// `private-key://KEY`. + #[arg( + long = "tempo.sponsor-signer", + value_name = "SIGNER", + requires = "sponsor", + conflicts_with = "sponsor_sig" + )] + pub sponsor_signer: Option, + /// Sponsor (fee payer) signature for Tempo sponsored transactions. /// /// The sponsor signs the `fee_payer_signature_hash` to commit to paying gas fees /// on behalf of the sender. Provide as a hex-encoded signature. - #[arg(long = "tempo.sponsor-signature", value_parser = parse_signature)] - pub sponsor_signature: Option, + #[arg( + long = "tempo.sponsor-sig", + alias = "tempo.sponsor-signature", + value_parser = parse_signature, + requires = "sponsor", + conflicts_with = "sponsor_signer" + )] + pub sponsor_sig: Option, /// Print the sponsor signature hash and exit. /// /// Computes the `fee_payer_signature_hash` for the transaction so that a sponsor /// knows what hash to sign. The transaction is not sent. - #[arg(long = "tempo.print-sponsor-hash")] + #[arg( + long = "tempo.print-sponsor-hash", + conflicts_with_all = &["sponsor", "sponsor_signer", "sponsor_sig"] + )] pub print_sponsor_hash: bool, /// Access key ID for Tempo Keychain signature transactions. @@ -56,14 +148,14 @@ pub struct TempoOpts { /// /// Sets nonce to 0 and nonce_key to U256::MAX, enabling time-bounded transaction /// validity via `--tempo.valid-before` and `--tempo.valid-after`. - #[arg(long = "tempo.expiring-nonce", requires = "valid_before")] + #[arg(long = "tempo.expiring-nonce", requires = "valid_before", conflicts_with = "expires")] pub expiring_nonce: bool, /// Upper bound timestamp for Tempo expiring nonce transactions. /// /// The transaction is only valid before this unix timestamp. /// Requires `--tempo.expiring-nonce`. - #[arg(long = "tempo.valid-before")] + #[arg(long = "tempo.valid-before", conflicts_with = "expires")] pub valid_before: Option, /// Lower bound timestamp for Tempo expiring nonce transactions. @@ -77,9 +169,12 @@ pub struct TempoOpts { impl TempoOpts { /// Returns `true` if any Tempo-specific option is set. pub const fn is_tempo(&self) -> bool { - self.fee_token.is_some() + self.common.is_tempo() || self.nonce_key.is_some() - || self.sponsor_signature.is_some() + || self.lane.is_some() + || self.sponsor.is_some() + || self.sponsor_signer.is_some() + || self.sponsor_sig.is_some() || self.print_sponsor_hash || self.key_id.is_some() || self.expiring_nonce @@ -87,6 +182,58 @@ impl TempoOpts { || self.valid_after.is_some() } + /// Returns the absolute `valid_before` unix timestamp derived from `--tempo.expires`, if set. + pub fn expires_at(&self) -> Option { + self.common.expires_at() + } + + /// Resolves `--tempo.expires` into concrete expiring-nonce fields. + /// + /// This computes the relative deadline once so later calls to [`Self::apply`] reuse the same + /// `valid_before` timestamp instead of deriving a fresh one. + pub fn resolve_expires(&mut self) -> Option { + let ts = self.expires_at()?; + self.expiring_nonce = true; + self.valid_before = Some(ts); + self.common.expires = None; + Some(ts) + } + + /// Returns `true` if a sponsor signature should be attached before submission. + pub const fn has_sponsor_submission(&self) -> bool { + self.sponsor.is_some() || self.sponsor_signer.is_some() || self.sponsor_sig.is_some() + } + + /// Resolves sponsor CLI options into a reusable sponsor config for transaction submission. + pub async fn sponsor_config(&self) -> Result> { + let Some(sponsor) = self.sponsor else { + return Ok(None); + }; + + let signer = if let Some(spec) = &self.sponsor_signer { + Some(Arc::new(Box::pin(resolve_tempo_sponsor_signer(spec)).await?)) + } else { + None + }; + + if let Some(signer) = &signer { + let signer_address = signer.address(); + if signer_address != sponsor { + eyre::bail!( + "Tempo sponsor signer address {signer_address} does not match --tempo.sponsor {sponsor}" + ); + } + } + + if signer.is_none() && self.sponsor_sig.is_none() { + eyre::bail!( + "--tempo.sponsor requires either --tempo.sponsor-signer or --tempo.sponsor-sig" + ); + } + + Ok(Some(TempoSponsor::new(sponsor, signer, self.sponsor_sig))) + } + /// Applies Tempo-specific options to a transaction request. /// /// All setters are no-ops for non-Tempo networks, so this is safe to call unconditionally. @@ -94,8 +241,9 @@ impl TempoOpts { where N::TransactionRequest: FoundryTransactionBuilder, { - // Handle expiring nonce mode: sets nonce=0 and nonce_key=U256::MAX - if self.expiring_nonce { + // Handle expiring nonce mode: sets nonce=0 and nonce_key=U256::MAX. + // --tempo.expires is a convenience alias that also sets valid_before = now + duration. + if self.expiring_nonce || self.common.expires.is_some() { tx.set_nonce(0); tx.set_nonce_key(U256::MAX); } else { @@ -107,11 +255,14 @@ impl TempoOpts { } } - if let Some(fee_token) = self.fee_token { + if let Some(fee_token) = self.common.fee_token { tx.set_fee_token(fee_token); } - if let Some(valid_before) = self.valid_before + // --tempo.expires sets valid_before relative to now; --tempo.valid-before takes a raw + // unix timestamp. The two flags are mutually exclusive (enforced by clap). + let effective_valid_before = self.expires_at().or(self.valid_before); + if let Some(valid_before) = effective_valid_before && let Some(v) = NonZeroU64::new(valid_before) { tx.set_valid_before(v); @@ -131,8 +282,7 @@ impl TempoOpts { // gas estimation so that `--tempo.print-sponsor-hash` and // `--tempo.sponsor-signature` produce identical gas estimates. Callers // should call `set_fee_payer_signature` on the built tx request. - if (self.sponsor_signature.is_some() || self.print_sponsor_hash) && tx.nonce_key().is_none() - { + if (self.has_sponsor_submission() || self.print_sponsor_hash) && tx.nonce_key().is_none() { tx.set_nonce_key(U256::ZERO); } } @@ -142,11 +292,83 @@ fn parse_signature(s: &str) -> Result { Signature::from_str(s).map_err(|e| format!("invalid signature: {e}")) } +/// Parses a seconds value for `--tempo.expires`, capped at the protocol maximum of 30 seconds. +fn parse_expires_seconds(s: &str) -> Result { + let secs: u64 = s + .parse() + .map_err(|_| format!("invalid value '{s}': expected an integer number of seconds"))?; + if secs > 30 { + return Err(format!("expires must be at most 30 seconds (got {secs})")); + } + Ok(secs) +} + #[cfg(test)] mod tests { use super::*; use alloy_primitives::address; + #[test] + fn parses_lane_arg() { + let opts = TempoOpts::try_parse_from(["", "--tempo.lane", "deploy"]).unwrap(); + assert_eq!(opts.lane.as_deref(), Some("deploy")); + assert!(opts.nonce_key.is_none()); + } + + #[test] + fn lane_conflicts_with_nonce_key() { + let err = + TempoOpts::try_parse_from(["", "--tempo.lane", "deploy", "--tempo.nonce-key", "1"]) + .unwrap_err(); + assert!( + err.to_string().contains("cannot be used with"), + "expected clap conflict error, got: {err}", + ); + } + + #[test] + fn parse_expires_flag() { + let opts = TempoOpts::try_parse_from(["", "--tempo.expires", "30"]).unwrap(); + assert_eq!(opts.common.expires, Some(30)); + + let opts = TempoOpts::try_parse_from(["", "--tempo.expires", "10"]).unwrap(); + assert_eq!(opts.common.expires, Some(10)); + + // exceeds 30s maximum + assert!(TempoOpts::try_parse_from(["", "--tempo.expires", "31"]).is_err()); + + // conflicts with --tempo.expiring-nonce + assert!( + TempoOpts::try_parse_from([ + "", + "--tempo.expires", + "30", + "--tempo.expiring-nonce", + "--tempo.valid-before", + "999" + ]) + .is_err() + ); + } + + #[test] + fn resolve_expires_materializes_valid_before() { + let before = + SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards").as_secs(); + let mut opts = TempoOpts::try_parse_from(["", "--tempo.expires", "10"]).unwrap(); + + let resolved = opts.resolve_expires().unwrap(); + let after = + SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards").as_secs(); + + assert!(resolved >= before + 10); + assert!(resolved <= after + 10); + assert!(opts.expiring_nonce); + assert_eq!(opts.valid_before, Some(resolved)); + assert_eq!(opts.common.expires, None); + assert_eq!(opts.expires_at(), None); + } + #[test] fn parse_fee_token_id() { let opts = TempoOpts::try_parse_from([ @@ -155,13 +377,69 @@ mod tests { "0x20C0000000000000000000000000000000000002", ]) .unwrap(); - assert_eq!(opts.fee_token, Some(address!("0x20C0000000000000000000000000000000000002")),); + assert_eq!( + opts.common.fee_token, + Some(address!("0x20C0000000000000000000000000000000000002")), + ); // AlphaUSD token ID is 1u64 let opts_with_id = TempoOpts::try_parse_from(["", "--tempo.fee-token", "1"]).unwrap(); assert_eq!( - opts_with_id.fee_token, + opts_with_id.common.fee_token, Some(address!("0x20C0000000000000000000000000000000000001")), ); } + + #[test] + fn parse_sponsor_signer() { + let opts = TempoOpts::try_parse_from([ + "", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + "--tempo.sponsor-signer", + "env://TEMPO_SPONSOR_PK", + ]) + .unwrap(); + + assert_eq!(opts.sponsor, Some(address!("0x1111111111111111111111111111111111111111"))); + assert_eq!(opts.sponsor_signer.as_deref(), Some("env://TEMPO_SPONSOR_PK")); + assert!(opts.sponsor_sig.is_none()); + assert!(opts.is_tempo()); + assert!(opts.has_sponsor_submission()); + } + + #[test] + fn sponsor_signer_requires_sponsor() { + assert!( + TempoOpts::try_parse_from(["", "--tempo.sponsor-signer", "env://SPONSOR"]).is_err() + ); + } + + #[test] + fn parse_sponsor_signature_alias() { + let opts = TempoOpts::try_parse_from([ + "", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + "--tempo.sponsor-signature", + "0x0eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca182b", + ]) + .unwrap(); + + assert_eq!(opts.sponsor, Some(address!("0x1111111111111111111111111111111111111111"))); + assert!(opts.sponsor_sig.is_some()); + } + + #[test] + fn print_sponsor_hash_conflicts_with_sponsor_submission() { + assert!( + TempoOpts::try_parse_from([ + "", + "--tempo.print-sponsor-hash", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + ]) + .is_err() + ); + } } diff --git a/crates/cli/src/utils/tempo.rs b/crates/cli/src/utils/tempo.rs index 647f52d316a6c..4b5715b9ebe08 100644 --- a/crates/cli/src/utils/tempo.rs +++ b/crates/cli/src/utils/tempo.rs @@ -1,8 +1,44 @@ -use std::str::FromStr; +//! Tempo utilities: fee token parsing and named nonce lanes (2D nonces). +//! +//! A "lane" is a friendly alias for a Tempo `nonce_key` (a [`U256`]). Lanes are defined in a +//! shared TOML file (default `tempo.lanes.toml` at the project root) so a team can reserve +//! independent sequential nonce streams for parallel scripts without coordinating on raw +//! `U256` selectors. +//! +//! Example `tempo.lanes.toml`: +//! +//! ```toml +//! deploy = 1 +//! ops = 2 +//! payments = 3 +//! ``` +//! +//! ```bash +//! cast erc20 transfer ... --tempo.lane payments +//! ``` -use alloy_primitives::Address; +use crate::opts::TempoOpts; +use alloy_primitives::{Address, U256}; +use eyre::{Result, eyre}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, + str::FromStr, +}; use tempo_primitives::TempoAddressExt; +/// Default name of the lanes file at the project root. +pub const DEFAULT_LANES_FILE: &str = "tempo.lanes.toml"; + +/// Result of resolving a `--tempo.lane ` argument against a lanes file. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolvedLane { + /// The lane name as provided on the CLI. + pub name: String, + /// The `nonce_key` the lane resolved to. + pub nonce_key: U256, +} + /// Parses a fee token address. pub fn parse_fee_token_address(address_or_id: &str) -> eyre::Result
{ Address::from_str(address_or_id).or_else(|_| Ok(token_id_to_address(address_or_id.parse()?))) @@ -14,3 +50,156 @@ fn token_id_to_address(token_id: u64) -> Address { address_bytes[12..20].copy_from_slice(&token_id.to_be_bytes()); Address::from(address_bytes) } + +/// Loads a TOML lanes file from `path`. +/// +/// Each top-level key is a lane name, and the value is the `nonce_key` (an integer or a +/// decimal/hex string parsed as [`U256`]). +pub fn load_lanes(path: &Path) -> Result> { + let contents = std::fs::read_to_string(path) + .map_err(|e| eyre!("failed to read tempo lanes file {}: {}", path.display(), e))?; + parse_lanes(&contents) + .map_err(|e| eyre!("failed to parse tempo lanes file {}: {}", path.display(), e)) +} + +fn parse_lanes(contents: &str) -> Result> { + let raw: BTreeMap = toml::from_str(contents)?; + let mut out = BTreeMap::new(); + for (name, value) in raw { + let nonce_key = match value { + toml::Value::Integer(n) => { + if n < 0 { + return Err(eyre!("invalid nonce_key for lane '{name}': must be non-negative")); + } + U256::from(n as u64) + } + toml::Value::String(s) => U256::from_str(s.trim()) + .map_err(|e| eyre!("invalid nonce_key for lane '{name}': {e}"))?, + other => { + return Err(eyre!( + "invalid nonce_key for lane '{name}': expected integer or string, got {}", + other.type_str(), + )); + } + }; + out.insert(name, nonce_key); + } + Ok(out) +} + +/// Resolves `opts.lane` against a lanes file and writes the resulting `nonce_key` to +/// `opts.nonce_key`. Returns the resolved lane (or `None` if no `--tempo.lane` was set). +/// +/// `root` is the project root used to locate the default lanes file +/// (`/tempo.lanes.toml`) when `--tempo.lanes-file` was not provided. +pub fn resolve_lane(opts: &mut TempoOpts, root: &Path) -> Result> { + let Some(lane_name) = opts.lane.clone() else { return Ok(None) }; + + let path: PathBuf = opts.lanes_file.clone().unwrap_or_else(|| root.join(DEFAULT_LANES_FILE)); + + if !path.exists() { + return Err(eyre!( + "tempo lanes file not found at {}\n\ + create it with `name = ` entries, e.g.:\n \ + deploy = 1\n \ + ops = 2\n \ + payments = 3", + path.display(), + )); + } + + let lanes = load_lanes(&path)?; + + let nonce_key = lanes.get(&lane_name).copied().ok_or_else(|| { + let mut known: Vec<&str> = lanes.keys().map(String::as_str).collect(); + known.sort_unstable(); + eyre!( + "lane '{lane_name}' not found in {} (known lanes: {})", + path.display(), + if known.is_empty() { "".to_string() } else { known.join(", ") }, + ) + })?; + + opts.nonce_key = Some(nonce_key); + Ok(Some(ResolvedLane { name: lane_name, nonce_key })) +} + +/// Prints `lane: (nonce_key=, nonce=)` to stderr (so it doesn't pollute +/// stdout for commands like `cast mktx` whose stdout is meant to be piped), giving +/// visibility into which 2D nonce lane was used. +pub fn maybe_print_resolved_lane(resolved: Option<&ResolvedLane>, nonce: u64) -> Result<()> { + if let Some(lane) = resolved { + sh_eprintln!("lane: {} (nonce_key={}, nonce={})", lane.name, lane.nonce_key, nonce)?; + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_int_and_string_lane_values() { + let toml = r#" +deploy = 1 +ops = 2 +payments = "3" +big = "115792089237316195423570985008687907853269984665640564039457584007913129639935" +"#; + let lanes = parse_lanes(toml).unwrap(); + assert_eq!(lanes.get("deploy"), Some(&U256::from(1u64))); + assert_eq!(lanes.get("ops"), Some(&U256::from(2u64))); + assert_eq!(lanes.get("payments"), Some(&U256::from(3u64))); + assert_eq!(lanes.get("big"), Some(&U256::MAX)); + } + + #[test] + fn parse_lanes_rejects_invalid_string() { + let toml = "broken = \"not-a-number\""; + let err = parse_lanes(toml).unwrap_err(); + assert!(err.to_string().contains("invalid nonce_key for lane 'broken'")); + } + + #[test] + fn resolve_lane_sets_nonce_key_and_returns_resolved() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join(DEFAULT_LANES_FILE); + std::fs::write(&path, "deploy = 7\npayments = 42\n").unwrap(); + + let mut opts = TempoOpts { lane: Some("payments".to_string()), ..Default::default() }; + let resolved = resolve_lane(&mut opts, dir.path()).unwrap().unwrap(); + assert_eq!(resolved.name, "payments"); + assert_eq!(resolved.nonce_key, U256::from(42u64)); + assert_eq!(opts.nonce_key, Some(U256::from(42u64))); + } + + #[test] + fn resolve_lane_returns_none_when_no_lane() { + let dir = tempfile::tempdir().unwrap(); + let mut opts = TempoOpts::default(); + let resolved = resolve_lane(&mut opts, dir.path()).unwrap(); + assert!(resolved.is_none()); + assert!(opts.nonce_key.is_none()); + } + + #[test] + fn resolve_lane_errors_when_file_missing() { + let dir = tempfile::tempdir().unwrap(); + let mut opts = TempoOpts { lane: Some("deploy".to_string()), ..Default::default() }; + let err = resolve_lane(&mut opts, dir.path()).unwrap_err(); + assert!(err.to_string().contains("tempo lanes file not found")); + } + + #[test] + fn resolve_lane_errors_when_lane_unknown() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join(DEFAULT_LANES_FILE); + std::fs::write(&path, "deploy = 1\nops = 2\n").unwrap(); + + let mut opts = TempoOpts { lane: Some("payments".to_string()), ..Default::default() }; + let err = resolve_lane(&mut opts, dir.path()).unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("lane 'payments' not found")); + assert!(msg.contains("deploy, ops")); + } +} diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 337c51f52d2c2..6921faabcb102 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -35,7 +35,7 @@ alloy-signer.workspace = true alloy-pubsub.workspace = true alloy-rpc-client.workspace = true alloy-rpc-types = { workspace = true, features = ["eth", "engine"] } -alloy-rpc-types-engine = { workspace = true, features = ["jwt"] } +alloy-rpc-types-engine = { workspace = true, features = ["jwt-aws-lc-rs"] } alloy-sol-types.workspace = true alloy-transport-ipc.workspace = true alloy-transport-ws.workspace = true @@ -44,8 +44,8 @@ alloy-transport.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } alloy-network.workspace = true -op-alloy-network.workspace = true -op-alloy-rpc-types.workspace = true +op-alloy-network = { workspace = true, optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } revm.workspace = true @@ -87,6 +87,10 @@ mpp.workspace = true foundry-wallets = { workspace = true, features = ["browser", "tempo"] } tokio-tungstenite.workspace = true futures.workspace = true +alloy-signer-local.workspace = true +base64.workspace = true +sha2 = "0.10" +tempfile.workspace = true [build-dependencies] chrono.workspace = true @@ -96,4 +100,12 @@ vergen = { workspace = true, features = ["build", "emit_and_set"] } foundry-evm-hardforks.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } axum = { workspace = true } -tempfile.workspace = true +k256 = { workspace = true } + +[features] +default = ["optimism"] +optimism = [ + "dep:op-alloy-network", + "dep:op-alloy-rpc-types", + "foundry-common-fmt/optimism", +] diff --git a/crates/common/build.rs b/crates/common/build.rs index d89e23be850f4..9afa01b5757ef 100644 --- a/crates/common/build.rs +++ b/crates/common/build.rs @@ -15,16 +15,13 @@ fn main() -> Result<(), Box> { let sha_short = &sha[..10]; let tag_name = try_env_var("TAG_NAME").unwrap_or_else(|| String::from("dev")); - let is_nightly = tag_name.contains("nightly"); - let version_suffix = if is_nightly { "nightly" } else { &tag_name }; + let version = release_version(&env_var("CARGO_PKG_VERSION"), &tag_name); + let is_nightly = tag_name.starts_with("nightly"); if is_nightly { println!("cargo:rustc-env=FOUNDRY_IS_NIGHTLY_VERSION=true"); } - let pkg_version = env_var("CARGO_PKG_VERSION"); - let version = format!("{pkg_version}-{version_suffix}"); - // `PROFILE` captures only release or debug. Get the actual name from the out directory. let out_dir = PathBuf::from(env_var("OUT_DIR")); let profile = out_dir.components().rev().nth(3).unwrap().as_os_str().to_str().unwrap(); @@ -87,6 +84,19 @@ fn env_var(name: &str) -> String { try_env_var(name).unwrap() } +fn release_version(pkg_version: &str, tag_name: &str) -> String { + if let Some(version) = tag_name.strip_prefix('v') { + return version.to_owned(); + } + + // Normalize `nightly-` to `nightly` so tarball and Docker nightly + // artifacts produce the same version string. The commit identifier is + // already included in the SemVer build metadata (after `+`). + let normalized = if tag_name.starts_with("nightly-") { "nightly" } else { tag_name }; + + format!("{pkg_version}-{normalized}") +} + fn try_env_var(name: &str) -> Option { println!("cargo:rerun-if-env-changed={name}"); std::env::var(name).ok() diff --git a/crates/common/fmt/Cargo.toml b/crates/common/fmt/Cargo.toml index 2c8e16bccdcc6..179c71048da5b 100644 --- a/crates/common/fmt/Cargo.toml +++ b/crates/common/fmt/Cargo.toml @@ -20,10 +20,10 @@ eyre.workspace = true # ui alloy-consensus.workspace = true -op-alloy-consensus.workspace = true +op-alloy-consensus = { workspace = true, optional = true } alloy-network.workspace = true alloy-rpc-types = { workspace = true, features = ["eth"] } -op-alloy-rpc-types.workspace = true +op-alloy-rpc-types = { workspace = true, optional = true } alloy-serde.workspace = true serde.workspace = true serde_json.workspace = true @@ -38,3 +38,7 @@ tempo-alloy.workspace = true [dev-dependencies] foundry-macros.workspace = true similar-asserts.workspace = true + +[features] +default = ["optimism"] +optimism = ["dep:op-alloy-consensus", "dep:op-alloy-rpc-types"] diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index e883810dcda34..2087a85236154 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -18,6 +18,7 @@ use alloy_rpc_types::{ AccessListItem, Block, BlockTransactions, Header, Log, Transaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{OpTxEnvelope, TxDeposit, TxPostExec}; use revm::context_interface::transaction::SignedAuthorization; use serde::Deserialize; @@ -448,6 +449,7 @@ input {}", } } +#[cfg(feature = "optimism")] impl UIfmt for TxDeposit { fn pretty(&self) -> String { format!( @@ -472,6 +474,7 @@ input {}", } } +#[cfg(feature = "optimism")] impl UIfmt for TxPostExec { fn pretty(&self) -> String { format!( @@ -606,6 +609,7 @@ type {:#x} } } +#[cfg(feature = "optimism")] impl UIfmt for OpTxEnvelope { fn pretty(&self) -> String { match self { @@ -651,6 +655,7 @@ effectiveGasPrice {} } } +#[cfg(feature = "optimism")] impl UIfmt for op_alloy_rpc_types::Transaction { fn pretty(&self) -> String { format!( @@ -786,6 +791,7 @@ impl UIfmtSignatureExt for AnyTxEnvelope { } } +#[cfg(feature = "optimism")] impl UIfmtSignatureExt for OpTxEnvelope { fn signature_pretty(&self) -> Option<(String, String, String)> { self.signature().map(|sig| { @@ -1135,6 +1141,7 @@ mod tests { assert_eq!(b.pretty(), b32.pretty()); } + #[cfg(feature = "optimism")] #[test] fn can_pretty_print_optimism_tx() { let s = r#" @@ -1186,6 +1193,7 @@ yParity 1 ); } + #[cfg(feature = "optimism")] #[test] fn can_pretty_print_optimism_tx_through_any() { let s = r#" diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 895b16b3b4532..95c7d4083f34e 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -383,16 +383,14 @@ impl ContractsByArtifact { &self, id: &str, ) -> Result>> { - let contracts = self - .iter() - .filter(|(artifact, _)| artifact.name == id || artifact.identifier() == id) - .collect::>(); - - if contracts.len() > 1 { + let mut iter = + self.iter().filter(|(artifact, _)| artifact.name == id || artifact.identifier() == id); + let first = iter.next(); + if first.is_some() && iter.next().is_some() { eyre::bail!("{id} has more than one implementation."); } - Ok(contracts.first().copied()) + Ok(first) } /// Finds abi by name or source path @@ -411,7 +409,7 @@ impl ContractsByArtifact { let mut funcs = BTreeMap::new(); let mut events = BTreeMap::new(); let mut errors_abi = JsonAbi::new(); - for (_name, contract) in self.iter() { + for contract in self.values() { for func in contract.abi.functions() { funcs.insert(func.selector(), func.clone()); } diff --git a/crates/common/src/provider/mpp/keys.rs b/crates/common/src/provider/mpp/keys.rs index 65640c48ab841..fa0fc80ed3d03 100644 --- a/crates/common/src/provider/mpp/keys.rs +++ b/crates/common/src/provider/mpp/keys.rs @@ -7,6 +7,7 @@ use crate::tempo::{TEMPO_PRIVATE_KEY_ENV, WalletType, read_tempo_keys_file}; use alloy_primitives::Address; +use std::env; use tracing::debug; /// Options for MPP key discovery filtering. @@ -55,7 +56,7 @@ pub fn discover_mpp_key() -> Option { /// target chain and the required currency. pub fn discover_mpp_config(opts: DiscoverOptions) -> Option { // 1. Check TEMPO_PRIVATE_KEY env var (no keychain metadata available) - if let Ok(key) = std::env::var(TEMPO_PRIVATE_KEY_ENV) { + if let Ok(key) = env::var(TEMPO_PRIVATE_KEY_ENV) { let key = key.trim().to_string(); if !key.is_empty() { debug!("using MPP key from {TEMPO_PRIVATE_KEY_ENV} env var"); @@ -73,11 +74,17 @@ pub fn discover_mpp_config(opts: DiscoverOptions) -> Option { // 2. Read $TEMPO_HOME/wallet/keys.toml (default: ~/.tempo/wallet/keys.toml) let keys_file = read_tempo_keys_file()?; + // `expiry == 0` means "no expiry" on the wire. + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0); + // Pick primary key using the same deterministic order as // `Keystore::primary_key()` in tempo-common: // passkey > first entry with inline key > first entry // Only entries with a usable inline key can provide a signing key. - // Filter by chain_id and currency when provided. + // Filter by chain_id, currency, and freshness when provided. let candidates: Vec<_> = keys_file .keys .iter() @@ -86,6 +93,7 @@ pub fn discover_mpp_config(opts: DiscoverOptions) -> Option { opts.currency .is_none_or(|cur| k.limits.is_empty() || k.limits.iter().any(|l| l.currency == cur)) }) + .filter(|k| k.expiry.is_none_or(|e| e == 0 || e > now)) .collect(); let primary = candidates @@ -135,6 +143,7 @@ mod tests { #[test] fn discover_from_tempo_home_keys_toml() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; let toml_content = format!( r#" @@ -160,6 +169,7 @@ chain_id = 4217 #[test] fn discover_env_var_takes_priority_over_keys_toml() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let file_key = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; let env_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; let toml_content = format!( @@ -187,6 +197,7 @@ key = "{file_key}" #[test] fn discover_returns_none_when_no_keys() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let (dir, _) = setup_keys_toml(""); unsafe { @@ -202,6 +213,7 @@ key = "{file_key}" #[test] fn discover_skips_entries_without_inline_key() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; let toml_content = format!( r#" @@ -344,6 +356,7 @@ key = "0xthe_key" #[test] fn discover_filters_by_chain_id() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); let mainnet_key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let testnet_key = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; let toml_content = format!( @@ -416,6 +429,62 @@ chain_id = 4217 unsafe { std::env::remove_var("TEMPO_HOME") }; } + #[test] + fn discover_filters_expired_entries() { + // Expired entries must not be selected, so the next 402 re-triggers + // the device-code flow instead of returning a stale key. + let _g = crate::tempo::test_env_mutex().blocking_lock(); + let expired_key = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + let fresh_key = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + let toml_content = format!( + r#" +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000001" +key = "{expired_key}" +chain_id = 4217 +expiry = 1 + +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000002" +key = "{fresh_key}" +chain_id = 4217 +expiry = 0 +"# + ); + let (dir, _) = setup_keys_toml(&toml_content); + unsafe { + std::env::set_var("TEMPO_HOME", dir.path()); + std::env::remove_var("TEMPO_PRIVATE_KEY"); + } + + // Even though the expired entry comes first, discovery skips it. + let config = + discover_mpp_config(DiscoverOptions { chain_id: Some(4217), ..Default::default() }); + assert_eq!(config.as_ref().unwrap().key, fresh_key); + + // With only the expired entry present, discovery returns None so the + // 402 path can run `ensure_access_key` again. + let only_expired = format!( + r#" +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000001" +key = "{expired_key}" +chain_id = 4217 +expiry = 1 +"# + ); + let (dir2, _) = setup_keys_toml(&only_expired); + unsafe { std::env::set_var("TEMPO_HOME", dir2.path()) }; + let config = + discover_mpp_config(DiscoverOptions { chain_id: Some(4217), ..Default::default() }); + assert!(config.is_none(), "expired-only keys.toml must not yield a usable key"); + + unsafe { std::env::remove_var("TEMPO_HOME") }; + } + #[test] fn parse_keys_toml_unknown_fields_ignored() { let toml_str = r#" diff --git a/crates/common/src/provider/mpp/session.rs b/crates/common/src/provider/mpp/session.rs index 334166b844613..c3e87f8cf42b5 100644 --- a/crates/common/src/provider/mpp/session.rs +++ b/crates/common/src/provider/mpp/session.rs @@ -175,6 +175,16 @@ impl SessionProvider { self } + /// Address that funds payments for this provider. + pub fn funding_wallet_address(&self) -> Address { + self.signing_mode.from_address(self.signer.address()) + } + + /// Chain ID from the selected wallet key, when known. + pub const fn key_chain_id(&self) -> Option { + self.key_chain_id + } + /// Set the chain ID and currencies from the key entry used to initialize /// this provider. Used to reject challenges for incompatible chains/currencies. /// When `chain_id` is `None` (e.g. env var key), chain filtering is skipped. diff --git a/crates/common/src/provider/mpp/transport.rs b/crates/common/src/provider/mpp/transport.rs index 67354dc2bd60d..9e3b16dedd59e 100644 --- a/crates/common/src/provider/mpp/transport.rs +++ b/crates/common/src/provider/mpp/transport.rs @@ -4,6 +4,7 @@ //! handling via the MPP protocol. When the RPC endpoint returns a 402 response, //! this transport automatically pays the challenge and retries the request. +use alloy_chains::Chain; use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_transport::{TransportError, TransportErrorKind, TransportFut, TransportResult}; use mpp::{ @@ -16,12 +17,17 @@ use mpp::{ use reqwest::{StatusCode, header::HeaderMap}; use std::{ collections::HashMap, - fmt, - sync::{Mutex, OnceLock}, + env, fmt, io, + io::IsTerminal, + process::{Command, Stdio}, + sync::{ + Arc, LazyLock, Mutex, + atomic::{AtomicBool, Ordering}, + }, task, time::Duration, }; -use tokio::sync::OwnedMutexGuard; +use tokio::sync::{Mutex as AsyncMutex, OwnedMutexGuard}; use tower::Service; use tracing::{Instrument, debug, debug_span, trace}; use url::Url; @@ -39,7 +45,27 @@ const MPP_RETRY_TIMEOUT: Duration = Duration::from_secs(120); /// Resolve the deposit amount from `MPP_DEPOSIT` env var or the default. fn default_deposit() -> u128 { - std::env::var("MPP_DEPOSIT").ok().and_then(|s| s.parse().ok()).unwrap_or(DEFAULT_DEPOSIT) + env::var("MPP_DEPOSIT").ok().and_then(|s| s.parse().ok()).unwrap_or(DEFAULT_DEPOSIT) +} + +#[derive(Clone, Debug, Default)] +pub(crate) struct FundingContext { + wallet_address: Option, + token: Option, + chain_id: Option, +} + +impl FundingContext { + fn token_line(&self) -> String { + self.token + .as_ref() + .map(|token| format!("Requested payment token: {token}\n\n")) + .unwrap_or_default() + } + + fn network(&self) -> Option { + self.chain_id.filter(|chain| chain.is_tempo()).map(|chain| chain.to_string()) + } } fn format_http_diagnostics(headers: &HeaderMap) -> String { @@ -60,12 +86,173 @@ fn format_http_diagnostics(headers: &HeaderMap) -> String { } } +fn tempo_wallet_fund_help(ctx: &FundingContext) -> String { + let mut command = "tempo wallet fund".to_string(); + if let Some(address) = ctx.wallet_address { + command.push_str(&format!(" --address {address}")); + } + if let Some(network) = ctx.network() { + command.push_str(&format!(" --network {network}")); + } + + let mut no_browser = command.clone(); + no_browser.push_str(" --no-browser"); + + format!( + "\n\nTempo wallet payment could not be funded for this paid RPC request.\n\n{}\ + Fund the wallet, then rerun the command:\n {command}\n\n\ + If this CLI is running on a remote or headless host, use:\n {no_browser}", + ctx.token_line() + ) +} + +/// Decide whether the interactive `tempo wallet fund` flow may be launched. +/// +/// Policy (library-safe): +/// - never run inside CI +/// - never run unless both stdin and stderr are real terminals +/// - `FOUNDRY_MPP_NO_AUTO_FUND` is honored as an opt-out; it must not bypass CI/TTY guards in +/// shared transport code that may be embedded inside long-running RPC daemons. +fn interactive_tempo_fund_allowed( + no_auto_fund: Option<&str>, + in_ci: bool, + stdin_is_terminal: bool, + stderr_is_terminal: bool, +) -> bool { + if no_auto_fund.is_some_and(|v| { + !(v == "0" || v.eq_ignore_ascii_case("false") || v.eq_ignore_ascii_case("off")) + }) { + return false; + } + + if in_ci { + return false; + } + + stdin_is_terminal && stderr_is_terminal +} + +fn can_run_interactive_tempo_fund() -> bool { + if cfg!(test) { + return false; + } + + interactive_tempo_fund_allowed( + std::env::var("FOUNDRY_MPP_NO_AUTO_FUND").ok().as_deref(), + std::env::var_os("CI").is_some(), + std::io::stdin().is_terminal(), + std::io::stderr().is_terminal(), + ) +} + +fn tempo_bin() -> String { + std::env::var("TEMPO_BIN").unwrap_or_else(|_| "tempo".to_string()) +} + +async fn run_interactive_tempo_fund(ctx: &FundingContext) -> TransportResult { + if !can_run_interactive_tempo_fund() { + return Ok(false); + } + + let tempo = tempo_bin(); + let mut args = vec!["wallet".to_string(), "fund".to_string()]; + if let Some(address) = ctx.wallet_address { + args.push("--address".to_string()); + args.push(address.to_string()); + } + if let Some(network) = ctx.network() { + args.push("--network".to_string()); + args.push(network); + } + + tracing::warn!( + token = ?ctx.token, + chain_id = ?ctx.chain_id, + "MPP payment could not be funded; opening `tempo wallet fund`" + ); + + let status = tokio::task::spawn_blocking(move || { + Command::new(tempo) + .args(args) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status() + }) + .await + .map_err(|e| { + TransportErrorKind::custom(std::io::Error::other(format!( + "failed to join tempo wallet fund process: {e}" + ))) + })? + .map_err(|e| { + TransportErrorKind::custom(std::io::Error::other(format!( + "failed to run `tempo wallet fund`: {e}{}", + tempo_wallet_fund_help(ctx) + ))) + })?; + + if status.success() { + Ok(true) + } else { + Err(TransportErrorKind::custom(std::io::Error::other(format!( + "`tempo wallet fund` exited with status {status}{}", + tempo_wallet_fund_help(ctx) + )))) + } +} + +/// Single-attempt guard around [`run_interactive_tempo_fund`]. +/// +/// Ensures that for one logical request we launch `tempo wallet fund` at most +/// once, regardless of how many recovery paths (`do_request`, `pay_and_retry`, +/// `handle_response_or_retry_after_fund`, ...) attempt it. +async fn maybe_auto_fund(used: &AtomicBool, ctx: &FundingContext) -> TransportResult { + if !can_run_interactive_tempo_fund() { + return Ok(false); + } + if used.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() { + return Ok(false); + } + run_interactive_tempo_fund(ctx).await +} + +/// Returns true iff a 402 response carries a structured insufficient-balance +/// problem (RFC 9457 `PaymentErrorDetails`). +/// +/// We deliberately do **not** match on free-text body content or on generic +/// `verification-failed` problem types, as those have many non-funding causes +/// (bad signature, replay, expired challenge, clock skew, key provisioning, +/// malformed auth, ...). +fn should_suggest_tempo_fund(status: StatusCode, body: &[u8]) -> bool { + if status != StatusCode::PAYMENT_REQUIRED { + return false; + } + let Ok(problem) = serde_json::from_slice::(body) else { + return false; + }; + problem.problem_type.ends_with("/insufficient-balance") +} + +fn format_mpp_payment_failure( + error: impl fmt::Display, + ctx: &FundingContext, + suggest_fund: bool, +) -> String { + let message = error.to_string(); + if suggest_fund { + format!("MPP payment failed: {message}{}", tempo_wallet_fund_help(ctx)) + } else { + format!("MPP payment failed: {message}") + } +} + /// Process-wide payment serialization locks, keyed by origin URL. /// /// Created eagerly so the lock exists before the first provider init, /// preventing concurrent first-402 races. -static GLOBAL_PAY_LOCKS: OnceLock>>>> = - OnceLock::new(); +static GLOBAL_PAY_LOCKS: LazyLock>>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); /// Production transport: lazily discovers MPP keys from the Tempo wallet on /// first 402 response. @@ -75,24 +262,21 @@ pub type LazyMppHttpTransport = MppHttpTransport; /// Tempo wallet configuration on first use. #[derive(Clone, Debug)] pub struct LazySessionProvider { - inner: std::sync::Arc>>, + inner: Arc>>, /// Eagerly-created, process-wide payment serialization lock for this origin. - pay_lock: std::sync::Arc>, + pay_lock: Arc>, origin: String, } impl LazySessionProvider { pub(super) fn new(origin: String) -> Self { - let pay_lock = { - let global = GLOBAL_PAY_LOCKS.get_or_init(|| Mutex::new(HashMap::new())); - global - .lock() - .unwrap() - .entry(origin.clone()) - .or_insert_with(|| std::sync::Arc::new(tokio::sync::Mutex::new(()))) - .clone() - }; - Self { inner: std::sync::Arc::new(Mutex::new(None)), pay_lock, origin } + let pay_lock = GLOBAL_PAY_LOCKS + .lock() + .unwrap() + .entry(origin.clone()) + .or_insert_with(|| Arc::new(AsyncMutex::new(()))) + .clone(); + Self { inner: Arc::new(Mutex::new(None)), pay_lock, origin } } fn set_key_provisioned(&self, provisioned: bool) { @@ -125,6 +309,14 @@ impl LazySessionProvider { } } + /// Drop the cached `SessionProvider` so the next `get_or_init` re-runs + /// discovery. Called after the device-code flow writes a fresh + /// `keys.toml` entry, so a long-lived transport doesn't keep paying with + /// the superseded key. + fn invalidate(&self) { + *self.inner.lock().unwrap() = None; + } + pub(super) fn get_or_init(&self, opts: DiscoverOptions) -> TransportResult { let mut guard = self.inner.lock().unwrap(); if let Some(ref provider) = *guard { @@ -132,18 +324,20 @@ impl LazySessionProvider { } let config = discover_mpp_config(opts).ok_or_else(|| { - TransportErrorKind::custom(std::io::Error::other( + TransportErrorKind::custom(io::Error::other( "RPC endpoint returned HTTP 402 Payment Required. \ This endpoint requires payment via the Machine Payments Protocol (MPP).\n\n\ - To configure MPP, install the Tempo wallet CLI and create a key:\n\ - \n curl -sSL https://tempo.xyz/install.sh | bash\ - \n tempo wallet login\ + Authorize an access key against your Tempo wallet:\n\ + \n cast tempo login\ + \n\nIn headless environments, pass `--no-browser` to print the authorization \ + URL instead of launching a browser:\n\ + \n cast tempo login --no-browser\ \n\nSee https://docs.tempo.xyz for more information.", )) })?; let signer: mpp::PrivateKeySigner = config.key.parse().map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!("invalid MPP key: {e}"))) + TransportErrorKind::custom(io::Error::other(format!("invalid MPP key: {e}"))) })?; let signing_mode = if let Some(wallet) = config.wallet_address { @@ -152,7 +346,7 @@ impl LazySessionProvider { .as_ref() .map(|hex_str| { crate::tempo::decode_key_authorization(hex_str).map(Box::new).map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!( + TransportErrorKind::custom(io::Error::other(format!( "invalid MPP key_authorization: {e}" ))) }) @@ -223,6 +417,17 @@ where P::Provider: Send + Sync + 'static, { async fn do_request(self, req: RequestPacket) -> TransportResult { + // Per-request guard: launch `tempo wallet fund` at most once for one + // logical request, regardless of how many recovery paths attempt it. + let auto_fund_used = AtomicBool::new(false); + self.do_request_inner(req, &auto_fund_used).await + } + + async fn do_request_inner( + self, + req: RequestPacket, + auto_fund_used: &AtomicBool, + ) -> TransportResult { let body = serde_json::to_vec(&req).map_err(TransportErrorKind::custom)?; let headers = req.headers(); @@ -246,15 +451,53 @@ where // held until the retry response is fully handled. let _pay_guard = self.provider.lock_pay().await; - let (resolved, challenge) = Self::select_challenge(&resp, &self.provider)?; + // No local key for any offered challenge → run device-code flow, + // invalidate the cached provider, and fetch a fresh 402 (the original + // may have expired during the browser/passkey flow). + let (resolved, challenge) = + if let Some(chain_id) = tempo_chain_needing_auth(&self.url, &resp) { + debug!(chain_id, "launching wallet.tempo authorization"); + let cfg = crate::tempo::EnsureAccessKeyConfig::from_env(chain_id); + crate::tempo::ensure_access_key(cfg).await.map_err(|e| { + TransportErrorKind::custom(io::Error::other(format!( + "tempo access key authorization failed: {e}" + ))) + })?; + self.provider.invalidate_cached_provider(); + self.fetch_fresh_challenge(&headers, &body).await? + } else { + Self::select_challenge(&resp, &self.provider)? + }; + let funding_ctx = self.provider.funding_context(&challenge); debug!(id = %challenge.id, method = %challenge.method, intent = %challenge.intent, "received MPP 402 challenge, paying"); - let credential = resolved.pay(&challenge).await.map_err(|e| { - TransportErrorKind::custom(std::io::Error::other(format!("MPP payment failed: {e}"))) - })?; + let credential = match resolved.pay(&challenge).await { + Ok(credential) => credential, + Err(e) => { + // Only the explicit `InsufficientBalance` variant is treated as + // a fundable error. Any other failure must surface unchanged so + // we don't mask payment/protocol issues behind a fund prompt. + let is_insufficient = matches!(e, mpp::MppError::InsufficientBalance(_)); + self.provider.rollback_pending(); + if is_insufficient && maybe_auto_fund(auto_fund_used, &funding_ctx).await? { + resolved.pay(&challenge).await.map_err(|e2| { + let suggest = matches!(e2, mpp::MppError::InsufficientBalance(_)); + self.provider.rollback_pending(); + TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e2, &funding_ctx, suggest), + )) + })? + } else { + return Err(TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e, &funding_ctx, is_insufficient), + ))); + } + } + }; let auth_header = format_authorization(&credential).map_err(|e| { + self.provider.rollback_pending(); TransportErrorKind::custom(std::io::Error::other(format!( "failed to format MPP credential: {e}" ))) @@ -286,9 +529,20 @@ where self.provider.commit_topup_and_track_voucher(); let resolved = self.provider.resolve()?; - let voucher_resp = self.pay_and_retry(&challenge, &resolved, &headers, &body).await?; - - let result = Self::handle_response(voucher_resp).await; + let voucher_resp = + self.pay_and_retry(&challenge, &resolved, &headers, &body, auto_fund_used).await?; + + // Route the voucher response through the funding-aware handler so + // a final 402 here also gets the fund retry / contextual help. + let result = self + .handle_response_or_retry_after_fund( + voucher_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; if result.is_ok() { self.provider.set_key_provisioned(true); self.provider.flush_pending(); @@ -304,7 +558,7 @@ where self.provider.rollback_pending(); self.provider.clear_channels(); - return Err(TransportErrorKind::custom(std::io::Error::other( + return Err(TransportErrorKind::custom(io::Error::other( "MPP channel not found on server (410 Gone). \ The server may have restarted or the channel was closed externally.\n\ Local channel state has been cleared. Re-run to open a new channel.", @@ -333,10 +587,19 @@ where debug!("MPP voucher stale, retrying with fresh voucher"); let resolved = self.provider.resolve()?; if resolved.supports(challenge.method.as_str(), challenge.intent.as_str()) { - let final_resp = - self.pay_and_retry(&challenge, &resolved, &headers, &body).await?; - - let result = Self::handle_response(final_resp).await; + let final_resp = self + .pay_and_retry(&challenge, &resolved, &headers, &body, auto_fund_used) + .await?; + + let result = self + .handle_response_or_retry_after_fund( + final_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; if result.is_ok() { self.provider.flush_pending(); } else { @@ -372,10 +635,19 @@ where let (resolved, fresh_challenge) = self.fetch_fresh_challenge(&headers, &body).await?; - let final_resp = - self.pay_and_retry(&fresh_challenge, &resolved, &headers, &body).await?; - - let result = Self::handle_response(final_resp).await; + let final_resp = self + .pay_and_retry(&fresh_challenge, &resolved, &headers, &body, auto_fund_used) + .await?; + + let result = self + .handle_response_or_retry_after_fund( + final_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; if result.is_ok() { self.provider.set_key_provisioned(true); self.provider.flush_pending(); @@ -386,9 +658,40 @@ where } self.provider.rollback_pending(); + if should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &retry_body) + && maybe_auto_fund(auto_fund_used, &funding_ctx).await? + { + let (resolved, fresh_challenge) = + self.fetch_fresh_challenge(&headers, &body).await?; + let final_resp = self + .pay_and_retry(&fresh_challenge, &resolved, &headers, &body, auto_fund_used) + .await?; + + let result = self + .handle_response_or_retry_after_fund( + final_resp, + &headers, + &body, + &funding_ctx, + auto_fund_used, + ) + .await; + if result.is_ok() { + self.provider.set_key_provisioned(true); + self.provider.flush_pending(); + } else { + self.provider.rollback_pending(); + } + return result; + } + + let mut error_text = format!("{retry_text}{diagnostics}"); + if should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &retry_body) { + error_text.push_str(&tempo_wallet_fund_help(&funding_ctx)); + } return Err(TransportErrorKind::http_error( StatusCode::PAYMENT_REQUIRED.as_u16(), - format!("{retry_text}{diagnostics}"), + error_text, )); } @@ -409,15 +712,32 @@ where provider: &P::Provider, headers: &reqwest::header::HeaderMap, body: &[u8], + auto_fund_used: &AtomicBool, ) -> TransportResult { - let credential = provider.pay(challenge).await.map_err(|e| { - self.provider.rollback_pending(); - TransportErrorKind::custom(std::io::Error::other(format!("MPP payment failed: {e}"))) - })?; + let funding_ctx = self.provider.funding_context(challenge); + let credential = match provider.pay(challenge).await { + Ok(credential) => credential, + Err(e) => { + self.provider.rollback_pending(); + let is_insufficient = matches!(e, mpp::MppError::InsufficientBalance(_)); + if is_insufficient && maybe_auto_fund(auto_fund_used, &funding_ctx).await? { + provider.pay(challenge).await.map_err(|e2| { + let suggest = matches!(e2, mpp::MppError::InsufficientBalance(_)); + TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e2, &funding_ctx, suggest), + )) + })? + } else { + return Err(TransportErrorKind::custom(std::io::Error::other( + format_mpp_payment_failure(e, &funding_ctx, is_insufficient), + ))); + } + } + }; let auth_header = format_authorization(&credential).map_err(|e| { self.provider.rollback_pending(); - TransportErrorKind::custom(std::io::Error::other(format!( + TransportErrorKind::custom(io::Error::other(format!( "failed to format MPP credential: {e}" ))) })?; @@ -437,6 +757,41 @@ where }) } + async fn handle_response_or_retry_after_fund( + &self, + resp: reqwest::Response, + headers: &reqwest::header::HeaderMap, + body: &[u8], + funding_ctx: &FundingContext, + auto_fund_used: &AtomicBool, + ) -> TransportResult { + if resp.status() != StatusCode::PAYMENT_REQUIRED { + return Self::handle_response_with_funding(resp, Some(funding_ctx)).await; + } + + let diagnostics = format_http_diagnostics(resp.headers()); + let status = resp.status(); + let resp_body = resp.bytes().await.map_err(TransportErrorKind::custom)?; + + if should_suggest_tempo_fund(status, &resp_body) + && maybe_auto_fund(auto_fund_used, funding_ctx).await? + { + self.provider.rollback_pending(); + + let (resolved, fresh_challenge) = self.fetch_fresh_challenge(headers, body).await?; + let final_resp = self + .pay_and_retry(&fresh_challenge, &resolved, headers, body, auto_fund_used) + .await?; + return Self::handle_response_with_funding(final_resp, Some(funding_ctx)).await; + } + + let mut error_text = format!("{}{diagnostics}", String::from_utf8_lossy(&resp_body)); + if should_suggest_tempo_fund(status, &resp_body) { + error_text.push_str(&tempo_wallet_fund_help(funding_ctx)); + } + Err(TransportErrorKind::http_error(status.as_u16(), error_text)) + } + /// Fetch a fresh 402 challenge from the server (unauthenticated request). /// /// Returns `Ok(Some((provider, challenge)))` if the server returns a 402 @@ -462,7 +817,7 @@ where // Non-402 → return whatever the server sent (could be success or error). let result = Self::handle_response(fresh_resp).await; return Err(result.err().unwrap_or_else(|| { - TransportErrorKind::custom(std::io::Error::other( + TransportErrorKind::custom(io::Error::other( "unexpected success on unauthenticated fresh probe", )) })); @@ -477,25 +832,14 @@ where resp: &reqwest::Response, provider: &P, ) -> TransportResult<(P::Provider, mpp::protocol::core::PaymentChallenge)> { - let www_auth_values: Vec<&str> = resp - .headers() - .get_all(WWW_AUTHENTICATE_HEADER) - .iter() - .filter_map(|v| v.to_str().ok()) - .collect(); - - if www_auth_values.is_empty() { - return Err(TransportErrorKind::custom(std::io::Error::other(format!( + let challenges = parse_challenges(resp); + if challenges.is_empty() && resp.headers().get(WWW_AUTHENTICATE_HEADER).is_none() { + return Err(TransportErrorKind::custom(io::Error::other(format!( "402 response missing WWW-Authenticate header{}", format_http_diagnostics(resp.headers()) )))); } - let challenges: Vec<_> = parse_www_authenticate_all(www_auth_values) - .into_iter() - .filter_map(|r| r.ok()) - .collect(); - let mut last_resolve_err: Option = None; let resolved_pair = challenges.iter().find_map(|c| { let (chain_id, currency) = extract_challenge_chain_and_currency(c); @@ -515,7 +859,7 @@ where } let offered: Vec<_> = challenges.iter().map(|c| format!("{}.{}", c.method, c.intent)).collect(); - TransportErrorKind::custom(std::io::Error::other(format!( + TransportErrorKind::custom(io::Error::other(format!( "no supported MPP challenge; server offered [{}]", offered.join(", "), ))) @@ -523,6 +867,17 @@ where } async fn handle_response(resp: reqwest::Response) -> TransportResult { + Self::handle_response_with_funding(resp, None).await + } + + /// Like [`Self::handle_response`] but, when an unsuccessful 402 looks like a + /// fundable error, appends actionable `tempo wallet fund` help that uses + /// the per-request `FundingContext` (so the suggested command includes + /// `--address` and `--network` when known). + async fn handle_response_with_funding( + resp: reqwest::Response, + funding_ctx: Option<&FundingContext>, + ) -> TransportResult { let status = resp.status(); debug!(%status, "received response from MPP transport"); let diagnostics = format_http_diagnostics(resp.headers()); @@ -536,10 +891,19 @@ where } if !status.is_success() { - return Err(TransportErrorKind::http_error( - status.as_u16(), - format!("{}{diagnostics}", String::from_utf8_lossy(&body)), - )); + let mut body_text = format!("{}{diagnostics}", String::from_utf8_lossy(&body)); + if should_suggest_tempo_fund(status, &body) { + let default_ctx; + let ctx = match funding_ctx { + Some(c) => c, + None => { + default_ctx = FundingContext::default(); + &default_ctx + } + }; + body_text.push_str(&tempo_wallet_fund_help(ctx)); + } + return Err(TransportErrorKind::http_error(status.as_u16(), body_text)); } serde_json::from_slice(&body) @@ -547,6 +911,57 @@ where } } +/// Returns `Some(chain_id)` when a 402 response should trigger the +/// `wallet.tempo.xyz` device-code authorization flow. +/// +/// Conditions: known Tempo endpoint, interactive (TTY, not `CI`), and no +/// offered Tempo challenge resolves against a local key on `(chain, currency)`. +/// The picked chain matches the first unresolved challenge — same iteration +/// order [`MppHttpTransport::select_challenge`] uses. +fn tempo_chain_needing_auth(url: &Url, resp: &reqwest::Response) -> Option { + if !io::stderr().is_terminal() || env::var_os("CI").is_some() { + return None; + } + pick_chain_needing_auth(url, &parse_challenges(resp)) +} + +/// Extract all parseable MPP challenges from a 402 response's `WWW-Authenticate` headers. +fn parse_challenges(resp: &reqwest::Response) -> Vec { + let values: Vec<&str> = resp + .headers() + .get_all(WWW_AUTHENTICATE_HEADER) + .iter() + .filter_map(|v| v.to_str().ok()) + .collect(); + parse_www_authenticate_all(values).into_iter().filter_map(|r| r.ok()).collect() +} + +/// Inner logic of [`tempo_chain_needing_auth`], factored out for testing. +fn pick_chain_needing_auth( + url: &Url, + challenges: &[mpp::protocol::core::PaymentChallenge], +) -> Option { + if !crate::tempo::is_known_tempo_endpoint(url) { + return None; + } + + let tempo_challenges: Vec<_> = + challenges.iter().filter(|c| c.method.as_str() == "tempo").collect(); + + // If any challenge already resolves with a local key, no auth needed. + let any_resolvable = tempo_challenges.iter().any(|c| { + let (chain_id, currency) = extract_challenge_chain_and_currency(c); + let currency = currency.and_then(|s| s.parse().ok()); + super::keys::discover_mpp_config(super::keys::DiscoverOptions { chain_id, currency }) + .is_some() + }); + if any_resolvable { + return None; + } + + tempo_challenges.iter().find_map(|c| extract_challenge_chain_and_currency(c).0) +} + /// Extract `(chainId, currency)` from a parsed MPP challenge. pub(super) fn extract_challenge_chain_and_currency( c: &mpp::protocol::core::PaymentChallenge, @@ -576,10 +991,28 @@ pub(crate) trait ResolveProvider { fn flush_pending(&self) {} fn rollback_pending(&self) {} fn commit_topup_and_track_voucher(&self) {} + /// Drop any cached payment provider so the next `resolve_for` re-runs + /// discovery. Called after the device-code flow writes a fresh + /// `keys.toml` entry. + fn invalidate_cached_provider(&self) {} + fn funding_wallet_address(&self) -> Option { + None + } + fn funding_chain_id(&self) -> Option { + None + } + fn funding_context(&self, challenge: &mpp::protocol::core::PaymentChallenge) -> FundingContext { + let (challenge_chain_id, token) = extract_challenge_chain_and_currency(challenge); + FundingContext { + wallet_address: self.funding_wallet_address(), + token, + chain_id: challenge_chain_id.or_else(|| self.funding_chain_id()).map(Chain::from_id), + } + } /// Acquire the payment serialization lock. The returned guard must be held /// across the entire 402 → pay → retry → response cycle to prevent /// concurrent channel opens and colliding expiring-nonce transactions. - fn lock_pay(&self) -> impl std::future::Future>> + Send { + fn lock_pay(&self) -> impl Future>> + Send { async { None } } } @@ -599,7 +1032,7 @@ impl ResolveProvider for LazySessionProvider { // regardless of opts. Re-check that the provider's key is compatible // with this challenge's chain/currency. if !provider.matches_challenge(opts.chain_id, opts.currency) { - return Err(TransportErrorKind::custom(std::io::Error::other( + return Err(TransportErrorKind::custom(io::Error::other( "cached provider does not match challenge chain/currency", ))); } @@ -623,7 +1056,16 @@ impl ResolveProvider for LazySessionProvider { fn commit_topup_and_track_voucher(&self) { Self::commit_topup_and_track_voucher(self) } - fn lock_pay(&self) -> impl std::future::Future>> + Send { + fn invalidate_cached_provider(&self) { + Self::invalidate(self) + } + fn funding_wallet_address(&self) -> Option { + self.inner.lock().unwrap().as_ref().map(|p| p.funding_wallet_address()) + } + fn funding_chain_id(&self) -> Option { + self.inner.lock().unwrap().as_ref().and_then(|p| p.key_chain_id()) + } + fn lock_pay(&self) -> impl Future>> + Send { let lock = self.pay_lock.clone(); async move { Some(lock.lock_owned().await) } } @@ -685,7 +1127,7 @@ mod tests { fn pay( &self, challenge: &PaymentChallenge, - ) -> impl std::future::Future> + Send { + ) -> impl Future> + Send { let echo = challenge.to_echo(); async move { Ok(PaymentCredential::with_source( @@ -697,6 +1139,21 @@ mod tests { } } + #[derive(Clone, Debug)] + struct InsufficientBalanceProvider; + + impl PaymentProvider for InsufficientBalanceProvider { + fn supports(&self, method: &str, intent: &str) -> bool { + method == "tempo" && (intent == "session" || intent == "charge") + } + + async fn pay(&self, _challenge: &PaymentChallenge) -> Result { + Err(MppError::InsufficientBalance(Some( + "wallet has 0 pathUSD but needs 100000".to_string(), + ))) + } + } + fn test_challenge() -> (PaymentChallenge, String) { let request = Base64UrlJson::from_value(&serde_json::json!({ "amount": "1000", @@ -853,8 +1310,238 @@ mod tests { handle.abort(); } + #[tokio::test] + async fn test_mpp_transport_payment_failure_suggests_tempo_wallet_fund() { + let (_, www_auth) = test_challenge(); + + let app = axum::Router::new().route( + "/", + post(move || { + let www_auth = www_auth.clone(); + async move { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("www-authenticate", www_auth)], + "Payment Required", + ) + } + }), + ); + + let (base_url, handle) = spawn_server(app).await; + let mut transport = MppHttpTransport::new( + reqwest::Client::new(), + Url::parse(&base_url).unwrap(), + InsufficientBalanceProvider, + ); + + let err = tower::Service::call(&mut transport, test_request()).await.unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("Tempo wallet payment could not be funded"), "got: {msg}"); + assert!(msg.contains("tempo wallet fund"), "got: {msg}"); + assert!(msg.contains("--no-browser"), "got: {msg}"); + assert!(msg.contains("Requested payment token: 0x20c0"), "got: {msg}"); + + handle.abort(); + } + + #[tokio::test] + async fn test_mpp_transport_retry_402_insufficient_balance_suggests_fund() { + let (_, www_auth) = test_challenge(); + + let app = axum::Router::new().route( + "/", + post(move |req: axum::http::Request| { + let www_auth = www_auth.clone(); + async move { + if req.headers().get("authorization").is_some() { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("content-type", "application/problem+json")], + serde_json::to_string( + &mpp::error::PaymentErrorDetails::session("insufficient-balance") + .with_title("InsufficientBalanceError") + .with_detail( + "Insufficient pathUSD balance: have 0, need 100000", + ), + ) + .unwrap(), + ) + .into_response() + } else { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("www-authenticate", www_auth)], + "Payment Required".to_string(), + ) + .into_response() + } + } + }), + ); + + let (base_url, handle) = spawn_server(app).await; + let mut transport = MppHttpTransport::new( + reqwest::Client::new(), + Url::parse(&base_url).unwrap(), + MockPaymentProvider, + ); + + let err = tower::Service::call(&mut transport, test_request()).await.unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("InsufficientBalanceError"), "got: {msg}"); + assert!(msg.contains("Tempo wallet payment could not be funded"), "got: {msg}"); + assert!(msg.contains("tempo wallet fund"), "got: {msg}"); + assert!(msg.contains("--no-browser"), "got: {msg}"); + assert!(msg.contains("Requested payment token: 0x20c0"), "got: {msg}"); + + handle.abort(); + } + + /// Generic `verification-failed` has many non-funding causes (bad signature, + /// replay, expired challenge, clock skew, ...). The transport must surface + /// the original error verbatim and must NOT add a "fund your wallet" hint. + #[tokio::test] + async fn test_mpp_transport_final_402_verification_failed_does_not_suggest_fund() { + let (_, www_auth) = test_challenge(); + + let app = axum::Router::new().route( + "/", + post(move |req: axum::http::Request| { + let www_auth = www_auth.clone(); + async move { + if req.headers().get("authorization").is_some() { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("content-type", "application/problem+json")], + serde_json::to_string( + &mpp::error::PaymentErrorDetails::core("verification-failed") + .with_title("Verification Failed") + .with_detail("Payment verification failed."), + ) + .unwrap(), + ) + .into_response() + } else { + ( + AxumStatusCode::PAYMENT_REQUIRED, + [("www-authenticate", www_auth)], + "Payment Required".to_string(), + ) + .into_response() + } + } + }), + ); + + let (base_url, handle) = spawn_server(app).await; + let mut transport = MppHttpTransport::new( + reqwest::Client::new(), + Url::parse(&base_url).unwrap(), + MockPaymentProvider, + ); + + let err = tower::Service::call(&mut transport, test_request()).await.unwrap_err(); + let msg = err.to_string(); + assert!(msg.contains("Verification Failed"), "got: {msg}"); + assert!( + !msg.contains("Tempo wallet payment could not be funded"), + "verification-failed must not be classified as fundable; got: {msg}" + ); + + handle.abort(); + } + + // --- Classifier unit tests -------------------------------------------- + + #[test] + fn classifier_only_triggers_on_explicit_insufficient_balance_problem() { + // explicit insufficient-balance → true + let body = serde_json::to_vec( + &mpp::error::PaymentErrorDetails::session("insufficient-balance") + .with_title("InsufficientBalanceError") + .with_detail("Insufficient pathUSD balance"), + ) + .unwrap(); + assert!(should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &body)); + } + + #[test] + fn classifier_does_not_trigger_on_verification_failed() { + let body = serde_json::to_vec( + &mpp::error::PaymentErrorDetails::core("verification-failed") + .with_title("Verification Failed") + .with_detail("Payment verification failed."), + ) + .unwrap(); + assert!(!should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, &body)); + } + + #[test] + fn classifier_does_not_trigger_on_unrelated_text_with_balance_words() { + // Free-text 402 body that just happens to mention the word "balance" + // must NOT trigger the fund suggestion (no structured problem details). + let body = + b"402 Payment Required: server could not balance ledger entries; insufficient inputs."; + assert!(!should_suggest_tempo_fund(StatusCode::PAYMENT_REQUIRED, body)); + } + + #[test] + fn classifier_does_not_trigger_outside_402() { + let body = serde_json::to_vec( + &mpp::error::PaymentErrorDetails::session("insufficient-balance") + .with_detail("Insufficient balance"), + ) + .unwrap(); + assert!(!should_suggest_tempo_fund(StatusCode::INTERNAL_SERVER_ERROR, &body)); + assert!(!should_suggest_tempo_fund(StatusCode::OK, &body)); + } + + #[test] + fn fund_help_includes_address_and_network_for_known_chain() { + let ctx = FundingContext { + wallet_address: Some("0x000000000000000000000000000000000000dEaD".parse().unwrap()), + token: Some("0x20c0".to_string()), + chain_id: Some(Chain::from_id(42431)), + }; + let help = tempo_wallet_fund_help(&ctx); + assert!(help.contains("--address 0x"), "missing --address: {help}"); + assert!(help.contains("--network tempo-moderato"), "missing --network: {help}"); + assert!(help.contains("--no-browser"), "missing --no-browser: {help}"); + assert!(help.contains("Requested payment token: 0x20c0"), "missing token: {help}"); + + let mainnet = FundingContext { chain_id: Some(Chain::from_id(4217)), ..ctx }; + let help2 = tempo_wallet_fund_help(&mainnet); + assert!(help2.contains("--network tempo"), "missing tempo network: {help2}"); + } + + #[test] + fn auto_fund_policy_blocks_in_ci_and_non_tty() { + assert!(!interactive_tempo_fund_allowed(Some("1"), true, true, true), "must not run in CI"); + assert!( + interactive_tempo_fund_allowed(Some("0"), false, true, true), + "FOUNDRY_MPP_NO_AUTO_FUND=0 must not disable" + ); + assert!( + interactive_tempo_fund_allowed(Some("false"), false, true, true), + "FOUNDRY_MPP_NO_AUTO_FUND=false must not disable" + ); + assert!( + !interactive_tempo_fund_allowed(None, false, false, true), + "stdin must be a terminal" + ); + assert!( + !interactive_tempo_fund_allowed(None, false, true, false), + "stderr must be a terminal" + ); + assert!(!interactive_tempo_fund_allowed(Some("1"), false, true, true)); + assert!(!interactive_tempo_fund_allowed(Some("true"), false, true, true)); + assert!(interactive_tempo_fund_allowed(None, false, true, true)); + } + #[tokio::test] async fn test_plain_http_402_shows_mpp_setup_instructions() { + let _g = crate::tempo::test_env_mutex().lock().await; let (_, www_auth) = test_challenge(); let app = axum::Router::new().route( @@ -920,6 +1607,32 @@ mod tests { ); } + /// `invalidate_cached_provider` clears the cache so the next + /// `get_or_init` re-runs discovery — the path `do_request` takes after + /// `ensure_access_key` writes a fresh `keys.toml` entry. + #[tokio::test] + async fn lazy_session_provider_invalidate_clears_cache() { + let _g = crate::tempo::test_env_mutex().lock().await; + // TEMPO_PRIVATE_KEY lets discovery succeed without a keys.toml. + let key_hex = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + unsafe { + std::env::set_var(crate::tempo::TEMPO_PRIVATE_KEY_ENV, key_hex); + std::env::remove_var(crate::tempo::TEMPO_HOME_ENV); + } + + let lazy = LazySessionProvider::new("https://rpc.example.com".into()); + let _ = lazy.get_or_init(Default::default()).expect("discovery succeeds"); + assert!(lazy.inner.lock().unwrap().is_some(), "expected provider to be cached"); + + ResolveProvider::invalidate_cached_provider(&lazy); + assert!(lazy.inner.lock().unwrap().is_none(), "expected cache to be cleared"); + + let _ = lazy.get_or_init(Default::default()).expect("re-discovery succeeds"); + assert!(lazy.inner.lock().unwrap().is_some(), "expected re-init to repopulate cache"); + + unsafe { std::env::remove_var(crate::tempo::TEMPO_PRIVATE_KEY_ENV) }; + } + #[test] fn challenge_chain_and_currency_extraction() { let extract = |headers: Vec<&str>| -> Vec<(Option, Option)> { @@ -955,4 +1668,73 @@ mod tests { ); assert_eq!(extract(vec![&no_details]), vec![(None, Some("0x20c0".into()))]); } + + /// Auth must trigger when a key matches the chain but not the currency. + #[test] + fn pick_chain_needing_auth_currency_aware() { + let _g = crate::tempo::test_env_mutex().blocking_lock(); + let dir = tempfile::tempdir().unwrap(); + let wallet = dir.path().join("wallet"); + std::fs::create_dir_all(&wallet).unwrap(); + std::fs::write( + wallet.join("keys.toml"), + r#" +[[keys]] +wallet_type = "passkey" +wallet_address = "0x0000000000000000000000000000000000000001" +key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +chain_id = 4217 + +[[keys.limits]] +currency = "0x20c0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +limit = "1000" +"#, + ) + .unwrap(); + unsafe { + std::env::set_var(crate::tempo::TEMPO_HOME_ENV, dir.path()); + std::env::remove_var(crate::tempo::TEMPO_PRIVATE_KEY_ENV); + } + + let url = Url::parse("https://rpc.mpp.tempo.xyz").unwrap(); + let mk = |currency: &str| -> PaymentChallenge { + PaymentChallenge { + id: "x".into(), + realm: "api".into(), + method: MethodName::new("tempo"), + intent: IntentName::new("charge"), + request: Base64UrlJson::from_value(&serde_json::json!({ + "amount": "1", + "currency": currency, + "recipient": "0xabc", + "methodDetails": { "chainId": 4217 } + })) + .unwrap(), + expires: None, + description: None, + digest: None, + opaque: None, + } + }; + + // Currency mismatch → auth needed. + let mismatched = mk("0x20c0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + assert_eq!(pick_chain_needing_auth(&url, &[mismatched]), Some(4217)); + + // Currency match → no auth. + let matched = mk("0x20c0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + assert_eq!(pick_chain_needing_auth(&url, &[matched]), None); + + // Non-Tempo host → never triggers, even without a key. + let stripe_url = Url::parse("https://api.stripe.com").unwrap(); + assert_eq!( + pick_chain_needing_auth( + &stripe_url, + &[mk("0x20c0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")] + ), + None, + ); + + unsafe { std::env::remove_var(crate::tempo::TEMPO_HOME_ENV) }; + } } diff --git a/crates/common/src/provider/mpp/ws.rs b/crates/common/src/provider/mpp/ws.rs index f631d0b08a2fc..69aef7d4f4cbc 100644 --- a/crates/common/src/provider/mpp/ws.rs +++ b/crates/common/src/provider/mpp/ws.rs @@ -378,6 +378,8 @@ mod tests { /// MPP server sends challenge → client pays → server sends receipt. #[tokio::test] async fn test_ws_mpp_challenge_credential_receipt() { + // Serialize with other tests that mutate TEMPO_PRIVATE_KEY / TEMPO_HOME. + let _g = crate::tempo::test_env_mutex().lock().await; let challenge = test_challenge(); let challenge_json = serde_json::to_value(&challenge).unwrap(); @@ -452,6 +454,8 @@ mod tests { /// MPP server sends challenge, client pays, server closes → rollback. #[tokio::test] async fn test_ws_mpp_rollback_on_post_pay_close() { + // Serialize with other tests that mutate TEMPO_PRIVATE_KEY / TEMPO_HOME. + let _g = crate::tempo::test_env_mutex().lock().await; let challenge = test_challenge(); let challenge_json = serde_json::to_value(&challenge).unwrap(); diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 7db1ebd1b3f91..f59a2efa75b8e 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -36,7 +36,11 @@ fn is_known_mpp_endpoint(url: &Url) -> bool { /// Only meant to be used internally by [RuntimeTransport]. #[derive(Clone, Debug)] pub enum InnerTransport { - /// HTTP transport with lazy MPP 402 handling + /// HTTP transport with lazy MPP 402 handling. + /// + /// For known Tempo endpoints, the MPP layer additionally runs the + /// `wallet.tempo.xyz` device-code flow on a 402 when no local access key + /// is configured (see [`crate::tempo::ensure_access_key`]). Http(LazyMppHttpTransport), /// WebSocket transport Ws(PubSubFrontend), diff --git a/crates/common/src/tempo/auth.rs b/crates/common/src/tempo/auth.rs new file mode 100644 index 0000000000000..d79306cfb74f2 --- /dev/null +++ b/crates/common/src/tempo/auth.rs @@ -0,0 +1,494 @@ +//! Tempo wallet device-code authorization flow. +//! +//! Implements the CLI side of the tempoxyz/accounts `cli-auth` device-code +//! protocol: generates a local secp256k1 access key, creates a PKCE-protected +//! device code, opens `wallet.tempo.xyz/cli-auth?code=` in the browser, +//! polls until the user authorizes the key on their passkey wallet, and writes +//! the resulting `keyAuthorization` to `~/.tempo/wallet/keys.toml`. + +use crate::tempo::{ + KeyEntry, KeyType, StoredTokenLimit, WalletType, decode_key_authorization, upsert_key_entry, +}; +use alloy_primitives::{Address, B256, hex}; +use alloy_signer_local::PrivateKeySigner; +use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; +use eyre::Result; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +#[cfg(any(unix, windows))] +use std::process::Command; +use std::{ + env, + sync::LazyLock, + time::{Duration, Instant}, +}; +use tempo_primitives::transaction::{SignatureType, SignedKeyAuthorization}; +use tokio::sync::Mutex; + +/// Default device-code service URL (production wallet.tempo.xyz). +const DEFAULT_CLI_AUTH_URL: &str = "https://wallet.tempo.xyz/cli-auth"; + +/// Returns `true` if `url`'s host is `tempo.xyz` or a subdomain of it. +pub(crate) fn is_known_tempo_endpoint(url: &url::Url) -> bool { + url.host_str().is_some_and(|host| host == "tempo.xyz" || host.ends_with(".tempo.xyz")) +} + +/// Env var to override the device-code service URL (for tests / staging). +const TEMPO_CLI_AUTH_URL_ENV: &str = "TEMPO_CLI_AUTH_URL"; + +const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(2); +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(300); + +/// Per-process serialization of concurrent `ensure_access_key` calls. +/// +/// Prevents two `cast` invocations in the same process from racing two browser +/// popups for the same chain. +static AUTH_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); + +/// Configuration for [`ensure_access_key`]. +#[derive(Clone, Debug)] +pub struct EnsureAccessKeyConfig { + /// Chain ID the access key is being authorized for. + pub chain_id: u64, + /// Device-code service base URL. Defaults to [`DEFAULT_CLI_AUTH_URL`]. + pub(crate) service_url: String, + /// Poll interval. + pub(crate) poll_interval: Duration, + /// Total timeout for the authorization flow. + pub(crate) timeout: Duration, + /// If `true`, print the authorization URL to stderr instead of opening a + /// browser. + pub no_browser: bool, +} + +impl EnsureAccessKeyConfig { + /// Build a config from the environment for the given chain. + /// + /// `no_browser` defaults to `true` under `CI`; callers (e.g. `cast tempo + /// login --no-browser`) may override it. + pub fn from_env(chain_id: u64) -> Self { + Self { + chain_id, + service_url: env::var(TEMPO_CLI_AUTH_URL_ENV) + .unwrap_or_else(|_| DEFAULT_CLI_AUTH_URL.to_string()), + poll_interval: DEFAULT_POLL_INTERVAL, + timeout: DEFAULT_TIMEOUT, + no_browser: env::var_os("CI").is_some(), + } + } +} + +/// Open `url` via the OS default browser handler. On platforms without a known +/// opener, this is a no-op (the URL is still printed by [`ensure_access_key`]). +fn open_browser(_url: &str) { + #[cfg(target_os = "macos")] + let _ = Command::new("open").arg(_url).spawn(); + #[cfg(target_os = "windows")] + let _ = Command::new("cmd").args(["/c", "start", "", _url]).spawn(); + #[cfg(all(unix, not(target_os = "macos")))] + let _ = Command::new("xdg-open").arg(_url).spawn(); +} + +/// Result of [`ensure_access_key`]. +#[derive(Debug, Clone)] +pub struct AccessKeyOutcome { + pub wallet_address: Address, + pub key_address: Address, + pub chain_id: u64, +} + +/// Run the device-code flow, persist the resulting key to `keys.toml`, and +/// return the new entry's identifying fields. +pub async fn ensure_access_key(cfg: EnsureAccessKeyConfig) -> Result { + let _guard = AUTH_LOCK.lock().await; + + let signer = PrivateKeySigner::random(); + let key_address = signer.address(); + // The server requires uncompressed SEC1 (65-byte `0x04 || X || Y`); the + // default `to_sec1_bytes()` would emit the compressed 33-byte form. + let pub_key_hex = format!( + "0x{}", + hex::encode(signer.credential().verifying_key().to_encoded_point(false).as_bytes()), + ); + + let code_verifier = random_code_verifier(); + let client = reqwest::Client::builder().timeout(Duration::from_secs(30)).build()?; + let service = cfg.service_url.trim_end_matches('/'); + + let create_req = CreateCodeRequest { + chain_id: cfg.chain_id, + code_challenge: sha256_b64url(&code_verifier), + key_type: "secp256k1", + pub_key: pub_key_hex, + }; + let code = create_code_with_retry(&client, service, &create_req, cfg.timeout).await?; + + let browser_url = format!("{service}?code={code}"); + if cfg.no_browser { + let _ = crate::sh_eprintln!("Open this URL to authorize: {browser_url}"); + } else { + let _ = crate::sh_eprintln!( + "Opening wallet.tempo to authorize an access key…\n {browser_url}" + ); + open_browser(&browser_url); + } + + let poll = PollRequest { code_verifier }; + let started = Instant::now(); + loop { + // Retry transient network/5xx/429 failures within `cfg.timeout`. + let send_res = client.post(format!("{service}/poll/{code}")).json(&poll).send().await; + + let resp = match send_res { + Ok(r) => r, + Err(e) if is_transient_error(&e) && started.elapsed() < cfg.timeout => { + tracing::debug!(error = %e, "transient error polling device code, retrying"); + tokio::time::sleep(cfg.poll_interval).await; + continue; + } + Err(e) => return Err(e.into()), + }; + + let status = resp.status(); + if !status.is_success() { + if is_transient_status(status) && started.elapsed() < cfg.timeout { + tracing::debug!(%status, "transient HTTP status polling device code, retrying"); + tokio::time::sleep(cfg.poll_interval).await; + continue; + } + let body = resp.text().await.unwrap_or_default(); + eyre::bail!("device-code poll failed ({status}): {body}"); + } + + let body: PollResponse = resp.json().await?; + match body { + PollResponse::Pending => { + if started.elapsed() > cfg.timeout { + eyre::bail!("timed out waiting for wallet authorization (code {code})"); + } + tokio::time::sleep(cfg.poll_interval).await; + } + PollResponse::Expired => { + eyre::bail!("device code {code} expired before authorization"); + } + PollResponse::Authorized { account_address, key_authorization } => { + let hex_str = key_authorization.ok_or_else(|| { + eyre::eyre!("wallet authorized response missing key_authorization") + })?; + let signed: SignedKeyAuthorization = decode_key_authorization(&hex_str)?; + // Reject mismatches before persisting — an unusable keys.toml + // entry would silently break the next 402 retry. + if signed.authorization.key_id != key_address { + eyre::bail!( + "wallet authorized key {} but the locally generated key is {}", + signed.authorization.key_id, + key_address, + ); + } + if signed.authorization.chain_id != cfg.chain_id { + eyre::bail!( + "wallet authorized chain {} but {} was requested", + signed.authorization.chain_id, + cfg.chain_id, + ); + } + if signed.authorization.key_type != SignatureType::Secp256k1 { + eyre::bail!( + "wallet returned keyType {:?} but secp256k1 was requested", + signed.authorization.key_type, + ); + } + let chain_id = signed.authorization.chain_id; + let key_authorization = + if hex_str.starts_with("0x") { hex_str } else { format!("0x{hex_str}") }; + let entry = KeyEntry { + wallet_type: WalletType::Passkey, + wallet_address: account_address, + chain_id, + key_type: match signed.authorization.key_type { + SignatureType::P256 => KeyType::P256, + SignatureType::WebAuthn => KeyType::WebAuthn, + _ => KeyType::Secp256k1, + }, + key_address: Some(key_address), + key: Some(format!("0x{}", hex::encode(signer.to_bytes()))), + key_authorization: Some(key_authorization), + expiry: signed.authorization.expiry.map(|n| n.get()), + limits: signed + .authorization + .limits + .unwrap_or_default() + .into_iter() + .map(|l| StoredTokenLimit { currency: l.token, limit: l.limit.to_string() }) + .collect(), + }; + upsert_key_entry(entry)?; + return Ok(AccessKeyOutcome { + wallet_address: account_address, + key_address, + chain_id, + }); + } + } + } +} + +fn is_transient_error(err: &reqwest::Error) -> bool { + err.is_timeout() || err.is_connect() || err.is_request() +} + +fn is_transient_status(status: reqwest::StatusCode) -> bool { + status.is_server_error() || status == reqwest::StatusCode::TOO_MANY_REQUESTS +} + +/// POST `/code` with exponential backoff on transient errors, bounded by `timeout`. +async fn create_code_with_retry( + client: &reqwest::Client, + service: &str, + req: &CreateCodeRequest, + timeout: Duration, +) -> Result { + let started = Instant::now(); + let mut backoff = Duration::from_millis(500); + loop { + let send_res = client.post(format!("{service}/code")).json(req).send().await; + + match send_res { + Ok(resp) => { + let status = resp.status(); + if status.is_success() { + let CreateCodeResponse { code } = resp.json().await?; + return Ok(code); + } + if is_transient_status(status) && started.elapsed() < timeout { + tracing::debug!(%status, "transient HTTP status creating device code, retrying"); + tokio::time::sleep(backoff).await; + backoff = (backoff * 2).min(Duration::from_secs(5)); + continue; + } + let body = resp.text().await.unwrap_or_default(); + eyre::bail!("device-code create failed ({status}): {body}"); + } + Err(e) if is_transient_error(&e) && started.elapsed() < timeout => { + tracing::debug!(error = %e, "transient error creating device code, retrying"); + tokio::time::sleep(backoff).await; + backoff = (backoff * 2).min(Duration::from_secs(5)); + } + Err(e) => return Err(e.into()), + } + } +} + +fn random_code_verifier() -> String { + let bytes = B256::random(); + URL_SAFE_NO_PAD.encode(bytes.as_slice()) +} + +fn sha256_b64url(input: &str) -> String { + let digest = Sha256::digest(input.as_bytes()); + URL_SAFE_NO_PAD.encode(digest) +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct CreateCodeRequest { + /// `0x`-hex per the SDK schema (server accepts hex string or bigint, not a plain JSON number). + #[serde(serialize_with = "serialize_u64_hex")] + chain_id: u64, + code_challenge: String, + key_type: &'static str, + pub_key: String, +} + +fn serialize_u64_hex(v: &u64, s: S) -> std::result::Result { + s.serialize_str(&format!("0x{v:x}")) +} + +#[derive(Deserialize)] +struct CreateCodeResponse { + code: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct PollRequest { + code_verifier: String, +} + +/// Matches `tempoxyz/wallet` poll response shape. +#[derive(Deserialize)] +#[serde(tag = "status", rename_all = "lowercase")] +enum PollResponse { + Pending, + Expired, + Authorized { + account_address: Address, + #[serde(default)] + key_authorization: Option, + }, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tempo::{TEMPO_HOME_ENV, read_tempo_keys_file, test_env_mutex}; + use axum::{Json, Router, extract::State, routing::post}; + use std::sync::{Arc, Mutex}; + + #[test] + fn pkce_challenge_matches_sdk_format() { + // Vector from RFC 7636 §4.2. + let verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + let challenge = sha256_b64url(verifier); + assert_eq!(challenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"); + } + + /// Recover the EOA from a SEC1-encoded public key (compressed or + /// uncompressed). + fn address_from_sec1_hex(s: &str) -> Address { + let stripped = s.strip_prefix("0x").unwrap_or(s); + let bytes = hex::decode(stripped).expect("valid hex"); + let vk = k256::ecdsa::VerifyingKey::from_sec1_bytes(&bytes).expect("valid SEC1 pubkey"); + Address::from_public_key(&vk) + } + + #[derive(Clone)] + struct MockState { + wallet: Arc>>, + /// Derived from the `pubKey` posted to `/code` so `/poll` can echo + /// back a matching `keyId`, like a real wallet would. + key_id: Arc>>, + /// Chain ID the mock `/poll` returns in `keyAuthorization`. + poll_chain_id: u64, + } + + async fn create_code_handler( + State(state): State, + Json(body): Json, + ) -> Json { + // Sanity: required fields present and chainId is a 0x-hex string, + // matching the SDK wire format the live server enforces. + let pub_key = body + .get("pubKey") + .and_then(|v| v.as_str()) + .unwrap_or_else(|| panic!("pubKey missing: {body}")); + assert!(body.get("codeChallenge").is_some(), "codeChallenge missing: {body}"); + let chain_id = body.get("chainId").unwrap_or_else(|| panic!("chainId missing: {body}")); + let chain_str = chain_id + .as_str() + .unwrap_or_else(|| panic!("chainId must be string, got {chain_id}: {body}")); + assert!(chain_str.starts_with("0x"), "chainId must be 0x-hex, got {chain_str}"); + let wallet: Address = "0x0000000000000000000000000000000000000042".parse().unwrap(); + *state.wallet.lock().unwrap() = Some(wallet); + *state.key_id.lock().unwrap() = Some(address_from_sec1_hex(pub_key)); + Json(serde_json::json!({ "code": "ABCDEFGH" })) + } + + /// Build the RLP-hex `SignedKeyAuthorization` blob the live server returns + /// in the `key_authorization` field. + fn signed_key_auth_hex(chain_id: u64, key_id: Address, expiry: u64) -> String { + use alloy_rlp::Encodable; + use tempo_primitives::transaction::{KeyAuthorization, PrimitiveSignature}; + let auth = KeyAuthorization::unrestricted(chain_id, SignatureType::Secp256k1, key_id) + .with_expiry(expiry); + let sig: PrimitiveSignature = serde_json::from_value(serde_json::json!({ + "type": "secp256k1", "r": "0x0", "s": "0x0", "yParity": 0 + })) + .unwrap(); + let signed = auth.into_signed(sig); + let mut buf = Vec::new(); + signed.encode(&mut buf); + format!("0x{}", hex::encode(buf)) + } + + async fn poll_handler(State(state): State) -> Json { + let wallet = state.wallet.lock().unwrap().expect("create_code must be called first"); + let key_id = state.key_id.lock().unwrap().expect("create_code must be called first"); + Json(serde_json::json!({ + "status": "authorized", + "account_address": wallet, + "key_authorization": signed_key_auth_hex(state.poll_chain_id, key_id, 9_999_999_999), + })) + } + + /// Spawn a mock wallet.tempo server whose `/poll` echoes `poll_chain_id`. + async fn spawn_mock_wallet(poll_chain_id: u64) -> (String, tokio::task::JoinHandle<()>) { + let app = Router::new() + .route("/code", post(create_code_handler)) + .route("/poll/{code}", post(poll_handler)) + .with_state(MockState { + wallet: Arc::default(), + key_id: Arc::default(), + poll_chain_id, + }); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let handle = tokio::spawn(async move { + axum::serve(listener, app).await.unwrap(); + }); + (format!("http://{addr}"), handle) + } + + fn test_cfg(service_url: String) -> EnsureAccessKeyConfig { + EnsureAccessKeyConfig { + chain_id: 4217, + service_url, + poll_interval: Duration::from_millis(10), + timeout: Duration::from_secs(2), + no_browser: true, + } + } + + #[tokio::test(flavor = "multi_thread")] + async fn ensure_access_key_happy_path_writes_keys_toml() { + // SAFETY: serialized with other tests that mutate TEMPO_HOME. + let _g = test_env_mutex().lock().await; + let tmp = tempfile::tempdir().unwrap(); + unsafe { std::env::set_var(TEMPO_HOME_ENV, tmp.path()) }; + + let (service_url, server) = spawn_mock_wallet(4217).await; + let outcome = ensure_access_key(test_cfg(service_url)).await.unwrap(); + + let expected_wallet: Address = + "0x0000000000000000000000000000000000000042".parse().unwrap(); + assert_eq!(outcome.chain_id, 4217); + assert_eq!(outcome.wallet_address, expected_wallet); + + let file = read_tempo_keys_file().expect("keys.toml written"); + assert_eq!(file.keys.len(), 1); + let entry = &file.keys[0]; + assert_eq!(entry.wallet_address, outcome.wallet_address); + assert_eq!(entry.key_address, Some(outcome.key_address)); + assert_eq!(entry.chain_id, 4217); + assert_eq!(entry.expiry, Some(9_999_999_999)); + let decoded: tempo_primitives::transaction::SignedKeyAuthorization = + crate::tempo::decode_key_authorization(entry.key_authorization.as_deref().unwrap()) + .expect("RLP roundtrip"); + assert_eq!(decoded.authorization.chain_id, 4217); + + server.abort(); + unsafe { std::env::remove_var(TEMPO_HOME_ENV) }; + } + + #[tokio::test(flavor = "multi_thread")] + async fn ensure_access_key_rejects_wrong_chain_id() { + // Wallet returns chain 99999 but client requested 4217 → must reject + // and persist nothing, else discovery would later fail to find a key + // for the requested chain. + let _g = test_env_mutex().lock().await; + let tmp = tempfile::tempdir().unwrap(); + unsafe { std::env::set_var(TEMPO_HOME_ENV, tmp.path()) }; + + let (service_url, server) = spawn_mock_wallet(99999).await; + let err = ensure_access_key(test_cfg(service_url)).await.unwrap_err(); + assert!( + err.to_string().contains("wallet authorized chain 99999 but 4217 was requested"), + "expected chain mismatch error, got: {err}" + ); + assert!(read_tempo_keys_file().is_none_or(|f| f.keys.is_empty())); + + server.abort(); + unsafe { std::env::remove_var(TEMPO_HOME_ENV) }; + } +} diff --git a/crates/common/src/tempo/mod.rs b/crates/common/src/tempo/mod.rs index ec51dc607b5ab..ef8d0212bd453 100644 --- a/crates/common/src/tempo/mod.rs +++ b/crates/common/src/tempo/mod.rs @@ -1,8 +1,24 @@ //! Tempo network utilities. +pub mod auth; + +use crate::FoundryTransactionBuilder; +use alloy_network::Network; +use alloy_primitives::{Address, B256, Signature}; +use alloy_signer::Signer; +use eyre::{Context, Result}; +use foundry_wallets::{RawWalletOpts, WalletOpts, WalletSigner}; +use std::sync::Arc; + mod keystore; + +pub(crate) use auth::is_known_tempo_endpoint; +pub use auth::{AccessKeyOutcome, EnsureAccessKeyConfig, ensure_access_key}; pub use keystore::*; +#[cfg(test)] +pub(crate) use keystore::test_env_mutex; + #[cfg(test)] mod tests; @@ -16,3 +32,173 @@ mod tests; /// /// See pub const TEMPO_BROWSER_GAS_BUFFER: u64 = 7_000; + +/// Gas sponsor configuration for Tempo fee-payer signatures. +#[derive(Clone, Debug)] +pub struct TempoSponsor { + sponsor: Address, + signer: Option>, + signature: Option, +} + +impl TempoSponsor { + pub const fn new( + sponsor: Address, + signer: Option>, + signature: Option, + ) -> Self { + Self { sponsor, signer, signature } + } + + pub const fn sponsor(&self) -> Address { + self.sponsor + } + + pub async fn attach_and_print( + &self, + tx: &mut N::TransactionRequest, + sender: Address, + ) -> Result + where + N::TransactionRequest: FoundryTransactionBuilder, + { + if self.sponsor == sender { + eyre::bail!( + "invalid Tempo sponsorship: sponsor {} must not equal transaction sender", + self.sponsor + ); + } + + let digest = tx.compute_sponsor_hash(sender).ok_or_else(|| { + eyre::eyre!( + "failed to compute Tempo sponsor digest; make sure this is a complete Tempo AA transaction" + ) + })?; + + let preview = TempoSponsorPreview { + sponsor: self.sponsor, + fee_token: tx.fee_token(), + valid_before: tx.valid_before().map(|v| v.get()), + valid_after: tx.valid_after().map(|v| v.get()), + digest, + }; + preview.print()?; + + let signature = if let Some(signature) = self.signature { + signature + } else if let Some(signer) = &self.signer { + signer.sign_hash(&digest).await.context("failed to sign Tempo sponsor digest")? + } else { + eyre::bail!("missing Tempo sponsor signature or signer") + }; + + let recovered = signature + .recover_address_from_prehash(&digest) + .context("failed to recover Tempo sponsor signature")?; + if recovered != self.sponsor { + eyre::bail!("Tempo sponsor signature recovered {recovered}, expected {}", self.sponsor); + } + if recovered == sender { + eyre::bail!( + "invalid Tempo sponsorship: recovered fee payer {recovered} must not equal transaction sender" + ); + } + + tx.set_fee_payer_signature(signature); + Ok(preview) + } +} + +/// User-visible sponsor digest metadata for a single outgoing Tempo transaction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TempoSponsorPreview { + pub sponsor: Address, + pub fee_token: Option
, + pub valid_before: Option, + pub valid_after: Option, + pub digest: B256, +} + +impl TempoSponsorPreview { + pub fn print(&self) -> Result<()> { + crate::sh_eprintln!("Tempo sponsor: {}", self.sponsor)?; + crate::sh_eprintln!( + "Tempo fee token: {}", + self.fee_token.map_or_else(|| "network default".to_string(), |addr| addr.to_string()) + )?; + crate::sh_eprintln!( + "Tempo validity: after {}, before {}", + self.valid_after.map_or_else(|| "none".to_string(), |v| v.to_string()), + self.valid_before.map_or_else(|| "none".to_string(), |v| v.to_string()) + )?; + crate::sh_eprintln!("Tempo sponsor digest: {:?}", self.digest)?; + Ok(()) + } +} + +/// Resolves a `--tempo.sponsor-signer` URI into a Foundry wallet signer. +pub async fn resolve_tempo_sponsor_signer(spec: &str) -> Result { + let spec = spec.trim(); + let (scheme, value) = spec + .split_once("://") + .map(|(scheme, value)| (scheme.to_ascii_lowercase(), value)) + .unwrap_or_else(|| (spec.to_ascii_lowercase(), "")); + + match scheme.as_str() { + "env" => { + if value.is_empty() { + eyre::bail!("env:// sponsor signer requires an environment variable name"); + } + let private_key = std::env::var(value) + .wrap_err_with(|| format!("{value} environment variable is required"))?; + foundry_wallets::utils::create_private_key_signer(&private_key) + } + "private-key" => { + if value.is_empty() { + eyre::bail!("private-key:// sponsor signer requires a private key"); + } + foundry_wallets::utils::create_private_key_signer(value) + } + "keystore" => { + if value.is_empty() { + eyre::bail!("keystore:// sponsor signer requires a keystore path"); + } + WalletOpts { keystore_path: Some(value.to_string()), ..Default::default() } + .signer() + .await + } + "account" => { + if value.is_empty() { + eyre::bail!("account:// sponsor signer requires an account name"); + } + WalletOpts { keystore_account_name: Some(value.to_string()), ..Default::default() } + .signer() + .await + } + "ledger" => { + let raw = RawWalletOpts { + hd_path: (!value.is_empty()).then(|| value.to_string()), + ..Default::default() + }; + WalletOpts { ledger: true, raw, ..Default::default() }.signer().await + } + "trezor" => { + let raw = RawWalletOpts { + hd_path: (!value.is_empty()).then(|| value.to_string()), + ..Default::default() + }; + WalletOpts { trezor: true, raw, ..Default::default() }.signer().await + } + "aws" => WalletOpts { aws: true, ..Default::default() }.signer().await, + "gcp" => WalletOpts { gcp: true, ..Default::default() }.signer().await, + "turnkey" => WalletOpts { turnkey: true, ..Default::default() }.signer().await, + "browser" => { + eyre::bail!( + "browser:// sponsor signing is not supported by the current browser wallet API; use --tempo.sponsor-sig or another sponsor signer" + ) + } + _ => eyre::bail!( + "unsupported Tempo sponsor signer `{spec}`; expected env://VAR, keystore://PATH, account://NAME, ledger://, trezor://, aws://, gcp://, turnkey://, or private-key://KEY" + ), + } +} diff --git a/crates/common/src/transactions/builder.rs b/crates/common/src/transactions/builder.rs index de03cf3adc73e..aa4c971680d00 100644 --- a/crates/common/src/transactions/builder.rs +++ b/crates/common/src/transactions/builder.rs @@ -9,7 +9,9 @@ use alloy_primitives::{Address, B256, Signature, TxKind, U256}; use alloy_provider::Provider; use alloy_signer::Signer; use eyre::Result; +#[cfg(feature = "optimism")] use op_alloy_network::Optimism; +#[cfg(feature = "optimism")] use op_alloy_rpc_types::OpTransactionRequest; use tempo_alloy::{TempoNetwork, provider::TempoProviderExt}; use tempo_primitives::{ @@ -244,6 +246,24 @@ pub trait FoundryTransactionBuilder: NetworkTransactionBuilder { /// on-chain as part of this transaction. fn set_key_authorization(&mut self, _key_authorization: SignedKeyAuthorization) {} + /// Embeds key authorization before gas estimation/signing if the access key is not yet + /// provisioned on-chain. + /// + /// This mirrors the mutation performed by [`Self::sign_with_access_key`], but makes the final + /// transaction body available before fee-payer sponsor digests are computed. + fn prepare_access_key_authorization<'a>( + &'a mut self, + _provider: &'a impl Provider, + _wallet_address: Address, + _key_address: Address, + _key_authorization: Option<&'a SignedKeyAuthorization>, + ) -> impl Future> + Send + 'a + where + Self: Send, + { + async { Ok(()) } + } + /// Converts a CREATE transaction into an AA-compatible call entry. /// /// Tempo AA transactions use a `calls` list instead of `to`+`input`. Must be @@ -355,6 +375,7 @@ impl FoundryTransactionBuilder for ::Transact } } +#[cfg(feature = "optimism")] impl FoundryTransactionBuilder for OpTransactionRequest { fn reset_gas_limit(&mut self) { self.as_mut().gas = None; @@ -439,6 +460,35 @@ impl FoundryTransactionBuilder for ::Tran self.key_authorization = Some(key_authorization); } + fn prepare_access_key_authorization<'a>( + &'a mut self, + provider: &'a impl Provider, + wallet_address: Address, + key_address: Address, + key_authorization: Option<&'a SignedKeyAuthorization>, + ) -> impl Future> + Send + 'a + where + Self: Send, + { + let auth = key_authorization.cloned(); + + async move { + if let Some(auth) = auth { + let is_provisioned = provider + .get_keychain_key(wallet_address, key_address) + .await + .map(|info| info.keyId != Address::ZERO) + .unwrap_or(false); + + if !is_provisioned { + self.set_key_authorization(auth); + } + } + + Ok(()) + } + } + fn convert_create_to_call(&mut self) { if self.calls.is_empty() && self.inner.to.is_some_and(|to| to.is_create()) { let input = self.inner.input.input().cloned().unwrap_or_default(); @@ -473,7 +523,12 @@ impl FoundryTransactionBuilder for ::Tran let is_provisioned = provisioning_fut.await.map(|info| info.keyId != Address::ZERO).unwrap_or(false); - if !is_provisioned { + if !is_provisioned && self.key_authorization.is_none() { + if self.fee_payer_signature.is_some() { + eyre::bail!( + "cannot add Tempo key authorization after fee payer signature was attached" + ); + } self.set_key_authorization(auth); } } diff --git a/crates/common/src/transactions/receipt.rs b/crates/common/src/transactions/receipt.rs index 9ca6cb02b10ee..c2e34419248c4 100644 --- a/crates/common/src/transactions/receipt.rs +++ b/crates/common/src/transactions/receipt.rs @@ -7,6 +7,7 @@ use alloy_provider::{ use alloy_rpc_types::{BlockId, TransactionReceipt}; use eyre::Result; use foundry_common_fmt::{UIfmt, UIfmtReceiptExt, get_pretty_receipt_attr}; +#[cfg(feature = "optimism")] use op_alloy_rpc_types::OpTransactionReceipt; use serde::{Deserialize, Serialize}; use tempo_alloy::rpc::TempoTransactionReceipt; @@ -23,6 +24,7 @@ impl FoundryReceiptResponse for TransactionReceipt { } } +#[cfg(feature = "optimism")] impl FoundryReceiptResponse for OpTransactionReceipt { fn set_contract_address(&mut self, contract_address: Address) { self.inner.contract_address = Some(contract_address); diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index bab59137b0130..8f63718e086cd 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -10,6 +10,10 @@ use std::path::PathBuf; pub struct FuzzConfig { /// The number of test cases that must execute for each property test pub runs: u32, + /// Optional 1-based fuzz run to execute. + pub run: Option, + /// Optional fuzz worker ID to pair with `run`. + pub worker: Option, /// Fails the fuzzed test if a revert occurs. pub fail_on_revert: bool, /// The maximum number of test case rejections allowed, @@ -37,6 +41,8 @@ impl Default for FuzzConfig { fn default() -> Self { Self { runs: 256, + run: None, + worker: None, fail_on_revert: true, max_test_rejects: 65536, seed: None, diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 270df14a6c291..000cefc26737a 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use crate::Config; use alloy_primitives::map::HashMap; use figment::{ @@ -5,6 +7,7 @@ use figment::{ value::{Dict, Map, Value}, }; use foundry_compilers::ProjectCompileOutput; +use foundry_evm_networks::NetworkVariant; use itertools::Itertools; mod natspec; @@ -123,6 +126,42 @@ impl InlineConfig { self.get_function(contract, function).is_some_and(|map| !map.is_empty()) } + /// Returns the configured [`NetworkVariant`] for a given test, checking function-level first + /// then contract-level. Returns `None` if no network annotation is present. + pub fn network_for( + &self, + profile: &Profile, + contract: &str, + function: &str, + ) -> Option { + let data = self.provide(contract, function).data().ok()?; + let dict = data.get(profile).or_else(|| data.get(&Profile::Default))?; + if let Some(Value::Dict(_, networks)) = dict.get("networks") + && let Some(Value::String(_, s)) = networks.get("network") + { + return s.parse().ok(); + } + None + } + + /// Returns all distinct [`NetworkVariant`]s referenced in any inline config annotation. + /// + /// This is used to determine whether a multi-network test pass is needed. + pub fn referenced_override_networks(&self, profile: &Profile) -> Vec { + let mut seen = BTreeSet::new(); + for (contract, function) in self.fn_level.keys() { + if let Some(v) = self.network_for(profile, contract, function) { + seen.insert(v); + } + } + for contract in self.contract_level.keys() { + if let Some(v) = self.network_for(profile, contract, "") { + seen.insert(v); + } + } + seen.into_iter().collect() + } + fn get_contract(&self, contract: &str) -> Option<&DataMap> { self.contract_level.get(contract) } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 1f5c35775f1dc..71eb2a96d7152 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2883,19 +2883,35 @@ impl BasicConfig { /// /// This serializes to a table with the name of the profile pub fn to_string_pretty(&self) -> Result { - let mut value = toml::Value::try_from(self)?; + let mut profile_body = toml::Value::try_from(self)?; if let Some(ref network) = self.network - && let toml::Value::Table(ref mut table) = value + && let toml::Value::Table(ref mut table) = profile_body { - table.insert(network.clone(), toml::Value::Boolean(true)); + table.insert("network".to_string(), toml::Value::String(network.clone())); + } + + let mut profile_section = toml::value::Table::new(); + profile_section.insert(self.profile.to_string(), profile_body); + + let mut document = toml::value::Table::new(); + document.insert("profile".to_string(), toml::Value::Table(profile_section)); + + if self.network.as_deref() == Some("tempo") { + let mut endpoints = toml::value::Table::new(); + endpoints.insert( + "tempo".to_string(), + toml::Value::String("https://rpc.tempo.xyz/".to_string()), + ); + endpoints.insert( + "moderato".to_string(), + toml::Value::String("https://rpc.moderato.tempo.xyz/".to_string()), + ); + document.insert("rpc_endpoints".to_string(), toml::Value::Table(endpoints)); } - let s = toml::to_string_pretty(&value)?; + + let body = toml::to_string_pretty(&toml::Value::Table(document))?; Ok(format!( - "\ -[profile.{}] -{s} -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options\n", - self.profile + "{body}\n# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options\n" )) } } @@ -6700,6 +6716,55 @@ mod tests { }); } + #[test] + fn no_unknown_key_warning_for_network_field() { + // Regression test: `network` is a flattened `Option` field of `NetworkConfigs`. It must + // not trigger an unknown-key warning, regardless of whether it is set. + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + network = "tempo" + "#, + )?; + + let cfg = Config::load().unwrap(); + assert!( + !cfg.warnings.iter().any( + |w| matches!(w, crate::Warning::UnknownKey { key, .. } if key == "network") + ), + "did not expect UnknownKey warning for `network`, got: {:?}", + cfg.warnings + ); + Ok(()) + }); + } + + #[test] + fn no_unknown_key_warning_for_legacy_tempo_alias() { + // Regression test: the legacy `tempo = true` alias must keep working without warnings. + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + tempo = true + "#, + )?; + + let cfg = Config::load().unwrap(); + assert!( + !cfg.warnings + .iter() + .any(|w| matches!(w, crate::Warning::UnknownKey { key, .. } if key == "tempo")), + "did not expect UnknownKey warning for `tempo`, got: {:?}", + cfg.warnings + ); + Ok(()) + }); + } + #[test] fn fails_on_ambiguous_version_in_compilation_restrictions() { figment::Jail::expect_with(|jail| { diff --git a/crates/config/src/providers/warnings.rs b/crates/config/src/providers/warnings.rs index 930066b29cf73..ff1d0b35def47 100644 --- a/crates/config/src/providers/warnings.rs +++ b/crates/config/src/providers/warnings.rs @@ -38,7 +38,10 @@ const DOC_KEYS: &[&str] = &["out", "title", "book", "homepage", "repository", "p const RESERVED_KEYS: &[&str] = &["extends"]; /// Keys kept for backward compatibility that should not trigger unknown key warnings. -const BACKWARD_COMPATIBLE_KEYS: &[&str] = &["solc_version"]; +/// +/// `tempo` and `optimism` are legacy aliases for `network = "tempo"` / `network = "optimism"` — +/// still accepted on input but no longer serialized in the default config. +const BACKWARD_COMPATIBLE_KEYS: &[&str] = &["solc_version", "tempo", "optimism"]; /// Generate warnings for unknown sections and deprecated keys pub struct WarningsProvider

{ diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 3c8cad85bae10..cc3dabd32d4bf 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -29,3 +29,11 @@ ratatui = { version = "0.30", default-features = false, features = [ revm.workspace = true tracing.workspace = true serde.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm-core/optimism", + "foundry-evm-traces/optimism", +] diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 77020a88fe3cd..814beab402729 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -14,7 +14,6 @@ repository.workspace = true workspace = true [dependencies] -forge-fmt.workspace = true foundry-common.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true @@ -29,8 +28,11 @@ mdbook-driver = { version = "0.5", default-features = false, features = ["search rayon.workspace = true serde_json.workspace = true serde.workspace = true -solang-parser.workspace = true thiserror.workspace = true toml.workspace = true tracing.workspace = true regex.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index 7ecada9d33e72..ae456e5de7e2c 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -1,6 +1,6 @@ use crate::{ AsDoc, BufWriter, Document, ParseItem, ParseSource, Parser, Preprocessor, - document::DocumentContent, helpers::merge_toml_table, solang_ext::Visitable, + document::DocumentContent, helpers::merge_toml_table, }; use alloy_primitives::map::HashMap; use eyre::{Context, Result}; @@ -129,41 +129,27 @@ impl DocBuilder { let gcx = compiler.gcx(); let documents = combined_sources .par_iter() - .enumerate() - .map(|(i, (path, from_library))| { + .map(|(path, from_library)| { let path = *path; let from_library = *from_library; let mut files = vec![]; // Read and parse source file - if let Some((_, ast)) = gcx.get_ast_source(path) - && let Some(source) = - forge_fmt::format_ast(gcx, ast, self.fmt.clone().into()) + if let Some((_, ast_source)) = gcx.get_ast_source(path) + && let Some(source_unit) = ast_source.ast.as_ref() { - let (mut source_unit, comments) = match solang_parser::parse(&source, i) { - Ok(res) => res, - Err(err) => { - if from_library { - // Ignore failures for library files - return Ok(files); - } - return Err(eyre::eyre!( - "Failed to parse Solidity code for {}\nDebug info: {:?}", - path.display(), - err - )); - } - }; + // Solar uses a global SourceMap: span BytePos values are global + // offsets, not per-file offsets. Subtract file.start_pos so that + // span-based indexing into the per-file source string is correct. + let source = ast_source.file.src.to_string(); + let file_start = ast_source.file.start_pos.to_usize(); - // Visit the parse tree - let mut doc = Parser::new(comments, source, self.fmt.tab_width); - source_unit - .visit(&mut doc) - .map_err(|err| eyre::eyre!("Failed to parse source: {err}"))?; + // Walk the solar AST directly + let doc = Parser::new(source, file_start, self.fmt.tab_width); + let all_items = doc.parse(source_unit); // Split the parsed items on top-level constants and rest. - let (items, consts): (Vec, Vec) = doc - .items() + let (items, consts): (Vec, Vec) = all_items .into_iter() .partition(|item| !matches!(item.source, ParseSource::Variable(_))); diff --git a/crates/doc/src/helpers.rs b/crates/doc/src/helpers.rs index 77f10329a88b8..7b36e2e257b71 100644 --- a/crates/doc/src/helpers.rs +++ b/crates/doc/src/helpers.rs @@ -1,25 +1,5 @@ -use itertools::Itertools; -use solang_parser::pt::FunctionDefinition; use toml::{Value, value::Table}; -/// Generates a function signature with parameter types (e.g., "functionName(type1,type2)"). -/// Returns the function name without parameters if the function has no parameters. -pub fn function_signature(func: &FunctionDefinition) -> String { - let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.clone()); - if func.params.is_empty() { - return func_name; - } - - format!( - "{}({})", - func_name, - func.params - .iter() - .map(|p| p.1.as_ref().map(|p| p.ty.to_string()).unwrap_or_default()) - .join(",") - ) -} - /// Merge original toml table with the override. pub(crate) fn merge_toml_table(table: &mut Table, override_table: Table) { for (key, override_value) in override_table { @@ -46,74 +26,3 @@ pub(crate) fn merge_toml_table(table: &mut Table, override_table: Table) { }; } } - -#[cfg(test)] -mod tests { - use super::*; - use solang_parser::{ - parse, - pt::{ContractPart, SourceUnit, SourceUnitPart}, - }; - - #[test] - fn test_function_signature_no_params() { - let (source_unit, _) = parse( - r#" - contract Test { - function foo() public {} - } - "#, - 0, - ) - .unwrap(); - - let func = extract_function(&source_unit); - assert_eq!(function_signature(func), "foo"); - } - - #[test] - fn test_function_signature_with_params() { - let (source_unit, _) = parse( - r#" - contract Test { - function transfer(address to, uint256 amount) public {} - } - "#, - 0, - ) - .unwrap(); - - let func = extract_function(&source_unit); - assert_eq!(function_signature(func), "transfer(address,uint256)"); - } - - #[test] - fn test_function_signature_constructor() { - let (source_unit, _) = parse( - r#" - contract Test { - constructor(address owner) {} - } - "#, - 0, - ) - .unwrap(); - - let func = extract_function(&source_unit); - assert_eq!(function_signature(func), "constructor(address)"); - } - - /// Helper to extract the first function from a parsed source unit - fn extract_function(source_unit: &SourceUnit) -> &FunctionDefinition { - for part in &source_unit.0 { - if let SourceUnitPart::ContractDefinition(contract) = part { - for part in &contract.parts { - if let ContractPart::FunctionDefinition(func) = part { - return func; - } - } - } - } - panic!("No function found in source unit"); - } -} diff --git a/crates/doc/src/lib.rs b/crates/doc/src/lib.rs index 847d58dfb1d8b..e50598cff83ac 100644 --- a/crates/doc/src/lib.rs +++ b/crates/doc/src/lib.rs @@ -22,6 +22,10 @@ mod helpers; mod parser; pub use parser::{ Comment, CommentTag, Comments, CommentsRef, ParseItem, ParseSource, Parser, error, + source::{ + BaseInfo, ContractKind, ContractSource, EnumSource, ErrorSource, EventSource, + FunctionSource, ParamInfo, StructSource, TypeSource, VariableAttr, VariableSource, + }, }; mod preprocessor; @@ -31,6 +35,3 @@ mod writer; pub use writer::{AsDoc, AsDocResult, BufWriter, Markdown}; pub use mdbook_driver; - -// old formatter dependencies -pub mod solang_ext; diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 3e915b1b7f479..e70f47e174a81 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -1,6 +1,5 @@ use alloy_primitives::map::HashMap; use derive_more::{Deref, DerefMut, derive::Display}; -use solang_parser::doccomment::DocCommentTag; /// The natspec comment tag explaining the purpose of the comment. /// See: . @@ -70,10 +69,10 @@ impl Comment { Self { tag, value } } - /// Create new instance of [Comment] from [DocCommentTag] + /// Create new instance of [Comment] from a tag string and value, /// if it has a valid natspec tag. - pub fn from_doc_comment(value: DocCommentTag) -> Option { - CommentTag::from_str(&value.tag).map(|tag| Self { tag, value: value.value }) + pub fn from_tag_and_value(tag: &str, value: String) -> Option { + CommentTag::from_str(tag).map(|tag| Self { tag, value }) } /// Split the comment at first word. @@ -145,9 +144,70 @@ impl Comments { } } -impl From> for Comments { - fn from(value: Vec) -> Self { - Self(value.into_iter().filter_map(Comment::from_doc_comment).collect()) +impl Comments { + /// Parse natspec comments from raw doc comment lines. + /// + /// Each line should be the raw text content of a `///` or `/** */` doc comment + /// with the comment delimiters already stripped (as provided by solar's `DocComment::symbol`). + /// + /// Natspec tags start with `@` (e.g. `@notice`, `@dev`, `@param`). + /// Lines without a tag at the start are treated as continuations of the previous tag, + /// or as `@notice` if no previous tag exists. + pub fn from_doc_lines(lines: impl IntoIterator>) -> Self { + let mut comments = Vec::new(); + let mut current_tag: Option = None; + let mut current_value = String::new(); + + let flush = |tag: &Option, value: &str, out: &mut Vec| { + let value = value.trim(); + if value.is_empty() && tag.is_none() { + return; + } + let tag_str = tag.as_deref().unwrap_or("notice"); + // Filter out `@solidity` tags and empty tags + if tag_str.trim() == "solidity" || tag_str.trim().is_empty() { + return; + } + if let Some(c) = Comment::from_tag_and_value(tag_str, value.to_string()) { + out.push(c); + } + }; + + for raw_line in lines { + let raw = raw_line.as_ref(); + // For block comments, process each line individually + for line in raw.lines() { + let trimmed = line.trim().trim_start_matches('*').trim(); + + if let Some(rest) = trimmed.strip_prefix('@') { + // Flush previous + flush(¤t_tag, ¤t_value, &mut comments); + // Parse new tag + let (tag, value) = rest.split_once(char::is_whitespace).unwrap_or((rest, "")); + current_tag = Some(tag.to_string()); + current_value = value.trim().to_string(); + } else if !trimmed.is_empty() { + // Continuation of current tag + if current_value.is_empty() { + current_value = trimmed.to_string(); + } else { + current_value.push('\n'); + current_value.push_str(trimmed); + } + } + } + } + + // Flush last + flush(¤t_tag, ¤t_value, &mut comments); + + Self(comments) + } +} + +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) } } diff --git a/crates/doc/src/parser/item.rs b/crates/doc/src/parser/item.rs index b50c0ccae9f4e..cca36d47301c8 100644 --- a/crates/doc/src/parser/item.rs +++ b/crates/doc/src/parser/item.rs @@ -1,12 +1,12 @@ -use crate::{Comments, helpers::function_signature, solang_ext::SafeUnwrap}; -use solang_parser::pt::{ - ContractDefinition, ContractTy, EnumDefinition, ErrorDefinition, EventDefinition, - FunctionDefinition, StructDefinition, TypeDefinition, VariableDefinition, +use crate::Comments; + +pub use super::source::{ + ContractSource, EnumSource, ErrorSource, EventSource, FunctionSource, ParseSource, + StructSource, VariableSource, }; -use std::ops::Range; /// The parsed item. -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct ParseItem { /// The parse tree source. pub source: ParseSource, @@ -18,12 +18,12 @@ pub struct ParseItem { pub code: String, } -/// Defines a method that filters [ParseItem]'s children and returns the source pt token of the +/// Defines a method that filters [ParseItem]'s children and returns the source data of the /// children matching the target variant as well as its comments. /// Returns [Option::None] if no children matching the variant are found. macro_rules! filter_children_fn { ($vis:vis fn $name:ident(&self, $variant:ident) -> $ret:ty) => { - /// Filter children items for [ParseSource::$variant] variants. + /// Filter children items for matching variants. $vis fn $name(&self) -> Option> { let items = self.children.iter().filter_map(|item| match item.source { ParseSource::$variant(ref inner) => Some((inner, &item.comments, &item.code)), @@ -39,12 +39,11 @@ macro_rules! filter_children_fn { }; } -/// Defines a method that returns [ParseSource] inner element if it matches -/// the variant +/// Defines a method that returns the inner element if it matches the variant. macro_rules! as_inner_source { ($vis:vis fn $name:ident(&self, $variant:ident) -> $ret:ty) => { - /// Return inner element if it matches $variant. - /// If the element doesn't match, returns [None] + /// Return inner element if it matches the variant. + /// If the element doesn't match, returns [None]. $vis fn $name(&self) -> Option<&$ret> { match self.source { ParseSource::$variant(ref inner) => Some(inner), @@ -77,38 +76,16 @@ impl ParseItem { self } - /// Set the source code of this [ParseItem]. - /// - /// The parameter should be the full source file where this parse item originated from. - pub fn with_code(mut self, source: &str, tab_width: usize) -> Self { - let mut code = source[self.source.range()].to_string(); - - // Special function case, add `;` at the end of definition. - if let ParseSource::Function(_) | ParseSource::Error(_) | ParseSource::Event(_) = - self.source - { - code.push(';'); - } - - // Remove extra indent from source lines. - let prefix = &" ".repeat(tab_width); - self.code = code - .lines() - .map(|line| line.strip_prefix(prefix).unwrap_or(line)) - .collect::>() - .join("\n"); + /// Set the code string for this [ParseItem]. + pub fn with_code(mut self, code: String) -> Self { + self.code = code; self } /// Format the item's filename. pub fn filename(&self) -> String { - let prefix = match self.source { - ParseSource::Contract(ref c) => match c.ty { - ContractTy::Contract(_) => "contract", - ContractTy::Abstract(_) => "abstract", - ContractTy::Interface(_) => "interface", - ContractTy::Library(_) => "library", - }, + let prefix = match &self.source { + ParseSource::Contract(c) => c.kind.as_str(), ParseSource::Function(_) => "function", ParseSource::Variable(_) => "variable", ParseSource::Event(_) => "event", @@ -121,77 +98,14 @@ impl ParseItem { format!("{prefix}.{ident}.md") } - filter_children_fn!(pub fn variables(&self, Variable) -> VariableDefinition); - filter_children_fn!(pub fn functions(&self, Function) -> FunctionDefinition); - filter_children_fn!(pub fn events(&self, Event) -> EventDefinition); - filter_children_fn!(pub fn errors(&self, Error) -> ErrorDefinition); - filter_children_fn!(pub fn structs(&self, Struct) -> StructDefinition); - filter_children_fn!(pub fn enums(&self, Enum) -> EnumDefinition); + filter_children_fn!(pub fn variables(&self, Variable) -> VariableSource); + filter_children_fn!(pub fn functions(&self, Function) -> FunctionSource); + filter_children_fn!(pub fn events(&self, Event) -> EventSource); + filter_children_fn!(pub fn errors(&self, Error) -> ErrorSource); + filter_children_fn!(pub fn structs(&self, Struct) -> StructSource); + filter_children_fn!(pub fn enums(&self, Enum) -> EnumSource); - as_inner_source!(pub fn as_contract(&self, Contract) -> ContractDefinition); - as_inner_source!(pub fn as_variable(&self, Variable) -> VariableDefinition); - as_inner_source!(pub fn as_function(&self, Function) -> FunctionDefinition); -} - -/// A wrapper type around pt token. -#[derive(Clone, Debug, PartialEq, Eq)] -#[allow(clippy::large_enum_variant)] -pub enum ParseSource { - /// Source contract definition. - Contract(Box), - /// Source function definition. - Function(FunctionDefinition), - /// Source variable definition. - Variable(VariableDefinition), - /// Source event definition. - Event(EventDefinition), - /// Source error definition. - Error(ErrorDefinition), - /// Source struct definition. - Struct(StructDefinition), - /// Source enum definition. - Enum(EnumDefinition), - /// Source type definition. - Type(TypeDefinition), -} - -impl ParseSource { - /// Get the identity of the source - pub fn ident(&self) -> String { - match self { - Self::Contract(contract) => contract.name.safe_unwrap().name.clone(), - Self::Variable(var) => var.name.safe_unwrap().name.clone(), - Self::Event(event) => event.name.safe_unwrap().name.clone(), - Self::Error(error) => error.name.safe_unwrap().name.clone(), - Self::Struct(structure) => structure.name.safe_unwrap().name.clone(), - Self::Enum(enumerable) => enumerable.name.safe_unwrap().name.clone(), - Self::Function(func) => { - func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.clone()) - } - Self::Type(ty) => ty.name.name.clone(), - } - } - - /// Get the signature of the source (for functions, includes parameter types) - pub fn signature(&self) -> String { - match self { - Self::Function(func) => function_signature(func), - _ => self.ident(), - } - } - - /// Get the range of this item in the source file. - pub fn range(&self) -> Range { - match self { - Self::Contract(contract) => contract.loc, - Self::Variable(var) => var.loc, - Self::Event(event) => event.loc, - Self::Error(error) => error.loc, - Self::Struct(structure) => structure.loc, - Self::Enum(enumerable) => enumerable.loc, - Self::Function(func) => func.loc_prototype, - Self::Type(ty) => ty.loc, - } - .range() - } + as_inner_source!(pub fn as_contract(&self, Contract) -> ContractSource); + as_inner_source!(pub fn as_variable(&self, Variable) -> VariableSource); + as_inner_source!(pub fn as_function(&self, Function) -> FunctionSource); } diff --git a/crates/doc/src/parser/mod.rs b/crates/doc/src/parser/mod.rs index 76da5d032382d..124d2aa3ed576 100644 --- a/crates/doc/src/parser/mod.rs +++ b/crates/doc/src/parser/mod.rs @@ -1,19 +1,12 @@ //! The parser module. -use crate::solang_ext::{Visitable, Visitor}; -use itertools::Itertools; -use solang_parser::{ - doccomment::{DocComment, parse_doccomments}, - pt::{ - Comment as SolangComment, EnumDefinition, ErrorDefinition, EventDefinition, - FunctionDefinition, Identifier, Loc, SourceUnit, SourceUnitPart, StructDefinition, - TypeDefinition, VariableDefinition, - }, -}; +use solar::parse::ast; /// Parser error. pub mod error; -use error::{ParserError, ParserResult}; + +/// Owned source types. +pub mod source; /// Parser item. mod item; @@ -23,206 +16,274 @@ pub use item::{ParseItem, ParseSource}; mod comment; pub use comment::{Comment, CommentTag, Comments, CommentsRef}; -/// The documentation parser. This type implements a [Visitor] trait. +use source::*; + +/// The documentation parser. /// -/// While walking the parse tree, [Parser] will collect relevant source items and corresponding -/// doc comments. The resulting [ParseItem]s can be accessed by calling [Parser::items]. -#[derive(Debug, Default)] +/// Walks the solar AST and extracts [`ParseItem`]s with owned source data and doc comments. +#[derive(Debug)] pub struct Parser { - /// Initial comments from solang parser. - comments: Vec, - /// Parser context. - context: ParserContext, /// Parsed results. items: Vec, - /// Source file. + /// The source code string of the file being parsed. source: String, + /// The global byte offset of this file's first byte in solar's source map. + /// + /// Solar uses a global `SourceMap` where each file's `BytePos` values start at + /// `file.start_pos` rather than 0. All span `lo`/`hi` values must be offset by this + /// amount before indexing into `self.source`. + file_start: usize, /// Tab width used to format code. tab_width: usize, } -/// [Parser] context. -#[derive(Debug, Default)] -struct ParserContext { - /// Current visited parent. - parent: Option, - /// Current start pointer for parsing doc comments. - doc_start_loc: usize, -} - impl Parser { /// Create a new instance of [Parser]. - pub fn new(comments: Vec, source: String, tab_width: usize) -> Self { - Self { comments, source, tab_width, ..Default::default() } + /// + /// `file_start` is `ast_source.file.start_pos.to_usize()` — the offset of the file's + /// first byte in solar's global source map. Pass `0` in tests where you parse directly. + pub const fn new(source: String, file_start: usize, tab_width: usize) -> Self { + Self { items: Vec::new(), source, file_start, tab_width } } - /// Return the parsed items. Consumes the parser. - pub fn items(self) -> Vec { + /// Parse a solar source unit and return the parsed items. + pub fn parse(mut self, source_unit: &ast::SourceUnit<'_>) -> Vec { + for item in source_unit.items.iter() { + if let Some(parsed) = self.parse_item(item) { + self.items.push(parsed); + } + } self.items } - /// Visit the children elements with parent context. - /// This function memoizes the previous parent, sets the context - /// to a new one and invokes a visit function. The context will be reset - /// to the previous parent at the end of the function. - fn with_parent( - &mut self, - mut parent: ParseItem, - mut visit: impl FnMut(&mut Self) -> ParserResult<()>, - ) -> ParserResult { - let curr = self.context.parent.take(); - self.context.parent = Some(parent); - visit(self)?; - parent = self.context.parent.take().unwrap(); - self.context.parent = curr; - Ok(parent) - } - - /// Adds a child element to the parent item if it exists. - /// Otherwise the element will be added to a top-level items collection. - /// Moves the doc comment pointer to the end location of the child element. - fn add_element_to_parent(&mut self, source: ParseSource, loc: Loc) -> ParserResult<()> { - let child = self.new_item(source, loc.start())?; - if let Some(parent) = self.context.parent.as_mut() { - parent.children.push(child); - } else { - self.items.push(child); + /// Parse a single solar AST item into a [ParseItem]. + fn parse_item(&self, item: &ast::Item<'_>) -> Option { + let docs = Self::parse_docs(&item.docs); + let span = item.span; + + match &item.kind { + ast::ItemKind::Contract(contract) => { + let source = self.parse_contract(contract); + let code = self.extract_code(span); + let mut parse_item = + ParseItem::new(ParseSource::Contract(source)).with_comments(docs); + + // Parse children + let mut children = Vec::new(); + for child in contract.body.iter() { + if let Some(parsed) = self.parse_item(child) { + children.push(parsed); + } + } + parse_item.children = children; + parse_item.code = code; + Some(parse_item) + } + ast::ItemKind::Function(func) => { + let source = self.parse_function(func); + let code = self.extract_prototype_code(func); + Some( + ParseItem::new(ParseSource::Function(source)) + .with_comments(docs) + .with_code(code), + ) + } + ast::ItemKind::Variable(var) => { + let source = self.parse_variable(var); + let code = self.extract_code(span); + Some( + ParseItem::new(ParseSource::Variable(source)) + .with_comments(docs) + .with_code(code), + ) + } + ast::ItemKind::Event(event) => { + let source = self.parse_event(event); + let code = self.extract_code(span); + Some(ParseItem::new(ParseSource::Event(source)).with_comments(docs).with_code(code)) + } + ast::ItemKind::Error(err) => { + let source = self.parse_error(err); + let code = self.extract_code(span); + Some(ParseItem::new(ParseSource::Error(source)).with_comments(docs).with_code(code)) + } + ast::ItemKind::Struct(strukt) => { + let source = self.parse_struct(strukt); + let code = self.extract_code(span); + Some( + ParseItem::new(ParseSource::Struct(source)).with_comments(docs).with_code(code), + ) + } + ast::ItemKind::Enum(enm) => { + let source = self.parse_enum(enm); + let code = self.extract_code(span); + Some(ParseItem::new(ParseSource::Enum(source)).with_comments(docs).with_code(code)) + } + ast::ItemKind::Udvt(udvt) => { + let source = TypeSource { name: udvt.name.to_string() }; + let code = self.extract_code(span); + Some(ParseItem::new(ParseSource::Type(source)).with_comments(docs).with_code(code)) + } + // Skip pragmas, imports, using directives + _ => None, } - self.context.doc_start_loc = loc.end(); - Ok(()) } - /// Create new [ParseItem] with comments and formatted code. - fn new_item(&mut self, source: ParseSource, loc_start: usize) -> ParserResult { - let docs = self.parse_docs(loc_start)?; - Ok(ParseItem::new(source).with_comments(docs).with_code(&self.source, self.tab_width)) + fn parse_contract(&self, contract: &ast::ItemContract<'_>) -> ContractSource { + let kind = match contract.kind { + ast::ContractKind::Contract => ContractKind::Contract, + ast::ContractKind::AbstractContract => ContractKind::Abstract, + ast::ContractKind::Interface => ContractKind::Interface, + ast::ContractKind::Library => ContractKind::Library, + }; + + let bases = contract + .bases + .iter() + .map(|base| { + let full_name = base.name.to_string(); + let ident = base.name.last().name.to_string(); + BaseInfo { name: full_name, ident } + }) + .collect(); + + ContractSource { name: contract.name.to_string(), kind, bases } } - /// Parse the doc comments from the current start location. - fn parse_docs(&mut self, end: usize) -> ParserResult { - self.parse_docs_range(self.context.doc_start_loc, end) + fn parse_function(&self, func: &ast::ItemFunction<'_>) -> FunctionSource { + let name = func.header.name.map(|n| n.to_string()); + let kind = func.kind.to_string(); + let params = self.parse_var_defs(&func.header.parameters); + let returns = + func.header.returns.as_deref().map(|r| self.parse_var_defs(r)).unwrap_or_default(); + FunctionSource { name, kind, params, returns } } - /// Parse doc comments from the within specified range. - fn parse_docs_range(&mut self, start: usize, end: usize) -> ParserResult { - let mut res = vec![]; - for comment in parse_doccomments(&self.comments, start, end) { - match comment { - DocComment::Line { comment } => res.push(comment), - DocComment::Block { comments } => res.extend(comments), + fn parse_variable(&self, var: &ast::VariableDefinition<'_>) -> VariableSource { + let name = var.name.map(|n| n.to_string()).unwrap_or_default(); + + let mut attrs = Vec::new(); + if let Some(m) = var.mutability { + match m { + ast::VarMut::Constant => attrs.push(VariableAttr::Constant), + ast::VarMut::Immutable => attrs.push(VariableAttr::Immutable), } } - // Filter out `@solidity` and empty tags - // See https://docs.soliditylang.org/en/v0.8.17/assembly.html#memory-safety - let res = res - .into_iter() - .filter(|c| c.tag.trim() != "solidity" && !c.tag.trim().is_empty()) - .collect_vec(); - Ok(res.into()) + VariableSource { name, attrs } } -} -impl Visitor for Parser { - type Error = ParserError; - - fn visit_source_unit(&mut self, source_unit: &mut SourceUnit) -> ParserResult<()> { - for source in &mut source_unit.0 { - match source { - SourceUnitPart::ContractDefinition(def) => { - // Create new contract parse item. - let contract = - self.new_item(ParseSource::Contract(def.clone()), def.loc.start())?; - - // Move the doc pointer to the contract location start. - self.context.doc_start_loc = def.loc.start(); - - // Parse child elements with current contract as parent - let contract = self.with_parent(contract, |doc| { - def.parts - .iter_mut() - .map(|d| d.visit(doc)) - .collect::>>()?; - Ok(()) - })?; - - // Move the doc pointer to the contract location end. - self.context.doc_start_loc = def.loc.end(); - - // Add contract to the parsed items. - self.items.push(contract); - } - SourceUnitPart::FunctionDefinition(func) => self.visit_function(func)?, - SourceUnitPart::EventDefinition(event) => self.visit_event(event)?, - SourceUnitPart::ErrorDefinition(error) => self.visit_error(error)?, - SourceUnitPart::StructDefinition(structure) => self.visit_struct(structure)?, - SourceUnitPart::EnumDefinition(enumerable) => self.visit_enum(enumerable)?, - SourceUnitPart::VariableDefinition(var) => self.visit_var_definition(var)?, - SourceUnitPart::TypeDefinition(ty) => self.visit_type_definition(ty)?, - _ => {} - }; - } + fn parse_event(&self, event: &ast::ItemEvent<'_>) -> EventSource { + let fields = self.parse_var_defs(&event.parameters); + EventSource { name: event.name.to_string(), fields } + } - Ok(()) + fn parse_error(&self, err: &ast::ItemError<'_>) -> ErrorSource { + let fields = self.parse_var_defs(&err.parameters); + ErrorSource { name: err.name.to_string(), fields } } - fn visit_enum(&mut self, enumerable: &mut EnumDefinition) -> ParserResult<()> { - self.add_element_to_parent(ParseSource::Enum(enumerable.clone()), enumerable.loc) + fn parse_struct(&self, strukt: &ast::ItemStruct<'_>) -> StructSource { + let fields = self.parse_var_defs(strukt.fields); + StructSource { name: strukt.name.to_string(), fields } } - fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> ParserResult<()> { - self.add_element_to_parent(ParseSource::Variable(var.clone()), var.loc) + /// Parse a list of variable definitions into [ParamInfo]. + fn parse_var_defs(&self, vars: &[ast::VariableDefinition<'_>]) -> Vec { + vars.iter() + .map(|v| ParamInfo { name: v.name.map(|n| n.to_string()), ty: self.type_string(&v.ty) }) + .collect() } - fn visit_function(&mut self, func: &mut FunctionDefinition) -> ParserResult<()> { - // If the function parameter doesn't have a name, try to set it with - // `@custom:name` tag if any was provided - let mut start_loc = func.loc.start(); - for (loc, param) in &mut func.params { - if let Some(param) = param - && param.name.is_none() - { - let docs = self.parse_docs_range(start_loc, loc.end())?; - let name_tag = docs.iter().find(|c| c.tag == CommentTag::Custom("name".to_owned())); - if let Some(name_tag) = name_tag - && let Some(name) = name_tag.value.trim().split(' ').next() - { - param.name = Some(Identifier { loc: Loc::Implicit, name: name.to_owned() }) - } - } - start_loc = loc.end(); + /// Extract the type as a string from the source code. + fn type_string(&self, ty: &ast::Type<'_>) -> String { + let lo = ty.span.lo().to_usize().saturating_sub(self.file_start); + let hi = ty.span.hi().to_usize().saturating_sub(self.file_start); + if lo < self.source.len() && hi <= self.source.len() && lo < hi { + self.source[lo..hi].to_string() + } else { + String::new() } + } - self.add_element_to_parent(ParseSource::Function(func.clone()), func.loc) + fn parse_enum(&self, enm: &ast::ItemEnum<'_>) -> EnumSource { + let variants = enm.variants.iter().map(|v| v.to_string()).collect(); + EnumSource { name: enm.name.to_string(), variants } } - fn visit_struct(&mut self, structure: &mut StructDefinition) -> ParserResult<()> { - self.add_element_to_parent(ParseSource::Struct(structure.clone()), structure.loc) + /// Parse doc comments from solar's [ast::DocComments] into our [Comments] type. + fn parse_docs(docs: &ast::DocComments<'_>) -> Comments { + if docs.is_empty() { + return Comments::default(); + } + Comments::from_doc_lines(docs.iter().map(|d| d.symbol.as_str())) } - fn visit_event(&mut self, event: &mut EventDefinition) -> ParserResult<()> { - self.add_element_to_parent(ParseSource::Event(event.clone()), event.loc) + /// Extract a code snippet from the source for the given span. + fn extract_code(&self, span: ast::Span) -> String { + let lo = span.lo().to_usize().saturating_sub(self.file_start); + let hi = span.hi().to_usize().saturating_sub(self.file_start); + if lo < self.source.len() && hi <= self.source.len() && lo < hi { + let code = &self.source[lo..hi]; + self.dedent(code) + } else { + String::new() + } } - fn visit_error(&mut self, error: &mut ErrorDefinition) -> ParserResult<()> { - self.add_element_to_parent(ParseSource::Error(error.clone()), error.loc) + /// Extract only the function prototype (excluding the body) from the source. + fn extract_prototype_code(&self, func: &ast::ItemFunction<'_>) -> String { + let lo = func.header.span.lo().to_usize().saturating_sub(self.file_start); + let hi = func.header.span.hi().to_usize().saturating_sub(self.file_start); + if lo < self.source.len() && hi <= self.source.len() && lo < hi { + let mut code = self.source[lo..hi].to_string(); + code.push(';'); + self.dedent(&code) + } else { + String::new() + } } - fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> ParserResult<()> { - self.add_element_to_parent(ParseSource::Type(def.clone()), def.loc) + /// Remove one level of indentation from code. + fn dedent(&self, code: &str) -> String { + let prefix = &" ".repeat(self.tab_width); + code.lines() + .map(|line| line.strip_prefix(prefix).unwrap_or(line)) + .collect::>() + .join("\n") } } #[cfg(test)] mod tests { use super::*; - use solang_parser::parse; fn parse_source(src: &str) -> Vec { - let (mut source, comments) = parse(src, 0).expect("failed to parse source"); - let mut doc = Parser::new(comments, src.to_owned(), 4); - source.visit(&mut doc).expect("failed to visit source"); - doc.items() + use solar::parse::{ + Parser as SolarParser, + ast::{Arena, interface}, + interface::Session, + }; + + let sess = + Session::builder().with_silent_emitter(Some("test parse failed".to_string())).build(); + + sess.enter(|| -> Vec { + let arena = Arena::new(); + let mut parser = SolarParser::from_source_code( + &sess, + &arena, + interface::source_map::FileName::Custom("test".to_string()), + src.to_string(), + ) + .expect("failed to create parser"); + + let source_unit = parser.parse_file().map_err(|e| e.emit()).expect("failed to parse"); + + // file_start=0: when parsing directly, solar's BytePos values start at 0. + let doc = Parser::new(src.to_string(), 0, 4); + doc.parse(&source_unit) + }) } macro_rules! test_single_unit { @@ -339,23 +400,39 @@ mod tests { assert!(matches!(fallback.source, ParseSource::Function(_))); } + #[test] + fn overloaded_function_signatures() { + let items = parse_source( + r" + interface IFoo { + function process(address addr) external; + function process(address[] calldata addrs) external; + function process(address addr, uint256 value) external; + } + ", + ); + assert_eq!(items.len(), 1); + let contract = items.first().unwrap(); + assert_eq!(contract.children.len(), 3); + let sigs: Vec = contract.children.iter().map(|ch| ch.source.signature()).collect(); + assert_eq!(sigs[0], "process(address)", "first overload"); + assert_eq!(sigs[1], "process(address[])", "second overload (array)"); + assert_eq!(sigs[2], "process(address,uint256)", "third overload"); + } + #[test] fn contract_with_doc_comments() { let items = parse_source( r" pragma solidity ^0.8.19; - /// @name Test - /// no tag - ///@notice Cool contract - /// @ dev This is not a dev tag + /// @notice Cool contract /** * @dev line one * line 2 */ contract Test { - /** my function - i like whitespace - */ + /// my function + /// i like whitespace function test() {} } ", diff --git a/crates/doc/src/parser/source.rs b/crates/doc/src/parser/source.rs new file mode 100644 index 0000000000000..5a2fbddf43d5a --- /dev/null +++ b/crates/doc/src/parser/source.rs @@ -0,0 +1,172 @@ +//! Owned source types extracted from the parse tree for documentation generation. +//! +//! These types hold only the data needed by the doc writer and preprocessors, +//! avoiding lifetime dependencies on the parser's arena-allocated AST. + +/// Information about a parameter, struct field, event/error parameter, etc. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParamInfo { + /// The parameter name, if any. + pub name: Option, + /// The type rendered as a string (e.g. `"uint256"`, `"address"`). + pub ty: String, +} + +/// A base contract reference (e.g. `is IERC721, Ownable`). +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BaseInfo { + /// The full dotted name (e.g. `"IERC721"` or `"SomeLib.Base"`). + pub name: String, + /// The last identifier in the path, used for linking. + pub ident: String, +} + +/// The kind of a contract-like definition. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ContractKind { + Contract, + Abstract, + Interface, + Library, +} + +impl ContractKind { + /// Returns the lowercase keyword string. + pub const fn as_str(&self) -> &'static str { + match self { + Self::Contract => "contract", + Self::Abstract => "abstract", + Self::Interface => "interface", + Self::Library => "library", + } + } +} + +/// Owned contract definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ContractSource { + pub name: String, + pub kind: ContractKind, + pub bases: Vec, +} + +/// Owned function definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FunctionSource { + /// The function name, or `None` for unnamed functions (fallback/receive). + pub name: Option, + /// The function kind as a display string (e.g. `"function"`, `"constructor"`, `"fallback"`). + pub kind: String, + /// Function parameters. + pub params: Vec, + /// Return parameters. + pub returns: Vec, +} + +impl FunctionSource { + /// Get the signature of the function, including parameter types. + pub fn signature(&self) -> String { + let name = self.name.as_deref().unwrap_or(&self.kind); + if self.params.is_empty() { + return name.to_string(); + } + format!( + "{}({})", + name, + self.params.iter().map(|p| p.ty.as_str()).collect::>().join(",") + ) + } +} + +/// Variable attribute relevant for doc generation. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum VariableAttr { + Constant, + Immutable, +} + +/// Owned variable definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VariableSource { + pub name: String, + pub attrs: Vec, +} + +/// Owned event definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EventSource { + pub name: String, + pub fields: Vec, +} + +/// Owned error definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ErrorSource { + pub name: String, + pub fields: Vec, +} + +/// Owned struct definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StructSource { + pub name: String, + pub fields: Vec, +} + +/// Owned enum definition data. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EnumSource { + pub name: String, + pub variants: Vec, +} + +/// Owned type definition data (user-defined value types). +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TypeSource { + pub name: String, +} + +/// A wrapper type around owned source data extracted from the parse tree. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ParseSource { + /// Source contract definition. + Contract(ContractSource), + /// Source function definition. + Function(FunctionSource), + /// Source variable definition. + Variable(VariableSource), + /// Source event definition. + Event(EventSource), + /// Source error definition. + Error(ErrorSource), + /// Source struct definition. + Struct(StructSource), + /// Source enum definition. + Enum(EnumSource), + /// Source type definition. + Type(TypeSource), +} + +impl ParseSource { + /// Get the identity of the source. + pub fn ident(&self) -> String { + match self { + Self::Contract(c) => c.name.clone(), + Self::Variable(v) => v.name.clone(), + Self::Event(e) => e.name.clone(), + Self::Error(e) => e.name.clone(), + Self::Struct(s) => s.name.clone(), + Self::Enum(e) => e.name.clone(), + Self::Function(f) => f.name.clone().unwrap_or_else(|| f.kind.clone()), + Self::Type(t) => t.name.clone(), + } + } + + /// Get the signature of the source (for functions, includes parameter types). + pub fn signature(&self) -> String { + match self { + Self::Function(f) => f.signature(), + _ => self.ident(), + } + } +} diff --git a/crates/doc/src/preprocessor/contract_inheritance.rs b/crates/doc/src/preprocessor/contract_inheritance.rs index 1c2159131b01a..5df27c12a4e72 100644 --- a/crates/doc/src/preprocessor/contract_inheritance.rs +++ b/crates/doc/src/preprocessor/contract_inheritance.rs @@ -1,7 +1,5 @@ use super::{Preprocessor, PreprocessorId}; -use crate::{ - Document, ParseSource, PreprocessorOutput, document::DocumentContent, solang_ext::SafeUnwrap, -}; +use crate::{Document, ParseSource, PreprocessorOutput, document::DocumentContent}; use alloy_primitives::map::HashMap; use std::path::PathBuf; @@ -11,7 +9,7 @@ pub const CONTRACT_INHERITANCE_ID: PreprocessorId = PreprocessorId("contract_inh /// The contract inheritance preprocessor. /// /// It matches the documents with inner [`ParseSource::Contract`](crate::ParseSource) elements, -/// iterates over their [Base](solang_parser::pt::Base)s and attempts +/// iterates over their base contracts and attempts /// to link them with the paths of the other contract documents. /// /// This preprocessor writes to [Document]'s context. @@ -34,8 +32,8 @@ impl Preprocessor for ContractInheritance { let mut links = HashMap::default(); // Attempt to match bases to other contracts - for base in &contract.base { - let base_ident = base.name.identifiers.last().unwrap().name.clone(); + for base in &contract.bases { + let base_ident = base.ident.clone(); if let Some(linked) = self.try_link_base(&base_ident, &documents) { links.insert(base_ident, linked); } @@ -60,7 +58,7 @@ impl ContractInheritance { } if let DocumentContent::Single(ref item) = candidate.content && let ParseSource::Contract(ref contract) = item.source - && base == contract.name.safe_unwrap().name + && base == contract.name { return Some(candidate.relative_output_path().to_path_buf()); } diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index 925a7cc8a2259..6fdda0e40d290 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -1,5 +1,5 @@ use super::{Preprocessor, PreprocessorId}; -use crate::{Comments, Document, ParseItem, ParseSource, solang_ext::SafeUnwrap}; +use crate::{Comments, Document, ParseItem, ParseSource}; use regex::{Captures, Match, Regex}; use std::{ borrow::Cow, @@ -84,7 +84,7 @@ impl InferInlineHyperlinks { for item in items { match &item.source { ParseSource::Contract(contract) => { - let name = &contract.name.safe_unwrap().name; + let name = &contract.name; if name == link.identifier { if link.part.is_none() { return Some(InlineLinkTarget::borrowed( @@ -102,11 +102,8 @@ impl InferInlineHyperlinks { // have so we can match the correct one if let Some(id) = &fun.name { // Note: constructors don't have a name - if id.name == link.ref_name() { - return Some(InlineLinkTarget::borrowed( - &id.name, - target_path.to_path_buf(), - )); + if id == link.ref_name() { + return Some(InlineLinkTarget::borrowed(id, target_path.to_path_buf())); } } else if link.ref_name() == "constructor" { return Some(InlineLinkTarget::borrowed( @@ -117,7 +114,7 @@ impl InferInlineHyperlinks { } ParseSource::Variable(_) => {} ParseSource::Event(ev) => { - let ev_name = &ev.name.safe_unwrap().name; + let ev_name = &ev.name; if ev_name == link.ref_name() { return Some(InlineLinkTarget::borrowed( ev_name, @@ -126,7 +123,7 @@ impl InferInlineHyperlinks { } } ParseSource::Error(err) => { - let err_name = &err.name.safe_unwrap().name; + let err_name = &err.name; if err_name == link.ref_name() { return Some(InlineLinkTarget::borrowed( err_name, @@ -135,7 +132,7 @@ impl InferInlineHyperlinks { } } ParseSource::Struct(structdef) => { - let struct_name = &structdef.name.safe_unwrap().name; + let struct_name = &structdef.name; if struct_name == link.ref_name() { return Some(InlineLinkTarget::borrowed( struct_name, diff --git a/crates/doc/src/preprocessor/inheritdoc.rs b/crates/doc/src/preprocessor/inheritdoc.rs index cc18bdfbd83fb..15d190f83d49c 100644 --- a/crates/doc/src/preprocessor/inheritdoc.rs +++ b/crates/doc/src/preprocessor/inheritdoc.rs @@ -1,7 +1,6 @@ use super::{Preprocessor, PreprocessorId}; use crate::{ Comments, Document, ParseItem, ParseSource, PreprocessorOutput, document::DocumentContent, - solang_ext::SafeUnwrap, }; use alloy_primitives::map::HashMap; @@ -72,7 +71,7 @@ impl Inheritdoc { for candidate in documents { if let DocumentContent::Single(ref item) = candidate.content && let ParseSource::Contract(ref contract) = item.source - && base == contract.name.safe_unwrap().name + && base == contract.name { // Not matched for the contract because it's a noop // https://docs.soliditylang.org/en/v0.8.17/natspec-format.html#tags diff --git a/crates/doc/src/solang_ext/ast_eq.rs b/crates/doc/src/solang_ext/ast_eq.rs deleted file mode 100644 index 1252ac4eb32bf..0000000000000 --- a/crates/doc/src/solang_ext/ast_eq.rs +++ /dev/null @@ -1,708 +0,0 @@ -use alloy_primitives::{Address, I256, U256}; -use solang_parser::pt::*; -use std::str::FromStr; - -/// Helper to convert a string number into a comparable one -fn to_num(string: &str) -> I256 { - if string.is_empty() { - return I256::ZERO; - } - string.replace('_', "").trim().parse().unwrap() -} - -/// Helper to convert the fractional part of a number into a comparable one. -/// This will reverse the number so that 0's can be ignored -fn to_num_reversed(string: &str) -> U256 { - if string.is_empty() { - return U256::from(0); - } - string.replace('_', "").trim().chars().rev().collect::().parse().unwrap() -} - -/// Helper to filter [ParameterList] to omit empty -/// parameters -fn filter_params(list: &ParameterList) -> ParameterList { - list.iter().filter(|(_, param)| param.is_some()).cloned().collect::>() -} - -/// Check if two ParseTrees are equal ignoring location information or ordering if ordering does -/// not matter -pub trait AstEq { - fn ast_eq(&self, other: &Self) -> bool; -} - -impl AstEq for Loc { - fn ast_eq(&self, _other: &Self) -> bool { - true - } -} - -impl AstEq for IdentifierPath { - fn ast_eq(&self, other: &Self) -> bool { - self.identifiers.ast_eq(&other.identifiers) - } -} - -impl AstEq for SourceUnit { - fn ast_eq(&self, other: &Self) -> bool { - self.0.ast_eq(&other.0) - } -} - -impl AstEq for VariableDefinition { - fn ast_eq(&self, other: &Self) -> bool { - let sorted_attrs = |def: &Self| { - let mut attrs = def.attrs.clone(); - attrs.sort(); - attrs - }; - self.ty.ast_eq(&other.ty) - && self.name.ast_eq(&other.name) - && self.initializer.ast_eq(&other.initializer) - && sorted_attrs(self).ast_eq(&sorted_attrs(other)) - } -} - -impl AstEq for FunctionDefinition { - fn ast_eq(&self, other: &Self) -> bool { - // attributes - let sorted_attrs = |def: &Self| { - let mut attrs = def.attributes.clone(); - attrs.sort(); - attrs - }; - - // params - let left_params = filter_params(&self.params); - let right_params = filter_params(&other.params); - let left_returns = filter_params(&self.returns); - let right_returns = filter_params(&other.returns); - - self.ty.ast_eq(&other.ty) - && self.name.ast_eq(&other.name) - && left_params.ast_eq(&right_params) - && self.return_not_returns.ast_eq(&other.return_not_returns) - && left_returns.ast_eq(&right_returns) - && self.body.ast_eq(&other.body) - && sorted_attrs(self).ast_eq(&sorted_attrs(other)) - } -} - -impl AstEq for Base { - fn ast_eq(&self, other: &Self) -> bool { - self.name.ast_eq(&other.name) - && self.args.clone().unwrap_or_default().ast_eq(&other.args.clone().unwrap_or_default()) - } -} - -impl AstEq for Vec -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - if self.len() == other.len() { - self.iter().zip(other.iter()).all(|(left, right)| left.ast_eq(right)) - } else { - false - } - } -} - -impl AstEq for Option -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - match (self, other) { - (Some(left), Some(right)) => left.ast_eq(right), - (None, None) => true, - _ => false, - } - } -} - -impl AstEq for Box -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - T::ast_eq(self, other) - } -} - -impl AstEq for () { - fn ast_eq(&self, _other: &Self) -> bool { - true - } -} - -impl AstEq for &T -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - T::ast_eq(self, other) - } -} - -impl AstEq for String { - fn ast_eq(&self, other: &Self) -> bool { - match (Address::from_str(self), Address::from_str(other)) { - (Ok(left), Ok(right)) => left == right, - _ => self == other, - } - } -} - -macro_rules! ast_eq_field { - (#[ast_eq_use($convert_func:ident)] $field:ident) => { - $convert_func($field) - }; - ($field:ident) => { - $field - }; -} - -macro_rules! gen_ast_eq_enum { - ($self:expr, $other:expr, $name:ident { - $($unit_variant:ident),* $(,)? - _ - $($tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? )),* $(,)? - _ - $($struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? }),* $(,)? - }) => { - match $self { - $($name::$unit_variant => gen_ast_eq_enum!($other, $name, $unit_variant),)* - $($name::$tuple_variant($($tuple_field),*) => - gen_ast_eq_enum!($other, $name, $tuple_variant ($($(#[ast_eq_use($tuple_convert_func)])? $tuple_field),*)),)* - $($name::$struct_variant { $($struct_field),* } => - gen_ast_eq_enum!($other, $name, $struct_variant {$($(#[ast_eq_use($struct_convert_func)])? $struct_field),*}),)* - } - }; - ($other:expr, $name:ident, $unit_variant:ident) => { - { - matches!($other, $name::$unit_variant) - } - }; - ($other:expr, $name:ident, $tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? ) ) => { - { - let left = ($(ast_eq_field!($(#[ast_eq_use($tuple_convert_func)])? $tuple_field)),*); - if let $name::$tuple_variant($($tuple_field),*) = $other { - let right = ($(ast_eq_field!($(#[ast_eq_use($tuple_convert_func)])? $tuple_field)),*); - left.ast_eq(&right) - } else { - false - } - } - }; - ($other:expr, $name:ident, $struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? } ) => { - { - let left = ($(ast_eq_field!($(#[ast_eq_use($struct_convert_func)])? $struct_field)),*); - if let $name::$struct_variant { $($struct_field),* } = $other { - let right = ($(ast_eq_field!($(#[ast_eq_use($struct_convert_func)])? $struct_field)),*); - left.ast_eq(&right) - } else { - false - } - } - }; -} - -macro_rules! wrap_in_box { - ($stmt:expr, $loc:expr) => { - if !matches!(**$stmt, Statement::Block { .. }) { - Box::new(Statement::Block { - loc: $loc, - unchecked: false, - statements: vec![*$stmt.clone()], - }) - } else { - $stmt.clone() - } - }; -} - -impl AstEq for Statement { - fn ast_eq(&self, other: &Self) -> bool { - match self { - Self::If(loc, expr, stmt1, stmt2) => { - #[expect(clippy::borrowed_box)] - let wrap_if = |stmt1: &Box, stmt2: &Option>| { - ( - wrap_in_box!(stmt1, *loc), - stmt2.as_ref().map(|stmt2| { - if matches!(**stmt2, Self::If(..)) { - stmt2.clone() - } else { - wrap_in_box!(stmt2, *loc) - } - }), - ) - }; - let (stmt1, stmt2) = wrap_if(stmt1, stmt2); - let left = (loc, expr, &stmt1, &stmt2); - 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) - } else { - false - } - } - Self::While(loc, expr, stmt1) => { - let stmt1 = wrap_in_box!(stmt1, *loc); - let left = (loc, expr, &stmt1); - if let Self::While(loc, expr, stmt1) = other { - let stmt1 = wrap_in_box!(stmt1, *loc); - let right = (loc, expr, &stmt1); - left.ast_eq(&right) - } else { - false - } - } - Self::DoWhile(loc, stmt1, expr) => { - let stmt1 = wrap_in_box!(stmt1, *loc); - let left = (loc, &stmt1, expr); - if let Self::DoWhile(loc, stmt1, expr) = other { - let stmt1 = wrap_in_box!(stmt1, *loc); - let right = (loc, &stmt1, expr); - left.ast_eq(&right) - } else { - false - } - } - 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 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) - } else { - false - } - } - 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 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); - left.ast_eq(&right) - } else { - false - } - } - _ => gen_ast_eq_enum!(self, other, Statement { - _ - Args(loc, args), - Expression(loc, expr), - VariableDefinition(loc, decl, expr), - Continue(loc, ), - Break(loc, ), - Return(loc, expr), - Revert(loc, expr, expr2), - RevertNamedArgs(loc, expr, args), - Emit(loc, expr), - // provide overridden variants regardless - If(loc, expr, stmt1, stmt2), - While(loc, expr, stmt1), - DoWhile(loc, stmt1, expr), - For(loc, stmt1, expr, stmt2, stmt3), - Try(loc, expr, params, clause), - Error(loc) - _ - Block { - loc, - unchecked, - statements, - }, - Assembly { - loc, - dialect, - block, - flags, - }, - }), - } - } -} - -macro_rules! derive_ast_eq { - ($name:ident) => { - impl AstEq for $name { - fn ast_eq(&self, other: &Self) -> bool { - self == other - } - } - }; - (($($index:tt $gen:tt),*)) => { - impl < $( $gen ),* > AstEq for ($($gen,)*) where $($gen: AstEq),* { - fn ast_eq(&self, other: &Self) -> bool { - $( - if !self.$index.ast_eq(&other.$index) { - return false - } - )* - true - } - } - }; - (struct $name:ident { $($field:ident),* $(,)? }) => { - impl AstEq for $name { - fn ast_eq(&self, other: &Self) -> bool { - let $name { $($field),* } = self; - let left = ($($field),*); - let $name { $($field),* } = other; - let right = ($($field),*); - left.ast_eq(&right) - } - } - }; - (enum $name:ident { - $($unit_variant:ident),* $(,)? - _ - $($tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? )),* $(,)? - _ - $($struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? }),* $(,)? - }) => { - impl AstEq for $name { - fn ast_eq(&self, other: &Self) -> bool { - gen_ast_eq_enum!(self, other, $name { - $($unit_variant),* - _ - $($tuple_variant ( $($(#[ast_eq_use($tuple_convert_func)])? $tuple_field),* )),* - _ - $($struct_variant { $($(#[ast_eq_use($struct_convert_func)])? $struct_field),* }),* - }) - } - } - } -} - -derive_ast_eq! { (0 A) } -derive_ast_eq! { (0 A, 1 B) } -derive_ast_eq! { (0 A, 1 B, 2 C) } -derive_ast_eq! { (0 A, 1 B, 2 C, 3 D) } -derive_ast_eq! { (0 A, 1 B, 2 C, 3 D, 4 E) } -derive_ast_eq! { (0 A, 1 B, 2 C, 3 D, 4 E, 5 F) } -derive_ast_eq! { (0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G) } -derive_ast_eq! { bool } -derive_ast_eq! { u8 } -derive_ast_eq! { u16 } -derive_ast_eq! { I256 } -derive_ast_eq! { U256 } -derive_ast_eq! { struct Identifier { loc, name } } -derive_ast_eq! { struct HexLiteral { loc, hex } } -derive_ast_eq! { struct StringLiteral { loc, unicode, string } } -derive_ast_eq! { struct Parameter { loc, annotation, ty, storage, name } } -derive_ast_eq! { struct NamedArgument { loc, name, expr } } -derive_ast_eq! { struct YulBlock { loc, statements } } -derive_ast_eq! { struct YulFunctionCall { loc, id, arguments } } -derive_ast_eq! { struct YulFunctionDefinition { loc, id, params, returns, body } } -derive_ast_eq! { struct YulSwitch { loc, condition, cases, default } } -derive_ast_eq! { struct YulFor { - loc, - init_block, - condition, - post_block, - execution_block, -}} -derive_ast_eq! { struct YulTypedIdentifier { loc, id, ty } } -derive_ast_eq! { struct VariableDeclaration { loc, ty, storage, name } } -derive_ast_eq! { struct Using { loc, list, ty, global } } -derive_ast_eq! { struct UsingFunction { loc, path, oper } } -derive_ast_eq! { struct TypeDefinition { loc, name, ty } } -derive_ast_eq! { struct ContractDefinition { loc, ty, name, base, layout, parts } } -derive_ast_eq! { struct EventParameter { loc, ty, indexed, name } } -derive_ast_eq! { struct ErrorParameter { loc, ty, name } } -derive_ast_eq! { struct EventDefinition { loc, name, fields, anonymous } } -derive_ast_eq! { struct ErrorDefinition { loc, keyword, name, fields } } -derive_ast_eq! { struct StructDefinition { loc, name, fields } } -derive_ast_eq! { struct EnumDefinition { loc, name, values } } -derive_ast_eq! { struct Annotation { loc, id, value } } -derive_ast_eq! { enum PragmaDirective { - _ - Identifier(loc, id1, id2), - StringLiteral(loc, id, lit), - Version(loc, id, version), - _ -}} -derive_ast_eq! { enum UsingList { - Error, - _ - Library(expr), - Functions(exprs), - _ -}} -derive_ast_eq! { enum UserDefinedOperator { - BitwiseAnd, - BitwiseNot, - Negate, - BitwiseOr, - BitwiseXor, - Add, - Divide, - Modulo, - Multiply, - Subtract, - Equal, - More, - MoreEqual, - Less, - LessEqual, - NotEqual, - _ - _ -}} -derive_ast_eq! { enum Visibility { - _ - External(loc), - Public(loc), - Internal(loc), - Private(loc), - _ -}} -derive_ast_eq! { enum Mutability { - _ - Pure(loc), - View(loc), - Constant(loc), - Payable(loc), - _ -}} -derive_ast_eq! { enum FunctionAttribute { - _ - Mutability(muta), - Visibility(visi), - Virtual(loc), - Immutable(loc), - Override(loc, idents), - BaseOrModifier(loc, base), - Error(loc), - _ -}} -derive_ast_eq! { enum StorageLocation { - _ - Memory(loc), - Storage(loc), - Calldata(loc), - Transient(loc), - _ -}} -derive_ast_eq! { enum Type { - Address, - AddressPayable, - Payable, - Bool, - Rational, - DynamicBytes, - String, - _ - Int(int), - Uint(int), - Bytes(int), - _ - Mapping{ loc, key, key_name, value, value_name }, - Function { params, attributes, returns }, -}} -derive_ast_eq! { enum Expression { - _ - PostIncrement(loc, expr1), - PostDecrement(loc, expr1), - New(loc, expr1), - ArraySubscript(loc, expr1, expr2), - ArraySlice( - loc, - expr1, - expr2, - expr3, - ), - MemberAccess(loc, expr1, ident1), - FunctionCall(loc, expr1, exprs1), - FunctionCallBlock(loc, expr1, stmt), - NamedFunctionCall(loc, expr1, args), - Not(loc, expr1), - BitwiseNot(loc, expr1), - Delete(loc, expr1), - PreIncrement(loc, expr1), - PreDecrement(loc, expr1), - UnaryPlus(loc, expr1), - Negate(loc, expr1), - Power(loc, expr1, expr2), - Multiply(loc, expr1, expr2), - Divide(loc, expr1, expr2), - Modulo(loc, expr1, expr2), - Add(loc, expr1, expr2), - Subtract(loc, expr1, expr2), - ShiftLeft(loc, expr1, expr2), - ShiftRight(loc, expr1, expr2), - BitwiseAnd(loc, expr1, expr2), - BitwiseXor(loc, expr1, expr2), - BitwiseOr(loc, expr1, expr2), - Less(loc, expr1, expr2), - More(loc, expr1, expr2), - LessEqual(loc, expr1, expr2), - MoreEqual(loc, expr1, expr2), - Equal(loc, expr1, expr2), - NotEqual(loc, expr1, expr2), - And(loc, expr1, expr2), - Or(loc, expr1, expr2), - ConditionalOperator(loc, expr1, expr2, expr3), - Assign(loc, expr1, expr2), - AssignOr(loc, expr1, expr2), - AssignAnd(loc, expr1, expr2), - AssignXor(loc, expr1, expr2), - AssignShiftLeft(loc, expr1, expr2), - AssignShiftRight(loc, expr1, expr2), - AssignAdd(loc, expr1, expr2), - AssignSubtract(loc, expr1, expr2), - AssignMultiply(loc, expr1, expr2), - AssignDivide(loc, expr1, expr2), - AssignModulo(loc, expr1, expr2), - BoolLiteral(loc, bool1), - NumberLiteral(loc, #[ast_eq_use(to_num)] str1, #[ast_eq_use(to_num)] str2, unit), - RationalNumberLiteral( - loc, - #[ast_eq_use(to_num)] str1, - #[ast_eq_use(to_num_reversed)] str2, - #[ast_eq_use(to_num)] str3, - unit - ), - HexNumberLiteral(loc, str1, unit), - StringLiteral(strs1), - Type(loc, ty1), - HexLiteral(hexs1), - AddressLiteral(loc, str1), - Variable(ident1), - List(loc, params1), - ArrayLiteral(loc, exprs1), - Parenthesis(loc, expr) - _ -}} -derive_ast_eq! { enum CatchClause { - _ - Simple(param, ident, stmt), - Named(loc, ident, param, stmt), - _ -}} -derive_ast_eq! { enum YulStatement { - _ - Assign(loc, exprs, expr), - VariableDeclaration(loc, idents, expr), - If(loc, expr, block), - For(yul_for), - Switch(switch), - Leave(loc), - Break(loc), - Continue(loc), - Block(block), - FunctionDefinition(def), - FunctionCall(func), - Error(loc), - _ -}} -derive_ast_eq! { enum YulExpression { - _ - BoolLiteral(loc, boo, ident), - NumberLiteral(loc, string1, string2, ident), - HexNumberLiteral(loc, string, ident), - HexStringLiteral(hex, ident), - StringLiteral(string, ident), - Variable(ident), - FunctionCall(func), - SuffixAccess(loc, expr, ident), - _ -}} -derive_ast_eq! { enum YulSwitchOptions { - _ - Case(loc, expr, block), - Default(loc, block), - _ -}} -derive_ast_eq! { enum SourceUnitPart { - _ - ContractDefinition(def), - PragmaDirective(pragma), - ImportDirective(import), - EnumDefinition(def), - StructDefinition(def), - EventDefinition(def), - ErrorDefinition(def), - FunctionDefinition(def), - VariableDefinition(def), - TypeDefinition(def), - Using(using), - StraySemicolon(loc), - Annotation(annotation), - _ -}} -derive_ast_eq! { enum ImportPath { - _ - Filename(lit), - Path(path), - _ -}} -derive_ast_eq! { enum Import { - _ - Plain(string, loc), - GlobalSymbol(string, ident, loc), - Rename(string, idents, loc), - _ -}} -derive_ast_eq! { enum FunctionTy { - Constructor, - Function, - Fallback, - Receive, - Modifier, - _ - _ -}} -derive_ast_eq! { enum ContractPart { - _ - StructDefinition(def), - EventDefinition(def), - EnumDefinition(def), - ErrorDefinition(def), - VariableDefinition(def), - FunctionDefinition(def), - TypeDefinition(def), - StraySemicolon(loc), - Using(using), - Annotation(annotation), - _ -}} -derive_ast_eq! { enum ContractTy { - _ - Abstract(loc), - Contract(loc), - Interface(loc), - Library(loc), - _ -}} -derive_ast_eq! { enum VariableAttribute { - _ - Visibility(visi), - Constant(loc), - Immutable(loc), - Override(loc, idents), - StorageType(st), - StorageLocation(st), - _ -}} - -// Who cares -impl AstEq for StorageType { - fn ast_eq(&self, _other: &Self) -> bool { - true - } -} - -impl AstEq for VersionComparator { - fn ast_eq(&self, _other: &Self) -> bool { - true - } -} diff --git a/crates/doc/src/solang_ext/loc.rs b/crates/doc/src/solang_ext/loc.rs deleted file mode 100644 index cec21d7714385..0000000000000 --- a/crates/doc/src/solang_ext/loc.rs +++ /dev/null @@ -1,168 +0,0 @@ -use solang_parser::pt; -use std::{borrow::Cow, rc::Rc, sync::Arc}; - -/// Returns the code location. -/// -/// Patched version of [`pt::CodeLocation`]: includes the block of a [`pt::FunctionDefinition`] in -/// its `loc`. -pub trait CodeLocationExt { - /// Returns the code location of `self`. - fn loc(&self) -> pt::Loc; -} - -impl CodeLocationExt for &T { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for &mut T { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Cow<'_, T> { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Box { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Rc { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Arc { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -// FunctionDefinition patch -impl CodeLocationExt for pt::FunctionDefinition { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - let mut loc = self.loc; - if let Some(ref body) = self.body { - loc.use_end_from(&pt::CodeLocation::loc(body)); - } - loc - } -} - -impl CodeLocationExt for pt::ContractPart { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - match self { - Self::FunctionDefinition(f) => f.loc(), - _ => pt::CodeLocation::loc(self), - } - } -} - -impl CodeLocationExt for pt::SourceUnitPart { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - match self { - Self::FunctionDefinition(f) => f.loc(), - _ => pt::CodeLocation::loc(self), - } - } -} - -impl CodeLocationExt for pt::ImportPath { - fn loc(&self) -> pt::Loc { - match self { - Self::Filename(s) => s.loc(), - Self::Path(i) => i.loc(), - } - } -} - -impl CodeLocationExt for pt::VersionComparator { - fn loc(&self) -> pt::Loc { - match self { - Self::Plain { loc, .. } - | Self::Operator { loc, .. } - | Self::Or { loc, .. } - | Self::Range { loc, .. } => *loc, - } - } -} - -macro_rules! impl_delegate { - ($($t:ty),+ $(,)?) => {$( - impl CodeLocationExt for $t { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - pt::CodeLocation::loc(self) - } - } - )+}; -} - -impl_delegate! { - pt::Annotation, - pt::Base, - pt::ContractDefinition, - pt::EnumDefinition, - pt::ErrorDefinition, - pt::ErrorParameter, - pt::EventDefinition, - pt::EventParameter, - pt::PragmaDirective, - // pt::FunctionDefinition, - pt::HexLiteral, - pt::Identifier, - pt::IdentifierPath, - pt::NamedArgument, - pt::Parameter, - // pt::SourceUnit, - pt::StringLiteral, - pt::StructDefinition, - pt::TypeDefinition, - pt::Using, - pt::UsingFunction, - pt::VariableDeclaration, - pt::VariableDefinition, - pt::YulBlock, - pt::YulFor, - pt::YulFunctionCall, - pt::YulFunctionDefinition, - pt::YulSwitch, - pt::YulTypedIdentifier, - - pt::CatchClause, - pt::Comment, - // pt::ContractPart, - pt::ContractTy, - pt::Expression, - pt::FunctionAttribute, - // pt::FunctionTy, - pt::Import, - pt::Loc, - pt::Mutability, - // pt::SourceUnitPart, - pt::Statement, - pt::StorageLocation, - // pt::Type, - // pt::UserDefinedOperator, - pt::UsingList, - pt::VariableAttribute, - // pt::Visibility, - pt::YulExpression, - pt::YulStatement, - pt::YulSwitchOptions, -} diff --git a/crates/doc/src/solang_ext/mod.rs b/crates/doc/src/solang_ext/mod.rs deleted file mode 100644 index d85dd1a5aace7..0000000000000 --- a/crates/doc/src/solang_ext/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Extension traits and modules to the [`solang_parser`] crate. - -/// Same as [`solang_parser::pt`], but with the patched `CodeLocation`. -pub mod pt { - #[doc(no_inline)] - pub use super::loc::CodeLocationExt as CodeLocation; - - #[doc(no_inline)] - pub use solang_parser::pt::{ - Annotation, Base, CatchClause, Comment, ContractDefinition, ContractPart, ContractTy, - EnumDefinition, ErrorDefinition, ErrorParameter, EventDefinition, EventParameter, - Expression, FunctionAttribute, FunctionDefinition, FunctionTy, HexLiteral, Identifier, - IdentifierPath, Import, ImportPath, Loc, Mutability, NamedArgument, OptionalCodeLocation, - Parameter, ParameterList, PragmaDirective, SourceUnit, SourceUnitPart, Statement, - StorageLocation, StringLiteral, StructDefinition, Type, TypeDefinition, - UserDefinedOperator, Using, UsingFunction, UsingList, VariableAttribute, - VariableDeclaration, VariableDefinition, Visibility, YulBlock, YulExpression, YulFor, - YulFunctionCall, YulFunctionDefinition, YulStatement, YulSwitch, YulSwitchOptions, - YulTypedIdentifier, - }; -} - -mod ast_eq; -mod loc; -mod safe_unwrap; -mod visit; - -pub use ast_eq::AstEq; -pub use loc::CodeLocationExt; -pub use safe_unwrap::SafeUnwrap; -pub use visit::{Visitable, Visitor}; diff --git a/crates/doc/src/solang_ext/safe_unwrap.rs b/crates/doc/src/solang_ext/safe_unwrap.rs deleted file mode 100644 index fe2810ad9705a..0000000000000 --- a/crates/doc/src/solang_ext/safe_unwrap.rs +++ /dev/null @@ -1,52 +0,0 @@ -use solang_parser::pt; - -/// Trait implemented to unwrap optional parse tree items initially introduced in -/// [hyperledger/solang#1068]. -/// -/// Note that the methods of this trait should only be used on parse tree items' fields, like -/// [pt::VariableDefinition] or [pt::EventDefinition], where the `name` field is `None` only when an -/// error occurred during parsing. -/// -/// [hyperledger/solang#1068]: https://github.com/hyperledger/solang/pull/1068 -pub trait SafeUnwrap { - /// See [SafeUnwrap]. - fn safe_unwrap(&self) -> &T; - - /// See [SafeUnwrap]. - fn safe_unwrap_mut(&mut self) -> &mut T; -} - -#[inline(never)] -#[cold] -#[track_caller] -fn invalid() -> ! { - panic!("invalid parse tree") -} - -macro_rules! impl_ { - ($($t:ty),+ $(,)?) => { - $( - impl SafeUnwrap<$t> for Option<$t> { - #[inline] - #[track_caller] - fn safe_unwrap(&self) -> &$t { - match *self { - Some(ref x) => x, - None => invalid(), - } - } - - #[inline] - #[track_caller] - fn safe_unwrap_mut(&mut self) -> &mut $t { - match *self { - Some(ref mut x) => x, - None => invalid(), - } - } - } - )+ - }; -} - -impl_!(pt::Identifier, pt::StringLiteral); diff --git a/crates/doc/src/solang_ext/visit.rs b/crates/doc/src/solang_ext/visit.rs deleted file mode 100644 index 80cb4f3dc1376..0000000000000 --- a/crates/doc/src/solang_ext/visit.rs +++ /dev/null @@ -1,621 +0,0 @@ -//! Visitor helpers to traverse the [solang Solidity Parse Tree](solang_parser::pt). - -use crate::solang_ext::{CodeLocationExt, pt::*}; - -/// A trait that is invoked while traversing the Solidity Parse Tree. -/// Each method of the [Visitor] trait is a hook that can be potentially overridden. -/// -/// Currently the main implementer of this trait is the [`Formatter`](crate::Formatter<'_>) struct. -pub trait Visitor { - type Error: std::error::Error; - - fn visit_source(&mut self, _loc: Loc) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_source_unit(&mut self, _source_unit: &mut SourceUnit) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_contract(&mut self, _contract: &mut ContractDefinition) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_annotation(&mut self, annotation: &mut Annotation) -> Result<(), Self::Error> { - self.visit_source(annotation.loc) - } - - fn visit_pragma(&mut self, pragma: &mut PragmaDirective) -> Result<(), Self::Error> { - self.visit_source(pragma.loc()) - } - - fn visit_import_plain( - &mut self, - _loc: Loc, - _import: &mut ImportPath, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_import_global( - &mut self, - _loc: Loc, - _global: &mut ImportPath, - _alias: &mut Identifier, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_import_renames( - &mut self, - _loc: Loc, - _imports: &mut [(Identifier, Option)], - _from: &mut ImportPath, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_enum(&mut self, _enum: &mut EnumDefinition) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_assembly( - &mut self, - loc: Loc, - _dialect: &mut Option, - _block: &mut YulBlock, - _flags: &mut Option>, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_block( - &mut self, - loc: Loc, - _unchecked: bool, - _statements: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_args(&mut self, loc: Loc, _args: &mut Vec) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - /// Don't write semicolon at the end because expressions can appear as both - /// part of other node and a statement in the function body - fn visit_expr(&mut self, loc: Loc, _expr: &mut Expression) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_ident(&mut self, loc: Loc, _ident: &mut Identifier) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_ident_path(&mut self, idents: &mut IdentifierPath) -> Result<(), Self::Error> { - self.visit_source(idents.loc) - } - - fn visit_emit(&mut self, loc: Loc, _event: &mut Expression) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<(), Self::Error> { - self.visit_source(var.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_var_definition_stmt( - &mut self, - loc: Loc, - _declaration: &mut VariableDeclaration, - _expr: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon() - } - - fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<(), Self::Error> { - self.visit_source(var.loc) - } - - fn visit_return( - &mut self, - loc: Loc, - _expr: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_revert( - &mut self, - loc: Loc, - _error: &mut Option, - _args: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_revert_named_args( - &mut self, - loc: Loc, - _error: &mut Option, - _args: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_break(&mut self, loc: Loc, _semicolon: bool) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_continue(&mut self, loc: Loc, _semicolon: bool) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - #[expect(clippy::type_complexity)] - fn visit_try( - &mut self, - loc: Loc, - _expr: &mut Expression, - _returns: &mut Option<(Vec<(Loc, Option)>, Box)>, - _clauses: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_if( - &mut self, - loc: Loc, - _cond: &mut Expression, - _if_branch: &mut Box, - _else_branch: &mut Option>, - _is_first_stmt: bool, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_do_while( - &mut self, - loc: Loc, - _body: &mut Statement, - _cond: &mut Expression, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_while( - &mut self, - loc: Loc, - _cond: &mut Expression, - _body: &mut Statement, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_for( - &mut self, - loc: Loc, - _init: &mut Option>, - _cond: &mut Option>, - _update: &mut Option>, - _body: &mut Option>, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<(), Self::Error> { - self.visit_source(func.loc())?; - if func.body.is_none() { - self.visit_stray_semicolon()?; - } - - Ok(()) - } - - fn visit_function_attribute( - &mut self, - attribute: &mut FunctionAttribute, - ) -> Result<(), Self::Error> { - self.visit_source(attribute.loc())?; - Ok(()) - } - - fn visit_var_attribute( - &mut self, - attribute: &mut VariableAttribute, - ) -> Result<(), Self::Error> { - self.visit_source(attribute.loc())?; - Ok(()) - } - - fn visit_base(&mut self, base: &mut Base) -> Result<(), Self::Error> { - self.visit_source(base.loc) - } - - fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<(), Self::Error> { - self.visit_source(parameter.loc) - } - - fn visit_struct(&mut self, structure: &mut StructDefinition) -> Result<(), Self::Error> { - self.visit_source(structure.loc)?; - - Ok(()) - } - - fn visit_event(&mut self, event: &mut EventDefinition) -> Result<(), Self::Error> { - self.visit_source(event.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_event_parameter(&mut self, param: &mut EventParameter) -> Result<(), Self::Error> { - self.visit_source(param.loc) - } - - fn visit_error(&mut self, error: &mut ErrorDefinition) -> Result<(), Self::Error> { - self.visit_source(error.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_error_parameter(&mut self, param: &mut ErrorParameter) -> Result<(), Self::Error> { - self.visit_source(param.loc) - } - - fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<(), Self::Error> { - self.visit_source(def.loc) - } - - fn visit_stray_semicolon(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_using(&mut self, using: &mut Using) -> Result<(), Self::Error> { - self.visit_source(using.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_yul_block( - &mut self, - loc: Loc, - _stmts: &mut Vec, - _attempt_single_line: bool, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_expr(&mut self, expr: &mut YulExpression) -> Result<(), Self::Error> { - self.visit_source(expr.loc()) - } - - fn visit_yul_assignment( - &mut self, - loc: Loc, - _exprs: &mut Vec, - _expr: &mut Option<&mut YulExpression>, - ) -> Result<(), Self::Error> - where - T: Visitable + CodeLocationExt, - { - self.visit_source(loc) - } - - fn visit_yul_for(&mut self, stmt: &mut YulFor) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_function_call(&mut self, stmt: &mut YulFunctionCall) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_fun_def(&mut self, stmt: &mut YulFunctionDefinition) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_if( - &mut self, - loc: Loc, - _expr: &mut YulExpression, - _block: &mut YulBlock, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_leave(&mut self, loc: Loc) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_switch(&mut self, stmt: &mut YulSwitch) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_var_declaration( - &mut self, - loc: Loc, - _idents: &mut Vec, - _expr: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_typed_ident(&mut self, ident: &mut YulTypedIdentifier) -> Result<(), Self::Error> { - self.visit_source(ident.loc) - } - - fn visit_parser_error(&mut self, loc: Loc) -> Result<(), Self::Error> { - self.visit_source(loc) - } -} - -/// Visitable trait for [`solang_parser::pt`] types. -/// -/// All [`solang_parser::pt`] types, such as [Statement], should implement the [Visitable] trait -/// that accepts a trait [Visitor] implementation, which has various callback handles for Solidity -/// Parse Tree nodes. -/// -/// We want to take a `&mut self` to be able to implement some advanced features in the future such -/// as modifying the Parse Tree before formatting it. -pub trait Visitable { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor; -} - -impl Visitable for &mut T -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - T::visit(self, v) - } -} - -impl Visitable for Option -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - if let Some(inner) = self.as_mut() { inner.visit(v) } else { Ok(()) } - } -} - -impl Visitable for Box -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - T::visit(self, v) - } -} - -impl Visitable for Vec -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - for item in self.iter_mut() { - item.visit(v)?; - } - Ok(()) - } -} - -impl Visitable for SourceUnitPart { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::ContractDefinition(contract) => v.visit_contract(contract), - Self::PragmaDirective(pragma) => v.visit_pragma(pragma), - 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), - } - } -} - -impl Visitable for Import { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::Plain(import, loc) => v.visit_import_plain(*loc, import), - Self::GlobalSymbol(global, import_as, loc) => { - v.visit_import_global(*loc, global, import_as) - } - Self::Rename(from, imports, loc) => v.visit_import_renames(*loc, imports, from), - } - } -} - -impl Visitable for ContractPart { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - 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), - } - } -} - -impl Visitable for Statement { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::Block { loc, unchecked, statements } => { - v.visit_block(*loc, *unchecked, statements) - } - Self::Assembly { loc, dialect, block, flags } => { - v.visit_assembly(*loc, dialect, block, flags) - } - 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) - } - Self::While(loc, cond, body) => v.visit_while(*loc, cond, body), - Self::Expression(loc, expr) => { - v.visit_expr(*loc, expr)?; - v.visit_stray_semicolon() - } - Self::VariableDefinition(loc, declaration, expr) => { - v.visit_var_definition_stmt(*loc, declaration, expr) - } - 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), - } - } -} - -impl Visitable for Loc { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_source(*self) - } -} - -impl Visitable for Expression { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_expr(self.loc(), self) - } -} - -impl Visitable for Identifier { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_ident(self.loc, self) - } -} - -impl Visitable for VariableDeclaration { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_var_declaration(self) - } -} - -impl Visitable for YulBlock { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_yul_block(self.loc, self.statements.as_mut(), false) - } -} - -impl Visitable for YulStatement { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - 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) - } - Self::Error(loc) => v.visit_parser_error(*loc), - } - } -} - -macro_rules! impl_visitable { - ($type:ty, $func:ident) => { - impl Visitable for $type { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.$func(self) - } - } - }; -} - -impl_visitable!(SourceUnit, visit_source_unit); -impl_visitable!(FunctionAttribute, visit_function_attribute); -impl_visitable!(VariableAttribute, visit_var_attribute); -impl_visitable!(Parameter, visit_parameter); -impl_visitable!(Base, visit_base); -impl_visitable!(EventParameter, visit_event_parameter); -impl_visitable!(ErrorParameter, visit_error_parameter); -impl_visitable!(IdentifierPath, visit_ident_path); -impl_visitable!(YulExpression, visit_yul_expr); -impl_visitable!(YulTypedIdentifier, visit_yul_typed_ident); diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 1502322ac87f4..888f6269623a5 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -1,14 +1,11 @@ use crate::{ - CONTRACT_INHERITANCE_ID, CommentTag, Comments, CommentsRef, DEPLOYMENTS_ID, Document, - GIT_SOURCE_ID, INHERITDOC_ID, Markdown, PreprocessorOutput, + BaseInfo, CONTRACT_INHERITANCE_ID, CommentTag, Comments, CommentsRef, DEPLOYMENTS_ID, Document, + FunctionSource, GIT_SOURCE_ID, INHERITDOC_ID, Markdown, PreprocessorOutput, VariableAttr, document::{DocumentContent, read_context}, - helpers::function_signature, parser::ParseSource, - solang_ext::SafeUnwrap, writer::BufWriter, }; use itertools::Itertools; -use solang_parser::pt::{Base, FunctionDefinition, VariableAttribute}; use std::path::Path; /// The result of [`AsDoc::as_doc`]. @@ -75,8 +72,8 @@ impl AsDoc for CommentsRef<'_> { writer.writeln_raw(format!( "{}{}: {}", if customs.len() == 1 { "" } else { "- " }, - &c.tag, - &c.value + c.tag, + c.value ))?; writer.writeln()?; } @@ -86,9 +83,9 @@ impl AsDoc for CommentsRef<'_> { } } -impl AsDoc for Base { +impl AsDoc for BaseInfo { fn as_doc(&self) -> AsDocResult { - Ok(self.name.identifiers.iter().map(|ident| ident.name.clone()).join(".")) + Ok(self.name.clone()) } } @@ -106,8 +103,7 @@ impl AsDoc for Document { } for item in items { - let func = item.as_function().unwrap(); - let heading = function_signature(func).replace(',', ", "); + let heading = item.source.signature().replace(',', ", "); writer.write_heading(&heading)?; writer.write_section(&item.comments, &item.code)?; } @@ -121,7 +117,7 @@ impl AsDoc for Document { for item in items { let var = item.as_variable().unwrap(); - writer.write_heading(&var.name.safe_unwrap().name)?; + writer.write_heading(&var.name)?; writer.write_section(&item.comments, &item.code)?; } } @@ -138,15 +134,15 @@ impl AsDoc for Document { match &item.source { ParseSource::Contract(contract) => { - if !contract.base.is_empty() { + if !contract.bases.is_empty() { writer.write_bold("Inherits:")?; let mut bases = vec![]; let linked = read_context!(self, CONTRACT_INHERITANCE_ID, ContractInheritance); - for base in &contract.base { + for base in &contract.bases { let base_doc = base.as_doc()?; - let base_ident = &base.name.identifiers.last().unwrap().name; + let base_ident = &base.ident; let link = linked .as_ref() @@ -179,8 +175,7 @@ impl AsDoc for Document { item.attrs.iter().any(|attr| { matches!( attr, - VariableAttribute::Constant(_) - | VariableAttribute::Immutable(_) + VariableAttr::Constant | VariableAttr::Immutable ) }) }); @@ -189,11 +184,11 @@ impl AsDoc for Document { writer.write_subtitle("Constants")?; constants.into_iter().try_for_each(|(item, comments, code)| { let comments = comments.merge_inheritdoc( - &item.name.safe_unwrap().name, + &item.name, read_context!(self, INHERITDOC_ID, Inheritdoc), ); - writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_heading(&item.name)?; writer.write_section(&comments, code)?; writer.writeln() })?; @@ -203,11 +198,11 @@ impl AsDoc for Document { writer.write_subtitle("State Variables")?; state_vars.into_iter().try_for_each(|(item, comments, code)| { let comments = comments.merge_inheritdoc( - &item.name.safe_unwrap().name, + &item.name, read_context!(self, INHERITDOC_ID, Inheritdoc), ); - writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_heading(&item.name)?; writer.write_section(&comments, code)?; writer.writeln() })?; @@ -225,7 +220,7 @@ impl AsDoc for Document { if let Some(events) = item.events() { writer.write_subtitle("Events")?; events.into_iter().try_for_each(|(item, comments, code)| { - writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_heading(&item.name)?; writer.write_section(comments, code)?; writer.try_write_events_table(&item.fields, comments) })?; @@ -234,7 +229,7 @@ impl AsDoc for Document { if let Some(errors) = item.errors() { writer.write_subtitle("Errors")?; errors.into_iter().try_for_each(|(item, comments, code)| { - writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_heading(&item.name)?; writer.write_section(comments, code)?; writer.try_write_errors_table(&item.fields, comments) })?; @@ -243,7 +238,7 @@ impl AsDoc for Document { if let Some(structs) = item.structs() { writer.write_subtitle("Structs")?; structs.into_iter().try_for_each(|(item, comments, code)| { - writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_heading(&item.name)?; writer.write_section(comments, code)?; writer.try_write_properties_table(&item.fields, comments) })?; @@ -252,7 +247,7 @@ impl AsDoc for Document { if let Some(enums) = item.enums() { writer.write_subtitle("Enums")?; enums.into_iter().try_for_each(|(item, comments, code)| { - writer.write_heading(&item.name.safe_unwrap().name)?; + writer.write_heading(&item.name)?; writer.write_section(comments, code)?; writer.try_write_variant_table(item, comments) })?; @@ -270,16 +265,16 @@ impl AsDoc for Document { writer.write_code(&item.code)?; // Write function parameter comments in a table - let params = - func.params.iter().filter_map(|p| p.1.as_ref()).collect::>(); - writer.try_write_param_table(CommentTag::Param, ¶ms, &item.comments)?; + writer.try_write_param_table( + CommentTag::Param, + &func.params, + &item.comments, + )?; // Write function return parameter comments in a table - let returns = - func.returns.iter().filter_map(|p| p.1.as_ref()).collect::>(); writer.try_write_param_table( CommentTag::Return, - &returns, + &func.returns, &item.comments, )?; @@ -315,12 +310,12 @@ impl Document { fn write_function( &self, writer: &mut BufWriter, - func: &FunctionDefinition, + func: &FunctionSource, comments: &Comments, code: &str, ) -> Result<(), std::fmt::Error> { - let func_sign = function_signature(func); - let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.clone()); + let func_name = func.name.as_deref().unwrap_or(&func.kind).to_string(); + let func_sign = func.signature(); let comments = comments.merge_inheritdoc(&func_sign, read_context!(self, INHERITDOC_ID, Inheritdoc)); @@ -336,12 +331,10 @@ impl Document { writer.write_code(code)?; // Write function parameter comments in a table - let params = func.params.iter().filter_map(|p| p.1.as_ref()).collect::>(); - writer.try_write_param_table(CommentTag::Param, ¶ms, &comments)?; + writer.try_write_param_table(CommentTag::Param, &func.params, &comments)?; // Write function return parameter comments in a table - let returns = func.returns.iter().filter_map(|p| p.1.as_ref()).collect::>(); - writer.try_write_param_table(CommentTag::Return, &returns, &comments)?; + writer.try_write_param_table(CommentTag::Return, &func.returns, &comments)?; writer.writeln()?; Ok(()) diff --git a/crates/doc/src/writer/traits.rs b/crates/doc/src/writer/traits.rs index 0b79718d5102f..7b0fbcc813a2b 100644 --- a/crates/doc/src/writer/traits.rs +++ b/crates/doc/src/writer/traits.rs @@ -1,58 +1,23 @@ //! Helper traits for writing documentation. -use solang_parser::pt::Expression; +use crate::ParamInfo; -/// Helper trait to abstract over a solang type that can be documented as parameter +/// Helper trait to abstract over a type that can be documented as a parameter. pub(crate) trait ParamLike { - /// Returns the type of the parameter. - fn ty(&self) -> &Expression; - /// Returns the type as a string. - fn type_name(&self) -> String { - self.ty().to_string() - } + fn type_name(&self) -> &str; /// Returns the identifier of the parameter. fn name(&self) -> Option<&str>; } -impl ParamLike for solang_parser::pt::Parameter { - fn ty(&self) -> &Expression { - &self.ty - } - - fn name(&self) -> Option<&str> { - self.name.as_ref().map(|id| id.name.as_str()) - } -} - -impl ParamLike for solang_parser::pt::VariableDeclaration { - fn ty(&self) -> &Expression { - &self.ty - } - - fn name(&self) -> Option<&str> { - self.name.as_ref().map(|id| id.name.as_str()) - } -} - -impl ParamLike for solang_parser::pt::EventParameter { - fn ty(&self) -> &Expression { - &self.ty - } - - fn name(&self) -> Option<&str> { - self.name.as_ref().map(|id| id.name.as_str()) - } -} - -impl ParamLike for solang_parser::pt::ErrorParameter { - fn ty(&self) -> &Expression { +impl ParamLike for ParamInfo { + fn type_name(&self) -> &str { &self.ty } fn name(&self) -> Option<&str> { - self.name.as_ref().map(|id| id.name.as_str()) + self.name.as_deref() } } @@ -60,8 +25,8 @@ impl ParamLike for &T where T: ParamLike, { - fn ty(&self) -> &Expression { - T::ty(*self) + fn type_name(&self) -> &str { + T::type_name(*self) } fn name(&self) -> Option<&str> { diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 03d569c17f500..801e813026a39 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -36,7 +36,7 @@ alloy-primitives = { workspace = true, features = [ alloy-provider.workspace = true alloy-network.workspace = true alloy-consensus.workspace = true -alloy-op-evm.workspace = true +alloy-op-evm = { workspace = true, optional = true } alloy-rpc-types = { workspace = true, features = ["anvil"] } alloy-sol-types.workspace = true alloy-rlp.workspace = true @@ -54,9 +54,10 @@ revm = { workspace = true, features = [ "blst", ] } revm-inspectors.workspace = true -op-alloy-consensus = { workspace = true, features = ["k256"] } -op-alloy-network.workspace = true -op-revm.workspace = true +op-alloy-consensus = { workspace = true, features = ["k256"], optional = true } +op-alloy-network = { workspace = true, optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } +op-revm = { workspace = true, optional = true } tempo-revm.workspace = true tempo-alloy.workspace = true tempo-contracts.workspace = true @@ -77,7 +78,18 @@ url.workspace = true [dev-dependencies] alloy-serde.workspace = true -op-alloy-consensus.workspace = true -op-alloy-rpc-types.workspace = true anvil.workspace = true foundry-test-utils.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "dep:op-alloy-consensus", + "dep:op-alloy-network", + "dep:op-alloy-rpc-types", + "dep:alloy-op-evm", + "dep:op-revm", + "foundry-common/optimism", + "foundry-evm-hardforks/optimism", + "foundry-evm-networks/optimism", +] diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 0cfd56a44219c..b836023a968b7 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -223,8 +223,8 @@ fn trimmed_hex(s: &[u8]) -> String { } else { format!( "{}…{} ({} bytes)", - &hex::encode(&s[..n / 2]), - &hex::encode(&s[s.len() - n / 2..]), + hex::encode(&s[..n / 2]), + hex::encode(&s[s.len() - n / 2..]), s.len(), ) } diff --git a/crates/evm/core/src/env.rs b/crates/evm/core/src/env.rs index 132b986f55e7f..76429e4bc78e3 100644 --- a/crates/evm/core/src/env.rs +++ b/crates/evm/core/src/env.rs @@ -4,13 +4,9 @@ use alloy_consensus::Typed2718; pub use alloy_evm::EvmEnv; use alloy_evm::FromRecoveredTx; use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse}; -use alloy_op_evm::OpTx; use alloy_primitives::{Address, B256, Bytes, U256}; -use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit}; -use op_revm::{ - OpTransaction, - transaction::{OpTxTr, deposit::DEPOSIT_TRANSACTION_TYPE}, -}; +#[cfg(feature = "optimism")] +use op_revm::transaction::deposit::DEPOSIT_TRANSACTION_TYPE; use revm::{ Context, Database, Journal, context::{Block, BlockEnv, Cfg, CfgEnv, Transaction, TxEnv}, @@ -236,9 +232,16 @@ pub trait FoundryTransaction: Transaction { /// Sets whether the transaction is a system transaction fn set_system_transaction(&mut self, _is_system_transaction: bool) {} - /// Returns `true` if transaction is of type [`DEPOSIT_TRANSACTION_TYPE`]. + /// Returns `true` if transaction is an Optimism deposit transaction. fn is_deposit(&self) -> bool { - self.tx_type() == DEPOSIT_TRANSACTION_TYPE + #[cfg(feature = "optimism")] + { + self.tx_type() == DEPOSIT_TRANSACTION_TYPE + } + #[cfg(not(feature = "optimism"))] + { + false + } } // Tempo methods @@ -320,188 +323,6 @@ impl FoundryTransaction for TxEnv { } } -impl FoundryTransaction for OpTransaction { - fn set_tx_type(&mut self, tx_type: u8) { - self.base.set_tx_type(tx_type); - } - - fn set_caller(&mut self, caller: Address) { - self.base.set_caller(caller); - } - - fn set_gas_limit(&mut self, gas_limit: u64) { - self.base.set_gas_limit(gas_limit); - } - - fn set_gas_price(&mut self, gas_price: u128) { - self.base.set_gas_price(gas_price); - } - - fn set_kind(&mut self, kind: TxKind) { - self.base.set_kind(kind); - } - - fn set_value(&mut self, value: U256) { - self.base.set_value(value); - } - - fn set_data(&mut self, data: Bytes) { - self.base.set_data(data); - } - - fn set_nonce(&mut self, nonce: u64) { - self.base.set_nonce(nonce); - } - - fn set_chain_id(&mut self, chain_id: Option) { - self.base.set_chain_id(chain_id); - } - - fn set_access_list(&mut self, access_list: AccessList) { - self.base.set_access_list(access_list); - } - - fn authorization_list_mut( - &mut self, - ) -> &mut Vec> { - self.base.authorization_list_mut() - } - - fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { - self.base.set_gas_priority_fee(gas_priority_fee); - } - - fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} - - fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} - - fn enveloped_tx(&self) -> Option<&Bytes> { - OpTxTr::enveloped_tx(self) - } - - fn set_enveloped_tx(&mut self, bytes: Bytes) { - self.enveloped_tx = Some(bytes); - } - - fn source_hash(&self) -> Option { - OpTxTr::source_hash(self) - } - - fn set_source_hash(&mut self, source_hash: B256) { - if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { - self.deposit.source_hash = source_hash; - } - } - - fn mint(&self) -> Option { - OpTxTr::mint(self) - } - - fn set_mint(&mut self, mint: u128) { - if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { - self.deposit.mint = Some(mint); - } - } - - fn is_system_transaction(&self) -> bool { - OpTxTr::is_system_transaction(self) - } - - fn set_system_transaction(&mut self, is_system_transaction: bool) { - if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { - self.deposit.is_system_transaction = is_system_transaction; - } - } -} - -impl FoundryTransaction for OpTx { - fn set_tx_type(&mut self, tx_type: u8) { - self.0.set_tx_type(tx_type); - } - - fn set_caller(&mut self, caller: Address) { - self.0.set_caller(caller); - } - - fn set_gas_limit(&mut self, gas_limit: u64) { - self.0.set_gas_limit(gas_limit); - } - - fn set_gas_price(&mut self, gas_price: u128) { - self.0.set_gas_price(gas_price); - } - - fn set_kind(&mut self, kind: TxKind) { - self.0.set_kind(kind); - } - - fn set_value(&mut self, value: U256) { - self.0.set_value(value); - } - - fn set_data(&mut self, data: Bytes) { - self.0.set_data(data); - } - - fn set_nonce(&mut self, nonce: u64) { - self.0.set_nonce(nonce); - } - - fn set_chain_id(&mut self, chain_id: Option) { - self.0.set_chain_id(chain_id); - } - - fn set_access_list(&mut self, access_list: AccessList) { - self.0.set_access_list(access_list); - } - - fn authorization_list_mut( - &mut self, - ) -> &mut Vec> { - self.0.authorization_list_mut() - } - - fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { - self.0.set_gas_priority_fee(gas_priority_fee); - } - - fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} - - fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} - - fn enveloped_tx(&self) -> Option<&Bytes> { - FoundryTransaction::enveloped_tx(&self.0) - } - - fn set_enveloped_tx(&mut self, bytes: Bytes) { - self.0.set_enveloped_tx(bytes); - } - - fn source_hash(&self) -> Option { - FoundryTransaction::source_hash(&self.0) - } - - fn set_source_hash(&mut self, source_hash: B256) { - self.0.set_source_hash(source_hash); - } - - fn mint(&self) -> Option { - FoundryTransaction::mint(&self.0) - } - - fn set_mint(&mut self, mint: u128) { - self.0.set_mint(mint); - } - - fn is_system_transaction(&self) -> bool { - FoundryTransaction::is_system_transaction(&self.0) - } - - fn set_system_transaction(&mut self, is_system_transaction: bool) { - self.0.set_system_transaction(is_system_transaction); - } -} - impl FoundryTransaction for TempoTxEnv { fn set_tx_type(&mut self, tx_type: u8) { self.inner.set_tx_type(tx_type); @@ -687,32 +508,6 @@ impl FromAnyRpcTransaction for TxEnv { } } -impl FromAnyRpcTransaction for OpTx { - fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { - if let Some(envelope) = tx.as_envelope() { - return Ok(Self(OpTransaction:: { - base: TxEnv::from_recovered_tx(envelope, tx.from()), - enveloped_tx: None, - deposit: Default::default(), - })); - } - - // Handle OP deposit transactions from `Unknown` envelope variant. - if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner - && unknown.ty() == DEPOSIT_TX_TYPE_ID - { - let mut fields = unknown.inner.fields.clone(); - fields.insert("from".to_string(), serde_json::to_value(tx.from())?); - let deposit_tx: TxDeposit = fields - .deserialize_into() - .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?; - return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from)); - } - - eyre::bail!("cannot convert unknown transaction type to OpTransaction") - } -} - impl FromAnyRpcTransaction for TempoTxEnv { fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { use alloy_consensus::Transaction as _; @@ -747,22 +542,234 @@ impl FromAnyRpcTransaction for TempoTxEnv { } } +#[cfg(feature = "optimism")] +mod optimism { + use super::*; + use alloy_op_evm::OpTx; + use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit}; + use op_revm::{OpTransaction, transaction::OpTxTr}; + + impl FoundryTransaction for OpTransaction { + fn set_tx_type(&mut self, tx_type: u8) { + self.base.set_tx_type(tx_type); + } + + fn set_caller(&mut self, caller: Address) { + self.base.set_caller(caller); + } + + fn set_gas_limit(&mut self, gas_limit: u64) { + self.base.set_gas_limit(gas_limit); + } + + fn set_gas_price(&mut self, gas_price: u128) { + self.base.set_gas_price(gas_price); + } + + fn set_kind(&mut self, kind: TxKind) { + self.base.set_kind(kind); + } + + fn set_value(&mut self, value: U256) { + self.base.set_value(value); + } + + fn set_data(&mut self, data: Bytes) { + self.base.set_data(data); + } + + fn set_nonce(&mut self, nonce: u64) { + self.base.set_nonce(nonce); + } + + fn set_chain_id(&mut self, chain_id: Option) { + self.base.set_chain_id(chain_id); + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.base.set_access_list(access_list); + } + + fn authorization_list_mut( + &mut self, + ) -> &mut Vec> { + self.base.authorization_list_mut() + } + + fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { + self.base.set_gas_priority_fee(gas_priority_fee); + } + + fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} + + fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} + + fn enveloped_tx(&self) -> Option<&Bytes> { + OpTxTr::enveloped_tx(self) + } + + fn set_enveloped_tx(&mut self, bytes: Bytes) { + self.enveloped_tx = Some(bytes); + } + + fn source_hash(&self) -> Option { + OpTxTr::source_hash(self) + } + + fn set_source_hash(&mut self, source_hash: B256) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.source_hash = source_hash; + } + } + + fn mint(&self) -> Option { + OpTxTr::mint(self) + } + + fn set_mint(&mut self, mint: u128) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.mint = Some(mint); + } + } + + fn is_system_transaction(&self) -> bool { + OpTxTr::is_system_transaction(self) + } + + fn set_system_transaction(&mut self, is_system_transaction: bool) { + if self.tx_type() == DEPOSIT_TRANSACTION_TYPE { + self.deposit.is_system_transaction = is_system_transaction; + } + } + } + + impl FoundryTransaction for OpTx { + fn set_tx_type(&mut self, tx_type: u8) { + self.0.set_tx_type(tx_type); + } + + fn set_caller(&mut self, caller: Address) { + self.0.set_caller(caller); + } + + fn set_gas_limit(&mut self, gas_limit: u64) { + self.0.set_gas_limit(gas_limit); + } + + fn set_gas_price(&mut self, gas_price: u128) { + self.0.set_gas_price(gas_price); + } + + fn set_kind(&mut self, kind: TxKind) { + self.0.set_kind(kind); + } + + fn set_value(&mut self, value: U256) { + self.0.set_value(value); + } + + fn set_data(&mut self, data: Bytes) { + self.0.set_data(data); + } + + fn set_nonce(&mut self, nonce: u64) { + self.0.set_nonce(nonce); + } + + fn set_chain_id(&mut self, chain_id: Option) { + self.0.set_chain_id(chain_id); + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.0.set_access_list(access_list); + } + + fn authorization_list_mut( + &mut self, + ) -> &mut Vec> { + self.0.authorization_list_mut() + } + + fn set_gas_priority_fee(&mut self, gas_priority_fee: Option) { + self.0.set_gas_priority_fee(gas_priority_fee); + } + + fn set_blob_hashes(&mut self, _blob_hashes: Vec) {} + + fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {} + + fn enveloped_tx(&self) -> Option<&Bytes> { + FoundryTransaction::enveloped_tx(&self.0) + } + + fn set_enveloped_tx(&mut self, bytes: Bytes) { + self.0.set_enveloped_tx(bytes); + } + + fn source_hash(&self) -> Option { + FoundryTransaction::source_hash(&self.0) + } + + fn set_source_hash(&mut self, source_hash: B256) { + self.0.set_source_hash(source_hash); + } + + fn mint(&self) -> Option { + FoundryTransaction::mint(&self.0) + } + + fn set_mint(&mut self, mint: u128) { + self.0.set_mint(mint); + } + + fn is_system_transaction(&self) -> bool { + FoundryTransaction::is_system_transaction(&self.0) + } + + fn set_system_transaction(&mut self, is_system_transaction: bool) { + self.0.set_system_transaction(is_system_transaction); + } + } + + impl FromAnyRpcTransaction for OpTx { + fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result { + if let Some(envelope) = tx.as_envelope() { + return Ok(Self(OpTransaction:: { + base: TxEnv::from_recovered_tx(envelope, tx.from()), + enveloped_tx: None, + deposit: Default::default(), + })); + } + + // Handle OP deposit transactions from `Unknown` envelope variant. + if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner + && unknown.ty() == DEPOSIT_TX_TYPE_ID + { + let mut fields = unknown.inner.fields.clone(); + fields.insert("from".to_string(), serde_json::to_value(tx.from())?); + let deposit_tx: TxDeposit = fields + .deserialize_into() + .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?; + return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from)); + } + + eyre::bail!("cannot convert unknown transaction type to OpTransaction") + } + } +} + #[cfg(test)] mod tests { use std::num::NonZeroU64; use super::*; - use alloy_consensus::{Sealed, Signed, TxEip1559, transaction::Recovered}; + use alloy_consensus::{Signed, TxEip1559, transaction::Recovered}; use alloy_evm::{EthEvmFactory, EvmFactory}; use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction}; - use alloy_op_evm::OpEvmFactory; use alloy_primitives::Signature; use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo}; use alloy_serde::WithOtherFields; use foundry_evm_hardforks::TempoHardfork; - use op_alloy_consensus::{OpTxEnvelope, transaction::OpTransactionInfo}; - use op_alloy_rpc_types::Transaction as OpRpcTransaction; - use op_revm::OpSpecId; use revm::database::EmptyDB; use tempo_alloy::primitives::{ AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope, @@ -793,30 +800,6 @@ mod tests { evm.ctx_mut().set_evm(evm_env); } - #[test] - fn op_evm_foundry_context_ext_implementation() { - let mut evm = - OpEvmFactory::::default().create_evm(EmptyDB::default(), EvmEnv::default()); - - // Test EVM Context Block mutation - evm.ctx_mut().block_mut().set_number(U256::from(123)); - assert_eq!(evm.ctx().block().number(), U256::from(123)); - - // Test EVM Context Tx mutation - evm.ctx_mut().tx_mut().set_nonce(99); - assert_eq!(evm.ctx().tx().nonce(), 99); - - // Test EVM Context Cfg mutation - evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN; - assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN); - - // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env - let tx_env = evm.ctx().tx_clone(); - evm.ctx_mut().set_tx(tx_env); - let evm_env = evm.ctx().evm_clone(); - evm.ctx_mut().set_evm(evm_env); - } - #[test] fn tempo_evm_foundry_context_ext_implementation() { let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default()); @@ -874,23 +857,6 @@ mod tests { assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB))); } - #[test] - fn from_any_rpc_transaction_for_op() { - let from = Address::random(); - let signed_tx = make_signed_eip1559(); - - // Build the eth TxEnv to compare against op base - let rpc_tx = RpcTransaction::from_transaction( - Recovered::new_unchecked(signed_tx.into(), from), - TransactionInfo::default(), - ); - let any_tx = >::from(rpc_tx); - let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap(); - - let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); - assert_eq!(op_tx_env.base, expected_base); - } - #[test] fn from_any_rpc_transaction_unknown_envelope_errors() { let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope { @@ -915,39 +881,6 @@ mod tests { assert!(result.to_string().contains("unknown transaction type")); } - #[test] - fn from_any_rpc_transaction_for_op_deposit() { - let from = Address::random(); - let source_hash = B256::random(); - let deposit = TxDeposit { - source_hash, - from, - to: TxKind::Call(Address::with_last_byte(0xCC)), - mint: 1111, - value: U256::from(200), - gas_limit: 21000, - is_system_transaction: true, - input: Default::default(), - }; - - // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as AnyRpcTransaction. - let op_rpc_tx = OpRpcTransaction::from_transaction( - Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from), - OpTransactionInfo::default(), - ); - let json = serde_json::to_value(&op_rpc_tx).unwrap(); - let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap(); - - let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); - assert_eq!(op_tx_env.base.caller, from); - assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC))); - assert_eq!(op_tx_env.base.value, U256::from(200)); - assert_eq!(op_tx_env.base.gas_limit, 21000); - assert_eq!(op_tx_env.deposit.source_hash, source_hash); - assert_eq!(op_tx_env.deposit.mint, Some(1111)); - assert!(op_tx_env.deposit.is_system_transaction); - } - #[test] fn from_any_rpc_transaction_for_tempo_eth_envelope() { let from = Address::random(); @@ -1004,4 +937,89 @@ mod tests { assert_eq!(tx_env.inner.chain_id, Some(42431)); assert_eq!(tx_env.fee_token, fee_token); } + + #[cfg(feature = "optimism")] + mod optimism { + use super::*; + use alloy_consensus::Sealed; + use alloy_op_evm::{OpEvmFactory, OpTx}; + use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo}; + use op_alloy_rpc_types::Transaction as OpRpcTransaction; + use op_revm::OpSpecId; + + #[test] + fn op_evm_foundry_context_ext_implementation() { + let mut evm = + OpEvmFactory::::default().create_evm(EmptyDB::default(), EvmEnv::default()); + + // Test EVM Context Block mutation + evm.ctx_mut().block_mut().set_number(U256::from(123)); + assert_eq!(evm.ctx().block().number(), U256::from(123)); + + // Test EVM Context Tx mutation + evm.ctx_mut().tx_mut().set_nonce(99); + assert_eq!(evm.ctx().tx().nonce(), 99); + + // Test EVM Context Cfg mutation + evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN; + assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN); + + // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env + let tx_env = evm.ctx().tx_clone(); + evm.ctx_mut().set_tx(tx_env); + let evm_env = evm.ctx().evm_clone(); + evm.ctx_mut().set_evm(evm_env); + } + + #[test] + fn from_any_rpc_transaction_for_op() { + let from = Address::random(); + let signed_tx = make_signed_eip1559(); + + // Build the eth TxEnv to compare against op base + let rpc_tx = RpcTransaction::from_transaction( + Recovered::new_unchecked(signed_tx.into(), from), + TransactionInfo::default(), + ); + let any_tx = >::from(rpc_tx); + let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap(); + + let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(op_tx_env.base, expected_base); + } + + #[test] + fn from_any_rpc_transaction_for_op_deposit() { + let from = Address::random(); + let source_hash = B256::random(); + let deposit = TxDeposit { + source_hash, + from, + to: TxKind::Call(Address::with_last_byte(0xCC)), + mint: 1111, + value: U256::from(200), + gas_limit: 21000, + is_system_transaction: true, + input: Default::default(), + }; + + // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as + // AnyRpcTransaction. + let op_rpc_tx = OpRpcTransaction::from_transaction( + Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from), + OpTransactionInfo::default(), + ); + let json = serde_json::to_value(&op_rpc_tx).unwrap(); + let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap(); + + let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap(); + assert_eq!(op_tx_env.base.caller, from); + assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC))); + assert_eq!(op_tx_env.base.value, U256::from(200)); + assert_eq!(op_tx_env.base.gas_limit, 21000); + assert_eq!(op_tx_env.deposit.source_hash, source_hash); + assert_eq!(op_tx_env.deposit.mint, Some(1111)); + assert!(op_tx_env.deposit.is_system_transaction); + } + } } diff --git a/crates/evm/core/src/evm/mod.rs b/crates/evm/core/src/evm/mod.rs index 708226be003a2..fc9e9e7d2810f 100644 --- a/crates/evm/core/src/evm/mod.rs +++ b/crates/evm/core/src/evm/mod.rs @@ -10,14 +10,11 @@ use alloy_evm::{ EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, precompiles::PrecompilesMap, }; use alloy_network::{Ethereum, Network}; -use alloy_op_evm::OpEvmFactory; use alloy_primitives::{Address, Signature, U256}; use alloy_rlp::Decodable; use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt}; use foundry_config::FromEvmVersion; use foundry_fork_db::{DatabaseError, ForkBlockEnv}; -use op_alloy_network::Optimism; -use op_revm::OpHaltReason; use revm::{ Database, context::{ @@ -36,10 +33,12 @@ use tempo_evm::evm::TempoEvmFactory; use tempo_revm::TempoHaltReason; pub mod eth; +#[cfg(feature = "optimism")] pub mod op; pub mod tempo; pub use eth::*; +#[cfg(feature = "optimism")] pub use op::*; pub use tempo::*; @@ -75,13 +74,6 @@ impl FoundryEvmNetwork for TempoEvmNetwork { type EvmFactory = TempoEvmFactory; } -#[derive(Clone, Copy, Debug, Default)] -pub struct OpEvmNetwork; -impl FoundryEvmNetwork for OpEvmNetwork { - type Network = Optimism; - type EvmFactory = OpEvmFactory; -} - /// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`]. pub type EvmFactoryFor = ::EvmFactory; pub type FoundryContextFor<'db, FEN> = @@ -249,15 +241,6 @@ impl IntoInstructionResult for HaltReason { } } -impl IntoInstructionResult for OpHaltReason { - fn into_instruction_result(self) -> InstructionResult { - match self { - Self::Base(eth) => eth.into(), - Self::FailedDeposit => InstructionResult::Stop, - } - } -} - impl IntoInstructionResult for TempoHaltReason { fn into_instruction_result(self) -> InstructionResult { match self { diff --git a/crates/evm/core/src/evm/op.rs b/crates/evm/core/src/evm/op.rs index cb8bf272d9a05..efb74ad3abf50 100644 --- a/crates/evm/core/src/evm/op.rs +++ b/crates/evm/core/src/evm/op.rs @@ -1,6 +1,7 @@ use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap}; use alloy_op_evm::{OpEvm, OpEvmContext, OpEvmFactory, OpTx}; use foundry_fork_db::DatabaseError; +use op_alloy_network::Optimism; use op_revm::{OpEvm as RevmEvm, OpHaltReason, OpSpecId, OpTransactionError, handler::OpHandler}; use revm::{ context::{ @@ -10,16 +11,33 @@ use revm::{ handler::{EthFrame, EvmTr, FrameResult, Handler, instructions::EthInstructions}, inspector::InspectorHandler, interpreter::{ - FrameInput, SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit, + FrameInput, InstructionResult, SharedMemory, interpreter::EthInterpreter, + interpreter_action::FrameInit, }, }; use crate::{ FoundryContextExt, FoundryInspectorExt, backend::{DatabaseExt, JournaledState}, - evm::{FoundryEvmFactory, NestedEvm}, + evm::{FoundryEvmFactory, FoundryEvmNetwork, IntoInstructionResult, NestedEvm}, }; +#[derive(Clone, Copy, Debug, Default)] +pub struct OpEvmNetwork; +impl FoundryEvmNetwork for OpEvmNetwork { + type Network = Optimism; + type EvmFactory = OpEvmFactory; +} + +impl IntoInstructionResult for OpHaltReason { + fn into_instruction_result(self) -> InstructionResult { + match self { + Self::Base(eth) => eth.into(), + Self::FailedDeposit => InstructionResult::Stop, + } + } +} + type OpEvmHandler<'db, I> = OpHandler, EVMError, EthFrame>; diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index aefa0e2ee9741..2284823047ca6 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -212,13 +212,18 @@ pub struct ForkDbStateSnapshot { } impl ForkDbStateSnapshot { - fn get_storage(&self, address: Address, index: U256) -> Option { - self.local - .cache - .accounts - .get(&address) - .and_then(|account| account.storage.get(&index)) - .copied() + /// Lookup storage in `state_snapshot`, then fall back to the backend (remote RPC). + fn storage_from_snapshot_or_backend( + &self, + address: Address, + index: U256, + ) -> Result { + // Check state_snapshot.storage first (data fetched by SharedBackend / disk cache). + if let Some(val) = self.state_snapshot.storage.get(&address).and_then(|s| s.get(&index)) { + return Ok(*val); + } + // Fall back to the underlying backend (SharedBackend → remote RPC). + DatabaseRef::storage_ref(&self.local, address, index) } } @@ -250,15 +255,9 @@ impl DatabaseRef for ForkDbStateSnapshot { match self.local.cache.accounts.get(&address) { Some(account) => match account.storage.get(&index) { Some(entry) => Ok(*entry), - None => match self.get_storage(address, index) { - None => DatabaseRef::storage_ref(&self.local, address, index), - Some(storage) => Ok(storage), - }, - }, - None => match self.get_storage(address, index) { - None => DatabaseRef::storage_ref(&self.local, address, index), - Some(storage) => Ok(storage), + None => self.storage_from_snapshot_or_backend(address, index), }, + None => self.storage_from_snapshot_or_backend(address, index), } } @@ -303,4 +302,28 @@ mod tests { assert!(loaded.is_some()); assert_eq!(loaded.unwrap(), info); } + + /// Verifies that `ForkDbStateSnapshot::storage_ref` reads from `state_snapshot.storage` + /// when the slot is missing from `local.cache.accounts`. Without this lookup the call + /// would fall through to the backend and return the unrelated remote value. + #[tokio::test(flavor = "multi_thread")] + async fn fork_db_state_snapshot_reads_storage_from_snapshot() { + let rpc = foundry_test_utils::rpc::next_http_rpc_endpoint(); + let provider = get_http_provider(rpc.clone()); + let meta = BlockchainDbMeta::new(BlockEnv::default(), rpc); + let db = BlockchainDb::new(meta, None); + let backend = SharedBackend::spawn_backend(Arc::new(provider), db, None).await; + + let address = Address::random(); + let slot = U256::from(42u64); + let expected = U256::from(0xdeadbeefu64); + + let mut state_snapshot = StateSnapshot::default(); + state_snapshot.storage.entry(address).or_default().insert(slot, expected); + + let snapshot = ForkDbStateSnapshot { local: CacheDB::new(backend), state_snapshot }; + + let got = DatabaseRef::storage_ref(&snapshot, address, slot).unwrap(); + assert_eq!(got, expected); + } } diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 1b2201a9b8b84..c2edbb9dfd33b 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -5,6 +5,9 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "optimism")] +use op_alloy_rpc_types as _; + use crate::constants::DEFAULT_CREATE2_DEPLOYER; use alloy_primitives::{Address, map::HashMap}; use auto_impl::auto_impl; diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 82efd4a1aaaaa..ab68eb08821e8 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -137,8 +137,12 @@ impl EvmOpts { /// [`NetworkConfigs::with_chain_id`] to auto-enable the correct network /// (e.g. Tempo, OP Stack) based on the chain ID. pub async fn infer_network_from_fork(&mut self) { + #[cfg(feature = "optimism")] + let already_op = self.networks.is_optimism(); + #[cfg(not(feature = "optimism"))] + let already_op = false; if !self.networks.is_tempo() - && !self.networks.is_optimism() + && !already_op && let Some(ref fork_url) = self.fork_url && let Ok(provider) = self.fork_provider_with_url::(fork_url) && let Ok(chain_id) = provider.get_chain_id().await @@ -474,6 +478,7 @@ mod tests { // Plain anvil (chain id 31337) without tempo flag -> Ethereum (no network flags set). assert!(!evm_opts.networks.is_tempo()); + #[cfg(feature = "optimism")] assert!(!evm_opts.networks.is_optimism()); assert!(!evm_opts.networks.is_celo()); assert_eq!(evm_opts.networks, NetworkConfigs::default()); diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index d2d7b077ee9f0..758604564726e 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -25,3 +25,7 @@ semver.workspace = true tracing.workspace = true rayon.workspace = true solar.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism", "foundry-evm-core/optimism"] diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 6bf7163d6e023..dd5138f532074 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -62,3 +62,16 @@ serde.workspace = true uuid.workspace = true rayon.workspace = true tokio.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-evm-core/optimism", + "foundry-evm-hardforks/optimism", + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-cheatcodes/optimism", + "foundry-evm-coverage/optimism", + "foundry-evm-fuzz/optimism", + "foundry-evm-traces/optimism", +] diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 33152b73dda3c..1932a834ab397 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -17,7 +17,7 @@ use foundry_evm_core::{ use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ BaseCounterExample, BasicTxDetails, CallDetails, CounterExample, FuzzCase, FuzzError, - FuzzFixtures, FuzzTestResult, + FuzzFixtures, FuzzRunMetadata, FuzzTestResult, strategies::{EvmFuzzState, fuzz_calldata, fuzz_calldata_from_state}, }; use foundry_evm_traces::SparsedTraceArena; @@ -71,6 +71,8 @@ struct WorkerState { runs: u32, /// Failure reason if this worker failed failure: Option, + /// Fuzz run metadata that produced the failure. + failure_run: Option, /// Last run timestamp in milliseconds /// /// Used to identify which worker ran last and collect its traces and call breakpoints @@ -93,6 +95,7 @@ impl WorkerState { deprecated_cheatcodes: HashMap::default(), runs: 0, failure: None, + failure_run: None, last_run_timestamp: 0, failed_corpus_replays: 0, } @@ -196,8 +199,14 @@ impl FuzzedExecutor { config: FuzzConfig, persisted_failure: Option, ) -> Self { - let max_workers = - if config.runs == 0 { 0 } else { Ord::max(1, config.runs / MIN_RUNS_PER_WORKER) }; + let run_limit = if config.run.is_some() { 1 } else { config.runs }; + let max_workers = if run_limit == 0 { + 0 + } else if config.run.is_some() { + 1 + } else { + Ord::max(1, run_limit / MIN_RUNS_PER_WORKER) + }; let num_workers = Ord::min(rayon::current_num_threads(), max_workers as usize); Self { executor_f: executor, runner, sender, config, persisted_failure, num_workers } } @@ -221,8 +230,9 @@ impl FuzzedExecutor { ) -> Result { let shared_state = SharedFuzzState::new(state, self.config.timeout, early_exit.clone()); - debug!(n = self.num_workers, "spawning workers"); - let workers = (0..self.num_workers) + let worker_ids = self.worker_ids(); + debug!(n = worker_ids.len(), "spawning workers"); + let workers = worker_ids .into_par_iter() .map(|worker_id| { let _guard = tokio_handle.enter(); @@ -364,8 +374,14 @@ impl FuzzedExecutor { } else { vec![] }; + let fuzz = failed_worker.failure_run.unwrap_or_default(); result.counterexample = Some(CounterExample::Single( - BaseCounterExample::from_fuzz_call(calldata, args, call.traces), + BaseCounterExample::from_fuzz_call(calldata, args, call.traces) + .with_fuzz_metadata(FuzzRunMetadata::new( + fuzz.seed.or(self.config.seed), + fuzz.run, + fuzz.worker, + )), )); } Some(TestCaseError::Reject(reason)) => { @@ -453,16 +469,7 @@ impl FuzzedExecutor { runner_config.cases = worker_runs; let mut runner = if let Some(seed) = self.config.seed { - // For deterministic parallel fuzzing, derive a unique seed for each worker - let worker_seed = if worker_id == 0 { - // Master worker uses the provided seed as is. - seed - } else { - // Derive a worker-specific seed using keccak256(seed || worker_id) - let seed_data = - [&seed.to_be_bytes::<32>()[..], &worker_id.to_be_bytes()[..]].concat(); - U256::from_be_bytes(keccak256(seed_data).0) - }; + let worker_seed = Self::fuzz_worker_seed(seed, worker_id); trace!(target: "forge::test", ?worker_seed, "deterministic seed for worker {worker_id}"); let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &worker_seed.to_be_bytes::<32>()); TestRunner::new_with_rng(runner_config, rng) @@ -470,11 +477,25 @@ impl FuzzedExecutor { TestRunner::new(runner_config) }; - let mut persisted_failure = self.persisted_failure.as_ref().filter(|_| worker_id == 0); + if let Some(target_run) = self.config.run { + for _ in 1..target_run { + if let Err(err) = corpus.new_input(&mut runner, &shared_state.state, func) { + worker.failure = Some(TestCaseError::fail(format!( + "failed to generate fuzzed input in worker {}: {err}", + worker.id + ))); + shared_state.try_claim_failure(worker_id); + return Ok(worker); + } + } + } + + let mut persisted_failure = + self.persisted_failure.as_ref().filter(|_| worker_id == 0 && self.config.run.is_none()); // Offset to stagger corpus syncs across workers; so that workers don't sync at the same // time. - let sync_offset = worker_id as u32 * 100; + let sync_offset = (worker_id as u32).saturating_mul(100); let sync_threshold = SYNC_INTERVAL + sync_offset; let mut runs_since_sync = sync_threshold; // Always sync at the start. let mut last_metrics_report = Instant::now(); @@ -483,11 +504,27 @@ impl FuzzedExecutor { // 2. Worker hasn't reached its specific run limit 'stop: while shared_state.should_continue() && worker.runs < worker_runs { // If counterexample recorded, replay it first, without incrementing runs. - let input = if worker_id == 0 + let (input, fuzz_run) = if worker_id == 0 && let Some(failure) = persisted_failure.take() && failure.calldata.get(..4).is_some_and(|selector| func.selector() == selector) { - failure.calldata.clone() + let seed = failure.fuzz.seed.or(self.config.seed); + if let Some(cheats) = executor.inspector_mut().cheatcodes.as_mut() + && let Some(seed) = seed + { + let run = failure.fuzz.run.unwrap_or(1); + let worker = failure.fuzz.worker.unwrap_or(worker_id as u32) as usize; + cheats.set_seed(Self::fuzz_run_seed(seed, worker, run)); + } + + ( + failure.calldata.clone(), + Some(FuzzRunMetadata::new( + seed, + failure.fuzz.run, + Some(failure.fuzz.worker.unwrap_or(worker_id as u32)), + )), + ) } else { runs_since_sync += 1; if runs_since_sync >= sync_threshold { @@ -503,13 +540,14 @@ impl FuzzedExecutor { runs_since_sync = 0; } + let fuzz_run = self.config.run.unwrap_or(worker.runs + 1); if let Some(cheats) = executor.inspector_mut().cheatcodes.as_mut() && let Some(seed) = self.config.seed { - cheats.set_seed(seed.wrapping_add(U256::from(worker.runs))); + cheats.set_seed(Self::fuzz_run_seed(seed, worker_id, fuzz_run)); } - match corpus.new_input(&mut runner, &shared_state.state, func) { + let input = match corpus.new_input(&mut runner, &shared_state.state, func) { Ok(input) => input, Err(err) => { worker.failure = Some(TestCaseError::fail(format!( @@ -519,13 +557,24 @@ impl FuzzedExecutor { shared_state.try_claim_failure(worker_id); break 'stop; } - } + }; + + ( + input, + Some(FuzzRunMetadata::new( + self.config.seed, + Some(fuzz_run), + Some(worker_id as u32), + )), + ) }; let mut inc_runs = || { let total_runs = shared_state.increment_runs(); debug_assert!( - shared_state.timer.is_enabled() || total_runs <= self.config.runs, + shared_state.timer.is_enabled() + || total_runs + <= if self.config.run.is_some() { 1 } else { self.config.runs }, "worker runs were not distributed correctly" ); worker.runs += 1; @@ -595,6 +644,7 @@ impl FuzzedExecutor { .. }) => { inc_runs(); + worker.failure_run = fuzz_run; // Only classify magic skip payloads when the revert originates from the // cheatcode address. @@ -656,7 +706,7 @@ impl FuzzedExecutor { /// Determines the number of runs per worker. const fn runs_per_worker(&self, worker_id: usize) -> u32 { let worker_id = worker_id as u32; - let total_runs = self.config.runs; + let total_runs = if self.config.run.is_some() { 1 } else { self.config.runs }; let n = self.num_workers as u32; let runs = total_runs / n; let remainder = total_runs % n; @@ -664,4 +714,29 @@ impl FuzzedExecutor { // assuming `worker_id` is in `0..n`. if worker_id < remainder { runs + 1 } else { runs } } + + /// Returns the worker IDs to execute. + fn worker_ids(&self) -> Vec { + if self.config.run.is_some() { + vec![self.config.worker.unwrap_or(0) as usize] + } else { + (0..self.num_workers).collect() + } + } + + /// Derives the deterministic RNG seed for a fuzz worker. + fn fuzz_worker_seed(seed: U256, worker_id: usize) -> U256 { + if worker_id == 0 { + seed + } else { + let worker_id = worker_id as u32; + let seed_data = [&seed.to_be_bytes::<32>()[..], &worker_id.to_be_bytes()[..]].concat(); + U256::from_be_bytes(keccak256(seed_data).0) + } + } + + /// Derives the deterministic RNG seed for cheatcode randomness in a worker-local run. + fn fuzz_run_seed(seed: U256, worker_id: usize, run: u32) -> U256 { + Self::fuzz_worker_seed(seed, worker_id).wrapping_add(U256::from(run.saturating_sub(1))) + } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 27d8e6a0ed588..e02cdbc393ee6 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -736,7 +736,7 @@ impl<'a, FEN: FoundryEvmNetwork> InvariantExecutor<'a, FEN> { if !msg.is_empty() { msg.push_str(", "); } - msg.push_str(&format!("{}", &corpus_manager.metrics)); + msg.push_str(&format!("{}", corpus_manager.metrics)); } progress.set_message(msg); } diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 62e4e80a73674..5629b17d936da 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -50,3 +50,12 @@ rand.workspace = true serde.workspace = true thiserror.workspace = true tracing.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm-core/optimism", + "foundry-evm-coverage/optimism", + "foundry-evm-traces/optimism", +] diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 44d71fb6deee3..9c3e7d179c7f6 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -33,6 +33,27 @@ pub use strategies::LiteralMaps; mod inspector; pub use inspector::Fuzzer; +/// Metadata needed to reproduce a fuzz run. +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] +pub struct FuzzRunMetadata { + /// Seed used for the worker's input stream. + #[serde(default, rename = "fuzz_seed", skip_serializing_if = "Option::is_none")] + pub seed: Option, + /// 1-based run inside the worker's input stream. + #[serde(default, rename = "fuzz_run", skip_serializing_if = "Option::is_none")] + pub run: Option, + /// Worker that generated the input stream. + #[serde(default, rename = "fuzz_worker", skip_serializing_if = "Option::is_none")] + pub worker: Option, +} + +impl FuzzRunMetadata { + /// Creates metadata for reproducing a fuzz run. + pub const fn new(seed: Option, run: Option, worker: Option) -> Self { + Self { seed, run, worker } + } +} + /// Details of a transaction generated by fuzz strategy for fuzzing a target. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BasicTxDetails { @@ -102,6 +123,9 @@ pub struct BaseCounterExample { /// Whether to display sequence as solidity. #[serde(skip)] pub show_solidity: bool, + /// Fuzz metadata needed to reproduce this counterexample. + #[serde(flatten)] + pub fuzz: FuzzRunMetadata, } impl BaseCounterExample { @@ -137,6 +161,7 @@ impl BaseCounterExample { ), traces, show_solidity, + fuzz: FuzzRunMetadata::default(), }; } } @@ -154,6 +179,7 @@ impl BaseCounterExample { raw_args: None, traces, show_solidity: false, + fuzz: FuzzRunMetadata::default(), } } @@ -176,8 +202,15 @@ impl BaseCounterExample { raw_args: Some(foundry_common::fmt::format_tokens_raw(&args).format(", ").to_string()), traces, show_solidity: false, + fuzz: FuzzRunMetadata::default(), } } + + /// Sets fuzz metadata for reproducing this counterexample. + pub const fn with_fuzz_metadata(mut self, fuzz: FuzzRunMetadata) -> Self { + self.fuzz = fuzz; + self + } } impl fmt::Display for BaseCounterExample { @@ -229,7 +262,7 @@ 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)? } if let Some(args) = &self.args { diff --git a/crates/evm/hardforks/Cargo.toml b/crates/evm/hardforks/Cargo.toml index 9bf318028487a..68f6fb23bab07 100644 --- a/crates/evm/hardforks/Cargo.toml +++ b/crates/evm/hardforks/Cargo.toml @@ -16,10 +16,14 @@ workspace = true [dependencies] alloy-chains.workspace = true alloy-hardforks = { workspace = true, features = ["serde"] } -alloy-op-hardforks = { workspace = true, features = ["serde"] } +alloy-op-hardforks = { workspace = true, features = ["serde"], optional = true } alloy-rpc-types.workspace = true -op-revm.workspace = true +op-revm = { workspace = true, optional = true } revm.workspace = true serde = { workspace = true, features = ["derive"] } tempo-chainspec.workspace = true foundry-compilers.workspace = true + +[features] +default = ["optimism"] +optimism = ["dep:alloy-op-hardforks", "dep:op-revm"] diff --git a/crates/evm/hardforks/src/lib.rs b/crates/evm/hardforks/src/lib.rs index 8a29ebb7af4ec..a8e0d51738263 100644 --- a/crates/evm/hardforks/src/lib.rs +++ b/crates/evm/hardforks/src/lib.rs @@ -8,11 +8,13 @@ use std::str::FromStr; use alloy_chains::Chain; use alloy_rpc_types::BlockNumberOrTag; use foundry_compilers::artifacts::EvmVersion; +#[cfg(feature = "optimism")] use op_revm::OpSpecId; use revm::primitives::hardfork::SpecId; use serde::{Deserialize, Serialize}; pub use alloy_hardforks::EthereumHardfork; +#[cfg(feature = "optimism")] pub use alloy_op_hardforks::OpHardfork; pub use tempo_chainspec::hardfork::TempoHardfork; @@ -20,6 +22,7 @@ pub use tempo_chainspec::hardfork::TempoHardfork; #[serde(into = "String")] pub enum FoundryHardfork { Ethereum(EthereumHardfork), + #[cfg(feature = "optimism")] Optimism(OpHardfork), Tempo(TempoHardfork), } @@ -28,6 +31,7 @@ impl From for String { fn from(fork: FoundryHardfork) -> Self { match fork { FoundryHardfork::Ethereum(h) => format!("{h}"), + #[cfg(feature = "optimism")] FoundryHardfork::Optimism(h) => format!("optimism:{h}"), FoundryHardfork::Tempo(h) => format!("tempo:{h}"), } @@ -64,6 +68,7 @@ impl FromStr for FoundryHardfork { .map(Self::Ethereum) .map_err(|_| format!("unknown ethereum hardfork '{fork_raw}'")), + #[cfg(feature = "optimism")] "op" | "optimism" => OpHardfork::from_str(&fork) .map(Self::Optimism) .map_err(|_| format!("unknown optimism hardfork '{fork_raw}'")), @@ -83,6 +88,7 @@ impl FoundryHardfork { Self::Ethereum(h) } + #[cfg(feature = "optimism")] pub const fn optimism(h: OpHardfork) -> Self { Self::Optimism(h) } @@ -95,6 +101,7 @@ impl FoundryHardfork { pub fn name(&self) -> String { match self { Self::Ethereum(h) => format!("{h}"), + #[cfg(feature = "optimism")] Self::Optimism(h) => format!("{h}"), Self::Tempo(h) => format!("{h}"), } @@ -106,6 +113,7 @@ impl FoundryHardfork { pub const fn namespace(&self) -> Option<&'static str> { match self { Self::Ethereum(_) => None, + #[cfg(feature = "optimism")] Self::Optimism(_) => Some("optimism"), Self::Tempo(_) => Some("tempo"), } @@ -119,6 +127,7 @@ impl FoundryHardfork { if let Some(fork) = EthereumHardfork::from_chain_and_timestamp(chain, timestamp) { return Some(Self::Ethereum(fork)); } + #[cfg(feature = "optimism")] if let Some(fork) = OpHardfork::from_chain_and_timestamp(chain, timestamp) { return Some(Self::Optimism(fork)); } @@ -143,12 +152,14 @@ impl From for EthereumHardfork { } } +#[cfg(feature = "optimism")] impl From for FoundryHardfork { fn from(value: OpHardfork) -> Self { Self::Optimism(value) } } +#[cfg(feature = "optimism")] impl From for OpHardfork { fn from(fork: FoundryHardfork) -> Self { match fork { @@ -177,12 +188,14 @@ impl From for SpecId { fn from(fork: FoundryHardfork) -> Self { match fork { FoundryHardfork::Ethereum(hardfork) => spec_id_from_ethereum_hardfork(hardfork), + #[cfg(feature = "optimism")] FoundryHardfork::Optimism(hardfork) => spec_id_from_optimism_hardfork(hardfork).into(), FoundryHardfork::Tempo(hardfork) => hardfork.into(), } } } +#[cfg(feature = "optimism")] impl From for OpSpecId { fn from(fork: FoundryHardfork) -> Self { match fork { @@ -223,6 +236,7 @@ pub fn spec_id_from_ethereum_hardfork(hardfork: EthereumHardfork) -> SpecId { } /// Map an `OptimismHardfork` enum into its corresponding `OpSpecId`. +#[cfg(feature = "optimism")] pub fn spec_id_from_optimism_hardfork(hardfork: OpHardfork) -> OpSpecId { match hardfork { OpHardfork::Bedrock => OpSpecId::BEDROCK, @@ -265,6 +279,7 @@ impl FromEvmVersion for SpecId { } } +#[cfg(feature = "optimism")] impl FromEvmVersion for OpSpecId { fn from_evm_version(version: EvmVersion) -> Self { match version { @@ -324,16 +339,6 @@ mod tests { assert_eq!(spec_id_from_ethereum_hardfork(EthereumHardfork::Osaka), SpecId::OSAKA); } - #[test] - fn test_optimism_spec_id_mapping() { - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK); - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH); - - // Test latest hardforks - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE); - assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP); - } - #[test] fn test_tempo_spec_id_mapping() { assert_eq!(SpecId::from(TempoHardfork::Genesis), SpecId::OSAKA); @@ -371,25 +376,40 @@ mod tests { } #[test] - fn test_from_chain_and_timestamp_op_mainnet() { - let op_chain_id = 10; - assert!(matches!( - FoundryHardfork::from_chain_and_timestamp(op_chain_id, u64::MAX), - Some(FoundryHardfork::Optimism(_)) - )); + fn test_from_chain_and_timestamp_unknown_chain() { + assert_eq!(FoundryHardfork::from_chain_and_timestamp(999999, 0), None); } - #[test] - fn test_from_chain_and_timestamp_base() { - let base_chain_id = 8453; - assert!(matches!( - FoundryHardfork::from_chain_and_timestamp(base_chain_id, u64::MAX), - Some(FoundryHardfork::Optimism(_)) - )); - } + #[cfg(feature = "optimism")] + mod optimism { + use super::*; - #[test] - fn test_from_chain_and_timestamp_unknown_chain() { - assert_eq!(FoundryHardfork::from_chain_and_timestamp(999999, 0), None); + #[test] + fn test_optimism_spec_id_mapping() { + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Bedrock), OpSpecId::BEDROCK); + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Regolith), OpSpecId::REGOLITH); + + // Test latest hardforks + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Holocene), OpSpecId::HOLOCENE); + assert_eq!(spec_id_from_optimism_hardfork(OpHardfork::Interop), OpSpecId::INTEROP); + } + + #[test] + fn test_from_chain_and_timestamp_op_mainnet() { + let op_chain_id = 10; + assert!(matches!( + FoundryHardfork::from_chain_and_timestamp(op_chain_id, u64::MAX), + Some(FoundryHardfork::Optimism(_)) + )); + } + + #[test] + fn test_from_chain_and_timestamp_base() { + let base_chain_id = 8453; + assert!(matches!( + FoundryHardfork::from_chain_and_timestamp(base_chain_id, u64::MAX), + Some(FoundryHardfork::Optimism(_)) + )); + } } } diff --git a/crates/evm/networks/Cargo.toml b/crates/evm/networks/Cargo.toml index a63ed34ba61cf..00c9abf0f90f7 100644 --- a/crates/evm/networks/Cargo.toml +++ b/crates/evm/networks/Cargo.toml @@ -19,7 +19,7 @@ foundry-evm-hardforks.workspace = true alloy-chains.workspace = true alloy-eips.workspace = true alloy-evm.workspace = true -alloy-op-hardforks.workspace = true +alloy-op-hardforks = { workspace = true, optional = true } alloy-primitives = { workspace = true, features = [ "serde", "getrandom", @@ -43,4 +43,8 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } serde.workspace = true [dev-dependencies] -serde_json.workspace = true \ No newline at end of file +serde_json.workspace = true + +[features] +default = ["optimism"] +optimism = ["dep:alloy-op-hardforks", "foundry-evm-hardforks/optimism"] diff --git a/crates/evm/networks/src/lib.rs b/crates/evm/networks/src/lib.rs index 4672deff353d0..384cee5a7bed3 100644 --- a/crates/evm/networks/src/lib.rs +++ b/crates/evm/networks/src/lib.rs @@ -11,7 +11,6 @@ use alloy_chains::{ }; use alloy_eips::eip1559::BaseFeeParams; use alloy_evm::precompiles::PrecompilesMap; -use alloy_op_hardforks::{OpChainHardforks, OpHardforks}; use alloy_primitives::{Address, ChainId, map::AddressHashMap}; use clap::Parser; use foundry_evm_hardforks::FoundryHardfork; @@ -20,20 +19,52 @@ use std::collections::BTreeMap; pub mod celo; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, clap::ValueEnum)] +#[cfg(feature = "optimism")] +mod optimism; + +#[derive( + Clone, + Copy, + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + clap::ValueEnum, +)] #[serde(rename_all = "lowercase")] #[clap(rename_all = "lowercase")] pub enum NetworkVariant { #[default] Ethereum, + #[cfg(feature = "optimism")] Optimism, Tempo, } +impl std::str::FromStr for NetworkVariant { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "ethereum" => Ok(Self::Ethereum), + #[cfg(feature = "optimism")] + "optimism" => Ok(Self::Optimism), + "tempo" => Ok(Self::Tempo), + _ => Err(format!("unknown network variant: {s}")), + } + } +} + impl NetworkVariant { pub const fn name(&self) -> &'static str { match self { Self::Ethereum => "ethereum", + #[cfg(feature = "optimism")] Self::Optimism => "optimism", Self::Tempo => "tempo", } @@ -50,31 +81,39 @@ impl From for NetworkVariant { fn from(chain_id: ChainId) -> Self { let chain = Chain::from_id(chain_id); if chain.is_tempo() { - Self::Tempo - } else if chain.is_optimism() { - Self::Optimism - } else { - Self::Ethereum + return Self::Tempo; + } + #[cfg(feature = "optimism")] + if chain.is_optimism() { + return Self::Optimism; } + Self::Ethereum } } -#[derive(Clone, Debug, Default, Parser, Serialize, Deserialize, Copy, PartialEq, Eq)] +#[derive(Clone, Debug, Default, Parser, Deserialize, Copy, PartialEq, Eq)] pub struct NetworkConfigs { /// Enable a specific network family. - #[arg(help_heading = "Networks", long, short, num_args = 1, value_name = "NETWORK", value_enum, conflicts_with_all = ["celo", "optimism", "tempo"])] - #[serde(skip_serializing_if = "Option::is_none")] - network: Option, + #[arg(help_heading = "Networks", long, short, num_args = 1, value_name = "NETWORK", value_enum, conflicts_with_all = ["celo", "tempo"])] + #[cfg_attr(feature = "optimism", arg(conflicts_with = "optimism"))] + #[serde(default)] + pub(crate) network: Option, /// Enable Celo network features. - #[arg(help_heading = "Networks", long, conflicts_with_all = ["network", "optimism", "tempo"])] + #[arg(help_heading = "Networks", long, conflicts_with_all = ["network", "tempo"])] + #[cfg_attr(feature = "optimism", arg(conflicts_with = "optimism"))] celo: bool, /// Enable Optimism network features (deprecated: use --network optimism). + #[cfg(feature = "optimism")] #[arg(long, hide = true, conflicts_with_all = ["network", "celo", "tempo"])] - // Skipped from configs (forge) as there is no feature to be added yet. - #[serde(skip)] - optimism: bool, + // Deserialize-only legacy alias: accepted in foundry.toml but never serialized — the + // canonical form is `network = "optimism"`. + #[serde(default)] + pub(crate) optimism: bool, /// Enable Tempo network features (deprecated: use --network tempo). - #[arg(long, hide = true, conflicts_with_all = ["network", "celo", "optimism"])] + #[arg(long, hide = true, conflicts_with_all = ["network", "celo"])] + #[cfg_attr(feature = "optimism", arg(conflicts_with = "optimism"))] + // Deserialize-only legacy alias: accepted in foundry.toml but never serialized — the + // canonical form is `network = "tempo"`. #[serde(default)] tempo: bool, /// Whether to bypass prevrandao. @@ -83,11 +122,22 @@ pub struct NetworkConfigs { bypass_prevrandao: bool, } -impl NetworkConfigs { - pub fn with_optimism() -> Self { - Self { network: Some(NetworkVariant::Optimism), optimism: true, ..Default::default() } +// Custom `Serialize` impl: always emits the *resolved* network as the canonical +// `network = "..."` field, and never emits the legacy `tempo` / `optimism` aliases. This avoids +// confusing output like `network = "tempo"` next to `tempo = false`, and ensures `tempo = true` +// in foundry.toml round-trips as `network = "tempo"`. +impl Serialize for NetworkConfigs { + fn serialize(&self, serializer: S) -> Result { + use serde::ser::SerializeStruct; + let mut s = serializer.serialize_struct("NetworkConfigs", 3)?; + s.serialize_field("network", &self.resolved_network())?; + s.serialize_field("celo", &self.celo)?; + s.serialize_field("bypass_prevrandao", &self.bypass_prevrandao)?; + s.end() } +} +impl NetworkConfigs { pub fn with_celo() -> Self { Self { celo: true, ..Default::default() } } @@ -96,11 +146,7 @@ impl NetworkConfigs { Self { network: Some(NetworkVariant::Tempo), tempo: true, ..Default::default() } } - pub fn is_optimism(&self) -> bool { - matches!(self.resolved_network(), Some(NetworkVariant::Optimism)) - } - - pub fn is_tempo(&self) -> bool { + pub const fn is_tempo(&self) -> bool { matches!(self.resolved_network(), Some(NetworkVariant::Tempo)) } @@ -109,14 +155,18 @@ impl NetworkConfigs { } /// Returns the resolved network variant, folding legacy flags. - fn resolved_network(&self) -> Option { - self.network.or(if self.optimism { - Some(NetworkVariant::Optimism) - } else if self.tempo { - Some(NetworkVariant::Tempo) - } else { - None - }) + const fn resolved_network(&self) -> Option { + if let Some(n) = self.network { + return Some(n); + } + #[cfg(feature = "optimism")] + if self.optimism { + return Some(NetworkVariant::Optimism); + } + if self.tempo { + return Some(NetworkVariant::Tempo); + } + None } /// Returns the name of the currently active non-Ethereum network, or `None` for plain Ethereum. @@ -132,16 +182,12 @@ impl NetworkConfigs { /// For Optimism networks, returns Canyon parameters if the Canyon hardfork is active /// at the given timestamp, otherwise returns pre-Canyon parameters. pub fn base_fee_params(&self, timestamp: u64) -> BaseFeeParams { + #[cfg(feature = "optimism")] if self.is_optimism() { - let op_hardforks = OpChainHardforks::op_mainnet(); - if op_hardforks.is_canyon_active_at_timestamp(timestamp) { - BaseFeeParams::optimism_canyon() - } else { - BaseFeeParams::optimism() - } - } else { - BaseFeeParams::ethereum() + return self.op_base_fee_params(timestamp); } + let _ = timestamp; + BaseFeeParams::ethereum() } pub fn bypass_prevrandao(&self, chain_id: u64) -> bool { @@ -156,21 +202,23 @@ impl NetworkConfigs { pub fn with_chain_id(self, chain_id: u64) -> Self { let chain = Chain::from_id(chain_id); - if self.resolved_network().is_none() { - if chain.is_tempo() { - Self::with_tempo() - } else if chain.is_optimism() { - Self::with_optimism() + if self.resolved_network().is_some() { + return if !self.celo + && matches!(chain.named(), Some(NamedChain::Celo | NamedChain::CeloSepolia)) + { + Self::with_celo() } else { self - } - } else if !self.celo - && matches!(chain.named(), Some(NamedChain::Celo | NamedChain::CeloSepolia)) - { - Self::with_celo() - } else { - self + }; + } + if chain.is_tempo() { + return Self::with_tempo(); + } + #[cfg(feature = "optimism")] + if chain.is_optimism() { + return Self::with_optimism(); } + self } /// Validates `hardfork` against the current `NetworkConfigs` and, if consistent, returns an @@ -190,6 +238,7 @@ impl NetworkConfigs { let network = match hardfork { FoundryHardfork::Ethereum(_) => self, FoundryHardfork::Tempo(_) => Self::with_tempo(), + #[cfg(feature = "optimism")] FoundryHardfork::Optimism(_) => Self::with_optimism(), }; @@ -225,6 +274,21 @@ impl NetworkConfigs { } } +impl From for NetworkConfigs { + fn from(network: NetworkVariant) -> Self { + match network { + NetworkVariant::Ethereum => Self::default(), + NetworkVariant::Tempo => { + Self { network: Some(network), tempo: true, ..Default::default() } + } + #[cfg(feature = "optimism")] + NetworkVariant::Optimism => { + Self { network: Some(network), optimism: true, ..Default::default() } + } + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -236,17 +300,6 @@ mod tests { let via_new = NetworkConfigs { network: Some(NetworkVariant::Tempo), ..Default::default() }; let via_old = NetworkConfigs { tempo: true, ..Default::default() }; assert_eq!(via_new.is_tempo(), via_old.is_tempo()); - assert_eq!(via_new.is_optimism(), via_old.is_optimism()); - assert_eq!(via_new.active_network_name(), via_old.active_network_name()); - } - - #[test] - fn new_optimism_flag_equivalent_to_legacy() { - let via_new = - NetworkConfigs { network: Some(NetworkVariant::Optimism), ..Default::default() }; - let via_old = NetworkConfigs { optimism: true, ..Default::default() }; - assert_eq!(via_new.is_optimism(), via_old.is_optimism()); - assert_eq!(via_new.is_tempo(), via_old.is_tempo()); assert_eq!(via_new.active_network_name(), via_old.active_network_name()); } @@ -258,31 +311,11 @@ mod tests { assert_eq!(cfg.active_network_name(), Some("tempo")); } - #[test] - fn active_network_name_optimism() { - let cfg = NetworkConfigs::with_optimism(); - assert_eq!(cfg.active_network_name(), Some("optimism")); - } - #[test] fn active_network_name_default_is_none() { assert_eq!(NetworkConfigs::default().active_network_name(), None); } - // --- new flag takes precedence over legacy flag --- - - #[test] - fn new_flag_wins_over_legacy_when_both_set() { - // --network optimism --tempo: network field wins - let cfg = NetworkConfigs { - network: Some(NetworkVariant::Optimism), - tempo: true, - ..Default::default() - }; - assert!(cfg.is_optimism()); - assert!(!cfg.is_tempo()); - } - // --- Serde round-trip --- #[test] @@ -291,16 +324,6 @@ mod tests { let json = serde_json::to_string(&original).unwrap(); let restored: NetworkConfigs = serde_json::from_str(&json).unwrap(); assert!(restored.is_tempo()); - assert!(!restored.is_optimism()); - } - - #[test] - fn serde_roundtrip_optimism() { - let original = NetworkConfigs::with_optimism(); - let json = serde_json::to_string(&original).unwrap(); - let restored: NetworkConfigs = serde_json::from_str(&json).unwrap(); - assert!(restored.is_optimism()); - assert!(!restored.is_tempo()); } #[test] @@ -311,13 +334,71 @@ mod tests { assert!(cfg.is_tempo()); } + #[test] + fn serde_serializes_legacy_alias_as_canonical_network() { + // Legacy `tempo = true` should serialize as the canonical `network = "tempo"`, + // and the legacy `tempo` / `optimism` keys must not appear in the output. + let cfg = NetworkConfigs { tempo: true, ..Default::default() }; + let json = serde_json::to_value(cfg).unwrap(); + assert_eq!(json["network"], serde_json::json!("tempo")); + assert!(json.get("tempo").is_none(), "legacy `tempo` key should not be serialized"); + assert!(json.get("optimism").is_none(), "legacy `optimism` key should not be serialized"); + } + #[test] fn serde_new_network_field_deserialized() { let json_tempo = r#"{"network": "tempo", "celo": false, "bypass_prevrandao": false}"#; let cfg_tempo: NetworkConfigs = serde_json::from_str(json_tempo).unwrap(); assert!(cfg_tempo.is_tempo()); - let json_optimism = r#"{"network": "optimism", "celo": false, "bypass_prevrandao": false}"#; - let cfg_optimism: NetworkConfigs = serde_json::from_str(json_optimism).unwrap(); - assert!(cfg_optimism.is_optimism()); + } + + #[cfg(feature = "optimism")] + mod optimism { + use super::*; + + #[test] + fn new_optimism_flag_equivalent_to_legacy() { + let via_new = + NetworkConfigs { network: Some(NetworkVariant::Optimism), ..Default::default() }; + let via_old = NetworkConfigs { optimism: true, ..Default::default() }; + assert_eq!(via_new.is_optimism(), via_old.is_optimism()); + assert_eq!(via_new.is_tempo(), via_old.is_tempo()); + assert_eq!(via_new.active_network_name(), via_old.active_network_name()); + } + + #[test] + fn active_network_name_optimism() { + let cfg = NetworkConfigs::with_optimism(); + assert_eq!(cfg.active_network_name(), Some("optimism")); + } + + #[test] + fn new_flag_wins_over_legacy_when_both_set() { + // --network optimism --tempo: network field wins + let cfg = NetworkConfigs { + network: Some(NetworkVariant::Optimism), + tempo: true, + ..Default::default() + }; + assert!(cfg.is_optimism()); + assert!(!cfg.is_tempo()); + } + + #[test] + fn serde_roundtrip_optimism() { + let original = NetworkConfigs::with_optimism(); + let json = serde_json::to_string(&original).unwrap(); + let restored: NetworkConfigs = serde_json::from_str(&json).unwrap(); + assert!(restored.is_optimism()); + assert!(!restored.is_tempo()); + } + + #[test] + fn serde_optimism_field_deserialized() { + let json_optimism = + r#"{"network": "optimism", "celo": false, "bypass_prevrandao": false}"#; + let cfg_optimism: NetworkConfigs = serde_json::from_str(json_optimism).unwrap(); + assert!(cfg_optimism.is_optimism()); + } } } diff --git a/crates/evm/networks/src/optimism.rs b/crates/evm/networks/src/optimism.rs new file mode 100644 index 0000000000000..5fffa38a333c7 --- /dev/null +++ b/crates/evm/networks/src/optimism.rs @@ -0,0 +1,25 @@ +//! Optimism-specific extensions for [`NetworkConfigs`] and related helpers. + +use crate::{NetworkConfigs, NetworkVariant}; +use alloy_eips::eip1559::BaseFeeParams; +use alloy_op_hardforks::{OpChainHardforks, OpHardforks}; + +impl NetworkConfigs { + pub fn with_optimism() -> Self { + Self { network: Some(NetworkVariant::Optimism), optimism: true, ..Default::default() } + } + + pub const fn is_optimism(&self) -> bool { + matches!(self.resolved_network(), Some(NetworkVariant::Optimism)) + } + + /// Optimism-specific base fee parameters, picking Canyon vs pre-Canyon based on `timestamp`. + pub(crate) fn op_base_fee_params(&self, timestamp: u64) -> BaseFeeParams { + let op_hardforks = OpChainHardforks::op_mainnet(); + if op_hardforks.is_canyon_active_at_timestamp(timestamp) { + BaseFeeParams::optimism_canyon() + } else { + BaseFeeParams::optimism() + } + } +} diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 90d2db724cebc..73d64d3ab5d07 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -50,3 +50,7 @@ tempfile.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing.workspace = true yansi.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism", "foundry-evm-core/optimism"] diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index bad5c577bc69e..b6f11772620f9 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -26,3 +26,7 @@ foundry-test-utils.workspace = true toml.workspace = true snapbox.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/fmt/src/state/mod.rs b/crates/fmt/src/state/mod.rs index 89a9bf152c8c2..4b986017b71dd 100644 --- a/crates/fmt/src/state/mod.rs +++ b/crates/fmt/src/state/mod.rs @@ -711,7 +711,7 @@ impl<'sess> State<'sess, '_> { // Merge the lines and let the wrapper handle breaking if needed let merged_line = format!( "{current_line} {next_content}", - next_content = &next_line[prefix.len()..].trim_start() + next_content = next_line[prefix.len()..].trim_start() ); result.push(merged_line); diff --git a/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol index 25074229db558..26809a9418cf0 100644 --- a/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/bracket-spacing.fmt.sol @@ -51,3 +51,9 @@ contract AnotherContract is { } contract WithLayoutAndBase layout at 69 is Base { } + +contract ERC7201Short layout at erc7201("s") is Base { } + +contract ERC7201Mid layout at erc7201("openzeppelin.med") is Base { } + +contract ERC7201OverMax layout at erc7201("openzeppelin.storage.exceeds.max") is Base { } diff --git a/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol b/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol index 990845d3d1349..9dca04b11e25c 100644 --- a/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/contract-new-lines.fmt.sol @@ -62,3 +62,12 @@ contract AnotherContract is {} contract WithLayoutAndBase layout at 69 is Base {} + +contract ERC7201Short layout at erc7201("s") is Base {} + +contract ERC7201Mid layout at erc7201("openzeppelin.med") is Base {} + +contract ERC7201OverMax layout at erc7201("openzeppelin.storage.exceeds.max") + is + Base +{} diff --git a/crates/fmt/testdata/ContractDefinition/fmt.sol b/crates/fmt/testdata/ContractDefinition/fmt.sol index 93cddcd2c6a20..afe196dbf3da9 100644 --- a/crates/fmt/testdata/ContractDefinition/fmt.sol +++ b/crates/fmt/testdata/ContractDefinition/fmt.sol @@ -57,3 +57,12 @@ contract AnotherContract is {} contract WithLayoutAndBase layout at 69 is Base {} + +contract ERC7201Short layout at erc7201("s") is Base {} + +contract ERC7201Mid layout at erc7201("openzeppelin.med") is Base {} + +contract ERC7201OverMax layout at erc7201("openzeppelin.storage.exceeds.max") + is + Base +{} diff --git a/crates/fmt/testdata/ContractDefinition/original.sol b/crates/fmt/testdata/ContractDefinition/original.sol index c0aa88a7d6d17..ed66f2c5728d2 100644 --- a/crates/fmt/testdata/ContractDefinition/original.sol +++ b/crates/fmt/testdata/ContractDefinition/original.sol @@ -50,3 +50,6 @@ contract AnotherContract is { } contract WithLayoutAndBase layout at 69 is Base {} +contract ERC7201Short layout at erc7201("s") is Base {} +contract ERC7201Mid layout at erc7201("openzeppelin.med") is Base {} +contract ERC7201OverMax layout at erc7201("openzeppelin.storage.exceeds.max") is Base {} diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 064834d248d5f..4e83eb168abca 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,6 +62,7 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true tempo-alloy.workspace = true @@ -117,7 +118,7 @@ tempfile.workspace = true alloy-signer-local.workspace = true [features] -default = ["jemalloc", "asm-keccak"] +default = ["jemalloc", "asm-keccak", "optimism"] asm-keccak = ["alloy-primitives/asm-keccak", "revm/asm-keccak"] jemalloc = ["foundry-cli/jemalloc"] mimalloc = ["foundry-cli/mimalloc"] @@ -126,3 +127,15 @@ aws-kms = ["foundry-wallets/aws-kms"] gcp-kms = ["foundry-wallets/gcp-kms"] turnkey = ["foundry-wallets/turnkey"] isolate-by-default = ["foundry-config/isolate-by-default"] +optimism = [ + "foundry-evm/optimism", + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-cli/optimism", + "forge-script/optimism", + "forge-verify/optimism", + "forge-doc/optimism", + "forge-fmt/optimism", + "forge-lint/optimism", + "forge-sol-macro-gen/optimism", +] diff --git a/crates/forge/assets/tempo/MailTemplate.s.sol b/crates/forge/assets/tempo/MailTemplate.s.sol index 27512efe4d5ec..45006f7cd0e06 100644 --- a/crates/forge/assets/tempo/MailTemplate.s.sol +++ b/crates/forge/assets/tempo/MailTemplate.s.sol @@ -14,7 +14,7 @@ contract MailScript is Script { function run(string memory salt) public { vm.startBroadcast(); - address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.ALPHA_USD_ADDRESS); + address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.PATH_USD_ADDRESS); StdPrecompiles.TIP_FEE_MANAGER.setUserToken(feeToken); ITIP20 token = ITIP20( diff --git a/crates/forge/assets/tempo/MailTemplate.t.sol b/crates/forge/assets/tempo/MailTemplate.t.sol index b1749db5df0bf..19760303860a1 100644 --- a/crates/forge/assets/tempo/MailTemplate.t.sol +++ b/crates/forge/assets/tempo/MailTemplate.t.sol @@ -17,7 +17,7 @@ contract MailTest is Test { address public constant BOB = address(0x70997970C51812dc3A010C7d01b50e0d17dc79C8); function setUp() public virtual { - address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.ALPHA_USD_ADDRESS); + address feeToken = vm.envOr("TEMPO_FEE_TOKEN", StdTokens.PATH_USD_ADDRESS); StdPrecompiles.TIP_FEE_MANAGER.setUserToken(feeToken); token = ITIP20( diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index ea034bce87185..b8ce2a9b945b1 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -87,8 +87,11 @@ impl CoverageArgs { config = self.load_config()?; } - // Set fuzz seed so coverage reports are deterministic - config.fuzz.seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + // Default to a static fuzz seed so coverage reports are deterministic, + // but allow the user to override it via `--fuzz-seed` or `[fuzz] seed` in config. + if config.fuzz.seed.is_none() { + config.fuzz.seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + } let (paths, mut output) = { let (project, output) = self.build(&config)?; diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 765bb64f95fdd..623723317cdcb 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -13,7 +13,10 @@ use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, - utils::{LoadConfig, find_contract_artifacts, read_constructor_args_file}, + utils::{ + LoadConfig, ResolvedLane, find_contract_artifacts, maybe_print_resolved_lane, + read_constructor_args_file, resolve_lane, + }, }; use foundry_common::{ FoundryTransactionBuilder, @@ -193,6 +196,12 @@ impl CreateArgs { constructor_args.as_deref().unwrap_or(&self.constructor_args), )? } else { + if !self.constructor_args.is_empty() || self.constructor_args_path.is_some() { + sh_warn!( + "`{}` has no constructor; ignoring provided constructor arguments", + self.contract.name + )?; + } vec![] }; @@ -203,6 +212,11 @@ impl CreateArgs { self.tx.tempo.key_id = Some(ak.key_address); } + // Resolve `--tempo.lane ` against the lanes file (default + // `/tempo.lanes.toml`) and populate `self.tx.tempo.nonce_key` from the lane. + // Must happen before `self.deploy(...)` so `TempoOpts::apply` picks up the nonce_key. + let resolved_lane = resolve_lane(&mut self.tx.tempo, &config.root)?; + // Whether to broadcast the transaction or not let dry_run = !self.broadcast; @@ -223,6 +237,7 @@ impl CreateArgs { dry_run, None, Some(browser), + resolved_lane, ) .await } else if self.unlocked { @@ -239,6 +254,7 @@ impl CreateArgs { dry_run, None, None, + resolved_lane, ) .await } else if let Some(ak) = access_key { @@ -259,6 +275,7 @@ impl CreateArgs { dry_run, Some((signer, ak)), None, + resolved_lane, ) .await } else { @@ -282,6 +299,7 @@ impl CreateArgs { dry_run, None, None, + resolved_lane, ) .await } @@ -362,6 +380,7 @@ impl CreateArgs { dry_run: bool, tempo_keychain: Option<(WalletSigner, TempoAccessKeyConfig)>, browser_signer: Option>, + resolved_lane: Option, ) -> Result<()> where N::TransactionRequest: FoundryTransactionBuilder + serde::Serialize, @@ -398,7 +417,7 @@ impl CreateArgs { // If Tempo chain fee token must be set if chain.is_tempo() { - if let Some(fee_token) = self.tx.tempo.fee_token { + if let Some(fee_token) = self.tx.tempo.common.fee_token { deployer.tx.set_fee_token(fee_token); } else { deployer.tx.set_fee_token(DEFAULT_FEE_TOKEN); @@ -408,15 +427,18 @@ impl CreateArgs { // Apply user-provided gas, fee, nonce, and Tempo options. self.tx.apply::(&mut deployer.tx, is_legacy); - // For keychain mode, set key_id and nonce_key before gas estimation. // Convert the CREATE into an AA-compatible call entry since Tempo AA // transactions use a `calls` list instead of `to`+`input`. + if chain.is_tempo() { + deployer.tx.convert_create_to_call(); + } + + // For keychain mode, set key_id and nonce_key before gas estimation. if let Some((_, ref ak)) = tempo_keychain { deployer.tx.set_key_id(ak.key_address); if deployer.tx.nonce_key().is_none() { deployer.tx.set_nonce_key(U256::ZERO); } - deployer.tx.convert_create_to_call(); } // Fetch defaults from provider for values not specified by user. @@ -424,6 +446,20 @@ impl CreateArgs { deployer.tx.set_nonce(provider.get_transaction_count(deployer_address).await?); } + maybe_print_resolved_lane(resolved_lane.as_ref(), deployer.tx.nonce().unwrap_or_default())?; + + if let Some((_, ref ak)) = tempo_keychain { + deployer + .tx + .prepare_access_key_authorization( + provider.as_ref(), + ak.wallet_address, + ak.key_address, + ak.key_authorization.as_ref(), + ) + .await?; + } + // set access list if specified if let Some(access_list) = match self.tx.access_list { None => None, @@ -500,6 +536,11 @@ impl CreateArgs { return Ok(()); } + let tempo_sponsor = self.tx.tempo.sponsor_config().await?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut deployer.tx, deployer_address).await?; + } + // Deploy the actual contract let (deployed_contract, receipt) = if let Some(browser) = browser_signer { // Browser wallet signs and sends the transaction diff --git a/crates/forge/src/cmd/snapshot.rs b/crates/forge/src/cmd/snapshot.rs index c8dc2ba72aae1..7c6fb51ce3266 100644 --- a/crates/forge/src/cmd/snapshot.rs +++ b/crates/forge/src/cmd/snapshot.rs @@ -99,8 +99,11 @@ impl GasSnapshotArgs { } pub async fn run(mut self) -> Result<()> { - // Set fuzz seed so gas snapshots are deterministic - self.test.fuzz_seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + // Default to a static fuzz seed so gas snapshots are deterministic, + // but allow the user to override it via `--fuzz-seed`. + if self.test.fuzz_seed.is_none() { + self.test.fuzz_seed = Some(U256::from_be_bytes(STATIC_FUZZ_SEED)); + } let outcome = self.test.compile_and_run().await?; outcome.ensure_ok(false)?; diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index da300c429e37e..dd8f3afd56197 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -3,7 +3,7 @@ use crate::{ MultiContractRunner, MultiContractRunnerBuilder, decode::decode_console_logs, gas_report::GasReport, - multi_runner::matches_artifact, + multi_runner::{MultiNetworkConfig, matches_artifact}, result::{SuiteResult, TestOutcome, TestStatus}, traces::{ CallTraceDecoderBuilder, InternalTraceMode, TraceKind, @@ -31,7 +31,7 @@ use foundry_compilers::{ utils::source_files_iter, }; use foundry_config::{ - Config, figment, + Config, InlineConfig, figment, figment::{ Metadata, Profile, Provider, value::{Dict, Map}, @@ -39,10 +39,11 @@ use foundry_config::{ filter::GlobMatcher, }; use foundry_debugger::Debugger; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ core::evm::{ - BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, SpecFor, TempoEvmNetwork, - TxEnvFor, + BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, SpecFor, TempoEvmNetwork, TxEnvFor, }, opts::EvmOpts, traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth}, @@ -169,6 +170,14 @@ pub struct TestArgs { #[arg(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] pub fuzz_runs: Option, + /// Run only the fuzz case at the given 1-based run index. + #[arg(long, env = "FOUNDRY_FUZZ_RUN", value_name = "RUN")] + pub fuzz_run: Option, + + /// Run the fuzz case from the given worker. Requires `--fuzz-run`. + #[arg(long, env = "FOUNDRY_FUZZ_WORKER", value_name = "WORKER", requires = "fuzz_run")] + pub fuzz_worker: Option, + /// Timeout for each fuzz run in seconds. #[arg(long, env = "FOUNDRY_FUZZ_TIMEOUT", value_name = "TIMEOUT")] pub fuzz_timeout: Option, @@ -301,6 +310,10 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, coverage: bool, ) -> Result { + if config.fuzz.run == Some(0) { + bail!("`fuzz.run` must be greater than 0"); + } + // Explicitly enable isolation for gas reports for more correct gas accounting. if self.gas_report { evm_opts.isolate = true; @@ -342,40 +355,80 @@ impl TestArgs { // Auto-detect network from fork chain ID when not explicitly configured. evm_opts.infer_network_from_fork().await; - // Dispatch based on network type. - let (libraries, mut outcome) = if evm_opts.networks.is_tempo() { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? - } else if evm_opts.networks.is_optimism() { - self.build_and_run_tests::( + // Parse inline config early to detect per-test network annotations. + let inline_config = InlineConfig::new_parsed(output, &config)?; + let override_networks = inline_config.referenced_override_networks(&config.profile); + + let (libraries, mut outcome) = if override_networks.is_empty() { + // Single-pass: no per-test network overrides, use global network setting. + self.dispatch_network( + &evm_opts, config, - evm_opts, + evm_opts.clone(), output, filter, coverage, should_debug, decode_internal, + MultiNetworkConfig::default(), ) .await? } else { - self.build_and_run_tests::( - config, - evm_opts, - output, - filter, - coverage, - should_debug, - decode_internal, - ) - .await? + // Multi-pass: run each distinct network separately and merge results. + let all_override_networks = override_networks.clone(); + let multi_pass_timer = Instant::now(); + + // Default pass: global network, runs tests without an explicit network annotation. + let (libraries, mut outcome) = self + .dispatch_network( + &evm_opts, + config.clone(), + evm_opts.clone(), + output, + filter, + coverage, + should_debug, + decode_internal, + MultiNetworkConfig { + all_override_networks: all_override_networks.clone(), + pass_network: None, + }, + ) + .await?; + + // Override passes: one per annotated network. + for &network in &override_networks { + let mut pass_evm_opts = evm_opts.clone(); + pass_evm_opts.networks = network.into(); + let (_, pass_outcome) = self + .dispatch_network( + &pass_evm_opts, + config.clone(), + pass_evm_opts.clone(), + output, + filter, + coverage, + should_debug, + decode_internal, + MultiNetworkConfig { + all_override_networks: all_override_networks.clone(), + pass_network: Some(network), + }, + ) + .await?; + merge_outcomes(&mut outcome, pass_outcome); + } + + // Print the merged summary (per-pass summaries are suppressed in `run_tests_inner`). + if !self.summary && !shell::is_json() { + sh_println!("{}", outcome.summary(multi_pass_timer.elapsed()))?; + } + if self.summary && !outcome.results.is_empty() { + let summary_report = TestSummaryReport::new(self.detailed, outcome.clone()); + sh_println!("{}", &summary_report)?; + } + + (libraries, outcome) }; if should_draw { @@ -461,6 +514,7 @@ impl TestArgs { coverage: bool, should_debug: bool, decode_internal: InternalTraceMode, + multi_network: MultiNetworkConfig, ) -> eyre::Result<(Libraries, TestOutcome)> { let verbosity = evm_opts.verbosity; let (evm_env, tx_env, fork_block) = @@ -476,6 +530,7 @@ impl TestArgs { .enable_isolation(evm_opts.isolate) .fail_fast(self.fail_fast) .set_coverage(coverage) + .with_multi_network(multi_network) .build::(output, evm_env, tx_env, evm_opts)?; let libraries = runner.libraries.clone(); @@ -483,6 +538,62 @@ impl TestArgs { Ok((libraries, outcome)) } + /// Dispatches `build_and_run_tests` to the correct network type based on `evm_opts.networks`. + #[allow(clippy::too_many_arguments)] + async fn dispatch_network( + &self, + dispatch_opts: &EvmOpts, + config: Config, + evm_opts: EvmOpts, + output: &ProjectCompileOutput, + filter: &ProjectPathsAwareFilter, + coverage: bool, + should_debug: bool, + decode_internal: InternalTraceMode, + multi_network: MultiNetworkConfig, + ) -> eyre::Result<(Libraries, TestOutcome)> { + if dispatch_opts.networks.is_tempo() { + self.build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + multi_network, + ) + .await + } else { + #[cfg(feature = "optimism")] + if dispatch_opts.networks.is_optimism() { + return self + .build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + multi_network, + ) + .await; + } + self.build_and_run_tests::( + config, + evm_opts, + output, + filter, + coverage, + should_debug, + decode_internal, + multi_network, + ) + .await + } + } + /// Run all tests that matches the filter predicate from a test runner async fn run_tests_inner( &self, @@ -586,6 +697,11 @@ impl TestArgs { let libraries = runner.libraries.clone(); + // Capture multi-pass state before moving `runner` into the spawn task. + // In multi-pass mode the per-pass summary is suppressed; the merged summary is + // printed once by the caller after all passes complete. + let is_multi_pass = !runner.tcfg.multi_network.all_override_networks.is_empty(); + // Run tests in a streaming fashion. let (tx, rx) = channel::<(String, SuiteResult)>(); let timer = Instant::now(); @@ -643,6 +759,13 @@ impl TestArgs { let tests = &mut suite_result.test_results; let has_tests = !tests.is_empty(); + // In multi-pass (per-test network override) mode, skip suites that contributed no + // tests to this pass so we don't emit a stray blank line in the suite header or + // pollute the outcome with empty entries. + if is_multi_pass && !has_tests && suite_result.warnings.is_empty() { + continue; + } + // Clear the addresses and labels from previous test. decoder.clear_addresses(); @@ -812,9 +935,8 @@ impl TestArgs { // // Exiting early with code 1 if differences are found. if self.gas_snapshot_check.unwrap_or(config.gas_snapshot_check) { - let differences_found = gas_snapshots.clone().into_iter().fold( - false, - |mut found, (group, snapshots)| { + let differences_found = + gas_snapshots.iter().fold(false, |mut found, (group, snapshots)| { // If the snapshot file doesn't exist, we can't compare so we skip. if !&config.snapshots.join(format!("{group}.json")).exists() { return found; @@ -852,8 +974,7 @@ impl TestArgs { } found - }, - ); + }); if differences_found { sh_eprintln!()?; @@ -875,13 +996,13 @@ impl TestArgs { fs::create_dir_all(&config.snapshots)?; // Write gas snapshots to disk per group. - gas_snapshots.clone().into_iter().for_each(|(group, snapshots)| { + for (group, snapshots) in &gas_snapshots { fs::write_pretty_json_file( &config.snapshots.join(format!("{group}.json")), &snapshots, ) .expect("Failed to write gas snapshots to disk"); - }); + } } } @@ -905,17 +1026,17 @@ impl TestArgs { if let Some(gas_report) = gas_report { let finalized = gas_report.finalize(); - sh_println!("{}", &finalized)?; + sh_println!("{finalized}")?; outcome.gas_report = Some(finalized); } - if !self.summary && !shell::is_json() { + if !is_multi_pass && !self.summary && !shell::is_json() { sh_println!("{}", outcome.summary(duration))?; } - if self.summary && !outcome.results.is_empty() { + if !is_multi_pass && self.summary && !outcome.results.is_empty() { let summary_report = TestSummaryReport::new(self.detailed, outcome.clone()); - sh_println!("{}", &summary_report)?; + sh_println!("{summary_report}")?; } // Reattach the task. @@ -982,6 +1103,12 @@ 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_run) = self.fuzz_run { + fuzz_dict.insert("run".to_string(), fuzz_run.into()); + } + if let Some(fuzz_worker) = self.fuzz_worker { + fuzz_dict.insert("worker".to_string(), fuzz_worker.into()); + } if let Some(fuzz_timeout) = self.fuzz_timeout { fuzz_dict.insert("timeout".to_string(), fuzz_timeout.into()); } @@ -1025,6 +1152,29 @@ fn list( Ok(TestOutcome::empty(Some(runner.known_contracts), false)) } +/// Merges `other` into `base` by extending suite results. +/// +/// For suites that appear in both, test results are combined (function-level pass routing ensures +/// each function appears in exactly one pass, so there are no key conflicts in practice). +fn merge_outcomes(base: &mut TestOutcome, other: TestOutcome) { + for (suite_id, other_suite) in other.results { + match base.results.entry(suite_id) { + std::collections::btree_map::Entry::Vacant(e) => { + e.insert(other_suite); + } + std::collections::btree_map::Entry::Occupied(mut e) => { + let base_suite = e.get_mut(); + base_suite.test_results.extend(other_suite.test_results); + base_suite.warnings.extend(other_suite.warnings); + base_suite.duration = base_suite.duration.max(other_suite.duration); + } + } + } + if let Some(decoder) = other.last_run_decoder { + base.last_run_decoder = Some(decoder); + } +} + /// Load persisted filter (with last test run failures) from file. fn last_run_failures(config: &Config) -> Option { match fs::read_to_string(&config.test_failures_file) { @@ -1133,6 +1283,14 @@ mod tests { assert!(args.fuzz_seed.is_some()); } + #[test] + fn fuzz_run() { + let args: TestArgs = + TestArgs::parse_from(["foundry-cli", "--fuzz-run", "10", "--fuzz-worker", "2"]); + assert_eq!(args.fuzz_run, Some(10)); + assert_eq!(args.fuzz_worker, Some(2)); + } + #[test] fn extract_chain() { let test = |arg: &str, expected: Chain| { diff --git a/crates/forge/src/cmd/test/summary.rs b/crates/forge/src/cmd/test/summary.rs index f8a72272af53c..a0123e896d0bf 100644 --- a/crates/forge/src/cmd/test/summary.rs +++ b/crates/forge/src/cmd/test/summary.rs @@ -25,9 +25,9 @@ impl TestSummaryReport { impl Display for TestSummaryReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { if shell::is_json() { - writeln!(f, "{}", &self.format_json_output(&self.is_detailed, &self.outcome))?; + writeln!(f, "{}", self.format_json_output(&self.is_detailed, &self.outcome))?; } else { - writeln!(f, "\n{}", &self.format_table_output(&self.is_detailed, &self.outcome))?; + writeln!(f, "\n{}", self.format_table_output(&self.is_detailed, &self.outcome))?; } Ok(()) } diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 6c93dc03b28b5..58b11d98874ed 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -146,7 +146,7 @@ impl GasReport { impl Display for GasReport { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { if shell::is_json() { - writeln!(f, "{}", &self.format_json_output())?; + writeln!(f, "{}", self.format_json_output())?; } else { for (name, contract) in &self.contracts { if contract.functions.is_empty() { diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 88bbc6156c812..675f0c3e6c99c 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -27,6 +27,7 @@ use foundry_evm::{ opts::EvmOpts, traces::{InternalTraceMode, TraceMode}, }; +use foundry_evm_networks::NetworkVariant; use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; @@ -280,6 +281,25 @@ impl MultiContractRunner { } } +/// Tracks network assignment across a multi-network test run. +/// +/// When inline config specifies different networks for different tests, the runner performs one +/// pass per distinct network. This struct encodes which pass we're in so each `ContractRunner` +/// can skip tests that belong to a different pass. +/// +/// Default (empty `all_override_networks`, `None` pass) = single-pass mode, every test runs. +#[derive(Clone, Debug, Default)] +pub struct MultiNetworkConfig { + /// All networks explicitly referenced in inline config annotations across the whole suite. + /// Empty means single-pass mode (no per-test network overrides present). + pub all_override_networks: Vec, + /// The network this pass is responsible for. + /// `None` = default pass: runs tests *without* an explicit network annotation (or annotated + /// with a network not in `all_override_networks`). + /// `Some(v)` = override pass: runs only tests annotated with exactly `v`. + pub pass_network: Option, +} + /// Configuration for the test runner. /// /// This is modified after instantiation through inline config. @@ -311,6 +331,9 @@ pub struct TestRunnerConfig { pub isolation: bool, /// Whether to exit early on test failure or if test run interrupted. pub early_exit: EarlyExit, + + /// Multi-network pass configuration. Default = single-pass mode. + pub multi_network: MultiNetworkConfig, } impl TestRunnerConfig { @@ -423,6 +446,8 @@ pub struct MultiContractRunnerBuilder { pub isolation: bool, /// Whether to exit early on test failure. pub fail_fast: bool, + /// Multi-network pass configuration. + pub multi_network: MultiNetworkConfig, } impl MultiContractRunnerBuilder { @@ -437,6 +462,7 @@ impl MultiContractRunnerBuilder { isolation: Default::default(), decode_internal: Default::default(), fail_fast: false, + multi_network: Default::default(), } } @@ -470,6 +496,11 @@ impl MultiContractRunnerBuilder { self } + pub fn with_multi_network(mut self, multi_network: MultiNetworkConfig) -> Self { + self.multi_network = multi_network; + self + } + pub const fn fail_fast(mut self, fail_fast: bool) -> Self { self.fail_fast = fail_fast; self @@ -594,6 +625,7 @@ impl MultiContractRunnerBuilder { inline_config: Arc::new(InlineConfig::new_parsed(output, &self.config)?), isolation: self.isolation, early_exit: EarlyExit::new(self.fail_fast), + multi_network: self.multi_network, config: self.config, }, diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index d924c416759a2..7feaf35254636 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -109,6 +109,25 @@ impl<'a, FEN: FoundryEvmNetwork> ContractRunner<'a, FEN> { } } + /// Returns `true` if `func` should run in the current multi-network pass. + /// + /// In single-pass mode (no inline network overrides) every function passes. + /// In multi-pass mode: + /// - Default pass (`pass_network = None`): includes functions *without* an override annotation. + /// - Override pass (`pass_network = Some(v)`): includes only functions annotated with `v`. + fn function_matches_network_pass(&self, func: &Function) -> bool { + let multi = &self.mcr.tcfg.multi_network; + if multi.all_override_networks.is_empty() { + return true; + } + let profile = &self.tcfg.config.profile; + let func_network = self.mcr.inline_config.network_for(profile, self.name, &func.name); + match &multi.pass_network { + None => func_network.is_none_or(|n| !multi.all_override_networks.contains(&n)), + Some(target) => func_network.as_ref() == Some(target), + } + } + /// 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, call_setup: bool) -> TestSetup { @@ -380,6 +399,7 @@ impl<'a, FEN: FoundryEvmNetwork> ContractRunner<'a, FEN> { .abi .functions() .filter(|func| filter.matches_test_function(func)) + .filter(|func| self.function_matches_network_pass(func)) .collect::>(); debug!( "Found {} test functions out of {} in {:?}", @@ -826,7 +846,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { ); if let Some(ref progress) = progress { - progress.set_prefix(format!("{}\n{warn}\n", &func.name)); + progress.set_prefix(format!("{}\n{warn}\n", func.name)); } else { let _ = sh_warn!("{warn}"); } @@ -1052,7 +1072,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> { self.cr.name, &func.name, fuzz_config.timeout, - fuzz_config.runs, + if fuzz_config.run.is_some() { 1 } else { fuzz_config.runs }, ); let state = self.build_fuzz_state(false); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index d3f1503faf569..5d8378c50c9ac 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -916,11 +916,18 @@ Installing tempo-std in [..] (url: https://github.com/tempoxyz/tempo-std, tag: N assert!(prj.root().join("foundry.toml").exists()); - // Verify foundry.toml contains `tempo = true` so subsequent commands auto-detect the network. + // Verify foundry.toml contains `network = "tempo"` so subsequent commands auto-detect the + // network. let foundry_toml = std::fs::read_to_string(prj.root().join("foundry.toml")).unwrap(); assert!( - foundry_toml.contains("tempo = true"), - "foundry.toml should contain `tempo = true`, got:\n{foundry_toml}" + foundry_toml.contains("network = \"tempo\""), + "foundry.toml should contain `network = \"tempo\"`, got:\n{foundry_toml}" + ); + assert!( + foundry_toml.contains("[rpc_endpoints]") + && foundry_toml.contains("tempo = \"https://rpc.tempo.xyz/\"") + && foundry_toml.contains("moderato = \"https://rpc.moderato.tempo.xyz/\""), + "foundry.toml should contain tempo rpc_endpoints, got:\n{foundry_toml}" ); assert!(prj.root().join("lib/forge-std").exists()); @@ -1816,7 +1823,7 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -1830,7 +1837,7 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -1844,7 +1851,7 @@ forgetest!(gas_report_all_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -1863,7 +1870,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -1879,7 +1886,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -1895,7 +1902,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -1921,7 +1928,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -1935,7 +1942,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -1949,7 +1956,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -1968,7 +1975,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -1984,7 +1991,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2000,7 +2007,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2026,7 +2033,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2040,7 +2047,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -2054,7 +2061,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2073,7 +2080,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2089,7 +2096,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2105,7 +2112,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2134,7 +2141,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2148,7 +2155,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -2162,7 +2169,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2181,7 +2188,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2197,7 +2204,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2213,7 +2220,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2246,7 +2253,7 @@ forgetest!(gas_report_some_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2265,7 +2272,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2293,7 +2300,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2312,7 +2319,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2340,7 +2347,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -2359,7 +2366,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2395,7 +2402,7 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -2409,7 +2416,7 @@ forgetest!(gas_report_ignore_some_contracts, |prj, cmd| { +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2428,7 +2435,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2444,7 +2451,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2476,7 +2483,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2490,7 +2497,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -2509,7 +2516,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2525,7 +2532,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2565,7 +2572,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2579,7 +2586,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| -| 133243 | 395 | | | | | +| 133219 | 395 | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------------------+-----------------+--------+--------+--------+---------| @@ -2593,7 +2600,7 @@ Ran 3 test suites [ELAPSED]: 3 tests passed, 0 failed, 0 skipped (3 total tests) +=============================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| -| 133027 | 394 | | | | | +| 133015 | 394 | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------+-----------------+-------+--------+-------+---------| @@ -2622,7 +2629,7 @@ Warning: ContractThree is listed in both 'gas_reports' and 'gas_reports_ignore'. { "contract": "src/Contracts.sol:ContractOne", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2638,7 +2645,7 @@ Warning: ContractThree is listed in both 'gas_reports' and 'gas_reports_ignore'. { "contract": "src/Contracts.sol:ContractThree", "deployment": { - "gas": 133243, + "gas": 133219, "size": 395 }, "functions": { @@ -2654,7 +2661,7 @@ Warning: ContractThree is listed in both 'gas_reports' and 'gas_reports_ignore'. { "contract": "src/Contracts.sol:ContractTwo", "deployment": { - "gas": 133027, + "gas": 133015, "size": 394 }, "functions": { @@ -2993,7 +3000,7 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] +=====================================================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| 132459 | 396 | | | | | +| 132471 | 396 | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------------------------------------+-----------------+-------+--------+-------+---------| @@ -3016,7 +3023,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "test/FallbackWithCalldataTest.sol:CounterWithFallback", "deployment": { - "gas": 132459, + "gas": 132471, "size": 396 }, "functions": { @@ -3106,7 +3113,7 @@ contract NestedDeploy is Test { +============================================================================================+ | Deployment Cost | Deployment Size | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| -| 328961 | 1163 | | | | | +| 328949 | 1163 | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| | | | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| @@ -3161,7 +3168,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "test/NestedDeployTest.sol:Parent", "deployment": { - "gas": 328961, + "gas": 328949, "size": 1163 }, "functions": { @@ -3918,7 +3925,7 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { +=======================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| -| 156813 | 509 | | | | | +| 156801 | 509 | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |----------------------------------+-----------------+-------+--------+-------+---------| @@ -3942,7 +3949,7 @@ forgetest_init!(gas_report_include_tests, |prj, cmd| { |-----------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------+-----------------+--------+--------+--------+---------| -| setUp | 218902 | 218902 | 218902 | 218902 | 1 | +| setUp | 218890 | 218890 | 218890 | 218890 | 1 | |-----------------------------------------+-----------------+--------+--------+--------+---------| | test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | ╰-----------------------------------------+-----------------+--------+--------+--------+---------╯ @@ -3960,7 +3967,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) | src/Counter.sol:Counter Contract | | | | | | |----------------------------------|-----------------|-------|--------|-------|---------| | Deployment Cost | Deployment Size | | | | | -| 156813 | 509 | | | | | +| 156801 | 509 | | | | | | | | | | | | | Function Name | Min | Avg | Median | Max | # Calls | | increment | 43482 | 43482 | 43482 | 43482 | 1 | @@ -3973,7 +3980,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) | 1544498 | 7573 | | | | | | | | | | | | | Function Name | Min | Avg | Median | Max | # Calls | -| setUp | 218902 | 218902 | 218902 | 218902 | 1 | +| setUp | 218890 | 218890 | 218890 | 218890 | 1 | | test_Increment | 51847 | 51847 | 51847 | 51847 | 1 | @@ -3990,7 +3997,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "src/Counter.sol:Counter", "deployment": { - "gas": 156813, + "gas": 156801, "size": 509 }, "functions": { @@ -4026,10 +4033,10 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "functions": { "setUp()": { "calls": 1, - "min": 218902, - "mean": 218902, - "median": 218902, - "max": 218902 + "min": 218890, + "mean": 218890, + "median": 218890, + "max": 218890 }, "test_Increment()": { "calls": 1, diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index dca88ad1c2f63..3eebca475a781 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -112,7 +112,6 @@ create2_deployer = "0x4e59b44847b379578588920ca78fbf26c0b4956c" assertions_revert = true legacy_assertions = false celo = false -tempo = false bypass_prevrandao = false transaction_timeout = 120 additional_compiler_profiles = [] @@ -578,6 +577,32 @@ forgetest_init!(can_get_evm_opts, |prj, _cmd| { } }); +// Regression test for : +// the bare `ETH_RPC_URL` env var must NOT cause `forge` commands to set +// `eth_rpc_url` (which would silently fork all `forge test` runs). +// Only `--rpc-url`, `foundry.toml`, the `FOUNDRY_ETH_RPC_URL` env var, or +// cheatcodes should configure forking. +forgetest_init!(eth_rpc_url_env_does_not_set_fork_url, |prj, _cmd| { + prj.initialize_default_contracts(); + let url = "http://127.0.0.1:8545"; + + let mut cmd = prj.forge_bin(); + cmd.arg("config") + .arg("--root") + .arg(prj.root()) + .arg("--json") + .env("ETH_RPC_URL", url) + // Make sure the figment-style env var is not set in the test environment. + .env_remove("FOUNDRY_ETH_RPC_URL"); + let output = cmd.output().unwrap(); + let stdout = String::from_utf8_lossy(&output.stdout); + let config: Config = serde_json::from_str(stdout.as_ref()).unwrap(); + assert_eq!( + config.eth_rpc_url, None, + "bare ETH_RPC_URL must not propagate to forge config (regression #14538)" + ); +}); + // checks that we can set various config values forgetest_init!(can_set_config_values, |prj, _cmd| { prj.initialize_default_contracts(); @@ -1270,6 +1295,8 @@ forgetest_init!(test_default_config, |prj, cmd| { "show_progress": false, "fuzz": { "runs": 256, + "run": null, + "worker": null, "fail_on_revert": true, "max_test_rejects": 65536, "seed": null, @@ -1437,8 +1464,8 @@ forgetest_init!(test_default_config, |prj, cmd| { "soldeer": null, "assertions_revert": true, "legacy_assertions": false, + "network": null, "celo": false, - "tempo": false, "bypass_prevrandao": false, "transaction_timeout": 120, "additional_compiler_profiles": [], diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index fbd84739635c5..f792f5b4a7887 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -1,12 +1,12 @@ use foundry_test_utils::util::ExtTester; // Actively maintained tests -// Last updated: June 19th 2025 +// Last updated: April 29th 2026 // #[test] fn forge_std() { - ExtTester::new("foundry-rs", "forge-std", "b69e66b0ff79924d487d49bf7fb47c9ec326acba") + ExtTester::new("foundry-rs", "forge-std", "8987040ede9553cea20c95ad40d0455930f9c8e0") // Skip fork tests. .args(["--nmc", "Fork"]) .verbosity(2) @@ -17,7 +17,7 @@ fn forge_std() { #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn prb_math() { - ExtTester::new("PaulRBerg", "prb-math", "aad73cfc6cdc2c9b660199b5b1e9db391ea48640") + ExtTester::new("PaulRBerg", "prb-math", "82e5ed5561d0a1c43a3a59edbf4291c8de26479e") .install_command(&["bun", "install", "--prefer-offline"]) // Try npm if bun fails / is not installed. .install_command(&["npm", "install", "--prefer-offline"]) @@ -40,7 +40,7 @@ fn prb_proxy() { #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn sablier_v2_core() { let mut tester = - ExtTester::new("sablier-labs", "v2-core", "d85521f5615f6c19612ff250ee89c57b9afa6aa2") + ExtTester::new("sablier-labs", "v2-core", "8b6823c019ff7556ac9ad24cbb5ac62821854d2f") // Skip fork tests. .args(["--nmc", "Fork"]) // Increase the gas limit: https://github.com/sablier-labs/v2-core/issues/956 @@ -65,7 +65,7 @@ fn sablier_v2_core() { #[test] fn solady() { let mut tester = - ExtTester::new("Vectorized", "solady", "cbcfe0009477aa329574f17e8db0a05703bb8bdd"); + ExtTester::new("Vectorized", "solady", "90db92ce173856605d24a554969f2c67cadbc7e9"); // This test expects the mover contract created via CREATE2 to be selfdestructed within the // same transaction. In isolation mode, each top-level call runs as a separate transaction @@ -82,7 +82,7 @@ fn solady() { #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] #[cfg(not(feature = "isolate-by-default"))] fn snekmate() { - ExtTester::new("pcaversaccio", "snekmate", "601031d244475b160a00f73053532528bf665cc3") + ExtTester::new("pcaversaccio", "snekmate", "1a54931129f2814cbbd7ddbafb4005707f8a5bf8") .install_command(&["pnpm", "install", "--prefer-offline"]) // Try npm if pnpm fails / is not installed. .install_command(&["npm", "install", "--prefer-offline"]) @@ -92,7 +92,7 @@ fn snekmate() { // #[test] fn mds1_multicall3() { - ExtTester::new("mds1", "multicall", "5f90062160aedb7c807fadca469ac783a0557b57").run(); + ExtTester::new("mds1", "multicall", "b667d67ecfa5361a81e8f110234ce242613b0012").run(); } // Legacy tests diff --git a/crates/forge/tests/cli/failure_assertions.rs b/crates/forge/tests/cli/failure_assertions.rs index 48a17c723b261..77d5a5e84cfbb 100644 --- a/crates/forge/tests/cli/failure_assertions.rs +++ b/crates/forge/tests/cli/failure_assertions.rs @@ -70,8 +70,13 @@ Suite result: FAILED. 0 passed; 7 failed; 0 skipped; [ELAPSED] .stdout_eq( r#"No files changed, compilation skipped ... +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectPartialRevertWrongReverterTopLevelCreate() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != [..]] testShouldFailExpectRevertNestedCreateInnerAddress() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectRevertWithBytesWrongReverterTopLevelCreate() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectRevertWrongReverterNestedCreate() ([GAS]) +[FAIL: Reverter != expected reverter: [..] != 0x000000000000000000000000000000000000dEaD] testShouldFailExpectRevertWrongReverterTopLevelCreate() ([GAS]) [FAIL: next call did not revert as expected] testShouldFailExpectRevertsNotOnImmediateNextCall() ([GAS]) -Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +Suite result: FAILED. 0 passed; 6 failed; 0 skipped; [ELAPSED] ... "#, ); diff --git a/crates/forge/tests/cli/inline_config.rs b/crates/forge/tests/cli/inline_config.rs index 04fb2369d83b0..ba01767d58b26 100644 --- a/crates/forge/tests/cli/inline_config.rs +++ b/crates/forge/tests/cli/inline_config.rs @@ -425,3 +425,107 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]]); }); + +// Checks that tests annotated with `forge-config: default.networks.network` run on the correct +// EVM network, and that unannotated tests run on the globally configured network. +// +// Each test makes a real call to the Tempo `TipFeeManager` precompile at +// `0xfeec000000000000000000000000000000000000` (a Tempo-only contract that exists on the +// Moderato testnet and is auto-injected by the in-memory Tempo EVM): +// +// * The default-network test asserts the precompile has no code (it does not exist on Ethereum). +// * The Tempo-network test asserts the precompile has code and `userTokens(address)` returns the +// unset zero-address sentinel, proving the Tempo network was actually selected for that test and +// the Tempo genesis state was loaded. +forgetest!(per_test_network_routing, |prj, cmd| { + prj.add_test( + "inline.sol", + r#" + address constant TIP_FEE_MANAGER = 0xfeEC000000000000000000000000000000000000; + + contract DefaultNetwork { + // No annotation -> runs on the globally selected network (Ethereum by default). + // The Tempo FeeManager precompile must NOT exist here. + function test_fee_manager_absent_on_ethereum() public view { + require( + TIP_FEE_MANAGER.code.length == 0, + "TipFeeManager should not exist on Ethereum" + ); + } + } + + contract TempoNetwork { + /// forge-config: default.networks.network = "tempo" + function test_fee_manager_callable_on_tempo() public view { + // Sentinel bytecode (0xef) is injected at every Tempo precompile address. + require( + TIP_FEE_MANAGER.code.length > 0, + "TipFeeManager must be deployed on Tempo" + ); + + // Call a Tempo-only method: `userTokens(address)` returns the user's preferred + // fee token, or the zero address when none is set. + (bool ok, bytes memory ret) = TIP_FEE_MANAGER.staticcall( + abi.encodeWithSignature("userTokens(address)", address(0)) + ); + require(ok, "userTokens call to TipFeeManager failed"); + require(ret.length == 32, "unexpected return data length"); + address token = abi.decode(ret, (address)); + require(token == address(0), "expected unset user fee token"); + } + } + + // Mixed contract: one function annotated with Tempo, one unannotated (runs on Ethereum). + contract MixedNetwork { + // No annotation -> runs on Ethereum; precompile must be absent. + function test_fee_manager_absent_on_ethereum() public view { + require( + TIP_FEE_MANAGER.code.length == 0, + "TipFeeManager should not exist on Ethereum" + ); + } + + /// forge-config: default.networks.network = "tempo" + function test_fee_manager_callable_on_tempo() public view { + require( + TIP_FEE_MANAGER.code.length > 0, + "TipFeeManager must be deployed on Tempo" + ); + + (bool ok, bytes memory ret) = TIP_FEE_MANAGER.staticcall( + abi.encodeWithSignature("userTokens(address)", address(0)) + ); + require(ok, "userTokens call to TipFeeManager failed"); + require(ret.length == 32, "unexpected return data length"); + address token = abi.decode(ret, (address)); + require(token == address(0), "expected unset user fee token"); + } + } + "#, + ); + + cmd.arg("test").assert_success().stdout_eq(str![[r#" +[COMPILING_FILES] with [SOLC_VERSION] +[SOLC_VERSION] [ELAPSED] +Compiler run successful! + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_absent_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_absent_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_callable_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 1 test for test/inline.sol:[..]Network +[PASS] test_fee_manager_callable_on_[..]() ([GAS]) +Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 3 test suites [ELAPSED]: 4 tests passed, 0 failed, 0 skipped (4 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/cli/lint.rs b/crates/forge/tests/cli/lint.rs index fd69907be2f09..8420e24eb3df7 100644 --- a/crates/forge/tests/cli/lint.rs +++ b/crates/forge/tests/cli/lint.rs @@ -1,4 +1,7 @@ -use forge_lint::{linter::Lint, sol::med::REGISTERED_LINTS}; +use forge_lint::{ + linter::Lint, + sol::{self, SolLint}, +}; use foundry_config::{ DenyLevel, LintSeverity, LinterConfig, SolidityErrorCode, lint::LintSpecificConfig, }; @@ -203,7 +206,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply "#]]); @@ -230,7 +233,7 @@ note[mixed-case-function]: function names should use mixedCase 9 │ function functionMIXEDCaseInfo() public {} │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCaseInfo` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function "#]]); @@ -610,7 +613,7 @@ note[mixed-case-function]: function names should use mixedCase 9 │ function functionMIXEDCaseInfo() public {} │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCaseInfo` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function "#]]); @@ -637,7 +640,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply "#]]); @@ -665,7 +668,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect 13 │ uint256 result = 8 >> localValue; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift "# @@ -694,7 +697,7 @@ warning[divide-before-multiply]: multiplication should occur before division to 16 │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply "#]]).stdout_eq(str![[r#" @@ -855,7 +858,7 @@ note[unused-import]: unused imports should be removed 8 │ import { _PascalCaseInfo } from "./ContractWithLints.sol"; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import "#]]); @@ -887,7 +890,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase 6 │ uint256 public CounterB_Fail_Lint; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `counterBFailLint` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable "#]]); @@ -992,7 +995,7 @@ forgetest!(lint_json_output_no_ansi_escape_codes, |prj, cmd| { ], "children": [ { - "message": "https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic", + "message": "https://getfoundry.sh/forge/linting/unwrapped-modifier-logic", "code": null, "level": "help", "spans": [], @@ -1048,7 +1051,7 @@ forgetest!(lint_json_output_no_ansi_escape_codes, |prj, cmd| { "rendered": null } ], - "rendered": "note[unwrapped-modifier-logic]: wrap modifier logic to reduce code size\n\nhelp: wrap modifier logic to reduce code size\n 9 + _onlyOwner();\n10 + _;\n11 + }\n12 + \n13 + function _onlyOwner() internal {\n14 + require(isOwner[msg.sender], \"Not owner\");\n15 + require(msg.sender != address(0), \"Zero address\");\n16 + }\n ╭▸ src/UnwrappedModifierTest.sol:8:13\n │\n 8 │ ┏ modifier onlyOwner() {\n 9 │ ┃ require(isOwner[msg.sender], \"Not owner\");\n10 │ ┃ require(msg.sender != address(0), \"Zero address\");\n11 │ ┃ _;\n12 │ ┃ }\n │ ┗━━━━━━━━━━━━━┛\n │\n ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic\n ╭╴\n 8 ± modifier onlyOwner() {\n ╰╴\n" + "rendered": "note[unwrapped-modifier-logic]: wrap modifier logic to reduce code size\n\nhelp: wrap modifier logic to reduce code size\n 9 + _onlyOwner();\n10 + _;\n11 + }\n12 + \n13 + function _onlyOwner() internal {\n14 + require(isOwner[msg.sender], \"Not owner\");\n15 + require(msg.sender != address(0), \"Zero address\");\n16 + }\n ╭▸ src/UnwrappedModifierTest.sol:8:13\n │\n 8 │ ┏ modifier onlyOwner() {\n 9 │ ┃ require(isOwner[msg.sender], \"Not owner\");\n10 │ ┃ require(msg.sender != address(0), \"Zero address\");\n11 │ ┃ _;\n12 │ ┃ }\n │ ┗━━━━━━━━━━━━━┛\n │\n ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic\n ╭╴\n 8 ± modifier onlyOwner() {\n ╰╴\n" } "#]], ); @@ -1129,46 +1132,46 @@ Warning: Key `deny_warnings` is being deprecated in favor of `deny = warnings`. #[tokio::test] async fn ensure_lint_rule_docs() { - const FOUNDRY_BOOK_LINT_PAGE_URL: &str = "https://book.getfoundry.sh/forge/linting"; - - // Fetch the content of the lint reference - let content = match reqwest::get(FOUNDRY_BOOK_LINT_PAGE_URL).await { - Ok(resp) => { - assert!( - resp.status().is_success(), - "Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}). Status: {status}", - status = resp.status() - ); - match resp.text().await { - Ok(text) => text, - Err(e) => { - panic!("Failed to read response text: {e}"); - } + let client = reqwest::Client::new(); + let mut failures = Vec::new(); + + for lint in registered_lints() { + let url = lint.help(); + let response = match client.get(url).send().await { + Ok(response) => response, + Err(err) => { + failures.push(format!("{} ({url}) could not be fetched: {err}", lint.id())); + continue; } + }; + + if !response.status().is_success() { + failures.push(format!("{} ({url}) returned HTTP {}", lint.id(), response.status())); + continue; } - Err(e) => { - panic!("Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}): {e}",); - } - }; - // Ensure no missing lints - let mut missing_lints = Vec::new(); - for lint in REGISTERED_LINTS { + let content = match response.text().await { + Ok(content) => content.to_lowercase(), + Err(err) => { + failures + .push(format!("{} ({url}) response body could not be read: {err}", lint.id())); + continue; + } + }; + let selector = lint.id().to_lowercase(); let selector_with_space = selector.replace('-', " "); - if !content.to_lowercase().contains(&selector) - && !content.to_lowercase().contains(&selector_with_space) - { - missing_lints.push(lint.id()); + if !content.contains(&selector) && !content.contains(&selector_with_space) { + failures.push(format!("{} ({url}) did not mention the lint id", lint.id())); } } - if !missing_lints.is_empty() { + if !failures.is_empty() { let mut msg = String::from( - "Foundry Book lint validation failed. The following lints must be added to the docs:\n", + "Foundry Book lint validation failed. The following lint pages are missing or invalid:\n", ); - for lint in missing_lints { - msg.push_str(&format!(" - {lint}\n")); + for failure in failures { + msg.push_str(&format!(" - {failure}\n")); } msg.push_str("Please open a PR: https://github.com/foundry-rs/book"); panic!("{msg}"); @@ -1177,11 +1180,21 @@ async fn ensure_lint_rule_docs() { #[test] fn ensure_no_privileged_lint_id() { - for lint in REGISTERED_LINTS { + for lint in registered_lints() { assert_ne!(lint.id(), "all", "lint-id 'all' is reserved. Please use a different id"); } } +fn registered_lints() -> impl Iterator { + sol::high::REGISTERED_LINTS + .iter() + .chain(sol::med::REGISTERED_LINTS) + .chain(sol::low::REGISTERED_LINTS) + .chain(sol::info::REGISTERED_LINTS) + .chain(sol::gas::REGISTERED_LINTS) + .chain(sol::codesize::REGISTERED_LINTS) +} + // forgetest!(dependency_warnings_do_not_affect_lint_exit_code, |prj, cmd| { // Library with code that triggers a solc warning (unused local variable) @@ -1265,3 +1278,195 @@ contract OldContract { "# ]]); }); + +const PRAGMA_INCONSISTENT_ALPHA: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract Alpha {} +"#; + +const PRAGMA_INCONSISTENT_BETA: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract Beta {} +"#; + +forgetest!(pragma_inconsistent_cross_file, |prj, cmd| { + prj.add_source("Alpha", PRAGMA_INCONSISTENT_ALPHA); + prj.add_source("Beta", PRAGMA_INCONSISTENT_BETA); + + cmd.arg("lint").args(["--only-lint", "pragma-inconsistent"]).assert_success().stderr_eq(str![ + [r#" +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + + +"#] + ]); +}); + +const PRAGMA_EXACT_A: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract A {} +"#; + +const PRAGMA_EXACT_B: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract B {} +"#; + +const PRAGMA_EXACT_C: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +contract C {} +"#; + +const PRAGMA_CARET_A: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract A {} +"#; + +const PRAGMA_CARET_B: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract B {} +"#; + +const PRAGMA_CARET_C: &str = r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract C {} +"#; + +const NO_PRAGMA_C: &str = r#" +// SPDX-License-Identifier: MIT + +contract C {} +"#; + +// Multiple files all using the exact same pragma must NOT warn. +forgetest!(pragma_inconsistent_consistent_exact_no_warning, |prj, cmd| { + prj.add_source("A", PRAGMA_EXACT_A); + prj.add_source("B", PRAGMA_EXACT_B); + prj.add_source("C", PRAGMA_EXACT_C); + + cmd.arg("lint") + .args(["--only-lint", "pragma-inconsistent"]) + .assert_success() + .stderr_eq(str![[r#""#]]); +}); + +// Multiple files all using the exact same caret pragma must NOT warn. +forgetest!(pragma_inconsistent_consistent_caret_no_warning, |prj, cmd| { + prj.add_source("A", PRAGMA_CARET_A); + prj.add_source("B", PRAGMA_CARET_B); + + cmd.arg("lint") + .args(["--only-lint", "pragma-inconsistent"]) + .assert_success() + .stderr_eq(str![[r#""#]]); +}); + +// A single file in the project cannot conflict with itself. +forgetest!(pragma_inconsistent_single_file_no_warning, |prj, cmd| { + prj.add_source("A", PRAGMA_CARET_A); + + cmd.arg("lint") + .args(["--only-lint", "pragma-inconsistent"]) + .assert_success() + .stderr_eq(str![[r#""#]]); +}); + +// Even files that share a requirement still emit when ANY other variant exists. +// Two files with `0.8.20` plus one file with `^0.8.20` => 3 emits total. +forgetest!(pragma_inconsistent_duplicates_among_conflict, |prj, cmd| { + prj.add_source("A", PRAGMA_EXACT_A); + prj.add_source("B", PRAGMA_EXACT_B); + prj.add_source("C", PRAGMA_CARET_C); + + cmd.arg("lint").args(["--only-lint", "pragma-inconsistent"]).assert_success().stderr_eq(str![ + [r#" +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + + +"#] + ]); +}); + +// Files without a `pragma solidity` directive must not affect the conflict computation. +// Note: `add_raw_source` is used here to bypass the helper that would otherwise inject a default +// `pragma solidity =;` for files that omit one. +forgetest!(pragma_inconsistent_files_without_pragma, |prj, cmd| { + prj.add_raw_source("A", PRAGMA_EXACT_A); + prj.add_raw_source("B", PRAGMA_CARET_B); + // C has no pragma at all; should be ignored by the cross-file check. + prj.add_raw_source("C", NO_PRAGMA_C); + + cmd.arg("lint").args(["--only-lint", "pragma-inconsistent"]).assert_success().stderr_eq(str![ + [r#" +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + [FILE]:3:1 + │ +3 │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + + +"#] + ]); +}); diff --git a/crates/forge/tests/cli/lint/geiger.rs b/crates/forge/tests/cli/lint/geiger.rs index faecfb212fb90..202866e83e35f 100644 --- a/crates/forge/tests/cli/lint/geiger.rs +++ b/crates/forge/tests/cli/lint/geiger.rs @@ -21,7 +21,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 9 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode Error: aborting due to 1 linter note(s) ... @@ -52,7 +52,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 9 │ bytes memory stuff = vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode Error: aborting due to 1 linter note(s) ... @@ -84,7 +84,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 9 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations [FILE]:10:20 @@ -92,7 +92,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 10 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations [FILE]:11:20 @@ -100,7 +100,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op 11 │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode Error: aborting due to 3 linter note(s) ... diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 242a0ebb4267f..031d80f0cf071 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -3241,7 +3241,7 @@ contract CounterScript is Script { error: the following required arguments were not provided: --broadcast -Usage: [..] script --broadcast --verify --rpc-url [ARGS]... +Usage: [..] script --broadcast --verify --rpc-url [ARGS]... For more information, try '--help'. diff --git a/crates/forge/tests/cli/test_cmd/fuzz.rs b/crates/forge/tests/cli/test_cmd/fuzz.rs index fefefb30d9b15..454b014a6e1bc 100644 --- a/crates/forge/tests/cli/test_cmd/fuzz.rs +++ b/crates/forge/tests/cli/test_cmd/fuzz.rs @@ -1,4 +1,5 @@ use alloy_primitives::U256; +use foundry_evm::fuzz::BaseCounterExample; use foundry_test_utils::{TestCommand, forgetest_init, str}; use regex::Regex; @@ -845,6 +846,8 @@ forgetest_init!(test_fuzz_random_uint_varies_across_runs, |prj, cmd| { prj.add_test( "RandomFuzzTest.t.sol", r#" +pragma solidity >=0.8.0; + import {Test} from "forge-std/Test.sol"; contract RandomFuzzTest is Test { @@ -868,3 +871,145 @@ Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) ... "#]]); }); + +forgetest_init!(test_fuzz_run_replays_random_uint_failure, |prj, cmd| { + prj.add_test( + "RandomFuzzTest.t.sol", + r#" +pragma solidity >=0.8.0; + +import {Test} from "forge-std/Test.sol"; + +contract RandomFuzzTest is Test { + function testFuzz_randomUint_shouldFail(uint256) public { + uint256 rand = vm.randomUint(0, 4); + assertTrue(rand != 0, "hit value 0"); + } +} + "#, + ); + + let expected_output = str![[r#" +... +Ran 1 test for test/RandomFuzzTest.t.sol:RandomFuzzTest +[FAIL: hit value 0; counterexample: [..]] testFuzz_randomUint_shouldFail(uint256) (runs: [..], [AVG_GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +"#]]; + + cmd.args(["test", "--fuzz-seed", "1", "--mt", "testFuzz_randomUint_shouldFail", "-j1"]) + .assert_failure() + .stdout_eq(expected_output.clone()); + + let failure_file = + prj.root().join("cache/fuzz/failures/RandomFuzzTest/testFuzz_randomUint_shouldFail"); + let persisted_failure: BaseCounterExample = + serde_json::from_slice(&std::fs::read(&failure_file).unwrap()).unwrap(); + assert_eq!(persisted_failure.fuzz.seed, Some(U256::from(1))); + assert_eq!(persisted_failure.fuzz.worker, Some(0)); + let fuzz_run = persisted_failure.fuzz.run.unwrap().to_string(); + let fuzz_worker = persisted_failure.fuzz.worker.unwrap().to_string(); + + cmd.forge_fuse() + .args([ + "test", + "--fuzz-seed", + "1", + "--fuzz-run", + &fuzz_run, + "--fuzz-worker", + &fuzz_worker, + "--mt", + "testFuzz_randomUint_shouldFail", + "-j1", + ]) + .assert_failure() + .stdout_eq(expected_output.clone()); + + cmd.forge_fuse().args(["test", "--rerun", "-j1"]).assert_failure().stdout_eq(expected_output); +}); + +forgetest_init!(test_fuzz_rerun_replays_random_uint_failure_without_seed, |prj, cmd| { + prj.add_test( + "RandomFuzzTest.t.sol", + r#" +pragma solidity >=0.8.0; + +import {Test} from "forge-std/Test.sol"; + +contract RandomFuzzTest is Test { + error Random(uint256 value); + + function testFuzz_randomUint_shouldFail(uint256) public { + revert Random(vm.randomUint()); + } +} + "#, + ); + + let expected_output = str![[r#" +... +Ran 1 test for test/RandomFuzzTest.t.sol:RandomFuzzTest +[FAIL: Random([..]); counterexample: [..]] testFuzz_randomUint_shouldFail(uint256) (runs: [..], [AVG_GAS]) +Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] +... +Tip: Run `forge test --rerun` to retry only the 1 failed test + +[SEED] (use `--fuzz-seed` to reproduce) + +"#]]; + + let assert = cmd + .args(["test", "--mt", "testFuzz_randomUint_shouldFail", "-j1"]) + .assert_failure() + .stdout_eq(expected_output.clone()); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let reason = random_failure_reason(&stdout); + + let failure_file = + prj.root().join("cache/fuzz/failures/RandomFuzzTest/testFuzz_randomUint_shouldFail"); + let persisted_failure: BaseCounterExample = + serde_json::from_slice(&std::fs::read(&failure_file).unwrap()).unwrap(); + let fuzz_seed = format!("{:#x}", persisted_failure.fuzz.seed.unwrap()); + let fuzz_run = persisted_failure.fuzz.run.unwrap().to_string(); + let fuzz_worker = persisted_failure.fuzz.worker.unwrap().to_string(); + + let assert = cmd + .forge_fuse() + .args([ + "test", + "--fuzz-seed", + &fuzz_seed, + "--fuzz-run", + &fuzz_run, + "--fuzz-worker", + &fuzz_worker, + "--mt", + "testFuzz_randomUint_shouldFail", + "-j1", + ]) + .assert_failure() + .stdout_eq(expected_output.clone()); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + assert_eq!(random_failure_reason(&stdout), reason, "{stdout}"); + + let assert = cmd + .forge_fuse() + .args(["test", "--rerun", "-j1"]) + .assert_failure() + .stdout_eq(expected_output); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + assert_eq!(random_failure_reason(&stdout), reason, "{stdout}"); + + let assert = cmd.forge_fuse().args(["test", "--rerun", "-j1"]).assert_failure(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + assert_eq!(random_failure_reason(&stdout), reason, "{stdout}"); +}); + +fn random_failure_reason(stdout: &str) -> String { + Regex::new(r"\[FAIL: (Random\([^)]+\))") + .unwrap() + .captures(stdout) + .unwrap_or_else(|| panic!("{stdout}"))[1] + .to_string() +} diff --git a/crates/forge/tests/cli/test_cmd/invariant/common.rs b/crates/forge/tests/cli/test_cmd/invariant/common.rs index fefd95def0412..107b387bb0ec5 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/common.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/common.rs @@ -2670,7 +2670,7 @@ contract InvariantWarp is Test { [FAIL: max time] [Sequence] (original: 3, shrunk: 1) vm.warp(block.timestamp + 656868); - vm.prank(0x00000000000000000000000000000000000012d2); + vm.prank(0x00000000000000000000000000000000000012d1); Warp(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); invariant_warp() (runs: 0, calls: 0, reverts: 2) ... diff --git a/crates/forge/tests/cli/test_cmd/invariant/mod.rs b/crates/forge/tests/cli/test_cmd/invariant/mod.rs index 723b5bd789c8b..bbe65f2f2f2fa 100644 --- a/crates/forge/tests/cli/test_cmd/invariant/mod.rs +++ b/crates/forge/tests/cli/test_cmd/invariant/mod.rs @@ -422,9 +422,9 @@ Failing tests: Encountered 1 failing test in test/InvariantSequenceLenTest.t.sol:InvariantSequenceLenTest [FAIL: invariant increment failure] [Sequence] (original: 3, shrunk: 3) - sender=0x00000000000000000000000000000000000014aD addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=0x0000000000000000000000000000000000001490 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] sender=0x8ef7F804bAd9183981A366EA618d9D47D3124649 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] - sender=0x00000000000000000000000000000000000016Ac addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[284406551521730736391345481857560031052359183671404042152984097777 [2.844e65]] + sender=0x00000000000000000000000000000000000016C5 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[284406551521730736391345481857560031052359183671404042152984097777 [2.844e65]] invariant_increment() (runs: 0, calls: 0, reverts: 0) Encountered a total of 1 failing tests, 0 tests succeeded @@ -448,11 +448,11 @@ Failing tests: Encountered 1 failing test in test/InvariantSequenceLenTest.t.sol:InvariantSequenceLenTest [FAIL: invariant increment failure] [Sequence] (original: 3, shrunk: 3) - vm.prank(0x00000000000000000000000000000000000014aD); + vm.prank(0x0000000000000000000000000000000000001490); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); vm.prank(0x8ef7F804bAd9183981A366EA618d9D47D3124649); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); - vm.prank(0x00000000000000000000000000000000000016Ac); + vm.prank(0x00000000000000000000000000000000000016C5); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).setNumber(284406551521730736391345481857560031052359183671404042152984097777); invariant_increment() (runs: 0, calls: 0, reverts: 0) @@ -476,9 +476,9 @@ Failing tests: Encountered 1 failing test in test/InvariantSequenceLenTest.t.sol:InvariantSequenceLenTest [FAIL: invariant increment failure] [Sequence] (original: 3, shrunk: 3) - sender=0x00000000000000000000000000000000000014aD addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=0x0000000000000000000000000000000000001490 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] sender=0x8ef7F804bAd9183981A366EA618d9D47D3124649 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] - sender=0x00000000000000000000000000000000000016Ac addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[284406551521730736391345481857560031052359183671404042152984097777 [2.844e65]] + sender=0x00000000000000000000000000000000000016C5 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[284406551521730736391345481857560031052359183671404042152984097777 [2.844e65]] invariant_increment() (runs: 1, calls: 1, reverts: 1) Encountered a total of 1 failing tests, 0 tests succeeded diff --git a/crates/forge/tests/cli/test_cmd/repros.rs b/crates/forge/tests/cli/test_cmd/repros.rs index 32bfe6a98a9fd..3803385b496ab 100644 --- a/crates/forge/tests/cli/test_cmd/repros.rs +++ b/crates/forge/tests/cli/test_cmd/repros.rs @@ -783,6 +783,66 @@ ParserError: Source "Missing.sol" not found: File not found. Searched the follow "#]]); }); +// https://github.com/foundry-rs/foundry/issues/10463 +forgetest_init!(issue_10463, |prj, cmd| { + prj.add_test( + "Issue10463.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Issue10463Test is Test { + event Foo(); + + error CustomError(uint256 code); + + function revertingBefore(bool shouldRevert) external { + if (shouldRevert) revert(); + emit Foo(); + } + + function revertingWithReason() external pure { + revert("revert reason"); + } + + function revertingWithCustomError() external pure { + revert CustomError(42); + } + + function testExpectEmitPreservesRevertWhenCallRevertsBeforeLog() public { + vm.expectEmit(); + emit Foo(); + + this.revertingBefore(true); + } + + function testExpectEmitPreservesRevertReason() public { + vm.expectEmit(); + emit Foo(); + + this.revertingWithReason(); + } + + function testExpectEmitPreservesCustomError() public { + vm.expectEmit(); + emit Foo(); + + this.revertingWithCustomError(); + } +} +"#, + ); + + cmd.arg("test").assert_failure().stdout_eq(str![[r#" +... +Ran 3 tests for test/Issue10463.t.sol:Issue10463Test +[FAIL: CustomError(42)] testExpectEmitPreservesCustomError() ([GAS]) +[FAIL: revert reason] testExpectEmitPreservesRevertReason() ([GAS]) +[FAIL: EvmError: Revert] testExpectEmitPreservesRevertWhenCallRevertsBeforeLog() ([GAS]) +Suite result: FAILED. 0 passed; 3 failed; 0 skipped; [ELAPSED] +... +"#]]); +}); + // https://github.com/foundry-rs/foundry/issues/12803 // Test gas underflow prevention on Cancun (no EIP-7702 gas floor) forgetest_init!(issue_12803_cancun, |prj, cmd| { diff --git a/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol b/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol index 7e482c6673155..838183a1b0b5e 100644 --- a/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol +++ b/crates/forge/tests/fixtures/ExpectRevertFailures.t.sol @@ -233,6 +233,63 @@ contract ExpectRevertWithReverterFailureTest is DSTest { aContract.doNotRevert(); aContract.callAndRevert(); } + + // + // Regression: must fail because 0xdead is not the actual reverter when a + // top-level CREATE constructor reverts directly. + function testShouldFailExpectRevertWrongReverterTopLevelCreate() public { + vm.expectRevert(address(0xdead)); + new DContract(); + } + + // + // Regression: must fail because the reverter address argument is enforced + // even when an exact-bytes pattern is also supplied for a top-level CREATE. + function testShouldFailExpectRevertWithBytesWrongReverterTopLevelCreate() public { + vm.expectRevert(abi.encodePacked("Reverted by DContract"), address(0xdead)); + new DContract(); + } + + // + // Regression: must fail because the reverter address argument is enforced + // for `expectPartialRevert(bytes4, address)` against a top-level CREATE. + function testShouldFailExpectPartialRevertWrongReverterTopLevelCreate() public { + vm.expectPartialRevert(bytes4(keccak256("Error(string)")), address(0xdead)); + new DContract(); + } + + // + // Regression: must fail when the innermost reverting frame is a nested + // CREATE and the reverter address argument does not match the would-be + // deployed address of the failed deployment. + function testShouldFailExpectRevertWrongReverterNestedCreate() public { + vm.expectRevert(address(0xdead)); + new NestedDContractCreator(); + } + + // + // Regression: documents the intended semantics for nested CREATEs — the + // matched reverter is the *outer* would-be-deployed address (the contract + // whose deployment failed), NOT the innermost reverting CREATE's address. + // Supplying the inner address must fail. + function testShouldFailExpectRevertNestedCreateInnerAddress() public { + // Outer = NestedDContractCreator at this contract's next nonce. + // Inner = DContract created from inside the outer constructor (deployer + // is the outer, nonce 1). + address outer = + vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + address inner = vm.computeCreateAddress(outer, 1); + vm.expectRevert(inner); + new NestedDContractCreator(); + } +} + +// Used by `testShouldFailExpectRevertWrongReverterNestedCreate`: a contract whose +// constructor directly creates another contract that reverts. +contract NestedDContractCreator { + constructor() { + new DContract(); + } } contract ExpectRevertCountFailureTest is DSTest { diff --git a/crates/lint/Cargo.toml b/crates/lint/Cargo.toml index 87864721432d9..589a0a5069e37 100644 --- a/crates/lint/Cargo.toml +++ b/crates/lint/Cargo.toml @@ -24,3 +24,7 @@ eyre.workspace = true heck.workspace = true rayon.workspace = true thiserror.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/lint/README.md b/crates/lint/README.md index 9e3f394eba832..9d5dad0c0272e 100644 --- a/crates/lint/README.md +++ b/crates/lint/README.md @@ -11,14 +11,20 @@ It helps enforce best practices and improve code quality within Foundry projects - `incorrect-shift`: Warns against shift operations where operands might be in the wrong order. - `unchecked-call`: Low-level calls should check the success return value. - `erc20-unchecked-transfer`: ERC20 `transfer` and `transferFrom` calls should check the return value. + - `rtlo`: Flags Unicode bidirectional override characters ("Trojan Source", CVE-2021-42574) that can hide malicious code. - **Medium Severity:** + - `boolean-cst`: Flags misuse of boolean constants. - `divide-before-multiply`: Warns against performing division before multiplication in the same expression, which can cause precision loss. - `incorrect-erc20-interface`: Flags ERC20 interfaces and implementations with non-compliant function signatures. - `incorrect-erc721-interface`: Flags ERC721 interfaces and implementations with non-compliant function signatures. + - `tx-origin`: Flags use of `tx.origin` in authorization-like predicates. - `unsafe-typecast`: Typecasts that can truncate values should be checked. - **Low Severity:** - `block-timestamp`: Warns when `block.timestamp` is used in a comparison, as it may be manipulated by validators. + - `missing-zero-check`: Address parameter is used in a state write or value transfer without a zero-address check. - **Informational / Style Guide:** + - `boolean-equal`: Boolean comparisons to constants should be simplified. + - `too-many-digits`: Numeric literals with 5+ consecutive zeros are error-prone. - `pascal-case-struct`: Flags for struct names not adhering to `PascalCase`. - `mixed-case-function`: Flags for function names not adhering to `mixedCase`. - `mixed-case-variable`: Flags for mutable variable names not adhering to `mixedCase`. @@ -28,9 +34,15 @@ It helps enforce best practices and improve code quality within Foundry projects - `unaliased-plain-import`: Use named imports `{A, B}` or alias `import ".." as X`. - `named-struct-fields`: Prefer initializing structs with named fields. - `unsafe-cheatcode`: Usage of unsafe cheatcodes that can perform dangerous operations. + - `multi-contract-file`: Prefer having only one contract, interface, or library per file. + - `interface-file-naming`: Interface file names should be prefixed with `I`. + - `interface-naming`: Interface names should be prefixed with `I`. + - `pragma-inconsistent`: Flags projects whose source files declare different Solidity pragma version requirements. - **Gas Optimizations:** - `asm-keccak256`: Recommends using inline assembly for `keccak256` for potential gas savings. + - `could-be-immutable`: Recommends declaring constructor-only state variables as `immutable`. - `custom-errors`: Recommends using custom errors instead of strings and plain reverts for potential gas savings. + - `unused-state-variables`: State variables that are never used should be removed. - **Code Size:** - `unwrapped-modifier-logic`: Recommends wrapping modifier logic to reduce contract code size. diff --git a/crates/lint/docs/README.md b/crates/lint/docs/README.md new file mode 100644 index 0000000000000..5eb4110a92e57 --- /dev/null +++ b/crates/lint/docs/README.md @@ -0,0 +1,52 @@ +# Forge lint documentation + +This directory contains one markdown file per registered `forge-lint` rule. Each file is referenced +by the lint's `help` URL (`https://getfoundry.sh/forge/linting/`) and is consumed by the +[Foundry book](https://github.com/foundry-rs/book) to render the lint reference page. + +## Adding a new lint + +When you add a new lint with `declare_forge_lint!`, you **must** also add a documentation file at +`crates/lint/docs/.md`. The presence of the file is enforced by the +`registered_lints_have_docs` unit test in [`crates/lint/src/sol/mod.rs`](../src/sol/mod.rs). + +Use [`_template.md`](./_template.md) as a starting point. + +## File structure + +Each lint doc file should follow this structure: + +```markdown +# + +**Severity**: `` +**ID**: `` + +A one-paragraph description of what this lint detects and why it matters. + +## What it does + +Explain precisely what the lint flags. + +## Why is this bad? + +Explain the impact (security, correctness, gas, readability). + +## Example + +### Bad + +```solidity +// triggering example +``` + +### Good + +```solidity +// non-triggering, recommended example +``` + +## Configuration + +Document any inline-config or `foundry.toml` options that affect this lint, if any. +``` diff --git a/crates/lint/docs/_template.md b/crates/lint/docs/_template.md new file mode 100644 index 0000000000000..41c735a0ba579 --- /dev/null +++ b/crates/lint/docs/_template.md @@ -0,0 +1,28 @@ +# + +**Severity**: `` +**ID**: `` + +One-paragraph summary of what this lint detects and why it matters. + +## What it does + +Explain precisely what the lint flags. + +## Why is this bad? + +Explain the impact (security, correctness, gas, readability). + +## Example + +### Bad + +```solidity +// triggering example +``` + +### Good + +```solidity +// non-triggering, recommended example +``` diff --git a/crates/lint/docs/asm-keccak256.md b/crates/lint/docs/asm-keccak256.md new file mode 100644 index 0000000000000..4678cfe9f8d12 --- /dev/null +++ b/crates/lint/docs/asm-keccak256.md @@ -0,0 +1,42 @@ +# Inefficient keccak256 call + +**Severity**: `Gas` +**ID**: `asm-keccak256` + +Flags calls to the high-level `keccak256(...)` builtin that can be cheaply rewritten with inline +assembly. + +## What it does + +Reports `keccak256(arg)` calls and (when possible) emits a fix suggestion that uses inline +assembly to compute the hash directly, avoiding the overhead of the high-level call. + +## Why is this bad? + +The high-level `keccak256` call performs additional memory management and ABI encoding compared +to a direct `keccak256(ptr, len)` opcode invocation. In hot paths the difference is visible in +gas reports. + +## Example + +### Bad + +```solidity +bytes32 h = keccak256(abi.encodePacked(a, b)); +``` + +### Good + +```solidity +bytes32 h; +assembly ("memory-safe") { + let m := mload(0x40) + mstore(m, a) + mstore(add(m, 0x20), b) + h := keccak256(m, 0x40) +} +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/block-timestamp.md b/crates/lint/docs/block-timestamp.md new file mode 100644 index 0000000000000..a51b55ff5d8cc --- /dev/null +++ b/crates/lint/docs/block-timestamp.md @@ -0,0 +1,44 @@ +# Use of block.timestamp in comparisons + +**Severity**: `Low` +**ID**: `block-timestamp` + +Flags use of `block.timestamp` as an operand of a comparison, where its value can be slightly +manipulated by the block proposer. + +## What it does + +Reports any comparison expression (`<`, `<=`, `>`, `>=`, `==`, `!=`) that directly or +transitively reads `block.timestamp`. + +## Why is this bad? + +Block proposers can adjust `block.timestamp` within a small window (a few seconds). This is +usually harmless, but for short-window logic — auctions ending, randomness, time-locked +withdrawals — a few seconds of manipulation can be enough for an attacker to capture value. + +Using `block.timestamp` for general scheduling (hours/days) is fine; what's risky is fine-grained +timing and treating timestamps as a source of randomness. + +## Example + +### Bad + +```solidity +function settle() external { + require(block.timestamp >= auctionEnd, "auction ongoing"); + // ... +} +``` + +### Good + +```solidity +// Prefer block numbers for tight windows, or accept a clearly large grace period. +require(block.number >= endBlock, "auction ongoing"); +``` + +## Notes + +This lint is intentionally conservative: not every flagged comparison is exploitable. Review +each occurrence in context. diff --git a/crates/lint/docs/boolean-cst.md b/crates/lint/docs/boolean-cst.md new file mode 100644 index 0000000000000..f5c65dfec2789 --- /dev/null +++ b/crates/lint/docs/boolean-cst.md @@ -0,0 +1,37 @@ +# Misuse of a boolean constant + +**Severity**: `Med` +**ID**: `boolean-cst` + +Flags expressions where a boolean constant (`true`/`false`) is used as a control-flow condition +or operand of a boolean operator, which usually indicates dead code or a leftover debug toggle. + +## What it does + +Reports `if (true)`, `if (false)`, `while (true)` outside of intentional infinite loops, and +boolean operators (`&&`, `||`) where one side is a literal `true`/`false`. + +## Why is this bad? + +A literal boolean as a condition makes the surrounding branch dead, hides logic errors, or +preserves a forgotten debug shortcut that bypasses real checks. + +## Example + +### Bad + +```solidity +if (true) { // always taken + doSomething(); +} +require(condition && true, "unreachable"); // 'true' is redundant +``` + +### Good + +```solidity +if (condition) { + doSomething(); +} +require(condition, "..."); +``` diff --git a/crates/lint/docs/boolean-equal.md b/crates/lint/docs/boolean-equal.md new file mode 100644 index 0000000000000..9397003b039b4 --- /dev/null +++ b/crates/lint/docs/boolean-equal.md @@ -0,0 +1,34 @@ +# Boolean comparison to a constant + +**Severity**: `Info` +**ID**: `boolean-equal` + +Flags expressions of the form `x == true`, `x == false`, `x != true`, `x != false`, which can be +simplified. + +## What it does + +Reports any equality comparison between a boolean expression and a literal `true` or `false`. + +## Why is this bad? + +Comparing a boolean to a boolean literal is redundant and harms readability. Use the boolean +expression directly (or its negation). + +## Example + +### Bad + +```solidity +if (paused == true) revert(); +if (paused == false) doSomething(); +require(ok != false, "fail"); +``` + +### Good + +```solidity +if (paused) revert(); +if (!paused) doSomething(); +require(ok, "fail"); +``` diff --git a/crates/lint/docs/could-be-immutable.md b/crates/lint/docs/could-be-immutable.md new file mode 100644 index 0000000000000..bda1de6379955 --- /dev/null +++ b/crates/lint/docs/could-be-immutable.md @@ -0,0 +1,42 @@ +# State variable could be immutable + +**Severity**: `Gas` +**ID**: `could-be-immutable` + +Flags state variables that are assigned only in the constructor and never written to afterward — +making them eligible to be declared `immutable`. + +## What it does + +Reports each non-`constant`, non-`immutable` state variable whose only writes occur in the +constructor (or in initialization at declaration time). + +## Why is this bad? + +`immutable` state variables are stored in the deployed bytecode rather than in storage, eliminating +an `SLOAD` per access and saving substantial gas across the contract's lifetime. Declaring such +variables `immutable` also expresses intent and prevents future writes. + +## Example + +### Bad + +```solidity +contract C { + address owner; + constructor() { owner = msg.sender; } +} +``` + +### Good + +```solidity +contract C { + address immutable OWNER; + constructor() { OWNER = msg.sender; } +} +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/custom-errors.md b/crates/lint/docs/custom-errors.md new file mode 100644 index 0000000000000..9e01e01d593e9 --- /dev/null +++ b/crates/lint/docs/custom-errors.md @@ -0,0 +1,45 @@ +# Prefer custom errors over revert strings + +**Severity**: `Gas` +**ID**: `custom-errors` + +Flags `require(cond, "message")`, `revert("message")`, and `revert()` calls; suggests replacing +them with a `revert CustomError(...)`. + +## What it does + +Reports `require` calls whose second argument is a string literal, and `revert(...)` calls that +are either bare or have a string-literal argument. + +## Why is this bad? + +Custom errors: +- cost less gas than encoding/decoding a string, +- can carry typed parameters for richer diagnostics, +- shrink contract bytecode (string constants live in code). + +Solidity 0.8.4+ supports custom errors natively. + +## Example + +### Bad + +```solidity +require(amount > 0, "amount must be > 0"); +revert("not authorized"); +revert(); +``` + +### Good + +```solidity +error AmountZero(); +error NotAuthorized(); + +if (amount == 0) revert AmountZero(); +if (!authorized) revert NotAuthorized(); +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/divide-before-multiply.md b/crates/lint/docs/divide-before-multiply.md new file mode 100644 index 0000000000000..f082bef19a1bd --- /dev/null +++ b/crates/lint/docs/divide-before-multiply.md @@ -0,0 +1,32 @@ +# Divide before multiply + +**Severity**: `Med` +**ID**: `divide-before-multiply` + +Flags arithmetic expressions where division is performed before multiplication, which can cause +unintended precision loss in integer arithmetic. + +## What it does + +Warns on expressions of the form `(a / b) * c` (or equivalent shapes), where the integer division +truncates before the result is multiplied. + +## Why is this bad? + +Solidity's integer division truncates toward zero. Performing `(a / b) * c` discards the remainder +of `a / b` before scaling, while `(a * c) / b` preserves precision. This pattern frequently +manifests as fee/share/yield miscalculations. + +## Example + +### Bad + +```solidity +uint256 share = (amount / total) * weight; // truncates first, then scales +``` + +### Good + +```solidity +uint256 share = (amount * weight) / total; // preserves precision +``` diff --git a/crates/lint/docs/erc20-unchecked-transfer.md b/crates/lint/docs/erc20-unchecked-transfer.md new file mode 100644 index 0000000000000..d7d053e020cca --- /dev/null +++ b/crates/lint/docs/erc20-unchecked-transfer.md @@ -0,0 +1,43 @@ +# Unchecked ERC20 transfer return value + +**Severity**: `High` +**ID**: `erc20-unchecked-transfer` + +Flags calls to ERC20 `transfer` and `transferFrom` where the boolean return value is ignored. + +## What it does + +Warns when a function with the same signature as +`transfer(address,uint256)` or `transferFrom(address,address,uint256)` and a `bool` return type is +invoked but the result is not checked. + +## Why is this bad? + +The ERC20 spec allows tokens to signal failure by returning `false` instead of reverting. Ignoring +the return value lets a "failed" transfer go unnoticed, allowing accounting to drift and creating +common DeFi exploits. Use a wrapper such as OpenZeppelin's `SafeERC20` or check the boolean +explicitly. + +## Example + +### Bad + +```solidity +token.transfer(to, amount); +token.transferFrom(from, to, amount); +``` + +### Good + +```solidity +require(token.transfer(to, amount), "transfer failed"); +require(token.transferFrom(from, to, amount), "transferFrom failed"); + +// or use SafeERC20 +SafeERC20.safeTransfer(token, to, amount); +``` + +## Notes + +This lint can produce false positives when the callee does not strictly conform to the ERC20 +interface (e.g. tokens that revert on failure rather than returning `false`). diff --git a/crates/lint/docs/incorrect-erc20-interface.md b/crates/lint/docs/incorrect-erc20-interface.md new file mode 100644 index 0000000000000..65fb8313c205f --- /dev/null +++ b/crates/lint/docs/incorrect-erc20-interface.md @@ -0,0 +1,42 @@ +# Incorrect ERC20 interface + +**Severity**: `Med` +**ID**: `incorrect-erc20-interface` + +Flags interfaces or contracts whose function signatures match an ERC20 method by name and +parameters but use the wrong return type. + +## What it does + +For each function whose name and parameter types match a canonical ERC20 method +(`totalSupply`, `balanceOf`, `transfer`, `transferFrom`, `approve`, `allowance`), the lint checks +that the return type matches the spec. A mismatch is reported. + +## Why is this bad? + +Tokens that diverge from the ERC20 spec break composability with the wider ecosystem (DEXes, +lending protocols, multisigs) and are a common source of integration bugs and exploits. + +## Example + +### Bad + +```solidity +interface IBadERC20 { + function balanceOf(address) external view returns (bool); // should be uint256 + function transfer(address, uint256) external; // should return bool +} +``` + +### Good + +```solidity +interface IERC20 { + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); +} +``` diff --git a/crates/lint/docs/incorrect-erc721-interface.md b/crates/lint/docs/incorrect-erc721-interface.md new file mode 100644 index 0000000000000..4803afdde7cc1 --- /dev/null +++ b/crates/lint/docs/incorrect-erc721-interface.md @@ -0,0 +1,48 @@ +# Incorrect ERC721 interface + +**Severity**: `Med` +**ID**: `incorrect-erc721-interface` + +Flags interfaces or contracts whose function signatures match an ERC721 (or ERC165) method by +name and parameters but use the wrong return type. + +## What it does + +For each function whose name and parameter types match a canonical ERC721/ERC165 method +(`balanceOf`, `ownerOf`, `safeTransferFrom`, `transferFrom`, `approve`, `setApprovalForAll`, +`getApproved`, `isApprovedForAll`, `supportsInterface`), the lint checks that the return type +matches the spec. A mismatch is reported. + +## Why is this bad? + +Non-conforming NFT contracts break marketplaces, indexers, and any protocol that relies on the +ERC721 spec. A wrong return type often compiles and deploys silently but causes integration +failures at runtime. + +## Example + +### Bad + +```solidity +interface IBadERC721 { + function balanceOf(address) external view returns (bool); // should be uint256 + function ownerOf(uint256) external view returns (bool); // should be address + function supportsInterface(bytes4) external view returns (uint256); // should be bool +} +``` + +### Good + +```solidity +interface IERC721 { + function balanceOf(address owner) external view returns (uint256); + function ownerOf(uint256 tokenId) external view returns (address); + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function transferFrom(address from, address to, uint256 tokenId) external; + function approve(address to, uint256 tokenId) external; + function setApprovalForAll(address operator, bool approved) external; + function getApproved(uint256 tokenId) external view returns (address); + function isApprovedForAll(address owner, address operator) external view returns (bool); + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} +``` diff --git a/crates/lint/docs/incorrect-shift.md b/crates/lint/docs/incorrect-shift.md new file mode 100644 index 0000000000000..a9a70c7f93128 --- /dev/null +++ b/crates/lint/docs/incorrect-shift.md @@ -0,0 +1,37 @@ +# Incorrect shift order + +**Severity**: `High` +**ID**: `incorrect-shift` + +Flags shift operations where a literal appears on the left and a non-literal on the right, which +is almost always the wrong operand order. + +## What it does + +Warns when the left-hand operand of `<<` or `>>` is a numeric literal and the right-hand operand +is a non-literal expression (e.g. a variable, function call, or composite expression). + +## Why is this bad? + +Shift expressions like `2 << x` are usually a typo for `x << 2`. In the former, the *value being +shifted* is a tiny constant and the *shift amount* is dynamic — almost never the intended +behavior, and a known source of bugs in production contracts. + +## Example + +### Bad + +```solidity +result = 2 << stateValue; // shift amount comes from state +result = 8 >> localValue; // shift amount comes from a local +result = 16 << (stateValue + 1); // shift amount is a dynamic expression +``` + +### Good + +```solidity +result = stateValue << 2; +result = localValue >> 3; +result = stateValue << localShiftAmount; +result = 1 << 8; // both literals — fine +``` diff --git a/crates/lint/docs/inline-assembly.md b/crates/lint/docs/inline-assembly.md new file mode 100644 index 0000000000000..bba61148b84c5 --- /dev/null +++ b/crates/lint/docs/inline-assembly.md @@ -0,0 +1,69 @@ +# Inline assembly + +**Severity**: `Info` +**ID**: `inline-assembly` + +Flags every `assembly { ... }` block. Inline assembly bypasses many of Solidity's safety +features (type checks, overflow checks, memory layout invariants) and is a common source of +high-impact bugs, so each occurrence should be reviewed deliberately. + +## What it does + +Reports every inline assembly statement, including blocks declared with the `"evmasm"` dialect +and/or the `("memory-safe")` flag. Blocks declared as memory-safe — either via the modern +`("memory-safe")` flag or the legacy `/// @solidity memory-safe-assembly` NatSpec marker — are +still reported, but with a softer message acknowledging the developer attestation: review +focuses on business logic and side effects rather than memory layout. + +## Why is this bad? + +Assembly skips Solidity's compile-time checks and many of its runtime guarantees. Mistakes +inside an `assembly` block can corrupt memory, break the free memory pointer, leak storage, +escalate privileges via `delegatecall`, or destroy the contract via `selfdestruct`. Even when +required for gas or features unavailable in high-level Solidity, assembly should be small, +documented, and reviewed. + +## When inline assembly is reasonable + +Some idioms are widely used and generally safe: + +- Reading transaction/chain context: `chainid()`, `gas()`, `returndatasize()`. +- Probing code: `codesize()`, `extcodesize(addr)`, `extcodehash(addr)`. +- Reading the free memory pointer: `mload(0x40)`. +- Cheap hashing of a known memory layout, when paired with `("memory-safe")`. + +If you must use assembly: + +1. Keep the block minimal and well-commented. +2. Add the `("memory-safe")` flag when the block does not violate Solidity's memory model, so + the optimizer (and reviewers) can rely on it. The legacy + `/// @solidity memory-safe-assembly` NatSpec marker on the line directly above the block is + also recognized for compatibility with older codebases. +3. Suppress the lint locally to mark the block as audited: + ```solidity + // forge-lint: disable-next-line(inline-assembly) + assembly ("memory-safe") { /* reviewed: ... */ } + ``` + +## Example + +### Bad + +```solidity +function rawCall(address target, bytes calldata data) external returns (bytes memory) { + assembly { + let ok := call(gas(), target, 0, add(data.offset, 0), data.length, 0, 0) + // ... + } +} +``` + +### Good + +```solidity +function rawCall(address target, bytes calldata data) external returns (bytes memory result) { + bool ok; + (ok, result) = target.call(data); + require(ok, "call failed"); +} +``` diff --git a/crates/lint/docs/interface-file-naming.md b/crates/lint/docs/interface-file-naming.md new file mode 100644 index 0000000000000..ff72a0c175e8e --- /dev/null +++ b/crates/lint/docs/interface-file-naming.md @@ -0,0 +1,31 @@ +# Interface file naming + +**Severity**: `Info` +**ID**: `interface-file-naming` + +Flags Solidity files whose only top-level declaration is an interface but whose filename is not +prefixed with `I`. + +## What it does + +Reports interface-only files whose path basename does not start with `I` (e.g. `IERC20.sol`). + +## Why is this bad? + +Prefixing interface filenames with `I` is the prevailing convention in the Solidity ecosystem. +Following it makes import paths predictable and lets reviewers tell at a glance whether they are +looking at an interface or an implementation. + +## Example + +### Bad + +```text +contracts/Token.sol // file contains only `interface Token { ... }` +``` + +### Good + +```text +contracts/IToken.sol // file contains only `interface IToken { ... }` +``` diff --git a/crates/lint/docs/interface-naming.md b/crates/lint/docs/interface-naming.md new file mode 100644 index 0000000000000..5c6b12b946091 --- /dev/null +++ b/crates/lint/docs/interface-naming.md @@ -0,0 +1,31 @@ +# Interface name should be prefixed with 'I' + +**Severity**: `Info` +**ID**: `interface-naming` + +Flags `interface` declarations whose names are not prefixed with `I`. + +## What it does + +Reports `interface Foo` where `Foo` does not start with `I` (e.g. `IFoo`). + +## Why is this bad? + +Prefixing interfaces with `I` is the prevailing convention in Solidity codebases (`IERC20`, +`IERC721`, `IUniswapV2Pair`, ...). Following it makes the role of each type unambiguous at use +sites and aligns with the matching +[`interface-file-naming`](https://getfoundry.sh/forge/linting/interface-file-naming) lint. + +## Example + +### Bad + +```solidity +interface ERC20 { /* ... */ } +``` + +### Good + +```solidity +interface IERC20 { /* ... */ } +``` diff --git a/crates/lint/docs/missing-zero-check.md b/crates/lint/docs/missing-zero-check.md new file mode 100644 index 0000000000000..7eab1f3a00117 --- /dev/null +++ b/crates/lint/docs/missing-zero-check.md @@ -0,0 +1,39 @@ +# Missing zero-address check + +**Severity**: `Low` +**ID**: `missing-zero-check` + +Flags entry-point functions and constructors where an `address` parameter flows into a state write +or value transfer without a zero-address guard. + +## What it does + +Performs a taint analysis from each `address` parameter of an externally callable, state-mutating +function (or constructor) and reports a parameter that reaches a sink (state write, `transfer`, +`call{value: ...}`, etc.) without first being compared against `address(0)` in an `if`/`require`/ +`assert` predicate. + +## Why is this bad? + +Forgetting a zero-address check is a common source of value loss: tokens become permanently +unrecoverable, ownership is renounced unintentionally, or upgrades are bricked. Adding an explicit +guard is cheap and removes an entire class of operational mistakes. + +## Example + +### Bad + +```solidity +function setOwner(address newOwner) external onlyOwner { + owner = newOwner; // no zero-address check +} +``` + +### Good + +```solidity +function setOwner(address newOwner) external onlyOwner { + require(newOwner != address(0), "zero address"); + owner = newOwner; +} +``` diff --git a/crates/lint/docs/mixed-case-function.md b/crates/lint/docs/mixed-case-function.md new file mode 100644 index 0000000000000..9997dcb5691c7 --- /dev/null +++ b/crates/lint/docs/mixed-case-function.md @@ -0,0 +1,32 @@ +# Function names should use mixedCase + +**Severity**: `Info` +**ID**: `mixed-case-function` + +Flags function names that do not follow `mixedCase`. + +## What it does + +Reports functions whose names contain underscores, start with an uppercase letter, or otherwise +deviate from `mixedCase`. Test functions starting with `test`, `invariant_`, or `statefulFuzz` +and user-defined patterns (e.g. `ERC20`) are exempted. + +## Why is this bad? + +The Solidity style guide recommends `mixedCase` for function names. Consistent style makes call +sites uniform, helps editor tooling, and reduces friction in code review. + +## Example + +### Bad + +```solidity +function get_balance() external view returns (uint256); +function GetBalance() external view returns (uint256); +``` + +### Good + +```solidity +function getBalance() external view returns (uint256); +``` diff --git a/crates/lint/docs/mixed-case-variable.md b/crates/lint/docs/mixed-case-variable.md new file mode 100644 index 0000000000000..3341e1a0c48ad --- /dev/null +++ b/crates/lint/docs/mixed-case-variable.md @@ -0,0 +1,36 @@ +# Mutable variable names should use mixedCase + +**Severity**: `Info` +**ID**: `mixed-case-variable` + +Flags mutable variable names (locals, parameters, mutable state) that do not follow `mixedCase`. + +## What it does + +Reports mutable variable identifiers that contain underscores, start with an uppercase letter, +or otherwise deviate from `mixedCase`. + +`constant` and `immutable` state variables are not flagged by this lint — see +[`screaming-snake-case-const`](https://getfoundry.sh/forge/linting/screaming-snake-case-const) and +[`screaming-snake-case-immutable`](https://getfoundry.sh/forge/linting/screaming-snake-case-immutable). + +## Why is this bad? + +The Solidity style guide recommends `mixedCase` for mutable variables. Consistent style makes +code easier to scan and review. + +## Example + +### Bad + +```solidity +uint256 public total_supply; +address Owner; +``` + +### Good + +```solidity +uint256 public totalSupply; +address owner; +``` diff --git a/crates/lint/docs/multi-contract-file.md b/crates/lint/docs/multi-contract-file.md new file mode 100644 index 0000000000000..beabc827e4ea6 --- /dev/null +++ b/crates/lint/docs/multi-contract-file.md @@ -0,0 +1,37 @@ +# Multiple contracts in one file + +**Severity**: `Info` +**ID**: `multi-contract-file` + +Flags source files that declare more than one top-level contract, interface, or library. + +## What it does + +Reports each top-level `contract`, `interface`, or `library` definition (after the first) in a +file that contains more than one such declaration. + +## Why is this bad? + +Keeping one contract per file improves discoverability (`grep`, IDE jump-to-file), simplifies +import paths, and avoids unintentional bytecode bloat from artifacts that bundle unrelated +contracts. + +## Example + +### Bad + +```solidity +// File: Token.sol +contract TokenA { /* ... */ } +contract TokenB { /* ... */ } +``` + +### Good + +```solidity +// File: TokenA.sol +contract TokenA { /* ... */ } + +// File: TokenB.sol +contract TokenB { /* ... */ } +``` diff --git a/crates/lint/docs/named-struct-fields.md b/crates/lint/docs/named-struct-fields.md new file mode 100644 index 0000000000000..45713e2555ddc --- /dev/null +++ b/crates/lint/docs/named-struct-fields.md @@ -0,0 +1,31 @@ +# Prefer named struct fields + +**Severity**: `Info` +**ID**: `named-struct-fields` + +Flags struct construction expressions that pass fields positionally instead of by name. + +## What it does + +Reports `Struct(a, b, c)` style struct construction; suggests `Struct({ field1: a, field2: b, +field3: c })` instead. + +## Why is this bad? + +Positional struct construction is fragile: adding or reordering fields silently changes the +meaning of every existing call site. Named-field construction is self-documenting and resilient +to struct changes. + +## Example + +### Bad + +```solidity +User memory u = User(addr, 100, true); +``` + +### Good + +```solidity +User memory u = User({ wallet: addr, balance: 100, active: true }); +``` diff --git a/crates/lint/docs/pascal-case-struct.md b/crates/lint/docs/pascal-case-struct.md new file mode 100644 index 0000000000000..02a243bd56bf4 --- /dev/null +++ b/crates/lint/docs/pascal-case-struct.md @@ -0,0 +1,31 @@ +# Struct names should use PascalCase + +**Severity**: `Info` +**ID**: `pascal-case-struct` + +Flags struct definitions whose names do not follow `PascalCase`. + +## What it does + +Reports any `struct` whose identifier does not match the `PascalCase` convention. + +## Why is this bad? + +The Solidity style guide recommends `PascalCase` for type-like names (contracts, structs, +enums, libraries). Consistent casing makes code easier to scan and integrates with editor +features and external tooling. + +## Example + +### Bad + +```solidity +struct user_info { uint256 balance; } +struct USERINFO { uint256 balance; } +``` + +### Good + +```solidity +struct UserInfo { uint256 balance; } +``` diff --git a/crates/lint/docs/pragma-inconsistent.md b/crates/lint/docs/pragma-inconsistent.md new file mode 100644 index 0000000000000..095f45783773d --- /dev/null +++ b/crates/lint/docs/pragma-inconsistent.md @@ -0,0 +1,41 @@ +# Inconsistent pragma directives + +**Severity**: `Info` +**ID**: `pragma-inconsistent` + +Flags projects whose source files declare incompatible or differently-shaped Solidity version +pragmas. + +## What it does + +Inspects every `pragma solidity ...;` directive across all input source files and reports when +their version requirements are inconsistent (different exact versions, mixed caret/tilde/range +shapes, etc.). + +## Why is this bad? + +A project compiled under multiple Solidity versions can subtly change behavior between files +(e.g. checked arithmetic, default visibility, ABI encoding). Aligning pragmas across the project +removes a hidden source of integration bugs and makes upgrades coordinated. + +## Example + +### Bad + +```solidity +// A.sol +pragma solidity 0.8.18; + +// B.sol +pragma solidity ^0.8.20; + +// C.sol +pragma solidity >=0.7.0 <0.9.0; +``` + +### Good + +```solidity +// All files +pragma solidity 0.8.20; +``` diff --git a/crates/lint/docs/rtlo.md b/crates/lint/docs/rtlo.md new file mode 100644 index 0000000000000..58ce648752c6f --- /dev/null +++ b/crates/lint/docs/rtlo.md @@ -0,0 +1,32 @@ +# Right-to-left override character + +**Severity**: `High` +**ID**: `rtlo` + +Flags the presence of Unicode bidirectional override characters in source code, which can be used +to hide malicious behavior ("Trojan Source", [CVE-2021-42574](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574)). + +## What it does + +Detects the right-to-left override codepoint (`U+202E`) and other bidirectional control characters +embedded in identifiers, strings, and comments. + +## Why is this bad? + +These characters render source code in a different visual order than how the compiler reads it, +allowing an attacker to make malicious code look benign on review. Solidity contracts are public +and frequently audited visually; this attack vector must not be ignored. + +## Example + +### Bad + +```solidity +// transfer(victim‮, attacker)/* // U+202E hidden between args +``` + +### Good + +```solidity +// Avoid bidirectional override characters in code and comments. +``` diff --git a/crates/lint/docs/screaming-snake-case-const.md b/crates/lint/docs/screaming-snake-case-const.md new file mode 100644 index 0000000000000..72a16c5875fae --- /dev/null +++ b/crates/lint/docs/screaming-snake-case-const.md @@ -0,0 +1,30 @@ +# Constants should use SCREAMING_SNAKE_CASE + +**Severity**: `Info` +**ID**: `screaming-snake-case-const` + +Flags `constant` state variables whose names do not follow `SCREAMING_SNAKE_CASE`. + +## What it does + +Reports state variables declared `constant` whose identifier deviates from `SCREAMING_SNAKE_CASE`. + +## Why is this bad? + +The Solidity style guide recommends `SCREAMING_SNAKE_CASE` for constants so they stand out from +mutable state and immutables at call sites. + +## Example + +### Bad + +```solidity +uint256 constant maxSupply = 1_000_000; +uint256 constant Max_Supply = 1_000_000; +``` + +### Good + +```solidity +uint256 constant MAX_SUPPLY = 1_000_000; +``` diff --git a/crates/lint/docs/screaming-snake-case-immutable.md b/crates/lint/docs/screaming-snake-case-immutable.md new file mode 100644 index 0000000000000..cee5590e16d27 --- /dev/null +++ b/crates/lint/docs/screaming-snake-case-immutable.md @@ -0,0 +1,31 @@ +# Immutables should use SCREAMING_SNAKE_CASE + +**Severity**: `Info` +**ID**: `screaming-snake-case-immutable` + +Flags `immutable` state variables whose names do not follow `SCREAMING_SNAKE_CASE`. + +## What it does + +Reports state variables declared `immutable` whose identifier deviates from +`SCREAMING_SNAKE_CASE`. + +## Why is this bad? + +The Solidity style guide recommends `SCREAMING_SNAKE_CASE` for `immutable` variables so they +visually align with `constant` ones and stand out from mutable state at call sites. + +## Example + +### Bad + +```solidity +address immutable owner; +address immutable Owner; +``` + +### Good + +```solidity +address immutable OWNER; +``` diff --git a/crates/lint/docs/too-many-digits.md b/crates/lint/docs/too-many-digits.md new file mode 100644 index 0000000000000..5decb67bec9c3 --- /dev/null +++ b/crates/lint/docs/too-many-digits.md @@ -0,0 +1,32 @@ +# Numeric literal with too many digits + +**Severity**: `Info` +**ID**: `too-many-digits` + +Flags numeric literals containing five or more consecutive zeros, which are easy to misread. + +## What it does + +Reports decimal numeric literals that contain a run of 5 or more `0` characters. + +## Why is this bad? + +Long sequences of zeros are difficult to count visually, and an off-by-one zero is a common bug +(e.g. funding `1_000_000` instead of `10_000_000`). Use scientific notation, sub-denominations, or +underscore separators to make the magnitude obvious. + +## Example + +### Bad + +```solidity +uint256 amount = 1000000000000000000; +``` + +### Good + +```solidity +uint256 amount = 1e18; +uint256 amount2 = 1 ether; +uint256 amount3 = 1_000_000_000_000_000_000; +``` diff --git a/crates/lint/docs/tx-origin.md b/crates/lint/docs/tx-origin.md new file mode 100644 index 0000000000000..26877cf9c0116 --- /dev/null +++ b/crates/lint/docs/tx-origin.md @@ -0,0 +1,34 @@ +# Use of tx.origin for authorization + +**Severity**: `Med` +**ID**: `tx-origin` + +Flags use of `tx.origin` inside authorization-like predicates such as `require`, `assert`, `if`, +`while`, and `for` conditions. + +## What it does + +Reports `tx.origin` reads when they are used as part of a guard condition. Plain reads outside of +guard predicates are not reported. + +## Why is this bad? + +`tx.origin` is the original externally owned account that started the whole transaction, not the +immediate caller. If authorization checks rely on `tx.origin`, a malicious contract can call the +protected contract while the legitimate owner is the transaction origin. + +Use `msg.sender` for authorization checks instead. + +## Example + +### Bad + +```solidity +require(tx.origin == owner, "not owner"); +``` + +### Good + +```solidity +require(msg.sender == owner, "not owner"); +``` diff --git a/crates/lint/docs/unaliased-plain-import.md b/crates/lint/docs/unaliased-plain-import.md new file mode 100644 index 0000000000000..be8c5120028d6 --- /dev/null +++ b/crates/lint/docs/unaliased-plain-import.md @@ -0,0 +1,34 @@ +# Unaliased plain import + +**Severity**: `Info` +**ID**: `unaliased-plain-import` + +Flags `import "path";` statements that pull in every top-level symbol from another file without +an alias. + +## What it does + +Reports plain imports of the form `import "path";`. Suggests using either named imports +(`import { A, B } from "path"`) or an aliased import (`import "path" as X`). + +## Why is this bad? + +Plain imports pollute the importing file's namespace and make the source of each symbol +non-obvious. Named or aliased imports make the dependency surface explicit and reduce the chance +of accidental name collisions. + +## Example + +### Bad + +```solidity +import "./Lib.sol"; +``` + +### Good + +```solidity +import { Foo, Bar } from "./Lib.sol"; +// or +import "./Lib.sol" as Lib; +``` diff --git a/crates/lint/docs/unchecked-call.md b/crates/lint/docs/unchecked-call.md new file mode 100644 index 0000000000000..9a0a4143a0e0e --- /dev/null +++ b/crates/lint/docs/unchecked-call.md @@ -0,0 +1,34 @@ +# Unchecked low-level call + +**Severity**: `High` +**ID**: `unchecked-call` + +Flags low-level calls (`call`, `delegatecall`, `staticcall`, `callcode`) whose `success` return +value is ignored. + +## What it does + +Warns when the boolean returned by a low-level call is discarded — either because the return value +is not assigned or because only the `bytes memory` payload is used. + +## Why is this bad? + +Low-level calls do **not** revert when the callee fails; they silently return `false`. Ignoring +the success flag means a failed call is indistinguishable from a successful one, leading to bugs +where state is updated on the assumption that an external interaction succeeded. + +## Example + +### Bad + +```solidity +target.call(data); // success ignored +(, bytes memory ret) = target.call(data); // only payload kept +``` + +### Good + +```solidity +(bool ok, ) = target.call(data); +require(ok, "call failed"); +``` diff --git a/crates/lint/docs/unsafe-cheatcode.md b/crates/lint/docs/unsafe-cheatcode.md new file mode 100644 index 0000000000000..0aef657b0b7be --- /dev/null +++ b/crates/lint/docs/unsafe-cheatcode.md @@ -0,0 +1,35 @@ +# Usage of unsafe cheatcodes + +**Severity**: `Info` +**ID**: `unsafe-cheatcode` + +Flags use of Foundry cheatcodes that perform dangerous side effects (filesystem access, network +activity, environment variable reads, etc.) so they cannot slip into production code unnoticed. + +## What it does + +Reports calls to cheatcodes whose effects extend beyond the EVM sandbox or that bypass typical +test invariants. The flagged set follows the cheatcode's +[`Safety::Unsafe`](https://book.getfoundry.sh/cheatcodes) classification. + +## Why is this bad? + +Unsafe cheatcodes can read/write files, hit the network, or fork external state. They are +appropriate in tests with explicit intent but should not be added without review, and must +never end up in shipped contract code. + +## Example + +### Bad + +```solidity +vm.writeFile("./out.txt", data); // unsafe — writes to host filesystem +vm.envString("PRIVATE_KEY"); // unsafe — reads host environment +``` + +### Good + +```solidity +// Use safe cheatcodes (vm.expectRevert, vm.prank, vm.warp, ...) and explicit +// inputs/fixtures instead of pulling state from the host environment. +``` diff --git a/crates/lint/docs/unsafe-typecast.md b/crates/lint/docs/unsafe-typecast.md new file mode 100644 index 0000000000000..89d493eec3c3f --- /dev/null +++ b/crates/lint/docs/unsafe-typecast.md @@ -0,0 +1,40 @@ +# Unsafe typecast + +**Severity**: `Med` +**ID**: `unsafe-typecast` + +Flags explicit numeric typecasts that can silently truncate or alter the value. + +## What it does + +Reports casts where the source value's type is wider than the target type +(e.g. `uint256 → uint128`, `int256 → uint128`), unless the cast is preceded by a check that +guarantees the value fits in the target. + +## Why is this bad? + +Solidity does **not** revert on narrowing casts; it silently keeps the lowest bits, which can +cause severe accounting bugs (e.g. amount overflows, wrong fees, broken invariants). Use a checked +cast helper such as OpenZeppelin's `SafeCast` whenever the source value is not provably bounded. + +## Example + +### Bad + +```solidity +function setAmount(uint256 amount) external { + smallAmount = uint128(amount); // silent truncation if amount >= 2**128 +} +``` + +### Good + +```solidity +function setAmount(uint256 amount) external { + require(amount <= type(uint128).max, "overflow"); + smallAmount = uint128(amount); +} + +// or +smallAmount = SafeCast.toUint128(amount); +``` diff --git a/crates/lint/docs/unused-import.md b/crates/lint/docs/unused-import.md new file mode 100644 index 0000000000000..08f2545a36587 --- /dev/null +++ b/crates/lint/docs/unused-import.md @@ -0,0 +1,40 @@ +# Unused import + +**Severity**: `Info` +**ID**: `unused-import` + +Flags imported symbols (or whole import statements) whose imported names are not referenced +anywhere in the source unit. + +## What it does + +Reports `import "..."`, `import "..." as X`, and `import { A, B } from "..."` statements where one +or more imported names are never used. Symbols brought in via `import * as X` are tracked through +`X.member` accesses. + +## Why is this bad? + +Unused imports add noise, slow down compilation, can cause name collisions, and frequently +indicate dead code or stale refactors. + +## Example + +### Bad + +```solidity +import { A, B } from "./Lib.sol"; // B is never used + +contract C { + A internal a; +} +``` + +### Good + +```solidity +import { A } from "./Lib.sol"; + +contract C { + A internal a; +} +``` diff --git a/crates/lint/docs/unused-state-variables.md b/crates/lint/docs/unused-state-variables.md new file mode 100644 index 0000000000000..758c6e58b911b --- /dev/null +++ b/crates/lint/docs/unused-state-variables.md @@ -0,0 +1,39 @@ +# Unused state variable + +**Severity**: `Gas` +**ID**: `unused-state-variables` + +Flags state variables that are declared but never read or written anywhere in the contract or its +descendants. + +## What it does + +Reports each state variable that has no read or write site across the project. + +## Why is this bad? + +Unused state variables waste storage slots, inflate deployment cost, and are a strong signal of +dead or stale code that should be removed. + +## Example + +### Bad + +```solidity +contract C { + uint256 unused; // never read or written + uint256 public total; // used elsewhere +} +``` + +### Good + +```solidity +contract C { + uint256 public total; +} +``` + +## Notes + +This is a `Gas`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/docs/unwrapped-modifier-logic.md b/crates/lint/docs/unwrapped-modifier-logic.md new file mode 100644 index 0000000000000..985c79962af07 --- /dev/null +++ b/crates/lint/docs/unwrapped-modifier-logic.md @@ -0,0 +1,51 @@ +# Unwrapped modifier logic + +**Severity**: `CodeSize` +**ID**: `unwrapped-modifier-logic` + +Flags modifiers whose body contains non-trivial logic that should be moved into a helper function +to reduce contract code size. + +## What it does + +Reports modifiers whose body contains statements other than a single placeholder, simple builtin +calls (`require`/`assert`), or a single library function call. Modifiers that use inline assembly +are exempted. + +## Why is this bad? + +Solidity inlines a modifier's body at every call site, so any non-trivial logic is duplicated +across all functions that use the modifier. Wrapping the logic in an internal function and calling +it from the modifier keeps the bytecode small while preserving behavior. + +## Example + +### Bad + +```solidity +modifier onlyAuth() { + if (!auth[msg.sender]) revert NotAuth(); + bytes32 nonce = keccak256(abi.encodePacked(msg.sender, block.number)); + seenNonce[nonce] = true; + _; +} +``` + +### Good + +```solidity +modifier onlyAuth() { + _checkAuth(); + _; +} + +function _checkAuth() internal { + if (!auth[msg.sender]) revert NotAuth(); + bytes32 nonce = keccak256(abi.encodePacked(msg.sender, block.number)); + seenNonce[nonce] = true; +} +``` + +## Notes + +This is a `CodeSize`-severity lint and is **not** applied to test or script files. diff --git a/crates/lint/src/linter/late.rs b/crates/lint/src/linter/late.rs index f7e97d00e7612..ffdc284cacc56 100644 --- a/crates/lint/src/linter/late.rs +++ b/crates/lint/src/linter/late.rs @@ -385,6 +385,7 @@ mod tests { false, LinterConfig { inline: &inline, lint_specific: &lint_specific }, Vec::new(), + None, ); let mut passes: Vec>> = vec![Box::new(RecordingPass { counts: counts.clone() })]; diff --git a/crates/lint/src/linter/mod.rs b/crates/lint/src/linter/mod.rs index fc140643eb68a..3e38a02726605 100644 --- a/crates/lint/src/linter/mod.rs +++ b/crates/lint/src/linter/mod.rs @@ -1,8 +1,10 @@ mod early; mod late; +mod project; pub use early::{EarlyLintPass, EarlyLintVisitor}; pub use late::{LateLintPass, LateLintVisitor}; +pub use project::{ProjectLintEmitter, ProjectLintPass, ProjectSource}; use foundry_common::comments::inline_config::InlineConfig; use foundry_compilers::Language; @@ -16,10 +18,11 @@ use solar::{ diagnostics::{ Applicability, DiagBuilder, DiagId, DiagMsg, MultiSpan, Style, SuggestionStyle, }, + source_map::SourceFile, }, sema::Compiler, }; -use std::path::PathBuf; +use std::{path::PathBuf, sync::Arc}; /// Trait representing a generic linter for analyzing and reporting issues in smart contract source /// code files. @@ -54,6 +57,8 @@ pub struct LintContext<'s, 'c> { with_json_emitter: bool, pub config: LinterConfig<'c>, active_lints: Vec<&'static str>, + /// The source file currently being linted, when known. + source_file: Option>, } pub struct LinterConfig<'s> { @@ -68,8 +73,9 @@ impl<'s, 'c> LintContext<'s, 'c> { with_json_emitter: bool, config: LinterConfig<'c>, active_lints: Vec<&'static str>, + source_file: Option>, ) -> Self { - Self { sess, with_description, with_json_emitter, config, active_lints } + Self { sess, with_description, with_json_emitter, config, active_lints, source_file } } fn add_help<'a>(&self, diag: DiagBuilder<'a, ()>, help: &'static str) -> DiagBuilder<'a, ()> { @@ -81,6 +87,11 @@ impl<'s, 'c> LintContext<'s, 'c> { self.sess } + /// Returns the source file currently being linted, if any. + pub const fn source_file(&self) -> Option<&Arc> { + self.source_file.as_ref() + } + // Helper method to check if a lint id is enabled. // // For performance reasons, some passes check several lints at once. Thus, this method is @@ -108,6 +119,25 @@ impl<'s, 'c> LintContext<'s, 'c> { diag.emit(); } + /// Emit a diagnostic with a caller-provided message instead of the lint's description. + /// + /// Useful when the message must vary per occurrence (e.g. embedding the offending + /// codepoint detected by the `rtlo` lint). + pub fn emit_with_msg(&self, lint: &'static L, span: Span, msg: impl Into) { + if self.config.inline.is_id_disabled(span, lint.id()) || !self.is_lint_enabled(lint.id()) { + return; + } + + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), msg.into()) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)); + + self.add_help(diag, lint.help()).emit(); + } + /// Emit a diagnostic with a code suggestion. /// /// If no span is provided for [`SuggestionKind::Fix`], it will use the lint's span. diff --git a/crates/lint/src/linter/project.rs b/crates/lint/src/linter/project.rs new file mode 100644 index 0000000000000..38fc1ad1ba59f --- /dev/null +++ b/crates/lint/src/linter/project.rs @@ -0,0 +1,92 @@ +use super::{Lint, LintContext, LinterConfig}; +use foundry_common::comments::inline_config::InlineConfig; +use foundry_config::lint::LintSpecificConfig; +use solar::{ + ast, + interface::{Session, Span, diagnostics::DiagMsg, source_map::SourceFile}, +}; +use std::{path::PathBuf, sync::Arc}; + +/// A single source unit visible to a project-wide lint pass, pre-loaded with its inline config so +/// emits respect `// forge-lint: disable-*` markers without rebuilding it per emit. +pub struct ProjectSource<'ast> { + pub path: PathBuf, + pub file: Arc, + pub ast: &'ast ast::SourceUnit<'ast>, + pub inline_config: InlineConfig>, +} + +/// Trait for lints that need to inspect every input source at once (e.g. cross-file checks). +/// +/// `check_project` runs once after all per-file [`super::EarlyLintPass`] / +/// [`super::LateLintPass`] passes have completed. +pub trait ProjectLintPass<'ast>: Send + Sync { + fn check_project(&mut self, ctx: &ProjectLintEmitter<'_, '_>, sources: &[ProjectSource<'ast>]); +} + +/// Helper passed to [`ProjectLintPass::check_project`] for emitting diagnostics against a specific +/// source. +pub struct ProjectLintEmitter<'s, 'c> { + sess: &'s Session, + with_description: bool, + with_json_emitter: bool, + lint_specific: &'c LintSpecificConfig, + active_lints: Vec<&'static str>, +} + +impl<'s, 'c> ProjectLintEmitter<'s, 'c> { + pub const fn new( + sess: &'s Session, + with_description: bool, + with_json_emitter: bool, + lint_specific: &'c LintSpecificConfig, + active_lints: Vec<&'static str>, + ) -> Self { + Self { sess, with_description, with_json_emitter, lint_specific, active_lints } + } + + /// Returns `true` if the given lint id is enabled for this run. Project passes that perform + /// expensive analysis should guard their work behind this check. + pub fn is_lint_enabled(&self, id: &'static str) -> bool { + self.active_lints.contains(&id) + } + + /// Emits a diagnostic with the lint's default description as the message. + pub fn emit<'a, 'ast, L: Lint>( + &'a self, + source: &'a ProjectSource<'ast>, + lint: &'static L, + span: Span, + ) where + 'c: 'a, + { + self.build_ctx(source).emit(lint, span); + } + + /// Emits a diagnostic with a caller-provided message. + pub fn emit_with_msg<'a, 'ast, L: Lint>( + &'a self, + source: &'a ProjectSource<'ast>, + lint: &'static L, + span: Span, + msg: impl Into, + ) where + 'c: 'a, + { + self.build_ctx(source).emit_with_msg(lint, span, msg); + } + + fn build_ctx<'a, 'ast>(&'a self, source: &'a ProjectSource<'ast>) -> LintContext<'s, 'a> + where + 'c: 'a, + { + LintContext::new( + self.sess, + self.with_description, + self.with_json_emitter, + LinterConfig { inline: &source.inline_config, lint_specific: self.lint_specific }, + self.active_lints.clone(), + Some(source.file.clone()), + ) + } +} diff --git a/crates/lint/src/sol/gas/immutable.rs b/crates/lint/src/sol/gas/immutable.rs new file mode 100644 index 0000000000000..5baba86996841 --- /dev/null +++ b/crates/lint/src/sol/gas/immutable.rs @@ -0,0 +1,406 @@ +use super::CouldBeImmutable; +use crate::{ + linter::{LateLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{self, UnOpKind}, + interface::{kw, sym}, + sema::hir::{self, ExprKind, Res, StmtKind, TypeKind}, +}; +use std::collections::HashSet; + +declare_forge_lint!( + COULD_BE_IMMUTABLE, + Severity::Gas, + "could-be-immutable", + "state variable could be declared immutable" +); + +impl<'hir> LateLintPass<'hir> for CouldBeImmutable { + fn check_nested_contract( + &mut self, + ctx: &LintContext, + hir: &'hir hir::Hir<'hir>, + contract_id: hir::ContractId, + ) { + let contract = hir.contract(contract_id); + if contract.kind == ast::ContractKind::Interface { + return; + } + if !is_most_derived_contract(hir, contract_id) { + return; + } + + let candidates: Vec<_> = contract + .linearized_bases + .iter() + .flat_map(|&contract_id| hir.contract(contract_id).variables()) + .filter(|&id| is_immutable_candidate_type(hir.variable(id))) + .collect(); + + if candidates.is_empty() { + return; + } + let candidate_set: HashSet<_> = candidates.iter().copied().collect(); + + if contract_contains_unlowered_stmt(hir, contract) { + return; + } + + let mut constructor_writes = HashSet::new(); + let mut runtime_writes = HashSet::new(); + + for &var_id in &candidates { + let var = hir.variable(var_id); + if var.initializer.is_some_and(|expr| !is_compile_time_constant(hir, expr)) { + constructor_writes.insert(var_id); + } + } + + for &contract_id in contract.linearized_bases { + for function_id in hir.contract(contract_id).all_functions() { + let function = hir.function(function_id); + if function.is_constructor() { + collect_modifier_writes( + hir, + function, + &candidate_set, + &mut constructor_writes, + &mut runtime_writes, + &mut HashSet::new(), + ); + + if let Some(body) = function.body { + collect_state_writes(hir, body, &candidate_set, &mut constructor_writes); + } + } else { + // Immutable variables can only be assigned inline or directly in constructor + // bodies, so writes hidden behind internal helpers are not valid candidates. + let mut modifier_argument_writes = HashSet::new(); + collect_modifier_writes( + hir, + function, + &candidate_set, + &mut modifier_argument_writes, + &mut runtime_writes, + &mut HashSet::new(), + ); + runtime_writes.extend(modifier_argument_writes); + + if let Some(body) = function.body { + collect_state_writes(hir, body, &candidate_set, &mut runtime_writes); + } + } + } + } + + for &var_id in &candidates { + if constructor_writes.contains(&var_id) && !runtime_writes.contains(&var_id) { + let var = hir.variable(var_id); + ctx.emit(&COULD_BE_IMMUTABLE, var.name.map_or(var.span, |name| name.span)); + } + } + } +} + +fn is_most_derived_contract(hir: &hir::Hir<'_>, contract_id: hir::ContractId) -> bool { + !hir.contracts() + .any(|contract| contract.linearized_bases.iter().skip(1).any(|&id| id == contract_id)) +} + +fn collect_modifier_writes<'hir>( + hir: &'hir hir::Hir<'hir>, + function: &'hir hir::Function<'hir>, + candidates: &HashSet, + argument_writes: &mut HashSet, + body_writes: &mut HashSet, + visited_modifiers: &mut HashSet, +) { + for modifier in function.modifiers { + for expr in modifier.args.exprs() { + collect_expr_writes(expr, candidates, argument_writes); + } + + let Some(modifier_id) = modifier.id.as_function() else { continue }; + if !visited_modifiers.insert(modifier_id) { + continue; + } + + let modifier = hir.function(modifier_id); + let mut nested_argument_writes = HashSet::new(); + collect_modifier_writes( + hir, + modifier, + candidates, + &mut nested_argument_writes, + body_writes, + visited_modifiers, + ); + body_writes.extend(nested_argument_writes); + if let Some(body) = modifier.body { + collect_state_writes(hir, body, candidates, body_writes); + } + } +} + +fn is_immutable_candidate_type(var: &hir::Variable<'_>) -> bool { + var.is_state_variable() + && var.mutability.is_none() + && match var.ty.kind { + TypeKind::Elementary(ty) => ty.is_value_type(), + TypeKind::Custom(hir::ItemId::Contract(_)) => true, + _ => false, + } +} + +fn contract_contains_unlowered_stmt<'hir>( + hir: &'hir hir::Hir<'hir>, + contract: &'hir hir::Contract<'hir>, +) -> bool { + contract.linearized_bases.iter().any(|&contract_id| { + hir.contract(contract_id).all_functions().any(|function_id| { + hir.function(function_id).body.is_some_and(|body| block_contains_unlowered_stmt(body)) + }) + }) +} + +fn block_contains_unlowered_stmt(block: hir::Block<'_>) -> bool { + block.stmts.iter().any(stmt_contains_unlowered_stmt) +} + +fn stmt_contains_unlowered_stmt(stmt: &hir::Stmt<'_>) -> bool { + match &stmt.kind { + StmtKind::Err(_) => true, + StmtKind::Block(block) | StmtKind::UncheckedBlock(block) | StmtKind::Loop(block, _) => { + block_contains_unlowered_stmt(*block) + } + StmtKind::If(_, then_stmt, else_stmt) => { + stmt_contains_unlowered_stmt(then_stmt) + || else_stmt.is_some_and(stmt_contains_unlowered_stmt) + } + StmtKind::Try(stmt_try) => { + stmt_try.clauses.iter().any(|clause| block_contains_unlowered_stmt(clause.block)) + } + StmtKind::DeclSingle(_) + | StmtKind::DeclMulti(_, _) + | StmtKind::Emit(_) + | StmtKind::Revert(_) + | StmtKind::Return(_) + | StmtKind::Break + | StmtKind::Continue + | StmtKind::Expr(_) + | StmtKind::Placeholder => false, + } +} + +fn collect_state_writes<'hir>( + hir: &'hir hir::Hir<'hir>, + block: hir::Block<'hir>, + candidates: &HashSet, + writes: &mut HashSet, +) { + for stmt in block.stmts { + collect_stmt_writes(hir, stmt, candidates, writes); + } +} + +fn collect_stmt_writes<'hir>( + hir: &'hir hir::Hir<'hir>, + stmt: &'hir hir::Stmt<'hir>, + candidates: &HashSet, + writes: &mut HashSet, +) { + match &stmt.kind { + StmtKind::Block(block) | StmtKind::UncheckedBlock(block) | StmtKind::Loop(block, _) => { + collect_state_writes(hir, *block, candidates, writes); + } + StmtKind::If(condition, then_stmt, else_stmt) => { + collect_expr_writes(condition, candidates, writes); + collect_stmt_writes(hir, then_stmt, candidates, writes); + if let Some(else_stmt) = else_stmt { + collect_stmt_writes(hir, else_stmt, candidates, writes); + } + } + StmtKind::Try(stmt_try) => { + collect_expr_writes(&stmt_try.expr, candidates, writes); + for clause in stmt_try.clauses { + collect_state_writes(hir, clause.block, candidates, writes); + } + } + StmtKind::DeclSingle(var_id) => { + if let Some(initializer) = hir.variable(*var_id).initializer { + collect_expr_writes(initializer, candidates, writes); + } + } + StmtKind::DeclMulti(_, expr) + | StmtKind::Emit(expr) + | StmtKind::Revert(expr) + | StmtKind::Return(Some(expr)) + | StmtKind::Expr(expr) => collect_expr_writes(expr, candidates, writes), + StmtKind::Return(None) + | StmtKind::Break + | StmtKind::Continue + | StmtKind::Placeholder + | StmtKind::Err(_) => {} + } +} + +fn collect_expr_writes<'hir>( + expr: &'hir hir::Expr<'hir>, + candidates: &HashSet, + writes: &mut HashSet, +) { + match &expr.kind { + ExprKind::Assign(lhs, _, rhs) => { + collect_lvalue_writes(lhs, candidates, writes); + collect_expr_writes(lhs, candidates, writes); + collect_expr_writes(rhs, candidates, writes); + } + ExprKind::Delete(inner) => { + collect_lvalue_writes(inner, candidates, writes); + collect_expr_writes(inner, candidates, writes); + } + ExprKind::Unary(op, inner) => { + if op.kind.has_side_effects() { + collect_lvalue_writes(inner, candidates, writes); + } + collect_expr_writes(inner, candidates, writes); + } + ExprKind::Array(exprs) => { + for expr in *exprs { + collect_expr_writes(expr, candidates, writes); + } + } + ExprKind::Binary(lhs, _, rhs) => { + collect_expr_writes(lhs, candidates, writes); + collect_expr_writes(rhs, candidates, writes); + } + ExprKind::Call(callee, args, named_args) => { + collect_expr_writes(callee, candidates, writes); + for expr in args.exprs() { + collect_expr_writes(expr, candidates, writes); + } + if let Some(named_args) = named_args { + for arg in *named_args { + collect_expr_writes(&arg.value, candidates, writes); + } + } + } + ExprKind::Index(base, index) => { + collect_expr_writes(base, candidates, writes); + if let Some(index) = index { + collect_expr_writes(index, candidates, writes); + } + } + ExprKind::Slice(base, start, end) => { + collect_expr_writes(base, candidates, writes); + if let Some(start) = start { + collect_expr_writes(start, candidates, writes); + } + if let Some(end) = end { + collect_expr_writes(end, candidates, writes); + } + } + ExprKind::Member(base, _) | ExprKind::Payable(base) => { + collect_expr_writes(base, candidates, writes); + } + ExprKind::Ternary(condition, then_expr, else_expr) => { + collect_expr_writes(condition, candidates, writes); + collect_expr_writes(then_expr, candidates, writes); + collect_expr_writes(else_expr, candidates, writes); + } + ExprKind::Tuple(exprs) => { + for expr in exprs.iter().flatten() { + collect_expr_writes(expr, candidates, writes); + } + } + ExprKind::Ident(_) + | ExprKind::Lit(_) + | ExprKind::New(_) + | ExprKind::TypeCall(_) + | ExprKind::Type(_) + | ExprKind::Err(_) => {} + } +} + +fn collect_lvalue_writes( + expr: &hir::Expr<'_>, + candidates: &HashSet, + writes: &mut HashSet, +) { + match &expr.peel_parens().kind { + ExprKind::Ident([Res::Item(hir::ItemId::Variable(id)), ..]) if candidates.contains(id) => { + writes.insert(*id); + } + ExprKind::Tuple(exprs) => { + for expr in exprs.iter().flatten() { + collect_lvalue_writes(expr, candidates, writes); + } + } + ExprKind::Index(base, _) + | ExprKind::Slice(base, _, _) + | ExprKind::Member(base, _) + | ExprKind::Payable(base) => collect_lvalue_writes(base, candidates, writes), + _ => {} + } +} + +fn is_compile_time_constant(hir: &hir::Hir<'_>, expr: &hir::Expr<'_>) -> bool { + match &expr.kind { + ExprKind::Lit(_) | ExprKind::Type(_) | ExprKind::TypeCall(_) => true, + ExprKind::Ident(resolutions) => resolutions.iter().all(|res| match res { + Res::Item(hir::ItemId::Variable(var_id)) => hir.variable(*var_id).is_constant(), + _ => false, + }), + ExprKind::Unary(op, inner) => { + !matches!( + op.kind, + UnOpKind::PreInc | UnOpKind::PreDec | UnOpKind::PostInc | UnOpKind::PostDec + ) && is_compile_time_constant(hir, inner) + } + ExprKind::Binary(lhs, _, rhs) => { + is_compile_time_constant(hir, lhs) && is_compile_time_constant(hir, rhs) + } + ExprKind::Call(callee, args, named_args) => { + is_allowed_constant_call(callee) + && args.exprs().all(|expr| is_compile_time_constant(hir, expr)) + && named_args.is_none_or(|args| { + args.iter().all(|arg| is_compile_time_constant(hir, &arg.value)) + }) + } + ExprKind::Ternary(condition, then_expr, else_expr) => { + is_compile_time_constant(hir, condition) + && is_compile_time_constant(hir, then_expr) + && is_compile_time_constant(hir, else_expr) + } + ExprKind::Tuple(exprs) => { + exprs.iter().flatten().all(|expr| is_compile_time_constant(hir, expr)) + } + ExprKind::Array(_) + | ExprKind::Assign(_, _, _) + | ExprKind::Delete(_) + | ExprKind::Index(_, _) + | ExprKind::Slice(_, _, _) + | ExprKind::Member(_, _) + | ExprKind::New(_) + | ExprKind::Payable(_) + | ExprKind::Err(_) => false, + } +} + +fn is_allowed_constant_call(callee: &hir::Expr<'_>) -> bool { + match &callee.kind { + ExprKind::Type(_) => true, + ExprKind::Ident([Res::Builtin(builtin), ..]) => { + let name = builtin.name(); + name == kw::Keccak256 + || name == kw::Addmod + || name == kw::Mulmod + || name == sym::sha256 + || name == sym::ripemd160 + || name == sym::ecrecover + } + _ => false, + } +} diff --git a/crates/lint/src/sol/gas/mod.rs b/crates/lint/src/sol/gas/mod.rs index 9a4c37f925cd5..2d5ce2a1becc1 100644 --- a/crates/lint/src/sol/gas/mod.rs +++ b/crates/lint/src/sol/gas/mod.rs @@ -1,8 +1,17 @@ use crate::sol::{EarlyLintPass, LateLintPass, SolLint}; mod custom_errors; +mod immutable; mod keccak; +mod unused_state_variables; use custom_errors::CUSTOM_ERRORS; +use immutable::COULD_BE_IMMUTABLE; use keccak::ASM_KECCAK256; +use unused_state_variables::UNUSED_STATE_VARIABLES; -register_lints!((CustomErrors, early, (CUSTOM_ERRORS)), (AsmKeccak256, late, (ASM_KECCAK256))); +register_lints!( + (AsmKeccak256, late, (ASM_KECCAK256)), + (CustomErrors, early, (CUSTOM_ERRORS)), + (CouldBeImmutable, late, (COULD_BE_IMMUTABLE)), + (UnusedStateVariables, late, (UNUSED_STATE_VARIABLES)), +); diff --git a/crates/lint/src/sol/gas/unused_state_variables.rs b/crates/lint/src/sol/gas/unused_state_variables.rs new file mode 100644 index 0000000000000..78d32c196b20c --- /dev/null +++ b/crates/lint/src/sol/gas/unused_state_variables.rs @@ -0,0 +1,90 @@ +use super::UnusedStateVariables; +use crate::{ + linter::{LateLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::ContractKind, + interface::data_structures::Never, + sema::hir::{self, Visit as _}, +}; +use std::{collections::HashSet, ops::ControlFlow}; + +declare_forge_lint!( + UNUSED_STATE_VARIABLES, + Severity::Gas, + "unused-state-variables", + "state variable is never used" +); + +impl<'hir> LateLintPass<'hir> for UnusedStateVariables { + fn check_contract( + &mut self, + ctx: &LintContext, + hir: &'hir hir::Hir<'hir>, + contract: &'hir hir::Contract<'hir>, + ) { + // Skip interfaces, they cannot have mutable state variables. + if contract.kind == ContractKind::Interface { + return; + } + + // Collect state variable IDs, skipping constants and immutables + // (those are handled by the compiler and don't occupy storage slots). + let state_vars: Vec = contract + .variables() + .filter(|&var_id| { + let var = hir.variable(var_id); + !var.is_constant() && !var.is_immutable() + }) + .collect(); + + if state_vars.is_empty() { + return; + } + + // Walk the full contract — functions (including modifier call args, parameters, returns, + // and bodies) and state variable initializers — to collect every variable referenced + // anywhere in this contract. + let mut collector = UsedVarCollector { hir, used: HashSet::new() }; + for func_id in contract.all_functions() { + let _ = collector.visit_nested_function(func_id); + } + // State variables can reference other state variables in their initializers. + for var_id in contract.variables() { + let _ = collector.visit_nested_var(var_id); + } + + // Report any state variable that was never referenced. + for var_id in state_vars { + if !collector.used.contains(&var_id) { + let var = hir.variable(var_id); + ctx.emit(&UNUSED_STATE_VARIABLES, var.span); + } + } + } +} + +struct UsedVarCollector<'hir> { + hir: &'hir hir::Hir<'hir>, + used: HashSet, +} + +impl<'hir> hir::Visit<'hir> for UsedVarCollector<'hir> { + type BreakValue = Never; + + fn hir(&self) -> &'hir hir::Hir<'hir> { + self.hir + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> ControlFlow { + if let hir::ExprKind::Ident(resolutions) = &expr.kind { + for res in *resolutions { + if let hir::Res::Item(hir::ItemId::Variable(var_id)) = res { + self.used.insert(*var_id); + } + } + } + self.walk_expr(expr) + } +} diff --git a/crates/lint/src/sol/high/mod.rs b/crates/lint/src/sol/high/mod.rs index de199e88ff33a..09658ac232461 100644 --- a/crates/lint/src/sol/high/mod.rs +++ b/crates/lint/src/sol/high/mod.rs @@ -1,13 +1,16 @@ use crate::sol::{EarlyLintPass, LateLintPass, SolLint}; mod incorrect_shift; +mod rtlo; mod unchecked_calls; use incorrect_shift::INCORRECT_SHIFT; +use rtlo::RTLO; use unchecked_calls::{ERC20_UNCHECKED_TRANSFER, UNCHECKED_CALL}; register_lints!( (IncorrectShift, early, (INCORRECT_SHIFT)), (UncheckedCall, early, (UNCHECKED_CALL)), - (UncheckedTransferERC20, late, (ERC20_UNCHECKED_TRANSFER)) + (UncheckedTransferERC20, late, (ERC20_UNCHECKED_TRANSFER)), + (Rtlo, early, (RTLO)) ); diff --git a/crates/lint/src/sol/high/rtlo.rs b/crates/lint/src/sol/high/rtlo.rs new file mode 100644 index 0000000000000..c121ac758da32 --- /dev/null +++ b/crates/lint/src/sol/high/rtlo.rs @@ -0,0 +1,58 @@ +use super::Rtlo; +use crate::{ + linter::{EarlyLintPass, Lint, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast, + interface::{BytePos, Span}, +}; + +declare_forge_lint!( + RTLO, + Severity::High, + "rtlo", + "unicode bidirectional override character can hide malicious code" +); + +impl<'ast> EarlyLintPass<'ast> for Rtlo { + fn check_full_source_unit( + &mut self, + ctx: &LintContext<'ast, '_>, + _unit: &'ast ast::SourceUnit<'ast>, + ) { + if !ctx.is_lint_enabled(RTLO.id()) { + return; + } + + // Scan the raw source so bidi chars in comments are also caught. + let Some(file) = ctx.source_file() else { return }; + + for (offset, ch) in file.src.char_indices() { + let Some(name) = bidi_char_name(ch) else { continue }; + + let lo = file.start_pos + BytePos::from_usize(offset); + let hi = lo + BytePos::from_usize(ch.len_utf8()); + let span = Span::new(lo, hi); + + ctx.emit_with_msg(&RTLO, span, format!("U+{:04X} ({name}) detected", ch as u32)); + } + } +} + +const fn bidi_char_name(ch: char) -> Option<&'static str> { + Some(match ch { + '\u{200E}' => "Left-to-Right Mark", + '\u{200F}' => "Right-to-Left Mark", + '\u{202A}' => "Left-to-Right Embedding", + '\u{202B}' => "Right-to-Left Embedding", + '\u{202C}' => "Pop Directional Formatting", + '\u{202D}' => "Left-to-Right Override", + '\u{202E}' => "Right-to-Left Override", + '\u{2066}' => "Left-to-Right Isolate", + '\u{2067}' => "Right-to-Left Isolate", + '\u{2068}' => "First Strong Isolate", + '\u{2069}' => "Pop Directional Isolate", + _ => return None, + }) +} diff --git a/crates/lint/src/sol/info/boolean_cst.rs b/crates/lint/src/sol/info/boolean_cst.rs new file mode 100644 index 0000000000000..50a7075338d3d --- /dev/null +++ b/crates/lint/src/sol/info/boolean_cst.rs @@ -0,0 +1,116 @@ +use super::BooleanCst; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{BinOp, BinOpKind, Expr, ExprKind, LitKind, Stmt, StmtKind, VariableDefinition}, + interface::SpannedOption, +}; + +declare_forge_lint!(BOOLEAN_CST, Severity::Med, "boolean-cst", "misuse of a boolean constant"); + +impl<'ast> EarlyLintPass<'ast> for BooleanCst { + fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) { + match &stmt.kind { + StmtKind::If(cond, ..) | StmtKind::DoWhile(_, cond) => { + check_expr(ctx, cond, ExprContext::Condition { allow_bare_true: false }); + } + StmtKind::While(cond, _) => { + check_expr(ctx, cond, ExprContext::Condition { allow_bare_true: true }); + } + StmtKind::For { cond: Some(cond), .. } => { + check_expr(ctx, cond, ExprContext::Condition { allow_bare_true: false }); + } + StmtKind::DeclMulti(_, expr) => check_allowed_bare_expr(ctx, expr), + StmtKind::Expr(expr) | StmtKind::Return(Some(expr)) => { + check_allowed_bare_expr(ctx, expr); + } + _ => {} + } + } + + fn check_variable_definition( + &mut self, + ctx: &LintContext, + var: &'ast VariableDefinition<'ast>, + ) { + if let Some(initializer) = &var.initializer { + check_allowed_bare_expr(ctx, initializer); + } + } +} + +#[derive(Clone, Copy)] +enum ExprContext { + Condition { allow_bare_true: bool }, + General, + AllowedBare, +} + +fn check_allowed_bare_expr(ctx: &LintContext, expr: &Expr<'_>) { + let context = + if bool_literal(expr).is_some() { ExprContext::AllowedBare } else { ExprContext::General }; + check_expr(ctx, expr, context); +} + +fn check_expr(ctx: &LintContext, expr: &Expr<'_>, context: ExprContext) { + if let Some(value) = bool_literal(expr) { + match context { + ExprContext::AllowedBare => {} + ExprContext::Condition { allow_bare_true: true } if value => {} + ExprContext::Condition { .. } | ExprContext::General => { + ctx.emit(&BOOLEAN_CST, expr.span); + } + } + return; + } + + match &expr.kind { + ExprKind::Assign(_, _, rhs) => check_allowed_bare_expr(ctx, rhs), + ExprKind::Binary(left, op, right) => check_binary_expr(ctx, left, *op, right), + ExprKind::Call(_, args) => { + for arg in args.exprs() { + check_allowed_bare_expr(ctx, arg); + } + } + ExprKind::Delete(expr) | ExprKind::Unary(_, expr) => { + check_expr(ctx, expr, ExprContext::General); + } + ExprKind::Ternary(cond, true_expr, false_expr) => { + check_expr(ctx, cond, ExprContext::Condition { allow_bare_true: false }); + check_expr(ctx, true_expr, ExprContext::General); + check_expr(ctx, false_expr, ExprContext::General); + } + ExprKind::Tuple(exprs) => { + for opt_expr in exprs.iter() { + if let SpannedOption::Some(expr) = opt_expr.as_ref() { + check_expr(ctx, expr, ExprContext::General); + } + } + } + _ => {} + } +} + +fn check_binary_expr(ctx: &LintContext, left: &Expr<'_>, op: BinOp, right: &Expr<'_>) { + if matches!(op.kind, BinOpKind::Eq | BinOpKind::Ne) + && (bool_literal(left).is_some() || bool_literal(right).is_some()) + { + return; + } + + check_expr(ctx, left, ExprContext::General); + check_expr(ctx, right, ExprContext::General); +} + +fn bool_literal(expr: &Expr<'_>) -> Option { + let expr = expr.peel_parens(); + if let ExprKind::Lit(lit, _) = &expr.kind + && let LitKind::Bool(value) = lit.kind + { + Some(value) + } else { + None + } +} diff --git a/crates/lint/src/sol/info/boolean_equal.rs b/crates/lint/src/sol/info/boolean_equal.rs new file mode 100644 index 0000000000000..89cd7ec136f75 --- /dev/null +++ b/crates/lint/src/sol/info/boolean_equal.rs @@ -0,0 +1,108 @@ +use super::BooleanEqual; +use crate::{ + linter::{EarlyLintPass, LintContext, Suggestion}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{BinOp, BinOpKind, Expr, ExprKind, LitKind}, + interface::diagnostics::Applicability, +}; + +declare_forge_lint!( + BOOLEAN_EQUAL, + Severity::Info, + "boolean-equal", + "boolean comparisons to constants should be simplified" +); + +impl<'ast> EarlyLintPass<'ast> for BooleanEqual { + fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) { + if let ExprKind::Binary( + left, + op @ BinOp { kind: BinOpKind::Eq | BinOpKind::Ne, .. }, + right, + ) = &expr.kind + { + match bool_comparison_suggestion(ctx, left, op.kind, right) { + BoolComparison::WithSuggestion(simplified) => { + ctx.emit_with_suggestion( + &BOOLEAN_EQUAL, + expr.span, + Suggestion::fix(simplified, Applicability::MachineApplicable) + .with_desc("consider simplifying to"), + ); + } + BoolComparison::WithoutSuggestion => ctx.emit(&BOOLEAN_EQUAL, expr.span), + BoolComparison::None => {} + } + } + } +} + +enum BoolComparison { + WithSuggestion(String), + WithoutSuggestion, + None, +} + +fn bool_comparison_suggestion( + ctx: &LintContext, + left: &Expr<'_>, + op: BinOpKind, + right: &Expr<'_>, +) -> BoolComparison { + let left_bool = bool_literal(left); + let right_bool = bool_literal(right); + + match (left_bool, right_bool) { + (Some(value), None) => simplify_expr(ctx, right, op, value), + (None, Some(value)) => simplify_expr(ctx, left, op, value), + (Some(_), Some(_)) => BoolComparison::WithoutSuggestion, + (None, None) => BoolComparison::None, + } +} + +fn bool_literal(expr: &Expr<'_>) -> Option { + let expr = expr.peel_parens(); + if let ExprKind::Lit(lit, _) = &expr.kind + && let LitKind::Bool(value) = lit.kind + { + Some(value) + } else { + None + } +} + +fn simplify_expr( + ctx: &LintContext, + expr: &Expr<'_>, + op: BinOpKind, + constant: bool, +) -> BoolComparison { + let Some(snippet) = ctx.span_to_snippet(expr.span) else { + return BoolComparison::WithoutSuggestion; + }; + + let simplified = match (op, constant) { + (BinOpKind::Eq, true) | (BinOpKind::Ne, false) => snippet, + (BinOpKind::Eq, false) | (BinOpKind::Ne, true) if can_negate_without_parens(expr) => { + format!("!{snippet}") + } + (BinOpKind::Eq, false) | (BinOpKind::Ne, true) => format!("!({snippet})"), + _ => return BoolComparison::None, + }; + + BoolComparison::WithSuggestion(simplified) +} + +fn can_negate_without_parens(expr: &Expr<'_>) -> bool { + matches!( + expr.peel_parens().kind, + ExprKind::Call(..) + | ExprKind::CallOptions(..) + | ExprKind::Ident(_) + | ExprKind::Index(..) + | ExprKind::Lit(..) + | ExprKind::Member(..) + ) +} diff --git a/crates/lint/src/sol/info/inline_assembly.rs b/crates/lint/src/sol/info/inline_assembly.rs new file mode 100644 index 0000000000000..1111129dada34 --- /dev/null +++ b/crates/lint/src/sol/info/inline_assembly.rs @@ -0,0 +1,71 @@ +use super::InlineAssembly; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{Stmt, StmtKind}, + interface::{BytePos, Span}, +}; + +declare_forge_lint!( + INLINE_ASSEMBLY, + Severity::Info, + "inline-assembly", + "usage of inline assembly; assembly bypasses Solidity safety features and should be reviewed" +); + +const ASSEMBLY_KW_LEN: u32 = 8; +const NATSPEC_MEMORY_SAFE_MARKER: &str = "@solidity memory-safe-assembly"; + +impl<'ast> EarlyLintPass<'ast> for InlineAssembly { + fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) { + let StmtKind::Assembly(asm) = &stmt.kind else { return }; + + let kw_span = assembly_keyword_span(stmt.span); + + let memory_safe = asm.flags.iter().any(|f| f.value.as_str() == "memory-safe") + || has_memory_safe_natspec(ctx, stmt.span.lo()); + + let msg = if memory_safe { + "inline assembly (declared memory-safe); review business logic and side effects" + } else { + "inline assembly used; review for memory safety and side effects" + }; + + ctx.emit_with_msg(&INLINE_ASSEMBLY, kw_span, msg); + } +} + +/// Narrows a span to the leading `assembly` keyword to keep diagnostics readable. +fn assembly_keyword_span(span: Span) -> Span { + span.with_hi(span.lo() + BytePos(ASSEMBLY_KW_LEN)) +} + +/// Returns `true` when the lines immediately preceding `stmt_lo` form a `///` NatSpec block +/// containing `@solidity memory-safe-assembly`. +fn has_memory_safe_natspec(ctx: &LintContext, stmt_lo: BytePos) -> bool { + let Some(source_file) = ctx.source_file() else { return false }; + let src = source_file.src.as_str(); + let start_pos = source_file.start_pos.to_u32(); + let lo_abs = stmt_lo.to_u32(); + if lo_abs < start_pos { + return false; + } + let offset = (lo_abs - start_pos) as usize; + if offset > src.len() { + return false; + } + + for line in src[..offset].lines().rev() { + let trimmed = line.trim_start(); + if trimmed.is_empty() { + continue; + } + let Some(rest) = trimmed.strip_prefix("///") else { return false }; + if rest.trim_start().starts_with(NATSPEC_MEMORY_SAFE_MARKER) { + return true; + } + } + false +} diff --git a/crates/lint/src/sol/info/mod.rs b/crates/lint/src/sol/info/mod.rs index 290583cdc965a..913c5d2ea9da3 100644 --- a/crates/lint/src/sol/info/mod.rs +++ b/crates/lint/src/sol/info/mod.rs @@ -3,6 +3,12 @@ use crate::sol::{EarlyLintPass, LateLintPass, SolLint}; mod mixed_case; use mixed_case::{MIXED_CASE_FUNCTION, MIXED_CASE_VARIABLE}; +mod boolean_cst; +use boolean_cst::BOOLEAN_CST; + +mod boolean_equal; +use boolean_equal::BOOLEAN_EQUAL; + mod pascal_case; use pascal_case::PASCAL_CASE_STRUCT; @@ -24,7 +30,18 @@ use multi_contract_file::MULTI_CONTRACT_FILE; mod interface_naming; use interface_naming::{INTERFACE_FILE_NAMING, INTERFACE_NAMING}; +mod too_many_digits; +use too_many_digits::TOO_MANY_DIGITS; + +mod pragma_directive; +use pragma_directive::PRAGMA_INCONSISTENT; + +mod inline_assembly; +use inline_assembly::INLINE_ASSEMBLY; + register_lints!( + (BooleanCst, early, (BOOLEAN_CST)), + (BooleanEqual, early, (BOOLEAN_EQUAL)), (PascalCaseStruct, early, (PASCAL_CASE_STRUCT)), (MixedCaseVariable, early, (MIXED_CASE_VARIABLE)), (MixedCaseFunction, early, (MIXED_CASE_FUNCTION)), @@ -34,4 +51,7 @@ register_lints!( (UnsafeCheatcodes, early, (UNSAFE_CHEATCODE_USAGE)), (MultiContractFile, early, (MULTI_CONTRACT_FILE)), (InterfaceFileNaming, early, (INTERFACE_FILE_NAMING, INTERFACE_NAMING)), + (TooManyDigits, early, (TOO_MANY_DIGITS)), + (PragmaDirective, project, (PRAGMA_INCONSISTENT)), + (InlineAssembly, early, (INLINE_ASSEMBLY)), ); diff --git a/crates/lint/src/sol/info/pragma_directive.rs b/crates/lint/src/sol/info/pragma_directive.rs new file mode 100644 index 0000000000000..b66b6bcff6ade --- /dev/null +++ b/crates/lint/src/sol/info/pragma_directive.rs @@ -0,0 +1,71 @@ +use crate::{ + linter::{Lint, ProjectLintEmitter, ProjectLintPass, ProjectSource}, + sol::{Severity, SolLint, info::PragmaDirective}, +}; +use solar::{ast, interface::Span}; + +declare_forge_lint!( + PRAGMA_INCONSISTENT, + Severity::Info, + "pragma-inconsistent", + "inconsistent Solidity pragma version requirements across the project" +); + +impl<'ast> ProjectLintPass<'ast> for PragmaDirective { + fn check_project(&mut self, ctx: &ProjectLintEmitter<'_, '_>, sources: &[ProjectSource<'ast>]) { + if !ctx.is_lint_enabled(PRAGMA_INCONSISTENT.id()) { + return; + } + + // Collect every `pragma solidity` directive across input sources, with its rendered + // version-requirement string for grouping. Stores source index to avoid lifetime + // invariance issues with `&ProjectSource<'ast>`. + let mut entries: Vec<(usize, Span, String)> = Vec::new(); + for (idx, source) in sources.iter().enumerate() { + for (span, req) in solidity_pragmas(source.ast) { + entries.push((idx, span, req.to_string())); + } + } + + // Stable order for snapshots and JSON output. + entries.sort_by(|a, b| { + sources[a.0].path.cmp(&sources[b.0].path).then(a.1.lo().cmp(&b.1.lo())) + }); + + // Build the distinct list once and bail if all sources agree. + let mut distinct: Vec<&str> = entries.iter().map(|(_, _, s)| s.as_str()).collect(); + distinct.sort_unstable(); + distinct.dedup(); + if distinct.len() < 2 { + return; + } + + for (idx, span, req_str) in &entries { + let others = distinct + .iter() + .filter(|v| **v != req_str.as_str()) + .copied() + .collect::>() + .join(", "); + let msg = format!( + "'pragma solidity {req_str};' conflicts with other version requirements in the project: {others}" + ); + ctx.emit_with_msg(&sources[*idx], &PRAGMA_INCONSISTENT, *span, msg); + } + } +} + +/// Yields every top-level `pragma solidity ...;` directive in `unit`. +fn solidity_pragmas<'ast>( + unit: &'ast ast::SourceUnit<'ast>, +) -> impl Iterator)> + 'ast { + unit.items.iter().filter_map(|item| match &item.kind { + ast::ItemKind::Pragma(p) => match &p.tokens { + ast::PragmaTokens::Version(ident, req) if ident.as_str() == "solidity" => { + Some((item.span, req)) + } + _ => None, + }, + _ => None, + }) +} diff --git a/crates/lint/src/sol/info/too_many_digits.rs b/crates/lint/src/sol/info/too_many_digits.rs new file mode 100644 index 0000000000000..3ba9e8abba2de --- /dev/null +++ b/crates/lint/src/sol/info/too_many_digits.rs @@ -0,0 +1,50 @@ +use super::TooManyDigits; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::ast::{Expr, ExprKind, LitKind}; + +declare_forge_lint!( + TOO_MANY_DIGITS, + Severity::Info, + "too-many-digits", + "numeric literal with many digits is error-prone; \ + use scientific notation, sub-denominations, or underscore separators" +); + +impl<'ast> EarlyLintPass<'ast> for TooManyDigits { + fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) { + let ExprKind::Lit(lit, sub_denom) = &expr.kind else { return }; + + // Only plain integer literals. `LitKind::Address` (40-hex-digit address) is a + // distinct variant and is therefore skipped automatically. + if !matches!(lit.kind, LitKind::Number(_)) { + return; + } + + // Skip literals with a sub-denomination, e.g. `1000000 gwei`, `5 minutes`. + if sub_denom.is_some() { + return; + } + + let s = lit.symbol.as_str(); + + // Skip hex literals — long zero runs in hex are usually intentional (masks, + // selectors, bit patterns) and there is no scientific-notation alternative. + if s.starts_with("0x") || s.starts_with("0X") { + return; + } + + // Skip if the user already used scientific notation (`1e18`). + if s.contains('e') || s.contains('E') { + return; + } + + // 5+ consecutive zeros in the literal as written. Underscores are + // preserved, so `1_000_000` passes while `1_000000` is flagged. + if s.contains("00000") { + ctx.emit(&TOO_MANY_DIGITS, lit.span); + } + } +} diff --git a/crates/lint/src/sol/macros.rs b/crates/lint/src/sol/macros.rs index 00d764770374a..8540ab8b95b8f 100644 --- a/crates/lint/src/sol/macros.rs +++ b/crates/lint/src/sol/macros.rs @@ -9,9 +9,11 @@ /// - `$desc`: A short description of the lint. /// /// # Note -/// Each lint must have a `help` section in the foundry book. This help field is auto-generated by -/// the macro. Because of that, to ensure that new lint rules have their corresponding docs in the -/// book, the existence of the lint rule's help section is validated with a unit test. +/// Each lint must have a corresponding markdown documentation file at +/// `crates/lint/docs/.md`. The `help` URL is auto-generated by the macro and points to +/// the per-lint page on the Foundry docs site (`getfoundry.sh/forge/linting/`). To +/// ensure that new lint rules have their corresponding docs, the existence of every registered +/// lint's markdown file is validated by a unit test (see `crates/lint/src/sol/mod.rs`). #[macro_export] macro_rules! declare_forge_lint { ($id:ident, $severity:expr, $str_id:expr, $desc:expr) => { @@ -20,7 +22,7 @@ macro_rules! declare_forge_lint { id: $str_id, severity: $severity, description: $desc, - help: concat!("https://book.getfoundry.sh/reference/forge/forge-lint#", $str_id), + help: concat!("https://getfoundry.sh/forge/linting/", $str_id), }; }; } @@ -53,6 +55,7 @@ macro_rules! register_lints { register_lints!(@early_impl $pass_id, $pass_type); register_lints!(@late_impl $pass_id, $pass_type); + register_lints!(@project_impl $pass_id, $pass_type); } )* }; @@ -89,10 +92,22 @@ macro_rules! register_lints { .flatten() .collect() } + + pub fn create_project_lint_passes<'ast>() -> Vec<(Box>, &'static [SolLint])> { + [ + $( + register_lints!(@project_create $pass_id, $pass_type), + )* + ] + .into_iter() + .flatten() + .collect() + } }; // --- HELPERS ------------------------------------------------------------ (@early_impl $_pass_id:ident, late) => {}; + (@early_impl $_pass_id:ident, project) => {}; (@early_impl $pass_id:ident, $other:ident) => { pub fn as_early_lint_pass<'a>() -> Box> { Box::new(Self::default()) @@ -100,22 +115,41 @@ macro_rules! register_lints { }; (@late_impl $_pass_id:ident, early) => {}; + (@late_impl $_pass_id:ident, project) => {}; (@late_impl $pass_id:ident, $other:ident) => { pub fn as_late_lint_pass<'hir>() -> Box> { Box::new(Self::default()) } }; + (@project_impl $_pass_id:ident, early) => {}; + (@project_impl $_pass_id:ident, late) => {}; + (@project_impl $_pass_id:ident, both) => {}; + (@project_impl $pass_id:ident, $other:ident) => { + pub fn as_project_lint_pass<'ast>() -> Box> { + Box::new(Self::default()) + } + }; + (@early_create $_pass_id:ident, late) => { None }; + (@early_create $_pass_id:ident, project) => { None }; (@early_create $pass_id:ident, $_other:ident) => { Some(($pass_id::as_early_lint_pass(), $pass_id::LINTS)) }; (@late_create $_pass_id:ident, early) => { None }; + (@late_create $_pass_id:ident, project) => { None }; (@late_create $pass_id:ident, $_other:ident) => { Some(($pass_id::as_late_lint_pass(), $pass_id::LINTS)) }; + (@project_create $_pass_id:ident, early) => { None }; + (@project_create $_pass_id:ident, late) => { None }; + (@project_create $_pass_id:ident, both) => { None }; + (@project_create $pass_id:ident, $_other:ident) => { + Some(($pass_id::as_project_lint_pass(), $pass_id::LINTS)) + }; + // --- ENTRY POINT --------------------------------------------------------- ( $($tokens:tt)* ) => { register_lints! { @declare_structs $($tokens)* } diff --git a/crates/lint/src/sol/med/mod.rs b/crates/lint/src/sol/med/mod.rs index ba7a09b0e9bac..2673ba23d3252 100644 --- a/crates/lint/src/sol/med/mod.rs +++ b/crates/lint/src/sol/med/mod.rs @@ -9,6 +9,9 @@ use incorrect_erc20_interface::INCORRECT_ERC20_INTERFACE; mod incorrect_erc721_interface; use incorrect_erc721_interface::INCORRECT_ERC721_INTERFACE; +mod tx_origin; +use tx_origin::TX_ORIGIN; + mod unsafe_typecast; use unsafe_typecast::UNSAFE_TYPECAST; @@ -16,5 +19,6 @@ register_lints!( (DivideBeforeMultiply, early, (DIVIDE_BEFORE_MULTIPLY)), (IncorrectERC20Interface, late, (INCORRECT_ERC20_INTERFACE)), (IncorrectERC721Interface, late, (INCORRECT_ERC721_INTERFACE)), + (TxOrigin, early, (TX_ORIGIN)), (UnsafeTypecast, late, (UNSAFE_TYPECAST)) ); diff --git a/crates/lint/src/sol/med/tx_origin.rs b/crates/lint/src/sol/med/tx_origin.rs new file mode 100644 index 0000000000000..00ff5f939ebfb --- /dev/null +++ b/crates/lint/src/sol/med/tx_origin.rs @@ -0,0 +1,101 @@ +use super::TxOrigin; +use crate::{ + linter::{EarlyLintPass, LintContext}, + sol::{Severity, SolLint}, +}; +use solar::{ + ast::{Expr, ExprKind, IndexKind, Stmt, StmtKind}, + interface::SpannedOption, +}; + +declare_forge_lint!( + TX_ORIGIN, + Severity::Med, + "tx-origin", + "`tx.origin` should not be used for authorization" +); + +impl<'ast> EarlyLintPass<'ast> for TxOrigin { + fn check_stmt(&mut self, ctx: &LintContext, stmt: &'ast Stmt<'ast>) { + match &stmt.kind { + StmtKind::If(cond, ..) | StmtKind::DoWhile(_, cond) => { + emit_if_contains_tx_origin(ctx, cond); + } + StmtKind::While(cond, _) => { + emit_if_contains_tx_origin(ctx, cond); + } + StmtKind::For { cond: Some(cond), .. } => { + emit_if_contains_tx_origin(ctx, cond); + } + _ => {} + } + } + + fn check_expr(&mut self, ctx: &LintContext, expr: &'ast Expr<'ast>) { + if let ExprKind::Call(callee, args) = &expr.kind + && is_require_or_assert_call(callee) + && let Some(cond) = args.exprs().next() + { + emit_if_contains_tx_origin(ctx, cond); + } + } +} + +fn emit_if_contains_tx_origin(ctx: &LintContext, expr: &Expr<'_>) { + if contains_tx_origin(expr) { + ctx.emit(&TX_ORIGIN, expr.span); + } +} + +fn contains_tx_origin(expr: &Expr<'_>) -> bool { + if is_tx_origin(expr) { + return true; + } + match &expr.kind { + ExprKind::Unary(_, inner) => contains_tx_origin(inner), + ExprKind::Binary(lhs, _, rhs) => contains_tx_origin(lhs) || contains_tx_origin(rhs), + ExprKind::Index(base, index) => { + contains_tx_origin(base) + || match index { + IndexKind::Index(Some(index)) => contains_tx_origin(index), + IndexKind::Range(start, end) => { + start.as_ref().is_some_and(|start| contains_tx_origin(start)) + || end.as_ref().is_some_and(|end| contains_tx_origin(end)) + } + _ => false, + } + } + ExprKind::Tuple(elems) => elems.iter().any(|elem| { + if let SpannedOption::Some(inner) = elem.as_ref() { + contains_tx_origin(inner) + } else { + false + } + }), + ExprKind::Call(callee, args) => { + contains_tx_origin(callee) || args.exprs().any(contains_tx_origin) + } + ExprKind::Ternary(cond, then_expr, else_expr) => { + contains_tx_origin(cond) + || contains_tx_origin(then_expr) + || contains_tx_origin(else_expr) + } + _ => false, + } +} + +fn is_tx_origin(expr: &Expr<'_>) -> bool { + matches!( + &expr.kind, + ExprKind::Member(base, member) + if member.as_str() == "origin" + && matches!(&base.kind, ExprKind::Ident(ident) if ident.as_str() == "tx") + ) +} + +fn is_require_or_assert_call(callee: &Expr<'_>) -> bool { + matches!( + &callee.kind, + ExprKind::Ident(ident) if matches!(ident.as_str(), "require" | "assert") + ) +} diff --git a/crates/lint/src/sol/mod.rs b/crates/lint/src/sol/mod.rs index 59de6a40df015..7ae073f2ea20b 100644 --- a/crates/lint/src/sol/mod.rs +++ b/crates/lint/src/sol/mod.rs @@ -1,6 +1,6 @@ use crate::linter::{ EarlyLintPass, EarlyLintVisitor, LateLintPass, LateLintVisitor, Lint, LintContext, Linter, - LinterConfig, + LinterConfig, ProjectLintEmitter, ProjectLintPass, ProjectSource, }; use foundry_common::{ comments::{ @@ -21,6 +21,7 @@ use solar::{ interface::{ Session, diagnostics::{self, HumanEmitter, JsonEmitter}, + source_map::SourceFile, }, sema::{ Compiler, Gcx, @@ -29,7 +30,7 @@ use solar::{ }; use std::{ path::{Path, PathBuf}, - sync::LazyLock, + sync::{Arc, LazyLock}, }; use thiserror::Error; @@ -130,6 +131,7 @@ impl<'a> SolidityLinter<'a> { ast: &'gcx ast::SourceUnit<'gcx>, path: &Path, inline_config: &InlineConfig>, + source_file: Option>, ) -> Result<(), diagnostics::ErrorGuaranteed> { // Declare all available passes and lints let mut passes_and_lints = Vec::new(); @@ -168,6 +170,7 @@ impl<'a> SolidityLinter<'a> { self.with_json_emitter, self.config(inline_config), lints, + source_file, ); let mut early_visitor = EarlyLintVisitor::new(&ctx, &mut passes); _ = early_visitor.visit_source_unit(ast); @@ -176,12 +179,69 @@ impl<'a> SolidityLinter<'a> { Ok(()) } + /// Runs all enabled project-wide lint passes against the given input sources. + fn process_project<'gcx>(&self, gcx: Gcx<'gcx>, input: &[PathBuf]) { + // Gather enabled project passes from every severity bucket. + let mut passes_and_lints: Vec<(Box>, &'static [SolLint])> = + Vec::new(); + passes_and_lints.extend(high::create_project_lint_passes()); + passes_and_lints.extend(med::create_project_lint_passes()); + passes_and_lints.extend(low::create_project_lint_passes()); + passes_and_lints.extend(info::create_project_lint_passes()); + passes_and_lints.extend(gas::create_project_lint_passes()); + passes_and_lints.extend(codesize::create_project_lint_passes()); + + let (mut passes, lint_ids): (Vec>>, Vec<_>) = passes_and_lints + .into_iter() + .fold((Vec::new(), Vec::new()), |(mut passes, mut ids), (pass, lints)| { + let included: Vec<_> = lints + .iter() + .filter_map(|lint| self.include_lint(*lint).then_some(lint.id)) + .collect(); + if !included.is_empty() { + passes.push(pass); + ids.extend(included); + } + (passes, ids) + }); + + if passes.is_empty() { + return; + } + + // Pre-load every input source with its inline config, in input order. + let sources: Vec> = input + .iter() + .filter_map(|path| { + let path = self.path_config.root.join(path); + let (_, source) = gcx.get_ast_source(&path)?; + let ast = source.ast.as_ref()?; + let comments = + Comments::new(&source.file, gcx.sess.source_map(), false, false, None); + let inline_config = parse_inline_config(gcx.sess, &comments, ast); + Some(ProjectSource { path, file: source.file.clone(), ast, inline_config }) + }) + .collect(); + + let emitter = ProjectLintEmitter::new( + gcx.sess, + self.with_description, + self.with_json_emitter, + self.lint_specific, + lint_ids, + ); + for pass in &mut passes { + pass.check_project(&emitter, &sources); + } + } + fn process_source_hir<'gcx>( &self, gcx: Gcx<'gcx>, source_id: hir::SourceId, path: &Path, inline_config: &InlineConfig>, + source_file: Option>, ) -> Result<(), diagnostics::ErrorGuaranteed> { // Declare all available passes and lints let mut passes_and_lints = Vec::new(); @@ -220,6 +280,7 @@ impl<'a> SolidityLinter<'a> { self.with_json_emitter, self.config(inline_config), lints, + source_file, ); let mut late_visitor = LateLintVisitor::new(&ctx, &mut passes, &gcx.hir); @@ -288,15 +349,30 @@ impl<'a> Linter for SolidityLinter<'a> { let inline_config = parse_inline_config(gcx.sess, &comments, ast); // Early lints. - let _ = self.process_source_ast(gcx.sess, ast, path, &inline_config); + let _ = self.process_source_ast( + gcx.sess, + ast, + path, + &inline_config, + Some(file.clone()), + ); // Late lints. let Some((hir_source_id, _)) = gcx.get_hir_source(path) else { panic!("HIR source not found for {}", path.display()); }; - let _ = self.process_source_hir(gcx, hir_source_id, path, &inline_config); + let _ = self.process_source_hir( + gcx, + hir_source_id, + path, + &inline_config, + Some(file.clone()), + ); }); + // Project-wide lints, run once after all per-file passes. + self.process_project(gcx, input); + convert_solar_errors(compiler.dcx()) })?; @@ -436,3 +512,75 @@ impl<'a> TryFrom<&'a str> for SolLint { Err(SolLintError::InvalidId(value.to_string())) } } + +#[cfg(test)] +mod tests { + use super::*; + + /// Every registered lint must have a markdown documentation file at + /// `crates/lint/docs/.md`. This test enforces that contract so that the `help` URL + /// generated by `declare_forge_lint!` always resolves to real documentation. + /// + /// When this test fails, add a new file at `crates/lint/docs/.md` describing the + /// lint. See [`crates/lint/docs/_template.md`](../../docs/_template.md) for the expected + /// structure. + #[test] + fn registered_lints_have_docs() { + let docs_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("docs"); + assert!(docs_dir.is_dir(), "missing docs directory at {}", docs_dir.display()); + + let all_lints: Vec<&'static SolLint> = high::REGISTERED_LINTS + .iter() + .chain(med::REGISTERED_LINTS) + .chain(low::REGISTERED_LINTS) + .chain(info::REGISTERED_LINTS) + .chain(gas::REGISTERED_LINTS) + .chain(codesize::REGISTERED_LINTS) + .collect(); + + let mut missing: Vec<&'static str> = Vec::new(); + let mut empty: Vec<&'static str> = Vec::new(); + for lint in &all_lints { + let path = docs_dir.join(format!("{}.md", lint.id())); + match std::fs::read_to_string(&path) { + Ok(content) => { + // Basic sanity: file should be non-trivial and reference the lint id. + if content.trim().is_empty() || !content.contains(lint.id()) { + empty.push(lint.id()); + } + } + Err(_) => missing.push(lint.id()), + } + } + + assert!( + missing.is_empty(), + "the following registered lints are missing a docs file at \ + `crates/lint/docs/.md`: {missing:?}\n\ + See `crates/lint/docs/_template.md` for the expected structure." + ); + assert!( + empty.is_empty(), + "the following lint docs files are empty or do not reference the lint id: {empty:?}" + ); + } + + /// The auto-generated `help` URL must point at the canonical Foundry docs site so that the + /// link printed in diagnostics resolves correctly. + #[test] + fn registered_lints_have_canonical_help_url() { + let all_lints: Vec<&'static SolLint> = high::REGISTERED_LINTS + .iter() + .chain(med::REGISTERED_LINTS) + .chain(low::REGISTERED_LINTS) + .chain(info::REGISTERED_LINTS) + .chain(gas::REGISTERED_LINTS) + .chain(codesize::REGISTERED_LINTS) + .collect(); + + for lint in all_lints { + let expected = format!("https://getfoundry.sh/forge/linting/{}", lint.id()); + assert_eq!(lint.help(), expected, "lint `{}` has a non-canonical help URL", lint.id()); + } + } +} diff --git a/crates/lint/testdata/BlockTimestamp.stderr b/crates/lint/testdata/BlockTimestamp.stderr index 016f8fa2bdb2d..62ab588ae7340 100644 --- a/crates/lint/testdata/BlockTimestamp.stderr +++ b/crates/lint/testdata/BlockTimestamp.stderr @@ -4,7 +4,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp > deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -12,7 +12,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp == 0; │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -20,7 +20,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp != 0; │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -28,7 +28,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp <= deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -36,7 +36,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp >= deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -44,7 +44,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp < deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -52,7 +52,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return deadline > block.timestamp; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -60,7 +60,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return block.timestamp + 1 > deadline; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -68,7 +68,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return (block.timestamp / 3600) == 0; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -76,7 +76,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ require(block.timestamp > deadline); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -84,7 +84,7 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ if (block.timestamp > deadline) { │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp warning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators ╭▸ ROOT/testdata/BlockTimestamp.sol:LL:CC @@ -92,5 +92,5 @@ warning[block-timestamp]: usage of `block.timestamp` in a comparison may be mani LL │ return foo(block.timestamp) > 0; │ ━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#block-timestamp + ╰ help: https://getfoundry.sh/forge/linting/block-timestamp diff --git a/crates/lint/testdata/BooleanCst.sol b/crates/lint/testdata/BooleanCst.sol new file mode 100644 index 0000000000000..2b7cdc32f6702 --- /dev/null +++ b/crates/lint/testdata/BooleanCst.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract BooleanCst { + function check(bool flag) public pure returns (bool) { + if (false) {} //~WARN: misuse of a boolean constant + if (flag || true) {} //~WARN: misuse of a boolean constant + if (flag ? true : false) {} + //~^WARN: misuse of a boolean constant + //~|WARN: misuse of a boolean constant + while (true) { + break; + } + + bool assigned = true; + return assigned && false; //~WARN: misuse of a boolean constant + } + + function allowedBareConstants(bool flag) public pure returns (bool) { + takesBool(true); + return true; + } + + function takesBool(bool value) internal pure {} +} diff --git a/crates/lint/testdata/BooleanCst.stderr b/crates/lint/testdata/BooleanCst.stderr new file mode 100644 index 0000000000000..75fdb0b57cea7 --- /dev/null +++ b/crates/lint/testdata/BooleanCst.stderr @@ -0,0 +1,40 @@ +warning[boolean-cst]: misuse of a boolean constant + ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC + │ +LL │ if (false) {} + │ ━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst + +warning[boolean-cst]: misuse of a boolean constant + ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC + │ +LL │ if (flag || true) {} + │ ━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst + +warning[boolean-cst]: misuse of a boolean constant + ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC + │ +LL │ if (flag ? true : false) {} + │ ━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst + +warning[boolean-cst]: misuse of a boolean constant + ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC + │ +LL │ if (flag ? true : false) {} + │ ━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst + +warning[boolean-cst]: misuse of a boolean constant + ╭▸ ROOT/testdata/BooleanCst.sol:LL:CC + │ +LL │ return assigned && false; + │ ━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-cst + diff --git a/crates/lint/testdata/BooleanEqual.sol b/crates/lint/testdata/BooleanEqual.sol new file mode 100644 index 0000000000000..30171e9c797ac --- /dev/null +++ b/crates/lint/testdata/BooleanEqual.sol @@ -0,0 +1,24 @@ +//@compile-flags: --severity info + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract BooleanEqual { + function check(bool enabled, bool paused, bool ready, bool done) public pure { + if (enabled == true) {} //~NOTE: boolean comparisons to constants should be simplified + if (paused == false) {} //~NOTE: boolean comparisons to constants should be simplified + if (true != ready) {} //~NOTE: boolean comparisons to constants should be simplified + while (done != false) { //~NOTE: boolean comparisons to constants should be simplified + break; + } + for (; enabled == true && paused != false;) { + //~^NOTE: boolean comparisons to constants should be simplified + //~|NOTE: boolean comparisons to constants should be simplified + break; + } + } + + function returnedComparison(bool enabled) public pure returns (bool) { + return enabled == true; //~NOTE: boolean comparisons to constants should be simplified + } +} diff --git a/crates/lint/testdata/BooleanEqual.stderr b/crates/lint/testdata/BooleanEqual.stderr new file mode 100644 index 0000000000000..590a85b806fcf --- /dev/null +++ b/crates/lint/testdata/BooleanEqual.stderr @@ -0,0 +1,56 @@ +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ if (enabled == true) {} + │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `enabled` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ if (paused == false) {} + │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `!paused` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ if (true != ready) {} + │ ━━━━━━━━━━━━━ help: consider simplifying to: `!ready` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ while (done != false) { + │ ━━━━━━━━━━━━━ help: consider simplifying to: `done` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ for (; enabled == true && paused != false;) { + │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `enabled` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ for (; enabled == true && paused != false;) { + │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `paused` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + +note[boolean-equal]: boolean comparisons to constants should be simplified + ╭▸ ROOT/testdata/BooleanEqual.sol:LL:CC + │ +LL │ return enabled == true; + │ ━━━━━━━━━━━━━━━ help: consider simplifying to: `enabled` + │ + ╰ help: https://getfoundry.sh/forge/linting/boolean-equal + diff --git a/crates/lint/testdata/CouldBeImmutable.sol b/crates/lint/testdata/CouldBeImmutable.sol new file mode 100644 index 0000000000000..cd7e9aea13d01 --- /dev/null +++ b/crates/lint/testdata/CouldBeImmutable.sol @@ -0,0 +1,85 @@ +//@compile-flags: --only-lint could-be-immutable + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +contract CouldBeImmutable { + uint256 public constant MAX = 10; + uint256 public immutable ALREADY_IMMUTABLE; + + address public owner; + address public deployer = msg.sender; + uint256 private configured; + bytes32 internal salt = keccak256(abi.encodePacked(block.timestamp)); + CouldBeImmutable private peer; + + uint256 private mutableValue; + uint256 private assignedInInternal; + uint256 private compileTimeConstant = 1 + 2; + string private dynamicValue; + + constructor(uint256 configured_, CouldBeImmutable peer_, string memory value) { + ALREADY_IMMUTABLE = configured_; + owner = msg.sender; + configured = configured_; + peer = peer_; + mutableValue = 1; + setInternal(1); + dynamicValue = value; + } + + function setMutableValue(uint256 newValue) public { + mutableValue = newValue; + } + + function setInternal(uint256 newValue) internal { + assignedInInternal = newValue; + } +} + +contract BaseImmutableCandidate { + uint256 internal inheritedBase; +} + +contract DerivedImmutableCandidate is BaseImmutableCandidate { + constructor(uint256 value) { + inheritedBase = value; + } +} + +contract BaseConstructorImmutableCandidate { + uint256 internal baseConfigured; + + constructor(uint256 value) { + baseConfigured = value; + } +} + +contract DerivedConstructorImmutableCandidate is BaseConstructorImmutableCandidate { + constructor(uint256 value) BaseConstructorImmutableCandidate(value) {} +} + +contract ModifierBodyWrite { + uint256 private fromModifier; + + modifier initializesState() { + fromModifier = 1; + _; + } + + constructor() initializesState() {} +} + +contract AssemblyWrite { + uint256 private fromAssembly; + + constructor() { + fromAssembly = 1; + } + + function mutate() public { + assembly { + sstore(0, 2) + } + } +} diff --git a/crates/lint/testdata/CouldBeImmutable.stderr b/crates/lint/testdata/CouldBeImmutable.stderr new file mode 100644 index 0000000000000..170682baf89d3 --- /dev/null +++ b/crates/lint/testdata/CouldBeImmutable.stderr @@ -0,0 +1,56 @@ +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ address public owner; + │ ━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ address public deployer = msg.sender; + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ uint256 private configured; + │ ━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ bytes32 internal salt = keccak256(abi.encodePacked(block.timestamp)); + │ ━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ CouldBeImmutable private peer; + │ ━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ uint256 internal inheritedBase; + │ ━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/CouldBeImmutable.sol:LL:CC + │ +LL │ uint256 internal baseConfigured; + │ ━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + diff --git a/crates/lint/testdata/CustomErrors.stderr b/crates/lint/testdata/CustomErrors.stderr index 66b3c11bc183c..286a649aee269 100644 --- a/crates/lint/testdata/CustomErrors.stderr +++ b/crates/lint/testdata/CustomErrors.stderr @@ -4,7 +4,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ require(a > 0, "Value must be greater than zero"); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -12,7 +12,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ … require(a >= 0 && a <= 100 || b == 50, "Complex condition should be linted"); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -20,7 +20,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ revert("Something went wrong"); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -28,7 +28,7 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ revert(""); │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors note[custom-errors]: prefer using custom errors on revert and require calls ╭▸ ROOT/testdata/CustomErrors.sol:LL:CC @@ -36,5 +36,5 @@ note[custom-errors]: prefer using custom errors on revert and require calls LL │ revert(); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#custom-errors + ╰ help: https://getfoundry.sh/forge/linting/custom-errors diff --git a/crates/lint/testdata/DivideBeforeMultiply.stderr b/crates/lint/testdata/DivideBeforeMultiply.stderr index c0e5ef78e2e1c..95022f65db874 100644 --- a/crates/lint/testdata/DivideBeforeMultiply.stderr +++ b/crates/lint/testdata/DivideBeforeMultiply.stderr @@ -4,7 +4,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ (1 / 2) * 3; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -12,7 +12,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ ((1 / 2) * 3) * 4; │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -20,7 +20,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ ((1 * 2) / 3) * 4; │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -28,7 +28,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ (1 / 2 / 3) * 4; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -36,7 +36,7 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ (1 / (2 + 3)) * 4; │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision ╭▸ ROOT/testdata/DivideBeforeMultiply.sol:LL:CC @@ -44,5 +44,5 @@ warning[divide-before-multiply]: multiplication should occur before division to LL │ 1 / ((2 / 3) * 3); │ ━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply + ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply diff --git a/crates/lint/testdata/Imports.stderr b/crates/lint/testdata/Imports.stderr index 8fa9800b27ded..1031f4f6f8ca0 100644 --- a/crates/lint/testdata/Imports.stderr +++ b/crates/lint/testdata/Imports.stderr @@ -4,7 +4,7 @@ note[unaliased-plain-import]: use named imports '{A, B}' or alias 'import ".." a LL │ import "./auxiliary/ImportsSomeFile.sol"; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unaliased-plain-import + ╰ help: https://getfoundry.sh/forge/linting/unaliased-plain-import note[unaliased-plain-import]: use named imports '{A, B}' or alias 'import ".." as X' ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -12,7 +12,7 @@ note[unaliased-plain-import]: use named imports '{A, B}' or alias 'import ".." a LL │ import "./auxiliary/ImportsAnotherFile.sol"; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unaliased-plain-import + ╰ help: https://getfoundry.sh/forge/linting/unaliased-plain-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -20,7 +20,7 @@ note[unused-import]: unused imports should be removed LL │ symbol2 as notUsed, │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -28,7 +28,7 @@ note[unused-import]: unused imports should be removed LL │ docSymbol, │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -36,7 +36,7 @@ note[unused-import]: unused imports should be removed LL │ docSymbol2, │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -44,7 +44,7 @@ note[unused-import]: unused imports should be removed LL │ docSymbolWrongTag, │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -52,7 +52,7 @@ note[unused-import]: unused imports should be removed LL │ symbolNotUsed, │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -60,7 +60,7 @@ note[unused-import]: unused imports should be removed LL │ IContractNotUsed │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -68,7 +68,7 @@ note[unused-import]: unused imports should be removed LL │ symbolNotUsed3 │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -76,7 +76,7 @@ note[unused-import]: unused imports should be removed LL │ CONSTANT_1 │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -84,7 +84,7 @@ note[unused-import]: unused imports should be removed LL │ YetAnotherType │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -92,7 +92,7 @@ note[unused-import]: unused imports should be removed LL │ import "./auxiliary/ImportsAnotherFile2.sol" as AnotherFile2; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import note[unused-import]: unused imports should be removed ╭▸ ROOT/testdata/Imports.sol:LL:CC @@ -100,5 +100,5 @@ note[unused-import]: unused imports should be removed LL │ import * as OtherUtils from "./auxiliary/ImportsUtils2.sol"; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unused-import + ╰ help: https://getfoundry.sh/forge/linting/unused-import diff --git a/crates/lint/testdata/IncorrectERC20Interface.stderr b/crates/lint/testdata/IncorrectERC20Interface.stderr index 3bb60ecce8320..33e2f1ca27d22 100644 --- a/crates/lint/testdata/IncorrectERC20Interface.stderr +++ b/crates/lint/testdata/IncorrectERC20Interface.stderr @@ -4,7 +4,7 @@ note[interface-naming]: interface names should be prefixed with 'I' LL │ interface ERC20 { │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#interface-naming + ╰ help: https://getfoundry.sh/forge/linting/interface-naming note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20 {} │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface ERC20 { │ ━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20Incorrect is IERC20 { │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -36,7 +36,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20Correct is IERC20 { │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -44,7 +44,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20NamedCorrect { │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -52,7 +52,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface INotERC20 { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -60,7 +60,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function transfer(address to, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -68,7 +68,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function approve(address spender, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -76,7 +76,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function transfer(address to, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -84,7 +84,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function transferFrom(address from, address to, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -92,7 +92,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function approve(address spender, uint256 value) external returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -100,7 +100,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function allowance(address owner, address spender) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -108,7 +108,7 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function balanceOf(address account) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface warning[incorrect-erc20-interface]: incorrect ERC20 function interface ╭▸ ROOT/testdata/IncorrectERC20Interface.sol:LL:CC @@ -116,5 +116,5 @@ warning[incorrect-erc20-interface]: incorrect ERC20 function interface LL │ function totalSupply() external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc20-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc20-interface diff --git a/crates/lint/testdata/IncorrectERC721Interface.stderr b/crates/lint/testdata/IncorrectERC721Interface.stderr index a88db93e39b10..2e68084c1cec1 100644 --- a/crates/lint/testdata/IncorrectERC721Interface.stderr +++ b/crates/lint/testdata/IncorrectERC721Interface.stderr @@ -4,7 +4,7 @@ note[interface-naming]: interface names should be prefixed with 'I' LL │ interface ERC721 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#interface-naming + ╰ help: https://getfoundry.sh/forge/linting/interface-naming note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721 {} │ ━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface ERC721 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721Incorrect is IERC721 { │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -36,7 +36,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721Correct is IERC721 { │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -44,7 +44,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC721NamedCorrect { │ ━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -52,7 +52,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface INotERC721 { │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -60,7 +60,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function balanceOf(address owner) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -68,7 +68,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function ownerOf(uint256 tokenId) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -76,7 +76,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function balanceOf(address owner) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -84,7 +84,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function ownerOf(uint256 tokenId) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -92,7 +92,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -100,7 +100,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function safeTransferFrom(address from, address to, uint256 tokenId) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -108,7 +108,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function transferFrom(address from, address to, uint256 tokenId) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -116,7 +116,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function approve(address to, uint256 tokenId) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -124,7 +124,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function setApprovalForAll(address operator, bool approved) external returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -132,7 +132,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function getApproved(uint256 tokenId) external view returns (bool); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -140,7 +140,7 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function isApprovedForAll(address owner, address operator) external view returns (address); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface warning[incorrect-erc721-interface]: incorrect ERC721 function interface ╭▸ ROOT/testdata/IncorrectERC721Interface.sol:LL:CC @@ -148,5 +148,5 @@ warning[incorrect-erc721-interface]: incorrect ERC721 function interface LL │ function supportsInterface(bytes4 interfaceId) external view returns (uint256); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-erc721-interface + ╰ help: https://getfoundry.sh/forge/linting/incorrect-erc721-interface diff --git a/crates/lint/testdata/IncorrectShift.stderr b/crates/lint/testdata/IncorrectShift.stderr index bce84c98df432..dfff32db897bb 100644 --- a/crates/lint/testdata/IncorrectShift.stderr +++ b/crates/lint/testdata/IncorrectShift.stderr @@ -4,7 +4,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 2 << stateValue; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -12,7 +12,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 8 >> localValue; │ ━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -20,7 +20,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 16 << (stateValue + 1); │ ━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -28,7 +28,7 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ result = 32 >> getAmount(); │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift warning[incorrect-shift]: the order of args in a shift operation is incorrect ╭▸ ROOT/testdata/IncorrectShift.sol:LL:CC @@ -36,5 +36,5 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect LL │ … result = 1 << (localValue > 10 ? localShiftAmount : stateShiftAmount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift + ╰ help: https://getfoundry.sh/forge/linting/incorrect-shift diff --git a/crates/lint/testdata/InlineAssembly.sol b/crates/lint/testdata/InlineAssembly.sol new file mode 100644 index 0000000000000..05917ea22784c --- /dev/null +++ b/crates/lint/testdata/InlineAssembly.sol @@ -0,0 +1,110 @@ +//@compile-flags: --severity info + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract InlineAssembly { + function bare() public view returns (uint256 id) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + id := chainid() + } + } + + function withMemorySafe() public view returns (uint256 size) { + assembly ("memory-safe") { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + size := extcodesize(address()) + } + } + + function withDialectAndMemorySafe() public view returns (uint256 ptr) { + assembly "evmasm" ("memory-safe") { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + ptr := mload(0x40) + } + } + + function withNatspecMemorySafe() public view returns (uint256 v) { + /// @solidity memory-safe-assembly + assembly { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + v := chainid() + } + } + + function withNatspecMemorySafeAndOtherDocs() public view returns (uint256 v) { + /// @notice does a thing + /// @solidity memory-safe-assembly + assembly { //~NOTE: inline assembly (declared memory-safe); review business logic and side effects + v := gas() + } + } + + function plainCommentDoesNotCount() public view returns (uint256 v) { + // solidity memory-safe-assembly + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := chainid() + } + } + + function nestedInControlFlow(bool flag) public view returns (uint256 v) { + if (flag) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := gas() + } + } + + for (uint256 i = 0; i < 1; ++i) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := add(v, 1) + } + } + } + + function nestedInUnchecked(uint256 x) public pure returns (uint256 v) { + unchecked { + v = x + 1; + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := add(v, 1) + } + } + } + + function nestedInTryCatch() public returns (uint256 v) { + try this.bare() returns (uint256) { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := 1 + } + } catch { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + v := 2 + } + } + } + + function suppressed() public view returns (uint256 id) { + // forge-lint: disable-next-line(inline-assembly) + assembly { + id := chainid() + } + } + + modifier guarded() { + assembly { //~NOTE: inline assembly used; review for memory safety and side effects + if iszero(caller()) { revert(0, 0) } + } + _; + } + + function suppressedRegion() public view returns (uint256 a, uint256 b) { + // forge-lint: disable-start(inline-assembly) + assembly { + a := chainid() + } + assembly ("memory-safe") { + b := gas() + } + // forge-lint: disable-end(inline-assembly) + } + + function noAssembly() public pure returns (uint256) { + return 42; + } +} diff --git a/crates/lint/testdata/InlineAssembly.stderr b/crates/lint/testdata/InlineAssembly.stderr new file mode 100644 index 0000000000000..12f8bcbacd14e --- /dev/null +++ b/crates/lint/testdata/InlineAssembly.stderr @@ -0,0 +1,96 @@ +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly ("memory-safe") { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly "evmasm" ("memory-safe") { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly (declared memory-safe); review business logic and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + +note[inline-assembly]: inline assembly used; review for memory safety and side effects + ╭▸ ROOT/testdata/InlineAssembly.sol:LL:CC + │ +LL │ assembly { + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/inline-assembly + diff --git a/crates/lint/testdata/Keccak256.sol b/crates/lint/testdata/Keccak256.sol index 41f856336b5b3..2457aed96d601 100644 --- a/crates/lint/testdata/Keccak256.sol +++ b/crates/lint/testdata/Keccak256.sol @@ -52,6 +52,7 @@ contract AsmKeccak256 { function assemblyHash(uint256 a, uint256 b) public pure returns (bytes32) { //optimized + // forge-lint: disable-next-line(inline-assembly) assembly { mstore(0x00, a) mstore(0x20, b) diff --git a/crates/lint/testdata/Keccak256.stderr b/crates/lint/testdata/Keccak256.stderr index 05d1dcf5641a8..a81e429e389a1 100644 --- a/crates/lint/testdata/Keccak256.stderr +++ b/crates/lint/testdata/Keccak256.stderr @@ -4,7 +4,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 MixedCase_Variable = 1; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `mixedCaseVariable` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -12,7 +12,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Another_MixedCase = 2; │ ━━━━━━━━━━━━━━━━━ help: consider using: `anotherMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -20,7 +20,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 YetAnother_MixedCase = 3; │ ━━━━━━━━━━━━━━━━━━━━ help: consider using: `yetAnotherMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -28,7 +28,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Enabled_MixedCase_Variable; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `enabledMixedCaseVariable` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -36,7 +36,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Enabled_MixedCase_Variable = 1; │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `enabledMixedCaseVariable` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -44,7 +44,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract AsmKeccak256 { │ ━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -52,7 +52,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract OtherAsmKeccak256 { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -60,7 +60,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract YetAnotherAsmKeccak256 { │ ━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -68,7 +68,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 hash = keccak256(abi.encodePacked(a, b, bytes32(bytes20(c)))); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -76,7 +76,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 afterDisabledBlock = keccak256(abi.encode(a, b, c)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -84,7 +84,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 loadsFromCalldata = keccak256(z); │ ━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -92,7 +92,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 loadsFromMemory = keccak256(y); │ ━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -100,7 +100,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 lintWithoutFix = keccak256(abi.encodePacked(a, b, c)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -108,7 +108,15 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ return keccak256(abi.encode(a, b, c)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 + +note[unused-state-variables]: state variable is never used + ╭▸ ROOT/testdata/Keccak256.sol:LL:CC + │ +LL │ uint256 Enabled_MixedCase_Variable; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -116,7 +124,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 doesNotUseScratchSpace = keccak256(abi.encode(x, y, x, y, x, y)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -124,7 +132,7 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ bytes32 doesUseScratchSpace = keccak256(abi.encode(x)); │ ━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline assembly ╭▸ ROOT/testdata/Keccak256.sol:LL:CC @@ -132,5 +140,5 @@ note[asm-keccak256]: use of inefficient hashing mechanism; consider using inline LL │ return keccak256(abi.encode(doesUseScratchSpace, doesNotUseScratchSpace)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256 + ╰ help: https://getfoundry.sh/forge/linting/asm-keccak256 diff --git a/crates/lint/testdata/MissingZeroCheck.stderr b/crates/lint/testdata/MissingZeroCheck.stderr index b55a902547fcf..81a9179e79c94 100644 --- a/crates/lint/testdata/MissingZeroCheck.stderr +++ b/crates/lint/testdata/MissingZeroCheck.stderr @@ -4,7 +4,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwner(address newOwner) external { │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -12,7 +12,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ constructor(address initialOwner) { │ ━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -20,7 +20,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function pay(address payable to) external { │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -28,7 +28,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function lowLevel(address payable to, bytes calldata data) external { │ ━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -36,7 +36,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function withUselessModifier(address a) external doesNothing(a) { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -44,7 +44,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwnerViaAlias(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -52,7 +52,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwnerViaReassign(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -60,7 +60,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function setOwnerViaCast(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -68,7 +68,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function payViaAlias(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -76,7 +76,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function mixedParams(address a, address b) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -84,7 +84,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function bothSinks(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -92,7 +92,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function ternaryAlias(address a, bool flag) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -100,7 +100,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function payableWrap(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -108,7 +108,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function modifierWithExpr(address a) external nonZero(addrIdentity(a)) { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -116,7 +116,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function delegateCallSink(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -124,7 +124,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function sendSinkStmt(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -132,7 +132,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function sendSinkDecl(address payable a) external { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -140,7 +140,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function multiHopTaint(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -148,7 +148,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardAfterSink(address a) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -156,7 +156,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardOnOneBranch(address a, bool flag) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -164,7 +164,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardInForLoop(address a, uint256 n) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -172,7 +172,7 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardInWhileLoop(address a, bool flag) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check warning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check ╭▸ ROOT/testdata/MissingZeroCheck.sol:LL:CC @@ -180,5 +180,5 @@ warning[missing-zero-check]: address parameter is used in a state write or value LL │ function guardInTryClause(address a, address payable target) external { │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#missing-zero-check + ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check diff --git a/crates/lint/testdata/MixedCase.stderr b/crates/lint/testdata/MixedCase.stderr index d290af5cdb5a8..2db30559ba5a6 100644 --- a/crates/lint/testdata/MixedCase.stderr +++ b/crates/lint/testdata/MixedCase.stderr @@ -4,7 +4,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 Variablemixedcase; │ ━━━━━━━━━━━━━━━━━ help: consider using: `variablemixedcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -12,7 +12,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 VARIABLE_MIXED_CASE; │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `variableMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -20,7 +20,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 VariableMixedCase; │ ━━━━━━━━━━━━━━━━━ help: consider using: `variableMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -28,7 +28,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 testVAL; │ ━━━━━━━ help: consider using: `testVal` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -36,7 +36,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 TestVal; │ ━━━━━━━ help: consider using: `testVal` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-variable]: mutable variables should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -44,7 +44,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase LL │ uint256 TESTVAL; │ ━━━━━━━ help: consider using: `testval` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-variable note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -52,7 +52,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function Functionmixedcase() public {} │ ━━━━━━━━━━━━━━━━━ help: consider using: `functionmixedcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -60,7 +60,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function FUNCTION_MIXED_CASE() public {} │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -68,7 +68,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function FunctionMixedCase() public {} │ ━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -76,7 +76,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function function_mixed_case() public {} │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `functionMixedCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -84,7 +84,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function invariantBalance_MixedCase_Enabled() public {} │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `invariantBalanceMixedCaseEnabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -92,7 +92,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function invariantbalance_mixedcase_enabled() public {} │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `invariantbalanceMixedcaseEnabled` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -100,7 +100,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function ERC20_DoSomething() public {} // invalid because of the underscore │ ━━━━━━━━━━━━━━━━━ help: consider using: `erc20DoSomething` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -108,7 +108,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function HAS_PARAMS(address addr) external view returns (uint256) {} │ ━━━━━━━━━━ help: consider using: `hasParams` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -116,7 +116,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function HAS_NO_RETURN() external view {} │ ━━━━━━━━━━━━━ help: consider using: `hasNoReturn` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -124,7 +124,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function HAS_MORE_THAN_ONE_RETURN() external view returns (uint256, uint256) {} │ ━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `hasMoreThanOneReturn` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[mixed-case-function]: function names should use mixedCase ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -132,7 +132,7 @@ note[mixed-case-function]: function names should use mixedCase LL │ function NOT_ELEMENTARY_RETURN() external view returns (uint256[] memory) {} │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `notElementaryReturn` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-function + ╰ help: https://getfoundry.sh/forge/linting/mixed-case-function note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -140,7 +140,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MixedCase.sol:LL:CC @@ -148,5 +148,5 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract MixedCaseTest { │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file diff --git a/crates/lint/testdata/MultiContractFile.stderr b/crates/lint/testdata/MultiContractFile.stderr index c6e4e32a2df55..e25f3d72ad01a 100644 --- a/crates/lint/testdata/MultiContractFile.stderr +++ b/crates/lint/testdata/MultiContractFile.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract A {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract B {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract C {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface I {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile.sol:LL:CC @@ -36,5 +36,5 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ library L {} │ ━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file diff --git a/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr b/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr index 41fc439ea7d1b..1912f16863712 100644 --- a/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr +++ b/crates/lint/testdata/MultiContractFile_InterfaceLibrary.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface I1 {} │ ━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile_InterfaceLibrary.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ library L1 {} │ ━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/MultiContractFile_InterfaceLibrary.sol:LL:CC @@ -20,5 +20,5 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract C1 {} │ ━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file diff --git a/crates/lint/testdata/NamedStructFields.stderr b/crates/lint/testdata/NamedStructFields.stderr index 6ee2160791cd2..cfb35637176bd 100644 --- a/crates/lint/testdata/NamedStructFields.stderr +++ b/crates/lint/testdata/NamedStructFields.stderr @@ -4,5 +4,5 @@ note[named-struct-fields]: prefer initializing structs with named fields LL │ Person memory person = Person("Alice", 25, address(0)); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ help: consider using named fields: `Person({ name: "Alice", age: 25, wallet: address(0) })` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#named-struct-fields + ╰ help: https://getfoundry.sh/forge/linting/named-struct-fields diff --git a/crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol new file mode 100644 index 0000000000000..bfc993baab794 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; //~NOTE: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: 0.8.18 +pragma solidity 0.8.18; //~NOTE: 'pragma solidity 0.8.18;' conflicts with other version requirements in the project: ^0.8.0 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr new file mode 100644 index 0000000000000..c2c967dee792f --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretAboveExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: 0.8.18 + ╭▸ ROOT/testdata/PragmaInconsistentCaretAboveExact.sol:LL:CC + │ +LL │ pragma solidity ^0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.18;' conflicts with other version requirements in the project: ^0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentCaretAboveExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.18; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol new file mode 100644 index 0000000000000..75bc17988accc --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; //~NOTE: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 +pragma solidity 0.8.20; //~NOTE: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr new file mode 100644 index 0000000000000..f60361718ba9b --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretMatchesExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: 0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretMatchesExact.sol:LL:CC + │ +LL │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretMatchesExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol new file mode 100644 index 0000000000000..37b06040c33a6 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; //~NOTE: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: ~0.8.20 +pragma solidity ~0.8.20; //~NOTE: 'pragma solidity ~0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr new file mode 100644 index 0000000000000..6c46f2478208d --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentCaretVsTilde.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity ^0.8.20;' conflicts with other version requirements in the project: ~0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretVsTilde.sol:LL:CC + │ +LL │ pragma solidity ^0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ~0.8.20;' conflicts with other version requirements in the project: ^0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentCaretVsTilde.sol:LL:CC + │ +LL │ pragma solidity ~0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentOrVsExact.sol b/crates/lint/testdata/PragmaInconsistentOrVsExact.sol new file mode 100644 index 0000000000000..f85a477cc8744 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentOrVsExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20 || 0.8.21; //~NOTE: 'pragma solidity 0.8.20 || 0.8.21;' conflicts with other version requirements in the project: 0.8.20 +pragma solidity 0.8.20; //~NOTE: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: 0.8.20 || 0.8.21 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentOrVsExact.stderr b/crates/lint/testdata/PragmaInconsistentOrVsExact.stderr new file mode 100644 index 0000000000000..acf6bd7c2d6e0 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentOrVsExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity 0.8.20 || 0.8.21;' conflicts with other version requirements in the project: 0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentOrVsExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20 || 0.8.21; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: 0.8.20 || 0.8.21 + ╭▸ ROOT/testdata/PragmaInconsistentOrVsExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentRangeVsExact.sol b/crates/lint/testdata/PragmaInconsistentRangeVsExact.sol new file mode 100644 index 0000000000000..d8fcb7a0eb4b1 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentRangeVsExact.sol @@ -0,0 +1,7 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; //~NOTE: 'pragma solidity >=0.8.0 <0.9.0;' conflicts with other version requirements in the project: 0.8.20 +pragma solidity 0.8.20; //~NOTE: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: >=0.8.0 <0.9.0 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr b/crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr new file mode 100644 index 0000000000000..5ac221b924c9a --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentRangeVsExact.stderr @@ -0,0 +1,16 @@ +note[pragma-inconsistent]: 'pragma solidity >=0.8.0 <0.9.0;' conflicts with other version requirements in the project: 0.8.20 + ╭▸ ROOT/testdata/PragmaInconsistentRangeVsExact.sol:LL:CC + │ +LL │ pragma solidity >=0.8.0 <0.9.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity 0.8.20;' conflicts with other version requirements in the project: >=0.8.0 <0.9.0 + ╭▸ ROOT/testdata/PragmaInconsistentRangeVsExact.sol:LL:CC + │ +LL │ pragma solidity 0.8.20; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/PragmaInconsistentThreeDistinct.sol b/crates/lint/testdata/PragmaInconsistentThreeDistinct.sol new file mode 100644 index 0000000000000..fe208e15efb63 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentThreeDistinct.sol @@ -0,0 +1,8 @@ +//@compile-flags: --only-lint pragma-inconsistent + +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; //~NOTE: 'pragma solidity >=0.8.0;' conflicts with other version requirements in the project: ^0.8.0, ~0.8.0 +pragma solidity ^0.8.0; //~NOTE: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ~0.8.0 +pragma solidity ~0.8.0; //~NOTE: 'pragma solidity ~0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ^0.8.0 + +contract Main {} diff --git a/crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr b/crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr new file mode 100644 index 0000000000000..e1e5ad7333fb2 --- /dev/null +++ b/crates/lint/testdata/PragmaInconsistentThreeDistinct.stderr @@ -0,0 +1,24 @@ +note[pragma-inconsistent]: 'pragma solidity >=0.8.0;' conflicts with other version requirements in the project: ^0.8.0, ~0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentThreeDistinct.sol:LL:CC + │ +LL │ pragma solidity >=0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ^0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ~0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentThreeDistinct.sol:LL:CC + │ +LL │ pragma solidity ^0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + +note[pragma-inconsistent]: 'pragma solidity ~0.8.0;' conflicts with other version requirements in the project: >=0.8.0, ^0.8.0 + ╭▸ ROOT/testdata/PragmaInconsistentThreeDistinct.sol:LL:CC + │ +LL │ pragma solidity ~0.8.0; + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/pragma-inconsistent + diff --git a/crates/lint/testdata/Rtlo.sol b/crates/lint/testdata/Rtlo.sol new file mode 100644 index 0000000000000..82f7e68a41548 --- /dev/null +++ b/crates/lint/testdata/Rtlo.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +// Tests for the `rtlo` lint, which detects "Trojan Source" bidirectional +// formatting characters (CVE-2021-42574). These have no legitimate use in +// Solidity source and can be used to hide malicious code. +// +// Note: solc itself rejects unbalanced directional override markers (error +// 8936), so each test uses a balanced opening/closing pair. Our lint flags +// each occurrence individually regardless of balance. + +contract Rtlo { + // SHOULD FAIL: every codepoint in the Trojan-Source set is flagged. + // Each line below contains two bidi characters (an opener and its closer) + // and produces two diagnostics. + + string public lre = unicode"‪_‬"; + //~^WARN: U+202A (Left-to-Right Embedding) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + string public rle = unicode"‫_‬"; + //~^WARN: U+202B (Right-to-Left Embedding) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + string public pdf = unicode"‪‬"; + //~^WARN: U+202A (Left-to-Right Embedding) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + string public lro = unicode"‭_‬"; + //~^WARN: U+202D (Left-to-Right Override) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + string public rlo = unicode"‮_‬"; + //~^WARN: U+202E (Right-to-Left Override) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + string public lri = unicode"⁦_⁩"; + //~^WARN: U+2066 (Left-to-Right Isolate) detected + //~|WARN: U+2069 (Pop Directional Isolate) detected + + string public rli = unicode"⁧_⁩"; + //~^WARN: U+2067 (Right-to-Left Isolate) detected + //~|WARN: U+2069 (Pop Directional Isolate) detected + + string public fsi = unicode"⁨_⁩"; + //~^WARN: U+2068 (First Strong Isolate) detected + //~|WARN: U+2069 (Pop Directional Isolate) detected + + string public pdi = unicode"⁦⁩"; + //~^WARN: U+2066 (Left-to-Right Isolate) detected + //~|WARN: U+2069 (Pop Directional Isolate) detected + + // SHOULD FAIL: bidi controls inside a block comment are also detected. + /* hidden‮ /* text ‬ */ uint256 inBlockComment; + //~^WARN: U+202E (Right-to-Left Override) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + // SHOULD FAIL: bidi controls inside a line comment are also detected. The + // expectation markers must come on separate lines because the ui-test + // parser only treats the first comment on a line as a marker. + // sneaky‮ payload ‬ trailing + //~^WARN: U+202E (Right-to-Left Override) detected + //~|WARN: U+202C (Pop Directional Formatting) detected + + // SHOULD PASS: inline-config disable suppresses the diagnostic. + // forge-lint: disable-next-line(rtlo) + string public suppressedLine = unicode"‮_‬"; + + // forge-lint: disable-start(rtlo) + string public suppressedA = unicode"‮_‬"; + string public suppressedB = unicode"⁦_⁩"; + // forge-lint: disable-end(rtlo) + + // SHOULD PASS: plain ASCII source, no bidi controls. + string public clean = "no bidi here"; + + // SHOULD FAIL: LRM/RLM marks (U+200E/U+200F) are also flagged. + string public marks = unicode"left‎right‏end"; + //~^WARN: U+200E (Left-to-Right Mark) detected + //~|WARN: U+200F (Right-to-Left Mark) detected +} diff --git a/crates/lint/testdata/Rtlo.stderr b/crates/lint/testdata/Rtlo.stderr new file mode 100644 index 0000000000000..93f5bb191532f --- /dev/null +++ b/crates/lint/testdata/Rtlo.stderr @@ -0,0 +1,192 @@ +warning[rtlo]: U+202A (Left-to-Right Embedding) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public lre = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public lre = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202B (Right-to-Left Embedding) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public rle = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public rle = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202A (Left-to-Right Embedding) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public pdf = unicode"��"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public pdf = unicode"��"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202D (Left-to-Right Override) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public lro = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public lro = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202E (Right-to-Left Override) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public rlo = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public rlo = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2066 (Left-to-Right Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public lri = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2069 (Pop Directional Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public lri = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2067 (Right-to-Left Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public rli = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2069 (Pop Directional Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public rli = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2068 (First Strong Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public fsi = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2069 (Pop Directional Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public fsi = unicode"�_�"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2066 (Left-to-Right Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public pdi = unicode"��"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+2069 (Pop Directional Isolate) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public pdi = unicode"��"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202E (Right-to-Left Override) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ /* hidden� /* text � */ uint256 inBlockComment; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ /* hidden� /* text � */ uint256 inBlockComment; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202E (Right-to-Left Override) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ // sneaky� payload � trailing + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ // sneaky� payload � trailing + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+200E (Left-to-Right Mark) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public marks = unicode"left‎right‏end"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+200F (Right-to-Left Mark) detected + ╭▸ ROOT/testdata/Rtlo.sol:LL:CC + │ +LL │ string public marks = unicode"left‎right‏end"; + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + diff --git a/crates/lint/testdata/RtloCommentsOnly.sol b/crates/lint/testdata/RtloCommentsOnly.sol new file mode 100644 index 0000000000000..82a1fbd7fc58e --- /dev/null +++ b/crates/lint/testdata/RtloCommentsOnly.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +// Bidi chars that only appear in comments (outside any item) must still be +// reported. + +// hidden‮ payload ‬ trailing +//~^WARN: U+202E (Right-to-Left Override) detected +//~|WARN: U+202C (Pop Directional Formatting) detected + +/* block‮ comment ‬ end */ +//~^WARN: U+202E (Right-to-Left Override) detected +//~|WARN: U+202C (Pop Directional Formatting) detected + +contract RtloCommentsOnly {} diff --git a/crates/lint/testdata/RtloCommentsOnly.stderr b/crates/lint/testdata/RtloCommentsOnly.stderr new file mode 100644 index 0000000000000..5a7ec9ee6e69d --- /dev/null +++ b/crates/lint/testdata/RtloCommentsOnly.stderr @@ -0,0 +1,32 @@ +warning[rtlo]: U+202E (Right-to-Left Override) detected + ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC + │ +LL │ // hidden� payload � trailing + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC + │ +LL │ // hidden� payload � trailing + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202E (Right-to-Left Override) detected + ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC + │ +LL │ /* block� comment � end */ + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + +warning[rtlo]: U+202C (Pop Directional Formatting) detected + ╭▸ ROOT/testdata/RtloCommentsOnly.sol:LL:CC + │ +LL │ /* block� comment � end */ + │ ━ + │ + ╰ help: https://getfoundry.sh/forge/linting/rtlo + diff --git a/crates/lint/testdata/ScreamingSnakeCase.stderr b/crates/lint/testdata/ScreamingSnakeCase.stderr index a740506ed74d8..36305bb268d9a 100644 --- a/crates/lint/testdata/ScreamingSnakeCase.stderr +++ b/crates/lint/testdata/ScreamingSnakeCase.stderr @@ -4,7 +4,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant screamingSnakeCase = 0; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -12,7 +12,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant screaming_snake_case = 0; │ ━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -20,7 +20,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant ScreamingSnakeCase = 0; │ ━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -28,7 +28,7 @@ note[screaming-snake-case-const]: constants should use SCREAMING_SNAKE_CASE LL │ uint256 constant SCREAMING_snake_case = 0; │ ━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-const + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-const note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -36,7 +36,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable screamingSnakeCase0 = 0; │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -44,7 +44,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable screaming_snake_case0 = 0; │ ━━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -52,7 +52,7 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable ScreamingSnakeCase0 = 0; │ ━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE ╭▸ ROOT/testdata/ScreamingSnakeCase.sol:LL:CC @@ -60,5 +60,5 @@ note[screaming-snake-case-immutable]: immutables should use SCREAMING_SNAKE_CASE LL │ uint256 immutable SCREAMING_snake_case_0 = 0; │ ━━━━━━━━━━━━━━━━━━━━━━ help: consider using: `SCREAMING_SNAKE_CASE_0` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#screaming-snake-case-immutable + ╰ help: https://getfoundry.sh/forge/linting/screaming-snake-case-immutable diff --git a/crates/lint/testdata/StructPascalCase.stderr b/crates/lint/testdata/StructPascalCase.stderr index 1c7bfa13ba84b..255c1c4d5d74b 100644 --- a/crates/lint/testdata/StructPascalCase.stderr +++ b/crates/lint/testdata/StructPascalCase.stderr @@ -4,7 +4,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct _PascalCase { │ ━━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -12,7 +12,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct pascalCase { │ ━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -20,7 +20,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct pascalcase { │ ━━━━━━━━━━ help: consider using: `Pascalcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -28,7 +28,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct pascal_case { │ ━━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -36,7 +36,7 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct PASCAL_CASE { │ ━━━━━━━━━━━ help: consider using: `PascalCase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct note[pascal-case-struct]: structs should use PascalCase ╭▸ ROOT/testdata/StructPascalCase.sol:LL:CC @@ -44,5 +44,5 @@ note[pascal-case-struct]: structs should use PascalCase LL │ struct PASCALCASE { │ ━━━━━━━━━━ help: consider using: `Pascalcase` │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#pascal-case-struct + ╰ help: https://getfoundry.sh/forge/linting/pascal-case-struct diff --git a/crates/lint/testdata/TooManyDigits.sol b/crates/lint/testdata/TooManyDigits.sol new file mode 100644 index 0000000000000..a56ad67fe379e --- /dev/null +++ b/crates/lint/testdata/TooManyDigits.sol @@ -0,0 +1,73 @@ +//@compile-flags: --severity info + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract TooManyDigits { + // SHOULD FAIL: plain decimal integer literals with 5+ consecutive zeros. + + uint256 stateA = 1000000000000000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + uint256 stateB = 100000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + function asReturn() public pure returns (uint256) { + return 10000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + function asComparison(uint256 x) public pure returns (bool) { + return x == 1000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + function asArg(address to) public { + _send(to, 50000000000); //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + function asArraySize() public pure { + uint256[100000] memory _arr; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + } + + // Zero-run in the middle (not just trailing). + uint256 middleZeros = 123000007; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + // Underscores that don't actually break up the zero run. + uint256 badGrouping = 1_000000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + // Underscore right after a single digit, leaving a 5-zero group. + uint256 badGrouping2 = 1_00000; //~NOTE: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + + // SHOULD PASS: + + // Boundary: 4 consecutive zeros (one short of the threshold). + uint256 fourZeros = 10000; + + // Uppercase scientific notation. + uint256 sciUpper = 1E18; + + // Scientific notation. + uint256 sci = 1e18; + + // Underscore-separated digit groups. + uint256 grouped = 1_000_000_000_000_000_000; + + // Sub-denominations. + uint256 oneEther = 1 ether; + uint256 oneGwei = 1 gwei; + uint256 fiveMin = 5 minutes; + + // Address literal (distinct AST kind, not flagged). + address adr = 0x1234567890123456789012345678901234567890; + + // Hex literal — intentional zero patterns (mask / padded value). + bytes32 mask = 0x0000000000000000000000000000000000000000000000000000000000000001; + uint256 hexNum = 0x100000; + + // Small literals (< 5 consecutive zeros). + uint256 small1 = 100; + uint256 small2 = 9999; + uint256 small3 = 1234; + uint256 spread = 101010; + + // Boolean literal. + bool flag = true; + + function _send(address, uint256) internal pure {} +} diff --git a/crates/lint/testdata/TooManyDigits.stderr b/crates/lint/testdata/TooManyDigits.stderr new file mode 100644 index 0000000000000..7e21a530776c2 --- /dev/null +++ b/crates/lint/testdata/TooManyDigits.stderr @@ -0,0 +1,72 @@ +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 stateA = 1000000000000000000; + │ ━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 stateB = 100000; + │ ━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … return 10000000; + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … return x == 1000000; + │ ━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … _send(to, 50000000000); + │ ━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ … uint256[100000] memory _arr; + │ ━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 middleZeros = 123000007; + │ ━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 badGrouping = 1_000000; + │ ━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + +note[too-many-digits]: numeric literal with many digits is error-prone; use scientific notation, sub-denominations, or underscore separators + ╭▸ ROOT/testdata/TooManyDigits.sol:LL:CC + │ +LL │ uint256 badGrouping2 = 1_00000; + │ ━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/too-many-digits + diff --git a/crates/lint/testdata/TxOrigin.sol b/crates/lint/testdata/TxOrigin.sol new file mode 100644 index 0000000000000..9728a7e528e5b --- /dev/null +++ b/crates/lint/testdata/TxOrigin.sol @@ -0,0 +1,65 @@ +//@compile-flags: --only-lint tx-origin +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract TxOrigin { + address public owner; + mapping(address => bool) public allowed; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(tx.origin == owner, "not owner"); //~WARN: `tx.origin` should not be used for authorization + _; + } + + function guardedByIf() external view { + if (tx.origin != owner) { //~WARN: `tx.origin` should not be used for authorization + revert("not owner"); + } + } + + function guardedByPredicate() external view { + assert(isOwner(tx.origin)); //~WARN: `tx.origin` should not be used for authorization + } + + function guardedByWhile() external view { + while (tx.origin == owner) { //~WARN: `tx.origin` should not be used for authorization + break; + } + } + + function guardedByFor() external view { + for (; tx.origin == owner;) { //~WARN: `tx.origin` should not be used for authorization + break; + } + } + + function guardedByDoWhile() external view { + do { + } while (tx.origin == owner); //~WARN: `tx.origin` should not be used for authorization + } + + function guardedByMapping() external view { + require(allowed[tx.origin], "not allowed"); //~WARN: `tx.origin` should not be used for authorization + require(allowed[tx.origin] == true, "not allowed"); //~WARN: `tx.origin` should not be used for authorization + } + + function guardedByTernary() external view { + require(tx.origin == owner ? true : false, "not owner"); //~WARN: `tx.origin` should not be used for authorization + } + + function readForLogging() external view returns (address) { + return tx.origin; + } + + function explicitSenderCheck() external view { + require(msg.sender == owner, "not owner"); + } + + function isOwner(address account) internal view returns (bool) { + return account == owner; + } +} diff --git a/crates/lint/testdata/TxOrigin.stderr b/crates/lint/testdata/TxOrigin.stderr new file mode 100644 index 0000000000000..7c2e70225b76d --- /dev/null +++ b/crates/lint/testdata/TxOrigin.stderr @@ -0,0 +1,72 @@ +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(tx.origin == owner, "not owner"); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ if (tx.origin != owner) { + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ assert(isOwner(tx.origin)); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ while (tx.origin == owner) { + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ for (; tx.origin == owner;) { + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ } while (tx.origin == owner); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(allowed[tx.origin], "not allowed"); + │ ━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(allowed[tx.origin] == true, "not allowed"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + +warning[tx-origin]: `tx.origin` should not be used for authorization + ╭▸ ROOT/testdata/TxOrigin.sol:LL:CC + │ +LL │ require(tx.origin == owner ? true : false, "not owner"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/tx-origin + diff --git a/crates/lint/testdata/UncheckedCall.stderr b/crates/lint/testdata/UncheckedCall.stderr index afb8ade4ea89b..8a8a9fa9b5e17 100644 --- a/crates/lint/testdata/UncheckedCall.stderr +++ b/crates/lint/testdata/UncheckedCall.stderr @@ -4,7 +4,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.call(data); │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -12,7 +12,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.call{value: value}(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -20,7 +20,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.delegatecall(data); │ ━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -28,7 +28,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target.staticcall(data); │ ━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -36,7 +36,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target1.call(""); │ ━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -44,7 +44,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ target2.delegatecall(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -52,7 +52,7 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ (, bytes memory data) = target.call(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call warning[unchecked-call]: Low-level calls should check the success return value ╭▸ ROOT/testdata/UncheckedCall.sol:LL:CC @@ -60,5 +60,5 @@ warning[unchecked-call]: Low-level calls should check the success return value LL │ (, existingData) = target.call(""); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unchecked-call + ╰ help: https://getfoundry.sh/forge/linting/unchecked-call diff --git a/crates/lint/testdata/UncheckedTransferERC20.stderr b/crates/lint/testdata/UncheckedTransferERC20.stderr index 733d22ce610d1..2c2caa69e7215 100644 --- a/crates/lint/testdata/UncheckedTransferERC20.stderr +++ b/crates/lint/testdata/UncheckedTransferERC20.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20 { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ interface IERC20Wrapper { │ ━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -20,7 +20,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract UncheckedTransfer { │ ━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -28,7 +28,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ library Currency { │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -36,7 +36,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract UncheckedTransferUsingCurrencyLib { │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -44,7 +44,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ IERC20(address(token)).transfer(to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -52,7 +52,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ token.transfer(to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -60,7 +60,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ … IERC20(address(token)).transferFrom(from, to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -68,7 +68,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ token.transferFrom(from, to, amount); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -76,7 +76,7 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ … IERC20(address(token)).transfer(recipients[i], amounts[i]); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value ╭▸ ROOT/testdata/UncheckedTransferERC20.sol:LL:CC @@ -84,5 +84,5 @@ warning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls sho LL │ token.transfer(recipients[i], amounts[i]); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer + ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer diff --git a/crates/lint/testdata/UnsafeCheatcodes.stderr b/crates/lint/testdata/UnsafeCheatcodes.stderr index e66a4d72c70de..5b8b429942e80 100644 --- a/crates/lint/testdata/UnsafeCheatcodes.stderr +++ b/crates/lint/testdata/UnsafeCheatcodes.stderr @@ -4,7 +4,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -12,7 +12,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.readFile("test.txt"); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -20,7 +20,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.readLine("test.txt"); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -28,7 +28,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.writeFile("test.txt", "data"); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -36,7 +36,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.writeLine("test.txt", "data"); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -44,7 +44,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.removeFile("test.txt"); │ ━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -52,7 +52,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.closeFile("test.txt"); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -60,7 +60,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.setEnv("KEY", "value"); │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -68,7 +68,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.deriveKey("mnemonic", 0); │ ━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -76,7 +76,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ bytes memory result = vm.ffi(inputs); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -84,7 +84,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.ffi(new string[](1)); │ ━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -92,7 +92,7 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.setEnv("KEY", "value"); │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous operations ╭▸ ROOT/testdata/UnsafeCheatcodes.sol:LL:CC @@ -100,5 +100,5 @@ note[unsafe-cheatcode]: usage of unsafe cheatcodes that can perform dangerous op LL │ vm.readFile("test.txt"); │ ━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-cheatcode + ╰ help: https://getfoundry.sh/forge/linting/unsafe-cheatcode diff --git a/crates/lint/testdata/UnsafeTypecast.stderr b/crates/lint/testdata/UnsafeTypecast.stderr index b3e0334d63d43..d909b90973e00 100644 --- a/crates/lint/testdata/UnsafeTypecast.stderr +++ b/crates/lint/testdata/UnsafeTypecast.stderr @@ -4,7 +4,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract UnsafeTypecast { │ ━━━━━━━━━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file note[multi-contract-file]: prefer having only one contract, interface or library per file ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -12,7 +12,7 @@ note[multi-contract-file]: prefer having only one contract, interface or library LL │ contract Repros { │ ━━━━━━ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#multi-contract-file + ╰ help: https://getfoundry.sh/forge/linting/multi-contract-file warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -26,7 +26,7 @@ LL │ uint248 b = uint248(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -40,7 +40,7 @@ LL │ uint240 c = uint240(b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -54,7 +54,7 @@ LL │ uint232 d = uint232(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -68,7 +68,7 @@ LL │ uint224 e = uint224(d); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -82,7 +82,7 @@ LL │ uint216 f = uint216(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -96,7 +96,7 @@ LL │ uint208 g = uint208(f); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -110,7 +110,7 @@ LL │ uint200 h = uint200(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -124,7 +124,7 @@ LL │ uint192 i = uint192(h); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -138,7 +138,7 @@ LL │ uint184 j = uint184(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -152,7 +152,7 @@ LL │ uint176 k = uint176(j); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -166,7 +166,7 @@ LL │ uint168 l = uint168(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -180,7 +180,7 @@ LL │ uint160 m = uint160(l); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -194,7 +194,7 @@ LL │ uint152 n = uint152(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -208,7 +208,7 @@ LL │ uint144 o = uint144(n); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -222,7 +222,7 @@ LL │ uint136 p = uint136(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -236,7 +236,7 @@ LL │ uint128 q = uint128(p); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -250,7 +250,7 @@ LL │ uint120 r = uint120(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -264,7 +264,7 @@ LL │ uint112 s = uint112(r); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -278,7 +278,7 @@ LL │ uint104 t = uint104(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -292,7 +292,7 @@ LL │ uint96 u = uint96(t); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -306,7 +306,7 @@ LL │ uint88 v = uint88(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -320,7 +320,7 @@ LL │ uint80 w = uint80(v); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -334,7 +334,7 @@ LL │ uint72 x = uint72(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -348,7 +348,7 @@ LL │ uint64 y = uint64(x); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -362,7 +362,7 @@ LL │ uint56 z = uint56(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -376,7 +376,7 @@ LL │ uint48 A = uint48(z); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -390,7 +390,7 @@ LL │ uint40 B = uint40(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -404,7 +404,7 @@ LL │ uint32 C = uint32(B); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -418,7 +418,7 @@ LL │ uint24 D = uint24(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -432,7 +432,7 @@ LL │ uint16 E = uint16(D); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -446,7 +446,7 @@ LL │ uint8 F = uint8(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -460,7 +460,7 @@ LL │ int248 b = int248(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -474,7 +474,7 @@ LL │ int240 c = int240(b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -488,7 +488,7 @@ LL │ int232 d = int232(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -502,7 +502,7 @@ LL │ int224 e = int224(d); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -516,7 +516,7 @@ LL │ int216 f = int216(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -530,7 +530,7 @@ LL │ int208 g = int208(f); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -544,7 +544,7 @@ LL │ int200 h = int200(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -558,7 +558,7 @@ LL │ int192 i = int192(h); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -572,7 +572,7 @@ LL │ int184 j = int184(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -586,7 +586,7 @@ LL │ int176 k = int176(j); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -600,7 +600,7 @@ LL │ int168 l = int168(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -614,7 +614,7 @@ LL │ int160 m = int160(l); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -628,7 +628,7 @@ LL │ int152 n = int152(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -642,7 +642,7 @@ LL │ int144 o = int144(n); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -656,7 +656,7 @@ LL │ int136 p = int136(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -670,7 +670,7 @@ LL │ int128 q = int128(p); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -684,7 +684,7 @@ LL │ int120 r = int120(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -698,7 +698,7 @@ LL │ int112 s = int112(r); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -712,7 +712,7 @@ LL │ int104 t = int104(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -726,7 +726,7 @@ LL │ int96 u = int96(t); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -740,7 +740,7 @@ LL │ int88 v = int88(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -754,7 +754,7 @@ LL │ int80 w = int80(v); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -768,7 +768,7 @@ LL │ int72 x = int72(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -782,7 +782,7 @@ LL │ int64 y = int64(x); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -796,7 +796,7 @@ LL │ int56 z = int56(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -810,7 +810,7 @@ LL │ int48 A = int48(z); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -824,7 +824,7 @@ LL │ int40 B = int40(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -838,7 +838,7 @@ LL │ int32 C = int32(B); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -852,7 +852,7 @@ LL │ int24 D = int24(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -866,7 +866,7 @@ LL │ int16 E = int16(D); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -880,7 +880,7 @@ LL │ int8 F = int8(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -894,7 +894,7 @@ LL │ bytes31 b = bytes31(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -908,7 +908,7 @@ LL │ bytes30 c = bytes30(b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -922,7 +922,7 @@ LL │ bytes29 d = bytes29(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -936,7 +936,7 @@ LL │ bytes28 e = bytes28(d); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -950,7 +950,7 @@ LL │ bytes27 f = bytes27(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -964,7 +964,7 @@ LL │ bytes26 g = bytes26(f); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -978,7 +978,7 @@ LL │ bytes25 h = bytes25(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -992,7 +992,7 @@ LL │ bytes24 i = bytes24(h); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1006,7 +1006,7 @@ LL │ bytes23 j = bytes23(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1020,7 +1020,7 @@ LL │ bytes22 k = bytes22(j); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1034,7 +1034,7 @@ LL │ bytes21 l = bytes21(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1048,7 +1048,7 @@ LL │ bytes20 m = bytes20(l); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1062,7 +1062,7 @@ LL │ bytes19 n = bytes19(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1076,7 +1076,7 @@ LL │ bytes18 o = bytes18(n); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1090,7 +1090,7 @@ LL │ bytes17 p = bytes17(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1104,7 +1104,7 @@ LL │ bytes16 q = bytes16(p); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1118,7 +1118,7 @@ LL │ bytes15 r = bytes15(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1132,7 +1132,7 @@ LL │ bytes14 s = bytes14(r); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1146,7 +1146,7 @@ LL │ bytes13 t = bytes13(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1160,7 +1160,7 @@ LL │ bytes12 u = bytes12(t); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1174,7 +1174,7 @@ LL │ bytes11 v = bytes11(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1188,7 +1188,7 @@ LL │ bytes10 w = bytes10(v); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1202,7 +1202,7 @@ LL │ bytes9 x = bytes9(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1216,7 +1216,7 @@ LL │ bytes8 y = bytes8(x); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1230,7 +1230,7 @@ LL │ bytes7 z = bytes7(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1244,7 +1244,7 @@ LL │ bytes6 A = bytes6(z); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1258,7 +1258,7 @@ LL │ bytes5 B = bytes5(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1272,7 +1272,7 @@ LL │ bytes4 C = bytes4(B); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1286,7 +1286,7 @@ LL │ bytes3 D = bytes3(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1300,7 +1300,7 @@ LL │ bytes2 E = bytes2(D); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1314,7 +1314,7 @@ LL │ bytes1 F = bytes1(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1328,7 +1328,7 @@ LL │ int256 b = int256(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1342,7 +1342,7 @@ LL │ int248 d = int248(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1356,7 +1356,7 @@ LL │ int240 f = int240(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1370,7 +1370,7 @@ LL │ int232 h = int232(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1384,7 +1384,7 @@ LL │ int224 j = int224(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1398,7 +1398,7 @@ LL │ int216 l = int216(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1412,7 +1412,7 @@ LL │ int208 n = int208(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1426,7 +1426,7 @@ LL │ int200 p = int200(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1440,7 +1440,7 @@ LL │ int192 r = int192(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1454,7 +1454,7 @@ LL │ int184 t = int184(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1468,7 +1468,7 @@ LL │ int176 v = int176(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1482,7 +1482,7 @@ LL │ int168 x = int168(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1496,7 +1496,7 @@ LL │ int160 z = int160(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1510,7 +1510,7 @@ LL │ int152 B = int152(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1524,7 +1524,7 @@ LL │ int144 D = int144(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1538,7 +1538,7 @@ LL │ int136 F = int136(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1552,7 +1552,7 @@ LL │ int128 H = int128(G); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1566,7 +1566,7 @@ LL │ int120 J = int120(I); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1580,7 +1580,7 @@ LL │ int112 L = int112(K); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1594,7 +1594,7 @@ LL │ int104 N = int104(M); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1608,7 +1608,7 @@ LL │ int96 P = int96(O); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1622,7 +1622,7 @@ LL │ int88 R = int88(Q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1636,7 +1636,7 @@ LL │ int80 T = int80(S); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1650,7 +1650,7 @@ LL │ int72 V = int72(U); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1664,7 +1664,7 @@ LL │ int64 X = int64(W); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1678,7 +1678,7 @@ LL │ int56 Z = int56(Y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1692,7 +1692,7 @@ LL │ int48 BB = int48(AA); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1706,7 +1706,7 @@ LL │ int40 DD = int40(CC); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1720,7 +1720,7 @@ LL │ int32 FF = int32(EE); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1734,7 +1734,7 @@ LL │ int24 HH = int24(GG); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1748,7 +1748,7 @@ LL │ int16 JJ = int16(II); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1762,7 +1762,7 @@ LL │ int8 LL = int8(KK); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1776,7 +1776,7 @@ LL │ uint256 b = uint256(a); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1790,7 +1790,7 @@ LL │ uint248 d = uint248(c); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1804,7 +1804,7 @@ LL │ uint240 f = uint240(e); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1818,7 +1818,7 @@ LL │ uint232 h = uint232(g); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1832,7 +1832,7 @@ LL │ uint224 j = uint224(i); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1846,7 +1846,7 @@ LL │ uint216 l = uint216(k); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1860,7 +1860,7 @@ LL │ uint208 n = uint208(m); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1874,7 +1874,7 @@ LL │ uint200 p = uint200(o); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1888,7 +1888,7 @@ LL │ uint192 r = uint192(q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1902,7 +1902,7 @@ LL │ uint184 t = uint184(s); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1916,7 +1916,7 @@ LL │ uint176 v = uint176(u); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1930,7 +1930,7 @@ LL │ uint168 x = uint168(w); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1944,7 +1944,7 @@ LL │ uint160 z = uint160(y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1958,7 +1958,7 @@ LL │ uint152 B = uint152(A); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1972,7 +1972,7 @@ LL │ uint144 D = uint144(C); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -1986,7 +1986,7 @@ LL │ uint136 F = uint136(E); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2000,7 +2000,7 @@ LL │ uint128 H = uint128(G); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2014,7 +2014,7 @@ LL │ uint120 J = uint120(I); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2028,7 +2028,7 @@ LL │ uint112 L = uint112(K); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2042,7 +2042,7 @@ LL │ uint104 N = uint104(M); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2056,7 +2056,7 @@ LL │ uint96 P = uint96(O); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2070,7 +2070,7 @@ LL │ uint88 R = uint88(Q); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2084,7 +2084,7 @@ LL │ uint80 T = uint80(S); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2098,7 +2098,7 @@ LL │ uint72 V = uint72(U); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2112,7 +2112,7 @@ LL │ uint64 X = uint64(W); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2126,7 +2126,7 @@ LL │ uint56 Z = uint56(Y); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2140,7 +2140,7 @@ LL │ uint48 BB = uint48(AA); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2154,7 +2154,7 @@ LL │ uint40 DD = uint40(CC); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2168,7 +2168,7 @@ LL │ uint32 FF = uint32(EE); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2182,7 +2182,7 @@ LL │ uint24 HH = uint24(GG); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2196,7 +2196,7 @@ LL │ uint16 JJ = uint16(II); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2210,7 +2210,7 @@ LL │ uint8 LL = uint8(KK); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2224,7 +2224,7 @@ LL │ bytes32 dataSlice = bytes32(data); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2238,7 +2238,7 @@ LL │ bytes32 strSlice = bytes32(bytes(str)); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2252,7 +2252,7 @@ LL │ uint128 aPlusB = uint128(int128(uint128(a)) + b); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2266,7 +2266,7 @@ LL │ uint64 unsafe = uint64(aPlusB); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2280,7 +2280,7 @@ LL │ return uint64(uint128(int128(uint128(a)) + b)); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast warning[unsafe-typecast]: typecasts that can truncate values should be checked ╭▸ ROOT/testdata/UnsafeTypecast.sol:LL:CC @@ -2294,5 +2294,5 @@ LL │ return uint64(uint128(int128(uint128(a)) + b)); │ // forge-lint: disable-next-line(unsafe-typecast) │ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast + ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast diff --git a/crates/lint/testdata/UnusedStateVariables.sol b/crates/lint/testdata/UnusedStateVariables.sol new file mode 100644 index 0000000000000..f68650d2a8d01 --- /dev/null +++ b/crates/lint/testdata/UnusedStateVariables.sol @@ -0,0 +1,52 @@ +//@compile-flags: --severity gas + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +contract UnusedVars { + uint256 unused; //~NOTE: state variable is never used + uint256 usedInRead; + uint256 usedInWrite; + address usedInBoth; + uint256 constant CONST = 1; // skip constant + uint256 immutable IMMUT; // skip immutable + + constructor() { + usedInBoth = msg.sender; + } + + function read() external view returns (uint256) { + return usedInRead; + } + + function write(uint256 v) external { + usedInWrite = v; + } + + function both() external view returns (address) { + return usedInBoth; + } +} + +// State variables used only as modifier call arguments must not be flagged. +contract UsedInModifierArg { + uint256 limit; + uint256 unused; //~NOTE: state variable is never used + + modifier limitedBy(uint256 max) { + require(msg.value <= max); + _; + } + + function foo() external payable limitedBy(limit) {} +} + +contract MultiUnused { + uint256 firstUnused; //~NOTE: state variable is never used + uint256 secondUnused; //~NOTE: state variable is never used + uint256 usedVar; + + function use() external view returns (uint256) { + return usedVar; + } +} diff --git a/crates/lint/testdata/UnusedStateVariables.stderr b/crates/lint/testdata/UnusedStateVariables.stderr new file mode 100644 index 0000000000000..92a0082bb8293 --- /dev/null +++ b/crates/lint/testdata/UnusedStateVariables.stderr @@ -0,0 +1,40 @@ +note[could-be-immutable]: state variable could be declared immutable + ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC + │ +LL │ address usedInBoth; + │ ━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/could-be-immutable + +note[unused-state-variables]: state variable is never used + ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC + │ +LL │ uint256 unused; + │ ━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables + +note[unused-state-variables]: state variable is never used + ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC + │ +LL │ uint256 unused; + │ ━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables + +note[unused-state-variables]: state variable is never used + ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC + │ +LL │ uint256 firstUnused; + │ ━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables + +note[unused-state-variables]: state variable is never used + ╭▸ ROOT/testdata/UnusedStateVariables.sol:LL:CC + │ +LL │ uint256 secondUnused; + │ ━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: https://getfoundry.sh/forge/linting/unused-state-variables + diff --git a/crates/lint/testdata/UnwrappedModifierLogic.stderr b/crates/lint/testdata/UnwrappedModifierLogic.stderr index 5e1bf754e60e4..dc5514c2d5e98 100644 --- a/crates/lint/testdata/UnwrappedModifierLogic.stderr +++ b/crates/lint/testdata/UnwrappedModifierLogic.stderr @@ -9,7 +9,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier multipleBeforePlaceholder() { @@ -35,7 +35,7 @@ LL │ ┃ checkInternal(msg.sender); LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier multipleAfterPlaceholder() { @@ -62,7 +62,7 @@ LL │ ┃ checkPublic(sender); LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier multipleBeforeAfterPlaceholder(address sender) { @@ -91,7 +91,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyOwner() { @@ -113,7 +113,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyRole(bytes32 role) { @@ -135,7 +135,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyRoleOrOpenRole(bytes32 role) { @@ -157,7 +157,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyRoleOrAdmin(bytes32 role, address admin) { @@ -180,7 +180,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier assign(address sender) { @@ -204,7 +204,7 @@ LL │ ┃ sender; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier uncheckedBlock(address sender) { @@ -228,7 +228,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier emitEvent(address sender) { @@ -250,7 +250,7 @@ LL │ ┃ _; LL │ ┃ } │ ┗━━━━━┛ │ - ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#unwrapped-modifier-logic + ╰ help: https://getfoundry.sh/forge/linting/unwrapped-modifier-logic help: wrap modifier logic to reduce code size ╭╴ LL ± modifier onlyOwnerContract(address sender) { diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 9970616900e6b..15363a6a40bb0 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -23,10 +23,10 @@ alloy-rpc-types-eth.workspace = true alloy-serde.workspace = true alloy-signer.workspace = true alloy-evm.workspace = true -op-alloy-consensus = { workspace = true, features = ["serde", "alloy-compat"] } -op-alloy-rpc-types.workspace = true -alloy-op-evm.workspace = true -op-revm.workspace = true +op-alloy-consensus = { workspace = true, features = ["serde", "alloy-compat"], optional = true } +op-alloy-rpc-types = { workspace = true, optional = true } +alloy-op-evm = { workspace = true, optional = true } +op-revm = { workspace = true, optional = true } revm.workspace = true serde_json.workspace = true serde = { version = "1.0", features = ["derive"] } @@ -34,3 +34,12 @@ derive_more.workspace = true tempo-primitives.workspace = true tempo-alloy.workspace = true tempo-revm.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "dep:op-alloy-consensus", + "dep:op-alloy-rpc-types", + "dep:alloy-op-evm", + "dep:op-revm", +] diff --git a/crates/primitives/src/network/mod.rs b/crates/primitives/src/network/mod.rs index 7000b580b6a73..0b840185b0383 100644 --- a/crates/primitives/src/network/mod.rs +++ b/crates/primitives/src/network/mod.rs @@ -1,12 +1,20 @@ use alloy_network::Network; +#[cfg(feature = "optimism")] +mod optimism; mod receipt; use alloy_provider::fillers::{ BlobGasFiller, ChainIdFiller, GasFiller, JoinFill, NonceFiller, RecommendedFillers, }; +#[cfg(feature = "optimism")] +pub use optimism::FoundryTransactionResponse; pub use receipt::*; +/// Default JSON-RPC transaction response when the `optimism` feature is disabled. +#[cfg(not(feature = "optimism"))] +pub type FoundryTransactionResponse = alloy_rpc_types_eth::Transaction; + /// Foundry network type. /// /// This network type supports Foundry-specific transaction types, including @@ -36,7 +44,7 @@ impl Network for FoundryNetwork { type TransactionRequest = crate::FoundryTransactionRequest; - type TransactionResponse = op_alloy_rpc_types::Transaction; + type TransactionResponse = FoundryTransactionResponse; type ReceiptResponse = crate::FoundryTxReceipt; diff --git a/crates/primitives/src/network/optimism.rs b/crates/primitives/src/network/optimism.rs new file mode 100644 index 0000000000000..aff30a755663f --- /dev/null +++ b/crates/primitives/src/network/optimism.rs @@ -0,0 +1,47 @@ +//! OP-stack-specific helpers and type aliases used by [`super::FoundryNetwork`] and +//! [`super::FoundryTxReceipt`]. + +use alloy_consensus::{Receipt, ReceiptWithBloom, TxReceipt}; +use alloy_primitives::U64; +use alloy_rpc_types::Log; +use alloy_serde::OtherFields; +use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom}; + +use crate::FoundryReceiptEnvelope; + +/// JSON-RPC transaction response type used by [`super::FoundryNetwork`]. +pub type FoundryTransactionResponse = op_alloy_rpc_types::Transaction; + +/// Build a [`FoundryReceiptEnvelope::Deposit`] from a `ReceiptWithBloom` plus the OP +/// deposit-specific fields decoded from the [`OtherFields`] of an `AnyTransactionReceipt`. +pub(super) fn build_deposit_receipt_envelope( + receipt_with_bloom: ReceiptWithBloom>, + other: &OtherFields, +) -> FoundryReceiptEnvelope { + // These fields may not be present in all receipts, so missing/invalid values are None. + let deposit_nonce = other + .get_deserialized::("depositNonce") + .transpose() + .ok() + .flatten() + .map(|v| v.to::()); + let deposit_receipt_version = other + .get_deserialized::("depositReceiptVersion") + .transpose() + .ok() + .flatten() + .map(|v| v.to::()); + + FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom { + receipt: OpDepositReceipt { + inner: Receipt { + status: alloy_consensus::Eip658Value::Eip658(receipt_with_bloom.status()), + cumulative_gas_used: receipt_with_bloom.cumulative_gas_used(), + logs: receipt_with_bloom.receipt.logs, + }, + deposit_nonce, + deposit_receipt_version, + }, + logs_bloom: receipt_with_bloom.logs_bloom, + }) +} diff --git a/crates/primitives/src/network/receipt.rs b/crates/primitives/src/network/receipt.rs index b727b4c39b345..6b01f9eaa9ee9 100644 --- a/crates/primitives/src/network/receipt.rs +++ b/crates/primitives/src/network/receipt.rs @@ -1,13 +1,13 @@ -use alloy_consensus::{Receipt, TxReceipt}; use alloy_network::{AnyReceiptEnvelope, AnyTransactionReceipt, ReceiptResponse}; -use alloy_primitives::{Address, B256, BlockHash, TxHash, U64}; +use alloy_primitives::{Address, B256, BlockHash, TxHash}; use alloy_rpc_types::{ConversionError, Log, TransactionReceipt}; use alloy_serde::WithOtherFields; use derive_more::AsRef; -use op_alloy_consensus::{OpDepositReceipt, OpDepositReceiptWithBloom}; use serde::{Deserialize, Serialize}; use tempo_primitives::TEMPO_TX_TYPE_ID; +#[cfg(feature = "optimism")] +use super::optimism::build_deposit_receipt_envelope; use crate::FoundryReceiptEnvelope; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, AsRef)] @@ -144,38 +144,8 @@ impl TryFrom for FoundryTxReceipt { 0x03 => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom), 0x04 => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom), TEMPO_TX_TYPE_ID => FoundryReceiptEnvelope::Tempo(receipt_with_bloom), - 0x7E => { - // Construct the deposit receipt, extracting optional deposit fields - // These fields may not be present in all receipts, so missing/invalid - // values are None - let deposit_nonce = other - .get_deserialized::("depositNonce") - .transpose() - .ok() - .flatten() - .map(|v| v.to::()); - let deposit_receipt_version = other - .get_deserialized::("depositReceiptVersion") - .transpose() - .ok() - .flatten() - .map(|v| v.to::()); - - FoundryReceiptEnvelope::Deposit(OpDepositReceiptWithBloom { - receipt: OpDepositReceipt { - inner: Receipt { - status: alloy_consensus::Eip658Value::Eip658( - receipt_with_bloom.status(), - ), - cumulative_gas_used: receipt_with_bloom.cumulative_gas_used(), - logs: receipt_with_bloom.receipt.logs, - }, - deposit_nonce, - deposit_receipt_version, - }, - logs_bloom: receipt_with_bloom.logs_bloom, - }) - } + #[cfg(feature = "optimism")] + 0x7E => build_deposit_receipt_envelope(receipt_with_bloom, &other), _ => { let tx_type = r#type; return Err(ConversionError::Custom(format!( diff --git a/crates/primitives/src/transaction/envelope.rs b/crates/primitives/src/transaction/envelope.rs index ab4aafcc294c0..0a009a1931f06 100644 --- a/crates/primitives/src/transaction/envelope.rs +++ b/crates/primitives/src/transaction/envelope.rs @@ -1,6 +1,7 @@ +#[cfg(feature = "optimism")] +use alloy_consensus::{Sealed, Transaction as _}; use alloy_consensus::{ - Sealed, Signed, Transaction as _, TransactionEnvelope, TxEip1559, TxEip2930, TxEnvelope, - TxLegacy, TxType, Typed2718, + Signed, TransactionEnvelope, TxEip1559, TxEip2930, TxEnvelope, TxLegacy, TxType, Typed2718, crypto::RecoveryError, transaction::{ SignerRecoverable, TxEip7702, TxHashRef, @@ -9,14 +10,10 @@ use alloy_consensus::{ }; use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse}; -use alloy_op_evm::OpTx; use alloy_primitives::{Address, B256, Bytes, TxHash}; use alloy_rpc_types::ConversionError; -use op_alloy_consensus::{ - DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait, POST_EXEC_TX_TYPE_ID, TxDeposit, - TxPostExec, -}; -use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; +#[cfg(feature = "optimism")] +use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, POST_EXEC_TX_TYPE_ID, TxDeposit, TxPostExec}; use revm::context::TxEnv; use tempo_primitives::{AASigned, TempoTransaction}; use tempo_revm::TempoTxEnv; @@ -57,9 +54,11 @@ pub enum FoundryTxEnvelope { /// OP stack deposit transaction. /// /// See . + #[cfg(feature = "optimism")] #[envelope(ty = 126)] Deposit(Sealed), /// OP stack post-execution synthetic transaction. + #[cfg(feature = "optimism")] #[envelope(ty = 0x7D)] PostExec(Sealed), /// Tempo transaction type. @@ -80,7 +79,9 @@ impl FoundryTxEnvelope { Self::Eip1559(tx) => Ok(TxEnvelope::Eip1559(tx)), Self::Eip4844(tx) => Ok(TxEnvelope::Eip4844(tx)), Self::Eip7702(tx) => Ok(TxEnvelope::Eip7702(tx)), + #[cfg(feature = "optimism")] Self::Deposit(_) => Err(self), + #[cfg(feature = "optimism")] Self::PostExec(_) => Err(self), Self::Tempo(_) => Err(self), } @@ -109,7 +110,9 @@ impl FoundryTxEnvelope { Self::Eip1559(t) => *t.hash(), Self::Eip4844(t) => *t.hash(), Self::Eip7702(t) => *t.hash(), + #[cfg(feature = "optimism")] Self::Deposit(t) => t.tx_hash(), + #[cfg(feature = "optimism")] Self::PostExec(t) => t.tx_hash(), Self::Tempo(t) => *t.hash(), } @@ -128,7 +131,9 @@ impl FoundryTxEnvelope { Self::Eip1559(tx) => tx.recover_signer()?, Self::Eip4844(tx) => tx.recover_signer()?, Self::Eip7702(tx) => tx.recover_signer()?, + #[cfg(feature = "optimism")] Self::Deposit(tx) => tx.from, + #[cfg(feature = "optimism")] Self::PostExec(tx) => tx.inner().signer_address(), Self::Tempo(tx) => tx.signature().recover_signer(&tx.signature_hash())?, }) @@ -143,7 +148,9 @@ impl TxHashRef for FoundryTxEnvelope { Self::Eip1559(t) => t.hash(), Self::Eip4844(t) => t.hash(), Self::Eip7702(t) => t.hash(), + #[cfg(feature = "optimism")] Self::Deposit(t) => t.hash_ref(), + #[cfg(feature = "optimism")] Self::PostExec(t) => t.hash_ref(), Self::Tempo(t) => t.hash(), } @@ -160,23 +167,6 @@ impl SignerRecoverable for FoundryTxEnvelope { } } -impl OpTransactionTrait for FoundryTxEnvelope { - fn is_deposit(&self) -> bool { - matches!(self, Self::Deposit(_)) - } - - fn as_deposit(&self) -> Option<&Sealed> { - match self { - Self::Deposit(tx) => Some(tx), - _ => None, - } - } - - fn as_post_exec(&self) -> Option<&Sealed> { - if let Self::PostExec(tx) = self { Some(tx) } else { None } - } -} - impl TryFrom for TxEnvelope { type Error = FoundryTxEnvelope; @@ -197,19 +187,6 @@ impl From for FoundryTxEnvelope { } } -impl From for FoundryTxEnvelope { - fn from(tx: op_alloy_consensus::OpTxEnvelope) -> Self { - match tx { - op_alloy_consensus::OpTxEnvelope::Legacy(tx) => Self::Legacy(tx), - op_alloy_consensus::OpTxEnvelope::Eip2930(tx) => Self::Eip2930(tx), - op_alloy_consensus::OpTxEnvelope::Eip1559(tx) => Self::Eip1559(tx), - op_alloy_consensus::OpTxEnvelope::Eip7702(tx) => Self::Eip7702(tx), - op_alloy_consensus::OpTxEnvelope::Deposit(tx) => Self::Deposit(tx), - op_alloy_consensus::OpTxEnvelope::PostExec(tx) => Self::PostExec(tx), - } - } -} - impl From for FoundryTxEnvelope { fn from(tx: tempo_primitives::TempoTxEnvelope) -> Self { match tx { @@ -236,33 +213,50 @@ impl TryFrom for FoundryTxEnvelope { TxEnvelope::Eip4844(tx) => Ok(Self::Eip4844(tx)), TxEnvelope::Eip7702(tx) => Ok(Self::Eip7702(tx)), }, - AnyTxEnvelope::Unknown(mut tx) => { - // Try to convert to deposit transaction - if tx.ty() == DEPOSIT_TX_TYPE_ID { - tx.inner.fields.insert("from".to_string(), serde_json::to_value(from).unwrap()); - let deposit_tx = - tx.inner.fields.deserialize_into::().map_err(|e| { - ConversionError::Custom(format!( - "Failed to deserialize deposit tx: {e}" - )) - })?; - - return Ok(Self::Deposit(Sealed::new(deposit_tx))); + AnyTxEnvelope::Unknown(tx) => { + #[cfg(feature = "optimism")] + { + let mut tx = tx; + let _ = from; + // Try to convert to deposit transaction + if tx.ty() == DEPOSIT_TX_TYPE_ID { + tx.inner + .fields + .insert("from".to_string(), serde_json::to_value(from).unwrap()); + let deposit_tx = + tx.inner.fields.deserialize_into::().map_err(|e| { + ConversionError::Custom(format!( + "Failed to deserialize deposit tx: {e}" + )) + })?; + + return Ok(Self::Deposit(Sealed::new(deposit_tx))); + } + + if tx.ty() == POST_EXEC_TX_TYPE_ID { + let post_exec_tx = + tx.inner.fields.deserialize_into::().map_err(|e| { + ConversionError::Custom(format!( + "Failed to deserialize post-exec tx: {e}" + )) + })?; + + return Ok(Self::PostExec(Sealed::new(post_exec_tx))); + } + + let tx_type = tx.ty(); + Err(ConversionError::Custom(format!( + "Unknown transaction type: 0x{tx_type:02X}" + ))) } - - if tx.ty() == POST_EXEC_TX_TYPE_ID { - let post_exec_tx = - tx.inner.fields.deserialize_into::().map_err(|e| { - ConversionError::Custom(format!( - "Failed to deserialize post-exec tx: {e}" - )) - })?; - - return Ok(Self::PostExec(Sealed::new(post_exec_tx))); + #[cfg(not(feature = "optimism"))] + { + let _ = from; + let tx_type = tx.ty(); + Err(ConversionError::Custom(format!( + "Unknown transaction type: 0x{tx_type:02X}" + ))) } - - let tx_type = tx.ty(); - Err(ConversionError::Custom(format!("Unknown transaction type: 0x{tx_type:02X}"))) } } } @@ -276,6 +270,7 @@ impl FromRecoveredTx for TxEnv { FoundryTxEnvelope::Eip1559(signed_tx) => Self::from_recovered_tx(signed_tx, caller), FoundryTxEnvelope::Eip4844(signed_tx) => Self::from_recovered_tx(signed_tx, caller), FoundryTxEnvelope::Eip7702(signed_tx) => Self::from_recovered_tx(signed_tx, caller), + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(sealed_tx) => { let tx = sealed_tx.inner(); Self { @@ -288,6 +283,7 @@ impl FromRecoveredTx for TxEnv { ..Default::default() } } + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(sealed_tx) => { let tx = sealed_tx.inner(); Self { @@ -303,63 +299,6 @@ impl FromRecoveredTx for TxEnv { } } -impl FromRecoveredTx for OpTransaction { - fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { - match tx { - FoundryTxEnvelope::Legacy(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip2930(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip1559(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip4844(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Eip7702(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Deposit(sealed_tx) => { - let deposit_tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: deposit_tx.ty(), - caller, - gas_limit: deposit_tx.gas_limit, - kind: deposit_tx.to, - value: deposit_tx.value, - data: deposit_tx.input.clone(), - ..Default::default() - }; - let deposit = DepositTransactionParts { - source_hash: deposit_tx.source_hash, - mint: Some(deposit_tx.mint), - is_system_transaction: deposit_tx.is_system_transaction, - }; - Self { base, enveloped_tx: None, deposit } - } - FoundryTxEnvelope::PostExec(sealed_tx) => { - let tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: tx.ty(), - caller, - kind: tx.kind(), - data: tx.input.clone(), - ..Default::default() - }; - Self { base, enveloped_tx: None, deposit: Default::default() } - } - FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), - } - } -} - impl FromTxWithEncoded for TxEnv { fn from_encoded_tx(tx: &FoundryTxEnvelope, sender: Address, _encoded: Bytes) -> Self { Self::from_recovered_tx(tx, sender) @@ -384,7 +323,9 @@ impl FromRecoveredTx for TempoTxEnv { FoundryTxEnvelope::Eip7702(signed_tx) => { Self::from(TxEnv::from_recovered_tx(signed_tx, caller)) } + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(_) => unreachable!("Deposit tx in Tempo context"), + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(_) => unreachable!("Post-exec tx in Tempo context"), FoundryTxEnvelope::Tempo(aa_signed) => Self::from_recovered_tx(aa_signed, caller), } @@ -397,75 +338,6 @@ impl FromTxWithEncoded for TempoTxEnv { } } -impl FromRecoveredTx for OpTx { - fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { - Self(OpTransaction::::from_recovered_tx(tx, caller)) - } -} - -impl FromTxWithEncoded for OpTx { - fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { - Self(OpTransaction::::from_encoded_tx(tx, caller, encoded)) - } -} - -impl FromTxWithEncoded for OpTransaction { - fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { - match tx { - FoundryTxEnvelope::Legacy(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip2930(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip1559(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip4844(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Eip7702(signed_tx) => { - let base = TxEnv::from_recovered_tx(signed_tx, caller); - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Deposit(sealed_tx) => { - let deposit_tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: deposit_tx.ty(), - caller, - gas_limit: deposit_tx.gas_limit, - kind: deposit_tx.to, - value: deposit_tx.value, - data: deposit_tx.input.clone(), - ..Default::default() - }; - let deposit = DepositTransactionParts { - source_hash: deposit_tx.source_hash, - mint: Some(deposit_tx.mint), - is_system_transaction: deposit_tx.is_system_transaction, - }; - Self { base, enveloped_tx: Some(encoded), deposit } - } - FoundryTxEnvelope::PostExec(sealed_tx) => { - let tx = sealed_tx.inner(); - let base = TxEnv { - tx_type: tx.ty(), - caller, - kind: tx.kind(), - data: tx.input.clone(), - ..Default::default() - }; - Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } - } - FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), - } - } -} - impl std::fmt::Display for FoundryTxType { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -474,7 +346,9 @@ impl std::fmt::Display for FoundryTxType { Self::Eip1559 => write!(f, "eip1559"), Self::Eip4844 => write!(f, "eip4844"), Self::Eip7702 => write!(f, "eip7702"), + #[cfg(feature = "optimism")] Self::Deposit => write!(f, "deposit"), + #[cfg(feature = "optimism")] Self::PostExec => write!(f, "post-exec"), Self::Tempo => write!(f, "tempo"), } @@ -501,7 +375,9 @@ impl From for FoundryTypedTx { FoundryTxEnvelope::Eip1559(signed_tx) => Self::Eip1559(signed_tx.strip_signature()), FoundryTxEnvelope::Eip4844(signed_tx) => Self::Eip4844(signed_tx.strip_signature()), FoundryTxEnvelope::Eip7702(signed_tx) => Self::Eip7702(signed_tx.strip_signature()), + #[cfg(feature = "optimism")] FoundryTxEnvelope::Deposit(sealed_tx) => Self::Deposit(sealed_tx.into_inner()), + #[cfg(feature = "optimism")] FoundryTxEnvelope::PostExec(sealed_tx) => Self::PostExec(sealed_tx.into_inner()), FoundryTxEnvelope::Tempo(signed_tx) => Self::Tempo(signed_tx.strip_signature()), } @@ -609,28 +485,6 @@ mod tests { assert_eq!(from, address!("0xA83C816D4f9b2783761a22BA6FADB0eB0606D7B2")); } - #[test] - fn test_decode_encode_deposit_tx() { - // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 - let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" - .parse::() - .unwrap(); - - // https://sepolia-optimism.etherscan.io/getRawTx?tx=0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 - let raw_tx = alloy_primitives::hex::decode( - "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", - ) - .unwrap(); - let dep_tx = FoundryTxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); - - let mut encoded = Vec::new(); - dep_tx.encode_2718(&mut encoded); - - assert_eq!(raw_tx, encoded); - - assert_eq!(tx_hash, dep_tx.hash()); - } - #[test] fn can_recover_sender_not_normalized() { let bytes = hex::decode("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); @@ -707,11 +561,6 @@ mod tests { assert_eq!(tx_env.caller, sender); assert_eq!(tx_env.gas_limit, 0x5208); assert_eq!(tx_env.gas_price, 1); - - // Test OpTransaction conversion via FromRecoveredTx trait - let op_tx = OpTransaction::::from_recovered_tx(&typed_tx, sender); - assert_eq!(op_tx.base.caller, sender); - assert_eq!(op_tx.base.gas_limit, 0x5208); } // Test vector from Tempo testnet: diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 7dccf3e30752f..18f39c437bfbc 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,7 +1,11 @@ mod envelope; +#[cfg(feature = "optimism")] +mod optimism; mod receipt; mod request; pub use envelope::{FoundryTxEnvelope, FoundryTxType, FoundryTypedTx}; +#[cfg(feature = "optimism")] +pub use optimism::get_deposit_tx_parts; pub use receipt::FoundryReceiptEnvelope; -pub use request::{FoundryTransactionRequest, get_deposit_tx_parts}; +pub use request::FoundryTransactionRequest; diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs new file mode 100644 index 0000000000000..5952b5d6a9020 --- /dev/null +++ b/crates/primitives/src/transaction/optimism.rs @@ -0,0 +1,300 @@ +//! OP-stack-specific impls for [`FoundryTxEnvelope`] and [`FoundryTransactionRequest`]. + +use alloy_consensus::{Sealed, Transaction as _, Typed2718}; +use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; +use alloy_op_evm::OpTx; +use alloy_primitives::{Address, B256, Bytes, U256}; +use alloy_serde::OtherFields; +use op_alloy_consensus::{ + OpDepositReceipt, OpDepositReceiptWithBloom, OpTransaction as OpTransactionTrait, OpTxEnvelope, + TxDeposit, TxPostExec, +}; +use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; +use revm::context::TxEnv; + +use super::{FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope}; + +impl OpTransactionTrait for FoundryTxEnvelope { + fn is_deposit(&self) -> bool { + matches!(self, Self::Deposit(_)) + } + + fn as_deposit(&self) -> Option<&Sealed> { + match self { + Self::Deposit(tx) => Some(tx), + _ => None, + } + } + + fn as_post_exec(&self) -> Option<&Sealed> { + if let Self::PostExec(tx) = self { Some(tx) } else { None } + } +} + +impl From for FoundryTxEnvelope { + fn from(tx: OpTxEnvelope) -> Self { + match tx { + OpTxEnvelope::Legacy(tx) => Self::Legacy(tx), + OpTxEnvelope::Eip2930(tx) => Self::Eip2930(tx), + OpTxEnvelope::Eip1559(tx) => Self::Eip1559(tx), + OpTxEnvelope::Eip7702(tx) => Self::Eip7702(tx), + OpTxEnvelope::Deposit(tx) => Self::Deposit(tx), + OpTxEnvelope::PostExec(tx) => Self::PostExec(tx), + } + } +} + +impl FromRecoveredTx for OpTransaction { + fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { + match tx { + FoundryTxEnvelope::Legacy(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip2930(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip1559(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip4844(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Eip7702(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Deposit(sealed_tx) => { + let deposit_tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: deposit_tx.ty(), + caller, + gas_limit: deposit_tx.gas_limit, + kind: deposit_tx.to, + value: deposit_tx.value, + data: deposit_tx.input.clone(), + ..Default::default() + }; + let deposit = DepositTransactionParts { + source_hash: deposit_tx.source_hash, + mint: Some(deposit_tx.mint), + is_system_transaction: deposit_tx.is_system_transaction, + }; + Self { base, enveloped_tx: None, deposit } + } + FoundryTxEnvelope::PostExec(sealed_tx) => { + let tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: tx.ty(), + caller, + kind: tx.kind(), + data: tx.input.clone(), + ..Default::default() + }; + Self { base, enveloped_tx: None, deposit: Default::default() } + } + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), + } + } +} + +impl FromRecoveredTx for OpTx { + fn from_recovered_tx(tx: &FoundryTxEnvelope, caller: Address) -> Self { + Self(OpTransaction::::from_recovered_tx(tx, caller)) + } +} + +impl FromTxWithEncoded for OpTx { + fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { + Self(OpTransaction::::from_encoded_tx(tx, caller, encoded)) + } +} + +impl FromTxWithEncoded for OpTransaction { + fn from_encoded_tx(tx: &FoundryTxEnvelope, caller: Address, encoded: Bytes) -> Self { + match tx { + FoundryTxEnvelope::Legacy(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip2930(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip1559(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip4844(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Eip7702(signed_tx) => { + let base = TxEnv::from_recovered_tx(signed_tx, caller); + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Deposit(sealed_tx) => { + let deposit_tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: deposit_tx.ty(), + caller, + gas_limit: deposit_tx.gas_limit, + kind: deposit_tx.to, + value: deposit_tx.value, + data: deposit_tx.input.clone(), + ..Default::default() + }; + let deposit = DepositTransactionParts { + source_hash: deposit_tx.source_hash, + mint: Some(deposit_tx.mint), + is_system_transaction: deposit_tx.is_system_transaction, + }; + Self { base, enveloped_tx: Some(encoded), deposit } + } + FoundryTxEnvelope::PostExec(sealed_tx) => { + let tx = sealed_tx.inner(); + let base = TxEnv { + tx_type: tx.ty(), + caller, + kind: tx.kind(), + data: tx.input.clone(), + ..Default::default() + }; + Self { base, enveloped_tx: Some(encoded), deposit: Default::default() } + } + FoundryTxEnvelope::Tempo(_) => unreachable!("Tempo tx in Optimism context"), + } + } +} + +impl From> for FoundryTransactionRequest { + fn from(tx: op_alloy_rpc_types::Transaction) -> Self { + tx.inner.into_inner().into() + } +} + +/// Converts `OtherFields` to `DepositTransactionParts`, produces error with missing fields. +pub fn get_deposit_tx_parts( + other: &OtherFields, +) -> Result> { + let mut missing = Vec::new(); + let source_hash = + other.get_deserialized::("sourceHash").transpose().ok().flatten().unwrap_or_else( + || { + missing.push("sourceHash"); + Default::default() + }, + ); + let mint = other + .get_deserialized::("mint") + .transpose() + .unwrap_or_else(|_| { + missing.push("mint"); + Default::default() + }) + .map(|value| value.to::()); + let is_system_transaction = + other.get_deserialized::("isSystemTx").transpose().ok().flatten().unwrap_or_else( + || { + missing.push("isSystemTx"); + Default::default() + }, + ); + if missing.is_empty() { + Ok(DepositTransactionParts { source_hash, mint, is_system_transaction }) + } else { + Err(missing) + } +} + +/// OP-stack-specific accessors on [`FoundryReceiptEnvelope`]. +impl FoundryReceiptEnvelope { + /// Return the receipt's deposit_nonce if it is a deposit receipt. + pub fn deposit_nonce(&self) -> Option { + self.as_deposit_receipt().and_then(|r| r.deposit_nonce) + } + + /// Return the receipt's deposit version if it is a deposit receipt. + pub fn deposit_receipt_version(&self) -> Option { + self.as_deposit_receipt().and_then(|r| r.deposit_receipt_version) + } + + /// Returns the deposit receipt if it is a deposit receipt. + pub const fn as_deposit_receipt_with_bloom(&self) -> Option<&OpDepositReceiptWithBloom> { + match self { + Self::Deposit(t) => Some(t), + _ => None, + } + } + + /// Returns the deposit receipt if it is a deposit receipt. + pub const fn as_deposit_receipt(&self) -> Option<&OpDepositReceipt> { + match self { + Self::Deposit(t) => Some(&t.receipt), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use alloy_network::eip2718::Encodable2718; + use alloy_primitives::TxHash; + use alloy_rlp::Decodable; + + use super::*; + + #[test] + fn test_from_recovered_tx_legacy_op() { + use alloy_consensus::transaction::SignerRecoverable; + + let tx = r#" + { + "type": "0x0", + "chainId": "0x1", + "nonce": "0x0", + "gas": "0x5208", + "gasPrice": "0x1", + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "value": "0x1", + "input": "0x", + "r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0", + "s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd", + "v": "0x1b", + "hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515" + }"#; + + let typed_tx: FoundryTxEnvelope = serde_json::from_str(tx).unwrap(); + let sender = typed_tx.recover_signer().unwrap(); + + // Test OpTransaction conversion via FromRecoveredTx trait + let op_tx = OpTransaction::::from_recovered_tx(&typed_tx, sender); + assert_eq!(op_tx.base.caller, sender); + assert_eq!(op_tx.base.gas_limit, 0x5208); + } + + #[test] + fn test_decode_encode_deposit_tx() { + // https://sepolia-optimism.etherscan.io/tx/0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let tx_hash: TxHash = "0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7" + .parse::() + .unwrap(); + + // https://sepolia-optimism.etherscan.io/getRawTx?tx=0xbf8b5f08c43e4b860715cd64fc0849bbce0d0ea20a76b269e7bc8886d112fca7 + let raw_tx = alloy_primitives::hex::decode( + "7ef861a0dfd7ae78bf3c414cfaa77f13c0205c82eb9365e217b2daa3448c3156b69b27ac94778f2146f48179643473b82931c4cd7b8f153efd94778f2146f48179643473b82931c4cd7b8f153efd872386f26fc10000872386f26fc10000830186a08080", + ) + .unwrap(); + let dep_tx = FoundryTxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); + + let mut encoded = Vec::new(); + dep_tx.encode_2718(&mut encoded); + + assert_eq!(raw_tx, encoded); + + assert_eq!(tx_hash, dep_tx.hash()); + } +} diff --git a/crates/primitives/src/transaction/receipt.rs b/crates/primitives/src/transaction/receipt.rs index fe209fc72c907..78bbcd1efa2fb 100644 --- a/crates/primitives/src/transaction/receipt.rs +++ b/crates/primitives/src/transaction/receipt.rs @@ -8,6 +8,7 @@ use alloy_network::eip2718::{ use alloy_primitives::{Bloom, Log, TxHash, logs_bloom}; use alloy_rlp::{BufMut, Decodable, Encodable, Header, bytes}; use alloy_rpc_types::{BlockNumHash, trace::otterscan::OtsReceipt}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{ DEPOSIT_TX_TYPE_ID, OpDepositReceipt, OpDepositReceiptWithBloom, POST_EXEC_TX_TYPE_ID, }; @@ -29,8 +30,10 @@ pub enum FoundryReceiptEnvelope { Eip4844(ReceiptWithBloom>), #[serde(rename = "0x4", alias = "0x04")] Eip7702(ReceiptWithBloom>), + #[cfg(feature = "optimism")] #[serde(rename = "0x7D", alias = "0x7d")] PostExec(ReceiptWithBloom>), + #[cfg(feature = "optimism")] #[serde(rename = "0x7E", alias = "0x7e")] Deposit(OpDepositReceiptWithBloom), #[serde(rename = "0x76")] @@ -44,7 +47,8 @@ impl FoundryReceiptEnvelope { cumulative_gas_used: u64, logs: impl IntoIterator, tx_type: FoundryTxType, - deposit_nonce: Option, + #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] deposit_nonce: Option, + #[cfg_attr(not(feature = "optimism"), allow(unused_variables))] deposit_receipt_version: Option, ) -> Self { let logs = logs.into_iter().collect::>(); @@ -67,9 +71,11 @@ impl FoundryReceiptEnvelope { FoundryTxType::Eip7702 => { Self::Eip7702(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) } + #[cfg(feature = "optimism")] FoundryTxType::PostExec => { Self::PostExec(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) } + #[cfg(feature = "optimism")] FoundryTxType::Deposit => { let inner = OpDepositReceiptWithBloom { receipt: OpDepositReceipt { @@ -112,13 +118,18 @@ impl FoundryReceiptEnvelope { removed: false, }) .collect::>(); + #[cfg(feature = "optimism")] + let (deposit_nonce, deposit_receipt_version) = + (self.deposit_nonce(), self.deposit_receipt_version()); + #[cfg(not(feature = "optimism"))] + let (deposit_nonce, deposit_receipt_version) = (None, None); FoundryReceiptEnvelope::::from_parts( self.status(), self.cumulative_gas_used(), logs, self.tx_type(), - self.deposit_nonce(), - self.deposit_receipt_version(), + deposit_nonce, + deposit_receipt_version, ) } } @@ -132,7 +143,9 @@ impl FoundryReceiptEnvelope { Self::Eip1559(_) => FoundryTxType::Eip1559, Self::Eip4844(_) => FoundryTxType::Eip4844, Self::Eip7702(_) => FoundryTxType::Eip7702, + #[cfg(feature = "optimism")] Self::PostExec(_) => FoundryTxType::PostExec, + #[cfg(feature = "optimism")] Self::Deposit(_) => FoundryTxType::Deposit, Self::Tempo(_) => FoundryTxType::Tempo, } @@ -158,8 +171,12 @@ impl FoundryReceiptEnvelope { Self::Eip1559(r) => FoundryReceiptEnvelope::Eip1559(r.map_logs(f)), Self::Eip4844(r) => FoundryReceiptEnvelope::Eip4844(r.map_logs(f)), Self::Eip7702(r) => FoundryReceiptEnvelope::Eip7702(r.map_logs(f)), + #[cfg(feature = "optimism")] Self::PostExec(r) => FoundryReceiptEnvelope::PostExec(r.map_logs(f)), - Self::Deposit(r) => FoundryReceiptEnvelope::Deposit(r.map_receipt(|r| r.map_logs(f))), + #[cfg(feature = "optimism")] + Self::Deposit(r) => FoundryReceiptEnvelope::Deposit( + r.map_receipt(|r: OpDepositReceipt| r.map_logs(f)), + ), Self::Tempo(r) => FoundryReceiptEnvelope::Tempo(r.map_logs(f)), } } @@ -182,38 +199,14 @@ impl FoundryReceiptEnvelope { Self::Eip1559(t) => &t.logs_bloom, Self::Eip4844(t) => &t.logs_bloom, Self::Eip7702(t) => &t.logs_bloom, + #[cfg(feature = "optimism")] Self::PostExec(t) => &t.logs_bloom, + #[cfg(feature = "optimism")] Self::Deposit(t) => &t.logs_bloom, Self::Tempo(t) => &t.logs_bloom, } } - /// Return the receipt's deposit_nonce if it is a deposit receipt. - pub fn deposit_nonce(&self) -> Option { - self.as_deposit_receipt().and_then(|r| r.deposit_nonce) - } - - /// Return the receipt's deposit version if it is a deposit receipt. - pub fn deposit_receipt_version(&self) -> Option { - self.as_deposit_receipt().and_then(|r| r.deposit_receipt_version) - } - - /// Returns the deposit receipt if it is a deposit receipt. - pub const fn as_deposit_receipt_with_bloom(&self) -> Option<&OpDepositReceiptWithBloom> { - match self { - Self::Deposit(t) => Some(t), - _ => None, - } - } - - /// Returns the deposit receipt if it is a deposit receipt. - pub const fn as_deposit_receipt(&self) -> Option<&OpDepositReceipt> { - match self { - Self::Deposit(t) => Some(&t.receipt), - _ => None, - } - } - /// Consumes the type and returns the underlying [`Receipt`]. pub fn into_receipt(self) -> Receipt { match self { @@ -222,8 +215,10 @@ impl FoundryReceiptEnvelope { | Self::Eip1559(t) | Self::Eip4844(t) | Self::Eip7702(t) - | Self::PostExec(t) | Self::Tempo(t) => t.receipt, + #[cfg(feature = "optimism")] + Self::PostExec(t) => t.receipt, + #[cfg(feature = "optimism")] Self::Deposit(t) => t.receipt.into_inner(), } } @@ -236,8 +231,10 @@ impl FoundryReceiptEnvelope { | Self::Eip1559(t) | Self::Eip4844(t) | Self::Eip7702(t) - | Self::PostExec(t) | Self::Tempo(t) => &t.receipt, + #[cfg(feature = "optimism")] + Self::PostExec(t) => &t.receipt, + #[cfg(feature = "optimism")] Self::Deposit(t) => &t.receipt.inner, } } @@ -287,7 +284,9 @@ impl Encodable for FoundryReceiptEnvelope { Self::Eip1559(r) => r.length() + 1, Self::Eip4844(r) => r.length() + 1, Self::Eip7702(r) => r.length() + 1, + #[cfg(feature = "optimism")] Self::PostExec(r) => r.length() + 1, + #[cfg(feature = "optimism")] Self::Deposit(r) => r.length() + 1, Self::Tempo(r) => r.length() + 1, _ => unreachable!("receipt already matched"), @@ -314,11 +313,13 @@ impl Encodable for FoundryReceiptEnvelope { EIP7702_TX_TYPE_ID.encode(out); r.encode(out); } + #[cfg(feature = "optimism")] Self::PostExec(r) => { Header { list: true, payload_length: payload_len }.encode(out); POST_EXEC_TX_TYPE_ID.encode(out); r.encode(out); } + #[cfg(feature = "optimism")] Self::Deposit(r) => { Header { list: true, payload_length: payload_len }.encode(out); DEPOSIT_TX_TYPE_ID.encode(out); @@ -371,18 +372,23 @@ impl Decodable for FoundryReceiptEnvelope { buf.advance(1); ::decode(buf) .map(FoundryReceiptEnvelope::Eip7702) - } else if receipt_type == POST_EXEC_TX_TYPE_ID { - buf.advance(1); - ::decode(buf) - .map(FoundryReceiptEnvelope::PostExec) - } else if receipt_type == DEPOSIT_TX_TYPE_ID { - buf.advance(1); - ::decode(buf) - .map(FoundryReceiptEnvelope::Deposit) } else if receipt_type == TEMPO_TX_TYPE_ID { buf.advance(1); ::decode(buf).map(FoundryReceiptEnvelope::Tempo) } else { + #[cfg(feature = "optimism")] + { + if receipt_type == POST_EXEC_TX_TYPE_ID { + buf.advance(1); + return ::decode(buf) + .map(FoundryReceiptEnvelope::PostExec); + } + if receipt_type == DEPOSIT_TX_TYPE_ID { + buf.advance(1); + return ::decode(buf) + .map(FoundryReceiptEnvelope::Deposit); + } + } Err(alloy_rlp::Error::Custom("invalid receipt type")) } } @@ -404,7 +410,9 @@ impl Typed2718 for FoundryReceiptEnvelope { Self::Eip1559(_) => EIP1559_TX_TYPE_ID, Self::Eip4844(_) => EIP4844_TX_TYPE_ID, Self::Eip7702(_) => EIP7702_TX_TYPE_ID, + #[cfg(feature = "optimism")] Self::PostExec(_) => POST_EXEC_TX_TYPE_ID, + #[cfg(feature = "optimism")] Self::Deposit(_) => DEPOSIT_TX_TYPE_ID, Self::Tempo(_) => TEMPO_TX_TYPE_ID, } @@ -419,7 +427,9 @@ impl Encodable2718 for FoundryReceiptEnvelope { Self::Eip1559(r) => 1 + r.length(), Self::Eip4844(r) => 1 + r.length(), Self::Eip7702(r) => 1 + r.length(), + #[cfg(feature = "optimism")] Self::PostExec(r) => 1 + r.length(), + #[cfg(feature = "optimism")] Self::Deposit(r) => 1 + r.length(), Self::Tempo(r) => 1 + r.length(), } @@ -435,8 +445,10 @@ impl Encodable2718 for FoundryReceiptEnvelope { | Self::Eip1559(r) | Self::Eip4844(r) | Self::Eip7702(r) - | Self::PostExec(r) | Self::Tempo(r) => r.encode(out), + #[cfg(feature = "optimism")] + Self::PostExec(r) => r.encode(out), + #[cfg(feature = "optimism")] Self::Deposit(r) => r.encode(out), } } @@ -444,15 +456,18 @@ impl Encodable2718 for FoundryReceiptEnvelope { impl Decodable2718 for FoundryReceiptEnvelope { fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result { - if ty == DEPOSIT_TX_TYPE_ID { - return Ok(Self::Deposit(OpDepositReceiptWithBloom::decode(buf)?)); + #[cfg(feature = "optimism")] + { + if ty == DEPOSIT_TX_TYPE_ID { + return Ok(Self::Deposit(OpDepositReceiptWithBloom::decode(buf)?)); + } + if ty == POST_EXEC_TX_TYPE_ID { + return Ok(Self::PostExec(ReceiptWithBloom::decode(buf)?)); + } } if ty == TEMPO_TX_TYPE_ID { return Ok(Self::Tempo(ReceiptWithBloom::decode(buf)?)); } - if ty == POST_EXEC_TX_TYPE_ID { - return Ok(Self::PostExec(ReceiptWithBloom::decode(buf)?)); - } match ReceiptEnvelope::typed_decode(ty, buf)? { ReceiptEnvelope::Eip2930(tx) => Ok(Self::Eip2930(tx)), ReceiptEnvelope::Eip1559(tx) => Ok(Self::Eip1559(tx)), @@ -646,8 +661,11 @@ mod tests { assert!(receipt.status()); assert_eq!(receipt.cumulative_gas_used(), 100000); assert!(receipt.logs().is_empty()); - assert!(receipt.deposit_nonce().is_none()); - assert!(receipt.deposit_receipt_version().is_none()); + #[cfg(feature = "optimism")] + { + assert!(receipt.deposit_nonce().is_none()); + assert!(receipt.deposit_receipt_version().is_none()); + } } #[test] diff --git a/crates/primitives/src/transaction/request.rs b/crates/primitives/src/transaction/request.rs index 2c4dbae8fdcd4..8ae31efbd5cb1 100644 --- a/crates/primitives/src/transaction/request.rs +++ b/crates/primitives/src/transaction/request.rs @@ -3,15 +3,19 @@ use alloy_network::{ BuildResult, NetworkTransactionBuilder, NetworkWallet, TransactionBuilder, TransactionBuilder4844, TransactionBuilderError, }; -use alloy_primitives::{Address, B256, ChainId, TxKind, U256}; +use alloy_primitives::{Address, ChainId, TxKind, U256}; use alloy_rpc_types::{AccessList, TransactionInputKind, TransactionRequest}; use alloy_serde::{OtherFields, WithOtherFields}; +#[cfg(feature = "optimism")] use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, POST_EXEC_TX_TYPE_ID, TxDeposit}; +#[cfg(feature = "optimism")] use op_revm::transaction::deposit::DepositTransactionParts; use serde::{Deserialize, Serialize}; use tempo_alloy::rpc::TempoTransactionRequest; use tempo_primitives::{TEMPO_TX_TYPE_ID, TempoTxType}; +#[cfg(feature = "optimism")] +use super::optimism::get_deposit_tx_parts; use super::{FoundryTxEnvelope, FoundryTxType, FoundryTypedTx}; use crate::FoundryNetwork; @@ -28,6 +32,7 @@ use crate::FoundryNetwork; #[derive(Clone, Debug, PartialEq, Eq)] pub enum FoundryTransactionRequest { Ethereum(TransactionRequest), + #[cfg(feature = "optimism")] Op(WithOtherFields), Tempo(Box), } @@ -44,6 +49,7 @@ impl FoundryTransactionRequest { pub fn into_inner(self) -> TransactionRequest { match self { Self::Ethereum(tx) => tx, + #[cfg(feature = "optimism")] Self::Op(tx) => tx.inner, Self::Tempo(tx) => tx.inner, } @@ -55,6 +61,7 @@ impl FoundryTransactionRequest { /// # Returns /// - Ok(deposit_tx_parts) if all necessary keys are present to build a deposit transaction. /// - Err(missing) if some keys are missing to build a deposit transaction. + #[cfg(feature = "optimism")] pub fn get_deposit_tx_parts(&self) -> Result> { match self { Self::Op(tx) => get_deposit_tx_parts(&tx.other), @@ -69,9 +76,11 @@ impl FoundryTransactionRequest { pub fn preferred_type(&self) -> FoundryTxType { match self { Self::Ethereum(tx) => tx.preferred_type().into(), + #[cfg(feature = "optimism")] Self::Op(tx) if tx.inner.transaction_type == Some(POST_EXEC_TX_TYPE_ID) => { FoundryTxType::PostExec } + #[cfg(feature = "optimism")] Self::Op(_) => FoundryTxType::Deposit, Self::Tempo(_) => FoundryTxType::Tempo, } @@ -95,6 +104,7 @@ impl FoundryTransactionRequest { /// Check if all necessary keys are present to build a Deposit transaction, returning a list of /// keys that are missing. + #[cfg(feature = "optimism")] pub fn complete_deposit(&self) -> Result<(), Vec<&'static str>> { self.get_deposit_tx_parts().map(|_| ()) } @@ -123,7 +133,9 @@ impl FoundryTransactionRequest { FoundryTxType::Eip1559 => self.as_ref().complete_1559(), FoundryTxType::Eip4844 => self.complete_4844(), FoundryTxType::Eip7702 => self.as_ref().complete_7702(), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => self.complete_deposit(), + #[cfg(feature = "optimism")] FoundryTxType::PostExec => Err(vec!["not implemented for post-exec tx"]), FoundryTxType::Tempo => self.complete_tempo(), } { @@ -138,9 +150,10 @@ impl FoundryTransactionRequest { /// Converts the request into a `FoundryTypedTx`, handling all Ethereum and OP-stack transaction /// types. pub fn build_typed_tx(self) -> Result { + #[cfg(feature = "optimism")] if let Ok(deposit_tx_parts) = self.get_deposit_tx_parts() { // Build deposit transaction - Ok(FoundryTypedTx::Deposit(TxDeposit { + return Ok(FoundryTypedTx::Deposit(TxDeposit { from: self.from().unwrap_or_default(), source_hash: deposit_tx_parts.source_hash, to: self.kind().unwrap_or_default(), @@ -149,8 +162,9 @@ impl FoundryTransactionRequest { gas_limit: self.gas_limit().unwrap_or_default(), is_system_transaction: deposit_tx_parts.is_system_transaction, input: self.input().cloned().unwrap_or_default(), - })) - } else if self.complete_tempo().is_ok() + })); + } + if self.complete_tempo().is_ok() && let Self::Tempo(tx_req) = self { // Build Tempo transaction @@ -192,6 +206,7 @@ impl Serialize for FoundryTransactionRequest { { match self { Self::Ethereum(tx) => tx.serialize(serializer), + #[cfg(feature = "optimism")] Self::Op(tx) => tx.serialize(serializer), Self::Tempo(tx) => tx.serialize(serializer), } @@ -211,6 +226,7 @@ impl AsRef for FoundryTransactionRequest { fn as_ref(&self) -> &TransactionRequest { match self { Self::Ethereum(tx) => tx, + #[cfg(feature = "optimism")] Self::Op(tx) => tx, Self::Tempo(tx) => tx.as_ref(), } @@ -221,6 +237,7 @@ impl AsMut for FoundryTransactionRequest { fn as_mut(&mut self) -> &mut TransactionRequest { match self { Self::Ethereum(tx) => tx, + #[cfg(feature = "optimism")] Self::Op(tx) => tx, Self::Tempo(tx) => tx.as_mut(), } @@ -244,15 +261,16 @@ impl From> for FoundryTransactionRequest { { tempo_tx_req.set_nonce_key(nonce_key); } - Self::Tempo(Box::new(tempo_tx_req)) - } else if tx.transaction_type == Some(DEPOSIT_TX_TYPE_ID) + return Self::Tempo(Box::new(tempo_tx_req)); + } + #[cfg(feature = "optimism")] + if tx.transaction_type == Some(DEPOSIT_TX_TYPE_ID) || tx.transaction_type == Some(POST_EXEC_TX_TYPE_ID) || get_deposit_tx_parts(&tx.other).is_ok() { - Self::Op(tx) - } else { - Self::Ethereum(tx.into_inner()) + return Self::Op(tx); } + Self::Ethereum(tx.into_inner()) } } @@ -264,6 +282,7 @@ impl From for FoundryTransactionRequest { FoundryTypedTx::Eip1559(tx) => Self::Ethereum(Into::::into(tx)), FoundryTypedTx::Eip4844(tx) => Self::Ethereum(Into::::into(tx)), FoundryTypedTx::Eip7702(tx) => Self::Ethereum(Into::::into(tx)), + #[cfg(feature = "optimism")] FoundryTypedTx::Deposit(tx) => { let other = OtherFields::from_iter([ ("sourceHash", tx.source_hash.to_string().into()), @@ -272,6 +291,7 @@ impl From for FoundryTransactionRequest { ]); WithOtherFields { inner: Into::::into(tx), other }.into() } + #[cfg(feature = "optimism")] FoundryTypedTx::PostExec(tx) => WithOtherFields { inner: Into::::into(tx), other: OtherFields::default(), @@ -307,8 +327,9 @@ impl From for FoundryTransactionRequest { } } -impl From> for FoundryTransactionRequest { - fn from(tx: op_alloy_rpc_types::Transaction) -> Self { +#[cfg(not(feature = "optimism"))] +impl From> for FoundryTransactionRequest { + fn from(tx: alloy_rpc_types_eth::Transaction) -> Self { tx.inner.into_inner().into() } } @@ -437,7 +458,9 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { FoundryTxType::Eip1559 => self.as_ref().complete_1559(), FoundryTxType::Eip4844 => self.as_ref().complete_4844(), FoundryTxType::Eip7702 => self.as_ref().complete_7702(), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => self.complete_deposit(), + #[cfg(feature = "optimism")] FoundryTxType::PostExec => Err(vec!["not implemented for post-exec tx"]), FoundryTxType::Tempo => self.complete_tempo(), } @@ -448,9 +471,14 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { } fn can_build(&self) -> bool { - self.as_ref().can_build() - || self.complete_deposit().is_ok() - || self.complete_tempo().is_ok() + if self.as_ref().can_build() || self.complete_tempo().is_ok() { + return true; + } + #[cfg(feature = "optimism")] + if self.complete_deposit().is_ok() { + return true; + } + false } fn output_tx_type(&self) -> FoundryTxType { @@ -465,7 +493,9 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { FoundryTxType::Eip1559 => self.as_ref().complete_1559().ok(), FoundryTxType::Eip4844 => self.as_ref().complete_4844().ok(), FoundryTxType::Eip7702 => self.as_ref().complete_7702().ok(), + #[cfg(feature = "optimism")] FoundryTxType::Deposit => self.complete_deposit().ok(), + #[cfg(feature = "optimism")] FoundryTxType::PostExec => self.complete_type(pref).ok(), FoundryTxType::Tempo => self.complete_tempo().ok(), }?; @@ -479,11 +509,21 @@ impl NetworkTransactionBuilder for FoundryTransactionRequest { let inner = self.as_mut(); inner.transaction_type = Some(preferred_type as u8); inner.gas.is_none().then(|| inner.set_gas_limit(Default::default())); - if !matches!(preferred_type, FoundryTxType::Deposit | FoundryTxType::Tempo) { + let is_deposit = { + #[cfg(feature = "optimism")] + { + preferred_type == FoundryTxType::Deposit + } + #[cfg(not(feature = "optimism"))] + { + false + } + }; + if !is_deposit && preferred_type != FoundryTxType::Tempo { inner.trim_conflicting_keys(); inner.populate_blob_hashes(); } - if preferred_type != FoundryTxType::Deposit { + if !is_deposit { inner.nonce.is_none().then(|| inner.set_nonce(Default::default())); } if matches!(preferred_type, FoundryTxType::Legacy | FoundryTxType::Eip2930) { @@ -548,42 +588,10 @@ impl TransactionBuilder4844 for FoundryTransactionRequest { } } -/// Converts `OtherFields` to `DepositTransactionParts`, produces error with missing fields -pub fn get_deposit_tx_parts( - other: &OtherFields, -) -> Result> { - let mut missing = Vec::new(); - let source_hash = - other.get_deserialized::("sourceHash").transpose().ok().flatten().unwrap_or_else( - || { - missing.push("sourceHash"); - Default::default() - }, - ); - let mint = other - .get_deserialized::("mint") - .transpose() - .unwrap_or_else(|_| { - missing.push("mint"); - Default::default() - }) - .map(|value| value.to::()); - let is_system_transaction = - other.get_deserialized::("isSystemTx").transpose().ok().flatten().unwrap_or_else( - || { - missing.push("isSystemTx"); - Default::default() - }, - ); - if missing.is_empty() { - Ok(DepositTransactionParts { source_hash, mint, is_system_transaction }) - } else { - Err(missing) - } -} - #[cfg(test)] mod tests { + use alloy_primitives::B256; + use super::*; fn default_tx_req() -> TransactionRequest { @@ -618,6 +626,7 @@ mod tests { } #[test] + #[cfg(feature = "optimism")] fn test_routing_op_by_deposit_fields() { let tx = default_tx_req(); let mut other = OtherFields::default(); @@ -669,6 +678,7 @@ mod tests { } #[test] + #[cfg(feature = "optimism")] fn test_serialization_op() { let tx = default_tx_req(); let mut other = OtherFields::default(); diff --git a/crates/script-sequence/Cargo.toml b/crates/script-sequence/Cargo.toml index 7f112ce1bbda8..ce94945fb27cf 100644 --- a/crates/script-sequence/Cargo.toml +++ b/crates/script-sequence/Cargo.toml @@ -27,3 +27,7 @@ revm-inspectors.workspace = true alloy-network.workspace = true alloy-primitives.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 332420d0d1c42..d243814c4b148 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -63,3 +63,15 @@ tempo-primitives.workspace = true [dev-dependencies] tempfile.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-evm/optimism", + "foundry-evm-networks/optimism", + "foundry-common/optimism", + "foundry-cheatcodes/optimism", + "foundry-cli/optimism", + "forge-script-sequence/optimism", + "forge-verify/optimism", +] diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 5ac7e4c669ade..f1bda2ccdcf55 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,8 +1,8 @@ -use std::{cmp::Ordering, sync::Arc, time::Duration}; +use std::{cmp::Ordering, num::NonZeroU64, sync::Arc, time::Duration}; use crate::{ - ScriptArgs, ScriptConfig, build::LinkedBuildData, progress::ScriptProgress, - sequence::ScriptSequenceKind, verify::BroadcastedState, + ScriptArgs, ScriptConfig, build::LinkedBuildData, needs_script_rpc_estimate, + progress::ScriptProgress, sequence::ScriptSequenceKind, verify::BroadcastedState, }; use alloy_chains::{Chain, NamedChain}; use alloy_consensus::{SignableTransaction, Signed}; @@ -21,11 +21,12 @@ use alloy_signer::Signature; use eyre::{Context, Result, bail}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::Wallets; -use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; +use foundry_cli::utils::has_batch_support; use foundry_common::{ FoundryTransactionBuilder, TransactionMaybeSigned, provider::{ProviderBuilder, try_get_http_provider}, shell, + tempo::TempoSponsor, }; use foundry_config::Config; use foundry_evm::core::evm::{FoundryEvmNetwork, TempoEvmNetwork}; @@ -100,7 +101,17 @@ where is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, + tempo_sponsor: Option<&TempoSponsor>, ) -> Result<()> { + let access_key_authorization = match self { + Self::AccessKey(_, _, access_key) => Some(( + access_key.wallet_address, + access_key.key_address, + access_key.key_authorization.clone(), + )), + _ => None, + }; + if let Self::Raw(tx, _) | Self::Unlocked(tx) | Self::Browser(tx, _) @@ -137,11 +148,28 @@ where } } + if let Some((wallet_address, key_address, key_authorization)) = + access_key_authorization.as_ref() + { + tx.prepare_access_key_authorization( + provider, + *wallet_address, + *key_address, + key_authorization.as_ref(), + ) + .await?; + } + // 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(tx, provider, estimate_multiplier).await?; } + + if let Some(sponsor) = tempo_sponsor { + let from = tx.from().expect("no sender"); + sponsor.attach_and_print::(tx, from).await?; + } } Ok(()) @@ -211,6 +239,7 @@ where is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, + tempo_sponsor: Option<&TempoSponsor>, ) -> Result { self.prepare( &provider, @@ -218,6 +247,7 @@ where is_fixed_gas_limit, estimate_via_rpc, estimate_multiplier, + tempo_sponsor, ) .await?; @@ -387,6 +417,27 @@ impl BundledState { SendTransactionsKind::Raw { eth_wallets, browser: self.browser_wallet, access_keys } }; + let tempo_sponsor = self.script_config.tempo.sponsor_config().await?.map(Arc::new); + if tempo_sponsor.is_some() && self.script_config.tempo.sponsor_sig.is_some() { + let remaining = self + .sequence + .sequences() + .iter() + .map(|sequence| { + sequence + .transactions() + .skip(sequence.receipts.len()) + .filter(|tx| tx.is_unsigned()) + .count() + }) + .sum::(); + if remaining > 1 { + eyre::bail!( + "--tempo.sponsor-sig can only sponsor one remaining script transaction; use --tempo.sponsor-signer for multi-transaction scripts" + ); + } + } + let progress = ScriptProgress::default(); for i in 0..self.sequence.sequences().len() { @@ -464,6 +515,11 @@ impl BundledState { let kind = match tx_with_metadata.tx().clone() { TransactionMaybeSigned::Signed { tx, .. } => { + if tempo_sponsor.is_some() { + eyre::bail!( + "cannot attach Tempo sponsor signature to an already signed script transaction" + ); + } SendTransactionKind::Signed(tx) } TransactionMaybeSigned::Unsigned(mut tx) => { @@ -487,6 +543,8 @@ impl BundledState { tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); } + self.script_config.tempo.apply::(&mut tx, None); + send_kind.for_sender(&from, tx)? } }; @@ -495,9 +553,13 @@ impl BundledState { }) .collect::>>()?; - let estimate_via_rpc = has_different_gas_calc(sequence.chain) - || self.script_config.evm_opts.networks.is_tempo() - || self.args.skip_simulation; + let estimate_via_rpc = needs_script_rpc_estimate( + sequence.chain, + self.script_config.evm_opts.networks.is_tempo(), + self.script_config.batch, + &self.script_config.tempo, + 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 @@ -525,6 +587,7 @@ impl BundledState { let pending_transactions = batch.iter().map(|(kind, is_fixed_gas_limit)| { let provider = provider.clone(); + let tempo_sponsor = tempo_sponsor.clone(); async move { let res = kind .clone() @@ -534,22 +597,36 @@ impl BundledState { *is_fixed_gas_limit, estimate_via_rpc, self.args.gas_estimate_multiplier, + tempo_sponsor.as_deref(), ) .await; - (res, kind, 0, None) + (res, kind, *is_fixed_gas_limit, 0, None) } .boxed() }); let mut buffer = pending_transactions.collect::>(); - 'send: while let Some((res, kind, attempt, original_res)) = - buffer.next().await + 'send: while let Some(( + res, + kind, + is_fixed_gas_limit, + attempt, + original_res, + )) = buffer.next().await { - if res.is_err() && attempt <= 3 { + if res.is_err() + && self.script_config.tempo.sponsor_sig.is_some() + && attempt == 0 + { + debug!( + "not retrying transaction because --tempo.sponsor-sig is a static signature" + ); + } else if res.is_err() && attempt <= 3 { // Try to resubmit the transaction let provider = provider.clone(); let progress = seq_progress.inner.clone(); + let tempo_sponsor = tempo_sponsor.clone(); buffer.push(Box::pin(async move { debug!(err=?res, ?attempt, "retrying transaction "); let attempt = attempt + 1; @@ -557,8 +634,24 @@ impl BundledState { "retrying transaction {res:?} (attempt {attempt})" )); tokio::time::sleep(Duration::from_millis(1000 * attempt)).await; - let r = kind.clone().send(provider).await; - (r, kind, attempt, original_res.or(Some(res))) + let r = kind + .clone() + .prepare_and_send( + provider, + sequential_broadcast, + is_fixed_gas_limit, + estimate_via_rpc, + self.args.gas_estimate_multiplier, + tempo_sponsor.as_deref(), + ) + .await; + ( + r, + kind, + is_fixed_gas_limit, + attempt, + original_res.or(Some(res)), + ) })); continue 'send; @@ -675,6 +768,7 @@ impl BundledState { let sequence = self.sequence.sequences_mut().get_mut(0).unwrap(); let provider = Arc::new(ProviderBuilder::::new(sequence.rpc_url()).build()?); + let tempo_sponsor = self.script_config.tempo.sponsor_config().await?; // Collect sender addresses - batch mode requires single sender let senders: AddressHashSet = sequence @@ -794,16 +888,35 @@ impl BundledState { max_priority_fee_per_gas: Some(max_priority_fee_per_gas), ..Default::default() }, - fee_token: self.script_config.fee_token, + fee_token: self.script_config.tempo.common.fee_token, calls: calls.clone(), + nonce_key: self.script_config.tempo.expiring_nonce.then_some(U256::MAX), + valid_before: self.script_config.tempo.valid_before.and_then(NonZeroU64::new), ..Default::default() }; + self.script_config.tempo.apply::(&mut batch_tx, None); + + if let BatchSigner::TempoKeychain(_, ak) = &batch_signer { + batch_tx.key_id = Some(ak.key_address); + batch_tx + .prepare_access_key_authorization( + provider.as_ref(), + ak.wallet_address, + ak.key_address, + ak.key_authorization.as_ref(), + ) + .await?; + } // Estimate gas for the batch transaction estimate_gas(&mut batch_tx, provider.as_ref(), self.args.gas_estimate_multiplier).await?; sh_println!("Estimated gas: {}", batch_tx.inner.gas.unwrap_or(0))?; + if let Some(sponsor) = &tempo_sponsor { + sponsor.attach_and_print::(&mut batch_tx, sender).await?; + } + // Sign and send let tx_hash = match batch_signer { BatchSigner::Wallet(wallet) => { @@ -816,8 +929,6 @@ impl BundledState { *pending.tx_hash() } BatchSigner::TempoKeychain(signer, access_key) => { - batch_tx.key_id = Some(access_key.key_address); - let raw_tx = batch_tx .sign_with_access_key( provider.as_ref(), diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index f5e4d46de0344..ea906c9b872e1 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -28,8 +28,8 @@ use eyre::{ContextCompat, Result}; use forge_script_sequence::{AdditionalContract, NestedValue}; use forge_verify::{RetryArgs, VerifierArgs}; use foundry_cli::{ - opts::{BuildOpts, EvmArgs, GlobalArgs}, - utils::{LoadConfig, parse_fee_token_address}, + opts::{BuildOpts, EvmArgs, GlobalArgs, TempoOpts}, + utils::{LoadConfig, has_different_gas_calc}, }; use foundry_common::{ CONTRACT_MAX_SIZE, ContractsByArtifact, SELECTOR_LEN, @@ -44,11 +44,13 @@ use foundry_config::{ value::{Dict, Map}, }, }; +#[cfg(feature = "optimism")] +use foundry_evm::core::evm::OpEvmNetwork; use foundry_evm::{ backend::Backend, core::{ Breakpoints, FoundryTransaction, - evm::{EthEvmNetwork, FoundryEvmNetwork, OpEvmNetwork, TempoEvmNetwork, TxEnvFor}, + evm::{EthEvmNetwork, FoundryEvmNetwork, TempoEvmNetwork, TxEnvFor}, tempo::PATH_USD_ADDRESS, }, executors::ExecutorBuilder, @@ -140,9 +142,9 @@ pub struct ScriptArgs { #[arg(long, requires = "batch", default_value = "100")] pub batch_size: usize, - /// Tempo fee token address for paying transaction fees. - #[arg(long = "tempo.fee-token", value_parser = parse_fee_token_address)] - pub fee_token: Option

, + /// Tempo transaction options. + #[command(flatten)] + pub tempo: TempoOpts, /// Skips on-chain simulation. #[arg(long)] @@ -246,13 +248,43 @@ pub struct ScriptArgs { pub retry: RetryArgs, } +const fn should_default_tempo_fee_token( + is_tempo_network: bool, + batch: bool, + tempo: &TempoOpts, +) -> bool { + // Plain `--network tempo` should stay an ordinary transaction; only Tempo AA opts get defaults. + is_tempo_network && tempo.common.fee_token.is_none() && (batch || tempo.is_tempo()) +} + +const fn needs_tempo_aa_rpc_estimate( + is_tempo_network: bool, + batch: bool, + tempo: &TempoOpts, +) -> bool { + is_tempo_network && (batch || tempo.is_tempo()) +} + +pub(crate) fn needs_script_rpc_estimate( + chain_id: u64, + is_tempo_network: bool, + batch: bool, + tempo: &TempoOpts, + skip_simulation: bool, +) -> bool { + // Tempo AA needs RPC estimation; plain Tempo scripts can use the local simulation result. + (has_different_gas_calc(chain_id) && !is_tempo_network) + || needs_tempo_aa_rpc_estimate(is_tempo_network, batch, tempo) + || skip_simulation +} + impl ScriptArgs { /// Loads config, resolves evm_opts (including network inference from fork), and returns them. async fn resolved_evm_opts(&self) -> Result<(Config, EvmOpts)> { let (config, mut evm_opts) = self.load_config_and_evm_opts()?; - if self.fee_token.is_some() { - // If fee token is set directly select tempo + if self.tempo.is_tempo() { + // If fee token or expiry is set directly select tempo evm_opts.networks = NetworkConfigs::with_tempo(); } else { // Auto-detect network from fork chain ID when not explicitly configured. @@ -285,13 +317,14 @@ impl ScriptArgs { } } - let fee_token = if evm_opts.networks.is_tempo() && self.fee_token.is_none() { - Some(PATH_USD_ADDRESS) - } else { - self.fee_token - }; + let mut tempo = self.tempo.clone(); + tempo.resolve_expires(); + + if should_default_tempo_fee_token(evm_opts.networks.is_tempo(), self.batch, &tempo) { + tempo.common.fee_token = Some(PATH_USD_ADDRESS); + } - let script_config = ScriptConfig::new(config, evm_opts, self.batch, fee_token).await?; + let script_config = ScriptConfig::new(config, evm_opts, self.batch, tempo).await?; Ok(PreprocessedState { args: self, script_config, script_wallets, browser_wallet }) } @@ -320,12 +353,15 @@ impl ScriptArgs { if broadcasted.args.verify { broadcasted.verify().await?; } - Ok(()) - } else if evm_opts.networks.is_optimism() { - self.run_generic_script::(config, evm_opts).await - } else { - self.run_generic_script::(config, evm_opts).await + return Ok(()); } + + #[cfg(feature = "optimism")] + if evm_opts.networks.is_optimism() { + return self.run_generic_script::(config, evm_opts).await; + } + + self.run_generic_script::(config, evm_opts).await } /// Prepares the bundled state (compile, simulate, bundle) and returns it @@ -708,8 +744,8 @@ pub struct ScriptConfig { pub backends: HashMap>, /// Whether to batch all broadcast transactions into a single Tempo batch transaction. pub batch: bool, - /// Tempo fee token address for paying transaction fees. - pub fee_token: Option
, + /// Tempo transaction options applied to broadcast transactions. + pub tempo: TempoOpts, } impl ScriptConfig { @@ -717,7 +753,7 @@ impl ScriptConfig { config: Config, evm_opts: EvmOpts, batch: bool, - fee_token: Option
, + tempo: TempoOpts, ) -> Result { let sender_nonce = if let Some(fork_url) = evm_opts.fork_url.as_ref() { next_nonce(evm_opts.sender, fork_url, evm_opts.fork_block_number).await? @@ -726,7 +762,7 @@ impl ScriptConfig { 1 }; - Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default(), batch, fee_token }) + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::default(), batch, tempo }) } pub async fn update_sender(&mut self, sender: Address) -> Result<()> { @@ -802,7 +838,7 @@ impl ScriptConfig { self.evm_opts.clone(), Some(known_contracts), Some(target), - self.fee_token, + self.tempo.common.fee_token, ) .into(), ) @@ -813,7 +849,7 @@ impl ScriptConfig { // Propagate fee token to the transaction environment so that internal EVM calls // (e.g. script deployment, setUp) use the correct fee token for Tempo networks. - tx_env.set_fee_token(self.fee_token); + tx_env.set_fee_token(self.tempo.common.fee_token); Ok(ScriptRunner::new(builder.build(evm_env, tx_env, db), self.evm_opts.clone())) } @@ -823,6 +859,7 @@ impl ScriptConfig { mod tests { use super::*; use alloy_network::Ethereum; + use alloy_primitives::address; use foundry_config::{NamedChain, UnresolvedEnvVarError}; use std::fs; use tempfile::tempdir; @@ -834,6 +871,50 @@ mod tests { assert_eq!(args.sig, sig); } + #[test] + fn can_parse_shared_tempo_opts() { + let args = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--tempo.fee-token", + "1", + "--tempo.expires", + "10", + ]); + + assert_eq!( + args.tempo.common.fee_token, + Some(address!("0x20C0000000000000000000000000000000000001")) + ); + assert_eq!(args.tempo.common.expires, Some(10)); + } + + #[test] + fn can_parse_sponsor_tempo_opts() { + let args = ScriptArgs::parse_from([ + "foundry-cli", + "Contract.sol", + "--tempo.sponsor", + "0x1111111111111111111111111111111111111111", + "--tempo.sponsor-signer", + "env://TEMPO_SPONSOR_PK", + ]); + + assert_eq!( + args.tempo.sponsor, + Some(address!("0x1111111111111111111111111111111111111111")) + ); + assert_eq!(args.tempo.sponsor_signer.as_deref(), Some("env://TEMPO_SPONSOR_PK")); + } + + #[test] + fn can_parse_full_tempo_opts() { + let args = + ScriptArgs::parse_from(["foundry-cli", "Contract.sol", "--tempo.nonce-key", "1"]); + + assert_eq!(args.tempo.nonce_key, Some(U256::from(1))); + } + #[test] fn can_parse_unlocked() { let args = ScriptArgs::parse_from([ diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index e2404d60ce2b9..b085f8eaf4545 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -6,7 +6,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; use foundry_cheatcodes::BroadcastableTransaction; -use foundry_common::{FoundryTransactionBuilder, TransactionMaybeSigned}; +use foundry_common::TransactionMaybeSigned; use foundry_config::Config; use foundry_evm::{ constants::CALLER, @@ -84,9 +84,7 @@ impl ScriptRunner { .with_input(code.clone()) .with_nonce(sender_nonce + library_transactions.len() as u64); - if let Some(fee_token) = script_config.fee_token { - tx_req.set_fee_token(fee_token); - } + script_config.tempo.apply::(&mut tx_req, None); library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), @@ -122,9 +120,7 @@ impl ScriptRunner { .with_nonce(sender_nonce + library_transactions.len() as u64) .with_to(create2_deployer); - if let Some(fee_token) = script_config.fee_token { - tx_req.set_fee_token(fee_token); - } + script_config.tempo.apply::(&mut tx_req, None); library_transactions.push_back(BroadcastableTransaction { rpc: self.evm_opts.fork_url.clone(), diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index ef10a1ce94082..fe1e9345fa23c 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -129,7 +129,7 @@ impl VerifyBundle { path: Some(artifact.source.to_string_lossy().to_string()), name: artifact .name - .strip_suffix(&format!(".{}", &artifact.profile)) + .strip_suffix(&format!(".{}", artifact.profile)) .unwrap_or_else(|| &artifact.name) .to_string(), }; diff --git a/crates/sol-macro-gen/Cargo.toml b/crates/sol-macro-gen/Cargo.toml index 69ea952d4d040..d3ad56a96cdd2 100644 --- a/crates/sol-macro-gen/Cargo.toml +++ b/crates/sol-macro-gen/Cargo.toml @@ -27,3 +27,7 @@ prettyplease.workspace = true eyre.workspace = true heck.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index d29f6358e93d2..8dc652bcb32bd 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -44,3 +44,7 @@ idna_adapter.workspace = true [dev-dependencies] tokio.workspace = true + +[features] +default = ["optimism"] +optimism = ["foundry-common/optimism"] diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 0a3fad720ef57..fa065425b5281 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -49,7 +49,7 @@ static TEMPLATE_LOCK: LazyLock = LazyLock::new(|| env::temp_dir().join("foundry-forge-test-template.lock")); /// The default Solc version used when compiling tests. -pub const SOLC_VERSION: &str = "0.8.33"; +pub const SOLC_VERSION: &str = "0.8.35"; /// Another Solc version used when compiling tests. /// diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 65a202911509f..e3372a2494f34 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -48,3 +48,12 @@ url.workspace = true tokio = { workspace = true, features = ["macros"] } foundry-test-utils.workspace = true tempfile.workspace = true + +[features] +default = ["optimism"] +optimism = [ + "foundry-common/optimism", + "foundry-evm/optimism", + "foundry-evm-networks/optimism", + "foundry-cli/optimism", +] diff --git a/crates/wallets/src/tempo.rs b/crates/wallets/src/tempo.rs new file mode 100644 index 0000000000000..a86b568fdea2b --- /dev/null +++ b/crates/wallets/src/tempo.rs @@ -0,0 +1,196 @@ +use alloy_eips::Encodable2718; +use alloy_primitives::{Address, hex}; +use alloy_rlp::Decodable; +use alloy_signer::Signer; +use eyre::Result; +use std::path::PathBuf; +use tempo_alloy::rpc::TempoTransactionRequest; +use tempo_primitives::transaction::{ + KeychainSignature, PrimitiveSignature, SignedKeyAuthorization, TempoSignature, +}; + +use crate::{WalletSigner, utils}; + +/// Wallet type: how this wallet was created. +#[derive(Clone, Copy, Default, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +enum WalletType { + #[default] + Local, + Passkey, +} + +/// Cryptographic key type. +#[derive(Clone, Copy, Default, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +enum KeyType { + #[default] + Secp256k1, + P256, + WebAuthn, +} + +/// A single entry from Tempo's `keys.toml`. +#[derive(serde::Deserialize)] +#[allow(dead_code)] +struct KeyEntry { + #[serde(default)] + wallet_type: WalletType, + #[serde(default)] + wallet_address: Address, + #[serde(default)] + chain_id: u64, + #[serde(default)] + key_type: KeyType, + #[serde(default)] + key_address: Option
, + #[serde(default)] + key: Option, + #[serde(default)] + key_authorization: Option, + #[serde(default)] + expiry: Option, + #[serde(default)] + limits: Vec, +} + +/// Per-token spending limit stored in `keys.toml`. +#[derive(serde::Deserialize)] +struct StoredTokenLimit { + #[allow(dead_code)] + currency: Address, + #[allow(dead_code)] + limit: String, +} + +/// The top-level structure of `~/.tempo/wallet/keys.toml`. +#[derive(serde::Deserialize)] +struct KeysFile { + #[serde(default)] + keys: Vec, +} + +/// Configuration for a Tempo access key (keychain mode). +/// +/// When a Tempo wallet entry uses keychain mode (`wallet_address != key_address`), the signer +/// is an access key that signs on behalf of the root wallet. This struct carries the metadata +/// needed to construct the correct transaction. +#[derive(Debug, Clone)] +pub struct TempoAccessKeyConfig { + /// The root wallet address (the `from` address for transactions). + pub wallet_address: Address, + /// The access key's address (derived from the private key that actually signs). + pub key_address: Address, + /// Decoded key authorization for on-chain provisioning. + /// + /// When present, callers should check whether the key is already provisioned on-chain + /// (via the AccountKeychain precompile) before including this in a transaction. + pub key_authorization: Option, +} + +/// Result of looking up an address in Tempo's key store. +pub enum TempoLookup { + /// A direct (EOA) signer was found — `wallet_address == key_address`. + Direct(WalletSigner), + /// A keychain (access key) signer was found — `wallet_address != key_address`. + Keychain(WalletSigner, Box), + /// No matching entry was found. + NotFound, +} + +/// Returns the path to Tempo's keys file. +/// +/// Respects `TEMPO_HOME` env var, defaulting to `~/.tempo`. +fn keys_path() -> Option { + let base = std::env::var_os("TEMPO_HOME") + .map(PathBuf::from) + .or_else(|| dirs::home_dir().map(|h| h.join(".tempo")))?; + Some(base.join("wallet").join("keys.toml")) +} + +/// Decodes a hex-encoded, RLP-encoded [`SignedKeyAuthorization`]. +fn decode_key_authorization(hex_str: &str) -> Result { + let bytes = hex::decode(hex_str)?; + let auth = SignedKeyAuthorization::decode(&mut bytes.as_slice())?; + Ok(auth) +} + +/// Looks up a signer for the given address in Tempo's `keys.toml`. +/// +/// Returns [`TempoLookup::Direct`] if a direct-mode (EOA) key is found, +/// [`TempoLookup::Keychain`] if a keychain-mode access key is found, +/// or [`TempoLookup::NotFound`] if no entry matches. +pub fn lookup_signer(from: Address) -> Result { + let path = match keys_path() { + Some(p) if p.is_file() => p, + _ => return Ok(TempoLookup::NotFound), + }; + + let contents = std::fs::read_to_string(&path)?; + let file: KeysFile = toml::from_str(&contents)?; + + for entry in &file.keys { + if entry.wallet_address != from { + continue; + } + + let Some(key) = &entry.key else { + continue; + }; + + // Direct mode: wallet_address == key_address (or key_address is absent). + let is_direct = + entry.key_address.is_none() || entry.key_address == Some(entry.wallet_address); + + let signer = utils::create_private_key_signer(key)?; + + if is_direct { + return Ok(TempoLookup::Direct(signer)); + } + + // Keychain mode: the key is an access key signing on behalf of wallet_address. + let key_authorization = + entry.key_authorization.as_deref().map(decode_key_authorization).transpose()?; + + let config = TempoAccessKeyConfig { + wallet_address: entry.wallet_address, + // SAFETY: `is_direct` was false, so `key_address` is `Some` and != wallet_address + key_address: entry.key_address.unwrap(), + key_authorization, + }; + return Ok(TempoLookup::Keychain(signer, Box::new(config))); + } + + Ok(TempoLookup::NotFound) +} + +/// Signs a Tempo transaction request using an access key (keychain V2 mode). +/// +/// Bypasses the standard `EthereumWallet` signing path and instead: +/// 1. Builds the `TempoTransaction` from the request +/// 2. Computes the V2 keychain signing hash +/// 3. Signs with the access key +/// 4. Wraps in a `KeychainSignature` and encodes to EIP-2718 wire format +pub async fn sign_with_access_key( + tx_request: impl Into, + signer: &impl Signer, + wallet_address: Address, +) -> Result> { + let tx_request: TempoTransactionRequest = tx_request.into(); + let tempo_tx = tx_request + .build_aa() + .map_err(|e| eyre::eyre!("failed to build Tempo AA transaction: {e}"))?; + + let sig_hash = tempo_tx.signature_hash(); + let signing_hash = KeychainSignature::signing_hash(sig_hash, wallet_address); + let raw_sig = signer.sign_hash(&signing_hash).await?; + + let keychain_sig = + KeychainSignature::new(wallet_address, PrimitiveSignature::Secp256k1(raw_sig)); + let aa_signed = tempo_tx.into_signed(TempoSignature::Keychain(keychain_sig)); + + let mut buf = Vec::new(); + aa_signed.encode_2718(&mut buf); + + Ok(buf) +} diff --git a/deny.toml b/deny.toml index 1a0e1e8e53005..0f891df4bfbfe 100644 --- a/deny.toml +++ b/deny.toml @@ -100,7 +100,10 @@ unknown-git = "deny" allow-git = [ "https://github.com/alloy-rs/alloy", "https://github.com/alloy-rs/evm", + "https://github.com/foundry-rs/compilers", + "https://github.com/foundry-rs/foundry-fork-db", "https://github.com/foundry-rs/foundry-core", + "https://github.com/foundry-rs/optimism", "https://github.com/paradigmxyz/revm-inspectors", "https://github.com/paradigmxyz/solar", "https://github.com/bluealloy/revm", @@ -111,7 +114,5 @@ allow-git = [ "https://github.com/tempoxyz/mpp-rs", # Transitive dependency of Tempo "https://github.com/paradigmxyz/reth", - "https://github.com/paradigmxyz/reth-core", - # Temporary: upstream OP crates until release is published. - "https://github.com/ethereum-optimism/optimism", + "https://github.com/stevencartavia/reth", ] diff --git a/docs/dev/lintrules.md b/docs/dev/lintrules.md index 6f5dbbd850784..969d7effe142f 100644 --- a/docs/dev/lintrules.md +++ b/docs/dev/lintrules.md @@ -60,6 +60,8 @@ Next, choose whether you want an [early or late lint pass](#choosing-between-ear - Implement the appropriate trait logic (`EarlyLintPass` or `LateLintPass`) for your lint. Do it in a new file within the relevant severity module (e.g., `src/sol/med/my_new_lint.rs`). +- Add a markdown documentation file for the lint at `crates/lint/docs/.md`. The file is referenced by the lint's `help` URL (`https://getfoundry.sh/forge/linting/`) and is consumed by the [Foundry book](https://github.com/foundry-rs/book) to render the lint reference page. Use [`crates/lint/docs/_template.md`](../../crates/lint/docs/_template.md) as a starting point. The presence of this file is enforced by the `registered_lints_have_docs` unit test in `crates/lint/src/sol/mod.rs`. + ### Choosing Between Early and Late Passes - **Use `EarlyLintPass`** for: diff --git a/flake.lock b/flake.lock index 27f426f491da6..343a79c0cda3d 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1777102577, - "narHash": "sha256-ycoy9svZOQgyInu/lwO7IEQtlP5liqYhEcF9m9hPRbM=", + "lastModified": 1777708550, + "narHash": "sha256-Qif3UXT0l5OQq8H9pRWt4/ia4gF48MWK2oHKL8uVx8U=", "owner": "nix-community", "repo": "fenix", - "rev": "f37403486c59376cd285f9685a8ef8ff25c09a3c", + "rev": "74c1591efaff494756b8d35ebe357c6c2bbdca96", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1776949667, - "narHash": "sha256-GMSVw35Q+294GlrTUKlx087E31z7KurReQ1YHSKp5iw=", + "lastModified": 1777641297, + "narHash": "sha256-WNGcmeOZ8Tr9dq6ztCspYbzWFswr2mPebM9LpsfGxPk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "01fbdeef22b76df85ea168fbfe1bfd9e63681b30", + "rev": "c6d65881c5624c9cae5ea6cedef24699b0c0a4c0", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1776800521, - "narHash": "sha256-f8YJfwAOsLFpIoqZuX3yF69UvMLrkx7iVzMH1pJU7cM=", + "lastModified": 1777639980, + "narHash": "sha256-6d7Hdurvbjc5uwJuc0YiK7rZBGj6Gs3uzfBFcTs+xCc=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "8954b66d43225e62c92e8bbcc8500191b5cceb1e", + "rev": "64cdaeb06f69b6b769a492edd88b022ae88e8ca2", "type": "github" }, "original": { diff --git a/foundryup/README.md b/foundryup/README.md index 29e91378929cc..2cf61f8725227 100644 --- a/foundryup/README.md +++ b/foundryup/README.md @@ -30,10 +30,10 @@ To install the latest **nightly** version: foundryup --install nightly ``` -To install a specific version (e.g. `v1.6.0`): +To install a specific version (e.g. `v1.7.0`): ```sh -foundryup --install v1.6.0 +foundryup --install v1.7.0 ``` To **list** all **versions** installed: diff --git a/foundryup/foundryup b/foundryup/foundryup index 9bbaea82e7e21..5fb086a75f8c1 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -3,7 +3,7 @@ set -eo pipefail # NOTE: if you make modifications to this script, please increment the version number. # WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date. -FOUNDRYUP_INSTALLER_VERSION="1.8.0" +FOUNDRYUP_INSTALLER_VERSION="1.8.3" BASE_DIR=${XDG_CONFIG_HOME:-$HOME} FOUNDRY_DIR=${FOUNDRY_DIR:-"$BASE_DIR/.foundry"} @@ -15,6 +15,13 @@ FOUNDRY_BIN_PATH="$FOUNDRY_BIN_DIR/foundryup" FOUNDRYUP_JOBS="" FOUNDRYUP_IGNORE_VERIFICATION=false +# Retry/backoff settings used for `fetch` (GitHub API calls). +# Recovers from transient HTTP 403/429/5xx responses returned by +# api.github.com under heavy load or per-IP rate limiting. +FOUNDRYUP_MAX_RETRIES=5 +FOUNDRYUP_RETRY_DELAY=2 +FOUNDRYUP_RETRY_MAX_TIME=60 + BINS=(forge cast anvil chisel) HASH_NAMES=() HASH_VALUES=() @@ -111,43 +118,7 @@ main() { # Install by downloading binaries if [[ "$FOUNDRYUP_REPO" == "foundry-rs/foundry" && -z "$FOUNDRYUP_BRANCH" && -z "$FOUNDRYUP_COMMIT" ]]; then FOUNDRYUP_VERSION=${FOUNDRYUP_VERSION:-latest} - - # Normalize versions (handle channels, versions without v prefix) - if [[ "$FOUNDRYUP_VERSION" == "latest" || "$FOUNDRYUP_VERSION" == "stable" ]]; then - # Resolve to the latest release (non-prerelease) via the GitHub API - say "fetching latest release from ${FOUNDRYUP_REPO}..." - FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases/latest" | awk ' - /"tag_name"[[:space:]]*:/ && !found { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); print; found=1 } - ') || err "failed to fetch releases from GitHub API" - if [ -z "$FOUNDRYUP_TAG" ]; then - err "could not find a latest release for ${FOUNDRYUP_REPO}" - fi - say "resolved release tag: ${FOUNDRYUP_TAG}" - FOUNDRYUP_VERSION="$FOUNDRYUP_TAG" - elif [[ "$FOUNDRYUP_VERSION" == "nightly" ]]; then - # Resolve to the latest nightly (prerelease) release via the GitHub API - say "fetching latest nightly releases from ${FOUNDRYUP_REPO}..." - FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases" | awk ' - /"tag_name"[[:space:]]*:/ { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); tag=$0 } - /"draft"[[:space:]]*:[[:space:]]*true/ { tag="" } - /"prerelease"[[:space:]]*:[[:space:]]*true/ && !found { if (tag ~ /^nightly-/) { print tag; found=1 } } - ') || err "failed to fetch releases from GitHub API" - if [ -z "$FOUNDRYUP_TAG" ]; then - err "could not find a nightly release for ${FOUNDRYUP_REPO}" - fi - say "resolved nightly release tag: ${FOUNDRYUP_TAG}" - FOUNDRYUP_VERSION="nightly" - elif [[ "$FOUNDRYUP_VERSION" =~ ^nightly- ]]; then - # Specific nightly tag (e.g. nightly-abc123...) - FOUNDRYUP_TAG="$FOUNDRYUP_VERSION" - FOUNDRYUP_VERSION="nightly" - elif [[ "$FOUNDRYUP_VERSION" == [[:digit:]]* ]]; then - # Add v prefix - FOUNDRYUP_VERSION="v${FOUNDRYUP_VERSION}" - FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" - else - FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" - fi + resolve_version_and_tag say "installing foundry (version ${FOUNDRYUP_VERSION}, tag ${FOUNDRYUP_TAG})" @@ -171,7 +142,7 @@ main() { tmp_dir="$(mktemp -d 2>/dev/null)" || err "failed to create temp dir" tmp="$tmp_dir/attestation.txt" ensure download "$ATTESTATION_URL" "$tmp" - + # Read the first line of the attestation file to get the artifact link. # The first line should contain the link to the attestation artifact. attestation_artifact_link="$(head -n1 "$tmp" | tr -d '\r')" @@ -284,7 +255,7 @@ main() { else say 'skipping manpage download: missing "tar"' fi - + if [ "$FOUNDRYUP_IGNORE_VERIFICATION" = true ]; then say "skipped SHA verification for downloaded binaries due to --force flag" else @@ -497,6 +468,18 @@ list() { use() { [ -z "$FOUNDRYUP_VERSION" ] && err "no version provided" + + # If the requested version is a channel (`latest`, `stable`, `nightly`) or a bare semver + # version (e.g. `1.7.0`, `1.6.0-rc1`), resolve it to the immutable tag directory created by + # `--install` (channels hit the GitHub API; semver versions get a `v` prefix). + # Falls back to the literal value for locally-built versions (branches, PRs, commits, custom names). + case "$FOUNDRYUP_VERSION" in + latest|stable|nightly|[0-9]*.[0-9]*.[0-9]*) + resolve_version_and_tag + FOUNDRYUP_VERSION="$FOUNDRYUP_TAG" + ;; + esac + FOUNDRY_VERSION_DIR="$FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_VERSION" if [ -d "$FOUNDRY_VERSION_DIR" ]; then @@ -675,12 +658,70 @@ ensure() { if ! "$@"; then err "command failed: $*"; fi } -# Silently fetches $1 to stdout +# Normalizes `FOUNDRYUP_VERSION` and resolves it to a concrete release tag, +# populating `FOUNDRYUP_TAG`. Handles the `latest`/`stable`/`nightly` channels +# (looked up via the GitHub API). +resolve_version_and_tag() { + FOUNDRYUP_REPO=${FOUNDRYUP_REPO:-foundry-rs/foundry} + if [[ "$FOUNDRYUP_VERSION" == "latest" || "$FOUNDRYUP_VERSION" == "stable" ]]; then + # Resolve to the latest release (non-prerelease) via the GitHub API. + say "fetching latest release tag from ${FOUNDRYUP_REPO}..." + FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases/latest" | awk ' + /"tag_name"[[:space:]]*:/ && !found { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); print; found=1 } + ') || err "failed to fetch release tags from GitHub API" + if [ -z "$FOUNDRYUP_TAG" ]; then + err "could not find a latest release tag for ${FOUNDRYUP_REPO}" + fi + say "resolved release tag: ${FOUNDRYUP_TAG}" + FOUNDRYUP_VERSION="$FOUNDRYUP_TAG" + elif [[ "$FOUNDRYUP_VERSION" == "nightly" ]]; then + # Resolve to the latest nightly (prerelease) release via the GitHub API. + # The GitHub API does not guarantee that releases are returned in + # chronological order, so we collect all matching nightlies along with + # their `published_at` timestamps and sort them ourselves. + say "fetching latest nightly release tags from ${FOUNDRYUP_REPO}..." + FOUNDRYUP_TAG=$(fetch "https://api.github.com/repos/${FOUNDRYUP_REPO}/releases" | awk ' + /"tag_name"[[:space:]]*:/ { gsub(/.*"tag_name"[[:space:]]*:[[:space:]]*"/, ""); gsub(/".*/, ""); tag=$0 } + /"published_at"[[:space:]]*:[[:space:]]*"/ { + pub=$0 + gsub(/.*"published_at"[[:space:]]*:[[:space:]]*"/, "", pub) + gsub(/".*/, "", pub) + if (tag ~ /^nightly-/) print pub "\t" tag + tag="" + } + ' | sort -r | awk -F '\t' 'NR==1 { print $2 }') || err "failed to fetch release tags from GitHub API" + if [ -z "$FOUNDRYUP_TAG" ]; then + err "could not find a nightly release tag for ${FOUNDRYUP_REPO}" + fi + say "resolved nightly release tag: ${FOUNDRYUP_TAG}" + FOUNDRYUP_VERSION="nightly" + elif [[ "$FOUNDRYUP_VERSION" =~ ^nightly- ]]; then + # Specific nightly tag (e.g. nightly-abc123...) + FOUNDRYUP_TAG="$FOUNDRYUP_VERSION" + FOUNDRYUP_VERSION="nightly" + elif [[ "$FOUNDRYUP_VERSION" == [[:digit:]]* ]]; then + # Add v prefix + FOUNDRYUP_VERSION="v${FOUNDRYUP_VERSION}" + FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" + else + FOUNDRYUP_TAG="${FOUNDRYUP_VERSION}" + fi +} + +# Silently fetches $1 to stdout. fetch() { if check_cmd curl; then - curl -fsSL "$1" + curl -fsSL \ + --retry "$FOUNDRYUP_MAX_RETRIES" \ + --retry-delay "$FOUNDRYUP_RETRY_DELAY" \ + --retry-max-time "$FOUNDRYUP_RETRY_MAX_TIME" \ + --retry-all-errors \ + "$1" else - wget -qO- "$1" + wget --tries="$FOUNDRYUP_MAX_RETRIES" \ + --waitretry="$FOUNDRYUP_RETRY_DELAY" \ + --retry-on-http-error=403,408,429,500,502,503,504 \ + -qO- "$1" fi } diff --git a/sleep.json b/sleep.json new file mode 100644 index 0000000000000..5b430e1e663f6 --- /dev/null +++ b/sleep.json @@ -0,0 +1,955 @@ +{ + "results": [ + { + "command": "sleep 0.020", + "mean": 0.023726515413333333, + "stddev": 0.004602014051751124, + "median": 0.02267755758, + "user": 0.0013185473333333334, + "system": 0.0020899164444444446, + "min": 0.02109890308, + "max": 0.05602819808, + "times": [ + 0.02856005608, + 0.02346135008, + 0.02202502208, + 0.02139558708, + 0.02265920408, + 0.02121691608, + 0.02272505608, + 0.02114247908, + 0.02157142808, + 0.021514666079999998, + 0.02161920108, + 0.02335035008, + 0.02224331408, + 0.02228639708, + 0.02152537208, + 0.021732302079999998, + 0.02273370308, + 0.02115513608, + 0.02268494308, + 0.02244547308, + 0.023943647079999998, + 0.02324528508, + 0.02152617908, + 0.023991903079999998, + 0.02250884108, + 0.02342551708, + 0.02113216608, + 0.02168223108, + 0.02222267508, + 0.02273532108, + 0.02273995308, + 0.05602819808, + 0.02501500608, + 0.03121396008, + 0.02424400108, + 0.02459129108, + 0.02633760708, + 0.02377406808, + 0.02365474708, + 0.02406064008, + 0.02300910408, + 0.02437339208, + 0.02317403908, + 0.02257532008, + 0.02267017208, + 0.02356714508, + 0.02367204808, + 0.02258227108, + 0.02330384008, + 0.02225645108, + 0.02478414908, + 0.02484724308, + 0.02270765708, + 0.02339114708, + 0.02450795908, + 0.02348840008, + 0.044674490080000004, + 0.028041754080000002, + 0.022940745079999998, + 0.02259975308, + 0.022112378079999998, + 0.02271348408, + 0.02320266708, + 0.02284982108, + 0.02244050908, + 0.02238655808, + 0.022084648079999998, + 0.02241669808, + 0.02523103408, + 0.02256237908, + 0.03532525108, + 0.02232798408, + 0.02173793008, + 0.021903001079999998, + 0.02288046308, + 0.02368652508, + 0.02211418708, + 0.02265551308, + 0.02187778308, + 0.02191395108, + 0.02182523808, + 0.02185612208, + 0.02109890308, + 0.02294132008, + 0.02191512608, + 0.02264461208, + 0.02227651108, + 0.02307147508, + 0.02227169708, + 0.02177434208 + ], + "memory_usage_byte": [ + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3014656, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3141632, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3268608, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.021", + "mean": 0.022889189941111117, + "stddev": 0.0007161191938371117, + "median": 0.02280623708, + "user": 0.0009166992592592593, + "system": 0.0016941181481481477, + "min": 0.02132554808, + "max": 0.02453766808, + "times": [ + 0.02311599608, + 0.02274468508, + 0.02193879008, + 0.02158843608, + 0.02329398008, + 0.02379494508, + 0.02260801308, + 0.02439507908, + 0.02448522508, + 0.02403379508, + 0.02298143008, + 0.02263027308, + 0.02229235308, + 0.02335063508, + 0.02377098008, + 0.02269184108, + 0.023631199079999998, + 0.02338021508, + 0.02198521708, + 0.02251586208, + 0.022295963079999998, + 0.02226397608, + 0.02453766808, + 0.02184453408, + 0.02289659908, + 0.02382663208, + 0.02347397108, + 0.02225926308, + 0.02207640608, + 0.02243237108, + 0.02278192608, + 0.02270514808, + 0.02245069008, + 0.023018867079999998, + 0.02399866208, + 0.02236840708, + 0.02366382208, + 0.02294188908, + 0.02155127708, + 0.02294999808, + 0.02132554808, + 0.02242025908, + 0.02202766108, + 0.02182175108, + 0.02272186608, + 0.02211805308, + 0.02319764908, + 0.022308045079999998, + 0.02345400908, + 0.022437877079999998, + 0.02273417808, + 0.02217370908, + 0.02254318408, + 0.023269922079999998, + 0.02384951108, + 0.02419476108, + 0.02439866908, + 0.02354840508, + 0.02304219108, + 0.02354960608, + 0.02382648708, + 0.02345751208, + 0.02367913708, + 0.02253067208, + 0.02215132608, + 0.022603942079999998, + 0.02284062808, + 0.02252907808, + 0.02220393508, + 0.023291509079999998, + 0.02399456908, + 0.02407123208, + 0.02279175108, + 0.02300624708, + 0.02309500408, + 0.023036532079999998, + 0.02303833108, + 0.02316846908, + 0.02228349608, + 0.02247140608, + 0.022482600079999998, + 0.02370720808, + 0.02220123708, + 0.02230588608, + 0.02333678708, + 0.02153336008, + 0.02203071908, + 0.02279195108, + 0.02353659108, + 0.02267460708, + 0.022536274079999998, + 0.022769262079999998, + 0.02314857808, + 0.02194885908, + 0.02355038408, + 0.02320035308, + 0.02307451408, + 0.02379926408, + 0.02330480208, + 0.02257055708, + 0.02330320308, + 0.02303003208, + 0.02327859908, + 0.02171311608, + 0.02282052308, + 0.02170123708, + 0.02254831308, + 0.02235855408 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "command": "sleep 0.022", + "mean": 0.02415569324504855, + "stddev": 0.0009830972994273135, + "median": 0.02409406108, + "user": 0.001165289514563107, + "system": 0.001767603883495146, + "min": 0.02243173808, + "max": 0.02755932908, + "times": [ + 0.02456728108, + 0.02650439708, + 0.02480475408, + 0.02452974808, + 0.02300978308, + 0.02521451608, + 0.02543841408, + 0.02538411108, + 0.02475773908, + 0.02403843308, + 0.02426362708, + 0.02326921708, + 0.02447185308, + 0.02361749008, + 0.02410661008, + 0.02371481508, + 0.02327300908, + 0.02430165908, + 0.02328269108, + 0.02315262608, + 0.02380195808, + 0.02283639508, + 0.02491355808, + 0.02401717008, + 0.02556049408, + 0.02350359508, + 0.02400529208, + 0.02533555808, + 0.02467923308, + 0.02478442308, + 0.02422068708, + 0.02352175108, + 0.02481882108, + 0.02456148108, + 0.02314905108, + 0.024188183079999998, + 0.02483985908, + 0.02289141308, + 0.02364977308, + 0.02354907008, + 0.02379135508, + 0.026812933079999997, + 0.023360627079999998, + 0.02331436308, + 0.02504176308, + 0.02358805508, + 0.02409406108, + 0.02350689508, + 0.02303628508, + 0.02430972408, + 0.02516170908, + 0.02352843108, + 0.02274564308, + 0.02345165808, + 0.02429327308, + 0.02252948108, + 0.02445868508, + 0.02755932908, + 0.02522621808, + 0.02491753008, + 0.022858510079999998, + 0.02401968108, + 0.02409596908, + 0.02390450108, + 0.02373108808, + 0.027211489079999998, + 0.02537487108, + 0.02319182608, + 0.02390569508, + 0.02490164708, + 0.02384732708, + 0.02243173808, + 0.02367003008, + 0.02494288308, + 0.02436298308, + 0.02390639308, + 0.02423030808, + 0.02430082908, + 0.02320845908, + 0.02421546708, + 0.02530823508, + 0.02368935308, + 0.02306283708, + 0.023536658079999998, + 0.02359881208, + 0.02438320308, + 0.02477724008, + 0.02362231908, + 0.02419465008, + 0.02596891608, + 0.02307578608, + 0.02459456508, + 0.02384055408, + 0.02421387408, + 0.02510733208, + 0.02473580508, + 0.02243970708, + 0.02253156008, + 0.02550018108, + 0.02440877608, + 0.02281331608, + 0.02354148408, + 0.02352098308 + ], + "memory_usage_byte": [ + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680, + 3399680 + ], + "exit_codes": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] +} diff --git a/testdata/default/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol index 839d97962aa94..ae0c8ed844f5d 100644 --- a/testdata/default/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -305,6 +305,91 @@ contract ExpectRevertWithReverterTest is Test { vm.expectRevert(address(cContract)); aContract.createDContractThroughCContract(); } + + // + // Regression: when the next operation is a top-level CREATE whose constructor + // reverts directly, the reverter address argument must be enforced (it used to + // be silently ignored). The matched reverter is the would-be-deployed address. + function testExpectRevertsWithReverterTopLevelCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(expected); + new DContract(); + + expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(abi.encodePacked("Reverted by DContract"), expected); + new DContract(); + } + + // + // Regression: when the next operation is a top-level CREATE whose constructor + // synchronously creates another contract that reverts (i.e. innermost frame is + // a CREATE), the matched reverter is the outer would-be-deployed address (the + // contract whose deployment failed). + function testExpectRevertsWithReverterNestedCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(expected); + new NestedDContractCreator(); + } + + // + // Regression: `expectPartialRevert(bytes4, address)` overload must enforce + // the reverter address argument when matching a top-level CREATE revert. + function testExpectPartialRevertWithReverterTopLevelCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + // `Reverted by DContract` triggers Solidity's `Error(string)` selector. + vm.expectPartialRevert(bytes4(keccak256("Error(string)")), expected); + new DContract(); + } + + // + // Regression: `expectRevert(bytes4, address)` (exact 4-byte selector + reverter) + // overload must enforce the reverter address argument for a top-level CREATE. + function testExpectRevertWithBytes4SelectorAndReverterTopLevelCreate() public { + address expected = vm.computeCreateAddress(address(this), vm.getNonce(address(this))); + vm.expectRevert(DCustomErrorContract.CustomError.selector, expected); + new DCustomErrorContract(); + } + + // + // Regression: `expectRevert(address, uint64)` count-bearing overload must + // exercise the `count > 1` branch in `create_end`. Use CREATE2 with the same + // salt so both deploys would resolve to the same would-be address (each + // constructor reverts so no contract is ever actually placed there). + function testExpectRevertsWithReverterCountTopLevelCreate2() public { + bytes32 salt = bytes32(uint256(0x42)); + address expected = vm.computeCreate2Address(salt, keccak256(type(DContract).creationCode), address(this)); + vm.expectRevert(expected, 2); + new DContract{salt: salt}(); + new DContract{salt: salt}(); + } + + // + // Regression: CREATE2 deploys must also enforce the reverter address argument. + function testExpectRevertsWithReverterTopLevelCreate2() public { + bytes32 salt = bytes32(uint256(0xC0FFEE)); + address expected = vm.computeCreate2Address(salt, keccak256(type(DContract).creationCode), address(this)); + vm.expectRevert(expected); + new DContract{salt: salt}(); + } +} + +// Used by `testExpectRevertsWithReverterNestedCreate`: a contract whose constructor +// directly creates another contract that reverts. +contract NestedDContractCreator { + constructor() { + new DContract(); + } +} + +// Used by `testExpectRevertWithBytes4SelectorAndReverterTopLevelCreate`: constructor +// reverts with a parameter-less custom error so the full revert data is exactly the +// 4-byte selector. +contract DCustomErrorContract { + error CustomError(); + + constructor() { + revert CustomError(); + } } contract ExpectRevertCount is Test { diff --git a/testdata/default/cheats/Fork2.t.sol b/testdata/default/cheats/Fork2.t.sol index 0941e508483fd..d83c0480b7e72 100644 --- a/testdata/default/cheats/Fork2.t.sol +++ b/testdata/default/cheats/Fork2.t.sol @@ -325,6 +325,7 @@ contract ForkTest is Test { struct LegacyTransactionResult { bytes32 blockHash; bytes blockNumber; + bytes blockTimestamp; bytes chainId; address from; bytes gas; diff --git a/testdata/default/cheats/GetFoundryVersion.t.sol b/testdata/default/cheats/GetFoundryVersion.t.sol index 6139b8b6b6a5e..f01b7cdd7d213 100644 --- a/testdata/default/cheats/GetFoundryVersion.t.sol +++ b/testdata/default/cheats/GetFoundryVersion.t.sol @@ -84,4 +84,55 @@ contract GetFoundryVersionTest is Test { // Should return true for past versions assertTrue(vm.foundryVersionAtLeast("0.2.0")); } + + /// Returns the `MAJOR.MINOR.PATCH` prefix of `vm.getFoundryVersion()`, + /// stripping any pre-release suffix (`-nightly`, `-dev`, …) and the + /// `+..` build metadata. + function _semverPrefix() internal view returns (string memory) { + string[] memory plusSplit = vm.split(vm.getFoundryVersion(), "+"); + require(plusSplit.length == 2, "Invalid version format: Missing '+' separator"); + string[] memory dashSplit = vm.split(plusSplit[0], "-"); + return dashSplit[0]; + } + + function testGetFoundryVersionMajorMinorPatchIsParseable() public view { + // The MAJOR.MINOR.PATCH prefix must always be three numeric components, + // regardless of build kind (tagged release / nightly / dev). + string[] memory parts = vm.split(_semverPrefix(), "."); + require(parts.length == 3, "Invalid semver prefix: expected MAJOR.MINOR.PATCH"); + // Each component must parse as a uint (this reverts on garbage). + vm.parseUint(parts[0]); + vm.parseUint(parts[1]); + vm.parseUint(parts[2]); + } + + function testGetFoundryVersionBuildProfile() public view { + // The build profile must be present and non-empty (e.g. "debug", "release", "dist", …). + string[] memory plusSplit = vm.split(vm.getFoundryVersion(), "+"); + string[] memory metadataComponents = vm.split(plusSplit[1], "."); + require(bytes(metadataComponents[2]).length > 0, "Build profile is empty"); + } + + function testFoundryVersionCmpAndAtLeastAreConsistent() public { + // `foundryVersionAtLeast(v)` must equal `foundryVersionCmp(v) >= 0` for any input. + string[3] memory probes = ["0.0.1", _semverPrefix(), "99.0.0"]; + for (uint256 i = 0; i < probes.length; i++) { + assertEq(vm.foundryVersionAtLeast(probes[i]), vm.foundryVersionCmp(probes[i]) >= 0); + } + } + + function testFoundryVersionCmpRejectsPreRelease() public { + vm._expectCheatcodeRevert(); + vm.foundryVersionCmp("1.0.0-nightly"); + } + + function testFoundryVersionCmpRejectsBuildMetadata() public { + vm._expectCheatcodeRevert(); + vm.foundryVersionCmp("1.0.0+abc1234567.1700000000.release"); + } + + function testFoundryVersionCmpRejectsInvalidVersion() public { + vm._expectCheatcodeRevert(); + vm.foundryVersionCmp("not-a-version"); + } } diff --git a/testdata/default/cheats/MockCall.t.sol b/testdata/default/cheats/MockCall.t.sol index e2ac74d6f70fa..d8019ab4f6ee8 100644 --- a/testdata/default/cheats/MockCall.t.sol +++ b/testdata/default/cheats/MockCall.t.sol @@ -158,6 +158,35 @@ contract MockCallTest is Test { assertEq(mock.pay{value: 50}(1), 100); } + function testMockCallWithValueTransfersBalance() public { + Mock mock = new Mock(); + uint256 value = 10; + vm.deal(address(this), value); + + vm.mockCall(address(mock), value, abi.encodeWithSelector(mock.pay.selector), abi.encode(10)); + + assertEq(address(mock).balance, 0); + assertEq(mock.pay{value: value}(1), 10); + assertEq(address(mock).balance, value); + assertEq(address(this).balance, 0); + } + + function testMockCallWithValueTransfersPrankedSenderBalance() public { + Mock mock = new Mock(); + address sender = address(0xBEEF); + uint256 value = 10; + vm.deal(address(this), 0); + vm.deal(sender, value); + + vm.mockCall(address(mock), value, abi.encodeWithSelector(mock.pay.selector), abi.encode(10)); + + vm.prank(sender); + assertEq(mock.pay{value: value}(1), 10); + assertEq(address(mock).balance, value); + assertEq(address(this).balance, 0); + assertEq(sender.balance, 0); + } + function testMockCallWithValueCalldataPrecedence() public { Mock mock = new Mock(); @@ -279,17 +308,25 @@ contract MockCallRevertTest is Test { function testMockCallRevertWithValue() public { Mock mock = new Mock(); + uint256 value = 10; + vm.deal(address(this), value); - vm.mockCallRevert(address(mock), 10, abi.encodeWithSelector(mock.pay.selector), ERROR_MESSAGE); + vm.mockCallRevert(address(mock), value, abi.encodeWithSelector(mock.pay.selector), ERROR_MESSAGE); assertEq(mock.pay(1), 1); assertEq(mock.pay(2), 2); - try mock.pay{value: 10}(1) { + uint256 initSenderBalance = address(this).balance; + uint256 initTargetBalance = address(mock).balance; + + try mock.pay{value: value}(1) { revert(); } catch (bytes memory err) { require(keccak256(err) == keccak256(ERROR_MESSAGE)); } + + assertEq(address(this).balance, initSenderBalance); + assertEq(address(mock).balance, initTargetBalance); } function testMockCallResetsMockCallRevert() public { diff --git a/testdata/default/cheats/MockCalls.t.sol b/testdata/default/cheats/MockCalls.t.sol index e0f5eef151db6..777543f28e361 100644 --- a/testdata/default/cheats/MockCalls.t.sol +++ b/testdata/default/cheats/MockCalls.t.sol @@ -28,13 +28,17 @@ contract MockCallsTest is Test { mocks[0] = abi.encode(2 ether); mocks[1] = abi.encode(1 ether); mocks[2] = abi.encode(6.423 ether); + vm.deal(address(this), 3 ether); vm.mockCalls(mockErc20, 1 ether, data, mocks); (, bytes memory ret1) = mockErc20.call{value: 1 ether}(data); assertEq(abi.decode(ret1, (uint256)), 2 ether); + assertEq(mockErc20.balance, 1 ether); (, bytes memory ret2) = mockErc20.call{value: 1 ether}(data); assertEq(abi.decode(ret2, (uint256)), 1 ether); + assertEq(mockErc20.balance, 2 ether); (, bytes memory ret3) = mockErc20.call{value: 1 ether}(data); assertEq(abi.decode(ret3, (uint256)), 6.423 ether); + assertEq(mockErc20.balance, 3 ether); } function testMockCalls() public { diff --git a/testdata/forge-std-rev b/testdata/forge-std-rev index b1716a0a12950..977c31eec5512 100644 --- a/testdata/forge-std-rev +++ b/testdata/forge-std-rev @@ -1 +1 @@ -8987040ede9553cea20c95ad40d0455930f9c8e0 \ No newline at end of file +620536fa5277db4e3fd46772d5cbc1ea0696fb43 \ No newline at end of file diff --git a/testdata/utils/Vm.sol b/testdata/utils/Vm.sol index d9f9b52821f52..e488a1820453e 100644 --- a/testdata/utils/Vm.sol +++ b/testdata/utils/Vm.sol @@ -36,121 +36,121 @@ interface Vm { function addr(uint256 privateKey) external pure returns (address keyAddr); function allowCheatcodes(address account) external; function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals) external pure; - function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals, string calldata error) external pure; + function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals, string calldata err) external pure; function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals) external pure; - function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals, string calldata error) external pure; + function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals, string calldata err) external pure; function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta) external pure; - function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string calldata error) external pure; + function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string calldata err) external pure; function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta) external pure; - function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string calldata error) external pure; + function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string calldata err) external pure; function assertApproxEqRelDecimal(uint256 left, uint256 right, uint256 maxPercentDelta, uint256 decimals) external pure; - function assertApproxEqRelDecimal(uint256 left, uint256 right, uint256 maxPercentDelta, uint256 decimals, string calldata error) external pure; + function assertApproxEqRelDecimal(uint256 left, uint256 right, uint256 maxPercentDelta, uint256 decimals, string calldata err) external pure; function assertApproxEqRelDecimal(int256 left, int256 right, uint256 maxPercentDelta, uint256 decimals) external pure; - function assertApproxEqRelDecimal(int256 left, int256 right, uint256 maxPercentDelta, uint256 decimals, string calldata error) external pure; + function assertApproxEqRelDecimal(int256 left, int256 right, uint256 maxPercentDelta, uint256 decimals, string calldata err) external pure; function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta) external pure; - function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string calldata error) external pure; + function assertApproxEqRel(uint256 left, uint256 right, uint256 maxPercentDelta, string calldata err) external pure; function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta) external pure; - function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta, string calldata error) external pure; + function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta, string calldata err) external pure; function assertEqDecimal(uint256 left, uint256 right, uint256 decimals) external pure; - function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; function assertEqDecimal(int256 left, int256 right, uint256 decimals) external pure; - function assertEqDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertEqDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; function assertEq(bool left, bool right) external pure; - function assertEq(bool left, bool right, string calldata error) external pure; + function assertEq(bool left, bool right, string calldata err) external pure; function assertEq(string calldata left, string calldata right) external pure; - function assertEq(string calldata left, string calldata right, string calldata error) external pure; + function assertEq(string calldata left, string calldata right, string calldata err) external pure; function assertEq(bytes calldata left, bytes calldata right) external pure; - function assertEq(bytes calldata left, bytes calldata right, string calldata error) external pure; + function assertEq(bytes calldata left, bytes calldata right, string calldata err) external pure; function assertEq(bool[] calldata left, bool[] calldata right) external pure; - function assertEq(bool[] calldata left, bool[] calldata right, string calldata error) external pure; + function assertEq(bool[] calldata left, bool[] calldata right, string calldata err) external pure; function assertEq(uint256[] calldata left, uint256[] calldata right) external pure; - function assertEq(uint256[] calldata left, uint256[] calldata right, string calldata error) external pure; + function assertEq(uint256[] calldata left, uint256[] calldata right, string calldata err) external pure; function assertEq(int256[] calldata left, int256[] calldata right) external pure; - function assertEq(int256[] calldata left, int256[] calldata right, string calldata error) external pure; + function assertEq(int256[] calldata left, int256[] calldata right, string calldata err) external pure; function assertEq(uint256 left, uint256 right) external pure; function assertEq(address[] calldata left, address[] calldata right) external pure; - function assertEq(address[] calldata left, address[] calldata right, string calldata error) external pure; + function assertEq(address[] calldata left, address[] calldata right, string calldata err) external pure; function assertEq(bytes32[] calldata left, bytes32[] calldata right) external pure; - function assertEq(bytes32[] calldata left, bytes32[] calldata right, string calldata error) external pure; + function assertEq(bytes32[] calldata left, bytes32[] calldata right, string calldata err) external pure; function assertEq(string[] calldata left, string[] calldata right) external pure; - function assertEq(string[] calldata left, string[] calldata right, string calldata error) external pure; + function assertEq(string[] calldata left, string[] calldata right, string calldata err) external pure; function assertEq(bytes[] calldata left, bytes[] calldata right) external pure; - function assertEq(bytes[] calldata left, bytes[] calldata right, string calldata error) external pure; - function assertEq(uint256 left, uint256 right, string calldata error) external pure; + function assertEq(bytes[] calldata left, bytes[] calldata right, string calldata err) external pure; + function assertEq(uint256 left, uint256 right, string calldata err) external pure; function assertEq(int256 left, int256 right) external pure; - function assertEq(int256 left, int256 right, string calldata error) external pure; + function assertEq(int256 left, int256 right, string calldata err) external pure; function assertEq(address left, address right) external pure; - function assertEq(address left, address right, string calldata error) external pure; + function assertEq(address left, address right, string calldata err) external pure; function assertEq(bytes32 left, bytes32 right) external pure; - function assertEq(bytes32 left, bytes32 right, string calldata error) external pure; + function assertEq(bytes32 left, bytes32 right, string calldata err) external pure; function assertFalse(bool condition) external pure; - function assertFalse(bool condition, string calldata error) external pure; + function assertFalse(bool condition, string calldata err) external pure; function assertGeDecimal(uint256 left, uint256 right, uint256 decimals) external pure; - function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; function assertGeDecimal(int256 left, int256 right, uint256 decimals) external pure; - function assertGeDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertGeDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; function assertGe(uint256 left, uint256 right) external pure; - function assertGe(uint256 left, uint256 right, string calldata error) external pure; + function assertGe(uint256 left, uint256 right, string calldata err) external pure; function assertGe(int256 left, int256 right) external pure; - function assertGe(int256 left, int256 right, string calldata error) external pure; + function assertGe(int256 left, int256 right, string calldata err) external pure; function assertGtDecimal(uint256 left, uint256 right, uint256 decimals) external pure; - function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; function assertGtDecimal(int256 left, int256 right, uint256 decimals) external pure; - function assertGtDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertGtDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; function assertGt(uint256 left, uint256 right) external pure; - function assertGt(uint256 left, uint256 right, string calldata error) external pure; + function assertGt(uint256 left, uint256 right, string calldata err) external pure; function assertGt(int256 left, int256 right) external pure; - function assertGt(int256 left, int256 right, string calldata error) external pure; + function assertGt(int256 left, int256 right, string calldata err) external pure; function assertLeDecimal(uint256 left, uint256 right, uint256 decimals) external pure; - function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; function assertLeDecimal(int256 left, int256 right, uint256 decimals) external pure; - function assertLeDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertLeDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; function assertLe(uint256 left, uint256 right) external pure; - function assertLe(uint256 left, uint256 right, string calldata error) external pure; + function assertLe(uint256 left, uint256 right, string calldata err) external pure; function assertLe(int256 left, int256 right) external pure; - function assertLe(int256 left, int256 right, string calldata error) external pure; + function assertLe(int256 left, int256 right, string calldata err) external pure; function assertLtDecimal(uint256 left, uint256 right, uint256 decimals) external pure; - function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; function assertLtDecimal(int256 left, int256 right, uint256 decimals) external pure; - function assertLtDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertLtDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; function assertLt(uint256 left, uint256 right) external pure; - function assertLt(uint256 left, uint256 right, string calldata error) external pure; + function assertLt(uint256 left, uint256 right, string calldata err) external pure; function assertLt(int256 left, int256 right) external pure; - function assertLt(int256 left, int256 right, string calldata error) external pure; + function assertLt(int256 left, int256 right, string calldata err) external pure; function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals) external pure; - function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata error) external pure; + function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string calldata err) external pure; function assertNotEqDecimal(int256 left, int256 right, uint256 decimals) external pure; - function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string calldata error) external pure; + function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string calldata err) external pure; function assertNotEq(bool left, bool right) external pure; - function assertNotEq(bool left, bool right, string calldata error) external pure; + function assertNotEq(bool left, bool right, string calldata err) external pure; function assertNotEq(string calldata left, string calldata right) external pure; - function assertNotEq(string calldata left, string calldata right, string calldata error) external pure; + function assertNotEq(string calldata left, string calldata right, string calldata err) external pure; function assertNotEq(bytes calldata left, bytes calldata right) external pure; - function assertNotEq(bytes calldata left, bytes calldata right, string calldata error) external pure; + function assertNotEq(bytes calldata left, bytes calldata right, string calldata err) external pure; function assertNotEq(bool[] calldata left, bool[] calldata right) external pure; - function assertNotEq(bool[] calldata left, bool[] calldata right, string calldata error) external pure; + function assertNotEq(bool[] calldata left, bool[] calldata right, string calldata err) external pure; function assertNotEq(uint256[] calldata left, uint256[] calldata right) external pure; - function assertNotEq(uint256[] calldata left, uint256[] calldata right, string calldata error) external pure; + function assertNotEq(uint256[] calldata left, uint256[] calldata right, string calldata err) external pure; function assertNotEq(int256[] calldata left, int256[] calldata right) external pure; - function assertNotEq(int256[] calldata left, int256[] calldata right, string calldata error) external pure; + function assertNotEq(int256[] calldata left, int256[] calldata right, string calldata err) external pure; function assertNotEq(uint256 left, uint256 right) external pure; function assertNotEq(address[] calldata left, address[] calldata right) external pure; - function assertNotEq(address[] calldata left, address[] calldata right, string calldata error) external pure; + function assertNotEq(address[] calldata left, address[] calldata right, string calldata err) external pure; function assertNotEq(bytes32[] calldata left, bytes32[] calldata right) external pure; - function assertNotEq(bytes32[] calldata left, bytes32[] calldata right, string calldata error) external pure; + function assertNotEq(bytes32[] calldata left, bytes32[] calldata right, string calldata err) external pure; function assertNotEq(string[] calldata left, string[] calldata right) external pure; - function assertNotEq(string[] calldata left, string[] calldata right, string calldata error) external pure; + function assertNotEq(string[] calldata left, string[] calldata right, string calldata err) external pure; function assertNotEq(bytes[] calldata left, bytes[] calldata right) external pure; - function assertNotEq(bytes[] calldata left, bytes[] calldata right, string calldata error) external pure; - function assertNotEq(uint256 left, uint256 right, string calldata error) external pure; + function assertNotEq(bytes[] calldata left, bytes[] calldata right, string calldata err) external pure; + function assertNotEq(uint256 left, uint256 right, string calldata err) external pure; function assertNotEq(int256 left, int256 right) external pure; - function assertNotEq(int256 left, int256 right, string calldata error) external pure; + function assertNotEq(int256 left, int256 right, string calldata err) external pure; function assertNotEq(address left, address right) external pure; - function assertNotEq(address left, address right, string calldata error) external pure; + function assertNotEq(address left, address right, string calldata err) external pure; function assertNotEq(bytes32 left, bytes32 right) external pure; - function assertNotEq(bytes32 left, bytes32 right, string calldata error) external pure; + function assertNotEq(bytes32 left, bytes32 right, string calldata err) external pure; function assertTrue(bool condition) external pure; - function assertTrue(bool condition, string calldata error) external pure; + function assertTrue(bool condition, string calldata err) external pure; function assume(bool condition) external pure; function assumeNoRevert() external pure; function assumeNoRevert(PotentialRevert calldata potentialRevert) external pure;